TI官方例程ADC转换到DMA之全程记录--- Example_2823xAdcToDMA.c
本帖最后由 我爱你的吻123 于 2015-10-30 14:19 编辑//###########################################################################
//
// FILE: Example_2823xAdcToDMA.c
//
// TITLE:DSP2823x ADC To DMA
//
// ASSUMPTIONS:
前情提要:所有红色的字体都与这个文件无关,是本人自己添加的解说。
//
// This program requires the DSP2823x header files.
//这个程序需要DSP2823x的头文件配置完全。
// Make sure the CPU clock speed is properly defined in
// DSP2833x_examples.h before compiling this example.
//编译这个例子之前,确定DSP2833x的时钟配置好了。
// Connect the signals to be converted to channel A0, A1, A2, and A3.
//把信号连接到A0, A1, A2, and A3通道。
// As supplied, this project is configured for "boot to SARAM"
// operation.The 2823x Boot Mode table is shown below.
这个工程要配置到从SARAM起动,2823X的起动模式已经在下表显示了。
// For information on configuring the boot mode of an eZdsp,
// please refer to the documentation included with the eZdsp,
//
// $Boot_Table:
//
// GPIO87 GPIO86 GPIO85 GPIO84
// XA15 XA14 XA13 XA12
// PU PU PU PU
// ==========================================
// 1 1 1 1 Jump to Flash
// 1 1 1 0 SCI-A boot
// 1 1 0 1 SPI-A boot
// 1 1 0 0 I2C-A boot
// 1 0 1 1 eCAN-A boot
// 1 0 1 0 McBSP-A boot
// 1 0 0 1 Jump to XINTF x16
// 1 0 0 0 Jump to XINTF x32
// 0 1 1 1 Jump to OTP
// 0 1 1 0 Parallel GPIO I/O boot
// 0 1 0 1 Parallel XINTF boot
// 0 1 0 0 Jump to SARAM <- "boot to SARAM"---------------在这里配置好
// 0 0 1 1 Branch to check boot mode
// 0 0 1 0 Boot to flash, bypass ADC cal
// 0 0 0 1 Boot to SARAM, bypass ADC cal
// 0 0 0 0 Boot to SCI-A, bypass ADC cal
// Boot_Table_End$
//
//
// DESCRIPTION:
//
// ADC is setup to convert 4 channels for each SOC received, withtotal of 10 SOCs.
// Each SOC initiates 4 conversions.
启动转换 Start of Conversion (SOC),ADC有四个通道要通过SOC转换。
// DMA is set up to capture the data on each SEQ1_INT.DMA will re-sort
// the data by channel sequentially, i.e. all channel0 data will be together
// all channel1 data will be together.
//DMA被设置为捕捉每个排序器的中断。DMA将按通道排序来重排所有数据。如,所有通道0的数据在一起,通道1的数据在一起。
// Code should stop in local_DINTCH1_ISR when complete
//
// Watch Variables:
// DMABuf1
//
//###########################################################################
//
// Original source by: M.P.
//
// $TI Release: 2833x/2823x Header Files and Peripheral Examples V133 $
// $Release Date: June 8, 2012 $
//###########################################################################
#include "DSP28x_Project.h" // Device Headerfile and Examples Include File
// ADC start parameters ADC开始参数
#if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT 我们缺省用的是系统150MHZ时钟。经过分频为25MHZ.
#define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz
#endif
#if (CPU_FRQ_100MHZ)
#define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
#endif
#define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
#define ADC_SHCLK0xf // S/H width in ADC module periods = 16 ADC clocks
#define AVG 1000// Average sample limit 平均样本限制
#define ZOFFSET 0x00// Average Zero offset 平均零点偏移
#define BUF_SIZE 40 // Sample buffer size 样本缓冲区大小
// Global variable for this example 全局变量
Uint16 j=0;
#pragma DATA_SECTION(DMABuf1,"DMARAML4");
volatile Uint16 DMABuf1;
volatile Uint16 *DMADest;
volatile Uint16 *DMASource;
#pragma DATA_SECTION(bufferB, ”my_sect”)
char bufferB;
在.cmd文件中建立对应的section就可以使用了。#pragma DATA_SECTION(函数名或全局变量名,"用户自定义在数据空间的段名");#pragma CODE_SECTION(函数名或全局变量名,"用户自定义在程序空间的段名");注意 不能在函数体内声明。必须在定义和使用前声明#pragma可以阻止对未调用的函数的优化
interrupt void local_DINTCH1_ISR(void); 中断
void main(void)
{
Uint16 i;
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
InitSysCtrl();时钟设置,在DSP2833x_SysCtrl.c这个文件中有相应的寄存器的值。
// Specific clock setting for this example:
EALLOW;
SysCtrlRegs.HISPCP.all = ADC_MODCLK; // HSPCLK = SYSCLKOUT/ADC_MODCLK HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
把ADC打开,用150M/6=25M
EDIS;
// Step 2. Initialize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.这个函数InitGpio()是要我们配置好相应的GPIO,就是说你的AD是从哪一个引脚输入的。GPIO有几个寄存器,方向寄存器,通用I/O寄存器等等。
// InitGpio();// Skipped for this example
// Step 3. Clear all interrupts and initialize PIE vector table:
清除所有的中断,并且初始化PIE,PIE就像是一整套的组合开关,把我们的所有中断都控制了起来,你想要什么样的中断,可以按你的要求来配置。要想知道详情的,可以看我的日志中关于PIECTRL.H和PIEVECT.H这两篇。我贴了一些图在上面。
// Disable CPU interrupts
DINT;
//---------------------------------------------------------------------------
//关闭CPU中断。这个DINT它是可以理解为一个汇编代码,它在这个DSP2833x_Device.h文件中定义。
Common CPU Definitions:
extern cregister volatile unsigned int IFR;
extern cregister volatile unsigned int IER;
#defineEINT asm(" clrc INTM")
#defineDINT asm(" setc INTM")
#defineERTM asm(" clrc DBGM")
#defineDRTM asm(" setc DBGM")
#defineEALLOW asm(" EALLOW")
#defineEDIS asm(" EDIS")
#defineESTOP0 asm(" ESTOP0")
// 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 DSP2833x_PieCtrl.c file.
InitPieCtrl();------------这外就是PIE的初始化的程序,接下来我会把它打开说说的。也是一种记录的过程。
// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;--------------------------------extern cregister volatile unsigned int IFR;extern cregister volatile unsigned int IER;这两个地方我有一点搞不明白,它的定义方式有点奇怪,涉及到编译器的自动处理。 cregister
使用cregister关键字,当我们定义的该类型的对象与C28x的标准的控制寄存器匹配时,编译器会自动产生相关的代码去控制对应的寄存器,使得我们可以在高级编程语言C/C++中对寄存器进行控制;如果不匹配则产生编译器错误。目前可匹配此类型的寄存器包括:IER:中断使能寄存器IFR:中断标志寄存器我们在这里就暂且这么理解一下吧!
// 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 DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
InitPieVectTable();
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // Allow access to EALLOW protected registers
PieVectTable.DINTCH1= &local_DINTCH1_ISR;
EDIS; // Disable access to EALLOW protected registers
IER = M_INT7 ; //Enable INT7 (7.1 DMA Ch1)
EnableInterrupts();
// Step 4. Initialize all the Device Peripherals:
// This function is found in DSP2833x_InitPeripherals.c
// InitPeripherals(); // Not required for this example 串行外设初始化
InitAdc();// For this example, init the ADC ADC寄存器初始化
// Specific ADC setup for this example:
AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;
AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;
AdcRegs.ADCTRL1.bit.SEQ_CASC = 0; // 0 Non-Cascaded Mode
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 0x1;
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 0x1;
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0;
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1;
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x2;
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x3;
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x0;
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x1;
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x2;
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x3;
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3; // Set up ADC to perform 4 conversions for every SOC
//Step 5. User specific code, enable interrupts:
// Initialize DMA
DMAInitialize();
// Clear Table
for (i=0; i<BUF_SIZE; i++)
{
DMABuf1 = 0;
}
// Configure DMA Channel
DMADest = &DMABuf1; //Point DMA destination to the beginning of the array
DMASource = &AdcMirror.ADCRESULT0; //Point DMA source to ADC result register base
DMACH1AddrConfig(DMADest,DMASource);
DMACH1BurstConfig(3,1,10);
DMACH1TransferConfig(9,1,0);
DMACH1WrapConfig(1,0,0,1);
DMACH1ModeConfig(DMA_SEQ1INT,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE,SYNC_DISABLE,SYNC_SRC,
OVRFLOW_DISABLE,SIXTEEN_BIT,CHINT_END,CHINT_ENABLE);
StartDMACH1();
// Start SEQ1
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 0x1;
for(i=0;i<10;i++){
for(j=0;j<1000;j++){}
AdcRegs.ADCTRL2.bit.SOC_SEQ1 = 1; //Normally ADC will be tied to ePWM, or timed routine
} //For this example will re-start manually
}
// INT7.1
interrupt void local_DINTCH1_ISR(void) // DMA Channel 1
{
// To receive more interrupts from this PIE group, acknowledge this interrupt
PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;
// Next two lines for debug only to halt the processor here
// Remove after inserting ISR Code
asm (" ESTOP0");
for(;;);
}
我的本意是想把八路音频信号通过DSP28335的ADC采样之后,经过DMA存储到相应的空间,利用这些数据来作一点处理。这是我先打的一个前期草稿,我会逐步完善的。有没有人做过这样的工作,如果有的话,希望指点一下。 牛人 持续关注 感谢分享 zhangmangui 发表于 2015-10-29 21:30
牛人 持续关注 感谢分享
多谢版主,还有打赏。这不得不激励我把这个帖子好好写下去啊。谢谢了! 我的这个文件是找的TI官方的ADC转DMA工程文件,我想就从这里记录我的行程吧。每个人都是可以下得到的。这是我的工程目录。
E:\ti\controlSUITE\device_support\f2833x\v132\DSP2833x_examples_ccsv4\adc_dma
有兴趣的人可以一起探讨一下啊。
通过ADC转换到DMA寄存器后,接下来会做相应的FIR,和广义自相关分析 。COME ON... 我们来看看下面这个函数都有些什么东东,在工程中起了什么作用。它来自于这个文件:FILE: DSP2833x_SysCtrl.c
1) InitSysCtrl();
void InitSysCtrl(void)
{
// Disable the watchdog 关闭看门狗,看门狗大家应该都清楚我就不用多说了,接下来会打开看门狗来看看的。
DisableDog();
// Initialize the PLL control: PLLCR and DIVSEL 初始化锁相环时钟控制,PLLCR,DIVSEL。喔这两个又是什么东西呢。 我们也要搞清楚。它告诉我们,这两个东东在DSP2833x_Examples.h这个头文件里面定义了。我们也要查清楚。
// DSP28_PLLCR and DSP28_DIVSEL are defined in DSP2833x_Examples.h
InitPll(DSP28_PLLCR,DSP28_DIVSEL);
// Initialize the peripheral clocks 初始化串行外设的时钟,也就是说一切都是时钟,没有时钟我们不能动,我们都不能吃饭了。这就是体现了一种叫做一切行动听指挥的味道。我有一种想法就是,数字信号处理我认为一切都是波,各种波的变换。而数字信号处理器则一切都是时钟,没有时钟不能干活。
InitPeripheralClocks();
}
1.1)接着上面的函数来说。
DisableDog();
上面的这个函数来自于DSP2833x_SysCtrl.c这个文件。
// Example: DisableDog://---------------------------------------------------------------------------
// This function disables the watchdog timer.喔 ,这个函数的意思就是关闭小狗。不让小狗叫了。
void DisableDog(void)
{
EALLOW;
SysCtrlRegs.WDCR= 0x0068;
EDIS;
}; WDCR 看门狗控制器,这里有一个很应该注意的问是,这个相应的校验位(位5--3)必须是“101”,要不然的话会拒绝访问并会立即触发复位。那么0x0068展开是 0000.0000.0110.1000(位5--3)正好是101.而第六位为1时,是禁止看门狗。到这里大功告成,看门狗禁止了。小狗睡觉去了。
1.2)InitPll(DSP28_PLLCR,DSP28_DIVSEL);
明天从这个开始,今天 有点累了。先睡觉去了。。。。。。。 继续盖楼。
1.2)InitPll(DSP28_PLLCR,DSP28_DIVSEL);
这个函数展开就是这么一大段函数体,我们来一一了解下。
void InitPll(Uint16 val, Uint16 divsel)
{
// Make sure the PLL is not running in limp mode 确保锁相环没有运行在软模式
这个是指锁相环的缓慢变化模式,对应的滤波器及电流变小。DSP28_SysCtrl.h这个头文件中定义的寄存器主要与系统控制相关,包括高速外设时钟预分频器寄存器HISPCP,低速外设时钟预分频寄存器LOSPCP,外设时钟控制寄存器PCLKCR,低功耗模式控制寄存器LPMCR0,低功耗模式控制寄存器LPMCR1,PLL控制寄存器PLLCR,系统控制与状态寄存器SCSR,看门狗计数器寄存器WDCNTR,看门狗复位密钥寄存器WDKEY,看门狗控制寄存器WDCR,128为加密密匙寄存器,加密状态与控制寄存器CSMSCR,128位加密密码寄存器,Flash选项寄存器FOPT,Flash功率模式寄存器FPWR,状态寄存器FSTATUS,Flash休眠到待机等待寄存器FSTDBYWAIT,Flash待机到活动寄存器FACTIVEWAIT,Flash读取存储状态寄存器FBANKWAIT,OTP读取存储状态寄存器FOTPWAIT。上面提到所有寄存器被分为了三组:系统控制寄存器组SysCtrlRegs,代码保护寄存器组CsmRegs,代码保护密码寄存器组CsmPwl,Flash寄存器组FlashRegs。
锁相环的设置由函数InitPll(DSP28_PLLCR,DSP28_DIVSEL)来完成,其中
DSP28_PLLCR、DSP28_DIVSEL分别在头文件中用宏定义赋值为10、2,这样系统的时钟频率就为150M(外部晶振30M)。InitPll(Uint16 val, Uint16 divsel)的函数体如下:
if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 0)
{ Uint16 MCLKSTS:1; // 3 Missing clock status bit 丢失时钟状态位
这个位,是PLL时钟寄存器中关于是否存在的标志。如果它不等于0则执行下面这句话。
// Missing external clock has been detected
// Replace this line with a call to an appropriate
// SystemShutdown(); function.
asm(" ESTOP0");
ESTOP0这个是2812的一个汇编指令,是用于仿真的,它有两个方面的知识:1、当用仿真器连接时如果ESTOP0置位(ESTOP0=1),那么整个DSP停止运行。1 S' R62、当不用仿真程序时,在程序中写这条指令相当于NOP(空指令),只是占了CPU的一个周期而已
}
// DIVSEL MUST be 0 before PLLCR can be changed from
// 0x0000. It is set to 0 by an external reset XRSn
// This puts us in 1/4
if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
EDIS;
}
// Change the PLLCR
if (SysCtrlRegs.PLLCR.bit.DIV != val)
{
EALLOW;
// Before setting PLLCR turn off missing clock detect logic
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;
SysCtrlRegs.PLLCR.bit.DIV = val;
EDIS;
// Optional: Wait for PLL to lock.
// During this time the CPU will switch to OSCCLK/2 until
// the PLL is stable.Once the PLL is stable the CPU will
// switch to the new PLL value.
//
// This time-to-lock is monitored by a PLL lock counter.
//
// Code is not required to sit and wait for the PLL to lock.
// However, if the code does anything that is timing critical,
// and requires the correct clock be locked, then it is best to
// wait until this switching has completed.
// Wait for the PLL lock bit to be set.
// The watchdog should be disabled before this loop, or fed within
// the loop via ServiceDog().
// Uncomment to disable the watchdog
DisableDog();
while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1)
{
// Uncomment to service the watchdog
// ServiceDog();
}
EALLOW;
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;
EDIS;
以上程序中含有“.bit”的为寄存器的位操作,“.bit”之前的为具体的寄存器。在DSP的程序体系中,将寄存器定义为结构体,有些结构体中还包含联合体。上述代码中,asm(" ESTOP0")表示停止仿真。EALLOW一般和EDIS配套使用,EALLOW就是将该标志位置位,允许对受保护的寄存器操作,在对受保护的寄存器操作之后,用EDIS恢复寄存器的被保护状态。
}
// If switching to 1/2
if((divsel == 1)||(divsel == 2))
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel;
EDIS;
}
// NOTE: ONLY USE THIS SETTING IF PLL IS BYPASSED (I.E. PLLCR = 0) OR OFF
// If switching to 1/1
// * First go to 1/2 and let the power settle
// The time required will depend on the system, this is only an example
// * Then switch to 1/1
if(divsel == 3)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
DELAY_US(50L);
SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
EDIS;
}
}
这个地方啰嗦的写了好多,总而言之就是配置PLL锁相环,给ADC配置为25MHZ.如果有不清楚的地方,我的日志里面也整理了一些资料。可以去看看。
在TI给的这个Example_2823xAdcToDMA.c工程文件里面,有一个奇怪的问题,这段话是这样写的。
// Step 2. Initialize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio();// Skipped for this example
也就是说初始化IO是屏蔽了的。我的问题是,那么我的模拟信号是从哪几个IO口输入进去的呢?
这个暂且放下。我们先讲一下这个InitPieCtrl();函数!
我们来分解一下关于PIE的初始化函数。
InitPieCtrl();
// This function initializes the PIE control registers to a known state.
//这个函数是把我们所有的PIE全部关闭的,我们这个片子上的所有的中断都是基于这个开关组合的。现在的情况是首先全部关闭掉,再依照自己的需求打开相应的中断。
void InitPieCtrl(void)
{
// Disable Interrupts at the CPU level:
DINT;
// Disable the PIE
PieCtrlRegs.PIECTRL.bit.ENPIE = 0;
// Clear all PIEIER registers: 清除所有中断使能位
PieCtrlRegs.PIEIER1.all = 0;
PieCtrlRegs.PIEIER2.all = 0;
PieCtrlRegs.PIEIER3.all = 0;
PieCtrlRegs.PIEIER4.all = 0;
PieCtrlRegs.PIEIER5.all = 0;
PieCtrlRegs.PIEIER6.all = 0;
PieCtrlRegs.PIEIER7.all = 0;
PieCtrlRegs.PIEIER8.all = 0;
PieCtrlRegs.PIEIER9.all = 0;
PieCtrlRegs.PIEIER10.all = 0;
PieCtrlRegs.PIEIER11.all = 0;
PieCtrlRegs.PIEIER12.all = 0;
// Clear all PIEIFR registers: 清除所有中断标志位
PieCtrlRegs.PIEIFR1.all = 0;
PieCtrlRegs.PIEIFR2.all = 0;
PieCtrlRegs.PIEIFR3.all = 0;
PieCtrlRegs.PIEIFR4.all = 0;
PieCtrlRegs.PIEIFR5.all = 0;
PieCtrlRegs.PIEIFR6.all = 0;
PieCtrlRegs.PIEIFR7.all = 0;
PieCtrlRegs.PIEIFR8.all = 0;
PieCtrlRegs.PIEIFR9.all = 0;
PieCtrlRegs.PIEIFR10.all = 0;
PieCtrlRegs.PIEIFR11.all = 0;
PieCtrlRegs.PIEIFR12.all = 0;
}
在这个InitPieVectTable();函数里面我们来看看它做了哪些事情。
/ 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 DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
2)InitPieVectTable();
// InitPieVectTable:
//---------------------------------------------------------------------------
// This function initializes the PIE vector table to a known state.
// This function must be executed after boot time.
//
void InitPieVectTable(void)
{
int16 i;
Uint32 *Source = (void *) &PieVectTableInit;
Uint32 *Dest = (void *) &PieVectTable;
EALLOW;
for(i=0; i < 128; i++) 这是一个重点要关注的点,这几句话的意思是指针赋值,就是把SOURCE的源指针赋给DEST的目的指针。我们知道PIE有96个中断向量,而PieVectTable这是一个全局的变量,它在这个DSP2833x_GlobalVariableDefs.c文件中定义了的。暂且 把这里意思理解为把中断向量表全局化吧。我也不知道对不对。 有知道的朋友可以讲解一下。
*Dest++ = *Source++;
EDIS;
// Enable the PIE Vector Table
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; 打开PIE控制器。我们可以把这个理解为一个总开关吧。
}
//===========================================================================
// End of file.
//===========================================================================
这个地方是开启DMA中断的程序。我们知道DMA在PIE第7组上,它有DMA通道。我们开了第七组的通道1.
Group 7 PIE Vectors
DINTCH1_ISR, // 7.1DMA channel 1
DINTCH2_ISR, // 7.2DMA channel 2
DINTCH3_ISR, // 7.3DMA channel 3
DINTCH4_ISR, // 7.4DMA channel 4
DINTCH5_ISR, // 7.5DMA channel 5
DINTCH6_ISR, // 7.6DMA channel 6
rsvd_ISR, // 7.7
rsvd_ISR, // 7.8
// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // Allow access to EALLOW protected registers
PieVectTable.DINTCH1= &local_DINTCH1_ISR;
EDIS; // Disable access to EALLOW protected registers
IER = M_INT7 ; //Enable INT7 (7.1 DMA Ch1)
EnableInterrupts();
void EnableInterrupts()
{
// Enable the PIE
PieCtrlRegs.PIECTRL.bit.ENPIE = 1;
这里是把PIE总中断开关打开了
// Enables PIE to drive a pulse into the CPU
PieCtrlRegs.PIEACK.all = 0xFFFF;
PIEACK这是一个中断应答位,第一个PIE 中断都有一上应答们,情况是这样的:
如果PIEACK=1则CPU会等待该中断,如果PIEACK=0;则PIE进入CPU中断。
-------------------这个地方一知道我解释的对不对,如果有问题请各位指出来。以免误导后来者。
// Enable Interrupts at the CPU level
EINT;
}
本帖最后由 我爱你的吻123 于 2015-10-30 15:25 编辑
在这个工程中,这是第四步,也就是我们所说的ADC初始化。我们来看看,这个函数到底做了点啥!
InitAdc();// For this example, init the ADC
//---------------------------------------------------------------------------
// InitAdc:
//---------------------------------------------------------------------------
// This function initializes ADC to a known state.
//
void InitAdc(void)
{
extern void DSP28x_usDelay(Uint32 Count);
// *IMPORTANT*
// The ADC_cal function, whichcopies the ADC calibration values from TI reserved
// OTP into the ADCREFSEL and ADCOFFTRIM registers, occurs automatically in the
// Boot ROM. If the boot ROM code is bypassed during the debug process, the
// following function MUST be called for the ADC to function according
// to specification. The clocks to the ADC MUST be enabled before calling this
// function.
// See the device data manual and/or the ADC Reference
// Manual for more information.
EALLOW;
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1;
要想知道这句话是什么意思 ,我们要先知道SysCtrlRegs是个什么,这个呢中文叫做系统控制寄存器,一般情况下,带系统两字就是非常牛的东东了。那么它真的很牛,什么高速、低速、串行外设都归它管。
接下来PCLKCR0这个叫 Peripheral clock control register 串行外设时钟控制器,注意这时有两个啊,一个0、一个1.不同的外设挂在不同的上面,在这里我们就不深究了,感兴趣的同学可以自己查一下资料啊。
这个PCLKCR寄存器啊,控制了很多东东,如MCBSPENCLK,SCIA-BENCLK,SPIAENCLK,
EVA-BENCLK,ADCENCLK.等等。
我们这里就用了ADCENCLK,如果ADCENCLK=1。则使能ADC外设时钟HSPCLK,那么是多少呢,150/4=25MHZ.所以说解释了这么长时间,我们就是给ADC加上了时钟。
ADC_cal();
EDIS;
// To powerup the ADC the ADCENCLK bit should be set first to enable
// clocks, followed by powering up the bandgap, reference circuitry, and ADC core.
// Before the first conversion is performed a 5ms delay must be observed
// after power up to give all analog circuits time to power up and settle
// Please note that for the delay function below to operate correctly the
// CPU_RATE define statement in the DSP2833x_Examples.h file must
// contain the correct CPU clock period in nanoseconds.
AdcRegs.ADCTRL3.all = 0x00E0;// Power up bandgap/reference/ADC circuits
上面AdcRegs.ADCTRL3这个真的要好好说说,这是我们能够使用ADC的关键啊。这个叫ADC寄存器。我们先来看张图啊。
这里详细介绍了这个寄存器的所有功能啊。不知道各位看不看的清楚。如果我们按照上面的0X00E0展开,则为0000.0000.1110.0000.大家注意了,这些个是什么意思呢。就是说, 我们采用顺序采样模式,采用相应的分频模式,ADC电源控制上电,ADC带隙电源控制上电,ADC参考控制电源上电,ADC选内部电源为参考。
到此,这个寄存器分解完毕。真是不简单啊。
DELAY_US(ADC_usDELAY); // Delay before converting ADC channels---这是一个延时函数,我就不细表了。
}
//===========================================================================
// End of file.
//===========================================================================
我们既然是做ADC相关的程序编写,那么就啰嗦一点把这个ADC_REGS也搞清楚。先看几个图。
struct ADC_REGS {
union ADCTRL1_REG ADCTRL1; // ADC Control 1
union ADCTRL2_REG ADCTRL2; // ADC Control 2
union ADCMAXCONV_REG ADCMAXCONV; // Max conversions 最大转换通道,比如你想转换3个通道,那么这个值就等于2. 0.1.2
union ADCCHSELSEQ1_REG ADCCHSELSEQ1;// Channel select sequencing control 1
union ADCCHSELSEQ2_REG ADCCHSELSEQ2;// Channel select sequencing control 2
union ADCCHSELSEQ3_REG ADCCHSELSEQ3;// Channel select sequencing control 3
union ADCCHSELSEQ4_REG ADCCHSELSEQ4;// Channel select sequencing control 4
union ADCASEQSR_REG ADCASEQSR; // Autosequence status register
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
union ADCTRL3_REG ADCTRL3; // ADC Control 3
union ADCST_REG ADCST; // ADC Status Register
Uint16 rsvd1;
Uint16 rsvd2;
union ADCREFSEL_REG ADCREFSEL; // Reference Select Register
union ADCOFFTRIM_REG ADCOFFTRIM; // Offset Trim Register
};
#define ADC_CKPS 0x1 // ADC module clock = HSPCLK/2*ADC_CKPS = 25.0MHz/(1*2) = 12.5MHz
#define ADC_SHCLK0xf // S/H width in ADC module periods = 16 ADC clocks
#define AVG 1000// Average sample limit
#define ZOFFSET 0x00// Average Zero offset
#define BUF_SIZE 40 // Sample buffer size
// Specific ADC setup for this example:
AdcRegs.ADCTRL1.bit.ACQ_PS = ADC_SHCLK;这里面ADC_SHCLK=0xf,就是说这里为四个1,这个位是采样时间预定标。就是说采样的开关时间。如果说学习过信号与系统,就会知道这个是什么意思了。就是说我把一段连续的信号变为离散的信号 ,我从什么时候开什么时候关,形成一个截断区间。这是我的理解。
AdcRegs.ADCTRL3.bit.ADCCLKPS = ADC_CKPS;这个我上面讲过, 这里ADC_CKPS=0x1.就是两分频。25MHZ分为2,为12.5MHZ.
AdcRegs.ADCTRL1.bit.SEQ_CASC = 0; // 0 Non-Cascaded Mode 这里这个位为0,则为双排序模式。为1则为级联模式。
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 0x1; 中断使能SEQ1
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 0x1; 将该位写1立即奖排序器复位为一个初始的“预触发”状态。
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0;
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; ----------这些就是排序器要转换的通道了。分为两组,每组四个。
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x2;
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x3;
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x0;
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x1;
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x2;
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x3;
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 3; // Set up ADC to perform 4 conversions for every SOC
每个SOC最大转换通道数。这里为四个。
这里是第五步了,我们要初始化DMA了。离目标越来越近了,有点小激动。。。。。
//Step 5. User specific code, enable interrupts:
// Initialize DMA
DMAInitialize();
// This function initializes the DMA to a known state.
// 这个函数把DMA初始到确切的状态。
void DMAInitialize(void)
{
EALLOW;
// Perform a hard reset on DMA
DmaRegs.DMACTRL.bit.HARDRESET = 1; DMA硬重启位
asm (" nop"); // one NOP required after HARDRESET
// Allow DMA to run free on emulation suspend DEBUG模式位
// 0 halt after current read-write operation 0停止随后的读写操作
// 1 continue running 1 继续执行。
DmaRegs.DEBUGCTRL.bit.FREE = 1;
EDIS;
}
// Clear Table
for (i=0; i<BUF_SIZE; i++)
{
DMABuf1 = 0;
}
这个地方很明确,我们是想把BUF清空,免得有其它乱码在这里面存在。
volatile Uint16 *DMADest; DMA目标地址指针
volatile Uint16 *DMASource; DMA源地址指针
interrupt void local_DINTCH1_ISR(void);
上面 这些是我们在文件中声明了一些变量。
void DMACH1AddrConfig(volatile Uint16 *DMA_Dest,volatile Uint16 *DMA_Source)
源地址有两个,一个A为用于传输时(随每个字节递增),另一个B作为返回的备份(当一帧结束后,重新装载入A)
目的地址有两个,一个A为用于传输时(随每个字节递增),另一个B作为返回的备份(当一帧结束后,重新装载入A)
每次启动DMA相应通道,都会把B装载入A
void DMACH1BurstConfig(Uint16 bsize, int16 srcbstep, int16 desbstep)
Bsize: 每一个脉冲传递的字的个数,实际脉冲数为bsize+1
Srcbstep:每传递一个字后,源地址A增量
Desbstep:每传递一个字后,目的地址A增量
void DMACH1TransferConfig(Uint16 tsize, int16 srctstep, int16 deststep)
Tsize:每一帧的脉冲个数,脉冲递减到0时(即一帧传递完成,也是DMA传递完成),产生DMA中断。实际帧数为tsize+1
Srctstep:每个脉冲的最后一个字传递结束后,源地址A增量
Deststep:每个脉冲的最后一个字传递结束后,目的地址A增量
void DMACH1WrapConfig(Uint16 srcwsize, int16 srcwstep, Uint16 deswsize, int16 deswstep)
Srcwsize:当已经传递的脉冲数为srcwsize+1的整数倍时,源地址(B)增加srcwstep(常为0),并装载入源地址A
Deswsize:当已经传递的脉冲数为deswsize+1的整数倍时,目的地址(B)增加deswstep(常为0),并装载入目的地址A
void DMACH1ModeConfig(Uint16 persel, Uint16 perinte, Uint16 oneshot, Uint16 cont, Uint16 synce, Uint16 syncsel, Uint16 ovrinte, Uint16 datasize, Uint16 chintmode, Uint16 chinte)
Persel:选择触发DMA的外设中断源
Perinte:外设中断使能,
Oneshot:使能时,外设产生一次中断,就能够把一帧传递完。禁止,外设产生一次中断,只能传递一个脉冲
Cont:使能时,每次DMA结束后,需要再次启动DMA时,就不需要调用void StartDMACH1(void)。禁止时,重启DMA,需要调用void StartDMACH1(void)
Datasize:设置每个字是16位或者32位
Chintmode:设置DMA中断是在DMA启动或者结束时产生
Chinte:DMA相应通道的中断使能(外设级)。
注:Perinte和Chinte同时使能时,才能进入DMA通道中断
仅Perinte使能,可以传输数据,但是不进入通道的中断程序
void StartDMACH1(void)
首次启动DMA,若Cont为禁止,每次DMA结束后,需要再次启动DMA时需要调用
只开启相应用于触发的外设级中断,不开启PIE对应位,则能够触发DMA而不触发CPU的中断程序
经过DMACH1ModeConfig配置的中断,DMA会自动清除相应外设级的中断标志位,不用程序清除
上面的这些说明是我找的一些资料。
// Configure DMA Channel 配置DMA通道
DMADest = &DMABuf1; //Point DMA destination to the beginning of the array 这里指出我们的目标地址是在DMABuf1这个寄存器
DMASource = &AdcMirror.ADCRESULT0; //Point DMA source to ADC result register base 源地址是这ADCRESULT0寄存器
DMACH1AddrConfig(DMADest,DMASource);
DMACH1BurstConfig(3,1,10);
Bsize: 每一个脉冲传递的字的个数,实际脉冲数为bsize+1
Srcbstep:每传递一个字后,源地址A增量
Desbstep:每传递一个字后,目的地址A增量 那么(3,1,10),每一个脉冲传递的个数为3,每传递一个字后,源地地址加1,目标地址加10;
DMACH1TransferConfig(9,1,0);
Tsize:每一帧的脉冲个数,脉冲递减到0时(即一帧传递完成,也是DMA传递完成),产生DMA中断。实际帧数为tsize+1
Srctstep:每个脉冲的最后一个字传递结束后,源地址A增量
Deststep:每个脉冲的最后一个字传递结束后,目的地址A增量
请按上面对号入座。!!!!!!!!!
DMACH1WrapConfig(1,0,0,1);
void DMACH1WrapConfig(Uint16 srcwsize, int16 srcwstep, Uint16 deswsize, int16 deswstep)
Srcwsize:当已经传递的脉冲数为srcwsize+1的整数倍时,源地址(B)增加srcwstep(常为0),并装载入源地址A
Deswsize:当已经传递的脉冲数为deswsize+1的整数倍时,目的地址(B)增加deswstep(常为0),并装载入目的地址A
请按上面对号入座。!!!!!!!!!
DMACH1ModeConfig(DMA_SEQ1INT,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE,SYNC_DISABLE,SYNC_SRC,
OVRFLOW_DISABLE,SIXTEEN_BIT,CHINT_END,CHINT_ENABLE);
void DMACH1ModeConfig(Uint16 persel, Uint16 perinte, Uint16 oneshot, Uint16 cont, Uint16 synce, Uint16 syncsel, Uint16 ovrinte, Uint16 datasize, Uint16 chintmode, Uint16 chinte)
Persel:选择触发DMA的外设中断源
Perinte:外设中断使能,
Oneshot:使能时,外设产生一次中断,就能够把一帧传递完。禁止,外设产生一次中断,只能传递一个脉冲
Cont:使能时,每次DMA结束后,需要再次启动DMA时,就不需要调用void StartDMACH1(void)。禁止时,重启DMA,需要调用void StartDMACH1(void)
Datasize:设置每个字是16位或者32位
Chintmode:设置DMA中断是在DMA启动或者结束时产生
Chinte:DMA相应通道的中断使能(外设级)。
注:Perinte和Chinte同时使能时,才能进入DMA通道中断
仅Perinte使能,可以传输数据,但是不进入通道的中断程序
请按上面对号入座。!!!!!!!!!
void DMACH1AddrConfig(volatile Uint16 *DMA_Dest,volatile Uint16 *DMA_Source)
{
EALLOW;
// Set up SOURCE address:
DmaRegs.CH1.SRC_BEG_ADDR_SHADOW = (Uint32)DMA_Source; // Point to beginning of source buffer
DmaRegs.CH1.SRC_ADDR_SHADOW = (Uint32)DMA_Source;
// Set up DESTINATION address:
DmaRegs.CH1.DST_BEG_ADDR_SHADOW = (Uint32)DMA_Dest; // Point to beginning of destination buffer
DmaRegs.CH1.DST_ADDR_SHADOW = (Uint32)DMA_Dest;
这个也好理解,就是源地址与目标地址的配置。
EDIS;
}
void DMACH1BurstConfig(Uint16 bsize, int16 srcbstep, int16 desbstep)
{
EALLOW;
// Set up BURST registers:
DmaRegs.CH1.BURST_SIZE.all = bsize; // Number of words(X-1) x-ferred in a burst
DmaRegs.CH1.SRC_BURST_STEP = srcbstep; // Increment source addr between each word x-ferred
DmaRegs.CH1.DST_BURST_STEP = desbstep; // Increment dest addr between each word x-ferred
EDIS;
}