|
|
View previous topic :: View next topic |
Author |
Message |
arrow
Joined: 17 May 2005 Posts: 213
|
Interrupting an Interrupt? |
Posted: Sat Jun 25, 2005 2:13 am |
|
|
Hi
I have a newbie question:
If I am executing the code wihtin an interrupt, and an event occurs that will trigger the same interrupt, will
(a) the subroutine finish the interrupt and then restart?
(b) will the subroutine be suspended and the interrupt subroutine start from the beginning?
(c) the 2nd interrupt be ignored?
(d) something else?
Thank you and
Regards
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Sat Jun 25, 2005 2:26 am |
|
|
The PIC automatically disables interrupts of the same level (priority level). This is note the same thing as interrupt priority with CCS uses to determine in which order to check for interrutps in the default interrupt handler.
The 18F support high and low prioity interrupts. It is possible for a high priority interrupt to interrupt a low priority interrupt with this family of processor.
In your specific example, when the same interrupt event occurs, and therefore the same priority, the first interrupt disables the interrupts. The interrupt handler will eventually clear the interrupt condition (except when noclear is used). Depending on the type of interrupt this means the second interrupt may be lost. Take for example interrupt on change, this gets cleared by reading the port. If the port changes twice before the port was read then the second interrupt interrupt event would be lost. If however the second interrupt event occured inside the interrupt handler but after the port had already been read, then the interrupt on change interrupt flag will be set. When the handler returns to the main program, the PIC will be immediately interrupted as a result of the second event that occurred inside the handler. _________________ 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: Sat Jun 25, 2005 2:38 am |
|
|
Hi Andrew
Thank you for your thorough explanation. Could you please tell me how to "read a port"?
I have the following code, and just to make absolutely sure:
#int_rb
void int_rb_isr(void){
if((port_b>>4)==0X0F){
ChX = get_timer1();
set_timer1(0);
}
if(port_b>>4==0XE){
ChXH = get_timer1();
}
}
if a second interrupt occurs while I am inside this subroutine, will it be called again?
Also, I am assuming that I can call this interrupt routine as a normal function as well-- is this right?
Thank you once again, and
Regards
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Sat Jun 25, 2005 4:11 am |
|
|
Quote: | Could you please tell me how to "read a port"? |
Both your referenced to port_b read the port. You probably did not mean to do this. You may have meant to do this..
Code: |
#int_rb
void int_rb_isr(void)
{
// an interrupt on change has occurred or we would not be here
// now read the port and clear the initial IOC event
// we need to read the port to enable the RBIF interrupt flag
// to be clearded
int my_port = port_b;
// if the port changes again then a second IOC event occurs
if((my_port>>4)==0X0F)
{
ChX = get_timer1();
set_timer1(0);
}
if(my_port>>4==0XE)
ChXH = get_timer1();
// the CCS compiler will now automatically attempt to clear the RBIF flag
// however is the second IOC event occured after the port was
// initially read then the PIC will prevent the bit being cleared
// in this case the interrupt handler will be invoked again on
// return from interrupt
}
|
Quote: | if a second interrupt occurs while I am inside this subroutine, will it be called again? |
If a second interrupt event occurs after you have done the my_port = port_b then another interrupt will occur when the handler has finished
Quote: |
Also, I am assuming that I can call this interrupt routine as a normal function as well-- is this right? |
No - you could stuff up the stack depending on the implementation. If you want to invoke this routine then you can fake the interrupt on change event which will cause the processor to be interrupted. To do this you set the RBIF flag in the INTCON register (please check the actual register, I don't know which PIC you are using). Obviously you have to have enabled the interrupt for this to happen.
[/quote][/code] _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
Last edited by asmallri on Sat Jun 25, 2005 7:32 pm; edited 1 time in total |
|
|
Guest
|
|
Posted: Sat Jun 25, 2005 5:55 am |
|
|
arrow wrote: | Hi Andrew
Thank you for your thorough explanation. Could you please tell me how to "read a port"?
I have the following code, and just to make absolutely sure:
#int_rb
void int_rb_isr(void){
if((port_b>>4)==0X0F){
ChX = get_timer1();
set_timer1(0);
}
if(port_b>>4==0XE){
ChXH = get_timer1();
}
}
if a second interrupt occurs while I am inside this subroutine, will it be called again?
Also, I am assuming that I can call this interrupt routine as a normal function as well-- is this right?
Thank you once again, and
Regards
arrow |
Depends on the interrupt, and the interrupt _flag_.
Now in the case of the default handler, the flag for the interrupt you are using, the flag is cleared when you exit the interrupt handler. Now if you add the keyword 'noclear' to the interrupt declaration, and then as soon as you have read port_B, clear the interrupt flag yourself, then when the code exits from the global handler, the routine will be called again, if the flag has been set inside the handler.
The other interrupt flags are not cleared by the handler, unless their own handler is caled, so will be set, 'waiting processing', when you exit.
However this incurs the timing overhead of calling the global handler again. The fastest handling is done like this:
Code: |
#int_rb noclear
void int_rb_isr(void){
int8 bval;
do {
bval=port_B>>4;
//remember you must declare port_B somewhere
//Only do the rotation once, and read the port once. This is quicker.
BCIF=0;
//This is the 'B changed interrupt flag' - will need a #bit
//definition to suit your processor
if(bval==0X0F){
ChX = get_timer1();
set_timer1(0);
}
else if(bval==0XE){
ChXH = get_timer1();
}
}
while (BCIF);
}
|
This will loop back inside the interrupt handler, if the port has changed again after the flag was cleared. The 'downside', is that if the port is continuously changing very fast, the code can get locked inside the handler routine...
Best Wishes |
|
|
arrow
Joined: 17 May 2005 Posts: 213
|
|
Posted: Tue Jun 28, 2005 1:48 am |
|
|
Hi
Thank you for your suggestions- I have implemented them, and things work well when there is a square wave comming into PIN_B4.
I am getting a weird result though when I write to external eeprom. This I do in the main routine in an infinite while loop.
I use i2c.
Is it possible that the INT_RB is somehow disrupting the i2c write commands. (This occurs sometimes- and not on every write- so I assume that it occurs when the write and the INT_RB coincide).
Any help on this would be greately aprpeciate.
Regards
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Tue Jun 28, 2005 2:38 am |
|
|
Yes it is possible and common to have an interrupt handler to adversly affect the main program but without viewing I can't offer much assistance. In interrupt handlers you have to be careful that you do not trash variables used by the mainline. For example, several procedures and functions are likely to be non-reentrant so you should not call procedures in the mainline that you also call in the interrupt handler.
If you change port directions (output to input for example) then what happens to peripherals connected to the outputs? Do the lines float? will this been seen by these peripherals as a level change etc etc. _________________ 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: Tue Jun 28, 2005 2:44 am |
|
|
Hi Andrew
Thank you for your reply. I am including the portion of the code that I think is relevant.
I am most concerned to find out what will happen if during the i2c write I get an INT_RB. Can you please help me with this?
Thank you once again for all your help.
Regards
arrow
Code: | //==================================================
void init_ext_eeprom() {
output_float(eeprom_scl);
output_float(eeprom_sda);
}
//--------------------------------
void pageWrite_ext_eeprom(long int address, byte mask){
int ik;
START:
i2c_start();
if(i2c_write(0xa0 | mask)!=0) //Device must ACKNOWLEDGE
goto START;
i2c_write(address>>8); //High Byte of Address
i2c_write(address&0X00FF); //Low Byte of Address
for(ik=0;ik<PAGE_SIZE;ik++){ //Write Page to EEPROM
i2c_write(dat[ik]);
}
i2c_stop();
}
//--------------------------------
byte read_ext_eeprom(long int address, byte mask) { //RANDOM Read Data
byte data;
i2c_start();
i2c_write(0xa0 | mask);
i2c_write(address>>8);
i2c_write(address&0X00FF);
i2c_start();
i2c_write(0xa1 | mask);
data=i2c_read(0);
i2c_stop();
return(data);
}
//==================================================
main() {
int i;
int icnt=0;
byte mask=0b00000000; //+++Chip "0"
long int noPages = EEPROM_SIZE/PAGE_SIZE;
long int iPage=0;
noPages = 1000;
//-------------------------------
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
//-------------------------------
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
//-------------------------------
//***StartUp- Make sure that Period != 0
Period = 0;
ChXH = ChYH = 0;
delay_ms(100);
icnt=0;
//-------------------------------
while(TRUE){
//***Store data to array
if(bShownX==0){
dat[icnt] = (Xaccel>>8)&0x00FF;
icnt++;
dat[icnt] = Xaccel&0x00FF;
icnt++;
printf("+%x%x ",dat[icnt-2], dat[icnt-1]);
bShownX = 1;
if(icnt==PAGE_SIZE){
pageWrite_ext_eeprom(iPage*PAGE_SIZE, mask);
iPage++;
if(iPage==noPages){
break;
}
icnt = 0;
}
}
}
//------------------------------
disable_interrupts(INT_RB);
disable_interrupts(GLOBAL);
//------------------------------
//***READ BACK CYCLE
for(iPage=0;iPage<noPages;iPage++){
//+++Read Chip "0"
mask = 0b00000000;
dat[0] = read_ext_eeprom(iPage*PAGE_SIZE, mask);
dat[1] = read_ext_eeprom(iPage*PAGE_SIZE+1, mask);
printf("+%x%x ", dat[0],dat[1]);
dat[0] = read_ext_eeprom(iPage*PAGE_SIZE+2, mask);
dat[1] = read_ext_eeprom(iPage*PAGE_SIZE+3, mask);
printf("+%x%x ", dat[0],dat[1]);
}
//====================================
}
//=======================================================
#int_rb
void int_rb_isr(void){
//***Read B Port only once
int Bport;
Bport = port_b;
//***X CHANNEL = B4 JUST GONE HIGH
if(bit_test(Bport,4) && priorStateX==0){
Period = get_timer1();
set_timer1(0);
ChX0 = 0;
PriorStateX = 1;
}
//***X CHANNEL = B4 JUST GONE LOW
if(!bit_test(Bport,4) && priorStateX==1){
ChXH = get_timer1()-ChX0;
PriorStateX = 0;
Xaccel = ChXH;
bShownX = false;
}
}
//================= |
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Tue Jun 28, 2005 3:24 am |
|
|
A few comments.
Code: |
//-------------------------------
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
//-------------------------------
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
//-------------------------------
//***StartUp- Make sure that Period != 0
Period = 0;
ChXH = ChYH = 0;
delay_ms(100); [ |
You have enabled the RB interrupt before you have finished your initialization. You did not clear the interrupt flag before enabling interrupts so there is a high probability that an interrupt will occur immediately you have enable interrupts.
Code: |
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
//-------------------------------
//***StartUp- Make sure that Period != 0
Period = 0;
ChXH = ChYH = 0;
//-------------------------------
clear_interrupt(INT_RB);
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
//-------------------------------
delay_ms(100);
|
Also there are variables that you are changing both inside and outside the interrupt handler. This can lead to problems when an interrupt occurs half way into processing the relevant set instruction in the mainline resulting in "states" being missed. If for some reason you cannot afford to disable interrupts (for example low latency critical timing applications) then, you might want to consider using flags when one part of the code (such as the mainline sets it) and another part (the interrupt handler) clears it.
However in your case I suggest you use disable_interrupt(INT_RB) immedaitely before you change the contents of the variable that will be modified or changed in the interrupt handler and then enable the interrupt immediately after you have modified the variable. _________________ 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: Tue Jun 28, 2005 4:03 am |
|
|
Hi Andrew
Thank you- I think I understand.
You would disable the INT_RB just before you use the eeprom_write, and re-enable it just after the eeprom is written to-- is this correct?
Also is there an equivalent command for:
Code: | clear_interrupt(INT_RB); |
(I am using a very old compiler)
All the best
arrow |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Tue Jun 28, 2005 4:25 am |
|
|
I don't know what PIC you are using so you will have to check the bits yourself. Here is how you would do this on an 18F processor
Code: |
bit_clear(INTCON,0); // clear_interrupt(INT_RB);
|
Before diabling and re-enabling interrupts for the EEPROM read and write, try disabling interrupts before you change any variables that are also modified in the interrupt handler and reenable them when you are finished.
As the code extract you have shown is not the full code it is also possible that the EEPROM problem is somewhere else. For example, have you selected fast_io? _________________ 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: Tue Jun 28, 2005 4:44 am |
|
|
Hi Andrew
My entire code- without the modifications you write about is attached below.
I am writting/ reading from an external eeprom. I have not used fast_io (is that important?).
What I observe is that when the data is computed, the results come out ok (printf).
When I read the data back- the data off the eeprom is not exactly what the first printf shows. The first 80 points (or so) are exactly the same, and then the data is not exaclty the same.
I am trying to get all the points to be the same.
Would you have any suggestions?
All the best
arrow
Code: |
#include <16F873.H>
#fuses LP,PROTECT,NOWDT,NOBROWNOUT
#use delay(clock=4000000)
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7,ERRORS)
//==================================================
// FUNCTION PROTOTYPES
void int_rb_isr(void);
//------------------------------------------------
// GLOBALS
long int ChXH, ChYH;
long int ChX0, ChY0;
long int Period;
long int Xaccel, Yaccel;
short bShownX, bShownY;
short priorStateX, priorStateY;
//==================================================
#byte port_b = 6
//==================================================
#define EEPROM_SDA PIN_C4
#define EEPROM_SCL PIN_C3
#define EEPROM_SIZE 32000
#define PAGE_SIZE 4
#define NO_EEPROMS 1
#use i2c(master,sda=EEPROM_SDA, scl=EEPROM_SCL, FAST) //NOFORCE_SW )
int dat[PAGE_SIZE];
//==================================================
void init_ext_eeprom() {
output_float(eeprom_scl);
output_float(eeprom_sda);
}
//--------------------------------
void pageWrite_ext_eeprom(long int address, byte mask){
int ik;
START:
i2c_start();
if(i2c_write(0xa0 | mask)!=0) //Device must ACKNOWLEDGE
goto START;
i2c_write(address>>8); //High Byte of Address
i2c_write(address&0X00FF); //Low Byte of Address
for(ik=0;ik<PAGE_SIZE;ik++){ //Write Page to EEPROM
i2c_write(dat[ik]);
}
i2c_stop();
}
//--------------------------------
byte read_ext_eeprom(long int address, byte mask) { //RANDOM Read Data
byte data;
i2c_start();
i2c_write(0xa0 | mask);
i2c_write(address>>8);
i2c_write(address&0X00FF);
i2c_start();
i2c_write(0xa1 | mask);
data=i2c_read(0);
i2c_stop();
return(data);
}
//==================================================
main() {
int i;
int icnt=0;
byte mask=0b00000000; //+++Chip "0"
long int noPages = EEPROM_SIZE/PAGE_SIZE;
long int iPage=0;
noPages = 1000;
//-------------------------------
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
//-------------------------------
//***StartUp- Make sure that Period != 0
Period = 0;
ChXH = ChYH = 0;
delay_ms(100);
icnt=0;
//-------------------------------
enable_interrupts(INT_RB);
enable_interrupts(GLOBAL);
//-------------------------------
while(TRUE){
//***Store data to array
disable_interrupts(INT_RB);
if(bShownX==0){
dat[icnt] = (Xaccel>>8)&0x00FF;
icnt++;
dat[icnt] = Xaccel&0x00FF;
icnt++;
printf("+%x%x ",dat[icnt-2], dat[icnt-1]);
bShownX = 1;
if(icnt==PAGE_SIZE){
pageWrite_ext_eeprom(iPage*PAGE_SIZE, mask);
iPage++;
if(iPage==noPages){
break;
}
icnt = 0;
}
}
enable_interrupts(INT_RB);
}
//------------------------------
disable_interrupts(INT_RB);
disable_interrupts(GLOBAL);
//------------------------------
//***READ BACK CYCLE
for(iPage=0;iPage<noPages;iPage++){
//+++Read Chip "0"
mask = 0b00000000;
dat[0] = read_ext_eeprom(iPage*PAGE_SIZE, mask);
dat[1] = read_ext_eeprom(iPage*PAGE_SIZE+1, mask);
printf("+%x%x ", dat[0],dat[1]);
dat[0] = read_ext_eeprom(iPage*PAGE_SIZE+2, mask);
dat[1] = read_ext_eeprom(iPage*PAGE_SIZE+3, mask);
printf("+%x%x ", dat[0],dat[1]);
}
//====================================
}
//=======================================================
#int_rb
void int_rb_isr(void){
//***Read B Port only once
int Bport;
Bport = port_b;
//***X CHANNEL = B4 JUST GONE HIGH
if(bit_test(Bport,4) && priorStateX==0){
Period = get_timer1();
set_timer1(0);
ChX0 = 0;
PriorStateX = 1;
}
//***X CHANNEL = B4 JUST GONE LOW
if(!bit_test(Bport,4) && priorStateX==1){
ChXH = get_timer1()-ChX0;
PriorStateX = 0;
// Xaccel = (int)(ChXH/(Period>>8)); //8 stands for 8 bit resolution
Xaccel = ChXH;
bShownX = false;
}
}
//======================================================= |
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Tue Jun 28, 2005 5:09 am |
|
|
arrow wrote: | Hi Andrew
What I observe is that when the data is computed, the results come out ok (printf).
When I read the data back- the data off the eeprom is not exactly what the first printf shows. The first 80 points (or so) are exactly the same, and then the data is not exaclty the same. |
80 bytes? I suggest writing some code that puts 0 in location 0, 1 in location 1, ..... up to 255 in location 255. Then read back the EEPROM and let us know what you find. _________________ 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 1:41 am |
|
|
Hi Andrew
Thank you for your suggestion- after doing what you said- and battling a lot- I have finally managed to get the eeprom to work as I expect it to.
I have another question on the same code:
As I understand it, you suggested that I disable interrupts, write to eeprom, and then re-enable the interrupts.
This works fine. Now I want to increase the speed, is there any way to have i2c write interrupted, timers set, then i2c to resume the write subroutine? i.e. if there is an interrupt, I want to return back to the subroutine where the i2c is done, and for thigns to resume?
I am posting the latest snippet of code-- that works when the interrupts are far (enough) between.
Once again thank you for all your help
Regards
arrow
Code: |
void pageWrite_ext_eeprom(long int address, int mask){
int ik;
START:
i2c_start();
if(i2c_write(0xa0 | mask)!=0) //Device must ACKNOWLEDGE
goto START;
i2c_write(address>>8); //High Byte of Address
i2c_write(address&0X00FF); //Low Byte of Address
for(ik=0;ik<PAGE_SIZE;ik++){ //Write Page to EEPROM
i2c_write(dat[ik]);
}
i2c_stop();
}
//--------------------------------
// IN MAIN{}
while(TRUE){
//***Store data to array
if(bShownX==0){
disable_interrupts(INT_RB);
dat[icnt] = (Xaccel>>8)&0x00FF;
icnt++;
dat[icnt] = Xaccel&0x00FF;
icnt++;
printf("+%x%x ",dat[icnt-2], dat[icnt-1]);
bShownX = 1;
if(icnt==PAGE_SIZE){
pageWrite_ext_eeprom(iPage*PAGE_SIZE, mask);
iPage++;
if(iPage==noPages){
break;
}
icnt = 0;
}
enable_interrupts(INT_RB);
}
}
//------------------------------
//NOW THE INTERRUPT ROUTINE
#int_rb
void int_rb_isr(void){
//***Read B Port only once
int Bport;
Bport = port_b;
//***X CHANNEL = B4 JUST GONE HIGH
if(bit_test(Bport,4) && priorStateX==0){
Period = get_timer1();
set_timer1(0);
ChX0 = 0;
PriorStateX = 1;
}
//***X CHANNEL = B4 JUST GONE LOW
if(!bit_test(Bport,4) && priorStateX==1){
ChXH = get_timer1()-ChX0;
PriorStateX = 0;
Xaccel = ChXH;
bShownX = false;
}
}
//=======================================================
|
|
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1635 Location: Perth, Australia
|
|
Posted: Thu Jun 30, 2005 2:14 am |
|
|
Try this. The interrupts are enabled and disabled around the testing and modification of the long values. Also in the interrupt handler you use "false" but in the mainline you use 0 and 1. You should use a consistent method to make it easier to read.
Code: | while(TRUE){
//***Store data to array
if(bShownX==0){
bShownX = 1;
disable_interrupts(INT_RB);
dat[icnt++] = (Xaccel>>8)&0x00FF;
dat[icnt++] = Xaccel&0x00FF;
enable_interrupts(INT_RB);
printf("+%x%x ",dat[icnt-2], dat[icnt-1]);
if(icnt==PAGE_SIZE){
pageWrite_ext_eeprom(iPage*PAGE_SIZE, mask);
iPage++;
if(iPage==noPages){
break;
}
icnt = 0;
}
}
} |
_________________ 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
|