基于STEP FPGA的UART串口通信模块驱动

 行业动态     |      2023-11-30 10:33:39    |      作者

硬件说明

通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的时间间隔是固定的。两个相邻位间的时间间隔与UART通信的波特率有关,波特率用来表征UART通信中数据传输的速率,即每秒钟传送的二进制位数。例如数据传送速率为120字符/秒,而每一个字符为10位(1个起始位,7个数据位,1个校验位,1个结束位),则其传送的波特率为10×120=1200字符/秒=1200波特。

  • 起始位:先发出一个逻辑”0”信号,表示传输字符的开始。
  • 数据位:可以是5~8位逻辑”0”或”1”。如ASCII码(7位),扩展BCD码(8位)。小端传输
  • 校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验)
  • 停止位:它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。
  • 空闲位:处于逻辑“1”状态,表示当前线路上没有资料传送。

我们这里使用的时序为去掉校验位的时序

本设计共有四个模块,一个top模块,一个baud模块,一个接收模块和一个发送模块,大家可以根据自己的需求进行调整。


Verilog代码

//-------------------------------------------------------------------- //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //-------------------------------------------------------------------- //Module:Uart_bus // //Author:Step // //Description:Themoduleforuartcommunication // // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2016/04/20|Initialver //-------------------------------------------------------------------- moduleUart_Bus# ( parameter BPS_PARA=1250//当使用12MHz时钟时波特率参数选择1250对应9600的波特率 ) ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 input rs232_rx, //FPGA中UART接收端,分配给UART模块中的发送端TXD output rs232_tx //FPGA中UART发送端,分配给UART模块中的接收端RXD ); /////////////////////////////////UART接收功能模块例化//////////////////////////////////// wire bps_en_rx,bps_clk_rx; wire [7:0] rx_data;//UART接收波特率时钟控制模块例化 Baud# ( .BPS_PARA (BPS_PARA ) ) Baud_rx ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_rx ), //接收时钟使能 .bps_clk (bps_clk_rx ) //接收时钟输出 ); //UART接收数据模块例化 Uart_RxUart_Rx_uut ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_rx ), //接收时钟使能 .bps_clk (bps_clk_rx ), //接收时钟输入 .rs232_rx (rs232_rx ), //UART接收输入 .rx_data (rx_data ) //接收到的数据 ); /////////////////////////////////UART发送功能模块例化//////////////////////////////////// wire bps_en_tx,bps_clk_tx; //UART发送波特率时钟控制模块例化 Baud# ( .BPS_PARA (BPS_PARA ) ) Baud_tx ( .clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_tx ), //发送时钟使能 .bps_clk (bps_clk_tx ) //发送时钟输出 ); //UART发送数据模块例化 Uart_TxUart_Tx_uut(.clk_in (clk_in ), //系统时钟 .rst_n_in (rst_n_in ), //系统复位,低有效 .bps_en (bps_en_tx ), //发送时钟使能 .bps_clk (bps_clk_tx ), //发送时钟输入 .rx_bps_en (bps_en_rx ), //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送 .tx_data (rx_data ), //需要发出的数据 .rs232_tx (rs232_tx ) //UART发送输出 ); endmodule

//-------------------------------------------------------------------- //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //-------------------------------------------------------------------- //Module:Baud // //Author:Step // //Description:Beatforuarttransferandreceivebaudrate // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2016/04/20|Initialver //-------------------------------------------------------------------- moduleBaud# ( parameter BPS_PARA=1250//当使用12MHz时钟时波特率参数选择1250对应9600的波特率 ) ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 input bps_en, //接收或发送时钟使能 output reg bps_clk //接收或发送时钟输出 ); reg [12:0] cnt;//计数器计数满足波特率时钟要求 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in) cnt<=1'b0; elseif((cnt>=BPS_PARA-1)||(!bps_en))//当时钟信号不使能(bps_en为低电平)时,计数器清零并停止计数 cnt<=1'b0; //当时钟信号使能时,计数器对系统时钟计数,周期为BPS_PARA个系统时钟周期 else cnt<=cnt+1'b1;end//产生相应波特率的时钟节拍,接收模块将以此节拍进行UART数据接收 always@(posedgeclk_inornegedgerst_n_in) begin if(!rst_n_in) bps_clk<=1'b0; elseif(cnt==(BPS_PARA>>1)) //BPS_PARA右移一位等于除2,因计数器终值BPS_PARA为数据更替时间点,所以计数器中值时为数据最稳定时间点 bps_clk<=1'b1; else bps_clk<=1'b0; end endmodule

//-------------------------------------------------------------------- //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //-------------------------------------------------------------------- //Module:Uart_Rx // //Author:Step // //Description:Thereceivemoduleofuartinterface // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2016/04/20|Initialver //-------------------------------------------------------------------- moduleUart_Rx(input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 output reg bps_en, //接收时钟使能 input bps_clk, //接收时钟输入 input rs232_rx, //UART接收输入 output reg [7:0] rx_data //接收到的数据 ); reg rs232_rx0,rs232_rx1,rs232_rx2; //多级延时锁存去除亚稳态 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in)begin rs232_rx0<=1'b0; rs232_rx1<=1'b0; rs232_rx2<=1'b0; endelsebegin rs232_rx0<=rs232_rx; rs232_rx1<=rs232_rx0; rs232_rx2<=rs232_rx1; endend//检测UART接收输入信号的下降沿 wire neg_rs232_rx=rs232_rx2&rs232_rx1&(~rs232_rx0)&(~rs232_rx); reg [3:0] num; //接收时钟使能信号的控制 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in) bps_en<=1'b0; elseif(neg_rs232_rx&&(!bps_en)) //当空闲状态(bps_en为低电平)时检测到UART接收信号下降沿,进入工作状态(bps_en为高电平),控制时钟模块产生接收时钟 bps_en<=1'b1; elseif(num==4'd9) //当完成一次UART接收操作后,退出工作状态,恢复空闲状态 bps_en<=1'b0; end reg [7:0] rx_data_r;//当处于工作状态中时,按照接收时钟的节拍获取数据 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in)begin num<=4'd0; rx_data<=8'd0; rx_data_r<=8'd0; endelseif(bps_en)begin if(bps_clk)begin num<=num+1'b1; if(num<=4'd8) rx_data_r[num-1]<=rs232_rx; //先接受低位再接收高位,8位有效数据 endelseif(num==4'd9)begin //完成一次UART接收操作后,将获取的数据输出 num<=4'd0; rx_data<=rx_data_r; end end end endmodule

//-------------------------------------------------------------------- //>>>>>>>>>>>>>>>>>>>>>>>>>COPYRIGHTNOTICE<<<<<<<<<<<<<<<<<<<<<<<<< //-------------------------------------------------------------------- //Module:Uart_Tx // //Author:Step // //Description:Thetransfermoduleofuartinterface // //-------------------------------------------------------------------- //CodeRevisionHistory: //-------------------------------------------------------------------- //Version:|Mod.Date:|ChangesMade: //V1.0|2016/04/20|Initialver //-------------------------------------------------------------------- moduleUart_Tx ( input clk_in, //系统时钟 input rst_n_in, //系统复位,低有效 output reg bps_en, //发送时钟使能 input bps_clk, //发送时钟输入 input rx_bps_en, //因需要自收自发,使用接收时钟使能判定:接收到新的数据,需要发送 input [7:0] tx_data, //需要发出的数据 output reg rs232_tx //UART发送输出 ); reg rx_bps_en_r;//延时锁存接收时钟使能信号 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in)rx_bps_en_r<=1'b0; elserx_bps_en_r<=rx_bps_en;end//检测接收时钟使能信号的下降沿,因为下降沿代表接收数据的完成,以此作为发送信号的激励 wire neg_rx_bps_en=rx_bps_en_r&(~rx_bps_en); reg [3:0] num; reg [9:0] tx_data_r; //根据接收数据的完成,驱动发送数据操作 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in)begin bps_en<=1'b0; tx_data_r<=8'd0; endelseif(neg_rx_bps_en)begin bps_en<=1'b1; //当检测到接收时钟使能信号的下降沿,表明接收完成,需要发送数据,使能发送时钟使能信号 tx_data_r<={1'b1,tx_data,1'b0}; endelseif(num==4'd10)begin bps_en<=1'b0; //一次UART发送需要10个时钟信号,然后结束 endend//当处于工作状态中时,按照发送时钟的节拍发送数据 always@(posedgeclk_inornegedgerst_n_in)begin if(!rst_n_in)begin num<=1'b0; rs232_tx<=1'b1; endelseif(bps_en)begin if(bps_clk)begin num<=num+1'b1; rs232_tx<=tx_data_r[num]; endelseif(num>=4'd10) num<=4'd0; end end endmodule


小结

本节主要为大家讲解了UART通信的原理及软件设计,需要大家掌握的同时自己创建工程,通过整个设计流程,生成FPGA配置文件加载测试。