Почему игнорируется параметр чувствительности (порог)

arduino-mega c++ platformio vscode

Аналоговый потенциометр, подключенный к Arduino. простейшая возможная установка. Цель состоит в том, чтобы отправлять сообщение через Serial всякий раз, когда значение изменяется.

Я не хочу сильно спамить последовательное соединение, поэтому значение должно измениться как минимум на 20 (порог чувствительности) перед отправкой пакета сообщения.

Однако на самом деле пакет сообщения отправляется, даже если значение изменилось только на 1. Почему игнорируется параметр чувствительности?

Обновлено: После отладки я вижу, что он вводит первый оператор IF в read(). Оператор IF оценивается как 0, что очень странно. Он не должен был входить в этот оператор IF!

MagicPot.h:

#define MAGIC_POT_MAX_RAW_VALUE_10B     1023
#define MAGIC_POT_MAX_RAW_VALUE_12B     4095
#define MAX_RAW_VALUE_DEFAULT           (MAGIC_POT_MAX_RAW_VALUE_10B)

class MagicPot
{
    public:
        typedef void (*callback_fn)();
        MagicPot(uint8_t pin, uint16_t minRead = 0, uint16_t maxRead = MAX_RAW_VALUE_DEFAULT, uint16_t maxRawRead = MAX_RAW_VALUE_DEFAULT): pin(pin), minRead(minRead), maxRead(maxRead), maxRawRead(maxRawRead) {};
        void begin();
        bool read(uint8_t sensitivity = 5);
        uint16_t getValue();
        uint16_t getRawValue();
        uint8_t pin;

    private:
        uint16_t minRead;
        uint16_t maxRead;
        uint16_t maxRawRead;
        callback_fn onChangeCallback;
        uint16_t value;
        uint16_t rawValue;
};

MagicPot.cpp:

void MagicPot::begin()
{
    this->onChangeCallback = nullptr;
    this->value = 0;
    this->rawValue = 0;
    pinMode(this->pin, INPUT);
    this->read(0);
}

bool MagicPot::read(uint8_t sensitivity)
{
    this->rawValue = analogRead(this->pin);

    uint16_t value = map(this->rawValue, 0, this->maxRawRead, this->minRead, this->maxRead);

    if (abs(value - this->value) > sensitivity)
    {
        this->value = value;
        return true;
    }
    else if (value < sensitivity && this->value != this->minRead)
    {
        this->value = minRead;
        return true;
    }
    else if (value + sensitivity > maxRead && this->value != this->maxRead)
    {
        this->value = this->maxRead;
        return true;
    }
    return false;
}

Код Arduino:

MagicPot analogPots[] = 
{
    MagicPot(A0),
}

void setup() {
  for (MagicPot& x : analogPots) //arr содержит только 1 элемент
    x.begin();
}
void loop() {
  for (MagicPot& pot : analogPots) //arr содержит только 1 элемент
  {
    if(true == pot.read(20))
      {
        CPPacket* analogPotChangedPacket = new CPPacket(HWEvent::Switch, pot.pin, pot.getValue());
        SendPacket( &myPacketSerial, analogPotChangedPacket);
        delete analogPotChangedPacket;
      }
  }

}

Фактический вывод: (чтение с компьютера(подключение через последовательный порт))

Тип Ошибка Цель Значение
Переключить НЕТ 54 516
Переключить НЕТ 54 515
Переключить НЕТ 54 514
Переключить НЕТ 54 513

Ожидаемый результат:

Тип Ошибка Цель Значение
Переключить НЕТ 54 516
Переключить НЕТ 54 496
Переключить НЕТ 54 456
Переключить НЕТ 54 436

, 👍2

Обсуждение

вы вычитаете два неподписанных типа, поэтому результат также сохраняется во временном беззнаковом типе. попробуйте abs((int16_t) value - this->value), @Juraj

@Juraj: это должно быть либо abs((int16_t) (value - this->value)), либо abs((int16_t) value - (int16_t) this->value). Или, еще лучше, сделать все подписанным. В противном случае, если только один член вычитания знаковый, беззнаковый тип выигрывает в игре «обычные арифметические преобразования»., @Edgar Bonet

@EdgarBonet Спасибо, странное поведение было вызвано uint16. Я протестировал запуск кода (с **uint**) в VisualStudio, и он сработал. Знаете, почему это работало в VS/Win11, но не в Arduino? Отличается ли версия С++? Компилятор? И почему компилятор не предупредил меня? P.s. я совсем новичок в с++, @David


1 ответ


Лучший ответ:

3

Рассмотрите это вычитание:

value - this->value

Как и в любой арифметической операции, операнды подвергаются интегральному преобразованию. продвижение, затем обычные арифметические преобразования. Немного упрощая, «интегральное продвижение» означает, что типы меньше чем int повышается до int, тогда как «обычные арифметические преобразования» означает, что меньший операнд повышается до типа большего. Если оба типа имеют одинаковый размер, выигрывает тип без знака.

На Arduino Mega int составляет 16 бит. Так как операнды вычитание оба uint16_t, типы не изменяются этими неявные преобразования. Вычитание выполняется с помощью uint16_t введите и оберните по модулю 216. Если вы ожидаете −1, вы получаете 65535.

Если вы приведете один из операндов к int16_t, он будет неявно преобразуется обратно в uint16_t с помощью обычных арифметических преобразований и вы получите тот же результат.

На вашем ПК int имеет длину 32 бита, поэтому оба операнда получаются неявно. преобразуется в int путем интегрального продвижения, и вы получаете ожидаемый подписанный результат.

Простое решение вашей проблемы – преобразовать все ваши переменные в int16_t. Я предлагаю вам всегда использовать подписанные типы по умолчанию и возвращать к беззнаковым типам, когда вам нужна циклическая семантика арифметические операции (иногда полезно).

,