C Interrupt für USART-Empfang in C/C++?

analog

Neues Mitglied
16. Juli 2011
26
0
1
62
Bad König
Sprachen
  1. ANSI C
Hallo Leute,

nachdem ich beim mega328 (Arduino Duemilanove) erfolgreich mit einer Timer2-Interruptroutine arbeite, möchte ich nun eine zweite Interruptroutine nutzen, die gestartet werden soll, wenn die USART Daten empfangen hat. Beim Timer-Interrupt arbeite ich mit

Code:
ISR(TIMER2_OVF_vect)
{
    ...
}

Das hat mir mal irgendwer verraten, keine Ahnung woher das kommt, und in der Literatur habe ich nix darüber gefunden. Leider ist es mir nicht gelungen, diese Syntax erfolgreich auf die USART-Interruptroutine zu übertragen. Kann mir jemand brauchbare Literatur/Links über Interruptprogrammierung (in C/C++) empfehlen? Da bin ich auch im Arduino-Playground nicht wirklich fündig geworden. Kann mir da bitte mal jemand weiterhelfen, auch wenn die Frage vielleicht bisschen blöd ist???

Danke im voraus

LG von Andreas
 
Hallo Andreas,

schau dir vielleicht bei rn-wissen mal das Kapitel USART an, dort werden auch Interrupts behandelt.

Grüße,
Dirk
 
Tach auch.

1. Man sollte sich erstmal vorstellen ;-)
2. Bei solchen Fragen schaut man erstmal ins Datenblatt des Herstellers.

Dein Interrupt-Vector heißt USART_RX_VECT.

Drinne hast Du dann sowas in der Art:

Code:
ISR (USART_RX_vect) {
	unsigned char data;
	data = UDR0;
}

Was noch wichtig wäre, versuch die Interrupt-Routinen möglichst kurz zu halten.

Dass Du in der Arduino-Hilfe nichts gefunden hast, kann ich irgendwie nicht so ganz glauben.

Beim Initialisieren vom USART musst Du noch drauf achten, dass RX-Interrupt an ist.

Grüße
Heinrich
 
Funktioniert leider immer noch nicht ...

Danke für Eure beiden Beiträge. Leider funktioniert "es" (ein Testprogramm, das MIDI-Daten verarbeiten soll) in der Interrupt-Version immer noch nicht. Ich werde mal meinen kompletten Code zum Besten geben, wenn ich zurück bin, vielleicht habt Ihr ja ne Idee - werde aber morgen erstmal operiert, da ich mir bei nem Sturz vom Fahrrad vor einigen Tagen den linken Unterarm gebrochen habe. Keine Ahnung, wie lange sie mich im KKH behalten werden.

Und a propos Vorstellung: Heinrich, schau bitte mal in mein Profil ;-)

Bis hoffentlich bald

Andreas
 
....da ich mir bei nem Sturz vom Fahrrad vor einigen Tagen den linken Unterarm gebrochen habe. Keine Ahnung, wie lange sie mich im KKH behalten werden.

Hallo Andreas!

Willkommen im AVR-Praxis Forum! :ciao:

Autsch, die Geschichte mit dem Fahrrad klingt nicht so angenehm.
Wünsche dir erst mal baldige Genesung und dass du nicht zu lange das Bett im KH hüten musst.

Wenn dir dort sonst langweiig werden sollte......
Es gibt ja so nette USB-UMTS-Sticks für den Laptop. :cool:


Grüße und gute Besserung,
Cassio
 
... da ich mir bei nem Sturz vom Fahrrad vor einigen Tagen den linken Unterarm gebrochen habe.

Andreas,
dann wünsche ich dir auch mal gute Besserung!

Und ja, am besten stell deinen Code mal in das Forum, vielleicht können wir dann besser weiterhelfen.

Grüße,
Dirk
 
Hallo Andreas,

das klingt ja böse mit dem Fahrrad. Wünsche Dir auf jeden Fall eine gute Besserung.

Grüße
Heinrich
 
Back again ....

Hallo, liebe Freunde,

habe mich sehr über Eure guten Wünsche gefreut. Das hat wirklich verdammt gut getan. Bin gestern entlassen worde, und es geht mir eigentlich ganz gut. Die OP hat ca. 80 Minuten gedauert, leider hat die Lokalanästhesie nicht so überzeugend gewirkt: irgendwann hatte ich dann bei jedem Handgriff des Chirurgen das gleiche Gefühl, wie wenn der Zahnarzt beim Bohren genau den Nerv trifft, und so hat mich der Narkosearzt dann doch noch ins Land der Träume geschickt. Die Röntgenbilder zeigen mir, dass ich jetzt ein Metallteil im Unterarm trage, das vom Umriss her an einen Flaschenöffner erinnert. Der Stiel ist mit drei Schrauben an der Speiche fixiert, und wo der Flaschenöffner oben in die Breite geht, halten vier längere Schrauben die zersplitterten Knochenteile zusammen ... nach Möglichkeit soll das Ding drin bleiben, denn es sei "nicht schön", so etwas wieder ausbauen zu müssen.
Sorry, mir ist schon klar, dass dies hier keine medizinische Plauderecke ist, aber nach so viel Anteilnahme musste das mal gesagt werden, grins ...

Ich werde in den nächsten Tagen mal die beiden Beispielprogramme für Euch aufbereiten. Freu mich auf Eure Hilfe.

LG Andreas
 
Teil 1: Aufgabenstellung und Polling-Version eines simplen MIDI-Programms

So, hier ist erst einmal die einfache Programmversion, die mit Polling arbeitet und vor allem FUNKTIONIERT. Habe ein einfaches Beispiel aus der MIDI-Programmierung ausgewählt: Der USART des Duemilanove empfängt via MIDI-Shield von SparkFun MIDI-Messages, die von einer beliebigen Groovebox bzw. Drum-Machine gesendet werden (in meinem Fall Korg ESX1 - das spielt aber letztlich keine Rolle). Das Programm sorgt dafür, dass nur MIDI-CLOCK-Messages (0xF8) einen "tickCounter" hochzählen: nach jeweils 12 Ticks wird die grüne LED des MIDI-Shield umgeschaltet, so dass man auf diese Weise pro Viertelnote eine Hell-Dunkel-Phase hat.

Code:
// TestUsartPolling


#define BAUD_31250 31


// initialsierung des usart0
void usart0Init(unsigned int ubrr)
{
  // set baud rate
  UBRR0H=(byte)(ubrr>>8);
  UBRR0L=(byte)ubrr;
  // enable receiver and transmitter
  UCSR0B=(1<<RXEN0) | (1<<TXEN0);
  // set frame format: 8 data, 1 stop bit 
  UCSR0C=3<<UCSZ00;
}


// lesen eines empfangenen byte
byte usart0Receive(void)
{
  // get and return received data from buffer
  return UDR0;
}


// wurde ein byte empfangen?
boolean isUsart0ReceiverReady(void)
{
  return UCSR0A & (1<<RXC0);
} 


void setup(void)
{
  // initialisierung usart
  usart0Init(BAUD_31250);

  // gruene led des MIDI-shield
  pinMode(6, OUTPUT);
}


boolean ledStatus=false;
byte tickCounter=0;


void loop(void){
  
  if(isUsart0ReceiverReady() && usart0Receive()==0xF8){
    
    // midi-clock 0xF8 empfangen
    // (24 clocks pro viertelnote)
    
    tickCounter++;
    if(tickCounter==12){
      tickCounter=0;
      if(ledStatus)
        ledStatus=false;
      else
        ledStatus=true;    
    }
    
    // gruene led aktualisieren
    digitalWrite(6, ledStatus);
  }

}

Letztlich soll das nun so umgeschrieben werden, dass das eigentliche Hauptprogramm loop() "leer" bleibt und Zählvorgang samt Umschaltung der LED in der ISR des USART-RCX-Interrupts erfolgen. Ich teste das morgen nochmal aus und stelle dann mal meinen (vermutlich wieder nicht funktionierenden) Code zur Diskussion.

P. S.: In umfangreicheren Programmen verwende ich die arduino-eigenen Befehle pinMode() und digitalWrite() etc. nicht mehr, sondern programmiere
 
Probleme mit den Eingabefeldern hier ...

Sorry, habe hier teilweise Probleme mit den Eingabefeldern; konnte zum Beispiel die Fehler im obigen Beitrag nicht korrigieren. Das Eingabefeld zeigt die ganzen Schalter zur Textmodifikation, aber anstelle eines eigentliches Texteingabefelds hab ich nur eine inaktive graue Fläche ... ??? :confused:
 
Hallo Andreas,

normalerweise würde ich sagen, dass es an der zeiltlichen Begrenzung für das Ändern von Beiträgen liegt, diese Zeit ist bei uns seit einiger Zeit auf 24 Stunden eingestellt (danach können nur noch die Moderatoren ändern). Dein Beitrag ist aber noch keine 24h "alt", ich weiß also im Moment nicht, woran es liegen könnte. :(

Dirk
 
Kleine Korrektur in loop()

loop() ist jetzt erst einmal ein bisschen verbessert worden; schließlich muss die LED-Anzeige nur dann aktualisiert werden, wenn der tickCounter übergelaufen ist. Das macht die spätere ISR schlanker, die ich gleich in Angriff nehmen werde:

Code:
void loop(void){
  
  if(isUsart0ReceiverReady() && usart0Receive()==0xF8){
    
    // midi-clock 0xF8 empfangen
    // (24 clocks pro viertelnote)
    
    tickCounter++;
    
    if(tickCounter==12){
      tickCounter=0;
      ledStatus=!ledStatus;
      // gruene led aktualisieren
      digitalWrite(6, ledStatus);
    }
    
  }

}
 
Teil 2: MIT Interrupt ... funktioniert NICHT!!!

So, hier isse endlich: die Interrupt-Version. Leider tut sich da nix! Sie wird aber fehlerfrei compiliert! Aufgefallen ist mir, dass

a) ich im Argument der ISR irgendeinen Quatsch reinschreiben kann, ohne dass sich der Compiler daran stört; beispielsweise also huhu statt USART0_RXC0_vect ...der Compiler meckert nicht!

b) ich Teile in der ISR auskommentieren kann, ohne dass sich die erzeugte Code-Länge ändert.

c) die Code-Länge aber zum Beispiel davon abhängt, ob ich in setup() erst das USART-Interrupt-Flag setze oder erst das globale Interrupt-Flag.

Kommt mir alles sehr komisch vor!

Code:
#define BAUD_31250 31


void usart0Init(unsigned int ubrr)
{
  // set baud rate
  UBRR0H=(byte)(ubrr>>8);
  UBRR0L=(byte)ubrr;
  // enable receiver and transmitter
  UCSR0B=(1<<RXEN0) | (1<<TXEN0);
  // set frame format: 8 data, 1 stop bit 
  UCSR0C=3<<UCSZ00;
}


// lesen eines empfangenen byte
byte usart0Receive(void)
{
  // get and return received data from buffer
  return UDR0;
}


/* ueberflussig bei interrupt-betrieb?
// wurde ein byte empfangen?
boolean isUsart0ReceiverReady(void)
{
  return UCSR0A & (1<<RXC0);
} 
*/


void setup(void)
{
  // initialisierung usart
  usart0Init(BAUD_31250);

  // gruene led des MIDI-shield
  pinMode(6, OUTPUT);

  // globales interrupt-flag setzen?
  sei();

  // enable usart-interrupt?
  UCSR0B |=(1<<RXCIE0);
}


boolean ledStatus=false;
byte tickCounter=0;


ISR(USART0_RXC0_vect)
{
   if(/* isUsart0ReceiverReady() && */ usart0Receive()==0xF8){
    
     // midi-clock 0xF8 empfangen
     // (24 clocks pro viertelnote)
    
     tickCounter++;
    
     if(tickCounter==12){
       tickCounter=0;
       ledStatus=!ledStatus;
       // gruene led aktualisieren
       digitalWrite(6, ledStatus);
     }
   }
}


void loop(void)
{
  ;
}

Bin für Eure Hilfe sehr dankbar!!!!

LG Andreas
 
a) und b) klingen irgendwie so, als ob die gesamte ISR wegoptimiert wird. Mangels C-Kenntnissen kann ich Dir da nicht wirklich helfen - letztendlich ist die ISR ja sowas ähnliches wie 'ne Subroutine. In ASM wird das durch eine Einsprungadresse (label) und ein (normalerweise(*)) RETI realisiert. Diese Einsprungadresse muß im entsprechenden Interruptvektor das Argument einer Sprunganweisung sein (normalerweise (*)).
In Bascom wird das ganze mittels "On Interuptquelle ISR" ähnlich "beauftragt" - den Code erzeugt ja dann Bascom.

Wie und wo Du hier dem Interrruptvektor den Sprungbefehl übergibst, kann ich nicht erkennen (mangelnde C-Kenntnisse) - falls diese Referenz auf die ISR aber wirklich fehlt, erkennt der Compiler möglicherweise, daß die Routine nie ausgeführt werden würde, und läßt sie vielleicht ganz weg (Spekulation!).

c) finde ich auch merkwürdig.

(*) bei Controllern mit mehr als 4KB Flash kann der gesammte Flash nicht mehr von jedem Punkt (also insbesondere auch nicht mehr von den Interrruptvektoren ganz vorn) mit dem 1word-Sprungbefehl RJMP erreicht werden. Stattdessen nimmt man den 2word-Sprungbefehl JMP. Folglich muß also jeder Interruptvektor 2 words groß sein.
RETI (oder auch RET) ist ein 1word Befehl - wenn sich hier die ISR also durch eine einzelne 1word-Instruktion umsetzen läßt, können die beiden Instruktionen auch direkt in den Interruptvektor. Man kann das jetzt noch mit nicht verwendeten Interruptvektoren weiter auf die Spitze treiben, aber ich schweife ab... diese Dinge stehen Dir in Hochsprachen eh meist nicht zur Verfügung.
Kann man eigentlich in C oder Bascom 'ne ISR mit RET beenden lassen (also ohne die INTs global gleich wieder freizugeben)? Bascom übersetzt das RETURN direkt in ein RETI, oder?
 
Es kommt noch schlimmer ...

Also, genau DAS ist auch immer mein Problem: Ich habe keinen Schimmer, wie der Compiler letztlich den Kram umsetzt. Wo findet man über solche Feinheiten detaillierte Informationen???

Ich hab jetzt auch mal noch ein bisschen mit "volatile" rumexperimentiert: das ist mir net immer so hundertprozentig klar, ob und wann genau man dieses Schlüsselwort braucht. Hat wohl aber auch was mit Interrupts zu tun ...

Und jetzt der Hammer: Hab mal die komplette ISR auskommentiert: Der erzeugte Code ist genau so lang wie vorher, stöhn ... ich flipp noch aus. Der Sch ...compiler nimmt die ISR also überhaupt nicht wahr?

Trotzdem: Es ist ja nicht so, dass diese ISR-Geschichte sonst nicht funktioniert hätte: Beim Overflow-Interrupt vom Timer2 hat das prima geklappt! Das hab ich nach eienm Rezept gemacht, das mir mal jemand geschickt hat ...
Was mich DABEI allerdings gewundert hat: Es heißt ja, dass man mit sei() die Interrupts global freigeben muss. Das war beim TIMER2_OVF aber gar nicht nötig - hat auch so geklappt.

Rätsel über Rätsel ... ich kämpf jetzt weiter ... :mad:
 
Hallo Andreas,

"volatile" benötigst du, wenn du eine globale Variable im Hauptprogramm verwendest, die aber auch in einer ISR verändert wird. Damit teilst du dem Compiler mit, dass der Inhalt der Variable auch ausserhalb von main() verändert werden kann. Der compiler würde ansonsten folgendes innerhalb der if-Abfrage sonst nicht ausführen. "d" wird in while(1) nie verändert, er optimiert if weg:
Code:
uint8_t d;

int main(void)
{

  d = 100;
  while (1)
  {
    if (d == 0)
    {
       //...
    }
  }
}

Gibt der Compiler denn irgendwelche Warnings aus?

Vielleicht stellst du auch mal dein Projekt hier ins Forum. Wenn es AVR/Atmel Studio GCC Projekt ist, könnte ich es compilieren.

Interrupts musst du immer erst durch sei() global freigeben. Eventuell hat das bei dir irgendeine Initialisierungsroutine gemacht, die nicht von dir ist, deren Inhalt du nicht kennst.

Gruß,
Dirk

EDIT:
Mir ist gerade etwas aufgefallen. Verwende folgenden Interruptvektor: USART_RX_vect
 
War voll vernagelt ... und JETZT FUNKTIONIERT ES!!!

Ja, Dirk, das war ich wohl irgendwie ... es klappt nämlich tatsächlich mit dem USART_RX_vect. Und wenn ich ehrlich bin, hatte das Hemi schon letzte Woche vor meinem KKH-Aufenthalt hier in der Diskussion zum Besten gegeben. Bilde mir auch ein, dass ich diese Variante ausprobiert habe ... aber vielleichr war da noch ein anderer Fehler im Programm.
Außerdem nutze ich bei einem anderen MIDI-Projekt gleichzeitig 4 USARTS, so dass ich mir USARTs ohne Nummer gar nicht mehr vorstellen kann ...

Es funktioniert also tatsächlich in der folgenden Form:

Code:
#define BAUD_31250 31

boolean ledStatus=false;
byte tickCounter=0;


ISR(USART_RX_vect)
{
   if(usart0Receive()==0xF8){
    
     // midi-clock 0xF8 empfangen
     // (24 clocks pro viertelnote)
    
     tickCounter++;
    
     if(tickCounter==12){
       tickCounter=0;
       ledStatus=!ledStatus;
       // gruene led aktualisieren
       digitalWrite(6, ledStatus);
     }
   }
}


void usart0Init(unsigned int ubrr)
{
  // set baud rate
  UBRR0H=(byte)(ubrr>>8);
  UBRR0L=(byte)ubrr;
  // enable receiver and transmitter
  UCSR0B=((1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0));
  // set frame format: 8 data, 1 stop bit 
  UCSR0C=3<<UCSZ00;
}


// lesen eines empfangenen byte
byte usart0Receive(void)
{
  // get and return received data from buffer
  return UDR0;
}


void setup(void)
{
  // initialisierung usart
  usart0Init(BAUD_31250);

  // gruene led des MIDI-shield
  pinMode(6, OUTPUT);
}



void loop(void)
{
  ;
}

Tatsache ist auch hier, dass ich ohne sei() auskomme. Sicher hast Du recht, dass da irgendwas im Hintergrund gemanaged wird, ohne dass man es mitbekommt. Vielleicht brauche ich doch mal nen durchsichtigeren Compiler?!

Gerne stelle ich mal mein(e) Projekt(e) aus dem Bereich Musiktechnik vor; muss mir dazu nochmal die Foren-Regeln etc. reinziehen.

Als nächstes wird es darum gehen, Timer- und USART-Interrupts (evtl. kommt noch ein SPI-Interrupt dazu) verschachtelt zu betreiben. Bin gespannt ...

Aber erstmal Danke an Dich (auch für "volatile"-Nachhilfe) und an Euch alle

Schönen Abend noch und LG von Andreas
 
Es geht weiter ...

So, der USART_RX- und der TIMER2_OVF-Interrupt funktionieren nun auch nebeneinander bzw. "verschachtelt" im selben Programm. Das ist schon fast mehr, als ich für diese Tage zu hoffen wagte ...

Damit ist es jetzt möglich, präzise Steuerspannungsverläufe (Stichwort: Envelope Generator z. B. ADSR oder LFO) für einen analogen Modularsynthesizer zu erzeugen und die Parameter der "virtuell-analogen" Module gleichzeitig via MIDI zu kontrollieren.

Falls Interesse besteht, berichte ich gerne ausführlicher über meine Arbeiten in diesem Umfeld, habe momentan nur keine Ahnung, womit und auf welchem Level ich da am besten anfange. MIDI alleine ist ja schon ein spannendes Thema für sich ... also: meldet Euch, falls ich was schreiben soll!

LG Andreas
 
Hi Andreas,

Falls Interesse besteht, berichte ich gerne ausführlicher über meine Arbeiten in diesem Umfeld, habe momentan nur keine Ahnung, womit und auf welchem Level ich da am besten anfange. MIDI alleine ist ja schon ein spannendes Thema für sich ... also: meldet Euch, falls ich was schreiben soll!
schreibs einfach rein ;) Interesse besteht immer :cool:

Wo du anfängst ist eigentlich dir überlassen. Über MIDI wird man bestimmt schon genug Infos finden (Stromschleife, galvanisch entkoppelt, ..., usw) also kannst du dich eigentlich auf die Erklärung deiner Projekte konzentrieren. Und wenn irgend jemandem etwas unklar ist, dann wird der schon fragen ;)

Gruß
Dino
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)