Forum: Mikrocontroller und Digitale Elektronik Eigenartiges Verhalten bei Schreibzugriff auf Register (ATMega162)


von Alex (Gast)


Lesenswert?

Hallo!

Ich hab hier ein Problem, an dem ich fast verzweifele...
Ich möchte eine Matrixtastatur an meinen ATMega162 an PORT A 
anschließen. Dafür will ich mir eine Funktion bauen, die die aktuell 
gedrückte Zahl ausgeben soll. Momentan erkenne ich nur die Zeile:
1
#define MATRIX_NOKEY  12
2
#define MATRIX_INVALID  13
3
4
#define nop() \
5
  __asm__("nop\n\t"  \
6
    "nop\n\t")
7
8
9
uint8_t matrix_poll_number() {
10
  uint8_t number = MATRIX_NOKEY;
11
  uint8_t input_value = 0;
12
13
  // Zeile herausbekommen
14
  DDRA = 0b10000111;                // Zeilen als Eingänge konfig.
15
  PORTA = 0b01111000;                // Pull-Ups aktivieren, Spalten auf GND ziehen
16
  nop();
17
  input_value = PINA;
18
19
  if (input_value == 0b01111000) number = MATRIX_NOKEY;
20
  else if (input_value == 0b00111000) {
21
    // Vierte Zeile
22
    number = 24;
23
  }
24
  else if (input_value == 0b01011000) {
25
    // Dritte Zeile
26
    number = 23;
27
  }
28
  else if (input_value == 0b01101000) {
29
    // Zweite Zeile
30
    number = 22;
31
  }
32
  else if (input_value == 0b01110000) {
33
    // Erste Zeile
34
    number = 21;
35
  }
36
  else number = MATRIX_INVALID;
37
38
  DDRA = 0b11111000;
39
  PORTA = 0b00000111;
40
41
  return number;
42
}

Die Funktion wird im Hauptprogramm periodisch ca. alle 500ms aufgerufen 
und der Rückgabewert wird auf dem UART ausgegeben.

Das komische ist nun folgendes: Wenn ich eine oder beide der vorletzten 
Anweisungen der Funktion, also DDRA = 0b11111000; und PORTA = 
0b00000111; auskommentiere, funktioniert alles wie erwartet.
Wenn aber beide Anweisungen drin sind kommt immer MATRIX_INVALID als 
Rückgabewert. Wie kann das denn sein, wenn zum Zeitpunkt der beiden 
Anweisungen der Inhalt der Variable number schon feststeht?

Kann mir da jemand helfen?

von Julian B. (julinho)


Lesenswert?

schaltplan?

von (prx) A. K. (prx)


Lesenswert?

Häng mal ein paar NOPs mehr rein. Oder gleich _delay_us(1). Wenn der Weg 
zu den Tasten länger und die Taktfrequenz hoch ist, dann könnte es sein, 
dass die eher hochohmigen Pullups nicht genug Zeit haben.

von Alex (Gast)


Lesenswert?

Ok, mit _delay_us(1); funktionierts. Aber nicht etwa, weil die 2 nops zu 
schnell waren, sondern wegen der Codeoptimierung. Aus welchen Grund auf 
immer hat der Compiler die beiden Schreibzugriffe auf das Register 
vorgezogen (vor die Abfrage von PINA). Fragt sich nur, warum...

von Krapao (Gast)


Lesenswert?

Weil du eine Optimierungsstufe angegeben hast, bei der ein Umordnen von 
Anweisungen erlaubt ist und durch die Umordnung ein schnellerer oder 
kleinerer (je nach Optimierungsvorgabe) Code entstanden ist.

Der Compiler weiss nicht, dass das Setzen der I/O-Pins und das Abfragen 
logisch und physikalisch zusammen gehören

Um das Problem zu umgehen, könnte man versuchen eine Funktion für diese 
Anweisungen zu verwenden ggf. auch als inline Funktion.

1
static inline uint8_t get_PINA(void)
2
{
3
  // Zeile herausbekommen
4
  DDRA = 0b10000111;  // Zeilen als Eingänge konfig.
5
  PORTA = 0b01111000; // Pull-Ups aktivieren, Spalten auf GND ziehen
6
  nop();
7
  return PINA;
8
}
9
10
uint8_t matrix_poll_number() {
11
  uint8_t number = MATRIX_NOKEY;
12
  uint8_t input_value = get_PINA();
13
...

von (prx) A. K. (prx)


Lesenswert?

Krapao schrieb:

> Der Compiler weiss nicht, dass das Setzen der I/O-Pins und das Abfragen
> logisch und physikalisch zusammen gehören

Die Ports sind volatile, da ist Umordnen unzulässig. Aber die NOPs waren 
nicht volatile (asm volatile (...)).

von (prx) A. K. (prx)


Lesenswert?

Alex schrieb:

> Ok, mit _delay_us(1); funktionierts. Aber nicht etwa, weil die 2 nops zu
> schnell waren, sondern wegen der Codeoptimierung. Aus welchen Grund auf
> immer hat der Compiler die beiden Schreibzugriffe auf das Register
> vorgezogen (vor die Abfrage von PINA). Fragt sich nur, warum...

Kannst du das belegen? Das wäre ein ernster Compilerfehler.

von Alex (Gast)


Lesenswert?

Vielen Dank für die schnelle Hilfe! So funktionierts!

von Alex (Gast)


Lesenswert?

A. K. schrieb:
> Alex schrieb:
>
>> Ok, mit _delay_us(1); funktionierts. Aber nicht etwa, weil die 2 nops zu
>> schnell waren, sondern wegen der Codeoptimierung. Aus welchen Grund auf
>> immer hat der Compiler die beiden Schreibzugriffe auf das Register
>> vorgezogen (vor die Abfrage von PINA). Fragt sich nur, warum...
>
> Kannst du das belegen? Das wäre ein ernster Compilerfehler.

Nein, kann ich nicht, da ich mich dabei getäuscht habe (sorry, hab was 
verwechselt.) Das Problem scheint zwar durch Codeoptimierung 
hervorgerufen zu werden, aber die Schreibzugriffe werden nicht 
verschoben.

von (prx) A. K. (prx)


Lesenswert?

Eben. Die NOPs wurden verschoben oder vielleicht auch ganz einkassiert, 
weil nicht volatile. Daher stimmte das Timing nur, wenn die Pullups 
vorher schon aktiv waren. Ohne die beiden Zeilen blieben sie aktiv und 
es funktionierte.

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.