CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Unexpected SPI behaviour

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
MrColin



Joined: 05 Sep 2014
Posts: 13

View user's profile Send private message

Unexpected SPI behaviour
PostPosted: Thu Sep 18, 2014 5:23 am     Reply with quote

Hi. I'm having an awkward problem with SPI on a PIC16F1947. Explaining is going to be tricky so please bear with me. I'm using v4.121 of the CCS compiler.

The code below is designed to do the following.


  • Switch on and setup device as SPI slave
  • Preload SPI buffer with value 0x33
  • Interrupt on reception of byte on SPI
  • Store byte in buffer ssprxbuffer
  • Load into SPI buffer value 0x66


The main() function has a while loop which runs every half second. If within that period any data has been received and stored in the rx buffer (ssprxbuffer) then printf the value.

So far so good. When connecting my PIC to an Aardvark SPI interface the following happens when sending a single byte after the PIC has been reset

  • Send from Aardvark the value 0x22
  • PIC sends on UART the value 0x22
  • Receive back at the Aardvark the value 0x33 (pre-loaded during start-up routine)


This is as expected. When I send my next character the following happens.

  • Send from Aardvark the value 0x22
  • PIC sends on UART the value 0x22
  • Receive back at the Aardvark the value 0x66 (loaded into the SPI buffer during interrupt)



Again, this is as expected. When it starts to get weird is when I send a couple of characters in a stream together. Here's an example of an operation.

  • Send from Aardvark the values 0xAA 0xBB 0xCC 0xDD
  • PIC sends on UART the value 0xAA 0xBB 0xCC 0xDD
  • Receive back at the Aardvark the value 0x66 0xAA 0xBB 0xCC




So... clearly the interrupt is running at the correct time as the buffer ssprxbuffer is being filled up with each received byte. The SPI hardware buffer is outputting during the reception of of the first byte the value 0x66 as expected, as it's been preloaded with the correct value.

Byte 2 is where it gets weird. From the previous interrupt SPI2BUF should be preloaded with the value 0x66. You'd expect 0x66 to be clocked out as Byte 2 is being clocked in. However, it's actually the previously received byte which is still residing in the buffer and being clocked out.

Now, my question is this. Is SPI2BUF double buffered? That is, even though I'm loading into it the value 0x66 it would seem that this only ever gets outputted if there's a big enough delay between the reception on Byte 1 and Byte 2. That's the only explanation I can come up with at the moment. As far as my code's concerned SPI2BUF should always have the value 0x66 sitting waiting the be sent on the next SPI transaction.

Any thoughts would be greatly appreciated as I've been arguing with the PIC's SPI operations for a couple of days now. I've even confirmed this unusual transaction on a scope, so I can confirm that the Aardvark is working as expected.

Many thanks for your time,
Colin


Code:

/*---------------------------------------------------------------*/
// Header files
#include <16F1947.h>

/*---------------------------------------------------------------*/

// Setup Device
#device adc = 10

// Fuses
#fuses ECH
#fuses PUT
#fuses HS
#fuses PLL_SW

/*---------------------------------------------------------------*/
// Setup device peripherals
#use delay(clock=8MHZ, restart_wdt) // CAREFUL HERE!!!!
#use rs232(uart1, baud=115200, PARITY=N, BITS=8, ERRORS)  // set up the uart

/*---------------------------------------------------------------*/
// SPI Operations
#define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H)
#define SPI_MODE_1 (SPI_L_TO_H)
#define SPI_MODE_2 (SPI_H_TO_L)
#define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H)

#byte SPI2BUF=0x219 //Give direct access to SSP buffer

#define SPI_BUFFFER_SIZE 16
char ssprxbuffer[SPI_BUFFFER_SIZE]; //receive buffer
char ssptxbuffer[SPI_BUFFFER_SIZE] = {0x66, 0x66, 0x66, 0x66}; //transmit buffer

int8 spi_buf_pos=0; //byte counter
int8 spi_bytes_read=0;
int1 spi_slave_data_avialable = FALSE;

void setup_spi_slave();
void print_spi_buffer();

/*---------------------------------------------------------------*/
// General Functions
void Setup_PIC(void);
void led_on();
void led_off();
void led_toggle();

/*---------------------------------------------------------------*/
// Setup the Pic
void Setup_PIC(void)
{
   // Osccon
   setup_oscillator(OSC_NORMAL);
    set_uart_speed(115200);

   // Setup Interrupts
   enable_interrupts(INT_SSP2);
   enable_interrupts(GLOBAL);

   // Setup Watchdog
   setup_wdt(WDT_OFF);
}
/*---------------------------------------------------------------*/

void led_on(){
    output_high(PIN_E1);
}

void led_off(){
    output_low(PIN_E1);
}

void led_toggle(){
    output_toggle(PIN_E1);
}

/*---------------------------------------------------------------*/
void main()
{
    // Setup PIC
    Setup_PIC();
    setup_spi_slave();
    led_on();
    SPI2BUF = 0x33; // Preload buffer


    while(1) {
       
        if(spi_bytes_read){
            print_spi_buffer();
        }

        led_toggle();
        delay_ms(500);
    }
}
/*---------------------------------------------------------------*/

void setup_spi_slave(){
  setup_spi2(SPI_SLAVE | SPI_MODE_1);
}

void print_spi_buffer(){

    signed int i, buf_pos, start;
   
    start = spi_buf_pos - spi_bytes_read;
    start = (start>=0) ? start : (SPI_BUFFFER_SIZE + start);

    for(i=0; i<spi_bytes_read; i++){

      // Ring buffer operations
        buf_pos = start + i;

        if(buf_pos>=SPI_BUFFFER_SIZE)
            buf_pos = i - (SPI_BUFFFER_SIZE - start);

        if(buf_pos<0)
            buf_pos += SPI_BUFFFER_SIZE + 1;

      // Output relevant value
        putc(ssprxbuffer[buf_pos]);
    }
    spi_bytes_read=0;
}

#INT_SSP2
void ssp_slave_has_data(void) {

    ssprxbuffer[spi_buf_pos] = SPI2BUF;

    if (++spi_buf_pos == SPI_BUFFFER_SIZE) spi_buf_pos = 0;
    if (++spi_bytes_read == SPI_BUFFFER_SIZE) spi_bytes_read = 0;

    SPI2BUF = 0x66; // Should always output 0x66 on next SPI transaction
}
MrColin



Joined: 05 Sep 2014
Posts: 13

View user's profile Send private message

PostPosted: Thu Sep 18, 2014 5:39 am     Reply with quote

After reading through the documentation again I can see that my hunch about buffering is probably the problem. So. Is there a better way for me to be handling my SPI transactions, other than enforcing a delay between packets from my master?

Code:

The MSSPx consists of a transmit/receive shift register
(SSPxSR) and a buffer register (SSPxBUF). The
SSPxSR shifts the data in and out of the device, MSb
first. The SSPxBUF holds the data that was written to
the SSPxSR until the received data is ready. Once the
8 bits of data have been received, that byte is moved to
the SSPxBUF register. Then, the Buffer Full Detect bit,
BF of the SSPxSTAT register, and the interrupt flag bit,
SSPxIF, are set. This double-buffering of the received
data (SSPxBUF) allows the next byte to start reception
before reading the data that was just received. Any
write to the SSPxBUF register during
transmission/reception of data will be ignored and the
write collision detect bit WCOL of the SSPxCON1
register, will be set. User software must clear the
WCOL bit to allow the following write(s) to the
SSPxBUF register to complete successfully.

When the application software is expecting to receive
valid data, the SSPxBUF should be read before the
next byte of data to transfer is written to the SSPxBUF.
The Buffer Full bit, BF of the SSPxSTAT register,
indicates when SSPxBUF has been loaded with the
received data (transmission is complete). When the
SSPxBUF is read, the BF bit is cleared. This data may
be irrelevant if the SPI is only a transmitter. Generally,
the MSSPx interrupt is used to determine when the
transmission/reception has completed. If the interrupt
method is not going to be used, then software polling
can be done to ensure that a write collision does not
occur.
Ttelmah



Joined: 11 Mar 2010
Posts: 19381

View user's profile Send private message

PostPosted: Thu Sep 18, 2014 7:16 am     Reply with quote

What you haven't mentioned is how fast the SPI I/F is running?.
Vital.

Real answer is to switch to a DsPIC, where the data can be transferred using DMA.... Very Happy

Problem otherwise is that it takes a lot of machine cycles to get 'into' the interrupt, and even more to pull data from an array (look at just how much work is involved in accessing a variable in an array...). All of this has to happen _before_ the next byte starts to clock.

You could obviously change the PIC up to 32MHz, to give four times the actual speed here.
MrColin



Joined: 05 Sep 2014
Posts: 13

View user's profile Send private message

PostPosted: Thu Sep 18, 2014 7:19 am     Reply with quote

Running the SPI pretty slow, 125kbps.

And yeah, a newer PIC with DMA would be amazing, but only the PIC16's we use have any heritage in space so it's what we're stuck using!

I'm going to see how quickly I can transfer data with the short delays in place before doing anything too major.
Ttelmah



Joined: 11 Mar 2010
Posts: 19381

View user's profile Send private message

PostPosted: Thu Sep 18, 2014 8:10 am     Reply with quote

Up the PIC speed to 32MHz, as the first thing to try.
All it costs is a few uA extra power, and gives an immediate *4 in performance.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group