Saturday, June 3, 2017

Library for interfacing AVR microcontrollers with ET16312N VFD (Vacuum Fluorescent Display) driver

I had a broken Philips DVP5960 DVD player and I thought it's a good idea to salvage and use the VFD display in some project. And so this code was born.

Vacuum Fluorescent Display (VFD) from a DVD Player controlled by AVR MCU
Vacuum Fluorescent Display (VFD) from a DVD Player controlled by AVR MCU

To know more about the communication protocol between the microcontroller and the VFD driver chip, visit this post Salvage a VFD from a broken DVD.

Currently supported VFD - Vacuum Fluorescent Display chip drivers by OnVFD:

- ET16312N
4 digits, 16 segments
5 digits, 16 segments
6 digits, 16 segments
7 digits, 15 segments
8 digits, 14 segments
9 digits, 13 segments
10 digits, 12 segments
11 digits, 11 segments

You only need three pins from the MCU to communicate with the VFD driver: CS, SCLK and DATA. I've described what each one does in the above linked post.

Included functions

void VFDInitialise(void);

- used to initialise the display. This function sets the MCU pins and the number of VFD digits and segments, clears the display and sets the cursor to first digit.

void VFDWriteString(const char *string);

- a function to display string data type
The macro VFDWriteStringPosition(string, position) can be used to write a string to a specific position (only on X axes).

void VFDWriteInt(int16_t number, int8_t nrOfDigits, bool displayColonSymbol);

- a function for displaying int data type.
int16_t number - the first parameter is the number to be displayed
int8_t nrOfDigits - number of digits the number is formed by. If this number is greater than the actual number of digits, the displayed number will be padded with zeros.
bool displayColonSymbol - it can be true or false and indicates whether you want the colon symbol to be displayed or not. Note that the colon is only available at a certain digit position and depends on the type of VFD display.
The macro  VFDWriteIntPosition(number, nrOfDigits, position, displayColonSymbol) can be used to write an int to a specific position (only on X axes).

void VFDClear(void);

- clears the display by writing a space on all the digits and sets the cursor back to position 1.

void VFDSetCursorPosition(uint8_t position);

- accepts a number from 1 to VFD's maximum number of digits. If position is 0 the cursor will be set at the last digit, if the position is greater than the maximum digits the cursor will be set to first digit.

The macro VFDHome() can be used to move the cursor to first digit.


- macro. Turn on the display. Dimming parameter can be a number from 0 to 7 - 0 is minimum and 7 is maximum.


 - maco. Turns off the display. Key scan continues.


 - macro. Change the display's brightness/dimming. Brightness parameter can be a number from 0 to 7 - 0 is minimum and 7 is maximum.

Functions related to VFD driver chip

uint8_t VFDReadKey(void);

- reads the key matrix and return a byte with the key button that is currently pressed. On my DVD's front panel there are only 4 buttons so they are not really arranged in a full matrix.

uint8_t VFDReadKeyButton(uint8_t delay);

- this function uses the above function to read the key matrix with some functions added. It returns 0 if no button is pressed or the number of the button pressed. In my case the returned byte from the VFD chip driver looks like this 0b0000 0001 if no button is pressed. The first LSB is always 1. If button 1 is pressed the byte is 0b0000 0011, button 2 is 0b0000 0101. So this function counts from the LSB not including the first bit and returns the button number. How the buttons correspond to numbers depends on the board layout.

uint8_t delay - this number depends on the delay time in your code. All this number is used for is to increment a global variable each time the function runs and when the number of the variable match this parameter the function returns the pressed button. Is like debouncing. I didn't add the delay inside the function so the main code doesn't have to wait for this function to terminate. The proper way would be to use an interrupt but this is easier to implement. You could add a 1ms delay in the main loop for example and make the delay equal to 50 and the button will be read every 50 ms.

uint8_t VFDReadSW(void); 

most VFD driver chips include 4-bit general purpose input port and can be used to attach 4 buttons. It returns 1 byte binary data. The first bit represents button 0, second bit button 2, third bit button 3 and fourth bit button 4.

void VFDControlLEDs(uint8_t leds);

- control up to 4 leds. First bit represents led 0 and bit 4 represents led 4. For the VFD driver chip 0 means led ON and 1 means led OFF but the function takes care of bit flipping.

Other extra functions

void VFDdisplayAllFonts(void);

- display all available characters by scrolling them. The speed can be changed by modifying VFD_SCROLL_SPEED.

void VFDscrollText(const char *string);

- pass a string to this function to be scrolled on the display. The speed can be changed by modifying VFD_SCROLL_SPEED. After the function completes, the cursor is set to first digit. Clearing the display is not necessary.


- macro. Display the degree symbol without C or F.

void VFDBusySpinningCircle(void);

- busy indicator formed by 4 segments at 45 degrees, each one fading behind the other and spinning in circle at the same time at about 1 revolution per second. Text can be displayed in the mean time. Check the video to see it.

void VFDSegmentsTest(void);

- lights up a segment from 1 to 16 every 2 seconds and displays the number. Useful if the screen is not supported by the library and you want to modify the code.

void VFDBlinkUserInput(uint8_t cursorStartPosition, uint8_t length);
- i have made this function for myself and i left it in the code i case someone want to use it and is not too confusing. It is useful for a menu. When the user selects something to modify e.g. time, temperature, etc., you can make a function that enters in a loop until the user presses a button. In that loop you can put this function and the characters at the cursor position will blink showing to the user what is about to modify. If there is a space than an underscore will be blinked. To keep track of the cursor position you can use cursorPositionX because the code will correct for 0 or over the number of digits values.
uint8_t cursorStartPosition - from what position to blink the display.
uint8_t length - how many characters to blink.


How to use OnVFD

Open the OnVFD.h file and in the SETUP section modify the MCU IO replacing the DDR and PORT and PIN data according to your setup.

Then bellow that you have the display configurations.

// VFD Display settings
// Number of segments for each digits 
#define VFD_SEGMENTS    15
// Number of digits
#define VFD_DIGITS      7

// The scroll speed in milliseconds used by the VFDscrollText function
#define VFD_SCROLL_SPEED  400 // In milliseconds

To save space many extra functions are skipped by the compiler using defines. If you need any of those functions write 1 to that define.

// Write 1 to enable the following functions
#define ENABLE_READ_SW_AND_KEY        0 // Enables functions to read SW and KEYs
#define ENABLE_TEXT_SCROLL            0 // Function for scrolling a text
#define ENABLE_DISPLAY_ALL_FONTS      0 // Display and scroll all available characters
#define ENABLE_MENU_INTERFACE_RELATED 0 // Enables VFDBlinkUserInput function
#define ENABLE_BUSY_INDICATOR         0 // Enables VFDBusySpinningCircle function
#define ENABLE_SEGMENTS_TEST          0 // A function to test segment numbering

#include "ET16312N.h"

int main(void){

    // Display text
    VFDWriteString("ON VFD");
    // Display time with colon
    // On my VFD display the colon is available when cursor 
    // is on digits 3 and 5. So we start from position 2 and display
    // two digits 21 so digit 1 will be on position 3 and the colon 
    // will be available.
    VFDWriteInt(21, 2, true); // hours
    // Minutes will be 01 even if we pass 1, the second parameter is 2 
    // meaning that two digits will be displayed 01.
    VFDWriteInt(1, 2, true); // minutes
    VFDWriteInt(10, 2, false); // seconds

    // Dim the display
    // Turn off and on the display
    // Busy indicator example
    VFDscrollText("BUSY INDICATOR");
    for(i=0; i<8000; i++){



Download OnVFD library:

ET16312N v1.0


  1. Greetings to the Author of the project; I have some questions:
    1) First, when I upload the "example" sketch to Arduino, it is showing the following: 'VFDscrollText' was not declared in this scope. (Lib ET16312N is installed)
    2) My VFD is 7 Digits with 13 Segments and uses the PT6312 as controller, which, I think is identical to the ET16312N, so it can be used with your code?
    3) My VFD segments are distributed differently; based on your drawing, i changed mine with letters, and the letter "b" lights up a complete segment, from top to bottom. So, could you help me make this watch?
    My thanks in advance,

    |\ | /|
    f | c | d | e
    | \ | / |
    -h-- b--g-
    | /|\ |
    j | k | l | i
    | / | \ |

    1. Hi Daniel. For the function "VFDscrollText" to be included you need to set this define to 1 in the setup section:
      #define ENABLE_TEXT_SCROLL 1
      Some functions that not all users would use are not included by default to make the code smaller.

      2) Probably yes. I wrote the code a long time ago and I would have to compare the datasheets of both IC's to see what protocols are they using but it doesn't hurt to try and see if it works.

      3) You would need to re-write FONTS array in that case. Also be sure these defines are according to your display:
      #define VFD_SEGMENTS 15 // replace with 13
      #define VFD_DIGITS 7

      If you rebuild the FONTS array, start with a simple character like '-' which is currently:
      {0b10000001, 0b00000001}, // - (bits 16, 9, 1)
      from right to left put a 1 to light the segments needed to form a character. With my display to form the - sign bits 1, 9 and 16 must be lit so notice that counting from right to left those bits are 1. If you don't know which segment is 1 replace the two bytes with:
      {0b00000000, 0b00000001}, then display the '-' sign and note what segment is lit and draw a diagram with it's position then check what segment bit 2 will light up
      {0b00000000, 0b00000010}, and so on.

  2. PS. After I posted the drawing, I saw that it was all changed; is it possible to send it in another way? Thanks

  3. Hi ILiviu
    I haven't tested your code yet and I have another question:
    Which the ET16312N's CS, CLK and DATA pins correspond with the Arduino because it is not specified in the code; Thanks

    1. Hi. Any pins you want can be used. By default they are defined like so but they can be changed with any pins:

      // MCU IO
      #define VFD_PIN_PORT PIND

      #define VFD_CS_DDR DDRD
      #define VFD_CS_PORT PORTD
      #define VFD_CS_PIN PD0

      #define VFD_SCLK_DDR DDRD
      #define VFD_SCLK_PORT PORTD
      #define VFD_SCLK_PIN PD1

      #define VFD_DATA_DDR DDRD
      #define VFD_DATA_PORT PORTD
      #define VFD_DATA_PIN PD2

      CS is on PD0, SCLK is on PD1 and DATA on PD2. If I remember correctly, all 3 pins must be on the same port.
      Now, to find out to what pins on the Arduino they correspond search for images using these keywords 'arduino pin to port mapping'. For Arduino Uno they will be PD0 - pin 0, PD1 - pin 1 and PD2 pin 2. I personally don't use Arduino but it should work.
      Wish you success with your project.

  4. Thank you ILiviu for the prompt reply!
    You say that you personally do not use Arduino, but that code shown at the bottom of the page called "Example:" cannot be compiled in the Arduino IDE? Although I found this line strange: int main (void) {
    And I would love to make this VFD Clock with Arduino!

    1. It can be compiled using Arduino with few exceptions. The code inside the main() until the while() loop must be placed inside the setup() function of the Arduino. What is inside the while() loop must be placed inside the loop() function of Arduino. There is also the part of using external libraries with Arduino but you need to search online for this because I'm not familiar with the procedure but I know it can be done because many have used some of my other libraries with Arduino.

    2. For testing I recommend using single functions first, like displaying numbers and see if it works. Less code easy to debug. The example code makes use of the buttons that the DVD player was using. If your layout is different it might not work. The simplest way would be to write the clock app from scratch using the provided functions to control the display.

  5. I'm in the very same situation as you, I have a broken DVP5960.
    I'm very happy to find your blog, thank you for the instructions!

    However, I would have a question: have you find any real life application where this setup can be useful? I mean, using the front PCB with the builtin PSU as a table clock is a kind of waste.
    I appreciate it as a challenge and experimenting, but what else it could be used for?

    1. I think the display can be used for a clock or something else where a simple display is needed, by making the display more compact. For this there are two main design challenges to consider.

      First, the power supply. The positive voltages can easily be produced using cheap DC to DC converter modules from China. Don't know if there are modules for negative voltages though. I am planning of using MC34063 for negative voltages. This IC's are the cheapest among the DC converters and can also be used to boost or lower the voltages.

      Second, the display board can be made smaller. From what I remember, I was looking at the schematic and board layout and I saw I could cut the board around the display where the buttons are. This is a bit risky if not done properly. Another option is to desolder all components and make a new smaller board but that requires more knowledge on how VFD hardware works and I have more study to do on that.

      I don't think is practical to run a vacuum display on batteries so for the power source I would use a 12V plug adapter. I am planning to do all these and make the display more compact to use it in another project but after I finish the battery charger I'm working on.

      I hope you can find it some good use.