PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Datenaustausch mit Interrupts: volatile



ikarus_177
05.07.2009, 12:04
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" (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#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:

#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

uwegw
05.07.2009, 12:19
Das volatile sorgt nicht für eine globale Sichtbarkeit der Variablen! Du musst sie als globale Variablen (aber weiterhin volatile) anlegen.

ikarus_177
05.07.2009, 13:29
Hi,

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

Viele Grüße

p_mork
06.07.2009, 12:48
@ikarus_177


...
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.

...
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

ikarus_177
06.07.2009, 17:08
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

Besserwessi
06.07.2009, 18:14
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.

hosti
06.07.2009, 18:18
ohne break; ist die ganze case abfrage nicht vollständig.
Zusätzlich gibt es auch noch continue; hiermit gelangt man wieder zur Schleifenbedingung

SprinterSB
06.07.2009, 20:13
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

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:

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.

hosti
06.07.2009, 20:25
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