Вот рабочий код калибровки 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)