I recently wanted to learn to program AVR MCU's using the data sheet directly and not use any Arduino code. I purchases the following book which has been excellent
http://www.amazon.com/Make-Programming-Learning-Software-Hardware/dp/1449355781/ref=sr_1_5?ie=UTF8&qid=1440151544&sr=8-5&keywords=avr. This book takes you through the process of setting up the gcc toolchain and your c Makefile manually on Windows, Linux, and Mac. It takes your through the atmegaxx8p hardware one peripheral at a time and shows you how to manipulate registers directly.
Oh and did I mention you can use notepad/VI/Emacs/textedit/.... plain old text editors to write your c code and run make install from the command line to read your make file, compile and link the code and upload to the Arduino using an ICSP programmer
Some of the benefits to this are:
- More efficient code (setting a pin output high takes 40 cycles in Arduino vs 1 cycle setting the register directly)
- The ability to read and set multiple pins at once
- More readable code once you get used to the register names / shorter code
- The ability to configure counters/timers how you want and not how Arduino wants
- Better ability to control the peripherals directly such as (using interrupts for ADC results, doing hardware serial/SPI/I2c (reduced code and cycles * for example: lots of Arduino code especially Adafruit modules states they don't support hardware serial yet*
- More complete understanding of the code, and improved programming skill as everything is a matter of bit shifting or masking
And finally I will leave you with blink code for an attiny85 that uses the watchdog timer to blink an led every second and sleep %99 of the time. this chip uses microamps of power and has a battery life of years compared to most Arduino projects. And you can look up what is actually happening by reading the data sheet as all the registers are defined there.
Also note most boot loaders such as the one used on the Adafruit Gemma/Trinket and other Arduino boot loaders do not support the use of this watchdog timer.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
volatile int f_wdt=1;
ISR(WDT_vect)
{
if (f_wdt == 0)
{
f_wdt=1;
}
}
void enterSleep(void)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
sleep_disable();
power_all_enable();
}
int main(void)
{
//Disable interrupts
cli();
// Configure ports 0 & 1 for output
DDRB = ((1<< DDB1) | (1<< DDB0));
// Configure initial state of pin 1 to HIGH
PORTB= 1 << DDB1;
// Set Watchdog Reset flag to 0
MCUSR &= ~(1<< WDRF);
WDTCR = 0x00;
// set watchdog enable and change enable bits to HIGH
WDTCR |= (1<< WDCE) | (1<< WDE);
// Enable Watchdog timer interrupt
WDTCR |= 1<< WDIE;
// set watchdog prescaler values to 1.0 seconds
WDTCR |= (1<< WDP2) | (1<< WDP1);
// Enable Interrupts
sei();
for(;;) {
// put your main code here, to run repeatedly:
if (f_wdt==1)
{
PORTB ^= ( (1 << PORTB1) | (1 << PORTB0));
f_wdt = 0;
enterSleep();
}
}
return 0;
}