STM32G070串口DMA&IDEL配置
0、概述
在配置STM32G070RBT6进行串口通信功能时,如果尝试开启空闲中断进行不定长数据接收时,串口一旦接收到数据,就会一直卡在串口中断,无法进行其他操作。配置空闲中断的方法是按照F系列的HAL库配置方法,本次使用的HAL版本是STM32Cube_FW_G0_V1.5.0。
1、解决方法
在查阅资料后,发现F4系列高版本的HAL库串口中断处理方法中,如果开启了串口空闲中断,中断回调函数里没有专门针对空闲中断处理的函数。按照HAL库给的方法,通过DMA接收数据并判断是否有空闲中断事件发生。其中,HAL库的中断函数代码参考如下:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart){uint32_t isrflags = READ_REG(huart->Instance->ISR);uint32_t cr1its= READ_REG(huart->Instance->CR1);uint32_t cr3its= READ_REG(huart->Instance->CR3);uint32_t errorflags;uint32_t errorcode;···············省略一部分···············/* Check current reception Mode :If Reception till IDLE event has been selected : *///检查是否产生空闲中断事件if ((huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)&& ((isrflags & USART_ISR_IDLE) != 0U)&& ((cr1its & USART_ISR_IDLE) != 0U)){//清除空闲中断标志位__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_IDLEF);/* Check if DMA mode is enabled in UART *///检查是否使用了串口DMA接收中断if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)){/* DMA mode enabled *//* Check received length : If all expected data are received, do nothing,(DMA cplt callback will be called).Otherwise, if at least one data has already been received, IDLE event is to be notified to user */uint16_t nb_remaining_rx_data = (uint16_t) __HAL_DMA_GET_COUNTER(huart->hdmarx);if ((nb_remaining_rx_data > 0U)&& (nb_remaining_rx_data < huart->RxXferSize)){/* Reception is not complete */huart->RxXferCount = nb_remaining_rx_data;/* In Normal mode, end DMA xfer and HAL UART Rx process*/if (HAL_IS_BIT_CLR(huart->hdmarx->Instance->CCR, DMA_CCR_CIRC)){/* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);/* Disable the DMA transfer for the receiver request by resetting the DMAR bitin the UART CR3 register */ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);/* At end of Rx process, restore huart->RxState to Ready */huart->RxState = HAL_UART_STATE_READY;huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);/* Last bytes received, so no need as the abort is immediate */(void)HAL_DMA_Abort(huart->hdmarx);}#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)/*Call registered Rx Event callback*/huart->RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));#else/*Call legacy weak Rx Event callback*///即使串口DMA没有接收完,产生空闲中断时,也会触发HAL_UARTEx_RxEventCallback回调函数HAL_UARTEx_RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));#endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */}return;}···············省略一部分···············
以上代码可以看出,新版本的HAL库将DMA接收中断和IDEL空闲中断结合起来用于接收不定长数据,当产生空闲中断时,HAL_UART_IRQHandler函数会检查是否使能了DMA接收中断:
Check received length : If all expected data are received, do nothing,(DMA cplt callback will be called).Otherwise, if at least one data has already been received, IDLE event is to be notified to user
从上面的解释可以看出,如果产生了空闲中断,检查DMA设定的数据长度是否接收满了,如果数据长度满了则会产生DMA中断,如果还没有接收满,但是已经有至少一字节数据被接收了,则会通过RxEventCallback回调函数通知用户来处理数据。
2、实验验证
2.1 STM32CubeMX配置
–>时钟配置:特别注意一定要对串口的时钟单独配置
–>串口DMA配置:Normal模式,其他默认即可
2.2 主要部分代码展示
main函数部分
MX_GPIO_Init();MX_DMA_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_SIZE);//_HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);//__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);//__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//不在主函数里使能空闲中断,程序也正常运行,建议不用添加/* USER CODE END 2 */
红色部分为使能串口接收中断和DMA&&空闲中断,HAL_UARTEx_ReceiveToIdle_DMA函数参数为串口句柄、数据缓存数组、DMA接收的最大长度
usart部分代码
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){if (huart->Instance == USART2){oldPos = newPos; // Update the last position before copying new data/* If the data in large and it is about to exceed the buffer size, we have to route it to the start of the buffer* This is to maintain the circular buffer* The old data in the main buffer will be overlapped*/if (oldPos+Size > MainBuf_SIZE) // If the current position + new data size is greater than the main buffer{uint16_t datatocopy = MainBuf_SIZE-oldPos; // find out how much space is left in the main buffermemcpy ((uint8_t *)MainBuf+oldPos, RxBuf, datatocopy); // copy data in that remaining spaceoldPos = 0; // point to the start of the buffermemcpy ((uint8_t *)MainBuf, (uint8_t *)RxBuf+datatocopy, (Size-datatocopy)); // copy the remaining datanewPos = (Size-datatocopy); // update the position}/* if the current position + new data size is less than the main buffer* we will simply copy the data into the buffer and update the position*/else{memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, Size);newPos = Size+oldPos;}/* start the DMA again */Uart2_SendStr((char*)RxBuf);HAL_UARTEx_ReceiveToIdle_DMA(&huart2, (uint8_t *) RxBuf, RxBuf_SIZE);__HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_HT);}/****************** PROCESS (Little) THE DATA HERE *********************//* Let's say we want to do if IDEL Event happend */}
以上代码可以自定义内容,按照以上程序内容如果一次向DMA发送的数据超过了RxBuf_SIZE,新数据会把旧数据覆盖掉,但是!!!如果一次发送的数据量很大,memcpy拷贝函数操作时会越界,最好不要一次发送上百字节的较长数据流,或者将数据缓冲数组设置大一些,否则回调函数里数组越界将会有不可测的事情发生。
3、总结
还是没有在新版本HAL库中找到能够单独使用IDEL中断函数的用法,以上方法是使用了DMA和IDEL中断进行不定长数据的接收。