NEC format bit-banged IR output using Macros and Include files.

PhilHornby

Senior Member
I've recently embarked on a project to implement a remote thermostat for a Dimplex Opti-myst Electric Stove, since the built-in one is hopeless.

As user pleitch discovered, when trying to do something similar with a Dimplex Air-conditioner, control via the IR remote is the only way to go - control via the mains input just toggles between 'OFF' and 'Standby'.

Here is my code to implement the three buttons on the Dimplex RC02-010 Remote Control, which uses the NEC protocol. I've used the #Macro and #Include functionality of the V6.x.x.x Picaxe Editor to try and improve readability.

I used InglewoodPete's Background Infrared Receiver to verify the output.


This is a demo program - that just sends the 'Centre' button repeatedly.

Code:
#picaxe 08M2
;+
; NEC.BAS - Demonstrate sending NEC format IR signals, using Macro and include file 
;        functionality of Picaxe Editor V6.x.x.x.
;
; Author: Phil Hornby: 
;
;-
Symbol Data_Pin    = C.2                            ;connect to IR LED, or driver transistor/mosfet
Symbol Debug_Pin    = C.4                            ;20uS pulse to sync oscilloscope - if req'd


#rem
 


This is the o/p of this program, as decoded by 'InglewoodPete's 20X2 program:-


See: http://www.picaxeforum.co.uk/showthread.php?16576-Background-Infrared-Receiver-for-32-bit-Codes




    Start >2h-TOut- 
    Start >2h
    <End 00000000111111111001000001101111>
    [42][25]%00000000.11111111 %10010000.01101111
    $00FF 906F


    which is identical to that produced by a physical Remote Control 
    (Dimplex model: RC02-010, "Centre" ON/OFF Button)


    Start >2h-TOut- 
    Start >2h
    <End 00000000111111111001000001101111>
    [42][25]%00000000.11111111 %10010000.01101111
    $00FF 906F


Dimplex Opti-myst (RC02-010) codes:-


LEFT (Flame)    => 0000 0000 | 1111 1111 | 0101 0000 | 1010 | 1111    (10)
CENTRE (ON/OFF)    => 0000 0000 | 1111 1111 | 1001 0000 | 0110 | 1111    (9)
RIGHT (HEAT)    => 0000 0000 | 1111 1111 | 1101 0000 | 0010 | 1111    (11)


(usually followed by one or more repeat codes - though the target hardware (a heater) ignores them.)


#endrem




#No_data
#No_end


#include "RemoteControl.basinc"


;
; Start execution
;
    setfreq M32                                ;warp factor 9


    ;dirs = %00010100                            ;Pin direction statements not needed - implied from usage




;    Loop forever, sending same code.


do
    pulsout Debug_Pin,15                        ;optional pulse to sync oscilloscope.
                                        ;15 * 1.25 = 18.75uS + 2uS overhead = 20 uS 
                                        
;    SendLeftButton                            ;Send code for Left Button ('Flame')
    SendCentreButton                            ;Send code for Centre Button (ON/OFF)
;    SendRightButton                            ;Send Code for Right Button ('Heater Power')
loop
and here is where the actual work is done:-

Code:
;+
; RemoteControl.basinc - macro definitions
;
;    All timings assume 32MHz operation.


;
; Macros used in generating an NEC IR signal; these are common to all Remotes. They generate the component
; parts of the NEC protocol.
;    
    #macro StartCarrier                        ;Turn on the PWM function
        ;
        ; The carrier is generated using the inbuilt PWM function.
        ;
        pwmout Data_Pin, 210, 278                ;Start PWM output @ 38000Hz at 33% @ 32MHz (from wizard)
    #endm


    #macro SendAGC
        ;
        ; Sends 9mS worth of pulses of 8uS +16uS gap. Then pause 4.5mS.
        ; (Apparently, was used to set AGC originally.)
        ;
        StartCarrier                        ;start 38KHz carrier o/p
        pauseus 7100                        ;wait 8.875mS (7100 /8 = 887.5 * 10 = 8.875mS)
        pwmout Data_Pin,off                    ;then shut it off - it will have been active for 9mS in total.
        
        pauseus 3400                        ;Pause further 4.25mS (3400 /8 = 425 * 10 = 4.25mS)
                                    ;to bring total gap to 4.5mS (including delay in starting first 'bit' o/p,
                                    ;which follows this preamble)
    #endm




    #macro Send38KHzBurst
        ;
        ; Sends 21 pulses of 8uS +16uS gap.
        ;
        StartCarrier                        ;start 38KHz carrier o/p
        pauseus 325                        ;wait 406uS (325 /8 = 40.625 * 10 = 406uS)
        pwmout Data_Pin,off                    ;then shut it off - it will have been active for 560uS in total.
    #endm


    #macro SendZero
        ;
        ; "0" bit
        ;
        Send38KHzBurst                        ;250~270uS overhead, starting & stopping pulse train
        pauseus 226                        ;226/8 = 28.25 * 10 (283) makes total gap = 560uS 
    #endm
    
    #macro SendOne
        ;
        ; "1" bit
        ;
        Send38KHzBurst                        ;250~270uS overhead, starting & stopping pulse train
        pauseus 1148                        ;1148/8 = 143.5 * 10 (1435) takes gap to 1.69mS
    #endm
    
    #macro SendEndPacket
        ;
        ; Mark the end with an extra bit and a long gap.
        ;
        Send38KHzBurst                        ;21 pulses of 38KHz carrier
        Pause 880                        ;then gap of 110mS (* 8 for 32MHz operation)
    #endm


;     Now, the Device-specific macros...
    
;
;    Macro to define the target device of a particular Remote Control.
;
    #macro SendAddress
        SendAGC                            ;marks start of packet
        ;
        ; Address Byte, LSB first "00000000" (This is Dimplex's choice...for reasons known only to them!)
        ;
        SendZero
        SendZero
        SendZero
        SendZero
        SendZero    
        SendZero
        SendZero
        SendZero
        ;
        ; Inverse of Address byte "11111111"
        ;
        SendOne
        SendOne
        SendOne
        SendOne
        SendOne
        SendOne
        SendOne
        SendOne
    #endm
;
;    Macro(s) to define each button on the Remote Control.
;


    #macro SendLeftButton
        ;
        ; Send Dimplex Opti-myst Remote Control "Left Button" ('Flame' = code 10)
        ;
        SendAddress
        ;
        ; Command byte, LSB first "01010000" (10)
        ;
        SendZero
        SendOne
        SendZero
        SendOne
        SendZero
        SendZero
        SendZero
        SendZero
        ;
        ; Inverse of Command byte "10101111"
        ;
        SendOne
        SendZero
        SendOne
        SendZero
        SendOne
        SendOne
        SendOne
        SendOne
        ;
        ; End of packet marker (33rd 'bit' and 110mS gap)
        ;
        SendEndPacket                        ;terminate the data
    #endm
    
    #macro SendCentreButton
        ;
        ; Send Dimplex Opti-myst Remote Control "Centre Button" (ON/OFF = code 9)
        ;
        SendAddress
        ;
        ; Command byte, LSB first "10010000" (09)
        ;
        SendOne
        SendZero
        SendZero
        SendOne
        SendZero
        SendZero
        SendZero
        SendZero
        ;
        ; Inverse of Command byte "01101111"
        ;
        SendZero
        SendOne
        SendOne
        SendZero
        SendOne
        SendOne
        SendOne
        SendOne
        ;
        ; End of packet marker (33rd 'bit' and 110mS gap)
        ;
        SendEndPacket                        ;terminate the data
    #endm


    #macro SendRightButton
        ;
        ; Send Dimplex Opti-myst Remote Control "Right Button" ('Heater Power' = code 11)
        ;
        SendAddress
        ;
        ; Command byte, LSB first "11010000" (11)
        ;
        SendOne
        SendOne
        SendZero
        SendOne
        SendZero
        SendZero
        SendZero
        SendZero
        ;
        ; Inverse of Command byte "00101111"
        ;
        SendZero
        SendZero
        SendOne
        SendZero
        SendOne
        SendOne
        SendOne
        SendOne
        ;
        ; End of packet marker (33rd 'bit' and 110mS gap)
        ;
        SendEndPacket                        ;terminate the data
    #endm
Attached files - RemoteControl.basinc had to be renamed to RemoteControl.bas to keep the Uploader happy...
 

Attachments

Last edited:

inglewoodpete

Senior Member
Thanks for putting the time and work into making this happen. I have often thought about developing an NEC code sender but got to do it.
 

AllyCat

Senior Member
Hi,

Here is an M2 program to encode any four bytes into the NEC IR transmitter format. Non-intuitively, as I've discovered before, it can be more difficult to write satisfactory code to transmit a protocol than to receive it. One reason is that the program must accurately reproduce all the protocol parameters, whilst receiving code may need only to "recognise" or synchronise to a few basic features of the coding. Also, the transmitting code here is used to modulate the carrier, whilst the receiving code is assumed to be supplied with demodulated data from the IR receiver.

Here, I've used the IR-NEC Protocol Decoder incorporated within the "PulseView" (Siglok) application, to validate the data timing. This appears to have quite critical requirements (for example it failed to "see" the "0" bits in the code from post #1), but of course doesn't indicate what it considers to be "wrong" with the data. Certainly, the timing of the (modulated) pulses and spaces is not completely consistent, which appears to be caused by the use of PWMOUT commands: Surprisingly, there are no "partial" IR pulses (which might be expected of an asynchronous modulator), perhaps because the Microchip data sheet indicates at least a capability of operating only with complete PWM cycles. However, I'm not clear if the observed inconsistency is caused by the PICaxe firmware or the base "silicon" hardware. Note that the PWMDUTY instruction is completely unusable because it takes at least 1ms to execute, even at a Setfreq M32.

The modulation can be removed (or inverted) by choosing appropriate values for DUTY and IDLE, but the uneven timing still occurs. However, it disappears if the PWMOUT commands are replaced by HIGH and LOW instructions. Another aspect of the data timing is that the PICaxe 08M2 chip executes the code significantly faster than a 20M2, so delay constants are supplied for both chips. The 14M2 timing is probably the same as the 20M2 and the 18M2 perhaps somewhere nearer to the 08M2 (because it does not need its internal ports remapped). X2s are probably even slower, but of course the clock the frequency can (and probably needs to) be increased to 64 MHz.

The structure of the program is quite critical, particularly because of the (short) timing of the NEC "0" databit, and some limitations of PICaxe Basic. The overhead on the PAUSEUS instruction (which is required twice, for the pulse and for the gap) is almost 100us at 32 MHz and the M2 has no direct "Shift" operations, nor the concept of a "Carry" flag. The NEC protocol transmits LSB first which normally would use Right-shifting, that needs the use of division. Also, copying a carry from the LSB end and adding it to the MSB end (of another word) is rather inefficient. Converesly, Left-shifting needs only multiplication by two, or even more efficiently by adding the word value and a carry value to itself, all in a single instruction line.

Therefore, the program uses only Left-Shifts and reverses the bit-order within the bytes before transmission. This could be performed in the initial data preparation, but a simple algorithm fits in neatly during the "AGC" pulse (or even during the "header" gap). An efficient "Mirror" reversing algorithm (also available as REV in X2s) is a topic that could fill a thread in itself, but the required bit-swapping is sufficiently "untidy" that the simple BITn re-assignments are the most efficient (or a 256 Byte Table-Lookup is easily the fastest).

For testing and demonstration, the code alternates Normal and Repeat packets, whilst in practice it appears that the Repeats should continue until a button is released. Many of the instruction line comments include an estimation of their execution period in PIC Instructions, which correspond to 125ns at a Setfreq of 32 MHz. Hopefully, I'll post a corresponding decoder routine in a suitable location on the forum, in due course.

Code:
; NEC InfraRed Encoder.  AllyCat January 2021
#picaxe 08M2   	 	; Faster than other M2s
;  #picaxe 20M2		; Slower than some M2s
#no_data

calibfreq 0			; 16 increases frequency by 1.6% (228 increases period by 3%)
setfreq m32
symbol tempb = b1						; Bit-addressable
symbol tempw = w1						; Bit-addressable
symbol wx = w2							; NEC Address (& complement)
symbol wy = w3							; NEC Command (& complement)
symbol PERIOD 	= 210					; For 38.5 kHz @ 32MHz
symbol DUTY 	= 900	;280					; Carrier DutyCycle (844 = 100%)
symbol IDLE 	= 0 							; Idle Low (900 for High)
symbol DATABITS = 64 + 32					; 64 gives Active low flag (33 counts to bit14 = 0)
; Timing Constants
	#IFDEF 08M2
symbol WidAGC 	= 4420				; Pauseus units = 0.125us
symbol WidHDR 	= 3200				; 4.5 ms standard Header Gap
symbol WidPUL0 = 88 					; For 0 Data pulse
symbol WidPUL1 = 200					; For 1 Data pulse
symbol WidGAP0 = 110					; For 0 Data Gap
symbol WidGAP1 = 820					; For 1 Data Gap
symbol DelREDO = 327 					; Pause (125us units) to next transmission
symbol WidAGCT = 7000				; Repeat AGC pulse width (no mirror code needed)
symbol WidHDR2 = 1600				; 2.25 ms Repeat Header Gap
symbol WidPUL 	= 360				; Repeat code pulse width
symbol DelREPT = 780					; Pause after Repeat burst
	#ENDIF
	#IFDEF 20m2						; Probably also for 14M2
symbol WidAGC 	= 4000 				; Pauseus units = 0.125us
symbol WidHDR 	= 3000 				; 4.5 ms standard Header Gap
symbol WidPUL0 =   35					; For 0 Data pulse
symbol WidPUL1 =  160 				; For 1 Data pulse
symbol WidGAP0 =   35 				; For 0 Data Gap
symbol WidGAP1 =  700 				; For 1 Data Gap
symbol DelREDO =  313					; Pause (125us units) to next transmission
symbol WidAGCT = 7130				; Repeat AGC pulse width (no mirror code needed)
symbol WidHDR2 = 1620				; 2.25 ms Repeat Header Gap
symbol WidPUL 	=  300				; Repeat code pulse width
symbol DelREPT =  772					; Pause after Repeat burst
	#ENDIF
symbol PWMop 	= c.2				; Transmit Output pin

main:
do
	b4 = 25							; Address
	b5 = b4 xor 255					; Extended (complement) Address
	b6 = 29							; Command
	b7 = b6 xor 255					; Command Complement 
	call NEC_TX
	pause DelREDO					; Frame repeat = 110 ms
	call NEC_RPT
	pause DelREPT	
loop

NEC_TX:
SendAGC:								; Framing pulse of continuous carrier (typically 9ms)
	pwmout PWMop,PERIOD,DUTY			; For 9 ms burst of 38kHz
Mirror:									; Reverse the bit sequence to allow Left-Shifts
	bptr = 4								; Address/Command Register address (bytes 4 to 7)
do										; \/PIC Instruction Cycles
	b3 = @bptr							; 500  ; Must be bit-addressable
	bit16 = bit31 : bit17 = bit30 ; 940
	bit18 = bit29 : bit19 = bit28 ; 940
	bit20 = bit27 : bit21 = bit26 ; 940
	bit22 = bit25 : bit23 = bit24	; 940
	@bptrinc = b2						; 500 + 4760 * 4 = 19040
loop until bptr = 8					; 4400
	b1 = b4 : b4 = b7 : b7 = b1	; 1500		; Swap bytes
	b1 = b5 : b5 = b6 : b6 = b1	; Total swap = 3.3ms @ 32MHz
	pauseus WidAGC						; 700 + 10N ICs
	pwmout PWMop,PERIOD,IDLE			; 800		
Header2:									; Typically 4.5ms
	pauseus WidHDR						; 700 + 10N
	b1 = DATABITS						; 500		; Number of bit pulses 
	w1 = wx								; 500		; To allow flag-testing
Sendbit:									; Nominal bit-pulses = 562ms
	on bit14 goto bitsdone					; 500 	; Fall through until bit14 = 0 (900 ICs)
Pulse:									; 562us = 4500 ICs @ 32MHz
	dec b1								; 600
	pwmout PWMop,PERIOD,DUTY		; 800 		; Start pulse
	if wy > 32767 then longgap			; 800		; Jump for long bit period
	wy = wy + wy + bit31				; 900		; Shift left and get carry
	w1 = w1 + w1						; 600
	pauseus WidPUL0					; 700 + 10N
Gap:										; 562us gap
	pwmout PWMop,PERIOD,IDLE		; 800		; End of pulse
	pauseus WidGAP0					; 700 + 10N
 	goto sendbit						; 800	
Longgap:									; ~1.7ms
	pauseus WidPUL1					; 700 + 10N
Gap1:
	pwmout PWMop,PERIOD,IDLE	 	; 800		; End of pulse
	wy = wy + wy + bit31 				; 900 	 	; Shift left and get carry
	w1 = w1 + w1 					; 600		; Shift Low word Left
	pauseus 	WidGAP1				; 700 + 10N
	goto sendbit						; 800
Bitsdone:								; (Total ~150 bytes)
 return

NEC_RPT:
Repeatcode:
	pwmout PWMop,PERIOD,DUTY		; 800		; Start pulse
	pauseus WidAGCT					; 900 * 8 - 700 = 7130
	pwmout PWMop,PERIOD,IDLE		; 800		; End of pulse
	pauseus WidHDR2					; 225 * 8
	pwmout PWMop,PERIOD,DUTY		; 800 	 	; Start pulse
	pauseus WidPUL					; 562 * 8
	pwmout PWMop,PERIOD,IDLE		; 800		; End of pulse
return									; (Total ~45 bytes)
NEC_IR_08M2..png

Cheers, Alan.
 

AllyCat

Senior Member
Hi,
I'll post a corresponding decoder routine in a suitable location on the forum, in due course.
I think this thread is probably the most suitable location for the Receiving Code, but first an "update". ;)

It would have been possible to "reverse" my above transmitter code, both figuratively and literally (e.g. using Right-Shifts in place of Left-Shifts) but my "preference" is for code that can run at 16 MHz. Not an easy challenge with already quite efficient code at 32 MHz, but I did find a few "tricks" to draft a viable 16 MHz Program. However, it was far from elegant and not comparable with Pete's beautifully documented "background" routine, linked earlier in this thread, so I put mine "on the back burner".

Then, an interesting enquiry in the Active Forum raised issues with PICaxe's Sony/IRIN command, which is not necessarily "better" or "worse" than the NEC protocol ("horses for courses"), but it is "different". It raised a few more ideas for "tricks" and I subsequently posted a 12-bit IR Decoder Code Snippet for the Sony/IRIN protocol in the Finished Projects section, which can work reliably at 16 MHz. Some of the principles described there have now been incorporated into the NEC code, so won't necessarily be described again here.

To devise the above, I had needed a "test transmitter", so I programmed an 08M2 with a single IROUT instruction within a few nested loops. This gave the opportunity to include a slightly modified version of my NEC code from above, but without the "Repeat" packets. The PICaxe/Sony IROUT signal is modulated (at 42 kHz) and had needed a simple R-C Low-Pass filter to retrieve the Raw Data for experimental decoding, so I adapted the NEC code to give Modulated and Raw (Baseband) outputs on the two spare pins. Easily done by just adding a LOW or HIGH pin command adjacent to each PWMOUT ...... instruction, and reducing the associated calibration time delays by about 40 units (i.e. 50 us at 32 MHz). Not really a Finished Project (at least by my standards), but it does sequentially transmit all "possible" codes (and the Raw Duty Cycle output does appear more consistent), so I'm attaching the adapted Program here for reference.

Thus, the "Sony/IROUT" project gave some more food for thought on decoding the NEC protocol (at 16 MHz) and I do now have a functional decoder program for the NEC protocol, that I plan to post here soon. But first, perhaps it's worth giving a brief comparison of the two protocols for anyone considering an "end-to-end" system design: For PICaxe users, the Sony is the obvious choice because of the embedded IRIN and IROUT commands, even in the 08M2. Up to "128 buttons" should be sufficient for most Remote Control applications (and my new code snippet offers all 12 bits, or 4096 "codes" from the IROUT instruction, if required). It's also quite "fast", transmitting each packet in 20 ms or less, compared with about 70 ms for the NEC protocol, but the Sony has no specific Error or Validity checking.

IMHO, the NEC protocol seems quite a "strange" choice, particularly for "simple" Remote Controls such as a heater; The 32 data bits and 70 ms transmission time appear excessive, but maybe allow for a vast range of different dedicated "Manufacturer Code" allocations. The recommended repetition of (Inverted) bytes reduces the capacity to 16 bits with some "protection", but again seems a strange choice, particularly in association with their "Repeat Code" concept. The inverted/duplicated bytes do give a constant message duration (always an equal number of 0 and 1 bits) but almost doubles the transmission time and gives only intrinsic Error Detection, not any direct Error Correction capability.

Normally, a simple Correction strategy would use a "majority decision" (i.e. from 3 consecutive, identical packets) and/or a two-dimensional checksum array. Therefore, combined with the "Repeat" packet header (i.e. no actual data bits transmitted; they are assumed to be unchanged), the consequence of one, or a few, "bad" bits in the first message can potentially produce an "Unrecoverable Error" (unknown value) regardless of how long the "transmitter button" is pressed. But in practice, some error correction capability may be possible if the designer has read up his theory on Hamming Distance. ;)

Finally, just a few more "cons": The overall transmitting duty cycle is around a greedy 65%, (i.e 70 ms in 110 ms), which is probably why the "Repeat" option was devised. Also, the LS Bit-first coding implies Right-Shifting, which cannot be programmed as efficiently as Left-shifting in M2 PICaxes, particuarly if the bits are not directly-addressable. The same does apply to the Sony and even RS232 formats, but 32 is the full availability of PICaxe individual bits (i.e. in b0 - b3, or w0 and w1); I wouldn't want to allocate all those bits to a single Code Snippet or Subroutine, and also the coding introduces a 33rd pulse (to terminate the 32nd bit), which might initiate another bit-period and cause an overflow. Combined with PICaxe Basic's lack of a "Carry" flag, this implies that a 3rd Word may need to be added to the Shift Register, whilst the 12-bit Sony protocol fits easily within one.

I'd intended to attach the test transmitter program as a file, but as I'm still (just) within the 10000 character limit, it might as well be in-line. ;)
Code:
; Test Transmitter for 12-bits Sony IR Protocol (IROUT) and NEC 32-bits 
; AllyCat March 2021
; Use 2n2 // 10k {with pullup diode} filter on C.1 to demodulate
#picaxe 08m2
#no_data
sertxd("IRout test on c.1 (Sony); c.2 (NEC-mod) & c.4 (NEC-raw)",cr,lf)
symbol CALVDD = 52429    	; 1024*1.024*1000/20  (ADC steps * Ref V / Resolution in mV)
symbol SIRCout = c.1		; Modulated
symbol NECmod = c.2		; PWM output
symbol NECout = c.4		; Unmodulated
pause 1000
do
for b11 = 1 to 255
	calibadc10 w1        				; Measure FVR (nominal 1.024 v) relative to Vdd (1024 steps)
	w2 = w1 / 2 + CALVDD			; Effectively round up CALVDD by half a (result) bit
	w2 = w2 / w1        				; Take the reciprocal to calculate (half) Vdd (tens of mV)
	calibadc10 w1        				; Read the value again because noise may be present :)
	w1 = CALVDD / w1 + w2		; Calculate Vdd/2 again and add in the first value
	sertxd(#b0," Vdd= ",#w1,"0 mV",cr,lf)
	for b10 = 0 to 255
		sertxd(#b11,":",#b10," ")		; Report the supply voltage
		irout SIRCout,b11,b10		; Send the Sony data packet (4 MHz clock)
		setfreq m32
		b4 = b11					; Address
		b5 = b4 xor 255			; Extended (complement) Address
		b6 = b10					; Command
		b7 = b6 xor 255			; Command Complement 
		call NEC_TX
		setfreq m4
	next
next
loop

symbol tempb = b1				; Bit-addressable
symbol tempw = w1				; Bit-addressable
symbol wx = w2					; NEC Address (& complement)
symbol wy = w3					; NEC Command (& complement)
symbol PERIOD 	= 210			; For 38.5 kHz @ 32MHz
symbol DUTY 	= 280				; Carrier DutyCycle (844 = 100%)
symbol IDLE 	= 0 					; Idle Low (900 for High)
symbol DATABITS = 64 + 32			; 64 gives Active low flag (33 counts to bit14 = 0)
; Timing Constants
	#IFDEF 08M2
symbol WidAGC 	= 4310	; 4420	; Pauseus units = 0.125us
symbol WidHDR 	= 3300	; 3200	; 4.5 ms standard Header Gap
symbol WidPUL0 = 32		;   48	; For 0 Data pulse
symbol WidPUL1 = 144		;  160	; For 1 Data pulse
symbol WidGAP0 = 100		;  70		; For 0 Data Gap
symbol WidGAP1 = 820		;  780	; For 1 Data Gap
symbol DelREDO = 287		;  327 	; Pause (125us units) to next transmission
	#ENDIF
symbol PWMop 	= c.2			; Transmit Output pin

NEC_TX:
SendAGC:							; Framing pulse of continuous carrier (typically 9ms)
	high NECout
	pwmout PWMop,PERIOD,DUTY		; For 9 ms burst of 38kHz
Mirror:									; Reverse the bit sequence to allow Left-Shifts
	bptr = 4								; Address/Command Register address (bytes 4 to 7)
do											; \/PIC Instruction Cycles
	b3 = @bptr							; 500  ; Must be bit-addressable
	bit16 = bit31 : bit17 = bit30 ; 940
	bit18 = bit29 : bit19 = bit28 ; 940
	bit20 = bit27 : bit21 = bit26 ; 940
	bit22 = bit25 : bit23 = bit24	; 940
	@bptrinc = b2						; 500 + 4760 * 4 = 19040
loop until bptr = 8					; 4400
	b1 = b4 : b4 = b7 : b7 = b1	; 1500		; Swap bytes
	b1 = b5 : b5 = b6 : b6 = b1	; Total swap = 3.3ms @ 32MHz
	pauseus WidAGC						; 700 + 10N ICs
	low NECout
	pwmout PWMop,PERIOD,IDLE			; 800		
Header2:									; Typically 4.5ms
	pauseus WidHDR						; 700 + 10N
	b1 = DATABITS						; 500		; Number of bit pulses 
	w1 = wx								; 500		; To allow flag-testing
Sendbit:									; Nominal bit-pulses = 562ms
	on bit14 goto bitsdone			; 500 	; Fall through until bit14 = 0 (900 ICs)
Pulse:									; 562us = 4500 ICs @ 32MHz
	dec b1								; 600
	High NECout
	pwmout PWMop,PERIOD,DUTY		; 800 		; Start pulse
	if wy > 32767 then longgap		; 800		; Jump for long bit period
	wy = wy + wy + bit31				; 900		; Shift left and get carry
	w1 = w1 + w1						; 600
	pauseus WidPUL0					; 700 + 10N
Gap:										; 562us gap
	low NECout
	pwmout PWMop,PERIOD,IDLE	; 800		; End of pulse
	pauseus WidGAP0				; 700 + 10N
 	goto sendbit					; 800	
Longgap:							; ~1.7ms
	pauseus WidPUL1				; 700 + 10N
Gap1:
	low NECout
	pwmout PWMop,PERIOD,IDLE	; 800		; End of pulse
	wy = wy + wy + bit31 			; 900 	; Shift left and get carry
	w1 = w1 + w1 				; 600		; Shift Low word Left
	pauseus 	WidGAP1			; 700 + 10N
	goto sendbit					; 800
Bitsdone:			; (Total ~150 bytes)
 return
Cheers, Alan.
 
Last edited:
Top