Peripheral standard library. Operation and analysis of the structure of the Main.c. file. Hello LEDs on STM32

So, we have already got up on our feet, in the sense that everything we need is connected to the conclusions of the microcontroller on the STM32VL Discovery board, we have learned to speak, in the C programming language, it would be time to create a project in the first class.

Writing a program

Once you've finished creating and setting up your project, you can start writing a real program. As is customary for all programmers, the first program written to work on a computer is a program that displays the inscription "HelloWorld", and for all microcontrollers, the first program for a microcontroller flashes an LED. We will not be an exception to this tradition and will write a program that will control the LD3 LED on the STM32VL Discovery board.

After creating an empty project in IAR, it generates minimal program code:

Now our program will always "spin" in a loop while.

In order for us to be able to control the LED, we need to enable clocking of the port to which it is connected and configure the corresponding pin of the microcontroller port to output. As we discussed earlier in the first part, for allowing port clocking FROM bit responds IOPCEN register RCC_APB2ENR... According to the document “ RM0041Referencemanual.pdf"To enable clock bus port FROM required in register RCC_APB2ENR set bit IOPCEN per unit. So that when this bit is set, we do not reset the others set in this register, we need to apply a logical addition operation (logical "OR") to the current state of the register and then write the resulting value into the contents of the register. In accordance with the structure of the ST library, access to the register value for reading and writing it is made through a pointer to the structure RCC-> APB2 ENR... Thus, recalling the material from the second part, you can write the following code that sets the bit IOPCEN in the register RCC_APB2ENR:

As you can see from the file "stm32f10x.h", the bit value IOPCEN defined as 0x00000010, which corresponds to the fourth bit ( IOPCEN) register APB2ENR and matches the value specified in the datasheet.

Now let's configure the output in the same way. 9 port FROM... To do this, we need to configure this port pin to output in push-pull mode. The register is responsible for setting the port mode for input / output GPIOC_CRH, we have already considered it in, its description is also in the section "7.2.2 Port configuration register high" of the datasheet. To configure the output to the output mode with a maximum speed of 2 MHz, it is necessary in the register GPIOC_CRH install MODE9 to one and reset the bit MODE9 to zero. Bits are responsible for setting the operating mode of the output as the main function with the push-pull output. CNF9 and CNF9 , to configure the required mode of operation, both of these bits must be cleared to zero.

Now the pin of the port to which the LED is connected is set to output, to control the LED, we need to change the state of the port pin by setting the output to a logic one. There are two ways to change the port pin state, the first is to write directly to the port status register the changed contents of the port register, just as we did the port setting. This method is not recommended due to the possibility of a situation in which an incorrect value may be written to the port register. This situation can arise if, during the change in the state of the register, from the moment when the state of the register has already been read and until the moment when the changed state is written to the register, any peripheral device or interrupt will change the state of this port. Upon completion of the operation to change the state of the register, the value will be written to the register without taking into account the changes that have occurred. Although the likelihood of this situation occurring is very low, it is still worth using another method in which the described situation is excluded. For this, there are two registers in the microcontroller GPIOx_BSRR and GPIOx_BRR... When writing a logical unit to the required bit of the register GPIOx_BRR the corresponding port pin will be reset to logical zero. Register GPIOx_BSRR can both set and reset the state of the port pins, to set the port pin to a logical unit, it is necessary to set the bits BSn, corresponding to the number of the required bit, these bits are located in the lower registers of the byte. To reset the state of the port output to logical zero, you must write the bits BRn corresponding pins, these bits are located in the most significant bits of the port register.

LD3 LED is connected to pin 9 port FROM... To turn on this LED, we need to apply a logical unit on the corresponding port pin to "light up" the LED.

Let's add the code for setting the output of the LED port to our program, and also add a software delay function to reduce the switching frequency of the LED:

// Do not forget to connect the header file with the description of the microcontroller registers

#include "stm32f10x.h"

void Delay ( void);

void Delay ( void)
{
unsigned long i;
for (i \u003d 0; i<2000000; i++);
}

//Our main function

void main ( void)
{


RCC-\u003e APB2ENR | \u003d RCC_APB2ENR_IOPCEN;

// clear the MODE9 bits (reset the MODE9_1 and MODE9_0 bits to zero)
GPIOC-\u003e CRH & \u003d ~ GPIO_CRH_MODE9;

// Set the MODE9_1 bit to configure the output to the output with a speed of 2MHz
GPIOC-\u003e CRH | \u003d GPIO_CRH_MODE9_1;

// clear the CNF bits (configure as general purpose output, balanced (push-pull))
GPIOC-\u003e CRH & \u003d ~ GPIO_CRH_CNF9;

while(1)
{

// Set pin 9 of port C to a logical unit (LED is on)
GPIOC-\u003e BSRR \u003d GPIO_BSRR_BS9;


Delay ();


GPIOC-\u003e BSRR \u003d GPIO_BSRR_BR9;


Delay ();

}
}

You can download the archive with the source code of the program written using the direct control of the microcontroller registers by following the link.

Our first workable program was written, when it was written, to work and configure the peripherals, we used data from the official datasheet " RM0041Referencemanual.pdf”, This source of information about the registers of the microcontroller is the most accurate, but in order to use it you have to re-read a lot of information, which complicates the writing of programs. To facilitate the process of configuring the peripherals of the microcontroller, there are various code generators, the official utility from the ST company is the Microxplorer program, but it is still not very functional and for this reason the third-party developers have created an alternative program “STM32 Program code generator » . This program allows you to easily get the peripheral configuration code using a convenient, intuitive graphical interface (see Fig. 2).


Figure: 2 Screenshot of STM32 code generator

As you can see from Figure 2, the code generated by the program for setting the LED output coincides with the code we wrote earlier.

To run the written program, after compiling the source code, you need to load our program into the microcontroller and see how it is executed.

LED flashing program debug video

Video of the LED blinking program on the STM32VL Discovery board

Library functions for working with peripherals

To simplify the work with setting up the registers of the microcontroller's peripherals, ST has developed libraries, thanks to the use of which, it is not necessary to read the datasheet so thoroughly, since when using these libraries, the work of writing a program will become closer to writing high-level programs, in view of the fact that everything low-level functions are implemented at the level of library functions. However, one should not completely abandon the use of direct work with the registers of the microcontroller, in view of the fact that library functions require more processor time for their execution, as a result of which their use in time-critical sections of the program is not justified. But still, in most cases, things like peripheral initialization are not critical to the runtime, and the convenience of using library functions is preferred.

Now let's write our program using the ST library. The program needs to configure the I / O ports, to use the library functions for configuring ports, you need to connect the header file " stm32f10x_gpio.h"(See Table 1). This file can be connected by uncommenting the corresponding line in the included header configuration file “ stm32f10x_conf.h". At the end of the file " stm32f10x_gpio.h»There is a list of function declarations for working with ports. A detailed description of all available functions can be found in the file “ stm32f10x_stdperiph_lib_um.chm», short description the most commonly used are shown in table 2.

Table 2: Description of Basic Port Configuration Functions

Function

Description of the function, passed and returned parameters

GPIO_DeInit (
GPIO_TypeDef * GPIOx)

Sets the values \u200b\u200bof the GPIOx port setting registers to their default values

GPIO_Init (
GPIO_TypeDef * GPIOx,

Installs the GPIOx port setting registers in accordance with the specified parameters in the GPIO_InitStruct structure

GPIO_StructInit (
GPIO_InitTypeDef * GPIO_InitStruct)

Fills in all the fields of the GPIO_InitStruct structure with default values

uint8_t GPIO_ReadInputDataBit (
GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin);

Reading the input value of the GPIO_Pin of the GPIOx port

uint16_t GPIO_ReadInputData (
GPIO_TypeDef * GPIOx)

Reading input values \u200b\u200bof all GPIOx port pins

GPIO_SetBits (
GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin)

Setting the output value of the GPIO_Pin pin of the GPIOx port to a logical unit

GPIO_ResetBits (
GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin)

Reset the output value of the GPIO_Pin pin of the GPIOx port to logical zero

GPIO_WriteBit (
GPIO_TypeDef * GPIOx,
uint16_t GPIO_Pin,
BitAction BitVal)

Writing the BitVal value to the GPIO_Pin pin of the GPIOx port

GPIO_Write (
GPIO_TypeDef * GPIOx,
uint16_t PortVal)

Writing the PortVal value to the GPIOx port

As can be seen from the description of the functions, as parameters of the port settings, etc., not many different individual parameters are passed to the function, but one structure. Structures are combined data that have some logical relationship. Unlike arrays, structures can contain data different types... In other words, a structure is a collection of different variables with different types combined into one kind of variable. The variables in this structure are called the fields of the structure, and they are accessed as follows, first the name of the structure is written, then the dot and the name of the structure field (the name of the variable in this structure) are written.

The list of variables included in the structures for the port functions is described in the same file slightly above the function description. So, for example, the structure “ GPIO_InitTypeDef"Has the following structure:

typedef struct
{

uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define * /

GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef * /

GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef * /

) GPIO_InitTypeDef;

The first field of this structure contains the variable " GPIO_ Pin»Type unsigned short, in this variable it is necessary to write flags of the numbers of the corresponding pins, for which it is supposed to make the necessary settings. You can customize several pins at once by setting several constants as a parameter using the operator bitwise OR (cm. ). Bitwise OR will "collect" all the ones from the listed constants, and the constants themselves are a mask designed for such use. Constant macros are specified in the same file below.

The second field of the structure " GPIO_InitTypeDef»Sets the maximum possible port output speed. The list of possible values \u200b\u200bfor this field is listed above:

Description of possible values:

  • GPIO_Mode_AIN - analog input (English Analog INput);
  • GPIO_Mode_IN_FLOATING - input without pull-up, dangling (English Input float) in the air
  • GPIO_Mode_IPD - Input Pull-down
  • GPIO_Mode_IPU - Input Pull-up
  • GPIO_Mode_Out_OD - output with open drain (English Output Open Drain)
  • GPIO_Mode_Out_PP - output by two states (English Output Push-Pull - back and forth)
  • GPIO_Mode_AF_OD - an open-drain output for alternate functions. Used in cases where the pin should be controlled by peripherals attached to this port pin (for example, Tx USART1 pin, etc.)
  • GPIO_Mode_AF_PP - the same, but with two states

In a similar way, you can see the structure of variables of other structures required to work with library functions.

To work with structures, just like variables, you must declare and assign them a unique name, after which you can refer to the fields of the declared structure by the name assigned to it.

// Declare the structure

/*
Before starting filling in the structure fields, it is recommended to initialize the contents of the structure with the default data, this is done in order to prevent writing incorrect data if, for some reason, not all the structure fields have been filled.

To pass the values \u200b\u200bof a structure to a function, you must put the & symbol before the structure name. This symbol tells the compiler that it is necessary to pass to the function not the values \u200b\u200bthemselves contained in the structure, but the memory address at which these values \u200b\u200bare located. This is done in order to reduce the number of necessary processor actions for copying the contents of the structure, and also allows you to save RAM. Thus, instead of passing a set of bytes contained in the structure to the function, only one containing the address of the structure will be transferred.
*/

/ * Write in the GPIO_Pin field of the GPIO_Init_struct structure the number of the port pin that we will configure further * /

GPIO_Init_struct.GPIO_Pin \u003d GPIO_Pin_9;

/ * Similarly, fill in the GPIO_Speed \u200b\u200bfield * /

/*
After we have filled in the necessary fields of the structure, this structure must be passed to a function that will make the necessary entry into the corresponding registers. In addition to the structure with the settings of this function, it is also necessary to pass the name of the port for which the settings are intended.
*/

Almost all peripherals are configured in approximately the same way, the differences are only in the parameters and commands specific to each device.

Now let's write our LED blinking program using only library functions.

// Do not forget to connect the header file with the description of the microcontroller registers

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

// declare a program delay function

void Delay ( void);

// the program delay function itself

void Delay ( void)
{
unsigned long i;
for (i \u003d 0; i<2000000; i++);
}

// Our main function

void main ( void)
{

// Enable clock bus port C
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC, ENABLE);

// Declare the structure for configuring the port
GPIO_InitTypeDef GPIO_Init_struct;

// Fill the structure with initial values
GPIO_StructInit (& GPIO_Init_struct);

/ * Write in the GPIO_Pin field of the GPIO_Init_struct structure the number of the port pin that we will configure further * /
GPIO_Init_struct.GPIO_Pin \u003d GPIO_Pin_9;

// Similarly, fill in the fields GPIO_Speed \u200b\u200band GPIO_Mode
GPIO_Init_struct.GPIO_Speed \u200b\u200b\u003d GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode \u003d GPIO_Mode_Out_PP;

// Pass the filled structure to perform actions on setting up registers
GPIO_Init (GPIOC, & GPIO_Init_struct);

// Our main infinite loop
while(1)
{
// Set pin 9 of port C to a logical unit (LED is on)
GPIO_SetBits (GPIOC, GPIO_Pin_9);

// Add a software delay to keep the LED on for a while
Delay ();

// Reset the state of pin 9 of port C to logical zero
GPIO_ResetBits (GPIOC, GPIO_Pin_9);

// Add again a software delay
Delay ();
}
}

link.

From the above example, it can be seen that the use of library functions for working with peripherals allows you to bring writing programs for a microcontroller closer to object-oriented programming, and also reduces the need for frequent access to the datasheet to read descriptions of microcontroller registers, but using library functions requires a higher knowledge of the programming language ... In view of this, for people who are not particularly familiar with programming, a simpler option for writing programs will be a way of writing programs without using library functions, with direct access to the registers of the microcontroller. For those who know the programming language well, but are poorly versed in microcontrollers, in particular STM32, the use of library functions greatly simplifies the process of writing programs.

This circumstance, as well as the fact that ST has taken care of a high degree of compatibility, both in hardware and software, of its various microcontrollers, contributes to their easier study, since there is no need to delve into the structural features of various controllers the STM32 series and allows you to choose any of the microcontrollers available in the STM32 line as a microcontroller for study.

Interrupt handler

Microcontrollers have one remarkable ability - to stop the execution of the main program at a certain event, and go on to execute a special subroutine - interrupt handler... Interrupt sources can be both external events - interrupts for receiving / transmitting data via any data transfer interface, or changing the output state, and internal - timer overflow, etc. The list of possible interrupt sources for STM32 microcontrollers is given in datasheet " RM0041 Reference manual" In chapter " 8 Interrupts and events».

Since the interrupt handler is also a function, it will be written as a normal function, but in order for the compiler to know that this function is a handler for a specific interrupt, the predefined names should be chosen as the function name, to which the interrupt vector redirections are indicated. A list of the names of these functions with a short description can be found in the assembly file " startup_stm32f10x_md_vl.s". One interrupt handler can have several sources of interrupts, for example, the interrupt handler function " USART1_IRQHandler"Can be called in case of completion of reception and end of transmission of a byte, etc.

To start working with interrupts, you need to configure and initialize the NVIC interrupt controller. In the Cortex M3 architecture, each interrupt can be assigned its own priority group for cases when several interrupts occur simultaneously. Then you should configure the interrupt source.

The NVIC_IRQChannel field indicates which interrupt we want to configure. The constant USART1_IRQn denotes the channel responsible for interrupts associated with USART1. It is defined in the file " stm32f10x.h", Other similar constants are also defined there.

The next two fields indicate the priority of the interrupts (the maximum values \u200b\u200bof these two parameters are determined by the selected priority group). The last field actually enables the use of interrupts.

Into function NVIC_Init, as well as when configuring ports, a pointer to the structure is passed to apply the settings made and write them to the corresponding registers of the microcontroller.

Now in the module settings it is necessary to set the parameters for which this module will generate an interrupt. First, you should turn on the interrupt, this is done by calling the function name_ITConfig ()which is located in the header file of the peripheral.

// Enable interrupts at the end of the byte transfer on USART1
USART_ITConfig (USART1, USART_IT_TXE, ENABLE);

// Enable interrupts at the end of receiving a byte by USART1
USART_ITConfig (USART1, USART_IT_RXNE, ENABLE);

The description of the parameters passed to the function can be found in the source code file of the peripheral device, just above the location of the function itself. This function enables or disables interrupts for various events from the specified peripheral module. When this function is executed, the microcontroller will be able to generate interrupts for the events we need.

After we get into the interrupt handling function, we need to check from which event the interrupt occurred, and then clear the cocked flag, otherwise, upon exiting the interrupt, the microcontroller will decide that we have not processed the interrupt, since the interrupt flag is still set.

Microcontrollers with a Cortex-M3 core have a dedicated system timer to perform various, small, repetitive actions with a precise period. The functions of this timer include only an interrupt call at strictly specified time intervals. Typically, in the interrupt called by this timer, code is placed to measure the duration of various processes. The declaration of the timer setting function is located in the file “ core_ cm3. h". The argument passed to the function is the number of system bus ticks between the intervals when the system timer interrupt handler is called.

SysTick_Config (clk);

Now that we have dealt with interrupts, we will rewrite our program using the system timer as a timing element. Since the timer " SysTick"Is systemic and it can be used by various functional blocks of our program, it would be reasonable to move the interrupt handling function from the system timer into a separate file, from this function call functions for each function block separately.

An example of a file "main.c" of a program for flashing an LED using an interrupt:

// We connect the header file with the description of the microcontroller registers

#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "main.h"

unsigned int LED_timer;

// A function called from the system timer interrupt handler function

void SysTick_Timer_main ( void)
{
// If the LED_timer variable has not yet reached 0,
if (LED_timer)
{
// Check its value, if it is more than 1500 turn on the LED
if (LED_timer\u003e 1500) GPIOC-\u003e BSRR \u003d GPIO_BSRR_BS9;

// otherwise, if less than or equal to 1500 then turn off
else GPIOC-\u003e BSRR \u003d GPIO_BSRR_BR9;

// Decrement the LED_timer variable
LED_timer--;
}

// If the value of the variable reaches zero, set the new value to 2000
else LED_timer \u003d 2000;
}

// Our main function

void main ( void)
{

// Enable clock bus port C
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC, ENABLE);

// Declare the structure for configuring the port
GPIO_InitTypeDef GPIO_Init_struct;

// Fill the structure with initial values
GPIO_StructInit (& GPIO_Init_struct);

/ * Write in the GPIO_Pin field of the GPIO_Init_struct structure the number of the port pin that we will configure further * /
GPIO_Init_struct.GPIO_Pin \u003d GPIO_Pin_9;

// Similarly, fill in the fields GPIO_Speed \u200b\u200band GPIO_Mode
GPIO_Init_struct.GPIO_Speed \u200b\u200b\u003d GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode \u003d GPIO_Mode_Out_PP;

// Pass the filled structure to perform actions on setting up registers
GPIO_Init (GPIOC, & GPIO_Init_struct);

// select the priority group for interrupts
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_0);

// Set up the system timer with an interval of 1ms
SysTick_Config (24000000/1000);

// Our main infinite loop
while(1)
{
// This time it is empty, all LED control occurs in interrupts
}
}

Part of the source code in the "stm32f10x_it.c" file:


#include "main.h"

/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/

void SysTick_Handler ( void)
{
SysTick_Timer_main ();
}

An example of a working draft of a LED blinking program using an interrupt can be downloaded from the link.

This concludes my story about the basics of developing programs for the STM32 microcontroller. I have provided all the information you need to be able to further self-study STM32 microcontrollers. The provided material is only a starting point, since a full description of working with microcontrollers cannot be described within the framework of any article. In addition, the study of microcontrollers without gaining practical experience is impossible, and real experience comes gradually over the years of work, experiments, with the accumulation of various software and hardware developments, as well as reading various articles and documentation on microcontrollers. But do not let this scare you, since the information provided in the article is quite enough to create your first device on a microcontroller, and you can acquire further knowledge and experience on your own, developing more and more complex and best devices and improving their skills.

I hope I was able to interest you in studying microcontrollers and developing devices on them, and my works will be useful and interesting to you.

Software required for development. In this article I will show you how to properly configure and link it. All commercial environments such as IAR EWARM or Keil uVision usually perform this integration themselves, but in our case everything will have to be configured manually, spending a lot of time on it. The advantage is that you have a chance to understand how it all works from the inside, and in the future you can flexibly customize everything for yourself. Before starting the setup, consider the structure of the environment in which we will work:

Eclipse will be used for convenient editing of function implementation files ( .c), header files ( .h) as well as assembly files ( .S). By "convenient" I mean the use of code completion, syntax highlighting, refactoring, navigation through functions and their prototypes. The files are automatically fed to the correct compilers, which generate the object code (in files .o). So far, this code does not contain absolute addresses variables and functions and therefore is not suitable for execution. The resulting object files are collected together by a linker. To know which parts of the address space to use, the builder uses a special file ( .ld), which is called a linker script. It usually contains the definition of section addresses and their sizes (code section displayed on flash, variable section displayed on RAM, etc.).

Finally, the linker generates an .elf file (Executable and Linkable Format), which contains, in addition to instructions and data, Debugging information used by the debugger. This format is not suitable for regular firmware vsprog, since this requires a more primitive memory image file (for example, Intel HEX - .hex). There is also a tool from the Sourcery CodeBench set (arm-none-eabi-objcopy) to generate it, and they integrate perfectly into eclipse using the installed ARM plugin.

To carry out the debugging itself, three programs are used:

  1. eclipse itself, which allows the programmer to "visually" use debugging, walk through the lines, move the mouse cursor over variables to view their values, and other conveniences
  2. arm-none-eabi-gdb - GDB client - debugger, which is secretly controlled by eclips (via stdin) as a reaction to the actions specified in paragraph 1. In turn, GDB connects to the OpenOCD Debug server, and all incoming commands are translated by the GDB debugger into commands that OpenOCD understands. GDB channel<-> OpenOCD is implemented over TCP.
  3. OpenOCD is a debug server that can communicate directly with the programmer. It runs in front of the client and waits for a TCP connection.

This scheme may seem very useless to you: why use the client and server separately and perform one more time translation of commands, if all this could be done with one debugger? The fact is that such an architecture theoretically makes it possible to conveniently interchange the client and the server. For example, if you need to use another programmer instead of versaloon, which will not support OpenOCD, but will support another special Debug server (for example, texane / stlink for the stlink programmer - which is in the STM32VLDiscovery debug board), then instead of starting OpenOCD you will simply run the required server and everything should work, without any additional gestures. At the same time, the opposite situation is possible: let's say you wanted to use the IAR EWARM environment together with versaloon instead of the Eclipse + CodeBench bundle. IAR has its own built-in Debug client, which will successfully connect to OpenOCD and control it, as well as receive the necessary data in response. However, all this sometimes remains only in theory, since the standards for communication between the client and the server are not strictly regulated, and in some places may differ, however, the configurations I indicated with st-link + eclipse and IAR + versaloon were successful for me.

Usually the client and the server run on the same machine and connect to the server at the address localhost: 3333(For openocd), or localhost: 4242 (for texane / stlink st-util). But no one bothers to open port 3333 or 4242 (and forward this port on the router to the external network) and your colleagues from another city will be able to connect and debug your piece of hardware. This trick is often used by embedders working on remote objects, access to which is limited.

Let's get started

Launch eclipse and select File-\u003e New-\u003e C Project, select the project type ARM Linux GCC (Sorcery G ++ Lite) and the name "stm32_ld_vl" (If you have STV32VLDiscovery, it would be more logical to name it "stm32_md_vl"):

Click Finish, minimize or close the Welcome window. So, the project is created, and the stm32_ld_vl folder should appear in your workspace. Now it needs to be filled with the necessary libraries.

As you understood from the name of the project, I will create a project for the ruler view low-density value line (LD_VL). To create a project for other microcontrollers, you must replace all files and defines in the name of which there is _LD_VL (or_ld_vl) to the ones you need, in accordance with the table:

Ruler view Designation Microcontrollers (x may vary)
Low-density value line _LD_VL STM32F100x4 STM32F100x6
Low-density _LD STM32F101x4 STM32F101x6
STM32F102x4 STM32F102x6
STM32F103x4 STM32F103x6
Medium-density value line _MD_VL STM32F100x8 STM32F100xB
Medium-density
_MD
STM32F101x8 STM32F101xB
STM32F102x8 STM32F102xB
STM32F103x8 STM32F103xB
High density Value line _HD_VL STM32F100xC STM32F100xD STM32F100xE
High density _HD STM32F101xC STM32F101xD STM32F101xE
STM32F103xC STM32F103xD STM32F103xE
XL-density _XL STM32F101xF STM32F101xG
STM32F103xF STM32F103xG
Connectivity line _CL STM32F105xx and STM32F107xx

To understand the logic of the table, you must be familiar with STM32 labeling. That is, if you have VLDiscovery, then you will have to replace everything related to _LD_VL with _MD_VL, since the STM32F100RB chip, which belongs to the Medium-density value line, is soldered in the discovery.

Adding CMSIS and STM32F10x Standard Peripherals Library to the project

CMSIS (Cortex Microcontroller Software Interface Standard) is a standardized library for working with Cortex microcontrollers, which implements the HAL (Hardware Abstraction Layer) level, that is, it allows you to abstract from the details of working with registers, finding register addresses by datasheets, etc. The library is a collection of sources in C and Asm. The core part of the library is the same for all Cortexes (be it ST, NXP, ATMEL, TI or whatever), and is developed by ARM. The other part of the library is responsible for peripherals, which are naturally different for different manufacturers. Therefore, in the end, the full library is still distributed by the manufacturer, although the core part can still be downloaded separately from the ARM website. The library contains address definitions, clock generator initialization code (conveniently configurable by define-s), and everything else that saves the programmer from manually entering into his projects the definition of addresses of all sorts of peripheral registers and the definition of the bits of the values \u200b\u200bof these registers.

But the guys from ST went further. Besides CMSIS support, they provide another library for STM32F10x called Standard Peripherals Library(SPL), which can be used in addition to CMSIS. The library provides faster and more convenient access to peripherals, and also controls (in some cases) the correctness of work with peripherals. Therefore, this library is often called a set of drivers for peripheral modules. It comes with a pack of examples categorized for different peripherals. There is also a library not only for STM32F10x, but also for other series.

You can download the entire SPL + CMSIS version 3.5 here: STM32F10x_StdPeriph_Lib_V3.5.0 or on the ST website. Unzip the archive. Create CMSIS and SPL folders in the project folder and start copying files to your project:

What to copy

Where to copy (considering
that's the project folder stm32_ld_vl)

File Description
Libraries / CMSIS / CM3 /
CoreSupport / core_cm3.c
stm32_ld_vl / CMSIS / core_cm3.c Cortex M3 core description
Libraries / CMSIS / CM3 /
CoreSupport / core_cm3.h
stm32_ld_vl / CMSIS / core_cm3.h Kernel description headers

ST / STM32F10x / system_stm32f10x.c
stm32_ld_vl / CMSIS /system_stm32f10x.c Initialization functions and
clock control
Libraries / CMSIS / CM3 / DeviceSupport /
ST / STM32F10x / system_stm32f10x.h
stm32_ld_vl / CMSIS /system_stm32f10x.h Titles to these functions
Libraries / CMSIS / CM3 / DeviceSupport /
ST / STM32F10x / stm32f10x.h
stm32_ld_vl / CMSIS /stm32f10x.h Basic description of the periphery
Libraries / CMSIS / CM3 / DeviceSupport /
ST / STM32F10x / startup / gcc_ride7 /
startup_stm32f10x_ld_vl.s
stm32_ld_vl / CMSIS /startup_stm32f10x_ld_vl.S
(!!! Attention file extension CAPITAL S)
Vector table file
interrupts and init-s on asm
Project / STM32F10x_StdPeriph_Template /
stm32f10x_conf.h
stm32_ld_vl / CMSIS / stm32f10x_conf.h Template for customization
peripheral modules

inc / *
stm32_ld_vl / SPL / inc / * SPL Headers
Libraries / STM32F10x_StdPeriph_Driver /
src / *
stm32_ld_vl / SPL / src / * SPL implementation

After copying, go to Eclipse and do Refresh in the project context menu. As a result, in the Project Explorer you should get the same structure as in the picture on the right.

You may have noticed that in the Libraries / CMSIS / CM3 / DeviceSupport / ST / STM32F10x / startup / folder there are folders for different IDEs (different IDEs use different compilers). I chose IDE Ride7 because it uses the GNU Tools for ARM Embedded compiler, which is compatible with our Sourcery CodeBench.

The entire library is configured using a preprocessor (using define-s), this will allow solving all the necessary branches at the compilation stage (or rather, even before it) and avoiding the load in the controller itself (which would be observed if the configuration was performed in RunTime). For example, all equipment is different for different rulers, and therefore in order for the library to "know" which ruler you want to use, you are asked to uncomment in the file stm32f10x.h one of the defines (corresponding to your ruler):

/ * #define STM32F10X_LD * / / *!< STM32F10X_LD: STM32 Low density devices */
/ * #define STM32F10X_LD_VL * / / *!< STM32F10X_LD_VL: STM32 Low density Value Line devices */
/ * #define STM32F10X_MD * / / *!< STM32F10X_MD: STM32 Medium density devices */

And so on...

But I do not recommend doing this. We will not touch the library files for now, but we will make the define later using the compiler settings in Eclipse. And then Eсlipse will call the compiler with the key -D STM32F10X_LD_VL, which for the preprocessor is absolutely equivalent to the situation if you uncommented "#define STM32F10X_LD_VL"... Thus, we will not change the code, as a result, if you wish, someday you will be able to take the library into a separate directory and not copy it to the folder of each new project.

Linker script

In the context menu of the project, select New-\u003e File-\u003e Other-\u003e General-\u003e File, Next. Select the root folder of the project (stm32_ld_vl). Enter the file name "stm32f100c4.ld" (or "stm32f100rb.ld" for discovery). Now copy and paste into eclipse:

ENTRY (Reset_Handler) MEMORY (FLASH (rx): ORIGIN \u003d 0x08000000, LENGTH \u003d 16K RAM (xrw): ORIGIN \u003d 0x20000000, LENGTH \u003d 4K) _estack \u003d ORIGIN (RAM) + LENGTH (RAM); MIN_HEAP_SIZE \u003d 0; MIN_STACK_SIZE \u003d 256; SECTIONS (/ * Interrupt vector table * / .isr_vector: (. \u003d ALIGN (4); KEEP (* (. Isr_vector)). \u003d ALIGN (4);)\u003e FLASH / * The program code and other data goes into FLASH * / .text: (. \u003d ALIGN (4); / * Code * / * (. text) * (. text *) / * Constants * / * (. rodata) * (. rodata *) / * ARM-\u003e Thumb and Thumb-\u003e ARM glue code * / * (. glue_7) * (. glue_7t) KEEP (* (. init)) KEEP (* (. fini)). \u003d ALIGN (4); _etext \u003d.;)\u003e FLASH. ARM.extab: (* (. ARM.extab * .gnu.linkonce.armextab. *))\u003e FLASH .ARM: (__exidx_start \u003d.; * (. ARM.exidx *) __exidx_end \u003d.;)\u003e FLASH .ARM. attributes: (* (. ARM.attributes))\u003e FLASH .preinit_array: (PROVIDE_HIDDEN (__preinit_array_start \u003d.); KEEP (* (. preinit_array *)) PROVIDE_HIDDEN (__preinit_array_end \u003d.);)\u003e FLASH: .init_array_end \u003d.);)\u003e FLASH: .init_array_end \u003d.); KEEP (* (SORT (.init_array. *))) KEEP (* (. Init_array *)) PROVIDE_HIDDEN (__init_array_end \u003d.);)\u003e FLASH .fini_array: (PROVIDE_HIDDEN (__fini_array_start \u003d *.); KEEP (.fini_array *)) KEEP (* (SORT (.fini_array. *))) PROVIDE_HIDDEN (__fini_array_end \u003d.); )\u003e FLASH _sidata \u003d.; / * Initialized data * / .data: AT (_sidata) (. \u003d ALIGN (4); _sdata \u003d.; / * Create a global symbol at data start * / * (. Data) * (. Data *). \u003d ALIGN (4); _edata \u003d.; / * Define a global symbol at data end * /)\u003e RAM / * Uninitialized data * /. \u003d ALIGN (4); .bss: (/ * This is used by the startup in order to initialize the .bss secion * / _sbss \u003d.; / * define a global symbol at bss start * / __bss_start__ \u003d _sbss; * (. bss) * (. bss *) * (COMMON). \u003d ALIGN (4); _ebss \u003d.; / * Define a global symbol at bss end * / __bss_end__ \u003d _ebss;)\u003e RAM PROVIDE (end \u003d _ebss); PROVIDE (_end \u003d _ebss); PROVIDE (__ HEAP_START \u003d _ebss); / * User_heap_stack section, used to check that there is enough RAM left * / ._user_heap_stack: (. \u003d ALIGN (4);. \u003d. + MIN_HEAP_SIZE;. \u003d. + MIN_STACK_SIZE;. \u003d ALIGN (4);)\u003e RAM / DISCARD /: (libc.a (*) libm.a (*) libgcc.a (*)))

Given l the inker script will be designed specifically for the STM32F100C4 controller (which has 16 KB of flash and 4 KB of RAM), if you have a different one, you will have to change the LENGTH parameters in the FLASH and RAM areas at the beginning of the file (for STM32F100RB, which is in Discovery: Flash 128K and RAM 8K).

We save the file.

Build Setup (C / C ++ Build)

Go to Project-\u003e Properties-\u003e C / C ++ Build-\u003e Settings-\u003e Tool Settings, and start configuring build tools:

1) Target Precessor

We choose which Cortex core the compiler will work for.

  • Processor: cortex-m3

2) ARM Sourcery Linux GCC C Compiler -\u003e Preprocessor

Add two define-a by passing them through the -D switch to the compiler.

  • STM32F10X_LD_VL - defines a ruler (I wrote about this define above)
  • USE_STDPERIPH_DRIVER - instructs the CMSIS library that it should use the SPL driver

3) ARM Sourcery Linux GCC C Compiler -\u003e Directories

Add paths to the includ libraries.

  • "$ (workspace_loc: / $ (ProjName) / CMSIS)"
  • "$ (workspace_loc: / $ (ProjName) / SPL / inc)"

Now, for example, if we write:

#include "stm32f10x.h

Then the compiler must first look for the file stm32f10x.h in the project directory (he always does this), he will not find it there and will start searching in the CMSIS folder, the path to which we indicated, well, he will find it.

4) ARM Sourcery Linux GCC C Compiler -\u003e Optimization

Turn on feature and data optimization

  • -ffunction-sections
  • -fdata-sections

As a result, all functions and data elements will be placed in separate sections, and the collector will be able to understand which sections are not used and will simply throw them out.

5) ARM Sourcery Linux GCC C Compiler -\u003e General

Add the path to our linker script: "$ (workspace_loc: / $ (ProjName) /stm32f100c4.ld)" (or whatever you call it).

And set the options:

  • Do not use standard start files - do not use standard start files.
  • Remove unused sections - remove unused sections

That's it, the setup is complete. OK.

Since the creation of the project, we have done a lot, and there are some things that Eclipse may have missed, so we need to tell him to revise the project file structure. To do this, from the context menu of the project you need to do Index -\u003e rebuild.

Hello LEDs on STM32

It's time to create the main project file: File -\u003e New -\u003e C / C ++ -\u003e Source File. Next. Source file name: main.c.

Copy and paste the following into the file:

#include "stm32f10x.h" uint8_t i \u003d 0; int main (void) (RCC-\u003e APB2ENR | \u003d RCC_APB2ENR_IOPBEN; // Enable PORTB Periph clock RCC-\u003e APB1ENR | \u003d RCC_APB1ENR_TIM2EN; // Enable TIM2 Periph clock // Disable JTAG for release LED PIN RCC-\u003e APB2ENB2 | \u003d RRCCENR AFIO-\u003e MAPR | \u003d AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // Clear PB4 and PB5 control register bits GPIOB-\u003e CRL & \u003d ~ (GPIO_CRL_MODE4 | GPIO_CRL_CNF4 | GPIO_CRL_MODE5 | GPIO_CRL_output max as PBF5) 10Mhz GPIOB-\u003e CRL | \u003d GPIO_CRL_MODE4_0 | GPIO_CRL_MODE5_0; TIM2-\u003e PSC \u003d SystemCoreClock / 1000 - 1; // 1000 tick / sec TIM2-\u003e ARR \u003d 1000; // 1 Interrupt / 1 sec TIM2-\u003e DIER | \u003d TIM_DIER_UIE; // Enable tim2 interrupt TIM2-\u003e CR1 | \u003d TIM_CR1_CEN; // Start count NVIC_EnableIRQ (TIM2_IRQn); // Enable IRQ while (1); // Infinity loop) void TIM2_IRQHandler (void) (TIM2-\u003e SR & \u003d ~ TIM_SR_UIF ; // Clean UIF Flag if (1 \u003d\u003d (i ++ & 0x1)) (GPIOB-\u003e BSRR \u003d GPIO_BSRR_BS4; // Set PB4 bit GPIOB-\u003e BSRR \u003d GPIO_BSRR_BR5; // Reset PB5 bit) else (GPIOB-\u003e BSRR \u003d GPIO_BSRR_B S5; // Set PB5 bit GPIOB-\u003e BSRR \u003d GPIO_BSRR_BR4; // Reset PB4 bit))

Although we included the SPL libraries, it was not used here. All calls to fields like RCC-\u003e APB2ENR are fully described in CMSIS.

You can run Project -\u003e Build All. If everything worked out, then the stm32_ld_vl.hex file should appear in the Debug folder of the project. It was automatically generated from elf by built-in tools. We flash the file and see how the LEDs flash with a frequency of once per second:

Vsprog -sstm32f1 -ms -oe -owf -I /home/user/workspace/stm32_ld_vl/Debug/stm32_ld_vl.hex -V "tvcc.set 3300"

Naturally, instead of / home / user / workspace / you must enter your path to the workspace.

For STM32VLDiscovery

The code is slightly different from the one I gave above for my debug shawl. The difference lies in the pins on which the LEDs "hang". If I had PB4 and PB5 on my board, then in Discovery they were PC8 and PC9.

#include "stm32f10x.h" uint8_t i \u003d 0; int main (void) (RCC-\u003e APB2ENR | \u003d RCC_APB2ENR_IOPCEN; // Enable PORTC Periph clock RCC-\u003e APB1ENR | \u003d RCC_APB1ENR_TIM2EN; // Enable TIM2 Periph clock // Clear PC8 and PC9 control register bits GPIOC-\u003e CRH & \u003d ~ (GPIO_CRH_MODE8 | GPIO_CRH_CNF8 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9); // Configure PC8 and PC9 as Push Pull output at max 10Mhz GPIOC-\u003e CRH | \u003d GPIO_CRH_MODE8_0 | GPIO_CRH_MOD2- sec - tick - PIO_CRH\u003e 1000 TIMCore TIM2-\u003e ARR \u003d 1000; // 1 Interrupt / sec (1000/100) TIM2-\u003e DIER | \u003d TIM_DIER_UIE; // Enable tim2 interrupt TIM2-\u003e CR1 | \u003d TIM_CR1_CEN; // Start count NVIC_EnableIRQ (TIM2_IRQn); // Enable IRQ while (1); // Infinity loop) void TIM2_IRQHandler (void) (TIM2-\u003e SR & \u003d ~ TIM_SR_UIF; // Clean UIF Flag if (1 \u003d\u003d (i ++ & 0x1)) (GPIOC-\u003e BSRR \u003d GPIO_BSRR_BS8 ; // Set PC8 bit GPIOC-\u003e BSRR \u003d GPIO_BSRR_BR9; // Reset PC9 bit) else (GPIOC-\u003e BSRR \u003d GPIO_BSRR_BS9; // Set PC9 bit GPIOC-\u003e BSRR \u003d GPIO_BSRR_BR8; // Reset PC8 bit))

Under Windows, you can flash the resulting hex (/workspace/stm32_md_vl/Debug/stm32_md_vl.hex) using the utility from ST.

Well, under linux, the st-flash utility. BUT!!! The utility does not hack the hex of the Intel HEX format (which is generated by default), so it is extremely important in the settings for creating a Flash image to select the binary format:

The file extension will not change (hex will remain as it was), but the file format will change. And only after that you can execute:

St-flash write v1 /home/user/workspace/stm32_md_vl/Debug/stm32_md_vl.hex 0x08000000

By the way, about the extension and format: usually binary files are labeled with the .bin extension, while Intel HEX files are named with the .hex extension. The difference in these two formats is more technical than functional: the binary format contains just bytes of instructions and data that will simply be written to the controller by the programmer "as is". IntelHEX, on the other hand, has not a binary format, but a textual one: exactly the same bytes are split into 4 bits and are represented character by character in ASCII format, and only the characters 0-9, AF are used (bin and hex are number systems with multiple bases, that is, 4 bits in bin can be represented as a single digit in hex). So the ihex format is more than 2 times the size of a regular binary file (every 4 bits are replaced by a byte + line breaks for easy reading), but it can be read in a regular text editor. Therefore, if you are going to send this file to someone, or use it in other programmers, it is advisable to rename it to stm32_md_vl.bin, so as not to mislead those who will look at its name.

So we have configured the firmware build for stm32. Next time I will tell you how

List of articles that will help to learn the STM32 microcontroller even for a beginner. Learn everything in detail with examples from LED blinking to brushless motor control. The examples use the Standard Peripheral Library (SPL).

STM32F103 test board, ST-Link programmer, and firmware for Windows and Ubuntu.

VIC (Nested vectored interrupt controller) - interrupt control module. Setting up and using interrupts. Interrupt priorities. Nested interrupts.

ADC (analog-to-digital converter). Power circuit and examples of using the ADC in various modes. Regular and Injected channels. Using ADC with DMA. Internal thermometer. Analog watchdog.

General purpose timers. Generating an interrupt at regular intervals. Measuring the time between two events.

Capturing a signal by a timer using the example of working with an ultrasonic sensor HC-SR04

Using a timer to work with an encoder.

PWM generation. LED brightness control. Servo control (servos). Sound generation.

When you just start programming microcontrollers or haven't done programming for a long time, it's not easy to understand someone else's code. Questions "What is it?" and "Where did this come from?" appear on almost every combination of letters and numbers. And the sooner the understanding of the logic "what? Why? And where?" Comes, the easier it is to study someone else's code, including examples. True, sometimes it takes more than one day to "jump through the code" and "look through the manuals".

All STM32F4xx microcontrollers have quite a lot of peripherals. Each microcontroller peripheral is assigned a specific, specific and non-relocatable memory area. Each memory area consists of memory registers, and these registers can be 8-bit, 16-bit, 32-bit, or whatever, depending on the microcontroller. In the STM32F4 microcontroller, these registers are 32-bit and each register has its own purpose and its own specific address. Nothing prevents their programs from accessing them directly by specifying the address. At what address this or that register is located and to which peripheral device it is indicated in the memory card. For STM32F4, such a memory card is available in document DM00031020.pdf, which can be found on st.com. The document is called

RM0090
Reference manual
STM32F405xx / 07xx, STM32F415xx / 17xx, STM32F42xxx and STM32F43xxx advanced ARM-based 32-bit MCUs

In chapter 2.3 Memory map on page 64 a table begins with the addresses of the register areas and their belonging to the peripheral device. In the same table, there is a link to a section with more detailed memory allocation for each peripheral.

On the left in the table, the address range is indicated, in the middle is the name of the periphery and in the last column - where there is a more detailed description of the memory allocation.

So for the GPIO general-purpose I / O ports in the memory allocation table, you can find that addresses are allocated for them starting from 0x4002 0000. The GPIOA general-purpose I / O port occupies the address range from 0x4002 000 to 0x4002 03FF. The GPIOB port occupies the address range 0x4002 400 - 0x4002 07FF. And so on.

In order to see a more detailed distribution in the range itself, you just need to follow the link.

There is also a table here, but with a memory card for the GPIO address range. According to this memory map, the first 4 bytes belong to the MODER register, the next 4 bytes belong to the OTYPER register, and so on. Register addresses are counted from the beginning of the range belonging to a specific GPIO port. That is, each GPIO register has a specific address that can be used when developing programs for the microcontroller.

But the use of register addresses for humans is inconvenient and fraught with a lot of errors. Therefore, microcontroller manufacturers create standard libraries that make it easier to work with microcontrollers. In these libraries, physical addresses are assigned their letter designation. For STM32F4xx these correspondences are specified in the file stm32f4xx.h... File stm32f4xx.h belongs to the library CMSIS and is located in the Libraries \\ CMSIS \\ ST \\ STM32F4xx \\ Include \\ folder.

Let's see how the GPIOA port is defined in the libraries. Everything else is defined similarly. It is enough to understand the principle. File stm32f4xx.hquite large and therefore it is better to use search or the possibilities that your toolchain provides.

For the GPIOA port, we find the line that mentions GPIOA_BASE

GPIOA_BASE is defined via AHB1PERIPH_BASE

AHB1PERIPH_BASE, in turn, is defined through PERIPH_BASE

And, in turn, PERIPH_BASE is defined as 0x4000 0000. If you look at the memory map of peripheral devices (in the section 2.3 Memory map on page 64), we will see this address at the very bottom of the table. The registers of the entire periphery of the STM32F4 microcontroller begin from this address. That is, PERIPH_BASE is the starting address of the entire periphery of the STM32F4xx microcontrollers in general, and the STM32F407VG microcontroller in particular ..

AHB1PERIPH_BASE is defined as the sum (PERIPH_BASE + 0x00020000). (see pictures back). This will be the address 0x4002 0000. In the memory card, the GPIO general purpose I / O ports begin at this address.

GPIOA_BASE is defined as (AHB1PERIPH_BASE + 0x0000), that is, this is the starting address of the GPIOA port register group.

Well, the GPIOA port itself is defined as a structure of registers, the placement of which in memory begins with the GPIOA_BASE address (see the line #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE).

The structure of each GPIO port is defined as type GPIO_TypeDef.

Thus, the standard libraries, in this case the file stm32f4xx.h, simply humanize machine addressing. If you see the entry GPIOA-\u003e ODR \u003d 1234, then this means that the number 1234 will be written at the address 0x40020014. GPIOA has a starting address of 0x40020000 and the ODR register has an address of 0x14 from the beginning of the range, therefore GPIOA-\u003e ODR has an address of 0x40020014.

Or for example, you do not like the GPIOA-\u003e ODR entry, then you can define #define GPIOA_ODR ((uint32_t *) 0x40020014) and get the same result by writing GPIOA_ODR \u003d 1234 ;. But how expedient is it? If you really want to enter your own designations, then it's better to just reassign the standard ones. How this is done can be seen in the file stm32f4_discovery.h For example, this is how one of the LEDs is defined there:

#define LED4_PIN GPIO_Pin_12
#define LED4_GPIO_PORT GPIOD
#define LED4_GPIO_CLK RCC_AHB1Periph_GPIOD

A more detailed description of the periphery of the ports is in stm32f4xx_gpio.h

Hello. As you remember in the last article, we configured the software package to work with STM32 microcontrollers and compiled the first program. In this post, we will get acquainted with the architecture of this board, the microcontroller and the available libraries for work.

Below is a picture of the board STM32F3 Discovery , where: 1 - MEMS sensor. 3-axis digital gyroscope L3GD20. 2 - MEMS system-in-case containing a 3-axis digital linear accelerometer and a 3-axis digital geomagnetic sensor LSM303DLHC. 4 - LD1 (PWR) - 3.3V power supply. 5 - LD2 - red / green LED. Red by default. Green means communication between ST-LINK / v2 (or V2-B) and PC. I have ST-LINK / v2-B as well as custom indication uSB port... 6.-LD3 / 10 (red), LD4 / 9 (blue), LD5 / 8 (orange) and LD6 / 7 (green). In the last entry, we were blinking the LD4 LED. 7. - Two buttons: user USER and RESET. 8. - USB USER with Mini-B connector.

9 - USB debugger / programmer ST-LINK / V2. 1 0. - Microcontroller STM32F303VCT6. 11. - External high-frequency generator 8 MHz. 12. - There should be a low-frequency generator, unfortunately not sealed. 13. - SWD - interface. 14. - Jumpers for selecting programming of external controllers or internal, in the first case, must be removed. 15 - Jumper JP3 - jumper designed to connect an ammeter to measure the controller's consumption. It is clear if it is deleted, then our pebble will not start. 16. - STM32F103C8T6 there is a debug board on it. 17. - LD3985M33R Regulator with low voltage drop and noise level, 150mA, 3.3V.

Now let's take a closer look at the architecture of the STM32F303VCT6 microcontroller. Its technical characteristics: LQFP-100 package, ARM Cortex-M4 core, maximum frequency cores 72MHz, program memory 256 Kbytes, type of program memory FLASH, RAM size 40 Kbytes SRAM, RAM 8 Kbytes, number of inputs / outputs 87, interfaces (CAN, I²C, IrDA, LIN, SPI, UART / USART, USB), peripherals (DMA, I2S, POR, PWM, WDT), ADC / DAC 4 * 12 bit / 2 * 12bit, supply voltage 2 ... 3.6 V, operating temperature –40 ... ... + 85 С. In the figure below the pinout, where we see 87 I / O ports, 45 of them are Normal I / Os (TC, TTa), 42 are 5-volt tolerant I / Os (FT, FTf) - compatible with 5 V. (on the board there are 5V pins on the right, 3.3V on the left ). Each digital I / O line can act as a common I / O line.
destination or alternative function. In the course of the progress of projects, we will consistently get acquainted with the periphery.

Consider the block diagram below. The heart is a 32-bit ARM Cortex-M4 core running up to 72 MHz. Has a built-in floating point FPU and memory protection unit MPU, built-in macro trace cells - Embedded Trace Macrocell (ETM), which can be used to monitor the execution of the main program inside the microcontroller. They are able to continuously output these observations through the ETM contacts as long as the device is running. NVIC (Nested vectored interrupt controller) - interrupt control module. TPIU (Trace Port Interface Unit). Contains memory FLASH -256 Kbytes, SRAM 40 Kbytes, RAM 8 Kbytes. A Bus matrix is \u200b\u200blocated between the core and memory, which allows devices to be connected directly. Also here we see two types of bus matrix AHB and APB, where the former is more productive and is used to connect high-speed internal components, and the latter is for peripherals (I / O devices). The controller has 4 12-bit ADCs (5Mbit / s) and a temperature sensor, 7 comparators (GP Comparator1 ... 7), 4 programmable operational amplifiers (OpAmp1 ... 4) (PGA (Programmable Gain Array)), 2 12 bit DAC channels, RTC (real time clock), two watchdog timers - independent and windowed (WinWatchdog and Ind. WDG32K), 17 general purpose and multifunction timers.

In general terms, we looked at the architecture of the controller. Now consider the available software libraries. Having made an overview, we can highlight the following: CMSIS, SPL and HAL. Let's consider each by applying in simple example flashing LED.

1). CMSIS (Cortex Microcontroller Software Interface Standard) is a standard library for Cortex®-M. Provides device support and simplifies programming interfaces. CMSIS provides consistent and simple interfaces for the core, its periphery and operating systems real time. Its use is a professional way of writing programs, because assumes direct writing to registers and, accordingly, constant reading and study of datasheets is necessary. Hardware level independent.
CMSIS includes the following components:
- CMSIS-CORE: Consistent system startup and peripheral access (Permanent system startup and peripheral access);
- CMSIS-RTOS: Deterministic Real-Time Software Execution (Deterministic execution of real-time software);
- CMSIS-DSP: Fast implementation of digital signal processing. digital processing signals);
- CMSIS-Driver: Generic peripheral interfaces for middleware and application code (General peripheral interfaces for middleware and application code);
- CMSIS-Pack: Easy access to reusable software components (Easy access to reusable software components);
- CMSIS-SVD: Consistent view to device and peripherals (consistent view of the device and peripherals);
- CMSIS-DAP: Connectivity to low-cost evaluation hardware. Debugging software.

For example, let's write a program - blink the LED. For this, we need documentation describing the registers. In my case RM0316 Reference manual STM32F303xB / C / D / E, STM32F303x6 / 8, STM32F328x8, STM32F358xC, STM32F398xE advanced ARM ® -based MCUs, as well as a description of the specific leg for which it is responsible DS9118: ARM®-based Cortex®-M4 32b MCU + FPU, up to 256KB Flash + 48KB SRAM, 4 ADCs, 2 DAC ch., 7 comp, 4 PGA, timers, 2.0-3.6 V. To begin with, we will load the port in the program. by default, everything is disabled, thereby achieving a reduced energy consumption. Open the Reference manual and look at the Reset and clock control section further on the RCC register map and see what the register is responsible for enabling IOPEEN

Let's go to the description of the timing of the periphery of this register AHB peripheral clock enable register (RCC_AHBENR), where we see that this port is under the 21st bit. Turn it on RCC-\u003e AHBENR | \u003d (1<<21) . Далее сконфигурируем регистры GPIO. Нас интересует три: GPIOE_MODER и GPIOx_ODR . C помощью них повторим программу с предыдущей статьи, затактируем PE8. Первый отвечает за конфигурацию входа выхода, выбираем 01: General purpose output mode. GPIOE->MODER | \u003d 0 × 10000. The second is for the inclusion of low / high level on the leg. Below is the program:

#include "stm32f3xx.h "// Microcontroller header file
unsigned int i;
void delay () (
for (i \u003d 0; i<500000;i++);
}
int main (void) (
RCC-\u003e AHBENR | \u003d (1<<21);
GPIOE-\u003e MODER | \u003d 0 × 10000;
while (1) (
delay ();
GPIOE-\u003e ODR | \u003d 0x100;
delay ();
GPIOE-\u003e ODR & \u003d ~ (0x100);
} }

2). SPL (Standard Peripherals Library) - this library is designed to integrate all processors from ST Electronics. Designed to simplify code portability and is primarily aimed at the novice developer. ST has been working on an SPL replacement called "low layer" that is compatible with HAL. Low Layer (LL) drivers are designed to provide an almost lightweight expert-oriented layer that is closer to the hardware than HAL. In addition to the HAL, LL APIs are also available. An example of the same program in SPL.

#include
#include
#include
#define LED GPIO_Pin_8
int main () (
long i;
GPIO_InitTypeDef gpio;
// Blue LED is connected to port E, pin 8 (AHB bus)
RCC_AHBPeriphClockCmd (RCC_AHBPeriph_GPIOE, ENABLE);
// Configure port E (LED)
GPIO_StructInit (& gpio); // declare and initialize a data structure variable
gpio.GPIO_Mode \u003d GPIO_Mode_OUT;
gpio.GPIO_Pin \u003d LED;
GPIO_Init (GPIOE, & gpio);
// Blinking LEDS
while (1) (
// On
GPIO_SetBits (GPIOE, LED);
for (i \u003d 0; i< 500000; i++);
// All off
GPIO_ResetBits (GPIOE, LED);
for (i \u003d 0; i< 500000; i++);
} }

Each function is described in the technical documentation UM1581 User manual Description of STM32F30xx / 31xx Standard Peripheral Library... Here we include three header files that contain the necessary data, structures, functions for managing reset and synchronization, as well as for configuring the I / O ports.

3). HAL- (Hardware Acess Level, Hardware Abstraction Layer) - Another common library for development. With which came the CubeMX program for the configuration, which we used in the last article. In the same place, we wrote a program for flashing an LED using this library. As you can see in the figure below, the cube generates the HAL and CMSIS drivers. Well, let's describe the main files used:
- system_stm32f3x.c and system_stm32f3x.h - provide the minimum set of functions for configuring the clocking system;
- core_cm4.h - provides access to the registers of the core and its peripherals;
- stm32f3x.h - microcontroller header file;
- startup_system32f3x.s - start code, contains a table of interrupt vectors, etc.

#include "main.h"
#include "stm32f3xx_hal.h"
void SystemClock_Config (void); / * Declare clocking configuration functions * /
static void MX_GPIO_Init (void); / * Initialize I / O * /
int main (void) (
/ * Reset of all peripherals, Initializes the Flash interface and the Systick. * /
HAL_Init ();
/ * Configure the system clock * /
SystemClock_Config ();
/ * Initialize all configured peripherals * /
MX_GPIO_Init ();
while (1) (
HAL_GPIO_TogglePin (GPIOE, GPIO_PIN_8); // Toggle the state of the leg
HAL_Delay (100); )
}
void SystemClock_Config (void){
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType \u003d RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState \u003d RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue \u003d 16;
RCC_OscInitStruct.PLL.PLLState \u003d RCC_PLL_NONE;
if (HAL_RCC_OscConfig (& RCC_OscInitStruct)! \u003d HAL_OK){

}
/ ** Initializes the CPU, AHB and APB busses clocks * /
RCC_ClkInitStruct.ClockType \u003d RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource \u003d RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider \u003d RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider \u003d RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider \u003d RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig (& RCC_ClkInitStruct, FLASH_LATENCY_0)! \u003d HAL_OK){
_Error_Handler (__FILE__, __LINE__);
}
/ ** Configure the Systick interrupt time * /
HAL_SYSTICK_Config (HAL_RCC_GetHCLKFreq () / 1000);
/ ** Configure the Systick * /
HAL_SYSTICK_CLKSourceConfig (SYSTICK_CLKSOURCE_HCLK);
/ * SysTick_IRQn interrupt configuration * /
HAL_NVIC_SetPriority (SysTick_IRQn, 0, 0);
}
/ ** Configure pins as Analog Input Output EVENT_OUT EXTI * /
static void MX_GPIO_Init (void){
GPIO_InitTypeDef GPIO_InitStruct;
/ * GPIO Ports Clock Enable * /
__HAL_RCC_GPIOE_CLK_ENABLE ();
/ * Configure GPIO pin Output Level * /
HAL_GPIO_WritePin (GPIOE, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11
| GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15, GPIO_PIN_RESET);
/ * Configure GPIO pins: PE8 PE9 PE10 PE11 PE12 PE13 PE14 PE15 * /
GPIO_InitStruct.Pin \u003d GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11
| GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode \u003d GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull \u003d GPIO_NOPULL;
GPIO_InitStruct.Speed \u200b\u200b\u003d GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init (GPIOE, & GPIO_InitStruct);
}
void _Error_Handler (char * file, int line){
while (1) (
} }
#ifdef USE_FULL_ASSERT

Void assert_failed (uint8_t * file, uint32_t line){
}
#endif
Here, as well as in the previous example, we can view the description of each function in the documentation, for example UM1786 User Manual Description of STM32F3 HAL and low-layer drivers.

We can summarize that the first option using CMSIS is less cumbersome. There is documentation for each library. In future projects, we will use HAL and CMSIS, using the STCube configuration program and, if possible, use the registers directly, without software wrappers. We will stop at this today. In the next article, we will consider the basic principles of building a smart home. Goodbye to everyone.

Did you like the article? To share with friends: