Continue to Site

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.

  • 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.

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:
Nice job Mister.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top