Precise delay (reprinted content) of c51)

Source: Internet
Author: User
Precise delay of c51

Some friends suggested that the accuracy of 51 Single-Chip Microcomputer delay program using C language is not enough. In fact, the C language is eventually compiled into an assembly language for running, so the assembly program will be very large, this affects the precision of latency. Here, we will post some online materials for your reference.

The following content is reprinted:

A Simple Study of 51 MCU Keil C delay Program

By: infinitespace studio/isjfk, 1.21.2004

Anyone can repost this article without specifying the original author and source, but it cannot be used for commercial purposes.

When a single-chip microcomputer is used, short latency is often required. The latency is short. Generally, the latency is from dozens to hundreds of microseconds (us ). Sometimes it also requires high accuracy. For example, when a single-chip microcomputer is used to drive DS18B20, the error allowed range is less than a dozen us, otherwise it is easy to make mistakes. In this case, using a timer is often a little tricky. In extreme cases, timers have even been used for other purposes. In this case, we need to find another method.
When I used to write a single-chip microcomputer program in assembly language, this problem was relatively easy to solve. For example, if we use a 12 MHz crystal oscillator 51 with a delay of 20 us, we can use the following code to meet the general needs:
MoV r0, #09 h
Loop: djnz r0, loop
The instruction period of 51 single-chip microcomputer is 1/12 of the crystal oscillator frequency, that is, 1 US cycle. MoV r0, # 09h requires two extreme periods, while djnz also requires two extreme periods. The number in R0 is (20-2)/2. With this method, you can easily implement latency of less than us. If it takes a longer time, you can use two layers of nesting. And the precision can reach 2 us. In general, this is enough.
Now, the more widely used is undoubtedly the C compiler of Keil. 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 drawback of C) is that the real-time performance is not guaranteed and the instruction cycle of code execution cannot be predicted. Therefore, in scenarios with high real-time requirements, compilation and C joint applications are also required. But is there a latency program that needs to be implemented through assembly? To find this answer, I did an experiment.
When using C language to implement latency programs, the first thing that comes to mind is the commonly used loop statements in C. The following code is frequently seen on the Internet:
Void delay2 (unsigned char I)
{
For (; I! = 0; I --);
}
How high is the accuracy of this code? To directly measure the effect of this code, I found the assembly code generated by Keil C based on this code:
; 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 #21
0006? C0010:
0006 22 RET
; Function _ delay2 (end)
I really don't know ~~~ I can only see how unexpected this latency program is ~~~ The four major statements require six machine cycles. That is to say, it has a precision of at most 6 us, which is not counted as a lcall and A ret. If we set the root delay length column of the I value assigned when calling a function to a table, it is:
I delay time/us
0 6
1 12
2 18
...
Because function calls require two lcall clock periods, the delay time is two more than the execution time of the slave function code. By the way, some friends write such code:
Void delay2 (unsigned char I)
{
Unsigned char;
For (A = I;! = 0; --);
}
Some people may think that this will generate longer assembly code, but it turns out:
; Function _ delay2 (BEGIN)
; Source line #18
; ---- Variable 'I 'assigned to register 'r7 '----
; Source line #19
; Source line #21
; ---- 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 #22
0006? C0010:
0006 22 RET
; Function _ delay2 (end)
The generated code is the same. However, this is indeed not a good habit. Because there is no need to introduce unnecessary variables here. We will continue to discuss the topic. Some friends even use the code like this for a longer latency:
Void delay2 (unsigned long I)
{
For (; I! = 0; I --);
}
What is the assembly code generated by this code? In fact, you don't need to know how horrible it is. $ # ^ % & % $ ...... let's take a look:
; Function _ delay2 (BEGIN)
; Source line #18
0000 8f00 R mov I + 03 h, r7
0002 8e00 R mov I + 02 h, R6
0004 8d00 R mov I + 01 H, R5
0006 8c00 R mov I, r4
; Source line #19
; Source line #20
0008? C0007:
0008 E4 CLR
0009 FF mov R7,
000a Fe mov R6,
000b FD mov R5,
000c FC mov R4,
000d ab00 R mov R3, I + 03 h
000f aa00 R mov R2, I + 02 h
0011 a900 R mov R1, I + 01 H
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 + 03 h
001d 24ff Add a, # 0ffh
001f F500 R mov I + 03 h,
0021 e500 R mov A, I + 02 h
0023 34ff addc a, # 0ffh
0025 F500 R mov I + 02 h,
0027 e500 R mov A, I + 01 H
0029 34ff addc a, # 0ffh
002b F500 R mov I + 01 H,
002d e500 R mov A, I
002f 34ff addc a, # 0ffh
0031 F500 R mov I,
0033 80d3 sjmp? C0007
; Source line #21
0035? C0010:
0035 22 RET
; Function _ delay2 (end)
Well, it can indeed be delayed for a long time ~~~ But there is no precision.
So, can I use C to achieve accurate latency? I slightly changed the code:
Void delay1 (unsigned char I)
{
While (I --);
}
Based on experience, the more concise the C code, the more concise the machine code can be obtained. What is the result? Let's take a look at the assembly code it generates. Hopefully, I pressed the "build target" key, and the result was huge:
; 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
0004 70fa jnz? C0004
; Source line #16
0006? C0006:
0006 22 RET
; Function _ delay1 (end)
Although the generated code is not the same as 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 cannot figure out how to simplify the source program. It seems that I will come up with this conclusion: "If we need us-level latency accuracy, we need to use assembly language ." But is that true? I am still unwilling. Because I don't believe that the famous Keil C compiler doesn't even use djnz ??? In fact, the program only needs one loop: djnz R7 and loop. Close to despair (in this case, people can really burst out. Oh, hehahaoha ~~~), I changed it:
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)
Days ~~~ A miracle occurs... I think this program can already meet the general needs. If a table is listed:
I delay time/us
1 5
2 7
3 9
...
When calculating the latency, the time spent on the two clock cycles of the lcall statement that calls the function is counted.
Finally, the result is clear. C can achieve unexpected results as long as it is used properly. Many friends complain that C is much more efficient than Assembly. In fact, if you have a deep understanding of the Compilation Principle of Keil C, you can use the appropriate syntax, optimize the generated C code. Although this seems unlikely, there are still some simple principles to follow: 1. Try to use the unsigned data structure. 2. Try to use the char type. Int is not enough, and then long is used. 3. If possible, do not use floating point. 4. Use concise code, because concise C code based on experience can often generate concise target code (although not in all cases ). 5 .. I can't think of it anymore ~~~


[Advertisement]
Ceiwei junior engineer

UID 1574
Essence 2
Points 294
Post 55
Prestige 10 points
RMB 662 coins
Promotion 0 Points
Read Permission 20
Register
Status Offline

#2 use the item and post it on, September 22,. Personal space short message and add it as a friend

51 MCU Keil C delay Program research again!

Author: see Source: www.waveshare.net
The above is a simple study of the latency program. From the perspective of precision, it has the following results:
Void delay2 (unsigned char I)
{
While (-- I );
}
Is the best method.

Analysis: assume that the plug-in is 12 Mb (which will be discussed later)
I compiled some parameters, read the assembly code, and observed and recorded the following data:
Delay2 (0): latency 518us 518-2*256 = 6
Delay2 (1): latency of 7 us (5us in the original post is incorrect, ^ _ ^)
Delay2 (10): latency: 25us 25-20 = 5
Delay2 (20): latency: 45us 45-40 = 5
Delay2 (100): latency 205us 205-200 = 5
Delay2 (200): latency 405us 405-400 = 5

As shown in the preceding figure, the scheduling speed is 2 us, and the maximum error is 6 us.
Precision is very high!

However, the maximum latency of this program is 518us, which obviously cannot meet the actual needs, because the latency is usually longer.

Then, we will discuss what will happen when T is allocated to two bytes, namely, the uint type.

Void delay8 (uint T)
{
While (-- t );
}
I compiled some parameters, read the assembly code, and observed and recorded the following data:
Delay8 (0): latency 524551us 524551-8*65536 = 263
Delay8 (1): latency: 15us
Delay8 (10): latency 85us 85-80 = 5
Delay8 (100): latency 806-800 = 6
Delay8 (1000): latency 8009-8000 = 9
Delay8 (10000): latency 80045-8000 = 45
Delay8 (65535): latency 524542us 524542-524280 = 262

If the program can be scheduled to 8 us, the maximum error is 263us, but this latency program still cannot meet the requirements, because the maximum latency is 524.551 Ms.

What about using ulong t?
It must be terrible. You don't have to look at the compiled assembly code...

So how can we get a relatively small scheduler, a large adjustable range, and a relatively small amount of Ram? See the following program:

/*------------------------------------------------------------------
Function Name: 50us latency
Note: Based on 1 MIPS, The at89 series correspond to 12 m crystal oscillator, And the w77 and w78 series correspond to 3 m crystal oscillator.
Example: Call delay_50us (20) to get 1 ms latency
Input:
Return: None
------------------------------------------------------------------*/
Void delay_50us (uint T)
{
Uchar J;
For (; t> 0; t --)
For (j = 19; j> 0; j --)
;
}

I compiled some parameters, read the assembly code, and observed and recorded the following data:
Delay_50us (1): latency 63us 63-50 = 13
Delay_50us (10): latency 513us 503-500 = 13
Delay_50us (100): latency 5013us 5013-5000 = 13
Delay_50us (1000): latency 50022-50000 = 22

Hehe, latency 50 ms, error only 22us, as C language is acceptable. In addition, if the accuracy is required, the timer should be used even if the assembly is used.

/*------------------------------------------------------------------
Function Name: 50 ms latency
Note: Based on 1 MIPS, The at89 series correspond to 12 m crystal oscillator, And the w77 and w78 series correspond to 3 m crystal oscillator.
Example: Call delay_50ms (20) to get 1 s latency
Global variable: None
Return: None
------------------------------------------------------------------*/
Void delay_50ms (uint T)
{
Uint J;
For (; t> 0; t --)
For (j = 6245; j> 0; j --)
;
}
I compiled some parameters, read the assembly code, and observed and recorded the following data:
Delay_50ms (1): latency 50 010 10us
Delay_50ms (10): latency 499 983 17us
Delay_50ms (100): latency 4 999 713 287us
Delay_50ms (1000): latency 4 997 022 2.978 Ms

Hehe, latency 50 s, error only 2.978 ms, acceptable!

The above program does not use long or a loop of more than three layers, but splits the delay into two programs to improve accuracy. It should be a good practice.

If you want to get a higher-precision latency, you can do this:
Void delay_50us (uint T)
{
Uchar J;
If (T> 255)
{
Targeted delay compensation;
}
If (T <1, 255)
{
Targeted delay compensation;
}
For (; t> 0; t --)
For (j = 18; j> 0; j --) // change the original 19 to 18 or smaller.
;
}
Void delay_50ms (uint T)
{
Uint J;
If (T> ...)
{
Targeted delay compensation;
}
If (T> ...)
{
Targeted delay compensation;
}
If (T> ...)
{
Targeted delay compensation;
}
......
......
For (; t> 0; t --)
For (j = 6244; j> 0; j --) // change the original 6245 value to 6244 or smaller.
;
}

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.