PIC detecting start bit with NEC IR protocol - Page 1
Forum section: electronics / general

author: GreyArea
author_rank: Contributor
timestamp: January 02, 2018, 09:20:44 pm
content: As an excercise I'm trying to write some code which uses a timing based method to detect the start bit of the NEC IR protcol.The approach I have taken is to detect the pin going low and count how many chunks (counts) of 50us delays happens before the pin goes high again.To detect the desired 9ms low pulse (before the 4.5ms space) I'm throwing out counts <8ms or >10msHeres the code snippet (the bottom half of the function is trimmed):Code:short read_data(void){unsigned uint8_t count, i;count = 0;// Check 9ms pulse (remote control sends logic high, pin goes low).while((RB4 == 0) && (count < 200)){count++;__delay_us(50);}// Is initial low pulse <8ms or >10ms?if( (count > 199) || (count < 160))return 0; // Exit, not NEC protocol.When I placed a breakpoint at the if statement, I noticed that the value for count was way out, it was 92 instead of approx 180 as it should be. I was using a 1Mhz clock at the time. I changed the clock speed to 8Mhz and noticed I captured a different result (163). This allowed the code to pass the check but was stlll approx 1ms out.I then did some testing and tried every clock speed the PIC (PIC16F87) could run at and recorded the results:Code:_XTAL_FREQOSCONspeedcount--------------------------------------------------------------312500x0000000031.25 Khz51250000b00010000125 Khz202500000b00100000250 Khz355000000b00110000500 Khz6010000000b010000001 Mhz9220000000b010100002 Mhz12140000000b011000004 Mhz14680000000b011100008 Mhz163I was under the impression that the _XTAL_FREQ macro would adjust __delay_us() call but it does seem like the error is coming from some kind of timing issue.For more precision I tried changing the "chunk" size to 10us and the boundaries to >800 <1000, centering on a value of 900 but at 8Mhz I got a result of 485 [EDIT: I changed the uint8_t to uint16_t to cope with the higher number]. The IR demodulator is behaving correctly as the attached trace shows. I'm a bit stumped now and any ideas would be welcome.

author: hexreader
author_rank: Frequent Contributor
timestamp: January 02, 2018, 10:05:03 pm
content: The while loop and other instructions within the loop require about 12 machine cycles to acheive (if I estimated correctly)With 1 MHz crystal, each machine cycle takes 4uS, so your full loop takes about twice as long as you think.Higher crystal frequencies mean that the loop overhead becomes less significant and the 50uS delay becomes the main timing factor.If you can spare a timer, then this is a better way to time the IR signal, as it is less dependant on instruction time.Your way is perfectly OK, but you need to factor in instruction execution time (possibly by reducing __delay_us() parameter to compensate.)

author: GreyArea
author_rank: Contributor
timestamp: January 02, 2018, 10:45:26 pm
content: Thanks, I think I was getting closer - by using the stopwatch in the simulator I'd worked out that with no __delay_us() call it took 18us to loop back to count++; (according to the MPLABX stopwatch). This doesn't seem to translate directly to the hardware though, I tried the following formula:8ms / 18us = 444.449ms / 18us = 500.10ms / 18us = 555.55This overflowed so I set a really high value and found that the loop breaks out at 1023. So using 9ms / 1023 = 8.8us that's a full 10us off the simulator stopwatch compared to actual hardware.Oh well, knowing the loop time is 8.8us seems good enough to move on, but as you say - a timer does seem a better way to implement this.Thanks a lot, Matt

author: mikerj
author_rank: Super Contributor
timestamp: January 02, 2018, 10:49:39 pm
content: You could make this work a little better by subtracting the loop overhead from your delay e.g. at 4MHz 12 instructions will take 12us, so set the delay to 50-12=38us.You can also reduce the loop overhead by decrementing a counter and testing for zero, since this can be done in a single machine instruction. Incrementing and testing if a target value has been reached requires a subtraction operation and then a test of the ALU flags, so takes multiple instructions.However hexreader is quite correct, this is a pretty nasty way to do loop timing unless there is no other choice. Your PIC contains a useful set of timers and a compare/capture unit which is perfect for doing this sort of job, and requires zero CPU overhead whilst the incoming bit period is being measured.