NRC-17 Decoder

The Nokia NRC-17 protocol transmits 16 real bits per message. Therefore all data will be shown in hexadecimal format on the PIC IR Receiver.
The four most significant bits of a message are the "sub-code" and can be seen as an extension to the 4-bit address. This sub-code will be displayed on the left most digit of the PIC IR Receiver, followed by the hexadecimal address on the next digit.
The 8 bit command number is displayed on the 2 right most digits.

NRC-17 will send a start message when you press a button on the remote, and a stop message when you release the button again. In between the actual message belonging to that particular key is transmitted repeatedly. These start and stop messages have the sub-code/address/command code $FFFE.
I found it rather disturbing to see the $FFFE code each time I released the key. Therefore I have decided to block that particular message from being displayed.

A particular property of the NRC-17 protocol is the fact that it can signal low battery level to the receiver. The receiver can than warn the user that it is time to replace the batteries of the remote. On the PIC IR Decoder a low battery indication is shown by the right most decimal point.

NRC-17 Decoder Software

In my knowledge base you can read that the NRC-17 protocol uses bi-phase modulation of the IR carrier to transmit a total of 17 bits. A bi-phase modulated bit can be thought of as two separate bits which are always the inverse of each other. A logical zero is represented by a "10" pattern on the IR input, while a logical one is represented by a "01" pattern. That is basically what we're going to use to decode the received message.

NRC17 State Machine
Example of the states of the IR state machine

The IR decoding software relies on a state machine, which is described in detail on the RC-5 page. Two extra states have been placed in between the RC-5 states to test the width of the gap between the start pulse and the first data pulse. For the rest there are only minor changes.

;-----------------------------------------------------------------------------
;
; IR receiver state machine
;
;-----------------------------------------------------------------------------

IR_MACHINE      MOVF    IR_STATE,W      Jump to present state
                MOVWF   PCL

No changes here, compared to the RC-5 decoder software. Again this routine is called every 50µs and will immediately jump to the current state routine.

;
---------------------------------------STATE 0, WAIT FOR BEGIN OF START BIT--

IR_STATE_0      BTFSC   PORTA,4         Input still high?
                RETURN                  Yes! Nothing to do

                MOVLW   HALF_TIME/2     Wait until we're in the center of the
                MOVWF   BIT_TIMER        pre-pulse
                MOVLW   IR_STATE_1      Next stop is state 1
                MOVWF   IR_STATE
                RETURN

This state is also identical to the RC-5 decoder's IR_STATE_0. The only difference is not obvious from this code because it only involves the value of HALF_TIME, which is adapted to the different pulse width of the NRC-17 code.

;---------------------------STATE 1, START BIT DETECTED, CHECK IF IT IS REAL--

IR_STATE_1      DECFSZ  BIT_TIMER       Wait until center of pre-pulse
                RETURN                  Time's not up yet!

                BTFSC   PORTA,4         Is the input still low?
                GOTO    IR_ERROR_1      Nope! Exit with error

                MOVLW   HALF_TIME       Set interval to half a bit time
                MOVWF   BIT_TIMER
                MOVLW   5               Next 5 samples must be high!
                MOVWF   IR_SHIFT
                MOVLW   IR_STATE_2      Which is checked next
                MOVWF   IR_STATE
                RETURN

Here is another difference between the RC-5 decoder software and the NRC-17 software. We are now expecting a pause of 2.5 bit times, or 3.5 bit times in case the battery is empty. Therefore we don't initialize the shift register yet. IR_SHIFT is now abused as a half-bit counter which will cause us to wait 5 half-bit times in state 2.

;-------------------STATE 2, PRE_PULSE DETECTED, NEXT 5 SAMPLES MUST BE HIGH--

IR_STATE_2      DECFSZ  BIT_TIMER       Wait until center of half bit
                RETURN                  Time's not up yet!

                BTFSS   PORTA,4         Is input high?
                GOTO    IR_ERROR_0      Nope! Exit with error

                MOVLW   HALF_TIME       Set interval to half a bit time again
                MOVWF   BIT_TIMER
                DECFSZ  IR_SHIFT        Decrement counter
                RETURN                  Counter <> 0, don't change state yet!

                MOVLW   IR_STATE_3      Go to next state
                MOVWF   IR_STATE
                CLRF    IR_SHIFT        Prepare shift register
                CLRF    IR_SHIFT+1
                RETURN

Here's the first inserted state. Again we'll wait until BIT_TIMER has reached 0 before we do anything. Then we check to see if the input is really high, which it should be during this 2.5-bit time pause. If it is not high we go into the error state, waiting for the input to get high before returning to state 0 again.
Then IR_STATE is set to 3 only if the decremented IR_SHIFT has become 0, otherwise it will remain in state 2. At the same time the IR shift register is cleared to 0 in order to receive the new message.

;---------STATE 3, EXPECTING START BIT, OR ANOTHER PAUSE IF BATTERY IS EMPTY--

IR_STATE_3      DECFSZ  BIT_TIMER       Wait until center of first half of
                RETURN                   start bit.

                BTFSS   PORTA,4         Is it the start bit (if input is low)?
                GOTO    IR_START_BIT    Yes! Handle it!

                BSF     FLAGS,7         Set battery empty flag
                MOVLW   HALF_TIME*2     Next stop is really the start bit!
                MOVWF   BIT_TIMER
                MOVLW   IR_STATE_4      Next time handle first half of a bit
                MOVWF   IR_STATE
                RETURN

The second inserted state is where we expect the first data bit, but only if we have waited until we are in the centre of the bit. If the IR input is low now we are indeed receiving the first bit, in which case we jump to IR_START_BIT which handles the rest.
Otherwise we may be receiving an extended pause signal, which indicates the low battery situation. In that case we set the low bat flag, set the bit timer for one whole bit and set the state machine to 4.
This flag will later be merged in with digit 4. The flag will be cleared again as soon as the display is cleared together with the received message when no more IR signals are received.

;-----------------------------------IR STATE 4, WAIT FOR FIRST HALF OF A BIT--

IR_STATE_4      DECFSZ  BIT_TIMER       Wait until center of first half of bit
                RETURN                  Keep waiting!

                MOVLW   IR_STATE_5      Next state is 5 if input is low
                BTFSS   PORTA,4
IR_START_BIT    MOVLW   IR_STATE_6      Input is high, next state is 6
                MOVWF   IR_STATE

                MOVLW   HALF_TIME       Restart bit timer
                MOVWF   BIT_TIMER
                RETURN

Nothing special in this state if you compare it to IR_STATE_2 of the RC-5 decoder software. The only difference is the presence of the label IR_START_BIT, which is the jump destination from state 3.

;---------------IR STATE 5, FIRST HALF WAS HIGH NOW IT MUST BE LOW FOR A "0"--

IR_STATE_5      DECFSZ  BIT_TIMER       Wait until center of 2nd half of bit
                RETURN                  Keep waiting!

                BTFSC   PORTA,4         Is input high now?
                GOTO    .ERROR          Nope! It's an error!

                BCF     STATUS,CARRY    A 0 was received, shift it in result
                RRF     IR_SHIFT+1,F
                RRF     IR_SHIFT,F
                MOVLW   HALF_TIME       Restart bit timer
                MOVWF   BIT_TIMER
                MOVLW   IR_STATE_4      In case we need some more bits
                BTFSC   STATUS,CARRY    We're done when Carry is 1
                MOVLW   IR_STATE_7      Carry is 1, received entire message
                MOVWF   IR_STATE
                RETURN

.ERROR          MOVLW   IR_ERROR_0      Wait until input gets high before
                MOVWF   IR_STATE         returning to state 0
                RETURN

Nothing special in this state compared to state 3 of the RC-5 decoding software. Oh, there is one thing: the polarity of the received bit which is inverted compared to RC-5.

Ehhh, how can this routine tell when a complete message is received? With RC-5 we prepared the shift register with a special value, but with NRC-17 we only cleared the entire shift register.
Don't worry! The NRC-17 protocol sends a total of 17 bits, and the very first bit is always a "1". After 17 shifts this "1" will fall out of the shift register, setting the Carry flag and thus signalling the end of the message.

;---------------IR STATE 6, FIRST HALF WAS LOW NOW IT MUST BE HIGH FOR A "1"--

IR_STATE_6      DECFSZ  BIT_TIMER       Wait until center of 2nd half of bit
                RETURN                  Keep waiting!

                BTFSS   PORTA,4         Is input high now?
                GOTO    IR_ERROR_1      Nope! It's an error!

                BSF     STATUS,CARRY    A 1 was received, shift it in result
                RRF     IR_SHIFT+1,F
                RRF     IR_SHIFT,F
                MOVLW   HALF_TIME       Restart bit timer
                MOVWF   BIT_TIMER
                MOVLW   IR_STATE_4      In case we need some more bits
                BTFSC   STATUS,CARRY    We're done when Carry is 1
                MOVLW   IR_STATE_7      Carry is 1, message complete!
                MOVWF   IR_STATE
                RETURN

Again nothing special compared to state 4 of the RC-5 decoding software, only the polarity of the received bit is inverted.

;--------------------------IR STATE 7, MESSAGE RECEIVED, START PROCESSING IT--

IR_STATE_7      INCF    IR_SHIFT+1,W    Could it be address $FF, command $FE?
                BTFSS   STATUS,ZERO
                GOTO    .SKIP           No, it can't be!
                MOVLW   2
                ADDWF   IR_SHIFT,W
                BTFSC   STATUS,ZERO
                GOTO    .QUIT           It is $FF, $FE. Don't display it

.SKIP           MOVLW   CLR_TIME        Set display clear timer
                MOVWF   CLR_DELAY
                MOVLW   DP_TIME         Flash receive LED
                MOVWF   DP_DELAY

                SWAPF   IR_SHIFT+1,W    Work from left to right
                CALL    HEX2SEGMENTS
                MOVWF   DIGIT1

                MOVF    IR_SHIFT+1,W    Do the same with 2nd digit
                CALL    HEX2SEGMENTS
                ANDLW   %0111.1111      Flash dot of this digit
                MOVWF   DIGIT2

                SWAPF   IR_SHIFT,W      And with the 3d digit
                CALL    HEX2SEGMENTS
                MOVWF   DIGIT3

                MOVF    IR_SHIFT,W      And finally with the last digit
                CALL    HEX2SEGMENTS
                MOVWF   DIGIT4
                BTFSC   FLAGS,7         Is battery empty?
                BCF     DIGIT4,7        Yes! Turn right most DP on

.QUIT           MOVLW   IR_STATE_8      Done enough for now. Let's finish it
                MOVWF   IR_STATE         in the last state
                RETURN

Once we arrive at IR_STATE_7 we received the entire message, which is now going to be decoded. First of all we check whether the special start or stop code $FFFE was received, in which case we simply ignore it.
Then the display clear delay time and the receive dot timer are restarted. Converting the 4 hexadecimal nibbles to digits on the display is a rather simple task because no extra shifts or manipulations are required with NRC-17. Bit 7 of the segment pattern for digit 2 is cleared to switch on the receive dot temporarily. And bit 7 of the pattern for digit 4 gets a copy of the Low battery flag.
That's all. The only thing left to be done is wait until the IR input line returns high again which is done in IR_STATE_8.

;----------------------------------IR STATE 8, WAIT FOR INPUT TO RETURN HIGH--

IR_STATE_8
IR_ERROR_0      MOVLW   IR_STATE_0      Reset state machine only if input is
                BTFSC   PORTA,4          high
                MOVWF   IR_STATE
                RETURN

;-----------------------------------------------------------IR ERROR STATE 1--

IR_ERROR_1      MOVLW   IR_STATE_0      Return to IR state 0
                MOVWF   IR_STATE
                RETURN

These two states should be familiar to you if you have seen the identical routines of the RC-5 decoder before.