完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
使用jlink连接mcu查看日志,偶发重新初始化,部分线程未退出,变量错位。
串口1的线程是在main最后面。 未见初始化打印,就已经发送数据了。并且发送的数据不对。感觉是重新初始化时,串口1线程未退出 |
|
相关推荐
1个回答
|
|
|
我们面对的问题是在使用J-Link连接MCU时,RT-Thread运行不正常,具体表现为: 1. 偶发重新初始化:可能是指系统偶尔会重启或者重新初始化。 2. 部分线程未退出:在重新初始化后,部分线程可能还在运行,导致状态混乱。 3. 变量错位:可能是指变量被意外修改,或者内存错乱。 4. 串口1的线程在main函数最后启动,但是未见初始化打印就已经发送数据,且数据不对。 5. 感觉是重新初始化时,串口1线程未退出,导致在重新初始化后旧线程还在运行,而新的初始化又开始了。 可能的原因分析: 1. 堆栈溢出:某个线程堆栈溢出,导致内存被破坏,可能引发复位或者变量错乱。 2. 中断处理问题:某个中断处理函数有bug,导致中断触发后系统行为异常。 3. 电源不稳定:导致MCU复位。 4. 看门狗复位:如果启用了看门狗,且没有及时喂狗,会导致复位。 5. 多线程竞争或资源未保护:共享资源访问冲突导致异常。 6. 内存管理问题:动态内存分配失败或内存泄漏导致系统崩溃。 7. 在系统初始化完成前启动了线程:比如在硬件初始化完成前,线程就开始运行并使用了未初始化的硬件。 解决步骤建议: 1. 检查复位原因: - MCU的复位寄存器可以记录复位原因(比如上电复位、看门狗复位、软件复位等)。在RT-Thread中,可以通过读取相关寄存器来获取复位原因。例如,在启动代码中或者main函数开始处,读取复位标志并打印出来,这样可以帮助我们定位问题。 2. 检查堆栈使用情况: - 使用RT-Thread提供的线程堆栈检查函数,例如在shell中使用`ps`命令查看线程堆栈使用情况,或者使用`list_thread`命令(取决于RT-Thread版本)。重点检查是否有线程堆栈使用率接近100%的情况。 - 可以考虑增加容易溢出线程的堆栈大小。 3. 检查看门狗: - 确认是否启用了看门狗。如果启用了,检查喂狗操作是否正常,特别是高优先级任务长时间占用CPU导致低优先级任务无法及时喂狗。 4. 检查中断处理: - 确保中断处理函数中没有进行过于耗时的操作,或者可能引起阻塞的操作。 - 检查中断优先级设置是否正确,尤其是系统相关的中断(如SysTick、PendSV)优先级是否设置为最低,以避免中断嵌套导致问题。 5. 检查共享资源访问: - 对于全局变量等共享资源,访问时应使用互斥锁(mutex)或者关中断等方式进行保护,避免多线程同时访问导致的数据错乱。 6. 检查硬件初始化和线程启动顺序: - 确保在启动线程之前,相关硬件(如串口)已经初始化完成。在main函数中,通常的流程是: - 硬件初始化(包括串口) - 打印初始化信息(比如版本信息) - 创建并启动其他线程 - 这样,在串口线程启动前,串口已经初始化好了。 7. 使用调试器设置断点: - 在J-Link调试环境下,可以在复位处理函数、看门狗中断处理函数以及一些关键位置设置断点,观察程序执行流程,特别是系统复位时的现场。 8. 内存检测: - 使用内存检测工具(如RTT的内存泄漏检测组件,或者硬件内存检测)来检查是否有内存越界或泄漏。 9. 日志系统问题: - 如果使用了串口日志,确保在系统启动早期(在调度器启动之前)不要使用可能导致阻塞的打印函数(因为此时没有线程调度)。RT-Thread的早期打印可以使用`rt_kprintf`,但注意它不会阻塞,但也要确保串口初始化完成。 10. 电源稳定性: - 使用示波器检查电源电压是否稳定,特别是在复位发生时。 针对“串口1线程在main最后面,未见初始化打印就已经发送数据,并且数据不对”的问题,我们可以采取以下措施: - 确认串口初始化是否在创建线程之前完成。如果串口线程在创建后立即运行,但此时串口硬件还没有初始化,那么发送数据就会出错。 ```c int main(void) { /* 硬件初始化 */ rt_hw_usart_init(); // 初始化串口硬件 /* 打印初始化信息 */ rt_kprintf("System start...n"); /* 创建线程 */ rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 512, 10, 10); rt_thread_startup(thread); /* 启动调度器 */ rt_system_scheduler_start(); return 0; } ``` - 另外,检查串口线程入口函数,确保在发送数据前,等待必要的信号量或事件,确保硬件已经就绪(不过一般硬件初始化在创建线程前,所以应该就绪了)。 11. 关于重新初始化时线程未退出: - 当系统复位(除了上电复位外)时,RAM中的数据可能没有清除,而程序重新运行,此时旧的线程控制块可能还保留在内存中,而系统重新初始化线程,导致出现两个相同的线程(一个未退出的旧线程和一个新线程)。这种情况通常称为“热复位”问题。 - 解决方法:在复位处理中,尽量使用软件复位(调用NVIC_SystemReset)并确保复位后RAM被初始化(可以通过设置复位后不初始化RAM,但这样会有残留,所以一般建议在软件复位前标记需要重新初始化,然后复位后根据标记重新初始化全局变量和静态变量)。 但是,对于RT-Thread系统,如果发生复位,整个系统都会重新启动,包括重新初始化所有数据结构。如果复位后没有进行完整的内存初始化,可能会有残留。因此,建议: - 在启动代码中,对于非冷启动(比如复位),进行必要的数据区清零(特别是.bss和.data段)或者重新初始化全局变量。 然而,标准的启动代码(如ARM Cortex-M)只会在上电复位时初始化.bss(清零)和.data(从Flash加载),其他复位不会。所以,如果发生了看门狗复位或者其他复位,这些全局变量和静态变量不会被重置。这可能导致旧数据残留,从而引起问题。 解决方案: - 在进入main函数后,首先检查复位原因,如果是非上电复位(比如看门狗复位),则执行全局数据重置。但这样做要非常小心,因为可能会破坏系统状态。或者,直接进行软件复位,让系统重新启动(通过NVIC_SystemReset()),并确保看门狗在复位后能够正常初始化,避免连续复位。 12. 使用J-Link的日志功能: - 可以使用J-Link的RTT Viewer工具来查看日志,这样不占用串口,且可以在调试时使用。同时,RTT日志可以更早地输出(在串口初始化之前),帮助定位问题。 综上所述,我们可以按照以下步骤进行: 步骤1:在main函数开始处读取并打印复位原因(如果串口还不能用,可以先将原因保存在变量中,稍后打印)。 步骤2:检查复位原因,如果是看门狗复位,则检查喂狗逻辑;如果是软件复位,则检查复位点;如果是其他复位,则检查硬件。 步骤3:检查线程堆栈大小,增加堆栈大小以避免溢出。 步骤4:确保硬件初始化在创建线程之前完成。 步骤5:在非上电复位时,可以打印警告,并考虑进行软件复位来清除所有状态(如果问题频繁,则先解决根本原因)。 步骤6:使用RTT Viewer替代串口日志,以便在系统启动早期获取日志。 由于问题偶发,可能需要长时间测试。同时,可以在关键位置增加日志,记录程序运行流程,以便在复位后查看复位前的操作。 示例代码:获取复位原因(以STM32为例) ```c #include "stm32f4xx.h" // 根据实际芯片型号包含 void print_reset_reason(void) { if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST)) { rt_kprintf(" Low Power Resetn"); } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST)) { rt_kprintf(" Window Watchdog Resetn"); } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)) { rt_kprintf(" Independent Watchdog Resetn"); } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST)) { rt_kprintf(" Software Resetn"); } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST)) { rt_kprintf(" Power On Resetn"); } else if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST)) { rt_kprintf(" External Pin Resetn"); } else { rt_kprintf(" Unknown Resetn"); } __HAL_RCC_CLEAR_RESET_FLAGS(); } ``` 在main函数开始处调用: ```c int main(void) { // 初始化串口之前,可以先初始化一个简单的日志系统(比如RTT)或者稍后打印 // 先初始化系统时钟等必要的硬件 // 打印复位原因(如果此时串口未初始化,可以稍后打印,但先保存原因) // 如果使用RTT,则可以直接打印 // print_reset_reason(); // ... 其他初始化 } ``` 由于在复位后,我们可能无法立即使用串口,因此建议: - 使用RTT(SEGGER RTT)来打印早期日志。 - 或者,将复位原因保存在一个全局变量中,该变量在复位后不会被初始化(可以通过在特殊内存区域定义,或者通过检查复位标志后立即保存到全局变量中,这个全局变量在.bss段,在非上电复位时不会被清零)。然后在串口初始化后打印出来。 关于全局变量在复位后不被清零的问题:默认情况下,只有上电复位才会初始化.bss和.data。所以,如果是其他复位,全局变量会保持复位前的值。我们可以利用这一点来保存复位原因。 但注意:如果发生了看门狗复位,可能系统已经处于异常状态,此时全局变量可能已被破坏。所以,在获取复位原因后立即保存,并且尽快打印。 最后,如果问题仍然无法解决,需要更详细的日志和调试信息,可以考虑使用J-Link进行单步调试,观察复位发生时的具体情况。 由于你提到“偶发”,所以问题可能比较隐蔽,需要耐心排查。希望以上建议能帮助你定位问题。 针对您描述的问题——使用J-Link连接MCU时,RT-Thread运行异常(偶发重新初始化、线程未退出、变量错位、串口数据异常),以下是系统化的解决方案: 核心原因分析
解决方案1. 定位复位原因在 2. 修复线程管理问题
3. 处理变量错位/未复位
4. 堆栈溢出检测
5. 中断与看门狗配置
6. 调试技巧
验证步骤
通过以上方法,可系统性解决复位、线程冲突和变量错位问题,确保系统稳定运行。 |
|
|
|
|
只有小组成员才能发言,加入小组>>
184个成员聚集在这个小组
加入小组rtstudio是否可像keil加载ini解决虚拟串口与mcu串口通讯?
1211 浏览 0 评论
【Vision Board创客营连载体验】基于RA8D1-Vision Board的自动路径规划小车
1775 浏览 1 评论
【Vision Board创客营连载体验】基于Vision Board的垃圾分类
2156 浏览 0 评论
【Vision Board创客营连载体验】使用 Vision Board 做一个 UVC Camera
1777 浏览 0 评论
【Vision Board创客营连载体验】TinyMaix进行手写数字识别
2004 浏览 0 评论
1461浏览 5评论
在RT-Thread Studio中新建的stm32f407-atk-explorer工程运行qemu失败,是什么原因引起的?
1764浏览 3评论
为什么rt_device_read()只能读取到两个字节数据?
359浏览 3评论
连得上热点,但是ping baidu.com出现timeout,请问跟什么有关?
418浏览 3评论
413浏览 2评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-1 10:36 , Processed in 0.766227 second(s), Total 77, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
5955
