Robot Timing Code

Here’s some timing code for a robot using the PIC processor. To use it link call() to the TMR0 overflow interrupt. Wait() delays the rest of the code for a number of seconds equal to the inputed float. Gettime() returns the current time in seconds since the robot started. Please post any comments!

timelibrary.c (777 Bytes)
timelibrary.h (92 Bytes)


timelibrary.c (777 Bytes)
timelibrary.h (92 Bytes)

It’s a nifty bit of code, but you can’t use the wait() function on the IFI Robot Controller. Once you hit that spin loop to wait for anything over 50ms or so, you’ll miss updating the master processor, the watchdog will time out, and your robot will be an impressive looking lump of aluminum and such sitting on the field.

The TMR0H and TMR0L bytes are built into the processor. They execute regardless of the code, as does the interrupt.

Question: Why would you not use the OpenTimerx(), CloseTimerx(), WriteTimerx() and ReadTimerx() function provided with the c18 libraries?

The PIC processor on the robot doesn’t include all of the registers used in C18, and it has a different processor speed.

You misunderstand me. The timer and timing functions will work just fine. Using floats on these robots makes me cringe, but it will work. But your wait(inp) function will kill the robot as sure as pulling the battery. The master processor HAS to be updated by user processor periodically, if you don’t the watchdog timer goes off and the master processor kills the robot. If you enter your while loop there you can to NOTHING but wait and can’t update the master processor. So the watchdog times out and the master processor kills the robot.

I’m making a fuss about this because it’s one of the most common mistakes made by rookie programmers and I really would rather there wasn’t code out there that made killing your robot as simple as calling a function.

Is there any way to disable the watchdog timer?

"It’s a nifty bit of code, but you can’t use the wait() function on the IFI Robot Controller. "

You really need to qualify that since it is not true in all cases.

You can call wait functions at user level (non-interrupt routine level) in EasyC, MPLAB/WPILIB, and other environments.

In these cases the get/put data routines are run at interrupt level off the system clock ISR. Doing this stuff in the background/at interrupt level means you don’t have to worry about what you’re doing at the non-interrupt level like doing wait() calls. As long as you don’t disable interrupts or don’t perform waits at interrupt level then it will work just fine.

Bud
PS the other nifty thing that easyc/wpilib does is the system clock notices when autonomous period ends and causes the processor to do a warm reset/reboot from interrupt level effectively exiting autonomous and when the main loops starts again, the operator call/loop is run. I’d done something similar, but did so by putting a call marker on the h/w stack and then at interrupt level when auto mode ended I popped stuff off the stack until I hit the marker and then did a return from interrupt level so in main it looked like the auto() routine had returned by itself. The warm reboot is easier/faster but that means you have to go through the c main startup/init code again.

“is there a way to disable the watchdog timer?”

No. Also, I’m not even sure there is a WDT used in the user processor – a check of the program memory map only showed a CLRWDT in the memory loop used to clear/initialize ram to zero at startup. But, its possible I missed something.

Its been a while since I looked at the default code on the user processor.

There are two PIC processors - a master processor and a user processor. First teams are programming the user processor. Default master code is loaded on the master processor.

The master processor is where all but PWM13-15 are connected. The master processor is designed to control all the motion of the robot such that if something goes wrong, it can detect the problem (like the user program on the user processor is whacked out) and stops all output. The OI comes into this processor too.

There is a data stream between the master and user processor over the SPI (serial) bus. This is a bi-directional serial 1-bit wide bus. The data stream is constant. The master is constantly sending the next rx data with OI information on the output stream and is simultaneously recieving the tx data from the user with the updated motor information.

On the user processor, the high priority interrupt routine services the SPI bus. It is constantly sending and recieving data into double buffered recieve and transmit data structures. It is filling up the ‘A’ rx buffer, while the ‘B’ rx buffer is sitting there for Getdata() to access. When this interrupt routine has recieved the last of the current ‘A’ rx data structure, it swaps buffers and sets a status flag indicating new rx data is available. It then starts filling up the ‘B’ rx buffer. A call to Getdata() will copy out the ‘A’ or ‘B’ buffer - whichever has the current completed rx data structure.

Calls to Putdata() place the tx data from the user into the send buffer not currently in use by the interrupt routine. You can repeatedly call Putdata() all you want, but until the high priority interrupt swaps buffers at a data structure boundary - the data doesn’t won’t go anywhere.

I don’t recall if it is the lack of a Getdata() call or something else, but the end result is the master processor is able to detect that the user processor has flaked out and isn’t consuming/updating data as it should. It asserts the !MCLR (master reset) on the user processor and holds it in the reset state giving you the red light of death. There is no way to prevent this from the user processor other than to do the Getdata/Putdata often enough to avoid the master processor killing your user processor.

I believe the IFI site has documentation that describes all this.