|
|
View previous topic :: View next topic |
Author |
Message |
weg22
Joined: 08 Jul 2005 Posts: 91
|
INT_TIMER0 interrupt causing weird output?? |
Posted: Mon Apr 17, 2006 10:15 pm |
|
|
Hi all,
The code below works perfectly fine without the INT_TIMER0 interrupt...that is, I see values for "wp1" and "wp2" correctly printed to hyperterminal. However, when including the TIMER0 interrupt, I see "wp1" and "wp2" printed to the screen every once in a while and then in between I see ASCII junk. For example:
…
…
wp1: 323
wpòÒHø
…
Høwp1: 323
wpòÒ
I attached the code in this post. What I'm guessing is happening is the TIMER0 interrupt is occurring during the RS232 interrupt and thus not allowing it to fill the char arrays? I tried to avoid this with the reset2 variable, but it still didn't work. Any help would be much appreciated.
Thanks in advance,
weg
Code: |
#include <16F877.H>
#include <stdlib.h>
#include <math.h>
#define LED PIN_C0
#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=4800, xmit=PIN_C6, rcv=PIN_C7, stream=GPS)
#use rs232(baud=4800, xmit=PIN_D1, rcv=PIN_D0, stream=PC)
// global variables
int rud_pwm=128, reset=0, reset2=0;
long pulseLength=0;
int i=0, j=0, k=0, commas=0, commas2=0;
char wp1[6], LAT[10], LONGI[11];
char c;
// Timer interrupt to generate PWM signals
#INT_TIMER0
void pwm_signal()
{
if(reset2==1) reset2=reset2;
else
{
reset=1;
//One of the parameters used to cause the interrupt to occur every 22.5 msec
set_timer0(37); // 61 for 20 msec
// RUDDER
output_high(PIN_B5); // generates initial high time ie 1 msec
delay_us(1000);
i=0;
for(i=0;i<rud_pwm;i++) // generates remaining high time, varied by variable "pwm"
{
//use this to tune duty cycle
delay_us(1);
}
output_low(PIN_B5);
}
}
// hardware interrupt to detect manual override
#int_ccp1
void isr()
{
reset=0;
set_timer1(0);
while(input(PIN_C2));
// if SW interrupt triggers during HW interrupt, prevent bogus pulseLength value
if(reset==0) pulseLength = get_timer1();
else
{
pulseLength = pulseLength;
reset=0;
}
}
#int_rda
void rda_isr(void)
{
reset2=1;
c = fgetc(GPS);
c = (char)c;
if(c=='$') {j=1; commas=0; commas2=0;}
if(c=='G' && j==1) j=2;
if(c=='P' && j==2) j=3;
if(c=='R' && j==3) j=4;
if(c=='M' && j==4) j=5;
if(c=='C' && j==5) j=6;
if(c=='B' && j==5) j=7;
if(c==',' && j==6) commas++;
if(c==',' && j==7) commas2++;
if(commas==3) // get latitude data (3)
{
if(c==',') k=0;
else {LAT[k]=c; k++;}
}
if(commas==5) // get longtitude data (5)
{
if(c==',') k=0;
else {LONGI[k]=c; k++;}
}
if(commas2==11) // get waypoint heading (11)
{
if(c==',') k=0;
else {wp1[k]=c; k++;}
}
reset2=0;
}
void main()
{
char afterDecimal[4];
float diff_Lat=0.0, diff_Long=0.0;
float currentLat=0.0, currentLong=0.0;
float currentWayptLat=0.0, currentWayptLong=0.0;
float waypointHeading=0.0;
long wpHeading=0;
// current waypoint (drexel parking lot)
currentWayptLat = 39.9540667*Pi/180.0;
currentWayptLong = 75.184833*Pi/180.0;
output_high(LED); delay_ms(1500);
output_low(LED); delay_ms(1500);
fprintf(PC, "Hello World\r\n");
// setup interrupts
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256); // other param causing interrupt to occur every 20 msec
setup_ccp1(CCP_CAPTURE_RE); // on rising edge
setup_timer_1(T1_INTERNAL);
// enable interrupts
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_CCP1);
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
while(1)
{
if(pulseLength>3750)
{
output_low(LED); // LED off under manual control
}
else
{
output_high(LED);
// latitude
afterDecimal[0] = LAT[5];
afterDecimal[1] = LAT[6];
afterDecimal[2] = LAT[7];
afterDecimal[3] = LAT[8];
currentLat = (float)(atol(LAT)/100) + ((float)(atol(LAT)%100) + atof(afterDecimal)/10000.0)/60.0;
currentLat = currentLat*Pi/180.0;
// longitude
afterDecimal[0] = LONGI[6];
afterDecimal[1] = LONGI[7];
afterDecimal[2] = LONGI[8];
afterDecimal[3] = LONGI[9];
currentLong = (float)(atol(LONGI)/100) + ((float)(atol(LONGI)%100) + atof(afterDecimal)/10000.0)/60.0;
currentLong = currentLong*Pi/180.0;
// calculate heading to waypoint
diff_Lat = currentWayptLat - currentLat;
diff_Long = currentWayptLong - currentLong;
waypointHeading = atan2(diff_Long,diff_Lat);
if(sin(currentWayptLong - currentLong)>0.0)
waypointHeading = 360.0 - waypointHeading*180.0/Pi;
else
waypointHeading = -waypointHeading*180.0/Pi;
wpHeading = atol(wp1); // in degrees
fprintf(PC, "wp1: %ld\r\n", wpHeading);
fprintf(PC, "wp2: %f\r\n\n", waypointHeading);
}
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Apr 17, 2006 10:45 pm |
|
|
RS-232 depends upon the proper bit timing being maintained.
In a software UART, the bit periods are generated by toggling
the Tx pin high and low, with a software delay loop between the
transitions. If you permit an interrupt to occur while a byte
is being transmitted by a soft UART, the bit timing will be
stretched out much longer than it should be. It takes many
microseconds just for the overhead to get in and out of an isr.
In addition to that, you have a huge 1000 usec delay in your isr. |
|
|
arunb
Joined: 08 Sep 2003 Posts: 492 Location: India
|
RE: |
Posted: Tue Apr 18, 2006 1:36 am |
|
|
Hi,
A general scheme would be to avoid receiving an interrupt while another one is being serviced, for this you must see which of the interrupts should take priority over the others.
Just ensure that you have only one interrupt enabled at all times..
thanks
arunb |
|
|
weg22
Joined: 08 Jul 2005 Posts: 91
|
|
Posted: Tue Apr 18, 2006 6:47 am |
|
|
So you're saying I should have something like below in my RDA interrupt loop?
Code: |
#int_rda
void rda_isr(void)
{
disable_interrupts(INT_TIMER0);
// rest of code...
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
}
|
|
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 18, 2006 7:50 am |
|
|
What you post, would be _lethal_. You _must never have a global interrupt enable in an interrupt handler_. This is a 'sure' way of making the code die.
Now the comment about only enabling one interrupt, is 'silly'. It is not needed, and has no advantages at all. The whole 'point' of the hardware flags triggered by interrupt events, is so that they can be remembered if they happen while other events are being dealt wth. The _big_ problem with your code, is having the long delay in the interrupt handler. The 'Golden rule' of interrupts, is _keep the handlers short_ (in 'time' terms).
If you need a long 'time' delay, triggered by an interrupt, then program one of the hardware timers to do this, start/enable this in the interrupt handler, and return, then when this triggers perform the 'delayed' function. Delays in interrupt handler of more than a very few uSecs, are a 'sure way' to generate problems.
Now assuming that you have a reasonably recent compiler, add the keyword 'DISABLE_INTS', to the use rs232 line for the 'software' UART, This will disable the interrupts, during the actual transmission/reception of characters on this stream. It won't help, if you are already inside a hugely long interrupt handler, but will stop the interrupt from 'interfering' with the data I/O.
Best Wishes |
|
|
weg22
Joined: 08 Jul 2005 Posts: 91
|
|
Posted: Tue Apr 18, 2006 8:29 am |
|
|
Alternatively, if I used the CCP2 pin to generate a PWM and eliminate the INT_TIMER0 loop...this should solve my problems, right?
Thanks for all the help,
weg |
|
|
|
|
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
|