您的位置:首页 > 其它

verilog语言RS232串口发送模块设计——采集ps2键盘数据在串口调试工具显示

2014-03-04 18:56 567 查看
关于rs232串口的协议和接口已经在《verilog语言RS232串口接收模块设计》这篇博客中有讲述:http://blog.csdn.net/baijingdong/article/details/20460019,

本设计结合前边做的几次工程,包括ps2键盘驱动,数码管输出等完成该设计。 该模块功能可以完成对ps2键盘的数据采集,采集数据在xilinx EXCD-1开发板的

数码管输出(输出为按键值对应的16进制ASCII码值),并将结果通过串口输出到pc机,在串口调试工具窗口显示。

该模块同样包括多个部分:

波特率产生部分:这一部分主要是收到串口传输有效信号rs_ena后,每间隔一定时间(波特率时间间隔)就产生一个时钟周期的(系统时钟)高电平rs_clk,
使得高电平时刻落在可采用串口数据的中间时刻点,以供接收时提供采样时刻。该部分与前面提到的博客中的模块完全一致。
数据发送部分:这部分要有一个数据发送开始信号,可以是按键,电路里有提取下降沿的电路,在下降沿时产生一个高电平,该高电平用于产生发送中断,发送计数器开始计数。外部波特率发生器开始产生数据发送时钟。在发送中断有效时每来一个数据发送时钟发送一次数据位,第一位发‘0’,随后发送有效数据,低位在前高位在后,随后发送校验码(可选),最后发送高电平,发送计数器计满后清零,发送中断结束,波特率发生器停止工作。
键盘驱动部分:该部分在我前面的博文《verilog语言的ps2键盘驱动设计》中有介绍:/article/9819912.html
波特率产生部分代码:(同博文《verilog语言RS232串口接收模块设计》)
module rs_clk_gen(
clk,rst,rs_clk,rs_ena
);
input clk;//系统时钟
input rst;//复位信号
input rs_ena; //串口通信允许信号
output rs_clk; //输出允许的波特率信号时钟

parameter N1=5207;//5208,9600bps
parameter N2=2603;//2604,

reg rs_clk='b0;
reg [13:0] count='d0;//计数器

always @(posedge clk or negedge rst)
begin
if(!rst)
begin
count<='d0;  //复位信号到来时,count计数器清零
end
else
if(count==N1 || !rs_ena) count<='d0;//当count计满或者无串口通信使能时count都不计数
else count<=count+'b1;//当且仅当count不为0,通信使能时count计数。
end
always @(posedge clk or negedge rst)

begin
if (!rst)
rs_clk<='b0;
else
if(count==N2&&rs_ena)//当且仅当count计数到一半且通信使能时允许时钟翻转
rs_clk<='b1;			//
else
rs_clk<='b0;//使得rs_clk是一个小波峰的时钟信号,在有效信号的数据位为高电平。

end
endmodule
数据发送部分代码:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: bupt abs_lab
// Engineer: mr. bai
//
// Create Date:    10:11:38 03/04/2014
// Design Name:
// Module Name:    rs_send
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module rs_send(
tx_start,//发送开始信号,如按键信号,产生下降沿
data_byte_in,//待发送数据
rs_clk,//发送数据时钟
rs_ena,//发送波特率使能
tx_data_out,//串行数据输出
clk,
rst
);
input clk; //输入时钟
input rst;//复位信号
input tx_start;//允许发送信号,是一个下降沿,如按键,后面需要提取该信号下降沿;
input [7:0]data_byte_in;//待发送的一个字节信号;
input rs_clk;//数据发送时钟信号,为9600的波特率信号,其为高电平时开始发送数据;
output reg rs_ena;//波特率发生器的启动信号
output reg tx_data_out='b1;//发送出的数据;
reg[3:0] count3=4'd0;//数据发送计数器
reg[7:0] tx_data;
//共有七组信号;
//==============================================
//提取tx_start信号的下降沿,
//============================================
reg tx_start0,tx_start1,tx_start2;

wire tx_start_ena= ~tx_start1 & tx_start2;//允许发送开始信号;

always @(posedge clk or negedge rst)
begin
if(!rst)
begin
tx_start0<='b1;
tx_start1<='b1;
tx_start2<='b1;
end
else
begin
tx_start0<=tx_start;
tx_start1<=tx_start0;
tx_start2<=tx_start1;
end
end
//============下降沿提取结束===============================

reg tx_int; //发送允许后,置高
always @(posedge clk or negedge rst)
begin
if(!rst)
begin
tx_int<='b0;
rs_ena<='b0;
end
else
begin
if(tx_start_ena)
begin
tx_data<=data_byte_in;//检测到发送信号时将输入信号锁存,启动发送中断,波特率产生模块使能
tx_int<='b1;
rs_ena<='b1;
end
else
if(count3>=4'd11)//发送计数器计满,停止发送中断,波特率模块停止工作
begin
tx_int<='b0;
rs_ena<='b0;
// count3<=4'd0;
end
end
end
always @(posedge clk or negedge rst)
begin
if(!rst)
tx_data_out<='b1; //串口在无有效数据输出时,一直为高电平
else
if(tx_int)
begin
if(rs_clk)//发送中断且发送时钟有效时,开始发送数据
begin
case (count3)
4'd1:    tx_data_out<='b0;
4'd2:	 tx_data_out<=data_byte_in[0];
4'd3:	 tx_data_out<=data_byte_in[1];
4'd4:	 tx_data_out<=data_byte_in[2];//先发送一个低电平,然后发送低位,后发生高位数据
4'd5:	 tx_data_out<=data_byte_in[3];

4'd6:	 tx_data_out<=data_byte_in[4];
4'd7:	 tx_data_out<=data_byte_in[5];
4'd8:	 tx_data_out<=data_byte_in[6];
4'd9:	 tx_data_out<=data_byte_in[7];
4'd10:  tx_data_out<=1'b1;//这里没有加奇数偶校验位;
default:tx_data_out<=1'b1;//无有效数据输出时,一直为高电平
endcase
end
// else if(count3>='d11) count3<='d0;
end
end
always @(posedge clk or negedge rst)
begin
if(!rst)
count3<='d0;
else if(!tx_int ||count3=='d11)//在非发送中断和发送完成时,发送计数器都应该清零
count3<='d0;
else if(rs_clk) 	count3<=count3+'b1;//发送波特率时钟有效时,每发送一个数据,计数器加1;
end
endmodule
ps2键盘驱动代码:
module ps2_driever(
clk,rst_n,ps2k_clk,ps2k_data,sm_bit,sm_seg,ps2_state,ps2_byte);

input clk;		//50M时钟信号
input rst_n;	//复位信号
input ps2k_clk;	//PS2接口时钟信号
input ps2k_data;		//PS2接口数据信号
//wire [7:0] ps2_byte;	// 1byte键值,只做简单的按键扫描
output ps2_state;		//键盘当前状态,ps2_state=1表示有键被按下
output reg [1:0] sm_bit='b01;
output reg [7:0]sm_seg;
output [7:0]ps2_byte;
//------------------------------------------
reg ps2k_clk_r0,ps2k_clk_r1,ps2k_clk_r2;	//ps2k_clk状态寄存器

//wire pos_ps2k_clk; 	// ps2k_clk上升沿标志位
wire neg_ps2k_clk;	// ps2k_clk下降沿标志位
//设备发送向主机的数据在下降沿有效,首先检测PS2k_clk的下降沿
//利用上面逻辑赋值语句可以提取得下降沿,neg_ps2k_clk为高电平时表示数据可以被采集
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
ps2k_clk_r0 <= 1'b0;
ps2k_clk_r1 <= 1'b0;
ps2k_clk_r2 <= 1'b0;
end
else begin								//锁存状态,进行滤波
ps2k_clk_r0 <= ps2k_clk;
ps2k_clk_r1 <= ps2k_clk_r0;
ps2k_clk_r2 <= ps2k_clk_r1;
end
end

assign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2;	//下降沿

//-----------------数据采集-------------------------
/*

帧结构:设备发往主机数据帧为11比特,(主机发送数据包为12bit)
1bit start bit ,This is always 0,
8bit data bits,
1 parity bit,(odd parity)校验位,奇校验,
data bits 为偶数个1时该位为1,
data bits 为奇数个1时该位为0.
1bit stop bit ,this is always 1.
num 范围为 'h00,'h0A;
*/
reg[7:0] ps2_byte_r;		//PC接收来自PS2的一个字节数据存储器
reg[7:0] temp_data;			//当前接收数据寄存器
reg[3:0] num;				//计数寄存器

always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
num <= 4'd0;
temp_data <= 8'd0;
end
else if(neg_ps2k_clk) begin	//检测到ps2k_clk的下降沿
case (num)
/*
帧结构中数据位为一个字节,且低位在前,高位在后,
这里要定义一个buf,size is one Byte.

*/
4'd0:	num <= num+1'b1;
4'd1:	begin
num <= num+1'b1;
temp_data[0] <= ps2k_data;	//bit0
end
4'd2:	begin
num <= num+1'b1;
temp_data[1] <= ps2k_data;	//bit1
end
4'd3:	begin
num <= num+1'b1;
temp_data[2] <= ps2k_data;	//bit2
end
4'd4:	begin
num <= num+1'b1;
temp_data[3] <= ps2k_data;	//bit3
end
4'd5:	begin
num <= num+1'b1;
temp_data[4] <= ps2k_data;	//bit4
end
4'd6:	begin
num <= num+1'b1;
temp_data[5] <= ps2k_data;	//bit5
end
4'd7:	begin
num <= num+1'b1;
temp_data[6] <= ps2k_data;	//bit6
end
4'd8:	begin
num <= num+1'b1;
temp_data[7] <= ps2k_data;	//bit7
end
4'd9:	begin
num <= num+1'b1;	//奇偶校验位,不做处理
end
4'd10: begin
num <= 4'd0;	// num清零
end
default: ;
endcase
end
end

reg key_f0;		//松键标志位,置1表示接收到数据8'hf0,再接收到下一个数据后清零
reg ps2_state_r;	//键盘当前状态,ps2_state_r=1表示有键被按下
//+++++++++++++++数据处理开始++++++++++++++++=============
always @ (posedge clk or negedge rst_n) begin	//接收数据的相应处理,这里只对1byte的键值进行处理
if(!rst_n) begin
key_f0 <= 1'b0;
ps2_state_r <= 1'b0;
end
else if(num==4'd10) ///一帧数据是否采集完。
begin	//刚传送完一个字节数据
if(temp_data == 8'hf0) key_f0 <= 1'b1;//判断该接收数据是否为断码
else
begin
//========================理解困难==================================
if(!key_f0)
begin	//说明有键按下
ps2_state_r <= 1'b1;
ps2_byte_r <= temp_data;	//锁存当前键值
end
else
begin
ps2_state_r <= 1'b0;
key_f0 <= 1'b0;
end
//=====================================================
end
end
end
/*+++++++++++++等效写法+++++++++++++++++++++++++++++
reg key_released;//收到码段后是否松开
reg [7:0] ps2_byte;

always @(posedge clk or negedge rst)
begin
if(!rst)
key_released<='b0;
else if(cnt=='h0A)//一帧数据是否采集完。
begin
if(ps2_byte_buf==8'hF0)//数据为段码f0
key_released<='b1;//松开标志位置位
else
key_released<='b0;
end
end

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

*/

reg[7:0] ps2_asci;	//接收数据的相应ASCII码

always @ (ps2_byte_r) begin
case (ps2_byte_r)		//键值转换为ASCII码,这里做的比较简单,只处理字母
8'h15: ps2_asci <= 8'h51;	//Q
8'h1d: ps2_asci <= 8'h57;	//W
8'h24: ps2_asci <= 8'h45;	//E
8'h2d: ps2_asci <= 8'h52;	//R
8'h2c: ps2_asci <= 8'h54;	//T
8'h35: ps2_asci <= 8'h59;	//Y
8'h3c: ps2_asci <= 8'h55;	//U
8'h43: ps2_asci <= 8'h49;	//I
8'h44: ps2_asci <= 8'h4f;	//O
8'h4d: ps2_asci <= 8'h50;	//P
8'h1c: ps2_asci <= 8'h41;	//A
8'h1b: ps2_asci <= 8'h53;	//S
8'h23: ps2_asci <= 8'h44;	//D
8'h2b: ps2_asci <= 8'h46;	//F
8'h34: ps2_asci <= 8'h47;	//G
8'h33: ps2_asci <= 8'h48;	//H
8'h3b: ps2_asci <= 8'h4a;	//J
8'h42: ps2_asci <= 8'h4b;	//K
8'h4b: ps2_asci <= 8'h4c;	//L
8'h1a: ps2_asci <= 8'h5a;	//Z
8'h22: ps2_asci <= 8'h58;	//X
8'h21: ps2_asci <= 8'h43;	//C
8'h2a: ps2_asci <= 8'h56;	//V
8'h32: ps2_asci <= 8'h42;	//B
8'h31: ps2_asci <= 8'h4e;	//N
8'h3a: ps2_asci <= 8'h4d;	//M
default: ;
endcase
end

assign ps2_byte = ps2_asci;
assign ps2_state = ps2_state_r;
//==================keyboard driver part over======================

//=======================1KHz div====display part start===================
parameter N2=50000;
reg clk3=1'b0;
reg [16:0]count3=17'd0;
//assign clk_out=clk3;

always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
count3<=17'd0;
clk3<=1'b0;
end
else
if(count3<N2-1)
begin
count3<=count3+1'b1;
if(count3<(N2/2-1))
clk3<=1'b0;
else
clk3<=1'b1;
end
else
begin
count3<=17'd0;
clk3<=1'b0;
end
end
//==================state select================
reg[3:0] Num;
always @(posedge clk3)
begin
case (sm_bit)
'b01:  begin
Num<=ps2_byte[7:4];
sm_bit<='b10;
end
'b10:  begin
Num<=ps2_byte[3:0];
sm_bit<='b01;
end
default:
Num<='b0;
endcase
/*if(sm_bit=='b01)
begin
Num<=ps2_byte[3:0];
sm_bit<='b10;
end
else if(sm_bit=='b10)
begin
Num<=ps2_byte[7:4];
sm_bit<='b01;
end
*/
end
//=========================================================
always @ (Num)//
begin
case (Num)
4'h0 : sm_seg = 8'h3f;   // "0"
4'h1 : sm_seg = 8'h06;   // "1"
4'h2 : sm_seg = 8'h5b;   // "2"
4'h3 : sm_seg = 8'h4f;   // "3"
4'h4 : sm_seg = 8'h66;   // "4"
4'h5 : sm_seg = 8'h6d;   // "5"//共阴极数码管表
4'h6 : sm_seg = 8'h7d;   // "6"
4'h7 : sm_seg = 8'h07;   // "7"
4'h8 : sm_seg = 8'h7f;   // "8"
4'h9 : sm_seg = 8'h6f;   // "9"
4'ha : sm_seg = 8'h77;   // "a"
4'hb : sm_seg = 8'h7c;   // "b"
4'hc : sm_seg = 8'h39;   // "c"
4'hd : sm_seg = 8'h5e;   // "d"
4'he : sm_seg = 8'h79;   // "e"
4'hf : sm_seg = 8'h71;   // "f"
endcase
end

//==============================================
endmodule
顶层模块代码:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date:    10:05:53 03/04/2014
// Design Name:
// Module Name:    rs232_send_top
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module rs232_send_top(
// tx_start,
//data_byte_in,
tx_data_out,
clk,
rst,
//============
ps2k_clk,
ps2k_data,
sm_bit,
sm_seg,
//	ps2_state,

//================
);
input clk;
input rst;
//input [7:0]data_byte_in;
wire tx_start;
output tx_data_out;
//调用模块管脚================
input ps2k_clk;
input ps2k_data;
output [1:0]sm_bit;
output [7:0]sm_seg;
wire ps2_state;
//=====================
wire [7:0]ps2_byte;
//====================
rs_clk_gen M1(
.clk(clk),//
.rst(rst),//
.rs_clk(rs_clk),////
.rs_ena(rs_ena)////
);

rs_send M2(
.tx_start(~ps2_state),  //这里采用ps_state ps2模块接收数据有效信号作为发送使能信号
.data_byte_in(ps2_byte),//将ps2接收的1byte信号作为输入
.rs_clk(rs_clk),////
.rs_ena(rs_ena),////
.tx_data_out(tx_data_out),
.clk(clk),//
.rst(rst)//
);
ps2_driever M3(
.clk(clk),
.rst_n(rst),
.ps2k_clk(ps2k_clk),
.ps2k_data(ps2k_data),
.sm_bit(sm_bit),
.sm_seg(sm_seg),
.ps2_state(ps2_state),
.ps2_byte(ps2_byte));

endmodule


效果展示:
器件连接如下图: 依次输入A——Z 26个字母,5a是输入最后一个字母Z时的数码管输出 ,查表可知 Z的16进制ascii码为5AH.



依次输入26个字母,调试工具窗口输出



注: 这里采用ps_state
ps2模块接收数据有效信号作为发送使能信号,因此数码管显示和调试工具显示基本是同步的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: