If you are keeping up I have designed a new venturi tube and 3D printed it.
Here it is in all it's splendour...if such a term can be used to describe it 😂
The newly printed Venturi Tube!
It didn't print too badly although my printer has room for improvement! I may well tweak the settings and print another as one of the ports for the tubes broke off. The design may need improving anyway. It will work for now...
Here is a poor diagram for the venturi tube. For some inexplicable reason I cannot easily export 2D images with dimensions from Fusion 360.
In order to write the firmware and calculate the required values from the sensor measurements we need to calculate the volume of each of the shaded sections of the venturi tube and apply the Bernoulli Venturi formula:
An is the blue shaded area which is made up of three cylinders: A1, A2 and A3. The volume area of a cylinder is found by the following formula:
If we now use the dimensions from the diagram we have the following for the blue shaded area (An):
The area of the blue section (An) is 14547.5675 mm^2 or 0.01455 m^2
Lets calculate the volume of the green section (Am):
The area of the green section (Am) is 4432.305 mm^2 or 0.0044 m^2
Note: I used a value of 3.142 for Pi...
Lets now attempt to calculate Q, the volumetric flow rate. We will use a value of 320 for P1 and 200 for P2, The density of air (µ) at 20 °C is 1.204:
Applying the values calculated above:
This simplifies to:
Which finally gives:
Therefore Q is:
The volumetric flow rate of air (Q) in the blue section, with a pressure differential of 120 has been calculated to be 65702.79 mm^3/s or 0.0657 m^3/s.
Lets calculate the volumetric flow rate of air (Q) in the green section:
This simplifies to:
Which finally gives:
Therefore Q is:
The volumetric flow rate of air (Q) in the green section, with a pressure
differential of 120 has been calculated to be 65702.79 mm^3/s or 0.0657 m^3/s.
Thankfully the calculations match for the different sections - It means theoretically the operations performed were correct...
We can now calculate the velocity of flow using the formula:
Using the values calculated:
which calculates to:
The velocity of flow in the blue section with a pressure differential of 120 has been calculated to be 0.4516 metres / second.
If we repeat for the green section:
Using the values calculated:
which calculates to:
The velocity of flow in the green section with a pressure differential
of 120 has been calculated to be 14.82e^-9 metres / second.
We can check our results are correct by applying Bernoulli's formula:
If we apply the values we have calculated:
This simplifies to:
Which finally calculates to:
As the pressure differential was set to 120 and calculating back came to 119.94 I think we are close enough! With long decimal numbers and rounding there will always be some errors which creep in - I'm happy that our method is correct.
We can know turn this into some firmware. We needed to perform the calculations to obtain the volume of the blue section and the volume of the green section. These will be used as constants in our firmware along with the constants used for the density of air at 20 °C.
The more astute among my readers will notice that the venturi formula uses µ as the symbol for the density of air. This is incorrect and it should be ρ.
Please accept my notational errors but I haven't the will or patience to change the µ symbol to ρ....
Please also accept my apologies for the really long and possibly boring post...It had to be done to get to the firmware writing stage.
If anyone is curious as to how I have obtained the formulae used these are applications of the Venturi Equation. I wrote about this in my previous post:
In the next post I will write some firmware for the STM32 Blue Pill and connect up the differential air pressure sensor, 16 bit ADC and test the venturi tube...right now I need some sleep...
Some of my blog readers in the Sudan have asked me to help them in designing equipment for respirators. At the moment everyone everywhere seems to be suffering with COVID-19 problems and I suspect certain countries are having a much worse time than others.
The readers have seen my efforts at designing a 3D printed venturi tube and believe it might help them in their efforts to design a easy to build respirator. There are several open source respirator projects at the moment - here are two of the more promising ones:
I have looked for information on the air flow sensing part of the requirements and cannot find much information. It would seem that this part of the problem is either being overlooked or resolved with off the shelf products. I don't have any issues with using off the shelf products but they will only be available to those that can afford to pay for them...and they are only available...while stocks last.
The product most discussed is the SFM3300 Mass Flow Meter made by a company called Sensiron:
These components will need to be mounted on or near to a new venturi tube which will be 3D printed to ensure it is easy to manufacture. The electronic signals from the breakout board will be connected to the analogue to digital converter (ADC) and the output from the ADC will connect to an STM32 microcontroller known as the 'blue pill'.
Latest Venturi Tube with indent for BME280 Breakout PCB
The principle dimensions for the tube are 105 x 36 x 31 mm and I will be 3D printing it very shortly to allow for testing. Once I have printed and tested one I will be performing all of the calculations required to use it with the MPX7002 Pressure sensor. I will be sharing the files once I have validated the design.
Internally the venturi tube has several cylindrical volumes removed in order to create the venturi apertures.
Side profile showing the internal volumes of the Venturi Tube
The next post will cover the calculations of the internal volumes of the tube ready for writing the firmware for the microcontroller.
Recently I started playing chess again and I also started watching a very lively bunch on Youtube who play chess and banter often. They play five minute blitz games and shoot the breeze and it's really entertaining and educational.
If any of my readers are interested in chess please check them out:
I noticed that they use a set of chess clocks but with liquid crystal displays which when filming doesn't show up well on camera. I think a seven segment LED display will be much clearer to see and would make the video editing easier.
Every good engineer or inventor should check if there is a product out there already before they rush off and develop something and unsurprisingly there is, chess clocks are incredibly popular products it would seem.
Here are some examples of chess clocks on the market:
To be honest...the main reason I never finished the original chess clocks is because I was able to download an application for my mobile phone which worked well and cost me nothing...I also struggled to engineer the PCBS down to a sensible size...at the time - I wasn't particularly keen on surface mount technology and I was making PCBS from scratch. I can now use surface mount happily and I can have printed circuit boards made easily for a small cost.
There is even a chess clock which uses seven segment displays which looks very well designed and realised but it is quite expensive:
ZMF-II Digital Chess Clock - image credit: Chesshouse.com
The ZMF-II is actually pretty close to what I would like to achieve however I have a few tricks to make it more useful than an average set of chess clocks. We can add wifi connectivity, sound and possibly video overlay of the clocks. I'm hoping the guys at coffee chess will like it....hopefully they will get in contact ;)
Lets list the functions we will need:
Two displays
A method of indicating who's turn it is
A method of setting the time / starting / stopping time
A simple sound output device.
A method of powering the device
Lets list the functions we would like to have:
Wifi or bluetooth connectivity
Video overlay of player clocks output to facilitate editing
Lets list the Components I think we will need (this may change):
1x off / on slide switch
1x programming / settings button
2x move buttons
2x LEDS to display which player has to move
2x four digit seven segment displays
1x small speaker to provide limited audio
1x 18650 battery to provide power
1x 18650 charging circuit with USB C connector
An ESP32 or possibly an FPGA with a softcore...
I possibly have all of the bits required in my electronics junk pile which is nice. Once we have the circuit working as required I'll design a PCB and get a permanent version of the circuit working. I may well design a laser cut or 3D printed case...
I also should probably read up on the rules for chess clocks!
Last post covered creating a one second counter in verilog and making an LED flash. In this post I wanted to cover how to make multiple counters. I mentioned in the previous post that multiple counters can be made without a degradation in speed or efficiency and they can be totally independent of each other - bold claims...lets see it done!
Lets make a counter module but unlike before lets make it standalone code that can be used and modified without affecting the inputs and outputs - that way when we need a counter we can just re-use the counter module code and not have to re-write it every time...unless we have a specific requirement to do so.
Here is a simple diagram of what I hope to achieve:
We will have four LEDS changing state depending on the counter output. The clock is our input and the LEDS are the outputs. The part we need to write is the counter module, before we do that though lets think about what we would want in a counter - Lets discuss counter requirements:
Range of the counter - How big a value will we need to count to before we need the count to reset and start again.
Resolution of the counter - What units do we want to count in: Units, Tens, Hundreds, Thousands, Binary???
Count direction - Do we want to count up from a value or count down from a value?
Load - Do we want to be able to start counting from a value other than zero??
Reset - Do we want to be able to reset the count at any time?
Overflow - Do we want to be able to detect if the count reached the maximum value and then started again?
What we are describing here is the behaviour of the counter. This is one way of coding in a hardware description language - The other method is known as structural where the structure of the logic gates needed to realise the function is described.
Before the advent of microcontrollers and FPGA there was (and still is) a very popular integrated circuit that I used called the 74LS163. It was a four bit binary counter with a ripple carry output. That means it could count from zero to fifteen in binary when a positive edged pulse was detected at the CLK pin. Each time an edge is detected the count increased by 1. When the count reached 16 an output was set known as the ripple carry output or RCO. This allowed a designer to cascade the devices together to make a 32 bit counter or a 48 bit counter, or even a 64 bit counter. I'm going to write some verilog code which reproduces the behaviour of the 74LS163 just as an exercise.
The datasheet for a 74LS163 is here just in case people are interested:
// 74LS163 Binary counter module
binary_counter(input a, b, c, d, enp, ent, load, clear, clk,
output qa, qb, qc, qd, rco );
always @(posedge clk)
begin//start the count on the rising clock edgeif (!clear)
begin
{qd, qc, qb, qa} <=4'b0; //if clear is low, set the count outputs to 0endif (clear &&!load)
begin//if load is set low, set count output to match the input
{qd, qc, qb, qa} <= {d,c,b,a};
endif (clear && load && enp && ent)
begin//if clear, load, ent and enp are set high, increment the count by one
{qd, qc, qb, qa} <= {qd, qc, qb, qa} +1;
endendassign rco = ent ? qa && qb && qc && qd :0; // if the count has reached 15 and ent set high set rco highendmodule
Lets explain the code:
We have made a module called binary_counter. It has inputs called a, b, c, d, enp, ent, load, clear and clk. It also has outputs called qa, qb, qc, qd and rco. The module section details the inputs and the outputs of the module and their types.
The next section always @(posedge clk) is similar to the main loop in a high level language. This is the description of the circuit that will be free running forever:
When a positive clock edge is detected on the clk input the follwing checks are performed:
Is the clear low? If it is low then set all the outputs to zero.
Is the clear high and the load low? If this is true then set the outputs qa, qb qc and qd to match the inputs a, b, c, d.
Is the clear high, load high enp high and ent high? If this is true then add one to the count value of bits on the outputs qa, qb, qc and qd.
Has the count reached 15 and has there been another clock edge? If this is true set the outputs to zero and set rco high.
If we were to flash this code to the ulx3s it would work...there is a little more code to write before we could use it. It isn't particularly practical though...If we needed a 4 bit binary counter we could just buy one and use it....not recreate it in FPGA fabric...that would be a bit of a waste of resources. The reason I've mentioned it is I want to illustrate the method of describing what is required.
I have found two methods for writing verilog code. The first method is to calculate the logic function required, then draw a diagram of it (structural implementation) and then write verilog code.
The other way is to draw a diagram which describes the behaviour of what is required and then write verilog code. Either method can be applied...I find the latter behavioural method easier.
Going back to our requirements:
Range of the counter - How big a value will we need to count to before we need the count to reset and start again.
It actually doesn't matter. We can decide when to start, stop and reset the count!
Resolution of the counter - What units do we want to count in: Units, Tens, Hundreds, Thousands, Binary???
Again, it doesn't matter. We can decide what increments we use!
Count direction - Do we want to count up from a value or count down from a value?
This time it matters, we can write code which describes both behaviours but we will need an input to set the count direction.
Load - Do we want to be able to start counting from a value other than zero??
This function was useful when using discrete devices with fixed range and resolution. We don't need this anymore as we can have any range and resolution we like.
Reset - Do we want to be able to reset the count at any time?
Yes! We will need an input to be able to start the counter from zero.
Overflow - Do we want to be able to detect if the count reached the maximum value and then started again?
This function was useful when using discrete devices with fixed range and resolution. We don't need this anymore as we can have any range and resolution we like.
Here is a diagram of what our counter would look like:
From this we can write the module statement for our counter:
module up_down_counter(
input clk, // 25 MHz clock input from the top moduleinput reset, // reset input from the top moduleinput up_down, // count up or down from the top moduleoutput [24:0] count_output // counter output to be passed to top module
);
Next we need an internal signal to act as a register which stores the result of the count process depending on whether the count is up or down, whether the reset is active (high) or a clock edge has been detected...
reg [24:0] count = 0;
Now we need to write the code that describes the actual counting process:
Here is the entire code for the module just in case it is needed:
module up_down_counter(
input clk, // 25 MHz clock input from the top moduleinput reset, // reset input from the top moduleinput up_down, // count up or down from the top moduleoutput [24:0] count_output // counter output to be passed to top module
);
reg [24:0] count = 0; // Register for the counteralways @(posedge clk orposedge reset)
beginif(reset)
count <= 0;
elseif(~up_down)
count <= count + 1;
else
count <= count - 1;
endassign count_output = count;
endmodule
So we have written a module which will count up or down to any value in unit increments...Copy and paste the code into a text file and call it up_down_counter.v
I used this folder:
C:\msys64\src\Alex\ulx3s\Multiple Counters
Now we need to write some code that calls our new counter module and supply the modules with the inputs needed and then couple the outputs to something we can physically see...LEDS!
Before we do that though we should really update our original diagram to reflect the module code we have written because it will help us write our top module.
The up_down counter module looks like this in diagram form:
Now we are going to use this information to update the top level diagram to implement some verilog code which flashes the LEDS at different rates. Here is the diagram:
From the diagram we can now work out what our top module inputs will be:
Clock, Reset, Up / Down
The outputs are LED 0, LED 1, LED 2 and LED 3
From here we are going to write the top.v code which will call the counter module we wrote earlier and attach the inputs and outputs. Before we do that though it is a good idea to simulate the module code to make sure it works as intended.
There are many simulation programs available for verilog. I decided to use the iverilog program which is well documented. The software is written and maintained by Stephen Williams and it is released under the GNU GPL license. I should also mention that I'm using the Windows 10 operating system...
Make sure that iverilog is added to your path in Windows! Now we need to write some code which tests the module.
This is known as a test bench and it is some more verilog code:
// Testbench Verilog code for up-down counter//`timescale 1ns/100ps`include"up_down_counter.v"//include the file which contains the module to be tested// create a test bench module with the inputs and the output(s)module up_down_counter_testbench();
reg clk, reset,up_down;
wire [24:0] count_output;
// instantiate the module to be tested with inputs and output(s)
up_down_counter counter_one
(clk, reset, up_down, count_output);
// tell the simulator what we want the simulation file to be called// and to get data for all variablesinitialbegin$dumpfile("test.vcd");
$dumpvars;
end// monitor the variables so we can see some text output from the simulationinitial$monitor("At time %t, count_output = %h (%0d)",
$time, count_output, count_output);
// create a clock running for 10 seconds high and 10 seconds lowinitialbegin
clk=0;
forever #10 clk=~clk;
end//exercise the inputs at suitable points in time and finish the simulationinitialbegin
reset=1;
up_down=0;
#20;
reset=0;
#120;
up_down=1;
#500$finish;
endendmodule
Create a text file called up_down_counter_tb.v and paste the above code into it. Save the file in the same directory as the other verilog code already written...I used:
C:\msys64\src\Alex\ulx3s\Multiple Counters
The comments for the code should be fairly self explanatory. Now it is time to run the simulation:
Open a command prompt window and navigate to the folder:
The command creates an iverilog simulation file with a vvp extension from our test bench code.
Next type the command:
vvp up_down_counter_tb.vvp
You should see the following output:
A file test.vcd has been created and we can now use this to look at the results of the simulation using a piece of software called GTKWave...
Type the following command:
gtkwave test.vcd
The GTKWave program should load:
Click on the module in the SST box in the top left corner to add the signals:
Double click on each signal to display it's simulated waveform:
Click on the magnifying glass icons to expand the traces to see the output!
Our code works! We can see that while the reset is high no counting occurs. When the reset is low and up_down is low the count increases, when up_down is high the count reduces...
Simulation is a very useful tool to check that the code we have written works as intended...Lets get on with writing the top module code to complete what we set out to do in the first place. It will be quite similar to the test bench code already written.
Create a text file called top.v and save it in the same folder as the other files:
C:\msys64\src\Alex\ulx3s\Multiple Counters
Here comes the code:
module top(
input clk_25mhz, // 25 MHz clock inputoutput [3:0] led, // 4 Bit LED Output registeroutput wifi_gpio0 // Output for Wifi enable
);
//internal signalsreg [24:0] counter_one_output; // Register for the first counter outputreg reset_counter_one; // Register for the reset on the first counterreg up_down_counter_one=0; // Register for the up_down on the first counterreg [24:0] counter_two_output; // Register for the second counter outputreg reset_counter_two; // Register for the reset on the second counterreg up_down_counter_two=0; // Register for the up_down on the second counterreg [24:0] counter_three_output; // Register for the third counter outputreg reset_counter_three; // Register for the reset on the third counterreg up_down_counter_three=0; // Register for the up_down on the third counterreg [24:0] counter_four_output; // Register for the fourth counter outputreg reset_counter_four; // Register for the reset on the fourth counterreg up_down_counter_four=0; // Register for the up_down on the fourth counter //first counter module instantiation
up_down_counter one_sec_counter(
.clk(clk_25mhz),
.reset(reset_counter_one),
.up_down(up_down_counter_one),
.count_output(counter_one_output)
);
//second counter module instantiation
up_down_counter half_sec_counter(
.clk(clk_25mhz),
.reset(reset_counter_two),
.up_down(up_down_counter_two),
.count_output(counter_two_output)
);
//third counter module instantiation
up_down_counter quarter_sec_counter(
.clk(clk_25mhz),
.reset(reset_counter_three),
.up_down(up_down_counter_three),
.count_output(counter_three_output)
);
//fourth counter module instantiation
up_down_counter eighth_sec_counter(
.clk(clk_25mhz),
.reset(reset_counter_four),
.up_down(up_down_counter_four),
.count_output(counter_four_output)
);
// initial settings// resets all high to start counts at same time// up_down all low to ensure we count up not downinitialbegin
reset_counter_one = 1;
reset_counter_two = 1;
reset_counter_three = 1;
reset_counter_four = 1;
up_down_counter_one = 0;
up_down_counter_two = 0;
up_down_counter_three = 0;
up_down_counter_four = 0;
endalways @(posedge clk_25mhz) // react on the positive clock edgebegin//counter one set to count for one second if (counter_one_output == 25000000) // If the count register has reached 25 million begin
led[0] <= ~led[0]; // toggle the LED[0]
reset_counter_one = 1; // set counter one back to zero endelse reset_counter_one = 0; // allow counter one to continue//counter two set to count for 0.5 seconds if (counter_two_output == 12500000) // If the count register has reached 12.5 million begin
led[1] <= ~led[1]; // toggle the LED[1]
reset_counter_two = 1; // set counter two back to zero endelse reset_counter_two = 0; // allow counter two to continue //counter three set to count for 0.25 secondsif (counter_three_output == 6250000) // If the count register has reached 6.25 million begin
led[2] <= ~led[2]; // toggle the LED[2]
reset_counter_three = 1; // set counter three back to zero endelse reset_counter_three = 0; // allow counter three to continue//counter four set to count for 0.125 seconds if (counter_four_output == 3125000) // If the count register has reached 3.125 million begin
led[3] <= ~led[3]; // toggle the LED[0]
reset_counter_four = 1; // set counter four back to zero endelse reset_counter_four = 0; // allow counter four to continueendassign wifi_gpio0 = 1'b1; //set the wifi_gpio Highendmodule
Copy and paste the above code into top.v and save it in the same directory as before.
Hopefully the code is fairly easy to understand...Here are the critical sections:
module top(
input clk_25mhz, // 25 MHz clock inputoutput [3:0] led, // 4 Bit LED Output registeroutput wifi_gpio0// Output for Wifi enable
);
The first part is the module statement with the inputs and the outputs - this module is called top because it is the top module and it has a clock input, an eight bit register called led and a single bit output for the wifi enable.
//internal signalsreg [24:0] counter_one_output; // Register for the first counter outputreg reset_counter_one; // Register for the reset on the first counterreg up_down_counter_one=0; // Register for the up_down on the first counter
The above section details the internal signals that are needed to be passed to and from the up_down counter
module:
A 24 bit counter register is needed to take and store the output from the up_down counter module
A reset register is needed to set the reset input inside the up_down counter module
An up_down_counter register is needed to set the up_down input inside the up_down counter module
This is repeated four times as there are four counter modules instantiated. The code for instantiating the module is below:
Finally we have the code which makes the FPGA react when the counters have reached a defined count:
always @(posedge clk_25mhz) // react on the positive clock edgebegin//counter one set to count for one secondif (counter_one_output == 25000000) // If the count register has reached 25 millionbegin
led[0] <= ~led[0]; // toggle the LED[0]
reset_counter_one = 1; // set counter one back to zeroendelse reset_counter_one = 0; // allow counter one to continue
When counter one has reached twenty-five million (one second) toggle led[0] to its alternative state, reset the count
If counter one hasn't reached twenty-five million don't reset the count...
Why twenty-five million? Well....25 MHz is the external clock frequency...that means the external clock oscillates twenty-five million times per second so if we want a count every second we need to count twenty-five million clock edges...
Lets upload the code to the board - fire up ConEmu.exe (Windows) and navigate to the folder:
C:\msys64\src\Alex\ulx3s\Multiple Counters
Remember that the board I'm working with has an ECP5 45F Lattice Semiconductor FPGA so the commands are tailored to it. If you are working with a ECP5 12F board or an ECP5 85F board you will need to change to commands appropriately:
Type the following command into the console:
apio build --board ulx3s-45f
Ignore the warnings ;)
Make sure you have the ULX3S development board connected via USB!
Now lets upload the bitstream to the ULX3S development board:
apio upload --board ulx3s-45f
Finally clean up the directory:
apio clean
If all went according to plan you should see the leds on the ULX3S board flashing!
Well...that was a lot of work! Apologies for the really long post. There was a lot to get through! Next post will be something simple and more fun hopefully!