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模块:
- 先设置周期(TBPRD)
- 再设置比较值(CMPA)
- 最后设置死区时间(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分频 |
| ... | ... | ... |
HSPCLKDIV 和 CLKDIV 两级分频,可以灵活地降低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 配置都是一样,只需修改对应的数字即可。里面涉及到的相关寄存器在前面已做介绍,大家可以用到哪个就查询哪个,不需要完完全全记住。