Arduino Micro: 69 должен отображаться, когда я нажимаю 1 на клавиатуре, однако появляется какое-то странное число (в данном случае 15).
// **У МЕНЯ ТАКЖЕ ПОЛУЧАЮТСЯ СТРАННЫЕ БЕЗУМНЫЕ ЗАДЕРЖКИ ПРИ НАЖАТИИ КНОПОК В МЕНЮ РЕЖИМОВ**
/*
Система управления запуском Джебедайи для космической программы Кербал
Альфа-сборка 0.70
Проект с открытым исходным кодом Джона Сеонга
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
const byte ROWS = 4;
const byte COLS = 3;
char keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
int timeV, thrustV, massV, gravityV;
byte rowPins[ROWS] = {12, 11, 10, 9};
byte colPins[COLS] = {8, 7, 6};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
bool keyDetect = false;
bool menuKeyDetect = false;
bool goHomeDetect = false;
bool countDownDetect = false;
bool countDownOutput = false;
bool twrCalDetect = false;
String timeValue, thrustValue, massValue, gravityValue;
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcd.clear();
Startup();
timeV = constrain(timeV, 15, 120);
}
void loop() {
PressKey();
if (menuKeyDetect == true) {
PressMenuKey();
}
if (goHomeDetect == true) {
GoHome();
}
if (countDownDetect == true) {
CountDownSequence();
}
if (countDownOutput == true) {
CountDownOutputSequence();
}
}
// Страницы & Разделы
void Startup() {
lcd.print("Jeb's Launch");
lcd.setCursor(0, 1);
lcd.print("Control System");
delay(3000);
lcd.clear();
lcd.print("Alpha Build 0.70");
lcd.setCursor(0, 1);
lcd.print(" STUDIO HORIZON");
delay(3000);
lcd.clear();
lcd.print("Welcome, Kerman");
lcd.setCursor(0, 1);
lcd.print("Press any key...");
keyDetect = true;
}
void ModeMenu() {
menuKeyDetect = true;
lcd.clear();
lcd.print("1. Countdown");
lcd.setCursor(0, 1);
lcd.print("2. TWR");
}
void CountDown() {
lcd.print("Time (sec): ");
lcd.setCursor(0, 1);
lcd.print("# to continue...");
goHomeDetect = true;
countDownDetect = true;
}
void TwrCal() {
lcd.print("Thrust: ");
lcd.setCursor(0, 1);
lcd.print("# to continue...");
goHomeDetect = true;
}
// Действия & Поведение
void PressKey() {
char key = keypad.getKey();
if (key) {
Serial.println(key);
if (keyDetect == true) {
lcd.clear();
ModeMenu();
keyDetect = false;
}
}
}
void PressMenuKey() {
char key = keypad.getKey();
if (key == '1') {
Serial.println(key);
if (menuKeyDetect == true) {
lcd.clear();
CountDown();
menuKeyDetect = false;
}
} else if (key == '2') {
Serial.println(key);
if (menuKeyDetect == true) {
lcd.clear();
TwrCal();
menuKeyDetect = false;
}
}
}
void GoHome() {
char key = keypad.getKey();
if (key == '*') {
lcd.clear();
ModeMenu();
}
}
void CountDownSequence() {
char key = keypad.getKey();
if (key == '0') {
timeV = timeV + 0;
}
if (key == '1') {
timeV = 69;
}
countDownDetect = false;
countDownOutput = true;
}
void CountDownOutputSequence() {
lcd.setCursor(13, 0);
Serial.println(timeV);
lcd.print(timeV);
}
@John Seong, 👍1
1 ответ
Лучший ответ:
Согласно документации, функция getKey()
неблокирующий. Это означает, что он не ожидает нажатия клавиши:
вместо этого он немедленно возвращает значение независимо от того, была нажата клавиша или нет. Если
никакая клавиша не была нажата (т.е. большую часть времени), она возвращает NUL
символ, который имеет нулевое числовое значение и оценивается как false
в
логический контекст. Отсюда идиома:
char key = keypad.getKey();
if (key) { // если клавиша действительно была нажата
// справиться
}
Функция CountDownSequence()
не учитывает это:
void CountDownSequence() {
char key = keypad.getKey(); // нет проверки на NUL
if (key == '0') {
timeV = timeV + 0; // это вообще ни на что не влияет
}
if (key == '1') {
timeV = 69; // это то, что вы ожидаете выполнить
}
countDownDetect = false; // эти две строки сообщают остальным
countDownOutput = true; // программа, на которой мы закончили.
}
Поскольку key
почти наверняка будет '\0'
, программа перейдет к
следующий шаг без инициализации timeV
.
В качестве примечания я рекомендую вам попробовать переписать свою программу как конечный
конечный автомат. Одна переменная state
сделает его менее
сбивает с толку и легче понять, чем текущие коллекции
логические значения.
Правка: расширение идеи конечного автомата. я попробовал реализация вашей концепции на его основе.
Для работы с системой меню, где пользователь может перемещаться по нескольким экранные «страницы», самый очевидный первый подход — иметь одно состояние за отображаемую страницу. Для этой программы эти состояния могут быть:
МЕНЮ
, предлагающее перейти к одному из следующих двух страницыSET_COUNT
позволяет пользователю установить продолжительность обратного отсчетаTWR
позволяет ему вычислить отношение тяги к весуCOUNT_DOWN
фактически запускает таймер обратного отсчета
В дальнейшем я добавил дополнительное переходное состояние под названием START
.
который идет прямо перед MENU
. Это не обязательно, и это только
цель состоит в том, чтобы избежать повторения кода, имея один переход
(START
→ MENU
), отвечающие за фактическое рисование меню.
При реализации конечного автомата моей первой рекомендацией будет можно начать с рисования диаграммы состояний, подобной этой:
На этой диаграмме каждый переход помечен событием, которое запускает Это. События представляют собой нажатия клавиш, за исключением «завершено», что означает, что обратный отсчет сделан. Диаграмма состояний может стать значительно больше сложнее, чем это, когда вы добавляете новые функции. Однако это очень хороший способ проектировать вашу систему с точки зрения пользователя взаимодействие.
После того как вы довольны диаграммой состояний, кодирование становится просто делом.
преобразования его в конструкцию switch
/case
с одним case
на
государство. Вот мой взгляд на это:
/*
Jebediah's Launch Control System for Kerbal Space Program
Alpha Build 0.70
An Open-Source Project by John Seong
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
const byte ROWS = 4;
const byte COLS = 3;
char keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
byte rowPins[ROWS] = {12, 11, 10, 9};
byte colPins[COLS] = {8, 7, 6};
Keypad keypad=Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// Показать двухстрочное сообщение на ЖК-дисплее.
void lcdShow(const char *line0, const char *line1) {
lcd.clear();
lcd.print(line0);
lcd.setCursor(0, 1);
lcd.print(line1);
}
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
lcdShow("Jeb's Launch", "Control System");
delay(3000);
lcdShow("Alpha Build 0.70", " STUDIO HORIZON");
delay(3000);
lcdShow("Welcome, Kerman", "Press any key...");
while (!keypad.getKey()) ; // ждем нажатия клавиши
}
void loop() {
static enum {START, MENU, SET_COUNT, COUNT_DOWN, TWR} state = START;
static uint32_t last_second; // значение millis() за последнюю полную секунду
static int count; // значение обратного отсчета
char key = keypad.getKey();
switch (state) {
case START: // переходное состояние
lcdShow("1. Countdown", "2. TWR");
state = MENU;
/* fallthrough */
case MENU:
if (key == '1') { // Обратный отсчет
lcdShow("Enter time", "seconds: ");
count = 0;
state = SET_COUNT;
} else if (key == '2') { // TWR
lcdShow("Thrust:", "# to continue...");
state = TWR;
}
break;
case SET_COUNT:
if (key >= '0' && key <= '9' && count <= 99) {
lcd.print(key);
count = 10 * count + (key - '0');
} else if (key == '#') {
lcdShow("Take off in", " seconds");
// Принудительное обновление при вводе COUNT_DOWN:
last_second = millis() - 1000;
count++;
state = COUNT_DOWN;
} else if (key == '*') {
state = START;
}
break;
case COUNT_DOWN:
if (millis() - last_second >= 1000) {
last_second += 1000;
count--;
if (count == 0) {
Serial.println("Take off!");
} else if (count < 0) {
state = START;
break;
}
lcd.setCursor(1, 1);
lcd.print(count<10 ? " " : count<100 ? " " : ""); // накладка
lcd.print(count);
} else if (key == '*') {
state = START;
}
break;
case TWR:
if (key == '*' || key == '#') {
state = START;
}
break;
}
}
Обратите внимание на комментарий «fallthrough» между START
и MENU
. Этот
указывает на то, что отсутствие break
в этом месте предназначено:
обработка случая MENU
последует сразу за обработкой
случай START
в той же итерации цикла. Цель падения
через, чтобы не потерять текущее нажатие клавиши, если таковое имеется. Если это не
важно, вы можете поставить здесь обычный break
и отложить обработку
MENU
к следующей итерации цикла.
Обратите внимание, что нажатия клавиш проверяются тестами вида
if (key == SOME_VALUE)
, что означает отсутствие необходимости в дополнительном
if (key)
для проверки фактического нажатия клавиши.
- Arduino Micro против Pro Micro
- Arduino Pro Micro не распознается компьютером и не отображается в портах
- Программирование Arduino Micro через RX/TX
- TCCR1A и TCCR2A на Леонардо
- 433 МГц отправка не принимается
- Arduino : отправка данных через спутник (или другие решения ? без GSM)
- Arduino Micro 5V Vin и контакты 3v3
- Вибрационный геймпад Arduino с силовой обратной связью