2019/09/11

MIDI 機器(リズムマシン R-8MK II)をコントロールしてみた

ギターの練習のお供にリズムマシンを使う時に、例えば8ビート1小節のパターンを延々と流しながら、というのは良くあるんではないかと思います。ただ、曲によってはAメロとBメロではパターンが違ったりするのに同じパターンで続けてしまったりするのですけど。

もちろんリズムマシンでパターンをいろいろ用意して、それを繋げて1つの曲に仕立ててカラオケがわりにそのパターンを流せば気持ちよく練習ができるとは思うんですが、まず曲を準備するのが面倒臭い。

とりあえず Aメロの基本パターン1小節、Bメロの基本パターン1小節がスイッチ1つで切り替えられるなら、まぁいいんじゃない?ということで、あまりお金をかけずにこれを実現する MIDI OUT 機器を作ってみます。

1. 簡単仕様


  • リズムマシンのターゲット Roland R-8MK II (手持ちがこれなので)
  • MIDI OUT コントローラのマイコンは micro:bit (手持ちのパーツを流用)
  • リズムマシンには A, B の2つのパターンを準備
  • リズムマシンはパターンプレイモードでパターンをあらかじめ実行
  • ボタンを押すと A, B のパターンを切り替える
もう少し機能をつけたいのですが、とりあえず最低限のやりたいことを実現します。
最初は Arduino で作ってたのですが、モーター回すのに micro:bit 使っていて、ボタンも備えられていることから micro:bit で実装してみました。

2. 実装イメージ

3.3V 電源の構成。本当に簡単簡単。4番ピンを 33ohm を通して 3.3V にプルアップ。5番ピンは 10ohm を通して UART の TX に。ここでは micro:bit の P2 を割り当てています。丁寧にやるなら 2番ピンとシールドの外側は GND に。ただし、それをやっていいのは MIDI OUT のみ。

ちなみに5V 系のマイコン例えば Arduino だったら 220ohm の抵抗を両方に使えばいいですね。


3. プログラム

シリアルの端子の送信を P2 に割り当てます。P1 はおまけ。

ボタンを押したらプログラムチェンジを送ります。プログラムチェンジは 2byte 構成。

最初の1バイトでプログラムチェンジのコード 0xC0 に CH 番号 - 1 を加えたもの。続いて、プログラム番号は 1バイトを送ります。

ブロックエディタだとバイナリーのデータを送るブロックがないので、この部分のみブロックエディタから JavaScript エディターに切り替えて入力します。ついでに関数にしてしまいます。

pins.createBuffer(2) で 2バイトの配列を準備。setNumber(変数, インデックス, 値) で配列の要素に値を設定。serial.writeBuffer() で準備した配列を送信します。


4. 実行
4.1 R-8MK II の設定

必要な設定は

MIDI -> TX CH -> 10CH

MIDI -> FUNCTION -> PGM CHANGE = ON
のみ。
パターンを二つ用意して、パターンプレイモードで START しておきます。


4.2 Play

A/B ボタンを押せばそれぞれにアサインされたパターンに切り替わります。

本当はフットスイッチを付けたいところ。デモなんで、お手軽に micro:bit のスイッチを使ってます。

CASIOPEA の Street Performer より


5. 次のステップ


  • フィルイン機能1
    A または B メロパターンを実行中にフィルインボタンを押すと、AまたはB用のフィルインパターンを1小節実行して、また元のパターンに戻る
  • フィルイン機能2
    A/B 切り替えボタンを押した時に、A->B 切り替え時フィルインまたは B->A フィルインパターンを自動的に1小節挟む
  • スタート、ストップトグルボタンの追加
  • テンポの自動取得
    フィルイン機能を入れるのにはテンポがわからないと適切なタイミングで次のパターンが入れられないと予想されます
  • 筐体とボタンの強度を上げる
    今のでは足で踏むとすぐ壊れます

というのをボチボチ実装していければいーなと思います。が、やるんだろうか。

6. 解説
ハマったところをメモがわりに解説しておきます。

6.1 MIDI 端子のピン配置

まずMIDI 規格書はこちら。CA-033 が電気的規格を理解するのに丁度いいです。
これがすごくハマりました。使う端子はたったの2つ。4番と5番。これがどちらかわからない!制作記事をみても多くが MIDI IN の回路だったりして。テスターのダイオードチェック機能を使えば MIDI IN 回路のフォトカプラの入力の極性から4番、5番端子が特定できそうなものだけれど、サージなどからの保護用のダイオードがパラレルに逆極性で入っているので、どちらもダイオードのアノード、カソードに見えてしまう。あと、MIDI IN - MIDI OUT はストレートケーブルで繋がるので、MIDI IN の 4 は MIDI OUT の 4 というように同じ番号同士がケーブルで接続されます。

結局こちらのブログの記事が助かりました。

CA-033 の規格書はどっちから見たんだ?オスコネクタ目線?? ちなみに IN 側も OUT 側も端子配置は同じ。正しくは OUT 側は 4番、5番の間に挟まれた 2番とシールドの外側は GND に落としますが、ここではサボっています。写真だと手前の空いている二つのピンがシールドで、奥の半田付けされたジャンパーピンの間のピンが2番です。ノイジーなステージでの使用を考えるならちゃんと GND に落とすべきですね。ただし IN 側はこれらのピンを GND に直接は落としません。浮かせるか、0.1uF のコンデンサを介して GND に落とします。折角フォトカプラで分離してるのに GND を直接ショートしたら意味ないということかなと思います。



6.2 MIDI OUT の回路と抵抗値

ここも最初は理解できませんでした。電流ループとか書かれてもねぇ。
結局、MIDI OUT 側の回路は、H 出力時、MIDI IN のフォトカプラ入力に 5mA 程度の電流を流し、L 出力時には電流を流さないという回路を考えればいいようです。電源電圧は 5V である必要はなく、3.3V でも良いのです。なぜならフォトカプラの入力に繋がる回路は発光ダイオードなので、そのダイオードが光る電流を流せばいいからですね。
ちなみに OUT 側の MIDI 端子 4番が電源電圧側(5V or 3.3V)と覚えれば良いです。フォトカプラのアノード側が繋がるので。

ところで抵抗はフォトカプラを動かす回路の電流制限という意味とショートした時の保護という意味で入れるようです。



VTX=5.5V RA=RC=220ohm
VTX=3.3V RA=33ohm, RC=10ohm


6.3 MIDI データフォーマットと R-8MK II


もう少し研究する必要があります。とりあえずプログラムチェンジだけ使いました。
0xC0 + (CH - 1), Pattern No. の 2byte 構成。

電池切れました。おしまい。

2019/09/08

micro:bit + LV8548MC (ステッピング モーター編)

責任感ゼロ、いじり放題の趣味のプログラミングやブログの書き込みは気楽でイイですよ。

さて、お待たせしました!ステッピング(ステッパ)モーター編です。micro:bit と LV8548MC を使って回してみます。ほとんど新しくなった LV8548MC のパッケージの解説となります。

ハードウェアの準備

モーターを除けば DC モーターと同じです。まずは前の記事を。

モーターはバイポーラ型ステッピングモーター MDP-35A を使いました。ググると仕様書が見つかると思いますが、4端子 A, A(上の線つき), B, B(上の線つき)で、A, B 二つのコイルそれぞれブレッドボードに刺さるようにピンヘッダを半田しました。


これを DC ブラシモーターのときに組んだブレッドボードに刺します。


LV8548MC の OUT1, OUT2, OUT3, OUT4 に先程のピンヘッダを刺します。
ハードの準備はこれでおしまいです。

ソフトウェアの準備

これも DC のソフトウェアで説明した通り。同じパッケージにステッピングモーターのブロックを追加しました。以下の GitHub の URL を拡張機能の追加で指定してください。詳しくは前回の記事で。

https://github.com/ttrsato/pxt-lv8548mc.git

ステッピングモーターブロックの説明

DC からいっぱい追加したので迷うかもしれませんが、最低限必要なものは STEP カテゴリーの最初の三つだけです。


ちなみに DC 用と混在できません。間違わないよう先頭に S: と書かれているのがステッピングモーター用。D: が DC モーター用です。

設定

set stepper で IN1〜IN4 の設定をすれば準備完了です。どのピンでもそれぞれが被らなければ構いません。プルダウンメニューから選んでください。全部のピンが見当たらない場合はマウスのスクロールで残りが見えると思います。
回したいモーターの仕様書をみて、A, B コイルの端子を調べて、それぞれのコイルの両端を A-A', B-B' に割り振れば良いです。

あと、Excitation(励磁)方法を選びますが、よくわからなければ 1/1(Full step, 2相励磁) を選んでおけば良いです。1/2 は(Half step, 1-2相励磁)。LV8548MC はこの2種類の励磁方法のみ選べます。マイクロステップをしたい方は別な IC を選んだ方がいいです。オンセミの Solution Kit なら、LV8702 か LV8714 ですね。
ちなみに 1/1 の場合、48ステップで軸が一回転するステッピングモーターの場合、48ステップで一周。1/2 の場合は倍の 96ステップで軸が一周します。

初めてのステッピングモータープログラム


3つのブロックを使っただけの簡単なプログラム。
ボタン A を押したら Forward 方向に48ステップ回って、Reverse(反対方向)に48ステップ回って、Forward に 24ステップ、Reverse に24ステップ回って終了します。


こんな感じ。


この run stepper ブロックは回転する方向 (Forward/Reverse) と動かすステップ数、速度を指定することができます。ms/step は 1ステップを動かす時間です。ms (1秒の1/1000) で指定します。最短は 1ms/step です。ただこの速度で動くかどうかはモーターと負荷次第です。プログラムの処理速度もどうだか。ちなみに負荷が重いなど期待した速度で回らなくなることを脱調と言います。


最後に指定しているのが stop stepper。ここでは Free(励磁無し) と Hold(励磁保持) を選択できます。run stepper では回し終わった後に Hold(励磁保持)状態になっています。電流が流れたままなので、コイルは磁力を持ち、軸は弱い力では動きません。Free ではコイルの磁力は無くなるので、軸は弱い力で回すことが出来るようになります。Hold 状態は電流を消費しますし、モーターがとても熱くなるので、位置を保持する必要がなければ Free しておいた方が良いかもしれません。

とりあえずこれらを組み合わせれば大体好きなことは出来るのではないかと思います。

残りの基本ブロック


run stepper accel と run stepper trap の二つがあります。

run stepper accel
加速、減速を実現するブロック。start の速度から stop の速度まで、段階的に steps で指定したステップ数回転します。

run accel デモ

run stepper trap
台形駆動します。台形駆動は、だんだん速く回転し、一定速度で回った後、止まる前に徐々に速度を遅くする機能です。グラフを書くと台形なのでそんな名前です。

run trap デモ

変換ブロック

残りは変換ブロックです。回す単位や、速度の単位を変えたい時に使います。物によっては STEPPER units ブロックで初期設定を入れる必要があります。
おまじないというわけではないのですが、


set steps/round を設定しておきましょう。今回使用した MDP-35A はステップ数 48 なので、48を設定します。ステップ角は 7.5° (360/48) なので、


set deg/step で設定しても同じです。これらを指定することによって、回転を角度や回転数で指定したり、速度を rpm で指定すると期待通りに動くようになります。つまり以下のブロックがようやく使えるようになるわけです。もちろん 1ステップ以下の動作は出来ないので丸められます。


これらのうち、STEPPER to steps 使うと最初のプログラムはこういう風に書き直せるわけです。


2周、1周、180°、90°など、直感的に値を設定することが出来るようになります。
応用編として、回転するステージがギアを介してステッピングモーターに繋がっていて、1 ステップで 0.1° 動くシステムなら、set 0.1 deg/step を設定すればステージの回転角度を与えて動かすことが出来ます。ちなみに set mm/step を設定しておくと、mm to steps を使えるようになります。これはスライダーなどのボールネジを使った直線駆動する機構に対する設定で、1ステップで移動する距離を mm でセットすると、mm to steps で移動したい距離を与えるとステップ数を返すようになり、上記の角度指定のように直感的な指定が可能になります。

STEPPER to ms/step は、速度の単位を変換するブロックです。
Hz や、rpm でステップの変化速度を与えたい場合はこれらを使ってください。

速度を周波数 Hz で与える

ちなみに上記のコードを実行すると、あれ?となりませんか。1Hz は 1秒間に回転する回数なので、1Hz だったら 1秒で 1回転するはずじゃんと。残念ながら 1ステップ移動する速度が 1Hz(1秒) です。一周するのに 48ステップなので遅いんです。rpm 指定も同様です。仕様です。いやいや、一周単位での Hz やら rpm で設定したいという方は Hz または rpm で与える数を rounds to steps で囲ってから Hz/rpm to ms/step に与えてください。


rounds to steps は一周のステップ数を掛けた答えを返すので、辻褄が合うんですね。

以上、で LV8548MC を使ったステッピングモーター駆動のためのブロックの説明はおしまいです。残念ながら micro:bit が一生懸命駆動パルスを生成しているので、モーターを回している最中は何も出来ませんので悪しからず。

おまけ

あっているかどうかわからないけど、台形駆動のパラメータの計算に苦労したので晒しておきます。面倒臭いのでバグがあると言われないと見直したくないです。間違いがあったら教えてね。

台形駆動は、トータルカウント(ステップ)数 Ca と、トータル時間 Ta、加減速時間 T1 と初期スピード S1 のみを与えて、残りは計算で求めています。

加速期間は速度 S1(ms/step) から S2(ms/step) まで徐々に1ステップの時間が短く(長く)なります。この時間を全部足したのが T1 になります(8)。また、T2 の時間をその時の1ステップ速度 S2 で割ったのが T2 という二つの方程式を解けばいい(4)。なんだけど意外に面倒臭い。答えは (10)。

台形駆動

余談。ステップ時間で見ると、台形じゃなく、上記のように台形の逆さま。じゃぁと速度に直すと 1/S1 とか逆数になるので、直線じゃないです。ただ周波数に変換すると S1 は実際は時間です。1/t=f なので周波数にすると台形になって、しかも直線になります。そういうこと?

今回から数式を埋め込めるように TeX のコード解釈できるようにした。TeX なんて学生の頃以来ですよ。でも数式書くならいまだに TeX いいよね。最高です!クヌース先生。

\begin{align}
T_{ams}=2 T1_{ms}+T2_{ms}
\end{align}
\begin{align}
C_{asteps}=2 C1_{steps}+C2_{steps}
\end{align}
\begin{align}
\Delta s=\dfrac {S1_{msps}-S2_{msps}}{C1_{steps}}
\end{align}
\begin{align}
C2_{steps}=\dfrac {T2_{ms}}{S2_{msps}}
\end{align}
\begin{align}
T1_{ms}&=(S2_{msps}+\Delta s)+(S2_{msps}+2\Delta s)+\cdots +(S2_{msps}+C_{asteps}\Delta s)\\
&= \sum^{C_{asteps}}_{i=1} (S2_{msps} + i \Delta s)\\
&= C_{asteps}S2_{msps} + (1 + 2 + \cdots + C_{asteps})\Delta s\\
&= C_{asteps}S2_{msps} + \dfrac {C_{asteps}(C_{asteps} + 1)}{2} \Delta s
\end{align}
\begin{align}
(C_{asteps} - 2)S2_{msps}^{2} + (C_{asteps}S1_{msps} - 4T1_{ms} - T2_{ms})S2_{msps} - T2_{ms}S1_{msps}
\end{align}
\begin{align}
S2_{msps} = \dfrac{-(C_{asteps}S1_{msps} - 4T1_{ms} - T2_{ms}) \pm \sqrt{(C_{asteps}S1_{msps} - 4T1_{ms} - T2_{ms})^{2} +4(C_{asteps} - 2)T2_{ms}S1_{msps}}}{2(C_{asteps} - 2)}
\end{align}



2019/09/01

micro:bit + LV8548MC (DC モーター編:プログラミング)

前回、micro:bit + LV8548MC (DC モーター編:とりあえず ハード)ではハードウェアに関してクドクド書きましたが今回は micro:bit に拡張機能で LV8548MC のパッケージを追加して、LV8548MC のブロックを使ってモーターを回してみます。プログラミングと言いつつ、単なるパッケージのブロック説明ですが。

ステッピングモーターブロックの追加に伴って内容アップデートしました。

拡張機能のインストール


GitHub に上げたパッケージを指定してインストールします(オリジナル)。
ブラウザーで MakeCode を開きます。既存のプロジェクトを開いてもいいですし、新規のプロジェクトを開いてもいいです。ちなみに iPhone 上でもプログラム作れますよ!

ちなみにこの拡張機能は LV8548MCSLDGEVK の機能を参考にはしていますが、完全にゼロから作っていますし、インターフェースも異なります。オシロスコープの私物が無いので、思ったような波形が出ているのかやや不安ですが、動作的にはまぁ、そんなに間違いは無いだろうといった感触です。

プロジェクトを開きます

拡張機能をインストールするために、高度なブロック、拡張機能のタブをクリックします。

拡張機能のインストール

LV8548MC の拡張機能を探します。検索ウィンドウに、
https://github.com/ttrsato/pxt-lv8548mc.git
を入力します。

拡張機能の検索ウィンドウ

検索すると lv8548mc が見つかります。クリックすると一瞬でインストールされます。

lv8548mc が見つかった!

MakeCode のエディターに LV8548MC のタブが増えています。
あとは使うだけ。


ブロックについて


LV8548MC の DC モーター用ブロックは3つあります(減りました)。残念ながらローカライズはやり方がいまいちわからなく(以前トライしたときにうまくいかなかった)、そのうち対応します。誰か知ってたら教えてー。

  • 初期化ブロック

Set DC Motor は LV8548MC のCH1 (IN1, IN2) または、CH2 (IN3, IN4) に繋ぐ micro:bit の端子を指定します。一つのチャネルに一つの初期化ブロックが必要です。DriveMode は PWM 駆動時の非通電時の動作の指定です。Open または Brake の指定ができます。
ちなみに DC モーター用のブロックの頭には D: が書かれています。ステッピングモーター用ブロック(S: で始まるブロック)とは共用出来ませんのでご注意を。

  • 速度指定ブロック

モーターを回すブロックはこれ一つです。ステッピングモーターブロックの追加の時に見直されました。

モーターの回る方向は Forward または Reverse (反転) で指定。速度は 0〜100 の割合 (duty) で与えることができます。ある一定以上の速度になるとモーターが回転を始めます。ほどほどに。定格外の電圧を加えている時は注意です。

  • 停止ブロック

 モーターを停止させます。
止めるときには Open または Brake を選ぶことができます。Open はモーターは慣性に従い緩やかに停止しますが、Brake はモーターを急激に停止させます。

サンプルプログラム


簡単なサンプルプログラムです。
徐々に速度をあげて、徐々に速度を下げるを繰り返すだけのプログラムです。




次はいよいよ ステッパー (ステッピング)モーターです。



micro:bit + LV8548MC (DC モーター編:とりあえず ハード)

オンセミLV8548MC が秋月で取り扱いが開始されました!

LV8548MC は DC ブラシモーターx2 またはステッピングモーターx1、電源は 4V〜16V のモータードライバーです。

また、そのままでは扱いづかいので、aitendo からピッチ変換基板が2種類(その1その2)が9月上旬から販売されるようです。

オンセミの LV8548MCSLDGEVK ならば付属の Arduino micro を使ってハードウェアの準備をほぼする必要なくモーターを回すことができるのですが、ここではmicro:bit で LV8548MC を動かしてみます。しかも安価に。ではそのハードウェアの準備をします。

まずはピッチ変換基板を使って、モータードライバー IC を使いやすくします。
待ちきれないので、aitendo の既存の SOP-10 ピッチ変換基板 を使いました。できれば新しく販売される基板を使ったほうが良いでしょう。フットパターンが LV8548MC に適しているので半田付けの際に位置決めしやすいし、配線が太めなようなので、大電流を流したいときに安心です。

さ、お試しならばとりあえず既存の変換基盤で大丈夫です。こんな風に半田付けしました。IC をよく見ると横棒が書いてあるのですが、横棒が近い辺が 1 pin (VCC) と 10 pin (OUT1) 側になります。




まぁまぁな感じで半田付けできました。


1 pin (VCC) と 6 pin (GND) の間にはデータシートにしたがって、パスコンを入れました。奮発してセラミックコンデンサ 10uF/50V。これがまた IC より高い。。(150円@千石電商)。データシートでの指定は 0.1uF〜10uFなので、安定性と財布次第で適宜変えてください。パスコンなので、無くても動いちゃうかもしれませんが、電源がプアだと動作が不安定になるので、入れるに越したことはありません。とりあえずセオリー通りに IC のなるべく近くに置きました。

micro:bit と DC モーターとの接続

micro:bit と LV8548MC の GND を一緒にすることを忘れないように。また 10uF のパスコンとは別に、100uF/50V の電解コンデンサーを LV8548MC の VCC - GND 間に入れています。パスコンは動作の安定のためですが、こちらは破壊防止です。壊したくなかったら入れておいたほうが無難です。
モーターが駆動しているときにモーターのコイルにエネルギーがたまります。モーターを急停止すると、このエネルギーをもとに回生電流が流れるのですが、ACアダプターなどの電源がこれを吸収しきれない場合、IC に印可される電圧が急上昇することがあり、IC が破壊に至る場合があるからです。100uF はこの上昇を抑える作用があります。
ちなみに LV8548MC の電源は手持ちの 6V/1A を使いました。LV8548MC は 4V〜16V で動作するので、この範囲の電源を接続してください。4V 以下では正常に動作しません。ちなみに、オンセミは LB1948MC という 2.5V〜16V で動作する LV8548MC とピンコンパチ(ファンクションも同じ)のモータードライバー IC もあります。4V 以下で動かしたい方はこちらを選択してもいいかもしれません。DigiKey などで取り扱いがあります。
モーターは子供達に破壊されたタミヤの2チャンネル リモコン・インセクトを分解して取り出したものです。

接続拡大

さてLV8548MC と micro:bit、モーターとの接続ですが簡単です。VCC, GND に電源、そして DC モーターを駆動するときは、

CH1: IN1, IN2 (OUT1, OUT2)
CH2: IN3, IN4 (OUT3, OUT4)
をペアとして接続してください。CH1, CH2 の定義は上記の通りとします。micro:bit でプログラムを書くときに指定するときに必要です。

IN1, IN2 および OUT1, OUT2 の極性にナーバスになる必要はないです。逆に回るだけなので、期待通りでなかったら接続を変えるか、ソフトの初期化の時の端子設定を逆にすればいいだけですから。

micro:bit と IN1〜IN4 を接続する必要がありますが micro:bit 側のピンは任意です(全部チェックしたわけではないですが)。また CH1, CH2 の両方のチャネルを接続しなくてもいいです。CH1 のみとか。IN1〜IN4 端子は内部でプルダウンされているので、何も接続していない場合は、例えば CH2 の IN3, IN4 に何も繋がない場合、OUT3, OUT4 はハイインピーダンスになるので問題ありません。

詳しくは LV8548MC のデータシートを参考にしてください。

ちなみに micro:bit に割り当てるピンに迷ったら、エッジコネクタとピンアウトの仕様を見てください。LED 表示やら、ボタン入力、アナログ入力など特別なファンクションに割り当てられていないのは P8 と P16 であることがわかります。自分の使いたい機能を持たないピンに割り当てれば良いと思います。
ただ、P0, P1, P2 以外の端子を利用しようとすると、Sparkfun などのマイクロビットブレイクアウトボード (リンクはマルツオンライン。色々な種類があって秋月などでも扱いがあります)が必要になります。上記写真では P13,14,15,16 に IN1,2,3,4 を割り当てています(これでは SPI 端子が使えません)。この手のボードを持っていない方は、まずは P0, P1, P2 を利用してみるのが良いかもしれません。

とりあえず DC モーターのハードはここまで。
micro:bit の LV8548MC 拡張機能パッケージやプログラミングについては次回









2019/07/31

Cyslonell EP2C5T144 を買ったよー!


Amazon で古い FPGA が安かったので、ついつい買ってしまった。Altera (Intel) の Cyslone ll EP2C5T144。1999円なんで、ほとんど部品代じゃないの?

上記、リンク先の Amazon のコメント欄が役に立ちます。

  • 意味不明の 0ohm pull-up,down の R1,R2,R9,R10 の取り外し
  • FPGA 経由で EPCQ デバイスへプログラミング(JIC プログラミング)を検索する
  • Quartus II は 13.1 より前、13.0sp1 (64-bit) Web Edition が使える
    http://fpgasoftware.intel.com/13.0sp1/


が参考になります。EPCQ デバイスプログラミングはまだ試していませんが。

またこのボードについてはCyclone II EP2C5 Mini Dev Board に情報がまとまっています。

FPGA 買ってみた というブログの記事も参考になります。

ちなみに YouTube ではボードの解説から、Quartus II の使い方も一通り説明がある以下の動画が参考になるかと。英語ですが。


追加の情報としては、

古いツールである Quartus II のライセンスは、以下のリンクから発行してもらえる。
インテル® FPGA ライセンス・サポートセンター
2. ライセンスのリクエスト、Quartus II ウェブ・エディションのリンク先からメールベースで発行されます。
Quartus II の Tool -> License Setup -> License file で添付のライセンスファイルを設定すれば OK。

USB Blaster は、怪しい激安物 659円送料無料でとりあえず問題なく書き込めました。
Windows 10 64bit 環境ですが、デバイスマネージャーからドライバーを認識させる必要はありました。ドライバーは、以下のディレクトリのものを入れればいいです。
C:\altera\13.0sp1\quartus\drivers

とりあえず簡単な Verilog の 50MHz の分周回路を書いて、L チカさせて、動いたレベルです。



さて、なにしますかね?

2019/04/06

Arty に Pmod VGA を繋げてみた


本当は HDMI か、DVI でディスプレイ直結してみたかったのだけど(一応そういう製品は存在するっぽい)、秋月で物色してたら Pmod VGA が売ってたので買ってきてみました。しかし、うちには VGA 入力のあるモニターがないので、VGA - HDMI コンバータを追加で買う羽目に。

この時点で Digilent に PmodVGA のチュートリアルがあるのでどうにかなるだろうと思っていたら、うまく行かないんですね。xdc のコンストレイントがうまくいっていないようなんだけど、ピンアサインの設定がいまいちわからないんだよなぁ。しかもコードは VHDL だし。

そこでググっていたら FPGAプログラミング大全 Xilinx編 のサンプルコードを見つけた。コードは Verilog なんで、さっと読めるし、とりあえず適当なプロジェクト作ってコードと xdc を読み込んだらあっという間にカラーパターン吐き出せました。


回路はシンプルで、Hsync, Vsync を生成して、タイミングに合わせてパターンを吐き出している感じ。色々調べながら作るのは大変なので楽させてもらいました。フレームバッファ用意して、それを表示させるのもアリですね。

本の方は古い Vivado 向けのようですが役に立ちそうなので注文しました。サンプルコードにお世話になったし、Pmod VGA の方はもう少し応用が載ってるっぽいんで。

とりあえず Pmod VGA が使えてよかった〜。




2019/03/31

Microblaze に豊四季タイニーBASIC を移植する

前回、Arty に Microblaze+カスタムペリフェラルを組み込んだ回路を作り上げました。
Microblaze に豊四季タイニーBASICを移植して、カスタムペリフェラルを動かしてみます。

BASIC のようなインタープリタ言語が入っているとカスタムペリフェラルの検証もお手軽ですね。

XSDK を直接立ち上げてプログラムを書くこともできるようですが、Vivado で前回作ったプロジェクト Arty_GSMB プロジェクトが立ち上がっているところから操作します。XSDK がインストールされていなかったら、しておきましょう。

Vivado のメニューの File から Launch SDK をクリックし、XSDK を立ち上げます。

場所はこのままで OK します。


XSDK の Project Explorer にはチュートリアルでエクスポートしたデータがあれば system_wrapper_hw_platform_0 と、今回新しく作った回路のデータ system_wrapper_hw_platform_1 が存在しています。なければ Vivado からエクスポートして、XSDK を立ち上げなおします。
system.hdf というファイルを開くと alu_double_0 がインスタンスされていることがわかります。無ければ‘Bitstream を更新していないとか、何か手順が抜けているはずです。見直してください。


ここまで出来ていればあとは移植するだけ。まずは Application Project を開いて、コードのひな型を作ります。豊四季タイニーBASIC を移植するにあたって UART は必須なので Hello World のテンプレートが役に立ちます。Hardware Platform を間違えないように。system_wrapper_hw_platform_1 ですよ。





このテンプレートのコードのトップは hello.c なので、main.c に名前変えました。なんとなく。変えなくてもいいですけど気持ち悪いので。

さ、下準備は整いました。あとは本当に移植するだけ。豊四季タイニーBASIC のソースコードを引っ張ってきます。いくつか種類があるのですが、移植が簡単だった Arduino 版がおすすめ。ところで上記のブログのコメント欄を見ないと気付かないのですが、確定版のソースは残念ながらバグがあります。これはパッチを当てて欲しかったです。詳細はコメントを見てください。

ソースは github の ココから落としてきました。展開すると basic.cpp と ttbasic.ino の二つのファイルがあり、普通の C での main.c に相当する ttbasic.ino ファイルから必要部分を main.c に移植し、basic.cpp をインポートして必要部分を追加修正します。

ちなみにこの basic.cpp、コメントが日本語なところが原因で、XSDK にインポートすると文字が化け化け。コンパイルすら通らなくなります。basic.c というファイル(中身は C なので cpp である必要はなしです) の空ファイルを作って、XSDK のエディタでこのファイルを開いているところに、別なエディタで開いた basic.cpp の中身をコピーしてペーストすると文字化けなどの問題がなくなります。

ttbasic.ino でやっていることは、乱数の初期化と basic() 関数の呼び出しです。basic() 関数は中で whle (1) の無限ループを回しているので呼び出すだけでいいです。従ってそのあとのコードは意味ないですが残しています。乱数の初期化はsrand() を呼び出しているのですが、ランダムなシードのソースが無いので(ADC の入力を使ってもいいとは思いますが)、仮に固定値 10 を与えています。main.c (hello.c) は以下のように書き換えます。

#include <stdio.h>
#include <stdlib.h>
#include "platform.h"

void basic(void);

int main()
{
    init_platform();

    srand(10);
    basic();

    cleanup_platform();
    return 0;
}
次に、basic.c を修正して豊四季タイニーBASIC が動作するようにしてみます。
ポイントはシリアル部分の移植で、これができればほぼ移植はできたも同然です。

まずはインクルードを追加修正します。

#include <stdio.h>
#include <stdlib.h>
#include <xuartlite_i.h>

ついでにエディションも変えておきます。起動時にメッセージで表示されます。

// TO-DO Rewrite these functions to fit your machine
#define STR_EDITION "MICROBLAZE"

ではシリアル周りを Microblaze に合わせます。移植は比較的簡単で、以下の三つの関数を移植するだけです。標準入出力は UART にアサインされているので、putchar(), getchar() に置き換えるだけです。c_kbhit() は UART の受信バッファにデータがあるかどうかを調べたい関数なので、UART のレジスタを直接調べます。実装している IP UART lite の仕様書を見ると STAT_REG の bit0 を見ればデータがあるかどうかわかります。下記のコードのようにすればバッファに有効なデータがあるかどうか確認することができます。UART0 マクロはもともと定義されていないので自分で定義していますが、これに関しては後で説明します。

void c_putch(char c){
    putchar(c);
}

char c_getch(){
    return getchar();
}

char c_kbhit(){
    return UART0->STAT_REG & XUL_SR_RX_FIFO_VALID_DATA;
}


XSDK で用意されたマクロや関数があるのですが好きなスタイルではないので、改めてマクロを定義します。ついでにカスタムペリフェラル alu_double のマクロの定義も追加します。

Project Explorer で作ったプロジェクト名 + _bsp 以下の microblaze_0/include/xparameters.h を探します。ここには alu_double だけでなくペリフェラルを含むいろいろなマクロ定義が書かれています。お目当ては UART と alu_double のベースアドレスです。ベースアドレスの定義を見つけたら定義を構造体をこのベースアドレスに割り当てます。

以下がその構造体とマクロの割り当て。

typedef struct {
    u32 RX_FIFO;
    u32 TX_FIFO;
    u32 STAT_REG;
    u32 CTRL_REG;
} UART_Type;

typedef struct {
 u32 DIN;
 u32 dummy1;
 u32 dummy2;
 u32 DOUT;
} ALU_DOUBLE_Type;

#define UART0  ((UART_Type*) XPAR_UARTLITE_0_BASEADDR)
#define ALU_DOUBLE ((ALU_DOUBLE_Type*) XPAR_ALU_DOUBLE_0_S00_AXI_BASEADDR)

構造体の変数は上記のような書き方をすると上から下に向かって変数がメモリに割り当てられます。構造体のポインタ定義をそれぞれのペリフェラルのベースアドレスに割り当てると構造体の変数への読み書きがそのまま、ペリフェラルのインターフェースへの読み書きになります。

あと忘れてはいけないのが乱数生成関数 getrnd() の移植。rand() 関数を使って対応しました。Linux 版がこの実装ですね。

// Return random number
short getrnd(short value) {
    return(rand() % value) + 1;
}

あともうひと手間でとりあえずの移植は完了です。
basic.c の中に putnum() という関数が定義されているのですが、これがすでに存在する関数なので、別な名前にします。tiny_putnum() という名前で置換しておきます。

これで BASIC が走るようになっているはずです。Arty を USB で接続して、Bitstream ファイルを書き込んで、Microblaze のコードを転送しましょう。Program FPGA アイコンをクリックして、Program ボタンをクリックします。Hardware Platform に system_wrapper_hw_platform_1 が選択されていることを確認のこと。



これで Arty に回路が書き込まれました。
最後に BASIC のコードを転送する前に、ターミナルソフトを起動して、シリアル接続をしておきます。9600bps で 8bit パリティ無です。ターミナルソフトの準備が終わったら、Run System Debugger (緑の丸に白三角)から、Run As/Launch on Hardware を選択しましょう。


プログラムが転送されて、Microblaze edition の豊四季タイニーBASIC が立ち上がりましたか?


成功すれば上記のような画面が出るはずです。やったー!

ではいよいよカスタムペリフェラルを BASIC から呼び出せるようにします。DOUBLE() という関数を作成してみましょう。引数に数字が与えられるとその二倍の値を返すようにします。実装には同様に引数を与えて結果が返る RND() 関数を参考にします。

まずは DOUBLE() 関数の本体から。alu_double にアクセスして演算結果を返す関数を作ります。getX2() という32bitの値を受け取って結果を返す関数です。
slv_reg0 の書き込みアドレスには DIN という 32bit の変数をアサインしたので、ALU_DOUBLE->DIN に値を代入することで、slv_reg0 へのデータ書き込みが実行されます。また、slv_reg3 のアドレスには DOUT という変数をアサインしたので、ALU_DOUBLE->DOUT を読み出せば演算結果を得られます。簡単ですねー。

short getX2(short value) {
    ALU_DOUBLE->DIN = value;
    return ALU_DOUBLE->DOUT;
}

あとはこれを呼び出す DOUBLE() 関数の実装です。まずは定義。コマンド名を kwtbl に追加して、i-code を列挙型に追加、後ろに空白を入れない中間コード i_nsa[] にそれぞれ追加します。


// Keyword table
const char *kwtbl[] = {
    "GOTO", "GOSUB", "RETURN",
    "FOR", "TO", "STEP", "NEXT",
    "IF", "REM", "STOP",
    "INPUT", "PRINT", "LET",
    ",", ";",
    "-", "+", "*", "/", "(", ")",
    ">=", "#", ">", "=", "<=", "<",
    "@", "RND", "DOUBLE", "ABS", "SIZE",
    "LIST", "RUN", "NEW"
};

// Keyword count
#define SIZE_KWTBL (sizeof(kwtbl) / sizeof(const char*))

// i-code(Intermediate code) assignment
enum {
    I_GOTO, I_GOSUB, I_RETURN,
    I_FOR, I_TO, I_STEP, I_NEXT,
    I_IF, I_REM, I_STOP,
    I_INPUT, I_PRINT, I_LET,
    I_COMMA, I_SEMI,
    I_MINUS, I_PLUS, I_MUL, I_DIV, I_OPEN, I_CLOSE,
    I_GTE, I_SHARP, I_GT, I_EQ, I_LTE, I_LT,
    I_ARRAY, I_RND, I_DOUBLE, I_ABS, I_SIZE,
    I_LIST, I_RUN, I_NEW,
    I_NUM, I_VAR, I_STR,
    I_EOL
};

// List formatting condition
// 後ろに空白を入れない中間コード
const unsigned char i_nsa[] = {
    I_RETURN, I_STOP, I_COMMA,
    I_MINUS, I_PLUS, I_MUL, I_DIV, I_OPEN, I_CLOSE,
    I_GTE, I_SHARP, I_GT, I_EQ, I_LTE, I_LT,
    I_ARRAY, I_RND, I_DOUBLE, I_ABS, I_SIZE
};

最後に中間コードを実行する関数 ivalue() に DOUBLE() を実装すれば出来上がりです。I_RND の隣にでも加えておきましょうか。

case I_DOUBLE:                 //関数DOUBLEの場合
    cip++;                             //中間コードポインタを次へ進める
    value = getparam();     //括弧の値を取得
    if (err)                            //もしエラーが生じたら
        break;                        //ここで打ち切る
    value = getX2(value); // x2 の計算結果を取得
    break;                            //ここで打ち切る

さぁ、もう一度プログラムを転送しなおして実行してみましょう。

うまくいったようです!ちゃんと演算結果を返してくれていますね。

ここまで出来ると、いろいろ夢は膨らみますね。とりあえず次は割り込み付きのペリフェラルを実装してみたいところです。ディスプレイを駆動する回路やら、キーボード入力を受け付ける回路を書いてみるとそれもまたいいですよねー。他には SDK プロジェクトを SPI flash に書き込んで毎回電源を入れるだけでブート出来るようにもしてみたいです。




Arty に Microblaze を導入して、カスタムペリフェラルを組み込んでみる

FPGA などで RTL で作ったハードワイヤードの回路というのは全くもって融通が利かないし、かといってシビアなタイミングや本当の並列動作はプロセッサと組み込みのソフトでは厳しいですよね?

「だったら両方入ってればいいじゃん!」

はい、どっちも入れましょう。プロセッサ+カスタムペリフェラルという形で。仕事じゃないので好き放題できます(笑)

手元には二年半ほど前に買って、埃をかぶっている Arty が転がっているのでこれを使います。今回はプロセッサには流行りの RISC-V は今回は敬遠して、Arty に簡単に導入できる Microblaze を採用してみます。ちなみに Microblaze も RISC プロセッサです。RISC プロセッサに関してはこちらの有名な本をどうぞ。

Arty

ところで Arty は Digilent の FPGA 評価ボード。いくつか種類があるんですが、入手性はどうなんだろう?特にこの安い無印 Arty。これは確か DigiKey で購入したような。。秋月でも取り扱いがあります(2019.3)。他には Avnet から直接購入することも出来るようです。

Microblaze は Arty 以外でも導入することが出来るので、Arty にこだわる必要はありません。ただ、Arty 安いし、小型なので Cafe でデバッグ出来ますよ(笑)

さて前置きはこれぐらいにして、Arty に Microblaze を導入して、AXI バスに適合するペリフェラルを作って、Microblaze にくっつけましょう。最後に Microblaze に豊四季タイニーBASICを移植して、カスタムペリフェラルを BASIC から動かせるようにしてみます。
それでは、

  • Microblaze を組み込む
  • カスタムペリフェラルを作って Microblaze にくっつける
  • 豊四季タイニーBASICを移植する

を順に追っていきたいと思います。

  1. Microblaze を組み込む
    Arty に関しては沢山のドキュメントが揃っています。これを参考にします。豊四季タイニーBASICを動かすことを目標にしているので、Getting Started with Microblaze がぴったりです。Microblaze と UART (シリアル) が入っているだけですが、豊四季タイニーBASIC はシリアル端末で動作させる仕様なので十分です。とりあえずこのチュートリアルに従って Microblaze を入れてみましょう。
    ここで注意点が1つ。Xilinx の FPGA のツール Vivado の最新版 2018.3 (2019.3 時点)がこのチュートリアルに沿って Microblaze を導入しようとすると途中でコケます。あまり深く追求したく無いので、とりあえず 2016.4 を導入してみたらこのチュートリアル通りにはできましたのでこのバージョンで試した結果を書いておきます。そのうち新しいバージョンでも再度挑戦してみようとは思いますが、めんどくさいのでいつになるやら。あと高位合成などしないので、フリーのライセンスで OK。あと、Microblaze のプログラムをコンパイルするために Vivado のインストール時に XSDK もインストールすることをお忘れなく。
    とりあえず適当なターミナルソフト(チュートリアルでは Tera Term 使ってる)で Hello World が出ればよしです。ここで作るプロジェクト名はチュートリアルと同じ Arty_GSMB にしておきます。
  2. カスタムペリフェラルと作って Microblaze にくっつける
    AXI バスに適合したインターフェースを持つ回路を用意できればいいのですが、Vivado では AXI スレーブ・マスターのひな型を作ることができて、ライブラリに登録されるので、他のプロジェクトから簡単に導入できるんですね。AXI バスとひな型の作り方はこちらのブログで紹介されているので、こちらを参考にしました。

    とりあえず、電子工作をまったく行わずに独自のペリフェラルを作ってみます。
    題して
    「2倍演算機!」
    です。

    32bit のデータを与えるとそのデータを二倍にして返すというとてもシンプルな回路です。ペリフェラルにデータを与えて結果を受け取るという基本中の基本ができればあとは応用ですから。

    Microblaze において AXI バスのペリフェラルはメモリマップドI/Oと言って、そのインターフェースはメモリー上に位置しています。プログラムから見れば特定のアドレスにデータを読み書きすることでペリフェラルにアクセスすることができます。

    こんな感じ。

    ではさっそくカスタムペリフェラルを作ってみます。ここからは先ほどのブログと被るところが多々ありますが、一応書いておきます。
    最初に上記のチュートリアルで作成したプロジェクト Arty_GSMB を開いているならば、Vivado のメニューの Tools から、Create and Package New IP をクリックします。



    Create a new AIX4 peripheral を選択して Next。



    IP の Name を適当につけてあげます。alu_double にしました。



    AXI バスのインターフェースを決めます。割り込みの信号もここで用意できるみたいですが、今回はいらないので有効にしません(今度試してみよう)。Lite, Slave でバス幅を 32、レジスターの数も最小の 4 にしておきます。



    ひな型ができたらすぐ編集するので、Edit IP を選択して Finish。



    続いて Project Manager の Hierarchy の Design Sources の下にある alu_double_v1_0_S00_AXI をダブルクリックするとひな型のコードが右側に現れます。



    このコードには以下の4つのレジスタ slv_reg0,1,2,3 を AXI バスから読み書きするという簡単なコードが書かれています。ここに値を二倍する回路を組んでいきます。



    二倍演算といっても、C などのプログラミング言語で左に1ビットシフトすればよいだけですよね。RTL でも一緒です。そこで、slv_reg0 に書かれた値を二倍して、本来 slv_reg3 を読み出す代わりに、二倍された値を読み出せるようにします。ちなみに slv_reg0 を読み出す代わりに演算した結果でもいいんですけど、ここではあえてそうしてみました。以下の always 文は、指定されたアドレスの値を読み出し用のデータバスに出力するマルチプレクサのコードです。マークしたところが書き換えた部分。簡単ですね。実際合成される回路ではビットシフトは LSB には GND が接続され、他は単なる配線の付け替えという論理回路の消費ゼロなお手軽な演算回路が出来上がります。これでも立派な演算回路ですよね(笑)



    ではここで、最初に作ったプロジェクト Arty_GSMB に戻ります。Project Manager の Design Sources から system_wrapper の下の system_i をダブルクリックするとブロックダイアグラムが開きます。





    ダイアグラムエディタの左にある Add IP アイコンをクリックして、Search で alu とタイプしてみると、先ほど作成した alu_double がみつかります。ENTER して追加すると、ブロックエディタにこの IP がインスタンスされます。



    Run Connection Automation をクリックすると



    以下のようなウィンドウが現れるので、そのまま OK すると AXI バスに自作 IP を自動的に接続してくれます。アドレスのアサインも適宜おこなってくれています。回路作成はこれでおしまい。簡単ですね。素晴らしい!



    新しく作成した回路の Bitstream を生成して、XSDK にエクスポートしておきます。これは最初に Microblaze を組み込んだ時と一緒です。エクスポートする際には Include bitstream にチェックすることを忘れないように。




    以上で HW の作成はおしまいです。豊四季タイニーBASICの移植は分けてアップします。

    それにしても、AXI スレーブのひな形がかんたんに作れて、接続できてしまうのはいいなぁ。仕事ではこういったのは全て手で書き起こしていたのでとても便利に感じます。