I have a project that I think would be best suited for an ATMega328P. However, in every simple project I've seen, people always hook up a 16MHz external oscillator. From what I can see, it should have an 8MHz internal oscillator. My project doesn't require a lot of processing power, nor does timing need to be very precise(other than for a UART and I2C). I have a programmer also, so I don't need to worry about bootloaders.

Is there any reason for me to use an external 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.

  • 2
    \$\begingroup\$ Let me reinforce what's being said here. Save yourself the time and headache and just get a crystal. If power is a concern, use a 32khz watch crystal (6PF for 48/88/168... not sure about the 328. check the migration sheet) to tune the internal oscillator on startup. The oscillator calibration routine is very finicky, so take care if you go that route. I've posted some example code for it below another answer. \$\endgroup\$ – bathMarm0t Dec 3 '15 at 7:54

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.

  • 2
    \$\begingroup\$ See also this app note on UART timing: maxim-ic.com/app-notes/index.mvp/id/2141 \$\endgroup\$ – drxzcl Mar 9 '12 at 8:30
  • \$\begingroup\$ Well, the UART is only for communication with a super simple serial display that runs at 9600bps... I think I'll end up ordering the oscillator and everything but seeing if it will work without it \$\endgroup\$ – Earlz Mar 12 '12 at 21:47

"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.

    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).
    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.
    }else if (temp < (cal_value-(cal_bandwidth/2))){
        //Oscillator is too slow.
        //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.
return osccal_temp;

It should also be noted that a crystal takes a long time to start. That is actually because of its precision : it only takes energy from a very narrow frequency band. This can be a burden for battery operated stuff where you wake up the mcu for a very short time every now and then : waiting for a ms at full power draw for the crystal to start is a net loss. Ceramic resonators are more accurate than internal RC oscillator but less than a crystal, and start accordingly.

Of course a 16MHz atmega drinks a lot more juice and needs higher voltage than a 8MHz one, but 8MHz (or lower, down to 32kHz) crystals are available; this mere choice can also be an energy saver.


If you don't need much or precise timing, there's no need for an external oscillator. While disassembling an some old printers, I happen to see a lot of IC's but not a single oscillator on board.


I guess you have already seen this app note: AVR053: Calibration of the internal RC oscillator.

I guess from it, and the app note from the comment of @drxzcl above, you should be able to decide theoretically what is right.

  • \$\begingroup\$ Where there is already an accepted answer, you should try to say something more, otherwise it's not very useful \$\endgroup\$ – clabacchio May 24 '12 at 11:37
  • \$\begingroup\$ @clabacchio - this answer is two days old, and the accepted dates from yesterday. It can't have been accepted when this was posted. \$\endgroup\$ – stevenvh May 25 '12 at 12:00
  • \$\begingroup\$ @stevenvh right, I didn't realize it was just an edit; though, it's an incomplete answer \$\endgroup\$ – clabacchio May 25 '12 at 12:07
  • \$\begingroup\$ @clabacchio - "incomplete answer". Agreed! I can't find "you should be able to decide" very helpful. \$\endgroup\$ – stevenvh May 25 '12 at 12:14
  • \$\begingroup\$ @clabacchio - Hey, mine also says "2 days ago" now. A minute ago it said "answered yesterday". Must have been exactly 48 hours! :-) \$\endgroup\$ – stevenvh May 25 '12 at 12:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.