完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
前言
本文是在使用 STM32L4 的串口 DMA 功能时,使用 HAL 库出现的一些问题,通过以下方式解决了 HAL 库中存在 DMA 发送和接收的一些问题。 STM32L4 的 DMA 简介 DMA Mapping DMA 相关配置及使用 以下根据 STM32L43xxx 系列进行 USART2 + DMA 的开发。 串口配置 sg_USART2_HandleStruct.Instance = USART2; sg_USART2_HandleStruct.Init.BaudRate = bound; sg_USART2_HandleStruct.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */ sg_USART2_HandleStruct.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */ sg_USART2_HandleStruct.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */ sg_USART2_HandleStruct.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */ sg_USART2_HandleStruct.Init.HwFlowCtl = UART_UART_HWCONTROL_NONE_RTS_CTS; sg_USART2_HandleStruct.Init.OverSampling = UART_OVERSAMPLING_16; sg_USART2_HandleStruct.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; sg_USART2_HandleStruct.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; HAL_UART_Init(&sg_USART2_HandleStruct) ; I/O 配置 /* USART2 clock enable */ __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /**USART2 GPIO Configuration PA2 ------> USART2_TX PA3 ------> USART2_RX */ GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_USART2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 配置并使用 TX + DMA DMA 配置 根据 DMA MAP 表可知,USART2 TX 可使用 DMA1 通道 7 (1-7),通道请求为 2 (0-7),方向为存储器到外设,并且设置字节长度。 /* Configure DMA Tx parameters */ sg_USART2_TxDMAHandleStruct.Instance = DMA1_Channel7; sg_USART2_TxDMAHandleStruct.Init.Request = DMA_REQUEST_2; sg_USART2_TxDMAHandleStruct.Init.Direction = DMA_MEMORY_TO_PERIPH; sg_USART2_TxDMAHandleStruct.Init.PeriphInc = DMA_PINC_DISABLE; sg_USART2_TxDMAHandleStruct.Init.MemInc = DMA_MINC_ENABLE; sg_USART2_TxDMAHandleStruct.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; sg_USART2_TxDMAHandleStruct.Init.MemDataAlignment = DMA_PDATAALIGN_BYTE; sg_USART2_TxDMAHandleStruct.Init.Priority = DMA_PRIORITY_HIGH; sg_USART2_TxDMAHandleStruct.Init.Mode = DMA_NORMAL; /* Associate the DMA handle */ __HAL_LINKDMA(uartHandle, hdmatx, sg_USART2_TxDMAHandleStruct); /* Stop any ongoing transfer and reset the state*/ HAL_DMA_DeInit(&sg_USART2_TxDMAHandleStruct); /* Configure the DMA Channel */ HAL_DMA_Init(&sg_USART2_TxDMAHandleStruct); DMA TX 使用(禁止中断) 定义一个发送函数(该函数没有使用发送完成中断处理,在下次进入该函数时检测 DMA 相关标志并清除,因此,须确保每次调用该函数的间隔时间能完成上次的数据传输),传输完成必须关闭串口 DMA ,否则不能启动下一次 DMA 传输。 void USART2_UART_Transmit(uint8_t *pData, uint16_t len) { if (__HAL_DMA_GET_FLAG(&sg_USART2_HandleStruct, DMA_FLAG_TC7)) { __HAL_DMA_CLEAR_FLAG(&sg_USART2_HandleStruct, DMA_FLAG_TC7); /* 清除DMA1_Steam7传输完成标志 */ HAL_UART_DMAStop(&sg_USART2_HandleStruct); /* 传输完成以后关闭串口DMA */ } HAL_UART_Transmit_DMA(&sg_USART2_HandleStruct, pData, len); } DMA TX 使用(使能中断) 暂无 配置并使用 RX + DMA DMA 配置 根据 DMA MAP 表可知,USART2 RX 可使用 DMA1 通道 6 (1-7),通道请求为 2 (0-7),方向为外设到存储器,并且设置字节长度。 /* Configure DMA Rx parameters */ sg_USART2_RxDMAHandleStruct.Instance = DMA1_Channel6; sg_USART2_RxDMAHandleStruct.Init.Request = DMA_REQUEST_2; sg_USART2_RxDMAHandleStruct.Init.Direction = DMA_PERIPH_TO_MEMORY; sg_USART2_RxDMAHandleStruct.Init.PeriphInc = DMA_PINC_DISABLE; sg_USART2_RxDMAHandleStruct.Init.MemInc = DMA_MINC_ENABLE; sg_USART2_RxDMAHandleStruct.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; sg_USART2_RxDMAHandleStruct.Init.MemDataAlignment = DMA_PDATAALIGN_BYTE; sg_USART2_RxDMAHandleStruct.Init.Priority = DMA_PRIORITY_HIGH; sg_USART2_RxDMAHandleStruct.Init.Mode = DMA_NORMAL; /* Associate the DMA handle */ __HAL_LINKDMA(uartHandle, hdmarx, sg_USART2_RxDMAHandleStruct); /* Stop any ongoing transfer and reset the state*/ HAL_DMA_DeInit(&sg_USART2_RxDMAHandleStruct); /* Configure the DMA Channel */ HAL_DMA_Init(&sg_USART2_RxDMAHandleStruct); 接收中断配置 使用 RX DMA,通常采用 DMA + RX + UART_IT_IDLE(空闲中断)用来接收不定长的数据内容。 DMA RX 使用 定义接收中断函数,做相关处理。 void USART2_IRQHandler(void) { uint32_t tmp; if ((__HAL_UART_GET_IT(&sg_USART2_HandleStruct, UART_IT_IDLE) != RESET)) { /* 清除相关标志 */ __HAL_UART_CLEAR_IDLEFLAG(&sg_USART2_HandleStruct); tmp = sg_USART2_HandleStruct.Instance->ISR;/* 通过读取该寄存器来清除 */ tmp = sg_USART2_HandleStruct.Instance->RDR;/* 通过读取该寄存器来清除 */ HAL_UART_DMAStop(&sg_USART2_HandleStruct); /* 获取实际接收长度 */ tmp = __HAL_DMA_GET_COUNTER(&sg_USART2_RxDMAHandleStruct); sg_USART2_RxLen = USART_MAX_RX_BUF_SIZE - tmp; g_Usart2RecvFinish = 1; /* 重新启动接收 */ HAL_UART_Receive_DMA(&sg_USART2_HandleStruct, sg_USART2_RxBuffer, USART_MAX_RX_BUF_SIZE); } HAL_UART_IRQHandler(&sg_USART2_HandleStruct); } 注意事项 1、串口接收中断中若通过函数 HAL_UART_Receive 读取串口数据,会出现没有正常读取数据,导致不停地进入接收中断,造成程序无法正常运行。 void USART2_IRQHandler(void) { uint32_t tmp; if ((__HAL_UART_GET_IT(&sg_USART2_HandleStruct, UART_IT_RXNE) != RESET)) { 将函数 HAL_UART_Receive(&sg_USART2_HandleStruct, sg_USART2_RxBuffer[sg_USART2_RxLen++], 1, 10); 修改为 sg_USART2_RxBuffer[sg_USART2_RxLen++] = USART2->RDR; } HAL_UART_IRQHandler(&sg_USART2_HandleStruct); } 2、在同时使用 DMA RX 和 DAM TX 的时候,在其中一个完成后会关闭串口 DMA,导致另一个受其影响,导致发送或接收异常。 原因: 由于使用了函数 HAL_UART_DMAStop(&sg_USART2_HandleStruct) 用来关闭 UASRT DMA 将TX 和 RX 均给关闭了,导致影响了另一 DMA 正常传输。 解决方案: 基于函数 HAL_UART_DMAStop(UART_HandleTypeDef *huart) 为原型,定义一个函数 MY_HAL_UART_DMAStop(UART_HandleTypeDef *huart, uint8_t obj), 用来可单独关闭其中一个 DMA 的传输,函数如下,其中函数 My_UART_EndTxTransfer 和 My_UART_EndRxTransfer 也是分别基于函数 HAL_UART_DMAStop(UART_HandleTypeDef *huart) 内部中调用的 UART_EndTxTransfer 和 UART_EndRxTransfer 为原型,除了函数名称,没有其他任何改动(在不改动 HAL 库的情况下可这样自定义三个函数,其中 USART_TX_DMA 是自定义的一个宏)。 /** * @brief End ongoing Tx transfer on UART peripheral (following error detection or Transmit completion). * @note 函数原型: UART_EndTxTransfer(UART_HandleTypeDef *huart), 函数功能完全一样 * @param huart UART handle. * @retval None */ static void My_UART_EndTxTransfer(UART_HandleTypeDef *huart) { #if defined(USART_CR1_FIFOEN) /* Disable TXEIE, TCIE, TXFT interrupts */ CLEAR_BIT(huart->Instance->CR1, (USART_CR1_TXEIE_TXFNFIE | USART_CR1_TCIE)); CLEAR_BIT(huart->Instance->CR3, (USART_CR3_TXFTIE)); #else /* Disable TXEIE and TCIE interrupts */ CLEAR_BIT(huart->Instance->CR1, (USART_CR1_TXEIE | USART_CR1_TCIE)); #endif /* USART_CR1_FIFOEN */ /* At end of Tx process, restore huart->gState to Ready */ huart->gState = HAL_UART_STATE_READY; } /** * @brief End ongoing Rx transfer on UART peripheral (following error detection or Reception completion). * @note 函数原型: UART_EndRxTransfer(UART_HandleTypeDef *huart), 函数功能完全一样 * @param huart UART handle. * @retval None */ static void My_UART_EndRxTransfer(UART_HandleTypeDef *huart) { /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */ #if defined(USART_CR1_FIFOEN) CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE)); CLEAR_BIT(huart->Instance->CR3, (USART_CR3_EIE | USART_CR3_RXFTIE)); #else CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE)); CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE); #endif /* USART_CR1_FIFOEN */ /* At end of Rx process, restore huart->RxState to Ready */ huart->RxState = HAL_UART_STATE_READY; /* Reset RxIsr function pointer */ huart->RxISR = NULL; } /** * @brief Stop the DMA Transfer. * @note 函数原型: HAL_UART_DMAStop(UART_HandleTypeDef *huart), 可单独关闭 TX/RX 其中一个DMA * @param huart UART handle. * @param obj USART_TX_DMA or USART_RX_DMA. * @retval HAL status */ HAL_StatusTypeDef MY_HAL_UART_DMAStop(UART_HandleTypeDef *huart, uint8_t obj) { const HAL_UART_StateTypeDef gstate = huart->gState; const HAL_UART_StateTypeDef rxstate = huart->RxState; if (obj == USART_TX_DMA) { /* Stop UART DMA Tx request if ongoing */ if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAT)) && (gstate == HAL_UART_STATE_BUSY_TX)) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT); /* Abort the UART DMA Tx channel */ if (huart->hdmatx != NULL) { if (HAL_DMA_Abort(huart->hdmatx) != HAL_OK) { if (HAL_DMA_GetError(huart->hdmatx) == HAL_DMA_ERROR_TIMEOUT) { /* Set error code to DMA */ huart->ErrorCode = HAL_UART_ERROR_DMA; return HAL_TIMEOUT; } } } My_UART_EndTxTransfer(huart); } } else { /* Stop UART DMA Rx request if ongoing */ if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) && (rxstate == HAL_UART_STATE_BUSY_RX)) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); /* Abort the UART DMA Rx channel */ if (huart->hdmarx != NULL) { if (HAL_DMA_Abort(huart->hdmarx) != HAL_OK) { if (HAL_DMA_GetError(huart->hdmarx) == HAL_DMA_ERROR_TIMEOUT) { /* Set error code to DMA */ huart->ErrorCode = HAL_UART_ERROR_DMA; return HAL_TIMEOUT; } } } My_UART_EndRxTransfer(huart); } } return HAL_OK; } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1936浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
729浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
569浏览 3评论
594浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
552浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 19:18 , Processed in 0.716870 second(s), Total 45, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号