从零开始写驱动——vfd专用驱动芯片HT16514并行驱动程序编写
前言
一直看别人搞的 vfd 很漂亮,前段时间淘了个 vfd 模块来,但没有模块资料,还好芯片没有打磨的,良心商家啊。周末抽空来研究一下这个东西。
从零开始
打开外壳
测试线路
- 查看芯片是 HT16514,去搜索到芯片手册(博客没有找到上传附件的功能,不然肯定要放一个附件的)
- 根据芯片手册的引脚图,测试插针与引脚的电阻,从而确定每个引脚的功能
- 测试后发现用的是 M68 并行接口方式,而且电路上没有做跳线开关调整功能,要是串行该多好啊,这并行接口岂不是要用光 arudino uno 的引脚了。不过反正是试着驱动它,是串行还是并行都一样,以后真有需要再改硬件
- 下面就是根据芯片手册来写驱动程序了
查看电平稳定时间都是用 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; }