'************************************************* ***************
; This code allows for data communication between 2 NRF24L01+
; modules running on a PIC microcontroller using the MSSP module.
; In my version of the code, I use DT-18 interrutps to increment a counter once
; every 500ms. When the counter reaches 10, the PIC sets the module up in TX mode
; and transmits 5 bytes. When it has completed, it will pause for 25us and
; wait for the IRQ pin to go low so that it could be reset by the user.
;
; There are no Automatic Acknowledge Functions enabled and also no Re-Transmit
; Functions enabled. The settings are for bare-bones use only.
;
; No settings for the MSSP module are listed as the register settings vary from
; PIC to PIC. Variables and constants are listed.
; Data transfer is at 1Mpbs at 0dbm. It allows for up to 32 bytes to be transmitted
; and received using the same code on each PIC. You would just need to make the array
; to your desired size and adjust the configuration accordingly.
; This code was inspired from forum member vancalst, http://sites.google.com/site/avcnetsite/mcu.
; His is a very good project I might add!
'************************************************* ***************


;*-*-*-*-*BE SURE TO INCLUDE THE DT_INTS FOR WHATEVER PIC YOU'RE WORKING WITH *-*-*-*-*

Code:
;******************************************************************************
;Constants.
;******************************************************************************
; SPI NRF24L01 commands
    ReadCmd         con    $00          ; Read command to read registers.
    WriteCmd        con    $20          ; Write command to write to registers.
    ReadRxPld       con    $61          ; Read Rx payload register address.
    WriteTxPld      con    $A0          ; Write Tx payload register address.
    FlushTx         con    $E1          ; Flush Tx register command.
    FlushRx         con    $E2          ; Flush Rx register command.
    ReuseTxPld      con    $E3          ; Reuse Tx payload register command.
    NoOperate       con    $FF          ; No operation, for reading STATUS register.
    
;******************************************************************************
; SPI NRF24L01 registers addresses
    ConfigAddr      con    $00          ; Config register, address $00.                  
    EnAAAddr        con    $01          ; Auto Acknowledgement Function register, address $01.
    EnRxAddr        con    $02          ; Enable the Receiver register, address $02.
    SetupAWAddr     con    $03          ; Setup Address Widths of Data Pipes, register address $03.
    SetupReTxAddr   con    $04          ; Setup Automatic Retransmission register, address $04.     
    SetupRfChAddr   con    $05          ; RF Channel selection regsiter, address, $05. Channels 0 to 127.
    RfSetupAddr     con    $06          ; RF setup register address.
    StatusAddr      con    $07          ; Status register address.
    ObsrvTxAddr     con    $08          ; Observe TX register address.
    CardetectAddr   con    $09          ; NRF_Carrier detect register address.
    RxPipe0Addr     con    $0A          ; NRF_RX Pipe 0 register address.
    RxPipe1Addr     con    $0B          ; NRF_RX pipe 1 register address.
    RxPipe2Addr     con    $0C          ; NRF_RX pipe 2 register address.
    RxPipe3Addr     con    $0D          ; NRF_RX pipe 3 register address.
    RxPipe4Addr     con    $0E          ; NRF_RX pipe 4 register address.
    RxPipe5Addr     con    $0F          ; NRF_RX pipe 5 register address.
    TxADDRAddr      con    $10          ; NRF_TX AdDRESS register address.
    RxPwPipe0Addr   con    $11          ; NRF_RX payload width pipe0 register address.
    RxPwPipe1Addr   con    $12          ; NRF_RX payload width pipe1 register address.
    RxPwPipe2Addr   con    $13          ; NRF_RX payload width pipe2 register address.
    RxPwPipe3Addr   con    $14          ; NRF_RX payload width pipe3 register address.
    RxPwPipe4Addr   con    $15          ; NRF_RX payload width pipe4 register address.
    RxPwPipe5Addr   con    $16          ; NRF_RX payload width pipe5 register address.
    FifoStatAddr    con    $17          ; NRF_FIFO status register register address.
    DynPldAddr      con    $1C          ; NRF_DYNPD Enable dynamic payload register address.
    FeatureAddr     con    $1D          ; NRF_Feature register address.
    
;******************************************************************************
; Port Pin names.
;*******************************************************************************
    HeartBeat   var     PORTX.Y
    NrfChipEn   var     PORTX.Y
    NrfChipSel  var     PORTX.Y
    NrfMosi     var     PORTX.Y
    NrfMiso     var     PORTX.Y
    NrfClock    var     PORTX.Y
    NrfIRQ      var     PORTX.Y


;*******************************************************************************
; Variables listing.
;*******************************************************************************
    SPICounter          var byte    ; General Puspose Counter.
    RxTxCounter         var byte    ; General Puspose Counter.

    NrfRxPckt           var byte[8]     
    NrfTxPckt           var byte[8] 

    WriteBytes          var byte
    Readbytes           var byte
    ReadSPIBuffer       var byte

;*******************************************************************************
;ASM INTERRUPTS COURTESY OF DARREL TAYLOR.    
ASM
INT_LIST  macro  ;  IntSource,       Label,             Type,   ResetFlag? 
    INT_Handler     TMR1_INT,       _Heart_Beat,        PBP,    YES        
    endm      
    INT_CREATE              ; Creates the interrupt processor    
ENDASM


;*******************************************************************************
    goto Start

;*******************************************************************************
Heart_Beat: 
    toggle HeartBeat 
     RxTxCounter = RxTxCounter + 1
@ INT_RETURN


;*******************************************************************************
;******************************************************************************* 
;*******************************************************************************
Start: 
    clear
    SSPEN = 1                                ; Enable SPI pins
    NrfChipEn = 1                            ; Enable the NRF Module.
    NrfChipSel = 1                           ; Disable the NRF Module.
    pause 500                                ; Wait 500ms for the rest of the system to power up.


InitNRF:  ; All Common registers are written to sequentially, $01 - $1D. The WRITE command includes
          ; the regsiter address being written to. A data byte must followed the Write 
          ; command. It will contain the information needing to be changed. 


    ; Writing to Address $01 
    NrfTxPckt[0] = WriteCmd + EnAAAddr       ; Read/Write to the EN_AA REGISTER Address $01.
    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $01. Disable all Auto Acknowledge Functions.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $02 
    NrfTxPckt[0] = WriteCmd + EnRxAddr       ; Read/Write to the EN_RXADDR REGISTER Address $02.
    NrfTxPckt[1] = 000001                 ; Data for EnAAAddr REGISTER Address $02. Enable Data Pipe o
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $03 
    NrfTxPckt[0] = WriteCmd + SetupAWAddr    ; Read/Write to the SETUP_AW REGISTER Address $03.
    NrfTxPckt[1] = 000001                 ; Data for EnAAAddr REGISTER Address $03. Address width is 3 bytes long.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $04 
    NrfTxPckt[0] = WriteCmd + SetupReTxAddr  ; Read/Write to the SETUP_RETR REGISTER Address $04.
    NrfTxPckt[1] = 010000                 ; Data for EnAAAddr REGISTER Address $04. AutoRetransmit set min and disabled.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $05 
    NrfTxPckt[0] = WriteCmd + SetupRfChAddr  ; Read/Write to the RF_CH REGISTER Address $05.
    NrfTxPckt[1] = 000010                 ; Data for EnAAAddr REGISTER Address $05. Rf Channel is set to default.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $06 
    NrfTxPckt[0] = WriteCmd + RfSetupAddr    ; Read/Write to the RF_SETUP REGISTER Address $06.
    NrfTxPckt[1] = 000110                 ; Data for EnAAAddr REGISTER Address $06. Data rate at 1Mbps, at 0dbm.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.

    ; Writing to Address $0A 
    NrfTxPckt[0] = WriteCmd + RxPipe0Addr    ; Read/Write to the RX_ADDR_P0 REGISTER Address $0A. 
    NrfTxPckt[1] = 000001                 ; Data for EnAAAddr REGISTER Address $0A. Receive Address length is the same as address width.
                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $0B 
'    NrfTxPckt[0] = WriteCmd + RxPipe1Addr    ; Read/Write to the RX_ADDR_P1 Address $0B.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $0B. Receive Address length is the same as address width.
'                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $0C 
'    NrfTxPckt[0] = WriteCmd + RxPipe2Addr    ; Read/Write to the RX_ADDR_P2 Address $0C.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $0C. Receive Address length is the same as address width.
'                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $0D 
'    NrfTxPckt[0] = WriteCmd + RxPipe3Addr    ; Read/Write to the RX_ADDR_P3 Address $0D.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $0D. Receive Address length is the same as address width.
'                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $0E 
'    NrfTxPckt[0] = WriteCmd + RxPipe4Addr    ; Read/Write to the RX_ADDR_P4 Address $0E.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $0E. Receive Address length is the same as address width.
'                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $0F 
'    NrfTxPckt[0] = WriteCmd + RxPipe5Addr    ; Read/Write to the RX_ADDR_P5 Address $0F.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $0F. Receive Address length is the same as address width.
'                                             ; Set to one, Current AW value will only use 1 byte instead of 3.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $10 
    NrfTxPckt[0] = WriteCmd + TxADDRAddr     ; Read/Write to the TX_ADDR REGISTER Address $10.
    NrfTxPckt[1] = 000001                 ; Data for EnAAAddr REGISTER Address $10. Set to same as Receive Address.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
    ; Writing to Address $11 
    NrfTxPckt[0] = WriteCmd + RxPwPipe0Addr  ; Read/Write to the RX_PW_P0 REGISTER Address $11.
    NrfTxPckt[1] = 000101                 ; Data for EnAAAddr REGISTER Address $11. Set the number of bytes expected to be received.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $12 
'    NrfTxPckt[0] = WriteCmd + RxPwPipe1Addr  ; Read/Write to the RX_PW_P1 REGISTER Address $12.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $12. Set the number of bytes expected to be received.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $13 
'    NrfTxPckt[0] = WriteCmd + RxPwPipe2Addr  ; Read/Write to the RX_PW_P2 REGISTER Address $13.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $13. Set the number of bytes expected to be received.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $14 
'    NrfTxPckt[0] = WriteCmd + RxPwPipe3Addr  ; Read/Write to the RX_PW_P3 REGISTER Address $14.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $14. Set the number of bytes expected to be received.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $15 
'    NrfTxPckt[0] = WriteCmd + RxPwPipe4Addr  ; Read/Write to the RX_PW_P4 REGISTER Address $15.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $15. Set the number of bytes expected to be received.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $16 
'    NrfTxPckt[0] = WriteCmd + RxPwPipe5Addr  ; Read/Write to the RX_PW_P5 REGISTER Address $16.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $16. Set the number of bytes expected to be received.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.


'    ; Writing to Address $1C 
'    NrfTxPckt[0] = WriteCmd + DynPldAddr     ; Read/Write to the DYNPD REGISTER Address $1C.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $1C. Indicate that packets will be of different lengths for each data pipe.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    
'    ; Writing to Address $1D 
'    NrfTxPckt[0] = WriteCmd + FeatureAddr    ; Read/Write to the FEATURE REGISTER Address $1D.
'    NrfTxPckt[1] = 000000                 ; Data for EnAAAddr REGISTER Address $1D. Enable/Disable extra features.
'    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
'    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
 
    gosub NrfRxMode
    NrfChipSel = 1                           ; Deselect the NRF Module.
    
    NrfChipEn = 1                            ; Enable the NRF Module.


@   INT_ENABLE  TMR1_INT                     ; Enable Timer 1 interrupts. 

;*******************************************************************************
Main:
rx:
    if RxTxCounter = 10 then : RxTxCounter = 0 : gosub tx
    if NrfIRQ = 0 then
        NrfChipEn = 0                        ; Disable the NRF Module.
        gosub  NrfWriteStatus
        gosub NrfSPIReadPayload
        gosub FlushRxRegs
        NrfChipEn = 1                        ; Enable the NRF Module.
        hserout2  [str NrfRxPckt\6,13,10]    ; Send straight to USB             
    endif
    goto main


;*******************************************************************************
tx:
    NrfChipEn = 0                            ; Disable the NRF Module.
    gosub NrfTxMode  


LoadTXPckt: 
    NrfTxPckt[0] = WriteTxPld                ; Write Tx payload register address. Address $A0.                           
    NrfTxPckt[1] = "T"                       ; Data for WriteTxPld REGISTER.
    NrfTxPckt[2] = "E"                       ; Data for WriteTxPld REGISTER.
    NrfTxPckt[3] = "S"                       ; Data for WriteTxPld REGISTER.
    NrfTxPckt[4] = "T"                       ; Data for WriteTxPld REGISTER.
    NrfTxPckt[5] = "1"                       ; Data for WriteTxPld REGISTER.
    WriteBytes = 5                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?        


    gosub NrfSPIWrite                        ; Send data to the Nrf Module.   


    NrfChipEn = 1                            ; Enable the NRF Module.
    pauseus 25                               ; Wait for for the module to complete transmission.
    NrfChipEn = 0                            ; Disable the NRF Module.
    pauseus 200                              ; Wait for IRQ to go low
    gosub  NrfWriteStatus                    ; Clear all flags in the module Status Register.


    gosub NrfRxMode
    NrfChipSel = 1                           ; Diselect the NRF Module.
    NrfChipEn = 1                            ; Enable the NRF Module.
    return      


;*******************************************************************************
NrfWriteStatus:
    ; Writing to Address $07 
    NrfTxPckt[0] = WriteCmd + StatusAddr     ; Read/Write to the STATUS REGISTER Address $07.
    NrfTxPckt[1] = 110000                 ; Data for EnAAAddr REGISTER Address $07.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite 
    return
;*******************************************************************************
NrfTxMode:
    ; Writing to Address $00  
    NrfTxPckt[0] = WriteCmd + ConfigAddr     ; Read/Write to the CONFIG REGISTER Address $00.
    NrfTxPckt[1] = 000010                 ; Data for CONFIG REGISTER Address $00.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    ; Writing to Address $07 
    NrfTxPckt[0] = WriteCmd + StatusAddr     ; Read/Write to the STATUS REGISTER Address $07.
    NrfTxPckt[1] = 010000                 ; Data for EnAAAddr REGISTER Address $07. Mask RXDataReceived and MAXRetransmit.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.      
    ; Writing to Address $1D 
    NrfTxPckt[0] = FlushTx                   ; Write command to flush the TX Payload register.
    WriteBytes = 0                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?   
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    return
    
;******************************************************************************* 
NrfRxMode:
    ; Writing to Address $00  
    NrfTxPckt[0] = WriteCmd + ConfigAddr     ; Read/Write to the CONFIG REGISTER Address $00.
    NrfTxPckt[1] = 110011                 ; Data for CONFIG REGISTER Address $00.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.  
    ; Writing to Address $07 
    NrfTxPckt[0] = WriteCmd + StatusAddr     ; Read/Write to the STATUS REGISTER Address $07.
    NrfTxPckt[1] = 110000                 ; Data for EnAAAddr REGISTER Address $07. Mask TXDataSent and MAXRetransmit.
    WriteBytes = 1                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    return
    
;******************************************************************************* 
FlushRxRegs:
    ; Writing to Address $1D 
    NrfTxPckt[0] = Flushrx                   ; Write command to flush the TX Payload register.                         
    WriteBytes = 0                           ; Number of byte to write to SPI module. 0 = 1 byte, counts from 0 to ?
    gosub NrfSPIWrite                        ; Send data to the Nrf Module.
    return   


;*******************************************************************************
SSP2IF_Check:
    while SSP2IF = 0                         ; Wait for the MSSP module to finish transmitting.
    wend
    SSP2IF = 0
    return
    
;*******************************************************************************
NrfStatus: 
    while NrfMiso = 1                        ; Wait until module is ready to accept new data.
    wend                                     
    return


;*******************************************************************************  
NrfSPIWrite:
    NrfChipSel = 0                           ; Select the NRF Module.
    for SPICounter = 0 to WriteBytes
        SSP2BUF = NrfTxPckt[SPICounter]      ; Write to the SPI buffer.
        gosub SSP2IF_Check                   ; Wait for the MSSP module to finish transmitting.
    Next SPICounter
    NrfChipSel = 1                           ; Deselect the NRF Module.
    return
    
;*******************************************************************************   
NrfSPIReadPayload: 
    NrfChipSel = 0                           ; Select the NRF Module.    
    SSP2BUF = ReadRxPld                      ; Write to the SPI buffer.
    gosub SSP2IF_Check                       ; Wait for the MSSP module to finish transmitting.
    for SPICounter = 0 to 4                  ; Initialize counter to number of bytes being received.
        SSP2BUF = $FF                        ; Write No_Op byte to the SPI buffer.
        gosub SSP2IF_Check                   ; Wait for the MSSP module to finish transmitting.
        NrfRxPckt[SPICounter] = SSP2BUF      ; Copy the received byte from the SPI buffer.
    Next SPICounter 
    NrfChipSel = 1                           ; Deselect the NRF Module.
    return
    
;*******************************************************************************  
    end