|
|
View previous topic :: View next topic |
Author |
Message |
Charles Linquist
Joined: 07 May 2005 Posts: 28 Location: Campbell, CA
|
Interrupt Problems - AGAIN! |
Posted: Tue May 24, 2005 4:37 pm |
|
|
First, I'm just a beginner with C, so please don't be too hard on me regarding my style!
The code below does try to work. The problem is that whenever I enter my RDA ISR, ** ALL ** interrupts are disabled.
As you can see, I toggle RB0 whenever I enter the 1mSec timer interrupt so that I can watch that with an oscilloscope.
My RDA ISR is entered whenever a key is hit. The routine is not exited until the buffer is full or an <ENTER> is pressed or the _100mSec_Counter has reached 100 (10 sec). Unfortunately the _100mSec_Counter never times out because the timer interrupt isn't running.
As soon as I leave the RDA ISR, the timer interrupt starts again.
The CCS manual states that there is no need to disable interrupts when in an ISR, because "interrupts are automatically disabled". They don't mean ALL interrupts are disabled when in an ISR, do they?
What could be causing this?
Code: |
#include <18F452.h>
#device adc=10
#include <stdlib.h>
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=20000000) // 20MHz clock
#use rs232(BAUD=9600,XMIT=PIN_C6,RCV=PIN_C7,ERRORS)
#use fast_io(b)
#define buffer_size 10
#priority RDA,rtcc,AD
int1 Flg1,Flg2,Flg3,Flg4,Flg5,Flg6,Flg7,Flg8,ADC_Done_Flag,Key_Buffer_Flag,Key_Wait_Timeout_Error;
int8 ToggleCounter2,ToggleCounter3,ToggleCounter4,ToggleCounter5,ToggleCounter6;
int8 ToggleCounter7,ToggleCounter8;
int8 NumFans,mSec_Counter,_100mSec_Counter,Old_100mSec_Counter;
int8 i,t;
int16 Sec,Intermed,RPM;
int16 ToggleCounter1,ADvalue;
float Vref;
int8 RecdChar,next_in,next_out;
int8 Keyin_buffer[buffer_size];
void Clear_Keyboard_Buffer()
{
int i;
for (i=0;i<buffer_size;i++)
Keyin_buffer[i] = 0;
}
#int_rtcc
// Beginning of ISR, called when TMR0 (rtcc) rolls over
// Every Millisecond
void rtcc_isr()
{
output_toggle(PIN_B0); //Just to see if we are alive
{set_rtcc(0x67);}
if (NumFans == 0x08)
{if (Input(Pin_D7) != Flg8)
{Flg8 = Input(Pin_D7);
ToggleCounter8++;}}
... stuff in here
if (Input(Pin_D0) != Flg1)
{Flg1 = Input(Pin_D0);
ToggleCounter1++;}
mSec_Counter++;
If (mSec_Counter == 100)
{ _100mSec_Counter++;
mSec_Counter = 0;} //Clear the millisecond counter
return;
}
// END OF rtcc ISR
#int_AD
void AD_isr()
{
ADvalue = Read_ADC(ADC_read_only);
ADC_Done_Flag = 1;
}
#int_RDA
void RDA_ISR()
{
i=0;
Key_Buffer_Flag = 1;
Key_Wait_Timeout_Error = 0;
_100mSec_Counter = 0;
while (true)
{
reenter:
if (kbhit());
{i = getc();
if (i==13) return;
if (i==0x08)
{printf("\b");
printf(" ");
printf("\b");
next_in--;
goto reenter;}
if (next_in > (Buffer_size -1)) return;
printf ("%c",i);
Keyin_buffer[next_in]= i;
next_in++;
}
printf("100mSec_Counter = %i",_100mSec_Counter);
if (_100mSec_Counter == 100)
{Key_Wait_Timeout_Error = 1;}
}
}
void main()
{
int8 a,MinCnt,FanNum;
port_b_pullups(TRUE);
setup_adc_ports(RA0_ANALOG);
setup_adc( ADC_CLOCK_INTERNAL );
set_adc_channel( 0 );
setup_psp(PSP_DISABLED);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(rtcc_INTERNAL|rtcc_DIV_32|rtcc_8_bit);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
enable_interrupts(INT_rtcc);
enable_interrupts(INT_AD);
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
set_tris_b(0x00); //All bits output
set_tris_a(0xF3); //One LSB input,
set_tris_c(0x80); //Bit 3 input, rest output
set_tris_d(0xFF); ///All input
set_tris_e(0x07); //Three LSBs input, rest output
NumFans = 0x07 & input_e(); //Read Port E, strip off 5 MSBs
NumFans = NumFans + 1;
NumFans = 1; //DEBUG ONLY
a = 0;
Sec = 0;
mSec_Counter = 0;
ADC_Done_Flag = 0;
Vref = 5;
MinCnt = 0x50;
next_in = 0;
ToggleCounter1 = 0; //Make certain that there is no error if fan
ToggleCounter2 = 0; //Routine not called
ToggleCounter3 = 0;
ToggleCounter4 = 0;
ToggleCounter5 = 0;
ToggleCounter6 = 0;
ToggleCounter7 = 0;
ToggleCounter8 = 0;
Clear_Keyboard_Buffer();
i = 0;
t = 0;
// Main Loop ---------------------------------------------------------------------------------------------
while (a == 0) { //Forever for this pgm
if (_100mSec_Counter >= 0x0A) { //Every 1 second, go into this routine
_100mSec_Counter = 0;
FanNum = 0; //Clear the variables
output_toggle(PIN_B1); //Just an indication that something is happening
if (NumFans == 0x08){
if (ToggleCounter8 < MinCnt) //Simplified code, realized that if FanNum > 0 that alone
{FanNum = 8;
} //indicated that a fan had failed.
}
... Stuff in here
if (NumFans >= 0x02){
if (ToggleCounter2 < MinCnt)
{FanNum = 2;
}
}
if (ToggleCounter1 <= MinCnt) { //Check for too slow, set flag if # of toggles are too low
FanNum = 1;
}
if (FanNum == 0)
{output_high(PIN_A3);} //Pin high if no error
else
{Output_low(PIN_A3); //Bring a pin low if any fan is too slow
for (i=0;i<FanNum;i++) // Flash number of times corresponding to failed fan
{Output_low(Pin_A2);
delay_ms(20);
Output_high(Pin_A2);
delay_ms(500);}}
RPM = ((ToggleCounter1 * 30)/2);
printf("Fan 1 RPM = %lu\n\r",RPM);
If (ADC_Done_Flag == 1)
{printf("Voltage = %2.3f\n\r",(float)ADvalue*Vref/1023);}
If (Key_Buffer_Flag == 1)
{Key_Buffer_Flag = 0;
printf("Input = ");
for (i=0;i<Buffer_size;i++)
{printf ("%c",Keyin_buffer[i]);
}
printf("\n\r");
Clear_Keyboard_Buffer();
next_in = 0;}
ADC_Done_Flag=0;
Read_ADC(ADC_Start_only);
ToggleCounter1 = 0;
ToggleCounter2 = 0;
ToggleCounter3 = 0;
ToggleCounter4 = 0;
ToggleCounter5 = 0;
ToggleCounter6 = 0;
ToggleCounter7 = 0;
ToggleCounter8 = 0;
}
}
}
|
|
|
|
Ttelmah Guest
|
|
Posted: Tue May 24, 2005 5:05 pm |
|
|
Yes, all interrupts are disabled in an ISR. More correctly, the global interrupt response is disabled, _and must be_. However the individual interrupt flags will still be set, so if (for instance), a 'timer' interrupt happens while you are in a receive data ISR, then when the RDA ISR is exited, the timer interrupt will then be serviced. This though is the key behind the 'mantra', recited on this board again and again, _keep interrupt handlers short_. If the total time used in any ISR, is longer than the time between events on any of the ISR's, data will be lost, or interrupt events will be missed.
In your RDA ISR for example, the fact that an RDA interrupt has occured, tells you that there is a character waiting. All the ISR should do, is read the character, and copy it to a buffer (you do not need to check kbhit). You main program should do any timing, and printing (remember that printing a single character, takes the same time as receiving a character, so printing very much in an ISR, _will_ mean the UART will overflow.
Even on a PC, interrupts at the same or lower priority will be disabled the same way.
Best Wishes |
|
|
Charles Linquist
Joined: 07 May 2005 Posts: 28 Location: Campbell, CA
|
|
Posted: Tue May 24, 2005 6:43 pm |
|
|
So, since global interrupts are disabled when in any ISR, the only reason to set the #priority compiler directive is to insure that when you come out of an ISR, pending interrupts are processed in a known order. Correct - or not. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Tue May 24, 2005 9:54 pm |
|
|
Charles Linquist wrote: | So, since global interrupts are disabled when in any ISR, the only reason to set the #priority compiler directive is to insure that when you come out of an ISR, pending interrupts are processed in a known order. Correct - or not. |
Correct
Interrupts are meant to be short as possible. You should receive the data into a buffer and do all the processing in the main loop. |
|
|
Ttelmah Guest
|
|
Posted: Wed May 25, 2005 2:58 am |
|
|
Yesish!.
Think of this scenario. You have a timer interrupt, which is used to send data to a LCD. You also have a serial interrupt. Functionally, missing a timer interrupt, will not actually matter. Even if two or three timeouts took place, the LCD data would eventually be sent. Conversely the serial interrupt, is a critical event. The priority statement, in this case, would allow you to ensure that the RS232 handler will always 'win' if both interrupt flags are set.
If #priority is not used, the interrupt routines are called in the order that they appear in the source code.
You also need to look at latency values. For instance, if you have a serial interrupt routine, that takes 30uSec to handle, and the serial rate is only 9600bps (so a 'character' time is nearly a mSec), while you also have a quadrature decode routine, that only takes 15uSec to handle (remember there is as much as a fifty instruction 'overhead', involved in the global interrupt handler), but may happen for short bursts at a rate up to 50KHz. You have 'error recovery' in the serial handler, so loss of a character is not a major event, then it becomes vital to ensure that the quadrature decode routine always wins.
You need to look at the delays involved in each routine, and the worst case scenario (this is normally when an interrupt triggers just after the code has started serving another interrupt). You also need to avoid disabling interrupts in the main code (in the example you have, using the putc/printf, and delays in the interrupt handler, ensures that interrupts will be disabled if these functions are used in the main code, making latency worse...
For reliability, unless you know exactly what you are doing, interrupt routines should be short, and simple.
Best Wishes |
|
|
|
|
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
|