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

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

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

  • 相关阅读:
    Java 多态
    HDFS读写原理
    HDFS详解
    Servlet基础
    Tomcat
    HTTP简介
    JDBC技术
    final、finally和finalize
    java 中的权限修饰符
    进程、线程、线程状态、多线程实现方法
  • 原文地址:https://www.cnblogs.com/QQ2962269558/p/11901272.html
Copyright © 2020-2023  润新知