2017年12月09日

NUCLEO-F767ZIのMicroPython用Ethernetドライバが動きました

ether02.png

先日着手したNUCLEO-F767ZIのオンボードイーサネットをMicroPythonで動かすためのドライバですが、ようやく動きました。
少しコードを整理したら公開する予定です。
途中で少しデバッグに苦労しましたが、基本的には自分のミスや知識不足が原因で、終わってみればあまり厄介なポイントは無かったように思います。

技術資料としてはUM1974が必須です。この資料のP28-29にEthernet周りのI/Oが記載されています。
CPUにはMAC層は内蔵されていますがPHYチップは外付けで、ドライバはCPUとPHYチップの結線およびPHYチップの仕様に従わなければなりません。もっとも、そのコード自体はSTM32CubeMXに付属するサンプルに含まれています(といってもサンプルは一つしか入っていないのですが)。今回もそのコードを参照しました。

また、TCP/IPとしてLwIPを使っていますので、その資料としてUM1713が有用です。LwIPそのものについての解説も付属しています。

この資料にあるとおり、LwIPにはRaw、NetConn、Socketという3種のAPIがあり、Raw以外の2つはスレッド機能を持つOSが必要となります。MicroPythonはLwIPをRaw APIで使用しています。(上記のSTM32CubeMX付属のサンプルはNetConnを使っています。)
LwIPをEthernetと接続するには、パケットが到着したかどうかをポーリングし、届いたパケットをLwIPに渡す処理を定期的に行う必要がありますが、このあたりは既にWIZNet用ドライバのお手本があります。

ですので、結局は初期化とパケットの読み書きを、STM32CubeMXのサンプルを参考に書くだけです。
・・・という見通しは実は初期段階から見えていたのですが、実際に着手してみると、ちゃんと動くようになるまで何日もかかりました。
やはりマイコンはデバッグがなかなかしんどいなあとあらためて思いました。
ラベル:MicroPython
posted by boochow at 23:03| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2017年12月01日

NUCLEO-F767ZIのMicroPython用Ethernetドライバ

ether01.png

昨日はサンプルアプリのWebサーバを動かしてみたNUCLEO-F767ZIですが、やはりMicroPythonからインターネット接続できるようにしたいところです。

しかし、以前も探してみたことがあったのですが、MicroPythonでNUCLEO-F767ZIのオンボードイーサネットを使うドライバは、現時点ではまだ無いようです。
だめもとで、先日のWIZnet5K+LwIPのドライバと、STM32CubeMXに付属のサンプルコードをを参考に、自分で作ってみることにしました。

中身がまだよく分かっていませんが、これらのコードを適当に切り貼りした結果、どうやらnetworkモジュールの下にEthernetドライバクラスを追加することはできました。
といっても、オブジェクトが生成できただけで、まだまともに動いてはいないのですが。
ラベル:MicroPython
posted by boochow at 00:57| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2017年11月30日

NUCLEO-F767ZIでWebサーバ


NUCLEO-F767ZIのEthernet端子はMicroPythonからは使えないので、テストのために付属のアプリケーションを動かしてみました。

以前ちょっとだけ使ってみたSTM32CubeMXと一緒に配布されているものです。
C:\Users\ユーザ名\STM32Cube\Repository\STM32Cube_FW_F7_V1.8.0\Projects\STM32F767ZI-Nucleo\Applications\LwIP\LwIP_HTTP_Server_Netconn_RTOS\SW4STM32

に入っています。(入っていない場合は、CubeMXのメニューのHelp→Install New Librariesから、最新の「Firmware Package for Family STM32F7」を選択してインストールします。)
これを、SW4STM32のImport...からGeneral→Existing Projects into Workspaceでワークスペースに追加してビルドします。

デフォルトではDHCPで動作しますので、動作しているIPアドレスのアタリをつける必要があります。

とりあえずEthernetが正常に動作していることは確認できました。
STM32F7-http-server.png


STM32CubeMXですが、このNUCLEO-F767ZIのように多機能なボードになってくると、初期設定を行うコードを自動生成してくれるのはそれなりに意味はあるかもしれません。
先日のRaspberry Piのベアメタルプログラミングでも、GPIOのモードを適切に設定するにはマニュアルを調べなければなりませんでしたが、そのへんがGUIで選択していくだけで済んでしまうのは、慣れると便利な気がします。
posted by boochow at 01:07| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2017年11月27日

NUCLEO-F767ZIを購入

nucleo-f767zi-1.jpg

STM32のボードは以前購入したNUCLEO-F401REを使っていましたが、結構楽しめることが分かったので、上のランクのCortex-M7ベースのボードを買ってみました。
何種類かあるのですが、価格はどれも同じなので数字が一番大きいNUCLEO-F767ZIにしました。
CPUはF767ZI、クロックは216MHz、SRAMは512KB、フラッシュは2MBで、12bitのADCとDAC、4 I2C、4 UART、6 SPIとリソースも豊富です。

早速MicroPythonを入れてみました。
MicroPythonで動くベンチマークテスト的なものがフォーラムの以下のページにあったので

Benchmark comparison of MicroPython boards - MicroPython Forum

試してみたところ、こんな数値になりました。
Pystone(1.2) time for 500 passes = 152 ms
This machine benchmarks at 3289 pystones/second

上記のページにある他のボードは最速でも2212なので、5割増しのスピードです。
ちなみにESP32だと
Pystone(1.2) time for 500 passes = 850 ms
This machine benchmarks at 588 pystones/second
という結果でした。

Ethernetも搭載されているのですが、現時点ではMicroPythonでサポートされていないのが残念です。
nucleo-f767zi-2.jpg
posted by boochow at 22:49| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2017年11月23日

STM32のMicroPythonでlwIPを使う

lwip-wiznet5500.png
現在のSTM32用のMicroPythonでは、TCP/IPとして利用できるのはWIZnetやCC3000などのTCP/IPモジュールだけです。
これに対して、以前の記事で、WIZnetのTCP/IPモジュールをイーサネットインタフェースとして使い、TCP/IP実装はlwIPを用いるような実装が進められていることを書きました。(lwIPはオープンソースの軽量なTCP/IP実装です。)
ただし、現時点ではこのコードはESP32/ESP8266のMicroPythonでないと動かないと思います。
urequestsは内部でsocket.readline()を使用していますが、CC3100やWIZnet5Kなど、上記以外のNICではreadline()が実装されていないからです。

そのため、WIZnet5Kに関してはTCP/IP実装はlwIPを使用し、WIZnet5Kは単なるイーサネットインタフェースとして使用するようなMicroPython側のドライバ実装が現在テスト中のようです。

stm32: Incorporate lwip stack and use Wiznet in MACRAW mode by dpgeorge ・ Pull Request #3379 ・ micropython/micropython

ただ、TCP/IPをメインCPUで処理するため、WIZnet5Kのソケット機能を使う場合よりも速度は低下するようです。

上記を書いた後、チューニングが進んでかなり高速化されたようなので、試してみることにしました。
この方式だと、WIZnetを使う場合と比べて、socketの機能がフル実装されますので、上に書いたようにurequestsなどの便利なモジュールが利用できるようになるというメリットがあります。

この実装はDamien George本人が行っていますが、現時点ではマスターブランチへのマージは行われていません。そのため、このブランチをダウンロードしてコンパイルする必要があります。
手順は以下の通りです。

まず、本家ではなくDamien GeorgeのMicroPythonリポジトリをcloneします。
mkdir micropython-lwip
cd micropython-lwip/
git clone https://github.com/dpgeorge/micropython.git


このリポジトリは沢山のブランチがありますので、ブランチの一覧を見ます。
cd micropython/
git branch -r


表示された中から、stm32-lwip-wiznetブランチを選択します。
git checkout -b stm32-lwip-wiznet origin/stm32-lwip-wiznet


ビルドします。
git submodule update --init
make -C mpy-cross
make -C ports/stm32 MICROPY_PY_WIZNET5K=5500 BOARD=NUCLEO_F401RE CROSS_COMPILE=~/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi-


追記:以下の事象をリポートしたところ、速攻で修正されたので、現在は以下の修正は必要なくなっています。

すると、以下のようなエラーが出ました。
LINK build-NUCLEO_F401RE/firmware.elf
build-NUCLEO_F401RE/main.o: In function `rand':
main.c:(.text.rand+0x0): undefined reference to `rng_get'
build-NUCLEO_F401RE/lib/lwip/src/core/dhcp.o: In function `dhcp_create_msg':
dhcp.c:(.text.dhcp_create_msg+0x30): undefined reference to `rng_get'
build-NUCLEO_F401RE/lib/lwip/src/core/ipv4/igmp.o: In function `igmp_delaying_member':
igmp.c:(.text.igmp_delaying_member+0x1e): undefined reference to `rng_get'
build-NUCLEO_F401RE/lib/lwip/src/core/ipv4/igmp.o: In function `igmp_joingroup':
igmp.c:(.text.igmp_joingroup+0x72): undefined reference to `rng_get'
Makefile:447: ターゲット 'build-NUCLEO_F401RE/firmware.elf' のレシピで失敗しました

リンクに失敗しているようです。rng_getが見つからないと言われています。

ということでちょっと調べたところ、以下のことが分かりました:

・lwIPは、移植の際に32bitの乱数を得る関数LWIP_RAND()を#defineで与える必要がある。乱数がいいかげんだと脆弱性につながるので、あまり適当な関数を与えないほうが良い。
・今回のMicroPythonの実装では、LWIP_RANDは ports/stm32/lwip-include/lwipopts.h の中で与えており、その定義は以下のようである。
extern uint32_t rng_get(void);
#define LWIP_RAND() rng_get()

・このrng_get()の実体は ports/stm32/rng.c で定義されており、その実装はHAL_RNG_GetRandomNumber()を呼び出すだけである。
・ただし、rng.cの中身全体が #if MICROPY_HW_ENABLE_RNG で囲われているので、rng_get()がリンクエラーになったということはMICROPY_HW_ENABLE_RNGが設定されていない可能性が高い。

ということで、ports/stm32/boardsの各ボードでどのようになっているか調べてみます。
cd ports/stm32/boards/
grep MICROPY_HW_ENABLE_RNG */mpconfigboard.h

結果を見ると、ESPRUINO_PICO、PYBLITEV10、STM32F411DISCで値が0になっているほか、F401REでは値の設定もされていません。
F401REがハードウェア乱数発生器を持っているのであれば1、持っていないのであれば0に設定すべきです。
ハードウェア乱数発生器の有無は、STM32のHALの実装を見ると分かります。
micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src の中にある、stm32f4xx_hal_rng.c を見てみます。
すると
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defi
ned(STM32F417xx) ||\
defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defi
ned(STM32F439xx) ||\
defined(STM32F410Tx) || defined(STM32F410Cx) || defined(STM32F410Rx) || defi
ned(STM32F469xx) ||\
defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx) || defi
ned(STM32F412Rx) ||\
defined(STM32F412Cx) || defined(STM32F413xx) || defined(STM32F423xx)

となっていて、残念ながらF401REは対象外のようです。
STMicroのアプリケーションノート「AN4230」を見ても、やはりF401REにはハードウェア乱数発生器は載っていないように見えます。(このアプリケーションノート自体は、生成された乱数が暗号学的乱数になっているかテストしたレポートです。)

従って、F401REではLWIP_RAND()に用いる乱数発生器をソフトウェアで実装する必要があります。もちろん暗号学的乱数であることが望ましいですが、MicroPythonにそこまで求めるのはどうかという気もします。

そもそも、乱数生成のソフトはMicroPythonにもともと入っている可能性が高いのではないか? と思い、調べてみたところ、以下が見つかりました。

Pseudo-random number generator ・ Issue #965 ・ micropython/micropython

yasmarangという乱数生成の関数が、MicroPythonのurandomモジュールで実装されていますので、これをLWIP_RAND()の実体として使うことにします。
まず、ports/stm32/rng.cの最後のほうにコードを追加して、ハードウェア乱数生成器が無い場合はyasmarang()を使うようにします。

追記:このやり方だとurandomモジュールが生成する乱数列の再現性が失われてしまうため、修正されたコードではrng.cの内部にyasmarangのコードを複製して使っています。

ports/stm32/rng.c:
#if MICROPY_HW_ENABLE_RNG
//途中省略
MP_DEFINE_CONST_FUN_OBJ_0(pyb_rng_get_obj, pyb_rng_get);

#else

extern uint32_t yasmarang(void);

uint32_t rng_get(void) {
return yasmarang();
}

#endif // MICROPY_HW_ENABLE_RNG

また、yasmarang()はstatic宣言されていますので、これを外部から呼び出せるようにstaticを外します。

extmod/modurandom.c
//STATIC uint32_t yasmarang(void)
uint32_t yasmarang(void)
{


これでリンクが通るようになりました。
使い方ですが、以前のWIZNET5Kと結線は全く同じですが、ソフトウェアは完全互換ではなく微妙に違います。
サンプルが ports/stm32/wiznet_connect.py に入っているのでその通りやればOKです。
明示的にactiveにしたりdhcpでアドレス設定させたりという処理が必要になるようです。

SPI2を使う結線の場合のサンプルです。
例によってスターウォーズ視聴です。
DHCPのアドレス取得など、WIZnet側に任せる場合と比べて初期化にちょっと時間がかかる印象がありますが、F401REを使う場合は、通信速度自体はむしろこちらのほうが高速です。


ラベル:MicroPython
posted by boochow at 10:04| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする
人気記事