目录
概述:
通过修改代码中的参数和输入数据位宽,可以实现在不同波特率下发送8的倍数(如8、16、24、32等)位数据。实现方法是将数据拆分成8bit一组,采用状态机结合并转串的方式实现分组发送。
uart通信协议的相关知识我就不介绍了,在网上都能找到非常详细的讲解。首先确定串口通信的数据格式及波特率,文中所采用的帧格式如下图所示。起始位为1位,数据位为8位,停止位为1位。无奇偶校验位。系统时钟50MHz,波特率为9600bps。
设计思路
波特率计数器:
以文中的50MHz系统时钟,9600波特率为例,发送1bit时间为1/9600(s),时钟周期为20ns,那么发送1bit需要的系统周期为(1/9600)/ 20ns ≈ 5208(个)。在发送过程中使用一个计数器计数,计数区间为(0~5208-1),这样的区间一共10个(一个字节需要发送10个bit)。
bit计数器:
需要一个计数器对发送的bit数计数(每当上一个计数器计数到5207则表示发送完了一个bit),计数区间(0~10)。以发送32bit为例,在上电初期bit计数器从0(IDLE状态)开始累加计数,当第一帧数据发送完毕后,bit计数器直接跳转到1(START状态)开始计数,直到4帧数据都发送完毕后,清零。
帧计数器:
初始上电时,在START状态由0变1,指示第一帧数据开始发送,每当状态跳转到STOP状态时,加一指示即将发送下一帧数据。当所有数据都发送完毕时,清零。
Verilog代码
uart_tx:
module uart_tx(
input wire sys_clk , //时钟
input wire sys_rst_n , //复位,低电平有效
input wire data_vld , //输入数据有效标志
input wire [31:0] uart_tx_i , //输入并行数据
output reg uart_tx_o //输出的串行一位数据
);
reg work_en; //指示发送机状态
reg [12:0] baud_cnt; //0~baud_cnt_max-1
reg [3:0] bit_cnt; //0~9 0:开始位 1~8:数据位 9:停止位
reg [2:0] byte_cnt; //分成4段发送 0~4:有效的是1~4
reg bit_flag; //在baud_cnt记到3时发送数据
reg [7:0] data_temp; //分段发送32bit数据
reg [7:0] data; //移位实现并串转换
reg start;
reg send;
reg [3:0] state;
reg [3:0] nstate;
wire end_work;
localparam IDLE = 4'b0001,
START = 4'b0010,
SEND = 4'b0100,
STOP = 4'b1000;
parameter BAUD_CNT_MAX = 13'd5208; //50MHz,9600bps
parameter BYTE_MAX = 3'd4;
parameter BIT_CNT_MAX = 4'd11;
//指示发送机状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
work_en <= 1'b0;
end
else if(data_vld) begin
work_en <= 1'b1;
end
else if(end_work) begin
work_en <= 1'b0;
end
end
//发送机结束状态标志
assign end_work = ((byte_cnt == BYTE_MAX) && bit_flag && (bit_cnt == 4'd10)) ? 1'b1 : 1'b0;
//当baud_cnt从0记到最大值时,表明可以发送1bit数据
//计数范围0~BAUD_CNT_MAX-1
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
baud_cnt <= 13'b0;
end
else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) begin
baud_cnt <= 13'b0;
end
else if(work_en == 1'b1) begin
baud_cnt <= baud_cnt + 1'b1;
end
end
//在baud_cnt记到3时发送数据
//发送数据标志
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
bit_flag <= 1'b0;
end
else if(baud_cnt == 13'd3) begin
bit_flag <= 1'b1;
end
else begin
bit_flag <= 1'b0;
end
end
//记录bit数 计数范围1~10,1是开始位,2~9是数据位,10是停止位
//注意优先级
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
bit_cnt <= 4'b0;
end
else if(bit_flag && bit_cnt == BIT_CNT_MAX - 1'b1) begin
case(byte_cnt)
BYTE_MAX: bit_cnt <= 4'b0;
default: bit_cnt <= 4'd1;
endcase
end
else if(work_en && bit_flag) begin
bit_cnt <= bit_cnt + 1'b1;
end
end
//字节计数,0~4
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
byte_cnt <= 3'd0;
end
else if(end_work) begin
byte_cnt <= 3'd0;
end
else if(state == IDLE && bit_flag) begin
byte_cnt <= 3'b1;
end
else if(state == STOP && bit_flag) begin
byte_cnt <= byte_cnt + 1'b1;
end
end
//状态机第一段
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
state <= IDLE;
end
else begin
state <= nstate;
end
end
//状态机第二段
always @(*) begin
case(state)
IDLE: begin
if(work_en && bit_flag) begin
nstate = START;
end
else begin
nstate = IDLE;
end
end
START: begin
if(bit_flag && bit_cnt == 4'd1) begin
nstate = SEND;
end
else begin
nstate = START;
end
end
SEND: begin
if(bit_flag && bit_cnt == 4'd9) begin
nstate = STOP;
end
else begin
nstate = SEND;
end
end
STOP: begin
if(bit_flag && bit_cnt == 4'd10) begin
if(byte_cnt < BYTE_MAX) begin
nstate = START;
end
else begin
nstate = IDLE;
end
end
else begin
nstate = STOP;
end
end
default: nstate = IDLE;
endcase
end
//状态机第三段
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
uart_tx_o <= 1'b1;
end
else begin
case(nstate)
IDLE: begin
uart_tx_o <= 1'b1;
end
START: begin
uart_tx_o <= 1'b0;
end
SEND: begin
uart_tx_o <= data[0];
end
STOP: begin
uart_tx_o <= 1'b1;
end
endcase
end
end
//分段赋值
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
data_temp <= 8'b0;
end
else begin
case(byte_cnt)
3'd1: data_temp <= uart_tx_i[7:0];
3'd2: data_temp <= uart_tx_i[15:8];
3'd3: data_temp <= uart_tx_i[23:16];
3'd4: data_temp <= uart_tx_i[31:24];
endcase
end
end
//状态机开始状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
start <= 1'b0;
end
else if(nstate == START) begin
start <= 1'b1;
end
else begin
start <= 1'b0;
end
end
//状态机发送数据状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
send <= 1'b0;
end
else if(nstate == SEND) begin
send <= 1'b1;
end
else begin
send <= 1'b0;
end
end
//数据在开始状态被赋值,在发送状态进行循环右移位
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
data <= 8'b0;
end
else if(start) begin
data <= data_temp;
end
else if(send && bit_flag) begin
data <= {data[0],data[7:1]};
end
end
endmodule
testbench:
`timescale 1ns/1ns
module tb_uart_tx();
reg sys_clk ;
reg sys_rst_n ;
reg data_vld ;
reg [31:0] uart_tx_i ;
wire uart_tx_o ;
always #10 sys_clk = ~sys_clk;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20;
sys_rst_n <= 1'b1;
end
//模拟发送7次数据,分别为0~7
initial begin
uart_tx_i <= 32'b0;
data_vld <= 1'b0;
#200
//发送数据0
uart_tx_i <= 32'h11223344;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
//每发送1bit数据需要5208个时钟周期,一帧8位数据为10bit
//需要发送32位数据,对应发送4帧
//所以需要数据延时(5208*20*60)后再产生下一个数据
#(5208*20*60);
//发送数据1
uart_tx_i <= 32'hddccbbaa;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
/* #(5208*20*10);
//发送数据2
uart_tx_i <= 8'd2;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
#(5208*20*10);
//发送数据3
uart_tx_i <= 8'd3;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
#(5208*20*10);
//发送数据4
uart_tx_i <= 8'd4;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
#(5208*20*10);
//发送数据5
uart_tx_i <= 8'd5;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
#(5208*20*10);
//发送数据6
uart_tx_i <= 8'd6;
data_vld <= 1'b1;
#20
data_vld <= 1'b0;
#(5208*20*10);
//发送数据7
uart_tx_i <= 8'd7;
data_vld <= 1'b1;
#20
data_vld <= 1'b0; */
end
//\* Instantiation \//
//------------------------uart_rx_inst------------------------
uart_tx uart_tx_inst(
.sys_clk (sys_clk ), //input sys_clk
.sys_rst_n (sys_rst_n ), //input sys_rst_n
.uart_tx_i (uart_tx_i ), //output [7:0] uart_tx_i
.data_vld (data_vld ), //output data_vld
.uart_tx_o (uart_tx_o ) //input tx
);
endmodule
wave(部分截图):
代码我已经全部仿真上板试验过了,都是没有问题的,文章中如果有遗漏或者错误的地方,还请小伙伴们多多指出。