提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言一、配置串口与DMA二、空闲中断服务函数二.串口+DMA发送三、主函数总结前言
提示:这里可以添加本文要记录的大概内容:
使用STM32F767实现串口空闲中断+DMA传输接收不定帧长度数据,以及当中可能遇到的错误总结
提示:以下是本篇文章正文内容,下面案例可供参考
一、配置串口与DMA
//硬件模块配置可直接通过CUBEMX配置void MX_USART1_UART_Init(void){huart1.Instance = USART1;//huart1.Init.BaudRate = 115200;//波特率配置huart1.Init.WordLength = UART_WORDLENGTH_8B;//设置通信字长huart1.Init.StopBits = UART_STOPBITS_1;//停止位1huart1.Init.Parity = UART_PARITY_NONE;//无奇偶校验huart1.Init.Mode = UART_MODE_TX_RX;//配置为收发模式huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;//无硬件流控制//下面三个采用默认值0就行,其实也可以不用配置huart1.Init.OverSampling = UART_OVERSAMPLING_16;huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}}//串口GPIO硬件配置与DMA配置void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle){GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO Configuration PA9------> USART1_TXPA10------> USART1_RX */GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USART1 DMA Init *//* USART1_RX Init */__HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能hdma_usart1_rx.Instance = DMA2_Stream5;//数据流配置hdma_usart1_rx.Init.Channel = DMA_CHANNEL_4;//通道配置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;//设置外设传输字长为字节hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//设置内存字长为字节hdma_usart1_rx.Init.Mode = DMA_NORMAL;//DMA传输模式配置为正常模式,只有一帧数据传输结束才会在开启下次DMA传输hdma_usart1_rx.Init.Priority = DMA_PRIORITY_HIGH;//传输优先级hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;//关闭FIFO模式if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)//初始化{Error_Handler();}__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);//将串口接收与DMA传输连接/* USART1_TX Init */hdma_usart1_tx.Instance = DMA2_Stream7;//数据流配置hdma_usart1_tx.Init.Channel = DMA_CHANNEL_4;//通道配置hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;//传输方向为内存到字节hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;//关闭外设自增hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;//开启内存自增hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;//设置外设传输字长为字节hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;//设置内存字长为字节hdma_usart1_tx.Init.Mode = DMA_NORMAL;//DMA传输模式配置为正常模式,只有一帧数据传输结束才会在开启下次DMA传输hdma_usart1_tx.Init.Priority = DMA_PRIORITY_HIGH;//传输优先级hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;//关闭FIFO模式if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)//初始化{Error_Handler();}__HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);//将串口发送与DMA传输连接/* USART1 interrupt Init */__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 开启空闲中断HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}}
二、空闲中断服务函数
代码如下(示例):
///中断服务函数void USART1_IRQHandler(void){/* USER CODE BEGIN USART1_IRQn 0 */if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)//判断中断是否发生{u16 l_val;//用于保存接收长度__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除空闲标志位//下面的步骤非常关键!!!HAL_UART_DMAStop(&huart1);//停止DMA传输SCB_DisableDCache(); //对于F7带Cache需要注意,读取DMA前先关闭,实验结果表明,若少了这一步DMA将只传输一次l_val = UART_BUFFSIZE - huart1.hdmarx->Instance->NDTR; // 通过DMA接收指针计算接收的字节数//HAL_UART_AbortReceive(&huart1);///*失能DMA接收*/Uart1_Str.Uart_RecvLens = l_val;/*接收标志位置1*/Rx_uart1_flag=1; SCB_EnableDCache();//对于F7带Cache需要注意,读取DMA完成后再打开HAL_UART_Receive_DMA(&huart1, Uart1_Str.Uart_RecvBuff, UART_BUFFSIZE); //再次设定接收缓存以及接收长度__HAL_UART_DISABLE_IT(&huart1, UART_IT_ERR);__HAL_UART_DISABLE_IT(&huart1, UART_IT_PE);}/* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);//回调函数/* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */}
二.串口+DMA发送
代码如下(示例):
u16 Uart1_DMA_Sent(u8 * Sendbuff, u16 Bufflens){u16 l_val = Bufflens > UART_BUFFSIZE ? UART_BUFFSIZE : Bufflens;if(Bufflens <= 0){return 0;}while(__HAL_DMA_GET_COUNTER(&hdma_usart1_tx));//检测DMA发送通道内还有没有数据if(Sendbuff){memcpy(Uart1_Str.Uart_SentBuff, Sendbuff, l_val);//将sendBuff里的数据送到Uart1_Str.Uart_SentBuff中,需包含string.h文件}HAL_UART_Transmit_DMA(&huart1, Uart1_Str.Uart_SentBuff, l_val);//DMA发送数据return l_val;}
三、主函数
int main(void){Cache_Enable(); //打开L1-CacheHAL_Init(); //初始化HAL库LED_Init();Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216);//延时初始化MX_USART1_UART_Init();//串口1初始化__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 开启空闲中断HAL_UART_Receive_DMA(&huart1, Uart1_Str.Uart_RecvBuff, UART_BUFFSIZE); // 设置接收DMA通道的接收缓冲区、接收长度// TIM2_Init(0xFFFF,10800*5-1);//定时器2初始化,以 2kHz 的频率计数//开机等待0.5ms// TIM3_Init(0xFFFF,10800-1);//定时器3初始化,定时器时钟为108M,分频系数为10800-1,所以定时器3的计数频率为108M/10800=10K// TIM4_Init(0xFFFF,10800*5-1);初始化 TIM4,以 2kHz 的频率计数 // TIM5_Init(1000-1,10800-1);//1ms进入一次中断,以1Mhz的频率计数 用来做时钟.11.01printf("Start test usart1 DMA....\n");while(1){if(Rx_uart1_flag) {Rx_uart1_flag=0;//清除接收标志Uart1_DMA_Sent(Uart1_Str.Uart_RecvBuff, Uart1_Str.Uart_RecvLens);//发送接收到的数据} }}
总结
在执行空闲中断函数的时候,必须先关闭DMA的传输,针对带Cache的STM32F767,需要注意,读取DMA前先将其关闭,否则将只出现DMA数据接收一次的情况。以上便是STM32F7串口kon+DMA传输配置的内容