geren2014 发表于 2019-11-5 09:46:21

Arduino 一颗灯点到极致

在上一期的“教你花式点灯”中,我们已经教会了大家如何将一颗灯点到极致。而点灯到这里还没有完哦,这次给大家来点进阶的小课程,来教教大家如何花式点亮一排灯。快快上车,老司机准备起飞了!
最简单的一个 LED 的实验之后,自然是增加几个 LED,咱排成一排来玩吧。最后,再把一排的 LED 排成一个 8 字来玩——七段数码管。流水灯一 —— 跑马灯第一个出场的肯定是经典的流水灯,也叫跑马灯
流水灯二 —— 端口整体操作再试流水灯,这里换一个端口整体操作,感受到更简洁没?
流水灯三 —— 74HC959 扩展 IO 口又是流水灯!这次使用 74HC595 来扩展 IO 口。对于 arduino 来说,IO 口资源是很紧张的,迟早会遇到不够用的时候,这时候就需要扩展。使用 74HC595 是一种比较常见的方案。


雨滴拖尾效果流水灯不好意思,还是流水灯。这次模拟得更真实一点,加上水滴的拖尾效果。
首先是简单的方法,使用 uno 的六个 pwm 口来实现。1. // ----------------------------------------------------------------------------2. // raindropLEDS.ino3. // 4. // Created 2015-06-045. // By seesea <seesea2517#gmail#com> 6. // 7. // 雨滴流动效果8. // 雨滴流动效果与流水灯(跑马灯)的区别在于雨滴流水效果有拖尾效果,即亮过的灯是慢慢熄灭的9. //10. // 使用 UNO 的六个 PWM 引脚实现雨滴流动的效果11. // UNO 的六个 PWM 引脚为 pin 3 5 6 9 10 11,这几个脚分别接 6 个 LED 正极,LED 负极接 GND12. // 13. // ----------------------------------------------------------------------------14. 15. const unsigned char pins[] = { 3, 5, 6, 9, 10, 11 };            // 六个 pwm 引脚16. const unsigned char initPwm= 240;         // 最亮的灯的 pwm 值,即移动的时候在最头一个灯的亮度17. const unsigned char deltaPwm = 10;          // 灯慢慢熄灭的 pwm 值,后一个灯比前一个灯暗多少。这相当于是一个等差队列。等差的亮度感觉不大好,所以引入下一个等比的因素18. const unsigned char deltaPercent = 30;      // 后一个灯比前一个灯暗,其亮度是前一个灯的百分之几。相对于前面的递减,这个相当于是等比级数19. const unsigned long delayMs= 100;         // 移动延迟,单位 ms20. const unsigned char pinNum   = sizeof(pins) / sizeof(pins);// 引脚数量,即 LED 个数21. 22. unsigned char ledPwm; // 存放运行时每一个 LED 的亮度 PWM 值23. 24. void setup()25. {26.   for (char i = 0; i < pinNum; ++i)27.   {28.         pinMode(pins, OUTPUT);29.         ledPwm = 0;30.   }31. }32. 33. void loop()34. {35.   static unsigned char head = 0;36.   37.   // 每一次进入 loop() 函数都对所有的灯亮度进行处理38.   for (unsigned char i = 0; i < pinNum; ++i)39.   {40.         ledPwm = ledPwm * deltaPercent / 100;41.         if (ledPwm <= deltaPwm)42.             ledPwm = 0;43.         else44.             ledPwm -= deltaPwm;45.         46.         if (i == head)47.             ledPwm = initPwm;48. 49.         analogWrite(pins, ledPwm);50.   }51. 52.   // 移动水滴头53.   head = (head + 1) % pinNum;54.   55.   // 延时56.   delay(delayMs);57. }https://mmbiz.qpic.cn/mmbiz_gif/XZMicpgcYibXFbiaVfVEQO5wU28QFyyHF90wmVy6CazNkLMoRX1iavEU4X3GTzt1UMdS76yEfCKlrXXyTfOGicPUmmg/640?wx_fmt=gif

雨滴拖尾效果流水灯二——数字 IO 口模拟前面只有 6 个 PWM 口实在是不过瘾啊,来,我们把所有的 IO 口都用上,包括模拟口,它也是可以作为数字 IO 口使用的。1. // ----------------------------------------------------------------------------2. // digitalRaindropLEDS.ino3. //4. // Created 2015-06-045. // By seesea <seesea2517#gmail#com>6. //7. // 数字引脚实现的雨滴流动效果8. // 雨滴流动效果与流水灯(跑马灯)的区别在于雨滴流水效果有拖尾效果,即亮过的灯是慢慢熄灭的9. //10. // 使用 UNO 的所有引脚用模拟 PWM 实现雨滴流动的效果,包括模拟输入口也可以用做数字输出11. // 各引脚接 LED 正极,LED 负极接 GND12. // ----------------------------------------------------------------------------13. 14. const unsigned char leds[] = { A5, A4, A3, A2, A1, A0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; // 所有的引脚按 LED 接线顺序排列15. const unsigned int maxPwm       = 100;    // 手工模拟 PWM,可以自己定义最大的 PWM 值是多少,所以定义一个整百整千的数比较方便计算16. const unsigned int initPwm      = 100;    // 最亮的灯的 pwm 值,即移动的时候在最头一个灯的亮度17. const unsigned int deltaPwm   = 1;      // 灯慢慢熄灭的 pwm 值,后一个灯比前一个灯暗多少。这相当于是一个等差队列。等差的亮度感觉不大好,所以引入下一个等比的因素18. const unsigned int deltaPercent = 70;   // 后一个灯比前一个灯暗,其亮度是前一个灯的百分之几。相对于前面的递减,这个相当于是等比级数19. const unsigned long delayMs   = 70;   // 移动延迟,单位 ms20. const unsigned char ledNum      = sizeof(leds) / sizeof(leds);// 引脚数量,即 LED 个数21. 22. unsigned int ledPwm; // 存放运行时每一个 LED 的亮度 PWM 值23. 24. void setup()25. {26.   for (char i = 0; i < ledNum; ++i)27.   {28.         pinMode(leds, OUTPUT);29.         ledPwm = 0;30.   }31. }32. 33. extern volatile unsigned long timer0_millis;    // 声明外部变量 timer0_millis 以便在程序中使用,其实就是 millis() 的返回值——程序运行的毫秒数34. void loop()35. {36.   static unsigned char head = 0;37.   static unsigned long lastTick = timer0_millis;38.   unsigned int i, j;39. 40.   // 先亮灯,等占空比到切换点的时候灭灯41.   for (i = 0; i < ledNum; ++i)42.   {43.         if (ledPwm == 0)44.             continue;45. 46.         digitalWrite(leds, HIGH);47.   }48. 49.   // 水滴头是最亮的50.   ledPwm = initPwm;51. 52.   // 这里就是数字口模拟的 PWM 程序了53.   for (i = 0; i < maxPwm; ++i)54.   {55.         for (j = 0; j < ledNum; ++j)56.         {57.             if (i == ledPwm)58.               digitalWrite(leds, LOW);59.         }60. 61.         delayMicroseconds(1);62.   }63. 64.   // 如果延时时间还没到,先跳出,不进行水滴的移动65.   // 由于是用数字口模拟的 PWM,程序要不停的跑,不能使用 delay() 来延时,会卡住的66.   if (timer0_millis - lastTick < delayMs)67.         return;68. 69.   lastTick = timer0_millis;70. 71.   // 处理每一个灯的亮度72.   for (i = 0; i < ledNum; ++i)73.   {74.         ledPwm = ledPwm * deltaPercent / 100;75.         if (ledPwm <= deltaPwm)76.             ledPwm = 0;77.         else78.             ledPwm -= deltaPwm;79. 80.         if (i == head)81.             ledPwm = initPwm;82.   }83. 84.   // 移动水滴头85.   head = (head + 1) % ledNum;86. }https://mmbiz.qpic.cn/mmbiz_gif/XZMicpgcYibXFbiaVfVEQO5wU28QFyyHF90J7HIcPMy2OqCYibibA8vKRGBCclzRo37yyuXxl5uYMFDvr3UP5atpH8g/640?wx_fmt=gif
POV摇摇棒是时候舞起来了,摇起来吧。1. // ----------------------------------------------------------------------------2. // povLEDS.ino3. //4. // Created 2015-05-315. // By seesea <seesea2517#gmail#com>6. //7. // 摇摇棒8. //9. // pin 0 - 7 接 LED 正极,LED 负极接 GND10. // pin 8 接水银开关一脚,水银开关另一脚接 GND11. // 水银开关水平于摇动方向固定放置。如果实际效果与预想效果水平相反,只要把水银开关两脚的接线互换就可以了12. // 注意:由于 pin 0 1 通过 LED 接地了,下载的时候需要断开才能下载成功13. // ----------------------------------------------------------------------------14. 15. const unsigned char key = 8;            // 水银开关16. const unsigned char delayTimeMs = 2;    // 扫描时LED的点亮延时17. 18. // 取模软件使用纵向8点上高位的方式取模19. unsigned char col[] =20. {21.   // 心22.   // 0x30,0x48,0x44,0x22,0x44,0x48,0x30,0x0023. 24.   // I 心 U25.   0x82,0xFE,0xFE,0x82,0x00,0x30,0x78,0x7C,26.   0x3E,0x7C,0x78,0x30,0x00,0xFC,0xFE,0x02,27.   0x02,0xFE,0xFC,0x00,0x00,0x0028. 29.   /* 1 2 330.   0x00,0x42,0xFE,0x02,0x00,0x00,0x00,0x00,31.   0x46,0x8A,0x92,0x92,0x62,0x00,0x00,0x00,32.   0x84,0x82,0x92,0xB2,0xCC,0x0033.   */34. };35. 36. unsigned char cols = (sizeof(col) / sizeof(col));37. 38. void setup()39. {40.   DDRD = 0xFF;41.   pinMode(key, INPUT_PULLUP);42. }43. 44. void loop()45. {46.   char i = 0;47.   char keyValue = digitalRead(key);48.   char delta = 1;49. 50.   if (keyValue == 1) // 摇动方向由右向左的时候51.   {52.         // 这里可以尝试两种方案53.         // 一种是向左扫的时候熄灭所有灯54.         // 另一种是反向点亮各列,从而由于方向也是反的,显示就变回正的55.         // 根据需要,把 if 条件改成 0 或 1 可以选择不同的方案56.         // 第一种方案的优点是不容易有重影,但缺点是亮度低57.         // 第二种方案的优缺正好相反58.         if (0)59.         {60.             // 向左摇熄灭所有灯61.             i = 0;62.             PORTD = 0x00;63. 64.             // 如果选择这种方案就不需要继续下面的扫描了,return掉即可65.             return;66.         }67.         else68.         {69.             delta = -delta;70.         }71.   }72. 73.   if (delta == 1)74.   {75.         // 向右摇的时候76.         for (i = 0; i < cols; ++i)77.         {78.             PORTD = col;79.             delay(delayTimeMs);80.         }81.   }82.   else83.   {84.         // 向左摇的时候85.         for(i = cols - 1; i >= 0; --i)86.         {87.             PORTD = col;88.             delay(delayTimeMs);89.         }90.   }91. }接线图我觉得得上一下,效果图嘛,从几百张照片里挑个好看的来:静态数码管显示总站成一排多无聊,是时候改改造行了。现在排成 8 字吧。先点亮一个 8 字,用静态显示方法。1. // ----------------------------------------------------------------------------2. // static7segLEDS.ino3. // 4. // Created 2015-06-065. // By seesea <seesea2517#gmail#com> 6. // 7. // 静态七段数码管8. // 使用 PORTD 静态显示七段数码管,循环显示 0 - F 9. // 10. // 这里使用的是共阳七段数码管,如果使用共阴的,只要把段码表取反即可11. // 数码管段的顺序按如下标示排列12. //13. //   A14. //    ---15. //F| G |B16. //    ---17. //E| D |C18. //    ---. DP19. //20. // 然后根据需要显示的数字生该段点亮输出 1,可以制作段码表。这种方式做出来的是共阴的,所以使用的时候取反即为共阳的段码21. // +---+--+--+--+--+--+--+--+--+-----+22. // |   | A| B| C| D| E| F| G|DP| 段码|23. // +---+--+--+--+--+--+--+--+--+-----+24. // | 0 | 1| 1| 1| 1| 1| 1| 0| 0| 0xFC|25. // +---+--+--+--+--+--+--+--+--+-----+26. // | 1 | 0| 1| 1| 0| 0| 0| 0| 0| 0x60|27. // +---+--+--+--+--+--+--+--+--+-----+28. // | 2 | 1| 1| 0| 1| 1| 0| 1| 0| 0xDA|29. // +---+--+--+--+--+--+--+--+--+-----+30. // | 3 | 1| 1| 1| 1| 0| 0| 1| 0| 0xF2|31. // +---+--+--+--+--+--+--+--+--+-----+32. // | 4 | 0| 1| 1| 0| 0| 1| 1| 0| 0x66|33. // +---+--+--+--+--+--+--+--+--+-----+34. // | 5 | 1| 0| 1| 1| 0| 1| 1| 0| 0xB6|35. // +---+--+--+--+--+--+--+--+--+-----+36. // | 6 | 1| 0| 1| 1| 1| 1| 1| 0| 0xBE|37. // +---+--+--+--+--+--+--+--+--+-----+38. // | 7 | 1| 1| 1| 0| 0| 0| 0| 0| 0xE0|39. // +---+--+--+--+--+--+--+--+--+-----+40. // | 8 | 1| 1| 1| 1| 1| 1| 1| 0| 0xFE|41. // +---+--+--+--+--+--+--+--+--+-----+42. // | 9 | 1| 1| 1| 1| 0| 1| 1| 0| 0xF6|43. // +---+--+--+--+--+--+--+--+--+-----+44. // | A | 1| 1| 1| 0| 1| 1| 1| 0| 0xEE|45. // +---+--+--+--+--+--+--+--+--+-----+46. // | B | 0| 0| 1| 1| 1| 1| 1| 0| 0x3E|47. // +---+--+--+--+--+--+--+--+--+-----+48. // | C | 0| 0| 0| 1| 1| 0| 1| 0| 0x1A|49. // +---+--+--+--+--+--+--+--+--+-----+50. // | D | 0| 1| 1| 1| 1| 0| 1| 0| 0x7A|51. // +---+--+--+--+--+--+--+--+--+-----+52. // | E | 1| 0| 0| 1| 1| 1| 1| 0| 0x9E|53. // +---+--+--+--+--+--+--+--+--+-----+54. // | F | 1| 0| 0| 0| 1| 1| 1| 0| 0x8E|55. // +---+--+--+--+--+--+--+--+--+-----+56. // ----------------------------------------------------------------------------57. 58. // 共阳段码表 0 - F59. const unsigned char segTable[] = {      60.   ~0xFC, ~0x60, ~0xDA, ~0xF2, ~0x66, ~0xB6,61.   ~0xBE, ~0xE0, ~0xFE, ~0xF6, ~0xEE, ~0x3E, 62.   ~0x1A, ~0x7A, ~0x9E, ~0x8E 63.   };64. 65. void setup()66. {67.   DDRD = 0xFF;    // PORTD 设置为输出68. }69. 70. void loop()71. {72.   for (char i = 0; i < sizeof(segTable) / sizeof(segTable); ++i)73.   {74.         PORTD = segTable;75.         delay(500);76.   }77. }https://mmbiz.qpic.cn/mmbiz_gif/XZMicpgcYibXFbiaVfVEQO5wU28QFyyHF90TsJbcicKN4CE589ibeDXWfPCvMxCST3BFy1ALiajUaXhOa1ujkb9rIFicg/640?wx_fmt=gif
动态数码管显示上面是一位数码管的显示,可以使用静态,如果要显示多位,那可得动起来了。1. // ----------------------------------------------------------------------------2. // dynamic7segLEDS.ino3. //4. // Created 2015-06-065. // By seesea <seesea2517#gmail#com>6. //7. // 动态七段数码管8. // 动态显示七段数码管,从 0000 开始计数,9999 后回 09. //10. // 使用 PORTD 进行段选,使用 pin 8 9 10 11 进行位选11. // ----------------------------------------------------------------------------12. 13. #define COM_ON   HIGH// 共阳数码管启用的公共端电平14. #define COM_OFF    LOW   // 共阳数码管禁用的公共端电平15. 16. // 共阳段码表 0 - F17. const unsigned char segTable[] = {18.   ~0xFC, ~0x60, ~0xDA, ~0xF2, ~0x66, ~0xB6,19.   ~0xBE, ~0xE0, ~0xFE, ~0xF6, ~0xEE, ~0x3E,20.   ~0x1A, ~0x7A, ~0x9E, ~0x8E21. };22. 23. const unsigned char pinPos[] = { 8, 9, 10, 11 };    // 位选管脚,按从低位到高位的排列24. const unsigned intinitNum= 0;                   // 计数初始值25. const unsigned intmaxNum   = 9999;                // 最大计数26. const unsigned char com_num= sizeof(pinPos) / sizeof(pinPos);// 公共端的数量,用于确定有几位27. const unsigned long delayMs= 50;                  // 动态扫描延迟时间28. 29. void setup()30. {31.   DDRD = 0xFF;    // PORTD 设置为输出32.   for (char i = 0; i < sizeof(pinPos) / sizeof(pinPos); ++i)33.   {34.         pinMode(pinPos, OUTPUT);35.   }36. }37. 38. // 于七段数码管上显示数字 num39. // 将 num 数字的个十百千等各位的数字一位一位分离出来显示到每一位数码管上40. void display7segLED(unsigned int num)41. {42.   unsigned int digital;43.   for (unsigned char i = 0; i < com_num; ++i)44.   {45.         digital = num % 10;46.         num    /= 10;47. 48.         PORTD   = segTable;49.         digitalWrite(pinPos, COM_ON);50. 51.         delayMicroseconds(10);52.         digitalWrite(pinPos, COM_OFF);   // 传说中的消隐,避免发生重影53.   }54. }55. 56. void loop()57. {58.   static unsigned long lastTick = millis();59.   static unsigned intnum      = initNum;60. 61.   display7segLED(num);62. 63.   // 动态扫描,不能用 delay 阻塞式的延迟哦,前面试验过的这种用法现在派上用场了64.   if (millis() - lastTick < delayMs)65.         return;66.         67.   lastTick = millis();68.   ++num;69.   if (num >= maxNum)70.         num = initNum;71. }

seesea 发表于 2020-2-27 11:41:04

来看看自己的作品

gxgllzh123 发表于 2020-3-13 08:39:24

神,大神
页: [1]
查看完整版本: Arduino 一颗灯点到极致