 |
| 昵称:tamson |
| 级别: |
| 贡献值:33832 |
| 经验值:13390 |
给他留言 |
| 来自:广东-深圳 |
|
校准的原理我先说一下: 使用两个定时器,timer1和timer2,使用timer1的原因是因为他是16位的定时器,可以计数很大,不至于很快就溢出,timer2是用来对外部32K晶体周期计数的,timer1是对内部RC谐振进行计数,等timer2溢出的时候,比较timer1的值和理论值的差别,对校准寄存器进行修改。可以多校准几次,寻找最小的那个差。代码如下: 有几个宏先给出值 #define MAX_CAL_LOOP_CNT (100) #define TARGETVAL_CALIBRATION (62500) 至于为什么TARGETVAL_CALIBRATION值为62500,给出说明,timer2计周期的个数为255个,那当32.768计数到255时,理论上使用内部RC计数的值应该为 8000000 * 256 / 32768 = 62500 bool pal_calibrate_rc_osc(void) { /* * Use the 32.768 kHz external crystal oscillator as reference clock source. */ /* * This is the actual value of the timer to be calibrated * after each calibration run. */ uint16_t curr_value_cal_timer = 0; /* This is the best OSCCAL value. */ uint8_t best_value_osccal = 0; /* * This is the smallest difference between the target value and the actual value * of the timer the timer to be calibrated. */ uint16_t best_dif_timers = ~(0); /* * This is the difference between between the target value and the actual value * of the timer the timer to be calibrated of the current calibration attempt. */ uint16_t curr_dif_timers = 0; uint16_t counter; uint8_t tccr2b; uint8_t tccr1b; uint8_t tccr1a; uint16_t seed = 0; bool cal_result = true; ENTER_CRITICAL_REGION(); /* * Save current values of timer status. */ tccr2b = TCCR2B; tccr1b = TCCR1B; tccr1a = TCCR1A; /* * Stop timers 1 and 2. * Set timer 1 to normal mode (no CTC, no PWM, just count). */ TCCR2B = 0; TCCR1B = 0; TCCR1A = 0; for (counter = 0; counter < MAX_CAL_LOOP_CNT; counter++) { /* * Delete pending timer 1 and 2 interrupts, and clear the * counters. */ TIFR1 = 0xFF; TIFR2 = 0xFF; TCNT2 = 0; TCNT1 = 0; /* Kick timer1 with internal clock source and no prescaler */ TCCR1B = (1 << CS10); /* * Kick timer2 with external 32.768 Hz asynchronous clock * and no prescaler */ TCCR2B = (1 << CS20); ASSR = (1 << AS2); /* * Wait for timer 2 to overflow. */ while (!(TIFR2 & (1 << TOV2))) { /* Wait */ } /* * Stop timer 1. Now, TCNT1 contains the number of CPU cycles * counted during F_CPU / (32 * 256) cycles. */ TCCR1B = 0; TCCR2B = 0; curr_value_cal_timer = TCNT1; seed += curr_value_cal_timer; /* * Check if reference timer is running at all, * i.e. the reference clock is available. */ if (0 == curr_value_cal_timer) { /* Reference timer is not running, return error. */ cal_result = false; break; } if (curr_value_cal_timer <= (uint16_t)(TARGETVAL_CALIBRATION)) { curr_dif_timers = (uint16_t)(TARGETVAL_CALIBRATION) - curr_value_cal_timer; } else { curr_dif_timers = curr_value_cal_timer - (uint16_t)(TARGETVAL_CALIBRATION); } if (curr_dif_timers < best_dif_timers) { best_dif_timers = curr_dif_timers; best_value_osccal = OSCCAL; } if (curr_value_cal_timer <= (uint16_t)(TARGETVAL_CALIBRATION)) { /* Too slow, increase speed */ OSCCAL++; } else { /* Too fast, lower speed */ OSCCAL--; } } /* * Set the seed for the random number generator based on the * calibration results. */ srand(seed); TCCR2B = tccr2b; TCCR1B = tccr1b; TCCR1A = tccr1a; LEAVE_CRITICAL_REGION(); OSCCAL = best_value_osccal; return (cal_result); } 由于随着时间的推移,内部RC又会不准了,所以要定时去校准,才能保证内部定时器准确。这样32.768K的晶体就可以作为在省电模式下定时器2的外部时钟。定时器2的设置给出: TIFR2 = _BV(OCF2A); TIFR2 = _BV(OCF2B); TIFR2 = _BV(TOV2); /* Enable the timer overflow interrupt */ TIMSK2 |= _BV(TOIE2); ASSR = _BV(AS2); //set async mode TCCR2B = 0x00; TCNT2 = 0x61; //set count OCR2A = 0x9F; TCCR2B = 0x07; //启动定时器,定时器的时间定为5s,这个时间根据自己的情况在修改 |