Project: Active load with microcontroller – Part 4: Firmware

This is a follow-up post to my previous project page Part 3 (Schematic, layout and pictures).

At this point the hardware development is finished and fortunately nothing blew up as I had plugged in a voltage source for the first time. 😉 But of course there is no functionality yet. The load sinks (nearly) no current which is a good sign because all port pins of the microcontroller are in a high impedance state while there is no firmware on it. That means the setpoint current and also the actual current of the current control loop are (nearly) zero (the opamp input “sees” ground). This is considered a safe state as nothing bad can happen in this case: no functionality – but also no potential to destroy something.

One major work package of this project is the development of the microcontroller firmware. It is mainly written in C++ while the STM32 library parts (developed by ST) are kept in C. Since the memory of the controller is somewhat limited (64 KB flash and 16 KB RAM), the firmware is based on a bare-metal approach: so no operating system.

There are no real-time constraints, so everything is cleanly organised in modules which are called sequentially in a main loop.

The following modules are part of the loop and are called in this order. The basic functions are discussed below.

Module: Input sense

This module contains the “eyes and ears” of the device. Both actual current and actual voltage are sampled by the internal ADC and are filtered here using an exponential moving average filter (EMA filter). The resulting values are converted into physical units and corrected by linear functions that have been determined during the calibration process (more on that later). Furthermore the average power is calculated (which is of course just the multiplication of current and voltage) and every 5 seconds a temperature reading is taken. A flag is set if the temperature exceeds 70°C.

All gathered pieces of information are stored in a SystemState object which is basically a container that represents all known/measurable states of the device.

Module: HMI USB

If a host is connected via USB to the device, a virtual COM port (CDC class) is made available. I believe in Windows 10 the driver is even automatically installed and loaded. In former Windows versions you need the virtual COM port driver that can be found on ST’s web site.

The active load device supports an SCPI-like syntax. It is basically a simple ASCII request-response protocol supporting commands and queries. All important data and functions are accessible via this interface.

To query the actual current, you have to enter MEAS:CURR? (+new line) and you get 1234 mA as an answer for example. To change the setpoint current to 1 A, you enter CURR 1000. The currently supported commands and queries will be attached soon.

Module: HMI Front

This module is responsible for the interaction between the user and the device. All useful data are displayed in 4 rows on the LCD: Actual current, setpoint current, actual voltage, current temperature and average dissipated power. If the overtemperature flag is set (>70°C), the last row begins to blink.

Another important task is the evaluation of the encoder. With every encoder step, the setpoint current is increased/decreased by 10 mA. When the encoder button is pressed, the setpoint current changes abruptly to 0. If it is pressed again (and the encoder position has not been changed since), the “old” setpoint current is restored abruptly.

If the difference between setpoint and actual current exceeds 100 mA, the green LED begins to blink slowly, indicating that the source does not provide enough current. If the overtemperature flag is set (>70°C), the LED blinks fast.

Module: Output control

This module controls the output of the system which sets the setpoint current of the device. According to the requested setpoint in the SystemCommand object, the resulting DAC code is calculated and corrected using a linear function determined during the calibration process.

There is one speciality here worth mentioning: If the setpoint current is near 0 (that is smaller than 1 mA), the DAC peripheral unit is disconnected from the port pin. The pin then remains in a high impedance state resulting in the safe state that I already mentioned earlier. The current draw at the load is minimised – it is even lower compared to the DAC output with code 0. This is due to offset voltages in the analog output stage of the DAC output. Ideally the current draw would be zero in this case. But this is not possible in this hardware approach because the opamp also has input-referred offset voltages which distort the actual setpoint. Since there is no (auxiliary) negative power rail (lower than GND), there is no way to equalise this non-ideal effects. The minimum current draw is around 7-8 mA (in my case).


To give the most accurate results (within the limits of the hardware of course) some device-specific adjustments are necessary. There are 3 subsystems that need calibration: the measurement of the actual voltage, the measurement of the actual current and the setting of the setpoint current.

These adjustments are done via USB interface. In the case of the measurement of the actual voltage, two different known voltages near the lower and the upper scale (5 V and 30 V for example) have to be applied to the device. These voltages are provided to the calibration process while the device measures its own voltages. Using a two-point calibration approach, gain and offset values of the resulting linear equation are calculated and saved in the internal flash of the microcontroller.

The same applies to the other two subsystems with the difference that the device sets the setpoint current to two different currents (e.g. 100 mA and 1 A). The real currents have to be measured with an external ampere meter and must be made available to the active load device.

Source code, binary and flashing

You can find the latest source code of the firmware in my GitHub repository. A binary file is also provided there.

The compiled binary has to be flashed into the microcontroller. You can use an SWD-enabled programmer (e.g. ST’s own ST-Link) or you can use the embedded DFU bootloader via USB interface – even when the microcontroller has never been flashed before. In the latter case you must press the “Boot” button on the active load device while powering it on. The device enumerates itself as a DFU device via USB. Then you can use ST’s own upgrade tool (look for “STM32 DfuSe”) or use another which supports the protocol.

Read further on as I describe the PC software tool which is part of the project in part 5.

By André Heßling

I am an electronic engineer living in Voerde, Germany.


Leave a comment

Your email address will not be published. Required fields are marked *