简介围绕TMS320F28335 DSP的永磁同步电机PMSM控制项目包面向从事电机驱动、伺服控制及嵌入式实时控制的工程师和电气类学生。包内以TI官方DSP2833x固件库为基础包含完整的工程配置与源码可帮助理解PMSM磁场定向控制、SVPWM调制、电流环与速度环设计等关键算法以及DSP外设初始化、PWM中断和ADC采样等底层驱动编写方法。资源共364个文件以C源码、汇编文件、头文件为主辅以CCS工程配置project、cproject、ccsproject和链接命令文件cmd并包含若干lib库与调试日志lograr压缩包仅1.23MB结构紧凑。包内还包含DSP2833x系列的启动、ADC校准、默认中断服务等底层代码便于对照学习浮点DSP的初始化流程也方便在此基础上快速搭建自己的电机控制工程。已有131人学习该资源适合需要参考实际工程结构或进行PMSM控制算法二次开发的用户。1. 为什么一套库文件决定了 PMSM 驱动板的下限PMSM 的 FOC 算法在 F28335 上跑起来之前先要过的不是坐标变换那一关而是工程里那批不太起眼的 asm 和 c 文件。DLOG4CHC.asm、DSP2833x_CodeStartBranch.asm、DSP2833x_usDelay.asm、DSP2833x_CSMPasswords.asm、DSP2833x_DisInt.asm、DSP2833x_ADC_cal.asm、DSP2833x_DBGIER.asm、DSP2833x_SWPrioritizedDefaultIsr.c、DSP2833x_DefaultIsr.c、DSP2833x_DMA.c这一组文件单独看各有归属合在一起正好构成 F28335 电机控制工程的地基启动引导、中断入口、AD 校准、DMA 搬运和调试输出。任何一个文件没放对段、没注册进链接脚本都会出现复位循环、中断不响应或者电流波形毛刺这一类看起来很玄学的问题。这篇文章就把这十个文件在 PMSM 驱动板里的职责拆开从复位到出电流波形的链路逐个过一遍给出能直接落到工程设计里的配置方法和排错顺序。2. 启动链路CodeStartBranch、RAM 引导与 CSM 密码的三角关系2.1 CodeStartBranch.asm 的任务跳转、复制与 Flash 等待F28335 复位后芯片先执行 Boot ROM 里的引导代码根据 GPIO 引脚电平决定从 Flash、SCI、SPI 还是并行接口引导。PMSM 驱动板量产固件一般走 Flash 引导Boot ROM 在 Flash 末尾找到分支入口地址后跳转此时执行权交到 CodeStartBranch.asm。这个汇编文件做的事情非常单一在芯片上电后第一时间取得_c_int00入口地址并在跳转前把运行在 Flash 的代码段复制到 RAM 中。原因是 Flash 有等待周期而电机控制中断要求每条指令的执行时间确定矢量控制和电流环在 Flash 里直接跑会引入不确定的延迟所以 TI 的习惯是把codestart和ramfuncs段分别规划。常见的链接脚本片段是这样安排的MEMORY { FLASH_B0 : origin 0x3F8000, length 0x2000 RAML3 : origin 0x009000, length 0x2000 } SECTIONS { codestart : FLASH_B0, PAGE 0 ramfuncs : LOAD FLASH_B0, RUN RAML3, LOAD_START(_RamfuncsLoadStart), RUN_START(_RamfuncsRunStart), PAGE 0 }LOAD_START和RUN_START这两个符号由链接器自动生成_RamfuncsLoadStart表示代码在 Flash 里的起始地址_RamfuncsRunStart表示运行时的 RAM 地址。工程里通常会在main开头用memcpy把这段函数从 Flash 搬到 RAM再调用 ADC 校准或 PWM 模块初始化。如果漏了这一步ADC_cal()这类对执行时间敏感的代码就会带着 Flash 等待周期运行校准结果偏大偏小都不一定表现出来就是 ADC 采样值与万用表实测电压系统性偏离而且偏差不固定。CodeStartBranch.asm 本身还有一个容易被忽略的属性它必须在编译后位于复位入口能跳转到的位置。若链接脚本没有给codestart段分配 Flash 地址下载后第一次上电可能正常复位之后反而跑飞因为 Boot ROM 拿到的分支地址已经在 Flash 里被后续代码覆盖。排查这类问题时先看.map文件里codestart的地址再确认复位后 PC 是否落在这里。2.2 usDelay.asm微秒延时的时钟基准怎么对齐DSP2833x_usDelay.asm提供的是一个纯软件延时函数DELAY_US(A)实现原理是用循环指令消耗 CPU 周期。使用前必须确定两个前提CPU 时钟频率以及延时函数本身是否已经复制到 RAM。F28335 的 CPU 频率最高 150MHz在 CCS 工程里通常用CPU_RATE宏来换算延时周期数常见的定义是#define CPU_RATE 6.667L // 150MHz 主频下1us 对应的周期数DELAY_US(10)就会让 CPU 空转大约 10 微秒。注意这里的周期换算依赖编译预处理时CPU_RATE的值如果在 100MHz 主频下仍然用 150MHz 的系数实际延时时间会变成 15 微秒左右虽然对 EPWM 初始化这种一次性操作影响不大但会误导对软件定时精度的判断。usDelay 在 PMSM 工程里主要出现在三个位置一是 ADC 上电后的内部上电延时二是 CSM 密码操作前后的时序窗口三是传感器供电后等待稳定。最后一个场景最值得注意旋变解码芯片或霍尔传感器的上电稳定时间通常是几毫秒到几十毫秒用 DELAY_US 硬等并不合适应该配合定时器或状态机。很多新手把 usDelay 用在长延时上导致 PWM 使能前阻塞时间过长驱动板表现为上电后电机迟迟不响应或者上位机握手超时。2.3 CSMPasswords.asm 的防锁规则CSM 是 F28335 的代码安全模块DSP2833x_CSMPasswords.asm文件的内容就是 8 个 16 位密码字。TI 默认把密码放在 Flash 最后 8 个字地址在 0x3F7FF8 到 0x3F7FFF 这一段。密码判断规则比较特殊只有在密码位置的值全部为 0xFFFF 时才认为 CSM 未使能任何与 0xFFFF 不同的值都会启用安全锁定而写入全 0 则会让芯片永久锁定。这意味着烧写固件时如果这个文件里的密码被意外修改成非 0xFFFF 的 128 位值下次连接 JTAG 时仿真器就无法读取 Flash 内容只能通过擦除整个 Flash 来解锁代价是固件和校准参数全部丢失。PMSM 量产项目中 CSM 有两个实际用途一是保护固件不被随意读取二是保护 Flash 里存储的电机参数和零位标定值。设计时要分清这两种需求固件加密可以用 CSM但参数区如果和代码放在同一个 Flash 扇区擦除解锁时参数也没了。所以常见做法是把电机参数放在独立扇区运行时通过 API 读取。下列表格汇总了这三个文件的典型角色文件链接位置异常表现CodeStartBranch.asmFlash 入口 codestart 段复位后跑飞PC 不在预期范围内usDelay.asmRAML 段需要 RUN_START 指向延时不准ADC 初始化失败CSMPasswords.asmFlash 末尾 CSM_PWL 段JTAG 连不上或 Flash 全拒读CSM 密码检查要在早期开发阶段就纳入版本管理通常做法是让 CSMPasswords.asm 始终输出全 0xFFFF量产时再替换成加密后的密码值并且在烧写脚本里加一道烧完立即回读的校验步骤避免加密值写错导致整板变砖。3. 中断分级与默认 ISRDisInt、DBGIER、SWPrioritizedDefaultIsr 怎么联合工作3.1 从 DINT 到 PIEF28335 的中断优先语义F28335 的中断体系分为 CPU 级和 PIE 级两层。CPU 级的 16 个中断由IER寄存器控制其中的INT1到INT12被 PIE 模块扩展成了 12 组、每组 8 个中断源。PMSM 控制里最常用的是ADCINT1和EPWM1_INT它们分别位于 PIE 组 1 的第 1 通道和组 3 的第 1 通道。DSP2833x_DisInt.asm里的DINT指令负责关闭 CPU 级全局中断它不只是执行一条SETC INTM还会同步等待流水线中的中断请求全部处理完防止在临界区入口处漏掉一个已经触发的中断。外设中断产生后先到 PIE 组对应的PIEACK位CPU 级允许后才能进入真正的 ISR。因此写中断使能时只置位外设模块的使能位是不够的还必须同时打开 PIE 组和 CPU 级 IER 对应的位。典型配置如下EALLOW; PieVectTable.ADCINT1 adcIsr; PieVectTable.EPWM1_INT epwm1Isr; EDIS; PieCtrlRegs.PIECTRL.bit.ENPIE 1; PieCtrlRegs.PIEIER1.bit.INTx1 1; // ADCINT1 使能 PieCtrlRegs.PIEIER3.bit.INTx1 1; // EPWM1_INT 使能 IER | M_INT1 | M_INT3; EINT;这段代码先替换中断向量表再打开 PIE 分组最后使能 CPU 级中断。顺序不能反因为在 EALLOW 写保护期间如果中断已经触发向量表还没替换完就会跳到默认 ISR。IER | M_INT1 | M_INT3中的M_INT1和M_INT3是 TI 头文件里预定义的位掩码宏分别对应 PIE 组 1 和组 3。调试时如果发现中断只进一次就不再响应多半是 ISR 内没有清 PIEACK或者清理了不是本组对应的 PIEACK。3.2 SWPrioritizedDefaultIsr 与 DefaultIsr 的备份角色DSP2833x_DefaultIsr.c为 PIE 中每个中断源都生成了一个默认入口函数函数体大多只有一条ESTOP0或者空循环。DSP2833x_SWPrioritizedDefaultIsr.c则是它的软件优先级版本在此基础上额外处理了同组多中断同时触发时的排序问题。F28335 的 PIE 硬件只保证组间优先级不保证组内中断的实时响应顺序当同一 PIE 组里的两个中断源同时置位时默认行为是 CPU 按 PIE 组的通道号统一跳转到第一个产生的中断而 SWPrioritized 版本会把所有组的待处理中断按优先级重新排序避免低优先级中断抢先。对 PMSM 工程来说这个文件的价值在于兜底。FOC 电流环以 PWM 中断为节拍速度环或编码器位置事件如果也映射到同一 PIE 组一旦出现同时触发电流中断必须保证先执行。实际开发时不要把这两个默认 ISR 文件直接删掉即使工程里所有中断向量都已替换也建议保留它们用来捕获未映射或映射错误的中断源。当程序突然进入ESTOP0循环时通过 CCS 的 Call Stack 查当前 PC 位置是否落在 DefaultIsr 内能快速定位是哪个外设触发了未注册的中断。典型例子把编码器 Z 相脉冲接到 EQEP 中断但没有替换向量电机一转就死机。原因就是 Z 相中断触发后跳进了默认 ISR 并执行ESTOP0。此时查看 PIE 组 5 的PIEIER位就能确认是否未初始化 EQEP 中断向量。3.3 中断配置实例用 EPWM1 中断承载 FOC 周期FOC 的经典执行节奏是EPWM1 的时基计数器在周期匹配时触发 ADC 采样采样转换完成后触发 ADCINT1ADCINT1 中读取电流并执行电流环再返回主循环等待下一个 PWM 周期。EPWM1_INT 也可以直接承担电流环入口此时要注意采样点的位置必须避开上下桥臂开关切换瞬间。下面的代码展示了 EPWM1 中断配置的核心部分EPwm1Regs.TBPRD 7500; // 150MHz 主频20kHz PWM EPwm1Regs.TBPHS.half.TBPHS 0; EPwm1Regs.TBCTR 0; EPwm1Regs.TBCTL.bit.CTRMODE TB_COUNT_UPDOWN; // 增减计数三角载波 EPwm1Regs.TBCTL.bit.HSPCLKDIV TB_DIV1; EPwm1Regs.TBCTL.bit.CLKDIV TB_DIV1; EPwm1Regs.ETSEL.bit.INTEN 1; EPwm1Regs.ETSEL.bit.INTSEL ET_CTR_ZERO; // 周期归零时触发中断 EPwm1Regs.ETPS.bit.INTPRD ET_1ST; // 每个周期触发一次TBPRD 7500对应 20kHz 的 PWM 开关频率计算方式是 150MHz 除以 2 倍周期计数值因为增减计数模式下 TBPRD 在一个完整周期内被计数两次。INTSEL ET_CTR_ZERO在计数器归零处触发此时载波处于最低点适合作为电流采样窗口中心。将 FOC 的 Clarke 变换、Park 变换和 PI 调节器全部放在 EPWM1 中断服务函数里时整个中断执行时间要控制在 PWM 周期的一半以内也就是 20kHz 下不超过 25 微秒否则下一次中断到来时会丢事件。IRQ 处理的最后一步是清除中断标志EPWM1 的 ISR 尾部需要写EPwm1Regs.ETCLR.bit.INT 1;然后清 PIEACK 组 3。原地不动的常见错误是在中断中操作了需要 EALLOW 保护的 PIE 配置寄存器导致中断内执行时间被拉长或者误把PieCtrlRegs.PIEACK.all PIEACK_GROUP3写成了PIEACK_GROUP1结果 ADC 中断永远挂起。4. ADC 校准与 DMA 搬运F28335 模拟量链路上的两个坑4.1 ADC_cal.asm 为什么必须在 RAM 中执行F28335 的 ADC 在出厂前会写入一组校准系数芯片上电后需要通过ADC_cal()函数把 DSP 内部存储的校准值加载到 ADCOFFTRIM 和 ADCREFSEL 寄存器中。DSP2833x_ADC_cal.asm就是完成这个动作的汇编函数它被放在一个特殊段里TI 要求这个函数必须在 RAM 中执行。原因是校准过程中涉及对 ADC 寄存器的连续写入和对结果寄存器的读取而 Flash 上的等待周期会改变这些操作的相对时序导致校准值不是最优值。典型调用形式是在 main 函数的开头初始化 ADC 模块之前执行extern void ADC_cal(void); void main(void) { InitSysCtrl(); DINT; memcpy(RamfuncsRunStart, RamfuncsLoadStart, (size_t)RamfuncsLoadSize); ADC_cal(); InitAdc(); InitEPwm(); // 后续初始化 }memcpy把包含 ADC_cal 在内的全部 RAM 函数从 Flash 复制到 RAM然后调用真正的校准函数。这里有一个顺序细节ADC_cal()必须在InitAdc()之前调用因为InitAdc()会写入 ADCTRL3 寄存器并切换 ADC 的参考电压源如果先初始化再校准参考源切换会覆盖校准结果。另一个细节是ADC_cal()不需要使能外设时钟因为校准逻辑直接访问器件内部存储与外设时钟无关。若跳过这步ADC 结果会出现固定的 offset 误差轻则电流零漂重则让 FOC 的 Id 轴产生直流偏置电机低速时出现明显抖动。4.2 DMA 从 ADC 结果寄存器搬运数据FOC 电流环运行在 20kHz每个 PWM 周期要采集三路电流和一路母线电压。若全部用 CPU 在中断里读取 ADC 结果寄存器会消耗大约几百个 CPU 周期累积到整个控制周期里并不划算。DMA 的作用是让 ADC 转换完成后自动把结果搬到一个连续缓冲区CPU 只读缓冲区不读 ADC 寄存器。F28335 的 DMA 模块包含六路通道其中 DMA6 在 TI 的软件例程中常用于 ADC 搬运。配置核心如下DmaRegs.DMA6.SRC (Uint32)AdcResult.ADCRESULT0; DmaRegs.DMA6.DST (Uint32)adcBuffer; DmaRegs.DMA6.BURST 0x0001; // 一次突发搬运 1 个 16 位字 DmaRegs.DMA6.TRANSFER 0x0010; // 16 次突发组成 1 帧 DmaRegs.DMA6.MODE 0x0010; // 触发源选择 ADCINT1SRC指向 ADC 结果寄存器 0 号通道DST指向内存缓冲区BURST 0x0001表示每个突发只搬一个字节TRANSFER 0x0010表示每个完整帧共搬运 16 个数据对应 16 次 ADC 转换结果MODE 0x0010把触发源映射到 ADCINT1也就是 ADC 转换完成后自动启动 DMA不需要 CPU 干预。实际使用中还要使能 DMA 通道和 PIE 的 DMA 中断位并且在 DMA 中断里切换缓冲区避免 CPU 读取时 DMA 正在写同一块内存。DMA 模式的优劣要看采样策略。单电阻电流采样时一次 PWM 周期内要在一个载波周期中分时采样两相电流这种场景不适合 DMA 连续搬运 16 次的结果更适合在中断里精确读取与 PWM 占空比相关的特定时刻。而双电阻或三电阻采样时ADC 转换序列已经通过硬件 SOC 触发错开DMA 把结果连续搬到缓冲区反而更稳定。选择的关键是看模拟量的采样时刻是否固定固定则用 DMA不固定则老老实实中断读取。4.3 采样触发与搬运触发的时序对齐ADC 采样的触发源通常选择 EPWM1 的某个事件点比如计数器到零、周期匹配或比较匹配。以三电阻 FOC 为例电流采样发生在 PWM 开关周期的中点附近此时三相下桥臂都导通电流通过采样电阻的路径最稳定。EPWM1 的时基归零事件恰好对应开关周期的中点因此典型配置是把 ADC 的 SOC 触发源指向这个事件。触发源特点适用场景EPWM1_ADCSOCA计数器归零采样点固定避开开关跳变三电阻 FOC20kHz 载波EPWM1_ADCSOCA比较匹配采样点可调跟随占空比变化单电阻采样重构两相电流CPU 软件触发灵活但抖动大低速标定离线测试DMA 的触发源设置要和 ADC 转换完成事件严格对应否则搬运的数据可能是上一周期残留。排查这类同步问题时最简单的方法是给 DMA 中断和 EPWM 中断分别置 GPIO 翻转信号把两路信号同时接到示波器上观察翻转沿的相对时间间隔。正常情况是 DMA 中断翻转沿紧跟在 ADC 转换结束后且每个 PWM 周期重复。如果抖动超过 1 微秒就说明触发源的映射或者转换结束标志的清除有问题。5. DLOG4CHC 与 PMSM 控制参数的观测连接5.1 DLOG4CHC 的四通道示波器链路怎么搭DLOG4CHC 是 F28335 平台上专门用来看内部控制变量的一套四通道数据记录宏。它的思路是把要观测的变量地址登记到一组结构中然后由后台任务周期性地往缓冲区里写入这些变量的实时值最后通过外部接口恢复成波形。DLOG4CHC 的典型对象是 Id、Iq、角度和转速配合外部 DAC 或上位机示波器就能在电机运行时看到完整的电流波形。初始化逻辑不复杂DLOG_4CH_BG.ptr1 id_fb; DLOG_4CH_BG.ptr2 iq_fb; DLOG_4CH_BG.ptr3 theta_elec; DLOG_4CH_BG.ptr4 speed_ref; DLOG_4CH_BG.size 0x400;ptr1到ptr4分别指向四个待观测变量size 0x400表示缓冲区深度为 1024 个点。然后要在每个 FOC 周期调用update宏把当前值写入缓冲区后台循环里调用output宏把缓冲区数据发送到外部设备。这个结构和现代电机调试器里的变量观测窗口是同一个思路只不过 DLOG4CHC 把链路做到了最底层。实际操作中常把DLOG_4CH_BG这个结构体放在整个 FOC ISR 的最后等电流环完全跑完再记录这样捕获到的就是最终控制效果而非中间过程。注意记录动作本身也会消耗 CPU 周期调试阶段可以常开性能验证时应关闭。size不能设得过大否则输出阶段会长时间占用串口或 DAC影响后台调度。5.2 SVPWM 扇区判断与有感零位标定的通道布局SVPWM 是 FOC 输出级的核心它把 Id/Iq 经过反 Park 变换后的 Uα、Uβ 组合成三相 PWM 占空比。扇区判断是 SVPWM 的第一步典型的实现是int calcSector(float uAlpha, float uBeta) { int sector 0; float tA uBeta; float tB 0.866f * uAlpha - 0.5f * uBeta; float tC -0.866f * uAlpha - 0.5f * uBeta; if (tB 0) sector | 1; if (tC 0) sector | 2; if (tA 0) sector | 4; return sector; }tA、tB、tC分别代表三相对称坐标系下的中间变量通过判断正负号组合出 1 到 6 的扇区编号。扇区判断直接影响后续相邻电压矢量的作用时间计算如果 Uα、Uβ 的缩放比例不一致扇区判断会出错表现为电机带载后电流波形在扇区边界处畸变。有感 PMSM 的零位标定本质是找到编码器零位与电角度零点的偏差。常见做法是给 q 轴电流为零、d 轴注入正向电流让转子被磁场锁定到 U 相绕组轴线上此时编码器角度与真实电角度的差值就是零位偏置。把这个偏置量存到 Flash 后每次上电运行时从 Flash 加载。这部分的数据适合用 DLOG4CHC 同时观察标定前角度、标定后角度和 d 轴电流验证转子是否真正锁定。在无感 FOC 控制里如果观测器没有收敛角度通道会出现持续漂移有感模式下角度通道应保持稳定只有负载突变时才有短暂波动。5.3 开关频率与电流采样时刻的关系PMSM 驱动板常见的 PWM 开关频率在 10kHz 到 20kHz 之间。选低了电流纹波大电机噪声明显选高了开关损耗上升ADC 采样窗口变短。F28335 的 150MHz 主频跑 16kHz 电流环一个周期约 62.5 微秒去掉死区、AD 转换和 SVPWM 计算留给控制环的时间仍然足够但若加上速度环、通讯协议栈和故障处理余量就会变得紧张。因此实际工程中经常把电流环放在 EPWM1 中断里速度环放在主循环或低优先级定时器中断。采样时刻要跟着 PWM 载波走双电阻采样时采样窗口放在下桥臂全部导通的区间。当调制比接近 1 时某一相下桥臂导通时间极短采样窗口不足电流重构会产生畸变。解决方式是限制最大调制比到 0.9 左右或者改用单电阻采样并把采样点动态跟随占空比。DLOG4CHC 的四个通道里用两个通道分别放同一相电流的给定值和反馈值就能直观看出高调制区是否出现了振铃或削顶。6. 从能编译到出波形五个边调边验证的技巧6.1 GPIO 翻转测量控制环耗时在 EPWM1 中断入口置高 GPIO0、出口置低 GPIO0用示波器量高电平时间就是整个 FOC 中断的实际执行时间。这个时间不包含 ADC 转换等待但如果中断内调用了DELAY_US或等待某个外设标志会显著拉宽高电平宽度。首次跑 FOC 前先做这一步确认执行时间不超过 PWM 周期的 50%。interrupt void epwm1Isr(void) { GpioDataRegs.GPASET.bit.GPIO0 1; // FOC 计算主体 GpioDataRegs.GPACLEAR.bit.GPIO0 1; PieCtrlRegs.PIEACK.all PIEACK_GROUP3; }GPIO0 翻转的额外开销只有两条指令对测量结果影响可忽略。注意清除 PIEACK 的代码放在 GPIO 置低之后否则测量出来的时间会包含中断向量返回前的多余指令。6.2 中断不响应先查 PIEACK 和 IER中断只进一次就卡死的现象先查 ISR 末尾是否有PieCtrlRegs.PIEACK.all PIEACK_GROUPx再查IER是否在初始化后被动过。调试器单步执行时EINT后的第一次中断响应会延迟到下一指令边界这是正常的不要误判。6.3 ADC 校准失效的表现如果 ADC 的采样结果整体偏移比如上电后读到的电压比实际值高 20 个 LSB 左右优先检查ADC_cal()是否被调用、是否在InitAdc()之前调用。RAAM 函数复制缺失时芯片会执行 Flash 里的校准代码这种问题的最快确认方法是在线查看AdcOffsetTrim寄存器值非预期值基本可以断定校准代码没有正确执行。6.4 CSM 锁死后的救援路径下载程序后 JTAG 无法连接且不像是电源问题就要怀疑 CSM 密码被非 0xFFFF 值覆盖。F28335 的 CSM 锁定后Code Composer Studio 会提示无法读取内存此时唯一的软件层面恢复方式是对芯片执行全擦除。量产环境里这一步会损失校准参数所以批产烧写脚本建议烧录完毕后回读 CSM 区域确认密码值正确再断电。6.5 单电阻采样窗口的动态核对单电阻采样重构三相电流时电流在占空比接近满值和接近零值的区域最容易失真。用 DLOG4CHC 同时记录 Iα 和 Iβ在低速带载工况下如果看到某相电流在 60 度边界附近出现明显尖峰大概率是采样窗口不足或采样点偏到了开关切换时刻。适当减小最大调制比或把 ADC 采样从固定归零触发改为比较匹配触发往往能直接消除这个失真。本文还有配套的精品资源点击获取