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.
// 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
reg [32:0] speeder = 2000000;
In the last always block I changed the 1000000 to speeder, and then added another always block.
// 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.