4. 软件框架设计:主循环架构、状态机设计、任务调度与优先级

好,咱们进入实战环节的骨架部分——软件框架。很多同学拿到一个FOC项目,上来就写算法,写到一半发现逻辑乱成一锅粥。我早期也犯过这个错,后来被一个老工程师点醒:先搭框架,再填肉。今天这章,我就把在线校准这套框架怎么搭,掰开了讲给你听。

4.1 主循环架构:别让CPU闲着

嵌入式系统里,主循环就是心脏。我习惯把主循环分成三个层级:

  • 硬实时层:电流环、速度环,必须用定时器中断触发,1kHz~20kHz不等。
  • 软实时层:状态机轮询、通讯处理、校准逻辑,放在主循环里跑。
  • 后台层:日志输出、参数保存、看门狗喂食,优先级最低。

在线校准属于软实时层。为什么?因为校准过程中,你不需要每微秒都去调整零位偏移,但也不能拖太久——电机可能还在转呢。

核心原则:主循环不能阻塞。校准过程中如果遇到通讯等待或EEPROM写入,必须拆成状态机片段,否则电流环一断,电机就抖。

我见过一个项目,工程师在校准函数里直接用了HAL_Delay(100),结果电机直接啸叫。嗯,从那以后,我所有校准逻辑都改成了非阻塞状态机。

4.2 状态机设计:三个状态走天下

在线校准的状态机,说白了就三个状态:空闲、校准中、校准完成。你想想看,再多就乱了。

状态 触发条件 主要动作 退出条件
空闲 系统初始化完成 / 校准完成 等待上位机指令或自动触发条件 收到校准开始指令
校准中 进入校准流程 执行旋转/对齐/采样/计算 零位偏移计算完成
校准完成 偏移值已写入 保存参数、切换回正常FOC 系统复位或重新触发校准

这里有个坑——校准中状态内部其实还有子状态。我一般会再拆成:

  1. 子状态1:准备——关闭速度环,切换到开环电流模式。
  2. 子状态2:对齐——给d轴注入电流,让转子锁到已知位置。
  3. 子状态3:采样——读取编码器值,记录原始数据。
  4. 子状态4:计算——做平均或滤波,算出零位偏移。
  5. 子状态5:验证——再转一圈,确认偏移值正确。

小技巧:每个子状态都设一个超时计数器。我曾经遇到过电机负载太大,转子没锁住就采样,结果偏移值差了30度。加个超时保护,至少不会让系统死在那。

4.3 任务调度与优先级:谁先谁后要拎清

在线校准不是孤立的任务,它要和电流环、通讯、故障检测抢CPU。我常用的调度策略是:

  • 电流环中断:最高优先级,谁都不能打断。校准期间电流环照常跑,只是目标值变了。
  • 校准状态机:主循环中轮询,每次只执行一个子状态的一小步。
  • 通讯任务:中等优先级,用环形缓冲区收数据,主循环里解析。
  • 故障检测:每10ms检查一次过流、过温,发现异常立刻强制退出校准。

我个人习惯用时间片轮询+状态机的组合。不用RTOS,因为很多低成本MCU跑不了。代码结构大概这样:

void main_loop(void)
{
    while(1)
    {
        // 1. 通讯解析(非阻塞)
        comm_process();

        // 2. 校准状态机(每次只跑一步)
        calib_state_machine();

        // 3. 故障检测(每10ms一次)
        if(time_elapsed_ms >= 10)
        {
            fault_check();
            time_elapsed_ms = 0;
        }

        // 4. 后台任务(日志、参数保存)
        background_tasks();
    }
}

你看,校准状态机只是主循环里的一个函数,每次调用只执行一个子状态的一小段代码。这样即使校准过程需要几秒钟,电流环也不会断。

注意:校准过程中如果检测到电机堵转或过流,必须立即强制退出校准状态,回到空闲状态,并置故障标志。我曾经在实验室里忘了加这个保护,结果校准到一半电机卡住,电流飙升到10A,差点烧了驱动板。

4.4 整体框架图:一张图看懂所有

下面这张SVG图,把主循环、状态机、任务优先级的关系画清楚了。你可以把它贴在开发板旁边,写代码时对照着看。

在线校准软件框架图 在线校准软件框架图 主循环 (Main Loop) 通讯解析 (中优先级) 校准状态机 (软实时) 故障检测 (10ms周期) 校准状态机 子状态1: 准备 子状态2: 对齐 子状态3: 采样 子状态4: 计算 子状态5: 验证 定时器中断 (最高优先级) 电流环 FOC 说明: 校准状态机在主循环中轮询,每次只执行一个子状态的一小步,不阻塞电流环中断。

4.5 避坑指南:我踩过的几个坑

框架搭好了,但实际跑起来还是会遇到问题。我挑几个典型的说说:

  • 坑1:校准过程中看门狗超时——校准如果涉及EEPROM写入,可能耗时几十毫秒。我习惯在校准子状态里手动喂狗,或者把写入操作拆成多次。
  • 坑2:通讯干扰导致误触发校准——曾经有个项目,上位机误发了校准指令,电机在高速运转时突然进入校准模式,直接飞车。后来我在空闲状态加了速度阈值判断:电机转速超过5%额定转速时,忽略校准指令。
  • 坑3:校准完成后的参数切换时机——零位偏移算出来后,不能直接在中断里修改。我一般会在主循环里设一个pending_update标志,等电流环进入安全窗口(比如过零点)再切换。

我的习惯:每个状态切换都打印一条调试日志,包括时间戳和当前状态ID。这样出了问题,翻日志就能定位到是哪个子状态卡住了。别问我怎么知道的——调了三天三夜的教训。