-         

Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 12

Thema: Audio => AVR (LM339)

  1. #1
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    35
    Beiträge
    1.987

    Audio => AVR (LM339)

    Anzeige

    Moin.

    Hab vor einiger Zeit hier schon mal gefragt, wie man ein Audiosignal am besten für nen AVR in ein Rechtecksignal umwandelt.
    Damals wurde mir der Tip mit dem LM339 gegeben. Ich habs zu der Zeit auch hinbekommen.
    Jetzt - über 1 Jahr später - wollt ich die Schaltung nocheinmal aufbauen, aber es klappt nicht so wirklich.

    Ziel ist es einfach, ein Audiosignal aus einer Soundkarte o.ä. so aufzubereiten, dass ich ein Rechtecksignal gleicher Frequenz hab, die dann an nem INT-Pin eines AVR bestimmt werden kann.

    µC ist ein ATMega8, 8MHz intern (ausreichend für diese Zwecke).

    Das Testprogramm soll, abhänging von der Frequenz einfach mal eine LED blinken lassen. Ein Software-Frequenzteiler teilt durch 5000, so dass z.B. 5 kHz die LED im Sekundentakt blinken lassen müsste.

    Die LED blinkt jedoch - unabhängig von der angelegten Frequenz - immer relativ gleich. Als Testsignal wird ein Sweep von 500 - 5000 Hz durchlaufen.

    Für das spätere Projekt sollen dann nur Frequenzen von 800 - 3000 Hz erkannt werden.

    Hier mal das Testprogramm:
    Code:
    #include "main.h"    // Funktionsprototypen
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include "glob_defs.h"  // Definitionen wie ENABLE = 1, DISABLE = 0, etc
    #include "glob_type.h" // Typen, ui8_t, ui16_t etc...
    
    volatile ui16_t tmpcnt = 0;
    
    void Sys_Init(void)
    {
      DDRC |= (1 << PC2) | (1 << PC3) | (1 << PC4) | (1 << PC5);
    }
    
    void INT0_Init(void)
    {
      // INT0, Steigende Flanke
      GICR  |= (1 << INT0); 
      MCUCR |= (1 << ISC01) | (1 << ISC00);
    }
    
    void Timer1_Init(void)
    {
      // Timer einfach zählen lassen, Prescaler 1
      TCCR1A = 0;           
      TCCR1B = (1 << CS10); 
      TCNT1 = 0;
    }
    
    int main(void)
    {
      Sys_Init();
      Timer1_Init();
      INT0_Init();
      sei();
      while(1)
      {
        if (tmpcnt >= 5000)
        {
          PORTC ^= (1 << PD5);
          tmpcnt = 0;
        }
      }
      return(0);
    }
    
    
    ISR (SIG_INTERRUPT0)
    {
      tmpcnt++;  
    }
    Hat hier jemand nen Verbesserungsvorschlag, Primär mal für die Hardware, um dort ein sauberes Signal zu bekommen?
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken audio2avr.jpg  
    #ifndef MfG
    #define MfG

  2. #2
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    17.04.2006
    Beiträge
    2.193
    So dürfte sich der Kondensator über den Eingangsstrom des 339 aufladen und der Ausgang immer log. 1 sein, vielleicht hält sich das auch die Waage mit den negativen Komponenten des Eingangssignals. So oder so gehört da aber ein Widerstand zB nach Masse hinter den Kondensator, um einen definierten Gleichstromfad zu haben. Und damit es ohne Signal nicht "flattert" noch einer nach +Ub, so dass am nichtinvertierenden Eingang immer etwas mehr als 0V anliegen (100mV?) Dann schaltet der Komparator nur bei negativen Signalanteilen <-100mV.

  3. #3
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Wenn nur diese einfache Schaltung (bis auf den fehlenden Widerstand nach GND) gebraucht wird, könnte man auch den AVR internen Komperator nehmen.

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    35
    Beiträge
    1.987
    Also hab jetzt mal den nicht-inv. Eingang (+) mit 10k gegen Masse gezogen; jetzt kommt ein sauberes Signal hinten raus.

    Wo der inv. Eingang (-) liegt, scheint egal zu sein. Zumindest verändert sich das Oszibild nicht in Abhängigkeit der Spannung (zwischen 0-5V)

    Der interne Komparator kann leider nicht (mehr) verwendet werden, da die Pins schon anderweitig belegt sind.
    #ifndef MfG
    #define MfG

  5. #5
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Die Spannung am inv. Eingang sollte schon einen Unterschied machen.
    Den internen Comperator im AVR kann man auch über normale AD Eingänge erreichen. Dann ist allerdings die Vergleichsspannung fest auf z.B. 1,2 V gelegt (könnte vom AVR Typ abhängen?).

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    35
    Beiträge
    1.987
    Also die Hardware läuft jetzt fehlerfrei.

    Jetzt gibt nur noch in der Software ein paar Probleme.
    Hab mal den Basic-Code von http://home.arcor.de/output/elektronik/5ton-AVR.pdf versucht zu verwenden; klappt "eigentlich" auch. "Eigentlich" deswegen, weil die erkannte Frequenz nicht mit der wirklichen übereinstimmt.
    Im angefügten Code muss ich von den #defines für FTONx noch etwas abziehen, damits stimmt.
    Also werden z.B. 1060 Hz gesendet, werden diese als ca. 1040 erkannt; bzw. ich muss 1080 senden, damit 1060 erkannt werden.

    Evtl ne Idee dazu? Ich denk mal, dass diese zusätzliche Zeit einfach dadurch zustande kommt, dass die ISR ja erst mal aufgerufen werden muss etc.


    EDIT: Mal wieder den Code vergessen:

    Code:
    // µC:   ATMega8
    // Takt: 8MHz intern
    // Pwr:  5 VDC
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include <string.h>
    #include "glob_type.h"    // Datentypen ui8_t, ui16_t, etc.
    
    #define FTON1 1060 -20
    #define FTON2 1160 -20
    #define FTON3 1270 -20
    #define FTON4 1400 -20
    #define FTON5 1530 -23
    #define FTON6 1670 -25
    #define FTON7 1830 -25
    #define FTON8 2000 -25
    #define FTON9 2200 -25
    #define FTON0 2400 -30
    #define FTONR 2600 -30
    
    
    
    #define VSF_IS_NEW         (statusflags & 0x01)
    #define VSF_IS_VALID       (statusflags & 0x02)
    #define VSF_IS_GOTOMAIN    (statusflags & 0x04)
    
    #define VSF_SET_VALNEW()     (statusflags |=  0x01)
    #define VSF_SET_VALOLD()     (statusflags &= ~0x01)
    
    #define VSF_SET_GOTOMAIN()   (statusflags |=  0x04)
    #define VSF_CLR_GOTOMAIN()   (statusflags &= ~0x04)
    
    #define VSF_SET_VALID()      (statusflags |=  0x02)
    #define VSF_SET_INVALID()    (statusflags &= ~0x02)
    
    #define TIMEOUT_MAX_VALUE 40  // Max. Timeout-Zyklen
    #define AVG_VALUES        15  // Anzahl Werte für Durchschnittsbildung  
    
    
             ui32_t lgFreq;        // "Rohfrequenz" nach 1. Berechnung
             ui32_t lgFreqSum;     // Summe für Durchschnittswert-Bildung
    
    volatile ui16_t T1Value;       // Gemessener Zählwert von T1 bei Signal an INT0
             ui16_t freqs[AVG_VALUES];  // Array für Mittelwertbildung
             ui16_t freq_avg;      // Durchschnittsfrequenz
             ui16_t fBorderLo;
             ui16_t fBorderHi;
    
             ui8_t TimeOut;       // Timeout-Variable
    volatile ui8_t statusflags;   // Enthält Status-Bits (Value status flags, VSF):
                                  // Bit 0:  Neuer Messwert per INT0 eingegangen
                                  // Bit 1:  Messwert ungültig (max. Zähldauer überschritten)
                                  // Bit 2:  "Goto Main"-Ersatz
             ui8_t Sequence[5];   // enthält empfangene Sequenz
             ui8_t SeqCnt;        // Anzahl Töne in Sequenz
             ui8_t Tone;          // Aktuell korrekt erkannter Ton
             ui8_t Tone_Old;      // Zuletzt korrekt erkannter Ton
             ui8_t icnt;          // allg. Zählvariable
    
    
    int main(void)
    {
      // einige Pins von PortC als Ausgang für Status-LEDs:
      DDRC |= (1 << PC0) | (1 << PC1) | (1 << PC2) | (1 << PC3) | (1 << PC4) | (1 << PC5);
      DDRB |= (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5);
    
      // Timer 1 setzen
      TCCR1A = 0;             // Standard Timer
      TCCR1B = (1 << CS10);   // Prescaler 1
      TCNT1 = 0;              // Startwert 0
      TIMSK |= TOIE1;         // Interrupt für Überlauf aktiv
    
      // INT0 auf steigender Flanke
      GICR  |= (1 << INT0); 
      MCUCR |= (1 << ISC01) | (1 << ISC00);
      
      // Interrupts global zulassen
      sei();
    
      // ************************************************************************************************
      // MAIN LOOP START
      // ************************************************************************************************
    
    PORTC = (1 << PC5); _delay_ms(100); PORTC = 0;
    PORTC = (1 << PC4); _delay_ms(100); PORTC = 0;
    PORTC = (1 << PC3); _delay_ms(100); PORTC = 0;
    PORTC = (1 << PC2); _delay_ms(100); PORTC = 0;
    PORTC = (1 << PC1); _delay_ms(100); PORTC = 0;
    PORTC = (1 << PC0); _delay_ms(100); PORTC = 0;
    
    PORTB = (1 << PB5); _delay_ms(100); PORTB = 0;
    PORTB = (1 << PB4); _delay_ms(100); PORTB = 0;
    PORTB = (1 << PB3); _delay_ms(100); PORTB = 0;
    PORTB = (1 << PB2); _delay_ms(100); PORTB = 0;
    PORTB = (1 << PB1); _delay_ms(100); PORTB = 0;
    
    
      while(1)
      {
        VSF_CLR_GOTOMAIN();
        _delay_ms(1); // kleine Pause
    
        // Prüfen, ob T1Value in sinnvollem Bereich liegt:
        //  800 Hz => T1Value = 10000
        // 3000 Hz => T1Value =  2666
        if ((VSF_IS_VALID) && (VSF_IS_NEW))
        {
          if ((T1Value > 10000) || (T1Value < 2666))
          {
            // Wenn Wert ausserhalb: ungültig:
            VSF_SET_INVALID();
          }
        }
    
        // Folgendes nur, wenn: Wert gültig + in sinnvollem Bereich
        if ((VSF_IS_VALID) && (VSF_IS_NEW))
        {
          if (T1Value != 0) { lgFreq = F_CPU / T1Value;}  // "Rohfrequenz" berechnen
          T1Value = 1;                                    // Worst Case: Division durch 0 verhindern.
    
          TimeOut++;
          if (TimeOut >= TIMEOUT_MAX_VALUE)
          {
            memset(Sequence, 0x00, 5);
            Tone_Old = 0;
            TimeOut = 0;
            SeqCnt = 0;
            PORTB = 0;
            PORTC = 0;
          }
    
          // Wertearray weiterschieben; ältesten Wert verwerfen
          for (icnt = AVG_VALUES-1 ; icnt > 0 ; icnt--)
          {
            freqs[icnt] = freqs[icnt-1];
          }
    
          // Errechnete Frequenz in Array ablegen
          freqs[0] = (ui16_t) lgFreq;
    
          // Durchschnitt berechnen
          lgFreqSum = 0;
          for (icnt = 0 ; icnt < AVG_VALUES ; icnt++)
          {
            lgFreqSum += freqs[icnt];
          }
          freq_avg = lgFreqSum / AVG_VALUES;
    
          // Überprüfen, ob Durchschnitt "gehalten" wird
          fBorderLo = ((ui16_t) lgFreq) - 10;
          fBorderHi = ((ui16_t) lgFreq) + 10;
    
          if ((freq_avg > fBorderLo-1) && (freq_avg < fBorderHi+1))
          {
            Tone = 0;
                 if ((freq_avg > FTON1 - 10 ) && (freq_avg < FTON1 + 10)) { Tone = '1'; }
            else if ((freq_avg > FTON2 - 10 ) && (freq_avg < FTON2 + 10)) { Tone = '2'; } 
            else if ((freq_avg > FTON3 - 10 ) && (freq_avg < FTON3 + 10)) { Tone = '3'; }
            else if ((freq_avg > FTON4 - 10 ) && (freq_avg < FTON4 + 10)) { Tone = '4'; }
            else if ((freq_avg > FTON5 - 10 ) && (freq_avg < FTON5 + 10)) { Tone = '5'; }
            else if ((freq_avg > FTON6 - 10 ) && (freq_avg < FTON6 + 10)) { Tone = '6'; }
            else if ((freq_avg > FTON7 - 10 ) && (freq_avg < FTON7 + 10)) { Tone = '7'; }
            else if ((freq_avg > FTON8 - 10 ) && (freq_avg < FTON8 + 10)) { Tone = '8'; }
            else if ((freq_avg > FTON9 - 10 ) && (freq_avg < FTON9 + 10)) { Tone = '9'; }
            else if ((freq_avg > FTON0 - 10 ) && (freq_avg < FTON0 + 10)) { Tone = '0'; }
            else if ((freq_avg > FTONR - 10 ) && (freq_avg < FTONR + 10)) { Tone = 'R'; }
            else
            { VSF_SET_GOTOMAIN(); }
          }
          else
          {
            VSF_SET_GOTOMAIN();
          }
    
          if (!VSF_IS_GOTOMAIN)
          {
            switch (Tone)
            {
              case '1':
                PORTB = 0;
                PORTC = (1 << PC5);
                break;
              case '2':
                PORTB = 0;
                PORTC = (1 << PC4);
                break;
              case '3':
                PORTB = 0;
                PORTC = (1 << PC3);
                break;
              case '4':
                PORTB = 0;
                PORTC = (1 << PC2);
                break;
              case '5':
                PORTB = 0;
                PORTC = (1 << PC1);
                break;
              case '6':
                PORTB = 0;
                PORTC = (1 << PC0;
                break;
              case '7':
                PORTB = (1 << PB5);
                PORTC = 0;
                break;
              case '8':
                PORTB = (1 << PB4);
                PORTC = 0;
                break;
              case '9':
                PORTB = (1 << PB3);
                PORTC = 0;
                break;
              case '0':
                PORTB = (1 << PB2);
                PORTC = 0;
                break;
              case 'R':
                PORTB = (1 << PB1);
                PORTC = 0;
                break;
              default:
                PORTB = 0;
                PORTC = 0;
            }
          }
          // Wert abgearbeitet und damit alt:
          VSF_SET_VALOLD();
        }
    
    
    
      }
      // ************************************************************************************************
      // MAIN LOOP ENDE
      // ************************************************************************************************
      return(0);
    }
    
    
    ISR (SIG_OVERFLOW1) // Interrupt Timer 1 Überlauf
    {
      //Letzter Impuls liegt zu lange zurück => Wert verwerfen/ungültig
      VSF_SET_INVALID();
    }
    
    ISR (SIG_INTERRUPT0)
    {
      T1Value = TCNT1;    // Wert sichern
      VSF_SET_VALID();    // Wert gültig
      VSF_SET_VALNEW();   // Neuer Wert
      TCNT1 = 0;          // Timer zurücksetzen
    }
    #ifndef MfG
    #define MfG

  7. #7
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    15.11.2004
    Ort
    Aachen
    Alter
    32
    Beiträge
    246
    Das liegt möglicherweise ganz einfach daran, dass du den internen Takt benutzt, der ist meistens ziemlich ungenau! Wenn du statt dessen einen Quarz nimmst, wird das Ergebnis genauer!
    Alternativ kannst du natürlich den Fehler software-technisch kalibrieren...

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von Jaecko
    Registriert seit
    16.10.2006
    Ort
    Lkr. Rottal/Inn
    Alter
    35
    Beiträge
    1.987
    Also bisher ist der fehler softwaretechnisch ausgeglichen.
    Dass der interne Takt ungenau ist, wusst ich zwar. Aber dass der gleich so schief geht... Bei einem meiner ersten Projekte (Binär-Uhr) hab ich auch nur den internen Takt verwendet, dabei war pro Tag nur ne Abweichung von ca. 10 Sekunden drin, also rechnerisch ca. 0,01%.

    In dem Bereich des Fehlers der Software hier isses aber etwa der 100-fache Fehler.


    Ne andere, etwas aufwändigere Idee wäre, für jeden Ton einfach nen einzelnen NE567 zu verwenden (wären halt dann 11 Stück). Nur weiss ich nicht, was da genauer ist... ein ATMega + Quarz und "guter" Software oder ein NE567 mit den richtigem Bauteilgemüse aussen rum.
    #ifndef MfG
    #define MfG

  9. #9
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    15.11.2004
    Ort
    Aachen
    Alter
    32
    Beiträge
    246
    Also ich würde es einfach mal mit einem Quarz probieren, das ist ja recht schnell und einfach gemacht.

    Die internen Oszillatoren sind halt mal besser, mal schlechter, das kann sich von Chip zu Chip ändern.
    At 5V, 25°C and 1.0 MHz Oscillator frequency
    selected, this calibration gives a frequency within ± 3% of the nominal frequency.

  10. #10
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Der NE567 hat nur eine begrentzte Trennschärfe. Außerdem hat man da immernoch die Temperaturabhängigkeit. Abgesehen vom Aufwand wird da der Controller (mit Quarz) mit einem guten Programm deultich besser. Selbst mit dem internen Takt (mit Kalibrierung) sollte man etwa die Qualität des NE567 erreichen.

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •