1.UART
UART是异步串行通信口的总称。它所包含的RS232RS449RS423等等是对应各种异步串行通信口的接口标准和总线标准。他们规定了通信口的电气特性、传输速率、连接特性和机械特性等一系列内容,实际上属于通信网络的底层概念,与通信协议没有直接关系。
几个相关名词的解释:
·波特率:每秒钟传送的bit的个数。
·起始位:先发出一个逻辑0的信号,表示传输数据的开始。
·数据位:衡量通信中实际数据位的参数,标准数据位可以是5、7、8位,从最低位开始传输。
·奇偶校验位:UART发送时,检查发送数据中“1”的个数,自动在奇偶校验位添加1/0,用于发送数据的校验。
·停止位:一个数据的结束标志,可以为1位、1.5位、2位的高电平。
·空闲位:处于逻辑1状态,表示当前线路上无数据传输。
·时序图:
·发送数据过程:空闲状态,线路处于高电平,当收到发送数据指令后,拉低电平一个数据位的时间,接着数据按从低位到高位依次发送,数据发送完毕,接着发送奇偶校验位和停止位(停止位为高电平),一帧数据发送结束。
·接收数据过程:空闲状态,线路处于高电平,当检测到线路的下降沿,说明线路有数据传输,按照约定的波特率从低位到高位接收数据,数据接收完毕,接着接收并比较奇偶校验位是否正确,如果正确则通知接收端设备准备接收数据或存入缓存。
由于UART是异步传输,没有同步传输时钟。为保证数据传输的正确性,每个数据有16个时钟采样,取中间的采样值,以保证不会误码或滑码。
·设计实例:
下面是一个UART的回环实例代码设计:
接收模块uart_rx:
module uart_rx( input rxd, input clk, output receive_ack, output reg [7:0] data_i ); parameter IDLE = 0; parameter RECEIVE = 1; parameter RECEIVE_END = 2; reg [3:0] CS,NS; reg [4:0] count; reg [7:0] data_o_tmp; always@(posedge clk) CS <= NS; always@(*) begin NS <= CS; case(CS) IDLE: if(!rxd) NS = RECEIVE; RECEIVE: if(count == 7) NS = RECEIVE_END;else NS = NS; RECEIVE_END:NS = IDLE; default: NS = IDLE; endcase end always@(posedge clk) if(CS == RECEIVE) count <= count + 1; else if(CS == IDLE | CS == RECEIVE_END) count <= 0; always @(posedge clk) if(CS == RECEIVE)begin data_i[6:0] <= data_i[7:1]; data_i[7] <= rxd; end assign receive_ack = (CS == RECEIVE_END) ? 1 : 0; endmodule发送模块uart_tx:
module uart_tx( input [7:0] data_o, input clk, input receive_ack, output reg txd ); parameter IDLE = 0; parameter SEND_START = 1; parameter SEND_DATA = 2; parameter SEND_END = 3; reg [3:0] CS,NS; reg [4:0] count; reg [7:0] data_o_tmp; always @ (posedge clk) CS <= NS; always @ (*) begin NS <= CS; case(CS) IDLE: begin if(receive_ack) NS = SEND_START; end SEND_START: begin NS = SEND_DATA; end SEND_DATA: begin if(count == 7) NS = SEND_END; end SEND_END: begin if(receive_ack) NS = SEND_START; end default: NS = IDLE; endcase end always @(posedge clk) if(CS == SEND_START) count <= count + 1; else if(CS == IDLE | CS == SEND_END) count <= 0; else count <= count; always @(posedge clk) if(CS == SEND_START) data_o_tmp <= data_o; else if(CS == SEND_DATA) data_o_tmp[6:0] <= data_o_tmp[7:1]; always @(posedge clk) if(CS == SEND_START) txd <= 0; else if(CS == SEND_DATA) txd <= data_o_tmp; else if(CS == SEND_END) txd <= 1; endmodulemodule uart_tx( input [7:0] data_o, input clk, input receive_ack, output reg txd ); parameter IDLE = 0; parameter SEND_START = 1; parameter SEND_DATA = 2; parameter SEND_END = 3; reg [3:0] CS,NS; reg [4:0] count; reg [7:0] data_o_tmp; always @ (posedge clk) CS <= NS; always @ (*) begin NS <= CS; case(CS) IDLE: begin if(receive_ack) NS = SEND_START; end SEND_START: begin NS = SEND_DATA; end SEND_DATA: begin if(count == 7) NS = SEND_END; end SEND_END: begin if(receive_ack) NS = SEND_START; end default: NS = IDLE; endcase end always @(posedge clk) if(CS == SEND_START) count <= count + 1; else if(CS == IDLE | CS == SEND_END) count <= 0; else count <= count; always @(posedge clk) if(CS == SEND_START) data_o_tmp <= data_o; else if(CS == SEND_DATA) data_o_tmp[6:0] <= data_o_tmp[7:1]; always @(posedge clk) if(CS == SEND_START) txd <= 0; else if(CS == SEND_DATA) txd <= data_o_tmp; else if(CS == SEND_END) txd <= 1; endmodule特定波特率产生模块clk_div:
module clk_div( input clk, output reg clk_out ); parameter baud_rata = 9600; parameter div_num = 'd125_000_000 /baud_rata; //分频数等于时钟频率除以想要得到的波特率 reg [15:0] num; always @(posedge clk) begin if(num == div_num) begin num <= 0; clk_out <= 1; end else begin num <= num + 1; clk_out <= 0; end end endmodule
顶层文件uart_top:
module uart_top( input clk, input rxd, output txd ); wire clk_9600; wire receive_ack; wire [7:0] data; uart_tx uart_tx ( .clk (clk_9600), .txd (txd), .data_o (data), .receive_ack(receive_ack) ); uart_rx uart_rx ( .clk (clk_9600), .rxd (rxd), .data_i (data), .receive_ack(receive_ack) ); clk_div clk_div ( .clk (clk), .clk_out (clk_9600) ); endmodule
2.PS/2 PS/2是一种双向同步串行通信协议。接口是一种6针的连接口,但只有四个引脚是有意义的,分别是Clock(时钟)、Data(数据)、VCC和GND。其中时钟和数据引脚是双向的。PS/2常用于连接某些输入设备,例如鼠标、键盘等。通信的两端通过时钟来同步,通过数据引脚来交换数据。任何一方想要抑制另外一方的通信,只需要将时钟引脚拉低即可。 如果是PC和PS/2键盘之间通信,PC必须做主机,即PC可以抑制键盘发送数据,而键盘不能抑制PC发送数据。 PS/2的每一位数据帧包含11-12位,具体含义如下:
数据位名称 | 说明 |
1个起始位 | 总是逻辑0 |
8个数据位 | 低位在前 |
1个奇偶校验位 | 奇校验 |
1个停止位 | 总是逻辑1 |
1个应答位 | 仅用在主机对设备的通信中 |
·PS/2的时序图:
由设备产生时钟和数据,主机根据时钟来读取数据。以FPGA和PS/2键盘为例,键盘产生时钟和数据,FPGA只需要读数据。当时钟下降沿时,FPGA记录数据信号。 ·设计实例: 主机为FPGA,根据PS/2的时序,得到键盘的按键值。虽然在时序图中,主机是在时钟下降沿读取数据,但实际上要为了排除噪声干扰,需要在FPGA端对信号进行滤波。下面给出设计代码。
module ps2_keyboard( input clk, input clr, input PS2C, //ps2 clk in input PS2D, //ps2 data in output [15:0] xkey);reg PS2CF;reg PS2DF;reg [7:0] ps2c_filter;reg [7:0] ps2d_filter;reg [10:0] shift1;reg [10:0] shift2; assign xkey = { shift2[8:1], shift1[8:1] };always @(posedge clk or posedge clr) begin if (clr) begin ps2c_filter <= 11'b0; ps2d_filter <= 11'b0; PS2CF <= 1; PS2DF <= 1; end else begin ps2c_filter[7] <= PS2C; ps2c_filter[6:0] <= ps2c_filter[7:1]; ps2d_filter[7] <= PS2D; ps2d_filter[6:0] <= ps2d_filter[7:1]; if(ps2c_filter == 8'b1111_1111) PS2CF <= 1; //去时钟毛刺 else if(ps2c_filter == 8'b0000_0000) PS2CF <= 0; if(ps2d_filter == 8'b1111_1111) PS2DF <= 1; //去数据毛刺 else if(ps2d_filter == 8'b0000_0000) PS2DF <= 0; endend always @(negedge PS2CF or posedge clr) begin if (clr) begin shift1 <= 11'b0; shift2 <= 11'b0; end else begin shift1 <= {PS2DF, shift1[10:1]}; shift2 <= {shift1[0], shift2[10:1]}; endend endmodule
原文标题:常用通信协议总结及FPGA实现(上)
文章出处:【微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。
全部0条评论
快来发表一下你的评论吧 !