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:


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 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 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:Arduino:Programme