对于大多数单片机的学习者或者是从事单片机行业的工程师来说,单片机驱动LED闪烁起来的时候,基本都是我们入坑的开始,同时当时的那种兴奋与喜悦都是难以忘怀的,从LED灯亮起,到闪烁,再到各种流水灯,能开心好几个晚上。

一个LED灯的驱动程序可以说是最简单的,但是随着学习的深入,以及经验的积累,慢慢的会发现,想写一个高效的、普适的、可移植的驱动模块又不是那么容易,每次一般都是根据实际的情况现写一个,当然现写一个也不会花很多时间,但是我们却每次做项目都会要重复着造着这么一个最简单的轮子,因此在这里我想写一个从最原始的到目前我的能力所能达到的最好用的LED驱动模块,当然,我本身也是一个小白,希望能抛砖引玉,欢迎各路大神,提出宝贵意见。

基本实际的项目里面LED的数量不会很多,大多数情况不会超过3个(需要单片机控制的),一般只有两种接法(极个别的时候动态扫描的方式除外):一种是拉电流,一种是灌电流,说白了就是高电平亮还是低电平亮的问题,在这里我们主要以单个的LED驱动的方式来说明。

一般的实际项目当中,LED灯的模式主要是以下几种:

常亮/常灭:这个就没什么好说的了

快闪/慢闪:主要是用于不同的状态模块如:正在联网到网络连接成功

触发反转:主要是用于通信状态的指示,当收到数据的时候通过不停的亮灭来指示灯正在通信。

呼吸模式:主要是用于等待用户操作的一个状态

故障码输出:类似摩斯码的当时通过亮的长短来组成一组故障码


以上几种模式不是绝对的,只是相对的,具体怎么用户不同的工程师不同的产品,具体情况具体分析,只要合乎逻辑就可以了。

现在我想要做就是比较好用且相对来说高效的一个LED驱动模块,先定一个小目标,达到以下功能要求:

高电平低电平驱动都可使用(当然这一条算是废话)

非常容易移植,随便哪个工程只需要修改引脚即可,自己配置模式即可

几乎涵盖了所有项目对于一个指示灯功能的需求,什么模式都可以使用

理论上支持无限多个LED指示灯(局限于内存空间)

C语言编写(曾经使用C++写过,但是当LED比较多的时候,效率会比较低,当然也与自己的编程水平有关,所以在这里还是使用C语言来写),但是使用起来类似面向对象的使用方式。

当然在这里,我不是一上来直接就这个LED驱动程序写出来,既然标题是单片机驱动LED灯的进阶之路,其实我更想通过一个简单的LED的驱动程序的不断演变,来传递一点编程的理念与编程思想,以及我们在编程的过程中所不断摸索出的一些技巧,这些东西的价值可能远大于一个LED灯的驱动程序。

入门阶段:

LED_ON;

Delayms(500);

LED_OFF;

Delayms(500);

当我们在写这种驱动程序的时候,相信思想还是比较符合人类的思考,这时候我们还没有被冠以IT男或者程序猿的标签,找对象还是比较好找的,但是这种写法,可能很难满足实际的项目需求了,或者说如果在项目中使用这种代码,那么用户用你开发的产品一定是很难受的。

初级阶段:

Void led_loop()

{

LED_Count++;

If(LED_Count>10)

LED_Count=0;

Switch(LED_Mode)

{

Case 0: //灭

LED_OFF;

Break;

Case 1: //亮

LED_ON;

Break;

Case 2: //闪烁

If(LED_Count<2)LED_ON;

Else LED_OFF;

Break;

}

}

当这样来写LED驱动程序的时候,相信基本以及能做很多简单的单片机产品了,甚至很多时候已经完全通过这种方式来满足我们很多的产品需求,这时候我们对单片机也比较了解了,重要的是我们知道了周期与刷新的概念,知道每隔多长时间刷新一次,一个控制周期是多少,也可以通过这种方式来实现多种多样的模式,甚至很多时候,我们会停留在这中方式很久,哪怕是后面,我们可以通过PWM,定时器等方式组合出呼吸灯、闪烁呀,也可以把每个变量变成数组的方式来驱动多个指示灯等等,但是我们的代码依然没法满足模块化的方式。

中级阶段

Typedef struct

{

Char mode;

Char status;

Int count;

Void (*led_set)(char );

}LedOpt_t;

Void led_register(LedOpt_t *led);

Void led_loop(LedOpt_t *led);

这时候,我们已经对与结构体、函数指针、面向对象等已经比较熟悉了,而且我相信,我们也已经做过好多个不大不小的项目了,其实这时候,只要我们善于发现与总结,善于学习更多大神的宝贵经验我们完全有能力写出一套更加实用的驱动程序来,其实这时候比较麻烦的是当有多个指示灯的时候,我们难免还是需要修改一点代码,这让我觉得很不爽,所以还是想写一个驱动部分不想修改的驱动模块。

高级阶段

typedef struct iLed_t

{

uint8_t state; //信号灯当前的状态

uint16_t *mode; //用于指明当前参照闪烁数组中的第几个成员

uint16_t modeLen; //每种模式的长度

uint16_t tickCnt; //tick计数

uint16_t modeIndex; //一个周期内的闪烁次数

int32_t times; //执行次数

void (*on)(void); //led亮

void (*off)(void); //led灭

void (*overCallBack)(void); //led闪烁结束回调函数

struct iLed_t *next; //下一个led灯

} iLed_t;

这个时候,我们使用到了c语言的链表功能,通过一个链表,来实现多个led灯查找,而不用在使用for(i=0;i

比如:

static iLed_t Led1;

static iLed_t Led2;

static iLed_t Led3;

const uint16_t LED_MODE_1[2]={500,500}; //模式1:亮500 灭500

const uint16_t LED_MODE_2[4]={200,200,100,800};//模式2:亮200,灭200,亮100,灭800

const uint16_t LED_MODE_3[4]={200,700,100,800,200,600};//模式3:亮200,灭700,亮100,灭800,亮200,灭600

我们可以根据我们的实际使用需求来随意定义自己的模式即可

led_creat(&Led1,led_1_on, led_1_off);

led_creat(&Led2,led_2_on, led_2_off);

led_creat(&Led3,led_3_on, led_3_off);

led_set_mode(&Led1,mode,modeLen,times);

Mode:选择模式如 LED_MODE_1

modeLen:当前所选模式的所占空间大小:sizeof(LED_MODE_1),

Times:当前模式执行次数

这样的一个LED驱动程序基本可以实现我们对一个LED指示灯近乎变态的需求,呼吸灯呀,故障码呀什么的都是可以的,另外其实不建议通过这种方式驱动呼吸灯,最好还是使用硬件PWM的方式,当然这个驱动程序也是支持的,下面我就我相对完成的代码全部写出来,另外里面有些思想也是参考了晚上很多大拿的代码,具体我也记不清是谁的了,反正是当时搜索了很多代码,如果有雷同,也是有可能的。

#ifndef _ILED_H_

#define _ILED_H_

#include "stdint.h"

#include "string.h"

#define LED_TICK_TIME 50 //心跳函数调用的时间间隔(单位:ms)

typedef struct iLed_t

{

uint8_t state; //信号灯当前的状态

uint16_t *mode; //用于指明当前参照闪烁数组中的第几个成员

uint16_t modeLen; //

uint16_t tickCnt; //

uint16_t modeIndex; //一个周期内的闪烁次数

int32_t times; //执行次数

void (*on)(void);

void (*off)(void);

void (*overCallBack)(void); //led闪烁结束回调函数

struct iLed_t *next;

} iLed_t;

void led_init(iLed_t *handle,void (*on)(void), void (*off)(void));

void led_set_mode(iLed_t *handle,uint16_t *mode,uint16_t modeLen,int16_t times);

int8_t led_start(iLed_t *handle);

void led_stop(iLed_t *handle);

void led_switch(iLed_t *handle,char state);

void led_toggle(iLed_t *handle);

int8_t led_creat(iLed_t *handle,void (*on)(void), void (*off)(void));

void led_reg_call_back(iLed_t *handle,void (*overCallBack)(void));

void led_tick_loop(void);

void led_loop(void);

void led_tick();

#endif

---------------------------------分割线----------------------------------------------------------------

#include "iLed.h"

#include "stddef.h"

static struct iLed_t * head_led = NULL;

static uint32_t _led_ticks = 0;

//初始化,主要是注册led

void led_init(iLed_t *handle,void (*on)(void), void (*off)(void))

{

memset(handle, 0, sizeof(struct iLed_t));

handle->on=on;

handle->off=off;

handle->modeIndex=0;

handle->modeLen=0;

handle->tickCnt=0;

handle->state=0;

handle->off();

}

//执行完成回调,可以不使用,当一个led灯执行完成之后会调用此函数

void led_reg_call_back(iLed_t *handle,void (*overCallBack)(void))

{

handle->overCallBack=overCallBack;

}

//启动led灯

int8_t led_start(iLed_t *handle)

{

struct iLed_t * target = head_led;

while(target)

{

if(target == handle) return -1;

target = target->next;

}

handle->next = head_led;

head_led = handle;

return 0;

}

//创建led,注册并启动

int8_t led_creat(iLed_t *handle,void (*on)(void), void (*off)(void))

{

led_init(handle,on,off);

return led_start(handle);

}

//设置led模式

void led_set_mode(iLed_t *handle,uint16_t *mode,uint16_t modeLen,int16_t times)

{

struct iLed_t * target = head_led;

while(target)

{

if(target == handle)

{

target->mode=mode;

target->times=times;

target->modeLen=modeLen/sizeof(mode[0]);

break;

}

else

{

target = target->next;

}

}

}

//led灯停止

void led_stop(iLed_t *handle)

{

struct iLed_t** curr;

for(curr = &head_led; *curr; )

{

struct iLed_t* entry = *curr;

if (entry == handle)

{

*curr = entry->next;

}

else

curr = &entry->next;

}

}

//led开关

void led_switch(iLed_t *handle,char state)

{

switch(state)

{

case 1:

handle->on();

handle->state=state;

break;

case 0:

handle->off();

handle->state=state;

break;

default:

break;

}

}

//触发反转

void led_toggle(iLed_t *handle)

{

handle->state=!handle->state;

led_switch(handle,handle->state);

}

//led主要的控制逻辑

void led_handle(iLed_t *handle)

{

if((handle->times!=0)&&(handle->modeLen>0))

{

handle->tickCnt++;

if((handle->tickCnt*LED_TICK_TIME)<=(handle->mode[handle->modeIndex]))

{

led_switch(handle,!(handle->modeIndex%2));

}

else

{

handle->tickCnt=0;

handle->modeIndex++;

if((handle->modeIndex)>=(handle->modeLen))

{

handle->modeIndex=0;

if(handle->times>0)

{

handle->times--;

if(handle->times==0)

{

if(handle->overCallBack!=NULL)

{

handle->overCallBack();

}

}

}

}

}

}

}

/*************************使用方法1***********************************/

//可以放在系统循环中

void led_loop(void)

{

struct iLed_t* target;

if(_led_ticks>=LED_TICK_TIME)

{

for(target=head_led; target; target=target->next) //通过链表查询

{

led_handle(target);

}

_led_ticks=0;

}

}

//系统滴答定时器中1ms执行一次

void led_tick()

{

_led_ticks++;

}

/*************************使用方法2***********************************/

//定时调用该函数 调用周期为 LED_TICK_TIME

void led_tick_loop(void)

{

struct iLed_t* target;

for(target=head_led; target; target=target->next) //通过链表查询

{

led_handle(target);

}

}

控制led闪烁次数_单片机驱动LED灯的进阶之路相关推荐

  1. 单片机led闪烁代码_单片机驱动LED发光二极管的电路以及编程

    一.单片机驱动单个发光二极管 1.电路 代码: 1.点亮单个LED二极管 #include<reg51.h> sbit LED1=P1^0:void main(void){LED1=1:w ...

  2. 单片机led闪烁代码_单片机、555实现LED闪烁电路

    有朋友在后台发消息,希望设计一个LED闪烁电路,闪烁频率为2Hz,想了解LED闪烁的电路原理是什么,下面解答一下,看看有没有写的很通俗易懂.要设计一个闪烁电路,闪烁频率为2Hz,即500ms亮灭交替. ...

  3. LED数码管静态显示 C语言程序,PIC单片机驱动LED数码管显示程序

    ;*****该程序用于驱动led数码管显示,在8个LED数码管上依次显示数字1.2.3.4.5.6.7.8******* ;****http://www.51hei.com 单片机学习网经典程序已测试 ...

  4. c语言控制led闪烁次数,单片机C语言程序设计之定时器控制4个LED滚动闪烁

    描述 通过单片机课程设计,熟练掌握C语言的编程方法,将理论联系到实践中去,提高我们的动脑和动手的能力.通过定时器控制4只LED滚动闪烁系统的设计,掌握定时/计数器的使用方法,和简单程序的编写,最终提高 ...

  5. led灯串怎么摆造型_如何驱动LED灯串小绝招

    也许有些人知道如何驱动LED灯串,可能就是采用大多数人都认同的一种大众化方法,但其实在这种大众化方法的背后其实还有许多人不知道的小绝招.今天小编就带你从其他地方入手更好的驱动led灯串. 在机械和电气 ...

  6. 光敏电阻控制led亮度程序_单片机开发系统学习LED亮度控制原理

    早期控制LED亮度的方法一般是采用模拟电路来调节LED的工作电流来实现,这种方式灵活性较差,很难达到智能控制的效果.本文将介绍如何通过一个普通MCS51单片机来产生PWM信号,达到调节LED的亮度的目 ...

  7. 51单片机led点阵C语言,51单片机驱动LED点阵扫描显示C语言程序

    #ifndef__Matrix_H__ #define__Matrix_H__ #ifdef__cplusplus extern"C" { #endif #define SET  ...

  8. 单片机旋转led程序c语言,[ 单片机 ] 旋转LED制做过程

    8.我采用的是USB烧写器,烧写的程序 9.我用的电源是一个LM317可调稳压电源 10.通电试验 需要注意的是要安排好电机轴两边的电路重量尽量相近. 2.程序 程序很简单,我这里给出一个C51 的示 ...

  9. 机械臂控制C语言程序,51单片机的6自由度机械臂 16路舵机控制 源码

    /*************************************************************************************************** ...

最新文章

  1. Java设计模式:观察者模式
  2. 网站性能优化思维导图
  3. namespace nvinfer1
  4. Apache 2,4版本 编译与安装 RedHat enterprises 6.2
  5. 重庆计算机学校电话号码,重庆计算机学校
  6. c和python的区别动图_C语言与Python 对程序员的差别到底在哪?
  7. C中的malloc:C中的动态内存分配
  8. 信息学奥赛一本通(1154:亲和数)
  9. 初步使用计算机学设计,幼儿园计算机教学设计参考
  10. 20172315 2018-2019-1《程序设计与数据结构》课程总结
  11. java开发异常Exception集锦
  12. Spring 的 IOC原理
  13. Python网络爬虫模块介绍:fake-useragent模块快速生成User-Agent信息
  14. Android Studio模拟器安装步骤
  15. 计算机网络一般包括资源子网和什么两部分,一个计算机网络一般包括什么和通信子网两部分...
  16. HTML学生个人网站作业设计:电影网站设计——橙色国外电影(13页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载
  17. 《宝岛双雄》曝正式海报 房祖名挑大梁的银幕转型之作
  18. tensorflow的slim调用预训练模型的权重进行迁移学习的一些感触
  19. 高职计算机自主招生面试题,高职自主招生面试题
  20. net软件工程师求职简历

热门文章

  1. Simple2D-22(重构)纹理池
  2. 使用JavaScript判断用户是否为手机设备
  3. unix网络编程之简介和运输层TCP/UDP
  4. NNIE-lite 为算法工程师而生
  5. Elasticsearch一些常用操作和一些基础概念
  6. Data-mediator入门系列2
  7. 《逻辑与计算机设计基础(原书第5版)》——1.7 格雷码
  8. LinkedList 链表总结
  9. Thrift编译错误解决方法
  10. 谷歌浏览器chrome假死、卡死、经常无反应,火狐firefox闪黑格子的解决办法(显卡/驱动兼容问题)...