1

我很困惑。我有一个相同的程序被上传到两个不同的 Arduino 板。它在 C++ 中。

这是一个更大的程序,但我将其缩减为仅存在问题的部分。基本上,我有一个“主机”Arduino 和一个“漫游者”Arduino 进行无线通信。有多个流动站单元,但问题只发生在其中一个上。漫游车有需要校准的电机,所以我的Motor命名空间中有静态变量来保存这些校准值。为了避免在源代码中更改这些值,每次我想校准它时重新编译和重新上传,我使用无线系统允许主机在运行时向流动站发送校准值。

这就是问题所在:在一个流动站上,如果我调用该ChangeSpeed方法,值不会被更新,但如果我直接修改变量,它们会得到更新。

让我强调一下,它在五分之四的漫游车上运行良好。问题恰好发生在一台漫游车上。上传到每个流动站的代码是相同的。

以下代码导致问题:

电机.h:

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

    void ChangeSpeed(unsigned char, unsigned char);
}

电机.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));
}

主要.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

案例二:

的流动站上,主机调用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++ 对我来说非常陌生。我不知道我是否正在做一些未定义行为的事情。


编辑:如果我将所有内容都放入一个文件中,它可以正常工作。仅当我将其拆分为头文件和 cpp 文件时,它才会失败。

主要.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 个不同文件中,那么您将有该变量的 3 个独立版本,每个.cpp文件一个。

相反,在头文件中将它们声明为

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

  void ChangeSpeed(unsigned char, unsigned char);
}

这告诉编译器某些文件将为这些变量提供定义并使用该公共共享定义。

然后,由于变量需要只定义一次(这称为一个定义规则),您应该将定义添加到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拥有这两个变量的单独副本。

您的案例 1 修改了Motor.cpp这些变量的副本,同时从Main.cpp. 案例 2 仅适用于Main.cpp副本,因此可以按预期工作。如果你把所有东西都放在一个文件中,你只会得到这些变量的一个副本。

您应该:

  1. 在标头中声明变量extern unsigned char left_speed, right_speed;,然后unsigned char left_speed = 0;在其中一个.cpp文件中声明值;
  2. 直接在其中一个文件中声明静态变量.cpp(例如Rotor.cpp)并使用函数来获取它们的值,就像使用一个来设置它们一样。
于 2012-07-15T20:06:58.683 回答