(続)IchigoJamで32x16ドットマトリクスLEDを制御する

はじめに

前回の記事では結局BASICでは、

IchigoJamのBASICからでは速度的につらかった。本気でやるときは機械語でよろしくすべし。

ということになったので、今回、機械語でよろしくすべく色々やってみたことをここに記す。

rohi.hatenablog.com

IchigoJam-FANに投稿した動画は、以下のプログラムを実行する前にvideo 0も実行している。

www.facebook.com

どこをマシン語で?

前回記事のBASICソースで言うところの内側16回ループのところ。ここは16クロック分のデータを出力するところ。ここをIchigiJam BASICで言うところの仮想アドレス#700からのマシン語ルーチンで実現するようにした。

外側の16回ループは16ライン分のループで、こちらはBASIC側で回している。

マシン語ルーチンへは前回記事で言うところの変数c, d, eに相当する3つの値を渡したいところ。ちょうど1.1beta6で追加された第2パラメータを使って、配列の特定位置([33], [34], [35])をマシン語ルーチン側から参照するようにした。

プログラム

BASIC側のプログラム。省メモリ対策として空白なし、マルチステートメント化、16進は短く10進表現にといったことをしているので見にくくなっている。

10 fori=0to5:outi,0:next
20 poke#700,132,35,27,1,202,90,128,35
30 poke#708,91,2,147,64,16,32,26,74
40 poke#710,112,181,140,90,25,74,137,90
50 poke#718,27,12,26,4,18,20,24,77
60 poke#720,210,23,24,78,42,64,50,128
70 poke#728,34,4,18,20,1,212,0,34
80 poke#730,0,224,19,74,20,78,50,128
90 poke#738,10,4,18,20,1,212,0,34
100 poke#740,0,224,15,74,17,78,50,128
110 poke#748,0,38,17,74,91,4,100,4
120 poke#750,73,4,1,56,22,128,27,12
130 poke#758,36,12,9,12,8,78,21,128
140 poke#760,0,40,218,209,11,75,12,74
150 poke#768,30,128,22,128,16,128,24,128
160 poke#770,112,188,2,188,8,71,192,70
170 poke#778,66,8,0,0,68,8,0,0
180 poke#780,255,15,0,0,4,0,1,80
190 poke#788,8,0,1,80,16,0,1,80
200 poke#790,32,0,1,80,0,4,1,80
210 poke#798,0,8,1,80
220 let[0],0,#6300,#9480,#9480,#7480,#1300,#6000,0
230 let[8],0,0,#8000,#4000,#4000,#4000,0,0
240 let[16],#e040,#4642,#4970,#484a,#484a,#e64a,0,0
250 let[24],#1c00,#0800,#08c6,#0925,#0925,#30d5,0,0
260 fori=0to15:[32]=i:[33]=[i]:[34]=[i+16]:u=usr(#700,0):next:goto260

10行目、このプログラムで使用するポート0〜5にとりあえず0出力させている。端子が他の用途に使われていたかもしれないので、念のための初期化のつもり有効性は未確認。 -->「先頭のOUTn,0、有効です」との見解をIchigoJam作者福野さんからいただきました。

20〜210行目、16クロック分を出力するためのマシン語ルーチン。アドレス#700からの配置。

220〜230行目、前回記事の110〜120行目に相当。

240〜250行目、同じく前回記事の130〜140行目に相当。

260行目、16ライン分のループ。マシン語ルーチンへは3つの16ビット値を渡す必要がありBASICの配列[32]、[33]、[34]を使用している。16行表示したら先頭行に戻るべくgotoしている。

マシン語ルーチン側のプログラム。これをbin2pokeしたものが、上記BASICソースの20〜210行めのpokeとなる。

#include <stdint.h>

#define GPIO1_BASE 0x50010000
#define GPIO1(pin) *(volatile uint16_t *)(GPIO1_BASE | (1 << ((pin) + 2)))
#define SET 0xfff
#define RESET 0

int16_t usr_calc(int16_t val, void *mem)
{
  uint16_t *array = (uint16_t *)(mem + 0x800 + sizeof(int16_t) * 32);
  uint16_t row = 1 << *array;
  uint16_t col1 = *++array;
  uint16_t col2 = *++array;
  int i;
  
  for (i = 0; i < 16; i++) {
    GPIO1(0) = (row & 0x8000) ? SET : RESET;
    GPIO1(1) = (col1 & 0x8000) ? SET : RESET;
    GPIO1(2) = (col2 & 0x8000) ? SET : RESET;
    row <<= 1;
    col1 <<= 1;
    col2 <<= 1;
    GPIO1(3) = RESET;
    GPIO1(3) = SET;
  }
  GPIO1(8) = SET;
  GPIO1(9) = SET;
  GPIO1(9) = RESET;
  GPIO1(8) = RESET;

  return 0;  /* success */
}

GPIO1のポート0〜3、8〜9を使う。BASICの配列[32]、[33]、[34]へは、関数の第2パラメータをベースに、配列格納領域の先頭0x800を加算し、さらに[0]〜[31]を飛ばすべくちょっと足し算している。結果、変数arrayが[32]を指すところから始まる。

マクロGPIO1()はポート1_nのnをパラメータとして、アドレス部でマスクを実現する方法を使ってアクセスしている。書き込み値を0xfffとしているがマスクとANDするのでどれか1ビットしか操作されない。

関連

今回の「機械語でよろしくすべし」を実現するために、CソースコードからBASICのpoke文に変換するスクリプトとドライブするMakefileを以下で公開している。

github.com

IchigoJamで32x16ドットマトリクスLEDを制御する

はじめに、そして結論

大阪日本橋のデジットで売られている32x16ドットマトリクスLEDをIchigoJamから制御するお話。IchigoJamのBASICからでは速度的につらかった。本気でやるときは機械語でよろしくすべし。

準備

32x16ドットマトリクスLEDはデジットに買いに行ってもいいし、上記の共立エレショップから通販してもいい。このマトリクスLEDモジュールは簡易資料/PDFにあるように10ピンのコネクタが背面にある。このコネクタにつなぐケーブルもついでに購入しておくと便利。ケーブルは適当なところで切断して、各線をピンヘッダに半田付けしておくとブレッドボードに接続しやすくなる。

IchigoJamからはマトリクスLEDモジュールのSIN_1, SIN_2, SIN_3, CLOCK, LATCH, ENABLEの計6本の信号をOUT1〜6端子で制御することにする。 マトリクスLEDモジュールの電源はVCC(ロジック部電源)とVLED(LED駆動電圧)の二つが必要でともに5Vとなっている。IchigoJamのVCCは3.3Vだと思うので別のところから5Vを供給することにする。

制御方法

このマトリクスLEDモジュールは、SIN_1〜3にそれぞれデータを出力してCLOCKを立ち上げること16回。その後一旦ENABLEを非アクティブにして、LATCHでデータ更新、再度ENABLEをアクティブにする。これでSIN_1で指定した行の1ライン分(32ドット)が出力されるようになっている。16ラインあるのでこれを16回繰り返すと32x16となる。

プログラム

10 rem initialization
20 out 6,1
100 rem data 'Ichigo Jam'
110 let [0], #0000, #6300, #9480, #9480, #7480, #1300, #6000, #0000
120 let [8], #0000, #0000, #8000, #4000, #4000, #4000, #0000, #0000
130 let [16], #e040, #4642, #4970, #484a, #484a, #e64a, #0000, #0000
140 let [24], #1c00, #0800, #08c6, #0925, #0925, #30d5, #0000, #0000
200 rem main
210 for i=0 to 15
220 c=1<<i
230 d=[i]
240 e=[i+16]
250 for j=0 to 15
260 out 1,c&#8000
270 out 2,d&#8000
280 out 3,e&#8000
290 c=c<<1
300 d=d<<1
310 e=e<<1
320 out 4,0
330 out 4,1
340 next
350 out 6,1
360 out 5,1
370 out 5,0
380 out 6,0
390 next
400 goto 210

20行目で一旦ENABLEを非アクティブにしておく。 110〜120行目が画面左半分の16x16の表示データ。配列の[0]が最上行、[15]が最下行。 130〜140行目が画面右半分の16x16の表示データ。配列の[16]が最上行、[31]が最下行。

ちなみにこんなデータはスプレッドシート的なものでこんな感じに作ってみた。緑が左側半分の16x16、黄色が右半分の16x16。 f:id:rohi:20150831141348p:plain

210行目から390行目までのFORループで16行分のループ。変数iが何行目かを示している。 220行目の変数cはSIN_1に出力するデータ。今回のループで出力する位置がHになるように1をi回左シフトしている。 230行目の変数dはSIN_2に出力するデータ。配列から画面左半分の1行分である16ビット値を取り出している。 240行目の変数eはSIN_3に出力するデータ。配列から画面右半分の1行分である16ビット値を取り出している。 250行目から340行目までのFORループも16回のループだが、こちらはラッチの1回分である16ビット分の送出のためのループ。変数c, d, eは左方向に1ビットずつシフトして最上位ビットが0以外なら対応するSIN_*にHを出力し、0ならLを出力する。 320〜330行目でCLOCKをL→HすることでSIN_1〜3のデータを取り込ませる。 350行目で一旦ENABLEを非アクティブにして360〜370行目でLATCHさせ、380行目でENABLEをアクティブにする。これで1行分の表示が更新される。 390行目のnextは変数iのループのためのnext。 400行目でこの処理の先頭に戻して無限ループとしている。

結果

とても遅くて1画面更新するのに約4秒かかっている。それを16行分に分割した時間ずつ、各1行が点灯し他の行が消灯しているという状況。

ちなみに比べてもしょうがないけどSTM32F4-Discoveryで作ったCプログラムでは、1ms周期で割り込みを起こさせてそこで1行分の更新をするようにしている。1画面に16msかかる感じなのでちらつきは感じない。 IchigoJamでの動作はちらつきとかそういうレベルではないので、BASICからの制御はちょっと使えない感じ。 複数行に同じ表示をする場合はSIN_1で複数ビットをHにすればよいので、例えば全面点灯/全面消灯みたいなものなら、1画面の点灯に上記プログラムの1行分の処理でまかなえる。なので、最短の点滅周期だと4秒÷16×2くらいでいけると予想する。

STM32CubeMXをMacで使ってみる

それはなに?たべれるの?

食べ物ではない。

まずは結論

STM32CubeMXをLinuxにインストールするという記事5V: Installing STM32CubeMX on Linuxを見つけたのでMacでやってみたらするっと動いたよ、というのが今回の結論。

手順

最初にインストーラを取ってくる。このページの下の方の[Download]ボタンから。

SetupSTM32CubeMX-4.7.1.exe

 ファイル名からわかるように、これはWindowsアプリである。file SetupSTM32CubeMX-4.7.1.exeしても...

SetupSTM32CubeMX-4.7.1.exe: PE32 executable for MS Windows (GUI) Intel 80386 32-bit

...ほらね。

これはインストーラなので、実行することで実際のSTM32CubeMX.exeをどこかにインストールすることができる。インストーラjavaで書かれているがlaunch4jというツールによりexe化されている。STM32CubeMX.exeも同様にjavaで書かれていてlaunch4jでexe化されている。

後追いなので手を抜きます

記事ではインストーラexeをunzipして、マニフェストファイルに書いてあるメインクラス名を調べて、それを実行している。この実行によりインストーラが動作し、任意のディレクトリにSTM32CubeMX.exeやらがインストールされる。
STM32CubeMX.exeもunzipして同じ要領でメインクラス名を調べてjavaに食わせて実行するとSTM32CubeMXが動作することになっている。

知の高速道路を踏み台にしている我々は、この記事から実行に必要なメインクラス名をすでに知っているので、わざわざunzipする必要はない。

java -cp SetupSTM32CubeMX-4.7.1.exe com.izforge.izpack.installer.bootstrap.Installer

...しまった、管理者権限がないよ! 的なメッセージダイアログが出てOKボタンしか押せない。押したらプログラムは終了する。

管理者権限付きで再度実行。

sudo java -cp SetupSTM32CubeMX-4.7.1.exe com.izforge.izpack.installer.bootstrap.Installer 

普通にインストーラが起動されてインストール先をきいてくる。指示に従って/Application/配下っぽいところでもいいし、どこかのディレクトリを指定してもよい。

どこか手元のディレクトリにインストールする場合、ファイルがのオーナがrootになっているのがいやなので後でchownしておいてもよい。

インストール先ディレクトリに移って、STM32CubeMXを起動する。

java -cp STM32CubeMX.exe com.st.microxplorer.maingui.IOConfigurator

ヘルプどおりに、もしくはネットのどこかにある使い方メモをたよりにSTM32CubeMXを使うことができる。

クロックやらピン配やらを思う存分コンフィグして、コード生成を実行することができる。

次は、なんかプログラムをarm-none-eabi-gccでビルドするところから。

さいごに

javaすごい! "Write once, run anywhere"である。