• AVR Freaks

Problems with SPI master-slave communication

Author
Maldus
Starting Member
  • Total Posts : 54
  • Reward points : 0
  • Joined: 2016/08/17 09:55:57
  • Location: 0
  • Status: offline
2019/07/11 05:54:12 (permalink)
0

Problems with SPI master-slave communication

Hello everyone,
I'm having quite some trouble with SPI master-slave communication between two PIC24EP256GP206.
I'm sending a total of 10 bytes as a packed enclosed in a preamble + crc combination. The 10 bytes are exchanged simultaneously.
The slave uses the internal SPI2 module, while the master works in bitbang.
The slave receives data just fine (barring a 10%-20% ratio of malformed packets that I'm attributing to distubarnces in the signals), but the master receives wrong formats with recurring patterns about 50% of the times.

The slave should be sending the following bytes: [0xA0, 1,2,3,4,5,6,7,8, 0xF8 (checksum)]
Instead, the master frequently receives some overlapping shift of the packet:

{0xA0, 1,2,3,4,5,6,7,8, 1}
{0xA0, 1,2,3,4,5,6,7, 0xA0, 1}
{7,8, 0xF8, 2,3,4,5,6,7,8}


If I try to read more than 10 bytes (e.g. 30) I can see that the pattern is recurring over the buffer. I am not sure about what this means since the slave only ever writes 10 times on the SPI2BUF.
It would appear as if the SPI2 module buffers more than 10 bytes into its FIFO, but I only write 10... It is unlikely (is it?) that they are stacked between multiple transactions because I reset the SPI module every time to clear the buffers.

This is the code on the Master's side:
 

 
 
 
            static unsigned int errorbuffer[128];
            static unsigned int error_ratio = 0;
            static int index = 0;
            static unsigned long counter = 0;
            int i;
            unsigned long sum = 0;
            uint8_t send[32] = {0xC1,0xAA,0x11,0,0xFF,0,0,0,0,0,0,0}; // This data is irrelevant
            send[9] = calculate_crc(&send[1], 8);
            uint8_t receive[32] = {0};
            send_packet_to_slave(send, receive, 32);
            
            if (elaborate_packet(receive, 10) != 0) { // This checks the crc, and it ends up being wrong about 50% of the times
                errorbuffer[index] = counter;
                index = (index + 1) % 128;
                counter  = 0;
            }
            else {
                counter++;
            }
            
            for (i = 0; i < 128; i++) {
                sum += errorbuffer[i];
            }
            sum = sum > 0 ? sum : 1;
            error_ratio = (100*100)/sum;
 
 
 

 
With this being initialization and utility routines:

 
 
 
uint8_t calculate_crc(uint8_t *buffer, int len) {
    int i;
    uint8_t crc = 0xF0;
    for (i = 0; i < len; i++)
        crc ^= buffer[i];
    
    return crc;
}

int send_packet_to_slave(uint8_t *send, uint8_t *receive, int len) {
    int i = 0, j = 0;
    uint8_t first = 0xFF;
    CS_SLAVE = 1;
    delay_us(10);
    
    while (j++ < 16 && first != 0xA0)
        first = SPIByteExchange(send[0]);
    receive[0] = first;
    
    for (i = 1; i < len; i++) {
        receive[i] = SPIByteExchange(send[i]);
        delay_us(1);
    }
    delay_us(100);
    CS_SLAVE = 0;
    return j;
}
 
 
 
 
 
 
 
char SPIByteExchange(char byte)
{
    int i;
    char val, received = 0, tmp;
    SPI_SCK = 1;
    
    for (i = 0; i < 8; i++)
    {
        val = byte & 0x80;
        
        if (val)
        {
            SPI_SDO = 1;
        }
        else
        {
            SPI_SDO = 0;
        }
        SPI_SCK = 1;
        delay_us();
        SPI_SCK = 0;
        delay_us();
        
        tmp = SPI_SDI;
        byte = byte << 1;
        received = received | tmp;
        if (i < 7)
            received = received << 1;
    }
    
    SPI_SCK = 1;
    return received;
}
 
 
 

 
This is the slave's loop:

 
 
 
    uint8_t buffer[128] = {0};
    uint8_t response[128] = {0};
    unsigned int errorcounter  = 0;
    int i;
 
 
 
 
 
 
 
for (;;) {
        
        if (CSS == 0) {
            i = 0;
            sendi = 0;
            tosend = 10;
            memset(response, 0, 32);
            response[0] = 0xA0;
            response[1] = 1;
            response[2] = 2;
            response[3] = 3;
            response[4] = 4;
            response[5] = 5;
            response[6] = 6;
            response[7] = 7;
            response[8] = 8;

            response[9] = calculate_crc(&response[1], 8);
            spi_slave_dout_on();
            clear_receive_buffer();
            
            spi_slave_blocking_send(response, 10);
            while (CSS == 0) {
                Nop();
            }
            spi_slave_dout_off();

            spi_slave_reset();
            memset(buffer, 0, 32);
        }
 
 
 

 
And there are the initialization and functions used:

 
 
 
int spi_slave_blocking_send(uint8_t *data, int len)
{
    int i = 0;
    // Wait for free buffer
    while (CSS == 0 && i < len) {
        if (!SPI2STATbits.SPITBF)
            SPI2BUF = ~data[i++];
    }
    return i;
}


 
 
 
int spi_slave_data_ready() {
    return !SPI2STATbits.SRXMPT;
    //return SPI2STATbits.SPIRBF;
}

void spi_slave_dout_off() {
    SPI_TRISTATE = 1;
}
void spi_slave_dout_on() {
    SPI_TRISTATE = 0;
}

void init_spi_slave(void)
{
    //Initialize the SPI
    PPSUnLock;
    PPSInput(IN_FN_PPS_SDI2,IN_PIN_PPS_RPI44);
    PPSInput(IN_FN_PPS_SS2, IN_PIN_PPS_RPI96);
    PPSInput(IN_FN_PPS_SCK2, IN_PIN_PPS_RP42);
    PPSOutput(OUT_FN_PPS_SDO2,OUT_PIN_PPS_RP57);
    PPSLock;
   
    // Configure CS pin as an input
    CSS_TRIS = INPUT_PIN;
    SDI2_TRIS = INPUT_PIN;
    SDO2_TRIS = OUTPUT_PIN;
    SCK2_TRIS = INPUT_PIN;
    
    SPI_TRISTATE_TRIS = OUTPUT_PIN;
    
    SPI2STAT = 0;
    SPI2CON1 = 0;
    SPI2CON1bits.SSEN = 1;//SS2 pin used
    SPI2CON1bits.MSTEN = 0; //slave mode
    SPI2CON1bits.MODE16 = 0; //8bit mode
    SPI2CON1bits.CKE = 0; //Serial output data changes on transition from Idle clock state to active clock state
    SPI2CON1bits.CKP = 1; //Idle state for clock is a high level; active state is a low level
    SPI2CON1bits.SMP = 0; //Input data is sampled at middle of data output time
    SPI2CON2 = 0;
    //SPI2CON2bits.FRMEN = 0; //Framed sync disabled
    SPI2CON2bits.SPIBEN = 1; //Enhanced buffer is enabled
    SPI2STATbits.SPIEN = 1;
    //SPI2CON2bits.SPIFSD = 1;
    
    IFS2bits.SPI2IF = 0;
    IEC2bits.SPI2IE = 0;
    IPC8bits.SPI2IP = 3;
    SPI2STATbits.SISEL = 0b010;
    
    spi_slave_dout_off();
}

void spi_slave_reset() {
    SPI2STATbits.SPIEN = 0;
    Nop();
    Nop();
    Nop();
    SPI2STATbits.SPIEN = 1;
    Nop();
    Nop();
    Nop();
}
 
 
 

 
In my experience the SPI modules for PIC24 are fairly arbitrary in their behavior, but I'd like to avoid working in bitbang on the slave side. Is there any glaring problem that I should be wary about?
post edited by Maldus - 2019/07/11 06:02:53
#1

3 Replies Related Threads

    Aussie Susan
    Super Member
    • Total Posts : 3584
    • Reward points : 0
    • Joined: 2008/08/18 22:20:40
    • Location: Melbourne, Australia
    • Status: offline
    Re: Problems with SPI master-slave communication 2019/07/11 20:59:38 (permalink)
    4 (1)
    My experience with the Microchip SPI hardware (of all flavours) is really solid provided you use them correctly (i.e.  on the slaves side you must us the \CS\ pin correctly).
    The slave should be reading the received values from the buffer, even if it is just throwing them away. If you done then you will set the SPIROV bit and the slave will then not receive any further values until it is cleared.
    For the slave, I would use the pseudo-code:

    int i = 0;
    while( i < len)
    {
        SPIxBUF = sendValues[i];
        while(!SPIxSTATbits.SPIRBF);
        junk = SPIxBUF;
        i++;
    }

    Also make sure that the bit-banged master is reading the values from the slave while the data is stable. Using the hardware makes this easy as you just set things up the same on both sides, but you have to get the timing right when you bit-bang. I've not gone through all of the code and timing diagrams but this could be where the problem lies.
    Susan
    #2
    Maldus
    Starting Member
    • Total Posts : 54
    • Reward points : 0
    • Joined: 2016/08/17 09:55:57
    • Location: 0
    • Status: offline
    Re: Problems with SPI master-slave communication 2019/07/11 22:31:46 (permalink)
    0
    Hello Susan, thank you for the insight.


    If you done then you will set the SPIROV bit and the slave will then not receive any further values until it is cleared.

    I was under the impression the SPIROV bit was something that was set in hardware and that I could only clear in software, with only notification purposes. You say it blocks further values from being read?


    Also make sure that the bit-banged master is reading the values from the slave while the data is stable

    Yes, I had some trouble in the beginning but now it seems to be stable. My problem does not concern the integrity of the data, single bytes are sent and received correctly; it seems more like I don't fully understand how the enhanced buffer works since the master receives copies of bytes that have not been loaded. I will probably either switch to legacy mode ( no enhanced buffer ), or use the dma.
    #3
    Aussie Susan
    Super Member
    • Total Posts : 3584
    • Reward points : 0
    • Joined: 2008/08/18 22:20:40
    • Location: Melbourne, Australia
    • Status: offline
    Re: Problems with SPI master-slave communication 2019/07/14 19:35:10 (permalink)
    4 (1)
    Its not just me that says it: Section 3.7 of the SPI FRM says:

    3.7 SPIx Error Handling
    If a new data word has been shifted into the SPIxSR register and the previous SPIxBUF register contents have not been read, the SPIROV bit (SPIxSTAT<6>) is set. Any received data in the SPIxSR register is not transferred and further data reception is disabled until the SPIROV bit is cleared. The SPIROV bit is not cleared automatically by the module; it must be cleared by the user application.

    As for the enhanced buffer operation, you need to read up  on how to populate the FIFO and make sure you understand when the various flags are set (and interrupts triggered if appropriate).
    If you want to use the DMA then it requires the enhanced buffer be turned off.
    Susan
    #4
    Jump to:
    © 2019 APG vNext Commercial Version 4.5