![[单片机][cx32][填坑日记] 对串口uart进行压力测试会死机](http://pic.xiahunao.cn/yaotu/[单片机][cx32][填坑日记] 对串口uart进行压力测试会死机)
起因由于cx32l003没有DMA功能所以采取软模串口空闲中断(定时器实现)。前言CX32L003 是一款低成本、高性价比的 ARM Cortex-M0 内核 MCU广泛应用于家电、电机控制、传感器采集等嵌入式场景。由于该芯片没有 DMA 功能串口接收无法借助硬件搬运数据因此本文采用「软模串口空闲中断定时器实现」方案在串口空闲时通过信号量唤醒指定线程处理接收到的数据。在压力测试过程中串口出现「失灵」现象。通过仿真定位发现串口接收中断标志位置 1 后一直无法清除导致中断服务函数被反复进入、系统卡死。本文基于官方 SDK 源码深入分析 HAL_UART_IRQHandler 中断服务函数的实现细节定位根因并给出修复方案供遇到同类问题的开发者参考。下面是本文的整体流程总览CX32L003 无 DMA 功能采用软模串口空闲中断定时器实现串口空闲时发送信号量唤醒线程处理数据压力测试发现串口失灵仿真定位接收中断标志位无法清除进入死循环分析 HAL_UART_IRQHandler源码定位根因修改中断判断逻辑 改为 ||加入 TX 中断判断解决中断死循环问题1. 问题现象与定位1.1 软模串口空闲中断方案由于 CX32L003 没有 DMA 功能串口接收采用「定时器模拟空闲中断」的方式实现每次串口空闲时发送信号量给指定线程处理刚刚接收到的数据。处理数据时需要经过以下操作关闭串口中断终止串口接收。处理数据。打开串口中断打开串口接收。软模串口空闲中断的处理流程如下是否串口接收数据定时器检测到串口空闲发送信号量唤醒指定线程关闭串口中断终止串口接收处理接收到的数据打开串口中断恢复串口接收1.2 压力测试暴露的问题经过压力测试串口发送「失灵」。通过仿真得知串口接收中断标志位置 1导致一直进入中断服务无法退出。1.3 排查过程下面详细描述通过仿真定位问题的完整排查步骤供遇到同类问题的开发者参考。第一步挂载仿真器复现问题在压力测试环境下使用 J-Link 仿真器连接 CX32L003在串口持续收发数据的过程中复现「串口失灵」现象。当串口发送停止响应时立即暂停程序观察当前执行位置。第二步查看中断标志位在仿真器的寄存器窗口中查看串口控制寄存器SCON的值。重点关注以下两个标志位UART_INTSR_RXNE接收完成标志位正常情况下接收一帧数据后该位置 1读取数据寄存器SBUF后由硬件自动清零。UART_CR1_RXNEIE接收完成中断使能位软件可通过CLEAR_BIT关闭。通过仿真发现串口「失灵」时UART_INTSR_RXNE一直为 1且程序反复进入HAL_UART_IRQHandler中断服务函数无法退出。第三步确认标志位无法清除在中断服务函数入口处设置断点单步执行并观察SCON寄存器的变化voidHAL_UART_IRQHandler(UART_HandleTypeDef*huart){uint32_tisrflagsREAD_REG(huart-Instance-SCON);uint32_tcr1itsREAD_REG(huart-Instance-SCON);/* 断点处观察 isrflags 与 cr1its 的值 */if(((isrflagsUART_INTSR_RXNE)!RESET)((cr1itsUART_CR1_RXNEIE)!RESET))/* 问题点使用 */{/* 读取数据寄存器清除接收完成标志位 */huart-Instance-SBUF;...}...}单步执行后发现当UART_CR1_RXNEIE被软件关闭后UART_INTSR_RXNE仍然为 1。由于判断条件使用即使标志位置位只要使能位被关闭条件就不成立代码不会执行huart-Instance-SBUF;这一行标志位永远无法被清除中断服务函数被反复进入形成死循环。第四步逐步分析 HAL_UART_IRQHandler 源码打开官方 SDK 中的CX32L003_HAL_Driver\Src\cx32l003_hal_uart.c定位到HAL_UART_IRQHandler中断服务函数逐行分析首先读取SCON寄存器到isrflags和cr1its。判断接收完成中断条件(isrflags UART_INTSR_RXNE) ! RESET且(cr1its UART_CR1_RXNEIE) ! RESET。若条件成立读取SBUF清除标志位否则进入发送中断判断分支。通过分析确认关闭中断接收只是关闭了「接收完成中断使能」并没有关闭「接收完成中断标志位」而标志位由硬件控制无法通过软件直接清除只能通过读取SBUF寄存器来清除。这就是导致死循环的根本原因。2. 根因分析与修复方案2.1 官方 SDK 源码分析通过对官方 SDK 分析问题定位在CX32L003_HAL_Driver\Src\cx32l003_hal_uart.c的HAL_UART_IRQHandler中断服务函数中主要把改为||。2.2 根因关闭中断接收只是关闭「接收完成中断使能」并没有关闭「接收完成中断标志位」第二个无法关闭由硬件控制。这就导致标志位一直无法清除进入死循环。2.3 解决方案解决思路只要有接收标志位就读取缓冲区。使用SET_BIT(huart-Instance-SCON, (UART_SCON_REN))使能串口接收。使用CLEAR_BIT(huart-Instance-SCON, (UART_SCON_REN))关闭串口接收。在HAL_UART_IRQHandler中断服务函数中加入 TX 中断判断elseif(((isrflagsUART_INTSR_TI)!RESET)){__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_TC);}下面是修复前后HAL_UART_IRQHandler中断服务函数的完整代码对比修改前使用存在死循环风险voidHAL_UART_IRQHandler(UART_HandleTypeDef*huart){uint32_tisrflagsREAD_REG(huart-Instance-SCON);uint32_tcr1itsREAD_REG(huart-Instance-SCON);/* 接收中断处理只有接收完成中断使能 且 接收完成标志位置位 才进入 */if(((isrflagsUART_INTSR_RXNE)!RESET)((cr1itsUART_CR1_RXNEIE)!RESET))/* 问题点使用 */{/* 读取数据寄存器清除接收完成标志位 */huart-Instance-SBUF;/* 处理接收到的数据 */...}/* 发送中断处理 */elseif(((isrflagsUART_INTSR_TI)!RESET)){__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_TC);}}修改后使用||修复死循环问题voidHAL_UART_IRQHandler(UART_HandleTypeDef*huart){uint32_tisrflagsREAD_REG(huart-Instance-SCON);uint32_tcr1itsREAD_REG(huart-Instance-SCON);/* 接收中断处理只要接收完成标志位置位 或 接收中断使能就读取缓冲区 */if(((isrflagsUART_INTSR_RXNE)!RESET)||((cr1itsUART_CR1_RXNEIE)!RESET))/* 修复点改为 || */{/* 只要有接收标志位就读取缓冲区确保标志位被清除 */huart-Instance-SBUF;/* 处理接收到的数据 */...}/* 发送中断处理 */elseif(((isrflagsUART_INTSR_TI)!RESET)){__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_TC);}}修改说明位置修改前修改后作用接收中断判断条件接收完成中断使能且接收完成标志位置位||接收完成中断使能或接收完成标志位置位关闭中断接收时只是关闭「接收完成中断使能」并没有关闭「接收完成中断标志位」该位由硬件控制无法软件清除。使用时一旦使能位被关闭而标志位仍为 1条件永远不成立标志位无法通过读取缓冲区清除导致中断死循环。改为||后只要标志位置位就读取缓冲区从而清除标志位退出中断。发送中断处理无加入else if (((isrflags UART_INTSR_TI) ! RESET))分支处理发送完成中断清除发送完成标志位UART_FLAG_TC避免发送中断标志位残留导致类似问题。下面是修复前后中断判断逻辑的对比修改前使用 使能位被关闭标志位仍为 1修改后使用 ||接收完成中断使能或 接收完成标志位置位只要有接收标志位就读取缓冲区清除标志位退出中断接收完成中断使能且 接收完成标志位置位读取缓冲区清除标志位条件永远不成立标志位无法清除中断死循环系统卡死根因关闭中断接收只关闭使能位标志位由硬件控制无法软件清除3. 总结本文从 CX32L003 无 DMA 的硬件限制出发介绍了软模串口空闲中断的实现方案并针对压力测试中出现的串口「失灵」问题深入分析了HAL_UART_IRQHandler中断服务函数的实现定位到「接收完成中断标志位无法清除」这一根因最终通过修改中断判断逻辑并加入 TX 中断处理解决了中断死循环问题。希望本文能为使用 CX32L003 或其他无 DMA 特性 MCU 的开发者提供参考。参考资料CX32L003 官方 SDKCX32L003_HAL_Driver\Src\cx32l003_hal_uart.cCX32L003 数据手册与参考手册ARM Cortex-M0 内核编程手册每次串口空闲时发送信号量给指定线程处理刚刚接收到数据。处理数据时需要经过以下操作关闭串口中断终止串口接收处理数据打开串口中断打开串口接收经过压力测试发送串口失灵了.通过仿真得知串口接收中断标志位置1导致一直进入中断服务无法退出。通过对官方sdk分析。CX32L003_HAL_Driver\Src\cx32l003_hal_uart.cHAL_UART_IRQHandler 中断服务函数主要把 “” 改 “||”.原因关闭中断接收只是关闭[接收完成中断使能]并没有关闭[接收完成中断标志位]第二个无法关闭硬件控制。这就导致标志位一直无法清除进入死循环。解决只要有接收标志位就读取缓冲区。SET_BIT(huart-Instance-SCON, (UART_SCON_REN));CLEAR_BIT(huart-Instance-SCON, (UART_SCON_REN));HAL_UART_IRQHandler 中断服务函数加入TX中断判断elseif(((isrflagsUART_INTSR_TI)!RESET)){__HAL_UART_CLEAR_FLAG(huart,UART_FLAG_TC);}