// ADC 宏定義
#ifndef __BSP_ADC_H #define __BSP_ADC_H #include "stm32f4xx.h" // ADC GPIO 宏定義 #define RHEOSTAT_ADC_GPIO_PORT GPIOC #define RHEOSTAT_ADC_GPIO_PIN GPIO_Pin_3 #define RHEOSTAT_ADC_GPIO_CLK RCC_AHB1Periph_GPIOC // ADC 序號宏定義 #define RHEOSTAT_ADC1 ADC1 #define RHEOSTAT_ADC1_CLK RCC_APB2Periph_ADC1 #define RHEOSTAT_ADC2 ADC2 #define RHEOSTAT_ADC2_CLK RCC_APB2Periph_ADC2 #define RHEOSTAT_ADC3 ADC3 #define RHEOSTAT_ADC3_CLK RCC_APB2Periph_ADC3 #define RHEOSTAT_ADC_CHANNEL ADC_Channel_13 // ADC CDR寄存器宏定義,ADC轉換后的數字值則存放在這里 #define RHEOSTAT_ADC_CDR_ADDR ((uint32_t)0x40012308) // ADC DMA 通道宏定義,這里我們使用DMA傳輸 #define RHEOSTAT_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define RHEOSTAT_ADC_DMA_CHANNEL DMA_Channel_0 #define RHEOSTAT_ADC_DMA_STREAM DMA2_Stream0 //#define RHEOSTAT_ADC_DR_ADDR ((u32)ADC3 + 0X4C)//模數轉 void Rheostat_Init(void); #endif /* __BSP_ADC_H */
// ADC 初始化
#include "./adc/bsp_adc.h" __IO uint32_t ADC_ConvertedValue[3]; static void Rheostat_ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能 GPIO 時鍾 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure); } static void Rheostat_ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // ------------------DMA Init 結構體參數 初始化-------------------------- // ADC1使用DMA2,數據流0,通道0,這個是手冊固定死的 // 開啟DMA時鍾 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE); // 外設基址為:ADC 數據寄存器地址 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_CDR_ADDR; // 存儲器地址,實際上就是一個內部SRAM的變量 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue; // 數據傳輸方向為外設到存儲器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 緩沖區大小為,指一次傳輸的數據量 DMA_InitStructure.DMA_BufferSize = 3; // 外設寄存器只有一個,地址不用遞增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 存儲器地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // // 外設數據大小為半字,即兩個字節 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 存儲器數據大小也為半字,跟外設數據大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word; // 循環傳輸模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 傳輸通道優先級為高,當使用一個DMA通道時,優先級設置不影響 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直連模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止時,這個不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 選擇 DMA 通道,通道存在於流中 DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL; //初始化DMA流,流相當於一個大的管道,管道里面有很多通道 DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA流 DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE); // 開啟ADC時鍾 RCC_APB2PeriphClockCmd(RHEOSTAT_ADC1_CLK , ENABLE); RCC_APB2PeriphClockCmd(RHEOSTAT_ADC2_CLK , ENABLE); RCC_APB2PeriphClockCmd(RHEOSTAT_ADC3_CLK , ENABLE); // -------------------ADC common 結構體 參數 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // 獨立ADC模式 ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_Interl; // 時鍾為fpclk x分頻 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接訪問模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2; // 采樣時間間隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 結構體 參數 初始化-------------------------- // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 禁止掃描模式,多通道采集才需要 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 連續轉換 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //禁止外部邊沿觸發 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部觸發通道,本例子使用軟件觸發,此值隨便賦值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //數據右對齊 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //轉換通道 1個 ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(RHEOSTAT_ADC1, &ADC_InitStructure); //--------------------------------------------------------------------------- // 配置 ADC 通道轉換順序為1,第一個轉換,采樣時間為3個時鍾周期 ADC_RegularChannelConfig(RHEOSTAT_ADC1, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_3Cycles); //--------------------------------------------------------------------------- ADC_Init(RHEOSTAT_ADC2, &ADC_InitStructure); // 配置 ADC 通道轉換順序為1,第一個轉換,采樣時間為3個時鍾周期 ADC_RegularChannelConfig(RHEOSTAT_ADC2, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_3Cycles); //--------------------------------------------------------------------------- ADC_Init(RHEOSTAT_ADC3, &ADC_InitStructure); // 配置 ADC 通道轉換順序為1,第一個轉換,采樣時間為3個時鍾周期 ADC_RegularChannelConfig(RHEOSTAT_ADC3, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_3Cycles); // 使能DMA請求 after last transfer (multi-ADC mode) ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); // 使能ADC DMA ADC_DMACmd(RHEOSTAT_ADC1, ENABLE); // 使能ADC ADC_Cmd(RHEOSTAT_ADC1, ENABLE); ADC_Cmd(RHEOSTAT_ADC2, ENABLE); ADC_Cmd(RHEOSTAT_ADC3, ENABLE); //開始adc轉換,軟件觸發 ADC_SoftwareStartConv(RHEOSTAT_ADC1); // ADC_SoftwareStartConv(RHEOSTAT_ADC2); // ADC_SoftwareStartConv(RHEOSTAT_ADC3); } void Rheostat_Init(void) { Rheostat_ADC_GPIO_Config(); Rheostat_ADC_Mode_Config(); }
// 主函數
#include "stm32f4xx.h" #include "./usart/bsp_debug_usart.h" #include "./adc/bsp_adc.h" // ADC1轉換的電壓值通過MDA方式傳到SRAM extern __IO uint32_t ADC_ConvertedValue[3]; // 局部變量,用於保存轉換計算后的電壓值 float ADC_ConvertedValueLocal[3]; static void Delay(__IO uint32_t nCount) //簡單的延時函數 { for(; nCount != 0; nCount--); } /** * @brief 主函數 * @param 無 * @retval 無 */ int main(void) { /*初始化USART 配置模式為 115200 8-N-1,中斷接收*/ Debug_USART_Config(); Rheostat_Init(); printf("RHEOSTAT_ADC_DR_ADDR 的地址 %X\n",RHEOSTAT_ADC_DR_ADDR); while (1) { Delay(0xffffee); ADC_ConvertedValueLocal[0] =(float)((uint16_t)ADC_ConvertedValue[0]*3.3/4096); ADC_ConvertedValueLocal[1] =(float)((uint16_t)ADC_ConvertedValue[1]*3.3/4096); ADC_ConvertedValueLocal[2] =(float)((uint16_t)ADC_ConvertedValue[2]*3.3/4096); printf("\r\n The current AD value = 0x%08X \r\n", ADC_ConvertedValue[0]); printf("\r\n The current AD value = 0x%08X \r\n", ADC_ConvertedValue[1]); printf("\r\n The current AD value = 0x%08X \r\n", ADC_ConvertedValue[2]); printf("\r\n The current ADC1 value = %f V \r\n",ADC_ConvertedValueLocal[0]); printf("\r\n The current ADC2 value = %f V \r\n",ADC_ConvertedValueLocal[1]); printf("\r\n The current ADC3 value = %f V \r\n",ADC_ConvertedValueLocal[2]); } }
掉坑提示:1、三個 ADC 共用一個通道 與數據流 :通道0、數據流0,是不是很奇怪
2、DMA數據的存儲地址為 通用ADC專用地址 為((uint32_t)0x40012308) ADC_CDR
3、DAM傳輸數據大小為一個字即4個字節,分別為ADC1/2/3的高或低16位組合,因為采樣的是同一個通道所以數據值相同,沒有應為數據傳輸先后的影響
4、ADC設置為三重ADC交替模式
5、 // 禁止DMA直接訪問模式
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
查看數據手冊:
6、三個ADC 同時設置成通道轉換順序為1,影響不大
7、 三重模式只需使能 ADC1 的 DMA 通道,ADC軟件觸發三個需要同時使能。