- 3D-Druck Einstieg und Tipps         
Ergebnis 1 bis 9 von 9

Thema: Datenaustausch mit Interrupts: volatile

  1. #1
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    31
    Beiträge
    601

    Datenaustausch mit Interrupts: volatile

    Anzeige

    LiFePo4 Akku selber bauen - Video
    Hallo,

    ich überarbeite gerade die Soft & Hardware meines Bots, und bin im Zuge dieser "Generalüberholung" auf C umgestiegen.

    Als erstes wollte ich mein Bascom-Programm zur Servoansteuerung, das auch recht gut funktioniert, nach C umlegen (der altbekannte Weg mit 16-Bit Timer und der Generierung der Impulse nacheinander). Dazu muss meine ISR ja auf einige Variablen im Hauptprogramm zugreifen können (Vektor für die Impulslängen der Servos).

    Laut mikrocontroller.net - Tutorial Abschnitt "Datenaustausch mit Interrupt-Routinen" habe ich diese Variablen mit "volatile" versehen. Trotzdem meckert der Compiler weiterhin, diese Variablen seien in der ISR nicht definiert.

    Vielleicht kann jemand damit was anfangen? Wo könnte der Hund begraben liegen?
    Hier der ungetestete Code:
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    
    /* -------------------------------------------------------------------------- */
    /* Präprozessoranweisungen:													  */
    /* -------------------------------------------------------------------------- */
    
    #define SERVOPORT PORTA
    #define DATENRICHTUNG DDRA
    #define SERVO1 1
    #define SERVO2 2
    #define SERVO3 3
    
    /* -------------------------------------------------------------------------- */
    /* Funktionsprototypen:														  */
    /* -------------------------------------------------------------------------- */
    
    void init_Servo(void);
    
    
    /* -------------------------------------------------------------------------- */
    /* Beginn der Hauptfunktion:												  */
    /* -------------------------------------------------------------------------- */
    
    int main (void)
    {
    	/* -------------------------------------------------------------------------- */
    	/* Definition von notwendigen Variablen:									  */
    	/* -------------------------------------------------------------------------- */
    
    	volatile uint16_t Servo[4] = {0};		//Pulslängen der Servos
    	volatile uint8_t iCounter = 0;
    	volatile uint8_t dServo = 0;		//Dummyvariable für Verzögerungszeit
    
    	init_Servo();
    
    
    	return (0);
    }
    
    /* -------------------------------------------------------------------------- */
    /* Funktionsdefinitionen:													  */
    /* -------------------------------------------------------------------------- */
    /* -------------------------------------------------------------------------- */
    /* Initialisieren des Timers und setzen der verwendeten Pins als Ausgang:	  */
    /* -------------------------------------------------------------------------- */
    
    void init_Servo ()
    {
    	//Setzen der Servopins als Ausgang:
    	DATENRICHTUNG |= (1 << SERVO1) | (1 << SERVO2) | (1 << SERVO3);
    
    	//Setzen der Ausgänge auf log. 0 (Anfangszustand)
    	SERVOPORT &= ~((1 << SERVO1) | (1 << SERVO2) | (1 << SERVO3));
    
    	//Konfigurieren des Timers:
    	TIMSK |= (1 << TOIE1);		//Overflow-Interrupt aktivieren
    	TCCR1B |= (1 << CS11);		//Prescale 8
    
    	sei();		//globale Interrupts aktivieren
    }
    
    /* -------------------------------------------------------------------------- */
    /* Interrupt bei Überlauf von Timer1:		  								  */
    /* -------------------------------------------------------------------------- */
    
    ISR(TIMER1_OVF_vect)
    {
    
    	switch (iCounter)
    	{
    		case 1: SERVOPORT &= ~(1 << SERVO1);
    		case 2: SERVOPORT &= ~(1 << SERVO2);
    		case 3: SERVOPORT &= ~(1 << SERVO3);
    		case 4: dServo = 0;
    	}
    
    	iCounter++;
    	if (iCounter == 4)
    	{
    		iCounter = 1;
    	}
    
    	switch (iCounter)
    	{
    		case 1: SERVOPORT |= (1 << SERVO1);
    		case 2: SERVOPORT |= (1 << SERVO2);
    		case 3: SERVOPORT |= (1 << SERVO3);
    		case 4: dServo = 1;
    	}
    
    	TCNT1 = 65535 - Servo[iCounter-1];
    
    }
    Ich verwende Eclipse incl. AVR-Plugin und avr-gcc unter Ubuntu.

    Viele Grüße
    ikarus_177

  2. #2
    Erfahrener Benutzer Robotik Einstein
    Registriert seit
    06.02.2005
    Ort
    Hamburg
    Alter
    38
    Beiträge
    4.255
    Das volatile sorgt nicht für eine globale Sichtbarkeit der Variablen! Du musst sie als globale Variablen (aber weiterhin volatile) anlegen.

  3. #3
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    31
    Beiträge
    601
    Hi,

    danke für den Hinweis, nun klappt's!

    Viele Grüße

  4. #4
    Erfahrener Benutzer Roboter Experte
    Registriert seit
    25.03.2006
    Ort
    Darmstadt
    Alter
    33
    Beiträge
    522
    @ikarus_177

    Code:
     ...
       switch (iCounter)
       {
          case 1: SERVOPORT |= (1 << SERVO1);
          case 2: SERVOPORT |= (1 << SERVO2);
          case 3: SERVOPORT |= (1 << SERVO3);
          case 4: dServo = 1;
       } ...
    Ich glaube da wird nicht das getan, was Du haben möchtest. In C ist das so, dass bei einem passenden case-Vergleich der komplette Code bis zum Ende des Blockes ausgeführt wird. D.h. wenn iCounter 2 ist, dann wird nicht nur SERVOPORT |= (1 << SERVO2); sondern auch SERVOPORT |= (1 << SERVO2); und SERVOPORT |= (1 << SERVO3); sowie dServo = 1; ausgeführt. Um das zu verhindern muss man am Ende jedes Abschnitts ein break; setzen, dadurch wird der switch-Block verlassen.
    Code:
     ...
       switch (iCounter)
       {
          case 1: SERVOPORT |= (1 << SERVO1); break;
          case 2: SERVOPORT |= (1 << SERVO2); break;
          case 3: SERVOPORT |= (1 << SERVO3); break;
          case 4: dServo = 1; break;
       } ...
    MfG Mark

  5. #5
    Erfahrener Benutzer Roboter Experte Avatar von ikarus_177
    Registriert seit
    31.12.2007
    Ort
    Grein
    Alter
    31
    Beiträge
    601
    Hi,

    danke Mark, das werde ich noch ergänzen.
    Aber so ganz hab ich das noch nicht kapiert, was hat das denn für einen Sinn, wenn sowieso alle 'case'-Fälle ausgeführt werden, wenn man break nicht verwendet? Oder ist ohne break die "Struktur" der Anweisung noch nicht vollständig?

    Bitte um Aufklärung,
    ikarus_177

  6. #6
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Die switch Strucktur ist ohne das break zulässig und syntaktisch richtig. Der Compiler erzeugt also dazu code, der auch funktionieren wird. Nur ist das in der Regel nicht das erwartete Ergebnis. Vermutlich wird der Compiler nicht mal eine Warning ausgeben.

    Ein Switch Statement ohne die Breaks macht nur sehr selten Sinn. Es könnte aber vorkommen.

    Ich persönlich halte das für ein Schwäche der Sprache C, vor allem wenn keine Warnung gibt.

    p.s.:
    Das Break hinter dem letzten Fall könnte man weglassen, schadet aber auch nichts.

  7. #7
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    ohne break; ist die ganze case abfrage nicht vollständig.
    Zusätzlich gibt es auch noch continue; hiermit gelangt man wieder zur Schleifenbedingung

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.802
    Zitat Zitat von hosti
    ohne break; ist die ganze case abfrage nicht vollständig.
    Zusätzlich gibt es auch noch continue; hiermit gelangt man wieder zur Schleifenbedingung
    Doch, auch ohne break ist ein case vollständig. Es ist Standard-C. Es wird eben mit dem Code des nächsten case weitergemacht, weil man keine Anweisung gegeben hat, mit dem switch aufzuhören.

    Falls es gewünscht ist, beim nächsten case-Label weiterzumachen, so ist ein Kommentar sinnvoll wie
    Code:
       case 1:
          // FALLSTHRU
       case 2:
          ...
    um explizit zu kennzeichnen, daß in case 1 kein break fehlt, sondern gewollt ist, daß es bei 2 weitergeht.

    Sinnvoll ist das um Fälle übersichtlich zusammenzufassen und Codeduplizierungen zu vermeiden:
    Code:
    case 1: 
    case 3:
    case 4:
    case 7:
    case 10:
    case 12:
       // mach was
       break;
    case 0: 
    case 2:
    case 9:
    default:
       // mach was
    case 8:
    case 11:
    case 13:
      // mach was
      break;
    continue hat mit switch/case nicht zu tun. Es gehört semantisch zu einer Schleife, die switch/case umgibt. Ein continue ohne Schleide um's switch führt zu einem Fehler.
    Disclaimer: none. Sue me.

  9. #9
    Erfahrener Benutzer Begeisterter Techniker
    Registriert seit
    02.05.2004
    Alter
    38
    Beiträge
    388
    1.
    In seinem Fall ist die caseabfrage aber ohne break; unvollständig.

    2. continue habe ich nur erwähnt als zusatz Info. Da man ab und an darüberstolpert.
    Und ich habe ja geschrieben das man zur Schleifenbedingung kommt. Und nicht zur case abfrage. Im nachhinein hab ich mich aber wirklich nicht klar ausgedrückt bezüglich continue, danke

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •  

Labornetzteil AliExpress