一、設計思路
- 5字節(40bit)數據准確的發送給8輸入端口,如何發送?
- 首先要設計一個字節計數器來算數據發送到了哪一個字節
- 設計三個狀態,分別為:空閑狀態、准備發送狀態、發送數據狀態
- 准備發送狀態主要完成的工作是:每到一個字節就使能串口模塊以及加載數據
- 40bit數據的處理方式是:每用完一個字節就右移八位,再將40bit數據的低八位發送給串口模塊
data <= data_tmp[7:0];
data_tmp <= {data_tmp[7:0],data_tmp[DATAN_W-1:8]};
更正:
在實際應用中,data應該先取高位,所以,以上代碼應該改為
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]};
二、頂層代碼
`timescale 1ns / 1ns
module uart_tx_multibyte(
clk , //時鍾
rst_n , //復位
data_n , //要發送的多字節數據
trans_go , //發送使能
uart_tx //串口發送數據
);
parameter IDLE = 3'b001; //空閑狀態
parameter S1 = 3'b010; //准備發送數據狀態,使能,裝載數據
parameter S2 = 3'b100; //發送數據狀態
//CNTBY_N字節數,CNTBY_W字節數計數器的位寬(更改這兩個參數,即可實現串口任意字節發送)
parameter CNTBY_N = 5; //發送數據字節個數
parameter DATAN_W = 8*CNTBY_N; //發送數據個數
parameter CNTBY_W = 3; //發送數據字節計數器位寬,5=3'b101
parameter DATA_W = 8; //一次串行發送的數據個數
parameter STATE_W = 3; //狀態機位寬
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), //復位
.data (data), //發送數據
.send_en (send_en), //發送使能
.baud_set (4'd4), //波特率設置,默認9600bps
.uart_tx (uart_tx), //串口發送
.tx_done (tx_done) //發送完成標志位
);
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
//狀態機
//狀態轉移描述
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
//狀態轉移條件判斷描述
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
//狀態轉移條件描述
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;
//狀態輸出
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
三、串口發送模塊代碼
module my_uart_tx(
clk , //時鍾
rst_n , //復位
data , //發送數據
send_en , //發送使能
baud_set , //波特率設置
uart_tx , //串口發送
tx_done //發送完成標志位
);
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
四、板級測試代碼
由於開發板輸入端口不夠,所以專門寫了一個每10毫秒發送一串數據的代碼
module uart_tx_multibyte_test(
clk ,
rst_n ,
uart_tx
);
parameter DATAN_W = 40;
parameter CNT_D = 500000;
parameter CNT_W = 19;
input clk;
input rst_n;
output uart_tx;
reg trans_go;
reg [DATAN_W-1:0] data_n;
reg [CNT_W-1:0] cnt;
wire end_cnt;
uart_tx_multibyte uart_tx_multibyte(
.clk (clk), //時鍾
.rst_n (rst_n), //復位
.data_n (data_n), //要發送的多字節數據
.trans_go (trans_go), //發送使能
.uart_tx (uart_tx) //串口發送數據
);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 0;
else if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
assign end_cnt = cnt == CNT_D - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_n <= 40'hA987654321;
else if(add_cnt_data)begin
if(end_cnt_data)
data_n <= 40'hA987654321;
else
data_n <= data_n +1'b1;
end
end
assign add_cnt_data = end_cnt;
assign end_cnt_data = add_cnt_data && data_n == 40'hB987654321 - 1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
trans_go <= 0;
else if(end_cnt)
trans_go <= 1;
else
trans_go <= 0;
end
endmodule
五、頂層代碼仿真文件
`timescale 1ns / 1ps
module uart_tx_multibyte_tb();
parameter DATAN_W = 40; //發送數據個數
parameter CYCLE = 20;
parameter RST_TIME = 3;
reg clk;
reg rst_n;
reg [DATAN_W-1:0] data_n;
reg trans_go;
wire uart_tx;
uart_tx_multibyte uart_tx_multibyte(
clk , //時鍾
rst_n , //復位
data_n , //要發送的多字節數據
trans_go , //發送使能
uart_tx //串口發送數據
);
initial begin
clk = 1;
forever begin
#(CYCLE/2);
clk = ~clk;
end
end
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(RST_TIME*CYCLE);
rst_n = 1;
end
initial begin
#1;
data_n = 0;
#(10*CYCLE);
data_n = 40'hA987654321;
#5400000;
data_n = 40'h123456789A;
end
initial begin
#1;
trans_go = 0;
#(10*CYCLE);
trans_go = 1;
#(CYCLE);
trans_go = 0;
#5400000;
trans_go = 1;
#(CYCLE);
trans_go = 0;
end
initial begin
#12000000;
$stop;
end
endmodule
六、測試代碼仿真文件
`timescale 1ns / 1ns
module uart_tx_multibyte_test_tb();
reg clk;
reg rst_n;
wire uart_tx;
uart_tx_multibyte_test uart_tx_multibyte_test(
clk ,
rst_n ,
uart_tx
);
parameter CYCLE = 20;
parameter RST_TIME = 3;
initial begin
clk = 1;
forever begin
#(CYCLE/2)
clk = ~clk;
end
end
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(RST_TIME * CYCLE);
rst_n = 1;
end
initial begin
#15203500
$stop;
end
endmodule
七、原工程文件
[https://github.com/CNLuzt/FPGA_CODE/tree/master/uart_tx_multibyte]