4. 位置环的软件架构:位置环在MCU/DSP中的实现框架
好,咱们聊完了位置环的理论和参数整定,接下来得落地了。
你想想看,再好的算法,如果不能在芯片上跑起来,那就是纸上谈兵。这一章,我就带你看看位置环在MCU或DSP里到底是怎么个活法。
说白了,就是软件架构。我这些年调试过的伺服驱动器、机器人关节,位置环的实现框架其实大同小异。核心就三件事:中断怎么安排、主循环干什么、实时性怎么保证。
4.1 位置环在MCU/DSP中的实现框架
先给你画个总图。我个人习惯把整个运动控制软件分成三层:
顶层:应用层 —— 负责通讯、参数管理、状态机切换。
中间层:实时控制层 —— 位置环、速度环、电流环,全在这里。
底层:硬件抽象层 —— 定时器、编码器接口、PWM输出、ADC采样。
位置环就住在中间层。它不能太靠上,否则实时性不够;也不能太靠下,否则耦合太紧,不好维护。
下面这张图是我用SVG画的,展示了位置环在软件框架中的位置和信号流向。你看一眼就明白了。
嗯,这张图你看懂了吗?从上往下是指令流,从下往上是反馈流。位置环夹在中间,既要接收应用层的目标位置,又要读取硬件层的实际位置,算出差值后输出给速度环。
4.2 中断服务程序与主循环的配合
这是很多初学者容易搞混的地方。我直接说结论:位置环必须在中断里跑,不能在主循环里跑。
为什么?因为主循环的执行时间不确定。你写个while(1),里面塞了通讯、显示、参数处理,万一哪次通讯卡住了,位置环就错过了采样时刻。这在运动控制里是致命的。
我个人习惯的做法是:
- 主循环:处理非实时任务。比如EtherCAT通讯解析、上位机指令缓存、故障记录、参数保存。
- 定时器中断:触发位置环计算。中断频率就是位置环的采样频率。
- 中断里只做最核心的事:读编码器 → 算位置误差 → 位置环PID → 输出给速度环。其他一概不干。
我的经验:我曾经在一个项目里把位置环和通讯放在同一个中断里,结果通讯偶尔丢包,位置环跟着抖动。后来我把通讯扔回主循环,位置环单独用一个高优先级定时器中断,问题立刻解决。记住:中断里只做必须实时的事。
下面是一个典型的伪代码框架,你看一下:
// 主循环 - 非实时任务
void main_loop(void)
{
while(1)
{
// 处理上位机指令(非实时)
if(comm_new_cmd_ready())
{
parse_command(); // 解析指令
update_target_pos(); // 更新目标位置
}
// 参数管理
if(param_need_save())
save_parameters_to_flash();
// 状态显示
update_led_status();
// 喂狗
kick_watchdog();
}
}
// 定时器中断 - 位置环实时计算
void TIMx_IRQHandler(void)
{
// 1. 读取编码器当前位置
int32_t actual_pos = encoder_read();
// 2. 计算位置误差
int32_t error_pos = target_pos - actual_pos;
// 3. 位置环PID计算
int32_t speed_ref = position_pid_calc(error_pos);
// 4. 限幅处理
if(speed_ref > MAX_SPEED) speed_ref = MAX_SPEED;
if(speed_ref < -MAX_SPEED) speed_ref = -MAX_SPEED;
// 5. 输出给速度环(速度环在下一个中断层级)
set_speed_reference(speed_ref);
// 6. 清除中断标志
TIM_ClearITPendingBit();
}
注意:中断服务程序里不要调用printf、delay、malloc这类阻塞或不可重入的函数。我见过有人在中断里打印调试信息,结果系统直接卡死。调试信息请用DMA或环形缓冲区,中断只负责往里写数据,主循环负责往外打印。
4.3 位置环的实时性要求
实时性,说白了就是「在规定的时间内必须完成规定的事」。对于位置环,这个「规定时间」就是采样周期。
举个例子:位置环跑1kHz,意味着每1ms要完成一次完整的计算。如果某次计算花了1.5ms,那下一次采样就错过了。这叫「任务超时」,在运动控制里是大忌。
我整理了一个表格,不同应用场景对位置环实时性的要求:
| 应用场景 | 位置环频率 | 允许最大抖动 | 典型MCU/DSP |
|---|---|---|---|
| 工业伺服驱动器 | 1kHz ~ 5kHz | < 10μs | TI C2000, STM32G4 |
| 机器人关节 | 2kHz ~ 10kHz | < 5μs | Infineon XMC, NXP RT |
| CNC数控机床 | 500Hz ~ 2kHz | < 20μs | FPGA + ARM |
| 3D打印机 | 200Hz ~ 1kHz | < 50μs | STM32F4, ESP32 |
你看,工业伺服要求抖动小于10微秒。这意味着你的中断响应时间、代码执行时间都必须严格控制。
怎么保证实时性?我给出几条实战建议:
- 关中断时间要短:不要在位置环中断里关全局中断去操作Flash或等待外设。如果必须操作,用DMA或乒乓缓冲。
- 计算量要预估:位置环PID计算、三角函数、浮点运算,这些都要算好时钟周期。我一般会在代码里加一个GPIO翻转,用示波器量实际执行时间。
- 优先级要合理:位置环中断优先级要高于通讯中断,低于电流环(如果有)。别让通讯中断把位置环打断了。
- 避免动态内存分配:中断里用
malloc?想都别想。所有变量都静态分配或栈分配。
核心原则:位置环的代码执行时间,必须小于采样周期的80%。留出20%的余量给中断嵌套和硬件响应。比如1kHz的采样周期是1000μs,你的代码必须在800μs内跑完。超过这个数,就该优化了。
嗯,说到优化,我提一个我常用的技巧。如果位置环计算量太大,可以把一些不紧急的运算挪到主循环。比如位置环的积分项可以拆成两部分:快速积分在中断里算,慢速积分(比如抗饱和处理)在主循环里算。这样既保证了实时性,又不丢功能。
好了,这一章的内容就这些。位置环的软件架构,说白了就是「中断扛实时,主循环扛业务」。把这个关系理清了,你的代码就不会乱。下一章我们聊位置环的调试工具和方法,到时候我会带几个实战案例,你准备好示波器就行。