⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ps2.v

📁 此模块用于"PS/2接口的鼠标或键盘"与"具有外部读写的8位并口单片机"双向通信模块. Verilog HDL语言编写,在Quartus II 8.1 (32-Bit)软件中编译,并下载至EPM7
💻 V
字号:
//PS/2接口与8位并口通信模块  YTL-2009-01-18
//此模块用于PS/2接口与单片机相互通信.
//用于EPM7128SLC84-10芯片实验已通过

module PS2 (sys_clk, ps_clk, ps_data, nCS, nOE, nWE, DataReady, SendFin, data, nReset, stat, out);
	parameter ps_read_later = 4'd7;			//读取延时,单位1uS; (默认值:4'd8)
	parameter ps_write_later = 4'd5;		//发送延时,单位1uS (默认值:4'd5)
	parameter ps_comm_overtime = 5'd15;		//PS2接口接收超时检测延时,单位100uS (默认值:5'd15)
	parameter ps_begin_time = 8'd120;		//PS2接口发送起始位延时量,单位1uS (默认值:8'd150)
	parameter ps_send_overtime = 5'd15;		//PS2接口发送超时检测延时,单位100uS (默认值:5'd20)
	parameter Prescaler = 6'd50;			//对于系统时钟分频值, 分频周期max=63*2=126uS/pulse (默认值:6'd50)
	
	input sys_clk;			//系统时钟 1Mhz
	input nCS, nOE, nWE, nReset;	//芯片选择|读取数据|发送数据|芯片复位
	input stat;				//0=读取PS/2最后接收到的数据,1=读取内部状态寄存器选择
	inout ps_clk, ps_data;	//PS/2时钟|PS/2数据
	inout [7:0] data;		//8位数据双向总线
	output DataReady;		//PS2数据有效中断(高电平有效)
	output SendFin;			//PS2数据发送完成(高电平有效)

	output [7:0] out;		//用于直接显示内部状态寄存器(status)的内容,可以不用.

	/*内部状态寄存器各位含义(只读)
	b0-外部无应答错误
	b1-发送数据超时错误
	b2-读取数据超时
	b3-奇偶检验错误
	b4-输入缓存溢出
	b5-允许b1位清除,用于外部在读取状态之前保留此信息
	b6-允许b2位清除,用于外部在读取状态之前保留此信息*/
	tri1 ps_clk, ps_data;
	reg [6:0] status;				//内部状态寄存器
	reg DataReady;					//已收到一字节的PS/2数据(高电平有效)
	reg main_clk;					//100分频后的脉冲
//声明外部发送PS2数据模块变量
	reg write_ps_enable;			//开始发送PS/2数据
	reg SendFin;					//PS数据发送完成中断输出(高电平有效)
//声明系统时钟计数模块变量
	reg send_overtime;				//发送数据超时脉冲信号
	reg ps_overtime;				//读取数据超时脉冲信号
//声明读取PS数据模块变量
	reg in_rdy;						//PS数据接收完成
	reg read_ps_enable;				//表明正在读取PS数据中
	reg [7:0] in_buff;				//输入缓存
//声明发送PS数据模块变量
	reg [7:0] out_buff;				//输出缓存
	reg ps_data_out;				//PS2_data串行输出
	reg ps_clk_out;					//PS2_clk时钟输出
	reg send_fin_pulse;				//通知"外部发送PS2数据进程"已完成发送脉冲信号
	wire clr_WE;					//复位发送
//读写PS/2时钟生成模块
	reg R_clk;						//PS/2读时钟延时信号(上升沿有效)
	reg W_clk;						//PS/2时钟延时信号(上升沿有效)
	reg clr_read_pulse;				//读取模块复位
	reg clr_write_pulse;			//发送模块复位
	wire clr_RW;					//复位读写PS/2时钟生成模块
//PS/2接口滤波变量
	reg ps_clk_filter;				//滤波后的PS/2-clk
	reg ps_data_filter;				//滤波后的PS/2-data



	initial 	//上电初始化内部寄存器
	begin
		in_buff <= 0;
		in_rdy <= 0;
		out_buff <= 0;
		DataReady <= 0;
		main_clk = 0;
		write_ps_enable <= 0;
		read_ps_enable <= 0;
		ps_data_out <= 1'bz;
		ps_clk_out <= 1'bz;	
		ps_overtime <= 0;
		status <= 5'b0;
		send_fin_pulse <= 0;
		send_overtime <= 0;					
		SendFin <= 1'b1;
		R_clk <= 0;
		W_clk <= 0;
		clr_read_pulse <= 0;
		clr_write_pulse <= 0;
		ps_clk_filter <= 0;
		ps_data_filter <= 0;
	end

	assign data = (!nOE && !nCS)? ((stat)? status : in_buff) : 8'bz;	//三态8位数据总线,外部读取PS2数据
	assign ps_data = (write_ps_enable)? ps_data_out : 1'bz;				//发送PS2数据
	assign ps_clk = (write_ps_enable)? ps_clk_out : 1'bz;				//发送clk数据
	assign clr_RW = ps_overtime | send_overtime | !nReset;				//读写脉冲生成器复位
	assign clr_WE = send_fin_pulse | send_overtime | clr_write_pulse;	//ps_write_clk_count状态复位
	assign out = status;


//对于1MHz(1uS)50分频,生成1个100uS/脉冲周期
	always @(posedge sys_clk)
	begin : Prescaler_clk
		reg [5:0] pc;
		pc = pc + 6'b1;
		if (pc>=Prescaler) begin 
			main_clk <= ~main_clk;
			pc <= 0;
		end
	end

	
//读取PS2数据时清除中断标志
	always @(posedge in_rdy or negedge nOE or negedge nReset)		
	begin
		if (!nOE || !nReset) begin
			if (!nCS && !stat) DataReady <= 0;	
		end
		else DataReady <= in_rdy;
	end
	
	
//外部发送PS2数据
	always @(negedge nWE or posedge clr_WE) 	
	begin
		if (clr_WE) begin
			write_ps_enable <= 0; 
			SendFin <= 1'b1;
		end
		else begin
			if (!nCS && !write_ps_enable) begin 
				SendFin <= 0;
				out_buff <= data;
				write_ps_enable <= 1'b1;
			end
		end
	end
	
	
//系统时钟计数
	always @(posedge main_clk or negedge nOE or negedge nReset)  	
	begin : count_overtime
		reg [4:0] sys_clk_count;
		reg befo;	//读写转换间清零计数器
		//reg clr1, clr2;
		
		if (!nReset) begin
			status[1] <= 0;
			status[2] <= 0;
			status[5] <= 1;
			status[6] <= 1;
			sys_clk_count <= 0;
			befo <= 0;
		end
		else if (!nOE) begin
			if (!nCS && stat) begin
				status[5] <= 1;
				status[6] <= 1;
			end
		end
		else begin
			ps_overtime <= 0;
			send_overtime <= 0;
			if ((read_ps_enable) || write_ps_enable) begin	//读计时||写计时
				sys_clk_count = sys_clk_count + 5'd1;
				
				//读取数据超时计时
				if (read_ps_enable) begin	
					if (!befo) begin
						if (sys_clk_count>=ps_comm_overtime) begin
							ps_overtime <= 1;
							status[2] <= 1'b1;
							sys_clk_count <= 5'd0;
						end
						else begin
							if (status[6]) begin
								status[2] <= 0;
								status[6] <= 0;
							end
						end
					end
					else begin
						sys_clk_count <= 0;
						befo <= 0;
					end
				end
				//发送数据超时计时
				else if (write_ps_enable) begin	
					if (befo) begin
						if (sys_clk_count>=ps_send_overtime) begin
							send_overtime <= 1;				//发送超时错误
							status[1] <= 1;
							sys_clk_count <= 5'd0;
						end begin
							if (status[5]) begin
								status[1] <= 0;
								status[5] <= 0;
							end
						end
					end
					else begin
						sys_clk_count <= 0;
						befo <= 1;
					end
				end
			end
			else begin
				sys_clk_count <= 0;
				befo <= 0;
			end
		end
	end	

	
//读取PS_DATA时序进程
	always @(posedge R_clk or posedge clr_read_pulse)		
	begin: CommPs2Data
		reg [3:0] ps_read_clk_count;	//PS2读取时钟计数器(状态时序)
		reg [7:0] in_temp;				//输入移位寄存器缓存
		
		if (clr_read_pulse) begin
			ps_read_clk_count <= 0;
			read_ps_enable <= 0;
		end
		else begin
			ps_read_clk_count = ps_read_clk_count + 4'd1;
			in_rdy <= 0;
			case (ps_read_clk_count)
				4'd1:		//读取起始位
				if (ps_data_filter) 			//等待起始位
					ps_read_clk_count <= 4'd0;
				else
					read_ps_enable <= 1;		//通知外部已开始读取数据
				
				4'd2,4'd3,4'd4,4'd5,4'd6,4'd7,4'd8,4'd9:	
				begin		//读取数据位8bit
					in_temp[6:0] <= in_temp[7:1];
					in_temp[7] <= ps_data_filter;
				end
				
				4'd10: 
				begin		//读取奇偶校验
					if (ps_data_filter != Parity(in_temp)) status[3] <= 1;
					else status[3] <= 0;
				end
				
				4'd11:
				begin		//验证停止位和奇偶校验
					ps_read_clk_count <= 0;
					read_ps_enable <= 0;
					if (ps_data_filter && !status[3]) begin
						if (!DataReady) begin
							in_rdy <= 1'b1;
							status[4] <= 0;
							in_buff <= in_temp;
						end
						else status[4] <= 1;
					end
				end
				default: begin
					ps_read_clk_count = 4'd0;
					read_ps_enable <= 0;
				end
			endcase
		end
	end


//发送PS2接口时序进程
	always @(negedge W_clk or posedge clr_write_pulse)			
	begin: SendPs2Data	
		reg [3:0] ps_write_clk_count;	//PS2发送时钟计数器(状态时序)
		
		if (clr_write_pulse) begin
			ps_write_clk_count <= 0;
			send_fin_pulse <= 0;
		end
		else begin
			send_fin_pulse <= 0;
			ps_write_clk_count = ps_write_clk_count + 4'b1;
			case (ps_write_clk_count)
			4'd1:		//抑制PS设备通信
			begin
				ps_data_out <= 1'bz;
				ps_clk_out <= 0;
			end
			
			4'd2: 		//请求发送PS/2数据
			begin
				ps_data_out <= 0;
				ps_clk_out <= 1'bz;
			end
			
			4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9, 4'd10:		//发送PS2数据位
				ps_data_out <= (out_buff[ps_write_clk_count-4'd3])? 1'bz : 1'b0;
				
			4'd11:		//发送PS2奇偶校验
				ps_data_out <= (Parity(out_buff))? 1'bz : 1'b0;
				
			4'd12:		//发送停止位
				ps_data_out <= 1'bz;

			4'd13:		//接收应答位
			begin
				status[0] <= ps_data_filter;
				send_fin_pulse <= 1'b1;
				ps_write_clk_count <= 0;
			end
			
			default: begin
				ps_write_clk_count <= 0;
			end
			endcase
		end
	end	


//PS/2接口滤波
always @(negedge sys_clk or negedge nReset)
begin: Filter
	reg [1:0] psclk_s;			//采样计数器
	reg [1:0] psdat_s;			//采样计数器
	//reg psclk;				//用于检测PSCLK的跳变

	if (!nReset) begin
		psclk_s <= 0;
		psdat_s <= 0;
		ps_clk_filter <= 0;
		ps_data_filter <= 0;
	end
	else begin
		//ps/2时钟线滤波
		if (ps_clk_filter != ps_clk) begin		//检测PS2时钟跳变
			if (psclk_s == 2'd3) begin			//3次采样ps_clk
				ps_clk_filter <= ps_clk;
				psclk_s <= 0;
			end 
			else begin
				psclk_s = psclk_s + 2'b1;
			end
		end 
		else
			psclk_s <= 0;
		//ps/2数据线滤波
		if (ps_data_filter != ps_data) begin		//检测PS2时钟跳变
			if (psdat_s == 2'd3) begin				//3次采样ps_data
				ps_data_filter <= ps_data;
				psdat_s <= 0;
			end 
			else begin
				psdat_s = psdat_s + 2'b1;
			end
		end 
		else
			psdat_s <= 0;
	end
end


//读写PS/2时钟生成模块
	always @(negedge sys_clk or posedge clr_RW)
	begin: RW_PSCLK
		reg [7:0] begin_time;	//读写PS/2延时
		reg clk_pulse;			//PS2时钟下降沿
		reg clk_status;			//用于检测ps_clk_filter时钟上升沿
		reg pswen;				//用于检测write_ps_enable的跳变
		reg wen_pulse;			//write_ps_enable时钟上升沿
		
		if (clr_RW) begin
			begin_time <= 0;
			R_clk <= 0;
			W_clk <= 0;
			pswen <= 0;
			clk_pulse <= 0;
			wen_pulse <= 0;
			clk_status <= 0;
			if (send_overtime) clr_write_pulse <= 1'b1;
			if (ps_overtime) clr_read_pulse <= 1'b1;
			if (!nReset) begin
				clr_write_pulse <= 1'b1;
				clr_read_pulse <= 1'b1;
			end
		end
		else begin
			R_clk <= 0;
			W_clk <= 0;
			clr_read_pulse <= 0;
			clr_write_pulse <= 0;
			begin_time <= (clk_pulse || wen_pulse)? begin_time + 7'd1 : 7'd0; 
			//out <= begin_time;
			
			if (clk_status != ps_clk_filter) begin	//检测PS_CLK跳变
				clk_status <= ps_clk_filter;
				if (!ps_clk_filter) begin			//检测PS_CLK下降沿
					clk_pulse <= 1;
				end
			end
						
			if (pswen != write_ps_enable) begin	//检测write_ps_enable时钟跳变
				pswen <= write_ps_enable;
				begin_time <= 7'd0;
				if (write_ps_enable) begin		//检测write_ps_enable时钟上升沿
					wen_pulse <= 1;				//第0位起始位发送数据的脉冲
					W_clk <= 1;
					clr_read_pulse <= 1;
				end		
				else
					clr_write_pulse <= 1;
			end

			if (wen_pulse) begin
				clk_pulse <= 0;
				if (begin_time >= ps_begin_time) begin
					wen_pulse <= 0;
					W_clk <= 1;
					begin_time <= 0;
				end
			end
			else if (write_ps_enable) begin	//写时钟成生
				if (begin_time >= ps_write_later) begin
					clk_pulse <= 0;
					begin_time <= 0;
					W_clk <= 1;
				end
			end
			else begin						//读时钟成生
				if (begin_time >= ps_read_later) begin
					clk_pulse <= 0;
					begin_time <= 0;
					R_clk <= 1;
				end
			end
		end
	end


//奇偶校验
	function Parity;
		input [7:0] Din;
		integer i;
		reg Par;
		begin 
			Par = 0;
			for (i=0; i<8; i=i+1)
				if (Din[i]) Par = Par + 1'b1;
			Parity = ~Par;		//偶校验
		end
	endfunction	
	
endmodule

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -