Werden Zeiger in AVR anders behandelt als beispielsweise x86?

7

Mikrocontroller: ATtiny13

IDE: Atmel Studio

Ich versuche, eine Hello World-Anwendung zu schreiben, indem ich High an Port PB4 schreibe.

Das funktioniert gut:

int main(void)
{
    DDRB = 0x10;
    PORTB = 0x10;

    while(1)
    {
    }
}

Bei der Definition von DDRB und PORTB zeigen sie erwartungsgemäß auf 0x17 und 0x18.

Dies funktioniert jedoch nicht:

int main(void)
{
    char *dir = (void *)0x17;
    char *port = (void *)0x18;

    *dir = (unsigned int)0x10;
    *port = (unsigned int)0x10;

    while(1)
    {
    }
}

Ist mein Code falsch oder muss ich etwas anderes tun, um Zeiger zu verwenden?

tgun926
quelle
1
Warum sollten Sie Zeiger für etwas so Einfaches verwenden?
Matt Young
2
Ich muss irgendwo anfangen - beim ersten Programmieren eines AVR-MCU.
tgun926
5
Ein Vergleich der Demontage beider wäre eine gute Übung.
Matt Young
1
Die unsigned intDarsteller machen keinen Sinn; Die Datenrichtungs- und Portregister sind nur 8 Bit breit. Es ist ziemlich unwahrscheinlich, dass es Ihr Problem ist, aber versuchen Sie es mit Casting uint8_t.
Sherellbc
@sherrellbc Ich habe verschiedene Arten von Casting ausprobiert und kein Casting, aber es hat keinen Unterschied gemacht. 0x10ist 8 Bit breit, nicht wahr?
tgun926

Antworten:

23

Zeiger sind Zeiger. Das sind sie. Sie werden überhaupt nicht anders behandelt (wie könnten Sie sie anders behandeln?)

Die Hauptunterschiede zwischen X86 und AVR sind:

  • AVR ist 8 Bit, X86 ist 32 Bit (oder 64 Bit für x86_64), daher haben Zeiger eine andere Größe.
  • Der AVR ist eine modifizierte Harvard-Architektur, daher gibt es mehr als einen Adressraum. Sie müssen also sicherstellen, dass Sie auf den richtigen Adressraum verweisen.

Außerdem macht Ihr Code keinen Sinn:

char *dir = (void *)0x17;

Eine Leere * einem Zeichen * zuweisen?

error: invalid conversion from void*’ to char*’

Ich bin momentan nicht für die Kompilierung des ATTiny13 eingerichtet, daher gelten diese Zahlen alle für den ATMega328p:

Der Zugriff auf DDRB und PORTB führt zu dieser Assembly:

12c:    80 e1           ldi r24, 0x10   ; 16  
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Der Zugriff auf einen Zeiger auf einen Speicherort führt zu dieser Assembly:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    80 93 17 00     sts 0x0017, r24
132:    80 93 18 00     sts 0x0018, r24

Wie Sie sehen können, sind DDRB und PORTB keine normalen Variablen. DDRB ist definiert als:

#define DDRB _SFR_IO8(0x04)

und PORTB als:

#define PORTB _SFR_IO8(0x05)

_SFR_IO8 () ist ein Makro:

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

und _MMIO_BYTE ist:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

__SFR_OFFSET kann je nach Chip entweder 0 oder 0x20 sein (normalerweise 0x20).

Das muss also bedeuten, dass die Adressen von DDRB und PORTB größer als 0x20 sein müssen.

Mit Blick auf den 328P-Chip ist DDRB 0x04 und PORTB 0x05. Zugriff als 0x24 und 0x25 mit den richtigen Datentypen also:

volatile uint8_t *dir = (volatile uint8_t *)0x24;
volatile uint8_t *port = (volatile uint8_t *)0x25;

*dir = 0x10;
*port = 0x10;

Ergebnisse in dieser Baugruppe:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Ähnlich aussehend? Der Compiler hat den 0x20-Offset erkannt, erkannt, dass es sich um SFRs handelt, und in den richtigen outAnweisungen ohne den 0x20-Offset kompiliert .

Der Zugriff auf Ihre Portadressen + 0x20 funktioniert möglicherweise für den ATTiny13.

Wenn man sich nur den ATTiny25 ansieht, DDRB = 0x10ergibt sich Folgendes :

out 0x17, r24

und der Zugriff auf einen Zeiger an der Adresse 0x37 führt zu:

out 0x17, r24

Das sieht also so aus, als wäre es wahrscheinlich (fügen Sie Ihrer Zeigeradresse 0x20 hinzu).

Majenko
quelle
Was meinst du damit, dass es mehr als einen Adressraum gibt? Ich habe mir die Dokumente für das mcu angesehen und es zeigt eine Speicherzuordnung, von der aus 0x0auf 0x9Fdie Register I / O und SRAM zugegriffen werden kann . Was sind die anderen Adressräume?
tgun926
2
Die Harvard-Architektur definiert mindestens 2 Adressräume - Programm (Flash) und Daten (sram). Es gibt spezielle Funktionen für den Zugriff auf den Programmadressraum.
Majenko
4
Sie können optimiert werden, da der Compiler nicht weiß, dass sie gelesen werden. Wenn eine Variable gesetzt wird, aber noch nie gelesen wird, kann sie den Schreibvorgang verwerfen, da sie nirgendwo verwendet wird. Wenn es flüchtig ist, teilt es dem Compiler mit, dass es an anderer Stelle gelesen wird (in diesem Fall in der Hardware), sodass der Schreibvorgang an Ort und Stelle bleibt.
Majenko
1
SFR = Sonderfunktionsregister. Es erkennt sie an der Adresse. Keine Ahnung wie, aber es tut. Sie sollten die Datei ioxxxx.h nach dem von Ihnen verwendeten Chip durchsuchen. Es enthält alle Definitionen für diese Register.
Majenko
1
Sogar auf dem im PC verwendeten 8x86 (und dem 8080 davor) gab es separate Anweisungen für den Zugriff auf E / A-Speicherplatz und Speicher. Einige E / A-Geräte, wie z. B. Anzeigekarten, verwenden zusätzlich zu einem E / A-Adressraum einen Teil des Speicheradressraums. Auf dem 8x86 gibt es jedoch die Möglichkeit, einen Zeiger auf z. B. das LPT1-Steuerregister zu definieren. Es hat eine Adresse (0x378), aber das Schreiben von 0x34 in einem typischen C-Dialekt würde dies erfordern outportb(0x378, 0x34);.
Supercat