altera小实验——LCD1602显示
源自於https://blog.csdn.net/moon9999/article/details/62436955
所用板子为altera DE2板子,FPGA为Cyclone II:EP2C35F672C6,quartus版本为13.0
1.LCD规格与接口
DE2板子上的LCD为16*2,是最简单的LCD显示屏。
数据储存器地址为第一行00H~0fH,第二行40H~4fH。但是需要注意的是,在需要向数据存贮器赋值时,需要赋值为80H~8fH和c0H~cfH,因为只有第一位置1数据存贮器地址地址输入才为有效。
接下来看下LCD用户接口,在此截取DE2用户手册。
用户接口标注如下:
[7:0]Data //8位数据总线
LCD_EN //使能信号
LCD_RW //读/写选择信号
LCD_RS //数据/命令选择信号
LCD_BLON //背光灯亮灭
LCD_ON //总开关
其中,LCD_ON LCD_BLON可暂时不关注,使用时置为1即可。
LCD_EN LCD_RS LCD_RW Data在时序中需要格外关注。
2.LCD时序图
LCD的时序图极为简单,概括来讲就是:
在LCD_EN=1时进行数据交互(因此可令LCD_EN = CLK_LCD,数据操作均发生在时钟高有效时段)
在LCD_RW=0时写入数据,LCD_RW=1时读出数据(基本没有用到)
在LCD_RS=0时操作指令,LCD_RS=1时操作数据
整体时序图如下(来自网络),里面的细节时序可暂时忽略。
3.LCD基本操作指令
LCD的操作整体上分为两类:指令配置与数据读写显示。指令表如下(来源网络)。
指令的解读(摘取自网络):
指令1:清屏,光标同时复位至00H位置(左上角);
指令2:光标复位,即光标复位至00H;
指令3:光标与显示移动设置。I/D:光标移动方向,高电平右移,低电平左移;S:屏幕上所有文字是否左移或右移,高电平表示有效,低电平表示无效;
指令4:显示开关控制。D:控制整体的显示开与关,高电平表示开显示,低电平表示关显示。C:控制光标的开与关,高电平表示有光标,低电平表示无光标 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁;
指令5:光标或显示移位 S/C :高电平时显示移动的文字,低电平时移动光标;
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符;功能设置命令 DL:高电平时为4位总线,低电平时为8位总线 N:低电平时为单行显示,高电平时为双行显示,F:低电平时显示5X7的点阵字符,高电平时显示5X10的显示字符。
指令7:字符发生器RAM地址设置;
指令8:DDRAM地址设置;
指令9:读忙信号和光标地址 BF:忙标志位,高电平表示忙,此时模块不能接收命令或数据,如果为低电平表示不忙。
4.如何显示一个字符
字符产生器CGROM中内置了192个常用字符,自定义字符产生器CGRAM还允许用户自定义8个字符。内置的192个字符如下
如要显示“A”,则需要在指令10阶段时候写入数据为8‘b0100_0001。
本次将大小写字母以参数形式写在了character.v中。主程序中可以直接调用。
parameter space = 8'b0010_0000;
parameter CAPITAL_A = 8'b0100_0001;
parameter CAPITAL_B = 8'b0100_0010;
parameter CAPITAL_C = 8'b0100_0011;
parameter CAPITAL_D = 8'b0100_0100;
parameter CAPITAL_E = 8'b0100_0101;
parameter CAPITAL_F = 8'b0100_0110;
parameter CAPITAL_G = 8'b0100_0111;
parameter CAPITAL_H = 8'b0100_1000;
parameter CAPITAL_I = 8'b0100_1001;
parameter CAPITAL_J = 8'b0100_1010;
parameter CAPITAL_K = 8'b0100_1011;
parameter CAPITAL_L = 8'b0100_1100;
parameter CAPITAL_M = 8'b0100_1101;
parameter CAPITAL_N = 8'b0100_1110;
parameter CAPITAL_O = 8'b0100_1111;
parameter CAPITAL_P = 8'b0101_0001;
parameter CAPITAL_Q = 8'b0101_0001;
parameter CAPITAL_R = 8'b0101_0010;
parameter CAPITAL_S = 8'b0101_0011;
parameter CAPITAL_T = 8'b0101_0100;
parameter CAPITAL_U = 8'b0101_0101;
parameter CAPITAL_V = 8'b0101_0110;
parameter CAPITAL_W = 8'b0101_0111;
parameter CAPITAL_X = 8'b0101_1000;
parameter CAPITAL_Y = 8'b0101_1001;
parameter CAPITAL_Z = 8'b0101_1010;
parameter LOWERCASE_a = 8'b0110_0001;
parameter LOWERCASE_b = 8'b0110_0010;
parameter LOWERCASE_c = 8'b0110_0011;
parameter LOWERCASE_d = 8'b0110_0100;
parameter LOWERCASE_e = 8'b0110_0101;
parameter LOWERCASE_f = 8'b0110_0110;
parameter LOWERCASE_g = 8'b0110_0111;
parameter LOWERCASE_h = 8'b0110_1000;
parameter LOWERCASE_i = 8'b0110_1001;
parameter LOWERCASE_j = 8'b0110_1010;
parameter LOWERCASE_k = 8'b0110_1011;
parameter LOWERCASE_l = 8'b0110_1100;
parameter LOWERCASE_m = 8'b0110_1101;
parameter LOWERCASE_n = 8'b0110_1110;
parameter LOWERCASE_o = 8'b0110_1111;
parameter LOWERCASE_p = 8'b0111_0001;
parameter LOWERCASE_q = 8'b0111_0001;
parameter LOWERCASE_r = 8'b0111_0010;
parameter LOWERCASE_s = 8'b0111_0011;
parameter LOWERCASE_t = 8'b0111_0100;
parameter LOWERCASE_u = 8'b0111_0101;
parameter LOWERCASE_v = 8'b0111_0110;
parameter LOWERCASE_w = 8'b0111_0111;
parameter LOWERCASE_x = 8'b0111_1000;
parameter LOWERCASE_y = 8'b0111_1001;
parameter LOWERCASE_z = 8'b0111_1010;
5.程序分析
1)接口
module work(
ClOCK_50,
KEY,
LCD_RW,
LCD_EN,
LCD_RS,
LCD_DATA,
LCD_ON,
LCD_BLON
);
input ClOCK_50;
input [3:0]KEY;
output LCD_RW, LCD_EN, LCD_RS, LCD_ON, LCD_BLON;
output [8:0]LCD_DATA;
assign LCD_RW = rw;
assign LCD_EN = CLK_500Hz;
assign LCD_RS = rs;
assign LCD_ON = 1;
assign LCD_BLON = 1;
assign LCD_DATA = data;
`include "character.v"
主时钟为50MHz,之后需要进行手动分频,LCD时钟不能过快。
!KEY[1]作为复位信号。其余信号均输出至LCD。
2)状态与参数设置
/*共14个状态:1个空闲状态,9个设置状态,两行读写包括地址与数据共计4个状态*/
parameter s_idle = 4'd0;
parameter s_clear = 4'd1;
parameter s_cursor = 4'd2;
parameter s_inputmode = 4'd3;
parameter s_switchmode = 4'd4;
parameter s_shiftmode = 4'd5;
parameter s_setfunction = 4'd6;
parameter s_setgeneraddr = 4'd7;
parameter s_setdataaddr1 = 4'd8;
parameter s_readbasy = 4'd9;
parameter s_writecgram1 = 4'd10;
parameter s_readram = 4'd11;
parameter s_setdataaddr2 = 4'd12;
parameter s_writecgram2 = 4'd13;
parameter IDLE = 8'bzzzz_zzzz;
parameter CLEAR = 8'b0000_0001; //清屏
parameter CURSOR = 8'b0000_0010; //光标返回
parameter INPUTMODE = 8'b0000_0110; //置输入模式 _01(I/D)S
parameter SWITCHMODE = 8'b0000_1111; //显示开关控制 _1DCB
parameter SHIFTMODE = 8'b0001_1100; //光标或字符移位 _(S/R)(R/L)**
parameter SETFUNCTION = 8'b0011_1100; //置功能 00_001(DL)_NF**
parameter SETGENERADDR = 8'b0100_0000; //置字符发生存贮地址
parameter SETDATAADDR = 8'b1000_0000; //置数据存贮器地址 起始地址
3)状态机跳转,并非每个状态都是必须的,参考了其他程序后,状态机如下
reg [3:0]state;
reg rw, rs;
reg [7:0]data;
reg [7:0]addr;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
state <= s_idle;
data <= 8'b0;
rw <= 1'b0;
rs <= 1'b0;
addr <= 8'b1000_0000;
end
else begin
case (state)
s_idle :begin
state <= s_clear;
data <= IDLE;
rw <= rw;
rs <= rs;
end
s_clear :begin
state <= s_inputmode;
data <= CLEAR;
rw <= 1'b0;
rs <= 1'b0;
end
s_inputmode :begin
state <= s_switchmode;
data <= INPUTMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_switchmode :begin
state <= s_shiftmode;
data <= SWITCHMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_shiftmode :begin
state <= s_setfunction;
data <= SHIFTMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_setfunction :begin
state <= s_setdataaddr1;
data <= SETFUNCTION;
rw <= 1'b0;
rs <= 1'b0;
end
s_setdataaddr1:begin
state <= s_writecgram1;
data <= addr;
rw <= 1'b0;
rs <= 1'b0;
end
s_writecgram1 :begin
if (addr == 8'b1000_1111) begin
state <= s_setdataaddr2;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= 8'b1100_0000;
end
else begin
state <= s_writecgram1;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= addr + 1'b1;
end
end
s_setdataaddr2:begin
state <= s_writecgram2;
data <= addr;
rw <= 1'b0;
rs <= 1'b0;
end
s_writecgram2 :begin
if (addr == 8'b1100_1111) begin
state <= s_setdataaddr1;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= 8'b1000_0000;
end
else begin
state <= s_writecgram2;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= addr + 1'b1;
end
end
default :begin
state <= state;
data <= data;
rw <= rw;
rs <= rs;
end
endcase
end
end
4)函数:设置每一个地址所显示数据(此处可DIY)
function [7:0]lcd_data;
input [7:0]lcd_addr;
begin
case(lcd_addr)
8'h81 :lcd_data = CAPITAL_I;
8'h83 :lcd_data = LOWERCASE_a;
8'h84 :lcd_data = LOWERCASE_m;
8'h86 :lcd_data = CAPITAL_G;
8'h87 :lcd_data = CAPITAL_J;
8'h88 :lcd_data = CAPITAL_M;
8'hc1 :lcd_data = CAPITAL_W;
8'hc2 :lcd_data = LOWERCASE_h;
8'hc3 :lcd_data = LOWERCASE_o;
8'hc5 :lcd_data = LOWERCASE_a;
8'hc6 :lcd_data = LOWERCASE_r;
8'hc7 :lcd_data = LOWERCASE_e;
8'hc9 :lcd_data = LOWERCASE_y;
8'hca :lcd_data = LOWERCASE_o;
8'hcb :lcd_data = LOWERCASE_u;
default:lcd_data = space;
endcase
end
endfunction
5)时钟分频
parameter CLK_LCD_times = 19'd030000;
reg [22:0]cnt;
reg CLK_LCD;
always@(posedge ClOCK_50 or posedge rst) begin
if(rst) begin
cnt <= 19'd0;
CLK_500Hz <= 1'b0;
end
else if(cnt == CLK_LCD_times) begin
cnt <= 19'd0;
CLK_LCD <= ~CLK_LCD;
end
else begin
cnt <= cnt+1'b1;
end
end
至此程序完成。文末附上完整代码。
6.程序结果
ps.完整代码如下
module work(
ClOCK_50,
KEY,
LCD_RW,
LCD_EN,
LCD_RS,
LCD_DATA,
LCD_ON,
LCD_BLON
);
input ClOCK_50;
input [3:0]KEY;
output LCD_RW, LCD_EN, LCD_RS, LCD_ON, LCD_BLON;
output [8:0]LCD_DATA;
assign LCD_RW = rw;
assign LCD_EN = CLK_LCD;
assign LCD_RS = rs;
assign LCD_ON = 1;
assign LCD_BLON = 1;
assign LCD_DATA = data;
`include "character.v"
/*共14个状态:1个空闲状态,9个设置状态,两行读写包括地址与数据共计4个状态*/
parameter s_idle = 4'd0;
parameter s_clear = 4'd1;
parameter s_cursor = 4'd2;
parameter s_inputmode = 4'd3;
parameter s_switchmode = 4'd4;
parameter s_shiftmode = 4'd5;
parameter s_setfunction = 4'd6;
parameter s_setgeneraddr = 4'd7;
parameter s_setdataaddr1 = 4'd8;
parameter s_readbasy = 4'd9;
parameter s_writecgram1 = 4'd10;
parameter s_readram = 4'd11;
parameter s_setdataaddr2 = 4'd12;
parameter s_writecgram2 = 4'd13;
parameter IDLE = 8'bzzzz_zzzz;
parameter CLEAR = 8'b0000_0001; //清屏
parameter CURSOR = 8'b0000_0010; //光标返回
parameter INPUTMODE = 8'b0000_0110; //置输入模式 _01(I/D)S
parameter SWITCHMODE = 8'b0000_1111; //显示开关控制 _1DCB
parameter SHIFTMODE = 8'b0001_1100; //光标或字符移位 _(S/R)(R/L)**
parameter SETFUNCTION = 8'b0011_1100; //置功能 00_001(DL)_NF**
parameter SETGENERADDR = 8'b0100_0000; //置字符发生存贮地址
parameter SETDATAADDR = 8'b1000_0000; //置数据存贮器地址 起始地址
wire clk, rst;
assign clk = CLK_LCD;
assign rst = !KEY[1];
reg [3:0]state;
reg rw, rs;
reg [7:0]data;
reg [7:0]addr;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
state <= s_idle;
data <= 8'b0;
rw <= 1'b0;
rs <= 1'b0;
addr <= 8'b1000_0000;
end
else begin
case (state)
s_idle :begin
state <= s_clear;
data <= IDLE;
rw <= rw;
rs <= rs;
end
s_clear :begin
state <= s_inputmode;
data <= CLEAR;
rw <= 1'b0;
rs <= 1'b0;
end
s_inputmode :begin
state <= s_switchmode;
data <= INPUTMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_switchmode :begin
state <= s_shiftmode;
data <= SWITCHMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_shiftmode :begin
state <= s_setfunction;
data <= SHIFTMODE;
rw <= 1'b0;
rs <= 1'b0;
end
s_setfunction :begin
state <= s_setdataaddr1;
data <= SETFUNCTION;
rw <= 1'b0;
rs <= 1'b0;
end
s_setdataaddr1:begin
state <= s_writecgram1;
data <= addr;
rw <= 1'b0;
rs <= 1'b0;
end
s_writecgram1 :begin
if (addr == 8'b1000_1111) begin
state <= s_setdataaddr2;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= 8'b1100_0000;
end
else begin
state <= s_writecgram1;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= addr + 1'b1;
end
end
s_setdataaddr2:begin
state <= s_writecgram2;
data <= addr;
rw <= 1'b0;
rs <= 1'b0;
end
s_writecgram2 :begin
if (addr == 8'b1100_1111) begin
state <= s_setdataaddr1;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= 8'b1000_0000;
end
else begin
state <= s_writecgram2;
data <= lcd_data(addr);
rw <= 1'b0;
rs <= 1'b1;
addr <= addr + 1'b1;
end
end
default :begin
state <= state;
data <= data;
rw <= rw;
rs <= rs;
end
endcase
end
end
function [7:0]lcd_data;
input [7:0]lcd_addr;
begin
case(lcd_addr)
8'h81 :lcd_data = CAPITAL_I;
8'h83 :lcd_data = LOWERCASE_a;
8'h84 :lcd_data = LOWERCASE_m;
8'h86 :lcd_data = CAPITAL_G;
8'h87 :lcd_data = CAPITAL_J;
8'h88 :lcd_data = CAPITAL_M;
8'hc1 :lcd_data = CAPITAL_W;
8'hc2 :lcd_data = LOWERCASE_h;
8'hc3 :lcd_data = LOWERCASE_o;
8'hc5 :lcd_data = LOWERCASE_a;
8'hc6 :lcd_data = LOWERCASE_r;
8'hc7 :lcd_data = LOWERCASE_e;
8'hc9 :lcd_data = LOWERCASE_y;
8'hca :lcd_data = LOWERCASE_o;
8'hcb :lcd_data = LOWERCASE_u;
default:lcd_data = space;
endcase
end
endfunction
parameter CLK_LCD_times = 19'd030000;
reg [22:0]cnt;
reg CLK_LCD;
always@(posedge ClOCK_50 or posedge rst) begin
if(rst) begin
cnt <= 19'd0;
CLK_500Hz <= 1'b0;
end
else if(cnt == CLK_LCD_times) begin
cnt <= 19'd0;
CLK_LCD <= ~CLK_LCD;
end
else begin
cnt <= cnt+1'b1;
end
end
endmodule
————————————————
原文链接:https://blog.csdn.net/moon9999/article/details/62436955
2020年1月28日 星期二
訂閱:
張貼留言 (Atom)
Messaging API作為替代方案
LINE超好用功能要沒了!LINE Notify明年3月底終止服務,有什麼替代方案? LINE Notify將於2025年3月31日結束服務,官方建議改用Messaging API作為替代方案。 //CHANNEL_ACCESS_TOKEN = 'Messaging ...
-
python pip 不是内部或外部命令 -- 解決方法 要安裝 Pyqt5 1. 首先,開啟命令提示字元。 2. 輸入 pip3 install pyqt5 好像不能執行 ! ! 錯誤顯示 : ‘ pip3 ’ 不是內部或外部命令、可執行的程式或批...
-
課程講義 下載 11/20 1) PPT 下載 + 程式下載 http://www.mediafire.com/file/cru4py7e8pptfda/106%E5%8B%A4%E7%9B%8A2-1.rar 11/27 2) PPT 下載...
-
• 認 識 PreFix、InFix、PostFix PreFix(前序式):* + 1 2 + 3 4 InFix(中序式): (1+2)*(3+4) PostFix(後序式):1 2 + 3 4 + * 後 序式的運算 例如: 運算時由 後序式的...
沒有留言:
張貼留言