1、串口基本知识
按照一个完整的字节包括一位起始位、8位数据位、一位停止位即总共十位数据来算,要想完整的实现这十
位数据的发送,就需要 11 个波特率时钟脉冲,第 1 个脉冲标记一次传输的起始,第 11 个脉冲标记一次传输的结束。
bps_clk 信号的第一个上升沿到来时,字节发送模块开始发送起始位,接下来的 2 到 9 个上升沿,发送 8 个数据位,第 10 个上升沿到第 11 个上升沿为停止位的发送。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| UART 发送一个字节时序图- 精细模拟 +-----------------------------------------------------------------------------+ | | | 波特率时钟 (bps_clk): | | __ __ __ __ __ __ __ __ __ __ __ | | clk: | |__| |__| |__| |__| | | |__| |__| |__| |__| |__| |__ | | l1 l2 l3 l4 l5 l6 l7 l8 l9 l10 l11 | | | | 发送数据线 (uart_tx): | | | | | START | BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7| STOP | | tx: |_______|______|______|______|______|______|______|______|_____|______| | |<----------------------- 10 位数据 ----------------------->| | | | | 时钟动作: | | ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ | | 开始 位0 位1 位2 位3 位4 位5 位6 位7 停止 结束 | | | +-----------------------------------------------------------------------------+
|
串口输出的时候,并转串,由并行数据,转化为串行数据一位一位发出去
1 2 3 4 5 6 7 8 9 10
| 输入 (8位) 0 ───────────────┐ 1 ───────────────┤ 1 ───────────────┤ 0 ───────────────┤ 0 ───────────────┤ ┌────────────┐ 0 ───────────────┼───┤ 处理单元 ├─── 输出波形 1 ───────────────┤ └────────────┘ ▁▁▁▂▃▅▆▇▇▆▅▃▂▁ 0 ───────────────┤ 0 ───────────────┘
|
2、串口的设计思想
1、串口通信模块设计的目的是用来发送数据的,因此需要有一个数据输入端口
2、串口通信,支持不同的波特率,所以需要有一个波特率设置端口
3、串口通信的本质就是将8位的并行数据通过一根信号线,在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据全部传出
4、串口通信以1位的低电平标志串行传输的开始,待8位数据传输完成之后,再以1位的高电平标志传输的结束。
5、控制信号,控制并转串模块什么开始工作。什么时候一个数据发送完成?须有一个发送开始信号,以及一个发送完成信号。
3、波特率与时钟的关系
1、这些计算好的分频系数值会写入到模块内部的 bps_DR寄存器中。模块中的分频计数器会一直数到 bps_DR的值,然后归零并产生一个短暂的脉冲,这个脉冲就是波特率时钟。每个波特率时钟脉冲到来时,模块就向外发送一个比特的数据。
2、波特率计算:在您设计的串口发送模块中,通过 baud_set[2:0]输入信号选择不同的分频系数。该系数由公式 分频值 = (50MHz系统时钟 / 目标波特率) - 1 计算得出并预设在模块中。例如,选择9600波特率时,分频系数为5207,模块内部计数器每数5207个系统时钟,就产生一个控制时钟脉冲来发送1比特数据。
3、与串口发送的关系:波特率直接决定了串口发送数据的“节奏”。每个波特率时钟脉冲到来时,发送模块就向 uart_tx线输出一个比特。波特率越高,脉冲间隔越短,发送速度越快。通信双方必须设置相同的波特率,接收方才能以同样的节奏采样,否则会导致数据错乱,通信失败。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
always@(posedge clk or posedge reset) if(reset) 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
|
baud_set |
波特率 |
波特率周期 |
波特率分频计数值 |
50M系统时钟计数值 |
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 |
4、总体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| module uart_byte_tx( clk, reset_n, data_byte, send_en, baud_set, uart_tx, tx_done, uart_state );
input clk ; input reset_n; input [7:0]data_byte; input send_en; input [2:0]baud_set; output reg uart_tx; output reg tx_done; output reg uart_state; wire reset=~reset_n; localparam START_BIT = 1'b0; localparam STOP_BIT = 1'b1; reg bps_clk; reg [15:0]div_cnt; reg [15:0]bps_DR; reg [3:0]bps_cnt; reg [7:0]data_byte_reg; always@(posedge clk or posedge reset) if(reset) uart_state <= 1'b0; else if(send_en) uart_state <= 1'b1; else if(bps_cnt == 4'd11) uart_state <= 1'b0; else uart_state <= uart_state; always@(posedge clk or posedge reset) if(reset) data_byte_reg <= 8'd0; else if(send_en) data_byte_reg <= data_byte; else data_byte_reg <= data_byte_reg; always@(posedge clk or posedge reset) if(reset) 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 always@(posedge clk or posedge reset) if(reset) 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; always@(posedge clk or posedge reset) if(reset) bps_clk <= 1'b0; else if(div_cnt == 16'd1) bps_clk <= 1'b1; else bps_clk <= 1'b0; always@(posedge clk or posedge reset) if(reset) 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; always@(posedge clk or posedge reset) if(reset) tx_done <= 1'b0; else if(bps_cnt == 4'd11) tx_done <= 1'b1; else tx_done <= 1'b0; always@(posedge clk or posedge reset) if(reset) uart_tx <= 1'b1; else begin case(bps_cnt) 0:uart_tx <= 1'b1; 1:uart_tx <= START_BIT; 2:uart_tx <= data_byte_reg[0]; 3:uart_tx <= data_byte_reg[1]; 4:uart_tx <= data_byte_reg[2]; 5:uart_tx <= data_byte_reg[3]; 6:uart_tx <= data_byte_reg[4]; 7:uart_tx <= data_byte_reg[5]; 8:uart_tx <= data_byte_reg[6]; 9:uart_tx <= data_byte_reg[7]; 10:uart_tx <= STOP_BIT; default:uart_tx <= 1'b1; endcase end
endmodule
|
5、testbench测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| `timescale 1ns/1ns `define CLK_PERIOD 20
module uart_byte_tx_tb;
reg clk; reg reset_n; reg [7:0]data_byte; reg send_en; reg [2:0]baud_set; wire uart_tx; wire tx_done; wire uart_state; uart_byte_tx uart_byte_tx( .clk(clk), .reset_n(reset_n),
.data_byte(data_byte), .send_en(send_en), .baud_set(baud_set),
.uart_tx(uart_tx), .tx_done(tx_done), .uart_state(uart_state) ); initial clk = 1; always#(`CLK_PERIOD/2)clk = ~clk; initial begin reset_n = 1'b0; data_byte = 8'd0; send_en = 1'd0; baud_set = 3'd4; #(`CLK_PERIOD*500 + 1 ) reset_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
|
代码解析:
- @(posedge tx_done)是一条“等待”或“同步”语句。它的作用是:暂停测试流程的执行,直到检测到 tx_done信号出现一个上升沿(从0变到1),然后才继续执行后面的代码。
6、测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
module uart_tx_test( input clk, input reset_n, output uart_tx, output led );
parameter MCNT = 49_999_999;
reg [7:0] data_byte; reg send_en; reg [25:0] cnt;
wire tx_done;
always@(posedge clk or negedge reset_n) if(!reset_n) cnt <= 25'd0; else if(cnt == MCNT) cnt <= 25'd0; else cnt <= cnt + 1'b1;
always@(posedge clk or negedge reset_n) if(!reset_n) send_en <= 1'b0; else if(cnt == MCNT) send_en <= 1'b1; else send_en <= 1'b0;
always@(posedge clk or negedge reset_n) if(!reset_n) data_byte <= 8'b0; else if(tx_done) data_byte <= data_byte + 1'b1; else data_byte <= data_byte;
uart_byte_tx uart_byte_tx( .clk(clk), .reset_n(reset_n), .data_byte(data_byte), .send_en(send_en), .baud_set(3'd0), .uart_tx(uart_tx), .tx_done(tx_done), .uart_state(led) );
endmodule
|