DSP的ADC设置与使用详解 -- 以F28069为例

该解决的问题早晚都要解决的。

ADC of F28069 Overview

  • single 12-bit converter fed by two sample and hold circuits
  • The basic principle of operation is centered around the configurations of individual conversions, called SOC’s, or Start-Of-Conversions.
  • 特性
    • 12-bit ADC core with built-in dual sample-and-hold (S+H)
    • Simultaneous sampling or sequential sampling modes
    • Full range analog input: 0 V to 3.3 V fixed, or VREFHI/VREFLO ratiometric
    • Up to 16-channel, multiplexed inputs
    • 16 SOC’s, configurable for trigger, sample window, and channel
    • 16 result registers (individually addressable) to store conversion values
    • Multiple trigger sources
      • S/W - software immediate start
      • ePWM 1-8
      • GPIO XINT2
      • CPU Timers 0/1/2
      • ADCINT1/2
    • 9 flexible PIE interrupts, can configure interrupt request after any conversion
      在这里插入图片描述

SOC Principle of Operation

  • 3 configurations — the trigger source that starts the conversion, the channel to convert, and the acquisition (sample) window size.
  • Each SOC is independently configured and can have any combination of the trigger, channel, and sample window size available
  • The trigger source for SOCx is configured by a combination of the TRIGSEL field in the ADCSOCxCTL
    register and the appropriate bits in the ADCINTSOCSEL1 or ADCINTSOCSEL2 register. Software can
    also force an SOC event with the ADCSOCFRC1 register. The channel and sample window size for SOCx are configured with the CHSEL and ACQPS fields of the ADCSOCxCTL register.

干货在这里

大致的采样过程:

  1. 预先配置单个SOC的3个配置:the trigger source (触发源), the channel to convert (采样转换频道ADC 16个任选), and the acquisition (sample) window size(采样窗口长度,根据硬件电路计算)
  2. 假设,我们将EPWM4A 作为 ADCINB0 和 ADCINB1共同的触发源。则应该:
AdcRegs.ADCSOC0CTL.bit.CHSEL = 8; //SOC0 to sample ADCB0, see at the datasheet page 548
AdcRegs.ADCSOC1CTL.bit.TRIGSEL 	= 11;    // set SOC1 start trigger on EPWM1A, due to round-robin SOC0 converts first then SOC1
AdcRegs.ADCSOC1CTL.bit.CHSEL = 9; //SOC0 to sample ADCB0, see at the datasheet page 548
AdcRegs.ADCSOC1CTL.bit.TRIGSEL 	= 11;    // 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
AdcRegs.ADCSOC1CTL.bit.ACQPS 	= 6;	// set SOC1 S/H Window to 7 ADC Clock Cycles
  1. TRIGSEL的11值,对应的是EPWM4 的ADCSOCA。如何设置EPWM4如何发出ADCSOCA呢?
    EPwm1Regs.ETSEL.bit.SOCAEN = 0;    // Disable SOC on A group
    EPwm1Regs.ETSEL.bit.SOCASEL = 1;   // Select SOC on up-count
    EPwm1Regs.ETSEL.bit.SOCAEN = 0;    // enable SOC on A group
  1. 当EPWM4A发出其SOCA时,SOC0开始接收信号,并经过ACQPS+1个ADC Clock后开始进行模数转换。默认情况下,由于SOC0和SOC1公用同一个触发源,SOC0先开始。
  2. 当模数转换结束后,ADC模块应当告诉DSP(发出中断信号),使其开始进行相关的中断程序;由于SOC0和SOC1用了同一个触发源,并且采样执行顺序为SOC0 - SOC1,故中断信号应该让SOC1的结束信号EOC1发出。
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

然后,在主程序中规定ADCINT1所对应的中断服务程序应该是什么,此处为ISR1(自己定义的函数)

    EALLOW;
    PieVectTable.ADCINT1 = &ISR1; //function for ADCA interrupt 1
    EDIS;

在中断函数中,不要忘了给ADCINT1的标志位清零,以便接受开始下一次的SOC。

AdcRegs.ADCINTFLGCLR.bit.ADCINT1 = 1;		//Clear ADCINT1 flag reinitialize for next SOC
  1. 在ADC结束之后,转换完成的数据存储到哪里了呢?在F28069中,有这样一个寄存器AdcResult,用于保存ADC的结果。相关的结构体定义如下:
struct ADC_RESULT_REGS {
	Uint16		ADCRESULT0;		// Conversion Result Buffer 0
	Uint16		ADCRESULT1;		// Conversion Result Buffer 1
	Uint16		ADCRESULT2;		// Conversion Result Buffer 2
	Uint16		ADCRESULT3;		// Conversion Result Buffer 3
	Uint16		ADCRESULT4;		// Conversion Result Buffer 4
	Uint16		ADCRESULT5;		// Conversion Result Buffer 5
	Uint16		ADCRESULT6;		// Conversion Result Buffer 6
	Uint16		ADCRESULT7;		// Conversion Result Buffer 7
	Uint16		ADCRESULT8;		// Conversion Result Buffer 8
	Uint16		ADCRESULT9;		// Conversion Result Buffer 9
	Uint16		ADCRESULT10;	// Conversion Result Buffer 10
	Uint16		ADCRESULT11;	// Conversion Result Buffer 11
	Uint16		ADCRESULT12;	// Conversion Result Buffer 12
	Uint16		ADCRESULT13;	// Conversion Result Buffer 13
	Uint16		ADCRESULT14;	// Conversion Result Buffer 14
	Uint16		ADCRESULT15;	// Conversion Result Buffer 15
};

此处,ADCRESULTx的编号和SOC是对应的。也即SOC0的数据会存到AdcResult.ADCRESULT0

  1. 另外,有一个容易忽视的设置,也即,在此例中,我们需要用到ADC的中断源ADCINT1,很明显,DSP自身是不会默认把所有的可用中断源都开启的,因此我们需要将我们要用到的中断源告诉DSP。
    参见page175由中断表我们可以知道,ADCINT1在终端表中对应的位置为INT1.1,ADCINT2对应INT1.2。我们只用到了ADCINT1,因此,将对应的中断源开启。
PieCtrlRegs.PIEIER1.bit.INTx1 = 1;

目前,我们将group1中的INT1.1使能了,但是,我们怎么样将Group1使能呢?

    IER |= M_INT1;      // Enable group 1 interrupts

首先,|=操作符为 按位或并赋值运算符 。C |= 2 等同于 C = C | 2。
IER为Interrupt Enable Register,为group的使能位。M_INT1 = 0x0001,相应地,如果要使能其他group,则需:

#define M_INT1  0x0001
#define M_INT2  0x0002
#define M_INT3  0x0004
#define M_INT4  0x0008
#define M_INT5  0x0010
#define M_INT6  0x0020
#define M_INT7  0x0040
#define M_INT8  0x0080
#define M_INT9  0x0100
#define M_INT10 0x0200
#define M_INT11 0x0400
#define M_INT12 0x0800
#define M_INT13 0x1000
#define M_INT14 0x2000
#define M_DLOG  0x4000
#define M_RTOS  0x8000

这样,group1使能,group1中的int1.1也被使能,这时候DSP就可以产生我们需要的中断信号了。
另外,再加上两句

    EINT;               // Enable Global interrupt INTM
    ERTM;               // Enable Global realtime interrupt DBGM

这两句代码没看懂,但是代码的注释还是挺简单的。

干货结束,开始愉快灌水。

TI例程

// Configure ADC
//###########################################################################
// Description:
//! \addtogroup f2806x_example_list
//! <h1> ADC Start of Conversion (adc_soc)</h1>
//! 
//! This ADC example uses ePWM1 to generate a periodic ADC SOC - ADCINT1.
//! Two channels are converted, ADCINA4 and ADCINA2.
//! 
//! \b Watch \b Variables \n
//! - Voltage1[10]    - Last 10 ADCRESULT0 values
//! - Voltage2[10]    - Last 10 ADCRESULT1 values
//! - ConversionCount - Current result number 0-9
//! - LoopCount       - Idle loop counter
//
//
//###########################################################################
// $TI Release: F2806x C/C++ Header Files and Peripheral Examples V151 $
// $Release Date: February  2, 2016 $
// $Copyright: Copyright (C) 2011-2016 Texas Instruments Incorporated -
//             http://www.ti.com/ ALL RIGHTS RESERVED $
//###########################################################################

#include "DSP28x_Project.h"     // Device Headerfile and Examples Include File

// Prototype statements for functions found within this file.
__interrupt void adc_isr(void);
void Adc_Config(void);


// Global variables used in this example:
Uint16 LoopCount;
Uint16 ConversionCount;
Uint16 Voltage1[10];
Uint16 Voltage2[10];


main()
{

// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the F2806x_SysCtrl.c file.
   InitSysCtrl();


// Step 2. Initialize GPIO:
// This example function is found in the F2806x_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 F2806x_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 F2806x_DefaultIsr.c.
// This function is found in F2806x_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:
// This function is found in F2806x_InitPeripherals.c
// InitPeripherals(); // Not required for this example
   InitAdc();  // For this example, init 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
   IER |= M_INT1; 						// Enable CPU Interrupt 1
   EINT;          						// Enable Global interrupt INTM
   ERTM;          						// Enable Global realtime interrupt DBGM

   LoopCount = 0;
   ConversionCount = 0;

// Configure ADC
	EALLOW;
    AdcRegs.ADCCTL2.bit.ADCNONOVERLAP = 1;	// Enable non-overlap mode
	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 	= 4;    // set SOC0 channel select to ADCINA4
    AdcRegs.ADCSOC1CTL.bit.CHSEL 	= 2;    // set SOC1 channel select to ADCINA2
    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	= 4;		// Select SOC from CMPA on upcount
   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(;;)
   {
      LoopCount++;
   }

}


__interrupt void  adc_isr(void)
{

  Voltage1[ConversionCount] = AdcResult.ADCRESULT0;
  Voltage2[ConversionCount] = AdcResult.ADCRESULT1;

  // 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;
}



  • 14
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值