本工程使用的是STM32F407系列,想要实现DMA的不定长数据接收,在串口数据量不大和工程体量不大的情况下,可以用串口轮询和串口中断来接收数据,而工程量变大而串口数据量变大时,实现UART的DMA发送接收就显得十分必要了,因为串口中断每收到一个byte的数据就会发生中断,这样会非常的消耗单片机的资源。而DMA接受一帧数据才会发生中断,可以极大的节省单片机的资源。
STM32中的代码使用HAL库,底层驱动使用STM32CUBE来生成,下面是重点。
使能USART3接口,到DMA setting中设置UART的DMA通道是什么,注意DMA Request Setting中的Mode是Circular,这样每一次DMA中断完成下一次数据到来前会自动打开DMA请求。同时UART通信接口的传输方式为异步通信。然后就是一些基本操作,这里不再细讲。
初始化代码中我将数据buffer的大小size设置成了1024个byte,同时初始化接收数据大小和BUFFER
/*USART初始化部分*/
#define DATA_MAX 1024
u16_t UART3_RX_NUM = 0;
u8_t UART3_RX_BUFFER[DATA_MAX] = {0};
然后在USER CODE 2下添加如下代码,用来打开UART空闲中断,因为UART进入空闲状态时,表示一帧数据接收结束
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart3,UART_IT_IDLE); //打开中断,接收完一组数据后产生中断
HAL_UART_Receive_DMA(&huart3,UART3_RX_BUFFER,DATA_MAX);
/* USER CODE END 2 */
中断部分我借鉴了rtthread中的部分UART DMA模式的代码,rtthread代码如下
我们可以总结出以下几点,即:
1.判断标志位是否置位
2.失能DMA中断
3.清除空闲中断标志位
4.得到接收的数据大小
5.使能DMA中断
6.进入串口中断
#ifdef RT_SERIAL_USING_DMA
/*UART的空闲中断是否置位需要判断两个寄存器的值,这里不清楚为何要多一个其中一个是状态寄存器*/
else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET)
&& (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET))
{
level = rt_hw_interrupt_disable();//失能硬件中断
recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle));
recv_len = recv_total_index - uart->dma_rx.last_index;
uart->dma_rx.last_index = recv_total_index;
rt_hw_interrupt_enable(level);//使能硬件中断
if (recv_len)
{
rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));
}
__HAL_UART_CLEAR_IDLEFLAG(&uart->handle);
}
用于复制
这是我在KEIL上修改的
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
uint8_t temp1,temp2 = 0;
temp1 = __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE);
temp2 = __HAL_UART_GET_IT_SOURCE(&huart3,UART_IT_IDLE);
if((temp1 != RESET)&&(temp2 != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart3);//清除中断标志位
__HAL_DMA_DISABLE(&hdma_usart3_rx);//失能DMA_UART3_RX
UART3_RX_NUM = (DATA_MAX) - hdma_usart3_rx.Instance->NDTR;//获取DMA搬运的数据
__HAL_DMA_SET_COUNTER(&hdma_usart3_rx,DATA_MAX);//设置DMA_UART3_RX接收大小为DATA_MAX,即重新等待接收。
__HAL_DMA_ENABLE(&hdma_usart3_rx);//使能DMA_UART_RX
}
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
最后就是在main.c下面写好回调函数
功能是收到数据后转发给UART1
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //接收完成
{
HAL_StatusTypeDef ret = 0;
if(UART3_RX_NUM)
{
UART3_RX_BUFFER[UART3_RX_NUM] = '\0';
//ret = HAL_UART_Transmit_DMA(&huart1,UART3_RX_BUFFER,(UART3_RX_NUM+1));
ret = HAL_UART_Transmit(&huart1,UART3_RX_BUFFER,(UART3_RX_NUM),0xff);
if(ret == HAL_OK)
{
UART3_RX_NUM = 0;
memset(UART3_RX_BUFFER,0,sizeof(UART3_RX_BUFFER));
}
}
}
这样子整个工程就完成啦