📄 ps2_keyboard_interface.v
字号:
//*****************************************************************************//
// PS2键盘接口程序 //
// 系统时钟48MHz //
// 最后修改日期2006.04.05 //
//*****************************************************************************//
//程序源于John Clayton的"ps2_keyboard_interface"
//------------------------------------------------------------------------------
//部分输入输出接口说明
//rx_extended:为扩展码(8'hE0)输出的标志位,当输出为'1'时说明输出的扫描码是扩展码。
// 如:按下"右ALT"键时(键码通码E0,11),rx_extended=1'b1,rx_scan_code=8'h11.
//rx_released:为断码(8'hF0)输出的标志位,当输出为'1'时说明输出的扫描码是断码。
// 如:按下"M"键后松开(M键断码F0,3A),rx_released=1'b1,rx_scan_code=8'h3A.
// 当扫描键码即是扩展码又是断码,则,rx_extended,rx_released都为1.
// 如:按下"右CTRL"键后松开(断码E0,F0,14),rx_extended=1'b1,rx_released=1'b1,
// rx_scan_code=8'h14.
//rx_shift_key_on:"SHIFT"键标志位,当输出为'1'时,表示"SHIFT"键是按住的.
//------------------------------------------------------------------------------
//传输数据时帧格式:
//收低位在前,高位在后
// bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 bit8 bit9 bit10
// 开始(0) D0 D1 D2 D3 D4 D5 D6 D7 奇校验位 停止(1)
//------------------------------------------------------------------------------
//******************************************************************************//
`define TOTAL_BITS 11 // 一个帧数据有11位数据
`define EXTEND_CODE 16'hE0 //扩展码如R_CTRL的通码为(E0,14)
`define RELEASE_CODE 16'hF0 //断码如R_CTRL的断码为(E0,F0,14);"F"的断码为(F0,2B)
`define LEFT_SHIFT 16'h12 //左SHIFT键通码
`define RIGHT_SHIFT 16'h59 //右SHIFT键断码
module ps2_keyboard_interface (
clock,
reset,
ps2_clk_in,
ps2_data_in,
ps2_clk_out,
ps2_data_out,
ps2_clk_dir,
ps2_data_dir,
rx_extended,
rx_released,
rx_shift_key_on,
rx_scan_code,
rx_ascii,
rx_data_ready,
rx_read,
tx_data,
tx_write,
tx_write_ack,
tx_error_no_keyboard_ack);
//I/O口
input clock; //系统时钟
input reset; //复位,高电平有效
input ps2_clk_in; //PS/2时钟线,输入口
input ps2_data_in; //PS/2数据线,输入口
output ps2_clk_out; //PS/2时钟线,输出口
output ps2_data_out; //PS/2数据线,输出口
output ps2_clk_dir; //PS/2时钟方向控制,高电平为输出,低电平为输入
output ps2_data_dir; //PS/2数据方向控制,高电平为输出,低电平为输入
output rx_extended; //扩展码标志
output rx_released; //断码标志
output rx_shift_key_on; //shift键状态标志
output [7:0] rx_scan_code; //扫描码输出
output [7:0] rx_ascii; //ASCII码输出
output rx_data_ready; //收到新数据置位,读取后清零
input rx_read; //读数据指示,有新数据且rx_read=1时,清rx_data_ready标志
input [7:0] tx_data; //要发送给PS/2键盘的数据或指令
input tx_write; //发送指令请求,高电平有效
output tx_write_ack;//写指令状态,写指令前为1,写过程为0,写完为1.结束写请求后为0
output tx_error_no_keyboard_ack;//PS/2键盘应答出错指示.
//I/O寄存器
reg rx_extended_r;
reg rx_released_r;
reg [7:0] rx_scan_code_r;
reg [7:0] rx_ascii_r;
reg rx_data_ready_r;
reg tx_error_no_keyboard_ack_r;
reg ps2_clk_out_r;
reg ps2_data_out_r;
reg ps2_clk_dir_r;
reg ps2_data_dir_r;
//内部寄存器
reg [`TOTAL_BITS-1:0] q; //移位寄存器,用于接收或发送数据
reg [3:0] m1_state; //状态机M1
reg [3:0] m1_next_state;
reg m2_state; //状态机M2
reg m2_next_state;
reg [3:0] bit_count; //移位计数器
reg enable_timer_400usec; //400uS计数器使能控制
reg enable_timer_5usec; //5uS计数器使能控制
reg [TIMER_400USEC_BITS-1:0] timer_400usec_count; //400uS计数器
reg [TIMER_5USEC_BITS-1:0] timer_5usec_count; //5uS计数器
reg [6:0] ascii; //ASCII码
reg left_shift_key; //左SHIFT键标志
reg right_shift_key; //左SHIFT键标志
reg hold_extended; //保持原先的值,复位或有新的值输出(rx_output_event=1)清零
reg hold_released; //保持原先的值,复位或有新的值输出(rx_output_event=1)清零
reg ps2_clk_in_r; //同步PS/2时钟信号
reg ps2_data_in_r; //同步PS/2数据信号
//内部信号
wire timer_400usec_done; //400uS计数器溢出标志
wire timer_5usec_done; //5uS计数器溢出标志
wire extended; //扩展码标志
wire released; //断码标志
wire rx_output_event; //收到键盘发送过来的一帧数据置位,且不是0xE0和0xF0
wire rx_output_strobe; //收到键盘发送过来的一帧数据置位,且不是0xE0、0xF0和SHIFT键值
wire tx_parity_bit; //奇偶验证位
wire rx_shifting_done; //接收一帧数据完毕置位
wire tx_shifting_done; //发送一帧数据完毕置位
wire [8:0] shift_key_plus_code; //包含shift键状态的扫描码
//计时器参数
//以下参数值按48MHz时钟计算
parameter TIMER_400USEC_VALUE = 19200; //400us延时,系统时钟的计数值
parameter TIMER_400USEC_BITS = 15; //400us计时,计数器位宽
parameter TIMER_5USEC_VALUE = 240; //5us延时,系统时钟的计数值
parameter TIMER_5USEC_BITS = 8; //5us计时,计数器位宽
parameter TRAP_SHIFT_KEYS = 0; //Default: No shift key trap.
//状态机M1状态参数表
parameter m1_rx_clk_h = 4'd1,
m1_rx_clk_l = 4'd0,
m1_rx_falling_edge_marker = 4'd12,
m1_rx_rising_edge_marker = 4'd13,
m1_tx_force_clk_l = 4'd3,
m1_tx_first_wait_clk_h = 4'd10,
m1_tx_reset_timer = 4'd11,
m1_tx_wait_clk_h = 4'd2,
m1_tx_clk_h = 4'd4,
m1_tx_clk_l = 4'd5,
m1_tx_wait_keyboard_ack = 4'd6,
m1_tx_done_recovery = 4'd7,
m1_tx_error_no_keyboard_ack = 4'd8,
m1_tx_rising_edge_marker = 4'd9;
//状态机M2状态参数表
parameter m2_rx_data_ready = 1'd1,
m2_rx_data_ready_ack = 1'd0;
//******************************************************************************//
//******************************************************************************//
assign ps2_clk_out = ps2_clk_out_r; //PS2时钟线输出
assign ps2_data_out = ps2_data_out_r; //PS2数据线输出
assign ps2_clk_dir = ps2_clk_dir_r; //PS2时钟方向
assign ps2_data_dir = ps2_data_dir_r; //PS2数据方向
assign rx_extended = rx_extended_r;
assign rx_released = rx_released_r;
assign rx_scan_code = rx_scan_code_r;
assign rx_ascii = rx_ascii_r;
assign rx_data_ready = rx_data_ready_r;
assign tx_error_no_keyboard_ack = tx_error_no_keyboard_ack_r;
//同步输入时钟、数据信号
always @(posedge clock)
begin
ps2_clk_in_r <= ps2_clk_in;
ps2_data_in_r <= ps2_data_in;
end
//**************************************************************************************
//状态机M1
always @(posedge clock)
begin
if (reset)
m1_state <= m1_rx_clk_h;
else
m1_state <= m1_next_state;
end
//M1状态转换逻辑
always @(m1_state
or q
or tx_shifting_done
or tx_write
or ps2_clk_in_r
or ps2_data_in_r
or timer_400usec_done
or timer_5usec_done
)
begin
ps2_clk_out_r <= 1'b1;
ps2_data_out_r <= 1'b1;
ps2_clk_dir_r <= 1'b0;
ps2_data_dir_r <= 1'b0;
tx_error_no_keyboard_ack_r <= 1'b0;
enable_timer_400usec <= 1'b0;
enable_timer_5usec <= 1'b0;
case (m1_state)
//以下四个状态为从PS/2键盘读数据到主机的状态
//-----------------------------------------------
m1_rx_clk_h : //读状态时钟高电平
begin
enable_timer_400usec <= 1'b1;
if (tx_write)
m1_next_state <= m1_tx_reset_timer;
else if (~ps2_clk_in_r)
m1_next_state <= m1_rx_falling_edge_marker;
else
m1_next_state <= m1_rx_clk_h;
end
m1_rx_falling_edge_marker : //读状态时钟下降沿标志
begin
m1_next_state <= m1_rx_clk_l;
end
m1_rx_clk_l : //读状态时钟低电平
begin
enable_timer_400usec <= 1'b1;
if (tx_write)
m1_next_state <= m1_tx_reset_timer;
else if (ps2_clk_in_r)
m1_next_state <= m1_rx_rising_edge_marker;
else
m1_next_state <= m1_rx_clk_l;
end
m1_rx_rising_edge_marker : //读状态时钟上升沿标志
begin
m1_next_state <= m1_rx_clk_h;
end
//------------------------------------------------
//以下为发送指令到PS/2键盘的转换状态
//------------------------------------------------
m1_tx_reset_timer: //写指令开始状态
begin
m1_next_state <= m1_tx_force_clk_l;
end
m1_tx_force_clk_l : //抑制通讯,拉低时钟线(100uS以上)准备发送指令给PS/2键盘
begin
enable_timer_400usec <= 1'b1;
ps2_clk_dir_r <= 1'b1; //置PS2时钟线为输出
ps2_clk_out_r <= 1'b0; //强制拉低ps2_clk时钟线
if (timer_400usec_done)
m1_next_state <= m1_tx_first_wait_clk_h;
else
m1_next_state <= m1_tx_force_clk_l;
end
m1_tx_first_wait_clk_h : //等待PS/2键盘把时钟线拉低
begin
enable_timer_5usec <= 1'b1;
ps2_data_dir_r <= 1'b1; //置PS2数据线为输出
ps2_data_out_r <= 1'b0; // 发送起始位
if (~ps2_clk_in_r && timer_5usec_done)
m1_next_state <= m1_tx_clk_l;
else
m1_next_state <= m1_tx_first_wait_clk_h;
end
m1_tx_clk_l :
begin
ps2_data_dir_r <= 1'b1; //置PS2数据线为输出
ps2_data_out_r <= q[0];
if (ps2_clk_in_r)
m1_next_state <= m1_tx_wait_clk_h;
else
m1_next_state <= m1_tx_clk_l;
end
m1_tx_wait_clk_h :
begin
enable_timer_5usec <= 1'b1;
ps2_data_dir_r <= 1'b1; //置PS2数据线为输出
ps2_data_out_r <= q[0];
if (ps2_clk_in_r && timer_5usec_done)
m1_next_state <= m1_tx_rising_edge_marker;
else
m1_next_state <= m1_tx_wait_clk_h;
end
m1_tx_rising_edge_marker :
begin
ps2_data_dir_r <= 1'b1; //置PS2数据线为输出
ps2_data_out_r <= q[0];
m1_next_state <= m1_tx_clk_h;
end
m1_tx_clk_h :
begin
ps2_data_dir_r <= 1'b1; //置PS2数据线为输出
ps2_data_out_r <= q[0];
if (tx_shifting_done)
m1_next_state <= m1_tx_wait_keyboard_ack;
else if (~ps2_clk_in_r)
m1_next_state <= m1_tx_clk_l;
else
m1_next_state <= m1_tx_clk_h;
end
m1_tx_wait_keyboard_ack :
begin
if (~ps2_clk_in_r && ps2_data_in_r)
m1_next_state <= m1_tx_error_no_keyboard_ack; //应答出错
else if (~ps2_clk_in_r && ~ps2_data_in_r)
m1_next_state <= m1_tx_done_recovery;
else
m1_next_state <= m1_tx_wait_keyboard_ack;
end
m1_tx_done_recovery :
begin
if (ps2_clk_in_r && ps2_data_in_r)
m1_next_state <= m1_rx_clk_h;
else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -