Auto mode... need help

Hey guys,

ok i doign some stuff in automode… nothing tooo cool… but its a start. Basicaly all i am doing is driving the robot forward and backwards. Now… i have it program in “if” statements

example.

if (count == 1)
{
pwm01 = 254;
}

sooo… i need a timer. count is what the time is. Sorta confusing but at 1 second its goign to go forward. But the problem i am having is creating code for the timer. Does the orignal code have a timer programmed already ? or should i make my own. let me know:)

The easiest way to do this is to increment a variable every time you get new data from the master processor.

Default Autonomous:


  while (autonomous_mode)   /* DO NOT CHANGE! */
  {
    if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
    {
        Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */

        /* Add your own autonomous code here. */

        Generate_Pwms(pwm13,pwm14,pwm15,pwm16);

        Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
    }
  }

The first thing you need to do is create a static variable to use as a counter.


static int i;

Then you need to increment the variable. The easiest way to do this is to increment the counter each time you get new data.


  while (autonomous_mode)   /* DO NOT CHANGE! */
  {
    if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
    {
        Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */

        **i++; // this will increment the 'i' variable by 1.**

        /* Add your own autonomous code here. */

        Generate_Pwms(pwm13,pwm14,pwm15,pwm16);

        Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
    }
  }

Now you can calculate the time by multiplying the time span(26.2) by the number if times(i) to get the actual time.


int time;

  while (autonomous_mode)   /* DO NOT CHANGE! */
  {
    if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
    {
        Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */
        i++; // this will increment the 'i' variable by 1.
        **time =  (i * 262)/2500 // if you avoid the decimal, by multiplying the time span by 10, your code will execute faster because it will not use a floatig points.
        // I divide the time(in 10000enths of a second) by 2500 so I get a result in 1/4 of a second**

        Generate_Pwms(pwm13,pwm14,pwm15,pwm16);

        Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
    }
  }

Example code to drive forward 1 second, then reverse for 1 second.


int time;

  while (autonomous_mode)   /* DO NOT CHANGE! */
  {
    if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
    {
        Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */
        i++; // this will increment the 'i' variable by 1.
        time =  (i * 262)/2500 // if you avoid the decimal, by multiplying the time span by 10, your code will execute faster because it will not use a floatig points.
        // I divide the time(in 10000enths of a second) by 2500 so I get a result in 1/4 of a second
**
        //drive code\\
        if (time < 4) pwm01 = pwm02 = 255; //forward
        else if(time < 8) pwm01 = pwm02 = 0; //reverse
        else if(time > 8) pwm01 = pwm02 = 127;//stop**

        Generate_Pwms(pwm13,pwm14,pwm15,pwm16);

        Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
    }
  }

I think that is what your looking for.

Hope this helps, and sorry for the long post. :wink:

That assumes that your code will take the same time as the default code does. Maybe you have some intensive stuff and each loops takes longer than the 26.2 ms loop. Your code works effectivly for most things though. teh_pwnerer795, if you wanted to use the chips timer post back and ill post code for using that.

I used a simpler approach this year and had no issue with it. :slight_smile:

I would doubt that you could tax the processor to the point that the autonomous code would take longer than 26.2ms

I would agree that this normally wouldn’t be a problem. In fact, the way the RC works is that if the master processor doesn’t get data back in 26.2 ms, your robot gets disabled. Therefore, it is possible that the loop will runner faster than 26.2 ms.

I would suggest using real timers.
For more information, take a look at IFI’s white-paper (PDF).

They have a very nice explanation of how to use the timers built into the PIC.

Awesome.

I know rite now my code isnt very intensive at the moment… but i do tend to have long code… lol my engineering program for turing was 1500 lines when someppls were 100. but got it down to 150 once i got some logic into it!!:)…but ye i’ll keep u posted if it works or not…


int time;

  while (autonomous_mode)   /* DO NOT CHANGE! */
  {
    if (statusflag.NEW_SPI_DATA)      /* 26.2ms loop area */
    {
        Getdata(&rxdata);   /* DO NOT DELETE, or you will be stuck here forever! */
        i++; // this will increment the 'i' variable by 1.
        **time =  (i * 262)/2500 // if you avoid the decimal, by multiplying the time span by 10, your code will execute faster because it will not use a floatig points.
        // I divide the time(in 10000enths of a second) by 2500 so I get a result in 1/4 of a second**

        Generate_Pwms(pwm13,pwm14,pwm15,pwm16);

        Putdata(&txdata);   /* DO NOT DELETE, or you will get no PWM outputs! */
    }
  }

no problem for the post… im sorta confused… if the computer cycles at 26.2 ms… will it not cycle rougly 38 times in a second?

if so… your code… where ‘i’ is increased by 1 every cycle, when it reaches 1 second… i == 38…

if so… time == 4 roughly at 1 second… shouldnt u divide it by 4? to get 1 second??? srry if i confused you:P

lol srry just trying to understand the logic behind ‘time’

OMG i am such an idiot I GET IT NOW:):smiley: SOO SRRY:) hm… another question… how do i round? i remember doing it in java… not suer if its the same in C

Actually the problem as mentioned before is its very unlikely to take that long. If memory serves it loops as fast as possible, so it could run anywhere from about 12ms to 26.2 and it will change depending on your autonomous code, if its really time sensitive, you should use the hardware timer, and its just good to know how to use that anyway.

What are you trying to round? If you’re using int variables, there is no decimal part and all math operations will result in truncation of the decimal (i.e. 1/4 => 0, 5/2 => 2).

If you really needed the decimal part, you could use the float type instead of int. There are some problems with doing this discussed here, and I wouldn’t really recommend doing this on the RC.

EHaskins offered up some good suggestions, but there is a slight annoyance with it. What happens if you have a 10 step time based routine and you need to change the first duration to be a little longer or shorter? You have to change them all.

One way to fix it would be to use constants, and make them relative to each other.

#define AUTO_STEP1_END    (10)
#define AUTO_STEP2_END    (AUTO_DURATION_STEP1 + 5)
.....
if(i <= AUTO_STEP1_END)
{
....
}
else if(i <= AUTO_STEP2_END)
{
...
}
...

If you want to make things a little more advanced, you could do something like this.

#define NOT_DONE           0
#define DONE               1
...
#define MAX_AUTO_STEP  3
...
enum
{
  PROG_STEP1,
  PROG_STEP2,
  PROG_STEP3,
  PROG_STEP_LAST = PROG_STEP2,
};
...
static int prog_done = NOT_DONE;
static int prog_step = 0;
...
int ret;
if(prog_step < PROG_STEP_LAST)
{
  switch prog_step
  {
    case PROG_STEP1:
      ret = do_step1(); // These function names could be more specific
      break;
    case PROG_STEP2:
      ret = do_step2(); // These function names could be more specific
      break;
    case PROG_STEP3:
      ret = do_step3(); // These function names could be more specific
      break;
    default:
      // Bad step number, exit now
      prog_step = PROG_STEP_LAST;
      break;
  }

  if(ret == DONE)
  {
    // Step is complete, move on
    prog_step++;
  }
}
else
{
  // Set all pwms to 127 to terminate the run
}
....
// Then you would define your program step functions to do what you needed

int do_step1(void)
{
   static int i = 0;
   int status = NOT_DONE;

   if(i >= 40)
   {
       // Set PWM values here
   }
   else
   {
     status = DONE;
   }
   
   return stats;
}

int do_step2(void)
{
   static int i = 0;
   int status = NOT_DONE;

   if(i >= 30)
   {
       // Set PWM values here
   }
   else
   {
     status = DONE;
   }
   
   return stats;
}

int do_step3(void)
{
.....
}

The benefit to this is maintainability. Each step is a self-containted program of its own residing within a function called from your autonomous loop. This gives you the advantage of not relying on absolute time. Each step can know how many loops to run for and report that it’s done.

Here’s another perk. If you want to add a delay between step 2 and 3, you could change the enum to have PROG_STEP_2A between PROG_STEP_2 and 3. Then, in your switch, you could do something like

...
case PROG_STEP_2A:
  ret = do_wait(100);
  break;
...

int do_wait(int loops)
{
  static int i = 0;
  int ret = NOT_DONE;

  if(i >= loops)
  {
    i = 0; // Leave the loop counter ready to go the next time.
    ret = DONE;
  }
  else
  {
    // Turn PWMs off
    i++;
  }
  return ret;
}

Now you have a nice reusable chunk of code that turns the motors off and waits for a certain number of loops before advancing. If all of a sudden you don’t need that step anymore, you can simply comment you the function call and set ret to DONE. You burn a loop, but that shouldn’t matter in most cases.

...
case PROG_STEP_2A:
  ret = DONE;
  // ret = do_wait(100);
  break;
...

I know this won’t matter too much if this is just an off season thing, but what Dave just mentioned can be VERY helpful at competition. If you need to adjust one of your autonomous actions by 1/2 or 1 second, this makes it so much more convenient and doable (especially if that time comes during the playoffs in between matches where you have maybe 5 minutes to do it tops).

No, that’s the entire point. It doesn’t loop as quickly as possible. It does its calculations and idles for the remaining part of the loop time. While I’m not at all sure about your robot being disabled if you don’t get data within 26.2 ms, it could be true. However, no reasonable code for your robot would go beyond the 26.2 ms loop. You’re not calculating pi to the 500th decimal place, you’re not using the cubic formula, and you certainly shouldn’t be using an loop except an iteration loop. Therefore, 26.2 ms is a safe figure by design.

Hope this helps,
Paul

I agree. What I posted above was a quick way to get started, but as with anything there’s room for improvement.

I have to say, after a season and a half of asking the strategy guy how long he wants it to go out, then stepping into Calculator, then punching the wrong numbers, then typing the wrong number I just calculated into my code (now converted to 26.2ms cycles), then seeing it totally mess up when I put it on the field…having a real timer is SO nice. I implemented the hardware timer, so now I have a Windows-style GetTickCount() type function, and it works very accurately. The scouts tell me that the bot needs to be in front of the other bot in 2 seconds: I give them 2 seconds. Not 2000ms / 26.2ms = 76 cycles–blech. Those never worked right for me.

What I haven’t solved yet is the overflow issue. What data type do I use to hold the huge number? I have no idea. I can’t reset the tick count to 0 each time it hits the upper limit my code is waiting for because I have more than one part of my code using this tick count. I think I’ll use the hugest data type I can get–I’ll have to look it up; I don’t know C in and out.

JBot

Here’s some code for an interrupt-driven timer.

Call Timer_1_Initialize() in the User_Initialization routine in user_routines.c, add the call to the interrupt handler to user_routines_fast.c. Time is in the timer_ variables.

Mike


/*******************************************************************************
*
*	TITLE:		wallclock.h 
*
*	VERSION:	0.1 (Beta)
*
*	DATE:		23-Sep-2006
*
*	AUTHOR:		Mike Luckham
*				sunrise@sunsys.net
*
********************************************************************************
*
*				You are free to use this source code for any non-commercial
*				use. Please do not make copies of this source code, modified
*				or un-modified, publicly available on the internet or
*				elsewhere without permission. Thanks.
*
*				Copyright ©2006-2007 Mike Luckham. All rights are reserved.
*
********************************************************************************
*
*	CHANGE LOG:
*
*	DATE         REV  DESCRIPTION
*	-----------  ---  ----------------------------------------------------------
*	23-Sep-2006  0.1  Mike Luckham - Original code.
*
*******************************************************************************/

#ifndef _WALLCLOCK_H
#define _WALLCLOCK_H

typedef struct {
    unsigned int hours;
	unsigned int minutes;
	unsigned int seconds;
	unsigned int msec;
	unsigned int usec;
	unsigned long timestamp;
} WallTime;

//  Do not write to any of these variables

extern volatile unsigned long timer_msecClock;				// milliseconds since system start (overflows after 49.7 days)

extern unsigned long Timer_1_MicrosecondsTimestamp(void);

#define timer_usecClock	Timer_1_MicrosecondsTimestamp()		// microseconds since system start (overflows after about 71 minutes)

// current time decoded

#define timer_microseconds	Timer_1_Microseconds()	
extern volatile unsigned int timer_milliseconds;
extern volatile unsigned int timer_100msec;
extern volatile unsigned int timer_seconds;
extern volatile unsigned int timer_minutes;
extern volatile unsigned int timer_hours;

extern void Timer_1_Reset(void);
extern void Timer_1_Initialize(void);
extern void Timer_1_Int_Handler(void);
extern void Timer_1_Load_Prescale(void);
extern unsigned int Timer_1_Microseconds(void);

extern void Timer_1_ReadTimeStruct(WallTime *);


#endif

/*******************************************************************************
*
*	TITLE:		wallclock.c 
*
*	VERSION:	0.1 (Beta)
*
*	DATE:		23-Sep-2006
*
*	AUTHOR:		Mike Luckham
*				sunrise@sunsys.net
*
*	COMMENTS:	This source code in this file implements a clock that is useful
*				for tracking time at millisecond resolution.  A timestamp value
*				is useful for comparing the time between events.  The timestamp
*				is decoded into hours/minutes/seconds/milliseconds, and the
*				current time can be read at microsecond resolution.
*
*				Since the clock is interrupt-driven, it runs independently and
*				maintains correct time between calls to user_routines.c by the master
*				scheduler.
*
*				To use the code, include wallclock.h in the user_routines.c and
*				user_routines_fast.c source files, and:
*
*				1) call Timer_1_Initialize() from user_routines.c in User_Initialization()
*
*				2) add the following to user_routines_fast.c in InterruptHandlerLow():
*
*					if (PIR1bits.TMR1IF && PIE1bits.TMR1IE)
*					{
*						PIR1bits.TMR1IF = 0;		// clear the interrupt flag
*				    	Timer_1_Int_Handler();
*					}
*
*				3) when you need to measure elapsed time, store timer_msecClock into
*				an unsigned long variable at the beginning of the interval, then subtract
*				timer_msecClock from the saved value at the end of the interval to obtain
*				the number of milliseconds which have elapsed.
*
*				If you need the microsecond portion of the time, store the value returned
*				by the timer_microseconds macro into an unsigned long variable.  Probably this
*				is of limited usefulness, although it could be useful in a busy-loop.
*
*				The decoded time is available in the timer_ variables.  Here is an example of
*				how to use them:
*
*					printf("%u:%u:%u.%u.%u
", timer_hours, timer_minutes, timer_seconds, timer_milliseconds, timer_microseconds);
*
*
********************************************************************************
*
*				You are free to use this source code for any non-commercial
*				use. Please do not make copies of this source code, modified
*				or un-modified, publicly available on the internet or
*				elsewhere without permission. Thanks.
*
*				Copyright ©2006-2007 Mike Luckham. All rights are reserved.
*
********************************************************************************
*
*	CHANGE LOG:
*
*	DATE         REV  DESCRIPTION
*	-----------  ---  ----------------------------------------------------------
*	23-Sep-2006  0.1  Mike Luckham - Original code.
*
*******************************************************************************/


#include "ifi_aliases.h"
#include "ifi_default.h"
#include "wallclock.h"


/********** GLOBAL CLOCK VARIABLES ***********************************************/

volatile unsigned long timer_msecClock = 0L;		// milliseconds since system start (overflows after 49.7 days)

// current wall-clock time

volatile unsigned int timer_100msec = 0;			// increments once every 100 milliseconds
volatile unsigned int timer_milliseconds = 0;	// increments once per millisecond
volatile unsigned int timer_seconds = 0;			// increments once per second
volatile unsigned int timer_minutes = 0;			// increments once per minute
volatile unsigned int timer_hours = 0;			// increments once per hour

// for the current microseconds count, use Timer_1_Microseconds

WallTime wTime;


// preload the timer so that the interrupt-on-overflow occurs 1000 microseconds later
// for microprocessor clock crystal 40MHz/4 = 10MHz tick rate = 65535 - 10000 (0xD8EF)

#define PRELOAD_LOW	 0xD8
#define PRELOAD_HIGH 0xEF



/*******************************************************************************
* FUNCTION NAME: Timer_1_Reset
* PURPOSE:       resets clock variables to zero
* CALLED FROM:   user_routines.c
* ARGUMENTS:     none
* RETURNS:       real-time clock variables are reset
*******************************************************************************/
void Timer_1_Reset(void)
{
	timer_msecClock = 0L;
	timer_milliseconds = 0;
	timer_seconds = 0;
	timer_minutes = 0;
	timer_hours = 0;
}

/*******************************************************************************
* FUNCTION NAME: Timer_1_Load_Prescale
* PURPOSE:       preload the timer to overflow after 1000 microseconds
* CALLED FROM:   user_routines_fast.c, user_routines.c
* ARGUMENTS:     none
* RETURNS:       Timer1 is preloaded
*******************************************************************************/
void Timer_1_Load_Prescale(void)
{
					// NOTE:  Timer specification requires MUST write high byte
					//        first, then low byte

	TMR1H = PRELOAD_LOW;	// preload the timer so that the interrupt-on-overflow occurs 1000 microseconds later
	TMR1L = PRELOAD_HIGH;	// for microprocessor clock crystal 40MHz/4 = 10MHz tick rate = 65535 - 10000 (0xD8EF)
}


/*******************************************************************************
* FUNCTION NAME: Timer_1_Int_Handler
* PURPOSE:       Updates the real time clock variables
* CALLED FROM:   user_routines_fast.c
* ARGUMENTS:     none
* RETURNS:       real-time clock variables are updated
*******************************************************************************/
void Timer_1_Int_Handler(void)
{
	// update the millisecond-counter
	// probably will be more useful than the msec/sec/min/hours/days variables

	// avoid calling a function from within the Interrupt handler, other register
	// context will need to be saved with a #pragma in user_routines_fast.c
	//  Timer_1_Load_Prescale();

	TMR1H = PRELOAD_LOW;	// reload the timer so that the interrupt-on-overflow occurs 1000 microseconds later
	TMR1L = PRELOAD_HIGH;	// for microprocessor clock crystal 40MHz/4 = 10MHz tick rate = 65535 - 10000 (0xD8EF)

	timer_msecClock++;

	timer_milliseconds++;
	if (timer_milliseconds > 999)
	{
		timer_milliseconds = 0;

		if (++timer_seconds > 59)
		{
			timer_seconds = 0;

			if(++timer_minutes > 59)
			{
				timer_minutes = 0;

				timer_hours++;
			}
		}
	}

	if ((timer_milliseconds % 100) == 0)
		timer_100msec++;
}


/***********************************************************************************
* FUNCTION NAME: Timer_1_Microseconds
* PURPOSE:       fetch the number of microseconds since the timer last interrupted
* CALLED FROM:   user_routines.c
* ARGUMENTS:     none
* RETURNS:       microseconds since the last interrupt
***********************************************************************************/
unsigned int Timer_1_Microseconds(void)
{
	unsigned int tmr1;

					// NOTE:  Timer specification requires MUST read low byte
					//        first, then high byte

	tmr1 = (unsigned int) TMR1L + ((unsigned int) TMR1H << 8);	// convert to 16-bit, then negate

	tmr1 = ~tmr1 + 1;				// (2's complement) to get remaining ticks until overflow

	// scale to the clock frequency - see Timer_1_Load_Prescale() comments for the number
	// of ticks per microsecond

	return (tmr1 / 10);
}


/***********************************************************************************
* FUNCTION NAME: Timer_1_MicrosecondsTimestamp
* PURPOSE:       fetch a timestamp which includes the number of microseconds
* CALLED FROM:   user_routines.c
* ARGUMENTS:     none
* RETURNS:       microseconds since the last interrupt
***********************************************************************************/
unsigned long Timer_1_MicrosecondsTimestamp(void)
{
	// WARNING: this is quite expensive to call, probably not very useful

	return (timer_msecClock * 1000) + (unsigned long) Timer_1_Microseconds();
}



/*******************************************************************************
* FUNCTION NAME: Timer_1_Initialize
* PURPOSE:       configure the Timer1 interrupt to overflow and interrupt after
*				 1000 microseconds
* CALLED FROM:   user_routines.c
* ARGUMENTS:     none
* RETURNS:       Timer1 is configured and TMR1IF interrupt enabled
*******************************************************************************/
void Timer_1_Initialize(void)
{
  T1CONbits.TMR1ON = 0;		// disable the clock while programming it

  T1CONbits.TMR1CS = 0;		// use internal clock (10 MHz)
  T1CONbits.T1CKPS0 = 0;	// set prescaler to 1:1, or OSC/4.  A 40 MHz crystal divided by 4 gives 10 ticks per microsecond.
  T1CONbits.T1CKPS1 = 0;
  T1CONbits.RD16 = 0;		// read timer using two 8-bit reads of TMR1L followed by TMR1H
							// reading the timer and subtracting it's value from FFFFh gives the number of microseconds
							//  since the timer was last loaded

  Timer_1_Load_Prescale();

  Timer_1_Reset();			// reset time variables

  T1CONbits.TMR1ON = 1;		// enable the clock
  PIE1bits.TMR1IE = 1;		// enable the Timer1 overflow interrupt
}




/*******************************************************************************
* FUNCTION NAME: Timer_1_ReadTimeStruct
* PURPOSE:       safely read the time values without disturbance from the
*				 interrupt service routines
* CALLED FROM:   user_routines.c
* ARGUMENTS:     none
* RETURNS:       pointer to WallTime structure
*******************************************************************************/
void Timer_1_ReadTimeStruct(WallTime * wTime)
{
	int intEnabled;

	intEnabled = PIE1bits.TMR1IE;	// disable timer interrupts while loading structure

	if (intEnabled != 0)
		PIE1bits.TMR1IE = 0;		// disable the Timer1 overflow interrupt

	wTime->hours = timer_hours;
	wTime->minutes = timer_minutes;
	wTime->seconds = timer_seconds;
	wTime->msec = timer_milliseconds;
	wTime->usec = Timer_1_Microseconds();
	wTime->timestamp = Timer_1_MicrosecondsTimestamp();

	if (intEnabled != 0)
		PIE1bits.TMR1IE = 1;		// enable the Timer1 overflow interrupt

}

Add this to the InterruptHandlerLow() routine in user_routines_fast.c

	
if (PIR1bits.TMR1IF && PIE1bits.TMR1IE)	// wallclock.c timer interrupt?
	{
		PIR1bits.TMR1IF = 0;		// clear the interrupt flag
	    Timer_1_Int_Handler();
	}

Note that Reset_Timer_1() doesn’t reset timer_100msec.

Otherwise, this code works very well, and is cleaner than the timer code I had been using.