Many months ago when I was on vacation, I had a chat with the fantastic people behind SnapEDA about my heavily opinionated thoughts around how to pick a microcontroller (aside: if you’re a hardware designer and you’ve never used SnapEDA – sign up for free and watch your life instantaneously become easier). They briefly wrote about those thoughts in their Top 10 Microcontrollers post.
Here we are about 6 months later, and I thought it would be good to re-visit some of those opinions. I’ve just selected a few new microcontrollers for some projects, so I’ve gone through these decisions again recently.
There are a multitude of factors to keep in mind when picking an MCU, but the problem is that there are usually TOO many factors, and it’s easy to get stuck into an “analysis paralysis” situation.
Below are a subset of design considerations when choosing an MCU, but you’ll notice that I dismiss some of them entirely.
To caveat everything in advance… Anything I’m talking about is related to the standard IoT/Wearables game – each industry has their own requirements that sometimes fall out of line, but I’m going to be generalizing. For example, a lot of the things I’m going to mention might be flipped on their head in aerospace or in the military (in fact, that last statement is 100% true) – but I’m just focusing on what I spend most of my time on.
Also, I called this post “How to pick a Microcontroller (for Beginners)” with good reason. If you know what you’re doing, you’ll recognize that there are a lot more nuances to think about when you need to pick the powerhouse for your design… But, I’d say 80% of typical designs could be selected as I’ve discussed below and be just fine.
EDIT: Based on some comments on this page and on HackerNews, it seems my use of “for Beginners” was a bit overreaching.
The spirit of what I was referring to was along the lines of people who had worked with hardware and microcontrollers a little bit before (maybe even just in school), and were maybe thinking about laying out their first board – or moving away from the Arduino world. Young startups would be a good example, as well as junior/intermediate hardware/firmware designers.
If you’ve never heard of some of these acronyms (UART/SPI/I2C) – then this might be a tough read. In this case, I’d recommend starting with an Arduino and calling it a day (my favourites are the Teensy boards). There is an incredible community and an almost limitless number of resources out there to get you up and running.
If you need an operating system (Linux), then use a Raspberry Pi with the same reasoning as the Arduino.
For the sake of it though:
- MCU: Microcontroller Unit – The topic of the post.
- BOM: Bill of Materials – A list of the components/assemblies (and quantities) you’d need to build up a product.
- UART: Universal Asynchronous Receiver/Transmitter – A (typically) low-speed, point-to-point serial interface used to communicate between components (e.g. MCU to modems, GPS, Bluetooth modules) or to provide off-board communication (e.g. MCU to a computer through a serial port)
- SPI: Serial Peripheral Interface – A (typically) high-speed, serial bus used to communicate over short-ranges (intra-board) between microcontrollers and peripherals (e.g. an accelerometer). A dedicated chip-select line is used to pick the specific peripheral to communicate with the MCU.
- I2C: Inter-Integrated Circuit – A (typically) medium-speed, multi-master, multi-peripheral communications bus used to communicate over short-distances (intra-board). Device addresses are used to communicate with a peripheral.
- BLE: Bluetooth Low Energy – A wireless communications standard intended for point-to-point communication between a Central (e.g. a phone) and a Peripheral (e.g. a heart rate monitor). I won’t go further, as I could write a book just from all the Bluetooth acronyms.
This might also be a good time to mention one of the first books I ever read on hardware design (still my favourite actually) – The Art of Electronics.
If you have to ask, then stick with ARM. Essentially, the discussion revolves around M0/M0+ vs M3 vs M4.
The simplest way to look at this is that you get more powerful chips at higher numbers, but that comes with higher power consumption. So – do you need computation speed? Or low power? Also, M4s tend to be more expensive (and come with more flash/memory) and M0’s are on the lower end of cost.
Note: If you get a ‘specialty’ core, you need to compare apples to apples. So, an STM32L1 vs STM32F1 isn’t a fair comparison of cost vs power consumption – because the L-series is specifically designed for lower power, while the F-series is more cost sensitive. So, my blatant generalizations won’t always apply to those cases.
Another quick breakdown is: If you need floating point processing, you’ll probably use an M4. If you need a dumb data router (e.g. read from sensors, send off over BLE or Wifi, turn on some LEDs, no real data processing), you’ll probably use an M0+. Anything in between will likely be an M3.
I just found this great image from AnandTech which should help extend that last point.
Similar to core size, I tend to bundle everything into “low power” and “fast”, and treat those as mutually exclusive. Huge cop out, but it works well enough as per my previous points.
Summary: Prices are negotiable. But for projections, you still need them.
Check out Octopart for a rough idea at your volume. At the very least, it’s helpful for comparing, if not the exact price, then ‘more expensive than’ vs ‘less expensive than’.
The wheelhouse I try to work in is $1-2, so that gives you an idea of what I do – nothing needing those crazy A7 processors. Quite often, this works out because I choose other sensors/components to offload some processing, or design analog circuits to handle filtering. Generally results in an overall cheaper design (but occasionally less extensible – hasn’t been a real problem yet).
Aside: I had originally written this post with a focus on price, and it ended up just being a complete rant on how designers price hardware projects solely around BOM cost and how components distributors are causing people to get work done offshore… Not wanting to lose any of my ranting-behaviour, I’ve saved that for next week’s post instead…
As stated earlier, the Core you select has a large impact on your power consumption. If you’re really keen on low power but still need processing power, then you might need to buy a specialty MCU (e.g. an STM32L-series) – but you can’t have it all, so expect to pay more for that.
But, that’s only one general metric of power budgets – power consumption while running (uA/MHz) vs power consumption while in sleep mode.
An incredibly important, and overlooked, aspect of power consumption is the set of peripherals that are available in each low power state.
For instance, can you use DMA/PWM/DACs while in sleep mode? I discovered on a recent Cypress PROC4 BLE project that I couldn’t maintain a DAC while in deep sleep mode, even though the documents suggested that it should be possible. This meant that without a workaround, I would have needed to keep the CPU awake and at a pretty substantial level of power just to maintain a DAC output on one of the pins. The difference in power modes was microamps to milliamps – so very substantial (factor of 1000x just for a DAC).
Also, don’t hyper-optimize unless you need it. Is the device always-on and powered from the mains? Then focus on cost and performance of the microcontroller – skip the power conversation.
An addendum to that also applies for battery powered designs. If your spec is to have the device operate for 1 week before a recharge, design it to go for 2-3 weeks before a recharge to account for inefficiencies that arise over time. DON’T design it to last 6 months – because you’re paying for all the extra lifetime, either on the BOM or in R&D, and it probably won’t be a value-add to the product.
Generally, peripherals are where I spend most of my time when looking at MCUs (and fancy stuff like, does this MCU have a built-in RTC, so I don’t need to buy an external one). That value-add is very important to me and making my life easier.
Usually, the questions here are answered as part of your requirements. Do you need SPI/I2C/UART/etc?
Atmel has some chips with generic ‘serial communication blocks’, that let you pick what each one is, which seems pretty cool. Cypress basically lets you put any functionality on any pin, which is way cooler.
However, there are some nuances here too. For instance, I required a USB-capable device that could do I2C/UART/SPI and about 20 GPIOs. I also needed it for $1. This is easily found, BUT, that USB-capability tends to be a USB in peripheral mode, not in host mode (as you need a USB OTG device here).
For USB OTG MCUs, you’re looking at adding at least another $1. In my situation specifically, I needed one USB peripheral and one USB OTG – so I ran into the problem of… Do I pay extra and keep only 1 MCU type in my design, or do I go for the lowest BOM cost, in favour of needing to support 2 different microcontrollers. Yeah, not an easy decision.
In this case, we just pushed back on the USB requirement entirely and never needed to make that decision!
Pro tip: Always add 1 UART in addition to however many your design requires – this can be used as a CLI/logger and is absolutely invaluable.
48 pin, 64 pin, 100 pin… It comes down to the number of IOs needed for GPIOs, I2C, SPI, ADCs, DACs, etc… Packaging is more a result of the requirements, rather than something to actively think about. For simplicity, pick the smallest number of pins that satisfies your IO requirement (plus hedge a little with a couple of extra GPIOs for changing requirements or debugging).
I guess the only thing to consider here is whether you look for BGA vs QFN vs … My main recommendation is to stay away from BGA – because they’re incredibly difficult to debug bad connections on (impossible to do visually).
Good tools will let you do visual pinouts, warn you when there will be conflicts and mistakes, and will generate thin hardware access layers, to save you from needing to manually screw with registers. I also like those visual pinouts as being an easy way to generate an IO map between my firmware and hardware designers, rather than using Excel.
You still need to UNDERSTAND the registers, but I’ll be damned if I want to spend a day or two screwing around with them, when a tool can just auto-generate the correct configurations and let me get to application-level coding.
Specifically… Documentation, manufacturer support, and community examples.
This level of support is by family of MCU, rather than exact part number, but it again comes down to how much time I want to screw around with an MCU just to get any work done.
Manufacturer support is nice, but community support is where you hear about the real problems and all the dirty secrets.
A simple, and cheap, way to do this is to prefer MCUs where there is a promoted breakout board. For ST, they have all kinds of discovery boards, but I like using chips on the Nucleo boards, because the company spends a lot of time making and marketing them (and giving a bunch away). As a result, those chips have way more people using them, more docs, more sample code, and more people finding errors – and are just generally more vetted.
And before anyone guffaws this approach, I’d like them to spend 12-16 weeks communicating with ST about newly discovered errata in their chips, and then tell me this approach isn’t better, safer, and more time-efficient.
How much grief will this chip cause me?
This might be more related to sensors and communication modules (ie. BLE), but for example, how many external components do I need to get this module up and running. Or maybe, how much boilerplate code do I need to get it running? How up-to-date are the examples for this chip?
With MCUs, it’s usually pretty simple, so maybe this is more for other components.
I just want to know that using this chip, my life is made easier, not harder. Seems obvious, but really isn’t.
Last, but not least… Flash size
This is incredibly important (also should be under ‘hardware considerations’), and I’ve left it for last, as it’s not as straight-forward as people think.
As flash size goes up, usually pin count and price go up too, so everything is related (e.g. find me a 32k flash MCU with 144 pins – won’t happen). SRAM matters too, but I usually hand wave that a little, assuming that if my application fits in flash, I’ll make it run in the allocated RAM.
This one takes a bit of experimentation to figure out, but I usually use developer kits to mock out my entire project first and then build into final hardware and components – so, the dev kit should have a huge flash and then you can narrow it down after the dev kit phase to save on cost, once you have the actual size.
If you’re using an RTOS (e.g. FreeRTOS), expect these requirements to go up slightly, as doing everything takes up more space and RAM. However, going back to a good toolset – using STCube (or similar) to provision your tasks, queues, and semaphores – it can calculate your minimum required heap before compile time, so you have an immediate baseline on what size RAM you need.
Then double it
One thing that is often forgotten… Over-the-air updates.
Let’s say you can do OTA updates using Wifi or Bluetooth… Okay, well, where is that backup application going to reside?
You can get a huge external flash chip on the cheap, which is always nice, but I personally like having everything internal to the MCU, to reduce the need for extra drivers in the bootloader, and just other things that can go wrong with having more components on the board.
So now, you need to have room on the MCU for your application, plus your bootloader (usually small), and then if you’re storing a backup application in your MCU, you need to double the application size to get your total flash size.
Some people get away without doing this by first wiping the main application, and then pushing their new application into that space (Nordic calls this ‘single bank’ applications in their BLE components).
My preference is ‘dual bank’ setups, where you always have your known working application, you push the update to a secondary ‘bank’ of empty space, and on reset, your bootloader points to that new application, and if something goes wrong, it falls back to the original application.
This is basically a fail-safe MCU in-field upgrade. I’ve seen too many single-bank OTAs fail in the field, and best case, it’s embarrassing that you have to try to perform an update multiple times because the user can’t use their device otherwise, and quite often – they’ve bricked it and need to return it to get a new one.
With the dual bank, in the event of an update failure, their device is still 100% functional, but just at a previous version.
Finally… I’m not an ST fanboy
Reading through this post again, I just realized I call out ST a lot – but just for clarity, I’m not specifically recommending their products. I do use them quite often, but that’s more so a function of them having literally every conceivable permutation of MCU across a variety of price points.
Additionally, the STCube is fantastic, and I’ve been having success using the free System Workbench IDE/compiler (no code size limitations).
But, as I’ve mentioned in other posts, I feel pity on anyone who has to interact directly with ST for support – because guaranteed your project will end up 6-12 weeks behind schedule as a result.
My feature image is of Infineon’s Tri-Core device, which I will be playing around with shortly – but that’s mostly for automotive applications (and is expensive).
I’ve heard great things about Freescale’s (err… NXP’s) entire Kinetis product line – but I’ve only used it a couple of times.
I have made a few applications using Atmel’s (err… Microchip’s) SAM D series which was okay. I wasn’t too impressed by Atmel’s offerings frankly.
Otherwise, a lot of designs are moving (have moved) to using the microcontrollers embedded in BLE or Wifi devices (which I prefer doing as well, for cost reasons). Cypress’s PSOC/PROC BLE and Nordic’s NRF52 lines are good examples of that.
Just beware of some oddball problems that can arise when using embedded microcontrollers. For instance, make sure you have a good grasp on how the underlying RF stack and your application code share workload (time sliced? Interrupt-driven?). Also, be careful that your application code won’t interfere with the underlying stack (e.g. a long tight-polling loop or flash access which could interfere with a BLE stack’s timing).
Anyways, that’s all for me this week – up next week, that pricing rant I promised!