STM32F103之USART学习记录

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 */
}

以上两条语句参考的是正点原子程序

另外在接收和发送程序的过程中,最好加一些延时,不然会卡住。

原文地址:https://www.cnblogs.com/QQ2962269558/p/11901272.html