前几天参加学校的FPGA比赛,做的是频率检测器,就2天时间,做的还可以,从1HZ到1MHZ,结果用1602液晶显示,还没有整理好,等整理好了也拿出来,先把1602液晶显示部分整理了下,拿出来和大家分享,我用的板子是EP<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />1C3T144C8N,时钟是24MHZ跑起来没有问题。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
代码我还是贴上来,直接上了……..
顶层模块 lcd.v
`timescale 1ns / 1ps
module lcd (
clk,sys_rst_n,
lcd_en,lcd_rs,lcd_rw,
lcd_data
);
input clk; //时钟24MHz,周期为41.6ns
input sys_rst_n; //系统复位信号,低电平复位
output lcd_en; //使能信号,1时读取信息,1→0(下降沿)执行指令
output lcd_rs; // 0=输入指令,1=输入数据
output lcd_rw; //0=向LCD写入指令或数据,1=从LCD读取信息
output[7:0] lcd_data; //LCD数据信号
wire clk_lcd; //用于将clk_div模块输出的clk_lcd链接到lcd_ctrl中
clk_div U1(
.clk(clk),
.sys_rst_n(sys_rst_n),
.clk_lcd(clk_lcd)
);
lcd_ctrl U2(
.clk_lcd(clk_lcd),
.sys_rst_n(sys_rst_n),
.lcd_en(lcd_en),
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_data(lcd_data)
);
Endmodule
分频模块clk_div.v
`timescale 1ns / 1ps
module clk_div (
clk,sys_rst_n,
clk_lcd
);
input clk;
input sys_rst_n; //sys_rst_n为全局复位信号(高电平有效)
output clk_lcd; //clk_lcd链接到lcd_ctrl中
reg [19:0] cnt; //对时钟进行计数分频
reg clk_BUF;
/********************************************************************************
** 模块名称:分频器
** 功能描述:通过计数器实现分频功能.
********************************************************************************/
always @ (posedge clk or negedge sys_rst_n)
if(!sys_rst_n) //低电平复位
cnt <= 20'd0;
else if(cnt >= 26026) //学习特权同学,提高代码健壮性
cnt <= 20'd0;
else
cnt <= cnt+1'b1;
always @ (posedge clk or negedge sys_rst_n)
if(!sys_rst_n)
clk_BUF <= 0;
else if(cnt == 26026)
clk_BUF <= ~clk_BUF;
assign clk_lcd = clk_BUF;
endmodule
液晶显示控制模块lcd_ctrl.v
`timescale 1ns / 1ps
module lcd_ctrl (
clk_lcd,sys_rst_n,
lcd_rs,lcd_rw,lcd_en,
lcd_data
);
input clk_lcd;
input sys_rst_n; //系统复位信号,低电平复位
output lcd_rs; // 0=输入指令,1=输入数据
output lcd_rw; //0=向LCD写入指令或数据,1=从LCD读取信息
output lcd_en; //使能信号,1时读取信息,1→0(下降沿)执行指令
output[7:0] lcd_data; //LCD数据信号
parameter CLEAR = 4'd1, //清屏指令
SETFUNCTION = 4'd2, //模式设置指令
SWITCHMODE = 4'd3, //开关控制指令
SETMODE = 4'd4, //设定显示屏或光标移动方向指令
SETDDRAM_1 = 4'd5, //设定第一行DDRAM地址指令
WRITERAM_1 = 4'd6, //向第一行写入的数码
WRITERAM_2 = 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_en_sel;
reg [5:0] display_count;
//状态控制
always @ (posedge clk_lcd or negedge sys_rst_n)
if(!sys_rst_n)
begin
state <= CLEAR;
lcd_rs_reg <= 1'b0;
lcd_data_reg <= 8'b00000000;
lcd_en_sel <= 1'b1;
display_count <= 4'b0;
end
else
case(state)
CLEAR:begin
state <= SETFUNCTION;
lcd_data_reg <= 8'b00000001;
end
SETFUNCTION:begin
state <= SWITCHMODE;
lcd_data_reg <= 8'b00111000;
end
SWITCHMODE:begin
state <= SETMODE;
lcd_data_reg <= 8'b00001100;
end
SETMODE:begin
state <= SETDDRAM_1;
lcd_data_reg <= 8'b00000110;
end
SETDDRAM_1:begin
state <= WRITERAM_1;
lcd_data_reg <= 8'b10000000; //写入第一行显示起始地址:第一行第二个位置
Data_First_Buf <= Data_First;
end
WRITERAM_1:begin
if(display_count == 6'd16) //display_count等于14时表示第一行数据已写完
begin
lcd_data_reg <= 8'b11000000; //送入写第二行的指令
lcd_rs_reg <= 1'b0;
display_count <= 4'b0;
Data_Second_Buf <= Data_Second;
state <= WRITERAM_2; //写完第一行进入写第二行状态
end
else
begin
lcd_data_reg <= Data_First_Buf[127:120];
Data_First_Buf <= (Data_First_Buf << 8);
lcd_rs_reg <= 1'b1; //RS=1表示写数据
display_count <= display_count + 1'b1;
state <= WRITERAM_1;
end
end
WRITERAM_2:begin
if(display_count == 6'd16)
begin
lcd_en_sel <= 1'b0;
lcd_rs_reg <= 1'b0;
display_count <= 4'b0;
state <= IDLE; //写完进入空闲状态
end
else
begin
lcd_data_reg <= Data_Second_Buf[127:120];
Data_Second_Buf <= (Data_Second_Buf << 8);
lcd_rs_reg <= 1'b1;
display_count <= display_count + 1'b1;
state <= WRITERAM_2;
end
end
IDLE:begin
state <= IDLE;
end
default:state <= CLEAR;
endcase
/*
功能设定指令
指令功能 指令编码 执行时间
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 /us
功能设定 0 0 0 0 1 DL N F X X 40
功能:设定数据总线位数、显示的行数及字型。参数设定的情况如下:
位名 设置
DL 0=数据总线为4位 1=数据总线为8位
N 0=显示1行 1=显示2行
F 0=5×7点阵/每字符 1=5×10点阵/每字符
显示开关控制指令
指令功能 指令编码 执行时间
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 /us
显示开关控制 0 0 0 0 0 0 1 D C B 40
功能:控制显示器开/关、光标显示/关闭以及光标是否闪烁。参数设定的情况如下:
位名 设置
D 0=显示功能关 1=显示功能开
C 0=无光标 1=有光标
B 0=光标闪烁 1=光标不闪烁
清屏指令
指令功能 指令编码 执行时间
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 /ms
清屏 0 0 0 0 0 0 0 0 0 1 1.64
功能:<1> 清除液晶显示器,即将DDRAM的内容全部填入"空白"的ASCII码20H;
<2> 光标归位,即将光标撤回液晶显示屏的左上方;
<3> 将地址计数器(AC)的值设为0。
设定显示屏或光标移动方向指令
指令功能 指令编码 执行时间
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 /us
设定显示屏或 0 0 0 0 0 1 S/C R/L X X 40
光标移动方向
功能:使光标移位或使整个显示屏幕移位。参数设定的情况如下:
S/C R/L 设定情况
0 0 光标左移1格,且AC值减1
0 1 光标右移1格,且AC值加1
1 0 显示器上字符全部左移一格,但光标不动
1 1 显示器上字符全部右移一格,但光标不动
*/
assign lcd_rw = lcd_rw_reg;
assign lcd_rs = lcd_rs_reg;
assign lcd_data = lcd_data_reg;
assign lcd_en = lcd_en_sel ? clk_lcd : 1'b0;
endmodule
觉得注释的还算详细吧,有什么错误的地方请大家指教,一起学习,嘿嘿
用户1638582 2013-8-20 11:47
用户402547 2012-12-25 20:42
用户383871 2011-9-15 15:53