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串口接收模块设计》)
效果展示:
器件连接如下图: 依次输入A——Z 26个字母,5a是输入最后一个字母Z时的数码管输出 ,查表可知 Z的16进制ascii码为5AH.
依次输入26个字母,调试工具窗口输出
注: 这里采用ps_state
ps2模块接收数据有效信号作为发送使能信号,因此数码管显示和调试工具显示基本是同步的
本设计结合前边做的几次工程,包括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 endmoduleps2键盘驱动代码:
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模块接收数据有效信号作为发送使能信号,因此数码管显示和调试工具显示基本是同步的
相关文章推荐
- verilog语言RS232串口接收模块设计——串口调试工具发送数据在数码管显示
- verilog语言的ps2键盘驱动设计
- QT串口发送十六进制数据接收串口数据并十六进制显示
- 串口发送32bit数据Verilog
- FPGA设计中RS232串口的Verilog实现(TX控制器)
- FPGA设计中RS232串口的Verilog实现(RX控制器)
- 下位机单片机c语言发送数据到串口,上位机pc机java语言获取端口数据
- 串口发送模块——1字节数据发送
- 基于rs232串口通讯的Verilog设计
- 2 C#串口或TCP远程采集数据 chart图表使用示例 保存数据到access数据库和每日.txt文件并实时显示各参数曲线
- Django向Highcharts图表发送数据并用模板语言实现动态显示
- 8通道同步并行数据采集PCI模块的设计
- 【ERP系统设计】【数据模块】2 安装Eclipse多国语言包
- 基于rs232串口通讯的Verilog设计
- C#串口采集短信GSM chart图表使用示例 保存数据到access数据库和每日.txt文件并实时显示各参数曲线
- Java SWT 设计RS232/RS485串口接受的界面,用线程读取和发送
- 串口发送数据到1602显示
- C#源码 任意语言任意大小任意字体任意排列 字符汉字取模及显示 可以自由编辑点阵 通过串口发送 pc端和手机端
- 纠错:基于FPGA串口发送彩色图片数据至VGA显示
- 基于java数据采集串口通讯的设计和实现