Урок 1.4. Подсчет количества нажатий кнопки. Кликер

Базовый курс "Программирование микроконтроллеров"
Модуль 1. Введение в Arduino. Работа с цифровым и аналоговым сигналом

Сборка и проверка схемы

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

Соберём схему из предыдущего урока:

Напишем код из прошлого урока, для отслеживания нажатий кнопки:

// константы используемых пинов
const int button = 2;
const int led = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;

void setup() 
{
    pinMode(led,OUTPUT);
    pinMode(button,INPUT);
    Serial.begin(9600);
}

void loop() 
{
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопканажата то включаем светодиод, в противном случае выключаем
  if(state == 1)
  {
    digitalWrite(led,HIGH);
    Serial.println("ON");
  }
  else
  {
    digitalWrite(led,LOW);
    Serial.println("OFF");
  }
}

Загрузим код в Ардуино и убедимся что всё прекрасно работает и светодиод загорается при нажатии на кнопку. Вспомним, что наша задача сделать такое устройство, которое будет подсчитывать количество нажатий кнопки.

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

Подсчёт количества нажатий кнопки

Напишем следующий код:

// константы используемых пинов
const int button = 2;
const int led = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;
// переменная для хранения количесва нажатий по кнопке
int N = 0;

void setup() 
{
    pinMode(led,OUTPUT);
    pinMode(button,INPUT);
    Serial.begin(9600);
}

void loop() 
{
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопканажата то включаем светодиод, в противном случае выключаем
  if(state == 1)
  {
    digitalWrite(led,HIGH);
    N++;  // увеличение значения переменной
    Serial.println(N);
  }
  else
  {
    digitalWrite(led,LOW);
    Serial.println(N);
  }
}

Загрузим код в Ардуино и нажмём на кнопку. Открыв монитор порта,  увидим следующую картину:

При нажатии на кнопку мы увидим, что значение переменной расчёт всё время, пока мы удерживаем толкатель кнопки во включенном положении.
Дело в том, что микроконтроллер выполняя нашу программу довольно часто обращается к порту кнопки и регистрирует что она нажата, т.е. наша программа регистрирует что кнопка нажата и удерживается, а нам необходимо что бы значение переменной N увеличивалось только тогда, когда кнопка была нажата в не зависимости от того как долго она удерживается в нажатом положении.

Наша цель, это отследить момент перехода с низкого состояния (не нажато) к высокому (нажато).

Для этого нам необходимо создать переменную, которая будет хранить предыдущее состояние кнопки и сравнивать его с текущим, и если предыдущее состояние было “не нажато” а текущее “нажато” значит произошёл клик по кнопке.

Напишем следующий код:

// константы используемых пинов
const int button = 2;
const int led = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;
// переменная для хранения количества нажатий по кнопке
int N = 0;
// переменная для хранения предыдущего состояния кнопки
int last_state = 0;

void setup() 
{
    pinMode(led,OUTPUT);
    pinMode(button,INPUT);
    Serial.begin(9600);
}

void loop() 
{
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопка сейчас нажата, а в прошлую итерацию была не нажата то включаем светодиод, в противном случае выключаем
  if(state == 1 && last_state == 0)
  {
    digitalWrite(led,HIGH);
    N++;  // увеличение значения переменной
    Serial.println(N);
    delay(10); // задержка для светодиода
  }
  else
  {
    digitalWrite(led,LOW);
    Serial.println(N);
  }
  //перезапись состояния кнопки для следующей итерации
  last_state = state;
}

Загружаем код и проверяем, что всё прекрасно работает!

Игра "Кликер"

Сделаем игру, в которой мы будем нажимать на кнопку какое-то количество раз. Конец игры наступит тогда, когда мы достигнем определенного количества нажатий и сопровождать это всё будем световой индикацией и выводом в монитор порта.

Соберите следующую схему:

В данной схеме мы используем RGB-светодиод, его красную компоненту мы будем использовать для обозначения клика по кнопке, а синюю – как сигнал о конце игры.

Напишем следующий код:

// константы используемых пинов
const int button = 2;
const int led_red = 4;
const int led_blue = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;
// переменная для хранения количества нажатий по кнопке
int N = 0;
// переменная для хранения предыдущего состояния кнопки
int last_state = 0;

void setup() 
{
    pinMode(led_red,OUTPUT);
    pinMode(led_blue,OUTPUT);
    pinMode(button,INPUT);
    Serial.begin(9600);
}

void loop() 
{
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопка сейчас нажата, а в прошлую итерацию была не нажата то включаем светодиод, в противном случае выключаем
  if(state == 1 && last_state == 0)
  {
    digitalWrite(led_red,HIGH);
    N++;  // увеличение значения переменной
    Serial.println(N);
    delay(10); // задержка для светодиода
  }
  else
  {
    digitalWrite(led_red,LOW);
    Serial.println(N);
  }
  //перезапись состояния кнопки для следующей итерации
  last_state = state;
}

Загрузим этот код и убедимся, что подсчёт количества нажатий происходит корректно. Далее, сделаем так, что бы игра имела конец, обусловленный определенным количеством нажатий на кнопку. Для этого создадим константу для хранения нужного количества нажатий и с помощью условного оператора остановим игру.

// константы используемых пинов
const int button = 2;
const int led_red = 4;
const int led_blue = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;
// переменная для хранения количества нажатий по кнопке
int N = 0;
// переменная для хранения предыдущего состояния кнопки
int last_state = 0;
// константа для хранения конечного числа нажатий
const int N_finish = 100;

void setup() {
  pinMode(led_red, OUTPUT);
  pinMode(led_blue, OUTPUT);
  pinMode(button, INPUT);
  Serial.begin(9600);
}

void loop() {
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопка сейчас нажата, а в прошлую итерацию была не нажата то включаем светодиод, в противном случае выключаем
  if (state == 1 && last_state == 0) {
    digitalWrite(led_red, HIGH);
    N++;  // увеличение значения переменной
    Serial.println(N);
    delay(10);  // задержка для светодиода
  } else {
    digitalWrite(led_red, LOW);
    Serial.println(N);
  }
  //перезапись состояния кнопки для следующей итерации
  last_state = state;
  // если текущее количество нажатий больше или равно конечному то сигнализируем об этом и перезапускаем игру
  if (N >= N_finish) {
    digitalWrite(led_blue, HIGH);
    Serial.println("YOU WIN!");
    delay(5000);
    N = 0;
    digitalWrite(led_blue, LOW);
  }
}

Молодцы, но для интереса, добавим в нашу игру спортивный элемент – пусть помимо вывода сообщения о победе, так же будем выводить количество секунд, которые нам понадобились для достижения нужного количества кликов.

Уберем команду ожидания для мигания светодиодом и используем специальную функцию millis() – она очень простая в использовании и возвращает количество миллисекунд прошедших с момента запуска микроконтроллера.

// константы используемых пинов
const int button = 2;
const int led_red = 3;
const int led_blue = 3;
// переменная для хранения текущего состояния кнопки
int state = 0;
// переменная для хранения количества нажатий по кнопке
int N = 0;
// переменная для хранения предыдущего состояния кнопки
int last_state = 0;
// константа для хранения конечного числа нажатий
const int N_finish = 100;
// переменные для хранения времени
unsigned long t_start = 0;
unsigned long t = 0;

void setup() {
  pinMode(led_red, OUTPUT);
  pinMode(led_blue, OUTPUT);
  pinMode(button, INPUT);
  Serial.begin(9600);
  t_start = millis();
}

void loop() {
  // считываем состояние кнопки
  state = digitalRead(button);
  // если кнопка сейчас нажата, а в прошлую итерацию была не нажата то включаем светодиод, в противном случае выключаем
  if (state == 1 && last_state == 0) {
    digitalWrite(led_red, HIGH);
    N++;  // увеличение значения переменной
    Serial.println(N);
  } else {
    digitalWrite(led_red, LOW);
    Serial.println(N);
  }
  //перезапись состояния кнопки для следующей итерации
  last_state = state;
  // если текущее количество нажатий больше или равно конечному то сигнализируем об этом и перезапускаем игру
  if (N >= N_finish) {
    t = millis() - t_start;  // расчёт времени, прошедшего с момента запуска игры
    float sec = t / 1000.0;
    digitalWrite(led_blue, HIGH);
    Serial.println("YOU WIN! TIME = " + String(sec));
    delay(5000);
    N = 0;
    t_start = millis();
    digitalWrite(led_blue, LOW);
  }
}

Поздравляем, проект завершен!