During all the training we are going to focus on preparing your skills to know several features of the microcontrollers like gpio, timers, uart, interrupts and etc. It is important to know the resources and more important to know the target, in this case, will be the ST stm32g0b1re, a 32bit microcontroller with an ARM Cortex-M0+ core, do not forget to download the following documentation: datasheet and reference manual.
Prior to anything you will need to read and install the tools from the way we work
The template
For every newbie, the best way to start is through a working out-of-the-box project. Here is my advice, create a specific directory where you gonna put all your projects, and be disciplined with all the projects you create, the last thing you want is to have directories all over your computer. For instance:
$ mkdir Workspace
$ cd Workspace
$ git clone https://github.com/ModularMX/template-g0.git myNewProject
$ code -r myNewProject
The template is basically a simple blinky led, the hello world of microcontrollers, to flash and run the program type ( do not forget the terminal shall be in your project directory ). This is the project base you will use for every program you make across the entire training.
$ make
....
arm-none-eabi-objcopy -Oihex Build/temp.elf Build/temp.hex
arm-none-eabi-objdump -S Build/temp.elf > Build/temp.lst
arm-none-eabi-size --format=berkeley Build/temp.elf
text data bss dec hex filename
2244 20 1572 3836 efc Build/temp.elf
Connect and flash your board
$ make flash
openocd -f board/st_nucleo_g0.cfg -c "program Build/temp.hex verify reset" -c shutdown
Open On-Chip Debugger 0.11.0+dev-00715-g480d4e177-dirty (2022-06-22-18:51)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : clock speed 2000 kHz
Info : STLINK V2J40M27 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.253088
Info : [stm32g0x.cpu] Cortex-M0+ r0p1 processor detected
Info : [stm32g0x.cpu] target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for stm32g0x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x0800023c msp: 0x20024000
** Programming Started **
Info : device idcode = 0x10006467 (STM32G0B/G0Cx - Rev A : 0x1000)
Info : RDP level 0 (0xAA)
Info : flash size = 512kbytes
Info : flash mode : dual-bank
Warn : Adding extra erase range, 0x080008d8 .. 0x08000fff
** Programming Finished **
** Verify Started **
** Verified OK **
** Resetting Target **
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
shutdown command invoked.
Enjoy watching your led blinking, and if you want to know more about all the target you can use with the project makefile take a look at
The msps file
Before jumping into the examples and exercises, I think it's a good idea to talk about the MspInit
functions and how they work. Essentially, any Init
function from nearly every HAL driver calls its respective HAL_xxx_MspInit
function (the GPIO_Init
is an exception). But let's look at an example with the serial port. Below, you can see an example of its initialization routine
UART_HandleTypeDef UartHandle; /*uart handler structure*/
/*uart configuration options for module USART2, 9600 baudrate,
8bits, 1 stop bit, no parity, no flow control, and 8 bit lenght */
UartHandle.Instance = USART2;
UartHandle.Init.BaudRate = 9600;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
/*init uart2 with previous paramters*/
HAL_UART_Init( &UartHandle );
The previous code looks perfectly normal, but something is missing. We didn’t specify the pins the serial port will use, nor have we written a single line about its configuration. If we look inside the HAL_UART_Init
, we will see that its respective HAL_UART_MspInit
is called almost at the beginning, or pretty much before configuring any registers. This Init
function is defined inside the HAL_UART
driver but with a weak qualifier, meaning we can overwrite the function with our own content
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
...
/* Init the low level hardware : GPIO, CLOCK */
HAL_UART_MspInit(huart);
...
}
Below is an example where we place the code necessary to configure pins A2 and A3 as Tx and Rx, and we also enable the UART and PORTA clock. You must keep in mind a few things: the pin configuration code is not mandatory to be placed in these functions, and you can define this function pretty much anywhere you like. The reason we place this kind of code here is simply to maintain structure, and that is the same reason why we place this function in the msps.c
file.
void HAL_UART_MspInit( UART_HandleTypeDef *huart )
{
GPIO_InitTypeDef GPIO_InitStruct; /*gpios init structure*/
__HAL_RCC_USART2_CLK_ENABLE(); /*enable usart2 clock*/
__HAL_RCC_GPIOA_CLK_ENABLE(); /*eneable port A clock */
/*set pin 2(tx) and pin 3(rx) from port A in altern mode usart2
the altern mode info can be found in device datasheet*/
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Alternate = GPIO_AF1_USART2;
/*apply configuration configuracion*/
HAL_GPIO_Init( GPIOA, &GPIO_InitStruct );
}
Now you know the main reason why you will see the pin initialization code in the msps.c
file in all our examples. We follow a similar approach with interrupt vectors in the ints.c
file, but that is something we will explain in its own article,
An here is a nice video introduction from ST