【特權FPGA】之PS/2鍵盤解碼

?0 故事背景

見過這種接口的朋友們,大概都已經成家立業了吧。不過今天我們不討論這種接口的歷史,只講講這種接口的設計。(如果還沒有成家的朋友也別生氣,做自己想做的事情就對了!)

1 時序分析

數據幀格式如圖所示,起始位為低電平,停止位為高電平,應答位僅用在主機對設備的通訊中使用。如果數據位中1的個數為偶數,校驗位就為1;如果數據位中1的個數為奇數,校驗位就為0;總之,數據位中1的個數加上校驗位中1的個數總為奇數,因此總進行奇校驗。(是不是發現它的數據傳輸和串口很像呢!)[1]

(為了簡化)當一個鍵(A~Z)被按下或按住,就發送通碼(都是f0);當一個鍵(A~Z)被釋放,就發送斷碼。

鍵盤掃描碼(實用于標準PC的101、102和104 鍵的鍵盤),按下發送通碼,彈起發送斷碼。[2]了解即可。

2 接口定義

信號名稱方向接口描述信息
clkinput時鐘信號,50MHz
rst_ninput復位信號,低電平有效
ps2k_clkinputPS/2接口時鐘信號
ps2k_datainputPS/2接口數據信號
rs232_txinputRS232發送數據信號

3 RTL視圖

4 整體代碼

Top層代碼:

`timescale 1ns / 1ps// Company: 
// Engineer:
//
// Create Date:    21:21:41 08/07/08
// Design Name:    
// Module Name:    ps2_key
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 歡迎加入EDN的FPGA/CPLD助學小組一起討論:http://group.ednchina.com/1375/module ps2_key(clk,rst_n,ps2k_clk,ps2k_data,rs232_tx);input clk;			//50M時鐘信號
input rst_n;		//復位信號
input ps2k_clk;		//PS2接口時鐘信號
input ps2k_data;	//PS2接口數據信號
output rs232_tx;	// RS232發送數據信號wire[7:0] ps2_byte;	// 1byte鍵值
wire ps2_state;		//按鍵狀態標志位wire bps_start;		//接收到數據后,波特率時鐘啟動信號置位
wire clk_bps;		// clk_bps的高電平為接收或者發送數據位的中間采樣點 ps2scan			ps2scan(		.clk(clk),			  	//按鍵掃描模塊.rst_n(rst_n),				.ps2k_clk(ps2k_clk),.ps2k_data(ps2k_data),.ps2_byte(ps2_byte),.ps2_state(ps2_state));speed_select	speed_select(			.clk(clk),.rst_n(rst_n),.bps_start(bps_start),.clk_bps(clk_bps));my_uart_tx		my_uart_tx(				.clk(clk),.rst_n(rst_n),.clk_bps(clk_bps),.rx_data(ps2_byte),.rx_int(ps2_state),.rs232_tx(rs232_tx),.bps_start(bps_start));endmodule

ps2scan代碼

`timescale 1ns / 1ps// Company: 
// Engineer:
//
// Create Date:    21:25:06 08/07/08
// Design Name:    
// Module Name:    ps2scan
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// module ps2scan(clk,rst_n,ps2k_clk,ps2k_data,ps2_byte,ps2_state);input        clk;		//50M時鐘信號
input        rst_n;	//復位信號
input   	 ps2k_clk;	//PS2接口時鐘信號
input   	 ps2k_data;		//PS2接口數據信號
output[7:0]  ps2_byte;	// 1byte鍵值,只做簡單的按鍵掃描
output       ps2_state;		//鍵盤當前狀態,ps2_state=1表示有鍵被按下 //------------------------------------------
reg ps2k_clk_r0,ps2k_clk_r1,ps2k_clk_r2;	//ps2k_clk狀態寄存器//wire pos_ps2k_clk; 	// ps2k_clk上升沿標志位
wire neg_ps2k_clk;	// ps2k_clk下降沿標志位always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginps2k_clk_r0 <= 1'b0;ps2k_clk_r1 <= 1'b0;ps2k_clk_r2 <= 1'b0;endelse begin								//鎖存狀態,進行濾波ps2k_clk_r0 <= ps2k_clk;ps2k_clk_r1 <= ps2k_clk_r0;ps2k_clk_r2 <= ps2k_clk_r1;end
endassign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2;	//下降沿//------------------------------------------
reg[7:0] ps2_byte_r;		//PC接收來自PS2的一個字節數據存儲器
reg[7:0] temp_data;			//當前接收數據寄存器
reg[3:0] num;				//計數寄存器always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginnum <= 4'd0;temp_data <= 8'd0;endelse if(neg_ps2k_clk) begin	//檢測到ps2k_clk的下降沿case (num)4'd0:	num <= num+1'b1;4'd1:	beginnum <= num+1'b1;temp_data[0] <= ps2k_data;	//bit0end4'd2:	beginnum <= num+1'b1;temp_data[1] <= ps2k_data;	//bit1end4'd3:	beginnum <= num+1'b1;temp_data[2] <= ps2k_data;	//bit2end4'd4:	beginnum <= num+1'b1;temp_data[3] <= ps2k_data;	//bit3end4'd5:	beginnum <= num+1'b1;temp_data[4] <= ps2k_data;	//bit4end4'd6:	beginnum <= num+1'b1;temp_data[5] <= ps2k_data;	//bit5end4'd7:	beginnum <= num+1'b1;temp_data[6] <= ps2k_data;	//bit6end4'd8:	beginnum <= num+1'b1;temp_data[7] <= ps2k_data;	//bit7end4'd9:	beginnum <= num+1'b1;	//奇偶校驗位,不做處理end4'd10: beginnum <= 4'd0;	// num清零enddefault: ;endcaseend	
endreg 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) beginkey_f0 <= 1'b0;ps2_state_r <= 1'b0;endelse if(num==4'd10) begin	//剛傳送完一個字節數據if(temp_data == 8'hf0) key_f0 <= 1'b1;else beginif(!key_f0) begin	//說明有鍵按下ps2_state_r <= 1'b1;ps2_byte_r <= temp_data;	//鎖存當前鍵值endelse beginps2_state_r <= 1'b0;key_f0 <= 1'b0;endendend
endreg[7:0] ps2_asci;	//接收數據的相應ASCII碼always @ (ps2_byte_r) begincase (ps2_byte_r)		//鍵值轉換為ASCII碼,這里做的比較簡單,只處理字母8'h15: ps2_asci <= 8'h51;	//Q8'h1d: ps2_asci <= 8'h57;	//W8'h24: ps2_asci <= 8'h45;	//E8'h2d: ps2_asci <= 8'h52;	//R8'h2c: ps2_asci <= 8'h54;	//T8'h35: ps2_asci <= 8'h59;	//Y8'h3c: ps2_asci <= 8'h55;	//U8'h43: ps2_asci <= 8'h49;	//I8'h44: ps2_asci <= 8'h4f;	//O8'h4d: ps2_asci <= 8'h50;	//P				  	8'h1c: ps2_asci <= 8'h41;	//A8'h1b: ps2_asci <= 8'h53;	//S8'h23: ps2_asci <= 8'h44;	//D8'h2b: ps2_asci <= 8'h46;	//F8'h34: ps2_asci <= 8'h47;	//G8'h33: ps2_asci <= 8'h48;	//H8'h3b: ps2_asci <= 8'h4a;	//J8'h42: ps2_asci <= 8'h4b;	//K8'h4b: ps2_asci <= 8'h4c;	//L8'h1a: ps2_asci <= 8'h5a;	//Z8'h22: ps2_asci <= 8'h58;	//X8'h21: ps2_asci <= 8'h43;	//C8'h2a: ps2_asci <= 8'h56;	//V8'h32: ps2_asci <= 8'h42;	//B8'h31: ps2_asci <= 8'h4e;	//N8'h3a: ps2_asci <= 8'h4d;	//Mdefault: ;endcase
endassign ps2_byte = ps2_asci;	 
assign ps2_state = ps2_state_r;endmodule

speed_select代碼

`timescale 1ns / 1ps// Company: 
// Engineer:
//
// Create Date:    17:27:40 08/28/08
// Design Name:    
// Module Name:    speed_select
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// module speed_select(clk,rst_n,bps_start,clk_bps);input clk;	// 50MHz主時鐘
input rst_n;	//低電平復位信號
input bps_start;	//接收到數據后,波特率時鐘啟動信號置位
output clk_bps;	// clk_bps的高電平為接收或者發送數據位的中間采樣點 /*
parameter 		bps9600 	= 5207,	//波特率為9600bpsbps19200 	= 2603,	//波特率為19200bpsbps38400 	= 1301,	//波特率為38400bpsbps57600 	= 867,	//波特率為57600bpsbps115200	= 433;	//波特率為115200bpsparameter 		bps9600_2 	= 2603,bps19200_2	= 1301,bps38400_2	= 650,bps57600_2	= 433,bps115200_2 = 216;  
*///以下波特率分頻計數值可參照上面的參數進行更改
`define		BPS_PARA		5207	//波特率為9600時的分頻計數值
`define 	BPS_PARA_2		2603	//波特率為9600時的分頻計數值的一半,用于數據采樣reg[12:0] cnt;			//分頻計數
reg clk_bps_r;			//波特率時鐘寄存器//----------------------------------------------------------
reg[2:0] uart_ctrl;	// uart波特率選擇寄存器
//----------------------------------------------------------always @ (posedge clk or negedge rst_n)if(!rst_n) cnt <= 13'd0;else if((cnt == `BPS_PARA) || !bps_start) cnt <= 13'd0;	//波特率計數清零else cnt <= cnt+1'b1;			//波特率時鐘計數啟動always @ (posedge clk or negedge rst_n)if(!rst_n) clk_bps_r <= 1'b0;else if(cnt == `BPS_PARA_2) clk_bps_r <= 1'b1;	// clk_bps_r高電平為接收數據位的中間采樣點,同時也作為發送數據的數據改變點else clk_bps_r <= 1'b0;assign clk_bps = clk_bps_r;endmodule

my_uart_tx代碼

`timescale 1ns / 1ps// Company: 
// Engineer:
//
// Create Date:    17:11:32 08/28/08
// Design Name:    
// Module Name:    my_uart_rx
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// module my_uart_tx(clk,rst_n,clk_bps,rx_data,rx_int,rs232_tx,bps_start);input clk;			// 50MHz主時鐘
input rst_n;		//低電平復位信號
input clk_bps;		// clk_bps的高電平為接收或者發送數據位的中間采樣點
input[7:0] rx_data;	//接收數據寄存器
input rx_int;		//接收數據中斷信號,接收到數據期間始終為高電平,在此利用它的上升沿來啟動發送數據
output rs232_tx;	// RS232發送數據信號
output bps_start;	//接收或者要發送數據,波特率時鐘啟動信號置位//---------------------------------------------------------
reg rx_int0,rx_int1,rx_int2;	//rx_int信號寄存器,捕捉下降沿濾波用
wire pos_rx_int;				// rx_int下降沿標志位always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginrx_int0 <= 1'b0;rx_int1 <= 1'b0;rx_int2 <= 1'b0;endelse beginrx_int0 <= rx_int;rx_int1 <= rx_int0;rx_int2 <= rx_int1;end
endassign pos_rx_int =  rx_int1 & ~rx_int2;	//捕捉到上升沿后,neg_rx_int拉地保持一個主時鐘周期//---------------------------------------------------------
reg[7:0] tx_data;	//待發送數據的寄存器
//---------------------------------------------------------
reg bps_start_r;
reg tx_en;	//發送數據使能信號,高有效
reg[3:0] num;always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginbps_start_r <= 1'bz;tx_en <= 1'b0;tx_data <= 8'd0;endelse if(pos_rx_int) begin	//接收數據完畢,準備把接收到的數據發出去bps_start_r <= 1'b1;tx_data <= rx_data;	//把接收到的數據存入發送數據寄存器tx_en <= 1'b1;		//進入發送數據狀態中endelse if(num==4'd11) begin	//數據發送完成,復位bps_start_r <= 1'b0;tx_en <= 1'b0;end
endassign bps_start = bps_start_r;//---------------------------------------------------------
reg rs232_tx_r;always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginnum <= 4'd0;rs232_tx_r <= 1'b1;endelse if(tx_en) beginif(clk_bps)	beginnum <= num+1'b1;case (num)4'd0:	rs232_tx_r <= 1'b0; 	//發送起始位4'd1:	rs232_tx_r <= tx_data[0];	//發送bit04'd2:	rs232_tx_r <= tx_data[1];	//發送bit14'd3: rs232_tx_r <= tx_data[2];	//發送bit24'd4: rs232_tx_r <= tx_data[3];	//發送bit34'd5: rs232_tx_r <= tx_data[4];	//發送bit44'd6: rs232_tx_r <= tx_data[5];	//發送bit54'd7:	rs232_tx_r <= tx_data[6];	//發送bit64'd8: rs232_tx_r <= tx_data[7];	//發送bit74'd9: rs232_tx_r <= 1'b1;	//發送結束位default: rs232_tx_r <= 1'b1;endcaseendelse if(num==4'd11) num <= 4'd0;	//復位end
endassign rs232_tx = rs232_tx_r;endmodule

5 總結

代碼中有詳細的解釋,有問題隨時討論。

知識是相互貫通的,夯實基礎,才能筑高樓。歡迎大家批評指正!

參考文獻

[1]特權FPGA PS2鍵盤解碼實驗

[2]PS2鍵盤掃描碼:通碼與斷碼 - JustXIII - 博客園

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76573.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76573.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76573.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

DAPP實戰篇:使用web3.js實現前端輸入錢包地址查詢該地址的USDT余額—操作篇

專欄:區塊鏈入門到放棄查看目錄-CSDN博客文章瀏覽閱讀396次。為了方便查看將本專欄的所有內容列出目錄,按照順序查看即可。后續也會在此規劃一下后續內容,因此如果遇到不能點擊的,代表還沒有更新。聲明:文中所出觀點大多數源于筆者多年開發經驗所總結,如果你想要知道區塊…

高中生學習數據隱私保護的“技術-制度-文化”協同機制研究

一、引言 1.1 研究背景與意義 在數字化時代的浪潮下&#xff0c;教育領域正經歷著深刻的變革&#xff0c;智能教育平臺如雨后春筍般涌現&#xff0c;為高中教育帶來了新的活力與機遇。這些平臺借助先進的信息技術&#xff0c;能夠實時收集、分析大量的高中生學習數據&#xf…

【Java多線程】告別線程混亂!深度解析Java多線程4大實現方式(附實戰案例)

一、繼承Thread類 實現步驟&#xff1a; 1.繼承Thread類 2.重寫run()方法 3.創建線程對象并調用start()方法 示例&#xff1a; class MyThread extends Thread {Overridepublic void run() {for (int i 0; i < 5; i) {System.out.println(Thread.currentThread().getNam…

全國產V7-690T核心板/算法驗證板/FPGA開發板

UD SOM-404全國產化信號處理模塊既可以作為核心板使用&#xff0c;也可以單獨使用。FPGA對外有80組GTY通過兩個FMC連接器全部引出&#xff0c;多個模塊可以級聯使用&#xff0c;擴展信號處理能力。FMC連接器也滿足標準規范&#xff0c;可以插入標準的FMC或FMC子板。模塊為100%國…

STM32_HAL庫提高中斷執行效率

目錄 中斷流程分析我的解決辦法優缺點 大家都在說STM32 HAL 庫中斷效率低下。具體哪里不行&#xff1f;如何優化&#xff1f; 我手里的項目要用到多個定時器TIM6、TIM7、TIM9、TIM10、TIM11、TIM12、TIM13&#xff0c;在處理這些定時器中斷的時候&#xff0c;也發現了這個問題。…

RabbitMQ惰性隊列的工作原理、消息持久化機制、同步刷盤的概念、延遲插件的使用方法

惰性隊列工作原理 惰性隊列通過盡可能多地將消息存儲到磁盤上來減少內存的使用。與傳統隊列相比&#xff0c;惰性隊列不會主動將消息加載到內存中&#xff0c;而是盡量讓消息停留在磁盤上&#xff0c;從而降低內存占用。盡管如此&#xff0c;它并不保證所有操作都是同步寫入磁…

Spark Core(二)

Spark-Core編程&#xff08;二&#xff09; RDD轉換算子 RDD 根據數據處理方式的不同將算子整體上分為 Value 類型、雙 Value 類型和 Key-Value 類型 Value類型 1&#xff09;map 將處理的數據逐條進行映射轉換&#xff0c;這里的轉換可以是類型的轉換&#xff0c;也可以是…

C#打開文件及目錄腳本

如果每天開始工作前都要做一些準備工作&#xff0c;比如打開文件或文件夾&#xff0c;我們可以使用代碼一鍵完成。 using System.Diagnostics; using System.IO;namespace OpenFile {internal class Program{static void Main(string[] args){Console.WriteLine("Hello, …

Python生成exe

其中的 -w 參數是 PyInstaller 用于窗口模式&#xff08;Windowed mode&#xff09;&#xff0c;它會關閉命令行窗口的輸出&#xff0c;這通常用于 圖形界面程序&#xff08;GUI&#xff09;&#xff0c;比如使用 PyQt6, Tkinter, PySide6 等。 所以&#xff1a; 如果你在沒有…

【大模型微調】如何解決llamaFactory微調效果與vllm部署效果不一致如何解決

以下個人沒整理太全 一、生成式語言模型的對話模板介紹 使用Qwen/Qwen1.5-0.5B-Chat訓練 對話模板不一樣。回答的內容就會不一樣。 我們可以看到例如qwen模型的tokenizer_config.json文件&#xff0c;就可以看到對話模板&#xff0c;一般同系列的模型&#xff0c;模板基本都…

Linux網絡編程——詳解網絡層IP協議、網段劃分、路由

目錄 一、前言 二、IP協議的認識 1、什么是IP協議&#xff1f; 2、IP協議報頭 三、網段劃分 1、初步認識IP與路由 2、IP地址 I、DHCP動態主機配置協議 3、IP地址的劃分 I、CIDR設計 II、子網數目的計算 III、子網掩碼的確定 四、特殊的IP地址 五、IP地址的數量限…

ansible+docker+docker-compose快速部署4節點高可用minio集群

目錄 github項目地址 示例服務器列表 安裝前 修改變量文件group_vars/all.yml 修改ansible主機清單 修改setup.sh安裝腳本 用法演示 安裝后驗證 github項目地址 https://github.com/sulibao/ansible_minio_cluster.git 示例服務器列表 安裝前 修改變量文件group_var…

MySql主從相關概念

想象一下&#xff0c;你的業務飛速增長&#xff0c;用戶請求如潮水般涌來&#xff0c;突然數據庫主庫宕機&#xff0c;數據丟失&#xff0c;服務癱瘓——這簡直是開發者的噩夢&#xff01;MySQL主從復制就像一張安全網&#xff0c;通過主庫寫、從庫讀的協作模式&#xff0c;不僅…

機械臂只有位置信息是否可以進行手眼標定?

平常我在做手眼標定時&#xff0c;一般都是通過OpenCV的cv::calibrateHandEye函數進行求解&#xff0c;需要輸入多組不同的機械臂位姿。今天遇到了一款舵機機器人&#xff0c;只能獲取位置&#xff0c;得不到姿態信息&#xff0c;想著那就把姿態都設為0&#xff0c;結果求不出來…

華為數字芯片機考2025合集2已校正

單選 1. 題目內容 關于亞穩態的描述錯誤的是&#xff08; &#xff09;。 1. 解題步驟 1.1 理解亞穩態&#xff08;Metastability&#xff09;的核心特性 亞穩態是指觸發器無法在指定時間內穩定輸出有效邏輯電平&#xff08;0或1&#xff09;的狀態&#xff0c;其關鍵特點…

T-Box車載系統介紹及其應用

定義 T-Box汽車系統&#xff0c;全稱為Telematics - BOX&#xff0c;也常簡稱為車載T - BOX&#xff0c;是汽車智能系統及車聯網系統中的核心組成部分&#xff0c;是安裝在車輛上的一種高科技遠程信息處理器。 工作原理 T-Box的核心功能主要通過MPU和MCU實現。MPU負責應用程序功…

[redis進階一]redis的持久化(1)RDB篇章

目錄 一 認識持久化 (1)先看總結圖 (2)什么是持久化? (3)redis是怎么進行持久化的呢 (4)簡單分析一下RDB持久化和AOF持久化的不同 二 RDB持久化 (1)RDB的觸發機制 (2)RDB的bgsave執行流程 (3)RDB文件的處理 (4)RDB的優缺點 (5)RDB效果演示板書 三 溫習Linux文件…

uniapp日常總結--uniapp頁面跳轉方式

uniapp日常總結--uniapp頁面跳轉方式_uniapp 跳轉-CSDN博客

《汽車電器與電子技術》實驗報告

SRS系統結構原理與故障檢測診斷 車輛上為什么要配安全氣囊&#xff1f;——解析汽車被動安全的關鍵防線 一、安全氣囊的核心作用&#xff1a;應對高速碰撞的“救命緩沖墊” 車輛在高速碰撞時&#xff08;如正面碰撞、側面碰撞&#xff09;&#xff0c;人體會因慣性以極高速度…

ffmpeg編解碼器相關函數

文章目錄 &#x1f3af; 你需要理解的核心結構體&#xff1a;&#x1f4e6; 常用函數及使用順序&#xff08;以解碼為例&#xff09;1?? avcodec_find_decoder() / avcodec_find_encoder()2?? avcodec_alloc_context3()3?? avcodec_parameters_to_context()4?? avcodec…