1、USART的主要特性
1)名称:串行异步通信接口
2)全双工、异步通信
3)发送和接收速率最高可达4.5MBits/s
4)可编程数据长度8或9bits
5)可配置的停止位:支持1或2位停止位
6)单线半双工通信
7)使用DMA(直接存储器访问)可配置多缓冲通信
8)支持独立的发送和接收
9)发送确认标志:接收缓冲区慢、发送缓冲空、发送结束标志
10)奇偶控制:传输奇偶位、检查接收数据的奇偶
11)四个错误检测标志:溢出错误、噪声错误、帧错误、奇偶错误
12)10个带中断源标志:发送数据寄存器为空、发送完成、接收数据寄存器满、溢出错误、噪声错误、帧错误、奇偶错误。。。
13)如果没有发生地址匹配,多处理器通信进入静音模式
14)从静音模式中唤醒
2、关于帧格式
1)起始位
2)一个数据字(8或9位) 最开始输出的是低位
3)停止位表示传输完成 停止位有0.5、1、1.5、2位
4)这个接口使用一个分数波特率发生器:12位尾数和4位小数
5)一个状态寄存器(USART_SR)
6)数据寄存器(USART_DR)
7)波特率寄存器(USART_BRR):12位尾数和4位小数
8)一个保证时间寄存器(USART_GTRP) 主要是在智能卡模式下
3、USART特性描述
1)数据发送字长可以选择为8位或9位,这个由USART_CR1寄存器决定
2)在开始位时,TX引脚为低;在结束位时,TX引脚为高
3)空闲帧
4)中断帧
5)设置发送使能位(TE)后,会发送一个空闲帧
4、可配置的停止位
1)停止位的位数控制寄存器2的12/13位来控制
2)默认停止位是1位 还有2位、0.5位、1.5位用于其他模式
3)空闲帧传输时包括停止位
4)中断帧是10或11个低比特+停止位 不会传输超过11个低位的比特
5、编程的步骤
1)通过将USART_CR1寄存器的UE位写1使能USART
2)写USART_CR1的M位来确定字长
3)配置USART_CR2寄存器确定停止位的长度
4)如果要进行多缓冲区通信时,在USART_CR3寄存器中配置DMA使能。
就如同多缓冲区通信那样配置DMA。
5)配置USART_BRR寄存器确定波特率
6)配置USART_CR1寄存器的TE位为1,使其在第一次数据发送时发送空闲帧
7)将要发送的数据写在USART_DR寄存器中(这样会清除TXE位)。
在单缓冲区中发送数据就重复这个操作。
8)将最后一个数据写进USART_DR寄存器后,等待TC=1。
这个可以保证最后一帧传输完了。
这个操作是有必要的,因为当USART被禁止或者进入停机模式时,可以避免
最后一帧被打断。
6、单字节通信
1)每当数据被写进数据寄存器后,TXE位都会被清掉。
2)TXE位由硬件设置,并且它可以保证:
a.数据已经从TDR转移到移位寄存器,并且数据发送已经开始了
b.TDR寄存器为空
c.下一个数据可以写在USART_DR寄存器中而不需要覆盖先前的数据。
3)如果TXEIE位被设置为1,这个位将产生一个中断。
4)当进行传输时,对USART_DR寄存器的写指令将数据存储在TDR寄存器中,并且在传输完成时数据会被复制到移位寄存器中。
5)当没有发生传输时,对USART_DR寄存器的写指令直接将数据存储在移位寄存器中,传输开始,并且TXE位立刻被置1。
6)如果在停止位后发送一帧数据,并且TXE位被设置了,TC位就会变高。
如果USART_CR1寄存器中的TCIE位被设置了,就会产生一个中断。
7)在把最后一个数据写进USART_CR1时,若要禁用USRAT或让微控制器进入
低功耗模式时必须等待TC=1。
8)TC位可通过如下的软件方法进行清除
a.读一下USART_SR寄存器
b.写一下USART_DR寄存器
c.TC位也可以通过写0来清除。这种清除方式仅仅建议在多缓冲通信的时候使用。
7、关于接收
1)USART可以接收8位或9位的数据,这取决于USART_CR1寄存器的M位。
2)在USART中,当检测到一个特定的样本序列时,起始位被检测到。
3)在USART接收的过程中,数据首先在RX引脚上在最低有效位上移动。
在这种模式下,USART_DR寄存器由内部总线和接收移位寄存器中间的缓冲区(RDR)组成。
8、接收程序编写过程
1)通过将USART_CR1寄存器的UE位写1使能USART。
2)写USART_CR1寄存器的M位确定接收字的长度。
3)写USART_CR2寄存器确定停止位。
4)如果要进行多缓冲区通信,在USART_CR3寄存器中使能DMA。
5)写USART_BRR配置接收波特率。
6)在USART_CR1寄存器中将RE写为1,这样可以在接收的过程中搜索起始位。
9、当接收到一个字符时
1)RXNE位被设置为1。它表示移位寄存器的内容被转移到RDR寄存器中。换句话说,数据被接受了并且可以读数据了。
2)如果RXNE位被置位了就会产生一个中断。
3)如果在接收的过程中如果检测到帧错误、噪声或溢出错误,错误标志位就被置位了。
4)在多缓冲区模式中,每接收一个字节后,RXNE就被置位,DMA读取数据寄存器后就会把位清掉。
5)在单缓冲模式下,通过软件读取USART_DR寄存器可以清除RXNE位。通过写0也可以清除RXNE标志位。在接收下一个字符之前必须要清除RXNE标志位以避免溢出错误。
6)在接收数据的过程中,RE位不能被重置。在接收的过程中,如果RE位被禁用的话,当前接收的字节会中止。
10、分割字符
当接收一个分割字符时,USART将其作为帧错误处理。
11、空闲字符
12、溢出错误
当在接收一个字符时,如果RXNE位没有被清除就会产生溢出错误。在RXNE位没有被清除之前,数据不能从移位寄存器转移至RDR寄存器。
13、当产生溢出中断时
1)ORE位会被置位
2)RDR寄存器里的内容会丢失。当读USART_DR寄存器时,以前的数据还是可用的。
3)移位寄存器会被覆盖。在这之后,接收到的数据都会丢失。
4)如果设置了RXNEIE位或者同时设置了EIE和DMA位,就会产生一个中断。
5)先对USART_SR寄存器进行一个读操作,然后对USART_DR寄存器进行一个读操作,ORE位就会被清除掉。
6)如果ORE位被置位,表示至少有一个数据丢失了。这里有两种可能性:
a)如果RXNE=1,最后的有效数据将存储在接收寄存器RDR中,并且可以读取。
b)如果RXNE=0。
14、噪声错误
15、帧错误
16、在接收期间可配置的停止位
17、分数波特率发生器
1)发送和接收的波特率会被设置为相同的值,波特率值由USARTDIV的尾数和分数编程。
2)USARTDIV的值由USART_BRR寄存器配置。
18、如何从USART_BRR寄存器获取USARTDIV的值
例子1:
关于波特率误差的计算
误差=(计算的波特率 - 期望波特率)/期望波特率
19、串口接收对时钟偏差的容忍
只有总系统时钟的偏差比USART接收的偏差小时,USART的异步接收才能正常工作。
20、多机通信
21、空闲线检测
当RWU位被写为1时,USART进入静音模式。
22、地址符号检测
在这种模式下,如果MSB=1,字节将被认为是地址,否则被认为数据。
23、奇偶控制
27.3.8 局域互联网络模式(LIN)
27.3.9 USART 同步模式
27.3.10 单线半双工通信
27.3.13 使用DMA进行连续通信
27.4 USART 中断
27.5 USART 模式配置
27.6 USART 寄存器 寄存器一般都是32位
1)Status register(USART_SR)状态寄存器
2)Data register(USART_DR)数据寄存器
3)Baud rate register(USART_BRR)波特率寄存器
4)Control register 1(USART_CR1)控制寄存器1
5)Control register 2(USART_CR2)控制寄存器2
6)Control register 3(USART_CR3)控制寄存器3
7)Guard time and prescaler register(USART_GTPR)保护时间和预分频寄存器
8)USART寄存器图
关键的函数:
1、void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
这个函数主要用来处理中断
2、UART_Receive_IT(huart);
这个函数在被包含在HAL_UART_IRQHandler(UART_HandleTypeDef *huart)函数里
这个函数首先判断数据字长是8位还是9位
疑问:
函数是怎么进入HAL_UART_IRQHandler(UART_HandleTypeDef *huart)这个函数
//=======================编程相关===============================//
1、在初始化一个串口时,先初始化与MCU无关的串口协议,再初始化与MCU相关的串口引脚
2、初始化一个外设,无非要初始化两方面的内容:一方面是相关协议;另一方面就是引脚相关了。
//========================串口发送程序===========================//
1、串口发送程序中很重要的一个寄存器是DR(数据寄存器)
从图中可以看出数据的流向
1)首先把数据往DR里写
2)DR里的数据先转移到TDR(发送数据寄存器)中
3)TDR的数据再转移到Transmit Shfit Register中
4)Transmit Shfit Register的数据最后通过TX口发送出去
//====================串口接收程序=======================//
1、我认为最关键的一点是:找到接收程序的入口
1)接收程序的入口函数是USART1_IRQHandler函数
2)在main函数里没有USART1_IRQHandler函数,如何才能进入该函数呢?
3)在启动文件里已经配置了向量表,向量表里存储的内容便是中断或异常程序的入口地址。
当发生与USART1相关的异常或中断时,PC指针就会移到USART1_IRQHandler函数的入口地址,执行相关程序。
2、第二个关键点是:理清楚USART1_IRQHandler函数里的一层层函数嵌套关系
3、串口接收中断程序中有一个关键函数
/**
* @brief Receives an amount of data in non blocking mode.
* @note When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
* the received data is handled as a set of u16. In this case, Size must indicate the number
* of u16 available through pData.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @param pData Pointer to data buffer (u8 or u16 data elements).
* @param Size Amount of data elements (u8 or u16) to be received.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
有了这个函数才会开启中
//接收程序遇到的第二个问题
串口在接收的过程中只能接收一次
原因:在中断结束后,还是要再次设置接收中断函数
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
而且这个函数放的位置也有讲究
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
while(HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY);
while(HAL_UART_Receive_IT(&huart1, huart1_RxBuffer, 1) != HAL_OK);
/* USER CODE END USART1_IRQn 1 */
}
以上两条语句参考的是正点原子程序
另外在接收和发送程序的过程中,最好加一些延时,不然会卡住。