1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

Here are some PIC multi-tasking systems

Discussion in 'Microcontrollers' started by Mr RB, Jan 2, 2010.

  1. 3v0

    3v0 Coop Build Coordinator Forum Supporter

    Joined:
    Jul 14, 2006
    Messages:
    9,404
    Likes:
    227
    Location:
    OKLAHOMA USA
    The use of the ADC is a good example of what is missing here.

    Reading an ADC has two parts. You start the conversion then wait for the conversion to finish. The read_adc() calls does both, not good in a multitasking system. The amount of time is small here but we could just as easily be waiting for a button push.

    In terms of this system we only want to bump sync[current] when the conversion is finished. As it exists the bumping in done by the THREAD_END macro. The thread should not advance past the test state/chunk unless the condition is satisfied.

    Code (text):

    #define AD0 0
    #define SW0 1

    THREAD_START(AD0) // adc related process
    adc_start_conversion(0);
    THREAD_BREAK
    [B]if (!adc_finished(0)) noBump=FALSE;[/B]
    THREAD_BREAK
    peocess_adc_value():
    THREAD_END(AD0)

    THREAD_START(SW0)  // a process using a switch
    // whatever setup is required
    THREAD_BREAK
    [B]if (not_switch_active) noBump=FALSE;[/B]
    THREAD_BREAK
    // post press peocessing
    THREAD_END(SW0)

     
    THREAD_START nees to clear the noBump flag
    THREAD_END should not bump the seq[] if noBump is set
     
  2. smanches

    smanches New Member

    Joined:
    Mar 5, 2009
    Messages:
    986
    Likes:
    8
    Location:
    Oregon, USA
    Actually, since they have a Stack Pointer, Stack Frame pointer and a stack limit register, you do not need to save the stack at all, just those registers. The stack is one big area of memory that you can pre-allocate (for lack of a better word) and just restore the pointers for each thread.

    Other than saving/restoring registers, and updating thread priority lists, there's not a lot of overhead on those PICs. The biggest advantage to a fully pre-emptive system is the user does not have to compile the tasking system into their code. It's not quite as efficient, as with combining all code, but gives the flexibility to run any relocatable code transparently.

    Since you can only do relocatable code, it would be perfect for on-demand loadable modules.
     
  3. Mr RB

    Mr RB Well-Known Member

    Joined:
    Jul 22, 2008
    Messages:
    4,716
    Likes:
    194
    Location:
    Out there
    Hmm. Good catch! I didn't imagine that would be needed, but I can see how it could be very useful. I think your suggested implementation is close to perfect, they only change I would suggest is maybe to set the noBump variable within the chunk itself;

    Code (text):

    THREAD_START(AD0) // adc related process
    adc_start_conversion(0);  // this chunk sets noBump, if it needs encores
    THREAD_BREAK
    process_adc_value():
    THREAD_END(AD0)
     
    I think that is more elegant as the need to re-iterate a chunk is really a property of that chunk itself, not a property of that thread which will have many chunks with differing needs.

    If anything what it really means is any thread chunk/state has the ability to call for an "encore" if it chooses, so each chunk can call for as much timeslice as it likes before the next chunk gets a go. It is a big step towards the co-operative model though with the chunks and/or threads having more control of timeslice and away from a preemptive (or "externally controlled") model where the chunks have little or no control.

    I lean towards that externally controlled model because to me the best multitasking system is to have multiple fast loops that can run at the same time. But I'm beginning to see you lean more towards an "object oriented" model where threads can just halt and wait for a button press etc. (Please correct me if I'm wrong there!)

    I don't like that object oriented model much. When I was reading through the XMOS examples they have 8 threads, and their object oriented model means that a UART thread will be completely halted waiting for a byte, and a keyboard thread will be halted waiting for a button press. That can make sense when the micro is REALLY good at passing that processor time on to the other (working) threads. But in a PIC system that is simulated multi-tasking and really NOT good at passing that time onto other threads it seems more sensible to steer away from a pure object oriented approach and use the multi-threads more like a few simultaneous small PICs.

    Where you might halt a thread waiting for a button press I would do something like run that thread every 250mS, and just poll the button as ONE of the thread chunks as there are probably other tasks that would be good to run every 250mS. Definitely a more vertical model with a few threads running a number of tasks each, rather than a pure horizontal model with a lot of threads doing one task each.

    (edit) Actually if a chunk wanted an encore it could just directly decrement the seq[] variable, so the macros could remain standard and not get larger and incur additional overhead.
     
    Last edited: Jan 13, 2010
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Mr RB

    Mr RB Well-Known Member

    Joined:
    Jul 22, 2008
    Messages:
    4,716
    Likes:
    194
    Location:
    Out there

    Nice! I didn't know the stack operated like that and could be handled that easily. That definitely makes it sound a bit more attractive.

    But I'm not sure you are right about the user not having to compile the multitasking system into their code. They still need to choose the number of threads, and write the code as separate threads, and be aware of the threads.

    Then all the thread stacks will manually have to be loaded at the start of execution, with the starting program counters for each thread. So that will require some effort by the user to org each thread code at specific start location in ROM, or have some constants for the starting org locations for each thread. I guess if the code is setup as proper "relocatable" then maybe it can auto calc all the thread start program counters.

    It would be a cool project but I'm not sure it will be THAT much more user friendly than the other systems. Seeing that the user still has to define threads, the only extra benefit is they don't need to insert thread breaks!
     
  6. 3v0

    3v0 Coop Build Coordinator Forum Supporter

    Joined:
    Jul 14, 2006
    Messages:
    9,404
    Likes:
    227
    Location:
    OKLAHOMA USA
    duplicate post
     
    Last edited: Jan 14, 2010
  7. 3v0

    3v0 Coop Build Coordinator Forum Supporter

    Joined:
    Jul 14, 2006
    Messages:
    9,404
    Likes:
    227
    Location:
    OKLAHOMA USA
    @RB:
    The idea of having the blocking thread stat decrement the seq var is good.


    Code (text):

    THREAD_START(AD0) // adc related process
    adc_start_conversion(0);
    THREAD_BREAK
    [B]if (!adc_finished(0)) seq[AD0]--;[/B]
    THREAD_BREAK
    peocess_adc_value():
    THREAD_END(AD0)
    I was under the impression that we were talking about cooperative
    multi tasking. By "Externally controlled" I assume you mean that there
    is some code outside the tasks that control execution. This can be the
    case in both cooperative and preemptive systems. Can we agree that this
    not task code is called a kernel?

    Structured programing and OOP are philosophies, we can code
    in their styles using any procedural language.
    Structured programming was developed in response to spaghetti
    code. OOP is an effort to make large systems more manageable
    and has its detractors.
    So if you are saying that I lean in the direction of good
    coding practices you are correct. But I did not suggest
    that we use objects.

    At the most fundamental level the difference between what weW
    are doing is in the placement of the state guard statement.
    The line of code that determines if any given state will execute.

    Your method embeds a guard statement for each state within the
    thread. NAOS uses a single guard statement per thread in the kernel
    (main loop). The state within the thread is selected via a
    switch/case, a n way branch.

    NAOS is a thread per task system. It allows one to set priority and
    blocking on a per task basis. The use of this
    in conjunction with ISR routines that unblock when resources become
    available is a powerful mechanism.

    As I said earlier a well designed cooperative system can out preform
    a preemptive one.
     
  8. smanches

    smanches New Member

    Joined:
    Mar 5, 2009
    Messages:
    986
    Likes:
    8
    Location:
    Oregon, USA
    I think more of what you are talking about here is signal handling. Having a thread block on a slow call until the state of whatever you are monitoring changes. This is the one place where an "ouside" influence would be good. For many of the events, you could tie the signal right to an interrupt, which can "wake-up" the thread in question. Although then you need someway to remove it from the active processing list and put it back on later.

    You could also do a Sleep() model where a thread can basically schedule itself. This could be used for polling routines without much of a change in the co-op code. Again, would need an actual scheduler though, so another outside influence.

    EDIT: I just realized that 3v0 already said much of this. :p
     
    Last edited: Jan 15, 2010
  9. 3v0

    3v0 Coop Build Coordinator Forum Supporter

    Joined:
    Jul 14, 2006
    Messages:
    9,404
    Likes:
    227
    Location:
    OKLAHOMA USA
    If anyone is interested in an example of a non intrusive multitasking system look at the one used by forth interpreters. Forth is a stack based language consisting of words and operators used with reverse Polish notation. As I recall it forth includes a thread switch at the end of each word. The down side is that it does a lot of unrequired thread switching.

    The NAOS system operates much as smanches suggested regarding sleeping. If one embraces the nature of this system you can do some interesting and powerful things using the sleep timers.

    The sleep timers in NAOS are located in array kTimer[]. An ISR decrements each kTimer element 4000 times per second. The use of the kTimer array allows us to use a single timer as it were many. Tasks may use one or more kTimers. kTimer values are polled.

    In the tutorial taskBLINK uses a single kTimer to blink an led.

    In the tutorial taskBEEP uses two kTimers to produce an alternating tone and rest without the use of loops. Using the timers alow us to set the duration of the sound independent of the frequency.

    Task BEEP uses two kTimers BEEP and DUR (duration). At the start of the task DUR is set to 750
    which is a bit under 1/2 second. kTimer BEEP is set to 10 in the state that switches the speaker on and off resulting in a (4000/(10+10)) or 1000Hz tone. The state that turns the speaker off checks to see if kTime DUR is zero. If not it goes back to the on state, If zero it sets kTimer BEEP to 750 which causes the task to sleep with the speaker off, musical rest. It also set the next state to init which causes it to start over.

    Code (text):

    JuneBug – BoostC - C18 Tutorials : Cooperative Multitasking (1st Draft, 4rd Edit)
    void taskBeep(void)
    {
      static byte seq=0;
      switch (seq)
      {
         state 0: // state INIT
        kTimer[DUR]=750;
        seq=1;
        break;
        state 1: // state ON
        SPEAKER_BIT_HI;
        kTimer[BEEP]=2; // 1000 Hz
        seq=2;
        break;
        state 2: // state OFF
        SPEAKER_BIT_LO;;
        if(kTimer[DUR]) // another pulse
       {
          kTimer[BEEP]=2;
          seq=1;
        }
        else // generate a rest
       {
          kTimer[BEEP]=750;
          seq=0;
        }
      }
    }
     
     
  10. Mr RB

    Mr RB Well-Known Member

    Joined:
    Jul 22, 2008
    Messages:
    4,716
    Likes:
    194
    Location:
    Out there
    Some good information there, thanks for that.

    It's looking a bit done though unless someone has something else new to bring to the table.
     
  11. spyeagle

    spyeagle New Member

    Joined:
    Jan 17, 2012
    Messages:
    1
    Likes:
    0
    Hi guys,

    I read the 3v0'tutorial, it was good and clear.
    I have used the switch case technique a long time ago without task scheduler at work. It is very simple to make progress the program and each piece of code is very quick.
    I have a remark about your source code NAOS. You decrement into interruption the timer of all task with a loop "for", I think it was not a good choice. Because an interruption should be the quickest than possible, if you have a lot tasks to manage, you will stay a lot of time in interruption, and your program won't be able to run correctly. And if you need to manage SPI, UART and CAN protocols and so forth with interruptions, you microcontroller would be full and not real time.

    So, I would like to share my tiny kernel in C programming language to manage tasks on a microcontroller. You can create a task with a period, suspend, resume and change a period of task at any time. This kernel can delete all task to create another sequencer as you want.
    A TickGet function is supply by the kernel to manage all timers as you want.
    You have to create just one interrupt function and replace the function Timer() to get your tiny-kernel for your own application.
    To resume this kernel is based on a circular linked list, to switch task on task. It is willingly written in a general way to help people to customise for theirs owns applications. There is no priority between task like a round-robin task scheduling. And I wrote this source code in respect of the MISRA guidelines (automotive norm)



    I hope to help people to manage tasks on microcontroller.
     
    Last edited by a moderator: May 9, 2012

Share This Page