2

I am working on a ultra-low power project using an ATMEGA328P, a nRF24L01 and a 0.47F super-capacitor to measure various sensors akin to Gammon's solar power project. I have been having problems with keeping the current consumption of the nRF24L01 low which appears to be a common problem as noted here. I only have been able to get the current down during sleep to 950uA as opposed to 7uA. Power for the nRF24L01 comes from an AMS1117 that is switched on and off from a digital pin. Thus, during sleep the nRF24L01 isn't drawing power directly so the current must be coming from the SPI. According to Gammon's code, after the radio message is sent he sets the SPI pins as OUTPUT and LOW following SPI.end(), like so:

bool ok = radio.write (&reading, sizeof reading);
radio.startListening ();
radio.powerDown ();
SPI.end ();
// set pins to OUTPUT and LOW  
for (byte i = 9; i <= 13; i++)
    {
    pinMode (i, OUTPUT);    
    digitalWrite (i, LOW); 
    }  // end of for loop

Since the current consumption is coming from the SPI, I decided to reverse this by setting the SPI pins to INPUT assuming that SPI.begin() would automatically set them back appropriately. At this point current during sleep was the expected 7uA but no messages were being sent. I checked around and found that there was a problem where SPI.end() causes SPI.transfer() to hang even after a new SPI.begin(). Unfortunately the solution there didn't work in my case.

I tried to change the pins to OUTPUT when coming out of sleep but that obviously didn't work. One of the posts pointed out that the pins shouldn't be configured as output before calling SPI.begin() because SPIClass::begin() does not have code in it to do so, and that this is probably done so for a reason. Indeed, in Gammon's code, where he sets all pins to OUTPUT and LOW during setup in order to minimize current consumption, he skips the SPI pins:

  // set pins to OUTPUT and LOW  
  for (byte i = 0; i <= A5; i++)
    {
    // skip radio pins
    if (i >= 9 && i <= 13)
      continue;
    pinMode (i, OUTPUT);    
    digitalWrite (i, LOW);  
    }  // end of for loop  

What I don't understand is why is it possible to set the SPI pins as OUTPUT after SPI.end() and not do the same after coming out of sleep and just before SPI.begin()? Also, why is it that if I tristate the pins after SPI.end() the SPI bus doesn't appear to work, that is, SPI.begin() appears to hang?

2
  • Interesting problem. How do you know that "it hangs"? The forum thread you refer to is almost 10 years old and this exact problem got already fixed.
    – Sim Son
    Commented Jun 17, 2021 at 6:01
  • You're right. I am not sure that it "hangs". But apparently the problem hasn't been fixed because by simply using SPI.end() current is still flowing through the SPI bus.
    – jhorv
    Commented Jun 17, 2021 at 10:44

1 Answer 1

2

I finally found an answer here. The exact problem is close to what I had thought: the MOSI pin isn't set low after SPI.end(). The solution is not to set the pin as INPUT as I had first thought, but to set the MOSI pin low by first adding

SPCR &= 0B10111111;

before putting the system to sleep (which disables the SPI) and then adding this line

SPCR |= 0B01000000;

after coming out of the sleep in order to re-enable the SPI.

Although this problem was already noted years ago and a fix apparently made, it still exists because SPI.end() should have disabled the SPI bus and it didn't, so this workaround is still needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.