//--------------------------------------------------
//линия, к которой подключена кнопка
#define BUTTON_INPUT 2
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порт для обработки кнопки
//вход с подтяжкой к плюсу питания
pinMode(BUTTON_INPUT, INPUT_PULLUP);
//настройки для передачи данных в терминал
Serial.begin(9600);
}
//супер цикл
void loop() {
//если кнопка нажата
if(digitalRead(BUTTON_INPUT) == LOW){
Serial.print("button pressed\r");
}
//если кнопка не нажата
else{
Serial.print("button not pressed\r");
}
}
//супер цикл
void loop() {
//проверка нажатия кнопки
if(digitalRead(BUTTON_INPUT) == LOW){
//один раз выводим текст
Serial.print("button pressed\r");
//ничего не делаем, пока кнопка нажата
while(digitalRead(BUTTON_INPUT) == LOW);
}
}
//величина задержки для защиты от дребезга кнопки
#define BUTTON_PROTECTION 50
//--------------------------------------------------
//супер цикл
void loop() {
//проверка нажатия кнопки
if(digitalRead(BUTTON_INPUT) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(BUTTON_INPUT) == LOW){
//один раз выводим текст
Serial.print("button pressed\r");
//ничего не делаем, пока кнопка нажата
while(digitalRead(BUTTON_INPUT) == LOW);
}
}
}
//--------------------------------------------------
//линия, к которой подключена кнопка
#define BUTTON_INPUT 2
//величина задержки для защиты от дребезга кнопки
#define BUTTON_PROTECTION 50
//физическое состояние кнопки
enum ButtonResult {buttonNotPress, buttonPress};
//--------------------------------------------------
//обработка кнопки
enum ButtonResult get_button(void){
if(digitalRead(BUTTON_INPUT) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(BUTTON_INPUT) == LOW){
//ничего не делаем, пока кнопка нажата
while(digitalRead(BUTTON_INPUT) == LOW);
//сообщаем, что кнопка нажата
return buttonPress;
}
}
//сообщаем, что кнопку не нажимали
return buttonNotPress;
}
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порт для обработки кнопки
//вход с подтяжкой к плюсу питания
pinMode(BUTTON_INPUT, INPUT_PULLUP);
//настройки для передачи данных в терминал
Serial.begin(9600);
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case buttonPress:
Serial.write("Button pressed\r");
break;
case buttonNotPress:break;
}
}
//--------------------------------------------------
//линия, к которой подключена кнопка
#define BUTTON_INPUT 2
//шаг измерения времени нажатия кнопки
#define TIME_STEP 10
//время длинного нажатия кнопки
#define BUTTON_LONG_PRESS_TIME 500
//время короткого нажатия кнопки
#define BUTTON_SHORT_PRESS_TIME 100
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //код если кнопка не нажата
buttonShortPress, //код короткого нажатия
buttonLongPress //код длинного нажатия
};
//--------------------------------------------------
//обработка кнопки
enum ButtonResult get_button(void){
//для измерения времени нажатия
uint16_t buttonPressTime = 0;
//проверка нажатия кнопки
while(digitalRead(BUTTON_INPUT) == LOW){
//шаг по шкале времени
delay(TIME_STEP);
//считаем время
buttonPressTime += TIME_STEP;
//это нужно, чтоб счетчик не переполнился, если кто-то уснет на кнопке
if(buttonPressTime > BUTTON_LONG_PRESS_TIME)
buttonPressTime = BUTTON_LONG_PRESS_TIME;
}
//проверяем длинное нажатие кнопки
if(buttonPressTime >= BUTTON_LONG_PRESS_TIME)
return buttonLongPress;
//проверяем короткое нажатие кнопки
if(buttonPressTime >= BUTTON_SHORT_PRESS_TIME)
return buttonShortPress;
//сообщаем, что кнопку не нажимали
return buttonNotPress;
}
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порт для обработки кнопки
//вход с подтяжкой к плюсу питания
pinMode(BUTTON_INPUT, INPUT_PULLUP);
//настройки для передачи данных в терминал
Serial.begin(9600);
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case buttonShortPress:
Serial.write("Button short pressed\r");
break;
case buttonLongPress:
Serial.write("Button long pressed\r");
break;
case buttonNotPress:break;
}
}
//--------------------------------------------------
//линии, к которым подключены кнопки
#define BUTTON_INPUT_SB1 3
#define BUTTON_INPUT_SB2 2
//величина задержки для защиты от дребезга кнопки
#define BUTTON_PROTECTION 50
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //если кнопка не нажата
button_SB1_Press, //если кнопка SB1 нажата
button_SB2_Press //если кнопка SB2 нажата
};
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порт для обработки кнопки
//вход с подтяжкой к плюсу питания
pinMode(BUTTON_INPUT_SB1, INPUT_PULLUP);
pinMode(BUTTON_INPUT_SB2, INPUT_PULLUP);
//настройки для передачи данных в терминал
Serial.begin(9600);
}
//--------------------------------------------------
//обработка кнопки
enum ButtonResult get_button(void){
//проверка нажатия кнопки SB1
if(digitalRead(BUTTON_INPUT_SB1) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(BUTTON_INPUT_SB1) == LOW){
//ничего не делаем, пока кнопка нажата
while(digitalRead(BUTTON_INPUT_SB1) == LOW);
//сообщаем, что кнопка нажата
return button_SB1_Press;
}
}
//проверка нажатия кнопки SB2
if(digitalRead(BUTTON_INPUT_SB2) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(BUTTON_INPUT_SB2) == LOW){
//ничего не делаем, пока кнопка нажата
while(digitalRead(BUTTON_INPUT_SB2) == LOW);
//сообщаем, что кнопка нажата
return button_SB2_Press;
}
}
//сообщаем, что кнопку не нажимали
return buttonNotPress;
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case button_SB1_Press:
Serial.write("Button SB1 pressed\r");
break;
case button_SB2_Press:
Serial.write("Button SB2 pressed\r");
break;
case buttonNotPress:break;
}
}
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //если кнопка не нажата
button_SB1_Press, //если кнопка SB1 нажата
button_SB2_Press, //если кнопка SB2 нажата
button_SB3_Press, //если кнопка SB3 нажата
button_SB4_Press //если кнопка SB4 нажата
};
//для описания параметров кнопки
struct ButtonConfig {
//номер входа, к которому подключена кнопка
uint8_t pin;
//код кнопки при нажатии
enum ButtonResult identifier;
};
//перечисление имен кнопок
enum ButtonNames {
ButtonNamesStart = 0,
SB1 = ButtonNamesStart, SB2, SB3, SB4,
NumberOfButtons
};
//массив для хранения параметров кнопок
struct ButtonConfig button[NumberOfButtons] = {
[SB1] = {.pin = 4, .identifier = button_SB1_Press},
[SB2] = {.pin = 5, .identifier = button_SB2_Press},
[SB3] = {.pin = 3, .identifier = button_SB3_Press},
[SB4] = {.pin = 2, .identifier = button_SB4_Press}
};
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порты для обработки кнопок
//вход с подтяжкой к плюсу питания
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
pinMode(button[num].pin, INPUT_PULLUP);
}
//настройки для передачи данных в терминал
Serial.begin(9600);
}
//--------------------------------------------------
//обработка кнопки
enum ButtonResult get_button(void){
//поочереди перебираем параметры кнопок
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//проверка кнопки по текущему индексу
if(digitalRead(button[num].pin) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(button[num].pin) == LOW){
//ничего не делаем, пока кнопка нажата
while(digitalRead(button[num].pin) == LOW);
//сообщаем, что кнопка нажата
return button[num].identifier;
}
}
}
//сообщаем, что кнопку не нажимали
return buttonNotPress;
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case button_SB1_Press:
Serial.write("Button SB1 pressed\r");
break;
case button_SB2_Press:
Serial.write("Button SB2 pressed\r");
break;
case button_SB3_Press:
Serial.write("Button SB3 pressed\r");
break;
case button_SB4_Press:
Serial.write("Button SB4 pressed\r");
break;
case buttonNotPress:break;
}
}
//величина задержки для защиты от дребезга кнопки
#define BUTTON_PROTECTION 50
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //если кнопка не нажата
button_SB1_Press, //если кнопка SB1 нажата
button_SB2_Press, //если кнопка SB2 нажата
button_SB3_Press, //если кнопка SB3 нажата
button_SB4_Press //если кнопка SB4 нажата
};
//--------------------------------------------------
//для описания параметров кнопки
struct ButtonConfig {
//номер входа, к которому подключена кнопка
uint8_t pin;
//код кнопки при нажатии
enum ButtonResult identifier;
};
//перечисление имен кнопок
enum ButtonNames {
ButtonNamesStart = 0,
SB1 = ButtonNamesStart, SB2, SB3, SB4,
NumberOfButtons
};
//--------------------------------------------------
//массив для хранения параметров кнопок
struct ButtonConfig button[NumberOfButtons] = {
[SB1] = {.pin = 4, .identifier = button_SB1_Press},
[SB2] = {.pin = 5, .identifier = button_SB2_Press},
[SB3] = {.pin = 3, .identifier = button_SB3_Press},
[SB4] = {.pin = 2, .identifier = button_SB4_Press}
};
//--------------------------------------------------
//обработка кнопки
enum ButtonResult get_button(void){
//поочереди перебираем параметры кнопок
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//проверка кнопки по текущему индексу
if(digitalRead(button[num].pin) == LOW){
//пауза для защиты от дребезга
delay(BUTTON_PROTECTION);
//повторный опрос кнопки
if(digitalRead(button[num].pin) == LOW){
//ничего не делаем, пока кнопка нажата
while(digitalRead(button[num].pin) == LOW);
//сообщаем, что кнопка нажата
return button[num].identifier;
}
}
}
//сообщаем, что кнопку не нажимали
return buttonNotPress;
}
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порты для обработки кнопок
//вход с подтяжкой к плюсу питания
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
pinMode(button[num].pin, INPUT_PULLUP);
}
//настройки для передачи данных в терминал
Serial.begin(9600);
Serial.write("Button test\r");
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case button_SB1_Press:
Serial.write("Button SB1 pressed\r");
break;
case button_SB2_Press:
Serial.write("Button SB2 pressed\r");
break;
case button_SB3_Press:
Serial.write("Button SB3 pressed\r");
break;
case button_SB4_Press:
Serial.write("Button SB4 pressed\r");
break;
case buttonNotPress:break;
}
}
//--------------------------------------------------
//шаг измерения времени нажатия кнопки
#define TIME_STEP 10
//время длинного нажатия кнопки
#define BUTTON_LONG_PRESS_TIME 500
//время короткого нажатия кнопки
#define BUTTON_SHORT_PRESS_TIME 100
//--------------------------------------------------
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //если кнопка не нажата
button_SB1_shortPress, //код короткого нажатия кнопки SB1
button_SB1_longPress, //код длинного нажатия кнопки SB1
button_SB2_shortPress, //код короткого нажатия кнопки SB2
button_SB2_longPress, //код длинного нажатия кнопки SB2
button_SB3_shortPress, //код короткого нажатия кнопки SB3
button_SB3_longPress, //код длинного нажатия кнопки SB3
button_SB4_shortPress, //код короткого нажатия кнопки SB4
button_SB4_longPress, //код длинного нажатия кнопки SB4
buttonPress //нажатие любой кнопки
};
//--------------------------------------------------
//перечисление имен кнопок
enum ButtonNames {
ButtonNamesStart = 0,
SB1 = ButtonNamesStart, SB2, SB3, SB4,
NumberOfButtons
};
//--------------------------------------------------
//для описания параметров кнопки
struct ButtonConfig {
//номер входа, к которому подключена кнопка
uint8_t pin;
//код кнопки при нажатии
enum ButtonResult shortPressIdentifier;
enum ButtonResult longPressIdentifier;
//для промежуточного хранения результата
enum ButtonResult result;
//для измерения длительности нажатия
uint16_t pressingTime;
};
//--------------------------------------------------
//массив для хранения параметров кнопок
struct ButtonConfig button[NumberOfButtons] = {
[SB1] = {.pin = 4, .shortPressIdentifier = button_SB1_shortPress, .longPressIdentifier = button_SB1_longPress},
[SB2] = {.pin = 5, .shortPressIdentifier = button_SB2_shortPress, .longPressIdentifier = button_SB2_longPress},
[SB3] = {.pin = 3, .shortPressIdentifier = button_SB3_shortPress, .longPressIdentifier = button_SB3_longPress},
[SB4] = {.pin = 2, .shortPressIdentifier = button_SB4_shortPress, .longPressIdentifier = button_SB4_longPress}
};
//--------------------------------------------------
//обработка текущего состояния кнопок
enum ButtonResult checkingButtonStatus(void){
//проверка, была ли нажата хоть одна кнопка
enum ButtonResult check = buttonNotPress;
//последовательно обрабатываем все кнопки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если кнопка нажата,
if(digitalRead(button[num].pin) == LOW){
//увеличиваем счетчик времени ее нажатия
button[num].pressingTime += TIME_STEP;
//чтобы счетчик не переполнился, если кнопка залипнет
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].pressingTime = BUTTON_LONG_PRESS_TIME;
//запоминаем, что кнопка нажималась
check = buttonPress;
}
//если не нажата, проверяем измеренное время
else{
//проверяем на длинное нажатие
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].result = button[num].longPressIdentifier;
//проверяем короткое нажатие
else if(button[num].pressingTime >= BUTTON_SHORT_PRESS_TIME)
button[num].result = button[num].shortPressIdentifier;
//сбрасываем время
button[num].pressingTime = 0;
}
}
//сообщаем, была ли хоть одна кнопка нажата
return check;
}
//обработка нажатия кнопок
enum ButtonResult get_button(void){
//для хранения кода кнопки
enum ButtonResult temp = buttonNotPress;
//пока хоть одна кнопка нажата, измеряем время
while(checkingButtonStatus() == buttonPress){
delay(TIME_STEP);
}
//проверяем результат обработки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если было нажатие кнопки, запоминаем его
if(button[num].result != buttonNotPress)
temp = button[num].result;
//сбрасываем результаты одработки
button[num].result = buttonNotPress;
button[num].pressingTime = 0;
}
//возвращаем код нажатия
return temp;
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case button_SB1_shortPress:
Serial.write("Button SB1 short pressed\r");
break;
case button_SB2_shortPress:
Serial.write("Button SB2 short pressed\r");
break;
case button_SB3_shortPress:
Serial.write("Button SB3 short pressed\r");
break;
case button_SB4_shortPress:
Serial.write("Button SB4 short pressed\r");
break;
case button_SB1_longPress:
Serial.write("Button SB1 long pressed\r");
break;
case button_SB2_longPress:
Serial.write("Button SB2 long pressed\r");
break;
case button_SB3_longPress:
Serial.write("Button SB3 long pressed\r");
break;
case button_SB4_longPress:
Serial.write("Button SB4 long pressed\r");
break;
case buttonNotPress:break;
}
}
//--------------------------------------------------
//шаг измерения времени нажатия кнопки
#define TIME_STEP 10
//время длинного нажатия кнопки
#define BUTTON_LONG_PRESS_TIME 500
//время короткого нажатия кнопки
#define BUTTON_SHORT_PRESS_TIME 100
//--------------------------------------------------
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress, //если кнопка не нажата
button_SB1_shortPress, //код короткого нажатия кнопки SB1
button_SB1_longPress, //код длинного нажатия кнопки SB1
button_SB2_shortPress, //код короткого нажатия кнопки SB2
button_SB2_longPress, //код длинного нажатия кнопки SB2
button_SB3_shortPress, //код короткого нажатия кнопки SB3
button_SB3_longPress, //код длинного нажатия кнопки SB3
button_SB4_shortPress, //код короткого нажатия кнопки SB4
button_SB4_longPress, //код длинного нажатия кнопки SB4
buttonPress //нажатие любой кнопки
};
//--------------------------------------------------
//перечисление имен кнопок
enum ButtonNames {
ButtonNamesStart = 0,
SB1 = ButtonNamesStart, SB2, SB3, SB4,
NumberOfButtons
};
//--------------------------------------------------
//для описания параметров кнопки
struct ButtonConfig {
//номер входа, к которому подключена кнопка
uint8_t pin;
//код кнопки при нажатии
enum ButtonResult shortPressIdentifier;
enum ButtonResult longPressIdentifier;
//для промежуточного хранения результата
enum ButtonResult result;
//для измерения длительности нажатия
uint16_t pressingTime;
};
//--------------------------------------------------
//массив для хранения параметров кнопок
struct ButtonConfig button[NumberOfButtons] = {
[SB1] = {.pin = 4, .shortPressIdentifier = button_SB1_shortPress, .longPressIdentifier = button_SB1_longPress},
[SB2] = {.pin = 5, .shortPressIdentifier = button_SB2_shortPress, .longPressIdentifier = button_SB2_longPress},
[SB3] = {.pin = 3, .shortPressIdentifier = button_SB3_shortPress, .longPressIdentifier = button_SB3_longPress},
[SB4] = {.pin = 2, .shortPressIdentifier = button_SB4_shortPress, .longPressIdentifier = button_SB4_longPress}
};
//--------------------------------------------------
//обработка текущего состояния кнопок
enum ButtonResult checkingButtonStatus(void){
//проверка, была ли нажата хоть одна кнопка
enum ButtonResult check = buttonNotPress;
//последовательно обрабатываем все кнопки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если кнопка нажата,
if(digitalRead(button[num].pin) == LOW){
//увеличиваем счетчик времени ее нажатия
button[num].pressingTime += TIME_STEP;
//чтобы счетчик не переполнился, если кнопка залипнет
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].pressingTime = BUTTON_LONG_PRESS_TIME;
//запоминаем, что кнопка нажималась
check = buttonPress;
}
//если не нажата, проверяем измеренное время
else{
//проверяем на длинное нажатие
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].result = button[num].longPressIdentifier;
//проверяем короткое нажатие
else if(button[num].pressingTime >= BUTTON_SHORT_PRESS_TIME)
button[num].result = button[num].shortPressIdentifier;
//сбрасываем время
button[num].pressingTime = 0;
}
}
//сообщаем, была ли хоть одна кнопка нажата
return check;
}
//--------------------------------------------------
//обработка нажатия кнопок
enum ButtonResult get_button(void){
//для хранения кода кнопки
enum ButtonResult temp = buttonNotPress;
//пока хоть одна кнопка нажата, измеряем время
while(checkingButtonStatus() == buttonPress){
delay(TIME_STEP);
}
//проверяем результат обработки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если было нажатие кнопки, запоминаем его
if(button[num].result != buttonNotPress)
temp = button[num].result;
//сбрасываем результаты одработки
button[num].result = buttonNotPress;
button[num].pressingTime = 0;
}
//возвращаем код нажатия
return temp;
}
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порты для обработки кнопок
//вход с подтяжкой к плюсу питания
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
pinMode(button[num].pin, INPUT_PULLUP);
}
//настройки для передачи данных в терминал
Serial.begin(9600);
Serial.write("Button test\r");
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
switch(get_button()){
case button_SB1_shortPress:
Serial.write("Button SB1 short pressed\r");
break;
case button_SB2_shortPress:
Serial.write("Button SB2 short pressed\r");
break;
case button_SB3_shortPress:
Serial.write("Button SB3 short pressed\r");
break;
case button_SB4_shortPress:
Serial.write("Button SB4 short pressed\r");
break;
case button_SB1_longPress:
Serial.write("Button SB1 long pressed\r");
break;
case button_SB2_longPress:
Serial.write("Button SB2 long pressed\r");
break;
case button_SB3_longPress:
Serial.write("Button SB3 long pressed\r");
break;
case button_SB4_longPress:
Serial.write("Button SB4 long pressed\r");
break;
case buttonNotPress:break;
}
}
//--------------------------------------------------
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress = 0, //если кнопка не нажата
button_SB1_shortPress = 1<<0, //код короткого нажатия кнопки SB1
button_SB1_longPress = 1<<1, //код длинного нажатия кнопки SB1
button_SB2_shortPress = 1<<2, //код короткого нажатия кнопки SB2
button_SB2_longPress = 1<<3, //код длинного нажатия кнопки SB2
button_SB3_shortPress = 1<<4, //код короткого нажатия кнопки SB3
button_SB3_longPress = 1<<5, //код длинного нажатия кнопки SB3
button_SB4_shortPress = 1<<6, //код короткого нажатия кнопки SB4
button_SB4_longPress = 1<<7, //код длинного нажатия кнопки SB4
buttonPress = 0xff //нажатие любой кнопки
};
//обработка нажатия кнопок
uint8_t get_button(void){
//для хранения кода кнопки
uint8_t temp = buttonNotPress;{
//проверяем результат обработки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если было нажатие кнопки, запоминаем его
if(button[num].result != buttonNotPress)
//вместо присвоения кода кнопки
//используем логическое умножение
temp |= button[num].result;
//сбрасываем результаты одработки
button[num].result = buttonNotPress;
button[num].pressingTime = 0;
}
if(temp & button_SB1_shortPress)
Serial.write("Button SB1 short pressed\r");
//--------------------------------------------------
//шаг измерения времени нажатия кнопки
#define TIME_STEP 10
//время длинного нажатия кнопки
#define BUTTON_LONG_PRESS_TIME 500
//время короткого нажатия кнопки
#define BUTTON_SHORT_PRESS_TIME 100
//--------------------------------------------------
//физическое состояние кнопки
enum ButtonResult {
buttonNotPress = 0, //если кнопка не нажата
button_SB1_shortPress = 1<<0, //код короткого нажатия кнопки SB1
button_SB1_longPress = 1<<1, //код длинного нажатия кнопки SB1
button_SB2_shortPress = 1<<2, //код короткого нажатия кнопки SB2
button_SB2_longPress = 1<<3, //код длинного нажатия кнопки SB2
button_SB3_shortPress = 1<<4, //код короткого нажатия кнопки SB3
button_SB3_longPress = 1<<5, //код длинного нажатия кнопки SB3
button_SB4_shortPress = 1<<6, //код короткого нажатия кнопки SB4
button_SB4_longPress = 1<<7, //код длинного нажатия кнопки SB4
buttonPress = 0xff //нажатие любой кнопки
};
//--------------------------------------------------
//перечисление имен кнопок
enum ButtonNames {
ButtonNamesStart = 0,
SB1 = ButtonNamesStart, SB2, SB3, SB4,
NumberOfButtons
};
//--------------------------------------------------
//для описания параметров кнопки
struct ButtonConfig {
//номер входа, к которому подключена кнопка
uint8_t pin;
//код кнопки при нажатии
enum ButtonResult shortPressIdentifier;
enum ButtonResult longPressIdentifier;
//для промежуточного хранения результата
enum ButtonResult result;
//для измерения длительности нажатия
uint16_t pressingTime;
};
//--------------------------------------------------
//массив для хранения параметров кнопок
struct ButtonConfig button[NumberOfButtons] = {
[SB1] = {.pin = 4, .shortPressIdentifier = button_SB1_shortPress, .longPressIdentifier = button_SB1_longPress},
[SB2] = {.pin = 5, .shortPressIdentifier = button_SB2_shortPress, .longPressIdentifier = button_SB2_longPress},
[SB3] = {.pin = 3, .shortPressIdentifier = button_SB3_shortPress, .longPressIdentifier = button_SB3_longPress},
[SB4] = {.pin = 2, .shortPressIdentifier = button_SB4_shortPress, .longPressIdentifier = button_SB4_longPress}
};
//--------------------------------------------------
//обработка текущего состояния кнопок
enum ButtonResult checkingButtonStatus(void){
//проверка, была ли нажата хоть одна кнопка
enum ButtonResult check = buttonNotPress;
//последовательно обрабатываем все кнопки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если кнопка нажата,
if(digitalRead(button[num].pin) == LOW){
//увеличиваем счетчик времени ее нажатия
button[num].pressingTime += TIME_STEP;
//чтобы счетчик не переполнился, если кнопка залипнет
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].pressingTime = BUTTON_LONG_PRESS_TIME;
//запоминаем, что кнопка нажималась
check = buttonPress;
}
//если не нажата, проверяем измеренное время
else{
//проверяем на длинное нажатие
if(button[num].pressingTime >= BUTTON_LONG_PRESS_TIME)
button[num].result = button[num].longPressIdentifier;
//проверяем короткое нажатие
else if(button[num].pressingTime >= BUTTON_SHORT_PRESS_TIME)
button[num].result = button[num].shortPressIdentifier;
//сбрасываем время
button[num].pressingTime = 0;
}
}
//сообщаем, была ли хоть одна кнопка нажата
return check;
}
//обработка нажатия кнопок
uint8_t get_button(void){
//для хранения кода кнопки
uint8_t temp = buttonNotPress;
//пока хоть одна кнопка нажата, измеряем время
while(checkingButtonStatus() == buttonPress){
delay(TIME_STEP);
}
//проверяем результат обработки
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
//если было нажатие кнопки, запоминаем его
if(button[num].result != buttonNotPress)
//вместо присвоения кода кнопки
//используем логическое умножение
temp |= button[num].result;
//сбрасываем результаты одработки
button[num].result = buttonNotPress;
button[num].pressingTime = 0;
}
//возвращаем код нажатия
return temp;
}
//--------------------------------------------------
//настройка периферии микроконтроллера
void setup() {
//настраиваем порты для обработки кнопок
//вход с подтяжкой к плюсу питания
for(uint8_t num = ButtonNamesStart; num < NumberOfButtons; ++num){
pinMode(button[num].pin, INPUT_PULLUP);
}
//настройки для передачи данных в терминал
Serial.begin(9600);
Serial.write("Button test\r");
}
//--------------------------------------------------
//супер цикл
void loop() {
//обработка нажатия кнопки
uint8_t temp = get_button();
if(temp & button_SB1_shortPress)
Serial.write("Button SB1 short pressed\r");
if(temp & button_SB2_shortPress)
Serial.write("Button SB2 short pressed\r");
if(temp & button_SB3_shortPress)
Serial.write("Button SB3 short pressed\r");
if(temp & button_SB4_shortPress)
Serial.write("Button SB4 short pressed\r");
if(temp & button_SB1_longPress)
Serial.write("Button SB1 long pressed\r");
if(temp & button_SB2_longPress)
Serial.write("Button SB2 long pressed\r");
if(temp & button_SB3_longPress)
Serial.write("Button SB3 long pressed\r");
if(temp & button_SB4_longPress)
Serial.write("Button SB4 long pressed\r");
}
Очень подробно и с диаграммами — круто.
Но — блокирующий опрос кнопок — это очень плохо, особенно, если проект сложный.
Вообще все блокирующее в микроконтроллерах — это всегда плохо, особенно, если их быстродействие не велико.
Гораздо правильней делать это с помощью чего-то похожего на конечный автомат.
И так и делают — одна из самых популярных (но не самых крутых) — это библиотека по работе с кнопками от AlexGyver.
Суть в том, что есть структура (или класс) кнопки, где хранится ее состояние, когда было это по времени (значение таймера отсчета), предыдущее состояние и тд.
И на основе этих данных можно и дабл- и трипл-клики обрабатывать, и удержание и клик+удержание и много чего еще, причем с помощью очень небольших изменений кода.
И для того, чтобы обновить состояние — нужно просто вызвать некую фю, передав туда отсчет времени. И делать это можно где-угодно — хочешь — в таймере, хочешь — по прерыванию, хочешь — в основном цикле. Лишь бы паузы между вызовами были не слишком большие — иначе реакция будет подтормаживать. А так — один раз тайминги настроил и забыл.
Это удобней и в разы универсальней.
При этом таких кнопок сколько угодно можно породить (хоть динамически), а в функции(ях) опроса можно эти кнопки либо последовательно перебирать, либо сразу все, либо по одной на вызов фии, либо вообще сделать одну фю на все кнопки. Все это отлично ложиться на классы в C++, да и вообще на ООП.
И самое главное — опрос не мешает выполнятся остальным частям кода.
Не используйте блокирующий код, пока вы четко не понимаете, что он действительно необходим. И это относится не только к кнопкам или микроконтроллерам.
Я в статье написал о том, что блокирующие функции - это не очень хорошо. И регулярно по тексту делал на этом акцент. Но, не разобравшись с блокирующими функциями, сложно идти дальше. Я обязательно напишу подобный гайд про неблокирующие способы обработки. Но, если бы я стал писать все разом, либо текст вышел бы очень поверхностный (а такого хватает в интернете), либо текст был бы очень длинный. Эту статью за один раз сложновато будет переварить, мне ее самому вычитывать даже было сложно. Поэтому я решил притормозить, и ограничиться в этот раз блокирующей обработкой.
По классам для обработки кнопок. Само по себе применение классов в ардуино меня даже немного забавляет. По сути основная часть пишет процедурные программы, в которых вкарячивается класс, ну пусть для работы с кнопкой. Зачем? Если пишешь процедурный код, пусть уже и обработка кнопок будет аналогичным образом реализована.
Раньше C++ не использовался ввиду того, что давал оверхэд при компиляции для микропррцессоров, сейчас компиляторы его практически не дают - разница может составлять совсем не много около - 0.2-0.5%
И раньше я также писал только на C из-за этого, сейчас куда правильней писать на C++ и иметь все его плюшки - программы получаются проще и гораздо более гибкими.
Но понятно, что при использовании C++, надо и метрологию его использовать, а не процедуры + вкоряченный класс)
При этом может получиться очень интересная штука - программа, написанная на C++ с использованием C++ особенностей получится меньшей по размеру и более быстрой, чем делующая тоже на C. На небольших программах это практически не видно, а вот с увеличением размера начинает ощущаться.
Деформация и зашоренность сознания сильно ограничивают. К примру, я сам не могу себя заставить на ООП писать под МК. У меня выработались практически "стандартные" для меня подходы, которые позволяют достаточно просто писать код. Я и сам уже анализировал результаты. К примеру, смотрел, как оптимизатор распределяет память для структур, очень не дурно, потери памяти действительно минимальные.
А что про начинающих говорить? Тут надо с базовым синтаксисом Си совладать, а потом еще ++ изучать. И дополнительно подходы осваивать. Сложно так сходу. Вот многие и останавливаются на Си.
Ну, надо начать просто - чем больше, тем лучше будет.
Например, появляется возможность использовать классы как скоп, чтобы не гадить в глобальную область видимости. (можно статический, если не предвидится несколько таких).
Использовать объекты, когда есть что-то повторяющиеся в системе (кнопки, например).
Использовать ссылки, вместо передачи по значению.
Ну и возможность выносить класс или набор классов, как отдельный файл с отдельным функционалом - это прямо киллерфича - глобал не загрязняется, а порождая тип, ну например OLED display( PORTA, PIN1) сразу понятно что куда почему + инициализация, если нужна.
Это вот прямо самое базовое.
Я когда-то оконные приложения начинал конструировать на WinAPI, меня массивы структур вполне устраивают.
Когда-то давно вышел пакет WinAVR для микроконтроллеров на основе GCC. Я, на радостях, в мейк файле ткнул ключ G++, и оно тогда заработало. Но вот в отладке оно мне совсем не понравилось. С тех пор как-то скептически относился к классам для микроконтроллеров.
Хотя понимаю, что для ARM-микроконтроллеров уже самое то, перейти на ООП и использовать RTOS. Но пока задач подходящего масштаба не возникало.
О "правильности"... а что считать правильно или не правильно? Есть задача, и есть подходящее для этой задачи решение. Зачем все подряд упаковывать в конечные автоматы?!
О правильности я в конце первого комментария написал - нужно всеми силами избегать блокирующего кода везде, где только можно (не только в микропроцессорах) , и использовать его только тогда, когда это прямо реально нужно и кейсов таких от силы пол-процента наберется.
Задачу можно решить быстро и в лоб, а можно решить хорошо. Блокирующий - это всегда быстро и в лоб, что потом чревато проблемами и переделками.
Потому что это более универсальней и покрывает весь спектр задач, это можно сделать один раз, а потом лишь переиспльзовать будучи на 99% уверенным, что код этот может всегда расширить на бОльшее колво случаев и работать он будет предсказуемо и четко. При этом самого кода при таком подходе будет не сильно больше, нежели у решения в лоб.
Если что-то делаешь для хобби, то конечно можно развернуться по полной. Но если проект за деньги, то пиши на эти деньги. Я так рассуждаю. Если оплачено впритык, еще и исходники просят, зачем изгаляться? Нет, конечно можно либо даром написать хороший код, либо отказаться работать за гроши.
Вот и так считаю - куда проще заинклюдить проверенный файл и вообще больше на это время не тратить.
Переиспользование универсального кода как раз и даёт возможность снизить стоимость разработки.
Я с таким проектом сталкивался. Считыватель допиливал лет десять назад под аттракционы. Там ребята вложились, спасибо им, а я чуть пропатчил их проект и тоже заработал.
Обратитесь к трудам Анатолия Шалыто. Даже он сам рекомендует использовать конечный автомат там, где это действительно необходимо. Для остального есть switch подходы, можно и с флагами писать, и, в конце концов, писать линейные алгоритмы.
Я точно старше 21 года, поэтому "бесконечный цикл" с исходящей из него стрелкой меня несколько удивляет.
Вас это удивляет, или это затруднительно для понимания?
Обратитесь к ГОСТ 19.701-90(ИСО 5807-85), там еще и не такое можно найти. В частности "3.2.2.6. Граница цикла". Мы просто привыкли из курса школьной информатики использовать крайне ограниченное количество символов для изображения алгоритмов.
Нубский вопрос. А можно ли сделать железную обвязку кнопки, чтобы разгрузить нешустрый мозг МК или это нерациональный способ?
Хороший вопрос, я ждал его! Наберитесь терпения. Я уже готовлю статью, где будет такое решение. Можно будет сделать выводы.
Можно, но везде есть своя цена. Проще всего решать дребезг программно.
Обычный антидребезг, схем много - простейшая кондер 100nf параллельно кнопке.
Можно ещё поставить небольшой резистор от кнопки к кондеру, дабы увеличить её срок службы
С конденсатором - это уже будет простейший RC-фильтр, временная постоянная которого равна R*C. Зная интервал опроса кнопки t, можно вычислить примерную требуемую ёмкость конденсатора: C = t / R.
Есть даже специализированная микросхема MC14490
Я в комментарии к предыдущей статье пример неблокирующей обработки дребезга приводил.
Когда кнопок больше 2 и есть лишний аналоговый вход, то проще посадить кнопки все кнопки на одну линию через резисторы, особенно если кнопки вынесены от платы с МК.
Это хорошо в том случае, если кнопки нажимаются не сильно часто. Иначе нередка ситуация, когда кнопки начинают обрабатываться неверно (сопротивление полудохлых кнопок увеличивается), что часто бывало в тех же древних ящиках, типа рубина
Очень и очень хорошо!
https://arduinomaster.ru/datchiki-arduino/ustranenie-drebezg-kontaktov-knopki/
смотрим "Аппаратный способ подавления дребезга кнопки", всё чем вы занимаетесь это дурота от незнания азов электроники
Вы не могли бы подробнее озвучить Ваше мнение?
ставим 1 конденсатор и решаем проблему дребезга контактов, стандартное решение которому 100 лет. можно даже через один все кнопки пустить. всё остальное скорее велосипеды с рисками по багам или поеданию ресурсов (может конечно и мизерного). если очень хочется то можно всё, но вопрос зачем когда решение это одна деталька на плате?
А Вы точно читали мою статью? Она разве о дребезге контактов?
Если бы Вы внимательно прочитали текст, было бы понятно, что статья о программировании кнопки, а не про конденсаторы.
Конденсатор все могут поставить... А без конденсатора слабо?
Триггер — Шмитта.
Для идеалистов - RS-триггер!
Когда-то начинал в школьные годы с TTL - навесной монтаж, выносная клавиатура на жгуте из МГТФ - работало нормально только с таким решением на микриках :)
Я на 555 одновибратор использовал
К слову, входы Arduino/ATmega имеют встроенные триггеры Шмитта.
Аппаратное подавление хорошо, но как показал опыт разработки промэлектроники работающей в тяжелых условиях, оно не спасает полностью. Работает пока постоянная времени RC цепочки (элементарного НЧ фильтра, интегратора) больше чем период дребезга у кнопки. Но потом со временем какой-то один из контактов либо повышает сопротивление, либо замерзает, либо загрязняется, либо окисляется, либо намагничивается, либо теряет упругость (даже если контакт - геркон с пятой приёмкой), либо на крыше включается радиостанция и начинает просачивается помеха, либо нажимающий подносит к контактам 50Hz сети. В итоге на вход при том что есть подобранная RC цепочка и ТШ начинает просачиваться сигнал быстрых срабатываний. Контакт вещь такая, не предугадаешь его дребезг, сюрпризы он выкинет обязательно. А уж нажимающего его человека - тем более. Поэтому там где возможно, желательно предусмотреть еще и программную обработку дребезга.
Хорошее замечание. Еще, как более частый пример, могу привести энкодеры в автомагнитолах. Там обычно используют RС-цепочки от дребезга. И сколько таких магнитол, у которых со временем энкодер начинает подглючивать?
Библиотека Keypad позволяет определять нажатие множества кнопок, подключённых матрицей, через порты столбцов и порты строк, на одни подаётся напряжение, с других считывается нажатие кнопок, если добавить к каждой кнопке диод, то можно определять нажатия и отпускания всех нажатых одновременно кнопок, без эффекта фантомного нажатия. Есть настраиваемая задержка антидребезга, и длительность долгого нажатия кнопки, если требуется это определять, поддерживается сканирование нескольких матриц.
Мегокрутая статья. Даже прошел регистрацию на Хабре, чтобы выразить своё восхищение. Всё крайне подробно. Я не начинающий, но много нового узнал. Теперь жду статью с неблокирующим опросом кнопок. А вообще искал как работать с кнопками через регистры. Нужно ужать код максимально. Ещё раз спасибо)
Спасибо за комментарий! Следующая статья к концу недели будет.
Картинку поправьте, у вас там SB5 из воздуха появился :)