STM32常用的uart收发轮询、中断、DMA、不定长数据接收

前言

stm32 uart常用的收发有轮询、中断、DMA

轮询方式

  1. 关键函数
    HAL_UART_Receive //UART接收函数
    HAL_UART_Transmit //UART发送函数

  2. 使用如下,直接在while循环里调用这两个函数即可实现固定长度数据接受与发送,缺点是需要CPU主动去轮询,非常消耗CPU资源,轮询属于阻塞方式,CPU一直在这里等着(在没有到达超时时间时)

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    HAL_UART_Receive(&huart1, receiveData, 2, HAL_MAX_DELAY);
    HAL_UART_Transmit(&huart1, receiveData, 2, HAL_MAX_DELAY);
  }

中断方式

  1. 关键函数
    HAL_UART_Transmit_IT //中断方式发送
    HAL_UART_Receive_IT //中断方式接收
    USART1_IRQHandler //USART1全局中断处理函数
    HAL_UART_RxCpltCallback //USART1接收完成回调函数
  2. 使用如下,需要在主程序进入while循环前开启中断接收,由于中断接收是不阻塞CPU的,所以,代码继续执行至while内部。
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, receiveData, 2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    //HAL_UART_Receive(&huart1, receiveData, 2, HAL_MAX_DELAY);
    //HAL_UART_Transmit(&huart1, receiveData, 2, HAL_MAX_DELAY);
  }
  /* USER CODE END 3 */
}

while内部不要写中断接收,因为中断接收属于非阻塞型,代码继续往后走,很有可能本次接收还没接收完毕呢,就又开启下次接收了,数据被覆盖掉。
当RX上有数据时,会触发USART1的中断,进而依据中断向量表走进对应的中断处理函数USART1_IRQHandler,在中断处理函数中调用了HAL_UART_IRQHandler(&huart1)

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

  /* USER CODE END USART1_IRQn 1 */
}

HAL这个函数封装的极其高度,我们不需要再像标准库编程那样去查uart寄存器状态,判断是否有数据,清中断,开启中断等底层操作,只用关心业务逻辑即可。这个中断处理函数里面有各种回调函数,发送完成回调,发送半完成回调,接收完成回调,接收半完成回调等等,这些函数都是弱定义,需要我们自己实现,尽量不要在库函数中添加自己的代码
在这里插入图片描述
我们直接把这个接口复制出来,放在mian.c文件中定义,这里定义把接收到内容直接原路发送出去,同时再次开启接收中断

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART_Transmit_IT(&huart1, receiveData, 2);
  HAL_UART_Receive_IT(&huart1, receiveData, 2);
}

DMA方式

中断方式还是有CPU参与的,uart上有数据时把CPU叫回来,没数据时,CPU去干其他的事,但是还有一种更好的方式就是给CPU请一个小秘书DMA,同时实现不定长数据的接收
在cubemx中开启uart的tx rx DMA传输通道
在这里插入图片描述
如何实现呢,很简单,把之前HAL_UART_Transmit_IT改成HAL_UART_Transmit_DMA,HAL_UART_Receive_IT改成HAL_UART_Receive_DMA即可

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  //HAL_UART_Receive_IT(&huart1, receiveData, 2);
  HAL_UART_Receive_DMA(&huart1, receiveData, 2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
...
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  //HAL_UART_Transmit_IT(&huart1, receiveData, 2);
  //HAL_UART_Receive_IT(&huart1, receiveData, 2);
  HAL_UART_Transmit_DMA(&huart1, receiveData, 2);
  HAL_UART_Receive_DMA(&huart1, receiveData, 2);
}

DMA方式实现不定长度接收

不定长数据的接收主要使用的时空闲中断,即当rx上没有数据时,触发一次DMA传输
关键函数
HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
但是此时的回调函数就不是
HAL_UART_RxCpltCallback 而是 HAL_UARTEx_RxEventCallback
代码实现也很简单

  /* USER CODE BEGIN 2 */
  //HAL_UART_Receive_IT(&huart1, receiveData, 2);
  //HAL_UART_Receive_DMA(&huart1, receiveData, 2);
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
...
...
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  HAL_UART_Transmit_DMA(&huart1, receiveData, Size);
  HAL_UARTEx_ReceiveToIdle_DMA(&huart1, receiveData, 128);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于STM32标准库的UART DMA收发FIFO例程: ```c #include "stm32f10x.h" #include <stdio.h> #define BUFFER_SIZE 64 uint8_t Rx_Buffer[BUFFER_SIZE]; uint8_t Tx_Buffer[BUFFER_SIZE]; uint8_t Rx_Idx = 0; uint8_t Tx_Idx = 0; void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); DMA_DeInit(DMA1_Channel5); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Rx_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5, &DMA_InitStructure); DMA_DeInit(DMA1_Channel4); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Tx_Buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel4, &DMA_InitStructure); USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); DMA_Cmd(DMA1_Channel5, ENABLE); DMA_Cmd(DMA1_Channel4, DISABLE); USART_Cmd(USART1, ENABLE); } uint8_t USART_GetChar(void) { uint8_t ch = Rx_Buffer[Rx_Idx++]; Rx_Idx %= BUFFER_SIZE; return ch; } void USART_PutChar(uint8_t ch) { Tx_Buffer[Tx_Idx++] = ch; Tx_Idx %= BUFFER_SIZE; DMA_Cmd(DMA1_Channel4, ENABLE); } void DMA1_Channel4_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC4)) { DMA_Cmd(DMA1_Channel4, DISABLE); DMA_ClearITPendingBit(DMA1_IT_TC4); } } int main(void) { USART_Config(); while (1) { if (Rx_Idx != 0) { uint8_t ch = USART_GetChar(); USART_PutChar(ch + 1); } } } ``` 这个例程使用了STM32UART1作为串口通信的接口,并且使用了DMA来进行数据的传输。在这个例程中,定义了一个接收缓冲区`Rx_Buffer`和一个发送缓冲区`Tx_Buffer`,分别用于存储接收到的数据和要发送数据。收到的数据会存储到接收缓冲区中,并且会在下一次轮询时被处理,处理完后会将发送数据存储到发送缓冲区中,并启动DMA进行发送
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值