Hi,
Although I posted a Program to Transmit (encode) the NEC IR Protocol, it appears that I discussed, but didn't ever actually post my prepared Receiving/Decoding Program, perhaps planning to make it compatible with the very similar Samsung protocol. However, I have now updated the Transmitter code and posted a new version of the Receiving program in the above thread. Also included is a discussion of the "Bit Banging" process, but I thought it could be useful to discuss in this thread why it's "not practical" to simply use the native PULSIN command to decode communications protocols such as this. However, on careful analysis, it appears that it
IS possible to use PULSIN to decode the NEC protocol, although with some potentially serious limitations. A functional Program will follow, with some of the practical issues explained, but most of the background detail from the above thread won't be duplicated here.
The
Data Sheet for a typical Three-terminal IR receiver module indicates that if it receives a "perfect" modulated IR beam with a Duty Cycle of 600 microseconds ON and 600 us OFF, then the demodulated switching waveform may vary between 400 us ON or OFF and 800 us OFF or ON (but still totalling 1200 us). The NEC protocol uses a slightly shorter cycle time with 562 us ON and OFF periods, so the range of the Pulse and Gap for a "Data 0" is probably around
365 to 765 us, with the Gap of a "Data 1" from
1475 to 1875 us. Thus the "0" and "1" bit periods are still clearly distinguishable, but the time available for processing the data and the actual numerical values recorded by PULSIN may have a considerable range. Therefore, it would be preferable for a decoding routine to measure the
Period of each incoming data bit, i.e. the time between successive positive- or negative-going transitions of the demodulated carrier signal. However, the PULSIN instruction measures only the width of a pulse (or the gap between two pulses), i.e. the time duration between a rising and a falling edge (or vice versa), so it is necessary to consider the Duty Cycle Range that the program may encounter.
For "Bit Banging" a data stream it's often necessary to "know" the execution time of each of the instructions employed so, many years ago I created a
Program that can measure the Execution Times of nearly all PICaxe instructions, using just a "real" (M2) chip (i.e. not the Simulator). A few values will be quoted here, but suffice to say that the program reports the typical number of "Base PIC Instruction Cycles", which have a duration of exactly 1 microsecond (with a 4 MHz clock), and proportionally shorter times at higher clock frequencies. Here, the main concern is with the maximum likely execution times, to ensure that all the required instructions can fit into the cycle time of a string of (the shorter) "0" Data Bits, i.e. 1.125 ms per Bit.
Unfortunately, PULSIN is one of the few commands that is difficult to measure an absolute execution time, because it includes a "waiting" time for the pulse to begin (which can be up to 655.35 ms at 4 MHz). The PULSIN must span the entire (variable) "Gap" time (to measure it) and also perform some "housekeeping" during the (fixed-width) carrier pulses on each side. Here, the main concern is with the situation when there is negligible waiting time, which is probably similar to a PULSOUT instruction, that has an "overhead" of around 700 ICs more than the actual pulse width. Obviously a 700 us instruction overhead (of a single PULSIN command at 4 MHz) cannot fit into even the NEC's nominal 560 us carrier pulse width, but at 8 MHz, the 350 us might be just compatible with the expected minimum pulse width of 365 us assumed above. Thus, in principle a program might be created around a "List" of repeated PULSIN commands (one for each Data bit), but several other restrictions need to be considered.
First, PULSIN delivers a WORD value, but PICaxe Basic supports only a total of 13 Words (plus another 6 available system "S_Wx" words) whilst the NEC protocol transmits 32 bits. Similarly there are only 26 named byte variables, but nearly all PICaxe chips support at least 100 bytes of "indirect" RAM, accessed by the PEEK/POKE and @BPTR "pointer" instructions. The @BPTRINC variable format is particularly useful because it auto-increments the pointer, so a simple list of repeated
PULSIN pin , polarity , @BPTRINC instructions could form the core of a viable program. However, the RAM accepts only Byte values, i.e. with a maximum value of 255, which corresponds to a time of 1,275 us with an 8 MHz clock. Thus a "1" Data Bit will overflow the Low Byte, loading it with a value up to
1875 - 1275 = 600 us, whilst a "Data 0" could be in the range
375 to 765 us. Thus the Low byte alone (which is all that can be written by the @BPTR) could not reliably distinuish between "0" and "1" data bits. Therefore it is necessary to add a separate instruction (typically consuming an additional 600 ICs) to write the High Word to RAM, requiring the clock to be increased to at least 16 MHz, for the program code to fit within the carrier pulse.
Another issue is that if PULSIN does not encounter a "valid" pulse, then a considerable time can elapse before it returns the zero "timeout" value, i.e. 164 ms at 16 MHz and 82 ms at 32 MHz. Such an event could occur if a "mistaken" AGC/Run-In pulse were encountered, but also the NEC "Repeat" command intentionally follows a normal AGC pulse with a single "data" pulse and then silence. Therefore the program really needs an "escape route" when a zero value is returned by any PULSIN, because otherwise it could effectively "hang" for up to 32 timeouts, or almost 3 seconds, even at 32 MHz. Perhaps it's still viable in a "List" format (at 32 MHz), but a Program Loop structure becomes much more appealing, however this requires also a separate "Exit" path when the expected number of bits has been received. Fortunately, the Byte Pointer can be used as the loop counter, but the "Jump Back" to close the core program loop itself adds considerably to the execution time, so a 32 MHz clock frequency becomes essential.
Again, the forum's 10,000 characters per post limit approaches, so here is my functional program, with comments that include the estimated Instruction Cycles contained in the core loop. These total around 400 microseconds, which is less than the nominal 562 us of the NEC protocol's carrier pulse width, but is longer than the potential "worst case" pulse from the IR receiver (around 360 us). Thus, in most cases the program decoding should be entirely reliable, but I plan to post more details of its operation in due course.
Code:
; NEC IR Receiver supporting Repeats using PULSIN at 32 MHz AllyCat February 2025
; Assumes Idle High, as from a 3-terminal IR Receiver. Typically <150 bytes
#picaxe 14m2 ; Or any M2 except 08M2LE
#no_data
#terminal 38400
' #DEFINE SHOWBITS ; For debugging
symbol irpin = pinb.5 ; For IF / Input commands
symbol irpul = b.5 ; For Pulsin command (0 = Idle High)
symbol marker = b.1 ; For debugging
symbol rs232 = b.2 ; Slower Baud Rate to appear on Logic Analyser
setfreq m32
main:
w2 = 0 : w3 = 0 ; For received Address and Command Words
getNEC:
pulsin irpul,0,w1 ; Read AGC pulse width, 800 per ms @ 32 MHz
if w1 < 5000 then getNEC ; Reject Short AGC pulse (<6.25 ms)
if w1 > 10000 then getNEC ; Reject Long AGC pulse (>12.5 ms)
bptr = 32
pulsout marker,1500 ; Or PAUSEUS - Delay to 2.25 ms
if irpin = 0 then rept ; Test for short Gap (Repeat Command)
on irpin goto rept ; Alternative instruction for Idle High input
dloop: ; \/ Typical Number of Base PIC Instruction Cycles
pulsin irpul,1,w1 ; 700 - Estimated minimum overhead (+ wait & pulse period)
@bptrinc = b3 ; 600 ; Store the High byte of W1
on b3 goto rept ; 700 ; Test for overflow zero (Timeout/Repeat)
if bptr < 64 then dloop ; 1200 Total 3200 ICs (400 us) Loop until 32 bits received
sertxd(cr,lf)
dec bptr ; Undo the last @.INC
doH:
b1 = @bptrdec ; 600 ; Get databit period (*320 us)
w3 = w3 + w3 + bit10 ; 900 ; Faster option but needs B1 and assumes /4
if bptr > 47 then doH ; 1200 Total 2700 * 16 * 5/4 = 6 ms ; Loop back for high word
doL:
w2 = @bptrdec / 4 + w2 + w2 ; 2000 ; Get databit + Shift low Word Left (slower but no B1)
if bptr > 31 then doL ; 1200 = 3200 * 16 * 5/4 = 7ms ; Loop back for low word
for bptr = 4 to 7 ; Display ASCII-Hex Nibbles
b2 = @bptr AND 15 ; Keep the lower 4 bits
b2 = b2 / 10 * 7 + b2 + "0" ; Convert to ASCII-Hex
b3 = @bptr / 16 ; Divide by 16 for upper 4 bits
b3 = b3 / 10 * 7 + b3 + "0" ; ASCII-Hex
serout rs232,n9600_32,(b3,b2)
sertxd(b3,b2,":")
next
#IFDEF SHOWBITS
sertxd(cr,lf) ; Display individual Bit-widths (320 us units)
for bptr = 32 to 63
sertxd(#@bptr) ; Value of PULSIN High Word
next
sertxd(cr,lf)
#ENDIF
goto main
rept:
serout rs232,n9600_32,("+") ; Repeat Message detected (or other Timeout)
sertxd("+")
goto main
Cheers, Alan.