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.

// 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.