串行通讯是指外部设备与计算机间只使用一根数据线(另外须要地线,还可能须要控制线)进行数据传送的方式。数据在一根数据线上按位依次传送。因为CPU与接口之间按并行方式传输,接口与外设之间按串行方式传输,所以串行通讯的基本功能是:在发送时,把CPU送来的并行数据转换为串行数据,逐位依次发送出去;在接受时,把外部设备发送过来的的串行数据逐位地接受,组装成并行数据,并行地送给CPU去处理。异步
异步通讯是以一个字符做为传输方式,通讯中两个字符间的时间间隔多少不固定,但同一个字符的两个相邻位的时间间隔固定。通讯过程当中没有时钟线。code
UART使用的是串行异步通讯。blog
基本的UART通讯协议:接口
波特率:表示每秒钟传输字符数的多少input
起始位:通常为逻辑“0”it
数据位:通常位6~8位之间可变,数据低位在前,高位在后class
校验位:能够为无校验位,奇校验,偶校验,常“0”或常“1”之间可选module
中止位:必须为逻辑“1”sed
空闲位:处于逻辑“1”状态并行
UART发送功能模块:
`define NONE 0 //无校验位 `define ODD 1 //奇校验 `define EVEN 2 //偶校验 `define MARK 3 //常“1” `define SPACE 4 //常“0” `define STOPBITS_ONE 0 //1位中止位 `define STOPBITS_ONEHALF 1 //1.5位中止位 `define STOPBITS_TWO 2 //2位中止位 module uart_tx #( parameter CLK_FREQ = 50_000_000, //时钟频率 parameter BPS = 9600, //波特率 parameter PARITY = `NONE, //数据位宽 parameter DATA_BITS = 8, //校验位 parameter STOP_BITS = `STOPBITS_ONE //中止位位宽 ) ( input clk, input rst_n, input [DATA_BITS-1:0] data, input enable, //定义一个发送使能信号 output reg tx, output wire free //定义一个发送端空闲状态 ); reg [DATA_BITS-1:0] data_2 = 0; reg flag = 0; //定义一个flag信号来锁住enable wire stop_flag; //定义一个传输中止脉冲 reg [31:0] cnt = 0; localparam [31:0] CNT_MAX = CLK_FREQ/BPS; reg [2:0] cstate = 0; localparam [2:0] FSM_IDEL = 0, //空闲位 FSM_START = 1, //开始位 FSM_DATA = 2, //数据位 FSM_PARITY = 3, //校验位 FSM_STOP = 4; //中止位 reg ifparity = 0; //有无校验位 reg parity_value = 0; //校验位的值 reg [3:0] num = 0; //定义一个计数num assign free = ~flag; assign stop_flag = (cstate == FSM_STOP && ((STOP_BITS == `STOPBITS_ONE && cnt == CNT_MAX-1) || (STOP_BITS == `STOPBITS_ONEHALF && num == 1 && cnt == CNT_MAX>>1) || (STOP_BITS == `STOPBITS_TWO && num == 1 && cnt == CNT_MAX-1))) ? 1'b1 : 1'b0; always @(posedge clk or negedge rst_n) if (!rst_n) data_2 <= 0; else if (enable && flag == 0) data_2 <= data; else data_2 <= data_2; always @(posedge clk or negedge rst_n) if (!rst_n) flag <= 0; else if (enable) flag <= 1; else if (stop_flag) flag <= 0; else flag <= flag; always @(posedge clk or negedge rst_n) if (!rst_n) cnt <= 0; else if (cnt == CNT_MAX-1 || flag == 0 ) cnt <= 0; else if (flag) cnt <= cnt + 1; else cnt <= cnt; always @(posedge clk or negedge rst_n) if (!rst_n) cstate <= FSM_IDEL; else case (cstate) FSM_IDEL : begin tx <= 1; if (flag) cstate <= FSM_START; else cstate <= FSM_IDEL; end FSM_START : begin tx <= 0; if (cnt == CNT_MAX-1) cstate <= FSM_DATA; else cstate <= FSM_START; end FSM_DATA : begin tx <= data_2 [num]; if (cnt == CNT_MAX-1 && num == DATA_BITS-1) begin num <= 0; cstate <= ifparity ? FSM_PARITY : FSM_STOP; end else if (cnt == CNT_MAX-1) num <= num + 1; else num <= num; end FSM_PARITY : begin tx <= parity_value; if (cnt == CNT_MAX-1) cstate <= FSM_STOP; else cstate <= FSM_PARITY; end FSM_STOP : begin tx <= 1; if (STOP_BITS == `STOPBITS_ONE && cnt == CNT_MAX-1) cstate <= FSM_IDEL; else if (STOP_BITS == `STOPBITS_ONEHALF) begin if (num == 1 && cnt == CNT_MAX>>1) begin cstate <= FSM_IDEL; num <= 0; end else if (cnt == CNT_MAX-1) num <=1; else cstate <= FSM_STOP; end else if (STOP_BITS == `STOPBITS_TWO) begin if (num == 1 && cnt == CNT_MAX-1) begin cstate <= FSM_IDEL; num <= 0; end else if (cnt == CNT_MAX-1) num <=1; else cstate <= FSM_STOP; end end default : cstate <= FSM_IDEL; endcase always @(*) if (!rst_n) begin ifparity = 0; parity_value = 0; end else case (PARITY) `NONE : begin ifparity = 0; parity_value = 0; end `ODD : begin ifparity = 1; parity_value = ~(^data_2); end `EVEN : begin ifparity = 1; parity_value = ^data_2; end `MARK : begin ifparity = 1; parity_value = 1; end `SPACE : begin ifparity = 1; parity_value = 0; end default : begin ifparity = 0; parity_value = 0; end endcase
UART接收功能模块:
`define NONE 0 //无校验位 `define ODD 1 //奇校验 `define EVEN 2 //偶校验 `define MARK 3 //常"1" `define SPACE 4 //常"0" `define STOPBITS_ONE 0 //1位中止位 `define STOPBITS_ONEHALF 1 //1.5位中止位 `define STOPBITS_TWO 2 //2位中止位 module uart_rx #( parameter BPS = 9600, //波特率 parameter CLK_FREQ = 50_000_000, //时钟频率 parameter DATA_BITS = 8, //数据位宽 parameter PARITY = `NONE, //校验位 parameter STOP_BITS = `STOPBITS_ONE //中止位位宽 ) ( input clk, input rst_n, input rx, output reg [DATA_BITS-1:0] data, output reg done, output reg err ); reg [1:0] in; always @(posedge clk or negedge rst_n) if (!rst_n) in <= 0; else in <= {in[0],rx}; wire en; assign en = (in[1] == 1 && in[0] == 0) ? 1 : 0; reg flag = 0; //定义一个接收信号标志位 always @(posedge clk or negedge rst_n) if (!rst_n) flag <= 0; else if (en) flag <= 1; else if (cstate == FSM_STOP && sampling) flag <= 0; else flag <= flag; reg [31:0] cnt = 0; localparam [31:0] CNT_MAX = CLK_FREQ/BPS; always @(posedge clk or negedge rst_n) if (!rst_n) cnt <= 0; else if (cnt == CNT_MAX-1 || flag == 0) cnt <= 0; else if (flag) cnt <= cnt + 1; else cnt <= cnt; reg sampling = 0; //定义一个采样脉冲信号 always @(posedge clk or negedge rst_n) if (!rst_n) sampling = 0; else if (cnt == CNT_MAX>>1) sampling = 1; else sampling = 0; reg [3:0] num = 0; //定位计数num reg ifparity = 0; //有无校验位 reg parity_value = 0; //定义校验位的值 reg parity_rx = 0; //定义接受信号中校验位的值 reg [2:0] cstate = 0; localparam [2:0] FSM_IDEL = 0, //空闲位 FSM_START = 1, //开始位 FSM_DATA = 2, //数据位 FSM_PARITY = 3, //校验位 FSM_STOP = 4; //中止位 always @(posedge clk or negedge rst_n) if (!rst_n) begin cstate <= FSM_IDEL; parity_rx <= 0; done <= 0; err <= 0; data <= 0; num <= 0; end else case (cstate) FSM_IDEL : if (flag) cstate <= FSM_START; else cstate <= FSM_IDEL; FSM_START : begin done <= 0; if (sampling) cstate <= FSM_DATA; else cstate <= FSM_START; end FSM_DATA : if (sampling && num <= DATA_BITS-1) begin data[num] <= rx; num <= num + 1; end else if (num == DATA_BITS) begin num <= 0; cstate <= ifparity ? FSM_PARITY : FSM_STOP ; end FSM_PARITY : if (sampling) begin parity_rx <= rx; cstate <= FSM_STOP; end else cstate <= FSM_PARITY; FSM_STOP : if (sampling) begin done <= 1; cstate <= FSM_IDEL; err <= ifparity ? (parity_rx == parity_value ? 0 : 1) : 0; end else cstate <= FSM_STOP; default : begin cstate <= FSM_IDEL; parity_rx <= 0; done <= 0; err <= 0; end endcase always @(*) if (!rst_n) begin ifparity = 0; parity_value = 0; end else case (PARITY) `NONE : begin ifparity = 0; parity_value = 0; end `ODD : begin ifparity = 1; parity_value = ~(^data); end `EVEN : begin ifparity = 1; parity_value = ^data; end `MARK : begin ifparity = 1; parity_value = 1; end `SPACE : begin ifparity = 1; parity_value = 0; end default : begin ifparity = 0; parity_value = 0; end endcase endmodule