我很困惑。我有一个相同的程序被上传到两个不同的Arduino板。它是用C ++编写的。
这是一个更大的计划,但我只是将其减少到有问题的部分。基本上,我有一个“主机”Arduino和一个“漫游”Arduino无线通信。有多个流动站单元,但问题只发生在其中一个上。流动站有需要校准的电机,因此我的Motor
命名空间中有静态变量来保存这些校准值。为了防止必须在源代码中更改这些值,每次我想要校准它时重新编译和重新加载,我使用无线系统允许主机在运行时将校准值发送到流动站。
问题在于:在一个流动站上,如果我调用ChangeSpeed
方法,则值不会更新,但如果我直接修改变量,它们会更新。
让我强调它可以在五个流动站中的四个中正常工作。问题恰好发生在一个流动站上。上传到每个流动站的代码是相同的。
以下代码导致问题:
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 ++对我来说很陌生。我不知道我是否正在做一些有不确定行为的事情。
编辑:如果我将所有内容都推送到一个文件中,它就可以了。一旦我将其分割成头文件和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
答案 0 :(得分:5)
您不应该将这些变量设为静态。静态全局变量意味着变量是编译单元的本地变量(通常是正在编译的.cpp
文件),因此如果您在头文件中声明了静态变量并将该头文件包含在3个不同的{{ 1}}单独编译的文件然后你将有3个独立版本的变量,每个.cpp
文件一个。
相反,在头文件中将它们声明为
.cpp
这告诉编译器某些文件将为这些变量提供定义并使用该公共共享定义。
然后,由于变量需要恰好定义一次(这称为one definition rule),您应该将定义添加到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
函数的定义所在。
在C ++中,ChangeSpeed
关键字与C#的工作方式大不相同。在类定义中使用时可能有点类似,但这就是相似之处的结束。
答案 1 :(得分:1)
通过声明变量static
,可以将其范围限制为当前代码单位。换句话说,通过在static
中添加.h
个变量,可以使Motor.cpp
和Main.cpp
拥有这两个变量的单独副本。
您的案例1会修改这些变量的Motor.cpp
副本,同时输出Main.cpp
中的变量。案例2仅适用于Main.cpp
个副本,因此它按预期工作。如果你把所有东西都推到一个文件中,你只需要获得这些变量的一个副本。
你应该:
extern unsigned char left_speed, right_speed;
,然后在其中一个unsigned char left_speed = 0;
文件中将值声明为.cpp
; .cpp
文件中声明静态变量(例如Rotor.cpp
)并使用函数获取它们的值,就像使用一个来设置它们一样。