Raspberry Pi 上的 Futaba GP9002A01A 图形 VFD

Futaba GP9002A01A graphic VFD on Raspbery Pi

我有这个 Futaba GP9002A01A 图形 VFD,想 运行 它在 Raspberry Pi 3 上:https://cdn-shop.adafruit.com/datasheets/GP9002A+Appnote.pdf

这个问题是这样的:无论我发送什么数据,它都只会显示像素雪花。这是源代码:

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;
}

我的代码有什么问题?感谢您提供任何有用的提示!

OK,我自己找到了解决办法:位字节序错了! RPi 3 在其 SPI 接口上使用大端,但显示需要小端。所以我做了一些研究,发现了这个简单的算法来反转一个字节中的位:

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;
}

然后我在处理显示的代码中所要做的就是使用上述函数:

buffer [0] = Reverse (0x01);

现在显示效果很好。 :)