4、算法移植准备:原算法代码结构分析、DSP内存映射规划、外设接口适配(ADC/ENC/PWM)

好,咱们正式开始动手移植前的准备工作。这一步做扎实了,后面能省掉一大半的调试时间。我见过不少工程师,拿到代码就往DSP里灌,结果跑起来各种奇怪问题——要么电流采样不对,要么PWM波形乱跳。说白了,就是移植前的功课没做够。

今天这一节,咱们就聚焦三件事:把原算法代码的结构摸透、把DSP的内存安排好、把外设接口对好。这三件事做完,你心里就有底了。

4.1 原算法代码结构分析

拿到一套现成的PMSM控制算法代码,别急着看细节。我个人的习惯是,先画一张代码模块依赖图。你想想看,电机控制代码通常分几大块:

  • 主控循环:一般是1kHz或者10kHz的定时中断,里面跑FOC核心计算
  • 电流环:PI调节器、坐标变换(Clark/Park)
  • 速度环:速度PI、转速计算
  • 位置环:如果有的话,一般是位置指令生成
  • 采样与保护:ADC触发、过流保护、故障处理
  • 通信与调试:SCI、CAN、上位机交互

我建议你先把这些模块的函数调用关系理清楚。比如电流环是在哪个中断里调用的?速度环是定时触发还是事件触发?这些信息直接决定了你后续怎么把代码往DSP的框架里塞。

核心要点:原代码的时序依赖是最容易出问题的地方。比如电流环必须在PWM周期开始前算完,否则输出会滞后一拍。我在项目中遇到过,原代码在STM32上跑得好好的,移植到DSP后电流波形开始抖动,查了两天才发现是中断优先级没配好,导致电流环计算被其他任务打断了。

这里我画了一张代码结构分析的流程图,帮你理清思路:

原算法代码结构分析流程 主控循环(定时中断) 电流环(FOC核心) 速度环(PI调节) 采样与保护(ADC/故障) 通信与调试(SCI/CAN) ▶ 确定中断频率(10kHz/1kHz) ▶ 检查Clark/Park变换实现 ▶ 确认PI参数格式(Q格式) ▶ 检查ADC触发与PWM同步 ▶ 确认通信协议与数据格式

4.2 DSP内存映射规划

DSP的内存架构和普通MCU不太一样。以TI的C2000系列为例,它有程序内存(Flash)数据内存(RAM),而且RAM还分好几个段。你想想看,电机控制里哪些数据要放RAM?哪些可以放Flash?

我一般这样规划:

内存区域 存放内容 说明
Flash(程序区) 代码、常量表(如sin/cos表)、PI参数初始值 掉电不丢失,但读取速度慢
RAM(数据区) 全局变量、栈、堆、中间计算结果 速度快,但掉电丢失
CLA RAM(如有) 控制律加速计算数据 可独立于主CPU运行
PieVectTable 中断向量表 必须映射到RAM(可重定位)

个人经验:我习惯把电流环的PI参数坐标变换矩阵放在RAM里,因为调试时经常要在线修改。而sin/cos查找表这种只读数据,直接放Flash就行,省RAM空间。曾经有个项目,我把整个查找表都放RAM里,结果内存爆了,后来才发现Flash里其实也能直接读取——嗯,这个坑我替你们踩过了。

具体到DSP的链接器命令文件(.cmd文件),你需要定义好各个段的起始地址和长度。比如:

/* 示例:F28379D的cmd文件片段 */
MEMORY
{
    FLASH_A  : origin = 0x080000, length = 0x002000  /* 128KB */
    RAMLS0   : origin = 0x00A000, length = 0x000800  /* 2KB */
    RAMLS1   : origin = 0x00A800, length = 0x000800  /* 2KB */
    RAMGS0   : origin = 0x00C000, length = 0x002000  /* 8KB */
}

SECTIONS
{
    .text       :> FLASH_A      /* 代码段 */
    .const      :> FLASH_A      /* 常量段 */
    .ebss       :> RAMLS0       /* 全局变量 */
    .stack      :> RAMLS1       /* 栈 */
    .buffer     :> RAMGS0       /* 自定义缓冲区 */
}

注意:DSP的堆栈大小一定要留够。电机控制里经常有嵌套中断,如果栈溢出,程序会跑飞到莫名其妙的地方。我建议至少留2KB的栈空间,如果用了浮点运算,还要考虑FPU寄存器压栈的开销。

4.3 外设接口适配(ADC/ENC/PWM)

这部分是移植工作的重头戏。原算法可能用的是STM32的HAL库,到了DSP这边,你得用寄存器级或者DriverLib重新配置。我挑三个最关键的接口来说:

4.3.1 ADC适配

PMSM控制里,ADC用来采样相电流和母线电压。DSP的ADC模块有几个特点:

  • 同步采样:C2000系列支持两路ADC同时采样,正好用来采两相电流
  • 触发源:一般用PWM的SOC(Start-of-Conversion)信号触发,保证采样时刻和PWM同步
  • 结果寄存器:采样结果存在ADCRESULTx里,注意是12位左对齐还是右对齐

我遇到过一个问题:原代码在STM32上用的是单端采样,但DSP的ADC输入范围是0~3.3V,而电流传感器的输出可能是带偏置的。这时候你得算好偏置电压,在软件里做偏移校正。否则采出来的电流值会偏大或偏小,FOC算出来全是错的。

4.3.2 编码器接口适配

编码器(ENC)接口,DSP通常用eQEP模块来处理。你需要配置:

  • 计数模式:方向计数还是正交计数
  • 位置计数器:QPOSCNT,注意它的位数(一般是32位)
  • 索引信号:用来找零位,每次上电后需要做一次寻零操作

避坑指南:我曾经在移植时忽略了编码器线数的配置。原代码用的是2500线的编码器,但DSP的eQEP默认配置是1000线,结果位置计算差了2.5倍,电机转起来一顿一顿的。所以一定要检查位置分辨率的匹配。

4.3.3 PWM适配

PWM模块是电机控制的执行端。DSP的ePWM模块功能非常强大,但配置也相对复杂。你需要关注:

  • 时基周期:决定了PWM频率,比如20kHz对应50us周期
  • 比较值:CMPA和CMPB,用来产生占空比
  • 死区时间:防止上下桥臂直通,一般设置200~500ns
  • 动作限定:比如PWM1A高电平有效,PWM1B低电平有效

我个人的习惯是,先把PWM配置成对称三角波模式,这样ADC可以在波谷和波峰触发采样,得到最稳定的电流值。然后逐步调整死区时间和比较值,确保波形正确。

好了,这三块内容讲完,你手头应该有一份清晰的移植检查清单了。代码结构分析帮你理清了依赖关系,内存映射规划让你知道数据放哪,外设接口适配则解决了硬件层面的对接问题。这三步走完,移植工作就完成了一半。

最后说一句:别急着一次把所有外设都配好。我建议先配PWM,输出一个固定占空比的波形,用示波器看对不对。然后配ADC,用手摸一下电流传感器,看数值有没有变化。最后配编码器,用手转电机,看位置计数器是不是在递增。每一步都验证通过,再往下走。这样出了问题,你能很快定位到是哪个环节。


公众号:蓝海资料掘金营,微信deep3321