Page 1 of 2
AVR-USB frequency meter
Posted: Wed Apr 09, 2008 8:18 am
by ps1x
Is there way to make usb frequency meter like EasyLogger?
I have done it with rs232 and it counts this way:
Fast timer counts ticks of itself between signal pulses, that way measurement done very quickly.
Can it be done on single chip like EasyLogger? i mean is there enought resources left for this job?
p.s. sorry for my poor english, but i try make it better
Posted: Thu Apr 10, 2008 11:46 am
by christian
You can do this with signals up to ~ 3 kHz. This gives an interrupt rate of ~ 300 microseconds which should be compatible with AVR-USB. For higher frequencies, you need a prescaler or simply count input pulses during a given period of time.
Posted: Mon Apr 14, 2008 7:42 pm
by spiff
christian wrote:You can do this with signals up to ~ 3 kHz. This gives an interrupt rate of ~ 300 microseconds which should be compatible with AVR-USB. For higher frequencies, you need a prescaler or simply count input pulses during a given period of time.
How about using the event capture function of a timer. When triggered externally, the timer value is stored WITHOUT needing precise timing of the interrupts.
A different approach would be to make it try both ways. First by trying to measure the time between pulses (as you suggest), then by counting pulses in a fixed amount of time.
Posted: Mon Apr 14, 2008 8:01 pm
by christian
If you use the input capture unit, you still get an interrupt every cycle. That gives the maximum frequency of ~ 3 kHz.
Other than that, the input capture unit can be very handy for precise measurements. I had frequency resolutions of more than 22 bits.
Posted: Mon Apr 14, 2008 8:16 pm
by spiff
christian wrote:If you use the input capture unit, you still get an interrupt every cycle. That gives the maximum frequency of ~ 3 kHz.
Is it necessary to generate an interrupt on the input capture. Wouldn't it be possible to just read the captured value from the main loop?
Posted: Mon Apr 14, 2008 8:18 pm
by christian
And take the difference with what? You need two captures which are a known number of cycles apart. This is easiest done with an interrupt.
Posted: Mon Apr 21, 2008 12:45 pm
by ps1x
But wait. How to do that, while INT0 and INT1 are used?
Posted: Mon Apr 21, 2008 12:49 pm
by christian
With the input capture unit, use pin ICP1. It captures the value of timer 1 when it triggers and generates an interrupt.
Posted: Mon Apr 21, 2008 5:58 pm
by ps1x
But i have frequencys from 1 to 30000 Hz. And You say this will work for 300 Hz. I need count input pulses which i make with outstanding hardware comparator.
Where can i read about WinAVR C language? its very different with CV-AVR...
Best regards!
Posted: Mon Apr 21, 2008 6:55 pm
by christian
You could connect your input signal to T0 AND to ICP. If the input frequency is below 3 kHz, use ICP for high resolution, if it's above 3 kHz, count pulses. This is not straight forward to implement and has problems if the input frequency changes, but it's the only idea I have which does not require external hardware.
Regarding gcc: see
http://www.nongnu.org/avr-libc/user-manual/pages.html for a good introduction.
Posted: Mon Apr 21, 2008 7:18 pm
by ps1x
But T0 is just 8bit timer. It cant contain large values, right?
And to setup T1 i must use
?
And then, read counted pulses using
?
Posted: Mon Apr 21, 2008 8:12 pm
by christian
You can extend Timer0 in software, but that's not easy. Connect the signal to T1 instead and switch between modes as required.
The entire task is not easy. You must be very careful to avoid problems when you are in ICP mode and the frequency increases. I would not recommend this as a beginner's project, at least not with this complexity. If you could use always the same algorithm, it would be much easier.
Posted: Mon Apr 21, 2008 10:17 pm
by Guest
Well, this is my study project and i simply must do it, either i whant it or not. But while it is study project i'm still intresting in it as in hobby project and want to get it. I have very experienced last days, but now kinda stucked. I cant understand main algorithm in program good.
i know:
Code: Select all
1. Count pulses with T1 input capture.
2. Count time with other timer.
3. When time reaches 1 sec. Send T1 contents to USB-HID.
if i cant get enough power for 2 tasks, maybe make other AVR just counting and this one as for sample SPI > USB-HID.
Posted: Tue Apr 22, 2008 10:47 am
by christian
If you can use two AVRs: connect them with SPI or whatever (low priority interrupts), use 20 MHz for the frequency measuring AVR and writhe the SPI interrupt routine in assembler. You need to count the number of input pulses during a given period (e.g. 1 second) and the number of CPU clocks taken for these pulses. You get the frequency by dividing.
I'm using the following ICP interrupt handler for a similar task:
Code: Select all
/*
General Description:
The input capture pin is connected to a VCO. We want to measure the frequency
of that VCO as exact as possible because it represents our analog value.
This is done by counting VCO pulses during one timer1 period and measure the
total time interval in timer1 ticks during all those pulses. The quotient is
then proportional to the frequency. The resulting ADC resolution is 16 bits
for one timer1 period. If we sum up multiple timer1 periods, we can increase
the resolution accordingly (e.g. 20 bits for a sum of 16 periods).
Limitations:
The current interrupt implementation requires:
- VCO frequency > clock / 65536 (= 61 Hz @ 4 MHz clock)
- VCO frequency < clock / (32 + 1) (= 120kHz @ 4MHz clock)
The evaluation code in the main loop may require tighter limits.
*/
/* configs for io.h */
#define __SFR_OFFSET 0
#define _VECTOR(N) __vector_ ## N /* io.h does not define this for asm */
#include <avr/io.h> /* for CPU I/O register definitions and vectors */
#include "hardware.h"
#define x1 r24
#define x2 r25
#define x3 r26
//#define AVERAGE_CNT (F_CPU / T1_PRESCALE / 65536 + UPDATE_FREQ / 2) / UPDATE_FREQ
#define AVERAGE_CNT 22
/* we choose an averaging count which gives a total measurement time as close as
* possible to 1/50Hz. This should result in good ripple rejection.
* F_CPU = 4e6
* 22 * 65536 / 4e6 = 0.360448s ~ 18/50Hz (deviation = 2.24% of a mains period)
*/
.text
.global SIG_INPUT_CAPTURE1
.type SIG_INPUT_CAPTURE1, @function
SIG_INPUT_CAPTURE1:
intCapture1: ;2 (branch taken from vector)
push x1 ;2
in x1, SREG ;1
push x1 ;2
in x1, TIFR ;1
andi x1, 1 << TOV1 ;1
brne .tm1HadOverflow ;1 -> 10
.ignoreOverflow:
lds x1, _totalPulses ;2
subi x1, lo8(-1) ;1
sts _totalPulses, x1 ;2
lds x1, _totalPulses+1 ;2
sbci x1, hi8(-1) ;1
sts _totalPulses+1, x1 ;2
.capture1Ready:
pop x1 ;2
out SREG, x1 ;1
pop x1 ;2
reti ;4
;--------
; 29 cycles for typical intr / 40 for ignored overflow
; avg. 5 cycles latency -> fmaxload = fcpu/34 (117k @ 4M)
; process data once every timer 1 overflow
.tm1HadOverflow: ;1 for branch taken
ldi x1, 1 << TOV1 ;1
out TIFR, x1 ;1
lds x1, _timerWrapCnt ;2
dec x1 ;1
breq .doEval ;1 -> 17
sts _timerWrapCnt, x1 ;2
rjmp .ignoreOverflow ;2 -> 21
.doEval: ;1 for branch taken
push x2 ;2
push x3 ;2
in x1, ICR1L ;1 -> 21 get captured value before next capture
in x2, ICR1H ;1 11 cycles latency + 21 = 32 -> fmax = fcpu/32 (125k @ 4M)
lds x3, _totalPulses ;2
subi x3, lo8(-1) ;1
sts totalPulses, x3 ;2 store values in global variable (no underscore)
lds x3, _totalPulses+1 ;2
sbci x3, hi8(-1) ;1
sts totalPulses+1, x3 ;2
clr x3 ;1
sts _totalPulses, x3 ;2 clear local counter
sts _totalPulses+1, x3 ;2
sei ;1 -> 40 cycles until intr enable
; now we have time and can relax :-)
lds x3, _startPulsePos
sts _startPulsePos, x1 ; store new pulsePos
sub x1, x3
lds x3, _startPulsePos+1
sts _startPulsePos+1, x2
sbc x2, x3 ; x2:x1 holds pos2 - pos1 now
sts totalClocks, x1
sts totalClocks+1, x2
ldi x3, AVERAGE_CNT
sts _timerWrapCnt, x3
sbrc x2, 7
dec x3 ; account for negative delta
sts totalClocks+2, x3
pop x3
pop x2
rjmp .capture1Ready
; Local symbols:
.lcomm _totalPulses, 2 ; total captured pulses
.lcomm _startPulsePos, 2 ; pulse position of starting edge in timer1 counts
.data
.type _timerWrapCnt, @object
.size _timerWrapCnt, 1
_timerWrapCnt: ; remaining number of overflows until measurement end
.byte AVERAGE_CNT
Please don't ask me for details about this code
Posted: Tue Apr 22, 2008 12:27 pm
by Guest
Thank you for reply. I will not ask about it
Anyway Asm is Oo for me. I connected 2 AVR's using UART and it works fine (using AVR-USB CDC). Now i will make it UART -> HID. Thank you.
Best regards.