2017年10月15日

Nucleo-F401RE + Ethernet(W5100) + MicroPythonでインターネット接続

stm32-w5100.jpg


MicroPythonが動くようになったNucleo-F401REですが、ESP32と比べるとネット接続がありません。
しかし、調べてみるとこのボードにArduino用のイーサネットシールドを接続できることが分かりました。

Adding ethernet connectivity to a STM32-Nucleo

実は、私も買ったまま使っていない(このパターン多いです)Arduino用イーサネットシールドを持っており、上記の記事と同じW5100というチップを使用しているものでした。

このチップはWIZnetというメーカーの製品で、単なるEthernetコントローラではなく、TCP/IPが実装されており、単体で通信ができます。
イメージとしてはESP8266のLANバージョンみたいなものでしょうか。
ESP8266よりもかなり以前からあるシリーズで、国内でもスイッチサイエンスさんなどで扱いがあります。

一方、MicroPythonについて調べてみると、このW5000シリーズがドライバとして組み込めることが分かりました。
ただ、現時点ではサポートされているのはW5200とW5500のみでした。

Ethernet Adapter for PyBoard Which? - MicroPython Forum

なおW5500とSTM32の組み合わせはごく最近バグフィックスされたようです。

drivers: WIP getting WizNet5500 working with stm32 port. by dpgeorge ・ Pull Request #3362 ・ micropython/micropython

残念ながらMicroPythonにはW5100のドライバは入っていませんでしたが、W5200やW5500のドライバを調べてみると、WIZnetが開発したドライバを利用していました。
そこで、WIZnetのGitHubをのぞいてみると、W5100のドライバも用意されていました。
また、これらチップの差分を吸収する抽象レイヤも用意されており、MicroPythonでもそれを使用しているようです。

ということで、「WIZnetのW5100用ドライバをMicroPythonへポーティングするのは簡単なのでは?」と思い、やってみることにしました。

利用したWIZnetのドライバは、現時点での最新版と思われるこちらのライブラリです。

Wiznet/ioLibrary_Driver: Create a repository of WIZnet ioLibrary.

ドライバの入っている「Ethernet」フォルダと、DNSなどのプロトコルを実装している「Internet」フォルダがありますが、Ethernetフォルダだけ使用しました。
MicroPythonではInternetフォルダのDNS実装を使っているようでしたので、最初はこちらも最新のものを使おうかと思ったのですが、最新のほうはコンパイルエラーが出る(というかバグが残っている)ので、使うのをやめました。

このEthernetフォルダを、micropython/drivers/wiznet5k/Ethernetに展開します。
そして、以下のように使用するチップの指定を変更します。
../../ioLibrary_Driver/Ethernet/wizchip_conf.h wizchip_conf.h 
64c64
< #define _WIZCHIP_ 5500 // 5100, 5200, 5300, 5500
---
> #define _WIZCHIP_ 5100 // 5100, 5200, 5300, 5500


また、ドライバの抽象レイヤであるwizchip_conf.cは構造体の初期化のところで文法エラーが出るので修正します。
../../ioLibrary_Driver/Ethernet/wizchip_conf.c wizchip_conf.c 
169,172c169,172
< wizchip_cris_enter,
< wizchip_cris_exit,
< wizchip_cs_select,
< wizchip_cs_deselect,
---
> {wizchip_cris_enter,
> wizchip_cris_exit},
> {wizchip_cs_select,
> wizchip_cs_deselect},
176,177c176,177
< wizchip_bus_readdata,
< wizchip_bus_writedata,
---
> {{wizchip_bus_readdata,
> wizchip_bus_writedata}},


WIZnetチップはソケットのインタフェースが実装されています。
関数名はconnect、listenなどよく知られたAPIが使われていますが、もともとMicroPythonに組み込まれていたものに倣って、ソケット関連の関数名を変更するマクロを導入しました。

../../ioLibrary_Driver/Ethernet/socket.h socket.h
87a88,90
> // use this macro for exported names to avoid name clashes
> #define WIZCHIP_EXPORT(name) wizchip_ ## name
>
162c165
< int8_t socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag);
---
> int8_t WIZCHIP_EXPORT(socket)(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag);


以下略しますが、関数名socket, close, listen, connect, disconnect, send, recv, sendto, recvfrom, ctlsocket, setsockopt, getsockoptについて全て上記の変更を行います。
また、socket.cについても同様の変更を行います。
関数の宣言部だけでなく、内部でclose()を呼び出しているところがあるのでそこも変更が必要です。

また、&演算子の使い方でコンパイル時にエラーが出るので、以下を修正しました。
520,521c520,521
< if((taddr == 0) && (getSn_MR(sn)&Sn_MR_MACRAW != Sn_MR_MACRAW)) return SOCKERR_IPINVALID;
< if((port == 0) && (getSn_MR(sn)&Sn_MR_MACRAW != Sn_MR_MACRAW)) return SOCKERR_PORTZERO;
---
> if((taddr == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_IPINVALID;
> if((port == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_PORTZERO;


W5100のドライバは全く変更する必要はありません。ただ、インクルードパスの都合でw5100.hを少しだけ修正します。
../../../../../ioLibrary_Driver/Ethernet/W5100/w5100.h w5100.h
44c44
< #include "wizchip_conf.h"
---
> #include "../wizchip_conf.h"


また、application/dns/フォルダのdns.hも同様に1行だけ修正します。
diff dns.c dns.c~
57c57
< #include "../../Ethernet/socket.h"
---
> #include "../../ethernet/socket.h"


ここでMicroPythonの内部構造を少し書いておきます。
STM32関連の実装は「ports/stm32」に集められています。
networkモジュールはmodnetwork.cで実装されていますが、種類の異なるNICごとの実装は別ファイルになります。
WIZnet5Kシリーズのための実装はmodnwwiznet5k.cにあり、この実装はSTM32のHALライブラリを介してSPIをたたいてWIZnet5Kのデバイスと通信しています。

WIZnet5KドライバのAPIが、もともとMicroPythonに含まれていたものと若干変わっているので、これらのドライバを呼び出すmodnwwiznet5k.c側の修正も必要になりました。
具体的には、SPIのコールバック関数として、1バイトずつの送受信を行うものが新たに必要になりましたので追加しました。
また、W5000シリーズのレジスタの呼び名が若干変更になっています。
diff modnwwiznet5k.c modnwwiznet5k.c.org 
41,42c41,42
< #include "Ethernet/wizchip_conf.h"
< #include "Ethernet/socket.h"
---
> #include "ethernet/wizchip_conf.h"
> #include "ethernet/socket.h"
74,87c74
< STATIC uint8_t wiz_spi_read(void) {
< uint8_t c;
< HAL_StatusTypeDef status = HAL_SPI_Receive(wiznet5k_obj.spi, &c, 1, 5000);
< (void)status;
< return c;
< }
<
< STATIC void wiz_spi_write(uint8_t c) {
< HAL_StatusTypeDef status = HAL_SPI_Transmit(wiznet5k_obj.spi, &c, 1, 5000);
< (void)status;
< }
<
< //STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) {
< STATIC void wiz_spiburst_read(uint8_t *buf, uint16_t len) {
---
> STATIC void wiz_spi_read(uint8_t *buf, uint32_t len) {
92,93c79
< //STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) {
< STATIC void wiz_spiburst_write(uint8_t *buf, uint16_t len) {
---
> STATIC void wiz_spi_write(const uint8_t *buf, uint32_t len) {
381d366
< reg_wizchip_spiburst_cbfunc(wiz_spiburst_read, wiz_spiburst_write);
424c409
< printf(" %02x", WIZCHIP_READ(WIZCHIP_OFFSET_INC(WIZCHIP_SREG_BLOCK(sn), i)));
---
> printf(" %02x", WIZCHIP_READ(WIZCHIP_SREG_ADDR(sn, i)));


最後にMakefileです。
こちらもW5200指定になっていたのでその修正と、インクルードパスの修正をします。
diff Makefile Makefile.org 
295,297c295,297
< Ethernet/W5100/w5100.c \
< Ethernet/wizchip_conf.c \
< Ethernet/socket.c \
---
> ethernet/w5200/w5200.c \
> ethernet/wizchip_conf.c \
> ethernet/socket.c \


以上で移植作業は完了です。
makeします。

cd micropython/ports/stm32/
make BOARD=NUCLEO_F401RE CROSS_COMPILE=~/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi- MICROPY_PY_WIZNET5K=1
cd build-NUCLEO_F401RE/
cp firmware.hex /mnt/hgfs/share/
#このあとST-Link Utilityで書き込み


実際にNucleo-F401REとW5100でインターネットアクセスしてみました。
結線は下図のようになります。
stm32-w5100.png

Adding ethernet connectivity to a STM32-Nucleoにも書かれていますが、このイーサネットシールドはSPIの信号が、Nucleoボードが持っていない6ピンの端子に出ているため、配線に工夫が必要です。
私のボードは6ピン端子の足元の部分に線が露出していたので、ICクリップを使って接続しました。
ethernet-shield-spi.jpg

まずはESP32のときと同様、アスキーアートSTAR WARSに接続してみます。
SPIオブジェクトがmachine.SPIだとクラスが違うというエラーが出てしまい、pyb.SPIを使う必要がありました。
このへんはMicroPythonの問題だと思います。



ちょっとカクカクしますが、一応表示されました。

starwars.png


以下の記事によると、W5kシリーズの通信速度は、最新のW5500でDMAを使用したとき4.4Mbps、使用しないとき1.7Mbpsくらい出るようです。

STM32F401REでSPI DMAを利用してW5500の送受信性能改善 | WIZnet JP Blog

HTTPでの通信も試してみました。
これまたESP32のときと同じネタです。
今回はLCDは使わず、とりあえず通信できるかどうかの確認です。
WIZNET5Kオブジェクトはreadline()が実装されていなかったので、コードは若干変わっています。



REPLの中で呼び出すと結果が表示されます。

>>> main()
2017-10-13
JPY/USD: 112.18
>>>


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

2017年10月09日

Nucleo F401REにMicroPythonをインストールしてみた

f401re-ssd1306.jpg
最近、MicroPythonにだいぶ興味が高まっているのですが、先日動作だけ確認したNucleo-F401REにもMicroPythonが移植されていますので、試しにインストールしてみました。

ファームウェアはMicroPythonのダウンロードページからもダウンロードできますが、ファイル形式がdfuという形式で書き込み方がわかりませんでしたので、今回はソースからビルドしました。
ビルド手順はmicropython Wikiにも記載はありますが、あまり詳しく書かれていないので、フォーラムのこちらの記事を参考にしました。

How to build and run MicroPython on STM32F7-Disco board - MicroPython Forum

上記で解説されている手順では、ファームウェアの書き込みにWindows用のツール(STM32 ST-Link Utility)を使いますが、Linuxで書き込む場合には以下の記事が参考になりそうでした。

Running micropyton on STM32Nucleo-F4

ちなみにビルドに使った環境はESP32のときと同様、Ubuntu-16.04です。
手順を簡単にメモしておくとこんな感じです。
wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update/+download/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2
tar xvfj gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2
sudo apt-get -y install lib32z1 lib32ncurses5
cd micropython/ports/stm32/
make BOARD=NUCLEO_F401RE CROSS_COMPILE=~/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi-
cd build-NUCLEO_F401RE/
cp firmware.hex /mnt/hgfs/share/

最後のcpで指定している/mnt/hgfs/shareは私の環境でのWindows側との共有フォルダです。
適宜変更してWindows側へfirmware.hexを持ってきてください。

Windows側へ持ってきたファイルをST-LINKユーティリティでNucleoへ書き込みます。
ST-LINKユーティリティは以下からダウンロードできます。(メールアドレスと氏名の入力が必要です。)

STSW-LINK004 - STM32 ST-LINK utility - STMicroelectronics

MicroPythonが起動した後の操作は、uPyCraftが使えます。
ただ、残念ながらファイルのダウンロード機能が通信エラーでうまく働きませんでした。
ボード側のバッファのサイズ等、条件が違うのかもしれません。
C-eでコードを直接ペーストするぶんには問題ありません。

先日試したOLEDのSSD1306を接続してみました。
結線は以下の通りです。
OLED Nucleo-F401RE
GND GND
VCC 3V3
D0 PB13(CLK)
D1 PB15(MOSI)
RES PC5
DC PC6
CS PB12(NSS)

f401re-ssd1306.png

信号線はArduinoコネクタ(ピンソケット)ではなくMorphoコネクタ(ピンヘッダ)のほうに出ています。
ピン配置は以下のようになっています。
f401re-pins.png

Nucleo-F401REにはSPIが3つあり、SPI1はArduinoコネクタから接続できます。
しかし、SPI1はSCKを接続した時点でuPyCraftとの通信がうまくいかなくなってしまったので、SPI2を使っています。
SPI1のSCKはPA5ですが、ここにはLED2もつながっています。SPIで使うには何か設定が必要なのかもしれません。

SPI1〜SPI3に使う信号線はハードウェアで決まっており、ピンの指定はできません。
信号とピンの関係はports/stm32/boards/NUCLEO_F401RE/mpconfigboard.hを見ると分かります。
今回はSPI2なので、以下の部分が必要な情報です。
なお、NSSはCSの別名らしいです。
#define MICROPY_HW_SPI2_NSS     (pin_B12)   //              pin 16 on CN10
#define MICROPY_HW_SPI2_SCK (pin_B13) // pin 30 on CN10
#define MICROPY_HW_SPI2_MISO (pin_B14) // pin 28 on CN10
#define MICROPY_HW_SPI2_MOSI (pin_B15) // pin 26 on CN10

以下が使い方の例です。
SPI2を使用するので、SPIのIDには2を指定します。
from machine import SPI,Pin
spi = SPI(2, baudrate=20000000, polarity=0, phase=0)
oled = SSD1306_SPI(128, 64, spi, res=Pin('PC5'), dc=Pin('PC6'), cs=Pin('PB12'))
oled.text('hello',0,0,1);oled.show()

実行結果が本記事の最初の写真です。
最初のMicroPythonはSTM32F405をターゲットとしていた(Micro Python: Python for microcontrollers by Damien George − Kickstarter)だけあって、動作としては何の問題もありません。
ただ、ホビイストにとっては、NucleoボードはESP32と比較すると大きいのと、WiFiが無いのが欠点ではあります。
ラベル:MicroPython
posted by boochow at 20:45| Comment(0) | stm32 | このブログの読者になる | 更新情報をチェックする

2017年10月08日

micro:bitのハードウェアのブロック図を描いてみた

microbit-block-diagram.png

micro:bitにはCPUが2つとセンサーとLEDが搭載されていますが、それぞれがどのようにつながっているのか一目で解る図が無かったので、作ってみました。(ちなみにこの図はdraw.ioで描いています。)

micro:bitの回路図はこちらに掲載されています。

bbcmicrobit/hardware: Hardware designs for the BBC micro:bit

ただ、これはちょっと見づらいので、以下に掲載されているリファレンスデザインの回路図を参考にしました。

microbit-foundation/microbit-reference-design: micro:bit Reference Design

メインのCPUはnRF51822のほうで、それに対してKL26Zはプログラマとして動作します。

nRF51822は5×5(内部的には3×9)のLED、I2C経由でセンサ2つに接続されており、KL26ZとUARTで通信できます。また、SPIが拡張コネクタに引き出されています。
拡張コネクタのピン配列は以下のページにあります。ただしP1, P2,...の番号はコネクタのピン番号で、nRF51822のポート番号ではありません。

Schematics

一方、KL26ZはUSBポートに対してmbedとして動作し、nRF51822をSWD(Serial Wire Debug)信号でプログラムします。KL26Zの信号線は拡張コネクタには接続されていません。
また、KL26ZはUARTでnRF51822と通信しますが、この通信はUSBポートのUSBシリアルインタフェースへそのまま受け渡されます。
詳細は以下のページに解説があります。

DAPLink and the USB Interface

micro:bitはターゲットCPUとプログラマCPUが同居しているという点ではArduino UNOに似ています。
また、プログラムの書き込み方法や、オンラインでプログラムをコンパイルできるという点はmbedの資産を活用しています。
Arduinoとmbedの良いとこ取りを狙った構成と言えそうです。
posted by boochow at 15:42| Comment(0) | micro:bit | このブログの読者になる | 更新情報をチェックする

micro:bitを動かしてみた

microbit.jpg

話題になっていたので買ってみたものの、放置して積ボードになる寸前だったmicro:bitです。
ESP32+MicroPythonが思ったより面白かったので、すっかり後回しになってしまっていました。

ですが、試食してみた結果、教育用としてはかなりよくできていると思っています。
日本語サイトもよくできています。
センスが良いですよね。
microbitorg.png


あと、開発ツールをちゃんと用意しているのがすごいです。
このサイトでプログラミングの勉強をするつもりは無いのですが、どのような教材なのかは興味があります。
子供向けのプログラミングですから、ハードル高いです。
分かりやすくて、楽しく、安定して動作することが必要です。

makecodemicrobitorg.png


ハードウェアは2000円ですが、価格の割には凝ったボードだなあというのが第一印象です。
以下の概要図(公式ページより。日本語訳は私がつけました)を見ても、教材として、柔軟性やコスト、使いやすさなど考え抜いたものだということが伝わってきます。
microbit-hardware.png


図にはありませんが、micro:bitはCPUを2つ積んでいます。

ひとつはNordic nRF51822で、Bluetoothにつながっています。もう一つはKinetis KL26Zで、USBにつながっています。
このKL26Zはmbed式のインタフェースになっており、USBに接続すると8MBのUSBストレージが現れます。
ここへプログラムをドラッグ&ドロップしてmicro:bitへ転送できるようになっています。
microbit-mbed.png

一方、nRF51822のほうはスマホとBluetoothで通信してプログラムを書き込むことができます。
そのためには専用のアプリおよびBluetoothのペアリングが必要になります。
microbit-pairing1.png

micro:bitのAボタンとBボタンを押したまま、リセットする(再起動するまでABボタンは押したまま)と、ペアリングモードになります。
ペアリングモードでLEDに表示されたパターンを、スマホアプリ側に入力します。
このパターンはいつも同じなので、おそらくmicro:bit側のMACアドレスなのでしょう。
microbit-pairing2.png

入力が完了すると、ペアリングが始まります。これはmicro:bit側で表示された数字をスマホアプリ側に入力することで行います。

パターンを正しく入力した後、micro:bitのAボタンを押すと、7桁(たぶん)の数字が順に表示されます。
これをスマホ側のアプリに入力するとペアリング完了です。
この操作はモタモタしているとタイムアウトになってしまいますので、少しハードルが高いです。


なお、USB経由でWindows PCに接続するとプラグ&プレイでドライバがインストールされますが、シリアルポートはドライバが見つからずにエラーになります。
win-driver-install.png

このデバイスドライバは以下からダウンロードすることができます。

Windows serial configuration - Handbook | Mbed

というわけでとりあえず動くようになりました。

まだ実際にプログラムは書いていませんが、JavaScript + Blockの環境はエミュレータが動いていますし、Pythonの環境も完成度が高いです。

題名未設定 - makecode.microbit.org

Micro:bit - Python editor

どちらもWebの上で動作し、操作も軽快です。
使い勝手だけならArduinoやmbedより上でしょう。
大したものだと思います。
posted by boochow at 00:34| Comment(0) | micro:bit | このブログの読者になる | 更新情報をチェックする

2017年10月07日

TFT LCD(ST7735R)をMicroPythonで動かしてみた

st7735r-micropython.jpg

ESP32+MicroPythonで、SPIでOLED表示ができたので、同じくSPI接続のTFT LCDも動かしてみました。
使用したLCDはこちらの記事で使用したもので、128×160ピクセル、コントローラはST7735系です。
MicroPythonでのドライバは、おなじみのAdafruitのものがありました。

adafruit/micropython-adafruit-rgb-display: MicroPython library for RGB pixel displays.

結線は基本的には前回使用したOLEDと同様に
LCD  ESP32
VLED 3V3
RST IO17
A0 IO16(DC)
SDA IO13(MOSI)
SCK IO14(CLK)
VCC 3V3
CS IO5
GND GND
と接続してみました。

esp32-st7735r.png


ソフトウェアですが、上記のAdafruitで公開されているファイルのうち、
rgb.py
st7735.py
の2つが必要です。

まずrgb.py、次にst7735.pyを転送もしくはREPLでコピペします。
やり方は以前と同じです。

使い方の例です。冒頭の写真の表示を出力します。
from machine import Pin, SPI
spi = SPI(1, baudrate=30000000, polarity=0, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
lcd = ST7735R(spi, dc=Pin(16), cs=Pin(18), rst=Pin(17))
lcd.fill(0)
for x in range(128):
for y in range(160):
lcd.pixel(x,y,color565(x*2, y*3//2, 255-x-y*3//4))

st7735.pyではST7735とST7735Rの二つのクラスが定義されていますが、私のLCDモジュールはST7735Rのほうで動作しました。
SPIのビットレートを早くしすぎるとディスプレイのほうが追いつかない(ドットが1つ飛ばしになる)ので、もしうまく動作しない場合はbaudrateの値を小さくしてみてください。

全部Micropythonで書かれているので、さすがに実行速度はあまり早くありません。
しかし、インタープリタで実行できる有難味は捨てがたいですね。
ラベル:MicroPython
posted by boochow at 19:36| Comment(0) | ESP8266/ESP32 | このブログの読者になる | 更新情報をチェックする
人気記事