前言:
stm32是嵌入式MCU开发中最多应用的芯片,很早之前我们开发ST芯一般都是标准库开发,标准库简洁好读,现在要配合CubeMX生成代码,所以官方主推HAL库和LL库,但是HAL代码冗杂很绕,因为出来也不久,有些代码使用之后不是那么好用。
这次我就来分享两个实际使用过程中遇到的两个问题,一个是使用uart的发送中断进行数据发送产生的数组访问越界的问题。一个是stop模式下,dma相关的外设休眠唤醒需要注意重新初始化。
这篇是uart使用的介绍:
作者:良知犹存
转载授权以及围观:欢迎关注微信公众号:羽林君
或者添加作者个人微信:become_me
情节介绍:
串口是我们经常使用的一个外设,一般我们为了发送速度变快,会使用DMA或者中断发送接收。而CubeMX配置下,HAL调用了自己的一套函数 HAL_UART_IRQHandler 层层调用。
在官方提供的 stm32f4xx_hal_uart.c 文件中你可以看到如下函数:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) { ..... /* UART in mode Transmitter ------------------------------------------------*/ if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { UART_Transmit_IT(huart); return; } ...... }
其中在UART_Transmit_IT 函数中 有一段 函数为
if (--huart->TxXferCount == 0U) { /* Disable the UART Transmit Complete Interrupt */ __HAL_UART_DISABLE_IT(huart, UART_IT_TXE); /* Enable the UART Transmit Complete Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TC); }
这里会把发送的 TxXferCount 的计数值自减,并判断是否为零。正常工作都没有问题,可是我们的设备实际使用过程中,上层的部分断电之后,会给底层通讯串口带了一个中断,这个时候继续减下去就会出现出现一个很大值,这个是因为 TxXferCount 是一个无符号的16位数据。
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
debug看到如下数据,原因上文提到。就是我遇到我们Linux核心板掉电之后会产生一个中断,导致这里判断时候自动减1,TxXferCount从 0 变成 -1 因为是无符号数据,所以数据表现为65535。
在全局搜索TxXferCount调用,我们可以看到TxXferSize 是发送buf的长度数据
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) { /* Check that a Tx process is not already ongoing */ if (huart->gState == HAL_UART_STATE_READY) { if ((pData == NULL) || (Size == 0U)) { return HAL_ERROR; } /* Process Locked */ __HAL_LOCK(huart); huart->pTxBuffPtr = pData; huart->TxXferSize = Size; huart->TxXferCount = Size; huart->ErrorCode = HAL_UART_ERROR_NONE; huart->gState = HAL_UART_STATE_BUSY_TX; /* Process Unlocked */ __HAL_UNLOCK(huart); /* Enable the UART Transmit data register empty Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TXE); return HAL_OK; } else { return HAL_BUSY; } }
此外我们还会发现一处 huart->TxXferCount 计数 自减 使用。
此处的函数如下, 伴随着一个很大的 TxXferCount开始自减, pdata16bits开始自加。刚开始越界的时候由于该内存被初始化过,所以没有问题,该循环执行一会之后,程序就会进入hardfault。
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout) { ...... huart->TxXferSize = Size; huart->TxXferCount = Size; while (huart->TxXferCount > 0U) { if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK) { return HAL_TIMEOUT; } if (pdata8bits == NULL) { huart->Instance->DR = (uint16_t)(*pdata16bits & 0x01FFU); pdata16bits++; } else { huart->Instance->DR = (uint8_t)(*pdata8bits & 0xFFU); pdata8bits++; } huart->TxXferCount--; } ...... }
修改建议:
和硬件沟通过,他们的掉电机制,就是如此无法修改。所以我们进行软件的一些修改,因为会产生一个中断导致计数值自减,所以我们初步确认进行自减处进行限制,先增加一个零值判断。
huart->TxXferCount == 0U
又考虑到我们单包数据单次不会超过150byte,所以又加上150字节的控制。(此处的数据流控制大家可以按照自己实际使用的情况进行酌情使用)
huart->TxXferCount > 150U
原函数:
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart) { ...... if (--huart->TxXferCount == 0U) { /* Disable the UART Transmit Complete Interrupt */ __HAL_UART_DISABLE_IT(huart, UART_IT_TXE); /* Enable the UART Transmit Complete Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TC); } ...... }
改为:
static HAL_StatusTypeDef UART_Transmit_IT(UART_HandleTypeDef *huart) { ...... if (huart->TxXferCount == 0U || --huart->TxXferCount == 0U || huart->TxXferCount > 150U ) { /* Disable the UART Transmit Complete Interrupt */ __HAL_UART_DISABLE_IT(huart, UART_IT_TXE); /* Enable the UART Transmit Complete Interrupt */ __HAL_UART_ENABLE_IT(huart, UART_IT_TC); } ...... }
最后代码可以正常的使用。
结语
这就是我分享的项目中遇到一个st官方库使用的问题,如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。
作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。