AVR-USB frequency meter
AVR-USB frequency meter
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
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
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.
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.
Regarding gcc: see http://www.nongnu.org/avr-libc/user-manual/pages.html for a good introduction.
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
?
And to setup T1 i must use
Code: Select all
TCCR1B=0x06;
?
And then, read counted pulses using
Code: Select all
TIFR1
?
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.
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.
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:
if i cant get enough power for 2 tasks, maybe make other AVR just counting and this one as for sample SPI > USB-HID.
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.
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:
Please don't ask me for details about this code
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