Archive for October, 2011
Cortex M3 For Dummies – STM32 Discovery Board
The ARM Cortex M3 has generated lots of buzz lately. Maybe you’ve been working with Arduinos, AVRs, or PICs for a while, and heard about the Cortex M3, but weren’t sure what it was all about or whether it was even relevant to you. This review will try to shed some light on the Cortex M3’s capabilities and development tools, using the STM32VLDiscovery board from ST Microelectronics.
The nice folks at Newark sent me a STM32VLDiscovery Cortex M3 evaluation board for review. This little board packs a big bunch for a remarkably low price. It can be found for under $10 if you hunt around for a deal, which is an amazing value. ST is also currently running a promotion in which residents of the USA and Canada can get a free STM32F4Discovery board, which is similar to the board reviewed here.
Although the STM32 Discovery board isn’t marketed as an Arduino competitor, it could be one. Its size, layout, and functionality make it a reasonable replacement for many applications needing a small microcontroller board with lots of I/Os for experiments and mad scientist projects. To help put its specs into context, I’ve selected a few other boards that readers may be familiar with for comparison purposes. All the boards contain a microcontroller along with one or two buttons and LEDs, with I/Os connected to hobbyist-friendly 0.1 inch headers, and can be programmed with a plain USB cable. In addition to the STM32 Discovery, they are the Arduino Uno, Arduino Mega 2560, and Copper AVR32.
STM32VL Discovery | Arduino Uno | Arduino Mega 2560 | Copper AVR32 | |
Price | $10 | $25 | $50 | $38 |
Processor | STM32F100 Cortex-M3 | ATmega328P AVR | ATmega2560 AVR | AT32UC3B1256 AVR |
Type | 32 bit | 8 bit | 8 bit | 32 bit |
Flash (KB) | 128 | 32 | 256 | 256 |
EEPROM (KB) | 0 | 1 | 4 | 0 |
RAM (KB) | 8 | 2 | 8 | 32 |
Max Speed (MHz) | 24 | 20 | 16 | 60 |
Voltage (V) | 2.0 – 3.6 | 1.8 – 5.5 | 1.8 – 5.5 | 3.0 – 3.6 |
User I/O Pins | 51 | 20 | 70 | 28 |
SPI channels | 2 | 2 | 5 | 3 |
I2C channels | 2 | 1 | 1 | 1 |
UART channels | 3 | 1 | 4 | 2 |
ADC channels | 16 | 8 | 16 | 6 |
DAC channels | 2 | 0 | 0 | 0 |
USB | no | no | no | yes |
The table makes it clear that you’re getting a lot of microcontroller for your money. ST is very likely selling these boards at a loss, because their goals are different than the makers of the other boards. ST isn’t trying to sell you a prototyping product, but rather they’re trying to get you familiar with their line of STM32 microcontrollers so you’ll go on to incorporate them into a product of your own design. To appreciate this, it’s necessary to understand the ARM Model that gave rise to the Cortex M3.
The ARM Model
The Cortex M3 and other ARM processors were designed by ARM Holdings, a British semiconductor company. ARM doesn’t actually manufacture the processors they design, but instead they license the designs to other semiconductor companies, who then turn them into specific chips and sell them under their own brand names. Thus ST’s STM32 line of microcontrollers, Texas Instruments’ Stellaris line, NXP’s LPC1000 line, Atmel’s AT91SAMxx line, and many others are all Cortex M3 microcontrollers. All use the same instruction set, and have very similar features, so you could find a chip in any of those lines that’s a near functional equivalent of the STM32F100RB chip on the Discovery board. The chips aren’t exact clones, however. They differ in the amount of on-chip memory, clock speeds, pin configuration, peripheral units, and other features.
Unfortunately this is the Cortex M3’s biggest obstacle to gaining more traction in the electronics hobbyist community, because there isn’t really a “Cortex M3 community”. There’s a Stellaris community, and an LPCxxxx community, and an STM32 community, and so on. Each one is just different enough from the others to make manufacturer-independent tools and code sharing difficult. By fracturing the community into many different parts, it also makes it harder for it to reach a critical mass necessary to catch significant public interest in the way AVR and PIC have.
ARM Cortex M3
So what exactly is a Cortex M3? It’s a microcontroller, like an AVR or PIC. That means it has built-in Flash memory and RAM, lots of general-purpose I/O pins that can be controlled individually through software, and built-in peripherals for things like serial communication or analog-to-digital conversion. Where the Cortex M3 differs from 8-bit AVRs and PICs is that it’s a 32-bit processor, capable of running at speeds up to 100 MHz in some versions, and is a cousin of the higher-end ARM processors found in devices like Apple’s iPad and in some PCs. It also offers larger memories than typically found on AVR or PIC microcontrollers. In short, it’s like a beefed-up version of the micrcontroller you’re using now.
A few interesting features of the Cortex M3 aren’t found on typical 8-bit microcontrollers, like hardware divide support, an internal PLL for clock synthesis, and two digital to analog converters. The M3 also has a clever remappable pin feature, which lets you choose from among several options for which pins to use for I2C, SPI, USART, ADC, and other hardware units. This provides extra flexibility in board design and port usage.
The Cortex M3 has a newer sibling called the Cortex M0. The M0 is geared towards lower cost, lower power applications than the M3, but the two are very similar. Most of what you read about the M3 on the web also applies to the M0.
While it has arguably generated the most buzz lately, the Cortex M3 is by no means the only entry in the 32-bit high performance microcontroller market attempting to supplant 8-bit mcus like the AVR ATmega. Another example is the AVR32 chip, found on the Copper board. This 32-bit microcontroller is also made by Atmel, although it shares little with Atmel’s 8-bit microcontrollers beyond the AVR name.
The STM32VLDiscovery Board
ST’s name for their Cortex M3 product line is STM32, and the STMVL32Discovery board is the smallest and least expensive of their Cortex M3 evaluation boards. Now who is this woman that appears on all of ST’s marketing materials for the STM32 line? Her photoshopped eyes look a bit disturbing. Some of their STM32 materials also use a rainbow-colored butterfly logo, which I like much better.
The STMVL32Discovery board is actually two boards in one: everything to the left of the vertical line in the above diagram is an ST-Link programming/debug module. It can be used with the Cortex M3 module on the right side of the board, or to program and debug a STM32 mcu on another board in a stand-alone application that lacks a programmer. I believe an earlier version of the ST Discovery board actually had perforations in the PCB so you could break-off the ST-Link module and use the two halves separately, but with the STMVL32Discovery there’s only a line in the silkscreen to remind you there are two functionally separate modules on a single board.
At roughly 3.3 x 1.7 inches, the board is smaller than a standard-size Arduino. Two rows of 0.1 inch header down the sides of the board make it easy to connect other components. The headers are on both the top and the bottom of the board, so you can make connections from either side, which is a nice touch. You could almost drop the ST Discovery right into a breadboard, except for the 6-pin header along its right edge. I’m not sure what ST was thinking here, because when inserted into a breadboard, these six pins will all be tied together in a single row. Depending on the layout of your breadboard, you may be able to insert the ST Discovery such that those six pins hang off the edge, unconnected.
Besides the Cortex M3 itself, the board also contains two user-controllable LEDs, a user push button, and a reset button. That’s similar to what you’ll find on an Arduino board, and is just enough to test things out and make sure it’s all working before connecting more components.
The board is powered over USB, or optionally from an external supply. When first connected to your PC, it immediately runs a demo program that flashes the LEDs in different patterns when you press the user push button. Under Windows it also automatically mounts itself as a read-only USB storage drive, which contains a few files with links to the online documentation. That’s pretty slick.
There’s no printed documentation included with the ST Discovery board, but the online user manual is quite well done. It includes a quick-start guide, block diagram, layout diagram, mechanical drawing, full schematics, explanation of all the jumpers and solder bridges, and a pin description table. The companion software package includes the libraries and header files for ST’s version of the Cortex M3 and for the ST Discovery board peripherals, as well as several example projects. On ST’s web site you’ll also find detailed tutorials for building and running the example projects using the three officially supported development toolchains.
ARM Development Tools
There are a bewildering number of development toolchain choices for ARM Cortex M3 development. It’s hard to overstate just how painful this makes the getting started process for a beginner. Worse still, the only officially-supported toolchains (IAR, Keil, and Atollic) are professional tools which are very expensive, and certainly won’t be of interest to any hobby developers. When their web page has a “get a quote” link instead of listing an actual price, that’s the clue to look elsewhere. For reference, the Keil tools cost $4895 for a single license, and the others are similar. Ouch!
The professional tools do all offer a time-limited trial version or a code size-limited version, but few hobbyists will be happy with those as a permanent solution.
If you’re willing to cough up a little money (but not $4895) for a well-made development tool with good support, Rowley Associates Crossworks is well-regarded and is just $150 for a personal license.
After looking at more than a dozen different development tools, I decided to put together my own toolchain based on the CodeSourcery CodeBench g++ Lite GNU command line tools and Eclipse C/C++ IDE. This excellent setup guide for Eclipse and CodeSourcery with STM32 describes the process in detail, so I won’t list all the setup steps here. Depending on your familiarity with other development environments and your tolerance for this sort of job, you may find the process anywhere from slightly tedious to completely impossible. It involves installing Eclipse, the Java runtime, CodeSourcery, GDB server, the STM32 SDK, and the ST-Link programming utility from six different sources, and then configuring them all to work together properly. It really makes you appreciate the convenience of a tool like Atmel’s AVR Studio, which performs all of the same functions in a single tool with a single download and install process. All together, it took me about 90 minutes to get the STM32 Cortex M3 development tools configured and program one of the example projects onto the ST Discovery board.
Cortex M3 Software Development
OK, let’s blink some LEDs. Here’s the simplest of the examples provided by ST:
#include "stm32f10x.h" #include "STM32vldiscovery.h"GPIO_InitTypeDef GPIO_InitStructure;void Delay(__IO uint32_t nCount);int main(void) { /* Configure all unused GPIO port pins in Analog Input mode (floating input trigger OFF), this will reduce the power consumption and increase the device immunity against EMI/EMC *************************************************/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_Init(GPIOE, &GPIO_InitStructure);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, DISABLE);/* Initialize Leds LD3 and LD4 mounted on STM32VLDISCOVERY board */ STM32vldiscovery_LEDInit(LED3); STM32vldiscovery_LEDInit(LED4);while (1) { /* Turn on LD2 and LD3 */ STM32vldiscovery_LEDOn(LED3); STM32vldiscovery_LEDOff(LED4); /* Insert delay */ Delay(0xAFFFFF);/* Turn off LD3 and LD4 */ STM32vldiscovery_LEDOff(LED3); STM32vldiscovery_LEDOn(LED4); /* Insert delay */ Delay(0xAFFFFF);} }void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); }
If you’ve previously developed software for other microcontrollers, then this probably looks fairly understandable. The first step is to configure all of the I/O pins as inputs, akin to setting the DDR (data direction) register on an AVR or calling pinMode() with the Arduino environment. In this case, the I/Os are configured by passing a struct to a function instead of directly twiddling some bits in a register, although it’s likely that the implementation of GPIO_Init() does something like that under the hood. That RCCABP2PeriphClockCmd() function looks a little strange, though– it appears the port’s clocks need to be explicitly enabled before configuration, then disabled afterwards.
The rest is just manipulation of the LEDs, but it’s a bit more abstract than most people are probably accustomed to seeing. You don’t normally need to initialize an LED or call a function to turn it on. These functions are provided by the ST Discovery board library to simplify development, but curious minds will want to know what they actually do. Here’s the implementation of STM32vldiscovery_LEDInit() and definitions of LED3 and LED4:
typedef enum { LED3 = 0, LED4 = 1 } Led_TypeDef;const uint32_t GPIO_CLK[LEDn] = {LED3_GPIO_CLK, LED4_GPIO_CLK};#define LED3_GPIO_CLK RCC_APB2Periph_GPIOC #define LED4_GPIO_CLK RCC_APB2Periph_GPIOCvoid STM32vldiscovery_LEDInit(Led_TypeDef Led) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable the GPIO_LED Clock */ RCC_APB2PeriphClockCmd(GPIO_CLK[Led], ENABLE);/* Configure the GPIO_LED pin */ GPIO_InitStructure.GPIO_Pin = GPIO_PIN[Led]; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT[Led], &GPIO_InitStructure); }
So STM32vldiscovery_LEDInit() enables the clock for port C, where the LEDs are connected, and then configures the correct pin in port C as an output. GPIO_Mode_Out_PP specifies a push-pull output, in other words a normal output that’s actively driven high and low. GPIO_Speed_50MHz controls the rise and fall times of the output signal. Selecting a faster speed will produce shorter rise and fall times, but will consume more current and generate more supply voltage swings and electrical noise.
Lastly, let’s take a look at STM32vldiscovery_LEDOn() and STM32vldiscovery_LEDOff():
void STM32vldiscovery_LEDOn(Led_TypeDef Led) { GPIO_PORT[Led]->BSRR = GPIO_PIN[Led]; }void STM32vldiscovery_LEDOff(Led_TypeDef Led) { GPIO_PORT[Led]->BRR = GPIO_PIN[Led]; }
Each port has a separate bit set (BSRR) and reset (BRR) register. Writing a 1 to the corresponding bit position for a pin sets the pin’s value to 1 or 0, depending on which register is written. Alternatively, all 16 pins of a port can be set at once by writing a 16-bit value to the port’s ODR register.
Note that all these GPIO_ APIs are specific to ST’s Cortex M3 library. If you’re using some other flavor of Cortex M3, then your method of controlling I/Os will be different.
Building the code and programming it to the ST Discovery board’s Cortex M3 is straightforward. Within Eclipse, select Project -> Build Project from the menu, and if all goes well you’ll end up with a .bin file. The LED blink example generates a 7K .bin file for the Debug configuration, and 5K in release. That’s a pretty big binary for just the few lines of code in main.c, but all those GPIO_ functions and other support libraries bloat the code further. Due to the larger size of Cortex M3 binaries, the larger Flash memory of the M3 as compared to an Arduino isn’t as significant an advantage as it first appears.
To program the board, launch the ST-Link utility program, and open the .bin file. Programming doesn’t automatically reset the board or launch the new program, though. After programming is complete, you can open the MCU Core dialog and press the System Reset button. From here you can also do other neat stuff like view all the mcu register contents, or single-step the clock.
Debugging
The built-in ST-Link on the ST Discovery board also supports live debugging of the program running on the Cortex M3. If you’re coming from the Arduino, or vanilla AVR development with an AVRISP mkII programmer that lacks debugging capability, this is a quantum leap forward. The people who wrote the Eclipse + Code Sourcery + STM32 setup guide also wrote an excellent hardware debugging setup guide, so I won’t repeat the steps here. Unfortunately, it involves downloading the entire Atollic Lite toolchain (250MB) just to get the ST-Link compatible gdbserver it contains. This also requires registering with Atollic, and getting a (free) activation key. It’s a hassle, but you only need to do it once.
Debugging works just how you’d expect. You can step through running code, set breakpoints, examine variable values, and view the call stack. Stepping through code seems unexpectedly slow, taking 1-2 seconds to step over a single line. The gdbserver also crashed a few times while I was debugging, and the only clue was that attempts to start a new debugging session failed with a generic error. I also found that stepping over a line that branches to itself, like
for(; nCount != 0; nCount--);
put the debugger into a state where the program was suspended, but the debugger thought it was running, forcing me to press the “pause” button to regain control. The overall debugging experience was a bit rough around the edges, but was much better than nothing. A different toolchain might have provided a smoother debugging experience, and I don’t fault the Cortex M3 or the ST Discovery board hardware for the debugging problems I encountered.
Beyond the Examples
Running through the examples is a good way to get familiar with the Cortex M3, but to really learn what it’s like to develop for, there’s nothing better than creating your own custom project. Using the LED blink program as a template, I was able to port some code for controlling a Nokia 5110 LCD display, and print the message shown in the photo. The LEDs, transistor, and resistor in the upper right are from an unrelated circuit on my breadboard, and the only connections between the Discovery board and the LCD are the seven wires at the bottom.
My example program shows the code used to print the message on the LCD. There were no real surprises during development, and it only took me about 30 minutes to get the LCD working with the ST Discovery board. The only oddity I encountered was that there doesn’t appear to be a calibrated wait/delay function anywhere in the STM32 libraries. I wrote my own delay_ms() function that’s roughly accurate, assuming a 24 MHz clock and code compiled in the Debug configuration. It’s an imperfect solution, so if there’s not already a suitable delay function somewhere in another ARM library, it will probably be necessary to use a timer to get delays independent of clock speed and compiler options.
The GPIO_ library is great for getting projects up and running quickly. For high-speed performance-sensitive designs, however, there may be an unacceptable amount of overhead involved in calling a library function every time an I/O pin needs to be manipulated. In practice, the compiler may inline these functions when building Release configuration code, but I didn’t investigate to confirm it.
When powered by USB, the voltage at the ST Discovery board’s 3V3 pin is only about 2.97V, with no load. That might cause problems for some devices, but the Nokia 5110 LCD worked fine at that voltage.
Conclusions
Are the Cortex M3 and STM32 Discovery board what you’ve been looking for? In what kinds of projects do they fit best?
As an alternative to an Arduino or vanilla AVR board, the STM32 Discovery board looks like a clear winner in terms of price and hardware. It has more I/Os, more peripherals, larger memories, faster core clock MHz speed, and more performance per MHz due to its 32-bit internal design. All that, and it’s priced at less than half the cost of an Arduino.
What the STM32 Discovery lacks is easy-to-use development tools, and a large community of other users to collaborate with. The method I used to create a toolchain is probably too complex for many beginners, and they won’t be able to afford the commercial alternatives, so they’ll likely be shut out of Cortex M3 development entirely. Even if they do get the toolchain installed successfully, they won’t find the wealth of examples, 3rd-party libraries, or other support materials that exist in the AVR world. The value of that support shouldn’t be underestimated. Performance and memory aren’t everything.
The STM32 Discovery board is better matched for experienced microcontroller developers who’ve already done some work outside the Arduino environment, and are ready to accept additional complexity in exchange for major hardware improvements. The 24 MHz 32-bit Cortex M3 on the STM32 Discovery board is already a nice step up from 8-bit AVRs, but it’s just the beginning of the Cortex M3 line. Higher-end members of the Cortex M3 family can perform complex real-time tasks not possible on an AVR, drive high resolution color displays, run a real-time micro-OS capable of scheduling many programs at once, or even run Linux. For those who need this kind of power, the Cortex M3 is a great choice.
Read 10 comments and join the conversationBLsync
My newest design is a PC downloader utility called blsync, created as part of the Backwoods Logger project. BLsync is a command line program with a corresponding hardware adapter, and it enables snapshot lists and temperature / altitude / pressure graphs to be transferred from the Logger to a PC. Above is a quick graph I put together in Excel from blsync data, showing temperature and air pressure in my home over a 40 hour period. If I’d been hiking recently, I’d show a nice altitude over time graph too.
The Backwoods Logger is a programmable graphing altimeter / thermometer, developed as an open hardware project. It’s designed for backpackers, runners, environmental scientists, and other people interested in environmental data logging over timescales from an hour to a few weeks. The Logger design is based on an Atmel ATmega328 microcontroller and Bosch BMP085 pressure / temperature sensor. You can build your own Logger following the published instructions, or purchase a pre-assembled Logger prototype. For additional details visit the project home page, or join the public discussion mailing list.
Bit Bang Serial
The Backwoods Logger wasn’t designed with a PC download function in mind, so retrofitting one after the fact was a challenge. The only external connector on the Logger is the 6-pin ISP port, used for programming the microcontroller firmware. After programming is complete, the pins MOSI, MISO, and SCK can be used as general-purpose I/Os or as an SPI interface. (The other three pins are VCC, GND, and RESET.) So what’s the simplest way to build a PC communication interface using those three pins?
I’m not aware of any straightforward way to build a PC interface using SPI, so my solution was to design a serial interface. Unfortunately the built-in USART that’s normally used for ATmega serial communication is hard-wired to use specific pins, and those pins aren’t connected to the ISP port. Instead of using the built-in USART, then, I wrote a pure-software serial port driver that works over the MOSI and MISO pins. It manually performs all the start bit and bit-to-bit timing functions necessary for a bidirectional serial connection. In order to reliably get the right serial baud rate, I also added some code to calibrate the internal RC oscillator using the external 32768 Hz crystal as a reference. It was all a little painful, but it works.
The Backwoods Logger comes in two versions that run at different clock speeds, which complicated the software serial driver a bit. The Logger Mini runs at 8 MHz, which provides 208 clock cycles between each bit at 38400 bps. That’s a fairly healthy amount of time to react to each bit and decide what to do with it. However, the Logger Classic runs at 1 MHz to minimize its power consumption, which provides only 26 clock cycles between bits. That’s not very much time for processing, and it means I wasn’t able to implement simultaneous two-way serial communication. At any given moment the Logger can either be receiving or sending serial data, but not both. There also needs to be a delay of about 200 microseconds between received bytes, in order to provide the Logger with sufficient time to process a byte before the next one arrives. There’s no receive buffer, so if the PC sends bytes with too short a byte-to-byte delay, data will be lost. The blsync program inserts the necessary delays when sending command bytes to the Logger. Since most of the data flow is in the other direction, this doesn’t impose any noticeable speed penalty. The complete set of graphs can be transferred from the Logger in about 300 milliseconds.
Physically, the blsync adapter is just a passive converter from the 3 x 2 ISP header to a 1 x 6 serial header, with RX connected to MOSI and TX connected to MISO. A MOSFET is used to perform level conversion (thanks Erik!), so either a 5V or 3.3V USB-Serial device can be used. If you know you’ll always be using a 3.3V USB-Serial device, you can omit the MOSFET and connect TX to MISO directly. The 1 x 6 header is designed to work with common USB-Serial devices such as the Adafruit FTDI Friend or Sparkfun FTDI Cable 3.3V.
I also designed a simple blsync adapter board, for a more rugged and permanent solution. It adds a pair of LEDs to indicate Logger and PC activity, but is otherwise the same as the circuit shown above.
Using BLsync
The blsync utility program can retrieve the Logger’s firmware version number, temperature / altitude / pressure graphs, and snapshot list. Data can be saved in CSV format for importing into Excel, or as raw binary.
The blsync utility is configured using command line options:
Usage: blsync -p port [-b speed] [-v] [-c] [-r] [-g filename] [-s filename] -p port Port to use for Logger communication, such as COM1. -b speed Bit rate for communication. Default is 38400. -v Display the Logger firmware version number. -c Save files in CSV format. This is the default. -r Save files in raw binary format instead of CSV. -g filename Sync the graph data, and save it to the named file. -s filename Sync the snapshot data, and save it to the named file.
For example, to sync the temperature, altitude, and pressure graphs from the Logger connected to COM3, and save them in CSV format to the file graphs.csv, the command line would be:
blsync.exe -p COM3 -c -g graphs.csv
By importing the CSV file into your favorite spreadsheet or other analysis program, you can perform whatever analysis you desire on the collected Logger data. A hiker might create a graph showing his friends how high he climbed or how cold it was. A conservationist might use several Loggers to collect data for a study of temperature and pressure at multiple locations within a habitat, and how they relate to observed species observations. Through hardware and software extensions, humidity or other custom sensor data could be collected as well. Because the Backwoods Logger is an open source, open hardware project, the sky’s the limit on what data might be collected and what interesting things might be done with it.
Read 1 comment and join the conversationThe Quest For A Simple Level Converter
Sometimes the simplest things give me the most trouble. I’ve been working on a downloader cable adapter for the Backwoods Logger, with the goal of supporting both 5V and 3.3V FTDI cables. Because the Backwoods Logger is a 3.3V design, the incoming TXD (transmit data) signal from a 5V cable needs to be lowered to 3.3V for safe operation. However, the incoming TXD signal from a 3.3V cable should be passed through unmodified. Outgoing signals from the Logger require no conversion, because a 3.3V output is still a valid logic “high” for a 5V system. I need a level converter for a single input, that operates correctly with both 5V and 3.3V inputs with no configuration or jumper settings.
Level Converter Chip
One solution is to use a 3.3V chip with 5V tolerant inputs, like a 74LVC244. That would work, but I’d prefer something simpler and smaller if possible, since I only have a single input to convert.
Clamp Diode
A second solution is to use a series resistor and a clamp diode, like this (image from daycounter.com):
That prevents the voltage at the 3.3V Backwoods Logger input from going more than a diode drop above the 3.3V supply. With a standard silicon diode’s drop of 0.6V, that clamps the voltage to 3.9V. For the ATmega328, that’s not safe: its maximum rated voltage on any input is just 0.5V about VCC. A germanium diode has a drop of 0.2 to 0.3V, so that would work, but it’s not a part that many people typically have handy in their parts bin.
This solution also has the drawbacks of consuming current from the 5V output, and dumping current into the 3.3V supply, raising the supply voltage. The FTDI outputs have a maximum output current of 24 mA. Assuming a germanium diode with a 0.2V drop, that means R1 needs to be at least 62.5 Ohms. Frankly I’m not sure how to quantify the risk of dumping current into the power supply. In the case of the Logger Classic with its tiny CR2032 battery, dumping 24 mA into the battery in the wrong direction definitely doesn’t sound good.
Zener Diode
The approach that appealed to me most was to use a series resistor and a Zener diode connected to ground, like this (image from daycounter.com):
The Zener has a known reverse-bias breakdown voltage called the Zener voltage. Raise the voltage above the Zener voltage, and the diode conducts. The series resistor produces a voltage drop, so that the voltage at the Backwoods Logger input never rises above the Zener voltage. You can get 3.0V or 3.3V Zeners (or lots of other values too).
So I ran out and bought some Zeners, and built this circuit, and it didn’t work at all how I’d expected it to. I used a 3.0V Zener, and a 100 Ohm series resistor, to limit the current drawn from the FTDI cable to 20 mA. When I connected a 5V dummy output, I got 2.91V at the Logger input. That seemed odd, it was off by 0.09V, but it was still close enough. Then I connected a 2.95V dummy input (the actual voltage from my crummy “3.3V” breadboard regulator), and I got 2.4V at the Logger input. Huh? That’s not going to work. I had expected that for any voltage below 3.0V the Zener would do nothing, and for anything above 3.0V it would clamp it to 3.0V, but that’s clearly not what happened.
What went wrong? Truthfully, I’m not exactly sure. The datasheets talk about a minimum current necessary to get the Zener effect, but I’m not sure that applies here. I can’t safely increase the current further anyway without damaging the FTDI cable. But would more current even solve this problem? It makes sense that the Zener wouldn’t really turn on instantaneously at 3.0V, but rather would begin to conduct more and more as the voltage approaches 3.0V. With a voltage of 2.95V, the Zener would already be partly conducting, pulling the voltage seen at the Logger input below 2.95V. But how much below? How can this be quantified?
One thing in particular bugs me about all the Zener diode datasheets: every datasheet lists values for standard measurements called Izt, Rzt, Izk, Rzk, Rz, and a few others. These are standard measures from some hypothetical standard Zener graph, but the datasheets never actually show this graph, and I’ve never been able to find one anywhere. I know “k” is for “knee” and I believe “t” is for “test”, but what I really need is an actual IV curve for a Zener with these values labeled. Then I think I’d understand this better.
Further Complications
Just to make things more interesting, there’s one more constraint to consider. The Logger Classic uses an unregulated battery as its supply. It can work just fine at battery voltages of 2.8V, and probably down to 2.5V or even lower. In order to stay within the VCC + 0.5V margin of the ATmega328P, the input voltage must not go more than half a volt above the potentially fading battery voltage. A standard 3.3V input when the battery is 2.7V would actually damage the ATmega. That’s why I chose to use a 3.0V Zener rather than a 3.3V one. That should be safe down to a battery voltage of 2.5V, below which I could configure the ATmega’s brownout detector to engage.
The Way Forward
I’m going to sleep on this, and see if anything brilliant comes to me. If anyone else has a suggestion, please reply in the comments. Assuming I can’t find a way to make the Zener work while still meeting the other constraints, then I’ll probably cave in and use a level converter chip. Without really understanding the implications of current flowing into the supply battery under the clamp diode method, I wouldn’t feel comfortable relying on that approach.
Read 19 comments and join the conversationMac Floppy Emulator Revisited
A few weeks ago, I posted some thoughts on an SD-card based floppy emulator for classic Macintosh computers. This idea has been on my mind again recently, and I’m trying to determine whether this could be done solely with a microcontroller and a few other support chips, or whether a CPLD or FPGA is needed. I also took a look at a few related designs for inspiration. If a working emulator could be built entirely from a microcontroller and other common parts in through-hole packages, then it could be built by anyone on a piece of perfboard or even a breadboard. In contrast, a CPLD or FPGA would require a custom-made circuit board, and probably voltage level shifters as well. Unfortunately, it looks like a lone microcontroller probably wouldn’t be sufficient.
Need for Speed
The fundamental problem is access speed. The Mac reads and writes the disk drive control registers like they were an external memory: there are 4 register select lines (one doubles as the write data line), a read data line, and a write enable. Reviewing the floppy driver ROM routines, there are typically only 3-4 CPU instructions between when the last register select line is set correctly, and when the data is read or written. Call it 3 instructions to be on the safe side: that’s 12 clocks of the 68000 CPU. If the microcontroller can’t respond that fast, it won’t work. For example, say the Mac wants to test whether the drive head is at track zero. It sets the register select lines to select the TK0 register, and 12 clock cycles later it reads the result from the drive. If the microcontroller can’t respond to the change on the register select lines fast enough to put the correct value on the read line before the Mac reads it, then an incorrect value will be read. For writes, the write enable signal LSTRB may be asserted for as little as 12 clocks. If the microcontroller doesn’t check the the signal at least that often, then a write may be missed.
I wrote some AVR code to do basic read/write management of the drive control registers, and looked at the compiled output. The read loop is 16 AVR instructions long, and something like 20 AVR clocks (I didn’t count exactly). If the AVR is clocked at the same 8 MHz speed as the Mac, then that’s not enough time. If it’s clocked at the max AVR speed of 20 MHz, then it might be fast enough. However, this assumes the read loop begins at the same instant that the CPU changes the register select lines. If instead the lines change just after the AVR reads them, then it will spend 15 clock cycles producing the result for the old register, and 16 more cycles producing the result for the new register. So in practice, the AVR needs to be fast enough process its read loop twice within the register-select-to-read-data time of the Mac.
Maybe with better optimized code, or an overclocked AVR, or a PIC or Propeller or another high-speed microcontroller, it might be fast enough to act as a set of emulated drive control registers. But it would be sitting in a busy loop constantly watching the register select lines, providing zero additional time to read/write the SD card, or poll for user input buttons, or refresh a status LCD. Using interrupts to watch the register select lines would only make things worse, since then you’d have the interrupt latency on top of the 16 AVR instructions of processing to produce a result. I don’t really see how this approach could work.
HxC Floppy Emulator
The HxC Floppy Emulator project uses a PIC and an SD card to emulate a floppy drive, and works on many classic computers like the Amiga and Atari ST, but not the Macintosh. So how does it do it? Unfortunately as far as I can tell, it’s a closed design and the schematics and PIC firmware source aren’t available. I’ve posted a question in the forums to confirm. I think I can guess why it works, though. Unlike the Mac, those other computers use a parallel control interface for the floppy drive. In other words, all the control lines (step direction, step request, side to read, write data, etc.) and state lines (track 0 indicator, read data, write protect, etc.) are broken out with individual wires. That’s a lot easier to implement with a microcontroller, and there’s no need to constantly watch the register select lines and race to put the right value on the data line before the Mac reads it.
Now What?
I still won’t rule out the micrcontroller-only approach entirely, but it would definitely require a high-speed microcontroller, carefully-crafted loops for speed, and cycle counting to generate the output data and motor speed tachometer signal with exactly the correct timings. That doesn’t sound like much fun. I keep thinking that I must be overlooking something, and maybe there still is a way to do this easily with just a microcontroller, but so far I’m not seeing it.
If a lone microcontroller won’t work, what about a microcontroller with a few support chips? For example, the mcu could output the contents of all 16 control registers simultaneously on separate pins, and the register select lines connected to a 16-to-1 mux to select the desired register without any mcu intervention. Or the registers could be implemented in an external register file or latch of some kind. I think this approach could work, but would end up with half a dozen or more low-density 7400 series and similar chips. And at that point, I think it would be better to replace them all with a single CPLD.
That brings me to where I think this is all going: an emulator consisting of a CPLD and a microcontroller working in tandem. That’s not the worst design, but it’s not great if the goal is to design an emulator that anyone can build. A CPLD-based design would require a custom PCB, JTAG programming hardware, and other headaches. That’s not a problem for me, but I was hoping to design something usable by a wider audience who would probably balk at those requirements.
Read 8 comments and join the conversationHalloween LED Display Demo
I’ve been working with my daughter Alice on an LED lightboard project for Halloween, and today we finished the construction. Whew! We started out two weeks ago with a giant 4 foot x 2 foot pegboard from Home Depot, and ambitious plans to make various Halloween shapes by populating specific holes. Reality quickly sunk in, though, as we realized how much work it would take to do something interesting with a 1152 hole pegboard. We eventually settled on building a much smaller 8 x 8 fully-populated LED matrix instead, controlled through software to make a variety of patterns and designs.
Even this 8 x 8 matrix was a lot of work, and for anyone contemplating a similar project, I’d recommend just buying a prebuilt LED display matrix like the Peggy 2LE instead. We probably could have found a simpler way to build the matrix, but it was a fun project nonetheless, and the result looks great. Take a look at the video, and then I’ll explain how it’s built.
We started with a pegboard with 1-inch hole spacing, painted black on the front display side. Alice cut lengths of bare copper wire for the column busses, and lay them on the back of the board, just to the right of the holes. The bus wires were temporarily fixed in place using torn strips of adhesive tape. She then laid more bare wire on top to form the row busses, with additional layers of tape at the crossing points to ensure the row and column wires didn’t touch. The tape didn’t stick very well, so she reinforced the grid with hot glue, taking care not to cover the points where LEDs would be soldered later.
The next job was mine: building the controller circuitry. I used a MAX7219 LED driver, which is a nice (but expensive) single chip solution for controlling up to 64 LEDs. It handles all the multiplexing and storage of pixel values, so the microcontroller doesn’t have to do anything expect send SPI commands to change values when desired. The 7219 rapidly scans through the columns at about 800 Hz, activating each one in turn. This is fast enough that human vision sees them as being all active at once. The chip has sixteen driver pins: one for each row and column. It pulls one column high while the others float, and pulls rows low if the LED at that row/column intersection should be lit.
The 7219 can supply 320 mA to a column, which when divided by eight rows provides 40 mA per LED when the column is activated. Because the scanning action results in a 1:8 duty cycle for the columns, the average current per LED is only 5 mA. Typical current for an LED is about 15 mA, so 5 mA is fairly low. I was worried the display would look pretty dim as a result, but as you can see in the video, it’s actually quite bright.
I built the 7219 circuit on a piece of perfboard, hand-placing all the connections between the chip, row/column headers, control header, and power. What a mess! Whenever I build something on perfboard, no matter how simple, it always seems to turn into a giant mess with flakey connections everywhere. Unfortunately this example was no different. Soldering in the components is no problem, but I can never get the wires to stay put when soldering the connections. Any connection involving more than two wires is also a hassle. My pain with perfboards is why I favor manufacturing custom PCBs even for simple projects, but in this case there wasn’t enough time before Halloween.
With the control circuit ready, the final construction step was to populate the board with 64 LEDs. Alice and I took turns soldering and bending LED legs to shape. It was her first big soldering project, and she had a blast. It helped that everything had wide open joints to solder, and a mistake couldn’t do anything worse than kill a 10 cent LED. Populating the whole board took about two hours, working slowly and carefully through each joint.
The finished board worked– sort of. It worked as long as you never lit more than 5 LEDs in a column, otherwise the display would go dark until you reset the Arduino. I wasn’t expecting this, but with my geek dad pride on the line, I had to find a solution.
My first thought was that this was some kind of clock glitch problem. I guessed that my terrible wiring combined with the sudden voltage swing from 6+ LEDs turning on was glitching the SPI clock. I spent some time fiddling with the clock wire and other control lines in an attempt to clean things up, to no avail. I also realized that a glitchly clock would likely produce display errors, but not make the screen go permanently dark, so I started looking for another explanation.
The display was finally fixed by reducing the current draw (and resulting brightness). The 7219 uses a current set resistor between two pins to control the current to the LEDs. I initially followed the sample circuit in the datasheet, using a 10K ohm resistor to provide maximum brightness. Through trial and error, I found that I had to increase the resistance to 30K before the display worked reliably. This should have resulted in a 3x reduction in current to the LEDs, but I only noticed a very minor reduction in perceived brightness.
Why was it necessary to use a larger set resistor? One possibility is that it was somehow drawing more than 320 mA with the original resistor, causing the 7219 to overheat or trip some kind of internal protection circuit. I can’t think what I might have done to make it draw too much current, though. A more likely explanation is that my shoddy power and ground wiring has a non-trivial amount of resistance, so when the current draw is high enough, +5V is pulled down and ground is pulled up far enough that either the Arduino or the 7219 no longer function. Either way, reducing the current fixes the problem, but one is a thermal problem and one is a supply voltage problem. I’ll leave further investigation for another holiday.
Now we need some cool animation displays for this board! Alice has been learning the basics of programming at Codecademy, and has already done some simple button-pushing and LED-blinking Arduino projects, so with a little help from me I think we can create a great-looking Halloween display.
Read 7 comments and join the conversationBackwoods Logger – Open Source and On Sale
The Backwoods Logger is a programmable graphing altimeter / thermometer, developed and documented here at Big Mess o’ Wires. Today is a big day for the Logger: I’m re-launching it as a formal open hardware project, and also offering a small test run of pre-assembled Backwoods Logger Mini units for sale. After corresponding with many enthusiastic people over the past months, I’m convinced there’s a community of Logger-heads out there that’s bigger than just me. My goal is to bring together people who are merely curious about the Backwoods Logger, or are interested in buying a prebuilt Logger, or have already built their own Logger, or are interested in making improvements to the existing Logger software and hardware.
Re-launching the Backwoods Logger as an open hardware project means it will no longer be a personal endeavor developed solely by me, but a collobarative effort that welcomes involvment from everyone. The project is hosted at Google Code: check out the new Backwoods Logger project page. All the source code, schematics, and other design files are there. And if you’re interested, please consider joining the project.
I’ve also created a Backwoods Logger discussion mailing list, intended for questions, ideas, or any other conversation about the Logger. The list is public and is hosted by Google Groups, and you can join or leave the list at any time via the group settings page. Membership in the discussion list is separate from membership in the Google Code project, and everyone’s welcome to join the mailing list regardless of whether they’re project developers or just curious about what’s happening in Logger Land.
Backwoods Logger Mini Sale
To help kick things off, I’ll be selling a small test run of pre-assembled Backwoods Logger Mini units. The Mini is the OLED version of the Logger, pictured above.
- Temperature measurements in 0.5 degree steps, from -10F to 117.5F
- Air pressure measurements in 0.01 in-Hg steps, from 5.9 to 36.12 in-Hg
- Altitude (calculated from air pressure) measurements in 2 ft steps, from -1384 ft to 14999 ft
- Can configure metric Loggers with units in degrees C, millibars, and meters on request (ask when ordering)
- Graphs of temperature, pressure, and altitude over time
- Three graph time scales: past 2 hours, past 10 hours, past 2.5 days
- Current rate of ascent/descent
- Estimated time of arrival at a user-defined altitude
- Weather forecast
- Station pressure and pressure at sea level
- Snapshot feature – make a permanent record of date, time, altitude, temperature, and pressure at important waypoints
- Current date and time display
- Battery voltage indicator
- Sound on/off control
- 128 x 64 OLED screen
- Battery Life: 3 – 5 months with a single AAA battery
- Dimensions: 1.9 x 1.1 x 0.7 inches (48 x 28 x 17 mm)
- Weight: 0.7 ounces (19g), including battery
Also check out this Backwoods Logger demo video to see a demonstration of the Logger’s features. The video shows a Logger Classic, which has a lower-resolution screen and is slightly bigger and heavier than the Mini, but otherwise has an identical set of features. Don’t see a feature you want? Join the project, and help create it.
The hole in the corner of the Mini board can be used to tie it to your backpack, or wear it around your neck on a lanyard. The Mini has no case, so if you expect wet weather you’ll need to provide your own. The last photo above shows a Mini inside a dental floss container!
The price for this test run will probably be about $59, but will depend on how many confirmed orders I receive beforehand. You should understand these will be prototype units, and may contain bugs or other defects, but I’ll do my utmost to resolve any problems that might occur. If you’re interested in purchasing a Backwoods Logger Mini, please contact me and let me know. I expect the lead time to be about six weeks.
Read 9 comments and join the conversation