【DSP学习】C2000_PWM中断程序_笔记

在 C2000 上写一个 PWM 发波与中断程序(F28335 实战)

整理自《DSP 自学视频(顾老师)从入门到编写(C2000)》第 18 集《写个 PWM 发波与中断的程序》 (UP 主:Study-Charging)。

本篇把视频里的讲解流程与关键配置梳理成文字,方便对照代码复习。


一、这节课要实现什么

用 F28335 的 ePWM 模块写一个完整工程,达到下面几个目标:

  • 使用 EPWM1 单元的两个引脚 EPWM1A 、EPWM1B ,输出一对互补的 PWM 波形;
  • PWM 频率固定为 10 kHz;
  • EPWM1A 与 EPWM1B 之间插入 1 µs 死区时间,用来安全驱动桥式电路(避免上下管直通);
  • 启用 ePWM 的周期中断 ,在中断服务函数里实时改变占空比。

提示:视频里还顺带介绍了一个辅助工具「C2000 助手」(可在 hellodsp 官网下载),写程序时用来快速查寄存器,不必死记硬背。


二、先配置 GPIO 引脚复用

要让 EPWM1A / EPWM1B 真正从芯片引脚上发出波形,第一步是把对应的 GPIO 引脚配置成 ePWM 功能:

  • EPWM1A → 对应 GPIO0;
  • EPWM1B → 对应 GPIO1。

做法:把 GPAMUX 寄存器里相应的位设为 1,引脚就变成 EPWM1A / EPWM1B 功能;如果设成 0,它就是普通 GPIO。这一步做错,后面再怎么配都不会出波形。


三、时钟与频率:怎么算出 TBPRD = 3750

PWM 周期由两个因素决定:时钟 和计数模式 。本节课假设采用增减计数模式(up-down count)。

在增减计数模式下,一个 PWM 周期等于 2 × TBPRD (计数器从 0 数到 TBPRD,再减回 0)。计数器每记一次数所花的时间,取决于 TBCLK。

TBCLK 由 SYSCLKOUT 和 TBCTL 里的两个分频位决定:

  • CLKDIV(对应视频里的 c2kdiv);
  • HSPCLKDIV(对应视频里的 hspclkdiv)。

本例设置:CLKDIV 效果为 1,HSPCLKDIV 效果为 2,而 SYSCLKOUT = 150 MHz,于是:

复制代码
TBCLK = 150 MHz / (1 × 2) = 75 MHz

也就是说计数器每记一次数,耗时 1 / 75 MHz。一个 PWM 周期 = 2 × TBPRD / TBCLK。

我们要的是 10 kHz,所以:

复制代码
TBPRD = TBCLK / (2 × f) = 75×10⁶ / (2 × 10⁴) = 3750

结论:在「75 MHz 时钟 + 增减计数」这套方案下,想输出 10 kHz ,就把周期寄存器 TBPRD 赋值为 3750。


四、动作限定(AQ):先让 A、B 输出相同的波形

通过动作限定控制寄存器 AQCTLA / AQCTLB,可以规定 EPWMxA / EPWMxB 在时间基准计数器(TBCTR)发生特定事件时做什么动作。

计数器能产生这些事件:CTR = 0、CTR = PRD、CTR = CMPA、CTR = CMPB,还可以叠加「递增 / 递减」方向。

本例只用 CMPA ,并让 EPWM1A 和 EPWM1B 做完全相同的动作:

  • 当 CTR = CMPA 且递增 时(CAU 事件)→ 引脚输出高电平;
  • 当 CTR = CMPA 且递减 时(CAD 事件)→ 引脚输出低电平。

此时 A 和 B 是完全同相的波形。占空比关系为:

复制代码
D = (TBPRD − CMPA) / TBPRD = 1 − CMPA / TBPRD

五、死区模块(DB):把同相波形变成互补 + 1 µs 死区

上一步得到的是两路一模一样的波形,而我们要的是互补且带死区 。这就交给**死区子模块(DB)**处理。

DB 模块的配置逻辑:

  1. 以 EPWM1A 的上升沿 和 EPWM1B 的下降沿作为死区延时的参考源;
  2. 经过 DB 后,EPWM1A 的上升沿被延迟、EPWM1B 的下降沿被延迟,二者错开,形成死区;
  3. 再把 EPWM1B 的极性取反。

这样一来,EPWM1A 保持原样,EPWM1B 变成「延迟 + 反相」的版本,A、B 就成互补关系,并且:

  • EPWM1A 导通(开关 S1 导通)时,EPWM1B 已完全关断;
  • EPWM1B 导通(开关 S2 导通)时,EPWM1A 已完全关断。

这正是驱动桥电路所需要的「死区」保护。如果反过来让两路同时为高,上下管就会直通 ,电路根本没法正常工作。

死区时间怎么算? DB 模块内部有一个计数器,每记一次数耗时同样是 1 / TBCLK = 1 / 75 MHz。要得到 1 µs 死区:

复制代码
DBRED = DBFED = 1 µs × 75 MHz = 75

所以把 DBRED(上升沿延迟)和 DBFED(下降沿延迟)都赋值为 75,死区就是 1 µs。


六、ePWM 中断:周期中断 INT3.1

本例会用到 ePWM 中断(EPWMxINT) 。在中断向量表里,它位于 PIE 第 3 组第 1 个 ,即 INT3.1。

中断事件选择为 周期匹配事件(CTR = PRD) :每当计数器回到 PRD,就产生一次中断。这样每个 PWM 周期都会进一次中断,我们就在中断里实时调整占空比。


七、main 函数与 ISR 的整体流程

视频带着读了一遍工程代码,核心流程如下:

main 函数:

  1. InitSysCtrl() 初始化系统控制:配置时钟(PLL)、初始化外设时钟;
    • 关键:必须把 EPWM1 的外设时钟使能,否则 TBCLK 不工作,PWM 根本不出波形。
  2. 初始化 GPIO:把 GPIO0 / GPIO1 通过 GPAMUX 配成 EPWM1A / EPWM1B。
  3. 初始化占空比变量 duty = 0.5(上电默认 50% 占空比)。
  4. 初始化 PIE 中断向量表、初始化 PIE 控制函数;
  5. 先 DINT 关 CPU 中断,清除 IER / IFR 标志位,再初始化中断向量表;
  6. 给中断源 EPWMxINT 在向量表里填入入口地址(也可直接写在 DSP2833x_DefaultIsr.c 的默认中断里);
  7. 由于 EPWMxINT 属于 CPU 中断 3、PIE 第 3 组第 1 个,对它使能;
  8. 重新 EINT 打开中断;
  9. 调用 PWM_Config() 配置 ePWM。
c 复制代码
#include "DSP2833x_Project.h"
#include <stdio.h>
#include <string.h>

// EPWM配置函数声明
void ConfigEPwm(void);
// EPWM1中断服务函数声明
interrupt void EPWM_INT(void);
Uint32 LoopCount;
float Duty;  // 控制占空比的参数

void main(void)
{
    // 初始化系统控制:PLL、看门狗、开启外设时钟
    InitSysCtrl();
    asm(" RPT #8 || NOP");

    EALLOW;  // 解锁受EALLOW保护的寄存器,允许写入
    InitEPwm1Gpio(); // 初始化EPWM1对应的IO引脚
    EDIS;    // 上锁,禁止修改受保护寄存器

    Duty=0.5; // 初始占空比 50%

    DINT;     // 关闭全局中断

    InitPieCtrl(); // 初始化PIE中断控制器

    // 关闭所有CPU中断,清除所有CPU中断标志位
    IER = 0x0000;
    IFR = 0x0000;
    InitPieVectTable(); // 初始化PIE中断向量表

    EALLOW;  // 解锁
    PieVectTable.EPWM1_INT = &EPWM_INT; // 将EPWM1中断绑定到自定义中断函数
    EDIS;    // 上锁

    IER |= M_INT3; // 开启CPU第3组中断
    PieCtrlRegs.PIEIER3.bit.INTx1 = 1; // 开启PIE3组第1路中断(EPWM1)

    EINT;    // 打开全局中断

    ConfigEPwm(); // 配置EPWM模块时基、比较、中断等参数

    while(1)
    {
        // 主循环空转,所有逻辑放在中断里面
    }
}

PWM_Config() 配置要点(全部对应前面的原理):

  • CLKDIV、HSPCLKDIV 配成 TBCLK = 75 MHz;
  • 计数模式设为增减计数;
  • TBPRD = 3750(得 10 kHz);
  • 只用 CMPA(CMPB 用不到,注释掉即可);
  • AQCTLA / AQCTLB:CTR=CMPA 递增→高、CTR=CMPA 递减→低;
  • 死区:A 上升沿延迟、B 下降沿延迟,DBRED = DBFED = 75,并把 B 反相;
  • 使能 ePWM 中断,事件选 CTR = PRD。

注意时钟同步位:配置前会先禁止 ePWM 时钟(让所有 ePWM 模块用同一时刻的 TBCLK 同步),配置完成后再打开,PWM 才正式开始工作。

c 复制代码
void ConfigEPwm()
{
    EALLOW;    // 解锁受保护寄存器,允许写入配置

    SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // 禁止EPWM时钟同步,单独配置时基模块

    EPwm1Regs.TBCTL.bit.CLKDIV=0;          // 时基时钟分频:不分频
    EPwm1Regs.TBCTL.bit.HSPCLKDIV=1;       // 高速时基分频:二分频,TBCLK=150M/2=75M
    EPwm1Regs.TBCTL.bit.CTRMODE=2;         // 计数模式:增减计数(对称PWM)

    EPwm1Regs.TBSTS.all=0;                 // 清零时基状态寄存器
    EPwm1Regs.TBPHS.half.TBPHS=0;          // 时基相位值设为0
    EPwm1Regs.TBCTR=0;                     // 时基计数器清零
    EPwm1Regs.TBPRD=3750;                  // 时基周期值,PWM频率10kHz

    EPwm1Regs.CMPCTL.bit.SHDWAMODE=1;      // CMPA比较寄存器:直接模式(立即生效)
    EPwm1Regs.CMPCTL.bit.SHDWBMODE=1;      // CMPB比较寄存器:直接模式(立即生效)
    EPwm1Regs.CMPA.half.CMPA =1875;        // CMPA初始值,占空比50%
    //EPwm1Regs.CMPB=1875;

    EPwm1Regs.AQCTLA.bit.CAU=2;            // 增计数到CMPA时,EPWMA输出高电平
    EPwm1Regs.AQCTLA.bit.CAD=1;            // 减计数到CMPA时,EPWMA输出低电平
    EPwm1Regs.AQCTLB.bit.CAU=2;            // 增计数到CMPA时,EPWMB输出高电平
    EPwm1Regs.AQCTLB.bit.CAD=1;            // 减计数到CMPA时,EPWMB输出低电平
    EPwm1Regs.AQSFR.all=0;                 // 清零动作限定软件强制寄存器
    EPwm1Regs.AQCSFRC.all=0;               // 清零连续软件强制寄存器

    EPwm1Regs.DBCTL.bit.IN_MODE=2;         // 死区输入模式:EPWMA上升沿延时,EPWMB下降沿延时
    EPwm1Regs.DBCTL.bit.POLSEL=2;          // 极性选择:EPWMB输出取反
    EPwm1Regs.DBCTL.bit.OUT_MODE=3;        // 死区输出模式:上升沿、下降沿死区都使能
    EPwm1Regs.DBRED=75;                    // 上升沿延时75个TBCLK,死区1us
    EPwm1Regs.DBFED=75;                    // 下降沿延时75个TBCLK,死区1us

    EPwm1Regs.ETSEL.bit.INTSEL=1;          // 中断触发事件:计数器等于TBPRD(周期点CTR=PRD)
    EPwm1Regs.ETSEL.bit.INTEN=1;           // 使能EPWM中断
    EPwm1Regs.ETPS.bit.INTPRD=1;           // 每发生1次事件,产生一次中断

    SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // 开启EPWM时钟同步,让时基时钟开始运行
    EDIS;    // 上锁,禁止修改受保护寄存器
}

ePWM 中断服务函数(ISR):

  • 完成的功能:实时改变 EPWM1A / EPWM1B 的占空比;
  • 退出外设中断必须做两件事 :
    1. 清除中断标志位;
    2. 响应同组(第 3 组)的中断(写 PIEACK)。
      这两步是正确退出外设中断、保证下次还能进中断的前提。
c 复制代码
interrupt void EPWM_INT(void)
{
    EPwm1Regs.CMPA.half.CMPA = (1-Duty)*EPwm1Regs.TBPRD;  // 更新比较寄存器,修改PWM占空比
    EPwm1Regs.ETCLR.bit.INT = 1;                         // 清除EPWM事件中断标志位
    PieCtrlRegs.PIEACK.all = PIEACK_GROUP3;              // PIE应答,释放第3组中断,允许同组其他中断触发
}

八、编译、下载与示波器验证

工程编译通过,生成 .out 文件,下载到板子运行后,用示波器核对结果:

  • 频率 :测得 PWM 频率 10 kHz,与设定一致;
  • 波形 :通道 1 是 EPWM1A,通道 2 是 EPWM1B,二者互补输出;
  • 死区 :上升沿延迟和下降沿延迟实测均为 1 µs,与分析一致;
  • 中断:在 ISR 里打了断点,程序能停在断点,说明 ePWM 中断确实被触发、能正常进入中断函数。

整段效果与理论设想完全吻合。


九、小结与下集预告

这一集把前面学的 ePWM 各子模块串成了一个能跑的完整工程:

  • GPIO 复用决定引脚功能;
  • TB (时基)决定时钟与频率(TBPRD = 3750 → 10 kHz);
  • AQ (动作限定)决定波形形状;
  • DB (死区)把同相变互补并加 1 µs 死区;
  • ET + PIE 把周期事件变成可响应的中断(INT3.1)。

下一集将进入 ADC 的采样模块,继续往「能采会控」的完整控制工程推进。

相关推荐
東隅已逝,桑榆非晚1 小时前
AVL数实现
c++·笔记·学习·算法
智购科技智能售货柜1 小时前
顾客已经关门,视觉售货柜还在处理旧画面:延迟积压怎么查?~YH
python·单片机·嵌入式硬件·物联网·eclipse
糖糖单片机设计1 小时前
基于 STM32 的智能电蚊香控制系统
stm32·单片机·嵌入式硬件
摇滚侠1 小时前
《大模型是怎样跑起来的(轻科技)》硬件篇 阅读笔记
人工智能·笔记·科技
m4Rk_1 小时前
【论文阅读】Agent 记忆机制(116):Hybrid Memory——用互补策展与离线巩固构建长期记忆
论文阅读·人工智能·学习·开源·github
一条泥憨鱼1 小时前
【Kafka】零基础学习教程
java·分布式·学习·kafka
CAIE研习社2 小时前
30岁以后学习AI应用,证书能起到什么作用?
人工智能·学习
职豚求职小程序2 小时前
百联集团校招在线测评~IQCAT行测+MAP性格
笔记
叼烟扛炮2 小时前
Linux 学习笔记(四十五):Web 安全基石——Cookie、Session 与 HTTPS 原理解析
linux·前端·网络·笔记·学习·tcp/ip·安全