将ATMega328与内部振荡器一起使用?


18

我有一个我认为最适合ATMega328P的项目。但是,在我见过的每个简单项目中,人们总是连接一个16MHz的外部振荡器。从我所看到的,它应该有一个8MHz的内部振荡器。我的项目不需要大量的处理能力,时序也不需要非常精确(除了UART和I2C)。我也有一个程序员,所以我不必担心引导程序。

我是否有理由使用外部振荡器?

Answers:


20

您无需说的是该内部振荡器的精度。我花了一些时间在第369页的数据表中找到它。

10%。百分之十!那是经过校准的振荡器?太厉害了 期望为此误差低至1%并非没有道理。Microchip / Atmel提供了一个文档,可以自己将振荡器校准为1%的精度。

I2C是一种同步协议,只要遵守最小和最大脉冲时间,时序精度就无关紧要。另一方面,
UART异步的,,因此计时精度确实很重要。大多数UART在最后一位(停止位)中允许出现半位错误,因此对于10位传输而言,这是5%。

出厂校准的振荡器在这里不起作用。您必须执行校准过程才能达到1%。在这种情况下,您可以使用内部振荡器。否则,您将不得不使用水晶。


1
让我加强在这里所说的话。节省您的时间和头痛,让它变得晶莹剔透。如果需要考虑功耗,请使用32khz的晶振(48/88/168为6PF ...不确定328。请检查迁移表)在启动时调谐内部振荡器。振荡器的校准程序非常挑剔,因此请谨慎行事。我已经在另一个答案下面发布了一些示例代码。
bathMarm0t

6

当您使用UART时,建议使用晶体振荡器。如果不是那样,您可以使用内部振荡器。一些MCU具有工厂调整的内部振荡器,可以适合于UART操作。


2
另请参见有关UART时序的此应用笔记:maxim-ic.com/app-notes/index.mvp/id/2141
drxzcl 2012年

那么,UART只与一个超级简单的串行显示,在9600运行...我想我会最终订货振荡器和一切,但看它是否会没有它的工作沟通
Earlz

3

“对时间不敏感”。UART对时间非常敏感。如果未正确同步,您将获得完整的垃圾。

选项1:使用普通晶体。适当更改时钟选择保险丝。晶体的选择取决于您要使用的波特率/您希望此设备运行的速度。有“魔术晶体”,对于标准费率(如果它们制造完美),将为您提供0%的误差。有关更多信息,请参见第20节[USART0]中的表(您已经阅读了数据手册。

在此处输入图片说明

选项2:如果担心功耗,则可以使用32khz晶体校准内部振荡器。使用32khz,您可以在睡眠模式下获得uA电流(我已将其降低至〜2uA)。您必须设置一个校准例程,尽管该例程涉及启动/停止计时器以及将timer2更改为异步模式。

328P代码可能有所不同...该功能当前适用于48/88(具有适当的F_CPU / baud定义。这有点丑陋/未完全重构,但我学到的东西比搞定在您工作时可以解决的事情要好得多。在AVRFreaks论坛上搜索“ tune 32khz crystal”之类的东西,这只是您将要进入的口味……不一定会起作用。

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;
}

2

还应注意,晶体需要很长时间才能启动。这实际上是因为它的精度:它仅从非常窄的频带中获取能量。这可能是电池供电的负担,在这种情况下,您有时会在很短的时间内唤醒单片机,然后:等待满功率消耗ms来启动晶振是净损失。陶瓷谐振器比内部RC振荡器更精确,但比晶体精度低,因此可以启动。

当然,一个16MHz的atmega比8MHz的果汁要喝更多的果汁,并且需要更高的电压,但是可以使用8MHz(或更低,低至32kHz)的晶体。这种单纯的选择也可以节省能源。


0

如果您不需要太多或精确的时序,则无需外部振荡器。在拆卸一些旧打印机时,我碰巧看到板上有很多IC,但没有一个振荡器。


0

我想您已经看过此应用笔记: AVR053:内部RC振荡器的校准

我想从中,以及上面@drxzcl的评论中的应用笔记,您应该能够从理论上确定什么是正确的。


如果已经有了一个可以接受的答案,则应该尝试多说点什么,否则它不是很有用
clabacchio

@clabacchio-这个答案已经两天了,接受的日期是昨天。发布时无法接受。
stevenvh 2012年

@stevenvh对,我没有意识到这只是一个编辑;但是,答案
并不

@clabacchio-“答案不完整”。同意!我找不到“您应该能够决定”的帮助。
stevenvh 2012年

@clabacchio-嘿,我现在也说“ 2天前”。一分钟前,它说“昨天答复”。一定是48小时!:-)
stevenvh 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.