|
|
View previous topic :: View next topic |
Author |
Message |
arrow
Joined: 17 May 2005 Posts: 213
|
|
Posted: Thu Jun 30, 2005 3:11 am |
|
|
Hi Andrew
Thank you- I shall try what you suggest
Can you please explain to me why the interrupts have to be disabled in the first place? (Do they absolutely have to be disabled?)
I am assuming that the interrupts will not effect the i2c_write() command if it comes when its being executed?
Regards
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Jun 30, 2005 3:50 am |
|
|
You are using "long" variables. These are 2 byte values. The problem occurs because to examine and/or modify these values in the mainline., the comiler must first evaluate the low order byte (usually) and then the high order byte. Depending on the operatio it can take several cycles to complete and can result in the incorrect modification of the variable.
Consider the following example. You have a value that is 0x00ff and the mainline is about to increment it by one. You would expect the result to be 0x0100. First the compiler increments the low order byte. The value is now 0x00 with the carry flag set. The compiler then increments the high order byte. and the final result is 0x01 (high order byte) 0x00 (low order byte). But what happens if an interrupt occurred immediately after the PIC incremented the low order byte? The interrupt handler sees the low order byte = 0x00 (the new value) and it sees the high order byte = 0x00 (not yet incremented) so the interrupt handler sees the long value of the 0x0000. In your specific example it is the other way around. You are testing a value set in the interrupt handler but the results are the same - your comparison is meaningless.
Do interrupts HAVE to be disabled? No. You are ways to code around the problem. For example is you used an 8 bit integer instead of a 16 bit then you would not have hit this problem.
HAving said that, the amount of time you are "losing" is 2us per loop. You may be adding 20us of jitter with respect to the INT_RB event but, compared with your earlier code, this is far superiour. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
Last edited by asmallri on Thu Jun 30, 2005 4:10 am; edited 1 time in total |
|
|
arrow
Joined: 17 May 2005 Posts: 213
|
|
Posted: Thu Jun 30, 2005 3:59 am |
|
|
Hi Andrew
Thank you very much!!-- I have tried what you suggested and it works pretty well.
I am not too worried about the 2us jitter. I am not certain why you say I get 20us jitter in the INT_RB event, but in any event both times are insignificantly small for me.
I noticed that by removing the first printf in the original code, things also work fine, and I dont miss any interrupts.
I will try and convert the int long to int as well- just to clean things up further.
I really appreciate all your help.
All the best
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Jun 30, 2005 4:14 am |
|
|
The lost 2us comes from having to add the enable and disable interrupt instruction in your for loop. Each instruction takes one cycle therefore adding 2us to the time it takes to run through the loop.
The 20us jitter occurs because when interrupts are enables, the interrupt event immediately interrupts the processor. Bu now with interrupts disabled in the mainline, the wirst case scenario is an interrupt event occurs immediately after the interrupt is disabled but the PIC ignores it but the interrupt is disabled (masked). It will take the PIC about 20us to execute the code between the disable interrupt instruction and the enable interrupt instruction. This is where the 20us jitter comes from. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
arrow
Joined: 17 May 2005 Posts: 213
|
|
Posted: Thu Jun 30, 2005 4:36 am |
|
|
Hi Andrew
Thank you for all your help and explanations! It has helped me tremendously.
Just to make absolutely certain- if I have one state of my B port, and then I go into the disable/ enable interrupts.
When I enable interrupts, I have another state of my B port- will I miss the event that has occured? (I think I will).
Also, if I am executing a line of code, and an interrupt occurs, does that mean that I first finish that line, and then go to the interrupt subroutine?
Regards
arrow |
|
|
Ttelmah Guest
|
|
Posted: Thu Jun 30, 2005 5:10 am |
|
|
Key thing is to use 'disable_interrupts(GLOBAL)'.
Basically, if you use this, is disables the flag, preventing the CPU from responding to any interrupt that occurs. However the individual interupt flags will still be set. Hence if you re-enable the global interrupt as soon as you have finished, at that moment, the chip will then call the interrupt handler.
Interrupt 'events' are checked in the first clock cycle of an instruction (not a line of code). You will enter the interrupt routine, the instruction after the event occured. However the actual interrupt 'handler', then takes perhaps 20 to 30 instructions to save the registers, and call the individual handler routines, so you do not actually 'reach' the interrupt code till quite a few instruction times latter.
As an example of how to disable interrupts, I wrote this (quite a while ago), when the compiler had a fault with the way it was disabling interrupts in a 'block move' routine, if it was used in both an interrupt handler, and outside. I therefore have to disable interrupts if the routine is called outside the interrupt handler, but leave them unchanged if the routine is called inside. I defined the internal processor registers and bits needed (particularly 'GIE', which is the 'global interrupt enable' bit), and check this before deciding what to do.
Code: |
//Replacement for the memcpy routine, including interrupt protection
#inline
void mymemcpy(char *dest,char *source, int num)
{
if (GIE)
{
disable_interrupts(GLOBAL);
#ASM
MOVFF dest,FSR0L
MOVFF &dest+1,FSR0H
MOVFF SOURCE,FSR1L
MOVFF &source+1,FSR1H
LOOP:
MOVFF POSTINC1,POSTINC0
DECFSZ num
GOTO LOOP
#ENDASM
enable_interrupts(GLOBAL);
}
else
{
#ASM
MOVFF dest,FSR0L
MOVFF &dest+1,FSR0H
MOVFF SOURCE,FSR1L
MOVFF &source+1,FSR1H
LOOP1:
MOVFF POSTINC1,POSTINC0
DECFSZ num
GOTO LOOP1
#ENDASM
}
}
|
With this, interrupts that have occured during the routine, are responded to as soon as the routine finishes, and if the interrupts are already disabled when it is called, they are left disabled when the routine exits.
In this particular case, it was quicker to repeat the move code in both the 'disabled', and 'non disabled' paths. However with larger blocks of code, the answer would be to save the flag, and then test again at the end to see whether to enable/disable.
Seperately, as another example, I write float data to an LCD, which is modified inside an interrupt handler. Hence for these, I use:
Code: |
float get_value(struct data_block reading) {
float temp;
INTOFF;
temp=reading.inputval;
INTON;
}
|
Here I have two 'macros' defined, just to make things easier, as:
#define INTON ENABLE_INTERRUPTS(GLOBAL)
#define INTOFF DISABLE_INTERRUPTS(GLOBAL)
Note however that I _don't use:
Code: |
INTOFF;
printf("%5.2f\n",reading.inputval);
INTON;
|
The reason is that the first example, disables the interrupts for just a couple of instruction times, while the data is copied from one location to another. Even the 'block move', for the block sizes I use (typically 16 bytes), only disables the interrupts for perhaps 40 instruction times. However using a printf, would disable the interrupts for the entire period while the data is converted to string format, and then for the serial transmission period (except for the last two characters, assuming the hardware UART is used). Probably something in the order of 4mSec, at 9600bps... This is long enough, that multiple interrupts could occur, and all but one of these will be missed...
Best Wishes |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Jun 30, 2005 5:13 am |
|
|
Quote: | Just to make absolutely certain- if I have one state of my B port, and then I go into the disable/ enable interrupts.
When I enable interrupts, I have another state of my B port- will I miss the event that has occured? (I think I will). |
No. You have disabled the interrupt but the RBIF interrupt flag is set by the event. When you subsequently enable interrupts, control will pass immediately to the interrupt handler because the flag is set.
Quote: | Also, if I am executing a line of code, and an interrupt occurs, does that mean that I first finish that line, and then go to the interrupt subroutine? |
No. A single line of C code can tranlate into a lot of assembler statements. The current assembler instruction will be executed by the PIC and then control will pass to the interrupt handler. It is possible that multiple interrupts can occur and be processed by the interrupt handlers while a single line of C code is being executed. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|