Problem with Software SPI

Discussion in 'Embedded Systems and Microcontrollers' started by Mrunal Ahirrao, Jan 25, 2016.

  1. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    Hello,
    I working on interfacing NRF24L01+ with PIC16f684(without hardware SPI). As this PIC don' got hardware SPI so I need to use Software SPI.I am using MikroC pro for PIC and it got software SPI library too. But its not working and PIC is not able to communicate with NRF. I think this is a RMW issue as software SPI is using I/O ports and I/O ports got RMW problem, as it worked once and now its not working. And MikroC don't share the source of their library so I cannot modify it to work using shadow registers. Please review my code:
    Code (Text):
    1.  
    2.  
    3. #define TX_mode                  // define TX_mode or RX_mode
    4. //#define chn 83
    5. #include "nRF24L01.h"
    6.  
    7.  
    8. const char IGM[]="I";
    9. //const char IGN[]="N";
    10. const char SDD[] ="S";
    11. const char RTH[]="R";
    12.  
    13.  
    14. // Pipe Addresses
    15. // adr0 should be unique
    16.  
    17. // Bytes are arranged lowest to highest
    18. const char adr0[] = {0x78,0x78,0x78,0x78,0x78};    // LSB first
    19.  
    20.  
    21. char Data_Out[21], stat;
    22. short dataLength = 20;
    23. char j;
    24. bit DS;
    25.  
    26. char txt[11];
    27.  
    28.  
    29. // Software SPI module connections
    30. sbit SoftSpi_SDI at SDI;
    31. sbit SoftSpi_SDO at SDO;
    32. sbit SoftSpi_CLK at RC3_bit;
    33.  
    34. sbit SoftSpi_SDI_Direction at TRISC4_bit;
    35. sbit SoftSpi_SDO_Direction at TRISC2_bit;
    36. sbit SoftSpi_CLK_Direction at TRISC3_bit;
    37. // End Software SPI module connections
    38.  
    39. // You can change these pins as long as your microcontroller supports SPI on the required pins
    40. sbit Irq_pin   at PORTC.B0; sfr;
    41. sbit Mosi_pin  at PORTC.B2; sfr;
    42. sbit Ce_pin    at PORTC.B1; sfr;
    43. sbit Sclk_pin  at PORTC.B3; sfr;
    44. sbit Csn_pin   at PORTC.B5; sfr;
    45. //sbit Miso_pin  at PORTC.B4; sfr;
    46.  
    47. sbit Irq_tris  at TRISC.B0; sfr;
    48. sbit Mosi_tris at TRISC.B5; sfr;
    49. sbit Ce_tris   at TRISC.B1; sfr;
    50. sbit Sclk_tris at TRISC.B3; sfr;
    51. sbit Csn_tris  at TRISC.B2; sfr;
    52. sbit Miso_tris at TRISC.B4; sfr;
    53.  
    54.  
    55. //declarations of IO devices
    56.  
    57. sbit NRF  at RC0_bit;//NRF power bit
    58. sbit ACK  at RA2_bit;// ACK LED to indicate ACK receieved from PRX
    59. bit IGNM ;
    60. bit SD   ;
    61. bit HT  ;
    62.  
    63.  
    64. bit DTS;
    65. // I use a procedure for any delay that is used more than once
    66. void Comm_Delay() {Delay_us(10);}       // You can experiment with this delay
    67. void Delay_10()   {Delay_ms(10);}
    68. void Delay_100()  {Delay_ms(100);}
    69. void Delay_500()  {Delay_ms(500);}
    70.  
    71. void Clear_Data(char dat[])
    72. {
    73.      char i;
    74.      for(i=0;i<dataLength;i++)dat[i] = ' ';
    75. }
    76.  
    77. void toggleCSN()
    78. {
    79.      Csn_pin = 1;
    80.      Delay_us(20);                      // You can experiment with this delay
    81.      Csn_pin = 0;
    82.      Comm_Delay();
    83. }
    84.  
    85. char Get_Status()
    86. {
    87.      char s;
    88.      Ce_pin = 0;
    89.      toggleCSN();
    90.      Soft_SPI_Write(STATUS);
    91.      Comm_Delay();
    92.      s = Soft_SPI_Read(NOP);
    93.      Comm_Delay();
    94.      Csn_pin = 1;
    95.      return s;
    96. }
    97.  
    98. char *getConst(char dest[], const char source[])
    99. {
    100.     int i = 0;
    101.     while (source[i])                  // while (source[i] != '\0')
    102.     {dest[i] = source[i]; i++;}
    103.     dest[i] = '\0';
    104.     return dest;
    105. }
    106.  
    107. char Get_FIFO_Flags()
    108. {
    109.      char s;
    110.      Ce_pin = 0;
    111.      toggleCSN();
    112.      Soft_SPI_Write(FIFO_STATUS);
    113.      Comm_Delay();
    114.      s = Soft_SPI_Read(NOP);
    115.      Comm_Delay();
    116.      Csn_pin = 1;
    117.      return s;
    118. }
    119.  
    120.  
    121.  
    122. void sendBuffer()
    123. {
    124.      char i;
    125.      do{
    126.          toggleCSN();
    127.         Soft_SPI_Write(STATUS | W_REGISTER);
    128.          Comm_Delay();
    129.          Soft_SPI_Write(0xff);            //clear flags
    130.          Comm_Delay();
    131.          toggleCSN();
    132.          Soft_SPI_Write(FLUSH_TX);
    133.          Comm_Delay();
    134.          toggleCSN();
    135.          Soft_SPI_Write(W_TX_PAYLOAD);
    136.          for(i = 0; i < dataLength; i++) {Comm_Delay(); Soft_SPI_Write(Data_Out[i]);}
    137.          Comm_Delay();
    138.          Csn_pin = 1;
    139.          Ce_pin = 1;
    140.          Delay_10();
    141.          j = (Get_Status() & 0x20);                     // keep trying until ack is received
    142.      }while(j==0);
    143.      ACK=0;//ACK receieved from PRX and hence ACK LED is lit
    144. }
    145.  
    146.  
    147.  
    148.  
    149.  
    150. char init_Radio()
    151. {
    152.        char i;
    153.        Ce_pin = 0;                                      // must be in standby or power down to write
    154.        Comm_Delay();
    155.        toggleCSN();
    156.        Soft_SPI_Write(CONFIG | W_REGISTER);
    157.        Comm_Delay();
    158.      
    159.      
    160.        #ifdef TX_mode
    161.        Soft_SPI_Write(PWR_UP+ CRCO + EN_CRC);             // Transmitter
    162.        Comm_Delay();
    163.  
    164.  
    165.        toggleCSN();
    166.        Soft_SPI_Write(EN_AA | W_REGISTER);
    167.        Comm_Delay();
    168.        Soft_SPI_Write(ENAA_P0);
    169.        Comm_Delay();
    170.  
    171.        toggleCSN();
    172.        Soft_SPI_Write(EN_RXADDR | W_REGISTER);
    173.        Comm_Delay();
    174.        Soft_SPI_Write(ERX_P0);
    175.        Comm_Delay();
    176.      
    177.        toggleCSN();
    178.        Soft_SPI_Write(RX_ADDR_P0 | W_REGISTER);
    179.        for(i=0;i<5;i++){Comm_Delay(); Soft_SPI_Write(adr0[i]);} // This is the pipe in use by this transmitter
    180.        Comm_Delay();
    181.                                                             // The addresses above and below must be the same
    182.        toggleCSN();                                         // Choose any of the 6 addresses used by the receiver
    183.        Soft_SPI_Write(TX_ADDR | W_REGISTER);
    184.        for(i=0;i<5;i++){Comm_Delay(); Soft_SPI_Write(adr0[i]);} // This is the pipe in use by this transmitter
    185.        Comm_Delay();
    186.        #endif
    187.  
    188.        toggleCSN();
    189.        Soft_SPI_Write(SETUP_AW | W_REGISTER);
    190.        Comm_Delay();
    191.        Soft_SPI_Write(AW5);
    192.        Comm_Delay();
    193.  
    194.        toggleCSN();
    195.        Soft_SPI_Write(SETUP_RETR | W_REGISTER);
    196.        Comm_Delay();
    197.        Soft_SPI_Write(0xfaf);
    198.        Comm_Delay();
    199.  
    200.        toggleCSN();
    201.        Soft_SPI_Write(RF_CH | W_REGISTER);
    202.        Comm_Delay();
    203.        Soft_SPI_Write(83);                                      // Set your channel here. Obviously it must be the same for TX and RX.
    204.        Comm_Delay();
    205.  
    206.        toggleCSN();
    207.        Soft_SPI_Write(RF_SETUP | W_REGISTER);
    208.        Comm_Delay();
    209.        Soft_SPI_Write(RF_PWR +RF_DR_LOW );
    210.        Comm_Delay();
    211.  
    212.  
    213.        toggleCSN();
    214.        Soft_SPI_Write(RX_PW_P0 | W_REGISTER);
    215.        Comm_Delay();
    216.        Soft_SPI_Write(dataLength);
    217.        Comm_Delay();
    218.  
    219.        Csn_pin = 1;
    220.        i = Get_Status();
    221.        return i;
    222. }
    223.  
    224.  
    225.  
    226.  
    227. // =============================================================================
    228. // =============================================================================
    229. void main(){
    230.         ADCON0=0x00;
    231.         ADCON1 = 0x00;                           // digital not analog
    232.         ANSEL=0;
    233.         CMCON0=0x07;
    234.         //TRISC.B5=0;//MOSI
    235.         //TRISC.B3=0;//SCLK
    236.         //TRISC.B4=1;//MISO
    237.         TRISC.B1=0;//CE
    238.         TRISC.B2=0;//CSN
    239.         TRISC.B0=0;//NRF power bit
    240.         TRISA =0b00001011;
    241.        Csn_tris = 0;
    242.        Csn_pin = 1;
    243.        Ce_tris = 0;
    244.        Ce_pin = 0;                              // 1 = listen or transmit
    245.       DTS=1;
    246.        Clear_Data(Data_Out);
    247.          NRF=1; //NRF is off
    248.           DS=0;
    249.           ACK=0;
    250.           IGNM=0;
    251.           SD=0;
    252.           HT=0;
    253.           delay_ms(500);
    254.           ACK=1;// firstly ACK will be OFF
    255.        do {
    256.       if(PORTA.RA3==1)  //Ignore mode button
    257.        {
    258.       IGNM=1;
    259.        //DTS=1;
    260.        }
    261.        if(PORTA.RA0==1) // Do not ignore mode button
    262.        {
    263.        SD=1;
    264.       // DTS=1;
    265.        }
    266.  
    267.        if(PORTA.RA1==1)
    268.        {
    269.        HT=1;
    270.        }
    271.            if(IGNM==1&&DTS==1) // if ignore button is pressed and data is to be send
    272.            {
    273.            DTS=0;
    274.            NRF=0;
    275.            CSN_pin=1;
    276.            Delay_ms(200);
    277.              Soft_SPI_Init();
    278.            Delay_ms(200);
    279.            stat = init_Radio();
    280.            Clear_Data(Data_Out);
    281.            getConst(Data_out,IGM);
    282.          // Ce_pin=1;
    283.          //  Csn_pin=1;
    284.            sendBuffer();
    285.            delay_ms(1);
    286.             Ce_pin=0;
    287.            Csn_pin=0;
    288.             NRF=1;
    289.             IGNM=0;
    290.            }
    291.          
    292.            if(SD==1&&DTS==1) // if Silent dialling is pressed
    293.            {
    294.            DTS=0;
    295.            NRF=0;
    296.            CSN_pin=1;
    297.            Delay_ms(200);
    298.              Soft_SPI_Init();
    299.            Delay_ms(200);
    300.            stat = init_Radio();
    301.            Clear_data(Data_out);
    302.            getConst(Data_out,SDD);
    303.            //Ce_pin=1;
    304.           // Csn_pin=1;
    305.            sendBuffer();
    306.            delay_ms(1);
    307.            Ce_pin=0;
    308.            Csn_pin=0;
    309.              NRF=1;
    310.              SD=0;
    311.            }
    312.          
    313.          if(HT==1&&DTS==1) // HT start button i pressed
    314.            {
    315.            DTS=0;
    316.            NRF=0;
    317.            CSN_pin=1;
    318.            Delay_ms(200);
    319.              Soft_SPI_Init();
    320.            Delay_ms(200);
    321.            stat = init_Radio();
    322.            Clear_Data(Data_Out);
    323.            getConst(Data_out,RTH);
    324.           // Ce_pin=1;
    325.          //  Csn_pin=1;
    326.            sendBuffer();
    327.            delay_ms(1);
    328.             Ce_pin=0;
    329.            Csn_pin=0;
    330.             NRF=1;
    331.             HT=0;
    332.            }
    333.          
    334.  
    335.        }while(1);
    336. }
     
  2. Papabravo

    Expert

    Feb 24, 2006
    10,140
    1,790
    I/O ports may or may not have read-modify-write problems depending on what they are and how you use them. You are not giving us much to go on, how about at least telling us which port pins you are using for which functions before we go diving into the code. I can't think of many parts of a soft SPI port implementation that requires an RMW instruction.
     
  3. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    M, we will need more information to help you. For example, when you it's not working, can you be more specific about what doesn't work? It worked once, then what changed after that? Have you tried to trace your code to see where things work and where they stop working?

    The secret to debugging software is to Start small and build up in small increments Start with the most basic code you can write to get something working. Then add some code to that, and make sure it still works. Repeat. If at some point it stops working, then go back a step and verify it works again. You will have isolated the problem to the most recent increment you added.
     
  4. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    I am using RC4 for SDI, RC2 for SDO and RC3 for SCLK
     
  5. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    Actually according to code, Csn_pin should be logic 1 at startup but its always logic 0 when measured with DMM and the ACK LED which lights once NRF receives acknowledge from receiver, but its ON as soon as I pull input to logic 1. It should be On only when NRF receives acknowledgement but its not receiving any acknowledgement as Receiver is not receiving any thing. I have confirmed this by checking NRF on receiver side by checking its IRQ pin, it goes low as soon as receives valid data but its always high.
     
  6. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    Your Toggle_CSN routine only lets the CSN pin be high for 20 usec, then sets it to zero.
    Is that backwards, or normal?
     
  7. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    Its normal and required while communicating with NRF. but in main() I set Csn_pin=1 at start up but its always OFF
     
  8. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    Then I'd suggest starting with that. Write a test program that has nothing in it except the lines in your current program that are needed to set csn high. Get that fixed, then move to the next problem.
     
    Mrunal Ahirrao likes this.
  9. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    Thank you I will try it.
     
  10. dannyf

    Well-Known Member

    Sep 13, 2015
    1,796
    360
    If you have a scope or logic analyzer put it on the pins to see if you are sending or receiving the right data.
     
  11. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    Actually I don't have a scope or logic analyser but DMM always shows 0V on those pins.
     
  12. dannyf

    Well-Known Member

    Sep 13, 2015
    1,796
    360
    That can be hard.

    there are logic-analyzer software based on pc's soundcards so you may try that.

    Eitherway, you have to establish that

    1) the pic is working;
    2) the spi is transmitting;
    3) it is sending / receiving the right data from the radio.

    1) can be done simply; 2) can be done with limited instrumentation - you can for example use leds, or diodes or software spi to establish 2). 3) would be hard to do without a scope / la or a hardware debugger.

    to make matters worse, you seem to be using a fairly rare compiler / ide.
     
  13. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    I am using MikroC pro for PIC. Can we write our own code for software SPI bit banging? In which we can counter the PIC16F RMW error.
     
  14. Mrunal Ahirrao

    Thread Starter New Member

    Nov 24, 2015
    8
    0
    I have removed all code and just added code to switch PORTA and PORTC pins ON/OFF and its working.
     
  15. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    Mrunal, did you get this working?
     
Loading...