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 を立ち上げます。
このテンプレートのコードのトップは 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) は以下のように書き換えます。
ポイントはシリアル部分の移植で、これができればほぼ移植はできたも同然です。
まずはインクルードを追加修正します。
ついでにエディションも変えておきます。起動時にメッセージで表示されます。
ではシリアル周りを Microblaze に合わせます。移植は比較的簡単で、以下の三つの関数を移植するだけです。標準入出力は UART にアサインされているので、putchar(), getchar() に置き換えるだけです。c_kbhit() は UART の受信バッファにデータがあるかどうかを調べたい関数なので、UART のレジスタを直接調べます。実装している IP UART lite の仕様書を見ると STAT_REG の bit0 を見ればデータがあるかどうかわかります。下記のコードのようにすればバッファに有効なデータがあるかどうか確認することができます。UART0 マクロはもともと定義されていないので自分で定義していますが、これに関しては後で説明します。
XSDK で用意されたマクロや関数があるのですが好きなスタイルではないので、改めてマクロを定義します。ついでにカスタムペリフェラル alu_double のマクロの定義も追加します。
Project Explorer で作ったプロジェクト名 + _bsp 以下の microblaze_0/include/xparameters.h を探します。ここには alu_double だけでなくペリフェラルを含むいろいろなマクロ定義が書かれています。お目当ては UART と alu_double のベースアドレスです。ベースアドレスの定義を見つけたら定義を構造体をこのベースアドレスに割り当てます。
以下がその構造体とマクロの割り当て。
構造体の変数は上記のような書き方をすると上から下に向かって変数がメモリに割り当てられます。構造体のポインタ定義をそれぞれのペリフェラルのベースアドレスに割り当てると構造体の変数への読み書きがそのまま、ペリフェラルのインターフェースへの読み書きになります。
あと忘れてはいけないのが乱数生成関数 getrnd() の移植。rand() 関数を使って対応しました。Linux 版がこの実装ですね。
あともうひと手間でとりあえずの移植は完了です。
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 を読み出せば演算結果を得られます。簡単ですねー。
あとはこれを呼び出す DOUBLE() 関数の実装です。まずは定義。コマンド名を kwtbl に追加して、i-code を列挙型に追加、後ろに空白を入れない中間コード i_nsa[] にそれぞれ追加します。
最後に中間コードを実行する関数 ivalue() に DOUBLE() を実装すれば出来上がりです。I_RND の隣にでも加えておきましょうか。
さぁ、もう一度プログラムを転送しなおして実行してみましょう。
うまくいったようです!ちゃんと演算結果を返してくれていますね。
ここまで出来ると、いろいろ夢は膨らみますね。とりあえず次は割り込み付きのペリフェラルを実装してみたいところです。ディスプレイを駆動する回路やら、キーボード入力を受け付ける回路を書いてみるとそれもまたいいですよねー。他には SDK プロジェクトを SPI flash に書き込んで毎回電源を入れるだけでブート出来るようにもしてみたいです。
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 ですよ。
ソースは 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 に書き込んで毎回電源を入れるだけでブート出来るようにもしてみたいです。
カテゴリ
Artix,
Arty,
AXI,
BASIC,
MicroBlaze,
Vivado,
Xilinx,
XSDK,
豊四季タイニーBASIC
Arty に Microblaze を導入して、カスタムペリフェラルを組み込んでみる
FPGA などで RTL で作ったハードワイヤードの回路というのは全くもって融通が利かないし、かといってシビアなタイミングや本当の並列動作はプロセッサと組み込みのソフトでは厳しいですよね?
ところで Arty は Digilent の FPGA 評価ボード。いくつか種類があるんですが、入手性はどうなんだろう?特にこの安い無印 Arty。これは確か DigiKey で購入したような。。秋月でも取り扱いがあります(2019.3)。他には Avnet から直接購入することも出来るようです。
Microblaze は Arty 以外でも導入することが出来るので、Arty にこだわる必要はありません。ただ、Arty 安いし、小型なので Cafe でデバッグ出来ますよ(笑)
さて前置きはこれぐらいにして、Arty に Microblaze を導入して、AXI バスに適合するペリフェラルを作って、Microblaze にくっつけましょう。最後に Microblaze に豊四季タイニーBASICを移植して、カスタムペリフェラルを BASIC から動かせるようにしてみます。
それでは、
を順に追っていきたいと思います。
「だったら両方入ってればいいじゃん!」
はい、どっちも入れましょう。プロセッサ+カスタムペリフェラルという形で。仕事じゃないので好き放題できます(笑)
手元には二年半ほど前に買って、埃をかぶっている Arty が転がっているのでこれを使います。今回はプロセッサには流行りの RISC-V は今回は敬遠して、Arty に簡単に導入できる Microblaze を採用してみます。ちなみに Microblaze も RISC プロセッサです。RISC プロセッサに関してはこちらの有名な本をどうぞ。
手元には二年半ほど前に買って、埃をかぶっている 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を移植する
を順に追っていきたいと思います。
- 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 にしておきます。 - カスタムペリフェラルと作って 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 スレーブのひな形がかんたんに作れて、接続できてしまうのはいいなぁ。仕事ではこういったのは全て手で書き起こしていたのでとても便利に感じます。
2019/03/26
MacBook Pro Mid 2015 の外付け SSD に Windows10 をインストール出来たー
macOS に対応していないツール (Xilinx Vivado) を動かすために 2016/11 ごろに外付け SSD に VirtualBox の仮想マシンを入れてそこで Windows やら Ubuntu をインストールし、ツールを動作させようとしばらく頑張っていましたが挫折していました。
VirtualBox 上でもツールはどうにかインストール出来て Bitstream を吐き出すところまではうまく行くものの、XSDK というツールから USB 経由で FPGA ボード (Arty) に Bitstream を書き込んで MicroBlaze (Xilinx のソフトマクロのプロセッサです)のプログラムを書き込むというところが安定して動作せずに諦めていました。しかもツールの動作遅いし。
MacBook Pro 本体の SSD に Bootcamp で Windows をインストールすればいいんだろうとは思っていましたが、256GB しかない容量に macOS と Windows の共存は無理で、どうしても外付けの SSD か HDD にインストールしたかったのです。しかし数年前は外付けの SSD に Bootcamp で Windows が入れられるとは思っていなかったので(そもそも正式対応しているインストール方法では無いので)、しばらくブランクがあったわけです。

しかし、以下の URL の情報を頼りに MacBook Pro に Windows10 をインストールすることに成功しました (2019/3)。ありがとうございます!
https://ameblo.jp/dai-vocalist/entry-12423490407.html
私の試した環境は以下。
MacBook Pro Mid 2015 (Core i7, 2.2GHz, Memory 16GB)
SSD: Elecom ESD-EC0240GRD
USB memory: Toshiba 16GB TOUSB16GWH USB2.0
Windows10: 1803 Japanese x64 ISO (ググって探しました)
Rufus: v2.18
WindowsSupport: macOS Mojaive 10.14.3 付属の Boot Campアシスタント 6.1.0 からダウンロード
まず上記 Windows と Rufus のバージョンを 2019/3 時点で最新のものを用意し、
https://blue1st.hateblo.jp/entry/2018/02/18/061620
を参考に DiskPart で Windows をインストールすることを試したのですが、最初はマウスやキーボードも認識するし、どうにか起動したものの、WindowsSupport を適用後から起動が怪しくなりました。先走って購入した Windows のライセンスが要らなくなるかもしれないとブルーになりました。。
そこでものは試しと
https://ameblo.jp/dai-vocalist/entry-12423490407.html
VirtualBox 上でもツールはどうにかインストール出来て Bitstream を吐き出すところまではうまく行くものの、XSDK というツールから USB 経由で FPGA ボード (Arty) に Bitstream を書き込んで MicroBlaze (Xilinx のソフトマクロのプロセッサです)のプログラムを書き込むというところが安定して動作せずに諦めていました。しかもツールの動作遅いし。
MacBook Pro 本体の SSD に Bootcamp で Windows をインストールすればいいんだろうとは思っていましたが、256GB しかない容量に macOS と Windows の共存は無理で、どうしても外付けの SSD か HDD にインストールしたかったのです。しかし数年前は外付けの SSD に Bootcamp で Windows が入れられるとは思っていなかったので(そもそも正式対応しているインストール方法では無いので)、しばらくブランクがあったわけです。

しかし、以下の URL の情報を頼りに MacBook Pro に Windows10 をインストールすることに成功しました (2019/3)。ありがとうございます!
https://ameblo.jp/dai-vocalist/entry-12423490407.html
私の試した環境は以下。
MacBook Pro Mid 2015 (Core i7, 2.2GHz, Memory 16GB)
SSD: Elecom ESD-EC0240GRD
USB memory: Toshiba 16GB TOUSB16GWH USB2.0
Windows10: 1803 Japanese x64 ISO (ググって探しました)
Rufus: v2.18
WindowsSupport: macOS Mojaive 10.14.3 付属の Boot Campアシスタント 6.1.0 からダウンロード
まず上記 Windows と Rufus のバージョンを 2019/3 時点で最新のものを用意し、
https://blue1st.hateblo.jp/entry/2018/02/18/061620
を参考に DiskPart で Windows をインストールすることを試したのですが、最初はマウスやキーボードも認識するし、どうにか起動したものの、WindowsSupport を適用後から起動が怪しくなりました。先走って購入した Windows のライセンスが要らなくなるかもしれないとブルーになりました。。
そこでものは試しと
https://ameblo.jp/dai-vocalist/entry-12423490407.html
を参考に再度挑戦したところ、インストールは成功し、安定して動作するようになりました!Windows Update もかかるのですが、その後も特に問題なさそうです。
バージョンの問題だったのか、インストールの仕方だったのか。
バージョンの問題だったのか、インストールの仕方だったのか。
とにかくまともに動いてるからよしと。
一点だけ再確認はちゃんとできていないのですが、Windows を触らずにほっておくとしばらくすると OS がスリープしてしまうのですが、そのタイミングで再起動がかかってしまったらしい事象に2度ほど会ったような気がします。気がつくと macOS が起動してしまっていたので。これを避けるために電源を繋いでいるときはスリープに入ることを禁止しました。とりあえず今の所問題なさそうです。
2018/11/03
オンセミの LV8548MCSLDGEVK でエナメル線で作ったモーターを回してみた!
オン・セミコンダクター社のモータドライバモジュールソリューションキットを使った、間違いなく世界初のアホな試みである。
エナメル線自作モーターは単三電池に巻きつけて形を整えたよくあるタイプです。磁石の置き方が変わってるかも。クリップにいい感じで貼り付けられます。磁力が足りないかも知れないと感じて、2つの磁石の同じ局を対向させています。原理的にはこの配置もアリかな?
これでも立派な DC ブラシモーターなので、LV8548MC で回すことができる。最大 1A 流せるドライバ IC なので、快調に回ってくれる。もちろん電源は 5V 4A 流せる秋月の電源から供給しているので十分電流を流せる。無駄にw
アップした動画では、PWM duty 100% で回し始めて、だんだん duty を下げて、また 100% まで duty を上げて最後に駆動を止める、という一連のシーケンス。。をやってるのだけど、なんとなく分かります?
エナメル線でモーターなんて小学生か中学生以来。
繊細なので綺麗に回せるものを作るのは案外難しかったです。
エナメル線自作モーターは単三電池に巻きつけて形を整えたよくあるタイプです。磁石の置き方が変わってるかも。クリップにいい感じで貼り付けられます。磁力が足りないかも知れないと感じて、2つの磁石の同じ局を対向させています。原理的にはこの配置もアリかな?
これでも立派な DC ブラシモーターなので、LV8548MC で回すことができる。最大 1A 流せるドライバ IC なので、快調に回ってくれる。もちろん電源は 5V 4A 流せる秋月の電源から供給しているので十分電流を流せる。無駄にw
エナメル線自作DCブラシモーター
よく回ってます♪
アップした動画では、PWM duty 100% で回し始めて、だんだん duty を下げて、また 100% まで duty を上げて最後に駆動を止める、という一連のシーケンス。。をやってるのだけど、なんとなく分かります?
エナメル線でモーターなんて小学生か中学生以来。
繊細なので綺麗に回せるものを作るのは案外難しかったです。
2018/09/20
2018/08/23
メダカ自動餌やり器
夏休みに家族旅行に出ることになりました。
旅行中でも餌あげられるように調べてみると、魚自動給餌器って2000円も出せば手に入るんですね?
でも今年は手元にいいオモチャがあるんで、それで魚自動給餌器作ってみました。去年はメダカを実家に預けたのだけど、今年はうちで面倒見ちゃいます!
その前に、どう作るかですよね。。
いくつか考えて見たけど、メカ的機構に凝ると大変なので(というかそういう作り込み苦手なので)簡単な物で済ませました。
原理は簡単♪
香辛料の容器みたいに穴の空いた容器を穴を下にすれば、餌がこぼれ出てくるようなモンです。容器を下にする時間をコントロールすれば、餌の量も調整可能です。
穴を上にすれば止まるし。
そうそう、この筒を回転させれば、それが出来るんですよね。回転速度と回数を変えれば餌の量もコントロールできそうです。一回転とかキッチリ回すにはステッパモータが丁度いいですよね〜。
さて、100均で買ってきた容器に穴を開けて、餌を入れてみて穴から餌が出てくることを確認してみます。3φ程度でいい感じです。
そこまで確認したら早速工作してみます。
モーターは後述する Solution Kit に付属のステッパモータを使います。この軸が6φ何ですけど、どう接続するか悩んだ挙句、秋葉原のヴィストンロボットセンターに飛び込んで探してみました。ロボット関連のお店なら何かあるだろうと目論んで。そして見つけたのが写真左上に写っているタミヤの AO-8038 ギヤードモーターハブシャフト。これがぴったりハマりました。
旅行中でも餌あげられるように調べてみると、魚自動給餌器って2000円も出せば手に入るんですね?
でも今年は手元にいいオモチャがあるんで、それで魚自動給餌器作ってみました。去年はメダカを実家に預けたのだけど、今年はうちで面倒見ちゃいます!
その前に、どう作るかですよね。。
いくつか考えて見たけど、メカ的機構に凝ると大変なので(というかそういう作り込み苦手なので)簡単な物で済ませました。
原理は簡単♪
香辛料の容器みたいに穴の空いた容器を穴を下にすれば、餌がこぼれ出てくるようなモンです。容器を下にする時間をコントロールすれば、餌の量も調整可能です。
穴を上にすれば止まるし。
そうそう、この筒を回転させれば、それが出来るんですよね。回転速度と回数を変えれば餌の量もコントロールできそうです。一回転とかキッチリ回すにはステッパモータが丁度いいですよね〜。
さて、100均で買ってきた容器に穴を開けて、餌を入れてみて穴から餌が出てくることを確認してみます。3φ程度でいい感じです。
そこまで確認したら早速工作してみます。
材料はこんな感じ
モーターは後述する Solution Kit に付属のステッパモータを使います。この軸が6φ何ですけど、どう接続するか悩んだ挙句、秋葉原のヴィストンロボットセンターに飛び込んで探してみました。ロボット関連のお店なら何かあるだろうと目論んで。そして見つけたのが写真左上に写っているタミヤの AO-8038 ギヤードモーターハブシャフト。これがぴったりハマりました。
イモネジでモータに固定します
付属するネジはナイロン入りで締めるのが大変なので、別途4φのナットを購入です。そして途中まで組み立てたのですが、、水槽固定用に買ってみた部品が入らない!甘かった。これは別途解決しました。
お、おぅ。入らない。。
さて、この機構を早速動かしてみます。
そこで活躍するのが、オン・セミコンダクターのモータ・ドライバ・ソリューション・キット。現在、DCブラシモータまたはステッパを回せる LV8548 のキットが Mouser か DigiKey で入手可能です。
電源が別途必要なんですが(秋月で売ってる電源でOK。LV8548 は 4〜12V が適当)、他はモータ含めてキットに入ってるので、モータと PC をくっつけて、付属の Arduino Micro に Web から落としてきた GUI 用のファームを Arduino IDE を使って書き込んであげて、Windows の GUI を立ち上げると GUI からモータを回せちゃいます。マイナスドライバーも附属してるので、半田付けすらいらないです。
※さりげなく macOS High Sierra 上の VirtualBox から Windows10 走らせて、そこで GUI を立ち上げています。保障外でしょうが、一応動いています。ただし、VirtualBox 上の Windows10 から Arduino IDE を使ってプログラムの書き込みは出来ないので、Mac 上で動かしてる Arduino IDE からファームを書いています。
こんな画面でポチポチと
使い方は何となく分かりますね。注意するところはステッパなので、ステップ角(Step Angle) をセットしないと有効にならない設定があるところぐらいですか。ステップ角はモータの仕様を見て、適宜設定してください。
あとは GUI でポチポチとパラメータを変えながら最適な餌の量を落とせる設定を探してみます。回転周波数と餌の量のグラフでもと思ってわざわざディジタルの計りまで買ったのだけど、餌の重さが小さすぎて面倒なのでやめました。さて、いい感じに餌が落ちるようになったら、Generate Program というボタンがあるので、押してみます。
生成されたスケッチ
今までの操作が、API を使った Arduino のスケッチの形で出力されます。
必要なのは初期設定とモータを回す、止めるといった部分のコードだけなので、余計な部分ぽいコードはガシガシ削除。あとは定時に餌をあげられるように時計などのコードを実装したら完成です!
簡単、簡単〜。
簡単、簡単〜。
こんな風に回ります
実際動かしてみると、ステッパーモータのガタガタした動きが、実はいい感じに瓶をシェイクしてくれます。餌の固まりを防げるし、餌の出が良くなると思います。むしろマイクロステップなんぞいらないですよ。このアプリケーションに関しては。
Full Step と Half Step の違いも試してみましたが、どっちもどっちな感じです。LV8548 は単純な構成なので、Half Step だとステップが細かくなって滑らかになるはずが、トルクが一定でないので味のある動きをします。LV8702 だと Half Step でもトルクが一定になるよう電流のコントロールをするモードがあるのですが(詳しくはデータシート参照)、滑らかになって餌やり器には多分都合が悪いです。
ちなみに LV8548 を搭載した場合はベースボードにある CN8 の A2-A5 端子が全部使えます(Arduino Micro の A2-A5 に直結されてる)。温度センサーをつけて、水温次第で餌をあげる回数や量をコントロールするのも楽しそう!
あと、プチ改造ですが、
Full Step と Half Step の違いも試してみましたが、どっちもどっちな感じです。LV8548 は単純な構成なので、Half Step だとステップが細かくなって滑らかになるはずが、トルクが一定でないので味のある動きをします。LV8702 だと Half Step でもトルクが一定になるよう電流のコントロールをするモードがあるのですが(詳しくはデータシート参照)、滑らかになって餌やり器には多分都合が悪いです。
ちなみに LV8548 を搭載した場合はベースボードにある CN8 の A2-A5 端子が全部使えます(Arduino Micro の A2-A5 に直結されてる)。温度センサーをつけて、水温次第で餌をあげる回数や量をコントロールするのも楽しそう!
あと、プチ改造ですが、
ベースボードの VCC と VIN をショート
ベースボードの VCC と Arduino の VIN を直結してあげると、USB から Arduino に給電する必要がなくなるので、モータ用の電源だけでボードを動作させることができます。
Arduino の VIN は 7V〜12V なので、モータに給電する電源次第では Arduino がうまく動かなくなったり、壊したりするので要注意です。
さ、これで安心して旅行に出かけられます!じゃ!
ソースコードはこちら↓
Arduino の VIN は 7V〜12V なので、モータに給電する電源次第では Arduino がうまく動かなくなったり、壊したりするので要注意です。
さ、これで安心して旅行に出かけられます!じゃ!
ソースコードはこちら↓
#include <LV8548_STEP_Lib.h>
#include <TimerOne.h>
#include <TimerThree.h>
// LV8548 Stepper library のインスタンス化
Lib_LV8548Step Lib;
/**
* @brief モーターの制御を表す値
*
* hms_def 時間構造体の変数 r に格納される
*/
enum {MT_FEED = 1, MT_FREE = 0};
/**
* @brief 時間格納構造体
*
* モーター制御用の変数を含む。RTC またはシーケンサーに使われる。
*/
typedef struct {
uint8_t h; ///< 時
uint8_t m; ///< 分
uint8_t s; ///< 秒
uint8_t r; ///< モーター制御
} hms_def;
// RTC 用、時間変数
hms_def rtc_time = {0, 0, 0, MT_FREE};
// シーケンサー用時間変数の配列
// 好きなように設定してして下さい
// ここでは毎回励磁を停止していますが、止めなくても大丈夫です。
// むしろ励磁しておくと止まっている位置から瓶が回転しにくくなるので、
// 不用意に穴の位置が変わってしまうことを低減できます。モーターは熱くなりますが。
hms_def timer_list[] = {
{ 7, 30, 0, MT_FEED}, ///< 朝の餌やり開始
{ 7, 30, 15, MT_FREE}, ///< 餌やり終了、360度回転後少ししてからモーターの励磁を停止
{12, 0, 0, MT_FEED}, ///< 昼の餌やり開始
{12, 0, 15, MT_FREE}, ///< 餌やり終了、360度回転後少ししてからモーターの励磁を停止
{16, 30, 0, MT_FEED}, ///< 夜の餌やり開始
{16, 30, 15, MT_FREE}, ///< 餌やり終了、360度回転後少ししてからモーターの励磁を停止
};
// シリアル通信用の文字列バッファと Index 用変数
char read_buff[5]; // FMT: x:dd + \0
int buff_ptr = 0;
/**
* @brief setup()
*/
void setup()
{
// コマンド入出力用
Serial.begin(19200);
// LV8548 のための初期化
Lib.initLib();
Timer1.initialize(65);
Timer1.attachInterrupt(interrupt);
// RTC 用の 1秒割り込みを Timer3 で作る
Timer3.initialize(1000000); // 1sec
Timer3.attachInterrupt(interrupt3);
delay(100);
// ステッパーの1ステップの角度設定
Lib.setStepAngle(7.5);
}
/**
* @brief Timer1 割り込みハンドラ
*
* LV8548 Stepper library が使用している。
*/
void interrupt()
{
Lib.timerFire(100);
}
/**
* @brief 24時間時計
*
* リアルタイム更新される時計で、時、分、秒の値を保存する。一秒に一度呼び出されることを期待しています。
*/
void updateRtc()
{
// Inc 秒
rtc_time.s += 1;
if (rtc_time.s == 60) {
rtc_time.m += 1;
rtc_time.s = 0;
}
// Inc 分
if (rtc_time.m == 60) {
rtc_time.h += 1;
rtc_time.m = 0;
}
// Inc 時
if (rtc_time.h == 24) {
rtc_time.h = 0;
}
}
/**
* @brief hms_def 時間構造体の時間要素の比較
*
* 時、分、秒の値を比較する
*
* @param st ソース時間
* @param dt ディスティネーション時間
* @retval true 時間一致。
* @retval false 時間不一致。
*/
bool timeIsEqualTo(hms_def *st, hms_def *dt)
{
return ((st->h == dt->h) &&
(st->m == dt->m) &&
(st->s == dt->s));
}
/**
* @brief hms_def 時間を表示する
*
* %H:%M:%D のフォーマットでシリアルに出力。
*
* @param t 表示したい時間構造体
*/
void printTime(hms_def *t)
{
Serial.print(t->h);
Serial.print(":");
Serial.print(t->m);
Serial.print(":");
Serial.print(t->s);
}
/**
* @brief hms_def 時間を表示する
*
* Timer3 の割り込みハンドラ
*/
void interrupt3()
{
// RTC の時間を更新する
updateRtc();
// RTC の時間をシリアルに出力
printTime(&rtc_time);
// シーケンサーの時間を1つずつ確認する
for (int i = 0; i < sizeof(timer_list) / sizeof(hms_def); i++) {
// 現在時刻と一致するかどうか
if (timeIsEqualTo(&rtc_time, (timer_list + i))) {
if (timer_list[i].r == MT_FEED) {
// 餌を与える時間
// モーターの回転速度次第で餌の量が変わりますので
// 適宜第一引数を調整して下さい。
// Half step はステップごとにトルクの変動があります。
// Full step との振動の違いも比較すると面白いです。
Serial.print(" <- FEED");
Lib.motorRotationDeg(12, 360.0, 0, 1);
} else if (timer_list[i].r == MT_FREE) {
// 餌を与えた後、モーターが回り切ってから低消費電力のため励磁を停止する。
Serial.print(" <- free");
Lib.motorRotationFree();
}
}
}
Serial.println("");
}
/**
* @brief シリアルで受信した文字列をパースする
*
* RTC の時、分、秒を更新することができる。
* 設定範囲外の場合は無視される。
*
* @param t 受信文字列
*/
void parseBuff(char* s)
{
int para = atoi(s + 2);
switch (s[0]) {
case 'h':
if (para >= 0 && para < 24) {
rtc_time.h = para;
Serial.print("Set h = ");
Serial.println(para);
}
break;
case 'm':
if (para >= 0 && para < 60) {
rtc_time.m = para;
Serial.print("Set m = ");
Serial.println(para);
}
break;
case 's':
if (para >= 0 && para < 60) {
rtc_time.s = para;
Serial.print("Set s = ");
Serial.println(para);
}
break;
}
}
/**
* @brief シリアルで文字列を受信する
*
* 文字列バッファー read_buff のサイズに達した場合、もしくは '\n' (LF) を
* 受信した場合は文字列の最後に '\0' (NULL) を埋め込み、parseBuff() をコールする。
*
* @param t 受信文字列
*/
void serialRead()
{
if (Serial.available() > 0) { // 受信したデータが存在する
char c = Serial.read(); // 受信データを読み込む
read_buff[buff_ptr++] = c;
if (buff_ptr == (sizeof(read_buff) - 1) || c == '\n') {
read_buff[buff_ptr] = '\0';
buff_ptr = 0;
parseBuff(read_buff);
}
}
}
/**
* @brief loop()
*/
void loop()
{
serialRead();
delay(100);
}
登録:
投稿 (Atom)









