Arduino-gebaseerde Vacuum Fluorescent Display (VFD) Controller Kit
Onderdelen en benodigdheden
| Arduino UNO (of elke 8-bit Arduino) | × | 1 |
| VFD Display | × | 1 |
| CD4094 (DIP geïntegreerde schakeling) | × | 2 |
| CD4017 (DIP geïntegreerde schakeling) | × | 2 |
| BC557 (of equivalent transistor: 2N3906, 2SA733, etc. of hogere spanning PNP) | × | 17 |
| BC547 (of equivalent: 2N3904, 2SC945, etc.) | × | 2 |
| PC817 (optocoupler, veelvoorkomend in schakelende voedingen) | × | 1 |
| 1N4007 – Hoge spanning, hoge stroom diode | × | 4 |
| Elektrolytische condensators (zie schema voor waarden en spanningen) | × | 4 |
| 470nF Condensator (elk type is goed) | × | 1 |
| Experimenteerprint (perfboard) | × | 1 |
| 1/4W Weerstanden (zie schema voor waarden) | × | 24 |
| Dupont Jumper wires | × | 1 set |
| Draad (dunne draden voor de printachterkant en voor het aansluiten van het display) | × | 1 |
Benodigde tools en machines
| Solderbout (generiek) |
| Solder tin (koop lokaal, geen goedkope spul, 0.5mm aanbevolen) |
| Tang/kniptang |
Over dit project
Wat is het nut?
VFD-displays zijn niet meer trending om duidelijke redenen: ze zijn van glas ("uh cool!"), hebben een gloeddraad ("echt??"), ze zijn eigenlijk vacuumbussen ("interessant!"), ze produceren een zo helder licht, soms gekleurd, dat er een donkere kunststof bovenop nodig is ("uh, weer cool!").
Normaal heb je een speciale driverchip nodig. Meeste modules/apparaten komen met zo'n chip die je onafhankelijk kunt besturen via I2C of SPI (veel makkelijker). Als je zo'n display koopt of hebt zonder zo'n chip, is het niet zo eenvoudig "aansluiten en gaan". Het ideale zou zijn die chip te kopen (meestal SMD-formaat). Deze schakeling geeft een behoorlijke interface voor een Arduino en kan waarschijnlijk elk VFD aansturen, inclusief de bulky "buis" vintage modellen, maar matrix-displays met veel segmenten zijn geen goed idee – je kunt de schakeling uitbreiden, maar DUIZENDEN... misschien niet.
Voor- en nadelen
Voordelen
- bijna nul kosten (hopelijk)
- onderdelen makkelijk te vinden
- hoge spanning capaciteit (tot honderden Volt op de anoden)
- onafhankelijke grid- en anode-spanningen
- alleen 3 digitale datalijnen nodig
- uitbreidbaar (wel meer CPU-werk vereist)
- code is klaar voor elke 8-bit Arduino
Nadelen
- vereist verschillende spanningen en voedingen (niet vreemd voor VFDs)
- geen dim-functie (behalve via spanning-aansturing)
- gebruikt twee Arduino-bronnen: SPI en timer1
- je kunt de microcontroller niet laten crashen, anders vriest de scan op een grid
- het is niet gewoon een paar weerstanden en LEDs, het vereist wel wat werk
Werkingsprincipe
Een VFD werkt meestal gemultiplext, als een 2D-matrix met X- en Y-as. Je moet het filament (de kathode, die dunne draden ervoor) opwarmen, de grond op dat filament aansluiten, een positieve spanning op een grid (besturingsgrid, één punt op de X-as) en weer een positieve spanning op een segmentpin (de anode, één punt op de Y-as). Op dat moment brandt één segment. Om willekeurige segmenten en combinaties aan te sturen met "weinig" draden, selecteert de multiplexing telkens één grid en configureert tegelijkertijd de anoden om de bijbehorende segmenten onder dat grid te laten branden. Een moment later selecteert het een ander grid en configureert de anoden voor de segmenten onder dat tweede grid. Door deze continue scan te versnellen, lijkt het alsof alles tegelijkertijd aangestuurd wordt – dit heet POV (Persistence Of Vision).
Deze schakeling gebruikt twee typen IC's uit dezelfde familie: twee CD4094 en twee CD4017. De 4094 drijven de anoden, de 4017 de grids. De 4094 kunnen een high/low-configuratie op hun uitgangen opslaan en zijn geschikt voor de anoden-kant. De 4017 zijn klassieke chips met 10 sequentiële uitgangen, perfect voor de grids. Zodra de 4094 geladen zijn met de anode-configuratie, past het "ok"-signaal (strobe) deze aan en schakelt tegelijkertijd de 4017 één stap verder, wat automatische sequencing mogelijk maakt.
Het voedingsgedeelte bestaat voornamelijk uit BC557-transistoren (of equivalent) die een bredere spanningswissel op de anoden toestaan, omdat deze displays hogere spanning nodig hebben dan de 5V van de Arduino. De grids worden direct door de 4017 aangestuurd. Een PC817-optocoupler stelt een hogere spanning dan 5V rond de 4017 mogelijk en ook een ander spanningsniveau dan de CD4094, wat het ontwerp ENORME vereenvoudigt.
De Arduino moet de orchestratie verzorgen: het moet alle segmentconfiguraties opslaan en de schakeling bij elke grid-switch laden met de anode-instelling. Dit vereist slimme code. De code die ik maakte stelt een timer-gebaseerde interrupt in die de 4094-chips bij elke stap herlaadt, ongeveer 1000 keer per seconde. Voor 10 grids geeft dit een verversingsfrequentie van 100Hz, wat goed is. Er is een data-array die de segmentconfiguratie opslaat en in-code gewijzigd kan worden zonder speciale procedures; de interrupt-routine uploadt de data van zich uit.
Voedingen
Grid-spanning op de 4017-chips: 5Vdc tot 18Vdc, minimaal 50mA, hoeft niet geregeld te zijn. Meestal is 12V voldoende; hoger verhogt de helderheid niet veel.
Anode-spanning: van 0V tot wat je transistoren aankunnen (50Vdc voor BC557). Meestal doet 20-30Vdc het prima, ongeregeld is goed. 50mA is meer dan genoeg.
Digitale voeding: 5Vdc of 3.3Vdc (voor die typen Arduinos/MCUs), ongeveer 100mA. Als de CD4094 traag worden, kun je de SPI-klok in de code verlagen en/of de "Q'S"-uitgang van de eerste 4094 gebruiken voor stabielere communicatie.
Filament-voeding: minimaal 5V 200-300mA. Als je al een DC-bron hebt, kun je de bruggleichrichter en 1000uF condensator weglaten, maar je KUNT niet dezelfde digitale voeding voor/vanaf de Arduino gebruiken. Het filament loopt vaak op ~3V en trekt ~150mA.
Tips en opmerkingen
- Voordat je draden aansluit, als je niet weet welke pin wat doet: test het display eerst. Meestal gaan twee AA-batterijen in serie prima voor het filament (meestal de zijcontacten), terwijl twee 9V-batterijen in serie de positieve polarisatie voor zowel grids als anoden geven. Grids en anoden zijn vaak gegroepeerd; vind de "anode nul" – vaak het eerste segment op het numerieke/alfanumerieke deel, vaak dezelfde volgorde voor alle grids. Omgekeerde volgorde maakt de software onhandig. Grids volgen meestal de pinnummering.
- Verhoog spanningen op grids en anoden verstandig; de phosphoren onherroepelijk verbranden. De schakeling scant, dus als het systeem crasht, geef je continu te veel vermogen op één grid-sectie.
- Speel met een compromis tussen grid/anode-spanningen. Grids kunnen stroom verbruiken, niet de moeite waard om daarop te drukken, beter de anoden in de gaten houden.
- Eén transformator voor je hele project is ideaal. Schakelende voedingen en geaarde voedingen kunnen problemen geven – voor experimenteren is een klassieke transformatorvoeding ALTIJD een goed idee.
- Je kunt deze driver uitbreiden met extra CD4094 en/of CD4017. Als je 8 anoden of minder nodig hebt, kun je de tweede 4094 weglaten; hetzelfde voor de 4017. Maar als je de driver compleet wilt hebben, bouw hem zo compleet mogelijk.
- Er zitten geen weerstanden op de bases van de anodetransistoren – dit geeft super-snelle aansturing en beperkt de onderdelenlijst, maar laat de CD4094 stroom verbruiken. Ze worden niet warm, maar sommige exemplaren kunnen een te sterke uitgang hebben; check stroomverbruik in dat geval (maximaal ~4mA per pin bij 5V voeding).
- Oude, uitgedaagde displays kunnen vernieuwd worden met stroomschokken op de filamenten. Als het hele display dim is (niet alleen een verbrond segment), zeggen ze: breng de draden zichtbaar tot gloeien (geel) ongeveer vijf keer op een rij voor een paar seconden. Dit vereist waarschijnlijk het dubbele van de nominële filamentspanning. Het schoont ze op, maar is riskant – het zijn geen gloeilampen, je kunt de draden breken.
Code
Arduino code met werkende routine Arduino
/* Versatile VFD Display Hardware Interface Arduino Program Copyright (C) 2019 Genny A. Carogna
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. */
#define ledLed 13
#define strobePin 4
#define strobeHold 10 // microseconden
// Data voor mijn displays (videorecorder, CD-speler, stereo) – verwijder en definieer gewoon "gridAmount" en "anodeAmount"
#define JVC
#define mitsubishiGrids 10
#define mitsubishiAnodes 9
#define marantzGrids 9
#define marantzAnodes 14
#define JVCGrids 11
#define JVCAnodes 19
#if defined mitsubishi
#define gridAmount mitsubishiGrids
#define anodeAmount mitsubishiAnodes
#elif defined marantz
#define gridAmount marantzGrids
#define anodeAmount marantzAnodes
#elif defined JVC
#define gridAmount JVCGrids
#define anodeAmount JVCAnodes
#endif
// segments[x] array slaat segmentbits op
// segments[0] = grid nul, LSB = segment nul in die grid
// Voor >8 segmenten per grid: 16 of 32 bit container per grid, beheerd automatisch via #define
#if anodeAmount > 16
volatile uint32_t segments[gridAmount] = {0};
#elif anodeAmount > 8
volatile uint16_t segments[gridAmount] = {0};
#else
volatile uint8_t segments[gridAmount] = {0};
#endif
void setup(){
// SPI-pins en functionele pins als output instellen (snel/direct)
*portModeRegister(digitalPinToPort(PIN_SPI_SS)) |= digitalPinToBitMask(PIN_SPI_SS);
*portModeRegister(digitalPinToPort(PIN_SPI_MOSI)) |= digitalPinToBitMask(PIN_SPI_MOSI);
*portModeRegister(digitalPinToPort(PIN_SPI_SCK)) |= digitalPinToBitMask(PIN_SPI_SCK);
*portModeRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin);
*portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin); // laag
*portModeRegister(digitalPinToPort(ledLed)) |= digitalPinToBitMask(ledLed);
*portOutputRegister(digitalPinToPort(ledLed)) &= ~digitalPinToBitMask(ledLed); // laag
delay(800); // wachten op CD4017 reset
cli(); // interrupts uitschakelen
// Timer1 configuratie: interrupt "grids keer 100" per seconde = 100Hz refresh
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 160000 / gridAmount;
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS10);
TIMSK1 |= (1 << OCIE1A);
// SPI configuratie (geen Arduino SPI tools – die gebruiken interrupts)
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); // 1MHz SCK
// SPSR = (1 << SPI2X); // uncomment voor halve kloksnelheid
sei(); // interrupts weer aan
}
void loop() {
show(); // screensaver demo, mag verwijderd worden
}
// Interrupt service routine: gebeurt bij elke grid-switch, 100x per seconde
ISR(TIMER1_COMPA_vect){
static uint8_t turn = 1; // onthoudt huidige grid
#if anodeAmount > 24
SPDR = ~uint8_t(segments[turn] >> 24);
while (!(SPSR & (1 << SPIF))) ;
#endif
#if anodeAmount > 16
SPDR = ~uint8_t(segments[turn] >> 16);
while (!(SPSR & (1 << SPIF))) ;
#endif
#if anodeAmount > 8
SPDR = ~uint8_t(segments[turn] >> 8);
while (!(SPSR & (1 << SPIF))) ;
#endif
SPDR = ~uint8_t(segments[turn]);
while (!(SPSR & (1 << SPIF))) ;
// Strobe: past anode-config toe EN schakelt 4017 één stap verder
*portOutputRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin);
delayMicroseconds(strobeHold);
*portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin);
turn++;
if (turn == gridAmount) turn = 0;
}
// DEMO FUNCTIES (specifiek voor mijn displays – mag verwijderd worden)
void show() {
#ifdef mitsubishi
for (uint8_t i = 0; i < 9; i++) for (uint8_t j = 0; j < 11; j++) { delay(200); mitsubishiPrintNum(j, i); }
for (uint8_t i = 0; i < 9; i++) for (uint8_t j = 0; j < 11; j++) { delay(20); mitsubishiPrintNum(j, i); }
#elif defined marantz
for (uint8_t i = 0; i < 6; i++) for (uint8_t j = 0; j < 11; j++) { delay(200); marantzPrintNum(j, i); }
for (uint8_t i = 0; i < 6; i++) for (uint8_t j = 0; j < 11; j++) { delay(20); marantzPrintNum(j, i); }
#elif defined JVC
// JVC demo code
#endif
delay(500);
// Segmenten flip
for (uint8_t i = 0; i < gridAmount; i++) for (uint8_t j = 0; j < anodeAmount + 1; j++) { delay(100); segments[i] = (uint32_t(1) << j); }
delay(500);
// Heel display knipperen
for (uint8_t i = 0; i < 5; i++) {
delay(200);
for (uint8_t j = 0; j < gridAmount; j++) segments[j] = 0xFFFFFFFF;
delay(200);
for (uint8_t j = 0; j < gridAmount; j++) segments[j] = 0;
}
delay(500);
}
void mitsubishiPrintNum(uint8_t val, uint8_t pos) {
if (pos > 2) pos++;
uint16_t mask = 0xFF80;
uint8_t numbers[] = {B00111111, B00000110, B01011011, B01001111, B01100110, B01101101, B01111101, B00100111, B01111111, B01101111, B00000000};
switch (pos) {
case 0: case 1: case 4: case 5: case 6: case 7: case 8:
segments[pos] &= mask;
segments[pos] |= numbers[val];
break;
case 2:
if (val == 1) segments[1] |= (1 << 8);
else segments[1] &= ~(uint16_t(1) << 8);
break;
case 9:
segments[9] &= 0xFFF0;
if (val == 1) segments[9] |= 3;
else if (val == 2) segments[9] |= B1101;
}
}
void marantzPrintNum(uint8_t val, uint8_t pos) {
if (pos > 5) return;
pos += 2;
uint16_t mask = 0xFF80;
uint8_t numbers[] = {B00111111, B00000110, B01011011, B01001111, B01100110, B01101101, B01111101, B00100111, B01111111, B01101111, B00000000};
segments[pos] &= mask;
segments[pos] |= numbers[val];
}
void JVCPrintNum(uint8_t val, uint8_t pos) {
// nog te implementeren
}
Schema
Ik heb niet ALLE BC557-transistoren getekend omdat ze identiek op alle CD4094-uitgangen zitten.
Er is een jumper-selector (rode lijn) op de 4017-chips die de twee 4017 reset na afloop van de grid-scan. Je moet de vliegende jumper op de uitgangspins NA de laatste grid aansluiten/solderen. Als je display 10 grids heeft, gaat de jumper op de 11e uitgang.
De Arduino-aansluitingen zijn in rood gemarkeerd. Je kunt de USB-voeding gebruiken, maar voor de overige voedingen wordt een klassieke transformator ten zeerste aangeraden als je al een geaarde voeding (zoals je PC) hebt. Alle voedingen moeten DC zijn, behalve misschien de filament-voeding. Ik heb diodes toegevoegd omdat je waarschijnlijk een aparte transformator met lage spanning nodig hebt. Filament-voeding is meestal ~3V 150mA, een 5V AC-transformator volstaat. De "ballast" is een draadgewonden vermogenspotentiometer van ~100ohm, of een vaste weerstand, of enkele 1N4007-diodes om de spanning te verlagen.
De BC557-transistoren trekken de anoden op naar anodische spanning; de 100kohm-weerstanden laten de spanning vallen wanneer het segment uitstaat, terwijl de CD4094 op 5V blijven.
De CD4017's worden gevoed met grid-spanning en hoeven geen extra transistoren. Er is een "ghetto" reset-schakeling voor de 4017 van ~0.1 seconde – wacht tot deze rustig is voordat je de code start. Het originele schema voor cascaderen van CD4017 vereiste extra logic gates; ik gebruikte een NPN-transistor en de optocoupler zelf om een AND-poort na te bootsen – snel genoeg voor de klokinputs (Schmitt-trigger).
Er zijn verschillende ground-verbindingen voor chips en componenten – bestudeer het schema goed.
Productieproces
- Bouw een Squid Game-thema Interactieve Pop met Arduino UNO – DIY Project
- Professionele gids voor bitmap-animatie op de ILI9341 2,4″ TFT Touchscreen Shield
- Web-gestuurd DMX-verlichtingssysteem – Arduino Master Controller
- Bouw een Klassiek Pong-spel op Arduino UNO met OLED-display – Stap-voor-stap Tutorial
- DIY Arduino USB Gaming Controller – Bouw Je Eigen High-Performance Gamepad
- Unopad: Naadloze Arduino MIDI-controller voor Ableton Live
- Arduino Temperatuurbewaking & Realtime Klok met 3.2" TFT-display
- Interactief Nextion HMI-weerstation met Arduino Mega en sensoren
- Bouw een DIY Voltmeter met Arduino en Nokia 5110 Display – Stap-voor-Stap Handleiding
- BME280 Temperatuur, Vochtigheid & Druk Weergeven op Nextion Touchscreen
- Realtime afstandsmeting met Arduino Sonar & Processing visualisatie