Close
0%
0%

Electronic Load 3.3V-16V 1A

Electronic load that supports 3.3V-16V at 1A of current. Equipped with keypad, LCD, rotary encoder, STM32 Microcontroller and more!

Similar projects worth following
Electronic load that supports 3.3V-16V at 1A of current. My goal was to develop a useful tool for my bench, while learning many aspect of electronics engineering. I could have made a simple electronic load, but I also wanted to create a useful development environment for the STM32 micro-controller family. I have not used the STM32 micro-controller family before, and would like to take advantage of its powerful capabilities, while branching away from the typical Arduino environment I have become comfortable with. Some of the features include a DAC (Digital to Analog Converter), custom keypad, rotary encoder, LCD module, voltage measurements, current measurements, temperature measurements, buzzer, USB to Serial interface & RGB LED.

I will be adding project details once I have the project a bit more finalized.  I plan on adding very detailed descriptions for each circuit, but would like to avoid re-doing a bunch of stuff if changes are required.

Please follow project logs in short term for project related details.  I plan on providing updates after completion of major milestones.

x-zip-compressed - 211.36 kB - 12/01/2019 at 19:27

Download

Adobe Portable Document Format - 268.95 kB - 12/01/2019 at 19:27

Preview
Download

OpenDocument Spreadsheet - 23.82 kB - 12/01/2019 at 19:27

Download

  • 1 × See Attached BOM See Attached BOM

  • Updating DAC Reference

    schwarzrmsu02/23/2020 at 19:02 0 comments

    In my last post I explained how transitioning from the external 3.3V reference to the internal 4.096V reference will help increase the accuracy of the DAC's output voltage.  In this post we are actually going to update the HW and SW in order to transition from the external reference to the internal reference.

    In order to use the DAC's internal 4.096V reference, we need to power the DAC from a voltage source that is higher in voltage.  Unfortunately the only two voltage rails we currently have available are 12V & 3.3V.  12V is too high for the DAC, and 3.3V is too low to use the internal reference.  Therefore we need to add a 5V rail which will be used to power the DAC.  5V is perfect because its higher than 4.096V, and lower than the maximum allowable voltage for the DAC.

    This type of update requires a mod board, therefore I designed one.  Below is a screen shot of the schematic:

    As you can see I am using a simple TLV76050DBZ 5V linear regulator, and I also added in a MIC803-46D3VC3 voltage monitor.  The voltage monitor will pull the reset line low when it detects the 5V regulator has dropped out.

    I designed a very small PCB that can be glued to the existing PCBA, and then wired in by soldering wires between mod board and main PCBA.  Below are screen shots of the mod board:

    Board Layout:

    3D Rendering:

    Hand Soldered PCBA:

    Now we will take this mod board and glue it to the PCBA in an area that does not have any components, and is close to the nets that we need to solder to.  Unfortunately I did not have an amazing place, but I settled on this location:

    Now we need to wire the mod board to the PCBA.  We will need to connect Ground, 12V, Reset and 5V.  Below is a walk through on how to do so:

    First we are going to route the wire for ground.  Locate the mod board PCBA:

    Locate the ground connection on the mod board PCBA:

    Solder a wire to the ground connection:

    Locate area of main PCBA with ground connection:

    Locate ground connection:

    Solder other end of wire to ground connection:

    Look over ground connection wiring:

    Next we are going to route the wire for 12V.  We can start by locating the mod board PCBA:

    Locate 12V connection on mod board PCBA:

    Solder a wire to the 12V connection:

    Locate area of main PCBA with 12V connection:

    Locate 12V connection:

    Solder other end of wire to 12V connection:

    Look over 12V connection wiring:

    Next we are going to route the wire for Reset.  We can start by locating the mod board PCBA:

    Locate reset connection on mod board PCBA:

    Solder a wire to the reset connection:

    Locate area of main PCBA with reset connection:

    Locate reset connection:

    Solder other end of wire to reset connection:

    Look over reset connection wiring:

    Lastly we are going to route the wire for 5V.  First we need to cut the trace that was originally connecting 3.3V to the DAC:

    Locate the area of main PCBA with this trace:

    Cut the trace:

    Now that the trace is cut, we can route the wire for 5V.  We can start by locating the mod board PCBA:

    Locate 5V connection on mod board PCBA:

    Solder a wire to the 5V connection:

    Locate area of main PCBA with 5V connection:

    Locate & Solder 5V connection:

    Look and review all of the wiring.  It is suggested to ring out each connection to check soldering:

    Lastly we need to update the SW to use this internal reference.  Below is the SW in order to generate 1V at the DAC output:

    uint8_t DAC_writeToAndUpdateDACRegisterCmdCode = 0b0011;
    uint8_t DAC_selectInternalReferenceCmdCode = 0b0110;
    
    uint16_t DAC_vSetData_u16 = 999;
    uint8_t DAC_dataUpperByte_u8 = 0;
    uint8_t DAC_dataMiddleByte_u8 = 0;
    uint16_t DAC_dataMiddleByte_u16 = 0;
    uint8_t DAC_dataLowerByte_u8 = 0;
    
    DAC_dataUpperByte_u8 = DAC_selectInternalReferenceCmdCode << 4;
    HAL_GPIO_WritePin(DAC_CS_DO_PORT,...
    Read more »

  • DAC Voltage Reference Worst Case Circuit Analysis

    schwarzrmsu02/16/2020 at 14:24 0 comments

    I noticed when writing my last post Digital To Analog Converter Control Using SPI that the voltage reference that I planned on using was not going to be possible.  I planned on using the internal 4.096V reference of the LTC2630, but I mistakenly only powered my DAC from a 3.3V voltage regulator.  Therefor in the short term I set my reference to be the supply voltage of 3.3V.  In this post I am going to explain why I want to use the 4.096V reference.

    The reason the voltage reference for the DAC is so important, is because the voltage reference can be a large contributor to the accuracy of the DAC's output.  DAC's simply take your reference voltage, and split it up into a number of steps, depending on the number of bits.  The LTC2630 is a 12 Bit DAC, therefore it splits the reference into 4095 steps.

    The voltage reference accuracy is important because the SW assumes the reference is perfect, but in reality the reference voltage could be slightly different due to a number of circumstances.  This is probably best explained with some examples, therefore I will show the worst case error when using the external 3.3V reference and the internal 4.096V reference.  Lets say we are trying to target a DAC output voltage of 1V in both examples.

    External 3.3V Reference Worst Case Circuit Analysis

    Below is an equation that will be done in SW in order to determine the correct number of Counts to send to the DAC in order to generate 1V:

    The software has to assume that the reference is perfectly 3.3V, but in reality it can be different from 3.3V.  This difference is determined by the worst case accuracy of the external 3.3V reference.  The 3.3V reference in our case is a simple Linear Regulator from ON Semiconductor (NCP1117DT33T5G):

    The output voltage range is defined in the datasheet in this section:

    With this information we can calculate the worst case output voltage when the reference is at its maximum or minimum output voltage:

    Internal 4.096V Reference Worst Case Circuit Analysis

    Below is an equation that will be done in SW in order to determine the correct number of Counts to send to the DAC in order to generate 1V:

    The software has to assume that the reference is perfectly 4.096V, but in reality it can be different from 4.096V.  This difference is determined by the worst case accuracy of the internal 4.096V reference.  The 4.096V reference in our case is inside the LTC2630 DAC:

     "The reference is INSIDE the DAC!" (Zoolander reference, pun intended)

    Image result for zoolander the files are in the computer

    This is where things get hard to compare apples to apples, but the datasheet leads me to believe that the error introduced by the internal reference is specified here in the datasheet for the DAC:

    We are designing for a maximum ambient temperature of 70C, therefore the worst case reference error should be around 0.07%.  This gives a minimum voltage of 4.093V and a maximum of 4.098V.

    With this information we can calculate the worst case output voltage when the reference is at its maximum or minimum output voltage:

    As you can see the error is significantly less when using the internal reference of the DAC.  Minimizing this error is very important because this DAC Output voltage accuracy will directly translate to load current accuracy.  My goal is for this electronic load to be very accurate, therefore I want to enable every option that will increase the accuracy of the load current.

    Please note that there are definitely other contributors to DAC Output voltage error, but the premise of this log was to compare the references and their contribution to the error.  If you wish to see a more lengthy explanation of an overall worst case analysis of output error, please let me know.

    I hope you enjoyed my post.  Thank you for reading.  Please feel free to leave a comment below.

    Thanks again.

  • Digital To Analog Converter Control Using SPI

    schwarzrmsu01/26/2020 at 15:02 0 comments

    Sorry for not posting in a while.  I just got back from Iceland, and dumped water all over my laptop.  I am excited to give and update after a long trip and a laptop covered in rice.

    The next challenge that I want to take on is the Digital To Analog Converter (DAC):

    Digital to analog converters are cool devices.  DAC's simply output a voltage somewhere between the power and ground reference you provide.  The voltage the DAC outputs is based on the message you send it via SPI.  This is a 12 bit DAC which means there are the following number of steps:

    Therefore we have the following resolution between 3.3V and ground:

    The first thing we are going to do is set the reference voltage to the 3.3V supply we are providing the DAC.  This can be done using the following Command Code outlined in the LTC2630 datasheet:

    Now that we know the Command Code we need to understand how the rest of the SPI message needs to be structured.  The message structure is shown below:

    I have broken up the 24 bit message into 3 8-bit variables that I can send in succession.

    1. DAC_dataUpperByte_u8

    2. DAC_dataMiddleByte_u8

    3. DAC_dataLowerByte_u8

    For this first message, the DAC is simply going to receive the command code for "Select Supply as Reference" and ignore the rest of the data.  Therefore we simply need to package DAC_dataUpperByte with this particular command code, and then send zeros for the remaining bits.  The tricky piece is packaging the upper byte.  This requires some bit shifting operations.  Below is the code that accomplishes this:

    uint8_t DAC_selectSupplyAsReferenceCmdCode = 0b0111;
    
    uint8_t DAC_dataUpperByte_u8 = 0b00000000;
    uint8_t DAC_dataMiddleByte_u8 = 0b00000000;
    uint8_t DAC_dataLowerByte_u8 = 0b00000000;
    
    DAC_dataUpperByte = DAC_selectSupplyAsReferenceCmdCode << 4;
    

    What this code does is takes a uint8_t variable named DAC_dataUpperByte_u8 and sets all bits within the register to 0:

    Then we place DAC_selectSupplyAsReferenceCmdCode into DAC_dataUpperByte_u8 register:

    Then we shift DAC_selectSupplyAsReferenceCmdCode 4 bits to the left in order to get it into the correct position within the LTC2630 SPI data structure:

    Now DAC_dataUpperByte_u8, DAC_dataMiddleByte_u8 & DAC_dataLowerByte_u8 are ready to be sent over the SPI bus from the micro controller to the DAC in order to change the internal reference to our 3.3V supply:

    HAL_GPIO_WritePin(DAC_CS_DO_PORT, DAC_CS_DO_PIN, RESET);
    HAL_SPI_Transmit(&hspi1, &DAC_dataUpperByte_u8, 1, 10);
    HAL_SPI_Transmit(&hspi1, &DAC_dataMiddleByte_u8, 1, 10);
    HAL_SPI_Transmit(&hspi1, &DAC_dataLowerByte_u8, 1, 10);
    HAL_GPIO_WritePin(DAC_CS_DO_PORT, DAC_CS_DO_PIN, SET);

    This code:

    1. Pulls the CS pin on the DAC low

    2. Sends DAC_dataUpperByte_u8 via SPI

    3. Sends DAC_dataMiddleByte_u8 via SPI

    4. Sends DAC_dataLowerByte_u8 via SPI

    5. Pulls the CS pin the DAC high

    The SPI message can be seen on a scope in the following way:

    Upper Byte:

    Middle Byte:

    Lower Byte:

    Now that we have the reference set, we can set a particular output voltage.  For this example we will attempt to set the DAC output voltage to 1.95V.  

    The first thing we need to do is figure out the command code for setting the output voltage:

    Luckily we can use the same bit shifting operation for the upper byte.

    Next we need to figure out the number of counts:

    Below is the conversion from 2420 decimal to binary:

    This is where things get tricky.  We need to take a 12 bit binary message a split it into two bytes named DAC_middleByte_u8 and DAC_lowerByte_u8.

    Below is the code for packaging DAC_middleByte_u8:

    uint16_t DAC_vSetData_u16 = 0b100101110100;
    uint8_t DAC_dataMiddleByte_u8 = 0b00000000;
    uint16_t DAC_dataMiddleByte_u16 = 0b0000000000000000;
    
    DAC_dataMiddleByte_u16 = DAC_vSetData_u16 >> 4;
    DAC_dataMiddleByte_u8 = DAC_dataMiddleByte_u16;

    This...

    Read more »

  • Key Pad Loop Times

    schwarzrmsu01/16/2020 at 23:57 0 comments

    In my previous project log I developed a function to read the key pad.  In this function there were some necessary delays in order to allow the digital output to fully charge the capacitor on the digital input filter before checking the digital input status (check out more details on the keypad in my previous project log).  This delay is based on the RC time constant of the RC low pass filter I have placed in front of each digital input for the key pad.

    Below is a visual example of whats happening in the circuit:

    You can see that the signal at TP7 and TP23 are instantaneous because there is no deliberate capacitance tied to that node.  Therefore the rise and fall times are going to be "instantaneous".  The problem is the signal at the digital input is not "instantaneous" because of the resistance/capacitance combination.  This resistance/capacitance combination is called a passive low pass filter.  Its a cheap and effective way of filtering out unintended high frequencies that you do not want present at your digital input.  These high frequencies can come from a number of sources, but typically it is outside RF being coupled into the trace acting like an antenna.

    In order to use this low pass filter, we need to wait for the signal to catch up before we read it.  This time is based off the RC time constant of the low pass filter.  The RC time constant formula is below:

    For our circuit we have to consider R as the two 10k resistors, and C as the 0.01uF capacitor.  It is best practice to use the worst case value when running an analysis to ensure its compatible across all possible conditions.  For this scenario the worst case condition is at the maximum resistance & capacitance values.  Our maximum component values are calculated below:

    Unfortunately 1T is not enough time since that will only allow the voltage to reach 63% of the steady state value.  Its best to wait at least 5T in order for the capacitor in the low pass filter to fully charge before reading the input.  This is shown best in the figures below:

    Therefore the delay we need to wait after each row is powered is calculated below:

    I rounded this value up to an even 1200us and tested the response:

    Here you can see the Digital Ouput Voltage (CH1) is instantaneous and the voltage at the Digital Input (CH2) has reached its steady state.  This is exactly what you want to see in order to ensure the voltage you we monitoring has reached its steady state.

    The problem is this takes place 4 times during the readKeyPad() function which gives us a worst case function delay of  ~4.8ms.  I measured this by executing the following code and taking a measurement on the green LED control:

         if (mainMillis - readKeyPadMillis >= 10)
          {
              turnOnGreenLED();
              readKeyPad();
              turnOffGreenLED();
              readKeyPadMillis = mainMillis;
          }
    

    This function will set the green LED control high, run the readKeyPad() function, and then set the green LED control low.  I did this an probed the green LED control to measure the time it takes to execute this function:

    As you can see, it takes 4.81ms to execute the readKeyPad() function.  This is too long in my opinion, therefore we need to make some HW changes in order to reduce the delays required for this feature.  We either need to reduce the low pass filter resistance or capacitance in order to reduce the RC time constant which directly correlates to the required delay.  This will move the corner frequency of our low pass filter, but this trade off is definitely worth it.  I decided to keep the resistance the same, but to replace the 0.01uF capacitors with 0.001uF capacitors:  

    First we had to find C14, C15, C16 & C17 on the PCBA:

    Remove these capacitors from the PCBA:

    Solder 0.001uF capacitors:

    Below is a waveform with the same delay but new capacitors:

    ... Read more »

  • Key Pad Fun Times

    schwarzrmsu01/16/2020 at 03:40 0 comments

    Now that I have figured out digital outputs & delays, the next logical step would be to play with those new features while introducing digital inputs as well.  A portion of the design that requires digital output controls, reading digital inputs and delays is the key pad.  In order to understand the keypad software, I will give an explanation of the keypad hardware.

    The key pad consists of 16 push buttons:

    Schematic View:

    PCBA View:

    The simplest approach would be to tie all 16 push buttons to a voltage source (3.3V) and route the normally open end of each push button to a discrete digital input.  The problem with this approach is it would require 16 GPIO pins on the micro which is a lot for one feature.  In order to minimize the required number of GPIO, I used a multiplexed approach which only requires 8 GPIO.

    The best way to explain the multiplexed approach is to go through an example.  I will show you how the micro-controller will determine that the 9 key (push button 11) is being pushed.  

    The micro-controller is going to individually power each row, one at a time using digital outputs I named KEY_PAD_R1, KEY_PAD_R2, KEY_PAD_R3 & KEY_PAD_R4.  While each row is getting powered individually, the micro-controller is going to check each column one at time using digital inputs I named KEY_PAD_C1, KEY_PAD_C2, KEY_PAD_C3 & KEY_PAD_C4.

    When KEY_PAD_R1 digital output is high, this is what the micro-controller will see when the 9 key is pushed:

    You can see all digital inputs are reading 0V which is an indication that the 1, 2, 3 or ESC keys are not being pushed.

    When KEY_PAD_R2 digital output is high, this is what the micro-controller will see when the 9 key is pushed:

    You can see all digital inputs are reading 0V which is an indication that the 4, 5, 6 or 0 keys are not being pushed.

    When KEY_PAD_R3 digital output is high, this is what the micro-controller will see when the 9 key is pushed:

    You can see in this scenario, the KEY_PAD_C3 digital input is reading 3.3V.  The micro will have the 9 key mapped to this condition of KEY_PAD_R3 digital output high & KEY_PAD_C3 digital input high.  In fact all keys will have a unique combination that will allow to the micro-controller discretely identify when each key is being pressed.

    Lastly when KEY_PAD_R4 digital output is high, this is what the micro-controller will see when the 9 key is pushed:

    You can see all digital inputs are reading 0V which is an indication that the ISET, ENTER, NULL or ON/OFF keys are not being pushed.

    The logic in the micro-controller can be mapped to this table:

    KEY_PAD_R1 Digital Ouput High & KEY_PAD_C1 Digital Input High = 1 Key is pressed

    KEY_PAD_R1 Digital Ouput High & KEY_PAD_C2 Digital Input High = 2 Key is pressed

    KEY_PAD_R1 Digital Ouput High & KEY_PAD_C3 Digital Input High = 3 Key is pressed

    KEY_PAD_R1 Digital Ouput High & KEY_PAD_C4 Digital Input High = ESC Key is pressed

    KEY_PAD_R2 Digital Ouput High & KEY_PAD_C1 Digital Input High = 4 Key is pressed

    KEY_PAD_R2 Digital Ouput High & KEY_PAD_C2 Digital Input High = 5 Key is pressed

    KEY_PAD_R2 Digital Ouput High & KEY_PAD_C3 Digital Input High = 6 Key is pressed

    KEY_PAD_R2 Digital Ouput High & KEY_PAD_C4 Digital Input High = 0 Key is pressed

    KEY_PAD_R3 Digital Ouput High & KEY_PAD_C1 Digital Input High = 7 Key is pressed

    KEY_PAD_R3 Digital Ouput High & KEY_PAD_C2 Digital Input High = 8 Key is pressed

    KEY_PAD_R3 Digital Ouput High & KEY_PAD_C3 Digital Input High = 9 Key is pressed

    KEY_PAD_R3 Digital Ouput High & KEY_PAD_C4 Digital Input High = . Key is pressed

    KEY_PAD_R4 Digital Ouput High & KEY_PAD_C1 Digital Input High = ISET Key is pressed

    KEY_PAD_R4 Digital Ouput High & KEY_PAD_C2 Digital Input High = ENTER Key is pressed

    KEY_PAD_R4 Digital Ouput High & KEY_PAD_C3 Digital Input High = NULL Key is pressed

    KEY_PAD_R4 Digital Ouput High & KEY_PAD_C4 Digital Input High = ON/OFF...

    Read more »

  • Millis Function

    schwarzrmsu01/13/2020 at 02:38 0 comments

    In order to avoid large pockets of delay, I will need to generate a function that will allow me to execute specific actions only after a certain amount of time has expired.  In arduino, this can be accomplished using the millis() function.  Since I am using the STM32 micro-controller and the STM32 IDE, I do not have the luxury of using this library function.  Therefore I have created my own millis function.  Probably not as good, but seems to be working pretty well. 

    The first step was to enable a new timer that counts up at a rate of 100kHz (In other words it counts to 100 every 1ms):

    static void MX_TIM7_Init(void)
    {
      htim7.Instance = TIM7;
      htim7.Init.Prescaler = 479; //479
      htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
      htim7.Init.Period = 65535;
      htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
      if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
      {
        Error_Handler();
      }
    }

    This function configures a timer to count up to 100 every 1ms.  The reason I have slowed down this timer is to prevent the 16 bit unsigned integer from over flowing too quickly.  This timer can overflow after following duration:

    Once all of my code is complete, I can start to bench mark the amount of time it takes to execute the entire stack.  With that information I can fine tune my timer frequency, but for now I will conservatively set the frequency to 100kHz.

    Now that I have a timer that counts to 100 every 1ms, I have created a function that counts how many times this happens:

    uint32_t millis(void)
    {
        uint16_t CURRENT_TIMER7_COUNT = __HAL_TIM_GET_COUNTER(&htim7);
    
        uint16_t currentMillis = 0;
        if(CURRENT_TIMER7_COUNT>=100)
        {
            currentMillis = CURRENT_TIMER7_COUNT/100;
            systemMillis = systemMillis + currentMillis;
            __HAL_TIM_SET_COUNTER (&htim7, 0);
        }
        return systemMillis;
    }

    This function checks the current count at the time this function is called.  If the count is larger than 100 it increases a variable called systemMillis by 1.  systemMillis is the current number of milliseconds the application has been running.  If the counter is increased it resets the counter to prevent it from overflowing.  systemMillis is a 32 bit unsigned integer which will take a long time to over flow.  The systemMillis variable will overflow after this specified amount of time:

    This is an acceptable amount of time to run this particular application with requiring a reset.  Once this timer expires I plan on resetting all the Millis related variables which might cause a slight timing mishap, but should not be noticeable to the user if the user has left application running longer than 49.71 days.

    I then tested this function within main while(1) by blinking an LED on and off and probing the LED DO port:

    mainMillis = millis();
    
    if (mainMillis - turnOnRedLedMillis >= 1000)
    {
        HAL_GPIO_TogglePin(DEBUG_RGB_LED_R_DO_PORT, DEBUG_RGB_LED_R_DO_PIN);
        turnOnRedLedMillis = mainMillis;
    }

     1s:

    100ms:

    10ms:

    5ms:

    1ms:

    These results are adequate for my needs.  This new function will help me execute portions of the code only when they really need to be execute.  I think this will be most useful for low priority portions of the code being executed less then portions that are higher priority.

    There are many uses for this function and you will be sure to see it future updates regarding SW.

    Hope you found this interesting, and if you have any ideas that would work better please let me know.  Thank you for reading.

  • Programming Header Simplification

    schwarzrmsu12/21/2019 at 04:53 0 comments

    I have been getting annoyed with my current programming header connections:

    The connections need to be individually un-done and re-done if you want to disconnect the programming tool from the PCB. Therefor i have decided to make an adapter board that would make my programming interface directly compatible with the main 14 pin header on the STLINK-V3:

    Image result for st link v3

    Below is the schematic I created:

    Below is the board I designed:

    I ordered these boards on OSH Park, and took advantage of the cool "After Dark" option that is currently available:

    This board construction is really cool because it uses a dark substrate and a clear soldermask.  This makes it look like the copper is exposed, but it is in fact under a layer of clear solder resist material.

    The construction on these boards is really incredible.  The copper etching and silk screen definition is insanely good.

    I then soldered the connector to the board, and this jumper board to my PCB:

    I then connected to the STLINK-V3 using the 14 way ribbon cable as mentioned earlier:

    After a quick test, I was able to connect to my micro-controller and successfully flash the micro-controller.  This makes disconnecting and reconnecting the STLINK-V3 much easier.  This also cleans up my bench setup as well.

    Hope you found this interesting.  Thanks for reading.

  • Hello World - LCD Edition

    schwarzrmsu12/19/2019 at 02:57 0 comments

    The LCD when powered looks so cool, lets make it say something now!  

    This has definitely been the hardest part yet, and will most likely be a work in progress as the SW matures.  Later versions are going to take a number of inputs and map them onto the display to show information related to the electronic load performance.  For now I simply want to create a function that will print a string onto the LCD.  I started by generating functions to initialize the LCD functionality:

    void LCD_functionSet(void)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);    //RS
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET);    //RW
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);    //DB7
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);    //DB6
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);        //DB5
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);        //DB4
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);        //DB3
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);    //DB2
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);    //DB1
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);    //DB0
    
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);    //E
        delay_us(1);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET);    //E
        delay_us(40);
        return;
    }
    
    void LCD_displayOnOffControl(void)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);    //RS
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET);    //RW
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);    //DB7
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);    //DB6
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);    //DB5
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);    //DB4
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);        //DB3
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);        //DB2
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);    //DB1
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);    //DB0
    
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);    //E
        delay_us(1);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET);    //E
        delay_us(40);
        return;
    }
    
    void LCD_clearDisplay(void)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);    //RS
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET);    //RW
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);    //DB7
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);    //DB6
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);    //DB5
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);    //DB4
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);    //DB3
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);    //DB2
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);    //DB1
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_SET);    //DB0
    
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);    //E
        delay_us(1);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET);    //E
        delay_ms(2);
        return;
    }
    
    void LCD_entryModeSet(void)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);    //RS
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_RESET);    //RW
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);    //DB7
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);    //DB6
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);    //DB5
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);    //DB4
    
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);    //DB3
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);        //DB2
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);        //DB1
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_12, GPIO_PIN_RESET);    //DB0
    
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_SET);    //E
        delay_us(1);
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET);    //E
        delay_us(40);
        return;
    }

    Now that I have initialized the LCD, I can send commands for the different characters and it will print the characters onto:

    I started with a function to print the letter A:

    void LCD_print_A(void)
    {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET);    //RS
     HAL_GPIO_WritePin(GPIOC,...
    Read more »

  • Hello World - UART Edition

    schwarzrmsu12/19/2019 at 02:04 2 comments

    The next goal for me is to get my UART running.  This will be a huge accomplishment since using a serial monitor is going to be very helpful when debugging code.  I found two really helpful functions online which allow you to pass a character array (char) and it will send the string via the defined UART interface:

    void UART_printString(UART_HandleTypeDef *huart, char _out[])
    {
        HAL_UART_Transmit(huart, (uint8_t *) _out, strlen(_out), 10);
    }
    
    void UART_printlnString(UART_HandleTypeDef *huart, char _out[])
    {
        HAL_UART_Transmit(huart, (uint8_t *) _out, strlen(_out), 10);
        char newline[2] = "\r\n";
        HAL_UART_Transmit(huart, (uint8_t *) newline, 2, 10);
    }

    I plugged in my USB connection and passed a simple "Hello World" string into the UART_printlnString() function:

    UART_printlnString(&huart2,"Hello World");
    delay_ms(1000);

    Unfortunately this did not work right away.  Through some quick trouble shooting, I noticed the USB VBUS fuse F3 had blown open:

    I knew this to be the case because I could measure 5V at J70 when connected to my computer, but J72 was reading ~0V.  After some head scratching, I looked into the details of my USB ESD protection U9 a bit further.  What I noticed is my silk screen did not have any pin 1 identification, and this device definitely needs to be oriented correctly when soldered:

    After some further investigation, I did in fact solder D9 backwards which results in a forward biased diode directly from VBUS to ground through fuse F3.  I corrected the issue by simply removing D9 for now, and replacing F3 with a new fuse.  (I will be fixing the silkscreen in future revisions of the PCB)

    After this change I got the re-assuring ba-da-da-boo from my PC that it recognized a USB device was plugged in.  

    I opened my serial monitor and wah lah:

  • Delays

    schwarzrmsu12/13/2019 at 00:45 0 comments

    One of the first things I do in a SW project is identify the delay functions I wish to use, and test them.  I have not used and STM32 mico before, therefore I am going to test the performance of the HAL_Delay() function:

    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
    HAL_Delay(1000);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
    HAL_Delay(1000);

    I measured each delay pulse width using a scope at 1s, 100ms, 10ms, 5ms & 1ms.

    1s:

    100ms:

    10ms:

    5ms:

    1ms:

    At longer delays >100ms I really like the HAL_Delay() function.  At lower delays <10ms I noticed there is nearly a +1ms error that may add a significant amount of overall delay if used often.  Also there is no option to go into a microsecond time frame which will definitely be necessary when writing functions for the LCD.  When driving the LCD, every character write requires a 40us delay, but if using the HAL_Delay(1), it would cause the following overall delay:

    This is a lot of delay for one type of feature, therefore I generated a micro second delay using an internal timer:

    void delay_us(uint16_t delay){
        delay = delay * 48;
        __HAL_TIM_SET_COUNTER (&htim6, 0);
        while (__HAL_TIM_GET_COUNTER(&htim6) < delay);
    }

    This function takes an integer (number of microseconds) multiplies it by 48.  Then it resets timer 6, and timer 6 starts counting up at 48MHz.  Therefore the timer counts to 48 in 1us.  The while loop waits until it counts all the way up to the limit and then returns back to where the function was called.  I made the same measurements on my delay_us() function.

    100us:

    10us:

    5us:

    1us:

    I am really happy with the delay_us() function.  Also I created a delay_ms function:

    void delay_ms(uint16_t delay){
        while(delay > 0){
            delay_us(1000);
            delay = delay - 1;
        }
    }

    This function simply takes an integer, and runs through delay_us(1000) that number of times.  Pretty simple.  Below are the measurements results:

    1s:

    100ms:

    10ms:

    5ms:

    1ms:

    I am very happy with the accuracy of the delay_ms() function as well.  I can be confident how long certain delays should be now that I understand and have measured these delays.

View all 13 project logs

  • 1
    Order PCB (Printed Circuit Board)

    The PCB is a custom designed component, therefore you will need to order this part from PCB manufacturers like https://oshpark.com/ or https://jlcpcb.com/.  The gerbers I have attached to this project are very conservatively designed in order to be directly compatible with these services.  You should be able to simply drop the zip file into the website and select the necessary settings.

    From JLPCB:

    Track the production progress and shipping until the boards arrive:

  • 2
    Order All Electrical & Mechanical Components

    The rest of the BOM items are off the shelf devices, therefore you can purchase these from a number of sources like https://www.digikey.com/ or https://www.mouser.com/.  The BOM I have attached to this project show Manufacturer, Manufacturer PN & Quantity:

    Quantity Description Manufacturer
    1 AUDIO PIEZO TRANSDUCER 12.5V SMD Murata Electronics North America
    1 CAP SMD CER 0.1UF 50V 10% X7R 0603 FLEXSAFE AVX Corporation
    1 CAP SMD CER 10UF 25V 10% X7R 1210 FLEXSAFE AVX Corporation
    15 CAP SMD CER 0.01UF 50V 10% X7R 0603 Samsung Electro-Mechanics
    1 CAP SMD AEC 10UF 35V 20% 105 Wurth Electronics Inc.
    12 CAP SMD CER 0.1UF 50V 10% X7R 0603 Yageo
    2 CAP SMD CER 47pF 50V 5% C0G/NP0 0603 Walsin Technology Corporation
    1 CAP SMD CER 4.7UF 25V 10% X5R 0805 Samsung Electro-Mechanics
    5 CAP SMD CER ESD 0.01UF 100V 10% X7R 0603 KEMET
    2 TVS DIODE 33V SMB Littelfuse Inc.
    1 DIODE SCHOTTKY 40V 1A SOD123 Diodes Incorporated
    2 TVS DIODE 3.3V SOD923 ON Semiconductor
    1 LED RGB 622NM 530NM 470NM 6SMD CREE Inc.
    1 LED GREEN CLEAR 0603 SMD Lite-On Inc.
    1 LED BLUE CLEAR 0603 SMD Lite-On Inc.
    1 DIODE ZENER 17V 500MW SOD123 ON Semiconductor
    1 FUSE 500MA 125VAC FAST 1206 Bel Fuse Inc.
    1 FUSE 2A 125VAC FAST 1206 Bel Fuse Inc.
    1 FUSE 250MA 125VAC FAST 1206 Bel Fuse Inc.
    1 FERRITE BEAD 40 OHM 0805 1LN Laird-Signal Integrity Products
    1 HEATSINK TO-220 W/PINS 1.5"TALL Aavid Thermal Division of Boyd Corporation
    1 CONN PWR JACK 2X5.5MM 24V 2.5A SOLDER CUI Inc.
    1 CONN HEADER VERT 4POS 2.54MM Harwin Inc.
    1 CONN USB MICRO B RECPT SMT R/A Amphenol ICC (FCI)
    4 TEST POINT PC MINI .040"D BLACK Keystone Electronics
    1 Test Sockets SINGLE PCB SOCK RED Deltron
    5 TEST POINT PC MINI .040"D WHITE Keystone Electronics
    1 Test Sockets SINGLE PCB SOCK BLACK Deltron
    1 MOUNTING KIT TO-220 Aavid Thermal Division of Boyd Corporation

    4 MOSFET N-CH 2.5OHM 60V 0.25A SOT-23 Rohm Semiconductor
    1 MOSFET P-CH 33mOHM 40V 3.3A DFN2020 Diodes Incorporated
    1 MOSFET N-CH 24mOHM 75V 80A LINEAR TO-220AB IXYS
    3 RES SMD 2K OHM 1% 1/10W 0603 Stackpole Electronics Inc
    2 RES SMD 100K OHM 1% 1/10W 0603 Stackpole Electronics Inc
    26 RES SMD 10K OHM 5% 1/10W 0603 Stackpole Electronics Inc
    1 RES SMD 12K OHM 5% 1/10W 0603 Stackpole Electronics Inc
    1 RES SMD 301 OHM 1% 1/2W 1206 Stackpole Electronics Inc
    1 RES SMD 931 OHM 1% 1/4W 1206 KOA Speer Electronics Inc.
    1 RES SMD 2.61K OHM 1% 1/4W 1206 KOA Speer Electronics Inc.
    1 RES SMD 442 OHM 1% 1/4W 1206 KOA Speer Electronics Inc.
    4 RES SMD 1K OHM 5% 1/10W 0603 Stackpole Electronics Inc
    2 RES SMD 4.7K OHM 5% 1/10W 0603 Stackpole Electronics Inc
    10 RES SMD 28.7 OHM 1% 1W 2512 Vishay Dale
    1 RES SMD 47 OHM 5% 1/10W 0603 Stackpole Electronics Inc
    3 RES SMD 0 OHM 1/10W 0603 Stackpole Electronics Inc
    1 RES SMD 75m OHM 1% 3W 2512 Bourns Inc.
    2 RES SMD 27 OHM 1% 1/10W 0603 Stackpole Electronics Inc
    3 RES SMD 20K OHM 1% 1/10W 0603 Stackpole Electronics Inc
    1 TRIMMER 100K OHM 5% 1/10W 1 TURN Bourns Inc.
    17 SWITCH TACTILE SPST-NO 0.02A 15V Panasonic Electronic Components
    1 ROTARY ENCODER MECHANICAL 24PPR Bourns Inc.
    1 THERM NTC 10KOHM 3984K RING LUG Vishay BC Components
    1 THERMISTOR NTC 10KOHM 3380K BEAD Murata Electronics
    1 IC OPAMP GP 4 CIRCUIT 14TSSOP Texas Instruments
    1 IC USB SERIAL FULL UART 20SSOP FTDI
    1 IC REG LINEAR 3.3V 1A DPAK-3 ON Semiconductor
    1 IC MPU SUPERVISOR 3.08V 20MS SC70-3 Microchip Technologies
    1 LCD COG CHAR 2X20 WH TRANSFL 3.3V Newhaven Display Intl
    1 IC MCU 32BIT 256KB FLASH 64LQFP STMicroelectronics
    1 IC DAC 12BIT V-OUT SC70-6 Linear Technology/Analog Devices
    1 IC CMOS 1 CIRCUIT SOT23-5 Texas Instruments
    1 IC MONITOR PWR/CURR BIDIR 0.5% 10MSOP Texas Instruments
    1 TVS DIODE 5.5V 10V SC88 ON Semiconductor
    1 CERAMIC RES 8.0000MHZ 10PF SMD Abracon LLC

    Manually load these parts into a cart and purchase the components.

  • 3
    Order Stencil & Solder Paste

    Once your PCB & components arrive, you will be ready to start assembling.  One critical custom tool will be the solder paste stencil which can be ordered using services like https://www.oshstencils.com/#%20.  The gerbers I have attached to this project are very conservatively designed in order to be directly compatible with these services.  You should be able to simply drop the zip file into the website and select the necessary settings..

    From OSH Stencils:

    Track the production progress and shipping until the stencil arrives.

    Also you will need solder paste.  I recommend using lead free, but be sure to investigate all of the usage warnings since it does contain chemicals.  I usually purchase my solder paste through OSH Stencils during checkout, but you can buy solder paste through many sources:

View all 9 instructions

Enjoy this project?

Share

Discussions

nike9307 wrote 12/17/2019 at 12:29 point

That project is really great. It really stands out head and shoulders above all of the other low power DC power loads in terms of user interface.

If i could just give some ideas for later versions (if you are considering making any more ofc.):
- Some sort of fan header. You can get greater currents that way, since you can get more power out of the system.
- Getting USB CDC driver is really easy using the STM32 tools (like the CubeMX). I'd personally go with one of the STM32F042 and skip the FTDI IC.  
Also your layout and documentation are really nice. Way nicer than some commercial products i've had to redesign. 

  Are you sure? yes | no

schwarzrmsu wrote 12/18/2019 at 03:02 point

Thank you for the kind words.  I do plan on coming out with future versions of the project.  

Currently finished the HW design on the EDU_1 revision (Engineering Development Unit), and working on developing SW.  I find it easiest to get the SW and test all the HW functionality on a single user friendly board.  I might go through a couple of EDU revisions before PROTO_1.  

In the proto phase I will try to design everything into more of a product.  So I will work a lot on picking a case and splitting up the design into multiple PCB's.  This is where adding a fan may be possible.  

Also I will probably look at higher power iterations once I have completed this project.  This project should give me many building blocks to make developing the higher power designs much easier.

Glad you like the project and thank you for the ideas.

  Are you sure? yes | no

nike9307 wrote 12/18/2019 at 08:25 point

You are welcome. I'm glad you liked my ideas.

I've always wanted to design a DC Power Load and once i have some free time i certainly will. Not because i need it, i have access to a very nice 300W Power Load. I just wanna make one. 
When that time comes i may steal your display. It has really easy to use form factor. 
I will probably be aiming for around 50W or so on mine. I'll probably use an old CPU cooler (LGA775 has some nice, cheap coolers) for the power transistor. Or i may go with an extruded aluminum cooler with 4-6 transistors. I dont know. 
I just had another idea - The LOAD_RESISTOR is your current measuring signal. Buffer, that signal (without the low pass filter and maybe amplified a bit) and have it go to a male BNC connector. That way you can plug your scope with a BNC-BNC Cable and see current in real time, not just averaged. Just dont forget the 50R termination. 
I'm sorry, if i'm intruding in your project, just some random ideas i have.

Best of luck with the software and with the future revisions of the board. 

  Are you sure? yes | no

Dan Maloney wrote 12/02/2019 at 16:57 point

Thanks for the great documentation. I learn a ton just looking at schematics that are nicely laid out like that.

  Are you sure? yes | no

schwarzrmsu wrote 12/03/2019 at 03:47 point

Thank you for the compliment.  I do my best to document my projects.  I have been working on a few over the past few years, but never decided on a platform to share them.  Hackaday.io seems to be exactly what I was looking for.  Slowly catching up with uploading content, but this is actually one of my newer projects.  Glad to hear other people can benefit from my work as well.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates