|
|
View previous topic :: View next topic |
Author |
Message |
picman62
Joined: 12 Dec 2014 Posts: 77
|
Interrupt affecting main code |
Posted: Tue Feb 10, 2015 4:43 pm |
|
|
I've got a problem with a frequency counter not working right when the LED starts blinking.
I have this LED in a TIMER0 interrupt and the frequency counter in the main code. When the interrupt is excluded, the frequency counter works precisely. But when the LED interrupt is engaged, the frequency counter becomes erratic with eg. in a signal of 5KHz is read as 5722Khz.
There's an int_rb interrupt that calls both counter and LED when button B7 is pressed.
Code is below.
Please, what is the correct approach of getting this right?
Thanks in advance.
Code: |
#include <16f877a.h>
#fuses hs,nowdt,nocpd,nolvp,noprotect
#use delay(clock=20M)
#use fast_io(c)
BYTE signal = 0;
#include <LCD.C>
#include <LCD_bargraph.c>
short int led0;
#int_timer0
void TimerLED()
{
led0 = !led0;
output_bit (pin_c1,led0);
set_timer0(303 + get_timer0()); // get_timer() //value for LED rate
}
#int_rb
void button_isr()
{
if( !input(PIN_B7) && !signal )
signal = 1;
else
if( !input(PIN_B7) && signal )
signal = 0;
}
void main()
{
set_tris_c(0b00000001);
enable_interrupts(global);
enable_interrupts(int_timer0 );
enable_interrupts(INT_RB);
LCD_init();
while(true)
{
INT1 SW4;
BOOLEAN ISPRESSED_KEY4=FALSE; // Boolean logic=0;
if ( (SW4 && !ISPRESSED_KEY4) )
{
ISPRESSED_KEY4=TRUE;
if ((cont==1)||(cont==2)||(cont==3))
do
{
if(signal)
{
set_timer1(0);
setup_timer_1(t1_external | T1_DIV_BY_1);
delay_ms(1000);
setup_timer_1(T1_DISABLED);
value=get_timer1();
if(Cont==1)
{
printf (lcd_putc,"\fFREQ %LU\r\n",value);
A=value/200;
B=value/300;
C=value/400;
D=value/10000;
}
if(value>=counter-(3) && value<=counter+(2))
{
bargraph(A);
setup_timer_0 ( RTCC_INTERNAL | RTCC_DIV_64);
}
}
}
while(signal);
if (!SW4) ISPRESSED_KEY4=FALSE;
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 10, 2015 5:16 pm |
|
|
Check the PIC data sheet. What's wrong in the items shown in bold
below ? What's the size of Timer0 ?
Quote: | #include <16f877a.h>
#fuses hs,nowdt,nocpd,nolvp,noprotect
#use delay(clock=20M)
#use fast_io(c)
BYTE signal = 0;
#include <LCD.C>
#include <LCD_bargraph.c>
short int led0;
#int_timer0
void TimerLED()
{
led0 = !led0;
output_bit (pin_c1,led0);
set_timer0(303 + get_timer0()); // get_timer() //value for LED rate
} |
|
|
|
picman62
Joined: 12 Dec 2014 Posts: 77
|
|
Posted: Tue Feb 10, 2015 5:35 pm |
|
|
Thanks, you're right. Should be 256 max. I was playing with the led rate and got lost in the value limit.
I just set it to 6 (256-6), but no change. Frequency counter offset value decreased but still showing something like 5180Hz for a 5KHz signal.
Something else is affecting its precision.
You've mentioned the PIC datasheet. But what info should I search for?
Also, did you find something wrong in the instructions I wrote, besides the TIMER0 value?
Regards. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9215 Location: Greensville,Ontario
|
|
Posted: Tue Feb 10, 2015 5:48 pm |
|
|
hmm.... I've looked a few times but......
you have a variable named 'value' ,
in your code 1/2 way down...
value=get_timer1();
yet I can't see where you've originally delcared it.
CCS C doesn't really like 'mid program declarations, though it might 'assume' it's an 8 bit variable. if so then...
B=value/300;
C=value/400;
D=value/10000; will give bad answers.
just ideas...
Jay |
|
|
picman62
Joined: 12 Dec 2014 Posts: 77
|
|
Posted: Tue Feb 10, 2015 6:56 pm |
|
|
Well, I spent all day away from my protoboard and was simulating everyting in PT.
I just got home and put the code to test in the hardware. Actually the frequency counter is indeed working. But not the LED. When pinB7 button is pressed, counter comes in but LED just light up. It does not blink at all. I changed the prescaler and the timer0 initial value. Nothing.
Maybe this is the reason why PT was showing the frequencies completely offset.
Either the LED is not really flashing or it is flashing so fast that it is not happening.
Whatever the reason, it might be related to how the code is written. That's why I asked PCM programmer if he thinks it's ok or not.
Anyway, this code for timer0 interrupt originally featured a 18F2550 PIC. I believe it's got a 16bit timer0. But as I am using values in the 256 limit, I see no problems, right? It must be something else.
Temtronic, the code is working fine with the declarations as they are. The only issue I have right now is really the LED. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 10, 2015 7:22 pm |
|
|
Quote: |
But when the LED interrupt is engaged, the frequency counter becomes
erratic with eg. in a signal of 5KHz is read as 5722Khz. |
That's because the Timer0 interrupt is occurring repeatedly during the
delay_ms(1000) execution time. Each interrupt injects maybe 15 usec
of additional delay. I don't know what your Timer0 interrupt rate is.
If you were interrupting at 10 KHz, then you would lengthen the
delay_ms(1000) by 150 ms more. Your external clock counter would
have a longer time to count pulses. It will give you a higher count.
That is what you are seeing.
If you don't want interrupts interfering with your frequency counter code
then disable interrupts before entering the code, and re-enable them after:
Quote: |
disable_interrupts(GLOBAL);
set_timer1(0);
setup_timer_1(t1_external | T1_DIV_BY_1);
delay_ms(1000);
setup_timer_1(T1_DISABLED);
value=get_timer1();
enable_interrupts(GLOBAL);
|
Your LED and your INT_RB won't work for 1 second. I don't know if this
is important to you or not. I suggest do whatever is the most simple
thing to quickly finish your program. Do a solution that is simple and
that you can understand. If my proposed solution above works, then
use it. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19477
|
|
Posted: Wed Feb 11, 2015 2:03 am |
|
|
The real key is to get away from the idea of using delay_ms, as a timing source. It's never going to be accurate if your processor is doing anything else.
Create a background 'tick' interrupt. Some useful interval. Use the count of this as your timing, rather than delay_ms.
As PCM_programmer says, you can stop the interrupts as an alternative,, but then (of course) you lose any arriving serial data.
I suggest instead of trying to get a timing of the LED, by updating the count, you use timer2, which can be set to give accurate timings.
So (for instance)
setup_timer2(T2_DIV_16,249,10);
Will give an interrupt 125 times a second.
Then if your LED wants to flash every second:
Code: |
#define PER_SECOND 125
int8 count;
#int_timer0
void TimerLED()
{
static int8 led_count=(PER_SECOND-1);
if (led_count)
--led_count;
else
{
output_toggle(PIN_C1);
led_count=(PER_SECOND-1);
}
if (count)
--count;
}
|
Then to delay, just set 'count' to the count required, and wait till it gets to zero. To get accurate timings, you'd ideally wait till the timer update occurs, before waiting. So:
Code: |
//With the above timer, for one second:
count=1;
while (count);
//Now trigger the start event for your detection 'set timer' etc..
count=(PER_SECOND-1);
while(count);
//get here one second later.
|
Experiment. |
|
|
picman62
Joined: 12 Dec 2014 Posts: 77
|
|
Posted: Wed Feb 11, 2015 8:21 am |
|
|
I wrote another code only featuring the timer0 interrupt and the counter instruction in the main. It worked now.
I used Ttelmah's code, but another similar code also worked.
Code: |
#include <16f877a.h>
#fuses hs,nowdt,nocpd,nolvp,noprotect, brownout
#use delay(clock=20M)
#use fast_io(c)
#include <LCD.C>
#define PER_SECOND 125
int count;
#int_timer0
void TimerLED()
{
static int8 led_count=(PER_SECOND-1);
if (led_count)
--led_count;
else
{
output_toggle(PIN_C1);
led_count=(PER_SECOND-1);
}
if (count)
--count;
}
unsigned int16 value;
void main()
{
set_tris_c(0b00000001);
enable_interrupts(global);
enable_interrupts(int_timer0 );
setup_timer_0(RTCC_INTERNAL | RTCC_DIV_64);//for three cases I have prescalers of 8, 16 and 64
LCD_init();
while(1)
{
set_timer1(0);
setup_timer_1(t1_external | T1_DIV_BY_1);
delay_ms(1000);
setup_timer_1(T1_DISABLED);
value=get_timer1();
lcd_gotoxy(11,4);
printf(lcd_putc,"\f%LU HZ ",value);
}
}
|
Note that I did not feature any button.
So the problem appears to reside in my code somewhere.
I suspect this could be the #int_rb. When button is pressed, counter and LED are on, but LED is inside a TIMER0 interrupt... Maybe this is the problem, since I have read here somewhere that an interrupt should not be inside another interrupt. I will include the int_rb interrupt next and will report back.
PCM programmer wrote: |
If you don't want interrupts interfering with your frequency counter code
then disable interrupts before entering the code, and re-enable them after:
|
Thanks. I did this, but LED remained lit (no flashing) and counter suffered a heavy offset from the real frequency reading.
Telmah wrote: |
Then to delay, just set 'count' to the count required, and wait till it gets to zero. To get accurate timings, you'd ideally wait till the timer update occurs, before waiting.
Experiment.
|
Thanks.
My LED time rate is not critical at all. I simply set it for fast blink when a given freq is read and slower blink when it offsets from that freq by some value. I also plan to use the TIMER2 for PWM, so I guess I can use TIMER0 for your instruction.
I noticed that if I change prescaler value in TIMER0 for faster or slower LED flashing, the frequency counter value is affected.
For a faster blink, frequency offsets higher, for a slower blink, the opposite happens.
Is there any way to avoid this condition? |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Feb 11, 2015 11:42 am |
|
|
picman62 wrote: | I wrote another code only featuring the timer0 interrupt and the counter instruction in the main. It worked now.
.........
.........
I used Ttelmah's code, but another similar code also worked.
My LED time rate is not critical at all. I simply set it for fast blink when a given freq is read and slower blink when it offsets from that freq by some value. I also plan to use the TIMER2 for PWM, so I guess I can use TIMER0 for your instruction.
I noticed that if I change prescaler value in TIMER0 for faster or slower LED flashing, the frequency counter value is affected.
For a faster blink, frequency offsets higher, for a slower blink, the opposite happens.
Is there any way to avoid this condition? |
You've got me confused.
In one sentence you say it works, then later there's a problem.
Please clarify.
Mike |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Feb 11, 2015 11:56 am |
|
|
Another problem is you don't know enough about C or program design
or debugging methods to be able to solve your problems. Every time
we try to help you, it typically doesn't work, and you can't fix it.
It either doesn't work or you're dissatified with it. So every one of your
threads ends in disappointment. Then everyone walks away and forgets
it until you do the next thread. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19477
|
|
Posted: Wed Feb 11, 2015 1:15 pm |
|
|
He is also missing the vital point of my code.
The point was to do the sample timing using the 'count' value, so that the time for sample would not change as other things happened in interrupts. Instead he has just used the LED flash part of it and kept the standard delay.... |
|
|
picman62
Joined: 12 Dec 2014 Posts: 77
|
|
Posted: Wed Feb 11, 2015 2:35 pm |
|
|
Yes, I don't know enough C to solve my problems. But I am striving. I knew nothing in early december. Thanks to some reading in this forum, today I am in the end part of my first code which is not so simple.
Mike, when I say it works, I was refering to the LED code in the TIMER0 interrupt I got from Ttelmah. The previous one did not.
Back to the topic, I inserted the #int_rb interrupt instruction and it also seemed to work in the 'interrupt code' I wrote to test this particular piece..
So I will go back to my routine to track this issue and make it work there too.
Ttelmah wrote:
Quote: |
He is also missing the vital point of my code.
The point was to do the sample timing using the 'count' value, so that the time for sample would not change as other things happened in interrupts. Instead he has just used the LED flash part of it and kept the standard delay....
|
Exactly. This would be my next question to you. I couldn't figure where to insert this last part of your code.
Code: |
//With the above timer, for one second:
count=1;
while (count);
//Now trigger the start event for your detection 'set timer' etc..
count=(PER_SECOND-1);
while(count);
//get here one second later.
|
Thanks in advance. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Feb 11, 2015 3:21 pm |
|
|
picman62 wrote: |
Mike, when I say it works, I was refering to the LED code in the TIMER0 interrupt I got from Ttelmah. The previous one did not.
| I'm still no wiser.
You need to marshal your thoughts.
Make a simple, clear, concise list of what needs to happen.
Show us the latest version of your code.
Explain what works and precisely what is wrong with what does not.
Simply dabbing code in an ad-hoc fashion will get you nowhere.
At this stage you've got us all guessing.
Mike |
|
|
picman62
Joined: 12 Dec 2014 Posts: 77
|
|
Posted: Thu Feb 12, 2015 4:00 am |
|
|
Mike Walne wrote: |
You need to marshal your thoughts.
Make a simple, clear, concise list of what needs to happen.
Show us the latest version of your code.
Explain what works and precisely what is wrong with what does not.
Simply dabbing code in an ad-hoc fashion will get you nowhere.
At this stage you've got us all guessing.
Mike |
Very well then.
I could trace the syntax issue in my code and now LED works along with the counter as desired.
So my list of help now resumes only to fix the counter offset problem. How to set the frequency counter correctly to work with interrupts.
As I am using delays for the counter, it's being affected by the time rate the LED flashes.
The LED is not critical to my project. But the counter is. The more precise it is, the better.
Now I see that this counter using delays, only work if it's not disturbed by any other instructions which also involves time. Also, in this counter, the resolution is dependent of gate time of the delay(1000ms).
This is why I found Ttelmah's suggestion interesting. Because it would count ticks and sounds like similar to the reciprocal counting approach where instead of counting the PIC's input signal, it counts the periods of the master clock.
I would apprecitate if Ttelmah could give details of the second part of his code above and how it would be written for my case.
I would like to keep the LED in timer0 as it is and use TIMER1 for the counter task. TIMER2 will be used for my last task , PWM.
Thanks for this help in advance.
Regards. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19477
|
|
Posted: Thu Feb 12, 2015 4:10 am |
|
|
Replace:
Code: |
set_timer1(0);
setup_timer_1(t1_external | T1_DIV_BY_1);
delay_ms(1000);
setup_timer_1(T1_DISABLED);
value=get_timer1();
//With
setup_timer_1(t1_external | T1_DIV_BY_1);
count=1;
while (count)
; //wait for interrupt
count=(PER_SECOND-1);
set_timer1(0); //start the count
while(count)
; //wait for one second
setup_timer_1(T1_DISABLED);
value=get_timer1();
|
With the timer interrupt setup as I originally posted.
However the interrupt needed to be on timer2, not timer0. I forgot to change this in my post.
If you want to change the LED flash rate, don't change the timer rate, but change the value used to re-load led_count.
You can still use the PWM with timer2 being used like this, it'll just run at 1250Hz. If this isn't suitable, then calculate the changes needed.
The point is that timer2, can be set to give an exact division by a factor (16*250*10), which gives exactly 125 interrupts per second, allowing you to get an accurate time. It's the only timer on these earlier PIC's that allows this sort of accuracy. |
|
|
|
|
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
|