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