1、协议原理:
UART(universal asynchronous receiver-transmitter)通用异步收发传输器。
uart串口通信需要两根信号线来实现,一根用于串口发送,一根用于串口接收。一开始高电平,然后拉低表示开始位,接着8个数据位,最后拉高表示停止位,并且进入空闲状态,等待下一次的数据传输。
因为uart通信没有时钟,因此只能规定多少时间发送一个二进制位来保证数据收发不会出错,这就是波特率。这里时钟频率50MHz,波特率9600bps,因此发送一个二进制位需要(50_000_000/9600=5208)个时钟,换言之,计数到5208就发送一位二进制。
2、协议代码:
代码由顶层模块、接收模块和发送模块构成。其中仿真的代码是仿真通过接收模块接收数据,再发送出来。同样串口调试助手也是通过接收模块接收数据,然后再发出来。
顶层模块:
1 module uart_top( 2 input sys_clk, 3 input sys_rst_n, 4 input uart_recv, 5 output uart_send 6 ); 7 8 wire [7:0]data; 9 wire data_en; 10 11 uart_recv 12 #( 13 .BPS_CNT (52) //仿真用的,串口调试不用直接删掉! 14 ) 15 u_uart_recv 16 ( 17 .sys_clk (sys_clk), 18 .sys_rst_n (sys_rst_n), 19 .data_in_recv (uart_recv), 20 .data_out_recv (data), 21 .data_en (data_en) 22 ); 23 24 uart_send 25 #( 26 .BPS_CNT (52) //仿真用的,串口调试不用直接删掉! 27 ) 28 u_uart_send 29 ( 30 .sys_clk (sys_clk), 31 .sys_rst_n (sys_rst_n), 32 .data_en (data_en), 33 .data_in_send (data), 34 .data_out_send (uart_send) 35 ); 36 endmodule
接收模块:
1 module uart_recv 2 ( 3 input sys_clk, 4 input sys_rst_n, 5 input data_in_recv, 6 output reg[7:0]data_out_recv, 7 output reg data_en 8 ); 9 parameter CLK=50_000_000; 10 parameter UART_BPS=9600; 11 parameter BPS_CNT=CLK/UART_BPS; 12 reg data_in_recv_d0; 13 reg data_in_recv_d1; 14 reg [15:0]bps_cnt; 15 reg [3:0]bit_cnt; 16 reg [7:0]data; 17 reg flag; 18 wire start_flag; 19 20 always @(posedge sys_clk or negedge sys_rst_n)begin 21 if(!sys_rst_n)begin 22 data_in_recv_d0<=1'b0; 23 data_in_recv_d1<=1'b0; 24 end 25 else begin 26 data_in_recv_d0<=data_in_recv; 27 data_in_recv_d1<=data_in_recv_d0; 28 end 29 end 30 assign start_flag=data_in_recv_d1 & (~data_in_recv_d0); 31 32 33 always @(posedge sys_clk or negedge sys_rst_n)begin 34 if(!sys_rst_n) 35 flag<=1'b0; 36 else if(start_flag) 37 flag<=1'b1; 38 else if((bit_cnt==9)&&(bps_cnt==BPS_CNT/2)) 39 flag<=1'b0; 40 else 41 flag<=flag; 42 end 43 44 always @(posedge sys_clk or negedge sys_rst_n)begin 45 if(!sys_rst_n)begin 46 bps_cnt<=16'd0; 47 bit_cnt<=4'd0; 48 end 49 else if(flag)begin 50 if(bps_cnt<BPS_CNT-1)begin 51 bps_cnt<=bps_cnt+1'b1; 52 bit_cnt<=bit_cnt; 53 end 54 else begin 55 bps_cnt<=16'd0; 56 bit_cnt<=bit_cnt+1; 57 end 58 end 59 else begin 60 bps_cnt<=16'd0; 61 bit_cnt<=4'd0; 62 end 63 end 64 65 always @(posedge sys_clk or negedge sys_rst_n) begin 66 if ( !sys_rst_n) 67 data <= 8'd0; 68 else if(flag) 69 if (bps_cnt == BPS_CNT/2) begin 70 case ( bit_cnt ) 71 4'd1 : data[0] <= data_in_recv_d1; 72 4'd2 : data[1] <= data_in_recv_d1; 73 4'd3 : data[2] <= data_in_recv_d1; 74 4'd4 : data[3] <= data_in_recv_d1; 75 4'd5 : data[4] <= data_in_recv_d1; 76 4'd6 : data[5] <= data_in_recv_d1; 77 4'd7 : data[6] <= data_in_recv_d1; 78 4'd8 : data[7] <= data_in_recv_d1; 79 default:; 80 endcase 81 end 82 else 83 data <= data; 84 else 85 data <= 8'd0; 86 end 87 88 always @(posedge sys_clk or negedge sys_rst_n)begin 89 if(!sys_rst_n)begin 90 data_out_recv<=8'd0; 91 data_en<=1'b0; 92 end 93 else if(bit_cnt==9)begin 94 data_out_recv<=data; 95 data_en<=1'b1; 96 end 97 else begin 98 data_en<=1'b0; 99 end 100 end 101 endmodule
发送模块:
1 module uart_send 2 ( 3 input sys_clk, 4 input sys_rst_n, 5 input data_en, 6 input [7:0]data_in_send, 7 output reg data_out_send 8 ); 9 parameter CLK=50_000_000; 10 parameter UART_BPS=9600; 11 parameter BPS_CNT=CLK/UART_BPS; 12 reg [7:0]tem_data; 13 reg [15:0]bps_cnt; 14 reg [3:0]bit_cnt; 15 reg send_flag; 16 17 always @(posedge sys_clk or negedge sys_rst_n)begin 18 if(!sys_rst_n) 19 send_flag<=1'b0; 20 else if(data_en)begin 21 tem_data<=data_in_send; 22 send_flag<=1'b1; 23 end 24 else if((bit_cnt==4'd9)&&(bps_cnt==BPS_CNT/2))begin 25 tem_data<=8'd0; 26 send_flag<=1'b0; 27 end 28 else begin 29 tem_data<=tem_data; 30 send_flag<=send_flag; 31 end 32 end 33 34 always @(posedge sys_clk or negedge sys_rst_n)begin 35 if(!sys_rst_n)begin 36 bps_cnt<=16'd0; 37 bit_cnt<=4'd0; 38 end 39 else if(send_flag)begin 40 if(bps_cnt<BPS_CNT-1)begin 41 bps_cnt<=bps_cnt+1'b1; 42 bit_cnt<=bit_cnt; 43 end 44 else begin 45 bps_cnt<=16'd0; 46 bit_cnt<=bit_cnt+1'b1; 47 end 48 end 49 else begin 50 bps_cnt<=16'd0; 51 bit_cnt<=4'd0; 52 end 53 end 54 55 always @(posedge sys_clk or negedge sys_rst_n) begin 56 if (!sys_rst_n) 57 data_out_send <= 1'b1; 58 else if (send_flag) 59 case(bit_cnt) 60 4'd0: data_out_send <= 1'b0; 61 4'd1: data_out_send <= tem_data[0]; 62 4'd2: data_out_send <= tem_data[1]; 63 4'd3: data_out_send <= tem_data[2]; 64 4'd4: data_out_send <= tem_data[3]; 65 4'd5: data_out_send <= tem_data[4]; 66 4'd6: data_out_send <= tem_data[5]; 67 4'd7: data_out_send <= tem_data[6]; 68 4'd8: data_out_send <= tem_data[7]; 69 4'd9: data_out_send <= 1'b1; 70 default: ; 71 endcase 72 else 73 data_out_send <= 1'b1; 74 end 75 endmodule
仿真:
1 `timescale 1ns/1ns 2 module uart_top_tb; 3 reg sys_clk ; 4 reg sys_rst_n ; 5 reg uart_recv ; 6 wire uart_send ; 7 8 uart_top u_uart_top 9 ( 10 .sys_clk (sys_clk ), 11 .sys_rst_n (sys_rst_n ), 12 .uart_recv (uart_recv ), 13 .uart_send (uart_send ) 14 ); 15 16 initial begin 17 sys_clk = 1; 18 end 19 always #10 sys_clk = ~sys_clk; 20 21 initial begin 22 sys_rst_n = 0; 23 #30 sys_rst_n = 1; 24 end 25 26 reg [7:0] mem[15:0] ; 27 integer i ; 28 integer j ; 29 30 31 initial $readmemh("./data.txt",mem); 32 33 task bit_cnt 34 ( 35 input [7:0] data 36 ); 37 begin 38 for(i=0;i<=9;i=i+1) begin //10?bit? 39 case(i) 40 0: uart_recv = 1'b0; 41 1: uart_recv = data[i-1]; 42 2: uart_recv = data[i-1]; 43 3: uart_recv = data[i-1]; 44 4: uart_recv = data[i-1]; 45 5: uart_recv = data[i-1]; 46 6: uart_recv = data[i-1]; 47 7: uart_recv = data[i-1]; 48 8: uart_recv = data[i-1]; 49 9: uart_recv = 1'b1; 50 endcase 51 #1040; 52 end 53 end 54 endtask 55 56 task rx_byte; 57 begin 58 for(j=0;j<=15;j=j+1) 59 bit_cnt(mem[j]); 60 end 61 endtask 62 63 initial begin 64 # 401 rx_byte(); 65 end 66 67 initial begin 68 #180000; 69 $stop; 70 end 71 endmodule
仿真的时候,在sim目录下建立一个data.txt存放宽度8位,深度16位的文件,然后仿真接收这个文件,然后发出这个文件。
这里的数字是十六进制,比如8'h00 8'h01...
仿真结果以及串口调试结果:
暂时还没有解惑的地方:
①不明白作者为什么在仿真的时候,顶层模块这里需要有一个52?
#(
.BPS_CNT (52) //仿真用的,串口调试不用直接删掉!
)
②根据接收模块最后的代码可以知道,当接收成功每一个字(8'h00 8'h01...)的所有二进制位,data_en就会被拉高,前面的8'h00 8'h01...到8'h0e,一直都是接收完之后就拉高data_en,然而为什么到了最后一个字8'h0f就没有出现了?