合宙AIR001 HAL库 串口接收空闲中断+DMA

合宙AIR001 HAL库 串口接收空闲中断+DMA

HAL库嘛,与STM32操作基本一致,下面是流程

初始化UART

这里用的是串口1,所以以下都是串口1的初始化

首先创建串口1的操作句柄

然后使能串口1的时钟与配置串口1的各项参数

UART_HandleTypeDef huart1;        // uart句柄
__HAL_RCC_USART1_CLK_ENABLE();               // 使能uart1时钟
huart1.Instance = USART1;                    // USART1
huart1.Init.BaudRate = 460800;               // 波特率
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据长度
huart1.Init.StopBits = UART_STOPBITS_1;      // 1位停止位
huart1.Init.Parity = UART_PARITY_NONE;       // 无校验位
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控
huart1.Init.Mode = UART_MODE_TX_RX;          // 收发模式

调用HAL库串口初始化函数进行串口初始化

HAL_UART_Init(&huart1);

配置串口1中断,中断号可以在air001_dev.h头文件中找到

如下图

image-20230818134448665

    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); // 抢占优先级0,子优先级0
    HAL_NVIC_EnableIRQ(USART1_IRQn);         // 使能串口1中断

复用GPIO复用功能

此处使用PF0与PF1作为串口1的RX与TX脚,引脚功能可以看手册

image-20230818142847553

首先使能GPIOF的时钟

__HAL_RCC_GPIOF_CLK_ENABLE(); // 使能GPIOF时钟

创建GPIO操作句柄

GPIO_InitTypeDef GPIO_InitStruct;

复用PF0与PF1为串口的输入输出

GPIO复用功能表可以看手册中的端口复用功能列表

image-20230818143346801

image-20230818143425323

    // PF0复用为USART1_RX
    GPIO_InitStruct.Pin = GPIO_PIN_0;                  // PF1
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;            // 推挽输出
    GPIO_InitStruct.Pull = GPIO_PULLUP;                // 上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
    GPIO_InitStruct.Alternate = GPIO_AF8_USART1;       // 复用为USART1_RX
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

    // PF1复用为USART1_TX
    GPIO_InitStruct.Pin = GPIO_PIN_1;                  // PF1
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;            // 推挽输出
    GPIO_InitStruct.Pull = GPIO_PULLUP;                // 上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
    GPIO_InitStruct.Alternate = GPIO_AF8_USART1;       // 复用为USART1_TX
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

到这里串口中断与GPIO部分就初始化完成了,下面初始化DMA部分

初始化DMA

同样的步骤,与STM32基本无异

首先使能DMA1时钟

__HAL_RCC_DMA_CLK_ENABLE();

创建DMA操作句柄

DMA_HandleTypeDef hdma_usart1_rx; // DMA句柄

配置DMA通道,模式,方向等信息

hdma_usart1_rx.Instance = DMA1_Channel1;                       // DMA1通道1
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;          // 外设到内存 方向
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;              // 外设非增量模式
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;                  // 内存增量模式
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; // 外设数据宽度为8位
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;    // 内存数据宽度为8位
hdma_usart1_rx.Init.Mode = DMA_NORMAL;                         // 正常模式
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;         // 优先级为非常高

调用接口初始化DMA

HAL_DMA_Init(&hdma_usart1_rx);

这里要注意,不要忘记把DMA与串口进行关联,不然读不到数据

__HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx);                 // 关联串口1的hdmarx
HAL_DMA_ChannelMap(&hdma_usart1_rx, DMA_CHANNEL_MAP_USART1_RX); // DMA与UART关联

DMA初始化完成

编写串口中断处理函数

首先找到startup_air_dev.s汇编启动代码,找到里面的串口1中断函数名,如下图

image-20230818135414385

复制此名称,在air001xx_it.c中编写中断处理函数

这里要注意,在中断处理函数最后一定要加上清除溢出错误这部分(读SR与DR寄存器),不然会导致上电就产生一个硬错误中断(暂时不清楚为啥)

中断内尽量简洁,不要有复杂耗时操作,这里是置位标志位,然后在main函数中处理

void USART1_IRQHandler(void) // 串口1中断
{
  if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) // 判断是否为空闲中断
  {
    UART1_FLAG = 1;
    __HAL_UART_CLEAR_IDLEFLAG(&huart1);
  }
  HAL_UART_IRQHandler(&huart1);
  // 清除溢出错误
  __HAL_UART_CLEAR_OREFLAG(&huart1);
}

开启串口接收中断与DMA

初始化完成串口与DMA之后使能串口空闲中断,开启串口DMA接收

__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);     // 开启串口1空闲中断
HAL_UART_Receive_DMA(&huart1, UART_BUF, RX_NUM); // 串口1接收DMA

这事如果有数据发送完成串口空闲,会触发串口空闲中断,中断中置位标志位,

在main函数while循环中判断标志位进行处理即可,先停止DMA接收然后处理完成后重新使能串口DMA接收

  while (1)
  {
    if (UART1_FLAG)
    {
      HAL_UART_DMAStop(&huart1);
      debug_print("%s", UART_BUF); // 打印串口接收到的数据
      APP_UART_Analysis();
      memset(UART_BUF, 0, sizeof(UART_BUF));
      HAL_UART_Receive_DMA(&huart1, UART_BUF, RX_NUM); // 串口1接收DMA
      UART1_FLAG = 0;
    }
}

END

QQ录屏20230818150853

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论
使用DMA空闲中断接收不定长数据的步骤如下: 1. 初始化串口DMA:配置串口接收模式,启用DMA传输;配置DMA为循环模式,传输大小为1字节,传输方向为从串口接收数据到内存。 2. 开启DMA传输:调用DMA启动函数启动DMA传输。 3. 开启串口接收中断:调用串口中断使能函数,开启空闲中断。 4. 在空闲中断中处理数据:当DMA传输完成并且串口没有接收到新数据时,说明接收完成,可以在空闲中断中处理接收到的数据。 下面是一个简单的例子: ```c #include "stm32f4xx_hal.h" #define RX_BUF_SIZE 256 UART_HandleTypeDef huart1; DMA_HandleTypeDef hdma_usart1_rx; uint8_t rx_buf[RX_BUF_SIZE]; uint8_t rx_len = 0; uint8_t rx_flag = 0; void UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } hdma_usart1_rx.Instance = DMA2_Stream2; hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4; hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart1_rx.Init.Mode = DMA_CIRCULAR; hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW; hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(&huart1, hdmarx, hdma_usart1_rx); HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); HAL_UART_Receive_DMA(&huart1, rx_buf, RX_BUF_SIZE); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { rx_len += RX_BUF_SIZE / 2 - hdma_usart1_rx.Instance->NDTR; } } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { __HAL_UART_CLEAR_PEFLAG(&huart1); HAL_UART_Receive_DMA(huart, rx_buf, RX_BUF_SIZE); } } void DMA2_Stream2_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_usart1_rx); } void HAL_UART_IdleCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { if (hdma_usart1_rx.Instance->NDTR == RX_BUF_SIZE) { rx_len = RX_BUF_SIZE; rx_flag = 1; } else { rx_len = RX_BUF_SIZE - hdma_usart1_rx.Instance->NDTR; } } } int main(void) { HAL_Init(); UART_Init(); while (1) { if (rx_flag) { // 处理接收到的数据 rx_flag = 0; } } } ``` 在上面的例子中,我们使用了循环DMA传输模式,当接收到一定数量的数据后,将触发空闲中断,并在空闲中断中处理接收到的数据。同时,在DMA传输完成和空闲中断中,我们使用了两个不同的回调函数,分别处理DMA传输完成和空闲中断的事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kongbai_w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值