#include <Arduino.h>
// Kolejność siatek na ekranie:
// lewa -> prawa
// G9 G8 G7 G6 G5 G4 G3 G2 G1
const uint8_t gridPins[9] = {
16, 17, 18, 8, 3, 9, 10, 11, 12
};
// Segmenty:
// 0=a, 1=j, 2=h, 3=k, 4=b, 5=f, 6=g, 7=m, 8=c, 9=e, 10=v, 11=n, 12=p, 13=d, 14=dp
const uint8_t segPins[15] = {
2, 42, 41, 40, 39,
38, 37, 36, 35, 45,
48, 47, 21, 20, 19
};
enum {
SEG_A = 0, SEG_J, SEG_H, SEG_K, SEG_B, SEG_F, SEG_G, SEG_M, SEG_C, SEG_E, SEG_V, SEG_N, SEG_P, SEG_D, SEG_DP
};
typedef struct {
char ch;
uint8_t seg[15];
} vfd_char_t;
// ------ a ------
// | \ | / |
// f h j k b
// | \ | / |
// --- n ----- g ---
// | / | \ |
// e v m p c
// | / | \ |
// ------ d ------ dp
const vfd_char_t vfdFont[] = {
// A, J, H, K, B, F, G, M, C, E, V, N, P, D, DP
{ '0', { 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0 } },
{ '1', { 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 } },
{ '2', { 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0 } },
{ '3', { 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0 } },
{ '4', { 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0 } },
{ '5', { 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 } },
{ '6', { 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0 } },
{ '7', { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 } },
{ '8', { 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0 } },
{ '9', { 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 } },
{ 'A', { 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0 } },
{ 'B', { 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0 } },
{ 'C', { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 } },
{ 'D', { 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0 } },
{ 'E', { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0 } },
{ 'F', { 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0 } },
{ 'G', { 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0 } },
{ 'H', { 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0 } },
{ 'I', { 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 } },
{ 'J', { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0 } },
{ 'K', { 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0 } },
{ 'L', { 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 } },
{ 'M', { 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0 } },
{ 'N', { 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0 } },
{ 'O', { 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0 } },
{ 'P', { 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0 } },
{ 'R', { 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0 } },
{ 'S', { 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0 } },
{ 'T', { 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
{ 'U', { 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0 } },
{ 'V', { 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0 } },
{ 'W', { 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0 } },
{ 'X', { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0 } },
{ 'Y', { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
{ 'Z', { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 } },
{ '-', { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0 } },
{ '_', { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
{ ' ', { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }
// A, J, H, K, B, F, G, M, C, E, V, N, P, D, DP
};
// Bufor znaków
volatile char displayBuffer[9] = {
'R', 'A', 'D', 'M', 'O', 'R', ' ', ' ', ' '
};
hw_timer_t *timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint8_t currentGrid = 0;
const uint8_t* getCharSegments(char c) {
for (size_t i = 0; i < sizeof(vfdFont) / sizeof(vfdFont[0]); i++) {
if (vfdFont[i].ch == c) {
return vfdFont[i].seg;
}
}
return vfdFont[sizeof(vfdFont) / sizeof(vfdFont[0]) - 1].seg;
}
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
// gasimy poprzednią siatkę
digitalWrite(gridPins[currentGrid], LOW);
// następna pozycja
currentGrid++;
if (currentGrid >= 9)
currentGrid = 0;
// czyszczenie segmentów
for (int i = 0; i < 15; i++) {
digitalWrite(segPins[i], LOW);
}
// pobranie segmentów znaku
const uint8_t* segs =
getCharSegments(displayBuffer[currentGrid]);
// ustawienie segmentów
for (int s = 0; s < 15; s++) {
if (segs[s]) {
digitalWrite(segPins[s], HIGH);
}
}
// aktywacja siatki
digitalWrite(gridPins[currentGrid], HIGH);
portEXIT_CRITICAL_ISR(&timerMux);
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < 9; i++) {
pinMode(gridPins[i], OUTPUT);
digitalWrite(gridPins[i], LOW);
}
for (int i = 0; i < 15; i++) {
pinMode(segPins[i], OUTPUT);
digitalWrite(segPins[i], LOW);
}
timer = timerBegin(1000000);
timerAttachInterrupt(timer, &onTimer);
// 2000 us = 2 ms
// pełne odświeżenie ~55 Hz
//timerAlarm(timer, 2000, true, 0);
//timerAlarm(timer, 5000, true, 0);
timerAlarm(timer, 8000, true, 0);
Serial.println("VFD start");
}
void loop() {
static unsigned long last = 0;
static int offset = 0;
const int FONT_SIZE = sizeof(vfdFont) / sizeof(vfdFont[0]);
if (millis() - last >= 500) {
last = millis();
portENTER_CRITICAL(&timerMux);
for (int i = 0; i < 9; i++) {
int idx = (offset + i) % FONT_SIZE;
displayBuffer[i] = vfdFont[idx].ch;
}
portEXIT_CRITICAL(&timerMux);
offset++;
if (offset > FONT_SIZE - 9) {
offset = 0;
}
}
}
/*
void loop() {
static uint32_t last = 0;
static bool state = false;
if (millis() - last > 2000) {
last = millis();
portENTER_CRITICAL(&timerMux);
if (state) {
memcpy((void*)displayBuffer, "RADMOR ", 9);
} else {
memcpy((void*)displayBuffer, "TUNER FM", 9);
}
state = !state;
portEXIT_CRITICAL(&timerMux);
}
}*/