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.
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.
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.