单片机软件常用设计分享(二)驱动设计之LED灯显示设计
- 前言
- 《驱动设计–LED灯显示驱动》
- 一、LED灯工作方式
- 1.常亮
- 2.常灭
- 3.闪烁
- 4.间歇性闪烁
- 二、LED灯驱动数据结构
- 灯状态
- 灯工作方式
- 灯闪烁
- 灯驱动数据结构
- 1)LED灯定义(已经取消LED_LIGHT_MAX定义)
- 2)LED灯驱动函数
- 3)LED灯控制参数结构
- 4)LED灯驱动结构
- 三、LED灯驱动代码设计
- 1.参数及变量定义
- 2.驱动处理
- 灯工作控制操作
- 灯驱动安装
- 灯驱动卸载
- 总结
- 1.必要的移植修改设计说明
- 2.外部调用接口
- 灯驱动使用示例
- 1)文件代码(驱动.h文件)
- 2)文件代码(驱动.c文件)
- 3)文件代码(测试代码)
- 五、后续内容
- 说明
- 附
前言
本人从事单片机软件设计工作多年,既从事过裸机系统的设计,也在小型嵌入式实时操作系统下进行过设计。因在工作当中发现有些人对单片机软件的设计非常随意,尤其是在驱动方面,其考虑问题过于单一,并且应用层与底层驱动之间耦合度较大。基于此,本人整理工作当中做过常用的最基本设计分享到这里,希望对需要的人有所帮助或参考。当然,可能我的设计方法并不是很好,所以也算是一种学习交流。
在整理的过程中,可能会缺乏统一规划,仅先整理一部分常用的驱动设计。至于其它部分的内容,待今后跟进需要再逐一补充。
《驱动设计–LED灯显示驱动》
似乎LED灯的显示没有什么,无非就是点亮与熄灭。确实,点亮或者熄灭一个LED灯很简单,就是操作一下I/O口就可以了。但是,在平时做项目时,经常会遇到要控制LED灯的闪烁,包括持续闪烁和闪烁一定的次数,甚至还包括需要设置闪烁的频率,亮灯的占空比等等。还有就是可能有多个LED灯在闪烁,如果闪烁频率相同时,每个LED灯闪烁时不同步,看起来就有些杂乱无章。
因此,还是有必要设计一个统一管理控制LED灯动作的一个驱动程序,使得其在使用时非常的方便。另外,这个倒是与linux操作系统的驱动有较大区别,可能有些人会认为这超出了通常LED灯驱动的设计。不过,我反而认为做这样的功能在驱动中,在一定小范围内显得通用性很好。
另外,有一个问题值得思考。用一个单色LED灯,用比较明显的方式,可以表示出多少种很容易区分的状态呢?这个问题我想在你看完整个完整的驱动后一定会有一个不一样的答案。当然,这也是在说明设计这个驱动的必要性。
说明:在调试完成数码屏驱动后,对此驱动做了一定修改,主要是取消了对于LED_LIGHT_MAX定义的依赖。
一、LED灯工作方式
一般包括3种工作方式,常亮、常灭、闪烁;但现在还需要追加一种,即间歇性闪烁。这种工作方式只是我自己对此的一种叫法,在这里首先对这种闪烁方式做一个简单说明。
比如有一种用LED闪烁的故障表示法: LED灯闪烁以3短1长的方式持续周期显示。即连续执行3次较快的闪烁,接着再执行1次较慢的闪烁。当然,这种显示方式有很多变化,我会在后面的说明中阐述。我把这种显示方式统一的称为:“间歇性闪烁”。
因此,一般LED灯包括4种工作方式,常亮、常灭、闪烁、间歇性闪烁。
1.常亮
LED灯一直处于点亮状态,这个比较简单,无需多作说明;
2.常灭
LED灯一直处于熄灭状态,这个也无需多作说明;
3.闪烁
LED灯处于两种交替状态,即点亮与熄灭状态,这个需要设置的参数就较多。包括:持续闪烁或闪烁一定次数,闪烁频率,点亮的占空比;
4.间歇性闪烁
LED灯处于两种较明显区别的频率交替显示,即快速闪烁与慢速闪烁,同时增加一个间歇。这样就需要设置的两组闪烁参数。同时,还需要设置这种显示方式的次数,以及间歇时间;
说明:以下在描述时只列出了相关部分代码,但会在最后给出完整代码。
二、LED灯驱动数据结构
LED灯驱动数据结构设计,包括LED灯状态、LED灯工作方式,LED灯闪烁参数,LED灯驱动结构,以下分别描述各个部分内容;
灯状态
LED灯状态仅两种,即点亮与熄灭,这种状态适合以枚举类型来定义
LED灯状态 (以下LLED为LED LIGHT缩写,并将LIGHT缩写L写在前面)
typedef enum
{
LLED_STATUS_ON, //点亮状态
LLED_STATUS_OFF //熄灭状态
}tLLedStatus;//LED灯状态
灯工作方式
LED灯工作方式前面已经讲过,就4种,也适合以枚举类型来定义
LED灯工作方式
typedef enum
{
LLED_MODE_BRIGHT, //常亮方式
LLED_MODE_DARKNESS, //熄灭方式
LLED_MODE_FLASH //闪烁方式
LLED_MODE_FLASH_INTERVAL //间歇性闪烁方式
}tLLedMode;//LED灯工作方式
灯闪烁
LED灯闪烁参数包括多个参数,分别是闪烁频率,闪烁占空比,闪烁次数,闪烁结束LED灯状态。
说明:LED灯的闪烁频率相邻之间其实观察起来并不明显,因此不必要每一个频率都有,而且有些频率按照设置参数不容易被整数划分为计数值。所以,以下仅定义了部分频率。
LED灯闪烁频率
typedef enum
{
LLED_FREQ_1_10Hz=100, //0.1Hz
LLED_FREQ_2_10Hz=50, //0.2Hz
LLED_FREQ_5_10Hz=20, //0.5Hz
LLED_FREQ_1Hz=10, //1Hz
LLED_FREQ_2Hz=5, //2Hz
LLED_FREQ_5Hz=2, //5Hz
LLED_FREQ_10Hz=1, //10Hz
}tLLedFreq;//LED灯闪烁频率
LED灯闪烁参数
注意:以下的占空比最后在计算为计数值时,是受闪烁频率与扫描时间限制的,并不一定会得到一个整数值。当然,扫描时间设置为最小值10ms,是考虑了性能问题的,其决定了实际可计算得到的占空比对应的计数值。
typedef struct
{
tLLedFreq freq; //闪烁频率
tLLedStatus endStatus; //闪烁结束LED灯状态
uint8_t dutyRatio; //取值范围[10/20/30/40/50/60/70/80/90]
uint8_t counter; //闪烁次数[0--255],0表示持续闪烁
}tLLedFlashParam;//LED灯闪烁参数
LED灯间歇闪烁参数
typedef struct
{
uint8_t count; //间歇闪烁执行次数
uint16_t darkTime; //熄灭时间,单位ms,但必须设置为扫描时间的整数值
tLLedFlashParam first; //闪烁first参数
tLLedFlashParam second; //闪烁second参数
}tLLedFlashIntervalParam;//LED灯间歇闪烁参数
LED灯闪烁计数器
typedef struct
{
uint8_t max; //最大计数值
uint8_t exec; //执行计数值
}tLLedCnt;//LED灯闪烁计数器
LED灯闪烁运行
以下的onCntMax与offCntMax主要由闪烁频率、占空比、扫描时间参数计算得出。
typedef struct
{
bool cntDetect; //计数检测标志(取值TRUE才可以检测计数值)
uint8_t intervalLevel; //记录间歇性运行的阶段
uint16_t count; //计数器
uint16_t onCntMax; //点亮计数器最大值
uint16_t offCntMax; //熄灭计数器最大值
tLLedCnt flashCnt; //闪烁计数器
tLLedCnt intervalCnt; //间歇闪烁计数器
tLLedStatus ndStatus; //闪烁结束LED灯状态
}tLLedFlashRun;//LED灯闪烁运行
灯驱动数据结构
1)LED灯定义(已经取消LED_LIGHT_MAX定义)
LED灯应该由具体的应用程序根据实际需要进行定义,除所有LED灯外,应该还需要定义LED_LIGHT_MAX项,因驱动中需要使用此参数。
LED灯定义(示例)
#define LED_LIGHT_RUN 0 //运行指示灯
#define LED_LIGHT_FAULT 1 //故障指示灯
#define LED_LIGHT_ALARM 2 //告警指示灯
#define LED_LIGHT_COMM 3 //通信指示灯
#define LED_LIGHT_MAX LED_LIGHT_COMM+1 //LED灯个数
2)LED灯驱动函数
以下驱动函数由初始化时外部提供,用以安装
LED灯驱动函数原型定义
typedef void(*tLLedVoid)(void); //LED无参函数原型
typedef bool(*tLLedCtrlFunc)(uint8_t,void*); //LED灯工作控制函数原型
typedef void(*tLLedOutputFunc)(tLLedStatus); //LED灯输出控制函数原型
LED灯应用层提供的驱动函数
//LED灯应用层提供的驱动函数
typedef struct
{
tLLedOutputFunc *output; //安装的LED灯输出控制函数
tLLedVoid init; //安装的LED灯底层初始化函数(可以设置为空)
tLLedVoid unInit; //安装的LED灯底层去初始化函数(可以设置为空)
tLLedVoid lock; //安装的LED灯互斥锁获取函数(可以设置为空)
tLLedVoid unLock; //安装的LED灯互斥锁归还函数(可以设置为空)
}tLLedDrvFunc;//LED灯驱动函数
3)LED灯控制参数结构
LED灯控制参数(调用LED灯工作控制时的入口参数),其中runParam的具体应用与含义在驱动文件中有详细说明。
typedef struct
{
tLLedMode mode; //LED灯工作模式
void* runParam; //LED工作运行参数
}tLLedCtrlParam;//LED灯控制参数
4)LED灯驱动结构
typedef struct
{
tLLedMode mode; //LED灯工作模式
tLLedTrig trig; //LED点亮及熄灭触发标志
tLLedStatus status; //LED灯状态
tLLedFlashRun run; //LED灯闪烁运行
tLLedFlashIntervalParam flashIntervalParam; //LED灯间歇性闪烁保存参数
}tLLed;//LED灯结构
typedef struct
{
tLLedVoid poll; //poll函数
tLLedSetupFunc ctrl; //LED灯控制功能函数
}tLLedHandle;//LED 灯驱动提供给应用层的操作句柄
typedef struct
{
uint8_t scanUnit; //LED扫描时间单位,ms
bool needChange; //LED灯需要改变状态时,将被设置为TRUE)
tLLed_Lock swLock; //LED软件互斥锁
tLLed *led; //LED灯组
tLLedDrvFunc drvFunc; //外部安装的驱动函数
tLLedHandle handle; //外部调用的功能函数(此类型说明在后面安装驱动中说明)
}tLLedDriver;//LED灯驱动数据结构
三、LED灯驱动代码设计
1.参数及变量定义
LLEDSCAN_UINT_DEF参数用于在应用层未提供扫描时间时使用。
//LLED_INTERVAL_MFLASH_DLY设置用于增加FIRST FLASH 与SECOND FLASH直接较明显的间隔
#define LLED_INTERVAL_MFLASH_DLY 1
//
#define LLEDSCAN_UINT_DEF 10 //LED灯扫描默认时间单位(ms)
//以下定义内存申请与释放
#define MALLOC_MEM(memSize) pvPortMalloc(memSize) //内存申请宏
#define FREE_MEM(pMem) vPortFree(pMem) //内存释放宏
tLLedDriver *pLLedDrv=NULL; //仅用于驱动内部的驱动指针
2.驱动处理
LED灯驱动轮询输出函数,这里仅说明其控制逻辑,涉及到的详细子模块在驱动文件中可查看。
从以下代码中其实可以看出,主要处理闪烁与间歇性闪烁。同时,当所有LED灯不需要再改变状态时,POLL将不会往下执行,这其中主要依靠pLLedDrv->needChange变量控制。
另外,如果有LED灯仅仅需要执行常亮或常灭,最好也通过ctrl函数的调用由poll来处理。或者你压根不用放在此驱动中。
void LLed_Poll(void)
{
uint8_t light;
bool output;
LLed_Lock();
//没有安装驱动,拒绝执行或 没有LED灯状态需要改变,则直接退出
if (pLLedDrv==NULL||pLLedDrv->needChange==FALSE)
{
LLed_UnLock();
return;
}
pLLedDrv->needChange=FALSE;
//循环处理所有LED灯
for (light=0;light<pLLedDrv->lightMax;light++)
{
//输出控制,并不是每次都需要输出,仅在状态发生改变时才执行(或强制输出)
output=FALSE;
if (pLLedDrv->led[light].mode==LLED_MODE_FLASH)
{
//闪烁操作处理
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
output=LLed_FlashOnCheck(light);//闪烁点亮计时,并检测切换至闪烁灭状态
else
output=LLed_FlashOffCheck(light);//闪烁熄灭计时,并检测切换至闪烁亮状态,同时,检测闪烁次数
}
else if (pLLedDrv->led[light].mode==LLED_MODE_FLASH_INTERVAL)
{
//间歇性闪烁操作处理
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_DARK)
{
pLLedDrv->led[light].run.count++;
if (pLLedDrv->led[light].run.count>=pLLedDrv->led[light].flashIntervalParam.darkTime)
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FINISH;
}
else
{
//间歇性闪烁的第一步及第二步均执行闪烁操作
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
output=LLed_FlashOnCheck(light);
else
output=LLed_FlashOffCheck(light);
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH_INTERVAL)
LLed_FlashIntervalSwitchToNext(light);
}
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_FINISH)
LLed_FlashIntervalFinish(light);
}
if (pLLedDrv->led[light].mode>=LLED_MODE_FLASH)
pLLedDrv->needChange=TRUE;
if (pLLedDrv->led[light].trig==LLED_TRIG_ON||output==TRUE)
{
pLLedDrv->led[light].trig=LLED_TRIG_OFF;
pLLedDrv->drvFunc.output[light](pLLedDrv->led[light].status);
}
}
LLed_UnLock();
}
灯工作控制操作
此函数执行每一个LED灯的工作控制操作。包括常亮、常灭、闪烁、间歇性闪烁4种工作方式设置。
bool LLedFuncCtrl(uint8_t light,void* pParam)
{
tLLedCtrlParam *pCtrlParam;
bool result=TRUE;
pCtrlParam=(tLLedCtrlParam*)pParam;
if (pLLedDrv==NULL||light>=pLLedDrv->lightMax)
return FALSE;
LLed_Lock();
//
pLLedDrv->led[light].run.cntDetect=FALSE;
switch (pCtrlParam->mode)
{
case LLED_MODE_BRIGHT:
if (pLLedDrv->led[light].mode!=LLED_MODE_BRIGHT)
{
//设置指定的LED灯工作于常亮
pLLedDrv->led[light].mode=LLED_MODE_BRIGHT;
pLLedDrv->led[light].status=LLED_STATUS_ON;
}
else
result=FALSE;
break;
case LLED_MODE_DARKNESS:
if (pLLedDrv->led[light].mode!=LLED_MODE_DARKNESS)
{
//设置指定的LED灯工作与常灭
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
}
else
result=FALSE;
break;
case LLED_MODE_FLASH:
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH)
{
tLLedFlashParam* pFlashParam;
pFlashParam=(tLLedFlashParam*)pCtrlParam->runParam;
//设置LED灯闪烁参数
result=LLed_FlashParamSetup(light,pFlashParam);
if (result==TRUE)
{
//同步相同频率的LED灯闪烁(未找到时,从点亮开始)
LLed_SyncFlash(light);
pLLedDrv->led[light].mode=LLED_MODE_FLASH;
}
}
else
result=FALSE;
break;
case LLED_MODE_FLASH_INTERVAL:
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH_INTERVAL)
{
tLLedFlashIntervalParam* pFlashIntervalParam;
pFlashIntervalParam=(tLLedFlashIntervalParam*)pCtrlParam->runParam;
result=LLed_FlashIntervalParamCheck(pFlashIntervalParam);
if (result==TRUE)
{
//间歇性闪烁应该不再需要考虑同步相同频率的问题
LLed_FlashParamSetup(light,&pFlashIntervalParam->first);
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
pLLedDrv->led[light].status=LLED_STATUS_OFF;
else
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].run.intervalCnt.exec=0;
pLLedDrv->led[light].run.intervalCnt.max=pFlashIntervalParam->count;
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FIRST;
pLLedDrv->led[light].flashIntervalParam=*pFlashIntervalParam;
pLLedDrv->led[light].flashIntervalParam.darkTime/=pLLedDrv->scanUnit;
pLLedDrv->led[light].mode=LLED_MODE_FLASH_INTERVAL;
}
}
else
result=FALSE;
break;
}
if (result==TRUE)
{
pLLedDrv->led[light].trig=LLED_TRIG_ON;
pLLedDrv->needChange=TRUE;
}
LLed_UnLock();
return result;
}
灯驱动安装
LED灯驱动安装主要完成驱动内存申请,驱动函数安装及功能函数返回等。
返回tLLedHandle*类型的LED灯操作句柄,用于应用程序调用,若返回为NULL,则安装失败;返回tLLedVoid类型的poll被用于POLL调用,tLLedCtrlFunc类型的ctrl被用于应用程序控制LED灯工作模式时调用,若返回为NULL,则安装失败;
tLLedHandle* LLed_Initial(tLLedInitParam *pInitParam)
{
bool drvFunc=TRUE;
uint8_t light;
if (pInitParam==NULL||pLLedDrv!=NULL)
return NULL;
for (light=0;light<pInitParam->lightMax;light++)
{
//检查所有LED灯输出函数是否为空
if (pInitParam->drvFunc.output[light]==NULL)
{
drvFunc=FALSE;
break;
}
}
//检查扫描时间
if (pInitParam->scanUnit>0&&(pInitParam->scanUnit<LLED_SCAN_MIN||pInitParam->scanUnit>LLED_SCAN_MAX||(pInitParam->scanUnit%10)!=0))
drvFunc=FALSE;
//检查互斥锁(必须提供成对操作,不能单一为空)
if ((pInitParam->drvFunc.lock!=NULL&&pInitParam->drvFunc.unLock==NULL)||(pInitParam->drvFunc.lock==NULL&&pInitParam->drvFunc.unLock!=NULL))
drvFunc=FALSE;
if (drvFunc==TRUE)//必须提供LED灯输出函数,否则安装失败
{
//申请驱动指针内存
pLLedDrv=MALLOC_MEM(sizeof(tLLedDriver)+sizeof(tLLed)*pInitParam->lightMax+sizeof(tLLedOutputFunc)*pInitParam->lightMax);
pLLedDrv->led=(tLLed*)((uint8_t*)pLLedDrv+sizeof(tLLedDriver));
pLLedDrv->drvFunc.output=(tLLedOutputFunc*)((uint8_t*)pLLedDrv->led+sizeof(tLLed)*pInitParam->lightMax);
pLLedDrv->needChange=FALSE;
pLLedDrv->lightMax=pInitParam->lightMax;
pLLedDrv->drvFunc=pInitParam->drvFunc;
for (light=0;light<pLLedDrv->lightMax;light++)
pLLedDrv->drvFunc.output[light]=pInitParam->drvFunc.output[light];
//初始化轮询扫描时间单位
if (pInitParam->scanUnit==0)
pLLedDrv->scanUnit=LLEDSCAN_UINT_DEF;
else
pLLedDrv->scanUnit=pInitParam->scanUnit;
if (pLLedDrv->drvFunc.lock==NULL)
pLLedDrv->swLock=LLED_UNLOCKED;
//执行硬件底层初始化
if (pLLedDrv->drvFunc.init!=NULL)
pLLedDrv->drvFunc.init();
//初始化输出(默认初始输出熄灭)
for (light=0;light<pLLedDrv->lightMax;light++)
{
//对于闪烁,cntDetect控制着在执行熄灭时是否进行闪烁计数
pLLedDrv->led[light].run.cntDetect=FALSE;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
pLLedDrv->led[light].trig=LLED_TRIG_OFF;
pLLedDrv->drvFunc.output[light](LLED_STATUS_OFF);
}
//设置LED灯外部操作控制功能函数及POLL函数指针
pLLedDrv->handle.ctrl=LLedFuncCtrl;
pLLedDrv->handle.poll=LLed_Poll;
return &pLLedDrv->handle;
}
else
{
//安装失败
pLLedDrv=NULL;
return NULL;
}
}
灯驱动卸载
LED灯驱动的卸载调用前,最好手动停止LED灯轮询的定时调用,此处不对其进行检查,仅使用应用层提供的互斥锁。
bool LLed_UnInitial(void)
{
bool result=TRUE;
LLed_Lock();
if (pLLedDrv!=NULL)
{
//执行低级去初始化操作
if (pLLedDrv->drvFunc.unInit!=NULL)
pLLedDrv->drvFunc.unInit();
FREE_MEM(pLLedDrv);
pLLedDrv=NULL;
}
else
result=FALSE;
LLed_UnLock();
return result;
}
总结
以上的驱动,实际上就4个主要函数:(在实际驱动文件中,包含更多的是辅助被调用的模块)
void LLed_Poll(void); ------LED灯轮询函数;
bool LLedFuncCtrl(uint8_t light,void* pParam);------LED灯工作控制函数;
tLLedHandle* LLed_Initial(tLLedInitParam *pInitParam);------LED灯初始化函数;
bool LLed_UnInitial(void); ------LED灯去初始化函数;
1.必要的移植修改设计说明
1)需要定义LED灯;
2)至少需要设计LED灯输出函数(output),且每一个LED灯有一个对应函数;
3)不管是裸机运行还是在操作系统下运行,均需要进行LED灯驱动安装;
4)设计中将轮询输出函数提供给应用层去决定如何执行,这样可以进一步降低耦合度以及增强LED灯驱动使用的灵活性。对于裸机运行可将轮询输出函数放在硬件定时器产生的POLL中执行;对于运行在操作系统(如FreeRTOS)下可以将轮询输出函数放在某个软件定时器的回调函数中执行(或发送事件到某个任务执行);
5)可能还会有一些没能想到的功能,如果有需要,仍然可以添加到轮询输出函数及外部功能函数中;
2.外部调用接口
驱动安装函数LLed_Initial是唯一直接提供给应用层必须调用的接口;
驱动卸载函数LLed_UnInitial是直接接提供给应用层可选调用的接口;
轮询输出函数LLed_Poll是间接提供给应用层周期性调用的接口;
外部功能函数LLedFuncCtrl是间接提供给应用层控制LED灯显示行为调用的接口;
灯驱动使用示例
由于本人不知道如何(或是否可以)在这里放文件,只有贴出全部代码,包括驱动文件与,同时提供一份文件,这是我用以调测此驱动的全部应用层代码。
因之前一位朋友打算使用这个驱动的代码,但由于我并没有编译及测试。为了不给类似的朋友造成困惑,这两天我对驱动进行了调测,并追加了一定功能(间歇性闪烁属于后来加上去的)。所以,以下给出的三份文件,是经过本人测试通过的,原则上可以直接使用。(当然,没有做异常测试,仅正常调用运行)
1)文件代码(驱动.h文件)
#ifndef _LLEDDRIVER_H
#define _LLEDDRIVER_H
#ifndef TRUE
#define TRUE 1
#define FALSE 0
typedef uint8_t bool;
#endif
//以下LLED为LED LIGHT缩写,并将LIGHT缩写L写在前面
//LED锁(非OS使用)
typedef enum
{
LLED_UNLOCKED,
LLED_LOCKED
}tLLed_Lock;
//LED灯状态
typedef enum
{
LLED_STATUS_ON, //点亮状态
LLED_STATUS_OFF //熄灭状态
}tLLedStatus;
//LED 输出强制触发标志
typedef enum
{
LLED_TRIG_ON, //触发打开
LLED_TRIG_OFF //触发关闭
}tLLedTrig;
//LED灯工作方式
typedef enum
{
LLED_MODE_BRIGHT, //常亮方式
LLED_MODE_DARKNESS, //熄灭方式
LLED_MODE_FLASH, //闪烁方式
LLED_MODE_FLASH_INTERVAL //间歇性闪烁方式
}tLLedMode;
/*----------------------------------------------------------------------
间歇性闪烁说明:分三个步骤执行
1 FIRST FLASH(短或长) ,以设定频率闪烁N次;
2 SECOND FLASH(长或短),以设定频率闪烁M次;
3 DARK,熄灭一定时间;
注:FIRST FLASH与SECOND FLASH应该设置为不同频率以实现
短闪或长闪;同时,如果N或M等于0,则跳过,但M与N
不能同时为 0.
另外,通过此参数可实现以下功能:
1) N 短闪+ M长闪+ 间歇;
2) N 短闪+ M长闪;
3) N 短闪+ 间歇;
4) N 短闪;
或以上短闪与长闪交换的效果
----------------------------------------------------------------------*/
#define LLED_FLASH_INTERVAL_FIRST 0 //间歇性运行第一个阶段(闪烁 N次)
#define LLED_FLASH_INTERVAL_SECOND 1 //间歇性运行第二个阶段(闪烁M次)
#define LLED_FLASH_INTERVAL_DARK 2 //间歇性运行第三个阶段(熄灭)
#define LLED_FLASH_INTERVAL_FINISH 3 //间隙性运行一组完成
//LED灯闪烁频率 (有些频率其实并没有比较明显的效果,不用定义每一个频率)
#define LLED_BASETIME 100 //基本时间(ms)
typedef enum
{
LLED_FREQ_1_10Hz=100, //0.1Hz
LLED_FREQ_2_10Hz=50, //0.2Hz
LLED_FREQ_5_10Hz=20, //0.5Hz
LLED_FREQ_1Hz=10, //1Hz
LLED_FREQ_2Hz=5, //2Hz
LLED_FREQ_5Hz=2, //5Hz
LLED_FREQ_10Hz=1, //10Hz
}tLLedFreq;//LED灯闪烁频率
//部分灯闪烁参数限定值
#define LLED_DUTY_MIN 10 //占空比最小值
#define LLED_DUTY_MAX 90 //占空比最大值
#define LLED_SCAN_MIN 10 //扫描时间最小值
#define LLED_SCAN_MAX 50 //扫描时间最大值
//LED灯闪烁参数
typedef struct
{
tLLedFreq freq; //闪烁频率
tLLedStatus endStatus; //闪烁结束LED灯状态
uint8_t dutyRatio; //取值范围[10/20/30/40/50/60/70/80/90]
uint8_t counter; //闪烁次数[0--255],0表示持续闪烁
}tLLedFlashParam;
//间歇性闪烁参数
typedef struct
{
uint8_t count; //间歇闪烁执行次数
uint16_t darkTime; //熄灭时间,单位ms,但必须设置为扫描时间的整数值
tLLedFlashParam first; //闪烁first参数
tLLedFlashParam second; //闪烁second参数
}tLLedFlashIntervalParam;
//LED灯闪烁与间歇性闪烁计数器
typedef struct
{
uint8_t max; //最大计数值
uint8_t exec; //执行计数值
}tLLedCnt;
//LED灯闪烁运行数据
typedef struct
{
bool cntDetect; //计数检测标志(取值TRUE才可以检测计数值)
uint8_t intervalLevel; //记录间歇性运行的阶段
uint16_t count; //计数器
uint16_t onCntMax; //点亮计数器最大值
uint16_t offCntMax; //熄灭计数器最大值
tLLedCnt flashCnt; //闪烁计数器
tLLedCnt intervalCnt; //间歇闪烁计数器
tLLedStatus endStatus; //闪烁结束LED灯状态
}tLLedFlashRun;
/*----------------------------------------------------------------------
闪烁开始执行时LED状态说明:
当前状态为:点亮,则会先从熄灭开始执行,同时会在下
一个点亮开始计数
当前状态为:熄灭,则直接从点亮开始执行,并开始计数
----------------------------------------------------------------------*/
//LED灯控制参数(调用LED灯控制输出时的入口参数)
typedef struct
{
tLLedMode mode; //LED灯工作模式
void* runParam; //LED工作运行参数
}tLLedCtrlParam;
/*----------------------------------------------------------------------
参数runParam说明:
1 当mode=LLED_MODE_BRIGHT/LLED_MODE_DARKNESS时
runParam未使用,可设置为NULL;
2 当mode=LLED_MODE_FLASH时
runParam需使用tLLedFlashParam类型的参数指针;
3 当mode=LLED_MODE_FLASH_INTERVAL时
runParam需使用tLLedFlashIntervalParam类型的参数指针;
----------------------------------------------------------------------*/
//LED灯驱动函数原型定义
typedef void(*tLLedVoid)(void); //LED无参函数原型
typedef bool(*tLLedCtrlFunc)(uint8_t,void*); //LED灯工作控制函数原型
typedef void(*tLLedOutputFunc)(tLLedStatus); //LED灯输出控制函数原型
//LED灯应用层提供的驱动函数
typedef struct
{
tLLedOutputFunc *output; //安装的LED灯输出控制函数
tLLedVoid init; //安装的LED灯底层初始化函数(可以设置为空)
tLLedVoid unInit; //安装的LED灯底层去初始化函数(可以设置为空)
tLLedVoid lock; //安装的LED灯互斥锁获取函数(可以设置为空)
tLLedVoid unLock; //安装的LED灯互斥锁归还函数(可以设置为空)
}tLLedDrvFunc;
//LED灯驱动安装参数
typedef struct
{
uint8_t scanUnit; //LED灯扫描时间[10/20/30/40/50]
uint8_t lightMax; //LED灯最大数目
tLLedDrvFunc drvFunc; //LED灯驱动函数
}tLLedInitParam;
//LED灯基本结构
typedef struct
{
tLLedMode mode; //LED灯工作模式
tLLedTrig trig; //LED点亮及熄灭触发标志
tLLedStatus status; //LED灯状态
tLLedFlashRun run; //LED灯闪烁运行
tLLedFlashIntervalParam flashIntervalParam; //LED灯间歇性闪烁保存参数
}tLLed;
//LED 灯驱动提供给应用层的操作句柄
typedef struct
{
tLLedVoid poll; //poll函数
tLLedCtrlFunc ctrl; //LED灯控制功能函数
}tLLedHandle;
/*----------------------------------------------------------------------
ctrl功能函数使用说明:
light为需要设置的LED灯序号
ledHandle为LED操作句柄
1,控制LED灯常亮或常灭:
tLLedCtrlParam ctrlParam;
=LLED_MODE_BRIGHT;//LLED_MODE_DARKNESS;
=NULL;
ledHandle->ctrl(light,&ctrlParam);
2,控制LED灯闪烁
tLLedCtrlParam ctrlParam;
tLLedFlashParam flashParam;
=LLED_MODE_FLASH;
=LLED_FREQ_1Hz;
=50;
=LLED_STATUS_OFF;
=LLED_FLASHKEEP_CNT;
=&flashParam;
ledHandle->ctrl(light,&ctrlParam);
3,控制LED灯为间歇性闪烁
tLLedCtrlParam ctrlParam;
tLLedFlashIntervalParam flashIntervalParam;
//以下设置3短1长闪烁(持续执行)
=LLED_MODE_FLASH_INTERVAL;
=LLED_FREQ_5Hz;
=50;
=LLED_STATUS_OFF;
=3;
=LLED_FREQ_1Hz;
=50;
=LLED_STATUS_OFF;
=1;
=1000;
=LLED_FLASHKEEP_CNT;
=&flashIntervalParam;
ledHandle->ctrl(light,&ctrlParam);
-----------------------------------------------------------------------*/
//LED灯驱动程序使用的数据结构
typedef struct
{
uint8_t scanUnit; //LED扫描时间单位,ms
uint8_t lightMax; //LED灯最大数目
bool needChange; //LED灯需要改变状态时,将被设置为TRUE)
tLLed_Lock swLock; //LED软件互斥锁
tLLed *led; //LED灯组
tLLedDrvFunc drvFunc; //外部安装的驱动函数
tLLedHandle handle; //外部调用的功能函数(此类型说明在后面安装驱动中说明)
}tLLedDriver;
#define LLED_FLASHKEEP_CNT 0 //持续闪烁(计数值设置为0)
tLLedHandle* LLed_Initial(tLLedInitParam *pInitParam);
bool LLed_UnInitial(void);
#endif
2)文件代码(驱动.c文件)
/*------------------------------------------说明:------------------------------------------------
此驱动已经被编译并调试验证,具体验证条件将在详细说明,此处仅
对一个设置进行说明:
1,在验证时USE_OS中定义使用了FreeRTOS,因此才有如下定义
#if USE_OS==OS_FREERTOS
#include ""
#endif
2,LLED驱动主要具备以下功能:
1) 通过调用初始化后提供操作句柄,其主要涉及2个功能:
A,poll指针函数,其被应用层在定时扫描位置调用;
B, ctrl指针函数,其被应用层需要控制LED灯时调用;
说明: 关于ctrl的调用参数说明,请见中定义的相关说明部分.
2) 整个驱动主要实现LED灯的多种模式控制操作:
A, 点亮/熄灭操作;
B, 普通闪烁控制操作;
C, 间歇性闪烁控制操作;
关于间歇性闪烁控制操作,请见中定义的相关说明部分.
--------------------------------------------------------------------------------------------------
注: 如果有需要,在考虑节约内存时,可以考虑将以下两个变量设计为指针并在需要执行时才申请内存,
并在执行完毕后释放内存。目前暂时不作修改。
tLLedDriver结构中led内的以下变量:
tLLedFlashRun run
tLLedFlashIntervalParam flashIntervalParam;
--------------------------------------------------------------------------------------------------*/
#define OS_NONE 0
#define OS_FREERTOS 1
#define USE_OS OS_FREERTOS
#if USE_OS==OS_FREERTOS
#include ""
#endif
#include ""
//LLED_INTERVAL_SECONDFLASH_DLY设置用于增加FIRST FLASH 与SECOND FLASH直接较明显的间隔
#define LLED_INTERVAL_SECONDFLASH_DLY 1
//
#define LLEDSCAN_UINT_DEF 10 //LED灯扫描默认时间单位(ms)
//以下定义内存申请与释放
#define MALLOC_MEM(memSize) pvPortMalloc(memSize) //内存申请宏
#define FREE_MEM(pMem) vPortFree(pMem) //内存释放宏
tLLedDriver *pLLedDrv=NULL; //仅用于驱动内部的驱动指针
//===========================================================================
//LED驱动操作互斥锁模块
/*--------------------------------------------------------------------------------------------------
* 摘要: 执行应用层提供的获取互斥锁操作或驱动内部的获取软件互斥锁
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_Lock(void)
{
if (pLLedDrv==NULL)
return;
if (pLLedDrv->drvFunc.lock!=NULL)
pLLedDrv->drvFunc.lock();
else
{
while (pLLedDrv->swLock==LLED_LOCKED);
pLLedDrv->swLock=LLED_LOCKED;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 执行应用层提供的归还互斥锁操作或驱动内部的归还软件互斥锁
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_UnLock(void)
{
if (pLLedDrv==NULL)
return;
if (pLLedDrv->drvFunc.unLock!=NULL)
pLLedDrv->drvFunc.unLock();
else
pLLedDrv->swLock=LLED_UNLOCKED;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 设置LED灯闪烁相关参数,根据闪烁频率与占空比计算点亮与熄灭
计数值,并设置闪烁次数与结束状态
* 参数: uint8_t light LED灯序号
* tLLedFlashParam* pFlashParam闪烁参数指针
* 返回: TRUE 设置成功
* FALSE设置失败
---------------------------------------------------------------------------------------------------*/
static bool LLed_FlashParamSetup(uint8_t light,tLLedFlashParam* pFlashParam)
{
uint16_t totalCnt;
totalCnt=pFlashParam->freq*LLED_BASETIME/pLLedDrv->scanUnit;
//这里totalCnt的计算结果不能≤1,否则无法执行闪烁
if (totalCnt<=1)
return FALSE;
if (pFlashParam->dutyRatio<LLED_DUTY_MIN||pFlashParam->dutyRatio>LLED_DUTY_MAX||(pFlashParam->dutyRatio%10)!=0)
return FALSE;
//根据占空比计算闪烁点亮的计数器值
pLLedDrv->led[light].run.onCntMax=totalCnt*pFlashParam->dutyRatio/100;
if (pLLedDrv->led[light].run.onCntMax==0)
pLLedDrv->led[light].run.onCntMax=1;
//计算闪烁熄灭的计数器值
pLLedDrv->led[light].run.offCntMax=totalCnt-pLLedDrv->led[light].run.onCntMax;
pLLedDrv->led[light].run.count=0;
pLLedDrv->led[light].run.flashCnt.exec=0;
pLLedDrv->led[light].run.flashCnt.max=pFlashParam->counter;
pLLedDrv->led[light].run.endStatus=pFlashParam->endStatus;
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯闪烁点亮计时检测,并在时间到达后切换至闪烁熄灭状态
* 参数: LED灯序号
* 返回: TRUE 输出控制
* FALSE不输出控制
---------------------------------------------------------------------------------------------------*/
static bool LLed_FlashOnCheck(uint8_t light)
{
bool output=FALSE;
//闪烁点亮计时
pLLedDrv->led[light].run.count++;
if (pLLedDrv->led[light].run.count>=pLLedDrv->led[light].run.onCntMax)
{
//计时达到设定值,切换至闪烁灭状态
pLLedDrv->led[light].run.count=0;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
if (pLLedDrv->led[light].run.flashCnt.max!=LLED_FLASHKEEP_CNT)
pLLedDrv->led[light].run.cntDetect=TRUE;
output=TRUE;
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯闪烁计数值计数并检测是否执行完毕,并在执行完毕时设置
* LED灯到指定的输出状态
* 参数: LED灯序号
* 返回: TRUE 输出控制
* FALSE不输出控制
* 说明: 此函数是由LLed_FlashOffCheck函数分离出来的模块
---------------------------------------------------------------------------------------------------*/
static bool LLed_FlashCountCheck(uint8_t light)
{
pLLedDrv->led[light].run.cntDetect=FALSE;
//闪烁次数计数,并检测闪烁次数
pLLedDrv->led[light].run.flashCnt.exec++;
if (pLLedDrv->led[light].run.flashCnt.exec>=pLLedDrv->led[light].run.flashCnt.max)
{
//闪烁次数达到设定值,结束闪烁
if (pLLedDrv->led[light].run.endStatus==LLED_STATUS_ON)
{
//指定结束LED灯状态为常亮
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].mode=LLED_MODE_BRIGHT;
}
else
{
//指定结束LED灯状态为常灭
pLLedDrv->led[light].status=LLED_STATUS_OFF;
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
}
return FALSE;
}
else//闪烁没有结束,继续执行(下一次执行灯亮状态)
{
pLLedDrv->led[light].status=LLED_STATUS_ON;
return TRUE;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯闪烁熄灭计时检测,并在时间到达后执行LED灯闪烁计数值计数
* 及检测,并在继续执行时切换至闪烁点亮状态。
* 参数: LED灯序号
* 返回: TRUE 输出控制
* FALSE不输出控制
---------------------------------------------------------------------------------------------------*/
static bool LLed_FlashOffCheck(uint8_t light)
{
bool output=FALSE;
//闪烁熄灭计时
pLLedDrv->led[light].run.count++;
if (pLLedDrv->led[light].run.count>=pLLedDrv->led[light].run.offCntMax)
{
//计时达到设定值
pLLedDrv->led[light].run.count=0;
if (pLLedDrv->led[light].run.cntDetect==TRUE)
output=LLed_FlashCountCheck(light);
else//持续闪烁,则继续执行(下一次执行灯亮状态)
{
pLLedDrv->led[light].status=LLED_STATUS_ON;
output=TRUE;
}
}
return output;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯间歇闪烁执行阶段切换,主要完成以下几个阶段切换
* 1,FIRST FLASH TO SECOND FLASH(LLED_FLASH_INTERVAL_FIRST --> LLED_FLASH_INTERVAL_SECOND)
* 2,SECOND FLASH TO DARK(LLED_FLASH_INTERVAL_SECOND --> LLED_FLASH_INTERVAL_DARK)
* 3,DARK TO FINISH(LLED_FLASH_INTERVAL_DARK --> LLED_FLASH_INTERVAL_FIN)
* 参数: LED灯序号
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_FlashIntervalSwitchToNext(uint8_t light)
{
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_FIRST)
{
if (pLLedDrv->led[light].flashIntervalParam.second.counter>0)
{
//切换至运行的第二阶段闪烁
LLed_FlashParamSetup(light,&pLLedDrv->led[light].flashIntervalParam.second);
#if LLED_INTERVAL_SECONDFLASH_DLY==1
//为了增加SECOND FLASH与FIRST FLASH之间的明显间隔,使SECOND FLASH延迟一个计数周期在开始
pLLedDrv->led[light].status=LLED_STATUS_OFF;
#else
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].trig=LLED_TRIG_ON;
#endif
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_SECOND;
}
else//第二阶段的执行次数为0,则跳过到第三阶段
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_DARK;
}
else//切换至运行的第三阶段熄灭
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_DARK;
//第三阶段的熄灭时间为0,则跳过执行
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_DARK&&pLLedDrv->led[light].flashIntervalParam.darkTime==0)
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FINISH;
pLLedDrv->led[light].mode=LLED_MODE_FLASH_INTERVAL;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯间歇闪烁一次执行完成处理,整个闪烁执行完毕时,则设置到
* 最后指定的输出状态;否则继续执行下一次间歇性闪烁。
* 参数: LED灯序号
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_FlashIntervalFinish(uint8_t light)
{
bool goon=TRUE;
if (pLLedDrv->led[light].run.intervalCnt.max!=LLED_FLASHKEEP_CNT)
{
pLLedDrv->led[light].run.intervalCnt.exec++;
if (pLLedDrv->led[light].run.intervalCnt.exec>=pLLedDrv->led[light].run.intervalCnt.max)
{
if (pLLedDrv->led[light].run.endStatus==LLED_STATUS_ON)
{
//指定结束LED灯状态为常亮
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].mode=LLED_MODE_BRIGHT;
}
else
{
//指定结束LED灯状态为常灭
pLLedDrv->led[light].status=LLED_STATUS_OFF;
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
}
goon=FALSE;
}
}
if (goon==TRUE)
{
LLed_FlashParamSetup(light,&pLLedDrv->led[light].flashIntervalParam.first);
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].trig=LLED_TRIG_ON;
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FIRST;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: LED灯POLLING,主要执行LED灯闪控制操作(也执行点亮与熄灭操作)
* 当所有LED灯都处于常亮或常灭时,此函数将不会往下执行。
* 参数: 无
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_Poll(void)
{
uint8_t light;
bool output;
LLed_Lock();
//没有安装驱动,拒绝执行或 没有LED灯状态需要改变,则直接退出
if (pLLedDrv==NULL||pLLedDrv->needChange==FALSE)
{
LLed_UnLock();
return;
}
pLLedDrv->needChange=FALSE;
//循环处理所有LED灯
for (light=0;light<pLLedDrv->lightMax;light++)
{
//输出控制,并不是每次都需要输出,仅在状态发生改变时才执行(或强制输出)
output=FALSE;
if (pLLedDrv->led[light].mode==LLED_MODE_FLASH)
{
//闪烁操作处理
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
output=LLed_FlashOnCheck(light);//闪烁点亮计时,并检测切换至闪烁灭状态
else
output=LLed_FlashOffCheck(light);//闪烁熄灭计时,并检测切换至闪烁亮状态,同时,检测闪烁次数
}
else if (pLLedDrv->led[light].mode==LLED_MODE_FLASH_INTERVAL)
{
//间歇性闪烁操作处理
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_DARK)
{
pLLedDrv->led[light].run.count++;
if (pLLedDrv->led[light].run.count>=pLLedDrv->led[light].flashIntervalParam.darkTime)
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FINISH;
}
else
{
//间歇性闪烁的第一步及第二步均执行闪烁操作
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
output=LLed_FlashOnCheck(light);
else
output=LLed_FlashOffCheck(light);
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH_INTERVAL)
LLed_FlashIntervalSwitchToNext(light);
}
if (pLLedDrv->led[light].run.intervalLevel==LLED_FLASH_INTERVAL_FINISH)
LLed_FlashIntervalFinish(light);
}
if (pLLedDrv->led[light].mode>=LLED_MODE_FLASH)
pLLedDrv->needChange=TRUE;
if (pLLedDrv->led[light].trig==LLED_TRIG_ON||output==TRUE)
{
pLLedDrv->led[light].trig=LLED_TRIG_OFF;
pLLedDrv->drvFunc.output[light](pLLedDrv->led[light].status);
}
}
LLed_UnLock();
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 检查间歇性闪烁参数合法性
* 参数: tLLedFlashIntervalParam pFlashIntervalParam 间歇性闪烁参数指针
* 返回: TRUE 检查OK
* FALSE检查FAIL
---------------------------------------------------------------------------------------------------*/
static bool LLed_FlashIntervalParamCheck(tLLedFlashIntervalParam* pFlashIntervalParam)
{
if (pFlashIntervalParam->darkTime!=0&&(pFlashIntervalParam->darkTime%pLLedDrv->scanUnit)!=0)
return FALSE;
if (pFlashIntervalParam->first.counter==0||
(pFlashIntervalParam->first.dutyRatio<LLED_DUTY_MIN||
pFlashIntervalParam->first.dutyRatio>LLED_DUTY_MAX||
(pFlashIntervalParam->first.dutyRatio%10)!=0))
return FALSE;
if (pFlashIntervalParam->second.counter!=0&&
(pFlashIntervalParam->second.dutyRatio<LLED_DUTY_MIN||
pFlashIntervalParam->second.dutyRatio>LLED_DUTY_MAX||
(pFlashIntervalParam->second.dutyRatio%10)!=0))
return FALSE;
else
return TRUE;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 根据提供的LED灯序号,查找相同频率的LED灯序号
* 参数: uint8_t light被比较的LED灯序号
* 返回: pLLedDrv->lightMax:未找到相同频率的LED灯
* <pLLedDrv->lightMax:找到了相同频率的LED灯
* 说明: 此函数是由LLed_SyncFlash函数分离出来的模块
---------------------------------------------------------------------------------------------------*/
static uint8_t LLed_FindSameFreq(uint8_t light)
{
uint8_t loop;
uint16_t syncedLedCnt;
syncedLedCnt=pLLedDrv->led[light].run.onCntMax+pLLedDrv->led[light].run.offCntMax;
for (loop=0;loop<pLLedDrv->lightMax;loop++)
{
if (loop!=light)
{
if (pLLedDrv->led[loop].mode==LLED_MODE_FLASH)
{
uint8_t syncLedCnt;
//计算查找到同步LED灯闪烁计数值
syncLedCnt=pLLedDrv->led[loop].run.onCntMax+pLLedDrv->led[loop].run.offCntMax;
if (syncLedCnt==syncedLedCnt)
{
//两个灯的闪烁计数值(频率)相同
break;
}
}
}
}
return loop;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 根据提供的LED灯序号,查找相同频率的LED灯序号,并与其保持闪烁
* 的同步性。若没有找到相同频率的LED灯时,则设置闪烁的初始状态
* 参数: uint8_t light被同步的LED灯序号
* 返回: 无
---------------------------------------------------------------------------------------------------*/
static void LLed_SyncFlash(uint8_t light)
{
uint8_t syncLed;
syncLed=LLed_FindSameFreq(light);
pLLedDrv->led[light].run.cntDetect=FALSE;
if (syncLed<pLLedDrv->lightMax)
{
//两个灯的闪烁计数值(频率)相同,则同步
if (pLLedDrv->led[syncLed].status==LLED_STATUS_ON)
{
if (pLLedDrv->led[syncLed].run.count<pLLedDrv->led[light].run.onCntMax)
{
//同步LED灯处于亮状态
pLLedDrv->led[light].run.count=pLLedDrv->led[syncLed].run.count;
pLLedDrv->led[light].status=LLED_STATUS_ON;
if (pLLedDrv->led[light].run.flashCnt.max!=LLED_FLASHKEEP_CNT)
pLLedDrv->led[light].run.cntDetect=TRUE;
}
else
{
//因占空比不同,使得同步的点亮计数值已经超出被同步的最大值
pLLedDrv->led[light].run.count=pLLedDrv->led[syncLed].run.count-pLLedDrv->led[light].run.onCntMax;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
}
}
else
{
if (pLLedDrv->led[syncLed].run.count<pLLedDrv->led[light].run.offCntMax)
{
//同步LED灯处于灭状态
pLLedDrv->led[light].run.count=pLLedDrv->led[syncLed].run.count;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
}
else
{
//因占空比不同,使得同步的熄灭计数值已经超出被同步的最大值
pLLedDrv->led[light].run.count=pLLedDrv->led[syncLed].run.count-pLLedDrv->led[light].run.offCntMax;
pLLedDrv->led[light].status=LLED_STATUS_ON;
if (pLLedDrv->led[light].run.flashCnt.max!=LLED_FLASHKEEP_CNT)
pLLedDrv->led[light].run.cntDetect=TRUE;
}
}
}
else
{
//没有找到同步LED灯,则设置被同步LED灯从点亮开始
pLLedDrv->led[light].run.count=0;
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
pLLedDrv->led[light].status=LLED_STATUS_OFF;
else
pLLedDrv->led[light].status=LLED_STATUS_ON;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 被安装在pLLedDrv->的LED灯工作状态设置函数,包括常亮与常灭
* 普通闪烁与间歇性闪烁。在闪烁或间歇性闪烁模式需要提供参数
* 参数: uint8_t light LED灯序号;
* void* pParam LED灯控制参数(tLLedCtrlParam类型);
* 其中tLLedCtrlParam内的参数runParam需按照以下方式提供:
* 1 当mode=LLED_MODE_BRIGHT/LLED_MODE_DARKNESS时
* runParam未使用,可设置为NULL;
* 2 当mode=LLED_MODE_FLASH时
* runParam需使用tLLedFlashParam类型的参数指针;
* 3 当mode=LLED_MODE_FLASH_INTERVAL时
* runParam需使用tLLedFlashIntervalParam类型的参数指针;
* 返回: TRUE 设置成功;
* FALSE设置失败;
---------------------------------------------------------------------------------------------------*/
static bool LLedFuncCtrl(uint8_t light,void* pParam)
{
tLLedCtrlParam *pCtrlParam;
bool result=TRUE;
pCtrlParam=(tLLedCtrlParam*)pParam;
if (pLLedDrv==NULL||light>=pLLedDrv->lightMax)
return FALSE;
LLed_Lock();
//
pLLedDrv->led[light].run.cntDetect=FALSE;
switch (pCtrlParam->mode)
{
case LLED_MODE_BRIGHT:
if (pLLedDrv->led[light].mode!=LLED_MODE_BRIGHT)
{
//设置指定的LED灯工作于常亮
pLLedDrv->led[light].mode=LLED_MODE_BRIGHT;
pLLedDrv->led[light].status=LLED_STATUS_ON;
}
else
result=FALSE;
break;
case LLED_MODE_DARKNESS:
if (pLLedDrv->led[light].mode!=LLED_MODE_DARKNESS)
{
//设置指定的LED灯工作与常灭
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
}
else
result=FALSE;
break;
case LLED_MODE_FLASH:
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH)
{
tLLedFlashParam* pFlashParam;
pFlashParam=(tLLedFlashParam*)pCtrlParam->runParam;
//设置LED灯闪烁参数
result=LLed_FlashParamSetup(light,pFlashParam);
if (result==TRUE)
{
//同步相同频率的LED灯闪烁(未找到时,从点亮开始)
LLed_SyncFlash(light);
pLLedDrv->led[light].mode=LLED_MODE_FLASH;
}
}
else
result=FALSE;
break;
case LLED_MODE_FLASH_INTERVAL:
if (pLLedDrv->led[light].mode!=LLED_MODE_FLASH_INTERVAL)
{
tLLedFlashIntervalParam* pFlashIntervalParam;
pFlashIntervalParam=(tLLedFlashIntervalParam*)pCtrlParam->runParam;
result=LLed_FlashIntervalParamCheck(pFlashIntervalParam);
if (result==TRUE)
{
//间歇性闪烁应该不再需要考虑同步相同频率的问题
LLed_FlashParamSetup(light,&pFlashIntervalParam->first);
if (pLLedDrv->led[light].status==LLED_STATUS_ON)
pLLedDrv->led[light].status=LLED_STATUS_OFF;
else
pLLedDrv->led[light].status=LLED_STATUS_ON;
pLLedDrv->led[light].run.intervalCnt.exec=0;
pLLedDrv->led[light].run.intervalCnt.max=pFlashIntervalParam->count;
pLLedDrv->led[light].run.intervalLevel=LLED_FLASH_INTERVAL_FIRST;
pLLedDrv->led[light].flashIntervalParam=*pFlashIntervalParam;
pLLedDrv->led[light].flashIntervalParam.darkTime/=pLLedDrv->scanUnit;
pLLedDrv->led[light].mode=LLED_MODE_FLASH_INTERVAL;
}
}
else
result=FALSE;
break;
}
if (result==TRUE)
{
pLLedDrv->led[light].trig=LLED_TRIG_ON;
pLLedDrv->needChange=TRUE;
}
LLed_UnLock();
return result;
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 提供给外部调用的驱动安装函数。主要完成驱动内存申请、驱动
* 控制输出初始化等。
* 参数: tLLedInitParam *pInitParam初始化参数
* scanUnit LED灯扫描时间[10/20/30/40/50],如超出此范围将安装失败
* drvFunc LED灯驱动函数,包括以下部分
* output 安装的LED灯输出控制函数
* init 安装的LED灯底层初始化函数(可以设置为空)
* unInit 安装的LED灯底层去初始化函数(可以设置为空)
* lock 安装的LED灯互斥锁获取函数(可以设置为空)
* unLock 安装的LED灯互斥锁归还函数(可以设置为空)
* 返回: tLLedHandle LED驱动操作句柄;
* 说明: 这是唯一一个直接提供给外部必须调用的函数。
* 另外,如果提供了lock函数,且操作的是操作系统的互斥量,最好将其
* 资源申请放在init中(并提供此函数).资源释放同时放在unInit中.可以
* 达到较好的效果.
---------------------------------------------------------------------------------------------------*/
tLLedHandle* LLed_Initial(tLLedInitParam *pInitParam)
{
bool drvFunc=TRUE;
uint8_t light;
if (pInitParam==NULL||pLLedDrv!=NULL)
return NULL;
for (light=0;light<pInitParam->lightMax;light++)
{
//检查所有LED灯输出函数是否为空
if (pInitParam->drvFunc.output[light]==NULL)
{
drvFunc=FALSE;
break;
}
}
//检查扫描时间
if (pInitParam->scanUnit>0&&(pInitParam->scanUnit<LLED_SCAN_MIN||pInitParam->scanUnit>LLED_SCAN_MAX||(pInitParam->scanUnit%10)!=0))
drvFunc=FALSE;
//检查互斥锁(必须提供成对操作,不能单一为空)
if ((pInitParam->drvFunc.lock!=NULL&&pInitParam->drvFunc.unLock==NULL)||(pInitParam->drvFunc.lock==NULL&&pInitParam->drvFunc.unLock!=NULL))
drvFunc=FALSE;
if (drvFunc==TRUE)//必须提供LED灯输出函数,否则安装失败
{
//申请驱动指针内存
pLLedDrv=MALLOC_MEM(sizeof(tLLedDriver)+sizeof(tLLed)*pInitParam->lightMax+sizeof(tLLedOutputFunc)*pInitParam->lightMax);
pLLedDrv->led=(tLLed*)((uint8_t*)pLLedDrv+sizeof(tLLedDriver));
pLLedDrv->drvFunc.output=(tLLedOutputFunc*)((uint8_t*)pLLedDrv->led+sizeof(tLLed)*pInitParam->lightMax);
pLLedDrv->needChange=FALSE;
pLLedDrv->lightMax=pInitParam->lightMax;
pLLedDrv->drvFunc=pInitParam->drvFunc;
for (light=0;light<pLLedDrv->lightMax;light++)
pLLedDrv->drvFunc.output[light]=pInitParam->drvFunc.output[light];
//初始化轮询扫描时间单位
if (pInitParam->scanUnit==0)
pLLedDrv->scanUnit=LLEDSCAN_UINT_DEF;
else
pLLedDrv->scanUnit=pInitParam->scanUnit;
if (pLLedDrv->drvFunc.lock==NULL)
pLLedDrv->swLock=LLED_UNLOCKED;
//执行硬件底层初始化
if (pLLedDrv->drvFunc.init!=NULL)
pLLedDrv->drvFunc.init();
//初始化输出(默认初始输出熄灭)
for (light=0;light<pLLedDrv->lightMax;light++)
{
//对于闪烁,首先执行熄灭(设置cntDetect=FALSE,则不会对闪烁计数)
pLLedDrv->led[light].run.cntDetect=FALSE;
pLLedDrv->led[light].status=LLED_STATUS_OFF;
pLLedDrv->led[light].mode=LLED_MODE_DARKNESS;
pLLedDrv->led[light].trig=LLED_TRIG_OFF;
pLLedDrv->drvFunc.output[light](LLED_STATUS_OFF);
}
//设置LED灯外部操作控制功能函数及POLL函数指针
pLLedDrv->handle.ctrl=LLedFuncCtrl;
pLLedDrv->handle.poll=LLed_Poll;
return &pLLedDrv->handle;
}
else
{
//安装失败
pLLedDrv=NULL;
return NULL;
}
}
/*--------------------------------------------------------------------------------------------------
* 摘要: 提供给外部调用的驱动卸载函数。主要完成驱动内存释放及底层
* 低级去初始化操作(如提供此函数)。
* 参数: 无
* 返回: TRUE 执行卸载成功
* FALSE执行卸载失败
* 说明: 如果LLed_Lock包含了应用层提供的操作系统互斥量操作,则申请资源
* 最好放在init函数,且释放资源放在unInit函数.
---------------------------------------------------------------------------------------------------*/
bool LLed_UnInitial(void)
{
bool result=TRUE;
LLed_Lock();
if (pLLedDrv!=NULL)
{
//执行低级去初始化操作
if (pLLedDrv->drvFunc.unInit!=NULL)
pLLedDrv->drvFunc.unInit();
FREE_MEM(pLLedDrv);
pLLedDrv=NULL;
}
else
result=FALSE;
LLed_UnLock();
return result;
}
//===========================================================================
/*--END--*/
3)文件代码(测试代码)
/*------------------------------------------说明:------------------------------------------------
此例程用于验证LED驱动,其使用环境如下:
1) MCU使用WZnet芯片W7500;
2) 实时操作系统FreeRTOS V9.0.0;
3) 编译系统使用IAR 8.22.1;
4) 硬件平台没有LED灯,但均使用模拟方式输出(输出调试信息);
5) 除包含""头文件外,还包括一个串口驱动"";
注:因验证比较简单,因此以下功能函数没有做太多详细说明
--------------------------------------------------------------------------------------------------*/
#include ""
#include ""
#include ""
//LED灯定义(此定义属于移植时需要修改的地方)
#define LED_LIGHT_RUN 0 //运行指示灯
#define LED_LIGHT_FAULT 1 //故障指示灯
#define LED_LIGHT_ALARM 2 //告警指示灯
#define LED_LIGHT_COMM 3 //通信指示灯
#define LED_LIGHT_MAX LED_LIGHT_COMM+1 //LED灯个数
typedef struct
{
osMutexId mutex;
tLLedHandle* handle;
}tLed;//应用层中的LED任务资源
static tLed led;
#define LED_POLL_TIME 10//ms led_poll轮询函数执行周期
#define Led_TaskStackDepth (configMINIMAL_STACK_SIZE)
void Led_AttendFlash(uint8_t light);
void Led_AttendFlashInterval(uint8_t light);
//===============================================================
//LED灯初始化与去初始化模块(可以用实际的LED灯驱动I/O设置替代)
//LED灯初始化
static void LedInit(void)
{
Comx_PrintStr(DBG_COM,"\r\nled initialize function\r\n-----------------------------\r\n");
Comx_PrintStr(DBG_COM,"run led initialize...\r\n");
Comx_PrintStr(DBG_COM,"fault led initialize...\r\n");
Comx_PrintStr(DBG_COM,"alarm led initialize...\r\n");
Comx_PrintStr(DBG_COM,"communication led initialize...\r\n");
Comx_PrintStr(DBG_COM,"-----------------------------\r\n");
}
//LED灯去初始化
static void LedUnInit(void)
{
Comx_PrintStr(DBG_COM,"run led uninitialize function...\r\n");
}
//===============================================================
//互斥锁获取与归还模块
//互斥锁获取
static void LedLock(void)
{
osMutexWait(led.mutex,osWaitForever);
}
//互斥锁归还
static void LedUnlock(void)
{
osMutexRelease(led.mutex);
}
//===============================================================
//LED输出控制模块(可以用实际的LED灯驱动I/O输出替代)
//运行指示灯控制输出
static void RunLedOutput(tLLedStatus status)
{
if (status==LLED_STATUS_ON)
Comx_PrintStr(DBG_COM,"run led on\r\n");
else
Comx_PrintStr(DBG_COM,"run led off\r\n");
}
//故障指示灯控制输出
static void FaultLedOutput(tLLedStatus status)
{
if (status==LLED_STATUS_ON)
Comx_PrintStr(DBG_COM,"fault led on\r\n");
else
Comx_PrintStr(DBG_COM,"fault led off\r\n");
}
//告警指示灯控制输出
static void AlarmLedOutput(tLLedStatus status)
{
if (status==LLED_STATUS_ON)
Comx_PrintStr(DBG_COM,"alarm led on\r\n");
else
Comx_PrintStr(DBG_COM,"alarm led off\r\n");
}
//通信指示灯控制输出
static void CommLedOutput(tLLedStatus status)
{
if (status==LLED_STATUS_ON)
Comx_PrintStr(DBG_COM,"communication led on\r\n");
else
Comx_PrintStr(DBG_COM,"communication led off\r\n");
}
//LED设置模块
const tLLedOutputFunc initFunc[LED_LIGHT_MAX]={RunLedOutput,FaultLedOutput,AlarmLedOutput,CommLedOutput};
//LED灯初始化设置
static void LedSetup(void)
{
uint8_t light;
tLLedInitParam initParam;
tLLedOutputFunc output[LED_LIGHT_MAX];
//创建操作系统互斥量资源
led.mutex=osMutexCreate(NULL);
initParam.drvFunc.output=output;
for (light=LED_LIGHT_RUN;light<LED_LIGHT_MAX;light++)
initParam.drvFunc.output[light]=initFunc[light];
initParam.drvFunc.init=LedInit;
initParam.drvFunc.unInit=LedUnInit;
initParam.drvFunc.lock=LedLock;
initParam.drvFunc.unLock=LedUnlock;
initParam.scanUnit=LED_POLL_TIME;
initParam.lightMax=LED_LIGHT_MAX;
led.handle=LLed_Initial(&initParam);
Led_AttendFlash(LED_LIGHT_RUN);
Led_AttendFlash(LED_LIGHT_COMM);
}
//===============================================================
//LED任务模块(FreeRTOS任务)
static void Led_Task(void const *pvParameters)
{
/*-----------------------------------------------------------------------------
这里仅仅是用于演示与测试,所以单独创建一个简单任务
并且仅执行一个简单的延时操作,然后就调用驱动提供的
poll函数.
如果在实际的应用中使用,则不需要单独创建此任务,可通过
软件定时器产生回调时发送消息等通知,由主任务产生调用
-----------------------------------------------------------------------------*/
LedSetup();
for (;;)
{
osDelay(LED_POLL_TIME);
led.handle->poll();
}
}
//===============================================================
//外部调用模块(创建FreeRTOS任务)
void LedTaskCreate(void)
{
osThreadDef(ledlight,Led_Task,osPriorityNormal,0,Led_TaskStackDepth);
osThreadCreate(osThread(ledlight), NULL);
}
//===============================================================
//调测模块(用于调试模块中根据串口输入进行修改)
//因闪烁与间歇性闪烁均用于调测,使用固定的参数
//添加LED灯闪烁操作
void Led_AttendFlash(uint8_t light)
{
tLLedCtrlParam ctrlParam;
tLLedFlashParam flashParam;
ctrlParam.mode=LLED_MODE_FLASH;
flashParam.freq=LLED_FREQ_1Hz;
flashParam.dutyRatio=50;
flashParam.endStatus=LLED_STATUS_OFF;
flashParam.counter=LLED_FLASHKEEP_CNT;
ctrlParam.runParam=&flashParam;
led.handle->ctrl(light,&ctrlParam);
}
//添加LED灯点亮操作
void Led_AttendBright(uint8_t light)
{
tLLedCtrlParam ctrlParam;
ctrlParam.mode=LLED_MODE_BRIGHT;
ctrlParam.runParam=NULL;
led.handle->ctrl(light,&ctrlParam);
}
//添加LED灯熄灭操作
void Led_AttendDarkness(uint8_t light)
{
tLLedCtrlParam ctrlParam;
ctrlParam.mode=LLED_MODE_DARKNESS;
ctrlParam.runParam=NULL;
led.handle->ctrl(light,&ctrlParam);
}
//添加LED灯间歇性闪烁操作(执行3短1长闪烁)
void Led_AttendFlashInterval(uint8_t light)
{
tLLedCtrlParam ctrlParam;
tLLedFlashIntervalParam flashIntervalParam;
ctrlParam.mode=LLED_MODE_FLASH_INTERVAL;
flashIntervalParam.first.freq=LLED_FREQ_5Hz;
flashIntervalParam.first.dutyRatio=50;
//注:实际不会被使用
flashIntervalParam.first.endStatus=LLED_STATUS_OFF;
flashIntervalParam.first.counter=3;
flashIntervalParam.second.freq=LLED_FREQ_1Hz;
flashIntervalParam.second.dutyRatio=50;
//在!=LLED_FLASHKEEP_CNT时会被使用
flashIntervalParam.second.endStatus=LLED_STATUS_OFF;
flashIntervalParam.second.counter=1;
flashIntervalParam.darkTime=1000;
flashIntervalParam.count=LLED_FLASHKEEP_CNT;
ctrlParam.runParam=&flashIntervalParam;
led.handle->ctrl(light,&ctrlParam);
}
//===============================================================
/*--END--*/
五、后续内容
下个分享内容:单片机软件常用设计分享(三)驱动设计之数码屏显示设计
说明
以上设计方法基本为本人在工作中所用,但为了分享,做了一定的整理并进行扩充(已经调测,有需要的请放心使用)。如有错误或BUG处,欢迎指正。
附
如果你感兴趣,可查看其它文档.
单片机软件常用设计分享(一)驱动设计之按键设计
单片机软件常用设计分享(三)驱动设计之数码屏显示设计
单片机软件常用设计分析(四)驱动设计之串口驱动设计
嵌入式开发
嵌入式开发
嵌入式开发
嵌入式开发