HC32开发笔记
记录,为日后遇到类似问题提供思路。
常见问题&解决办法
时钟初始化过程中,进入到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为例:
- 从华大官网找到对应的芯片包,从芯片包里找到.FLM文件,这里搜索以下网上也有别人整理好的,含芯片配置文件和烧录算法;
- 用记事本打开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> - 找到你电脑上JLink的安装目录
C:\Program Files\SEGGER\JLink或者C:\Users\你的用户名\AppData\Roaming\SEGGER\JLink,将.FLM和JLinkDevices.xml拷贝进去。- 好像在J-FlashJLink 7.62后优化了手动增加新MCU型号支持方法,在用户AppData下增加芯片1
- 这样重新打开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分析也困难。
重构第一步就应该确保重构前功能正常,先做好环境的测试验证工作,不是撸起袖子就干!
参考资料
-
痞子衡嵌入式. (2024). 痞子衡嵌入式:从JLink V7.62开始优化了手动增加新MCU型号支持方法. https://www.cnblogs.com/henjay724/p/18203031 ↩