////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//公司:
//工程师: Lawson
//
//创建日期: 2004年10月10日
//设计名字:
//模块名字: PS2驱动器 Driver
//目标装置:
//工具版本: Xilinx ISE 6.3.02i
//描述:
// 按照PS2协议,驱动PS2设备并给出错误信号
//属性:
//
//修改:
//
//附加说明:
//
//
// 读时序状态图:
//
// CLK:
//______| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|_____…………………………______| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
//
// 延时时钟信号
// key_clk:
//_______| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|____________________| ̄ ̄ ̄ ̄ ̄ ̄|_____…………………………______| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
// 状态: Idle | Read_start | Read_start_complete| Read_data |
Read_data_complete | Read_data ←→ Read_data_complete |
Read_finish | Idle
//
// bit_0 bit_1 - bit_7,P_bit,stop_bit
// DATA:
// ̄ ̄ ̄ ̄ ̄ ̄ ̄|________________________________|〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓|〓〓〓〓………………………… ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
// ↑ ↑ ↑
// 操作: 数据读入 counter_reg加一 数据读入
//
//
// 写时序状态图:
// 延时100uS
// CLK:
//_____________________| ̄ ̄ ̄ ̄ ̄ ̄ ̄|___________________| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|______………………………………… ̄ ̄ ̄|_______________| ̄ ̄ ̄ ̄ ̄ ̄ ̄|_______________| ̄ ̄ ̄ ̄ ̄
//
// 状态: Idle | Inhibit_com || Write_start | Write_data |
//Write_data_complete | Write_data ←→ Write_data_complete | Write_stop |
// Read_ACK | Write_finish | Idle
// ↑
// Inhibit_com_complete
// bit_0 bit_1 - bit_7,P_bit stop_bit
// DATA:
// ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|________________|〓〓〓〓〓〓〓〓〓〓|〓〓〓〓〓〓〓〓〓〓〓〓〓…………………………………〓〓〓〓| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|______________________| ̄ ̄ ̄
// ↑ ↑ ↑ ↑ ↑
// 操作: 释放时钟线 写数据 写数据 释放数据线
//读ACK
// 下拉数据线 counter_reg加一
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
`timescale 1 ns / 1 ns
module PS2_Driver (
//bidirectional
ps2_data, //ps2设备数据端
ps2_clk, //ps2设备时钟端
//input
w_data, //驱动器数据输入端
ena, //驱动器使能端
rw, //驱动器读写控制端(1:读,0:写)
drv_clk, //驱动器时钟端
clr_n, //驱动器复位端
//output
r_data, //驱动器数据输出端
ready, //驱动器完成信号
error //驱动器错误代码输出端
);
//+--------------------------- 参数设置-----------------------------------+
parameter DATA_WIDTH = 8; //输入/输出数据宽度
parameter COUNT = 4; //状态码宽度
parameter TIME = 8'd100; //抑制通信时间
//+-----------------------------------------------------------------------+
inout ps2_data /* synthesis xc_padtype = "IOBUF_LVTTL_F_24" */;
inout ps2_clk /* synthesis xc_padtype = "IOBUF_LVTTL_F_24" */;
input [DATA_WIDTH-1:0] w_data;
input rw;
input ena;
input clr_n;
input drv_clk;
output [DATA_WIDTH-1:0] r_data;
output ready;
output [1:0] error;
//+-------------------------------PS2状态代码------------------------------+
parameter Idle = 4'h0; //初始化
parameter Read_start = 4'h1; //读开始位
parameter Read_start_complete = 4'h2; //完成读开始位
parameter Read_data = 4'h3; //读数据
parameter Read_data_complete = 4'h4; //完成读数据
parameter Read_finish = 4'h5; //完成读操作
parameter Inhibit_com = 4'h6; //抑制通讯
parameter Inhibit_com_complete = 4'h7; //完成抑制通讯
parameter Write_start = 4'h8; //写开始位
parameter Write_data = 4'h9; //写数据
parameter Write_data_complete = 4'hA; //完成写数据
parameter Write_stop = 4'hB; //写停止位
parameter Read_ACK = 4'hC; //读应答位
parameter Write_finish = 4'hD; //完成写操作
//+-----------------------------KeyBoard 错误代码--------------------------+
parameter PARITY_ERROR = 2'h1; //效验错误
parameter NO_ACK = 2'h2; //无应答错误
//+------------------------------------------------------------------------+
reg [COUNT-1:0] current_state;
reg [COUNT-1:0] next_state;
reg [DATA_WIDTH:0] r_data_reg; // | 8 | 7-0 |
// 效验位 数据位
// | P | DATA |
reg [DATA_WIDTH:0] w_data_reg; // | 8 | 7-0 |
// 效验位 数据位
// | P | DATA |
reg clk_w;
reg data_w;
reg key_reg;
reg ready;
reg [9:0] clk_sampling_reg;
reg [2:0] data_sampling_reg;
reg [1:0] error;
reg [DATA_WIDTH-1:0] counter_reg;
wire key_data;
wire k_data;
wire key_clk;
wire k_clk;
wire clr_n;
wire write_parity;
wire read_parity;
//------------------------------------------------------------------------------//
// //
// 接口逻辑函数 //
// //
//------------------------------------------------------------------------------//
assign r_data = r_data_reg [7:0];
assign write_parity = ^~w_data;
assign read_parity = ^~r_data_reg [7:0];
//对延时4个时钟,延时7个时钟,延时10个时钟后的ps2_clk信号进行取样
assign key_clk = (clk_sampling_reg[3]&clk_sampling_reg[6])|
(clk_sampling_reg[6]&clk_sampling_reg[9])|(clk_sampling_reg[3]&clk_sampling_reg[9]);
assign key_data = (data_sampling_reg[0]&data_sampling_reg[1])|
(data_sampling_reg[1]&data_sampling_reg[2])|(data_sampling_reg[2]&data_sampling_reg[0]);
//ps2_data双向IO口
assign ps2_data = data_w ? 1'bz : key_reg; // 1:输入 0:输出
assign k_data = ps2_data;
//ps2_data双向IO口
assign ps2_clk = clk_w ? 1'bz : 1'b0;
assign k_clk = ps2_clk;
//------------------------------------------------------------------------------//
// //
// 采样输入 //
// //
//------------------------------------------------------------------------------//
always @ (posedge drv_clk or negedge clr_n)
if (!clr_n) clk_sampling_reg <= 0;
else clk_sampling_reg <= {clk_sampling_reg[8:0],k_clk};
always @ (posedge drv_clk or negedge clr_n)
if (!clr_n) data_sampling_reg <= 0;
else data_sampling_reg <= {data_sampling_reg[1:0],k_data};
//------------------------------------------------------------------------------//
// //
// PS2驱动器状态机 //
// //
//------------------------------------------------------------------------------//
always @(posedge drv_clk or negedge clr_n)
if (!clr_n) begin
current_state <= Idle;
end
else begin
current_state <= next_state;
end
always @(current_state or ena or rw or k_clk or k_data or key_clk or
key_data or counter_reg)
case(current_state)
Idle: begin
if(ena) begin
if(rw) begin
next_state = Read_start;
end
else next_state = Inhibit_com;
end
else begin
next_state = Idle;
end
end
//---------------------------------PS2驱动器读状态-------------------------------//
Read_start: begin
if( key_clk == 0 && key_data == 0) begin
next_state = Read_start_complete;
end
else next_state = Read_start;
end
Read_start_complete: begin
if( key_clk == 1) begin
next_state = Read_data;
end
else next_state = Read_start_complete;
end
Read_data: begin //读数据和效验位
if( key_clk == 0 ) begin
next_state = Read_data_complete;
end
else next_state = Read_data;
end
Read_data_complete: begin
if( key_clk == 1 ) begin
if( counter_reg == 8'd9) begin
next_state = Read_finish;
end
else begin
next_state = Read_data;
end
end
else next_state = Read_data_complete;
end
Read_finish: begin
if( key_clk == 1 && key_data == 1) begin
next_state = Idle;
end
else next_state = Read_finish;
end
//---------------------------------PS2驱动器写状态-------------------------------//
Inhibit_com: begin
if(counter_reg == TIME) begin //抑制通讯约100μS
next_state = Inhibit_com_complete;
end
else next_state = Inhibit_com;
end
Inhibit_com_complete: begin
if( k_clk == 1 ) begin
next_state = Write_start;
end
else next_state = Inhibit_com_complete;
end
Write_start: begin
if( k_clk == 0 ) begin
next_state = Write_data;
end
else next_state = Write_start;
end
Write_data: begin
if( k_clk == 1 ) begin
next_state = Write_data_complete;
end
else next_state = Write_data;
end
Write_data_complete: begin
if( k_clk == 0 ) begin
if( counter_reg == 8'd9) begin
next_state = Write_stop;
end
else begin
next_state = Write_data;
end
end
else next_state = Write_data_complete;
end
Write_stop: begin
if( k_clk == 1 ) begin
next_state = Read_ACK;
end
else next_state = Write_stop;
end
Read_ACK: begin //应答检测
if( k_clk == 0) begin
next_state = Write_finish;
end
else next_state = Read_ACK;
end
Write_finish: begin
if( k_data == 1 && k_clk == 1 ) begin
next_state = Idle;
end
else begin
next_state = Write_finish;
end
end
default: begin
next_state = 'bx;
end
endcase
//------------------------------------------------------------------------------//
// //
// PS2驱动器控制输出 //
// //
//------------------------------------------------------------------------------//
always @( posedge drv_clk or negedge clr_n)
if (!clr_n) begin
r_data_reg <= 0;
w_data_reg <= 0;
clk_w <= 0;
data_w <= 1;
key_reg <= 0;
ready <= 0;
error <= 0;
counter_reg <= 0;
end
else begin
case (current_state)
Idle: begin
$display("Idle start");
ready <= 0;
clk_w <= 0; //抑制通讯
end
//------------------------------------------------------------------------------//
// //
// PS2驱动器读操作 //
// //
//------------------------------------------------------------------------------//
Read_start: begin
clk_w <= 1;
$display("Read start");
end
Read_start_complete: begin
$display("Complete read start");
end
Read_data: begin
if( key_clk == 0 ) begin
r_data_reg [counter_reg] <= key_data; //读数据、效验位和结束位
$display("read r_data[%d] BIT is %b",counter_reg,ps2_data);
end
else $display("Wait negative edge");
end
Read_data_complete: begin
if( key_clk == 1 ) begin
counter_reg <= counter_reg + 1;
end
else $display("Wait advancing edge");
end
Read_finish: begin
ready <= 1;
counter_reg <= 0;
$display("PS2K parity bit is %b",r_data_reg [8]);
if( read_parity == r_data_reg [8] )
begin //数据效验位和接收效验位比较
$display("PS2K parity bit is right");
$display("Complete read r_data is %b",r_data);
end
else begin
$display("PS2K parity bit is error");
error <= PARITY_ERROR;
end
end
//------------------------------------------------------------------------------//
// //
// PS2驱动器写操作 //
// //
//------------------------------------------------------------------------------//
Inhibit_com: begin
counter_reg <= counter_reg + 1; //延时计数
$display("Inhibit communication");
end
Inhibit_com_complete: begin
if( k_clk == 1 ) begin
w_data_reg <= {write_parity,w_data}; //写数据寄存器
counter_reg <= 0;
end
else begin
$display("Complete Inhibit communication");
data_w <= 0; //设数据线为输出
key_reg <= 0; //置数据线为"0"
clk_w <= 1; //释放时钟线
end
end
Write_start: begin
if( k_clk == 0 ) begin
$display("write w_data_reg[%d] BIT is
%b",counter_reg,w_data_reg[counter_reg]);
key_reg <= w_data_reg [counter_reg]; //写0位数据
end
else begin
$display("Write start");
end
end
Write_data: begin
if( k_clk == 1 ) begin
counter_reg <= counter_reg + 1; //加位数计数器
key_reg <= w_data_reg [counter_reg]; //写1-7位,校验位数据
$display("write w_data_reg[%d] BIT is
%b",counter_reg,w_data_reg[counter_reg]);
end
else begin
$display("Write data");
$display("Wait advancing edge");
end
end
Write_data_complete: begin
$display("Wait negative edge");
end
Write_stop: begin
data_w <= 1; //释放数据线
counter_reg <= 0;
$display("Write stop");
end
Read_ACK: begin //应答检测
if( k_clk == 0 ) begin
if( k_data == 0) begin
$display("PS2K_DATA ACK ");
end
else begin
$display("PS2K_DATA NO ACK ");
error <= NO_ACK;
end
end
else begin
$display("Read ACK");
end
end
Write_finish: begin
$display("Complete write data");
ready <= 1;
end
endcase
end
endmodule
//1、命名规则很统一,但是不太符合惯例,惯例也就是没有人说必须,但是大家都这样做^_^,
// 一般parameter/define等用大写,其他都是小写。182-202中always @ 格式不统一。
//2、不要使用tab键或者将tab键自动转换为空格,因为不同的编辑器可能会看到不同结果,
// 就像你现在肯定不能想象我这边看到的代码是什么样子,如果使用空格就没有这个问题。
//说明一点,虽然一般代码文字编写规范不是很重要,但是好的规范会给人professional的感觉,所以还是很重要的^_^
//
分享到:
相关推荐
这样的资源对于学习Verilog和数字逻辑设计,特别是PS2接口实现的人来说非常宝贵。通常,这样的程序会包含详细的注释来解释代码的工作原理,帮助初学者理解并应用到实际设计中。 在标签“ps2程序”中,我们可以推断...
3. **状态机设计**:PS2键盘控制器通常需要一个状态机来管理与键盘交互的不同阶段,如等待数据、读取数据、解析数据等。状态机可以用case语句实现,每个状态对应一种操作。 4. **同步电路**:由于Verilog设计的硬件...
verilog hdl FPGA PS2时序控制接口源代码 很经典 很实用
在本文中,我们将深入探讨如何使用Verilog语言来模拟PS2协议,特别是在FPGA(Field-Programmable Gate Array)平台上实现。PS2协议相对简单,只需要四条线连接,其中VCC为+5V电源,时钟和数据线是双向的bidir接口,...
本篇文章将深入探讨如何利用Verilog进行PS2(Personal System/2)键盘的设计,这是一种常见于早期个人电脑的接口技术,尽管现在已经被USB取代,但在FPGA(Field-Programmable Gate Array)学习和应用中,PS2键盘仍然...
1. **时钟和复用器**:由于PS2协议的数据和时钟线是分离的,我们需要在Verilog中创建两个独立的输入端口来处理这两个信号。时钟信号通常比数据传输速率快,所以需要用到边沿触发的D触发器来捕获数据。复用器用于在...
verilog编程ps2接口设计,基于fpga的设计
**PS2键盘接口Verilog实现详解** 在数字电路设计领域,Verilog是一种广泛使用的硬件描述语言,用于描述和实现各种数字逻辑系统。本篇将详细探讨如何使用Verilog实现PS/2(Personal System/2)键盘接口。PS/2键盘...
本项目关注的是在Verilog环境下实现PS2(Personal System/2)键盘接口的程序。PS2键盘是早期个人计算机上常见的接口,尽管现在已被USB接口取代,但在学习嵌入式系统和硬件设计时,理解PS2接口仍然是很有价值的。 ...
在实际的设计过程中,开发者首先需要了解PS2接口规范,然后用Verilog HDL编写代码,最后通过仿真验证和FPGA硬件实现来测试设计是否符合预期。这个实验不仅帮助学生掌握Verilog编程,还能加深对数字系统设计和接口...
在这个特定的“verilog PS2 源程序”项目中,开发者使用Verilog编写了代码,以便在DE2开发板上实现与PS/2(Personal System/2)键盘的接口。 PS/2键盘接口是IBM个人电脑上的一种标准接口,用于连接键盘和鼠标。它...
从提供的部分代码来看,有两个主要的Verilog模块:`ps2_key`作为顶层模块,负责协调整个系统的运作;`ps2scan`是底层模块之一,专门处理PS/2键盘的扫描和解码。`ps2_key`模块接收到50MHz的时钟信号(`clk`),复位...
这个Verilog代码将帮助你理解如何用硬件描述语言来处理PS/2协议,包括时序控制、数据解析和错误检测。 PS/2键盘协议基于异步串行通信,通常使用4线连接,其中两根线用于数据传输(数据线),两根线用于时钟信号...
总的来说,通过这个Verilog实现的PS/2键盘控制器,我们可以学习到串行通信协议的实现,状态机设计,错误检测,以及FPGA设计的基本原则,这些都是数字逻辑设计的基础知识,对于电子工程师来说是非常有价值的。
在这个项目中,我们关注的是如何使用FPGA来实现PS2(Personal System/2)键盘的驱动。PS2键盘是早期个人电脑上常见的输入设备接口,尽管现在已被USB接口取代,但在学习和实验环境中仍然有其价值。 首先,我们需要...
ps2键盘verilog代码 可以使用已经测试过 比较完善的一个代码
FPGA控制PS2键盘verilog设计Quartus9.1工程源码+设计说明文件,可以做为你的学习设计参考。 1. 这个实例通过开发板上面的PS/2接口接收键盘输入的数据,在LCD上面显示出来; 2. 工程在project文件夹里面,打开工程; 3...
在这个场景下,开发人员使用Verilog来设计一个模块,该模块能够接收并处理来自PS/2键盘的输入信号,然后可能将这些信号转换为可读的数据格式供其他系统使用。 标签“demo fpga__ps2__键盘 verilog_ps2键盘”是对...
标题中的"S9_PS2_LCD.rar_12864_12864 verilog_ps2_ps2_lcd_12864_verilog 128"提到了几个关键概念,包括“S9_PS2_LCD”、“12864”、“verilog”、“ps2”和“ps2_lcd_12864”。这些关键词暗示了这是一个关于使用...