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.