Hello,
I have written some sketchy code, to test if it is possible to measure some reasonably high frequencies with an Uno, without any external components.
In my setup I use one Uno to generate a simple signal (output on/off), and apparently I am able to generate and read a signal of appx. 1 MHz.
Doing various modifications to change frequency seems to reflect correctly in my measurement, but I do have one major problem: I don't have a frequency counter nor a scope, so I cannot verify my readings
I would really appreciate if someone with an instrument would take the time to load the following few lines of code on a "naked" Uno, and report what the output frequency is:
void setup() {
pinMode(5, OUTPUT); //we use this pin for pulse output
noInterrupts(); // Disable all INT (speeds up things a little)
}
void loop() {
PORTD = B00100000; // sets digital pin 5 HIGH
PORTD = B00000000; // sets digital pin 5 LOW
}
I attach the code for my freq counter which should be fairly self-explanatory, but I will try to elaborate on relevant details if requested.
/* --------------------------------------------------------------------------------
Arduino Uno Pulse/freq counter - testing draft
SYNOPSIS:
This is a "better-than-nothing" pulse & frequency counter.
uC RESSOURCES:
I use Timer1 for counting. Even though it is not an asynch counter, it should still
be useable if input freq is reasonably lower that the system clockfreq.
If the timer overflows during sample time, it will increment the var: overflowT1.
(Output Compare is enabled in the code, but I still use max = 65535 to have as little
interference with the timer during counting - to avoid missing pulses)
Timer 1 is a 16 bit timer, so max is 65535, giving some (theoretical) ranges of:
6 MHz@0,01s = 60.000 pulses (delay = 10ms)
12 MHz@0,005s = 60.000 pulses (delay = 5ms)
So far, it looks fairly convincing - at least I get different readings for various
setups, and the observed frequencies are in the right neighborhood ;-)
CONNECTION, IF & TEST:
I count the pulses on Timer1 input pin (Arduino Uno pin 5).
The input is purely digital, - i.e. it will probably have to be a neat TTL/5V
square-wave signal.
When I take the input from another Arduino Uno running
this simple snippet of code, I get a reading about 1MHz:
void setup() {
pinMode(5, OUTPUT); //we use this pin for pulse output
noInterrupts(); // Disable all INT (speeds up things a little)
}
void loop() {
PORTD = B00100000; // sets digital pin 5 HIGH
PORTD = B00000000; // sets digital pin 5 LOW
}
OPTIONS:
I have added some simple sample-time control, giving the sample times (in mS):
5
10
50
100
500
1000
Just hit +/- in the Arduino Serial Monitor (remember to place cursor in input field)
TODO:
Find out how long time a OCR-INT takes, so the time can be added to sample time
when calculating freq (for freq around 1 MHz@0.1S it probably gives an error about 0.1%)
**** All content is Open Source ****
*** Sven Karlsen, april 2014 ***
** always share, to show You care **
-------------------------------------------------------------------------------- */
// ************************************************************************************************************************************
const unsigned long OCR1_PRESET = 65535;// preset TOP value for Timer1 Output Compare (used for either OCR1A or OCR1B)
unsigned long pulseCnt = 0; // for a snapshot of the timer value
int overflowT1 = 0; // timer overflow (65535)
int sampleTime = 5; // start with shortest sample time (5 ms)
int sampleFactor1 = 10;
int sampleFactor2 = 2;
/* ************************************************************************************************************************************
Timer 1 setup
This code sets up Timer1, - it has been prepared for various other purposes, so the OCR-settings have been included.
If you only want to use the code for a counter, you may omit the OCIE1X & OCR1X actions and use the ISR(TIMER1_OVF_vect){} int
instead.
If you use OCR, and set the value < 65535, then you must either reset Timer1 value in the ISR (enable TCNT1 = 0), or reset Timer1
somewhere else in the code.
------------------------------------------------------------------------------------------------------------------------------------ */
void initT1() { // setup timer1
// SK: see Atmel Data Sheet on ATmega328 for my pg/table refs
TCCR1A = 4; // timer Mode, - Timer1 is set in CTC Mode (Clear Timer on Compare match)
// SK: pg. 136 table 15.4
TCCR1B = 1<<CS12 | 1<<CS11 | 1<<CS10; // timer Input clock, - input clock is set to external on rising edge
// SK: pg 137 table 15.5
TIMSK1 = 1<<TOIE1; // enable timer overflow interrupt
TIMSK1 = 1<<OCIE1A; // enable timer output compare reg A interrupt
// TIMSK1 = 1<<OCIE1B; // enable timer output compare reg B interrupt
OCR1A = OCR1_PRESET; // preset OCR1A register ( if using ISR(TIMER1_COMPA_vect) {} )
// OCR1B = OCR1_PRESET; // preset OCR1A register ( if using ISR(TIMER1_COMPB_vect) {} )
TCNT1 = 0; // reset timer1 value
}
ISR(TIMER1_COMPA_vect) { // install an interrupt service routine (ISR) for Timer1 Output Compare Match Reg A
//ISR(TIMER1_OVF_vect) { // install an interrupt service routine (ISR) for Timer1 overflow
overflowT1++;
// TCNT1 = 0; // reset timer1 value (only required if using OCR-match less than 65535
}
// ************************************************************************************************************************************
void setup() {
// Call initT1 function to setup Timer1
initT1();
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// ************************************************************************************************************************************
void loop() {
// Count pulses during requested sample time
overflowT1 = 0; // reset overflow counter
TCNT1 = 0; // reset Timer1 value
TCCR1B = 1<<CS12 | 1<<CS11 | 1<<CS10; // start timer Input clock: external, rising edge
delay(sampleTime); // wait for the set sampling time
TCCR1B = 0<<CS12 | 0<<CS11 | 0<<CS10; // stop timer Input clock
pulseCnt = TCNT1; // take a snapshot of timer 1 current value
pulseCnt += overflowT1 * OCR1_PRESET; // add any counts from overflow during sample time
// Report the observations to serial
Serial.print(overflowT1); // print #of Timer1 overflows
Serial.print(" - pulses@sampletime:");
Serial.print(pulseCnt); // print (calculated) pulse count
Serial.print("@");
Serial.print(sampleTime); // print the sample time we're using
Serial.print("mS - freq:");
Serial.print(pulseCnt/sampleTime); // print freq (integer part)
Serial.print(",");
Serial.print((pulseCnt%sampleTime)*sampleFactor2); // print freq (decimal part)
Serial.println(" kHz");
// reset and take a break ;-)
pulseCnt = 0;
delay(100);
// see if operator send us a request, - if so (and it's valid) act on it.
byte serialSignal = Serial.read();
switch(serialSignal) {
case '+': // longer sampling time
if (sampleTime < 1000) {
if (sampleFactor2 == 1) {sampleFactor2 = 2; sampleFactor1 *= 10;}
else {sampleFactor2 = 1;}
}
sampleTime = sampleFactor1 / sampleFactor2;
break;
case '-': // shorter sampling time
if (sampleTime > 5) {
if (sampleFactor2 == 2) {sampleFactor2 = 1; sampleFactor1 /= 10;}
else {sampleFactor2 = 2;}
}
sampleTime = sampleFactor1 / sampleFactor2;
default : break;
}
}