Вот рабочий код калибровки RC генератора меги, который использую в
одной из железок, имеющей только часовой кварц. На скорости uart
38400 проблем не встречал.
#include <mega328p.h>
#define AS2 5
#define CS20 0
#define TCR2UB 0
#define OCR2UB 2
#define TCN2UB 4
#define ASYNC_TIMER AS2
#define NO_PRESCALING CS20
#define ASYNC_TIMER_CONTROL_REGISTER TCCR2B
#define ASYNC_TIMER_CONTROL_UPDATE_BUSY TCR2UB
#define OUTPUT_COMPARE_UPDATE_BUSY OCR2UB
#define TIMER_UPDATE_BUSY TCN2UB
#define TIMER TCNT2
#define OSCCAL_RESOLUTION 8
#define LOOP_CYCLES 7
/*! Modify CALIBRATION_FREQUENCY to desired calibration frequency
*/
#define CALIBRATION_FREQUENCY 4000000
/*! Frequency of the external oscillator. A 32kHz crystal is recommended
*/
#define XTAL_FREQUENCY 32768
#define EXTERNAL_TICKS 100 // ticks on XTAL. Modify to increase/decrease accuracy
/*! \brief Fixed calibration values and macros
*
* These values are fixed and used by all calibration methods. Not to be modified.
*
*/
#define RUNNING 0
#define FINISHED 1
#define DEFAULT_OSCCAL_MASK 0x00 // Lower half and
#define DEFAULT_OSCCAL_MASK_HIGH 0x80 // upper half for devices with splitted OSCCAL register
#define DEFAULT_OSCCAL_HIGH ((1 << (OSCCAL_RESOLUTION - 1)) | DEFAULT_OSCCAL_MASK_HIGH)
#define INITIAL_STEP (1 << (OSCCAL_RESOLUTION - 2))
#define DEFAULT_OSCCAL ((1 << (OSCCAL_RESOLUTION - 1)) | DEFAULT_OSCCAL_MASK)
// **** Functions implemented as macros to avoid function calls
#define PREPARE_CALIBRATION() \
calStep = INITIAL_STEP; \
calibration = RUNNING;
#define COMPUTE_COUNT_VALUE() \
countVal = ((EXTERNAL_TICKS*CALIBRATION_FREQUENCY)/(XTAL_FREQUENCY*LOOP_CYCLES));
// Set up timer to be ASYNCHRONOUS from the CPU clock with a second EXTERNAL 32,768kHz CRYSTAL driving it. No prescaling on asynchronous timer.
#define SETUP_ASYNC_TIMER() \
ASSR |= (1<<ASYNC_TIMER); \
ASYNC_TIMER_CONTROL_REGISTER = (1<<NO_PRESCALING);
// Absolute value macro.
#define ABS(var) (((var) < 0) ? -(var) : (var));
#define NOP() #asm("nop");
//! The binary search step size
unsigned char calStep;
//! The desired counter value
unsigned int countVal;
//! Calibration status
unsigned int calibration;
/*
* This function increments a counter for a given ammount of ticks on
* on the external watch crystal.
*
*/
unsigned int Counter(void){
unsigned int cnt;
cnt = 0; // Reset counter
TIMER = 0x00; // Reset async timer/counter
while (ASSR & ((1<<OUTPUT_COMPARE_UPDATE_BUSY)|(1<<TIMER_UPDATE_BUSY)|(1<<ASYNC_TIMER_CONTROL_UPDATE_BUSY))); // Wait until async timer is updated (Async Status reg. busy flags).
do{ // cnt++: Increment counter - the add immediate to word (ADIW) takes 2 cycles of code.
cnt++; // Devices with async TCNT in I/0 space use 1 cycle reading, 2 for devices with async TCNT in extended I/O space
} while (TIMER < EXTERNAL_TICKS); // CPI takes 1 cycle, BRCS takes 2 cycles, resulting in: 2+1(or 2)+1+2=6(or 7) CPU cycles
return cnt; // NB! Different compilers may give different CPU cycles!
}
/*! \brief Initializes the calibration.
*
* Computes the count value needed to compare the desired internal oscillator
* speed with the external watch crystal, and sets up the asynchronous timer.
*
*/
void CalibrationInit(void){
COMPUTE_COUNT_VALUE(); // Computes countVal for use in the calibration
OSCCAL = DEFAULT_OSCCAL;
NOP();
SETUP_ASYNC_TIMER(); // Asynchronous timer setup
}
void CalibrateInternalRc(void){
unsigned int count;
unsigned char cycles = 0x80;
do{
count = Counter();
if (count > countVal)
OSCCAL--; // If count is more than count value corresponding to the given frequency:
NOP(); // - decrease speed
if (count < countVal)
OSCCAL++;
NOP(); // If count is less: - increase speed
if (count == countVal)
cycles=1;
} while(--cycles); // Calibrate using 128(0x80) calibration cycles
}
void calibrate_rc(void)
{
CalibrationInit(); // Initiates calibration
PREPARE_CALIBRATION(); // Sets initial stepsize and sets calibration state to "running"
CalibrateInternalRc(); // Calibrates to selected frequency
}
-
- спасибо, глянул бегло, - крутим OSCCAL по результатам сравнения с асинхронным таймером. Интересно, насколько можно вытянуть температурные дрейфы и статистический разброс?? у RC "конские" уходы... И как на минимум 115200 будет? более 1-2% дрейф baudrate недопустим. Можно ещё BRCONST корректировать, но там чуть сложнее будет попадать в нужную точку характеристики, возможно. - Adept(12.09.2023 11:47)