I use KeilC uVision4 for 89C51 MCU. I use "Inline ASM Code", like this :
void main() { #pragma ASM MOV R7, #(80000/40000) #pragma ENDASM }
-> When COMPILE, ASM result file give a wrong value to R7
MOV R7, #0x00
But when i change like this :
void main() { #pragma ASM MOV R7, #(60000/30000) #pragma ENDASM }
-> The COMPILER give correct value to R7
MOV R7, #0x02
=> Seem, there must be a restriction when the ASM Compiler calculate (80000/40000) ? => And is there any way to pass this restriction. Please help me ! Regards !
Well, maybe AX51 compiler can pass this restriction.
When i first create my project, I choose "LX51 & AX51 instead of old BL51 & A51", then everything works fine. Thanks !
I don't know your chip at all, but it is striking that 60000 is just under an unsigned 16 bit integer value limit (2^16 - 1), 80000 slightly above...
See: http://www.keil.com/forum/docs/thread16416.asp
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 !
In my opinion, this would be a perfect place to use a little assembly module.
"A little ASM Module", did you mean : write that Delay routines in a seperate ASM file ??? I think we have to use MACRO here ???
Yes, in an asm file.
Macros, as always, are optional.
But i don't want to use MCU to calculate vR3, vR2, vR1, let the Compiler do that, then just add the result of vR3, vR2, vR1 to the Delay() Routines.
void Delay() { #pragma ASM MOV R1, #vR1 // vR1 calculate from Compiler MOV R2, #vR2 // vR2 calculate from Compiler MOV R3, #vR3 // vR3 calculate from Compiler DJNZ R1, $ DJNZ R2, $-2 DJNZ R3, $-4 #pragma ENDASM }
I mean that, in the main routine, i call Delay MACRO :
void main() { DelayMCs(3000); // 3000 are numeric const, not variable }
then the compiler scan and compile this MACRO to something like :
#define Interval 3000 #define t3 Interval/2 #define vR3 ((t3+C2-2)/(C2+1)) #define t2 (t3+C2-(C2+1)*vR3) #define vR2 ((t2+C1-1)/(C1+1)) #define t1 (t2+C1-(C2+1)*vR2) #define vR1 ((t1+C0-0)/(C0+1)) Delay();
So vR1, vR2, vR3 is calculated from Compiler Just "Delay();" is compile to code to download to MCU
I totally agree!
That is, a separate Assembly source file; eg, delay.s
As already noted, manually hacking the registers used internally be 'C' is a Really Bad Idea!
Instructions on how to do exactly this here: www.8052.com/.../149030
Simples!
Thanks a lot for your helps. In a couple of days, I found that 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 }
And ... It works fine ! Of course, manually hacking the registers is really Bad. I will put the RoutineDelayMCs in an ASM Module later.
Thanks again ... all friends, Regards !
View all questions in Keil forum