Forum: Mikrocontroller und Digitale Elektronik Atmega16 Ausgänge, Zeitmultiplexing, Switch-Case


von Matze (Gast)


Lesenswert?

Hallo,

Ich möchte mit folgendem Code erreichen dass alle enthaltenen Ausgänge 
nacheinander Eingschaltet werden und die Gleiche Einschaltzeit haben.
(Zeitmultiplexing für Display)

Wenn nun y von 0 auf 10 hochzählt sollte jeder Zustand genau 1 mal 
auftreten, dies ist jedoch nicht der fall.

Der 1. Ausgang bekommt 1 Puls, der 2. bekommt 2, der Vierte 4 usw

Woran könnte das liegen, ich finde den Fehler nicht.
1
#include <avr/io.h>
2
3
int main(void)
4
{
5
      DDRA  = 0xFF;
6
      DDRB  = 0xFF;
7
      DDRC  = 0xFF;
8
      DDRD  = 0xFF;
9
      PORTA=0xFF;
10
      PORTB=0xFF;
11
      PORTC=0xFF;
12
      PORTD=0xFF;
13
    while(1)
14
    {
15
      for(int y=0;y<11;y++)
16
      {
17
      PORTA &= ~(1 << PA0);   // Rück-Setzen
18
      PORTB &= ~(1 << PB0);   
19
      PORTB &= ~(1 << PB1);  
20
      PORTB &= ~(1 << PB2);   
21
      PORTB &= ~(1 << PB3);  
22
      PORTB &= ~(1 << PB4);   
23
      PORTD &= ~(1 << PD1);  
24
      PORTD &= ~(1 << PD2);   
25
      PORTD &= ~(1 << PD3);  
26
      PORTD &= ~(1 << PD4);   
27
      PORTD &= ~(1 << PD5);   
28
      switch(y)
29
      {
30
      case 0: PORTA |= (1 << PA0);   // Setzen
31
      case 1: PORTB |= (1 << PB0);   
32
      case 2: PORTB |= (1 << PB1);  
33
      case 3: PORTB |= (1 << PB2);   
34
      case 4: PORTB |= (1 << PB3);  
35
      case 5: PORTB |= (1 << PB4);  
36
      case 6: PORTD |= (1 << PD1);   
37
      case 7: PORTD |= (1 << PD2);   
38
      case 8: PORTD |= (1 << PD3);   
39
      case 9: PORTD |= (1 << PD4);   
40
      case 10: PORTD |= (1 << PD5);  
41
      }            
42
      }  
43
    }
44
}

Danke für eure Hilfe

von Stefan W. (dl6dx)


Lesenswert?

Matze schrieb:
1
    switch(y)
2
       {
3
       case 0: PORTA |= (1 << PA0);
4
       case 1: PORTB |= (1 << PB0);
5
       (...)

Die "case"-Label sind Einsprungpunkte. Sie beenden nicht die 
Codesequenz, die davor steht. Im Moment wird bei y == 0 jeder der Pins 
gesetzt, bei y == 1 wird PB0 bis PD5 gesetzt, bei y == 2 PB1 bis PD5 
(und so weiter).

Wenn du willst, dass bei y == 0 nur PA0 gesetzt wird, bei y == 1 nur 
PB0 (usw.), musst du den switch jeweils über "break" verlassen:
1
    switch(y)
2
       {
3
       case 0: PORTA |= (1 << PA0); break;
4
       case 1: PORTB |= (1 << PB0); break;
5
       case 2: PORTB |= (1 << PB1); break;
6
       case 3: PORTB |= (1 << PB2); break;
7
       case 4: PORTB |= (1 << PB3); break;
8
       case 5: PORTB |= (1 << PB4); break;
9
       case 6: PORTD |= (1 << PD1); break;
10
       case 7: PORTD |= (1 << PD2); break;
11
       case 8: PORTD |= (1 << PD3); break;
12
       case 9: PORTD |= (1 << PD4); break;
13
       case 10: PORTD |= (1 << PD5); break;
14
       }

Tipp: Lies dir das Kapitel deines C-Buchs über Kontrollstrukturen besser 
noch mal durch.

Außerdem kannst du alle Portbits eines Ports auf einmal zurück setzen:
1
  PORTA &= ~(1<<PA0);
2
  PORTB &= ~((1<<PB0) | (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4));
3
  PORTD &= ~((1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5));

Da Portregister als volatile deklariert sind, darf der Compiler hier 
nichts zusammenfassen. So hast du 10 read-modify-writeback-Operationen 
statt der minimal möglichen 3.



Grüße

Stefan

Edit: Nur fürs Protokoll: Ja, beim letzen Label (case 10:) ist der Break 
obsolet (das Ende des switch-Konstrukts ist eh erreicht). Schadet aber 
auch nicht.

von Peter II (Gast)


Lesenswert?

Stefan Wagner schrieb:
> PORTD &= ~((1<<PD1) | (1<<PD2) | (1<<PD3) | (1<<PD4) | (1<<PD5));
>
> Da Portregister als volatile deklariert sind, darf der Compiler hier
> nichts zusammenfassen. So hast du 10 read-modify-writeback-Operationen
> statt der minimal möglichen 3.

denke nicht. Dann die Rechte seite darf auf einmal ausgewertet werden. 
Es ist nur kein Setzen einen einzelnen Bits sonder es werden alles 8 
Bits auf einmal geändert. Das ganze man Probleme wenn noch eine ISR den 
port gleichzeitig ändert.

von STK500-Besitzer (Gast)


Lesenswert?

Stefan Wagner schrieb:
> Da Portregister als volatile deklariert sind, darf der Compiler hier
> nichts zusammenfassen. So hast du 10 read-modify-writeback-Operationen
> statt der minimal möglichen 3.

im rechten Teil steht eine Konstante... Die wird schon zur Compile-Zeit 
berechnet (oder auch wann anders, aber nicht mehr zur Programmlaufzeit)

von Stefan W. (dl6dx)


Lesenswert?

Peter II schrieb:
> denke nicht. Denn die rechte Seite darf auf einmal ausgewertet werden.
> Es ist nur kein Setzen einen einzelnen Bits sondern es werden alle 8
> Bits auf einmal geändert.

Bei dem Code, den ich gepostet habe, ist das auch so. Da ist constant 
folding im jeweiligen rvalue möglich und er reduziert sich zu einer 
einzigen Konstanten.

Bei dem vom TO geposteten Code
1
      PORTA &= ~(1 << PA0);   // Rück-Setzen
2
      PORTB &= ~(1 << PB0);   
3
      PORTB &= ~(1 << PB1);  
4
      (...)

wird je Statement nur ein Bit gelöscht. Und da die Portregister als 
volatile dekariert sind, darf kein constant folding über alle Statements 
hinweg stattfinden.

Grüße

Stefan

von Stefan W. (dl6dx)


Lesenswert?

Stefan Wagner schrieb:
> Da Portregister als volatile deklariert sind, darf der Compiler hier
> nichts zusammenfassen. So hast du 10 read-modify-writeback-Operationen
> statt der minimal möglichen 3.

Nur zur Klarstellung: Das bezog sich auf den originalen Code des TO, der 
je Statement nur ein Bit zurücksetzte.

Grüße

Stefan

von Peter D. (peda)


Lesenswert?

Matze schrieb:
> und die Gleiche Einschaltzeit haben.

Dann brauchst Du erstmal einen Timerinterrupt.

von Andreas (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Dann brauchst Du erstmal einen Timerinterrupt.

Wozu?
Er benutzt keine Interrupts, folglich wird die for-Schleife immer in der 
selben Zeit durchlaufen. Daher sind auch die Pulse immer gleich lang - 
bis auf den letzten (PD5), der ist etwas länger als die anderen - das 
könnte man aber leicht reparieren. ;)

von Peter D. (peda)


Lesenswert?

Andreas schrieb:
> folglich wird die for-Schleife immer in der
> selben Zeit durchlaufen.

Das ist aber sehr kurzsichtig gedacht.
Programme sollen ja in der Regel noch mehr machen als nur multiplexen 
und dann ändert sich ständig die Programmlaufzeit.

Eine gleiche Pulslänge über sämtliche Programmlaufzeiten zu erzielen, 
ist ähnlich einfach, wie einen Tisch aus Streichhölzern zusammen zu 
leimen.
Man muß sich die Arbeit ja nicht absichtlich schwerer machen.

Ich will aber niemanden was aufzwingen.
Vielleicht macht es ja manchem Spaß, eine Sackgasse zu programmieren und 
dann alles wegzuschmeißen und nochmal komplett neu anzufangen.

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.