FPGA——任意位寬的UART串口通信發(fā)送模塊實(shí)現(xiàn)
掃描二維碼
隨時隨地手機(jī)看文章
概
在實(shí)際工程應(yīng)用中,常常需要通過串口一次性傳輸多字節(jié)的數(shù)據(jù)。例如,以太網(wǎng)模塊可能會發(fā)送經(jīng)過8字節(jié)對齊的一個時鐘周期的64位數(shù)據(jù),然后通過串口傳遞給下游的傳感器設(shè)備。類似地,DDR模塊可能以32字節(jié)對齊的形式發(fā)送一個時鐘周期的256位數(shù)據(jù),通過串口傳輸給上位機(jī)。如果每次都需要通過修改Verilog代碼的方式將數(shù)據(jù)拆分成8位的塊再發(fā)送給串口,將會顯得非常繁瑣。本文通過引入只有三種狀態(tài)的狀態(tài)機(jī),只需更改參數(shù)就可實(shí)現(xiàn)對任意字節(jié)位寬的串口數(shù)據(jù)發(fā)送。這種方法使得在不同情境下輕松適應(yīng)不同的數(shù)據(jù)格式,極大地提高了系統(tǒng)的靈活性和可維護(hù)性。

設(shè)計思路
首先,需要設(shè)計一個字節(jié)計數(shù)器,用于追蹤數(shù)據(jù)發(fā)送到了哪一個字節(jié)。
接著,設(shè)計三個狀態(tài):空閑狀態(tài)、準(zhǔn)備發(fā)送狀態(tài)和發(fā)送數(shù)據(jù)狀態(tài)。
在準(zhǔn)備發(fā)送狀態(tài)下,主要完成兩項(xiàng)任務(wù):一是每到達(dá)一個字節(jié)就啟用串口模塊,二是加載數(shù)據(jù)以準(zhǔn)備發(fā)送。
對于N字節(jié)數(shù)據(jù)的處理方式:每使用完一個字節(jié)后,執(zhí)行右移八位的操作,然后將N位數(shù)據(jù)的低八位發(fā)送給串口模塊(其中N為整數(shù))。這確保了數(shù)據(jù)的有效傳輸,并適應(yīng)了不同字節(jié)長度的情況。
data先從高位字節(jié)發(fā)送:
data <= data_tmp[DATAN_W-1:DATAN_W-8]; data_tmp <= {data_tmp[DATAN_W-9:0],data_tmp[DATAN_W-1:DATAN_W-8]};
data先從低位字節(jié)發(fā)送:
data <= data_tmp[7:0]; data_tmp <= {data_tmp[7:0],data_tmp[DATAN_W-1:8]};
多字節(jié)發(fā)送模塊
該模塊實(shí)現(xiàn)了多數(shù)據(jù)傳輸?shù)幕A(chǔ)功能,當(dāng)然,可以擴(kuò)展一個busy信號,當(dāng)狀態(tài)機(jī)未工作完成的時候,反壓上游模塊,告訴上游模塊不能再發(fā)。busy信號可以可以通過狀態(tài)機(jī)的IDLE狀態(tài)來生成。
`timescale 1ns / 1ns module uart_tx_multibyte( clk , //時鐘 rst_n , //復(fù)位 data_n , //要發(fā)送的多字節(jié)數(shù)據(jù) trans_go , //發(fā)送使能 uart_tx //串口發(fā)送數(shù)據(jù) ); parameter IDLE = 3'b001; //空閑狀態(tài) parameter S1 = 3'b010; //準(zhǔn)備發(fā)送數(shù)據(jù)狀態(tài),使能,裝載數(shù)據(jù) parameter S2 = 3'b100; //發(fā)送數(shù)據(jù)狀態(tài) //CNTBY_N字節(jié)數(shù),CNTBY_W字節(jié)數(shù)計數(shù)器的位寬(更改這兩個參數(shù),即可實(shí)現(xiàn)串口任意字節(jié)發(fā)送) parameter CNTBY_N = 5; //發(fā)送數(shù)據(jù)字節(jié)個數(shù) parameter DATAN_W = 8*CNTBY_N; //發(fā)送數(shù)據(jù)個數(shù) parameter CNTBY_W = 3; //發(fā)送數(shù)據(jù)字節(jié)計數(shù)器位寬,5=3'b101 parameter DATA_W = 8; //一次串行發(fā)送的數(shù)據(jù)個數(shù) parameter STATE_W = 3; //狀態(tài)機(jī)位寬 input clk; input rst_n; input [DATAN_W-1:0] data_n; input trans_go; output uart_tx; wire uart_tx; reg [CNTBY_W-1:0] cnt_bytes; wire add_cnt_bytes; wire end_cnt_bytes; reg cnt_bytes_flag; wire tx_done; reg [DATA_W-1:0] data; reg send_en; reg [STATE_W-1:0] state_c; reg [STATE_W-1:0] state_n; wire IDLE2S1_start; wire S12S2_start; wire S22S1_start ; wire S22IDLE_start ; reg [DATAN_W-1:0] data_tmp; my_uart_tx my_uart_tx( .clk (clk), //時鐘 .rst_n (rst_n), //復(fù)位 .data (data), //發(fā)送數(shù)據(jù) .send_en (send_en), //發(fā)送使能 .baud_set (4'd4), //波特率設(shè)置,默認(rèn)9600bps .uart_tx (uart_tx), //串口發(fā)送 .tx_done (tx_done) //發(fā)送完成標(biāo)志位 ); always @(posedge clk or negedge rst_n)begin if(!rst_n) cnt_bytes <= 0; else if(add_cnt_bytes)begin if(end_cnt_bytes) cnt_bytes <= 0; else cnt_bytes <= cnt_bytes + 1'b1; end end assign add_cnt_bytes = cnt_bytes_flag; assign end_cnt_bytes = add_cnt_bytes && cnt_bytes == CNTBY_N - 1; always @(*)begin if(tx_done) cnt_bytes_flag = 1; else cnt_bytes_flag = 0; end //狀態(tài)機(jī) //狀態(tài)轉(zhuǎn)移描述 always @(posedge clk or negedge rst_n)begin if(!rst_n) state_c <= IDLE; else state_c <= state_n; end //狀態(tài)轉(zhuǎn)移條件判斷描述 always @(*)begin case(state_c) IDLE:begin if(IDLE2S1_start) state_n = S1; else state_n = state_c; end S1:begin if(S12S2_start) state_n = S2; else state_n = state_c; end S2:begin if(S22S1_start) state_n = S1; else if(S22IDLE_start) state_n = IDLE; else state_n = state_c; end default: state_n = IDLE; endcase end //狀態(tài)轉(zhuǎn)移條件描述 assign IDLE2S1_start = state_c==IDLE && trans_go; assign S12S2_start = state_c==S1 && send_en; assign S22S1_start = state_c==S2 && tx_done && !end_cnt_bytes; assign S22IDLE_start = state_c==S2 && end_cnt_bytes; //狀態(tài)輸出 always @(posedge clk or negedge rst_n)begin if(!rst_n) send_en <= 0; else if(state_c == S1) send_en <= 1; else send_en <= 0; end always @(posedge clk or negedge rst_n)begin if(!rst_n)begin data <= 0; data_tmp <= 0; end else if(state_c == IDLE)begin data <= 0; data_tmp <= data_n; end else if(state_c == S1 && !send_en)begin data <= data_tmp[DATAN_W-1:DATAN_W-8]; data_tmp <= {data_tmp[DATAN_W-9:0],data_tmp[DATAN_W-1:DATAN_W-8]}; end end endmodule
UART發(fā)送模塊
module my_uart_tx( clk , //時鐘 rst_n , //復(fù)位 data , //發(fā)送數(shù)據(jù) send_en , //發(fā)送使能 baud_set , //波特率設(shè)置 uart_tx , //串口發(fā)送 tx_done //發(fā)送完成標(biāo)志位 ); parameter DATA_W = 8; parameter SET_W = 3; parameter BYTE_D = 10; parameter BAUT_W = 17; parameter BYTE_W = 4; input clk; input rst_n; input [DATA_W-1:0] data; input send_en; input [SET_W-1:0] baud_set; output uart_tx; output tx_done; reg uart_tx; reg tx_done; reg [BAUT_W-1:0] cnt_baud; reg [BAUT_W-1:0] baud; wire add_cnt_baud; wire end_cnt_baud; reg [BYTE_W-1:0] cnt_byte; wire add_cnt_byte; wire end_cnt_byte; reg [BYTE_D-1:0] uart_data; reg cnt_baud_flag; always @(posedge clk or negedge rst_n)begin if(!rst_n) cnt_baud <= 0; else if(add_cnt_baud)begin if(end_cnt_baud) cnt_baud <= 0; else cnt_baud <= cnt_baud + 1'b1; end end assign add_cnt_baud = cnt_baud_flag; assign end_cnt_baud = add_cnt_baud && cnt_baud == baud - 1; always @(posedge clk or negedge rst_n)begin if(!rst_n) cnt_byte <= 0; else if(add_cnt_byte)begin if(end_cnt_byte) cnt_byte <= 0; else cnt_byte <= cnt_byte + 1'b1; end end assign add_cnt_byte = end_cnt_baud; assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_D - 1; always @(posedge clk or negedge rst_n)begin if(!rst_n) uart_tx <= 1; else if(cnt_baud == 0 && add_cnt_baud) uart_tx <= uart_data[cnt_byte]; end always @(posedge clk or negedge rst_n)begin if(!rst_n) uart_data <= 10'b0; else if(!cnt_baud_flag && send_en) uart_data <= {1'b1,data,1'b0}; end always @(posedge clk or negedge rst_n)begin if(!rst_n) cnt_baud_flag <= 0; else if(send_en) cnt_baud_flag <= 1; else if(end_cnt_byte) cnt_baud_flag <= 0; end always @(*)begin case(baud_set) 3'd0:baud = 17'd83333;//600bps 3'd1:baud = 17'd41666;//1200bps 3'd2:baud = 17'd20833;//2400bps 3'd3:baud = 17'd10416;//4800bps 3'd4:baud = 17'd5208 ;//9600bps 3'd5:baud = 17'd2604 ;//19200bps 3'd6:baud = 17'd1302 ;//38400bps 3'd7:baud = 17'd868 ;//57600bps default: baud = 0; endcase end always @(posedge clk or negedge rst_n)begin if(!rst_n) tx_done <= 0; else if(end_cnt_byte) tx_done <= 1; else tx_done <= 0; end endmodule





