Browse over 10,000 Electronics Projects

A high current power supply built around a server voltage regulator

A high current power supply built around a server voltage regulator

The firmware

I connected up the 12V input to my bench PSU and switched on. The power LED came on which meant that the 5V regulator was working. Of course there was no output from the NXA66 because the relay that controls power to it was switched off. What I did next was to check that the AtMega328P would talk to me through the ISP header. It did, so now I’m good to go with writing the firmware.

The basic aims of the firmware are:

When the device is powered up it should restore its most recent settings from the onboard EEPROM. If the output enable switch is in the ‘on’ position then the output should be immediately enabled. This enables the device to continue where it left off in the event of an unexpected reboot or power outage.

The upper LED should show the voltage level reported by the INA226. The lower LED should show the current reported by the INA226.

There are two switches and a rotary encoder with an integrated button. Their functionality is as follows:

    • The VSP switch should switch between 3.3V and whatever alternative voltage level is provided by the NXA66, either 5V or 2.5V.

Since the NXA66 does not like switching levels while the power is on we will use the relay to cut the power before setting the new level and switching the relay back on.

  • The output enable switch electronically disables the output.

The rotary encoder and its integrated button will be used to navigate through a single-level menu of configuration options displayed on the upper display. Pressing the button will bring up the first menu item. Turning the knob will ‘scroll’ through the options. Pressing the button again will enter that menu option and then the knob can be used to adjust the configured value and the knob will confirm it and save the new value to EEPROM. Doing nothing for an idle time of 10 seconds will abort the menu process and go back to the main voltage/current display.

Rudimentary but intelligible letters can be displayed on the 4-digit LED display and that will be enough for me to provide the menu navigation. The configurable options will be:

  • Calibration. The INA226 is configured with a calibration value for a perfect 2mΩ current sense resistor. In the real world the actual resistor value will be off by a small amount and this option will allow me to compensate for that. By applying a load and monitoring the current flow with an accurate instrument I will be able to change the calibration so my displayed current matches the instrument.
  • Over-current protection. I will be able to program an upper-limit to the output current. If this limit is exceeded then the output will be automatically disabled. I’ll be doing this by monitoring the polled output from the INA226 so there will be a delayed response of a few hundred milliseconds — roughly the same as a slow-blow fuse.
  • Data logging. I will periodically transmit the measured current and voltage values to the UART port. This option allows me to configure how often that happens, or disable it.
  • LED brightness. A convenience feature to allow me to adjust the brightness of the LED displays.
  • Fan activation levels. The fan can be configured to switch on when a certain temperature is exceeded and then switch off when the temperature falls back below a lower level. I’ll be able to customise those temperature levels with this option.
  • Temperature display. There’s nowhere to continuously display the temperature so this option will allow me to check its current level.

It didn’t take long to write the firmware and I’m quite pleased with the result. It’s all open-source of course and you can view it here on github. In the bin directory you can find hex files that correspond to each release. These can be uploaded directly to the AtMega328p using the USBASP programmer.



Advertisement1


avr-size nxa66.elf | tee nxa66.siz
   text    data     bss     dec     hex filename
   7486     180     191    7857    1eb1 nxa66.elf

Looking at the compiled size it appears that I could have fitted it into an AtMega8 but there’s no price advantage for me to do that since they both cost about the same in single units. That’s one thing I really like about the AVR 8-bit instruction set – the code density is so high that you get a lot of functionality into a small amount of flash. This firmware would have been at least double the size if implemented in the ARM 16-bit thumb instruction set.

The MCU operates on its internal 8MHz oscillator. The fuse values required for that are programmed using avrdude:

$ avrdude -c usbasp -p m328p -e -U lfuse:w:0xe2:m -U hfuse:w:0xde:m

The main loop of the firmware polls the INA226 every 200ms and updates the display. The configuration menu, if active, also runs in the main polling loop. Everything else runs asynchronously using interrupts:

  • A 1Hz timer periodically triggers an asynchronous ADC conversion on the MCP9700 analog input. The ADC completion interrupt is used to read and convert the temperature reading. Click here to see how that’s done. Note that conversion to celsius is done with purely integer arithmetic – we don’t want to pull in bloated and slow floating point libraries just for this.
  • The simple 8-bit Timer0 is used as a general purpose millisecond ticker. Click here to see how that’s done.
  • The power good signal from the NXA66 is connected to the INT0 pin. When the pin changes state we get an interrupt and set the onboard LED accordingly. Click here to see the code for that.
  • The UART transmitter uses an interrupt to tell it when the transmit register is ready for data. We use this to send out our data logging strings without having to do a blocking poll on the register that holds the ‘ready’ flag. Click here to see the code.

Programming using interrupts can greatly increase the efficiency of your firmware, indeed many of my STM32 firmware implementations are entirely interrupt driven. That is, the main loop does absolutely nothing at all. Programming using interrupts does require additional care and attention though. Some of the most important that spring to mind are:

The volatile qualifier must be used on data that is to be shared with code executed in a different context. This will stop the compiler re-ordering instructions or caching writes that could mess things up. Don’t use volatile unless you need it though because it restricts the optimiser’s attempts to make you look good.

You cannot read or write a variable in the main loop that is accessed in an interrupt context if that variable is wider than the MCU can write in an atomic instruction. For the AVR that means you cannot share an int because it’s 16 bits wide and requires two instructions to write. An interrupt can occur between the two instructions and leave the variable in an incorrect state. To get around this on the AVR, either disable interrupts while a wide variable is accessed or use an 8-bit flag to indicate a ‘locked’ state.

Don’t spend ages in an interrupt service routine if other things could be held up by you. In an environment without hierarchical interrupts everything else is suspended and important interrupts such as those that keep time will not be running.

You will not be able to use functionality that depends on other interrupts being serviced. For example, polling a millisecond timer counter in an interrupt routine isn’t going to work because the code that updates the counter cannot run.

Pages: 1 2 3 4 5 6 7 8 9 10

 


Top