在多年以前,我刚开始学单片机的时候,就想着要用51单片机加上0.96英寸的OLED DIY一个电子表,但是可惜当时水平有限,没能实现。现在我早已玩转了STM32,准备向ARM9进发,突然想到了当年的想法,今天终于亲手实现了它。

整体设计

毕竟是要实现当年的想法,所以整体还是选择了常见的STC单片机作为主控,以及当时选定的0.96英寸OLED作为屏幕。一般STC都会选择搭配DS1302 RTC芯片用来计时,那么也一起加上。按键方面,为了操作方便,我没有使用普通的微动开关,那种太硬了,戴在手上摁起来不方便。我选择了一种拨轮开关(见图16.1),它上面有一个摇柄,可以上下推动,也可以按下去。一个开关可以同时实现上下选择和确定选择,这在MP3上比较常见,网上价格也不贵,大约三毛钱一个。另外,考虑到OLED比较费电,使用纽扣电池作为电源并不妥,于是我采用了可充电的锂聚合物电池作为主电源,另加了一块TP4056作为充电管理IC。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(1)

图16.1 一种常见的拨轮开关

电路原理

1. 主控电路

主控电路中最主要的部分就是STC15L2K60S2的单片机(见图16.2),我选择了SOP28封装的版本,比较小,制作手表比较合适。这是一款基于51内核的单片机,最高主频35MHz,具有60KB的ROM和2KB的RAM,虽然配置并不强大,但是做个手表还是绰绰有余的。型号中带L的为低电压版本,采用3.3V供电。其实这个系列的单片机应该是前几年初学者非常常用的,我当时学习的也是STC单片机。不过近几年随着国外开源硬件的发展,Arduino系的东西大有取代原来STC单片机国内DIY初学者入门必备地位的势头啊。当然相比Arduino,STC也是有它的优势的:第一就是便宜,一片STC只要5~8元就可以购得;第二是它的电路十分简单,最新的STC15系列,一片芯片就可以组成最小系统,和Arduino Mini一样,装上面包板直接可以用,不必连接外部复位或者晶体振荡器之类的东西;第三就是它的内部高精准RC振荡器可以调节频率,可以等我们把程序写完了再来调节频率,找到功耗和性能的平衡点;第四,它的社区支持并不差,Arduino有许多现成的程序可以利用,STC也一样,在国内有很多讨论51单片机的论坛,里面的程序都可以借鉴。因为数据量不大,手表对刷新率也没有很高的要求,我这个设计没有用到硬件的SPI,通过I/O口模拟SPI与OLED和DS1302通信。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(2)

图16.2 STC15L2K60S2单片机

2. 屏幕及其外围电路

我使用了Univision出品的一款OLED屏幕(见图16.3),型号UG-2864HMBEG01,尺寸为0.96英寸,分辨率为128像素×64像素,白色单色,支持I2C、3线SPI、4线SPI、I80和68K五种接口协议,可以说全兼容,同时内置了电荷泵,提供OLED驱动所需的高压,给电路设计提供了极大的方便。OLED显示技术具有自发光的特性,采用非常薄的有机材料涂层和玻璃基板,当有电流通过时,这些有机材料就会发光,而且OLED显示屏幕可视角度大,对比度非常高,做得好的话,可以做到正无限比为1。其实这种屏幕在MP3上十分常见,不过现在出厂的MP3大多已经采用LCD了。但是OLED屏幕也并非没有缺点,长时间显示同样的静止图案会造成烧屏,所以实际上并不是很适合用来做手表,而且用OLED做手表的话,确实费电了一些。我觉得理想的方案还是用一块反射式的STN屏幕。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(3)

图16.3 OLED屏幕及其外围电路

3. RTC(实时时钟)电路

DS1302这款芯片其实相信大家都应该比较了解了,是一款高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、周、日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5~5.5V,采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器(见图16.4)。我这里没有连接备用电池,直接把主供电连上了LDO的输出。值得一提的是,DS1302使用的是BCD(全称为Binary-Coded Decimal)编码,是一种二进制的数字编码形式,用二进制编码的十进制代码。这种编码形式利用了4个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷进行。但是实际上在单片机里面,BCD还给我们带来了一点小麻烦,单片机能处理的是二进制码,而不是BCD码,所以要进行一下转换。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(4)

图16.4 RTC(实时时钟)电路

4. 供电电路

供电方面采用了一片ME6219 LDO(低压差线性稳压器),见图16.5,它的压降仅为0.2V,可以满足锂电池供电的需要,像一般常用的AMS1117,它的压降达到了1V,也就是当锂电池电压为3.7V时,AMS1117最多只能输出2.7V的电压,完全起不到稳压的作用。同时,锂电池要考虑的问题就是电量检测,一方面提醒使用者充电,另一方面也要保护锂电池,电量过低就强制关机,以免过放对锂电池造成损伤。单片机的供电电压是3.3V,而电池的电压则是3.5~4.2V,是高于单片机电源电压的,因此就必须设计一个分压电路,分压之后再接入单片机的ADC进行测量。分压电路是始终连接在电池上的,如果电池电量全消耗在这个分压上,那也太冤枉了。所以我选择了两个1MΩ的电阻来分压(见图16.6),也就是2MΩ的阻值,根据欧姆定律可知,在3.7V标准电压下的电流仅为2μA,符合要求。但是这样又会出现一个问题,ADC也不是理想电表,存在一个输入阻抗的问题,我没有研究过STC的输入阻抗,但是明显不可能会远高于1MΩ,这样就必须加一个电容来减少输入阻抗过低对分压的影响。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(5)

图16.5 ME6219低压差线性稳压器

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(6)

图16.6 分压电路

5. 充电电路

充电使用了常见的TP4056作为充电管理IC (见图16.7),TP4056是一款完整的单节锂离子电池恒流/恒压线性充电器,其底部带有散热片的SOP8封装与较少的外部元件数目,这使得TP4056成为便携式应用的理想选择。TP4056适用于USB电源和适配器电源,其输入电压范围为4~8V,充电电流最大1000mA。它的充电电流是通过一个外部电阻来调节的。其实TP4056的外部电路设计还是挺简单的,但是我还是单独使用了一块现成的充电板,因为主洞洞板上放不下了,最终也导致整个作品非常厚,也算是一点遗憾吧。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(7)

图16.7 充电电路

其实现在大多数的高级点的移动设备都会选择专门的PMIC (电源管理集成电路),它们通常提供了多路DC-DC和LDO输出,满足不同设备的供电需求,内置锂电池充电管理功能,部分还内置了库仑计,可以更精确地测定电池电量,并进行功耗控制。这类PMIC通常提供了I2C接口,主处理器可以通过I2C和PMIC进行通信,获取电量、电流之类的数据,并且可以直接通过软件来调整电压之类的参数,十分强大。不过像51这种设计就用不上那种东西啦,那种一般都是给Cortex-A级别的处理器用的。

硬件制作

因为用到的元器件并不复杂,连接也不是很多,所以我选用了洞洞板 飞线的形式,虽然这样比较麻烦,但是可以省下打样的钱,不过对“手艺”有一定的要求。工具方面,一把好用的电烙铁,一把美工刀,以及一把热熔胶枪就够了,都是比较常规的工具。材料方面,请准备原理图里面出现的各种元器件、一卷焊锡丝、一卷漆包线、一块洞洞板,以及你的热情和耐心。

首先,把洞洞板裁成需要的大小,然后在上面合适的位置用美工刀割出SOP28的焊盘(见图16.8),因为SOP28的引脚间距为1.27mm,而洞洞板的洞间距为2.54mm,因此把每个洞的焊盘割成两半就可以焊接SOP28的芯片了。割的时候不一定要在正中间,但是一定要割干净,最好空出一条,避免短路之类的事情发生。

割完焊盘就可以焊接主MCU了,这个没有什么值得注意的,不要让引脚短路就可以了(见图16.9)。如果短路了,用把好点的烙铁,加点助焊剂(或者带助焊剂的焊锡也可以),烙铁可以把多余的焊锡吸起来,然后用海绵擦掉烙铁头上的锡就好了。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(8)

图16.8 割出SOP28的焊盘

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(9)

图16.9 焊上单片机

然后,处理和MCU连接最多的屏幕部分。虽然采用了四线制SPI通信,但是这个OLED内部电荷泵需要几个外部的电容,相对还是比较麻烦的。屏幕的引脚间距仅为0.8mm,比较考验“焊功”(见图16.10)。建议漆包线在焊接之前先镀好锡,会方便很多。屏幕部分完成之后,看起来就是一团糟的样子(见图16.11)。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(10)

图16.10 处理屏幕与单片机的连接

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(11)

图16.11 连接好的样子

STC单片机使用串口下载,准备好串口的接口也是必不可少的。如果使用插针,担心会刺到手(其实是我多虑了,不会发生这种事情),于是我选择了排孔,并且是没有出头的设计,这样可以说是对本来就不充裕的电路板空间的巨大浪费,但是凸出来实在太难看,于是我还是这样做了,如图16.12所示。

这个接口只要“飞”上VCC、TXD、RXD和GND这4个引脚就够了,不过我这里先只连接了VBAT,也就是锂电池电源。下一步是焊接LDO,在LDO焊接完成之后,就可以开始调试OLED驱动了(见图16.13),顺便检查之前的连接是否可靠。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(12)

图16.12 用于串口下载的排孔

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(13)

图16.13 焊接LDO

事实上,这个OLED困扰了我大半个小时,一直没有显示,检查连接也似乎没什么问题,也没有虚焊,后来仔细看了Datasheet才发现我犯了一个十分低级的错误,OLED的VCC并非逻辑电源输入,而是屏幕驱动电压输入,应该接上12V外部电源,在使用内部电荷泵升压的时候应该在外部对底连接一个电容。解决问题后,显示正常了(见图16.14)。

下面就是用同样的方法处理DS1302,也就不多说了。我使用了一条铁丝架在拨轮开关上加强固定,再顺便焊上锂电充电板。因为该电路的功耗很低,所以不需要使用很粗的漆包线,用最细的来连接电路完全不成问题,充电板直接和锂电池连接就好,如图16.15所示。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(14)

图16.14 OLED驱动正常了

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(15)

图16.15 充电板和控制板接到一起

在最终打胶并把充电板装上去之前,一定要确保所有硬件部分都已调试正常了,因为打胶基本上是不可逆的,等打完胶再发现有什么错误就太迟了。还有记得一定要设计一个断电的方法,因为STC单片机必须要断电一下才能进入ISP模式。我使用了一个跳线来解决这个问题。于是,就有了图16.16、图16.17所示的成品了。

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(16)

图16.16 制作好的LOED手表正面

单片机是如何制作电路图的 单片机控制的OLED简易电子表原型(17)

图16.17 背面是锂电池

个人感觉这个做完的“手表”还是厚了一点,屏幕也有点小,如果以后有机会再改进。

软件编写

因为目前就只打算实现时钟显示这一单一功能,所以代码设计十分简单。首先就是要解决各个部件的驱动。

1. 屏幕驱动

屏幕的分辨率为128像素×64像素。因为是单色的,所以一个1bit就可以表示一个像素,一个字节中有8个像素,所以整个屏幕显示内容所要占用的内存空间为1KB (128×64/8),可以放进STC的RAM,所以我就在RAM里面建立了一个屏幕缓冲区,所有绘图操作都在缓冲区里面进行,这样可以大幅减少和屏幕的通信,加快绘图速度。怎么理解呢?比如要点亮屏幕上的一个像素,但是像素是以8个为单位存在一个字节里面的,要操作里面某一位,只能先把这个字节读出来,修改后再写回去,也就是读改写。注意,单片机和屏幕的通信速度并不快,一直这样读改写速度会非常慢。可以计算一下,这样操作一个像素就要传输两个字节的数据,一共8192个像素,就要传输16384个字节的数据,也就是16KB,而如果先在缓冲区内画完,再传输,只要传输1024个字节就足够了,速度自然就快了。

另外还有一点,一般绘图都要清空屏幕,不然后来的东西会和先前有的叠在一起,就看不清楚了。清空的代价就是闪烁,屏幕会先变全黑再显示出需要的东西,如果使用了缓冲的设计,就不会有这种问题。

2. DS1302 驱动

DS1302的驱动程序在网上很常见,也不困难,我就简单说下BCD和二进制的互转吧。BCD是什么意思呢?简单理解就是在十进制数前面加个0x,然后就变成十六进制了。举个例子,35的BCD编码就是0x35,而二进制编码却应该是0x23。在C语言中,写程序的时候写十进制或者十六进制都没有关系,程序里面写35,编译器会自动认为就是0x23。如果把BCD码用在程序里面,就会出现问题,主要是加减法的问题。还是之前的35,如果加上5,应该是40,这点用二进制码表示是没有问题的,但如果是BCD呢?BCD 0x35加上BCD 0x05应该变成BCD 0x45,但是在程序里面写0x35 0x05结果是0x3A,3A在BCD码中是没有意义的,应该直接进位才对。不过BCD其实转换起来并不困难,因为BCD码4bit对应一个十进制数,一个字节对应两个十进制数。在DS1302驱动中,只涉及1个字节的转换,所以程序就十分简单,以下是简单的BCD转二进制代码的方法。

BIN=(BCD/16)×10 (BCD);

除以16和取模16就是获得一个字节中前4bit和后4bit,比如0x35,分别返回3和5,然后第一位乘以十加上第二位就是最终需要的结果了。

3. 主程序设计

我的主程序设计十分简单,先读取时间,比较和上次读取到的时间是否有变化,如果有就显示在屏幕上,然后检测按键是否按下,如果按下,则启动修改时间的函数。具体设计大家还是看代码吧,主函数大家可以自己来写,这样也满足个性化的需求。

后记

制作这个东西加起来需要的时间没有超过24小时,主要的时间还是花费在了飞线上,或许这样飞线的难度有点太高了,并不适合大家仿制。不过采用飞线制作主要还是为了一种心情,今天想到了,第二天就能把它做出来,这是打样PCB做不到的。

多年前我就买过0.96英寸的OLED模块,想用它DIY一个非常酷的手表,但是非常遗憾的是,被我不小心把正负极接反给烧了。然后,我为了玩大屏,买了STM32开发板,为了做成品,自己画了不少PCB。结果就是一个项目所要耗费的时间越来越长,比如我现在在折腾的一款图形计算器,上上下下设计、打样、写程序,再改设计、修改bug什么的,已经做了4版硬件,花了近1年的时间,到现在我还是不满意,准备用ARM9处理器继续做第5版硬件,我也不知道什么时候能完成。虽然工程越做越大,所谓的技术含量也越来越高,但是却似乎少了当初那份纯粹的乐趣,那份质朴的感动。

■介绍一个与众不同的淘宝店铺:首页-数码达人小二-淘宝网,点开有惊喜!

,