上集一起学习了异步串口收发器UART的电平,波特率,校验位,等内涵知识点,以及RS232,RS485,RS422等基于异步串口的实际通讯总线。
这一集我们一起来写一个简单的UART发送器,所以先回忆一下之前说的一个字节的格式:
我们下面的工作就是“画”出这个波形来。
直接分析代码,注意重要知识点:
- 需要一个慢的“时钟”,我们可以先用一个计数器,从0-N这样来回循环计数,当等于其中某一数值的时候,就是走了一轮,就是用了(N+1)个周期。这样我们提取计数器等于某一(0-N)内数值的条件,就可以作为(N+1)周期的同步使能信号了:
reg [3:0] cnt;
wire bit_syn = (cnt==9) ;
always @(posedge clk)if ( ~nrst ) cnt_clk <= 0 ;else cnt <= ( cnt == 9) ? 0:( 1 + cnt) ;
always @(posedge clk ) if (bit_syn) LED<=LED+1; 这样实现效果。
- 这里感受一下程序状态机的用法。是一种非常好的实现串行操作序列的方法。这里有个核心本质要把握,1,根据当前状态和输入或者状态条件确定下一个状态。2,根据当前状态,输入或者其他状态条件确定输出。程序状态机编写的时候可以不必太在意状态码,如果必要,编写往后再进行优化。
- 存在问题:波特率可能会存在可累积误差。
`timescale 1ns/1ns /* uart_tx #( .BAUDRATE(115200), .FREQ(200_000_000)) I_uart_tx ( .clk(clk), .nrst(nrst), .wr(wr), .din(din), .tx(tx) , .rdy(rdy), .parity_type(parity_type) ); */ module uart_tx #( parameter BAUDRATE = 115200, parameter FREQ = 200000000 )( input clk, nrst, input wr, input [7:0] din, output reg rdy, input[1:0] parity_type , output reg tx ); reg [7:0] din_save ; /* parity_type 0: NONE 1: ODD 2: EVEN */ // wire if_byte_is_odd = din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7] ; // 1 // wire if_byte_is_odd = din[0] ^ din[1] ^ din[2] ^ din[3] ^ din[4] ^ din[5] ^ din[6] ^ din[7] ; // 2 wire if_byte_is_odd = ^din[7:0]; reg [31:0] cnt_clk ; wire bit_syn = cnt_clk == (FREQ/BAUDRATE-1) ; always @(posedge clk)if ( ~nrst ) cnt_clk <= 0 ;else cnt_clk <= (bit_syn) ? 0:( 1 + cnt_clk) ; reg [7:0] st ; always@(posedge clk) if (~nrst) st<=0; else case (st) 0 : if ( wr )begin din_save<=din ; st<=1;end 1 : if (bit_syn) st<=2 ; // syn to bit_syn 2 : if (bit_syn) st<=3; // start bit 3 : if (bit_syn) st<=4; // send bit 0 4 : if (bit_syn) st<=5; // send bit 1 5 : if (bit_syn) st<=6; // send bit 2 6 : if (bit_syn) st<=7; // send bit 3 7 : if (bit_syn) st<=8; // send bit 4 8 : if (bit_syn) st<=9; // send bit 5 9 : if (bit_syn) st<=10; // send bit 6 10 : if (bit_syn) st<=11; // send bit 7 11 : if (bit_syn) st<=12; // parity 12 : if (bit_syn) st<=13; // stop bit 1 13 : if (bit_syn) st<=14; // stop bit 2 14 : if (bit_syn) st<=15; // stop bit 3 15 : if (bit_syn) st<=0; default st<=0; endcase always@ (posedge clk)if (~nrst)rdy<=1; else case (st )0: rdy <=1;default rdy<=0;endcase always@ (posedge clk)if (~nrst)tx<=1; else case (st) 0,1 : tx<=1; 2 : tx<=0; 3 : tx<=din_save[0]; 4 : tx<=din_save[1]; 5 : tx<=din_save[2]; 6 : tx<=din_save[3]; 7 : tx<=din_save[4]; 8 : tx<=din_save[5]; 9 : tx<=din_save[6]; 10 : tx<=din_save[7]; 11 : begin case (parity_type)0:tx <=1; 1:tx <= ~ if_byte_is_odd ;//odd 2:tx <= if_byte_is_odd ;// even default tx <= 1; endcase end 12,13,14,15 : tx<=1; default tx<=1;endcase endmodule module uart_tx_tb ; reg clk =0; reg nrst=0; reg wr=0; reg[1:0] parity_type = 0; wire rdy ,tx ; reg [7:0] din = 8'haa ; always #5 clk = ~clk ; initial begin #100 ; nrst = 1 ; #100 ; @(posedge clk);wr = 1; @(posedge clk);wr = 0; end uart_tx #( .BAUDRATE(115200), .FREQ(100000000)) I_uart_tx ( .clk(clk), .nrst(nrst), .wr(wr), .din(din), .tx(tx), .rdy(rdy), .parity_type( 0 ) ); endmodule