# 微算機 Lab9 - A/D Converter
> 參考自[去年教材](https://hackmd.io/Mg65IxZYS9mjDybkGu-qZg)
:::spoiler 文章目錄
[toc]
:::
## ADC簡介
### 什麼是ADC
主要功能 : 把輸入的類比訊號轉成數位數值
本次Lab會把可變電阻輸入的電壓轉成數值形式
更多參考 ADC - [成大資工](http://wiki.csie.ncku.edu.tw/embedded/ADC)
### VREF與resolution
VREF+ : 上界的參考電壓,若輸入電壓為此值則輸出為最大值
VREF- : 下界的參考電壓,若輸入電壓為此值則輸出為最小值
Resolution : ADC的解析度,若為 10bit 代表輸出從 0 ~ 1023
> e.g., VREF- = 0V, VREF+ = 10V, Resolution : 10bits (range = [0,1023])
> 0V-> 0
> 5V -> 511
> 10V -> 1023
---
## ADC 流程:
**Acquisition -> Convertion -> Discharge(wait before next acquisition) -> Idle until you set GODONE bit -> Acquisition -> ...**
> Acquisition : 採樣輸入電壓
> Conversion : 將電壓轉換成數值
> Discharge : 釋放電壓
### $T_{AD}$
* A/D Clock period, the time required to convert one bit
* 越小越好,==但要大於0.7μs==

##### $T_{AD}$ 設定
透過查表設定ADCS(ADCON2)

假設頻率 $F_{OSC}$ 是 2.86 MHz, 則周期 ($T_{OSC}$)會是 $\frac{1}{2.86 \times 10^6} \approx 0.35\mu s$,為了滿足最低 A/D Clock period ($0.7 \mu s$ ),要把$T_{AD}$設成兩倍的$T_{OSC}$,Operation欄位中的數值即為$T_{AD}$

e.g.,假設頻率 (Fosc) 是 1 MHz,透過查表得知ADCS要設成000,
而Operation欄位是2×Tosc=2×$\frac{1}{1MHz} =2𝜇𝑠$,因此$T_{AD}$ 為2𝜇𝑠
### Acquisition
採樣輸入電壓,需要時間

依據data sheet的推導(p. 228),==acquisition time最少會花$2.4 \mu s$==

依據[$T_{AD}$](###$T_{AD}$)的時間決定ACQT,若$T_{AD}$為$2 \mu s$,則ACQT要設成001,也就是2$T_{AD}$= $4 \mu s$>$2.4 \mu s$
### Conversion
將採樣電壓轉換成數值,需要時間

依據data sheet,conversion需要花11到12個 $T_{AD}$
### Discharge
釋放電壓
#### Left/Right justified
ADC轉換的結果放在ADRES register裡,存放的方式分為left justified與right justified,可依據使用需求設置
e.g., 需要8 bits resolution,設定為left justified,取ADRESH數值 ; 需要10 bits resolution,則設定為right justified,將ADRESH前兩bits與ADRESL結合

### 時間表

---
## PIC18 ADC register introduction
### ADCON0
CHS : 設定analog input 輸入腳位
GO/DONE : 設為1時(ADCON0bits.GO = 1)開始做ADC,轉換完後 GO/DONE會自動設為0
ADON : 開啟ADC功能

### ADCON1
VCFG1 : 設定下界參考電壓
VCFG0 :設定上界參考電壓
PCFG : 設定ANx PORT為類比還是數位,使用 ADC 的同時若發現其他 PORT 的 input 值怪怪的也許是誤把那些 PORT 設成 analog input

### ADCON2
ADFM : 設定justified
ADCS : 選擇conversion clock
ACQT : 選擇acquisition time要幾個$T_{AD}$

### ADRESH、ADRESL
result of conversion

---
## Workflow of ADC using interrupt I/O

### Step1. Configure the ADC module:
* Select VREF (ADCON1.VCFG0, ADCON1.VCFG1)
* Select A/D port control(ADCON1.PCFG)
* Select A/D input channel (ADCON0.CHS)
* Select A/D conversion clock (ADCON2.ADCS)
* Select A/D acquisition time (ADCON2.ACQT)
* Select justified method (ADCON2.ADFM)
* Turn on A/D module (ADCON0.ADON)
> Note : The port pins needed as analog inputs must have their corresponding TRIS bits set (input).
### Step2. Configure the ADC interrupt:
* Enable A/D interrupt (PIE1.ADIE)
* Clear A/D interrupt flag bit (PIR1.ADIF)
* Enable peripheral interrupt (INTCON.PEIE)
* Set GIE bit (INTCON.GIE)
### Step3. Start conversion:
* Set GO/DONE bit (ADCON0.GO)
### Step4. Conversion completed:
* Go to ISR
* Read value of ADRES register
* Do things you want
* Clear ADC interrupt flag bit (PIR1.ADIF)
### Step5. Next conversion(if required) :
* You need to have a minimum wait of 2 $T_{AD}$ before next acquisition start, then go back to step 3.
## Variable resistor
左邊接 5V,右邊接地,中間接 Analog 輸入

---
## 範例code
```cpp
#include <xc.h>
#include<stdio.h>
#include<stdlib.h>
#include <time.h>
#pragma config OSC = INTIO67 //OSCILLATOR SELECTION BITS (INTERNAL OSCILLATOR BLOCK, PORT FUNCTION ON RA6 AND RA7)
#pragma config WDT = OFF //Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config PWRT = OFF //Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = ON //Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
#pragma config PBADEN = OFF //PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LVP = OFF //Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config CPD = OFF //Data EEPROM Code Protection bit (Data EEPROM not code-protected)
void __interrupt(high_priority)H_ISR(){
//step4
int value = ADRESH;
//do things
//clear flag bit
PIR1bits.ADIF = 0;
//step5 & go back step3
/*
delay at least 2tad
ADCON0bits.GO = 1;
*/
return;
}
void main(void)
{
//configure OSC and port
OSCCONbits.IRCF = 0b100; //1MHz
TRISAbits.RA0 = 1; //analog input port
//step1
ADCON1bits.VCFG0 = 0;
ADCON1bits.VCFG1 = 0;
ADCON1bits.PCFG = 0b1110; //AN0 為analog input,其他則是 digital
ADCON0bits.CHS = 0b0000; //AN0 當作 analog input
ADCON2bits.ADCS = 0b000; //查表後設000(1Mhz < 2.86Mhz)
ADCON2bits.ACQT = 0b001; //Tad = 2 us acquisition time設2Tad = 4 > 2.4
ADCON0bits.ADON = 1;
ADCON2bits.ADFM = 0; //left justified
//step2
PIE1bits.ADIE = 1;
PIR1bits.ADIF = 0;
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
//step3
ADCON0bits.GO = 1;
while(1);
return;
}
```