基于28035的ePWM触发ADC采样设计

目录

前言

玖道最近在做一个开关电源项目,需要用到TI 的TMS320F28035 芯片,实现控制电路的设计。简单来说就是利用28035采集信号量,经过算法计算,得到占空比,然后利用ePWM模块输出PWM波来控制功率开关管的导通和关断,从而实现拓扑的功率变换。

为了实现控制,我们需要在一个开关周期(PWM周期)内实现ADC采样,占空比计算等。那么,如何实现在每个开关周期内进行ADC采样呢?

答案是利用ePWM模块在每个PWM周期内去触发ADC模块进行采样,那究竟如何配置实现?

这就是本文要讲的主题了~

实验目的

通过ePWM模块和ADC模块,实现ePWM在每个PWM周期内发出SOC(start of conversion)信号,触发ADC进行采样,以此深入理解两个模块的工作原理与配置细节。

实验要求

如下图所示,通过ePWM模块产生固定频率和占空比的PWM信号,并在每个PWM周期开始处进行采样,即采样频率等于PWM频率。

基于28035的ePWM触发ADC采样设计

硬件电路

如下图所示,ADC通过RG引脚采样可调电阻的电压值,并保存在结果寄存器ADCRESULT中。跳线帽短接3-5脚,可变电阻的电压值通过RGADIN0B相连,即ADC可通过ADIN0B对可变电阻值进行采样。这里的ADIN0B指的是ADC 16路复用输入通道中的GROUP B channel 0。具体如下图所示。
基于28035的ePWM触发ADC采样设计
基于28035的ePWM触发ADC采样设计
基于28035的ePWM触发ADC采样设计
基于28035的ePWM触发ADC采样设计
注意,ADCINB0就是一个ADC输入采样引脚,并不是通过GPIO进行复用的,所以并不需要配置GPIO,直接使用就行。

实验步骤

基于28035的ePWM触发ADC采样设计
整个系统的程序流程图,大致如上图所示。

首先,是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周期性采样。
基于28035的ePWM触发ADC采样设计

总结

本文简单介绍了利用TMS320F28035的EPWM模块产生SOC信号,以此触发ADC模块进行采样的大致设计思路及流程,并结合官方例程给出相应代码和实验结果,其中并未详细讲述EPWM模块和ADC模块相关寄存器的具体配置过程,读者可参考TI官方手册:

  • 2803x Piccolo Technical Reference Manual.pdf
  • tms320f28035.pdf

体会

笔者认为学习DSP最好的方式就是:官方例程+数据手册+用户手册+开发板+CCS。

具体来讲呢,官方例程可以给我们提供一个程序正确的整体架构,比如一些初始化步骤,先初始化哪部分,然后再初始化哪部分,最后再怎么样。

数据手册+用户手册可以说是最重要的资料了,必须要反复研读英文原版(记住是英文版,中文版可能会有很多翻译错误,笔者踩过坑),而且最好是结合代码研读,并做好笔记,方便以后复习。

配合数据手册和用户手册将官方例程看懂之后,就可以进行自己的一些配置了,从底层新建工程,一步一步编写自己的代码,然后结合CCS+开发板进行调试。

调试代码的时间可能是编写代码所费时间的数倍不止,因为程序会有很多Bug,这个时候就要结合数据手册和用户手册,对代码进行一行一行的检查,弄懂每一行代码的作用,思考逻辑或寄存器配置是否错误。

当你从头到尾,成功完成一个小实验,实现理论到实验验证的闭环,相信你的能力和信心会提高不少。

上一篇:[Systemverilog学习笔记] class


下一篇:Eigen库学习笔记(三)