Categories

# VFD Night Light with Attiny84

VFD Night Light DIY Project. Developed and written by Frasier Chew.

Vacuum Fluorescent Displays (VFDs) have been around for over 6 decades. Although its heyday has passed in favour of other newer display technologies like the LCD, they are still being used today because of their robustness. VFDs are able to operate under a wide range of temperature conditions and offer exceptional viewing angles.

A simple explanation of how VFDs work is that a cathode filament supplies the electrons which are attracted to phosphor coated anodes. When the phosphor coating is hit by electrons, they fluoresce and emit visible light.

This article is organised as follows. Firstly, section 1 presents and showcases the project. Secondly, section 2 explains how the device is designed and operated. Finally, the annex provides a gallery of pictures, a list of materials and the project code.

## 1. Description of the Project

### 1.1 VFD IV-25

In this article, I will show you how I made a cool night light using an old IV-25 Soviet VFD:

The IV-25 is a 7 segmented bar graph display tube. It is a very simple tube because it has no screen grids and has individual output pins for each anode, making multiplexing unnecessary.

### 1.2 VFD Night Light

The IV-25 night light is bright enough to illuminate a dark room with a soft green glow. Using the Attiny84, I have coded for 4 different operating sequences for the VFD which is controlled using a rotary selector switch. The whole circuit is powered by a 18650 lithium battery and can be charged with a TP4056 module.

The four modes of operation are:

## 2. VFD Light Build and Operation

This section shows how the VFD Night Light was designed and built. It includes the following subsections:

• Power supply.
• PCB Schematic and the explanation of the different electronic stages.
• Software.

### 2.1 Power Supply

#### 2.1.1 Requirements

The Attiny84 microcontroller requires 5Vdc.

Apart from that, in order to use this VFD, 2 different voltages are required:

• 25 Vdc for the anodes
• 2.4 Vac for the cathode filament. Using DC with the filament will result in an unwanted concentration gradient across the VFD tube which can cause uneven illumination of the anode segments. (Section 2.2.1 explains the generation of this voltage)

#### 2.1.2 Voltage Boost Converter

Two MT3608 Boost Converter Modules were used to generate the 25Vdc for the anode segments and the 5Vdc to power the Attiny84 microcontroller:

These modules step up the 3.7Vdc from the 18650 battery and provide a constant voltage output which can be adjusted using the potentiometer.

### 2.2 Schematics

#### 2.2.1 VFD Filament Supply

For the filament supply, I used the LM4871 audio amplifier IC. This is the modern equivalent of the now obsolete LM9022, a dedicated VFD filament driver IC, but is essentially the same chip. I followed the self-oscillation application circuit to generate pseudo AC using a square wave:

A 100 ohm resistor (R33) was used to limit the current to the filament such that it barely glows. It is important not to overdrive the filament as it will significantly reduce the lifespan of the tube while only marginally increasing brightness.

#### 2.2.2 VFD and Microcontroller Interface

I used Kevin Rye’s circuit design to interface the Attiny84 microcontroller to the VFD. More information about his design can be found here.

This interface is based on NPN and PNP transistors, namely the references 2N3904 and 2N3906 respectively.

#### 2.2.3 Mode Selection

Because of output port constraints of the Attiny84, I could only code for a maximum of 4 different operating sequences for the VFD. Hence I decided to use a Single Pole 4 Throw (SP4T) rotary switch.

Note If you decide to use a rotary switch with more poles, the extra poles can simply be ignored by not connecting them. So do not worry if you cannot find the exact switch type. You only really need to consider the number of Throws/Positions.

If more sequences are desired, a microcontroller with more I/O pins like the Atmega328 can be used along with other types of switches.

### 2.3 Software

In order to fade segments on and off, Pulse Width Modulation (PWM) is required. Unfortunately, the Attiny84 does not have enough PWM pins to separately control each of the 7 anode segments hence software PWM is required.

Łukasz Podkalicki has written a code which uses delays in a clever way to pulse fade an LED. The code can be found here. His code works by firstly defining a Max and Min delay value. The Min value is decremented until a condition is met. This triggers “dir = 1” and causes the Min value to Increment until another condition is met which triggers “dir = 0”. “dir” alternates between 1 and 0 creating a pulse fade effect on an LED.

In Annex 3 you can find my Atmel studios code incorporating his software PWM. I highly encourage you to play around with it and create your own operating sequences!

## Annex: VFD Night Light Details

### Annex 3: Code

/*
* IV-25 VFD Night Light.c
*
* Created: 15/12/2022 4:33:34 PM
* Author : Frasier
*/

#include <avr/io.h>
#include <stdio.h>
#define F_CPU 1000000L // 8Mhz internal osc, divided by 8 (fuse)
#include <util/delay.h>

//VFD 7 outputs mapped to PORTA
int seg_1 = 5;
int seg_2 = 4;
int seg_3 = 3;
int seg_4 = 6;
int seg_5 = 0;
int seg_6 = 1;
int seg_7 = 2;

int trigger = 0;

//#define DELAY_MAX 512 // value is defined within the for loop itself
#define DELAY_MIN 1
/*
# "Value of DELAY_MAX and DELAY_MAIN should be from range <1, 2^16>"
# "Value of DELAY_MAX should be greater then DELAY_MIN"
*/

/*
Mode 1 - PB0
Move from segment to segment, bounce back.

Mode 2 - PB1
Single segment fade on off, moves upwards then resets

Mode 3 - PB2
Full segment illumination

Mode 4 - PA7
*/

int main(void)
{
// Defining Outputs

// Enable first 7 ports as Output for VFD segments
DDRA |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6);
// Enable selector switch Input for mode selection
DDRA &= ~(1 << 7);
DDRB &= ~(1 << 0) | (1 << 1) | (1 << 2);

// Configure pull up resistors
PORTA |= (1 << 7);
PORTB |= (1 << 0) | (1 << 1) | (1 << 2);

uint16_t delay = DELAY_MIN;
uint8_t dir = 0;

int count = 0; // counter for break, for Mode 2 pulse

while (1)
{

if( !(PINB & (1 << 0)) ) // if PB0 is pressed, Mode 1
{
for (int i = 0; i < 7; i++) // Count upwards
{
if( !(PINB & (1 << 1)) ) {trigger = 1;} // if PB1 is pressed set trigger, break out of loop
if (trigger == 1)
{
clear();
trigger = 0;
break;
}
int a = select(i);
int b;
PORTA |= (1 << a); // Turn on segment
if (i > 0) // turn off the previous segment once "i" is at least 1
{
b = select(i-1);
PORTA &= ~(1 << b); // Turn off segment
}
_delay_ms(1000);

if (i == 6) {PORTA &= ~(1 << seg_7);} // Turn off final segment
}

for (int t = 5; t > 0; t--) // Count downwards
{
if( !(PINB & (1 << 1)) ) {trigger = 1;} // if PB1 is pressed set trigger, break out of loop
if (trigger == 1)
{
clear();
trigger = 0;
break;
}
int c = select(t);
int d;
PORTA |= (1 << c); // Turn on segment

if (t < 6)
{
d = select(t+1);
PORTA &= ~(1 << d); // Turn off segment
}
_delay_ms(1000);

if (t == 1) {PORTA &= ~(1 << seg_2);} // Turn off final segment

}
}
if( !(PINB & (1 << 1)) ) // if PB1 is pressed, Mode 2
{	int DELAY_MAX = 800;
for (int w = 0; w < 7; w++) // Count upwards
{
if( !(PINB & (1 << 0)) | !(PINB & (1 << 2)) ) {trigger = 1;} // if PB0 or PB2 is pressed set trigger, break out of loop
if (trigger == 1)
{
clear();
trigger = 0;
break;
}
int a = select(w);
//int b;
//PORTA |= (1 << a); // Turn on segment

for (int m = 0; m < 10000; m++)
{
if( !(PINB & (1 << 0)) | !(PINB & (1 << 2)) ) {trigger = 1;} // if PB0 or PB2 is pressed set trigger, break out of loop
if (trigger == 1)
{
clear();
trigger = 0;
break;
}

PORTA &= ~(1 << a); // Turn segment off
_delay_loop_2(delay);

PORTA |= (1 << a); // Turn segment on
_delay_loop_2(DELAY_MAX - delay);

if (dir)
if (++delay >= (DELAY_MAX - 1))
{
dir = 0;
count++;
}
}
else
if (--delay <= DELAY_MIN)
{
dir = 1;
}
}

if (count == 1)
{
count = 0;
break;
}
}

PORTA &= ~(1 << a); // Turn off segment

}
}
if( !(PINB & (1 << 2)) ) // if PB2 is pressed, Mode 3
{
PORTA |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); // Turn on all segments
trigger = 1;
}
if( !(PINA & (1 << 7)) ) // if PA7 is pressed, Mode 4
{
int DELAY_MAX = 1024;

if (trigger == 1) {
trigger = 0;
clear();
}

//PORTA |= (1 << seg_3); // compare the brightness of this led, test case
//PORTA |= (1<<seg_4);

clear(); // LED off
_delay_loop_2(delay);

PORTA |= (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6); // Turn all segments on
_delay_loop_2(DELAY_MAX - delay);

if (++delay >= (DELAY_MAX - 1)) dir = 0;
}
if (--delay <= DELAY_MIN) dir = 1;}

}

}
}

// Function for mapping the segment outputs in the correct order
int select(int a) {
if (a == 0){return seg_1;}
if (a == 1){return seg_2;}
if (a == 2){return seg_3;}
if (a == 3){return seg_4;}
if (a == 4){return seg_5;}
if (a == 5){return seg_6;}
if (a == 6){return seg_7;}
}

void clear() { // Function for clearing all the segments
PORTA &= ~(1 << 0);
PORTA &= ~(1 << 1);
PORTA &= ~(1 << 2);
PORTA &= ~(1 << 3);
PORTA &= ~(1 << 4);
PORTA &= ~(1 << 5);
PORTA &= ~(1 << 6);
}



Subscription
If you liked this contribution, feel free to subscribe to our newsletter: