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 ~~)
Previous article:What are the differences in the P0~P3 port structures of the MCS-51 microcontroller?
Next article:51 MCU external expansion RAM
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
- Understanding C language pointers
- Intelligent POS system block diagram analysis and its seven key functional module solutions
- Animation demonstrates the working principle of capacitors and the principle of capacitive sensors
- AT commands in C language
- Circuit Playground Bluefruit NeoPixel Animated Remote Control
- 5224. FAQs on debugging C2000 CLA on CCS V1.0
- GD32F105RBT6 About FMC internal flash operation issues
- This week's review information is here~
- A pitfall of threadx queue, and also pay attention to a mistake in the tough guy tutorial
- About 48V input buck type DCDC chip