之前写过不使用DMA的串口接收不定长数据:[stm32]UART串口利用空闲中断接收一帧不定长数据 这里记录一下采用DMA+UART串口方式接收不定长数据 同样使用CUBEMX配置好生成代码(省略)
1.使用串口空闲中断+串口DMA中断方式实现,只不过这里的HAL_UART_Receive_IT()变成DMA方式HAL_UART_Receive_DMA()
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart2,USART2_RXBUF,USART2_RX_SIZE);
__HAL_UART_ENABLE_IT()使能了空闲中断,当接收到一帧数据后就会发生中断 HAL_UART_Receive_DMA()使用DMA的方式开始接收指定长度的数据,并且使能了DMA相关中断,主要是半传输和传输完成中断
2.中断处理 这个中断处理函数会在数据半传输完成和数据全传输完成中断触发后进入,并调用相对应的半传和全传完成回调函数,这里不需要做其他处理,回调函数也不做处理
void DMA1_Stream6_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hdma_usart2_tx);
}
在串口中断处理函数中,判断空闲中断的到来后,调用自定义的回调函数APP_UART_DMARxIdleCpltCallback(),这里的中断函数在空闲中断触发后进入,不会因为每接收1个数据而进入一次,因为 HAL_UART_Receive_DMA()函数中没有开启UART_IT_RXNE数据寄存器非空中断,这是与HAL_UART_Receive_IT()不一样的。
void USART2_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)!=RESET)
{
APP_UART_DMARxIdleCpltCallback(&huart2,&hdma_usart2_rx,USART2_RXBUF);
__HAL_UART_CLEAR_IDLEFLAG(&huart2) ;
}
HAL_UART_IRQHandler(&huart2);
}
3.自定义的空闲中断回调函数
void APP_UART_DMARxIdleCpltCallback(UART_HandleTypeDef*huart,DMA_HandleTypeDef *hdma,uint8_t*buf)
{
uint8_t len=0;
if(huart->Instance==USART2)
{
HAL_UART_DMAStop(&huart2);
len = huart2.RxXferSize - hdma_usart2_rx.Instance->NDTR;
CDC_Transmit_FS(buf,len);
HAL_UART_Receive_DMA(&huart2,buf,USART2_RX_SIZE);
}
}
(重点)进入回调函数后,先停止串口DMA,然后计算接收到的数据长度len,其中 huart2.RxXferSize:为要接收的数据大小,在HAL_UART_Receive_DMA()中定义了 hdma_usart2_rx.Instance->NDTR:串口DMA剩余接收数据长度,这里不再使用huart2.RxXferCount,因为这个数没有在HAL_UART_Receive_DMA()中用到,一直为0 最后调用 HAL_UART_Receive_DMA()重新开始接收数据,下次接收的数据又会从buf[0]开始存放(CDC_Transmit_FS(buf,len)为USB回传数据,注释不用)
注意:要保证接收的每帧数据长度小于在HAL_UART_Receive_DMA()函数中定义的接收数据长度
|