Pumpkin User Forums
  Coding
  Too short OSDelay()

Post New Topic  Post A Reply
profile | register | preferences | faq | search

UBBFriend: Email This Page to Someone! next newest topic | next oldest topic
Author Topic:   Too short OSDelay()
Jenny
Junior Member
posted November 01, 2006 11:29     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
Ok, it all seems so obvious now. I'll try both of your suggestions. Thanks a lot for all help!

/Jenny

IP:

aek
Moderator
posted November 01, 2006 07:53     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
So, I think I understand your situation now.

You have a function (SED_Bell()) that generates a sound and the sound is 400ms long. Correct?

So, here is what is happening in your system:

OSTimer() is called every 40ms by an ISR. This happens all the time (i.e. every 40ms Salvo registers that a system tick has happened).

Additionally, OSSched() runs "as often as possible" from main(), i.e. it runs whenever a task yields via as OS_Xyz() call. Note that during the 400ms of SED_Bell(), OSSched() is not called (because only a task can yield to the scheduler, and SED_Bell() is a function).

So what happens is that during SED_Bell(), the numer of ticks that were called (400/40 = 10) is saved up in Salvo's lost tick counter, because OSSched() is not called during SED_Bell(). Then, after SED_Bell() finishes, you call OS_Delay() (which is a context switch), and then OSSched() runs. (BTW, you can watch OSlostTicks from within MPLAB.) Since there are lost ticks "saved up" in the system, they need to be cleared out ASAP by OSSched(), and so any delay that is less than the number of loast ticks will expire immediately. This is absolutely the correct behavior, because it's imperative that Salvo get all the delays "back on track" to correct for the fact that you have held off the scheduler for 400ms (10 ticks).

When you have a function like SED_Bell() that causes a task to no longer yield to the scheduler in a reasonable time (i.e. > 1 system tick), you are no longer multitasking, and you will see behavior like what has perplexed you here.

If you must have accurate (<< 1 system tick) timing with SED_Bell(), then you should use a High ISR to generate the sound. This will have two benefits -- 1) it will be most accurate, because it will be driven solely by the (hardware) interrupt system, and 2) as long as you set OSPIC18_INTERRUPT_MASK to only disable low-priority interrupts and make Salvo service calls (e.g. OSTimer()) from a low ISR, then the timing of your high ISR will be unaffected (zero interrupt latency) by Salvo. With a configuration like this, everything will work exactly as you think it should -- Salvo will do all the multitasking (including task delays), and your app will alsohave super-accurate timing with things like the sound. I'd suggest reviewing http://www.pumpkininc.com/content/doc/press/pumpkin_supsi2005.pdf -- this talks precisely about this sort of approach.

If the 400ms does not have to be accurate, then you could use Salvo to generate those delays as well (i.e. use in-line code to do what SED_Bell() is doing, with OS_Delay() as the means of setting the delays).

Another option is to set the tick period to be 500ms -- that would be the quickest (obvious) fix. But this solution does not address the fact that you are killing multitasking with a SED_Bell() function that lasts 400ms.

quote:
Another question out of curiosity, what happens if something is executed that takes several ticks but the OSTimer and scheduler is not called during this period of time? Will it be like if "time stood still"?
Yes, but OSTimer() is normally called from an ISR, and so you shouldn't (ever) disable that ISR. Your problem is all due to the fact that SED_Bell() lasts 400ms and during that time, you do not yield to OSSched().

------------------

IP:

Jenny
Junior Member
posted November 01, 2006 00:16     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
Hi Andrew,

The sound pattern is; sound, OSDelay(), sound, OSDelay(), sound, OSdelay(). I did not know that the scheduler needs to be called every tick, I thought that yielding every 400 ms would be enough since the other tasks wants to run every 500ms-10,000ms (except for one task that will be slightly delayed). How can I solve this without disturbing the sound? If I yield in the middle of the loops this is the effect. Do I need to generate the sound in a high ISR instead? Another question out of curiosity, what happens if something is executed that takes several ticks but the OSTimer and scheduler is not called during this period of time? Will it be like if "time stood still"?

Thanks.
Jenny

IP:

aek
Moderator
posted October 31, 2006 09:19     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
quote:
The nestled functions are supposed to generate sound, a specific pattern 400ms*3 with an OSDelay(5, soundTask) between each repetition. So, yes the task will be occupied through several ticks (~40 between each repetition). Is this a problem? The OSTimer is still called every 10 ms. Should the task be shorter? Is there a limit to the execution time of a task?
400ms (milliseconds)? Not 400us (microseconds)? If milliseconds, yes, you will have a problem.

You're saying that the pattern should be:

...
pattern for 400ms (inline delay)
pattern for 400ms (inline delay)
pattern for 400ms (inline delay)
delay for 50ms (via OS_Delay)
...

Is this correct?

There is no limit to how long the task can run.

Please clarify, then I can continue.

Note: Any time that you are running (e.g. in a task) and not yielding back to the scheduler (via an OS_Xyz()) within a system tick, you are causing the timer system to "fill up". As soon as you yield to Salvo's scheduler, the timer system will "clear out" up to 255 "lost ticks". If e.g. a task was delayed with 3 ticks prior to the hold-off, and the holdoff lasts 19 ticks, then that task will run immediately as soon as the schduler runs, because its ticks expired while the scheduler was held off because the tasks did not yield for 19 ticks. So, normally we see these sorts of delay issues from tasks that began their delays before the failure to yield in a task. But that's not what you are doing (because you said that the problem delay is in this very task, and you said the delay is essentially 0 instead of 5 ticks).

------------------

IP:

Jenny
Junior Member
posted October 31, 2006 06:03     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
Hi again!

I have narrowed it down to just one task and tested it in MPLAB SIM (10 MHz). The problem still exists.

The nestled functions are supposed to generate sound, a specific pattern 400ms*3 with an OSDelay(5, soundTask) between each repetition. So, yes the task will be occupied through several ticks (~40 between each repetition). Is this a problem? The OSTimer is still called every 10 ms. Should the task be shorter? Is there a limit to the execution time of a task?

I attach the code used in MPLAB SIM:

code:

#include <salvo.h>
#include <iros.h>


static void TaskBac(void);
static void SysInit(void);


#define DELAY_50_MS 5
#define DELAY_200_MS 20
#define DELAY_1150_MS 115

void main( void )
{
/* initialize Salvo.*/
OSInit();

/* create tasks */
(void)OSCreateTask(TaskBac, TASK_BAC_P, (UInt8)PRIO_TASK_BAC);

/*lint -e717 */
OSEi();
/*lint +e717 */
for (;;)
{
OSSched();
}
}


static void TaskBac(void)
{
static sysStates_et sysState = SYS_RUN_INIT;
static UInt8 times = 2;
/*
sound generator
*/
static UInt8 repeats; // no of repeats
static UInt8 cycles; // no of cycles, constant
static UInt8 timeHL; // time high to start with, counted down
static UInt8 cntr = 0;
static UInt8 flash = 0;
static UInt8 tmpTimeHigh;


for(;;)
{
switch(sysState)
{
case SYS_SHUTDOWN:
//nothing happens here, debug version
break;

case SYS_STANDBY:
//nothing happens here, debug version
break;

case SYS_START_UP_TEST:
//nothing happens here, debug version
break;

case SYS_RUN_INIT:
// Start timers, interrupts, set variables
SysInit();
/* ok, lets do the state stuff */
/* Let the started tasks exec */
OS_Delay((UInt16)DELAY_200_MS, TaskBac);
sysState = SYS_RUN;
break;

case SYS_RUN:
/* IN Port */
repeats = 3;
cycles = 0x35;
do
{
timeHL = 20;
do
{
//simplified SED_BELL cut'n pasted into task

//REMOVING THIS while loop -> OSDelay() works fine, 50ms delay!
/* do
{
tmpTimeHigh = timeHL;

//REMOVING THIS while loop -> OSDelay() works ok, 45ms delay!
while(tmpTimeHigh)
{
tmpTimeHigh--;
}
}
while(--cycles);*/
//end of simplified SED_BELL
--timeHL;
}
while(timeHL > (UInt8)4);
LED_WARN_OUT = 1;
OS_Delay((UInt16)DELAY_50_MS, TaskBacAlarm);
LED_WARN_OUT = 0;
}
while(--repeats);

OS_Delay((UInt16)DELAY_1150_MS, TaskBacAlarm);
break;

case SYS_SERVICE:
//nothing happens here, debug version
break;

default:
break;
}
}
}

static void SysInit(void)
{

/* T0CON TMR0ON T08BIT T0CS T0SE PSA T0PS2 T0PS1 T0PS0
* 0 1 0 1 0 1 1 1
*/
T0CON = 0x57;
/* Timer 0 int */
INTCON2bits.TMR0IP = 0;
/* enable int prio */
RCONbits.IPEN = 1;
/* Start interrupts*/
INTCON |=0xE0;
/* Start timer 0*/
T0CONbits.TMR0ON = 1;
}


#pragma interruptlow ISRLow save=PROD,section(".tmpdata")
void ISRLow( void )
{
// rember to set RB6 & RB7 low when alarm not active
if ( INTCONbits.TMR0IE && INTCONbits.TMR0IF )
{
INTCONbits.TMR0IF = 0;
TMR0H = TMR0H_RELOAD;
TMR0L = TMR0L_RELOAD;
(void)OSTimer();
}

}


[This message has been edited by aek (edited October 31, 2006).]

IP:

Jenny
Junior Member
posted October 27, 2006 07:04     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
I have tried without optimization, will try to narrow it down to just this task.

Thanks!

IP:

aek
Moderator
posted October 27, 2006 00:19     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
Looking at your latest description, it sounds like there is some effect of your nested functions that is affecting overall timing. Do you have some numbers (in ms) as to how long those delays take?

I suspect what is happening is that you have something in that task that is taking much longer than you think -- several ticks worth -- and this is skewing the behavior of OS_Delay().

------------------

IP:

aek
Moderator
posted October 27, 2006 00:14     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
Two things:

1) Have you tried the code with all optimizations disabled?

2) Can you reproduce the problem (maybe a simple applicvation with only this task) in the mPLAB SIM?

------------------

IP:

Jenny
Junior Member
posted October 26, 2006 22:26     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
Sorry for being unclear 25' = 25,000 instructions. I was a bit quick when I cut'n pasted the code, of course it should be static, I changed this but the problem still exists. Any ideas?

IP:

aek
Moderator
posted October 26, 2006 09:16     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
quote:
//REMOVING THIS while loop -> OSDelay() works fine!
Change it (tmpTimeHigh) to static. The current Salvo port for MCC18 does not support any auto variables in tasks. Note that in your original post all of the task's variables were static, and so I didn't mention this -- thought you knew.

------------------

[This message has been edited by aek (edited October 26, 2006).]

IP:

aek
Moderator
posted October 26, 2006 09:13     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
quote:
My tick rate is not too fast I think, 25' instructions (10MHz) between calling OSTimer().
25 instructions? Or 25,000 instructions.

I.e. what is the rate (measured, with a 'scope) at which you are calling OSTimer()?

------------------

IP:

Jenny
Junior Member
posted October 26, 2006 01:11     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
Hello again,

Thanks for your quick reply, I've been occupied but looked at your tips yesterday.
1) My tick rate is not too fast I think, 25' instructions (10MHz) between calling OSTimer().
2) Using MPLAB I can see that value=5 is used in OSDelay()
3) Changing the interrupt mask is not helping
4) I think this is the problem and I have tried to slim down the code. When I only used the OSDelay()'s in the task without function calls the OSDelay's works fine. However when adding the do{...}while loops the OSDelay will fail when I add a while{} loop in the last do{..}while loop (see code below). (SED_Bell() contains a do{..}while loop with while{} calls in it, see the first code I sent you). I troubleshoot by setting a LED before the OSDelay() call, clearing it after the call and then look at it on the oscilloscope. The PIC handle the loops correctly but it seems as though something that the OSDelay() is using is manipulated when adding the last loop. The system just seems to yield(OS_Yield) since the time the LED is lightened can vary, 1-6ms or more. Doing two calls to OSDelay() after each other will result in what seems to be a yield and then a real delay. I attach some code that is slimmed down and simplified.

Code:

code:
static void TaskBacAlarm(void)
{
/*
sound generator
*/
static UInt8 repeats; // no of repeats
static UInt8 cycles; // no of cycles, constant
static UInt8 timeHL; // time high to start with, counted down
static UInt8 cntr = 0;
static UInt8 flash = 0;
UInt8 tmpTimeHigh;
/* Init */

for(; ;)
{
/* IN Port */
if (cntr)
{
repeats = 3;
cycles = 0x35;
do
{
timeHL = 20;
do
{
//simplified SED_BELL cut'n pasted into task
do
{
tmpTimeHigh = timeHL;

//REMOVING THIS while loop -> OSDelay() works fine!
while(tmpTimeHigh)
{
tmpTimeHigh--;
}
}
while(--cycles);
//end of simplified SED_BELL
--timeHL;
}
while(timeHL > (UInt8)4);
LED_WARN_OUT = 1;
OS_Delay((UInt16)DELAY_50_MS, TaskBacAlarm);
LED_WARN_OUT = 0;
}
while(--repeats);
}
else
{
LED_BAC_ON = 0;
LED_WARN_OUT = 1;
OS_Delay((UInt16)DELAY_50_MS, TaskBacAlarm);
LED_WARN_OUT = 0;
}
cntr ^= 1;
OS_Delay((UInt16)DELAY_1150_MS, TaskBacAlarm);
}
}


[This message has been edited by aek (edited October 26, 2006).]

IP:

aek
Moderator
posted October 19, 2006 09:51     Click Here to See the Profile for aek     Edit/Delete Message   Reply w/Quote
That's odd ... since Salvo's timer accuracy is specified as +/- 1 tick, you should only see "nonexistent" delays if you are doing OS_Delay(1) since 1 (-1) = 0. Any delay argument > 1 should always result in a proper, noticeable delay.

I can think of a few things:

1) your tick rate is too fast for your clock, i.e. not enough instructions transpire between calls to OSTimer(). We recommend 2,000-10,000 instruction cycles between calls to OSTimer(). Is it possible some other part of your application is messing with your clock?

2) I would examine the disassembly to ensure that the value=5 parameter is in fact being used in that particular call to OS_Delay(). Occasionally one finds a compiler error.

3) Are you controlling your interrupts (see OSPIC18_INTERRUPT_MASK) properly? If not, you could have an interrupt-based corruption of that parameter or of Salvo itself.

4) Are you _sure_ that the OS_Delay() call is failing, and that it's not something else in overall program flow.

The way I would debug this would be to step through the Salvo source (either via a debug-enabled library, or via a source-code build) and verify that a value of 5 is making its way into the function OSDelay(). This would eliminate all the variable issues and point most likely at incorrect use of OSPIC18_INTERRUPT_MASK.

The way you're using OS_Delay() is consistent across the calls and I don't see any glaring errors.

------------------

IP:

Jenny
Junior Member
posted October 19, 2006 08:02     Click Here to See the Profile for Jenny     Edit/Delete Message   Reply w/Quote
I am using a PIC18F4620 with compiler mcc18 v2.4 and salvo pro v3.2.2, MPLAB v7.31. I have some problems with one OSDelay() call in a task, it seems as though it is just yielding. The OSTimer() is called in an ISR every 10 ms. The other OSDelay:s seems to work fine, the one:s in the same task as well as in the other tasks. The delay I want is 50 ms ( OSDelay(5,Task) ) but when I debug it the execution time often is around 1 ms and the OSTimer() is only called once before it returns to the next instruction in the task. What am I doing wrong? If I change the number of ticks I start to get a delay when tick >40 (40 ms when tick=40, tick=50 -> delay of 160ms).

Code:

code:

/*
****************************************************************************
** Runtime include files
****************************************************************************
*/
#include <delays.h>
#include <usart.h>
#include <portb.h>

/*
****************************************************************************
** OS include files
****************************************************************************
*/
#include <salvo.h>
#include <iros.h>


#define DELAY_50_MS 5
#define DELAY_100_MS 10
#define DELAY_1150_MS 115


static void TaskBacAlarm(void)
{
static UInt16 inP1;
static UInt16 percP1;
static calibPressure_st cpress;
static movementAlarms_et passAlarm;
/*
sound generator
*/
static UInt8 repeats; // no of repeats
static UInt8 cycles; // no of cycles, constant
static UInt8 timeHL; // time high to start with, counted down
static UInt8 cntr = 0;
static UInt8 flash = 0;

/* Init */
cpress = CAL_GetCalibPressure();

for(; ;)
{
/* IN Port */
inP1 = ts_P1;
passAlarm = MVT_alarm;
if(passAlarm == MVT_NO_ALARM )
{
percP1 = P1_ConvertToPercentage(inP1);
// is Pressure below alarm level?
if(inP1 <= cpress.alarm)
{
if (cntr)
{
repeats = 3;
cycles = 0x30;

// Instead of calling function PressureAlarm we generate the sound in task context so we can
// yiel to let the other task run.
// set low pressure alarm variable to true, high and low interrupt can not be disbled->miss a button press
lowPressAlarmOn = TRUE;
do
{
timeHL = 20;
do
{

SED_Bell(timeHL,0,cycles);
--timeHL;
}
while(timeHL > (UInt8)5);

if (repeats == 2)
{ //display red light in the middle of sound
LED_BAC_ON = 0;
LED_WARN_OUT = 1;
}
SED_Bell(timeHL,0,cycles);
//THIS OSDELAY IS THE ONE NOT WORKING
OS_Delay((UInt16)DELAY_50_MS, TaskBacAlarm);
LED_WARN_OUT = 0;
}
while(--repeats);
lowPressAlarmOn = FALSE;
}
else
{
LED_BAC_ON = 0;
LED_WARN_OUT = 1;
OS_Delay((UInt16)DELAY_50_MS, TaskBacAlarm);
LED_WARN_OUT = 0;
}
cntr ^= 1;
ts_lowPress = TRUE;
}
else
{
if(ts_PositionLight)
{
LED_WARN_OUT = 0;
LED_BAC_ON =(UInt16)1;
OS_Delay((UInt16)DELAY_100_MS, TaskBacAlarm);
LED_BAC_ON =(UInt16)0;
}
ts_lowPress = FALSE;
}
}
else if(passAlarm == MVT_P_ALARM)
{
if(ts_PositionLight)
{
if (flash)
{
LED_BAC_ON =(UInt16)1;
}
else
{
LED_WARN_OUT = (UInt16)1;
}
flash ^= (UInt16)1;
OS_Delay((UInt16)DELAY_100_MS, TaskBacAlarm);
LED_BAC_ON =(UInt16)0;
LED_WARN_OUT =(UInt16)0;

}
}
OS_Delay((UInt16)DELAY_1150_MS, TaskBacAlarm);
}
}


[This message has been edited by aek (edited October 19, 2006).]

IP:

All times are ET

next newest topic | next oldest topic

Administrative Options: Close Topic | Archive/Move | Delete Topic
Post New Topic  Post A Reply
Hop to:

Contact Us | Pumpkin Home Page

©2000-2008 Pumpkin, Inc. All Rights Reserved. Pumpkin and the Pumpkin logo, Salvo and the Salvo logo, The RTOS that runs in tiny places, CubeSat Kit and the CubeSat Kit logo are all trademarks of Pumpkin, Inc. All other trademarks are the properties of their respective owners.


Ultimate Bulletin Board 5.46a