【FPGA】串口收發的verilog實現


2020.8.1

一、嘰呱嘰呱

作為一名電子專業的小白菜,在大二的暑假終於開博客啦。目前開博客寫博文主要是為了在學習的過程中有所輸出+能和他人多多交流,也算是自己學習開發的一個記錄。

嘛,這個暑假到現在學了一丟丟基礎的verilog知識,短期目標是好好把FPGA學下去。

那其他廢話也不多說了,本文以串口收發的verilog代碼實現為主(基本復現黑金AX301的串口代碼),輔以一些必要的原理。

二、實驗原理

1.異步串口通信協議

串口傳輸時序圖:

 

從波形來看,在沒有數據傳輸時數據線保持高電平。當一次下降沿事件發生時,我們認為開始一次數據傳輸。代碼中默認一位起始位,八位數據位,無校驗位。

 

三、程序設計

1.串口接收模塊:

串口接收采用狀態機,如下圖:

 

 

 

 S_IDLE:空閑狀態,表明此時無數據傳輸,當遇到輸入數據下降沿后轉換狀態至S_START;

S_START:起始狀態,此時數據線上為起始位的傳輸,當一位起始位傳輸結束后轉入S_REC_BYTE;

S_REC_BYTE:接收數據狀態,此時數據線接收串行輸入的數據,接收完畢后轉入S_STOP;

S_STOP:停止狀態,此時數據接收完畢,在等待半位數據的時間后(以免錯過下一個數據的起始位判斷),轉入S_IDLE(代碼實現中去掉了S_DATA狀態)。

 

2.串口發送模塊:

 

與接收模塊類似,

 S_IDLE:空閑狀態,表明此時無數據傳輸,當遇到輸入數據下降沿后轉換狀態至S_START;

S_START:起始狀態,此時數據線上為起始位的傳輸,當一位起始位傳輸結束后轉入S_REC_BYTE;

S_SEND_BYTE:發送數據狀態,此時數據線發送輸入的並行八位數據,接收完畢后轉入S_STOP;

S_STOP:停止狀態,此時數據接收完畢,在等待一位數據的時間后,轉入S_IDLE。

 

3.實現功能

其他細節原理不多贅述,我的代碼基本與黑金開發板AX301的串口代碼一致,只是自己復現了一遍加了些自己理解的注釋。

實現串口調試助手發送數據至FPGA開發板,FPGA接收數據后返回對應數據,可在串口調試助手上查看。

 

4.具體代碼:

①串口接收模塊

  1 module uart_rx
  2 #(
  3     parameter                 CLK_FRE = 50, //系統時鍾頻率 50(MHz)
  4      parameter                 UART_BPS = 115200 //波特率為115200
  5   )
  6 (
  7    input                       clk,
  8     input                       rst_n,
  9     input                       rx_in,
 10     output reg[7:0]        rx_out,
 11     output reg                rx_data_valid
 12 );
 13 
 14 localparam              CYCLE            = CLK_FRE * 1000000 / UART_BPS; //傳輸一位數據需幾個時鍾
 15 localparam                     S_IDLE         = 3'd1; //輸入信號空閑狀態
 16 localparam                     S_START         = 3'd2; //輸入信號檢測到下降沿后,開始接收起始位
 17 localparam                    S_REC_BYTE     = 3'd3; //接收到一位起始位,開始接受數據
 18 localparam                     S_STOP         = 3'd4; //八位數據接收完畢
 19 
 20 
 21 reg                             rx_in_0;  //接收的數據延時一個時鍾
 22 reg                             rx_in_1; //接收的數據延時兩個時鍾
 23 wire                            rx_negedge; //rx_in下降沿判斷
 24 reg [2:0]                    state;  //當前輸入信號狀態
 25 reg [2:0]                    next_state; //輸入信號下一個狀態
 26 reg [8:0]                    cycle_cnt; //傳輸一位數據時,時鍾計數
 27 reg [3:0]                     bit_cnt; //接收數據時,每位數據計數
 28 reg [7:0]                    rx_bits; //接收數據的臨時存儲
 29 
 30 
 31 //判斷輸入信號rx_in是否遇到下降沿
 32 assign rx_negedge = rx_in_1 && (!rx_in_0); //輸入信號rx_in下降沿邊沿檢測
 33 
 34 always@(posedge clk or negedge rst_n)
 35 begin
 36    if(rst_n == 1'b0)
 37     begin
 38        rx_in_0 <= 1'b0;
 39         rx_in_1 <= 1'b0;
 40    end
 41     else
 42     begin
 43        rx_in_0 <= rx_in;
 44         rx_in_1 <= rx_in_0;
 45     end
 46 end
 47 
 48 //輸入信號狀態轉換
 49 always@(posedge clk or negedge rst_n)
 50 begin
 51     if(rst_n == 1'b0)
 52         state <= S_IDLE;
 53     else
 54         state <= next_state;
 55 end
 56 
 57 //輸入信號狀態機
 58 always@(*)
 59 begin
 60     case(state)
 61     S_IDLE:
 62         if(rx_negedge == 1'b1) //檢測輸入信號下降沿
 63             next_state = S_START;
 64         else
 65            next_state = S_IDLE;
 66     S_START:
 67        if(cycle_cnt == CYCLE - 1) //等到一位起始位后
 68             next_state = S_REC_BYTE;
 69         else
 70            next_state = S_START;
 71     S_REC_BYTE:
 72        if(cycle_cnt == CYCLE - 1 && bit_cnt == 3'd7) //接收完八位數據后轉換狀態
 73            next_state = S_STOP;
 74       else
 75             next_state = S_REC_BYTE;
 76     S_STOP:
 77         if(cycle_cnt == CYCLE/2 -1) //等待半位以免錯過下一個數據的起始位判斷
 78             next_state = S_IDLE;
 79         else
 80             next_state = S_STOP;        
 81     default:
 82         next_state = S_IDLE;
 83     endcase
 84 end
 85 
 86 //接收一位數據時,時鍾計數
 87 always@(posedge clk or negedge rst_n)
 88 begin
 89     if(rst_n == 1'b0)
 90         cycle_cnt <= 9'b0;
 91     else if(next_state != state || (state == S_REC_BYTE && cycle_cnt == CYCLE - 1)) //每當狀態轉變或接收數據時接收完一位數據,需要重新計數
 92        cycle_cnt <= 9'b0; 
 93     else 
 94        cycle_cnt <= cycle_cnt + 9'b1;
 95 end
 96 
 97 //接收數據時,判斷接收了幾位數據的計數
 98 always@(posedge clk or negedge rst_n)
 99 begin
100     if(rst_n == 1'b0)
101         bit_cnt <= 3'b0;
102     else if(state == S_REC_BYTE) //當計數一位的時鍾完成,bit_cnt + 1
103     begin
104        if(cycle_cnt == CYCLE - 1)
105             bit_cnt <= bit_cnt + 3'b1;
106         else
107             bit_cnt <= bit_cnt;
108     end
109     else
110         bit_cnt <= 3'b0;
111 end
112 
113 //接收數據臨時存儲,串轉並
114 always@(posedge clk or negedge rst_n)
115 begin
116     if(rst_n == 1'b0)
117         rx_bits <= 8'b0;
118     else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 -1)
119         rx_bits[bit_cnt] <= rx_in;
120     else
121         rx_bits <= rx_bits;
122 end
123 
124 //輸出接收到的數據
125 always@(posedge clk or negedge rst_n)
126 begin
127     if(rst_n == 1'b0)
128         rx_out <= 8'b0;
129     else if(state == S_STOP && next_state != state)
130         rx_out <= rx_bits;
131 end
132 
133 //判斷接收到的數據是否有效    
134 always@(posedge clk or negedge rst_n)
135 begin
136     if(rst_n == 1'b0)
137         rx_data_valid <= 1'b0;
138     else if(state == S_STOP && next_state!=state)
139         rx_data_valid <= 1'b1;
140     else if(state == S_IDLE)
141         rx_data_valid <= 1'b0;
142 end    
143     
144 endmodule    
View Code

 

②串口發送模塊

  1 module uart_tx
  2 #( parameter CLK_FRE = 50,
  3    parameter UART_BPS = 115200
  4 )
  5 (  input             clk,
  6     input             rst_n,
  7     input [7:0]        tx_data_in,
  8     input             tx_data_valid,
  9     output             tx_data_out,
 10     output reg        tx_data_ready
 11 );
 12 
 13 localparam             CYCLE = CLK_FRE * 1000000 / UART_BPS; //傳輸一位數據需要幾個時鍾
 14 localparam             S_IDLE = 3'd1;
 15 localparam             S_START = 3'd2;
 16 localparam             S_SEND_BYTE = 3'd3;
 17 localparam             S_STOP = 3'd4;
 18 
 19 reg[2:0]             state;
 20 reg[2:0]                next_state;
 21 reg[8:0]                cycle_cnt;
 22 reg[3:0]                bit_cnt;
 23 reg                    tx_data_reg; //串行輸出數據
 24 reg[7:0]                tx_data_latch; //輸入的要發送的數據鎖存
 25 
 26 //發送狀態轉換
 27 always@(posedge clk or negedge rst_n)
 28 begin
 29     if(rst_n == 1'b0)
 30         state <= S_IDLE;
 31     else
 32         state <= next_state;
 33 end
 34 
 35 //發送狀態機
 36 always@(*)
 37 begin
 38     case(state)
 39         S_IDLE:
 40             if(tx_data_valid == 1'b1) //發送請求有效時
 41                 next_state <= S_START;
 42             else
 43                 next_state <= S_IDLE;
 44         S_START:
 45            if(cycle_cnt == CYCLE - 1) //發送完一位起始位后
 46                 next_state <= S_SEND_BYTE;
 47             else
 48                 next_state <= S_START;
 49         S_SEND_BYTE:
 50             if(bit_cnt == 3'd7 && cycle_cnt == CYCLE - 1) //發送完八位數據后
 51                 next_state <= S_STOP;
 52             else
 53                 next_state <= S_SEND_BYTE;
 54         S_STOP:
 55             if(cycle_cnt == CYCLE - 1) //等一位數據的時間轉到空閑狀態
 56                 next_state <= S_IDLE;
 57             else
 58                 next_state <= S_STOP;
 59         default:
 60             next_state <= S_IDLE;            
 61     endcase
 62 end
 63 
 64 //發送一位數據時,時鍾計數
 65 always@(posedge clk or negedge rst_n)
 66 begin
 67     if(rst_n == 1'b0)
 68         cycle_cnt <= 0;
 69     else if(next_state != state ||(state == S_SEND_BYTE && cycle_cnt == CYCLE - 1))
 70         cycle_cnt <=0;
 71     else
 72         cycle_cnt <= cycle_cnt + 9'b1;
 73 end
 74 
 75 //發送數據時,判斷發送了幾位數據的計數
 76 always@(posedge clk or negedge rst_n)
 77 begin    
 78     if(rst_n == 1'b0)
 79         bit_cnt <= 0;
 80     else if(state == S_SEND_BYTE)
 81     begin
 82         if(cycle_cnt == CYCLE - 1)
 83             bit_cnt <= bit_cnt + 3'b1;
 84         else
 85             bit_cnt <= bit_cnt;
 86     end
 87     else
 88         bit_cnt <= 0;
 89 end
 90 
 91 //輸入數據鎖存
 92 always@(posedge clk or negedge rst_n)
 93 begin
 94     if(rst_n == 1'b0)
 95         tx_data_latch <= 8'b0;
 96     else if(state == S_IDLE && tx_data_valid == 1'b1) //當輸入數據線處於空閑狀態且輸入數據有效時
 97         tx_data_latch <= tx_data_in;
 98 end
 99 
100 //輸出數據,並入轉串出
101 always@(posedge clk or negedge rst_n)
102 begin
103     if(rst_n == 1'b0)
104         tx_data_reg <= 1'b1; 
105     else 
106         case(state)
107             S_IDLE,S_STOP:
108                 tx_data_reg <= 1'b1;
109             S_START:
110                 tx_data_reg <= 1'b0;
111             S_SEND_BYTE:
112                tx_data_reg <= tx_data_latch[bit_cnt];
113             default:
114                 tx_data_reg <= 1'b1;
115         endcase
116 end
117 assign tx_data_out = tx_data_reg;
118 
119 //輸出數據完畢標志的檢驗
120 always@(posedge clk or negedge rst_n)
121 begin
122     if(rst_n == 1'b0)
123         tx_data_ready <= 1'b0;
124     else if(state == S_STOP && cycle_cnt == CYCLE - 1) //僅在輸出數據完畢后的一段時間,該標志置1
125         tx_data_ready <= 1'b1;
126     else if(state == S_IDLE) //我的理解是:使輸出數據完畢的標志的時間保持一段時間,以免數據的錯發漏發
127         if(tx_data_valid == 1'b1)
128             tx_data_ready <= 1'b0;
129         else
130             tx_data_ready <= 1'b1;
131 end
132 
133 endmodule
View Code

 

③頂層模塊

 1 module uart_top(
 2     input clk,
 3     input rst_n,
 4     input uart_rx,
 5     output uart_tx
 6 );
 7 
 8 parameter CLK_FRE = 50;
 9 parameter BAUD_RATE = 115200;
10 localparam IDLE = 2'd1;
11 localparam WAIT = 2'd2;
12 
13 
14 wire            rx_data_valid;
15 wire[7:0]    rx_data_out;
16 reg[7:0]        tx_data_in;
17 reg            tx_data_valid;
18 wire            tx_data_ready;    
19 reg[1:0]        state;        
20 reg[8:0]        wait_cnt;
21 
22 always@(posedge clk or negedge rst_n)
23 begin
24     if(rst_n == 1'b0)
25     begin
26         wait_cnt <= 9'b0;
27         state <= IDLE;
28         tx_data_valid <= 1'b0;
29         tx_data_in <= 8'b0;
30     end
31     else
32         case(state)
33             IDLE:
34                 state <= WAIT;
35             WAIT:
36             begin
37                 if(rx_data_valid == 1'b1) //當數據接收完畢后,發送有效位置1
38                 begin
39                     tx_data_valid = 1'b1;
40                     tx_data_in <= rx_data_out;
41                 end
42                 else if(tx_data_valid == 1'b1 && tx_data_ready == 1'b1) //如果數據發送完成,發送有效位置0
43                     tx_data_valid <= 1'b0;
44                 else
45                     wait_cnt <= wait_cnt + 1;
46                 if(wait_cnt >= CLK_FRE * 1000000) //如果等待超過一秒
47                 begin
48                     state <= IDLE;
49                     wait_cnt <= 9'b0;
50                 end
51             end
52             default:
53                 state <= IDLE;
54         endcase
55                 
56         
57 end
58 
59 uart_rx
60 #( 
61     .CLK_FRE             (CLK_FRE),
62     .UART_BPS             (BAUD_RATE)
63 )uart_rx_inst
64 (    
65     .clk                    (clk),
66     .rst_n                (rst_n),
67     .rx_in                (uart_rx),
68     .rx_out                (rx_data_out),
69     .rx_data_valid     (rx_data_valid)
70 );
71 
72 uart_tx
73 #( 
74     .CLK_FRE             (CLK_FRE),
75     .UART_BPS             (BAUD_RATE)
76 )uart_tx_inst
77 (    
78     .clk                    (clk),
79     .rst_n                (rst_n),    
80     .tx_data_in         (tx_data_in),
81     .tx_data_valid        (tx_data_valid),
82     .tx_data_out        (uart_tx),
83     .tx_data_ready        (tx_data_ready)
84 );
85 
86 
87 
88 endmodule
View Code

 

5.調試結果

 

 成功!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM