Изменение состояния светодиода с помощью кнопки приводит к нестабильному результату

Я пытаюсь запустить этот код:

const int buttonPin = 7;     
const int ledPin13 =  13;      

int buttonState = 0;         
int lastButtonState = buttonState;
bool flag = true;

void setup() {
  // инициализируем вывод светодиода как выход:
  pinMode(ledPin13, OUTPUT);
  // инициализируем вывод кнопки как вход:
  pinMode(buttonPin, INPUT);
}

void loop() {

  buttonState = digitalRead(buttonPin);


  if (buttonState = HIGH && lastButtonState != buttonState) {
    flag = !flag;
    if (flag){
      digitalWrite(ledPin13, LOW);
    } else {
      digitalWrite(ledPin13, HIGH);
    }
  }
}

в этой модели SimulIDE:

<circuit reactStep="50" animate="0" type="simulide_0.1" noLinStep="10" noLinAcc="5" speed="1000000">

Node-16: 
<item labelrot="0" y="-204" valLabRot="0" valLabelx="0" valLabely="0" hflip="1" vflip="1" x="-220" labelx="-16" labely="-24" Show_id="false" objectName="Node-16" itemtype="Node" id="Node-16" rotation="0"/>

Arduino Uno-4: 
<item hflip="1" Show_id="true" valLabely="0" itemtype="Arduino" vflip="1" labelx="0" objectName="Arduino Uno-4" labely="-20" y="-244" Ser_Port="false" labelrot="0" rotation="0" Program="../../Google Drive/Active projects/SRL/Students/Damian2019/Simulation/20190409/noDelay_20190409/noDelay_20190409.ino.standard.hex" Ser_Monitor="false" id="Arduino Uno-4" valLabelx="0" Mhz="16" valLabRot="0" x="-148"/>

Resistor-3: 
<item Unit=" O" hflip="1" Show_id="false" valLabely="6" itemtype="Resistor" Show_res="true" vflip="1" labelx="-12" objectName="Resistor-3" labely="-24" y="-164" labelrot="0" rotation="-90" Resistance="100" id="Resistor-3" valLabelx="-16" valLabRot="0" x="-220"/>

Push-2: 
<item labelrot="0" y="-308" valLabRot="0" valLabelx="0" valLabely="0" hflip="1" vflip="1" x="-100" labelx="-16" labely="-24" Show_id="false" objectName="Push-2" itemtype="Push" id="Push-2" rotation="0"/>

Connector-5: 
<item hflip="1" Show_id="false" valLabely="0" itemtype="Connector" vflip="1" startpinid="Resistor-3-lPin" labelx="-16" objectName="Connector-5" labely="-24" y="-148" endpinid="Arduino Uno-4-GND0" enodeid="Circ_eNode-6" labelrot="0" pointList="-220,-148,-220,-108,-140,-108" rotation="0" id="Connector-5" valLabelx="0" valLabRot="0" x="-220"/>

Connector-11: 
<item hflip="1" Show_id="false" valLabely="0" itemtype="Connector" vflip="1" startpinid="Push-2-rnod" labelx="-16" objectName="Connector-11" labely="-24" y="-308" endpinid="Arduino Uno-4-V5V" enodeid="Circ_eNode-12" labelrot="0" pointList="-84,-308,44,-308,44,-148,4,-148" rotation="0" id="Connector-11" valLabelx="0" valLabRot="0" x="-84"/>

Connector-9: 
<item hflip="1" Show_id="false" valLabely="0" itemtype="Connector" vflip="1" startpinid="Push-2-lnod" labelx="-16" objectName="Connector-9" labely="-24" y="-308" endpinid="Node-16-0" enodeid="enode-15" labelrot="0" pointList="-116,-308,-220,-308,-220,-204" rotation="0" id="Connector-9" valLabelx="0" valLabRot="0" x="-116"/>

Connector-15: 
<item hflip="1" Show_id="false" valLabely="0" itemtype="Connector" vflip="1" startpinid="Arduino Uno-4-PD7" labelx="-16" objectName="Connector-15" labely="-24" y="-172" endpinid="Node-16-1" enodeid="enode-15" labelrot="0" pointList="-140,-172,-172,-172,-172,-204,-220,-204" rotation="0" id="Connector-15" valLabelx="0" valLabRot="0" x="-140"/>

Connector-17: 
<item hflip="1" Show_id="false" valLabely="0" itemtype="Connector" vflip="1" startpinid="Node-16-2" labelx="-16" objectName="Connector-17" labely="-24" y="-204" endpinid="Resistor-3-rPin" enodeid="enode-15" labelrot="0" pointList="-220,-204,-220,-180" rotation="0" id="Connector-17" valLabelx="0" valLabRot="0" x="-220"/>

PlotterWidget-13: 
<item modal="false" childrenRect="" normalGeometry="" baseSize="" geometry="" sizeIncrement="" windowOpacity="1" windowModified="false" enabled="true" maximumSize="" childrenRegion="" maximumHeight="200" inputMethodHints="0" mouseTracking="false" minimumSize="" frameGeometry="" sizeHint="" windowIconText="" locale="" minimumSizeHint="" height="200" isActiveWindow="true" x="0" accessibleName="" layoutDirection="0" autoFillBackground="false" width="200" windowFilePath="" windowModality="0" maximized="false" sizePolicy="" MinVolt="-500" fullScreen="false" windowTitle="" windowIcon="" maximumWidth="1000" objectName="PlotterWidget-13" toolTip="" toolTipDuration="-1" focus="false" MaxVolt="500" palette="" font="MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0" whatsThis="" pos="" minimumWidth="200" minimumHeight="200" size="" focusPolicy="0" itemtype="Plotter" updatesEnabled="true" accessibleDescription="" y="0" rect="" frameSize="" minimized="false" acceptDrops="false" styleSheet="" cursor="" visible="false" statusTip="" contextMenuPolicy="1"/>

SerialPortWidget-14: 
<item modal="false" childrenRect="" normalGeometry="" SettingsProp="COM1,0,3,0,0,0" baseSize="" geometry="" sizeIncrement="" windowOpacity="1" windowModified="false" enabled="true" maximumSize="" childrenRegion="" maximumHeight="170" inputMethodHints="0" mouseTracking="false" minimumSize="" frameGeometry="" sizeHint="" windowIconText="" locale="" minimumSizeHint="" height="141" isActiveWindow="true" x="0" accessibleName="" layoutDirection="0" autoFillBackground="false" width="313" windowFilePath="" windowModality="0" maximized="false" sizePolicy="" fullScreen="false" windowTitle="Settings" windowIcon="" maximumWidth="450" objectName="SerialPortWidget-14" toolTip="" toolTipDuration="-1" focus="false" palette="" font="MS Shell Dlg 2,8.25,-1,5,50,0,0,0,0,0" whatsThis="" pos="" minimumWidth="0" minimumHeight="0" size="" focusPolicy="0" itemtype="SerialPort" updatesEnabled="true" accessibleDescription="" y="0" rect="" frameSize="" minimized="false" acceptDrops="false" styleSheet="" cursor="" visible="false" statusTip="" contextMenuPolicy="1"/>

</circuit>

и я ожидаю, что светодиод будет включаться и выключаться каждый раз, когда я нажимаю кнопку, однако результат нестабильный:


Я был бы признателен, если бы вы помогли мне узнать, что-то не так с моим кодом или схемой, и мне следует ожидать такого же поведения на реальном оборудовании, или это проблема моделирования?

PS1. Все файлы также можно скачать здесь.

PS2. Я использую этот пример с сайта Arduino.org.

PS3. Я также попробовал использовать настоящую Arduino, реализовав приведенные ниже предложения. Однако проблема не решена. Вы можете посмотреть видео здесь.

, 👍1

Обсуждение

Вам нужно обнаружение изменения состояния: https://www.arduino.cc/en/Tutorial/StateChangeDetection, @Jot


4 ответа


2

Некоторые изменения в вашем коде:

const int buttonPin = 7;     
const int ledPin13 =  13;      

int buttonState = 0;         
int lastButtonState = buttonState;
bool flag = true;

void setup() {
  // инициализируем вывод светодиода как выход:
  pinMode(ledPin13, OUTPUT);
  // инициализируем вывод кнопки как вход:
  pinMode(buttonPin, INPUT);
}

void loop() {

  buttonState = digitalRead(buttonPin);


  if (buttonState == HIGH && lastButtonState != buttonState) {
    lastButtonState = buttonState;
    flag =!flag;
    if (flag){
      digitalWrite(ledPin13, LOW);
    } else {
      digitalWrite(ledPin13, HIGH);
    }
  }
}

Во-вторых, у вас плохая модель оборудования. АНОД светодиода должен быть подключен к PIN13, а катод – к GND. Переключатель должен быть установлен только между 3,3 В и контактом 7. В вашей модели, замыкая переключатель, вы замыкаете 3,3 В и GND. Если вы предоставили внешнее подтягивающее напряжение, вам следует использовать резистор номиналом не менее 4,7 кОм. 100 Ом никогда не работает.

У меня нет программного обеспечения для дизайна, поэтому я использовал Paint и сделал для вас скетч.

schematic

,

ах, плохо, что lastButtonState = buttonState; был в реальном коде, но я случайно пропустил его в примере. не могли бы вы нарисовать правильную схему? Я не уверен, правильно ли я понимаю, что вы имеете в виду., @Foad

ОП использует 5 В, а не 3,3 В. И как переключатель его цепи замыкает 3,3 В/5 В на землю? Если бы контакт 7 был выходом и НИЗКИМ, это был бы путь с низким сопротивлением к земле. Возможно, было бы неплохо поставить резистор 1 кОм на вывод 7., @Duncan C

В вашей схеме вам нужен токоограничивающий резистор на светодиоде, иначе он будет потреблять слишком большой ток от контакта 13 и перегружать светодиод (но я думаю, что ОП использует встроенный светодиод на контакте 13, а не добавляет внешний светодиод.), @Duncan C

В коде все еще есть проблема: значение LastButtonState всегда будет HIGH, поскольку оно находится внутри блока if(buttonState == HIGH)., @orithena


0

Это комментарий (но в комментарии нельзя использовать выравнивание текста):

Вместо

if (flag){
  digitalWrite(ledPin13, LOW);
} else {
  digitalWrite(ledPin13, HIGH);
}

Вы можете использовать:

digitalWrite(ledPin13, flag ? LOW : HIGH);

(Однако функционально они на 100% идентичны).

,

хороший. Я не знал, что на языке Arduino также есть тернарный оператор., @Foad

На самом деле Arduino IDE очень похожа на C++. Тернарный оператор взят из языка C., @Michel Keijzers

Очень приятно, что язык Arduino совместим с распространенными компиляторами C/C++., @Foad

Также на C++ написано несколько библиотек (поэтому вы можете использовать ОО/классы); однако некоторые функции лучше пропустить (например, библиотеку Boost, если она вообще работает) из-за динамического управления памятью, а не наличия только нескольких КБ SRAM., @Michel Keijzers

Я не понимаю, почему они проголосовали против вас. В любом случае я ценю вашу поддержку., @Foad

Вероятно, потому, что это должен быть комментарий (но выравнивание кода возможно только внутри ответа)., @Michel Keijzers

@Foad Вы можете рассматривать файл эскиза Arduino как часть стандартного файла C/C++. Перед компиляцией скетч просто вставляется в основной файл стандартного дерева сборки GCC, рядом с ним просто копируются включенные файлы библиотек и аппаратные библиотеки. Затем gcc вступает во владение; вы можете увидеть его вывод, если включите подробную компиляцию в настройках Arduino IDE., @orithena

@jsotola в основном int 0, 1, bool false, true и LOW, HIGH одинаковы?, @Foad

@orthena, это за кадром avr-gcc?, @Foad

@Foad Какой именно gcc используется, зависит от выбранной платы (для моих плат это gcc-xtensa, но я использую только платы ESP8266 ^^). Подробный вывод компиляции покажет точный используемый двоичный файл компилятора., @orithena

@orithena, как/где мне установить опцию verbos?, @Foad

@jsotola может быть опасно принимать значения для LOW и HIGH, что, если ST решит, что LOW — 25, а HIGH — 38? Маловероятно, но я бы не хотел, чтобы мой код зависел от значений LOW и HIGH., @Michel Keijzers

@Foad См. снимок экрана https://www.arduino.cc/en/Main/Preferences, «Показать подробный вывод во время компиляции [x]»., @orithena


2

Я выявил здесь несколько проблем:

  • Подтягивающий резистор кнопки сопротивлением 100 Ом слишком мал. Обычно здесь используется сопротивление от 1 до 100 кОм.
  • если ( buttonState = HIGH ) не сравнивает, он присваивает buttonState значение HIGH (перезаписывая значение, прочитанное с помощью digitalRead( buttonPin)). Используйте здесь оператор сравнения для равенства ==, иначе if просто проверяет, присвоено ли buttonState «правдивое» значение (кстати, HIGH является одним из них) .
  • Как уже отмечали другие, LastButtonState должен быть установлен где-то после блока if!

Поскольку многие Arduino имеют встроенные подтягивающие резисторы, я бы сказал: полностью отключите резистор, вместо этого инициализируйте входной контакт кнопки (7), чтобы использовать его встроенный подтягивающий резистор... это инвертирует логику, и вы Однако придется подключить кнопку между контактом 7 и GND (т.е. поменять местами кнопку и резистор):

    pinMode(buttonPin, INPUT_PULLUP);

Это будет иметь тот же результат, как если бы вы заменили резистор сопротивлением 100 Ом на резистор сопротивлением 20 кОм после замены кнопки и резистора, но этот резистор теперь находится внутри Arduino. (Будьте осторожны, если вы используете «Arduino-совместимые» платы; иногда на них отсутствуют подтягивающие резисторы или они исправлены!).

Есть еще одна проблема: подпрыгивание. Нажатие механической кнопки приводит к тому, что контакты внутри кнопки немного подпрыгивают друг о друга, что приводит к очень быстрому сигналу ВЫСОКИЙ-НИЗКИЙ-ВЫСОКИЙ-НИЗКИЙ и т. д., прежде чем он установится в целевое состояние. Вы можете немного облегчить эту ситуацию, добавив delay(5) в конец вашего loop(), но используя библиотеку кнопок (например, Кнопка, доступная в диспетчере библиотек Arduino IDE), которая устраняет дребезг сигнала, в большинстве случаев приводит к лучшим результатам.

Это приведет к следующему коду (также включая подсказку о тернарном операторе Мишеля Кейзерса):

const int buttonPin = 7;     
const int ledPin13 =  13;      

int buttonState = 0;         
int lastButtonState = buttonState;
bool flag = true;

void setup() {
  // инициализируем вывод светодиода как выход:
  pinMode(ledPin13, OUTPUT);
  // инициализируем вывод кнопки как вход:
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  buttonState = digitalRead(buttonPin);

  if (buttonState == LOW && lastButtonState != buttonState) {
    flag = !flag;
    digitalWrite(ledPin13, flag ? LOW : HIGH);
  }

  lastButtonState = buttonState;
  delay(5);
}

Это должно помочь (правда, я не пробовал).

,

О боже, как я могла совершить такую ошибку!!! Спасибо, что указали на это. одна проблема, которую вы должны учитывать, заключается в том, что Arduino uno не имеет INPUT_PULLDOWN. Видимо такое есть только у Зеро? Я получаю сообщение об ошибке: «INPUT_PULLDOWN не был объявлен в этой области», @Foad

хорошо. кажется, pinMode(buttonPin, INPUT_PULLDOWN); эквивалентен pinMode(pin, INPUT); digitalWrite(pin, LOW); и pinMode(buttonPin, INPUT_PULLUP); — это pinMode(pin, INPUT); digitalWrite(pin, HIGH); и этот синтаксис не работает должным образом в некоторых версиях Arduino IDE., @Foad

@Foad Вы правы, согласно https://www.arduino.cc/en/Tutorial/DigitalPins, INPUT_PULLDOWN кажется довольно редким... но INPUT_PULLUP должен работать. Однако использование этого параметра приведет к инвертированию логики на выводе кнопки. Я отредактирую, чтобы отразить это., @orithena

не могли бы вы 1. нарисовать схему с использованием подтягивающего резистора 2. указать мне хорошую библиотеку кнопок? еще раз спасибо., @Foad

@Foad Я добавил ссылку на самую простую библиотеку кнопок с отвязкой от диспетчера библиотек Arduino IDE; документация находится на его странице github. Рисование схемы, должно быть, придется подождать, пока я вернусь домой., @orithena


1

Уже есть отличные ответы, но я также узнал о функции прерывания:

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, FALLING);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  state = !state;
}

какой-то момент:

  • независимо от использования прерывания или описанных выше методов, процесс не достигнет стабильного/детерминированного результата. Проблема заключается в аппаратном обеспечении кнопки, и могут возникать колебания, которые может интерпретироваться MCU. Один из способов облегчить ситуацию — использовать задержку.
  • attachInterrupt имеет четыре режима: LOW, Change, FALLING и RISING. Следует использовать режимы FALLING и RISING. два других также приводят к весьма непредсказуемому результату.
  • Для прерываний можно использовать только контакты 1 и 2.
  • Существует также возможность совместить два метода, которые вы можете увидеть здесь, но я не уверен, что это улучшит стабильность.

Я использовал это видео и информацию на эта страница.

,