verilog语言的ps2键盘驱动设计

2023-05-16

PS/2接口是目前最常见的鼠标接口,最初是IBM公司的专利,俗称“小口”。这是一种鼠标和键盘的专用接口,是一种6针的圆型接口。本设计完成了ps2键盘驱动,并将键盘对应的16进制ascii码值在数码管输出。

一、ps2接口协议

协议参考文档链接:http://wenku.baidu.com/link?url=HBkLHCwo2zwdScmz2zPfig4T5eJZhcpj4qC65zTjQBE47GiubPOfg0QOKxflxTMs9F_vzyEtTNjX4fq1qsiMciBprAFxoeEkc3hpFaWnMZ3

主要内容:常见的PS/2端口有两类:一种5脚的DIN或6脚的mini-DIN,这两种连接器在电气特性上是十分类似的,两者只有一点不同那就是管脚的排列,具有6 脚mini-DIN 的键盘
通常被叫做PS/2 键盘,而那些有5 脚DIN 叫做 AT 设备。常见ps2键盘接口如下所示:

 由于电源和地都是主机接口提供,主要用于驱动设计的管脚只有两个,data和clock,该clock时钟是键盘产生的,最大的时钟频率是33kHz 而且大多数设备工作在10 20kHz。

PS/2 鼠标和键盘履行一种双向同步串行协议, 换句话说每次数据线上发送一位数据并且每在时钟线上发一个脉冲就被读入,键盘/ 鼠标可以发送数据到主机,而主机也可以发送数据到设备

 从键盘/ 鼠标发送到主机的数据在时钟信号的下降沿, 当时钟从高变到低的时候 被读取 。从主机发送到键盘/鼠标的数据在上升沿,当时钟从低变到高的时候被读取。

 不管通讯的方向怎样键盘/鼠标总是产生时钟信号

该同步串行协议的帧结构如下:(从键盘到主机的帧长度为11bit)

注意校验位是对数据位的校验,且是奇校验,当数据位为偶数个1时该位置为1,为奇数个1时该位为0.总是保证1的个数为奇数个。

每一位都是在时钟的下降沿被读取:

二、电气接口协议(扫描码):键盘的处理器花费很多的时间来扫描或监视按键矩阵,如果它发现有键被按下释放或按住,键盘将发送扫描码的信息包到计算机。

     扫描码有两种不同的类型: 通码和断码

     当一个键被按下或按住就发送通码,当一个键被释放就发送断码。每个按键被分配了唯一的通码和断码,这样主机通过查找唯一的扫描码就可以测定是哪个按键。 每个键一整套的通断码组成了 扫描码集, 有三套标准的扫描码集分别是第一套、 第二套和第三套, 所有现代的键盘默认使用第二套扫描码。

只要一个键被按下这个键的通码就被发送到计算机,记住通码只表示键盘上的一个按键,它不表示印刷在按键上的那个字符。这就意味着在通码和ASCII 码之间没有已定义的关联,直到主机把扫描码翻译成一个字符或命令。在verilog 语句中需要将通码用case语句转换层所需要的ascii码。

多数通码长度为一个字节,少数扩展码为2或4个字节,这种码都含有E0H。

同理,只要键一释放断码就会被发送每个键都有它自己唯一的通码
它们也都有唯一的断码,不用总是通过查表来找出按键的断码。在通码和断码之间存在着必然的联系,多数第二套断码有两字节长。它们的第一个字节是F0h ,第二个字节是这个键的通码扩
展按键的断码,通常有三个字节。它们前两个字节是E0h,F0h 。最后一个字节是这个按键通码的最后一个字节,一些例子如下。

三、设计过程。

主要包括键盘驱动模块,ascii码转换,数码管显示等模块构成,其中数码管显示部分可以参考我的前一篇博文:http://blog.csdn.net/baijingdong/article/details/20220363

主要程序如下:主要思路将在注释中阐述

module ps2_keyboard_driver(clk,rst_n,ps2k_clk,ps2k_data,sm_bit,sm_seg,ps2_state);

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;

//------------------------------------------
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



 

四、测试:

本设计直接输出对应ASCII码值,根据16进制ascii码表;

    Q------->51H

    W------->57H

    E-------->45H

    R-------->52H

    T-------->54H

图1-器件连接

 

图2-测试

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

verilog语言的ps2键盘驱动设计 的相关文章

  • $readmem 可以在 Verilog 中综合吗?

    我正在尝试在 FPGA 上实现微控制器 我需要为其程序提供一个 ROM 如果我使用 readmemb 它会被正确合成到 ROM 中吗 如果不是 执行此操作的标准方法是什么 这取决于合成工具是否 readmemb是可以合成的 阿尔特拉的推荐的
  • 在verilog中使用for循环生成

    我试图理解为什么我们在verilog 中使用generate 和for 循环 一起使用生成和 for 循环 reg 3 0 temp genvar i generate for i 0 i lt 3 i i 1 begin always p
  • Verilog 位更改位置

    假设我有一个寄存器reg 15 0 my reg 其中包含一个16位signed sample 如何找到第一位变化的位置 意思是 如果假设my reg 16 b0001011011010111 我怎么知道第一个变化是0 to 1 is at
  • 带有always_comb结构的Systemverilog问题

    我对这个 SystemVerilog 代码有疑问 这是代码 module mult multiplicand multiplier Product clk clear Startm endm input 31 0 multiplicand
  • 如何在 verilog 中不使用 while() 循环(用于综合)?

    我已经养成了开发大量测试平台并使用 for 和 while 循环进行测试的习惯 没关系 问题是我已经将这种习惯用于对应该可综合的电路进行编码 XST等拒绝合成代码 无需对合成参数进行额外修改 例如 while num lt test num
  • Verilog HDL ?操作员

    什么是 用 Verilog 做什么 例如 以下命令是什么意思 input first din input 7 0 din output 127 0 parity reg 127 0 parity wire 7 0 feedback assi
  • 「Verilog学习笔记」 Johnson Counter

    专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点 刷题网站用的是牛客网 timescale 1ns 1ns module JC counter input clk input rst n output reg 3 0
  • 「Verilog学习笔记」游戏机计费程序

    专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点 刷题网站用的是牛客网 timescale 1ns 1ns module game count input rst n 异位复位信号 低电平有效 input clk 时
  • 为什么这个 verilog 关系语句返回 true?

    我有一条名为 sin hall2 的 9 位签名线 该语句返回 true sin hall2 8 0 gt 9 d1 当我查看模拟时 sin hall2 169 我假设这是 verilog 处理比较负数的方式 但我做错了什么 当我执行 si
  • Verilog 中总是后面跟着 #(...) pound 是什么意思?

    在一个简单的时钟生成器示例中 我看到以下代码 always cycle 2 clk clk 我以前总是见过 但没见过井号 我试图在文档中找到它 但我所能找到的只是一些对 实值端口 的引用 没有进一步的阐述 这是一个延迟操作 它本质上只是读取
  • 用于 Verilog 或 SystemVerilog 的 TAP(测试任何协议)模块

    是否有 TAP 测试任何协议 http testanything org Verilog 的实现 那就太好了 因为这样我就可以使用证明来自动检查我的结果 更新 10 9 09 有人问为什么不使用断言 部分 TAP 为我提供了一些很好的报告
  • 使用 Verilator 和 VPI 读取寄存器数组

    所以我在我的verilog中定义了以下寄存器 reg 31 0 register mem 0 15 verilator public 我的目标是从我的 verilator c 代码中读取存储在其中的 16 个值中的每一个 我发现有关 VPI
  • 在verilog中将wire值转换为整数

    我想将电线中的数据转换为整数 例如 wire 2 0 w 3 b101 我想要一个将其转换为 5 并将其存储在整数中的方法 我怎样才能以比这更好的方式做到这一点 j 1 for i 0 i lt 2 i i 1 begin a a w i
  • if 语句导致 Verilog 中的锁存推断?

    我正在编写用于合成算法的 Verilog 代码 我对哪些情况可能导致推断锁存器有点困惑 下面是这样的一段代码 虽然它在模拟中工作得很好 但我担心它可能会导致硬件问题 always b1 or b2 b1 map b2 map m1 map
  • 如何使用 Verilog 和 FPGA 计算一系列组合电路的传播延迟?

    我是 FPGA 和 HDL 的新手 但我正在尝试学习 但无法弄清楚这一点 如何通过多个级别的组合逻辑来计算或估计传播延迟 我可以仅凭经验确定这一点 还是可以在设计时弄清楚 在这种情况下 我使用 FPGA 来实现奇偶校验设置和检查电路 该电路
  • 开始后跟冒号和变量是什么意思?

    什么是data mux意思是这里 它只是块的名称吗 if PORT CONFIG 32 P0 1 b1 begin data mux end 这些是块名称 它们特别适用于generate块 例如 您可以定义一个generate块如 genv
  • 在 Verilog 设计中产生时钟故障

    我正在使用 Verilog 设计芯片 我有一个 3 位计数器 我希望当计数器处于第 8 次循环时 应该有一个时钟故障 之后就可以正常工作了 在 Verilog 设计中产生时钟故障的可能方法是什么 在时钟信号上注入毛刺的一种方法是使用forc
  • 在 Verilog 程序中使用连续分配?

    在 Verilog 程序中使用连续赋值是否可能和 或有用 例如 是否有任何理由将assign里面一个always堵塞 例如这段代码 always begin assign data in Data end 此外 是否可以用这种方法生成顺序逻
  • 具有内部赋值延迟的阻塞和非阻塞语句之间的区别

    以下 2 个 verilog 代码片段有什么区别 1 always in out 5 in AND 2 always in out lt 5 in 考虑到always块中不存在其他行 输出会有什么不同吗 问题参考幻灯片 16 参见 o5 和
  • 如何在 icarus verilog 中包含文件?

    我知道基本的 include filename v 命令 但是 我试图包含另一个文件夹中的模块 现在 该模块还包括同一文件夹中存在的其他模块 但是 当我尝试在最顶层运行该模块时 出现错误 C Users Dell Desktop MIPS

随机推荐