The method we deployed in Chapter 1, A Practical Introduction to ARM® CORTEX® used
Startup.c
to provide a very basic Run Time Environment (RTE), and although this is sufficient to get started blinking LEDs, we need to define a more advanced RTE to take advantage of the other peripherals we'll meet in future recipes. The Application Programmers Interface (API) that STMicroelectronics (STMicro) provide for their microcontrollers is called a hardware abstraction layer (HAL), and CMSIS v2.0 compliant programs must configure this before initializing their peripherals. The RTE manager offers two routes named Classic and STM32CubeMX to configure the HAL. Selecting STM32CubeMX invokes a graphical tool developed by STMicro (freely available at www.st.com) that creates the RTE (that is, generates RTE.h
and imports the associated libraries). We describe this process in Chapter 9, Embedded Toolchain. Since we're already familiar with the Classic API, we'll continue to use this, and simply add a few lines of code to configure the HAL.
For configuring the HAL follow the steps outlined:
helloBlinky_c1v1
which we created in Chapter 1, A Practical Introduction to ARM® CORTEX®, Understanding the simple use of GPIO and name it helloBlinky_c2v0
.#include "cmsis_os.h"
void SystemClock_Config(void)
in the file helloBlinky.c
.CMSIS-RTOS Blinky
):#ifdef __RTX extern uint32_t os_time; uint32_t HAL_GetTick(void) { return os_time; } #endif
SystemClock_Config ( )
from the example project CMSIS-RTOS Blinky
, and paste this into the file helloBlinky.c
.HAL_Init ( )
and SystemClock_Config ( )
at the beginning of main()
. Our source code file helloBlinky.c
should now appear as follows:#include "stm32f4xx_hal.h" #include "Board_LED.h" #include "cmsis_os.h" /* Function Prototype */ void SystemClock_Config(void); #ifdef __RTX extern uint32_t os_time; uint32_t HAL_GetTick(void) { return os_time; } #endif /** * System Clock Configuration */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; /* Enable Power Control clock */ __HAL_RCC_PWR_CLK_ENABLE(); /* The voltage scaling allows optimizing the power consumption when the device is clocked below the maximum system frequency (see datasheet). */ __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /* Enable HSE Oscillator and activate PLL with HSE as source */ RCC_OscInitStruct.OscillatorType =RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 25; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; HAL_RCC_OscConfig(&RCC_OscInitStruct); /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); } /** * Main function */ int main (void) { const unsigned int Off_Code = 0x0000; const unsigned int On_Code = 0x00FF; unsigned int i; HAL_Init ( ); /* Init Hardware Abstraction Layer */ SystemClock_Config ( ); /* Config Clocks */ LED_Initialize ( ); /* LED Init */ // etc... }
Notice that the code executes about 10 times faster than the recipe of Chapter 1, A Practical Introduction to ARM® CORTEX®. Try commenting out the call SystemClock_Config ( )
in main ( )
by placing //
immediately before the statement. Rebuild and run. Compare the execution speed of the two versions.
The function SystemClock_Config ( )
comprehensively configures the clock tree shown in Figure 16 of STMicro's reference manual RM0090 (www.st.com). It selects the Phase Locked
Loop (PLL) clock derived from the 25 MHz crystal controlled HSE clock as the System Clock, and configures the multiplier N = 336 and dividers P = 2 and M = 25. The system clock frequency is given by:
The configuration values are held in two data structures (structs) called RCC_OscInitStruct
and RCC_ClkInitStruct
.
As we will see later in the chapter, functions may be declared implicitly by the function definition or explicitly by a function prototype. Function prototypes are considered to be preferable, and these are often declared in header files (for example, see Board_LED.h
). So, in case we've given a prototype declaration first,
Structs just identify the arrangements of data in memory. We will discuss structs later once we've dealt with more basic data types such as integers.
Finally, the following section of code:
#include "cmsis_os.h" #ifdef __RTX extern uint32_t os_time; uint32_t HAL_GetTick(void) { return os_time; } #endif
It isn't strictly necessary for a program that only uses GPIO, but subsequent recipes using other peripherals need it. So, to avoid illustrating the configuration each time, we'll assume this boilerplate is included in all future recipes.
Lastly, we've called our source code file helloBlinky.c
. This is the same name we gave the project. By convention, this indicates that this source code file contains the main()
function.
18.225.254.192