在某些场合,例如想用DAC输出一个正弦波形,最简单的想法就是用一个Timer定时器做一个中断,在中断中,赋给DAC一个新的值,并且触发DAC转换。这样做会有一些弊端,例如在复杂的系统中,中断会有很多个,Timer的中断优先级不可能总是最高的。因此,相位上会有问题。另外,如果用中断,会增加整个系统的功耗。因为需要不停的唤醒MCU,来处理中断。而且功耗和Timer的定时周期息息相关。最后,如果想做一个频率较高的中断,则也会增加整个系统的负担。MCU会忙于处理这个timer中断。执行效率就会下降。
鉴于我们的片内有DMA控制器,因此,我们可以借用DMA来实现这个功能。
实现效果:用Timer的1ms定时,来触发DMA发送数据给DAC,让DAC输出一个指定的波形。
硬件准备:使用TG 的STK, 使用DAC0的CH0来输出一个波形。GPIO口对应为PB11
软件准备:
Timer: 使用系统默认14MHz主频,因此1ms定时的TOP值为14000. 做一个1ms的定时。另外,需要使用DMA来将timer溢出标志位清零,因此,timerInit.dmaClrAct = true;
DMA:源地址为RAM中的数据,目标地址为DAC。触发条件为 chnlCfg.select = DMAREQ_TIMER1_UFOF; //Timer1 的溢出中断标志
DAC:默认配置
例程如下:
#include <stdint.h>
#include <stdbool.h>
#include "efm32.h"
#include "efm32_chip.h"
#include "efm32_cmu.h"
#include "efm32_gpio.h"
#include "efm32_dac.h"
#include "efm32_prs.h"
#include "efm32_dma.h"
#include "efm32_timer.h"
#define DMA_CHANNEL_DAC 2
#define SPITXSAMPLES 9
#pragma data_alignment=256
DMA_DESCRIPTOR_TypeDef dmaControlBlock[DMA_CHAN_COUNT * 2];
/* DMA init structure */
DMA_Init_TypeDef dmaInit;
/* DMA callback structure */
DMA_CB_TypeDef cb[DMA_CHAN_COUNT];
unsigned short ucDACBuffer[SPITXSAMPLES] = {0,400,800,1200,1600,2000,2800,3200,3600};
void DAC_setup(void)
{
/* Use default settings */
DAC_Init_TypeDef init = DAC_INIT_DEFAULT;
DAC_InitChannel_TypeDef initChannel = DAC_INITCHANNEL_DEFAULT;
/* Enable the DAC clock */
CMU_ClockEnable(cmuClock_DAC0, true);
/* Calculate the DAC clock prescaler value that will result in a DAC clock
* close to 500kHz. Second parameter is zero, if the HFPERCLK value is 0, the
* function will check what the current value actually is. */
init.prescale = DAC_PrescaleCalc(500000, 0);
/* Set reference voltage to 1.25V */
init.reference = dacRef2V5;
/* Initialize the DAC and DAC channel. */
DAC_Init(DAC0, &init);
DAC_InitChannel(DAC0, &initChannel, 0);
}
void Timer_Setup(void)
{
CMU_ClockEnable(cmuClock_TIMER1, true);
/* Select TIMER0 parameters */
TIMER_Init_TypeDef timerInit =
{
.enable = true,
.debugRun = true,
.prescale = timerPrescale1,
.clkSel = timerClkSelHFPerClk,
.fallAction = timerInputActionNone,
.riseAction = timerInputActionNone,
.mode = timerModeUp,
.dmaClrAct = false,
.quadModeX4 = false,
.oneShot = false,
.sync = false,
};
timerInit.dmaClrAct = true;
/* Enable overflow interrupt */
//TIMER_IntEnable(TIMER1, TIMER_IF_OF);
/* Enable TIMER0 interrupt vector in NVIC */
//NVIC_EnableIRQ(TIMER1_IRQn);
/* Set TIMER Top value */
TIMER_TopSet(TIMER1, 14000);
/* Configure TIMER */
TIMER_Init(TIMER1, &timerInit);
}
void DACTransferComplete(unsigned int channel, bool primary, void *user)
{
DMA_ActivateBasic(DMA_CHANNEL_DAC,
true,
false,
NULL,
NULL,
SPITXSAMPLES - 1);
}
void DMA_Setup(void)
{
DMA_CfgChannel_TypeDef chnlCfg;
DMA_CfgDescr_TypeDef descrCfg;
/* Setting up call-back function */
cb[DMA_CHANNEL_DAC].cbFunc = DACTransferComplete;
cb[DMA_CHANNEL_DAC].userPtr = NULL;
/* Setting up channel */
chnlCfg.highPri = false;
chnlCfg.enableInt = true;
chnlCfg.select = DMAREQ_TIMER1_UFOF;
chnlCfg.cb = &(cb[DMA_CHANNEL_DAC]);
DMA_CfgChannel(DMA_CHANNEL_DAC, &chnlCfg);
/* Setting up channel descriptor */
descrCfg.dstInc = dmaDataIncNone;
descrCfg.srcInc = dmaDataInc2;
descrCfg.size = dmaDataSize2;
descrCfg.arbRate = dmaArbitrate1;
descrCfg.hprot = 0;
DMA_CfgDescr(DMA_CHANNEL_DAC, true, &descrCfg);
/*Starting transfer. Using Basic since every transfer must be initiated
by the ADC. */
DMA_ActivateBasic(DMA_CHANNEL_DAC,
true,
false,
(void *)&(DAC0->CH0DATA),
(void *)&ucDACBuffer,
SPITXSAMPLES - 1);
}
void DMAInit(void)
{
CMU_ClockEnable(cmuClock_DMA, true);
/* Initializing the DMA */
dmaInit.hprot = 0;
dmaInit.controlBlock = dmaControlBlock;
DMA_Init(&dmaInit);
}
/**************************************************************************//**
* @brief Main function
*****************************************************************************/
int main(void)
{
/* Chip errata */
CHIP_Init();
CMU_ClockEnable(cmuClock_HFPER, true);
CMU_ClockEnable(cmuClock_GPIO, true);
DMAInit();
DAC_setup();
/* Enable DAC channel 0, located on pin PB11 */
DAC_Enable(DAC0, 0, true);
Timer_Setup();
DMA_Setup();
unsigned short usDACValue = 0;;
/* Infinite blink loop */
while (1)
{
}
}
最终的效果: