坛子里有很多单片机驱动LCD12864的代码,但用FPGA来驱动的比较少。
今天做了FPGA驱动LCD12864的实验,把代码发上来,与大家分享。
PS:仿佛这样的任务不适合用FPGA来做。
代码如下:
module LCD12864(
input CLK, //系统时钟输入
input RST_N, //系统复位输入
output reg LCD_RS, //LCD的寄存器选择输出信号
output LCD_RW, //LCD的读、写操作选择输出信号
output LCD_E, //LCD使能信号
output reg [7:0]LCD_D, //LCD的数据总线(不进行读操作,故为输出)
output PSB,
output RST_LCD
);
reg CLK_LCD; //LCD时钟信号
reg [23:0]cnt;
assign PSB = 1;
assign RST_LCD = 1;
// CLK频率为50MHz, 产生LCD时钟信号, 10Hz
always @(posedge CLK or negedge RST_N)
begin
if (!RST_N)
begin
cnt <= 24'b0;
CLK_LCD <= 0;
end
else if(cnt == 2499999)
begin
cnt <= 0;
CLK_LCD <= ~CLK_LCD;
end
else
cnt <= cnt +1'b1;
end
reg [8:0] state; //State Machine code
parameter IDLE = 9'b00000000; //初始状态,下一个状态为CLEAR
parameter SETFUNCTION = 9'b00000001; //功能设置:8位数据接口
parameter SETFUNCTION2 = 9'b00000010;
parameter SWITCHMODE = 9'b00000100; //显示开关控制:开显示,光标和闪烁关闭
parameter CLEAR = 9'b00001000; //清屏
parameter SETMODE = 9'b00010000; //输入方式设置:数据读写操作后,地址自动加一/画面不动
parameter SETDDRAM = 9'b00100000; //设置DDRAM的地址:第一行起始为0x80/第二行为0x90
parameter WRITERAM = 9'b01000000; //数据写入DDRAM相应的地址
parameter STOP = 9'b10000000; //LCD操作完毕,释放其控制
reg flag; //标志位,LCD操作完毕为0
reg [4:0]char_cnt;
reg [7:0]data_disp;
assign LCD_RW = 1'b0; //没有读操作,R/W信号始终为低电平
assign LCD_E = (flag == 1)?CLK_LCD:1'b0; //E信号出现高电平以及下降沿的时刻与LCD时钟相同
always @(posedge CLK_LCD or negedge RST_N) //只有在写数据操作时,RS信号才为高电平,其余为低电平
begin
if(!RST_N)
LCD_RS <= 1'b0;
else if(state == WRITERAM)
LCD_RS <= 1'b1;
else
LCD_RS <= 1'b0;
end
// State Machine
always @(posedge CLK_LCD or negedge RST_N)
begin
if(!RST_N)
begin
state <= IDLE;
LCD_D <= 8'bzzzzzzzz;
char_cnt <= 5'b0;
flag <= 1'b1;
end
else begin
case(state)
IDLE:
begin
state <= SETFUNCTION;
LCD_D <= 8'bzzzzzzzz;
end
SETFUNCTION:
begin
state <= SETFUNCTION2;
LCD_D <= 8'h30; // 8-bit 控制界面,基本指令集动作
end
SETFUNCTION2:
begin
state <= SWITCHMODE;
LCD_D <= 8'h30; // 清屏
end
SWITCHMODE:
begin
state <= CLEAR;
LCD_D <= 8'h0c; // 显示开关:开显示,光标和闪烁关闭
end
CLEAR:
begin
state <= SETMODE;
LCD_D <= 8'h01;
end
SETMODE:
begin
state <= SETDDRAM;
LCD_D <= 8'h06; // 输入方式设置: 数据读写后,地址自动加1,画面不动
end
SETDDRAM:
begin
state <= WRITERAM;
if(char_cnt == 0) //如果显示的是第一个字符,则设置第一行的首字符地址
begin
LCD_D <= 8'h80; //Line1
end
else //第二次设置时,是设置第二行的首字符地址
begin
LCD_D <= 8'h90; //Line2
end
end
WRITERAM:
begin
if(char_cnt <= 11)
begin
char_cnt <= char_cnt + 1'b1;
LCD_D <= data_disp;
if( char_cnt == 11 )
state <= SETDDRAM;
else
state <= WRITERAM;
end
else if( char_cnt >= 12 && char_cnt <= 25)
begin
LCD_D <= data_disp;
if(char_cnt == 25)
begin
state <= STOP;
char_cnt <= 5'b0;
flag <= 1'b0;
end
else
begin
state <= WRITERAM;
char_cnt <= char_cnt + 1'b1;
end
end
end
STOP: state <= STOP;
default: state <= IDLE;
endcase
end
end
always @(char_cnt) //输出的字符
begin
case (char_cnt)
6'd0: data_disp = "H"; // 第
6'd1: data_disp = "e"; // 一
6'd2: data_disp = "l"; // 行
6'd3: data_disp = "l"; // 的
6'd4: data_disp = "o"; // 显
6'd5: data_disp = ","; // 示
6'd6: data_disp = "w"; // 内
6'd7: data_disp = "o"; // 容
6'd8: data_disp = "r"; //
6'd9: data_disp = "l"; // 字符串变量形式
6'd10: data_disp = "d"; //
6'd11: data_disp ="!"; //
6'd12: data_disp = "w"; // 第二行的显示内容
6'd13: data_disp = "w"; //
6'd14: data_disp = "w";
6'd15: data_disp = ".";
6'd16: data_disp = "o";
6'd17: data_disp = "u";
6'd18: data_disp = "r";
6'd19: data_disp = "d";
6'd20: data_disp = "e";
6'd21: data_disp = "v";
6'd22: data_disp = ".";
6'd23: data_disp = "c";
6'd24: data_disp = "n";
default : data_disp = 8'd32;
endcase
end
endmodule |
|