tear086 发表于 2010-3-10 11:16:15

PS2键盘,LCD1602显示:此处只简单处理了大写字母和数字(Veilog)

新近写的,水平有限,欢迎拍砖。

PS2键盘部分参考了特权的例程,这里表示感谢。
http://blog.ednchina.com/ilove314/153929/message.aspx

ps2_keyboard_test.v
----------------------------------------------------------
module ps2_keyboard_test(
input      CLOCK_50,
input      Q_KEY,
// PS2 Interface
input      PS2_CLK,
input      PS2_DAT,
// LCD1602 Interface
output LCD1602_DATA,            // LCD1602数据总线               
output       LCD1602_E,               // LCD1602使能
output       LCD1602_RS,            // LCD1602指令数据选择
output       LCD1602_RW               // LCD1602读写选择
);

//++++++++++++++++++++++++++++++++++++++
// PS2键盘采集 开始
//++++++++++++++++++++++++++++++++++++++
wire key_val;                     // 单一键值ASCII码
wire key_pressed;                     // 键盘按下标志

ps2_keyboard_drive ps2_u0(
.clk(CLOCK_50),
.rst_n(Q_KEY),
//
.ps2_clk(PS2_CLK),
.ps2_dat(PS2_DAT),
//
.key_pressed(key_pressed),
.key_val(key_val)
);
//--------------------------------------
// PS2键盘采集 结束
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
//LCD1602显示 开始
//++++++++++++++++++++++++++++++++++++++
// 0 ~ (8*16-1) = 128
reg row1_val;                   // LCD1602 第1行字符
reg row2_val;                   // LCD1602 第2行字符

always @ (posedge CLOCK_50, negedge Q_KEY)
if (!Q_KEY)
begin
    // 16bits-> 0123456789ABCDEF <-
    row1_val = "PS2 rcved is    ";
    row2_val = "Amy-studio Pub";
end
else
    row1_val <= key_val;


// 例化LCD1602驱动
lcd1602_drive lcd_1602_u0(
.clk            (CLOCK_50),
.rst_n            (Q_KEY),
// LCD1602 Input Value
.row1_val         (row1_val),
.row2_val         (row2_val),
// LCD1602 Interface
.lcd_data         (LCD1602_DATA),
.lcd_e            (LCD1602_E),
.lcd_rs         (LCD1602_RS),
.lcd_rw         (LCD1602_RW)
);
//--------------------------------------
//LCD1602显示 结束
//--------------------------------------


endmodule
----------------------------------------------------------

tear086 发表于 2010-3-10 11:16:59

lcd1602_drive.v
----------------------------------------------------------
module lcd1602_drive(
input            clk,               // 50MHz时钟
input            rst_n,             // 复位信号
// LCD1602 Input Value
input       row1_val,          // 第一行字符
input       row2_val,          // 第二行字符
// LCD1602 Interface
output reg lcd_data,          // 数据总线
output             lcd_e,             // 使能信号
output reg         lcd_rs,            // 指令、数据选择
output             lcd_rw             // 读、写选择
);

// +++++++++++++++++++++++++++++++++++++
// 分频模块 开始
// +++++++++++++++++++++++++++++++++++++
reg cnt;                         // 计数子

always @ (posedge clk, negedge rst_n)
if (!rst_n)
    cnt <= 0;
else
    cnt <= cnt + 1'b1;

// 500Khz ~ 1MHz 皆可
wire lcd_clk = cnt;               // (2^16 / 50M) = 1.31ms                                 
// -------------------------------------
// 分频模块 结束
// -------------------------------------


// +++++++++++++++++++++++++++++++++++++
// LCD1602驱动模块 开始
// +++++++++++++++++++++++++++++++++++++
// 格雷码编码:共40个状态
parameter IDLE         = 8'h00;
// 写指令,初始化
parameter DISP_SET   = 8'h01;         // 显示模式设置
parameter DISP_OFF   = 8'h03;         // 显示关闭
parameter CLR_SCR      = 8'h02;         // 显示清屏
parameter CURSOR_SET1= 8'h06;         // 显示光标移动设置
parameter CURSOR_SET2= 8'h07;         // 显示开及光标设置
// 显示第一行
parameter ROW1_ADDR    = 8'h05;         // 写第1行起始地址
parameter ROW1_0       = 8'h04;
parameter ROW1_1       = 8'h0C;
parameter ROW1_2       = 8'h0D;
parameter ROW1_3       = 8'h0F;
parameter ROW1_4       = 8'h0E;
parameter ROW1_5       = 8'h0A;
parameter ROW1_6       = 8'h0B;
parameter ROW1_7       = 8'h09;
parameter ROW1_8       = 8'h08;
parameter ROW1_9       = 8'h18;
parameter ROW1_A       = 8'h19;
parameter ROW1_B       = 8'h1B;
parameter ROW1_C       = 8'h1A;
parameter ROW1_D       = 8'h1E;
parameter ROW1_E       = 8'h1F;
parameter ROW1_F       = 8'h1D;
// 显示第二行
parameter ROW2_ADDR    = 8'h1C;         // 写第2行起始地址
parameter ROW2_0       = 8'h14;
parameter ROW2_1       = 8'h15;
parameter ROW2_2       = 8'h17;
parameter ROW2_3       = 8'h16;
parameter ROW2_4       = 8'h12;
parameter ROW2_5       = 8'h13;
parameter ROW2_6       = 8'h11;
parameter ROW2_7       = 8'h10;
parameter ROW2_8       = 8'h30;
parameter ROW2_9       = 8'h31;
parameter ROW2_A       = 8'h33;
parameter ROW2_B       = 8'h32;
parameter ROW2_C       = 8'h36;
parameter ROW2_D       = 8'h37;
parameter ROW2_E       = 8'h35;
parameter ROW2_F       = 8'h34;

reg current_state, next_state;    // 现态、次态

// FSM: always1
always @ (posedge lcd_clk, negedge rst_n)
if(!rst_n)current_state <= IDLE;
else      current_state <= next_state;

// FSM: always2
always
begin
case(current_state)
    IDLE      : next_state = DISP_SET;
    // 写指令,初始化
    DISP_SET    : next_state = DISP_OFF;
    DISP_OFF    : next_state = CLR_SCR;
    CLR_SCR   : next_state = CURSOR_SET1;
    CURSOR_SET1 : next_state = CURSOR_SET2;
    CURSOR_SET2 : next_state = ROW1_ADDR;
    // 显示第一行
    ROW1_ADDR   : next_state = ROW1_0;
    ROW1_0      : next_state = ROW1_1;
    ROW1_1      : next_state = ROW1_2;
    ROW1_2      : next_state = ROW1_3;
    ROW1_3      : next_state = ROW1_4;
    ROW1_4      : next_state = ROW1_5;
    ROW1_5      : next_state = ROW1_6;
    ROW1_6      : next_state = ROW1_7;
    ROW1_7      : next_state = ROW1_8;
    ROW1_8      : next_state = ROW1_9;
    ROW1_9      : next_state = ROW1_A;
    ROW1_A      : next_state = ROW1_B;
    ROW1_B      : next_state = ROW1_C;
    ROW1_C      : next_state = ROW1_D;
    ROW1_D      : next_state = ROW1_E;
    ROW1_E      : next_state = ROW1_F;
    ROW1_F      : next_state = ROW2_ADDR;
    // 显示第二行
    ROW2_ADDR   : next_state = ROW2_0;
    ROW2_0      : next_state = ROW2_1;
    ROW2_1      : next_state = ROW2_2;
    ROW2_2      : next_state = ROW2_3;
    ROW2_3      : next_state = ROW2_4;
    ROW2_4      : next_state = ROW2_5;
    ROW2_5      : next_state = ROW2_6;
    ROW2_6      : next_state = ROW2_7;
    ROW2_7      : next_state = ROW2_8;
    ROW2_8      : next_state = ROW2_9;
    ROW2_9      : next_state = ROW2_A;
    ROW2_A      : next_state = ROW2_B;
    ROW2_B      : next_state = ROW2_C;
    ROW2_C      : next_state = ROW2_D;
    ROW2_D      : next_state = ROW2_E;
    ROW2_E      : next_state = ROW2_F;
    ROW2_F      : next_state = ROW1_ADDR;
    //
    default   : next_state = IDLE ;
endcase
end

// FSM: always3
always @ (posedge lcd_clk, negedge rst_n)
begin
if(!rst_n)
begin
    lcd_rs   <= 0;
    lcd_data <= 8'hxx;
end
else
begin
    // 写lcd_rs
    case(next_state)      
      IDLE      : lcd_rs <= 0;
      // 写指令,初始化
      DISP_SET    : lcd_rs <= 0;
      DISP_OFF    : lcd_rs <= 0;
      CLR_SCR   : lcd_rs <= 0;
      CURSOR_SET1 : lcd_rs <= 0;
      CURSOR_SET2 : lcd_rs <= 0;
      // 写数据,显示第一行
      ROW1_ADDR   : lcd_rs <= 0;
      ROW1_0      : lcd_rs <= 1;
      ROW1_1      : lcd_rs <= 1;
      ROW1_2      : lcd_rs <= 1;
      ROW1_3      : lcd_rs <= 1;
      ROW1_4      : lcd_rs <= 1;
      ROW1_5      : lcd_rs <= 1;
      ROW1_6      : lcd_rs <= 1;
      ROW1_7      : lcd_rs <= 1;
      ROW1_8      : lcd_rs <= 1;
      ROW1_9      : lcd_rs <= 1;
      ROW1_A      : lcd_rs <= 1;
      ROW1_B      : lcd_rs <= 1;
      ROW1_C      : lcd_rs <= 1;
      ROW1_D      : lcd_rs <= 1;
      ROW1_E      : lcd_rs <= 1;
      ROW1_F      : lcd_rs <= 1;
      // 写数据,显示第二行
      ROW2_ADDR   : lcd_rs <= 0;
      ROW2_0      : lcd_rs <= 1;
      ROW2_1      : lcd_rs <= 1;
      ROW2_2      : lcd_rs <= 1;
      ROW2_3      : lcd_rs <= 1;
      ROW2_4      : lcd_rs <= 1;
      ROW2_5      : lcd_rs <= 1;
      ROW2_6      : lcd_rs <= 1;
      ROW2_7      : lcd_rs <= 1;
      ROW2_8      : lcd_rs <= 1;
      ROW2_9      : lcd_rs <= 1;
      ROW2_A      : lcd_rs <= 1;
      ROW2_B      : lcd_rs <= 1;
      ROW2_C      : lcd_rs <= 1;
      ROW2_D      : lcd_rs <= 1;
      ROW2_E      : lcd_rs <= 1;
      ROW2_F      : lcd_rs <= 1;
    endcase   
   
    // 写lcd_data
    case(next_state)
      IDLE      : lcd_data <= 8'hxx;
      // 写指令,初始化
      DISP_SET    : lcd_data <= 8'h38;
      DISP_OFF    : lcd_data <= 8'h08;
      CLR_SCR   : lcd_data <= 8'h01;
      CURSOR_SET1 : lcd_data <= 8'h06;
      CURSOR_SET2 : lcd_data <= 8'h0C;
      // 写数据,显示第一行
      ROW1_ADDR   : lcd_data <= 8'h80;
      ROW1_0      : lcd_data <= row1_val;
      ROW1_1      : lcd_data <= row1_val;
      ROW1_2      : lcd_data <= row1_val;
      ROW1_3      : lcd_data <= row1_val;
      ROW1_4      : lcd_data <= row1_val[ 95: 88];
      ROW1_5      : lcd_data <= row1_val[ 87: 80];
      ROW1_6      : lcd_data <= row1_val[ 79: 72];
      ROW1_7      : lcd_data <= row1_val[ 71: 64];
      ROW1_8      : lcd_data <= row1_val[ 63: 56];
      ROW1_9      : lcd_data <= row1_val[ 55: 48];
      ROW1_A      : lcd_data <= row1_val[ 47: 40];
      ROW1_B      : lcd_data <= row1_val[ 39: 32];
      ROW1_C      : lcd_data <= row1_val[ 31: 24];
      ROW1_D      : lcd_data <= row1_val[ 23: 16];
      ROW1_E      : lcd_data <= row1_val[ 15:8];
      ROW1_F      : lcd_data <= row1_val;
      // 写数据,显示第二行
      ROW2_ADDR   : lcd_data <= 8'hC0;
      ROW2_0      : lcd_data <= row2_val;
      ROW2_1      : lcd_data <= row2_val;
      ROW2_2      : lcd_data <= row2_val;
      ROW2_3      : lcd_data <= row2_val;
      ROW2_4      : lcd_data <= row2_val[ 95: 88];
      ROW2_5      : lcd_data <= row2_val[ 87: 80];
      ROW2_6      : lcd_data <= row2_val[ 79: 72];
      ROW2_7      : lcd_data <= row2_val[ 71: 64];
      ROW2_8      : lcd_data <= row2_val[ 63: 56];
      ROW2_9      : lcd_data <= row2_val[ 55: 48];
      ROW2_A      : lcd_data <= row2_val[ 47: 40];
      ROW2_B      : lcd_data <= row2_val[ 39: 32];
      ROW2_C      : lcd_data <= row2_val[ 31: 24];
      ROW2_D      : lcd_data <= row2_val[ 23: 16];
      ROW2_E      : lcd_data <= row2_val[ 15:8];
      ROW2_F      : lcd_data <= row2_val;
    endcase
end
end

assign lcd_e= lcd_clk;                // 数据在时钟高电平被锁存               
assign lcd_rw = 1'b0;                   // 只写
// -------------------------------------
// LCD1602驱动模块 结束
// -------------------------------------

endmodule
----------------------------------------------------------

tear086 发表于 2010-3-10 11:17:44

http://cache.amobbs.com/bbs_upload782111/files_27/ourdev_537576.png
(原文件名:PS2 键盘值.png)

ps2_keyboard_drive.v
----------------------------------------------------------
module ps2_keyboard_drive(
input            clk,               // 50M时钟信号
input            rst_n,               // 复位信号
//
input            ps2_clk,             // PS2接口时钟信号
input            ps2_dat,             // PS2接口数据信号
//
output reg       key_pressed,         // 键盘按下标志
output reg key_val            // 单一键值ASCII码
);

//++++++++++++++++++++++++++++++++++++++
// 检测PS2_CLK的下降沿 开始
//++++++++++++++++++++++++++++++++++++++
reg ps2_clk_r0, ps2_clk_r1;

always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
begin
    ps2_clk_r0 <= 1'b0;
    ps2_clk_r1 <= 1'b0;
end
else
begin
    ps2_clk_r0 <= ps2_clk;
    ps2_clk_r1 <= ps2_clk_r0;
end
end

/*            _____       _____       _
*ps2_clk_r0:      |_____|   |_____|
*                  _____       _____
* ~ps2_clk_r0: _____|   |_____|   |_
*                _____       _____
* ps2_clk_r1 : _|   |_____|   |_____
*                  _         _
* ps2_clk_n: _____| |_________| |_____
*/
wire ps2_clk_n = (~ps2_clk_r0) & ps2_clk_r1;
//--------------------------------------
// 检测PS2_CLK的下降沿 结束
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
// 从PS2采集数据 开始
//++++++++++++++++++++++++++++++++++++++
reg cnt;                        // 计数子 0x0 ~ 0xA

always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
    cnt <= 0;
else if (ps2_clk_n)                   // ps2_clk的下降沿
begin
    if (cnt >= 4'hA)
      cnt <= 0;
    else
      cnt <= cnt + 1'b1;
end
end

reg ps2_byte_buf;               // 采集到的字节的缓存

always @ (posedge clk, negedge rst_n)
begin
if(!rst_n)
    ps2_byte_buf <= 8'h0;
else if (ps2_clk_n)                   // ps2_clk的下降沿
    case (cnt)
      4'h1    : ps2_byte_buf <= ps2_dat;// bit0
      4'h2    : ps2_byte_buf <= ps2_dat;// bit1
      4'h3    : ps2_byte_buf <= ps2_dat;// bit2
      4'h4    : ps2_byte_buf <= ps2_dat;// bit3
      4'h5    : ps2_byte_buf <= ps2_dat;// bit4
      4'h6    : ps2_byte_buf <= ps2_dat;// bit5
      4'h7    : ps2_byte_buf <= ps2_dat;// bit6
      4'h8    : ps2_byte_buf <= ps2_dat;// bit7
      default : ;
    endcase
end
//--------------------------------------
// 从PS2采集数据 结束
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
// 键值处理 开始
//++++++++++++++++++++++++++++++++++++++
reg key_released;                     // 接收到段码F0后,松开标志
reg ps2_byte;                     // 采集到的字节

// 处理断码标志
always @ (posedge clk, negedge rst_n)
begin
if (!rst_n)
    key_released <= 0;
else if (cnt == 4'hA)               // 采集完一个字节?
begin
    if (ps2_byte_buf == 8'hF0)          // 接收到段码F0后
      key_released <= 1;                // 松开标志置一
    else
      key_released <= 0;                // 松开标志清零
end
end

// 采集键值
always @ (posedge clk, negedge rst_n)
begin            
if (!rst_n)
    key_pressed<= 0;
else if (cnt == 4'hA)               // 采集完一个字节?
begin      
    if (!key_released)                  // 有键按过?
    begin
      ps2_byte    <= ps2_byte_buf;      // 锁存当前键值
      key_pressed <= 1;               // 按下标志置一
    end
    else
      key_pressed <= 0;               // 按下标志清零
end
end
//--------------------------------------
// 键值处理 结束
//--------------------------------------


//++++++++++++++++++++++++++++++++++++++
// PS2键值-->ASCII 开始
// 此处只处理大写字母和数字
//++++++++++++++++++++++++++++++++++++++
always @ (*)
case (ps2_byte)
    8'h16   : key_val <= "1";
    8'h1E   : key_val <= "2";
    8'h26   : key_val <= "3";
    8'h25   : key_val <= "4";
    8'h2E   : key_val <= "5";
    8'h36   : key_val <= "6";
    8'h3D   : key_val <= "7";
    8'h3E   : key_val <= "8";
    8'h46   : key_val <= "9";
    8'h45   : key_val <= "0";
   
    8'h15   : key_val <= "Q";
    8'h1D   : key_val <= "W";
    8'h24   : key_val <= "E";
    8'h2D   : key_val <= "R";
    8'h2C   : key_val <= "T";
    8'h35   : key_val <= "Y";
    8'h3C   : key_val <= "U";
    8'h43   : key_val <= "I";
    8'h44   : key_val <= "O";
    8'h4D   : key_val <= "P";
   
    8'h1C   : key_val <= "A";
    8'h1B   : key_val <= "S";
    8'h23   : key_val <= "D";
    8'h2B   : key_val <= "F";
    8'h34   : key_val <= "G";
    8'h33   : key_val <= "H";
    8'h3B   : key_val <= "J";
    8'h42   : key_val <= "K";
    8'h4B   : key_val <= "L";
   
    8'h1A   : key_val <= "Z";
    8'h22   : key_val <= "X";
    8'h21   : key_val <= "C";
    8'h2A   : key_val <= "V";
    8'h32   : key_val <= "B";
    8'h31   : key_val <= "N";
    8'h3A   : key_val <= "M";
    default : key_val <= " ";
endcase
//--------------------------------------
// 键盘值-->ASCII 结束
//--------------------------------------

endmodule
----------------------------------------------------------

1181zjf 发表于 2010-3-10 11:40:02

mark

lmldai 发表于 2010-3-10 22:44:29

学习下

xinjie1023 发表于 2010-6-5 21:07:55

学习。

xiangxinaiqing 发表于 2010-6-7 21:03:33

M

maqingbiao 发表于 2010-6-8 22:38:16

不错

ringan865 发表于 2010-6-9 10:06:26

mark

wanwzy 发表于 2010-8-26 08:45:14

mark

oceanx 发表于 2010-8-26 11:25:57

mark

kunpeng032 发表于 2010-8-26 12:33:40

学习了。。。

geniusjia 发表于 2010-8-26 12:46:13

mark

lingaoxun 发表于 2010-8-28 11:14:13

heibaogame 发表于 2010-10-14 13:05:29

mark

avrpicarm 发表于 2010-10-14 14:58:06

注释很给力

yuphone 发表于 2010-10-14 16:01:39

回复【15楼】avrpicarm
-----------------------------------------------------------------------

呵呵,这就是我的风格。

AG17 发表于 2010-11-19 16:44:41

学习了

_ssgeyoujie 发表于 2011-1-31 01:13:20

学习了。

hui3700 发表于 2011-1-31 10:13:32

mark

shexujia 发表于 2011-2-22 12:02:40

谢谢楼主的共享,总算搞得动1602了

hbchf 发表于 2011-3-9 21:55:00

mark

liuhui1987812 发表于 2011-4-12 19:07:46

回复【楼主位】tear086 .COM 缺氧
-----------------------------------------------------------------------
我将代码下载到DE2-70上,发现有时候LCD没有显示,有时候又显示,但是代码初始给的是
PS2 rcved is
Amy-studio Pub 当我通过键盘输入一个字母时 PS2 rcved is
Amy-studio Pub 这个还在,我觉得应该通过代码把他清除掉。

ctk520 发表于 2011-8-12 09:38:42

mark!

lb693294195 发表于 2011-11-16 22:45:11

回复【2楼】tear086 .COM 缺氧
-----------------------------------------------------------------------

超牛啊,领教了

tongluren 发表于 2011-12-19 13:59:59

mark~

nuaatao 发表于 2011-12-21 20:24:30

mark

qciqci 发表于 2011-12-21 20:29:17

您好,冒昧问下,能将整个工程上传吗?对开发cpld整个流程还不是很熟悉,谢谢,楼主威武!

yusufu 发表于 2011-12-22 11:50:12

mark~~

mage99 发表于 2011-12-29 03:20:34

mark~

gonnavis 发表于 2011-12-30 04:14:02

mark

greatwall2 发表于 2011-12-30 08:12:23

mark: ps/2 keyboard

chenqi 发表于 2011-12-30 08:45:39

mark

fpga2009 发表于 2012-1-1 23:03:44

mark

lxb007 发表于 2012-3-7 22:35:52

MARK

89712568 发表于 2012-4-5 17:50:48

深奥。。

tianpengyu 发表于 2012-4-16 21:27:16

我用VHDL也写过 不会Veilog 感觉还好

vijay 发表于 2012-4-20 16:35:13

M{:biggrin:}
页: [1]
查看完整版本: PS2键盘,LCD1602显示:此处只简单处理了大写字母和数字(Veilog)