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

Handling hardware and software events (in C).

Discussion in 'Code Repository' started by misterT, Feb 18, 2016.

Tags:
  1. misterT

    misterT Well-Known Member Most Helpful Member

    Joined:
    Apr 19, 2010
    Messages:
    2,681
    Likes:
    361
    Location:
    Finland
    The example application here needs to handle two buttons: Button 1 and Button 2.
    - Both buttons have a dedicated interrupt service routine (isr). If Button 1 is pressed, "isr_button_1();" is executed etc.
    - The application needs to detect each button press.
    - The application also needs to detect a button press combo 1-1-2.

    The goal is to abstract the events from hardware level to software level efficiently. To separate the 'business logic' from hardware in a flexible way.

    The Main-function to test and showcase the event system:
    Code (C):
    int main(void)
    {
        initialize_eventlogger();
        initialize_uart(B38400);

        printf("Reset\n");

        /* Simulate button pushes by calling the interrupt service routines.
           Combo 1-1-2 should be detected by the software event system */

        isr_button_1();
        isr_button_2();
        isr_button_1();
        isr_button_1();
        isr_button_2(); /* Combo */
        isr_button_2();
        isr_button_1();
        isr_button_1();
        isr_button_1();
        isr_button_2(); /* Combo */
        isr_button_1();

        /* Force combo event in software */
        eventlogger_event(EVENT_BUTTON_COMBO);

        /* Main loop */
        while (1)
        {
            static struct event next_event;

            /* Handle events */
            while(eventlogger_get(&next_event))
            {
                if      (next_event.type == EVENT_BUTTON_1)     { printf("Button 1\n"); }
                else if (next_event.type == EVENT_BUTTON_2)     { printf("Button 2\n"); }
                else if (next_event.type == EVENT_BUTTON_COMBO) { printf("COMBO!\n"); }
                /* You can have as many different kinds of events you want.. */

                /* Call the event callback */
                next_event.callback(&next_event);
            }
        }
    }
    /******************************************************************************/
    This outputs:
    Code (text):
    Reset
    Button 1
    Button 2
    Button 1
    Button 1
    Button 2
    Button 2
    Button 1
    Button 1
    Button 1
    Button 2
    Button 1
    COMBO!
    COMBO!
    COMBO!
    The Interrupt Service Routine (ISR) functions for the buttons:
    Code (C):
    void
    isr_button_1(void)
    {
        /* Do debounce and other low level logic.
           . . .  */

        /* Raise event */
        eventlogger_create_event(EVENT_BUTTON_1, &combo_state_machine, NULL);
    }
    /******************************************************************************/

    void
    isr_button_2(void)
    {
        /* Do debounce and other low level logic.
           . . .  */

        /* Raise event */
        eventlogger_create_event(EVENT_BUTTON_2, &combo_state_machine, NULL);
    }
    /******************************************************************************/
    These two ISR functions create a software event which is handled in the main loop. They pass a function pointer with the event. This is the event callback which in this case points to a function which tracks the button press combo.

    Code (C):
    void
    combo_state_machine(struct event *e)
    {
        static uint8_t state = 0;

        /* Simple state machine to detect combo 1-1-2 */

        if (state == 0) {
            /* For combo, we expect button 1 */
            if (e->type == EVENT_BUTTON_1) { state++; } else { state = 0; }
        }

        else if (state == 1) {
            /* For combo, we expect button 1 */
            if (e->type == EVENT_BUTTON_1) { state++; } else { state = 0; }
        }

        else if (state == 2) {
            /* For combo, we expect button 2 */
            if (e->type == EVENT_BUTTON_2) {
                /* We have a combo! */
                eventlogger_event(EVENT_BUTTON_COMBO);
                state = 0;
            }
        }

        /* Reset state machine by default */
        else { state = 0; }
    }
    /******************************************************************************/
    This kind of software event system is very powerful tool to handle all kinds of events.. external or internal. Below is the eventlogger-module source.

    Code (C):
    /*
    * eventlogger.h
    */


    #ifndef EVENTLOGGER_H
    #define EVENTLOGGER_H

    /* Preprocessor includes */
    #include <stdint.h>

    /* Event type enums */
    enum event_type {
        EVENT_VOID,
        EVENT_BUTTON_1,
        EVENT_BUTTON_2,
        EVENT_BUTTON_COMBO
    };


    /* Typedef for event callback function pointers.
       This is a pointer to functions with prototype:
       void callback(struct event *e); */

    struct event;
    typedef void (*event_callback)(struct event *e);

    struct event {
        uint32_t          timestamp_sec;
        enum event_type   type;
        event_callback    callback;
    };

    void initialize_eventlogger(void);

    /* Copies the event passed to the event queue
       Does not make changes to the passed event (hard copy)
       Return 0 on success, 1 on fail (buffer full) */

    uint8_t eventlogger_put(struct event* e);

    /* Copies the next event in the queue to given placeholder struct.
       Return 1 if event was available, 0 if event buffer empty */

    uint8_t eventlogger_get(struct event* e);

    /* Helper function to create simple events.
       Return 0 on success, 1 on fail (buffer full) */

    uint8_t eventlogger_event(enum event_type type);

    /* Helper function to create events.
       Return 0 on success, 1 on fail (buffer full) */

    uint8_t eventlogger_create_event(enum event_type type, event_callback callback, void *data);

    #endif /* EVENTLOGGER_H */
    Code (C):

    /*
    * eventlogger.c
    */


    /* Preprocessor includes */
    #include <stdlib.h>
    #include <stdint.h>
    #include "eventlogger.h"


    /*******************************************************************************
    * Private variables and constants
    */

    #define MAX_EVENTS (16)

    volatile static struct event event_pool[MAX_EVENTS];

    volatile static uint8_t fifo_head; /* write */
    volatile static uint8_t fifo_tail; /* read */


    /*******************************************************************************
    * Private Function declarations
    */


    /* Updates the head or tail variable to next data in fifo buffer
       Returns 0 on success and 1 on failure (buffer full or empty) */

    uint8_t next_head(void);
    uint8_t next_tail(void);

    /* Default callback function. Does nothing. */
    void idle_event_callback(struct event *e);


    /*******************************************************************************
    * Function definitions
    */


    void
    initialize_eventlogger(void)
    {
        /* Initialize event pool */
        uint8_t i = 0;
        for(i=0; i<MAX_EVENTS; i++)
        {
            event_pool[i].timestamp_sec = 0;
            event_pool[i].type = EVENT_VOID;
            event_pool[i].callback = &idle_event_callback;
        }

        /* Initialize fifo */
        fifo_head = 0;
        fifo_tail = 0;
    }
    /*****************************************************************************/


    uint8_t
    eventlogger_get(struct event *e)
    {
        if(e == NULL) {
            /* Null pointer */
            return 0;
        }

        /* Advance the tail index */
        if(next_tail()) {
            /* Buffer empty, nothing to get */
            return 0;
        }

        /* Copy the event */
        e->timestamp_sec = event_pool[fifo_tail].timestamp_sec;
        e->type = event_pool[fifo_tail].type;
        e->callback = event_pool[fifo_tail].callback;

        return 1;
    }
    /*****************************************************************************/

    uint8_t
    eventlogger_put(struct event* e)
    {
        if(e == NULL) {
            /* Null pointer */
            return 1;
        }

        /* Advance the head index */
        if(next_head()) {
            /* Buffer full */
            return 1;
        }

        /* Create new event */
        event_pool[fifo_head].timestamp_sec = e->timestamp_sec;
        event_pool[fifo_head].type = e->type;
        event_pool[fifo_head].callback = e->callback;

        return 0;
    }
    /*****************************************************************************/


    uint8_t
    eventlogger_event(enum event_type type)
    {
        return eventlogger_create_event(type, &idle_event_callback, NULL);
    }
    /******************************************************************************/


    uint8_t
    eventlogger_create_event(enum event_type type, event_callback callback, void *data)
    {
        /* Advance the head index */
        if(next_head()) {
            /* Buffer full */
            return 1;
        }

        if (callback == NULL) { callback = &idle_event_callback; }

        /* Initialize new event */
        event_pool[fifo_head].timestamp_sec = 0;
        event_pool[fifo_head].type = type;
        event_pool[fifo_head].callback = callback;

        return 0;
    }
    /******************************************************************************/


    uint8_t
    next_head(void)
    {
        uint8_t head;

        head = fifo_head + 1;

        if(head >= MAX_EVENTS) {
            /* The index overflows -> wrap */
            head = 0;
        }

        if(head == fifo_tail) {
            /* The buffer is full -> return with error */
            return 1;
        }

        /* All is OK, update head and return success */
        fifo_head = head;
        return 0;
    }
    /*****************************************************************************/


    uint8_t
    next_tail(void)
    {
        if(fifo_head == fifo_tail) {
            /* Buffer is empty -> return error */
            return 1;
        }

        /* Find next tail */
        fifo_tail++;

        if(fifo_tail >= MAX_EVENTS) {
            /* The index overflows -> wrap */
            fifo_tail = 0;
        }

        return 0;
    }
    /*****************************************************************************/


    void idle_event_callback(struct event *e)
    {

    }
    /*****************************************************************************/
     
    Last edited: Feb 19, 2016
  2. Mikebits

    Mikebits Well-Known Member

    Joined:
    May 24, 2008
    Messages:
    6,063
    Likes:
    153
    Location:
    San Diego, Ca
    Nice job Mister.
     

Share This Page