Джойстик на arduino ky-023

Подключение двух осевого джойстика KY-023 к Arduino. Пишем простую игру

Сегодня разберёмся как работает простое, но тем не менее интересное устройство, которое можно применить во множестве систем и поделок. Джойстик (прямо как в playstation) – состоит из двух переменных резисторов, которые изменяют своё сопротивление по оси X и оси Y. Из этих двух параметров можно легко сделать, например курсор навигации по меню, или использовать игровую механику. Взглянув на принципиальную схему, можно легко понять логику работы.

ky-023 схема устройство джойстика

Исходя из этой схемы очень легко подключать джойстик к любому микроконтроллеру. Обратите внимание, что выводы на разных джойстиках могут быть размещены по разному. Нужно будет подать питание на джойстик, аналоговые оси соединить с АЦП, а кнопку можно вывести на любой дискретный вход.

Подключение джойстика ky-23 к arduino

В программе для удобства определяем аналоговые входы для осей X и Y.

#define axis_X A1    // Ось Х подключена к Analog 0
#define axis_Y A2    // Ось Y подключена к Analog 1

Затем определим переменные для хранения результатов считывания АЦП для осей (int val_X, val_Y;).

int val_X, val_Y;    // Переменные для хранения значений осей

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

Serial.print("X:");
Serial.println(val_X, DEC);      // Выводим значение в Serial Monitor X
Serial.print(" | Y:");
Serial.println(val_Y, DEC);      // Выводим значение в Serial Monitor Y

Запустим программу и посмотрим результаты работы джойстика. В состоянии покоя результаты Х и Y равны около 450, с отклонениями до 900 и 0 при работы джойстика.

джойстик ky-023 arduino координаты

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

unsigned int x;
unsigned int y;

Экран я взял из прошлой статьи, разрешением 320х240, а границы считывания джойстика составляют 900, поэтому можно смело поделить результат считывания на 3. Получается в состоянии покоя 450/3 = 150px. Если экран расположить вертикально, то середина будет 320/2=160px. Почти подходит, можно как раз сделать полоску статуса сверху. А вот для горизонтали нужно значение 120px, поэтому вычтем 30px от результата. Вообще в итоге я сделал так в функции считывания показателей джойстика.

void read_joy()
{  
  val_X = analogRead(axis_X);    // Считываем аналоговое значение оси Х
  val_Y = analogRead(axis_Y);    // Считываем аналоговое значение оси Y
  x = (val_X / 3)- 30;
  y = (val_Y / 3)+ 10;
}

Всё хорошо, но сами координаты нужно чем то отобразить. Я не придумал ничего лучше, как сделать это кружочками из библиотеки adafruit . Четыре с разными интервалами смотрелись лучше всего.

void cursor_aim (void)
{
  tft.drawCircle(x, y, 3, ILI9341_RED);
  tft.drawCircle(x, y, 4, ILI9341_RED);
  tft.drawCircle(x, y, 7, ILI9341_RED);
  tft.drawCircle(x, y, 8, ILI9341_RED);
}

Сразу после отрисовки я немного жду, и стираю результат, перед считыванием следующих показаний. Так на экране не образуется шлейфа, и курсор всегда будет отрисован один раз на новых координатах.

void cursor_aim (void)
{
  tft.drawCircle(x, y, 3, ILI9341_RED);
  tft.drawCircle(x, y, 4, ILI9341_RED);
  tft.drawCircle(x, y, 7, ILI9341_RED);
  tft.drawCircle(x, y, 8, ILI9341_RED);
}

Теперь сверху сделаем строку состояния.

  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_RED);    tft.setTextSize(2);
  tft.print("SCORE:::");

Ну и чтобы прицел не стирал эту строку, ограничим координаты перед отрисовкой.

  if (y<30)
  {
    y=30;
  }

Должна получится такая картина.

Смотрите также:  Подключение дисплея ili9341 к ESP8266. Вывод JPG картинки
игра на ардуино с дисплеем

Прицел двигается, всё хорошо. Теперь продумаем появление врага. Ему тоже нужны будут координаты (случайные), Для этого в setup добавим следующую команду, которая возьмёт со свободного аналогового пина A0 случайное значение (контакт в воздухе).

randomSeed(analogRead(0));

Теперь сделаем функцию случайных координат, которую будем вызывать на старте, и после того, как нашего монстра убили.

void random_enemy (void) {
  x_enemy = random(2, 210);
  y_enemy = random(30, 290);
}

Создадим переменную для хранения флага нового врага.

unsigned int flag_enemy = 1;

При первом запуске она будет равна 1, поэтому отработает следующая функция. Также при попадании в монстра, флаг снова будет обновляться (это сделаем далее).

void new_enemy (void)
{
  if (flag_enemy == 1)
  {
    random_enemy();
    flag_enemy = 0;
  }
}

Теперь нарисуем монстра. Я сделал его из 4 прямоугольников зелёного цвета. Монстра нужно отрисовывать каждый кадр в loop(), потому-что курсор может стереть его.

void monster (void) {
  tft.fillRect(x_enemy, y_enemy, 30, 15, ILI9341_GREEN);
  tft.fillRect(x_enemy, (y_enemy - 4), 7, 4, ILI9341_GREEN);
  tft.fillRect((x_enemy + 23), (y_enemy - 4), 7, 4, ILI9341_GREEN);
  tft.fillRect((x_enemy + 5), (y_enemy + 15), 20, 5, ILI9341_GREEN);
}

Логика такая – монстр рисуется каждый раз, но вот координаты обновляются только при его смерти. Смерть наступает при обновлении флага new_enemy, а значит нужно придумать условие. Так как курсор и монстр имеют координаты, можно придумать уравнение. Проблема, что курсор рисуется из центра, а прямоугольники нет, но немного применив математику я быстро придумал решение из 4 формул. Также пятым условием я добавил нажатие на кнопку. Также здесь мы будем обновлять счётчик очков (добавлена переменная count2, и сбрасывать флаг монстра flag_enemy, чтобы отрисовать нового, по случайным координатам.

void strike (void) {
  if (digitalRead(button)==0 && x_enemy + 30 > x && x_enemy < x && y_enemy + 20 > y && y_enemy < y)
  {
    count2++;
    tft.fillRect(x_enemy, y_enemy, 30, 15, ILI9341_BLACK);
    tft.fillRect(x_enemy, (y_enemy - 4), 7, 4, ILI9341_BLACK);
    tft.fillRect((x_enemy + 23), (y_enemy - 4), 7, 4, ILI9341_BLACK);
    tft.fillRect((x_enemy + 5), (y_enemy + 15), 20, 5, ILI9341_BLACK);
    flag_enemy = 1;
  }
}

Только не забудьте объявить кнопку с джойстика, у меня она подключена на D2.

#define button 2     // Кнопка подключена к D2

Смотрим на результат.

Смотрите также:  led 5x5x5
игра на ардуино с дисплеем как написать

Не хватает конечно счётчика. Мы увеличиваем переменную count2, а сравнивать будем её с count, и если она поменялась, то меняем значение на экране и также её инкрементируем, для дальнейшего сравнения.

void scores (void)
{
  if (count != count2) {
    count++;
    tft.fillRect(200, 0, 50, 25, ILI9341_BLACK);
    tft.setTextColor(ILI9341_RED);    tft.setTextSize(2);
    tft.setCursor(200, 0);
    tft.println(count);
  }
}

Не забываем объявить эти переменные

unsigned int count;
unsigned int count2;

Теперь проверяем наш файл loop(). Я добавил небольшую задержку, для более красивого свечения курсора.

void loop(void) {
  new_enemy();
  scores();
  clear_aim();
  read_joy();
  cursor_aim();
  monster();
  strike();
  delay(30);
}
игра на ардуино с прицелом

Всё работает. Можно конечно улучшить механику, сделать чтобы монстры менялись, если не убиты в течении времени, но цель была не в этом, думаю мы отлично справились с с тестированием джойстика ky-023. Видео игры

Полный код игры можно взять на гитхаб.

1 комментарий к “Подключение двух осевого джойстика KY-023 к Arduino. Пишем простую игру”

  1. Данил

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

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *