历史上的今天
今天是: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位整数类型,定义在 我们用的单片机是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 还有一个你可能疑惑的地方是,流水灯的逻辑本应是关闭上一个灯,打开下一个灯,然后延时一段时间,为什么在程序中是打开、延时,再关闭?想一想你会发现没什么不对,不过是把循环体的界限移动了一步。 实际上,如果按前一种逻辑来写程序的话,循环体得写成这样: 1 led_set((i + 3) % 4, false); 2 led_set(i % 4, true); 3 delay(250); 这样虽然逻辑也正确,但反而更难理解。 你也许还有疑问:把上一个灯关闭了以后,程序还要运行一会才能打开下一个灯,会不会看起来有一段时间是没有灯亮的?观察一下你的开发板,你就知道不会,原因也很简单,关灯和开灯之间的时间是微秒级的,人眼根本无法察觉。 除了上述实现,流水灯还有一种效果,就是灯依次亮起,再依次关闭。
下一篇:AVR单片机教程——闪烁LED
史海拾趣
|
我在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注册表保存方式的时候。。在系统进入到定位程序的时候就不动了。点屏幕定位针不动。。按esc也不行。。。但是点屏幕的时候在pb的debug中可以看到点的坐标。。。。按道理说系统应该是卡死了才对。。可是为什么那里还可以打印出来坐标了? ...… 查看全部问答> |
|
以全彩屏为例,通常红、绿、蓝白平衡配比为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月02日~2012年8月05日 2012年北京国际应急救灾设备技术展览会展会日期:2012年8月29日~2012年8月31日展会场馆:全国农业展览馆 2012北京国际社会公共安全及司法监狱 ...… 查看全部问答> |
|
这件“神物”对我这个懒人来说肯定是特别的有用处!当然,如果你记性不好的话也是很有帮助的!它是一款锂电池的充电器!但它却拥有烤面包机一样的处理方式与漂亮的外观! 当你把要充的电板从上至下放入之 ...… 查看全部问答> |
|
摘要:为解决不同性能指标数据采集系统开发时间较长的问题,提出了一种将FPGA软核技术应用于高速数据采集系统设计的方法。系统以Xilinx公司的FPGA为例设计软核,使用VHDL语言对软核进行模块化设计。介绍了数据采集系统的硬件电路、USB固件程序、USB ...… 查看全部问答> |




