web-dev-qa-db-ja.com

struct c ++ / arduinoにビットをパックします

私は構造体を持っています:

typedef struct {
  uint8_t month;  // 1..12 [4 bits]
  uint8_t date;   // 1..31 [5 bits]
  uint8_t hour;   // 00..23 [5 bits]
  uint8_t minute; // 00..59 [6 bits]
  uint8_t second; // 00..59 [6 bits]
} TimeStamp;

しかし、5ではなく4バイトしか消費しないようにパックしたいと思います。

ビットをシフトしてより厳密な構造を作成する方法はありますか?

大したことではないように思えるかもしれませんが、EEPROMに保存されるため、1 KBを節約すると4 KBページで512バイトが余分に節約されます(残りの6ビットは他の何かに使用できます)。

23
Kent

探しているのはビットフィールドです。

次のようになります。

typedef struct {
  uint32_t month  : 4;   // 1..12 [4 bits]
  uint32_t date   : 5;   // 1..31 [5 bits]
  uint32_t hour   : 5;   // 00..23 [5 bits]
  uint32_t minute : 6;   // 00..59 [6 bits]
  uint32_t second : 6;   // 00..59 [6 bits]
} TimeStamp;

コンパイラーによっては、パディングなしで4バイトに収まるために、この場合、メンバーのサイズは4バイト(つまり、uint32_t)でなければなりません。そうしないと、uint8_tを使用している場合、構造体のメンバーは各バイト境界でオーバーフローしないようにパディングされ、5バイトの構造体になります。これを一般的なルールとして使用すると、コンパイラの矛盾を防ぐことができます。

ビットフィールドについて少し詳しく説明したMSDNリンクを次に示します。

C++ビットフィールド

29
Ian A.B.K.

ビットフィールドは、一般的にこれを行うための1つの「正しい」方法ですが、年の初めから数秒だけを保存するのはなぜですか?これらを快適に保存するには4バイトで十分です。実際、1970年から2038年までの秒を保存するには4バイトで十分です。その年から他の情報を取得するのは、現在の年を知っている限りは簡単な練習です。関心のある時間範囲は70年未満です(さらに、タイムスタンプを68年の範囲にグループ化し、各範囲のオフセットを保存することもできます)。

14
Cubic

別の解決策は、1つの32ビット変数に値を格納し、ビットシフトで個々のアイテムを取得することです。

uint32_t timestamp = xxxx;

uint8_t month = timestamp & 0x0F;
uint8_t date = (timestamp & 0x1F0) >> 4;
uint8_t hour = (timestamp & 0x3E00) >> 9;
uint8_t minute = (timestamp & 0xFC000) >> 14;
uint8_t second = (timestamp & 0x3F00000) >> 20;
12

2秒の精度を処理できる場合、MS-DOSタイムスタンプ形式では16ビットを使用して日付(1980年を7ビット、月を4、日を5)、時間を16ビット(時間を5、分は6、秒は5)。 Arduinoのようなプロセッサでは、16ビット境界を越えて値を分割するコードを書くことができるかもしれませんが、そのような分割を避けることができればコードはより効率的だと思います(MS-DOSが2秒を受け入れることでしたように)正確さ)。

そうでない場合は、別の回答で述べたように、32ビットの秒数を使用すると、「カレンダー形式」で物事を追跡しようとするよりも、いくつかの基本時間がより効率的であることが多いためです。カレンダー形式の日付から次の日付に進める必要がある場合、それを行うコードは、カレンダーの日付と線形日付を変換するコードよりも簡単かもしれませんが、他の多くのことを行う必要がある場合(偶数日付から前の日付に戻ります)入力または表示されたときに日付を線形形式に変換したり、日付を線形形式に変換したりする必要があります。そうでなければ、単に秒の線形数で動作します。

うるう年の3月1日を基準日として選択すると、線形の秒数での作業がより便利になります。次に、日付が1461を超えている間に、日付からその日付を減算し、年に4を加算します(Arduinoでは16ビットの比較と減算が効率的であり、2040でもループは単一の16x16除算よりも時間がかかりません)。日付が364を超える場合は、365を減算して年を増やし、さらに2回まで試行します[3回目の減算後の日付が365の場合、そのままにします]。

すべてのコーナーケースが正しく機能するように注意する必要がありますが、8ビットまたは16ビットの小さなマイクロでも、変換は驚くほど効率的です。

2
supercat