本文记录一段旅程–控制无源蜂鸣器嗡嗡作响。
背景
小时候,看科幻电影都是瞪大了眼睛,竖直了耳朵,生怕错过了精彩的情节。仙女座星系,宇宙飞船距某颗类地行星10千公里;南极圈,海冰和冰山在海平面上肆意分布,科考船即将抵达科考站。忽然,飞船,科考船报警器发出间断性的低鸣,各仓位笼罩在频闪的红光中,飞船即将与一颗陨星交汇,必须要尽快脱离当前轨道;科考船前方3公里的冰山海平面以下的结构会对科考船造成威胁,必须重新规划路线。舰长召集大副,领航员快速拟定应对计划,各部门紧急就位,齐心协力,化险为夷。
我决定使用手里的资源模拟报警器的运作方式。这次的主角是声音,而声源是一颗无源蜂鸣器,还是拿出手里的51单片机开发板,泡上一杯茶,开始一段新的旅行。
一、硬件驱动器
先看一看开发板原理图中关于蜂鸣器的部分。器件的驱动顺序是:单片机IO->ULN2003D->蜂鸣器。
我不知道ULN2003D是如何驱动蜂鸣器工作的,需要先了解一下ULN2003D的信息做好攻略再下手。看了一眼开发板上实际用的芯片是ULN2003A,因此查也应该查ULN2003A,不然可能因为一些细节问题抓耳挠腮自找无趣。查了一下数据手册,自我感觉有两张图比较重要。第一个是下面的芯片内部框图。
可以看到这芯片其实由7组相同的基本单元组成,不言而喻,另一个重要的是基本单元的描述图。
通过上面的信息加上一点点手册上的细节,可以产生新的两张图,一张用来描述这个蜂鸣器电路的硬件电气特性,一张用来描述它的数字特性。
我只想知道怎么使用这个芯片,所以我只关注ULN2003A的传输特性。手册中Iin最大值是25mA,意味这它不希望前端提供过大的电流把它冲烂,Iout最大值为500mA意味着当负载工作需要的电流大于500mA时,驱动器会表示它无能为力,可究竟蜂鸣器需要多大的驱动电流我也不知道,因为没找到相应的手册,只能在实际工况测试一下满不满足。
接下来是蜂鸣器模块的数字特性。芯片内部框图中特意描述的续流二极管我给省去了,因为我觉得作者的意图是想告诉我当应用于感性负载时你应该把这个续流二极管用起来,这属于电气特性。5V,TTL电平意味着我可以不加思考地将STC89C52单片机的IO口连到达林顿管的输入管脚上;建立时间限制了驱动器能跟踪输入信号的最大频率,否则输出会根据输入信号的频率与这个临界值的差值大小会产生不同程度的失真,再深究下去就要把拉普拉斯请出来了,还是不麻烦他老人家了。
无源蜂鸣器的激励信号并不是简单的逻辑1或逻辑0,而是具有一定频率的激励源,因为声音是物体机械震动产生的。但究竟这个激励源是正弦波还是方波还是什么别的稀奇古怪的东西就不得而知,遇事不决先看攻略,打开demo,先ctrl+c,ctrl+v再说。然后我就搞清楚了demo里面是用方波来驱动无源蜂鸣器的,然而具体别的波行不行,我暂时也不在乎。demo里面给的驱动信号是像下面这个样子的。
这是一个500Hz的方波信号,持续一段时间之后就停了,或许是怕一直响扰民被投诉,也或许是自己听多了都上头,总的来说考虑得还是比较周到。
二、软件驱动器
设计软件之前先重新泡一杯茶。就好像旅途中准备去下一个目的地之前总是想先找点东西填肚子喝点水才有劲一样,看着一片片茶叶在茶汤中慢慢落下总有说不出的惬意。
出发前回顾一下目的地在哪里。飞船里的报警器的声音有缓和一点的,也有急促一点的,我应该同时包含几种不同蜂鸣间隔的报警音。还有就是demo里面发出来的声音音调和我在电影里听到的也不太一样,也得试出来。所以我决定走下面图示的路线。
通过多路复用器可以选择要关闭报警还是其他什么花里胡哨的报警类型,为了让人能明显听得出蜂鸣器在响一下停一下,这些间隔时间是100ms起步的,然后复用器输出与蜂鸣器的发声驱动信号相与得到实际的蜂鸣器驱动信号,目的地就确定下来了。至于使用一个定时器Timer0作为脉冲的时基,一是为了节约定时器,二是为了方便修改时间参数。
虚拟定时器在前一篇《【电子电路基础实验】LED闪烁实验》中已经提到过了,就不说了。
虚拟逻辑多路复用器是这次要建造的新的工具。
- selfActionSignal是自身动作信号,顾名思义,当自己的输出发生变化时,复用器会设置这个标志来通知别的虚拟器件,至于别的器件作什么响应跟自己没有关系,反正我通知到了。
- currentOutput就是我自己当前的实际输出。
- 枚举变量linkCH用来确定currentOutput是链接到的哪个输入通道。
- inputChannel1-3就是复用器的3个输入端口。
- step函数,让复用器完整地运行一次,获取输入,更新输出。
- processSignal函数,看一看当前输出端口应该链接到哪个输入端口。
typedef struct logicMUX //逻辑复用器
{
unsigned char selfActionSignal;
unsigned char currentOutput;
enum
{
OUT_LINK_TO_CH1 = 0,
OUT_LINK_TO_CH2,
OUT_LINK_TO_CH3,
}linkCH;
unsigned char inputChannel1;
unsigned char inputChannel2;
unsigned char inputChannel3;
void (*step)(struct logicMUX* multiplexer);
void (*processSignal)(unsigned char signal, unsigned char value, struct logicMUX* multiplexer);
}logicMUX_t;
尽管c语言中有逻辑与运算“&&”,但是我还是需要将它显式地表达出来,以构造完备的虚拟逻辑器件集和。
uint8_t logic_and2Input(uint8_t a, uint8_t b)
{
if(a && b)
return 1;
else
return 0;
}
新的设备到这里就介绍完成了,接下来就可以构造整个工程的代码了。
#include "reg52.h"
#<