I used to think about using a compiler to write C code for MCUs. What does the compiler do for us? How does the compiler allocate variables and codes? So I had nothing to do and looked at what was in the compiler installation path. I used the ICCAVR compiler and Atmel's atmega64 at work. So I was playing with this compiler and MCU~~~.
To be honest, ICCAVR compiler is indeed very simple and convenient, but it is powerful (of course, I have never used other compilers o(╯□╰)o). I will not repeat its basic usage again. In the compiler environment, click the help menu to pop up Show Library Source Code passwd, and then click it to pop up a small prompt box: password is ICCAVR. Go to the installation directory of ICCAVR and you will see a compressed package libsrc.zip, which is in the libsrc.avr folder. Haha, I guess you already know the decompression password of this compressed package. There are commonly used C library function source code and assembly code of commonly used functions.
There is an init.s file in the libsrc.avr folder. This file is a common file for initializing the mega series MCU. It contains dozens of lines of assembly code. The first code executed when the MCU is powered on is this code, not the code you wrote~~~The code is as follows:
; init.s
;
; to be included by the crt*.s files
;
; initialize stacks
;
; set the hardware stack to ram_end, and the software stack some
; bytes below that
ldi R28, out $3D,R28 out $3E,R29 subi R28, ldi R16,0xAA ; sentenial std Y+0,R16 ; put sentenial at bottom of HW stack clr R0 ldi R30,<__bss_start ldi R31,>__bss_start ldi R17,>__bss_end ; this loop zeros out all the data in the bss area ; init_loop: cpi R30,<__bss_end cpc R31,R17 breq init_done st Z+,R0 rjmp init_loop init_done: std Z+0,R16 ; put sentenial at bottom of SW stack ; copy data from idata to data ; idata contains the initialized values of the global variables ldi R30,<__idata_start ldi R31,>__idata_start ldi R26,<__data_start ldi R27,>__data_start ldi R17,>__idata_end ; set RAMPZ always. If this is a main app, then RAMPZ needs to reset to ; zero if invoked through the bootloader ldi R16,USE_ELPM out 0x3B,R16 copy_loop: cpi R30,<__idata_end cpc R31,R17 breq copy_done .if USE_ELPM elpm ; load (RAMPZ:Z) .else lpm ; load (Z) byte into R0 .endif R30,1 st X+,R0 rjmp copy_loop copy_done: If you are not familiar with assembly, please supplement it yourself. This code also uses some of ICC's own compiler pseudo-instructions: <, > are modulo and integer operations on $FF respectively. The constants such as ram_end and hwstk_size are determined when we select the chip type when creating a new project, or they can be changed in the project option. Usually the default hwstk_size hardware stack size is 30, and the size of ram_end depends on the chip you use. For AVR64, the value is 10ff. This is related to the memory organization inside the chip, which marks the terminal address of the MCU's sram. The .text pseudo-instruction marks the following generation as being located in the code area, the _start:: label is the compiler's internal start label, and _main is our program entry. Note: The :: in the ICC compiler represents an external label, and : represents an internal label. First, use the immediate value to load ldi to store the high-end address of RAM into the Y pointer, and assign the Y pointer to the SP stack pointer out $3D. The 0x3D in R28 is the address of SPL. In this way, the stack pointer is set through the first four instructions. Then use the same method to set the stack size. Since the Y pointer points to the high-end address of the stack, then use the subi instruction to subtract the stack size you set in the compiler environment, and point the Y pointer to the bottom of the hardware stack. At the same time, the bottom of the hardware stack is adjacent to the top of the software stack. In order to prevent stack overflow, the ICC compiler specifically stores 0xAA as a marker there (ldi R16, 0xAA std Y+0, R16). In fact, if you look at the macro function in the header file to check whether the stack overflows, it determines whether the 0XAA stored there is overwritten. After setting up the stack, the memory for the variables is allocated. For the allocation of variable memory, the compiler operates as follows: define first and then allocate, and the memory will not be optimized for speed (it will not do even byte optimization or anything, because SRAM is very scarce). Variables are divided into variables with initial values and variables without initial values (definition and declaration of variables). ICC stores these two types of variables in the bss area and data area respectively. The bss area stores variables without initial values (global variables with only declarations but no definitions, etc.), and the data area stores global variables with initial values (global variables and static-modified variables with initial values). The processing of the bss area is naturally very simple. clr R0 ldi R30,<__bss_start ldi R31,>__bss_start ldi R17,>__bss_end init_loop: cpi R30,<__bss_end cpc R31,R17 breq init_done st Z+,R0 rjmp init_loop Use the processor characteristic instructions to set the Z pointer, use the pointer self-increment storage and jump, and clear all the bss variables in the C project you wrote by default. At this time, executing std z+0, R16 still stores the 0xAA mark at the top of the bss area. Between the bss area and the hardware stack area is the software stack area. It's like this: when the compiler helps you plan the memory, your variable space starts from the low address, first plans the data area, then divides the bss area, and the rest is the software stack area, and finally the hardware stack area. So when you have too many global variables, the hardware stack is certain, then the software stack is bound to be squeezed too small. The software stack is used for stacking and popping during function calls, interrupt scene protection and other operations. So this situation is particularly prone to stack overflow~~~! ! ! ldi R30,<__idata_start ldi R31,>__idata_start ldi R26,<__data_start ldi R27,>__data_start ldi R17,>__idata_end At this point, the difference between the idata area and the data area appears. The idata area specifies the storage location of your global variables with initial values in the Flash, otherwise how can the MCU remember the initial values of the variables you defined? copy_loop: cpi R30,<__idata_end cpc R31,R17 breq copy_done .if USE_ELPM elpm ; load (RAMPZ:Z) .else lpm ; load (Z) byte into R0 .endif R30,1 st X+,R0 rjmp copy_loop copy_done: This code copies all the initial values to the corresponding positions in the data area in RAM, and determines whether elpm is needed based on the chip type. The entire init.s code is like this, and it is also mentioned in the previous comment; to be included by the crt*.s files. This general code will be referenced in the crtxxboot.s code. The general boot code is as follows: ; make sure to assemble w/ -n flag, e.g. ; iasavr -n crt... ; ; bootloader startup file, same as crtavr.s except that vectors are routed ; to the bootloader section and use jmp for the vector ; .include "area.s" .text __start:: ; entry point ; route vector ldi R16.1 out 0x35,R16 ; MCUCR = 1, unlock IVSEL ldi R16.2 out 0x35,R16 ; MCUCR = 2, set ivsel The above code is used to set the interrupt vector table location, whether it is located at the beginning of the Flash or the bls area (this will be discussed later) USE_ELPM = 0; .include "init.s" ; call user main routine call _main looks for your main function. Now the compiler has prepared everything for you and handed the MCU to you. _exit:: rjmp _exit ; interrupt vectors. The first entry is the reset vector ; .area vector(abs) calibrates the absolute address of the interrupt vector, located at 00000H, makes a jump, and looks for __start .org 0 jmp __start Well, this completes the initialization of ICCAVR to MCU. This is my understanding. I hope you can criticize and correct me.
Previous article:AVR Development Arduino Method (Part 2) Interrupt Subsystem
Next article:AVR Lesson 8: Independent Buttons
Recommended ReadingLatest update time:2024-11-17 03:05
- Popular Resources
- Popular amplifiers
- Principles and Applications of Single Chip Microcomputers 3rd Edition (Zhang Yigang)
- Metronom Real-Time Operating System RTOS for AVR microcontrollers
- Learn C language for AVR microcontrollers easily (with video tutorial) (Yan Yu, Li Jia, Qin Wenhai)
- ATmega16 MCU C language programming classic example (Chen Zhongping)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- Introducing the TI C2000 stack evaluation method
- Note on ATMEGA168 replacing ATMEGA8 microcontroller
- MicroPython's official documentation has been significantly adjusted
- DISP0_CONTRAST is used as GPIO to drive LED
- (Bonus 6) GD32L233 Review - Random Numbers (with software random number generation algorithm)
- Disassembly of a compact portable negative ion generator
- MM32F031 Development Board Evaluation 4: UART and ADC Test
- Half-bridge DC/DC power supply based on LM5036
- Share ESP8266smartconfig source code
- The voltage of HM1548 cannot be raised to 24V. I have tried 10UH inductor and 22UF capacitor, but they are not good. Can someone take a look at it?