This is about how you can build a simple ARM program using free software tools and really cheap development boards subsidized by the manufacturer. If you wish to skip my story about how I came to using it, skip to the next section.
My story
I have been programming microprocessors since 1989, "assembling" my first simple program to binary, by hand on paper during a bus trip. I had been interested in working with ARM microcontrollers for some time but intimidated by the many possible approaches to ARM development. Which processor, which development environment and whether I should use an operating system or not. The ARM processor family is large, and since ARM does not produce physical processors, there are a large number of manufacturers who produce ARM processors, from which you can buy. But which one? Where do we start?I am not really fond of large processor boards which claim to do everything. I am also not a fan of large and heavyweight development environments that claim to do everything. Both of them carry an enormous complexity that is difficult to understand, and although easy to start working with, writing your first program and build flashy
gadgets, in the long run it is difficult to understand the inner workings of the board or environment and therefore difficult to master the fundamentals. One reason is that they were built to the reasoning of the manufacturers with customers of professional programmer teams writing huge programs in mind. Not necessarily in-line with my way of thinking. Besides, you are now tied to a certain manufacturer and lose the freedom to pick the best one for the application.
The ease of entry generally comes at a price; you need to pay money or maintain a heavyweight board or development environment, tied to their updates and bug fixes, as well as pay cash to keep using beyond the trial period, or go beyond code size limitations etc.
What I wanted was something simple. A simple board that I could add to as I went along, and a simple compiler that was virtually limitless.
First of all, the compiler. GCC is one of the best compilers around. But several years back, there was no simple way of using GCC on embedded targets but compile your own compiler from scratch. You started with a GCC suite for the development PC, then compiled a version of it from source generate a compiler for the target processor, then used it to compile the libraries for the target processor etc. It was a long and tedious process, which required a lot of parameter tweaks, which if done wrong, produced a dud; a compiler which would compile code without errors, but that code was not fit to run on the target processor. You need to start over again with no clear directions of how to fix the problem. Been there, done that. if it is your paying job, you can endure it. But for hobby purposes where you use it off and on, the labour becomes old too quickly.
Then the board; there are many boards around. Some of them flashy, with every peripheral, including a HD TFT display and the kitchen sink. They come with a nice board support package to run all of those peripherals. The problem is, if you are not an expert, then you are left with complex devices which are difficult to learn how they work and you cannot modify or use the system to build what you want with it. So I consider the complex systems as short lived passion. Simpler boards which are almost like pin header adapters are the best in my opinion. They are minimal in that they provide clock, voltage regulation and perhaps some connection like USB or serial, and they bring all pins of the processor to some easy to connect header pins.
Start simple, learn it all, keep your cash too
I was fortunate to come across three things in the Internet:1. Chinese cheap suppliers
2. The open source e-book : "Discovering the STM32 Microcontroller" by
Geoffrey Brown from Indiana University: http:www.cs.indiana.edu/~geobrown/book.pdf
3. The pre-compiled GCC suite maintained by some folks at ARM.
All of what I will explain can be done in Linux or Windows. I am not a Windows user, but have supervised students who have quickly gotten it working under windows. During this endeavour, I was lucky to come across Prof. Geoffrey Brown's on-line book stated above. It follows the same approach as me, and was extremely helpful in the first steps when I started working with ARM STM32F10x microcontrollers. You should download it and read at least the first 4 chapters, and then those related to your peripherals. Very clear and concise explanations.
The following is how you can set up a development environment based on GCC (GNU Compiler Collection) and run your first program.
Downloads
Get the GCC compiler tools
From: https://launchpad.net/gcc-arm-embeddedI used the gcc-arm-none-eabi-4_9-2015q2-20150609
But the newest version can be used instead. It has support for other processors such as the STM32F4 series with hardware floating point support as well.
Download the appropriate pre-compiled binary for your operating system. The binaries are quite well behaved, in that they simply extract to a single folder, and do not spawn other files at remote directories, or modify operating system components (such as Win
registry). If you are not interested any more, simply erase the folder, and it is gone forever from your system.
Get the STM libraries
Download the STM32F10x libraries from ST microelectronics, (or, go to www.st.com and search for stsw-stm32054). The page says that the libraries have been superseded by STM32CubeF1. However, they are still available, work well and stable(CubeF1 is not ready at the time of writing, apparently). You will need to scroll down to the bottom of the page to find the offering. I used the: STM32F10x_StdPeriph_Lib_V3.5.0 in my examples.The libraries are simply extracted under a single folder, and consist of source and configuration files for the target processor that will be used when compiling your code. They include no executables for the host computer.
Install GNU Make
The GNU Make is also required. It is naturally included in *NIX systems. If your OS does not have it, (such as in Windows) the source can be downloaded from:https://www.gnu.org/software/make/
However, it is better to download a pre-built binary from:
http://gnuwin32.sourceforge.net/packages/make.htm
In some Windows distributions, a few DLL files might be missing. You can download the dependencies also from the same site. The latest pre-built binary version at the time of writing is 3.81. Do not worry much about the latest version; make has been around since 1989, and it is a really old, well behaved and stable program; any version
should do.
And the Device Programmer
A device programmer for the STM32F10x is necessary. See below for a short discussion. In some cases, you can get by without it, since the Nucleo boards provide a clever programming method which does not need any special program to be installed in the host computer.A good text editor. Gedit is popular and native in Linux. However Emacs is much better if you can invest in a short amount of time to learn it. Learn the many keyboard shortcuts, and you can write text much more efficiently than the guy next door who must reach for the arrow keys and mouse to do every which task. A colleague used to say that "Emacs is like smoking. First it makes you cough, but once you get used to it, you can never quit." There are also other people who say learning Emacs is like quitting smoking; they've done it many times... In Windows there is Notepad++, PSPad or other similar freeware that are very good.
Installation
Decide on a suitable directory in your file system, preferably somewhere under your home directory. Let's call it the development folder. I built a directory structure that looks like:ARM/
Compiler/
Compiler directory/
Library directory/
Code/
My code/
Documents/
Various collected and created information.
Any naming will do, as long as you keep track of what you put where. There is no magic structure you must obey. This is the nice thing about building your tools!
Install/extract all the files.
First, add 'make' to the PATH environment variable (search how to add to path for your OS) and test that it works in a command shell:
$ make
It should respond:
make: *** No targets specified and no makefile found. Stop.
Correct any errors.
Then, setup GCC. You should add the GCC compiler to your PATH variable so that any program is aware of its presence. Alternatively, you can explicitly specify its location to GNU Make. I recommend adding GCC to PATH. When done, test 'gcc' by typing in a command shell:
$ arm-none-eabi-gcc --version
And the response should be:
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors) 4.9.3 20150529
(release) [ARM/embedded-4_9-branch revision 224288]
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Of course the version number may be different. If there is any "not found" error, you must correct it now.Extract the library to the appropriate location. There is no simple way to test the success, except going into the directory and seeing that the files have actually been created.
A command shell is also necessary. In Linux, there is already a powerful command shell that you can use. In Windows there are two choices. You can either use the standard command shell by selecting "Run" from the main menu, and typing 'cmd'. However, I recommend using Windows PowerShell which is much more, er, powerful, and actually
carries much of Bash functionality from Unix systems. It is not well known, but a standard part of the distribution. See Wikipedia entry or other sources on using Windows PowerShell.
At this point, you have a working toolset to compile code for many ARM Cortex processors. Check which ones your GCC supports by the following command:
$ arm-none-eabi-gcc -print-multi-lib
The processor board
There are obviously many boards out there. Since I like the simple boards, I used this one from China:The board that I used when starting my development. |
It can be found at DealExtreme.com, here, costing about $6.5. However, I think that it is better to buy something more common, so the STM NUCLEO-F103RB is probably a better choice. This post is based around the Nucleo board; it is more readily available, and has a debugger hardware on-board.
STM32 Nucleo board with STM32F103RB |
Compiling your first project
The next step is to compile a sample project, and download it to your target processor board. First download the example code that I wrote for the STM32F103 Nucleo board, to a suitable location in the development folder.Download the source to a suitable directory.
The first thing is to modify the supplied Makefile to match your particular GCC and STM32F10x peripheral library installations. Open the Makefile with a text editor. Do not double click on the source files and end up opening them in Visual C or similar GUI; we have nothing to do with Visual C! In Makefile, edit the definitions: "TOOLROOT" and "LIBROOT", to suit your installation. Find the lines where it says:
TOOLROOT=.....
LIBROOT=......
Modify, save, and that should be enough.Next open a command shell and navigate to the project source directory. Type:
$ make
Much text will fly by, ending with the last command of the Makefile:
arm-none-eabi-objcopy -O binary Blink32F103_Nucleo-Sample.elf Blink32F103_Nucleo-Sample.bin
Unix has a "No news is good news" policy. If it does not complain, there is nothing wrong. Make has the same policy regardless of it running on Linux or Windows. If there is an error, make will stop with either an 'error' message, or something like:
make: *** No rule to make target `main.o', needed by `Blink32F103_Nucleo-Sample.elf'. Stop.
If it all goes right, you will end up with three files:
Blink32F103_Nucleo-Sample.bin
Blink32F103_Nucleo-Sample.elf
Blink32F103_Nucleo-Sample.map
The file name will be the same as the name of the directory that you created for the project, but the extensions will be the same as those shown. We will use the first one: 'Blink32F103_Nucleo-Sample.bin' to burn into the processor. The .elf file is to be used with the GDB debugger, and the .map file shows the memory map of the project; where each function is located, where each variable is located etc.
How to burn into the processor
At this point it is necessary to use a device programmer hardware. Luckily, there is one already attached to the Nucleo boards, the STLINK V2. The software to communicate with it from Windows is the ST-Link utility, a lightweight freeware program from ST Microelectronics, which works well. It can be downloaded from www.st.com and search for "STSW-LINK004".For Linux, there is the free software "ST-link" it can directly interface with GDB also.
However, I like to use an even simpler and lighter program, "stm32flash". It is extremely simple to use. However, you need to connect a USB-serial(TTL) converter to the UART1 RX and TX pins physically, and pull BOOT0 pin to ground going out of reset. I like stm32flash a lot because of its simplicity so I use it.
The final and probably easiest way of programming a Nucleo board is to use its clever user interface. When you connect the Nucleo to your host computer, it is recognized as a USB mass storage device (AKA usb thumb drive). Simply drag and drop your executable binary file (<filename>.bin extension) into the drive, and it will be programmed into the flash. Easy as that!
At this point, perhaps after a reset, the green LED labelled LD2 should start to blink once a second, in a rather comforting way. Good job.
How the project is made up
Once you can compile and write your code into the microcontroller, and see the LED blinking, you can sit back and relax! You can start to build more projects quite easily now. Now let's look into the structure of the "project". It is made of the following files and their role:
Makefile
This is the orchestra conductor. It directs the compiler and the linker about your options, the paths of the libraries, what steps to take to obtain the .bin files from the .c files, which source files are needed in the project and many more things.
The most important thing you need to put in it are the names of the source files that will be needed to create the binary executable. That is stated in the lines that read: "OBJS= main.o" and "OBJS += stm32f10x_gpio.o stm32f10x_rcc.o". It is you who should know what to put there. But the examples make it relatively easy to figure that out. "main.o" must obviously be there. If you are using any GPIO pins, "stm32f10x_gpio.o" library file must be there and so on. See the header file inclusions
in "main.c" for a hint. Note that we are not putting in the names of the '.c' source files, but the names of the object files. It does not need to be like that, but this Makefile was written in that way. The rest of the Makefile can be copied over from project to project with the stated modifications, until you know better.
The most important thing you need to put in it are the names of the source files that will be needed to create the binary executable. That is stated in the lines that read: "OBJS= main.o" and "OBJS += stm32f10x_gpio.o stm32f10x_rcc.o". It is you who should know what to put there. But the examples make it relatively easy to figure that out. "main.o" must obviously be there. If you are using any GPIO pins, "stm32f10x_gpio.o" library file must be there and so on. See the header file inclusions
in "main.c" for a hint. Note that we are not putting in the names of the '.c' source files, but the names of the object files. It does not need to be like that, but this Makefile was written in that way. The rest of the Makefile can be copied over from project to project with the stated modifications, until you know better.
main.c
This is obviously the meat of the matter. It is originally taken from the eaxmples folder in the STM32F10x libraries and Geoffrey Brown's book. Please read it through so that you understand what is going on.
stm32f103.ld
The linker script. We are getting into the nuts and bolts of how a program is compiled here. When you prepare the "main.c" file, you will reference some functions and not define them (in standard 'C', the first program that you write, helloworld.c, generally contains the function "printf()". Where is it defined?). Such functions are defined in a library. The library file that they are defined is specified in the Makefile (remember the "OBJS += stm32f10x_gpio.o" line in the Makefile?). After all the '.c' files are compiled into separate object files '.o', the function references are still not in one place. The main job of the linker (called 'arm-none-eabi-ld' in GCC, or just 'ld') is to look into each object file, find the function calls that are not defined in that file, try to match the definition of the function in the other specified object files, and "link the call with the definition" (that being why it is called a linker).
But the file "stm32f103.ld" was never mentioned in this discussion? The second
major job of the linker is to place the data, variables, functions etc. in the proper positions of the processor memory. This script describes the processor memory to the linker. It defines how much RAM and programmable ROM memory the processor has, where they are located in the memory map; which locations must the code (called 'text') be written, which locations the uninitialized variables (.bss), constants (.rodata) and initialized variables (.data) etc. be placed. If you move on to another processor, the linker script must be modified to reflect the memory structure of the new processor, otherwise the program will not work. It is worth looking through the linker script file to get acquainted with it.
But the file "stm32f103.ld" was never mentioned in this discussion? The second
major job of the linker is to place the data, variables, functions etc. in the proper positions of the processor memory. This script describes the processor memory to the linker. It defines how much RAM and programmable ROM memory the processor has, where they are located in the memory map; which locations must the code (called 'text') be written, which locations the uninitialized variables (.bss), constants (.rodata) and initialized variables (.data) etc. be placed. If you move on to another processor, the linker script must be modified to reflect the memory structure of the new processor, otherwise the program will not work. It is worth looking through the linker script file to get acquainted with it.
stm32f10x_conf.h
This is a configuration file for the processor. Not used extensively in this application.startup_stm32f10x.c
This is one of the unsung heros of all 'C' programs; the startup program. It sets up the processor. See the function void Reset_Handler(void) which initializes the initialized data (declarations like int x=5;), calls the SystemInit() function from the library which, among other things, initializes and declares the system clock frequency, and finally branches into our famous 'main()' function. "main" is no magic function name, but something simply defined as such in the startup program. In the second part of the file are the declarations of the interrupt handlers, which are defined as weak aliases to dummy functions. When you declare your own interrupt handler in your source code, they will override the original definitions.
So this is all that makes a "project" for plain GCC. The beauty of this is that all the components are there in plain sight. None of the file names and even extensions are magic values. You can modify anything as you wish, and structure your programs to your requirements and taste. You do not need to follow complicated directory structures created by commercial development environment manufacturers, aimed mostly at development of large programs, which intimidate our simple blinking lights programs. However, this simple looking setup has unlimited potential, in which it can be extended to work with the most complicated of projects. Indeed many commercial development environments are nothing more than "nice" frontends to this very same GCC setup that we have built up!
So this is all that makes a "project" for plain GCC. The beauty of this is that all the components are there in plain sight. None of the file names and even extensions are magic values. You can modify anything as you wish, and structure your programs to your requirements and taste. You do not need to follow complicated directory structures created by commercial development environment manufacturers, aimed mostly at development of large programs, which intimidate our simple blinking lights programs. However, this simple looking setup has unlimited potential, in which it can be extended to work with the most complicated of projects. Indeed many commercial development environments are nothing more than "nice" frontends to this very same GCC setup that we have built up!
No comments:
Post a Comment