|
|
View previous topic :: View next topic |
Author |
Message |
MrColin
Joined: 05 Sep 2014 Posts: 13
|
Unexpected SPI behaviour |
Posted: Thu Sep 18, 2014 5:23 am |
|
|
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
|
|
Posted: Thu Sep 18, 2014 5:39 am |
|
|
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: 19517
|
|
Posted: Thu Sep 18, 2014 7:16 am |
|
|
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....
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
|
|
Posted: Thu Sep 18, 2014 7:19 am |
|
|
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: 19517
|
|
Posted: Thu Sep 18, 2014 8:10 am |
|
|
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. |
|
|
|
|
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
|