This article introduces the idea of concurrency and a mechanism that many computers provide for dealing with concurrency called interrupts.

This article is the first in a series that will explore the concepts of concurrency and interrupts. The bulk of the series will deal directly with microcontrollers and GPIO interrupts.

In this initial piece, we'll address the basic definitions of these concepts and consider how they're relevant to writing code for a microcontroller.

 

What Is Concurrency?

Let's talk about concurrency using an example we are all familiar with. Usually, when you go to bed, you want to wake up around a particular time. You have two goals here: going to bed so you can rest but also making sure you don’t sleep past your desired wake up time. 

One way to make sure you don’t miss your wake up time is to stay awake and watch the clock, but that would defeat your purpose of wanting to go to bed. To help remedy this, we usually use an alarm clock (either a device or trusted person who would already be awake by that time) to wake us up at the particular time so we can focus on sleeping and still meet our goal of waking up on time.

Concurrency is the term we use to describe the scenario when you have two or more processes to attend to at the same time but only have the capacity to physically deal with only one of the processes at any point in time

In the above example, the two processes to attend to are sleep and keeping track of time (so we wake up at the desired time). It is physically impossible for the same person to do both at the same time. 

Usually, we are only interested in particular events associated with the processes we are attending to. As you notice above, the only event we care about from a time perspective is whether it is time to wake up. The typical way to address concurrency then is to have another physical resource (the alarm clock or person) monitor for the event we are interested in and then alert (or interrupt) us to the fact that the event has occurred so we can attend to it. 

Figure 1 shows a timeline illustration of this.

 

Figure 1. Timeline illustration of going to bed and letting alarm wake you up at a specific time.

 

Concurrency in Embedded and Cyber-Physical Systems 

Embedded and cyber-physical systems typically have to deal with concurrency. 

An example of this concept in action is a microwave. The device needs to be able to handle button presses or detect when you open the door while the food is warming. Pressing the pause or cancel button or opening the door will cause the food to stop warming. 

Microcontrollers—which are the computers you typically program to be the “brain” of the system—are designed with concurrency in mind.

In addition to the general-purpose CPU, they contain a number of specialized hardware devices (called peripherals) for interacting with the outside world so you can connect things like buttons and displays and other external hardware you need to implement your system. Usually, these peripherals have an interrupt mechanism so the CPU can ask them to watch for a particular event and then interrupt the CPU when that event occurs.

 

Higher-Level Overview of Interrupts and Concurrent Programs

Interrupts on a microcontroller work just like the example with sleeping and the alarm clock I discussed earlier. 

You can write a program that configures a peripheral to watch for a certain event and interrupt the CPU when that event occurs—for example, when the GPIO input value goes from 0 to 1. When your program runs, the CPU executes the pieces of your main program. When the event you are waiting for occurs, the peripheral signals to the CPU that the event has occurred. If everything is configured properly, the CPU will stop whatever it was doing in the main program, execute a special function called the interrupt service routine (ISR) or interrupt handler, and then go back to the main program to continue from where it left off when it was done. 

You have to write the ISR as part of your code. Assuming you are using C to write your program, that means you have to declare and define it somewhere in the source code that will be built as part of the executable that the microcontroller will run.

However, you never call the ISR, yourself. All microcontroller development tools have a specific way for you to tell the C compiler that a certain function is an ISR and which specific interrupt it is associated with. Once you have done this properly, when the specific interrupt occurs, the CPU will automatically jump to that function and execute it. 

 

An Example Interrupt in C Code

Because of the way interrupts work, handling concurrency in programming may seem a bit non-intuitive at first.

The following figures walk through how a code written in C that has an ISR would operate to help provide a good way to think of concurrent programs. It assumes that all the interrupt configuration is done properly.

 

Figure 2. Code in C with interrupt

 

  1. The CPU is executing code in the while loop in the main function.
  2. The event of interest (change from 1 to 0) occurs on the pin that is configured for the interrupt.
  3. The CPU stops executing the code in the while loop and executes the interrupt service routine.
  4. The CPU resumes execution of the main loop from where it left off after finishing executing the interrupt service routine.
  5. The CPU is executing code in the while loop.
  6. The event of interest occurs again.
  7. The CPU stops executing the code in the while loop and executes the interrupt service routine.
  8. The CPU resumes execution of the main loop from where it left off after finishing executing the interrupt service routine.

 

Interrupts for Peripherals and GPIO

The majority of this series is focused on the concepts of concurrency and interrupts in reference to microcontrollers. Next, we'll talk about GPIO interrupts and how they work. 

 

Comments

1 Comment


  • Ajay Kumar 18 2019-10-04

    Thanks for sharing this article Philip, it’s really interesting and easy to understand for a newbie like me.