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
=======================================================================================
2.UART模块设计中涉及到的状态机的知识
状态机:通过不同的状态迁移来实现一种顺序逻辑控制。一般有Mealy型和Moore型两种,写法一般有两端式和三段式,这里
采用三段式:即 D触发器 + 状态变换 + 指令控制,转换关系如下图所示:
===================================================================================
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);