HC32开发笔记

记录,为日后遇到类似问题提供思路。

常见问题&解决办法

J-LINK杀电脑事件

我有一个朋友,调试电控板时严格遵循“断电操作”,结果烧录器炸了,电脑USB口也烧了。🙂

因为之前已经有同事带电调试电脑被炸,但是没人说清楚到底是什么原因,搞得人心惶惶。风水轮流转今天吃瓜吃到了兄弟家。

我们用的是两脚插头,上面有一个船型开关,插入插座时零火线方向不固定。本次操作中,插头的火线(L)恰好接到了船型开关的输入端,而零线(N)直接接到了电控板上。

朋友拨下船型开关“断电”时,实际上断开的只是零线(N)回路,而火线(L)始终通过电源线直接连在电控板的所有铜皮、GND层和芯片地脚上。

此时开发板的“地”(GND)被火线强制抬升至接近220V。由于回路被切断,板子不工作、指示灯不亮,肉眼完全看不出异常。

朋友将烧录器的GND线连接到开发板的GND上。此时:开发板GND = 220V火线电位,电脑USB口的GND = 真正的0V大地电位;

两者通过烧录器相连,等于直接将220V火线接到了电脑USB口的金属外壳和主板GND上。瞬时大电流击穿烧录器内部所有隔离元件,并长驱直入灌入电脑主板,烧毁USB控制器及南桥芯片。

还好没有被电到只是炸台电脑助助兴。。。

解决办法:可以在两脚插头上做明显极性标记,或者搞个隔离变压器,这样妈妈再也不用担心我被电了。

更新:电脑主板烧了,开不了机了。拿去电脑电修了。

RS-485通信热插拔后无法恢复

RS-485总线在通信超时或物理断开(如带电插拔)后重新连接,上层应用显示通信链路已恢复,但MCU无法再接收到来自总线的数据,完全锁死无数据,需要重启MCU才能恢复正常。

排除物理层故障(偏置电阻、收发器),因为再故障前可正常通信。考虑因为异常之后进入了一个未知状态。尝试加断点/打印,通信故障后未正常进入中断,查看UART错误标志寄存器被置起,故障清楚异常标志位后,通信可恢复。确认是清除遗漏问题,加固代码。

根因:问题根源在于UART外设进入了错误锁定状态,且未被正确清除。手册中有描述 “RS-485总线带电插拔或断开瞬间,信号线电平不稳定,极易导致接收端产生帧错误(Frame Error, FE)——即接收到的数据帧缺少有效的停止位”,“FE=1后不能继续接收数据”,“PE=1后不能继续接收数据”,一旦这些错误标志被硬件置位,即使后续总线信号恢复正常,UART模块也会停止向RXNE(接收数据寄存器非空)中断提供新数据,导致CPU无法感知新数据的到来。

解决办法:在中断结束时候或主循环定时清除故故障标志位,防止通信因故障进入死锁状态而无法恢复。

时钟初始化过程中,进入到ddl异常中断,死循环

/**
 * @brief DDL assert error handle function
 * @param [in] file                     Point to the current assert the wrong file.
 * @param [in] line                     Point line assert the wrong file in the current.
 * @retval None
 */
__WEAKDEF void DDL_AssertHandler(const char *file, int line)
{
    /* Users can re-implement this function to print information */
    DDL_Printf("Wrong parameters value: file %s on line %d\r\n", file, line);

    for (;;) {
    }
}

卡在这个for死循环里面了,当执行 CLK_SetClockDiv() 函数时进入DDL_AssertHandler死循环,通常是因为触发了芯片的断言(Assert)机制,表明配置参数存在非法情况。

解决办法:参考了deepseek的建议,开始排查,排查了是否时钟分频参数设置错误、时钟配置寄存器可能被写保护。很幸运,确实是时钟还在写保护状态导致。解锁保护后时钟配置正常执行。

延时函数不准确、串口发送数据与PC端接受到的数据不匹配。

解决办法:发现是未正确初始化时钟导致。初始化时钟后问题解决。

J-Flash 每次打开报错 device tle9863qxw20: flash bank 0x11000000: no loader specified

解决办法:C:\Users\你的用户名\AppData\Roaming\SEGGER\JLinkDevices\JLinkDevices.xml 删除问题不复现,应该是文件中芯片地址配置错误导致

jlinkdevices.xml文件本用于J-Link识别芯片型号及硬件参数,并关联对应的Flash算法文件(.FLM)。但多次修改该文件仍报异常,删除后问题不复现。主要用HC32F460系列单片机,jlink默认的有对应的芯片类型可以用,可以暂时不用额外添加

J-Flash 添加非默认芯片

以HC32F170JATA为例:

  1. 从华大官网找到对应的芯片包,从芯片包里找到.FLM文件,这里搜索以下网上也有别人整理好的,含芯片配置文件和烧录算法;
  2. 用记事本打开JLinkDevices.xml文件,根据HC32F170JATA芯片的实际情况,将支持包内JLinkDevices.xml中的描述代码段修改,因为这里之前用别人的一直报错,打算自己重新写一下。 JLinkDevices.xml文件如下:
     <Device>
     <!-- 1. 基础信息:告诉 JLink 芯片内核是 Cortex-M0+,以及 RAM 大小和地址 -->
     <ChipInfo Vendor="XHSC" Name="HC32L17x"  WorkRAMAddr="0x20000000" WorkRAMSize="0x4000" Core="JLINK_CORE_CORTEX_M0"/>
    
     <!-- 2. 烧录算法:告诉 JLink Flash 的地址、大小,以及最重要的算法文件 (.FLM) 的存放位置 -->
     <FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/HC32L17x_128KB.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
     </Device>
    
  3. 找到你电脑上JLink的安装目录C:\Program Files\SEGGER\JLink或者C:\Users\你的用户名\AppData\Roaming\SEGGER\JLink,将.FLM和JLinkDevices.xml拷贝进去。
    • 好像在J-FlashJLink 7.62后优化了手动增加新MCU型号支持方法,在用户AppData下增加芯片1
  4. 这样重新打开J-Flash就可以找到新增芯片

改了程序下载地址之后程序起不来

问题背景: 有客户需要定制程序,需要我们提供我们软件BSP,他们自己写应用控制逻辑。当完成业务逻辑剥离后打算测试,发现程序一直跑飞,未按照预期运行,尝试DEBUG,直接运行,发现已经跑飞了,反汇编窗口跳转到了0xF404D0F8 AAAA ADD r2, sp, #0x2A8,跑飞!!单步运行后无法看堆栈信息,提示“Could not stop Cortex-M device! Please check the JTAG cable”,然后DEBUG就自动退出了!!

首先很明显确定已经跑飞了,根据我单步调试的结果问题再初始化阶段。0xF404D0F8这块不是正常程序代码地址。

尝试重启,能够稳定复现。回顾了一下我的操作,主要是移除了应用层代码,修改了程序烧录起始地址。因为不需要bootloader了,让程序直接从0开始进入。比较了下代码,没有BSP层没有大改动,程序是批量过的,考虑暂时先排除代码层面问题。

尝试屏蔽了部分初始化程序,问题仍然存在。

向大佬沟通,考虑中断跑飞了,使用__disable_irq();在初始化后屏蔽中断,问题复现。尝试在初始化前屏蔽中断问消失!缩小范围为中断问题。

查看中断向量表,#define VECT_TAB_OFFSET (0x0UL),逻辑上看中断向量表为0,没有问题。但是发现外面套了层宏。但是全局搜索都没有找到这个宏。

/* Vector Table base offset field */
#ifndef VECT_TAB_OFFSET
    #ifndef USE_OTA_FUNC
    #define VECT_TAB_OFFSET                 (0x0UL)     /*!< This value must be a multiple of 0x400. */
#else
    #define VECT_TAB_OFFSET                 (0x4000UL)     /*!< This value must be a multiple of 0x400. */
#endif

A thousand years later,仍然没找到问题,裂开了。尝试直接屏蔽此段代码,直接定义中断向量表偏移地址设置为0,故障消失。淦!

最中找到在Keil中定义了该宏,导致中断向量表错误,初始化后,进入错误中断程序地址跑飞。

跑飞常见原因:

  • 栈溢出,超出了为栈分配的空间,把旁边的数据给“踩”了。当用到旁边的数据后,程序乱掉。
  • 野指针,程序跑到了一个无效的地址。
  • 向量表错误,CPU在收到中断请求时,查了一个错误地址,导致跳到了错误的地方去执行代码。
  • 之前还遇到过供电不稳的,也又可能跑飞。
  • 也有可能Flash程序读取错误,坏块之类的。导致程序跑飞。
  • 。。。

AI战士的滑铁卢日常,联合体错误使用导致CAN通信异常问题

最近一个新项目,需要用到CAN通信。现在的MCU CAN模块已经非常集成,内部自带控制器,支持报文过滤、FIFO、自动收发,外部只需接一个CAN收发器,通过寄存器读写数据,稳定可靠。

按理说,CAN发送这种基础功能,代码写过无数遍了,闭着眼睛都能写。于是偷懒让AI帮忙重构老代码,抽离公用层,简化逻辑,重新生成一个发送函数。

/**
 * @brief  CAN发送数据
 * @param  id: CAN ID
 * @param  data: 8字节数据指针
 * @retval None
 */
void CanSendData(uint32_t id, uint8_t *data)
{
    stc_can_tx_frame_t stcTx;
    int32_t i32Ret;

    //填充发送帧
    stcTx.u32ID = id;
    stcTx.IDE = 1;
    stcTx.DLC = 8;
    stcTx.u32Ctrl = 0; // 错误,联合体内容被清空


    //复制数据
    if(data != NULL)
    {
        for(uint8_t i = 0; i < 8; i++)
        {
            stcTx.au8Data[i] = data[i];
        }
    }

    //发送
    CAN_FillTxFrame(CAN_UNIT, CAN_TX_BUF_PTB, &stcTx);
    CAN_StartTx(CAN_UNIT, CAN_TX_REQ_PTB);
}

代码非常简洁清晰:配ID格式和数据长度,最后填数据、发送。但偏偏就是发不出去。排查接线、时钟、寄存器都没问题,甚至开始怀疑硬件本身。

尝试DEBUG单步调试,发现判断为ID错误,通过打印变量,发现整个控制字相关的配置(IDE、DLC)都没有正确写入。

追根溯源,问题出在驱动库中stc_can_tx_frame_t结构体的定义上,先配置了DLC、IDE后再清空了u32Ctrl导致。真是一个愚蠢的操作,使用联合体在操作同一块内存。

AI在老项目中看到有xxx.u32Ctrl = 0这行代码,想都没想就复制过来了。还顺手调整了顺序。也是太相信AI了,给AI反馈错误信息,AI也是胡乱分析,最后只好自己来。

/**
 * @brief CAN TX frame data structure.
 */
typedef struct {
    uint32_t u32ID;                         /*!< 11 bits standard ID or 29 bits extended ID, depending on IDE. */
    union {
        uint32_t u32Ctrl;
        struct {
            uint32_t DLC: 4;                /*!< Data length code. Length of the data segment of data frame.
                                                 It should be zero while the frame is remote frame.
                                                 This parameter can be a value of @ref CAN_Data_Length_Code */
            uint32_t BRS: 1;                /*!< Bit rate switch. */
            uint32_t FDF: 1;                /*!< CAN FD frame. */
            uint32_t RTR: 1;                /*!< Remote transmission request bit.
                                                 It is used to distinguish between data frames and remote frames. */
            uint32_t IDE: 1;                /*!< Identifier extension flag.
                                                 It is used to distinguish between standard format and extended format.
                                                 This parameter can be a 1 or 0. */
            uint32_t RSVD: 24;              /*!< Reserved bits. */
        };
    };
    uint8_t au8Data[8U];                    /*!< TX data payload. */
} stc_can_tx_frame_t;

又尝试了一下,AI也是能发现的,不过需要把错误描述精确到CAN ID错误。如果无法有效的描述现象,只是回答CAN无法收发数据,像这种隐性问题AI分析也困难。

重构第一步就应该确保重构前功能正常,先做好环境的测试验证工作,不是撸起袖子就干!

参考资料

  1. 痞子衡嵌入式. (2024). 痞子衡嵌入式:从JLink V7.62开始优化了手动增加新MCU型号支持方法. https://www.cnblogs.com/henjay724/p/18203031 

results matching ""

    No results matching ""