IPB

Welcome Guest ( Log In | Register )

> Precise Timings With Timer 0, Deriving precise time delays with timer0
Shree
post Nov 16 2009, 07:32 AM
Post #1


Regular
*

Group: EstablishedMember
Posts: 64
Joined: 20-February 09
From: India
Member No.: 4,973



Hey Wizards,
I am trying to create a timer which would be on for the value entered in eeprom. This value along with a switch can work for mins as well as secs (i.e. if the switch is on, the eeprom value 'X' is X minutes else the X is X secs. This value is displayed on a 7 segment display (3 digits). I am implementing a 16F877A for this (Shame I am using a Heman's sword to kill an ant! But couldnt find a solution to drive all three displays at a time.). The program runs as per my expectation. The only problem though is I get a 1 sec error every minute (so 999 secs more for same mins!!!!). Below is the code I have written. I know my skill with the code writting is very primitive, but please can somebody help me by looking into it and let me know if there is any chance to get error free (or atleast unnoticeably low error) execution?
my software:

CODE
#include <system.h>
#include <Eeprom877A.h>
#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _WDT_OFF & _XT_OSC & _LVP_OFF & _PWRTE_OFF & _CP_OFF
unsigned char t=0, t1=0, t2=0, u=0, x=0, y=0, disp=0, disp1=0, disp2=0, units=0, tens=0, hundreds=0, z=0;
unsigned int dist=0;
bit grn=0;
void eeprom_load(void);
void display(void);

void interrupt(void)
{
    tmr0+=9; //Adjusting timer0 to count upto 250uSec only and not 256usec.
    t++;
    if(t>19)
    {                //1 second routine and if value of u is 60, it would count 60 seconds
        t=0;
        t1++; //Increaments after 20 interrupts i.e. 5ms
        if(t1>199)
        {
            t1=0;
            t2++; //Increaments after 200X20 interrupts i.e. 1 sec
            if(t2>u)
            {
                t2=0;
                dist--;
            }
        }
    }
    intcon.2=0; //TOIF
}

void main()
{
    Begin: unsigned char ux=0;
    u=ux;
    trisa=0x1b;
    adcon1=0x8e; //making all the pins on portA except AN0 as digital IO
    porta.2=1; //Power ON Indicator
    porta.5=0; //Relay pin
    trisc=0x00;
    portc=0x3f; //Display 0 on portC which is connected to LSB(units) digit.
    trisb=0x00;
    portb=0xbf; //Display 0 on portB which is MSB(hundreds) and b.7 is connected to cathode via trasistor, which means b.7 is display enable/disable pin
    trisd=0x00;
    portd=0x3f; //Displat 0 on portD which is the middle(tens) digit of display
    trise=0x07;
    if(porta.4==1)//porta.4 is a pin which when high makes the timer to enter programming mode where the delay time can be written in eeprom
    {
        eeprom_load(); //Function to write eeprom
        while(1){}
    }
    else
    {
        while(1)//Wait till the switch is pressed
        {
            if(porte.2==1) // Enable switch
            {
                delay_ms(64); //Switch debouncing
                while(porte.2==1)
                {}
                delay_ms(32);
                grn=1; //Flag
                goto frm;
            }
        }
        frm: if(grn==1)
        {
            read_eeprom(0x01); //From header file
            disp2=eedata;
            read_eeprom(0x00);
            disp1=eedata;
            dist=((disp2*100)+disp1);//After reading both locations the original value stored is constructed
                         //Look in the last part of eeprom_load function here disp1 is same as 'z'
            if(porta.1==1) //Choosing if the count is for seconds or minutes
            {
                u=60;
            }
            if(porta.1==0)
            {
                u=0;
            }    
            option_reg=0x08; //Configuring timer0 prescaler 1:1, CS: Internal clock
            tmr0=0x06;
            intcon.5=1; // Enable Timer0 interrupt
            intcon.7=1; //Enable global interrupt
            while(dist!=0)
            {
                portb.7=1; //Enable Display
                porta.5=1; //Make the relay on
                units=dist%10;
                disp=units;
                display();
                portc=x;
                tens=(dist%100)/10;
                disp=tens;
                display();
                portd=x;
                hundreds=dist/100;
                disp=hundreds;
                display();
                portb=x+128;
            }
            
            porta.5=0; //Relay OFF
            intcon.5=0; //TMR0 interrupt off
            intcon.7=0; //GIE off
            goto Begin; // Be ready for another count
        }
    }    

}

void eeprom_load()
{
    portb.7=1; //Enable Display
    delay_ms(128);
    while(1)
    {
        strt:
        if(porte.0==1) //Increaments the units display digit
        {
            delay_ms(64);
            while(porte.0==1){}
            delay_ms(32);
            disp++;
            if(disp>9)
            {
                disp=0;
            }
            display();
            portc=x;
        }
        if(porte.1==1) //Shifts the value in the tens digit
        {
            delay_ms(64);
            while(porte.1==1){}
            delay_ms(32);
            y++;
            if(y==1)
            {
                portc=0x3f;
                disp1=disp;
                display();
                portd=x;
                disp=0;
            }
            if(y==2)
            {
                portc=0x3f;
                disp2=disp1;
                disp1=disp;
                display();
                portd=x;
                disp=disp2;
                display();
                portb=x+128;
                disp=0;
            }
            if(y>=3)
            {
                y=0;
                disp=0;
                x=0;
                disp1=0;
                disp2=0;
                portc=0x3f;
                portd=0x3f;
                portb=0xbf;
                goto strt;
            }
        }
        if(porte.2==1) //The value is final for should be written to eeprom
        {
            delay_ms(64);
            while(porte.2==1){}
            delay_ms(32);
            z=((disp1*10)+disp); //Adding value in units and tens so that it could be stored in 1 eeprom location instead of 2 (as the value
            goto entry;          //wont exceed 255)
        }
    }
    entry:
    write_eeprom(0x00,z); //Writing to eeprom units and tens
    write_eeprom(0x01,disp2); //writing to eeprom hundreds digit
    disp1=0;
    disp2=0;
    disp=0;
}        

void display()
{
    if(disp==0)//Values are such that portX.0 is connected to 'a' segment and 'g' segment is connected to portX.6
    {
        x=0x3f;
    }
    if(disp==1)
    {
        x=0x06;
    }
    if(disp==2)
    {
        x=0x5b;
    }
    if(disp==3)
    {
        x=0x4f;
    }
    if(disp==4)
    {
        x=0x66;
    }
    if(disp==5)
    {
        x=0x6d;
    }
    if(disp==6)
    {
        x=0x7d;
    }
    if(disp==7)
    {
        x=0x07;
    }
    if(disp==8)
    {
        x=0x7f;
    }
    if(disp==9)
    {
        x=0x6f;
    }
}


CODE
/* A Header file which would do reading and writing EEPROM in 16F877A */

unsigned char read_eeprom(unsigned char read_addr)
{        
    eeadr=read_addr;
    eecon1.7=0; //resetting EEPGD bit to access data memory
    eecon1.0=1; //Enabling Read operation (RD bit high)
    return (unsigned char)eedata;
}

void write_eeprom(unsigned char write_addr, unsigned char write_data)
{
    intcon.7=0; //Turning off interrupts
    pir2.4=0; //EEIF flag bit is reset in case there was any write previously
    eeadr=write_addr;
    eedata=write_data;
    eecon1.7=0; //EEPGD bit to access data memory
    eecon1.2=1; //Enabling write operation by setting WREN bit
    eecon2=0X55; //REquisite
    eecon2=0XAA; //These are kinda passwords
    eecon1.1=1; //enabling WR bit
    while(eecon1.1!=0)
    {
    }
    eecon1.2=0; //WREN bit
}

Thanks in Advance
Regards
Shree

This post has been edited by Shree: Nov 17 2009, 11:21 AM
Reason for edit: added code tags around the code
update Picpack 3.0 Released
Go to the top of the page
 
+Quote Post
 
Start new topic
Replies
trossin
post Nov 18 2009, 07:00 PM
Post #2


Enthusiast
**

Group: Moderator
Posts: 161
Joined: 5-April 06
From: Colorado
Member No.: 1,034



A few years back we talked about the fact that TIMER 0 is not a good choice for a clock as there is an inherent error in reloading the counter. Since you are using a device that has newer timers (TIMER 2) you should use the new timer since you can set the compare value once and just recieve an interrupt at the desired rate. TIMER 0 is fine for keyboad debouncing but not for real-time clocks.

I attached Clock.c and Clock.h that implements a clock using timer 2 that I used for a timelapse photo machine ( http://www.tedrossin.x10hosting.com/Electronics/Pic/Pic.html ) . The code includes a fine adjust to calibrate out crystal errors. The parts per million error on a cyrstal > 1MHz is usually pretty bad (20 to 50) such that your clock will lose or gain 10 to 20 seconds a day. A tuned 32KHz cyrstal connected to be a timer source is another way to get a good timebase. These cyrstals have very low PPM errors.
This code has more than you want but should at least show you how to modify your code to use timer 2. If it is confusing, never mind as what you have may be good enough if you don't plan on leaving it running for more than a few days.

If you want the ulitimate in accuracy on the cheap, go for sampling the power line (dropped down in voltage of course) to get a very reliable 60 or 50 Hz depending on where you live. The power companies control the beats per day to keep good old synchonous clock motors on the right time.

I tried to search for the thread on this subject but could not find it. We had a blast a few year back about this issue.
Attached File(s)
Attached File  CLOCK.H ( 492bytes ) Number of downloads: 3
Attached File  CLOCK.C ( 4.24K ) Number of downloads: 3
 
Go to the top of the page
 
+Quote Post
ra68gi
post Nov 22 2009, 03:18 AM
Post #3


Super Enthusiast
***

Group: EstablishedMember
Posts: 227
Joined: 4-November 06
From: India
Member No.: 1,837



QUOTE (trossin @ Nov 19 2009, 01:30 AM) *
A few years back we talked about the fact that TIMER 0 is not a good choice for a clock as there is an inherent error in reloading the counter. Since you are using a device that has newer timers (TIMER 2) you should use the new timer since you can set the compare value once and just recieve an interrupt at the desired rate. TIMER 0 is fine for keyboad debouncing but not for real-time clocks.

I attached Clock.c and Clock.h that implements a clock using timer 2 that I used for a timelapse photo machine ( http://www.tedrossin.x10hosting.com/Electronics/Pic/Pic.html ) . The code includes a fine adjust to calibrate out crystal errors. The parts per million error on a cyrstal > 1MHz is usually pretty bad (20 to 50) such that your clock will lose or gain 10 to 20 seconds a day. A tuned 32KHz cyrstal connected to be a timer source is another way to get a good timebase. These cyrstals have very low PPM errors.
This code has more than you want but should at least show you how to modify your code to use timer 2. If it is confusing, never mind as what you have may be good enough if you don't plan on leaving it running for more than a few days.

If you want the ulitimate in accuracy on the cheap, go for sampling the power line (dropped down in voltage of course) to get a very reliable 60 or 50 Hz depending on where you live. The power companies control the beats per day to keep good old synchonous clock motors on the right time.

I tried to search for the thread on this subject but could not find it. We had a blast a few year back about this issue.


Hi Trossin,
I disagree that you can't get accurate timing with tmr0. As of the crystal error is concerned you may be right. But every one uses a crystal.
The sample code I had given in the thread "programming pic micro using boostc" is correct. I have checked it up using mplab stopwatch. And I have used it
as a real time clock & it works fine. Build the program in mplab sim, make a break point in the first line in the ISR, reset the stop watch on the first interrupt
& run it several times & you will see that the stop watch show 250us every time. The code tmr0 +=9 ; is all you need to add in the isr. Its a very simple code & it works.
Shree, the error u might have got could be because u did'nt reset the timer at the first interrupt. The first interrupt will have a over head of the initial port setting & register config settings.
Where u don't need to measure time intervals of 250us, Pommie's code is better because your code doesn't get interrupted often.

Regards
Raghunathan.

This post has been edited by ra68gi: Nov 22 2009, 03:41 AM
Go to the top of the page
 
+Quote Post
Shree
post Nov 23 2009, 07:21 AM
Post #4


Regular
*

Group: EstablishedMember
Posts: 64
Joined: 20-February 09
From: India
Member No.: 4,973



QUOTE
Shree, the error u might have got could be because u did'nt reset the timer at the first interrupt. The first interrupt will have a over head of the initial port setting & register config settings.
Where u don't need to measure time intervals of 250us, Pommie's code is better because your code doesn't get interrupted often.

Hello Mr. Raghunathan,
Well I had posted the error in the code earlier, and now I get a good timing (1.000000 secs) on MPLAB sim. About the first interrupt overhead, I had seen the problem and this could be sorted out by preloading the timer0 just before activating interrupts. The value could be calibrated while simulating. My preload value of 44 yeilded me a perfect 1 sec (atleast on mplab sim).
Regards
Shree

P.S: Why dont we make a pinned topic of web addresses for code snippets/harware projects which are based exclusively on boostC by eminent programmers like ted and many others who have worked around pic and done many projects, which they want to share with others. Anybody can give a link to their own web address too where he/she has done some job with pics. It might be of quite help. This clicked me when I saw ted rossin's site. What we could do is just provide link to these sites, but only condition would be all (or most) of the coding must be done in boostC.
Go to the top of the page
 
+Quote Post

Posts in this topic
- Shree   Precise Timings With Timer 0   Nov 16 2009, 07:32 AM
- - IanM   *** Edited in responce to Shree's edits above ...   Nov 16 2009, 09:44 AM
|- - Shree   Dear Ian, Accept my appologies for t...   Nov 16 2009, 11:22 AM
|- - Dave   QUOTE (Shree @ Nov 16 2009, 11:22 AM) 1. ...   Nov 16 2009, 12:37 PM
|- - IanM   QUOTE (Shree @ Nov 16 2009, 11:22 AM) ......   Nov 17 2009, 01:25 AM
|- - russ_hensel   Just a general comment on Ian's response. I t...   Nov 17 2009, 01:37 AM
- - Shree   Hey wizards Thanks for all ...   Nov 17 2009, 11:34 AM
- - Reynard   OK! Who's upsetting Reynard ? Shree, Rem...   Nov 17 2009, 12:10 PM
|- - Shree   Wow, Reynard that code was something!!...   Nov 17 2009, 12:28 PM
- - ra68gi   Hi Sree, I didn't have time to go through your...   Nov 17 2009, 12:32 PM
|- - Shree   QUOTE (ra68gi @ Nov 17 2009, 12:32 PM) Hi...   Nov 17 2009, 12:38 PM
|- - ra68gi   QUOTE (Shree @ Nov 17 2009, 07:08 PM) QUO...   Nov 17 2009, 01:22 PM
|- - ra68gi   Hey Sree, Once you enter into a while(1) loop in t...   Nov 17 2009, 02:00 PM
- - Reynard   Shree, Are you taking into account that seconds g...   Nov 17 2009, 01:08 PM
|- - Shree   QUOTE (Reynard @ Nov 17 2009, 01:08 PM) S...   Nov 17 2009, 02:45 PM
|- - IanM   Your timings are all too long by one part in 60? H...   Nov 17 2009, 03:18 PM
- - IanM   Hi, Shree I've been through your Final.c, put...   Nov 17 2009, 02:31 PM
- - Shree   Hello Wizards Finally found...   Nov 18 2009, 12:05 PM
- - trossin   A few years back we talked about the fact that TIM...   Nov 18 2009, 07:00 PM
|- - ra68gi   QUOTE (trossin @ Nov 19 2009, 01:30 AM) A...   Nov 22 2009, 03:18 AM
|- - Shree   QUOTE Shree, the error u might have got could be b...   Nov 23 2009, 07:21 AM
- - IanM   Thanks for that Ted. I am sure Shree will learn a ...   Nov 19 2009, 03:49 AM
- - Pommie   Another way to count 5mS is to keep track of how m...   Nov 19 2009, 05:34 AM
|- - Shree   Hey Wizards, Thanks again for...   Nov 19 2009, 07:44 AM
|- - IanM   QUOTE (Reynard (to Shree) @ Nov 19 20...   Nov 19 2009, 10:55 AM
- - Reynard   Hi Shree, As you are using a 16F877A device, why ...   Nov 19 2009, 09:53 AM
|- - ra68gi   QUOTE (Reynard @ Nov 19 2009, 04:23 PM) H...   Nov 22 2009, 04:57 AM
- - Shree   Hello Wizards, I understand...   Nov 19 2009, 01:00 PM
- - Pommie   Doing it the way I posted earlier will give you a ...   Nov 19 2009, 02:03 PM
- - IanM   QUOTE (Pommie @ Nov 19 2009, 02:03 PM) Do...   Nov 19 2009, 02:48 PM


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 



Lo-Fi Version Time is now: 3rd September 2010 - 03:00 PM