1.关于URAT的知识

2.UART模块设计中涉及到的状态机的知识

3.UART模块的编写:以UART_TX为例

4.UART模块的使用

============================================================================

1.关于UART的知识

(1)硬件工作原理及概念介绍
UART : Universal Asynchronous Receiver/Transmitter,通用异步接收/发送器,也就是通常所说的串口,是一种通用串行数据传输总线,可以实现全双工传输。

       在嵌入式领域,UART已经是一种最基本的模块,其发送和接收是两个完全独立的通路, 分别是发送“TX” 和接收“RX“通道,既然是独立的通路,发送和接收端又没有时钟参考,所以从协议上规定了串行通信必须按一定的数据速率来接收和发送,这个速率定义为 ”波特率“,即每秒发送的数据的位数。 常用的波特率有:9600/38400/57600/115200……, 波特率确定后,每一位所占用的时间就可以计算出来,例如:115200bps, 每一位的周期为:1/115200 = 8.68us, 因此用我们开发板上的计数器对外部50MHz进行计数的话,8680ns / 20ns = 1B2
(2)帧结构

       下面我们来看一下一个完整的数据帧,第一位为开始位,始终为0,最后一位为停止位,始终为1

uart demo_sed

=======================================================================================

2.UART模块设计中涉及到的状态机的知识

状态机:通过不同的状态迁移来实现一种顺序逻辑控制。一般有Mealy型和Moore型两种,写法一般有两端式和三段式,这里

采用三段式:即  D触发器 +  状态变换 +  指令控制,转换关系如下图所示:

 

uart demo_状态机_02

 

===================================================================================

3.UART模块的编写:以UART_TX为例

程序编写涉及到两个部分:波特率的的控制,帧数据的状态机构成

(1)波特率的控制: 

//状态转换所需的控制信号.
assign BAUD_RATE   = (CLK_DIV_CNT == 16'h1B2);
assign TX_FINISH   = (TX_CS == IDLE);
assign DATA_FINISH = (TX_BIT_CNT == 3'h7);

//波特率的设置.
always @ (posedge SYSCLK or negedge RST_B)
begin
  if(!RST_B)
     CLK_DIV_CNT       <= `UD 16'h0;
  else
     CLK_DIV_CNT       <= `UD CLK_DIV_CNT_N;
end

always @ (*)
begin
  if(TX_FINISH)
     CLK_DIV_CNT_N   = 16'h0;
  else if(CLK_DIV_CNT == 16'h1B2)//baud rate is 115200
     CLK_DIV_CNT_N   = 16'h0;
  else
     CLK_DIV_CNT_N   = CLK_DIV_CNT + 16'h1;
end

//计算8位发送数据的位数,在状态Send_Data中将8位数据转换成串行数据.
always @ (posedge SYSCLK or negedge RST_B)
begin
  if(!RST_B)
     TX_BIT_CNT       <= `UD 3'h0;
  else
     TX_BIT_CNT       <= `UD TX_BIT_CNT_N;
end

always @ (*)
begin
  if(!TX_EN)
     TX_BIT_CNT_N    = 3'h0;

  else if((TX_BIT_CNT == 3'h7) && (BAUD_RATE))
     TX_BIT_CNT_N    = 3'h0;
  else if((TX_CS == SEND_DATA) && (BAUD_RATE))
     TX_BIT_CNT_N    = TX_BIT_CNT + 3'h1;
  else 
     TX_BIT_CNT_N    = TX_BIT_CNT;
end

//计算TX_EN置1时IDLE到SEND_START的缓冲的时间.
always @ (posedge SYSCLK or negedge RST_B)
begin
  if(!RST_B)
     TIME_CNT       <= `UD 4'h0;
  else
     TIME_CNT       <= `UD TIME_CNT_N;
end

always @ (*)
begin
  if(TX_CS != IDLE)
     TIME_CNT_N   = 4'h0;
  else
     TIME_CNT_N   = TIME_CNT + 4'h1;
end

 

(2)帧数据的状态机构成 

//D触发器段,存储状态值
always @ (posedge SYSCLK or negedge RST_B)
begin
  if(!RST_B)
    TX_CS        <= `UD IDLE;
  else
    TX_CS         <= `UD TX_NS;
end
//状态转换段

always @ (*)
begin
  case(TX_CS)
    IDLE       :  if((TX_EN)&&(TIME_CNT == 4'hF))// && BAUD_RATE)
                 TX_NS = SEND_START;
               else
             TX_NS = TX_CS;
         
    SEND_START    :  if(BAUD_RATE)
                 TX_NS = SEND_DATA;
               else
             TX_NS = TX_CS;
         
    SEND_DATA    :  if(DATA_FINISH && BAUD_RATE)
                 TX_NS = SEND_STOP;
               else
             TX_NS = TX_CS;
    
    SEND_STOP    :  if(BAUD_RATE)
                 TX_NS = SEND_END;
               else
             TX_NS = TX_CS;
    SEND_END    :  TX_NS = IDLE;
    default    :  TX_NS = IDLE;
  endcase
end

always @ (posedge SYSCLK or negedge RST_B)
begin
  if(!RST_B)
    UART_TX   <= `UD 1'h1;
  else
    UART_TX   <= `UD UART_TX_N;
end
//指令控制段
always @ (*)
begin
  if(TX_EN)
  begin
  case(TX_CS)
    IDLE       :  UART_TX_N  = 1'h1;
    SEND_START :  UART_TX_N  = 1'h0;
    SEND_DATA  :  case(TX_BIT_CNT)
                    3'h0  :  UART_TX_N  = TX_DATA[0];
                    3'h1  :  UART_TX_N  = TX_DATA[1];
                    3'h2  :  UART_TX_N  = TX_DATA[2];
                    3'h3  :  UART_TX_N  = TX_DATA[3];
                    3'h4  :  UART_TX_N  = TX_DATA[4];
                    3'h5  :  UART_TX_N  = TX_DATA[5];
                    3'h6  :  UART_TX_N  = TX_DATA[6];
                    3'h7  :  UART_TX_N  = TX_DATA[7];
            default: UART_TX_N  = 1'h1;
          endcase
    SEND_STOP  :  UART_TX_N  = 1'h1;
    default       :  UART_TX_N  = 1'h0;
  endcase
  end
  else
   UART_TX_N = 1'h1;
end

 

 至此完成UART_TX发送模块的核心代码,我发现涉及到硬件驱动的都用状态机加时序控制来实现数据的传输. 

-------------------------------------------------------------------------------------------------------------

UART模块编写特点:

1.内部逻辑一共用三个寄存器:状态机状态 TX_CS、      波特率计数 CLK_DIV_CNT、          串行数据位数 BIT_CNT

   输出口用了一个寄存器:  UART串口发送端 UART_TX

2.虽然状态不同,但是步调一致,每次状态的改变用CLK_DIV_CNT与BAUD_RATE相互配合

 =================================================================================

4.UART模块的使用

(1)UART_TX.V

 

UART_TX I_UART_TX      //UART_TX模块
    (
    .SYSCLK        (SYSCLK),
    .RST_B        (RST_B),
    .TX_DATA    (TX_DATA),      //输入:要发送的数据
    .TX_EN        (TX_EN),      //输入:发送使能端
    .UART_TX    (UART_TX),     //输出:串行发送端
    .TX_FINISH    (TX_FINISH)   //输出:发送结束标志
    );

 

 

附:更改波特率改该模块下语句中的数据16‘h1b2

always @ (*)
begin
  if(TX_FINISH)
    CLK_DIV_CNT_N   = 16'h0;
  else if(CLK_DIV_CNT == 16'h1B2)    //baud rate is 115200
    CLK_DIV_CNT_N   = 16'h0;
  else
    CLK_DIV_CNT_N   = CLK_DIV_CNT + 16'h1;
end
assign BAUD_RATE   = (CLK_DIV_CNT == 16'h1B2);