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.