CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Co-habitation of interrupts, or dis/enabling inside ISR

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Pete Smith
Guest







Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Wed Jul 16, 2003 9:31 am     Reply with quote

Hi all.

Got a bit of a problem with my latest code (groan from the audience :-).

I'm using 3.146. Pic is 12F675.

I'm using Timer 0 DIV 4 to give me the timings for a 2400 baud buffered serial port. This works perfectly on its own.

I'm trying to use Timer 1 DIV 8, with a 5 interrupt countdown timer, to give me a ~3 second serial port timeout, which sets a flag, and forces bgetc() to return zero. I monitor this flag, and once all the "data" is in, if it's set, it knows there's been a timeout, and tries the last operation again.

This works perfectly on its own too.

However, when I try to run them together, things start going wrong. TIMER 1 must be interfering with the running of the micro or something, because if it's running, I get garbage out of the serial port.

My incoming serial port is based on 2 interrupts. INT_RB notices when the data line goes low. This then enables timer 0 (timer 0 has previously been disabled), and it then does its thing. Once the last bit is in, Timer 0 disables itself.

I've tried disabling Timer 1 as soon as the serial port changes, and then enabling it once all the data's in, but I'm still getting corrupted data.

Does anyone have suggestions?

Should/can Timer0 and Timer 1 interfere with each other in this way? Or have I done something really stupid? Even the very existence of the #INT_TIMER1 code in the program causes the problem, even if it's explicitly disabled using disable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
and the timer code doesn't actually contain anything anyway.

The code is below if anyone's interested.

Thanks,

Pete.

#include <12F675.h> // We're using a 12F675
#fuses xt,NOWDT,NOPROTECT, NOMCLR, NOBROWNOUT,noput // Set the fuses
#use delay(clock=4000000) // It's running at 4MHz

#include <defines.h> // Set up all the defines
#include <tih_picregisters.h> // and all the registers

#use fast_io(A) // We want to use fast IO
#use RS232 (baud=2400, xmit=GP0, parity=N, bits=9,ERRORS) // We want a 2400 baud RS232 port

// --------------------------------------------------------------------------------------------------------------------------

int old_gpio; // Old GPIO value
short input_changed; // Flag to say the input has changed
short input_status; // Flag to say what the input now is
char packet[PACKET_SIZE]; // TRM data packet
short rs232_start_bit; // The RS232 start bit
short rs232_got_byte; // The RS232 "got them all"
short tmr1hit; // TMR1 interrupt
short serial_timeout=0;
int tmr1count; // TMR1 count
int rs232_current_bit; // The current RS232 receiving bit.
int rs232_byte; // Current RS232 received byte.
byte serial_buffer[BUFFER_SIZE]; // define a global buffer of this size
byte next_in = 0; // and set up buffer pointers
byte next_out = 0;

// --------------------------------------------------------------------------------------------------------------------------
#INT_RB // The "Wakeup on change" interrupt
void change(void) // The actual function
{
int changes; // Need to know the changes

changes=old_gpio ^ gpio; // EOR the old value and new
old_gpio=gpio; // and make a note of the new value

if (bit_test(changes,1)) // If bit 1 (incoming serial port) has changed
{
if (!bit_test(old_gpio,1)) // If the serial port is now low
{
if (!rs232_start_bit)
{
rs232_start_bit=1; // We've got the 1st bit
rs232_got_byte=0; // So we've not got the last one
rs232_current_bit=0; // and we're currently on bit 0
rs232_byte=0; // Clear the received byte
IOC = 0b001000; // Interrupt now only on the input bit, not the serial port
enable_interrupts(INT_TIMER0); // RTCC interrupt
//disable_interrupts(INT_TIMER1);
set_timer0(100); // Set timer 0 to 1.5 bits wide @ 2400 baud.
}
}
}

if (bit_test(changes,3)) // Check for bit 3 having changed
{
input_changed=1; // If it has, set the flag
input_status=bit_test(old_gpio,3); // and find out what it is now
}
}

// --------------------------------------------------------------------------------------------------------------------------

#INT_TIMER0 // Timer 0 interrupt
void rtcc(void)
{
int t; // Temp storage

set_timer0(152); // Set timer 0 to 1 bit wide @ 2400 baud.
if (rs232_current_bit>0) // If we're on bit 1 or more (the data itself)
{
if (input(GP1))
{
rs232_byte=rs232_byte | 128;
}
if (rs232_current_bit<8)
{
rs232_byte=rs232_byte>>1; // Shift the data left 1
}
}
rs232_current_bit++; // Increment the RS232 bit count;

if (rs232_current_bit>8) // If we've grabbed the whole byte
{
rs232_start_bit=0; // Clear the "start" bit
rs232_got_byte=1; // And announce we have the bit
disable_interrupts(INT_TIMER0); // RTCC interrupt disabled, because we're now done.
//enable_interrupts(INT_TIMER1); // Timer 1 enabled
//set_timer1(0x00); // Set to zero
//tmr1count=5; // Set counter to max, to initialise the serial port timeout
IOC = 0b001010; // Interrupt on RS232 in again.
serial_timeout=0; // Serial port has not timed out.

serial_buffer[next_in]=rs232_byte; // Grab the data in the input buffer
t=next_in; // make a note of its location
next_in=(next_in+1) \% BUFFER_SIZE; // add one to it
if(next_in==next_out) // if the next input location is same as next out
{
next_in=t; // then buffer is full !!
}
}
}

// --------------------------------------------------------------------------------------------------------------------------

#INT_TIMER1 // Timer 0 interrupt
void serialtimeout(void)
{
}

// --------------------------------------------------------------------------------------------------------------------------

byte bgetc(void) // Buffered getc()
{
byte c; // Received data

while(!bkbhit) // so long as there's no data waiting
{
if (serial_timeout) // Check to see if we have serial port timeout
{
return(0x00); // If we do, return zero.
}
}
c=serial_buffer[next_out]; // grab the next data byte
next_out=(next_out+1) \% BUFFER_SIZE; // change the "next out" variable
putc(c);
return(c); // return the data
}
// --------------------------------------------------------------------------------------------------------------------------

void transmit(char opcode,int data_length,char *payload) // Transmit a message
{
int checksum=0; // We need a checksum
int loop; // Loop variable
int temp; // And temp

checksum+=(int)opcode; // Add 1st opcode to checksum
putc(opcode); // Send to serial port

temp=data_length / 10; // Get data length MS digit
putc((temp)+48); // Send it.
checksum+=temp+48; // And add to checksum
temp=data_length \% 10; // Get data length LS digit
putc((temp)+48); // send it
checksum+=temp+48; // And add to checksum

for (loop=0;loop<data_length;loop++) // Go through the data packet
{
temp=*(payload+loop); // Get the byte to be sent
putc(temp); // Send it
checksum+=temp; // Increment the checksum
}

temp=checksum / 16; // Get the checksum MSD
if (temp>9) // If the digit is A-F
{
putc((temp)+55); // then we need to add 55, to get the letter and Send it
}
else // otherwise
{
putc((temp)+48); // we need to add 48, to get the number and Send it
}

temp=checksum \% 16; // Get the checksum LSD
if (temp>9) // If the digit is A-F
{
putc((temp)+55); // then we need to add 55, to get the letter and Send it
}
else // otherwise
{
putc((temp)+48); // we need to add 48, to get the number and Send it
}
putc(0x0d); // Send CR & we're done
next_in=0;
next_out=0;
}
// --------------------------------------------------------------------------------------------------------------------------

int transmit_simple(char opcode,int data_length,char *payload) // Transmit a "simple" message
{
int temp; // Temp storage
short quit;

temp=0; // Set an initial value for a timer

while (temp == 0) // So long as this timer is zero (i.e. we've not received anything)
{
transmit(opcode,data_length,payload); // Transmit the data
temp=250; // Set temp to "500ms" (i.e. 50)
quit=1; // Quit flag to 1

while (quit) // While the flag=1
{
delay_ms(2); // Wait 10msec
if (bkbhit)
{
quit=0; // If there's data, leave the "wait"
}
temp--; // Decement temp
if (!temp) // If temp hits zero
{
quit=0; // Then there's been a timeout
}
}
}
temp=bgetc(); // Get the simple response

return(temp); // Return it
}

// --------------------------------------------------------------------------------------------------------------------------

int transmit_complex(char opcode,int data_length,char *payload) // Transmit a "simple" message, returns length and payload
{
int temp; // Temp storage
int loop; // Loop
int checksum; // Checksum
int rx_checksum; // Checksum (received)
int rx_opcode; // The opcode
short quit; // Quit flag
short success; // Successfully got packet.
int count;

transmit(opcode,data_length,payload); // Transmit the data
//enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
set_timer1(0x00);
tmr1count=5;

putc('O');

temp=bgetc(); // Get the opcode
checksum=temp; // Get the checksum so far
rx_opcode=temp; // The opcode is the variable

if (!serial_timeout)
{
putc('L');
temp=bgetc(); // Get the data length
checksum+=temp;
data_length=(temp-48)*10; // Multiply by 10
temp=bgetc(); // get the data length LSD
checksum+=temp;
data_length+=(temp-48); // and add it
printf("\%02X",data_length);
}

if (!serial_timeout)
{
putc('D');
for (loop=0;loop<data_length;loop++) // Go for as many times as is necessary
{
temp=bgetc(); // Get the data from the port
checksum+=temp; // Add the data to the checksum
*(payload+loop)=temp; // And store the data in the buffer
}
}

if (!serial_timeout)
{
putc('S');
temp=bgetc(); // Get the data from the port
if (temp>95) // If the char is lower case,
{
temp-=86; // subtract 86, to get 10-15
}

if (temp>64) // If the char is upper case,
{
temp-=55; // subtract 86, to get 10-15
}

if (temp>47) // If the char is an alpha case,
{
temp-=48; // subtract 86, to get 10-15
}

rx_checksum=temp*16; // Multiply by 16 and store in the checksum

temp=bgetc(); // Get the data length
if (temp>95) // If the char is lower case,
{
temp-=86; // subtract 86, to get 10-15
}

if (temp>64) // If the char is upper case,
{
temp-=55; // subtract 86, to get 10-15
}

if (temp>47) // If the char is an alpha case,
{
temp-=48; // subtract 86, to get 10-15
}
rx_checksum+=temp; // Now add it to the total

temp=bgetc(); // Grab the EOF
}

printf("R=\%02X C=\%02X\n\r",rx_checksum,checksum);

//disable_interrupts(INT_TIMER1); // RTCC interrupt disabled - don't want it yet.
enable_interrupts(GLOBAL);
serial_timeout=0;
if ((rx_checksum != checksum) || (serial_timeout)) // If the two checksums don't match or we've had a serial timeout
{
return('F'); // Then we've got a corrupted packet, so exit
}
else
{
return(data_length); // Return the data length if they do match
}
}

// --------------------------------------------------------------------------------------------------------------------------


void setup(void) // Set everything up
{
set_tris_a(0b001110); // Set the TRIS register

enable_interrupts(INT_RB); // We want wakeup on change interrupts
disable_interrupts(INT_TIMER0); // RTCC interrupt disabled - don't want it yet.
disable_interrupts(INT_TIMER1); // RTCC interrupt disabled - don't want it yet.
enable_interrupts(global); // Enable them all

setup_timer_0(RTCC_DIV_4); // Setup timer 0
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8); // Setup timer 0
port_b_pullups(false); // Pullups off
GPIO=0; // and all outputs low
tmr1count=5; // Set serial port timeout to 3 sec
rs232_start_bit=0;
ADCON0 = 0; // ADC off
ANSEL = 0; // GPIO pins 0,1,2 and 4 set to all digital
CMCON = 7; // Comparators off
IOC = 0b001010; // Interrupt

bit_set(rs232_errors, 7); // Set bit 9 of serial data to 1 (i.e. 2 stop bits)
}

// --------------------------------------------------------------------------------------------------------------------------

void main(void)
{
int temp;
short toggle;
int loop;

setup(); // Setup functions etc
next_in = 0; // and set up buffer pointers
next_out = 0;

while (1==1) // Infinite loop
{

temp='F';
while (temp == 'F')
{
strcpy(packet,"Hello");
temp=transmit_complex(REQUEST_UNIQUE_ID,5,packet);
}

printf("Received \%X bytes\n\r",temp);
for (loop=0;loop<temp;loop++)
{
printf("\%c",*(packet+loop));
}
printf("\n\r");
}
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516069
R.J.Hamlett
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Wed Jul 16, 2003 10:21 am     Reply with quote

:=Hi all.
:=
:=Got a bit of a problem with my latest code (groan from the audience :-).
:=
:=I'm using 3.146. Pic is 12F675.
:=
:=I'm using Timer 0 DIV 4 to give me the timings for a 2400 baud buffered serial port. This works perfectly on its own.
:=
:=I'm trying to use Timer 1 DIV 8, with a 5 interrupt countdown timer, to give me a ~3 second serial port timeout, which sets a flag, and forces bgetc() to return zero. I monitor this flag, and once all the "data" is in, if it's set, it knows there's been a timeout, and tries the last operation again.
:=
:=This works perfectly on its own too.
:=
:=However, when I try to run them together, things start going wrong. TIMER 1 must be interfering with the running of the micro or something, because if it's running, I get garbage out of the serial port.
:=
:=My incoming serial port is based on 2 interrupts. INT_RB notices when the data line goes low. This then enables timer 0 (timer 0 has previously been disabled), and it then does its thing. Once the last bit is in, Timer 0 disables itself.
:=
:=I've tried disabling Timer 1 as soon as the serial port changes, and then enabling it once all the data's in, but I'm still getting corrupted data.
:=
:=Does anyone have suggestions?
One comment straight away. You say that the serial runs OK on it's own, but I'd expect it to run a little slow. The reason is that when an interrupt occurs, there is a significant delay before the hardware responds, and then a longer delay, while the code does the 'housekeeping', of saving certain essential registers. By the time you actually arrive at the instruction to set the RTCC counter to 152, I'd expect perhaps 15 to 20uSec to have allready passed. On this basis, the RS232, would probably run at about 2300, to 2330bps. Now this is about 4\% 'error', and is close enough to work. However when the second interrupt is running, on some occasions, you will be inside this interrupt handler, when the RTCC interrupt occurs, and then the time involved in finishing this handler, then has to occur, before the RTCC handler can be called. On these occasions, your allready 'strained' accuracy, then falls apart...
The first thing to try, is to change the code:

set_timer0(152);

to this:
#byte TMR0=0x1

TMR0+=152;

This way, the errors will no longer be 'cumulative', since the routine will move on from the current time, by the required amount.
This may be all that is needed. However it is _vital_, that the timer1 code is kept short...
It may also be worth specifying the #priority line, and putting INT_RTCC first, since this sets the order in which the interrupts are checked by the handler, which can help a little.

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516071
Pete Smith
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Wed Jul 16, 2003 10:50 am     Reply with quote

:=:=Hi all.
:=:=
:=:=Got a bit of a problem with my latest code (groan from the audience :-).
:=:=
:=:=I'm using 3.146. Pic is 12F675.
:=:=
:=:=I'm using Timer 0 DIV 4 to give me the timings for a 2400 baud buffered serial port. This works perfectly on its own.
:=:=
:=:=I'm trying to use Timer 1 DIV 8, with a 5 interrupt countdown timer, to give me a ~3 second serial port timeout, which sets a flag, and forces bgetc() to return zero. I monitor this flag, and once all the "data" is in, if it's set, it knows there's been a timeout, and tries the last operation again.
:=:=

[snip]

:=One comment straight away. You say that the serial runs OK on it's own, but I'd expect it to run a little slow. The reason is that when an interrupt occurs, there is a significant delay before the hardware responds, and then a longer delay, while the code does the 'housekeeping', of saving certain essential registers. By the time you actually arrive at the instruction to set the RTCC counter to 152, I'd expect perhaps 15 to 20uSec to have allready passed. On this basis, the RS232, would probably run at about 2300, to 2330bps. Now this is about 4\% 'error', and is close enough to work. However when the second interrupt is running, on some occasions, you will be inside this interrupt handler, when the RTCC interrupt occurs, and then the time involved in finishing this handler, then has to occur, before the RTCC handler can be called. On these occasions, your allready 'strained' accuracy, then falls apart...
:=The first thing to try, is to change the code:
:=
:=set_timer0(152);
:=
:=to this:
:=#byte TMR0=0x1
:=
:=TMR0+=152;
:=
:=This way, the errors will no longer be 'cumulative', since the routine will move on from the current time, by the required amount.

OK, that's a good point.

:=This may be all that is needed. However it is _vital_, that the timer1 code is kept short...

When it's present, it's just a decrement, check for zero, and flag set.

The annoying thing is that I get the problem, even though it's disabled.

I suppose that if the interrupt handler was _that_ sensitive, then even if it's there, but disabled, the code will still be in place to run it when it's needed, and this may be pushing it over the edge.

:=It may also be worth specifying the #priority line, and putting INT_RTCC first, since this sets the order in which the interrupts are checked by the handler, which can help a little.

I didn't realise that this was supported for non 18 parts.

Thanks for the pointers, I'll give it a look tomorrow.
Pete.
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516072
R.J.Hamlett
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Thu Jul 17, 2003 2:15 am     Reply with quote

:=:=:=Hi all.
:=:=:=
:=:=:=Got a bit of a problem with my latest code (groan from the audience :-).
:=:=:=
:=:=:=I'm using 3.146. Pic is 12F675.
:=:=:=
:=:=:=I'm using Timer 0 DIV 4 to give me the timings for a 2400 baud buffered serial port. This works perfectly on its own.
:=:=:=
:=:=:=I'm trying to use Timer 1 DIV 8, with a 5 interrupt countdown timer, to give me a ~3 second serial port timeout, which sets a flag, and forces bgetc() to return zero. I monitor this flag, and once all the "data" is in, if it's set, it knows there's been a timeout, and tries the last operation again.
:=:=:=
:=
:=[snip]
:=
:=:=One comment straight away. You say that the serial runs OK on it's own, but I'd expect it to run a little slow. The reason is that when an interrupt occurs, there is a significant delay before the hardware responds, and then a longer delay, while the code does the 'housekeeping', of saving certain essential registers. By the time you actually arrive at the instruction to set the RTCC counter to 152, I'd expect perhaps 15 to 20uSec to have allready passed. On this basis, the RS232, would probably run at about 2300, to 2330bps. Now this is about 4\% 'error', and is close enough to work. However when the second interrupt is running, on some occasions, you will be inside this interrupt handler, when the RTCC interrupt occurs, and then the time involved in finishing this handler, then has to occur, before the RTCC handler can be called. On these occasions, your allready 'strained' accuracy, then falls apart...
:=:=The first thing to try, is to change the code:
:=:=
:=:=set_timer0(152);
:=:=
:=:=to this:
:=:=#byte TMR0=0x1
:=:=
:=:=TMR0+=152;
:=:=
:=:=This way, the errors will no longer be 'cumulative', since the routine will move on from the current time, by the required amount.
:=
:=OK, that's a good point.
:=
:=:=This may be all that is needed. However it is _vital_, that the timer1 code is kept short...
:=
:=When it's present, it's just a decrement, check for zero, and flag set.
:=
:=The annoying thing is that I get the problem, even though it's disabled.
:=
This suggests it may just be timing. Assuming the default priority has the RTCC tested after Timer1, then having the interrupt 'present', will add 4uSec to the interrupt latency for the RTCC. I suspect you are walking right 'on the edge' with the total latency, and this addition is enough to step over into problems. Doing a bit more detailed 'instruction count', gives the latency at 30uSec for the chip and clock you have (without the second interrupt enabled). This gives a baud rate of 2242Hz with the existing code, and 2222Hz with the second interrupt present. This pushes the error to 8\%, and I suspect it is this alone, which is giving your fault.

:=I suppose that if the interrupt handler was _that_ sensitive, then even if it's there, but disabled, the code will still be in place to run it when it's needed, and this may be pushing it over the edge.
:=
:=:=It may also be worth specifying the #priority line, and putting INT_RTCC first, since this sets the order in which the interrupts are checked by the handler, which can help a little.
:=
:=I didn't realise that this was supported for non 18 parts.
Yes, it behaves differently though. On the older parts, all it does, is sets the order that the tests for the interrupt flags appear inside the global interrupt handler. Hence if you set a particular source at the head of the list, this will be the source that is tested first. With several sources, it can make a significant difference, and in your case, it would mean that in the event of both interrupt flags being set at the same time (or within a few uSec of one another), it'll be the serial routine that gets handled the first time the interrupt handler is called. It is not 'hardware priority' like the 18 parts (this is handled with a different command anyway, using the 'fast' instruction after declaring the handler), but just a 'search order' change.

:=Thanks for the pointers, I'll give it a look tomorrow.
:=Pete.

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516091
Pete Smith
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Thu Jul 17, 2003 2:34 am     Reply with quote

<font face="Courier New" size=-1>:=:=:=:=Hi all.
:=:=:=:=
:=:=:=:=Got a bit of a problem with my latest code (groan from the audience :-).
:=:=:=:=
:=:=:=:=I'm using 3.146. Pic is 12F675.
[snip]

:=:=
:=:=The annoying thing is that I get the problem, even though it's disabled.
:=:=
:=This suggests it may just be timing. Assuming the default priority has the RTCC tested after Timer1, then having the interrupt 'present', will add 4uSec to the interrupt latency for the RTCC. I suspect you are walking right 'on the edge' with the total latency, and this addition is enough to step over into problems. Doing a bit more detailed 'instruction count', gives the latency at 30uSec for the chip and clock you have (without the second interrupt enabled). This gives a baud rate of 2242Hz with the existing code, and 2222Hz with the second interrupt present. This pushes the error to 8\%, and I suspect it is this alone, which is giving your fault.

As a debug, I've added a toggling output to my TIMER0 interrupt, so every time it's run, it gives me an edge to lock against. I'm looking at the incoming data stream, and this output on a scope.

The TIMER0 output seems to be massively unstable. It's swinging all over the place. Even with Timer1 disabled, it's still doing it. I'm going to have a very close look at my TIMER0 code, and if that doesn't work, I may have to make other changes to my code, like assume I'm never going to need to receive data unless I ask for it (which I know shouldn't happen, but I like to have code more capable than is needed), and just use the CCS getc() command, or possibly use the WDT to perform the timeout, assuming it can co-exist with the prescaler I've set for timer 0.

OK, bit more checking. The reason it was dancing wildly was twofold.

First, I'd not zeroed the status at the beginning of each byte, so the phase was swapping every byte.

Second, the TMR0=<value> didn't seem to agree with it in the slightest.

I've now taken a closer look, and I seem to be missing the 1st 3 bits, so some serious value swapping is needed I think!

[EDIT]
OK, I've tweaked the timer0 values a little, and I'm now in sync with the incoming data stream. I've used 104 for the start bit to half way through bit 0, and then using 156, and on the scope, the edges are now halfway through all the bits.

I have another intermittent problem though. I wrote above I was missing the 1st 3 bits. This happens occasionally, and requires the PIC to be re-started. It starts apparently totally at random!

Apart from this, tweaking the timing to overcome the latency of the ISR seems to have worked marvellously!

[EDIT again :-)]

The 3 bit delay is actually variable, because the INT_RB is missing the negative going edge of the start bit, and is locking onto the next negative going edge instead. This one's probably down to poor programming on my part or something.

Thanks for the pointers, I'll know in the future!

Pete.</font>
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516093
R.J.Hamlett
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Thu Jul 17, 2003 4:22 am     Reply with quote

:=<font face="Courier New" size=-1>:=:=:=:=Hi all.
:=:=:=:=:=
:=:=:=:=:=Got a bit of a problem with my latest code (groan from the audience :-).
:=:=:=:=:=
:=:=:=:=:=I'm using 3.146. Pic is 12F675.
:=[snip]
:=
:=:=:=
:=:=:=The annoying thing is that I get the problem, even though it's disabled.
:=:=:=
:=:=This suggests it may just be timing. Assuming the default priority has the RTCC tested after Timer1, then having the interrupt 'present', will add 4uSec to the interrupt latency for the RTCC. I suspect you are walking right 'on the edge' with the total latency, and this addition is enough to step over into problems. Doing a bit more detailed 'instruction count', gives the latency at 30uSec for the chip and clock you have (without the second interrupt enabled). This gives a baud rate of 2242Hz with the existing code, and 2222Hz with the second interrupt present. This pushes the error to 8\%, and I suspect it is this alone, which is giving your fault.
:=
:=As a debug, I've added a toggling output to my TIMER0 interrupt, so every time it's run, it gives me an edge to lock against. I'm looking at the incoming data stream, and this output on a scope.
:=
:=The TIMER0 output seems to be massively unstable. It's swinging all over the place. Even with Timer1 disabled, it's still doing it. I'm going to have a very close look at my TIMER0 code, and if that doesn't work, I may have to make other changes to my code, like assume I'm never going to need to receive data unless I ask for it (which I know shouldn't happen, but I like to have code more capable than is needed), and just use the CCS getc() command, or possibly use the WDT to perform the timeout, assuming it can co-exist with the prescaler I've set for timer 0.
:=
One little 'tweak' you could consider, is to ensure the interrupt flags are clear before enabling them. Remember, that (for instance), when you disable the INT_RB, while data is being 'handled' by the RTCC interrupt, when you then re-enable INT_RB, the interrupt flag will allready be set, since there will have been several 'edges' since the handler was last called. In a recent thread about swapping between two interrupt sources, I posted a basic version of what is needed to prevent this being a problem, with:

#define I_EXT 0
#define I_TIMER2 1
#bit T2IF=0xC,1
#bit EXIF=0x8B,1

switch_to_interrupt(int int_reqd) {
//Ensure neither interrupt is enabled
disable_interrupts(INT_EXT);
disable_interrupts(INT_TIMER2);
if(int_reqd==I_EXT) {
//prevent immediate interrupt for past rollovers
EXIF=0;
enable_interrupts(INT_EXT);
}
else {
T2IF=0;
enable_interrupts(INT_TIMER2);
}
}

Now this is for a different chip version, but shows the point, that you should clear the interrupt flag, before you turn the interrupt on (the case where the interrupt occurs between clearing the flag, and enabling the interrupt, is not normally a problem, since for an 'edge' here, it is reasonable to call the handler, it is the situation where the flag has been left set from 'past' events, that has to be dealt with...).

:=OK, bit more checking. The reason it was dancing wildly was twofold.
:=
:=First, I'd not zeroed the status at the beginning of each byte, so the phase was swapping every byte.
:=
:=Second, the TMR0=<value> didn't seem to agree with it in the slightest.
:=
:=I've now taken a closer look, and I seem to be missing the 1st 3 bits, so some serious value swapping is needed I think!
:=
:=[EDIT]
:=OK, I've tweaked the timer0 values a little, and I'm now in sync with the incoming data stream. I've used 104 for the start bit to half way through bit 0, and then using 156, and on the scope, the edges are now halfway through all the bits.
:=
Sounds much better. :-)

:=I have another intermittent problem though. I wrote above I was missing the 1st 3 bits. This happens occasionally, and requires the PIC to be re-started. It starts apparently totally at random!
:=
This may be because the interrupt flag is allready set, resulting in the routine starting 'early', and reading idle signal, from in front of the actual data?.

:=Apart from this, tweaking the timing to overcome the latency of the ISR seems to have worked marvellously!
:=
:=[EDIT again :-)]
:=
:=The 3 bit delay is actually variable, because the INT_RB is missing the negative going edge of the start bit, and is locking onto the next negative going edge instead. This one's probably down to poor programming on my part or something.
:=
:=Thanks for the pointers, I'll know in the future!
:=
:=Pete.</font>

Try clearing the RBIF, before enabling this, and the TMR0IF before enabling this, and see if it 'cracks' the problems.

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516095
Pete Smith
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Thu Jul 17, 2003 6:35 am     Reply with quote

:=:=
:=:=The 3 bit delay is actually variable, because the INT_RB is missing the negative going edge of the start bit, and is locking onto the next negative going edge instead. This one's probably down to poor programming on my part or something.
:=:=
:=:=Thanks for the pointers, I'll know in the future!
:=
:=Try clearing the RBIF, before enabling this, and the TMR0IF before enabling this, and see if it 'cracks' the problems.

Got it cracked now.

To avoid re-triggers, I was disabling the Interrupt on Change bit for the serial port input, and once all the data was in, I'd then re-enable it. On a whim, I used a flag to say we were currently receiving data so as to only receive one start bit, and then removed the references to the IOC register, and it works perfectly now! When the data rate is flat out, it occasionally drops a bit, but the protocol will take car of that, because it uses checksums.

The interesting thing is that using Timer 1, the built in software UART fails to work properly as well!

Again, thanks for the pointers to the latency of the ISR. I knew there was a latency, but not that it was as high as that.

Pete.
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516097
R.J.Hamlett
Guest







Re: Co-habitation of interrupts, or dis/enabling inside ISR
PostPosted: Thu Jul 17, 2003 7:50 am     Reply with quote

:=:=:=
:=:=:=The 3 bit delay is actually variable, because the INT_RB is missing the negative going edge of the start bit, and is locking onto the next negative going edge instead. This one's probably down to poor programming on my part or something.
:=:=:=
:=:=:=Thanks for the pointers, I'll know in the future!
:=:=
:=:=Try clearing the RBIF, before enabling this, and the TMR0IF before enabling this, and see if it 'cracks' the problems.
:=
:=Got it cracked now.
:=
:=To avoid re-triggers, I was disabling the Interrupt on Change bit for the serial port input, and once all the data was in, I'd then re-enable it. On a whim, I used a flag to say we were currently receiving data so as to only receive one start bit, and then removed the references to the IOC register, and it works perfectly now! When the data rate is flat out, it occasionally drops a bit, but the protocol will take car of that, because it uses checksums.
:=
:=The interesting thing is that using Timer 1, the built in software UART fails to work properly as well!
:=
Yes. The 'software UART', is purely done using static times. It is not written to survive having anything else going on... A caveat for a lot of situations.

:=Again, thanks for the pointers to the latency of the ISR. I knew there was a latency, but not that it was as high as that.
:=
:=Pete.
Yes. The latency is pretty 'foul'. Unfortunately, the PIC doesn't help at all in this regard (well the 18 family, do with their ability to save the three most important registers automatically), but it is a real pity that there isn't a faster way of saving the large number of registers that are involved...
It is perhaps worth remembering, that a simple routine like the ones you are showing, doesn't really need to 'touch' many of the normal C registers. Hence you could in the worst case, re-write the global handler in assembler, and get the latency down to perhaps only four or 5 uSec. Also using the RTCC, and 'adding forward' to generate the expected new counter, and then polling for this (rather than using an IRQ), can allow suprising things to be done. I have a set of code here, that handles interrupt driven operation of an external A-D converter, and full duplex 9600bps comms, all in software on a 4MHz chip, and hasn't 'missed a beat' over several years of operation on hundreds of sites.
Glad it is working now. :-)

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516099
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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