VFD Clock project using ESP32-S3 , directly manipulating the ESP registers to enable easier multiplexing and segment mapping.

VFD5

The NOS IV22 - VFD tubes have a date code of 1986 so they were treated to some NOS Bakelight sockets, yes they were dis-com-boob-erated and the oxides polished off the pins.

pcb

Resin 3D facia board secured the sockets ready for soldering

resin000

Colour coded test Wiring loom

wiring loom

base1base2

The IV22 tube has a "Hot" cathode filament, 8 Anode segments (inc dp) plus an Anode Gate.

Luckily VFD's are relative low voltage, however this iteration uses overkill MPS A42 transistors for switching 28Volts to the segments and multiplexed Anode gates (2N2222 - 2N3904 transistors could also be used). A simple 5V>28V boost module is used for the switching Anode supply.

Boost

Cathode current is happy @90mA (recommended 100mA).

Drivers

To keep time after power loss a simple DS3231 RTC module with battery back is used.

rtc

---------------------------------------------------------------------------------------

Get you going Clock Code :-

gif

Worthy of note :-  a batch of the ESP32-S3 GPIO's run sequentially 1 - 18 so you can shout a loud Word of binary data to multiple GPIO's in a single command. (Kudus on you Expressif)

---------------------------------------------------------------------------------------

#include <DS3231.h> // RTC Real Time Clock
#include <Wire.h>
DS3231 myRTC;
bool century = false;
bool h12Flag; bool pmFlag;
byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits;
bool alarmDy, alarmH12Flag, alarmPmFlag;
int houR;int minutE;int seconD;
int numArray[12]={0x81,0xCF,0x92,0x86,0xCC,0xA4,0xE0,0x8F,0x80,0x8C,0x7F}; //8xSegments
int digitArray[7]={0xFE00,0xFD00,0xFB00,0xF700,0xEF00,0xDF00}; //  6x Display Anodes
#define PARALLEL_0  1 //  start pin number number with consecutive GPIOs ESP32S2 can support 1-14 (workio) maybe 15-18

int countTime =0;
int count=0;
int counter=999;
void setup() {
Serial.begin(115200);
  Wire.begin(42,41); // i2c pins on ESP32-S3
   timeGet(); // call Debug to check if RTC is up and running
 for (int i = 0; i < 14; i++) {    pinMode(PARALLEL_0 + i, INPUT);  }  parallel_set_outputs();
} // set up the parallel registers
void loop() {
   if (millis()-countTime >=1000){seconD=myRTC.getSecond();minutE=myRTC.getMinute();houR=myRTC.getHour(h12Flag, pmFlag);countTime=millis();} // every second call RTC clock
parallel_write((digitArray[0]) | (numArray[houR/10]));delay(1);  //write to Registers
parallel_write((digitArray[1]) | (numArray[houR%10]));delay(1);  // i.e Anode Gate and
parallel_write((digitArray[2]) | (numArray[minutE/10]));delay(1);  // anode segments
parallel_write((digitArray[3]) | (numArray[minutE%10]));delay(1);  // with segment maps
parallel_write((digitArray[4]) | (numArray[seconD/10]));delay(1);  // delay affects multiplexing
parallel_write((digitArray[5]) | (numArray[seconD%10]));delay(1);  

}

void parallel_write(uint16_t value) {
  uint16_t output = (REG_READ(GPIO_OUT_REG) & ~(0xFFFF << PARALLEL_0)) | (((uint16_t)value) << PARALLEL_0);
  REG_WRITE(GPIO_OUT_REG, output);
}

void parallel_set_outputs(void) {
  REG_WRITE(GPIO_ENABLE_W1TS_REG, 0xFFFF << PARALLEL_0);
}

uint8_t parallel_read(void) {  uint16_t input = REG_READ(GPIO_IN_REG);  return (input >> PARALLEL_0);
}
void timeGet() {
  Serial.print(myRTC.getYear(), DEC); Serial.print(' ');
  Serial.print(myRTC.getMonth(century), DEC);  Serial.print(" ");
  Serial.print(myRTC.getDate(), DEC);  Serial.print(" ");
  Serial.print(myRTC.getDoW(), DEC);  Serial.print(" ");
  Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC);  Serial.print(" ");
  Serial.print(myRTC.getMinute(), DEC);  Serial.print(" ");
  Serial.print(myRTC.getSecond(), DEC);
  Serial.print(" T="); Serial.println(myRTC.getTemperature(), 2);
 }  
 void setTime(){  // one time set clock up call
     myRTC.setClockMode(false);  // set to 24h      //setClockMode(true); // set to 12h
     myRTC.setYear(23);
     myRTC.setMonth(8);
     myRTC.setDate(6);
     myRTC.setDoW(0);
     myRTC.setHour(11);
     myRTC.setMinute(45);
     myRTC.setSecond(00);    
}