单片机软件常用设计分享(三)驱动设计之数码屏显示设计


前言

   本人从事单片机软件设计工作多年,既从事过裸机系统的设计,也在小型嵌入式实时操作系统下进行过设计。因在工作当中发现有些人对单片机软件的设计非常随意,尤其是在驱动方面,其考虑问题过于单一,并且应用层与底层驱动之间耦合度较大。基于此,本人整理工作当中做过常用的最基本设计分享到这里,希望对需要的人有所帮助或参考。当然,可能我的设计方法并不是很好,所以也算是一种学习交流。
  在整理的过程中,可能会缺乏统一规划,仅先整理一部分常用的驱动设计。至于其它部分的内容,待今后根据需要再逐一补充。

《驱动设计–数码屏显示驱动》

  数码显示,主要指的是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灯显示设计
单片机软件常用设计分析(四)驱动设计之串口驱动设计
嵌入式开发<串口调试工具>
嵌入式开发<网络调试工具>
嵌入式开发<单片机软件调试>
嵌入式开发<单片机软件升级>

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值