Search code examples
c++spiraspberry-pi3vfd

Futaba GP9002A01A graphic VFD on Raspbery Pi


I've got this Futaba GP9002A01A graphic VFD and would like to run it on a Raspberry Pi 3: https://cdn-shop.adafruit.com/datasheets/GP9002A+Appnote.pdf

This issue is the following: It would only display pixel snow no matter which data I send it. Here is the sourcecode:

vfd.h:

// Header file for vfd.cpp class file.
// This class controls the Futaba GP9002A01A graphic VFD attached to the host via SPI link.

#ifndef VFD_H
#define VFD_H

using namespace std;

class vfd {
private:
  static const int CHANNEL                      =  0;             // SPI channel 0 means that we use pin CE0_N (#24) to address the display.
  static const int CD_PIN                       = 17;             // Additionally, our VFD requireds an extra GPIO input (here it's pin #11 aka GPIO17).
  static const uint32_t wait_interval_extralong = (uint32_t) 400; // Wait interval used to let the VFD module clear its display buffer (command 0x06)
  static const uint32_t wait_interval_long      = (uint32_t) 1;   // Wait interval used after each command
  static const uint32_t wait_interval_short     = (uint32_t) 1;   // Wait interval used before each command
  static bool  on;

public:
  static int  Initialize (); // Initializes the display.
  static bool IsSwitchedOn (); // Whether the display is currently on.
  static void DisplayChar (int spi_handle, char pos_x, char pos_y, char size_x, char size_y, string text); // Displays a character.
  static void DisplayPixel (int spi_handle, char addr_upper, char addr_lower, char pixels []); // Displays a series of pixels.
  static void SwitchOn (int spi_handle); // Switches the display on.
  static void SwitchOff (int spi_handle); // Switches the display off.
  static void Clear (int spi_handle);     // Clears the display.
};

#endif

vfd.cpp:

#include <iostream>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "vfd.h"

extern "C" {
#include <pigpio.h>
}

using namespace std;

bool     vfd::on;

int vfd::Initialize ()
{
  int fd_spi;

  on = false;

  cout << "Initializing display...";

  fd_spi = spiOpen(CHANNEL, 500000, 3);

  if (fd_spi == -1)  cout << "failed! Error code: " << errno  << endl;
  else               cout << "done. Handler: " << fd_spi << endl;

  gpioSetMode (CD_PIN, PI_OUTPUT);

  return fd_spi;
}

void vfd::DisplayPixel (int spi_handle, char addr_upper, char addr_lower, char pixels [])
{
  char buffer [100];

  gpioWrite (CD_PIN, 1);
  buffer [0] = 0x01;                      // Activates display buffer #1.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  buffer [0] = 0x0E;                      // Sets the lower portion of the pixel address.
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  gpioWrite (CD_PIN, 0);
  buffer [0] = addr_lower;                // address
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  gpioWrite (CD_PIN, 1);
  buffer [0] = 0x0F;                      // Sets the upper portion of the pixel address.
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  gpioWrite (CD_PIN, 0);
  buffer [0] = addr_upper;                // address
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  gpioWrite (CD_PIN, 1);
  buffer [0] = 0x08;                      // Illuminates the selected pixel.
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  gpioWrite (CD_PIN, 0);
  buffer [0] = pixels [0];                // pixel data
  gpioDelay (wait_interval_short);
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  cout << "Splashscreen displayed." << endl;
}

void vfd::Clear (int spi_handle)
{
  char buffer [100];

  gpioWrite (CD_PIN, 1);
  buffer [0] = 0x00;                      // De-activates both display buffers.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  buffer [0] = 0x06;                      // Clears the display.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_extralong);
  gpioWrite (CD_PIN, 0);
}

void vfd::SwitchOn (int spi_handle)
{
  char buffer [100];

  gpioWrite (CD_PIN, 1);
  gpioDelay (wait_interval_short);
  buffer [0] = 0x14;                      // Sets the operating mode of the display.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  gpioWrite (CD_PIN, 0);
  gpioDelay (wait_interval_short);
  buffer [0] = 0x10;                      // Green-white without grayscale.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  gpioWrite (CD_PIN, 1);
  gpioDelay (wait_interval_short);
  buffer [0] = 0x13;                      // Sets the brightness of the display.
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);
  gpioWrite (CD_PIN, 0);
  gpioDelay (wait_interval_short);
  buffer [0] = 0x12;                      // 70%
  spiWrite (spi_handle, buffer, 1);
  gpioDelay (wait_interval_long);

  vfd::Clear (spi_handle);

  vfd::on = true;
  cout << "Display ready." << endl;
}

void vfd::SwitchOff (int spi_handle)
{
  vfd::Clear (spi_handle);

  vfd::on = false;
  cout << "Display switched off." << endl;
}

bool vfd::IsSwitchedOn ()
{
  return on;
}

What's wrong with my code? Thank you for any useful hints!


Solution

  • OK, I found a solution myself: The bit-endianness was wrong! The RPi 3 uses big-endian on its SPI interface, but the display expects little-endian. So I did a little research and found this simple algo to reverse the bits in a byte:

    char vfd::Reverse (char b)
    {
      b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
      b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
      b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
      return b;
    }
    

    All I had to do then in the code addressing the display is to use the abovementioned function:

    buffer [0] = Reverse (0x01);
    

    Now the display works perfectly. :)