Forum: Mikrocontroller und Digitale Elektronik MIDI mit MSP430F1611


von Christian Z. (fiselgrulm)


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.
1
void MIDI_Init()
2
  {
3
    P3SEL |= 0x30;        // P3.4 und P3.5 --> TXD/RXD der USART0
4
    U0CTL &= ~(I2C+SYNC+I2CEN)  // noch gesetzte Bits des I²C-Modus löschen
5
                                // (wird in anderer Routine verwendet)
6
    U0CTL |= SWRST;         // Software-Reset der USART0
7
    ME1 |= UTXE0 + URXE0;       // TXD/RXD des USART0-Moduls aktivieren
8
    UCTL0 |= CHAR;       // 8-Bit Zeichenlänge
9
    UTCTL0 |= SSEL1;            // UCLK = SMCLK (4MHz)
10
    UBR00 = 0x80;               // Teilerfaktor für Baudrate einstellen
11
    UBR10 = 0x00;               // (4MHz / 128 = 31250bps)
12
    UMCTL0 = 0x00;              // keine Teilerkorrektur (Modulation) nötig
13
    UCTL0 &= ~SWRST;            // Software-Reset der USART0 aufheben
14
    IE1 |= URXIE0 + UTXIE0;     // RX- und TX-Interrupt für USART0 akt.
15
    IFG1 &= ~UTXIFG0;           // initales Interrupt-Flag löschen
16
  }

Das wäre mein Ansatz für eine Senderoutine:
1
void MIDI_Out(BYTE midi_ch, BYTE midi_bk, BYTE midi_pr)
2
  {
3
    if (midi_bk != 0xFF)  // falls Bank Select vorliegt
4
      {
5
        while (!(IFG1 & UTXIFG0)); // TX-Buffer der USART0 bereit?
6
          TXBUF0 = 0xB0 + midi_ch; // Statusbyte Control Change
7
        while (!(IFG1 & UTXIFG0));
8
          TXBUF0 = 0x00;  // 1. D.Byte Bank Select (Contr. 0 - MSB)
9
        while (!(IFG1 & UTXIFG0));
10
          TXBUF0 = 0x00;  // 2. D.Byte Bank Select (Banknummer MSB)
11
        while (!(IFG1 & UTXIFG0));
12
          TXBUF0 = 0xB0 + midi_ch; // Statusbyte Control Change
13
        while (!(IFG1 & UTXIFG0));
14
          TXBUF0 = 0x20;  // 1. D.Byte Bank Select (Contr. 32 - LSB)
15
        while (!(IFG1 & UTXIFG0));
16
          TXBUF0 = midi_bk;  // 2. D.Byte Bank Select (Banknummer LSB)
17
      }
18
    if (midi_pr != 0xFF)  // falls Program Change vorliegt
19
      {
20
        while (!(IFG1 & UTXIFG0));
21
          TXBUF0 = 0xC0 + midi_ch;  // Statusbyte Program Change
22
        while (!(IFG1 & UTXIFG0));
23
          TXBUF0 = midi_pr;    // Datenbyte Program Change(Programmnr.)
24
        while (!(IFG1 & UTXIFG0));
25
          IE1 &= ~UTXIE0;           // TX-Interrupt für USART0 deaktivieren
26
      }
27
  }

Ich bin allerdings auch auf solche Konstrukte gestoßen:
1
#pragma vector = UART0TX_VECTOR
2
__interrupt void usart0_tx(void)
3
  {
4
5
  }

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:
1
#pragma vector = USART0RX_VECTOR
2
__interrupt void usart0_rx(void)
3
  {
4
    // RXBUF0 abfragen und in Variable speichern?  
5
  }

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!

von Jörg S. (joerg-s)


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.

von Christian Z. (fiselgrulm)


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.

von Jörg S. (joerg-s)


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):
1
#pragma vector = USART0RX_VECTOR
2
__interrupt void usart0_rx(void)
3
{
4
  unsigned int timeout;
5
6
  while (timeout < 30000)  // Zahl muss angepasst werden
7
  {
8
    if (IFG1 & URXIFG0)
9
    {
10
      uart_buffer[byte_cnt++] = U0RXBUF;
11
      timeout = 0;
12
    }
13
14
    timeout++;
15
  }
16
}

von Christian Z. (fiselgrulm)


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.

von Jörg S. (joerg-s)


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.

von Christian Z. (fiselgrulm)


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?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.