目錄
ADC做爲一種模數轉換功能,在實際應用中很是經常使用,那麼也經常有各類個性化需求,包括對其轉換開始時間、順序等的要求等。STM32也提供了多種ADC觸發方式來知足要求,包括軟件觸發、定時器觸發和外部觸發等等。數組
本文簡單介紹軟件觸發,重點說明定時器觸發。同時,網上的資料多數是介紹規則組的配置,而本文把規則組和注入組都進行說明,方便你們學習和比較。過程當中也會說起本身的一些思考。函數
1. ADC簡介
1.1ADC通道和轉換時間
STM32f103有3個AD模數轉換器,每一個ADC都有18個通道,能夠測量16個外部和2個內部模擬量。最大理論轉換頻率爲1Mhz,也就是轉換時間爲1us(在 ADCCLK = 14Mhz,採樣週期爲1.5個時鐘週期時,由於總時間是採樣時間+12.5個時鐘週期)。而實際用的時候最短轉換時間約爲1.17 u s u s us,這是由於掛載的時鐘頻率爲72MHz,分頻爲6時ADC時鐘爲12MHz,能夠知足低於14MHz,最大時鐘超過14Mhz會致使ADC轉換準確度下降。學習
每一個ADC的各個通道對應接口如圖:
es5
STM32的ADC是12位精度的,即數據最大爲4096spa
STM32的ADC轉換有兩種通道,規則通道和注入通道,注入通道能夠搶佔式地打斷規則通道的採樣,執行注入通道採樣後,再執行以前的規則通道採樣,和中斷相似。3d
1.2ADC觸發方式、模式等的設置(ADC_CR2)
STM32的ADC能夠由外部事件觸發(例如定時器更新、捕獲,EXTI線)和軟件觸發(即在配置相關寄存器時,直接開啓採樣)。這裏能夠看一下參考手冊裏的說明:
code
在配置轉換的時候有一些模式,這裏進行一下說明:blog
單次模式、連續模式:ADC單通道要求進行一次ADC轉換時配置爲單次模式使能,這樣ADC的這個通道轉換一次後就中止轉換。要求進行連續ADC轉換時配置爲連續模式使能,這樣ADC的這個通道轉換一次後接着進行下一次。
掃描模式:對應多個ADC通道的狀況,每一個通道依次進行轉換。
教程
上面的各類內容,包括觸發方式、轉換模式等的設置,都在ADC_CR2寄存器裏體現,詳情能夠去參考手冊裏看看寄存器的說明。接口
1.3ADC的數據存儲(ADC_DR和ADC_JDRx)
1.3.1 規則通道
ADC在規則通道存儲數據時,只有一個數據寄存器ADC_DR
寄存器中有16位來存放數據,而ADC精度是12位,因此涉及到數據左或右u對齊的問題。
在連續屢次ADC轉換時,因爲只有一個數據寄存器,規則通道的數據會被下一次轉換的數據覆蓋,因此ADC經常和DMA一塊兒使用,利用DMA模式將轉換的數據,傳輸在一個數組中,程序對數組讀操做就能夠獲得轉換的結果。
1.3.2 注入通道
注入通道的每一個通道都有本身的數據寄存器,不過一樣涉及到數據對齊方式的設置。
1.4工做流程
STM32的ADC在單次轉換模式下,只執行一次轉換,首先要置爲ADC_CR2 寄存器的ADON 位,該模式能夠經過軟件觸發啓動,也能夠經過外部觸發啓動(均適用於規則通道和注入通道),這時CONT 位爲0。以規則通道爲例,轉換開始後,SWSTART位(開始規則通道轉換位)自動清除,一旦所選擇的通道轉換完成,轉換結果將被存在ADC_DR 寄存器,EOC (轉換結束)標誌將被置位,若是設置了EOCIE ,則會產生中斷。而後ADC將中止,直到下次啓動。
2 軟件觸發方式
軟件觸發方式是最基本的ADC觸發方式,這裏我用規則組來作。配置的時候配置成不使用外部觸發,而且在單次轉換下,每次轉換時都須要對啓動規則組轉換對應的位置位即調用對應的函數,這個後面結合着代碼來講。這裏我把ADC部分的配置貼上來。
void Adc_Init() { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE ); //使能ADC1通道時鐘 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分頻因子爲6 //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入引腳 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //復位ADC1,將外設 ADC1 的所有寄存器重設爲缺省值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工做模式:ADC1和ADC2工做在獨立模式 ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //轉換由軟件而不是外部觸發啓動 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數據右對齊 ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規則轉換的ADC通道的數目 ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器 //DMACmd(ADC1, ENABLE);//開啓ADC的DMA支持 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_ResetCalibration(ADC1); //使能復位校準 while(ADC_GetResetCalibrationStatus(ADC1)); //等待復位校準結束 ADC_StartCalibration(ADC1); //開啓AD校準 while(ADC_GetCalibrationStatus(ADC1)); //等待校準結束 }
以上就是單通道、軟件觸發模式下規則組的配置狀況,基本說明在註釋裏都包括了。那麼配置完畢之後,何時開始一次轉換呢?咱們看調用的函數
u16 Get_Adc(u8 ch) { ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5); ADC_SoftwareStartConvCmd(ADC1,ENABLE); while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }
咱們每調用一次這個函數,就執行一次ADC轉換,並返回轉換的結果值。那麼咱們來分析一下這個函數裏的語句:
ADC_RegularChannelConfig這個函數決定了在多通道採樣的狀況下,對每一個通道的採樣順序、採樣時間的配置,ch即通道名,1是第一個進行轉換。
ADC_SoftwareStartConvCmd這個函數對寄存器ADC_CR2的SWSTART位進行操做,執行完此句之後就真正開始一次ADC轉換。
仔細思考的一些同窗可能會有一些疑惑,好比ADC_SoftwareStartConvCmd函數使能和ADC_Cmd是什麼關係?狀況是這樣的,前者操做的是ADC_CR2的SWSTART位,後者操做的是ADC_CR2的ADON位,後者至關於電源,當ADON低位時,操做SWSTART位無效;當ADON高位時,置SWSTART位能夠開啓一次轉換。
其實這裏我在實驗的時候遇到了兩個問題,一個是執行完ADC_SoftwareStartConvCmd這條語句後寄存器被置位從而執行轉換,可是卻沒有將對應的位清零的語句,那麼爲何只執行了一次而不是反覆在轉換呢?另外一個問題是當轉換完成後EOC被置位,一樣沒有該位清零的語句,那麼這個轉換完成位就一直是高位,後面再判斷的時候就老是轉換完成了,這顯然是不合邏輯的,問題出在哪裏呢?
這個問題我上網查了不少教程和諮詢,都沒有被說起,通過了很長時間的思索之後,最後在參考手冊的寄存器描述裏找到了很是細節的答案,手冊裏是這麼描述的:
第一行寫到,轉換開始後硬件立刻清除此位。也就是說爲了讓轉換開始而置的位在轉換一開始就被清楚了,方便後面的配置。就讓我豁然開朗。
對於第二個問題,在參考手冊裏也找到了答案:
EOC轉換結束標誌位在讀取本次ADC轉換結果數據的時候被自動清除,這樣一樣有利於後面程序的執行。
3定時器TRGO觸發ADC
3.1使用規則組配置
在規則組配置過程時,ADC配置部分基本內容和軟件觸發相同,僅有下面這些改動
- 觸發方式修改:ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; 也就是把本來的ADC_ExternalTrigConv_None改掉
- 使能外部觸發: ADC_ExternalTrigConvCmd(ADC1,ENABLE);
- 同時我發現,這時不用配置ADC_SoftwareStartConvCmd就也能夠正常工做了,我不太清除這個函數究竟是使能軟件轉換開啓的仍是使能整個規則組轉換開啓的,如今傾向於前者,有比較清楚的大佬能夠給解釋一下。
而後就是配置定時器的內容了,注意選擇讓定時去產生更新觸發。
整個配置部分代碼以下:
void Adc_Init() { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE ); //使能ADC1通道時鐘 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分頻因子改成6 //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模擬輸入引腳 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //復位ADC1,將外設 ADC1 的所有寄存器重設爲缺省值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工做模式:ADC1和ADC2工做在獨立模式 ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC數據右對齊 ADC_InitStructure.ADC_NbrOfChannel = 1; //順序進行規則轉換的ADC通道的數目 ADC_Init(ADC1, &ADC_InitStructure); //根據ADC_InitStruct中指定的參數初始化外設ADCx的寄存器 ADC_ExternalTrigConvCmd(ADC1,ENABLE); // ADC_DMACmd(ADC1, ENABLE);//開啓ADC的DMA支持 ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_239Cycles5); ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_ResetCalibration(ADC1); //使能復位校準 while(ADC_GetResetCalibrationStatus(ADC1)); //等待復位校準結束 ADC_StartCalibration(ADC1); //開啓AD校準 while(ADC_GetCalibrationStatus(ADC1)); //等待校準結束 // ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的軟件轉換啓動功能 } void TIM3_init(u16 arr,u16 psc) { NVIC_InitTypeDef NVIC_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器週期的值 80K TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來做爲TIMx時鐘頻率除數的預分頻值 不分頻 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); //TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE ); //使能指定的TIM3中斷,容許更新中斷 // TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清中斷以避免一啓用中斷後當即產生中斷 TIM_Cmd(TIM3, ENABLE); //使能TIM1 }
而後調用ADC_GetConversionValue(ADC1)函數就能夠獲得ADC轉換的值了。經過配置定時器的arr和psc值,能夠實現任意時間間隔的ADC轉換。
這裏要注意的一個點就是,若是你不須要定時器中斷,那就不要使能定時器中斷,不然你使能了中斷卻又沒有寫中斷服務函數的話,程序會跑飛。我看不少人分享的代碼裏都使能了定時器中斷但也不用,實在不能理解。
3.2使用注入組配置
通常來講,用到注入組了確定是以前已經有了規則組了,否則就不必讓它做爲注入組了。在規則組和注入組混合使用的狀況下,規則組的配置沒有任何變化,注入組的配置以下:
ADC_InjectedSequencerLengthConfig(ADC1, 2); //注入組有兩個通道 ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1,ADC_SampleTime_13Cycles5); ADC_InjectedChannelConfig(ADC1, ADC_Channel_6, 2,ADC_SampleTime_13Cycles5);//對兩個注入組的通道進行設置 ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_TRGO); ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE); //使能 ADC1的經外部觸發啓動注入組轉換功能 //ADC_ITConfig(ADC1,ADC_IT_JEOC,ENABLE); //注入組ADC轉換結束的中斷 ADC_AutoInjectedConvCmd(ADC1, DISABLE); //使能或者失能指定 ADC 在規則組轉化後自動開始注入組轉換
若是是軟件觸發方式的注入組,就把觸發方式ADC_ExternalTrigInjectedConvConfig改變一下,同時不用使能外部觸發啓動功能,而使能ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE)便可。
同時也別忘了,每一個通道對應的GPIO端口也要進行初始化配置。
在讀取注入組的轉換數據時,使用的函數變成了ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
它包括了兩個入口參數,一個是ADCx,另外一個是注入通道的順序序號,這時由於注入組每一個通道都有本身的數據寄存器,因此能夠讀指定通道的數據。
以上就是關於ADC觸發部分介紹的內容了,但願你們讀了會有些收穫!
最後參考了一些別人的總結,列出了ADC部分的各類函數
ADC_DeInit 將外設 ADCx 的所有寄存器重設爲缺省值 ADC_Init 根據 ADC_InitStruct 中指定的參數初始化外設 ADCx 的寄存器 ADC_StructInit 把 ADC_InitStruct 中的每個參數按缺省值填入 ADC_Cmd 使能或者失能指定的 ADC ADC_DMACmd 使能或者失能指定的 ADC 的 DMA 請求 ADC_ITConfig 使能或者失能指定的 ADC 的中斷 ADC_ResetCalibration 重置指定的 ADC 的校準寄存器 ADC_GetResetCalibrationStatus 獲取 ADC 重置校準寄存器的狀態 ADC_StartCalibration 開始指定 ADC 的校準程序 ADC_GetCalibrationStatus 獲取指定 ADC 的校準狀態 ADC_SoftwareStartConvCmd 使能或者失能指定的 ADC 的軟件轉換啓動功能 ADC_GetSoftwareStartConvStatus 獲取 ADC 軟件轉換啓動狀態 ADC_DiscModeChannelCountConfig 對 ADC 規則組通道配置間斷模式 ADC_DiscModeCmd 使能或者失能指定的 ADC 規則組通道的間斷模式 ADC_RegularChannelConfig 設置指定 ADC 的規則組通道,設置它們的轉化順序和採樣時間 ADC_ExternalTrigConvConfig 使能或者失能 ADCx 的經外部觸發啓動轉換功能 ADC_GetConversionValue 返回最近一次 ADCx 規則組的轉換結果 ADC_GetDuelModeConversionValue 返回最近一次雙 ADC 模式下的轉換結果 ADC_AutoInjectedConvCmd 使能或者失能指定 ADC 在規則組轉化後自動開始注入組轉換 ADC_InjectedDiscModeCmd 使能或者失能指定 ADC 的注入組間斷模式 ADC_ExternalTrigInjectedConvConfig 配置 ADCx 的外部觸發啓動注入組轉換功能 ADC_ExternalTrigInjectedConvCmd 使能或者失能 ADCx 的經外部觸發啓動注入組轉換功能 ADC_SoftwareStartinjectedConvCmd 使能或者失能 ADCx 軟件啓動注入組轉換功能 ADC_GetsoftwareStartinjectedConvStatus 獲取指定 ADC 的軟件啓動注入組轉換狀態 ADC_InjectedChannleConfig 設置指定 ADC 的注入組通道,設置它們的轉化順序和採樣時間 ADC_InjectedSequencerLengthConfig 設置注入組通道的轉換序列長度 ADC_SetinjectedOffset 設置注入組通道的轉換偏移值 ADC_GetInjectedConversionValue 返回 ADC 指定注入通道的轉換結果 ADC_AnalogWatchdogCmd 使能或者失能指定單個/全體,規則/注入組通道上的模擬看門狗 ADC_AnalogWatchdongThresholdsConfig 設置模擬看門狗的高/低閾值 ADC_AnalogWatchdongSingleChannelConfig對單個 ADC 通道設置模擬看門狗 ADC_TampSensorVrefintCmd 使能或者失能溫度傳感器和內部參考電壓通道 ADC_GetFlagStatus 檢查制定 ADC 標誌位置 1 與否 ADC_ClearFlag 清除 ADCx 的待處理標誌位 ADC_GetITStatus 檢查指定的 ADC 中斷是否發生 ADC_ClearITPendingBit 清除 ADCx 的中斷待處理位