目录
前言
玖道最近在做一个开关电源项目,需要用到TI 的TMS320F28035 芯片,实现控制电路的设计。简单来说就是利用28035采集信号量,经过算法计算,得到占空比,然后利用ePWM模块输出PWM波来控制功率开关管的导通和关断,从而实现拓扑的功率变换。
为了实现控制,我们需要在一个开关周期(PWM周期)内实现ADC采样,占空比计算等。那么,如何实现在每个开关周期内进行ADC采样呢?
答案是利用ePWM模块在每个PWM周期内去触发ADC模块进行采样,那究竟如何配置实现?
这就是本文要讲的主题了~
实验目的
通过ePWM模块和ADC模块,实现ePWM在每个PWM周期内发出SOC(start of conversion)信号,触发ADC进行采样,以此深入理解两个模块的工作原理与配置细节。
实验要求
如下图所示,通过ePWM模块产生固定频率和占空比的PWM信号,并在每个PWM周期开始处进行采样,即采样频率等于PWM频率。
硬件电路
如下图所示,ADC通过RG
引脚采样可调电阻的电压值,并保存在结果寄存器ADCRESULT
中。跳线帽短接3-5脚,可变电阻的电压值通过RG
与ADIN0B
相连,即ADC可通过ADIN0B
对可变电阻值进行采样。这里的ADIN0B指的是ADC 16路复用输入通道中的GROUP B channel 0。具体如下图所示。
注意,ADCINB0
就是一个ADC输入采样引脚,并不是通过GPIO进行复用的,所以并不需要配置GPIO,直接使用就行。
实验步骤
整个系统的程序流程图,大致如上图所示。
首先,是DSP系统的一些初始化操作,包括锁相环,看门狗,外设时钟使能,关闭中断,初始化PIE中断向量表等。
其次,就是配置EPWM模块,产生固定频率和占空比的PWM,并设置触发EPWMxSOCA
(或EPWMxSOCB,这里选择EPWMxSOCA)信号。
然后,ADC模块配置采样输入通道,触发源(接收EPWMxSOCA),采样窗的大小,并选择某个SOCx(x:0-15
,这里选择SOC1)进行ADC转换和触发中断ADCINTx(x:1-9)
。
最后,就是进入循环,进行喂狗,显示等其他后台操作。发生ADC中断时,进入ADC中断服务函数,取出可变电阻采样值并保存到数组中。
大致流程就是这样,当然读懂的前提是充分了解28035的EPWM模块和ADC模块,即熟读TI官方的用户手册和数据手册。
另外,这里面还有很多细节,未曾提及,主要还是参考官方例程和芯片手册,然后结合需求进行修改。
代码解释
#include "DSP28x_Project.h" // Device Header file and Examples Include File
// Prototype statements for functions found within this file.
__interrupt void adc_isr(void);
// Global variables used in this example:
Uint16 ConversionCount;
Uint16 Voltage1[10];
int main()
{
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2803x_SysCtrl.c file.
InitSysCtrl();
// Step 2. Initialize GPIO:
// This example function is found in the DSP2803x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio(); // Skipped for this example
// 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 DSP2803x_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 DSP2803x_DefaultIsr.c.
// This function is found in DSP2803x_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 register
PieVectTable.ADCINT1 = &adc_isr;
EDIS; // This is needed to disable write to EALLOW protected registers
// Step 4. Initialize all the Device Peripherals:
InitAdc(); // For this example, initialize the ADC
AdcOffsetSelfCal();
// Step 5. User specific code, enable interrupts:
// Enable ADCINT1 in PIE
PieCtrlRegs.PIEIER1.bit.INTx1 = 1; // Enable INT 1.1 in the PIE for ADCINT1
IER |= M_INT1; // Enable CPU Interrupt 1
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global real time interrupt DBGM
ConversionCount = 0;
// Configure ADC
// Note: Channel ADCINB0 will be double sampled to workaround the ADC 1st sample issue for rev0 silicon errata
EALLOW;
AdcRegs.ADCCTL1.bit.INTPULSEPOS = 1; //ADCINT1 trips after AdcResults latch
AdcRegs.INTSEL1N2.bit.INT1E = 1; //Enabled ADCINT1
AdcRegs.INTSEL1N2.bit.INT1CONT = 0; //Disable ADCINT1 Continuous mode
AdcRegs.INTSEL1N2.bit.INT1SEL = 1; //setup EOC1 to trigger ADCINT1 to fire
AdcRegs.ADCSOC0CTL.bit.CHSEL = 8; //set SOC0 channel select to ADCINB0(dummy sample for rev0 errata workaround)
AdcRegs.ADCSOC1CTL.bit.CHSEL = 8; //set SOC1 channel select to ADCINB0
AdcRegs.ADCSOC0CTL.bit.TRIGSEL = 5; //set SOC0 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
AdcRegs.ADCSOC1CTL.bit.TRIGSEL = 5; //set SOC1 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
AdcRegs.ADCSOC0CTL.bit.ACQPS = 6; //set SOC0 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
AdcRegs.ADCSOC1CTL.bit.ACQPS = 6; //set SOC1 S/H Window to 7 ADC Clock Cycles, (6 ACQPS plus 1)
EDIS;
// Assumes ePWM1 clock is already enabled in InitSysCtrl();
EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group
EPwm1Regs.ETSEL.bit.SOCASEL = 1; // generate SOCx when TBCTR = ZERO
EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
EPwm1Regs.CMPA.half.CMPA = 0x0080; // Set compare A value
EPwm1Regs.TBPRD = 0xFFFF; // Set period for ePWM1
EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start
// Wait for ADC interrupt
for(;;)
{
}
}
__interrupt void adc_isr(void)
{
Voltage1[ConversionCount] = AdcResult.ADCRESULT1; //discard ADCRESULT0 as part of the workaround to the 1st sample errata for rev0
// If 20 conversions have been logged, start over
if(ConversionCount == 9)
{
ConversionCount = 0;
}
else
{
ConversionCount++;
}
AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; // Clear ADCINT1 flag reinitialize for next SOC
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE
return;
}
实验结果
编辑好代码后,烧录到芯片中,然后Debug,全速运行,通过窗口[Window->show view->expression]
添加采样值。可以看到,10个采样值在实时进行刷新,即实现了PWM触发ADC周期性采样。
总结
本文简单介绍了利用TMS320F28035的EPWM模块产生SOC信号,以此触发ADC模块进行采样的大致设计思路及流程,并结合官方例程给出相应代码和实验结果,其中并未详细讲述EPWM模块和ADC模块相关寄存器的具体配置过程,读者可参考TI官方手册:
- 2803x Piccolo Technical Reference Manual.pdf
- tms320f28035.pdf
体会
笔者认为学习DSP最好的方式就是:官方例程+数据手册+用户手册+开发板+CCS。
具体来讲呢,官方例程
可以给我们提供一个程序正确的整体架构,比如一些初始化步骤,先初始化哪部分,然后再初始化哪部分,最后再怎么样。
数据手册+用户手册
可以说是最重要的资料了,必须要反复研读英文原版(记住是英文版,中文版可能会有很多翻译错误,笔者踩过坑),而且最好是结合代码研读,并做好笔记,方便以后复习。
配合数据手册和用户手册将官方例程看懂之后,就可以进行自己的一些配置了,从底层新建工程,一步一步编写自己的代码,然后结合CCS+开发板进行调试。
调试代码的时间可能是编写代码所费时间的数倍不止,因为程序会有很多Bug,这个时候就要结合数据手册和用户手册,对代码进行一行一行的检查,弄懂每一行代码的作用,思考逻辑或寄存器配置是否错误。
当你从头到尾,成功完成一个小实验,实现理论到实验验证的闭环,相信你的能力和信心会提高不少。