Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

Event Driven Programming Worksheet

The Superloop

So far most of the code we have been writing makes use of the superloop

int main() {
  // put your setup code here, to run once:
  while(1) {
      ledOne = 1; 
	  ledTwo = 1;
      wait(0.5);
      ledOne = 0;
	  ledTwo = 0;
      wait(0.5);
      }
}

While this is a simple approach to implement, we start to get problems when we need to do different tasks at the same time.

  • Tasks are implemented Sequentially
  • If one task performs many steps, we have to wait for it to complete before we can move on to the next one
  • What happens if we have a task that needs to deal with getting user input?

One (simple to program, but complex to design) solution to dealing with several tasks is to add state to the Loop. For example:

while(1){

   if (State == 0){
      // Do State 0 Things
   }
   else if (State == 1){
      // Do State 1 Things
   }
}

And a solution to the user input problem is to use busy waiting

 while(1) {
	 if buttonPressed {
	     ...Do Something
		 }
	 }

However, both these approaches have can get complicated very quickly, as we need to keep track of multiple states, and design complex ways of dealing with multiple things happening at once.

Interrupts

A Better approach is to break the program into a set of tasks, and trigger these when required. Each Task can be encapsulated in its own Function.

We can attach this Function to an interrupt. This allows our system to respond quickly to events occurring in the system.

Interrupts Button Example

We made use of Interrupts in the first session. Using a Hardware interrupt to trigger an event when a button was pressed.

Here we:

NOTE: Hardware Interrupts can fire based on different changes of state in the PIN. Here we attach to the rising state, so the interupt is fired when the button is pressed.

  • Rising (Moving from OFF to ON)
  • Falling (Moving from ON to OFF)
InterruptIn button(BUTTON1)

//Function called when button is pressed.
void hander(){
  ... //Do Something
}

int main(){
	//Attach interrupt
	button.rise(&hander);
}

Ticker Example

We can also use Software Interrupts, through a ticker object. (https://os.mbed.com/docs/mbed-os/v5.15/apis/ticker.html)

The following code creates a ticker, which is then used to fire an interrupt every second. This provides us one way of triggering events that occur at regular intervals.

#include "mbed.h"

Ticker theticker;
DigitalOut led1(LED1);
DigitalOut led2(LED2);

void handler() {
    led2 = !led2;
}

int main() {
    theticker.attach(&handler, 1.0); // call flip function every 1 seconds

    // spin in a main loop. ticker will interrupt it to call handler
    while(1) {
        led1 = !led1;
        wait(0.5);
    }
}

TASK:

Our Ticker only blinks 2 LEDS, lets add some more to the Mix:

  • Modify the code above (or in the Lab sheet)
    • Use theticker to Get LED2 to flash every 2 Seconds
    • Get LED3 to flash 10 times at in interval of 0.5 Seconds
    • Use a ticker object to run the LED3 code every 5 seconds.
  • Do you notice a problem with this approach?

Code Demonstrating The Problem with Interrupts.

#include "mbed.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

InterruptIn btn(BUTTON1);

//Function that performs a blocking task 
void handler(){
    for (int x=0; x<10; x++){
        led2 = !led2;
        wait(0.25);
    }
}

//Main Loop
int main() {
	//Attach our blocking function to an interupt
    btn.rise(&handler);
    
    while (1){
        led1 = !led1;
        wait(0.5);
    }
}

Using Event Queues

Event Queues

Event queues give us a nicer way of dealing with things from interrupts. We can generate an Event Queue Object, then assign tasks to it.

This has the Advantage of moving the code outside of the

Each of these tasks is then executed in Sequence.

Event Queue Code (Hardware with Threading)

#include "mbed.h"
#include "mbed_events.h" 

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

//Event Queue Version
// Create a queue that can hold a maximum of 32 events
EventQueue queue(32 * EVENTS_EVENT_SIZE);
//Create an empty thread to run objects in the event queue
Thread thread;

InterruptIn button(p12);
//InterruptIn(BUTTON1);

void ledEvent(){
    for (int x=0; x<10; x++){
        led2 = !led2;
        wait(0.1);
    }
}
 
 
//Main Program
int main() {    
    // Start the event queue
    thread.start(callback(&queue, &EventQueue::dispatch_forever));
	//And make the button interrupt add an event to the queue
    button.rise(queue.event(ledEvent));

    while (true) {
        led1 = !led1;
        wait(0.25);
        //Debug with Light
        led3=buttonState;
    }
}

Event Queues Interrupts and other Locations

We can also add items to the Event Queue from other locations

#include "mbed.h"
#include "mbed_events.h" 

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);

//Event Queue Version
// Create a queue that can hold a maximum of 32 events
EventQueue queue(32 * EVENTS_EVENT_SIZE);
//Create an empty thread to run objects in the event queue
Thread thread;

InterruptIn button(BUTTON1);
//InterruptIn(BUTTON1);


void ledThreeEvent(){
    led3 = !led3;
}

void ledEvent(){
    //Add a new event to toggle the LED
    queue.call(ledThreeEvent);
    for (int x=0; x<10; x++){
        led2 = !led2;
        wait(0.1);
    }
}

 
//Main Program
int main() {    
    // Start the event queue
    thread.start(callback(&queue, &EventQueue::dispatch_forever));
	//And make the button interrupt add an event to the queue
    button.rise(queue.event(ledEvent));

    while (true) {
        led1 = !led1;
        wait(0.25);
    }
}

Event Queue Code (Simulator)

As we cannot have threads (except the main thread) in the event queue we neeed to take a slightly different approach.

Here we use the main Thread to hold the event queue object.

#include "mbed.h"
#include "mbed_events.h"

// An EventQueue is a very useful construct in Mbed OS, it allows you to schedule events
// and to defer from one context to another (e.g. from ISR to normal thread) without
// writing your own state machines, and while maintaining context.
// https://os.mbed.com/docs/v5.6/tutorials/the-eventqueue-api.html
EventQueue queue;

DigitalOut led1(LED1);
DigitalOut led2(LED2);

InterruptIn btn(BUTTON1);

void blink_led() {
    printf("blink_led invoked\n");
    led1 = !led1;
}

// This does not run in an ISR, so it's safe to use `printf` or other blocking calls
void btn_fall() {
    printf("btn_fall invoked\n");
    led2 = !led2;
}

int main() {
    // Schedule an event to run every second
    queue.call_every(1000, &blink_led);

    // Normally code in the `fall` handler runs in an ISR,
    // but you can directly defer it to the thread that runs the queue
    btn.fall(queue.event(&btn_fall));

    // Because the simulator does not support multiple threads,
    // we have to call dispatch_forever from the main thread.
    // Typically you'd run this on a separate thread within Mbed's RTOS.
    queue.dispatch_forever();
}

Event Queue Tasks

Event Queue Task

In this task we start to bring things together. Lets use the Event Queue and to allow our Interrupts to work Correctly.

  • Modify the provided code to use an event queue to meet the following tasks:
    • Normal Operation: Led1 Flashes at a regular interval
    • Button Pressed: Led2 Flashes 10 times
    • Button Released: Led3 Flashes 6 times

What Happens if we press the button Multiple times? Does the Event Queue store the behaviour.

Event Queue Task 2:

In this example, you will need to think about keeping track of the STATE of the button.

  • Lets take a look at the Queuing Behaviour
  • Without Using the Main Loop
    • LED1: Flashes every second
    • LED2: Flashes 5 Times every 10 Seconds
    • On Button Press:
      • Presses 0-4: LED4: Flashes 10 times
      • Press 5: Cancel any remaining Flashes.

What happens if we:

  • Press the button Twice?
  • Press the button 5 Times?

How is the Event Queue helping us manage multiple Interrupts.