|
|
View previous topic :: View next topic |
Author |
Message |
jov_damo86
Joined: 29 Nov 2007 Posts: 9 Location: Sarrat, Ilocos Norte,Philippines
|
|
Posted: Thu Jan 10, 2008 5:21 am |
|
|
int32 Ticker;
int8 Seconds=0;
//optional:
// int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;
////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 2001 - 2099
////////////////////////////////////////////////////////////////////////////////
#define IS_LEAP(year) (year%4 == 0)
////////////////////////////////////////////////////////////////////////////////
// Initialize RTC
////////////////////////////////////////////////////////////////////////////////
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
// exactly every 65536 clock cycles
// (about 76 times per second)
enable_interrupts( INT_TIMER1 ); // Start RTC
}
////////////////////////////////////////////////////////////////////////////////
// -=Process Zero Drift Real Time Clock Information=-
//
// Most algorithms configure the timer to generate an interrupt every 100ms, and
// then count the number of interrupts. The problem in that approach is that most
// clock frequencies can't be divided by 256 and you don't get an exact 100ms.
// The small errors will add up to an error of several seconds a day.
//
// The algorithm presented here is exact in the long run because it doesn't
// count the number of interrupts but counts the number of clock cycles.
////////////////////////////////////////////////////////////////////////////////
#int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
}
/* --- Optional part start ---
if(Seconds == 60) {Minutes++; Seconds=0;
if(Minutes == 60) {Hours++; Minutes=0;
if(Hours == 24) {Days++; Hours=0;
if ( (Days == 29 && Month==2 && !IS_LEAP(Year))
|| (Days == 30 && Month==2)
|| (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))
|| (Days == 32)
) {Month++;Days=0;}
if(Month == 13) {Year++; Month=0;}
}}}
--- Optional part end --- */
}
Wow great! it was very accurate...i tried it! thanks for the code...
But,any code to modified:
int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;
or seconds using a push button
Thanks in advance! |
|
|
nachomanz
Joined: 17 Jan 2008 Posts: 1
|
|
Posted: Thu Jan 17, 2008 7:30 am |
|
|
Hi,
Can I use this code to measure milliseconds or microseconds? How can I do it?
Thanks!! |
|
|
jov_damo86
Joined: 29 Nov 2007 Posts: 9 Location: Sarrat, Ilocos Norte,Philippines
|
|
Posted: Sun Feb 10, 2008 1:47 am |
|
|
I guess you can modify the program by multiplying the seconds to make into smaller units:
int8 millisecond, microsecond
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
millisecond=seconds*1000; //in millisecond
microsecond=seconds*1000000 //in microsecond
}
for the conversion:
1000 millisec=1second
1^6 microsec = 1 second |
|
|
LourenceGuo
Joined: 14 Feb 2008 Posts: 7 Location: China, Shenzhen
|
|
Posted: Thu Feb 14, 2008 6:58 pm |
|
|
I tried this code with 8MHz crystal with PIC16LF873A, it works.
But the when I let the clock work over 12 hours, it is 6seconds faster than my computer, I am not sure if my computer clock is so precise, but is always work well.
I add some function to test the time: a 2*16 LCD display, and a temperature sensor to measure the temperature, three keys to set the time.
And I modified a little in the:
--- Optional part start ---
The begining value for Days and Month can not be 0, so I changed it to 1.
Code: | ///////////////////////////////////////////////////////////
// Zero Drift Real Time Clock
// Original code supplied by Neutone.
// Some small optimizations by C.Kielstra.
///////////////////////////////////////////////////////////
#include <16F873A.h>
#device ADC=10
#use delay(clock=8000000)
#fuses HS,NOWDT,NOLVP,NOBROWNOUT
#include "lcd.c"
//RTC variables
#define XTAL_FREQUENCY 8000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4) // 1 clock tick = 1 instr. cycle = crystal frequency / 4
int32 Ticker;
int8 Seconds=0;
//int8 Seconds_H=0;
//int8 Seconds_L=0;
int8 Minutes=0;
int8 Minutes_H=0;
int8 Minutes_L=0;
int8 Hours=0;
int8 Hours_H=0;
int8 Hours_L=0;
int8 Days=0;
int8 Days_H=0;
int8 Days_L=0;
int8 Month=0;
int8 Month_H=0;
int8 Month_L=0;
int8 Year=0;
int8 Year_H=0;
int8 Year_L=0;
float ad_value;
signed int ad_result;
int8 bk_counter;
int8 cursor_position=1;
int8 pre_cursor_position;
int8 number_count=0;
int8 my_pre_second;
int1 key_flag=0;
int1 set_key_flag=0;
int1 up_key_flag;
int1 down_key_flag;
char disp_char[]={'0','1','2','3','4','5','6','7','8','9'};
void time_display();
void Initialize_RTC(void);
void TIMER1_isr();
void key_detection();
void temp_ad();
void temp_display();
#define SET_OK_KEY input(PIN_C0)
#define UP_KEY input(PIN_C1)
#define DOWN_KEY input(PIN_C2)
#define BK PIN_B0
#priority TIMER1, TIMER0//set interrupt priority - timer1, timer0
//optional:
// int8 Year=0,Month=0,Days=0,Hours=0,Minutes=0;
////////////////////////////////////////////////////////////////////////////////
// Test whether a given year is a leap year.
// This optimized version only works for the period 2001 - 2099
////////////////////////////////////////////////////////////////////////////////
#define IS_LEAP(year) (year%4 == 0)
////////////////////////////////////////////////////////////////////////////////
// Initialize RTC
////////////////////////////////////////////////////////////////////////////////
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
// exactly every 65536 clock cycles
// (about 30.5 times per second)
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2); // setup interrupts
enable_interrupts( INT_TIMER1 ); // Start RTC
enable_interrupts(int_timer0); // Enables the interrupt Timer0
setup_adc_ports(AN0_AN1_VSS_VREF); //setup adc channel: AN0, AN1; VREF_VSS as refference voltage
setup_adc(ADC_CLOCK_INTERNAL); //use internal clock source
setup_timer_2(T2_DISABLED,0,1);
SETUP_CCP1(CCP_OFF);
SETUP_CCP2(CCP_OFF);
SETUP_SPI(SPI_SS_DISABLED);
setup_comparator(NC_NC_NC_NC);
//output_float(PIN_A0);
//output_float(PIN_A1);
output_float(PIN_A2);
//output_float(PIN_A3);
output_float(PIN_A4);
output_float(PIN_A5);
output_float(PIN_C3);
output_float(PIN_C4);
output_float(PIN_C5);
output_float(PIN_C6);
output_float(PIN_C7);
}
////////////////////////////////////////////////////////////////////////////////
// -=Process Zero Drift Real Time Clock Information=-
//
// Most algorithms configure the timer to generate an interrupt every 100ms, and
// then count the number of interrupts. The problem in that approach is that most
// clock frequencies can't be divided by 256 and you don't get an exact 100ms.
// The small errors will add up to an error of several seconds a day.
//
// The algorithm presented here is exact in the long run because it doesn't
// count the number of interrupts but counts the number of clock cycles.
////////////////////////////////////////////////////////////////////////////////
#INT_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt, Ticker=Ticker-65535
if ( Ticker < 65536 ) // If second has expired
{
Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second,Ticker=Ticker+65536
seconds++; // Increment number of seconds
}
// --- Optional part start ---
if(Seconds == 60)
{
Minutes++; Seconds=0;
if(Minutes == 60)
{
Hours++; Minutes=0;
if(Hours == 24)
{
Days++; Hours=0;
if ( (Days == 29 && Month==2 && !IS_LEAP(Year))
|| (Days == 30 && Month==2)
|| (Days == 31 && (Month==4 || Month==6 || Month==9 || Month==11 ))
|| (Days == 32)
) {Month++;Days=1;}
if(Month == 13) {Year++; Month=1;}
}
}
}
// --- Optional part end ---
}
#int_rtcc
void clock_isr()
{
if(key_flag==1)
{
output_high(BK);
if (seconds != my_pre_second)
{
my_pre_second = seconds;
bk_counter++;
}
else if(bk_counter==5)
{
output_low(BK);
key_flag=0;
bk_counter=0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Example program for using the RTC
////////////////////////////////////////////////////////////////////////////////
void main()
{
//int8 prev_second;
Initialize_RTC();
enable_interrupts( GLOBAL );
lcd_init();
output_low(BK);
delay_ms(10);
// loop forever
while(TRUE)
{
time_display();
key_detection();
temp_ad();
temp_display();
//sleep();
//delay_ms(10);
}
}
void key_detection()
{}
void time_display()
{}
//*****************************
void temp_display()
{}
void temp_ad()
{ } |
_________________ All things are difficult before they are easy. |
|
|
Neutone
Joined: 08 Sep 2003 Posts: 839 Location: Houston
|
|
Posted: Fri Feb 15, 2008 12:49 pm |
|
|
LourenceGuo wrote: | I tried this code with 8MHz crystal with PIC16LF873A, it works.
But the when I let the clock work over 12 hours, it is 6seconds faster than my computer, I am not sure if my computer clock is so precise, but is always work well. |
I would guess that your crystal oscillator circuit has a problem. Make sure you have the right capacitor values. You may need a resistor in the circuit for improved stability. I would be surprised if your PC is off by 6 seconds in 12 hours. |
|
|
LourenceGuo
Joined: 14 Feb 2008 Posts: 7 Location: China, Shenzhen
|
|
Posted: Fri Feb 22, 2008 9:00 pm |
|
|
Thank you for the reply!
The crystal I used is 8MHz, HC-49S, but the load capacitor and ppm is
not sure. The capacitor I used is pf, there is no resistor in parallel or serial connection with the crystal.
Please kindly help to figure out how to connect the resistor to stablize the crystal, and what is the value ? Because the design we made before is not so critical for the crystal circuit.
And I also will try to use a clear load capacitor and ppm crystal to try the time precision.
The computer I use, I think have battery to hold the time on the mainboard, so the time for computer will never stopped.
Thank you! _________________ All things are difficult before they are easy. |
|
|
hayee
Joined: 05 Sep 2007 Posts: 252
|
|
Posted: Tue Jun 24, 2008 1:11 am |
|
|
Hi Everyone,
U guys r really doing well. I have seen ur RTC code and tried it But i have a problem in this. I have add lcd also for display but it is not working properly. When i add this line to the code
Code: |
printXY_LCD(prev_second,1,1);
|
the clock is not working and when i remove this line and show results on pc through serial port this works fine. What is wrong in my code ? Am i initializing lcd wrong or is there any other problem ?
Code: |
#include "RTC Timer1.h"
#include "string.h"
char str[];
#include "lcd.c"
#define XTAL_FREQUENCY 20000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4)
int32 Ticker;
int8 Seconds=0;
int8 Minutes=0,Hours=0;
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
// exactly every 65536 clock cycles
// (about 76 times per second)
enable_interrupts( INT_TIMER1 ); // Start RTC
}
#int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
}
if(Seconds == 60)
{
Minutes++;
Seconds=0;
}
if(Minutes == 60)
{
Hours++;
Minutes=0;
}
if(Hours == 24)
{
Hours=0;
}
}
void main()
{
int8 prev_second;
Initialize_RTC();
enable_interrupts( GLOBAL );
{
initLCD();
delay_ms(dly);
// loop forever
while(1)
{
if (seconds != prev_second)
{
prev_second = seconds;
printf(" %d:%d:%d \r\n",Hours,Minutes,prev_second);
printXY_LCD(prev_second,1,1);
delay_ms(dly);
}
}
}
}
|
|
|
|
hayee
Joined: 05 Sep 2007 Posts: 252
|
|
Posted: Fri Jun 27, 2008 10:55 pm |
|
|
Hi Everyone,
I have solved the problem.It was an initializing problem.
Thanx |
|
|
sjharris
Joined: 11 May 2006 Posts: 78
|
|
Posted: Mon Jul 07, 2008 8:30 am |
|
|
I would like to use this code with a 16F877, have made the code and simulated it in Labcenter electronics and it seems to drift -3 minutes in 15 minutes of operation.
So I programmed into a PIC and tried it in circuit but I can't get it to work. Does the 16f877 need an external oscillator on it to make the internal clock work??
TIA
SJHarris |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Oct 21, 2008 3:45 pm |
|
|
A late response but I don't like open questions.
sjharris wrote: | Does the 16f877 need an external oscillator on it to make the internal clock work?? | The PIC16F877 has no internal clock, and even if it did it wouldn't be accurate enough for a clock application. Use an external crystal. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
hardware can do a nice job on this and FAR more ROBUST |
Posted: Sat Nov 22, 2008 2:03 pm |
|
|
I did a project over a year ago that required fail safe time keeping no matter what happened to the pic ( watchdog , batt change, powerdown etc) and am much more in favor of using parts like the ST micro M41T94MQ6E, which is a fine 2nd generation+ , free standing RTC clock, with its own 32khz crystal.
It includes extra non volatile ram - so can be used with a family parts to add NV storage and is communicated with using SPI .
my project had to remember the time no matter what happened in restart etc land and also had to be a deterministic design, so no interrupts were used.
I see a lot of crazy bending AND FOLDING of pic code in this post , when a bit of extra HARDWARE - controlled by the PIC can do a MUCH better and more robust job of it . and with a small diode coupled ultracap, can KEEP telling accurate RealTime for many MINUTES - while batteries are changed
if you want REAL time keeping use a REAL time keeping chip ;-))
just my 2 cents |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Sat Nov 22, 2008 4:16 pm |
|
|
Yes, a hardware RTC is a better solution for a lot of applications. I guess, the other forum members participating in this thread did already know about. |
|
|
volcane
Joined: 23 Feb 2008 Posts: 29
|
Re: Timer based Real Time Clock (RTC) |
Posted: Sun Mar 15, 2009 5:50 pm |
|
|
Quote: | #int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
} |
it is better change this part of code:
Code: | #int_TIMER1
void TIMER1_isr()
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker = TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
} |
In this way the increase of the seconds occurs every 76 cycles equal to 0996s , first cycles ranged from 76 to 77 |
|
|
mutthunaveen
Joined: 08 Apr 2009 Posts: 100 Location: Chennai, India
|
it works perfectly on my 16F877 |
Posted: Sat Dec 12, 2009 12:53 am |
|
|
Thanks for your code..... thank you very much.
Now I am calculating for the drift. |
|
|
nukeman
Joined: 04 Jan 2010 Posts: 3
|
|
Posted: Mon Jan 18, 2010 11:43 am |
|
|
I'll admit I'm new to PIC programming, but I'm pretty experienced with math in general and I've figured out a way to save a little memory when using this algorithm. I'm sure this isn't generally as much of a concern when using a PIC that has 8k words or more memory, but if you're trying to do a lot of stuff at once with one of the smaller chips, saving every byte you can is very handy.
The basic principle involves first noting that the TIMER1_FREQUENCY value and 65536 can generally be divided by some meaningfully large power of 2. In my particular implementation, TIMER1_FREQUENCY is 1000000, so both values can be divided evenly by 64. This means we're now decrementing Ticker by 1024 each time instead of 65536 and TIMER1_FREQUENCY becomes TIMER1_FREQUENCY_BY_64, or 15625. If we do this, it means we can now declare Ticker as an int16 instead of an int32.
I believe in total this saves at least 2 bytes of RAM and 6 to 8 words of ROM. Of course, it isn't quite as pretty to look at. For anyone who chooses to make this change, I recommend commenting it well enough so you can figure out later what you did. At the very least, comment out anything you change so if you're looking at it later you can figure out what it used to look like. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|