Problem with Software SPI

Thread Starter

Mrunal Ahirrao

Joined Nov 24, 2015
8
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:
#define TX_mode                  // define TX_mode or RX_mode
//#define chn 83
#include "nRF24L01.h"


const char IGM[]="I";
//const char IGN[]="N";
const char SDD[] ="S";
const char RTH[]="R";


// Pipe Addresses
// adr0 should be unique

// Bytes are arranged lowest to highest
const char adr0[] = {0x78,0x78,0x78,0x78,0x78};    // LSB first


char Data_Out[21], stat;
short dataLength = 20;
char j;
bit DS;

char txt[11];


// Software SPI module connections
sbit SoftSpi_SDI at SDI;
sbit SoftSpi_SDO at SDO;
sbit SoftSpi_CLK at RC3_bit;

sbit SoftSpi_SDI_Direction at TRISC4_bit;
sbit SoftSpi_SDO_Direction at TRISC2_bit;
sbit SoftSpi_CLK_Direction at TRISC3_bit;
// End Software SPI module connections

// You can change these pins as long as your microcontroller supports SPI on the required pins
sbit Irq_pin   at PORTC.B0; sfr;
sbit Mosi_pin  at PORTC.B2; sfr;
sbit Ce_pin    at PORTC.B1; sfr;
sbit Sclk_pin  at PORTC.B3; sfr;
sbit Csn_pin   at PORTC.B5; sfr;
//sbit Miso_pin  at PORTC.B4; sfr;

sbit Irq_tris  at TRISC.B0; sfr;
sbit Mosi_tris at TRISC.B5; sfr;
sbit Ce_tris   at TRISC.B1; sfr;
sbit Sclk_tris at TRISC.B3; sfr;
sbit Csn_tris  at TRISC.B2; sfr;
sbit Miso_tris at TRISC.B4; sfr;


//declarations of IO devices

sbit NRF  at RC0_bit;//NRF power bit
sbit ACK  at RA2_bit;// ACK LED to indicate ACK receieved from PRX
bit IGNM ;
bit SD   ;
bit HT  ;


bit DTS;
// I use a procedure for any delay that is used more than once
void Comm_Delay() {Delay_us(10);}       // You can experiment with this delay
void Delay_10()   {Delay_ms(10);}
void Delay_100()  {Delay_ms(100);}
void Delay_500()  {Delay_ms(500);}

void Clear_Data(char dat[])
{
     char i;
     for(i=0;i<dataLength;i++)dat[i] = ' ';
}

void toggleCSN()
{
     Csn_pin = 1;
     Delay_us(20);                      // You can experiment with this delay
     Csn_pin = 0;
     Comm_Delay();
}

char Get_Status()
{
     char s;
     Ce_pin = 0;
     toggleCSN();
     Soft_SPI_Write(STATUS);
     Comm_Delay();
     s = Soft_SPI_Read(NOP);
     Comm_Delay();
     Csn_pin = 1;
     return s;
}

char *getConst(char dest[], const char source[])
{
    int i = 0;
    while (source[i])                  // while (source[i] != '\0')
    {dest[i] = source[i]; i++;}
    dest[i] = '\0';
    return dest;
}

char Get_FIFO_Flags()
{
     char s;
     Ce_pin = 0;
     toggleCSN();
     Soft_SPI_Write(FIFO_STATUS);
     Comm_Delay();
     s = Soft_SPI_Read(NOP);
     Comm_Delay();
     Csn_pin = 1;
     return s;
}



void sendBuffer()
{
     char i;
     do{
         toggleCSN();
        Soft_SPI_Write(STATUS | W_REGISTER);
         Comm_Delay();
         Soft_SPI_Write(0xff);            //clear flags
         Comm_Delay();
         toggleCSN();
         Soft_SPI_Write(FLUSH_TX);
         Comm_Delay();
         toggleCSN();
         Soft_SPI_Write(W_TX_PAYLOAD);
         for(i = 0; i < dataLength; i++) {Comm_Delay(); Soft_SPI_Write(Data_Out[i]);}
         Comm_Delay();
         Csn_pin = 1;
         Ce_pin = 1;
         Delay_10();
         j = (Get_Status() & 0x20);                     // keep trying until ack is received
     }while(j==0);
     ACK=0;//ACK receieved from PRX and hence ACK LED is lit
}





char init_Radio()
{
       char i;
       Ce_pin = 0;                                      // must be in standby or power down to write
       Comm_Delay();
       toggleCSN();
       Soft_SPI_Write(CONFIG | W_REGISTER);
       Comm_Delay();
      
      
       #ifdef TX_mode
       Soft_SPI_Write(PWR_UP+ CRCO + EN_CRC);             // Transmitter
       Comm_Delay();


       toggleCSN();
       Soft_SPI_Write(EN_AA | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(ENAA_P0);
       Comm_Delay();

       toggleCSN();
       Soft_SPI_Write(EN_RXADDR | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(ERX_P0);
       Comm_Delay();
      
       toggleCSN();
       Soft_SPI_Write(RX_ADDR_P0 | W_REGISTER);
       for(i=0;i<5;i++){Comm_Delay(); Soft_SPI_Write(adr0[i]);} // This is the pipe in use by this transmitter
       Comm_Delay();
                                                            // The addresses above and below must be the same
       toggleCSN();                                         // Choose any of the 6 addresses used by the receiver
       Soft_SPI_Write(TX_ADDR | W_REGISTER);
       for(i=0;i<5;i++){Comm_Delay(); Soft_SPI_Write(adr0[i]);} // This is the pipe in use by this transmitter
       Comm_Delay();
       #endif

       toggleCSN();
       Soft_SPI_Write(SETUP_AW | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(AW5);
       Comm_Delay();

       toggleCSN();
       Soft_SPI_Write(SETUP_RETR | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(0xfaf);
       Comm_Delay();

       toggleCSN();
       Soft_SPI_Write(RF_CH | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(83);                                      // Set your channel here. Obviously it must be the same for TX and RX.
       Comm_Delay();

       toggleCSN();
       Soft_SPI_Write(RF_SETUP | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(RF_PWR +RF_DR_LOW );
       Comm_Delay();


       toggleCSN();
       Soft_SPI_Write(RX_PW_P0 | W_REGISTER);
       Comm_Delay();
       Soft_SPI_Write(dataLength);
       Comm_Delay();

       Csn_pin = 1;
       i = Get_Status();
       return i;
}




// =============================================================================
// =============================================================================
void main(){
        ADCON0=0x00;
        ADCON1 = 0x00;                           // digital not analog
        ANSEL=0;
        CMCON0=0x07;
        //TRISC.B5=0;//MOSI
        //TRISC.B3=0;//SCLK
        //TRISC.B4=1;//MISO
        TRISC.B1=0;//CE
        TRISC.B2=0;//CSN
        TRISC.B0=0;//NRF power bit
        TRISA =0b00001011;
       Csn_tris = 0;
       Csn_pin = 1;
       Ce_tris = 0;
       Ce_pin = 0;                              // 1 = listen or transmit
      DTS=1;
       Clear_Data(Data_Out);
         NRF=1; //NRF is off
          DS=0;
          ACK=0;
          IGNM=0;
          SD=0;
          HT=0;
          delay_ms(500);
          ACK=1;// firstly ACK will be OFF
       do {
      if(PORTA.RA3==1)  //Ignore mode button
       {
      IGNM=1;
       //DTS=1;
       }
       if(PORTA.RA0==1) // Do not ignore mode button
       {
       SD=1;
      // DTS=1;
       }

       if(PORTA.RA1==1)
       {
       HT=1;
       }
           if(IGNM==1&&DTS==1) // if ignore button is pressed and data is to be send
           {
           DTS=0;
           NRF=0;
           CSN_pin=1;
           Delay_ms(200);
             Soft_SPI_Init();
           Delay_ms(200);
           stat = init_Radio();
           Clear_Data(Data_Out);
           getConst(Data_out,IGM);
         // Ce_pin=1;
         //  Csn_pin=1;
           sendBuffer();
           delay_ms(1);
            Ce_pin=0;
           Csn_pin=0;
            NRF=1;
            IGNM=0;
           }
          
           if(SD==1&&DTS==1) // if Silent dialling is pressed
           {
           DTS=0;
           NRF=0;
           CSN_pin=1;
           Delay_ms(200);
             Soft_SPI_Init();
           Delay_ms(200);
           stat = init_Radio();
           Clear_data(Data_out);
           getConst(Data_out,SDD);
           //Ce_pin=1;
          // Csn_pin=1;
           sendBuffer();
           delay_ms(1);
           Ce_pin=0;
           Csn_pin=0;
             NRF=1;
             SD=0;
           }
          
         if(HT==1&&DTS==1) // HT start button i pressed
           {
           DTS=0;
           NRF=0;
           CSN_pin=1;
           Delay_ms(200);
             Soft_SPI_Init();
           Delay_ms(200);
           stat = init_Radio();
           Clear_Data(Data_Out);
           getConst(Data_out,RTH);
          // Ce_pin=1;
         //  Csn_pin=1;
           sendBuffer();
           delay_ms(1);
            Ce_pin=0;
           Csn_pin=0;
            NRF=1;
            HT=0;
           }
          

       }while(1);
}
 

Papabravo

Joined Feb 24, 2006
21,225
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.
 

sailorjoe

Joined Jun 4, 2013
365
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.
 

Thread Starter

Mrunal Ahirrao

Joined Nov 24, 2015
8
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.
I am using RC4 for SDI, RC2 for SDO and RC3 for SCLK
 

Thread Starter

Mrunal Ahirrao

Joined Nov 24, 2015
8
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.
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.
 

sailorjoe

Joined Jun 4, 2013
365
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.
 

dannyf

Joined Sep 13, 2015
2,197
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.
 

Thread Starter

Mrunal Ahirrao

Joined Nov 24, 2015
8
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.
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.
 

Thread Starter

Mrunal Ahirrao

Joined Nov 24, 2015
8
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.
I have removed all code and just added code to switch PORTA and PORTC pins ON/OFF and its working.
 
Top