PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : switch-Anweisung springt immer zum selben case X Befehl



HF SHOOTER
05.11.2007, 19:02
Hallo

Mit dem unten stehenden Code habe ich meine Probleme:

// richtige 7-Segment-Anzeige freischalten (Masse schalten)
switch(Ziffer)
{
case 0: break;
case 1: PORTB_temp &= ~(1 << PB2); PORTB_temp |= (1 << PB0); break;
case 2: PORTB_temp &= ~(1 << PB0); PORTB_temp |= (1 << PB2); break;
}

Die Variable Ziffer (uint8_t) hat entweder den Wert 0, 1 oder 2. Egal welcher Wert diese hat, es wird immer nur der Teil hinter case 1 ausgeführt, wenn Ziffer eine 2 hat wird trotzdem case 1 ausgeführt.

Habe euch ein Bild beim Debugen mit angehängt.

EDIT: Auch wenn cihd as ganze mit IF-Bedingung filtern möchte wird immer davon ausgegangen das Ziffer 1 ist, auch wenn es 2 ist. Folgender Code führt auch wenn Ziffer 2 ist den Teil bei Ziffer 1 aus:

if (Ziffer == 1)
{
PORTB_temp &= ~(1 << PB2);
PORTB_temp |= (1 << PB0);
}
if (Ziffer == 2)
{
PORTB_temp &= ~(1 << PB0);
PORTB_temp |= (1 << PB2);
}

Aber warum funktioniert es dann bei diesem Codeabschnitt ohne Probleme?

// Segmente einstellen
switch(StelleZahl[Ziffer - 1])
{
// Segment: gfedcba
case 0: SEGMENT = 0b00111111; break;
case 1: SEGMENT = 0b00000110; break;
case 2: SEGMENT = 0b01011011; break;
case 3: SEGMENT = 0b01001111; break;
case 4: SEGMENT = 0b01100110; break;
case 5: SEGMENT = 0b01101101; break;
case 6: SEGMENT = 0b01111100; break;
case 7: SEGMENT = 0b00000111; break;
case 8: SEGMENT = 0b01111111; break;
case 9: SEGMENT = 0b01100111; break;
// Segment: gfedcba
}


Und falls jemand den kompletten Code einsehen will, hier ist er:

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Projekt: Drehzahlmesser

Anzeige der Drehzahl auf 5 7-Segment-Anzeigen.
Anzeigen werden im Multiplexverfahren angesteuert,
die Drehzahl alle 100ms aktualisiert.

Später sollen noch max. Drehzahlspeicherung sowie eine
Begrüßung beim Einschalten hinzukommen.

AUTOR: Benjamin Ruppert
DATUM: 01.11.2007 - gute Frage

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#include <avr/io.h>
#include <stdint.h>
#include <util/delay.h>
#include <avr/interrupt.h>


/* Variablendeklaration */
uint8_t Ziffer; // 1 - 5, 1 links, 5 rechts
uint16_t Zahl; // Drehzahl, max 5-stellig
uint8_t StelleZahl[5] = {1,2,5,7,9};
uint8_t SEGMENT;

uint8_t PORTB_temp;
uint8_t PORTC_temp;




/* Prototypendekleration */



/* Interruptserviceroutinen */
ISR(TIMER0_OVF_vect)
{
Ziffer++;
if (Ziffer > 2) { Ziffer = 1; }
}



int main(void)
{
/* Ein- / Ausgänge definieren */
DDRC = 0b00111111; // PC0 - PC5 als Ausgang
DDRB = 0b00000111; // PB0 - PB1 als Ausgang

/* Timer einstellen */
TCCR0 |= (0 << CS00) | (1 << CS01); // Vorteiler 64

/* Interrupts einschalten / festlegen */
TIMSK |= (1<<TOIE0);
sei();


// DEBUG Settings
Ziffer = 1;
//Zahl = 2;

while(1)
{

// Segmente einstellen
switch(StelleZahl[Ziffer - 1])
{
// Segment: gfedcba
case 0: SEGMENT = 0b00111111; break;
case 1: SEGMENT = 0b00000110; break;
case 2: SEGMENT = 0b01011011; break;
case 3: SEGMENT = 0b01001111; break;
case 4: SEGMENT = 0b01100110; break;
case 5: SEGMENT = 0b01101101; break;
case 6: SEGMENT = 0b01111100; break;
case 7: SEGMENT = 0b00000111; break;
case 8: SEGMENT = 0b01111111; break;
case 9: SEGMENT = 0b01100111; break;
// Segment: gfedcba
}


PORTB_temp = PORTB; //oder PINB?!?!?!?!?!!?!?
PORTB_temp &= ~(1 << PB1); // PB1 auf 0
PORTC_temp = 0x00;


// Ausgänge setzen, wie 7-Segment angeschlossen ist
if (SEGMENT & (1 << 0)) { PORTC_temp |= 0b00001000; } //a an PC3
if (SEGMENT & (1 << 1)) { PORTC_temp |= 0b00010000; } //b an PC4
if (SEGMENT & (1 << 2)) { PORTC_temp |= 0b00000001; } //c an PC0
if (SEGMENT & (1 << 3)) { PORTC_temp |= 0b00000010; } //d an PC1

if (SEGMENT & (1 << 4)) { PORTB_temp |= 0b00000010; } //e an PB1

if (SEGMENT & (1 << 5)) { PORTC_temp |= 0b00000100; } //f an PC2
if (SEGMENT & (1 << 6)) { PORTC_temp |= 0b00100000; } //g an PC5



//PORTC = 0x00; // PC0 - PC5 auf 0
//PORTB &= ~(1 << 1); // PB1 auf 0

// richtige 7-Segment-Anzeige freischalten (Masse schalten)
switch(Ziffer)
{
case 0: break;
case 1: PORTB_temp &= ~(1 << PB2); PORTB_temp |= (1 << PB0); break;
case 2: PORTB_temp &= ~(1 << PB0); PORTB_temp |= (1 << PB2); break;
}

PORTC = PORTC_temp;
PORTB = PORTB_temp;
}
}



Ich bekomm es im Moment einfach nicht ein meine Kopf rein warum er immer davon ausgeht das Ziffer 1 ist (alos case 1 ausführt) obwohl doch aus dem Bild ganz klar hervorgeht das Ziffer 2 ist, aber nein es wird trotzdem case 1 ausgeführt.

Für Hilfe bin ich sehr dankbar.

mfg
Benny

askazo
05.11.2007, 19:57
Der kleine aber feine Unterschied ist, dass "Ziffer" in einer Interruptroutine geändert wird.

Deklariere die Variable Ziffer mal als

volatile uint8_t Ziffer;

dann sollte es klappen.

Zur Erklärung:
Wenn eine Variable in einer Interruptroutine ändert und auch außerhalb der ISR verwendet wird, muss diese immer mit "volatile" deklariert werden. Der Compiler versucht, den Programmcode zu optimieren. So will er auch unnötiges neueinlesen von Variablen aus dem RAM in die Arbeitsregister verhindern. Da der Compiler aber nicht vorhersagen kann, wann eine Interruptroutine ausgeführt wird, kann es dabei Probleme geben. Mit "volatile" sagt man dem Compiler, dass er vor jeder Verwendung den aktuellen Wert der Variablen überprüfen soll.

askazo

PicNick
05.11.2007, 20:01
mmhhh.
Auf jeden Fall: wenn ISR und normal mit der gleichen Variablen arbeiten sollen, dann:
"volatile" dazuschreiben.
Ziffer bekommt am anfang "1" und das ändert sich nie.

Ich glaub' , da betrügt dich der Debugger/ Simulator.

:oops: ui, da war schon wer.

HF SHOOTER
05.11.2007, 20:06
Alles klar so einfach gehts (wenn mans weiß)

Besten Dank euch beiden.

mfg
Benny