# Chapter 10 Timers and Analog Interface ###### tags: `microcontroller` ## PIC18F Timers - PIC18F4321 有 4 個 timers ,分別為: 1. Timer0 ( 8-bit or 16-bit ) - 當使用 internal clock 時是一個 timer ,當使用 external clock 時變成 counter 。 2. Timer1 ( 16-bit ) - 這邊說明只講他只能作為 16-bit 的 timer or counter ,不能作為 8-bit 的使用。 - 與 Timer0 一樣,當使用 internal clock 時是一個 timer ,當使用 external clock 時變成 counter 。 3. Timer2 (8-bit) - 在使用 internal clock 時可以作為 8-bit timer ,但他不能當 counter 使用。 4. and Timer3 ( 16-bit ) 。 - 跟 Timer1 一樣,他只能作為 16-bit 的 timer or counter ,不能作為 8-bit 的使用。 - 基本功能就是能在 rising/falling edge of a clock 時, incremented/decremented timer 的 register 。 - 也可以作為 **event counters** ,像在開關被操作幾次後,會觸發什麼程式(不只是計時的功能,也能計次)。 - Timer1, Timer2, Timer3 可作為 **CCP** ( Capture/Compare/Pulse Width Modulation ) operation 使用。 - 總結來說, timer 就是一個 binary counter 用來計算,當他達到 maximum value 時,會回到 0 ,然後 overflow 的 flag 會變成 1 ,在有設定的時候還能夠觸發 interrupt 。 :::warning 這邊 Timer2 比較特別,他有一個 register 叫 PR2 ,當 TMR2 = PR2 時,就會觸發 overflow ,跟其他的到達最大值不一樣。 ::: ![](https://i.imgur.com/pQ8t5o7.png) ### Timer interrputs - 因為如果單看 TMRxIF bit 有沒有變 HIGH 的話,程式不能夠執行下去,所以才會需要 interrupt 。 - 因為 timers 被視為 外部裝置,所以要打開 INTCON 裡面的 peripheral interrupt bit ( PEIE ) 。 - 開啟 timer0 interrupt : ```c= BCF INTCON, TMR0IF BSF INTCON, TMR0IE BSF INTCON, PEIE BSF INTCON, GIE ``` - 當觸發 interrupt 時, PC 會寫入 0x0000008 的地址( default )。 ### Prescaler and Postscaler 1. **Prescaler** - 在 clock 上面在切分,變成 n 個 clock period 才會進一個 timer period 。 - 範例:如果 Timer0 的 prescaler 設為 4 , clock frequency 是 4 MHz ,則 Timer0 的 timer frequency 為 $\frac{Fosc}{4}÷4=0.25$ Mhz 。 - 每一個 Timer 好像都有 prescaler 。 2. **Postscaler** - slow down the interrupt generation. - 印象中就是要 overflow 幾次才會觸發到 interrupt 。 - 只有 Timer2 有, ppt 還講到 Timer2 的 prescaler 可以 delay 的比 postscaler 還要多。 ### Timer0 - 可以作為 8-bit/16-bit timer 或 counter 。 - 當使用 internal clock 的時候作為 timer ;當使用 external clock ( T0CK1/RA4 )時作為 counter 。 #### Timer0 as a timer 1. set **TMR0ON** (bit 7 of T0CON register ) - let the Timer0 start. 2. 這時候 TMR0L ( in 8-bit mode ) 或 TMR0H:TMR0L ( in 16-bit mode ),就會往上加。 3. 直到達到 overflow 的時候, **TMR0IF** flag ( in INTCON ) 就會變 1 。 4. 然後 TMR0L 就會歸 0 。 :::info 如果要停止 Timer0 只要把 TMR0ON 關掉就好了( set the bit to 0 )。 ![](https://i.imgur.com/1fxFm4f.png) ::: #### Timer0 as a counter 1. set **T0CS** ( bit 5 in the T0CON ) - let Timer0 use the external clock. 2. (選擇性)可以調整 **T0Se** ( bit 4 in the T0CON ),來決定是要 trigger by rising/falling edge 。 3. 其他就等 overflow 發生之類的。 #### Timer0 Read/Write in 16-Bit Mode - **TMR0H** is not actual high byte of Timer0 in 16-bit mode. - 在執行 16-bit Timer0 操作之前一定要先把 TMR0H 歸 0 避免不必要的錯誤。 - 實際上的 upper 8-bit value 是存在 latched register 裡面,然後在存入 TMR0H 中。 - 當 TMR0L 的值發生改變的時候, TMR0H 的值才會更新。 #### Timer0 Presaler - 當我們寫入 Timer module 時(像 CLRF TMR0 )之類的操作時,他會自動清除 presaler count 。 #### Timer0 Interrupt and Timer0 Flag bits - 要打開 **TMR0IE** 來允許 Timer0 interrupt 。 - 當 overflow 時 **TMR0IF** 會變成 1 來代表。 ```c= INTCONbits.TMR0IF = 0; INTCONbits.PEIE = 1; INTCONbits.TMR0IE = 1; INTCONbits.GIE = 1; T0CONbits.TMR0ON = 1; ``` - 發生 interrupt 時, PC 會讀進 0x00008的地址。 - Timer0 的 interrupt priority 是設定在 **TMR0IP** ( bit 2 of INTCON2 ),可設定為 high/low priority interrupt 。 - 當為 low priority interrupt 時, PC 會讀入 0x000018 的地址。 :::danger 要記得 interrupt code 裡面要把 **TMR0IF** 設為 0 ,不然會一直觸發 interrupt 。 ::: :::info - 這邊要會算總共大概會 delay 多久,一定要記得 4次的 CPU clock 才能完成一次的 instruction ( in 8-bit device ) - **Time delay = Instruction cycle * Prescale value * Count** - 而且除了你看到的,還會有 extra cycle ( set TMR0L to 0x00/set TMR0IF to 1 ),所以在 count 那邊除了真正會計算到的,還會再 + 1 。 - delay 1 ms : ```c= #include <p18f4520.h> void main(void){ OSCCON = 0x70; //8 MHz T0CON = 0x06; //initialize T0CON TMR0H = 0xFF; //Initialize TMR0H TMR0L = 0xF0; //Initialize TMR0L INTCONbits.TMR0IF = 0; //Clear Timer0 flag bit T0CONbits.TMR0ON = 1; //Start timer0 while(INTCONbits.TMR0IF == 0); //Wait for Timer0 flag bit to be 1 T0CONbits.TMR0ON = 0; //Stop Timer0 While(1); } ``` - 關於 Timer interrupt 的部份去看實驗。 ::: ### Timer1 - 就只能用 16-bit ,包含 **TMR1H** 、 **TMR1L** 來存。 - **TMRIF** 在 PIR1 register 裡面,當 Timer1 overflow 時會變成 1 。 - 在從 0xFFFF to 0x0000 時也需要 extra clycle 。 - 可由 T1CON 控制。 :::info ![](https://i.imgur.com/zQ3umim.png) ::: #### Timer1 Operation - 可作為 timer 、 同步或非同步 counter ,可由 **TMR1CS** 決定:若為 0 則使用 internal clock ,若為 1 則在每一個 external clock 的 risting edge 加 1 。 - 有一個 on-chip 的 crystal oscillator circuit 可由 **T1OSCEN** bit 來開啟,他的頻率是 32 kHz ,而 Timer1 可由 **TMR1ON** bit 開啟。 #### Timer1 Interrupts - 當 TMR1 register pair ( TMR1H:TMR1L ) overflow 時,**TMR1IF** 會變成 1 來代表 interrupt 發生。 - Timer1 的 interrupt 可以由 TMR1IE ( bit 0 of PIE1 ) 來開啟。 :::info PIR1 ![](https://i.imgur.com/lSWaL8s.png) --- PIE1 ![](https://i.imgur.com/CtZbxxp.png) ::: :::danger 注意這邊計算 delay 時要記得加 extra cycle (就是他會自己把 TMR1H:TMR1L 變成 0 ,雖然你沒有打這段程式碼,但也要算一個 instruction cycle) ::: - 後面也是一些實做範例,要注意的就是前面的 config 要 OFF 一些東西,跟 Timer0 的一樣。 :::warning 要記得 assemble 跟 C 的 config 寫法不一樣 assemble ```c= config WDT = OFF; ``` C ```c= #pragma config WDT = OFF ``` ::: ### Timer2 - 包含 8-bit timer register 和 8-bit peroid register ( TMR2 and PR2 )。 - 是唯一一個 Timer 裡面有 postscaler 的,但 Timer2 只能作為 timer 使用。 - 可以由 *T2CON* register 來控制,當 *T2CON* 被清 0 時也等於 Timer2 停止。 :::info ![](https://i.imgur.com/C3fWTsW.png) ::: #### Timer2 Operation - *PR2* register 通常會被初始化成一個特定的數字,而當 TMR2 到達那個數字的時候(當然中間會經過 prescaler and postscaler), **TMR2IF** bit 就會變成 1 ,並把 TMR2 歸 0 。 :::warning 要注意 1. **TMR2IF** bit 是在 *PIR1* register 裡面。 2. 在 interrupt code 中要記得把 **TMR2IF** 歸 0 。 ::: #### Timer2 Interrupt 1. Set **PEIE** and **GIE** in *INTCON* register to 1. 2. Set **TMR2IE** bit in *PIE1* register to 1. 3. Clear **TMR2IF** bit in *PIR1* register to 0. 4. Clear *TMR2* register to 0. 5. Initialize *PR2* and *T2CON* with appropriate data. 6. 第五點中要記得包含把 Timer2 打開那個 bit ( in T2CON ) ,不然就在這步驟做。 7. 再來就等他 interrupt , PC 裡面就會讀進 0x08 這個 address 了。 :::info 當你發現你的 prescaler and postscaler 都用到最大值還到達不了他的要求時,就要使用另一個變數來做 counter 了。 ::: - 後面也是一堆實做,單都是關於 delay 的,不過如果考試沒有限制的話直接用內建的 delay function 好像就可以了吧? ### Timer3 - 可以作為 16-bit 的 timer or counter (也只能作為 16-bit 使用)。 - 透過 *T3CON* register 來控制,通常跟 CCP mudule 有關。 :::info ![](https://i.imgur.com/z63nQQN.png) ::: #### Timer3 Operation - 可以調整 **TMR3CS** bit 來決定他要用啥 mode ,有 timer/synchronous counter/asynchronous counter 可使用(但 synchronous 或 asynchronous 要自己寫,他只幫你決定 input source 是 internal 或是 external 而已)。 #### Timer3 Interrupt - 當 TMR3 register pair ( TMR3H:TMR3L ) overflow 時, **TMR3IF** bit ( bit 1 of PIR2 )會被設為 1 ,然後 TMR3 register pair 會被清 0 。 - 如果要使用 interrupt 記得要開 **TMR3IE** bit ( bit 1 of PIE2 ) 。 :::info PIR2 ![](https://i.imgur.com/n3TPwhn.png) PIE2 ![](https://i.imgur.com/IeSEdjj.png) ::: :::danger 注意,如果同一個 program 裡面用到多個 timer interrupt 時,因為他們都會跳到 0x08 這個 address ,所以在 0x08 這邊要寫一個 check 來檢查到底是哪一個 timer interrupt 了。 ::: ## Analog Interface - 完整的Analog Interface 應該包含 A/D and D/A converter ,但 PIC18F4321 只有包含 on-chip A/D converter , D/A chip 要外接。 ### PIC18F on-chip ADC ( A/D Converter ) - PIC18F 的 on-chip A/D converter 提供 8-bit or 10-bit 。 - ADC 的 characteristics 包含以下幾項: 1. Resolution (解析度) - 就看 8-bit 還是 10-bit 輸出,輸出的範圍越廣,代表在同一個區間的電壓可以被分割的越多份,解析度就越高。 - PIC18F4520 應該就只是分割 0V ~ 5V 的電壓。 2. Reference voltage (參考電壓) - 用來區分要劃分電壓的 maximum 和 minimum ,要先給一個參考的最大值與最小值,劃分才會有意義。 - Voltage input/output 分別為 Vref+/Vref- 。 3. ADC channels (A/D converter 通道) - ADC hardware module 連到一個 multiplexer 來區分 channel ,每一個 channel 都可以決定要不要連到 analog voltage 來進行 A/D convert ,像 PIC18F 有 13 個 channel ,從 AN0 ~ AN12 。 4. Acquisition time (採樣時間) - 當決定一個 ADC channel 時,他需要一段時間來把裡面的一個 capacitor 充好電,這個時間就叫 acquisition time 。 - 當達到 acquisition time 時, PIC18F 會斷開 channel 與 source 的連結,然後開始 conversion 。 5. ADC clock - 就一次 A/D conversion 的週期 Tad 。 - 每一個 channel 都可以 convert 出 8-bit or 10-bit 的 output 。 #### 8-bit output A/D conversion - 有 3 個 control register ( *ADCON0* ~ *ADCON2* ),加上 2 個 data registers ( *ADRESH*, *ADRESL* ),全部皆為 8-bit registers 。 1. ADCON0 register - 用來設定要哪一個 channel。 - 用來啟動 ADC conversion 。 - 用來開啟 ADC conversion 。 :::info ![](https://i.imgur.com/Fx6LKnQ.png) ::: :::warning 注意, **ADON** bit 只是 enable 而已,要設定 **GODONE** A/D converter 才會開始動作。 ::: 2. ADCON1 register - 用來設定 reference voltage 要從哪邊讀。 - 用來設定 AN0 ~ AN12 哪一些要 analog input 哪一些要 digital I/O 。 :::info ![](https://i.imgur.com/p97ykwd.png) ::: 3. ADCON2 register - 用來設定 output 要 Right/Left justified ( Right 代表全部靠右, ADRESH 有 2-bit ADRESL 有 8-bit )。 - 用來設定 ADC acquisition time 。 - 用來設定 A/D conversion clock 。 :::danger 關於 left justified 上面寫適用於 8-bit 的 output ,因為他會只把 8-bit 的結果存入 ADRESH 中,而 ADRESL 中的結果直接捨棄掉,跟我在實驗課聽到的不一樣?? ::: - 不論是組合語言或 C 都可以寫。 :::info - 在 C 中的 ADCON0bits.GO = ADCON0bits.DONE ,兩種寫法效果同等。 ::: #### A/D interrupt 1. Set **ADIE** bit in *PIE1* to 1 . 2. Clear **ADIF** bit in *PIR1* to 0 . 3. Set **PEIE** bit in *INTCON* to 1 . 4. Set **GIE** bit in *INTCON* to 1 . 5. Start A/D conversion by setting **GO/DONE** bit in *ADCON0* to 1 . (不用開 ADON 嗎?) - 當一次的 A/D conversion 結束時, **ADIF** bit in *PIR1* 會被設為 1 。 - 如果想設定 A/D interrupt 是 low priority 的話可以調整 **ADIP** bit in *PIR1* 。 #### Interfacing an external D/A Converter - 就把讀進去的 digital 在用某個 port ( 8-bit ) 傳出去,然後再去看 external device 的 datasheet 看要怎麼設定吧?