Intro: Realtime OS / LCD on $12 Discovery Board
One day I had a college professor getting ready to teach us students a powerful mathematical trick in EE101 - mesh circuit analysis I think. Before he got started he said, "Today, my students, I give you a BIG gun." It got our attention and I remember the quote, but sadly, I'd be hard pressed to do mesh analysis today.
Today, my Instructible readers, I give you BIG guns!
For all you hardcore hot-shot embedded software programmers, time to step up to the next level by learning how to use an embedded realtime OS on your next project. Not only do you get a priority based preemptive realtime OS, you also get all the goodies that go along with a realtime embedded OS like: Tasks, Queues, binary semaphores, counting semaphores, recursive semaphores and mutexes for communication and synchronization between tasks, or between tasks and interrupts. Gives me goose-bumps just thinking about it! All this software power comes by way of FreeRTOS which has been around a long time.
Oh, and the hardware we're gonna run this on makes my head spin. We're not talking little a Pic or Atmel micro. No, no, we're gonna run this OS on a new 24MHz 32 Bit ARM® Cortex™-M3 from ST Micro. The folks at ST Micro have made a development board, called the STM32 VL Discovery (VL is short for value line), available for $12 dollars! They are probably selling the board at a loss. For comparison, this board is about 1/3 the cost of a standard Arduino board.
The 24MHz STM32F100RBT6B micro that is used on the Discovery board is probably the slowest they sell. Most of these chips run much faster! However, the chip itself is not overly expensive - Digi-Key carriers the chip for $3.50 on orders over 100 pieces. For $3.50 you get 128k flash and 8k RAM - no EEPROM needed since the micro can write to its own flash.
Below are the specs right off Mouser's web page for the STM32F100RBT6B chip:
- Core: ARM Cortex M3
- Data Bus Width: 32 bit
- Program Memory Type: Flash
- Program Memory Size: 128 KB
- Data RAM Size: 8 KB
- Interface Type: I2C, SPI, USART
- Maximum Clock Frequency: 24 MHz
- Number of Programmable I/Os: 51
- Number of Timers: 6
- Operating Supply Voltage: 2 V to 3.6 V
- Maximum Operating Temperature: + 85 C
- Package / Case: LQFP-64
- 3rd Party Development Tools: EWARM, EWARM-BL, MDK-ARM, RL-ARM, ULINK2
- Development Tools By Supplier: STM32100B-EVAL
- Minimum Operating Temperature: - 40 C
- On-Chip ADC: 12 bit, 16 Channel
On-Chip DAC: 12 bit, 2 Channel
Learning this stuff has real application in the job market too. Jabbering on about your latest Arduino project is not going to get much attention in an interview with an employer. However, explaining how you solved a priority inversion problem on a embedded realtime OS will! Trust me, I've interviewed / hired lots of embedded programmers over the years as a former Motorola employee.
Here is a link to purchase the STM32 Discovery for $12 bucks from Mouser. Buy two just in case - the shipping cost is crazy so you might as well get two. Oh, and the LCD in the picture also comes from Mouser ($12.50 each).
The OS we're going to use is called FreeRTOS and their web address is listed next.
Don't worry, you can use this OS in your commercial product without exposing your source code. The OS uses a modified GPL license and is also royalty free. For a complete breakdown of the license terms here is another link:
The FreeRTOS realtime OS has been around for a long time and is very stable. The OS has been ported to 26 different architectures and is very well documented. I first used FreeRTOS on a product for GM. My code, coupled with FreeRTOS, is running around in 10's of thousands of vehicles. In those days, I used FreeRTOS on a Freescale Star12 chip (much less powerful). I truly believe using FreeRTOS on that project saved my bacon.
Having the OS able to run on 26 different architectures, as I said before, does not mean your tied to a specific piece of hardware. I like freedom of choice. If you really want to dive in, I would suggest purchasing the book "Using the FreeRTOS Real Time Kernel - a Practical Guide". The eBook version is only $25 bucks - buying the book helps the development of FreeRTOS too. You can easily find the book at FreeRTOS.org. Note, I have no financial interest in either FreeRTOS or ST - I just like both.
To keep up on future developments I'm working on you can always link to my RSS feed from my web site. I try to post intermediate steps before I gather enough stuff together for an instructible. My web site (Powerhouse Electronics) address is listed next:
For the RSS feed, add a "live" bookmark to:
Step 1: Software Tools
Getting a good build / debug environment setup seem to be more than half the battle. This is really key in the long term. Having a serious debugger is often overlooked when deciding on hardware. Many of the low-end micros don't even have a debugger. This forces the programmer to use printf statements and pin toggling to debug code.
Anyway, seems like there are three choices when it comes to an IDE to program and debug the STM32 Discovery board. I think all these IDE's are based on the Eclipse code base. The contenders are:
- Atollic, LiteTrueSTUDIO
- IAR, Embedded Workbench® for ARM
- Keil, MDK-ARMTM
I'm not sure about IAR or Keil, but Atollic does not run on Linux. Nor does it run under Wine, I'm told. However, it does just fine running on Linux inside a VMWare virtual WinXP machine. The screen-shot below shows WinXP running in a window with Atollic running inside that. Total build time is 20 seconds after a "clean". Not too bad. Well, I guess it goes without saying, but I hear Atollic also runs OK on Windows too. If you must.
The second screen-shot below shows the debugger in action. The debugger inside the Atollic IDE works very well. Everything works as you would expect. There are also some video's that Atollic has made that show how to use the debugger to the fullest. I've watched the videos twice already and will probably watch them yet again. There is nothing more precious to an embedded software programmer than a powerful debugger!
Atollic makes downloading and installing TrueSTUDIO easy. After you register they automatically email you a license key within a minute. The windows installer is easy to use and quick. The first time the software is run it will ask you for a "Workspace" location. This is simply a directory where all your source code is located. Note, there is also an easy way to switch to another workspace too.
Step 2: Building / Running FreeRTOS
This is going to be easy. In no time at all your gonna be up and running with a 32 bit micro running a full blow RTOS!
Simply download the attached zip file and expand somewhere on your hard-drive. After that's done, you should have a directory called "Copper" setting somewhere on your hard-drive. Now, fire-up TrueSTUDIO and click on menu option File/Switch Workspace/Other... Inside the dialog box, point Atollic to the new directory you just unzipped called "Copper".
With the STM32 Discovery board attached to a USB port you should be ready to go. Under menu option "Project" click on "Clean...". This will purge all the old binary build files if there are any. Next, press the key combination control-b to build all the source code. After 20 to 30 seconds all the source files will be recompiled. There should be no errors or warnings after the build process completes. Note, normal builds take just a couple seconds since the compiler only has to compile one or two files.
OK, we're ready to rock and roll. With the STM32 Discovery board connected, click on the little bug on the tool bar (hover the mouse over it - it should say "Debug Copper.elf"). After the nag ad, the debugger downloads the binary to the board and then waits for your command. This all takes about 10 to 15 seconds. Inside the debugger, press F8 to start the program running.
With the attached source code compiled and running on the board you should see some cool LED activity. The green LED is flashing quickly and the blue LED is fading up and down slowly. I programmed the blue LED to kinda look like the Apple logo - fads up and down like a heart beat.
All this LED stuff is happing in vApplicationTickHook() which is located in "utils.c". The function vApplicationTickHook is an OS callback function. The OS calls this function on every OS tick. Note, the tick rate is changeable in the FreeRTOSConfig.h file. Look for a variable called "configTICK_RATE_HZ" to change the basic tick rate. Default tick rate is set for 100Hz (or every 10ms).
So, the green LED toggles on every 5th tick (10ms * 5 = 50ms). That gives 10 flashes per second. The blue LED is using a PWM output which is running at 100Hz. The timer was setup so that values of 0 to 1000 given an output of 0 to 100%. In other words, each increment of the duty cycle value changes the PWM output by 0.1%. Coooool. The OS tick callback function just steps the duty cycle back and forth.
The point of twiddling the LEDs is to make sure the OS is running smoothly and has no hiccups. If any of your code suspends, interrupts, or blocks the OS for long periods then the LEDs will make that visible.
Step 3: Hardware Hacking LCD Style
It takes just seven wires to interface a SPI LCD to the Discovery board. The signals are power, ground, SPI clock, SPI data, chip select, command / data, and reset. If only one device is on the SPI bus then the chip select could be hardwired for always select.
Blow is a lists the Discovery pins which are being used in my source code.
128-Kbyte Flash, 8-Kbyte RAM
P1 Name Function
11 PC0 Analog In 1 (0 to 3.0 Volts)
12 PC1 Analog In 2
13 PC2 Analog In 3
14 PC3 Analog In 4
15 PA0 User Button on Discovery
20 PA5 SPI1 SClk to LCD
21 PA6 LCD Data=1 / Cmd = 0
22 PA7 SPI1 MOSI to LCD (SDA)
23 PC4 LCD Reset
24 PC5 LCD SCE (CS0)
25 PB0 BTN_UP
P2 Name Function
1 PB10 USART3 Tx (Debug Port)
2 PB11 USART3 Rx (Debug Port)
3 PB12 BTN_LEFT
4 PB13 BTN_CENTER
5 PB14 BTN_RIGHT
6 PB15 BTN_DOWN
P3 Name Function
4 PC8 Blue LED on Discovery (100Hz TIM3_CH3)
5 PC9 Green LED on Discovery
24 PB8 RC Servo Motor Out1 (50Hz TIM4_CH3)
25 PB9 RC Servo Motor Out2 (50Hz TIM4_CH4)
I've had two different LCD types connect to the Discovery board so far. One display comes from Sparkfun and is called a Nokia 5110. The second display comes from Mouser / Electronic Assembly. Links for both displays follow:
My goal was to find a really inexpensive graphic LCD. The Sparkfun / Nokia LCD is 84x48 pixels and costs $10 bucks. The Mouser / EA display is 102x64 pixels and comes in three flavors - the EA display cost $12.36.
The Sparkfun / Nokia seems like old surplus stuff. Plus, there were a lot of negative comments about problems with the LCD / board mounting. The Nokia display uses a zebra rubber thing to interface the glass to the PCB. The zebra thing seems to be causing problems. The EA display, on the other hand, is brand new, has more pixels (but uses a goofy memory layout), and costs more. But, for me, the translucent blue LCD is just so sexy. Both display types are read only. There is no feedback from the display possible. Seems some restriction by using a SPI interface to the LCD. My vote goes with the EA display even though it cost more just a bit more.
The attached source code can drive either type of display. There is a #define near the top in lcd.h that sets which way to build the source. Just set the #define correctly and rebuild / reflash to switch between displays.
Driving a little LCD really shows some of the capability of the ARM micro.
- RAM buffer in the micro holds all the pixel data. Think of it as a shadow RAM buffer.
- The LCD task in the OS handles sending the shadow RAM buffer to the LCD.
- The entire LCD is rewritten at a 10Hz rate. Could be any rate really. The LCD basic hardware refresh rate of the LCD glass is around 50Hz.
- The LCD task sends one page (a page is one byte / 8 pixels high by X bytes / pixels long where X is the pixel width of the display) at a time. So, a 102x64 pixel display has eight pages where each page is 102 bytes long.
- Sending one page at a time allows the shadow ram to be "manipulated" before being sent to the LCD. As an example, the display can be invert 180 degrees in software if needed. Note, some displays can do this in hardware - but, each display seems to do this differently. Could also flip black-on-white to white-on-black using software. It's just cool to be able to inject changes as the pixels are getting ready to fly out of the SPI port. Note, all LCD pictures in this instructible are using the software invert feature.
- The LCD task uses DMA to transfer the data to the LCD. While the task is waiting for the DMA to complete it calls "taskYIELD()" so that other lower priority tasks can do work. This is HUGE, the micro is free to do other stuff while the DMA channel is spitting out SPI data at full speed. Note, higher priority tasks will preempt the lcd task automatically.
- The SPI bus is running at 3MHz (Sparkfun display can only handle 4 MHz). So, a 102 byte page gets transferred in just over 300 microseconds! The EA display can handle much higher SPI clock. But, there is no rush - a higher clock is more susceptible to noise.
- Using DMA transfer means all 102 bytes (one page) are transferred without a single interruption of the ARM micro. The DMA reads the RAM buffer and keeps the SPI port 100% jammed packed busy until done - all without hassling the micro one little bit. The SPI bytes are packed so perfectly because this is all done at the hardware level. Software would never be able to do as good a job.
- Once a DMA page transfer is done, the LCD task adjusts the DMA pointer to the next page and the process repeats until all the pages are transferred.
- The EA LCD display has 8 pages and takes just under 10ms to transfer. See the scope plot below.
- Given the EA display has 6528 pixels and we're sending them 10 times per second, the SPI bandwidth to the display is running at over 65kb/sec. With the DMA doing the bulk of the work there is almost NO load on the micro to keep the display updated at this rate! Blows me away. This kind of xfer to a LCD would choke a little Pic or Atmel chip. Doubling the the refresh rate to 20Hz would add only a slight increase in CPU load, as an example.
Step 4: Other Hardware / Software Tricks
There is some other stuff going on in the source code I've provided in this instructible that is worth noting.
Date / Time Code:
To calculate date / time I used the clib library functions. This ia a little tricky because clib uses malloc to grab chucks of memory. This causes problems for a RTOS that needs to task switch. The solution is to get clib to use the malloc functions in the OS instead of the malloc functions in the library. Luckily, some nice bloke on the Internet sorted it out and provided a solution. See Michal Demin's web site for more info.
So, the hardware uses the external 32.768 kHz crystal to keep a 32 bit counter incrementing once per second. The counter is battery backed (coin cell) and does NOT get reset EVER. Hardware resets, debugging, or reflashing does NOT reset the counter! The counter can be set through software, however. Setting the value to the number of seconds since January 1st, 1970 makes clib happy. Once set, there are a bunch of clib functions to calculate time. Note, given a date / time / timezone, clib can reverse calculate the number of seconds for you.
Once clib knows the timezone, the date / time functions are ready to go. The date / time on the LCD is good regardless of daylight savings time, leap year, or any other junk I don't understand. In effect, once set, the date / time should be good year round for years (other than drift).
To set the date / time, I uncomment a section of code (look in ctime.c near the top) that does the reverse time calculation. The 32 bit counter is updated and the program continues. Then, I comment out the date / time code that sets the time and reflash the micro. It's a nasty hack - but, I just ran out of hacking juice to write some button code to set the time / date. Feel free.
I just started to write some code to add a debug console. The idea is, take one of the unused UART channels and use it for application debug messages. The console can also be interactive so that repetitive messages can be toggled on, and off. I've used this so I can watch analog values scroll. It's nice to be able to turn this on, and off.
Features of the debug console include:
- Debug console runs as a separate task - the lowest priority task. Designed to be low impact on the system.
Other tasks can call "vDebugPrintf()" to send a formatted message out the debug serial port.
- The formatted messages are loaded into a OS FIFO queue.
- The debug task continually transfers character out of the FIFO queue and into the serial port.
- The lightweight source code for vDebugPrintf is provide so you can add your own special stuff.
Automatic RC PWM Output:
Two pins have been setup with a 50Hz PWM output. The idea is that these pins will be used to drive RC servo motors in the future. With a simple write to a micro register the duty cycle of the PWM output can be changed, A connected RC servo motor would then respond my moving to the new position.
Again, this just shows how easy it is to setup and control hardware. Generating a PWM output with micro second resolution without some hardware assist would be a major hassle. The ARM hardware makes this all to easy.
Automatic Analog to Digital Conversions:
Part of my initializing hardware includes setting up a few AtoD channels. Right now, I've got four channels setup that are using another DMA channel. After a conversion is complete, the DMA channel copies the value into a RAM buffer automatically. The analog to digital converter than moves onto the next pin automatically. The whole process repeats over and over again all without any CPU overhead.
Therefore, the RAM buffer always has the latest (most up to-date) analog to digital values. Simple use the values from the RAM buffer and know they are the most fresh. Keeping the RAM values up to date is all done without any interruption of the micro!
CPU Idle Counter:
When the OS has no work to do (all tasks are in an idle state) the OS calls another user callback function called "vApplicationIdleHook()". I programmed this function to simply increment a 32 bit counter.
After one second has passed (100 OS ticks) the value of the counter can be printed to the debug console. The counter is then reset to zero and the process repeats.
By watching the debug console printout from the idle hook we can see how much idle time there is in the OS. Kinda like watching the CPU loading graph on a PC.
Even with all that's going on in the OS, LCD data transfers, analog to digital conversions, RC PWM outputs, LED twiddling, the idle hook racks up more than 350,000 counts per second! Simply amazing there is some much horsepower left over considering how much the CPU is already doing. This baby has got some legs!
Step 5: Artsy Fartsy Floating LCD
Well, it kinda turned out OK. It does look cool at night - the super bright white LEDs really light up the wall - not bad for 20mA each. I guess it looks OK during the day too. Still, I think it's going to take a while for the wife to accept it mixed in with her fancy pants art objects.
To supply power I simply used a five volt phone charger that had a USB charging plug. That plugs right into the board's USB port and provides power. The coin cell keeps the 32 kHz crystal and timer running even in a power loss. I measured the current drain from the coin cell at under 15 micro amps.
Step 6: Final Thoughts
This instructible was intended to get you excited about using an inexpensive Arm processor. By loaded the free IDE from Atollic you get a super powerful Eclipse based development environment. The debugger baked into the IDE is really awesome. The $12 dollar Discovery board is also an awesome bargain. The STM32 Arm processor blows me away. Lastly, the FreeRTOS glues everything together and makes it a dream to program. Simply install Atollic's software, drop in my source code, and hit the compile / download button. Bang, you should be up and running!
My hope was to provide an exciting project that you can use to get your feet wet. My next step is to turn the Discovery board into a two wheel robot for kids to hack on. Stay tuned.
As always, intermediate stuff can be found on my web site: ph-elec.com