uCos task for polled I/O, reasonable speed expectations?

Discussion to talk about hardware related topics only.
Post Reply
lslarry86
Posts: 24
Joined: Tue Jul 18, 2017 12:28 pm

uCos task for polled I/O, reasonable speed expectations?

Post by lslarry86 »

I have a "legacy" system (2017+) running on 5441x and NetBurner 2.8. It polls J2.10 for a trigger and sends a line from a CSV file out the serial port when the trigger is detected. Embedded webserver, ftp server, and SD card code are all straight from examples. This has worked for several years, but I went to speed it up and found that it was missing triggers. So I took the input polling out of my main task and put it in an RTOS task. That's where things get interesting. Depending on some choices, it looks like my new task never runs, MAIN never runs, or my new task runs but misses more triggers than before.

First, the system has been working with 200 mS cycles on a 50% or 75% duty cycle, so the trigger short phase has been at least 50 mS out of 200. Now I want to detect triggers at 3 mS down to 1 mS. Is that practical with 5441X, while still supporting network services?

Next, here is my new task. Big picture, it increments a counter every rising edge.

Code: Select all

DWORD TriggerWatchTaskStack[USER_TASK_STK_SIZE];
uint32_t Trig10Count = 0;
uint32_t Trig29Count = 0;

void TriggerWatchTask(void* pUnused)
{
	int lastTrig1 = -1;
	int lastTrig2 = -1;
	/*
	DWORD bit_mask = 0;
	OS_FLAGS flags;
	flags.current_flags = 0;
	flags.pWaitinglist = NULL;
	*/
	while (TRUE)
	{
		//OSTimeDly(0); // yield, 0 ticks: misses gaps at 5/sec.  -1: the task does not run.
		//OSFlagPendAllNoWait(&flags, bit_mask); // no yield, MAIN does not run,
		int thisTrig1 = J1[10];
		J2[LedIndexes[0]] = thisTrig1;
		if (thisTrig1 != lastTrig1)
		{
			if (lastTrig1 >= 0)
			{   // TRIGGER
				bool risingEdge = (thisTrig1 == 0) ? true : false;
				if (InvertTrigger)
				{
					risingEdge = ! risingEdge;
				}
				if (risingEdge)
				{
					Trig10Count += 1;
				}
			}
			lastTrig1 = thisTrig1;
		}
		// Secondary trigger: J2.29(UART0.7 RTS)
		//triggering seems to work with or without this setting: J2[29].function(0);
		int thisTrig2 = J2[29];
		J2[LedIndexes[1]] = thisTrig2;
		if (thisTrig2 != lastTrig2)
		{
			if (WebCfgRtsTrigger())
			{
				if (lastTrig2 >= 0)
				{   // TRIGGER
					bool risingEdge = (thisTrig2 == 0) ? true : false;
					if (risingEdge)
					{
						Trig29Count += 1;
					}
				}
				lastTrig2 = thisTrig2;
			}
		}
	}
}
and this is the initialization in UserMain. In the infinite loop, UserMain checks Trig??Count and reacts appropriately.

Code: Select all

	OSTaskCreate( TriggerWatchTask,
			(void  *)NULL,
			&TriggerWatchTaskStack[USER_TASK_STK_SIZE],
			TriggerWatchTaskStack,
			MAIN_PRIO - 1); // also tried ETHER_SEND_PRIO - 1
As shown, that code prevents UserMain from running: if I click anything on the embedded web page, it errors with a "reset connection" error, and the system doesn't do anything on trigger. Strangely, LED0 and LED1 are ON steady and do not follow the inputs - does that mean that the Trigger task is not really running?

If I uncomment OSTimeDly(0), everything runs and the LEDs follow the inputs, but the system misses more triggers than it did at first. If I uncomment OSFlagPendAllNoWait(&flags, bit_mask) (with OSTimeDly(0) commented), the system acts like it did with both lines commented. Both calls are an attempt at some kind of RTOS yield. If I set the task priority at MAIN_PRIO + 1, the LEDs are off steady, telling me that the task never runs.

Obviously I'm missing something about uCos. So my questions are:
* how short of an input pulse should be detectable?
* is there a better way to detect short input pulses, in a system that is also doing network services?
* if not, how do I get my task the right number of timeslices?
User avatar
TomNB
Posts: 553
Joined: Tue May 10, 2016 8:22 am

Re: uCos task for polled I/O, reasonable speed expectations?

Post by TomNB »

Hello,

The issue you are running into is that a Real Time Operating System (RTOS) means "the highest priority task ready to run will run". It is not a time slice like you would have in linux or windows. There is an article here for 2.x tools users that may help: https://www.netburner.com/download/comp ... p-designs/. What you have is a task with a higher priority than your other tasks that never blocks, so no lower priority task will ever run.

For things like input triggers, polling is usually the last option. An interrupt either into a GPIO or a timer input channel or IRQ input pin is a much better option because you will never miss anything. There is always a chance of missing something with polling an input unless all you case about is something slow and steady state, meaning you don't care about the exact time the edge of the trigger occurred. If that is your use case, then you can add in the OSTimeDly() as you have, but that is still not as optimal solution as having an interrupt since you won't waste any cycles or take time away from the other parts of the system unless the event actually happens.

You don't want to do much in an interrupt service routine (ISR), they should only be on the order of microseconds. So one way to do this is have the higher priority task to handle things just as you have done, but also set up an interrupt for your trigger signal. The task pends on a semaphore, which is set in the ISR. So the task will never run unless an interrupt occurs.

You can read chapter 14 of the NetBurner Programmers Guide in your installation docs folder for a detailed description of how the RTOS works, blocking functions, and task switching. In general, the task scheduler will be called whenever a RTOS function blocks, or an interrupt occurs. There are also many RTOS examples in the examples folder under RTOS.

NOTE: For 3.x tools users, the NBRTOS has substantially more features than the 2.x RTOS, and the docs are online at www.netburner.com.

If you decide to go the interrupt route, your next questions will be about how to configure the hardware. I don't know much about that for a GPIO input on your particular platform, and since this is an existing design you may not have the freedom to change the signal to a different pin. Our hardware engineers can help more on that if you open a support ticket at https://support.netburner.com.

Or as I mentioned, if the time delay works for you, you can just use that. Especially if the timing isn't critical.
User avatar
pbreed
Posts: 1086
Joined: Thu Apr 24, 2008 3:58 pm

Re: uCos task for polled I/O, reasonable speed expectations?

Post by pbreed »

Which pin has the trigger?

If its one of the timer or IRQ pins we cans etup an intrrupt triggering a Semaphore so its never missed.
If you need to do polling at 1msec detection, one way would be to setup a 1Khz PIT interrupt and check in there...
When you tell me what pin your monitoring we can offer solutions.


Under 2.X software (since your running 5441X you can also run 3.x s/w)

Then

#include <pitr_sem.h>
#include <ucos.h>

//global OS_SEM mysem;

//In setup...
mysem.Init(0);
InitPitOSSem(1, &mysem,1000);

Then in your monitoring task:

while(1)
{
mysem.Pend(); //will wake up 1000 times a second
//check your input
}

Thats a little faster than the OSTimeDly(1)
Post Reply