Go Down

Topic: ATtiny13a low power with direct connected LEDs to PWM pins in inverted mode (Read 372 times) previous topic - next topic

UriShX

Hi there,

I made a basic circuit around the ATtiny13a, utilizing a CR2032 battery as current source, to light up a couple of LEDs when touch is detected. The project was meant to be a quick and simple business card, and as such parts were selected for low price and (relative) ease of soldering by hand. Project details can be found at Hackaday.io, if anyone's interested.

A few months have gone by, and I figured ti's probably not a very good idea to have the t13 run all the time, and the circuit would probably function just fine with a wdt interrupt sleep routine, which will run every ~0.5 second or so. I also figured that for the next iteration I'd better change the LEDs to lower power ones, since my currently selected ones consume A LOT of power. As I said, I chose them for lowest price in Q=10, and should have probably done better research beforehand.

Anyway - in order to get the PWM function working seamlessly, I set timer0 for fast PWM with inverted inputs, as shown here.
Code: [Select]
TCCR0A = _BV(COM0A0) | _BV(COM0A1) | _BV(COM0B0) | _BV(COM0B1) | _BV(WGM00) | _BV(WGM01);

Now, when I try to turn the power down, as outlined in this thread, my DMM reading is 0.72-0.76 mA, using the code below:

Code: [Select]
#ifdef __AVR_ATtiny13__
  #define SLEEP_FOREVER  128
  #define SLEEP_016MS    ~(1<<WDP3)|~(1<<WDP2)|~(1<<WDP1)|~(1<<WDP0)
  #define SLEEP_125MS    (1<<WDP1) | (1<<WDP0)
  #define SLEEP_250MS    (1<<WDP2)
  #define SLEEP_500MS    (1<<WDP2) | (1<<WDP0)
  #define SLEEP_1SEC     (1<<WDP2) | (1<<WDP1)
  #define SLEEP_2SEC     (1<<WDP2) | (1<<WDP1) | (1<<WDP0)
  #define SLEEP_4SEC     (1<<WDP3)
  #define SLEEP_8SEC     (1<<WDP3) | (1<<WDP0)
#endif /* ifdef __AVR_ATtiny13__ */

//Discard adc interrupt
EMPTY_INTERRUPT(ADC_vect);

// set system into the sleep state
// system wakes up when watchdog times out
void system_sleep(byte b) {  
  ACSR = ADMUX = ADCSRA = 0;  
  ACSR |= (1 << ACD);                  // Analog comparator off
  ADCSRA &= ~(1<<ADEN);                // switch Analog to Digitalconverter OFF
  PRR |= (1<<PRTIM0) | (1<<PRADC);

  if (b != SLEEP_FOREVER) updateWatchDog(b);
    
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  power_all_disable();
  sleep_enable();
  sleep_bod_disable(); //Disable BOD
  sleep_cpu();  //Do not use sleep_mode(), contains sleep_enable(), sleep_cpu(),sleep_disable()
  sleep_disable();
  power_all_enable();
  updateWatchDog(SLEEP_016MS); // Set watchdog interrupt cycle to lowest for millis()
  ACSR &= ~(1 << ACD);   // Analog comparator on
  ADCSRA |= (1<<ADEN);   // switch Analog to Digitalconverter ON
  PRR &= ~(1<<PRTIM0) | ~(1<<PRADC);
}

void updateWatchDog(uint8_t c) {
    cli(); // No interrupts; timed sequence
    if (c == SLEEP_016MS) WDTCR &= SLEEP_016MS;//~(1<<WDP3)|~(1<<WDP2)|~(1<<WDP1)|~(1<<WDP0);
    else WDTCR |= c;    // Set watchdog timer
    WDTCR |= (1 << WDTIE); // Enable watchdog timer interrupts
    sei(); // Enable global interrupts or we never wake
}

void updateOCR(uint8_t var) {
  OCR0A = OCR0B = var;
}

void setup()
{
  PORTB = 0x00;// sets all digital pins LOW
  DDRB |= (1 << DDB0) | (1 << DDB1); //B00000011;  // sets Arduino pins 1 to 7 as outputs, pin 0 as input
  // Configure counter/timer0 for fast PWM w/inverted input on PB0 and PB1
  TCCR0A = _BV(COM0A0) | _BV(COM0A1) | _BV(COM0B0) | _BV(COM0B1) | _BV(WGM00) | _BV(WGM01);

  ref0 = ADCTouch.read(ADC_PIN, 256);    //create reference values to account for the capacitance of the pad
  delay(1);

  if (ref0 > 0) updateOCR(255); // Confirm ref0 initialization
}

void loop()
{
  ADCTouch.read(ADC_PIN, 2);
  currentMillis = millis();
  touchSenseVal = ADCTouch.read(ADC_PIN, 32);   //no second parameter --> 100 samples
  touchSenseVal -= ref0;
  if (touchSenseVal > THRESH) {
    // if touch detected, flicker
    flicker();
  } else if (isLit) {
    // if LEDs were previously on, keep them on for the duration of _COUNTDOWN, then turn off
    if (currentMillis - previousMillis >= _COUNTDOWN) {
      // save the last time you blinked the LED
      previousMillis = currentMillis;
      // if the LED is off turn it on and vice-versa:
      val = 255;// inverted PWM values
      isLit = false;
      updateOCR(val);
    } else {
      val = 0;// inverted PWM values
      updateOCR(val);
    }
  } else {
    system_sleep(SLEEP_1SEC);
  }
}


I tried turning DDB0 & DDB1 to inputs and setting PB0 & PB1 LOW, but the LEDs were turned half-way on, and got a reading of 0.65 mA. I also have very limited space for programming on the t13, but am definitely open to changes in the design if someone can suggest a low-power way of getting everything running the way it should.

UriShX

I realized my measurements were wrong, since I had the programming pins (ICSP) connected to the ISP using a Sparkfun level shifter. Pulled that out, and also turned the out put pins off using:
Code: [Select]
void outPinSet() {
  PORTB = 0x00;// sets all digital pins LOW
  DDRB ^= (1 << DDB0) | (1 << DDB1); // Reverse state of pins 0 & 1
}

As a result my DMM now reads 0.11 mA in sleep mode. It's not quite as optimal as I would like it to be, but it's far better than it used to be.

The problem now is that I need a long delay (about 2 seconds) before I calibrate the ADCTouch function, so when one inserts a battery, there will be sufficient time in order for his/hers hands will not affect the touch sensitivity calibration. When I add the pin switching function, I'm short by 4 bytes ("Sketch uses 1030 bytes (100%) of program storage space. Maximum is 1024 bytes.").

I suppose I can make do without BOD at all, since there is no critical memory functions that will be damaged if it's not used, but I think having the BOD may help the card seem more "professional" by not having it act erratically when the battery starts to drain significantly.

Any ideas will be much appreciated,
Thanks,
UriSh

Go Up