high resolution frequency counter implementation
high resolution frequency counter implementation for atmega328p
part of my lcmeter project available here:
https://github.com/texane/lcmeter
/* high resolution frequency counter implementation
*/
/* timer2 interrupt handler. timer1 is an extended
32 bits register (16 bits hard + 16 softs)
incremented once per:
1 / (fcpu / prescal) <=> prescal / fcpu
thus, it will overflow at:
2^16 * prescal / fcpu
on tim2 overflow, the interrupt handler is called
and stores the tim1 current value in tim1_cur_counter.
thus, the tim1 value integrated over the whole
tim1 period is:
(tim1_ovf_counter * 2^16) + tim1_cur_counter.
tim2_is_ovf is set to notify the application.
*/
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)
{
/* disable tim1 before reading */
TCCR1B = 0;
tim1_cur_counter = TCNT1;
/* disable tim2 */
TCCR2B = 0;
tim2_is_ovf = 1;
}
}
/* timer2 interrupt handler. timer2 is a 8 bits counter
incremented by the input signal rising edges. since
8 bits are not enough to integrate, an auxiliary
register (tim2_ovf_counter) is updated on overflow.
tim2_ovf_counter is an 8 bits register, and will
overflow without any notice past 0xff.
*/
static volatile uint8_t tim1_ovf_counter;
ISR(TIMER1_OVF_vect)
{
++tim1_ovf_counter;
}
static void hfc_start(void)
{
/* resolution: 1.907349 hz per tick */
/* fmax: 500 khz */
/* acquisition time: 0.524288 seconds */
/* disable interrupts */
TIMSK1 = 0;
TIMSK2 = 0;
/* reset stuff */
tim1_ovf_counter = 0;
tim1_cur_counter = 0;
tim2_is_ovf = 0;
/* 0x100 overflows make 16 bits */
tim2_ovf_counter = 0xff;
/* configure tim2
normal operation
prescaler 128
enable interrupt on overflow
*/
TCNT2 = 0;
TIMSK2 = 1 << 0;
TCCR2A = 0;
TCCR2B = 0;
/* configure tim1
t1 pin (pd5) rising edge as external clock
*/
DDRD &= ~(1 << 5);
TCNT1 = 0;
TIMSK1 = 1 << 0;
TCCR1A = 0;
TCCR1B = 0;
/* start tim1, tim2 */
TCCR1B = 7 << 0;
TCCR2B = 5 << 0;
}
static uint8_t hfc_poll(void)
{
return tim2_is_ovf;
}
static uint32_t hfc_wait(void)
{
/* busy wait for tim1 to overflow. returns the resulting
16 bits counter, to be multiplied by the frequency
resolution (refer to hfc_start) to get the actual
frequency.
*/
/* force inline, do not use hfc_poll */
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;
}