【DSP学习】增强型脉宽调制 EPWM 实验-基于普中DSP开发攻略

0 参考材料

基于普中DSP开发攻略.pdf

1 PWM 输出配置步骤

让 F28355 的 ePWM 管脚输出 PWM,具体步骤如下:(EPWM 相关库函数在 DSP2833x_EPwm.c 和 DSP2833x_EPwm.h 文件中)

(1) 使能 ePWM 外设时钟及失能时基模块时钟

要使用 ePWM 外设则需开启相应时钟,在对 ePWM 相关寄存器配置时得先关闭时基模块时钟,待配置好后在开启,可以保证同步。具体代码如下:

c 复制代码
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;	// Disable TBCLK within the ePWM
SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 1;	// ePWM6
EDIS;

先关掉时钟,配置好所有寄存器,再开时钟,让所有配置同时生效


逐句解释

c 复制代码
EALLOW;  // 解锁受保护寄存器(PCLKCR0/1是受保护的)
c 复制代码
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;  // 关闭ePWM模块的时基时钟
c 复制代码
SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 1;  // 使能ePWM6的外设时钟
  • EPWM6ENCLK = ePWM6 ENable CLock
  • = 1 :给ePWM6模块提供外设时钟(让它能工作)
  • 注意:这里只开了ePWM6,其他ePWM模块没开(节省功耗)
c 复制代码
EDIS;  // 重新锁定

为什么需要先关闭 TBCLKSYNC?

问题场景

假设你正在配置ePWM6模块:

  1. 先设置周期(TBPRD)
  2. 再设置比较值(CMPA)
  3. 最后设置死区时间(DBRED)

如果不关时钟 :寄存器是边改边生效 的。可能刚写完周期值,计数器就按新周期开始跑了,但比较值和死区还没配置好 → 输出不确定的、有害的波形


PCLKCR0 和 PCLKCR1 的区别

寄存器 作用 本例中使用
PCLKCR0.bit.TBCLKSYNC 控制所有ePWM模块的时基时钟(计数器时钟)同步开关 = 0(关闭)
PCLKCR1.bit.EPWM6ENCLK 控制ePWM6模块的外设时钟(寄存器访问时钟)使能 = 1(开启)

两者关系

  • 外设时钟(EPWM6ENCLK)= 让模块能被访问、配置寄存器
  • 时基时钟(TBCLKSYNC)= 让模块的计数器跑起来,真的产生PWM波

(2) 开启 ePWM 对应 GPIO 时钟及初始化配置

由于 PWM 输出通道是对应着 F28335 芯片的 IO 口,所以需要使能对应的端口时钟,并将对应 IO 口配置为 ePWM 输出功能。初始化 ePWM GPIO 的函数 TI 已经提供给我们了,直接调用即可。本章使用的是 ePWM6,调用的函数如下:

c 复制代码
InitEPwm6Gpio();

其内部具体实现代码如下:

c 复制代码
void InitEPwm6Gpio(void)
{
EALLOW;
/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.
GpioCtrlRegs.GPAPUD.bit.GPIO10 = 0; // Enable pull-up on GPIO10 (EPWM6A)
GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0; // Enable pull-up on GPIO11 (EPWM6B)
/* Configure ePWM-6 pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be ePWM6 functional pins.
// Comment out other unwanted lines
GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 1;	// Configure GPIO10 as EPWM6A
GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 1;	// Configure GPIO11 as EPWM6B
EDIS;
}

我们是使用 GPIO10、GPIO11 的 ePWM6A 和 ePWM6B 功能,即对这两个 IO 口初始化,使能上拉和 GPIO 外设复用功能.


这段代码是在配置GPIO10和GPIO11引脚,让它们从普通的输入输出口,变成专门的ePWM6A和ePWM6B输出引脚

逐句解释

1. 使能内部上拉电阻

c 复制代码
GpioCtrlRegs.GPAPUD.bit.GPIO10 = 0;  // Enable pull-up on GPIO10
GpioCtrlRegs.GPAPUD.bit.GPIO11 = 0;  // Enable pull-up on GPIO11
寄存器 含义
GPAPUD GPIO10 0 使能内部上拉电阻
GPAPUD GPIO10 1 禁用上拉电阻

GPAPUD = GPIO A Port Pull-Up Disable(A端口上拉禁用)

  • 名字容易混淆 :叫"Pull-Up Disable",但写0是使能上拉
  • 这是个反逻辑寄存器

上拉电阻的作用

  • 引脚悬空时,上拉电阻把电平拉高到3.3V(避免浮空)
  • PWM输出时,上拉不影响输出驱动能力(因为输出时驱动电路更强)

为什么要使能? 习惯性配置,防止在引脚状态切换的瞬间出现不确定电平。


2. 配置引脚为ePWM功能

c 复制代码
GpioCtrlRegs.GPAMUX1.bit.GPIO10 = 1;  // Configure GPIO10 as EPWM6A
GpioCtrlRegs.GPAMUX1.bit.GPIO11 = 1;  // Configure GPIO11 as EPWM6B
寄存器 含义
GPAMUX1 GPIO10 0 普通GPIO
GPAMUX1 GPIO10 1 ePWM6A
GPAMUX1 GPIO10 2 其他外设功能
GPAMUX1 GPIO10 3 其他外设功能

GPAMUX1 = GPIO A Port MUX 1(A端口复用选择寄存器1)

MUX = Multiplexer(多路复用器),相当于一个电子开关:

复制代码
GPAMUX1 = 0 → 选 GPIO
GPAMUX1 = 1 → 选 ePWM6A
GPAMUX1 = 2 → 选 其他

GPAMUX1 = 1 就是把内部ePWM6A模块的输出,连接到GPIO10引脚上。


(3) 初始化时基模块,即配置 TB 相关寄存器值

要产生 PWM 波,时基模块是必不可少的,通过配置它可以确定计数器的计数方式、周期频率、是否同步等。比如我们要设置 ePWM6 计数方式为向上计数,不使用相位同步功能,计数器计数频率为系统时钟频率,计数器初值为 0 等,具体配置代码如下:

c 复制代码
//SetupSync
EPwm6Regs.TBCTL.bit.SYNCOSEL=TB_SYNC_DISABLE; //Passthrough
//Alloweachtimertobesync'ed
EPwm6Regs.TBCTL.bit.PHSEN=TB_DISABLE;
EPwm6Regs.TBPHS.half.TBPHS=0;
EPwm6Regs.TBCTR=0x0000; //Clearcounter
EPwm6Regs.TBPRD=tbprd;
EPwm6Regs.TBCTL.bit.CTRMODE=TB_COUNT_UP; //Countup
EPwm6Regs.TBCTL.bit.HSPCLKDIV=TB_DIV1;
EPwm6Regs.TBCTL.bit.CLKDIV=TB_DIV1;

这段代码是配置ePWM6模块的时基模块 ,决定了PWM波形的频率、计数模式、同步行为等基础参数。

简单说:设置PWM的"心跳"节奏


逐句解释

1. 配置同步输出

c 复制代码
EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
寄存器位 含义
SYNCOSEL TB_SYNC_DISABLE 禁用同步输出(不向后续ePWM模块发送同步信号)
SYNCOSEL TB_SYNC_IN(0) 同步输入信号直接作为同步输出
SYNCOSEL TB_SYNC_DISABLE(2) 不同步

同步的作用 :让多个ePWM模块的计数器同时从0开始,保证它们的相位一致。比如三相电机控制,U/V/W三相的PWM需要同步。

这里设为禁用,意思是ePWM6不需要去同步后面的模块(因为ePWM6可能是最后一个)。


2. 禁用相位加载

c 复制代码
EPwm6Regs.TBCTL.bit.PHSEN = TB_DISABLE;
寄存器位 含义
PHSEN TB_ENABLE(1) 使能相位加载(收到同步信号时,把TBPHS加载到TBCTR)
PHSEN TB_DISABLE(0) 禁用相位加载

相位加载 :当收到同步输入信号时,是否把TBPHS(相位寄存器的值)加载到计数器。

PHSEN = 0 :收到同步信号时,计数器不做任何事(不从0开始,保持当前值)。


3. 设置相位值

c 复制代码
EPwm6Regs.TBPHS.half.TBPHS = 0;
  • TBPHS = Time Base Phase(时基相位寄存器)
  • = 0:收到同步信号时,要加载的相位值是0

虽然上面PHSEN = TB_DISABLE禁用了相位加载,但还是设置了TBPHS=0,是一种规范性写法,方便以后修改。


4. 清零计数器

c 复制代码
EPwm6Regs.TBCTR = 0x0000;  // Clear counter
  • TBCTR = Time Base Counter(时基计数器)
  • 手动把计数器清零,确保从一个已知的初始状态开始

这是配置阶段的准备工作,防止之前残留的计数值干扰。


5. 设置周期值

c 复制代码
EPwm6Regs.TBPRD = tbprd;
  • TBPRD = Time Base Period(时基周期寄存器)
  • tbprd是一个变量/宏,决定了PWM的周期

周期与频率的关系

复制代码
PWM频率 = 系统时钟 / (TBPRD + 1) / (HSPCLKDIV × CLKDIV)

例如:系统时钟=100MHz,tbprd=2000,分频=1
频率 = 100MHz / (2000+1) ≈ 50kHz

6. 设置计数模式

c 复制代码
EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;
模式 计数器行为
TB_COUNT_UP(0) 向上计数 0 → TBPRD → 0 → TBPRD ...
TB_COUNT_DOWN(1) 向下计数 TBPRD → 0 → TBPRD → 0 ...
TB_COUNT_UPDOWN(2) 向上向下计数 0 → TBPRD → 0 → TBPRD ...(对称)

向上计数是最常用的模式,产生的PWM波形左边对齐。


7. 设置时钟分频

c 复制代码
EPwm6Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;  // 高速外设时钟分频
EPwm6Regs.TBCTL.bit.CLKDIV = TB_DIV1;     // 时基时钟分频
分频系数 效果
TB_DIV1(0) 1 不分频,频率最高
TB_DIV2(1) 2 2分频
TB_DIV4(2) 4 4分频
... ... ...

HSPCLKDIVCLKDIV 两级分频,可以灵活地降低PWM频率。

TB_DIV1 = 不分频:计数器频率 = 系统时钟频率(通常是100MHz)


综合效果

这段配置产生的效果:

复制代码
计数模式:向上计数(0 → 2000 → 0 → 2000)
频率:假设系统时钟100MHz,不分频,周期2000 → 频率≈50kHz
相位加载:禁用,不同步其他模块
同步输出:禁用

(4) 初始化比较模块,即配置 CC 相关寄存器值

要产生可调的 PWM 波,除了需要时基模块外,CC 模块也是必不可少的,通过配置它可以确定比较寄存器值的加载模式,比较器值、占空比等。比如我们要设置 ePWM6 加载方式为计数器为 0 加载、比较器值为 0 等,具体配置代码如下:

c 复制代码
//SetupshadowregisterloadonZERO
EPwm6Regs.CMPCTL.bit.SHDWAMODE=CC_SHADOW;
EPwm6Regs.CMPCTL.bit.SHDWBMODE=CC_SHADOW;
EPwm6Regs.CMPCTL.bit.LOADAMODE=CC_CTR_ZERO;
EPwm6Regs.CMPCTL.bit.LOADBMODE=CC_CTR_ZERO;
//SetComparevalues
EPwm6Regs.CMPA.half.CMPA=0; //SetcompareAvalue
EPwm6Regs.CMPB=0; //SetCompareBvalue

(5) 初始化动作限定模块,即配置 AQ 相关寄存器值

要产生可调 PWM 波,除了需要 TB、CC 模块外,AQ 模块也是必不可少的,通过配置它可以确定 PWM 输出波形方式等。比如我们要设置 ePWM6 输出,当 ePWMA计数器计数到 0 时输出低电平,当 ePWMA 计数器向上计数到 CMPA 时输出高电平,当 ePWMB 计数器计数到 0 时输出低电平,当 ePWMB 计数器向上计数到 CMPB 时输出高电平,具体配置代码如下:

c 复制代码
//Setactions
EPwm6Regs.AQCTLA.bit.ZRO=AQ_CLEAR; //SetPWM1AonZero
EPwm6Regs.AQCTLA.bit.CAU=AQ_SET; //ClearPWM1AoneventA,upcount
EPwm6Regs.AQCTLB.bit.ZRO=AQ_CLEAR; //SetPWM1BonZero
EPwm6Regs.AQCTLB.bit.CBU=AQ_SET; //ClearPWM1BoneventB,upcount

(6)初始化事件触发模块,即配置ET相关寄存器值

当需要事件触发输出控制,就需要对ET相关寄存器配置。比如计数器计数到0时,同时使能事件触发中断,每发生一次触发事件就输出PWM。相关配置代码如下:

c 复制代码
EPwm6Regs.ETSEL.bit.INTSEL=ET_CTR_ZERO; //SelectINTonZeroevent
EPwm6Regs.ETSEL.bit.INTEN=1; //EnableINT
EPwm6Regs.ETPS.bit.INTPRD=ET_1ST; //GenerateINTon1stevent

(7) 初始化死区模块、斩波模块,即配置 DB、PC 相关寄存器值

如果不是特殊应用的话,一般不对死区模块和斩波模块配置,它们的应用方法在前面各小节也介绍过,所以这里我们不作介绍。
(8) 使能时基计数器时钟

将各模块寄存器配置好后,最后开启时基计数器时钟,完成这步操作,对应的 IO 口即可输出 PWM 波。开启时基计数器时钟代码如下:

c 复制代码
EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;// Start all the timers synced
EDIS;

这里列举的是 ePWM6 的配置,至于其他 ePWM 配置都是一样,只需修改对应的数字即可。里面涉及到的相关寄存器在前面已做介绍,大家可以用到哪个就查询哪个,不需要完完全全记住。

相关推荐
咸甜适中16 小时前
rust语言学习笔记Trait(十五)Drop(释放资源)
笔记·学习·rust
IT笔记17 小时前
【Rust】 Rust宏学习笔记
笔记·学习·rust
网络与设备以及操作系统学习使用者17 小时前
路由器如何实现跨VLAN通信
运维·网络·学习·华为·智能路由器
182******208317 小时前
2026年学C语言还有出路吗?学习需要报班吗?
c语言·开发语言·学习
东方佑18 小时前
可学习破坏策略:实现大语言模型二倍推理加速的统一自洽框架
人工智能·学习·语言模型
FreakStudio18 小时前
大话电容传感器和电容SOC芯片,看这一篇就够了
python·单片机·嵌入式·面向对象·并行计算·电子diy·电子计算机
森林古猿118 小时前
论CDQ分治
c++·学习·算法·排序算法
AOwhisky18 小时前
MySQL 学习笔记(第三期):SQL 语言之数据操作与单表查询
linux·运维·笔记·sql·学习·mysql·云计算
信看18 小时前
常见通信接口
单片机·嵌入式硬件
Niyy_19 小时前
Rust 学习笔记 01
笔记·学习·rust