SOUNDRX

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Von Robert und Frank M. (ukw)

Das Programmpaket SOUNDRX kann Daten mit ca. 2000 Zeichen pro Sekunde über die Soundkarte vom PC an einen ATmega übertragen. Dazu wandelt es die Daten in eine interne WAVE-Datei um und spielt diese über die Soundkarte ab.

Das Paket enthält dafür ein Beispielprogramm sowohl für den µC als auch PC. Ebenso ist ein Bootloader für den µC und das dazugehörige PC-Programm enthalten, um ATmegas mit mind. 2 KiB Bootloadergröße über ein simples Audiokabel zu flashen.

Nicht jedem Otto-Normal-Endanwender von µC-Schaltungen steht ein Flashgerät oder eine serielle Schnittstelle (UART) zur Verfügung, um Daten zum Mikrocontroller zu übertragen oder gar ein Firmware-Update durchzuführen. Was aber ein jeder PC-Anwender hat, ist eine Soundkarte.

Protokoll

Das zugrundeliegende Prinzip ist eigentlich schon 30 Jahre alt. Damals hat man in den C64/ZX-Spectrum die Programme oder Daten mit einem Kassettenrecorder geladen. Hier passiert eigentlich genau dasselbe: Die Daten werden in hörbare Töne gewandelt und dann an den µC übertragen. Zur Übertragung wird ein flankencodiertes Protokoll verwendet. Folgende Pegel-Längen werden verwendet:

Bit Pulsdauer
Start 9T
Stop 6T
Daten "1" 3T
Daten "0" 2T

Ein Datenblock besteht aus:

  • 2 × Startbit
  • Daten beliebiger Länge
  • 2 × Stoppbit

Im Empfängermodul ist eine Fehlererkennung enthalten. Es werden erkannt:

  • Frame Errors durch ungültige Pegel-Längen
  • Buffer Overflow Errors durch Überlaufen des Empfänger-Ringpuffers
  • Buffer Underrun (Timeout) durch Abbruch des Senders

In diesen Fällen wird der Empfang abgebrochen.

Dieses Programmpaket ist hauptsächlich für Endanwenderschaltungen gedacht, um Firmware-Updates zu ermöglichen, ohne dass sich der Endanwender eine komplette Entwicklungs-Hardware bzw. USB->Seriellwandler anschaffen muss. SOUNDRX kann aber auch zur einfachen Übertragung von Anwendungsdaten an einen µC dienen, die der µC weiterverarbeiten soll.

Der Anwender braucht lediglich ein simples Audiokabel mit zwei 3,5mm Klinkenstecker. Dazu zieht er sein Audiokabel einfach aus dem PC-Lautsprecher heraus und steckt es in die entsprechende Audio-Buchse der untenstehenden Schaltung, welche idealerweise mit auf der Anwendungsschaltung integriert ist.

Da die Daten als WAVE auf der PC-Soundkarte ausgegeben werden, ist kein spezieller Treiber für die Soundkarte notwendig. Es reicht sogar aus, dem Endanwender einfach die erzeugte WAVE-Datei zuzuschicken. Diese Datei kann dann von einem beliebigen WAVE-Player (unter Windows/Linux/Apple oder auch vom Billigst-MP3-Stick) an den µC gesendet werden.

Die gegenwärtige Übertragungsgeschwindigkeit beträgt ca. 2.000 Zeichen pro Sekunde. Das ist vergleichbar mit einer UART-Übertragungsrate von über 19200 Baud.

Schaltplan

Der Schaltplan ist sehr einfach gehalten. Man benötigt lediglich einen Kerko als Kondensator und ein Poti. Verwendet man am Eingang eine 3,5mm Klinkenbuchse, kann man ein Standard-Audiokabel, was jeder Soundkarte beiliegt, verwenden. Die Bauteile kosten in der Summe nur einige Cent. Die einfache Schaltungsvariante benötigt eine Kalibrierung, um das Poti exakt auf die Schaltschwelle des verwendeten ATmega einzustellen. Die Luxusvariante benötigt dagegen keine Kalibrierung.

Schaltplan 1 der Version 1.5.0 (Einfachst-Variante, benötigt Kalibrierung)

Einfache Schaltungsvariante

Name Wert
C1 100nF
R1 Trimmpoti 10K



Luxusversion

Schaltplan 2 der Version 1.5.0 (Luxusversion, benötigt keine Kalibrierung)
Name Wert
C1, C2 1µF
IC1 LM358
R1, R2, R6, R7, R8 100K
R3 4,7K
R4, R5 1K

Beide Schaltungen wurden ausführlich im Thread Schaltschwelle bei ATmega digital Eingang diskutiert. Vielen Dank an Klaus De lisson (kolisson) für den Entwurf der OpAmp-Schaltung.

Die Lautstärke der PC-Soundkarte sollte auf einen Pegel zwischen 75% und 100% eingestellt werden.

Download

Source-Code

Der Source-Code lässt sich einfach kompilieren, indem man die Projekt-Datei sndrx.aps in den WinAVR, bzw. die sndtx.sln in Microsofts Visual Studio lädt.

Quellcode-Dateien auf der µC-Seite

  • soundrx.c - Das Empfängermodul
  • soundrx.h - Zugehörige Headerdatei
  • soundrxconfig.h - Konfigurationsdatei
  • main.c - Demoprogramm

Konfiguration

Die Konfiguration besteht lediglich aus dem Anpassen der Preprozessor-Konstanten in soundrxconfig.h.

Diese sind:

Samples pro Sekunde

Dieser Wert sollte normalerweise nicht geändert werden, siehe auch Wahl der Samples/sec:

// wave format: samples per second, min is 12500, max. is 44100
#define SNDRX_F_SAMPLES    41667

Größe des Ringbuffers

Dieser Wert kann zwischen 0 und 255 eingestellt werden:

// size of ringbuffer, if == 0, ringbuffer will be disabled
#define SNDRX_RINGBUFSIZE    32

Mit dem Wert 0 wird der Ringbuffer deaktiviert. Dies ist bei zeitkritischen Anwendungen nicht zu empfehlen, da sonst Zeichen verloren gehen können und SOUNDRX dann die Übertragung mit einem Overflow-Error abbricht.

Konfiguration des Eingang-Pins

#define SNDRX_PRT     'B'   // Port B
#define SNDRX_BIT      2    // use PB2 as sound input

Konfiguration der Kalibrierung

→ Siehe auch Kalibrierung

// flag: 0: don't calibrate, 1: calibrate, default is 0
#define SNDRX_CALIBRATE            0
// Port of calibration LED
#define CALIBRATE_LED_PORT         PORTD
#define CALIBRATE_LED_DDR          DDRD
#define CALIBRATE_LED              6


Logging

Nur zum Debuggen notwendig, nicht ändern.

// flag: 0: don't log, 1: log on uart, default is 0
#define SNDRX_LOGGING              0

Taktfrequenz

Die Taktfrequenz des AVR µCs sollte mindestens 8 MHz betragen. Das Projekt ist eingestellt auf einen ATmega168 mit 8 MHz.

Demo-Programm

Im Programmpaket ist eine einfache Main-Funktion als Demo-Programm enthalten, um die Funktion von SOUNDRX zu demonstrieren. Hier ein Auszug:

int main (void)
{
    uint8_t ch;

    sndrx_init();
    led_init ();

    sei ();
    uart_puts ("Hello, this is sndrx.\r\n");

    while(1)
    {
        if (sndrx_poll (&ch, 0) > 0)
        {
            uart_putc (ch);
        }
    }

    return 0;
}

Die Schleife liest die über die Soundkarte gesendeten Zeichen ein und gibt sie hier über den UART mit 38400 Bd wieder aus. Der ausführliche Code inklusive Fehlerbehandlung steht in main.c.

Auf dem PC kann man nun eine beliebige Textdatei an den µC senden mit folgendem Befehl:

 sndtx.exe Dateiname

Dabei wandelt sndtx.exe die Daten zunächst intern in eine WAVE-Datei im Speicher um und spielt diese dann über die Soundkarte ab.

Wenn man die Datei nur wandeln, aber nicht senden möchte, ruft man sndtx.exe folgendermaßen auf:

 sndtx.exe Dateiname Dateiname.wav

Dann kann man anschließend die Datei Dateiname.wav mit einem x-beliebigen WAVE-Player unter Windows/Linux/Apple oder über einen Billigst-MP3-Stick abspielen und somit an den µC übertragen.

Beispiel:

 sndtx.exe README.txt

Möchte man eine niedrige Übertragungsrate benutzen, kann man die Anzahl der Samples pro Sekunde einstellen. Der Standard-Wert ist 41667. Beachte hierzu Wahl der Samples/sec.

Beispiel:

 sndtx.exe -s 31250 README.txt

Mit einem einfachen Terminalprogramm (z.B. PuTTY) kann man sich nun beim µC-Demoprogramm den Inhalt der Textdatei anschauen, welcher vom µC über den UART als echo wiedergegeben wird. Da die Übertragungsgeschwindigkeit von SOUNDRX mit über 2.000 Zeichen pro Sekunde die übliche UART-Baudrate von 19200 Baud übersteigt, muss das Terminalprogramm auf 38400 Baud eingestellt werden. Sonst kommt es zu einem Buffer-Overflow in SOUNDRX beim Einlesen der Daten. Ein solcher Buffer-Overflow wird von SOUNDRX zuverlässig erkannt. Der Status kann dazu vom Anwendungsprogramm ausgewertet werden. Im Beispiel wird dann "overflow error" auf dem UART ausgegeben. Bricht der Anwender die Übertragung von sndtx.exe durch Drücken der Tastenkombination STRG-C ab, wird auch dies von SOUNDRX erkannt und als Buffer Underrun bzw. Timeout gemeldet.

Hier als Auszug die Fehlerbehandlung aus main.c, also dem µC-Beispiel-Programm:

    switch (sndrx_status ())          // get current status
    {
        case SNDRX_INFO_IDLE:         // SNDRX is idle
        {
            LED_GREEN_OFF;            // switch TRANSMIT LED OFF
            break;
        }
        case SNDRX_INFO_TRANSMIT:     // SNDRX receives data
        {
            LED_GREEN_ON;             // switch TRANSMIT LED ON
            LED_RED_OFF;              // switch ERROR LED OFF
            break;
        }
        case SNDRX_ERROR_FRAME:       // SNDRX got a frame error
        {
            LED_GREEN_OFF;            // switch TRANSMIT LED OFF
            LED_RED_ON;               // switch ERROR LED ON
            uart_puts ("\r\nframe error!\r\n");
            break;
        }
        case SNDRX_ERROR_OVERFLOW:    // SNDRX detected an overflow error
        {
            LED_GREEN_OFF;            // switch TRANSMIT LED OFF
            LED_RED_ON;               // switch ERROR LED ON
            uart_puts ("\r\noverflow error!\r\n");
            break;
        }
        case SNDRX_ERROR_TIMEOUT:     // SNDRX detected a buffer underrun/timeout error
        {
            LED_GREEN_OFF;            // switch TRANSMIT LED OFF
            LED_RED_ON;               // switch ERROR LED ON
            uart_puts ("\r\ntimeout error!\r\n");
            break;
        }
    }

Quellcode-Dateien für den PC:

  • sndtx.c - Sendermodul für die Soundkarte
  • sndtx.h - Zugehörige Headerdatei
  • main.c - Demoprogramm

Das main-Modul kann einen beliebigen Buffer an SOUNDRX übergeben, welcher dann auf der Soundkarte als PCM-WAVE ausgegeben wird.

Aufruf der WAVE-Abspiel-Funktion:

    sound_play (unsigned char * buf, long bufsize, int samples, char * target)

Argumente:

    unsigned char * buf;            // Buffer
    long            bufsize;        // Buffergröße
    int             samples;        // Samples, 44100 ist Maximum
    char *          target;         // optional: Name von Zieldatei.wav

Die Beispiel-Main-Funktion liest dazu die angegebene Datei in einen Buffer ein und übergibt sie an die obige Funktion, welche die Daten dann über die Soundkarte ausgibt.

Beispiel-Kommando:

  sndtx.exe README.txt

Das PC-Programm zeigt dann auf der Eingabeaufforderungs-Konsole beispielsweise folgendes:

 buffer size: 27014 wave size: 1104938 time: 00:25 speed: 1080 bytes/sec
 TIME: 00:01 ETC: 00:24   4%

Die letzte Zeile wird auf der Konsole fortlaufend aktualisiert.

Der allgemeine Aufruf von sndtx.exe lautet:

  sndtx.exe [-s samples] Dateiname [Dateiname.wav]

wobei die in Klammern angegebenen Argumente optional sind. Standardwert für samples ist 41667.

Kalibrierung

LED zur Kalibrierung

Verwendet man die einfache Schaltungsvariante mit 1 Kerko und 1 Poti, muss das Potentiometer exakt auf die Schaltschwelle des verwendeten Mikrocontrollers eingestellt werden.

Dafür ist folgendes Vorgehen nötig:

  • SNDRX_CALIBRATE in sndrxconfig.h konfigurieren, siehe Konfiguration der Kalibrierung
  • Demoprogramm neu übersetzen und flashen
  • PC-Programm aus der Eingabeaufferderung aufrufen mit:
       sndtx.exe -c 

Die LED wird nun mehr oder minder hell leuchten. Nun stellt man das Timmpotentiometer so ein, dass ein Maximum an Helligkeit angezeigt wird. Die Schaltschwelle des ATmega liegt knapp unterhalb der halben Betriebsspannung. Damit ist die Kalibrierung auch schon beendet. Man kann das PC-Programm dann mit STRG-C abbrechen.

Bei der Verwendung der "Luxusversion" der Schaltung ist kein Abgleich notwendig.

Hello World für SOUNDRX

Hier noch als einfaches Beispiel die Version von "Hello World" für SOUNDRX:

#include "soundtx.h"

int
main (int argc, char ** argv)
{
    char     buf[64];
    int      bufsize;
    int      samples;
    char *   target;

    strcpy (buf, "Hello, world\r\n");   // Bufferinhalt
    bufsize = strlen (buf);             // Größe des Buffers
    samples = 41667;                    // Samples: 44100 ist Maximum
    target = (char *) NULL;             // optional: Name von ZielDatei.wav

    sound_play (buf, bufsize, samples, target);
    return (0);
}

Wahl der Samples/sec

Der aufmerksame Leser wird sich schon gefragt haben, wie der Standardwert von 41667 Samples/sec zustandekommt.

Die Antwort:

Der Timer 1 des ATmega läuft mit einem Prescaler von 64. Damit kommt man auf eine Grundfrequenz F = 8MHz / 64 = 125kHz. Damit möglichst ganzzahlige Werte beim Messen der Pegellängen herauskommt, sollten Samplefrequenzen gewählt werden, die ein ganzzahliges Verhältnis zu F = 125kHz (bei F_CPU = 8MHz) haben.

Hier eine Tabelle für verschiedene Werte von F_CPU und F_SAMPLES:

Samples Mittlere
Übertragungs-
rate
[Zeichen/s]
8MHz 12MHz 16MHz 20MHz
41667 2083 * *
39063 1953 *
37500 1875 *
35714 1786 *
31250 1563 * * * *
20833 1042 * * * *
15625 781 * * * *
12500 625 * * * (*)

Der Wert 41667 Samples/sec ist also optimal für ATmegas mit 8MHz und 16MHz. Der höchstmögliche Wert, der für alle 4 oben aufgeführten CPU-Frequenzen gut funktioniert, liegt bei 31250 Samples/sec. Alledings sinkt dann die mittlere Übertragungsgeschwindigkeit auf 1563 Zeichen/sec.

Man sollte bedenken, dass die angegebenen Übertragungsgeschwindigkeiten lediglich Mittelwerte sind. Enthalten die zu übertragenen Daten viele binäre Nullen, erhöht sich die Geschwindigkeit um den Faktor 1,25. Umgekehrt kann sich die Geschwindigkeit um den Faktor 0,83 ändern, wenn die Daten sehr viele binäre Einsen enthalten. Die oben angegebene mittlere Geschwindigkeit von 2083 ch/sec schwankt in Wirklichkeit zwischen 1736 ch/sec und 2605 ch/sec.

Bootloader

Ebenfalls enthalten ist ein Bootloader, mit dem sich der µC per Sound flashen lässt. Dazu muss die sndrx-bootloader.aps kompiliert und geflasht werden. Nach Resetten des µCs hat man 3 Sekunden Zeit, um unter Windows das Flashprogramm zu starten.

Allgemeiner Aufruf:

 sndflash.exe [-s samples] Dateiname.hex [Dateiname.wav]

wobei die in eckigen Klammern angegebenen Argumente optional sind. Im einfachsten Fall reicht:

 sndflash.exe Dateiname.hex

sndflash liest die Hex-Datei ein, überprüft die Daten auf Plausiblilität (Checksums etc.) und überträgt anschließend die Daten binär.

Aufbau:

  • Zeichen '$' als Start-Zeichen
  • Startadresse High-Byte
  • Startadresse Low-Byte
  • Anzahl der Pages High-Byte - 1 Page = 128 Byte
  • Anzahl der Pages Low-Byte
  • Datenblock, nach jeder Page ein zusätzliches Checksum-Byte

Mit sndflash.exe kann man auch eine WAVE-Datei erzeugen, statt die HEX-Datei direkt "abzuspielen". Dies geht folgendermaßen:

 sndflash.exe Dateiname.hex Dateiname.wav

Die erzeugte WAVE-Datei kann dann vom Endanwender mit einem beliebigen WAVE-Player abgespielt und somit an den µC übertragen werden.

Möchte man eine niedrige Übertragungsrate benutzen, kann man die Anzahl der Samples pro Sekunde auf einen niedrigeren Wert einstellen. Der Standard-Wert ist 41667.

Beispiel:

 sndflash.exe -s 22050 Dateiname.hex
LED zur Anzeige des Bootloader-Status'
Wichtig
In diesem Fall muss sndrxconfig.h auf der Empfängerseite (µC) entsprechend konfiguriert werden.

Auf der µC-Seite kann über eine LED der Status beim Flashen angezeigt werden. Dazu muss in sndrx-bootloader.c USE_LED gesetzt werden. Darunter kann der Port konfiguriert werden, an den die LED angeschlossen ist. In diesem Beispiel handelt es sich um PD6:

#define USE_LED         1

#define LED_PORT        PORTD
#define LED_DDR         DDRD
#define LED             6

Beim Start des µCs leuchtet dann die LED für 3 Sekunden auf, bevor der Bootloader-Vorgang abgebrochen wird und ins normale Anwendungsprogramm gewechselt wird.

Startet man innerhalb dieser 3 Sekunden das Flash-Programm auf dem PC, flackert die LED während der Übertragung auf, um anzuzeigen, dass die Daten angenommen und im Flash gespeichert werden. Nach dem Flashen erlischt die LED und es wird ins Anwendungsprogramm gesprungen. Tritt allerdings ein Übertragungsfehler auf, leuchtet die LED dauerhaft auf, um anzuzeigen, dass man nun das flash-Programm auf der PC-Seite neu starten muss, um den Flash-Vorgang zu wiederholen.

Weitere Entwicklung

Geplant sind momentan folgende Änderungen bzw. Verbesserungen:

Aufrufen einer Callback-Funktion
im Falle eines Übertragungsfehlers. Derzeit ist es lediglich möglich, eine LED im Falle eines Übertragungsfehlers aufleuchten zu lassen. Dies soll durch eine Callback-Funktion ersetzt werden, um dem Anwender die Fehlerbehandlung durch eigene Routinen zu ermöglichen.
Test mit MAX485 / SN75176
Test, ob RS485-Transceiver sich auch als Eingangsschaltung eignen.
Anschluss eines Kondensatormikrofons
Durch den Anschluss eines Mikrofons nebst Verstärkerschaltung könnte man die Daten auch drahtlos übertragen. Dafür wäre evtl. dieses Kondensatormikrofon geeignet.
Linux-Portierung des Sendemoduls
sndtx und sndflash auf Linux portieren.

Weitere Vorschläge zur Verbesserung des Programms oder der Schaltung können gerne im Forum gemacht werden.

Versionshistorie

10.07.2011 V 1.0.0

  • Erste Version

10.07.2011 V 1.1.0

  • sndtx.exe kann nun auch WAVE-Dateien erzeugen, die mit einem beliebigen WAVE-Player übertragen werden können.

14.07.2011 V 1.2.0

  • Neue Eingangsschaltung mit nur einem einem Kerko, drei Widerständen und einem Transistor
  • Geschwindigkeitserhöhung auf über 1000 Zeichen/sec
  • Bugfix in sndtx.exe beim Interpretieren der HEX-Datei (kurze Zeilen)
  • Standard-Eingangspin von D0 auf B2 geändert

17.07.2011 V 1.3.0

  • Binärübertragung für Bootloader, dadurch Verdoppelung der Flash-Geschwindigkeit
  • Neues PC-Flash-Programm sndflash.exe
  • Abschalten des Ringbuffers nun möglich mit SNDRX_RINGBUFSIZE = 0
  • Optimierung der ISR-Status-Variablen

20.07.2011 V 1.4.0

  • Pullup-Widerstand am Transistor wegoptimiert, übernimmt nun der ATmega.
  • Bootloader von der Größe her auf unter 1KB reduziert
  • Bugfix Bootloader: Timer wird nun vor dem Applikationsstart wieder deaktiviert
  • Bugfix Bootloader: Wenn die Übertragung fehlerhaft war, wird nun auf eine neue Übertragung gewartet

22.08.2011 V 1.5.0

  • Sendeformat auf flankencodiertes Protokoll umgestellt
  • Erhöhung der Übertragungsgeschwindigkeit auf ca. 2000 Zeichen/sec
  • Vereinfachung der Empfangsschaltung auf 1 Kondensator + 1 Poti
  • Kalibrierungsprogramm für die Schaltung
  • Checksums für Bootloaderübertragung eingebaut

Literatur