MOD54415 HiRes Timer vs Watchdog

for everything else
jediengineer
Posts: 192
Joined: Mon Dec 17, 2012 6:24 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by jediengineer »

Thanks RidgeGlider - I've been reading up on them. Hoping to get it working correctly ASAP. I already have one task created - the UDP receive task - I need the timers to be able to interrupt that as well... We'll see how it goes. Hopefully by monday I've got it all set...
Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by Ridgeglider »

OK Just remember that if you post a semaphore, only one task, the task w/the highest prio (lowest#) that's pending for it will get it. In other words, if your timeout ISR event needs to trigger several things (MUX, UDP, etc?), you may want to have more than one semaphore. Two tasks can't pend on the same semaphore since only the one w/highest prio will get it. Not to complicate things more than needed, OS_FLAGS are similar to semaphores, but unlike pending on a semaphore, pending on an OS_FLAGS object allows a task to wait until (and this is a choice) ALL of a series of events have occurred, or ANY of a series of an events has occurred. In other words, unlike semaphores OS_FLAGS allow a task to pend on more than one event in a pretty simple way.
User avatar
dciliske
Posts: 624
Joined: Mon Feb 06, 2012 9:37 am
Location: San Diego, CA
Contact:

Re: MOD54415 HiRes Timer vs Watchdog

Post by dciliske »

Here's a really quick outline/example for you (and anyone else dealing with this). It posts to the semaphore about once per second (on a 5441X part, running at 250Mhz).

Code: Select all

#include "predef.h"
#include <stdio.h>
#include <ctype.h>
#include <startnet.h>
#include <autoupdate.h>
#include <dhcpclient.h>
#include <sim.h>
#include <cfinter.h>
#include <intcdefs.h>


extern "C" {
void UserMain(void * pd);
}


static uint32_t taskCount;
static uint32_t addTaskCount;
static uint32_t isrCount;
static OS_SEM   taskSem;
static OS_SEM   addTaskSem;
static int      taskToPost;

const char * AppName="pit_sem";

INTERRUPT( exPitISR, 0x2400 )
{
    sim2.pit[1].pcsr |= 0x0004; // Clear pit irq flag
    isrCount++;
    if (taskToPost) {
        OSSemPost( &addTaskSem );
    }
    else {
        OSSemPost( &taskSem );
    }
}

void InitPit()
{
    SETUP_PIT1_ISR( exPitISR, 4 );
    sim2.pit[1].pmr = 61035; // set count
    sim2.pit[1].pcsr = 0x0C1B; // set prescale = 4096, rld, and enable
}

void SecondaryTask( void * pd )
{
    while (1) {
        OSSemPend( &taskSem, 0 );
        taskCount++;
        iprintf("Semaphore posted, consumed by task A\r\n");
        iprintf("    isrCount: %ld\r\n", isrCount);
        iprintf("    taskCount: %ld\r\n\r\n", taskCount);
    }
}

void TertiaryTask( void * pd )
{
    while (1) {
        OSSemPend( &addTaskSem, 0 );
        addTaskCount++;
        iprintf("Semaphore posted, consumed by task B\r\n");
        iprintf("    isrCount: %ld\r\n", isrCount);
        iprintf("    addTaskCount: %ld\r\n\r\n", addTaskCount);
    }
}

void UserMain(void * pd)
{
    InitializeStack();/* Setup the TCP/IP stack buffers etc.. */

    GetDHCPAddressIfNecessary();/*Get a DHCP address if needed*/
    /*You may want to add a check for the return value from this function*/
    /*See the function definition in  \nburn\include\dhcpclient.h*/

    OSChangePrio(MAIN_PRIO);/* Change our priority from highest to something in the middle */

    EnableAutoUpdate();/* Enable the ability to update code over the network */

    StartHTTP();/* Start the web serrver */

    OSSemInit( &taskSem, 0 );
    OSSemInit( &addTaskSem, 0 );

    OSSimpleTaskCreatewName( SecondaryTask, MAIN_PRIO - 2, "Secondary Task" );
    OSSimpleTaskCreatewName( TertiaryTask, MAIN_PRIO - 1, "Tertiary Task" );

    iprintf("Application started\n");

    InitPit();
    while (1)
    {
        OSTimeDly(TICKS_PER_SECOND / 2);
        if (charavail()) {
            getchar();
            taskToPost = !taskToPost;
        }
    }
}
-Dan
Dan Ciliske
Project Engineer
Netburner, Inc
jediengineer
Posts: 192
Joined: Mon Dec 17, 2012 6:24 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by jediengineer »

Wow, between the both of you, this gives me a lot to look at! Thanks guys! let me refer back to an earlier statement - I need the timer to change depending upon which state the software is in - idle, run, or power. In idle, i only need to interrupt once per second, twice in run, and 10x per second in power mode. Can that be done with only one PIT? If I read right, there's only 2 PIT timers available so I'd have to reset the count on the timer each time I change modes... Or is this something that should be done with the HiRes Timer?
User avatar
dciliske
Posts: 624
Joined: Mon Feb 06, 2012 9:37 am
Location: San Diego, CA
Contact:

Re: MOD54415 HiRes Timer vs Watchdog

Post by dciliske »

well, If you just need to change the timings, you can do this with one pit timer, just divide that counter (pit.pmr) by 2 or 10 (or shift the prescaler, or some combination). You probably would also want to cleare the "overwrite" bit (bit 4) of the pcsr to not immediately trigger the next reset (and skipping that interrupt).

So, with a prescale of 4096, 1Hz~= 61035. For 2Hz, shift the prescaler down by one power to 2048 and keep the count. For 10Hz, divide the count by 5, to 12207 and keep the prescaler at 2048.

-Dan
Dan Ciliske
Project Engineer
Netburner, Inc
sulliwk06
Posts: 118
Joined: Tue Sep 17, 2013 7:14 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by sulliwk06 »

This might be a bit of overkill for what you need, but I'll offer it up anyways. It's a good way to manage several timers while only using one PIT


To set this up you need to create a 1 ms interrupt with a PIT and call CheckOneShotTimers() (I should probably rename it since it does continuous timers too) in it somewhere. I have a separate file that sets up the interrupt, but if you need help with that I can get it. Once you have the interrupt set up, you can create a Timer object to automatically call a function for you.

example:

Code: Select all

Timer *myTimer;

void mySemaphorePoster()
{
	//post semaphore
}

void main()
{
	bool callRepeatedly = true;
	myTimer = new Timer(mySemaphorePoster);
	myTimer->Start(1000, callRepeatedly);
}
this will call mySemaphorePoster() every 1000 ms inside your 1 ms interrupt. or you can set it to just call it once. You can stop it and start it with a new time if you need it. If you don't use a pointer to the timer object then when the timer object goes out of scope the timer is cancelled, but you can change that in the timer destructor if you want.

I haven't really put this one through the meat grinder to make sure it's 100% thread safe, but I haven't had any problems with it yet.


I redacted a little bit because some of it referenced other classes I've made.

timers.cpp

Code: Select all


struct Function
{
	void ( *function )();
};

struct OneShotTimerElement
{
	unsigned long timeOffset;
	std::list<Function> functionList;
	std::list<BackgroundTimer *> timerList;
};

std::list<OneShotTimerElement> OneShotTimerList;

void TimerExpired(BackgroundTimer *timer)
{
	if(timer->numRunningTimers-- == 1 && !timer->canceled)
	{
		switch(timer->executionLocation)
		{
		case EXECUTE_INTERRUPT:
			timer->function();
			break;

// This references a special task of mine that you won't have
//		case EXECUTE_MISC:
//			QueueFunction(timer->function);
//			break;
		default:
			break;
		}

		if(timer->continuous)
		{
			timer->Start(timer->timeLimit);
		}
		else
		{
			if(timer->selfDestruct)
			{
				delete timer;
			}
		}
	}
}

void CheckOneShotTimers()
{
	if(OneShotTimerList.size())
	{
		//if a time offset of 0 somehow gets in the list, this might be bad, but it would only happen if they specified a time of 0
		if(!(--OneShotTimerList.front().timeOffset))
		{
			std::list<BackgroundTimer *>::iterator iter;
			iter = OneShotTimerList.front().timerList.begin();

			//call all the functions in the list
			while(iter != OneShotTimerList.front().timerList.end())
			{
				TimerExpired(*iter);//->function();
				iter++;
			}

			OneShotTimerList.pop_front();
		}
	}
}

char NewOneShotTimer(BackgroundTimer *timer, unsigned long ms)
{
	if(!functionTimerInitialized)
	{
		return false;
	}

	if(OneShotTimerList.size())
	{
		std::list<OneShotTimerElement>::iterator iter;
		iter = OneShotTimerList.begin();
		while(iter != OneShotTimerList.end())
		{
			if(iter->timeOffset == ms)
			{
				iter->timerList.push_back(timer);
				return true;
			}
			if(ms > iter->timeOffset)
			{
				ms -= iter->timeOffset;//the offset times are cumulative
				iter++;
			}
			else
			{
				OneShotTimerElement element;
				element.timerList.push_back(timer);
				element.timeOffset = ms;
				OneShotTimerList.insert(iter, element);

				iter->timeOffset -= ms;//adjust the next timer
				return true;
			}
		}
		//if we hit here it was bigger than all other remaining timers, put it at the end
	}

	OneShotTimerElement element;
	element.timerList.push_back(timer);
	element.timeOffset = ms;
	OneShotTimerList.push_back(element);
	return true;
}

timers.h

Code: Select all


#ifndef _TIMERS_H_
#define _TIMERS_H_

#include <nbtime.h>

#define EXECUTE_INTERRUPT 0
#define EXECUTE_MISC 1

class BackgroundTimer;

char NewOneShotTimer(BackgroundTimer *timer, unsigned long ms);

//background timer
class BackgroundTimer
{

public:
	bool canceled;
//	time_t startTime;
	unsigned int timeLimit;//in ms
	unsigned int numRunningTimers;
	bool continuous;
	bool selfDestruct;
	void ( *function )();
	char executionLocation;//run in interrupt or misc task
//	BackgroundTimer() {selfDestruct = false; canceled = false; numRunningTimers = 0;}
	BackgroundTimer(void ( *func )(), bool repeat = false, char execLocation = EXECUTE_INTERRUPT)
	{
		selfDestruct = false;
		executionLocation = execLocation;
		canceled = false;
		numRunningTimers = 0;
		function = func;
		continuous = repeat;
	}
	void Cancel() {canceled = true;}
	void SetSelfDestruct(bool destroy) {selfDestruct = destroy;}
	void Start(unsigned int msec)
	{
//		startTime = time(NULL);
		timeLimit = msec;

		//queue timer
		if(NewOneShotTimer(this, msec))
		{
			numRunningTimers++;
			canceled = false;
		}
	}
};




//don't initialize in an interrupt
//don't set new function in interrupt
class Timer
{
	BackgroundTimer *bgTimer;

public:
	Timer() {bgTimer = 0;}
	Timer(void ( *func )(), bool repeat = false, char execLocation = EXECUTE_INTERRUPT)
	{
		if(!InInterrupt())
		{
			bgTimer = new BackgroundTimer(func, repeat, execLocation);
		}
		else
		{
			//can't create timer in interrupt
			bgTimer = 0;
		}
	}
	~Timer()
	{
		if(bgTimer)
		{
			if(bgTimer->numRunningTimers)
			{
				bgTimer->SetSelfDestruct(true);
				bgTimer->Cancel();
			}
			else
			{
				delete bgTimer;
			}
		}
	}
	void SetFunction(void ( *func )(), bool repeat = false, char execLocation = EXECUTE_INTERRUPT)
	{
		if(bgTimer)
		{
			bgTimer->SetSelfDestruct(true);
			bgTimer->Cancel();
		}

		bgTimer = new BackgroundTimer(func, repeat, execLocation);
		/*if(!InInterrupt()) //this references my interrupt file, just don't create a timer inside an interrupt
		{
			bgTimer = new BackgroundTimer(func, repeat, execLocation);
		}
		else
		{
			//can't create timer in interrupt
			bgTimer = 0;
		}*/
	}
	void Start(unsigned int msec, bool repeat = false) {if(bgTimer) {bgTimer->continuous = repeat; bgTimer->Start(msec);}}
	void Stop() {if(bgTimer) {bgTimer->Cancel();}}

};

char OneShotTimer(void ( *function )(), unsigned long ms);
void CheckOneShotTimers();

#endif

Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by Ridgeglider »

Sure, in Dan's code see the InitPit() function. It sets up a count and an appropriate prescaler, as well as telling the timer to reload (not oneshot) and then enable. Since this is a 16 bit counter, count values must be 16 bits or less. For longer intervals, slow down the count w/ the prescaler (4-bit prescaler value range from 1 to 32768). For shorter intervals speed up the prescaler. To configure a different interval, disable the IRQ (bit 0 of PCSR), then modify count (PMR register), then re-enable and set prescaler (PCSR again) in one write, so three three writes to change period. Done. But even easier, write a function to disable the pit by writing bit 0 of the PCSR as above, then just call the function declared in pitr_sem.h to set the period:
int ConfigurePitTimer( int timer, int pit_per_sec );
Then no need to calc prescaler or count.
jediengineer
Posts: 192
Joined: Mon Dec 17, 2012 6:24 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by jediengineer »

Thanks sulliwk06, but that does seem like a bit of overkill.

Dan and Ridgeglider,

I'm setting it up now - I think between the two of you, I might have a pretty good idea about how the semaphores work, and a little about the threading process. I am unsure about something though - task priority. The interrupts from this timer should be top priority - this will be constantly running and the modes will be switching consistently. Can I have more than one task thread at the same priority?

Example: I'm using the UDPReader function from an example to read incoming data and make adjustments on the fly. it will also be sending a packet out. If the timer trips in the middle of that function, which is at the same priority as the UDPReader, will it interrupt the process to complete its task or will it let the UDPReader thread finish first? Another reason I'm concerned about this is that I have the CWT running as well... It gets serviced by the timer ISR and is set for the longest I'm allowed (by spec) to let the system hang - 1.07 seconds.. So I have the 1 second timer reset the CWT - but aside from that, that's the only question hanging on my brain at the moment with respect to the semaphore use...
Ridgeglider
Posts: 513
Joined: Sat Apr 26, 2008 7:14 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by Ridgeglider »

Nope, one task per prio! After an IRQ occurs, or when the task switcher runs, the task with the highest priority that's ready to run will run. Read that statement again! When a task is blocked, (ie waiting for a resource, something like a semaphore) it is not ready to run. It becomes ready to run when the resource (semaphore) becomes available.

If two tasks are ready to run, the one w/ highest prio (lowest#) will run when the task switcher switches. A task that is running will not yield until it either hits a blocking call (eg OSSemPend(), or OSTimeDly, etc) or an IRQ occurs. When assigning task prios, you just have to decide what's most important. Top level tasks (eg: UserMain) usually depend on lower level (higher prio) tasks to run reliably.

Once a semaphore is posted by a task or ISR it does not go away. Therefore if another task is pending on that semaphore, as long as other higher prio tasks yield, (ie blocks to wait for some resource), that lower prio tasks will run as long as there's bandwidth. Of course, if your highest prio task blocks on a resource that's continuously available, no other tasks will run because there is not enough time to do everything you've scheduled.

Ideally, when you create a task you'll check the return value to be sure the task completed successfully. Task creation will fail if you try to assign a previously assigned task prio. If you're assigning two tasks to the same prio, creation WILL fail.

Note that there are two ways to create tasks. One uses macros: OSSimpleTaskCreate() or OSSimpleTaskCreatewName(). The other uses functions (and more arguments): OSTaskCreate() or OSTaskCreatewName(). ONLY the functions produce return values to tell you when there's an error:

Code: Select all

//	if (OSTaskCreate(PostTo_MySemaphore_Task,
//					(void*) MyData,	//or NULL to not pass in data
//					(void*) &PostTo_MySemaphore_TaskStk[USER_TASK_STK_SIZE],
//					(void*) PostTo_MySemaphore_TaskStk, PRIO_FOR_POST_TO_MYSEMAPHORE_TASK)
//		!= OS_NO_ERR) { // Handle error
jediengineer
Posts: 192
Joined: Mon Dec 17, 2012 6:24 am

Re: MOD54415 HiRes Timer vs Watchdog

Post by jediengineer »

Oh, Ok, I get it, makes more sense now. I tried following Dan's example but the compiler threw me an error and I'm not sure why... it doesn't say.

Code I used:

Code: Select all

#include......


OS_SEM PollTimer;

...
...
INTERRUPT(exPitISR, 0x2400)
{
	sim2.pit[1].pcsr |= 0x0004;  											// clear the PIT IRQ flag
	OSSemPost(&PollTimer);													// post semaphore for DataPoll() routine
}

void InitPIT()
{
    SETUP_PIT1_ISR( exPitISR, 4 );											// setup PIT to trigger ISR
    sim2.pit[1].pmr = 61035; 												// set count
    sim2.pit[1].pcsr = 0x0C1B; 												// set prescale = 4096, rld, and enable
}

void DataPoll()
{
	while (1)

		OSSemPend( &PollTimer, 0);
		sys_poll();
		// reset CWT
		sim2.scm.cwsr = 0x55;
		sim2.scm.cwsr = 0xAA;
		iprintf("ISR Complete. \r\n");
	}
}

void UserMain(void*pd)
{
...
...
    OSSemInit( &PollTimer, 0 );												// Initialize semaphore

    OSSimpleTaskCreatewName(DataPoll, MAIN_PRIO -2, "Timed Data Poll");		// create task with higher priority than main and UDPReaderMain 

    InitPIT();

    while(1)
   {
   ...
   }

}
the error comes at this line:

Code: Select all

 OSSimpleTaskCreatewName(DataPoll, MAIN_PRIO -2, "Timed Data Poll");
and the compiler says:
"..\main.cpp:437: error: invalid conversion from 'void (*)()' to 'void (*)(void*)'
..\main.cpp:437: error: initializing argument 1 of 'BYTE OSTaskCreatewName(void (*)(void*), void*, void*, void*, BYTE, const char*)'"

Is this something I did in my code? Did I not understand What Dan posted?
Post Reply