ATTiny841 - Sleep Command and Register usage.

Hi.

I am trying to incorporate more register use in my code (mainly for learning purposes).

I have planned an IR remote which will use an ATTiny841 running on a 3V CR2032.
The PCBs are not here yet, but I thought Id get started on the code.

I have commented on my code as I have gone along.

My 2 questions:

  1. How do you get the ATtiny841 to “sleep”. I seem to be able to find the “sleep mode” register and the “Power Saving” register and the “Sleep enable” bit in MCUCR, but how do you actually then send the “Sleep Command”?

  2. Does my code seem reasonable so far for the use of registers to trigger interrupts and make a 38KHz modulated IR signal?

/*  Using Power Down mode with Pin Change interrupts to wake unit.

   MCUCR has the SE (sleep enable bit).
   Sleep Mode Select bits (SM1 and SM0) are in MCUCR. SM[1:0] = 2 is "Power-down".
   PRR register is the Power Reduction Register.
   Setting PRR = 0xFF will use least power while sleeping.
   Remember to reset PRR = 0 and MCUCR|=(0<<SE) on waking!


*/
#define ID 29

#define IRon TCCR0A |= (1<<COM0A0);
#define IRoff TCCR0A |= (0<<COM0A0);
#define ledOn PORTB |=(1<<PORTB3);
#define ledOff PORTB |=(0<<PORTB3);

volatile boolean button_pressed = false;

void setup() {

  //PB2 is the IR led
  //PB3 is the status LED
  //PORTA(7:0) are all buttons...need an internal pull up.


  // Set the LEDs as OUTPUTS.
  DDRB = 0;
  DDRB |= (1 << DDB2) | (1 << DDB3);

  // Enable all pull ups on PORTA.
  PUEA = 0xFF;

  // Set up the 38Khz modulated signal using 8 bit Timer 0
  // Using CTC mode with a compare register A. (Mode 2)
  // Toggle the IR LED on by setting the COM0A0 bit to a 1...
  TCCR0A = 0;
  TCCR0A |= (1 << WGM01);
  TCCR0B = 0;
  TCCR0B |= (1 << CS00); // No pre-scale.

  // At 8MHZ, 38KHz = 210 periods.
  // Need to toggle twice in a period (L-H, H-L)
  //So toggle every 105 "ticks"

  OCR0A = 105;

  // Set up the interrupt for the pins. Pin Change Interrupt.

  SREG = 0;
  SREG |= 0b10000000; // Enable Interrupts.



  // Enable Pin Change Interrupts on PCINT[7:0] i.e. PORTA pins (the buttons).
  
  GIMSK = 0;
  GIMSK |= (1 << PCIE0);
  PCMSK0 = 0xFF;  // Set all PORTA (PCINT[7:0]) pins as "interruptable".

// Set the sleep mode...
  MCUCR = 0;
  MCUCR |= (1<<SM1)|(0<<SM0); // Sleep mode = Power Down.

}

void loop() {

  // Turn on modules again...

  PRR = 0;
  SREG = 0; // Turn off interrupts.

  if (button_pressed) {

    // Do the code stuff...

  }

// ==========Go to sleep ===============

PRR = 0xFF; // Turn off stuff to save power.
SREG = 0b10000000; // Turn on Interrupts...
MCUCR |= (1<<SE); // Set sleep enable.
extern void sleep_cpu();   // SLEEP command.
__asm__ __volatile__ ( "sleep" "\n\t" :: );

}

ISR(PCINT0_vect) {
  /* This ISR is called when a Pin Change Interrupt occurs on a PORTA pin.
   *  The button_pressed boolean is set true to allow main() to 
   *  then read the state of the pins (PINA) and then carry out code.
   */
  ledOn
  button_pressed = true;
}

unsigned long form_packet() {
  unsigned long pack = 0;
  byte buttons = !PINA;
  //add the ID to the pack...
  pack += ID;
  // Now shift left 8 to fit in the buttons byte...
  pack = pack << 8;
  pack += buttons;
  //Now shift six places for the !ID
  pack = pack << 6;
  pack += !ID;
  //Shift 8 for the inverse of the buttons...
  pack += !buttons;
  //Now shift 4 for the remaining bits to add a 4 bit CHK.

  //Check is the number of 1s in the pack...
  byte CHK = 0;
  for (byte i = 0; i < 32; i++) {
    CHK += (pack >> i) & 1;
  }
  pack += CHK;
}
  1. How do you get the ATtiny841 to "sleep". I seem to be able to find the "sleep mode" register and the "Power Saving" register and the "Sleep enable" bit in MCUCR, but how do you actually then send the "Sleep Command"?

This code puts the processor into power_down sleep mode.

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();