aitendoで売っている(売っていた?)小型カラー液晶をCPLDでドライブするテストです。
4年ちょっと昔に売られ始めた時に買ったものです。その時はFT245RLあたりのICを使って、PCから制御していましたが、書き換えをもっと早くしたいというのと、CPLDのお勉強も兼ねて。
昔の記事//beiznotes.org/200911071257539777-2/
MAX IIのページにSC1602Bキャラクタ液晶の制御例がありましたが、Verilogコードが初心者が書いたみたいになっているのですが、なんなんでしょうか。とりあえずソースコード検索をしようとgoogle codeに行ったら404で、サービス終了で、Koderに行ったらこれもまたサービス移行していた。Ohlohコードサーチでverilogコードを探したらなんとかひとつ見つかって、それを参考にしました。
CPLDではCPUみたいに上から順番に処理するというのがめんどくさいです。ループばかりの本処理にはいってしまえばよいのですが、その前の初期化処理はひとつひとつにステートを割り当てて、ステートマシンで処理しています。
オプティマイズさんのところのMAX II基板に接続しました。
ピンクを表示させたら発色が悪いので、ガンマとか、VCOMとかも設定することにしたけども、それでもまだ悪いです。まあそもそもバックライトムラなどもございますので、妥協すべきことがらなんでしょう。
Verilog HDLコードです。197LE
`timescale 1 ps / 1 ps
module lcdzy( RESX, CSX, D_CX, WRX, DATA );
//input rstx;
output reg [7:0] DATA;
output reg RESX;//reset
output reg CSX;//chip select low enable
output reg D_CX;//data / command
output reg WRX;//write enable
wire rstx;
assign rstx = 1'b1;
//output RDX; //-> always write
reg [3:0] state, next_st; //state reg.
reg [15:0] wait_ms;
reg [5:0] init_addr;
wire [7:0] init_in, data_in;
reg [15:0] data_addr;//16 bit 7+7+1 128*128*2
reg [13:0] wait_cnt;
reg [7:0] write_data;
wire init_dcx;
wire clk;
rc_osc rc_osc(1'b1, clk);
LCD_INIT_SEQ lcd_init_seq(init_addr, init_in, init_dcx);
LCD_DATA_SEQ lcd_data_seq(data_addr, data_in);
// BIDIRECTIONAL TRI STATE LCD DATA BUS
//assign DATA_BUS = (RDX? 8'bz: data_value);
`define IDOL 4'h1
`define INIT 4'h0
`define RESET1 4'h2
`define RESET2 4'h3
`define SLEEP_OUT 4'h4
`define DISP_ON 4'h5
`define INIT_SEQ 4'h6
`define RAM_WR 4'h7
`define DATA_SEQ 4'h8
`define WRITE 4'h9
`define WRITE_ENA 4'ha
`define WAIT 4'hb
`define SOFT_RST 4'hc
`define MS_CNT 14'd5555 // 1e-3 s * 5.5e6 Hz = 5.5e3
`define DATA_CNT_MAX 16'h7fff//
initial begin
state = `INIT;
wait_ms <= 8'h0;
wait_cnt <= 1'b0;
init_addr <= 6'h0;
data_addr <= 16'h000;
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b0;
end
//clk gen -> slow enogh at 3.3 MHz
/*always @ posedge clk_3M3 or negede reset)
if (!rst) begin
clk_count_
*/
//state reg
/*always @ ( posedge clk or negedge rst ) begin
if ( !rst )
cur_st <= INIT;
else
cur_st <= next_st;
end*/
//state gen
always @ ( posedge clk or negedge rstx ) begin
if ( !rstx ) begin
state <= `INIT;
wait_ms <= 16'h0;
wait_cnt <= 1'b0;
init_addr <= 6'h0;
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b0;
end else begin
case ( state )
`IDOL: begin
next_st <= `INIT;
state <= `WAIT;
wait_ms <= 16'h1000;
end
`INIT: begin //wait lcd pow on
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b1;
state <= `WAIT;
next_st <= `RESET1;
wait_ms <= 6'd50;//50ms
end
`RESET1: begin
RESX <= 1'b0;//RESET
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b1;
state <= `WAIT;
wait_ms <= 6'd1;//more than 10us
next_st <= `RESET2;
end
`RESET2: begin//WAIT stable
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b1;
state <= `WAIT;
wait_ms <= 7'd120;//120ms
next_st <= `SLEEP_OUT;
end
`SLEEP_OUT: begin//sleep out
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= 1'b0;
WRX <= 1'b1;
write_data <= 8'h11;
state <= `WRITE;
next_st <= `DISP_ON;
wait_ms <= 7'd120;//120ms
end
`DISP_ON: begin//disp on
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= 1'b0;
WRX <= 1'b1;
write_data <= 8'h29;
state <= `WRITE;
next_st <= `INIT_SEQ;
wait_ms <= 7'd120;//120ms
end
`INIT_SEQ: begin
if ( init_in == 8'h00
&& init_dcx == 1'b0 ) begin //end init
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b0;
WRX <= 1'b1;
state <= `RAM_WR;
init_addr <= 4'h0;
//DATA <= 8'h00;
end else begin
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= init_dcx;
WRX <= 1'b1;
write_data <= init_in;
state <= `WRITE;
next_st <= `INIT_SEQ;
init_addr <= init_addr + 1'b1;
end
end
`RAM_WR: begin
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= 1'b0;
WRX <= 1'b1;
write_data <= 8'h2c;
state <= `WRITE;
next_st <= `DATA_SEQ;
wait_ms <= 1'b0;//0 ms
end
`DATA_SEQ: begin
if ( data_addr > `DATA_CNT_MAX ) begin
RESX <= 1'b1;
CSX <= 1'b1;
D_CX <= 1'b1;
WRX <= 1'b1;
DATA <= 8'h00;
state = `IDOL;
data_addr <= 16'h0000;
end else begin
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= 1'b1;
WRX <= 1'b1;
write_data <= data_in;
state <= `WRITE;
next_st <= `DATA_SEQ;
data_addr <= data_addr + 1'b1;
end
end
`WRITE: begin
WRX <= 1'b0;//wait 30 ns
DATA <= write_data;
state <= `WRITE_ENA;
end
`WRITE_ENA: begin
WRX <= 1'b1;//wait 30 ns
if (wait_ms == 0)
state <= next_st;
else
state <= `WAIT;
end
`WAIT: begin
if ( wait_ms > 1'b0 ) begin
state <= `WAIT;
if ( wait_cnt < `MS_CNT ) begin
wait_cnt <= wait_cnt + 1'b1;
end else begin
wait_cnt <= 1'b0;
wait_ms <= wait_ms - 1'b1;
end
end else begin
state <= next_st;
wait_cnt <= 1'b0;
end
end
`SOFT_RST: begin //software reset
RESX <= 1'b1;
CSX <= 1'b0;
D_CX <= 1'b0;
WRX <= 1'b1;
write_data <= 8'h01;
state <= `WRITE;
next_st <= `SLEEP_OUT;
wait_ms <= 7'd120;
end
endcase
end
end
endmodule
module LCD_INIT_SEQ ( addr, out, dcx );
input [5:0] addr;
output reg [7:0] out;
output reg dcx;
always
case ( addr )// after soft_reset, sleep_out
6'h00: begin out <= 8'h36; dcx <= 1'b0; end//Memory data acc
6'h01: begin out <= 8'h88; dcx <= 1'b1; end//mirror xy BGR
6'h02: begin out <= 8'h3a; dcx <= 1'b0; end//interface
6'h03: begin out <= 8'h05; dcx <= 1'b1; end//16bit mode
6'h04: begin out <= 8'h2a; dcx <= 1'b0; end//column addr set
6'h05: begin out <= 8'h00; dcx <= 1'b1; end//
6'h06: begin out <= 8'h02; dcx <= 1'b1; end//02h
6'h07: begin out <= 8'h00; dcx <= 1'b1; end//
6'h08: begin out <= 8'h81; dcx <= 1'b1; end//81h
6'h09: begin out <= 8'h2b; dcx <= 1'b0; end//row addr set
6'h0a: begin out <= 8'h00; dcx <= 1'b1; end//
6'h0b: begin out <= 8'h03; dcx <= 1'b1; end//03h
6'h0c: begin out <= 8'h00; dcx <= 1'b1; end//
6'h0d: begin out <= 8'h82; dcx <= 1'b1; end//82h
6'h0e: begin out <= 8'hc1; dcx <= 1'b0; end//POW CTL 2 VGHH VCLL
6'h0f: begin out <= 8'h07; dcx <= 1'b1; end//14.7V, -12.25V
6'h10: begin out <= 8'hc5; dcx <= 1'b0; end//VCOM CTL1
6'h11: begin out <= 8'h3c; dcx <= 1'b1; end//VCOMH
6'h12: begin out <= 8'h4f; dcx <= 1'b1; end//VCOML
6'h13: begin out <= 8'he0; dcx <= 1'b0; end//gamma +
6'h14: begin out <= 8'h06; dcx <= 1'b1; end//
6'h15: begin out <= 8'h0e; dcx <= 1'b1; end//
6'h16: begin out <= 8'h05; dcx <= 1'b1; end//
6'h17: begin out <= 8'h20; dcx <= 1'b1; end//
6'h18: begin out <= 8'h27; dcx <= 1'b1; end//
6'h19: begin out <= 8'h23; dcx <= 1'b1; end//
6'h1a: begin out <= 8'h1c; dcx <= 1'b1; end//
6'h1b: begin out <= 8'h21; dcx <= 1'b1; end//
6'h1c: begin out <= 8'h20; dcx <= 1'b1; end//
6'h1d: begin out <= 8'h1c; dcx <= 1'b1; end//
6'h1e: begin out <= 8'h26; dcx <= 1'b1; end//
6'h1f: begin out <= 8'h2f; dcx <= 1'b1; end//
6'h20: begin out <= 8'h00; dcx <= 1'b1; end//
6'h21: begin out <= 8'h03; dcx <= 1'b1; end//
6'h22: begin out <= 8'h00; dcx <= 1'b1; end//
6'h23: begin out <= 8'h24; dcx <= 1'b1; end//
6'h24: begin out <= 8'he1; dcx <= 1'b0; end//gamma -
6'h25: begin out <= 8'h06; dcx <= 1'b1; end//
6'h26: begin out <= 8'h10; dcx <= 1'b1; end//
6'h27: begin out <= 8'h05; dcx <= 1'b1; end//
6'h28: begin out <= 8'h21; dcx <= 1'b1; end//
6'h29: begin out <= 8'h27; dcx <= 1'b1; end//
6'h2a: begin out <= 8'h22; dcx <= 1'b1; end//
6'h2b: begin out <= 8'h1c; dcx <= 1'b1; end//
6'h2c: begin out <= 8'h21; dcx <= 1'b1; end//
6'h2d: begin out <= 8'h1f; dcx <= 1'b1; end//
6'h2e: begin out <= 8'h1d; dcx <= 1'b1; end//
6'h2f: begin out <= 8'h27; dcx <= 1'b1; end//
6'h30: begin out <= 8'h2f; dcx <= 1'b1; end//
6'h31: begin out <= 8'h05; dcx <= 1'b1; end//
6'h32: begin out <= 8'h03; dcx <= 1'b1; end//
6'h33: begin out <= 8'h00; dcx <= 1'b1; end//
6'h34: begin out <= 8'h3f; dcx <= 1'b1; end//
default: begin out <= 8'h00; dcx <= 1'b0; end//nop -> end
endcase
endmodule
module LCD_DATA_SEQ ( addr, out );
input [15:0] addr;
output reg [7:0] out;
always if ( (addr & 16'h0001) == 0 )
out <= {5'h1f, 3'b011};//ff719c
//out <= {2'hf, ( ( addr >> 9 ) & 6'hff )}; //0x8000
else
out <= {3'b100, 5'b10011};
//out <= ( ( addr >> 1 ) & 8'hff );
endmodule
ピンバック: aitendoのTFT液晶モジュール ZY-FGD1442701V1を、パソコンから制御する方法