天天看點

stm32的DMA+ADC多通道資料采集1前言2常用的幾個使能函數介紹3幾個關鍵點4代碼編寫5 結果

1前言

硬體:stm32f103rct6,輸入大容量産品。

軟體:keil MDK5.0

固件庫:stm32f1标準外設庫。

調試軟體:友善序列槽助手

stm32有多達16個通道,常用的采樣方法有兩種,一是分時采集每個通道的資料,用查詢或者中斷的方式采集完一個通道的資料,将通道設定為下一個,依次采集,這種方法思路簡單,但是效率不高,适合初學者。二是利用DMA功能采集多個規則通道的資料,注意是規則通道,因為規則通道是多通道共用一個資料寄存器ADCx->DR存放結果,而注入通道有多個寄存器,注入采樣以後再講。下面進入正題。

2常用的幾個使能函數介紹

我們用到adc和dma外設,首先要對其使能,下面是幾個重要的使能函數,因為有些使能函數使能一次即可,有些使能函數轉換完後自動清零,要重新使能一次,這裡是個大坑,如果你不再次使能,采樣根本不行。

ADC_Cmd(ADC1, ENABLE),設定ADC_CR2的ADON位,讓adc上電,這個函數使能一次即可。

ADC_DMACmd(ADC1, ENABLE) 設定ADC_CR2的DMA位,使能ADC的DMA請求,使能一次即可。

DMA_Cmd(DMA1_Channel1, DISABLE) 設定DMA1對應的ADC通道,通道1使能,使能一次即可,但需要注意的是這個函數與DMA_SetCurrDataCounter(DMA1_Channel1, 2) 相關,要設定dma的傳輸量,這個函數的第二個參數必須為DISABLE。

ADC_SoftwareStartConvCmd(ADC1, ENABLE) 使能ADC_CR1的SWSTART位,當adc完成一次轉換後,這個位自動變為0,要重新打開adc,切記,這函數每完成一次adc采樣後都要再調用一次,表示用軟體觸發的方式開始adc采樣。

3幾個關鍵點

1 因為是多通道,要開啟adc的掃描模式(連續模式是否開始視自己的需求而定)。

2 ADC有自己的分頻控制器,不要忘記打開。可以調用RCC_ADCCLKConfig函數。

3 因為是多通道,要設定每個通道的采樣先後順序,調用ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5)函數。

4 因為ADC的DR是16位的,是以DMA的外設資料寬度為halfword。

5 如果開啟了DMA的中斷一定不要忘記中斷使能。

6 如果DMA不采用回環模式,每次傳送完資料後,DMA_CNDTRx寄存器清零,要重裝這個寄存器的值,在重裝前確定DMAde通道是關閉的,否則重裝無效。

4代碼編寫

4.1 初始化ADC,并設定采樣通道的次序。

void ADC1_init(void)

{

ADC_InitTypeDef ADC_InitStruct;

GPIO_InitTypeDef io;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//這裡一定不要忘記,這裡有單獨的ADC分頻控制器

io.GPIO_Mode = GPIO_Mode_AIN;//PA0 PA1分别作為ADC1的通道0 通道1
io.GPIO_Pin = (GPIO_Pin_0|GPIO_Pin_1);
GPIO_Init(GPIOA, &io);	

ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = ENABLE;//開啟掃描模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//close continuous mode
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 2;//兩個通道

ADC_Init(ADC1, &ADC_InitStruct);


ADC_Cmd(ADC1, ENABLE);//adc使能
ADC_DMACmd(ADC1, ENABLE);//dma請求使能

ADC_ResetCalibration(ADC1);//複位校準
while(ADC_GetResetCalibrationStatus(ADC1));//等待複位轉換結束
ADC_StartCalibration(ADC1);//開始校準
while(ADC_GetCalibrationStatus(ADC1));//等待開始校準結束

ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);//設定采樣通道的次序,有幾個通道調用幾次這個函數
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
}
           

通道的總數量用ADC_InitStruct.ADC_NbrOfChannel = X設定,同時下面要調用幾次ADC_RegularChannelConfig()函數,設定通道的采樣次序。

4.2 DMA初始化

void ADC1_DMA_config(DMA_Channel_TypeDef* DMAy_Channelx,u32 memAddr,u32 periphAddr,u8 bufSize)

{

DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1時鐘使能

//DMA_DeInit(DMAy_Channelx);

/* 初始化DMA */
DMA_InitStructure.DMA_PeripheralBaseAddr = periphAddr;//外設位址
DMA_InitStructure.DMA_MemoryBaseAddr = memAddr;//記憶體位址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外設是源位址
DMA_InitStructure.DMA_BufferSize = bufSize;//一次DMA傳輸的位元組量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設位址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//記憶體位址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//adc的DR是16位的
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//按half word
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 正常模式,傳輸完一次後資料量不自動重載
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //
DMA_Init(DMAy_Channelx, &DMA_InitStructure);//DMA初始化

#if(ADC1_DMA_INT_EN)
{
	NVIC_InitTypeDef NVIC_InitStruct;
	
	NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn; //ADC1挂在DMA的通道1上
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	DMA_ITConfig(DMAy_Channelx,DMA_IT_TC,ENABLE);//DMA chaneel interrupt EN
}
#endif
}	
           

以上代碼完成了DMA結構體配置,宏ADC1_DMA_INT_EN為1,表示使用DMA中斷,下面配置了NVIC結構體,并開啟DMA傳輸完成中斷。因為ADC的DR是16位的是以設定外設資料寬度為halfword,傳輸資料量為2,

這裡一定要注意,傳輸的資料量表示傳送的次數,而傳送次數由源位址資料寬度決定,因為有兩個通道是以配置傳輸資料量為2,表示傳送兩次,實際傳送了4個位元組。這裡一定要注意,如果配置為4則進不了DMA中斷。這是新手很容易犯的錯誤。

4.3 設定DMA傳送數量并用軟體觸發adc。

void app_ADC1DMA_enable(void)

{

DMA_Cmd(DMA1_Channel1, DISABLE);//DMA channel enable

DMA_SetCurrDataCounter(DMA1_Channel1, 2);

DMA_Cmd(DMA1_Channel1, ENABLE);

ADC_SoftwareStartConvCmd(ADC1, ENABLE);//軟體觸發

}

因為沒有開始DMA回環模式,每次傳送完後,DMA_CNDTRx寄存器清零,這時不管DMA使能與否DMA都會停止,是以要重新設定傳送的資料量,再此之前一定要先關閉DMA通道,切記。

4.4中斷函數

void DMA1_Channel1_IRQHandler(void)

{

if(DMA_GetFlagStatus(DMA1_FLAG_TC1))

{

dma1FinishFlag = 1;

DMA_ClearFlag(DMA1_FLAG_TC1);

}

}

沒啥好說的,在中斷裡置位在main裡面查詢,查詢到置1就處理資料。

4.5 主函數。

nt main()

{

extern u8 dma1FinishFlag;

ST_USART1_RCV_BUF rxbuf={{0},0,0,0};

u32 temp = 0;

u8 i;

//float fval;

SysTick_Init(72);
NVIC_PriorityGroupConfig(2);
LED_Init();
usart1_config(9600);
ADC1_init();
ADC1_DMA_config(DMA1_Channel1,(u32)rxbuf.buf,(u32)&ADC1->DR,2);
app_ADC1DMA_enable();
while(1)
{
	temp = 0;
	if(dma1FinishFlag)
	{
		dma1FinishFlag = 0;//清空标志位
		printf("voltage1 is %f\n",(rxbuf.buf[1]<<8|rxbuf.buf[0])/4096.0*3.3);
		printf("voltage2 is %f\n",(rxbuf.buf[3]<<8|rxbuf.buf[2])/4096.0*3.3);
		app_ADC1DMA_enable();//開始下一次傳輸
		delay_ms(1000);
		PBout(11)=~PBout(11);
	}
}
}
           

要注意ADC1_DMA_config傳參數時,外設位址是(u32)&ADC1->DR,不要忘記&。

5 結果

編譯下載下傳程式,在序列槽助手輸出結果如下所示:

stm32的DMA+ADC多通道資料采集1前言2常用的幾個使能函數介紹3幾個關鍵點4代碼編寫5 結果

本例程用了兩個通道,上圖輸出了倆通道的電壓,一個通道接0v,一個人通道接1v。

繼續閱讀