使用带有内部振荡器的 ATMega328?

电器工程 avr 大气压 振荡器
2022-01-22 23:14:03

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

我有什么理由使用外部振荡器吗?

4个回答

你不说的是这个内部振荡器的精度是多少。我花了一些时间在第 369 页 的数据表中找到它。

10%。百分之十!那对于校准的振荡器呢?这太可怕了。期望 this 的误差低至1%并非不合理。Microchip/Atmel 提供了一份用于自行校准振荡器以达到 1% 精度的文档。

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

工厂校准的振荡器不会在这里做。您必须通过校准程序才能达到 1%。在这种情况下,您可以使用内部振荡器。否则你将不得不使用水晶。

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

“时间不敏感”。UART 对时间非常敏感。如果没有正确同步,您将得到完全的垃圾。

选项1:使用普通水晶。适当更改时钟选择保险丝。水晶的选择取决于你想使用什么波特/你想让这个东西走多快。有“魔法水晶”会给你标准费率的 0% 误差(如果它们制造得很好)。有关更多信息,请参见第 20 节 [USART0] 中的表格(您已阅读数据表……对???):)。

在此处输入图像描述

选项 2:如果担心功率问题,您可以使用 32khz 晶振校准内部振荡器。使用 32khz,您可以在睡眠模式下获得 uA 电流(我已将其降至 ~2uA)。您必须设置一个校准例程,其中涉及启动/停止计时器以及将 timer2 交替到异步模式。

328P 代码可能有所不同...此功能目前适用于 48/88(具有适当的 F_CPU/波特率定义。它有点难看/没有完全重构,但我学到了比在你工作时搞砸的东西更好在最后期限。在 AVRFreaks 论坛上搜索“调谐 32khz 水晶”之类的东西。这只是对你将要进入的内容的一种品味......不一定会起作用。

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

还应注意,晶体需要很长时间才能启动。这实际上是因为它的精度:它只从非常窄的频带中获取能量。对于电池供电的东西来说,这可能是一种负担,在这种情况下,您会时不时地在很短的时间内唤醒 MCU:在全功率消耗下等待一毫秒以使晶体启动是净损失。陶瓷谐振器比内部 RC 振荡器更准确,但不如晶体,并相应地启动。

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