前言

最近在家裡讀到一本堪稱聖經等級的,由 Elliot Williams 所著,「Make:AVR程式設計」,驚為天人啊。這本主要是在教你如何使用Atmel AVR 系列的單晶片,來製作各式各樣的應用。

Ateml AVR 系列的晶片,最出名的應該是 Arduino Uno 所採用的 Atmega328p 這個單晶片微處理器囉。不過作者超強的,捨棄 Arduino 的硬體,教你如何只需要石英晶體振盪器就好,就可以讓MCU動作來控制東西囉。程式撰寫也不使用 Arduino IDE,而是採用 Atmel 原廠所推出的 Avr-gcc 的原廠標準 C/C++ 語言開發環境跟Atmel原廠所提供的 AVR 單晶片專屬的原廠函式庫,真的是開了眼界了。

其中一章,講如何控制 AVR 微處理上面的10位元解析度的類比數位轉換器ADC。真的有點驚嚇到的深入,次一個章節,作者提到一個我從未聽過,被稱作 Over-Sampling 過取樣的演算法。透過這個演算法,可以利用時間來換空間啊。透過多次的取樣跟適當的演算法,竟然可以把 AVR 內部廉價的 10 bits 解析度的ADC,輕易的提升至高價高品質的 12 bits 的解析度。

哇!這簡直太神奇了,這樣用一片台幣兩百五十塊不到廉價 Arduino,簡直直接可以當作儀器儀控或是工業機台的數位介面的心臟了嘛!真是印象深刻。

原書本是用C語言來實作,這裡我們當然不用C囉。為了推廣FORTH讓我們來用 Arduino 上面的 Flash FORTH 來來實作吧。順便把 Flash FORTH 進一步延伸,打通 Atmega328p 這顆上面的 ADC 控制的任督二脈,這樣我們各種應用就無敵囉。

 

ATmega328 ADC硬體概述

底下列舉了從 Data Sheet 節錄下來,ATmega328 的 ADC 電路方塊圖,要了解並替它寫 ADC控制程式,從這張圖開始。

 

Atmega328 ADC circuit.png

 

控制整個 ADC 的運作,靠的是上面三個方塊所顯示的暫存器:

1. ADMUX (ADC Multiplexer Select):

用來控制 ADC 的多工器。這個多工器有兩個,一個用來控制 ADC 的參考電壓輸入:有三個 (1) 外部電源電壓 AVCC,(2)內部矽能帶電壓(1.1V),(3)外部參考電壓 AREF。另一個多工器用來控制要轉換的訊號源:一共11個,(1)內建類比溫度計,(2)接地GND,(3)內部矽能帶參考電壓,(4)ADC7,(5)ADC6,... (11)ADC0

2. ADCSRA (ADC Control & Status Register):

用來控制 ADC 開始轉換,及轉換結束指示及中斷控制,結果輸出格式。很重要的,其中控制了 Prescaler這個時脈除頻器,這個除頻器將CPU的時脈分頻後提供ADC工作,所以其除頻倍率決定了 ADC 取樣的速度。

3. ADCH/ADCL (ADC Data Register): 這兩個8位元的資料暫存器,儲存了最後 ADC 的結果。 ADCH 為 High Byte, ADCL 為 Low Byte。除此之外,可以控制 ADCSRA 中的 ADLAR 來決定左齊或右齊的格式。

這裡,這個ADC採用的是被稱之為「漸近法」的方式來進行轉換。

「漸近法」的核心是一個數位轉類比轉換器 DAC 跟一個類比的比較器。這個 DAC的解析度,決定的最後轉換的最終解析度。轉換的過程大概是這樣:

首先電壓保持電路將輸入電壓暫時保持住不動。

數位轉類比轉換器DAC 先給 B1000000000 的類比電壓出來,然後比較器比較一下這個電壓跟輸入電壓誰大。假如DAC的電壓比較大,那轉換出來的結果就是 B1?????????,否則就是 B0?????????

數位轉類比轉換器DAC 再給 BX100000000 的類比電壓出來,然後比較器比較一下這個電壓跟輸入電壓誰大。假如DAC的電壓比較大,那轉換出來的結果就是 BX1????????,否則就是 BX0????????

數位轉類比轉換器DAC 再給 BXX10000000 的類比電壓出來,然後比較器比較一下這個電壓跟輸入電壓誰大。假如DAC的電壓比較大,那轉換出來的結果就是 BXX1???????,否則就是 BXX0???????

...

就這樣,連續比較十次,最後轉換出來結果的每一個bit就確定囉,轉換完畢。

這整個運作就放在 Conversion Logic 的這個方塊裡面,根據Prescaler除頻器所提供時脈脈波,逐步控制 10-BIT DAC 跟讀回 Sample & Hold Comparator 類比電壓比較器的結果後,將每一個 bit 結果填入 ADCH/ADCL 結果暫存器之中。

 

 

ADC暫存器細節

 

ADMUX (ADC Multiplexer Select):

ADMUX.png

MUX0-MUX3: 用來控制訊號輸入的來源

MUX3 - MUX0
MUX[3:0] Single Ended Input
0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7
1000 Temperature Sensor
1001 --
1010 --
1011 --
1100 --
1101 --
1110 1.1V Band Gap Voltage
1111 0V (GND)

 

ADLAR: 用來控制轉換結果在 ADCH/ADCL 中的左齊或右齊。

ADLAR=0, 右齊

ADCH             Bit9 Bit8
ADCL Bit7 Bit6 zit5 Bit4 Bit3 Bit2 Bit1 Bit0

ADLAR=1, 左齊

ADCH Bit9 Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2
ADCL Bit1 Bit0            

 

REFS0 - REFS1
REF[1:0] Voltage Reference Source
00 External AREF
01 AVcc with external capacitor at AREF pin
10 --
11 Internal 1.1V Band Gap Voltage Reference

 

ADCSRA (ADC Control & Status Register)

ADCSRA.png

ADEN: ADC Enable

寫入 1 = 啟動 ADC 電路運作

寫入 0 = 關閉 ADC 電路運作,如果正在轉換中則立即終止。

 

ADSC: ADC Start Conversion

寫入 1 = 在 Single Conversion Mode 下,開始每一次轉換。在 Free Run Mode下,開始第一次轉換。

0 = 當轉換完畢,此 bit 會被ADC轉換硬體設成 0。輪詢可以透過檢查這個 bit 來知道轉換結束了沒。

 

ADATE: ADC Auto Trigger Enable

寫入 1 = 採用外部觸發源來自動進行 ADC 轉換,所以被稱作 Auto Trigger 模式

寫入 0 = 關閉 Auto Trigger 模式。

 

ADIF: ADC Interrupt Flag

寫入 1 = 代表清除 Interrupt 旗號。

0 = 當 ADC轉換硬體完成 ADC轉換,且結果已經完全送至 ADCH/ADCL 時,此bit會被設成 0。 同時假如 ADIE (ADC Interrupt Enable) 有被設成1的話,執行對應的中斷程式。當對應的中斷有被執行的話,會自動被設回 1 (代表清除 Interrupt 旗號)。

 

ADIE: ADC Interrupt Enable

寫入 1 = 開啟 ADC 中斷,採用中斷方式處理 ADC轉換結果。

寫入 0 = 關閉 ADC 中斷,採用輪詢方式處理 ADC轉換結果。

 

ADPS2 - ADPS0: ADC Prescaler Select  控制 ADC 時脈頻率的分頻器倍率控制

ADC分頻器選擇
CPU Clock ADPS[2:0] Prescaler division factor ADC Frequency
1 MHz 001 2 500 kHz
1 MHz 010 4 250 kHz
1 MHz 011 8 125 kHz
1 MHz 100 16 62.5 kHz
1 MHz 101 32 31.25 kHz
8 MHz 011 8 500 kHz
8 MHz 100 16 250 kHz
8 MHz 101 32 125 kHz
8 MHz 110 64 62.5 kHz
8 MHz 111 128 31.25 kHz
12 MHz 110 64 187.5 kHz
12 MHz 111 128 93.75 kHz
16 MHz 101 32 500 kHz
16 MHz 110 64 250 kHz
16 MHz 111 128 125 kHz

透過對 ADPS[2:0] 的控制跟 CPU 時脈的搭配,可以控制 ADC 的轉換頻率。 (附註: UNO 用的 CPU 時脈是 16MHz)

要注意的,時脈越快, ADC轉換結果就越不準確。 Atmel 公司只能保證 ADC 用 200kHz 以下的頻率,10 bit 的準確度才能維持。假如準確度比較不重要,速度比較重要的情況下,可以使用超過250 kHz 的速度來運作。

 

ADCH/ADCL (ADC Data Register)

這兩個8位元的資料暫存器,儲存了最後 ADC 的結果。 ADCH 為 High Byte, ADCL 為 Low Byte。要注意的,為了防止競爭狀態 (Race Condition) 所造成不正確的結果。當程式讀取 ADCL的時候, ADCH/ADCL會暫時鎖住。這時候硬體會暫時無法對ADCH/ADCL寫入資料,直到 ADCH 被讀取後才會解除鎖定。這樣避免ADC硬體轉換完畢時跟 ADCH/ADCL 同時讀取資料時的競爭狀態所造成不正確的結果。

所以建議 ADCL 先讀,然後再讀取 ADCH。

 

 

DIDR0 (Digital Input Disable Register 0)

DIDR0.png

因為 Atmega 單晶片的眾多 Pin 腳都是多用途的,例如數位輸入輸出 Digital I/O 跟這個 Analog In 的 ADC 是分享使用同一隻腳位,所以要使用 ADC 前要記得設定一下這個 DIDR0 的暫存器,強制關閉對應 ADC 的數位邏輯輸入(緩衝區)的使用,這樣也可以降低無用的電源消耗。

1 = Disable Digital I/O (buffer)

0 = Enable Digital I/O (buffer)

 

ADC 使用程序

Atmega 單晶片的 ADC 操作有兩種模式,單一轉換模式 Single Conversion Mode,跟自由轉換模式 Free Run Mode下。

單一轉換模式,靠的是由程式逐次逐次的呼叫,來觸發執行對應的轉換,程式透過輪詢,來得知轉換結束否並收取轉換後的結果。自由轉換模式則是利用硬體的觸發源,自動觸發 ADC 不斷的重複運作,同時利用硬體中斷來收資料。

這裡,敘述一下單一轉換模式下的步驟。

1. 設定: Auto Trigger Enable = 0 (OFF), ADC Interrupt Enable = 0 (Disable)

2. 關閉數位輸入 DIDR0

2. 設定所需運作的時脈 ADSP2..ADSP0,啟動 ADC電路 ADC Enable = 1

3. 設定參考電壓源:REFS0, REFS1 ,看要是 AREF 還是 AVcc。設定左齊還是右齊 ADLAR

4. 設定訊號輸入源:MUX3..MUX0

5. 設定 ADC Interrupt Flag = 1 (Clear), ADC Start Conversion = 1 (Start) 開始 ADC 轉換

6. 輪詢 ADC Start Conversion,當它為0時轉換結束。

7. 取出 ADCH/ADCL 轉換後的結果。

8. 如要繼續轉換,回到 5.

 

 

ADC 過抽樣理論

過抽樣的理論, Atmel 的一份文件「AVR121: Enhancing ADC resolution by oversampling」裡面有詳細的描述,建議有興趣的可以細看。

這裡摘錄一下它的結果。

首先大家都知道有個 Nyquist 取樣定理,要對已知頻率 f-signal 的訊號源取樣,最小的不失真的取樣頻率被稱為 f-nyquist 會是此訊號源頻率的兩倍!

f-nyquist > 2 * f-signal

而過抽樣理論告訴我們,假如你想要額外增加解析度,每增加 n 位元,則你需要增加 4^n 倍的取樣頻率才能達到。

f-oversampling = 4^n * f-nyquist

這裡我們並沒有真實加快取樣頻率,而是用原來的頻率多抽樣 4^n 倍。 (等於是放慢抽樣頻率的意思)

最後過抽樣的所有數據總和起來之後,除以 sf = 2^n 就會是我們所要的,增加 n 位元的結果。

 

例如,

原來 10 bits,想增加 1bit解析度至11 bits

4^1=4 所以增加抽樣至 4次,將所有結果總合後除以 2^1 = 2 就會得到 11 bits 的結果

原來 10 bits,想增加 2bit解析度至12 bits

4^2=16 所以增加抽樣至 16次,將所有結果總合後除以 2^2 = 4 就會得到 12 bits 的結果

... (依此類推)

 

over-sample.png

 

 

FORTH 程式碼解說

剛剛都介紹的差不多了,所以 FORTH 程式碼解說如下

開啟ADC硬體

: AdcOpen ( --)         \ Initial and open ADC hardware
   %00111111 DIDR0  c!  \ Disable ADC 0 - 5 
   %01000000 ADMUX  c!  \ AVcc as ref , right-adjust result , Ch0
   %00000110 ADCSRA c!  \ Single conversion mode , prescaler 64
   ADEN bOn             \ Enable ADC
   AdcIFClear           \ clear Interrupt flag
;

先設定 DIDR0 關掉所有 ADC0 - ADC5 的 Digital I/O buffer電路

設定參考電位為 AVcc, 右齊的結果格式,輸入訊號暫時預設為 Channel 0 (ADC0)

Auto Trigger = 0, Interrupt Enable = 0, prescalar = 64 (Arduino Uno CPU clock = 16 Mhz,所以取樣率是 250 kHz)

ADEN = 1 開啟 ADC 電路運作

ADC Interrupt Flag = 1 (Clear)

 

等待 ADC 轉換結果

: AdcWait  ( --)        \ check until conversion completed
   begin  ADSC b@ 0=    \ Start Conversion = 0, Complete!
   until
;

就不斷輪詢 ADSC (ADC Start Conversion),當它是0的時候轉換結束。

 

關閉 ADC 硬體運作

: AdcClose ( --)
   ADEN bOff            \ Disable ADC
   AdcIFClear           \ clear Interrupt flag
;

就設定 ADEN (ADC Enable) = 0

然後恢復 ADIF (ADC Interrupt Flag) = 1

 

設定 ADC 的頻道,訊號輸入源

: CH! ( #ch --)
   AdcWait              \ wait and make sure all has done
   $0f and              \ channel selected by lower nibble
   ADMUX c@ $f0 and     \ fetch upper nibble
   or 
   ADMUX c!          
;

先檢查一下,是否 ADC 在 Idle 的狀態。是才做設定,否則等待其轉換結束。

0xF0 AND 截取一下使用者輸入的頻道 ID,只允許 0 - 15 的數值。

將這個數值放入 ADMUX 暫存器中的 MUX0-MUX3 囉,要求多工器切換所選擇對應的訊號輸入源囉。

 

啟動ADC轉換並取得 ADC 的結果

: Adc@  (  -- u)
   AdcWait                 \ wait and make sure all has done
   ADSC bOn                \ start ADC conversion             
   AdcWait                 \ wait ADC complete
   ADCL c@                 \ get low byte result
   ADCH c@ #8 lshift  or   \ get high byte result and merge
   AdcIFClear              \ clear Interrupt flag
;

 

確認一下 ADC 是否已經 Idle,沒有就等待其轉換結束才開始。

ADSC (ADC Start Conversion) = 1 要求ADC 開始轉換!

AdcWait 等待 ADSC = 0 轉換結束!

先從 ADCL 取出 Low Byte 的結果!

在從 ADCH 取出 High Byte 的結果,左移八位後,合併至 Low Byte 成最終 10 bits 的結果。

AdcIFClear 清除 ADIF (ADC Interrupt Flag 設成 1)

 

 

過抽樣 over-sampling

我們要把ADC 10位元的解析度推到 12位元,根據過超樣 Over Sampling 的理論,所以要10位元取樣16次之後將總和除以4,就是12位元的結果囉。

這裡要注意的,我們 FORTH 系統在8位元的微處理器下,一個 word 的數字是 2 bytes,也就是説,不帶符號的話,數值範圍是 0 - 65535。

10 bits 的 ADC,最大的數字是 1023。連加16次後最大的數字是 1024*16 - 1 = 16383 小於 65535 ,所以蠻夠用的,沒有爆掉。所以就放心的用個 for-loop 把它們全部加起來除以4就是答案囉。

因為不帶符號,所以要用不帶符號的除法 u/

: 12Adc@  ( -- u)          \ 12 bit ADC by over-sampling
   0   16 for  Adc@ + next  4 u/  
;

 

比例算數

傳統的 FORTH 語言不喜歡浮點數,語言發明人認為用固定整數運算,既優雅又快速。

這裡剛好可以展示一下 FORTH 固定整數運算中,佔有非常重要比例的,比例算數的使用方式。如何利用比例算數 */ 優雅地達到跟浮點數一樣的結果。

對於比例算數,筆者比較熟悉的是 ANSI FORTH 標準的帶符號指令 */ ,很驚訝的是 Flash FORTH 並沒有提供。 Flash FORTH 反而只提供了另一個不是那麼標準的,只針對不帶符號整數運算的 u*/mod 。也許 Flash FORTH 的開發者認為,不帶符號的整數運算就很夠了吧。

在我們這個例子,也確實如此。 我們這個 ADC 的運作,結果都是不帶符號的整數。

來看一下 u*/mod 官方的文件解說

u*/mod     ( u1 u2 u3 -- u4(remainder) u5(quotient) )
           Unsigned u1*u2/u3 with 32 bit intermediate result

 

非常清楚, 堆疊上三個數字 u1 u2 u3 , 然後算出 u5 = u1*u2/u3 , u4 = mod(u1, u3) 就 u1 除以 u3 後的餘數。

為什麼叫比例算數呢? 就是把整數 以 u2/u3 的比例給取出來,得到新的整數。 也許你會問,這太麻煩了吧, 不是 u1*u2 然後再除以 u3 不就得了。

問題通常沒有那麼美好, 一個 2bytes 的整數最高為 65535, 假如兩個 2bytes 的 65535相乘,這個結果的數字就直接爆掉而得到錯誤的結果。需要 4bytes 才能容納相乘後的結果。這個比例運算 */ 就是為了預防結果爆掉而發明出來的。 當 u1*u2 時,會先用 4bytes的雙整數來暫存結果,然後除以 u3 後,再轉回2bytes俗稱1word的單整數。這樣就可以得到最精確,不會爆掉的結果。

有了 ADC的數值,我們希望轉成實際的電壓值,那要怎麼算呢?假如今天 12 bits ADC是讀到 2021 這個數字。 12bits,最大是 2^12 = 4096 假如這時候的參考電位是 5.0 Volt

用浮點數,你會這麼算 5 V * 2021 / 4096 = 2.467 V

可是用整數,假如你這樣算,你只會得到 2 V,後面的小數點都不見了,因為整數沒有小數點。

所以不能這樣算,假如我們希望有小數點下四位的準確度,你要這樣算

50000 * 2021 / 4096 = 24670

也就是説,那個小數點是隱形的囉,程式設計師要自己去控制它。同時也要小心,不可以讓整數超過 65535 這個最大的數字而爆掉。

所以就是 50000 2021 / 4096   u*/mod nip   對 50000這個數字,以 2021/4096 的比例,分出新的數字。

因為 u*/mod 會多個餘數,所以用 nip 這個堆疊指令把它去掉。

 

數字轉換跟列印

FORTH 為了方便處理這個隱形的小數點的列印,也提供了非常方便的數字轉字串指令。

<# 代表要開始轉換一個 4bytes 的雙整數至字串, 每一個 # 代表轉換一位, [char] . hold 代表把小數點放上去, #s 代表轉換剩下所有的位數, #> 代表結束轉換並將字串準備好。type是字串列印的指令,將整個轉換好的字串列印出來。

所以要列印四位小數的雙整數,指令碼如下

<# # # # # [char] . hold #s #> type

很清楚,很方便吧。

為了通用性,這些列印的指令是針對 4bytes的雙整數的,我們是2byte的單整數,所以要湊上 4bytes。

對於不帶符號的單整數,因為 FORTH 的雙整數,帶符號的High Bytes 在後面,我們只要補個 0 就好。假如你是帶符號的,就得用 s>d 這個單整數轉雙整數的指令,它會適當地把符號補上。所以這段完整程式碼如下。

: (.Volt)  ( adc max-adc --)  \ print out voltage value: X.XXXX Volt
   Vref  swap   u*/mod   nip
   0 <# # # # # [char] . hold #s #> type
;

因為我們要針對 10bits = 1024 跟 12bits = 4096 兩種不同解析度來計算,所以先弄個中介 (.Volt) 列印指令。

最終列印指令,

10 bits ADC

: .Volt    ( 10adc --)        \ print out voltage with 10 bits
   1024 (.Volt)
;

12 bits ADC

: .Volt    ( 12adc --)        \ print out voltage with 12 bits
   4096 (.Volt)
;

 

測試指令

開啟 Adc ,設定好多工器的 Channel 用10bits ADC 讀取結果,用 over-sampling 模擬 12bits ADC 讀取結果,分別印出原始值跟所讀到的電壓值,連續執行10次,然後關閉 Adc 結束。

 

: Test ( CH --)
   AdcOpen   CH!  cr
   10 
   for Adc@  ." Adc 10bit = " dup . ." , " .Volt   ." V" cr
     12Adc@  ." Adc 12bit = " dup . ." , " .12Volt ." V" cr cr
   next
   AdcClose
;

 

測試結果

首先是測試的硬體,已經有 Flash FORTH 系統在裡面的 Arduino

IMG_6940.png

 

來先接地測試一下 Channel 0 (ADC0),完美,都是零

IMG_6939.png

 

再來接到 Arduino Uno 內建 5V Vcc 腳位測試一下 5V 的狀態

IMG_6941.png

兩個都是 4.9951 V,還不賴!

 

最後接到 Arduino Uno 內建 3.3 Vcc 腳位測試一下 3.3V 的狀態

IMG_6942.png

一個 10 bits 解析度比較差,所以永遠都是 3.4033V

另一個 12bits 解析度比較好,所以在 3.3984V - 3.3996V 之間跳動,可以看到更細微的電壓變化!

 

 

萬歲,打通 ADC 這個任督二脈,可以用廉價的 Arduino Uno,再用 Flash FORTH 語言直接來做儀控惹!!

 

 

原始程式碼列表

 

\
\  ADC Uno
\    Frank Lin 2021.7.02
\

\
\ bit masks
\


%00000001 constant bit0
%00000010 constant bit1
%00000100 constant bit2
%00001000 constant bit3
%00010000 constant bit4
%00100000 constant bit5
%01000000 constant bit6
%10000000 constant bit7


: Bit_Def    
   create swap c, c,
   does>  dup  c@        ( mask)
   swap char+ c@         ( register)
;

: bOn   mset ;

: bOff  mclr ;

: b@   mtst ;


marker --adc--

flash


\ Registers for ADC

$78 constant ADCL     \ ADC Data Register Low Byte
$79 constant ADCH     \ ADC Data Register High Byte
$7a constant ADCSRA   \ ADC Control and Status Register A
$7c constant ADMUX    \ ADC Multiplexer Select
$7e constant DIDR0    \ Digital Input Disable Register


\ Bit masks for ADC Control and Status Register (ADCSR)

bit7 ADCSRA Bit_Def ADEN    \ ADC Enable
bit6 ADCSRA Bit_Def ADSC    \ ADC Start Conversion
bit4 ADCSRA Bit_Def ADIF    \ ADC Interrupt Flag

 

: AdcIFClear            \ Clear ADC Interrupt Flag
   ADIF bOn             \ clear Interrupt flag by set to 1
;


: AdcOpen ( --)         \ Initial and open ADC hardware
   %00111111 DIDR0  c!  \ Disable ADC 0 - 5 
   %01000000 ADMUX  c!  \ AVcc as ref , right-adjust result , Ch0
   %00000110 ADCSRA c!  \ Single conversion mode , prescaler 64
   ADEN bOn             \ Enable ADC
   AdcIFClear           \ clear Interrupt flag
;

: AdcClose ( --)
   ADEN bOff            \ Disable ADC
   AdcIFClear           \ clear Interrupt flag
;

: AdcWait  ( --)        \ check until conversion completed
   begin  ADSC b@ 0=    \ Start Conversion = 0, Complete!
   until
;

: CH! ( #ch --)
   AdcWait              \ wait and make sure all has done
   $0f and              \ channel selected by lower nibble
   ADMUX c@ $f0 and     \ fetch upper nibble
   or 
   ADMUX c!          
;

: Adc@  (  -- u)
   AdcWait                 \ wait and make sure all has done
   ADSC bOn                \ start ADC conversion             
   AdcWait                 \ wait ADC complete
   ADCL c@                 \ get low byte result
   ADCH c@ #8 lshift  or   \ get high byte result and merge
   AdcIFClear              \ clear Interrupt flag
;

 

: 12Adc@  ( -- u)          \ 12 bit ADC by over-sampling
   0   16 for  Adc@ + next  4 u/  
;

 

marker --test--


50000 constant Vref    ( Reference Voltage for ADC)

: (.Volt)  ( adc max-adc --)  \ print out voltage value: X.XXXX Volt
   Vref  swap   u*/mod   nip
   0 <# # # # # [char] . hold #s #> type
;

: .12Volt  ( 12adc --)        \ print out voltage with 12 bits
   4096 (.Volt)
;

: .Volt    ( 10adc --)        \ print out voltage with 10 bits
   1024 (.Volt)
;


: Test ( CH --)
   AdcOpen   CH!  cr
   10 
   for Adc@  ." Adc 10bit = " dup . ." , " .Volt   ." V" cr
     12Adc@  ." Adc 12bit = " dup . ." , " .12Volt ." V" cr cr
   next
   AdcClose
;


       

 

XXX

 

arrow
arrow

    ohiyooo2 發表在 痞客邦 留言(0) 人氣()