More FPGA and NeoPixels

Animated colors with FPGA and WS2812b strip.

After understanding the code from Vivonomicon‘s post I was able to animate the colors in a section of an WS2812B strip using an Icebreaker FPGA.

I had originally thought that this project would not be so difficult to implement. Once I “got” the concepts I was able to create the animated colors. The following code includes lines from Vivonomicon.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Cause yosys to throw an error when we implicitly declare nets
// `default_nettype none
 
// Project entry point
module top (
  input  CLK,
  input  BTN_N, BTN1, BTN2, BTN3,
  output LED1, LED2, LED3, LED4, LED5,
  output P1B1,
);
  // Neopixel state machine.
  reg [2:0] state;
  reg [1:0] npxc;
  reg [12:0] lpxc;
  reg [7:0] bits;
  reg [7:0] led_num;
  reg [24:0] test_color = 24'b000000010000000100000000;
  reg [1:0] isiton;
  reg [24:0] color_run1 = 24'b000000001000000000000000;
  reg [24:0] color_run2 = 24'b000000001000000000010000;
  reg [24:0] color_run3 = 24'b000000001000000010000000;
  reg [24:0] color_run4 = 24'b000000000000000010000000;
  reg [24:0] color_run5 = 24'b100000000000000010000000;
  reg [24:0] color_run6 = 24'b100000000000000000000000;
  reg [24:0] color_run7 = 24'b100000001000000000000000;
  reg [24:0] color_temp = 24'b000000001000000000000000;
  reg [32:0] running_it = 0;
 
  assign LED1 = 1'b0;
  assign LED2 = 1'b0;
  assign LED3 = 1'b0;
  assign LED4 = 1'b0;
  assign LED5 = 1'b0;
  assign P1B1 = isiton;
 
  always @(posedge CLK) begin
    // Process the state machine; states 0-3 are the four WS2812B 'ticks',
    // each consisting of 83.33 * 4 ~= 333.33 nanoseconds. Four of those
    // ticks are then ~1333.33 nanoseconds long, and we can get close to
    // the ideal 1250ns period.
    // A '1' is 3 high periods followed by 1 low period (999.99/333.33 ns)
    // A '0' is 1 high period followed by 3 low periods (333.33/999.99 ns)
 
    if (state == 0 || state == 1 || state == 2 || state == 3)
      begin                 // run this for 3 times then increase state
        npxc = npxc + 1;
        if (npxc == 0)
          begin
            state = state + 1;
          end
      end // end of states 0, 1, 2, OR 3
    if (state == 4) // increases bits then if 24 set to 0 and increase state to 5, if <24 state = 0
      begin
        bits = bits + 1;
        if (bits == 24)
          begin
            bits = 0;
            state = state + 1;
          end
        else
          begin
            state = 0;
          end
      end // end of state 4
    if (state == 5) // go to the next pixel on the strip. if it is at the end state = 6, otherwise state = 0
      begin
        led_num = led_num + 1;
        case (led_num)
        1:  test_color = color_run1;
        2:  test_color = color_run2;
        3:  test_color = color_run3;
        4:  test_color = color_run4;
        5:  test_color = color_run5;
        6:  test_color = color_run6;
        7:  test_color = color_run7;
        default: test_color = 24'b000000000000000000000000;
        endcase
        if (led_num == 10)
          begin
            led_num = 0;
            state = state + 1;
          end
        else
          begin
            state = 0;
          end
      end // end of state 5
    if (state == 6) // This is a 12 bit counter sending 0 out the serial indicating reset of the strip
      begin         // it lasts for >80µs meaning that the end of the pixel numbers has been reached
        lpxc = lpxc + 1;
        if (lpxc == 0)
          begin
            state = 0;
          end
      end // end of state 6
    // Set the correct pin state.
    if (test_color & (1 << bits)) // if the current bit of test_color is 1, or ON, serial is high
      begin
      if (state == 0 || state == 1 || state == 2) //  run three cycles of high for pixel ON
        begin
          isiton <= 1;
        end
      else if (state == 3 || state == 6) // run one cycle of low for pixel ON
        begin
          isiton <= 0;
        end
      end // end of testing the & bit is 1. If the current bit is 0, or OFF, serial is low.
    else
      begin
      if (state == 0) // run one cycle of high for pixel OFF
        begin
          isiton <= 1;
        end
      else if (state == 1 || state == 2 || state == 3 || state == 6) // run three cycles of low for pixel off
        begin
          isiton <= 0;
        end
    end // end of testing the & bit is 0
  end // end of the always main program
 
  // make another always module to change the colors in a cycle
  always @(posedge CLK) begin
    running_it++;
    if (running_it == 1000000) begin
      running_it = 0;
      color_temp = color_run1;
      color_run1 = color_run2;
      color_run2 = color_run3;
      color_run3 = color_run4;
      color_run4 = color_run5;
      color_run5 = color_run6;
      color_run6 = color_run7;
      color_run7 = color_temp;
    end // end of the running_it and reset
  end // end of the always that cycles the color_run stuff
 
endmodule

There might be better ways of implementing the result, but I think I did pretty well as a beginner in FPGA land.

Now that I have this completed I might be able to add a different power source for the 5v on the WS2812B strip. Then I should be able to add more strips by using the other PMOD pins. This Icebreaker has three PMODs built on it. That would be 24 strips. It would make for a great animation.

In the mean time I could add the dip-switch PMOD and use the switches to determine the speed that the cycling lights run. At first I thought of using a potentiometer to control the speed. I have some, probably too many. The dip-switch module is sitting out right by where I have been coding.

Company is coming for the weekend and I am going to have to clear up this space. Luckily, I am going to be sleeping in the trailer. I can move all of my stuff out there. I’ll have to come back into the house to get more parts, as needed.

Later that night

Before going to fiddle lessons I wanted to see if I could indeed add the dip-switch PMOD. I ended up adding some code to the existing which is displayed above. Up in the reg area I added

1
reg [32:0] speeder = 2000000;

In the last always block I changed the 1000000 to speeder, and then added another always block.

1
2
3
4
5
6
7
8
// another always block to change the speed with DIP-switch PMOD on the A section
  always @(posedge CLK) begin
    if (!P1A1) speeder = 2000000;
    if (P1A1) speeder = 1000000;
    if (P1A1 & P1A2) speeder = 800000;
    if (P1A1 & P1A2 & P1A3) speeder  = 600000;
    if (P1A1 & P1A2 & P1A3 & P1A4) speeder = 400000;
  end // end of the always block for speed

Turning on the dip-switches in order together will change the value of the variable speeder. It works well, but I have to mind where I put my fingers while toggling the dip-switches.