Blog for my various projects, experiments, and learnings

Drawing to a Small TFT Display: the ILI9341 and STM32

As you learn about more of your microcontroller’s peripherals and start to work with more types of sensors and actuators, you will probably want to add small displays to your projects. Previously, I wrote about creating a simple program to draw data to an SSD1331 OLED display, but while they look great, the small size and low resolution can be limiting. Fortunately, the larger (and slightly cheaper) ILI9341 TFT display module uses a nearly-identical SPI communication protocol, so this tutorial will build on that previous post by going over how to draw to a 2.2″ ILI9341 module using the STM32’s hardware SPI peripheral.

An ILI9341 display being driven by an STM32F0 chip.

An ILI9341 display being driven by an STM32F0 chip. Technically this isn’t a ‘Nucleo’ board, but the code is the same.

We’ll cover the basic steps of setting up the required GPIO pins, initializing the SPI peripheral, starting the display, and then finally drawing pixel colors to it. This tutorial won’t read any data from the display, so we can use the hardware peripheral’s MISO pin for other purposes and leave the TFT’s MISO pin disconnected. And as with my previous STM32 posts, example code will be provided for both the STM32F031K6 and STM32L031K6 ‘Nucleo’ boards.

“Bare Metal” STM32 Programming (Part 5): Timer Peripherals and the System Clock

As you start to create microcontroller projects with more complicated logic, it probably won’t take long before you to want to add some sort of time-based functionality. How should you ask your chip to do something on a schedule?

These days, we don’t have to count clock cycles in ‘delay’ methods. The STM32 line of chips have a variety of “timer” peripherals available, and they are flexible enough to use for all kinds of different things. The “advanced control” timer peripheral is particularly complicated and I won’t try to cover it in this quick overview, but the basic and general-purpose timers are easy to get started with for simple counters and interrupts.

In this tutorial, we will write code to enable a hardware interrupt triggered by a timer peripheral, and use that to blink an LED. Just like the last couple of posts in this series, the STM32F031K6 and STM32L031K6 chips will be used to demonstrate these concepts. Since this tutorial will only use a single LED, you won’t need anything other than an affordable “Nucleo” board with one of those chips and a micro-USB cable:

"Nucleo-32" Board with Blinking LED

You don’t even need a breadboard!

You can double-check that the clock speeds are what we expect by counting the LED’s on/off timings against a clock. It should change about 60 times every minute with the timer set to 1 second, although the internal oscillator is not quite as precise as an external “HSE” crystal oscillator would be. As usual, an example project demonstrating this code is available in a Github repository.

Scripting a Box: Making Laser-Cut Battery Holders

Previously, I posted a tutorial about writing SVG files by hand to create simple patterns for a laser cutter to produce. But as I noted at the end of that post, writing that sort of file by hand is not a great solution. It is difficult to make a design that can be modified later, since SVG files don’t appear to have a good way to store variables yet.

So in this tutorial, I’ll go over the process of writing a simple Python script to create the same sort of “divided grid box” as in the previous SVG-writing tutorial across a wide range of dimensions. Then I’ll demonstrate how to use it to create a small 4-cell AAA battery holder out of something that isn’t plastic:

AAA battery case made of laser-cut wood

AAA battery case made of laser-cut wood

While “3D printing” a case allows you to add curves and small overhangs to hold the batteries in place, these simple laser-cut boxes can only have perpendicular edges. But I’ve found that using the sorts of spring contacts that you find in most commercial battery cases provides enough pressure to hold the batteries in place, at least until you hold the case upside down and knock it against something to pop them out.

Plus, 3D printing is comparatively slow; a battery case of this size would take between 30-60 minutes to print out on a Prusa i3 running at high speed, but a CO2 laser can cut out these parts in about 60 seconds. You do need to glue the pieces together, but if you adjusted the scripts to account for the “kerf” of your particular laser/material, you might be able to make them press-fit. Anyways, let’s get started!

Your Own Hardware: Using KiCAD to Design a Minimal STM32 Development Board

It’s great to be able to write programs for a chip’s evaluation boards, but the real strength of microcontrollers is their ability to act as a low-cost, low-power “brain” for larger designs or products. And along those lines, I’ve been writing a few tutorials about bootstrapping some basic ‘bare metal’ STM32 projects using an STM32F031K6 “Nucleo” board sold by ST.

That’s a great way to get started and test ideas out, but what if you want to try your hand at building a robot, or a home automation widget, or some other sort of complex machine? It’s nice to avoid huge messes of breadboards and wires once you have a basic prototype working, and these days it only costs a few dollars to get a small custom circuit board manufactured. The catch is, they usually take a few weeks to arrive and you need to provide the design. Still, the boards that we design in this tutorial will cost less than $2 each.

In this tutorial, we will use a suite of free software called KiCAD to produce a small example board using the same basic STM32F031K6 chip that I’ve been writing programming examples for. Our board won’t be quite as nice as ST’s, and it will require an external USB device for programming and debugging. But on the other hand, our board will be smaller and cheaper, and you will be able to put the same design onto more holistic boards with other parts for your kickass robot or electronic vehicle or <insert dream here>:

multiple board renders

Left: A board like the one you’ll design in KiCAD. Right: OSHPark’s renders of the front and rear of the board.

The design files described in this post are all available in a Github repository, if you want a reference to follow along with.

When is Now? The DS3231 Real-Time Clock

Time may be an artificial construct, but try telling your boss that ‘Monday’ has no meaning. It is useful for a program to be able to schedule actions for a certain date, display the current time on a clock or calendar, or perform other tasks which use weird units of time like ‘seconds’ or ‘days’. For those types of tasks, an embedded developer might reach for an ‘RTC’ device, which stands for ‘Real-Time Clock’. They provide a way to keep accurate time, often with features like backup power supplies. Many RTCs also offer ‘wakeup’ alarms for other devices, so they are especially useful in energy-efficient designs.

The STM32 line of chips which I’ll continue to use in this tutorial have a built-in RTC peripheral, but they require an external 32.768KHz ‘LSE’ (Low-Speed External) crystal oscillator to keep accurate time. Also, managing the STM32’s backup power supply is sort of complicated.

Instead, this tutorial will walk through using the ‘I2C’ peripheral on an STM32 chip to communicate with a cheap DS3231 RTC module. Specifically, I will talk about a widely-available board labeled ZS-042, which includes 4KB of EEPROM on its I2C bus and space for a “coin cell” battery to provide several years of backup power. But the same commands should work with other DS3231 boards, such as the smaller ones in the upper-left here:

DS3231 Modules

A handful of DS3231 modules and their backup batteries.

An example project demonstrating the concepts outlined in this post using either an STM32F031K6 or STM32L031K6 “Nucleo” board is available on Github.

“Bare Metal” STM32 Programming (Part 4): Intro to Hardware Interrupts

In a previous entry in this tutorial series, I tried to walk through some basic steps for writing an example STM32 program which toggled an LED on and off when a button is pressed. But that program only checked the button’s status once every cycle of the ‘main’ loop, and in a complex application each loop iteration could take a fairly long time. If a button press were very short and our application was busy for a long time, the program could miss the input.

When you want to respond to input very quickly and consistently on a microcontroller, it is usually a good idea to use a ‘hardware interrupt’ to run some code when certain hardware events are detected. In this tutorial, we will look at the STM32’s ‘EXTI’ interrupt lines, which can be set to trigger when the state of a GPIO pin changes.

And once we have a simple ‘button press’ interrupt triggering, we can easily demonstrate a real-world use by extending it to listen for faster inputs such as “rotary encoder” dials:

Rotary Encoders

A couple of “rotary encoder” dials – the small resistors and capacitors on the back are for debouncing.

This type of dial ‘clicks’ in small steps when turned in either direction; they are nice tactile inputs, but it can be difficult to read them without hardware interrupts because of the large number of rapid pulses that they can generate when you twist them. So let’s get started!

STM32 Software SPI SSD1331 Sketch

In a previous post, I wrote about designing a ‘breakout board’ for an SSD1331 OLED display with 96×64 pixels and 16 bits of color per pixel. With the hardware already put together, this post will cover writing a basic software driver for the displays. To keep things simple, we will talk to the display using software SPI functions instead of the STM32’s SPI hardware peripheral.

If you want to skip assembling your own boards, you can also buy a pre-made display such as this one sold by Adafruit. They have also written a library for these displays which works with several common types of microcontrollers, if you just want to use them without worrying about the display settings. But if you want to try understanding this sort of communication at a lower level, read on!

Working SSD1331/STM32

The finished program will display a predefined framebuffer, like this little logo!

Since many small microcontrollers – including the STM32F031K6 discussed in this example – don’t have 12KB of RAM available to store a 96×64 display at 16 bits per pixel, I’ll use a framebuffer with just 4 bits per pixel in this example (3KB), and map those 16 values to a palette. This example builds on the first few “Bare Metal STM32 Programming” tutorials that I’ve been writing, so here is a Github repository with the entire example project (including supporting files) if you don’t want to read those.

“Bare Metal” STM32 Programming (Part 3.5): Supporting Multiple Chips

The first few of these ‘STM32 programming’ tutorials have only supported a single microcontroller – the STM32F031K6. But in reality, you will probably want to select a chip based on speed, price, power efficiency, which peripherals your application needs, etc. It’s nice to be able to compile your projects for different chips without needing to make changes to the code, and it’s also useful to be able to copy/paste code between projects which target different chips.

So in this tutorial, we will take the simple ‘button-controlled LED’ example project from last time, and extend it to work with an STM32L031K6 core. Again, you can buy a ‘Nucleo’ board with this chip for about $11. It is fairly similar to the STM32F031K6 that we used last time, but there are plenty of differences between ST’s F0 and L0 lines. Here are some key examples for these chips in particular:

STM32F031K6 STM32L031K6
MCU Core Cortex-M0 Cortex-M0+
SRAM 4KB 8KB
Max. Speed 48MHz 32MHz

 

These differences are fairly minor, but we will need to provide a slightly different linker script and startup logic. The vector table will also be different, and we will need to add a few more of ST’s “device header” files for the new chip. Also, since the ‘L’ series uses a newer core architecture, we will need to update the places where we specify cortex-m0 in our assembly files and compiler options to optionally use cortex-m0plus instead.

This process is actually fairly painless, and all of the files discussed after the break are available in a Github repository if you haven’t read the previous tutorials which this example builds on. So, let’s get started!

“Bare Metal” STM32 Programming (Part 3): LEDs and Buttons!

In a previous post, I walked through creating a basic project for an ‘STM32F031K6’ microcontroller to boot the chip into a ‘main’ C method. But the actual program didn’t do much, so in this post we will learn how to use the STM32’s ‘GPIO’ peripheral to listen for a button press and blink an LED.

The ‘Nucleo’ boards provided by ST have an LED already built into the board, but they don’t have a button (besides the reset one,) so we’ll need to connect one externally:

Nucleo board on a breadboard with a button.

‘Nucleo’ STM32F031K6 board with a button.

The green ‘LD3’ LED is attached to pin B3 on the board. The 100nF capacitor across the button should help reduce noise, one side of the button connects to ground through a jumper wire, and I put a 470Ω resistor between the other side of the button and pin B1.

Strangely, the B1 pin is labeled ‘D6’ on the Nucleo boards; I think that ST wanted to use the same footprint and labeling as the popular Arduino Nano. You can find the actual pin mappings in section 6.11 of this reference document, or they’re also printed on the informational card that comes with the board. The resistor and capacitor are both optional – they’re just a very simple form of debouncing. Next up, the code!

“Bare Metal” STM32 Programming (Part 2): Making it to ‘Main’

In a previous post, I tried to walk through some very minimal code to get an STM32 chip to boot into a simple “Hello, World” assembly program. But that quick introduction left out some important concepts, and usually people don’t want to write an entire program in an assembly language.

So in this tutorial, we’re going to build on that ‘absolute minimum’ example, and write some more complete ‘startup’ code which will run a familiar C program’s “main” method when it finishes.

We’ll use the STM32F031K6 chip as an example again; it is one of ST’s simpler ARM chips, and you can buy a pre-made ‘Nucleo’ board for just a little over $10.

What Will We Write?

This example project will consist of a few different files, but there’s still a good chance you can count them on one hand:

  • A more complete ‘Linker Script’ to map our C program’s individual sections of memory onto the chip.
  • A ‘Vector Table’ file which will point every possible hardware interrupt to a default ‘interrupt handler’ – we’ll go over how to actually use these later.
  • A ‘boot code’ file which will contain a reset handler to copy information to RAM and then jump into the ‘main’ method.
  • A ‘main.c’ file which will contain our actual program logic.
  • A ‘Makefile’ which will let GNU Make build the project for us, so we don’t have to copy/paste GCC commands into a console like last time.

Hopefully you’ll come out of this post with a decent starting point for STM32F0 projects, and a general understanding of what is required to create your own projects for other chips.

DIY OLED Display Boards: SSD1306 and SSD1331

OLED displays are excellent solutions for low-power, high-visibility UIs that don’t need to depict much detail and can be smaller than a square-inch or two. These days, they are cheap and available enough to be viable options for the hobbyist:

display modules

Top: The display panels as they arrive in the mail. Bottom: Boards with the circuits described in this post – the panels are glued to the other side.

These are two small display panels which you can find on Taobao, Alibaba, or eBay in small quantities for roughly $2-4 each. The one on the left is a 96×64-pixel SSD1331 16-bit color display. The one on the right is a 128×64-pixel SSD1306 monochrome display where each pixel is either ‘off’ or ‘on’ – typically ‘on’ is a white or blue color. Some of them have a row of 16px along the top set to yellow, but each pixel is still only one color.

In this post, we will walk through the circuitry (although not the code) required to control these displays using a microcontroller, including circuit schematics for laying out a ‘breakout board’ in your preferred EDA program – I used KiCAD, and I’ll also provide a link to those projects if you don’t want to design a new board.

Writing a Box: SVG Basics

Lately, I’ve been looking for ways to get people interested in basic electronics, and things like kits and/or lessons seem like a great way to do that. I’ve also been looking for ways to learn about making things with a laser cutter, so I decided to put together a stack-able box that could double as storage and a display for available electronics parts at a local makerspace.

The basic idea was simple; start with a box ‘outline’ similar to those generated by Makercase, and then lay out a grid pattern of ‘dotted lines’ to slot crenellated dividers into. I wanted something like a shallow tray, with several horizontal and vertical dividers to hold different types of parts. What could possibly go wrong?

At this point, with a basic idea and maybe a quick sketch, most sane people would open up a program like Autodesk Inventor, Solidworks, or Adobe Illustrator. Options like Inkscape, OpenSCAD, or SolveSpace would also work well if you like free software. But after having some trouble with SVG exports, I wondered if it might make sense to just write the files by hand, for a simple grid pattern.

I guess it depends on your definition of ‘sense’! In this post, we’ll learn how to write SVG files for a small laser-cut ‘test’ box:

test box sketch and assembly

Quick sketch of a test box, and what it’ll look like.