Handling hardware and software events (in C).

Status
Not open for further replies.

misterT

Well-Known Member
Most Helpful Member
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:
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:
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:
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.

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.

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 */

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:
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…