DE0にLCDを半田づけしてFPGAで制御する

DE0の拡張キットにLCDがついていて、これ使えばいろいろ表示できるかなぽわわ〜んと気になっていたので、半田づけしてきた。

とはいえ、半田ごてとか持ってないし、そもそも半田づけするようなスペースの余裕はないので、今回も「博多図工室」で場所と道具を借りて作業した。


LCDを半田付けしてDE0の電源を入れると、回路を書き換えていなければ「Welcome to the Altera DE0 Board」と表示される。

ほんとなら、バックライトもつくはずなのだけど、もしかしたら半田づけに失敗したかもしれない・・・


ところで、LCDとはなんぞやという話だけど、見てのとおり液晶ディスプレイで、ここで取り付けたのはキャラクタ液晶なので、グラフィックは表示できないけどフォントを持っていて文字の出力が簡単にできる。
で、規格があるわけではないのだけど、だいたい同じコントローラICか互換品を使っているようなので、どの製品も同じように制御できるらしい。
そのあたりの説明はここが役に立った。
LCDモジュール SC162シリーズ


ただ、「文字の出力が簡単にできる」とはいえ、直接の制御はやっぱりめんどう。そのあたりの制御はここが役にたった。
2.3. キャラクタ液晶の利用


上のサイトや仕様書のコマンド発行手順には、コマンドを入れてしばらくたってENを立ち上げて、最後にアドレスを指定することになっているのだけど、今回は、一気にコマンドとアドレスを指定して同時にENも立ち上げてる。そして、一定時間たってENをおろす。それで動いた。DE0付属のサンプルもそのような処理になっていた。
ENを立ち上げてからおろすまでは、230nsということなので、12クロックほどウェイトを入れている。

    if(cnt == 17'd0) begin
        param <= msgdata[26:25];
        data <= msgdata[24:17];
        en <= 1'b1;
        cnt <= cnt + 17'd1;
    end else if(cnt == 17'd12) begin
        en <= 1'b0;
        cnt <= 17'd0;
        phase <= `WAIT;
    end else begin
        cnt = cnt + 17'd1;
    end

msgdataには コマンドx2bit、アドレスx8bit、ウェイト時間x17bitというデータを入れてる。


はまったのが、起動時の挙動で、どうもうまくいかないと思ってボタンを押して起動するようにしたらうまくいった。DE0のCD-ROMに入ってるサンプルを見ると、20ビットのカウンタがいっぱいになるまでウェイトを入れてから起動処理をしているようだ。なので、同様の処理をいれてみたら、うまく動くようになった。

    reg reset;
    reg [19:0] reset_delay;
    always @(posedge clk) begin
        if(reset_delay != 20'hfffff) begin
            reset_delay <= reset_delay + 20'd1;
            reset <= 1'b0;
        end else begin
            reset <= 1'b1;
        end
    end 


あと、仕様書では画面クリアのあとの処理時間は1.53msで、50MHzのクロックだと1クロック20nsだから、76500クロックまてばいいはずなのだけど、2文字分表示されなかったので、2150クロック2文字分で4300クロック余分に待つようにしたら、ちゃんと一文字目から表示された。
1文字出力の処理時間は、仕様書どおり43μsで2150クロックのウェイトでいいようだ。


ということで、だいぶハードのスペックシートも読めるようになってきた。しかし、これを実際のアプリケーションで制御するのは、結構大変な気がする。
あと、このソースはDE0のサンプルが動いている前提なので、LCDの初期化を行っていないので注意。

`define COMM 3'd0
`define WAIT 3'd1
`define FIN 3'd2

module lcd(
    input clk, 
    output lcd_rw,
    output lcd_rs,
    output lcd_en,
    output [7:0] lcd_data,
    output lcd_blon
    
);
    assign lcd_blon = 1'b1;

    reg [1:0] param;
    assign lcd_rs = param[1];
    assign lcd_rw = param[0];
    reg en;
    assign lcd_en = en;
    reg [7:0]data;
    assign lcd_data = data;
    
    reg [2:0] phase;
    reg [16:0] cnt;

    reg reset;
    reg [19:0] reset_delay;
    always @(posedge clk) begin
        if(reset_delay != 20'hfffff) begin
            reset_delay <= reset_delay + 20'd1;
            reset <= 1'b0;
        end else begin
            reset <= 1'b1;
        end
    end 
    
    reg [26:0] msgdata;
    reg [3:0] msgindex;
    
    always @(posedge clk or negedge reset) begin
        if(!reset) begin
            cnt <= 17'd0;
            phase <= `COMM;
            msgindex <= 0;
        end else begin
            case(phase)
                `COMM:begin
                    if(cnt == 17'd0) begin
                        param <= msgdata[26:25];//2'b00;
                        data <= msgdata[24:17];//8'h01;
                        en <= 1'b1;
                        cnt <= cnt + 17'd1;
                    end else if(cnt == 17'd12) begin
                        en <= 1'b0;
                        cnt <= 17'd0;
                        phase <= `WAIT;
                    end else begin
                        cnt = cnt + 17'd1;
                    end
                end
                `WAIT:begin
                    if(cnt == msgdata[16:0])begin
                        if(msgindex == 4'h4)begin
                            cnt = 17'd0;
                            phase <= `FIN;
                        end else begin
                            cnt = 17'd0;
                            phase <= `COMM;
                            msgindex <= msgindex + 1;
                        end
                    end else begin
                        cnt = cnt + 17'd1;
                    end
                end
                default: begin
                end
            endcase
        end
    end

    always @(msgindex) begin
        case(msgindex)
        4'h0: msgdata <= {2'b00, 8'h01, 17'd80800};//clear wait for 76500?
        4'h1: msgdata <= {2'b10, 8'h40, 17'd2150};//'@'
        4'h2: msgdata <= {2'b10, 8'h6b, 17'd2150};//'k'
        4'h3: msgdata <= {2'b10, 8'h69, 17'd2150};//'i'
        4'h4: msgdata <= {2'b10, 8'h73, 17'd2150};//'s'
        default: msgdata <= {2'b00, 8'h00, 17'd0};
        endcase
    end
endmodule