3

I'm building a battery operated device and must shutoff MOSI pin during sleep because it leaks current through the SD card if I don't (about 400 µA).

Problem is, it won't stay off. I've looked at SPI documentation and as far as I know I just need to call spi.end() then set pinmode to output and finally digitalWrite LOW. I observe with an oscilloscope that it does go low upon my asking it to but only for about 1 µs (actually it goes low twice within a 5 µs time period).

I've also tried manually setting the pin low using the PORT registers and the behavior is the same... see relevant code:

spi.end();  

//pinMode(pin_MOSI, OUTPUT);
//digitalWrite(pin_MOSI, LOW);

//Both above (commented out) and below result in the same behavior -- pin does not stay LOW

DDRB |= B00100000;            //set pin 5 as output
PORTB &= B11011111;           //set output low for pin 5

delay(5000);

...As you can see there is a delay to ensure that it isn't being set high by some other code.

Is there some sort of SPI interrupt that automatically sets the pin high again?

Hardware details:

MCU: ATMEGA1284P; Programmer: Atmel-ICE


UPDATE (13-Mar-2019):

I didn't realize that SPI is implemented by Atmel in hardware directly (see datasheet page 105):

MOSI/PCINT13 – Port B, Bit 5 – MOSI: SPI Master Data output, Slave Data input for SPI channel. When the SPI0 is enabled as a slave, this pin is configured as an input regardless of the setting of DDB5. When the SPI is enabled as a master, the data direction of this pin is controlled by DDB5. When the pin is forced to be an input, the pull-up can still be controlled by the PORTB5 bit.

I am currently chasing down this latest information and will hopefully come to a resolution soon. I will post any progress.

techenthu
  • 125
  • 5
DatuPuti
  • 151
  • 6

1 Answers1

2

Indeed, SPI is hardware implemented. I was able to get the MOSI pin to operate as intended by clearing the SPI enable bit from the SPCR register as follows:

SPCR &= 0B10111111;  //disable SPI

To re-enable after sleep, I'm assuming I'll just do SPCR |= 0B01000000; ...pretty sure that will work but I'll update here if I have to modify my solution.

.

Perhaps also pertinant information:

I was using an object of SPIClass in SPI.h which is from the Arduino core libraries -- for some reason it wasn't disabling SPI in hardware when calling the spi.end() method, maybe because there was some other SPI object initialized by some other library. Here is the source from SPI.cpp for the end method:

void SPIClass::end() {
  uint8_t sreg = SREG;
  noInterrupts(); // Protect from a scheduler and prevent transactionBegin
  // Decrease the reference counter
  if (initialized)
    initialized--;
  // If there are no more references disable SPI
  if (!initialized) {
    SPCR &= ~_BV(SPE);
    interruptMode = 0;
    #ifdef SPI_TRANSACTION_MISMATCH_LED
    inTransactionFlag = 0;
    #endif
  }
  SREG = sreg;
}

The line SPCR &= ~_BV(SPE); should have done the job for me but I'm guessing it never got there because initialized was probably still non-zero.

DatuPuti
  • 151
  • 6