ATtiny412 Project Advice

Here's what's required:

  1. Unit is in low power mode most of the time. Running from 5 volts; drawing less that a few microamps.

  2. Every minute, a 30kHZ square wave burst (about 20 mSec long) is sent to LED1 and another pin listens to a remote control receiver for a reflected IR wave. If received, LED2 flashes for 100mSec every 2 seconds. Power is supplied from an IO pin and is removed from the receiver when not active.

  3. Every minute or few minutes, an A to D reading is taken to check on battery voltage. If below a threshold, another LED flashes for 10 mSec every 4 seconds to alert the user.

Pins:
UPDI
LED1
LED2
Receiver Vdd
Receiver signal

The 412 was chosen for its low price, UPDI programming and features. Quantities in the hundreds.

Below is code I found and modified that gets the basic RTC/PIT system working but I don't yet see how to incorporate the needed functions. What's the general way to call functions in a sleeping system?

Is the project too much for the chip? Would a different chip, or a 2 chip solution make it easier? I have experience with PIC chips but very little with AVR.

Any thoughts would be greatly appreciated.

RTC/PIT test code:

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

static void ledON()  {PORTA.DIRSET = PIN3_bm;  PORTA.OUTSET = PIN3_bm;}
static void ledOFF()  {PORTA.DIRSET = PIN3_bm;  PORTA.OUTCLR = PIN3_bm;}

int i=0;

ISR(RTC_PIT_vect){
    RTC.PITINTFLAGS = 1; //clear PI flag
    i=(i+1)%10;
    if(i==0) ledON();    
    if(i==1) ledOFF();   
     }

 int main(){
    //rtc clksel is 0 by default = int32k
    RTC.PITINTCTRL = 1; //PI, enable pit irq
    while( RTC.PITSTATUS & 1 ){} //check CTRLBUSY before writing pitctrla
    RTC.PITCTRLA = (0xE<<3)|1; //PERIOD 32768 cycles, PITEN

    SLPCTRL.CTRLA = (2<<1)|1; //POWERDOWN, SEN sleep enable
    sei(); //enable interrupts
    
    while(1){
        asm( "sleep ");
         }

}

Wakeup, normal function calls, then back to sleep, all in a (possibly endless) loop.

Ah, makes sense. So wake up from inside the ISR.
Thanks.

Depends on how that MCU works, and the sleep settings. With the proper settings, any interrupt can wake up the processor to begin normal processing.

Nothing need be done in the ISR (although an empty ISR must be defined, corresponding to the triggered interrupt).

See this excellent tutorial: Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors

If that code does what you want, you could try something like the following.
(Are you sure you need to clear the interrupt flag? With most devices that is automatic.)

ISR(RTC_PIT_vect){
    RTC.PITINTFLAGS = 1; //clear PI flag
     }

// in main(), loop(), etc.

    while(1){
    asm( "sleep ");
    i=(i+1)%10;
    if(i==0) ledON();    
    if(i==1) ledOFF();
}   

This seems to work, but how does the code right after the sleep instruction get reached? Does PIT wake the processor up every second?

I tried putting a delay(100) instruction in but then nothing works. What is the issue there?

Thanks.

That is how wake from sleep is supposed to work. The interrupt starts the CPU running at the instruction immediately following the sleep instruction, as described in the Gammon tutorial linked above.

Yes, I read the link. Good reading. I have used sleep mode with other processors and was aware that the instruction after the sleep command gets executed upon wakeup.

My main source of confusion is the RTC/PIT usage which I am very unfamiliar with. The code in the link is more familiar to me than the code above with macros (like PIN3_bm which I can't seem to find the definition of) and with everything being done by manipulating registers.

Can I still use digitalWrite() and other "Arduino" functions?

The functions I need to call in the loop need to use something like delay(), which doesn't seem to work here.

The sleep instruction should be called in the main program, and wake up should start the processor running the next instruction. That is where you should execute all "normal" function calls and instructions.

For delay() to function normally, interrupts have to be on. It will not work as you expect within an ISR and may even hang up the processor if you try. If delay() doesn't work in the main loop, then the global interrupts may be off.

I haven't looked carefully at the ATtiny412 data sheet, but you should study it very carefully, as there are many differences between that series and the AVR series of MCUs discussed by Nick Gammon.

Thank you much for your helpful suggestions.

I looked at the PIT section of the ATtiny412 data sheet, and it does appear that the overflow interrupt flag needs to be explicitly reset in the ISR.

That is a significant change from the original AVR/ATtiny design philosophy for peripherals, but who knows why?

Interesting. I think another difference is that the wdt only resets the chip, but with the older tinys you could use it in a non-reset mode to prodice a periodic interrupt. Is this correct?

Yes, that is correct, and the wdt periodic interrupt is very handy. The ISR can be completely empty. The 32 kHz oscillator looks like a very nice replacement for the wdt, with the latter's few options for timing.

Another post reminded me that with some Arduino cores, replacing the hidden main() skips initialization steps, such as setting up delay().

Use the standard convention of setup() and loop() to avoid that.

Wow. That's very interesting. Will try. Thanks!

That was it! delay() works now.

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

static void ledTog(){ PORTA.DIRSET = PIN3_bm; PORTA.OUTTGL = PIN3_bm; } //led = PB5

 ISR(RTC_PIT_vect)
    {
    RTC.PITINTFLAGS = 1; //clear PI flag   
    }

void setup() 
  {
  //rtc clksel is 0 by default = int32k
  
  while( RTC.PITSTATUS & 1 ){} //check CTRLBUSY before writing pitctrla
  RTC.PITCTRLA = (0xE<<3)|1; //PERIOD 32768 cycles, PITEN
  RTC.CTRLA = 0x20;
  RTC.PITINTCTRL = 1; //PI, enable pit irq
  SLPCTRL.CTRLA = (2<<1)|1; //POWERDOWN, SEN sleep enable
  sei(); //enable interrupts  
  }

void loop() 
  {
  asm( "sleep ");
  ledTog();
  delay(5000);
  }

The delay(5000) defeats the power saving benefit of sleep. I would put it to sleep for 5 more seconds, instead.

Exactly. Just trying to learn the basic ropes here.

In terms of dialing out different times for tasks, why doesn't the RTC prescaler work in the below code?
LED continues to toggle once per second regardless of what's loaded in the RTC prescaler bits.

#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

static void ledTog(){ PORTA.DIRSET = PIN3_bm; PORTA.OUTTGL = PIN3_bm; } 

 ISR(RTC_PIT_vect)
    {
    RTC.PITINTFLAGS = 1; //clear PI flag   
    }

void setup() 
  {
  //rtc clksel is 0 by default = int32k
  
  while( RTC.PITSTATUS & 1 ){} //check CTRLBUSY before writing pitctrla
  RTC.PITCTRLA = (0xE<<3)|1; //PERIOD 32768 cycles, PITEN
  /////////////
  RTC.CTRLA = (4<<3) | 1;   /// RTC prescale doesn't work??
  ////////////
  RTC.PITINTCTRL = 1; //PI, enable pit irq
  SLPCTRL.CTRLA = (2<<1)|1; //POWERDOWN, SEN sleep enable
  sei(); //enable interrupts  
  }

void loop() 
  {
  asm( "sleep ");
  ledTog();
  }

From the data sheet, it is not clear to me that the prescaler output is used by the PIT. Check the Microchip forum.

Hmm- Yes the 412 datasheet shows RTC is not prescaled for PIT, but the 202/402 shows it is. I'm actually programming the 202 and that prescaler doesn't work there but the graphic suggests it should. More digging...

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.