Arduino - Enum变量值本身会发生变化

时间:2022-06-19 23:57:04

I created an Arduino mega sketch to control LEDs channel to simulate simple sunset and sunrize. In my class LEDChannel declaration, i have an public enum to represent the state of the LED Channel and i have a private instance (CurrentState) of State enum in the class to keep track of this LEDChannel instance.

我创建了一个Arduino超级草图来控制LED通道,以模拟简单的日落和日晒。在我的类LEDChannel声明中,我有一个公共枚举来表示LED通道的状态,并且我在类中有一个State enum的私有实例(CurrentState)来跟踪这个LEDChannel实例。

Everything compile well and is uploaded to my Arduino Mega. The problem is when i debug my code and check the value of CurrentState variable i get a value outside of enum range. Sometime i get value 7579 (actual value in my last test). Another day i can get a value of 612. The value should only be 0 to 4. I verified my code and any place i change the value of this variable, i use the enum. By example :

一切编译得很好,并上传到我的Arduino Mega。问题是当我调试我的代码并检查CurrentState变量的值时,我得到一个超出枚举范围的值。有时我得到7579的值(我上次测试的实际值)。另一天,我可以获得612的值。该值应该只有0到4.我验证了我的代码和任何地方我改变了这个变量的值,我使用枚举。例如:

CurrentState = ManualSunset;

CurrentState variable receive the value NoAction in the default constructor of LEDChannel Class.

CurrentState变量在LEDChannel类的默认构造函数中接收值NoAction。

The class LEDChannel is part of a bigger project to make an Aquarium controller with other class to represent RTC time module, Bluetooth module, Fan and temperature sensor.

LEDChannel类是一个更大的项目的一部分,用于制作与其他类别的水族馆控制器,以代表RTC时间模块,蓝牙模块,风扇和温度传感器。

I developed my project with Visual Studio Community 2015 with vMicro plugin for Arduino. If you need the complete project, i will find a way to make it available.

我使用Visual Studio Community 2015和Arduino的vMicro插件开发了我的项目。如果您需要完整的项目,我会找到一种方法使其可用。

Here is the LEDChannel Declaration/definition and main Arduino program .ino. This is a light version of my code. I removed all unnecessary code. To reproduce the problem, just comment line 13 in main program nNbLEDChannel = 1;. You should then get value ManualSunrise (3) for variable CurrentState. Take not that nNbLEDChannel is only used in main .ino file.

这是LED通道声明/定义和主要的Arduino程序.ino。这是我的代码的简易版本。我删除了所有不必要的代码要重现该问题,只需在主程序nNbLEDChannel = 1;中注释第13行。然后,您应该为变量CurrentState获取值ManualSunrise(3)。请注意,nNbLEDChannel仅用于主.ino文件。

#pragma once

#if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
#else
    #include "WProgram.h"
#endif

//#include "DataTypes.h"
typedef byte duration;
class AquariumSimulator;

class LEDChannel
{
public:
    typedef enum State
    {
        AutomaticSunset = 0,
        AutomaticSunrise = 1,
        ManualSunset = 2,
        ManualSunrise = 3,
        NoAction = 4
    };

private:
    byte MaxBrightness;
    unsigned long lastTick;
    byte CurrentBrightness;
    LEDChannel::State CurrentState;
    byte DigitalPin;
    duration ManualSunsetDuration;// In secondes
    duration ManualSunriseDuration;// In secondes
    AquariumSimulator* pAquariumSim;

public:
    void StartManualSunrise(duration SecDuration);
    void PerformSunrise();
    LEDChannel();
    LEDChannel(byte DigitalPIN, AquariumSimulator* pSim);
private:
    void PerformManualSunrise();        
    void SetCurrentBrightness(byte value);
    void IncreaseCurrentBrightness(byte value = 1);
};

Here is the LEDChannel.cpp

这是LEDChannel.cpp

#include "LEDChannel.h"
/*
Constuctor
*/
LEDChannel::LEDChannel(byte DigitalPIN, AquariumSimulator* pSim)
{
    LEDChannel();
    // Initialize default values
    pAquariumSim = pSim;
    pinMode(DigitalPIN, OUTPUT);// Configure Digital pin that control LED Channel
}

LEDChannel::LEDChannel() 
{
    CurrentState = NoAction;

}

void LEDChannel::PerformSunrise()
{

    switch (CurrentState)
    {
    case AutomaticSunrise:
        //PerformAutomaticSunrise();
        break;

    case ManualSunrise:
        PerformManualSunrise();
        break;
    }
}

void LEDChannel::PerformManualSunrise()
{

    unsigned long currentTick = millis();
    if (currentTick >= (lastTick + ManualSunriseDuration / MaxBrightness))
    {

        // If current brightness is at max brigthness value, stop sunset
        if (CurrentBrightness == MaxBrightness)
        {
            CurrentState = NoAction;
            lastTick = 0;
        }
        else
        {
            IncreaseCurrentBrightness();
            lastTick = currentTick;
        }
    }
}

void LEDChannel::SetCurrentBrightness(byte value)
{
    if (value > 255)
        CurrentBrightness = 255;
    else
        CurrentBrightness = value;
    analogWrite(DigitalPin, CurrentBrightness);

}

void LEDChannel::IncreaseCurrentBrightness(byte value)
{
    if ((CurrentBrightness + value) <= 255)
        CurrentBrightness += value;
    else
        CurrentBrightness = 255;
    SetCurrentBrightness(CurrentBrightness);
}

// Manual Sunrise for a duration in secondes
void LEDChannel::StartManualSunrise(duration SecDuration)
{

    switch (CurrentState)
    {
    case NoAction:
        CurrentState = ManualSunrise;
        ManualSunriseDuration = SecDuration;
        SetCurrentBrightness(0);
        break;
    }

}

And main program .ino

主程序.ino

#include "LEDChannel.h"

LEDChannel** pLED;
byte nNbLEDChannel;

void setup()
{
    pLED = new LEDChannel*[1];
    pLED[0] = new LEDChannel(44, NULL);

    /*  If i put the next line as comment CurrentState get a valid value. 
    If i assigne a value to nNbLEDChannel I get invalid value for CurrentState*/
    nNbLEDChannel = 1;  

    pLED[0]->StartManualSunrise(10);
}

void loop()
{
    pLED[0]->PerformSunrise();
}

2 个解决方案

#1


0  

I didn't get out my Arduino UNO board, but I am pretty sure your main problem is the fact that you are trying to call the default constructor from within another constructor. Edit: The call to LEDChannel() you did actually constructed a new instance of the class which then never gets used or assigned. The version of the C++ standard being used by default by Arduino doesn't support that feature, see the following answer for more information (It also demonstraits the correct syntax for calling a constructor from another constructor if you are using a version of the standard which supports it). This means that the CurrentState variable is uninitialized and will be some random value as you are observing. Uncommenting out the line:

我没有拿出我的Arduino UNO板,但我很确定你的主要问题是你试图从另一个构造函数中调用默认构造函数。编辑:调用LEDChannel()你实际上构建了一个新的类实例,然后永远不会被使用或分配。 Arduino默认使用的C ++标准版本不支持该功能,请参阅以下答案以获取更多信息(如果您使用的是标准版本,它还演示了从另一个构造函数调用构造函数的正确语法,支持它)。这意味着CurrentState变量未初始化,并且在您观察时将是一些随机值。取消注释:

nNbLEDChannel = 1;

really doesn't have anything to do with the error and just rearranges the memory which apparently happens to put the CurrentState variable to a valid value.

实际上与错误没有任何关系,只是重新排列显然将CurrentState变量置于有效值的内存。

Similar functionality, having common code within a constructor, can be achieved by moving your common initialization code out of the default constructor and into another function like this, which is then called by both constructors (don't forget to initialize all of the member variables of the class in this function to a default value and then do any other "special" initialization after it):

类似的功能,在构造函数中具有公共代码,可以通过将公共初始化代码从默认构造函数移动到另一个这样的函数来实现,然后由两个构造函数调用(不要忘记初始化所有成员变量)将此函数中的类设置为默认值,然后执行任何其他“特殊”初始化):

LEDChannel::LEDChannel(byte DigitalPIN, AquariumSimulator* pSim)
{
    // Initialize default values
    commonInitFunction();

    //Do other "special" initialization
    pAquariumSim = pSim;
    //pinMode(DigitalPIN, OUTPUT);// Configure Digital pin that control LED Channel
}

LEDChannel::LEDChannel()
{
    // Initialize default values
    commonInitFunction();

    //Do other "special" initialization
}

void LEDChannel::commonInitFunction()
{
    CurrentState = NoAction;
    //make sure all other member variables have been initialized to something either in here or
    //in your constructors to keep from having unexpected behavior
}

Although it is a good idea to initialize variables in an initializer list, as stated here, which would then change your constructors to be the following (Note: I just picked some random default values):

虽然初始化初始化列表中的变量是一个好主意,如此处所述,然后将构造函数更改为以下(注意:我只选择了一些随机默认值):

LEDChannel::LEDChannel(byte DigitalPIN, AquariumSimulator* pSim):
  MaxBrightness(255),
  lastTick(0),
  CurrentBrightness(0),
  CurrentState(NoAction),
  DigitalPin(DigitalPIN),
  ManualSunsetDuration(60),
  ManualSunriseDuration(60),
  pAquariumSim(pSim)
{
    //pinMode(DigitalPIN, OUTPUT);// Configure Digital pin that control LED Channel
}

LEDChannel::LEDChannel():
  MaxBrightness(255),
  lastTick(0),
  CurrentBrightness(0),
  CurrentState(NoAction),
  DigitalPin(0),
  ManualSunsetDuration(60),
  ManualSunriseDuration(60),
  pAquariumSim(NULL)
{
    CurrentState = NoAction;
}

This ensures all values are initialized (including in the proper order) for the instantiation of the class.

这可确保为类的实例化初始化所有值(包括以正确的顺序)。

On a side note, I did notice a few other items in the code you provided which might cause you some unexpected behavior.

在旁注中,我确实注意到您提供的代码中的一些其他项目可能会导致您出现意外行为。

Since CurrentBrightness is a byte (i.e. uint8_t), in your void LEDChannel::IncreaseCurrentBrightness(byte value) method, your check to see if the CurrentBrightness + value is <= 255. This will only ever be false if the sum is equal to 255 because of overflow. Basically, if the CurrentBrightness is 250 and I add the value of 10 to it then I will get a value of 4 if I am not mistaken. This is probably not the desired behavior of this check.

由于CurrentBrightness是一个字节(即uint8_t),在你的void LEDChannel :: IncreaseCurrentBrightness(字节值)方法中,检查CurrentBrightness +值是否<= 255.如果总和等于255,这将只是假。因为溢出基本上,如果CurrentBrightness为250并且我将值10添加到它,那么如果我没有弄错,我将获得值4。这可能不是此检查所需的行为。

The second is that the typedef keyword for your enum is not doing anything since you didn't specify a type. In C++, you don't need the typedef for an enumeration or struct if you only want to refer to it by the original name you gave it, like you did here. See some of the answers here, even though none of them were accepted the first few are

第二个是你的枚举的typedef关键字没有做任何事情,因为你没有指定类型。在C ++中,如果你只想用你给它的原始名称来引用枚举或结构,则不需要typedef,就像你在这里所做的那样。看看这里的一些答案,即使前几个都没有被接受

#2


0  

Have you guaranteed that loop() cannot be called before setup() ?

你能保证在setup()之前不能调用loop()吗?

It is suspicious that setting a value immediately after your pointer array is the "source" of the problem. This leads me to suspect that either the pLED value is getting changed or you have some other indexing problem that is causing the memory where nNbLEDChannel resides to be interpreted as a LEDChannel object.

在指针数组是问题的“来源”之后立即设置一个值是可疑的。这导致我怀疑pLED值是否正在改变,或者您有一些其他索引问题导致nNbLEDChannel所在的内存被解释为LEDChannel对象。

#1


0  

I didn't get out my Arduino UNO board, but I am pretty sure your main problem is the fact that you are trying to call the default constructor from within another constructor. Edit: The call to LEDChannel() you did actually constructed a new instance of the class which then never gets used or assigned. The version of the C++ standard being used by default by Arduino doesn't support that feature, see the following answer for more information (It also demonstraits the correct syntax for calling a constructor from another constructor if you are using a version of the standard which supports it). This means that the CurrentState variable is uninitialized and will be some random value as you are observing. Uncommenting out the line:

我没有拿出我的Arduino UNO板,但我很确定你的主要问题是你试图从另一个构造函数中调用默认构造函数。编辑:调用LEDChannel()你实际上构建了一个新的类实例,然后永远不会被使用或分配。 Arduino默认使用的C ++标准版本不支持该功能,请参阅以下答案以获取更多信息(如果您使用的是标准版本,它还演示了从另一个构造函数调用构造函数的正确语法,支持它)。这意味着CurrentState变量未初始化,并且在您观察时将是一些随机值。取消注释:

nNbLEDChannel = 1;

really doesn't have anything to do with the error and just rearranges the memory which apparently happens to put the CurrentState variable to a valid value.

实际上与错误没有任何关系,只是重新排列显然将CurrentState变量置于有效值的内存。

Similar functionality, having common code within a constructor, can be achieved by moving your common initialization code out of the default constructor and into another function like this, which is then called by both constructors (don't forget to initialize all of the member variables of the class in this function to a default value and then do any other "special" initialization after it):

类似的功能,在构造函数中具有公共代码,可以通过将公共初始化代码从默认构造函数移动到另一个这样的函数来实现,然后由两个构造函数调用(不要忘记初始化所有成员变量)将此函数中的类设置为默认值,然后执行任何其他“特殊”初始化):

LEDChannel::LEDChannel(byte DigitalPIN, AquariumSimulator* pSim)
{
    // Initialize default values
    commonInitFunction();

    //Do other "special" initialization
    pAquariumSim = pSim;
    //pinMode(DigitalPIN, OUTPUT);// Configure Digital pin that control LED Channel
}

LEDChannel::LEDChannel()
{
    // Initialize default values
    commonInitFunction();

    //Do other "special" initialization
}

void LEDChannel::commonInitFunction()
{
    CurrentState = NoAction;
    //make sure all other member variables have been initialized to something either in here or
    //in your constructors to keep from having unexpected behavior
}

Although it is a good idea to initialize variables in an initializer list, as stated here, which would then change your constructors to be the following (Note: I just picked some random default values):

虽然初始化初始化列表中的变量是一个好主意,如此处所述,然后将构造函数更改为以下(注意:我只选择了一些随机默认值):

LEDChannel::LEDChannel(byte DigitalPIN, AquariumSimulator* pSim):
  MaxBrightness(255),
  lastTick(0),
  CurrentBrightness(0),
  CurrentState(NoAction),
  DigitalPin(DigitalPIN),
  ManualSunsetDuration(60),
  ManualSunriseDuration(60),
  pAquariumSim(pSim)
{
    //pinMode(DigitalPIN, OUTPUT);// Configure Digital pin that control LED Channel
}

LEDChannel::LEDChannel():
  MaxBrightness(255),
  lastTick(0),
  CurrentBrightness(0),
  CurrentState(NoAction),
  DigitalPin(0),
  ManualSunsetDuration(60),
  ManualSunriseDuration(60),
  pAquariumSim(NULL)
{
    CurrentState = NoAction;
}

This ensures all values are initialized (including in the proper order) for the instantiation of the class.

这可确保为类的实例化初始化所有值(包括以正确的顺序)。

On a side note, I did notice a few other items in the code you provided which might cause you some unexpected behavior.

在旁注中,我确实注意到您提供的代码中的一些其他项目可能会导致您出现意外行为。

Since CurrentBrightness is a byte (i.e. uint8_t), in your void LEDChannel::IncreaseCurrentBrightness(byte value) method, your check to see if the CurrentBrightness + value is <= 255. This will only ever be false if the sum is equal to 255 because of overflow. Basically, if the CurrentBrightness is 250 and I add the value of 10 to it then I will get a value of 4 if I am not mistaken. This is probably not the desired behavior of this check.

由于CurrentBrightness是一个字节(即uint8_t),在你的void LEDChannel :: IncreaseCurrentBrightness(字节值)方法中,检查CurrentBrightness +值是否<= 255.如果总和等于255,这将只是假。因为溢出基本上,如果CurrentBrightness为250并且我将值10添加到它,那么如果我没有弄错,我将获得值4。这可能不是此检查所需的行为。

The second is that the typedef keyword for your enum is not doing anything since you didn't specify a type. In C++, you don't need the typedef for an enumeration or struct if you only want to refer to it by the original name you gave it, like you did here. See some of the answers here, even though none of them were accepted the first few are

第二个是你的枚举的typedef关键字没有做任何事情,因为你没有指定类型。在C ++中,如果你只想用你给它的原始名称来引用枚举或结构,则不需要typedef,就像你在这里所做的那样。看看这里的一些答案,即使前几个都没有被接受

#2


0  

Have you guaranteed that loop() cannot be called before setup() ?

你能保证在setup()之前不能调用loop()吗?

It is suspicious that setting a value immediately after your pointer array is the "source" of the problem. This leads me to suspect that either the pLED value is getting changed or you have some other indexing problem that is causing the memory where nNbLEDChannel resides to be interpreted as a LEDChannel object.

在指针数组是问题的“来源”之后立即设置一个值是可疑的。这导致我怀疑pLED值是否正在改变,或者您有一些其他索引问题导致nNbLEDChannel所在的内存被解释为LEDChannel对象。