前言
學習了之後才知道原來STM32F03自帶的ADC功能還是挺強大的,10位ADC,一次可以掃描16個通道。還可以使用DMA來讀取轉換結果,最重要的是不用加ADC外設了。
ADC知識點
多的就不說了,手冊上寫的也是很詳細的(不會真有人學習STM32不看手冊把!不會吧!不會吧!不會吧!)
點這裡下載下傳手冊 提取碼:x49i
1.STM32F103有三個ADC外設分别是ADC.ADC2和ADC3
2.ADC一共有18個通道 其中:ADC1的模拟輸入通道16和通道17在晶片内部分别連到了溫度傳感器和VREFINT。ADC2的模拟輸入通道16和通道17在晶片内部連到了VSS。ADC3模拟輸入通道9、14、15、16、17與Vss相連。其餘的通道都是共用GPIO的。
3.ADC轉換時有兩種轉換方式分組,分别是,規則轉換組和注入轉換組。轉換時都是按照這兩個分組進行的。我們正常使用的是規則轉換,注入轉換就相當于一個中斷,在規則轉換時,如果注入組被觸發,就會先轉換注入組裡的通道,轉換完成後接着轉換規則組。也就是說注入組的轉換優先級更高。
4.ADC各個通道的轉換模式有:單次、連續、掃描或間斷模式。規則組和注入組都可以設定成這四種模式。
單次模式:隻執行單次轉換.按規則組(或是注入組)裡的轉換順序逐個通道轉換一次直到最後一個通道轉換完成後結束。軟體觸發隻适用規則組,
連續模式:連續執行轉換,和單次模式同理i,隻是最後一個通道轉換完成後不結束開始新的一輪轉換。注入組是不會連續轉換的,因為注入隻是規則組的中途插入轉換(中斷),如果一直連續轉換就沒意義了。
掃描模式:打開掃描模式就是多通道轉換,根據規則組(或是注入組)裡規定的轉換順序進行轉換,關閉掃描模式就是單通道轉換,隻轉換規則組(或是注入組)裡轉換順序為1的通道。
間斷模式:把規則組(或是注入組)分成若幹個子分組進行轉換,子分組的通道數可以通過寄存器設定最多8個。注意注入組是不可以設定的,通道數隻能為1,按注入組順序逐個轉換。
接下來看代碼配置這裡隻用标準庫代碼為例
代碼解析
打開ADC時鐘:我這裡隻用到ADC1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //打開ADC1時鐘
再打開用到的通道對應GPIO的時鐘,然後配置GPIO為複用輸入模式,比如配置通道5
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA,&GPIO_InitStructure);
接下來配置ADC
ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式(是否啟用雙ADC模式) 使用獨立模式
ADC1_InitStructure.ADC_ScanConvMode = ENABLE; //掃描模式(單通道與多通道) 使能掃描模式
ADC1_InitStructure.ADC_ContinuousConvMode = ENABLE; //連續模式(連續模式或是單次模式) 使能連續模式
ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //規則組觸發啟動轉換方式 配置軟體觸發
ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //轉換結果資料對齊方式 右對齊
ADC1_InitStructure.ADC_NbrOfChannel = 2; //規則組通道數(範圍1到16) 2個通道
ADC_Init(ADC1, &ADC1_InitStructure); //設定參數
上面隻是完成了一部配置設定置還不足以進行ADC轉換,可以了解為通用配置,下面進行細節配置
要進行校準的話先配置
ADC_StartCalibration(ADC1); //開始ADC1的校準程式
while(ADC_GetCalibrationStatus(ADC1)!=SET); //判斷校準是否結完成
配置規則組或是注入組轉換順序表 以内部溫度傳感器為例
//規則組
ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_55Cycles5); //規則組序号2配置和設定采樣時間 設定為通道17
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5); //規則組序号1配置和設定采樣時間 設定為通道16
//注入組
ADC_InjectedChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_55Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5);
//還要配合
ADC_InjectedSequencerLengthConfig(ADC1,2); //設定注入組序列長度 範圍1到4
DMA請求有需要就打開 随便使能内部CPU溫度傳感器
ADC_DMACmd(ADC1, ENABLE); //使能ADC使用DMA請求
ADC_TempSensorVrefintCmd(ENABLE); //使能内部溫度傳感器和内部參考電壓
然後是啟動規則或是注入轉換,注入組是不可以單獨啟動的,我們一開始啟動的是規則組,然後才可以通過外部觸發或是自動在規則轉換完成之後啟動。
//這裡以軟體觸發啟動規則組
ADC_Cmd(ADC1, ENABLE); //ADC使能 ADC1使能一定要在啟動之前
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能軟體觸發啟動規則組轉換
//軟體啟動注入組
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartInjectedConvCmd(ADC1,ENABLE);
//外部觸發啟動大同小異 隻是注入組在使能之前要調用外部觸發啟動配置函數 注意是配置函數不是使能函數
ADC_ExternalTrigConvCmd(ADC1,ENABLE); //規則胡外部觸發啟動使能 配置在一開始的通用配置裡設定的
ADC_ExternalTrigInjectedConvConfig(ADC1,ADC_ExternalTrigInjecConv_T1_CC4); //配置注入組外部觸發啟動 這裡隻是舉個例子
ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE); //使能注入組外部觸發啟動
下面看下ADC的中斷,和配置序列槽中斷的方法是一樣的。這裡要注意下:ADC1和ADC2的中斷映射在同一個中斷向量上,而ADC3的中斷有自己的中斷向量。意思是ADC1和ADC2共用同一個中斷。規則和注入組轉換結束時能産生中斷,當模拟看門狗狀态位被設定時也能産生中斷。它們都有獨立的中斷使能位。中斷源:
1.ADC_IT_EOC 規則組中斷
2.ADC_IT_JEOC 注入組中斷
3.ADC_IT_AWD 看門狗中斷
NVIC_InitTypeDef NVIC_InitStructure; //定義中斷初始化結構體變量
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); //使能ADC1中斷并設定中斷源
ADC_ClearITPendingBit(ADC2, ADC_IT_EOC); //清除ADC ADC_IT_EOC中斷标志
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; //指定序列槽1的中斷
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能序列槽1中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //中斷優先級
NVIC_Init(&NVIC_InitStructure); //初始化中斷
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //中斷優先級分組
接下來配置DMA: 外設位址就是ADC轉換後的資料寄存器 DR,在标準庫裡為 ADC1->DR然後要把這個位址轉換為32位的無符号類型。記憶體位址也是同理,可以定義一個16位的無符号數組,數組的元素個數可以是通道數。
DMA1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); //設定外設位址
DMA1_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Internal1; //記憶體位址
DMA1_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //設定傳輸方向 這裡設定設定外設為傳輸來源
DMA1_InitStructure.DMA_BufferSize = DMA_MemoryDataSize_HalfWord; //設定指定DMA通道的緩存大小 這裡設定16位半字
DMA1_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設位址是否遞增 這裡設定不遞增
DMA1_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //記憶體位址是否遞增 這裡設定遞增
DMA1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外設資料寬度 16位
DMA1_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //記憶體資料寬度 16位
DMA1_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA工作模式 設定為循環模式
DMA1_InitStructure.DMA_Priority = DMA_Priority_Low; //設定通道的軟體優先級 這裡設定低優先級
DMA1_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA的記憶體到記憶體傳輸設定 這裡選擇不設定
DMA_Init(DMA1_Channel1,&DMA1_InitStructure); //結構體初始化
DMA_SetCurrDataCounter(DMA1_Channel1,2); //設定DMA1的通道1 傳輸數量
DMA_Cmd(DMA1_Channel1,ENABLE); //使能DMA1的通道1 因為ADC1和2的DMA請求在DMA1的通道1
這裡看下具體配置,以規則轉換後自動啟動注入
用不到的地方我就注釋了
uint16_t Internal1[2]; //存放ADC的轉換結果
void Init_ADC1_IN17_IN18() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //打開DMA1時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //打開ADC1時鐘
ADC_InitTypeDef ADC1_InitStructure; //ADC1初始化結構體
DMA_InitTypeDef DMA1_InitStructure; //DMA1初始化結構體
NVIC_InitTypeDef NVIC_InitStructure; //定義中斷初始化結構體變量
DMA1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); //設定外設位址
DMA1_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&Internal1; //記憶體位址
DMA1_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //設定傳輸方向 這裡設定設定外設為傳輸來源
DMA1_InitStructure.DMA_BufferSize = DMA_MemoryDataSize_HalfWord; //設定指定DMA通道的緩存大小 這裡設定16位半字
DMA1_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外設位址是否遞增 這裡設定不遞增
DMA1_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //記憶體位址是否遞增 這裡設定遞增
DMA1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外設資料寬度 16位
DMA1_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //記憶體資料寬度 16位
DMA1_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA工作模式 設定為循環模式
DMA1_InitStructure.DMA_Priority = DMA_Priority_Low; //設定通道的軟體優先級 這裡設定低優先級
DMA1_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA的記憶體到記憶體傳輸設定 這裡選擇不設定
DMA_Init(DMA1_Channel1,&DMA1_InitStructure); //結構體初始化
DMA_SetCurrDataCounter(DMA1_Channel1,2); //設定DMA1的通道1 傳輸數量
DMA_Cmd(DMA1_Channel1,ENABLE); //使能DMA1的通道1 因為ADC1和2的DMA請求在DMA1的通道1
ADC1_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC模式 使用獨立模式
ADC1_InitStructure.ADC_ScanConvMode = ENABLE; //掃描模式 使能掃描模式
ADC1_InitStructure.ADC_ContinuousConvMode = ENABLE; //連續模式 使能連續模式
ADC1_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //觸發啟動轉換方式 配置軟體觸發
ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //轉換結果資料對齊方式 右對齊
ADC1_InitStructure.ADC_NbrOfChannel = 2; //規則組通道數(範圍1到16) 2個通道
ADC_Init(ADC1, &ADC1_InitStructure); //設定參數
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); //使能ADC1中斷并設定中斷源
ADC_ClearITPendingBit(ADC2, ADC_IT_EOC); //清除ADC ADC_IT_EOC中斷标志
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; //指定序列槽1的中斷
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能序列槽1中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //中斷優先級
NVIC_Init(&NVIC_InitStructure); //初始化中斷
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //中斷優先級分組
ADC_StartCalibration(ADC1); //開始ADC1的校準程式
while(ADC_GetCalibrationStatus(ADC1)!=SET); //判斷校準是否結完成
ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_55Cycles5); //規則組序号2配置和設定采樣時間 設定為通道17
ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_55Cycles5); //規則組序号1配置和設定采樣時間 設定為通道16
//ADC_DMACmd(ADC1, ENABLE); //使能ADC使用DMA請求
ADC_TempSensorVrefintCmd(ENABLE); //使能内部溫度傳感器和内部參考電壓
//ADC_ExternalTrigConvCmd(ADC1,ENABLE); //規則胡使能外部觸發啟動
//ADC_AutoInjectedConvCmd(ADC1, ENABLE); //規則轉換後自動進行注入轉換
//ADC_ExternalTrigInjectedConvConfig(ADC1,ADC_ExternalTrigConv_None); //注入組外部觸發啟動配置
//ADC_InjectedSequencerLengthConfig(ADC1,1); //設定注入組序列長度
//ADC_InjectedChannelConfig(ADC1, ADC_Channel_17, 1, ADC_SampleTime_55Cycles5); //注入組轉換順序配置
//ADC_InjectedChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_55Cycles5); //注入組轉換順序配置
ADC_Cmd(ADC1, ENABLE); //ADC使能
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能軟體觸發啟動規則組轉換
//ADC_SoftwareStartInjectedConvCmd(ADC1,ENABLE); //軟體觸發啟動注入組轉換
//ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE); //使能注入組外部觸發啟動配置
}
最後就是擷取ADC的轉換結果了:如果采用DMA的話,用的時候直接讀取數組裡的值就可以了,可以了解為DMA自動幫我們指派好了我們直接用就可以了。ADC1和ADC2的中斷換數固定寫法void ADC1_2_IRQHandler(void),使用中斷要注意隻有當規則組或者是注入組裡的通道全部轉換完成後才産生中斷。使用一般使用ADC都是開啟DMA傳輸。
void ADC1_2_IRQHandler(void){
if(ADC_GetITStatus(ADC1, ADC_IT_EOC)==SET){ //檢查中斷标志是否置位
Internal1[0]=ADC_GetConversionValue(ADC1); //把轉換結果存儲到數組中
}
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); //清除中斷标志
}
之後就是資料的換算了。