/*************************************************
//Module: lcd1602
//File Name: lcd1602.v
//Version: 2.0
//Date: 2011.12.5
//Author: wang li
//Code Type: RTL
//Description: LCD1602液晶显示
// clk——时钟输入(1位)
// rst——复位信号输入(1位)
// 输入看模块
-- 内部显示地址
-- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-- 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 第一行
-- 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 第二行
**************************************************/
module lcd1602(clk,reset,LCD_E,LCD_RW,LCD_RS,LCD_DATA);
input clk; //时钟信号,50Mhz
input reset; //复位信号,低电平进行复位
output LCD_E; //1602使能引脚,1时读取信息,1->0(下降沿)执行命令
output LCD_RS; //1602数据——H/命令——L 选择端
output LCD_RW; //1602写——L/读——H 选择端
output [7:0] LCD_DATA; //1602数据传输端口
//----------------------
//-----时钟分频模块-----
wire clk_lcd; // 用于将clk_div模块输出的clk_lcd接到lcd_ctrl中
reg [11:0] cnt; //对时钟进行计数分频
reg clk_buf;
always @(posedge clk or negedge reset)
begin
if(!reset) //低电平进行复位
cnt<=0;
else if(cnt>=50000) //1ms的范围
cnt<=0;
else
cnt<=cnt+1;
end
always @(posedge clk or negedge reset)
begin
if(!reset) //低电平进行复位
cnt<=0;
else if(cnt==50000) //1ms的范围
clk_buf<=~clk_buf;
assign clk_lcd=clk_buf;
end
//----------------------
//-----显示控制模块-----
parameter
CLEAR = 4'd1, //清屏指令
SET_FUNCTION = 4'd2, //工作方式设置指令
SWITCH_MODE = 4'd3, //开关控制指令
SET_MODE = 4'd4, //设定显示屏或光标移动方向指令
SET_DDRAM1 = 4'd5, //设定第一行DDRAM地址指令
WRITE_RAM1 = 4'd6, //向第一行写入的数码
WRITE_RAM2 = 4'd7,
IDLE = 4'd8; //空闲
reg [127:0] Data_First = "www.endchina.com"; //液晶显示的第一行的数据
reg [127:0] Data_Second = " chick_kid "; //液晶显示的第二行的数据
reg [127:0] Data_First_Buf,
Data_Second_Buf; //液晶显示的数据缓存
//-----LCD状态机寄存器-----
reg[3:0] state; //当前状态寄存器
reg lcd_rs_reg = 1'b0; //lcd_rs输出寄存器
reg lcd_rw_reg = 1'b0; //lcd_rw输出寄存器
reg[7:0] lcd_data_reg; //lcd_data输出寄存器
reg lcd_e_sel;
reg [5:0] display_count;
wire LCD_E; //1602使能引脚,1时读取信息,1->0(下降沿)执行命令
wire LCD_RS; //1602数据——H/命令——L 选择端
wire LCD_RW; //1602写——L/读——H 选择端
wire [7:0] LCD_DATA; //1602数据传输端口
//-----状态控制-----
always @(posedge clk_lcd or negedge reset)
begin
if(!reset)
begin
state<=CLEAR;
lcd_rs_reg<=0;
lcd_data_reg<=0;
lcd_e_sel<=1; //为什么是1
display_count<=0;
end
else
case(state)
CLEAR: //清屏指令
begin
state<=SET_FUNCTION;
lcd_data_reg<=8'b0000_0001;
end
SET_FUNCTION: //工作方式设置
begin
state<=SWITCH_MODE;
lcd_data_reg<=8'b0011_1000;
//第4位DL:0=数据总线为4位;1=数据总线为8位
//第3位N:0=显示1行;1=显示2行
//第2位F:0=5×7点阵/每字符;1=5×10点阵/每字符
end
SWITCH_MODE: //显示开关控制指令
begin
state<=SET_MODE;
lcd_data_reg<=8'b0000_1100;
//第2位D:0=显示功能关;1=显示功能开
//第1位C:0=无光标;1=有光标
//第0位B:0=光标闪烁 1=光标不闪烁
end
SET_MODE: //设定显示屏或光标移动方向指令
begin
state <= SET_DDRAM1;
lcd_data_reg<=8'b0000_0110;
//第3位S/C;第2位R/L
// S/C R/L 设定情况
// 0 0 光标左移1格,且AC值减1
// 0 1 光标右移1格,且AC值加1
// 1 0 显示器上字符全部左移一格,但光标不动
// 1 1 显示器上字符全部右移一格,但光标不动
end
SET_DDRAM1: //设定第一行DDRAM地址指令
begin
state<=WRITE_RAM1;
//-----写入第一行显示起始地址:-----
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 第一行
// 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 第二行
lcd_data_reg<=8'h80+8'h01; //第一行第二个位置
Data_First_Buf<=Data_First;
end
WRITE_RAM1: //向第一行写入的数码
begin
if(display_count==16) //表示第一行写完
begin
lcd_data_reg<=8'h80+8'h40; //送入写第二行的指令
lcd_rs_reg<=0;
display_count<=0;
Data_Second_Buf<=Data_Second;
state<=WRITE_RAM2; //写完第一行进入写第二行状态
end
else
begin
lcd_data_reg<=Data_First_Buf[127:120];
Data_First_Buf<=(Data_First_Buf<<8); //左移
lcd_rs_reg<=1; RS=1表示写数据
display_count<=display_count+1;
state<=WRITE_RAM1;
end
end
WRITE_RAM2: //向第2行写入的数码
begin
if(display_count==16)
begin
lcd_e_sel<=0;
lcd_rs_reg<=0;
display_count<=0;
state<=IDLE; //写完进入空闲状态
end
else
begin
lcd_data_reg<=Data_Second_Buf[127:120];
Data_Second_Buf <= (Data_Second_Buf << 8);
lcd_rs_reg <= 1;
display_count <= display_count +1;
state <= WRITE_RAM2;
end
end
IDLE:
begin
state<=IDLE;
end
default:
state<=CLEAR;
endcase
assign LCD_RW = lcd_rw_reg;
assign LCD_RS = lcd_rs_reg;
assign LCD_DATA = lcd_data_reg;
assign LCD_E = lcd_e_sel ? clk_lcd : 1'b0;
end
endmodule
求助
最新推荐文章于 2024-08-24 18:27:59 发布