ATtiny88初體驗(五):ADC

2023-08-29 18:04:41

ATtiny88初體驗(五):ADC

ADC模組介紹

ATtiny88微控制器包含一個10bit解析度的ADC模組,擁有8個通道,最大取樣率15kSPS,轉換時間14us。ATtiny88的ADC參考電壓可以來自外部,也可以使用內部1.1V的電壓源。支援自由執行模式和單次轉換模式,支援多種自動觸發源,在睡眠模式下擁有噪聲消除器。

注意:為了使用ADC模組, PRR 暫存器的 PRADC 位必須設為0。

ADC轉換結果儲存在 ADCHADCL 暫存器中,可以通過 ADLAR 位來選擇左對齊還是右對齊。為了防止在讀取ADC結果時,ADC結果發生改變,必須先讀取 ADCL 暫存器,然後再讀取 ADCH 暫存器。因為在讀取 ADCL 暫存器時,ADC轉換的結果會被鎖定,直到 ADCH 暫存器被讀取為止。

ADC有兩種模式:單次觸發模式和自由執行模式。在單次觸發模式下,向 ADSC 位寫1啟動切換,轉換完成後該位會自動清零;在自由執行模式下, ADSC 位會一直維持1。

在睡眠模式下,ADC模組可以啟用噪聲消除器減少來自CPU核心和其他I/O外設的噪聲,方法如下:

  1. 確保ADC使能並處於非忙狀態,選擇單次轉換模式,使能ADC轉換結束中斷。
  2. 進入ADC噪聲減少模式(或空閒模式),當CPU停止工作時,ADC會開始一次轉換。
  3. 當轉換結束時,ADC轉換結束中斷會喚醒CPU,除非再次使用睡眠命令,否則CPU會一直處於活躍狀態。

注意:進入除空閒模式及ADC噪聲減少模式外的其他睡眠模式時,不會自動關閉ADC,建議在進入這些睡眠模式時將 ADEN 位清零。

ADC轉換結果與電壓的關係如下式所示:

\[ADC=\frac{V_{IN} \times 1024}{V_{REF}} \]

ATtiny88內部有一個溫度感測器,它連線到ADC8通道,在測量溫度時,必須選擇內部1.1V參考電壓。

ADC的測量電壓與溫度約為線性關係,靈敏度約為1LSB/℃,典型值如下:

為了獲得更高的精度,可以使用如下公式進行軟體校正:

\[T = k \times [(ADCH << 8) | ADCL] + T_{OS} \]

其中, \(k\) 是斜率,是固定的,通常數值非常接近1, \(T_{OS}\) 是感測器偏移量。

相關暫存器

  • REFS0 :參考電壓選擇。
  • ADLAR :ADC結果左對齊,設為0右對齊,設為1左對齊。
  • MUX[3:0] :模擬通道選擇。

  • ADEN :使能ADC。
  • ADSC :啟動ADC轉換,轉換結束後自動清零。
  • ADATE :使能ADC自動觸發。
  • ADIF :ADC中斷標誌位,中斷程式執行結束後清零,或者也可以寫1清零。
  • ADPS[2:0] :ADC分頻選擇,分頻後的頻率不要超過1MHz。

  • ADTS[2:0] :ADC自動觸發源選擇。

  • ADCnD :關閉對應ADC引腳的數位輸入緩衝。

程式碼

下面的程式碼展示瞭如何使用ATtiny88的ADC模組讀取ADC0通道(PC0引腳)的電壓值,程式碼檔案的整體組織結構如下:

.
├── Makefile
├── inc
│   ├── serial.h
│   └── serial_stdio.h
└── src
    ├── main.c
    ├── serial.c
    └── serial_stdio.c

src/main.c 原始檔的內容如下:

#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <serial_stdio.h>

static void delay(void);

int main(void)
{
    cli();
    stdio_setup();              // initialize stdio and redirect it to serial
    ADMUX = _BV(REFS0);         // external reference, align right, select channel ADC0(PC0)
    ADCSRA = _BV(ADEN) | _BV(ADIF) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
                                // enable ADC, clear ADC interrupt flag, disable ADC interrupt, division factor = 128
    DIDR0 = _BV(ADC0D);         // disable digital input buffer of ADC0 pin
    sei();

    for (;;) {
        ADCSRA |= _BV(ADSC);            // start conversion
        while (!(ADCSRA & _BV(ADIF)));  // wait for completion
        uint16_t value = ADCL;          // read low byte first
        value |= ADCH << 8;             // then read the high
        uint16_t voltage = (5000UL * value) >> 10;  // convert digital value to voltage
        printf("ADC0 value: 0x%04X, voltage: %dmV.\r\n", value, voltage);
        ADCSRA |= _BV(ADIF);            // clear flag
        delay();
    }
}

static void delay(void)
{
    for (volatile uint32_t i = 0; i < 0x8000; i++);
}

參考資料

  1. ATtiny88 Datasheet