Hello, Dears First I say,I'm afraid I don't have good skills writing in English.
Please see my code. I defined my inline function code at seva.c file. This is the seva.c file
#include "seva.h" ... ... __inline void delay_ms(uint32_t ms) { register uint32_t i = ms; for ( ; i ; --i ) { delay_us(250); delay_us(250); delay_us(250); delay_us(250); } } __inline void delay_us(uint32_t us ) { register uint32_t i = us; for (; i; --i) { __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); } } ... ...
and I declare function prototypes at seva.h file. seva.h
... ... __inline delay_us(uint32_t us); __inline delay_ms(uint32_t ms);
when I try to call these delay functions in main.c file. main.c
#include "seva.h" ... ... void main(void) { P04 = 1; delay_us(100); P04 = 0; }
As you guess, It cause L6218E error. and I found http://www.keil.com/forum/13177/ this column. I defined every my inline code from seva.c to seva.h file. and It works well(means no compile error).
But there is one thing still confused.. See, Next code. seva.c
... ... __inline void delay_loop1( ) { register uint32_t i = 500; for (; i; --i) { __NOP(); } } ... ...
seva.h
... ... __inline void delay_loop1(void); ... ...
and I call this function at main.c file like above. but It doesn't have any comfiler errors. As I think, the difference delay_loop1 function and delay_ms function is only whether has parameter or not.
please tell me why delay_loop1 function compile without error, and why inline function has parameter occur errors.
Did you understand what I try to saying? I think I wrote too difficult.
Thanks for reading.
The compiler is free to make a decision if it will honor the inline keyword or not.
If it does not honor the keyword, then it will treat the function as a normal function and will place the compiled body of the function in the object file. So it will be possible to link with the function.
If it does honor the keyword, then it doesn't need to store any compiled function body in any object file, since it will directly inline the instructions where you "call" the function.
If your header file doesn't include the actual implementation but only a function prototype, then the compiler will obviously not be able to inline the code where you call the function.
So when you play with inline keywords, you should have the full implementation in the header file so the compiler can produce correct output both if it decides to inline the function or not. If you only want to use the inline function in a single source file, then you obviously doesn't need to have the implementation in a header file - it's enough that it's in the same file where you will use the inline function.
Next thing here is that in this code, the compiler can decide that it doesn't see any side effects. So it might decide that it never need to implement the function at all. Or since it knows the iteration count is always 500, it may optimize the loop by unrolling the loop in which case it may decide the code will be too large to inline.
__inline void delay_loop1( ) { register uint32_t i = 500; for (; i; --i) { __NOP(); } }
Note that the compiler may decide that instead of looping 500 times with one __NOP() it might instead go for a 10 loop iterations of 50 __NOP().
So you really shouldn't rely too much on software loops for delays, since you can't control what optimization the compiler may do - optimizations that will greatly affect how fast the loop will run. Or optimizations that might totally remove the loop giving you a zero-us delay.
Thanks Sir! Thanks to your kind explanation, my head be cleared. To demonstrate what you said, I used __forceinline instead of __inline in delay_loop1 function.
__forceinline void delay_loop1( ) { register uint32_t i = 500; for (; i; --i) { __NOP(); } }
Can I ask one more question? This is about optimizations in my delay function.
How about using volatile qualifier? Does it still non rely delay function??
__inline void delay_loop1( ) { volatile register uint32_t i = 500; for (; i; --i) { __NOP(); } }
Thanks,
Most probably, the compiler has already added a magic marker to the __NOP() intrinsic to make sure that the compiler knows that the nop instruction should be seen as required even if it isn't seen to generate any side effect.
The reason the compiler does support the __NOP() intrinsic, is to allow extremely short delays between two other instructions. Some ARM chips with an external interrupt handler required one or more nop to delay the processor until enable/disable interrupt had been effectuated because of the signaling delay between core and interrupt controller.
And it's also common when togging I/O pins that there might be a need for one or more clock cycles of setup/hold times.
But the important thing is that you can't much control what code the compiler produces for a "dumb" busy-loop. So it's better to use a timer for your delays. Either with interrupt or the code may busy-loop polling the current timer value or timer zero-count flag.