Понадобилось мне откалибровать встроенный RC-генератор ATtiny13a.
Просмотрев AVR053, и исходники из iNET, пошел своим путем.
Как это работает: запускаем таймер 0 на частоте контроллера без предделителя, и считаем кол-во его отсчетов за определенное кол-во периодов эталонной частоты. Делаем это с разными перебираемыми значениями OSCCAL. Выбираем то значение OSCCAL, при котором разница между этими измеренными и теоретически расчитанным будет минимальной.
По окончании работы алгоритма, лучшее значение OSCCAL записывается в 0 ячейку EEPROM:
eeprom_write_byte((uint8_t*) 0, best_osc);
Далее это значение можно прочитать через
- make readeeprom
или в начало своего кода вставить
uint8_t new_osc = eeprom_read_byte((uint8_t*)0);
if(new_osc <= 0x7F)
OSCCAL = new_osc;
Естественно, FUSE EESAVE должен быть запрограммирован.
Так-же, для контроля, по окончании работы алгоритма, на выходе OCR0A генерируется меандр с частотой F_CPU/2.
За эталонную частоту принял выход 32 кГц китайского модуля DS3231, т.к. он у меня есть, и для его работы достаточно лишь подать питание на этот модуль.
В исходном тексте это выглядит так:
- тактовая частота контроллера, как и тип контроллера, определены в makefile
MCU = attiny13a
F_CPU = 9600000
- эталонная частота
#define CCLOCK 32768
- куда подавать эталонную частоту
#define FREQ_DDR DDRB
#define FREQ_PORT PORTB
#define FREQ_PIN PINB
#define FREQ_BIT 2
- кол-во периодов эталонной частоты для подсчета
#define CCYCLES 40
- теоретически расчитанное кол-во тиков (оно не должно превышать 65535, т.к. используетя 2 байта)
#define CTARGET ((F_CPU*CCYCLES+CCLOCK/2)/CCLOCK)
- искать OSCCAL будем в окрестностях его значений 1%(~15 сек):
#define CDEVIATION ((CTARGET + 50) / 100) // 1%
- если в 1% нигде не попали, тогда программа уйдет в бесконечный цикл
while(new_osc > MAX_OSCCAL); // error
- тогда можно увеличить погрешность поиска
- при удачном исходе, на пин OCR0A выдается меандр F_CPU/2 для контроля.
Функция int16_t Deviation(uint8_t osccal) возвращает разницу между измеренным и теор значениями отсчетов. После записи каждого нового значения OSCCAL делается пауза 100 мсек для устаканивания генератора.
Функция uint8_t BestDevNearest(uint8_t nearosc) выбирает наилучшее значение из ближайших -+7 значений OSCCAL.
Не знаю, кому это может понадобиться.
Собирается через make в avr-gcc.


