Подключение LCD1602 и клавиатуры 4×4 к ESP32C3 (LuatOS)

    Пример подключения дисплея LCD1602 по I2C интерфейсу и клавиатуры 4×4 через матрицу контактов к контроллеру ESP32C3 на плате LuatOS.

Внешний вид макета:

ESP32C3

LCD1602 подключен по интерфейсу I2C через PCF8574 I2C.

Клавиатура: контакты 1, 2, 3, 4 – столбцы; контакты 5, 6, 7, 8 – строки. Функционал клавиш: цифровые – печать значения; A – cтереть экран; B – переход с одной строки на другую; C – заполнение всех знакомест экрана; D – демонстрация бегущей строки (нажатие любой клавиши включает свой функционал); * – сдвиг курсора влево; # – сдвиг курсора вправо.

Кнопки RST и BOOT введены для удобства (они больше, чем на самом ESP32C3, к тому же у меня модуль установлен маркировкой контактов вверх).

ESP32C3

Назначение контактов ESP32C3 LuatOS

Таблица соединений

БПКнопкиКлавиатураLCD1602ESP32C3
+5V+5V
GNDGND
RSTEN
BOOTIO09
GNDGND
1IO02
2IO03
3IO10
4IO06
5IO12
6IO18
7IO19
8IO013
GNDGND
VCC+5V
SDAIO04
SCLIO05

В Arduino IDE выбрал ESP32C3 Dev Module. Для правильной прошивки в “Инструменты” нужно установить опцию Flash Mode в DIO.

Скачать Arduino IDE 2.3.8

arduino-ide_2.3.8_Windows_64bit.exe (2 Загрузки)

Скетч:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// НАСТРОЙКИ ПИНОВ ДЛЯ ESP32-C3 
#define I2C_SDA 4 
#define I2C_SCL 5

// Клавиатура 4x4
const byte ROW_PINS[4] = {12, 18, 19, 13};   // GPIO для строк
const byte COL_PINS[4] = {2, 3, 10, 6};      // GPIO для столбцов

// Символы на клавиатуре в соответствии с матрицей
char keys[4][4] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
LiquidCrystal_I2C lcd(0x27, 16, 2);  // Адрес I2C = 0x27

int cursorX = 0;      // текущая позиция курсора (0..15)
int cursorY = 0;      // текущая строка (0 или 1)

// Флаги для бегущей строки
bool marqueeActive = false;
unsigned long lastMarqueeTime = 0;
int marqueeOffset = 0;
String marqueeLine1 = "Hotel California        ";  // 16 символов + пробелы
String marqueeLine2 = "   Eagles 1976          ";  // 16 символов + пробелы

// ИНИЦИАЛИЗАЦИЯ КЛАВИАТУРЫ
void initKeypad() {
  for (int i = 0; i < 4; i++) {
    pinMode(ROW_PINS[i], OUTPUT);
    digitalWrite(ROW_PINS[i], HIGH); // по умолчанию высокий уровень
  }
  for (int i = 0; i < 4; i++) {
    pinMode(COL_PINS[i], INPUT_PULLUP);
  }
}

// Опрос клавиатуры (без задержек, неблокирующий)
char getKey() {
  for (int r = 0; r < 4; r++) {
    // активируем текущую строку (низкий уровень)
    digitalWrite(ROW_PINS[r], LOW);
    
    for (int c = 0; c < 4; c++) {
      if (digitalRead(COL_PINS[c]) == LOW) {
        // небольшая задержка на антидребезг
        delay(50);
        while (digitalRead(COL_PINS[c]) == LOW) { } // ждём отпускания
        digitalWrite(ROW_PINS[r], HIGH); // деактивируем строку
        return keys[r][c];
      }
    }
    digitalWrite(ROW_PINS[r], HIGH); // деактивируем строку
  }
  return 0; // ничего не нажато
}

// ФУНКЦИИ УПРАВЛЕНИЯ КУРСОРОМ И ЭКРАНОМ
void showCursor() {
  lcd.setCursor(cursorX, cursorY);
  lcd.cursor();      // включаем видимость курсора
  lcd.blink();       // и мигание
}

void moveCursorLeft() {
  if (cursorX > 0) {
    cursorX--;
  } else if (cursorY == 1) {
    // если в начале второй строки, переходим в конец первой
    cursorX = 15;
    cursorY = 0;
  } else if (cursorY == 0 && cursorX == 0) {
    // уже в начале первой строки — не двигаемся
    return;
  }
  lcd.setCursor(cursorX, cursorY);
  showCursor();
}

void moveCursorRight() {
  if (cursorX < 15) {
    cursorX++;
  } else if (cursorY == 0) {
    cursorX = 0;
    cursorY = 1;
  } else if (cursorY == 1 && cursorX == 15) {
    // в конце второй строки — не двигаемся
    return;
  }
  lcd.setCursor(cursorX, cursorY);
  showCursor();
}

void toggleLine() {
  // Перемещение курсора между строками в первую позицию
  if (cursorY == 0) {
    cursorY = 1;
    cursorX = 0;
  } else {
    cursorY = 0;
    cursorX = 0;
  }
  lcd.setCursor(cursorX, cursorY);
  showCursor();
}

void clearScreen() {
  lcd.clear();
  // после очистки курсор автоматически становится на (0,0)
  cursorX = 0;
  cursorY = 0;
  showCursor();
}

void fillAlphabet() {
  // Заполнение 32 символами: AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPP
  char alphabet[33] = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPP";
  lcd.clear();
  lcd.setCursor(0, 0);
  for (int i = 0; i < 16; i++) {
    lcd.print(alphabet[i]);
  }
  lcd.setCursor(0, 1);
  for (int i = 16; i < 32; i++) {
    lcd.print(alphabet[i]);
  }
  // курсор остаётся на первой позиции первой строки
  cursorX = 0;
  cursorY = 0;
  lcd.setCursor(cursorX, cursorY);
  showCursor();
}

void startMarquee() {
  marqueeActive = true;
  marqueeOffset = 0;
  lastMarqueeTime = millis();
  
  // Гарантируем достаточную длину для бесконечной прокрутки
  while (marqueeLine1.length() < 48) {
    marqueeLine1 += marqueeLine1;
  }
  while (marqueeLine2.length() < 48) {
    marqueeLine2 += marqueeLine2;
  }
}

void updateMarquee() {
  if (!marqueeActive) return;
  
  unsigned long now = millis();
  if (now - lastMarqueeTime >= 1000) {
    lastMarqueeTime = now;
    
    // Выводим первую строку со сдвигом
    lcd.setCursor(0, 0);
    lcd.print(marqueeLine1.substring(marqueeOffset, marqueeOffset + 16));
    
    // Выводим вторую строку со сдвигом
    lcd.setCursor(0, 1);
    lcd.print(marqueeLine2.substring(marqueeOffset, marqueeOffset + 16));
    
    // Увеличиваем смещение, зацикливая его
    marqueeOffset++;
    if (marqueeOffset >= marqueeLine1.length() - 16) {
      marqueeOffset = 0;
    }
    
    // Восстанавливаем курсор
    lcd.setCursor(cursorX, cursorY);
    showCursor();
  }
}

void stopMarqueeIfNeeded(char key) {
  if (marqueeActive && key != 0) {
    marqueeActive = false;
    // Текст замирает в текущем состоянии
    // Просто показываем курсор
    lcd.setCursor(cursorX, cursorY);
    showCursor();
  }
}

void setup() {
  // Инициализация I2C для LCD
  Wire.begin(I2C_SDA, I2C_SCL);
  lcd.init();
  lcd.backlight();
  
  // Начальный вывод
  lcd.setCursor(0, 0);
  lcd.print("Hotel California");
  lcd.setCursor(0, 1);
  lcd.print("   Eagles 1976");
  
  // Курсор в виде подчеркивания в первой позиции первой строки
  cursorX = 0;
  cursorY = 0;
  lcd.setCursor(cursorX, cursorY);
  lcd.cursor();      // включаем подчеркивание
  lcd.blink();       // мигание курсора
  
  // Инициализация клавиатуры
  initKeypad();
}

void loop() {
  char key = getKey();
  
  if (key != 0) {
    // При любом нажатии останавливаем бегущую строку
    stopMarqueeIfNeeded(key);
    
    switch (key) {
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
      lcd.print(key);
 
      // Сдвигаем курсор вправо
      if (cursorX < 15) {
      cursorX++;
      } 
      else if (cursorY == 0) {
        // Переход на вторую строку
        cursorX = 0;
        cursorY = 1;
    } 
      else if (cursorY == 1 && cursorX == 15) {
      // Достигнут конец экрана - перемещаем в начало первой строки
      cursorX = 0;
      cursorY = 0;
      }
        lcd.setCursor(cursorX, cursorY);
        showCursor();
        break;
        
      case 'A':
        clearScreen();
        break;
        
      case 'B':
        toggleLine();
        break;
        
      case 'C':
        fillAlphabet();
        break;
        
      case 'D':
        startMarquee();
        break;
        
      case '*':
        moveCursorLeft();
        break;
        
      case '#':
        moveCursorRight();
        break;
    }
  }
  
  // Обновление бегущей строки (если активна)
  if (marqueeActive) {
    updateMarquee();
  }
  
  delay(20); // небольшое торможение для стабильности
}

Немного истории.

Термин Marquee (марки́) — это прямое название эффекта «бегущей строки» в программировании и дизайне.
Вот что их связывает:
1. Происхождение названия: Слово Marquee изначально означало козырек или навес над входом в театр или казино. В XX веке на таких навесах стали размещать ламповые панели с движущимся текстом (анонсами фильмов или шоу). Отсюда название перекочевало в цифровую среду.
2. HTML-тег <marquee>: В эпоху раннего интернета (Internet Explorer 3.0 и выше) существовал специальный тег <marquee>текст</marquee>. Любой текст внутри него автоматически начинал двигаться по экрану. Сейчас тег считается устаревшим, но само слово стало стандартом для обозначения этого эффекта.

Скачать ESP32C3_LuatOS_LCD1602_keyboard_4x4.ino

ESP32C3_LuatOS_LCD1602_keyboard_4x4.ino (3 Загрузки)

Ссылка на мою ретро версию на PIC16F870:

PIC16F870, клавиатура, LCD на HD44780, 7-сегментный дисплей 

 

Добавить комментарий

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