NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » SPI: Checking SPIF while in an interrupt Handler

September 16, 2012
by jlaskowski
jlaskowski's Avatar

I am writing some SPI code where the MCU is the master and a CC3000 WiFi chip is the slave. The code responds to a CC3000 interrupt request to initiate the first SPI write. Can I do an SPI write while in an interrupt handler? I don't have the SPI interrupt turned on, so I'm just waiting for the SPIF flag to get set in a loop:

unsigned char SPI_WriteReadByte(unsigned char outByte)
{
    unsigned char inByte;

    SPDR = outByte;
blink_green();
    // wait for SPIF (SPI end-of-transmission flag) to be set to know we can write another byte
    while(!(SPSR & (1<<SPIF)));
blink_yellow();

    inByte = SPDR;
    return inByte;
}

I noticed that writing to the UART for logging with HyperTerminal doesn't work during this interrupt (although writing to the LCD does), so I'm wondering if all SPI work has to be done outside of an interrupt handler.

September 16, 2012
by jlaskowski
jlaskowski's Avatar

Here's my config code:

void configure_spi_with_cc3000() {

    // Clear PRSPI (in Power Reduction Register) to enable SPI module power
    PRR &= ~(1<<PRSPI);

    // PB5 (pin 19) = SCK (Master clock output) 
    // Set DDB5 on Data Direction Register for Port B (DDRB) to configure PB5 as output
    DDRB |= (1<<DDB5);

    // PB4 (pin 18) = MISO (master input) does not need to be configured to be input when MCU is SPI Master

    // PB3 (pin 17) = MOSI (master output) (DDB3 must be set properly for this to be output)
    DDRB |= (1<<DDB3);

    // PB2  (pin 16) = SS (slave select) (DDB2 must be set properly for this to be output)
    DDRB |= (1<<DDB2);

    // Set the SPI Interrupt Enable bit (SPIE) in the SPCR Register to have an interrupt requested after each byte is sent/received
    //SPCR |= (1<<SPIE)

    // Enable SPI
    SPCR |= (1<<SPE);

    // Make SPI Data Transmission Order to be Most Significant Bit First (MSB)
    SPCR &= ~(1<<DORD);

    // Put SPI in Master mode
    SPCR |= (1<<MSTR);

    // Set SPI clock characteristics as per CC3000 requirements
    SPCR &= ~(1<<CPOL);
    SPCR &= ~(1<<CPHA);

    // set SPI Clock with slowest speed (once I know it works, I can test with increase speeds)
    SPCR |= (1<<SPR1); 
    SPCR |= (1<<SPR0);
    SPSR |= (1<<SPI2X);

    // configure Port C3 (CC3000 PWR_EN) as output
    DDRC |= (1<<DDC3);

    // configure Port C2 (CC3000 SPI_IRQ) as input
    DDRD &= ~(1<<DDD2);

    // activate the pull-up resistor on Port D2
    PORTD |= (1<<PD2);

    // enable interrupt from CC3000's SPI_IRQ on PD2
    PCICR |= _BV(PCIE2);   //Enable PCINT2
    PCMSK2 |= _BV(PCINT18); //Trigger on change of PCINT18 (PD2)

    sei();  // enable global interrupts
}
September 16, 2012
by jlaskowski
jlaskowski's Avatar

Re-reading my original post, I realize I didn't mention the specific problem I'm having. The problem is that SPIF is not being set after writing to SPDR.

September 16, 2012
by jlaskowski
jlaskowski's Avatar

It seems if when I set these bits this way, the Master wasn't actually being set, but auto-cleared:

// Enable SPI, Master, set clock rate fck/2 (maximum)
SPCR |= (1<<SPE);
SPCR |= (1<<MSTR);

However when I set them like this, it stayed set:

// Enable SPI, Master, set clock rate fck/2 (maximum)
SPCR = (1<<SPE)|(1<<MSTR);

The ATMega168 doc says it will auto-clear the MSTR if you try to set it when SS is set as input and it's low. That should not be the case, since I set SS as output before this. Not to mention, it doesn't indicate it should make a difference whether you set the bits in SPCR in two instructions or in one.

September 16, 2012
by Noter
Noter's Avatar

In general it's a good idea to do as little as possible in an interrupt routine and give control back as soon as possible. One thing to keep in mind is that usually other interrupts will be blocked while you're in an interrupt routine.

Maybe reviewing this working example of spi master/slave will help you solve your problem ... Master/Slave SPI

September 17, 2012
by jlaskowski
jlaskowski's Avatar

I actually got it to work using an example of SPI for ATMega168 online. As I mentioned, what I found seems like it might be a bug in the ATMega168 chip. I think I set the MSTR bit correctly both ways as I've shown above, but only one works.

The document does say this: This bit selects Master SPI mode when written to one, and Slave SPI mode when written logic zero. If SS is configured as an input and is driven low while MSTR is set, MSTR will be cleared, and SPIF in SPSR will become set. The user will then have to set MSTR to re-enable SPI Master mode.

So there are times when MSTR is forced low, however, as you can see, I set SS as an output, not an input:

// PB2  (pin 16) = SS (slave select) (DDB2 must be set properly for this to be output)
DDRB |= (1<<DDB2);
September 17, 2012
by Noter
Noter's Avatar

When you enable SPI without setting MSTR 1st, it is in slave mode and that configures SS as an input. If SS is not held high then when you set MSTR it immediately goes back to slave mode. Simple to test, wire SS high and I expect your MSTR will take effect as you expect.

September 17, 2012
by Noter
Noter's Avatar

18.3.1 Slave Mode When the SPI is configured as a Slave, the Slave Select (SS) pin is always input ...

Post a Reply

Please log in to post a reply.

Did you know that NerdKits has been featured in the MIT Undergraduate Research Journal? Learn more...