Embedded Systems: Tiny Computers, Everywhere

Tiny (about 1cm across), cheap (normally around $1), and very low power (often less than one milliamp), fully programmable microprocessors can disappear inside a variety of consumer devices: cars, microwave ovens, toys, and even PC peripherals like mice, hard drives, and such.  These "embedded" computers typically run a fixed and simple program on the bare CPU, without an operating system.  The program to run is stored in nonvolatile ROM or flash memory.  

Plain old C is considered a "high level language" on embedded machines, and many commercial embedded products are still written in assembly language.  20MHz is considered "fast".  Integer multiply instructions are a rare luxury--floating point is almost never found. A $5 chip is considered "expensive".

A few families of embedded microcontrollers out there that I've used:

I've written code for real projects on Arduino, PIC, and 68HC11.  I've used all of the above at least at the demo level.  I mostly use Arduino for one-off projects, due to the full C++ support and good standard library.

Embedded Instruction Set Encodings

Comparing instruction encodings, they range from fairly clean 32-bit instructions on ARM:

ARM 32-bit instruction set encoding

Arduino/AVR uses a very peculiar 16-bit encoding.  There are 32 registers, like ARM, but the bit allocation is contorted to put the destination register bits at a fixed location starting at bit 4, which sometimes forces source register or constant bits to be non-contiguous!  At the hardware level, it's very easy to reassemble isolated scattered bits into a chunk of whatever size you need, but it makes for more complex machine code generation software.

atmel AVR8 16-bit instruction encoding

The PIC16F has a comparatively clean encoding, with values at least stored contiguously.  This version only uses 14 bits per instruction, which is not a power of two.  But because programs are typically stored in dedicated instruction-only flash memory, which can be built to be only 14 bits wide, this doesn't cause problems with wasted storage.

PIC16F instruction set encoding


PIC Microcontroller Hardware

Here's the Microchip PIC 12F675 reference manual.  The 12F675 is a tiny 8-pin chip about the size of your fingernail, and it'll run on anything from 2v to 5v.  It's got an internal 4MHz oscillator, flash memory space for 1024 14-bit instructions, an 8-entry hardware call stack, about 64 bytes of RAM, a 128-byte EEPROM, and a 4-channel 50kHz analog to digital converter.  It retails for about $1.

The way it works is you feed in ground on pin 8 (marked Vss in the datasheet), anything from +2V to +5V on pin 1 (marked Vdd on the datasheet), and all the other pins are programmable as analog or digital inputs or outputs in software.  Pin 4, GP3, can only act as an input line, not an output.  GP3 is also used as a high-voltage input to program the chip.

+5V -|-u-|- GND 
GP5 -|.  |- GP0
GP4 -|   |- GP1
GP3 >|---|- GP2

(This is a top-down view.  The "u" is the orienting notch in the chip.  Chips inserted backwards may be fried!)

You upload a program from a "real" computer onto the PIC by hooking up a PICkit 2 "device programmer", and routing the lines from the PICkit 2 snout pins:

  >   1.) GP3 VPP programming voltage
        2.) +5V (USB power)
        3.) GND
        4.) GP0 icspdat (programmed-in data)
        5.) GP1 icspclk (programming data clock)
        6.) not connected

By convention, there's always a triangle pointing to pin 1.  Again, if you hook the PICkit 2 up backwards, you might fry your PIC.  Frying one now and then is basically inevitable.  It's OK, they're $1.

Embedded Inputs

There are a bunch of different input devices you can hook up to embedded microcontrollers.  Basically, input pins read voltage, so anything you can convert into voltage, you can input into the controller.  One really common device for this is a voltage divider, which is basically just two resistors in series, where you hook up the middle lead to the microcontroller.

For example, pushing a button normally closes a contact, brining the resistance of the button from infinity down to zero.  Alone, that's useless.  But if you put 5V on one terminal of the button, and connect the other terminal to both the microcontroller input pin and a 1Kohm "pull-down" resistor (the other end hooked to ground), then you've effectively built a voltage divider with the button as the top resistor.  If the button is unpushed, its infinite resistance allows the pull-down resistor to pull the micro's pin down to ground, zero volts.  When the button is pushed, it shorts the micro's input pin up to 5v.  A little current leaks through the pull-down resistor, which is fine.  Light switches, limit switches, keyboard and mouse buttons, thermostat mercury bulbs, etc all boil down to just contact or no contact, and are interfaced in exactly the same way.

One caveat: pushing a button may result in several hundred tiny contact/no contact pulses a few microseconds wide.  You typically clean this up with a "debounce" circuit, either a hardware resistor-capacitor filter circuit, or a software function that only checks the button at 50Hz or so.

Another example, a "thermistor" is a resistor whose resistance varies with temperature.  If you set up a voltage divider as above, but plug a thermistor into the top half, you can convert temperature to voltage.

Analog TV, analog audio, and VGA signals are already just quickly-changing voltage patterns, so you can run them straight into an analog input pin of a microcontroller!

Embedded Outputs

Output pins output voltage, usually just either 0v or 5v, but can supply up to a few dozen milliamps.  This is just enough to light up a small LED, although you usually want a 1Kohm (or so) current limiting resistor inline with the LED.

To switch a useful amount of current, say to run a little motor, you usually need an interface device like a transistor.  "Signal" transistors can switch up to a few hundred milliamps, and "power" transistors can switch up to a few amps.  FET transistors can go up to dozens of amps fairly cheaply.  

One annoying thing about transistors is they only conduct current in one direction.  An electromechanical relay can switch AC current, although usually you need a transistor to push enough juice through the relay coil to get it to close.  I like relays, because they can't be hooked up backwards, and they're a lot tougher to fry than transistors.  Downside with relays is they're slow, energy intensive, and electrically and even acoustically noisy.

To turn a DC motor in either direction, you need an H-Bridge.  In theory, you can build these yourself from high power FET transistors, but in practice, it's way easier, especially at high power, to just buy a single-chip H-bridge (I really like the ST VNH3SP30TR, which switches up to 30 amps at 16 volts for $8), or even buy a prepackaged radio control electronic speed controller (ESC).

One cool output device is a servo, which is just a little motor, position sensor, H-bridge, and controller circuit integrated into one handy case.  You tell the servo what position to go to with a pulse-width-modulated signal: 5v for 1ms means all the way to the left, for 2ms means all the way to the right, and 1.5ms means halfway in between.  Most servos can seek to several hundred separate positions.  You usually repeat the seek signal every 15-30 milliseconds.  Servos use just three wires: black for ground, red (in the middle) for 5v, and white for the PWM position signal.  Servos are as low as $3 direct from China (although watch out for shipping!).

If talking to another big or little computer, you can speak USB (which is fast enough you usually need special hardware to speak it), plain slow serial (where bits are known fixed and *slow* times), I2CSPIB, or any of a bunch of weirder protocols.

Embedded Programming Models

Typically, embedded programs look like this:

	... initialize hardware ...
while (1) {
... read my inputs ...
... decide if anything needs to change ...
... if so, write changes to outputs ...

The main infinite loop is the "control loop".  The idea is microcontrollers usually are hardwired into known, fixed hardware, so they only have one job to do.  

You usually don't have files or permanent storage of any kind, don't have an OS, don't have multiple threads or processes, and in general are missing all the crap we've come to expect from computers.  On the minus side, your "debugger" is blinking LEDs and a voltmeter!

CS 441 Lecture Note, 2014, Dr. Orion LawlorUAF Computer Science Department.