====== Side-Tone Generator für PCW Fistcheck ====== Für das reglmässige Gebe-Training mit der Hand-Taste ist für die Nutzung von PWC-Fistcheck \\ (Ernst F. Schroeder DJ7HS https://www.qsl.net/dj7hs/download.htm) ein Side-Tone Generator auf ARDUINO Basis mit regelbarer Frequenz \\ und zwei Ausgängen deren Pegel unabhängig von einander regelbar ist enstanden.\\ \\ Schaltbild:\\ {{f11:technik:arduino:programme:arduinosidetonegenerator.png}} \\ Da hier der Einfachheit halber für jeden Ausgang ein eigender ARDUINO Pin genutzt wurde ist der Sketch etwas komplexer ausgefallen.\\ Die allseits beliebte und bekannte Arduino Funktion [[https://www.arduino.cc/reference/de/language/functions/advanced-io/tone |tone()]] scheitert leider mit der Ausgabe an den zweiten Pin.\\ Da die Frequenz aber auch veränderbar sein sollte wenn der Ton ausgegeben wird und Ansätze mit delay bzw. der Abfrage von [[https://forum.arduino.cc/index.php?topic=503368.0| milis()]] unbefriedigend in der Ton-Qualität waren (Knacken) fand letzlich die Nutzung eines Interrupt-Handlers eines\\ ARDUINO internen Timers der beim Überlauf eben jenen Interrupt erzeugt und die ensprechende Sub-Routine für die Interrupt Behandlung anspringt Anwendung.\\ Der Vorteil dabei ist das der Timer wesentlich genauer ist als die delay() Funktion die einfach nur wartet und den Arduino zum Nichtstun verdammt.\\ Der Timer wird vom Prozessor-Takt (16 Mhz) unabhängig vom Arduino Code hochgezählt. Beim Überlauf über 65536 wird der entsprechende Interrupt ausgelöst.\\ Der Arduino unterbricht nun seine main-loop und behandelt erst mal den Code in der Interrupt-Handler Routine um danach an der Stelle in der Main-Loop\\ fortzufahren an der der Interrupt zugeschlagen hat.\\ Details dazu z.B.: \\ https://www.instructables.com/id/Arduino-Timer-Interrupts/ \\ und \\ https://www.robotshop.com/community/forum/t/arduino-101-timers-and-interrupts/13072 \\ \\ Auf ein Beispiel aus dem Leben übertragen ist es so als wenn jemand den ganzen Tag am Fenster sitzt und auf den Post-Boten wartet.\\ Alternativ kommt er jede Minute oder alle zwei zurück ans Fenster um zu schauen ob der Post-Bote da ist.\\ Das ist nicht wirklich effektiv.\\ Wie elegant ist es doch seine Aufgaben zu erledigen, und sich von der Türklingel (Dem Interrupt) dazu rufen zu lassen das Nötige mit dem Post-Boten zu regeln.\\ \\ Der Sketch unten ist dokumentiert und wird auf einem ARDUINO Nano genutzt.\\ Andere Vertreter aus der ARDUINO Familie sind sicher auch möglich, der Code ist dann aber eventuell enstprechend anzupassen.\\ \\ /* CW Side-Tone genarator with adjustable frequency and two outputs (One for head-phones and one for PC-SoundCard The code has been written to train Morse-Code with straight key and check with Precision CW Fistcheck From Ernst F. Schroeder DJ7HS see https://www.qsl.net/dj7hs/download.htm December 2018 Kai DM3KB */ // Analog Pins // int potPin = 0; // input pin for the potentiometer A0 Hardware Pin 19 // Digital Pins // int tonePin = 10; // Tone output to headphone pin D10 Hardware Pin 13 int outpin = 5; // Tone output to PC pin D05 Hardware Pin 8 int keyin = 6; // Input from Key pin D06 Hardware Pin 9 int led = 13; // LED Pin D13 Hardware Pin 16 // Declare Variables int val = 0; // variable to store the value read from the potentiometer int wave = 1; // variable to flag half wave int timerstat = 0; // Timer Status Flag int playflag = 0; // Play Tone Flag int ledstat = 0; // LED Status Flag unsigned long LTCNT1; // variable for Timer1 pre-set to match of halve-wave of a frequency void setup() { // initialize the digital pin as an output. pinMode(tonePin, OUTPUT); // declare the tonePin as an OUTPUT pinMode(outpin, OUTPUT); // declare the outpin as an OUTPUT pinMode(led, OUTPUT); // declare the ledPin as an OUTPUT // Setup control input pins pinMode(keyin, INPUT); // Setup control output pins digitalWrite(led, LOW); // turn the LED off by making the voltage LOW // Timer 1, set up to enable Overflow interrupt after 65536 with prescale 1 noInterrupts(); // Switch of all Interrupts TCCR1A = 0; // set entire TCCR0A register to 0 TCCR1B = 0; // set entire TCCR0B register to 0 TCNT1 = 39168; // Pre-Inizialize the Timer1 TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10); // set 1 as Prescale-Wert TIMSK1 |= (1 << TOIE1); // Activate Timer Overflow Interrupt interrupts(); // Enable all Interrupts again //Serial.begin(9600); // open the serial port at 9600 bps: } // Interrupt -Handler for // Timer1 Overflow ISR(TIMER1_OVF_vect) { TCNT1 = LTCNT1; // Init counter again with actual value from Poti if (playflag == 1) { // If play Flag valid (1) play // square-wave to both output pins with timing read from Potentiometer which meet the frequency if (wave == 1) { //Play positive wave part digitalWrite(tonePin, HIGH); digitalWrite(outpin, HIGH); } else { //Play negative wave part digitalWrite(tonePin, LOW); digitalWrite(outpin, LOW); } } wave = !wave; // Toggel wave Flag } // The (Main) loop routine runs over and over again forever: void loop() { // Check if Key is pressed if (digitalRead(keyin)== LOW){ wave = 1; while(digitalRead(keyin)== LOW){ // As long key in is LOW (Key is pressed so we need an output!!!) if (timerstat == 0 ) { //Serial.print(" = : Enable Interrupt Play\n"); playflag = 1; timerstat = 1; } if ( ledstat == 0 ) { digitalWrite(led, HIGH); // turn the LED on by making the voltage HIGH ledstat = 1; } // Read tone frequency from Poti and map to sutialbe values val = analogRead(potPin); // read the value from the sensor // TCNT1 is the pre-set for the timer to start. // So Timer1 will count starting from 39168 or 55625 or any value in between and creating an // Overflow Interrupt when reaching 65536 // It needs to be set to time that a halve wave of the frequency need. // Formula is here: 65536 - (65536 - 16000000 / 1 / 350 / 2) // Max Value of Timer - ( Max Value of Timer / Arduino Clock Rate / PreScale / Frequency in Hz / 2 ) LTCNT1 = map(val, 0, 1023, 39168, 55625); //Map to value for TCNT1: 350 Hz = 39168 - 1250 Hz = 55625 // Generate square-wave to both output pins with frequency read from Potentiometer // by Timer1 Interrupt routine ISR(TIMER1_OVF_vect) } } if (digitalRead(keyin)== HIGH){ while(digitalRead(keyin)== HIGH){ // Key has bee released so no output if ( timerstat == 1 ) { //Serial.print(" = : Disable Interrupt Play\n"); playflag = 0; timerstat = 0; } wave = 0; // But check Speed-Potentiometer val = analogRead(potPin); // read the value from the sensor LTCNT1 = map(val, 0, 1023, 39168, 55625); //Map to value for TCNT1: 350 Hz = 39168 - 1250 Hz = 55625 if ( ledstat == 1 ) { digitalWrite(led, LOW); // turn the LED off by making the voltage LOW ledstat = 0; } } } } // End Zurück zu: [[f11:technik:arduino:start#Programme|F11:Arduino:Programme]]