|
|
View previous topic :: View next topic |
Author |
Message |
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
problem delay function by timer0 |
Posted: Sun Aug 12, 2012 2:24 pm |
|
|
hi guys,
I'm doing a project that counts seconds and minutes on 4-digit 7SD.
Seconds and minutes are generated using timer0 , but in the main function i was forced to use delay_ms function to avoid the flickering on the 7SD, then the project worked 100%.
But my plan was not to use "delay" function, I wanted to work with the timing using real time.
However I tried so many things to solve this but all didn't make sense.
So how can I make delay function using timer0 like the one ccs uses, I mean
for example if I need 50ms delay, I would use "mydelay_ms".
A clear example:
Code: |
output_high(pin_b0); //turn on led on B0
mydelay_ms(50); //wait 50 ! using timer0
|
I hope if I can get help from anyone, please guys, and thank you all.
This is the code that works but using delay:
Code: |
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use Delay(clock=4000000)
//values to be displayed on 7SD 0-9 in hex(lookup table)
byte const display[10]={0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10};
byte c1=0, c2=0, c3=0, c4=0, high_count;
//HIGH_START: number of interrupts every sec: (F/4)/ Prescalar / 2^8 = 61(16393us) so every second 61 interrupts are generated.
#define HIGH_START 61
//this isr function is entered on each timer0 overflow(0-255)
#INT_RTCC
void clock_isr()
{
if(--high_count==0) //decrease high_count from 61 to 0 on every interrupt, when high_count reaches 0, it means 1Sec
{
++c1; //increment c1(first digit seconds) every second
high_count=HIGH_START; //reinitiallising high_count to HIGH_START(61)
}
}
void main()
{
high_count=HIGH_START; //high_count gets 61
set_rtcc(0); //timer0 starts counting from 0
setup_counters(RTCC_INTERNAL, RTCC_DIV_64); //using internal instruction, using pre-scalar of 64
enable_interrupts(INT_RTCC); //enable timer0 interrupt
enable_interrupts(GLOBAL); //enable global interrupt
for(;;)
{
if(c1==10) //each time c1(first digit seconds) reaches 10
{
c2++; //increment c2(second digit seconds)
c1=0; //reset c1(first digit seconds)
if(c2==6) //if c2(second digit seconds) reaches 6, that means minute counter will start
{
c3++; //increment c3(first digit minutes)
c2=0; //reset c2(second digit seconds)
}
if(c3==10) //if c3(first digit minutes) reaches 10
{
c4++; //increment c4(second digit minutes)
c3=0; //reset c3(first digit minutes)
}
}
//send high to C0 to select c1(first digit seconds), then display its value
output_c(0b00000001);
output_b(display[c1]);
delay_ms(10);
//send high to C1 to select c2(second digit seconds), then display value
output_c(0b00000010);
output_b(display[c2]);
delay_ms(10);
//send high to C2 to select c3(first digit minutes), then display its value
output_c(0b00000100);
output_b(display[c3]);
delay_ms(10);
//send high to C3 to select c4(second digit minutes), then display its value number from lookup table
output_c(0b00001000);
output_b(display[c4]);
delay_ms(10);
}//endless loop
}//main
|
And this is the code, I tried to avoid using delay but didn't work!!!:
Code: |
#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use Delay(clock=4000000)
//values to be displayed on 7SD 0-9 in hex(lookup table)
byte const display[10]={0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10};
byte c1=0, c2=0, c3=0, c4=0, high_count, high_count_2, t1=0;
//HIGH_START: number of interrupts every sec: (F/4)/ Prescalar / 2^8 = 61(16393us) so every second 61 interrupts are generated.
#define HIGH_START 61
#define HIGH_START_2 30 //half a second 61/2=30(50ms)
//this isr function is entered on each timer0 overflow(0-255)
#INT_RTCC
void clock_isr()
{
if(--high_count==0) //decrease high_count from 61 to 0 on every interrupt, when high_count reaches 0, it means 1Sec
{
++c1; //increment c1(first digit seconds) every second
high_count=HIGH_START; //reinitiallising high_count to HIGH_START(61)
}
if(--high_count_2==0)//decrease from 30 to 0, on each interrupt
{
++t1;
high_count_2=HIGH_START_2;
}
}
void main()
{
high_count_2=HIGH_START_2;
high_count=HIGH_START; //high_count gets 61
set_rtcc(0); //timer0 starts counting from 0
setup_counters(RTCC_INTERNAL, RTCC_DIV_64); //using internal instruction, using pre-scalar of 64
enable_interrupts(INT_RTCC); //enable timer0 interrupt
enable_interrupts(GLOBAL); //enable global interrupt
for(;;)
{
loop:
if(c1==10) //each time c1(first digit seconds) reaches 10
{
c2++; //increment c2(second digit seconds)
c1=0; //reset c1(first digit seconds)
if(c2==6) //if c2(second digit seconds) reaches 6, that means minute counter will start
{
c3++; //increment c3(first digit minutes)
c2=0; //reset c2(second digit seconds)
}
if(c3==10) //if c3(first digit minutes) reaches 10
{
c4++; //increment c4(second digit minutes)
c3=0; //reset c3(first digit minutes)
}
}
//send high to C0 to select c1(first digit seconds), then display its value number from lookup table
output_c(0b00000001);
output_b(display[c1]);
//delay_ms(10);
if(t1==10){ //if 10ms , do below
//send high to C1 to select c2(second digit seconds), then display its value number from lookup table
output_c(0b00000010);
output_b(display[c2]);
//delay_ms(10);
}
if(t1==20){ //after 20ms , do below
//send high to C2 to select c3(first digit minutes), then display its value number from lookup table
output_c(0b00000100);
output_b(display[c3]);
//delay_ms(10);
}
if(t1==30){after 30ms, do below
//send high to C3 to select c4(second digit minutes), then display its value number from lookup table
output_c(0b00001000);
output_b(display[c4]);
//delay_ms(10);
}
if(t1==40) t1=0; goto loop; //now after 40ms, reset t1 start over
}//endless loop
}//main
|
|
|
|
NePe
Joined: 21 Jun 2011 Posts: 9
|
|
Posted: Sun Aug 12, 2012 2:57 pm |
|
|
Are you using external clock for timer0?
Try something like that:
Code: |
void my_delay(int16 time) {
set_timer0(0);
while(get_timer0()!=time);
} |
|
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Sun Aug 12, 2012 3:12 pm |
|
|
Quote: |
Try something like that:
|
better yet - dont
The compare is of an 8 bit timer with a 16 bit count with your 16f877 PIC in YOUR app.
It is only going to work if it is an 18F part (used in 16bit timer0 mode) - NOT a 16F part as timer0 is only 8 bits. |
|
|
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
|
Posted: Sun Aug 12, 2012 4:35 pm |
|
|
NePe wrote: | Are you using external clock for timer0?
Try something like that:
Code: |
void my_delay(int16 time) {
set_timer0(0);
while(get_timer0()!=time);
} |
|
I'm using internal clock.
I will try your method but can you please clarify more about your method, and by the way I'm using an 8bit timer0 so I think int16 will not work here!
Thanks guys. |
|
|
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
|
Posted: Sun Aug 12, 2012 5:21 pm |
|
|
NePe thank you I tried it and it worked 100%,
I changed your code to:
Code: |
void my_delay(int8 time)
{
while(t1 != time); t1=0;
}
|
I'm just wondering about "while(get_timer0()!=time);" does this hold the cpu if the timer is not equal to the variable "time" ??
Because as think this line says if the timer counter is not equal time stay here, not really sure. |
|
|
NePe
Joined: 21 Jun 2011 Posts: 9
|
|
Posted: Sun Aug 12, 2012 7:43 pm |
|
|
Code: |
void my_delay(int8 time) {
set_timer0(0);
while(get_timer0()!=time);
}
|
Its simply checks the timer value until its reach the value of the time variable.
Code: |
void my_delay(int8 time)
{
while(t1 != time); t1=0;
}
|
I'm not really understand your code. Where the t1 variable comes from?
Quote: |
I'm using internal clock.
|
When you are using same clock for the cpu and the timer, no difference between accuracy of them. Why do you want to use timers for delay ? |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Mon Aug 13, 2012 1:59 am |
|
|
Well, there are plus's and minus's. Delay_xx has the minus of being blocking ( until it times out the main loop code is blocked). Delay_ms has the plus is that it is easy to use.
Now a timer with an ISR or just a test in code to see the timer count will be non blocking. The plus is that main code loop can be running as the timer counts out. The minus is that it is a bit more complicated to get the timer set right to give the exact delay needed.
A non blocking example could be the need to make a short event visible say data is received over the CAN bus. The CAN receive isr sets flag for the delay timer isr to turn on a LED. The timer isr turns on the LED and when it times out it turns it off. The main code isn't blocked by the LED timer and can process the CAN bus data received. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Mon Aug 13, 2012 3:58 am |
|
|
You've got a rounding off error in your time generation.
You could lift something from this code which solves the problem.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177
Like DK says there are pros and cons. With an ISR you can have (within limits) as many timers going as you want, without your code getting locked up.
OK. So you don't actually need one for your simple clock, but you are obviously looking to do something more advanced in the future and can see the need to avoid delay_ms(xx).
Mike |
|
|
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
|
Posted: Mon Aug 13, 2012 10:16 am |
|
|
NePe wrote: | Code: |
void my_delay(int8 time) {
set_timer0(0);
while(get_timer0()!=time);
}
|
Its simply checks the timer value until its reach the value of the time variable.
Code: |
void my_delay(int8 time)
{
while(t1 != time); t1=0;
}
|
I'm not really understand your code. Where the t1 variable comes from?
Quote: |
I'm using internal clock.
|
When you are using same clock for the cpu and the timer, no difference between accuracy of them. Why do you want to use timers for delay ? |
"t1" is actually a micro second counter to count each timer0 overflow.
i just wanted to avoid using "delay" because it hold the CPU from doing anything unless the delay given is passed, so i was planing to use timer0 to handle this but instate it can do other task until given time is passed. |
|
|
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
|
Posted: Mon Aug 13, 2012 10:23 am |
|
|
Mike Walne wrote: | You've got a rounding off error in your time generation.
You could lift something from this code which solves the problem.
http://www.ccsinfo.com/forum/viewtopic.php?t=26177
Like DK says there are pros and cons. With an ISR you can have (within limits) as many timers going as you want, without your code getting locked up.
OK. So you don't actually need one for your simple clock, but you are obviously looking to do something more advanced in the future and can see the need to avoid delay_ms(xx).
Mike |
Thanks Mike I saw the link and found it really valuable worth testing. I'm actually gonna modify it to use it in my timer project.
You were right about the rounding off error, when I compared my timer with my computer clock it's not accurate after like 6 sec.
But can the code in the link be used in my project since I'm using an 8bit timer?
Thanks again mike.
and thanks to everybody else. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Tue Aug 14, 2012 1:58 am |
|
|
Quote: | But can the code in the link be used in my project since I'm using an 8bit timer? |
Yes, of course it can.
There are posts in the thread which explain how to do it.
In your case you simply exchange the 65536 (the number of machine cycles per interrupt) with your value of 64*256 = 16384. The effect will be that the seconds count advances after 61 interrupts most times and occasionally after 62. There will be a small amount of jitter, which you're unlikely to notice.
Mike
EDIT
Quote: | You were right about the rounding off error, when I compared my timer with my computer clock it's not accurate after like 6 sec. |
Even with the code you've got you should not be seeing such a gross error. Have a close look at your oscillator.
(1) Are you using a ceramic resonator or a crystal?
(2) Have you got all the recommended Rs and Cs fitted?
(3) Does it look stable on a 'scope? |
|
|
semmoor
Joined: 09 May 2012 Posts: 46 Location: KSA
|
|
Posted: Wed Aug 15, 2012 1:53 pm |
|
|
Mike Walne wrote: | Quote: | But can the code in the link be used in my project since I'm using an 8bit timer? |
Yes, of course it can.
There are posts in the thread which explain how to do it.
In your case you simply exchange the 65536 (the number of machine cycles per interrupt) with your value of 64*256 = 16384. The effect will be that the seconds count advances after 61 interrupts most times and occasionally after 62. There will be a small amount of jitter, which you're unlikely to notice.
Mike
EDIT
Quote: | You were right about the rounding off error, when I compared my timer with my computer clock it's not accurate after like 6 sec. |
Even with the code you've got you should not be seeing such a gross error. Have a close look at your oscillator.
(1) Are you using a ceramic resonator or a crystal?
(2) Have you got all the recommended Rs and Cs fitted?
(3) Does it look stable on a 'scope? |
Sorry Mike, I was busy.
I did the code with an 8bit timer0 as you suggested, and it worked
Thank you Mike.
By the way I'm using 4M crystal and I put 22Pf capacitor and it's working fine, I tested it. |
|
|
|
|
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
|