關(guān)于Stm32定時(shí)器+ADC+DMA進(jìn)行AD采樣的實(shí)現(xiàn)
注:此STM32單片機(jī)為STM32F103系列的
Stm32的ADC有DMA功能這都毋庸置疑,也是我們用的最多的!然而,如果我們要對(duì)一個(gè)信號(hào)(比如脈搏信號(hào))進(jìn)行定時(shí)采樣(也就是隔一段時(shí)間,比如說2ms),有三種方法:
1、使用定時(shí)器中斷每隔一定時(shí)間進(jìn)行ADC轉(zhuǎn)換,這樣每次都必須讀ADC的數(shù)據(jù)寄存器,非常浪費(fèi)時(shí)間!
2、把ADC設(shè)置成連續(xù)轉(zhuǎn)換模式,同時(shí)對(duì)應(yīng)的DMA通道開啟循環(huán)模式,這樣ADC就一直在進(jìn)行數(shù)據(jù)采集然后通過DMA把數(shù)據(jù)搬運(yùn)至內(nèi)存。但是這樣做的話還得加一個(gè)定時(shí)中斷,用來定時(shí)讀取內(nèi)存中的數(shù)據(jù)!
3、使用ADC的定時(shí)器觸發(fā)ADC轉(zhuǎn)換的功能,然后使用DMA進(jìn)行數(shù)據(jù)的搬運(yùn)!這樣只要設(shè)置好定時(shí)器的觸發(fā)間隔,就能實(shí)現(xiàn)ADC定時(shí)采樣轉(zhuǎn)換的功能,然后可以在程序的死循環(huán)中一直檢測(cè)DMA轉(zhuǎn)換完成標(biāo)志,然后進(jìn)行數(shù)據(jù)的讀取,或者使能DMA轉(zhuǎn)換完成中斷,這樣每次轉(zhuǎn)換完成就會(huì)產(chǎn)生中斷,我是采用第二種方法。下面上代碼:我這里使用的單通道
//定時(shí)器初始化
voidTIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;
TIM_OCInitTypeDefTIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_TimeBaseStructure.TIM_Period=1999;//設(shè)置2ms一次TIM2比較的周期
TIM_TimeBaseStructure.TIM_Prescaler=71;//系統(tǒng)主頻72M,這里分頻71,相當(dāng)于1000K的定時(shí)器2時(shí)鐘
TIM_TimeBaseStructure.TIM_ClockDivision=0x0;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//下面詳細(xì)說明
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_Pulse=1000;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//如果是PWM1要為L(zhǎng)ow,PWM2則為High
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
//TIM_InternalClockConfig(TIM2);
//TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable);
//TIM_UpdateDisableConfig(TIM2,DISABLE);
}
//ADC_DMA初始化配置
voidADC_DMA_Config(void)
{
DMA_InitTypeDefDMA_InitStructure;//注:ADC為12位模數(shù)轉(zhuǎn)換器,只有ADCConvertedValue的低12位有效
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA時(shí)鐘
DMA_DeInit(DMA1_Channel1);//開啟DMA1的第一通道
DMA_InitStructure.DMA_PeripheralBaseAddr=ADC1_DR_Address;//DMA對(duì)應(yīng)的外設(shè)基地址
DMA_InitStructure.DMA_MemoryBaseAddr=(uint32_t)&ADCConvertedValue;//內(nèi)存存儲(chǔ)基地址
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//DMA的轉(zhuǎn)換模式為SRC模式,由外設(shè)搬移到內(nèi)存
DMA_InitStructure.DMA_BufferSize=1;//DMA緩存大小,1個(gè)
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//接收一次數(shù)據(jù)后,設(shè)備地址禁止后移
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Disable;//關(guān)閉接收一次數(shù)據(jù)后,目標(biāo)內(nèi)存地址后移
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//定義外設(shè)數(shù)據(jù)寬度為16位
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//DMA搬移數(shù)據(jù)尺寸,HalfWord就是為16位
DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//循環(huán)轉(zhuǎn)換模式
DMA_InitStructure.DMA_Priority=DMA_Priority_High;//DMA優(yōu)先級(jí)高
DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//M2M模式禁用
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);//使能傳輸完成中斷
}
//ADC初始化
voidPulseSenosrInit(void)
{
//當(dāng)外部觸發(fā)信號(hào)被選為ADC規(guī)則或注入轉(zhuǎn)換時(shí),只有它的上升沿可以啟動(dòng)轉(zhuǎn)換
ADC_InitTypeDefADC_InitStructure;
ADC_GPIO_Configuration();//IO口配置
TIM2_Configuration();//定時(shí)器配置
ADC_DMA_Config();//ADC_DMA配置
ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;//獨(dú)立的轉(zhuǎn)換模式ADC_DUALMOD[3:0]=0000;
ADC_InitStructure.ADC_ScanConvMode=DISABLE;//關(guān)閉掃描模式因?yàn)橹挥幸粋€(gè)通道
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//關(guān)閉連續(xù)轉(zhuǎn)換模式否則只要觸發(fā)一次,
//后續(xù)的轉(zhuǎn)換就會(huì)永不停歇(除非CONT清0),這樣第一次以后的ADC,就不是由TIM2_CC2來觸發(fā)了
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T2_CC2;//軟件轉(zhuǎn)換模式
ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//對(duì)齊方式,ADC為12位中,右對(duì)齊方式ADC_ALIGN=0;
ADC_InitStructure.ADC_NbrOfChannel=1;//開啟通道數(shù),1個(gè)ADC_SQR1[23:20]=0000;
//ADC_SQR1[23:20]設(shè)置通道數(shù)目的選擇
ADC_Init(ADC1,&ADC_InitStructure);
//RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置時(shí)鐘(12MHz),在RCC里面還應(yīng)配置APB2=AHB時(shí)鐘72MHz
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,1,ADC_SampleTime_1Cycles5);
//ADC_SMPR2ADC_SMPR1設(shè)置每個(gè)通道的采樣時(shí)間
//ADC_SQR1[19:0]?DC_SQR1[29:0]?DC_SQR3[29:0]設(shè)置對(duì)應(yīng)通道的轉(zhuǎn)換順序適用于多通道采樣
//ADC通道組,第3個(gè)通道采樣順序1,轉(zhuǎn)換時(shí)間
ADC_ExternalTrigConvCmd(ADC1,ENABLE);//設(shè)置外部觸發(fā)模式使能(這個(gè)“外部“其實(shí)僅僅是相//對(duì)于ADC模塊的外部,
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);//ADC命令,使能ADC_ADON=1
ADC_ResetCalibration(ADC1);//重新校準(zhǔn)
while(ADC_GetResetCalibrationStatus(ADC1));//等待重新校準(zhǔn)完成
ADC_StartCalibration(ADC1);//開始校準(zhǔn)ADC_RSTCAL=1;初始化校準(zhǔn)寄存器
while(ADC_GetCalibrationStatus(ADC1));//等待校準(zhǔn)完成ADC_CAL=0;
//ADC_SoftwareStartConvCmd(ADC1,ENABLE);//連續(xù)轉(zhuǎn)換開始,ADC通過DMA方式不斷的更新RAM區(qū)。
//ADC_SWSTART=1開始規(guī)則轉(zhuǎn)換切記軟件觸發(fā)也屬于外部事件要設(shè)置ADC_EXTTRIG=1
//////實(shí)際上還是在STM32內(nèi)部)
TIM_Cmd(TIM2,ENABLE);//最后面打開定時(shí)器使能
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA
}
//中斷處理函數(shù)
voidDMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1)!=RESET){
//自己的中斷處理代碼但是記住程序不要太復(fù)雜最好不要超過中斷時(shí)間
DMA_ClearITPendingBit(DMA1_IT_TC1);
}
}
//中斷配置
NVIC_InitTypeDefNVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel=DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
voidADC_GPIO_Configuration(void)//ADC配置函數(shù)
{
GPIO_InitTypeDefGPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA,ENABLE);//使能ADC和GPIOA時(shí)鐘
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;//管腳2
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模擬輸入模式
GPIO_Init(GPIOA,&GPIO_InitStructure);//GPIO組
}