PY32F系列MCU在串口接收时跑飞的根因分析(2)

接前一篇文章:PY32F系列MCU在串口接收时跑飞的根因分析(1)

二、问题分析

4. 实验确认

上一回在分析排查板子接收串口错误数据跑飞的问题时,定位到了是串口中断处理程序中else分支代码所导致的问题。再来回顾一下。完整的串口接收中断处理代码(示例代码)如下:

cpp 复制代码
void USART1_IRQHandler(void)
{
	__IO uint8_t data;
	static uint16_t length = 0;
 
	if (__HAL_UART_GET_IT_SOURCE(&Uart1Handle, UART_IT_RXNE) != RESET)
	{
		data = Uart1Handle.Instance->DR;
 
		if (uart1_rx_cnt < (UART_RX_BUFFER_SIZE - 1))
		{
			switch (uart1_rx_cnt)
			{
				case 0:
					if (data == 0x55) //frame header 1
						uart1_rx_buf[uart1_rx_cnt++] = data;
					else
						uart1_rx_cnt = 0;
					break;
				case 1:
					if (data == 0xAA) //frame header 2
						uart1_rx_buf[uart1_rx_cnt++] = data;
					else
						uart1_rx_cnt = 0;
					break;
				case 2:
					uart1_rx_buf[uart1_rx_cnt++] = data;
					break;
				case 3:
					......
					break;
				case 4:
					......
					break;
				default:
					uart1_rx_buf[uart1_rx_cnt] = data;
 
					if (uart1_rx_cnt >= 3 + length + 2 + 2 - 1)
					{
						if (uart1_rx_buf[uart1_rx_cnt] == 0xCC)
						{
							uart1_rx_cnt++;
							recv_cmd1 = 1;
						}
						else
						{
							uart1_rx_cnt = 0;
							memset((uint8_t *)uart1_rx_buf, 0x00, sizeof(uart1_rx_buf));
						}
						break;
					}
					uart1_rx_cnt++;
					break;
			}
		}
		else
		{
			uart1_rx_cnt = 0;
		}
 
		__HAL_UART_CLEAR_FLAG(&Uart1Handle, UART_IT_RXNE);
	}
	
	HAL_UART_IRQHandler(&Uart1Handle);
}

问题出在default中的else分支中,重点怀疑是memset一行代码导致的问题。

为了验证就是是否是这一行代码引发的问题,将这一行注释掉。编译、烧录,再次运行,仍然发送错误数据以及正确数据,这次不会出现跑飞的情况了,最多就是先发送错误帧的情况下,接下来发送的一帧正确数据也会被"带坏",但再接下来就正常了。这就是说,确实如之前猜测,问题出在了memset这一行。

但正如上一回所讲的,memset函数是系统标准库代码,笔者也没有用错,也不存在数组溢出的问题。为什么会导致程序跑飞呢?

三、根因分析

这个问题确实比较令人费解。没办法,只能求助于AI工具了。笔者之前在"PY32F系列MCU在OTA时App区概率性跑不起来的根因分析"系列文章中曾经提到过Cursor,当时用它来正向定位解决了OTA遇到的跑飞问题。当时在那个系列文章中曾经提到,后续使用Cursor工具又解决了另一个棘手问题(PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(6)-CSDN博客),

就是本系列的这个问题了。

Cursor给出的根因分析如下:

串口中断里这条memset会把中断拖过一个字节的时间,USART溢出之后中断就再也不退出。memset本身没有把内存写坏。

由于工程开了MicroLIB,因此memset这一句编出来是 __aeabi_memclr,给uart1_rx_buf清 256字节。链接信息里这个函数栈占用是0,只是一段字节循环。串口波特率是1500000(1.5M),一个字节大约6.7us。PY32F040的USART没有FIFO,清256字节远长于这个间隔,后面的字节进不了数据寄存器,硬件置上溢出标志ORE。

中断函数末尾又调用了HAL_UART_IRQHandler()。ORE已经置位、并且RXNE中断是开着的时候,这个函数读完状态就return,没有按"先读SR、再读DR"把ORE清掉。ORE在RXNE中断开启时会一直产生中断请求,所以一返回又马上再进USART1_IRQHandler。主循环再也得不到执行,表现(表象)就是跑飞。没有memset这行时,收一个字节的处理时间短于字节间隔,ORE起不来,所以只有加上它才出现。

四、最终解决

解决方案很简单,就是注释掉memset这一行就可以了。

因为中断里也不需要清这256字节。uart1_rx_cnt = 0之后,下一帧会从下标0重新覆盖。主循环处理完命令后本来就会memset这块缓冲。

五、总结与感悟

说实在的,如果说之前那个OTA问题笔者还能够自己想明白(虽然比AI时间长得多,但最终能够根据结果反推,进而想清楚),那么这个问题就让笔者看到了自己这二十几年在MCU层面的不足了。以我的功力,能够定位到memset这一行,也能够通过将其封掉来解决问题,但把这个问题想得如此透彻,以目前的功力来说无论如何做不到。还是要将基础再打牢一些,甚至得"二次学艺",才能弥补这种理论基础上的"先天不足"了。

笔者因为这两次败于AI,确实一度感到沮丧甚至心灰意冷。但后来使用Cursor分析了基于SoC的Linux源码,对于这一块Cursor表现得比较弱,远不如MCU领域所展现出来的专业和精深。看来短时间(几年)之内程序员们还不至于没饭吃,只要他们努力提高自身修为,还是能够与AI一较高下的。

相关推荐
wuyk5554 小时前
嵌入式驱动开发:量产级工程化实战(第9篇)——低功耗下的通信可靠性
c语言·驱动开发·stm32·单片机·嵌入式硬件
创界工坊工作室4 小时前
24-基于单片机的仓鼠养殖环境监控系统设计
stm32·单片机·嵌入式硬件·proteus
碧海银沙音频科技研究院5 小时前
基于炬芯ATS录音卡BLE控制协议
人工智能·嵌入式硬件·算法·语音识别
comyccccc6 小时前
PCB走线设计秘籍:从载流到3D预览全解析学习笔记
单片机·嵌入式硬件
俊基科技6 小时前
A-59 工业级 AI 语音处理模组:100 dB 消回音 + 神经网络降噪的工程实践
嵌入式硬件·神经网络·硬件开发·语音模块·回音消除·拾音降噪
endeavor126 小时前
can总线(五)
c语言·stm32·单片机·can
威嵌神州6 小时前
10.1 寸集成加密专网加固平板|外场加密通信硬件基线选型评估
人工智能·嵌入式硬件·平板
深圳市尚想信息技术有限公司6 小时前
TM52F1376海速芯HITENX Flash-8位MCU单片机,多通道ADC + 段码显示一体化主控芯片
单片机·嵌入式硬件·mcu·flask·芯片·控制器
威嵌神州7 小时前
国产化便携式加固终端选型|加固平板、手持终端产品谱系解析
嵌入式硬件·平板