Categories
Electronics Projects

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:

Samples of VFD IV-25

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:

Mode 1Move from segment to segment, bounce back.
Mode 2Single segment fade on off, moves upwards then resets.
Mode 3Full segment illumination, static.
Mode 4Full segment illumination, pulse fade.

1.2.1 Mode 1

1.2.2 Mode 2

1.2.3 Mode 3

1.2.4 Mode 4

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:

MT3608 to supply VFDs for a cool night light.

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

More information about how the MT3608 module works can be found in this link.

2.2 Schematics

The project schematics can be downloaded from this link. Alternatively, they can be obtained by downloading the following picture:

Schematics of VFD Night Light with Attiny84

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:

Self oscillating AC Square Wave
Copied from LM9022 datasheet.

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.

MCU to VFD Interface

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 1: Gallery

VFD Night Light 1 of 3
VFD Night Light 2 of 3
VFD Night Light 3 of 3

Annex 2: List of Materials

ReferenceNumber of Units
IV-25 VFD1
Attiny84 MCU1
LM48711
2N3904 NPN7
2N3906 PNP7
Battery 186501
ON-OFF Switch1
SP4T Rotary Switch1
MT3608 Module2
TP4056 Charging Module1
Resistors
47K14
10K10
4K77
2K21
1001
Capacitors
10 uF Electrolytic1
1 uF Electrolytic1
0.01 uF Ceramic1

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;

// @@@@@@@@@@ fade code referenced from <blog.podkalicki.com/attiny13-led-fading-with-delay-function/>
//#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
Full segment illumination, pulse fade
*/




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);
	
// @@@@@@@@@@ fade code
	        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) 
					{ // fade-in
						if (++delay >= (DELAY_MAX - 1)) 
						{
							dir = 0;
							count++;
						}
					}
					else 
					{ // fade-out
					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);
		
		//@@@@@@@@@@@@@@@ fade code
	
	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 (dir) { // fade-in
		if (++delay >= (DELAY_MAX - 1)) dir = 0;
	}
	else { // fade-out
	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: