View Full Version : Is there a built in tick/millisecond counter?
Aalfabob
01-12-2004, 23:52
My first question is, are there any counters built into the main unit?
If there is, how would i access it?
Rickertsen2
02-12-2004, 00:24
My first question is, are there any counters built into the main unit?
If there is, how would i access it?
The pic has things called timers. they are as the name describes timers. they can be configured to count in just about whatever unit you want,and they can be used for alot more than just time. read the section of the datasheet about timers, and search the forums. There have been numerous discussions on timers. I think there may even be code on the IFI that shows an example of how to set up an interrupt based and a polled timer.
Kevin Watson
02-12-2004, 01:04
My first question is, are there any counters built into the main unit?
If there is, how would i access it?
The edu_clock example that does what you want can be found here: http://kevin.org/frc/. It's for the EDU-RC, but should be easy to adapt to the FRC-RC. This is the header of clock.c:
/************************************************** *****************************
*
* TITLE: clock.c
*
* VERSION: 0.3 (Beta)
*
* DATE: 18-Dec-2003
*
* AUTHOR: R. Kevin Watson
* kevinw@jpl.nasa.gov
*
* COMMENTS: This demonstrates the use of a timer and associated interrupt.
* In this example we'll setup a timer to generate an interrupt
* every millisecond (= 1000Hz rate). In response to this interrupt,
* the microcontroller will execute a specific piece of code called
* an interrupt handler (see user_routines_fast.c). This interrupt
* handler will simply increment the global variables "msClock" and
* "Clock". High-level user code can then access these variables to
* create sophisticated state machines and highly accurate sequencers.
* We'll just keep the time.
*
*
************************************************** ******************************
*
* Change log:
*
* DATE REV DESCRIPTION
* ----------- --- ----------------------------------------------------------
* 14-Dec-2003 0.1 RKW Original
* 16-Dec-2003 0.2 Accuracy bug: Changed the value of PR2 to 249 (was 250)
* 18-Dec-2003 0.3 Fixed 25 hour/day and 366 day/year bugs
*
************************************************** *****************************
Let me know if you have any trouble.
-Kevin
eugenebrooks
02-12-2004, 01:10
My first question is, are there any counters built into the main unit?
If there is, how would i access it?
Kevin Watson's site has example code for a timer for both the EDU and Competition RC. See www.kevin.org/frc
There is a hazard associated with reading a counter value that is maintained by the interrupt handler, but this is easily dealt with. See the section on interrupt programming in "An Introduction to C Programming for FIRST Robotics Applications," you will want the latest version from www.srvhsrobotics.org, in the Technical page.
Have fun!
Kevin Watson
02-12-2004, 03:36
There is a hazard associated with reading a counter value that is maintained by the interrupt handler Eugene is referring to one of the problems with using interrupts (and multitasking systems in general). The code in clock.c assumes that the timer 2 interrupt service routine won't update the value of "Clock" while the function Display_Time() accesses it. For this example the chances of this happening is pretty slim. In general this is something that should be taken into consideration to prevent a race condition. Possible solutions include briefly disabling interrupts while the variable is accessed or using a synchronization mechanism like a semaphore. There's lots of information about this topic on the web.
Another possible problem is when the variable can be modified through some process the compiler doesn't know about. You can inform the compiler of this possibility by using the keyword "volatile" in the variable's declaration. There's also much information about the volatile keyword on the web.
-Kevin
eugenebrooks
02-12-2004, 15:45
Eugene is referring to one of the problems with using interrupts (and multitasking systems in general). The code in clock.c assumes that the timer 2 interrupt service routine won't update the value of "Clock" while the function Display_Time() accesses it. For this example the chances of this happening is pretty slim. In general this is something that should be taken into consideration to prevent a race condition. Possible solutions include briefly disabling interrupts while the variable is accessed or using a synchronization mechanism like a semaphore.
Another possible problem is when the variable can be modified through some process the compiler doesn't know about. You can inform the compiler of this possibility by using the keyword "volatile" in the variable's declaration. There's also much information about the volatile keyword on the web.
-Kevin
The solution is to read the clock/counter twice, and if you don't get the same value, read it twice until you do! The C-BOT compiler does not appear to "optimize away" the required reads, so the use of volatile does not appear to be required. If this were a problem, using volatile would be your last hope.
How often you get a corrupt value depends on your usage. If you busy poll the clock value in the "fast loop", you will get a bad value every few seconds.
Kevin Watson
02-12-2004, 17:45
The solution is to read the clock/counter twice, and if you don't get the same value, read it twice until you do!Well, yes you could do this, but in general it's not a good idea because the two tasks that may alter the one variable may be synchronous to one another. Because of this you may constantly read the wrong value. It's better to just temporally disable the interrupt (e.g., PIE1bits.TMR2IE = 0, <access variable>, PIE1bits.TMR2IE = 1) or use a synchronization mechanism, like a semaphore.
The C-BOT compiler does not appear to "optimize away" the required reads, so the use of volatile does not appear to be required. If this were a problem, using volatile would be your last hope. The C18 compiler does indeed store values locally for some time, most notably in the W register. It's just good practice to use the volatile keyword where applicable.
How often you get a corrupt value depends on your usage. If you busy poll the clock value in the "fast loop", you will get a bad value every few seconds.It's a very hard thing to quantify. As an example, if the variable in question is a char, you'll never get corrupted data because all byte accesses on the 18F8520 are atomic in nature.
-Kevin
Al Skierkiewicz
02-12-2004, 18:45
Isn't there a time mark on the dashboard port data stream?
Dave Flowerday
02-12-2004, 19:33
Isn't there a time mark on the dashboard port data stream?
Yes, in a way. Each packet coming in to the RC (and leaving, for that matter) has a packet number associated with it. It's an 8-bit number that increments for each packet. There are 40 packets per second, so 1 bit in the packet number represents about 25 milliseconds. If your timing needs do not require high accuracy and/or are very coarse (i.e. you want to do something every 500 milliseconds or something), this could be a simple way to do it. There are some gotchas, though: since it is an 8 bit number it does overflow about every 4 seconds which means if you do try to use it as a timer you'll have to handle that overflow. Also if a packet is corrupted or not received it will not be passed to the user processor, so if you have a bad radio link there will appear to be gaps in the packet numbers. If you take that all into account in your software though then it can have its uses. I think we may have used it as a rudimentary timer in our RC last year ourselves.
eugenebrooks
02-12-2004, 21:53
It's a very hard thing to quantify. As an example, if the variable in question is a char, you'll never get corrupted data because all byte accesses on the 18F8520 are atomic in nature.
-Kevin
The variable in question is an unsigned long, msClock. Here is an example that shows the bogus msClock values you can get. You get one every few seconds. I stand corrected on volatile, the C18 user manual reccomends it, and mcClock was declared as such in clock.h.
#include "printf_lib.h"
void Process_Data_From_Local_IO(void) {
/* oldmsClock must be saved between calls.
*/
static unsigned long oldmsClock = 0;
unsigned long newmsClock;
newmsClock = msClock;
/* If we have a little byte trip from
the devil that we weren't expecting...
*/
if(newmsClock < oldmsClock) {
printf("Hit: old = %lx, new = %lx\n",
oldmsClock, newmsClock);
}
oldmsClock = newmsClock;
}
Hit output will be of the form:
Hit: old = 0dff new = 0d00
Hit: old = 020ff new = 02000
Hit: old = 02eff new = 02e00
Hit: old = 040ff new = 04000
That the interrupt is splitting the read of the low order byte,
and the next one up, is clear.
Kevin Watson
03-12-2004, 00:16
The variable in question is an unsigned long, msClock. Here is an example that shows the bogus msClock values you can get. You get one every few seconds.
I'd like to point out two things:
1) You're using the variable 'msClock' outside of the interrupt service routine without using the the techniques I described above. I did not do this in my example code. But because you did, the likelyhood of getting corrupted data is one thousand times greater when compared to how I used the 'Clock' variable.
2) The function Display_Time() in my example is only called every seventeen milliseconds, which means the variable 'Clock' is only being accessed around fifty-nine times a second. In addition to what you did above, you've also moved error checking code into the Process_Data_From_Local_IO() loop which executes, oh, one-hundred thousand times a second, which increases the likelyhood of creating an error by an additional factor of seventeen-hundred times.
Combined, these changes increase the likelyhood of creating a race condition by a factor of roughly a million. If you're seeing an error every, say, two seconds when you increased the likelyhood of generating one by over a million, is what you've done really meaningful?
I wrote that example in the clearest, most concise way I know how to. My goal is to "set the hook" and get folks interested in investigating other cool things their robot controller can do. Introducing the concept of task synchronization is unnecessary and counterproductive for such an example.
Volatile does not appear to be required, probably due to the size of the unsigned long compared to the size of PIC machine registers... Yes it is. You're spending quite a bit of effort in trying to poke holes in my example code. If you don't know that the 'volatile' keyword is indeed necessary, we shouldn't be having this exchange.
-Kevin
eugenebrooks
03-12-2004, 00:52
I'd like to point out two things:
You're spending quite a bit of effort in trying to poke holes in my example code. If you don't know that the 'volatile' keyword is indeed necessary, we shouldn't be having this exchange.
-Kevin
Whoa there Kevin! I think that your example code is a
great contribution to the community, and I refer to it as such.
I am not trying to poke holes in it at all... I only pointed out
that the hazard exists, and can be run into depending on how,
and how often, you read the counter.
vBulletin® v3.6.4, Copyright ©2000-2017, Jelsoft Enterprises Ltd.