STM32之CubeL4(二)---USART + DMA + HAL

一、UART简介

1.1 串口通信简介

一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种,它们的区别是:

通信方式 并行通信 串行通信
传输原理 数据各个位同时传输 数据按位顺序传输
优点 传输速度快 占用引脚资源少
缺点 占用引脚资源多 传输速度相对较慢

在集成电路芯片的内部、同一硬件板上各部件之间等(比如指令总线、数据总线、系统总线等)的数据传送通常采用并行通信方式;硬件板与外接设备间为了减少引脚占用常采用串行通信方式,这里我们重点关注开发板与外界设备的通信,故继续看串行通信的分类:

串行通信 分类 特点
工作模式 单工模式
半双工模式
全双工模式
通讯双方一方为发送端,另一方则固定为接收端,数据只能单向传输;
通讯双方既可以发射也可以接收,但是接收和发射不能同时进行;
双方既可以发射也可以接收,且能在同一时刻进行发送和接收操作;
同步方式 同步通信
异步通信
带时钟同步信号传输,双方靠时钟同步,比如:SPI,IIC通信接口;
不带时钟同步信号,双方需规约好传输速率,比如:UART;

在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符(用于数据同步的信号位,比如起始位、停止位、奇偶校验位等),所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。

常见的串行通信接口汇总如下:

通信协议 引脚说明 工作模式 同步方式
UART TXD:发送端
RXT:接收端
GND:共地
全双工 异步通信
SPI SCK:同步时钟
MISO:主机输入,从机输出
MOSI:主机输出,从机输入
全双工 同步通信
I2C / IIC SCK:同步时钟
SDA:数据输入/输出端
半双工 同步通信

1.2 USART简介

USART全称为Universal Synchronous Asynchronous Receiver and Transmitter,通用同步-异步接收发射器,怎么既同步又异步呢?实际上USART既支持同步通信也支持异步通信,如果作为异步通信就是UART(Universal Asynchronous Receiver and Transmitter),作为同步通信相比UART多了一根时钟同步信号线,USART相当于UART的增强版,二者的关系如下:
USART与UART区别
由上图可以看出USART以同步方式通信需要时钟同步信号,但不需要额外的起始、停止位,可以实现更快的传输速度。但USART的支持范围更有限,后面我们主要介绍其作为异步通信也即UART通信。

串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口,通讯双方的数据包格式要规约一致才能正常收发数据。UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据位+1位奇偶校验位(可选)+1位停止位,如下图所示:
串口异步通讯格式帧
奇偶校验位分为奇校验和偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。

STM32L475中USART结构框图如下图所示:
STM32L475 USART结构框图
这个框图分成上、中、下三个部分。本文大概讲述一下各个部分的内容,具体的可以看《SMT32L475VE Reference manual》中的描述。

框图的上部分,数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去(感觉上面框图箭头有误)。

框图的中间部分,UART的发送移位寄存器和接收移位寄存器分别由发送控制器和接收控制器来进行控制,而发送控制器和接收控制器主要受硬件流控信号和时钟信号控制。硬件流控nRTS / nCTS (Request To Send / Clear To Send,n表示低电平有效)可以控制数据发送、接收的速率,防止因数据缓冲区溢出而导致部分数据丢失的情况出现,在很多情况下为了减少引脚数量,都省去了硬件流控引脚而采用软件流控方式以达到同样的目的。针对USART的同步通信方式还有一个发送器时钟输出引脚CK,仅适用于同步模式。

框图的下部分,接收控制器、发送控制器都有一个进入的箭头,分别连接到接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。

上面提到了波特率,讲波特率之前首先了解一下通讯速率,通讯速率通常是以比特率来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易和比特率混淆的概念是“波特率”,它表示每秒传输了多少码元。码元是通讯信号调制的概念,时间间隔相同的符号来表示一个二进制数字,这样的信号就称为码元。如常见的通讯传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;若传输中,有0V、2V、4V和6V分别表示00、01、10、11,那么每个码元可以表示四种状态,两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率,其实二者是有区别的。 异步通讯由于没有时钟信号,所以两个通讯设备需要规约好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为4800,9600,115200。

我们的STM32L475开发板支持的串口模式及特征如下:
STM32L475 USART模式特征

1.3 USART配置

前面介绍了UART数据帧的格式和波特率的概念,UART的初始化主要是确定数据帧格式和双方通信的波特率,下面先介绍UART初始化结构体的定义:

// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
  * @brief UART Init Structure definition
  */
typedef struct
{
   
  uint32_t BaudRate;                  /*!< This member configures the UART communication baud rate.
                                           The baud rate register is computed using the following formula:
                                           LPUART:
                                           =======
                                              Baud Rate Register = ((256 * lpuart_ker_ckpres) / ((huart->Init.BaudRate)))
                                           where lpuart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable)
                                           UART:
                                           =====
                                           - If oversampling is 16 or in LIN mode,
                                              Baud Rate Register = ((uart_ker_ckpres) / ((huart->Init.BaudRate)))
                                           - If oversampling is 8,
                                              Baud Rate Register[15:4] = ((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[15:4]
                                              Baud Rate Register[3] =  0
                                              Baud Rate Register[2:0] =  (((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[3:0]) >> 1
                                           where uart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable) */

  uint32_t WordLength;                /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref UARTEx_Word_Length. */

  uint32_t StopBits;                  /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref UART_Stop_Bits. */

  uint32_t Parity;                    /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref UART_Parity
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */

  uint32_t Mode;                      /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref UART_Mode. */

  uint32_t HwFlowCtl;                 /*!< Specifies whether the hardware flow control mode is enabled
                                           or disabled.
                                           This parameter can be a value of @ref UART_Hardware_Flow_Control. */

  uint32_t OverSampling;              /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to f_PCLK/8).
                                           This parameter can be a value of @ref UART_Over_Sampling. */

  uint32_t OneBitSampling;            /*!< Specifies whether a single sample or three samples' majority vote is selected.
                                           Selecting the single sample method increases the receiver tolerance to clock
                                           deviations. This parameter can be a value of @ref UART_OneBit_Sampling. */

#if defined(USART_PRESC_PRESCALER)
  uint32_t ClockPrescaler;            /*!< Specifies the prescaler value used to divide the UART clock source.
                                           This parameter can be a value of @ref UART_ClockPrescaler. */
#endif /* USART_PRESC_PRESCALER */

} UART_InitTypeDef;

/** @defgroup UART_Stop_Bits   UART Number of Stop Bits
  */
#define UART_STOPBITS_0_5                    USART_CR2_STOP_0                     /*!< UART frame with 0.5 stop bit  */
#define UART_STOPBITS_1                     0x00000000U                           /*!< UART frame with 1 stop bit    */
#define UART_STOPBITS_1_5                   (USART_CR2_STOP_0 | USART_CR2_STOP_1) /*!< UART frame with 1.5 stop bits */
#define UART_STOPBITS_2                      USART_CR2_STOP_1                     /*!< UART frame with 2 stop bits   */

/** @defgroup UART_Parity  UART Parity
  */
#define UART_PARITY_NONE                    0x00000000U                        /*!< No parity   */
#define UART_PARITY_EVEN                    USART_CR1_PCE                      /*!< Even parity */
#define UART_PARITY_ODD                     (USART_CR1_PCE | USART_CR1_PS)     /*!< Odd parity  

/** @defgroup UART_Mode UART Transfer Mode
  */
#define UART_MODE_RX                        USART_CR1_RE                    /*!< RX mode        */
#define UART_MODE_TX                        USART_CR1_TE                    /*!< TX mode        */
#define UART_MODE_TX_RX                     (USART_CR1_TE |USART_CR1_RE)    /*!< RX and TX mode */

/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control
  */
#define UART_HWCONTROL_NONE                  0x00000000U                          /*!< No hardware control       */
#define UART_HWCONTROL_RTS                   USART_CR3_RTSE                       /*!< Request To Send           */
#define UART_HWCONTROL_CTS                   USART_CR3_CTSE                       /*!< Clear To Send             */
#define UART_HWCONTROL_RTS_CTS               (USART_CR3_RTSE | USART_CR3_CTSE)    /*!< Request and Clear To Send */

/** @defgroup UART_Over_Sampling UART Over Sampling
  */
#define UART_OVERSAMPLING_16                0x00000000U         /*!< Oversampling by 16 */
#define UART_OVERSAMPLING_8                 USART_CR1_OVER8     /*!< Oversampling by 8  */

/** @defgroup UART_OneBit_Sampling UART One Bit Sampling Method
  */
#define UART_ONE_BIT_SAMPLE_DISABLE         0x00000000U         /*!< One-bit sampling disable */
#define UART_ONE_BIT_SAMPLE_ENABLE          USART_CR3_ONEBIT    /*!< One-bit sampling enable  */

UART初始化结构体UART_InitTypeDef中的成员配置在CubeMX配置中会涉及,但多数可以使用默认配置,如果默认配置满足不了需求,则需要了解结构体成员有哪些配置选项,各配置选项有何意义。

前面介绍HAL库提供了更高的抽象,将每类外设抽象为一个句柄,对实例化句柄的操作相当于对该外设的操作,这就类似于C++中面向对象编程的思想。前面介绍GPIO更偏底层,只提供了初始化结构体,不需要句柄结构体,所以并没有介绍句柄结构体是怎样实现的?下面以UART为例介绍其句柄结构体定义如下:

// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
  * @brief  UART handle Structure definition
  */
typedef struct __UART_HandleTypeDef
{
   
  USART_TypeDef            *Instance;                /*!< UART registers base address        */

  UART_InitTypeDef         Init;                     /*!< UART communication parameters      */

  UART_AdvFeatureInitTypeDef AdvancedInit;           /*!< UART Advanced Features initialization parameters */

  uint8_t                  *pTxBuffPtr;              /*!< Pointer to UART Tx transfer Buffer */

  uint16_t                 TxXferSize;               /*!< UART Tx Transfer size              */

  __IO uint16_t            TxXferCount;              /*!< UART Tx Transfer Counter           */

  uint8_t                  *pRxBuffPtr;              /*!< Pointer to UART Rx transfer Buffer */

  uint16_t                 RxXferSize;               /*!< UART Rx Transfer size              */

  __IO uint16_t            RxXferCount;              /*!< UART Rx Transfer Counter           */

  uint16_t                 Mask;                     /*!< UART Rx RDR register mask          */

#if defined(USART_CR1_FIFOEN)
  uint32_t                 FifoMode;                 /*!< Specifies if the FIFO mode is being used.
                                                          This parameter can be a value of @ref UARTEx_FIFO_mode. */

  uint16_t                 NbRxDataToProcess;        /*!< Number of data to process during RX ISR execution */

  uint16_t                 NbTxDataToProcess;        /*!< Number of data to process during TX ISR execution */
#endif /*USART_CR1_FIFOEN */

  void (*RxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Rx IRQ handler   */

  void (*TxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Tx IRQ handler   */

  DMA_HandleTypeDef        *hdmatx;                  /*!< UART Tx DMA Handle parameters      */

  DMA_HandleTypeDef        *hdmarx;                  /*!< UART Rx DMA Handle parameters      */

  HAL_LockTypeDef           Lock;                    /*!< Locking object                     */

  __IO HAL_UART_StateTypeDef    gState;              /*!< UART state information related to global Handle management
                                                          and also related to Tx operations.
                                                          This parameter can be a value of @ref HAL_UART_StateTypeDef */

  __IO HAL_UART_StateTypeDef    RxState;             /*!< UART state information related to Rx operations.
                                                          This parameter can be a value of @ref HAL_UART_StateTypeDef */

  __IO uint32_t                 ErrorCode;           /*!< UART Error code                    */

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Tx Half Complete Callback        */
  void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Tx Complete Callback             */
  void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Rx Half Complete Callback        */
  void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Rx Complete Callback             */
  void (* ErrorCallback)(struct __UART_HandleTypeDef *huart);             /*!< UART Error Callback                   */
  void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Abort Complete Callback          */
  void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
  void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart)
### 回答1: 好的,下面是关于STM32 USART发送和接收使用DMA的回答: DMA (Direct Memory Access)是一种在不占用CPU的情况下直接进行数据传输的技术。对于STM32USART模块,使用DMA可以大大减轻CPU的负担,提高数据传输效率。 首先,需要配置USARTDMA。可以使用STM32CubeMX工具来快速生成初始化代码。在配置USART时,需要设置USART的波特率、数据位、停止位和校验位等参数。在配置DMA时,需要设置DMA的传输方向、数据长度、传输模式、优先级和中断等参数。 接下来,可以使用HAL库提供的函数来启动DMA传输。在发送数据时,可以使用函数HAL_UART_Transmit_DMA()启动DMA传输,该函数会将数据存储在指定的缓冲区中,并将缓冲区的地址传递给DMA进行传输。在接收数据时,可以使用函数HAL_UART_Receive_DMA()启动DMA传输,该函数会将接收到的数据存储在指定的缓冲区中,并将缓冲区的地址传递给DMA进行传输。 在DMA传输完成后,可以使用HAL库提供的回调函数来进行处理。对于发送完成,可以使用函数HAL_UART_TxCpltCallback();对于接收完成,可以使用函数HAL_UART_RxCpltCallback()。 需要注意的是,在使用DMA进行数据传输时,需要确保缓冲区的大小足够大,以便存储需要传输的数据。 希望这个回答能够帮助你了解STM32 USART发送和接收使用DMA的相关知识。如果你有任何其他问题,可以随时提出。 ### 回答2: STM32是一种基于ARM Cortex-M内核的微控制器。在STM32中,USART是其中一种通信接口,它可以用于串口通讯,支持同步和异步模式,而DMA是指直接存储器访问,是一种通过硬件优化(减少中央处理器的干预)直接访问外设(如USART)数据的技术。 在STM32中,USART发送和接收都可以使用DMA技术来实现。DMA可以减少CPU的干预,提高数据传输效率。使用DMA时,USART的数据传输不再需要中断和CPU参与,而是通过DMA控制器直接完成。 在USART发送中,使用DMA可以提高发送速率和可靠性,并减轻CPU的 burden。具体实现步骤包括:初始化USARTDMA,选择传输方向并设置数据长度和地址,启动DMA传输,并检查传输是否完成。 在USART接收中,同样使用DMA可以提高接收速度和减轻CPU压力,具体操作步骤和发送时类似,首先初始化USARTDMA,设置数据长度和地址,选择传输方向并启动DMA传输,然后等待数据接收完成,在回调函数中处理接收到的数据。 总之,使用DMA技术可以极大地提高USART的数据传输效率和可靠性。但也要注意在配置和使用时遵循相关规范,避免发生错误。 ### 回答3: STM32是一类广泛应用于微控制器中的芯片,其中USART是一种通信协议,可以实现串行通信。在STM32上,USART不仅支持单字节发送和接收,还可以使用DMA实现数据的快速传输。在本文中,我们将探讨STM32USART发送和接收时如何使用DMA。 首先,让我们来了解一下DMA(直接存储器访问)的概念。DMA是一种高效的数据传输方式,允许数据在外设和内存之间直接传输,而不需要CPU的干预。在使用DMA进行数据传输时,我们需要设置源地址、目的地址和传输长度,然后启动DMA传输,再次目标数据传输完成,DMA会自动发出传输完成的中断信号。 在STM32中,我们可以通过代码来配置DMA。首先,我们需要定义源地址、目的地址和传输长度,然后将它们写入DMA控制器的相关寄存器。为了使用USARTDMA进行数据传输,我们还需要将DMAUSART相关的寄存器连接起来。在最后一步,我们需要启动DMA传输,等待传输完成中断信号。 使用DMA进行USART发送和接收操作,需要针对每个操作分别进行配置。在发送方面,我们需要设置USART的数据寄存器作为源地址,DMA的数据寄存器作为目的地址,并将传输长度设置为待发送的字节数。在接收方面,我们需要设置USART的数据寄存器作为目的地址,DMA的数据寄存器作为源地址,并将传输长度设置为接收缓冲区的大小。 总之,使用DMA进行USART发送和接收操作可以大大提高数据传输效率和操作速度。但是,需要注意的是,在进行DMA配置时需要仔细处理相关寄存器和缓冲区,否则可能会导致数据传输错误。因此,在使用DMA进行通信之前,我们应该对USARTDMA的相关概念有一定的理解,并根据具体的需求进行合理的配置。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流云IoT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值