Using the ATMega328 with the internal oscillator?

What you don't say is what the accuracy of this internal oscillator is. It took me some time to find it in the datasheet, on page 369.

10%. Ten percent! And that for a calibrated oscillator? This is awful. It isn't unreasonable to expect error as low as 1% for this. Microchip/Atmel provides a document for calibrating the oscillator yourself to 1% accuracy.

I2C is a synchronous protocol, and timing accuracy isn't relevant as long as minimum and maximum pulse times are respected.
UART on the other hand is asynchronous, and then timing accuracy is important indeed. Most UARTs allow a half bit error in the last bit (the stop bit), so that's 5% for a 10 bit transmission.

The factory calibrated oscillator won't do here. You'll have to go through the calibration procedure to get to 1%. In that case you can use the internal oscillator. Otherwize you'll have to use a crystal.


As you are using a UART, a crystal oscillator would be advisable. If it wasn't for that, you could use the internal oscillator. Some MCUs have factory-trimmed internal oscillators, which can be suitable for UART operation.


"Not time sensitive". UART is very time sensitive. You'll get complete garbage if it's not synced appropriately.

Option 1: Use a normal crystal. Change clock select fuse appropriately. Crystal selection depends on what baud you want to use / how fast you want this thing to go. There's "magic crystals" that will give you 0% error for standard rates (if they're manufactured perfectly). See the tables in Section 20 [USART0] for more info (you have read the datasheet.... right???) :).

enter image description here

Option 2: You can calibrate the internal oscillator using a 32khz crystal if power is a concern. With 32khz you can get uA currents in sleep mode (I've gotten them down to ~2uA). You have to setup a calibration routine though which involves starting / stopping timers and alternating timer2 to asynchronous mode.

The 328P code may differ... this function currently works on 48/88 (with appropriate F_CPU/baud definitions. It's a little ugly / not refactored completely but I've learned better than to screw around with things that work when you're on a deadline. Search on the AVRFreaks forum for "tune 32khz crystal" something like that. This is just a taste for what you'll be getting into... Not necessarily what's going to work.

char OSCCAL_calibration(char starting_cal, int cal_value){
//Function calibrates the internal oscillator so usart comms go through.
//Works by continually checking two different timers:
//   (0 -> tied to internal, and 2 -> async to crystal).
//  Recommended cal_value = 5900 for the crystals we're using.
//  Must be running 8MHZ with clkdiv8 fuse enabled.
//  TODO: Make sure to check all the math on this later.
unsigned char calibrate = FALSE;
int temp;
unsigned char tempL;
volatile char osccal_temp=starting_cal;
int cal_bandwidth = 50;

//int cal_value = 6250;
//int cal_value = 5900; //Works.  Need to find out why.

//Dont use clock prescalers.  We're already div8ing.
//CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable
// set prescaler = 8, Inter 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)

OCR2B = 200;            // set timer2 compare value.  We probably only need to compare A
OCR2A = 200;

TIMSK0 = 0;             // delete any interrupt sources

TCCR2A = (1<<WGM21);    //Normal operation.  Reset timer on hitting TOP (ocr2a).
TCCR2B = (1<<CS20);     // start timer2 with no prescaling

TCCR1B = (1<<CS10);     // start timer1 with no prescaling

//wait for everythnig to mellow out.
while((ASSR & (1<<TCN2UB)) | (ASSR & (1<<OCR2BUB)) | (ASSR & (1<<TCR2BUB)) | (ASSR & (1<<OCR2AUB)) | (ASSR & (TCR2AUB)));       //wait for TCN2UB and TCR2UB to be cleared

//This is specifically for the crystal to stabilize.  Check for better times.
_delay_ms(1000);

while(!calibrate){
    cli();  // 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

    //Stop timer on compare match.
    while ( !(TIFR2 & (1<<OCF2A)) );
    TCCR1B = 0;

    //Check for overflows (useless if it happens).
    sei();
    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;
        }

    //Check timer value against calculated value.           
    if (temp > (cal_value+(cal_bandwidth/2))){
        //Oscillator is too fast.
        osccal_temp--;
        OSCCAL=osccal_temp;
    }else if (temp < (cal_value-(cal_bandwidth/2))){
        //Oscillator is too slow.
        osccal_temp++;
        OSCCAL=osccal_temp;
    }else{
        //Just right.
        calibrate = TRUE;
        }

    TCCR1B = (1<<CS10); // start timer1
    }

//TODO: Stop timers, ya?
//Now setup timer2 to run "normally" aka async+interrupts.
//Disable interrupt source. Set mask.  Wait for registers to clear.
TIFR2 = (1<<TOV2);
TIMSK2 = (1<<TOIE2);
ASSR = (1<<AS2);        //select asynchronous operation of timer2 (32,768kHz)
TIMSK0 = 0;             // delete any interrupt sources

//Normal Op. 256 prescale.
TCCR2A = 0x00;
TCCR2B = (1<<CS22) | (1<<CS21);

TCCR1B = 0x00;     // turn off timer1

//wait for everythnig to mellow out.
while((ASSR & (1<<TCN2UB)) | (ASSR & (1<<OCR2BUB)) | (ASSR & (1<<TCR2BUB)) | (ASSR & (1<<OCR2AUB)) | (ASSR & (TCR2AUB)));       //wait for TCN2UB and TCR2UB to be cleared

//This is specifically for the crystal to stabilize.  Check for better times.
_delay_ms(1000);
return osccal_temp;
}