ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Суббота
21 декабря
1350185 Топик полностью
Гyдвин, волшебник (12.09.2023 11:36, просмотров: 209) ответил Adept на мне вот реально интересно как оптимальным способом сделать "онлайн" подстройку скорости UART, по часовому кварцу, если МК на внутреннем RC
Вот рабочий код калибровки 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
}