I've seen this strange behaviour several times and always attributed it to errors in my code. Now I am beginning to think there is a problem with task switching.
My program requires PID for actuators and physics calculations for acceleration curves. Both are very sensitive to timing, especially differential part of PID. Here is how it is implemented:
- Main task executes in a loop calling some logic and I/O procedures. It prepares sensor readings in global variables and sends serial commands using values from other global variables.
- Additional WaitForInterval task takes sensor readings from main task, calculates PID and curves and puts results into global variables.
Now, unless I put sleep() into main loop the additional task becomes erratic and sometimes fails to execute at all.
In another program I had main task with loop and two additional tasks. This did not work at all untill I put sleep() into all tasks including main. However in main loop there was a branch activating when a button is pressed and I forgot to put sleep() into that branch. Every time I pressed button the other two tasks simply stopped.
Edit: forgot to mention, this is running on ZX24n
Main task blocks others
Re: Main task blocks others
I'm not sure I completely understand the circumstances that lead to the problem. Can you create a minimal test case that reproduces the issue?wwwoholic wrote:Now I am beginning to think there is a problem with task switching.
- Don Kinzer
I don't have working device at the moment to check the example, but here is a very simplified snippet of the code that failed last time:
In the code above if I remove "call sleep(MAIN_PERIOD)" from the main loop then it often gets stuck sending the same output over and over, as if MotorTask() never called.
Code: Select all
public sub Main()
call SetInterval(CURVE_PERIOD)
CallTask MotorTask, MotorStack
do
input = GetADC(pin)
call PutQueueByte(txQueue, output)
call sleep(MAIN_PERIOD)
loop
end sub
public sub MotorTask()
do
call WaitForInterval(0)
call LockTask()
output = ProcessCurves(input)
call UnlockTask()
loop
end sub
How fast do you expect the task to actually see a new value to report back to the main task?
The Main task will loop very fast, and there may not have been time for the task to report a new value before the main task has sent a LOT of messages withthe same value.
Alternatively, there is a stack problem. Either too small a task stack, or a stack overrun from too much RAM utilization.
-Tony
The Main task will loop very fast, and there may not have been time for the task to report a new value before the main task has sent a LOT of messages withthe same value.
Alternatively, there is a stack problem. Either too small a task stack, or a stack overrun from too much RAM utilization.
-Tony
I think main task spends most of the time waiting for the queue. This actually might explain the behaviour if queue blocking somehow prevents other tasks from running.
I don't think this is stack problem since program works just fine with sleep(). Besides, in my estimate stack is about 50 bytes larger than necessary for the longest invocation chain.
I don't think this is stack problem since program works just fine with sleep(). Besides, in my estimate stack is about 50 bytes larger than necessary for the longest invocation chain.
It doesn't as can be shown with the simple test program below. In this test case, the main task floods a queue with data. The queue can either be the transmit queue for Com1 or an unserviced queue. The task "task1" simply toggles a pin as an indication that it is running. If you compile and run this program you'll find that the pin toggles irrespective of which queue is used proving that queue blocking in one task, whether temporary or permanent, does not prevent another task from running.wwwoholic wrote:This actually might explain the behaviour if queue blocking somehow prevents other tasks from running.
So far, I have not been able to construct a test case that behaves as you have described. If you can devise one, that would be most helpful.
Code: Select all
Const taskPin as Byte = A.7
Dim ts1(1 to 100) as Byte
#if 0
' define the queue as the Com1 transmit queue
Dim oq() as Byte Based Register.TxQueue
#else
' define an unserviced queue
Dim oq(1 to 20) as Byte
#endif
Dim i as Byte
Sub Main()
# set the target pin to be an output
Call PutPin(taskPin, 1)
' invoke the alternate task
CallTask task1, ts1
' This loop floods the queue with data, faster than
' it can be removed (if at all).
Do
If (i < 10) Then
Call PutQueueByte(oq, i + &H41)
i = i + 1
Else
Call PutQueueByte(oq, &H0d)
Call PutQueueByte(oq, &H0a)
i = 0
End If
Loop
End Sub
Sub task1()
Do
' toggle a pin to indicate that this task is running
Register.Pin(taskPin) = PortMask(taskPin)
Loop
End Sub
- Don Kinzer