Forum: Mikrocontroller und Digitale Elektronik Probleme auf ATmega88PA


von Marc B. (drakenfly)


Lesenswert?

Hallo Leute!

Ich bin derzeit schwer am rätseln, da mir ein sehr mysteriöses Problem 
auftritt. Es handelt sich um eine Binäre Uhr, bei der die LEDs 
pulsierend betrieben werden.

Dazu habe ich mir folgendes gedacht:

#define LED_MASK 0b00000000111101111111011111110011

ich habe eine Maske, die aussagt, ob sich an der aktuellen Position 
überhaupt eine LED befindet. Der Aufbau ist ja so:

x o | x o | x o       <-- an PC2
x o | o o | o o       <-- an PC3
o o | o o | o o       <-- an PC4
o o | o o | o o       <-- an PC5

trotzdem zähle ich die Positionen so:

3 7 | 11 15 | 19 23
2 6 | 10 14 | 18 22
1 5 |  9 13 | 17 21
0 4 |  8 12 | 16 20

also, egal ob eine LED da ist, oder nicht, da ist eine Fix-Position. 
Dies mache ich, damit ich von einer einfachen Counter-Variable, die sich 
in einem Bereich von 0-24 bewegt, einfach auf die Matrix umrechnen kann.

Wie man oben sieht sind die Zeilen von oben nach unten an PC2 - PC5 
angeschlossen, die Spalten sind von links nach rechts an PB0 - PB5
PORTB ist für GND zuständig, PORTC für VCC.

Das heißt, wenn ich LED 14 einschalten will:
PORTC = 0x00; // Alles aus (in Sperrrichtung)
PORTB = 0xFF;
PORTC |= (1<<PC3);
PORTB &= ~(1<<PB3);

So, nun lasse ich das ganze über einen Timer pulsieren.
1
#define LED_MASK 0b00000000111101111111011111110011
2
#define LED_MAX 32 // 24 LEDS -> bei 25 wieder auf 0 setzen!
3
4
ISR (TIMER0_OVF_vect)
5
{
6
PORTB = 0xFF; // Alles aus, Dioden in Sperrichtung
7
PORTC = 0x00;
8
PORTB &= ~(1<<(currentLed / 4)); // Hier sieht man nochmal, was ich mit dem Umrechnen meinte
9
PORTC |= (1<<(5 - (currentLed % 4)));
10
do
11
{
12
  currentLed++;
13
}
14
while (~LED_MASK & (1<<currentLed));
15
if (currentLed >= LED_MAX) { // Es gibt nur "24" (aufgrund der Löcher) LEDS
16
  currentLed = 0; // -> Wieder von vorne beginnen
17
}
18
}

Komischerweise leuchtet bei diesem Code aber nicht LED 15 auf.

Und wenn ich es nicht mehr pulsieren lasse, sondern auf CPU/1024 stelle 
und die einzelnen aufleuchten sehe, fällt auch auf, dass der µc versucht 
NACH der 15. LED jede LED auf. Man merkt das durch die zeitlichen 
Abstände, z.B. ist zwischen 18 und 20 ein längerer Abstand und nach 24 
dauert es auch 8 Schritte bis er wieder bei 0 beginnt.

Ich dachte mir, dass es etwas damit zu tun hat, dass es sich hier um 32 
bit handelt, und der ATmega88PA das vllt. nicht verarbeiten kann.

Habt ihr eine Idee? Ich bin ja doch noch ein bisschen ein Frischling und 
habe nicht sooo viel Erfahrung. Aber ich bin seit 5 Stunden am 
experimentieren und bekomm es nicht hin =/

Vielen Dank im Vorhinein!
Lg Marc

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Marc Brexner schrieb:
> Ich dachte mir, dass es etwas damit zu tun hat, dass es sich hier um 32
> bit handelt, und der ATmega88PA das vllt. nicht verarbeiten kann.

Der ATmega88 kann keine 32-Bit Werte verarbeiten, aber darum künnert 
sich der Compiler -- vorausgesetzt, der C-Code veranlasst ihn dazu.

Da wir die Deklarationen nicht sehen dürfen, rate ich mal, daß die 
Variablen nur 16 Bits breit sind.

Ein Anfang wäre z.B. anstatt
> #define LED_MASK 0b00000000111101111111011111110011
ein
1
#define LED_MASK 0b00000000111101111111011111110011UL
damit das nicht nur eine 16-Bit Konstante ist, sondern 32 Bits hat.

Ausserdem empfehle ich dir Warnungen zu aktivieren (z.B. -W -Wall) und 
darauf zu achten.

von Karl H. (kbuchegg)


Lesenswert?

1
while (~LED_MASK & (1<<currentLed));

du musst die 1 zu einem long machen, damit du auch die Bits über dem Bit 
15 noch erreichst. Wenn currentLED den Wert 16 hat, würde sonst

   1<<currentLED

eine glatte 0 ergeben, weil du das 1-Bit aus dem Bereich für int (16 
Bit) hinausgeschoben hast.

1
while (~LED_MASK & (1UL<<currentLed));


> und nach 24
> dauert es auch 8 Schritte bis er wieder bei 0 beginnt.

Logo.
Zitat
1
#define LED_MAX 32 // 24 LEDS -> bei 25 wieder auf 0 setzen!

wieso 32? Du hast doch nur 24 Led.

von Marc B. (drakenfly)


Lesenswert?

Also an und für sich klappt das jetzt endlich, danke für den Hinweis! 
Auf das hätte ich eigentlich auch selbst kommen müssen.... :(

Trotz allem leuchtet jetzt komischerweise die letzte LED heller als alle 
anderen.
1
#define LED_MASK 0b00000000111101111111011111110011UL
2
#define LED_MAX 24 // 24 LEDS -> bei 25 wieder auf 0 setzen!
3
ISR (TIMER0_OVF_vect)
4
{
5
  // LEDS pulsieren lassen über Register Display
6
  PORTB = 0xFF; // Alles aus, Dioden in Sperrichtung
7
  PORTC = 0x00;
8
  PORTB &= ~(1UL<<(currentLed / 4));
9
  PORTC |= (1UL<<(5 - (currentLed % 4)));
10
  do
11
  {
12
    currentLed++;
13
  }
14
  while ((~LED_MASK) & (1UL<<currentLed));
15
  if (currentLed >= LED_MAX) { // Es gibt nur "24" (aufgrund der Löcher) LEDS
16
    currentLed = 0; // -> Wieder von vorne beginnen
17
  }
18
}

Setze ich LED_MAX auf 23 leuchtet die letzte gar nicht mehr...

Lg Marc

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Möglicherweise wird für die letzte LED so lange geschoben (immerhin sind 
da einige 0en in LED_MASK), daß die nächste IRQ verpennt wird und erst 
verzögert nachgeholt wird.

1. In PORTB |= ... genügt 1 <<  anstatt 1UL << weil es nur einen
   8-Bit Wert berechnet wird und es dietch /4 bzw. %4 nicht zum
   Überlauf kommt.

2. Anstatt 1UL << in der Schleife ist es besser, eine Variable x mit
   LED_MASK zu füllen und die per ISR einmal nach rechts zu schieben.
   Wenn x == 0 ist, beginnt ein neuer Zyklus. Abgetestet, ob
   eine LED vorhanden ist, geht dann einfach per (x & 1).

Vielleicht löst das die Effizienzprobleme. Rumgeschiebe um variable 
offsets ist leider ineffizient auf AVR, zumal es in der Schleife 
mehrfach geschieht, vor allem gegen Ende, also nach LED 23.

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.