历史上的今天
返回首页

历史上的今天

今天是:2024年12月03日(星期二)

正在发生

2019年12月03日 | AVR单片机教程——流水灯

2019-12-03 来源:eefocus

上次我们用 delay 函数与 while 循环实现了一个LED的闪烁。这一次我们把所有LED加入进来,让它们依次闪烁,形成流水灯的效果。


开发板上有4个LED,我们可以用不多的语句把循环体直接描述出来(看看就行,不用敲):


 1 led_set(LED_RED   , LED_ON);

 2 delay(250);

 3 led_set(LED_RED   , LED_OFF);

 4 led_set(LED_YELLOW, LED_ON);

 5 delay(250);

 6 led_set(LED_YELLOW, LED_OFF);

 7 led_set(LED_GREEN , LED_ON);

 8 delay(250);

 9 led_set(LED_GREEN , LED_OFF);

10 led_set(LED_BLUE  , LED_ON);

11 delay(250);

12 led_set(LED_BLUE  , LED_OFF);


但是如果LED多了怎么办?这样写一点都不优雅。一个更合理的方法是,用数组把LED存储起来,在无限循环中套循环,对LED进行遍历。代码如下:


 1 #include

 2 #include

 3 

 4 int main()

 5 {

 6     led_init();

 7     const uint8_t leds[LED_COUNT] = {LED_RED, LED_YELLOW, LED_GREEN, LED_BLUE};

 8     while (1)

 9     {

10         for (uint8_t i = 0; i < LED_COUNT; i++)

11         {

12             led_set(leds[i], LED_ON);

13             delay(250);

14             led_set(leds[i], LED_OFF);

15         }

16     }

17 }


在代码中,uint8_t 是一种无符号8位整数类型,定义在 中。相应地有 int8_t 表示带符号8位整数,以及 int16_t 、uint16_t 等,一直到64位。avr-gcc还提供了24位整型作为编译器扩展:__int24、 __uint24 ,但它们毕竟是编译器扩展,尽量别用;同时如果你的单片机程序中有16位整数搞不定的东西,那就应该考虑简化一下了。


我们用的单片机是8位机,指令只能处理8位整数,如果是16位,则需要多条指令进行组合。而C语言内置类型 int 在这个环境中是16位的。因此,为了节省空间、提升性能,当一个数可以用8位表示时,应该使用 int8_t 或 uint8_t 代替 int 。


讲了这么多,为什么LED可以用 uint8_t 表示呢?把光标移动到任一表示LED的宏上,右键,点击Goto Implementation,或按下快捷键Alt+G,你就能看到头文件中对这些宏的定义:


1 #define LED_RED    (uint8_t)(0)

2 #define LED_YELLOW (uint8_t)(1)

3 #define LED_GREEN  (uint8_t)(2)

4 #define LED_BLUE   (uint8_t)(3)


同时,led_set 等函数都接受 uint8_t 类型的参数表示LED:


1 void led_set(uint8_t _which, bool _on);

2 void led_flip(uint8_t _which);

这些都能说明为什么用 uint8_t 就能保存一个表示LED的宏。至于我为什么选择用 uint8_t ,当然是因为刚才说过的性能因素。


顺便,我们可以发现表示LED状态的宏本质上就是 true 和 false ,表示LED的宏就是简单的0、1、2、3,因此程序中可以不用数组来存储LED,也不再需要任何宏,并且 true 和 false 也很容易对应到灯的亮和暗上:


 1 #include

 2 

 3 int main(void)

 4 {

 5     led_init();

 6     uint8_t i = 0;

 7     while (1)

 8     {

 9         led_set(i % 4, true);

10         delay(250);

11         led_set(i % 4, false);

12         i++;

13     }

14 }


第一行包含了头文件 ee.h ,相当于包含了库中所有的头文件。


这样的程序是不是更好看一些?实际上 LED_RED 等宏的作用都只有让程序变得直观,如果直接用数字表示更方便,也完全没有问题。


这里还用到一个小技巧来消除内层循环:在循环外设一个变量,每次循环让它加1,传入的参数由它本身改为它对4的模。


你可能会想如果 i 过大了怎么办?当 i 等于255时, i++ 会让 i 变为0,而此时传入函数的参数还是从3回到0,符合控制逻辑。


为什么程序没有 #include 就能直接使用 true 和 false ?因为你包含的头文件中用到了它们,这个头文件必定或直接或间接地包含了标准库头文件,因此你就可以直接用了。


还有一个你可能疑惑的地方是,流水灯的逻辑本应是关闭上一个灯,打开下一个灯,然后延时一段时间,为什么在程序中是打开、延时,再关闭?想一想你会发现没什么不对,不过是把循环体的界限移动了一步。


实际上,如果按前一种逻辑来写程序的话,循环体得写成这样:


1 led_set((i + 3) % 4, false);

2 led_set(i % 4, true);

3 delay(250);

这样虽然逻辑也正确,但反而更难理解。


你也许还有疑问:把上一个灯关闭了以后,程序还要运行一会才能打开下一个灯,会不会看起来有一段时间是没有灯亮的?观察一下你的开发板,你就知道不会,原因也很简单,关灯和开灯之间的时间是微秒级的,人眼根本无法察觉。


除了上述实现,流水灯还有一种效果,就是灯依次亮起,再依次关闭。

推荐阅读

史海拾趣

EMC Technology RF Labs公司的发展小趣事

随着对射频技术的深入研究,RF Labs在多个领域取得了技术突破。他们成功开发了一系列高性能的射频电阻、衰减器、定向耦合器等关键元件,这些产品不仅具有优异的性能,而且能够满足各种复杂的应用场景需求。同时,RF Labs还不断推出创新产品,如智能探测器温度测量端子等,这些产品为客户提供了更加便捷和高效的解决方案。

Amveco Toroidal Power Products公司的发展小趣事

Amveco Toroidal Power Products公司成立于XXXX年,初始阶段主要专注于环形变压器的研发与生产。创始人凭借对环形变压器技术的深入了解和市场需求的敏锐洞察,带领团队克服了初创期的种种困难,成功开发出了具有高效能、低噪音特点的环形变压器产品,并赢得了客户的初步认可。随着市场口碑的逐渐积累,公司的业务规模开始稳步扩大。

ENSIGN公司的发展小趣事

ENSIGN不仅关注传统电子行业的发展,还积极探索新的业务领域。最近,ENSIGN集团宣布收购位于亚利桑那州的医疗保健园区River Park Post Acute和Elmwood Senior Living,以及其他七家公司。这一收购标志着ENSIGN正逐步向医疗保健领域进军,寻求多元化的发展机会。

Elektron公司的发展小趣事

随着环保意识的不断提高和可持续发展理念的普及,Elektron公司开始注重环保和可持续发展方面的工作。公司积极采用环保材料和节能技术,降低产品制造过程中的能耗和排放。同时,Elektron还加强了对废弃物的处理和回收工作,努力实现资源的循环利用和环境的可持续发展。这些措施不仅有助于公司降低成本和提高效率,也符合社会和消费者对环保和可持续发展的期望和要求。

以上五个故事分别从不同角度概述了Elektron公司的发展过程,包括创立与初期发展、技术创新与产品多样化、全球扩张与市场领导地位、技术突破与行业引领以及环保理念与可持续发展等方面。这些故事共同构成了Elektron公司丰富多彩的发展历程和辉煌成就。

Graseby Infrared公司的发展小趣事
按下反转启动按钮(如SB2),首先KM1的线圈失电,KM1主触头断开,电动机停止。随后,KM2的线圈得电,KM2主触头闭合,电动机按L3-L2-L1的相序接通电源,实现反转。同时,KM2的常开辅助触头闭合自锁。
Bud Industries公司的发展小趣事
按下正转启动按钮(如SB1),接触器KM1的线圈得电,KM1主触头闭合,电动机按L1-L2-L3的相序接通电源,实现正转。同时,KM1的常开辅助触头闭合自锁,保持电动机持续运转。

问答坊 | AI 解惑

wince 通过CF卡里添加阿拉伯字体

我在project.reg 里添加了如下内容 [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\FontPath]     \"FontPath\"=\"\\\\Hard Disk\\\\Font\"  ; 字库文件所在目录 [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\FontLink\\SystemL ...…

查看全部问答>

【问题还在,俄第神纳!】

本以为解决了,结果还不行! 目前的情况: 计算机发信号给LED显示屏,中间用433无线模块传输。发送端通过232转485接无线模块,接收端模块通过485接显示屏。正常。但因为433模块不好用,所以客户想用我们的超级无线传输模块。但是,他要求,用我们 ...…

查看全部问答>

关于HIVE注册表实现中的一些问题。。。

我在使用hive注册表保存方式的时候。。在系统进入到定位程序的时候就不动了。点屏幕定位针不动。。按esc也不行。。。但是点屏幕的时候在pb的debug中可以看到点的坐标。。。。按道理说系统应该是卡死了才对。。可是为什么那里还可以打印出来坐标了? ...…

查看全部问答>

关于水表奇怪的现象

开阀或关阀时,完全打开或关闭的一瞬间显示屏就很暗了,但是能看到一点屏还是按照要求在显示的,只是很暗!这是怎么回事,干扰吗?…

查看全部问答>

LED显示屏亮度计算方法

以全彩屏为例,通常红、绿、蓝白平衡配比为3:4:1 红色LED 灯亮度:亮度(CD)/M2÷点数/M2×0.3(白平衡配比占30%)÷2 绿色LED 灯亮度:亮度(CD)/M2÷点数/M2×0.6(白平衡配比占60%) 蓝色LED 灯亮度:亮度(CD)/M2÷点数/M2×0.1(白平衡 ...…

查看全部问答>

【设计工具】利用 Xilinx FPGA 打造出高端比特精度和周期精度浮点 DSP 算法实现方案

       系统要求和可用 FPGA 的当前趋势使浮点实现方法变得更加重用。本白皮书和视频说明了(与其它设计流程的不同之处)DSP 系统生成器 13.3 如何为用户提供针对比特精度、周期精度、单精度、双精度和定制精度浮点 ...…

查看全部问答>

2012年8月份北京举办的一些重要行业展会和研讨会

  数字世界亚洲博览会展会场馆:北京国家会议中心举办时间:2012年8月02日~2012年8月05日 2012年北京国际应急救灾设备技术展览会展会日期:2012年8月29日~2012年8月31日展会场馆:全国农业展览馆 2012北京国际社会公共安全及司法监狱 ...…

查看全部问答>

烤面包机般的充电器 电满后电池自动弹出

        这件“神物”对我这个懒人来说肯定是特别的有用处!当然,如果你记性不好的话也是很有帮助的!它是一款锂电池的充电器!但它却拥有烤面包机一样的处理方式与漂亮的外观!  当你把要充的电板从上至下放入之 ...…

查看全部问答>

基于赛灵思FPGA软核的高速数据采集系统设计

摘要:为解决不同性能指标数据采集系统开发时间较长的问题,提出了一种将FPGA软核技术应用于高速数据采集系统设计的方法。系统以Xilinx公司的FPGA为例设计软核,使用VHDL语言对软核进行模块化设计。介绍了数据采集系统的硬件电路、USB固件程序、USB ...…

查看全部问答>