Tips n Tricks

From wikipost

Here's a compilation of various tips and tricks I came across over the years. Feel free to use and re-use as much as you like.


set, clear and toggle macros

With AVR micros an often cryptic method is used to set, clear or toggle bits. Take for instance the following example to set a digital port to output and set it to a digital '1'.

#DEFINE LEDPIN PB0        // define LED pin

DDRB |= (1 << LEDPIN);    // set to output

PORTB |= (1 << LEDPIN);   // write a digital '1'

With these macros writing a digital '1' is now as simple as:

// Some helper MACROS
#define SET |=
#define CLR &=~
#define TOG ^=

#DEFINE LEDPIN PB0         // define LED pin

DDRB SET (1 << LEDPIN);    // set to output

PORTB SET (1 << LEDPIN);   // write a digital '1'


bitwise operations

Similar to the above, here are some commands to set, clear or toggle a specific bit:

  • set (to 1) bit 2 (the third bit from the right) of variable a
a |= (1<<2);
  • clear (to 0) bit 2 (the third bit from the right) of variable a
a &= ~(1<<2);
  • toggle bit 2 (the third bit from the right) of variable a
a ^= (1<<2);


Below is a set of macros that works with ANSI C to do bit operations:

#define bit_get(p,m) ((p) & (m))
#define bit_set(p,m) ((p) |= (m))
#define bit_clear(p,m) ((p) &= ~(m))
#define bit_flip(p,m) ((p) ^= (m))
#define bit_write(c,p,m) (c ? bit_set(p,m) : bit_clear(p,m))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))


byte type

In some programming environments the 'byte' type is readily available. It's usually just an unsigned char but if you don't want to search-and-replace every instance of byte with unsigned char in your code then here's a simple type definition that creates the byte type.

typedef unsigned char byte; //create byte type


rounding and decimals

Float values sometimes have too many decimals. Here are some generic rounding formulas:

fl =  (int) (fl + 0.5f)/1.0f;            // round to 0 decimals
fl =  (int) (fl * 10 + 0.5f)/10.0f;      // round to 1 decimals
fl =  (int) (fl * 100 + 0.5f)/100.0f;    // round to 2 decimals


AVR Sleep Mode

A recent project I built using an ATtiny85 micro was working fine and using about 4mA in idle mode. The circuit and its peripherals were powered through a 78L05 so I was wondering by how much I could lower the current draw by using the sleep function of the micro.

Here's how I configured sleep mode to bring the current draw down to 2.4mA. This suggests that the voltage regulator still takes some energy to run at very low levels..

The technique below uses the expiry of the on-board watchdog timer to generate an interrupt which wakes the micro.


Step 1 - import the sleep, watchdog and interrupt libraries

Location: at the top of your code

#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>


Step 2 - add some helper functions

Location: above the main() block


/* NOTES:
 
                            ATtiny85   ATtiny84
  Watchdog Timer Register:   WDTCR      WDTCSR


*/


void wdtEnable(void)
{
    wdt_reset();
    cli();
    MCUSR = 0x00;
    WDTCR |= _BV(WDCE) | _BV(WDE);    
    WDTCR = _BV(WDIE) | _BV(WDP2); // 250ms
    sei();
}

//disable the wdt
void wdtDisable(void)
{
    wdt_reset();
    cli();
    MCUSR = 0x00;
    WDTCR |= _BV(WDCE) | _BV(WDE);
    WDTCR = 0x00;
    sei();
}

void system_sleep() 
{
		ACSR |= _BV(ACD); //disable the analog comparator
        ADCSRA &= ~_BV(ADEN); //disable ADC
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_enable();
        wdtEnable(); //start the WDT 
        //turn off the brown-out detector.
        //must have an ATtiny45 or ATtiny85 rev C or later for software to be able to disable the BOD.
        //current while sleeping will be <0.5uA if BOD is disabled, <25uA if not.
        cli();
        mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
        mcucr2 = mcucr1 & ~_BV(BODSE);
        MCUCR = mcucr1;
        MCUCR = mcucr2;
        sei(); //ensure interrupts enabled so we can wake up again
        sleep_cpu(); //go to sleep
                                       //----zzzz----zzzz----zzzz----zzzz
        cli(); //wake up here, disable interrupts        
        sleep_disable();
        wdtDisable(); //don't need the watchdog while we're awake
        sei(); //enable interrupts again (but INT0 is disabled above)
}


// insert the ISR function outside the main() loop, all the way at the bottom of the code
ISR(WDT_vect) {} //don't need to do anything here when the WDT wakes the MCU


Step 3 - insert code to configure the sleep/watchdog registers once at startup

Location: at the top of the main() block

wdt_reset();
wdtDisable();


Step 4 - add your sleep command at the idle position(s)

Location: normaly where you would have a delay at the end of a loop in main()

system_sleep();		


Step 5 - disable BOD in the command to flash the code to the micro or in software (not all models supported!)

-U hfuse:w:0xDF:m