ComProcWorkshop/第1章:数値をLEDに表示する
ドットマトリクスLEDに数値を表示する方法を学ぶ
ComProc CPU Board Rev.1 に搭載されているドットマトリクスLEDは8×8=64ドット
64個のLEDを一斉に光らせるのは難しい
Tang Nano 4Kには64本ものIOは無い
8個のLEDなら一斉に光らせるのは簡単
まずは1行だけ光らせてみる
ドットマトリクスLEDの仕様
OSL641501-BRA のデータシート によれば、内部の配線図は次の通り
https://gyazo.com/d419044cd7461d121f8477edc3af3027
行方向で見ると、LEDのアノードが共通に接続され、カソードが個別に制御できるようになっている
好きな場所を光らせる
例えば左上2カ所のLEDを点灯させるには、次のようにする
https://gyazo.com/6b230b648ce2383180a495151714e31a
9番ピンに電源(5Vとか)を供給した状態で
13番ピンと3番ピンを0Vに接続(短絡、スイッチON)し
ただ短絡すると過大電流でLEDが壊れるので、抵抗を挟む
4、10、6、11、15、16番ピンはどこにも接続しない(解放)
電子スイッチ
先の回路図は機械スイッチだった
実際には電子スイッチを使う
MOSFETというスイッチ
FPGAからON/OFFを制御できる
スタティック vs ダイナミック
これで1行だけ光らせることができる。→スタティック点灯
高速に1~8行目を順に点灯させることで、人間の目には全体が光っているように見せることができる。→ダイナミック点灯
明るさは1/8になる
ComProc CPU Board Rev.1はダイナミック点灯が前提の設計になっていて、それくらいがちょうど良い
やってみよう:スタティック点灯
FPGAボードのサンプルプログラムを試す
ワークショップのサンプルコード置き場
chap1/board/rev2_tangnano9kがComProc CPU Board Rev.2用のコード
SRAM Programで書き込むことで、初期プログラムが消されずに済む
ドットマトリクスLEDの最上段が10101010のパターンで点灯する
点灯させる行や列を変えてみる
Tang Nanoへのプログラムの書き込み方法についての参考資料
Sipeed Tang Nanoで遊んでみる (Linux版)
SiPeed Tang Nanoの環境構築(Windows編)
サンプルコード解説
main.svが中核となるコード
記述言語はSystemVerilog-2017
module main(入出力定義);~endmoduleはmainという名のモジュールを定義
「入出力定義」はモジュールの入出力信号を定義
~endmoduleにモジュールの内容を記載
モジュール
FPGAの回路記述をまとめる単位
電子部品で言うと「IC」のようなもの
入出力端子があり、何らかの機能を実現する
どのくらいの粒度でモジュールに分けるかに決まりはない
他のモジュールから利用されないものをトップレベルモジュールという
module mainはトップレベルモジュールである
入出力定義
モジュールの入出力信号を列挙する
C言語でいう「関数の仮引数」のようなもの
input sys_clkのように、単に信号名を書くと1ビットの信号
複数ビットを束ねる場合は[7:0] led_colのように添え字を書く
ピン定義
port.cst 信号名と物理的なピンの対応を定義する
拡張子はPhysical Constraints(物理的制約)の意味
IO_LOC "ピン名" ピン番号;
IO_PORT "ピン名" ピンの設定;
ピン定義の例
code:port.cstより
IO_LOC "sys_clk" 45;
IO_PORT "sys_clk" PULL_MODE=NONE IO_TYPE=LVCMOS33;
Tang Nano 4Kには27MHzの発振器が載っていて、45番に接続されている
PULL_MODE=NONE: 入力のプルアップ/プルダウン抵抗を無効にする
IO_TYPE=LVCMOS33: 入出力電圧を3.3Vに設定する
Tang Nano 4Kではどのピンを何Vに設定すべきかが規定されている
継続代入文
信号に値を継続的に代入する
「配線」に相当
assing 信号名 = 値;
assign led_col = 8'b10101010;
led_colの偶数ビットに0を、奇数ビットに1を「配線」する
継続代入文と信号の変化
右辺に現れる信号が変化するたびに代入される
assign led_row = rst_n ? 9'b000000001 : 9'd0;
rst_nが変化すると値が再計算され、led_rowが更新される
整数リテラルの記法
ビット幅 ' 進数 値
9'd0は、9ビットの0
8'b10101010は、8ビットの170
0b10101010 = 0xAA = 170
always文
always @(センシティビティリスト) begin~end
センシティビティリストに列挙された信号が変化した際にbegin~endを実行
always @(posedge sys_clk)はsys_clkの立ち上がりエッジで実行
手続き代入とlogic
手続き代入:always文の中にある代入
rst_n <= rst_n_raw;
手続き代入を行う信号はlogicとして宣言
logic rst_n;
クロック
FPGAは一般的にクロックに同期させて回路を動かす
sys_clkは27MHzの発振器が接続されているので、これを使うのが手軽
スイッチとチャタリング
rst_n_rawはタクタイルスイッチが接続されたピン
https://gyazo.com/93fd84b630fdc2e02ce79f2d07485ca2/thumb/150.jpg
スイッチを押したときと離したときにチャタリングが起きる
タクタイルスイッチのチャタリングを検証
チャタリングの緩和
チャタリングを緩和するためにクロックに同期して読み取る
code:Verilog
always @(posedge sys_clk) begin
rst_n <= rst_n_raw;
end
チャタリングが起きていても、クロックのタイミングでしか読まないので比較的安定する
やってみよう:ダイナミック点灯
1msくらいで点灯する行を切り替える
1msの時間待ち:クロックをカウンタで数える
行の切り替え:led_rowの中の1にするビットをずらす
000000001→000000010→000001000→……
ダイナミック点灯のサンプルプログラム
カウンタを用いた時間待ち
code:Verilog
always @(posedge sys_clk, negedge rst_n) begin
if (!rst_n)
counter <= 16'd0;
else if (counter >= PERIOD - 1)
counter <= 16'd0;
else
counter <= counter + 16'd1;
end
https://gyazo.com/26b3bd755c3460f2a42201da8d5b2636/thumb/350.png
行の切り替え
row_indexを「点灯する行」の番号とする
led_rowの中の1になるビットの位置とも言える
code:Verilog
assign led_row = led_on(counter) << row_index;
always @(posedge sys_clk) begin
if (!rst_n)
row_index <= 3'd0;
else if (counter == 0)
row_index <= row_index + 3'd1;
end
先頭の行でだけLEDを光らせる
led_colがLEDの点灯パターンを表す
code:Verilog
assign led_col = led_pattern(row_index);
function 7:0 led_pattern(input 2:0 row_index);
case (row_index)
3'd0: led_pattern = 8'b10101010;
default: led_pattern = 8'b00000000;
endcase
endfunction
行切り替え時の消灯
点灯させたまま行を切り替えると、前後の行がうっすら光ってしまう
ドットマトリクスLEDの行を切り替える前後で消灯させる
code:Verilog
function led_on(input 15:0 counter);
led_on = (GAP <= counter) && (counter < PERIOD - GAP);
endfunction
https://gyazo.com/05362c9fc2f4e4789f71a9aca4e16cf3/thumb/400.png