单片机软件常用设计分享(三)驱动设计之数码屏显示设计
前言
本人从事单片机软件设计工作多年,既从事过裸机系统的设计,也在小型嵌入式实时操作系统下进行过设计。因在工作当中发现有些人对单片机软件的设计非常随意,尤其是在驱动方面,其考虑问题过于单一,并且应用层与底层驱动之间耦合度较大。基于此,本人整理工作当中做过常用的最基本设计分享到这里,希望对需要的人有所帮助或参考。当然,可能我的设计方法并不是很好,所以也算是一种学习交流。
在整理的过程中,可能会缺乏统一规划,仅先整理一部分常用的驱动设计。至于其它部分的内容,待今后根据需要再逐一补充。
《驱动设计–数码屏显示驱动》
数码显示,主要指的是7段LED数码显示。实际上,只要是7段数码显示,甚至米子数码显示均适用于此设计。同样在平时做项目时,经常会遇到要控制数码显示的闪烁、交替及滚屏,对于闪烁,包括持续闪烁和闪烁一定的次数,还包括需要设置闪烁的频率,亮与灭的占空比等等;对于交替显示,将依次显示交替内容,并可以设置更换内容的频率,并可设置持续交替和交替一定的次数;对于滚屏显示同样包括持续滚动还是滚动一定的次数,还包括滚动速度的设置。
因此,同样还是有必要设计一个统一管理控制数码屏显示的一个驱动程序,使得其在使用时非常的方便。
另外,这次对驱动进行调试过程中,为了进一步降低软件耦合度,将数码屏设计的所有代码移除。因此,现在的驱动不仅仅适用于7段数码屏,其完全适用于一行或一列的所有显示方式。为了减小修改量,以下仍然对此驱动称为数码屏。
一、数码屏显示方式
一般包括4种显示方式,正常显示、闪烁显示、交替显示、滚动显示;
1.正常显示
数码屏按位(digit)显示输出;
2.闪烁显示
数码屏指定digit位处于两种交替显示状态,即显示与不显示状态;这个需要设置的参数就较多。包括:持续闪烁或闪烁一定次数,闪烁频率,显示占空比等等;
3.交替显示
数码屏指定digit位按照显示内容交替显示,可设置显示时间及更换的时间,持续显示或显示一定次数;为了简便,这里在设计上有一个限制,即被交替显示的每个文本缓冲区需要设置为长度一致(没有使用的存储位置应该补为空),并将多个文本连续的存放在一个缓冲区中。(当然,如果非要设计长度不一致的文本用以交替显示,则驱动部分需要进行必要的修改);
4.滚动显示
数码屏显示内容,按照一定的移动速度,由左向右滚动显示。这个可以设置滚动速度,还可以设置持续滚动或滚动一定次数;同时考虑了开始滚动与结束滚动的状态,因此也会有相关参数的设置;
二、数码屏驱动数据结构
数码屏显示驱动数据结构设计,包括数码屏工作方式、数码屏交替显示参数及状态,数码屏闪烁参数及状态,数码屏滚动参数及状态、数码屏驱动结构,以下分别描述各个部分内容;
1.基本数据结构
以下DDISP为DIGHIT DISPALAY缩写
1)显示状态
//数码屏显示状态
typedef enum
{
DDISP_STATUS_ON, //点亮状态
DDISP_STATUS_OFF //熄灭状态
}tDDispStatus;
2)计数器
//显示屏计数器
typedef struct
{
uint8_t max; //计数最大值(取值0表示持续)
uint8_t exec; //正在执行的计数值
}tDDispCnt;
//显示屏操作起始与终止位置
typedef struct
{
uint8_t start; //起始位置(以数字0开始)
uint8_t end; //终止位置
}tDDispPos;
3)显示频率
//数码屏显示频率
typedef enum
{
DDISP_FREQ_1_10Hz=100, //0.1Hz
DDISP_FREQ_2_10Hz=50, //0.2Hz
DDISP_FREQ_5_10Hz=20, //0.5Hz
DDISP_FREQ_1Hz=10, //1Hz
DDISP_FREQ_2Hz=5, //2Hz
DDISP_FREQ_5Hz=2, //5Hz
DDISP_FREQ_10Hz=1, //10Hz
}tDDispFreq;
4)显示方式
数码屏包括4种显示方式,适合以枚举类型来定义
//数码屏工作方式
typedef enum
{
DDISP_MODE_NORMAL, //正常方式
DDISP_MODE_FLASH, //闪烁方式
DDISP_MODE_ALTERNATING, //交替方式
DDISP_MODE_SCROLL //滚动方式
}tDDispMode;
2.运行数据
1)闪烁运行数据结构
此结构记录执行闪烁显示过程中的部分相关数据及状态。
//闪烁显示运行数据
typedef struct
{
tDDispStatus status; //显示状态
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pDispBuff; //显示内容缓冲区(存储整个digitMax长度显示数据)
tDDispCnt repeat; //重复执行计数值
}tDDispFlash;
2)交替显示运行数据
此结构记录交替闪烁显示过程中的部分相关数据及状态。
//交替显示运行数据
typedef struct
{
bool groupDectect; //显示内容换组检测
tDDispStatus status; //显示状态
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pDispBuff; //显示内容缓冲区(数组)
tDDispCnt group; //显示内容组数
tDDispCnt repeat; //重复执行计数值
}tDDispAlternate;
3)滚动显示运行数据
数码屏滚动显示,内容的更换以从左到右(或从上到下)移动方式实现。
此结构记录滚动显示过程中的部分相关数据及状态。
//滚动显示运行数据
/*-------------------------------------------------------------------------------------------
滚动开始与结束都存在两种效果
1. 开始滚动时存在从起始位置前有多少空格的设置prefixSpace
1) 如滚动区域为digit N,则可设置的空格数为0--N;
2) 不过一般应该设置为0或者N-1/N,设置中间数据似乎有些突兀;
3) 设置为N,其效果就是全部为空格,即不显示;
2. 结束滚动时存在最后一个字符后面有多少个空格的设置suffixSpace
1) 如滚动区域为digit N,则可设置的空格数为0--N;
2) 不过一般应该设置为0或者N-1/N,设置中间数据似乎有些突兀;
3) 设置为N,其效果就是全部为空格,即不显示;
3, prefixSpace和suffixSpace应该不会同时设置为N,否则感觉会怪怪的;
-------------------------------------------------------------------------------------------*/
typedef struct
{
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pDispBuff; //显示内容缓冲区
tDDispCnt offset; //显示内容缓冲区偏移位置
tDDispCnt prefixSpace; //前导空格数(exec逐步自减至0)
tDDispCnt suffixSpace; //后缀空格数(出现空格后exec逐步自加至max)
tDDispCnt repeat; //重复执行计数值
}tDDispScroll;
3.控制参数
用于设置数码屏在多种显示方式中的参数数据,四种显示方式则对应四种参数数据。
//显示控制参数数据(其中pParam根据mode不同而指向不同的显示参数结构)
typedef struct
{
tDDispMode mode; //显示模式
void* pParam; //显示参数
}tDDispCtrlParam;
1)正常显示参数
指定显示区域及显示数据
//正常显示参数
typedef struct
{
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pBuff; //显示内容缓冲区
}tDDispNormalParam;
2)闪烁显示参数
指定闪烁区域及闪烁数据(以下对部分参数作了说明描述)
typedef struct
{
/*-------------------------------------------------------------------------------------
pBuff与onlyFlashData参数说明
1, pBuff!=NULL时,其数据与onlyFlashData设置相关
A, onlyFlashData=TRUE,pBuff仅存放闪烁数据,其余数据从当前显示获取pBuff[0]被写入显存的
pos.start位置,其余以此类推;
B, onlyFlashData=FALSE,pBuff存放完整数据,其显示界面与当前显示内容无关.
pBuff[pos.start]被写入显存的pos.start位置,其余以此类推;
2, pBuff==NULL时,其数据与当前显示内容关联
onlyChange参数说明
onlyChange=TRUE时,除pos及pBuff必须设置数据外,其余数据不必设置,同时控制函数也将不检查其余
数据.存储关系参考1,A说明
---------------------------------------------------------------------------------------*/
bool onlyChange; //仅仅更改闪烁数据(适用于闪烁修改数据)
bool onlyFlashData; //取值TRUE,指示pBuff内存储的仅是FLASH数据,取值FALSE存储完整数据
tDDispFreq freq; //闪烁频率
uint8_t dutyRatio; //取值范围[10/20/30/40/50]
uint8_t counter; //闪烁次数[0--255],0表示持续闪烁
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pBuff; //显示(闪烁)内容缓冲区(可以设置为NULL)
}tDDispFlashParam;
3)交替显示参数
指定显示区域及交替显示的数据组
typedef struct
{
uint8_t counter; //交替显示次数[0--255],0表示持续执行交替显示
uint8_t group; //显示内容组数
uint16_t onTimems; //显示时间(ms)
uint16_t offTimems; //更换显示时间(ms)<可以设置为0,则直接切换到下一组>
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pBuff; //显示内容缓冲区(组)
}tDDispAlternateParam;
4)滚动显示参数
指定显示区域及滚动显示的数据
typedef struct
{
uint8_t counter; //重复执行次数[0--255],0表示持续执行滚动显示
uint8_t charTotal; //滚动字符总数(不能小于滚动区域digit数)
uint8_t prefixSpace; //前导空格数(必须小于等于滚动区域digit数)
uint8_t suffixSpace; //后缀空格数(必须小于等于滚动区域digit数)
uint16_t timems; //滚动一个字符显示时间(不小于扫描时间,且应该取整数<不强制要求>)
tDDispPos pos; //显示滚动内容在数码屏的起止digit位置
char *pBuff; //显示内容缓冲区
}tDDispScrollParam;
4.驱动数据结构
注:这里取消了digit数码屏的定义
1)数码屏驱动函数原型定义
typedef void(*tDDispVoid)(void); //数码屏无参函数原型
typedef bool(*tDDispCtrlFunc)(tDDispCtrlParam*); //数码屏工作控制函数原型
typedef void(*tDDispOutputFunc)(char*); //数码屏输出控制函数原型
2)数码屏驱动函数
以下驱动函数由初始化时外部提供,用以安装
//数码屏驱动函数
typedef struct
{
tDDispOutputFunc output; //安装的数码屏输出控制函数
tDDispVoid init; //安装的数码屏底层初始化函数(可以设置为空)
tDDispVoid unInit; //安装的数码屏底层去初始化函数(可以设置为空)
tDDispVoid lock; //安装的数码屏互斥锁获取函数(可以设置为空)
tDDispVoid unLock; //安装的数码屏互斥锁归还函数(可以设置为空)
}tDDispDrvFunc;
3)数码屏驱动安装参数
此参数传递给初始化函数
//数码屏驱动安装参数
typedef struct
{
bool alwaysScan; //总是扫描标志(表示需要不断刷新以维持显示)
uint8_t scanUnit; //数码屏扫描时间[10/20/30/40/50]
uint8_t digitMax; //数码屏最大digit
tDDispDrvFunc drvFunc; //数码屏驱动函数
}tDDispInitParam;
4)驱动结构
//数码屏基本结构
typedef struct
{
bool cntDetect; //计数检测标志(取值TRUE才可以检测计数值)
uint16_t count; //计数器
uint16_t onCntMax; //点亮计数器最大值
uint16_t offCntMax; //熄灭计数器最大值
tDDispMode mode; //显示模式
tDDispTrig trig; //数码屏点亮及熄灭触发标志
char *pDispBuff; //正常显示内容缓冲区
char *pFuncDispBuff; //功能显示缓冲区
tDDispFlash *pFlash; //闪烁运行数据
tDDispAlternate *pAlternate; //交替显示数据
tDDispScroll *pScroll; //滚动显示数据
}tDDisp;
//数码屏驱动提供给应用层的操作句柄
typedef struct
{
tDDispVoid poll; //poll函数
tDDispCtrlFunc ctrl; //数码屏控制功能函数
}tDDispHandle;
//数码屏驱动程序使用的数据结构
typedef struct
{
uint8_t scanUnit; //数码屏扫描时间单位,ms
uint8_t digitMax; //数码屏最大digit
bool alwaysScan; //总是扫描标志(表示需要不断刷新)
bool needChange; //数码屏需要改变状态时,将被设置为TRUE)
tDDisp_Lock swLock; //数码屏软件互斥锁
tDDisp disp; //数码屏运行数据
tDDispDrvFunc drvFunc; //外部安装的驱动函数
tDDispHandle handle; //外部调用的功能函数(此类型说明在后面安装驱动中说明)
}tDDispDriver;
三、数码屏驱动代码设计
说明:本次改动已经取消字库定义部分,但为了给有需要的朋友参考如何设计7段数码屏的字库,将保留1-1)至1-3)部分内容。
1.字库定义(驱动中已经取消)
注意:驱动中定义了底层输出操作函数中空格符如下定义(最好底层设计与此一致)
#define DDISP_SPACE ' '
1)字符索引号定义(7段数码屏示例)
#define CH_0 0
#define CH_1 1
#define CH_2 2
#define CH_3 3
#define CH_4 4
#define CH_5 5
#define CH_6 6
#define CH_7 7
#define CH_8 8
#define CH_9 9
#define CH_A 10
#define CH_b 11
#define CH_C 12
#define CH_c 13
#define CH_d 14
#define CH_E 15
#define CH_F 16
#define CH_g 17
#define CH_H 18
#define CH_I 19
#define CH_J 20
#define CH_L 21
#define CH_N 22
#define CH_n 23
#define CH_O 24
#define CH_o 25
#define CH_P 26
#define CH_q 27
#define CH_r 28
#define CH_S 29
#define CH_T 30
#define CH_U 31
#define CH_u 32
#define CH_y 33
//7段数码段定义
// --a--
// | |
// f b
// | |
// --g--
// | |
// e c
// | |
// --d-- 。p
#define CH_Seg_a 34//7段数码a段
#define CH_Seg_b 35//7段数码b段
#define CH_Seg_c 36//7段数码c段
#define CH_Seg_d 37//7段数码d段
#define CH_Seg_e 38//7段数码e段
#define CH_Seg_f 39//7段数码f段
#define CH_Seg_g 40//7段数码g段
#define CH_Seg_Point 41//7段数码小数点P
//
#define CH_LFBracket 42//左括号
#define CH_RTBracket 43//右括号
#define CH_Line 44//横线
#define CH_UnderLine 45//下划线
#define CH_EquLine 46//等号线
#define CH_NULL 47//空(无显示)
2)字符定义(7段数码屏示例)
//实际使用时Seg_xx可以被修改(其可对应I/O口)
#define Seg_p 0x80
#define Seg_a 0x40
#define Seg_b 0x20
#define Seg_c 0x10
#define Seg_d 0x08
#define Seg_e 0x04
#define Seg_f 0x02
#define Seg_g 0x01
#define Seg_null 0x00
//实际使用时可以不修改
#define FONT_0 Seg_a|Seg_b|Seg_c|Seg_d|Seg_e|Seg_f
#define FONT_1 Seg_b|Seg_c
#define FONT_2 Seg_a|Seg_b|Seg_d|Seg_e|Seg_g
#define FONT_3 Seg_a|Seg_b|Seg_c|Seg_d|Seg_g
#define FONT_4 Seg_b|Seg_c|Seg_f|Seg_g
#define FONT_5 Seg_a|Seg_c|Seg_d|Seg_f|Seg_g
#define FONT_6 Seg_a|Seg_c|Seg_d|Seg_e|Seg_f|Seg_g
#define FONT_7 Seg_a|Seg_b|Seg_c|Seg_f
#define FONT_8 Seg_a|Seg_b|Seg_c|Seg_d|Seg_e|Seg_f|Seg_g
#define FONT_9 Seg_a|Seg_b|Seg_c|Seg_d|Seg_f|Seg_g
#define FONT_A Seg_a|Seg_b|Seg_c|Seg_e|Seg_f|Seg_g
#define FONT_b Seg_c|Seg_d|Seg_e|Seg_f|Seg_g
#define FONT_C Seg_a|Seg_d|Seg_e|Seg_f
#define FONT_c Seg_d|Seg_e|Seg_g
#define FONT_d Seg_b|Seg_c|Seg_d|Seg_e|Seg_g
#define FONT_E Seg_a|Seg_d|Seg_e|Seg_f|Seg_g
#define FONT_F Seg_a|Seg_e|Seg_f|Seg_g
#define FONT_g Seg_a|Seg_b|Seg_c|Seg_d|Seg_f|Seg_g
#define FONT_H Seg_b|Seg_c|Seg_e|Seg_f|Seg_g
#define FONT_I Seg_e|Seg_f
#define FONT_J Seg_b|Seg_c|Seg_d|Seg_e
#define FONT_L Seg_d|Seg_e|Seg_f
#define FONT_N Seg_a|Seg_b|Seg_c|Seg_e|Seg_f
#define FONT_n Seg_c|Seg_e|Seg_g
#define FONT_O Seg_a|Seg_b|Seg_c|Seg_d|Seg_e|Seg_f
#define FONT_o Seg_c|Seg_d|Seg_e|Seg_g
#define FONT_P Seg_a|Seg_b|Seg_e|Seg_f|Seg_g
#define FONT_q Seg_a|Seg_b|Seg_c|Seg_f|Seg_g
#define FONT_r Seg_e|Seg_g
#define FONT_S Seg_a|Seg_c|Seg_d|Seg_f|Seg_g
#define FONT_T Seg_a|Seg_e|Seg_f
#define FONT_U Seg_b|Seg_c|Seg_d|Seg_e|Seg_f
#define FONT_u Seg_c|Seg_d|Seg_e
#define FONT_y Seg_b|Seg_c|Seg_d|Seg_f|Seg_g
#define FONT_Seg_a Seg_a
#define FONT_Seg_b Seg_b
#define FONT_Seg_c Seg_c
#define FONT_Seg_d Seg_d
#define FONT_Seg_e Seg_e
#define FONT_Seg_f Seg_f
#define FONT_Seg_g Seg_g
#define FONT_Seg_Point Seg_p
#define FONT_LFBracket Se