单片机编程技巧之重用外设驱动代码
第六章为重用外设驱动代码,本文内容为6.5 键盘与数码管接口。
6.5 键盘与数码管接口
>>> 6.5.1 zlg72128 接口
当矩阵扩大到一定数目时,逐行扫描的方法会显得费时,如果需要对2 个以上的按键“同时”操作时,则处理起来更是麻烦。zlg72128 是zlg 自行设计的数码管显示驱动与键盘扫描管理芯片,能够直接驱动12 位共阴式数码管(或96 只独立的led),同时还可以扫描管理多达32 个按键,其中的8 个按键如同电脑键盘上的ctrl、shift 和alt 键一样可以作为功能键使用。
另外,zlg72128 内部还设置有连击计数器,能够使某键按下后不松手而连续有效。该芯片为工业级芯片,抗干扰能力强,在工业测控中已有大量应用。
1. 特点
直接驱动12 位1 英寸以下的共阴式数码管或96 只独立的led;
能够管理多达32 个按键,其中的8 个按键可以用作功能键,自动消除抖动;
利用功率电路可以方便地驱动1 英寸以上的大型数码管;
具有位闪烁、位消隐、段点亮、段熄灭、功能键、连击键计数等强大功能;
具有10 种数字和21 种字母的译码显示功能,亦可直接向显示缓存写入显示数据;
软件配置支持0~12 个数码管显示模式;
与mcu 之间采用i2c串行总线接口;
工作电压范围:3.0~5.5v;
工作温度范围:-40~+85℃;
封装:tssop28。
2. 典型应用电路
如图6.12 所示为zlg72128 的管脚排列图,其相应的管脚功能说明详见表6.34。
表6.34 引脚功能表
图6.12 zlg72128 管脚排列图
如图6.13 所示为按键电路,zlg72128 能够管理多达32 个按键(4 行8 列),行线分别连接com8 ~ com11 引脚,列线分别连接com0 ~ com7。特别地,前3 行按键(共计24个按键)是普通按键,按键按下时会通过int 引脚通知用户,按键释放时不做任何通知。最后一行按键(共计8 个按键)是功能键,其以一个8 位数据表示8 个键值的状态,f0 ~ f7分别对应bit0 ~ bit 7。按下时相应位为0,释放时相应位为1,只要表示这8 个按键的8 位数据值发生变化,则会通过int 引脚通知用户,因此对于功能按键,按键按下或释放用户均能够得到通知。
注意,需要在键盘电路与zlg72128 芯片引脚之间连接一个电阻,其典型值为1kω。在多数应用中可能不需要这么多的键,这时既可以按行也可以按列裁减键盘。需要注意的是,该按键电路对于3 个或3 个以上键按下的情况是不适用的。
图6.13 按键电路
如图6.14 所示是针对2 个或2 个以上功能键与普通键搭配使用的情况下的按键电路,在功能键与普通键之间加了一个二极管,注意:二极管应该尽量选择导通压降较小的。
图6.14 多个功能键复用按键电路
如图6.15 所示为zlg72128 的典型应用电路原理图,用户在使用芯片驱动数码管与管理按键时,可参考该电路进行电路设计。zlg72128 只能直接驱动12 位共阴式数码管驱动,在数码管的段与zlg72128 芯片引脚之间需要接一个限流电阻,其典型值为270ω。如果需要增大数码管的亮度,则可以适当减小电阻值。zlg72128 的驱动能力毕竟有限,当使用大型数码管时,则可能显示亮度不够,这时可以适当减小数码管的限流电阻值以增加亮度,阻值最小为200ω,如果亮度依旧不够,就必须加入功率驱动电路,详见zlg72128 用户手册(http://www.zlgmcu.com)。
为了使zlg72128 芯片电源稳定,一般在vcc 和gnd 之间接入一个47~470μf 的电解电容。按照i2c总线协议的要求,信号线scl 和sda 上必须分别接上拉电阻,其典型值是4.7kω。当通信速率大于100kbps 时,建议减小上拉电阻的值。芯片复位引脚rst 是低电平有效,可以将其接入到mcu 的i/o 来控制其复位。key_int 引脚可输出按键中断请求信号(低电平有效),可以连接到mcu 的i/o 来获取按键按下或释放事件。
图6.15 zlg72128 典型应用电路
3. 寄存器详解
zlg72128 内部有12 个显示缓冲寄存器dispbuf0~dispbuf11,它们直接决定数码管显示的内容。zlg72128 提供有2 种显示控制方式,一种是直接向显存写入字型数据,另一种是通过向命令缓冲寄存器写入控制指令实现自动译码显示。访问这些寄存器需要通过i2c总线接口来实现,zlg72128 的i2c总线器件地址是60h(写操作)和61h(读操作),访问
内部寄存器要通过“子地址”实现。
(1)系统寄存器systemreg(地址:00h)
系统寄存器的第0 位(lsb)称作keyavi,标志着按键是否有效,0-没有按键被按下,1-有某个按键被按下。systemreg 寄存器的其它位暂时没有定义。当按下某个键时,zlg72128 的key_int 引脚会产生一个低电平的中断请求信号。当读取键值后,中断信号就会自动撤销(变为高电平),而keyavi 也同时予以反映。正常情况下mcu 只需要判断key_int 引脚即可。通过不断查询keyavi 位也能判断是否有键按下,这样就可以节省微控制器的一根i/o 口线,但是i2c总线处于频繁的活动状态,多消耗电流且不利于抗干扰。
(2)键值寄存器key(地址:01h)
如果k1~k24 的某个普通键被按下,则微控制器可以从键值寄存器key 中读取相应的键值1~24。如果微控制器发现zlg72128 的key_int 引脚产生了中断请求,而从key 中读到的键值是0,则表示按下的可能是功能键。键值寄存器key 的值在被读走后自动变成0。
(3)连击计数器repeatcnt(地址:02h)
zlg72128 为k1~k24 提供了连击计数功能。所谓连击是指按住某个普通键不松手,经过两秒钟的延迟后,开始连续有效,连续有效间隔时间约两百毫秒。这一特性跟电脑上的键盘很类似。在微控制器能够及时响应按键中断并及时读取键值的前提下,当按住某个普通键一直不松手时:首先会产生一次中断信号,这时连击计数器repeatcnt 的值仍然是0;经过两秒延迟后,会连续产生中断信号,每中断一次repeatcnt 就自动加1;当repeatcnt 计数到255 时就不再增加,而中断信号继续有效。在此期间,键值寄存器的值每次都会产生。
(4)功能键寄存器functionkey(地址:03h)
zlg72128 提供的8 个功能键f0~f7。功能键常常是配合普通键一起使用的,就像电脑键盘上的shift、ctrl 和alt 键。当然功能键也可以单独去使用,就像电脑键盘上的f1~f12。当按下某个功能键时,在key_int 引脚也会像按普通键那样产生中断信号。功能键的键值是被保存在functionkey 寄存器中的。功能键寄存器functionkey 的初始值是ffh,每一个位对应一个功能键,第0 位(lsb)对应f0,第1 位对应f1,依此类推,第7 位(msb)对应f7。某一功能键被按下时,相应的functionkey 位就清零。功能键还有一个特性就是“二次中断”,按下时产生一次中断信号,抬起时又会产生一次中断信号;而普通键只会在被按下时产生一次中断。
(5)命令缓冲区cmdbuf0 和cmdbuf1(地址:07h 和08h)
通过向命令缓冲区写入相关的控制命令可以实现段寻址、下载显示数据功能。
(6)闪烁控制寄存器flashonoff(地址:0bh)
flashonoff 寄存器决定闪烁频率和占空比。复位值为0111 0111b。高4 位表示闪烁时亮的持续时间,低4 位表示闪烁时灭的持续时间。改变flashonoff 的值,可以同时改变闪烁频率和占空比。flashonoff 取值00h 时可获得最快的闪烁速度,亮灭时间计算公式如下:
t = n × 50 + 150ms
t 为闪烁时亮或灭的持续时间,n 为寄存器的高4 位或低4 位的值,取值0~15.最快闪烁频率为3.33hz(周期为300ms),最慢闪烁频率为0.55hz(周期为1.8s)。特别说明:单独设置flashonoff 寄存器的值,不会看到显示闪烁,而应该配合闪烁控制命令一起使用。
(7)消隐寄存器dispctrl0(地址:0ch)和dispctrl1(地址:0dh)
如表6.35 所示为消隐寄存器,dispctrl0、dispctrl1 寄存器决定哪些位是否显示,对应数码管的1~12 位。寄存器位为1 时,对应数码管位不显示。复位值都是0x00,即数码管的12 个位都扫描显示。
表6.35 消隐寄存器
在实际应用中可能需要显示的位数不足12 位,例如只显示8 位,这时可以把dispctrl0的值设置为0x0f,把dispctrl1 的值设置为0x00,则数码管的第0~7 位被扫描显示,而第8~12 位不会显示。
(8)闪烁寄存器flash0(地址:0eh)和flash1(地址:0fh)
如表6.36 所示为闪烁寄存器,flash0、flash 1 寄存器决定哪些位是否闪烁,对应数码管的1~12 位。寄存器位为1 时,对应数码管位闪烁。复位值都是0x00,即数码管的12 个位都不闪烁。
表6.36 闪烁寄存器
在实际应用中可能需要某些位闪烁,例如最后2 位闪烁,这时可以把flash0 的值设置为0x00,把flash1 的值设置为0x03,则数码管的第1、2 位闪烁,而第3~12 位不会闪烁。
(9)显示缓冲区dispbuf0~dispbuf11(地址:10h~1bh)
dispbuf0~dispbuf11 这12 个寄存器的取值直接决定了数码管的显示内容。每个寄存器的8 个位分别对应数码管的a、b、c、d、e、f、g、dp 段,msb 对应a,lsb 对应dp。例如大写字母h 的字型数据为6eh(不带小数点)或6fh(带小数点)。
4. 控制命令详解
寄存器cmdbuf0(地址:07h)和cmdbuf1(地址:08h)共同组成命令缓冲区。通过向命令缓冲区写入相关的控制命令可以实现段寻址、下载显示数据、控制闪烁等功能。
(1)段寻址(segonoff)
如表6.37 所示为段寻址寄存器,在段寻址命令中12 位数码管被看成是96 个段,每一个段实际上就是一个独立的led。
双字节命令在指令格式中,cmdbuff0 的高4 位“0001”是命令码,cmdbuff0 的最低位on 位表示该段是否点亮,0—熄灭,1—点亮。cmdbuff0 的b3b2b1b0 是位地址,取值0~11。s3s2s1s0 是4 位段地址,取值0~7,对应数码管的a、b、c、d、e、f、g、dp。
表6.37 段寻址寄存器
(2)下载数据并译码(download)
如表6.38 所示为下载数据及译码寄存器,双字节命令在指令格式中,cmdbuff0 的高4位“0010”是命令码a3a2a1a0 是数码管显示数据的位地址,位地址编号按从左到右的顺序依次为11、10、9、8、……、0,dp 控制小数点是否点亮,0-熄灭,1-点亮。flash 表示是否要闪烁,0-正常显示,1-闪烁。d4d3d2d1d0 是要显示的数据,包括10 种数字和21种字母,显示数据按照表6.39 中的规则进行译码。
表6.38 下载数据、译码寄存器
表6.39 下载数据并译码命令的数据表
(3)复位命令(reset)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“0011”是命令码,其功能是将所有led 熄灭,详见表6.40。
表6.40 复位命令寄存器
(4)测试命令(test)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“0100”是命令码,其功能是将所有led 按照0.5s 的速率闪烁,详见表6.41。
表6.41 测试命令寄存器
(5)左移命令(shiftleft)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“0101”是命令码,详见表6.42。功能是以数码管的位为单位的,左移n 位。左移后右边空出的位不显示任何内容,即全部led熄灭状态。n 的取值范围1~11,大于11 的值无效,n 的值由cmdbuf0 的低4 位决定,按下列公式计算:
n = (b3×8)+( b2×4)+ (b1×2)+ b0
表6.42 左移命令寄存器
(6)循环左移命令(cyclicshiftleft)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“0110”是命令码,详见表6.43。功能是以数码管的位为单位的,循环左移n 位。
左移后右边显示从最左边移出的内容。n 的取值范围1~11,大于11 的值无效,n 的值由cmdbuf0 的低4 位决定,按下列公式计算:
n = (b3×8)+( b2×4)+ (b1×2)+ b0
表6.43 循环左移命令寄存器
(7)右移命令(shiftright)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“0111”是命令码,详见表6.44。功能是以数码管的位为单位的,右移n 位。
右移后左边空出的位不显示任何内容,即全部led 熄灭状态。n 的取值范围1~11,大于11 的值无效,n 的值由cmdbuf0 的低4 位决定,按下列公式计算:
n = (b3×8)+( b2×4)+ (b1×2)+ b0
表6.44 右移命令寄存器
(8)循环右移命令(cyclicshiftright)
单字节命令,在指令格式中,cmdbuf0 的高4 位的“1000”是命令码,详见表6.45。功能是以数码管的位为单位的,循环右移n 位。右移后左边显示从最右边移出的内容,n 的取值范围1~11,大于11 的值无效,n 的值由cmdbuf0 的低4 位决定,按下列公式计算:
n = (b3×8)+( b2×4)+ (b1×2)+ b0
表6.45 循环右移命令寄存器
(9)数码管扫描位数设置指令 (scanning)
单字节命令,在指令格式中cmdbuf0 的高4 位的“1001”是命令码,设置数码管扫描位数n,详见表6.46。n 的取值为0~12,大于12 按12 位进行扫描。扫描位数n 以位选端第1位开始到位选端第n 位扫描有效。n 的值由cmdbuf0 的低四位决定,按下列公式计算。
n = (b3×8)+( b2×4)+ (b1×2)+ b0
表6.46 扫描位数设置寄存器
在使用过程中,如果不需要12 位数码管显示,从最高位开始裁剪,同时将数码扫描位数设置成相应的数码管位数。数码管的扫描位数减少后,有用的显示位由于分配的扫描时间更多,因而显示亮度得以提高。
>>> 6.5.2 zlg72128 初始化
ametal 已经提供了zlg72128 的驱动函数,使用其它各功能函数管理数码管和按键前,必须先完成zlg72128 的初始化。其初始化函数(am_zlg72128.h)的原型为:
该函数意在获取zlg72128 的实例句柄。其中,p_dev 是指向am_zlg72128_dev_t 类型实例的指针,p_devinfo 是指向am_zlg72128_devinfo_t 类型实例信息的指针。
实例
定义am_zlg72128_dev_t 类型(am_zlg72128.h)实例如下:
其中,g_zlg72128_dev 为用户自定义的实例,其地址作为p_dev 的实参传递。
实例信息
实例信息描述了中断引脚相关的信息, 其类型am_zlg72128_devinfo_t 的定义(am_zlg72128.h)如下:
其中,use_int_pin 表示是否使用zlg72128 的中断输出引脚(key_int)。若该值为true,表明需要使用中断引脚,此时int_pin 指定与主控制器(如lpc824)连接的引脚号,按键键值将在引脚中断中获取;若该值为false,表明不使用中断引脚,此时interval_ms 指定查询键值的时间间隔。
一般地,只要主控器的i/o 资源不是非常紧缺,均会使用中断引脚。若为节省一个i/o中断资源,可将use_int_pin 设置为false,此时将不占用io 中断资源,而系统将会以查询的方式从zlg72128 中获取键值,这就会耗费一定的cpu 资源,因为每隔一段时间就要主动查询一次键值。假设使用zlg72128 的中断引脚,主控制器使用lpc824,zlg72128 的key_int 引脚与lpc824 的pio0_17 连接。其实例信息定义如下:
i2c句柄i2c_handle
若使用lpc824 的i2c1 与zlg72128 通信,则i2c句柄可以通过lpc82x 的i2c1 实例初始化函数am_lpc82x_i2c1_inst_init()获得。即:
获得的i2c句柄即可直接作为i2c_handle 的实参传递。
实例句柄
am_zlg72128_init()函数的返回值为zlg72128 实例的句柄,该句柄将作为其它功能接口(数码管显示、按键管理等)的第一个参数(handle)的实参。
其类型am_zlg72128_handle_t(am_zlg72128.h)定义如下:
若返回值为null,说明初始化失败;若返回值不为null,说明返回一个有效的handle。
基于模块化编程思想,将初始化相关的实例、实例信息等的定义存放到对应的配置文件中,通过头文件引出实例初始化函数接口,源文件和头文件的程序范例分别详见程序清单6.111 和程序清单6.112。
程序清单6.111 zlg72128 实例初始化函数实现(am_hwconf_zlg72128.c)
程序清单6.112 zlg72128 实例初始化函数声明(am_hwconf_zlg72128.h)
后续只需要使用无参数的实例初始化函数即可获取到zlg72128 的实例句柄。即:
>>> 6.5.3 按键管理接口函数
zlg72128 支持32 个键(4 行8 列矩阵键盘),其中,前3 行为普通键,同一时刻只能有一个普通键按下。最后一行为功能键,多个功能键可以同时按下。按键管理仅一个注册按键回调接口函数。
为了在检测到按键事件(有键按下)时,及时将按键事件通知用户,需要用户注册一个回调函数,当有按键事件发生时,将自动调用用户注册的回调函数。其函数原型为:
其中,pfn_key_cb 为注册的按键回调函数,p_arg 为回调函数的第一个参数的值,即当检测到按键事件自动调用回调函数时,将p_arg 的值作为回调函数的第一个参数的值。
回调函数的类型am_zlg72128_key_cb_t(am_zlg72128.h)定义如下:
由此可见,回调函数有4 个参数,用户可以通过这些参数获取按键相关的信息。特别地,第一个参数p_arg 为用户自定义的参数,其值即为注册回调函数时p_arg 参数设置的值。
key_val、repeat_cnt、funkey_val 表示按键事件的相关信息,zlg72128 可能的按键事件有以下3 种:
有普通键按下(普通键释放不作为按键事件)
当有普通键按下时,key_val 表示按下键的键值,键值的有效范围为1 ~ 24,普通键的键值已在am_zlg72128.h 中定义为宏,宏名为am_zlg72128_key_x_y,其中x 表示行号(1 ~ 3),y 表示列号(1 ~ 8),如第2 行第5 个键的键值为:am_zlg72128_key_2_5。
普通键一直按下(处于连击状态)
普通键按下保持时间超过2s 后进入连击状态,处于连击状态时,每隔200ms 左右会产生一个按键事件,并使用一个连击计数器对产生的按键事件计数,每产生一个按键事件连击计数器的值加1,由于连击计数器的位宽为8 位,因此,当值达到255 后不再加1,但同样还会继续产生按键事件,直到键释放,连击计数器清0。处于连击状态时,key_val 表示按下键的键值,repeat_cnt 表示连击计数器的值。
功能键状态发生变化(功能键按下或释放都会造成状态改变)
funkey_val 的值表示所有功能键的状态。最后一行最多8 个键,从左至右分别为f0 ~ f7,与funkey_val 的bit0 ~ bit7 一一对应,位值为0 表示对应功能键按下,位值为1 表示对应功能键未按下。当无任何功能键按下时,funkey_val 的值为0xff。只要funkey_val 的值发生改变,就会产生一个按键事件,功能键不提供连击功能。可以使用am_zlg72128.h 中的宏
am_zlg72128_funkey_check(funkey_val, funkey_num)来简单判断某一功能键是否按下。funkey_num 用于表示需要检测的功能键, 值已经定义为宏, f0 ~ f7 分别为am_zlg72128_funkey_0 ~ am_zlg72128_funkey_7。若对应键按下,则宏值为ture;反之,则宏值false。例如,通过funkey_val 判断f0 是否按下可以使用如下语句:
功能键类似pc 机上的ctrl、alt、shift 等按键,使用普通键和功能键很容易实现组合键应用,注册按键回调函数的范例程序详见程序清单6.113。
程序清单6.113 zlg72128 注册按键回调函数使用范例
若只按下第一行第一个键,则led0 状态翻转,若按下第一行第一个键的同时,也按下了功能键f0,则led1 状态翻转,该示例简单的展示了组合键的使用方法。
>>> 6.5.4 数码管显示接口函数
zlg72128 支持12 位共阴式数码管,以及闪烁、位移等功能,虽然接口函数种类繁多,但各个接口函数的功能较为简单,下面将一一介绍各个接口函数的使用方法。
1. 闪烁持续时间
当数码管闪烁时,设置其点亮和熄灭持续时间的函数原型为:
上电时,数码管点亮和熄灭的持续时间默认值为500ms。on_ms 和off_ms 有效的时间值为150、200、250、……、800、850、900,即150ms ~ 900ms,且时间间隔为50ms。若时间间隔不是这些值,应该选择一个最接近的值。比如,设置数码管以最快的频率闪烁,即亮、灭时间最短为150ms,其使用方法如下:
注:仅设置闪烁时间还不能立即看到闪烁现象,必须打开某位的闪烁开关后才能看到闪烁现象,详见am_zlg72128_digitron_flash_ctrl()函数介绍。
2. 闪烁控制
控制数码管是否闪烁的函数原型为:
其中,ctrl_val 为控制值,bit0 ~ bit11 为有效位,分别对应数码管0 ~ 11,位值为0 时不闪烁,位值为1 时闪烁。上电默认值为0x0000,即所有数码管均不闪烁。比如,控制所有数码管闪烁,其使用方法如下:
注:由于初始时可能数码管未显示任何内容,这段代码可能看不到闪烁现象,因此可以在设置前,使用后续相关api 使数码管显示一些实际有效的内容。
3. 显示属性(开或关)
显示属性是指控制哪些数码管显示,哪些数码管不显示。在默认情况下,所有数码管均处于打开显示状态,扫描12 位数码管。而实际上,可能需要显示的位数并不足12 位,此时可以使用该函数关闭某些位的显示。其函数原型为:
其中,ctrl_val 为控制值,bit0 ~ bit11 为有效位,分别对应数码管0 ~ 11,位值为0 时打开显示,位值为1 时关闭显示。上电的默认值为0x0000,即所有位均正常显示。比如,只使用了数码管0 ~ 7,基于此,可以关闭数码管8 ~ 11,其使用方法如下:
注:使用该函数控制显示属性时,对应数码管的段码内容并不会改变。
4. 显示字符
在指定位置显示字符,zlg72128 已经提供了0 ~ 9 这10 个数字和常见的21 种字母的自动译码显示,无需应用再自行译码。其函数原型为:
显示的字符必须是zlg72128 已经支持的可以自动完成译码的字符,包括字符'0'~'9'与abcdefghijlopqrtuycht(区分大小写)。注意,若要显示数字1,则ch 参数应为字符'1',而不是数字1。
若指定的字符不支持,则返回-am_enotsup。只要成功显示,则返回am_ok。若需要显示一些自定义的图形,使用 am_zlg72128_digitron_dispbuf_set() 直接设置显示的段码。
比如,在数码管0 显示字符f,不显示小数点,不闪烁,其使用方法如下:
5. 显示字符串
指定字符串显示的起始位置,开始显示一个字符串。其函数原型为:
字符串显示遇到字符结束标志'