FPGA-串口发送模块
------------恢复内容开始------------
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)是一种异步收发传输器,其在数据发送时将并行数据转换成串行数据来传输,在数据接收时将接收到的串行数据转换成并行数据,可以实现全双工传输和接收。它包括了 RS232、RS449、RS423、 RS422 和 RS485 等接口标准规范和总线标准规范。换句话说,UART 是异步串行通信的总称。而 RS232、RS449、RS423、RS422 和 RS485 等,是对应各种异步串行通信口的接口标准和总线标准,它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容。
图1 串口发送时序图
图2 串口发送逻辑框图
根据选择的波特率计算出相应的波特率时钟,如115200的波特率对应的波特率时钟周期为8680ns,50M时钟对应下时钟计时数为433。波特率时钟周期计时器为div_cnt。波特率时钟计数器为bps_cnt,记录波特率时钟的个数。send_en为发送标志,当send_en时,开始发送数据,数据发送依据bps_cnt的数值来进行,由于串口发送协议起始位之前需为1,因此bps_cnt=0时,uart_tx=1;bps_cnt=1-10时,uart_tx依次等于起始位、数据位、停止位,为了确保停止位保持一位的高电平,bps_cnt=11时,uart_tx=1,因此bps_cnt在0-11内循环,bps_cnt=11时立即清零。为了让send_en信号出现后能立即开始发送数据,设置div_cnt每次为0时,bps_cnt就+1。十位数据发送完成后,tx_done拉高,同时send_en拉低,标志发送完毕。
利用串口发送模块实现一个每隔10ms,计数加1的计数器。计数从0开始,通过send_en和tx_done信号配合,当count_10ms开始计数时,send_en信号拉高,直到tx_done信号到来时拉低,每当tx_done信号到来时,计时数+1。
串口发送模块代码:
module urat_tx( input wire clk , input wire rstn , input wire [ 2:0] baudrate , input wire [ 7:0] data , input wire ena , output reg tx , output reg tx_done ); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Parameter & Signal +++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ reg [ 12:0] baud_clock ; reg [ 12:0] div_cnt ; reg [ 3:0] bps_cnt ; //reg ena ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Main code ++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ always @(*) begin //baudrate choose case (baudrate) 3'd0:baud_clock<=13'd433; 3'd1:baud_clock<=13'd867; 3'd2:baud_clock<=13'd1301; 3'd3:baud_clock<=13'd2603; 3'd4:baud_clock<=13'd5207; default: baud_clock<=13'd433; endcase end always @(posedge clk or negedge rstn) begin //div_cnt if(!rstn) div_cnt<=0; else if(ena)begin if(div_cnt==baud_clock) div_cnt<=0; else div_cnt<=div_cnt+1'b1; end else div_cnt<=0; end always @(posedge clk or negedge rstn) begin //bps_cnt if(!rstn) bps_cnt<=0; else if(ena)begin if(div_cnt==0) begin if(bps_cnt==11) bps_cnt<=0; else bps_cnt<=bps_cnt+1'b1; end end else bps_cnt<=0; end always @(posedge clk or negedge rstn) begin if(!rstn) tx<=0; else begin case (bps_cnt) 0:tx<=1; 1:tx<=0; 2:tx<=data[0]; 3:tx<=data[1]; 4:tx<=data[2]; 5:tx<=data[3]; 6:tx<=data[4]; 7:tx<=data[5]; 8:tx<=data[6]; 9:tx<=data[7]; 10:tx<=1; 11:tx<=1; default:tx<=1; endcase end end always @(posedge clk or negedge rstn) begin //tx_done if(!rstn) tx_done<=0; else if((bps_cnt==11)&&(div_cnt==1)) tx_done<=1; else tx_done<=0; end /*always @(posedge clk or negedge rstn) begin //ena if(!rstn) ena<=0; else if(bps_cnt==0) ena<=1; else if(tx_done) ena<=0; end*/ endmodule
每隔10ms计数器代码:
module uart_tx_count( clk, rstn, tx ); input clk ; input rstn ; output tx ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Parameter & Signal +++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ reg [ 7:0] data ; wire tx_done ; reg ena ; reg [ 18:0] count_10ms ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Main code ++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ urat_tx urat_tx_inst( .clk (clk ), .rstn (rstn ), .baudrate (3'd0 ), .data (data ), .ena (ena ), .tx (tx ), .tx_done (tx_done ) ); always @(posedge clk or negedge rstn) begin //count_10ms if(!rstn) count_10ms<=0; else if(count_10ms==499999) count_10ms<=0; else count_10ms<=count_10ms+1'b1; end always @(posedge clk or negedge rstn) begin //ena if(!rstn) ena<=0; else if(count_10ms==1) ena<=1; else if(tx_done) ena<=0; end always @(posedge clk or negedge rstn) begin if(!rstn) data<=0; else if(tx_done) data<=data+1'b1; end endmodule
testbench代码:
`timescale 1ns / 1ns module urat_tx_count_tb(); reg clk; reg rstn; wire tx; uart_tx_count u_uart_tx_count( .clk (clk ), .rstn (rstn ), .tx (tx ) ); initial clk=1; always#10 clk=!clk; initial begin rstn=0; #201; rstn=1; #10000000; $stop; end endmodule
Vivado仿真结果:
可以看到10ms计数周期中,只有发送信号时,ena信号才为高电平,发送结束标志信号tx_done到来后拉低,计数值加1。