Forum: Mikrocontroller und Digitale Elektronik Pointer in C


von Oliver L. (ollil)


Lesenswert?

Hi,

in meinem Programm muss ich Daten von einem Port abholen und dabei 
diverse andere Pin-Stati beachten.
Es ist so, das die Anzahl der Daten durch ein vorheriges Lesen des Ports 
ermittelt werden.

1. Lese Port, ermittle Anzahl abzuholende Bytes
Ergebniss ist immer ein vielfaches von 2!
2. Lese Port erneut und zwar so oft wie Bytes abzuholen sind.

Ich habe mir am Anfang einfach ein großes Array deklariert mit der 
maximalen abzuholenden Größe die auftreten wird.

Ich wollte nun dazu übergehen, den Code zu modularisieren und auch mit 
Pointern und dynamischer Allokierung der Daten arbeiten. Folgender 
relevanter Codeabschnitt:
1
int main(void);
2
    uint8_t *cmdbuffer = NULL;
3
4
    wdc_read_data_from_p8k(&cmdbuffer
5
                          ,9
6
                          ,INFO_STAT_GCMD
7
    );
8
}
9
10
void wdc_read_data_from_p8k(uint8_t **buffer, uint8_t count, uint8_t wdc_status)
11
{
12
    int datacnt;
13
     
14
    (*buffer) = (uint8_t *)malloc(count * sizeof(**buffer));
15
    configure_port_data_read();
16
    datacnt = 0;
17
    port_info_set( wdc_status );
18
    while ( !isset_info_te() );
19
    while ( !isset_info_wdardy() );
20
    while ( datacnt < count )
21
    {
22
        while ( isset_info_wdardy() );
23
        port_info_set( INFO_ASTB | wdc_status );
24
        (*buffer)[datacnt] = PINA;
25
        port_info_set( wdc_status );
26
        while ( !isset_info_wdardy() );
27
        datacnt++;
28
    }
29
    port_info_set( INFO_CLEAR );
30
}

Kann mir erstmal einer sagen, ob das ganze korrektem C-Code entspricht?

Wenn ich buffer global als "uint8_t buffer[9]" deklariere, und auf 
malloc verzichte und direkt verwende in der Funktion, werden folgende 
Bytes gelesen:

18002eaaaa550200ff

Das ist korrekt.
Mit meiner Pointer-Lösung scheinen Bytes verschluckt zu werden:

18002eaaaa02ff1800

Ich denke, das kann zwei Ursachen haben:
1.) Die Zuweisung "(*buffer)[datacnt] = PINA" braucht mehr Zeit als 
"buffer[datacnt] = PINA" und da gehen dadurch Bytes verloren
2.) Das Array wird nicht korrekt allokiert. Bzw. mein C-Code hat einfach 
semantische Fehler.

Bin für jeden Tipp offen.

von Karl H. (kbuchegg)


Lesenswert?

Oliver Lehmann schrieb:

> Kann mir erstmal einer sagen, ob das ganze korrektem C-Code entspricht?

Jetztr nur auf das Array bzw. die Pointer Verwendung bezogen:
Ja, sieht gut aus

Den Rest hab ich mir nicht anbgesehen.

> Wenn ich buffer global als "uint8_t buffer[9]" deklariere, und auf
> malloc verzichte und direkt verwende in der Funktion, werden folgende
> Bytes gelesen:
>
> 18002eaaaa550200ff
>
> Das ist korrekt.
> Mit meiner Pointer-Lösung scheinen Bytes verschluckt zu werden:
>
> 18002eaaaa02ff1800
>
> Ich denke, das kann zwei Ursachen haben:
> 1.) Die Zuweisung "(*buffer)[datacnt] = PINA" braucht mehr Zeit als
> "buffer[datacnt] = PINA" und da gehen dadurch Bytes verloren

Das kann sein.
Wenn dein Code so zeitkritisch ist, müsste man sich da was anderes 
einfallen lassen.
Welche zeitlichen Nebenbedingungen hast du denn?

> 2.) Das Array wird nicht korrekt allokiert. Bzw. mein C-Code hat einfach
> semantische Fehler.

Nö, sieht gut aus.

>
> Bin für jeden Tipp offen.

von Klaus W. (mfgkw)


Lesenswert?

Das sieht mir auch korrekt aus.

Wenn es wirklich einen Unterschied gibt, würde ich darauf tippen, daß 
die erste Version falsch ist.
Wenn du es so geändert hast, wie ich vermute (mit dem globalen Puffer), 
wäre es eh falsch - aber den Code sehe ich ja nicht.

von Andreas B. (andreasb)


Lesenswert?

Ich habe den Beitrag nur überflogen, und ein "Fehler" entdeckt, der zwar 
dein Problem nicht lösen, aber ggf. trotzdem etwas bringen...

>    int datacnt;

wäre doch besser ein uint8_t?

Ich gehe mal von einem AVR aus, da ist "int" 16bit, und somit langsamer 
als uint8_t.

Zu 1)
ggf. könntest du uint8_t * buffer2 = *buffer;

schreibe, und dann buffer2 verwenden, ich bin mir aber nicht sicher ob 
das schneller ist...


mfg Andreas

von Peter II (Gast)


Lesenswert?

das sollt schneller sein

 uint8_t* akt_pos = buffer;
 while ( datacnt < count )
    {
        while ( isset_info_wdardy() );
        port_info_set( INFO_ASTB | wdc_status );
        *akt_pos  = PINA;
        akt_pos++;
        port_info_set( wdc_status );
        while ( !isset_info_wdardy() );
        datacnt++;
    }

von Andreas B. (andreasb)


Lesenswert?

Auf Intel Rechnernen villeicht.

Wenn es aber auf einem AVR läuft ist akt_pos++; eine 16 bit 
Inkrementation.

Wird hingegen mit einem 8bit Offset auf ein Array zugegriffen ist dies 
schneller, und der Compiler legt dies meistens auch noch in ein 
Register, kann ansonsten mit "register" gefördert (aber nicht erzwungen) 
werden.

Darum wenn möglich 8bit Offset und keine Pointer Arithmetik, den Pointer 
sind 16bittig auf AVRs...


mfg Andreas

Peter II schrieb:
> das sollt schneller sein
>
>  uint8_t* akt_pos = buffer;
>  while ( datacnt < count )
>     {
>         while ( isset_info_wdardy() );
>         port_info_set( INFO_ASTB | wdc_status );
>         *akt_pos  = PINA;
>         akt_pos++;
>         port_info_set( wdc_status );
>         while ( !isset_info_wdardy() );
>         datacnt++;
>     }

von Oliver L. (ollil)


Lesenswert?

So...

ich habe nun folgenden Code, dieser wird korrekt ausgeführt:

Die 9 ist natürlich nur ein Beispiel - es können bis zu 4096 Bytes 
übertragen werden.
1
int main(void)
2
{
3
    uint16_t cmd_counter = 9;
4
    uint8_t *cmd_buffer = NULL;
5
6
    cmd_buffer = malloc(cmd_counter*sizeof(cmd_buffer));
7
8
    wdc_read_data_from_p8k(cmd_buffer
9
                          ,cmd_counter
10
                          ,INFO_STAT_GCMD
11
                          );
12
}
13
14
void wdc_read_data_from_p8k(uint8_t *buffer, uint8_t count, uint8_t wdc_status)
15
{
16
    int datacnt;
17
   
18
    configure_port_data_read();
19
    datacnt = 0;
20
    port_info_set( wdc_status );
21
    while ( !isset_info_te() );
22
    while ( !isset_info_wdardy() );
23
    while ( datacnt < count )
24
    {
25
        while ( isset_info_wdardy() );
26
        port_info_set( INFO_ASTB | wdc_status );
27
        buffer[datacnt] = PINA;
28
        port_info_set( wdc_status );
29
        while ( !isset_info_wdardy() );
30
        datacnt++;
31
    }
32
    port_info_set( INFO_CLEAR );
33
    
34
}

Ich hätte halt nur  den malloc() gern mit in der Funktion gekappselt....
Achja - es ist ein ATMega1284P

von Peter II (Gast)


Lesenswert?

Andreas B. schrieb:
> Auf Intel Rechnernen villeicht.
> Wenn es aber auf einem AVR läuft ist akt_pos++; eine 16 bit
> Inkrementation.

aber ein pointer im AVR ist auch > 8bit. Und damit muss auch eine 
Operation mit 8 bit mit überlauf gemacht werden.

Bei der original version muss er sich 2 zeiger merken, einmal den anfang 
für den buffer und einmal die Position wo er hinscreiben muss, damit 
braucht er 2 pointer register (und ich glaube er nutzt nur eines). Damit 
kann er es nicht in den registern halten und muss ständig den zeiger auf 
den anfang neu laden.

Eventuell kann ja jemand mal den ASM code von beiden Varianten posten, 
habe gerade keine passene umgebung da.

von Karl H. (kbuchegg)


Lesenswert?

Oliver Lehmann schrieb:

> Ich hätte halt nur  den malloc() gern mit in der Funktion gekappselt....

Was in C meistens gar keine so gute Idee ist.

von Andreas B. (andreasb)


Lesenswert?

Oliver Lehmann schrieb:
> Die 9 ist natürlich nur ein Beispiel - es können bis zu 4096 Bytes
> übertragen werden.

Dann reicht aber der uint8_t nicht mehr!

Peter II schrieb:
> Andreas B. schrieb:
>> Auf Intel Rechnernen villeicht.
>> Wenn es aber auf einem AVR läuft ist akt_pos++; eine 16 bit
>> Inkrementation.
>
> aber ein pointer im AVR ist auch > 8bit.

Ja, 16 bit, das meinte ich oben...

> Und damit muss auch eine
> Operation mit 8 bit mit überlauf gemacht werden.

Genau, der AVR kann aber mit einem 8bit Offset zugreifen, dann musst du 
auch nur diesen Offset inkrementieren. (der "Basepointer" kann in einem 
Register bleiben, er ändert ja nicht)

Wurde hier im Forum mal komplett analysiert, weiss nicht mehr genau 
welcher Thread.

Es hat sich aber aber sowieso erledigt, denn bei 4096 braucht er sowieso 
einen 16bit Counter.

Wahrscheinlich dürfte dann die Pointerversion schneller sein, da er es 
direkt mit einem Pointer machen kann...


mfg Andreas

von free() (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was in C meistens gar keine so gute Idee ist.

... weil z.B. noch die Frage im Raum steht, wer den allozierten Speicher 
wieder freigibt.

von Oliver L. (ollil)


Lesenswert?

Andreas B. schrieb:
> Oliver Lehmann schrieb:
>> Die 9 ist natürlich nur ein Beispiel - es können bis zu 4096 Bytes
>> übertragen werden.
>
> Dann reicht aber der uint8_t nicht mehr!

Du meinst bei der Deklaration der Function weil dort count als uint8_t 
deklariert ist (was in der Tat falsch ist), oder bei der Deklaration von 
cmd_buffer?



Hier mal die ASM-Quellen:

mit malloc in der Funktion (1. Posting)
http://files.pofo.de/main_1.s

mit malloc vor der Funktion
http://files.pofo.de/main_2.s

von Karl H. (kbuchegg)


Lesenswert?

free() schrieb:
> Karl Heinz Buchegger schrieb:
>> Was in C meistens gar keine so gute Idee ist.
>
> ... weil z.B. noch die Frage im Raum steht, wer den allozierten Speicher
> wieder freigibt.

Genau. Ist zb ein Thema.

Ein anderes Thema lautet: Was soll die Funktion machen, wenn sie den 
Speicher nicht bekommt?
Wieder ein anderes Thema lautet: Wenn die Funktion den Speicher einfach 
vorgegeben bekommt, dann kann ich ihr dynamisch allokierten Speicher 
aber auch ein fix allokiertes Array als Speicherfläche anbieten. D.h. 
aus Softwaresicht ist diese Variante universeller benutzbar.

von Walter S. (avatar)


Lesenswert?

Oliver Lehmann schrieb:
> sizeof(cmd_buffer)

das ist nicht das was du willst, das gibt dir die Größe eines Pointers,
dein array besteht aber nicht aus pointern!

von Oliver L. (ollil)


Lesenswert?

Walter S. schrieb:
> Oliver Lehmann schrieb:
>> sizeof(cmd_buffer)
>
> das ist nicht das was du willst, das gibt dir die Größe eines Pointers,
> dein array besteht aber nicht aus pointern!

besser sizeof(uint8_t)?

von Karl H. (kbuchegg)


Lesenswert?

Oliver Lehmann schrieb:
> Walter S. schrieb:
>> Oliver Lehmann schrieb:
>>> sizeof(cmd_buffer)
>>
>> das ist nicht das was du willst, das gibt dir die Größe eines Pointers,
>> dein array besteht aber nicht aus pointern!
>
> besser sizeof(uint8_t)?


sizeof(*cmd_buffer)

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.