stm32 fancy point water lamp

Posted by mgoerz on Thu, 14 Oct 2021 20:22:17 +0200

preface

This paper briefly introduces different ways to realize water lamp, and compares the similarities and differences under different ways.
The circuit is built with STM32 minimum system core board (STM32F103C8T6) + panel board + 3 red, green and blue LEDs. The three ports of GPIOA, GPIOB and GPIOC are used to control the LED lights to flash in turn, with an interval of 1 second.
Here we use GPIOA_Pin_12,GPIOB_Pin_1,GPIOC_Pin_14 control red, green and blue LED lights respectively.

1, Firmware library water lamp

(1) New project

Because the ST firmware library is used, the configuration of this part is cumbersome. For details, please refer to some online configuration processes

Establishment of stm32f103c8t6 engineering template

The project document template and reference manual configured by me are also placed later for reference

(2) Configure GPIO ports

Three steps for initialization and setting of GPIO port

  • Clock configuration
  • Input / output mode setting
  • Maximum rate setting

Let's follow this step to start configuring GPIOB_Pin_0 port
stm32 provides a firmware library encapsulated in c language. We can directly call the corresponding library functions for what functions we want to achieve.

  • Configure clock
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //Turn on the clock of the peripheral GPIOB
  • GPIO initialization structure

The library function provides a structure to configure the input / output mode setting and maximum rate setting of GPIO port

as follows

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;
  • It is configured as universal push-pull output with an output speed of 2M
    GPIO_InitTypeDef   GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;  			//The output mode is universal push-pull output
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0 ;             //The selected port is GPIO_ Pin_ one
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;				//The output speed is 2M
	
	GPIO_Init(GPIOB,&GPIO_InitStruct);

One GPIO_Pin_0 configuration completed.

Due to the high repeatability of this part of the code, you can write your own function to encapsulate the appeal process to achieve code simplicity.

Therefore, I put the relevant functions of water lamp in led.c and declare them in led.h.

(3) Improve the project and build the circuit

  • Other functions

After the GPIO port is configured, we can control the led to turn on and off. However, if we need to achieve an accurate delay of 1s and the LED flashes, we need to add a delay function. There is no more introduction to this part of the system interrupt.
Software delay (no operation during cyclic operation) can also be used here. The disadvantage is that it can not achieve accurate delay and low operation efficiency.

  • Build circuit
    This time, we use c8t6 to control the red, green and yellow lights on the bread board to flash in turn. The circuit diagram is as follows.

2, Register flow lamp

(1) Register mapping

1. Learn to find register address

Take gpiob - > CRL register as an example, find the register address in stm32f103x Chinese reference manual

  • Find the starting address of the GPIOB port

    You can see that the starting address of GPIOB port is 0X4001 0C00

  • Locate the port configuration low register (GPIOx_CRL) in the GPIO register


The offset address of the register is 0x00, so the address of GPIOB - > CRL is (starting address of GPIOB port + offset address) 0X4001 0C00+0x00=0X4001 0C00

2. Write other register addresses

Repeat the above steps to find the addresses of other required registers

/*RCC Peripheral base address*/
#define  RCC_BASE             (unsigned int)(0x40021000)
/*RCC The AHB1 clock enable register address is forcibly converted to a pointer*/
#define  RCC_APB2ENR		 *(unsigned int*)(RCC_BASE+0x18)

/*GPIOB Peripheral base address*/
#define  GPIOB_BASE           (unsigned int)(0X40010C00)

/* GPIOB Register address, cast to pointer */
#define GPIOB_CRL			*(unsigned int*)(GPIOB_BASE+0x00)
#define GPIOB_CRH			*(unsigned int*)(GPIOB_BASE+0x04)
#define GPIOB_IDR			*(unsigned int*)(GPIOB_BASE+0x08)
#define GPIOB_ODR			*(unsigned int*)(GPIOB_BASE+0x0C)
#define GPIOB_BSRR	        *(unsigned int*)(GPIOB_BASE+0x10)
#define GPIOB_BRR			*(unsigned int*)(GPIOB_BASE+0x14)
#define GPIOB_LCKR		    *(unsigned int*)(GPIOB_BASE+0x18)

/*GPIOC Peripheral base address*/
#define GPIOC_BASE           (unsigned int)(0x40011000)

/* GPIOC Register address, cast to pointer */
#define GPIOC_CRL			*(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH			*(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR			*(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR			*(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR	        *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR			*(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR		    *(unsigned int*)(GPIOC_BASE+0x18)

	
/*GPIOC Peripheral base address*/
#define GPIOD_BASE            (unsigned int)(0x40011400)

/* GPIOC Register address, cast to pointer */
#define GPIOD_CRL			*(unsigned int*)(GPIOD_BASE+0x00)
#define GPIOD_CRH			*(unsigned int*)(GPIOD_BASE+0x04)
#define GPIOD_IDR			*(unsigned int*)(GPIOD_BASE+0x08)
#define GPIOD_ODR			*(unsigned int*)(GPIOD_BASE+0x0C)
#define GPIOD_BSRR	        *(unsigned int*)(GPIOD_BASE+0x10)
#define GPIOD_BRR			*(unsigned int*)(GPIOD_BASE+0x14)
#define GPIOD_LCKR		    *(unsigned int*)(GPIOD_BASE+0x18)

The above includes all the registers involved in this experiment. The macro definition is used here to make the code more readable. The macro of the register name is defined as the address corresponding to the register, otherwise it has been operating against the address. Later, I don't know which register to operate.

(2) Function of register

Since register programming is involved, it is important for us to understand the role of relevant registers to be used.

To configure GPIOB_Pin_1 is the general push-pull output mode as an example

  • APB2 peripheral clock enable register (RCC_APB2ENR)

There are many peripherals in STM32. In order to reduce power consumption, each peripheral corresponds to a clock. These clocks are turned off when the chip is just powered on. If you want the peripherals to work, you must turn on the corresponding clock.

The function of this register is to control the switches of all peripheral clocks attached to APB2 bus. For each peripheral, we must turn on the corresponding clock. Here we can see RCC_ Bit 3 of the apb2enr register controls the clock on and off of the GPIOB.

-Turn on the clock of GPIOB peripheral

 RCC_APB2ENR |= (1<<3); //Shift 1 left 3 bits to 0x10, and RCC_APB2ENR performs or operation 
  • Port configuration low register (GPIOx_CRL)

GPIOx_CRL contains pins 0-7 (control GPIO low 8 bits), and each pin occupies 4 register bits. The MODE bit is used to configure the output speed, and the CNF bit is used to configure various input and output modes.

We configure GPIOB_Pin_1 is universal push-pull output, and the output speed is 2M

    GPIOB_CRL &= ~( 0x0F<< (4*1));// 0x0F shifts left by 4 bits, reverses, and then connects with GPIOB_CRL performs and operation to change the data of 4-7 bits to 0
	GPIOB_CRL |= (2<<4*0);   //2 shift 0 bit to the left and 0x02, set the second position to 1, configure PB1 as general push-pull output, and the speed is 2M
  • Port output data register (GPIOx_ODR)

The function of this register is very simple. It controls the output data to 0 or 1.

Therefore, it is also very simple for us to control the LED delay flashing, that is, to control the ODR register to output 1 first, the LED light is on, delay for a period of time, control the ODR register to output 0 first, the LED light is off, and keep cycling, so as to realize the effect of running water lamp.

Control GPIOB_Pin_1 output is 1

GPIOB_ODR |= (1<<1);  //1 shifts left by 1 bit and becomes 0x10, which is consistent with gpiob_ The ODR performs an or operation to change its second bit to 1

Control GPIOB_Pin_1 output is 0

GPIOB_ODR &= ~(1<<1); 1 Shift left by 1 bit and reverse,And GPIOB_ODR Performs an and operation to change its second bit to 0

(3) Register programming

  • The total code is as follows
void delay(unsigned int i);

int main(void)
{
	// Turn on the clock of GPIOA, GPIOB and GPIOC ports
	RCC_APB2ENR |= (7<<2);

	//Clear the port bit of control PA12
	GPIOA_CRH &= ~( 0x0F<< (4*4));	
	// PA12 is configured as universal push-pull output with a speed of 2M
	GPIOA_CRH |= (2<<4*4);
	
	
	//Clear the port bit of control PB1
	GPIOB_CRL &= ~( 0x0F<< (4*1));	
	// PB1 is configured as universal push-pull output with a speed of 2M
	GPIOB_CRL |= (2<<4*1);	
	
	
	//Clear the port bit of control PC14
	GPIOC_CRH &= ~( 0x0F<< (4*6));	
	// PC14 is configured as universal push-pull output with a speed of 2M
	GPIOC_CRH |= (2<<4*6);

	while(1)
	{
		GPIOB_ODR |= (1<<1);
		delay(100);                 //Red flashing
	  GPIOB_ODR &= ~(1<<1);
	  delay(100);
		
		GPIOA_ODR |= (1<<12);
		delay(100);					//The yellow light flashes
	  GPIOA_ODR &= ~(1<<12);
	  delay(100);
		
		GPIOC_ODR |= (1<<14);
		delay(100);					//The green light flashes
	  GPIOC_ODR &= ~(1<<14);
	   delay(100);		
	}

}


void delay(unsigned int i)
{
	unsigned char j;               //Simple delay function
	unsigned char k;
	for(;i>0;i--)
		for(j =500; j>0; j--) 
	       for(k =200; k>0; k--);
}

Note: 4 * 0 is used in this code because it is used for GPIO_CRL(GPIO_CRL controls one pin every four bits). After changing 0 in 4 * 0 to 1, it is convenient to operate the subsequent pins. This writing method is for the convenience of subsequent operations.

When writing your own code, don't ignore the register address defined above.

3, Assembly language water lamp

  • When building a pure assembly project file and configuring the project file environment, do not select 'startup' and 'core' (because the reference program has its own functions such as startup file, which will lead to conflict after selection)

  • The code is as follows

RCC_APB2ENR EQU 0x40021018

GPIOA_CRH EQU   0x40010804
GPIOA_ODR EQU   0x4001080C
                                    
GPIOB_CRL EQU   0x40010C00    ;Register mapping
GPIOB_ODR EQU   0x40010C0C	
	
GPIOC_CRH EQU   0x40011004
GPIOC_ODR EQU   0x4001100C	
	
	
Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

                AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 
		
                
MainLoop		BL LED2_Init
                BL LED2_ON
                BL Delay             ;LED2 The lamp flashes
                BL LED2_OFF
                BL Delay
				
				BL LED1_Init				
				BL LED1_ON
                BL Delay             ;LED1 The lamp flashes
                BL LED1_OFF
                BL Delay
				
                BL LED3_Init				
				BL LED3_ON
                BL Delay            ;LED3 The lamp flashes
                BL LED3_OFF
                BL Delay
				
                B MainLoop
				
             
LED1_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x08         ; Turn on the clock of port GPIOB		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1 is configured as a universal push-pull output
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002   
                LDR R1,=GPIOB_ODR          ;GPIO_Pin_1 The output is 0
                STR R0,[R1]
             
                POP {R0,R1,PC}


             
LED1_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002    ;GPIO_Pin_1 output is 0, LED1 is off
			    LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED1_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOB_ODR
                ORR R0,R0,#0X00000002    ;GPIO_Pin_1 output is 1, LED1 is on
                 LDR R1,=GPIOB_ODR
                STR R0,[R1]
                POP {R0,R1,PC}           


				

LED2_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x04 		   ; Turn on the clock of GPIOA
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOA_CRH
                ORR R0,R0,#0X00020000   ;GPIOA_Pin_12 is configured as a universal push-pull output
                LDR R1,=GPIOA_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOA_ODR
                BIC R0,R0,#0X00001000   
                LDR R1,=GPIOA_ODR            ;GPIOA_Pin_12 The output is 0
                STR R0,[R1]
             
                POP {R0,R1,PC}
				
LED2_OFF
                PUSH {R0,R1, LR}   
                
               LDR R0,=GPIOA_ODR
               BIC R0,R0,#0X00001000        ;GPIOA_Pin_12 output is 0, LED2 is off
			    LDR R1,=GPIOA_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED2_ON
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOA_ODR
                ORR R0,R0,#0X00001000     ;GPIOA_Pin_12 output is 1, LED2 is on
				 LDR R1,=GPIOA_ODR
                STR R0,[R1]
				
				 POP {R0,R1,PC}
				 

LED3_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x10 		    ; Turn on the clock of GPIOC
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]                
                
                LDR R0,=GPIOC_CRH
                ORR R0,R0,#0X02000000   ;GPIOC_Pin_14 is configured as a universal push-pull output
                LDR R1,=GPIOC_CRH
                STR R0,[R1]
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000   ;GPIOC_Pin_14 output is 0
                LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_OFF
                PUSH {R0,R1, LR}    
                
                LDR R0,=GPIOC_ODR
                BIC R0,R0,#0X00004000  ;GPIOC_Pin_14 output is 0, LED3 goes out
			    LDR R1,=GPIOC_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}
             
LED3_ON
                PUSH {R0,R1, LR}    
                
                 LDR R0,=GPIOC_ODR
                ORR R0,R0,#0X00004000   ;GPIOC_Pin_14 output is 1, LED3 is on
                 LDR R1,=GPIOC_ODR
                STR R0,[R1]
				
                POP {R0,R1,PC}        
                
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1

                CMP R0,#300
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#300
                BCC DelayLoop0

                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                POP {R0,R1,PC}    

                END
  • About stm32 startup file
	
Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

                AREA    RESET, DATA, READONLY

__Vectors       DCD     __initial_sp               
                DCD     Reset_Handler              
                    
                    
                AREA    |.text|, CODE, READONLY
                    
                THUMB
                REQUIRE8
                PRESERVE8
                    
                ENTRY
Reset_Handler 

We just said that we should not select the 'startup' startup file when creating a new project, because we have defined some functions about the startup ` 'startup file here. If the startup startup file is included, it will cause conflicts.

Brief introduction to 'startup' startup file

The startup file is written by assembly and is the first program to be executed after the system is powered on and reset. The main work is as follows:

  1. Initialize stack pointer SP=_initial_sp
  2. Initialize PC pointer = Reset_Handler
  3. Initialize interrupt vector table
  4. Configure system clock
  5. Call C library function_ Main initializes the user stack, and finally calls the main function to the world of C
  • LED port initialization
             
LED1_Init
                PUSH {R0,R1, LR}
                
                LDR R0,=RCC_APB2ENR
                ORR R0,R0,#0x08       ; Turn on the clock of port GPIOB		
                LDR R1,=RCC_APB2ENR
                STR R0,[R1]
                
                
                LDR R0,=GPIOB_CRL
                ORR R0,R0,#0X00000020   ;GPIOB_Pin_1 is configured as universal push-pull output, and the output speed is 2M
                LDR R1,=GPIOB_CRL
                STR R0,[R1]
                
                LDR R0,=GPIOB_ODR
                BIC R0,R0,#0X00000002   ; Configure GPIOB_Pin_1. The output is low level and the LED is off
                LDR R1,=GPIOB_ODR
                STR R0,[R1]
             
                POP {R0,R1,PC}

  • Software delay
Delay
                PUSH {R0,R1, LR}
                
                MOVS R0,#0
                MOVS R1,#0
                MOVS R2,#0
                
DelayLoop0        
                ADDS R0,R0,#1

                CMP R0,#300
                BCC DelayLoop0
                
                MOVS R0,#0
                ADDS R1,R1,#1
                CMP R1,#300
                BCC DelayLoop0

                MOVS R0,#0
                MOVS R1,#0
                ADDS R2,R2,#1
                CMP R2,#15
                BCC DelayLoop0
                
                POP {R0,R1,PC}    

This part adopts software delay. The specific principle is that R0, R1 and R2 are initialized to 0, R0 plus 1. When R0 is greater than 300, R1 plus 1, then R0 becomes 0, R0 continues to add 1, and cycle. When R1 is greater than 300, R2 plus 1, R0 and R1 become 0. Continue this operation.
This delay function lasts about 300 * 300 * 15 = 1350000 instruction cycles.

  • Summary

Some parts of the assembly code of this instance are repeated, resulting in some redundancy of the code. In addition, in fact, the lighting method I use is essentially operating registers, but I use assembly language. You can refer to the above register lighting program to compare the difference between the two.
For the convenience of comparison, I try to keep the naming of variables in the assembler the same as that in the register.

4, Actual effect

The above three methods are to control GPIOA_Pin_12 ,GPIOB_Pin_1,GPIOC_ Pin_ The final implementation results of port 14 are as follows.

summary

From c language firmware library to register to assembly language, this experiment involves more and more low-level, and has a deeper understanding of some functions of stm32 register.
In register operation, pointer and macro definitions are used for shift left, shift right, XOR, bitwise and, bitwise OR, as well as the structure of c language. It can be regarded as learning for practical use.
The deficiency of the experiment is that the precise delay is not used when using assembly language, but the second is to use the software delay with low accuracy. The reason is still unfamiliar.

I hope you will not hesitate to point out the inadequacies mentioned above.

reference resources
STM32 from address to register
STM32 register introduction, address search, and direct operation register

Reference books
STM32 Library Development Guide - wildfire based developer development board
STM32F10X reference manual

Data link

Link: https://pan.baidu.com/s/1w5iVrx-Ob_C5z5MljxZkww
Extraction code: i4wl

Topics: stm32