View Single Post
  #15   Spotlight this post!  
Unread 11-12-2006, 17:07
mluckham's Avatar
mluckham mluckham is offline
Registered User
FRC #0758 (Sky Robotics)
Team Role: Mentor
 
Join Date: Mar 2006
Rookie Year: 2006
Location: Ontario, Canada
Posts: 116
mluckham will become famous soon enoughmluckham will become famous soon enough
Re: Auto mode... need help

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


Code:
/*******************************************************************************
*
*	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


Code:
/*******************************************************************************
*
*	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\n", 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


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