Go Down

Topic: Software BOD Disable (Read 2723 times) previous topic - next topic

sdinnu

Aug 17, 2012, 11:31 pm Last Edit: Aug 17, 2012, 11:38 pm by sdinnu Reason: 1
Hey Guys,

I am trying to create a software disable of BOD. I currently have the fuses set for BOD to be enabled at 1.8v on my tiny84. However, I want to disable the BOD during sleep in order to save power (i know its not much but its relevant). My attiny84 (running arduino-tiny core) is version C so according to the datasheet it should have the feature to do the disable.

According to the datasheet:
Quote
In order to disable BOD during sleep (see Table 7-1 on page 33) the BODS bit must be written to
logic one. This is controlled by a timed sequence and the enable bit, BODSE in MCUCR. First,
both BODS and BODSE must be set to one. Second, within four clock cycles, BODS must be set to one and BODSE must be set to zero. The BODS bit is active three clock cycles after it is set. A sleep instruction must be executed while BODS is active in order to turn off the BOD for the actual sleep mode. The BODS bit is automatically cleared after three clock cycles.


So currently my code has a watchdog timer set for every 4 seconds.

The following method ensures all the pins are correctly positioned before sleep and allows me to sleep in multiples of 4 seconds to reach longer sleeps (30-60 seconds)

Code: [Select]

void timedSleep(int waitTime)
{
  digitalWrite(ampPowPin,LOW);
  pinMode(transPowPin,INPUT); // transmitter power pin
  pinMode(ampPowPin,INPUT);
  pinMode(txPin,INPUT);
  pinMode(ledPin,INPUT);
 
  // Calculate the delay time
  int waitCounter = 0;
  while (waitCounter != waitTime)
  {
    system_sleep();
    waitCounter++;
  }
 
  pinMode(txPin,OUTPUT);
  pinMode(ledPin,OUTPUT);
  pinMode(ampPowPin,OUTPUT);
  digitalWrite(ampPowPin,HIGH);
}


This method is called by the above method and should stop the BOD and put the chip to sleep but its not working properly.
Code: [Select]
void system_sleep() {
 
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  MCUCR = (1<<BODS) | (1<<BODSE); // set both BODS and BODSE to 1
  MCUCR = (1<<BODS) | (0<<BODSE); // set BODS to 1 and BODSE to 0
  sleep_cpu ();         // sleep within 3 clock cycles of above
  //sleep_mode();                        // System sleeps here

  sleep_disable();                     // System continues execution here when watchdog timed out
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON

}


Can anyone help me out?

Tom Carpenter

#1
Aug 17, 2012, 11:57 pm Last Edit: Aug 17, 2012, 11:59 pm by Tom Carpenter Reason: 1
The problem is that you are disabling the Sleep enable bit when you set the BODS and BODSE bits because you are using an assignment not a bitwise or. However in this case you have to as you are clearing a bit as well.

You need to replace this code:

Code: [Select]
 sleep_enable();
 MCUCR = (1<<BODS) | (1<<BODSE); // set both BODS and BODSE to 1
 MCUCR = (1<<BODS) | (0<<BODSE); // set BODS to 1 and BODSE to 0


With one of these two
(1)
Code: [Select]
 MCUCR = (1<<BODS) | (1<<BODSE); // set both BODS and BODSE to 1
 MCUCR = (1<<BODS) | (0<<BODSE); // set BODS to 1 and BODSE to 0
 sleep_enable(); //this should allow sleep_cpu() to still be called within 3 clock cycles as this only takes one (sbi instruction)


(2)
Code: [Select]

 MCUCR = (1<<BODS) | (1<<BODSE); // set both BODS and BODSE to 1
 MCUCR = (1<<SE) | (1<<BODS) | (0<<BODSE); // set BODS to 1 and BODSE to 0, set the sleep enable bit at the same time.
~Tom~

sdinnu

For some reason both of the code examples you gave me ended up not working. They both caused my tiny84 to not go to a complete sleep state (sleep at .74mA rather than .02mA) so im not sure exactly what it did.

However I did find code that does work properly in my project

Code: [Select]

set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  MCUCR = MCUCR | bit(BODSE) | bit(BODS); // timed sequence
  MCUCR = MCUCR & ~ bit(BODSE) | bit(BODS);
  sleep_cpu();

Tom Carpenter

Thats interesting because sleep_enable() is defined as this:

Code: [Select]

#define _SLEEP_CONTROL_REG  MCUCR
#define _SLEEP_ENABLE_MASK  _BV(SE)

#define sleep_enable()             \
do {                               \
 _SLEEP_CONTROL_REG |= (uint8_t)_SLEEP_ENABLE_MASK;   \
} while(0)


So that essentially optimises to:

MCUCR |= _BV(SE)
in other words:
MCUCR = MCUCR | (1<<SE);


EDIT:
I see what happened, you were not just overwriting the SE bit, but also the sleep mode select bits essentially putting it into idle mode sleep.

Just for pretty codes sake, your code can be written equally as:
Code: [Select]
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
 sleep_enable();
 MCUCR |= bit(BODSE) | bit(BODS); // timed sequence
 MCUCR = (MCUCR & ~bit(BODSE)) | bit(BODS);
 sleep_cpu();

But that is just personal preference as it better shows what is happening. Both are perfectly valid.

Out of curiosity, does this work:
Code: [Select]
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
 sleep_enable();
 MCUCR |= bit(BODSE) | bit(BODS); // timed sequence
 MCUCR &= ~bit(BODSE); //BODS is already high.
 sleep_cpu();

It may not do as the second modification of MCUCR will optimise to use the cbi() which means both are not being written at the same time. It would be interesting to know if it does work though.
~Tom~

sdinnu

Your rewrite code did work perfectly. However your second code block does not work. It does allow the mC to go into power down sleep but does not disable the BOD.

Thanks alot btw. Bitwise operators sometimes hurt my head... gotta sit down and take it step by step haha.

Tom Carpenter

Yeah, I didn't think the second one would. Its all down to the way the compiler chooses which assembler instruction to use.
~Tom~

Go Up