串口發(fā)送與接收模塊設(shè)計(jì)代碼分析
1.1Tx_Bps_Gen
Tx_Bps_Gen為發(fā)送波特率生成模塊,每當(dāng)有Byte_En信號(hào)到來時(shí),即開始產(chǎn)生發(fā)送一個(gè)完整字節(jié)的數(shù)據(jù)需要的完整波特率時(shí)鐘信號(hào)。
本設(shè)計(jì),波特率支持9600bps到921600bps。例如,需要產(chǎn)生的波特率時(shí)鐘為9600bps,即波特率時(shí)鐘頻率為9600Hz,周期為104.17us。生成9600Hz波特率時(shí)鐘的核心思想就是對(duì)系統(tǒng)時(shí)鐘進(jìn)行計(jì)數(shù),這里設(shè)定系統(tǒng)時(shí)鐘為50MHz,則一個(gè)時(shí)鐘的周期為20ns,我們只需要對(duì)系統(tǒng)時(shí)鐘計(jì)數(shù)5208次,每計(jì)數(shù)5208次產(chǎn)生一個(gè)時(shí)鐘周期的高電平脈沖,即可實(shí)現(xiàn)生成9600Hz波特率時(shí)鐘的功能。相應(yīng)代碼如下所示:
018 parameter system_clk = 50_000_000; /*輸入時(shí)鐘頻率設(shè)定,默認(rèn)50M*/
019
020 /*根據(jù)輸入時(shí)鐘頻率計(jì)算生成各波特率時(shí)分頻計(jì)數(shù)器的計(jì)數(shù)最大值*/
021 localparam bps9600 = system_clk/9600 - 1;
022 localparam bps19200 = system_clk/19200 - 1;
023 localparam bps38400 = system_clk/38400 - 1;
024 localparam bps57600 = system_clk/57600 - 1;
025 localparam bps115200 = system_clk/115200 - 1;
026 localparam bps230400 = system_clk/230400 - 1;
027 localparam bps460800 = system_clk/460800 - 1;
028 localparam bps921600 = system_clk/921600 - 1;
029
030 reg [31:0]BPS_PARA;/*波特率分頻計(jì)數(shù)器的計(jì)數(shù)最大值*/
031
032 always@(posedge Clk or negedge Rst_n)
033 if(!Rst_n)begin
034 BPS_PARA 《= bps9600;/*復(fù)位時(shí)波特率默認(rèn)為9600bps*/
035 end
036 else begin
037 case(Baud_Set)/*根據(jù)波特率控制信號(hào)選擇不同的波特率計(jì)數(shù)器計(jì)數(shù)最大值*/
038 3‘d0: BPS_PARA 《= bps9600;
039 3’d1: BPS_PARA 《= bps19200;
040 3‘d2: BPS_PARA 《= bps38400;
041 3’d3: BPS_PARA 《= bps57600;
042 3‘d4: BPS_PARA 《= bps115200;
043 3’d5: BPS_PARA 《= bps230400;
044 3‘d6: BPS_PARA 《= bps460800;
045 3’d7: BPS_PARA 《= bps921600;
046 default: BPS_PARA 《= bps9600;
047 endcase
048 end
049
050 //=========================================================
051 reg[12:0]Count;
052
053 reg n_state;
054 localparam IDEL_1 = 1‘b0,
055 SEND = 1’b1;
056
057 reg BPS_EN;
058
059 /*-------波特率時(shí)鐘生成控制邏輯--------------*/
060 always@(posedge Clk or negedge Rst_n)
061 if(!Rst_n)begin
062 BPS_EN 《= 1‘b0;
063 n_state 《= IDEL_1;
064 end
065 else begin
066 case(n_state)
067 IDEL_1:
068 if(Byte_En)begin/*檢測(cè)到字節(jié)發(fā)送使能信號(hào),則啟動(dòng)波特率生成進(jìn)程,同時(shí)進(jìn)入發(fā)送狀態(tài)*/
069 BPS_EN 《= 1’b1;
070 n_state 《= SEND;
071 end
072 else begin
073 n_state 《= IDEL_1;
074 BPS_EN 《= 1‘b0;
075 end
076 SEND:
077 if(Tx_Done == 1)begin/*發(fā)送完成,關(guān)閉波特率生成進(jìn)程,回到空閑狀態(tài)*/
078 BPS_EN 《= 1’b0;
079 n_state 《= IDEL_1;
080 end
081 else begin
082 n_state 《= SEND;
083 BPS_EN 《= 1‘b1;
084 end
085 default:n_state 《= IDEL_1;
086 endcase
087 end
088
089 /*-------波特率時(shí)鐘生成定時(shí)器--------------*/
090 always@(posedge Clk or negedge Rst_n)
091 if(!Rst_n)
092 Count 《= 13’d0;
093 else if(BPS_EN == 1‘b0)
094 Count 《= 13’d0;
095 else begin
096 if(Count == BPS_PARA)
097 Count 《= 13‘d0;
098 else
099 Count 《= Count + 1’b1;
100 end
101
102 /*輸出數(shù)據(jù)接收采樣時(shí)鐘*/
103 //-----------------------------------------------
104 always @(posedge Clk or negedge Rst_n)
105 if(!Rst_n)
106 Bps_Clk 《= 1‘b0;
107 else if(Count== 1)
108 Bps_Clk 《= 1’b1;
109 else
110 Bps_Clk 《= 1‘b0;
第18行“parameter system_clk = 50_000_000;”,這里用一個(gè)全局參數(shù)定義了系統(tǒng)時(shí)鐘,暫時(shí)設(shè)定為50M,可根據(jù)實(shí)際使用的板卡上的工作時(shí)鐘進(jìn)行修改。
所謂波特率生成,就是用一個(gè)定時(shí)器來定時(shí),產(chǎn)生頻率與對(duì)應(yīng)波特率時(shí)鐘頻率相同的時(shí)鐘信號(hào)。例如,我們使用波特率為115200bps,則我們需要產(chǎn)生一個(gè)頻率為115200Hz的時(shí)鐘信號(hào)。那么如何產(chǎn)生這樣一個(gè)115200Hz的時(shí)鐘信號(hào)呢?這里,我們首先將115200Hz時(shí)鐘信號(hào)的周期計(jì)算出來,1秒鐘為1000_000_000ns,因此波特率時(shí)鐘的周期Tb= 1000000000/115200 =8680.6ns,即115200信號(hào)的一個(gè)周期為8680.6ns,那么,我們只需要設(shè)定我們的定時(shí)器定時(shí)時(shí)間為8680.6ns,每當(dāng)定時(shí)時(shí)間到,產(chǎn)生一個(gè)系統(tǒng)時(shí)鐘周期長度的高脈沖信號(hào)即可。系統(tǒng)時(shí)鐘頻率為50MHz,即周期為20ns,那么,我們只需要計(jì)數(shù)8680/20個(gè)系統(tǒng)時(shí)鐘,就可獲得8680ns的定時(shí),即bps115200=Tb/Tclk - 1=Tb*fclk - 1=fclk/115200-1。相應(yīng)的,其它波特率定時(shí)值的計(jì)算與此類似,這里小梅哥就不再一一分析。20行至28行為波特率定時(shí)器定時(shí)值的計(jì)算部分。
為了能夠通過外部控制波特率,設(shè)計(jì)中使用了一個(gè)3位的波特率選擇端口:Baud_Set。通過給此端口不同的值,就能選擇不同的波特率,此端口控制不同波特率的原理很簡單,就是一個(gè)多路選擇器,第32行至第48行即為此多路選擇器的控制代碼, Baud_Set的值與各波特率的對(duì)應(yīng)關(guān)系如下:
000 :9600bps;
001 :19200bps;
010 :38400bps;
011 :57600bps;
100 :115200bps;
101 :230400bps;
110 :460800bps;
111 :921600bps;
1.2Uart_Byte_Tx
Uart_Byte_Tx為字節(jié)發(fā)送模塊,該模塊在波特率時(shí)鐘的節(jié)拍下,依照UART通信協(xié)議發(fā)送一個(gè)完整的字節(jié)的數(shù)據(jù)。當(dāng)一個(gè)字節(jié)發(fā)送完畢后,Tx_Done產(chǎn)生一個(gè)高脈沖信號(hào),以告知其它模塊或邏輯一個(gè)字節(jié)的數(shù)據(jù)已經(jīng)傳輸完成,可以開始下一個(gè)字節(jié)的發(fā)送了。其發(fā)送一個(gè)字節(jié)數(shù)據(jù)的實(shí)現(xiàn)代碼如下:
33 /*計(jì)數(shù)波特率時(shí)鐘,11個(gè)波特率時(shí)鐘為一次完整的數(shù)據(jù)發(fā)送過程*/
34 always@(posedge Clk or negedge Rst_n)
35 if(!Rst_n)
36 Bps_Clk_Cnt 《= 4’b0;
37 else if(Bps_Clk_Cnt == 4‘d11)
38 Bps_Clk_Cnt 《= 4’b0;
39 else if(Bps_Clk)
40 Bps_Clk_Cnt 《= Bps_Clk_Cnt + 1‘b1;
41 else
42 Bps_Clk_Cnt 《= Bps_Clk_Cnt;
43
44 /*生成數(shù)據(jù)發(fā)送完成標(biāo)志信號(hào)*/
45 always@(posedge Clk or negedge Rst_n)
46 if(!Rst_n)
47 Tx_Done 《= 1’b0;
48 else if(Bps_Clk_Cnt == 4‘d11)
49 Tx_Done 《= 1’b1;
50 else
51 Tx_Done 《= 1‘b0;
52
53 /*在開始發(fā)送起始位的時(shí)候就讀取并寄存Data_Byte,以免Data_Byte變化導(dǎo)致數(shù)據(jù)的丟失*/
54 always@(posedge Clk or negedge Rst_n)
55 if(!Rst_n)
56 Data = 8’d0;
57 else if(Bps_Clk & Bps_Clk_Cnt == 4‘d1)
58 Data 《= Data_Byte;
59 else
60 Data 《= Data;
61
62 /*發(fā)送數(shù)據(jù)序列機(jī)*/
63 always@(posedge Clk or negedge Rst_n)
64 if(!Rst_n)
65 Rs232_Tx 《= 1’b1;
66 else begin
67 case(Bps_Clk_Cnt)
68 4‘d1: Rs232_Tx 《= 1’b0;
69 4‘d2: Rs232_Tx 《= Data[0];
70 4’d3: Rs232_Tx 《= Data[1];
71 4‘d4: Rs232_Tx 《= Data[2];
72 4’d5: Rs232_Tx 《= Data[3];
73 4‘d6: Rs232_Tx 《= Data[4];
74 4’d7: Rs232_Tx 《= Data[5];
75 4‘d8: Rs232_Tx 《= Data[6];
76 4’d9: Rs232_Tx 《= Data[7];
77 4‘d10: Rs232_Tx 《= 1’b1;
78 default:Rs232_Tx 《= 1‘b1;
79 endcase
80 end
在UART協(xié)議中,一個(gè)完整的字節(jié)包括一位起始位、8位數(shù)據(jù)位、一位停止位即總共十位數(shù)據(jù),那么,要想完整的實(shí)現(xiàn)這十位數(shù)據(jù)的發(fā)送,就需要11個(gè)波特率時(shí)鐘脈沖,如下所示:
BPS_CLK信號(hào)的第一個(gè)上升沿到來時(shí),字節(jié)發(fā)送模塊開始發(fā)送起始位,接下來的2到9個(gè)上升沿,發(fā)送8個(gè)數(shù)據(jù)位,第10個(gè)上升沿到第11個(gè)上升沿為停止位的發(fā)送。
單個(gè)串口接收模塊中實(shí)現(xiàn)串口數(shù)據(jù)接收的主要代碼如下所示:
025 always @ (posedge Clk or negedge Rst_n)
026 if(!Rst_n) begin
027 Rs232_Rx0 《= 1’b0;
028 Rs232_Rx1 《= 1‘b0;
029 Rs232_Rx2 《= 1’b0;
030 Rs232_Rx3 《= 1‘b0;
031 end
032 else begin
033 Rs232_Rx0 《= Rs232_Rx;
034 Rs232_Rx1 《= Rs232_Rx0;
035 Rs232_Rx2 《= Rs232_Rx1;
036 Rs232_Rx3 《= Rs232_Rx2;
037 end
038
039 wire neg_Rs232_Rx= Rs232_Rx3 & Rs232_Rx2 & ~Rs232_Rx1 & ~Rs232_Rx0;
040
041 assign Byte_En = neg_Rs232_Rx;
042
043 /*----------計(jì)數(shù)采樣時(shí)鐘--------------*/
044 /*9倍波特率采樣時(shí)鐘,故一個(gè)完整的接收過程有90個(gè)波特率時(shí)鐘*/
045 reg[6:0]Sample_Clk_Cnt;
046 always @ (posedge Clk or negedge Rst_n)
047 if(!Rst_n)
048 Sample_Clk_Cnt 《= 7’d0;
049 else if(Sample_Clk)begin
050 if(Sample_Clk_Cnt == 7‘d89)
051 Sample_Clk_Cnt 《= 7’d0;
052 else
053 Sample_Clk_Cnt 《= Sample_Clk_Cnt + 1‘b1;
054 end
055 else
056 Sample_Clk_Cnt 《= Sample_Clk_Cnt;
057
058 reg [1:0]Start_Bit; /*起始位,這里雖然定義,但并未使用該位來判斷接收數(shù)據(jù)的正確性,即默認(rèn)接收都是成功的*/
059 reg [1:0]Stop_Bit; /*停止位,這里雖然定義,但并未使用該位來判斷接收數(shù)據(jù)的正確性,即默認(rèn)接收都是成功的*/
060 reg [1:0] Data_Tmp[7:0];/*此部分較為復(fù)雜,請(qǐng)參看說明文檔中相關(guān)解釋*/
061
062 always @ (posedge Clk or negedge Rst_n)
063 if(!Rst_n)begin
064 Data_Tmp[0] 《= 2’d0;
065 Data_Tmp[1] 《= 2‘d0;
066 Data_Tmp[2] 《= 2’d0;
067 Data_Tmp[3] 《= 2‘d0;
068 Data_Tmp[4] 《= 2’d0;
069 Data_Tmp[5] 《= 2‘d0;
070 Data_Tmp[6] 《= 2’d0;
071 Data_Tmp[7] 《= 2‘d0;
072 Start_Bit 《= 2’d0;
073 Stop_Bit 《= 2‘d0;
074 end
075 else if(Sample_Clk)begin
076 case(Sample_Clk_Cnt)
077 7’d0:
078 begin
079 Data_Tmp[0] 《= 2‘d0;
080 Data_Tmp[1] 《= 2’d0;
081 Data_Tmp[2] 《= 2‘d0;
082 Data_Tmp[3] 《= 2’d0;
083 Data_Tmp[4] 《= 2‘d0;
084 Data_Tmp[5] 《= 2’d0;
085 Data_Tmp[6] 《= 2‘d0;
086 Data_Tmp[7] 《= 2’d0;
087 Start_Bit 《= 2‘d0;
088 Stop_Bit 《= 2’d0;
089 end
090 7‘d3,7’d4,7‘d5: Start_Bit 《= Start_Bit + Rs232_Rx;
091 7’d12,7‘d13,7’d14:Data_Tmp[0] 《= Data_Tmp[0] + Rs232_Rx;
092 7‘d21,7’d22,7‘d23:Data_Tmp[1] 《= Data_Tmp[1] + Rs232_Rx;
093 7’d30,7‘d31,7’d32:Data_Tmp[2] 《= Data_Tmp[2] + Rs232_Rx;
094 7‘d39,7’d40,7‘d41:Data_Tmp[3] 《= Data_Tmp[3] + Rs232_Rx;
095 7’d48,7‘d49,7’d50:Data_Tmp[4] 《= Data_Tmp[4] + Rs232_Rx;
096 7‘d57,7’d58,7‘d59:Data_Tmp[5] 《= Data_Tmp[5] + Rs232_Rx;
097 7’d66,7‘d67,7’d68:Data_Tmp[6] 《= Data_Tmp[6] + Rs232_Rx;
098 7‘d75,7’d76,7‘d77:Data_Tmp[7] 《= Data_Tmp[7] + Rs232_Rx;
099 7’d84,7‘d85,7’d86:Stop_Bit 《= Stop_Bit + Rs232_Rx;
100 default:;
101 endcase
102 end
103 else ;
根據(jù)串口發(fā)送協(xié)議,一個(gè)字節(jié)的數(shù)據(jù)傳輸是以一個(gè)波特率周期的低電平作為起始位的,因此,成功接收UART串口數(shù)據(jù)的核心就是準(zhǔn)確檢測(cè)起始位。由于外部串口發(fā)送過來的數(shù)據(jù)與接收系統(tǒng)不在同一個(gè)時(shí)鐘域,因此不能直接使用該信號(hào)的下降沿來作為檢測(cè)標(biāo)志,我們需要在fpga中,采用專用的邊沿檢測(cè)電路來實(shí)現(xiàn),第25行至37行通過四個(gè)移位寄存器,存儲(chǔ)連續(xù)四個(gè)時(shí)鐘上升沿時(shí)外部發(fā)送數(shù)據(jù)線的狀態(tài),第39行通過比較前兩個(gè)時(shí)鐘時(shí)數(shù)據(jù)線的狀態(tài)與后兩個(gè)時(shí)鐘時(shí)數(shù)據(jù)線的狀態(tài),來得到該數(shù)據(jù)線的準(zhǔn)確下降沿,以此保證起始位的準(zhǔn)確檢測(cè)。
在簡單的串口接收中,我們通常選取一位數(shù)據(jù)的中間時(shí)刻進(jìn)行采樣,因?yàn)榇藭r(shí)數(shù)據(jù)最穩(wěn)定,但是在工業(yè)環(huán)境中,存在著各種干擾,在干擾存在的情況下,如果采用傳統(tǒng)的中間時(shí)刻采樣一次的方式,采樣結(jié)果就有可能受到干擾而出錯(cuò)。為了濾除這種干擾,這里采用多次采樣求概率的方式。如下圖,將一位數(shù)據(jù)平均分成9個(gè)時(shí)間段,對(duì)位于中間的三個(gè)時(shí)間段進(jìn)行采樣。然后對(duì)三個(gè)采樣結(jié)果進(jìn)行統(tǒng)計(jì)判斷,如果某種電平狀態(tài)在三次采樣結(jié)果中占到了兩次及以上,則可以判定此電平狀態(tài)即為正確的數(shù)據(jù)電平。例如4、5、6時(shí)刻采樣結(jié)果分別為1、1、0,那么就取此位解碼結(jié)果為1,否則,若三次采樣結(jié)果為0、1、0,則解碼結(jié)果就為0。
因?yàn)椴蓸右晃恍枰?個(gè)時(shí)鐘上升沿,因此,采樣一個(gè)完整的數(shù)據(jù)需要10*9,即90個(gè)時(shí)鐘上升沿,這里,采樣時(shí)鐘為波特率時(shí)鐘的9倍。產(chǎn)生采樣時(shí)鐘的部分代碼如下所示:
089 /*-------波特率時(shí)鐘生成定時(shí)器--------------*/
090 always@(posedge Clk or negedge Rst_n)
091 if(!Rst_n)
092 Count 《= 10‘d0;
093 else if(BPS_EN == 1’b0)
094 Count 《= 10‘d0;
095 else begin
096 if(Count == BPS_PARA)
097 Count 《= 10’d0;
098 else
099 Count 《= Count + 1‘b1;
100 end
101
102 //=====================================================
103 /*輸出數(shù)據(jù)接收采樣時(shí)鐘*/
104 always @(posedge Clk or negedge Rst_n)
105 if(!Rst_n)
106 Sample_Clk 《= 1’b0;
107 else if(Count== 1)
108 Sample_Clk 《= 1‘b1;
109 else
110 Sample_Clk 《= 1’b0;
這里,BPS_PARA的計(jì)算原理和前面Tx_Bps_Gen模塊中的BPS_PARA的計(jì)算原理一致,不過這里,因?yàn)椴蓸訒r(shí)鐘為波特率時(shí)鐘的9倍,所以,BPS_PARA為Tx_Bps_Gen模塊中的BPS_PARA的1/9。計(jì)算BPS_PARA的相關(guān)代碼如下:
018 parameter system_clk = 50_000_000; /*輸入時(shí)鐘頻率設(shè)定,默認(rèn)50M*/
019
020 /*根據(jù)輸入時(shí)鐘頻率計(jì)算生成各波特率時(shí)分頻計(jì)數(shù)器的計(jì)數(shù)最大值*/
021 localparam bps9600 = system_clk/9600/9 - 1;
022 localparam bps19200 = system_clk/19200/9 - 1;
023 localparam bps38400 = system_clk/38400/9 - 1;
024 localparam bps57600 = system_clk/57600/9 - 1;
025 localparam bps115200 = system_clk/115200/9 - 1;
026 localparam bps230400 = system_clk/230400/9 - 1;
027 localparam bps460800 = system_clk/460800/9 - 1;
028 localparam bps921600 = system_clk/921600/9 - 1;
029
030 reg [31:0]BPS_PARA;/*波特率分頻計(jì)數(shù)器的計(jì)數(shù)最大值*/
031
032 always@(posedge Clk or negedge Rst_n)
033 if(!Rst_n)begin
034 BPS_PARA 《= bps9600; /*復(fù)位時(shí)波特率默認(rèn)為9600bps*/
035 end
036 else begin
037 case(Baud_Set) /*根據(jù)波特率控制信號(hào)選擇不同的波特率計(jì)數(shù)器計(jì)數(shù)最大值*/
038 3‘d0: BPS_PARA 《= bps9600;
039 3’d1: BPS_PARA 《= bps19200;
040 3‘d2: BPS_PARA 《= bps38400;
041 3’d3: BPS_PARA 《= bps57600;
042 3‘d4: BPS_PARA 《= bps115200;
043 3’d5: BPS_PARA 《= bps230400;
044 3‘d6: BPS_PARA 《= bps460800;
045 3’d7: BPS_PARA 《= bps921600;
046 default: BPS_PARA 《= bps9600;/*異常情況,恢復(fù)到9600的波特率*/
047 endcase
048 end
責(zé)任編輯:lq6
-
接收模塊
+關(guān)注
關(guān)注
1文章
19瀏覽量
10440 -
時(shí)鐘信號(hào)
+關(guān)注
關(guān)注
4文章
441瀏覽量
28475 -
分頻計(jì)數(shù)器
+關(guān)注
關(guān)注
0文章
4瀏覽量
7918
原文標(biāo)題:基于ZX-2型FPGA開發(fā)板的串口示波器(二)
文章出處:【微信號(hào):gh_9d70b445f494,微信公眾號(hào):FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論