LCD12864带字库,型号:CM12864-12.其相关数据手册可以在百度中搜索“ST7920 系列中文图形液晶模块使用说明书”,里面有详细的介绍。这里就不在多描述。
其原理简图:(我们只需关心接口部分)
接口定义:这里注意V0,我一开始就栽倒这,网上搜到的资料中,这PIN可以悬空或接个滑动电阻,程序检查好多遍,没有发现问题,就是不显示,接到3.3V上也不行,后来把V0直接接到5V上,就可以正常显示。在这提醒大家数据手册仅供参考,具体还是以自己的实物为主。避免走没必要的弯路。
管脚号 |
管脚名称 |
电平 |
管脚功能描述 |
1 |
VSS |
0V |
电源地 |
2 |
VCC |
5V |
电源正 |
3 |
V0 |
- |
对比度(亮度)调整.(直接接到5V上最靠谱) |
4 |
RS(CS) |
H/L |
RS=“H”,表示DB7——DB0为显示数据 RS=“L”,表示DB7——DB0为显示指令数据 |
5 |
R/W(SID) |
H/L |
R/W=“H”,E=“H”,数据被读到DB7——DB0 R/W=“L”,E=“H→L”, DB7——DB0的数据被写到IR或DR |
6 |
E(SCLK) |
H/L |
使能信号 |
7 |
DB0 |
H/L |
三态数据线 |
8 |
DB1 |
H/L |
三态数据线 |
9 |
DB2 |
H/L |
三态数据线 |
10 |
DB3 |
H/L |
三态数据线 |
11 |
DB4 |
H/L |
三态数据线 |
12 |
DB5 |
H/L |
三态数据线 |
13 |
DB6 |
H/L |
三态数据线 |
14 |
DB7 |
H/L |
三态数据线 |
15 |
PSB |
H/L |
H:8位或4位并口方式,L:串口方式 |
16 |
NC |
- |
空脚 |
17 |
/RESET |
H/L |
复位端,低电平有效(悬空或接到5V都可以) |
18 |
VOUT |
- |
LCD驱动电压输出端(悬空) |
19 |
A |
VDD |
背光源正端(+5V) |
20 |
K |
VSS |
背光源负端(0V) |
ST7920数据手册中,提到每次在写命令之前都要判断LCD是否处于空闲状态,是为0,否则为1,本次试验全过程只写不读,为了避开LCD忙的状态,可以采取隔断时间写一次命令或数据。在指令表中,可以看到每个命令执行的时间,这里注意,除清除显示和地址归位,网上有不同的说法,有的说是1.6ms,有的4.6ms外,其他命令执行时间都是72us,不管怎么样,我们还是取最大值更为靠谱,4.6ms取个整数,好听点的6ms吧。当然可以取比这个更大(LCD在显示数据时,是一个接一个的显示出来,就可以察觉到,取的越大,显示速度就越慢),看自己的决定了。利用FPGA分频,产生一个周期6ms的时钟,给LCD的E管脚,进行控制写数据或命令。在8为并口写操作时序图中,DB0~DB7是在E为高时,写入数据。
知道了何时给LCD写数据或命令,还得知道如何把自己想要显示的东西显示在LCD屏幕上,这点也不难:
第一得知道中文字行的编码,可以查国标GB2312,来调用CGROM字库,比如"文:CEC4、少:C9D9、清:C7E5“。或可以下载“汉字十六进制转换工具”软件直接生成。
第二得知道显示文字的地址该如何设置,就是一个文字我可以摆在第一行的开头或结尾或其他行。如下图示。
第三若自带的库中没有你想要的东西时,可以自己定义,比如代码中的星星月亮组合图标,库中没有,那么我们可以自己定义(组合图标的编码可以用字模软件进行提取)。自定义字符具体操作,先设定CGRAM存储地址,其次把该组合图标的编码数据放置到该地址中,在设置该图标在LCD屏幕显示的位置,最后连续写两次数据(注意写的是数据不是命令,我也被这东东栽倒一次),先写高8it,后写低8bit,就可以了。此时知道写这数据到底是啥数据。那么就得理解0x0000,0x0002,0x0004,0x0006这四种编码.至于这四种编码有什么区别,我也不是很明白,在程序中,0x0000和0x0006没发现有区别,显示正常;0x0002,在星星的左上角有个小点;0x0004显示乱码。
第四半宽字型,数据手册中写的是”半宽字型ROM(HCGROM),总共提供126 个西文字型(16×8 点阵)“,应该就是英文字符。程序中的"LCD12864"。
在写代码之前理清思路,程序的流程该如何跑,写起代码来也比较顺手。如下图。前面设置好频率后,每步中的延时就不用在考虑了。
代码:(使用并口,只有写)
LCD12864.v
module LCD12864(
//input
sys_clk,
rst_n, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:] lcd_data;
output lcd_psb;//H:parallel module L:SPI module /***************************************************/
parameter T3MS = 'd149_999;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
INIT_DOT_SET = 'd5,
SET_DDRAM = 'd6,
WRITE_DATA1 = 'd7,
INIT_FUN_SET3 = 'd8,
SET_CGRAM = 'd9,
WRITE_DATA2 = 'd10,
SET_DDRAM2 = 'd11,
SET_CUSTOM_L = 'd12,
SET_CUSTOM_H = 'd13,
STOP = 'd14;
/***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1;
/***************************************************/
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if((state == WRITE_DATA1) || (state == WRITE_DATA2)|| (state == SET_CUSTOM_H) || (state == SET_CUSTOM_L))
lcd_rs <= 'b1; //写数据模式
else
lcd_rs <= 'b0; //写命令模式
/***************************************************/
reg [:] state;
reg [:] lcd_data;
reg [:] num;
reg en;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) begin
state <= IDLE;
lcd_data <= 'hzz;
en <= 'b1;
num <= 'd0;
end
else
case(state)
IDLE:
begin
state <= INIT_FUN_SET1;
lcd_data <= 'hzz;
en <= 'b1;
end INIT_FUN_SET1:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_FUN_SET2;
end INIT_FUN_SET2:
begin
lcd_data <= 'h30; //功能设定
state <= INIT_DISPLAY;
end INIT_DISPLAY:
begin
lcd_data <= 'h0c; //显示设定
state <= INIT_CLEAR;
end INIT_CLEAR:
begin
lcd_data <= 'h01; //清屏
state <= INIT_DOT_SET;
end INIT_DOT_SET:
begin
lcd_data <= 'h06; //进入点设定
state <= SET_DDRAM;
end SET_DDRAM:
begin
if(num == 'd0)
lcd_data <= 'h81;//1 line
else if(num == 'd12)
lcd_data <= 'h92;//2 line
else if(num == 'd18)
lcd_data <= 'h8a;//3 line
else if(num == 'd26)
lcd_data <= 'h99;//4 line state <= WRITE_DATA1;
end WRITE_DATA1:
begin
if(num <= 'd11) begin
num <= num + 'b1;
lcd_data <= dis_data;
if(num == 'd11)
state <= SET_DDRAM;
else
state <= WRITE_DATA1;
end
else if( num > 'd11 && num <= 7'd17) begin
num <= num + 'b1;
lcd_data <= dis_data;
if(num == 'd17)
state <= SET_DDRAM;
else
state <= WRITE_DATA1;
end
else if(num > 'd17 && num <= 7'd25) begin
num <= num + 'b1;
lcd_data <= dis_data;
if(num == 'd25)
state <= SET_DDRAM;
else
state <= WRITE_DATA1;
end
else if(num > 'd25 && num <= 7'd33) begin
num <= num + 'b1;
lcd_data <= dis_data;
if(num == 'd33)
state <= INIT_FUN_SET3;
else
state <= WRITE_DATA1;
end
end INIT_FUN_SET3:
begin
lcd_data <= 'h30;//功能设定
state <= SET_CGRAM;
end SET_CGRAM:
begin
lcd_data <= 'h40;//设定CGRAM字符的位置
state <= WRITE_DATA2;
end WRITE_DATA2:
begin
if(num >= 'd34 && num <= 7'd65) begin
num <= num + 'b1;
lcd_data <= dis_data;
state <= WRITE_DATA2;
end
else begin
num <= 'd0;
state <= SET_DDRAM2;
end
end SET_DDRAM2:
begin
lcd_data <= 'h9e; //4line
state <= SET_CUSTOM_H;//设置自定义显示字符编码
end SET_CUSTOM_H:
begin
lcd_data <= 'h00;//高8bit
state <= SET_CUSTOM_L;
end SET_CUSTOM_L:
begin
lcd_data <= 'h06;//低8bit 00:正常 02:星星左上角多了一小点 04:显示的是乱码 06:显示也正常
state <= STOP;
end STOP:
begin
en <= 'b0;//显示完了,lcd_e就一直拉为低
state <= STOP;
end default: state <= IDLE;
endcase
/***************************************************/
reg [:] dis_data;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n)
dis_data <= 'hzz;
else
case(num)
//1 Line 81位置显示 欢迎访问博客
'd0 : dis_data <= 8'hbb;
'd1 : dis_data <= 8'hb6;
'd2 : dis_data <= 8'hd3;
'd3 : dis_data <= 8'had;
'd4 : dis_data <= 8'hb7;
'd5 : dis_data <= 8'hc3;
'd6 : dis_data <= 8'hce;
'd7 : dis_data <= 8'hca;
'd8 : dis_data <= 8'hb2;
'd9 : dis_data <= 8'ha9;
'd10 : dis_data <= 8'hbf;
'd11 : dis_data <= 8'hcd;
//2Line 92位置显示 文少清
'd12 : dis_data <= 8'hce;
'd13 : dis_data <= 8'hc4;
'd14 : dis_data <= 8'hc9;
'd15 : dis_data <= 8'hd9;
'd16 : dis_data <= 8'hC7;
'd17 : dis_data <= 8'he5;
//3Line 8a位置显示 LCD12864
'd18 : dis_data <= "L";
'd19 : dis_data <= "C";
'd20 : dis_data <= "D";
'd21 : dis_data <= "1";
'd22 : dis_data <= "2";
'd23 : dis_data <= "8";
'd24 : dis_data <= "6";
'd25 : dis_data <= "4";
//4Line 99位置显示 谢谢!
'd26 : dis_data <= 8'hd0;
'd27 : dis_data <= 8'hbb;
'd28 : dis_data <= 8'hd0;
'd29 : dis_data <= 8'hbb;
'd30 : dis_data <= 8'ha3;
'd31 : dis_data <= 8'ha1;
'd32 : dis_data <= " "; //这两次空格可以不写的
'd33 : dis_data <= " ";
//4line 9e位置显示星星月亮
'd34 : dis_data <= 8'h08;
'd35 : dis_data <= 8'h20;
'd36 : dis_data <= 8'h1c;
'd37 : dis_data <= 8'h10;
'd38 : dis_data <= 8'h1c;
'd39 : dis_data <= 8'h1c;
'd40 : dis_data <= 8'hff;
'd41 : dis_data <= 8'h9e;
'd42 : dis_data <= 8'h7f;
'd43 : dis_data <= 8'h1e;
'd43 : dis_data <= 8'h1c;
'd45 : dis_data <= 8'h1f;
'd46 : dis_data <= 8'h3e;
'd47 : dis_data <= 8'h1f;
'd48 : dis_data <= 8'h3e;
'd49 : dis_data <= 8'h1f;
'd50 : dis_data <= 8'h77;
'd51 : dis_data <= 8'h1f;
'd52 : dis_data <= 8'h41;
'd53 : dis_data <= 8'h3f;
'd54 : dis_data <= 8'h00;
'd55 : dis_data <= 8'h7e;
'd56 : dis_data <= 8'h00;
'd57 : dis_data <= 8'hfe;
'd58 : dis_data <= 8'h83;
'd59 : dis_data <= 8'hfc;
'd60 : dis_data <= 8'h7f;
'd61 : dis_data <= 8'hf8;
'd62 : dis_data <= 8'h3f;
'd63 : dis_data <= 8'hf0;
'd64 : dis_data <= 8'h0f;
'd65 : dis_data <= 8'hc0;
default: dis_data <= 'h00;
endcase
/***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
endmodule
显示效果:
图片显示待续。