Tricky! Carry on then…
I have actually done 75% of this but guess what I am waiting on to arrive…yup a Nano.
I need to order a 8 segment display as well actually, I coded against a OLED as I had one but not a LED.
I re-wrote the “calculations” part to give Note + cents, though it’s using floats now. The way that LEDs are lit would be better done using bit masking I just haven’t gotten round to it as I want to test the tuner code first. The tuner is in a cpp/h file combo can can be used by what ever you want to.
Obviously the result will only be as good as the frequency you give it and octaves 0-3 having integer frequencies will affect the result more and more as the note gets lower.
Here it is…
Confirmed working on a '168 nano by @ChristianBloch .
It is designed for the original PCB.
If you change some of the outputs so all the 7seg+dot are on one port, you can obviously output the whole 8 bits in a single instruction.
Same for the 3 ‘accuracy’ LEDs.
// https://www.instructables.com/member/amandaghassaei/
// Changelog
// Code origin Sam / LookMumNoComputer and Amandaghassaei
// 9 Jan 2020: Jos Bouten aka Zaphod B:
// - put frequencies in a table and simplified controlling the led display
// - put strings in flash memory to use less program memory space.
// 18 Jan 2020
// Added test of clipping led.
// 29 Febr 2020
// Added a switch mechanism to the code to make it easy to use either a common anode or common
// cathode LED-display.
// Set the const LED_DISPLAY_TYPE to the type of LED display you use (COMMON_ANODE or COMMON_CATHODE).
// 26 Jan 2021 EricM (ematecki@hotmail.com)
// clean up :
// - new note table
// - use dichotomy
// - replaced strings with bits
// 28 Mai 2022 EricM (ematecki@hotmail.com)
// fixed some hexadecimal numbers which should have been binary (0x -> 0b) oops
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
*/
//
// uncomment the one matching your 7seg display
//
#define COMMON_ANODE
//#define COMMON_CATHODE
// Uncomment the following #define if you want to check the 7-segment display
// 3 signalling LEDs and clipping LED continuously for testing purposes.
// Leave it commented out if you want to run the tuner.
//#define LED_TEST
// DO NOT CHANGE ANYTHING BELOW THIS LINE
//-------------------------------------------------------------------------------------------
// Accuracy LEDs
const int LED3 = 18;
const int LED4 = 19;
const int LED5 = 17;
const int ACC_LOW = 0b100;
const int ACC_SPOTON = 0b010;
const int ACC_HIGH = 0b001;
// 7 segment display output pins;
const int LEDA = 8;
const int LEDB = 9;
const int LEDC = 4;
const int LEDD = 3;
const int LEDE = 2;
const int LEDF = 7;
const int LEDG = 6;
const int LEDDP = 5;
const int CLIPPING_LED = 13;
//=============================================================== frequency determination code
// Data storage variables.
byte newData = 0;
byte prevData = 0;
// Freq variables.
unsigned int period;
#define HALF_SAMPLE_VALUE 127
#define TIMER_RATE 38462
#define TIMER_RATE_10 (TIMER_RATE * 10)
// Data storage variables.
unsigned int time = 0; // Keeps time and sends values to store in timer[] occasionally.
#define BUFFER_SIZE 10
int timer[BUFFER_SIZE]; // Sstorage for timing of events.
int slope[BUFFER_SIZE]; // Storage for slope of events.
unsigned int totalTimer; // Used to calculate period.
byte index = 0; // Current storage index.
int maxSlope = 0; // Used to calculate max slope as trigger point.
int newSlope; // Storage for incoming slope data.
// Variables for decided whether you have a match.
#define MAX_NO_MATCH_VALUE 9
byte noMatch = 0; // Counts how many non-matches you've received to reset variables if it's been too long.
byte slopeTol = 3; // Slope tolerance - adjust this if you need.
int timerTol = 10; // Timer tolerance - adjust this if you need.
// Variables for amp detection.
unsigned int ampTimer = 0;
byte maxAmp = 0;
byte checkMaxAmp;
byte ampThreshold = 30; // Raise if you have a very noisy signal.
long clippingTimer = 0;
// Clipping indicator variables.
boolean clipping = true;
#define CLIPPING_TIME (5 * TIMER_RATE) // This should amount to 2 seconds.
void
reset() // Clear out some variables.
{
index = 0; // Reset index.
noMatch = 0; // Reset match counter.
maxSlope = 0; // Reset slope.
}
void
SetupISR()
{
cli(); // Disable interrupts.
// Set up continuous sampling of analog pin 0.
// Clear ADCSRA and ADCSRB registers.
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (1 << REFS0); // Set reference voltage.
ADMUX |= (1 << ADLAR); // Left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // Set ADC clock with 32 prescaler -> 16mHz / 32 = 500kHz.
ADCSRA |= (1 << ADATE); // Enable auto trigger.
ADCSRA |= (1 << ADIE); // Enable interrupts when measurement complete.
ADCSRA |= (1 << ADEN); // Enable ADC.
ADCSRA |= (1 << ADSC); // Start ADC measurements.
sei(); // Enable interrupts.
}
ISR( ADC_vect ) // When new ADC value ready.
{
PORTB &= B11101111; // Set pin 12 low.
prevData = newData; // Store previous value.
newData = ADCH; // Get value from A0.
if( prevData < HALF_SAMPLE_VALUE && newData >= HALF_SAMPLE_VALUE ) // if increasing and crossing midpoint
{
newSlope = newData - prevData; // Calculate slope
if( abs(newSlope - maxSlope) < slopeTol ) // If slopes are ==
{
// Record new data and reset time.
slope[index] = newSlope;
timer[index] = time;
time = 0;
if( index == 0 ) // New max slope just reset.
{
PORTB |= B00010000; // Set pin 12 high.
noMatch = 0;
index++; // Increment index.
}
else if( abs(timer[0] - timer[index]) < timerTol && abs(slope[0] - newSlope) < slopeTol ) //if timer duration and slopes match
{
// Sum timer values.
totalTimer = 0;
for( byte i = 0; i < index; ++i )
{
totalTimer += timer[i];
}
period = totalTimer; // Set period.
// Reset new zero index values to compare with.
timer[0] = timer[index];
slope[0] = slope[index];
index = 1; // Set index to 1.
PORTB |= B00010000; // Set pin 12 high.
noMatch = 0;
}
else
{ // Crossing midpoint but not match.
index++; // Increment index.
if( index > BUFFER_SIZE - 1 )
{
reset();
}
}
}
else if( newSlope > maxSlope ) // If new slope is much larger than max slope.
{
maxSlope = newSlope;
time = 0; // Reset clock.
noMatch = 0;
index = 0; // Reset index.
}
else // Slope not steep enough.
{
noMatch++; // Increment no match counter.
if( noMatch > MAX_NO_MATCH_VALUE )
{
reset();
}
}
}
if( newData == 0 || newData == 1023 ) // If clipping
{
PORTB |= B00100000; // set pin 13 high, i.e. turn on clipping indicator led.
clipping = true; // Currently clipping.
}
time++; // Increment timer at rate of 38.5kHz
clippingTimer++;
if( clippingTimer > CLIPPING_TIME )
{
PORTB &= B11011111; // Set pin 13 low, i.e. turn off clipping indicator led.
clipping = false; // Currently not clipping.
clippingTimer = 0;
}
ampTimer++; // Increment amplitude timer.
if( abs(HALF_SAMPLE_VALUE - ADCH) > maxAmp )
{
maxAmp = abs(HALF_SAMPLE_VALUE-ADCH);
}
if( ampTimer == 1000 )
{
ampTimer = 0;
checkMaxAmp = maxAmp;
maxAmp = 0;
}
}
//============================================================================================ user interface
struct cFreq4
{
/// the four frequencies of the range (in Hz*10)
uint16_t mLowest, mLow, mHigh, mHighest;
char mNote; ///< C c D d E F f G g A a B lower case denotes #
char mOctave; ///< number of the octave (not used 'coz we only have one digit display...)
};
static const cFreq4 sgNotes[] PROGMEM =
{
{ 159, 162, 164, 167, 'C', 0 }, // 16.352 Hz
{ 168, 172, 174, 177, 'c', 0 }, // 17.324 Hz
{ 178, 182, 184, 188, 'D', 0 }, // 18.354 Hz
{ 189, 193, 195, 199, 'd', 0 }, // 19.445 Hz
{ 200, 204, 207, 211, 'E', 0 }, // 20.602 Hz
{ 212, 216, 219, 224, 'F', 0 }, // 21.827 Hz
{ 225, 229, 232, 237, 'f', 0 }, // 23.125 Hz
{ 238, 243, 246, 251, 'G', 0 }, // 24.500 Hz
{ 252, 257, 261, 266, 'g', 0 }, // 25.957 Hz
{ 267, 272, 277, 282, 'A', 0 }, // 27.500 Hz
{ 283, 289, 293, 299, 'a', 0 }, // 29.135 Hz
{ 300, 306, 311, 317, 'B', 0 }, // 30.868 Hz
{ 318, 324, 329, 336, 'C', 1 }, // 32.703 Hz
{ 337, 343, 349, 356, 'c', 1 }, // 34.648 Hz
{ 357, 364, 370, 377, 'D', 1 }, // 36.708 Hz
{ 378, 385, 392, 399, 'd', 1 }, // 38.891 Hz
{ 400, 408, 415, 423, 'E', 1 }, // 41.203 Hz
{ 424, 432, 440, 448, 'F', 1 }, // 43.654 Hz
{ 449, 458, 466, 475, 'f', 1 }, // 46.249 Hz
{ 476, 485, 494, 503, 'G', 1 }, // 48.999 Hz
{ 504, 514, 523, 533, 'g', 1 }, // 51.913 Hz
{ 534, 545, 554, 565, 'A', 1 }, // 55.000 Hz
{ 566, 577, 587, 599, 'a', 1 }, // 58.270 Hz
{ 600, 611, 622, 634, 'B', 1 }, // 61.735 Hz
{ 635, 648, 659, 672, 'C', 2 }, // 65.406 Hz
{ 673, 686, 699, 712, 'c', 2 }, // 69.296 Hz
{ 713, 727, 740, 755, 'D', 2 }, // 73.416 Hz
{ 756, 770, 784, 800, 'd', 2 }, // 77.782 Hz
{ 801, 816, 831, 847, 'E', 2 }, // 82.407 Hz
{ 848, 865, 881, 898, 'F', 2 }, // 87.307 Hz
{ 899, 916, 933, 951, 'f', 2 }, // 92.499 Hz
{ 952, 971, 988, 1008, 'G', 2 }, // 97.999 Hz
{ 1009, 1028, 1047, 1068, 'g', 2 }, // 103.826 Hz
{ 1069, 1089, 1110, 1131, 'A', 2 }, // 110.000 Hz
{ 1132, 1154, 1176, 1199, 'a', 2 }, // 116.541 Hz
{ 1200, 1223, 1246, 1270, 'B', 2 }, // 123.471 Hz
{ 1271, 1296, 1320, 1345, 'C', 3 }, // 130.813 Hz
{ 1346, 1373, 1398, 1426, 'c', 3 }, // 138.591 Hz
{ 1427, 1454, 1482, 1510, 'D', 3 }, // 146.832 Hz
{ 1511, 1541, 1570, 1600, 'd', 3 }, // 155.563 Hz
{ 1601, 1632, 1663, 1695, 'E', 3 }, // 164.814 Hz
{ 1696, 1729, 1762, 1796, 'F', 3 }, // 174.614 Hz
{ 1797, 1832, 1867, 1903, 'f', 3 }, // 184.997 Hz
{ 1904, 1941, 1978, 2016, 'G', 3 }, // 195.998 Hz
{ 2017, 2057, 2096, 2136, 'g', 3 }, // 207.652 Hz
{ 2137, 2179, 2220, 2263, 'A', 3 }, // 220.000 Hz
{ 2264, 2308, 2352, 2398, 'a', 3 }, // 233.082 Hz
{ 2399, 2446, 2492, 2541, 'B', 3 }, // 246.942 Hz
{ 2542, 2591, 2641, 2692, 'C', 4 }, // 261.626 Hz
{ 2693, 2745, 2798, 2852, 'c', 4 }, // 277.183 Hz
{ 2853, 2909, 2964, 3022, 'D', 4 }, // 293.665 Hz
{ 3023, 3081, 3140, 3201, 'd', 4 }, // 311.127 Hz
{ 3202, 3265, 3327, 3392, 'E', 4 }, // 329.628 Hz
{ 3393, 3459, 3525, 3594, 'F', 4 }, // 349.228 Hz
{ 3595, 3664, 3735, 3807, 'f', 4 }, // 369.994 Hz
{ 3808, 3882, 3957, 4034, 'G', 4 }, // 391.995 Hz
{ 4035, 4113, 4192, 4274, 'g', 4 }, // 415.305 Hz
{ 4275, 4358, 4442, 4528, 'A', 4 }, // 440.000 Hz
{ 4529, 4617, 4706, 4797, 'a', 4 }, // 466.164 Hz
{ 4798, 4892, 4986, 5083, 'B', 4 }, // 493.883 Hz
{ 5084, 5182, 5282, 5385, 'C', 5 }, // 523.251 Hz
{ 5386, 5491, 5596, 5705, 'c', 5 }, // 554.365 Hz
{ 5706, 5817, 5929, 6044, 'D', 5 }, // 587.330 Hz
{ 6045, 6163, 6282, 6404, 'd', 5 }, // 622.254 Hz
{ 6405, 6529, 6655, 6785, 'E', 5 }, // 659.255 Hz
{ 6786, 6918, 7051, 7188, 'F', 5 }, // 698.456 Hz
{ 7189, 7329, 7470, 7616, 'f', 5 }, // 739.989 Hz
{ 7617, 7765, 7915, 8069, 'G', 5 }, // 783.991 Hz
{ 8070, 8227, 8385, 8548, 'g', 5 }, // 830.609 Hz
{ 8549, 8716, 8884, 9057, 'A', 5 }, // 880.000 Hz
{ 9058, 9234, 9412, 9595, 'a', 5 }, // 932.328 Hz
{ 9596, 9783, 9972, 10166, 'B', 5 }, // 987.767 Hz
#if 0 // frqeuncy determination code cannot handle this high a frequency ?
{ 10167, 10365, 10565, 10771, 'C', 6 }, // 1046.502 Hz
{ 10772, 10981, 11194, 11411, 'c', 6 }, // 1108.731 Hz
{ 11412, 11634, 11859, 12090, 'D', 6 }, // 1174.659 Hz
{ 12091, 12326, 12564, 12809, 'd', 6 }, // 1244.508 Hz
{ 12810, 13059, 13312, 13570, 'E', 6 }, // 1318.510 Hz
{ 13571, 13835, 14103, 14377, 'F', 6 }, // 1396.913 Hz
{ 14378, 14658, 14942, 15232, 'f', 6 }, // 1479.978 Hz
{ 15233, 15530, 15830, 16138, 'G', 6 }, // 1567.982 Hz
{ 16139, 16453, 16772, 17098, 'g', 6 }, // 1661.219 Hz
{ 17099, 17431, 17769, 18115, 'A', 6 }, // 1760.000 Hz
{ 18116, 18468, 18826, 19192, 'a', 6 }, // 1864.655 Hz
{ 19193, 19566, 19945, 20333, 'B', 6 }, // 1975.533 Hz
{ 20334, 20730, 21132, 21542, 'C', 7 }, // 2093.005 Hz
{ 21543, 21962, 22388, 22823, 'c', 7 }, // 2217.461 Hz
{ 22824, 23268, 23719, 24181, 'D', 7 }, // 2349.318 Hz
{ 24182, 24652, 25130, 25618, 'd', 7 }, // 2489.016 Hz
{ 25619, 26118, 26624, 27142, 'E', 7 }, // 2637.020 Hz
{ 27143, 27671, 28208, 28756, 'F', 7 }, // 2793.826 Hz
{ 28757, 29316, 29885, 30466, 'f', 7 }, // 2959.955 Hz
{ 30467, 31059, 31662, 32278, 'G', 7 }, // 3135.963 Hz
{ 32279, 32906, 33545, 34197, 'g', 7 }, // 3322.438 Hz
{ 34198, 34863, 35540, 36230, 'A', 7 }, // 3520.000 Hz
{ 36231, 36936, 37653, 38385, 'a', 7 }, // 3729.310 Hz
{ 38386, 39132, 39892, 40667, 'B', 7 }, // 3951.066 Hz
{ 40668, 41459, 42264, 43086, 'C', 8 }, // 4186.009 Hz
{ 43087, 43924, 44777, 45648, 'c', 8 }, // 4434.922 Hz
{ 45649, 46536, 47440, 48362, 'D', 8 }, // 4698.636 Hz
{ 48363, 49303, 50261, 51238, 'd', 8 }, // 4978.032 Hz
{ 51239, 52235, 53250, 54285, 'E', 8 }, // 5274.041 Hz
{ 54286, 55341, 56416, 57513, 'F', 8 }, // 5587.652 Hz
{ 57514, 58632, 59771, 60933, 'f', 8 }, // 5919.911 Hz
{ 60934, 62118, 63325, 64556, 'G', 8 }, // 6271.927 Hz
// uint16_t => overflow !
{ 64557, 65812, 67091, 68395, 'g', 8 }, // 6644.875 Hz
{ 68396, 69726, 71080, 72462, 'A', 8 }, // 7040.000 Hz
{ 72463, 73872, 75307, 76771, 'a', 8 }, // 7458.620 Hz
{ 76772, 78264, 79785, 81336, 'B', 8 }, // 7902.133 Hz
{ 81337, 82918, 84529, 86172, 'C', 9 }, // 8372.018 Hz
{ 86173, 87849, 89555, 91297, 'c', 9 }, // 8869.844 Hz
{ 91298, 93072, 94881, 96725, 'D', 9 }, // 9397.273 Hz
{ 96726, 98607, 100523, 102477, 'd', 9 }, // 9956.063 Hz
{ 102478, 104470, 106500, 108571, 'E', 9 }, // 10548.082 Hz
{ 108572, 110682, 112833, 115027, 'F', 9 }, // 11175.303 Hz
{ 115028, 117264, 119543, 121867, 'f', 9 }, // 11839.822 Hz
{ 121868, 124237, 126651, 129113, 'G', 9 }, // 12543.854 Hz
{ 129114, 131624, 134182, 136791, 'g', 9 }, // 13289.750 Hz
{ 136792, 139451, 142161, 144925, 'A', 9 }, // 14080.000 Hz
{ 144926, 147743, 150614, 153542, 'a', 9 }, // 14917.240 Hz
{ 153543, 156528, 159570, 162673, 'B', 9 }, // 15804.266 Hz
#endif
};
static const int sgNoteCount = sizeof sgNotes/sizeof sgNotes[0];
static uint16_t sgLowestFrequency = 0;
static uint16_t sgHighestFrequency = 65535;
void
setup()
{
pinMode( LED3, OUTPUT );
pinMode( LED4, OUTPUT );
pinMode( LED5, OUTPUT );
pinMode( LEDA, OUTPUT );
pinMode( LEDB, OUTPUT );
pinMode( LEDC, OUTPUT );
pinMode( LEDD, OUTPUT );
pinMode( LEDE, OUTPUT );
pinMode( LEDF, OUTPUT );
pinMode( LEDG, OUTPUT );
pinMode( LEDDP, OUTPUT );
Serial.begin( 9600 );
sgLowestFrequency = pgm_read_word_near( &sgNotes[0].mLowest );
sgHighestFrequency = pgm_read_word_near( &sgNotes[sgNoteCount-1].mHighest );
SetupISR();
// Run a test of all leds.
#ifndef LED_TEST
TestLedsIndividually( 50 );
TestMusicalChars( 50 );
TestLedsIndividually( 50 );
TestMusicalChars( 50 );
#endif
}
#ifdef LED_TEST
void
loop()//EM:
{
TestLedsIndividually( 500 );
TestMusicalChars( 500 );
}
#else
void
loop()//EM:
{
uint16_t frequency = TIMER_RATE_10 / period; // Timer rate with an extra zero/period.
// check for out of bounds freq
if( frequency < sgLowestFrequency )
{
ShowCharacter( '<' );
return;
}
if( frequency > sgHighestFrequency )
{
ShowCharacter( '>' );
return;
}
// dichotomy to find note
int begin = 0;
int end = sgNoteCount-1;
while( begin != end )
{
int middle = (begin + end) >> 1;
if( middle == begin )
{
// we found the note, now test its accuracy
if( frequency < pgm_read_word_near( &sgNotes[middle].mLow ) )
SetAccuracyLeds( ACC_LOW );
else if( frequency > pgm_read_word_near( &sgNotes[middle].mHigh ) )
SetAccuracyLeds( ACC_HIGH );
else
SetAccuracyLeds( ACC_SPOTON );
ShowCharacter( pgm_read_byte_near( &sgNotes[middle].mNote ) );
break;
}
if( frequency < pgm_read_word_near( &sgNotes[middle].mLowest ) )
end = middle;
else
begin = middle;
}
delay( 70 );
// Serial.print(frequency / 10);
// Serial.println(F("Hz"));
}
#endif
void
TestLedsIndividually( int iDelay )//EM:
{
// test 7-seg LEDs
int bits = 0b10000000;
for( int i = 0; i < 8; ++i )
{
Set7Segment( bits );
delay( iDelay );
bits >>= 1;
}
Set7Segment( 0 );
// test accuracy LEDs
SetAccuracyLeds( ACC_LOW );
delay( iDelay );
SetAccuracyLeds( ACC_SPOTON );
delay( iDelay );
SetAccuracyLeds( ACC_HIGH );
delay( iDelay );
SetAccuracyLeds( 0 );
}
void
TestMusicalChars( int iDelay )//EM:
{
const char* iChars = "C c D d E F f G g A a B ";
int n = strlen( iChars );
for( int i = 0; i < n; ++i )
{
ShowCharacter( iChars[i] );
delay( iDelay );
}
}
void
SetAccuracyLeds( int iAccuracy )//EM:
{
//TODO: decoding is wrong because I haven't yet decided what it should be ! ============================================
// Decode led pattern and switch on/off the leds.
#ifdef COMMON_ANODE //TODO: just be sure it should not be !defined() ===================================================
iAccuracy = ~iAccuracy;
#endif
digitalWrite( LED3, (iAccuracy & ACC_LOW ) ? HIGH : LOW );
digitalWrite( LED4, (iAccuracy & ACC_SPOTON) ? HIGH : LOW );
digitalWrite( LED5, (iAccuracy & ACC_HIGH ) ? HIGH : LOW );
// digitalWrite( CLIPPING_LED, (iBits & 0b00010000) ? HIGH : LOW );
}
//bits ABCDEFG.
void
Set7Segment( int iBits )//EM:
{
// Decode 7-seg pattern and switch on/off the leds.
#ifdef COMMON_ANODE //TODO: just be sure it should not be !defined() ===================================================
iBits = ~iBits;
#endif
digitalWrite( LEDA, (iBits & 0b10000000) ? HIGH : LOW );
digitalWrite( LEDB, (iBits & 0b01000000) ? HIGH : LOW );
digitalWrite( LEDC, (iBits & 0b00100000) ? HIGH : LOW );
digitalWrite( LEDD, (iBits & 0b00010000) ? HIGH : LOW );
digitalWrite( LEDE, (iBits & 0b00001000) ? HIGH : LOW );
digitalWrite( LEDF, (iBits & 0b00000100) ? HIGH : LOW );
digitalWrite( LEDG, (iBits & 0b00000010) ? HIGH : LOW );
digitalWrite( LEDDP, (iBits & 0b00000001) ? HIGH : LOW );
}
// AAAA
// F B
// F B
// F B
// GGGG
// E C
// E C
// E C
// DDDD
// DP
void
ShowCharacter( char iChar )//EM:
{
switch( iChar )
{
//ABCDEFG.
case ' ': Set7Segment( 0b00000000 ); return; // off
case 'A': Set7Segment( 0b11101110 ); return;
case 'a': Set7Segment( 0b11101111 ); return;
case 'B': Set7Segment( 0b00111110 ); return;
case 'C': Set7Segment( 0b10011100 ); return;
case 'c': Set7Segment( 0b10011101 ); return;
case 'D': Set7Segment( 0b01111010 ); return;
case 'd': Set7Segment( 0b01111011 ); return;
case 'E': Set7Segment( 0b10011110 ); return;
case 'F': Set7Segment( 0b10001110 ); return;
case 'f': Set7Segment( 0b10001111 ); return;
case 'G': Set7Segment( 0b10111110 ); return;
case 'g': Set7Segment( 0b10111111 ); return;
case '<': Set7Segment( 0b00001110 ); return; // freq too low
case '>': Set7Segment( 0b01100010 ); return; // freq too high
default: Set7Segment( 0b00000001 ); return;
}
}
// test pattern : (E-G-A-G-D-C-D-E)
Yes, this is a life saver! Are you going to put it on GitHub?
Yes it is planned… but my plans always take too long to become reality
At least, now it is here so everybody can pick it up until I post it on github (or rather gitlab where I already have an account).
If you don’t like the way the characters are displayed, just change the bits in the ShowCharacter() function.
I know @ChristianBloch did change some of them.
Reading wondering round Aldi and that looks nice. The common anode handling is a nice use of binary stuff which is something I’m having to re learn after years of .net!
Could use the DP to show a sharp which is how my old tuner did it.
Does the Berlin School have a built in clock or does it need an external one?
I bodged up a test setup for the maths+ OLED tuner in my lunch. iPad puts out 2v peak to peak into a non inverting op amp to get to 4.7v ish then it’s offset to centre on 2.5v so the arduino is happy (all in the original instructable).
It works but it’s accuracy is not perfect. The result based on the input is right but the calculated frequency is a tad off.
I’m feeding it a 440 hz A4 triangle here but you can see the measured frequency in the top corner is off by the amount of cents on the 2nd line.
In tune is ± 5 cents which would draw a box round the note. I seem to detect at about -11.58 or +8.21 which I am guessing is the resolution of the timer somewhere.
This is rendering at 10hz on the screen, but it will happily render way faster if unbound but that doesn’t make for easy viewing in a demo as it’s too twitchy.
When I get a chance I’ll swap to the table method Eric posted above and see if that’s more reliable.
Shame as I was hopeful on this
good question I really don’t know haven’t used it yet . I didn’t really buy it to use as a clock .
hard to tell from the description but I would say no , not that it really matters for how I plan on using it.
More I’ve looked at it more than once but I don’t have a clock to feed it! Getting started is the hardest part it seems.
Have an LFO with a square output? An oscillator that can be tuned really low? An audio output from somewhere that can be turned up a bit? But also, yeah, get or build some kind of clock module.
A 555 lfo makes an excellent clock
This is so awesome
Built @jessecakeindustries glide Verified Stripboard Layouts! - #422 by jessecakeindustries - testing tomorrow… curious what it will do to audio signals as well as cv
Are they true “analog outputs” ?
I mean, you have CV ins and the hands move accordingy
Allways a small original detail ! excellent
Just tested Eric’s method above and it’s far more stable. Though I seem to have picked up an off by one somewhere as it it’s showing an a (A#) not an A. I’ll dig into why, hopefully a copy paste error somewhere, but this seems to be the way forward.
The frequency here is x10 as per the original code though it would be easy to display as a decimal if desired.