本节是介绍如何使用CubeMX配置STM32的串口,并通过DMA方式实现串口通信,包含发送和接受数据。
开发环境:CubeMX+MDK5.27
芯片型号:STM32F103ZET6
时间:2020/07/12
DMA:全称Direct Memory Access,翻译为直接存储器访问。其作用是在外设与存储器之间、存储器与存储器之间进行高速数据传输。DMA的传输过程的初始化和启动均由CPU完成,传输过程则靠DMA控制器执行,无需CPU参与,这大大节省了CPU资源,提高了CPU的利用率。DMA最大的优点就是传输数据的过程完全不需要CPU参与。
DMA四要素:
1.传输源——DMA数据的来源。
2.传输目标——DMA数据传输的目的。
3.传输数量——DMA传输数据的数量。
4触发信号——启动一次DMA数据传输的动作。
STM32F103ZET6(以下简称F1)中的DMA:
F1中具备两个DMA控制器:DMA1和DMA2。DMA1拥有七个通道,DMA2则只有五个通道。每个DMA控制器用于管理一个或多个外设的存储器访问请求,并通过总线仲裁器来协调各个DMA请求的优先级。
DMA数据传输模式:
1——普通模式
传输结束后(即传输数据的数量为零时),将不再产生DMA操作,若开始新的DMA传输,需要关闭DMA通道,然后重新启动DMA传输。本节将采用普通模式传输数据。
2——循环模式
可用于处理环形缓冲区和连续数据流(例如ADC扫描模式)。当激活循环模式后,每轮传输结束时,要传输的数据数量将自动用设置的初始值进行加载,并继续响应DMA请求。
实现步骤
第一步,在CubeMX中配置串口,前面的章节配置的都是串口1,既然已经在串口1上成功通信,那么本次我们配置串口2,如果想使用串口1完成也没有问题。CubeMX配置图如下
DMA_Settings页面点击Add添加如图所示的两个通道即可,DMA Request Settings保持默认。再点击NVIC_Settings并勾选USART2_global_interrupt的enable选项。
第二步、编写代码,在main.c的USER CODE BEGIN PV下定义下列变量
uint8_t Rx_Buff[100]={0};uint8_t Rx_Count = 0;uint8_t Rx_Flag = 0;
在USER CODE BEGIN 2下使能串口空闲中断,启动DMA接收
_HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);HAL_UART_Receive_DMA(&huart2, (uint8_t*)Rx_Buff,sizeof(Rx_Buff));
在stm32f1xx_it.c的USART2_IRQHandler函数中添加对空闲中断的判断
void USART2_IRQHandler(void){/* USER CODE BEGIN USART2_IRQn 0 *//* USER CODE END USART2_IRQn 0 */HAL_UART_IRQHandler(&huart2);/* USER CODE BEGIN USART2_IRQn 1 *///空闲中断处理if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET){extern void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);__HAL_UART_CLEAR_IDLEFLAG(&huart2);HAL_UART_IdleCpltCallback(&huart2);}/* USER CODE END USART2_IRQn 1 */}
此处简单介绍一下空闲中断的作用:在串口传输数据时,UART_FLAG_IDLE串口空闲标志位始终为0,当数据传输完成串口空闲标志位就为1。此时就产生了空闲中断,我们编写空闲中断回调函数,使Rx_Flag=1,接着在while(1)中判断Rx_Flag从而进行后面的步骤。
在USER CODE BEGIN 3下添加如下代码
if(Rx_Flag == 1){Rx_Flag = 0;Rx_Count = sizeof(Rx_Buff) - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);HAL_UART_Transmit_DMA(&huart2,(uint8_t*)Rx_Buff,Rx_Count);Rx_Count = 0;__HAL_DMA_DISABLE(&hdma_usart2_rx);}
上述代码的作用:首先判断Rx_Flag的值从而判断接收是否完成,若完成,先清除Rx_Flag,再计算接收数据长度,用总长度sizeof(Rx_Buff)减去DMA数据通道中待接受的数据个数HAL_DMA_GET_COUNTER(&hdma_usart2_rx)就得到了已接收到的数据长度Rx_Count。再调用HAL_UART_Transmit_DMA将数据发送出去,然后清零Rx_Count保证下一次能够正常接收数据。最后关闭DMA,此时会触发DMA中断,调用DMA中断回调函数启动下一次DMA接收,具体可追踪stm32f1xx_it.c中的DMA1_Channel16_IRQHandler中的函数。此处不作详细分析。另外,如果在调用HAL_DMA_GET_COUNTER(&hdma_usart2_rx)函数时,提示hdma_usart2_rx找不到,则需要在usart.h中添加以下代码
DMA_HandleTypeDef hdma_usart2_rx;DMA_HandleTypeDef hdma_usart2_tx;
第三步、实验结果
总结:DMA的使用对于我来说是第一次,目前来说还没有感受到DMA的全部魅力,希望可以慢慢发现它的更高级的用法,……….后续待更新**
