/* ** This is the version that does NOT use a crystal timebase but lets the Arduino perform that function. A frequency counter by Nick Kennedy, WA5BDU starting 12/18/2020 Main purposes are: 1) Learn to use the 74LV8154 counter IC and interface it to the Arduino. 2) Output periodic measurements to the serial port for the purpose of measuring drift and jumps in frequency, with my HT-37 for example. In order to learn and demonstrate function, I let the Arduino provide the 1 second gating signal. For good accuracy I'll need an external crystal or GPS based gating source. First working counter: 12/19/2020 LAST VERSION & SAVE: V1.0, 12/19/2020 V1.1 Now using interrupt to produce 1 second period 12/20/2020 */ // Define I/O pins #define GAL 2 // These pins select 1 of the 4 bytes of data to access #define GAU 3 #define GBL 4 #define GBU 5 // Next 8 pins have the 8 bits of count data // Note I can read 0 thru 5 with PINC and 7 & 8 with PINB #define Y0 A0 // A0 - A5 are PC0 - PC5 #define Y1 A1 #define Y2 A2 #define Y3 A3 #define Y4 A4 #define Y5 A5 #define Y6 8 // PB0 #define Y7 9 // PB1 #define gate 6 // RCLK - rising edge stores counter to registers #define clear 7 // CCLR low clears counters uint8_t linecount = 0; unsigned long freq; //bool int_flag; volatile bool counting; // tell isp either start or stop count volatile bool dataReady; // flag from isp to main line // ****************** S E T U P ************************************* void setup(void) { pinMode(GAL, OUTPUT); pinMode(GAU, OUTPUT); pinMode(GBL, OUTPUT); pinMode(GBU, OUTPUT); pinMode(gate, OUTPUT); pinMode(clear, OUTPUT); pinMode(Y0, INPUT); pinMode(Y1, INPUT); pinMode(Y2, INPUT); pinMode(Y3, INPUT); pinMode(Y4, INPUT); pinMode(Y5, INPUT); pinMode(Y6, INPUT); pinMode(Y7, INPUT); digitalWrite(GAL, HIGH); // These 4 lines are active LOW digitalWrite(GAU, HIGH); digitalWrite(GBL, HIGH); digitalWrite(GBU, HIGH); digitalWrite(gate, LOW); digitalWrite(clear, HIGH); Serial.begin(9600); Serial.println("Frequency counter by WA5BDU V1.0"); dataReady = false; counting = false; // From Instructables: cli();//stop interrupts //set timer1 interrupt at 1Hz TCCR1A = 0;// set entire TCCR1A register to 0 TCCR1B = 0;// same for TCCR1B TCNT1 = 0;//initialize counter value to 0 // set compare match register for 1hz increments // NRK NOTE: changed 15624 to 15630 empirically based on readout // of known accurate frequency. 7150000/7147111 * 15624 = 14630.315 OCR1A = 15630;// = (16*10^6) / (1*1024) - 1 (must be <65536) // turn on CTC mode TCCR1B |= (1 << WGM12); // Set CS10 and CS12 bits for 1024 prescaler TCCR1B |= (1 << CS12) | (1 << CS10); // enable timer compare interrupt TIMSK1 |= (1 << OCIE1A); sei();//allow interrupts } // ************************ end SETUP **************************************** // ******************* M A I N L O O P ************************************* void loop(void) { while(!dataReady); // wait until ready dataReady = false; read32(); //int_flag = false; Serial.print("frequency: "); Serial.println(freq); linecount++; if(linecount == 25) { Serial.println(); Serial.println("+++++++++++++++++++++++++++++++++++++++++++++"); Serial.println(); linecount = 0; } } // Function to read a byte from 74LV8154. Assumes register // has been selected. // Note that PINC is an AVR specific function or opcode that gets all // the bits in Port C at once. In this case only the lower six // are valid, so mask off the top two. And note that there are also // a PINB and PIND functions. uint8_t get_byte() { uint8_t tempbyte; tempbyte = PINC & B00111111; // bitwise AND strip top two bits tempbyte = tempbyte | (PINB << 6);// move to B6-7 and OR return tempbyte; } // This routine will read all four bytes of frequency data and put // them in the long int 'freq' void read32() { digitalWrite(GBU, LOW); // highest byte of four freq = (unsigned long) get_byte(); freq = freq << 8; digitalWrite(GBU, HIGH); digitalWrite(GBL, LOW); // next highest freq = freq | (unsigned long) get_byte(); freq = freq << 8; digitalWrite(GBL, HIGH); digitalWrite(GAU, LOW); // third - bits 8 - 15 freq = freq | (unsigned long) get_byte(); freq = freq << 8; digitalWrite(GAU, HIGH); digitalWrite(GAL, LOW); // lowest byte, b0:7 freq = freq | (unsigned long) get_byte(); digitalWrite(GAL, HIGH); doCorrection(); // correct for time being slightly off of 1.0 seconds } void doCorrection() { // Multiplies freq by a correction factor based on known source uint16_t adder; adder = (int)((float) freq * 0.0000244); freq = freq + adder; } void read_1s() { /* startcount = true; // tell isp to start count digitalWrite(gate, LOW); digitalWrite(clear, LOW); delay(1); digitalWrite(clear, HIGH); delay(1000); delayMicroseconds(388); // Fine tune gate period digitalWrite(gate, HIGH); // low to high transfers to registers delay(1); digitalWrite(gate, LOW); */ } // Interrupt service routine: ISR(TIMER1_COMPA_vect){ //change the 0 to 1 for timer1 and 2 for timer2 //interrupt commands here uint8_t i; if(!counting) { digitalWrite(clear, LOW); // minimum low time is 20 ns asm ( "nop \n" ); digitalWrite( clear, HIGH); digitalWrite(gate, LOW); // enable counting counting = true; //Serial.println("phase 1"); } else { //for(i = 50; i>0; i--); // use some time to stretch count time digitalWrite(gate, HIGH); // low to high transfers to registers counting = false; digitalWrite(gate, LOW); dataReady = true; //Serial.println("phase 2"); } }