unsigned long の解説と実装リファレンス

記事内には広告が含まれています。

unsigned long の解説と実装リファレンス

【概要】

unsigned long は、Arduinoおよび標準的なC/C++プログラミングにおいて「符号なしの長整数型」として定義されるデータ型です。特定の「関数」ではなく、数値を格納するためのメモリ領域を確保する基本データ型ですが、Arduino環境では特に millis() や micros() といった時間計測関数と密接に関連し、ノンブロッキングな処理やタイムアウト管理において中心的な役割を果たします。

【仕様】

  • データ型: 符号なし長整数型 (Unsigned Long)
  • メモリサイズ: 32ビット(4バイト)※Arduino Uno (AVR) および ESP32, STM32 等の主要なプラットフォームで共通。
  • 表現範囲: 0 から 4,294,967,295 まで。
  • 初期状態: 未初期化の場合、メモリ上の不定な値(ゴミデータ)が入る可能性があるため、明示的な初期化(例: unsigned long timer = 0;)が強く推奨される。

【技術的特徴】

1. 他のデータ型との比較

| 型 | サイズ | 符号 | 範囲 | 主な用途 |
| int | 16ビット (AVR) | あり | -32,768 ~ 32,767 | 小規模なカウンタ |
| unsigned int | 16ビット (AVR) | なし | 0 ~ 65,535 | 状態フラグ、ビットマスク |
| long | 32ビット | あり | -2,147,483,648 ~ 2,147,483,647 | 符号あり大きな数値 |
| unsigned long | 32ビット | なし | 0 ~ 4,294,967,295 | 時間計測、大きなカウンタ |

2. ハードウェアレベルの挙動

  • 演算性能: 32ビットCPU(ESP32等)では単一レジスタで処理されるが、8ビットCPU(AVR)では複数のレジスタを跨いで加算・比較が行われるため、演算のオーバーヘッドに注意が必要である。
  • オーバーフロー特性: 最大値に達した次の加算で、値は 0 にリセットされる(Modulo $2^{32}$ 動作)。これは計算式において、適切な差分計算を用いることで正常に動作する性質を利用している。

【注意点】

  • オーバーフローの考慮: millis() 関数を使用する場合、約70分でオーバーフローが発生する。しかし、単純な比較 current >= last ではなく、差分を取る計算 (current - last >= INTERVAL) を用いることで、オーバーフローの影響を受けずに正しく動作する。
  • 型変換の罠: int 型の変数に millis() の値を代入すると、約32秒で数値が飽和し、正しく計測できなくなる。時間計測には必ず unsigned long を使用すること。
  • 非アトミック性: 8ビットマイコン(AVR)において、unsigned long の読み書きは複数命令に分割されるため、割り込み処理中に値が更新されると、一部のバイトだけが更新された不整合な値を取得する可能性がある。クリティカルな箇所では割り込み禁止、またはアトミックな操作を考慮する必要がある。

【サンプルコード】

以下のコードは、millis() 関数と unsigned long を使用して、delay() を使用せずに正確な周期でLEDを点滅させる「ノンブロッキング」な実装例である。

const int LED_PIN = 13;
const unsigned long INTERVAL = 1000; // 1000ms = 1秒
unsigned long previousMillis = 0; 
bool ledState = false;

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis();

  // 差分を取ることでオーバーフローの影響を回避
  if (currentMillis - previousMillis >= INTERVAL) {
    previousMillis = currentMillis;
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  }
}

【活用例】

  1. タイムアウト処理: 通信プロトコル(I2C/UART)のレスポンス待ち時間の監視。
  2. 周期的なセンサーサンプリング: delay() を使用せずに、特定の周期(例:200msごと)でセンサー値を読み取るタスクの管理。
  3. パルス幅計測: micros() と組み合わせて、外部入力のパルス幅を正確に計測する。
  4. 状態遷移マシン(FSM): システムの各状態(State)における滞在時間を管理するタイマーとしての利用。

コメント

タイトルとURLをコピーしました