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言語 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

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