// ================================================================
// Futaba vfd Emulator – ESP32-S3 DevKitC-1
// Protokół: HD44780 Synchronous Serial Interface
// SPI: sprzętowe SPI2
//
// Pinout ESP32-S3:
// MOSI (DIN) = GPIO 11
// SCK (CLK) = GPIO 12
// CS (STB) = GPIO 10 – aktywny LOW
// RST (RES) = GPIO 2
//
// Start byte protokołu HD44780 serial:
// 0xF8 = zapis instrukcji (R/W=0, RS=0)
// 0xFA = zapis danych (R/W=0, RS=1)
// ================================================================
#include <SPI.h>
// ── Piny
#define PIN_MOSI 11
#define PIN_SCK 12
#define PIN_CS 10
#define PIN_RST 2
// ── SPI
// HD44780 serial: Mode 0 (CPOL=0, CPHA=0), LSB first, max 500kHz
SPIClass vfd_spi(SPI);
SPISettings vfd_spi_settings(500000, LSBFIRST, SPI_MODE0);
// ── Niskopoziomowe wysyłanie przez SPI
static void spi_send(uint8_t start_byte, uint8_t data_byte) {
Serial.print("[SPI] start=0x"); Serial.print(start_byte, HEX);
Serial.print(" data=0x"); Serial.println(data_byte, HEX);
vfd_spi.beginTransaction(vfd_spi_settings);
digitalWrite(PIN_CS, LOW);
vfd_spi.transfer(start_byte);
vfd_spi.transfer(data_byte);
digitalWrite(PIN_CS, HIGH);
vfd_spi.endTransaction();
delayMicroseconds(100);
}
// ── API wyświetlacza
void vfd_cmd(uint8_t cmd) {
spi_send(0xF8, cmd); // R/W=0, RS=0
}
void vfd_data(uint8_t data) {
spi_send(0xFA, data); // R/W=0, RS=1
}
void vfd_write(uint8_t c) {
vfd_data(c);
}
void vfd_print(const char *str) {
while (*str) vfd_data((uint8_t)*str++);
}
void vfd_clear() {
vfd_cmd(0x01);
delay(5);
}
void vfd_setCursor(uint8_t col, uint8_t row) {
vfd_cmd(0x80 | (col + (row ? 0x40 : 0x00)));
}
// ── Inicjalizacja
void vfd_init() {
delay(50);
// Function Set: DL=1, N=1, BR=00 (100%)
// 0x20 | 0x10(DL) | 0x08(N) | 0x00(BR) = 0x38
// Uwaga: w datasheecie bity to [DL N x BR1 BR0]
// 0b00111100 = DL=1, N=1, x=1, BR=00 → 0x3C
vfd_cmd(0x3C);
delay(5);
// Display ON: D=1, C=0, B=0
vfd_cmd(0x0C);
delay(5);
// Clear
vfd_cmd(0x01);
delay(5);
// Entry Mode: I/D=1 (increment), S=0
vfd_cmd(0x06);
delay(5);
}
// ── CGRAM – własne znaki
// slot: 0–7, bitmap: 8 bajtów, format HD44780 (bity [4:0])
void vfd_createChar(uint8_t slot, const uint8_t *bitmap) {
vfd_cmd(0x40 | (slot << 3));
for (uint8_t i = 0; i < 8; i++)
vfd_data(bitmap[i]);
// Wróć do DDRAM
vfd_setCursor(0, 0);
}
// ── Polskie znaki (CGRAM 0–6)
#define PL_A 0 // ą
#define PL_E 1 // ę
#define PL_O 2 // ó
#define PL_S 3 // ś
#define PL_Z 4 // ź / ż
#define PL_C 5 // ć
#define PL_N 6 // ń
// slot 7 – wolny
const uint8_t cgram_a_ogn[8] = {0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F, 0x01, 0x03}; // ą
const uint8_t cgram_e_ogn[8] = {0x00, 0x0E, 0x11, 0x1F, 0x10, 0x0E, 0x02, 0x03}; // ę
const uint8_t cgram_o_akt[8] = {0x04, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E, 0x00}; // ó
const uint8_t cgram_s_akt[8] = {0x04, 0x00, 0x0E, 0x10, 0x0E, 0x01, 0x1E, 0x00}; // ś
const uint8_t cgram_z_akt[8] = {0x04, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F, 0x00}; // ź/ż
const uint8_t cgram_c_akt[8] = {0x04, 0x00, 0x0E, 0x10, 0x10, 0x11, 0x0E, 0x00}; // ć
const uint8_t cgram_n_akt[8] = {0x04, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11, 0x00}; // ń
void vfd_loadPolish() {
vfd_createChar(PL_A, cgram_a_ogn);
vfd_createChar(PL_E, cgram_e_ogn);
vfd_createChar(PL_O, cgram_o_akt);
vfd_createChar(PL_S, cgram_s_akt);
vfd_createChar(PL_Z, cgram_z_akt);
vfd_createChar(PL_C, cgram_c_akt);
vfd_createChar(PL_N, cgram_n_akt);
}
// ── Pomocnicza: drukuj string z polskimi znakami
// W stringu użyj \x00=ą \x01=ę \x02=ó \x03=ś \x04=ź \x05=ć \x06=ń
// Przykład: vfd_printPL("Wej\x03cie"); → "Wejście"
void vfd_printPL(const char *str) {
while (*str) {
vfd_data((uint8_t)*str++);
}
}
// Setup i Loop
void setup() {
Serial.begin(115200);
pinMode(PIN_CS, OUTPUT);
pinMode(PIN_RST, OUTPUT);
digitalWrite(PIN_CS, HIGH);
digitalWrite(PIN_RST, LOW);
// Inicjalizacja VSPI z własnymi pinami
vfd_spi.begin(PIN_SCK, -1, PIN_MOSI, PIN_CS);
// Reset sprzętowy
delay(10);
digitalWrite(PIN_RST, HIGH);
delay(50);
vfd_init();
vfd_loadPolish();
Serial.println("vfd gotowy");
}
void loop() {
// Test 1: ASCII
vfd_clear();
vfd_setCursor(0, 0);
vfd_print("ABCDEFGH");
delay(2000);
// Test 2: cyfry
vfd_clear();
vfd_setCursor(0, 0);
vfd_print("12345678");
delay(2000);
// Test 3: polskie znaki
// "Cze\x03\x05" = "Cze" + ś + ć = "Cześć"... (brakuje ć na końcu, jest w slocie 5)
vfd_clear();
vfd_setCursor(0, 0);
// Cze + ś(slot3) + ć(slot5) = "Cześć" (skrócone do 8 znaków)
vfd_printPL("Cze\x03\x05!");
delay(2000);
// Test 4: Display ON/OFF
vfd_cmd(0x08); // Display OFF
delay(500);
vfd_cmd(0x0C); // Display ON
delay(500);
// Test 5: jasność
vfd_clear();
vfd_setCursor(0, 0);
vfd_print("Bright!");
for (uint8_t br = 0; br < 4; br++) {
vfd_cmd(0x3C | br); // BR1 BR0 = 00/01/10/11
delay(800);
}
vfd_cmd(0x3C); // wróć do 100%
}