Hi,
I was just wondering, what is efficient?
- declaring a temp variable inside ISR.
or
- declaring it as a global variable and use as temp variable in ISR.
Thanks,
K
Hi,
I was just wondering, what is efficient?
- declaring a temp variable inside ISR.
or
- declaring it as a global variable and use as temp variable in ISR.
Thanks,
K
Define "efficient".
Execution speed?
Code space?
Source size?
BTW, if you declare globally, you would still need VOLATILE attribute.
Jim
BTW, if you declare globally, you would still need VOLATILE attribute.
use as temp variable in ISR
How wide is the variable?
"How long is a piece of string?"
If you are cycle-counting trying to get a 24-cycle ISR down to 20 cycles, then (IME) the "most efficient" in terms of cycle count is to have dedicated registers to use when in the ISR. That may not be practical for your toolchain.
If not cycle-counting, then I'd say that the size and cycle count would be about the same for the global (or static) variables versus locals. That would be for, say, about 4 bytes worth.
Why not try yourself and see in the asm listing/disassembly?
Locals need some way to allocate space for them on the stack, so there will be some extra handling in prologue/epilogue.
See also Lee's comment on register allocation. This path is not trivial and laid with gotchas. But if you are that concerned about efficiency, you are bound to go for assembler - not [only] for speed, but mainly for control.
JW
Thank you all for sharing your points.
I am using an ATmega32.
I have all three timers used, 1. timer1 for PWM for two motors timer0 for Servo PWM, timer2 for PID sampling.
timer2 has a overflow interrupt at 72 clicks/sec rate. This ISR reads the encoder value into a temp variable (an int8_t) and then pass that along PID function for the calculations (right now I am having the temp variable as global so I do not pass it as parameter to PID function).
Then there are set of lines to construct a string in a tempstringbuffer[40] that send to PC serial port via (interrupt based UART). This tempstringbuffer is defined global as well.
I just had a thought that keeping them global will permanently consume those many bytes from the RAM.
but if I put them as local to ISR they will be allocated, used and then free the RAM once the ISR is finished, and make it available for other variables.
But then this ISR is called 72 times a second, so the allocation and freeing happen so many times, and consume so many CPU cycles.
Thus I wonder what would be my preferred way.
wek - I am not very comfortable with asm yet, never tried it before. So if I take your words, I should be fine with globals.
JohanEkdahl - You prefer OP's way (I am sure I do not know OP). i.e. use local variables.
ka7ehk - I think I am concerned about speed of execution. My code is approx. 9 KB of the 32KB limit on ATMEGA32 so code space and source size are no worries as of now.
Thanks
K
I just had a thought that keeping them global will permanently consume those many bytes from the RAM.
but if I put them as local to ISR they will be allocated, used and then free the RAM once the ISR is finished, and make it available for other variables.
Remember that an interrupt can occur at any time in your code.
It could happen to be executing in the most deeply nested function with the highest use of stack frame automatics.
So what happens if the interrupt fires and then tries to make room for some more (ISR owned) automatics and there is not enough RAM to do so?
Disaster - that's what.
So whether the variables themselves are dynamically created on the stack or permanently allocated in .bss/.data doesn't matter because you have to ensure that there's always room for their allocation no matter what.
In which case you might as well allocate them globally because that way you are guaranteed the ISR will have room for all the variables it uses.
HOWEVER if your ISR() just has a few ints/chars/uint8/16's defined at the top there about a 90-100% chance that they'll never be allocated in RAM anyway but will be optimised into some of the 32 machine registers. This cannot happen if they are global.
So I'd suggest you keep anything local to the ISR() that is "small" as stack frame automatics and anything "large" (buffers/arrays) global to guarantee there will be RAM to accommodate them at any time (as there has to be).
so the allocation and freeing happen so many times, and consume so many CPU cycles.
BTW you said:
Then there are set of lines to construct a string in a tempstringbuffer[40] that send to PC serial port via (interrupt based UART).
(*) this shows the difference in an ISR() that uses a stack frame and one that doesn't:
#include#include #define STACK #ifndef STACK char n; char buff[10]; #endif ISR(INT0_vect) { #ifdef STACK char n; char buff[10]; #endif for (n=0; n<10; n++) { buff[n] = PINB; } PORTD = buff[3]; PORTD = buff[7]; } int main(void) { while(1) { } }
With STACK defined:
__vector_1: //==> ISR(INT0_vect) { push r1 push r0 in r0,__SREG__ push r0 clr __zero_reg__ push r18 push r24 push r25 push r30 push r31 push r28 push r29 in r28,__SP_L__ in r29,__SP_H__ sbiw r28,10 out __SP_H__,r29 out __SP_L__,r28 /* prologue: Signal */ /* frame size = 10 */ /* stack size = 20 */ .L__stack_usage = 20 movw r30,r28 adiw r30,1 //==> ISR(INT0_vect) { movw r24,r28 adiw r24,11 .L2: //==> buff[n] = PINB; in r18,0x16 st Z+,r18 //==> for (n=0; n<10; n++) { cp r30,r24 cpc r31,r25 brne .L2 //==> PORTD = buff[3]; ldd r24,Y+4 out 0x12,r24 //==> PORTD = buff[7]; ldd r24,Y+8 out 0x12,r24 /* epilogue start */ //==> } adiw r28,10 in __tmp_reg__,__SREG__ cli out __SP_H__,r29 out __SREG__,__tmp_reg__ out __SP_L__,r28 pop r29 pop r28 pop r31 pop r30 pop r25 pop r24 pop r18 pop r0 out __SREG__,r0 pop r0 pop r1 reti
and without STACK defined:
__vector_1: //==> ISR(INT0_vect) { push r1 push r0 in r0,__SREG__ push r0 clr __zero_reg__ push r24 push r25 push r30 push r31 /* prologue: Signal */ /* frame size = 0 */ /* stack size = 7 */ .L__stack_usage = 7 //==> for (n=0; n<10; n++) { sts n,__zero_reg__ rjmp .L2 .L3: //==> buff[n] = PINB; in r25,0x16 mov r30,r24 ldi r31,0 subi r30,lo8(-(buff)) sbci r31,hi8(-(buff)) st Z,r25 //==> for (n=0; n<10; n++) { subi r24,lo8(-(1)) sts n,r24 .L2: //==> for (n=0; n<10; n++) { lds r24,n cpi r24,lo8(10) brlo .L3 //==> PORTD = buff[3]; lds r24,buff+3 out 0x12,r24 //==> PORTD = buff[7]; lds r24,buff+7 out 0x12,r24 /* epilogue start */ //==> } pop r31 pop r30 pop r25 pop r24 pop r0 out __SREG__,r0 pop r0 pop r1 reti
Most of the difference is to do with how the variables are accessed. Not how they are created/destroyed.
Most of the difference is to do with how the variables are accessed. Not how they are created/destroyed.
Thanks Clawson, it makes a lot more sense now (I learnt MCUs by trail and error and you know the theory in and out, that makes the difference, probably I should start reading from A to Z rather than picking up topics).
Regards,
K
HOWEVER if your ISR() just has a few ints/chars/uint8/16's defined at the top there about a 90-100% chance that they'll never be allocated in RAM anyway but will be optimised into some of the 32 machine registers. This cannot happen if they are global.
So I'd suggest you keep anything local to the ISR() that is "small" as stack frame automatics and anything "large" (buffers/arrays) global to guarantee there will be RAM to accommodate them at any time (as there has to be).
Making a local global or static will not make a stack crash less likely.
It might even become more likely and harder to diagnose.
If there is a buffer other functions do not need to see a,
there is probably a design error.