第8章
類比訊號轉數位-ADC
8.1 ADC簡介
物理世界的觀測皆為連續的類比訊號,若要將其輸入電腦進行數值運算,則必須轉換為數位訊號。本章將介紹如何使用STM32內部的ADC(Analog to Digital)進行訊號轉換,並使讀者瞭解:
l ADC的使用
l ADC與DMA同步運作方式
ADC轉換流程如下圖所示,單晶片控制外部的類比訊號驅動裝置,需要將類比訊號經由類比/數位轉換器(Analog to Digital Converter)轉成數位訊號後,才能由單晶片處理,如果要將這些數位訊號再轉成類比訊號傳出去時,就需要數位訊號經由數位/類比轉換器(Digital to Analog Converter)轉成類比訊號。
ADC轉換原理分成兩項,取樣保存與量化,如下圖所示:
l 取樣保存(sampling and Holding):按取樣定理對類比信號進行等時間間隔取樣,這些樣值在時間上是離散的值,但在幅度上仍然是連續類比量,如下圖第二張所示,取樣率(Sample rate)越高則訊號越不易失真 。
量化(Quantization):在幅值上再用離散值來表示,量化的位元數越高則解析度越高,如下圖第三張所示,將訊號量化成000到1111的值。
8.2 功能概述
以下為ADC電路圖,ADC類比訊號經由GPIO接腳進入,總共有16個通道,經過ADC多工器連接到Analog to digital converter轉換,Analog to digital converter內部又再細分為注入通道與規則通道,轉換成功的值分別存在規則/注入的暫存器中,規則/注入轉換成功以及Analog watchdog發生時可產生中斷通知,此外,ADC的Clock來自APB2 prescaler。
ADC對應的GPIO腳如下圖所示,每個外部的類比輸入channel都有規定的GPIO接腳,在STM32F10x8 p27可找到這張表格。
如果要由外部輸入類比訊號 ,GPIO的初始設定如下所示:
8.2.1 ADC Channel
STM32 將ADC分成兩個通道組:規則通道和注入通道組。規則通道(Regular channels)相當於運行的程序,最多包含16個通道;注入通道(Injected channels)相當於中斷的程序,最多包含4個通道,當程序正在執行的時候,中斷是用來打斷你的執行順序,因此注入通道的轉換可以打斷規則通道的轉換,在注入通道被轉換完成後規則通道才可以繼續轉換。
舉例說明,假如你在家里的院子内放了5個溫度感測器,室内放了3個溫度感測器;你需要時時刻刻室外溫度,但偶爾你想看看室内的溫度;
因此你可以使用規則通道組循環掃描室外的5個感測器並顯示ADC轉換结果,當你想看室内溫度時,透過一個按钮啟動注入通道组(3個室内感測器)並暫時顯示室内溫度,當你放開這個按鈕後,系统又會回到規則通道組繼續檢測室外溫度。
規則通道的暫存器如以下三張圖所示,ADC_SQRx表示規則序列中的第1~16個轉換,ADC_SQR1中的L[3:0]用於存儲規則序列的長度,其他的SQ13~16則存儲了規則序列中第13~16個通道的編號(0~17)。另外兩個規則序列暫存器同ADC_SQR1。
當規則通道轉換完後,轉換的數據被儲存在16位的ADC_DR暫存器中。
注入通道的暫存器如以下所示,ADC_JSQR表示規則序列中的第1~4個轉換,ADC_JDRx存放注射通道轉換後的值。
8.2.2 ADC watchdog
ADC watchdog的主要用意是,當ADC轉換的模擬電壓低於低閥值或高於高閥值,ADC watchdog中的AWD位就會被設置。這些閥值位於在ADC_HTR和ADC_LTR暫存器的最低12個有效位中。通過設置ADC_CR1暫存器的AWDIE位來產生相應中斷,通過配置ADC_CR1暫存器,模擬看門狗可以作用於1個或多個通道,如下表所示。
8.2.3 ADC Time diagram
如下圖所示,ADC在開始精確轉換前需要一個穩定時間tSTAB。在開始ADC轉換和14個時鐘週期後,EOC標誌被設置,16位ADC數據暫存器包含轉換的結果。
8.2.3 ADC Conversion mode
ADC 轉換模式分為以下幾種:
l Single conversion mode (單次轉換模式):
單次轉換模式下,ADC只執行一次轉換。該模式既可通過設置ADC_CR2暫存器的ADON位(只適用於規則通道)啟動也可通過外部觸發啟動(適用於規則通道或註入通道),這時CONT位為0
l Continuous conversion mode(連續轉換模式):
在連續轉換模式中,當前面ADC轉換一結束馬上就啟動另一次轉換。此模式可通過外部觸發啟動或通過設置ADC_CR2暫存器上的ADON位啟動,此時CONT位是1。
l Scan mode(掃描模式):
掃描模式可通過設置ADC_CR1暫存器的SCAN位來選擇。一旦這個位被設置,ADC掃描所有被ADC_SQRX暫存器(規則通道)或ADC_JSQR(注入通道)選中的所有通道。在每個組的每個通道上執行單次轉換。在每個轉換結束時,同一組的下一個通道被自動轉換。如果設置了CONT位,轉換不會在選擇組的最後一個通道上停止,而是再次從選擇組的第一個通道繼續轉換。
如果設置了DMA位,在每次EOC後,DMA控制器把規則組通道的轉換數據傳輸到SRAM中。而注入通道轉換的數據總是存儲在ADC_JDRx暫存器中。
l Discontinuous mode(間斷模式):
<規則組>
此模式通過設置ADC_CR1暫存器上的DISCEN。它可以用來執行一個短序列的n次轉換(n<=8),此轉換是ADC_SQRx暫存器所選擇的轉換序列的一部分,n由ADC_CR1暫存器的DISCNUM[2:0]位給出。
一個外部觸發信號可以啟動ADC_SQRx暫存器中描述的下一輪n 次轉換,直到此序列所有的轉換完成為止。總的序列長度由ADC_SQR1暫存器的L[3:0]定義。
舉例:
n=3,被轉換的通道= 0, 1, 2, 3, 6, 7, 9, 10
第一次觸發:轉換的序列為 0,1,2
第二次觸發:轉換的序列為 3,6,7
第三次觸發:轉換的序列為9,10,並產生EOC事件
第四次觸發:轉換的序列 0,1,2
<注入組>
此模式通過設置ADC_CR1暫存器的JDISCEN。在一個外部觸發事件後,給模式按序轉換ADC_JSQR暫存器中選擇的序列。
一個外部觸發信號可以啟動ADC_JSQR暫存器選擇的下一個通道序列的轉換,直到序列中所有的轉換完成為止。總的序列長度由ADC_JSQR暫存器的JL[1:0]位定義。
例子:
n=1,被轉換的通道 = 1,2,3
第一次觸發:通道1被轉換
第二次觸發:通道2被轉換
第三次觸發:通道3被轉換,並且產生EOC和JEOC事件
第四次觸發:通道1被轉換
l Dual ADC mode
在有2個或以上ADC的器件中,可以使用雙ADC模式,在雙ADC模式裡,根據ADC1_CR1暫存器中DUALMOD[2:0]位所選的模式,轉換的啟動可以是ADC1主和ADC2從的交替觸發或同時觸發,共有六種可能的模式:
– Injected simultaneous mode (同時注入模式)
– Regular simultaneous mode(同時規則模式)
– Fast interleaved mode(快速交替模式)
– Slow interleaved mode(慢速交替模式)
– Alternate trigger mode(交替模式)
– Independent mode(獨立模式)
8.2.4 ADC Calibration
ADC內部的校正模式,校正可大幅減少內部精準度的誤差,在校準期間,每個電容器上都會計算出一個誤差修正碼(數字值),這個碼用於消除在隨後的轉換中每個電容器上產生的誤差,透過ADC_CP2暫存器中的CAL位來啟動校正,校正結束後,CAL位會被硬體復位。
8.2.5 ADC alignment
ADC_CR2寄存器中的ALIGN位選擇轉換後數據儲存的對齊方式。數據可以左對齊或右對齊,如圖所示。注入組通道轉換的數據值已經減去了在ADC_JOFRx寄存器中定義的偏移量,因此結果可以是一個負值。 SEXT位是擴展的符號值。
對於規則組通道,不需減去偏移值,因此只有12個位有效。
8.2.6DMA request
規則通道轉換後的數值儲存在一個唯一的暫存器中,所以當轉換多個規則通道時需要使用DMA,用來避免遺失已經儲存在ADC_DR暫存器的數據。
只有在規則通道轉換結束後才能產生DMA的請求,並且將轉換後的數據從ADC_DR的暫存器傳輸到用戶指定的目的地位址。
8.2.7Temperature sensor
溫度傳感器可以用來測量器件周圍的溫度(TA),溫度傳感器在內部和ADCx_IN16輸入通道相連接,此通道把傳感器輸出的電壓轉換成數字值。溫度傳感器模擬輸入推薦取樣時間是17.1μs。下圖是溫度傳感器的方框圖,當沒有被使用時,傳感器可以置於關電模式。
主要特徵:
l 量測溫度範圍:-40到125度
l 精確度:+/- 1.5° C
讀取溫度的方式:
1. 選擇ADCx_IN16輸入通道
2. 選擇取樣時間大於2.2 μs
3. 設置ADC_CR2的TSVREFE位,以喚醒關電模式下的溫度感測器
(使用function ADC_TempSensorVrefintCmd(ENABLE);)
4.通過設置ADON位啟動ADC轉換
(使用function ADC_SoftwareStartConvCmd(ADC1, ENABLE);)
5.讀ADC暫存器上的VSENSE數據結果
6.利用下列公式得出溫度,公式是以溫度25度時的電壓作為基準值,再去判斷跟他的差異,其中VSENSE是現在溫度的電壓值,Vdd是晶片的電壓值(我們的SIOC約為3v再低一點),由於ADC converter會將現在溫度轉成數值,因此我們必須透過算式將數值轉換成電壓再做計算,電壓相減後再除以Avg_Slop(溫度變化對應的電壓的參數),便可得到目前的溫度。
8.2.8 ADC Control register
下圖介紹ADC中的控制暫存器。
8.3ADC Standard Driver Library
以下介紹ADC的Firmware Libraryfunctions。
ADC暫存器結構, ADC_TypeDef, 定義在stm32f10x_map.h 檔案,如以下敘述:
ADC周邊宣告在stm32f10x_map.h:
8.3.1ADC firmware library function
以下提供了各種樣式的ADC function:
以下列舉幾項實驗會用到的Function,詳細請參考ARM-based 32-bit MCU STM32F101xx and STM32F103xx firmware library.
ADC Cmd function
l ADC DMA Cmd function
l ADC RegularChannelConfig function
l ADC TempSensorVrefintCmd function
l ADC ResetCalibration function
l ADC SoftwareStartConvCmd function
8.4立即演練
嵌入式軟體架構如圖說明:
嵌入式軟體Grafcet圖:
實驗1 使用超級終端機顯示目前溫度
實驗說明:自然界的真實訊號是呈現連續的類比訊號,若要將其輸入電腦中進行數值運算,則必須轉換為數位訊號,請使用ADC與DMA同步運作方式,將溫度經由ADC轉換後的結果放在DMA,再到DMA取值,取16次之後做平均再帶入公式取得溫度,當按下DFU Button後用VCP輸出到螢幕上顯示。
如下圖所示,DMA channel 1 連接ADC1,請利用此規定來操作。
程式碼片段介紹:
l RCCConfiguration
voidRCC_Configuration(void)
{
/* Enable peripheral clocks ------------------------------*/
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* Enable GPIOB clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}
l GPIO Configure
voidGPIO_Configuration(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
l ADCConfiguration
voidADC_Configuration(void)
{ADC_InitTypeDefADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel16 configuration */
//insert your code
/* Temperature Enable */
//insert your code
/* Enable ADC1 DMA */
//insert your code
/* Enable ADC1 */
//insert your code
/* Enable ADC1 reset calibaration register */
//insert your code
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
//insert your code
}
l DMAConfiguration
voidDMA_Configuration(void)
{DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADCConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
}
l .User code
voidCount_Temperature(void)
{
//取平均16次的ADCConvertedValue 來計算(比較準確),
//取溫度必須間隔一下,請使用Delay();
//溫度=
//印出現在溫度
}
l Main program
int main(void)
{/* System USB configuration */
Set_System();
/*pause*/
getchar();
/* System clocks configuration */
RCC_Configuration();
/* GPIO configuration */
GPIO_Configuration();
/* DMA1 channel1 configuration */
DMA_Configuration();
/* ADC1 configuration*/
ADC_Configuration();
printf("Configuration finish\n\r");
printf("Please presee DFU Button to get Temperature\n\r");
while(1)
{/*presee DFU Button to get Temperature*/
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==1)
Count_Temperature();
}
實驗結果:
留言列表