Programmierung SPI Kommunikation Befehlsworterstellung

OP #6321615
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
}
Persönliche Seite #6321684
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
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren