mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik MIDI mit MSP430F1611


Autor: Christian Z. (fiselgrulm)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo! Ich möchte ein MIDI-Interface aufbauen, das Teil eines Projektes 
mit dem MSP430F1611 ist. Es sollen Program Change und Bank 
Select-Messages gesendet und empfangen werden. Dafür wird der UART-Modus 
der USART0 benutzt. Das ganze soll folgendermaßen implementiert werden:

In einer Routine des Hauptprogramms sollen die MIDI-Parameter 
(Kanalnummer, Banknummer, Programmnummer) abgefragt werden. Mit dem Ende 
der Abfrage sollen die entsprechenen MIDI-Befehle über die UART gesendet 
werden. Der Empfang der gleichen Parameter soll bewirken, daß das 
Hauptprogramm darauf reagiert und in eine Subroutine springt.

Ich möchte jetzt mal Tips einholen, wie das ganze am besten zu 
realisieren ist. Ich kann zwar einigermaßen in C programmieren, aber 
diese µC-Geschichte mit ihren Interrupts ist für mich immernoch Neuland. 
Zumindest beim Empfang denke ich, müßte ein Interruptflag abgefragt 
werden, auf das das Programm reagiert.

Der Rumpfcode sieht momentan folgendermaßen aus:

Die UART0 wird für MIDI initialisiert; die Routine habe ich aus einem 
der TI-Beispielcodes und für meine Zwecke angepasst.
void MIDI_Init()
  {
    P3SEL |= 0x30;        // P3.4 und P3.5 --> TXD/RXD der USART0
    U0CTL &= ~(I2C+SYNC+I2CEN)  // noch gesetzte Bits des I²C-Modus löschen
                                // (wird in anderer Routine verwendet)
    U0CTL |= SWRST;         // Software-Reset der USART0
    ME1 |= UTXE0 + URXE0;       // TXD/RXD des USART0-Moduls aktivieren
    UCTL0 |= CHAR;       // 8-Bit Zeichenlänge
    UTCTL0 |= SSEL1;            // UCLK = SMCLK (4MHz)
    UBR00 = 0x80;               // Teilerfaktor für Baudrate einstellen
    UBR10 = 0x00;               // (4MHz / 128 = 31250bps)
    UMCTL0 = 0x00;              // keine Teilerkorrektur (Modulation) nötig
    UCTL0 &= ~SWRST;            // Software-Reset der USART0 aufheben
    IE1 |= URXIE0 + UTXIE0;     // RX- und TX-Interrupt für USART0 akt.
    IFG1 &= ~UTXIFG0;           // initales Interrupt-Flag löschen
  }

Das wäre mein Ansatz für eine Senderoutine:
void MIDI_Out(BYTE midi_ch, BYTE midi_bk, BYTE midi_pr)
  {
    if (midi_bk != 0xFF)  // falls Bank Select vorliegt
      {
        while (!(IFG1 & UTXIFG0)); // TX-Buffer der USART0 bereit?
          TXBUF0 = 0xB0 + midi_ch; // Statusbyte Control Change
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = 0x00;  // 1. D.Byte Bank Select (Contr. 0 - MSB)
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = 0x00;  // 2. D.Byte Bank Select (Banknummer MSB)
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = 0xB0 + midi_ch; // Statusbyte Control Change
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = 0x20;  // 1. D.Byte Bank Select (Contr. 32 - LSB)
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = midi_bk;  // 2. D.Byte Bank Select (Banknummer LSB)
      }
    if (midi_pr != 0xFF)  // falls Program Change vorliegt
      {
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = 0xC0 + midi_ch;  // Statusbyte Program Change
        while (!(IFG1 & UTXIFG0));
          TXBUF0 = midi_pr;    // Datenbyte Program Change(Programmnr.)
        while (!(IFG1 & UTXIFG0));
          IE1 &= ~UTXIE0;           // TX-Interrupt für USART0 deaktivieren
      }
  }

Ich bin allerdings auch auf solche Konstrukte gestoßen:
#pragma vector = UART0TX_VECTOR
__interrupt void usart0_tx(void)
  {

  }

Wie gesagt, ich bin auf dem Gebiet der Interruptprogrammierung völliger 
Neuling. Wie würde ich die zu sendenden Parameter denn in dem Fall an 
usart0_tx(void) übergeben? Was müßte zwischen den geschweiften Klammern 
stehen?

Der Empfang müßte doch sicherlich in jedem Falle in etwa so realisiert 
werden:
#pragma vector = USART0RX_VECTOR
__interrupt void usart0_rx(void)
  {
    // RXBUF0 abfragen und in Variable speichern?  
  }

Und wie rufe ich die Interruptroutine aus dem Hauptprogramm auf? Wäre 
schön, wenn mir hier jemand ein bißchen Hilfestellung geben könnte. 
Danke schonmal!

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wie gesagt, ich bin auf dem Gebiet der Interruptprogrammierung völliger
>Neuling. Wie würde ich die zu sendenden Parameter denn in dem Fall an
>usart0_tx(void) übergeben?
Brauchst du denn zwingend TX Interrupt?

>Und wie rufe ich die Interruptroutine aus dem Hauptprogramm auf?
Für den TX Interrupt müsstest du erst mal ein Zeichen in den TX Buffer 
schieben, der Rest würde dann über die Interrupt Routine laufen.


Aber erst mal Grundsatzfrage:
Musst du gleichzeitig senden und empfangen können? Wenn nein, ist auch 
kein Interrupt notwendig. Vielleicht höchstens um zu erkennen ob ein 
Zeichen angekommen ist. Aber selbst das lässt sich auch ohne Interrupt 
handhaben solange der µC sonst nichts zu tun hat.

Autor: Christian Z. (fiselgrulm)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Brauchst du denn zwingend TX Interrupt?

Hmm, beim senden, denke ich, brauche ich ihn nicht. Es ist so, daß ich 
am Gerät MIDI-Kanal, Bank und Programm abfrage, und mit dem Bestätigen 
der Abfrage raussende.

Also rufe ich dafür eine Funktion auf, die die Parameter in den 
TX-Buffer schreibt?

> Musst du gleichzeitig senden und empfangen können? Wenn nein, ist auch
> kein Interrupt notwendig. Vielleicht höchstens um zu erkennen ob ein
> Zeichen angekommen ist. Aber selbst das lässt sich auch ohne Interrupt
> handhaben solange der µC sonst nichts zu tun hat.

Ich muß nicht gleichzeitig senden und empfangen können. Wie gesagt, das 
Senden geschieht nur auf eine Eingabe am Gerät hin. Auf Empfang soll 
aber laufend im Hintergrund "gelauscht" werden. Wenn ein Signal am 
MIDI-In ankommt, soll das Programm darauf reagieren und in eine 
Unterroutine springen. Der µC ist währenddessen schon beschäftigt, die 
MIDI-Sache ist nur ein Teil der Anwendung.

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Also rufe ich dafür eine Funktion auf, die die Parameter in den
>TX-Buffer schreibt?
Deine Senderoutine (ohne Interrupt) hast du ja schon geschrieben. Die 
könnte man so lassen. UTXIE0 musst du allerdings weg machen.

>Auf Empfang soll aber laufend im Hintergrund "gelauscht" werden.
Es gibt natürlich etliche Möglickeiten sowas zu programmieren. Einfach 
gehalten könnte es so gehen (ungetestet):
#pragma vector = USART0RX_VECTOR
__interrupt void usart0_rx(void)
{
  unsigned int timeout;

  while (timeout < 30000)  // Zahl muss angepasst werden
  {
    if (IFG1 & URXIFG0)
    {
      uart_buffer[byte_cnt++] = U0RXBUF;
      timeout = 0;
    }

    timeout++;
  }
}

Autor: Christian Z. (fiselgrulm)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Deine Senderoutine (ohne Interrupt) hast du ja schon geschrieben. Die
> könnte man so lassen. UTXIE0 musst du allerdings weg machen.

Ok, danke dir.

Der Timeout in der Empfangsroutine dient dazu, wieder zurück ins 
Programm zu springen, wenn er nicht mehr durch eingehende Daten 
zurückgesetzt wird, richtig?

Ich werde es mal so probieren.

Autor: Jörg S. (joerg-s)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Der Timeout in der Empfangsroutine dient dazu, wieder zurück ins
>Programm zu springen, wenn er nicht mehr durch eingehende Daten
>zurückgesetzt wird, richtig?
Richtig, ja.
In der Hauptroutine müsste dan auf "byte_cnt > 0" getestet werden um zu 
erkennen das Daten angekommen sind.
Ich sehe gerade das die Initialisierung von timeout noch fehlt. Also 
noch ein "timeout = 0;" vor die while Schleife.

Autor: Christian Z. (fiselgrulm)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kam bis jetzt nur dazu, den MIDI-Out zu testen, also die 
Senderoutine. Mit meinem Ansatz oben funktioniert es nicht, P3.4 liegt 
dauerhaft auf LOW. Ich weiß nun nicht, liegt es an der Initialisierung 
oder an der Senderoutine?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.