Forum: Mikrocontroller und Digitale Elektronik Keil C51 und Bitfelder in einer Struktur (8051)


von Neb N. (bluemorph)


Lesenswert?

Hallo Leute,

ich habe ein Problem. Im Zuge meiner BA - Arbeit möchte ich ein SD 
Karten Modul in ein Systemtelefon integrieren. Im Systeltelefon wird ein 
8051 Modell verwendet (AT89C51RB2). Hier wird mit dem Keil C51 Compiler 
gearbeitet.

Ich möchte nun gerne die beiden Informationsregister CID und CSD der SD 
Karte auslesen. Diese sind jeweils 16 (+ 2 Byte CRC) Byte lang. Beide 
Register schreibe ich in globale unsigned char Felder v_cid[18] und 
v_csd[18]. Die beiden Felder beinhalten nun die ausgelesenen Daten der 
SD Karten Register.
Die 16 Byte Daten des CID Registers beinhalten Informationen, wobei 
einige Informationen ein Byte lange sind, andere nur wenige Bits. Um 
besser an diese Informationen heranzukommen, habe ich eine Struktur 
definiert:
1
typedef struct {
2
                t_uchar  m_id;
3
                t_uchar  oem[2];
4
                t_uchar  pnm[5];
5
                t_uchar  prv;
6
                long     psn;
7
                unsigned            : 4;
8
                unsigned year       : 8;
9
                unsigned month      : 4;
10
                unsigned crc7       : 7;
11
                unsigned            : 1;
12
                unsigned crc16      : 16;
13
                unsigned            : 0;
14
                } T_cid;

In einer Funktion möchte ich mir nun die Informationen ausgeben lassen. 
Dafür folgender Code:
1
T_cid *V_cid = (T_cid *) &v_cid[0];

Damit habe ich meine Struktur nun "über" das Feld gelegt, worin meine 
Daten liegen.

Lasse ich mir nun den string "pnm" ausgeben (indem ich V_cid->pnm 
anspreche), so beinhaltet dieser den product name der SD Karte. Dieser 
stimmt auch mit dem Inhalt von v_cid überein.

Lasse ich mir hingegen mit V_cid->year das Jahr, bzw. mit V_cid->month 
den Monat anzeigen, so stehen da falsche Werte.

Damit ihr alles nachvollziehen könnt, hier die Daten die in v_cid drin 
stehen (im HEX Format):

03 53 44 53 44 30 32 47 80 70 FA 11 ED 0 0 99 4F EE E1

Die Fett markierten Hex - Ziffern sollten eigentlich das Jahr und den 
Monat enthalten.

09 -> Jahr
9  -> September

Beim Zugriff erhalte ich folgende Daten:
V_cid->year  = 09
V_cid->month = 00
V_cid->crc7  = 6e

Wie ihr seht steht da nicht wirklich das drin, was ich gerne hätte.

Gleiches mit dem CSD Register und dem v_csd Feld. Diesmal sieht die 
Struktur so aus:
1
typedef struct {
2
                unsigned                : 8;
3
                unsigned taac           : 8;
4
                unsigned nsac           : 8;
5
                unsigned tran_speed     : 8;
6
                unsigned ccc            : 12;
7
                unsigned read_bl_len    :  4;
8
                unsigned read_bl_part   : 1;
9
                unsigned write_blk_mis  : 1;
10
                unsigned read_blk_mis   : 1;
11
                unsigned dsr_imp        : 1;
12
                unsigned                : 2;
13
                unsigned c_size         : 12;
14
                unsigned vdd_r_cur_min  : 3;
15
                unsigned vdd_r_cur_max  : 3;
16
                unsigned vdd_w_cur_min  : 3;
17
                unsigned vdd_w_cur_max  : 3;
18
                unsigned c_size_mult    : 3;
19
                unsigned erase_blk_len  : 1;
20
                unsigned sector_size    : 7;
21
                unsigned wp_grp_size    : 7;
22
                unsigned wp_grp_enable  : 1;
23
                unsigned                : 2;
24
                unsigned r2w_factor     : 3;
25
                unsigned write_bl_len   : 4;
26
                unsigned write_bl_part  : 1;
27
                unsigned                : 5;
28
                unsigned file_form_grp  : 1;
29
                unsigned copy           : 1;
30
                unsigned perm_w_prot    : 1;
31
                unsigned tmp_w_prot     : 1;
32
                unsigned file_format    : 2;
33
                unsigned                : 2;
34
                unsigned crc7           : 7;
35
                unsigned                : 1;
36
                unsigned crc16          : 16;
37
                unsigned                : 0;
38
                } T_csd;

Die Hex-Daten im Feld v_csd sehen folgendermaßen aus:

00 26 00 32 5F 5A 83 AE FE FB CF FF 92 80 40 DF 9F C5

Ich hab das auch mal aufgeschlüsselt:
00h      --> reserved bits
26h      --> TAAC
00h      --> NSAC
32h      --> TRAN_SPEED
5fh 0101b    --> CCC
1010b      --> READ_BL_LEN
1b            --> READ_BL_PARTIAL
0b      --> WRITE_BLK_MISALIGN
0b      --> READ_BLK_MISALIGN
0b      --> DSR_IMP
00b      --> reserved bits
11b 1010b 1110b 11b  --> C_SIZE
111b      --> VDD_R_CURR_MIN
110b      --> VDD_R_CURR_MAX
111b      --> VDD_W_CURR_MIN
110b      --> VDD_W_CURR_MAX
111b      --> C_SIZE_MULT
1b      --> EREASE_BLK_LEN
0011111b    --> SECTOR_SIZE
1111111b    --> WP_GRP_SIZE
1b      --> WP_GRP_ENABLE
00b      --> reserved bits
100b      --> R2W_FACTOR
1010b      --> WRITE_BL_LEN
0b      --> WRITE_BL_PARTIAL
00000b      --> reserved bits
0b      --> FILE_FORMAT_GRP
1b      --> COPY
0b      --> PERM_WRITE_PROTECT
0b      --> TMP_WRITE_PROTECT
00b       --> FILE_FORMAT
00b      --> reserved bits
dfh      --> CRC7 (+ LSB = 1)
9fh c5h     --> CRC16

Beim auslesen der Daten mit V_csd kommt folgendes heraus:

V_csd->taac        = 00
V_csd->tran_speed  = 00
V_csd->read_bl_len = 05
V_csd->c_size_mult = 07

Wie ihr seht, passt auch hier nichts wirklich überein!

Hat irgendeiner einen Tip für mich, wie ich das nun besser hinbekomme? 
Bzw. weis jemand woran das liegt?

MfG BlueMorph

von AkkiSan (Gast)


Lesenswert?

Benny Nestler schrieb:
> Hat irgendeiner einen Tip für mich, wie ich das nun besser hinbekomme?
> Bzw. weis jemand woran das liegt?

Ja, will mir aber nicht die Finger wundtippen, Tschuldigung ;-)

http://www.keil.com/support/man/docs/c51/c51_le_bitaddrobj.htm
http://www.keil.com/support/docs/3079.htm
http://www.keil.com/support/docs/1279.htm
http://www.keil.com/support/docs/1948.htm

von R. W. (quakeman)


Lesenswert?

Also in deinem geposteten Werten sind einige Ungereimtheiten drin.

Erstens besteht das CID aus 128 Bit im SPI und 136 Bit im SD Modus, was 
genau 16/17 Bytes entsprechen. Du hast aber eine 18 Byte lange Folge für 
deine CID Daten gepostet und auch bisher nicht erwähnt, in welchem Modus 
du die Karte ansprichst.
Dann ist in deinem Struct am Ende ein crc16 Feld, welches es im CID 
Register nicht gibt. Danach folgt noch ein leeres Feld der Länge 0 Bits, 
was keinen Sinn hat.
In deinem Struct für das CSD Register hast du am Ende ebenfalls eine 
CRC16 und ein 0 Bit Platzhalter, die es nicht gibt.
Lies dir mal die offizielle "Physical Layer Simplified Specification 
Version 2.00" von sdcard.org [1] durch bezüglich der CID und CSD 
Register.

Du solltest deine Structs erst mal korrigieren und schauen, dass du auch 
nur 128 Bits für das CID und CSD Register ausliest und nicht mehr.

Ich habe bisher immer ohne struct die Daten aus den 128Bit rausgezogen 
gehabt. Aber wenn ich mir so deine Structs anschaue sieht es doch 
deutlich übersichtlicher aus damit zu arbeiten als dauernd mit den Bits 
herumzuschubsen. :)

Ciao,
     Rainer

[1] http://www.sdcard.org/developers/tech/sdcard/pls/

von Neb N. (bluemorph)


Lesenswert?

Hallo Leute,

@AkkiSan, vielen Dank für deine Links. Nach einigem Suchen bin ich auch 
selbst auf das Dokument http://www.keil.com/support/docs/1948.htm 
gestoßen. Da steht eindeutig drin, warum mein Vorhaben nicht einfach so 
umzusetzen ist. Der Keilcompiler speichert eine Bitleisten Structur 
einfach ziemlich doof ab (zumindest für meinen Fall).

@Fox Mulder, danke auch dir für deinen Beitrag. Zum CID / CSD Register 
gehört natürlich nicht die 16 Bit CRC Checksumme, da hast du recht. 
Allerdings bleibt es ja mir überlassen, was ich in meiner Structur mit 
einfließen lasse. Da ja nun mal nach dem CID / CSD Register eine CRC16 
Checksumme folgt habe ich diese einfach mit in die Struktur aufgenommen.
1
 ...
2
unsigned : 0;
3
}

Das bedeutet, dass ein memoryalignment gemacht wird. Sprich, das nächste 
Bit, die nächste Variable, die nächste Structur (was auch immer) beginnt 
nun wieder an einer neuen Wortgrenze.

Um nun gewisse Informationen aus den Registern auszulesen habe ich mir 
nun eine Funktion geschrieben, der man das Quell Datenfeld, ein 
Startbit, eine Länge und ein Zielpointer übergeben kann. Dann wird die 
Information aus dem Quellfeld an die Zieladresse kopiert.

MfG und vielen Dank für eure Mithilfe!

BlueMorph

von ??? (Gast)


Lesenswert?

Benny Nestler schrieb:
> Da steht eindeutig drin, warum mein Vorhaben nicht einfach so
> umzusetzen ist. Der Keilcompiler speichert eine Bitleisten Structur
> einfach ziemlich doof ab (zumindest für meinen Fall).

Hallo Benny,

bitte erkläre mal genau, warum es bei dir nicht geht. Ich lege auch 
structs aus Bitmasken über Datenfelder. Dazu nutze ich unions, das spart 
den cast, und deklariere die Bit Strukturen mit pragma pack.

???

von Neb N. (bluemorph)


Lesenswert?

Okay, also laut dem Dokument http://www.keil.com/support/docs/1948.htm 
speichert der Keil Compiler Bitfelder in folgender Form ab:
1
   1          typedef struct
2
   2          {
3
   3            unsigned int bit_0 : 1;        // Byte 1, Bit 0
4
   4            unsigned int bit_1 : 1;        // Byte 1, Bit 1
5
   5            unsigned int bit_2 : 1;        // Byte 1, Bit 2
6
   6            unsigned int bit_3 : 1;        // Byte 1, Bit 3
7
   7            unsigned int bit_4 : 1;        // Byte 1, Bit 4
8
   8            unsigned int bit_5 : 1;        // Byte 1, Bit 5
9
   9            unsigned int bit_6 : 1;        // Byte 1, Bit 6
10
  10            unsigned int bit_7 : 1;        // Byte 1, Bit 7
11
  11            unsigned int bit_8 : 1;        // Byte 0, Bit 0
12
  12            unsigned int bit_9 : 1;        // Byte 0, Bit 1
13
  13            unsigned int bit_10: 1;        // Byte 0, Bit 2
14
  14            unsigned int bit_11: 1;        // Byte 0, Bit 3
15
  15            unsigned int bit_12: 1;        // Byte 0, Bit 4
16
  16            unsigned int bit_13: 1;        // Byte 0, Bit 5
17
  17            unsigned int bit_14: 1;        // Byte 0, Bit 6
18
  18            unsigned int bit_15: 1;        // Byte 0, Bit 7
19
  19          }bit_st;

Als Beispiel nehme ich nun mal das Byte 14 und 15 des CID Registers 
einer SD Karte. Diese beiden Bytes sind folgendermaßen eingeteilt (in 
Bits):

Byte 14:  0000  yyyy
Byte 15:  YYYY  mmmm

y/Y - Bits enthalten "Year"  - Informationen
m - Bits enthalten "month" - Informationen
die vier Nullen im Byte 14 sind reserved Bits und sind nicht mit zu 
beachten.

Würde ich nun die folgende Struktur über Byte 14 und 15 legen, würde ich 
diese also so deklarieren:

typedef struct {
                unsigned int dontneed  : 4;
                unsigned int year      : 8;
                unsigned int month     : 4;
               } T_date;

Der Compiler würde nun folgendes ablegen:
- in dontneed : yyyy
- in year     : 0000 mmmm
- in month    : YYYY

Wie du siehst sind also über eine Union / structur die Daten nicht 
wirklich zu erreichen! Zumindest ist es bei mir so. Ich betreibe die SD 
Karte im SPI Mode (mit Software SPI, da der benutzte uProzessor kein SPI 
hat).

Ist das okay so?

MfG BlueMorph

von ??? (Gast)


Lesenswert?

Warum deklarierst du die Bit Strukturen mit "int", das geht doch mit 
"char" auch. Dann erübrigt sich das Problem mit der Byte Stellung.

Ich werde es die Tage mal ausprobieren.

???

von Neb N. (bluemorph)


Lesenswert?

Ich nehme an, dass auch wenn ich statt unsigned int, unsigned char nehme 
dasselbe Problem haben werde, da der Compiler trotzdem immer mit dem LSB 
eines jeden Bytes anfangen wird.

Demnach würde es den selben Effekt geben, egal, ob ich nun ein unsigned 
int oder ein unsigned char nehme. Denke ich zumindest.

Mir ist gerade auch aufgefallen, dass ich im vorigen Beitrag was falsch 
geschrieben habe. Ich denke der Inhalt der Strukturwerte müsste 
eigentlich so aussehen:

- in dontneed : mmmm
- in year     : YYYY 0000
- in month    : yyyy

Wähle ich stattdessen unsigned chars, müsste die Struktur wie im vorigen 
Beitrag geschrieben abgelegt werden. Also nochmal so:

- in dontneed : yyyy
- in year     : 0000 mmmm
- in month    : YYYY

von Neb N. (bluemorph)


Lesenswert?

Mit pragma pack habe ich es allerdings nocht nicht probiert! Im 
Handbuch was ich über den Keil Compiler hatte, stand nichts von einem 
pack! Was genau mach das #pragma pack?

MfG BlueMorph

von Neb N. (bluemorph)


Lesenswert?

Okay, ich bin nicht fündig geworden, ob der C51 Keil Compiler die 
#pragma pack Direktive unterstützt. Laut den Erläuterungen des Keil 
Forums (http://www.keil.com/support/docs/1689.htm) würde ich denken, 
dass die Füll - Bytes ausgelassen werden. Da allerdings der 8051 ein 8 
bit Prozessor ist, hätte ich gedacht, dass der Compiler sowieso 
byteorientiert und nicht wortorientiert (2 - Byte, auf dem 8051) 
arbeitet.

MfG BlueMorph

von ??? (Gast)


Lesenswert?

Du hast recht, ist ja ein 8Bitter, also kein pragma pack.

Habe dein Bsp. mal umgebaut
1
#pragma CODE
2
3
#include <REG52.H>
4
#include <string.h>
5
6
typedef unsigned char t_uchar;
7
typedef unsigned int t_uint;
8
9
const t_uchar ser[] = {
10
  0x03, 0x53, 0x44, 0x53,
11
  0x44, 0x30, 0x32, 0x47,
12
  0x80, 0x70, 0xFA, 0x11,
13
  0xED, 0x00, 0x99, 0x4F,
14
  0xEE, 0xE1};
15
16
17
typedef struct {
18
                 t_uchar  m_id;     // 0x03
19
                 t_uchar  oem[2];  // 0x53, 0x44
20
                 t_uchar  pnm[5];  // 0x53, 0x44, 0x30, 0x32, 0x47
21
                 t_uchar  prv;     // 0x80
22
                 long     psn;     // 0x70, 0xFA, 0x11, 0xED
23
                 t_uchar s1    : 4;  // 0X?0
24
                 t_uchar year  : 8;  // 0x90
25
                 t_uchar month : 4;  // 0x?9
26
                 t_uchar crc7  : 7;  // 0x47
27
                 t_uchar s2    : 1;  // 0x01
28
                 t_uint  crc16 : 16;  // 0xEEE1
29
                 t_uchar s3    : 8;
30
               } T_cid;
31
32
33
union {
34
       T_cid     cid;
35
       t_uchar   str[sizeof (ser)+1];
36
      } bicid;
37
38
t_uint   y, m, c;
39
40
void main (void) {
41
42
  do {
43
    memcpy (bicid.str, ser, sizeof (ser));
44
45
    y = bicid.cid.year;
46
    m = bicid.cid.month;
47
    c = bicid.cid.crc7;
48
49
  } while (1);
50
}

und das Ergebnis
1
            ; y = ...
2
0013 AF00        R     MOV     R7,bicid+0EH
3
0015 EF                MOV     A,R7
4
0016 750000      R     MOV     y,#00H
5
0019 F500        R     MOV     y+01H,A
6
7
            ; m = ...
8
001B AF00        R     MOV     R7,bicid+0FH
9
001D EF                MOV     A,R7
10
001E 540F              ANL     A,#0FH
11
0020 750000      R     MOV     m,#00H
12
0023 F500        R     MOV     m+01H,A

Sollte so gehen, oder?

???

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.