Since I had a samd10 xplained mini 2 feet away from my tiny3217 curiosity nano, it was begging for a chance to show up its cousin. It is showing 38400551 +/-2 also clocked via an efr32 (38.4MHz clkout, and its 32k xtal providing the 1Hz via timer output). The advantage here of course is no interrupts needed (I'm running the samd at its reset clock speed of 1MHz).
It took me about 5 times longer to get it to work as I have a hard time making sense of the samd sometimes. Not sure if I have it correct, but it is working. Probably a dozen better ways to do this, but I focused on the first one I thought of using TC1/2 as a 32bit timer.
The avr is easy to use, but is working harder as it only can capture via the 12bit timer. The samd is harder to use, but it exerts little effort in doing the same job. Same as always with 8bit vs 32bit. The price difference is minimal, so if I wanted to create a simple freq counter I would go with the samd. In either case its probably not going to replace a bench instrument but will be good enough for many uses (and better than many handheld multimeters). Hanging a 32k xtal on to either will get you to a pretty stable 1Hz gate so about the only remaining questions would be is how to display the info and how to buffer the input signal.
#include "MySam.hpp" //samd10 xplained mini
//led = PA09, debug = uart0, tx PA10, 9600baud
//to make changing pins easier, store pin info here and change as needed
static const struct { PIN pin; Eic::EXTINTn extintN; Evsys::SOURCE evsrc; }
Gate1Hz { PA16, Eic.EXTINT0, Evsys::EXTINT0 };
static const struct { Gclk::GENERATOR genN; PIN pin; }
ExtFreq { Gclk::GEN5, PA15 };
int main(){
Gclk.generatorSource( ExtFreq.genN, Gclk.GCLKIN ); //use GCLK_IO[n] as clock source
Pin( ExtFreq.pin, INPUT ).altFunction( ALTFUNC_GCLK ); //pin = GCLK_IO[n] input
Eic //setup gate event, will be 1Hz gate (capture) event for Tc132
.gclk( ExtFreq.genN ) //same async clock as tc1 counter
.config( Gate1Hz.extintN, Eic.RISE )
.eventOn( Gate1Hz.extintN )
.on();
Pin( Gate1Hz.pin, INPUT ).altFunction( ALTFUNC_EIC );
Evsys //setup async event on CH0, EXTINT0 -> TC1EVU
.userChannel( Evsys.TC1EVU, Evsys.CH0 )
.sourceChannel( Gate1Hz.evsrc, Evsys.CH0, Evsys.ASYNC );
Tc132 //Tc1+Tc2 32bit counter, capture on event to CC0
.gclk( ExtFreq.genN )
.eventCapture( Tc132.CAPT0, Tc132.PPW )
.readContinuous( Tc132.CC0 )
.on();
for(;;){
led.toggle();
while( not Tc132.isFlag(Tc132.CAPT0) ){} //1Hz
auto period = Tc132.capturePeriod();
debug << FG CYAN << period/1000000 /*MHz*/ << '.' << setwf(6,'0') << period%1000000 << FG ORANGE " MHz"
<< endl;
}
}
or, putting the power of C++ to work by making the freq counter a class where you can just specify the pins you want (templates allow easy compile time checking for validity of glk_io and extint pins, along with sorting out the appropriate genN, extintN, and evsys source from the pins provided, but templates are not a necessity to make this class work).
#include "MySam.hpp" //samd10 xplained mini
//led = PA09, debug = uart0, tx PA10, 9600baud
FreqCounter<PA15, PA16> freq; //<pin count (gclk_io), pin 1hz (extint) [, evsys channel]>
int main(){
for(;;){
led.toggle();
u32 period;
while( not freq.read(period) ){}
debug << FG CYAN << period/1000000 /*MHz*/ << '.' << setwf(6,'0') << period%1000000 << FG ORANGE " MHz" << endl;
}
}
edit- I added duty cycle by capturing both 1Hz edges.
edit- I subtracted duty cycle as it was of the 1Hz gate clock, which is of no value (my thinking cap was not on). I don't think there is a way to measure duty cycle internally as there is no other clock to use as a measuring stick (at least for the higher frequencies).