This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

my Function Pointer Array as State machine example



By first doing a: typedef int (*IntFunc)(void) simplifies it a little.
unlike case, functions can be repeated as you can see that red_off is there twice.

This is for a G2553 Launchpad done with IAR C99
No need for a 32K crystal as I slow down smlk/8 and TA /8 just for this example.

With more complicated states you of course don't have just to go to next state, maybe repeat same state or jump to any state.
if jumping states, best to use enum so to give each state a name.
Each state has it's own delay setting before next state is coming up, if they all have same delay move that up to single point by flag &= ~BIT0;

This is my first C code as I just started to learn it last week, been an assembler programmer for decades.
This is the correct way to program the msp430 to save power, sleep as much as possible wake up and do thing fast and come back later after a nap.
32K crystal for ACLK TA and LPM3 for best power saving of course.

#include <msp430.h>         // bitfield version is io430.h
int unsigned m1state=0;
int unsigned flag=0;
int red_on();               // declaration of functions 
int red_off();
int green_off();
int red_green_on();
int red_green_off();

typedef int (*IntFunc)(void);
IntFunc const DoState[6] = { red_green_off, red_on, red_off, red_green_on, red_off, green_off};

int main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
  P1OUT = 0;
  P1DIR = BIT0+BIT6;                        // P1.0 & P1.6 output, rest is input
  P1OUT |= BIT3;                            // prepare button 
  P1REN |= BIT3;
  P1IES |= BIT3;  
  P1IFG &= ~BIT3;                
  P1IE  |= BIT3;
  P2OUT =0;
  P2REN =0x3f;
  P3OUT =0;
  P3REN =0xff; 
  BCSCTL2 |= DIVS_3;                        // SMCLK div by 8
  TACTL = TASSEL_2 + ID_3 + MC_2;           // SMCLK /8, contmode
  while(1)
  {
    __bic_SR_register(GIE);                 // we don't want a IRQ to happen between if and __bis
    if (!flag)                              // only go back to sleep if non pending
    {  
      __bis_SR_register(LPM0_bits + GIE);   // Enter LPM0 w/ interrupt 
      __no_operation();
    }
    __bis_SR_register(GIE);                 // enable irq again
    
    if (flag & BIT0)                        // one of them that woke us up wanted us 
    {
      flag &= ~BIT0;
      m1state= DoState[m1state]();          // do the state and set the state-number for next time
    } 
  }  
}


int red_green_off(void)                     // our entry init state
  {
    P1OUT &= ~(BIT0+BIT6);                  // turn off both led's 
    CCR0 = TA0R + 4000;                     // come back soon
    CCTL0 = CCIE;                           // enable irq
    return ++m1state;
  }

int red_on(void)
  {
    P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
    P1IE  |= BIT3;                          // turn button irq back on
    P1OUT |= BIT0;
    CCR0 += 12000;
    return ++m1state;
  }

int red_off(void)
  {
    P1OUT &= ~BIT0;
    CCR0 += 15000;
    return ++m1state;
  }

int red_green_on(void)
  {
    P1OUT |=BIT0+BIT6;
    CCR0 += 22000;
    return ++m1state;
  }

int green_off(void)                         // it's also our exit state
  {
    P1OUT &= ~BIT6;
    CCTL0 = 0; 
    return 0;
  }
//\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\//
#pragma vector=PORT1_VECTOR 
__interrupt void PORT1(void)       // only one IRQ on P1 no need to search IFG register
{ 
   P1IE  &= ~BIT3;                 // turn off button irq as a way of debounce
   P1IFG &= ~BIT3;                 // Clear Interrupt Flag
   m1state = 0;
   flag |= BIT0;
   __bic_SR_register_on_exit(LPM0_bits);   
}
//\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\//
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
  flag |= BIT0;
  __bic_SR_register_on_exit(LPM0_bits);
}
//\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\////\\//\\//

  •  Good job Tony, well commented and also structured, just some caveats I read you learned C code just recently.

    Tony Philipsson said:
    int unsigned m1state=0;

     This global variable is modified by interrupt and in main, compiler maybe it can optimize it so move to registers and when interrupt code is executed don't reflect to main, this case declare as volatile to force compiler read every time before use.

    volatile int unsigned m1state=0;
    

    Tony Philipsson said:
    int unsigned flag=0;

    same for this I seen late...

    Tony Philipsson said:
    int red_green_off(void) // our entry init state { P1OUT &= ~(BIT0+BIT6); // turn off both led's CCR0 = TA0R + 4000; // come back soon CCTL0 = CCIE; // enable irq return ++m1state; }

     also this is deprecated due you return a value is still visible from caller, just pass it by value and return new value or pass by reference then change it:

    first solution control state from main:

    Tony Philipsson said:
    IntFunc const DoState[6] = { red_green_off, red_on, red_off, red_green_on, red_off, green_off};

    IntFunc const DoState[] = { red_green_off, red_on, red_off, red_green_on, red_off, green_off};
    
    Tony Philipsson said:
    m1state= DoState[m1state](); // do the state and set the state-number for next time


    DoState[m1state++]()
    if(m1state>sizeof(DoState)/sizeof(DoState[0])) m1state=0;

    Tony Philipsson said:
    int red_on(void) { P1IFG &= ~BIT3; // Clear Interrupt Flag just in case P1IE |= BIT3; // turn button irq back on P1OUT |= BIT0; CCR0 += 12000; return ++m1state; }
    int red_on(int m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        return ++m1state;
      }
    
    // call as
    nextstate=red_on(currentstate);
    
    by reference not changing source
    
    int red_on(int *m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        return *m1state + 1;
      }
    
    // call function
    nextstate = red_on(&currentstate);
    
    changing source
    
    void red_on(int *m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        *m1state+=1; // change value of variable pointed by
      }
    
    /// call function
    
    nextstate(&m1state); // next state reflected to variable pointed by


    sizeof turn size of passed object.
    Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.

    Welcome to c programmers, style and passion are your force. C is more error prone than asm, my first c approach was to return immediately to 68K coding in asm, second time 12 pages full of assembler 68K, interrupt and manipulation of array, 3 lines of c code so I left you imagine where one subtle error was ;)


  • Roberto Romano said:
    Welcome to c programmers, style and passion are your force. C is more error prone than asm, my first c approach was to return immediately to 68K coding in asm, second time 12 pages full of assembler 68K, interrupt and manipulation of array, 3 lines of c code so I left you imagine where one subtle error was ;)

     This new forum code is very crappy... broken things are more than improvement we seen...

     Syntax highlight does the horrible you can see so I leave to @BLAKEit can do some action...

    DoState[m1state++]()
    if(m1state>sizeof(DoState)/sizeof(DoState[0])) m1state=0;

    Tony Philipsson
    int red_on(void) { P1IFG &= ~BIT3; // Clear Interrupt Flag just in case P1IE |= BIT3; // turn button irq back on P1OUT |= BIT0; CCR0 += 12000; return ++m1state; }
    int red_on(int m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        return ++m1state;
      }
    
    // call as
    nextstate=red_on(currentstate);
    
    by reference not changing source
    
    int red_on(int *m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        return *m1state + 1;
      }
    
    // call function
    nextstate = red_on(&currentstate);
    
    changing source
    
    void red_on(int *m1state)
      {
        P1IFG &= ~BIT3;                         // Clear Interrupt Flag just in case
        P1IE  |= BIT3;                          // turn button irq back on
        P1OUT |= BIT0;
        CCR0 += 12000;
        *m1state+=1; // change value of variable pointed by
      }
    
    /// call function
    
    nextstate(&m1state); // next state reflected to variable pointed by


    sizeof turn size of passed object.
    Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.


    sizeof turn size of passed object.
    Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.
  • Roberto Romano said:


    sizeof turn size of passed object.
    Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of

    sorry no way to have text readable, no preview... no editable no way to point what happen.. this is a worst coding .... aand bad user interface too, care forever in front of someone can be an human using your code and treat as you like to....

    sizeof turn size of passed object.
    Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.







    sizeof turn size of passed object.
     Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.



  •  HELP

     This forum code is very buggy...!!!! BLAKE how can I remove code highlight and why posting link automatically got this?

     

    anyway last lines was:

    sizeof turn size of passed object.
     Array with empty braket is dimension free so you can add an element, then dynamic calculation on main can adjust state table without searching on code.
    array name is address

    & operator is bitwise and but also address of.
    * is pointer and dereference, on declaration set variable as pointer to, on function return the value of.

    sorry for multicomment forum interface mangled my reply....

  • >remove code highlight
    I click on the html button and edit it by hand.

    Yes returning the next state is redundant as the variable is global.
    As I can return an INT, I just wanted to return something maybe error code later
    But most fpga statemachines I seen don't auto increment to next state but each state set next state.
    But If the machine always just do next state I could see using auto-increment, it's personal taste.
     
    [] and the use sizeof was something I wanted to add later.
    Also adding array boundary check so to not crash the system if state number by an error is to high.

    Also that button IRQ forces a restart to beginning m1state but if the irq happen just before a already
    running state sets _next_state_on_exit it will no longer be 0 so need to do something there to handle that.
     
    This is a simple example and in a real world if each state are only a few instructions you could put it directly in the ISR.
    But when the state gets more complicated and some of them take to much time hogging the IRQ systems,
    setting a flag and let a main state machine handle it is better.

    You can have up to 16 statemachines (16 tasks) running at the same time, in a non-preemptive multitasking.
    And if main is already awake it does not harm that an IRQ clears lpm bits if they where already cleared.

    And one statemachine state can invoke the start another statemachine,
    by just setting that machines flag as before main is going to sleep it's checking that all flags are set to zero.

  • >But most fpga statemachines I seen don't auto increment to next state but each state set next state.

    Most likely you refer to proper state machines, not "non-preemptive task switcher" you are offering :)

    >You can have up to 16 statemachines (16 tasks) running at the same time, in a non-preemptive multitasking.

    You definitely shall not confuse state machine to task.

    No offense, but I do not share idea to split code of state machine in a way you offer - because you are breaking mostly all of the beauties of "classic" switch(state){} machines which are (usually) doing more than some predetermined, sequential set of code.

    Thou _long_ predetermined execution list of limited set of function calls would be exactly one reason why I would implement something like your code. Just to save memory I would create two arrays: put sequence with function _ID's_ into byte array and save function pointers having one item per function - into another array.

  • Maybe I'm not using correct terms but simplified: statemachine = a task = work at hand sliced up, never using delay loops etc
    This is a Hello World of task switcher to help people here understand it.

    To add later: A task can use a master fixed time sliced scheduler with ID where each task using a bits in a byte as a wheel to divide or as heartbeat pattern as you don't to waste 2 timers, as having 6 independent-in-time tasks is not needed most of time.
    Some task don't need a delay/sleep but will slice its work out of courtesy to other task.

    Not planning to write a full RTOS

  • Ilmars said:
    No offense, but I do not share idea to split code of state machine in a way you offer - because you are breaking mostly all of the beauties of "classic" switch(state){} machines which are (usually) doing more than some predetermined, sequential set of code.

     Hi Ilmars, I was writing also this, state machine generally can do some decision and state must change to next or to a loop, I prefer see state machine as a long switch case, more clean and controllable.

     Your proposal is some form of sequencer or task switcher on fixed table basis.

     Forever I prefer as Ilman suggested an array with some token relative to function list, more better is an array of struct where on record field next task delay and next function are stored to completely control task scheduling.

     Preemption require switch task list from a timed interrupt or by preempting calling task done so another task can use remaining time.

     On superloop basis to switch task and maintain an efficient parallel poll of more than one task you can define some non blocking timer you use to fire task when entered or use also to break a non responding code.

     As this is your first C exercise good work..

     Just another subtle chracter association...

     //\\//\\//\\

     good separator, artistic and amusing but take care of \char is an escape sequence and can trigger error on compiler.

     Sorry again for the unreadable post, Tony I assure you I refuse to edit html, I am not so proficient and who wrote this rubbish got pay'd to simplify our work not to worsten forum... This forum code is very worst and too many thing where perfectly working on old one where broken. I see no more performance nor improvement..

  • Don't have to edit the html to much, just delete blocks of <pre class ect
    Is this better? but some don't have their IDE set to UTF-8 but to System

    //┌───────────────────────────────────────────────────────────────────────┐
    #pragma vector=PORT1_VECTOR 
    __interrupt void PORT1(void)       // only one IRQ on P1 
    
    //╞═══════════════════════════════════════════════════════════════════════╡
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void Timer_A (void)
    
    //└───────────────────────────────────────────────────────────────────────┘

  • >Not planning to write a full RTOS

    Good idea because there are many around - for virtually any taste or need. Thou I know that it is tempting to prove that you can (too), prove mostly to yourself. What's important - to know when to stop, to know when it's enough trying to build something that already exists and build that is truly unique.
  • Tony Philipsson said:
    Yes returning the next state is redundant as the variable is global.

     Sorry Tony, I tryid wrote on hashed post, this is deprecated, you are returning the value of a global variable and you are accessing a global variable inside a procedure, this is deprecated. Also if you need a state variable internal to a function/procedure use a static variable but not a gloabal one, you can use a global one if you need share to many procedure, don't forget volatile also in this case due one routine can alter this variable and compiler must be informed of.

     

    Tony Philipsson said:
    But most fpga statemachines I seen don't auto increment to next state but each state set next state.
    But If the machine always just do next state I could see using auto-increment, it's personal taste.

     State machine on FPGA are different things, which language you know? If VHDL then state machine are not real and hardware is sintetized from sequence and silicon maybe don't have variable nor bit are used... FPGA are parallel hardware and state machine work in a different way so implementation is not one to one to language as is C, C is deterministic in execution (not deeply optimized) FPGA code can change from one build to next one with just another small bit.

    Tony Philipsson said:
    This is a simple example and in a real world if each state are only a few instructions you could put it directly in the ISR.
    But when the state gets more complicated and some of them take to much time hogging the IRQ systems,
    setting a flag and let a main state machine handle it is better.

     Forever is a bad idea have a long interrupt handler, I learned to make it as short as possible and demand execution to main loop.

     

    Tony Philipsson said:
    You can have up to 16 statemachines (16 tasks) running at the same time, in a non-preemptive multitasking.
    And if main is already awake it does not harm that an IRQ clears lpm bits if they where already cleared.

     I cannot figure how this can be done without a scheduler, if you build a good cooperative multitask it can work better than preemptive, the worst we have at hand is windoze, the worst system I ever seen.

    Tony Philipsson said:
    And one statemachine state can invoke the start another statemachine,
    by just setting that machines flag as before main is going to sleep it's checking that all flags are set to zero.

     This is a sort of waiting queue or primitive semaphore but not a real multitask.. To multitask you need a context switcher where stack is saved and another stack pattern can be loaded from stack list to activate a new task.

     I used seldom preemption and task switching on MSP, good cooperative and driver interrupt based are the most solid.

     Semaphore are clean, embedding a task switch as I seen on a tampered code is not good and sometimes is compiler dependent too...

  • Any E2E specific feedback or questions please direct to the (Missing Forum) including on the 3rd party syntax highlighter tool. Let's keep this Forum thread on topic covering "my Function Pointer Array as State machine example" from Tony.

  • >the worst we have at hand is windoze, the worst system I ever seen.

    Worst system for what purpose? I hope that I will not start opsystem advocacy/adoration flames, however I believe that _every_ system we know have it's purpose. Otherwise we would not hear of it. Perhaps windose is good to run broadest range of various applications/games/crapware we can't even count, but is really bad as deterministic realtime operating system which it never even pretended to be? With similar success we can compare x86 to msp430 or apples to oranges
  • Ilmars said:
    Worst system for what purpose?

     Hi Ilmars, purpose is not what I was addressing, so it don't care, maybe for some task can be good, but between your word too there is the answer, we learned from our old teacher and book how to build an operating system, we seen other than they recommended...

     On MSP430 side I was of the idea starting some crappy manipulation of stack or starting jumping from one piece of code to another is like destructuring code.. (Famous "spaghetti like programming")

     Real time sometime can depend on how things are assembled on, having the best preemptive fast context switching cannot help run smoothlessy crapped code.

     On MSP430 I think two choice can be good, super loop with good support for polling and timeout or RTOS of choice, this can simplify code but require who write it to have knwledge of how multitask has to be used.

     I discouraged Tony from wrong habit, is more simple when one is at begin of learning.

  • What I hear - that we both more or less agree. I hardly can imagine (commercial) product that would need task switcher. Simple systems supposedly shall have superloop, for complex systems you better use RTOS. It is always better to throw out semaphore/mutex/mailbox support when you don't need them rather than add one when it appears to be necessary. Task switchers are indeed good for learning and maybe some quick proof of concept prototyping when you don't have RTOS tools and knowledge on hand but want to quickly throw various code together, verify first and only then think how to code-size optimize if ever needed.

  • I just found that there is something called QP
    Is this not something between superloop and full blow RTOS?
    ------------------------------------------
    QP™ (Quantum Platform) is a family of lightweight, open source software frameworks for building responsive and modular real-time embedded applications as systems of cooperating, event-driven active objects (actors)

    Active objects (actors) are encapsulated state machines that run in their own thread of execution and process events asynchronously using an event-driven receive loop. They inherently support and automatically enforce the best practices of concurrent programming, such as: keeping the thread's data local and bound to the thread itself, asynchronous inter-thread communication without blocking, and using state machines instead of convoluted IF-ELSE logic (a.k.a. "spaghetti" code). In contrast, raw RTOS-based threading lets you do anything and offers no help or automation for the best practices.
  • Tony Philipsson said:
    I just found that there is something called QP
    Is this not something between superloop and full blow RTOS?

    Reading provided description it looks to me like non-preemptive RTOS with some new twist. Interesting stuff! In my opinion worth to try and tell your experience - if you wish and have time for that.

**Attention** This is a public forum