单片机软件常用设计分享(三)驱动设计之数码屏显示设计
前言
本人从事单片机软件设计工作多年,既从事过裸机系统的设计,也在小型嵌入式实时操作系统下进行过设计。因在工作当中发现有些人对单片机软件的设计非常随意,尤其是在驱动方面,其考虑问题过于单一,并且应用层与底层驱动之间耦合度较大。基于此,本人整理工作当中做过常用的最基本设计分享到这里,希望对需要的人有所帮助或参考。当然,可能我的设计方法并不是很好,所以也算是一种学习交流。
在整理的过程中,可能会缺乏统一规划,仅先整理一部分常用的驱动设计。至于其它部分的内容,待今后根据需要再逐一补充。
《驱动设计–数码屏显示驱动》
数码显示,主要指的是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 Seg_a|Seg_d|Seg_e|Seg_f
#define FONT_RTBracket Seg_a|Seg_b|Seg_c|Seg_d
#define FONT_Line Seg_g
#define FONT_UnderLine Seg_d
#define FONT_EquLine Seg_d|Seg_g
#define FONT_NULL Seg_null
3)字库定义(7段数码屏示例)
DDispFont命名不可以修改
const uint8_t DDispFont[]=
{
FONT_0,FONT_1,FONT_2,FONT_3,FONT_4,FONT_5,FONT_6,FONT_7,FONT_8,FONT_9,
FONT_A,FONT_b,FONT_C,FONT_c,FONT_d,FONT_E,FONT_F,FONT_g,FONT_H,FONT_I,
FONT_J,FONT_L,FONT_N,FONT_n,FONT_O,FONT_o,FONT_P,FONT_q,FONT_r,FONT_S,
FONT_T,FONT_U,FONT_u,FONT_y,FONT_Seg_a,FONT_Seg_b,FONT_Seg_c,FONT_Seg_d,FONT_Seg_e,FONT_Seg_f,
FONT_Seg_g,FONT_Seg_Point,FONT_LFBracket,FONT_RTBracket,FONT_Line,FONT_UnderLine,FONT_EquLine,FONT_NULL
};
2.参数及变量定义
#define DDISPSCAN_UINT_DEF 10 //数码屏扫描默认时间(ms)
//以下定义内存申请与释放
#define MALLOC_MEM(memSize) pvPortMalloc(memSize) //内存申请宏
#define FREE_MEM(pMem) vPortFree(pMem) //内存释放宏
tDDispDriver *pDDispDrv=NULL; //仅用于驱动内部的驱动指针
3.驱动轮询处理
驱动轮询输出函数,这里仅说明其控制逻辑,涉及到的详细子模块在驱动文件中可查看。
从以下代码中其实可以看出,如没有指定维持扫描,则在数码屏正常显示时,POLL将不会往下执行,这其中主要依靠pDDispDrv->needChange变量控制。
另外,POLL主要根据显示方式执行相应的处理,并确定下次扫描时是否需要继续执行。
static void DDisp_Poll(void)
{
bool output=FALSE;
//驱动安装失败,拒绝执行
if (pDDispDrv==NULL||pDDispDrv->needChange==FALSE)
return;
if (pDDispDrv->alwaysScan==FALSE)
pDDispDrv->needChange=FALSE;
else
output=TRUE;
DDisp_Lock();
//根据显示方式分开处理
switch (pDDispDrv->disp.mode)
{
case DDISP_MODE_NORMAL:
output=TRUE;
break;
case DDISP_MODE_FLASH:
if (pDDispDrv->disp.pFlash->status==DDISP_STATUS_ON)
output=DDisp_FlashOnCheck(); //闪烁点亮计时,并检测切换至闪烁灭状态
else
output=DDisp_FlashOffCheck(); //闪烁熄灭计时,并检测切换至闪烁亮状态,同时,检测闪烁次数
break;
case DDISP_MODE_ALTERNATING:
if (pDDispDrv->disp.pAlternate->status==DDISP_STATUS_ON)
output=DDisp_AlternateOnCheck();
else
output=DDisp_AlternateOffCheck();
break;
case DDISP_MODE_SCROLL:
output=DDisp_Scroll();
break;
}
if (pDDispDrv->disp.mode>=DDISP_MODE_FLASH)
pDDispDrv->needChange=TRUE;
if (pDDispDrv->disp.trig==DDISP_TRIG_ON||output==TRUE)
{
pDDispDrv->disp.trig=DDISP_TRIG_OFF;
if (pDDispDrv->disp.mode==DDISP_MODE_NORMAL)
pDDispDrv->drvFunc.output(pDDispDrv->disp.pDispBuff);
else
pDDispDrv->drvFunc.output(pDDispDrv->disp.pFuncDispBuff);
}
DDisp_UnLock();
}
4.驱动控制操作
可以对数码屏进行4种工作方式操作,其控制指针中的参数将根据具体的工作方式执行相应的参数。以下代码中同样仅描述其逻辑处理,涉及到的子模块将在驱动文件中可查看。
static bool DDisp_CtrlFunc(tDDispCtrlParam *pCtrl)
{
bool result=FALSE;
DDisp_Lock();
if (pCtrl!=NULL&&pCtrl->pParam!=NULL)
{
switch (pCtrl->mode)
{
case DDISP_MODE_NORMAL:
result=DDisp_CtrlNormal((tDDispNormalParam*)pCtrl->pParam);
break;
case DDISP_MODE_FLASH:
result=DDisp_CtrlFlash((tDDispFlashParam*)pCtrl->pParam);
break;
case DDISP_MODE_ALTERNATING:
result=DDisp_CtrlAlterate((tDDispAlternateParam*)pCtrl->pParam);
break;
case DDISP_MODE_SCROLL:
result=DDisp_CtrlScroll((tDDispScrollParam*)pCtrl->pParam);
break;
}
}
if (result==TRUE)
{
pDDispDrv->disp.trig=DDISP_TRIG_ON;
pDDispDrv->needChange=TRUE;
}
DDisp_UnLock();
return result;
}
5.数码屏驱动安装
数码屏驱动安装主要完成驱动内存申请,驱动函数安装及控制函数返回等。
返回tDDispHandle*类型的数码屏操作句柄,用于应用程序调用,若返回为NULL,则安装失败;返回tDDispVoid类型的poll被用于POLL调用,tDDispCtrlFunc类型的ctrl被用于应用程序控制数码屏工作模式时调用;
安装中使用的相关数据结构前面已经列出,此处不再说明。
数码屏驱动安装函数
tDDispHandle* DDisp_Initial(tDDispInitParam *pInitParam)
{
uint8_t loop;
if (pInitParam==NULL||
pInitParam->drvFunc.output==NULL||
pInitParam->digitMax==0||
pInitParam->scanUnit<DDISP_SCAN_MIN||
pInitParam->scanUnit>DDISP_SCAN_MAX||
pDDispDrv!=NULL)
return NULL;
pDDispDrv=MALLOC_MEM(sizeof(tDDispDriver)+pInitParam->digitMax);
pDDispDrv->disp.pDispBuff=(char*)pDDispDrv+sizeof(tDDispDriver);
for (loop=0;loop<pInitParam->digitMax;loop++)
pDDispDrv->disp.pDispBuff[loop]=DDISP_SPACE;
//检查扫描时间
if (pInitParam->scanUnit==0)
pDDispDrv->scanUnit=DDISPSCAN_UINT_DEF;
else
pDDispDrv->scanUnit=pInitParam->scanUnit;
pDDispDrv->alwaysScan=pInitParam->alwaysScan;
pDDispDrv->digitMax=pInitParam->digitMax;
pDDispDrv->needChange=TRUE;
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.trig=DDISP_TRIG_OFF;
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
//安装驱动处理函数及设置参数
pDDispDrv->drvFunc=pInitParam->drvFunc;
//执行低级初始化操作
if (pDDispDrv->drvFunc.init!=NULL)
pDDispDrv->drvFunc.init();
pDDispDrv->handle.poll=DDisp_Poll;
pDDispDrv->handle.ctrl=DDisp_CtrlFunc;
return &pDDispDrv->handle;
}
6.数码屏驱动卸载
数码屏驱动的卸载调用前,必须手动停止数码屏驱动轮询的定时调用,此处不对其进行检查,仅使用应用层提供的互斥锁。
bool DDisp_UnInitial(void)
{
bool result=TRUE;
DDisp_Lock();
if (pDDispDrv!=NULL)
{
//执行低级去初始化操作
if (pDDispDrv->drvFunc.unInit!=NULL)
pDDispDrv->drvFunc.unInit();
DDisp_FreeFuncResources();
FREE_MEM(pDDispDrv);
pDDispDrv=NULL;
}
else
result=FALSE;
DDisp_UnLock();
return result;
}
四、 总结
1.必要的移植修改设计说明
1)需要在应用层定义数码屏,仅需要与驱动统一DDISP_SPACE定义;
2)至少需要设计数码屏输出函数(output);
3)不管是裸机运行还是在操作系统下运行,均需要进行数码屏驱动安装;
4)设计中将轮询函数提供给上层去决定如何执行,这样可以进一步降低耦合度以及增强数码屏驱动使用的灵活性。对于裸机运行可将轮询函数放在硬件定时器产生的POLLING中执行;对于运行在操作系统(如FreeRTOS)下可以将轮询函数放在某个软件定时器的回调函数中执行(或发送事件到某个任务执行);甚至,可以根据具体的操作系统设计核心功能,尤其是不需要扫描的情况下,可以设计为更新时执行,但这样就大大降低了其通用性,这不是本设计所追求的。
5)可能还会有一些没能想到的功能,如果有需要,仍然可以添加到轮询输出函数及外部功能函数中;
2.外部调用接口
1)驱动安装函数DDisp_Initial是唯一直接提供给应用层必须调用的接口;
2)驱动卸载函数DDisp_UnInitial是直接接提供给应用层可选调用的接口;
3)轮询输出函数DDisp_Poll是间接提供给应用层周期性调用的接口;
4)外部功能函数DDisp_CtrlFunc是间接提供给应用层控制数码屏工作方式调用的接口;
3.数码屏驱动使用示例
由于本人不知道如何(或是否可以)在这里放文件,只有贴出全部代码,包括驱动文件DDispDriver.c与DDispDriver.h,同时提供一份Display.c文件,这是我用以调测此驱动的全部应用层代码。
以下给出的三份文件,是经过本人测试通过的,原则上可以直接使用。(当然,没有做异常测试,仅正常调用运行)
1)DDispDriver.h文件代码(驱动.h文件)
#ifndef _LLEDDRIVER_H
#define _LLEDDRIVER_H
#ifndef TRUE
#define TRUE 1
#define FALSE 0
typedef uint8_t bool;
#endif
//以下DDISP为DIGHIT DISPALAY缩写
//数码屏软件锁(非OS使用)
typedef enum
{
DDISP_UNLOCKED,
DDISP_LOCKED
}tDDisp_Lock;
//数码屏显示状态
typedef enum
{
DDISP_STATUS_ON, //点亮状态
DDISP_STATUS_OFF //熄灭状态
}tDDispStatus;
//数码屏 输出强制触发标志
typedef enum
{
DDISP_TRIG_ON, //触发打开
DDISP_TRIG_OFF //触发关闭
}tDDispTrig;
//显示屏计数器
typedef struct
{
uint8_t max; //计数最大值(取值0表示持续)
uint8_t exec; //正在执行的计数值
}tDDispCnt;
//显示屏操作起始与终止位置
typedef struct
{
uint8_t start; //起始位置(以数字0开始)
uint8_t end; //终止位置
}tDDispPos;
#define DDISP_BASETIME 100 //基本时间(ms)<基于闪烁最高频率的时间>
//部分闪烁参数限定值
#define DDISP_DUTY_MIN 10 //占空比最小值
#define DDISP_DUTY_MAX 90 //占空比最大值
#define DDISP_SCAN_MIN 10 //扫描时间最小值
#define DDISP_SCAN_MAX 50 //扫描时间最大值
//显示屏闪烁频率
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;
//数码屏工作方式
typedef enum
{
DDISP_MODE_NORMAL, //正常方式
DDISP_MODE_FLASH, //闪烁方式
DDISP_MODE_ALTERNATING, //交替方式
DDISP_MODE_SCROLL //滚动方式
}tDDispMode;
/*------------------------------------------------------------------------------------------
关于数码屏部分显示的说明:
公共特性,可以设置在显示屏的开始与结束位置执行显示的区域。即参数tDDispPos pos均需要设置,并
指定了显示区域。
1 FLASH(闪烁)显示:在指定区域按照指定频率与占空比闪烁显示指定内容。
其显示与闪烁内容有2种方式:
1) 更新全部(或部分)原有显示内容,并在指定区域闪烁显示;
2) 使用原有显示内容,并在指定区域闪烁显示;
关于设置部分的详细内容请见tDDispFlashParam参数说明
2 ALTERNATING(交替)显示:在指定区域按照指定的时间间隔交替显示设置的
多组文本内容。以下参数在设置时有一定约束或要求
1) 多组显示文本必须等长且不超过数码屏显示位数;
2) 显示文本不等长时,可以空格补齐;
3) 交替显示时间间隔包括2个时间,即显示时间与更换熄灭时间,如不
希望有熄灭时间,则直接设置为0即可。否则应该设置为扫描基本时间的倍数;
3 SCROL(滚动)显示:在指定区域以向左(或等效于左的方式)单个字符按照 设定的使用移动显示。以下
参数在设置时有一定约束或要求
1) 显示文本长度必须大于显示区域长度(否则不需要滚动显示);
2) 可以指定开始位置在显示区域的任何位置,或开始时显示区域全熄;
3) 可以指定结束位置在显示区域的任何位置,或结束时显示区域全熄;
4) 最好不必设置开始显示与结束显示同时全熄(似乎没有意义);
关于滚动开始与结束相关设置,请参考tDDispScroll中prefixSpace和suffixSpace描述
-------------------------------------------------------------------------------------------*/
//-----------------------------------------------------------------------------------------
//以下为控制数码屏显示时的设置参数,包括:
//tDDispCtrlParam(tDDispNormalParam/tDDispFlashParam/tDDispAlternateParam/tDDispScrollParam)
//正常显示参数
typedef struct
{
//pBuff:参考tDDispFlashParam中参数说明的1,A部分描述(其存储相似)
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pBuff; //显示内容缓冲区
}tDDispNormalParam;
//数码屏闪烁参数
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;
//数码屏交替显示参数
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;
//数码屏滚动显示参数
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;
//显示控制参数数据
typedef struct
{
tDDispMode mode; //显示模式
void* pParam; //显示参数
}tDDispCtrlParam;
//-----------------------------------------------------------------------------------------
//以下为数码屏除正常显示外的三种运行数据结构描述:
//闪烁显示运行数据
typedef struct
{
tDDispStatus status; //显示状态
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pDispBuff; //显示内容缓冲区(存储整个digitMax长度显示数据)
tDDispCnt repeat; //重复执行计数值
}tDDispFlash;
//交替显示运行数据
typedef struct
{
bool groupDectect; //显示内容换组检测
tDDispStatus status; //显示状态
tDDispPos pos; //显示内容在数码屏的起止digit位置
char *pDispBuff; //显示内容缓冲区(数组)
tDDispCnt group; //显示内容组数
tDDispCnt repeat; //重复执行计数值
}tDDispAlternate;
//滚动显示运行数据
/*-------------------------------------------------------------------------------------------
滚动开始与结束都存在两种效果
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;
//-----------------------------------------------------------------------------------------
//数码屏驱动函数原型定义
typedef void(*tDDispVoid)(void); //数码屏无参函数原型
typedef bool(*tDDispCtrlFunc)(tDDispCtrlParam*);//数码屏工作控制函数原型
typedef void(*tDDispOutputFunc)(char*); //数码屏输出控制函数原型
//数码屏驱动函数
typedef struct
{
tDDispOutputFunc output; //安装的数码屏输出控制函数
tDDispVoid init; //安装的数码屏底层初始化函数(可以设置为空)
tDDispVoid unInit; //安装的数码屏底层去初始化函数(可以设置为空)
tDDispVoid lock; //安装的数码屏互斥锁获取函数(可以设置为空)
tDDispVoid unLock; //安装的数码屏互斥锁归还函数(可以设置为空)
}tDDispDrvFunc;
//数码屏驱动安装参数
typedef struct
{
bool alwaysScan; //总是扫描标志(表示需要不断刷新以维持显示)
uint8_t scanUnit; //数码屏扫描时间[10/20/30/40/50]
uint8_t digitMax; //数码屏最大digit
tDDispDrvFunc drvFunc; //数码屏驱动函数
}tDDispInitParam;
//数码屏基本结构
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;
//驱动对显示内容有一个建议性的约束,即显示数据应该使用ASCII码,对于熄灭的数据则指定为空格.
//当然,也可以不用受到此约束限制,而仅仅修改DDISP_SPACE即可.
#define DDISP_SPACE ' ' //定义的空格字符
#define DDISP_EXECKEEP_CNT 0 //持续执行(计数值设置为0)
tDDispHandle* DDisp_Initial(tDDispInitParam *pInitParam);
bool DDisp_UnInitial(void);
#endif
2)DDispDriver.c文件代码(驱动.c文件)
/*------------------------------------------说明:------------------------------------------------
此驱动已经被编译并调试验证,具体验证条件将在Display.c详细说明,此处仅对一个设置进行说明:
1,在验证时USE_OS中定义使用了FreeRTOS,因此才有如下定义
#if USE_OS==OS_FREERTOS
#include "FreeRTOS.h"
#endif
2,DDISPDRIVER驱动主要具备以下功能:
1) 通过调用初始化后提供操作句柄,其主要涉及2个功能:
A,poll指针函数,其被应用层在定时扫描位置调用;
B, ctrl指针函数,其被应用层需要控制数码屏显示时调用;
说明: 关于ctrl的调用参数说明,请见DDispDriver.h中定义的相关说明部分.
2) 整个驱动主要实现数码屏的多种模式控制操作:
A, 正常显示操作;
B, 闪烁控制操作;
C, 交替显示控制操作;
D, 滚动显示控制操作;
关于B/C/D三种显示方式,请见DDispDriver.h中定义的相关说明部分.
--------------------------------------------------------------------------------------------------
注:
此版本驱动设计中,已经考虑到了内存的灵活使用,因此已经将除正常显示外的三种显示使用内存设计为指针方
式。在使用时申请,不再使用后释放。
另外,已经将数码屏底层设计部分的代码移除,并进一步降低了软件耦合度,甚至其应用不仅仅针对数码屏了,
而是一切适用于此显示方式的均可使用。
--------------------------------------------------------------------------------------------------*/
#define OS_NONE 0
#define OS_FREERTOS 1
#define USE_OS OS_FREERTOS
#include "serial.h"
#if USE_OS==OS_FREERTOS
#include "FreeRTOS.h"
#endif
#include "DDispDriver.h"
//
#define DDISPSCAN_UINT_DEF 10 //数码屏扫描默认时间(ms)
//以下定义内存申请与释放
#define MALLOC_MEM(memSize) pvPortMalloc(memSize) //内存申请宏
#define FREE_MEM(pMem) vPortFree(pMem) //内存释放宏
tDDispDriver *pDDispDrv=NULL; //仅用于驱动内部的驱动指针
//===========================================================================
//数码屏驱动操作互斥锁模块
/*--------------------------------------------------------------------------------------------------
* 摘要: 执行应用层提供的获取互斥锁操作或驱动内部的获取软件互斥锁
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void DDisp_Lock(void)
{
if (pDDispDrv==NULL)
return;
if (pDDispDrv->drvFunc.lock!=NULL)
pDDispDrv->drvFunc.lock();
else
{
while (pDDispDrv->swLock==DDISP_LOCKED);
pDDispDrv->swLock=DDISP_LOCKED;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 执行应用层提供的归还互斥锁操作或驱动内部的归还软件互斥锁
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void DDisp_UnLock(void)
{
if (pDDispDrv==NULL)
return;
if (pDDispDrv->drvFunc.unLock!=NULL)
pDDispDrv->drvFunc.unLock();
else
pDDispDrv->swLock=DDISP_UNLOCKED;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 释放驱动中申请的运行数据内存(根据显示模式释放相应内存)
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void DDisp_FreeFuncResources(void)
{
if (pDDispDrv->disp.mode==DDISP_MODE_FLASH)
{
FREE_MEM(pDDispDrv->disp.pFlash);
pDDispDrv->disp.pFlash=NULL;
}
else if (pDDispDrv->disp.mode==DDISP_MODE_ALTERNATING)
{
FREE_MEM(pDDispDrv->disp.pAlternate);
pDDispDrv->disp.pAlternate=NULL;
}
else if (pDDispDrv->disp.mode==DDISP_MODE_SCROLL)
{
FREE_MEM(pDDispDrv->disp.pScroll);
pDDispDrv->disp.pScroll=NULL;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏闪烁点亮计时检测,并在时间到达后切换至闪烁熄灭状态
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_FlashOnCheck(void)
{
bool output=FALSE;
pDDispDrv->disp.count++;
if (pDDispDrv->disp.count>=pDDispDrv->disp.onCntMax)
{
//计时达到设定值,切换至闪烁灭状态
uint8_t loop;
for (loop=pDDispDrv->disp.pFlash->pos.start;loop<=pDDispDrv->disp.pFlash->pos.end;loop++)
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
pDDispDrv->disp.count=0;
pDDispDrv->disp.pFlash->status=DDISP_STATUS_OFF;
if (pDDispDrv->disp.pFlash->repeat.max!=DDISP_EXECKEEP_CNT)
pDDispDrv->disp.cntDetect=TRUE;
output=TRUE;
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏闪烁计数值计数并检测是否执行完毕,并在执行完毕时设置数码屏到正常显示状态(使用正常显存
* 输出显示内容)
* 参数: 无
* 返回: TRUE 维持闪烁显示
* FALSE 结束闪烁并恢复正常显示
* 说明: 此函数是由DDisp_FlashOffCheck函数分离出来的模块
---------------------------------------------------------------------------------------------------*/
static bool DDisp_FlashCountCheck(void)
{
pDDispDrv->disp.cntDetect=FALSE; //闪烁次数计数,并检测闪烁次数
pDDispDrv->disp.pFlash->repeat.exec++;
if (pDDispDrv->disp.pFlash->repeat.exec>=pDDispDrv->disp.pFlash->repeat.max)
{
//闪烁次数达到设定值,结束闪烁
DDisp_FreeFuncResources();
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
return FALSE;
}
else//闪烁没有结束,继续执行(下一次执行灯亮状态)
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏闪烁熄灭计时检测,并在时间到达后执行数码屏闪烁计数值计数及检测,并在继续执行时切换至闪
* 烁点亮状态。
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_FlashOffCheck(void)
{
bool output=FALSE;
pDDispDrv->disp.count++;
if (pDDispDrv->disp.count>=pDDispDrv->disp.offCntMax)
{
//计时达到设定值
pDDispDrv->disp.count=0;
if (pDDispDrv->disp.cntDetect==TRUE)
output=DDisp_FlashCountCheck();
else//持续闪烁,则继续执行(下一次执行灯亮状态)
output=TRUE;
if (output==TRUE)
{
uint8_t loop;
for (loop=pDDispDrv->disp.pFlash->pos.start;loop<=pDDispDrv->disp.pFlash->pos.end;loop++)
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pFlash->pDispBuff[loop];
pDDispDrv->disp.pFlash->status=DDISP_STATUS_ON;
output=TRUE;
}
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏交替显示时切换至下一组文本显示
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_AlternateNextGroup(void)
{
bool output=FALSE;
if (pDDispDrv->disp.pAlternate->groupDectect==TRUE)
{
pDDispDrv->disp.pAlternate->group.exec++;
if (pDDispDrv->disp.pAlternate->group.exec>=pDDispDrv->disp.pAlternate->group.max)
{
//重复下一次
pDDispDrv->disp.pAlternate->group.exec=0;
if (pDDispDrv->disp.cntDetect==TRUE)
{
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.pAlternate->repeat.exec++;
if (pDDispDrv->disp.pAlternate->repeat.exec>=pDDispDrv->disp.pAlternate->repeat.max)
{
//结束交替显示
DDisp_FreeFuncResources();
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
output=TRUE;
}
}
}
}
if (pDDispDrv->disp.mode==DDISP_MODE_ALTERNATING)
{
uint8_t loop,groupOffset;
//取一组显示数据
groupOffset=pDDispDrv->disp.pAlternate->group.exec*pDDispDrv->digitMax;
for (loop=0;loop<pDDispDrv->digitMax;loop++)
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pAlternate->pDispBuff[groupOffset+loop];
pDDispDrv->disp.pAlternate->status=DDISP_STATUS_ON;
output=TRUE;
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏交替显示点亮计时检测,并在时间到达后切换至熄灭状态或切换至下一组文本显示。
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_AlternateOnCheck(void)
{
bool output=FALSE;
pDDispDrv->disp.count++;
if (pDDispDrv->disp.count>=pDDispDrv->disp.onCntMax)
{
uint8_t loop;
for (loop=pDDispDrv->disp.pAlternate->pos.start;loop<=pDDispDrv->disp.pAlternate->pos.end;loop++)
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
pDDispDrv->disp.count=0;
pDDispDrv->disp.pAlternate->groupDectect=TRUE;
if (pDDispDrv->disp.offCntMax==0)
output=DDisp_AlternateNextGroup();
else
{
pDDispDrv->disp.pAlternate->status=DDISP_STATUS_OFF;
if (pDDispDrv->disp.pAlternate->repeat.max!=DDISP_EXECKEEP_CNT)
pDDispDrv->disp.cntDetect=TRUE;
output=TRUE;
}
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏交替显示熄灭计时检测,并在时间到达后切换至下一组文本
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_AlternateOffCheck(void)
{
bool output=FALSE;
pDDispDrv->disp.count++;
if (pDDispDrv->disp.count>=pDDispDrv->disp.offCntMax)
{
//准备换一组内容显示
pDDispDrv->disp.count=0;
output=DDisp_AlternateNextGroup();
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏滚动显示处理(未分模块),主要完成显示计数、移动数据、重复显示计数等处理。
* 参数: 无
* 返回: TRUE 刷新数码屏输出显示;
* FALSE 维持数码屏当前显示;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_Scroll(void)
{
bool output=FALSE;
uint8_t scrollAreaLen=pDDispDrv->disp.pScroll->pos.end-pDDispDrv->disp.pScroll->pos.start+1;
pDDispDrv->disp.count++;
if (pDDispDrv->disp.count>=pDDispDrv->disp.onCntMax)
{
pDDispDrv->disp.count=0;
//从下一个位置开始读取字符显示
if (pDDispDrv->disp.pScroll->offset.exec==pDDispDrv->disp.pScroll->offset.max)
pDDispDrv->disp.pScroll->offset.exec=0;
else
{
if (pDDispDrv->disp.pScroll->prefixSpace.exec==0)
pDDispDrv->disp.pScroll->offset.exec++;
}
//
if (pDDispDrv->disp.pScroll->prefixSpace.exec>0)
pDDispDrv->disp.pScroll->prefixSpace.exec--;//前导空格逐步减一操作
if ((pDDispDrv->disp.pScroll->offset.max-pDDispDrv->disp.pScroll->offset.exec)<scrollAreaLen)
{
//最后一个数据已经显示在数码屏滚动区域的尾部,添加后缀空格(如添加后缀空格数大于0)
if (pDDispDrv->disp.pScroll->suffixSpace.max>0&&(pDDispDrv->disp.pScroll->suffixSpace.exec<=pDDispDrv->disp.pScroll->suffixSpace.max))
pDDispDrv->disp.pScroll->suffixSpace.exec++;//后缀空格逐步加一操作
}
/*------------------------------------------------------------------------------
以下满足两个条件之一则结束本次滚动
1, 设置了后缀空格,且后缀空格添加完毕;
2, 没有设置后缀空格,且最后一个字符已经显示;
------------------------------------------------------------------------------*/
if ((pDDispDrv->disp.pScroll->suffixSpace.max>0&&pDDispDrv->disp.pScroll->suffixSpace.max<pDDispDrv->disp.pScroll->suffixSpace.exec)||
(pDDispDrv->disp.pScroll->suffixSpace.max==0&&(pDDispDrv->disp.pScroll->offset.max-pDDispDrv->disp.pScroll->offset.exec)<scrollAreaLen))
{
pDDispDrv->disp.pScroll->prefixSpace.exec=pDDispDrv->disp.pScroll->prefixSpace.max;
pDDispDrv->disp.pScroll->suffixSpace.exec=0;
if (pDDispDrv->disp.pScroll->prefixSpace.exec==scrollAreaLen)
pDDispDrv->disp.pScroll->offset.exec=pDDispDrv->disp.pScroll->offset.max;
else
pDDispDrv->disp.pScroll->offset.exec=0;
//重复下一次
if (pDDispDrv->disp.cntDetect==TRUE)
{
pDDispDrv->disp.pScroll->repeat.exec++;
if (pDDispDrv->disp.pScroll->repeat.exec>=pDDispDrv->disp.pScroll->repeat.max)
{
//结束滚动显示
DDisp_FreeFuncResources();
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
output=TRUE;
}
}
}
if (pDDispDrv->disp.mode==DDISP_MODE_SCROLL)
{
uint8_t loop,offset,prefixSpace;
offset=pDDispDrv->disp.pScroll->offset.exec;
prefixSpace=pDDispDrv->disp.pScroll->prefixSpace.exec;
for (loop=pDDispDrv->disp.pScroll->pos.start;loop<=pDDispDrv->disp.pScroll->pos.end;loop++)
{
//首先添加前导空格
if (prefixSpace>0)
{
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
prefixSpace--;
}
//其次添加显示字符
else if (offset<pDDispDrv->disp.pScroll->offset.max)
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pScroll->pDispBuff[offset++];
//最后添加后缀空格(注:前导空格与后缀空格不会同时存在)
else
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
}
output=TRUE;
}
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏POLLING处理。主要执行数码屏除正常显示外的三种模块控制操作。当不需要扫描维持,且数码
* 屏处于正常显示时将不往下执行
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void DDisp_Poll(void)
{
bool output=FALSE;
//驱动安装失败,拒绝执行
if (pDDispDrv==NULL)
return;
if (pDDispDrv->needChange==FALSE)
return;
if (pDDispDrv->alwaysScan==FALSE)
pDDispDrv->needChange=FALSE;
else
output=TRUE;
DDisp_Lock();
//根据显示方式分开处理
switch (pDDispDrv->disp.mode)
{
case DDISP_MODE_NORMAL:
output=TRUE;
break;
case DDISP_MODE_FLASH:
if (pDDispDrv->disp.pFlash->status==DDISP_STATUS_ON)
output=DDisp_FlashOnCheck(); //闪烁点亮计时,并检测切换至闪烁灭状态
else
output=DDisp_FlashOffCheck(); //闪烁熄灭计时,并检测切换至闪烁亮状态,同时,检测闪烁次数
break;
case DDISP_MODE_ALTERNATING:
if (pDDispDrv->disp.pAlternate->status==DDISP_STATUS_ON)
output=DDisp_AlternateOnCheck();
else
output=DDisp_AlternateOffCheck();
break;
case DDISP_MODE_SCROLL:
output=DDisp_Scroll();
break;
}
if (pDDispDrv->disp.mode>=DDISP_MODE_FLASH)
pDDispDrv->needChange=TRUE;
if (pDDispDrv->disp.trig==DDISP_TRIG_ON||output==TRUE)
{
pDDispDrv->disp.trig=DDISP_TRIG_OFF;
if (pDDispDrv->disp.mode==DDISP_MODE_NORMAL)
pDDispDrv->drvFunc.output(pDDispDrv->disp.pDispBuff);
else
pDDispDrv->drvFunc.output(pDDispDrv->disp.pFuncDispBuff);
}
DDisp_UnLock();
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏控制操作中执行正常显示设置
* 参数: tDDispNormalParam *pParam正常显示控制参数数据指针
* 返回: TRUE 操作成功;
* FALSE操作失败;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_CtrlNormal(tDDispNormalParam *pParam)
{
uint8_t offset,loop;
if (pParam->pBuff==NULL||
pParam->pos.end<pParam->pos.start||
pParam->pos.start>=pDDispDrv->digitMax||
pParam->pos.end>=pDDispDrv->digitMax||
(pParam->pos.end-pParam->pos.start+1)>pDDispDrv->digitMax)
return FALSE;
DDisp_FreeFuncResources();
for (loop=0,offset=pParam->pos.start;offset<=pParam->pos.end;offset++,loop++)
pDDispDrv->disp.pDispBuff[offset]=pParam->pBuff[loop];
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏控制操作中执行闪烁显示设置
* 参数: tDDispFlashParam *pParam闪烁显示控制参数数据指针
* 返回: TRUE 操作成功;
* FALSE操作失败;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_CtrlFlash(tDDispFlashParam *pParam)
{
uint16_t totalCnt;
uint8_t offset,loop;
if (pParam->pos.end<pParam->pos.start||pParam->pos.start>=pDDispDrv->digitMax||pParam->pos.end>=pDDispDrv->digitMax||
(pParam->pos.end-pParam->pos.start+1)>pDDispDrv->digitMax)
return FALSE;
if (pParam->onlyChange==TRUE)
{
//仅仅更新闪烁内容
if (pParam->pBuff==NULL||pDDispDrv->disp.mode!=DDISP_MODE_FLASH)
return FALSE;
offset=0;
for (loop=pParam->pos.start;loop<=pParam->pos.end;loop++)
{
pDDispDrv->disp.pFlash->pDispBuff[loop]=pParam->pBuff[offset];
pDDispDrv->disp.pFuncDispBuff[loop]=pParam->pBuff[offset++];
}
//立即更新内容
pDDispDrv->disp.pFlash->pos=pParam->pos;
pDDispDrv->disp.pFlash->status=DDISP_STATUS_ON;
pDDispDrv->disp.count=0;
return TRUE;
}
totalCnt=pParam->freq*DDISP_BASETIME/pDDispDrv->scanUnit; //这里totalCnt的计算结果不能≤1,否则无法执行闪烁
if (totalCnt<=1)
return FALSE;
if (pParam->dutyRatio<DDISP_DUTY_MIN||pParam->dutyRatio>DDISP_DUTY_MAX||(pParam->dutyRatio%10)!=0)
return FALSE;
DDisp_FreeFuncResources();
//申请FLASH资源及显存
pDDispDrv->disp.pFlash=(tDDispFlash*)MALLOC_MEM(sizeof(tDDispFlash)+pDDispDrv->digitMax*2);
pDDispDrv->disp.pFuncDispBuff=(char*)pDDispDrv->disp.pFlash+sizeof(tDDispFlash);
pDDispDrv->disp.pFlash->pDispBuff=pDDispDrv->disp.pFuncDispBuff+pDDispDrv->digitMax;
//设置FLASH相关参数
pDDispDrv->disp.pFlash->pos=pParam->pos;
pDDispDrv->disp.pFlash->repeat.exec=0;
pDDispDrv->disp.pFlash->repeat.max=pParam->counter;
if (pParam->pBuff==NULL)
{
//复制当前显示内容
for (loop=0;loop<pDDispDrv->digitMax;loop++)
pDDispDrv->disp.pFlash->pDispBuff[loop]=pDDispDrv->disp.pDispBuff[loop];
}
else
{
//复制设置内容
if (pParam->onlyFlashData==TRUE)
{
//仅复制闪烁内容(其余数据从当前显示获取)
offset=0;
for (loop=0;loop<pDDispDrv->digitMax;loop++)
{
if (loop>=pParam->pos.start&&loop<=pParam->pos.end)
pDDispDrv->disp.pFlash->pDispBuff[loop]=pParam->pBuff[offset++];
else
pDDispDrv->disp.pFlash->pDispBuff[loop]=pDDispDrv->disp.pDispBuff[loop];
}
}
else
{
//完全复制设置内容
for (loop=0;loop<pDDispDrv->digitMax;loop++)
pDDispDrv->disp.pFlash->pDispBuff[loop]=pParam->pBuff[loop];
}
}
//设计闪烁总是从熄灭开始
pDDispDrv->disp.pFlash->status=DDISP_STATUS_OFF;
//设置功能显存
for (loop=0;loop<pDDispDrv->digitMax;loop++)
{
if (loop>=pParam->pos.start&&loop<=pParam->pos.end)
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
else
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pFlash->pDispBuff[loop];
}
//设置显示参数
//根据占空比计算闪烁点亮的计数器值
pDDispDrv->disp.onCntMax=totalCnt*pParam->dutyRatio/100;
if (pDDispDrv->disp.onCntMax==0)
pDDispDrv->disp.onCntMax=1; //计算闪烁熄灭的计数器值
pDDispDrv->disp.offCntMax=totalCnt-pDDispDrv->disp.onCntMax;
pDDispDrv->disp.count=0;
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.mode=DDISP_MODE_FLASH;
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏控制操作中执行交替显示设置
* 参数: tDDispAlternateParam *pParam交替显示控制参数数据指针
* 返回: TRUE 操作成功;
* FALSE操作失败;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_CtrlAlterate(tDDispAlternateParam *pParam)
{
uint8_t group,groupOffset,loop,offset;
if (pParam->pos.end<pParam->pos.start||pParam->pos.start>=pDDispDrv->digitMax||pParam->pos.end>=pDDispDrv->digitMax||
(pParam->pos.end-pParam->pos.start+1)>pDDispDrv->digitMax||
pParam->pBuff==NULL||pParam->group==0||pParam->onTimems<pDDispDrv->scanUnit||
(pParam->offTimems>0&&pParam->offTimems<pDDispDrv->scanUnit))
return FALSE;
DDisp_FreeFuncResources();
//申请ALTERNATE资源及显存
pDDispDrv->disp.pAlternate=(tDDispAlternate*)MALLOC_MEM(sizeof(tDDispAlternate)+pDDispDrv->digitMax*(pParam->group+1));
pDDispDrv->disp.pFuncDispBuff=(char*)pDDispDrv->disp.pAlternate+sizeof(tDDispAlternate);
pDDispDrv->disp.pAlternate->pDispBuff=pDDispDrv->disp.pFuncDispBuff+pDDispDrv->digitMax;
//设置ALTERNATE相关参数
pDDispDrv->disp.pAlternate->groupDectect=FALSE;
pDDispDrv->disp.pAlternate->pos=pParam->pos;
pDDispDrv->disp.pAlternate->group.exec=0;
pDDispDrv->disp.pAlternate->group.max=pParam->group;
pDDispDrv->disp.pAlternate->repeat.exec=0;
pDDispDrv->disp.pAlternate->repeat.max=pParam->counter;
//设计闪烁总是从熄灭开始
pDDispDrv->disp.pAlternate->status=DDISP_STATUS_OFF;
//复制交替显示内容
offset=0;
for (group=0;group<pParam->group;group++)
{
groupOffset=group*pDDispDrv->digitMax;
for (loop=0;loop<pDDispDrv->digitMax;loop++)
{
if (loop>=pParam->pos.start&&loop<=pParam->pos.end)
pDDispDrv->disp.pAlternate->pDispBuff[groupOffset+loop]=pParam->pBuff[offset++];
else
pDDispDrv->disp.pAlternate->pDispBuff[groupOffset+loop]=pDDispDrv->disp.pDispBuff[loop];
}
}
//设置功能显存
for (loop=0;loop<pDDispDrv->digitMax;loop++)
{
if (loop>=pParam->pos.start&&loop<=pParam->pos.end)
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
else
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pDispBuff[loop];
}
//设置显示参数
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.count=0;
pDDispDrv->disp.onCntMax=pParam->onTimems/pDDispDrv->scanUnit;
pDDispDrv->disp.offCntMax=pParam->offTimems/pDDispDrv->scanUnit;
pDDispDrv->disp.mode=DDISP_MODE_ALTERNATING;
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 数码屏控制操作中执行滚动显示设置
* 参数: tDDispScrollParam *pParam滚动显示控制参数数据指针
* 返回: TRUE 操作成功;
* FALSE操作失败;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_CtrlScroll(tDDispScrollParam *pParam)
{
uint8_t loop,scrollAreaLen,prefixSpace,offset;
scrollAreaLen=pParam->pos.end-pParam->pos.start+1;
if (pParam->pos.end<pParam->pos.start||pParam->pos.start>=pDDispDrv->digitMax||pParam->pos.end>=pDDispDrv->digitMax||
scrollAreaLen>pDDispDrv->digitMax||pParam->charTotal<scrollAreaLen||
pParam->prefixSpace>scrollAreaLen|| pParam->suffixSpace>scrollAreaLen||
pParam->pBuff==NULL||pParam->timems<pDDispDrv->scanUnit)
return FALSE;
DDisp_FreeFuncResources();
//申请SCROLL资源及显存
pDDispDrv->disp.pScroll=(tDDispScroll*)MALLOC_MEM(sizeof(tDDispScroll)+pDDispDrv->digitMax+pParam->charTotal);
pDDispDrv->disp.pFuncDispBuff=(char*)pDDispDrv->disp.pScroll+sizeof(tDDispScroll);
pDDispDrv->disp.pScroll->pDispBuff=pDDispDrv->disp.pFuncDispBuff+pDDispDrv->digitMax;
//设置SCROLL相关参数
pDDispDrv->disp.pScroll->pos=pParam->pos;
if (pParam->prefixSpace==scrollAreaLen)
pDDispDrv->disp.pScroll->offset.exec=pParam->charTotal;
else
pDDispDrv->disp.pScroll->offset.exec=0;
pDDispDrv->disp.pScroll->offset.max=pParam->charTotal;
pDDispDrv->disp.pScroll->prefixSpace.exec=pParam->prefixSpace;
pDDispDrv->disp.pScroll->prefixSpace.max=pParam->prefixSpace;
pDDispDrv->disp.pScroll->suffixSpace.exec=0;
pDDispDrv->disp.pScroll->suffixSpace.max=pParam->suffixSpace;
pDDispDrv->disp.pScroll->repeat.exec=0;
pDDispDrv->disp.pScroll->repeat.max=pParam->counter;
//复制滚动数据
for (loop=0;loop<pParam->charTotal;loop++)
pDDispDrv->disp.pScroll->pDispBuff[loop]=pParam->pBuff[loop];
//设置功能显存
prefixSpace=pParam->prefixSpace;
offset=0;
for (loop=0;loop<pDDispDrv->digitMax;loop++)
{
if (loop>=pParam->pos.start&&loop<=pParam->pos.end)
{
if (prefixSpace>0)
{
pDDispDrv->disp.pFuncDispBuff[loop]=DDISP_SPACE;
prefixSpace--;
}
else
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pScroll->pDispBuff[offset++];
}
else
pDDispDrv->disp.pFuncDispBuff[loop]=pDDispDrv->disp.pDispBuff[loop];
}
//设置显示参数
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.count=0;
pDDispDrv->disp.onCntMax=pParam->timems/pDDispDrv->scanUnit;
pDDispDrv->disp.mode=DDISP_MODE_SCROLL;
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 被安装在pDDispDrv->handle.ctrl的数码屏工作状态设置函数,包括以下四种显示方式控制:
* 正常显示/闪烁显示/交替显示/滚动显示
* 参数: tDDispCtrlParam *pCtrl控制参数
* pCtrl->mode 显示方式(四种);
* pCtrl->pParam 显示控制参数(void*),其根据pCtrl->mode不同而指向不同的结构
* 1) 当pCtrl->mode==DDISP_MODE_NORMAL时
* pCtrl->pParam指向tDDispNormalParam类型参数数据;
* 2) 当pCtrl->mode==DDISP_MODE_FLASH时
* pCtrl->pParam指向tDDispFlashParam类型参数数据;
* 3) 当pCtrl->mode==DDISP_MODE_ALTERNATING时
* pCtrl->pParam指向tDDispAlternateParam类型参数数据;
* 4) 当pCtrl->mode==DDISP_MODE_SCROLL时
* pCtrl->pParam指向tDDispScrollParam类型参数数据;
* 返回: TRUE 设置成功;
* FALSE设置失败;
---------------------------------------------------------------------------------------------------*/
static bool DDisp_CtrlFunc(tDDispCtrlParam *pCtrl)
{
bool result=FALSE;
DDisp_Lock();
if (pCtrl!=NULL&&pCtrl->pParam!=NULL)
{
switch (pCtrl->mode)
{
case DDISP_MODE_NORMAL:
result=DDisp_CtrlNormal((tDDispNormalParam*)pCtrl->pParam);
break;
case DDISP_MODE_FLASH:
result=DDisp_CtrlFlash((tDDispFlashParam*)pCtrl->pParam);
break;
case DDISP_MODE_ALTERNATING:
result=DDisp_CtrlAlterate((tDDispAlternateParam*)pCtrl->pParam);
break;
case DDISP_MODE_SCROLL:
result=DDisp_CtrlScroll((tDDispScrollParam*)pCtrl->pParam);
break;
}
}
if (result==TRUE)
{
pDDispDrv->disp.trig=DDISP_TRIG_ON;
pDDispDrv->needChange=TRUE;
}
DDisp_UnLock();
return result;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 提供给外部调用的驱动安装函数。主要完成驱动内存申请、驱动控制输出初始化等。
* 参数: tDDispInitParam *pInitParam初始化参数
* alwaysScan 数码屏维持扫描输出(依赖于MCU I/O扫描时如此)
* scanUnit 数码屏扫描时间[10/20/30/40/50],如超出此范围将安装失败
* digitMax 数码屏digit位数
* drvFunc 数码屏驱动函数,包括以下部分
* output安装的数码屏输出控制函数
* init 安装的数码屏底层初始化函数(可以设置为空)
* unInit 安装的数码屏底层去初始化函数(可以设置为空)
* lock 安装的数码屏互斥锁获取函数(可以设置为空)
* unLock安装的数码屏互斥锁归还函数(可以设置为空)
* 返回: tDDispHandle 数码屏驱动操作句柄;
* 说明: 这是唯一一个直接提供给外部必须调用的函数。
* 另外,如果提供了lock函数,且操作的是操作系统的互斥量,最好将其资源申请放在init中(并提供此函数).
* 资源释放同时放在unInit中.可以达到较好的效果.
---------------------------------------------------------------------------------------------------*/
tDDispHandle* DDisp_Initial(tDDispInitParam *pInitParam)
{
uint8_t loop;
if (pInitParam==NULL||
pInitParam->drvFunc.output==NULL||
pInitParam->digitMax==0||
pInitParam->scanUnit<DDISP_SCAN_MIN||
pInitParam->scanUnit>DDISP_SCAN_MAX||
pDDispDrv!=NULL)
return NULL;
pDDispDrv=MALLOC_MEM(sizeof(tDDispDriver)+pInitParam->digitMax);
pDDispDrv->disp.pDispBuff=(char*)pDDispDrv+sizeof(tDDispDriver);
for (loop=0;loop<pInitParam->digitMax;loop++)
pDDispDrv->disp.pDispBuff[loop]=DDISP_SPACE;
//检查扫描时间
if (pInitParam->scanUnit==0)
pDDispDrv->scanUnit=DDISPSCAN_UINT_DEF;
else
pDDispDrv->scanUnit=pInitParam->scanUnit;
pDDispDrv->alwaysScan=pInitParam->alwaysScan;
pDDispDrv->digitMax=pInitParam->digitMax;
pDDispDrv->needChange=TRUE;
pDDispDrv->disp.cntDetect=FALSE;
pDDispDrv->disp.trig=DDISP_TRIG_OFF;
pDDispDrv->disp.mode=DDISP_MODE_NORMAL;
//安装驱动处理函数及设置参数
pDDispDrv->drvFunc=pInitParam->drvFunc;
//执行低级初始化操作
if (pDDispDrv->drvFunc.init!=NULL)
pDDispDrv->drvFunc.init();
pDDispDrv->handle.poll=DDisp_Poll;
pDDispDrv->handle.ctrl=DDisp_CtrlFunc;
return &pDDispDrv->handle;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 提供给外部调用的驱动卸载函数。主要完成驱动内存释放及底层低级去初始化操作(如提供此函数)。
* 参数: 无
* 返回: TRUE 执行卸载成功
* FALSE执行卸载失败
* 说明: 如果LLed_Lock包含了应用层提供的操作系统互斥量操作,则申请资源最好放在init函数,且释放资源放在
* unInit函数.
---------------------------------------------------------------------------------------------------*/
bool DDisp_UnInitial(void)
{
bool result=TRUE;
DDisp_Lock();
if (pDDispDrv!=NULL)
{
//执行低级去初始化操作
if (pDDispDrv->drvFunc.unInit!=NULL)
pDDispDrv->drvFunc.unInit();
DDisp_FreeFuncResources();
FREE_MEM(pDDispDrv);
pDDispDrv=NULL;
}
else
result=FALSE;
DDisp_UnLock();
return result;
}
//===========================================================================
/*--END--*/
3)Display.c文件代码(测试代码)
/*------------------------------------------说明:------------------------------------------------
此例程用于验证数码屏驱动,其使用环境如下:
1) MCU使用WZnet芯片W7500;
2) 实时操作系统FreeRTOS V9.0.0;
3) 编译系统使用IAR 8.22.1;
4) 硬件平台没有数码屏,但使用模拟方式输出(通过串口在指定位置输出);
5) 除包含"FreeRTOS.H"头文件外,还包括一个串口驱动"Serial.h"及<string.h>;
注:因验证比较简单,因此以下功能函数没有做太多详细说明
--------------------------------------------------------------------------------------------------*/
#include <string.h>
#include "Serial.h"
#include "DDispDriver.h"
//---------------------------------------------------------------------------------------------------
//应用层中的显示任务资源
typedef struct
{
osMutexId mutex;
tDDispHandle* handle;
}tDisplay;
#define DDISP_POLL_TIME 10 //ms ddisp_poll轮询函数执行周期
#define DDISP_DIGIT_MAX 10 //定义digitMax
//FreeRTOS按键任务栈
#define DDisp_TaskStackDepth (configMINIMAL_STACK_SIZE)
char dispMem[DDISP_DIGIT_MAX+1]; //应用层显存
static tDisplay display;
//互斥锁获取与归还模块
//互斥锁获取
static void Display_Lock(void)
{
osMutexWait(display.mutex,osWaitForever);
}
//互斥锁归还
static void Display_UnLock(void)
{
osMutexRelease(display.mutex);
}
//DISPLAY底层初始化(以串口输出模拟)
static void Display_Init(void)
{
Comx_PrintStr(DBG_COM,"digit display initialize function\r\n");
}
//DISPLAY底层去初始化(以串口输出模拟)
static void Display_UnInit(void)
{
Comx_PrintStr(DBG_COM,"digit display uninitialize function...\r\n");
}
/*
DISPLAY底层输出函数(在串口指定位置输出字符串)
说明:
此处考虑到使用I/O扫描维持的方式,因此采用的是刷新整个digit的输出方式(理论上存在闪烁及效率问题)。
因此,并未提供指定位置开始输出的接口。
如果你有这个需求,则需要修改DDisp_CtrlFunc函数中的每一个显示模块,将显示位置复制到pDDispDrv中
(需添加位置变量),并修改Display_Output的参数(添加一个位置变量)。
注意:如果显示区域外的数据需要更新,则应该手动更新.
*/
static void Display_Output(char* pBuff)
{
uint8_t loop;
for (loop=0;loop<DDISP_DIGIT_MAX;loop++)
dispMem[loop]=pBuff[loop];
dispMem[DDISP_DIGIT_MAX]='\0';
/*
注:以下有一个本人设计串口工具中使用的格式化输出
\bn\b:指示退格删除n个字符(串口工具在行首解析此格式)
实际效果则是串口工具总是在同一位置显示字符串
*/
Comx_PrintStr(DBG_COM,"\\b10\\b%s",dispMem);
}
//DISPLAY初始化设置
static void Display_Setup(void)
{
uint8_t loop;
tDDispInitParam initParam;
for (loop=0;loop<DDISP_DIGIT_MAX+1;loop++)
dispMem[loop]=DDISP_SPACE;
//创建操作系统互斥量资源
display.mutex=osMutexCreate(NULL);
initParam.drvFunc.init=Display_Init;
initParam.drvFunc.unInit=Display_UnInit;
initParam.drvFunc.lock=Display_Lock;
initParam.drvFunc.unLock=Display_UnLock;
initParam.drvFunc.output=Display_Output;
initParam.alwaysScan=FALSE;
initParam.scanUnit=DDISP_POLL_TIME;
initParam.digitMax=DDISP_DIGIT_MAX;
display.handle=DDisp_Initial(&initParam);
}
//===============================================================
//DISPLAY任务模块(FreeRTOS任务)
static void Display_Task(void const *pvParameters)
{
/*-----------------------------------------------------------------------------
这里仅仅是用于演示与测试,所以单独创建一个简单任务并且仅执行一个简单的延时操作,然
后就调用驱动提供的poll函数.
如果在实际的应用中使用,则不需要单独创建此任务,可通过软件定时器产生回调时发送消息等
通知,由主任务产生调用
-----------------------------------------------------------------------------*/
Display_Setup();
for (;;)
{
osDelay(DDISP_POLL_TIME);
display.handle->poll();
}
}
//===============================================================
//外部调用模块(创建FreeRTOS任务)
void Display_TaskCreate(void)
{
osThreadDef(disp,Display_Task,osPriorityNormal,0,DDisp_TaskStackDepth);
osThreadCreate(osThread(disp), NULL);
}
//===============================================================
//调测模块(用于调试模块中根据串口输入进行修改)
//添加按下按键操作
//添加DISPLAY正常显示操作
void Display_AttendNormal(uint8_t start,char* pBuff)
{
tDDispCtrlParam ctrlParam;
tDDispNormalParam normalParam;
ctrlParam.mode=DDISP_MODE_NORMAL;
normalParam.pos.start=start;
normalParam.pos.end=start+strlen(pBuff)-1;
normalParam.pBuff=pBuff;
ctrlParam.pParam=&normalParam;
display.handle->ctrl(&ctrlParam);
}
//添加DISPLAY闪烁操作(onlyChange=FALSE,onlyFlashData=FALSE,全部为新数据)
void Display_AttendFlash_AllNew(uint8_t start,uint8_t end,char* pBuff)
{
tDDispCtrlParam ctrlParam;
tDDispFlashParam flashParam;
ctrlParam.mode=DDISP_MODE_FLASH;
flashParam.onlyChange=FALSE;
flashParam.onlyFlashData=FALSE;
flashParam.freq=DDISP_FREQ_1Hz;
flashParam.dutyRatio=50;
flashParam.counter=DDISP_EXECKEEP_CNT;
flashParam.pos.start=start;
flashParam.pos.end=end;
flashParam.pBuff=pBuff;
ctrlParam.pParam=&flashParam;
display.handle->ctrl(&ctrlParam);
}
//添加DISPLAY闪烁操作(onlyChange=TRUE,仅仅更新闪烁数据)
void Display_AttendFlash_OnlyChange(uint8_t start,uint8_t end,char* pBuff)
{
tDDispCtrlParam ctrlParam;
tDDispFlashParam flashParam;
ctrlParam.mode=DDISP_MODE_FLASH;
flashParam.onlyChange=TRUE;
flashParam.pos.start=start;
flashParam.pos.end=end;
flashParam.pBuff=pBuff;
ctrlParam.pParam=&flashParam;
display.handle->ctrl(&ctrlParam);
}
//添加DISPLAY闪烁操作(onlyChange=FALSE,onlyFlashData=TRUE,使用原数据与复制的闪烁数据)
void Display_AttendFlash_OnlyFlashData(uint8_t start,uint8_t end,char* pBuff)
{
tDDispCtrlParam ctrlParam;
tDDispFlashParam flashParam;
ctrlParam.mode=DDISP_MODE_FLASH;
flashParam.onlyChange=FALSE;
flashParam.onlyFlashData=TRUE;
flashParam.freq=DDISP_FREQ_1Hz;
flashParam.dutyRatio=50;
flashParam.counter=DDISP_EXECKEEP_CNT;
flashParam.pos.start=start;
flashParam.pos.end=end;
flashParam.pBuff=pBuff;
ctrlParam.pParam=&flashParam;
display.handle->ctrl(&ctrlParam);
}
//添加DISPLAY交替显示操作
void Display_AttendAlternate(uint16_t offTime,uint8_t start,uint8_t end,uint8_t group,char* pBuff)
{
tDDispCtrlParam ctrlParam;
tDDispAlternateParam alternateParam;
//pBuff数据长度=group*(end-start+1)
ctrlParam.mode=DDISP_MODE_ALTERNATING;
alternateParam.counter=DDISP_EXECKEEP_CNT;
alternateParam.group=group;
alternateParam.onTimems=1000;
alternateParam.offTimems=offTime;
alternateParam.pos.start=start;
alternateParam.pos.end=end;
alternateParam.pBuff=pBuff;
ctrlParam.pParam=&alternateParam;
display.handle->ctrl(&ctrlParam);
}
//添加DISPLAY滚动显示操作
void Display_AttendScroll(uint8_t start,uint8_t end,char* pBuff,uint16_t len)
{
tDDispCtrlParam ctrlParam;
tDDispScrollParam scrollParam;
ctrlParam.mode=DDISP_MODE_SCROLL;
scrollParam.counter=DDISP_EXECKEEP_CNT;
scrollParam.charTotal=len;
scrollParam.prefixSpace=end-start+1;
scrollParam.suffixSpace=end-start;
scrollParam.pos.start=start;
scrollParam.pos.end=end;
scrollParam.timems=200;
scrollParam.pBuff=pBuff;
ctrlParam.pParam=&scrollParam;
display.handle->ctrl(&ctrlParam);
}
//===============================================================
/*--END--*/
五、后续内容
下个分享内容:单片机软件常用设计分享(四)驱动设计之串口驱动设计
说明
以上设计方法基本为本人在工作中所用,但为了分享,做了一定的整理并进行扩充(已经调测,有需要的请放心使用)。如有错误或BUG处,欢迎指正。
附
如果你感兴趣,可查看其它文档.
单片机软件常用设计分享(一)驱动设计之按键设计
单片机软件常用设计分享(二)驱动设计之LED灯显示设计
单片机软件常用设计分析(四)驱动设计之串口驱动设计
嵌入式开发<串口调试工具>
嵌入式开发<网络调试工具>
嵌入式开发<单片机软件调试>
嵌入式开发<单片机软件升级>