Group Blog
 
<<
มีนาคม 2554
 12345
6789101112
13141516171819
20212223242526
2728293031 
 
13 มีนาคม 2554
 
All Blogs
 
Tip&Trick PIC Microcontroller, 1.000 mS Timer0's Interrupt (In-depth explanation)

วันนี้เขียนโปรแกรมนาฬิกาเล่นๆ ตั้งใจจะให้ความละเอียดของเวลามีค่า 1mS
นั่งคำนวณเวลาอยู่หลายรอบ ไม่ลงตัวสักที ทั้งๆ ที่รู้อยู่ในใจว่าไม่มีทางจะลงตัว ที่ XTAL 8MHz
และใช้ Prescaler เท่ากับ 1:8 แน่นอน ค่านี้เป็นค่าที่เหมาะสมสำหรับ Timer0 (8-bit)
ที่ XTAL 8MHz ไม่มีอะไรที่เป็นไปไม่ได้ดังนั้นต้องหาลู่ทางกันต่อไป
ด้วยโครงสร้างของ PIC16F และหลายๆ ตัว FCY=FOSC/4 นั่นคือ 1 Machine cycle (MC) จะมีค่าเท่ากับ
4/8MHz ซึ่งเท่ากับ 0.5uS ห่างกันตั้ง 2,000 เท่าเมื่อเทียบกับเวาลาที่ต้องการ คือ 1mS แบบนี้ก็หมูๆแล้ว
กลับไปพิจารณากันที่โปรแกรมต่อ มองอะไรไม่ค่อยออก งั้นเข้าไปดูใน Assembly แล้วกัน ชัดเจนเลย
แบบนี้ก็ต้องชดเชยค่าจากการคำนวณกันหน่อย เพราะการ Interrupt ก็ต้องเสียเวลาเล็กน้อย ก่อนที่
CPU จะเข้ามาใน ISR นั่นคือ 4 MC ตามด้วยการ save context (เรื่องนี้ว่างๆ จะมาเล่าให้ฟัง เพราะน่าสนใจ
สำหรับคนที่เล่น RTOS หรือนักพัฒนา Real time Kernel ทั้งหลาย) อีก 9 MC ตามมาด้วยการ Check ว่ามี
การเปิดใช้งาน Interrupt อยู่จริงหรือเปล่า ตรงนี้เสียไปอีก 2 MC เท่านั้นยังไม่พอ การ re-load Timer
ต้องทำแบบ Manual (ไม่ได้เป็น Auto-Reload) ทำให้เสียไปอีก 4 MC รวมกันทั้งหมดก็ปาเข้าไป 19 MC
นั่นก็คือ 2 Clock ของ Timer (2*8 MC) กับอีก 3 MC นอกจากนี้อย่าลืมว่าหลังจาก Timer ถูกเขียน
ค่าใหม่ลงไป การนับจะเริ่มใหม่ และเวลาจะเริ่มจาก MC ต่อไป แน่นอน ก็ต้องเสียไปอีก 1 MC
สรุปแล้วตอนนี้ เศษเหลือคือ 4 MC หาคำสั่งที่ใช้ 1 MC มาใส่ลองไป 1 คำสั่ง (ทำซ้ำ 4 ครั้ง) ในที่นี้เลือก
คำสั่ง nop (no operation)ที่เลือกเป็น asm() เพราะต้องการความมั่นใจว่า optimizer จะไม่ ignore คำสั่ง
ไร้ประโยชน์เหล่านี้ (ส่วนนี้ค่อยเล่าให้ฟังในส่วนของ Optimization)  ต่อไปคือ 2 Clock ของ Timer
หรือ 16 MC นั่นเอง ค่าเหล่านี้เรีกว่า "Overhead" ต่อไปก็ต้องทำการชดเชยลงไปที่ค่า reload ของ Timer
ในที่นี้คือ TIMER_VALUE แต่อย่าเผลอนะครับ Timer จะ Overflow เมื่อค่าวนกลับ สำหรับ Timer แบบ 8-bit
จะเกิด Overflow ขั้นเมื่อค่าของ Timer เปลี่ยนจาก 0xFF เป็น 0x00 นั่นเป็นตัวบอกว่าจะต้องชดเชยไปอีก 1 Clock
ของ Timer หรือ 8 MCY ผลที่ได้ก็เป็นไปตามโปรแกรม (ดูในส่วน define TIMER_VALUE) เก็บไว้ใช้ได้ตราบนานเท่านาน

ลงเชิงลึกไปหน่อยครับ แต่ในงานที่ต้องการความเที่ยงตรงสูงๆ สิ่งเหล่านี้เป็นเพียงเรื่องพื้นฐานเท่านั้น
หวังเป็นอย่างยิ่งว่าจะเป็นประโยชน์ (หรืออาจเป็นโทษ) กับท่านผู้อ่านนะครับ


/************************************************
* FILE: MAIN.C *
* CPU: PIC16F887 *
* XTAL: 8MHz *
* IDE: MPLAB IDE v8.63 *
* COMPILER: HI-TECH PIC-C Compiler vV9.71a *
* AUTHOR: SANTI NURATCH @ SHADOWWARES.COM *
* DATE: 13 March, 2011 *
* DESCRIPTION: *
* Using Timer0 and its interrupt. *
* The T0's interrupt is trigered every 1mS. *
*************************************************/

#include <htc.h>
//................................................................................

__CONFIG(INTIO & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT);
#define _XTAL_FREQ 8000000.0f /* Clock Source */
#define _FCY _XTAL_FREQ/4.0f /* CPU and Peripheral Clock */
#define TIMER_TICK_RATE 1000.0f /* System Clock Rate */
#ifdef _TIMING_DEBUG_
#define TIMER_PRESCALER 2.0f /* PRES=2, see in Init() */
#else
#define TIMER_PRESCALER 8.0f /* PRES=8, see in Init() */
#endif
#define TIMER_VALUE (256f+3f-(_FCY/TIMER_PRESCALER/TIMER_TICK_RATE)+0.5)
/*-4 is an overhead compensation */
//................................................................................
volatile unsigned int ISRTickCounter = 0;

//................................................................................

void SysInit(void){
TMR0 = (unsigned char)TIMER_VALUE;
GIE = 1; // Enable Global Interrupt
PEIE = 1; // Enables peripheral interrupts
OPTION = 2; // 1:8 T0's Prescaler
    #ifdef _TIMING_DEBUG_
OPTION = 0; // 1:2 T0's Prescaler
    #endif
T0IF = 0; // Clear interrupt flag
T0IE = 1; // Enable T0's interrupt
TMR0 = 0; // TIMER_VALUE
}

static void interrupt ISR(void) @ 0x004{
if(T0IE && T0IF){
asm("nop"); asm("nop"); /* Overhead compensation */
asm("nop"); asm("nop");
        #ifdef _TIMING_DEBUG_
TMR0 = -8; /* 16uS ISR Freq @ FOSC=8MHz, PRES=2*/
        #else
TMR0 = (unsigned char)TIMER_VALUE;
        #endif
T0IF = 0;
ISRTickCounter++;
}
}

void main(void){
SysInit();
while(1);
}



ต่อไปก็มาดูหลักฐานกันครับ ว่าทุกสิ่งทุกอย่างนั้นถูกจริงหรือเปล่า
ใช้  Oscilloscope มาวัดสัญญาณหรอ? ไม่ใช่หรอกเพราะมันบอกอะไรไม่ค่อยได้
ทำอย่างไรดี วิธีง่ายๆ และยืนยันได้ครับ ใช้ Simulator ที่ติดมากับ MPLAB นั่นแหละ
นับกันเป็น Machine Cycle ไปเลย จากข้อมูลของ Hardware ที่บอกไปด้านบน
ระยะเวลา 1mS ตะต้องใช้จำนวน Machine Cycle เท่ากับ 2,000 Cycles (2,000*0.5=1,000 = 1mS)
ผลการทำงาน ก็เป็นไปตามที่คำนวณทุกประการครับ ตามรูปด้านล่าง





--
By Santi
//www.shadowwares.com/forum


Create Date : 13 มีนาคม 2554
Last Update : 14 มีนาคม 2554 11:37:05 น. 0 comments
Counter : 1242 Pageviews.

TheInsight
Location :


[ดู Profile ทั้งหมด]

ให้ทิปเจ้าของ Blog [?]
ฝากข้อความหลังไมค์
Rss Feed
Smember
ผู้ติดตามบล็อก : 3 คน [?]




Links
 

 Pantip.com | PantipMarket.com | Pantown.com | © 2004 BlogGang.com allrights reserved.