Approaches to a Manchester Decoder in Software - Page 1
Forum section: electronics / general

author: Dabbot
author_rank: Regular Contributor
timestamp: July 14, 2019, 03:48:45 am
content: I've managed to get a one-way IR link between two chips using the usual Vishay TSOP series receiver and a simple custom Manchester protocol for data transmission.The routine I've written is basically an interrupt driven state machine. It reads the data & re-clocks on each mid-bit edge, ignoring inter-bit edges and terminating reception if the mid-bit edge does not arrive within a window (currently 1/2 bit time either side of the expected edge).It works well enough, but should I be:a) Re-clocking on the mid-bit edge entirely, instead of performing a smaller adjustment?b) Terminating reception entirely if it skips a beat, which would most likely just be a single bit error?c) Reading the data directly after the mid-bit edge?

author: mskeete
author_rank: Contributor
timestamp: July 14, 2019, 04:40:51 am
content: Is this similar to RC5?You shouldn't need to reclock so often unless you have inaccurate clocks and or super long messages

author: Dabbot
author_rank: Regular Contributor
timestamp: July 14, 2019, 04:58:23 am
content: Quote from: mskeete on July 14, 2019, 04:40:51 amSomewhat similar. 38Khz carrier. 1.9KHz bit period. One start bit, 16 data bits.As for re-clocking so often, my thinking was mitigating a wonky start bit and ISR latency jitter.

author: mskeete
author_rank: Contributor
timestamp: July 14, 2019, 10:40:31 am
content: many moons ago (1996) I wrote some code for a PIC 16C84 to decode RC5The start bit was detected by polling in the main routine and then the interrupts were synchronised (by reloading the interrupt timer counter) to occur in the middle of the expected data bits.If the half bits weren't 0,1 or 1,0 (Manchester encoding) then there's an error (dont care which type of error) and you reset and wait again.That worked very well for me and the PIC was running form a 4Mhz resonator.You're probably using something a lot faster with smaller latencies so if its working for you then i wouldn't worry too much

author: Dabbot
author_rank: Regular Contributor
timestamp: July 14, 2019, 11:01:26 am
content: Quote from: mskeete on July 14, 2019, 10:40:31 amPIC12F1572 and TMR2 at 8MHz internal RC.I'll give a slightly different method some thought. It'd be nice to read in each half of the bits into separate registers, then XOR them together for bit error detection.

author: mskeete
author_rank: Contributor
timestamp: July 14, 2019, 11:22:15 am
content: Attached is the code I wrote - complete with spelling mistakes.It's written in assembler.It was for a remote controlled audio preamplifier so there's a load of code in there for controlling a LMC1983 and storing stuff in EEPROM which you can ignore.

author: Dabbot
author_rank: Regular Contributor
timestamp: July 15, 2019, 11:07:22 am
content: Nicely done. I rewrote my code to take a similar approach to you, just letting the timer fire in the middle of every half bit.It works well! Just for fun, I brought it up on the scope. One trace for the IR receiver output, one trace for a micro output that blips when the timer fires. It gets a bit close to the edge of the half bits at the end of the transmission, but not bad.

author: wrynczuk
author_rank: Newbie
timestamp: July 16, 2019, 11:39:47 am
content: There is another interesting and neat approach:https://www.clearwater.com.au/code/rc5Believe it or not, but I implemented the algorithm successfully (yet few years ago) on an industrial PLC using the ladder language. Definitely, it is a good solution for low-end devices.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 16, 2019, 01:54:51 pm
content: Quote from: Dabbot on July 14, 2019, 04:58:23 amQuote from: Dabbot on July 14, 2019, 11:01:26 amI wrote a decoder all in software (polling, no interrupts, no timers), it's for a 6502 @ 1MHz. It self syncs and can decode manchester streams of between 13 and 15 kbits/s in packets of up to 256 bytes maximum.https://gist.github.com/xk/1f455af5c9017150b8d6234743e9c2a3Quote from: Dabbot on July 14, 2019, 03:48:45 amMine resyncs at every mid-bit edge, terminates at the first missing mid-bit edge, and it's an err unless the missing mid-bit edge happens just after a complete byte, in which case it means an end of packet which is ok.What do you mean with "Reading the data directly after the mid-bit edge"? The mid-bit edge (direction) is the data, isn't it?

author: Dabbot
author_rank: Regular Contributor
timestamp: July 16, 2019, 09:37:54 pm
content: Quote from: GeorgeOfTheJungle on July 16, 2019, 01:54:51 pmThe interrupt is generated by the edge (interrupt on change), so the ISR reads the value of the pin to determine what the edge was.Yes, the mid-bit edge is the data. And so is the state of the pin before (inverted) and after said edge within the bit period.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 17, 2019, 10:57:59 am
content: Quote from: Dabbot on July 16, 2019, 09:37:54 pmOh, ok. Isn't there a race condition?

author: Dabbot
author_rank: Regular Contributor
timestamp: July 17, 2019, 11:17:42 am
content: Quote from: GeorgeOfTheJungle on July 17, 2019, 10:57:59 amNo race condition. The IOC peripheral raises an interrupt when there's a transition on the pin, so the CPU executes the ISR. The ISR checks the source of the interrupt and, if it was from the IOC, reads the value on the pin.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 17, 2019, 01:54:30 pm
content: Quote from: Dabbot on July 17, 2019, 11:17:42 amAnd, if the pin has flipped again before the ISR gets to read the value? Say it was a glitch, a 5ns pulse for example?

author: mskeete
author_rank: Contributor
timestamp: July 17, 2019, 02:40:27 pm
content: I think you'd have to be very unlucky to get a set of glitches compatible with Manchester encoding.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: July 17, 2019, 02:53:55 pm
content: Use a capture peripheral to capture and interrupt on every edge, filter noise by rejecting any gap that's too long or short, classify into long or short gaps and decode with a state machine.

author: Kjelt
author_rank: Super Contributor
timestamp: July 17, 2019, 03:09:16 pm
content: IIRC on my work we oversampled 16x the signal.So just sample 16x the bittime and store in a queue and process when you have the time or the queue is getting full.8x also worked, 4x was not fullproof IIRC (long time ago)

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 17, 2019, 04:08:31 pm
content: Ok, that PIC does not tell you if it's been a rising or falling edge. So you can either use two pins tied together, one configured to interrupt on rising edges and the other on falling edges, or do as you're doing with a single pin and look at the pin's value upon entry to the ISR. Hopefully, if it was a glitch and there's been two edges, the ISR will be immediately triggered again upon rti? (If not you've got a problem) And you'll see the timing as not ok on the second invocation of the ISR and abort?

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 17, 2019, 04:40:51 pm
content: Quote from: GeorgeOfTheJungle on July 17, 2019, 04:08:31 pmI would not necessarily count on that, but additionally checking that every edge detected leads to a different state should at least ensure that you won't make a decoding error?

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 17, 2019, 04:48:18 pm
content: Yes, that's a good idea. Problem solved!

author: nctnico
author_rank: Super Contributor
timestamp: July 17, 2019, 07:36:35 pm
content: Quote from: Dabbot on July 16, 2019, 09:37:54 pmDon't implement it like that. Having interrupts fire on GPIOs is asking for trouble. If something goes wrong in the hardware a GPIO interrupt can easely swamp your processor and at least cause an uneven / fluctuating / erratic processing load causing hard to reproduce problems and worst case crash the whole thing.What I always do to decode manchester like streams is use a timer interrupt at around 6 times the bitrate (in your case it would be 11.4kHz) and sample the input from the timer interrupt. When you detect a change in the signal you can check how many timer interrupts ago the previous edge occured. Between 2 and 3 ticks is a short pulse, between 4 and 7 ticks is a long pulse.

author: Kjelt
author_rank: Super Contributor
timestamp: July 17, 2019, 08:26:02 pm
content: That's how we did it but then with 8x or even 16x oversampling. The int serv routine is only sample the pin and store the boolean or bit in a ringbuffer aka the queue and exit.When there is time to process or the queue is full you read the queue and write the decoded bits in a different buffer. Then when it is complete you decode the buffer and execute the command or whatever needs to be done.The only exception is when you're processor has absolutely nothing else to do, if it is a dedicated decoder like the old SAA3049 RC5 decoder back in the day.In all the other cases first do the RMA analysis.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 17, 2019, 08:32:22 pm
content: To each his own.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: July 17, 2019, 10:42:16 pm
content: Also bear in mind that Manchester code is polarity-ambivolent so you don't actually need to know the polarity of the edges as long as you detect all of them

author: nctnico
author_rank: Super Contributor
timestamp: July 17, 2019, 11:01:09 pm
content: Quote from: Kjelt on July 17, 2019, 08:26:02 pmThat is rather cumbersome because of the overhead of buffering and this way also brings non-deterministic code execution. You'll need the processor time anyway and it takes less CPU cycles to do the job when the data enters the system. Life becomes much simpler if you view the interrupt controller as a hardware based time slicing OS.

author: Kjelt
author_rank: Super Contributor
timestamp: July 18, 2019, 07:07:22 am
content: Quote from: nctnico on July 17, 2019, 11:01:09 pmIt all depends on the other processes and priorities that need to be executed. If there are processes running that are time critical you don't want to get stuck too long in your interrupt service routine.In the end we had multiple signals that needed sampling so we made a RMA to balance the load. We did not have an RTOS back then (8 bitter) and just made our own version handling the tasks according to priorities. IIRC the "jitter" on the rc5 decoding was less than 3ms since it was a human interface well within specifications, I don't know the max response time to human interfaces anymore but believe it was something in the order of 50-100ms or thereabout.

author: Dabbot
author_rank: Regular Contributor
timestamp: July 18, 2019, 07:48:01 am
content: Quote from: nctnico on July 17, 2019, 07:36:35 pmGood point. But short of discarding IOC events altogether, I think it would be better to simply enable them when expected. Sample as you need but take advantage of IOC to realign to the signal.

author: Dabbot
author_rank: Regular Contributor
timestamp: July 18, 2019, 07:57:50 am
content: Quote from: GeorgeOfTheJungle on July 17, 2019, 04:08:31 pmIt can, but it takes a bit more work. The IOC peripheral has two separate circuits for rising and falling edges, but they both flip the same interrupt flag. These two parts can be enabled separately, meaning you get only one type or the other, but this of course requires a bit more work in software.I was just taking the lazy approach of enabling both and checking the pin value immediately following. And there's nothing wrong with that approach, given the speed the ISR is coming through at compared to the signal.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 18, 2019, 10:19:50 am
content: Quote from: mikeselectricstuff on July 17, 2019, 10:42:16 pmWhat you say only applies to differential Manchester encoding I think. In a single ended stream the direction is the value, thus you need to know it.

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 18, 2019, 01:33:19 pm
content: Quote from: GeorgeOfTheJungle on July 18, 2019, 10:19:50 amYep. Original Manchester relies on fixed transitions to code 1's and 0's (there are two opposing conventions though). So it's definitely not polarity-agnostic.Differential Manchester is.

author: NorthGuy
author_rank: Super Contributor
timestamp: July 18, 2019, 02:46:48 pm
content: It is not necessary to capture both edges. If you only measure times between consecutive rising edges (or consecutive falling edges), this is enough to re-construct the data.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 19, 2019, 12:52:01 pm
content: Quote from: NorthGuy on July 18, 2019, 02:46:48 pmSorry but I don't get it, please explain, if S is short and L is long, what byte is this?if rising edge= 1⎺\___/‾‾‾\___/‾\_/‾‾‾\_/‾\_/‾\_ => 01011000S L L L S S L S S S S_/‾‾‾\___/‾‾‾\_/‾\___/‾\_/‾\_/‾ => 10100111if rising edge= 0⎺\___/‾‾‾\___/‾\_/‾‾‾\_/‾\_/‾\_ => 10100111S L L L S S L S S S S_/‾‾‾\___/‾‾‾\_/‾\___/‾\_/‾\_/‾ => 01011000

author: NorthGuy
author_rank: Super Contributor
timestamp: July 19, 2019, 02:19:31 pm
content: Quote from: GeorgeOfTheJungle on July 19, 2019, 12:52:01 pmYour shorts and longs are between edges of different polarity. What I said is that you only need to detect edges of one polarity - either you detect only rising edges, or only falling edges. And then you measure the distance between consecutive edges of the same polarity. Of course, you know whether you capture rising or falling edges. You also know whether your Manchester represnts '1' with riding edges or with falling edges.You need to synchronize it first so that you know where the edge is as you normally do at the beginning - a string of '1', or a string of '0' - which looks the same, but you know how the transmission begins. Then you start decoding. Assuming rising edge is '1' and falling edge is '0':If the last decoded bit was '1' and the distance between consecutive rising edges is:- 1 bit - you received "1"- 1 1/2 bits - you received "00"- 2 bits - you received "01"If the last decoded bit was '0' and the distance between consecutive rising edges is:- 1 bit - you received "0"- 1 1/2 bits - you received "1"

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 19, 2019, 07:58:19 pm
content: Quote from: NorthGuy on July 19, 2019, 02:19:31 pmOk, we're on the same page then. With the durations S L L L S S L S S S S alone it isn't enough to decode the data, AFAIK. Thanks!

author: NorthGuy
author_rank: Super Contributor
timestamp: July 19, 2019, 08:32:39 pm
content: Quote from: GeorgeOfTheJungle on July 19, 2019, 07:58:19 pmDon't know what do you mean by the "same page". We're talking about totally different things. I'm saying that you can decode by capturing only one kind of edges (either risingorfalling) as opposed to capturing both risingandfalling edges. You're saying something about pulse duration which you classified as S and L, but I don't really get what you're saying and how it is related to my considerations.

author: GeorgeOfTheJungle
author_rank: Super Contributor
timestamp: July 19, 2019, 10:51:22 pm
content: Quote from: NorthGuy on July 19, 2019, 08:32:39 pmYou said:Quote

author: NorthGuy
author_rank: Super Contributor
timestamp: July 20, 2019, 05:01:35 am
content: Quote from: GeorgeOfTheJungle on July 19, 2019, 10:51:22 pmYep. I don't know how to explain this better. I'll try graphics. Here's the signal (from your previous post):Code:⎺\___/‾‾‾\___/‾\_/‾‾‾\_/‾\_/‾\_Here's all the edges (rising and falling):Code:\ / \ / \ / \ / \ / \Here's rising edges only:Code:/ / / / /Here's the distances between them:Code:2, 1, 1 1/2, 1This is enough to reconstruct data.Here's the method in case you're interested (from my previous post):Quote from: NorthGuy on July 19, 2019, 02:19:31 pmMakes sense?

author: Dabbot
author_rank: Regular Contributor
timestamp: July 20, 2019, 07:21:10 am
content: Quote from: NorthGuy on July 19, 2019, 02:19:31 pmThat would be an interesting method to try. I might give that a go once I'm done experimenting with the current code.

author: Dabbot
author_rank: Regular Contributor
timestamp: July 21, 2019, 05:27:28 am
content: Here's what I've come up with. Code below.When the signal begins, TMR2 is loaded with the period and offset to interrupt in the middle of each half bit. The value on the pin is sampled each time TMR2 fires, with the left-hand and right-hand bits stored in respective registers. However, the receiver will not check the vaildity of received bits, leaving that for main code.It will reclock on mid-bit edges, but only within a window, ignoring anything too whacky.I've also taken nctnico's advice on potential IOC spam trapping the microcontroller in its ISR, so IOC is only enabled when expecting an edge and disabled once it is received / missed.The receiver indicates completion via its state register. It's enabled again in main code by clearing the state register and enabling IOC on the pin.The main code just transforms and spits the data out to the EUSART.Code:#include "p12f1572.inc"; CONFIG1; __config 0xFFAC__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_SWDTEN & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOREN_ON & _CLKOUTEN_OFF; CONFIG2; __config 0xFEFC__CONFIG _CONFIG2, _WRT_ALL & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LPBOREN_OFF & _LVP_ON; ********** Common RAM working registers **********; Maincblock 0x70AREG, BREG, CREG, DREGLREG, HREG, TEMP0, TEMP1endcTEMP = 0x76; ISRcblock 0x78ISR_AREG, ISR_BREG, ISR_CREG, ISR_DREGISR_LREG, ISR_HREG, ISR_TEMP0, ISR_TEMP1endcISR_TEMP = 0x7E; ********** General registers **********; IR receiver registers on bank 0ISR_IRRX_STATE = 0x0060; Receiver stateISR_IRRX_DATL0 = 0x0061; First byte of left hand bitsISR_IRRX_DATL1 = 0x0062; Second byte of left hand bitsISR_IRRX_DATR0 = 0x0063; First byte of right hand bitsISR_IRRX_DATR1 = 0x0064; Second byte of right hand bits; ********** FIFO locations **********RX_FIFO = 0x0140; Start of receiver FIFO data structure. Occupies 10 bytes.TX_FIFO = 0x0150; Start of transmitter FIFO data structure. Occupies 10 bytes.; ********** Constants **********; PR2 values for 3.8KBd manchester; Assume Fosc/4 = 2MHz, TMR2 prescaler = 1:4IRRX_14BIT = d'65'; PR2 value for 1/4 bit periodIRRX_12BIT = d'131'; PR2 value for 1/2 bit periodIRRX_RCLK_L = d'45'; Reclocking window lower limitIRRX_RCLK_H = d'85'; Reclocking window upper limit; ********** Reset vector **********RES_VECT CODE 0x0000GOTO INIT; ********** Interrupt vector **********ISR CODE 0x0004BANKSEL PIR1BTFSC PIR1, TMR2IF; Infrared receiver. TMR2 handler.GOTO ISR_IRRX_TBTFSC INTCON, IOCIF; Infrared receiver. IOC handler.GOTO ISR_IRRX_PBTFSC PIR1, RCIF; EUSART receiverGOTO ISR_EURXBTFSC PIR1, TXIF; EUSART transmitterGOTO ISR_EUTXRESET; Oops!ISR_IRRX_TBCF PIR1, TMR2IFBANKSEL LATA; Pulse debugging pinBSF LATA, LATA0BCF LATA, LATA0BANKSEL 0; Read branching stateMOVLW b'00000011'ANDWF ISR_IRRX_STATE, WBRW; Branch to handlerGOTO IRRX_T_STARTGOTO IRRX_T_BITLGOTO IRRX_T_BITRGOTO IRRX_T_FINISHIRRX_T_START; We shouldn't get a timeout in this stateBANKSEL T2CONBCF T2CON, TMR2ON; Turn TMR2 offBANKSEL 0BSF ISR_IRRX_STATE, 0; Set branching state to finishBSF ISR_IRRX_STATE, 1RETFIE; DoneIRRX_T_BITL; Receive left-hand bitBANKSEL 0BCF STATUS, C; Read inverted dataBTFSS PORTA, RA2; PORTA is on bank 0BSF STATUS, CMOVF ISR_IRRX_STATE, W; Have we received all data?XORLW b'01000101'; 16 bits + start bit + current branching stateBTFSC STATUS, Z; If not, continueGOTO IRRX_T_LAST; If so, finish upRLF ISR_IRRX_DATL1, F; Rotate left bit onto registersRLF ISR_IRRX_DATL0, FBCF ISR_IRRX_STATE, 0; Set branching state to BITRBSF ISR_IRRX_STATE, 1BANKSEL IOCAF; Enable IOC for RA2BSF IOCAP, IOCAP2BSF IOCAN, IOCAN2RETFIE; DoneIRRX_T_BITR; Receive right-hand bitBANKSEL 0BCF STATUS, C; Read inverted dataBTFSS PORTA, RA2; PORTA is on bank 0BSF STATUS, CRLF ISR_IRRX_DATR1, F; Rotate right bit onto registersRLF ISR_IRRX_DATR0, FMOVLW b'00000100'; Increment bit countADDWF ISR_IRRX_STATE, FBSF ISR_IRRX_STATE, 0; Set branching state to BITLBCF ISR_IRRX_STATE, 1BANKSEL IOCAFBCF IOCAP, IOCAP2; Disable IOC for RA2BCF IOCAN, IOCAN2BCF IOCAF, IOCAF2; Clear IOC flag for RA2RETFIE; DoneIRRX_T_LAST; All bits received and last edge has passedBSF ISR_IRRX_STATE, 0; Set state to FINISHBSF ISR_IRRX_STATE, 1BANKSEL T2CON; Turn TMR2 offBCF T2CON, TMR2ONRETFIE; DoneIRRX_T_FINISH; We shouldn't get a timeout in this stateBANKSEL T2CON; Turn TMR2 offBCF T2CON, TMR2ONRETFIE; DoneISR_IRRX_PBANKSEL IOCAFBCF IOCAP, IOCAP2; Disable IOC for RA2BCF IOCAN, IOCAN2BCF IOCAF, IOCAF2; Clear IOC flag for RA2BANKSEL 0; Read branching stateMOVLW b'00000011'ANDWF ISR_IRRX_STATE, WBRW; Branch to handlerGOTO IRRX_P_STARTRETFIEGOTO IRRX_P_RCLKRETFIEIRRX_P_START; Start edge received. ISR latency (instruction cycles): 17; When accounting for ISR latency, use:; ( instruction cycles + 4 ) / TMR2 prescaler (4)BANKSEL T2CON; Configure and start TMR2MOVLW IRRX_14BIT + 7; Offset TMR2 to middle of 1/2 bit, accounting for ISR latencyMOVWF TMR2MOVLW IRRX_12BIT; TMR2 will fire in the middle of every 1/2 bitMOVWF PR2BSF T2CON, TMR2ON; ISR latency (instruction cycles): 23BANKSEL 0BSF ISR_IRRX_STATE, 0; Set branching state to BITLBCF ISR_IRRX_STATE, 1RETFIE; DoneIRRX_P_RCLK; Reclock TMR2. ISR latency (instruction cycles): 17; When accounting for ISR latency, use:; ( instruction cycles + 4 ) / TMR2 prescaler (4)BANKSEL T2CONMOVF TMR2, W; Capture TMR2 value. ISR latency (instruction cycles): 19ADDLW -6; Account for ISR latencyMOVWF ISR_TEMP;MOVF ISR_TEMP, WSUBLW IRRX_RCLK_H; Determine if the edge arrived within the reclock windowMOVLW IRRX_RCLK_L; STATUS, C will be set if within the reclock windowBTFSC STATUS, CSUBWF ISR_TEMP, WMOVLW IRRX_14BIT + 8; Offset TMR2 to middle of 1/2 bit, accounting for ISR latencyBTFSC STATUS, C; Skip the adjustment if not within reclock windowMOVWF TMR2; ISR latency (instruction cycles): 28BANKSEL LATABTFSC STATUS, C; Pulse debugging pin if reclockedBSF LATA, LATA0BCF LATA, LATA0RETFIE; DoneISR_EURXBANKSEL RCSTABTFSC RCSTA, FERR; Discard if framing errorGOTO EURX_DISCARDMOVLW high RX_FIFO; Select RX FIFOMOVWF FSR0HMOVLW low RX_FIFOMOVWF FSR0MOVIW 1 ; Check if RX FIFO is fullSUBWF INDF0, WXORLW d'8'BTFSC STATUS, Z; Discard if RX FIFO is fullGOTO EURX_DISCARDMOVF INDF0, W; Enqueue data into RX FIFOANDLW d'7'MOVWF ISR_TEMPADDWF FSR0L, FMOVF RCREG, WMOVWI 2 MOVF ISR_TEMP, WSUBWF FSR0L, FINCF INDF0, FRETFIE; DoneEURX_DISCARDMOVF RCREG, W; Discard received characterRETFIE; DoneISR_EUTXBANKSEL TXREGMOVLW high TX_FIFO; Select TX FIFOMOVWF FSR0HMOVLW low TX_FIFOMOVWF FSR0MOVIW 1 ; Check if TX FIFO is emptyXORWF INDF0, WBTFSC STATUS, Z; If TX FIFO is empty, disable interrupts and returnGOTO EUTX_OFFMOVIW ++ FSR0; Dequeue data from TX FIFO and transmitANDLW d'7'MOVWF ISR_TEMPADDWF FSR0L, FMOVIW 1 MOVWF TXREGMOVF ISR_TEMP, WSUBWF FSR0L, FINCF INDF0, FRETFIE; DoneEUTX_OFF; Switch transmitter interrupts offBANKSEL PIE1BCF PIE1, TXIERETFIE; Done; ********** Main loop **********MAIN_PROG CODEINITBANKSEL OSCCONMOVLW b'01110000'; Software PLL disabled, 8MHz IRCF, clock as per FOSC<1:0> in config wordsMOVWF OSCCONBANKSEL LATA; Pin configCLRF LATABANKSEL ANSELABCF ANSELA, ANSA2; Switch off analog for RA2 (IR RX)BCF ANSELA, ANSA4; Switch off analog for RA4 (UART TX)BANKSEL TRISABCF TRISA, TRISA0; RA0 as output for LED / debug;BANKSEL IOCAP;BSF IOCAP, IOCAP2; Enable positive edge interrupts for RA2;BSF IOCAN, IOCAN2; Enable negative edge interrupts for RA2BANKSEL T2CONMOVLW b'00000001'; Configure TMR2. No postscaler, off, 1:4 prescalerMOVWF T2CONBANKSEL APFCON; Place the EUSART on alternate pinsBSF APFCON, RXDTSEL; RX on RA5BSF APFCON, TXCKSEL; TX on RA4BANKSEL BAUDCON; EUSART;MOVLW b'01000000'; Default POR value;MOVWF BAUDCONBCF TXSTA, SYNC; 9600 baud rate async for 8MHz FoscBCF TXSTA, BRGHBCF BAUDCON, BRG16MOVLW d'12'MOVWF SPBRGCLRF SPBRGHBSF TXSTA, TXEN; Enable transmitterBSF RCSTA, CREN; Enable receiverBSF RCSTA, SPEN; Enable the serial portBANKSEL PIE1; InterruptsBSF PIE1, RCIE; Enable receiver interruptBSF PIE1, TMR2IE; TMR2 interruptBSF INTCON, PEIE; Enable peripheral interruptsBSF INTCON, IOCIE; Enable interrupt on changeBSF INTCON, GIE; Enable global interruptsBANKSEL RX_FIFO; Initialise the FIFOsCLRF RX_FIFOCLRF RX_FIFO + 1CLRF TX_FIFOCLRF TX_FIFO + 1BANKSEL 0MAIN_LOOPCLRF ISR_IRRX_STATE; Reset IR receiver stateBANKSEL IOCAP; Enable IR receiverBSF IOCAN, IOCAN2BANKSEL 0; Wait until we have data from IR receiverWAIT_IR_DATAMOVLW b'00000011'ANDWF ISR_IRRX_STATE, WXORLW b'00000011'BTFSS STATUS, ZGOTO WAIT_IR_DATASWAPF ISR_IRRX_STATE, W; Convert data to hexadecimal chars and send via UARTCALL BIN2HEXCALL SEND_CHARMOVF ISR_IRRX_STATE, WCALL BIN2HEXCALL SEND_CHARMOVLW a' 'CALL SEND_CHARSWAPF ISR_IRRX_DATL0, WCALL BIN2HEXCALL SEND_CHARMOVF ISR_IRRX_DATL0, WCALL BIN2HEXCALL SEND_CHARMOVLW a' 'CALL SEND_CHARSWAPF ISR_IRRX_DATL1, WCALL BIN2HEXCALL SEND_CHARMOVF ISR_IRRX_DATL1, WCALL BIN2HEXCALL SEND_CHARMOVLW a' 'CALL SEND_CHARSWAPF ISR_IRRX_DATR0, WCALL BIN2HEXCALL SEND_CHARMOVF ISR_IRRX_DATR0, WCALL BIN2HEXCALL SEND_CHARMOVLW a' 'CALL SEND_CHARSWAPF ISR_IRRX_DATR1, WCALL BIN2HEXCALL SEND_CHARMOVF ISR_IRRX_DATR1, WCALL BIN2HEXCALL SEND_CHARMOVLW d'13'CALL SEND_CHARMOVLW d'10'CALL SEND_CHARGOTO MAIN_LOOP; ********** Subroutines **********; Enqueue data in WREG for transmission via EUSART.; If the FIFO is full, wait for room.SEND_CHARMOVWF TEMP0; Store data to sendMOVLW high TX_FIFO; Select TX FIFO data structureMOVWF FSR0HMOVLW low TX_FIFOMOVWF FSR0SEND_CHAR_WMOVIW 1 ; Wait for roomSUBWF INDF0, WXORLW d'8'BTFSC STATUS, ZGOTO SEND_CHAR_WMOVF INDF0, W; Enqueue data onto FIFOANDLW d'7'MOVWF TEMP1ADDWF FSR0L, FMOVF TEMP0, WMOVWI 2 MOVF TEMP1, WSUBWF FSR0L, FINCF INDF0, FMOVF BSR, W; Back up BSRBANKSEL PIE1; Enable the transmitterBSF PIE1, TXIEMOVWF BSR; Restore BSRRETLW d'0'; Done; Return the ASCII hexadecimal character; for the least significant nibble in WREG.BIN2HEXANDLW b'00001111'BRWRETLW a'0'RETLW a'1'RETLW a'2'RETLW a'3'RETLW a'4'RETLW a'5'RETLW a'6'RETLW a'7'RETLW a'8'RETLW a'9'RETLW a'A'RETLW a'B'RETLW a'C'RETLW a'D'RETLW a'E'RETLW a'F'END