在Application note中,ADC已经有了很多的例子,唯独缺少一个多通道定时触发进行扫描的例程。从理论上讲,将ADC配置为多通道ADC转换,配合DMA进行数据传输,而且使用Timer+PRS的方式,进行自动触发,这个功能应该是完全OK的。但是只是缺少一个例程,以及自行研究的过程。
首先配置ADC。
将ADC配置为多通道采样,例程中将ADC配置为通道2~通道4输入,唯一不一样的就是需要使能PRS,因为PRS需要将Timer与ADC相连接,这样的话,Timer就能通过PRS来触发ADC扫描了。
ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
ADC_InitScan_TypeDef scanInit = ADC_INITSCAN_DEFAULT;
...................
scanInit.prsEnable = true;
配置Timer。
Timer决定了ADC会隔多久会进行一次多通道扫描。例程里将Timer配置成一个简单的溢出计数器,溢出的周期为32KHz左右。
配置DMA。
由于ADC Scan的结果寄存器只有一个,因此必须使用DMA将ADC的结果转到RAM中。配置参考了ADC Scan的例程,DMA数据搬运的触发信号配置为DMAREQ_ADC0_SCAN。另外需要使能DMA中断,并且设置DMA传输完成函数。在传输完成函数中,刷新DMA。
void transferComplete(unsigned int channel, bool primary, void *user)
{
DMA_ActivateBasic(DMA_CHANNEL,
true,
false,
samples,
(void *)((uint32_t) &(ADC0->SCANDATA)),
NUM_SAMPLES - 1);
printf("%d, %d, %d \r\n", samples[0], samples[1], samples[2]);
GPIO_PinOutToggle(gpioPortD, 7);
}
配置PRS。
连接Timer与ADC,使用PRS通道0
工作流程:
系统初始化,包括PRS初始化及配置。
1.初始化ADC
2.初始化DMA,并且初始化一次DMA传输DMA_ActivateBasic(),等待Timer触发ADC进行扫描
3.初始化Timer
4.Timer定时溢出,通过PRS触发ADC Scan. 每次ADC Scan有数据时即DMAREQ_ADC0_SCAN信号成立时,通过DMA传输到RAM中。
5.当DMA传输完毕,调用DMA回调函数完成DMA刷新,等待下一次Timer触发。回到步骤4。
6.在例程中加入了SWO输出,以及ADC扫描溢出中断等测试条件。
例程如下,可能比较多些,各位看官请耐心。
#include <stdio.h>
#include "efm32.h"
#include "em_chip.h"
#include "em_emu.h"
#include "em_cmu.h"
#include "em_adc.h"
#include "em_prs.h"
#include "em_timer.h"
#include "em_dma.h"
#include "em_gpio.h"
#include "stdio.h"
/** DMA control block, requires proper alignment. */
#if defined (__ICCARM__)
#pragma data_alignment=256
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2];
#elif defined (__CC_ARM)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#elif defined (__GNUC__)
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2] __attribute__ ((aligned(256)));
#else
#error Undefined toolkit, need to define alignment
#endif
/* DMA callback structure */
DMA_CB_TypeDef cb;
/** DMA channel used for scan sequence sampling adc channel 2, 3 and 4. */
#define DMA_CHANNEL 0
#define NUM_SAMPLES 3
uint32_t samples[NUM_SAMPLES];
void setupSWO(void)
{
uint32_t *dwt_ctrl = (uint32_t *) 0xE0001000;
uint32_t *tpiu_prescaler = (uint32_t *) 0xE0040010;
uint32_t *tpiu_protocol = (uint32_t *) 0xE00400F0;
CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_GPIO;
/* Enable Serial wire output pin */
GPIO->ROUTE |= GPIO_ROUTE_SWOPEN;
#if defined(_EFM32_GIANT_FAMILY)
/* Set location 0 */
GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC0;
/* Enable output on pin - GPIO Port F, Pin 2 */
GPIO->P[5].MODEL &= ~(_GPIO_P_MODEL_MODE2_MASK);
GPIO->P[5].MODEL |= GPIO_P_MODEL_MODE2_PUSHPULL;
#else
/* Set location 1 */
GPIO->ROUTE = (GPIO->ROUTE & ~(_GPIO_ROUTE_SWLOCATION_MASK)) | GPIO_ROUTE_SWLOCATION_LOC1;
/* Enable output on pin */
GPIO->P[2].MODEH &= ~(_GPIO_P_MODEH_MODE15_MASK);
GPIO->P[2].MODEH |= GPIO_P_MODEH_MODE15_PUSHPULL;
#endif
/* Enable debug clock AUXHFRCO */
CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN;
while(!(CMU->STATUS & CMU_STATUS_AUXHFRCORDY));
/* Enable trace in core debug */
CoreDebug->DHCSR |= 1;
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* Enable PC and IRQ sampling output */
*dwt_ctrl = 0x400113FF;
/* Set TPIU prescaler to 16. */
*tpiu_prescaler = 0xf;
/* Set protocol to NRZ */
*tpiu_protocol = 2;
/* Unlock ITM and output data */
ITM->LAR = 0xC5ACCE55;
ITM->TCR = 0x10009;
}
/**************************************************************************//**
* @brief Call-back called when transfer is complete
*****************************************************************************/
void transferComplete(unsigned int channel, bool primary, void *user)
{
DMA_ActivateBasic(DMA_CHANNEL,
true,
false,
samples,
(void *)((uint32_t) &(ADC0->SCANDATA)),
NUM_SAMPLES - 1);
printf("%d, %d, %d \r\n", samples[0], samples[1], samples[2]);//打印log信息
GPIO_PinOutToggle(gpioPortD, 7); //用来测试ADC扫描是否正常工作
}
/**************************************************************************//**
* @brief ADC0_IRQHandler
* Interrupt Service Routine for ADC
*****************************************************************************/
void ADC0_IRQHandler(void)
{
/* Clear ADC0 interrupt flag */
ADC0->IFC = ADC_IFC_SCANOF;
printf("ADC Scan overflow");//用来判断是否有ADC扫描结果溢出的情况发生
}
/***************************************************************************//**
* @brief
* Configure ADC usage for this application.
*******************************************************************************/
static void ADCConfig(void)
{
ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
ADC_InitScan_TypeDef scanInit = ADC_INITSCAN_DEFAULT;
/* Init common issues for both single conversion and scan mode */
init.timebase = ADC_TimebaseCalc(0);
init.prescale = ADC_PrescaleCalc(7000000, 0);
ADC_Init(ADC0, &init);
/* Init for scan sequence use ( for dvk: accelerometer X, Y and Z axis). */
scanInit.reference = adcRefVDD;
scanInit.input = ADC_SCANCTRL_INPUTMASK_CH2 | ADC_SCANCTRL_INPUTMASK_CH3 | ADC_SCANCTRL_INPUTMASK_CH4;
scanInit.prsEnable = true;
ADC_InitScan(ADC0, &scanInit);
/* Enable ADC Interrupt when Single Conversion Complete */
ADC0->IEN = ADC_IEN_SCANOF;
/* Enable ADC interrupt vector in NVIC*/
NVIC_EnableIRQ(ADC0_IRQn);
}
/***************************************************************************//**
* @brief
* Configure Timer for this application.
*******************************************************************************/
static void TimerConfig(void)
{
/* Use default timer settings */
TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
/* Change prescaler to 64, gives roughly 3 overflows per
* second at 14MHz with 0xffff as top value */
timerInit.prescale = timerPrescale1;
TIMER_TopSet(TIMER0, 440);
TIMER_Init(TIMER0, &timerInit);
}
/***************************************************************************//**
* @brief
* Configure DMA usage for this application.
*******************************************************************************/
static void DMAConfig(void)
{
DMA_Init_TypeDef dmaInit;
DMA_CfgDescr_TypeDef descrCfg;
DMA_CfgChannel_TypeDef chnlCfg;
/* Configure general DMA issues */
dmaInit.hprot = 0;
dmaInit.controlBlock = dmaControlBlock;
DMA_Init(&dmaInit);
/* Setting up call-back function */
cb.cbFunc = transferComplete;
cb.userPtr = NULL;
/* Configure DMA channel used */
chnlCfg.highPri = false;
chnlCfg.enableInt = true;
chnlCfg.select = DMAREQ_ADC0_SCAN;
chnlCfg.cb = &cb;
DMA_CfgChannel(DMA_CHANNEL, &chnlCfg);
descrCfg.dstInc = dmaDataInc4;
descrCfg.srcInc = dmaDataIncNone;
descrCfg.size = dmaDataSize4;
descrCfg.arbRate = dmaArbitrate1;
descrCfg.hprot = 0;
DMA_CfgDescr(DMA_CHANNEL, true, &descrCfg);
DMA_ActivateBasic(DMA_CHANNEL,
true,
false,
samples,
(void *)((uint32_t) &(ADC0->SCANDATA)),
NUM_SAMPLES - 1);
}
/******************************************************************************
* @brief Main function
* The main file starts a timer and uses PRS to trigger an ADC conversion.
* It waits in EM1 until the ADC conversion is complete, then prints the
* result on the lcd.
*****************************************************************************/
int main(void)
{
/* Initialize chip */
CHIP_Init();
setupSWO();
CMU_ClockEnable(cmuClock_HFPER, true);
/* Enable clocks required */
CMU_ClockEnable(cmuClock_ADC0, true);
CMU_ClockEnable(cmuClock_PRS, true);
CMU_ClockEnable(cmuClock_TIMER0, true);
CMU_ClockEnable(cmuClock_DMA, true);
CMU_ClockEnable(cmuClock_GPIO, true);
GPIO_PinModeSet(gpioPortD, 7, gpioModePushPull, 1);
/* Select TIMER0 as source and TIMER0OF (Timer0 overflow) as signal (rising edge) */
PRS_SourceSignalSet(0, PRS_CH_CTRL_SOURCESEL_TIMER0, PRS_CH_CTRL_SIGSEL_TIMER0OF, prsEdgePos);
ADCConfig();
DMAConfig();
TimerConfig();
/* Stay in this loop forever */
while (1)
{
}
}
工程链接: