This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F2013: NSP430F2013 + nRF24L01 SPI communication issue

Part Number: MSP430F2013

Hi,

I have interfaced a MSP430F2013 with an nRF24L01 module. The SPI settings are all correct and working properly, as i am able to write and read the registers of the RF module.

However, when the data from the other Rf module arrives, I am unable to read it. It is reporting all zeroes. i am not surer whether the problem lies on the MSP side or the RF module side.

The hardware  I am using is - ezMSP430 development kit. Has anyone faced a similar issue?

  • If you can, post the code you're using for reading the FIFO. Typically this is separate from the register-reading code, and sometimes all it takes is a typo.

    I vaguely recall that the first Rx byte of every transaction (usually ignored) is a status byte. Is that zero also?

  • Hi Bruce,

    Thanks for the response!  The status byte is being read correctly, it is only the data that is being read as zeroes. Here is the code for your reference, please excuse the silly things like using loops to get delays, i did a quick and dirty implementation,  for a prototype :), once the rf part is cleared, will clean it up.

    /*
     * File Name: main.c
     *
     */



    #include <msp430.h>
    #include "types.h"


    //nRF24L01 register addresses - total 23(decimal)/0x17(Hex)
    //---------------------------------------------------------
    #define REG_CONFIG          0x00    //CONFIG : MASK INTERRUPTS - RX DR, TX DS and MAX RT OVER, enable CRC, CRC TYPE, PWR UP, PRIM_RX/TX
    #define REG_EN_AA           0X01    //ENABLE AUTO ACK
    #define REG_EN_RXADDR       0X02    //ENABLE RX PIPES
    #define REG_SETUP_AW        0X03    //SETUP ADDRESS WIDTH - 1 TO 5 BYTES
    #define REG_SETUP_RETR      0X04    //SETUP NO. OF RETRIES
    #define REG_RF_CH           0X05    //SETUP RF CHANNEL
    #define REG_RF_SETUP        0X06    //RF PARAMETERS SETUP : PLL LOCK, DATARATE, POWER LVL
    #define REG_STATUS          0X07    //STATUS - RX DATA READY, TX DATA SENT, MAX_RT, RX_P_NO (RX PIPE WHERE DATA HAS ARRIVED), TX_FULL
    #define REG_OBSERVE_TX      0X08    //OBSERVE TX PARAMETERS PACKET LOSS COUNT, RESENT PKT COUNT
    #define REG_CD              0X09    //CARRIER DETECT
    #define REG_RX_ADDR_P0      0X0A    //DEFAULT 0XE7E7E7E7
    #define REG_RX_ADDR_P1      0X0B    //DEFAULT 0XC2C2C2C2
    #define REG_RX_ADDR_P2      0X0C    //DEFAULT LSB - OXC3, REST REMAIN SAME AS P1
    #define REG_RX_ADDR_P3      0X0D    //DEFAULT LSB - 0XC4
    #define REG_RX_ADDR_P4      0X0E    //DEFAULT LSB - 0XC5
    #define REG_RX_ADDR_P5      0X0F    //DEFAULT LSB - 0XC6
    #define REG_TX_ADDR         0X10        //DEFAULT 0XE7E7E7E7
    #define REG_RX_PW_P0        0X11    //PAYLOAD WIDTH PIPE 0 - DEFAULT 0 MAX 32
    #define REG_RX_PW_P1        0X12    //SAME AS ABOVE PIPE1
    #define REG_RX_PW_P2        0X13    //SAME AS ABOVE PIPE2
    #define REG_RX_PW_P3        0X14    //SAME AS ABOVE PIPE3
    #define REG_RX_PW_P4        0X15    //SAME AS ABOVE PIPE4
    #define REG_RX_PW_P5        0X16    //SAME AS ABOVE PIPE5
    #define FIFO_STATUS         0X17    // STATUS - TX_REUSE, TX_FULL, TX_EMPTY, RX_FULL, RX_EMPTY

    //Register Read and Write command masks
    //While using the register read and write commands, & these with the 5-bit
    //register address to obtain the respective register read/write command
    //-----------------------------------------------------------------------
    #define R_REG_CMD_MASK      0x00
    #define W_REG_CMD_MASK      0x20

    //NRF24 Configuration masks
    //-------------------------
    #define NRF_CFG_PWRUP    0x02
    #define NRF_CFG_PWRDN    0xFD

    #define NRF_CFG_PRIM_RX  0xFE
    #define NRF_CFG_PRIM_TX  0x01

    #define NRF_CFG_ENABLE_CRC      0x08
    #define NRF_CFG_DISABLE_CRC     0xF7

    #define NRF_CFG_CRC_SCHEME_1B   0x04
    #define NRF_CFG_CRC_SCHEME_2B   0xFB

    //Address Width for the RF transceiver
    //------------------------------------
    #define NRF_AW_3B               0x01
    #define NRF_AW_4B               0x10
    #define NRF_AW_5B               0x11

    //nRF24L01 Chip select and enable masks
    //-------------------------------------
    #define NRF_CS_MASK             0xEF        //Active low bit P1.4
    #define NRF_CE_MASK             0x08        //Active high bit P1.3

    //Enable Auto acknowledge masks
    //-----------------------------


    //Read Write operation instructions
    //-----------------------------------
    #define R_RX_PAYLOAD    0x61
    #define W_TX_PAYLOAD    0xA0
    #define FLUSH_TX        0xE1
    #define FLUSH_RX        0xE2
    #define REUSE_TX_PL     0xE3
    #define NOP             0xFF

    //Data packet position indices
    //----------------------------
    #define ADDR_POS         0x01
    #define CONTEXT_POS      0x02
    #define RMT_CMD_POS      0x03
    #define DATA_HI_POS      0x04
    #define DATA_LO_POS      0x05

    #define CMD_OFF          0x00
    #define CMD_ON           0x01

    #define SELF_ADDR         0x01          //This is the logical address which is different for each slave
    #define BROADCAST_ADDR    0xFF          //This is the broadcast address, which indicates that all slaves should act on the data.
    #define ADDR_WIDTH        0x05          //Using 5-byte addresses

    //remote commands from the master lamp
    //------------------------------------
    #define RMT_CMD_BTN_STATE_CHANGE  0x01
    #define RMT_CMD_SET_BRIGHTNESS    0x02

    /*Global definitions*/
    //--------------------
    unsigned char g_rxBuf[8];                  //global buffer to receive everything from the nRf module, sized to suit the max payload length
    unsigned char g_txBuf[8];                  //global buffer to send everything to the nRf module, sized to suit the max payload length
    unsigned char g_payloadBuf[8];             //global buffer to hold the data receivedfrom the master, sized to suit the max payload length


    //Since we are going to use logical addressing, all subordinate nodes will have the same physical addresses, but the payload will
    //contain the logical address to distinguish the individual nodes
    const unsigned char g_txAddress[6] = {"10000"};
    const unsigned char g_rxAddress[6] = {"20000"};


    UINT8   g_datalength = 1;                   //length includes the command byte. So minimum is 1
    UINT8   g_transfer_complete = 0;            //indicates SPI transfer between the MSP and the radio is complete
    UINT8   g_data_ready = 0;
    UINT8   g_buf_index = 0;
    UINT8   g_init_done = 0;
    const UINT8 g_payloadWidth = 7;             //Max payload size is 32bytes, but our data is only 7 bytes .

    //Function Prototypes
    //---------------------
    void Init_Port1(void);
    void Init_SPI(void);
    void Init_PWM(void);
    void Init_Radio(void);
    void Process_Master_Command(void);
    void Radio_CE_Enable(void);
    void Radio_CE_Disable(void);                         //Stop SPI comms
    void Radio_CS_Enable(void);
    void Radio_CS_Disable(void);                         //Stop SPI comms
    void Radio_WriteRegister(UINT8 regName, UINT8 length);
    void Radio_ReadRegister(UINT8 regName, UINT8 len);


    /**
     * main.c
     */
    int main(void)
    {
      volatile unsigned int i;

      WDTCTL = WDTPW + WDTHOLD;             // Stop watchdog timer

      //Initialize the internal peripherals first
      Init_Port1();
      Init_SPI();
      Init_PWM();

    /* Enable the general interrupt here because otherwise we will not be able to use
     * the SPI interrupt in Radio_Init !!
     */

      _BIS_SR(GIE);

      Init_Radio();


      while(1)
      {
          unsigned int i;
          //if(g_data_ready){                       //interrupt -data has arrived, commented to test with polling mode since IRQ is not going low :(
          for(i=0;i<0x1ff;i++);

              Process_Master_Command();
            //  g_data_ready = 0;
              P1OUT &= ~BIT0;
          //}
      }

    }

    //Interrupt Service Routines
    //--------------------------

    // USI interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USI_VECTOR
    __interrupt void universal_serial_interface(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USI_VECTOR))) universal_serial_interface (void)
    #else
    #error Compiler not supported!
    #endif
    {
        //read out the received data into a buffer,
        //before it gets ovverwritten by the next
        g_rxBuf[g_buf_index] = USISRL ;

        //check if first time after power up or if transaction in progress
        g_datalength--;
        if(g_datalength == 0){
            g_buf_index = 0;
            g_transfer_complete = 1;               //transfer complete
            if(USICTL1 & 0x01)
                USICTL1 &= ~USIIFG;
        }
        else {  //more data expected to be sent
            g_buf_index++;
            USISRL = g_txBuf[g_buf_index];
            USICNT = 8;                       //re-load counter
        }

    }

    //Port1 GPIO pin interrupt service routine
    #pragma vector = PORT1_VECTOR
    __interrupt void InterruptVectorPort1()
    {
            P1IFG &= ~BIT1;                     // Clear Interrupt Flag
            P1OUT |= BIT0;
            g_data_ready = 1;                     //Something has come in from the master
    }

    //End of interrupt service routines


    //All functional code goes here

    /********************************************************************************************
     * Function : Init_Port1
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     **********************************************************************************************/
    void Init_Port1(void)
    {

        //Bit configuration
        // P1.7 :: P1.6 :: P1.5 :: P1.4 :: P1.3 :: P1.2 :: P1.1 :: P1.0
        // OUT  :: IN   :: OUT  :: OUT  :: OUT  :: OUT  :: IN   :: OUT
        // MOSI :: MISO :: SCK  :: CSN  :: CE   :: PWM  :: IRQ  :: LED

        P1DIR |= BIT4 + BIT3 + BIT2 + BIT0;         // P1.4, P1.3, P1.2 and P1.0 output
        P1REN |= BIT4;// + BIT1;                       // Enable pullup for P1.4 (CSN) and P1.1(IRQ)
        P1IE |= BIT1;                               //Enable port1.1 interrupt
        P1IES |= BIT1;                              //High to Low or Falling Edge, since nRF24's IRQ signal is active low
        //Initial state setting
        P1OUT |= BIT4;                              // Drive high P1.4 - CSN active low
        P1OUT &= ~BIT3;                             // Drive P1.3 low, CE is active high
        P1OUT &= ~BIT2;                             // Drive P1.2 low, MOSFET turned off by default
        P1OUT &= ~BIT0;                             // Drive P1.0 low, LED is active high
    }

    /********************************************************************************************
     * Function : Radio_CE_Enable
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     **********************************************************************************************/
    void Init_SPI(void)
    {
      USICTL0 |= USIPE7 +  USIPE6 + USIPE5 + USIMST + USIOE; // Port, SPI master
      USICTL1 |=  USIIE + USICKPH;              //Counter interrupt, flag remains set,clock phase required is 1 for nRF24

      USICKCTL = USIDIV_4 + USISSEL_2;          // /16 SMCLK
      USICTL0 &= ~USISWRST;                     // USI released for operation

    }

    /********************************************************************************************
     * Function : Init_PWM
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     **********************************************************************************************/
    void Init_PWM(void)
    {
    #if 0
          P1DIR |= 0x04;                            // P1.2
          P1SEL |= 0x04;                            // P1.2 configured as alternate function 1
          CCR0 = 512-1;                             // PWM Period
          CCTL1 = OUTMOD_7;                         // CCR1 reset/set
          CCR1 = 384;                               // CCR1 PWM duty cycle
          TACTL = TASSEL_2 + MC_1;                  // SMCLK, up mode
    #endif
    }

    /********************************************************************************************
     * Function : Config_Radio
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     **********************************************************************************************/
    void Init_Radio(void)
    {
       unsigned int i;
       Radio_CS_Disable();


        Radio_ReadRegister((UINT8)REG_CONFIG, 2);
        g_txBuf[1] = 0x01;
        Radio_WriteRegister((UINT8)REG_CONFIG, 2);        //PWR_UP = false, mode = PRIM_RX
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_CONFIG, 2);

        g_txBuf[1] = 0x03;
        Radio_WriteRegister((UINT8)REG_SETUP_AW, 2);      //address width - 5 bytes
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_SETUP_AW, 2);

        for(  i = 0; i<ADDR_WIDTH ;i++ ){
            g_txBuf[i+1] = g_txAddress[i];
        }
        Radio_WriteRegister((UINT8)REG_TX_ADDR, 6);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_TX_ADDR, 6);

        Radio_WriteRegister((UINT8)REG_RX_ADDR_P0, 6);     //This may seem a bit confusing, but the Tx address and Rx pipe 0 address are he same
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RX_ADDR_P0, 6);


        g_txBuf[1] = 0x07;                          //Set the payload size to 7 bytes
        Radio_WriteRegister((UINT8)REG_RX_PW_P0, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RX_PW_P0, 2);

        for(  i = 0; i< ADDR_WIDTH ;i++ ){
                g_txBuf[i+1] = g_rxAddress[i];
            }
        Radio_WriteRegister((UINT8)REG_RX_ADDR_P1, 6); //And Rx pipe 1 address is the
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RX_ADDR_P1, 6);

        g_txBuf[1] = 0x07;
        Radio_WriteRegister((UINT8)REG_RX_PW_P1, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RX_PW_P1, 2);

        g_txBuf[1] = 0x00;
        Radio_WriteRegister((UINT8)REG_EN_AA, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_EN_AA, 2);

        g_txBuf[1] = 0x03;
        Radio_WriteRegister((UINT8)REG_EN_RXADDR, 2);
        for(i = 0xff; i ; i--);

        g_txBuf[1] = 0x5F;
        Radio_WriteRegister((UINT8)REG_SETUP_RETR, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_SETUP_RETR, 2);

        g_txBuf[1] = 0x4C;                           //select RF channel 76
        Radio_WriteRegister((UINT8)REG_RF_CH, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RF_CH, 2);

        g_txBuf[1] = 0x07;                           //select RF data rate 1 Mbps and power 0dBm
        Radio_WriteRegister((UINT8)REG_RF_SETUP, 2);
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_RF_SETUP, 2);

        Radio_WriteRegister((UINT8)FLUSH_TX, 1);
        for(i = 0xff; i ; i--);
        Radio_WriteRegister((UINT8)FLUSH_RX, 1);

        g_txBuf[1] = 0x43;                          //Configure only RX_DR interrupt
        Radio_WriteRegister((UINT8)REG_CONFIG, 2);  //PWR_UP = true, mode = PRIM_RX
        for(i = 0xff; i ; i--);
        Radio_ReadRegister((UINT8)REG_CONFIG, 2);

        Radio_CE_Enable();                          //starts the reception mode - PRIM_RX
    }

    /********************************************************************************************
     * Function : Radio_ReadPayload
     * This function reads the messages sent from the master - command for turning the LED on/off
     * or adjust the brightness
     * Parameters :
     **********************************************************************************************/
    unsigned int Radio_ReadPayload(void)
    {
        unsigned char status;
        unsigned int i;

        Radio_ReadRegister(REG_STATUS, 2);
        status = g_rxBuf[0];

        if(status & 0x40)                                //if data received : RX_DR is set,
        {
            if(status & 0x02){                           //pipe number = 1.
                //Now read the payload
                g_txBuf[0] = R_RX_PAYLOAD;
                g_datalength = 8;
                USISRL = g_txBuf[0];
                USICNT = 8;
                while(!g_transfer_complete){;}
                for(i = 0; i < g_payloadWidth; i++){
                    g_payloadBuf[i] = g_rxBuf[i+2];             //transfer contents to the payload register, because the Tx buffer is overwritten soon.
                }

                g_txBuf[1] = status | 0x70;                 //reset the interrupt flags
                Radio_WriteRegister(REG_STATUS,2);
                for(i = 0; i< 0xff; i++);
                return g_payloadWidth;
            }
            return 0;
        }
        else
            return 0;
    }

    #if 0
    /********************************************************************************************
     * Function : Radio_SendPayload
     * This function sends messages to the master, like the current status of the lamp,
     * or brightness level. For future.
     **********************************************************************************************/
    unsigned int Radio_SendPayload(NRF_WriteTypeDef writeType, unsigned char* txBuf, unsigned int numBytes)
    {
        //For now we have nothing to transmit to the master, hence not implemented
        return 0;
    }

    #endif

    /********************************************************************************************
     * Function : Radio_CS_Enable
     * This function asserts the Chip Select signal to enable SPI read / write to the nRF24 chip
     * Parameters : None
     * Returns: Nothing
     **********************************************************************************************/
    void Radio_CS_Enable()
    {
        // drive CSN pin low;
        P1OUT &= ~BIT4;
    }

    /********************************************************************************************
     * Function : Radio_CS_Disable
     * This function de-asserts the Chip Select signal to disable SPI read / write to the nRF24 chip
     * Parameters : None
     * Returns: Nothing
     **********************************************************************************************/
    void Radio_CS_Disable()
    {
        // drive CSN pin high;
            P1OUT |= BIT4;
    }

    /********************************************************************************************
     * Function : Radio_CE_Enable
     * This function asserts the Chip Enable signal to control the transmission or reception
     * In PRIM_TX mode, we keep the CE line low until we wish to send something. When we need
     * to send we dump the payload into the TX_PLD register and assert this pin for  a short
     * duration.
     * In PRIM_RX mode, this pin must always be held high in order to listen for transmissions.
     * Parameters : None
     * Returns: Nothing
     **********************************************************************************************/
    void Radio_CE_Enable()
    {
        // drive CSN pin low;
        P1OUT |= BIT3;
    }

    /********************************************************************************************
     * Function : Radio_CE_Disable
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     **********************************************************************************************/
    void Radio_CE_Disable()
    {
        // drive CSN pin high;
            P1OUT &= ~BIT3;
    }

    /********************************************************************************************
     * Function : Radio_ReadRegister
     * This function reads a given number of bytes from the specified register
     * within the nRF24L01 over the SPI bus. Here, we just start the operation
     * and the main transfer happens inside the USI interrupt service routine
     * We always use the global g_txBuff buffer for the transfers
     **********************************************************************************************/
    void Radio_ReadRegister(UINT8 regName, UINT8 len)
    {
        unsigned int i;
        Radio_CS_Enable();                                  //Start SPI comms
        for(i = 0; i < 128; i++);
        g_transfer_complete = g_buf_index = 0;              //set the flag to false
        g_txBuf[0] = regName | (UINT8)R_REG_CMD_MASK;       //Set the register read mask with the register address
        g_datalength = len;                                 //How many bytes are to be transferred
        USISRL = g_txBuf[0];                                //Load the first byte into the USI data register
        USICNT = 8;                                         //load the bit count to start the transfer
        while(!g_transfer_complete)
        {   ;  }                                            //wait for the flag to be set
        g_transfer_complete = 0;
        Radio_CS_Disable();
        for(i = 0; i < 128; i++);                           //Stop SPI comms

    }


    /********************************************************************************************
     * Function : Radio_WriteRegister
     * This function writes a given number of bytes to the specified register
     * within the nRF24L01 over the SPI bus
     * We always use the global g_txBuff buffer for the transfers
     **********************************************************************************************/
    void Radio_WriteRegister(UINT8 regName, UINT8 len)
    {
        unsigned int i;
        Radio_CS_Enable();                         //Start SPI comms
        for(i = 0x7f; i ; i--);
        g_transfer_complete = g_buf_index = 0;
        g_datalength = len;//set the flag to false
        g_txBuf[0] = regName | (UINT8)W_REG_CMD_MASK;
        USISRL = g_txBuf[0];
        USICNT = 8;                  //load the bit count to start the transfer
        while(!g_transfer_complete)
        {   ;  }                                     //wait for the flag to be set
        g_transfer_complete = 0;
        Radio_CS_Disable();                         //Start SPI comms
        for(i = 0x7f; i ; i--);

    }

    /********************************************************************************************
     * Function : Process_Master_Command
     * This function processes the commands received from the master.
     * The commands can be of type on/off and dimming level setting
     **********************************************************************************************/
    void Process_Master_Command(void)
    {
        UINT8 addr = g_rxBuf[ADDR_POS];

        if(!Radio_ReadPayload()){
            if((addr == BROADCAST_ADDR) || (addr == SELF_ADDR))   //if it is a broadcaast message
                {
                    switch(g_payloadBuf[RMT_CMD_POS]){

                    case 1:
                        if (g_rxBuf[DATA_LO_POS] == CMD_ON)
                            P1OUT |= BIT2;                  //turn on the lamp
                        else
                            P1OUT &= ~BIT2;                 //turn off the lamp
                        break;
                    case 2:

                        break;

                    }
                }
        }


    }
    /********************************************************************************************
     * Function : LED_BrightnessControl
     * This function varies the PWM duty cycle in accordance with the setting received from the master.
     * The brighness is translated into the PWM
     **********************************************************************************************/
    void LED_BrightnessControl(int brightness)
    {
        //To Do
    }

    Regards

    Sreenivasa Chary

  • Hi sreenivasa,

    In order to check whether the problem lies on the MSP430 side or the NRE24L01 side, you can refer to the code examples, where msp430x20x3_usi_02, msp430x20x3_usi_03, msp430x20x3_usi_04 and msp430x20x3_usi_05 are all code examples related the SPI interface.

    http://dev.ti.com/tirex/explore/node?node=AP2kp.Wkb-aPCeyAi0c3ag__IOGqZri__LATEST

    Best Regards

    Johnson

  • It looks like you forgot to assert/de-assert CS around the R_RX_PAYLOAD request. That could cause the symptom you described.

  • Hi Bruce,

    Thanks for the review comment !! It solved the problem. I missed out asserting the CSN line.  In the register read and write  functions i had added the CS enable and disable calls, hence the status register read was working fine.

    Regards

    Sreenivasa Chary

**Attention** This is a public forum