Abstract: From the STM32 new project, compile and download procedures, let the novice from shallow to deep, enjoy the fun of STM32 standard library development.

Since the appearance of CubeMX and other image configuration software, students often click a few mouse to solve the configuration of the single-chip computer. It is reasonable and efficient to use fast configuration software in business scenarios that pursue development speed, but it is more important to know what is and why it is in students’ learning scenarios.

Here are three important things to learn about embedded, including but not limited to,

1, learn how to refer to the official manual and official code to write their own programs.

2. Accumulate common code segments and know which codes are needed to deal with the problems.

Follow the big guy, step by step.

First of all, we all know that programming is generally referred to the “Reference Manual”, and for chip selection or chip data, refer to the “data manual”. In addition, all books on STM32 are based on the first two (+Cortex Kernel Manuals).

Second: to distinguish what is the kernel peripheral and peripheral outside the kernel, in order to distinguish, according to a saying on the Internet, “peripheral outside the kernel” to “processor peripheral” instead.

Also: standard libraries are rarely used nowadays, all are HAL libraries, but as a way of teaching in colleges and universities,

We will give an overview of standard library development using STM32f10xxx as an example.

1. STM32 system structure

STM32f10xxx System architecture

The kernel IP

The Cortex-M3 has several internal bus interfaces that enable CM3 to address and access memory at the same time: the instruction store bus (two), the system bus, and the private peripheral bus. There are two Code store buses responsible for access to the Code store (FLASH peripherals), the I-Code bus and the D-Code bus.

I-code for finger fetching and D-code for table lookup are optimized for optimal execution speed.

The System bus (System) is used to access memory and peripherals, covering areas including SRAM, on-chip peripherals, off-chip RAM, off-chip extenders, and a portion of the system-level storage area.

The private peripheral bus is responsible for accessing some private peripherals, mainly debugging components. They are also in system-level storage.

There is also an MDA bus. Literally, DMA stands for Data Memory Access, a bridge between the core and peripherals. It can access peripherals, memory, transfer independent of the CPU, and two-way communication. In short, this guy is a fast data handler who is not controlled by the boss.

Processor peripherals (peripherals outside the kernel)

From the structural block diagram, STM32 is equipped with serial port, timer, IO port, FSMC, SDIO, SPI, I2C and so on. These peripherals are mounted to AHB, APB2 and APB1 respectively according to different speeds.

2. Register

What is a register? Register is built into each IP peripherals, is a memory used to configure the function of the peripherals, and has the corresponding address. The encapsulation of all libraries begins with mapping.

If register development is not “smelly and long”, it is inefficient and error-prone to demap addresses and assign bytes to registers.

Come on, just kidding.

You may have heard of the International C Language Garble Competition (IOCCC).

#include <stdio.h> main(t,_,a)char *a; {return! 0<t? t<3? The main (a + - 79-13, the main (- 87, 1 - _, the main (86, 0, a + 1) + a)) : 1, t < _? main(t+1,_,a):3,main(-94,-27+t,a)&&t==2? _ < 13? main(2,_+1,"%s %d %d\n"):9:16:t<0? t<-72? main(_,t, "@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\ ; #q#n+,/+k#; *+,/'r :'d*'3,}{w+K w'K:'+}e#'; dq#'l \ q#'+d'K#! /+k#; q#'r}eKK#}w'r}eKK{nl]'/#; #q#n'){)#}w'){){nl]'/+#n'; d}rw' i; # \ ){nl]! /n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{; [{nl]'/w#q#n'wk nw' \ iwk{KK{nl]! /w{%'l##w#' i; :{nl]'/*{q#'ld; r'}{nlwb! /*de}'c \ ;; {nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+; #'rdq#w! nr'/ ') }+}{rl#'{n' ')# \ }'+}##(!! /") :t<-50? _==*a? putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0<t? The main (2, 2, "% s") : * a = = '/' | | main (0, the main (- 61, * a, "! ek; dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O; m.vpbks,fxntdCeghiry"),a+1); }Copy the code

Libraries exist to solve this kind of problem, to semantic code. Semantic thinking is not only embedded, front-end code is also pursuing semantic features.

All things begin with a lamp

(1) Analysis of kernel library files

  • cor_cm3.h

This header file implements: 1, the kernel structure register definition, 2, kernel register memory mapping, 3, memory register bit definition. Like the processor-specific header file stm32f10x.h, one is for the kernel register and one is for outside the kernel, that is, for the processor register.

  • misc.h

Kernel application library header file, corresponding to stm32f10x_XXX.h

  • misc.c

Kernel application function library file, corresponding to STM32f10X_XXX.c. In the CM3 kernel there are some functional components, such as NVIC, SCB, ITM, MPU, CoreDebug, CM3 with a very rich functional components, but chip manufacturers in the design of MCU have some are not necessarily necessary, can be tailored, For example, MPU and ITM are not found in STM32. Among them, NVIC can be found in the microcontroller of each CM3 kernel, but it can only be a subset of CM3 NVIC. There is also a SysTick in NVIC, which is a system timer. It can be used for the time base and generally used for the operating system timer. The two files, misc.h and mics. C, provide functions to operate these components and can be directly ported to CM3 kernel microcontrollers.

(2) Processor peripheral library file analysis

  • startup_stm32f10x_hd.s

This is the startup file compiled by assembly, is the first program of STM32 power-on startup, the startup file mainly realizes: 1, initialization stack pointer SP; 2. Set PC pointer to Reset_Handler; Stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c = stytem_stm32f10x.c 5. Jump to the label _main and finally go to the world of C.

  • system_stm32f10x.c

The function of this file is to implement a variety of commonly used system clock setting functions, there are 72M, 56M, 48, 36,24,8 M, we use is to set the system clock to 72M.

  • Stm32f10x.h

This header file is very important. This header file implements: 1, the structure definition of the processor peripheral register 2, the memory mapping of the processor peripheral 3, the bit definition of the processor peripheral register.

For 1 and 2 we explained when we used registers to light the LED.

Where 3: processor peripheral register bit definition, this is very important, what is the specific meaning? We know there are many bits a register, each a write 1 or 0 of the function is different, the processor peripheral registers a definition is write peripherals every bits of each register 1 hexadecimal number is defined as a macro, macro name with the name of the who, said, if we operate the register to open a certain function, So instead of trying to figure out what the value is, you can just look it up in this header file.

Let’s take the on-chip peripheral ADC as an example. Suppose we want to start the ADC conversion. According to the manual, we know that we want to control the bit 0: ADON of the REGISTER ADC_CR2, that is, write 1 to bit 0, that is:

ADC->CR2=0x00000001;
Copy the code

That’s the general way to do it. Now this header contains the bit definitions for the ADON bits:

 #define ADC_CR2_ADON ((uint32_t)0x00000001)
Copy the code

With this bit definition, the code we just wrote becomes:

ADC->CR2=ADC_CR2_ADON
Copy the code
  • stm32f10x_xxx.h

Peripheral XXX application function library header file, which mainly defines the structure of a function of the peripheral, such as the universal timer has a lot of functions, timing function, output comparison function, input capture function, and the universal timer has a lot of registers to achieve a function, such as timing function, We don’t know exactly what registers we’re going to operate on, so this header file is packed up for us and the registers that are going to perform a function are defined in the form of a mechanism, like a universal timer that’s going to perform a timing function, We only need to initialize the TIM_TimeBaseInitTypeDef member of the structure, which is the register that the timing needs to operate on. With this header file, we know which registers we need to manipulate to implement a function, and then refer back to the manual for details of these registers.

  • stm32f10x_xxx.c

Stm32f10x_xxx. c: peripheral XXX application function library, which write good operation of XXX peripheral all commonly used functions, we use library programming, the use of the most is here function.

(3) the SystemInit

Create main.c in the project.

Compiling the main function directly from this file will cause an error:

Undefined symbol SystemInit (referred from startup_stm32f10x_hd.o).
Copy the code

The error says SystemInit is not defined. We know from analyzing the startup file startup_stm32f10X_hD.s,

1; Reset handler 2 Reset_Handler PROC 3 EXPORT Reset_Handler [WEAK] 4 IMPORT __main 5 ; IMPORT SystemInit 6 ; LDR R0, =SystemInit 7 BLX R0 8 LDR R0, =__main 9 BX R0 10 ENDPCopy the code

In the assembly; A semicolon means comment

Line 5 and line 6 Reset_Handler calls SystemInit, which is used to initialize the system clock in the library file system_stm32f10x.c. We could write a new function to do the whole thing, but for simplicity, we’ll define an empty SystemInit function in our main file to fool the compiler and get rid of this error. About the configuration of the system clock will be out of the RCC clock tree details, the main configuration of the clock control register (RCC_CR) and clock configuration register (RCC_CFGR) these two registers, but it is best to directly use CubeMX directly generated, because it is a bit lengthy configuration process.

If we are using a library, there is a library function SystemInit that will set the system clock to 72M for us.

Now that we’re not using the library, what’s the clock now? The answer is 8M, when the external HSE is not enabled or occurs

During the barrier, the system clock was provided by the internal low-speed clock LSI. Now we have not enabled HSE, so the default system clock is LSI=8M.

(4) Library packaging level

As shown, reaching level 4 is what we know as the firmware library or HAL library. There are, of course, many more considerations to consider when writing a library. We need to understand the general process of library encapsulation.

The library packaging level is divided into four levels to introduce a sense of hierarchy, just like the upgrade of fighting monsters, cognitive understanding of the upgrade.

As we all know, there are three steps to manipulating GPIO output:

Clock control:

STM32 has many peripherals. To reduce power consumption, each peripherals has a corresponding clock when the system is reset

These clocks are turned off and must be turned on in order for the peripheral to work.

All peripherals of THE STM32 clock are managed by a special peripheral called RCC (Reset and ClockControl), which is in chapter 6 of the STM32 reference manual.

The peripherals of STM32 are mounted to three main systems: AHB, APB2, AND APB1. AHB is high-speed bus, APB2 is second, and APB1 is third. All IO ports are mounted to APB2 bus, belonging to high speed

Peripherals.

Mode configuration:

This is controlled by the port configuration register. The interface configuration registers are divided into high and low registers, and each 4 bits controls an I/O interface. Therefore, the low register is configured on the interface: CRL controls the lower 8 bits of the I/O interface. The high register is configured on the interface: CRH controls the higher 8 bits of the I/O interface. In a group of four control bits, CNFy[1:0] is used to control the input and output of the port, and MODEy[1:0] is used to control the rate of the output mode, also known as the response speed of the drive circuit. Note that the rate here has nothing to do with the program, see the article for details: [Embedded] GPIO pin speed, flip speed, output speed difference input has 4 modes, output has 4 modes, we choose the general push-pull output when controlling LED. There are three output speed modes: 2M, 10M, and 50M. In this case, 2M is selected.

Level control:

STM32 IO port is more complex, if you want to Output 1 and 0, you need to control: port Output data register ODR to achieve, ODR is: Output data register abbreviation, in STM32, its register name is English shorthand, easy to remember. From the manual we know that the ODR is a 32-bit register, valid 16 bits lower and reserved 16 bits higher. The lower 16 bits correspond to IO0 to IO16. As long as 0 or 1 is written to the corresponding position, the low or high level can be output.

Level 1: Base address macro definition

Clock control:

In STM32, each peripheral has a starting address, called the peripheral base address, and the peripheral registers are arranged according to this base address in order of 32 bits each. Table lookup shows that the clock is controlled by the APB2 peripheral clock enable register (RCC_APB2ENR), where the PB port clock is enabled by bit 3 of this register. We can calculate the address of RCC_APB2ENR by base address + offset 0x18:0x40021018. Then the clock code to enable the PB port is as follows:

# define RCC_APB2ENR * (volatile unsigned long *) 0 x40021018 / / open port B clock RCC_APB2ENR | = 1 < < 3;Copy the code

Mode configuration:

Like RCC_APB2ENR, the starting address of GPIOB is 0X4001 0C00. We can also calculate the address of GPIO_CRL as 0x40010C00. Then the code that sets PB0 as a universal push-pull output with an output rate of 2M is as follows:

GPIOB_ODR register address: 0X4001 0C00 + 0X0C = 0X4001 0C0C Now we can define the GPIOB_ODR register as follows:

#define GPIOB_ODR *(Volatile unsigned Long *)0x40010C0C //PB0 GPIOB_ODR = 0<<0;Copy the code

Level 1: Base address macro defines the complete code for controlling an LED with STM32:

1 #define RCC_APB2ENR *(volatile unsigned long *)0x40021018 2 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00 3 # define GPIOB_ODR * (volatile unsigned long *) 0 x40010c0c 45 int main (void) 6 {7 / / open port B clock 8 RCC_APB2ENR | = 1 < < 3; 9 10 / / configuration PB0 as general push-pull output mode, the rate of 2 m 11 GPIOB_CRL = (2 < < 0) | (0 < < 2); PB0 output low level, light LED 14 GPIOB_ODR = 0<<0; 15 } 16 17 void SystemInit(void) 18 { 19 }Copy the code

Level 2: Base address macros define struct encapsulation

Peripheral register structure encapsulation

When we operate on registers above, we operate on the absolute address of the register, which would be very troublesome if we operate on every register like this. We consider that the address of the peripheral registers is based on the offset address of the peripheral base address, and is incremented consecutively at the peripheral base address, each register is 32 or 16 bytes long, similar to the way the members of the structure are. So we can define a kind of peripheral structure, the address of the structure is equal to the base address of the peripheral, the members of the structure are equal to the register, the order of the members is the same as the order of the register. In this way, when we operate registers, we do not need to find the absolute address every time. As long as we know the base address of the peripheral, we can operate all registers of the peripheral, that is, operate the members of the structure.

Next, we define a GPIO register structure. The members of the structure are GPIO registers. The members are arranged in order of the offset address of the register from low to high. (struct usage reference 【C language 】 (2) : keyword detailed introduction)

1 typedef struct {
2 volatile uint32_t CRL;
3 volatile uint32_t CRH;
4 volatile uint32_t IDR;
5 volatile uint32_t ODR;
6 volatile uint32_t BSRR;
7 volatile uint32_t BRR;
8 volatile uint32_t LCKR;
9 } GPIO_TypeDef;
Copy the code

In the 8.2 Register Description section of STM32 Chinese Reference Manual, we can find the 7 register descriptions in the structure. When lighting the LED, we only use the CRL and ODR registers. As for the functions of other registers, you can see the manual for yourself. In the GPIO structure, we use two data types, one is uint32_t, which represents the unsigned 32-bit integer type, because GPIO registers are all 32 bits. This type declaration renames an unsigned int using a typedef in the standard stdint.h header file, which we can include in our program. The other is volatile, which tells the compiler that the variable is changing and that it must be read and written directly each time. This ensures that each read or write to the register is actually performed correctly.

Peripherals encapsulation

STM32F1 series GPIO ports are divided into A to G, namely, GPIOA, GPIOB…… GPIOG. Each port contains a register in the GPIO_TypeDef structure. We can define each GPIO port as a GPIO_TypeDef pointer according to the base address of each port in the manual. Then we can manipulate the register of each port according to the port name (actually now the structure pointer). The code implementation is as follows:

1 #define GPIOA ((GPIO_TypeDef *) 0X4001 0800)
2 #define GPIOB ((GPIO_TypeDef *) 0X4001 0C00)
3 #define GPIOC ((GPIO_TypeDef *) 0X4001 1000)
4 #define GPIOD ((GPIO_TypeDef *) 0X4001 1400)
5 #define GPIOE ((GPIO_TypeDef *) 0X4001 1800)
6 #define GPIOF ((GPIO_TypeDef *) 0X4001 1C00)
7 #define GPIOG ((GPIO_TypeDef *) 0X4001 2000)
Copy the code

Peripheral memory mapping

The cortex-M3 memory system is the first of its kind in chapter 5 of the Cortex-M3 Authoritative Guide. The address space of CM3 is 4GB, as shown below:

What we are going to talk about here is the peripherals on the chip, which is the base area of the registers we call. The total size of the peripherals is 512MB, 512MB is its limit space. Not every single chip microcomputer is used up, in fact, each MCU manufacturer only uses part of it. STM32F1 series uses: 0x4000 0000 ~0x5003 FFFF. The register of STM32 is now located in this region

  • APB1, APB2, and AHB bus base address

The register of STM32 is located in this area, where ST designs three buses: AHB, APB2 and APB1, among which AHB and APB2 are high-speed buses and APB1 is low-speed bus. Different peripherals are mounted to the three buses according to the speed. APB1, APB2, and AHB from bottom to top. The addresses of each bus are APB1:0x40000000, APB2:0x4001 0000, and AHB: 0x4001 8000 respectively. The base addresses of these three buses are obtained from section 2.3 of STM32 Chinese Reference Manual — Memory image: the base address of APB1 is the start address of TIM2 timer, the base address of APB2 is the start address of AFIO, and the base address of AHB is the start address of SDIO.

The APB1 address is also called the peripheral base address, which is the base address of all peripherals and is called PERIPH_BASE. Now we define these three bus addresses with macros. When we define other peripheral base addresses in the future, we only need to add offset addresses to the three bus base addresses, the code is as follows:

1 #define PERIPH_BASE ((uint32_t)0x40000000)
2 #define APB1PERIPH_BASE PERIPH_BASE
3 #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
4 #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
Copy the code
  • GPIO port base address

Since GPIO is mounted on the APB2 bus, now we can calculate the base address of each GPIO port according to the base address of APB2, using the macro definition code as follows:

1 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
2 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
3 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
4 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
5 #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
6 #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
7 #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)
Copy the code

Level 2: Base address macro definition + structure package complete code to control an LED with STM32:

1 #include <stdint.h> 2 #define __IO volatile 3 4typedef struct { 5 __IO uint32_t CRL; 6 __IO uint32_t CRH; 7 __IO uint32_t IDR; 8 __IO uint32_t ODR; 9 __IO uint32_t BSRR; 10 __IO uint32_t BRR; 11 __IO uint32_t LCKR; 12 } GPIO_TypeDef; 13 14typedef struct { 15 __IO uint32_t CR; 16 __IO uint32_t CFGR; 17 __IO uint32_t CIR; 18 __IO uint32_t APB2RSTR; 19 __IO uint32_t APB1RSTR; 20 __IO uint32_t AHBENR; 21 __IO uint32_t APB2ENR; 22 __IO uint32_t APB1ENR; 23 __IO uint32_t BDCR; 24 __IO uint32_t CSR; 25 } RCC_TypeDef; 26 27 #define PERIPH_BASE ((uint32_t)0x40000000) 28 29 #define APB1PERIPH_BASE PERIPH_BASE 30 #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) 31 #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) 32 33 #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) 34 #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) 35 #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) 36 #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) 37 #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) 38 #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) 39 #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) 40 #define RCC_BASE (AHBPERIPH_BASE + 0x1000) 41 42 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) 43 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) 44 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) 45 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) 46 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) 47 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) 48 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) 49 #define RCC  ((RCC_TypeDef *) RCC_BASE) 50 51 52 #define RCC_APB2ENR *(volatile unsigned long *)0x40021018 53 #define GPIOB_CRL *(volatile unsigned long *)0x40010C00 54 #define GPIOB_ODR *(volatile unsigned long *)0x40010C0C 55 56 int main(void) 57 {/ / open port B clock 58 59 RCC - > APB2ENR | = 1 < < 3; 60, 61 / / configure PB0 as general push-pull output mode, the rate of 2 m 62 GPIOB - > the CRL = (2 < < 0) | (0 < < 2); GPIOB->ODR = 0<<0; 66 67 } 68 69 void SystemInit(void) 70 { 71 }Copy the code

Level 2 changes:

Define a GPIO register structure. The members of the structure contain all the registers of the peripheral. The order of the members is the same as the offset address of the register, and the data type of the members is the same as the register.

② Peripheral memory mapping, that is, the address and peripheral to establish a one-to-one correspondence.

③ Peripheral declaration, that is, the name of a peripheral is defined as a pointer to a peripheral register structure type.

(4) Through the structural gymnastics as a register, to achieve lighting LED.

Level 3: Base address macro definition **+ struct encapsulation + “bit encapsulation” **** (corresponding byte encapsulation for each bit) **

Above, we control the Output data Register (ODR) when controlling the GPIO Output content. ODR is a 16-bit register, which must be controlled in the form of words. In fact, we can also control the BSRR and BRR registers to control the IO level. Here we briefly introduce the function of the BRR register, BSRR to see the manual study.

Bit-clear register BRR can only implement bit-clear zero operation, is a 32-bit register, low 16 bits is effective, write 0 does not affect, write 1 clear 0. Now we want to make PB0 output low level and light up LED, just write 1 to BR0 bit of BRR and 0 to other bits. The code is as follows:

1 GPIOB->BRR = 0X0001;
Copy the code

At this point, PB0 outputs the low level and the LED lights up. If PB2 is to output low level, then:

1 GPIOB->BRR = 0X0004;
Copy the code

If you want pb3/4/5/6… What about these IO output low levels? The idea is the same, just assign different values to the corresponding positions of the BRR. Because BRR is a 16-bit register with a large number of bits, it is easy to make mistakes when assigning values, and it is difficult to clearly know which IO is controlled from the assigned hexadecimal numbers. At this point, can we implement the macro definition for each position 1 of BRR, such as GPIO_Pin_0 for 0X0001 and GPIO_Pin_2 for 0X0004? As long as we define it once, it can be used in the future, and it can be known by name. The code for “bit encapsulation” (the corresponding byte encapsulation for each bit) is as follows:

1 #define GPIO_Pin_0 ((uint16_t)0x0001) /*! < Pin 0 selected */ 2 #define GPIO_Pin_1 ((uint16_t)0x0002) /*! < Pin 1 selected */ 3 #define GPIO_Pin_2 ((uint16_t)0x0004) /*! < Pin 2 selected */ 4 #define GPIO_Pin_3 ((uint16_t)0x0008) /*! < Pin 3 selected */ 5 #define GPIO_Pin_4 ((uint16_t)0x0010) /*! < Pin 4 selected */ 6 #define GPIO_Pin_5 ((uint16_t)0x0020) /*! < Pin 5 selected */ 7 #define GPIO_Pin_6 ((uint16_t)0x0040) /*! < Pin 6 selected */ 8 #define GPIO_Pin_7 ((uint16_t)0x0080) /*! < Pin 7 selected */ 9 #define GPIO_Pin_8 ((uint16_t)0x0100) /*! < Pin 8 selected */ 10 #define GPIO_Pin_9 ((uint16_t)0x0200) /*! < Pin 9 selected */ 11 #define GPIO_Pin_10 ((uint16_t)0x0400) /*! < Pin 10 selected */ 12 #define GPIO_Pin_11 ((uint16_t)0x0800) /*! < Pin 11 selected */ 13 #define GPIO_Pin_12 ((uint16_t)0x1000) /*! < Pin 12 selected */ 14 #define GPIO_Pin_13 ((uint16_t)0x2000) /*! < Pin 13 selected */ 15 #define GPIO_Pin_14 ((uint16_t)0x4000) /*! < Pin 14 selected */ 16 #define GPIO_Pin_15 ((uint16_t)0x8000) /*! < Pin 15 selected */ 17 #define GPIO_Pin_All ((uint16_t)0xFFFF) /*! < All pins selected */Copy the code

At this point, PB0 outputs the low level and the code becomes:

1 GPIOB->BRR = GPIO_Pin_0;
Copy the code

If PB0/PB15 output low level at the same time, use or operation, code:

1 GPIOB->BRR = GPIO_Pin_0|GPIO_Pin_15;
Copy the code

In order not to make main look redundant, the code wrapped in the library above should not be in main because it is related to GPIO, we can put these macros in a separate header file. Create stm32f10X_gpio. h in the project directory, put the package code in it, and add the file to the project. We just need to include the header file in main.c.

Level 4: Base address macro definition + struct encapsulation + “bit encapsulation” + function encapsulation

When we light up the LED, we control the IO of PB0. If the LED is connected to other IO, we need to modify the GPIOB to other ports. In fact, it is very quick and convenient to modify in this way. However, in order to improve the readability and portability of the program, can we write A special function to reset A certain bit of GPIO? This function has two parameters, one is GPIOX (X=A… G), and the other is GPIO_Pin (0… The BRR register is controlled by GPIOX and GPIO_Pin.

1 void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) 2 { 3 GPIOx->BRR = GPIO_Pin; 4}Copy the code

At this point, PB0 outputs the low level, and the code for lighting the LED becomes:

1 GPIO_ResetBits(GPIOB,GPIO_Pin_0);
Copy the code

Similarly, we can control the REGISTER BSRR to turn off the LED. The code is as follows:

Void GPIO_SetBits(GPIO_TypeDef* GPIOx, Uint16_t GPIO_Pin) 3 {4 GPIOx->BSRR = GPIO_Pin; 5}Copy the code

In this case, PB0 outputs a high level, and the code for turning off the LED becomes:

1 GPIO_SetBits(GPIOB,GPIO_Pin_0);
Copy the code

Also, since this function controls GPIO, we can create a special file for GPIO related functions. Create stm32f10X_gPIo. c in the project directory and put the GPIO related functions in it. Do we find that we just created a new header file stm32f10x_gPIO. H, these two files are related to peripheral GPIO. The function in the C file will use the definition in the H header file. These two files are complementary, so the stm32f10X_gpio. C file also contains the stm32f10X_gpio. h header file. Don’t forget to include the stm32f10x.h header, because that’s where all the registers are defined.

If we write functions for other peripherals, we should create two files just like GPIO to store functions, such as stm32f10x_rcc.c and stm32f10x_rcc.h for RCC peripherals. Other outside according to gourd gourd gourd gourd can be drawn.

(5) Example writing

Above, is an overview of the library sealing process, we are using library functions to write LED programs

①**** management library header file

When we start calling library functions to write code, some libraries we don’t need, we don’t need to compile at compile time, we can control by a total header file stm32f10x_conf.h, the main code of this header file is as follows:

1 //#include "stm32f10x_adc.h"
2 //#include "stm32f10x_bkp.h"
3 //#include "stm32f10x_can.h"
4 //#include "stm32f10x_cec.h"
5 //#include "stm32f10x_crc.h"
6 //#include "stm32f10x_dac.h"
7 //#include "stm32f10x_dbgmcu.h"
8 //#include "stm32f10x_dma.h"
9 //#include "stm32f10x_exti.h"
10 //#include "stm32f10x_flash.h"
11 //#include "stm32f10x_fsmc.h"
12 #include "stm32f10x_gpio.h"
13 //#include "stm32f10x_i2c.h"
14 //#include "stm32f10x_iwdg.h"
15 //#include "stm32f10x_pwr.h"
16 #include "stm32f10x_rcc.h"
17 //#include "stm32f10x_rtc.h"
18 //#include "stm32f10x_sdio.h"
19 //#include "stm32f10x_spi.h"
20 //#include "stm32f10x_tim.h"
21 //#include "stm32f10x_usart.h"
22 //#include "stm32f10x_wwdg.h"
23 //#include "misc.h"
Copy the code

This contains all the peripherals header files, we only need RCC and GPIO library functions to light an LED, RCC controls the clock, GPIO controls the specific IO port. So we comment out the header files of other peripheral library functions and uncomment the corresponding header files when we need them. The stm32f10x_conf.h header is included at the end of the stm32f10x.h header, at line 8296:

1 #ifdef USE_STDPERIPH_DRIVER
2 #include "stm32f10x_conf.h"

3 #endif
Copy the code

If USE_STDPERIPH_DRIVER is defined, it contains the stm32f10x_conf.h header file. When we create a new project, in the magic wand TAB C/C++, we define the macro USE_STDPERIPH_DRIVER, so the stm32f10x_conf.h header file is included in stm32f10x.h, and we only need to call one header file when we write the program: Stm32f10x. H. (Preprocessing instructions will be detailed in the article of [C language].)

Write LED initialization function

After the register lit LED operation, we know that the programming essentials for operating a GPIO output are as follows:

1. Enable GPIO port clock

2. Select the IO port to be controlled, that is, PIN

3. Set the output speed of the I/O port, that is, speed

4. Select the IO output mode, that is, mode

5. Output high/low level

The clock function of STM32 is very rich, and the configuration is flexible. In order to reduce power consumption, the clock of each peripheral can be turned off and on independently. All clock-related functions in STM32 are controlled by the RCC peripheral. There are three registers in the RCC that control the switch on and off of the peripheral clock: RCC_APHENR, RCC_APB2ENR and RCC_APB1ENR, AHB, APB2 and APB1 represent three buses on which all peripherals are mounted. GPIO is a high-speed peripheral mounted on APB2 bus. So its clock has RCC_APB2ENR control.

GPIO clock control

Firmware library functions: RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)

1 void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState) 2 { 3 /* Check the parameters */ 4 assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph)); 5 assert_param(IS_FUNCTIONAL_STATE(NewState)); 6 if (NewState ! = DISABLE) { 7 RCC->APB2ENR |= RCC_APB2Periph; 8 } else { 9 RCC->APB2ENR &= ~RCC_APB2Periph; 11 10}}Copy the code

Once the program is compiled, place the cursor over the function/variable/macro definition and press F12 on the keyboard or Go to Definition of the right mouse button to find the prototype. Firmware library underlying operating is RCC peripherals APB2ENR this register, macro RCC_APB2Periph_GPIOB prototype is: 0 x00000008, namely, (1 < < 3), and restore into store operation is: RCC – > APB2ENR | = 1 < < < 3. Compared to the firmware library operation, the code readability of register operation is very poor, only to consult the register configuration to know the function of the specific code, while the firmware library operation is exactly the opposite, see the name.

GPIO port configuration

Pin, speed, and mode of GPIO are all controlled by the port configuration register of GPIO. Among them, IO0 IO7 is controlled by the low register CRL configured on the port, and IO8 IO15 is configured by the high register CRH configured on the port. The firmware library encapsulates the pin, speed, and mode of the port configuration into a structure:

1 typedef struct {
2 uint16_t GPIO_Pin;
3 GPIOSpeed_TypeDef GPIO_Speed;
4 GPIOMode_TypeDef GPIO_Mode;
5 } GPIO_InitTypeDef;
Copy the code

Pin can be GPIO_Pin_0 to GPIO_Pin_15 or GPIO_Pin_All, which are predefined library macros.

Speed is also encapsulated as a structure:

1 typedef enum { 2 GPIO_Speed_10MHz = 1, 3 GPIO_Speed_2MHz, 4 GPIO_Speed_50MHz 5 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 6 7// Set pin to push-pull output 8 gPIo_initstructure. GPIO_Mode = GPIO_Mode_Out_PP; 9 10 // Set pin rate to 50MHz 11 gPIo_initstructure. GPIO_Speed = GPIO_Speed_2MHz; 12 13 /* Call the library function, initialize GPIOB0*/ 14 GPIO_Init(GPIOB, &GPIO_InitStructure);Copy the code

If there are different mode configurations for different pins on the same port, the GPIO initialization function will be called after each pin configuration, and the code is as follows:

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // Pull up GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // push pull output gPIo_initstructure. GPIO_Speed = GPIO_Speed_2MHz; // Push pull output gPIo_initstructure. GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);Copy the code

GPIO output control

GPIO output control can be controlled by the port data output register ODR, port bit setting/clearing register BSRR and port bit clearing register BRR. Port output register ODR is a 32-bit register, valid 16 bits lower, corresponding to IO0 to IO15, can only be operated in the form of words, generally using registers.

// PB0 output high level, light LED GPIOB->ODR = 1<<0;Copy the code

Port bit clearing register BRR is a 32-bit register, valid in the lower 16 bits, corresponding to IO0 to IO15. It can only be operated in the form of words.

// PB0 output low level, light LED GPIO_ResetBits(GPIOB, GPIO_Pin_0);Copy the code

BSRR is a 32-bit register, 16 bits lower for setting, valid for write 1, 16 bits higher for reset, valid for write 1, equivalent to a BRR register. We don’t use the high 16 bits, but we use the BRR register, so the BSRR register is used for setting.

// PB0 output high level, off LED GPIO_SetBits(GPIOB, GPIO_Pin_0);Copy the code

Firmware library LED GPIO initialization function

1 void LED_GPIO_Config(void) 2 {3 // Define a GPIO_InitTypeDef structure; 5 6// ENABLE the GPIOB clock 7 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_Pin = GPIO_Pin_0; 11 12 // Set pin to push-pull output 13 gPIo_initstructure. GPIO_Mode = GPIO_Mode_Out_PP; 14 15 // Set pin rate to 50MHz 16 gPIo_initstructure. GPIO_Speed = GPIO_Speed_50MHz; 17 18 /* Call the library function, initialize GPIOB0*/ 19 GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_0); 23}Copy the code

The main function

1 #include "stm32f10x.h" 2 3 void SOFT_Delay(__IO uint32_t nCount); 4 void LED_GPIO_Config(void); 5 6int main(void) 7 {8 Statup_stm32f10x_hd.s has called 9 // SystemInit() to initialize the system clock to 72MHZ 12 13 LED_GPIO_Config(); 14 15 while (1) {16 // light the LED 17 GPIO_ResetBits(GPIOB, GPIO_Pin_0); 18 Time_Delay(0x0FFFFF); 19 20 // Turn off LED 21 GPIO_SetBits(GPIOB, GPIO_Pin_0); 22 Time_Delay(0x0FFFFF); 26 void Time_Delay(volatile uint32_t Count) 27 {28 for (; Count ! = 0; Count--); 29}Copy the code

Void Time_Delay(volatile uint32_t Count) Instead of the traditional flag bit judgment method, more elegant and convenient management of program time trigger timing.

This article is shared from Huawei cloud community “[Embedded] Step by step, understand the library development”, the original author: LongYorke.

Click to follow, the first time to learn about Huawei cloud fresh technology ~