STM32F1xx, some advice

I have worked with the ST Micro ARM Cortex-M3 micro-controllers. I think I like them but here is some advice to help you along.

  • Yes, you really need all those capacitors. These things run at 75MHz or better. Put the caps close to the pins.
  • These things are more easily damaged by ESD than one might think. Yes, I know they’re supposedly marketed for automotive applications. Yes, I know the data-sheet says class blah blah blah. You need to do more, I had a product whose only vulnerability was the battery compartment and I still had to do more. I found this helpful
  • Build your own cross compiler from scratch! You will learn things about C/C++ that you need to know to be a good embedded systems engineer. I have some stuff like crt0 and linker scripts on this site. Yes I cut and pasted them but I was about 3/4ths of the way through writing them from scratch before I found prettier ones on-line and no, I did not waste my clients time, I was able to solve deep problems with other people’s projects at the same client because I understood this stuff. I stood on the shoulders of giants and here’s a link to one of those giants 🙂 The Olimex site also has some info for building a cross compiler.
  • I like and recommend the Olimex boards but they have the same ESD problems I had so don’t be lulled into a false sense of security, add mitigation to your design. I used a TVP diode between VDD and VSS on the line coming from the battery clip.
  • I have the FlySwatterII and the Olimex ARM-USB-OCD-H, I prefer the latter mostly because it can provide power.
  • Use the ST micro provided libraries sparingly, sometimes it’s easier to understand the peripheral and write your own code than to try to sort out their library madness or HAL.
  • OpenOCD and gdb are awesome. Yes gdb has a steep learning curve, no it’s not pretty, but it is the most powerful debugger known to man… The only thing I’ve ever seen that was close ran on a VAX when I was a kid. I find cgdb helpful. I sometimes use the layout stuff but it’s a bit young (read buggy) but helpful. Seriously, learn to cope with gdb it can do things no other debugger can.
  • Read the errata these things have a surprising number of hardware bugs!

PIC Microchip, some advice

I like 8bit PICs but here’s some advice:

  • You want to use assembly. Really, don’t mess with whatever crazy (non-ANSI) c compiler these clowns are pushing. Assembly (particularly on these chips) is not hard.
  • MPLAB X is horrible, it takes all the things I love about PICs and makes it complicated.*
  • Don’t mess with the PICkit3 it only works with MPLAB X. You want the PICkit2, it works with pk2cmd. If you really need those debugging features then it’s time to move on to something with an ARM core (Serial Wire Debug gdb and all that)
  • If you need to do math use a lookup table (array). If your math is complicated then do it in C and use something with an 32 bit ARM core (read: not a PIC); these things don’t have FPUs. Really, most of them multiply by adding in a loop, an FPU is like a pocket calculator for your CPU.
  • Use the simplest chip you need to do the job, start out with something with with a modest number of peripherals lest you be overwhelmed.
  • Really, learn assembler your going to get stuck anyway and have to write some assembly anyway… and it’ll make you feel smart!

* What they’re really up to here is to immerse you in their marketing madness. Hey Microchip, if ya want me to use your high end chips than you need to contribute to gcc or clang or both, if it’s not an open source ANSI compiler I won’t use it, I am not going to learn your crazy dialect of C or pay you for the privilege of writing code that will create demand for your chip!

Beacons using a PIC10F200

Before there were standards like iBeacon and Eddystone we had to “roll our own.” This is a primitive beacon using a PIC10F200 (8pin DIP) along with a simple 315MHz OOK transmitter. Later versions, not shown here, use SDR.

beacon.asm

This is written in PIC assembly. It simply transmits my FCC Call sign with a number tacked on the end. Production versions would substitute a UUID and checksum in place of my Call sign. The signal is intentionally weak, we only want to know if the watch (wearable device) is within a few meters. Battery life is important too, this thing should be good for about 57 years on a couple AAA batteries.

The gist of what this code does is to set a timer and go to sleep. When the timer expires the chip wakes up and increments some counter (the timer durations are rather short) and we need some randomization to minimize the possibility of interference betwixt proximal beacons*. If it’s time to send a burst, my Call sign, then it does so, resets the counter and timer and goes back to sleep. The idea is to spend as much time asleep as possible to conserve batteries.

beacon.asm

Makefile

* This is a bit tough to explain so bare with me. The oscillators on these things are not precise. So imagine we were to send a burst every 30 wake-ups (e.g. no randomization). Okay so lets say you have two of these close together and we put batteries in them at the same time. Now every 30 wake-ups they both wake up and clobber each other (the wearable can’t hear either because they “talk” over each other). Now eventually, because the oscillators are imprecise, they drift apart but that may take hours; therefore, we introduce some randomization to keep them asynchronous. I’m skipping some nuance here but I hope you get the idea.

Toaster Oven Re-Flow

This project involves a lot of teaching an old company some new tricks, but there are a million things that could go wrong with this stunt! My goal was to keep costs low as we flush out a new process for doing things. Toaster Oven re-flow is just what it sounds like. Instead to spending $10K on a re-flow oven I did some reading and convinced my boss, Larry Skutchan, that we should at least try this. My yield stands at about 80%. We have prototyped this whole product in-house, the only thing we sent out were the board spins.

The Prototype

Here is the prototype fresh off the 3D printer! It was a real pleasure to work with Industrial Designer / Modeler Andrew Dakin  and Industrial Engineer / 3D printer guru Andrew Moulton, they took some very primitive ideas and turned them into something awesome! It’s hard to see but the 26 keys across the top have the Braille letters of the alphabet on them.BBPrototype

Braille Buzz Main Board

BBMainBoardV0.2This is the second board spin. I designed this board and product from scratch. I started with the data-sheets for the ST Micro chip, took some tips form the Olmex boards that featured a similar chip, crossed my fingers and it worked! I have used Eagle (very popular), and Altium (very complicated), and I even tried PCB (too primitive) but my favorite is kiCad (just right). I suspect Verilog can be used for PCB layout, it’s really for “programming” logic gates, but I have my suspicions that it could be a good choice for really complex PCBs.

OpenOCD for BrailleBuzz (STM32F103)

This is my convenience stuff for working with openOCD. “debug” loads the “executable” bin and halts the MCU. “run” loads the “executable” bin and starts it running. “samples” loads some data in to the second bank of flash.

Compile environment for BrailleBuzz (STM32F103)

Okay three big topics here: crt0 (also known as c0), crt1 (also known as c1), and the linking scripts. crt0 is the code that needs to run before your main, it sets up things like the stack. crt1 is the low level stuff that would normally be OS calls. I sorta simulate an OS because we don’t have one, so I simulate a heap and malloc, etc. The linking scripts tell the linker where to put things in the address space.

crt0

This is the code that executes before your main function gets called. Execution starts at 0000h (actually it may be a pointer on this implementation). But before one can actually run any C one has to do some set-up stuff. Traditionally one writes crt0 in assembly and indeed my original was in assembly. If your careful and don’t use certain C constructs you can write crt0 in c itself. There is heavy interaction with the linker script as we need to prepare things like the BSS, EBSS, stack etc. Remember C is gonna need to know where in the address space (which changes from implementation to implementation) things like RAM(rw), flash(ro) and heap are.

crt0.c

crt1

This is the low level operating system type stuff. Well, we don’t have one, but we’d still like things like printf and malloc to work or at least not crash. Remember after you call printf, somewhere down the pike some ascii (or unicode) has to go somewhere. In our case we’re going to output it to one of the UARTs. This is very convenient when debugging! This code took a long time to sort out and is incomplete. I got some of the stubs from the manufacturer. Recall that in a unix OS we would be opening files (in dev), reading from and writing to them but here we don’t have an OS or a file-system so we make some assumptions, lots of assumptions.

crt1.h

crt1.c

linker script

We need to tell the linker where to put things. What addresses correspond to RAM? What addresses correspond to ROM(flash)? Where’s the “heap”, how big is it.

stm32.ld

IRQ handlers

Lastly we have the irq handlers. We mostly just go into an infinite loop. Naturally we really should do better than this but I never got round to it.

irq.h

irq.c

Build environment for BrailleBuzz (the GNU tool-chain for STM32F103)

I am documenting from memory here, I did this over a year ago and have just now gotten round to documenting it. There are a bunch of things I gloss over and one or two inaccuracies that I am miss-remembering. Nevertheless, this should give you a clue and along with the excellent documentation over at gnu.org it should get you going. Of particular use are my bash scripts, read them, understand them, and optionally use them verbatim. Also use the docs on the olmex site. I used those development boards to bootstrap this project they are excellent! I don’t know if it’s the “right” way but what I did was to create a usr directory off my home directory. While I have root on my box, and everything else around here, my goal was to avoid using it so that I could simply zip it up and hand it off to another developer; which is what I have recently done.

tallen@timmy:~/usr$ ls -l
total 32
drwxrwxr-x 5 tallen tallen 4096 Jan 24 2014 arm-none-eabi
drwxrwxr-x 2 tallen tallen 4096 Mar 24 2014 bin
drwxrwxr-x 12 tallen tallen 4096 Feb 26 2014 build
drwxrwxr-x 3 tallen tallen 4096 Mar 17 2014 include
drwxrwxr-x 3 tallen tallen 4096 Mar 24 2014 lib
drwxrwxr-x 3 tallen tallen 4096 Jan 24 2014 libexec
drwxrwxr-x 9 tallen tallen 4096 Feb 12 2014 share
drwxrwxr-x 11 tallen tallen 4096 Mar 24 2014 src

Under src one puts the source for the various tools, e.g. binutils, gcc, gdb, openocd, speex, and gmp, mpc, mpfr; the latter three are prerequisites for gcc.

tallen@timmy:~/usr/src$ ls -l
total 138384
drwxrwxr-x 17 tallen tallen 4096 Jan 24 2014 binutils-2.24
-rw-rw-r-- 1 tallen tallen 22716802 Dec 2 2013 binutils-2.24.tar.bz2
drwxr-xr-x 33 tallen tallen 4096 Oct 16 2013 gcc-4.8.2
-rw-rw-r-- 1 tallen tallen 85999682 Oct 16 2013 gcc-4.8.2.tar.bz2
drwxrwxr-x 15 tallen tallen 4096 Feb 11 2014 gdb-7.7
-rw-rw-r-- 1 tallen tallen 24846320 Feb 5 2014 gdb-7.7.tar.bz2
drwxr-xr-x 15 tallen tallen 4096 Sep 30 2013 gmp-5.1.3
-rw-rw-r-- 1 tallen tallen 1818812 Sep 30 2013 gmp-5.1.3.tar.xz
drwxr-xr-x 6 tallen tallen 4096 Jan 15 2014 mpc-1.0.2
-rw-rw-r-- 1 tallen tallen 633173 Jan 15 2014 mpc-1.0.2.tar.gz
drwxr-xr-x 9 tallen tallen 4096 Mar 13 2013 mpfr-3.1.2
-rw-rw-r-- 1 tallen tallen 1074388 Mar 13 2013 mpfr-3.1.2.tar.xz
drwxrwxr-x 9 tallen tallen 4096 Mar 24 2014 newlib
drwxr-xr-x 8 tallen tallen 4096 May 5 2013 openocd-0.7.0
-rw------- 1 tallen tallen 3493924 Feb 10 2014 openocd-0.7.0.tar.bz2
drwxrwxr-x 9 tallen tallen 4096 Apr 7 2014 speex-1.2rc1
-rw-rw-r-- 1 tallen tallen 1061882 Jul 23 2008 speex-1.2rc1.tar.gz

Next one creates a build directory under usr. Under build one creates directories for each of the utilities enumerated below. I also played some games with soft links to facilitate version upgrades later:

tallen@timmy:~/usr/build$ ls -l
total 168
lrwxrwxrwx 1 tallen tallen 14 Jan 24 2014 binutils -> binutils-2.24/
drwxrwxr-x 11 tallen tallen 4096 Feb 18 2014 binutils-2.24
lrwxrwxrwx 1 tallen tallen 11 Jan 24 2014 gcc_1 -> gcc-4.8.2_1
lrwxrwxrwx 1 tallen tallen 11 Jan 24 2014 gcc_2 -> gcc-4.8.2_2
drwxrwxr-x 14 tallen tallen 4096 Feb 18 2014 gcc-4.8.2_1
drwxrwxr-x 12 tallen tallen 4096 Mar 24 2014 gcc-4.8.2_2
-rw-rw-r-- 1 tallen tallen 129742 Feb 10 2014 gcc_arm-eabi_build.pdf
lrwxrwxrwx 1 tallen tallen 7 Feb 11 2014 gdb -> gdb-7.7
drwxrwxr-x 11 tallen tallen 4096 Feb 11 2014 gdb-7.7
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 gmp -> gmp-5.1.3/
drwxrwxr-x 15 tallen tallen 4096 May 29 14:20 gmp-5.1.3
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 mpc -> mpc-1.0.2/
drwxrwxr-x 5 tallen tallen 4096 Apr 7 2014 mpc-1.0.2
lrwxrwxrwx 1 tallen tallen 10 Jan 24 2014 mpfr -> mpfr-3.1.2
drwxrwxr-x 6 tallen tallen 4096 Mar 17 2014 mpfr-3.1.2
drwxrwxr-x 4 tallen tallen 4096 Apr 7 2014 newlib
lrwxrwxrwx 1 tallen tallen 14 Feb 10 2014 openocd -> openocd-0.7.0/
drwxrwxr-x 5 tallen tallen 4096 Feb 11 2014 openocd-0.7.0
lrwxrwxrwx 1 tallen tallen 12 Feb 26 2014 speex -> speex-1.2rc1
drwxrwxr-x 9 tallen tallen 4096 Apr 9 2014 speex-1.2rc1

binutils

Binutils includes things like the assembler, you do remember that the assembler is the back-end for gcc; that is, gcc really just produces assembly which is fed to the assembler, the linker, the archiver, and other tools. There are practical reasons why these steps are combined under the gcc command line interface. One creates a directory in which to one builds the binutils. I created a small bash script.

Don’t forget to do “make install”

gmp

One creates a directory in which to one builds gmp. I created a small bash script.

mpc

One creates a directory in which to one builds mpc. I created a small bash script.

mpfr

One creates a directory in which to one builds mpfr. I created a small bash script.

gcc

One builds gcc in two steps and the previous tasks are prerequisites. Let’s take a moment and think about what we’re actually doing here. We are using the host’s native gcc compiler to build a cross compiler from (more or less) the same source that built the native compiler (look up what GNU actually stands for and see if you get the joke); that is, a compiler that runs on one arch (the host, probably an Intel variant) but produces code that will run on another arch (the target, in this case, an ARM variant). To make things more confusing the compiler actually needs itself. So one builds a striped down version of gcc (just C), and then one builds the full version of gcc (C & C++). Confused yet, no? GNUs not unix… GNUs not unix…

gcc_1

One creates a directory in which to one builds the stripped down gcc, gcc_1. I created a small bash script.

Don’t forget to do “make install”

newlib or libc

You’ll need newlib or libc to build the full compiler. The full compiler needs libc stuff for it’s built-ins. One needs to build newlib, this is a slightly stripped down version of libc especially for MCUs and SOCs. So use the stripped down gcc to build newlib, I created a small bash script.

gcc_1

Then one builds the fill compiler in the directory gcc_2. I created a small bash script.

Notice the “–with-newlib” the full compiler needs libc stuff for it’s built-ins. One needs to build newlib, this is a slightly stripped down version of libc especially for MCUs and SOCs. So use the stripped down gcc to build newlib, then build the full version of gcc and then re-build newlib.

gdb

Your gonna want a debugger and if doesn’t get any better than gdb. Okay, it’s got a steep learning curve, the the curses stuff has issues, but it has features. Perhaps my favorite feature is that it works more or less the same on whatever platform and I don’t know about you but I haven’t the time to learn a bunch of proprietary UIs. GDB works with openocd to do remote debugging.

openocd

I had to build openocd from scratch to get a workable version. This was over a year ago and openocd was very young, it has matured a lot and the stock copy that comes with your distro will probably work for you nowadays, I include it here just for completeness.

Embedded Makefile