Some interesting features of Keil C51 that must be noted

Publisher:梦中徐来Latest update time:2016-12-15 Source: eefocusKeywords:Keil Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

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) 


Keywords:Keil Reference address:Some interesting features of Keil C51 that must be noted

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

Usage of sbit sfr in c51 programming
1 First distinguish between bit and sbit   Bit is similar to int char, except that char = 8 bits and bit = 1 bit. They are all variables, and the compiler assigns addresses during the compilation process. Unless you specify, the address is random. This address is the entire addressable space, RAM+FLASH+extension spa
[Microcontroller]
The transmit interrupt flag on the C51 microcontroller
For most of the microcontrollers, there is a special flag to judge whether the serial port sends or receives data. For example, in c51, TIx and RIx. TIx indicates whether the data in the data buffer has been sent. TIx = 1 means that the data has been sent, and TIx = 0 means that the data has not been sent yet. S
[Microcontroller]
Proteus and Keil Cx51 microcontroller simulation (timer application 1)
Use a timer to make the LEDs connected to P0, P!, P2, and P3 ports flash 10 times every 10 seconds; set the low and high level lights of P0, P!, P2, and P3 ports to be on, otherwise the lights will be off. Circuit diagram: C program:   #include reg51.h #include intrins.h #define uchar unsigned char bit fl
[Microcontroller]
C51# Study Notes 01# | Getting Started Tutorial on Keil Software
Specific tutorial for using Keil software: 1. Create a new project - - , select the save file directory, and then select the chip model - in the pop-up menu bar. Note that a box will pop up here and select . Note: Some Keil versions of new projects are in - - 2. Create a new .C file - , a new text file wi
[Microcontroller]
C51# Study Notes 01# | Getting Started Tutorial on Keil Software
C51 MCU————Bus and system expansion
1. Citation As mentioned above, the characteristics of single-chip microcomputers are small size, full functions, compact system structure, and can meet the requirements of small needs. So what about a slightly larger embedded system? Then it is possible that your data memory, program memory, and IO ports may not be
[Microcontroller]
C51 MCU————Bus and system expansion
Keil C51's 18th keyword extension of C language: using
In the 8051 series microcontrollers, the first 32 bytes of the internal RAM are divided into 4 groups, each with 8 registers. The names of the 8 registers in each group are R0-R7. By setting two bits in the PSW register, you can choose which of the 4 groups of registers to use. Register sets are very useful when handl
[Microcontroller]
C51 program for adjustable time alarm clock
/* Program effect: clock, including adjustable time, alarm rings when time is up, buzzer, this program has been debugged You can download the code of this program from http://www.51hei.com/ziliao/file/naozhong.rar . The speaker is connected to p2.0. The port can be changed by yourself. */
[Microcontroller]
C51 programming specifications
Introduction: When programming, the first thing to consider is the feasibility of the program, followed by readability, portability, robustness, and testability. This is the general rule. However, many people ignore readability, portability, and robustness (the debuggable methods may be different), which is wrong. Let
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号