从零开始写驱动——vfd专用驱动芯片HT16514并行驱动程序编写

前言

一直看别人搞的 vfd 很漂亮,前段时间淘了个 vfd 模块来,但没有模块资料,还好芯片没有打磨的,良心商家啊。周末抽空来研究一下这个东西。

 

从零开始

打开外壳

测试线路

  1. 查看芯片是 HT16514,去搜索到芯片手册(博客没有找到上传附件的功能,不然肯定要放一个附件的)
  2. 根据芯片手册的引脚图,测试插针与引脚的电阻,从而确定每个引脚的功能
  3. 测试后发现用的是 M68 并行接口方式,而且电路上没有做跳线开关调整功能,要是串行该多好啊,这并行接口岂不是要用光 arudino uno 的引脚了。不过反正是试着驱动它,是串行还是并行都一样,以后真有需要再改硬件
  4. 下面就是根据芯片手册来写驱动程序了
    查看电平稳定时间都是用 ns 做单位的,对于 arduino 的执行效率来说应该是不需要考虑这个问题,因此驱动程序里没有加上延时处理,如果哪天用的板子速度太快,执行结果不正常的话,那就需要考虑加上延时了。

驱动编写

最主要就是根据时序图,及命令表来设置每个引脚的电平高低,以及电平变化时间,这就完成了:

驱动程序

没有找到上传附件的功能,那就把程序完整贴出来喽。

把驱动写成一个类的形式,方便使用。并行接口有 8 位和 4 位两种模式,用不同的构造函数来初始化实例来实现两种模式的区分。8 位模式的话,反正都要用这么多引脚了,直接使用 uno 的 PORTD 来当数据 IO 口,方便写程序操作,但下载程序的时候要注意拔掉 0 1 两脚接线以免下载失败。

头文件:

// ----------------------------------------------------------------------------
// driverHT16514Parallel.h
//
// Created 2015-07-04
// By seesea <seesea2517#gmail#com>
//
// HT16514 并行驱动
// 根据当前需要只实现部分功能
// ----------------------------------------------------------------------------

#ifndef _DRIVERHT16514PARALLEL_H_
#define _DRIVERHT16514PARALLEL_H_

#include <arduino.h>

#define DATA_PORT  PORTD
#define DDR_PORT   DDRD

class HT16514Parallel
{
private:
    char RW;            // RW
    char E;             // E
    char RS;            // RS
    char dataPins[4];   // 四位数据线的时候使用的引脚,数据引脚存放顺序由高到低,即 0:DB7 ... 3:DB4
    static const byte dataPinMask[4]; // 四位数据线时使用的引脚掩码

    bool isDisplayOn;   // 显示是否打开,是的话 true
    bool isBlink;       // 是否开启闪烁,是的话 true
    bool isShowCursor;  // 是否显示光标,是的话 true

    char dataLength;    // 数据长度,使用 PORTD 的时候是 8 位,使用四位数据线的时候是 4 位
    char rowNumber;     // 显示器行数

    char curRow;        // 输出位置之当前行
    char curCol;        // 输出位置之当前列

public:  
    HT16514Parallel(char _RW, char _E, char _RS);        // 构造函数,使用 PORTD 来做数据输出
    HT16514Parallel(char _RW, char _E, char _RS,         // 构造函数,使用四位并行方式,用指定的四个引脚来做数据输出
                    char D7,  char D6, char D5, char D4);

    void sendCommand(byte cmd);                          // 发送命令
    void sendData(byte data);                            // 发送数据

    void init(char dl);                                  // 初始化模块数据
    void initDisplay();                                  // 初始化显示屏
    void clear();                                        // 清屏
    void moveCursorHome();                               // 光标回左上角
    void displayOn();                                    // 显示开
    void displayOff();                                   // 显示关
    void blinkOnCursor();                                // 光标闪烁开
    void blinkOffCursor();                               // 光标闪烁关
    void showCursor();                                   // 显示光标
    void hideCursor();                                   // 隐藏光标
    void shiftLeftDisplay();                             // 左移显示内容
    void shiftRightDisplay();                            // 右移显示内容
    void shiftLeftCursor();                              // 左移光标
    void shiftRightCursor();                             // 右移光标
    void setBrightLevel(char level);                     // 设置亮度,共有四级:0 级最亮,4 级最暗
    void putChar(char ch, char row = -1, char col = -1); // 于指定位置显示指定字符,不指定位置则在当前位置显示
    void print(char *str, char row = -1, char col = -1); // 于指定位置显示指定字符串,不指定位置则在当前位置显示
};

#endif

类实现文件:

// ----------------------------------------------------------------------------
// driverHT16514Parallel.cpp
//
// Created 2015-07-04
// By seesea <seesea2517#gmail#com>
//
// HT16514 并行驱动
// 根据当前需要只实现部分功能
// ----------------------------------------------------------------------------

#include "driverHT16514Parallel.h"

const byte HT16514Parallel::dataPinMask[4] = { 0x80, 0x40, 0x20, 0x10 };

// 构造函数
HT16514Parallel::HT16514Parallel(char _RW, char _E, char _RS)
{
    RW = _RW;
    E  = _E;
    RS = _RS;

    init(8);
}

// 构造函数,使用四位并行方式,用指定的四个引脚来做数据输出
HT16514Parallel::HT16514Parallel(char _RW, char _E, char _RS, char D7, char D6, char D5, char D4)
{
    RW = _RW;
    E  = _E;
    RS = _RS;

    dataPins[0] = D7;
    dataPins[1] = D6;
    dataPins[2] = D5;
    dataPins[3] = D4;

    init(4);
}

// 发送命令给 HT16514
void HT16514Parallel::sendCommand(byte cmd)
{
    char i;

    digitalWrite(RS, LOW);
    digitalWrite(RW, LOW);
    
    if (dataLength == 8)
    {
        digitalWrite(E,  HIGH);
        DATA_PORT = cmd;
        digitalWrite(E,  LOW);
    }
    else
    {
        // 先写高四位
        digitalWrite(E,  HIGH);
        for (i = 0; i < 4; ++i)
        {
            digitalWrite(dataPins[i], ((dataPinMask[i] & cmd) ? HIGH : LOW));
        }
        digitalWrite(E,  LOW);

        // 低四位移高四位后,同上操作
        cmd <<= 4;
        digitalWrite(E,  HIGH);
        for (i = 0; i < 4; ++i)
        {
            digitalWrite(dataPins[i], ((dataPinMask[i] & cmd) ? HIGH : LOW));
        }
        digitalWrite(E,  LOW);
    }
}

// 发送数据给 HT16514
void HT16514Parallel::sendData(byte data)
{
    char i;

    digitalWrite(RS, HIGH);
    digitalWrite(RW, LOW);

    if (dataLength == 8)
    {        
        digitalWrite(E,  HIGH);
        DATA_PORT = data;
        digitalWrite(E,  LOW);
    }
    else
    {
        // 先写高四位
        digitalWrite(E,  HIGH);
        for (i = 0; i < 4; ++i)
        {
            digitalWrite(dataPins[i], ((dataPinMask[i] & data) ? HIGH : LOW));
        }
        digitalWrite(E,  LOW);

        // 低四位移高四位后,同上操作
        data <<= 4;
        digitalWrite(E,  HIGH);
        for (i = 0; i < 4; ++i)
        {
            digitalWrite(dataPins[i], ((dataPinMask[i] & data) ? HIGH : LOW));
        }
        digitalWrite(E,  LOW);
    }
}

// 初始化
void HT16514Parallel::init(char dl)
{
    pinMode(RW, OUTPUT);
    pinMode(E,  OUTPUT);
    pinMode(RS, OUTPUT);
    
    if (dl == 8)
    {
        DDR_PORT = 0xFF;
    }
    else
    {
        for (char i = 0; i < 4; ++i)
        {
            pinMode(dataPins[i], OUTPUT);
        }
    }

    dataLength = dl;
    rowNumber  = 2;     // 针对当前的 1602 vfd 直接写 2 了,以后有需要再加扩展

    curRow = 0;
    curCol = 0;

    initDisplay();
}

// 初始关闭闪烁和光标显示(这也是reset后的状态)
void HT16514Parallel::initDisplay()
{
    isDisplayOn  = true;
    isBlink      = false;
    isShowCursor = false;

    // 如果是四位模式,则需要手工设置。因为默认是八位
    // 并且由于初始时是八位模式,所以要先按八位发命令设置四位模式
    if (dataLength == 4)
    {
        byte cmd = 0x20;

        if (rowNumber == 2)
            cmd |= 0x08;

        // 临时设置为八位发送设置四位的命令
        dataLength = 8;
        sendCommand(cmd);
        dataLength = 4;
    }

    moveCursorHome();    
    displayOn();     // 开启显示,关闭闪烁和光标显示
}

// 清屏
void HT16514Parallel::clear()
{
    sendCommand(0x00);
}

// 移动光标到左上角
void HT16514Parallel::moveCursorHome()
{
    curRow = 0;
    curCol = 0;

    sendCommand(0x02);
}

// 开显示
void HT16514Parallel::displayOn()
{
    byte cmd = 0x08;

    isDisplayOn = true;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 关显示
void HT16514Parallel::displayOff()
{
    byte cmd = 0x08;

    isDisplayOn = false;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 光标开闪烁
void HT16514Parallel::blinkOnCursor()
{
    byte cmd = 0x08;

    isBlink = true;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 光标关闪烁
void HT16514Parallel::blinkOffCursor()
{
    byte cmd = 0x08;

    isBlink = false;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 开光标
void HT16514Parallel::showCursor()
{
    byte cmd = 0x08;

    isShowCursor = true;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 关光标
void HT16514Parallel::hideCursor()
{
    byte cmd = 0x08;

    isShowCursor = false;

    if (isDisplayOn)
        cmd |= 0x04;

    if (isBlink)
        cmd |= 0x01;

    if (isShowCursor)
        cmd |= 0x02;

    sendCommand(cmd);
}

// 左移显示内容
void HT16514Parallel::shiftLeftDisplay()
{
    sendCommand(0x18);
}

// 右移显示内容
void HT16514Parallel::shiftRightDisplay()
{
    sendCommand(0x1C);
}

// 左移光标
void HT16514Parallel::shiftLeftCursor()
{
    sendCommand(0x10);
}

// 右移光标
void HT16514Parallel::shiftRightCursor()
{
    sendCommand(0x14);
}
   
// 设置亮度,共有四级:0 级最亮,4 级最暗
void HT16514Parallel::setBrightLevel(char level)
{
    byte cmd = 0x20;

    if (level < 1 || level > 4)
        return;

    if (dataLength == 8)
        cmd |= 0x10;

    if (rowNumber == 2)
        cmd |= 0x08;

    cmd |= level;

    sendCommand(cmd);
}

// 于指定位置显示指定字符,若未指定位置则以当前位置显示
void HT16514Parallel::putChar(char ch, char row, char col)
{
    byte cmd = 0x80;
    char pos = 0;
    bool isCtrlChar = false;

    switch(ch)
    {
        case '\r': curCol = 0;  isCtrlChar = true; break;
        case '\n': ++curRow;    isCtrlChar = true; break;
        case '\b': --curCol;    isCtrlChar = true; break;
        case '\t': curCol += 4; isCtrlChar = true; break;
        case '\0': return;
        default  : break;
    };

    if (isCtrlChar)
    {
        pos = 0x40 * curRow + curCol;
        sendCommand(cmd | pos);
        return;
    }
    
    if (row >= 0 && col >= 0)
    {
        pos = 0x40 * row + col;

        sendCommand(cmd | pos);
        
        curRow = row;
        curCol = col;
    }
    
    sendData(ch);
    ++curCol;
}

// 于指定位置显示指定字符串,若未指定位置则以当前位置显示
void HT16514Parallel::print(char *str, char row, char col)
{
    char *p = str;
    
    putChar(*p, row, col);
    for (p = str + 1; *p != '\0'; ++p)
    {
        putChar(*p);
    }
}

arduino 测试程序:

// ----------------------------------------------------------------------------
// vfdHT16514Parallel.ino
// 
// Created 2015-07-04
// By seesea <seesea2517#gmail#com> 
// 
// HT16541 VFD 驱动测试程序
// ----------------------------------------------------------------------------
#include "driverHT16514Parallel.h"

#define  RW  10
#define  E   9
#define  RS  8

HT16514Parallel vfd(RW, E, RS, 7, 6, 5, 4);
// HT16514Parallel vfd(RW, E, RS);

void setup()
{
    vfd.clear();
    vfd.moveCursorHome();
    vfd.setBrightLevel(4);
    vfd.print("Have a good day!", 0, 0);
    vfd.print("09:45:00", 1, 4);
}

void loop()
{
    static unsigned char sec = 0;
    char str[5];

    sprintf(str, "%02d", sec);
    vfd.print(str, 1, 10);
    delay(100);
    sec = (sec + 1) % 60;
 }

 

显示效果

posted @ 2015-07-05 10:54  听听海看看云  阅读(3137)  评论(0编辑  收藏  举报