Forum: Mikrocontroller und Digitale Elektronik ATMEGA8 uart/midi Empfangen


von Urban M. (stromlos)


Lesenswert?

Hallo Zusammen,
ich habe mir hier schon so manchen Tip geholt, komme aber an einer 
Stelle nicht weiter.Manchmal fehlt einfach das richtige Suchwort und da 
wäre ich um jeden Tip dankbar. Ich finde jede Menge zu Midi senden, aber 
kaum etwas zum Empfangen.

Ich baue eine Midi-Steuerung für Gitarrenverstärker, das empfängt 
Programm Change und schaltet dann entsprechend ein paar Relais.

Ich habe einen fertigen Bausatz, der Funktioniert auch gut, jedoch war 
ich mit der Programmierung nicht einverstanden und so fing das Theater 
an :-)

Ich habe bisher ein Programm gebaut, das an PD2 und PD3 per Taster 4 
Relais hoch bzw. runter schaltet. Das funktioniert einwandfrei soweit. 
Es ist immer ein Relais geschaltet.

Dann habe ich eine Uart mit IRQ Steurung eingebaut und im AVR Simulator 
IDE getestet, da sende ich 0xCX 0x01, das wäre Programmchange auf Kanal 
X und Programmnummer 1. Funktioniert.

Nun habe ich das Ganze auf den Chip geflusht und da klemmt es irgendwo 
:-)

Da ich noch keinen Weg gefunden habe, auf dem Chip zu debuggen, habe ich 
folgendes gemacht. Immer wenn ein Zeichen anliegt, schalte ein Relais 
weiter und schreibe das Zeichen in den eeprom und wenn nichts mehr 
kommt, schreibe eine 0 ins eeprom (als terminator).

Beim Testen passiert folgendes: Bei jedem Programmchange, wird nur 
einmal geschaltet (ich hätte zweimal erwartet) Also bekomme ich immerhin 
mit, das was am Midi passiert.

Das steht im eeprom: 0000EA00F200F200F200FA00, ich habe Programm 
50,51,52 usw gesendet.

Hier mal der testcode Ausschnitt:
(das uart_putc ist für den Simulator)
1
int main(void)
2
{
3
4
  program = 0;
5
  gotChange = 0;
6
  uart_init();
7
  sei();
8
9
  while (1){
10
  if (uart_data_received()) {
11
      switchChannel(UP);//schaltet eine Relais weiter -> klack
12
        // Zeichen wurde empfangen, jetzt abholen
13
        uint8_t c;
14
        c = uart_getc();
15
      uart_putc(c);   
16
      eeprom_write_byte((uint8_t*)program++,c);
17
      gotChange = 1;
18
        
19
      }
20
      else if (gotChange == 1) {
21
      gotChange = 0;
22
      eeprom_write_byte((uint8_t*)program++,0x00);
23
    }
24
  }
25
}

von Karl H. (kbuchegg)


Lesenswert?

Urban M. schrieb:

> Hier mal der testcode Ausschnitt:
> (das uart_putc ist für den Simulator)

Der hilft dir nicht viel.
Denn im Simulator ist immer alles perfekt: Die Taktfrequenz ist genau 
die, die du eingestellt hast, etc. etc.

Meines erachtens ist es ein Fehler, wenn man bei der UART gleich mit dem 
Empfang loslegt.
Die andere Richtung (unter mithilfe eines PC mit einem Terminalprogramm 
drauf) ist viel sinnvoller als erstes in Betrieb zu nehmen.

Meine Empfehlung wäre:
Sieh zu, dass du vom AVR aus per UART etwas wegschickst und sieh zu, 
dass du am PC mit einem Terminalprogramm das dann auch sichtbar kriegst.
Und solange du am PC nicht genau das siehst, was am AVR weggeschickt 
wird (zb ein simples 'x'), solange hast du ein Problem mit der Baudrate, 
welches in den meisten Fällen auf eine falsche Taktversorgung des µC 
zurückzuführen ist.

Wenn das dann klappt, dann klappt auch die Gegenrichtung im Regelfall 
sofort.

Alles andere ist stochern im Nebel.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Alternativ könnte man versuchen, erst mal abzuklären, ob die 
Taktfrequenz des AVR auch wirklich die ist, mit der man die Faktoren bei 
der UART Initialisierung berechnet hat.
Ob der Takt 1Mhz ist, oder 4Mhz oder 8 Mhz oder 16Mhz kann man mit einem 
einfach _delay_ms ermitteln. Schwieriger wird es zwischen 11Mhz und 
12Mhz zu unterscheiden. Aber den Fall hat man eigentlich nicht. Denn man 
weiss ja, was auf dem Quarz drauf steht und die Alternative ist ja dann 
nur, ob der AVR noch mit den Default 1Mhz läuft, oder ob der Quarz aktiv 
ist und der AVR mit 11Mhz läuft. Das wiederrum lässt sich mit einer LED 
und delay-gesteuertem Delay leicht überprüfen.

: Bearbeitet durch User
von Urban M. (stromlos)


Lesenswert?

Erst mal danke für die schnelle Antwort, ich programmiere das Teil im 
Moment mit der internen 1Mhz Frequenz und habe das in den Optionen und 
Fuses eingestellt, bzw belassen. Der Uart rechnet so:
1
#define UBRR_BAUD ((F_CPU/(16L*UART_BAUD_RATE))-1)

Kann aber gut sein, das da noch etwas nicht stimmt. ich habe mir die 
Terminal.exe gesaugt und bekomme immerhin eine Verbindung, empfangen tue 
ich erst mal nix :-)....da muss ich mich noch mit beschäftigen.

Gibt es da Standard Unix Tools, die ggf besser geeignet sind? Ich habe 
hier OSX und bekomme auch fast all Pinguinableger zum Laufen.

von Karl H. (kbuchegg)


Lesenswert?

Denk auch bitte daran, dass das Schreiben ins EEPROM langsam ist.

Beim Mega32 (das Datenblatt hab ich gerade offen) gibt Atmel an, dass es 
rund 8400 Takzyklen dauert. Bei 1Mhz sind das immerhin  ca 8.5ms. Da 
muss die Baudrate gar nicht so hoch sein, dass dir die UART Übertragung 
das Programm 'überläuft'.

Ev. das empfangene lieber erst mal im SRAM zwischenspeichern und dann zb 
auf Knopfdruck ins EEPROM transfierieren, damit du mit dem Brenner da 
rann kommst.

: Bearbeitet durch User
von Urban M. (stromlos)


Lesenswert?

So, ich habe mal weiter gegoogelt und das ist vermutlich mein 
Gedankenfehler gewesen......Empfangen :-)

Mit Deinen Infos: UART - Terminal - Senden - Problem, finde ich nun eine 
ganze Menge und werde mich da erst mal langsam durchwurschteln, und ein 
Testprogramm bauen, was ausschließlich mit einem Terminal quasselt.

Wenn das klappt, habe ich wohl die Baudrate gefunden und einen kleinen 
Schritt weiter gemacht.

Ich verkrieche mich mal in die Testhöhle, I'll keep u updated.

von Urban M. (stromlos)


Lesenswert?

Danke, Deine Antwort kam als ich geschrieben habe :-)

Das eeprom Schreiben habe ich erst später eingebaut, ist aber ein Tip, 
die empfangenen Zeichen erst mal zu Puffern und dann auf Knopfdruck ins 
eeprom schreiben.

von Urban M. (stromlos)


Lesenswert?

Ich bin wohl ein Stück weiter und denke es liegt an der Baudrate. Midi 
spielt mit 9600 und das bekommt man mit 1MHz offensichtlich nicht hin. 
Der Bausatz läuft mit 7.3728 MHz, also werde ich einen entsprechenden 
Ozi dranhängen. Da kann man aber viel falsch machen, ergo werde ich viel 
Spaß haben :-)

Um mit einem Terminal zu quasseln benötige ich wohl andere Hardware, 
daher habe ich das erst mal verworfen. Grundsätzlich bekomme ich ja Midi 
Signale...eben nur unsinnige....

Diese Links sind brauchbar für mein Projekt und will sie hier auch 
teilen, falls noch jemand über diesen thread stolpert.

Midi Controller:
http://www.oldcrows.net/~patchell/smb1/midi.pdf

Das Gegenstück, das Program Change Fussbrett:
https://github.com/eepp/avr-midi-footswitch/blob/master/main.c

Baudratenrechner:
http://www.gjlay.de/helferlein/avr-uart-rechner.html

Der Midi Bausatz:
http://www.tube-town.net/ttstore/Bausaetze/Sonstige-Bausaetze/Kit-Midi-Kanalumschalter::6193.html

Der Bausatz ist sehr gut, lediglich die Tastersteuerung gefällt mir 
nicht so gut.

Die Midi Quellcodes muss ich mir Stückweise zu Gemüte führen, aber mit 
dem Uart Tutorial hier, klappt das schon. Sobald ich was habe, melde ich 
mich noch mal.

von Jonas B. (jibi)


Lesenswert?

>Midi spielt mit 9600

Nö, eigentlich sind es 31250.

Gruß J

von Urban M. (stromlos)


Lesenswert?

So ein dröges "Nö" enttäuscht mich aber lach

Die Richtigkeit meiner Quellen konnte ich noch nicht prüfen, jedenfalls 
bekomme ich 31250 noch weniger sauber hin mit 1MHz

von spess53 (Gast)


Lesenswert?

Hi

>Die Richtigkeit meiner Quellen konnte ich noch nicht prüfen, jedenfalls
>bekomme ich 31250 noch weniger sauber hin mit 1MHz

31250Bd bei 1MHz bekommst du sogar mit einem Fehler von genau 0% hin.

MfG Spess

von Urban M. (stromlos)


Lesenswert?

Der schlaue Baudrechner sagt alles was über 4800 liegt, geht 
nicht...sauber :-).....aber das werde ich noch mal testen.

Mit den oben geposteten Quellen werde ich mein Midi Geraffelt noch mal 
neu bauen und mir für die Baudraten mal einen Test machen, dass ich die 
ggf on the fly ändern kann....egal ob das klappt oder nicht...hinterher 
bin sogar ich schlauer :-)

von spess53 (Gast)


Lesenswert?

Hi

>Der schlaue Baudrechner sagt alles was über 4800 liegt, geht
>nicht...sauber :-).....aber das werde ich noch mal testen.

Dann tauge er nichts. Midi hat eine Bitlänge von 32 µs. Da die UART 16 
mal pro Bit abtastet geschieht das alle 2µs, also mit einer Freqenz von 
500kHz. Und 500kHz sind nunmal 1MHz/2. Macht beim AVR ein UBRR von 1.

MfG Spess

von Urban M. (stromlos)


Lesenswert?

Et funzt nu, ich habe alles richtig gemacht, bis auf...
1
 #define BAUD 9600UL
Getreut dem Motto, real men never read a manual, habe ich das nie in 
Frage gestellt. :-)

Hätte Jonas gleich gesagt: Hau dir mal eine selber rein und mach das:
1
 #define BAUD 31250UL
wäre alles gut gewesen.


Ich habe das ganze uart Geraffelt noch mal neu geschrieben und mir alle 
Zeilen die ich nicht kapiert habe noch mal nachgelesen....nun bin sogar 
ich schlauer.

Ich teste noch an Optimierungen und werde dann mein Beispiel mal 
anhängen.

Besten Dank für die Tips.

von Urban M. (stromlos)


Angehängte Dateien:

Lesenswert?

So, ich habe das Programm nun soweit, das ich es hier anhängen 
kann....falls noch Jemand über das Problem stolpert, sollte ja auch eine 
Lösung zu finden sein.

Midi Program_Change empfangen und darauf reagieren, sowie passend zum 
Programm die Daten speichern......es ist jedoch nicht durchgetestet, da 
ich aus meinem Ungetüm einfach alles was nicht hierher passt entfernt 
habe.

von Karl H. (kbuchegg)


Lesenswert?

BIst du an Kritik interessiert?

Das hier
1
void enableIRQ(unsigned char receiveOnly )
2
{
3
  if (midiEnabled == 1) return;
4
  if (receiveOnly) UCSRB = (( 1 << RXCIE ) | ( 1 << RXEN ) ); 
5
  else UCSRB = (( 1 << RXCIE ) | ( 1 << RXEN ) | ( 1 << TXEN )); 
6
  midiEnabled = 1;
7
}
(ich meine die Variable midiEnabled) ist den Aufwand nicht wert. Die 
ganze Abfragerei dieser Variablen bzw. Setzen derselben kostet dich 
mehr, als wie wenn du ganz einfach in den Registern die Bits setzt. Der 
UART Einheit ist das sowieso egal, wenn du bereits gesetzte Bits nochmal 
setzt.
1
void enableIRQ(unsigned char receiveOnly )
2
{
3
  UCSRB = ( 1 << RXCIE ) | ( 1 << RXEN );
4
  if( !receiveOnly )
5
    UCSBR |= ( 1 << TXEN );
6
}

selbiges beim disablen.
Den Funktionsnamen find ich nicht so prickelnd. Denn eigentlich wird ja 
hier die UART als ganzes aktiviert und nicht nur der IRQ.

Hier
1
unsigned char readByte( void )
2
{
3
  unsigned char newTail;
4
 
5
    newTail = (UART_RxTail + 1) & UART_RX_BUFFER_MASK;
6
    UART_RxTail = newTail; 
7
 
8
    return UART_RxBuf[newTail];// return buffer byte
9
}
hast du eine Race Condition. Du setzt UART_RxTail zu früh auf den neuen 
Wert. Wenn dir zwischen der Zuweisung an UART_RxTail und dem abholen der 
Daten aus dem Array ein UART Empfangs Interrupt dazwischenknallt UND der 
Ringbuffer bereits voll ist, dann überschreibt dir der Empfangsinterrupt 
genau das eine Byte im Array, das du gerade im Begriff bist zu holen.

OK. der Fall wird bei dir wahrscheinlich so nicht vorkommen und wenn der 
Ringbuffer voll ist, hast du ganz andere Probleme. Trotzdem ist es eine 
Racecondition.
1
SIGNAL(SIG_UART_RECV)         
2
{
SIGNAL ist seit vielen, vielen Jahren veraltet.


1
void checkEeprom()
2
{
3
....
4
    byte = 0x00;
5
    eeprom_write_byte((uint8_t*)ADR_GLOBAL,byte);

du verschleierst hier mehr, als du gut machst. Warum nicht einfach
1
    eeprom_write_byte( (uint8_t*)ADR_GLOBAL, 0x00 );

und warum nicht den cast gleich in das ADR_GLOBAL Makro mit integrieren?
1
#define ADR_GLOBAL ((uint8_t*)128) //adress where globals are saved
2
#define ADR_DETECT ((uint8_t*)129) //test byte, if eeprom is init
für etwas anderes als die Adressierung ins EEPROM kannst du die beiden 
Makros sowieso nicht gebrauchen (zumindest in der überwiegenden Mehrzahl 
der Fälle)
1
      //show write effect clean blinks
2
      for (int i = 1; i < 5;i++) {      
3
        clean(true);
4
        _delay_ms(PAUSE - 100);  
5
        clean(false);
6
      }
wozu der int?
Abgesehen davon: das da was blinkt ist theoretisch. Wenn du nicht gerade 
Superman bist, wirst du da nichts blinken sehen. Da fehlt ein 2.ter 
delay. Blinken besteht nun mal aus 2(!) Zuständen. Und beide müssen 
entsprechend lange anliegen, damit langsame Menschen die beiden auch 
sehen können.

: Bearbeitet durch User
von Urban M. (stromlos)


Lesenswert?

Klaro bin ich an Kritik interessiert, immer feste druff :-)

Grundsätzlich optimiere ich noch an dem Code und Hilfe kann nie schaden.

midiEnabled habe ich eingeführt, weil mir nicht ganz klar ist ob und wie 
viel Zeit das setzen der Bits kostet. Ich mache sonst GUI Programme und 
bin paranoid, wenn etwas unnötig verzögert und ggf. die GUI einfriert 
:-)

Das ist auch nur drin, weil ich alle PIN's für Schalter brauche und 
eigentlich gar nicht senden will. Luft nach oben ist immer vorhanden...

readByte hast Du uneingeschränkt recht :-)

Dem Signal geht es wie mir....völlig veraltet....muss ausgetauscht 
werden. Das ist never change a running code....Wie und wann welcher ISR 
aufgerufen wird, ist mir noch nicht so ganz klar, da ich das so gewohnt 
bin: connect(ptrUart,SIGNAL(sig_MidiDaten()), this, SLOT(onMidiDaten()) 
);

defines verbessern, ja...ich gelobe Besserung.

Die Loop zum Blinken ist tatsächlich Müll...habe ich so herunter 
getippt....da war vorher was Anderes, was aber das Textprogramm nur 
verkompliziert hätte...ein delay noch am Ende und gut ist, der int ist 
auch Gewohnheit ....ich tippe fast täglich for(int i =...... Das geht 
dann automatisch :-)

Danke

von Urban M. (stromlos)


Angehängte Dateien:

Lesenswert?

So ich habe noch mal ein Update gemacht und mich um die kleinen 
Problemchen oben gekümmert. Das
1
#define ADR_GLOBAL 128
 habe ich belassen, weil ich das an anderer Stelle noch mal als Zahl 
brauche und dann müsste ich zurückcasten.....nee :-)

Es ist jetzt jedoch eine gute Basis für meine 3 Projekte..

Kann man den Anhang oben löschen, ich kann das leider nicht mehr 
editieren?

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.