Having spent about 8 hours over the last few days, I am beyond my wits end with this...
I am using a MEGA328PB Xplained Mini and attempting to exchange data between SPI0 (MASTER) and SPI1 (SLAVE) using interrupts.
SPI0 sends the string "MASTER" to SPI1, while SPI1 sends the string "SLAVE " to SPI0. For reasons I have been unable to determine, SPI1 generates a write collision (WCOL) when its ISR writes the "L" in "SLAVE " to SPDR1. I inserted code in the SPI1 ISR to set an output pin to the value of the WCOL flag in the SPSR. This is the bottom trace on the logic analyzer.
I am completely lost... Any suggestions would be appreciated.
Code:
#include <string.h> #include <avr/cpufunc.h> #include <avr/interrupt.h> #include <avr/io.h> #include <avr/pgmspace.h> #include <util/delay.h> #include "port.h" // state flags #define SPI_BUSY_bm (1 << 0) #define SPI_MASTER_bm (1 << 1) #define SPI_ONLINE_bm (1 << 7) // i/o pin bitmaps #define SCK0_bm PIN5_bm /* PORT_B */ #define MISO0_bm PIN4_bm /* PORT_B */ #define MOSI0_bm PIN3_bm /* PORT_B */ #define SS0_bm PIN2_bm /* PORT_B */ #define SCK1_bm PIN1_bm /* PORT_C */ #define MISO1_bm PIN0_bm /* PORT_C */ #define MOSI1_bm PIN3_bm /* PORT_E */ #define SS1_bm PIN2_bm /* PORT_E */ #define WCOL_bm PIN2_bm /* PORT_C */ // miscel #define SPIF_bm (1 << SPIF) #define SPI_SPR_DIV_128_gc 3 #define SPI_BUFFER_SIZE 16 #define SS_DELAY_us 50 typedef struct spi_status_struct { char* rx_buf; //!< buffer to transfer to/from char* tx_buf; //!< buffer to transfer to/from uint8_t count; //!< number of bytes left to transfer uint8_t flags; //!< state flags } spi_status_t; volatile spi_status_t spi0_status; volatile spi_status_t spi1_status; int main(void) { char spi0_rx_buffer[SPI_BUFFER_SIZE]; char spi0_tx_buffer[SPI_BUFFER_SIZE]; char spi1_rx_buffer[SPI_BUFFER_SIZE]; char spi1_tx_buffer[SPI_BUFFER_SIZE]; // init master SPI PORT_B.PORT = MISO0_bm | SS0_bm; PORT_B.DDR = SCK0_bm | MOSI0_bm | SS0_bm; SPCR0 = (1 << SPE) | (1 << MSTR); // init slave SPI PORT_E.PORT = SS1_bm; PORT_C.DDR = MISO1_bm | WCOL_bm; SPCR1 = (1 << SPE); _delay_ms(5); do { cli(); // clear Rx buffers memset(spi0_rx_buffer, 0, SPI_BUFFER_SIZE); memset(spi1_rx_buffer, 0, SPI_BUFFER_SIZE); // load Tx buffers strcpy_P(spi0_tx_buffer, PSTR("MASTER")); strcpy_P(spi1_tx_buffer, PSTR("SLAVE ")); // init spi0 status spi0_status.rx_buf = spi0_rx_buffer; spi0_status.tx_buf = spi0_tx_buffer; spi0_status.count = strlen(spi0_tx_buffer); spi0_status.flags = SPI_ONLINE_bm | SPI_MASTER_bm; SPCR0 = (1 << SPE) | (1 << MSTR) | (1 << SPIE) | SPI_SPR_DIV_128_gc; // init spi1 status spi1_status.rx_buf = spi1_rx_buffer; spi1_status.tx_buf = spi1_tx_buffer; spi1_status.count = strlen(spi1_tx_buffer); spi1_status.flags = SPI_ONLINE_bm; SPCR1 = (1 << SPE) | (1 << SPIE) | SPI_SPR_DIV_128_gc; // clear WCOL trigger output PORT_C.PORT &= ~WCOL_bm; sei(); // output byte to be sent after slave is selected SPDR1 = *spi1_status.tx_buf++; // select and pause PORT_B.PORT &= ~SS0_bm; _delay_us(SS_DELAY_us); // output first byte from master SPDR0 = *spi0_status.tx_buf++; while (spi0_status.flags & SPI_BUSY_bm || \ spi1_status.flags & SPI_BUSY_bm); _delay_ms(25); } while (1); return 0; } ISR(SPI0_STC_vect) { if (spi0_status.count) { // transfer next byte *spi0_status.rx_buf++ = SPDR0; SPDR0 = *spi0_status.tx_buf++; spi0_status.count--; } else { // disable SPIF interrupt and clear BUSY flag spi0_status.flags = SPI_ONLINE_bm | SPI_MASTER_bm; SPCR0 = (1 << SPE) | (1 << MSTR) | SPI_SPR_DIV_128_gc; // pause and deselect _delay_us(SS_DELAY_us); PORT_B.PORT |= SS0_bm; } } ISR(SPI1_STC_vect) { if (spi1_status.count) { // transfer next byte SPDR1 = *spi1_status.tx_buf++; *spi1_status.rx_buf++ = SPDR1; spi1_status.count--; } else { // disable SPIF interrupt and clear BUSY flag SPCR1 = (1 << SPE) | SPI_SPR_DIV_128_gc; spi1_status.flags = SPI_ONLINE_bm; } if (SPSR1 & (1 << WCOL)) { // set WCOL trigger output PORT_C.PORT |= WCOL_bm; } else { // clear WCOL trigger output PORT_C.PORT &= ~WCOL_bm; } }
Logic analyzer capture:
Here is "port.h":
#ifndef PORT_H_ #define PORT_H_ #include <stdint.h> #include <avr/io.h> typedef struct PORT_struct { volatile uint8_t PIN; volatile uint8_t DDR; volatile uint8_t PORT; } PORT_t; #define PORT_B (*(PORT_t*) 0x23) #define PORT_C (*(PORT_t*) 0x26) #define PORT_D (*(PORT_t*) 0x29) #define PORT_E (*(PORT_t*) 0x2C) #define PIN0_bm (1 << PIN0) #define PIN1_bm (1 << PIN1) #define PIN2_bm (1 << PIN2) #define PIN3_bm (1 << PIN3) #define PIN4_bm (1 << PIN4) #define PIN5_bm (1 << PIN5) #define PIN6_bm (1 << PIN6) #define PIN7_bm (1 << PIN7) #endif
edit: added "Mini" after "Xplained"