Introduction
In part 1, I introduced a range of commercially available development boards which would allow the newcomer to get started with 32bit ARM microcontrollers - specifically the STM32Fxxx range from ST Microelectronics.
I also highlighted a list of software tools which allow the user to develop code for these boards, from othe traditional compiler IDE, free GCC based such as CooCox CoIDE, to newer tools, such as online compilers, mbed, Espruino and simpler IDEs aimed at the newcomer, such as Arduino STM32.
There is a lot of choice available, both in terms of the hardware boards and the programming tools. It's a case of knowing what's out there - so you can choose a combination that suits your application and your programming experience.
These ARM boards fill the middle ground between Arduino and Raspberry Pi. Whilst many times more powerful than the original Arduino, they lack the GHz speed and the Linux operating system of the Raspberry Pi. Nevertheless, this gives them baremetal flexability and means that they are ideal for standalone, high speed, battery powered applications - and complement the capabilities of the Raspberry Pi.
Getting Started.
Here are some options:
Newcomer
1. Arduino STM32
If you have previously worked with Arduino, but are looking for more of a challenge or a faster microcontroller, then the transition may be made quite easily by using Arduino STM32. This is a community led project that upgrades the Arduino IDE toolchain so that it can be used to program the STM32 with "Arduino code". A sketch written for Arduino may be compiled and uploaded onto a board fitted with a STM32F103. All programming is done within the same familiar Arduino IDE.
The STM32F103 is a 72MHz microcontroller available with a range of different flash and RAM size options - all of which exceed the size of the Arduino MEGA. In comparative, standard Benchmark tests, the STM32F103 will execute code at between 4 and 7 times the speed of the Arduino mega.
However it's real advantage is the rich mix of peripherals that come with the '103.
On chip USB
12 bit ADCs
12 bit DACs
Multiple USARTs
Multiple I2C and SPI interfaces
Greater code and RAM size - for larger applications
Arduino STM32 has been tailored to suit the STM32F103 series of M3 microcontrollers - and is now a mature implementation, however it is now starting to support the M4 processors such as the '303 and '407.
2. mbed
mbed is an online compiler tool that supports ARM hardware from multiple vendors. It has comprehensive code libraries to support the various peripherals, and allows easy porting from one vendor's ARM devices to another. In terms of the STMF32 family it supports a range of Nucleo, Discovery and 3rd party boards.
From a programming point of view, mbed is similar to Arduino, but has a much cleaner, professional looking IDE. The code is compiled by an online compiler, where the binary file is generated and then forwarded to the browser as a download file. The bin file is then dragged onto the icon - which represents the target board as a flash drive, and the code is loaded and executed. It's fairly straightforward and easy to use. There are many code examples and templates to get you started and an online reference of what the various library functions do. As the compiler is cloud based, the application works on all platforms Windows, MAC and Linux.
If you want to progress away from the Arduino environment, and try out a wide range of powerful processors from many vendors - then mbed is probably the best way forwards.
3. Javascript for Microcontrollers
This is an interesting development by Espruino. It allows a few of the STM32Fxxx family of microcontrollers to be programmed using Javascript - generated from an online script editor.
Espruino produce a couple of their own target boards, the Espruino and the Pico, additionally the Javascript can be run on a variety of 3rd party boards including the Discovery F3, F4 and Nucleo 401 boards.
It is backed up with a comprehensive range of "modules" - essentially libraries that support common hardware devices - such as colour LCDs, WiFi, Ethernet controllers, and all the real world hardware that turns the microcontroller into a proper application. The list of hardware modules may be found here.
Intermediate
4. CooCox CoIDE
If you are an intermediate programmer, familiar with traditional IDE toolchains, then CoIDE from CooCox offers a free toolchain based on GCC and Eclipse. It takes a little bit of getting used to CooCox, but it forms a useful framework for developing code projects. CooCox supports ARM microcontrollers and boards from a range of vendors, and has built in extensions for flash programming the device.
5. Keil MDK-ARM Microcontroller Development Kit based on uVision 5
This is a full featured commercial tool chain from one of the principal compiler vendors. As a commercial product, it is likely to be too expensive for the amateur or hobbyist, however it is also available as a free to use, code size limited (32K) evaluation copy.
When I came to develop code on the STM32F746 BoB target board - this was the only available option. Shortly, there will be an option from mbed to support the STM32F746 Discovery platform.
Peripheral Vision
Having chosen your board, and your preferred toolchain, it's then a case of following some simple code examples in order to start exercising the hardware and peripherals. This where it starts to get a little complicated, primarily because a 32bit ARM microcontroller is a very complex piece of silicon.
The ARM core communicates with all of the on-chip peripherals via a vast array of memory mapped registers. For example a UART will have a Transmit register and a Receive register and a Status register. All of the on-chip peripherals will have several registers controlling their operation. Unlike an 8-bit processor, it's very difficult to remember what all these registers are, so ST Microelectronics provides a library of helper functions, called the Standard Peripheral Library. They do this for each of the microcontrollers in the various STM32 families. Very often the peripheral registers are given the same name from device to device - but that is not always the case. You might get code running perfectly well on a STM32F407, and then find that who ever wrote the Standard Peripheral Library for the STM32F303, used a different set of incompatible register names - doh. This is what keeps you on your toes with ARM baremetal programming.
Fortunately there is a Reference Manual for every processor, which gives all of the register names and usually code examples - you just have to keep reading, or finding specific code examples online to help you.
HAL
The Standard Peripheral Libraries are the first layer of hardware abstraction offered on the STM32 parts. About 18 months ago, STM moved towards a new system STM32 Cube. This replaced the standard peripheral libraries with a new Hardware Abstraction Layer - called HAL. HAL is designed to improve code portability and allow access to complex library resources - but if you are new to it, it is quite a challenging concept to take on board. HAL will be the subject of a future post - once I feel competent enough to describe it in detail.
Arduino removes the complexity of the underlying hardware with a series of common C++ functions, provided by their "Wiring" functions. This is also a hardware abstraction layer, which makes coding advanced hardware interactivity a lot easier for the newcomer. Arduino STM32 makes use of this "wiring" layer, to provide an Arduino-like programming environment for the STM32F103 range of processors.
Establishing a Base Camp
Every programmer, when faced with a new device, quickly wants to establish some familiar territory prior to starting the main task of coding. Having Arduino or mbed as a lingua franca is one good way - but what if your particular choice of processor is not yet supported by these?
Most of my coding is done using CooCox CoIDE - so within this environment, I wanted to establish a convenient framework to allow applications to be more easily developed.
The STM32Fxxx range of microcontrollers have a vast range of onchip peripherals. To use these effectively they first have to be configured and then enabled prior to use. To keep track of all the peripheral code, I tend to keep it in a separate module from the main code.
To illustrate the quantity of peripherals in a typical STM32F M4 application
8 to 10 Timers for frequency generation, PWM, quadrature decoding etc
4 USARTs
4 ADCs
2 DACs
3 SPI
2 I2C
Up to 80 GPIO lines
DMA
RTC
etc etc
Each of these peripherals needs a block of code to configure it - so the peripheral configuration module might have 30 such blocks of code and run to some 1500 lines.
Fortunately once this code has been written, it is relatively simple to maintain and modify. It is relatively easy to move from one STM32Fxxx family member to another.
To avoid the confusion and complexity of a modern coding project - especially handling the peripheral configuration, I developed a strategy, that would help organise the code, and one that would allow ease of porting to other processors, when they came available. I started with just two code modules to start with:
main.c
periph_config.c
main.c - as it's name suggest is where you put your main loop and application code
periph_config.c is where you put all the hardware specific, peripheral initialisation functions.
In this way, as and when you needed to add a new peripheral, it could just be added to the functions contained in periph_config.c
For example, my application requires a serial interface to a PC. So within periph_config.c, I write the function that configures USART1, in terms of baudrate, start bits, stop bits, parity etc.
void USART1_Config(void)
{
// Put the USART1 initialisation code here
}
As the application grew and I needed to configure the remaining 3 USARTS, I just had to add their configuration code to periph_config.c - for example
void USART2_Config(void)
{
// Put the USART2 initialisation code here
}
void USART3_Config(void)
{
// Put the USART3 initialisation code here
}
This process was repeated for the remaining peripherals, ADCs, DACs, I2C, SPI, GPIO, DMA etc. If a new peripheral was needed or an existing one modified - I just had to edit periph_config.c.
The next thing was to make sure that all of the right peripherals were initialised at the start of my main code. To do this I wrote an Init() function:
void Init(void)
{
USART1_Config();
USART2_Config();
USART3_Config();
// and so on for all the other peripherals.........
}
Borrowing from the SetUp() and Loop() concept from Arduino
main()
{
Init(); // All of the peripheral initialisation code is executed once here
while(1)
{
// Put you main loop code within this while loop
}
} // end of main
// Private functions used within main are placed here
Using this simple method, I had a coding environment quite similar to what I was familiar with in Arduino.
Next Time
In the next part, I will look at some of the functions which help build up a simple application.
No comments:
Post a Comment