在上一講單通道ADC電壓采集的基礎上,本節(jié)主要介紹CKS32F4xx系列產(chǎn)品基于DMA傳輸?shù)?a target="_blank">ADC多通道電壓采集轉(zhuǎn)換實現(xiàn)。
DMA傳輸在ADC中的應用
DMA是直接存儲器存取,通常在使用ADC時,需要通過MCU內(nèi)核不停的讀取數(shù)據(jù),如果使用DMA,那么讀取的過程會繞過MCU,減輕MCU內(nèi)核的處理壓力,這樣有利于資源的充分利用,提高ADC數(shù)據(jù)的處理效率。由于ADC規(guī)則通道組只有一個數(shù)據(jù)寄存器中,當轉(zhuǎn)換多個通道時,使用DMA還可以避免丟失已經(jīng)存儲在ADC_DR寄存器中的數(shù)據(jù)。在使能DMA模式的情況下,每完成規(guī)則通道組中的一個通道轉(zhuǎn)換后,都會生成一個DMA請求,便可將轉(zhuǎn)換的數(shù)據(jù)從ADC_DR寄存器傳輸?shù)接弥付ǖ哪繕藘?nèi)存位置。這樣取代單通道實驗使用中斷服務的讀取方法,可以實現(xiàn)多通道ADC應用中高速高效的采集。
軟件設計要點
跟單通道例程一樣,編寫兩個ADC驅(qū)動文件,bsp_adc.h和bsp_adc.c,用來存放ADC所用IO引腳的初始化函數(shù)以及ADC和DMA相關配置函數(shù),主要流程為:
(1)初始化配置ADC目標引腳為模擬輸入模式;
(2)使能ADC時鐘和DMA時鐘;
(3)配置DMA從ADC數(shù)據(jù)寄存器傳輸數(shù)據(jù)到指定的存儲區(qū);
(4)配置通用ADC為獨立模式;
(5)設置ADC為12位分辨率,啟動掃描,連續(xù)轉(zhuǎn)換,不需要外部觸發(fā);
(6)設置ADC轉(zhuǎn)換通道順序及采樣時間;
(7)使能DMA請求,DMA在AD轉(zhuǎn)換完自動傳輸數(shù)據(jù)到指定的存儲區(qū);
(8)啟動ADC模塊;
(9)軟件使能觸發(fā)ADC轉(zhuǎn)換。
這里需要注意的是,在使用ADC+DMA功能時,如果在啟動ADC轉(zhuǎn)換之后使能DMA,ADC采樣數(shù)據(jù)可能會出現(xiàn)異常。因此建議先配置ADC及DMA相關參數(shù),最后啟動ADC轉(zhuǎn)換。
代碼實現(xiàn)
受篇幅限制,這里只介紹核心的部分代碼,有些變量的設置,頭文件的包含等并沒有涉及到,完整的代碼請參考本課程配套的例程。相關核心代碼實現(xiàn)如下:
(1)ADC宏定義
#defineTEMP_NOFCHANEL3 /*=====================通道1IO======================*/ //PB0ADCIO宏定義,可用杜邦線接3V3或者GND來實驗 #defineTEMP_ADC_GPIO_PORT1GPIOB #defineTEMP_ADC_GPIO_PIN1GPIO_Pin_0 #defineTEMP_ADC_GPIO_CLK1RCC_AHB1Periph_GPIOB #defineTEMP_ADC_CHANNEL1ADC_Channel_8 /*=====================通道2IO======================*/ //PB1ADCIO宏定義,可用杜邦線接3V3或者GND來實驗 #defineTEMP_ADC_GPIO_PORT2GPIOB #defineTEMP_ADC_GPIO_PIN2GPIO_Pin_1 #defineTEMP_ADC_GPIO_CLK2RCC_AHB1Periph_GPIOB #defineTEMP_ADC_CHANNEL2ADC_Channel_9 /*=====================通道3IO======================*/ //PA6ADCIO宏定義,可用杜邦線接3V3或者GND來實驗 #defineTEMP_ADC_GPIO_PORT3GPIOA #defineTEMP_ADC_GPIO_PIN3GPIO_Pin_6 #defineTEMP_ADC_GPIO_CLK3RCC_AHB1Periph_GPIOA #defineTEMP_ADC_CHANNEL3ADC_Channel_6 //ADC序號宏定義 #defineTEMP_ADCADC1 #defineTEMP_ADC_CLKRCC_APB2Periph_ADC1 //ADCDR寄存器宏定義,ADC轉(zhuǎn)換后的數(shù)字值則存放在這里 #defineTEMP_ADC_DR_ADDR((u32)ADC1+0x4c) //ADCDMA通道宏定義,使用DMA傳輸 #defineTEMP_ADC_DMA_CLKRCC_AHB1Periph_DMA2 #defineTEMP_ADC_DMA_CHANNELDMA_Channel_0 #defineTEMP_ADC_DMA_STREAMDMA2_Stream0
定義多個通道進行多通道ADC實驗,并且定義DMA相關配置。
(2)ADC GPIO初始化
staticvoidTemp_ADC_GPIO_Config(void) { GPIO_InitTypeDefGPIO_InitStructure; /*=====================通道1======================*/ RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK1,ENABLE);//使能GPIO時鐘 GPIO_InitStructure.GPIO_Pin=TEMP_ADC_GPIO_PIN1;//配置IO GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不上拉不下拉 GPIO_Init(TEMP_ADC_GPIO_PORT1, GPIO_InitStructure); /*=====================通道2======================*/ RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK2,ENABLE);//使能GPIO時鐘 GPIO_InitStructure.GPIO_Pin=TEMP_ADC_GPIO_PIN2;//配置IO GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不上拉不下拉 GPIO_Init(TEMP_ADC_GPIO_PORT2, GPIO_InitStructure); /*=====================通道3=======================*/ RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK3,ENABLE);//使能GPIO時鐘 GPIO_InitStructure.GPIO_Pin=TEMP_ADC_GPIO_PIN3;//配置IO GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不上拉不下拉 GPIO_Init(TEMP_ADC_GPIO_PORT3, GPIO_InitStructure); }
使用到GPIO時候都必須開啟對應的GPIO時鐘,GPIO用于AD轉(zhuǎn)換功能必須配置為模擬輸入模式。
(3)配置ADC工作模式
staticvoidTemp_ADC_Mode_Config(void) { DMA_InitTypeDefDMA_InitStructure; ADC_InitTypeDefADC_InitStructure; ADC_CommonInitTypeDefADC_CommonInitStructure; //--------------DMAInit結(jié)構(gòu)體參數(shù)初始化------------- //ADC1使用DMA2,數(shù)據(jù)流0,通道0, RCC_AHB1PeriphClockCmd(TEMP_ADC_DMA_CLK,ENABLE);//開啟DMA時鐘 DMA_InitStructure.DMA_PeripheralBaseAddr=TEMP_ADC_DR_ADDR;//外設基址為:ADC數(shù)據(jù)寄存器地址 DMA_InitStructure.DMA_Memory0BaseAddr=(u32)ADC_ConvertedValue;//AD值存儲地址 DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;//數(shù)據(jù)傳輸方向為外設到存儲器 DMA_InitStructure.DMA_BufferSize=TEMP_NOFCHANEL;//緩沖區(qū)大小,指一次傳輸?shù)臄?shù)據(jù)量 DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外設寄存器只有一個,地址不遞增 DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存儲器地址固定 DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;//外設數(shù)據(jù)大小為半字 DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//存儲器數(shù)據(jù)大小也為半字 DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;//循環(huán)傳輸模式 DMA_InitStructure.DMA_Priority=DMA_Priority_High;//DMA傳輸通道優(yōu)先級為高 DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable;//禁止DMAFIFO,使用直連模式 DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_HalfFull;//FIFO大小,F(xiàn)IFO禁止時不用配置 DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; DMA_InitStructure.DMA_Channel=TEMP_ADC_DMA_CHANNEL;//選擇DMA通道,通道存在于流中 DMA_Init(TEMP_ADC_DMA_STREAM, DMA_InitStructure);//初始化DMA流, DMA_Cmd(TEMP_ADC_DMA_STREAM,ENABLE);//使能DMA流 RCC_APB2PeriphClockCmd(TEMP_ADC_CLK,ENABLE);//開啟ADC時鐘 //-------------ADCCommon結(jié)構(gòu)體參數(shù)初始化---------------- ADC_CommonInitStructure.ADC_Mode=ADC_Mode_Independent;//獨立ADC模式 ADC_CommonInitStructure.ADC_Prescaler=ADC_Prescaler_Div4;//時鐘為fpclkx分頻 ADC_CommonInitStructure.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled;//禁止DMA直接訪問模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_20Cycles;//采樣時間間隔 ADC_CommonInit( ADC_CommonInitStructure); //-------------------ADCInit結(jié)構(gòu)體參數(shù)初始化-------------------------- ADC_StructInit( ADC_InitStructure); ADC_InitStructure.ADC_Resolution=ADC_Resolution_12b;//ADC分辨率 ADC_InitStructure.ADC_ScanConvMode=ENABLE;//掃描模式,多通道采集需要 ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//連續(xù)轉(zhuǎn)換 ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_None;//禁止外部邊沿觸發(fā) ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T1_CC1;//外部觸發(fā)通道,使用軟件觸發(fā)時此值隨便賦值即可 ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//數(shù)據(jù)右對齊ADC_InitStructure.ADC_NbrOfConversion=TEMP_NOFCHANEL;//轉(zhuǎn)換通道3個 ADC_Init(TEMP_ADC, ADC_InitStructure); //配置ADC通道轉(zhuǎn)換順序和采樣時間周期 ADC_RegularChannelConfig(TEMP_ADC,TEMP_ADC_CHANNEL1,1,ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(TEMP_ADC,TEMP_ADC_CHANNEL2,2,ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(TEMP_ADC,TEMP_ADC_CHANNEL3,3,ADC_SampleTime_3Cycles); ADC_DMARequestAfterLastTransferCmd(TEMP_ADC,ENABLE);//使能DMA請求 ADC_DMACmd(TEMP_ADC,ENABLE);//使能ADCDMA ADC_Cmd(TEMP_ADC,ENABLE);//使能ADC ADC_SoftwareStartConv(TEMP_ADC);//開始ADC轉(zhuǎn)換,軟件觸發(fā) }
首先,使用DMA_InitTypeDef定義了DMA初始化類型變量,另外使用ADC_InitTypeDef和ADC_CommonInitTypeDef結(jié)構(gòu)體分別定義一個ADC初始化和ADC通用類型變量。
調(diào)用RCC_APB2PeriphClockCmd()開啟ADC時鐘以及RCC_AHB1PeriphClockCmd()開啟DMA時鐘。
對DMA進行必要的配置。首先設置外設基地址就是ADC的規(guī)則數(shù)據(jù)寄存器地址;存儲器的地址就是指定的數(shù)據(jù)存儲區(qū)空間,ADC_ConvertedValue是我們定義的一個全局數(shù)組名,它是一個無符號16位含有3個元素的整數(shù)數(shù)組;ADC規(guī)則轉(zhuǎn)換對應只有一個數(shù)據(jù)寄存器,所以地址不能遞增,而定義的存儲區(qū)是專門用來存放不同通道數(shù)據(jù)的,所以需要自動地址遞增。ADC的規(guī)則數(shù)據(jù)寄存器只有低16位有效,實際存放的數(shù)據(jù)只有12位而已,所以設置數(shù)據(jù)大小為半字大小。ADC配置為連續(xù)轉(zhuǎn)換模式,DMA也設置為循環(huán)傳輸模式。設置好DMA相關參數(shù)后就使能DMA的ADC通道。
接下來使用ADC_CommonInitTypeDef結(jié)構(gòu)體變量ADC_CommonInitStructure來配置ADC為獨立模式、分頻系數(shù)4、20個周期的采樣延遲,并調(diào)用ADC_CommonInit函數(shù)完成ADC通用工作環(huán)境配置。
使用ADC_InitTypeDef結(jié)構(gòu)體變量ADC_InitStructure來配置ADC1為12位分辨率、使能掃描模式、啟動連續(xù)轉(zhuǎn)換、使用內(nèi)部軟件觸發(fā)無需外部觸發(fā)事件、使用右對齊數(shù)據(jù)格式、轉(zhuǎn)換通道為3,并調(diào)用ADC_Init函數(shù)完成ADC1工作環(huán)境配置。
ADC_RegularChannelConfifig函數(shù)用來綁定ADC通道轉(zhuǎn)換順序和采樣時間。分別綁定3個ADC通道引腳并設置相應的轉(zhuǎn)換順序。
ADC_DMARequestAfterLastTransferCmd函數(shù)控制是否使能ADC的DMA請求,如果使能請求,并調(diào)用ADC_DMACmd函數(shù)使能DMA,則在ADC轉(zhuǎn)換完成后就請求DMA實現(xiàn)數(shù)據(jù)傳輸。ADC_Cmd函數(shù)控制ADC轉(zhuǎn)換啟動和停止。
最后使用軟件觸發(fā)調(diào)用ADC_SoftwareStartConvCmd函數(shù)進行使能配置。
(4)Main程序
/**主函數(shù)*/ intmain(void) { Debug_USART_Config(); Temp_Init(); while(1) { ADC_ConvertedValueLocal[0]=(float)ADC_ConvertedValue[0]/4096*(float)3.3; ADC_ConvertedValueLocal[1]=(float)ADC_ConvertedValue[1]/4096*(float)3.3; ADC_ConvertedValueLocal[2]=(float)ADC_ConvertedValue[2]/4096*(float)3.3; printf("rnPB0value=%fVrn",ADC_ConvertedValueLocal[0]); printf("rnPB1value=%fVrn",ADC_ConvertedValueLocal[1]); printf("rnPA6value=%fVrn",ADC_ConvertedValueLocal[2]); Delay(0xffffff); } }
主函數(shù)先調(diào)用Debug_USART_Config函數(shù)配置調(diào)試串口相關參數(shù),接下來調(diào)用Temp_Init函數(shù)進行ADC初始化配置并啟動ADC。配置了DMA數(shù)據(jù)傳輸,它會自動把ADC轉(zhuǎn)換完成后數(shù)據(jù)保存到數(shù)組ADC_ConvertedValue內(nèi),我們只要使用數(shù)組就可以了。經(jīng)過簡單地計算就可以得到每個通道對應的實際電壓。
來源:中科芯MCU
-
存儲器
+關注
關注
38文章
7484瀏覽量
163761 -
adc
+關注
關注
98文章
6495瀏覽量
544460 -
dma
+關注
關注
3文章
560瀏覽量
100544 -
電壓采集
+關注
關注
2文章
21瀏覽量
14219
發(fā)布評論請先 登錄
相關推薦
評論