ADC的引入
什麼是ADC
(1) ADC : analog digital converter, AD 轉換,模數轉換(也就是模拟轉數字)
(2) CPU 本身是數字的,而外部世界變量(如電壓、溫度、高度、壓力···)都是模拟的,是以需要用 CPU 來處理這些外部的模拟變量的時候就需要做 AD 轉換。
為什麼需要 ADC
(1)為了用數字技術來處理外部的模拟實體量。
關于模拟量和數字量
(1)模拟的就是連續的,現實生活當中的時間、電壓、高度等都是模拟的(連續分布的,劃分的話可以無限的更小劃分)。模拟量反映在數學裡面就是無限小數位(從 0 到 1 之間有無數個數)
(2)數字的就是離散的,離線的就是不連續的。這種離散處理實際上是從數學上對現實中的模拟量的一種有限精度的描述。數字化就是 離散化,就是把連續分布的模拟量按照一定精度進行取點(采樣)變成有限多個不連續分布的數字值,就叫數字量。
(3)數字化的意義就在于可以用(離散)數學來簡化描述模拟量,這東西是計算機技術的基礎。
(4)計算機處理參量的時候都是數字化的,計算機需要數字化的值來參與運算。如果系統輸入參數中有模拟量,就需要外加 AD 轉換器将模拟量轉成數字量再給計算機。
有 AD 自然就有 DA
(1) AD 是 analog to digital,DA 自然就是 digital to analog,數字轉模拟。
(2)純粹用 cpu 是不可能實作數字轉模拟,因為 cpu 本身就是數字的。使用一些(具有一些積分或微分效果的)實體器件就可實作數字轉模拟。
(3)數字轉模拟的作用。譬如可以用來做波形發生器。
ADC的主要相關概念
量程(模拟量輸入範圍)
(1) AD 轉換器是一個電子器件,是以他隻能輸入電壓信号。其他種類的模拟信号要先經過傳感器(Sensor)的轉換變成模拟的電壓信号然後才能給 AD。
(2) AD 輸入端的模拟電壓要求有一個範圍,一般是 0〜3.3V 或 0〜5V 或者是 0〜12V 等等。模拟電壓的範圍是 AD 晶片本身的一個參數。實際工作時給 AD 的電壓信号不能超過這個電壓範圍。
精度(分辨率 resolution )
(1) AD 轉換輸出的數字值是有一定的位數的(譬如說 10 位,意思就是輸出的數字值是用 10 個二進制位來表示的,這種就叫 10 位 AD )。這個位數就表示了轉換精度。
(2) 10 位 AD 就相當于把整個範圍分成了 1024 個格子,每個格子之間的間隔就是電壓的表示精度。加入 AD 晶片的量程是 0 〜 3.3V,則每個格子代表的電壓值是3.3V/1024 = 0.0032265V。如果此時 AD 轉換後得到的數字量是 447,則這個數字量代表的模拟值是:447×0.0032265V = 1.44V。
(3) AD 的位數越多,則每個格子表示的電壓值越小,将來算出來的模拟電壓值就越精确。
(4) AD 的模拟量程一樣的情況下,AD 精度位數越多精度越高,測出來的值越準。但是如果 AD 的量程不一樣。譬如 2 個 AD,A 的量程是 0〜50V,B 的量程是 0〜0.5V,A 是 12 位的,B 是 10 位的,可能 B 的精度比 A 的還要高。(A的精度:50/1024 = 0.04883,B的精度:0.5/4096 = 0.000122)
轉換速率( MSPS 與 conventor clock 的不同)
(1)首先要明白:AD 晶片進行 AD 轉換是要耗費時間的。這個時間需要多久,不同的晶片是不一樣的,同一顆晶片在配置不一樣(譬如說精度配置為 10 位時時間比精度配置為 12 位時要小,譬如說有些 AD 可以配轉換時鐘,時鐘頻率高則轉換時間短)時轉換時間也不一樣。
(2)詳細的需要時間可以參考資料手冊。一般資料手冊中描述轉換速率用的機關是MSPS(第一個 M 是 兆,S 是 sample,就是 采樣;PS 就是 per second,總的意思就是 兆樣本每秒,每秒種轉出來多少 M 個數字值)
(3) AD 工作都需要一個時鐘,這個時鐘有一個範圍,我們實際給他配置時不要超出這個範圍就可以了。AD 轉換是在這個時鐘下進行的,時鐘的頻率控制着 AD 轉換的速率。注意:時鐘頻率和 MSPS 不是一回事,隻是成正比不是完全相等。譬如 S5PV210 中的 AD 轉換器,MSPS = 時鐘頻率 / 5
通道數
(1) AD 晶片有多少路 analog input 通道,代表了将來可以同時進行多少路模拟信号的輸入。
S5PV210 的 ADC 控制器
ADC 和(電阻式)觸摸屏的關系
(1) ADC 在 210 的資料手冊的 Section10.7
(2)電阻式觸摸屏本身工作時依賴于 AD 轉換,是以在 210 的 SoC 中電阻觸摸屏接口本身和 ADC 接口是合二為一的。或者說電阻觸摸屏接口使用了(複用了) ADC 的接口。
ADC的工作時鐘框圖
(1) ADCCLK 是 ADC 控制器工作的時鐘,也就是1.13.2.3節中講到的 conventor clock。從時鐘框圖可以看出,它是 PCLK(當然是 PCLK_PSYS )經過了一次分頻後得到的。是以将來初始化 ADC 控制器時一定有一個步驟是初始化這裡的分頻器。
210 的 10 個 ADC 通道(注意 ADC 引腳和 GPIO 的差別)
(1)210 一共支援 10 個 ADC 通道,分别叫 AIN[0] ~ AIN[9]。理論上可以同時做 10 路 AD 轉換。
(2) SoC 的引腳至少分 2 種:digit 數字引腳和 analog 模拟引腳。我們以前接觸的GPIO 都屬于數字引腳,ADC channel 通道引腳屬于模拟引腳。數字引腳和模拟引腳一般是不能混用的。
ADC控制器的主要寄存器
TSADCCON0
TSDATX0 TSDATY0 轉出來的AD值存在這裡,我們讀也是讀這裡
CLRINTADC0 清中斷
ADCMUX 選擇目前正在操作的AD通道
(1)等待觸摸屏轉換完畢的方法有2種:一種是檢查标志位,第二種是中斷。第一種方式下我們先開啟一次轉換然後循環不停檢查标志位直到标志位為1表明已經轉換完可以去讀了;第二種方式下就是設定好中斷,寫好中斷isr來讀取AD轉換資料。然後開啟中斷後CPU就不用管了,等AD轉換完成後會生成一個中斷信号給CPU,就會進入中斷處理流程。第一種方法是同步的,第二種方式是異步的。
(2)AD轉換都是需要反複進行的,那麼轉完一次一般要立即開啟下一次轉換,是以需要有一種機制能夠在一次轉完時自動開啟下一次。這個機制就叫start by read,這個機制的工作方法是:當我們讀取本次AD轉換的AD值後,硬體自動開啟下一次AD轉換。
AD 轉換的程式設計實踐
AD 控制器初始化
循環進行 AD 采樣
start by read 模式介紹
(1)應用方法:開啟start by read模式,第一次先讀一次丢掉,這次讀就能開啟下一次 AD 轉換,然後以後就可以不停的讀取 AD 值了。
#include "main.h"
#define TSADCCON0 0xE1700000
#define TSDATX0 0xE170000C
#define TSDATY0 0xE1700010
//#define CLRINTADC0 0xE1700000
#define ADCMUX 0xE170001C
#define rTSADCCON0 (*(volatile unsigned int *)TSADCCON0)
#define rTSDATX0 (*(volatile unsigned int *)TSDATX0)
#define rTSDATY0 (*(volatile unsigned int *)TSDATY0)
//#define rCLRINTADC0 (*(volatile unsigned int *)CLRINTADC0)
#define rADCMUX (*(volatile unsigned int *)ADCMUX)
// 初始化ADC控制器的函數
static void adc_init(void)
{
rTSADCCON0 |= (<<); // resolution set to 12bit
rTSADCCON0 |= (<<); // enable clock prescaler
rTSADCCON0 &= ~(<<);
rTSADCCON0 |= (<<); // convertor clock = 66/66M=1MHz, MSPS=200KHz
rTSADCCON0 &= ~(<<); // normal operation mode
rTSADCCON0 &= ~(<<); // disable start by read mode
rADCMUX &= ~(<<); // MUX選擇ADCIN0
}
// 注意:第一,延時要确實能延時;第二,延時時間必須合适。
static void delay(void)
{
volatile unsigned int i, j;
for (i=; i<; i++)
for (j=; j<; j++);
}
// 測試ADC,完成的功能就是循環檢測ADC并得到ADC轉換數字值列印出來
void adc_test(void)
{
unsigned int val = ;
adc_init();
while ()
{
// 第一步:手工開啟ADC轉換
rTSADCCON0 |= (<<);
// 第二步:等待ADC轉換完畢
while (!(rTSADCCON0 & (<<)));
// 第三步:讀取ADC的數字值
// 第四步:處理/顯示數字值
val = rTSDATX0;
printf("x: bit14 = %d.\n", (val & (<<)));
printf("x: adc value = %d.\n", (val & (<<)));
val = rTSDATY0;
printf("y: bit14 = %d.\n", (val & (<<)));
printf("y: adc value = %d.\n", (val & (<<)));
// 第五步:延時一段
delay();
}