I use KeilC uVision4 for 89C51 MCU. By "Inline ASM", i can easily direct write value to R7 register, like this :
#pragma ASM MOV R7, #10 #pragma ENDASM
But now, there are some reason that i have to write to R7 register direct from C (not from inline ASM), eg:
R7 = 10;
Of course, the C compiler does not understand R7 as MCU Register (not Inline ASM).
-> Is there anyway to solve this problem. Please help me if anyone know, thanks a lot !
Well, I intend to write an Accurate "ASM Delay Routine" by "Inline ASM".
Let take the simplest delay code:
#pragma ASM MOV R1, #vR1 // Calculate Time spent From Here DJNZ R1, $ //2 Machine cycle (MCs) #pragma ENDASM
With 8051MCU, it spent : t1 = vR1 x 2 machine cycles (MCs)
Now, with 2 registers used:
#pragma ASM MOV R1, #vR1 MOV R2, #vR2 // Calculate Time spent From Here DJNZ R1, $ DJNZ R2, $-2 #pragma ENDASM
time spent : t2 = (t1 + 257vR2 - 256) x 2 MCs
Finally, with 3 registers used:
#pragma ASM MOV R1, #vR1 MOV R2, #vR2 MOV R3, #vR3 DJNZ R1, $ DJNZ R2, $-2 DJNZ R3, $-4 #pragma ENDASM
time spent : t3 = (t2 + 65793vR3 - 65792) x 2 MCs
Generraly, with n Register used, we can delay :
tn = (tn-1 + (Cn-1 + 1)vRn - Cn-1) x 2 MCs where :
Cn = 256^n + 256^(n-1) + ... + 256 + 0
Now i write a delay 3000MCs routine :
//8051 MCU #define Interval 3000 // Change here to change delay in MCs) #define C0 0 // Constant in formula above #define C1 256 // Constant #define C2 65792 // Constant #define t3 Interval/2 // DJNZ take 2 MCs, so divide by 2 for easier calculation #define vR3 ((t3+C2-2)/(C2+1)) // Calculate R3 value #define t2 (t3+C2-(C2+1)*vR3) #define vR2 ((t2+C1-1)/(C1+1)) // Calculate R2 value #define t1 (t2+C1-(C2+1)*vR2) #define vR1 ((t1+C0-0)/(C0+1)) // Calculate R1 value void Delay() { #pragma ASM MOV R1, #vR1 MOV R2, #vR2 MOV R3, #vR3 DJNZ R1, $ DJNZ R2, $-2 DJNZ R3, $-4 #pragma ENDASM }
Use in main routine :
void main() { Delay(); // Delay 3000 Machine Cycles }
=> For easier use, i should write a DelayMCs() Macro, eg :
#define DelayMCs(Interval) //Code Here???
Then, use this Macro like this :
void main() { DelayMCs(7000); }
=> That's my idea. But i don't know how to write this Macro. Can anyone help me build "Code Here" above. Thanks !
See: http://www.keil.com/forum/docs/thread16420.asp
It is very simple to do this as a separate assembly module - and, in my opinion, much to be preferred. See the above link.
By "Inline ASM", i can easily direct write value to R7 register, like this :
By "having a gun", i can easily shoot myself in the foot, like this
that something can be done, does not mean it is right to do it.
Erik
Thanks a lot for your helps. In a couple of days, I found the Macro should be as following :
// Delay Routine void RoutineDelayMCs(unsigned int interval) { #pragma ASM DJNZ R7, $ DJNZ R6, $-2 #pragma ENDASM } // Delay Macro -> Calculate interval to pass into Delay Routine #define MacroDelayMCs(interval) \ ((!(((interval)/2) - 257*((((interval)/2)+256-1)/257))) ? \ RoutineDelayMCs(257*((((interval)/2)+256-1)/257)) : RoutineDelayMCs(((interval)/2)+256-(((interval)/2)+256-1)/257)) // Offset Macro -> Calculate offset MCs of MOV, LCALL, RET ... command -> for better accuracy // Accuracy (if interval even, error = 0; if interval odd, error = -1 MCs // interval apply Range : 14 -> 131592 #define DelayMCs(interval) \ MacroDelayMCs(interval-10) void main() { DelayMCs(14); // This call delay 14 Machine Cycles DelayMCs(512); // This call delay 512 MCs DelayMCs(131592); // This call delay 131592 MCs }
It works fine ! Of course, manually hacking the registers is really Bad. I will put the RoutineDelayMCs in an ASM Module later.
Thanks again, friends !
... to make something simple & safe into something over-complicated and dangerous!
Yes.
For me, it would definitely take less time and effort to write a routine in assembler than to understand those macros!
... that an Engineer is someone who can make for a pound what any fool can make for a tenner.
I think we have just seen a proof by counter-example...
;-)
Maybe you understand my idea incorrectly. I've used MikroC for 8051. That compiler has a built-in Delay_ms() routines. When I want to delay 10ms, just code
Delay_ms(10);
The compiler will auto generate source code (to delay 10ms) :
_Delay_10ms : MOV R5, #8 MOV R6, #1 MOV R7, #247 DJNZ R7, PC-2 DJNZ R6, PC-4 DJNZ R5, PC-6 RET
Then, I wonder if I can write a Macro like that in KeilC. Just code "Delay_ms(x);" with x is any number you want. Anything wrong in my idea ?
Yes - trying to mess about with macros in 'C'!
When it comes to programming, there is not always a clear right or wrong.
However, some methods have clear advantages and some clear disadvantages. I can see no advantage in what you're trying to do - And I think others here would concur. It is simply cumbersome and potentially error prone.
A piece of complicated looking code does not mean that author is a genius. On the contrary, quite often it is the simple elegant pieces of code that are written by the smarter people.
There's nothing much simpler than a software delay loop - Why try to complicate it?
"I can see no advantage in what you're trying to do"
Absolutely: no advantage whatsoever; but plenty of clear disadvantages!!
"often it is the simple elegant pieces of code that are written by the smarter people."
Indeed - hence the "Engineer" comment earlier!
View all questions in Keil forum