Pseudo real time clock on TI TMS320C6713 DSP

I needed to keep track of time in my program written in C/C++ running directly on the TI DSP platform where I did not have external RTC hardware. My solution was to override a couple of library functions from the C library and take advantage of the built in library routines for time management.

I’ve overridden the clock() and time() functions from the library. What I mean by overriding them is not in the traditional C++ sense, but simply in that by using my own functions that are declared locally, they get linked instead of the functions in the library. I also declare the _tz data structure that gets used for setting the correct timezone information for some of the standard output functions. I’m only running my system configured as UCT.

When the system starts, it calls the RTCStart() function which configures one of the two timer resources in the DSP to call my interrupt service routine (isr) clock_isr() every 1/1000th of a second.

My isr increments a volatile long integer. The clock() and time() functions simply access that long integer.

Over time I have some drift of my real time. Because I’m running on a tiny platform that normally only communicates over an RS422 serial port and doesn’t have significant RAM or storage I don’t want to add much but I’ve been wondering if there is some significantly better way that I should be attacking this problem?

TIMER_Handle	hTimer1 = (TIMER_Handle) INV;
//////////////////////////////////////////////////////////////////////////////
//// Clock Stuff /////////////////////////////////////////////////////////////
volatile unsigned long int DSP_clock_counter = 0;
volatile time_t time_offset = 0;
extern "C" {
	interrupt void clock_isr(void)
	{
		DSP_clock_counter++;
		if (DSP_clock_counter > 0xFFFFFFFFFFFFFFF) 
			DSP_clock_counter = 0;
	}
	_CODE_ACCESS clock_t clock(void) // Including this function overrides the function in the default library, allowing all of the standard time routines to be used.
	{
		return(DSP_clock_counter);
	}
	_DATA_ACCESS TZ _tz =
	{
		0,		/* daylight */
		0,		/* timezone */
		"UCT",	/* tzname   */
		"DST",	/* dstname  */
	};
	_CODE_ACCESS time_t time(time_t *timer) // Including this function overrides the function in the default library, allowing all of the standard time routines to be used.
	{
	   time_t result = (DSP_clock_counter / 1000) + time_offset;
	   if(timer)
	       *timer = result;
	   return(result);
	}
} // end of extern "C" 
time_t settime(time_t timer)
{
	time_offset = timer - (DSP_clock_counter / 1000);
	return(time_offset);
}
time_t clock2time(clock_t clock)
{
	return((clock / 1000) + time_offset);
}
void RTCStart(void)
{
	if (hTimer1 == INV)
		hTimer1 = TIMER_open(TIMER_DEV1, TIMER_OPEN_RESET);	// open timer and set to default 
	if (hTimer1 != INV)
		TIMER_configArgs(hTimer1,
		    0x00000281,        /*  Control Register (CTL) */ // CLKSRC=1 CP=0 HLD=1 GO=0 FUNC=1
		    0x0000C350,        /*  Period Register (PRD)  */
		    0x00000000         /*  Counter Register (CNT) */
			);
	IRQ_enable(IRQ_EVT_TINT1);
	install_interrupt(CPU_INT15, clock_isr);      // clock period interrupt (Timer1 Interrupt)
}
void RTCStop(void)
{
	if (hTimer1 != INV)
	{
		TIMER_close(hTimer1);				// timer: wait
		hTimer1 = (TIMER_Handle) INV;
		install_interrupt(CPU_INT15, NULL);	// measurement period interrupt (Timer1 Interrupt)
	}
}

Then there are some support functions that I’ve got for simple output that are generic and not related to the DSP.

void GetISO8601Time(char ISO8601Time[25], clock_t theTime)
{
	time_t timer = clock2time(theTime);
	int milliseconds = theTime % 1000;
	struct tm * UTC = gmtime(&timer);
	sprintf(ISO8601Time,"%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
		UTC->tm_year+1900,
		UTC->tm_mon+1,
		UTC->tm_mday,
		UTC->tm_hour,
		UTC->tm_min,
		UTC->tm_sec,
		milliseconds);
}
void GetISO8601Time(char ISO8601Time[25])
{
	GetISO8601Time(ISO8601Time, clock());
}

Here’s an example of code that sets the time offset

int ReaderDevicesetTimeUTC(char * InputBuffer, int InputBufferSize, const unsigned short LastSample, uart_init_struct &std_uart, FILE * tx)
{
	struct tm UTC;
	UTC.tm_year = 0;
	UTC.tm_mon = 0;
	UTC.tm_mday = 1;
	UTC.tm_hour = 0;
	UTC.tm_min = 0;
	UTC.tm_sec = 0;
	UTC.tm_wday = 0;
	UTC.tm_yday = 0;
	UTC.tm_isdst = 0;
	int millisec = 0;
	if (2 < sscanf(InputBuffer,"ReaderDevice.setTimeUTC:%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
		&(UTC.tm_year),
		&(UTC.tm_mon),
		&(UTC.tm_mday),
		&(UTC.tm_hour),
		&(UTC.tm_min),
		&(UTC.tm_sec),
		&millisec))
	{
		UTC.tm_year -= 1900;
		UTC.tm_mon -= 1;
		settime(mktime(&UTC));
	}
	if (tx != NULL)
	{
		char ISO8601Time[25];
		clock_t theTime = clock();
		GetISO8601Time(ISO8601Time, theTime);
		unsigned long int TimeTicks = theTime;
		fprintf(tx,"ReaderDevice.setTimeUTC:%24s TimeTicks:%lu\n", ISO8601Time, TimeTicks);
	}
	return(0);
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s