Изменение состояния светодиода с помощью кнопки приводит к нестабильному результату
Я пытаюсь запустить этот код:
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, реализовав приведенные ниже предложения. Однако проблема не решена. Вы можете посмотреть видео здесь.
@Foad, 👍1
Обсуждение4 ответа
Некоторые изменения в вашем коде:
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 и сделал для вас скетч.
ах, плохо, что lastButtonState = buttonState;
был в реальном коде, но я случайно пропустил его в примере. не могли бы вы нарисовать правильную схему? Я не уверен, правильно ли я понимаю, что вы имеете в виду., @Foad
ОП использует 5 В, а не 3,3 В. И как переключатель его цепи замыкает 3,3 В/5 В на землю? Если бы контакт 7 был выходом и НИЗКИМ, это был бы путь с низким сопротивлением к земле. Возможно, было бы неплохо поставить резистор 1 кОм на вывод 7., @Duncan C
В вашей схеме вам нужен токоограничивающий резистор на светодиоде, иначе он будет потреблять слишком большой ток от контакта 13 и перегружать светодиод (но я думаю, что ОП использует встроенный светодиод на контакте 13, а не добавляет внешний светодиод.), @Duncan C
В коде все еще есть проблема: значение LastButtonState всегда будет HIGH, поскольку оно находится внутри блока if(buttonState == HIGH)., @orithena
Это комментарий (но в комментарии нельзя использовать выравнивание текста):
Вместо
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
Я выявил здесь несколько проблем:
- Подтягивающий резистор кнопки сопротивлением 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
Уже есть отличные ответы, но я также узнал о функции прерывания:
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
. - Существует также возможность совместить два метода, которые вы можете увидеть здесь, но я не уверен, что это улучшит стабильность.
Я использовал это видео и информацию на эта страница.
- Остановить мигание светодиодов
- Интеграция 2 кнопок для включения и выключения светодиода.
- Код Arduino для управления 4 светодиодами с 4 кнопок
- Нужен ли подтягивающий/понижающий резистор для цепи светодиода кнопки?
- Светодиод с кнопочным управлением Arduino со сборкой AVR
- Как повторить другое действие внутри цикла?
- Кнопка переключения переключает между операторами обращения с разблокированием кнопки
- ШИМ-управление, как остановить мерцание светодиода?
Вам нужно обнаружение изменения состояния: https://www.arduino.cc/en/Tutorial/StateChangeDetection, @Jot