2019年02月24日

nim言語:Lubuntu bionic 18.04用 i386のnim-0.19.4のdebパッケージを作ったメモ 2019/02

nim言語:Lubuntu bionic 18.04用 i386のnim-0.19.4のdebパッケージを作ったメモ 2019/02
nim言語でUbuntu/Lubuntuのi386用の*.debパッケージは、
nim_0.19.4-1_i386.debというのが存在するが、これは詳細不明なんだけど、
おそらく「Lubuntu i386 18.10 cosmic」用のもので、
自分が使用している「Lubuntu i386 18.04 bionic」だとlibc6のバージョン不整合でインストールできない。
orz
同じ場所にあるnim_0.19.0-1_i386.debはインストール可能だが、せっかくなので現在最新版の
0.19.4をインストールしたいのだった。

以下、
nim言語:Lubuntu bionic 18.04用 i386のnim-0.19.4のdebパッケージを自分で作った時のメモ 2019/02

参考ページ:
    debパッケージ作成の解説ページ
    第152回 パッケージをビルドしてみる
        http://gihyo.jp/admin/serial/01/ubuntu-recipe/0152

    Ubuntuの*.debとソースパッケージがあるフォルダ
        https://ftp.yz.yamagata-u.ac.jp/pub/linux/ubuntu/archives/pool/universe/n/nim

参考ページに従って以下のコマンドを実行した。
$ pwd /home/username
$ sudo apt-get install build-essential devscripts
$ dget -u https://ftp.yz.yamagata-u.ac.jp/pub/linux/ubuntu/archives/pool/universe/n/nim/nim_0.19.4-1.dsc
$ sudo apt-get build-dep nim
$ sudo apt install help2man
$ cd nim -0.19.4
$ time dpkg-buildpackage -r -uc -b -d
...
...
dpkg-deb: building package 'nim' in '../nim_0.19.4-1_i386.deb'.
	install -d debian/.debhelper/scratch-space/build-nim
	dpkg-deb --build debian/.debhelper/nim/dbgsym-root debian/.debhelper/scratch-space/build-nim
dpkg-deb: building package 'nim-dbgsym' in 'debian/.debhelper/scratch-space/build-nim/nim-dbgsym_0.19.4-1_i386.deb'.
	Renaming nim-dbgsym_0.19.4-1_i386.deb to nim-dbgsym_0.19.4-1_i386.ddeb
	mv debian/.debhelper/scratch-space/build-nim/nim-dbgsym_0.19.4-1_i386.deb ../nim-dbgsym_0.19.4-1_i386.ddeb
	dpkg-deb --build debian/nim-doc ..
dpkg-deb: building package 'nim-doc' in '../nim-doc_0.19.4-1_all.deb'.
 dpkg-genbuildinfo --build=binary
 dpkg-genchanges --build=binary >../nim_0.19.4-1_i386.changes
dpkg-genchanges: info: binary-only upload (no source code included)
 dpkg-source --after-build nim-0.19.4
dpkg-source: warning: Testsuite field contains value autopkgtest, but no tests control file debian/tests/control
dpkg-buildpackage: info: binary-only upload (no source included)

real	29m8.233s
user	22m43.114s
sys	2m12.617s

$ cd ..
$ cp nim_0.19.4-1_i386.deb nim_0.19.4-1_i386_bionic_18.04.deb
$ sudo apt install ./nim_0.19.4-1_i386_bionic_18.04.deb
$ nim -v
Nim Compiler Version 0.19.4 [Linux: i386]
Compiled at 2019-03-05
Copyright (c) 2006-2018 by Andreas Rumpf

active boot switches: -d:release
完成したdebパッケージはこれ。 nim_0.19.4-1_i386_bionic_18.04.deb ファイル名は混乱を避けるため[bionic,18.04]を明示してあります。 i386用なので需要はないと思うものの。。。 インストールは上にもあるように、 $ sudo apt install ./nim_0.19.4-1_i386_bionic_18.04.deb でOK. なぜか日付が未来になっている。。。 :-)


posted by Copyright (C) avrin All Rights Reserved. at 11:21| Comment(0) | nim言語 | このブログの読者になる | 更新情報をチェックする

2019年01月26日

nim言語:AVR:Arduino UNO用にコンパイルしたときのメモ 2019/01

nim言語:AVR:Arduino UNO用にコンパイルしたときのメモ 2019/01
STM32F0マイコンでnim言語のコンパイル実行ができたので、次は
AVR:Arduino UNO 互換機(atmega328p)でもnim言語で動かそうと試みた。

やり方は色々あるけど、
(1) Arduinoライブラリをnim言語から呼び出して使う方法。
     C++ライブラリなのでうまく使えるかは未調査。
        ここの人はarmでArudiunoをやったみたい。(詳細不明)
        Nim wrapper using c2nim for Arduino and MediaTek LinkIt One development
        https://github.com/gokr/ardunimo
(2) Arduinoライブラリは使わずに、ポート設定等を自前でベタ書きして使う方法。

上記(2)のパターンは、nim 0.12.0 の頃の例がここにあった。(今は nim 0.19.2)
Nim on Arduino
https://disconnected.systems/blog/nim-on-adruino/
現在のnim 0.19.2ではコンパイルできないので修正が必要だ。
上の例は、
C言語のサブルーチンを作ってそれをnimから呼び出す方法。
それでもいいけど、それだと、C言語と縁が切れないので (^^;

avr-gccのヘッダファイルをc2nimで変換して、nim言語で全て完結する様にしてみた記録。

結論からいうと、nimコンパイラ側の都合で、 orz
現時点で、AVRで使うのは難しい気がします。
ただし、コンパイルオプション等で制御可能かどうかは未調査。
問題はこう、
 現時点のnimコンパイラ(AVR指定した場合)では、
 int32型は内部でint型に変換されてしまう作りになっている。
バックエンドとなるavr-gccに於いて「int」は「16bit」である。
証明終わり。
orz orz orz

ごく普通の話なので、どこかにスイッチがあるかも。
なければやっぱり終了か。
orz

nim言語ソース側で、int32の数値や型を使わない前提なら動作はするけど。。。。orz
8bit/16bitマイコンは全滅、身近で可能性があるのはARMを除くとPIC32MXだけだ。

コンパイルスイッチとint32の使用を避けることで切り抜けた
いろいろ調査したり試したところ、int32の使用を避ることと、
コンパイルスイッチに以下を追加することで、
--passC:"-DHAVE_STDINT_H" 
なんとか使えそうなことがわかりました。
この場合でも、
int32型は使ってはいけません。 int32はuint32で代用します。
どこまで使えるかはアレだけど。。。

せっかくなので、
以下の挙動を確認しました。
(1) ヘッダファイル
   avr-gccのatmega328p用のヘッダファイル、
   iom328p.h
   sfr_defs.h
   をc2nimで変換して
   iom328p.nim
   sfr_defs.nim
   とした。
   volatileレジスタのアクセスのためにnim/arm/STM32F0で作成した
   reg_utils.incも使用。
(2) Arduino ボード上のLEDを1秒周期で点滅。
   avr-libc:<util/delay.h>内の「_delay_ms()関数を」nim言語側から呼び出すのに成功。
    便利な busy-wait 遅延ループ
    http://cega.jp/avr-libc-jp/group__util__delay.html
    最初は_delay_ms()を使いましたが、今はTIMER0の1msec割り込みでmillis()関数を
    作って対応しています。
(3) UARTを38400bpsで設定、ChaNさんのxprintf()の接続に成功。
    シリアルポートに適当な文字列を1秒ごとに出力する。
(4) PWM出力とPWM周期割り込みを起動
(5) SPI:
    SPI出力テストは、sys.nim内のLED_DEBUG=falseの場合に有効になります。(デフォルトはtrue)
    (LEDポートとSPI クロックが同じポートなので)
    SPI SCK=8MHz でテストデータ出力。 (0xFFを出しているだけ)
    SPIデータはPWM周期割り込み中で出力します。(8バイト分)
    pwm.nim 参照
(6) 割り込み関数: 定義と呼び出し方
    emitプラグマで定義していますが、別途C言語ファイルを作ってコンパイル・リンクする
    方法もあるかと。
    systick.nimを参照。

コンパイル
    avr-gcc(ver.5.4.0)(注2) とnim(ver.0.19.2)に実行パスが通っている必要があります。
    makeとrmコマンドも必要です。
    コマンドラインで
    $ nim c main
    と打つだけでコンパイルが完了してAVR用のelfファイルが出力されますが、
    AVRに書き込むためにHEXファイルが必要なので、結局
    $ make
    でコンパイルするのが無難かと。この場合mapファイルも出力されます。
    Arduino UNOに書き込む時は, guidude等を使います。
        http://mpu.seesaa.net/article/399685603.html
    コンパイルに必要な情報はnim.cfgというファイルに集約されています。

nim.cfg
以下で、"--"で始まるオプションはnimのコマンドライン引数として渡すことも可能。
書き方もいろいろあるみたい。
黄色の部分は結構重要で、不要なオプションが付かない様にしてあります。
これも、どの階層のnim.cfgを無効にするか等、コンパイル時オプションで回避することも可能な様です。
詳細はここ、
Configuration files
https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files
あとC言語ソースをnimからコンパイルして使う場合、"-std=gnu11"等を指定した方が良い。(注3)
avr.standalone.gcc.exe = "avr-gcc"
avr.standalone.gcc.linkerexe = "avr-gcc"

gcc.options.always = ""
gcc.options.debug =""

--stacktrace:off
--gc:none
--os:standalone
--cpu:avr
--deadCodeElim:on
--passC:"-Os"
--passC:"-DF_CPU=16000000UL"
--passC:"-mmcu=atmega328p"
--passC:"-ffunction-sections -fdata-sections"
--passL:"-mmcu=atmega328p"
--passL:"-Wl,--gc-sections"
-d:release
--opt:size
--nimcache:nimcache
--verbosity:1

# Very important option for 8bit AVR
--passC:"-DHAVE_STDINT_H" 
nimソースコード 以下が、AVRレジスタ定義を直接使ったコード。 import iom328p の定義で、すべてのレジスタ設定ができるのでC言語ファイルなしでもコードを 書くことができる。 (今回はお試しなのでC言語関数の「xprintf()」と「_delay_ms()」を使ってみた) main.nim:
#[
  2019/01 by audin (http://mpu.seesaa.net)
  Nim language test program for [[ Arduino UNO ]] and its compatilbes.
  AVR = atmega328p / 16MHz
  avr-gcc: ver.5.4.0
  nim: 0.19.2
  UART baudrate 38400bps
  This program is under MIT license.

  Note: DON'T USE type of int32. USE uint32 instead of it,
      AND ADD --passC:"-DHAVE_STDINT_H" TO COMPILATION OPTION OF nim.
      ( nim 0.19.2 at this moment )
  Resources mainly used:
      LED    (sys.nim): PB5,D13 ( led_on()/led_off() )
      UART   (uart.nim): 38400bps with xprintf() (by polling out one char).
      TIMER0 (systick.nim): 1msec interval timer for millis() function with interrupt TIMER0_OVF_vect.
      TIMER1 (pwm.nim,systick.nim): Period timer for PWM out,
                                 OC1A: PB1, 15pin, D10
                                 OC1B: PB2, 16pin, D9
                                 TIMER1_OVF_vect is used as PWM period interrupt.
      SPI (spi.nim) : SCK=8MHz with chip select pin,
                                 CS: PB0, 14pin, D8 ( cs_set()/cs_clr() )
      Test pin (sys.nim): Output,PD7, 13pin, D7    ( test_on()/test_off() )
]#

import iom328p
import sys,pwm,systick,spi,uart

{.compile:"xprintf.c".}
proc xprintf(formatstr: cstring){.header: "../xprintf.h", importc: "xprintf", varargs,used.}
###
#proc delay_ms(ms:cdouble){.header:"<util/delay.h>",importc:"_delay_ms",used.}


proc initPort*() =
#/* set pull up to i/o port. */
    PORTB.st 0xFF'u8
    PORTC.st 0xFF'u8
    PORTD.st 0xFF'u8
    DDRB.st 0xEF'u8 #/* in: PB4 */
    DDRD.st 0xFF'u8 # all output port

# main program
proc main() =
    initPort()
    initSystick()
    initUart(mBRate(BAUDRATE)) # 38400bps
    when not LED_DEBUG:
        initSpi() # SCK=8MHz
    # PWM setting
    initPwm()
    setPwmPeriod(44100'u32) # PWM period [Hz]
    pwm1_duty(50)
    pwm2_duty(100)
    pwm_start()

    # enable PWM period interrupt
    enablePwmPeriodIntr()

    # enable all interrupt
    ei()

    var num_ui = 0.uint
    while true:
        when LED_DEBUG:
            led_on()
            wait_ms(500)
            led_off()
            wait_ms(500)
        else:
            wait_ms(1000)
        xprintf("\n Number = %u", num_ui)
        num_ui += 10


# Run main function
main() 
コンパイルに必要なファイル: main.nim 以外に必須なファイルは黄色のもの。 Makefileの中でHEXファイルの生成をしています。
    iom328p.nim   # I/Oレジスタ・アドレス定義ファイル(a)
    main.nim
    Makefile
    nim.cfg
    panicoverride.nim
    reg_utils.inc      # nimのvolatileアクセス用 (b)
    sfr_defs.inc       # avr-gccのvolatileアクセス用 (c)
    xprintf.c
    xprintf.h
    pwm.nim
    systick.nim
    spi.nim
    sys.nim
    uart.nim
    
(a)と(b)があれば(c)をほぼ全部省略可能だが「sfr_defs.inc」になるべく手を入れずに使用した。 Dowonload 準備中 nimallフォルダ: 上記コードとファイル群 nimcフォルダ: 以下のページのコードをnim 0.19.2でコンパイル可能にしたもの。 Nim on Arduino https://disconnected.systems/blog/nim-on-adruino/ 割り込みの禁止許可の定義: インラインアセンブラを使いました。
proc ei*(){.used,inline.} =
    asm """
        "sei" : : : "memory"
    """
proc di*(){.used,inline.} =
    asm """
        "cli" : : : "memory"
    """
割り込み関数の定義 こんな感じ (systick.nim)でemitプラグマを使って、 割り込み関数をC言語でnimに埋め込んで、中身はnim側で記述します。 systick.nim:
{.emit: """
#include <avr/interrupt.h>

static void inline systickIntr(void);
ISR(TIMER0_OVF_vect){
    systickIntr();
}""".}

{.emit:"""
#include <avr/interrupt.h>

static void inline  pwmPeriodIntr(void);
#pragma GCC optimize ("O3")
ISR(TIMER1_OVF_vect){
   pwmPeriodIntr();
}""".}

中身は以下のように記述。
proc systickIntr(){.exportc,inline.} =
   ... some code ...

proc pwmPeriodIntr(){.exportc,inline.} =
   ... some code ...
上記青色の #pragma GCC optimize ("O3") の追加で割り込み関数を高速化することも可能です。 かなり効果ありました。(その代わりコードサイズは増える) ここまでの感想 現時点(2019/01)では世界的に見ても、8bit/16bitマイコンではLEDチカチカ以上のサンプルがなかなか 発見できない中、 int32問題に遭遇し、最初は使えないかと思ったが、なんとか使える様に工夫できたと思います。 (o_o)/ 発見したページ msp430(16bit)マイコン用。割り込みを使っている様だ。 https://gitlab.com/jalexander8717/msp430f5510-nim/tree/master (注1) 使用中のnimコンパイラは32bit版でnim 0.19.2。 (注2) Arduino 1.8.8に付属のavr-gccを使用。 (注3) gnu11はArduinoでC言語ソースのコンパイル時指定されているもの。 gnu+11は同様にC++言語ソースの時指定されている。 avr-gcc 4.x系ではC89がデフォルト、5.x系ではgnu11がデフォルト。 参考: GCC 5系初のリリースとなる「GCC 5.1」登場 https://mag.osdn.jp/15/04/24/152100
posted by Copyright (C) avrin All Rights Reserved. at 22:33| Comment(0) | nim言語 | このブログの読者になる | 更新情報をチェックする

2018年12月23日

nim言語:STM32のヘッダファイルをc2nimした時のメモ 2018

nim言語:STM32のヘッダファイルをc2nimした時のメモ 2018
c2nimは、c言語のソースとヘッダファイルをnim言語ソースに変換してしまうツール。
$ nimble install c2nim
でインストールできる。

$ c2nim foo.c $ c2nim boo.h $ c2nim --cpp moo.cpp
等で変換できる。 でも、中身がC言語で拡張子が*.cppファイルのソースはうまくいかない感じ。 その場合、拡張子を*.cにするとOKだった。orz 以下は、STM32マイコンのヘッダファイルを変換したときの記録。 STM32CubeMXで生成された「Nucleo-F030R8」用のプロジェクトフォルダ以下にある、 Drivers\CMSIS\Device\ST\STM32F0xx\Include\stm32f030x8.h というヘッダファイル。 5500行くらいあるんだけど、これをc2nimで変換してみました。 そのままではエラーで変換できないので「手動」で修正しました。 以下その時の記録。 誰かが変換スクリプトを作るのを期待したい。 (1) [置換対応] 「__IO」は「volatile」に置換。 (2) c2nimの実行時オプションに --skipincludeを追加。 これは#include文をimport文に変換しないオプション。 手動でやってもたいした違いはない。 (3) [置換対応] 先頭が一つ以上のアンダースコアで始まる名前はアンダースコアをカットする。
例: __FOO_BAR ---> FOO_BAR _FOO_FUZZ ---> FOO_FUZZ
この手のプレフィックスについては、コマンドラインオプションで --prefix:__ --prefix:_を追加で自動カットできそう。(試してないけど) これで「c2nim」が通るはず。 次は変換されたnimファイルを実際にインポートして出たエラーを潰していきます。 (4pre) volatileは結局エラーになるので全部カット。 (1)の時点で__IOをカットしたほうがよかったか。 (4)[置換対応] unin32_t、uint16_t、uint8_tはそれぞれuint32、uint16、uint8に置換 これらはサフィックスをカットする--suffix:_tオプションで 自動カットできるかも。(試してない) (5) キャストエラーを修正する。数カ所。
const CRC_IDR_IDR* = (cast[uint8_t](0x000000FF)) ## !< General-purpose 8-bit data register bits
const CRC_IDR_IDR* = (0x000000FF.uint8) ## !< General-purpose 8-bit data register bits
uint16版のエラーも出るのでそれも同様に修正する。 (6)名前の衝突エラーを修正する。 これはnimが「_」(アンダースコア)を無視して"識別子を認識する"ことに由来している場合と、 c2nimのバグの可能性と2種類ある様ですが、対処します。 例えば以下の行はエラーになるが、なぜエラーなのは不明。
GPIO_MODER_MODER10* = GPIO_MODER_MODER10_Msk
しかたがないので、単にコメントアウトします。(適当なサフィックスを付けて残す手もある)
# GPIO_MODER_MODER10* = GPIO_MODER_MODER10_Msk
これは名前の衝突の例:コメントアウトします。 WWDG_CR_T0* = WWDG_CR_T_0 (7) 未定義の識別子をコメントアウトする。 RCC_AHBENR_TSCEN等々が未定義エラーになるのでコメントにする。 (8) 外部参照の関数名もコメントアウト 今のところ対処法が不明なので全部コメントアウトします。 例えば、ADC1_IRQHandlerは割り込みハンドラーなので外部ファイルに存在するが、 インポート方法がわからないので全部when文でカット。 リネームしているだけなので不要かと。
when false: const ADC1_COMP_IRQHandler* = ADC1_IRQHandler ... ...
実際に使った例 実際には、core_cm0.hを変換したものをインクルードして使っています。 ここで使ったもの。 nim言語でNucleo-F030R8を動かした時のメモ 2018/12
posted by Copyright (C) avrin All Rights Reserved. at 11:55| Comment(0) | nim言語 | このブログの読者になる | 更新情報をチェックする

2018年12月22日

nim言語でNucleo-F030R8を動かした時のメモ 2018/12

nim言語でNucleo-F030R8を動かした時のメモ 2018/12
はじまり
    源流
    nimについて (https://nim-lang.org/)
STM32F3-DiscoveryでLEDチカチカ
Nucleo-F030R8で動かす
STM提供のレジスタ定義ファイルをc2nimで変換して使う
    c2nimのインストール
    変換のポイント:
    c2nimで変換中:
    実際の使用時:
nim言語で書いたSTM32F0用設定コード
    コンパイルに必要なもの
ハマりポイント
    Makefile:
    SPIとNSS設定
    volatile効いてない !? orz
main.nim
ファイル構成
ダウンロード
その後


はじまり
源流
    基本的に、こちらのかたのブログ記事に触発されて、いろいろやってみた記録。感謝。
         なんとなく活動記録。
        電子工作・プログラミングの備忘録
        NimでLチカ on STM32 (1)
        http://blueeyes.sakura.ne.jp/2018/05/22/1243/
        NimでLチカ on STM32 (2)
        http://blueeyes.sakura.ne.jp/2018/05/28/1256/
        Githubオリジナルソース
        https://github.com/shima-529/STM32OnNim

nimについて (https://nim-lang.org/)
    現在の最新版。
    nim Nim Compiler Version 0.19.0

    rust言語に挫折してから。。。時は流れた。。。
    nim言語を発見して勉強用にライフゲームなどを作っていたところ、
    上記ブログに遭遇したのだった。

STM32F3-DiscoveryでLEDチカチカ
最初は、 https://github.com/shima-529/STM32OnNim
このソースを頂いてSTM32F3-Discovery用に変更してLEDチカチカさせました。
結局、変更点はLEDポートの変更だけでした。
コアが同じなのでリンカスクリプトもベクタファイルもLED点灯程度なら無変更でOKでした。(^^)/
https://www.st.com/ja/evaluation-tools/stm32f3discovery.html


Nucleo-F030R8で動かす
STM32F3-Discoveryで簡単に動いたのに気をよくして、
以前よく遊んだmbedボード「Nucleo-F030R8」で動かそうと決意しました。
NucleoボードのほうがUSB-CDC(UART)が付いているので便利。
以下、Nucleo-F030R8で動くまでの記録を記述。

https://os.mbed.com/platforms/ST-Nucleo-F030R8/
 

STM提供のレジスタ定義ファイルをc2nimで変換して使う
使う分のレジスタ構造体だけを定義していく手もあるけど、
STM32CubeMXについてくるヘッダーファイルをc2nimで変換してみました。
変換したファイルは「stm32f030x8.h」、「core_cm0.h」、
あと 「cmsis_gcc.h」 から「wfi」とかを少し持ってきました。
変換のポイント:
    nim言語:STM32のヘッダファイルをc2nimした時のメモ 2018

nim言語で書いたSTM32F0用設定コード
基本的にレジスタをビット単位で直たたき、"Reset初期値のままでいける場合は
あえて変更しない"という方針。(^^;
(1) PLLで48MHzまでクロックアップ。
(2) Systickタイマー1msec又は10msec周期で割り込みハンドラ起動。
(3) USART2(PA3,PA3)を115200bpsで設定。Nucleo-F030R8の標準UARTポート。
(4) TIM3。PWM用の設定。
(5) PWM周期で割り込みハンドラ起動。
(6) PWM出力を4ch分設定。固定デューティで出力。
(7) SPI1をSCK=12MHzで設定。8bit/16bitポーリング通信。
    PB6はCS(チップセレクト)で使用。
(8) printf()関数、呼び出し可能に設定。
    syscalls.cを追加。
(9) ChaNさんのxprintf()関数、呼び出し設定。
    軽量なので、こちらをデバッグ用にメインで使用。
(10) コードサイズを小さくしたいのでgccのオプションは「-Os」。

コンパイルに必要なもの
Winodws上でmakeしますが、Linux上でもそのままmakeできます。
Windowsの場合、下記(1)〜(3)のコマンドへ実行パスを通しておく必要があります。

(1) nimコンパイラ。上記の本家から取ってきてインストールしておきます。
    現時点では以下のバージョン
    nim Nim Compiler Version 0.19.0
    少なくともこれ以降のバージョンが良い。
    Ubuntu系の場合、「apt install nim 」だと古い可能性があるので、
    $nim -vでバージョンを確認します。
    以下から Version 0.19.0のdebファイルを取得して
    https://packages.debian.org/sid/i386/nim/download
    $ sudo dpkg -i nim_0.19.0-1.deb
    で最新版がインストールできます。
(2) gccは、arm-none-eabi-gcc: https://launchpad.net/gcc-arm-embedded ここから
    最新版をもらってきてインストールしておきます。 (最新じゃなくても別にいいけど)
(3) make,rm コマンドが必要。
(4) コンパイルはMakefileがあるトップフォルダに移動、MS-DOSコマンドラインで
    $ make
    成功すれば「nucleo_f030r8.hex」ができているので、
    STM32CubeProgrammer(注2)やOpenOCDなどで書き込みます。
    自分の場合はST-Link Utilityに付属の「ST-LINK_CLI.exe」で書き込んでいます。(注2)
    Makefile内の以下の行の「#」をカットすればmake後、自動でFLASHに書き込まれます。
    #   "d:\STM32-ST-LINK-Utility\ST-LINK Utility\ST-LINK_CLI.exe" -c SWD -P $(TARGET).hex -Rst
    (フォルダ名は書き換える必要あり)
(5) コンパイルがこける場合
    もう一度、”make”でうまくいく場合があります。
    こける原因は今のところ、nimコンパイル側にあると踏んでいます。

ハマりポイント
苦労した点、という意味。
Makefile:
    nimとgccのコンパイルを「Make 一発」できる様に変更。
    これがなんとも悶絶した。orz
    makeの実行直後には、まだnimが生成した*.cファイル群は存在しないが、
    その存在しないファイルに対して依存関係を記述しないといけない、
    えっ? パニック。(^^;
    で、なんとかやっつけた。(オイ
    ただし、状況依存で途中で止まる場合があるので、その時はもう一度
    makeすればOK.

SPIとNSS設定
    これと次の問題が同時に発生して地獄に落ちました。orz
    STMのF0マイコンでNSSピンの制御が可能なものは、「NSSピンを使わない単純なマスター通信」を
    するために、以下のどちらかの設定が必ず必要、
    (a)SPI_CR1_SSM = 1 且つ SPI_CR1_SSI = 1
     または、
    (b) CR2_SSOE = 1
    上のどちらかを設定しないと、SPIはピクリとも動かない。
    orz orz orz

    STM32CubeMXが生成した初期化コードだと以下の行が該当部分。
    SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
    これを基に、
    LL_SPI_Init(SPI1, &SPI_InitStruct)内でこっそり上記のビットが設定されている。
    こっそり !?
    mbedやSTM32CubeMXに頼りすぎた呪いだ。
    呪い !?

volatile効いてない !? orz
    (i)SPI通信がさっぱり動かないので、SPIのレジスタを色々変えてみたり、
       設定を見直してみたりしたけどダメ。
    (ii) printfでSPI_CR1等のレジスタ値を見ると設定したはずの値が返ってこないし、
         設定値を変えるたびに、その値も微妙に変化するも規則性はないようだ。orz
    (iii) な、なんか壊れてる。。。 orz (i) に戻る。
    というループから「break」できなくて、
    orz orz orz だった。
    結局、
    逆アセンブラリストを順に確認していって、最後の最後に送信レジスタに値を設定するところ、
    よく見ると、
    SPIを有効にする前に、送信レジスタ(SPI_DR)に送信データが入り、
    その後、SPIを有効にしていた。    じゅ、順番が逆!
    アセンブラレベルだと順番が逆になっていた。orz
    で、ようやく
    実はvolatileが効いてないことがわかる。

    volatileについてはvolatileLoad/volatileStoreでケリがついていると思ったが
    微妙なようだ。(全部効いてないということではない感じ、ではある)
    正当な対処方法がわからないのでvolatile{Load,Store}関数呼び出しの最後に
    「nop()挿入」でやっつけた。(注1)

    ということで、SPIが意図どおり動くようになって、かなりすっきりしました。
    (^^)/

犯人は自分だった orz
    その後、全てうまく動いたので、このvolatile問題を再調査してみたところ、
    実は、自分で追加したコードが非volatileになっていたことがわかったのだ。
    orz orz orz
    話すと長いんだけどnimコンパイラのバグを避けるためのコードを入れたら、
    それがバグっていたという話。 orz

nimのvolatile関数について
    マイコンのペリフェラルレジスタ(volatileアクセスで)を8bit/16bitのデータ長を明示的に
    指定してアクセスする場合に、volatileStore/volatileLoad関数が不正なコードを
    吐き出すと思っていたが、以下の関数を追加、修正することで回避した。
    volatileStore/volatileLoad関数を使う自分の理解不足が招いた不具合でした。 orz orz orz
template ld8*[T: SomeInteger](reg: T): uint8 =
  volatile_Load8(cast[ptr uint8](reg.addr) )

template ld16*[T: SomeInteger](reg: T): uint16 =
  volatile_Load16(cast[ptr uint16](reg.addr) )

template st*[T,U: SomeInteger](reg: T, val: U) =
  volatileStore(cast[ptr U](reg.addr), val)
問題は解決したので上で入れた「nop」は全部カットしました。 main.nim
# For NUCLEO-F030R8
import startup
import stm32f030x8
import systick, uart, pwm, gpio, sys, spi

#proc printf(formatstr: cstring){.header: "<stdio.h>", importc: "printf", varargs.}
proc xprintf(formatstr: cstring){.header: "xprintf.h", importc: "xprintf", varargs,used.}

proc IO_Test(spi:auto){.used.} =
    var x = 0
    var sdata :uint8 = 0
    while true:
        # Core register viewing
        xprintf("\n%04d",x)
        xprintf("\nSPI.CR1 = %08X(%X)" ,SPI1.CR1,cast[int](SPI1.CR1.addr) )
        xprintf("\nSPI.CR2 = %08X(%X)" ,SPI1.CR2,cast[int](SPI1.CR2.addr) )
        xprintf("\nRCC.AHBENR  = %08X" ,RCC.AHBENR)
        xprintf("\nRCC.APB1ENR = %08X" ,RCC.APB1ENR)
        xprintf("\nRCC.APB2ENR = %08X" ,RCC.APB2ENR)
        xprintf("\nGPIOA.MODER = %08X" ,GPIOA.MODER)
        xprintf("\nGPIOA.AFR[0]= %08X" ,GPIOA.AFR[0])
        xprintf("\nGPIOA.AFR[1]= %08X" ,GPIOA.AFR[1])
        xprintf("\nGPIOB.MODER = %08X" ,GPIOB.MODER)
        xprintf("\nGPIOB.AFR[0]= %08X" ,GPIOB.AFR[0])
        xprintf("\nGPIOB.AFR[1]= %08X" ,GPIOB.AFR[1])
        xprintf("\n")
        inc(x)
        setTickCounter(200)
        while getTickCounter() > 0: # wait 10msec
            # SPI loop back test
            cs_on()
            var n = spi.send(sdata)
            n = spi.send(n)
            n = spi.send(n)
            n = spi.send(n)
            cs_off()
            if sdata != n:
                xprintf("\nSPI data EEROR!")
                while true:
                    discard
            inc(sdata)


block: # main function
    initSysClock48mhz()
    initGPIO()
    # Start systick timer and interrupt
    discard SysTick_Config(SYSTEM_CLOCK div 1000) # 1msec (1000Hz)
    initSerial(USART2)
    initSPI(SPI1)
    spiEnable()
    initPwm()
    pwm_period_timer_start()

    # PWM tset  Duty setting
    pwm_dutyR_hi( 512)
    pwm_dutyL_hi( 512 shr 1)
    #
    pwm_dutyR_low(512 shr 2)
    pwm_dutyL_low(512 shr 3)

    IO_Test(SPI1) 
上のxprintf()のUART出力(115200bps)は以下、
1767
SPI.CR1 = 0000034C(40013000)
SPI.CR2 = 00001700(40013004)
RCC.AHBENR  = 000E0014
RCC.APB1ENR = 00020002
RCC.APB2ENR = 00001000
GPIOA.MODER = 2800A8A0
GPIOA.AFR[0]= 00001100
GPIOA.AFR[1]= 00000000
GPIOB.MODER = 00001A00
GPIOB.AFR[0]= 00110000
GPIOB.AFR[1]= 00000000
ファイル構成
Top
│  LICENSE
│  Makefile
│  README.md
│
└─src
    │  gpio.nim
    │  main.nim
    │  panicoverride.nim
    │  port_setting.txt
    │  pwm.nim
    │  spi.nim
    │  sys.nim
    │  uart.nim
    │
    ├─f0
    │      STM32F030R8Tx_FLASH.ld
    │      stm32f030x8.nim
    │      vector_f030.c
    │
    ├─lib
    │  └─xprintf
    │          xprintf.c
    │          xprintf.h
    │
    ├─nimcache
    │      dummy.txt
    │
    └─sys
            core_cm0.inc
            nimbase.h
            reg_utils.inc
            startup.nim
            syscalls.c
            systick.nim
ダウンロード 準備中 その後 上のソフトをベースに「SDカードをSPIモードで初期化」まで成功したので、 難関は切り抜けたという感じ。 (^^)/ (注1) {.volatile.}プラグマを使ってみると、ゾッとする様な「ゾッとしないコード」を吐いてきたので 使うのやめました。orz 経緯的には、"{.volatile.}だけだと組み込み用では使えない"、じゃあ ってことでvolatileLoad/volatileSoreが作られたらしい。 {.volatile.}は変数の最適化を停止させる時に使用します。 (注2) STM32CubeProgrammerにもコマンドライン版がある様なので、ちょっと使ってみる。
posted by Copyright (C) avrin All Rights Reserved. at 19:49| Comment(0) | nim言語 | このブログの読者になる | 更新情報をチェックする

2018年12月16日

nim言語に入門した時のメモ 2018/12

nim言語に入門した時のメモ 2018/12
はじまり
2017年末にrust言語を発見した時、同時に発見していた言語、nim。
rust言語はそれから3ヶ月弱で挫折しました。orz
。。。時は流れて。。。ようやくnim言語のことを思い出した。



公式FAQ
https://nim-lang.org/faq.html

特徴
Pascal/Delphi/Lazarus風味
    公式FAQでは影響が強い順に以下となっている
     Modula3, Delphi, Ada, C++, Python, Lisp, Oberon.
    緑色の言語がPascal系。

Pascal風味なところ:
    Type宣言、Set型、case文の書式、関数内関数、
    整数の割り算は「div」、余りは「mod」、
    シフト命令は 「shl, shr」、
    論理演算子は、「xor, and, or, not」、
    等々、Pascalと同じだ。

Python風味なインデント構文
    "スペース文字"を使ってインデント構文を形成するのはPythonと同じ。
    インデント量は自分の場合「4」にした。
    公式系では「2」。
    さすがに「2」は、みずらくて やだなぁ。。。orz
    https://nim-lang.org/docs/manual.html#lexical-analysis-indentation

Pythonのpass
    https://nim-lang.org/docs/manual.html#statements-and-expressions-discard-statement
    Pythonでたまに使う「pass」はnimでは「discard」になる。
    空っぽの関数は以下になる。
proc foo() = discard
ちなみに、この空き関数を関数テーブルに入れると意味不明のエラーが出て、 これが原因だと気づくのに時間がかかった orz 変数は、すべて初期化済み https://nim-lang.org/docs/manual.html#statements-and-expressions-var-statement プログラムが開始した時点で、全てのグローバル、ローカル変数は 規定値で初期化済みである。規定値が何かはリンク先を参照。 例えば、数値型は0,文字列は「""」, シーケンス(seq)は「@[]」で初期化される。 この初期化動作を回避させることもできる。 result変数は「定義済みで初期化済み」 https://nim-lang.org/docs/manual.html#statements-and-expressions-return-statement 値を返す関数は、関数内で暗黙的に「result変数」が"返り値と同じ型"で定義済みで、 且つ、初期化済みである。(注1) 初期化値は上と同じ。 (注意点:result変数を関数内に於いて自前で定義してしまうと、 正しい値を返さないのでやってはいけない。)
proc intAdd(n:int): int = for i in 1..n: result += i # 1からnまで加算して返す proc strAdd(s:string): string = for i in 1..3: resutl &= s # resut = result + s と同じ意味
以下は全て正しく動作し、同じ値を返す。 「...」は、他の文があってもなくても良いという意味。
(1) proc add(x,y:int):int = ... return x + y (2) proc add(x,y:int):int = ... result = x + y (3) proc add(x,y:int):int = ... result = x + y ... return result (4) proc add(x,y:int):int = ... result = x + y ... return (5) proc add(x,y:int):int = ... x + y
(3)と(4)は表現がしつこすぎて使わないと思う。 戻り値のある関数呼び出しで、"戻り値を使っていない"と怒られた https://nim-lang.org/docs/manual.html#statements-and-expressions-discard-statement 呼び出し側で戻り値を使わない時は「discard」を付けないとコンパイルエラーになる。
定義時: proc add(x,y:int): int = x + y 呼び出し時: discard add(2,3)
この動作仕様は回避可能。 関数内のスタティック変数は{.global.}で定義 C言語でたまに使う「変数の"関数内static宣言"」は、nimでは
proc foo() = var 変数名 {.global.} = 5 ... ...
の様に書く。 関数名、変数名、等々の命名規則 https://nim-lang.org/docs/manual.html#statements-and-expressions-if-expression これが結構変わっていて、Pascal系の「大文字、小文字は区別しない」の 類似型と思われる。その規則はこう、 (1) 大文字、小文字は区別しない。但し、最初の一文字目を除く。 (2) "_"(アンダースコア)は無視する。 なので以下の2つの識別子は同じものと見なされる。
get_outque_count() getOutQueCount()
以下は違うものと見なされる。
var barbar = 1 var Barbar = 1
これについてのスレッドがこちら、 https://forum.nim-lang.org/t/4388 ifとcaseは値を返せる ifとcaseは式でもあるので値を返せる。 elif節は省略可だが、else節は省略できない。
var y = 10 let x = if y > 3: 複文も可 返す値 elif y == 1: 複文も可 返す値 else: 複文も可 返す値
実はblockも値を返せる manualでは探せなかったけど、Web上では見つかった。 関数内関数 普通に記述できるけど、manualでは確認できなかった。 (注1) Pascalの場合、result変数は未初期化。
posted by Copyright (C) avrin All Rights Reserved. at 10:01| Comment(0) | nim言語 | このブログの読者になる | 更新情報をチェックする
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。