В некоторых приложениях для измерения электрических характеристик требуется определение частоты с высокой точностью. Это можно сделать с помощью простого микроконтроллера без использования дорогостоящего оборудования.
Представленный код реализует счетчик частоты на микроконтроллере atmega328p.
/*
timer1 представляет собой расширенный 32-разрядный регистр (16 бит аппаратн. + 16 бит программн.).
Далее fcpu – частота процессора, prescal – значение предделителя
Инкрементирование происходит один раз за:
1 / (fcpu / prescal) <=> prescal / fcpu
Таким образом, переполнение произойдет при:
2^16 * prescal / fcpu
При переполнении tim2 вызывается обработчик прерываний и сохраняет текущее значение tim1 в tim1_cur_counter.
Таким образом, значение tim1, проинтегрированное за весь период tim1
(tim1_ovf_counter * 2^16) + tim1_cur_counter.
timer1 представляет собой 8-разрядный счетчик, инкрементируемый передним фронтом входного сигнала.
*/
static volatile uint8_t tim2_ovf_counter;
static volatile uint8_t tim2_is_ovf;
static volatile uint16_t tim1_cur_counter;
ISR(TIMER2_OVF_vect)
{
if ((tim2_ovf_counter--) == 0)
{
/* отключаем tim1 перед считыванием */
TCCR1B = 0;
tim1_cur_counter = TCNT1;
/* отключаем tim2 */
TCCR2B = 0;
tim2_is_ovf = 1;
}
}
static volatile uint8_t tim1_ovf_counter;
ISR(TIMER1_OVF_vect)
{
++tim1_ovf_counter;
}
static void hfc_start(void)
{
/* разрешение: 1.907349 Гц на итакт */
/* Максимальная частота fmax: 500 КГц */
/* время определения: 0.524288 секунд */
/* отключаем прерывания */
TIMSK1 = 0;
TIMSK2 = 0;
/* обнуляем регистры */
tim1_ovf_counter = 0;
tim1_cur_counter = 0;
tim2_is_ovf = 0;
/* 0x100 переполнений заполнят 16 бит */
tim2_ovf_counter = 0xff;
/* настройка tim2
нормальный режим работы
предделитель 128
разрешаем прерывания по переполнению
*/
TCNT2 = 0;
TIMSK2 = 1 << 0;
TCCR2A = 0;
TCCR2B = 0;
/* настройка tim1
вывод t1 (pd5) в качестве источника внешнего сигнала (передний фронт)
*/
DDRD &= ~(1 << 5);
TCNT1 = 0;
TIMSK1 = 1 << 0;
TCCR1A = 0;
TCCR1B = 0;
/* запуск tim1, tim2 */
TCCR1B = 7 << 0;
TCCR2B = 5 << 0;
}
static uint8_t hfc_poll(void)
{
return tim2_is_ovf;
}
static uint32_t hfc_wait(void)
{
/*
Ожидаем переполнения tim1. Возвращаем результирующее значение
16-битного счетчика, которое должно быть умножено на разрешение
по частоте для того, чтобы получить действительное значение частоты.
*/
while (tim2_is_ovf == 0) ;
return ((uint32_t)tim1_ovf_counter << 16) | (uint32_t)tim1_cur_counter;
}
static inline uint32_t hfc_start_wait(void)
{
hfc_start();
return hfc_wait();
}
static inline double hfc_to_hz(uint32_t counter)
{
return 1.907349 * (double)counter;
}