Page 1 of 2

Noob question about creating threads

Posted: Tue Apr 24, 2012 5:11 pm
by MasterFrmMO88
I dont do much C programming so my knowledge is rather limited.

I have an app loaded on my MOD5270 that simply receives a command over a TCP connection and executes one of 32 functions based on the command it receives. Each function sets a GPIO pin to high, waits 1/10 of a second then sets it back to low, which is when the function completes. So the TCP server can send commands at a rate of 500 a second and the Client (NB) can only interpret commands at a rate of 10 a second. Basically I am looking for a way to get rid of this bottle neck and I am asking if someone can help me with a bit of code that will start a new thread each time the client receives a command, then close that thread when the function completes, but still be able to receive commands and create threads during this time. The Client will only have to run each function once so at any given time the absolute maximum (however very unlikely) amount of threads that would be running is 32.

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 6:03 pm
by seulater
Explain a bit more about what your doing. I can tell you that NB can receive and process TCP packets far, far greater than 10 a second.
Also, your not going to want or need to create that many threads.

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 6:18 pm
by MasterFrmMO88
Okay. I actually calculated the processor clock on it awhile back and I know it can process TCP commands much faster, on the order of less than a millisecond if memory serves. I figured I would only be able to have a handful of running threads and 99.999% of the time I would only need a handful anyway. But here's what happens:

-TCP receives a packet (in this case a single character)
-the single character is used in a select case statement
-based on the case, it will run 1 of 32 different functions:
-function1{
J2[X].set();
OSTimeDly(2);
J2[X].clr();
}

That's the entire process. Based on how simple it is, I KNOW it should be able to go faster. Where I am wanting it to go from here is I dont want the function call to halt the TCP process. Furthermore, the TCP process needs to be able to call more of those functions even if one is already running. There are 32 separate functions like that so it wont need to call the same function twice.

Does that help a bit?

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 6:31 pm
by seulater
let me ask this. you mentioned that the client is receiving 500 commnads per sec.
so what happens in the case where you want to set one pin high and before the OSTimeDly(2); for that pin expires you receive another command for another pin.
do you need to still finish out that delay, or do you want to cancel it and set the new pin state?

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 7:41 pm
by Chris Ruff
Here's an idea....

1.Instantiate a PIT timer at, say, 10ms period
2.your select() falls through with a tcp message- you parse it, set the GPIO high, and call the function.
3. in the PIT timer, if the GPIO pin is found high, drop it in the interrupt code n 10-milliseconds later.

don't use any OSTimeDly() calls. each call is 50ms. they are not compatible with what you are trying to do.

You can speed up the entire Netburner uCOS latency period from 50ms to 10 or 20ms if you need to.
Look in the programmer's manual for TICKS_PER_SECOND. I have done this on many projects to make
everything quicker. (even though the thrashing of the OS increases- it speeds up certain types of programs-
especially ones written with OSTimeDly() coded)

Chris

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 8:33 pm
by MasterFrmMO88
seulater wrote:let me ask this. you mentioned that the client is receiving 500 commnads per sec.
The server CAN send 500 commands per second but that's a special case and most of the time it wont even exceed a handful per second. The main reason for this is to make sure that if it does exceed ten per second, those pins can still be set. At this point, if the server tries to send commands faster than 100ms, those commands get dropped and the pins never get set.
seulater wrote: so what happens in the case where you want to set one pin high and before the OSTimeDly(2); for that pin expires you receive another command for another pin.
do you need to still finish out that delay, or do you want to cancel it and set the new pin state?
I need it to finish out the delay. The delay is because the device reading the pin's high state has to have adequate time to take the reading.

The reason I was thinking of threads is because after creating the thread, it would be relatively autonomous in that, as one command comes in it will start a thread to set that pin to high, then immediately go back to reading the connection instead of having to wait out the delay to set the pin back to low. So a scenario (not exact, just hypothetical) would be:

0ms: command is received
1ms: thread 1 starts to set pin x to high
2ms: TCP function resumes
50ms: command is received
51ms: thread 2 starts to set pin y to high
52ms: TCP function resumes
101ms: thread 1 sets pin x back to low and stops thread
151ms: thread 2 sets pin y back to low and stops thread

Using the linear progression it does now with the OSTimeDly, the command at 50ms wouldn't be received because it is processing the delay of the first command.

Also, Chris, I have a PIT already created on the program for a 1ms delay. The easiest way I see of doing it at this point is to take the separate functions out all together and set the pins high directly in the case statement. Creating an array of integer variables that would all start counting up with the PIT. As the pin is set to high it's particular variable is restarted to 0. Then as the TCP function continues, it checks that the variable is still under 100. When it reaches 100, it sets the corresponding pin back to low. Is that kind of what you mean? (Again, C isn't my language and everything else is self taught to this point so if I'm way off base, chalk it up to inexperience.) Although, I could see doing something like that, but to keep things from being too bogged down (again) I would prefer to run the PIT counter in a separate thread. Since all the variables run at once, I would only need one additional thread for that which leads me back to my initial question about a simple example for starting a thread.
Chris Ruff wrote:don't use any OSTimeDly() calls. each call is 50ms. they are not compatible with what you are trying to do.
And yes, I've hated that **** thing since day one. There's a previous topic floating around here somewhere with me ranting about the precision of it being absolute crap. I'm just not entirely sure how to work around it in this case.

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 9:20 pm
by ScottM
This is a little underspecified. You want a pin to go high for 0.1 secs. But what do you want to have happen in this case:?

0 ms: receive command to set line 1 high (and go off at 100ms)
50ms: receive command to set line 1 high (and go of at...)

The answer might be a) 150ms or b) 200ms, depending on what you're after. Or this might be a don't-care condition; you might KNOW you'll never get overlapping commands for the same pin. But which behaviour you want changes how you tackle it.

You don't actually need threads for this, and if you like answer (b) above, it's probably simpler without them. Also, how accurate do you want the timing, and does it matter how much of the board's processing you chew up doing this?

What I might do is set up a DMA interrupt to go off every (say) 10ms. I'd also have an array of "countdowns", one value for each pin. On each interrupt, scan the array, and decrement any nonzero value. If the decrement results in 0, set the corresponding pin off.

When a command comes in, set the corresponding array location to 10, and then turn the corresponding pin on. (Do those two steps with interrupts turned off.)

This approach is a little nasty because some pins might only be on for 90ms, not 100ms. It also does more in an interrupt handler than some might like. But it's small and simple. And it's trivial to decide how to handle overlapping requests for the same pin - you either SET the array slot to 10, or ADD 10 to it, when the command comes in. If you need more precision, set the timer to 1ms interrupts and use counts of 100. It chews up more of the CPU, but you might not care.

Re: Noob question about creating threads

Posted: Tue Apr 24, 2012 11:30 pm
by MasterFrmMO88
ScottM wrote:The answer might be a) 150ms or b) 200ms, depending on what you're after. Or this might be a don't-care condition; you might KNOW you'll never get overlapping commands for the same pin. But which behaviour you want changes how you tackle it.
The program on the server side prevents an over-lap scenario, but in that case it would most likely be choice a in that it would just restart the 0.1 sec delay.
ScottM wrote:Also, how accurate do you want the timing, and does it matter how much of the board's processing you chew up doing this?
The timing in the delay doesn't matter too much as long as the pin is high long enough for the receiving end to read it. In terms of processing, it's is not terribly important so long as the delay between a TCP packet receipt and execution remains small (<5ms preferable).
MasterFrmMO88 wrote:What I might do is set up a DMA interrupt to go off every (say) 10ms. I'd also have an array of "countdowns", one value for each pin. On each interrupt, scan the array, and decrement any nonzero value. If the decrement results in 0, set the corresponding pin off.

When a command comes in, set the corresponding array location to 10, and then turn the corresponding pin on. (Do those two steps with interrupts turned off.)
That's pretty much exactly what I was describing in my last post. My question is can I set this up so it runs independent of the TCP function? I'd like the TCP function and the GPIO functions to be able to run simultaneously so the only delay created in the TCP function by the GPIO functions is caused simply by resource use.

I apologize for all of the confusion. I'm having a hard time describing what I want because I am learning as I go. I can handle the array but could you give me a short example of the code for the DMA interrupt?

Re: Noob question about creating threads

Posted: Wed Apr 25, 2012 12:01 am
by tod
I can tell you the general architectural approach I take and maybe that will help or at least give you some ideas. When the main process boots I have it launch a second task that does nothing but process incoming tcp commands. The main task can then enter a main loop where it pends on a mailbox for some period of time typically 1-5 ticks, if it doesn't get any cmd from the tcp thread it does whatever other housekeeping it needs to do any then pends again.

You will find a partially elided and commented version below. It uses some calls from my library but I hope the names are clear enough to give you the general idea. I usually set my TCP task to a priority of HTTP_PRIO-1, which gives it a higher priority than the main task (and the HTTP Server if it's running). I don't want to miss any incoming TCP commands, even if they have to qet queued up before I act on them. I launch the TCP task before I enter the MainEventLoop, but it could also be launched here.

Code: Select all

    void Startup::MainEventLoop()
    {
        const bool FOREVER = true;
        const int MAILBOX_WAIT_TICKS = 5;
        const int NUMBER_MAILBOX_WAITS = 5;

        OS_MBOX& tcp_mailbox = m_tcpServer.GetMailbox();

        //Sometimes I'll spin up other tasks here. 
        EncoderTask encoder_task;
        encoder_task.SetTaskPriority(MAIN_PRIO + 3);
        encoder_task.Startup();

        unsigned int msg_counter = 0;

        while (FOREVER)
        {
            BYTE err;
            const void * const pdata_from_tcp = OSMboxPend(&tcp_mailbox, MAILBOX_WAIT_TICKS, &err);

            if (err != OS_TIMEOUT) //We got a command from the user
            {
                string puser_command = static_cast<const char* const > (pdata_from_tcp);
                ParseIncomingMsg(puser_command); //Incoming TCP message gets acted on here.
            }
            else  //no commands were detected
            {
                m_touchPanelMediator.UpdatePhaseDisplay();

                if (msg_counter % NUMBER_MAILBOX_WAITS == 0) //Things we want to do at a slower rate
                {
                    UpdateLockDetectOnDisplayIfChanged();
                }
                //Things we want to do every time a mailbox wait times out go here
                ++msg_counter;
            }
        }
    }



If ParseIncomingMsg() has to do anything long it either uses timers, interrupts or it sends a message to another task. I don't really want to be caught up doing things that keep the main loop from cycling for more than maybe 10-20 ms. I messed around with tasks a lot when writing my core library and unit testing it. Starting and Killing tasks is a tricky business. Much better to just start a few and keep them alive. When I need to share data among tasks I pass it via Mailboxes or one of the other mechanisms offered by the uCOS.

Re: Noob question about creating threads

Posted: Wed Apr 25, 2012 5:08 am
by Ridgeglider
There are lots of timer resources:
Four 16 bit PIT timers. I don't think there's an app note for the 5270 PT, but here's one for the 5282. I seem to remember that the '82 interrupts get set up a little differently than on other platforms, but the ISR itself should be very similar. The system uses PIT0 I believe to create the OSTimeDly intervals. http://www.netburner.com/downloads/mod5 ... ppNote.pdf

Four 32 bit DMA Timers
http://www.netburner.com/downloads/mod5 ... -timer.pdf

This hardware is used in the "Stopwatch" code, now called the "HiResTimer" described in this post: http://forum.embeddedethernet.com/viewt ... ?f=7&t=397