#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
// ==================== CẤU HÌNH LCD I2C ====================
// Địa chỉ I2C mặc định của module PCF8574: 0x27
// STM32 Blue Pill I2C1: SDA = PB7, SCL = PB6
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ==================== CẤU HÌNH KEYPAD 4x4 ====================
const byte ROWS = 4;
const byte COLS = 4;
// Bản đồ phím
char hexaKeys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// Chân kết nối - ROW là OUTPUT, COL là INPUT
byte rowPins[ROWS] = {PA0, PA1, PA2, PA3}; // Hàng
byte colPins[COLS] = {PA4, PA5, PA6, PA7}; // Cột
// Tạo đối tượng Keypad
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
// ==================== BIẾN TOÀN CỤC ====================
String inputBuffer = ""; // Buffer lưu chuỗi nhập
const int MAX_INPUT_LENGTH = 16; // Giới hạn ký tự trên 1 dòng
int cursorCol = 0; // Vị trí con trỏ cột
int cursorRow = 1; // Hiển thị input ở dòng 2
// ==================== CUSTOM CHARACTERS ====================
// Icon bàn phím
byte keyboardIcon[8] = {
0b11111,
0b10001,
0b10101,
0b10001,
0b10101,
0b10001,
0b11111,
0b00000
};
// ==================== SETUP ====================
void setup() {
// Khởi tạo Serial để debug
Serial.begin(115200);
Serial.println(F("================================="));
Serial.println(F(" STM32 Keyboard Project"));
Serial.println(F(" LCD I2C + Keypad 4x4"));
Serial.println(F("================================="));
// Khởi tạo I2C trên STM32
Wire.begin();
// Khởi tạo LCD
lcd.init();
lcd.backlight();
// Tạo custom character
lcd.createChar(0, keyboardIcon);
// Hiển thị màn hình chào
showWelcomeScreen();
delay(2000);
// Hiển thị màn hình nhập liệu
showInputScreen();
}
// ==================== LOOP ====================
void loop() {
// Đọc phím từ keypad
char key = customKeypad.getKey();
if (key) {
Serial.print(F("Key pressed: "));
Serial.println(key);
processKey(key);
}
}
// ==================== XỬ LÝ PHÍM ====================
void processKey(char key) {
switch (key) {
case '#':
// Phím # = ENTER → Xác nhận input
handleEnter();
break;
case '*':
// Phím * = BACKSPACE → Xóa ký tự cuối
handleBackspace();
break;
case 'A':
// Phím A = Xóa toàn bộ
handleClearAll();
break;
case 'B':
// Phím B = Chức năng tùy chỉnh (hiện thông tin)
handleFunctionB();
break;
case 'C':
// Phím C = Chức năng tùy chỉnh
handleFunctionC();
break;
case 'D':
// Phím D = Chức năng tùy chỉnh
handleFunctionD();
break;
default:
// Phím số 0-9 → Thêm vào buffer
handleCharInput(key);
break;
}
}
// ==================== XỬ LÝ NHẬP KÝ TỰ ====================
void handleCharInput(char key) {
if (inputBuffer.length() < MAX_INPUT_LENGTH) {
inputBuffer += key;
cursorCol = inputBuffer.length();
// Cập nhật LCD dòng 2
lcd.setCursor(0, 1);
lcd.print(inputBuffer);
// Hiển thị con trỏ nhấp nháy
lcd.setCursor(cursorCol, 1);
lcd.blink();
Serial.print(F("Buffer: "));
Serial.println(inputBuffer);
} else {
// Buffer đầy - thông báo
lcd.setCursor(0, 0);
lcd.print(F(" Buffer Full! "));
delay(500);
lcd.setCursor(0, 0);
lcd.print(F("Input: "));
}
}
// ==================== XỬ LÝ ENTER ====================
void handleEnter() {
if (inputBuffer.length() > 0) {
Serial.print(F("ENTER - Data: "));
Serial.println(inputBuffer);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Confirmed:"));
lcd.setCursor(0, 1);
lcd.print(inputBuffer);
delay(1500);
// Reset
inputBuffer = "";
cursorCol = 0;
showInputScreen();
}
}
// ==================== XỬ LÝ BACKSPACE ====================
void handleBackspace() {
if (inputBuffer.length() > 0) {
inputBuffer.remove(inputBuffer.length() - 1);
cursorCol = inputBuffer.length();
// Xóa dòng 2 và viết lại
lcd.setCursor(0, 1);
lcd.print(F(" ")); // Xóa 16 ký tự
lcd.setCursor(0, 1);
lcd.print(inputBuffer);
lcd.setCursor(cursorCol, 1);
Serial.print(F("Backspace - Buffer: "));
Serial.println(inputBuffer);
}
}
// ==================== XỬ LÝ XÓA TẤT CẢ ====================
void handleClearAll() {
inputBuffer = "";
cursorCol = 0;
lcd.setCursor(0, 1);
lcd.print(F(" "));
lcd.setCursor(0, 1);
Serial.println(F("Clear All"));
}
// ==================== CÁC PHÍM CHỨC NĂNG ====================
void handleFunctionB() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Len:"));
lcd.print(inputBuffer.length());
lcd.print(F(" Max:"));
lcd.print(MAX_INPUT_LENGTH);
lcd.setCursor(0, 1);
lcd.print(F("[*]Back [#]OK"));
Serial.println(F("Function B - Show Info"));
delay(2000);
showInputScreen();
// Hiển thị lại buffer hiện tại
lcd.setCursor(0, 1);
lcd.print(inputBuffer);
}
void handleFunctionC() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(0); // Custom icon
lcd.print(F(" STM32 KB v1.0"));
lcd.setCursor(0, 1);
lcd.print(F(" Wokwi Demo"));
Serial.println(F("Function C - About"));
delay(2000);
showInputScreen();
lcd.setCursor(0, 1);
lcd.print(inputBuffer);
}
void handleFunctionD() {
// Toggle đèn nền LCD
static bool backlightOn = true;
backlightOn = !backlightOn;
if (backlightOn) {
lcd.backlight();
Serial.println(F("Backlight ON"));
} else {
lcd.noBacklight();
Serial.println(F("Backlight OFF"));
}
}
// ==================== MÀN HÌNH ====================
void showWelcomeScreen() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(0); // Custom icon
lcd.print(F(" STM32 Keypad"));
lcd.setCursor(0, 1);
lcd.print(F(" Loading..."));
// Animation loading
for (int i = 0; i < 3; i++) {
delay(300);
lcd.setCursor(12 + i, 1);
lcd.print(F("."));
}
}
void showInputScreen() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(F("Input:"));
lcd.setCursor(0, 1);
lcd.blink();
}STM32F411E-DISCO
Driver paso a paso
Motor paso a paso
Driver motor DC
Motor DC reductora
Servo (pinza/muñeca)
Encoder magnetico I2C
Sensor distancia ToF I2C
Pull-ups I2C 4.7k
Botones seleccion color
Encendido
Inicio pintura
Emergencia / Reset
Finales de carrera