The Calculation Method of Cx51 Programming Stack

Publisher:静默思考Latest update time:2011-08-23 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

introduction

Using C language to program MCS51 series microcontrollers is an inevitable trend in the development and application of microcontrollers. Keil's C51 compiler supports the classic 8051 and 8051 derivatives, commonly known as Cx51. It should be said that Cx51 is an extension of C language on MCS51 microcontrollers. It has both the common features of C language and its own characteristics. This article introduces the calculation method of stack when programming Cx51 .

1 Stack overflow problem

The MCS51 series of microcontrollers set the stack in the on-chip RAM. Due to the limited on-chip RAM resources, the range of the stack area is also limited. If the stack area is too large, it will reduce the storage space for other data, and if it is too small, it is easy to overflow. The so-called stack overflow means that when the stack area is full, a new stack push operation is required. At this time, the pushed content has to be stored in the special function register (SFR) of the non-stack area or the data area outside the stack. The content of the special function register affects the state of the system, and the content of the data area is easily modified by the program. In this way, when the stack pop operation (such as subroutine return) is performed later, the content has changed, and the program is messed up. Therefore, the stack area must be enough, and it is better to be larger. To prevent stack overflow in Cx51 programming, two problems must be solved: first, accurately calculate the stack size allocated by the system to the user, assuming it is M; second, accurately calculate the stack size required by the user, assuming it is N. It is required that M≥N. The following analyzes these two problems respectively.

2 Computing System

In Cx51 programming, because dynamic local variables are resident in memory, they are actually equivalent to local static variables, and the space is not released even at the end of the function call (this is different from the standard C language). The Cx51 compiler stores all variables in the on-chip and off-chip RAM according to the user's settings. After the space is allocated for the on-chip variables, the remaining space is used as stack space, which is the maximum possible stack space. Of course, because Cx51 is a C language that can access registers (special function registers), SP can be accessed in the program to set the stack space smaller. However, generally no one does this. This article only discusses variables placed in the on-chip RAM. We divide variables into two cases:

① Local variables used as function parameters and function return values. Such variables should be stored in register groups as much as possible. For the convenience of discussion, it is assumed that register group 0 is used uniformly, and the specific addresses are 0x00~0x07. A maximum of 3 parameters can be passed. If the number of parameters is large, the extra parameters are stored in the memory (address after 0x08). Here, it is assumed that the number of parameters of each function is no more than 3.

② The global variables we define in the program, as well as local variables that are not used as function parameters and function return values. The above two types of variables are stored after address 0x08 in the memory. After storage, the stack pointer SP points to the last byte of the on-chip RAM where the variable is allocated. Because the stack of the MCS51 microcontroller is a full-increment stack and the stack width is 8 bits, when a push operation is required, the stack pointer is first increased by 1, and then the valid content is pushed into the stack. With the above rules, the stack space allocated to the user by the system can be accurately calculated. Taking the function of finding the greatest common divisor and the least common multiple of two numbers as an example, the code is as follows:

#include

unsigned char max(unsigned char a, unsigned char b);

unsigned char min(unsigned char a, unsigned char b);

unsigned char M;

void main (void) {

unsigned char n;

M = max(12, 9);

n = min(12, 9);

}

unsigned char max(unsigned char a, unsigned char b){

while(a != b) {

if(a > b)

a = a - b;

else

b = b - a;

}

return a;

}

unsigned char min(unsigned char a, unsigned char b){

unsigned char k;

k = a*b/M;

return k;

}

The resource allocation in this program is as follows: a global variable M (unsigned character type) stores the greatest common divisor; a local variable n (unsigned character type) is defined in the main function to store the least common multiple; the function unsigned char max (unsigned char a, unsigned char b) for finding the greatest common divisor has two parameters a and b; the function unsigned char min (unsigned char a, unsigned char b) for finding the least common multiple has two parameters a and b, and a variable k is defined to store the return value of the function. The space allocated by the system to the variables can be calculated from this. The parameters and return values ​​of the function are stored in the working register group, so they do not occupy the space after the address 0x08. The system only allocates storage space to the variables M and n. These two variables occupy two bytes (addresses 0x08 and 0x09), so the stack pointer SP should point to 0x09. The system resource usage of the generated code after the Cx51 system is compiled is as follows: the address of the global variable M is 0x08, the address of n is 0x09, and the value of SP is 0x09. This is consistent with our calculation results.

3 Calculate the stack size required by the user

How big should the stack area be to be considered sufficient? In Cx51 programming, the size of the stack required by the user can be calculated from the number of nesting levels of ordinary subroutines and interrupt subroutines. The call of ordinary subroutines is relatively simple. Each time it is called, the return address of the function is saved in the stack, and this address occupies two bytes. When functions are nested, starting from the innermost subroutine, the total number of bytes required for the stack is the number of nesting levels multiplied by 2. The stack requirements of interrupt subroutines are divided into two cases:

① The interrupt subroutine uses the register group before the interrupt occurs. When an interrupt occurs, 2 bytes are required to save the return address of the interrupt subroutine. After the interrupt occurs, the system automatically performs the following operations in the interrupt subroutine: ACC, B, DPH, DPL, PSW, R0~R7, a total of 13 registers, are pushed onto the stack. Including the interrupt return address, the interrupt stack requires 15 bytes.

② The interrupt subroutine uses its own dedicated register group. In this case, the contents of R0~R7 do not need to be saved, which can reduce the stack requirements. Other contents still need to be pushed on the stack for protection. When an interrupt occurs, 2 bytes are required to save the return address of the interrupt subroutine. After the interrupt occurs, the system will automatically perform the following operations in the interrupt subroutine: ACC, B, DPH, DPL, PSW, a total of 5 registers are pushed on the stack. Adding the interrupt return address, the stack requirement is 7 bytes. However, it should be noted that if a sub-function is called in the interrupt subroutine, and the function requires parameters and return values, the called sub-function and the interrupt subroutine must use the same register group, otherwise unpredictable consequences will occur. Take a temperature test system as an example. The system uses 8051 as the processor. After the A/D conversion is completed, the temperature signal is reminded to receive and process the microcontroller through external interrupt 0. Timing interrupt 0 is used as a monitoring program, and the interrupt cycle is 20 ms. The temperature signal can be measured automatically (once per second) or manually (measured after pressing the measurement key). These two measurement methods can be switched by the control key. The nesting of interrupt subroutines and common subfunctions is as follows: the display subroutine is called in the timer interrupt program, and there is no function call inside the external interrupt 0. Part of the program is as follows:

void int0(void) interrupt 0 using 1 {

Read conversion data;

Data processing;

}

void time0 (void) interrupt 1 {

The count value is reloaded;

Read key;

Key processing;

leddisp(adat); //display

}

void main (void) {

Initialization of relevant data and self-check of digital display;

External interrupt and timer initialization settings;

MCU sleep;

}

void leddisp(unsigned char *pt) {

Use serial port working mode 0 to send display data, and display it statically after conversion by 74LS164;

}

Next, we analyze the maximum stack requirement of this program. Assume that when timer 0 is interrupted, the display function void leddisp(unsigned char *pt) is called. When the display function is called, the A/D conversion ends and the external interrupt 0 interrupt occurs. At this time, the maximum stack requirement of the program should be: timer 0 (15 bytes) + display function (2 bytes) + external interrupt 0 (7 bytes) = 24 bytes.

Conclusion

By accurately calculating the stack space allocated to the user by the compiler system and the user's own maximum stack requirement, not only can the stack overflow problem be fundamentally solved, but also the relatively tight resources of the microcontroller can be well arranged. In addition, by storing an appropriate amount of local variables in the on-chip memory, the execution speed of the software can be effectively improved.

Reference address:The Calculation Method of Cx51 Programming Stack

Previous article:Design of CAN bus signal acquisition node based on P87C591
Next article:Construction of embedded remote power grid monitoring system based on SX52BD 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号