51 MCU Keil C Delay Program

Publisher:Joyful222LifeLatest update time:2013-03-04 Source: 21IC Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

When using a single-chip microcomputer, you often encounter situations where a short delay is required. The required delay time is very short, usually tens to hundreds of microseconds (us). Sometimes very high precision is also required. For example, when using a single-chip microcomputer to drive a DS18B20, the allowable error range is within a dozen us, otherwise it is easy to make mistakes. In this case, using a timer is often a bit of an overreaction. In extreme cases, the timer has even been used for other purposes. At this time, we need to think of other ways.

In the past, when we used assembly language to write microcontroller programs, this problem was relatively easy to solve. For example, if we used a 51 with a 12MHz crystal oscillator and wanted to delay for 20us, we could use the following code to meet general needs:

mov r0, #09h

loop: djnz r0, loop

The instruction cycle of the 51 single-chip microcomputer is 1/12 of the crystal oscillator frequency, that is, one cycle is 1us. mov r0,

#09h needs 2 cycles, and djnz also needs 2 cycles. So the number stored in r0 is (20-2)/2. This method can easily achieve a delay of less than 256us. If a longer time is needed, two layers of nesting can be used. And the accuracy can reach 2us, which is generally enough.

Now, the most widely used one is undoubtedly Keil's C compiler. Compared with assembly, C has many advantages, such as easy maintenance, easy to understand, and suitable for large projects. But the disadvantage (I think this is the only disadvantage of C) is that the real-time performance is not guaranteed, and the instruction cycle of code execution cannot be predicted. Therefore, in situations where real-time performance is required, the joint application of assembly and C is also required. But does such a delay program also need to be implemented in assembly? In order to find the answer, I did an experiment.

When implementing a delay program in C language, the first thing that comes to mind is the commonly used loop statement in C. The following code is what I often see on the Internet:

void delay2(unsigned char i)

{

for(; i != 0; i--);

}

How accurate can this code be? In order to directly measure the effect of this code, I used the Keil C

According to the assembly code generated by this code, it was found out:

; FUNCTION _delay2 (BEGIN)

; SOURCE LINE

#18

;---- Variable \'i\' assigned to Register \'R7\' ----

; SOURCE LINE

#19

; SOURCE LINE

#20

0000 ?C0007:

0000 EF MOV A,R7

0001 6003 JZ ?C0010

0003 1F DEC R7

0004 80FA SJMP ?C0007

; SOURCE LINE

# twenty one

0006 ?C0010:

0006 22 RET

; FUNCTION _delay2 (END)

You really don't know until you see it~~~ You will know how inaccurate this delay program is~~~ Just looking at the main four statements, it takes 6 machine cycles. In other words, its accuracy is at most 6us, and this does not include one

lcall and a ret. If we list the value of i assigned when calling the function and the delay length in a table, it is:

i delay time/us

0 6

1 12

2 18

...

Because the function call requires 2 clock cycles of lcall, delay

time is 2 more than the execution time of the function code. By the way, some friends wrote the following code:

void delay2(unsigned char i)

{

unsigned char a;

for(a = i; a != 0; a--);

}

One might think that this would generate longer assembly code, but it turns out that:

; FUNCTION _delay2 (BEGIN)

; SOURCE LINE

#18

;---- Variable \'i\' assigned to Register \'R7\' ----

; SOURCE LINE

#19

; SOURCE LINE

# 21[page]

;---- Variable \'a\' assigned to Register \'R7\' ----

0000 ?C0007:

0000 EF MOV A,R7

0001 6003 JZ ?C0010

0003 1F DEC R7

0004 80FA SJMP ?C0007

; SOURCE LINE

# twenty two

0006 ?C0010:

0006 22 RET

; FUNCTION _delay2 (END)

The generated code is the same. But this is not a good habit. Because there is really no need to introduce extra variables here. Let's continue to discuss the topic. Some friends even used such code to get a longer delay:

void delay2(unsigned long i)

{

for(; i != 0; i--);

}

What does the assembly code generated by this code look like? In fact, you don’t have to think about how horrible it is$#^%&%$...... Let’s take a look:

; FUNCTION _delay2 (BEGIN)

; SOURCE LINE

#18

0000 8F00 R MOV i+03H,R7

0002 8E00 R MOV i+02H,R6

0004 8D00 R MOV i+01H,R5

0006 8C00 R MOV i,R4

; SOURCE LINE

#19

; SOURCE LINE

#20

0008 ?C0007:

0008 E4 CLR A

0009 FF MOV R7,A

000A FE MOV R6,A

000B FD MOV R5,A

000C FC MOV R4,A

000D AB00 R MOV R3,i+03H

000F AA00 R MOV R2,i+02H

0011 A900 R MOV R1,i+01H

0013 A800 R MOV R0,i

0015 C3 CLR C

0016 120000 E LCALL ?C?ULCMP

0019 601A JZ ?C0010

001B E500 R MOV A,i+03H

001D 24FF ADD A,#0FFH

001F F500 R MOV i+03H,A

0021 E500 R MOV A,i+02H

0023 34FF ADDC A,#0FFH

0025 F500 R MOV i+02H,A

0027 E500 R MOV A,i+01H

0029 34FF ADDC A,#0FFH

002B F500 R MOV i+01H,A

002D E500 R MOV A,i

002F 34FF ADDC A,#0FFH

0031 F500 R MOV i,A

0033 80D3 SJMP ?C0007

; SOURCE LINE

# twenty one

0035 ?C0010:

0035 22 RET

; FUNCTION _delay2 (END)

Haha, this can indeed delay for a long time~~~ but there is no accuracy at all.

So, can we achieve accurate delay using C? I modified the code slightly:

void delay1(unsigned char i)

{

while(i--);

}

Because according to experience, the more concise the C code is, the more concise the machine code is. So what is the result? Take out the generated assembly code and you will know. I pressed the "Build" button with hope.

target" key, the result was a huge blow:

; FUNCTION _delay1 (BEGIN)

; SOURCE LINE

#13

;---- Variable \'i\' assigned to Register \'R7\' ----

; SOURCE LINE

#14

0000 ?C0004:

; SOURCE LINE

#15

0000 AE07 MOV R6,AR7

0002 1F DEC R7

0003 EE MOV A,R6[page]

0004 70FA JNZ ?C0004

; SOURCE LINE

#16

0006 ?C0006:

0006 22 RET

; FUNCTION _delay1 (END)

Although the generated code is not quite the same as using the for statement, I can say without a doubt that the efficiency of the two methods is the same. It seems that this is the end, because I really can't think of any room for simplification of the source program. It seems that I will come to this conclusion: "If you need us-level delay accuracy, use assembly language when necessary." But is it really like this? I still don't want to give up. Because I don't believe the famous

Keil C compiler doesn't even know how to use djnz??? Because in fact, the program body only needs one loop: djnz r7,

loop. When I was almost desperate (people can often explode in this situation, oh hehe~~~), I changed it casually:

void delay1(unsigned char i)

{

while(--i);

}

Absent-minded compilation, look at the source code:

; FUNCTION _delay1 (BEGIN)

; SOURCE LINE

#13

;---- Variable \'i\' assigned to Register \'R7\' ----

; SOURCE LINE

#14

0000 ?C0004:

; SOURCE LINE

#15

0000 DFFE DJNZ R7,?C0004

; SOURCE LINE

#16

0002 ?C0006:

0002 22 RET

; FUNCTION _delay1 (END)

Oh my god, a miracle has happened... I think this program should be able to meet the needs of general situations. If you make a table:

i delay time/us

1 5

2 7

3 9

...

When calculating the delay time, the 2 clock cycles spent on the lcall statement that calls the function are already taken into account.

Finally, the result is clear. As long as it is used reasonably, C can still achieve unexpected results. Many friends complain that C efficiency is much worse than assembly. In fact, if you use Keil

If you have a deeper understanding of C compiler theory, you can optimize the generated C code by using appropriate syntax. Even if it seems unlikely, there are still some simple principles to follow: 1. Try to use unsigned data structures. 2. Try to use char type, and if it is not enough, use int, and then long. 3. If possible, do not use floating point type. 4. Use concise code, because according to experience, concise C code can often generate concise target code (although not in all cases). 5... I can't remember it, oh ha ha ~~~ (chills ~~)

Reference address:51 MCU Keil C Delay Program

Previous article:What are the differences in the P0~P3 port structures of the MCS-51 microcontroller?
Next article:51 MCU external expansion RAM

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号