三星VFD荧光电子屏-显示模块
基于三星米字屏设计的VFD显示模组。
VFD显示模组具有9位显示内容,可显示数字和字母 驱动芯片是PT6315,通讯协议SPI。 DIN、CLK、RST三线组成 负压产生约为-35V,50%占空比的交流灯丝驱动,显示清晰。 外部供电需 5V供电,不可超过5V标准。 电路中请保证供电电流,防止下压降导致其他电路或单片机无法正常运行,通讯电平为3.3V。
立创开源链接地址:https://oshwhub.com/yc_chen/vfd-xian-shi-mo-kuai-san-hnv
1. 复刻说明
复刻请注意屏幕引脚尺寸超过免费打样限制。PCB板大小(125.146mmx32.131mm)
屏幕购买链接:
https://item.taobao.com/item.htm?id=754425064614&skuId=5253614212938这里提供一小批嘉立创的PCB空板有需要的可以另外选择:
https://item.taobao.com/item.htm?id=754425064614&skuId=5253614212938
在线帮助手册文档:http://doc.saisaiwa.com/vfd-hnv-model/
技术交流QQ群:676436122
喜欢的可以玩一玩另一款VFD时钟:https://oshwhub.com/yc_chen/zyc-vfd-shi-zhong
2. STC8H的驱动小型开发板
在本开源EDA项目中,其中有一个Board是集成STC8H的驱动开发小板的PCB设计。
STC8的烧录代码Git仓库:https://github.com/ccy-studio/CCY-HNVM-TEST
3. 驱动代码适用于单片机全平台
本驱动库代码,您可以直接在您的项目中创建对应的文件,然后直接复制过去,请注意编码格式。
之后您需要修改下对应的宏定义,即可完成全部适配
驱动库文件分别拆分成两份封装API,其中以底层通讯pt6315,另一个则可以理解成应用层操作显示内容库API。
首先根据您的平台例如 C51、Arduino系列、STM32等等 修改初始化IO和延时函数的宏定义。
例如: C51的延时采用软件延时
void delay_us(u32 us) { //c51单片机的软件延时,这里只是实例不要复制
unsigned char data i;
do {
_nop_();
i = 5;
while (--i)
;
} while (--us);
}
Arduino系列的微秒延时
delayMicroseconds(1000) //以微秒为单位时间,延时1000微秒,即1毫秒
STM32的微秒延时可以软件或者定时器,这里就不做多的介绍了。
后面就是操作IO的部分宏定义,如果是C51那么可以是 P31=1类似这种,Arduino的可以使用 digitalWrite(pin, value) 来操作IO引脚的电平翻转。 请根据您的平台去做特定的修改。下面的代码部分将会体现。
1. 创建文件: pt6315.h
#ifndef __PT6315__
#define __PT6315__
#define CLK_1 P_CLK = 1
#define CLK_0 P_CLK = 0
#define DIN_1 P_DIN = 1
#define DIN_0 P_DIN = 0
#define STB_1 P_STB = 1
#define STB_0 P_STB = 0
#define delay_us(x) my_delay_us(x)
#define delay_ms(x) my_delay_ms(x)
void ptSetDisplayLight(uint8_t onOff, uint8_t brightnessVal);
void setModeWirteDisplayMode(uint8_t addressMode);
void setDisplayMode(uint8_t digit);
void sendDigAndData(uint8_t dig, uint8_t* dat, size_t len);
#endif
在宏定义这里CLK_1 、CLK_0 等等 三个IO引脚的宏定义需要您根据自己的平台去编写IO的翻转,上图的示例是C51的
假如您的平台是Arduino那么需要修改代码例如:
#define CLK_1 digitalWrite(CLK,HIGH) //高电平输出
#define CLK_0 digitalWrite(CLK,LOW) //低电平输出
...
...
注意在使用前需要在main方法内一定要先初始化IO引脚,初始化IO引脚,初始化IO引脚,,例如Arduino系列操作**pinMode()**函数。
修改宏定义的两个延时函数:delay_us、delay_ms ,例如还是Arduino系列的则可以修改为
注意如果是Arduino需要引入头文件: #include <Arduino.h> 其他平台也类似。
#define delay_us(x) delayMicroseconds(x) //微秒延时
#define delay_ms(x) delay(x) //毫秒延时
在复制下面的代码前,需要先确认您修改完成了上面的宏定义的方法。
2. 创建文件: pt6315.c
#include "pt6315.h"
void writeData(uint8_t dat) {
size_t i = 0;
CLK_0;
for (i = 0; i < 8; i++) {
delay_us(10);
if (dat & 0x01) {
DIN_1;
} else {
DIN_0;
}
delay_us(10);
CLK_1;
delay_us(10);
CLK_0;
dat >>= 1;
}
}
void setModeWirteDisplayMode(uint8_t addressMode) {
uint8_t command = 0x40;
if (addressMode) {
command |= 0x4;
}
STB_1;
delay_us(10);
STB_0;
delay_us(10);
writeData(command);
delay_us(10);
STB_1;
}
/**
* COMMANDS 1 显示模式设置命令
* 0000 : 4位, 24段
* 0001 :5位, 23段
* 0010 : 6位数字, 22段
* 0011 : 7位, 21段
* 0100 : 8位, 20段
* 0101 : 9位, 19段
* 0110 : 10位, 18段
* 0111 : 11位, 17段
* 1XXX : 12位, 16段
*/
void setDisplayMode(uint8_t digit) {
STB_1;
delay_us(10);
STB_0;
delay_us(10);
writeData(digit);
delay_us(10);
STB_1;
}
/**
* 显示控制命令 COMMANDS 4
* @param onOff 0显示关闭,1开启显示
* @param brightnessVal 亮度占空比 0~7调整
* 000:脉冲宽度= 1/16 0
* 001:脉冲宽度= 2/16 1
* 010 :脉冲宽度= 4/16 0x2
* 011 :脉冲宽度= 10/16 3
* 100:脉冲宽度= 11/16 4
* 101 :脉冲宽度= 12/16 0x5
* 110:脉冲宽度= 13/16 6
* 111:脉冲宽度= 14/16 0x7
*/
void ptSetDisplayLight(uint8_t onOff, uint8_t brightnessVal) {
uint8_t command = 0x80 | brightnessVal;
if (onOff) {
command |= 0x8;
}
STB_1;
delay_us(10);
STB_0;
delay_us(10);
writeData(command);
delay_us(10);
STB_1;
}
void sendDigAndData(uint8_t dig, const uint8_t* dat, size_t len) {
size_t i = 0;
STB_1;
delay_us(10);
STB_0;
delay_us(10);
writeData(0xc0 | dig);
delay_us(10);
for (i = 0; i < len; i++) {
writeData(dat[i]);
}
delay_us(10);
STB_1;
}
3. 创建文件:gui.h
#ifndef __VFD_GUI_
#define __VFD_GUI_
#include "pt6315.h"
// VFD位数
#define VFD_DIG_LEN 9
/**
* 初始化
*/
void vfd_gui_init();
/**
* 停止关闭显示
*/
void vfd_gui_stop();
/**
* 清空VFD屏幕显示,循环刷新如果使用vfd_gui_set_text方则不需要使用它。
*/
void vfd_gui_clear();
/**
* 显示一串文字,从0位开始。
* (自动清空覆盖显示,方便每次不用调用clear防止闪屏出现)
* @param colon 是否显示冒号 0|1
* @param left_first_conlon 是否显示左边冒号 0|1
*/
void vfd_gui_set_text(const char* string,
const u8 colon,
const u8 left_first_conlon);
/**
* 要点亮的ICON图标,可宏定义传参可参考acg动画
*/
void vfd_gui_set_icon(u32 buf);
/**
* 设置亮度等级 1~7
*/
void vfd_gui_set_blk_level(size_t level);
/**
* acg动画
*/
void vfd_gui_acg_update();
long map(long x, long in_min, long in_max, long out_min, long out_max);
#endif
4. 创建文件:gui.c
#include "gui.h"
u8 lightLevel = 1; // 亮度级别
static u8 send_buf[3 * VFD_DIG_LEN] = {0};
static u8 send_buf_cache[3 * VFD_DIG_LEN] = {0};
const u32 fonts[38] = {
0x333300, // ASCII:0,ASCII_N:48 index:0
0x201000, // ASCII:1,ASCII_N:49 index:1
0xe12100, // ASCII:2,ASCII_N:50 index:2
0xe13000, // ASCII:3,ASCII_N:51 index:3
0xe21000, // ASCII:4,ASCII_N:52 index:4
0xc33000, // ASCII:5,ASCII_N:53 index:5
0xc33100, // ASCII:6,ASCII_N:54 index:6
0x211000, // ASCII:7,ASCII_N:55 index:7
0xe33100, // ASCII:8,ASCII_N:56 index:8
0xe33000, // ASCII:9,ASCII_N:57 index:9
0x080400, // ASCII::,ASCII_N:58 index:10
0xe31100, // ASCII:A,ASCII_N:65 index:11
0xa93400, // ASCII:B,ASCII_N:66 index:12
0x032100, // ASCII:C,ASCII_N:67 index:13
0x293400, // ASCII:D,ASCII_N:68 index:14
0xc32100, // ASCII:E,ASCII_N:69 index:15
0xc30100, // ASCII:F,ASCII_N:70 index:16
0xc33100, // ASCII:G,ASCII_N:71 index:17
0xe21100, // ASCII:H,ASCII_N:72 index:18
0x092400, // ASCII:I,ASCII_N:73 index:19
0x082400, // ASCII:J,ASCII_N:74 index:20
0x520900, // ASCII:K,ASCII_N:75 index:21
0x022100, // ASCII:L,ASCII_N:76 index:22
0x361100, // ASCII:M,ASCII_N:77 index:23
0x261900, // ASCII:N,ASCII_N:78 index:24
0x233100, // ASCII:O,ASCII_N:79 index:25
0xe10100, // ASCII:P,ASCII_N:80 index:26
0x233900, // ASCII:Q,ASCII_N:81 index:27
0xe30900, // ASCII:R,ASCII_N:82 index:28
0xc33000, // ASCII:S,ASCII_N:83 index:29
0x090400, // ASCII:T,ASCII_N:84 index:30
0x223100, // ASCII:U,ASCII_N:85 index:31
0x241800, // ASCII:V,ASCII_N:86 index:32
0x221b00, // ASCII:W,ASCII_N:87 index:33
0x140a00, // ASCII:X,ASCII_N:88 index:34
0x140400, // ASCII:Y,ASCII_N:89 index:35
0x112004, // ASCII:Z,ASCII_N:90 index:36
0xc00000, // ASCII:-,ASCII_N:45 index:37
};
u32 gui_get_font(char c);
void vfd_gui_init() {
VFD_EN = 1;
// VFD Setting
setDisplayMode(6);
setModeWirteDisplayMode(0);
vfd_gui_set_blk_level(7);
vfd_gui_clear();
}
void vfd_gui_stop() {
VFD_EN = 0;
vfd_gui_clear();
}
void vfd_gui_clear() {
memset(send_buf, 0x00, sizeof(send_buf));
sendDigAndData(0, send_buf, sizeof(send_buf));
}
void vfd_gui_set_icon(u32 buf) {
uint8_t arr[3] = {0};
memset(arr, 0x00, sizeof(arr));
if (buf) {
arr[0] = (buf >> 16) & 0xFF;
arr[1] = (buf >> 8) & 0xFF;
arr[2] = buf & 0xFF;
}
sendDigAndData(0x1b, arr, 3);
}
void vfd_gui_set_text(const char* string,
const u8 colon,
const u8 left_first_conlon) {
size_t str_len = strlen(string);
size_t index = 0, i = 0;
size_t len = str_len > VFD_DIG_LEN ? VFD_DIG_LEN : str_len;
memset(send_buf, 0x00, sizeof(send_buf));
for (i = 0; i < len; i++) {
if (string[i] && string[i] != '\0') {
u32 buf = gui_get_font(string[i]);
send_buf[index++] = (buf >> 16) & 0xFF;
send_buf[index++] = (buf >> 8) & 0xFF;
send_buf[index++] = buf & 0xFF;
}
}
if (left_first_conlon) {
send_buf[7] |= 0x40;
}
if (colon) {
send_buf[13] |= 0x40;
send_buf[19] |= 0x40;
}
sendDigAndData(0, send_buf, sizeof(send_buf));
}
/**
* 亮度调节 1~7
*/
void vfd_gui_set_blk_level(size_t level) {
if (level == lightLevel) {
return;
}
lightLevel = level;
ptSetDisplayLight(1, lightLevel);
}
long map(long x, long in_min, long in_max, long out_min, long out_max) {
const long dividend = out_max - out_min;
const long divisor = in_max - in_min;
const long delta = x - in_min;
return (delta * dividend + (divisor / 2)) / divisor + out_min;
}
u32 gui_get_font(char c) {
if (c == ' ') {
return 0;
}
if (c == '-') {
return fonts[37];
}
if (c >= 48 && c <= 58) {
return fonts[map(c, 48, 58, 0, 10)];
} else if (c >= 65 && c <= 90) {
return fonts[map(c, 65, 90, 11, 36)];
} else if (c >= 97 && c <= 122) {
return gui_get_font(c - 32);
} else {
return 0;
}
}
/**
* acg动画
*/
void vfd_gui_acg_update() {
static u8 acf_i = 9;
if (acf_i == 9) {
static u32 icon = 0x040000;
static u8 sec = 0;
vfd_gui_set_icon(icon);
sec++;
if (sec == 3) {
icon = 0x008000;
} else if (sec < 3) {
icon = (0x040000 >> sec);
} else {
icon = (0x040000 << (sec - 3));
}
if (sec == 4) {
acf_i = 0;
}
if (sec == 7) {
sec = 0;
icon = 0x040000;
}
} else {
u8 bi = acf_i == 0 ? 1 : (acf_i + 1) * 3 - 2;
memcpy(send_buf_cache, send_buf, sizeof(send_buf));
if (acf_i == 2 || acf_i == 4 || acf_i == 6) {
send_buf_cache[bi] |= 0x80;
} else {
send_buf_cache[bi] |= 0xC0;
}
if (acf_i == 0) {
vfd_gui_set_icon(0);
}
sendDigAndData(0, send_buf_cache, sizeof(send_buf_cache));
acf_i++;
if (acf_i == 9) {
sendDigAndData(0, send_buf, sizeof(send_buf));
}
}
}
在代码中有一个宏定义:VFD_EN ,这个则是模块中EN引脚的电平翻转宏定义,这里可以根据您自己的平台做响应的修改。
例如Arduino系列的可以改为: digitalWrite(VFD_EN ,HIGH)、digitalWrite(VFD_EN ,LOW)
5. 简单使用
#include "gui.h"
void main(){
//初始化 DIN、CLK、RST、EN引脚IO为推挽输出模式
...
...
vfd_gui_init(); //初始化
vfd_gui_set_blk_level(7); //亮度调整
while(1){
delay_ms(500);
vfd_gui_set_text("123456789",0,0); //设置显示内容
vfd_gui_acg_update();//动画帧刷新,如果使用RTOS需要对数据传输pt6315上锁
}
}
评论区