Some tips on using the Keil C to write 51 single-chip delay program

When using a single-chip microcomputer, it is often encountered that a short time delay is required. The delay time required is very short, usually tens to hundreds of microseconds (us). Sometimes it needs high precision. For example, when the DS18B20 is driven by a single-chip microcomputer, the error tolerance is within a dozen or so, otherwise it is easy to make mistakes. In this case, using a timer is often a bit of a fuss. In extreme cases, the timer has even been used for other purposes. At this time, we need another way.

When I used to write a single-chip program in assembly language, this problem was relatively easy to solve. For example, using a 12MHz crystal oscillator 51, intend to delay 20us, as long as the following code, you can meet the general needs:

Mov r0, #09h

Loop: djnz r0, loop

The instruction cycle of the 51 MCU is 1/12 of the crystal frequency, which is 1us cycle. Mov r0, #09h requires 2 extreme cycles, and djnz also requires 2 extreme cycles. Then the number in r0 is (20-2)/2. In this way, it is very convenient to implement a delay of less than 256us. If you need more time, you can use two levels of nesting. And the accuracy can reach 2us, in general, this is enough.

Now, the wider application is undoubtedly Keil's C compiler. Relative to assembly, C has many advantages, such as easy to maintain, easy to understand, suitable for large projects. But the shortcomings (I think this is the only drawback of C) is that there is no guarantee of real-time performance, and it is impossible to predict the instruction cycle of code execution. Therefore, in the case of high real-time requirements, a joint application of assembly and C is also required. But is this a delay program, but also need to use assembly to achieve? In order to find this answer, I did an experiment.

To implement the delay program in C language, the first thing that comes to mind is the loop statement commonly used by C. The following code is what I often see on the web:

Void delay2(unsigned char i)

{

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

}

How high can this code achieve? In order 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 how to know~~~ I don't know how inaccurate this delay program is. ~~~ Looking at the main four statements, you need 6 machine cycles. In other words, its accuracy is at most 6us, which does not count an lcall and a ret. If we list the length of the i-value root delay assigned when the function is called, it is:

i delay TIme/us

0 6

1 12

2 18

.....

Because the function call requires 1 call of lcall, the delay TIme is 2 more than the execution time of the function code. By the way, some friends wrote this code:

Void delay2(unsigned char i)

{

Unsigned char a;

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

}

Some might think this will 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

;---- 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 code it generates is the same. But this is really not a good habit. Because there is really no need to introduce extra variables here. We continue to discuss the topic. Some friends even used such code in order to have 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, don't even think about how horrible it is ##^%&%$. .. Let us 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 # 21

0035 ? C0010:

0035 22 RET

; FUNCTION _delay2 (END)

Oh, this can indeed be delayed for a long time~~~ but there is no precision at all.

So, can you achieve accurate delays with C? I changed the code a bit:

Void delay1(unsigned char i)

{

While(i--);

}

Because of the experience, the simpler C code can often produce the more concise machine code. So what is the result? Take the assembly code generated by it and take a look at it. With hope, I pressed the "Build target" button, and the result is 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 any doubt that the efficiency of the two methods is the same. It seems to stop here, because I really can't think of any room for simplification in the source program. It seems that I am coming to this conclusion: "If you need us-level delay precision, use assembly language when needed." But is this really true? I am still not willing. Because I don't believe that the famous Keil C compiler will not even use djnz? ? ? Because in fact, only one loop is needed in the body: djnz r7, loop. Nearly desperate (often people can break out in this situation, oh hehe~~~), I changed it a bit:

Void delay1(unsigned char i)

{

While(--i);

}

Absent-minded compilation, see 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)

The day ~~~ miracle has appeared. .. .. I think this program should already be able to meet the needs of the general situation. If you list a table:

i delay time/us

1 5

2 7

3 9

.....

When calculating the delay time, it has counted the time of 2 clock cycles taken by the lcall statement of the calling function.

Finally, the results are 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 have a deeper understanding of Keil C's compilation principle, you can use the appropriate grammar to optimize the generated C code. Even if this seems unlikely, there are some simple principles to follow: 1. Try to use unsigned data structures. 2. Try to use char type, it is not enough to use int, then long. 3. If possible, do not use floating point. 4. Use concise code, because by convention, concise C code can often generate concise object code (although not in all cases).

A Battery Charger supplies current to the base plate. Once the AGV is in charging position and the collector has made contact with the base plate, the AGV computer turns on the current.

The base plate has chamfered entry/exit ramps to facilitate smooth drive-on/drive-off of the spring loaded collector.


A battery charging contact consists of a base plate, which is installed on the floor or laterally at a bracket adjacent of the AGV runway, and a current collector which is installed on the vehicle.

 

Battery charging stations may be installed anywhere within the system where the production process allows the AGV to stop (staging areas, turn arounds, loading stops etc.). 

Single Phase Charging Contacts

Single Phase Battery Charging Contacts,60A Battery Charging Contacts,100A Battery Charging Contacts,Single Phase Charging Contacts

Xinxiang Taihang Jiaxin Electric Tech Co., Ltd , https://www.chargers.be