/* Frequency Probe David Johnson-Davies - www.technoblogy.com - 7th December 2020 ATtiny84 @ 12 MHz (external crystal; BOD disabled) CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license: http://creativecommons.org/licenses/by/4.0/ */ #include #include // OLED I2C 128 x 32 monochrome display ********************************************** const int OLEDAddress = 0x3C; // Initialisation sequence for OLED module int const InitLen = 15; const unsigned char Init[InitLen] PROGMEM = { 0xA8, // Set multiplex 0x1F, // for 32 rows 0x8D, // Charge pump 0x14, 0x20, // Memory mode 0x01, // Vertical addressing 0xA1, // 0xA0/0xA1 flip horizontally 0xC8, // 0xC0/0xC8 flip vertically 0xDA, // Set comp ins 0x02, 0xD9, // Set pre charge 0xF1, 0xDB, // Set vcom deselect 0x40, 0xAF // Display on }; const int data = 0x40; const int single = 0x80; const int command = 0x00; void InitDisplay () { Wire.beginTransmission(OLEDAddress); Wire.write(command); for (uint8_t c=0; c=0; i--) { for (int j=1; j<3; j++) { if (((col0>>i & 0b11) == (3-j)) && ((col1>>i & 0b11) == j)) { col0R = col0R | 1<<((i*2)+j); col1L = col1L | 1<<((i*2)+3-j); } } } } Wire.write(col0L); Wire.write(col0L>>8); Wire.write(col0R); Wire.write(col0R>>8); col0L = col1L; col0R = col1R; } col0 = col1; } if (Scale == 1) Wire.write(col0); else { Wire.write(col0L); Wire.write(col0L>>8); Wire.write(col0R); Wire.write(col0R>>8); } Wire.endTransmission(); } // Plot text starting at the current plot position void PlotText(PGM_P s, int line, int column) { int p = (int)s; while (1) { char c = pgm_read_byte(p++); if (c == 0) return; PlotChar(c, line, column); column = column + Scale*6; } } // Display a fixed-point value starting at line, column void PlotValue (unsigned long freq, int line, int column, int places) { bool suppress = true; int divisor = 10000; for (unsigned int d=5; d>0; d--) { if (d == places) { PlotChar('.', line, column); column = column + Scale*6; } char c = (freq/divisor) % 10 +'0'; if (c == '0' && suppress && d>places+1) c = ' '; else suppress = false; PlotChar(c, line, column); column = column + Scale*6; divisor = divisor / 10; } } // Display frequencies ********************************************** const int Col = 0; void PlotFrequency (unsigned long c, int line) { unsigned long f = (c * 46875)/512; if (f >= 1000000) { PlotValue(f/100, line, Col, 4); PlotText(PSTR(" MHz"), line, Col+6*6*Scale); } else if (f >= 100000) { PlotValue(f/10, line, Col, 2); PlotText(PSTR(" kHz"), line, Col+6*6*Scale); } else if (f >= 10000) { PlotValue(f, line, Col, 3); PlotText(PSTR(" kHz"), line, Col+6*6*Scale); } else PlotText(PSTR("----------"), line, Col); } void PlotInterval (unsigned long c, int line) { if (c > 0 && c < 120) { PlotValue((unsigned long)1200000 / c, line, Col, 2); PlotText(PSTR(" kHz"), line, Col+6*6*Scale); } else if (c < 1200) { PlotValue((unsigned long)12000000 / c, line, Col, 3); PlotText(PSTR(" kHz"), line, Col+6*6*Scale); } else if (c < 12000) { PlotValue((unsigned long)120000000 / c, line, Col, 4); PlotText(PSTR(" kHz"), line, Col+6*6*Scale); } else if (c < 120000) { PlotValue((unsigned long)1200000000 / c, line, Col, 2); PlotText(PSTR(" Hz "), line, Col+6*6*Scale); } else if (c < 1200000) { PlotValue((unsigned long)1200000000 / (c/10), line, Col, 3); PlotText(PSTR(" Hz "), line, Col+6*6*Scale); } else { PlotValue((unsigned long)1200000000 / (c/100), line, Col, 4); PlotText(PSTR(" Hz "), line, Col+6*6*Scale); } } void PlotVoltage (unsigned long c, int line) { unsigned long v = c * 125 / 256; PlotValue(v, line, Col, 2); PlotText(PSTR(" V "), line, Col+6*6*Scale); } // Interrupt routines ********************************************** volatile unsigned int FreqHigh, IntHigh, Timer; volatile unsigned long Count0, Count1, Count; volatile boolean Capture = false; volatile uint8_t Captures = 0; // Timer/Counter0 is used in frequency mode ISR(TIM0_COMPA_vect) { FreqHigh++; } // Timer/Counter1 compare interrupt is used in frequency mode ISR(TIM1_COMPA_vect) { uint8_t temp = TCNT0; TIMSK1 = 0; // Turn off interrupt TIMSK0 = 0; // Turn off interrupt Count = (unsigned long)FreqHigh<<8 | temp; Capture = true; } // Timer/Counter1 overflow interrupt is used in interval mode ISR(TIM1_OVF_vect) { IntHigh++; } // Timer/Counter1 input capture interrupt is used in interval mode ISR(TIM1_CAPT_vect) { unsigned int temp = ICR1; Count0 = Count1; Count1 = (unsigned long)IntHigh<<16 | temp; Captures++; if (Captures == 2) { Count = Count1 - Count0; TIMSK1 = 0; } } // Watchdog interrupt gives a 1-second delay ISR(WDT_vect) { WDTCSR = WDTCSR | 1< 0); WDTCSR = WDTCSR & ~(0< 320) { PlotFrequency(c, 1); Sleep = Timeout; DelaySecond(); } else if (c == 0) mode = VOLTAGE; else mode = INTERVAL; } else if (mode == INTERVAL) { c = GetInterval(); if (c > 390) { PlotInterval(c, 1); Sleep = Timeout; } else if (c == 0) mode = VOLTAGE; else mode = FREQUENCY; } else if (mode == VOLTAGE) { c = GetVoltage(); PlotVoltage(c, 1); if (c < 40) Sleep--; DelaySecond(); mode = FREQUENCY; } } while (Sleep > 0); // Go to sleep TIMSK0 = 0; TIMSK1 = 0; WDTCSR = 0; // Make sure there are no interrupts DisplayOnOff(0); // Display off ADCSRA = ADCSRA & ~(1<