?? 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 + -