CC2541的ADC支持多達14位的模擬數字轉換與高達12位的有效位數。它包括一個模擬多路轉換器,具有多達8個各自可獨立配置的通道,一個參考電壓發生器。轉換結果通過DMA寫入存儲器。還具有若干運行模式。
ADC主要特性如下:
- 可選的抽取率,設置了7~12位的分辨率;
- 8個獨立輸入通道,可接受單端或差分信號;
- 參考電壓可選為內部,外部單端,外部差分,或AVDD5;
- 產生中斷請求;
- 轉換結束時的DMA觸發;
- 溫度傳感器輸入;
- 電池測量功能。
圖1
P0引腳上的信號可以作為ADC輸入來使用。在下面,這些引腳叫做AIN0—AIN7引腳,輸入腳AIN0—AIN7與ADC連接。
輸入腳可配置成單端或差動輸入。如選擇差動輸入,包含成對輸入AIN0-AIN1,AIN2-AIN3,AIN4-AIN5和AIN6-AIN7;注意這些引腳既不能加載負電壓,也不能加載大於VDD的電壓。
除了輸入腳AIN0-AIN7外,片上的溫度傳感器也可以用來作為ADC溫度測量的輸入。如要實現這個功能,需設置寄存器TR0.ADCTM和ATEST.ATESTCTRL。
單端輸入AIN0至AIN7可代表通道號0至7,通道號8至11分別代表差動輸入AIN0-AIN1,AIN2-AIN3,AIN4-AIN5,AIN6-AIN7;通道12表示GND,通道13表示溫度傳感器,通道15表示AVDD5/3。這些值在ADCCON2.SCH和ADCCON3.SCH中設置。
我們看到ADCCON2和ADCCON3這兩個寄存器的定義基本相同,但是用法不同,ADCCON2用於ADC序列轉換的配置,而ADCCON3則用於單個ADC通道的配置。所謂ADC序列就是多個ADC通道按照次序分別轉換。注意:不是同時轉換的,從圖1我們也可以看出,ADC的模擬輸入接一個選擇器,同一時刻只能選擇一個通道接入進行ADC轉換。
如果選擇片上的溫度傳感器作為ADC溫度測量的輸入,則需要通過配置寄存器TR0和ATEST來獲得片上溫度,不過這個溫度測量誤差很大,我們一般不用,這里也就不給出例程了。
啟用片內溫度采集配置寄存器:
1 TR0 |= 0x01; 2 ATEST |= 0x01;
1、ADC序列轉換
ADC序列轉換無需CPU的參與,ADC能夠完成一個序列的轉換,並通過DMA把結果寫入內存。
寄存器APCFG影響轉換序列,來自I/O引腳的8位模擬輸入不一定是程序設置的模擬輸入。如某一通道是序列的一部分,但在APCFG中相應模擬輸入是禁止的,那此通道將被跳過。當使用差動輸入時,兩個輸入腳在APCFG寄存器中必須被設置成模擬輸入。
ADCCON2.SCH用來定義ADC輸入的轉換序列。如ADCCON2.SCH被設為小於8,轉換序列包含一個通道(從0到ADCCON2.SCH中設置的通道號),當ADCCON2.SCH值設為8至12時,序列是差動輸入,從通道8至程序設置的通道號;當大於12時,序列包含只選擇的通道。
2、單個ADC轉換
除了序列轉換外,ADC可以通過編程執行單個轉換。通過寫入ADCCON3寄存器可以觸發一個轉換,轉換立即啟動,除非一個轉換序列正在進行中,這種情況下,當序列完成后,馬上執行單個轉換。
3、寄存器ADCCON1
ADC的數字轉換結果可以通過寄存器ADCCON1獲得,寄存器ADCCON1的定義如下圖所示。
- ADCCON1.EOC:轉換結束狀態位,當轉換結束時設高電平,當讀取ADCH時低電平。
- ADCCON1.ST位用來啟動序列轉換的,當這位設高電平、ADCCON1.STSEL是11且當前無轉換運行時序列啟動開始。當序列轉換結束時,這位自動清除為低電平。
- ADCCON1.STSEL位用來選擇哪個事件將啟動一個新的序列轉換。此項選擇有:外部引腳P2.0上升沿事件,之前序列的結束事件,定時器通道0比較事件,或ADCCON1.ST設1事件。
4、ADC轉換結果
數字轉換結果以2進制補碼形式表示的,最高位是符號位。
對於單端輸入配置,由於ADC輸入不能接負電壓,轉換結果總是正的當輸入信號等於參考電壓VREF時達到最大轉換結果。
對於差分輸入配置,ADC輸入電壓為兩個引腳的電壓之差,兩腳的輸入信號不同,結果可能是負的;當采樣率為512,模擬輸入Vconv=VREF時,12MSB的數字轉換結果為2047,當模擬輸入等於-VREF時,轉換結果為-2048。
通過讀ADCCON2.SCH位,知道正在轉換的是哪個通道,在序列轉換中,ADCL和ADCH中的結果是前一個通道ADC轉換的值。如轉換序列已結束,ADCCON2.SCH將有一個大於最后通道數一個以上的值,但如最后寫入ADCCON2.SCH中的通道數是12或更大,讀回的是相同的值。
5、ADC參考電壓
模數轉換的參考電壓可選擇於內部產生電壓,AVDD5腳電壓,應用於AIN7輸入腳的外部電壓,或應用於AIN6-AIN7輸入的差動電壓。內部參考電壓對於CC2541來說是1.25V,比較小,能轉換的最大模擬電壓最大也只能是1.25V,AVDD5腳電壓一般為3.3V,精度也不是很高。轉換結果的准確度依靠於參考電壓的穩定性和噪聲度,所以對於要求較高的ADC轉換建議從AIN7輸入腳接入高精度的參考電壓。
6、ADC轉換時間
ADC只能運行於32MHZ XOSC。執行一個轉換的時間依靠於被選擇的采樣率,一般上,轉換時間由以下公式所得:
Tconv=(decimation rate+16)*0.25us.
可見分辨率越高,轉換時間越長。
7、ADC中斷
只有單通道ADC轉換才有ADC中斷,序列ADC轉換沒有ADC中斷。
The ADC generates an interrupt when a single conversion triggered by writing to ADCCON3 has completed.No interrupt is generated when a conversion from the sequence is completed.
8、ADC DMA觸發
每完成一個序列轉換,ADC將產生一個DMA觸發。單獨轉換完成不產生DMA觸發。
在ADCCON2.SCH中設置8個通道,每個通道都有一個DMA觸發。當通道轉換中准備好一個采樣時,將激活一個DMA觸發。DMA觸發命名為ADC_CHsd,s是單端通道,d是差動通道。
另外,當ADC序列轉換通道中准備好一個新數據時,一個DMA觸發(ADC_CHALL)將激活。
單個ADC轉換讀取ADC值的程序如下:
1 /****************************************************************************** 2 *函 數 名:InitADC 3 *功 能:ADC初始化 4 *入口參數:參考電壓 reference、轉換通道 channel、分辨率resolution 5 *出口參數:ADC轉換結果 6 ******************************************************************************/ 7 uint Read_advalue(uchar reference, uchar channel, uchar resolution) 8 { 9 uint value; 10 uchar tmpADCCON3 = ADCCON3; 11 12 APCFG |= 1 << channel ; //設置ADC輸入通道,模擬I/O使能 13 14 ADCCON3 = (reference | resolution | channel); 15 ADCIF = 0; // 16 17 while(!ADCIF); //等待 AD 轉換完成 18 value = ADCL >> 2; //ADCL 寄存器低 2 位無效 19 value |= ((uint)ADCH << 6); //連接AD轉換結果高位和低位 20 21 //根據分辨率獲得ADC轉換結果有效位 22 switch(resolution) 23 { 24 case ADC_7_BIT: value >>= 7;break; 25 case ADC_9_BIT: value >>= 5;break; 26 case ADC_10_BIT: value >>= 4;break; 27 case ADC_12_BIT: value >>= 2;break; 28 default:; 29 } 30 31 ADCCON3 = tmpADCCON3; 32 return (value); 33 }
主程序:采集VDD值。
1 /****************************************************************************** 2 *程序入口函數 3 ******************************************************************************/ 4 int main(void) 5 { 6 uint vddvalue; //ADC轉換值 7 8 InitClock(); //32MHz時鍾 9 InitUART(); //UART0串口初始化 10 11 while(1) 12 { 13 //ADC參考電壓AVDD5引腳電源電壓:3.3V,分辨率12位,采集通道:VDD/3,VDD=3.3V 14 vddvalue = Read_advalue(ADC_REF_AVDD5, 0x0f, ADC_12_BIT); 15 vddvalue = (vddvalue*33) >> 11; 16 vddvalue = vddvalue*3; 17 buf[0] = vddvalue/10 + '0'; 18 buf[1] = '.'; 19 buf[2] =vddvalue%10 + '0'; 20 21 UartSendString(buf,strlen(buf)); //串口上傳采樣VDD值 22 Delay1ms(2000); //每隔2s上傳一次值 23 } 24 }
這里給出協議棧的adc轉換函數參照對比。

1 #include "hal_adc.h" 2 uint16 u16cvalu=HalAdcRead(HAL_ADC_CHANNEL_4,HAL_ADC_RESOLUTION_12); 3 分辨率設置為12位時,從源碼可以看出,可用位是ADCH 8位+ADCH高4位,其中ADCH最高位是符號位,所以有11位的分辨率,0-2047 4 默認基准電壓3.3V 5 uint16 HalAdcRead (uint8 channel, uint8 resolution) 6 { 7 int16 reading = 0; 8 9 #if (HAL_ADC == TRUE) 10 11 uint8 i, resbits; 12 uint8 adctemp; 13 volatile uint8 tmp; 14 uint8 adcChannel = 1; 15 uint8 reference; 16 17 /* store the previously set reference voltage selection */ 18 reference = ADCCON3 & HAL_ADC_REF_BITS; 19 20 /* 21 * If Analog input channel is AIN0..AIN7, make sure corresponing P0 I/O pin is enabled. The code 22 * does NOT disable the pin at the end of this function. I think it is better to leave the pin 23 * enabled because the results will be more accurate. Because of the inherent capacitance on the 24 * pin, it takes time for the voltage on the pin to charge up to its steady-state level. If 25 * HalAdcRead() has to turn on the pin for every conversion, the results may show a lower voltage 26 * than actuality because the pin did not have time to fully charge. 27 */ 28 if (channel < 8) 29 { 30 for (i=0; i < channel; i++) 31 { 32 adcChannel <<= 1; 33 } 34 } 35 36 /* Enable channel */ 37 ADCCFG |= adcChannel; 38 39 /* Convert resolution to decimation rate */ 40 switch (resolution) 41 { 42 case HAL_ADC_RESOLUTION_8: 43 resbits = HAL_ADC_DEC_064; 44 break; 45 case HAL_ADC_RESOLUTION_10: 46 resbits = HAL_ADC_DEC_128; 47 break; 48 case HAL_ADC_RESOLUTION_12: 49 resbits = HAL_ADC_DEC_256; 50 break; 51 case HAL_ADC_RESOLUTION_14: 52 default: 53 resbits = HAL_ADC_DEC_512; 54 break; 55 } 56 57 /* read ADCL,ADCH to clear EOC */ 58 tmp = ADCL; 59 tmp = ADCH; 60 61 /* Setup Sample */ 62 adctemp = ADCCON3; 63 adctemp &= ~(HAL_ADC_CHN_BITS | HAL_ADC_DEC_BITS | HAL_ADC_REF_BITS); 64 adctemp |= channel | resbits | (reference); 65 66 /* writing to this register starts the extra conversion */ 67 ADCCON3 = adctemp; 68 69 /* Wait for the conversion to be done */ 70 while (!(ADCCON1 & HAL_ADC_EOC)); 71 72 /* Disable channel after done conversion */ 73 ADCCFG &= (adcChannel ^ 0xFF); 74 75 /* Read the result */ 76 reading = (int16) (ADCL); 77 reading |= (int16) (ADCH << 8); 78 79 /* Treat small negative as 0 */ 80 if (reading < 0) 81 reading = 0; 82 83 switch (resolution) 84 { 85 case HAL_ADC_RESOLUTION_8: 86 reading >>= 8; 87 break; 88 case HAL_ADC_RESOLUTION_10: 89 reading >>= 6; 90 break; 91 case HAL_ADC_RESOLUTION_12: 92 reading >>= 4; 93 break; 94 case HAL_ADC_RESOLUTION_14: 95 default: 96 reading >>= 2; 97 break; 98 } 99 #else 100 // unused arguments 101 (void) channel; 102 (void) resolution; 103 #endif 104 105 return ((uint16)reading); 106 }
調試結果:顯示VDD值3.3V。
關於程序注意以下幾點:
1、要配置一個端口0腳為一個ADC輸入,APCFG寄存器中相應的位必須設置為1。這個寄存器的默認值選擇端口0引腳為非ADC,即數字輸入輸出。APCFG寄存器的設置將覆蓋P0SEL的設置,所以無需再配置P0SEL,另外對於I/O口作為外設功能,都無需配置方向,即無需配置寄存器PxDIR。
2、對於單次ADC轉換的配置,只需要配置寄存器ADCCON3,無需配置寄存器ADCCON1和ADCCON2。對於判斷轉換是否結束,還有一種判斷方法:
1 ADCCON1 |=0X30; //ADC啟動方式選擇為ADCCON1.ST=1事件 2 ADCCON1 |= 0x40; //啟動轉換 3 while(!(ADCCON1 & 0x80)); //等待 AD 轉換完成
ADCCON1.STSEL是用於啟動轉換序列的觸發方式的,對於單次ADC轉換,個人感覺這樣配置不好,以后對於單次ADC轉換,不采用這種判斷方式。
單次轉換判斷是否轉換結束:判斷ADC中斷標志ADCIF。
3、ADCH的最高位是符號位,對於單次測量,結果總是正的,所以符號位總是0。14位的ADC轉換值有效值並不是14位的。
有效分辨率如下:
00: 64 decimation rate (7 bits ENOB)----ADCH低7位
01: 128 decimation rate (9 bits ENOB)---ADCH低7位+ADCH高2位
10: 256 decimation rate (10 bits ENOB)--ADCH低7位+ADCH高3位
11: 512 decimation rate (12 bits ENOB)--ADCH低7位+ADCL高5位
例如:采集VDD/3值時,使用12位分辨率,參考電壓AVDD5:3.3V
VDD/3 = vddvalue*3.3/2^11
擴大10倍
VDD/3 = vddvalue*33/2^11
為什么是除以2^11而不是2^12,因為最高位是符號位,12位分辨率實際上只有11位。
VDD = (vddvalue*33/2^11) * 3
4、差分輸入可以用來做比較器。比如通道ADCCON3.ECH=1000,對應差分輸入AIN0-AIN1。如果要比較一個模擬信號和另一個模擬信號的大小關系,只需要將這兩個信號分別接入AIN0和AIN1,然后判斷ADCH的最高位,如果是1,則AIN0<AIN1,如果是0,則AIN0>=AIN1。
5、最大轉換電壓等於參考電壓,而參考電壓的選擇不能大於芯片的電源電壓,一般為3.3V。雖然差分輸入可以轉換負電壓,但是每一個模擬輸入引腳都必須是正電壓且小於電源電壓VDD,負電壓是指兩個輸入通道的差值。