Computing stuff tied to the physical world

Welcome to ’32

There are thousands of different microcontrollers (µC’s) to choose from. In this context, we’re going to use one which is easy to handle yet powerful enough to take us deeply into the modern world of embedded µC chips.

Many choices ahead. First, the architecture: 8-bit, 16-bit, or 32-bit? The lowest-cost option used to be 8-bit: all the machine instructions operate on 8 bits at a time (although the program counter quickly outgrew that, and went to 16 or more bits). What this means is that the µC can deal directly with numbers in the range 0..255 (uint8_t) or -128..+127 (int8_t). Not a lot you can do with that really, but fortunately every C compiler can easily sidestep that by adding a layer of abstraction, so that larger values are handled by generating multiple instructions at once for each requested operation.

In an 8-bit µC, such as the Arduino’s ATmega328, the expression 1234 + 5678 simply takes a bit more work for the compiler and the µC. Even more so with 1234 * 5678, which produces a 32-bit result (well, 23 bits to be exact, i.e. 7006652). When programming in C or C++, we can effectively ignore the issues and just declare our variables to have the proper 8-, 16-, or 32-bit width. Except for one aspect that is: all those extra instruction do end up getting stored in memory, so the program that ends up running in that 8-bit µC will be a bit larger than we’d expect by just counting the operations in our original program.

With 16-bits µC’s, such as the popular MSP430, life is a bit simpler. There’s quite a bit you can do with numbers in the range -32,768..+32,767 (int16_t), and a program counter which can address at least 64 KB of flash memory, so it’s a pretty sweet spot.

But why stop there. With 32-bits, we can count microseconds in an hour without overflowing the default numeric variable, or even seconds in a lifetime. Perhaps more importantly these days, as the memory sizes of many low-cost µC’s are increasing well beyond 64 KB, is the fact that a 32-bit architecture has some very practical benefits:

  • all memory can easily be addressed with a 32-bit pointer, both flash and RAM
  • we rarely need to think about overflow when the basic arithmetic size is 32 bits
  • all basic integer, floating point, and pointer operations use the same size values

Unlike an 8-bit ATmega328, a 32-bit µC does not need to play tricks to access flash memory or RAM – it’s all one pointer away. As a consquence, constants and static strings can be kept in flash memory, and don’t need to be copied to RAM. On very low-end µC’s with very limited RAM space, this can be an important benefit.

Note that being 32-bit on the arithmetic and pointer side of things does not mean that every variable needs to be stored in 32 bits. Even with 32-bit architectures, the standard is to keep memory byte-addressable, i.e. each 8-bit memory unit can be accessed individually. An array of 8-bit ints is as compact on a 32-bit µC as it is on an 8-bit one, the 32-bit version simply “widens” the 8-bit value to fill the 32-bit register when fetched, and vice versa.

Even on the code size, 32-bit architectures are not necessarily more wasteful than 8-bit designs, for a couple of reasons. For one, accesses to memory can be “indexed”, i.e. offset from another register value by a small value. Such instructions do not waste 32-bits to store small offsets. Another factor is that every arithmetic and pointer operation will act on the full 32 bits, instead of requiring multi-precision (and hence multi-instruction) trickery to make 8- and 16-bit operations simulate a 32-bit variant.

In fact, it’s not uncommon for 32-bit chips to generate less machine code for the same application as their 8- and 16-bit counterparts. One reason is perhaps that C and C++ compilers tend to focus on 16- and 32-bit operations (because 8-bit ints are often too limiting). A hand-assembled program for an 8-bit chip can probably still be made smaller than a hand-assembled one for ARM, but the reality is that assembly code is very rarely written these days. It’s simply too labour intensive, compared to C and C++.

So much for the architecture. Let’s see what options there are with ARM, the 32-bit de-facto standard today. Interestingly, ARM is not a single manufacturer of chips, but a supplier of designs licensed to a large number of different chip manufacturer. Unlike the AVR, PIC, or MSP series, choosing ARM does not limit one’s options to a single supplier. The difference is that each chip “vendor” makes different feature trade-offs and adds their own set of hardware peripherals onto that common µC architecture.

This is a big deal. It means we can pick ARM as architecture, and get all our software tools set up in just the way we like it, yet still shop around to pick a particular chip to work with. If that chip later turns out to stagnate in development, or is missing features we’d like to start using, we can switch to a different vendor and retain the investment in time and effort spent in setting up our tools. With luck we can keep most of our code – or at least port it without having to start from scratch, since the µC core architecture is still the same.

In short: let’s commit to ARM, and let’s pick one specific chip for now, just to get started. Here is a very nice one, since it’s small yet easily handled – as 8-pins DIP package:

DSC 4784

This is the LPC810 from NXP (used to be part of Philips). Make no mistake, it is a small and very low-end ARM chip. After all, with 2 pins used for power, this leaves just 6 I/O pins, and four of them need to be used in a specific way to load software into that chip!

Some specs: 4 KB flash, 1 KB RAM, 2x USART, 1x I2C, 1x SPI, 4x 32-bit timer, 4x PWM.

The chip runs on 1.8 .. 3.6 V, consumes at most 3.3 mA, and powers down to 1 µA.

As you’ll see, this little 8-pin chip is filled with oodles of 32-bit fun, and it’s surprisingly easy to fool around with. It even includes some magic, in the form of a “switch matrix”.

[Back to article index]