« Arduino IDEでRaspberry Pi Pico:BSch3V部品ライブラリ | トップページ | Arduino IDEでRaspberry Pi Pico:シリアル受信バッファを増やす »

2022年4月19日 (火)

Arduino IDEでRaspberry Pi Pico:millisをwordで処理した時の異常

2022年4月14日:Arduino IDEでRaspberry Pi Pico:32bitマイコンがバグを生む
この検証用スケッチを書いてみました。

/*****  millisを使った時間待ち  *****/
// uint32_tではなくwordで
word ms_0;
word ms_1;
void delay2(word dly)
{
ms_0 = (word)millis(); // 開始値
while(1){
digitalWrite(2, HIGH); // テスト用パルス出力
ms_1 = (word)millis(); // 現在値
if((ms_1 - ms_0) >= dly) break; // ★1 時間経過
// if((word)(ms_1 - ms_0) >= dly) break; // ★2
digitalWrite(2, LOW);
}
}

/***** SETUP *****/
void setup() {
Serial.begin(9600); // TX
pinMode(2, OUTPUT); // PD2
pinMode(3, OUTPUT); // PD3
pinMode(LED_BUILTIN, OUTPUT); // LED
}

/***** LOOP *****/
void loop() {
byte f_xLED = 0; // LED点滅用
word cnt = 0; // loopカウンタ
char tx_bff[64]; // 送信文字列
while(1){
f_xLED ^= 1; // LED トグル
digitalWrite(LED_BUILTIN, f_xLED);
delay2(1000); // 1000ms wait
sprintf(tx_bff, "#%4d %5u %5u",
cnt, ms_1, ms_0);
Serial.println(tx_bff); // 改行
cnt++;
if(cnt > 9999) cnt = 0;
}
}

★1 と ★2の違いが重要。
★2は引き算結果を(word)でキャストしています。

Arduino UNOで動いていた<★1>の処理、
これをRaspberry Pi Picoに持ってくると、
  cnt ms_1 ms_0
 # 61 62002 61002
 # 62 63002 62002
 # 63 64002 63002
 # 64 65002 64002 ←ここまで動いて停止

数値の比較ができず(32bitのマイナス値になる)、
時間待ち関数から抜け出せません。

これを<★2>のようにすると、Pi PicoでもArduino UNO
でもオーバーフロー発生部分を無事に通過できます。
こんな具合。
  cnt ms_1 ms_0
 # 63 64002 63002
 # 64 65002 64002 ←ここで止まっていたのが、
 # 65   466 65002  (65536+466=66002) 通過
 # 66  1466  466
 # 67  2466 1466
 # 68  3466 2466

符号なし16bit値同士の減算、オーバーフローが起こっても
16bitならうまく「差」が算出されます。
「466 - 65002」は16進だと「01D2 - FDEA」。
「01D2 - FDEA = FFFF・03E8」となり、不要な上位の
「FFFF」は捨てられて下位の16bit値、「03E8」が出てきます。
16進の03E8は10進で1000。
ちゃんと1000ms待ちが動きます。

それが32bitマイコンだと「暗黙の型変換と符号拡張」 が悪さをして、
「01D2 - FDEA = FFFF・03E8 = -64536」とマイナスの値になって
しまいます。
  ※符号なし16bit = word = uint16_t で比較されるつもりが
   符号付の long = int32_t (32bitマイコンでは int だ) で
   比較が行われるのです。
結果、1000msの経過がチェックできません。
今回の例では、減算値ms_0は65002と固定されています。
ms_1が0~65535の間を変化しても、1000を越える値は出てきません
いつまでたってもこのdelayルーチンから抜け出せないのです。

このスカタンの場合、65秒ほど経てば「あれ? なんかおかしい
と気が付くわけですが、もっと複雑な処理で、異常の出現まで
時間がかかるような場合や、条件が重なった時だけ発生する
ものだと、むちゃ怖いバグになってしまいます。

8bitマイコンで正しく動いていたルーチンを32bitマイコンに
持ってくると、転けてしまって動かない。
よく考えておかないと、こんなことが現実に起こります。
「暗黙の型変換と符号拡張」 、昔のルーチンを借用する時は
どうぞ気をつけてください。

|

« Arduino IDEでRaspberry Pi Pico:BSch3V部品ライブラリ | トップページ | Arduino IDEでRaspberry Pi Pico:シリアル受信バッファを増やす »

失敗」カテゴリの記事

Arduino」カテゴリの記事

重箱の隅」カテゴリの記事

ラズパイ・ピコ」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« Arduino IDEでRaspberry Pi Pico:BSch3V部品ライブラリ | トップページ | Arduino IDEでRaspberry Pi Pico:シリアル受信バッファを増やす »