Designing Custom Hardware with Microcontrollers Part 1 of 2 – Introduction and Circuitry
Making the jump from Arduino-based hardware to creating your own PCBs with microcontrollers on them initially seems like an incredibly daunting process. However, learning those skills unlocks a vast array of project opportunities, as well as teaching you hands-on, practical electronics skills.
In this article, we’ll look at all that is required to start creating your own custom microcontroller-based hardware designs. You’ll see that there actually isn’t too much to this, as microcontroller manufacturers over the years have tried to make the learning curve less steep and their devices more, and more accessible. This is both from an electrical point of view but also – equally importantly – from a programming point of view.
Next time, we’ll then examine how to lay out the microcontroller and supporting circuitry on a PCB using Altium Designer.
Choice of Microcontroller
As is the case with pretty much any type of integrated circuit, there are an incredibly large number of microcontrollers to choose from.
There are many different manufactures (for example, Texas Instruments, STMicroelectronics, Espressif – to name but a few), packages (for example, BGA, QFN, LQFP), flash and RAM sizes, core speeds, and many more parameters to decide on.
To be quite honest, unless your needs are terribly specific, the available options between manufacturers do not tend to vary too much in terms of interface capabilities and performance. It’s a fairly safe bet to go with any of the major brands.
For the majority of my projects, I end up going with STMicrolectronics’ STM32 line of microcontrollers (disclaimer: I have no connection or agreement with STMicroelectronics – I simply like their products).
The reasons for this choice are their very user-friendly toolchain (STM32CubeIDE), which lets you do pin-out planning, is shipped with a hardware abstraction layer (HAL) for straightforward firmware development, and their large portfolio of microcontrollers of varying capabilities. In addition, their debugger (STLink) is inexpensive and easy-to-use.
I’d strongly suggest browsing through distributor and manufacturer websites, identifying a – for you – suitable microcontroller family, and sticking with those parts. This way, you’ll only need one type of debugger, the hardware designs will be very similar from project-to-project, and your firmware development process will become more and more familiar – rather than having to jump between different toolchains.
Debugging
An additional perk of moving on from Arduino-based designs is being able to use debuggers. Debuggers are specialised programming interface, that allow you to upload code, as well as do real-time, programme debugging. For example, setting breakpoints, monitoring variables, and so forth – all without having to print to a serial console.
There are general purpose debuggers available that are compatible with a variety of different manufacturer’s products. However, each manufacturer will typically offer their microcontrollers’ specific debugger. For STM32 devices, for example, this is the ST-Link debugger.
The debugger is connected to the microcontroller typically via PCB header that exposes dedicated debugging and programming pins. This interface is either serial wire debug (SWD) or JTAG – either of which are must-haves for serious firmware development.
Minimum Required Circuitry
Once you have decided on a suitable microcontroller family with a compatible debugger, I’m sure you’re eager to move on to creating actual hardware for a custom project.
Creating the schematic and typical required connections for a microcontroller is usually a straightforward, standardised process.
Power
Firstly, we need to power the microcontroller. Typically, a microcontroller will only require a single supply rail – and in most cases this will be at +3.3V.
Have a look at the image above, which shows the power supply section of a powerful STM32H7 microcontroller. Power supply input pins to the microcontroller are termed VDD (as well as VBAT), which is the digital voltage rail. Each VDD pin requires a 100nF decoupling capacitor placed close to the relevant VDD and VSS (or GND) pins.
In addition, your microcontroller of choice may have analogue peripherals, such as an analogue-to-digital converter, which also requires power. For this supply input (VDDA and VREF+), we need to perform some filtering in the form of a PI filter, as the analogue section will be more sensitive to noise than the digital part of the microcontroller. This is shown in above picture with C216, FB200 (a ferrite bead), and C216.
Your microcontroller may have internal regulators that need to be externally bypassed with further decoupling capacitors. For this particular STM32H7 IC, these pins happen to be named VCAP. The value of 2.2uF per pin was extracted from the relevant datasheet.
Configuration and Debug
There are two main methods of uploading code to the microcontroller. The first is via a debug probe and SWD or JTAG, and the second is via a bootloader. In the case of STM32 microcontrollers, the bootloader allows the user to upload code via UART, I2C, USB, and similar interfaces (device dependent!) without needing a debugger.
To enable the bootloader, we need to pull the BOOT0 pin high before powering up the microcontroller.
To then run the program, we need to pull BOOT0 low before re-powering the device. It’s therefore a good idea to make the state of BOOT0 togglable via a switch, for example.
In the microcontroller will solely be programmed via a debugger, then you can tie BOOT0 low (to ground) permanently.
As a debug interface, I’ve opted to use SWD for this design, which is a two-wire interface (data: SWDIO, and clock: SWCLK). In addition, I’m exposing the SWO (trace) signal, which allows me to plot variables in real-time, and the NRST (reset, inverted logic) line which, when pulled low, performs a hardware reset of the microcontroller.
The signals are connected to a debug header with optional, but strongly recommended, ESD protection in the form of TVS diodes.
Crystal
While most microcontrollers will contain an internal oscillator, which is sufficient for a large number of designs, it usually pays off to attach an external crystal (or oscillator) to the microcontroller.
For STM32 devices in particular, there are two types of oscillator. High-speed external (HSE, for general timing purposes) and low-speed external (LSE, for the real-time clock). Most of my projects simply require the HSE oscillator.
The image above shows the tried-and-true method of connecting an external crystal to the microcontroller. The crystal’s maximum and minimum allowed frequency is given in the microcontroller’s datasheet.
You’ll have to add load capacitors (C200 and C201, in this example) which are dependent on the load capacitance given in the crystal’s datasheet. Once you have the crystal’s load capacitance, subtract 3 to 5pf of stray capacitance, and then finally multiply that number by two to arrive at the required capacitance value.
The feed resistor’s job (R214, in this case) is to prevent overdriving the crystal, which may lead to creation of unwanted harmonics. A value in the order of 10s of Ohms is typically sufficient.
Pinout Planning
With these connections in place, and of course with a suitable power supply for the VDD rail, the microcontroller has everything it needs to boot up and be programmed. However, in that state it is a bit useless, as we haven’t connected it to any peripheral.
For STM32 devices, you can either use the datasheet to see which pins can perform which required function (I2C, UART, and so forth), or use STMicroelectronics’ very handy and free STM32CubeIDE toolchain. This allows you to quickly see what peripherals are available and set up a pin-out for your hardware.
Since pin-out planning is very project-specific, we won’t be covering the details in this article. If you are curious to learn more, make sure to check out this video, which details all required steps.
Next Steps
In the next blog post, we’ll create a simple pin-out and then go through best practices for PCB design with microcontrollers. For example, best placement of decoupling capacitors, the crystal oscillator, and debug header.