
TMS320F28377s配置20khz互补PWM+死区
参考这里咱们主时钟频率为200M,分频后为50M,Ttbclk = 1 / 50M = 2e-8;TBPRD = Tpwm / 2 * Ttbclk = 5e-5 / 2 * 2e-8 = 1250此时频率为20khz。到此时基单元就基本配置完了,剩下依据需求调整相关寄存器即可。
学习了一下28377如何配置pwm,写篇文章整理一下,希望可以帮到大家。
文章涉及ccs和c2000ware,在ti官网可以下载。
1 . 新建一个空工程,具体操作可以参考上一篇文章
https://mp.csdn.net/mp_blog/creation/editor/138989966
(记得改工程名为pwm,我改的是pwm_gpio)
2.在source文件内导入epwm.c文件(这里同样需要gpio.c文件)其余按照第一节标准导入。(可以建立一个模板工程,每次直接复制模板工程,省的次次导入),完成后在source文件内新建main,c文件和pwm_gpio.c文件用来存放主函数和pwm配置相关函数,并在include文件内建立pwm_gpio.h和Myproject.h文件,Myproject.h用来封装pwm.h和其他自己写的.h文件(在主函数里只用声明Myproject.h便于查看,同时也符合ti套娃的习惯)配置完成后如下:
3 . 文件配置完了就要开始写主函数和pwm相关配置
首先要清楚pwm基础的寄存器:
EPWM主模块包含很多子模块,有时基单元,动作,死去,中断等,常用的有TB(时基单元)、CC(比较计数)、AQ(动作)、ET(中断),DB(死区)。
其中,TB主要控制PWM频率,CC主要控制PWM占空比,AQ为产生反比较后比较器所产生的变化(例如电平变为高电平等),中断和死区很好理解。
TB寄存器中有:
TBPRD:周期寄存器 (设置的时钟周期存入此,可通过阴影寄存器缓冲后写入,也可通过活动寄存器立即写入)
TBCTR:时基计数变值寄存器 (时基当前所计数的值存入,用于和所设定周期值比较)
TBPHS:时基相位寄存器
TBSTS:时基状态寄存器
TBCTL:控制寄存器 (重要)
AQ寄存器中有:
CAU:上升沿TBCTL == CMP
CAD:下降沿TBCTL == CMP
CC寄存器中有:
CMPA:比较寄存器A,其值与TBCTR值比较,相同时,事件发送到动作模块。
CMPB:比较寄存器B,其值与TBCTR值比较,相同时,事件发送到动作模块。
CMPCTL:控制寄存器(重要)
AQ寄存器中有:
AQCTLA:动作限定输出A控制寄存器
AQCTLB:动作限定输出A控制寄存器
DB寄存器中有:
OUT_MOOD:死区输出模式
DBCTL: 输出模式和极性选择等
DBRED: 高有效互补输出
DBFED: 低有效互补输出
这一部分,不清楚的可以参考手册1902页往后。
清楚寄存器后要根据自己的需求计算相关数据,其中TBCLK由主时钟频率分频而来
这里的EPWMCLK为100Mhz,(SYSCLKOUT为200Mhz系统通过EPWMCLKDIV做了一次二分频)
HSPCLKDIV是高速预分频系数,CLKDIV是预分频系数。
//
// Setup TBCLK
//
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UPDOWN; // Count up
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; // Disable phase loading
EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1; // Clock ratio to SYSCLKOUT
EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;
代码中选择计数器模式为增减计数,计算周期时要乘以二。这里高速预分频为1,预分频为1,TBCLK自然就是100Mhz,
参考这里咱们主时钟频率为200M,分频后为100M,
Ttbclk = 1 / 100M = 1e-8;
TBPRD = Tpwm / 2 * Ttbclk = 5e-5 / 2 * 1e-8 = 2500
此时频率为20khz。到此时基单元就基本配置完了,剩下依据需求调整相关寄存器即可。
4 . 时基单元结束后,还需要配置占空比,即比较值CMAP,
//
// Setup compare
//
EPwm1Regs.CMPA.bit.CMPA = 1250;
这里依据需求进行配置只要不大于2500即可
更改寄存器时选择通过影子寄存器加载CMPA,在TBCTL为0时使能,代码如下:
EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW; // Load registers every ZERO
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;
5 . 在TBCTR == CMAP时触发动作,上升沿相等时记高电平下降沿相等时记低电平
//
// Set actions
//
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // Set PWM1A on Zero
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CAU = AQ_CLEAR; // Set PWM1A on Zero
EPwm1Regs.AQCTLB.bit.CAD = AQ_SET;
这样一个基础的波形就有了,我们只需要再为他添上死区即可。
6 . 对死区进行配置
//
// Active Low PWMs - Setup Deadband
//
EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;
EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_LO;
EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL;
EPwm1Regs.DBRED.bit.DBRED = EPWM1_MIN_DB;
EPwm1Regs.DBFED.bit.DBFED = EPWM1_MIN_DB;
EPwm1_DB_Direction = DB_UP;
__interrupt void epwm1_isr(void)
{
if(EPwm1_DB_Direction == DB_UP)
{
if(EPwm1Regs.DBFED.bit.DBFED < EPWM1_MAX_DB)
{
EPwm1Regs.DBFED.bit.DBFED++;
EPwm1Regs.DBRED.bit.DBRED++;
}
else
{
EPwm1_DB_Direction = DB_DOWN;
EPwm1Regs.DBFED.bit.DBFED--;
EPwm1Regs.DBRED.bit.DBRED--;
}
}
else
{
if(EPwm1Regs.DBFED.bit.DBFED == EPWM1_MIN_DB)
{
EPwm1_DB_Direction = DB_UP;
EPwm1Regs.DBFED.bit.DBFED++;
EPwm1Regs.DBRED.bit.DBRED++;
}
else
{
EPwm1Regs.DBFED.bit.DBFED--;
EPwm1Regs.DBRED.bit.DBRED--;
}
}
EPwm1TimerIntCount++;
//
// Interrupt where we will change the deadband
//
EPwm3Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO; // Select INT on Zero event
EPwm3Regs.ETSEL.bit.INTEN = 1; // Enable INT
EPwm3Regs.ETPS.bit.INTPRD = ET_1ST; // Generate INT on 3rd event
两段代码实现通过对死区标志位EPwm1_DB_Direction的判断进中断来实现死区在MIN_OB到MAX_OB之间跃动(死区时间计算为DBRED * TBCLK 和 DBFED * TBCLK),每一个周期改变一次,也可以取消中断直接给死区时间一个值。
配置代码到这里就结束了。
7 . 下来就是对主函数的配置
#include "MYPROJECT.h"
#include "F28x_Project.h"
//
// Globals
//
//
// Main
//
void main(void)
{
//
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the F2837xS_SysCtrl.c file.
//
InitSysCtrl();
//
// Step 2. Initialize GPIO:
// This example function is found in the F2837xS_Gpio.c file and
// illustrates how to set the GPIO to its default state.
//
// InitGpio();
//
// enable PWM1, PWM2 and PWM3
//
CpuSysRegs.PCLKCR2.bit.EPWM1=1;
CpuSysRegs.PCLKCR2.bit.EPWM2=1;
CpuSysRegs.PCLKCR2.bit.EPWM3=1;
//
// For this case just init GPIO pins for ePWM1, ePWM2, ePWM3
// These functions are in the F2837xS_EPwm.c file
//
InitEPwm1Gpio();
InitEPwm2Gpio();
InitEPwm3Gpio();
//
// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
//
DINT;
//
// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the F2837xS_PieCtrl.c file.
//
InitPieCtrl();
//
// Disable CPU interrupts and clear all CPU interrupt flags:
//
IER = 0x0000;
IFR = 0x0000;
//
// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in F2837xS_DefaultIsr.c.
// This function is found in F2837xS_PieVect.c.
//
InitPieVectTable();
//
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
//
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.EPWM1_INT = &epwm1_isr;
PieVectTable.EPWM2_INT = &epwm2_isr;
PieVectTable.EPWM3_INT = &epwm3_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
//
// Step 4. Initialize the Device Peripherals:
//
EALLOW;
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC =0;
EDIS;
InitEPwm1Example();
InitEPwm2Example();
InitEPwm3Example();
EALLOW;
CpuSysRegs.PCLKCR0.bit.TBCLKSYNC =1;
EDIS;
//
// Step 5. User specific code, enable interrupts:
// Initialize counters:
//
//
// Enable CPU INT3 which is connected to EPWM1-3 INT:
//
IER |= M_INT3;
//
// Enable EPWM INTn in the PIE: Group 3 interrupt 1-3
//
PieCtrlRegs.PIEIER3.bit.INTx1 = 1;
PieCtrlRegs.PIEIER3.bit.INTx2 = 1;
PieCtrlRegs.PIEIER3.bit.INTx3 = 1;
//
// Enable global Interrupts and higher priority real-time debug events:
//
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM
//
// Step 6. IDLE loop. Just sit and loop forever (optional):
//
for(;;)
{
asm (" NOP");
}
}
这样就可以实现带死区pwm输出。感谢观看。(文章为本人学习内容,至少存在一处错误,希望读者多多包涵,如发现问题,还请指出,谢谢)
注意:实际测试中发现输出频率 与计算值相差二倍,经检查主时钟频率有问题,咱们选择外部晶振10Mhz,经过40倍频和二分频后应该为200M,实际只有100M,发现这里的判断条件直接进了else。
解决方案:在属性重内定义 _LAUNCHXL_F28377S
便可以解决。
更多推荐
所有评论(0)