タイトルの通りで、Rustで試行錯誤するのはコンパイル、ロード、デバッグの時間がかかり、細かいエラーも出る状況で、コンパイル言語による試作がつらくてテンションが下がっている(ドローンのプロペラの風切り音がうるさすぎるというのもある)。当初の方針を変更して、制御プログラムの初期段階の試作はMicroPythonで行うことにする。
MicroPythonを再度ビルドする
$ uname -a Linux DESKTOP-TRNV8F8 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux $ pwd /home/<uid>/lang/up/stm32/src/micropython/ports/stm32 $ make BOARD=NUCLEO_F401CC V=1 DEBUG=1 arm-none-eabi-size build-NUCLEO_F401CC/firmware.elf text data bss dec hex filename 237472 12 20184 257668 3ee84 build-NUCLEO_F401CC/firmware.elf GEN build-NUCLEO_F401CC/firmware0.bin arm-none-eabi-objcopy -O binary -j .isr_vector build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware0.bin GEN build-NUCLEO_F401CC/firmware1.bin arm-none-eabi-objcopy -O binary -j .text -j .data -j .ARM build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware1.bin GEN build-NUCLEO_F401CC/firmware.dfu python3 ../../tools/dfu.py -D 0x0483:0xDF11 -b 0x08000000:build-NUCLEO_F401CC/firmware0.bin -b 0x08020000:build-NUCLEO_F401CC/firmware1.bin build-NUCLEO_F401CC/firmware.dfu GEN build-NUCLEO_F401CC/firmware.hex arm-none-eabi-objcopy -O ihex build-NUCLEO_F401CC/firmware.elf build-NUCLEO_F401CC/firmware.hex
バイナリサイズは251KBか? Flashが256KBなので、ぎりぎり収まっている。
できたら、センサは3-WireによるSPI制御なので、SPIドライバを改修して、3-Wireモードで動くようにしたい。Cで動かさないとセンサ部分の性能が出ないと思われるので。 サイズ削減のためにFileIOやFlash上のファイルシステムは使えないので、プログラムは毎回Copy&Paste等でDRONEに送り付ける必要がある。
OpenOCD起動
./bin-x64/openocd.exe -f ./scripts/interface/stlink-v2.cfg -f ./scripts/target/stm32f4x.cfg
GDBからOpenOCDに接続してファームをロード
$ pwd /home/<uid>/lang/up/stm32/src/micropython/ports/stm32/build-NUCLEO_F401CC $ /usr/local/GNUToolsARMEmbedded/4.8_2013q4/bin/arm-none-eabi-gdb.exe firmware.elf GNU gdb (GNU Tools for ARM Embedded Processors) 7.6.0.20131129-cvs Copyright (C) 2013 Free Software Foundation, Inc. (gdb) target remote localhost:3333 Remote debugging using localhost:3333 0x00000000 in ?? () (gdb) monitor reset halt Unable to match requested speed 2000 kHz, using 1800 kHz Unable to match requested speed 2000 kHz, using 1800 kHz adapter speed: 1800 kHz target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000400 msp: 0x20010000 (gdb) load Loading section .isr_vector, size 0x1184 lma 0x8000000 Loading section .text, size 0x38e14 lma 0x8005000 Loading section .ARM, size 0x8 lma 0x803de14 Loading section .data, size 0xc lma 0x803de1c Start address 0x802d968, load size 237484 Transfer rate: 32 KB/sec, 11308 bytes/write.
一応焼けた。
>>> MPY: soft reboot MicroPython v1.16-243-g8c4ba575f-dirty on 2021-10-30; STEVAL-DRONE01 with STM32F401CC Type "help()" for more information. >>> help() Welcome to MicroPython! For online help please visit http://micropython.org/help/. Quick overview of commands for the board: pyb.info() -- print some general information pyb.delay(n) -- wait for n milliseconds pyb.millis() -- get number of milliseconds Switch methods: (), callback(f) pyb.LED(n) -- create an LED object for 0bthardware random number pyb.Servo(n) -- create Servo object for servo n (n=1, -itrrupt a running program CTRL-D -- on a blank line, do a soft reset of >>> help("modules") __main__ math uctypes urandom _onewire micropython uerrno uselect builtins pyb uio ustruct cmath uarray umachine usys gc ucollect >>> dir(machine) ['__class__', '__name__', 'ADC', 'DEEPSLEEP_RESET', 'HARD_RESET', 'I2C', 'PWRON_RESET', 'Pin', 'RTC', 'SOFT_RESET', 'SPI', 'Signal', 'SoftI2C', 'SoftSPI', 'Timer', 'UART', 'WDT', 'WDT_RESET', 'bootloader', 'deepsleep', 'disable_irq', 'enable_irq', 'freq', 'idle', 'info', 'lightsleep', 'mem16', 'mem32', 'mem8', 'reset', 'reset_cause', 'sleep', 'soft_reset', >>> dir(pyb) ['__class__', '__name__', 'main', 'ADC', 'ADCAll', 'ExtInt', 'LED', 'Pin', 'RTC', 'SPI', 'Switch', 'Timer', 'UART', 'country', 'dht_readinto', 'disable_irq', 'enable_irq', 'fault_debug', 'repl_info', 'repl_uart', 'wfi']
PWMはどこに行ったのだろうか。。
MicroPythonのManualによると、、pyb.Timerを使ってPWMを構成するようだ。
pyb.Pin と pyb.Timer を参照: from pyb import Pin, Timer p = Pin('X1') # X1 は TIM2, CH1 を持ちます tim = Timer(2, freq=1000) ch = tim.channel(1, Timer.PWM, pin=p) ch.pulse_width_percent(50)
pyboard 用クイックリファレンス — MicroPython 1.17 ドキュメント
MicroPythonのPinクラス、Timerクラスを組み合わせてPWMにより4基のモータを制御するサンプル
from pyb import Pin, Timer pin_m1 = Pin(Pin.board.PB6) motor1 = Timer(4, freq=500).channel(1, Timer.PWM, pin=pin_m1) motor1.pulse_width_percent(0) pin_m2 = Pin(Pin.board.PB7) motor2 = Timer(4, freq=500).channel(2, Timer.PWM, pin=pin_m2) motor2.pulse_width_percent(0) pin_m3 = Pin(Pin.board.PB8) motor3 = Timer(4, freq=500).channel(3, Timer.PWM, pin=pin_m3) motor3.pulse_width_percent(0) pin_m4 = Pin(Pin.board.PB9) motor4 = Timer(4, freq=500).channel(4, Timer.PWM, pin=pin_m4) motor4.pulse_width_percent(0)
motor4.pulse_width_percent(0)は0%なので、これを10等にすると10%の出力になる。
PinとTimerの組み合わせは決まっており、ハードウエア仕様書か、stm32f401_af.csvを参照して正しい組み合わせを指定する必要あり。間違っているとAFエラーになる(例:ValueError: Pin(B5) doesn't have an af for Timer(1))。
Python/MicroPythonには自作モジュールを追加する仕組みがあり、それを試してみる。
ユーザ開発用サンプルのexamples/usercmoduleがあるので、これをmicropythonのソースツリーの外側に配置して、make時にUSER_C_MODULESとして指定して組み込んでみる。
$ make BOARD=NUCLEO_F401CC USER_C_MODULES=../../../mymodule V=1 DEBUG=1
sampleソースを修正して、stdroneモジュールに修正、再ビルド。実行した結果、stdroneモジュールが見えている
$ make BOARD=NUCLEO_F401CC USER_C_MODULES=../../../mymodule V=1 DEBUG=1
>>> import stdrone >>> dir(stdrone) ['__class__', '__name__', 'add_ints'] >>> stdrone.add_ints(1,2) 3
今の状態だと、stdroneモジュール内に関数が直接置かれている状態なので、、この状態からSPI3W(SPI 3Wireモード)クラス等を作って、init / read / write 等に仕立てる必要がある。