Intro: Bare Mbed MCU With Full Debug
What's in this Instructable:
In this instructable we are going to be looking at how to program a barebones arm chip via Mbed with full debugging capability! Let me warn you ahead of time, this will be accomplished with linux command line tools. While this will be difficult if you have never used linux before, I do lay out most (if not all) the commands in detail so this may be a good time to give it a shot!
I am really writing this tutorial as if my audience has experience with something like arduino and mbed and uses (or has used) some kind of linux desktop. I expect that if you have any experience using command line compilers (or even full blown IDES like atmel studio, IAR, EWARM, Keil etc) and have used linux package managers (i.e. apt-get, aptitude, yum, pacmac etc) this will be quite easy for you.
One of the things I liked most about working with arduino was how easy it was to start incorporating it onto custom applications. It was simply a matter of unplugging the chip from the socket and shoving it into a breadboard. From there you are pretty much free to play with your breadboard components until you had everything just so. Once you were happy, just solder it to some perf board, or better yet, send a custom design design to OSH Park!
Lately, however, I have been doing a lot of real time DSP (digital signal processing), and I have grown accustomed to the power of 32-bit arm chips, with the convenience of the MBed compiler. My main concern with MBed at the moment is that I have had little to no success in implementing a full debugger. In addition, MBeds "Drag & Drop" style of programming have really left me confused as to how I can program my own custom MBed PCB.
Mbed has lots of good resources available (or at least, it seems like they have started putting work into documenting some of their hardware features) in the form of the Mbed HDK (Hardware Development Kit). In it you will find both surface-mount and thru-hole designs of the circuitry used to make the Drag & Drop programming click.
This is all good. However, I really want to spend as much time as possible designing and building MY applications, not designing, building, and troubleshooting Mbeds programming hardware. And even if I pull it off, I still won't have the debug I want!
"Whats that?" it calls to me, "You want full featured arm compiler and debugger? You want it for free? Welcome aboard, we'd love to help you help yourself!"
Linux is so pretentious.
As it turns out, linux can provide all this. And the process has become much more painless in the last few years. All you will need to pull this off is:
- A Jtag debugger (there are even ways around this as I discuss later).
- Your favorite ARM or Mbed enabled chip.
- A linux computer.
This instructable is basically an over-hyped tutorial of how to use the following packages harmoniously:
- MBED CLI (optional if you don't use MBed)
I have put a fresh installation of Ubuntu 16.04 on my laptop so I can properly document how to build all of these software packages and incorporate them into your project! It has been a long time since I have posted here, but I am very excited about this project! I don't expect that many people are interested in this, and that the people who are already know it far better than I, but there seemed to be a real gap here and I find I learn best by pretending I need to explain it to someone else. So onward and upward!
Step 1: Get the Software
Install GCC for ARM
The first thing you need to do is install the GCC ARM compiler. For those who don't know, or haven't used GCC before, this is the software that takes your .c or .cpp files and turns them into hex and binary files that your ARM chip can understand.
Installing the compiler is actually quite easy on Ubuntu:
$ sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa $ sudo apt-get update $ sudo apt-get install gcc-arm-embedded
I'll tell ya, when I was a boy I remember this being much harder. It used to take all kinds of git and make sorcery to "summon-arm-toolchain". Now it is an official PPA and installed on my laptop on the first try.
Life is good.
This package also includes a special version of the GDB (GNU Project Debugger) specifically tailored for ARM programming. This is my first time using it myself, but this software essentially provides the debug capability you are used to with a full blown ARM IDE. The GDB uses its own set of commands that are actually more feature rich and easy to use than I anticipated (although perhaps not documented intuitively). For example, within GDB you can step through your ARM program and dynamically assign breakpoints as expected, but you can even call previously defined functions without having to recompile your code! This gives ARM programming an almost script like experience that I had never had before. While it takes some learning, so far it has seems worth it.
There are some graphical tools available for GDB (DDD and KDBG), but I will not go into that here.
Install Mbed CLI
The nice folks at Mbed have released their "command line interface" (CLI) that gives you almost all the mbed functionality your used to from the compiler so you can build and manage your projects locally on your machine. This is really great work and is worth checking here. They have included lots of tutorial videos and written documentation. They have laid out the programming in a very intuitive manner, starting a new mbed project from the command line is almost as easy as starting a new git project.
The MBed CLI depends on both Git and Mercurial in order to fetch the relevant build files. They can be installed via:
$ sudo apt-get install mercurial $ sudo apt-get install git
You need will need pip in order to install Mbed to your machine:
$ sudo apt-get install python-pip
If you haven't used pip before, it is simply the package manager for python programs. Now, install the mbed cli:
$ sudo pip install mbed-cli
This all built on my machine on the first try. So far this has been suspiciously easy...
Now it is time to install OpenOCD (Open On-Chip Debugger); as I understand it, OpenOCD is the layer of software the takes your generic debugging commands from GDB, and converts them into commands that both your programmer and target can understand.
I was able to install it simply by:
$ sudo apt-get install openocd
A lot of website online say that you need change some permissions in "/etc/udev/rules.d/" to give it dialout access, but that was not the case for me.
Step 2: A Quick Note About Hardware
For those of you who are fresh out of some Arduino or Mbed bootcamp, this step is for you. When programming any microcontroller, you pretty much HAVE to use another different microcontroller platform to program your new one. It is very much a chicken-and-egg situation. This is called a programmer or debugger. The programmer takes your computer USB commands and translates it into a communication protocol that YOUR microntroller can understand. This communication protocol is pretty much always JTAG (eevblog plug here) or some kind of serial protocol (SWD in our case).
It is definitely worth while to buy a full JTAG programmer, especially if you are going to be programming a large volume or variety of ARM chips.
However, I was largely motivated to complete this project because of the cheap availability of MBed enabled ARM programmers and chips available from STs "Nucleo" line. They include an SWD programmer (JTAG can be found on some Discovery boards) as well as a UART to USB bridge; so you can do a full debug and stream live data via UART at the same time, with the same hardware. Pretty cool huh? The serial streaming is must have for me as I like to do a lot post-processing and live graphing on my computer via Python or Matlab.
I have noticed a lot of free compiler support the STM32-F441RET6 mcu, a cortex m4 with mbed support. By the end of this project we will have also added full debug support. This seems like hard value to beat at just $14.
So in a nutshell, if you don't have a JTAG programmer laying around, and you don't want to spring for one, you can use the built in ST-Link in any of the STM32 Discovery or Nucleo boards as a programmer/debugger/serial monitor.
That being said, you don't have to be using ST hardware only to proceed from here, just know that all I am using to do my programming and debugging is an STLink snapped off one my old Nucleo boards.
Step 3: Configure OpenOCD
This next step is probably the most critical and most likely where people are going to steer off course. It took me a while to get things right as well. If you get through this, you are in good shape.
Its time to test if OpenOCD can communicate with your hardware setup. The easiest way to get through this is choose a debugger (called an "interface" in OpenOCD) and microntroller (likewise a "target") that is compatible with OpenOCD. Luckily, many devices are supported.
You can find a list of supported debuggers by typing:
$ ls /usr/share/openocd/scripts/interface
Here are the premade openocd configuration files that have already been written for different debugging interfaces. Lucky for me, I am using an STLink which is supported by config files "stlink-v2.cfg", "stlink-v1.cfg" and "stink-v2-1.cfg". This last one is what is compatible with the nucleo header. I have included a picture of the output above for those who haven't installed openocd yet.
Likewise, you can find a list of supported ARM chips by entering:
$ ls /usr/share/openocd/scripts/target
Each file will will not correspond to one chip, but a range of them. For example, I am programming an STM32F303K8T6. The corresponding configuration file is "stm32f3x.cfg"
There are also configuration files for assembled boards:
$ ls /usr/share/openocd/scripts/board
If you can find your hardware in these lists you should be ready to test your setup!
Running openocd essentially creates a some kind of server (telnet) your machine that can handle communication from both your computers debug commands (via GDB) and your microntrollers debugger.
Launhing openocd should be as easy as pointing it to the config files that describe your hardware.
$ openocd -f interface/<your_debugger.cfg> -f target/<your_mcu.cfg>
So for example I can simply run:
$ openocd -f interface/stinlink-v2-1.cfg -f target/stm32f3x.cfg
If everything goes smoothly the command will remain open, indicating the server is running. In addition, on the Stlink boards you will see the LED switch between green and red.
If this does not work for you, its time to recheck your wiring and that everything is plugged in correctly. After that, you should check google and see if other people have had similar problems when using your platform.
You can find exhaustive documentation for OpenOCD in the User Guide.
If this all works, its time to build and compile a program to run on your microcontroller!
Step 4: Construct and Compile an MBed Program
Now lets create a an Mbed program that we can debug. I am going to create a simple one on the command line just so we can practice going through the motions. I have, however, included a link to a git repository at the end of this step if you would rather do things that way.
Navigate somewhere where you like to work in your desktop. I created an "MBed" folder in my Documents.
$ mbed new gdb_demo $ cd mbed-os-example-blinky
It may take a few minutes; once inside this folder you should be able to navigate around inside the project.
Once you are happy with the file it is time to compile it. Compiling with Mbed is much easier than compiling with GCC. All you need to do is tell it what compiler toolchain you are using and what Mbed "Board" you are building for. It looks something like:
$ mbed compile -t <your_toolchain> -m <your_target_mcu>
Since we are using the ARM_GCC chain we just need to find out what name Mbed has given our chip. You can find a list of the supported devices by mis-entering a device name:
$ mbed compile -t GCC_ARM -m what_is_my_board?
And finally compile:
$ mbed compile -t GCC_ARM -m NUCLEO_F303RE
Almost there! Now we need to convert the .bin file that Mbed generates into a .hex. I did this with the package "srecord". The binary file is in the folder named "BUILD".
$ sudo apt-get install srecord $ cd BUILD/your_target_mcu/GCC_ARM $ srec_cat mbed-os-example-blinky.bin -binary -o mbed-os-example-blinky.hex -intel
Expertly done! Now that we have compiled a hex file, we can finally upload it to our board using OpenOCD and GDB!
Step 5: Upload the Program
Step 6: Modify Mbed for Debug
I know, I know, I know. I promised debug functionality.
Well now that we have all of our software and hardware playing nicely with eachother, lets go ahead and change the mbed configuration to support GDB debugging. Start by opening the directory "mbed-os-example-blinky" with the command line. Now lets use the Mbed CLI to export a Makefile thats compatible with our compiler:
$ mbed export -i gcc_arm
Now there should be a file named "Makefile" in that directory. This is what we will edit in order to provide the debug capability. Open up the Makefile with your favorite and add the following lines (for me I added them somewhere around line 355 and 403. These makefiles are created automatically so they could be different on different machines):
C_FLAGS += -g3
These flags tell the C compiler to track certain debugging symbols (function names, variable names, etc) that can used by GDB later.
In addition, add these flags for the C++ compiler:
CXX_FLAGS += -g3
I'll be honest, I don't know anything about compiler optimization, but this worked for me just fine.
One other thing, if you exported your project from the Mbed Compiler rather than Mbed CLI, the process is slightly different. You must add the "-g" and "-O0" flags to definites of "CC_FLAGS" and "CPPC_FLAGS".
Excellent, now back on the command line you should be able to
from the projects top directory. After its done building a directory named ".build" will be added to project. This project contains the .elf and .hex required to program and debug your project!
Edit: Sometimes I have seen the Makefile put into a seperate directory names "projects_files". You can still just modify the Makefile there, and the .build directory will be in the same directory.
Edit: Depending on the location of the Makefile, I have also seen the output go in the normal "BUILD" folder.
If I was a better man I would know how to edit the "mbed export" function to automatically include these flags in the Makefile, but alas, I have fallen short. But I have started a discussion about it on the MBed forum. It is actually pretty unclear to me what parts of MBed are open source and what parts aren't...
Step 7: The Proof
Is in the TASTING of the pudding.
My dad always emphasized the tasting aspect of that expression...
Allright, lets fire this puppy up. Open two terminals in the build file that contains all of the project outputs (.hex .elf etc).
With the first, start your openocd server:
$ openocd -f interface/your_interface.cfg -f target/your_target.cfg
Leave that running, then in the other terminal start the debugger:
Load the .elf file that contains all your debug info:
(gdb) file mbed-os-example-blinky.elf
If everything has worked correctly, you won't see a "no symbols" message from GDB and you'll be able to view the source via:
(gdb) tui enable (gdb) l main.cpp:1
You should be able to see your source code in the viewer above. Sometimes I find I have to hit the up arrow in order to get the screen to refresh.
This is actually my number one contention with the software. I don't understand why the main.cpp file is not loaded to the source code viewer after choosing the .elf file. I often have to use the list - or l - command followed by an up arrow to get it display. If anyone knows how to make this more stable, or how to set up proper OpenOCD and GDB configuration files to make this more reliable, it would be much appreciated!
If it works, how awesome is that! This is almost TOO good for a text user interface!
Now connect GDB to OpenOCD, load the new code to your chip and stop the mcu:
(gdb) target remote localhost: 3333 (gdb) load mbed-os-example-blinky.hex
(gdb) monitor reset halt
Now your chip is running the new firmware and is ready to debug. You should see the processors current position in the source code above. Go ahead and add a breakpoint to the line you are intereset in:
(gdb) breakpoint 10
Remember try using the "list" command again, or just an up arrow to refresh the source viewer.
Now if you should be able to toggle your led by using the "continue" or "c" function: