MOD54415 HiRes Timer vs Watchdog
-
- Posts: 192
- Joined: Mon Dec 17, 2012 6:24 am
Re: MOD54415 HiRes Timer vs Watchdog
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...
-
- Posts: 513
- Joined: Sat Apr 26, 2008 7:14 am
Re: MOD54415 HiRes Timer vs Watchdog
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.
Re: MOD54415 HiRes Timer vs Watchdog
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).
-Dan
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 Ciliske
Project Engineer
Netburner, Inc
Project Engineer
Netburner, Inc
-
- Posts: 192
- Joined: Mon Dec 17, 2012 6:24 am
Re: MOD54415 HiRes Timer vs Watchdog
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?
Re: MOD54415 HiRes Timer vs Watchdog
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
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
Project Engineer
Netburner, Inc
Re: MOD54415 HiRes Timer vs Watchdog
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:
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
timers.h
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);
}
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
-
- Posts: 513
- Joined: Sat Apr 26, 2008 7:14 am
Re: MOD54415 HiRes Timer vs Watchdog
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.
int ConfigurePitTimer( int timer, int pit_per_sec );
Then no need to calc prescaler or count.
-
- Posts: 192
- Joined: Mon Dec 17, 2012 6:24 am
Re: MOD54415 HiRes Timer vs Watchdog
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...
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...
-
- Posts: 513
- Joined: Sat Apr 26, 2008 7:14 am
Re: MOD54415 HiRes Timer vs Watchdog
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:
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
-
- Posts: 192
- Joined: Mon Dec 17, 2012 6:24 am
Re: MOD54415 HiRes Timer vs Watchdog
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:
the error comes at this line:
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?
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)
{
...
}
}
Code: Select all
OSSimpleTaskCreatewName(DataPoll, MAIN_PRIO -2, "Timed Data Poll");
"..\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?