Source: 21ic WeChat Official Account
In the last issue of “Teaching You to Light Up in Style,” we taught everyone how to maximize the lighting of a single LED. But the lighting fun doesn’t stop there! This time, we have a more advanced course to teach you how to stylishly light up a row of LEDs. Buckle up, the experienced driver is ready to take off!
After the simplest LED experiment, naturally, we will increase the number of LEDs and arrange them in a row. Finally, let’s arrange the row of LEDs into the shape of the number 8 — a seven-segment display.
Running Light 1 — Marquee Light
The first to appear is the classic running light, also known as the marquee light.
Running Light 2 — Overall Port Operation
Let’s try the running light again, this time using an overall port operation. Does it feel simpler?
Running Light 3 — 74HC595 IO Expansion
Another running light! This time, we use the 74HC595 to expand the IO ports. For Arduino, the IO port resources are quite limited, and sooner or later, you will encounter situations where there aren’t enough. This is when expansion is necessary. Using the 74HC595 is a common solution.
Raindrop Trail Effect Running Light
Sorry, it’s still a running light. This time we simulate a more realistic effect with a raindrop trailing effect.
First, we will use six PWM ports of the UNO to achieve this.
1. // —————————————————————————-
2. // raindropLEDS.ino
3. //
4. // Created 2015-06-04
5. // By seesea <seesea2517#gmail#com>
6. //
7. // Raindrop Flow Effect
8. // The difference between the raindrop flow effect and the running light is that the raindrop effect has a trailing effect, meaning the lights that have been on gradually extinguish.
9. //
10. // Using the six PWM pins of the UNO to achieve the raindrop flow effect
11. // The six PWM pins of the UNO are pin 3, 5, 6, 9, 10, 11, these pins connect to the positive terminals of six LEDs, and the negative terminals connect to GND.
12. //
13. // —————————————————————————-
14.
15. const unsigned char pins[] = { 3, 5, 6, 9, 10, 11 }; // Six PWM pins
16. const unsigned char initPwm = 240; // The PWM value of the brightest light, which is the brightness of the first light when moving
17. const unsigned char deltaPwm = 10; // The PWM value at which the light gradually extinguishes, how much darker the next light is compared to the previous one. This is equivalent to an arithmetic sequence. The arithmetic brightness feels not very good, so we introduce the next geometric factor.
18. const unsigned char deltaPercent = 30; // The next light is darker than the previous light, its brightness is a percentage of the previous light. Compared to the previous decrement, this is equivalent to a geometric series.
19. const unsigned long delayMs = 100; // Movement delay, in ms
20. const unsigned char pinNum = sizeof(pins) / sizeof(pins[0]); // Number of pins, i.e., number of LEDs
21.
22. unsigned char ledPwm[pinNum]; // Store the PWM value of each LED during operation
23.
24. void setup()
25. {
26. for (char i = 0; i < pinNum; ++i)
27. {
28. pinMode(pins[i], OUTPUT);
29. ledPwm[i] = 0;
30. }
31. }
32.
33. void loop()
34. {
35. static unsigned char head = 0;
36.
37. // Every time we enter the loop() function, we process the brightness of all the lights
38. for (unsigned char i = 0; i < pinNum; ++i)
39. {
40. ledPwm[i] = ledPwm[i] * deltaPercent / 100;
41. if (ledPwm[i] <= deltaPwm)
42. ledPwm[i] = 0;
43. else
44. ledPwm[i] -= deltaPwm;
45.
46. if (i == head)
47. ledPwm[i] = initPwm;
48.
49. analogWrite(pins[i], ledPwm[i]);
50. }
51.
52. // Move the raindrop head
53. head = (head + 1) % pinNum;
54.
55. // Delay
56. delay(delayMs);
57. }
Raindrop Trail Effect Running Light 2 — Digital IO Simulation
Having only six PWM ports is not enough, let’s use all the IO ports, including the analog ports, which can also be used as digital IO ports.
1. // —————————————————————————-
2. // digitalRaindropLEDS.ino
3. //
4. // Created 2015-06-04
5. // By seesea <seesea2517#gmail#com>
6. //
7. // Raindrop flow effect implemented with digital pins
8. // The difference between the raindrop flow effect and the running light is that the raindrop effect has a trailing effect, meaning the lights that have been on gradually extinguish.
9. //
10. // Using all pins of the UNO to implement the raindrop flow effect with simulated PWM, including analog input ports that can also be used for digital output
11. // Each pin connects to the positive terminal of an LED, and the negative terminal connects to GND
12. // —————————————————————————-
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 }; // All pins arranged in the order of LED connections
15. const unsigned int maxPwm = 100; // Manually simulated PWM, you can define the maximum PWM value, so it’s convenient to calculate with round numbers
16. const unsigned int initPwm = 100; // The PWM value of the brightest light, which is the brightness of the first light when moving
17. const unsigned int deltaPwm = 1; // The PWM value at which the light gradually extinguishes, how much darker the next light is compared to the previous one. This is equivalent to an arithmetic sequence. The arithmetic brightness feels not very good, so we introduce the next geometric factor.
18. const unsigned int deltaPercent = 70; // The next light is darker than the previous light, its brightness is a percentage of the previous light. Compared to the previous decrement, this is equivalent to a geometric series.
19. const unsigned long delayMs = 70; // Movement delay, in ms
20. const unsigned char ledNum = sizeof(leds) / sizeof(leds[0]); // Number of pins, i.e., number of LEDs
21.
22. unsigned int ledPwm[ledNum]; // Store the PWM value of each LED during operation
23.
24. void setup()
25. {
26. for (char i = 0; i < ledNum; ++i)
27. {
28. pinMode(leds[i], OUTPUT);
29. ledPwm[i] = 0;
30. }
31. }
32.
33. extern volatile unsigned long timer0_millis; // Declare external variable timer0_millis for use in the program, which is actually the return value of millis() — the number of milliseconds the program has been running
34. void loop()
35. {
36. static unsigned char head = 0;
37. static unsigned long lastTick = timer0_millis;
38. unsigned int i, j;
39.
40. // First, turn on the lights, and when the duty cycle reaches the switching point, turn off the lights
41. for (i = 0; i < ledNum; ++i)
42. {
43. if (ledPwm[i] == 0)
44. continue;
45.
46. digitalWrite(leds[i], HIGH);
47. }
48.
49. // The raindrop head is the brightest
50. ledPwm[head] = initPwm;
51.
52. // Here is the digital pin simulated PWM program
53. for (i = 0; i < maxPwm; ++i)
54. {
55. for (j = 0; j < ledNum; ++j)
56. {
57. if (i == ledPwm[j])
58. digitalWrite(leds[j], LOW);
59. }
60.
61. delayMicroseconds(1);
62. }
63.
64. // If the delay time has not yet elapsed, exit without moving the raindrop
65. // Since this is a digital pin simulated PWM, the program must keep running, and cannot use delay() for blocking, or it will get stuck
66. if (timer0_millis – lastTick < delayMs)
67. return;
68.
69. lastTick = timer0_millis;
70.
71. // Process the brightness of each light
72. for (i = 0; i < ledNum; ++i)
73. {
74. ledPwm[i] = ledPwm[i] * deltaPercent / 100;
75. if (ledPwm[i] <= deltaPwm)
76. ledPwm[i] = 0;
77. else
78. ledPwm[i] -= deltaPwm;
79.
80. if (i == head)
81. ledPwm[i] = initPwm;
82. }
83.
84. // Move the raindrop head
85. head = (head + 1) % ledNum;
86. }
POV Wand is Time to Dance, Let’s Shake It!
1. // —————————————————————————-
2. // povLEDS.ino
3. //
4. // Created 2015-05-31
5. // By seesea <seesea2517#gmail#com>
6. //
7. // POV Wand
8. //
9. // pin 0 – 7 connect to the positive terminal of the LED, and the negative terminal connects to GND
10. // pin 8 connects to one leg of the mercury switch, the other leg connects to GND
11. // The mercury switch is fixed horizontally in the direction of shaking. If the actual effect is opposite to the expected effect, just swap the connections of the two legs of the mercury switch.
12. // Note: Since pin 0 and 1 are grounded through the LED, they need to be disconnected during download for successful downloading.
13. // —————————————————————————-
14.
15. const unsigned char key = 8; // Mercury switch
16. const unsigned char delayTimeMs = 2; // Delay for LED lighting during scanning
17.
18. // The software uses the high position of the vertical 8 points for modeling
19. unsigned char col[] =
20. {
21. // Heart
22. // 0x30,0x48,0x44,0x22,0x44,0x48,0x30,0x00
23.
24. // I heart U
25. 0x82,0xFE,0xFE,0x82,0x00,0x30,0x78,0x7C,
26. 0x3E,0x7C,0x78,0x30,0x00,0xFC,0xFE,0x02,
27. 0x02,0xFE,0xFC,0x00,0x00,0x00
28.
29. /* 1 2 3
30. 0x00,0x42,0xFE,0x02,0x00,0x00,0x00,0x00,
31. 0x46,0x8A,0x92,0x92,0x62,0x00,0x00,0x00,
32. 0x84,0x82,0x92,0xB2,0xCC,0x00
33. */
34. };
35.
36. unsigned char cols = (sizeof(col) / sizeof(col[0]));
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) // When shaking from right to left
51. {
52. // Here you can try two schemes
53. // One is to turn off all lights when sweeping to the left
54. // The other is to light up each column in reverse, so the direction is also reversed, and the display returns to normal.
55. // Depending on your needs, you can change the if condition to 0 or 1 to choose different schemes
56. // The advantage of the first scheme is that it is less likely to have ghosting, but the disadvantage is low brightness
57. // The advantages and disadvantages of the second scheme are exactly the opposite
58. if (0)
59. {
60. // Turn off all lights when shaking to the left
61. i = 0;
62. PORTD = 0x00;
63.
64. // If you choose this scheme, you don’t need to continue with the scanning below, just return;
65. return;
66. }
67. else
68. {
69. delta = -delta;
70. }
71. }
72.
73. if (delta == 1)
74. {
75. // When shaking to the right
76. for (i = 0; i < cols; ++i)
77. {
78. PORTD = col[i];
79. delay(delayTimeMs);
80. }
81. }
82. else
83. {
84. // When shaking to the left
85. for(i = cols – 1; i >= 0; –i)
86. {
87. PORTD = col[i];
88. delay(delayTimeMs);
89. }
90. }
91. }
Here’s a wiring diagram I think should be included, a visual effect, picked from hundreds of photos:
Static 7-Segment Display
It’s so boring to line them all up, it’s time to modify it. Now let’s arrange them into the shape of the number 8. First, light up an 8 using static display methods.
1. // —————————————————————————-
2. // static7segLEDS.ino
3. //
4. // Created 2015-06-06
5. // By seesea <seesea2517#gmail#com>
6. //
7. // Static 7-Segment Display
8. // Using PORTD to statically display the 7-segment display, cycling through 0 – F
9. //
10. // This uses a common anode 7-segment display, if using a common cathode, just invert the segment code table.
11. // The order of the segments of the display is arranged as follows
12. //
13. // A
14. // —
15. // F| G |B
16. // —
17. // E| D |C
18. // — . DP
19. //
20. // Then create a segment code table based on the numbers to be displayed, which can be produced by outputting 1 for the segments to be lit. This method produces a common cathode, so invert it for common anode.
21. // +—+–+–+–+–+–+–+–+–+—–+
22. // | | A| B| C| D| E| F| G|DP| Segment Code|
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. // Common anode segment code table 0 – F
59. 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; // Set PORTD as output
68. }
69.
70. void loop()
71. {
72. for (char i = 0; i < sizeof(segTable) / sizeof(segTable[0]); ++i)
73. {
74. PORTD = segTable[i];
75. delay(500);
76. }
77. }
Dynamic 7-Segment Display
The above shows a single-digit display, which can be static; however, to display multiple digits, we need to make it dynamic.
1. // —————————————————————————-
2. // dynamic7segLEDS.ino
3. //
4. // Created 2015-06-06
5. // By seesea <seesea2517#gmail#com>
6. //
7. // Dynamic 7-Segment Display
8. // Dynamically display the 7-segment display, counting from 0000 to 9999 and then back to 0
9. //
10. // Using PORTD for segment selection, using pin 8, 9, 10, 11 for digit selection
11. // —————————————————————————-
12.
13. #define COM_ON HIGH // Common anode display common terminal enable level
14. #define COM_OFF LOW // Common anode display common terminal disable level
15.
16. // Common anode segment code table 0 – F
17. const unsigned char segTable[] = {
18. ~0xFC, ~0x60, ~0xDA, ~0xF2, ~0x66, ~0xB6,
19. ~0xBE, ~0xE0, ~0xFE, ~0xF6, ~0xEE, ~0x3E,
20. ~0x1A, ~0x7A, ~0x9E, ~0x8E
21. };
22.
23. const unsigned char pinPos[] = { 8, 9, 10, 11 }; // Digit selection pins, arranged from low to high
24. const unsigned int initNum = 0; // Initial counting value
25. const unsigned int maxNum = 9999; // Maximum count
26. const unsigned char com_num = sizeof(pinPos) / sizeof(pinPos[0]); // Number of common terminals, used to determine how many digits
27. const unsigned long delayMs = 50; // Dynamic scanning delay time
28.
29. void setup()
30. {
31. DDRD = 0xFF; // Set PORTD as output
32. for (char i = 0; i < sizeof(pinPos) / sizeof(pinPos[0]); ++i)
33. {
34. pinMode(pinPos[i], OUTPUT);
35. }
36. }
37.
38. // Display number num on the 7-segment display
39. // Separate the digits of num into units, tens, hundreds, and thousands for display on each digit
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[digital];
49. digitalWrite(pinPos[i], COM_ON);
50.
51. delayMicroseconds(10);
52. digitalWrite(pinPos[i], COM_OFF); // The legendary ghosting avoidance
53. }
54. }
55.
56. void loop()
57. {
58. static unsigned long lastTick = millis();
59. static unsigned int num = initNum;
60.
61. display7segLED(num);
62.
63. // Dynamic scanning, cannot use delay for blocking delays, the previous experiments have shown that this method is useful now
64. if (millis() – lastTick < delayMs)
65. return;
66.
67. lastTick = millis();
68. ++num;
69. if (num >= maxNum)
70. num = initNum;
71. }
Have you all learned it? Still not satisfied?!
This article is reprinted from the internet, copyright belongs to the original author. If you feel it is not good, please contact us to delete it!
Advertisement
About Lichuang Mall
Lichuang Mall (WWW.SZLCSC.COM) is a one-stop online procurement self-operated mall for electronic components, with over 10,000 self-operated warehouses and more than 190,000 kinds of stock. Guaranteeing 100% original and genuine products, and has achieved lightning delivery within 4 hours! The group’s electronic full industry chain self-operated services cover online EDA (LCEDA), PCB prototyping, component mall, stencil manufacturing, SMT placement, electronic design education, and solutions.