# 微算機 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== ![](https://i.imgur.com/2AdWYvY.png) ##### $T_{AD}$ 設定 透過查表設定ADCS(ADCON2) ![](https://i.imgur.com/WjumzEG.png) 假設頻率 $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}$ ![](https://i.imgur.com/3wq34Ga.png) e.g.,假設頻率 (Fosc) 是 1 MHz,透過查表得知ADCS要設成000, 而Operation欄位是2×Tosc=2×$\frac{1}{1MHz} =2𝜇𝑠$,因此$T_{AD}$ 為2𝜇𝑠 ### Acquisition 採樣輸入電壓,需要時間 ![](https://i.imgur.com/qTehf30.png) 依據data sheet的推導(p. 228),==acquisition time最少會花$2.4 \mu s$== ![](https://i.imgur.com/AkcYY8d.png) 依據[$T_{AD}$](###$T_{AD}$)的時間決定ACQT,若$T_{AD}$為$2 \mu s$,則ACQT要設成001,也就是2$T_{AD}$= $4 \mu s$>$2.4 \mu s$ ### Conversion 將採樣電壓轉換成數值,需要時間 ![](https://i.imgur.com/dSia1sR.png) 依據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結合 ![](https://i.imgur.com/KbM4a3W.png) ### 時間表 ![](https://i.imgur.com/2mBGeCa.png) --- ## PIC18 ADC register introduction ### ADCON0 CHS : 設定analog input 輸入腳位 GO/DONE : 設為1時(ADCON0bits.GO = 1)開始做ADC,轉換完後 GO/DONE會自動設為0 ADON : 開啟ADC功能 ![](https://i.imgur.com/PLqjucv.jpg) ### ADCON1 VCFG1 : 設定下界參考電壓 VCFG0 :設定上界參考電壓 PCFG : 設定ANx PORT為類比還是數位,使用 ADC 的同時若發現其他 PORT 的 input 值怪怪的也許是誤把那些 PORT 設成 analog input ![](https://i.imgur.com/C9XKdy0.png) ### ADCON2 ADFM : 設定justified ADCS : 選擇conversion clock ACQT : 選擇acquisition time要幾個$T_{AD}$ ![](https://i.imgur.com/z3NfUe1.png) ### ADRESH、ADRESL result of conversion ![](https://i.imgur.com/KbM4a3W.png) --- ## Workflow of ADC using interrupt I/O ![image.png](https://hackmd.io/_uploads/B1BtlEfQT.png) ### 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 輸入 ![](https://i.imgur.com/H1dEM62.png) --- ## 範例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; } ```