Keil C51 is known as the best development environment for 51 series microcontrollers, and everyone must be familiar with it. Some of its common features are also known to everyone (it is also mentioned in books), such as: because the RAM in 51 is very small, C51 functions do not pass parameters through the stack (except for reentrant functions), and local variables are not stored in the stack, but in fixed RAM and registers. Then take a look at the following program.
void fun1(unsigned char i)
{
…
}
Normally, parameter i is passed to a function through R7, so where is its actual address? Is it R7? Before answering this question, let's first understand some interesting features of Keil C51 (not considering reentrant functions).
1. The codes generated by defining a function before calling it and defining it after calling it are very different (especially when the optimization level is greater than level 3). (I am not sure why, probably because if it is defined before calling it, the calling function already knows how the called function uses the registers, so it can optimize the function itself; but if it is defined after calling it, the function does not know how the called function uses the registers, and it assumes that the called function has changed the registers (ACC, B, DPH, DPL, PSW, R0, R1, R2, R3, R4, R5,, R6, R7), so no valid data is stored in these registers)
2. When a function calls a function, except for the return address, the contents of any other registers (ACC, B, DPH, DPL, PSW, R0, R1, R2, R3, R4, R5, R6, R7) are not saved in the stack. (Unless the called function uses the using feature)
3. The interrupt function is an exception. It calculates the changes made to registers (ACC, B, DPH, DPL, PSW, R0, R1, R2, R3, R4, R5, R6, R7) by itself and the functions it calls, and saves the corresponding registers that it thinks have been changed.
4. When writing programs in C, try to avoid using the using n (n=0,1,2,3) feature. (This feature has some problems in my use, I don't know if it is a small bug)
The following tests were all performed in (environment keil c51 v7.20) with the optimization level set to default.
Let’s look at the first feature question first.
example 1:
void fun2(void)
{
}
void fun1(unsigned char i)
{
fun2();
while(i--);
}
Its assembly code is as follows:
; void fun2(void)
RSEG ?PR?fun2?TEST
fun2:
; SOURCE LINE # 12
; {
; SOURCE LINE # 13
; }
; SOURCE LINE # 14
RET
; END OF fun2
;
; void fun1(unsigned char i)
RSEG ?PR?_fun1?TEST
_fun1:
USING 0
; SOURCE LINE # 16
;---- Variable 'i?240' assigned to Register 'R7' ----
; {
; SOURCE LINE # 17
; fun2();
; SOURCE LINE # 18
LCALL fun2
?C0003:
; while(i--);
; SOURCE LINE # 19
MOV R6,AR7
DEC R7
MOV A,R6
JNZ ?C0003
; }
; SOURCE LINE # 20
?C0005:
RET
; END OF _fun1
From this we can see that fun2() is defined before fun1(), fun1() knows how fun2() uses registers, knows that R7 has not changed, and the parameter i is stored in R7, that is, i is R7. (;---- Variable 'i?140' assigned to Register 'R7' ----)
Look at another situation
void fun2(void);
void fun1(unsigned char i)
{
fun2();
while(i--);
}
void fun2(void)
{
}
The assembly code is as follows:
; void fun1(unsigned char i)
RSEG ?PR?_fun1?TEST
_fun1:
USING 0
; SOURCE LINE # 14
MOV i?140,R7
; {
; SOURCE LINE # 15
; fun2();
; SOURCE LINE # 16
LCALL fun2
?C0002:
; while(i--);
; SOURCE LINE # 17
MOV R7,i?140
DEC i?140
MOV A,R7
JNZ ?C0002
; }
; SOURCE LINE # 18
?C0004:
RET
; END OF _fun1
;
; void fun2(void)
RSEG ?PR?fun2?TEST
fun2:
; SOURCE LINE # 20
; {
; SOURCE LINE # 21
; }
; SOURCE LINE # 22
RET
; END OF fun2
fun2() is defined after fun1() is called. Since fun1() does not know how fun2() uses registers when calling fun2(), it is assumed that fun2() changes all registers (ACC, B, DPH, DPL, PSW, R0, R1, R2, R3, R4, R5, R6, R7). Since fun1() believes that fun2() changes the values of registers (including R7), although i is passed through R7, it has been changed by calling fun2(), so it cannot exist in R7 anymore, and an extra Byte is used to store it in RAM.
This also explains the problem at the beginning. The storage of parameter i depends on the problem.
Haha, isn't it interesting? This is a very useful feature in saving RAM. (Have you also saved 1 Byte of RAM for yourself?)
This example also explains the second feature. When a function calls another function, except for the return address, the contents of any other registers (ACC, B, DPH, DPL, PSW, R0, R1, R2, R3, R4, R5, R6, R7) are not saved in the stack. Before calling a function, try not to save valid data in these registers. If it is unavoidable, store the valid data in a fixed RAM.
Regarding the interrupt function problem, I wonder what you would think when you see that the following program has a difference of 55 bytes.
Example 2:
void OSTimeDly(void); //using 1
static void Timer0OVInt(void) interrupt 1 //using 1
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTimeDly();
}
void OSTimeDly(void) //using 1
{
}
and
void OSTimeDly(void) //using 1
{
}
static void Timer0OVInt(void) interrupt 1 //using 1
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTimeDly();
}
Their assembly codes are:
; static void Timer0OVInt(void) interrupt 1 //using 1
RSEG ?PR?Timer0OVInt?TEST
USING 0
Timer0OVInt:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
MOV PSW,#00H
PUSH AR0
PUSH AR1
PUSH AR2
PUSH AR3
PUSH AR4
PUSH AR5
PUSH AR6
PUSH AR7
USING 0
; SOURCE LINE # 24
; {
; TR0 = 0;
; SOURCE LINE # 26
CLR TR0
; TH0 = 100;
; SOURCE LINE # 27
MOV TH0,#064H
; TL0 = 100;
; SOURCE LINE # 28
MOV TL0,#064H
; TR0 = 1;
; SOURCE LINE # 29
SETB TR0
;
; OSTimeDly();
; SOURCE LINE # 31
LCALL OSTimeDly
; }
; SOURCE LINE # 32
POP AR7
POP AR6
POP AR5
POP AR4
POP AR3
POP AR2
POP AR1
POP AR0
POP PSW
POP DPL
POP DPH
POP B
POP ACC
RETI
; END OF Timer0OVInt
;
;
; void OSTimeDly(void) //using 1
RSEG ?PR?OSTimeDly?TEST
OSTimeDly:
; SOURCE LINE # 35
; {
; SOURCE LINE # 36
;
; }
; SOURCE LINE # 38
RET
; END OF OSTimeDly
and
; void OSTimeDly(void) //using 1
RSEG ?PR?OSTimeDly?TEST
OSTimeDly:
; SOURCE LINE # 22
; {
; SOURCE LINE # 23
;
; }
; SOURCE LINE # 25
RET
; END OF OSTimeDly
CSEG AT 0000BH
LJMP Timer0OVInt
;
; static void Timer0OVInt(void) interrupt 1 //using 1
RSEG ?PR?Timer0OVInt?TEST
USING 0
Timer0OVInt:
; SOURCE LINE # 27
; {
; TR0 = 0;
; SOURCE LINE # 29
CLR TR0
; TH0 = 100;
; SOURCE LINE # 30
MOV TH0,#064H
; TL0 = 100;
; SOURCE LINE # 31
MOV TL0,#064H
; TR0 = 1;
; SOURCE LINE # 32
SETB TR0
;
; OSTimeDly();
; SOURCE LINE # 34
LCALL OSTimeDly
; }
; SOURCE LINE # 35
RETI
; END OF Timer0OVInt
The assembly code of this example well explains the above features 1 and 3.
As for the fourth characteristic, it is worth a special mention. See the following example:
Example 3:
void OSTimeDly(void);
static void Timer0OVInt(void) interrupt 1 using 0
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTimeDly();
}
void OSTimeDly(void) // using 0
{
}
Its assembly code is
; static void Timer0OVInt(void) interrupt 1 using 0
RSEG ?PR?Timer0OVInt?TEST
USING 0
Timer0OVInt:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
USING 0
MOV PSW,#00H
; SOURCE LINE # 24
; {
; TR0 = 0;
; SOURCE LINE # 26
CLR TR0
; TH0 = 100;
; SOURCE LINE # 27
MOV TH0,#064H
; TL0 = 100;
; SOURCE LINE # 28
MOV TL0,#064H
; TR0 = 1;
; SOURCE LINE # 29
SETB TR0
;
; OSTimeDly();
; SOURCE LINE # 31
LCALL OSTimeDly
; }
; SOURCE LINE # 32
POP PSW
POP DPL
POP DPH
POP B
POP ACC
RETI
; END OF Timer0OVInt
;
; void OSTimeDly(void) // using 0
RSEG ?PR?OSTimeDly?TEST
OSTimeDly:
; SOURCE LINE # 34
; {
; SOURCE LINE # 35
;
; }
; SOURCE LINE # 37
RET
; END OF OSTimeDly
In this example, except that the interrupt function uses using 0, there is no difference from the program in the previous example, but the assembly code is very different. The assembly code in this example no longer saves the values of R0 ---- R7. (By default, the function in Keil C51 uses register group 0. When the interrupt function uses using n, n = 1, 2, 3 may be correct, but when n = 0, the program already has a bug (this bug will not appear only when the interrupt function and the function it calls do not change the values of R0 ---- R7))
One conclusion is that if using n is used in the interrupt function, the interrupt will no longer save the values of R0----R7.
From this, we can infer that when a high-priority interrupt function and a low-priority interrupt function use using n at the same time (n = 0, 1, 2, 3), this bug is very hidden. (This is exactly what people can't imagine)
Finally, let's look at an example
Example 4:
void OSTimeDly(unsigned char i);
static void Timer0OVInt(void) interrupt 1 using 1
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTimeDly(5);
}
void OSTimeDly(unsigned char i) // using 0
{
while(i--);
}
Compiled results
; static void Timer0OVInt(void) interrupt 1 using 1
RSEG ?PR?Timer0OVInt?TEST
USING 1
Timer0OVInt:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
USING 1
MOV PSW,#08H
; SOURCE LINE # 25
; {
; TR0 = 0;
; SOURCE LINE # 27
CLR TR0
; TH0 = 100;
; SOURCE LINE # 28
MOV TH0,#064H
; TL0 = 100;
; SOURCE LINE # 29
MOV TL0,#064H
; TR0 = 1;
; SOURCE LINE # 30
SETB TR0
;
; OSTimeDly(5);
; SOURCE LINE # 32
MOV R7,#05H
LCALL _OSTimeDly
; }
; SOURCE LINE # 33
POP PSW
POP DPL
POP DPH
POP B
POP ACC
RETI
; END OF Timer0OVInt
;
; void OSTimeDly(unsigned char i) // using 0
RSEG ?PR?_OSTimeDly?TEST
_OSTimeDly:
USING 0
; SOURCE LINE # 35
;---- Variable 'i?441' assigned to Register 'R7' ----
; {
; SOURCE LINE # 36
?C0009:
; while(i--);
; SOURCE LINE # 37
MOV R6,AR7
DEC R7
MOV A,R6
JNZ ?C0009
; }
; SOURCE LINE # 38
?C0011:
RET
; END OF _OSTimeDly
Note the assembly code here in OSTimeDly(),
MOV R6,AR7
DEC R7
Because the register bank used by the Timer0OVInt() function is 1 (using 1), and OSTimeDly() uses register bank 0 by default (the default register bank will not be changed explicitly by the code). Therefore, when Timer0OVInt() calls OSTimeDly(), the register bank is still bank 1, the address of R7 is 15, and the address of AR7 is the address of R7 in the register bank used by OSTimeDly(), which is 7 in register bank 0. Therefore, when AR7 is 0, this is an infinite loop.
Conclusion: functions using different register sets cannot call each other (except in special cases)
Previous article:A non-preemptive operating system architecture based on C51 single-chip microcomputer
Next article:Design of temperature and humidity monitoring system based on RS485 bus
Recommended ReadingLatest update time:2024-11-17 00:18
- Popular Resources
- Popular amplifiers
- 西门子S7-12001500 PLC SCL语言编程从入门到精通 (北岛李工)
- Siemens Motion Control Technology and Engineering Applications (Tongxue, edited by Wu Xiaojun)
- How to read electrical control circuit diagrams (Classic best-selling books on electronics and electrical engineering) (Zheng Fengyi)
- MCU C language programming and Proteus simulation technology (Xu Aijun)
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!
- 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
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Date in spring + go out and get a sea bass
- 【Multi-function open source custom macro keyboard】Bluetooth keypad project construction and experiment
- Can I teach myself Java to find a job?
- Last day, 74% winning rate: Watch the video "Avoiding Common AC-DC Design Pitfalls" and win a prize
- Serial port baud rate selection
- [RVB2601 creative application development] Mobile modbus master station
- About Sobel Algorithm
- What is the best engineer like?
- STM32 interrupt priority grouping setting
- FreeRtos blocking issue discussion