Создание анимации и игр на LCD 16×2 с Arduino UNO
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino UNO | 1 |
| Символьный LCD 16×2 (HD44780-совместимый) | 1 |
| Потенциометр 10 кОм (для контраста) | 1 |
| Резистор 220 Ом (подсветка) | 1 |
| Макетная плата и перемычки | 1 комплект |
Приложения и сервисы
- Arduino IDE
О проекте
Проект посвящен основам анимации на символьном LCD 16×2 с Arduino. Принципы применимы к дисплеям других размеров.
Подключение LCD
Схема подключения (4-битный режим):
| Вывод LCD | Назначение | Подключение |
|---|---|---|
| 1 (VSS) | Земля | Отрицательная шина макетной платы (GND Arduino) |
| 2 (VDD) | Питание +5V | Положительная шина (5V Arduino) |
| 3 (VO) | Контраст | Средний вывод потенциометра (крайние — на +5V и GND) |
| 4 (RS) | Выбор регистра | Arduino pin 12 |
| 5 (R/W) | Чтение/запись | Земля (режим записи) |
| 6 (E) | Строб | Arduino pin 11 |
| 11 (D4) | Данные 4 | Arduino pin 4 |
| 12 (D5) | Данные 5 | Arduino pin 5 |
| 13 (D6) | Данные 6 | Arduino pin 6 |
| 14 (D7) | Данные 7 | Arduino pin 7 |
| 15 (A) | Анод подсветки | +5V через резистор 220 Ом |
| 16 (K) | Катод подсветки | Земля |
Тест: включите питание. Если нижняя строка загорелась — LCD работает. Если нет — проверьте пайку, проводку, замените дисплей.
После успешного теста подключите линии данных D4–D7 (пины 4–7 Arduino) и загрузите тестовый скетч:
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 4, 5, 6, 7); // rs, e, d4, d5, d6, d7
void setup() {
lcd.begin(16, 2);
lcd.setCursor(0, 1);
lcd.print("Working");
}
void loop() {}
Если выводится «Working» — подключение верное. Если мусор или пусто — проверьте контраст (поверните потенциометр), пайку данных (нет ли коротких замыканий), соответствие пинов в коде схеме.
Разбор тестового кода
LiquidCrystal lcd(12,11,4,5,6,7);— создание объекта lcd с указанием пинов интерфейса.lcd.begin(16,2);— инициализация экрана 16 столбцов, 2 строки (подстройте под свой дисплей).lcd.setCursor(0,1);— установка курсора: столбец 0, строка 1 (нумерация с 0).lcd.print("Welcome");— вывод текста в позицию курсора.
Бегущая строка
Вывод текста, прокручивающегося справа налево:
void setup() {
lcd.begin(16,2);
}
void loop() {
int n = 15;
char message[] = "scrolling text!";
char* ptr = message;
while(n != -14) {
lcd.clear();
lcd.setCursor(n, 1);
if(n < 0) ptr++;
lcd.print(ptr);
n--;
delay(250);
}
}
Указатель ptr сдвигается при выходе за левый край, чтобы текст продолжал прокручиваться. delay(250) задает скорость — меняйте под нужды.
Пользовательские символы
Библиотека LiquidCrystal позволяет создавать до 8 своих символов (5×8 пикселей). Каждый символ — массив из 8 байт, где 1 = пиксель включен, 0 = выключен.
byte dino[] = {
B01110,
B01110,
B01100,
B01111,
B11101,
B01010,
B01010,
B01010
};
Создание и вывод:
void setup() {
lcd.createChar(0, dino); // символ 0 = dino
lcd.begin(16,2);
lcd.setCursor(0,1);
lcd.write(byte(0)); // вывод по адресу 0
}
Создавать символы удобно в онлайн-редакторе: LCD Character Creator.
Анимация «Летающая стрела»
Используем три пользовательских символа: наконечник, тело, хвост.
byte arrowhead[] = { B00001, B00011, B00111, B01111, B00111, B00011, B00001, B00000 };
byte arrowbody[] = { B00000, B00000, B00000, B11111, B11111, B00000, B00000, B00000 };
byte arrowtail[] = { B00011, B00111, B01111, B11111, B11111, B00111, B00011, B00000 };
void setup() {
lcd.createChar(0, arrowhead);
lcd.createChar(1, arrowbody);
lcd.createChar(2, arrowtail);
lcd.begin(16,2);
}
void loop() {
int n = 15;
while(n != -1) {
lcd.clear();
lcd.setCursor(0,1);
lcd.write(byte(0));
lcd.write(byte(1));
lcd.write(byte(2));
n--;
delay(50);
}
}
Можно заметить «хвост» от предыдущего кадра — это артефакт медленного обновления жидких кристаллов, не ошибка.
Игра «Уворачивайся от стрел»
Суть: герой (человечек) стоит на нижней строке (строка 1). Стрелы летят справа налево по той же строке. Нажатие кнопки (pin 2, прерывание) перемещает героя на верхнюю строку (строка 0). Попадание — конец игры. Очки считаются каждый цикл.
Ключевые элементы кода:
- Символ героя
man[](5×8). attachInterrupt(0, buttonin, CHANGE);— прерывание по изменению на pin 2 (INT0). Функцияbuttonin()читает кнопку и меняет переменнуюlevel(0 или 1).randomSeed(analogRead(A0));— истинно случайный seed от шума на не подключенном аналоговом входе.- В цикле
while(n!=-1)стрела движется, проверяется столкновение приn==1(позиция героя). drawman()рисует героя в текущей строкеlevel.stopgame()выводит «Game over» и счет, сбрасывает переменные, ждет 3 сек.
Полный код игры приведен в разделе Code. Это база для более сложных игр — добавляйте кнопки, уровни, врагов!
Код
Код анимации (стрела)
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
byte arrowhead[] = { B00001, B00011, B00111, B01111, B00111, B00011, B00001, B00000 };
byte arrowbody[] = { B00000, B00000, B00000, B11111, B11111, B00000, B00000, B00000 };
byte arrowtail[] = { B00011, B00111, B01111, B11111, B11111, B00111, B00011, B00000 };
byte man[] = { B01110, B01110, B00100, B01110, B10101, B00100, B01010, B10001 };
void setup() {
lcd.createChar(0, arrowhead);
lcd.createChar(1, arrowbody);
lcd.createChar(2, arrowtail);
lcd.createChar(3, man);
lcd.begin(16, 2);
}
void loop() {
int n = 15;
while(n != -1) {
lcd.clear();
delay(10);
lcd.setCursor(0,0);
lcd.print("by Debanshu Das");
delay(65);
n--;
}
}
Код игры
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int level = 1;
int score = 0;
byte arrowhead[] = { B00001, B00011, B00111, B01111, B00111, B00011, B00001, B00000 };
byte arrowbody[] = { B00000, B00000, B00000, B11111, B11111, B00000, B00000, B00000 };
byte arrowtail[] = { B00011, B00111, B01111, B11111, B11111, B00111, B00011, B00000 };
byte man[] = { B01110, B01110, B00100, B01110, B10101, B00100, B01010, B10001 };
void setup() {
lcd.createChar(0, arrowhead);
lcd.createChar(1, arrowbody);
lcd.createChar(2, arrowtail);
lcd.createChar(3, man);
lcd.begin(16, 2);
attachInterrupt(0, buttonin, CHANGE);
randomSeed(analogRead(A0));
}
int n;
void loop() {
n = 15;
int rnd = random(15, 25);
while(n != -1) {
lcd.clear();
delay(10);
drawman();
lcd.setCursor(n, 1);
if(n == 1) {
if(level == 1) { stopgame(); continue; }
}
lcd.write(byte(0));
lcd.write(byte(1));
lcd.write(byte(2));
lcd.setCursor(10, 0);
lcd.print(score);
delay(100 - rnd);
n--;
score++;
if(level == 0) score--;
}
}
void drawman() {
lcd.setCursor(1, level);
lcd.write(byte(3));
}
void buttonin() {
if(digitalRead(2) == LOW) level = 0;
else level = 1;
}
void stopgame() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Game over");
lcd.setCursor(10, 0);
lcd.print(score);
level = 1;
score = 0;
n = 15;
delay(3000);
}
Схема

Производственный процесс
- Arduino-датчики: типы, применения и реальные проекты
- Сборка портативной игровой консоли-клона Arduboy на Arduino Micro с проектированием PCB
- Умный вольтметр на Arduino и смартфоне — простое DIY-проект
- Логгер температуры и влажности на Arduino с DHT11 и Ethernet Shield
- Управление Arduino Uno из Python 3: команды через последовательный порт и демонстрация с LED
- Клеточный автомат «Жизнь» Конвея на Arduino Nano с OLED 128x64
- Сборка компактного FM-радио на Arduino Nano и RDA5807M
- Надежная погодная станция на Arduino с датчиками DHT
- Создание классической аркадной игры на Arduino с LCD, зуммером и джойстиком
- MKR1000 + DHT22: мониторинг температуры и влажности в Azure в реальном времени