📄 ps2.v
字号:
//PS/2接口与8位并口通信模块 YTL-2009-01-18
//此模块用于PS/2接口与单片机相互通信.
//用于EPM7128SLC84-10芯片实验已通过
module PS2 (sys_clk, ps_clk, ps_data, nCS, nOE, nWE, DataReady, SendFin, data, nReset, stat, out);
parameter ps_read_later = 4'd7; //读取延时,单位1uS; (默认值:4'd8)
parameter ps_write_later = 4'd5; //发送延时,单位1uS (默认值:4'd5)
parameter ps_comm_overtime = 5'd15; //PS2接口接收超时检测延时,单位100uS (默认值:5'd15)
parameter ps_begin_time = 8'd120; //PS2接口发送起始位延时量,单位1uS (默认值:8'd150)
parameter ps_send_overtime = 5'd15; //PS2接口发送超时检测延时,单位100uS (默认值:5'd20)
parameter Prescaler = 6'd50; //对于系统时钟分频值, 分频周期max=63*2=126uS/pulse (默认值:6'd50)
input sys_clk; //系统时钟 1Mhz
input nCS, nOE, nWE, nReset; //芯片选择|读取数据|发送数据|芯片复位
input stat; //0=读取PS/2最后接收到的数据,1=读取内部状态寄存器选择
inout ps_clk, ps_data; //PS/2时钟|PS/2数据
inout [7:0] data; //8位数据双向总线
output DataReady; //PS2数据有效中断(高电平有效)
output SendFin; //PS2数据发送完成(高电平有效)
output [7:0] out; //用于直接显示内部状态寄存器(status)的内容,可以不用.
/*内部状态寄存器各位含义(只读)
b0-外部无应答错误
b1-发送数据超时错误
b2-读取数据超时
b3-奇偶检验错误
b4-输入缓存溢出
b5-允许b1位清除,用于外部在读取状态之前保留此信息
b6-允许b2位清除,用于外部在读取状态之前保留此信息*/
tri1 ps_clk, ps_data;
reg [6:0] status; //内部状态寄存器
reg DataReady; //已收到一字节的PS/2数据(高电平有效)
reg main_clk; //100分频后的脉冲
//声明外部发送PS2数据模块变量
reg write_ps_enable; //开始发送PS/2数据
reg SendFin; //PS数据发送完成中断输出(高电平有效)
//声明系统时钟计数模块变量
reg send_overtime; //发送数据超时脉冲信号
reg ps_overtime; //读取数据超时脉冲信号
//声明读取PS数据模块变量
reg in_rdy; //PS数据接收完成
reg read_ps_enable; //表明正在读取PS数据中
reg [7:0] in_buff; //输入缓存
//声明发送PS数据模块变量
reg [7:0] out_buff; //输出缓存
reg ps_data_out; //PS2_data串行输出
reg ps_clk_out; //PS2_clk时钟输出
reg send_fin_pulse; //通知"外部发送PS2数据进程"已完成发送脉冲信号
wire clr_WE; //复位发送
//读写PS/2时钟生成模块
reg R_clk; //PS/2读时钟延时信号(上升沿有效)
reg W_clk; //PS/2时钟延时信号(上升沿有效)
reg clr_read_pulse; //读取模块复位
reg clr_write_pulse; //发送模块复位
wire clr_RW; //复位读写PS/2时钟生成模块
//PS/2接口滤波变量
reg ps_clk_filter; //滤波后的PS/2-clk
reg ps_data_filter; //滤波后的PS/2-data
initial //上电初始化内部寄存器
begin
in_buff <= 0;
in_rdy <= 0;
out_buff <= 0;
DataReady <= 0;
main_clk = 0;
write_ps_enable <= 0;
read_ps_enable <= 0;
ps_data_out <= 1'bz;
ps_clk_out <= 1'bz;
ps_overtime <= 0;
status <= 5'b0;
send_fin_pulse <= 0;
send_overtime <= 0;
SendFin <= 1'b1;
R_clk <= 0;
W_clk <= 0;
clr_read_pulse <= 0;
clr_write_pulse <= 0;
ps_clk_filter <= 0;
ps_data_filter <= 0;
end
assign data = (!nOE && !nCS)? ((stat)? status : in_buff) : 8'bz; //三态8位数据总线,外部读取PS2数据
assign ps_data = (write_ps_enable)? ps_data_out : 1'bz; //发送PS2数据
assign ps_clk = (write_ps_enable)? ps_clk_out : 1'bz; //发送clk数据
assign clr_RW = ps_overtime | send_overtime | !nReset; //读写脉冲生成器复位
assign clr_WE = send_fin_pulse | send_overtime | clr_write_pulse; //ps_write_clk_count状态复位
assign out = status;
//对于1MHz(1uS)50分频,生成1个100uS/脉冲周期
always @(posedge sys_clk)
begin : Prescaler_clk
reg [5:0] pc;
pc = pc + 6'b1;
if (pc>=Prescaler) begin
main_clk <= ~main_clk;
pc <= 0;
end
end
//读取PS2数据时清除中断标志
always @(posedge in_rdy or negedge nOE or negedge nReset)
begin
if (!nOE || !nReset) begin
if (!nCS && !stat) DataReady <= 0;
end
else DataReady <= in_rdy;
end
//外部发送PS2数据
always @(negedge nWE or posedge clr_WE)
begin
if (clr_WE) begin
write_ps_enable <= 0;
SendFin <= 1'b1;
end
else begin
if (!nCS && !write_ps_enable) begin
SendFin <= 0;
out_buff <= data;
write_ps_enable <= 1'b1;
end
end
end
//系统时钟计数
always @(posedge main_clk or negedge nOE or negedge nReset)
begin : count_overtime
reg [4:0] sys_clk_count;
reg befo; //读写转换间清零计数器
//reg clr1, clr2;
if (!nReset) begin
status[1] <= 0;
status[2] <= 0;
status[5] <= 1;
status[6] <= 1;
sys_clk_count <= 0;
befo <= 0;
end
else if (!nOE) begin
if (!nCS && stat) begin
status[5] <= 1;
status[6] <= 1;
end
end
else begin
ps_overtime <= 0;
send_overtime <= 0;
if ((read_ps_enable) || write_ps_enable) begin //读计时||写计时
sys_clk_count = sys_clk_count + 5'd1;
//读取数据超时计时
if (read_ps_enable) begin
if (!befo) begin
if (sys_clk_count>=ps_comm_overtime) begin
ps_overtime <= 1;
status[2] <= 1'b1;
sys_clk_count <= 5'd0;
end
else begin
if (status[6]) begin
status[2] <= 0;
status[6] <= 0;
end
end
end
else begin
sys_clk_count <= 0;
befo <= 0;
end
end
//发送数据超时计时
else if (write_ps_enable) begin
if (befo) begin
if (sys_clk_count>=ps_send_overtime) begin
send_overtime <= 1; //发送超时错误
status[1] <= 1;
sys_clk_count <= 5'd0;
end begin
if (status[5]) begin
status[1] <= 0;
status[5] <= 0;
end
end
end
else begin
sys_clk_count <= 0;
befo <= 1;
end
end
end
else begin
sys_clk_count <= 0;
befo <= 0;
end
end
end
//读取PS_DATA时序进程
always @(posedge R_clk or posedge clr_read_pulse)
begin: CommPs2Data
reg [3:0] ps_read_clk_count; //PS2读取时钟计数器(状态时序)
reg [7:0] in_temp; //输入移位寄存器缓存
if (clr_read_pulse) begin
ps_read_clk_count <= 0;
read_ps_enable <= 0;
end
else begin
ps_read_clk_count = ps_read_clk_count + 4'd1;
in_rdy <= 0;
case (ps_read_clk_count)
4'd1: //读取起始位
if (ps_data_filter) //等待起始位
ps_read_clk_count <= 4'd0;
else
read_ps_enable <= 1; //通知外部已开始读取数据
4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9:
begin //读取数据位8bit
in_temp[6:0] <= in_temp[7:1];
in_temp[7] <= ps_data_filter;
end
4'd10:
begin //读取奇偶校验
if (ps_data_filter != Parity(in_temp)) status[3] <= 1;
else status[3] <= 0;
end
4'd11:
begin //验证停止位和奇偶校验
ps_read_clk_count <= 0;
read_ps_enable <= 0;
if (ps_data_filter && !status[3]) begin
if (!DataReady) begin
in_rdy <= 1'b1;
status[4] <= 0;
in_buff <= in_temp;
end
else status[4] <= 1;
end
end
default: begin
ps_read_clk_count = 4'd0;
read_ps_enable <= 0;
end
endcase
end
end
//发送PS2接口时序进程
always @(negedge W_clk or posedge clr_write_pulse)
begin: SendPs2Data
reg [3:0] ps_write_clk_count; //PS2发送时钟计数器(状态时序)
if (clr_write_pulse) begin
ps_write_clk_count <= 0;
send_fin_pulse <= 0;
end
else begin
send_fin_pulse <= 0;
ps_write_clk_count = ps_write_clk_count + 4'b1;
case (ps_write_clk_count)
4'd1: //抑制PS设备通信
begin
ps_data_out <= 1'bz;
ps_clk_out <= 0;
end
4'd2: //请求发送PS/2数据
begin
ps_data_out <= 0;
ps_clk_out <= 1'bz;
end
4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9, 4'd10: //发送PS2数据位
ps_data_out <= (out_buff[ps_write_clk_count-4'd3])? 1'bz : 1'b0;
4'd11: //发送PS2奇偶校验
ps_data_out <= (Parity(out_buff))? 1'bz : 1'b0;
4'd12: //发送停止位
ps_data_out <= 1'bz;
4'd13: //接收应答位
begin
status[0] <= ps_data_filter;
send_fin_pulse <= 1'b1;
ps_write_clk_count <= 0;
end
default: begin
ps_write_clk_count <= 0;
end
endcase
end
end
//PS/2接口滤波
always @(negedge sys_clk or negedge nReset)
begin: Filter
reg [1:0] psclk_s; //采样计数器
reg [1:0] psdat_s; //采样计数器
//reg psclk; //用于检测PSCLK的跳变
if (!nReset) begin
psclk_s <= 0;
psdat_s <= 0;
ps_clk_filter <= 0;
ps_data_filter <= 0;
end
else begin
//ps/2时钟线滤波
if (ps_clk_filter != ps_clk) begin //检测PS2时钟跳变
if (psclk_s == 2'd3) begin //3次采样ps_clk
ps_clk_filter <= ps_clk;
psclk_s <= 0;
end
else begin
psclk_s = psclk_s + 2'b1;
end
end
else
psclk_s <= 0;
//ps/2数据线滤波
if (ps_data_filter != ps_data) begin //检测PS2时钟跳变
if (psdat_s == 2'd3) begin //3次采样ps_data
ps_data_filter <= ps_data;
psdat_s <= 0;
end
else begin
psdat_s = psdat_s + 2'b1;
end
end
else
psdat_s <= 0;
end
end
//读写PS/2时钟生成模块
always @(negedge sys_clk or posedge clr_RW)
begin: RW_PSCLK
reg [7:0] begin_time; //读写PS/2延时
reg clk_pulse; //PS2时钟下降沿
reg clk_status; //用于检测ps_clk_filter时钟上升沿
reg pswen; //用于检测write_ps_enable的跳变
reg wen_pulse; //write_ps_enable时钟上升沿
if (clr_RW) begin
begin_time <= 0;
R_clk <= 0;
W_clk <= 0;
pswen <= 0;
clk_pulse <= 0;
wen_pulse <= 0;
clk_status <= 0;
if (send_overtime) clr_write_pulse <= 1'b1;
if (ps_overtime) clr_read_pulse <= 1'b1;
if (!nReset) begin
clr_write_pulse <= 1'b1;
clr_read_pulse <= 1'b1;
end
end
else begin
R_clk <= 0;
W_clk <= 0;
clr_read_pulse <= 0;
clr_write_pulse <= 0;
begin_time <= (clk_pulse || wen_pulse)? begin_time + 7'd1 : 7'd0;
//out <= begin_time;
if (clk_status != ps_clk_filter) begin //检测PS_CLK跳变
clk_status <= ps_clk_filter;
if (!ps_clk_filter) begin //检测PS_CLK下降沿
clk_pulse <= 1;
end
end
if (pswen != write_ps_enable) begin //检测write_ps_enable时钟跳变
pswen <= write_ps_enable;
begin_time <= 7'd0;
if (write_ps_enable) begin //检测write_ps_enable时钟上升沿
wen_pulse <= 1; //第0位起始位发送数据的脉冲
W_clk <= 1;
clr_read_pulse <= 1;
end
else
clr_write_pulse <= 1;
end
if (wen_pulse) begin
clk_pulse <= 0;
if (begin_time >= ps_begin_time) begin
wen_pulse <= 0;
W_clk <= 1;
begin_time <= 0;
end
end
else if (write_ps_enable) begin //写时钟成生
if (begin_time >= ps_write_later) begin
clk_pulse <= 0;
begin_time <= 0;
W_clk <= 1;
end
end
else begin //读时钟成生
if (begin_time >= ps_read_later) begin
clk_pulse <= 0;
begin_time <= 0;
R_clk <= 1;
end
end
end
end
//奇偶校验
function Parity;
input [7:0] Din;
integer i;
reg Par;
begin
Par = 0;
for (i=0; i<8; i=i+1)
if (Din[i]) Par = Par + 1'b1;
Parity = ~Par; //偶校验
end
endfunction
endmodule
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -