The washing machine hardware is built around an STM32 Nucleo, with a choice of an ARM Cortex-M0 or Cortex-M4 core. The mix of peripherals and programming models creates many interesting design and TDD challenges.
This page aims to give you a functional overview of the hardware, and some samples on how to drive the peripherals needed to get your Washing Machine working. We don’t tell you much here about what you’re going to do with these things - that’s all over on the specification page - only what the moving parts are, and how to make them move.
We show some sample fragments of code for driving the peripherals via the vendor’s HAL, mostly in C. Note that these are not suggested design, just minimum self-contained examples of one way of interacting with the hardware - you can choose to do it differently, even develop your own HAL from scratch.
The hardware is designed to give a realistic mix of peripheral interactions, and programming models. Among these are:
The hardware block diagram below shows a high-level view of the system architecture. The diagram can be zoomed with a click, but there’s also a PDF version that you can download and print
There’s a full pinout and configuration model freely available for the kata, including the HAL that’s generated from the model. If you want to modify the model or use a GUI to explore the configuration, you can download the STM32CubeMX tool for free from the ST Microelectronics website (requires account login, download link is at very bottom of the page).
For the very impatient, here’s the key graphic that you need. The
definitions of these peripherals, ports and pins can be found in the
main.h in the generated code.
There are some ICs on the board that do mundane-but-necessary jobs for the design to work, but aren’t a factor in the coding challenge. Briefly, these are a level shifter IC for bridging between 5V TTL and 3.3V CMOS logic levels, and an op-amp IC to support some analog elements of the design. These aren’t covered in the rest of the walkthrough, and they’re omitted in the block diagram above.
Without question the easiest of the hardware interactions you’ll have to work with, the design uses basic GPIO input and output for:
If you’re choosing to use the generated STM32 HAL, all of the pin and
port definitions are in
main.h, and you can use these definitions in
your code (lightly edited for page fit - check actual definitions):
The slide switch that simulates the door being opened and closed can be read with a HAL function.
Again, this is one of the simpler hardware interactions, although a significant event in the specification. There’s no external interrupt configured for this pin, so you will have to poll it: it’s an intentional hardware design choice to have a different type of hardware interaction from the push buttons.
There’s a twist to the push-button GPIO. By default, these are configured to generate an external interrupt on a falling edge. So, although you can read from these GPIO pins to see current state, you also have to implement an ISR handler for the falling-edge event.
You can change this configuration if you’d prefer to use polling instead, but ISRs add some nuance to the problem, in the sense of safely managing system state in the face of polled events, timed events, and interrupts. Your call.
Using the HAL, you need to define functions corresponding to the
interrupt vector table, for each of the three
interrupt lines defined in
You can ignore the others - these are defined as “weak” symbols. The
HAL requires you to call a helper function to clear the pending
interrupt. Here’s an example for
The STM32CubeMX tool will spit out these definitions for you, and it’s up to you to decide (a) what they do and (b) where these should live in your code.
A final word on ISRs - if you’re choosing to use FreeRTOS for this
project, remember to use the
The ADC peripheral on the STM32 is very sophisticated. There are multiple modes of operation, to suit different types of application. Covering the ADC peripheral in-depth is beyond the scope of this material, so we’re going to look at a basic mode of operation only, as configured in the starter HAL project. You’re free to tweak this. ST Micro publish an application note that surveys these different modes, and applications.
The STM32F411 has a single ADC peripheral that’s able to scan 16
different channels. By default, the sample HAL has the ADC peripheral
configured to scan the two channels corresponding to
SENSOR_TEMP (which is
ADC_CHANNEL_7) in the pinout diagram earlier.
Using interrupts at the end of each channel scan requires you to implement an IRQ handler, and also call (yet another) magic HAL function which will invoke a callback, and then clear the pending interrupt:
Also in your code, you need to drive the conversion by telling the peripheral to make a conversion. In this case, we’re starting it in interrupt mode, which result in the ISR above being called when the conversion is complete:
By default in the HAL starter project, the ADC peripheral is configured in what ST Micro call either Scan Single Conversion Mode or Multichannel Single Mode (yes, actually in the same document). The summary is this: you explicitly initiate each conversion in your code rather than the peripheral running in the background, and each conversion you do will scan a successive channel in a rank, then wrap back around to the start.
We only have two channels, so in effect the conversions will alternate
SENSOR_FILL / ADC_CHANNEL_6 and
ADC_CHANNEL_7 for each call to
The four-digit segment displays are driven by a MAX7221 LED driver. You can read the datasheet on the Maxim site . You interact with the LED driver chip via a 3-wire SPI connection. This is a unidirectional connection, with a MOSI line from the MCU to the LED driver, no MISO line back: fire and forget.
It’s important to know that the
NSS line is software-controlled: you
need to drive the NSS line low prior to transferring data to select
the LED driver IC, then back high when completed. If the NSS isn’t
low, the transmission will be ignored, and there’s no way of detecting
this condition in code.
If you’re using the STM32 HAL, the MAX7221 LED driver expects a pair
of bytes making up a single 16-bit “command”. The HAL SPI
configuration is for the SPI default of 8-bit transfer, and the
HAL_SPI_Transmit function is used to transfer two bytes in a single
You can connect a logic analyser to the the 3 SPI lines directly on
the Nucleo headers to verify the output signals:
The “electric motor” that turns the washing machine is powered by a PWM signal. A 100% duty cycle represents the motor running at max power, and 0% is the motor stopped.
The current motor duty cycle is shown by the circular LED display: this is essentially a voltmeter bar graph that reflects the duty cycle. To make this work with the analog LED driver, the PWM signal passes through an op-amp buffer to an RC (resistor-capacitor) low-pass filter, which smooths the PWM signal.
There’s a trim potentiometer
R11 that you might need to adjust
slightly to make sure that all 10 LEDs are lit when the duty cycle is
100%. It’s also worth noting that the RC low-pass filter component
values were chosen to give a perceptible ramp-up-and-down effect as
the duty cycle changes. There’s no code involved in this effect, only
a couple of passive components and a dubious sense of aesthetics.
In the HAL initialisation code for the PWM timer, is this magic value
This sets, in effect, the “dynamic range” of the PWM signal. You can
set values for the comparator using the HAL macro
Beyond the PWM output, we’re entirely into analog-land, so things are
imprecise - the analog level response to the PWM output is not exactly
Period of 1000 with these comparison values works
effectively for increasing the power in 2 X LED increments:
The board needs to be powered by an external USB source via the USB Type B connector. The USB connection to the Nucleo - if you’re even using it - won’t work on its own. The board draws more power than can sensibly be supplied by the ST Nucleo’s onboard power supply, and a surprising amount of the design needs a 5V supply (mainly down to limited choice of LED driver ICs in DIP packages).
JP5 must be set to
E5V for the external power supply to
be used. From the ST Nucleo 64 manual:
Dual-target TDD means you’ll spend less time trying to debug your program logic on the target device. But, mysteries still need to be solved, and running your code on both workstation and target is what makes it dual.
You have three main options here:
You can use the Nucleo’s own built-in ST-Link debugger over a USB connection, and open-source tools like OpenOCD to bridge a GDB debug session to the firmware. One of the reasons the Nucleo was selected as the platform was to make this available to people without professional debug gear.
You can replace the Nucleo’s built-in ST-Link firmware with Segger’s propietary debug firmware ST-LINK On-Board , and then use an evaluation license of Segger tools for programming and debugging the target device. Note that you don’t get the unlimited flash breakpoints feature, this is only possible with some models of J-Link.
If you have professional JTAG/SWD debug hardware (Segger J-Link for example), you can connect this to the standard 20-pin JTAG header. Note that the design requires an SWD interface, so you’ll need to configure the right debug interface in your tools of choice.
Any of these options should work fine for this kata, but if you have a professional debug probe, it would be obvious and sensible to make this your first choice.
The magic string for connection from the J-Link commander tool is:
Note that only the 100-pin variants of the STM32F411 MCU support
dedicated streaming trace pins (
PE2-PE6), but not on this 64-pin
variant. We’re planning trace support in the next generation of the
hardware design, though.
You have the choice of using Segger’s GDB server and either
gdbtui, or configure your IDE’s integrated GDB
debug to connect to the Segger GDB server (tip: this second option
works very well indeed for JetBrains CLion .
If you’re using OpenOCD , you’ll need to refer to the OpenOCD documentation for building and installing the tool, but the good news is that the Nucleo F411 is well supported. A possible configuration is as simple as this:
If you want a convenience script that does no more than flash the board, this is a reasonable starting point:
Be aware that you’ll be limited to 6 hardware breakpoints only, and that the debugging experience is considerably slower than using a more powerful debug probe.