View previous topic :: View next topic |
Author |
Message |
random guy
Joined: 02 Jun 2007 Posts: 11
|
timer0() |
Posted: Mon Jun 04, 2007 10:06 pm |
|
|
hello, i want to use the timer (timer0) for the 16f690 in order to keep track of the amount of time in between interrupts (in this case the inturrupt happens when a button is pressed). when the inturrupt is executed it should read the time of timer0 and average it with a preset delaylength that is used to delay the length of leds shining in main() but for some reason it doesnt do that. it just ends up going amazingly slow, more than the length of time i spend in between inturrupts. delaylength is an global variable.
here is my code.
thanks for the help.
EDIT: ccs vr. 4.013
Code: | #include "C:\Program Files\PICC\Projects\16f690.h"
#int_RA
#fuses INTRC_IO,NOPROTECT, NOWDT, NOMCLR
#use delay (clock=20000000)
int delaylength;
RA3_isr()
{
int8 c;
int time;
//get the time since last call to isr
time=get_timer0();
//set clock to zero
set_timer0(0);
//average old dleay with new one
delaylength=((delaylength+time)/2);
c = input_a();
}
void main()
{
int c;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
c=input(PIN_A3);
clear_interrupt(INT_RA3);
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
setup_oscillator(False);
set_timer0(0); //set timer to zero
delaylength=2; //an initial value
//loop the leds 0-1-2-3-2-1-0
while (1)
{
output_high(PIN_C0);
delay_ms(delaylength);
output_low(PIN_C0);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C3);
delay_ms(delaylength);
output_low(PIN_C3);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
}
}
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Tue Jun 05, 2007 2:25 am |
|
|
Are you sure your processor is running at 20MHz? I didn't check but think such high frequencies are not supported for the RC clock option. |
|
|
random guy
Joined: 02 Jun 2007 Posts: 11
|
|
Posted: Tue Jun 05, 2007 9:32 am |
|
|
well i am kinda new to this but i looked at the beginning of the data sheet
http://ww1.microchip.com/downloads/en/devicedoc/41262A.pdf
and on page 3 it says
Quote: | Operating speed:
- DC – 20 MHz oscillator/clock input |
so i used that as the clock. is there somewhere else i should check? |
|
|
Ttelmah Guest
|
|
Posted: Tue Jun 05, 2007 10:25 am |
|
|
Note the word _input_.
The INT_RC oscillator, is the _internal_ oscillator. It runs at 8MHz _max_. You can run the chip at '20MHz', but only by feeding it with an external 20MHz oscillator.
With the current settings, the compiler cannot satisfy your specified clock rate, using the internal oscillator, so the default rate will be selected. 32KHz...
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 05, 2007 2:38 pm |
|
|
The latest 16F690 data sheet is the Rev. D version.
http://ww1.microchip.com/downloads/en/DeviceDoc/41262D.pdf
You can get the latest data sheet by going to the "Data Sheet Finder"
page on the Microchip website. There's a link to it at the top of the
main page:
http://www.microchip.com
Then use the drop-down box to get to the page for the 16F690.
Download the data sheet, and look in Section 3.0 on the Oscillator Module.
There are several drawing in there that show the frequency choices
for the internal oscillator. The highest freq is always shown at 8 MHz. |
|
|
random guy
Joined: 02 Jun 2007 Posts: 11
|
|
Posted: Tue Jun 05, 2007 9:13 pm |
|
|
hmm well i changed the clock to
#use delay (clock=8000000)
but it still was experiencing the same problem of seemingly not continueing the led sequence. in my last test i tried dividing the variable time (should be the amount of time since the start to the inturrupt and then from there on in between inturrupts) by a large number
delaylength=((delaylength+(time/10000000000000000000000000))/2);
still took a while. i waited like 2 or 3 min and it continued the led sequence after that long amount of time staying at that point in the sequence. after that the variable delay didnt seem to be changed (kept blinking at same speed) and inturrupt button didnt do anything after that.
here is the code. thank you for the help.
Code: | #include "C:\Program Files\PICC\Projects\16f690.h"
#int_RA
#fuses INTRC_IO,NOPROTECT, NOWDT, NOMCLR
#use delay (clock=8000000)
int delaylength;
RA3_isr()
{
int8 c;
int time;
//get the time since last call to isr
time=get_timer0();
//set clock to zero
set_timer0(0);
//average old dleay with new one
delaylength=((delaylength+(time/10000000000000000000000000))/2);
c = input_a();
}
void main()
{
int c;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
c=input(PIN_A3);
clear_interrupt(INT_RA3);
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
setup_oscillator(False);
set_timer0(0); //set timer to zero
delaylength=2; //an initial value
//loop the leds 0-1-2-3-2-1-0
while (1)
{
output_high(PIN_C0);
delay_ms(delaylength);
output_low(PIN_C0);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C3);
delay_ms(delaylength);
output_low(PIN_C3);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
}
}
|
EDIT: it does seem kinda weird that a delay of 2 miliseconds (or at least it should be) isnt super fast. the leds are staying bright and pausing for less than a second but staying much longer than the small time of 2 miliseconds. i dont so much mind what units the time is stored in or how long it thinks a milisecond is so long as it is consistant so that i can average the amounts of time between inturrupts and have the speed of the leds blinking minic that speed. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 05, 2007 10:13 pm |
|
|
Quote: | delaylength=((delaylength+(time/10000000000000000000000000))/2); |
You need to learn the datatypes in CCS. You can't just do anything.
CCS will only work with specific data types, and each data type can
hold a specified maximum value.
Download the CCS manual.
http://www.ccsinfo.com/downloads/ccs_c_manual.pdf
Read the section on Data Types. Note that the largest integer type
is an int32 (unsigned).
Run the calculator that comes with Windows. Type in 2, the press
the x^y key. Then type in 32 and press the = key. That's the largest
number that can be put into an int32.
If you want a language in which you can do anything (or at least, a lot),
then you want Basic.
C is constrained. You have to learn the rules.
C tutorials:
http://www2.its.strath.ac.uk/courses/c/
http://cslibrary.stanford.edu/101/EssentialC.pdf
Note that CCS is not full ANSI C. It's a subset of that, with some
extensions (invented by CCS) for the PIC. |
|
|
random guy
Joined: 02 Jun 2007 Posts: 11
|
|
Posted: Tue Jun 05, 2007 11:23 pm |
|
|
well that was more or less just a test to see if it was counting microseconds, nanoseconds or something. since adding delaylength (which was 2) to time (some number) and dividing it by 2 was producing such a large number i was assuming from a mathematical standpoint that time must be a large number. but there seems to be something else wrong. i get that feeling because after the one long delay the value of delaylength isnt changed nor does the code enter the inturrupt after that (or doesnt seem to since it doesnt produce the same result as the first time it is pressed or the result that i am seeking).
i tried removing the division and making time a 32 bit int just in case whatever form the timer is counting up by might produce a number too big for 8 bits. didnt work though. hope the overflow isnt so bad that it doesnt fit in 32 bits, would explain why it is acting contrary to how it should though.
thanks for the help so far and the reading material, may be usful to brush up on my c and such. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Jun 06, 2007 2:53 am |
|
|
Code: | setup_oscillator(False); | This will disable the internal oscillator. I'm surprised your code is doing anything at all. Remove this line.
Code: | #int_RA
#fuses INTRC_IO,NOPROTECT, NOWDT, NOMCLR
#use delay (clock=8000000)
int delaylength;
RA3_isr()
{ | Keep the line #int_RA as close as possible to the function start, don't put other unrelated keywords in between as this makes the code hard to read and could confuse the compiler.
It's good programming practice to declare variables with keywords that define the exact variable type. You are mixing both int8 and int which are the same in the CCS compiler but when porting to another compiler the int specifier might be equal to a signed int32.
Code: | delaylength=((delaylength+(time/10000000000000000000000000))/2);
| As PCM already told you this is not going to work, the large number is larger than what fits in an int32. Even so, for an integer division the result of time (an int8) divided by the large number will equal 0. This means that after a few interrupts your averaged delaylength will go to zero as well.
Your previous averaging algorithm was better: Code: | delaylength=((delaylength+time)/2); | But this has a potential problem of int8 overflow in the addition. Prevent the overflow by casting one of the int8 variables to an int16: Code: | delaylength= ( ((int16)delaylength + time) / 2); |
Check the timer0 rate. I don't know what is connected to pin_A3 but you might have overflow problems. At 8MHz and a prescaler of 256 Timer0 will increment every 128us, having an 8 bits size it will overflow every 32ms.
Compiler version 4.013 is to be considered a beta release and has many known problems. Around release 4.030 the v4.xxx compiler became more or less useable. Save everyone a lot of time by downgrading to v3.249 (the last stable release and still available for download from the website) or upgrade to at least v4.030.
Note: I'm surprised by the large number of people all using the same old 4.013 release. CCS must have sold a lot of these without upgrade support... |
|
|
random guy
Joined: 02 Jun 2007 Posts: 11
|
|
Posted: Wed Jun 06, 2007 8:08 pm |
|
|
ok great those tips really helped, A3 is a button actually, at least for the moment. later i am going to exchange it with a light sensor and set it up such that i can pick up the reflections of water droplets dripping at a fast rate (almost a constant stream of water but be separated enough to be individual drops).
when i test it out now the delay length is definetly changing which is good. but i do have the overflow issue since it isnt being activated within 32ms each time (i think that is the only problem). anyway i can change it to allow for a longer amount of time, maybe by changing it from 8 bit to 32 bit. 32ms might be pushing it a little with when i switch to the other form of input, definetly too short for the tests i am doing now with it being a button press. a few seconds would be good.
i am trying to build up hte application gradually such that i dont get overly confused. here is the latest revision of my code in case anyone wanted to see.
Code: | #include "C:\Program Files\PICC\Projects\16f690.h"
#fuses INTRC_IO,NOPROTECT, NOWDT, NOMCLR
#use delay (clock=8000000)
int delaylength;
#int_RA
RA3_isr()
{
int8 c;
int32 time;
//get the time since last call to isr
time=get_timer0();
//set clock to zero
set_timer0(0);
//average old delay with new one
delaylength= ( ((int32)delaylength + time) / 2);
c = input_a();
}
void main()
{
int c;
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
c=input(PIN_A3);
clear_interrupt(INT_RA3);
enable_interrupts(INT_RA3);
enable_interrupts(GLOBAL);
set_timer0(0); //set timer to zero
delaylength=2000; //an initial value
//loop the leds 0-1-2-3-2-1-0
while (1)
{
output_high(PIN_C0);
delay_ms(delaylength);
output_low(PIN_C0);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C3);
delay_ms(delaylength);
output_low(PIN_C3);
delay_ms(delaylength/2);
output_high(PIN_C2);
delay_ms(delaylength);
output_low(PIN_C2);
delay_ms(delaylength/2);
output_high(PIN_C1);
delay_ms(delaylength);
output_low(PIN_C1);
delay_ms(delaylength/2);
}
}
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Thu Jun 07, 2007 4:39 am |
|
|
Code: | int32 time;
//get the time since last call to isr
time=get_timer0(); | timer0 is 8 bit. assigning to an int32 is overkill and a waste of resources. Same applies to the cast to an int32 you are doing, an int16 will do the job. Why did you change my example code?
Code: | delaylength=2000; //an initial value | delaylength is an int8, the value of 2000 doesn't fit and will be truncated to 2000/256 = 7.
An easy way to extend the 32ms timer0 period is by lowering the clock frequency. To do this change the #use delay() directive to the desired clock frequency, for example 1MHz. Check chapter 3.5.4 of the PIC16F690 datasheet for all allowed clock frequencies. |
|
|
Ttelmah Guest
|
|
Posted: Thu Jun 07, 2007 5:16 am |
|
|
Actually, as a small 'comment', 2000, will give 208, not 7. It is 2000%256, that gets stored.
Best Wishes |
|
|
|