Forum: Mikrocontroller und Digitale Elektronik Strukt initialisieren probleme


von Chandler B. (chandler)


Lesenswert?

Hallo,
ich möchte den Status von verschiedenen Relais in einet Struktur 
speichern
1
typedef enum
2
{
3
    RELAIS_1,
4
    RELAIS_2,
5
    RELAIS_3,
6
    RELAIS_4,
7
    RELAIS_5,
8
    NOF_RELAIS
9
} Relais_te;
10
11
typedef struct
12
{
13
    const uint8_t pin;
14
    bool state_b;
15
} Relais_ts;
16
17
18
static Relais_ts relais_as[NOF_RELAIS] = {
19
    [RELAIS_1] = { .pin = (1 << PINB1), .state_b = false },
20
    [RELAIS_2] = { .pin = (1 << PINB2), .state_b = false },
21
    [RELAIS_3] = { .pin = (1 << PINB3), .state_b = false },
22
    [RELAIS_4] = { .pin = (1 << PINB4), .state_b = false },
23
    [RELAIS_5] = { .pin = (1 << PINB5), .state_b = false }
24
};

jedoch bekomme ich bei der Initialisierung für jeden Pin den Wert 0 
heraus.
Ich verstehe es aber nicht, woran es noch liegen könnte.

Was mache ich bei der initialisierung falsch?
1
static void relaisInit_v(void);
2
static void relaisInit_v(void)
3
{
4
    char c;
5
    for (uint8_t relais_e=0; relais_e<NOF_RELAIS; relais_e++)
6
    {
7
        Relais_ts* relais_ps;
8
        relais_ps = &relais_as[RELAIS_5];
9
        c = relais_e + '0';
10
        uart_characterPut(c);
11
        c = relais_ps->pin + '0';
12
        uart_characterPut(c);
13
14
        UNSETPIN(PORTB, relais_ps->pin);
15
        relais_ps->state_b = false;
16
    }
17
}
hier bekomme dann als ausgabe
0010203040

: Verschoben durch Moderator
von Klaus H. (klummel69)


Lesenswert?

Liegt vermutlich daran, dass du in Zeile 8 immer das gleiche Element 
ansprichst.

relais_ps = &relais_as[RELAIS_5];

Da solte vermutlich der index relais_e rein?

von Chandler B. (chandler)


Lesenswert?

Klaus H. schrieb:
> Liegt vermutlich daran, dass du in Zeile 8 immer das gleiche
> Element
> ansprichst.
>
> relais_ps = &relais_as[RELAIS_5];
>
> Da solte vermutlich der index relais_e rein?

Ja, da sollte relais_e rein.
Trotzdem sollte keine 0 rauskommen.
Auch wenn ich relais_e eintrage, bekomme ich immer 0 zurück.

Da 1 << PINB5 auch nicht mehr in einem character reinpasst das auch noch 
ein wenig angepasst.
1
... ...
2
        if (relais_ps->pin == 0)
3
        {
4
            c = 0 + '0';
5
        }
6
        else
7
        {
8
            c = 1 + '0';
9
        }
10
        //c = relais_ps->pin + '0';
11
        uart_characterPut(c);

: Bearbeitet durch User
von A. S. (rava)


Lesenswert?

passt denn 1 << PINX in ein uint8?

von Klaus H. (klummel69)


Lesenswert?

Das riecht ziemlich nach einem Overflow.

Sind die PIN Nummern >= 8 ?
Speichere mal direkt PINBx ohne Shift und schau dir die Ausgabe aus.

Hast Du die Warnings des Compilers ausgeschaltet?

Was nutzt Du denn für einen Compiler/Plattform?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Die Initialisierung funktioniert jedenfalls:
1
int main(void)
2
{
3
  PORTB = relais_as[RELAIS_4].pin;
4
  a4:  80 e1         ldi  r24, 0x10  ; 16
5
  a6:  85 b9         out  0x05, r24  ; 5
6
  PORTB = relais_as[RELAIS_5].pin;
7
  a8:  80 e2         ldi  r24, 0x20  ; 32
8
  aa:  85 b9         out  0x05, r24  ; 5

von Andreas M. (amesser)


Lesenswert?

Möglicherweise wird relais_as beim Anlaufen von der runtime nicht sauber 
initialisiert? Man könnte mal in den de-assemblierten Code schauen um 
das zu prüfen.

von Oliver S. (oliverso)


Lesenswert?

Chandler B. schrieb:
> Hallo,
> ich möchte den Status von verschiedenen Relais in einet Struktur
> speichern

Du postest in der Rubrik "PC-Programmierung", der Code lässt aber auf 
einen AVR schliessen.

Welcher Compiler in welcher Version für welche Zielhardware ist es denn?

Oliver

von Chandler B. (chandler)


Lesenswert?

Oliver S. schrieb:
> Du postest in der Rubrik "PC-Programmierung", der Code lässt aber auf
> einen AVR schliessen.
>
> Welcher Compiler in welcher Version für welche Zielhardware ist es denn?

oh ?, tatsache :/
Ja, es geht auch um einem AVR auf einem Arduino Board (atmega328p)

als compiler benutze ich den avr-gcc compiler und flashe das ganze mit 
avrdude über isp auf dem atmega.

Klaus H. schrieb:
> Sind die PIN Nummern >= 8 ?
> Speichere mal direkt PINBx ohne Shift und schau dir die Ausgabe aus.
Die Pins sind PINB0=0, PINB1=1, PINB2=2, ..., PINB7=7 also sowieso 
kleiner als 8 und ich benutze auch nur bis PINB5

Ich muss das morgen noch einmal testen, wenn ich wieder zu hause bin.

: Bearbeitet durch User
von Sebastian W. (wangnick)


Lesenswert?

Passiert bei dieser vereinfachten Initialisierung dasselbe?
1
static Relais_ts relais_as[NOF_RELAIS] = {
2
    {1 << PINB1},
3
    {1 << PINB2},
4
    {1 << PINB3},
5
    {1 << PINB4},
6
    {1 << PINB5}
7
};

LG, Sebastian

von Peter D. (peda)


Lesenswert?

Nur mal Interesse halber, welcher tiefere Sinn steckt dahinter, die 
Pinzugriffe derartig zu verschwurbeln.
D.h. wie soll die praktische Anwendung aussehen?

Vermutlich wird der Compiler dadurch gezwungen, die Zugriffe memory 
mapped zu machen, also nicht einfach über direkte Bitbefehle und auch 
nicht atomar (mögliche Konflikte beim Setzen in Interrupts).

von Oliver S. (oliverso)


Lesenswert?

Chandler B. schrieb:
> als compiler benutze ich den avr-gcc compiler

Schön, und welche Version genau? Windows oder Linux?

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte poste mal ein compilierbares Minimalbeispiel, sowohl den 
Sourcecode als auch das bei dir compilierte ELF-File.

von Klaus H. (klummel69)


Lesenswert?

Peter D. schrieb:
> Vermutlich wird der Compiler dadurch gezwungen, die Zugriffe memory
> mapped zu machen, also nicht einfach über direkte Bitbefehle und auch
> nicht atomar (mögliche Konflikte beim Setzen in Interrupts).

Vermutlich. Wobei gcc durchaus einige Varianten erkennt und entsprechend 
sbi oder cbi einsetzt.

Würde hier aber auch empfehlen statt der Maske eher die Pin-Nummer zu 
speichern und im Code "PORTB |= (1<< x.pin)"... zu nutzen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus H. schrieb:
> Würde hier aber auch empfehlen statt der Maske eher die Pin-Nummer zu
> speichern und im Code "PORTB |= (1<< x.pin)"... zu nutzen.

Ineffizient. Worst case hast du dann zur Laufzeit eine Schleife nur 
dafür, dass du die Bitmaske ausrechnest.

von Klaus H. (klummel69)


Lesenswert?

Jörg W. schrieb:
> Ineffizient. Worst case hast du dann zur Laufzeit eine Schleife nur
> dafür, dass du die Bitmaske ausrechnest.

Warum?
Ineeffizient bedeutet unwirtschaftlich oder nicht leistungsfähig.
Aber das gilt im Sinne der Ressourcen nur wenn es von der 
Geschwindigkeit darauf ankommt. Wenn ich beim Programmieren schnell zum 
Ziel komme ist das durchaus "effizient"....

Wenn die Pinnummer statt der Maske gespeichert wird, kann man auch 
direkt die asm Befehle (sbi, cbi ) nutzen. Schneller geht's nicht. Aber 
das würde ich nur machen wenn es aus Performance Gründen Sinn macht.

Goldene Regel der Softwareprogrammierung:
Nie im Voraus manuell optimieren. Nur wenn eine messbare Verbesserung 
daraus entsteht. Aktuelle Compiler können es normalerweise besser.

Beitrag #7599031 wurde vom Autor gelöscht.
von Chandler B. (chandler)


Angehängte Dateien:

Lesenswert?

Jörg W. schrieb:
> Bitte poste mal ein compilierbares Minimalbeispiel, sowohl den
> Sourcecode als auch das bei dir compilierte ELF-File.

siehe anhang

Oliver S. schrieb:
> Schön, und welche Version genau? Windows oder Linux?

Windows
avr-gcc (GCC) 5.3.0

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus H. schrieb:

> Wenn die Pinnummer statt der Maske gespeichert wird, kann man auch
> direkt die asm Befehle (sbi, cbi ) nutzen. Schneller geht's nicht.

Du denkst viel zu sehr in Assembler.

In C gibt es keine Notation für eine Bit-Nummer. Es gibt nur Bitmasken, 
mit denen man logische Verknüpfungen machen kann. Wenn du die Bitmaske 
gleich in der Initialisierung erzeugst, dann ist sie zumindest 
garantiert für den Compiler sichtbar eine Konstante. Erzeugst du sie 
erst später, weil du nur eine Bit-Nummer hast, dann hängt es vom Kontext 
ab, ob der Compiler erkennen kann, dass sie konstant ist. Kann er es 
nicht, läufst du Gefahr, dass dort eine Schleife aus Schiebebefehlen 
produziert wird.

Die Umwandlung einer konstanten Bitmaske in CBI/SBI (oder SBIS/SBIC) ist 
ohnehin Aufgabe des Compilers, die kannst du ihm nicht abnehmen, indem 
du irgendwo nur Bit-Nummern abspeicherst.

Chandler B. schrieb:
> siehe anhang

Der Code sieht korrekt aus. Deine struct steht als Einziges in .data:
1
Contents of section .data:
2
 800100 02000400 08001000 2000               ........ .

.data wird auch ordnungsgemäß in den RAM geladen beim Start:
1
00000074 <__do_copy_data>:
2
  74:   11 e0           ldi     r17, 0x01       ; 1
3
  76:   a0 e0           ldi     r26, 0x00       ; 0
4
  78:   b1 e0           ldi     r27, 0x01       ; 1
5
  7a:   e6 ed           ldi     r30, 0xD6       ; 214
6
  7c:   f2 e0           ldi     r31, 0x02       ; 2
7
  7e:   02 c0           rjmp    .+4             ; 0x84 <__do_copy_data+0x10>
8
  80:   05 90           lpm     r0, Z+
9
  82:   0d 92           st      X+, r0
10
  84:   aa 30           cpi     r26, 0x0A       ; 10
11
  86:   b1 07           cpc     r27, r17
12
  88:   d9 f7           brne    .-10            ; 0x80 <__do_copy_data+0xc>

Falls du nicht direkt das ELF programmierst sondern das Hex-File, auch 
das sieht korrekt aus:
1
:0A02D60002000400080010002000E0

Die Abfrage der einzelnen Pin-Masken ist etwas umständlich, aber 
korrekt:
1
 1b0:   00 e0           ldi     r16, 0x00       ; 0
2
 1b2:   11 e0           ldi     r17, 0x01       ; 1
3
 1b4:   80 e3           ldi     r24, 0x30       ; 48
4
 1b6:   f8 2e           mov     r15, r24
5
 1b8:   8f 2d           mov     r24, r15
6
 1ba:   0e 94 53 00     call    0xa6    ; 0xa6 <uart_characterPut>
7
 1be:   f8 01           movw    r30, r16
8
 1c0:   80 81           ld      r24, Z
9
 1c2:   88 23           and     r24, r24
10
 1c4:   11 f0           breq    .+4             ; 0x1ca <main+0x4e>
11
 1c6:   81 e3           ldi     r24, 0x31       ; 49
12
 1c8:   01 c0           rjmp    .+2             ; 0x1cc <main+0x50>
13
 1ca:   80 e3           ldi     r24, 0x30       ; 48
14
 1cc:   0e 94 53 00     call    0xa6    ; 0xa6 <uart_characterPut>
15
 1d0:   95 b1           in      r25, 0x05       ; 5
16
 1d2:   f8 01           movw    r30, r16
17
 1d4:   80 81           ld      r24, Z
18
 1d6:   89 2b           or      r24, r25
19
 1d8:   85 b9           out     0x05, r24       ; 5
20

In r30:r31 steht (anfangs) die Adresse 0x100 (aus r16:r17 übernommen). 
Der entsprechende Wert wird zuerst gelesen und auf 0 / !0 verglichen, in 
Abhhängig davon wird '0' oder '1' in r24 geladen und uart_characterPut() 
gerufen. Danach wird PORTB eingelesen, der Wert erneut von 0x100 
gelesen, mit dem Wert aus PORTB verODERt und auf PORTB geschrieben.

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Der Code sieht korrekt aus.

Ja, aber das hier dürfte dann der Quell allen Übels sein:
1
static volatile unsigned char strTxBuffer[1024];
2
static volatile unsigned char strRxBuffer[1024];

Oliver

von Klaus H. (klummel69)


Lesenswert?

Jörg W. schrieb:
> Du denkst viel zu sehr in Assembler.
Ups das wollte ich eigentlich nicht. Ich wollte ausdrücken dass man 
nicht zu schnell optimieren sollte.

Ist schon eine Weile her, dass ich avr programmiert habe. Das meiste 
wurde mit Bitmasken gemacht. Nur in speziellen Optimierungsfällen 
(Geschwindigkeit einer Pin Ausgabe) hab ich darauf geachtet, dass 
sbi/cbi genutzt wurde.

Hatte nicht mehr daran gedacht dass die Pin-Nummer im Opcode des Befehls 
gespeichert ist. Der Compiler kann es dann nicht in allen Fällen 
auflösen.
Asche auf mein Haupt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Jörg W. schrieb:
>> Der Code sieht korrekt aus.
>
> Ja, aber das hier dürfte dann der Quell allen Übels sein:

Uff, ja. :-}

2 KiB allein an Puffern für einen Controller, der insgesamt 2 KiB SRAM 
hat.

Klaus H. schrieb:
> Nur in speziellen Optimierungsfällen (Geschwindigkeit einer Pin
> Ausgabe) hab ich darauf geachtet, dass sbi/cbi genutzt wurde.

Ist ja durchaus sinnvoll.

> Hatte nicht mehr daran gedacht dass die Pin-Nummer im Opcode des Befehls
> gespeichert ist.

Ja, leider. Das ist wohl der Preis dafür, dass man diese Befehle in 
einem Taktzyklus abarbeiten kann.

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.