I personally think the designers of the chip did a fantastic job and their efforts should not be overlooked!
An additional cool feature of the Atmega chips is that you can use the 32768 Hz crystal to calibrate the 8 MHz internal oscillator, for much more accurate timing on RS232 transmissions, etc.
If anyone is interested, and Just for completeness and future reference, here is the code for a wind gauge remote sensor, which counts pulses, keeps time with an RTC and outputs RPM data on the uart. This was done using Atmel Studio IV, so is not directly Arduino compatible.
// wind gauge controller, ATmega168
// This code incorporates an RTC formed by Timer2 and a 32768 xtal on OSC pins
// The RC oscillator is calibrated by comparison with the 32768 standard,
// so that RS232 baud rates are more accurate.
// Dorkboard platform!
// choose fuses for 8 MHz internal clock, LP xtal (32768 Hz) on OSC pins
// sjames dot remington @ gmail.com
// Using Atmel Studio IV on this project
#include <avr/io.h>
//8 MHz internal RC , internal divider set to 8 programmatically
#define F_CPU 1000000UL
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/io.h>
// Global variables for RTC
volatile unsigned int ticks = 0; //global tick counter, increments @ 128.00 Hz
volatile unsigned char RTC_buffer[]={'0','0','0','0','0','0',0}; //daytime, with zero terminator
volatile unsigned int dayno=0; //day number since startup
/*
** long, approximate delays
*/
void delay1ms(unsigned int ms)
{
while (ms--) _delay_ms(1);
}
void OSCCAL_calibration(void);
#include "uart.c"
/*
// initialize Timer2 as asynchronous 32768 Hz timing source
*/
void timer_init(void) {
TCCR0B = 0; //stop Timer 0
TCCR2B = 0; //stop Timer 2
TIMSK2 = 0; // disable Timer 2 interrupts
ASSR = (1<<AS2); // select asynchronous operation of Timer2
TCNT2 = 0; // clear Timer 2 counter
TCCR2A = 0; //normal count up mode, no port output
TCCR2B = (1<<CS20); // select precaler = 1 : 32768 Hz / 256 => 7.8125 msec/tick
// TCCR2B = (1<<CS22) | (1<<CS20); // select precaler: 128 => 1 sec between each overflow
while (ASSR & ((1<<TCN2UB)|(1<<TCR2BUB))); // wait for TCN2UB and TCR2BUB to be cleared
TIFR2 = (1<<TOV2); // clear interrupt-flag
TIMSK2 = (1<<TOIE2); // enable Timer2 overflow interrupt
/* initialize timer 0 for 4kHz square wave PWM on PD6
TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00); //OC0A (PD6/D6), set at BOTTOM, clear on Compare Match, Mode =3 FAST PWM
TCCR0B = (1<<CS01); // Start Timer 0, no prescaling, 1MHz/256 = ~ 4 kHz
OCR0A = 127; //square wave, 50% duty cycle
*/
// alternative to 4kHz output above: 15 Hz output
// TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00); // OC0A (PD6) and OC0B (PD5)
// TCCR0B = (1<<CS02); // /256 = 15 Hz = 20 mph calibration output!
}
// set up I/O for printf function
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
/*
** MAIN
*/
int main(void)
{
unsigned int period,rpm,maxrpm,nsamples;
unsigned long average;
DDRB = (1<<PB5); //output LED Arduino Pin 13
DDRD = (1<<PD1)|(1<<PD6); //output on Arduino Pin 6, UART TX
DDRC = (1<<PC1); //all PORTC input except PC1
PORTC = (1<<PC1); //power for the wind gauge
PORTB |= (1<<PB5); //OFF on Dorkboard, ON on Arduino
OSCCAL_calibration();
// reprogram timers:
timer_init();
stdout = &uart_str;
uart_init(4800); //0.2% accuracy at 1 MHz clock speed
delay1ms(3000);
printf("Anemom_1.0 ,%u,%s\r",dayno,RTC_buffer);
sei(); //enable interrupts (Timer 2)
nsamples = 0;
average = 0;
char ten_seconds=RTC_buffer[4]; //starting value of ten seconds counter
//first pass: synchronizes edges of wind gauge output with ~3 second timeout
ticks=0;
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white
while(1){
//count timer ticks for 1 revolution (2 hi/low cycles on input)
ticks=0;
PORTB |= (1<<PB5); //off //now time one full revolution
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
PORTB &= ~(1<<PB5); //on
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white
PORTB |= (1<<PB5); //off
while( ((PINC&(1<<PC0)) ==0) && (ticks<384)); //seeing white, wait till black
PORTB &= ~(1<<PB5); //on
while( ((PINC&(1<<PC0)) !=0) && (ticks<384)); //seeing black, wait till white
PORTB |= (1<<PB5); //off
period = ticks;
if (period >= 384) period = 0; //stopped
// this version for the 12/2013 modified Weathertronics anemometer, 2 low/high per revolution
// 1 rpm = 60*128 ticks = 7680
if(period) rpm = 7860UL/period; //convert to rpm (60 mph ~ 1400 rpm, so this should be safe)
else rpm = 0;
if (rpm > maxrpm) maxrpm = rpm;
average = average + (unsigned long) rpm;
nsamples++;
if(RTC_buffer[4] != ten_seconds) {
ten_seconds = RTC_buffer[4];
if (nsamples > 0) {
average=average/nsamples;
}
// output data on uart line
printf("%u,%s,%u,%u\r",dayno,RTC_buffer,(unsigned int) average,maxrpm);
//after manual calibration, conversion factor is mph = rpm*0.044275 or rpm/22.586
nsamples = 0;
average = 0;
maxrpm = 0;
}
}
}
//******************************************************************
// Timer2 Interrupt Service
// 32 kKz / 256 = 128 Hz (7.8125 ms/tick)
// provides global tick timer and RTC
//ISR (TIMER2_COMPA_vect) {
ISR (TIMER2_OVF_vect) {
static uint8_t counter=128;
uint8_t i;
char t[6];
ticks++;
counter--;
if(!counter) { //1 second has passed
counter=128;
// RTC function
for (i=0;i<6;i++) t[i]=RTC_buffer[i]; //make a copy, so don't change digit more than once on rollover
t[5]++; // increment second
if (t[5] > '9')
{
t[5]='0'; // increment ten seconds
t[4]++;
if ( t[4] > '5')
{
t[4]='0';
t[3]++; // increment minutes
if (t[3] > '9')
{
t[3]='0';
t[2]++; // increment ten minutes
if (t[2] > '5')
{
t[2]='0';
t[1]++; // increment hours
char b = t[0]; // tens of hours, handle rollover at 19 or 23
if ( ((b < '2') && (t[1] > '9')) || ((b=='2') && (t[1] > '3')) )
{
t[1]='0';
t[0]++; // increment ten hours and day number, if midnight rollover
if (t[0] > '2') {t[0]='0'; dayno++;}
}
}
}
}
}
for (i=0;i<6;i++) RTC_buffer[i]=t[i]; //copy updated time
}
}
//
// Calibrate the internal OSCCAL byte, using the external
// 32768 Hz crystal as reference. Sets system clock to 1 MHz
//
void OSCCAL_calibration(void) //This version specific to ATmegaXX8 (tested with ATmega168)
{
unsigned char calibrate = 0; //FALSE;
int temp;
unsigned char tempL;
CLKPR = (1<<CLKPCE); // set Clock Prescaler Change Enable
// set prescaler = 8, Internal RC 8Mhz / 8 = 1Mhz
CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
TIMSK2 = 0; //disable OCIE2A and TOIE2
ASSR = (1<<AS2); //select asynchronous operation of timer2 (32,768kHz)
OCR2A = 200; // set timer2 compare value
TIMSK0 = 0; // disable all interrupt sources
TCCR1B = (1<<CS10); // start timer1 with no prescaling
TCCR2B = (1<<CS20); // start timer2 with no prescaling (ATmega169 is TCCR2A!)
while ((ASSR & 0x01) | (ASSR & 0x04)); //wait for TCN2UB and TCR2BUB to be cleared
// wait for external crystal to stabilise
delay1ms(2000);
while(!calibrate)
{
cli(); // mt __disable_interrupt(); // disable global interrupt
TIFR1 = 0xFF; // delete TIFR1 flags
TIFR2 = 0xFF; // delete TIFR2 flags
TCNT1H = 0; // clear timer1 counter
TCNT1L = 0;
TCNT2 = 0; // clear timer2 counter
while ( !(TIFR2 && (1<<OCF2A)) ); // wait for timer2 compareflag
TCCR1B = 0; // stop timer1
sei(); // __enable_interrupt(); // enable global interrupt
if ( (TIFR1 && (1<<TOV1)) )
{
temp = 0xFFFF; // if timer1 overflows, set the temp to 0xFFFF
}
else
{ // read out the timer1 counter value
tempL = TCNT1L;
temp = TCNT1H;
temp = (temp << 8);
temp += tempL;
}
if (temp > 6250)
{
OSCCAL--; // the internRC oscillator runs too fast, decrease the OSCCAL
}
else if (temp < 6120)
{
OSCCAL++; // the internRC oscillator runs too slow, increase the OSCCAL
}
else
calibrate = 1; //TRUE; // the internal RC is correct
TCCR1B = (1<<CS10); // start timer1
}
}