This chapter may seem trivial at first glance, but it is surprisingly difficult to create reliable dimming, fading and animation effects on an LED or a set of LEDs. I’ll describe the layers of timing that is necessary, and you will build a sweeping animation similar to the red scanner bar that we saw on the hood of the Michael Knight’s Firebird on the 80’s TV show, Knight Rider. In the process of completing the task, you will learn all about MCU clock sources and scaling, interrupts and their handlers, and how to manage all of that from a system level. You need to get much of this under your belt as the foundation for more complex lessons that are coming.
Materials Needed for This Lesson
Most clocks used in an MCU in the core and by peripherals are not the kind that are used for keeping time. A clock in computer hardware is used to give the electronic circuits a heartbeat so that everything works in lock step. Clocks are used to adjust the operating speed of the part. A peripheral with a slow clock uses the least amount of power. Higher frequencies result in more power consumption.
When you are trying to get your gadget working, the last thing you want to think about is how to configure the clocks. But if you get the configuration wrong, you will pay for it with a system that stubbornly refuse to function. So do yourself a favor and read through this short overview. I’m trying to pull out the most important things that you should know for now. The following figures may give you another perspective on how some of the components of a clocking architecture relate.
General Topology of the Clock Architecture
All clocks in the EFM32 MCU are digital waveforms that are controlled by the Clock Management Unit (CMU) and are usually sourced from analog oscillators. The oscillators in the EFM32 family are either a quartz-based crystal or a Resistor-Capacitor (RC) network. The quartz-based crystal oscillator must be installed in the printed circuit board along with two capacitors and enter the MCU on a pair of pins. The resonant frequency of the crystal oscillator is fixed and must be chosen when the board is assembled. The RC clock sources exist inside the MCU and the resonant frequency can be controlled through configuration registers. Crystal oscillators have higher precision than the RC oscillators, the latter of which have tuning mechanisms driven by the MCU that are used to continually compensate for their inaccuracies.
There are two available frequency bands for clock sources. The low-frequency band is sourced at 32.768 kHz and the high frequency band is sourced in an adjustable range of 1 to 32 MHz (except on Zero Gecko which is limited to 24MHz.) There is a Low Frequency Crystal Oscillator (LFXO) source and a High-Frequency Crystal Oscillator (HFXO) source as well as a Low-Frequency RC Oscillator (LFRCO) source and a High-Frequency RC Oscillator (HFRCO) source. The clock sources are chosen based on the needs of the application. Tradeoffs can be made for accuracy versus extra board components, fast execution time versus low power. The clock source can be changed while the MCU is operational in order to adjust the power consumption to the task at hand. The HFRCO has a very fast wake up time and is therefore the default source to start up the MCU from reset.
There are also two special clock sources, the first of which is called AUXCLK that runs at 14 MHz for flash programming and SWO output, and an Ultra-Low Frequency RC Oscillator (ULFRCO) that runs at 1 kHz and used for deep sleep states to run the Watchdog Timer peripheral.
Once you have chosen a clock source, the digital clocks that are generated from those sources have adjustable operating frequency and are routed through a clock multiplexer. They are controlled via configuration registers within the MCU. Once a clock is routed to a peripheral, it is only possible to scale the clock speed downward (i.e. it cannot multiply the clock source to create a faster clock than its source) and is accomplished with something called a prescaler or clock divider. A prescaler setting of one is the highest possible frequency. The upper limits of clock division (producing the slowest possible operating clocks) are described in the Reference Manual for your MCU. The digital clocks can themselves become clock input sources to other peripherals on the design.
Configurable Clock Path Through the EFM32 MCU
There is a lot more to know about clocks, and I will be covering that as we move into each peripheral. If you want to know more, you can read more about it in the Reference Manual for your MCU as well in the Application Notes found using the tile in Simplicity Studio. Use the Clock Management Unit (CMU) software tools available in em_cmu.h to control your clock sources.
Why is all of this stuff important? Because in order to keep track of time for our programming, it is necessary to understand how many operations can be done per second, and we also need to make sure that the number of operations is consistent in each and every second. Otherwise, you will end up with a solution that drifts, creating a poor user experience.
Sequential versus Interrupt-based Programming
So far, we have written code that is strictly sequential in nature. Execution starts at the top of a program, steps through the code and the logic of each line directly controls the inputs and outputs of the device and determines which lines of code will execute next. Sequential code is easy to understand because you can kick back and trace through the program line by line from the comfort of your armchair with a printout and a pipe in your mouth (or not) and figure out what is going to happen next. You don’t need to worry about anything happening outside of your little program. The drawback to this is that you can only do one thing at a time. If you want make it appear that you are doing more than one thing at a time, you must create a fast-running while loop and kick off the different events one after another. We did that in the last lesson when we would look for a button press, and then blink an LED one time on each pass through the while loop.
When programming a system that is running an Operating System (OS), our programs run as threads or as processes. Each thread or process can be written in a perfectly sequential manner and then OS will take care of giving each thread or process time on the CPU to do its work, creating the illusion that multiple things are happening at once, even on a single CPU core. It is possible to run an OS in an embedded system on the EFM32 family, but it requires a lot of resources just to run the OS itself and it is not always the most cost-effective way to do it. And because resources are limited and non-standard, an embedded OS brings its own share of headaches.
In order to handle multiple tasks at once without an OS, firmware designers turn to interrupt sources (also known as Interrupt Requests or IRQ’s) that trigger interrupt handlers (also known as Interrupt Service Routines or ISR’s) to leap into action only when a specific event happens. In fact, OSes are built on top of interrupts and abstract them from the programmer. The EFM32 family has a great many available interrupt sources that you can configure and then write an interrupt handler to do something at the exact moment when the interrupt event happens. We could rewrite our example from the last lesson with timer-based interrupts, and it would make the logic simpler in this case. If you recall, we used an input button to select whether or not an LED was blinking. One press would start it blinking and another press would turn it off. We also had added the ability to put it into a deep sleep EM4 mode when the LED was off. The procedure to do the same thing using interrupts follows.
configure GPIOs wait for input button to be unpressed configure EM4 wake up mechanism configure button press interrupt configure LED timer interrupt LED_state = false define button pressed interrupt handler function: turn off LED wait for button to be unpressed enter EM4 sleep state define LED blink timer interrupt handler function: if LED_state == false turn on the LED LED_state = true else turn off the LED LED_state = false while (true) enter EM3 sleep state
By the way, I will use this pseudocode occasionally to communicate the intention of code without getting mired in the syntax of a C program.
The pseudocode assumes that we configure the timer for an appropriate interrupt period to be used to toggle the LED on and off, and configure the button interrupts for the correct pin and polarity. The procedure is easy to understand and has the enormous benefit of being more power efficient because there is no while loop running all of the time. The MCU will stay in a low power state nearly all of the time, only waking up when someone presses a button, or when a timer expires and it has to flip a single GPIO to a different state, and then it goes right back to sleep.
The use of interrupts for our simple blinking LED is optional. We have easily handled it with sequential programming. But in order to create dimmers, faders, and “chasers” on multiple LEDs, while also handling the logic of whatever problem you are trying to solve, it would get pretty complicated to do all of that in a single while loop. Interrupts and hardware timers are usually the right choice and will let our solution run on batteries for a longer period of time.
We will pick up this conversation with the SysTick Interrupt in the next lesson.