Free Lattice Generator

0 views
Skip to first unread message

Venice Sassone

unread,
Aug 5, 2024, 2:00:57 PM8/5/24
to oolpiweboo
TheLattice Generator is a simple MatLab program that automatically generates various lattice geometries direct to STL format. These periodic structures are intended for use with the metallic additive manufacturing technologies of Selective Laser Melting (SLM) or Electron Beam Melting (EBM), however could be applied to a number of other additive technologies that require the input of an STL file.

Developed out the apparent need for a fast and simple way to generate STL lattice structures for research purposes, the program is a part of a preliminary work in developing a much larger lattice generation capability. In its current state, the program includes the following uniform lattice types:

-Body Centred Cubic (BCC)

-Face Centred Cubic (FCC)

-Modified FCC variants removing x-y, y-z, and x-z planes

-Combined BCC and FCC cell

-Modified combined BCC and FCC removing x-y plane

-Simple cube (box cell)

In theory, any lattice cell configuration could be added to the program provided that it can occupy a cubic volume and is able to be tessellated.

In addition, it was observed that creating Finite Element (FE) meshes was incredibly time consuming via traditional techniques. In response, the lattice generator script has the added functionality of generating FE meshes automatically in NASTRAN format. Other formats are possible, however for the purposes of the development project NASTRAN was all that was needed. In its current form, the lattice FE mesh is a 3D beam element mesh with the properties of the STL lattice applied. Loads or constraints are not generated, and the material properties are only set as a place holder. These aspects need to be modified in a pre-processor (HyperMesh, Patran etc.).


New features and significant performance improvements have been added with this version, including: Binary STL file support, a new rapid uniform lattice creation option, new bulk form STL writing, and new cell types.


The reason why I'm trying to do this is because I want to create hasse diagrams of lattices for fun, but doing so by hand is a very tiring process and i want an algorithm to help me draw my lattices (I'm using Motion Canvas). Having a pseudocode would be wonderful however I really appreciate any pointer that can help me in my struggle!


A couple of years back, I did a collection of blogs called 'Waves' using this board (see the links at the end). For those blogs I wired up a Microchip 12-bit DAC, but I thought it might be interesting to get a converter that could give me a bit more resolution for doing audio stuff with, so I splashed out and bought myself a Pimoroni Pico Audio board [PIM544] as a quick way to prototype without having to design a pcb. The board is intended for use with a Pico, but the interface is standard I2S, so I don't suppose it will prove to be too difficult to drive it from an FPGA instead. The device they've used on the board is a PCM5100A from TI. It's not a full CODEC, just the DAC side, and it can apparently manage to work to 32 bits at the slower sampling rates.


To start with I'm going to wire it up and then, as a test, see if I can generate a pair of computed, fixed-frequency, quadrature sinusoids on the left and right channels using the Taylor-series method I experimented with in one of the Waves blogs [4]. I'll need to extend what I did previously because, back then, I stopped short and didn't implement fully the phase-accumulator that will be necessary to generate a continuous waveform.


Keep in mind that I'm not a DSP specialist and don't have a professional background in this area, so this is me experimenting and trying out stuff: it's most definitely not intended as a tutorial. Also, I'm self-taught with the HDL, so I may be passing on some bad habits here.


The hardware side is very simple and consists of a carrier board, made out of stripboard, to connect the Brevia board to the Pico Audio board. Not very elegant, but it will work well enough for a prototype. The apple is a Red Pippin.


The extra red wire, that you can see in the photograph above, is 5V that I've had to bring across from the USB area of the Brevia board. That's needed because the Pimoroni board has a regulator on it, presumably to get a clean 3.3V rail, but there's only +3.3V on the Brevia's header. The additional chip and crystal, that you can see on the stripboard, forms a 12.288MHz oscillator that I'm going to feed to the FPGA and use as an alternative to the Brevia's 50MHz oscillator. I tried to derive this within the FPGA, but I couldn't get an exact 12.288MHz clock from the 50MHz using the PLL. The reason for wanting the frequency of 12.288MHz is because it's a multiple of common audio sample rates like 48ksps, 96ksps, etc.


The design of the logic in the FPGA falls naturally into several parts: a 'phase accumulator', to keep track of where we are on the waveform; the combined sine and cosine calculation, to generate the next pair of output samples given the phase; and the I2S interface, to transmit the left and right samples (sine and cosine results) to the external DAC.


These will all have to be kept 'in step' so that output samples arrive at the DAC at the right time. As the I2S output has to run continuously, I'm going to design it so that the I2S interface 'free runs' on the clock and, at a fixed point in the output cycle, triggers an update of the phase accumulator and then the subsequent sine and cosine calculation. Because, in this case, the sine and cosine have the same frequency, they can be calculated together, with some of the calculation shared.


To make life easy for myself, I'm going to have everything synchronous to one clock. Rather than that being directly the 12.288MHz clock that comes from my oscillator, I'm going to 'perch' an internal PLL [phase-locked loop] on top of it. That PLL will multiply the frequency by four and generate an internal 'master' clock of 49.152MHz that will drive all my logic. That's then still a multiple of the common sample rates used for audio (I'm actually using 96ksps here) but has the advantage over the 12.288MHz clock that I can do a good bit more calculation between the sample times (this particular XP2 part only has a small number of multipliers to work with, so I'm thinking ahead as to how I might make best use of them). It is, though, still a modest enough frequency that I'm not going to run into any real timing issues within the FPGA (provided I'm reasonably careful with the way I design) and I can focus most of my attention on what the logic does.


The I2S interface is little more than a shift register to turn the computed samples into a serial bitstream as per the I2S standard. Main challenge will be to make sure that the bits come out in the right order and that they line up properly with the word clock - I should be able to manage that with the help of an oscilloscope. One small curiosity, from going back and reading the original Philips spec for I2S, is that it doesn't seem to strictly specify which of the two samples comes out first (left or right channel of the stereo pair).


With the previous blog, I took the odd powers as they came along, to give me a sinewave, and discarded the even ones. That wasted cycles but made for a fairly simple structure in the FPGA. If we look at the respective series for sine and cosine, though,


we can see that directing the powers alternately to the sine and the cosine calculation to build each respective result could be reasonably efficient and would nicely elaborate the way my original algorithm worked, so I'm going to try that and see how I get on.


The starting value for the computation, the phase (theta) of the waveform for the next sample time, comes from a 'phase accumulator'. For a single, fixed frequency, like I'm aiming for here, the phase increment that gets added to the accumulator each time will be a constant. Change it to a different constant and we will step along our imaginary sinewave, that we imagine ourselves sampling, at a different pace and get a different, fixed output frequency as a result. Start varying the increment and we'll have a varying frequency, which sounds like it might be fun to play with.


That, then, leads to a fundamental and interesting question that I don't know the proper answer to, not having a background in real DSP stuff. How do I represent the phase? One possible approach might be to simply use a signed fixed-point or signed floating-point number and wrap the accumulator when we get to a value of pi (back to -pi). That, though, requires some awkward arithmetic to detect when we run past pi and subtract 2*pi to make it cyclic. An alternative method, and the one I'm going to try here, is to normalise the phase so that the value in the phase accumulator represents the fraction of a full turn. That way, the phase accumulator arithmetic will be operating modulo a power of 2 and will simply wrap round without me having to do any comparisons at all. The normalisation is simply to divide both sides of the series through by pi.


Finally, I need to consider the factorials in both equations. There are the two issues of where they come from and how we achieve what looks on paper like a division. Although the factorials could be computed by the FPGA, that's wasteful as they are exactly the same every time the calculation is done. Since there aren't too many of them, it's going to be more straightforward to precompute them and store them in a ROM. The division I'm going to avoid altogether by the simple expedient of calculating the reciprocal to put in the ROM: that can then be multiplied with the power using a multiplier, so avoiding all the messiness of division. The normalisation can also be done by precomputation, simply by adjusting those ROM values. So, in the end, what looks like a complex piece of computation, turns out to be something that can be done with two multipliers, a ROM (to hold the coefficients), a bit of addition (I'm going to use 2's complement, fixed-point arithmetic, so even the subtractions can be baked into the constants in the ROM by simply making them negative numbers where appropriate, rather than positive), some registers for temporary storage, and a bit of multiplexing to steer results to where we want them. After trundling through 20 cycles (10 terms for each of sine and cosine), I should then have my sine and cosine values.

3a8082e126
Reply all
Reply to author
Forward
0 new messages