串行通訊是指外部設備與計算機間只使用一根數據線(另外須要地線,還可能須要控制線)進行數據傳送的方式。數據在一根數據線上按位依次傳送。因爲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