1.串口发送原理介绍
上图为窗口发送8位数据的原理,可以看出,平常不发送时候数据线处于高电平状态,开始发送时,出现开始标志即低电平,随后传输八bit数据,最后再通过停止位(高电平)来结束数据的发送。
上图为参考小梅哥串口发送模块的设计,信号名称大致不变,接下来主要看代码设计。
2.串口发送代码实现
module uart_tx(
clk,
rst_n,
data_byte,
send_en,
baud_set,
data_tx,
tx_done,
uart_state
);
input clk; //clk 时钟
input rst_n;//reset 复位信号
input send_en;//enable 发送使能信号
input [2:0]baud_set;//baud 波特率设置
input [7:0]data_byte;//待发送的一个字节的数据
output reg data_tx;//发送出去的一个字节数据
output reg tx_done;//end_flag 串口发送完成标志
output reg uart_state;//state 串口状态标志
reg bps_clk;//baud clk 波特率时钟
reg [15:0]div_cnt;// div counter 分频计数
reg [15:0]bps_DR;// div counter max 波特率分频计数的最大值
reg [3:0]bps_cnt;// baud counter 波特率计数,用于发送
reg [7:0]r_data_byte; //暂存的发送数据
localparam start_bit = 1'b0; //开始标志
localparam stop_bit = 1'b1; //结束标志
//uart_state
//本always块主要用于实现串口状态的转变
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)uart_state<=1'b0;//复位,状态为0
else if(send_en)
uart_state<=1'b1;//发送使能,状态为1,表示正在发送
else if(bps_cnt == 4'd10)
uart_state<=1'b0; //发送完成
else
uart_state<=uart_state;
end
//data reg
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
r_data_byte<=8'b0000_0000;
else if(send_en)
r_data_byte<=data_byte;
end
//baud set
//该模块实现波特率的选择,更加利于模块的复用
//计数的最大值通过第一节中的最后一张图得出
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
bps_DR<=16'd5207;
else
begin
case(baud_set)
0: bps_DR<=16'd5207;
1: bps_DR<=16'd2603;
2: bps_DR<=16'd1301;
3: bps_DR<=16'd867;
4: bps_DR<=16'd433;
default: bps_DR<=16'd5207;
endcase
end
end
//counter 分频计数模块
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
div_cnt<=16'd0;
else if(send_en)begin
if(div_cnt == bps_DR)
div_cnt<=16'd0;
else
div_cnt<=div_cnt+1'b1;
end
else
div_cnt<=16'd0;
end
//bps_clk gen 波特率脉冲生成模块
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
bps_clk<=1'd0;
else if (div_cnt == 16'd1)//这里有个细节,大家可以研究一下,为什么是计数到1,不是计数到dps_DR
bps_clk<=1'b1;
else
bps_clk<=1'b0;
end
//bps counter
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
bps_cnt<=4'd0;
else if(bps_cnt ==4'd11)
bps_cnt<=4'd0;
else if(bps_clk)
bps_cnt<=bps_cnt+1'b1;
else
bps_cnt<=bps_cnt;
end
//tx_done 发送结束标志
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
tx_done<=1'd0;
else if(bps_cnt ==4'd11)
tx_done<<=1'd1;
else
tx_done<=1'd0;
end
//uart_tx 实际发送实现,注意这边是低位先发送
always@(posedge clk or negedge rst_n)
if(!rst_n)
data_tx<= 1'b1;
else begin
case(bps_cnt)
0:data_tx<=1'b1;
1:data_tx<=start_bit;
2:data_tx<=r_data_byte[0];
3:data_tx<=r_data_byte[1];
4:data_tx<=r_data_byte[2];
5:data_tx<=r_data_byte[3];
6:data_tx<=r_data_byte[4];
7:data_tx<=r_data_byte[5];
8:data_tx<=r_data_byte[6];
9:data_tx<=r_data_byte[7];
10:data_tx<=stop_bit;
default:data_tx<=1'b1;
endcase
end
endmodule
3.串口发送仿真验证
下面是对上面写的串口发送模块进行的仿真
PS:本文借鉴了小梅哥的教程