代码源于小梅哥FPGA视频教程,本人仅参考记录学习
起始位+数据+停止位一共十位数据
baud_set | 波特率 | 波特率周期 | 波特率分频计数值 | System_clk_period = 20计数值 |
---|---|---|---|---|
0 | 9600 | 104167ns | 104167/ System_clk_period | 5208-1 |
1 | 19200 | 52083ns | 52083/ System_clk_period | 2604-1 |
2 | 38400 | 26041ns | 26041/ System_clk_period | 1302-1 |
3 | 57600 | 17361ns | 17361/ System_clk_period | 868-1 |
4 | 115200 | 8680ns | 8680/ System_clk_period | 434-1 |
uart_state部分对应代码如下:
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
uart_state=1'b0;
else if(send_en)
uart_state=1'b1;
//else if(Tx_done)//用此句会导致Tx_done有两个时钟周期而不是一个
else if(bps_cnt==4'd11)
uart_state=1'b0;
else
uart_state<=uart_state;
将要发送的数据用寄存器寄存起来,以免发送过程中数据突然变化
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
r_data_byte<=8'd0;
else if(send_en)
r_data_byte<=r_data_byte;
else
r_data_byte<=r_data_byte;
查找表模块,实现分频计数值的输出,送入分频模块产生bps_clk
always@(posedge Clk or negedge Rst_n)
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;
// 5:bps_DR<=16'd5207
// 6:bps_DR<=16'd5207
// 7:bps_DR<=16'd5207
default:bps_DR<=16'd5207;
endcase
end
分频计数器模块
//counter
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt<=16'd0;
else if(uart_state)begin
if (div_cnt==bps_DR)
div_cnt<=16'd0;
else
div_cnt<=div_cnt+1'b1;
end
else
div_cnt<=16'd0;
根据分频计数器模块的输出产生bps_clk
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_clk<=1'b0;
else if (div_cnt==16'd1)
bps_clk<=1'b1;
else
bps_clk<=1'b0;
bps_cnt 计数,到11清零,控制节奏
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
bps_cnt<=4'd0;
//else if(Tx_done)//用此句会导致Tx_done有两个时钟周期而不是一个
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;
产生发送完成信号tx_done
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Tx_done<=1'b0;
else if (bps_cnt==4'd11)
Tx_done=1'b1;
else
Tx_done<=1'b0;
十选一多路器,控制输出哪一位
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
Rs232_Tx<=1'b1;
else begin
case(bps_cnt)
0:Rs232_Tx<=1'b1;
1:Rs232_Tx<=START_BIT;
2:Rs232_Tx<=r_data_byte[0];
3:Rs232_Tx<=r_data_byte[1];
4:Rs232_Tx<=r_data_byte[2];
5:Rs232_Tx<=r_data_byte[3];
6:Rs232_Tx<=r_data_byte[4];
7:Rs232_Tx<=r_data_byte[5];
8:Rs232_Tx<=r_data_byte[6];
9:Rs232_Tx<=r_data_byte[7];
10:Rs232_Tx<=STOP_BIT;
default Rs232_Tx<=1'b1;
endcase
end
`timescale 1ns/1ns
`define clk_period 20
module uart_byte_tx_tb;
reg Clk;
reg Rst_n;
reg [7:0]data_byte;
reg send_en;
reg [2:0]baud_set;
wire Rs232_Tx;
wire Tx_Done;
wire uart_state;
uart_byte_tx uart_byte_tx(
.Clk(Clk),
.Rst_n(Rst_n),
.data_byte(data_byte),
.send_en(send_en),
.baud_set(baud_set),
.Rs232_Tx(Rs232_Tx),
.Tx_Done(Tx_Done),
.uart_state(uart_state)
);
initial Clk = 1;
always#(`clk_period/2)Clk = ~Clk;
initial begin
Rst_n = 1'b0;
data_byte = 8'd0;
send_en = 1'd0;
baud_set = 3'd4;//115200波特率
#(`clk_period*20 + 1 )
Rst_n = 1'b1;
#(`clk_period*50);
data_byte = 8'haa;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
data_byte = 8'h55;
send_en = 1'd1;
#`clk_period;
send_en = 1'd0;
@(posedge Tx_Done)
#(`clk_period*5000);
$stop;
end
endmodule
//小梅哥AC620开发板
module uart_byte_tx_top(clk,rst_n,data_in,uart_tx,send_en,tx_done,uart_state);
input [7:0]data_in;
input clk;
input rst_n;
input send_en;
output uart_tx;
output tx_done;
output uart_state;
//例化
uart_byte_tx myUART(//测试能否发送
.Clk(clk),
.Rst_n(rst_n),
.data_byte(data_in),
.send_en(~send_en),//开关按下为低电平所以取个反
.baud_set(0),
.Rs232_Tx(uart_tx),
.Tx_Done(tx_done),
.uart_state(uart_state)
);
endmodule
我使用的keysking的在线串口助手
本文章使用limfx的vscode插件快速发布