本章結合上一節內容復習更好理解【江協科技STM32】ADC數模轉換器-學習筆記-CSDN博客
一、ADC單通道
?接線圖
ADC初始化??
①RCC開啟時鐘,包括ADC和GPIO的時鐘,另外ADCCLK的分頻器也要配置
②配置GPIO,,把需要用的GPIO配置成模擬輸入模式(GPIO_Mode_AIN模式下GPIO無效,即斷開GPIO,防止GPIO輸入輸出對我模擬電壓造成干擾),所以AIN模式就是ADC專屬模式
?③配置多路開關,把左邊通道接入到右邊的規則組列表里
④配置ADC轉換器(初始化ADC),一個結構體可以配置完ADC轉換器和AD數據寄存器
⑤開關控制,調用ADC_Cmd()函數,開啟ADC?
代碼:?
void AD_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //開啟ADC1的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*設置ADC時鐘*/RCC_ADCCLKConfig(RCC_PCLK2_Div6); //選擇時鐘6分頻,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA0引腳初始化為模擬輸入/*規則組通道配置*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //規則組序列1的位置,配置為通道0/*ADC初始化*/ADC_InitTypeDef ADC_InitStructure; //定義結構體變量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,選擇獨立模式,即單獨使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //數據對齊,選擇右對齊ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部觸發,使用軟件觸發,不需要外部觸發ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //連續轉換,失能,每轉換一次規則組序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE; //掃描模式,失能,只轉換規則組的序列1這一個位置ADC_InitStructure.ADC_NbrOfChannel = 1; //通道數,為1,僅在掃描模式下,才需要指定大于1的數,在非掃描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure); //將結構體變量交給ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC開始運行/*ADC校準*/ADC_ResetCalibration(ADC1); //固定流程,內部有電路會自動執行校準while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}
函數解釋:?
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)?//配置ADC時鐘(ADCCLK)
參數 | 說明 |
RCC_PCLK2 | 定義ADC時鐘分頻器。這個鐘是由APB2時鐘(PCLK2) |
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)//根據指定的參數初始化ADCx外設在ADC_InitStruct
參數 | 說明 |
ADCx | 其中x可為1、2或3,選擇ADC外設 |
ADC_InitStruct | 指向ADC_InitTypeDef結構體的指針指定ADC外設的配置信息 |
?ADC初始化結構定義
參數 | 說明 |
ADC_Mode | 將ADC配置為獨立或操作雙模式 |
ADC_ScanConvMode | 指定轉換是在Scan(多通道)模式還是Single(單通道)模式下執行,可選“ENABLE”或“DISABLE”,“ENABLE”是掃描,“DISABLE”是非掃描? |
ADC_ContinuousConvMode | 指定轉換是在連續模式還是單模式下執行,可選“ENABLE”或“DISABLE”,“ENABLE”是連續轉換模式,“DISABLE”是單次轉換模式 |
ADC_ExternalTrigConv | 定義用于啟動模擬的外部觸發器對常規信道進行數字轉換 |
ADC_DataAlign | 指定ADC數據是左對齊還是右對齊 |
ADC_NbrOfChannel | 指定要轉換的ADC通道的數量使用順序器為常規通道組,取值范圍為1 ~ 16 |
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)//啟用或禁用所選ADC軟件啟動轉換
參數 | 說明 |
ADCx | 其中x可以是1、2或3來選擇ADC外設 |
NewState | 所選ADC軟件啟動轉換的新狀態,取值為:ENABLE或DISABLE |
void ADC_ResetCalibration(ADC_TypeDef* ADCx);//重置所選ADC校準寄存器
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);//獲取所選ADC復位校準寄存器狀態
void ADC_StartCalibration(ADC_TypeDef* ADCx);啟動選定的ADC校準過程
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);獲取所選ADC校準狀態
使用方法:固定流程,內部有電路會自動執行校準
?? ?ADC_ResetCalibration(ADC1);?? ??? ??? ??? ??? ??? ??? ??? ?
?? ?while (ADC_GetResetCalibrationStatus(ADC1) == SET);//如果SET(1)一直循環,如果RESET(0)校準完成,跳出循環
?? ?ADC_StartCalibration(ADC1);
?? ?while (ADC_GetCalibrationStatus(ADC1) == SET);?
?FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)//檢查是否設置了指定的ADC標志
作用:想知道轉換是否結束就調用此函數,后一個參數給EOC標志位,判斷是不是置1,如果轉換結束,跳出循環,標志位置1;否則一直在循環中轉換。
參數 | 說明 |
ADCx | 其中x可以是1、2或3來選擇ADC外設 |
ADC_FLAG | 指定要檢查的標志 |
返回值:ADC_FLAG的新狀態(SET或RESET)?1或0
返回的SET或RESET和轉換是否完成的對應關系如下:
?具體等待多長時間:用最后一條公式算
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)//為所選ADC配置其對應的常規通道排序器和它的采樣時間
參數 | 說明 |
ADCx | 其中x可以是1、2或3來選擇ADC外設 |
Rank | 常規組測序器中的秩。取值范圍為1 ~ 16 |
ADC_Channel | 要配置的ADC通道 |
ADC_SampleTime | 要為所選通道設置的采樣時間值 |
?Rank對應:?
?ADC_Channel:?
?ADC_SampleTime:
?uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)//返回常規通道的最后一次ADCx轉換結果數據
寫入參數:?可以是1、2或3來選擇ADC外設
返回值:Data轉換值
?獲取AD轉換的值
uint16_t AD_GetValue(void)
{ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發AD轉換一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC標志位,即等待AD轉換結束return ADC_GetConversionValue(ADC1); //讀數據寄存器,得到AD轉換的結果
}
?main函數
讀取電壓值:只需要對數據進行一個線性變換,用AD值?/ 4095 * 3.3?,就可以將0~4095轉換為0~3V電壓,這里的ADValue是個整數值,在除以4095后會舍棄掉小數值,會導致計算錯誤,所以要將ADValue做一個類型強制轉換。
?(uint16_t)(Voltage * 100) % 100, 2) //Voltage*100擴大100倍,1.23變成123,然后對100取余,123對100取余就是23,這就把小數部分取出來了。由于浮點數不能取余,所以要做強制類型轉換變成整數。
uint16_t ADValue; //定義AD值變量
float Voltage; //定義電壓變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化AD_Init(); //AD初始化/*顯示靜態字符串*/OLED_ShowString(1, 1, "ADValue:");OLED_ShowString(2, 1, "Voltage:0.00V");while (1){ADValue = AD_GetValue(); //獲取AD轉換的值Voltage = (float)ADValue / 4095 * 3.3; //將AD值線性變換到0~3.3的范圍,表示電壓OLED_ShowNum(1, 9, ADValue, 4); //顯示AD值OLED_ShowNum(2, 9, Voltage, 1); //顯示電壓值的整數部分OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2); //顯示電壓值的小數部分Delay_ms(100); //延時100ms,手動增加一些轉換的間隔時間}
}
?二、ADC多通道
ADC初始化?
也是用單次轉換非掃描模式,只需要在每次觸發轉換之前手動修改列表第一個位置的通道就可以了。比如在第一次轉換寫入通道0,之后觸發、等待、讀值;然后在第二次轉換寫入通道1,之后觸發、等待、讀值;然后在第三次轉換寫入通道2,之后觸發、等待、讀值;在轉換前指定一下通道,在啟動轉換,就可以實現多通道功能了。
void AD_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //開啟ADC1的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘/*設置ADC時鐘*/RCC_ADCCLKConfig(RCC_PCLK2_Div6); //選擇時鐘6分頻,ADCCLK = 72MHz / 6 = 12MHz/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); //將PA0、PA1、PA2和PA3引腳初始化為模擬輸入/*不在此處配置規則組序列,而是在每次AD轉換前配置,這樣可以靈活更改AD轉換的通道*//*ADC初始化*/ADC_InitTypeDef ADC_InitStructure; //定義結構體變量ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,選擇獨立模式,即單獨使用ADC1ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //數據對齊,選擇右對齊ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部觸發,使用軟件觸發,不需要外部觸發ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //連續轉換,失能,每轉換一次規則組序列后停止ADC_InitStructure.ADC_ScanConvMode = DISABLE; //掃描模式,失能,只轉換規則組的序列1這一個位置ADC_InitStructure.ADC_NbrOfChannel = 1; //通道數,為1,僅在掃描模式下,才需要指定大于1的數,在非掃描模式下,只能是1ADC_Init(ADC1, &ADC_InitStructure); //將結構體變量交給ADC_Init,配置ADC1/*ADC使能*/ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC開始運行/*ADC校準*/ADC_ResetCalibration(ADC1); //固定流程,內部有電路會自動執行校準while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);
}
獲取AD轉換值?
uint16_t AD_GetValue(uint8_t ADC_Channel)
{ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); //在每次轉換前,根據函數形參靈活更改規則組的通道1ADC_SoftwareStartConvCmd(ADC1, ENABLE); //軟件觸發AD轉換一次while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC標志位,即等待AD轉換結束return ADC_GetConversionValue(ADC1); //讀數據寄存器,得到AD轉換的結果
}
main函數
uint16_t AD0, AD1, AD2, AD3; //定義AD值變量int main(void)
{/*模塊初始化*/OLED_Init(); //OLED初始化AD_Init(); //AD初始化/*顯示靜態字符串*/OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:");while (1){AD0 = AD_GetValue(ADC_Channel_0); //單次啟動ADC,轉換通道0AD1 = AD_GetValue(ADC_Channel_1); //單次啟動ADC,轉換通道1AD2 = AD_GetValue(ADC_Channel_2); //單次啟動ADC,轉換通道2AD3 = AD_GetValue(ADC_Channel_3); //單次啟動ADC,轉換通道3OLED_ShowNum(1, 5, AD0, 4); //顯示通道0的轉換結果AD0OLED_ShowNum(2, 5, AD1, 4); //顯示通道1的轉換結果AD1OLED_ShowNum(3, 5, AD2, 4); //顯示通道2的轉換結果AD2OLED_ShowNum(4, 5, AD3, 4); //顯示通道3的轉換結果AD3Delay_ms(100); //延時100ms,手動增加一些轉換的間隔時間}
}
?代碼源自學習江協科技