ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

STM32 FOC调试记录

STM32 FOC调试记录

STM32 FOC调试记录:程序卡死在set_pwm_duty,weak符号踩坑

现象

ADC中断正常后,把电流模式代码加进 while(1):

c

motor_control_context.torque_norm_d = 0;

motor_control_context.torque_norm_q = 0.4;

motor_control_context.type = control_type_torque;

HAL_Delay(1000);

程序卡在这段代码后面,后面的printf不打印了。更严重的是,电机开始发烫——PWM还在输出,但FOC中断不再更新占空比,某一相持续通电。

排查过程

第一步:用printf逐段定位

在关键位置加printf,看程序执行到哪一步:

c

printf("A\r\n");

motor_control_context.type = control_type_torque;

printf("B\r\n");

HAL_Delay(1000);

printf("C\r\n");

打印到B就停了,说明卡在 HAL_Delay(1000) 里或者它后面。

第二步:在lib_torque_control里加printf

c

void lib_torque_control(float torque_norm_d, float torque_norm_q)

{

printf("T1\r\n");

float d = torque_d_loop(torque_norm_d);

printf("T2\r\n");

float q = torque_q_loop(torque_norm_q);

printf("T3\r\n");

foc_forward(d, q, rotor_logic_angle);

printf("T4\r\n");

}

打印到T3就停了,卡在 foc_forward。

第三步:在foc_forward里加printf

c

void foc_forward(float d, float q, float rotor_rad)

{

printf("F1\r\n");

float d_u = 0, d_v = 0, d_w = 0;

svpwm(rotor_rad, d, q, &d_u, &d_v, &d_w);

printf("F2\r\n");

set_pwm_duty(d_u, d_v, d_w);

printf("F3\r\n");

}

打印了4个F1(进入函数、三个变量初始化、svpwm完成),第5个F1(set_pwm_duty之后)没打印。

卡在set_pwm_duty函数里。

根因:weak符号被链接器选中

打开 foc.c,发现里面有一个weak版本的 set_pwm_duty:

c

void set_pwm_duty(float d_u, float d_v, float d_w) __attribute__((weak));

void set_pwm_duty(float d_u, float d_v, float d_w)

{

while (1)

;

}

我的 main.c 里明明有强符号版本:

c

void set_pwm_duty(float d_u, float d_v, float d_w)

{

d_u = min(d_u, 0.9);

d_v = min(d_v, 0.9);

d_w = min(d_w, 0.9);

__disable_irq();

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, d_u * htim1.Instance->ARR);

// ...

__enable_irq();

}

但链接器选了 foc.c 里的weak版本,运行时卡在 while(1)。

关于weak符号

__attribute__((weak)) 是GCC/ARM编译器的弱符号机制。

规则:如果别处有同名的强符号,链接器就用强符号;如果没有,才用弱符号。

原作者的意图:

foc.c 是算法层,不应该关心底层硬件

所以放一个weak版本的 set_pwm_duty 作为占位

如果你的工程里实现了硬件驱动,就用你的

如果忘了实现,就执行 while(1) 死循环,让你一运行就发现

这个设计思路是对的,但在我这翻车了。

为什么链接器选错了

可能的原因:

链接顺序问题:foc.o 在 main.o 之前被链接,链接器先看到weak版本就定了

编译器对weak的处理有差异:不同工具链行为可能不一致

main.c里的强符号因为某种原因没被识别

结果就是本该用“主胎”的,用了“备胎”,卡死在while(1)。

修复方法

删掉 foc.c 里的weak函数定义,只保留一行声明:

c

void set_pwm_duty(float d_u, float d_v, float d_w);

这样链接器只能去 main.c 里找实现,没有选择余地。

改完后编译烧录,程序不再卡死,电机正常响应。

声明和定义的区别

写法 类型 作用

void set_pwm_duty(float d_u, float d_v, float d_w); 声明 告诉编译器有这个函数,具体实现去别处找

void set_pwm_duty(float d_u, float d_v, float d_w) { ... } 定义 给出函数的完整实现

唯一区别在末尾:有分号 = 声明,有花括号 = 定义。

总结

项目 内容

现象 程序卡死,电机发烫

根因 链接器选了foc.c里的weak版本,卡在while(1)

解决 删掉weak定义,只保留声明

教训 weak符号调试时要小心,编译期报错比运行期死循环好

返回列表