1

私は困惑しています。同じプログラムを2つの異なるArduinoボードにアップロードしています。それはC++です。

これははるかに大きなプログラムですが、問題のある部分だけに切り詰めています。基本的に、私には「ホスト」Arduinoと「ローバー」Arduinoがワイヤレスで通信しています。複数のローバーユニットがありますが、問題はそのうちの1つでのみ発生しています。ローバーにはキャリブレーションが必要なモーターがあるため、Motor名前空間にこれらのキャリブレーション値を保持する静的変数があります。ソースコードでこれらの値を変更する必要がないように、キャリブレーションを行うたびに再コンパイルして再アップロードするために、ワイヤレスシステムを使用して、ホストが実行時にキャリブレーション値をローバーに送信できるようにしています。

問題は次のとおりです。あるローバーでは、ChangeSpeedメソッドを呼び出しても値は更新されませんが、変数を直接変更すると値は更新されます。

5つのローバーのうち4つで正常に機能することを強調しておきます。問題はちょうど1台のローバーで起こっています。各ローバーにアップロードされるコードは同じです。

次のコードが問題を引き起こしています。

Motor.h:

namespace Motor
{
    static unsigned char left_speed = 0;
    static unsigned char right_speed = 0;

    void ChangeSpeed(unsigned char, unsigned char);
}

Motor.cpp:

void Motor::ChangeSpeed(unsigned char l_speed, unsigned char r_speed)
{
    left_speed = l_speed;
    right_speed = r_speed; 

    soft.println("Change speed: " + String(left_speed) + ", " + String(right_speed));
}

Main.cpp:

void UpdateSpeedValuesBad(unsigned char l_speed, unsigned char r_speed)
{
    Motor::ChangeSpeed(l_speed, r_speed);
    soft.println("Motor write: " + String(l_speed) + ", " + String(r_speed));
}

void UpdateSpeedValuesGood(unsigned char l_speed, unsigned char r_speed)
{
    Motor::left_speed = l_speed;
    Motor::right_speed = r_speed;
    soft.println("Motor write: " + String(l_speed) + ", " + String(r_speed));
}

void ReturnSpeedValues()
{
    soft.println("Motor read: " + String(Motor::left_speed) + ", " + String(Motor::right_speed));
}

ケース1:

不良ローバーでは、ホストがを呼び出してUpdateSpeedValuesBad(5, 5)から、を呼び出しますReturnSpeedValues。出力は次のとおりです。

Change speed: 5, 5
Motor write: 5, 5
Motor read: 0, 0

ケース2:

不良ローバーでは、ホストがを呼び出してUpdateSpeedValuesGood(5, 5)から、を呼び出しますReturnSpeedValues。出力は次のとおりです。

Motor write: 5, 5
Motor read: 5, 5

ケース3:

優れたローバーでは、ホストはを呼び出しUpdateSpeedValuesBad(5, 5)てから、を呼び出しますReturnSpeedValues。出力は次のとおりです。

Change speed: 5, 5
Motor write: 5, 5
Motor read: 5, 5

私は根本的に間違ったことをしていますか?私はC#のバックグラウンドを持っているので、C++は私にはかなり異質です。未定義の振る舞いをしているのかどうかわかりません。


編集:すべてを1つのファイルにまとめると、正常に機能します。ヘッダーファイルとcppファイルに分割すると失敗します。

Main.cpp:

#include <SoftwareSerial.h>

SoftwareSerial soft(9, 10);

namespace Motor
{
  static int left_speed = 0;

  void ChangeSpeed(unsigned char);
}

void Motor::ChangeSpeed(unsigned char l_speed)
{
  left_speed = l_speed;
  soft.println("Change speed: " + String(left_speed));
}

void setup()
{
  soft.begin(9600);

  soft.println("Before: " + String(Motor::left_speed));

  Motor::ChangeSpeed(5);
  soft.println("Bad attempt: " + String(Motor::left_speed));

  Motor::left_speed = 5;
  soft.println("Good attempt: " + String(Motor::left_speed));
}

void loop()
{
}

出力:

Before: 0
Change speed: 5
Bad attempt: 5
Good attempt: 5

編集2:私はアセンブリに飛び込み、悪いケースのためにこれを見つけました。ChangeSpeed値を直接呼び出すか更新するかに基づいて、異なるメモリアドレスを使用しています。なぜそうなるのか誰もが知っていますか?それはコンパイラのバグですか、それともアドレスが同じであることが保証されていませんか?

000000a8 <setup>:
{ 
    Motor::ChangeSpeed(5, 6);
  a8:   85 e0           ldi r24, 0x05   ; 5
  aa:   66 e0           ldi r22, 0x06   ; 6
  ac:   0e 94 5f 00     call    0xbe    ; 0xbe <_ZN5Motor11ChangeSpeedEhh>

    Motor::left_speed = 5;
  b0:   85 e0           ldi r24, 0x05   ; 5
  b2:   80 93 00 01     sts 0x0100, r24

    Motor::right_speed = 6;
  b6:   86 e0           ldi r24, 0x06   ; 6
  b8:   80 93 01 01     sts 0x0101, r24
}
  bc:   08 95           ret

000000be <_ZN5Motor11ChangeSpeedEhh>:

void Motor::ChangeSpeed( unsigned char l_speed, unsigned char r_speed )
{
    left_speed = l_speed;
  be:   80 93 02 01     sts 0x0102, r24
    right_speed = r_speed; 
  c2:   60 93 03 01     sts 0x0103, r22
  c6:   08 95           ret
4

2 に答える 2

5

これらの変数を静的にしないでください。静的グローバル変数は、変数がコンパイルユニット(通常、.cppコンパイルされるファイル)に対してローカルであることを意味します。したがって、静的変数をヘッダーファイルで宣言し、そのヘッダーファイルを.cpp別々にコンパイルされる3つの異なるファイルにインクルードする場合は、各ファイルに1つずつ、その変数の3つの独立したバージョンがあり.cppます。

代わりに、ヘッダーファイルでそれらを次のように宣言します

namespace Motor {
  extern unsigned char left_speed;
  extern unsigned char right_speed;

  void ChangeSpeed(unsigned char, unsigned char);
}

これは、いくつかのファイルがこれらの変数の定義を提供し、その共通の共有定義を使用することをコンパイラーに通知します。

次に、変数を1回だけ定義する必要があるため(これは単一定義規則と呼ばれます)、次の項目に定義を追加する必要がありますMotor.cpp

unsigned char Motor::left_speed = 0;
unsigned char Motor::right_speed = 0;

関数Motor.cppの定義があるので、定義を保持することにしました。ChangeSpeed

C ++では、staticキーワードの動作はC#とは大きく異なります。クラス定義内で使用すると多少似ているかもしれませんが、ここで類似点が終わります。

于 2012-07-15T20:12:07.947 に答える
1

変数を宣言することにより、変数staticの範囲を現在のコード単位に制限します。言い換えると、にstatic変数を含める.hことMotor.cppMain.cpp、これら2つの変数の別々のコピーを作成します。

ケース1は、Motor.cppからの変数を出力しながら、これらの変数のコピーを変更しMain.cppます。ケース2はMain.cppコピーでのみ機能するため、期待どおりに機能します。そして、すべてを1つのファイルにまとめると、それらの変数のコピーが1つだけ取得されます。

次のいずれかを行う必要があります。

  1. extern unsigned char left_speed, right_speed;ヘッダーのように変数を宣言してから、ファイルunsigned char left_speed = 0;の1つで値を宣言します。.cpp
  2. ファイルの1つで静的変数を.cpp直接宣言し(たとえばRotor.cpp)、関数を使用して、値を設定するのと同じように値を取得します。
于 2012-07-15T20:06:58.683 に答える