That is pretty close to 13. 5 ms. Why are we using
TMR1? When we start saving times to measure for our
data bits, we need some way to stop looking if the signal is
interrupted, otherwise we’ll just appear to “hang” and do
nothing. TMR1 will be set to values that are just a little
larger than the inter-packet repeat rate. For NEC, this is
108 ms; for SONY, it is 45 ms. TMR1 is a 16-bit timer and it
only has four prescales to choose from, so I took the one
that got me close to 108 ms. Finally, I set the INT interrupt
to trigger on a high-to-low transition for my measurements.
Here is the code for the actual interrupt service routine:
#int_global
void isr(void)
/*
Lets handle all ISR save/recovery functions, the
default isn’t lean enough for
an ISR that is highly time critical.
*/
{
#asm
//store current state of processor
MOVWF save_w
SWAPF status,W
MOVWF save_status
SWAPF FSR,W
MOVWF save_FSR
BCF status,5
BCF status,6
#endasm
//Set to page 0 for SFR’s
//We only have two interrupts, so if it isn’t
this one, it’s the other.
if (bit_test(PIR1,0))
{
//Timer1 overflow IRQ
bits[wBits++] = 255; //timed out
bit_clear(PIR1,0); //Clear TMR1 int flag
bit_clear(T1CON,0); //Turn TMR1 off until...
}
if (bit_test(INTCON,1))
{
//CCP1 capture IRQ
bits[wBits++] = TMR0; //save the pulse time
bit_clear(INTCON,1); //clear external
//interrupt flag
//reset the timeout
TMR1H = t1h;
TMR1L = t1l;
bit_set(T1CON,0);
TMR0 = 0;
//turn TMR1 back on so
//we can time out
//clear out the timer
}
if (wBits == MAXBIT)
wBits=0;
//rollover the bit
//buffer
#asm
// restore processor and return from interrupt
SWAPF save_FSR,W
MOVWF FSR
SWAPF save_status,W
MOVWF status
SWAPF save_w,F
SWAPF save_w,W
#endasm
}
20 SERVO 07.2008
I can hear the groaning now! I’ve used PIC assembly
language! In CCS C, the compiler flag “#int_global” means
that CCS will not handle the saving of registers that need
to be saved during an ISR call. This means that we need to
do it. Really, the only reasonable way to do this is with
some simple assembly code. This function needs to be
FAST and we do that by keeping it lean. I combine this
lean ISR with the definitions at the top of the program that
looks like this:
unsigned char save_w;
#locate save_w=0x7f
unsigned char save_status;
#locate save_status=0x7e
unsigned char save_FSR;
#locate save_FSR=0x7d
unsigned char wBits=0;
#locate wBits = 0x7c
//These next 3 bytes
//are saved on interrupt
//To make access to
//these variables fast
//Keep them in common
//memory for ISR use
unsigned char t1h;
#locate t1h = 0x7b
unsigned char t1l;
#locate t1l = 0x7a
/*
Give me direct access to several SFR’s that CCS
doesn’t handle the way I want.
*/
#byte TXREG = 0x15
#byte T1CON = 0x10
#byte INTCON = 0x0B
#byte FSR = 0x04
#byte status = 0x0
#byte TMR1L = 0x0E
#byte TMR1H = 0x0F
#byte PIR1 = 0x0C
#byte TMR0 = 0x01
Those confusing compiler directives tell the compiler to
put certain variables into “access RAM” that is available in
all data banks. This means that I don’t have to waste time
switching banks to save our timing measurements or our
saved registers. Make sure you look this up in the CCS
manual. You don’t have to do this in other micros that
don’t use banked data memory.
Now look at the ISR from above. When we get an
interrupt from INT, we record the value in the TMR0 register
in the next available data slot in our ring buffer (called ring
because it rolls over to 0 when it reaches the end of the
“ring”). If we time out on TMR1, then we stuff a 255 into
the buffer telling the decoding routine that we timed out
and we should ditch the entire set of numbers and wait for
the next start to be detected.
The second part of the program will decode the times
saved in the buffer and turn them into 1s and 0s. It uses its
own pointer into the ring buffer and knows when to look
for a value when the read pointer is different from the
write pointer. I’ve put lots of comments into this code, so
I’m not going to go over it line-by-line. Here is what the bit
decoding routine looks like: