前置き
組込みマイコンでの話になります。 当方STM32F4(1ワードが32bit)を使った環境です。
注意事項
マイコン(コンパイラ)の種類に応じて、バイトオーダーでのエンディアンの違いとかあるので、ここで紹介しているコード参考にしたけど、結果が一致しないとかするとかは注意してください。
union(共用体)使わない場合
「32bitのデータをSPI通信で転送するときに16bitを2回に分けて送信する」なんてことは組込み業界ではよくある話ですが、チャットGPTでも初手で出してくるコードはこんな感じでしょう。
void SetSpiData32(uint32_t val) {
FPGA_SPI_Write(BaseAddress + 0x00, (uint16_t)(val & 0xFFFF)); // Set lower 16 bits
FPGA_SPI_Write(BaseAddress + 0x01, (uint16_t)((val >> 16) & 0xFFFF)); // Set upper 16 bits
}
ビットシフトとマスク処理が入っていますので、処理サイクル数はかさんでしまいます。
最近のコンパイラは賢いので最適化してビットシフトしないようなコードに変換しているかもしれませんが。。。
union(共用体)使うといいぞ
#ifndef _TYPEDEF_H_
#define _TYPEDEF_H_
#include <stdint.h>
// 32bitを16bitと8bitのでアクセスするための共用体
union uint32_u {
uint32_t UI32;
struct {
uint16_t LO;
uint16_t HI;
} UI16;
struct {
uint8_t LL;
uint8_t LH;
uint8_t HL;
uint8_t HH;
} UI8;
};
// 16bitを8bitのでアクセスするための共用体
union uint16_u {
uint16_t UI16;
struct {
uint8_t LO;
uint8_t HI;
} UI8;
};
#endif /* _TYPEDEF_H_ */
こんな感じの共用体を定義しますと、32bitの領域を16bitの上位下位、8bitで4分割してアクセスできるようになります。
確認方法
注意事項に書きましたとおり、環境依存がありますので真偽を確認します。
コードはこのような感じで、32bitで入力して、16bit,8bitで読み出します。
printf("--------------------------------\r\n");
printf("Progrum Start\r\n");
printf("--------------------------------\r\n");
uint32_u val;
val.UI32 = 0x12345678;
printf("UI8.HH : 0x%02X\r\n", val.UI8.HH);
printf("UI8.HL : 0x%02X\r\n", val.UI8.HL);
printf("UI8.LH : 0x%02X\r\n", val.UI8.LH);
printf("UI8.LL : 0x%02X\r\n", val.UI8.LL);
printf("UI16.HI: 0x%04X\r\n", val.UI16.HI);
printf("UI16.LO: 0x%04X\r\n", val.UI16.LO);
printf("UI32 : 0x%08X\r\n", val.UI32);
さよならビットシフト
ということで共用体を利用した場合、このようなコードで書けますのでビットシフトも無くなり、上位下位どっちにアクセスしているのか見やすくなりますので、「私のことは嫌いになっても共用体のことは嫌いにならないでください」✋
void SetSpiData32(uint32_u val) {
FPGA_SPI_Write(BaseAddress + 0x00, val.UI16.LO); // Set lower 16 bits
FPGA_SPI_Write(BaseAddress + 0x01, val.UI16.HI); // Set upper 16 bits
}