Forum: Mikrocontroller und Digitale Elektronik Programmierung SPI Kommunikation Befehlsworterstellung


von Anonymous U. (gastt)


Lesenswert?

Ich würde gerne meinen Code zur Diskussion stellen. Es handelt sich um 
die Ansteuerung eines Chips per SPI. Dieser hat ein 24 Bit langen 
Befehl. Die ganzen Bits, die mit const definiert werden, ändern sich 
nicht. Zur besseren Lesbarkeit sind sie aber extra aufgeführt. Dann wird 
das 24 Bit breite Wort in der concat Variable zusammengeführt. Soweit so 
gut, dass sollte der Compiler wegoptimieren können. Allerdings kann ich 
wegen der falschen Anordnung (Endianess) der Bytes in der concat 
variable diese nicht direkt an die SPI Funktion übergeben. Das heißt ich 
muss sie erst umsortieren. Wie macht man das besser?
1
static void Set_Reference_Counter(uint32_t cntr_val_14b){
2
    const uint32_t ctrl_bits = 0b00;
3
    cntr_val_14b &= 0x00003FFF;
4
    const uint32_t anti_backlash_width = 0b00;
5
    const uint32_t test_mode = 0b00;
6
    const uint32_t lock_detect_prec = 0b0;
7
8
    uint32_t concat = (lock_detect_prec<<20) | (test_mode<<18) | (anti_backlash_width<<16) | (cntr_val_14b<<2) | ctrl_bits;
9
    uint8_t data[3];
10
    data[0] = *((uint8_t*) &concat + 2);
11
    data[1] = *((uint8_t*) &concat + 1);
12
    data[2] = *((uint8_t*) &concat + 0);
13
14
    HAL_GPIO_WritePin(SPI3_CS_PLL_CLK_GPIO_Port, SPI3_CS_PLL_CLK_Pin, GPIO_PIN_SET);
15
    HAL_SPI_Transmit(&hspi3, (uint8_t*) &data, 3, 100);
16
    HAL_GPIO_WritePin(SPI3_CS_PLL_CLK_GPIO_Port, SPI3_CS_PLL_CLK_Pin, GPIO_PIN_RESET);
17
}

von Jim M. (turboj)


Lesenswert?

Mach die Bitschieberei für concat gleich als 3 einzelne Bytes.

von Anonymous U. (gastt)


Lesenswert?

Habe ich überlegt. Allerdings kann ich halt bei der 32 Bit Variable den 
Shiftamount direkt aus dem Datenblatt rauslesen. Wenn ich es einzeln 
mach, dann muss ich wieder selber rumrechnen.

von Niklas G. (erlkoenig) Benutzerseite


Angehängte Dateien:

Lesenswert?

Anonymous U. schrieb:
> data[0] = *((uint8_t*) &concat + 2);
>     data[1] = *((uint8_t*) &concat + 1);
>     data[2] = *((uint8_t*) &concat + 0);

Das in einen "char"-Typ umcasten und dereferenzieren ist zwar erlaubt, 
aber plattform-abhängig. Ok, der Code ist eh STM32-spezifisch.


Anonymous U. schrieb:
> HAL_SPI_Transmit(&hspi3, (uint8_t*) &data, 3, 100);

Der Cast ist unnötig, du kannst einfach "data" schreiben.

Zur Frage: Das ist ein Fall für Serialisierung. Mit meiner 
µSer-Bibliothek kann man solche Fälle vereinfachen. Zuerst definiert man 
sich ein struct mit den entsprechenden Einträgen und deren Längen in 
Bits:
1
#include <uSer/uSer.hh>
2
3
struct Command1 {
4
    USER_STRUCT (Command1, uSer::AttrNone)
5
    
6
    USER_MEM(std::uint8_t, ctrl_bits, uSer::Width<2>)
7
    USER_MEM(std::uint16_t, cntr_val_14b, uSer::Width<14>)
8
    USER_MEM(std::uint8_t, anti_backlash_width, uSer::Width<2>)
9
    USER_MEM(std::uint8_t, test_mode, uSer::Width<2>)
10
    USER_MEM(std::uint8_t, lock_detect_prec, uSer::Width<4>)
11
12
    USER_ENUM_MEM (ctrl_bits, cntr_val_14b, anti_backlash_width, test_mode, lock_detect_prec)
13
};

Dann schreibt man die gewünschten Werte direkt hinein:
1
static void Set_Reference_Counter (uint32_t cntr_val_14b){
2
    Command1 cmd;
3
4
    cmd.ctrl_bits = 0b00;
5
    cmd.cntr_val_14b = cntr_val_14b;
6
    cmd.anti_backlash_width = 0b00;
7
    cmd.test_mode = 0b00;
8
    cmd.lock_detect_prec = 0b0;

Dann lässt man sie in einen Integer zusammenpacken. Die Berechnung der 
Shifts geschieht dabei automatisch, sodass man nicht selbst rechnen 
muss:
1
    uint32_t concat [1];
2
    uSer::serialize (concat, cmd);

Zum Schluss wandelt man diesen Integer in ein Byte-Array um und gibt 
dabei die "Big Endian" Reihenfolge an, sodass die Bytes umgekehrt 
abgelegt werden:
1
    uint8_t data [3];
2
    uSer::serialize<uSer::Width<24>, uSer::ByteOrder::BE> (data, concat);
3
}

Das "data" Array kann dann wie zuvor ausgegeben werden. Das ganze ist 
dann 100% portabel und funktioniert auf jeder Prozessor-Architektur.

Die Bibliothek: https://github.com/Erlkoenig90/uSer

von Anonymous U. (gastt)


Lesenswert?

Oh, Danke :-) Sowas ist auf jeden Fall schön beim Programmieren. 
Allerdings wird hier der Compiler nicht viel optimieren können?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Anonymous U. schrieb:
> Allerdings wird hier der Compiler nicht viel optimieren können?

Doch, das wird praktisch völlig wegoptimiert. Da sollte im Endeffekt das 
gleiche rauskommen wie bei dem originalen Code. Das kann man ggf. im 
generierten Assembler-Code überprüfen. Siehe auch:
https://erlkoenig90.github.io/uSer-doc/html/tutorial.html#Minimize

von Anonymous U. (gastt)


Lesenswert?

Haha, des wollt ich hören. Dann muss ich selber nicht im Code 
nachfielseln :-D Ne, Spaß beiseite, vielen Dank des werd ich mal 
ausprobiern.

von Maxim B. (max182)


Lesenswert?

Anonymous U. schrieb:
> Dieser hat ein 24 Bit langen
> Befehl.

Warum benutzt du dann uint32_t? In GCC gibt es auch __uint24

von Niklas Gürtler (Gast)


Lesenswert?

Maxim B. schrieb:
> Warum benutzt du dann uint32_t? In GCC gibt es auch __uint24

Das bringt bei Cortex-M nichts, weil der 32bit-int am Effizientesten 
verarbeiten kann. Da der Wert ohnehin temporär ist, spielt der 
Speicherverbrauch auch keine Rolle.

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.