For the UNO R3 there is software published by Nick Gammon in 2012, and by Paul Stoffregen published in 2018 (or may be before). They can compete with my old Heathkit counter very well, showing stable results up to 5 MHz.
I expected the R4 could do it better, but up to now, their software does not support the UNO R4. So using digitalRead is the only way to read frequencies for me.
The direct counting method results in a maximum frequency of 51 kHz, with reciprocal counting I get 410 kHz, and the results are not very stable. May be somebody finds the time to improve my code.
(I use Serial1 to send results to another Arduino)
const byte input = A0;
void setup() {
Serial1.begin(9600);
Serial1.println(__FILE__);
}
void loop() {
Serial1.println(directCount());
//Serial1.println(reciprocalCount());
}
long directCount() {
long count = 0;
const int scale = 10;
const long t = micros() + 1E6 / scale;
byte a = digitalRead(input);
while (micros() < t) {
byte b = digitalRead(input);
// rising edge:
if (a < b) count++;
a = b;
}
return scale * count;
}
double reciprocalCount() {
const int maxCount = 10000;
int count = maxCount;
byte a = digitalRead(input);
long t3 = micros();
while (count) {
byte b = digitalRead(input);
// rising edge
if (a < b) count--;
a = b;
}
long t4 = micros();
double dt = (t4 - t3) / 1E6; // seconds
return maxCount / dt;
}
In looking I found this: The Arduino Frequency Counter can measure frequency in the range from 1Hz to 7MHz. The Frequency Detector can detect frequency from 38 Hz to 9612 Hz. This is a SWAG as I am not familiar with the part. To get this they used the internal timer as a counter. If you prescale it you can go even higher.
Well the big problem with the R4 boards is they don't use a crystal but a free running oscillator. This means that any frequency measurement is anything but stable. I would abandon any idea that this is a good board for what you want to do.
I am measuring the 48000kHz is stable to c.200Hz using the Clock Frequency Accuracy Measurement Circuit (CAC) with an externally supplied 1kHz clock into the Minima's Heart Pad. The jitter isn't necessarily completely from the actual HOCO but in part being the method of measurement.
I am not sure if you have found anything that works better for you or not.
I have not played much with it, but some of the approaches I might take include:
a) faster digitalRead:
I know some of the others have probably have better versions than the quick and dirty digitalWriteFast, digitalToggleFast and digitalReadFast functions, but I have a set up in the file: UNOR4-stuff/libraries/UNOR4_digitalWriteFast/UNOR4_digitalWriteFast.h at main ยท KurtE/UNOR4-stuff (github.com)
If you call it with something the compiler knows is a constant pin number, which I believe you are doing, the code will optimize down to a few instructions (hopefully).
const byte input = 12;
volatile uint32_t count = 0;
void setup() {
while (!Serial && millis() < 5000) {}
Serial.begin(115200);
// Set switch as input with pullup
pinMode(input, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);
}
void loop() {
// No code in Loop
Serial1.println(directCount());
}
long directCount() {
const int scale = 10;
count = 0;
const long t = micros() + 1E6 / scale;
while (micros() < t) {}
return scale * count;
}
void my_isr() {
count++;
}
Note: typed on the fly... It compiles but not tested.
This uses attachInterrupt, which is only available on some of the pins. The Arduino documentation for the UNO R4 mainly says this is supported on pins 2 and 3 only.
However, I know it is supported on some other pins as well. If you look at the advanced section of the pinouts web page: ABX00080-full-pinout.pdf (arduino.cc)
You will see which pins.
Quick update: I did a quick and dirty sketch on a Teensy Micromod,
which used PWM to generate tics.
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
while(!Serial && millis() < 5000) {}
Serial.begin(115200);
Serial.print("Starting at 1000");
analogWriteFrequency(2, 1000.0);
analogWrite(2, 32);
}
void loop() {
if (Serial.available()) {
uint32_t new_frequency = 0;
int ch;
while ((ch = Serial.read()) > 0) {
if (ch >= '0' && ch <= '9') {
new_frequency = new_frequency * 10 + ch - '0';
}
else break;
}
while (Serial.read() != -1) {};
if (new_frequency) {
analogWriteFrequency(2, new_frequency);
Serial.printf("New Frequency %u\n", new_frequency);
}
}
digitalToggleFast(LED_BUILTIN);
delay(500);
}
I then tried sketch similar to above, except it could be configured to use digitalReadFast or do it by Interrupts.
#include <UNOR4_digitalWriteFast.h>
const byte input = 12;
volatile uint32_t count = 0;
//#define USE_ATTACH_INTERRUPT
void setup() {
while (!Serial && millis() < 5000) {}
Serial.begin(115200);
// Set switch as input with pullup
pinMode(input, INPUT_PULLUP);
#ifdef USE_ATTACH_INTERRUPT
attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);
#else
#endif
}
void loop() {
// No code in Loop
Serial.println(directCount());
}
#ifdef USE_ATTACH_INTERRUPT
long directCount() {
const int scale = 10;
count = 0;
uint32_t t = micros() + 1E6 / scale;
while (micros() < t) {}
return scale * count;
}
void my_isr() {
count++;
}
#else
long directCount() {
count = 0;
const int scale = 10;
uint32_t t = micros() + 1E6 / scale;
uint32_t a = digitalReadFast(input);
while (micros() < t) {
uint32_t b = digitalReadFast(input);
// rising edge:
if (a < b) count++;
a = b;
}
return scale * count;
}
#endif
The Interrupt version worked better, but I think it sort of topped out at around 300,000 pulses per second.
This does not surprise me too much, that it did not get up to something like 5mhz, as the processor only runs at 48mhz so less than 10 clocks per interrupt, might be asking for too much.
Probably could still speed it up, if one bypassed the core layering and tried to field the interrupts directly and not through a callback...
The digitalReadFast, was not nearly as stable or as fast, as noted in the first post.
Clock Frequency Accuracy Measurement Circuit (CAC)
Using EK-RA4M1 eval board... HOCO internal clock still used for USB module
Leonardo Timer4 for 1mS external timer into P204
Note: 1024 reading averaging for each printed result.
I want to do the same on my R4 WiFI but it seems to be a bit of a kludge to fit a XTAL. Not sure how different Minima is. the WiFi schematic shows XTAL on pins 9/10 (P213/P212) of the R7FA4M1 MCU connected to 9/10 (P213/P212) on the LED matrix. I couldn't find board layout for R4 (e.g eagle .brd files) . I'm wondering how you wired it in exactly? There are 11 pins tips protruding on top of the R4 next to the LED matrix, presumably these are LED Matrix pins 0-10. But which end is which? Maybe the pins are in the order shown on sheet 2 of the schematic (attached)?
I have a DG1022Z frequency generator that I can try before going to a XTAL.
Earlier in the code we attached an interrupt: attachInterrupt(digitalPinToInterrupt(input), &my_isr, RISING);
And the interrupt handler:
void my_isr() {
count++;
}
So it is important that the global variable count is defined as volatile. volatile uint32_t count = 0;
Which tells the compiler that something else might change the value...
I have the Minima, and it has the pads accessible on the PCB to fit a crystal and the two capacitors... although I ended up "trimming" with three per side!!!
I don't see it possible on the WiFi without a major attack - if I was doing it I would lift the two pins off the PCB and free-wire the crystal to them. But even I would rather not have to do that, and I do build free-wire prototypes with 0.50mm pitch devices!
Since adding a crystal clearly improves general precision, it would make sense to provide some pads for crystal install on the next UNO hardware release. Maybe there's somewhere on this forum where I can add that suggestion to the list ?
PS: your photo ... now that's what I call a busy workplace ... organised chaos
PPS: curious: how much is a EK-RA4M1 eval board to buy?
Note that the second AVR is an ATmega1284P with 32 IO pins on 4 ports, 2 UARTs and the AVR features like ADC and I2C and SPI ports.
Oh yeah, 128K flash (big fast storage), 16K SRAM and 4K EEPROM.
That chip is the biggest DIP in the AVR line! I paid $5.50 each for them at Mouser before 2020.