Waiting for something to happen in an embedded system – a while loop

An embedded system is normally a CPU [or some CPUs] interfaced to a number of peripheral devices. The software processes data and controls the peripherals. In summary, that is what embedded systems are all about. But, of course, there are many other details and that is what occupies embedded software developers most of the time. A significant issue is around peripherals and timing …

Timing issues in embedded systems can be challenging. Time is relative. In simplistic terms, the processing in the CPU is normally very fast, whereas peripherals, particularly mechanical devices and human interfaces, may be orders of magnitude slower. This can easily be managed and, most of the time, is manifest in the software waiting for something to happen.

The term “waiting” is vague. Sometimes, in simpler systems, it literally means that the software is idling, waiting for a response; in other contexts, the software continues to do other work, pending an interrupt. It is the former situation that interests me today and begs the question: how can a CPU do nothing?

We will assume that there is a peripheral device, which has a control register at address 0x8000000 and this register will change from 0 to 1 when an operation is complete. The first thing we need to do perhaps is give the device a name to make our code readable and that could be done like this:

#define DEVICE_CONTROL (*(unsigned *)0x8000000)

Here the number is cast to be a pointer to unsigned and then the pointer is dereferenced, so the symbol DEVICE_CONTROL can be used anywhere that an unsigned variable might be valid. However, this is not quite good enough, as a smart compiler might optimize access to the [what it thinks is a] memory location by reading it once into a register and using that copy thereafter. We need to make a small change to the definition:

#define DEVICE_CONTROL (*(volatile unsigned *)0x8000000)

The addition of volatile tells the compiler to actually read the memory location every time it is referenced and desist from optimizing.

We can now write some device driver code like this:

<set the device doing something>

while (!DEVICE_CONTROL) ;   // wait for it to finish

<do the next thing>

This code is valid enough, but the style is not good in [at least] two respects: the code exploits the fact that a non-zero value in C means “true”, which is unclear; the while loop is empty, which is OK, but the semicolon could easily be missed by someone quickly reading the code. It can easily be made a little clearer:

<set the device doing something>

while (DEVICE_CONTROL == 0)   // wait for it to finish
   ;

<do the next thing>

The complier will generate the same code either way, but it is always important to consider the readability of your code.

The code, as it stands, will work, but has two shortcomings:

  • It is checking the device very frequently – every few microseconds – but it is likely that the device will operate on a much slower timescale. Checking less frequently would deliver the same functionality and responsiveness and give the opportunity to save power.
  • There is no provision for failure of the device. If no response is received, the code will loop for ever.

We can address each of these in turn. First the sampling frequency and power saving. All that is necessary is to put the CPU to sleep [i.e. into a low power mode] for short periods [say, 250 milliseconds] between each test:

<set the device doing something>

while (DEVICE_CONTROL == 0)   // wait for it to finish
{
   sleep(250);
}

<do the next thing>

The best way to deal with possible device failure is to implement a timeout. Perhaps 10 seconds [40 x 250mS] is appropriate:

#define TIMEOUT 40

<set the device doing something>

for (int i=TIMEOUT; (DEVICE_CONTROL == 0) && (i > 0); i--)   // wait for it to finish
{
   sleep(250);
}

if (DEVICE_CONTROL == 0)
{
   <process error condition>
}

<do the next thing>

Checking the value of i on exit from the for loop would be another way ascertain why the loop terminated.

In a more complex system, a real time operating system is likely to be used, which provides facilities for dealing with such timing issues. It would then be easy for a task to loop and relinquish control for a period each time around, thus allowing other tasks to do their jobs. It would probably be better for a task to await a flag from an interrupt service routine, indicating that the device was finished. Typically, waiting for a flag can have a timeout associated with the API call.

It is, of course, essential to carefully debug all your embedded software, but sometimes, and, in particular with time-sensitive code like this, there is the opportunity for last minute optimization. For example, how long do you need to wait between checks of a device’s status? Tools such as Sourcery Analyzer are ideal for this job.

Want to stay up to date on news from Siemens Digital Industries Software? Click here to choose content that's right for you

Comments

0 thoughts about “Waiting for something to happen in an embedded system – a while loop
  • Hey Colin —

    I think maybe the code right under “We can now write some device driver code like this:” (notice the exclamation mark)

    while ( ! DEVICE_CONTROL) ;

  • Hi Colin, My company makes debouncer chips that solve the very problem you wrote about in this blog. I would like to link to this article from my site, http://www.logiswitch.net with your permission.
    We also manufacture bounce-free switches, and are planning to introduce the addition of switches with the LogiSwitch handshake protocol to the line coming soon. Please let me have your mailing address and I will send you some samples of the LogiSwitch products if you have an interest.

  • Hi, LogiSwitch products may be seen at http://www.logiswitch.net. Some rather significant changes are in the works with new upcoming products, but all the current line of bounce-free switches and Debouncer ICs can be seen there. I find the just typing logiswitch on the address line takes you to the site.

  • Hi Colin,
    Since designing the debouncer chips incorporating the handshake protocol, we have decided to add the handshake capability to the bounce-free switches. In either polled or interrupt mode the handshake brings a lot to the party, completely eliminating polling for release and eliminating multiple repeats of single switch cycles in interrupt routines. Please contact me at mike@logiswitch.net for samples if you have an interest.

Leave a Reply

This article first appeared on the Siemens Digital Industries Software blog at https://blogs.sw.siemens.com/embedded-software/2016/07/18/waiting-for-something-to-happen-in-an-embedded-system-a-while-loop/