Saturday, 20 December 2025

Designing an FPGA development board support plate with Freecad for 3D printing

I have been playing with my FPGA development boards again.  I favour the open source programming environment based upon Yosys called Icestudio.  The website is below:

Icestudio

I am a member of their forum and one of the other members wanted a support for their FPGA development board called the Alhambra II:


I already 3D printed one which was very well designed by one of the other members - Fernando Brea.

If anyone wants to buy an Alhambra II to learn FPGA development (I recommend them) they are available here:

Buy the Alhambra 2

The request came up on the forum to make a support for the board without text.  

Fernando very kindly has shared his original design on a github page:

https://github.com/fernandobrea/Alhambra_II_3D_Support

I will state that Fernando's design skills are excellent.  The function of the support without the requirement for screws and cut outs for the buttons is inspired.  I rarely do so well with mechanical design - my primary skill is with electronic design and test and not mechanical design - we cannot be great at everything...but that won't stop me trying😉

There is a version available with text and without text.  The version without text does not have the base extension which is what I believe is what the request is for - the branding on the version with text may not be to everyone's taste.

So to that end I will attempt to create a Freecad design with the base present but without text.  I'm going to share how I achieved this in the hope that others might learn from the experience.  If one is following along with this tutorial they will need to download and install Freecad:

https://wiki.freecad.org/Download

Fernando's Freecad skills far outmatch my own so in an effort to learn something I am going to reverse engineer his design and hopefully reproduce something I can work with.

The Freecad feature tree alone is a testament to the work flow:

This means I can use the information to recreate the design of the base and merge it with the support section providing what is required.  There is in my opinion no right or wrong way to do things as long as the job works and gets completed without taking too much time and money.  Too much of anything is subjective so other people may disagree...

Here is the work flow:

Load up Freecad - I'm using the Windows version but Linux and Mac are also supported.

Start a new design in Freecad in the part design workbench and call it Alhambra II 3D support AL or whatever you would like.

Next create a 'Body' - a container to 'keep' all of the design sections in one place.

Next create a new sketch on the X-Y Plane so that whatever we model is from the bottom up:


Next we need to draw the base plate which goes under the Alhambra 2 printed circuit board:

I looked at the original design and found the dimension of the base portion of the design:

The base is a rectangular shape measuring 82 mm x 75.2 mm.  It has 4 mm radiused corners and a central cut-out feature.

Lets sketch those features:


Starting with the 'Centered Rectangle Tool' - lets create the main part of the base by creating a rectangle measuring 82 mm x 75.2 mm.

The screen should look like this:


 Next we are going to add the corner radii (fillets) - Click on the create fillet tool and select each horizontal and vertical line near the corner.  Do not worry about accuracy:


Do not worry that the sketch is under constrained...we will resolve that later on.  Next we need to make all of the fillets equal.  Hold down the CTRL key and select all four fillets and then click on the 'Constrain Equal' tool or press 'E':


Now lets set a dimension to the fillets by clicking on a fillet and clicking of the smart dimension tool:


Set the fillet radius to 4 mm.  The screen should now look like above and the drawing should be green denoting that the design is fully constrained.  It won't be for long however as we are going to add the central cut out.  This next step isn't strictly necessary but it will save on plastic when we come to 3D print the design.

Click on the 'Centered Rectangle Tool' and create another rectangle within the centre of the sketch.  The rectangle dimensions are: 68.8 mm x 53 mm.


Next we are going to add another fillet to the inner rectangle on the bottom left corner as this was present on the original.  Click on the 'create fillet' tool and click on the inner horizontal and vertical lines where they intersect in the bottom left corner - do not worry about accuracy:


When you have created the fillet it should look like this:


Next we need to dimension the fillet we just created - click on the dimension tool and then click on the fillet and set it to 3.17 mm (the same dimension that Fernando used in his implementation).


In the original design there were two small holes in the top right and bottom left corners - Lets add them.  

Click on the toggle construction geometry tool:


This allows one to add reference features without actually affecting the design.  We are going to use it to place markers where the holes need to be placed.  Fernando did this slightly differently in his implementation - either way is correct.  I like construction geometry.  The holes are 10 mm vertically and horizontally offset from the outer lines of the base.  So if we create centred rectangle which measures 72 mm x 65.2 mm in construction geometry the corner points will be exactly where required in each corner. This was calculated: (82 mm - 10 mm = 72 mm) and (75.2 mm - 10 mm = 65.2 mm)

Lets create the construction rectangle:


We can now add two circles to the top right and bottom left corners of the blue dashed rectangle (construction geometry) which will position the holes where we need them.

Click on the construction geometry tool to turn it off and and then click on the create a circle by centre tool:


Then click on the bottom left vertex of the blue dashed square and create a circle - the size isn't important, repeat the same process for the top right vertex of the blue dashed square:


Next select hold down the CTRL key and select both circles and then click on the 'Constrain Equal' tool or press 'E':


Now lets dimension the circles we have just created to bring the sketch back to fully constrained - click on the dimension tool and select constrain diameter and then click on one of the circles:


Set the diameter to be 1.4 mm which gives the same as that which Fernando used in the original design.

Your sketch should now look something like this:


As the sketch is fully constrained we can exit the sketch although after all of that work I would sincerely suggest we save our work so that we don't lose everything we have created so far:


Lets exit the sketch by clicking on the close button or the leave sketch button and return to the part design work bench:


The screen should now look like this:


We are now going to create the 3D shape by extruding our design to create the base in 3D.  

Click on the 'Pad tool' and set the amount of extrusion to 3 mm:


The shape should now look like this:


If we look at the design from an isometric view point it looks like this:


I actually think this is the best bit about 3D modelling and design - seeing ones idea become reality even virtually is very rewarding.  I believe Nicola Tesla said it best:

"I do not think there is any deeper fulfilment that can capture a human heart than the feeling of an inventor seeing his ideas materialized"

Anyway...lets get on with creating the support...

At this point we can continue to model the original design from scratch or we could copy the files Fernando has shared and merge them with our base plate creating what we require.

I quite like this approach as to re-model all the features from scratch will be quite time consuming and for a beginner difficult to achieve.  I may do this myself as I have moments where I want to practice my Freecad modelling skills and this is an opportunity to do that.  However I know that me documenting those steps will make it very hard to follow for a novice so I have decided to document the short cut version.

If you haven't already please download Fernando's Freecad file for the plate design from here:

https://github.com/fernandobrea/Alhambra_II_3D_Support/blob/master/Alhambra_II_3D_support.fcstd

Open the file and navigate to this body called 'Final Without Text':


Right click and select copy:


Select all and Click 'OK'

We are going to paste this Body into our design and then merge the two things together so create one whole part.

Navigate to our recent design and right click paste next to our body:


The screen should look like this:


We now have the central support section, button and switch cut outs and stand offs which we can join with the base to create the final part.

If we look at the models in isometric we can see that there is a slight issue:


The body we have imported is sitting below the base plate.  Lets resolve that.

Click on the Body icon in the tree so that is is highlighted and then right click and select 'Transform':


Then click on the y transformation arrow and move the base down until the base of our two parts match:


Click Ok once you are happy with the positioning.  

Now...at this point I have to admit my intention was to join these two bodies together using a Boolean Fuse Operation.  That unfortunately did not work.  So I'm going to do something sneaky...

Select everything! Click on the edit menu and choose select all:


Next Click on the file menu and select Export:


Choose to export an STL file and give the file a suitable name:


Click Save.

We have now created and STL file with both bodies and can 3D print the support for our FPGA:

I loaded up the file in Cura however any slicer program will do:


I'm printing mine in PLA but any plastic would probably work as the design is not overly complex.

I will share the results once I have printed it.  Two hours and forty minutes isn't too bad.

There are other ways I could have achieved this result.  I could have exported the central section as a step file and then re-imported it into Freecad and merged the two bodies together.  The proper thing would be to model all of the features like Fernando did so that we have a clean file to begin with.  Fernando's version appears to have issues with the version of Freecad I'm using.

I sincerely doubt any of this will matter as once printed this will work perfectly well.

If people would like the files we created for their own nefarious purposes they are here:

Alhambra 2 Stand - Google Drive Link

That is all for now.... 

Take care always - Langster!




 

Monday, 15 December 2025

Updating a 25 Year old Test Rig!

I recently (about six months ago) had to update a high power test rig.  The original test rig had been in service for over 25 years and was showing its age.  It wasn't working properly due to interference from external sources which was causing the control of the test equipment to malfunction.

The rig was used to control a power converter product during temperature and vibration stress testing.  The power converter itself would receive electrical energy from an external source and then use this electricity to generate a very stable 28 V dc which was then used to power sub systems.  I cannot provide the specific function as it is proprietary.

I won't share a photo of the units under test as that is also proprietary.  I can however discuss how the test rig was originally implemented using some custom hardware and various pieces of off the shelf instrumentation to achieve the various test signals and routines required as this is fairly standard for any test rig.

The device under test is essentially a very high power switch mode buck boost converter which takes in a range of voltage inputs and provides a steady output at a range of power requirements as needed.  Various outputs are also monitored to ensure that the device under test does not change state whilst under test.

The test regime required several different test conditions to be satisfied but essentially relied on the control of two HP 6681A power supplies to provide the simulated electrical supply from the external source. The unit under test was powered by these supplies and combined with a switch box which would satisfy the input signals needed. A large external load provided the standard operating conditions the unit would be exposed to and the outputs present would be monitored by a data-logger.

The original test rig is below:

The image shows several pieces of test equipment - 5x lab power supplies which were used to power and control the test rig, the 2x high power power supplies used to provide the power to the devices under test and 2x high power resistive loads.  A Keysight data-logger is used to monitor the device under test outputs and the data is displayed on the fly on the computer monitor on the top shelf.

The critical part of the system was the device which provided the voltage profile to control the 2x large high power supplies which we shall call the profile generator.

A rough diagram of the test rig is below:

The profile generator is the key part of the system as it is that which controls the power supply to the device under test. The signal required was a slowly changing ramp signal which began at 24.5 V dc and reduced to 12 V dc in a second and then slowly increased over 18 seconds back up to 24.5 V dc simulating a specific start condition.  The unit under test had to maintain its 28 V dc output  under load during this ramp without any of the outputs being affected.  

The original profile generator was in a very sorry state.  It had been constructed on strip board and was showing its age.  The circuitry used was also very out-dated and contained 555 timers (one of my favourite devices), 741 Operational amplifiers and an TTL logic counters used to create the time period needed to produce the ramp.  The profile of the ramp itself was hard coded with a UV erasable EEPROM, the output of which was passed to a DAC08 digital to analogue converter.  I should state that I have no issue with this implementation as it had worked for many many years without issue.  It was only recently found to be affected by external interference due to the test rig being moved from one part of the building to another.  A photo of the original profile generator is below:

There was unfortunately not a great deal of documentation for the profile generator and it had been repaired on several occasions.  The operational amplifier circuitry required positive and negative supplies which had been mis-connected a few times which destroyed the linear regulators and often took out the 555 timer and the op-amps as well. 

For weeks I attempted to find out what was causing the issue with the test rig.  The profile generator output was a negative voltage signal between 0 and -5 V dc.  This signal was passed to the voltage control input of the HP 6681A which in turn controlled the output between 12 V dc and 24.5 V dc. Various probing of points within the system with an oscilloscope showed some spurious signals which no amount of filtering or cable screening could remove. A photo of an oscilloscope trace showing the spurious signal is below:


Once I realised that I couldn't fix the current rig I decided to record the output of the original profile generator and reproduce it somehow.  I disconnected the profile generator from the test rig and recorded the output with an oscilloscope and a data-logger which allowed me to obtain the profile in comma separated values.  The traces recorded by the oscilloscope are below, the yellow trace is the output from the power supply (12 V dc to 24.5 V dc), the blue trace is the voltage control signal from the profile generator (0 V dc to -5 V dc).


From this recording I tried to program an arbitrary function generator to produce the required waveform.  Unfortunately this was also affected by the same spurious interference signal which plagued the original profile generator.  

At this point I had a bit of an epiphany and decided to use the recording of the signal to program a microcontroller with a built in digital analogue converter (DAC).  As I had one available, I used an Arduino R4.  This development board is normally used by hobbyists and students but is a perfectly functional microcontroller and is very well supported and fairly easy to program.  

The specifications of the arduino R4 are here: Arduino R4 Datasheet

The Arduino R4 had a 10 bit DAC and an op-amp which I could use to buffer the signal if needed.  It was helpfully a 5 V dc device as well which meant I could interface it with the HP Power supply without any extra circuitry or voltage manipulation required.

The first thing I did was write some code to create the voltage profile required. I had to convert the recorded 5 V dc CSV data to 1023 bit values to be able to produce the profile required but this was easily achieved with a spreadsheet formula. The microcontroller firmware worked like this:

1. Check if a trigger signal has been received.

2. If trigger is present output the voltage ramp over 18 seconds creating the ramp from hard coded bit values calculated from comma separated values recorded.

3. Light an LED to show that the profile generator is active to provide visual feedback to an operator.

4. Loop back to number 1.

I then tested the system and found it worked perfectly.  I then re-read all of the test requirements and specifications to ensure I had missed something critically required.  Luckily there didn't appear to be anything insurmountable and I proceeded to write the code:

Click here to view the Arduino Code

    
//Profile Generator Mk2
//Alex Lang

// constants won't change. They're used here to set pin numbers:

const int outputPin = A0; // DAC output pin on R4 (Analog pin A0)
const int dataSize = 51;
const unsigned long totalRampTime = 48000;
const int buttonPin = 3;  // the number of the pushbutton pin
const int ledPin = 13;    // the number of the LED pin

int buttonState = 0;  // variable for reading the pushbutton status

//Voltage data (scaled to 10 bits: 0-1023)
uint16_t voltageData[dataSize] = {
  352,773,749,722,696,667,640,614,587,561,534,507,481,452,426,399,
  372,346,319,293,293,293,293,293,293,293,293,293,293,293,293,293,
  293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,352
};

void setup() {
  analogWriteResolution(10); // Set analogWrite resolution to 10 bits  
  pinMode(ledPin, OUTPUT); // initialize the LED pin as an output
  pinMode(buttonPin, INPUT); // initialize the pushbutton pin as an input
}

void loop() {
analogWrite(outputPin, 352); // Output 10-bit value directly: equates to -1.71 V dc
read_Trigger();


}

void read_Trigger(){
  
  buttonState = digitalRead(buttonPin); // read the state of the pushbutton value
  
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == HIGH) {
    // turn LED on:
    digitalWrite(ledPin, HIGH);
    Profile_start();
  } else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

void Profile_start(){
  
  unsigned long startTime = millis();
  unsigned long currentTime = startTime;
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    analogWrite(outputPin, voltageData[i]); // Output 10-bit value directly
    elapsedTime = millis() - startTime;
    while(elapsedTime < (i+1) * delayTime){
      elapsedTime = millis() - startTime;
    }
  }
  delay(100);
}
  

  

The code worked well enough! The next stage involved checking all the product test requirements one more time with the design authority for the product as we needed to be certain that this new implementation of the profile generator wouldn't cause any issues for the reporting of the test results from the devices being tested - no issues were found.

I ended up modifying the code somewhat as it transpired that two profiles were required for the testing with two separate triggers:

Click here to view the full Arduino Code
    
//Test code for analogue voltage output via pin A0
// Author: Alexander Lang
// Date 14/05/2025
// Updated after changing PSU 1 as there appeared to be an issue.

// Variables

const int outputPin = A0; // DAC output pin on R4 (Analog pin A0).
const int apuTrigger = 5; // Trigger input from APU signal
const int pp3Trigger = 6; // Trigger input from PP3 Signal
const int dataSize = 40;  // Number of steps in APU Ramp.
const int ledPin = 13;    // Using built in LED to show when an APU trigger has been received. 
const unsigned long totalRampTime = 19000; // Time period for ramp in milli-seconds

float originalVoltageData[dataSize] = {
-2.75,-2.75,-2.7,-2.65,-2.60,-2.55,-2.50,-2.45,-2.40,-2.35,
-2.30,-2.25,-2.20,-2.15,-2.10,-2.05,-2.00,-1.95,-1.90,-1.85,
-1.80,-1.75,-1.70,-1.65,-1.60,-1.55,-1.50,-1.45,-1.40,-1.35,
-1.30,-1.25,-1.20,-1.15,-1.10,-1.10,-1.10,-1.10,-1.10,-1.10,
};

//Scaled 10-bit voltage data (0-1023)
uint16_t scaledVoltageData[dataSize];

// Setup function to set pin states

void setup() {
    Serial.begin(9600); // Set serial Monitor Running at 9600 baud
    //analogWriteResolution(12); // Set analogWrite resolution to 12 bits
    pinMode(apuTrigger, INPUT);  // Configure pin as an input
    pinMode(pp3Trigger, INPUT);  // Configure pin as an input
    pinMode(ledPin, OUTPUT);     // Configure pin as an output

    analogWriteResolution(10);   // Set analogWrite resolution to 10 bits

  //Scale the original voltage data to the 10-bit range (0-1023)
  float minValue = 0; // Find the minimum voltage
  float maxValue = -4.75; // Find the maximum voltage

  for (int i = 0; i < dataSize; i++) {
     //Use the map function to linearly scale the voltage to the 0-1023 range
    scaledVoltageData[i] = map(originalVoltageData[i] * 100, minValue * 100, maxValue * 100, 0, 1023);
     //We multiply by 100 to work with integers in the map function for better precision
  }
}

void loop() {

analogWrite(outputPin, 336); //Set output 336 to default to -1.564 V to ensure a 20.5 Volt output at the agilent PSU - CR16514
delay(100);                  //Delay to ensure no issues

readAPUTriggerState(); //Read the APU trigger state.  If a trigger has been received output the profile.
readPP3TriggerState(); //Read the APU trigger state.  If a trigger has been received output the profile.
}

void readAPUTriggerState(){
byte apuTriggerState = digitalRead(apuTrigger);

if (apuTriggerState == HIGH) {
      Serial.println("APU Trigger Received");
      digitalWrite(ledPin, HIGH);
      APU_start();
  }
  else {
      Serial.println("APU Trigger not received");
      digitalWrite(ledPin, LOW);
  }
  delay(100);
}

void readPP3TriggerState(){
byte pp3TriggerState = digitalRead(pp3Trigger);

if (pp3TriggerState == HIGH) {
      Serial.println("PP3 Trigger Received");
      digitalWrite(ledPin, HIGH);
      PP3_start();
  }
  else {
      Serial.println("PP3 Trigger not received");
      digitalWrite(ledPin, LOW);
  }
  delay(100);
}

void PP3_start(){
 unsigned long startTime = millis();
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    Serial.print("PP3 in operation");
    Serial.println();
    analogWrite(outputPin, 200);  //Set profile generator to provide 0.93 V dc which equates to a 24.5 V dc output at the agilent PSU CR
    delay(300);
    elapsedTime = millis() - startTime;
    while (elapsedTime < (i + 1) * delayTime) {
      elapsedTime = millis() - startTime;
    }
  }
  delay(10); // Small delay at the end
}

void APU_start(){
  unsigned long startTime = millis();
  unsigned long elapsedTime = 0;
  unsigned long delayTime = totalRampTime / dataSize;

  for (int i = 0; i < dataSize; i++) {
    Serial.print(scaledVoltageData[i]);
    Serial.println();
    analogWrite(outputPin, scaledVoltageData[i]);
    delay(300);
    elapsedTime = millis() - startTime;
    while (elapsedTime < (i + 1) * delayTime) {
      elapsedTime = millis() - startTime;
    }
  }
  delay(10); // Small delay at the end
} 

I then switched to designing an enclosure for the new profile generator.  As time was of the essence and I didn't have access to machining services I couldn't use an off the shelf enclosure.  I designed something bespoke which could be 3D printed.  There are distinct benefits to doing this.  Everything fits first time without needing any machining or post processing and all the internal components should fit perfectly.

I used Freecad to design the enclosure as I didn't have access to a commercial cad package although I could have used Solidworks if more time had been available.  In previous projects I have created a parametric box design where I can set the required internal dimensions and a program then creates an enclosure and lid for me.  All that is then left to do is to create some internal support for the arduino R4 development board and holes for the various 4 mm banana connectors and LEDS present.


The 3D printer was then pressed into service and a case was created in less than a day.  Construction took less than 30 minutes and for once no further iterations were required...I will admit I often need more than one attempt at mechanical design...but that is the beauty of rapid prototyping, further iterations are cheap and easy to achieve assuming one has the time available.

The final result looks like this:

It has been in use ever since and I not heard of any issues or anything other than positive feedback from the production test department.

Because my profile generator used an off the shelf USB C power supply it was possible to remove 4x of the external lab power supplies from the test rig as these were being used to satisfy the positive and negative power requirements from the original profile generator.  These power supplies are being used in other areas within the test department.

I have been informed that the HP power supplies used originally in the test rig are to be replaced as they are 'long in the tooth'.  The new power supplies have been selected to work with the profile generator as it works exactly as intended - quite the endorsement!

That's all for now - Langster!