We have the wallclock code to operate Timer 1 from here running on our robot. I won’t really say use, because it was going to be used to send the robot into different states (End of autonomous, not enough time left to score, so on) during autonomous, but that code was removed due to difficulties with the feedback system. It’ll probably end up being used in autonomous at Palmetto for, at the very least, deploying the robot into scoring mode during autonomous. (Ideally, depending on how busy the practice field is, we’d like to get some scoring going with dead reckoning, but that is far less likely)
Our team was originally going to use gear tooth sensors to get feedback from the arm during autonomous but it kind of fell through due to time constraints and I’m now working on code that will move the arm based on the amount of time passed since the function first began.
If you just to designate the arm to move to certain positions at a certain time, just in autonomous mode, you don’t need to use the timer. In 2005, we had a 3 point autonomous by just doing something like this…
Autonomous_Mode(void)
{
static int timer = 0;
if(timer == 20)
move arm up;
else if(timer == 40)
move forward;
else if(timer == 60)
drop arm;
++ timer;
}
Surprisingly, we didn’t even overflow once within the 15 second autonomous. Those may not have been the exact values we used, but they pretty low. I think the first two were under 100.
I think a timer would be nice to use for my needs since it is exact to the second so I know how long I will need to move the arm rather than relying on the processors looping to change the arm position. I have looked into doing it the processor looping way but I feel safer using interrupt timers, it seems more exact to me .
well… lets see… using ccp’s on TMR3:
- Period Clock for ‘overclocked’ PWM’s (update @100Hz as opposed to IFI’s
38Hz update… a 263% improvement :rolleyes: ) - %duty cycle up time (@16bits as opposed to IFI’s 8) for left motors
(resolution increases x256 :ahh: ) - " " for right motors
TMR1:
- In combination with INT2 on PORTB, timing pulses coming back from
ultrasonic transceiver to get distance in great 16bit accuracy (+/-0.05")
CCP’s with programmable hardware triggers on compare matches… with the prescalar I’m using for the pwm timebase, getting accuracy of 0.0000002sec on pulse timing. Embedded peripherals make me happy!
For those of you who follow RALFF, I just finished v2 for 1024’s 2007 robot. Have to say it was quite a bit more complicated than last years with the SDAR and all…
-q
Wow! Even I didn’t understand that haha I think I was being agreed with. Seems like 1024’s robot is going to have one complicated code this year
Well Spencer if you want to know about any of it just give me a yell. Or, better yet if you are going to Buckeye or the Championship come over to 1024, ask for Q and I’ll help you out in person.
Once you learn the hardware… you’ll love the hardware… you can do so much cool stuff! It takes the mystique away from things like PWM too, lets you see how downright simple it is… not to mention gives you the capability of updating PID’s at 100Hz as opposed to 38Hz… and don’t forget the extra resolution!
Anyhow… if you all have any questions about onboard peripherals (i.e. CCP’s, Timers, ADCs, UARTS, etc), or anything of the kind, give me a yell, always glad to help.
one last thing… if anybody is having the encoder blues of too fast of an interrupt rate or not enough resolution, try out the analog encoders by US digital…they are awesome… about 1008 counts of resolution and you don’t need to sample them very fast at all… only about 4 times per revolution, unless you figure out a better driver that needs less… for us, to get resolution of about 0.1" the encoder only needs to be sampled at 90Hz and goes up to about 80~90Hz… which paired perfectly with the PID and PWM update rate of my CCP based high frequency PWM driver.
whew ok well i need to get some sleep, sure there’ll be plenty of coding to do come tomorrow morning. good luck to everybody else who at boilermaker this weekend!
-q
Thanks Qbranch! If I ever need any interrupt help, I’ll gladly ask . I was reading through IFI’s white paper on timer interrupts and they have two methods: polled and interrupt driven. They say that either way is the same result but if it is suppose to cause an intterrupt at 25ms, and the fast loop is 26.2ms would it not cause some bias? I think the interrupt route would be more exact. Is there any difference?
Don’t be fooled… there is a HUGE difference!
Picture this… the IFI main program loop polls for a bit saying that a new packet of data has been passed to the user processor from the master. Then, the IFI code calls Process_Data_From_Master_uP(). The whole time you are in this routine, you wont be polling for your flag… so that can mean up to many milliseconds of delay! Definitely not acceptible error for running a PID, calclating RPM, timing pulses, etc.
The interrupts will always be more (key word: more) exact than polling. While it does take a few microseconds to get into the interrupt, this is surely better than a few MILLIseconds. Of course there’s a way around even the delay of interrupts… but we’ll save CCP’s for some time when I can talk with pad and pen and not have to type it all out.
Do you know how to use interrupts? Its kind of fun… but then again I think a lot of things are fun others dont . Kidding aside, if you need some tips/tricks/hints/explanations i’ll give em.
Sometime i’m going to write a whitepaper on all the internal peripherals inside the processor… should be helpful. Just have to sit down and do it… ugh.
-q
All I need the timer for is moving the arm, like I’ve said before, but I would like it to be exact so I guess I’ll work with the interrupt then, thanks for the clarification. I’m not exactly an expert when it comes to writing interrupt code but I can do the bare minimum needed. I’ve learned from Kevin Watson’s interrupt codes how they should be handled and everything. IFI mentions in their white paper that what I should do is get out of the interrupt as fast as possible and just set a variable inside the interrupt and handle it later. I find this kind of weird so what I’m planning on doing is instead of doing it their way, just incrementing the variable I have for keeping the seconds timed and incrementing it inside the interrupt. I don’t see a problem with it, is there anything I should know about this?
EDIT: When I mention incrementing the seconds in the interrupt, I mean after the initial 25ms and 100ms interrupts
Since the interrupt handler blocks other interrupts, you want to do as little as possible in inerrupt context. Once you have incremented your timer count, the rest of the calculation should be “interruptable”, so it should be done in the main code in the “fast loop” section (i.e. outside the 26ms data transfer to master processor section).
Theory aside, you probably won’t notice any problem with doing what you propose. As described, you are doing a fairly quick operation, and you don’t have many other interrupt sources that you could be holding off.
We have used the timers (based on the white paper) and find it very usefull to have a high (milisecond) resolution clock for various purposes. For example, if you have a routine to move an arm to a position, it could check the time when it starts and abort if it takes too long to get to position. Alternately, you can use the time to schedule activities on ticks other than the 26ms heartbeat provide by the getdata/putdata requirement.
A good tip… make interrupt code as efficient as possible… but often times you can’t set a flag and handle it later, since this would be the same as just polling for the interrupt flag in the first place.
If you need to do something longer-time in the routine, but avoid this like the plague if you can, I just suggest that you use a digital I/O pin as a flag pin for how long your interrupt is running, and just time the pulse on your oscilloscope.
Another suggestion, just not a pin (ex rc_dig_out18 = !rc_dig_out18; ) in the Process_Data_From_Local_IO() routine, and wherever you don’t see the constant frequency of the main processor loop you know the processor is busy, a good way to guage your % loading on your processor.
-q
Thanks for all the help everyone! I’ve created the timer, wrote my autonomous code and I should be up and running fully in Waterloo . It’ll require a bit a tweaking, but I’m up for the challenge
.
Team 11 uses a timer to count 14.5 seconds, we drop our keeper last half second of autonomous mode… it worked great for us and we never had any problems with it
We implemented Timers into our code this year, and they are great. We set ours to fire off every millisecond. Unfortunately, we didn’t have the time, but we plan to write a wrapper around one timer so we can schedule “events” to happen at certain time intervals. But so far, the timers have been proven to be extremely useful and you should definitely implement them.
Some tips, your interrupt routine should be fast. In fact, you should really only be really incrementing a number in it. Remember that since your timer is firing every X milliseconds, your number is being increased very rapidly. So use a big type like a long, or a long short. These will last your for days.
Also, always, always, always, disable your timer interrupts before trying to access your “tick” variable. Bad things will happen if you don’t.
We have a scheduler based on the hardware timers. It allows scheduling of events at regular intervals, or single shot events at some time in the future.
It uses function pointers to allow the scheduler code to avoid needning to know what functions it may be calling.
What kind of things are you scheduling?
Anything that needs to be called on a regular basis. For example, a regular sensor reading that is faster than the 26ms loop, but not “as fast as possible”.
Spencer,
I don’t know exactly what you need in terms of timer resolution but team 116 has been using a real time clock that runs off of timer 4 with 5ms ticks. Code segments to setup the timer, handle the interrupt and do something with the timer are below. We choose timer4 because we could put it in a period mode so it automatically interrupt every Nms and we didn’t know of any other code that used that timer. As you will see the ISR just increments a variable and that veriable is checked in user_routines_fast where ClockTick is called. Currently we have 10 software elapsed timers and up to 10 concurrent “Event Timers”. When an event timer times out a routine is automatically called. So an event is scheduled to occur some time in the future. Lots of times we schedule events for error conditions and remove the event if something good happens like the camera actually returns a T-packet or acks a virtual window command
The h file and the timer library for 8520 and 8722 are attached. Rename the lib files to .lib instead of .txt
Here is an example of how you would print out your average processor loading once per second:
In the initialization code after you have made the RTC initialize call, use
ScheduleEventTimer(ProcessorUtilization,“some time”,unused parameter);
This routine will run after “some time” in ms
void ProcessorUtilization(void)
{
unsigned int percent_utilized;
percent_utilized = (25600 - idle_count) >> 8; /*an idle count of 25,600 means none of the processor is being used */
printf(“PU = %d%%\r”,percent_utilized);
idle_count = 0;
ScheduleEventTimer(ProcessorUtilization,1000,0); /* Run again in 1 second */
}
the variable idle_count needs to be incremented in process_data_from local IO fast like this:
Delay10TCYx(39);
idle_count += 1; /* Every time this variable is incremented, 39us? of time was spent idle */
The way this works is we waist 39us every time through the fast loop and count how often that happens each second. If you hit 80% or more you need to make code changes for sure. 60% is our peak target.
So, if you want to try the 116 RTC - that would be great - let me know how it worked, if not the following code segments should help you set up a timer.
Greg
This code goes in the timer file - Initialize RTC gets called in the user_routines initialization section
/*******************************************************************************
*
- FUNCTION: InitializeRTC()
- PURPOSE: Initializes the real time clock timers
- CALLED FROM:
- PARAMETERS: None
- RETURNS: Nothing
- COMMENTS:
*******************************************************************************/
void InitializeRTC(void)
{
Initialize_Timer_4();
InitializeEventTimers();
InitializeElapsedTimers();
}
/*******************************************************************************
*
- FUNCTION: Initialize_Timer_4()
- PURPOSE: Initializes the timer 1 hardware, which is responsible for
-
producing real time clock ticks
- CALLED FROM: 116_timers.c/Initialize_RTC()
- PARAMETERS: Unsigned integer containing the sample rate expressed in Hz
- RETURNS: Nothing
- COMMENTS: The default tick rate is 200Hz (5ms)
*******************************************************************************/
void Initialize_Timer_4(void)
{
// use these parameters for a 200Hz Timer Rate
// use a 1:16 prescaler and 1:14 postscaler
T4CON = 0b01101010;
// Count to 221 before rolling over and generating
// an interrupt (223.21 - 2 is ideal)
PR4 = 221;
// make sure the timer 1 register starts at zero
TMR4 = 0x00;
// timer 2 interrupt is low priority
IPR3bits.TMR4IP = 0;
// to prevent a spurious interrupt, make sure the interrupt flag is reset
PIR3bits.TMR4IF = 0;
// enable the timer 4 interrupt
PIE3bits.TMR4IE = 1;
// enable timer 4
T4CONbits.TMR4ON = 1;
}
/*******************************************************************************
*
- FUNCTION: Timer_4_Int_Handler()
- PURPOSE: Timer 4 interrupt service routine
- CALLED FROM: user_routines_fast.c/InterruptHandlerLow()
- PARAMETERS: None
- RETURNS: Nothing
- COMMENTS:
*******************************************************************************/
void Timer_4_Int_Handler(void)
{
rtc_tick += 1;
// rc_dig_out18 ^= 1;
}
This code gets put in Interrupt_Handler_low
if(PIR3bits.TMR4IF && PIE3bits.TMR4IE) // timer 4 interrupt?
{
PIR3bits.TMR4IF = 0; // clear the timer 4 interrupt flag [92]
Timer_4_Int_Handler(); // call the timer 4 interrupt handler (in 116_timers.c)
}
This code actually deals with the clock ticks - ClockTick does whatever you need to do with the time information. Don’t forgot to disable the interrupt if you change a variable it can change.
void Process_Data_From_Local_IO(void)
{
/* Add code here that you want to be executed every program loop. */
unsigned char ticks;
unsigned char i;
/* Actually execute the real time clock code */
ticks = rtc_tick;
if(ticks > 0){ /* The RTC has ticked */
// if(ticks > 1)
// printf(“THE CODE IS GETTING BOGGED DOWN - %3d TICKS\r”,ticks);
for(i = 0; i < ticks; i++)
{
ClockTick();
//printf(“HOW LONG DOES IT TAKE TO PUT OUT THIS STRING EVERY 5 ms?\r”);
}
PIE3bits.TMR4IE = 0; /*Disable the timer interrupt */
rtc_tick = 0;
PIE3bits.TMR4IE = 1; /*Enable the timer interrupt */
}
116_timers.h (6.38 KB)
116_timers_8520_lib.txt (43.2 KB)
116_timers_lib.txt (52.3 KB)
116_timers.h (6.38 KB)
116_timers_8520_lib.txt (43.2 KB)
116_timers_lib.txt (52.3 KB)
It seems like timers are a problem for a lot of teams so I posted ours this morning on this thread in the wrong place… I didn’t see the rest of the thread or that the issue was already solved.
It sounds like you have taken a similar approach with event timers that call routines when they time out. After all when all your other sensors have failed or you should still at least have timers! The example shows one way to estimate processor loading which also seems to come up a lot.