合宙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头文件中找到
如下图
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); // 抢占优先级0,子优先级0
HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能串口1中断
复用GPIO复用功能
此处使用PF0与PF1作为串口1的RX与TX脚,引脚功能可以看手册
首先使能GPIOF的时钟
__HAL_RCC_GPIOF_CLK_ENABLE(); // 使能GPIOF时钟
创建GPIO操作句柄
GPIO_InitTypeDef GPIO_InitStruct;
复用PF0与PF1为串口的输入输出
GPIO复用功能表可以看手册中的端口复用功能列表
// 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中断函数名,如下图
复制此名称,在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;
}
}