目录
前言
如何控制HPDL 1414
利用74HC595控制HPDL-1414的电路
软件设计/看不见的大坑
操控和码表的对应方法
设计中需要避坑的地方
HPDL-1414 驱动程序
测试代码,主文件
测试代码,标签页文件
这次新收到一个有趣的屏幕,HPDL 1414,是HP做的,同样款式西门子也有做,应该是用于一些仪器上的古老电子元件。
本体并不是很大,1414应该十之八九是说4*4mm,这个东西小,但是亮度很高,设计上巧妙的用了一个凸透镜让数值显示的更清晰。
本身段位特别多,所以可以显示大量的字符
内置有一个驱动器了,所以没需要再使用上非常厉害IO扩展,这是让人觉得很方便的一点
要写软件之前需要先明白硬件,先来区分脚位,这一款的脚位和显示方向有差别,可以依据侧面的字符进行判断,侧面一面HPDL 1414,这一面的最左也是PIN 1
看脚位的定义,可以看到是D5数据输入,本身的数据是并联输入的。
所以HPDL 1414这一款产品驱动起来很简单,需要做的就是按照这个表格来输入数据,D0-D6这七个脚位决定字符,A1和A0决定是第几位数码管,WR是写入信号引脚,低电平有效,这个的作用就是让你可以并联多个HPDL1414,然后通过控制WR来决定要改变哪一个的数值。
我预计是使用74HC595来控制输入,这是一个非常古典的芯片,5V驱动,通过3个脚位可以引出8个脚位,并且它在设计上支持多个74HC595串联,虽然驱动能力和C51差不多,但是优点是廉价耐用。
使用595间接控制HPDL-1414这样可以让引脚数减少到6个(SCK,RCK,CLR,DS以及HPDL的A1和A0)之少,当然这也是个坑,后面我会说一下。
首先是HPDL-1414和MCU的连接,我用的是Arduino UNO,因为方便测试且驱动力大,这里我把控制段位的A1和A0接入到数字引脚D8,D7上面。
HPDL的WR用于控制数据改写,在高电平的情况下是有效的,所以接一个10K的电阻,串联到GND让它平常是失能状态
这个简单的模块的另一个部分就是74HC595了,这个芯片同样不需要太多外部元器件,13引脚一定要连接到GND,我一开始没有连接到GND然后拿去制板,造成的结果就是74HC595一直工作不正常,不管怎么控制所有输出引脚都是高电平,后面飞了线解决。
可以看到我在10脚位接了一个10K电阻,再串接到高电平,使得CLR脚位平常是失能的状态,这个脚位发送一个低电平脉冲可以让74HC595的输出全部清零。
通过一个连接器,飞线和MCU进行连接,如果要更方便测试可以在PCB设计上预留几个测试点。
我的原理图是这样画的,仅供一个参考,因为HPDL-1414内置有驱动所以基本来说不需要外部电路。
最后这一款产品的PCB的完整版设计是这样
我找不到HPDL-1414的建模文件,简化的绘制了一个,方便后面设计外壳时候使用。
为了减少体积,在第二次改版中,我尽量的使用了贴片元件,一开始时候使用的是直插元器件(因为是库存)。
最终外观大概是这样子的。
电路原理图,以及我测试中和MCU的连接
这个软件设计确实很不得了,当然这要从74HC595说起,这一个芯片看资料很容易云里雾里,为什么,因为它的引脚标注名称就有一大堆,比如一个11引脚,在一些地方叫SH_CP
在AltiumDesigner的库里面却是SRCK
然后,我再百度一下资料,又变成了SHIFT CLOCK
会出现这个情况,主要还是因为这个芯片太简单,国内仿制很多所以有几家出了自己的datasheet,然后自己命名,于是乎就这样了,所以还是记控制脚位吧,名称伤不起。
10脚:重置
11脚:数据输入的时钟线
12脚:输出存储器锁存时钟线
13脚:输入串口
先来写74HC595 的代码, 引脚接哪儿可以看我刚刚的图,写这个代码很简单,先定义脚位,然后搞一下初始化,74HC595的控制是这样的
#define CLR 9
#define SI 10
#define RCK 11 //SH_CP clock
#define SRCK 12 //ST_CP latch
void _74HC595_Init()
{
pinMode(RCK, OUTPUT);//接收数据使能引脚
pinMode(SRCK, OUTPUT);//时钟引脚
pinMode(SI, OUTPUT); //数据输入引脚
pinMode(CLR, OUTPUT);
digitalWrite(CLR, HIGH);
}
毕竟是始祖芯片,Arduino官方库都有个专门准备的函数用来把一个8位数据输入到74HC595中
void _74HC595(int data)
{
printPin(data);
shiftOut(SI, SRCK, LSBFIRST, data); //调用官方的函数,通过10脚给595数据
digitalWrite(RCK, LOW); //将输出存储器11脚上加低电平让芯片准备好接收数据
digitalWrite(RCK, HIGH); //将输出存储器11脚恢复到高电平
digitalWrite(RCK, LOW);
}
这样就写好了,但是有个小问题,就是这个输入的数据,最终是反向的
比如我程序上是输入:1000 0000(也就是0x80)
实际上74HC595的D7-D0的输出:0000 0001
所以还要做个转换,这里加一个函数,把数值预先反向一下,这样输出再反向也就正回来了
int reverse8( int c )
{
c = ( c & 0x55 ) << 1 | ( c & 0xAA ) >> 1 ;
c = ( c & 0x33 ) << 2 | ( c & 0xCC ) >> 2 ;
c = ( c & 0x0F ) << 4 | ( c & 0xF0 ) >> 4 ;
return c;
}
然后,我要说一说我的设计中最糟糕的部分了,我为了节约引脚把HPDL的WR连接到了595的最后一个输出位上面。
所以,要输入的数据,本来只是7位,而实际上是8位,最后的D7(也是最开始输入的数据)必须置一个位,因为要把修改HPDL显示内容的“开关”打开。
也因此,我虽然输入的内容对应码表,但实际上要在函数中增加一个D7位
(这部分之后再补充)
看看码表,可以看到数值在表上是由D6-D4和D3-D0定义的,得益于段位特别多,所以这个显示的数据也很多样
比如说我要显示1234,那么就需要0x31,0x32,0x33和0x34
最后,如果一切妥当,就可以看到
举一反三,也就能设置一个动态的效果了
这一款产品的驱动不难,但是有需要避开坑的几个点:
1.芯片的选择,如果你选择使用74HC595,最后会发现节约的引脚位也没有几个,但是程序设计上反而更复杂了,并口输入数据和串口输入数据,后者会需要顾及更多。如果不是引脚奇缺,还是别用了。
2.我在74HC595的一个脚位上折腾了很久,因为忘记接入到GND。
3.这个东西的电路设计一定要考虑连接的稳健性,测试电路上我使用了杜邦线和连接器,测试是否是接触不良导致程序不能执行耗费了至少半小时的时间。
#define HDPL_A0 7
#define HDPL_A1 8
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
_74HC595_Init();
Clr();
pinMode(HDPL_A0, OUTPUT);
pinMode(HDPL_A1, OUTPUT);
//高低位反向,先低后高
HPDL_Build(1, 0x43); //1001 1011
HPDL_Build(2, 0x4F); //1001 1101
HPDL_Build(3, 0x4D); //1001 0101
HPDL_Build(4, 0x36); //1001 1011
//Convert(0x34);
}
int i = 0;
void loop() {
DisNumber(2,7);
DisNumber(3,2);
HPDL_Build(4,0x25);
delay(1000);
}
void DisNumber(int vec, int num)
{
if (num > 9) num = 9;
if (num < 0) num = 0;
HPDL_Build(vec, 0x30 + i); //1001 1011
delay(100);
}
void SelectVec(int i)
{
digitalWrite(HDPL_A0, LOW);
digitalWrite(HDPL_A1, LOW);
switch (i)
{
case 4:
break;
case 3:
digitalWrite(HDPL_A0, HIGH);
break;
case 2:
digitalWrite(HDPL_A1, HIGH);
break;
case 1:
digitalWrite(HDPL_A0, HIGH);
digitalWrite(HDPL_A1, HIGH);
break;
default:
break;
}
delay(10);
}
void HPDL_Build(int sector, int data)
{
data = reverse8(data); //反向
SelectVec(sector);
_74HC595_Test(data);
Clr();
if (data & 0x01 == 1)
_74HC595_Test(data - 0x01);
}
void printPin(int data)
{
Serial.println("Current set:");
Serial.println("A7------------A0");
for (int i = 0; i < 8; i++)
{
Serial.print(data & 0x01);
Serial.print(" ");
data = data >> 1; //右移一位
}
Serial.println(" ");
Serial.println("----------------");
}
//Input: 0101 1100 Output: 1010 0011
int Convert(int data)
{
int datL, datH;
int datLN, datHN;
int output = 0;
Serial.println("convert data:");
printPin(data);
datL = data & 0xF;
datH = data & 0xF0;
datLN = (datL) & 0x01; // 0000
datLN = datLN << 1;
datLN += (datL >> 1) & 0x01; // 0000 + 0110&0x01
datLN = datLN << 1;
datLN += (datL >> 2) & 0x01; // 0000 + 0011&0x01
datLN = datLN << 1;
datLN += (datL >> 3) & 0x01; //0010 + 0001&0x01 = 0011
datHN = datH & 0x01;
datHN = datHN << 1;
datHN += (datH >> 1) & 0x01;
datHN = datHN << 2;
datHN += (datH >> 2) & 0x01;
datHN = datHN << 3;
datHN += (datH >> 3) & 0x01;
output = datHN << 4 + datLN;
Serial.println("output data:");
printPin(output);
return output;
}
int reverse8( int c )
{
c = ( c & 0x55 ) << 1 | ( c & 0xAA ) >> 1 ;
c = ( c & 0x33 ) << 2 | ( c & 0xCC ) >> 2 ;
c = ( c & 0x0F ) << 4 | ( c & 0xF0 ) >> 4 ;
return c;
}
#define CLR 9
#define SI 10
#define RCK 11 //SH_CP clock
#define SRCK 12 //ST_CP latch
void _74HC595_Init()
{
pinMode(RCK, OUTPUT);//接收数据使能引脚
pinMode(SRCK, OUTPUT);//时钟引脚
pinMode(SI, OUTPUT); //数据输入引脚
pinMode(CLR, OUTPUT);
digitalWrite(CLR, HIGH);
}
void _74HC595_Test(int data)
{
shiftOut(SI, SRCK, LSBFIRST, data);
digitalWrite(RCK, LOW); //将ST_CP口上加低电平让芯片准备好接收数据
digitalWrite(RCK, HIGH); //将ST_CP这个针脚恢复到高电平
digitalWrite(RCK, LOW);
}
void Clr()
{
//清空
digitalWrite(CLR, HIGH);
delay(1);
digitalWrite(CLR, LOW);
delay(1);
digitalWrite(CLR, HIGH);
}