Tuesday, 27 September 2016

My Ruby Notes

So I needed to learn Ruby. Mostly because we are going down the Puppet route at work and so I needed to work with a library that was Ruby based.

At first glance it looks very Python like but not. I thought I'd write up my notes here as I read (both for my own uses and for others in case this is useful).

So my background is as a C/C++/Java developer so I tend to look things in terms of that or the other languages (Perl, Python etc) that I have worked with. Please read these notes with that context in mind.

Comments

  • Hash (#) and multi-line wrapped in =begin and =end. The begin/end must be at the start of the line

Identifiers

  • One starting with lower case or underscore are local? (local to the scope) Weird. Otherwise what scope?
  • Can end in ! or ? or =  This is odd also
  • Object data members (instance variables) begin with @
  • Static (class variables) begin with @@
  • Variables starting with $ are global.
  • Variables in caps are constants
  • self is like self in Python or this in C++ I think.
  • nil is like Java Null
  • true/false are lower-case - seems odd as I would expect them to be FALSE and TRUE. Or $FALSE and $TRUE. Hmm
  • Long list of pre-defined $ variables. See here. Very perl like.

Literals

  • Strings quoted with single or double quotes. Can backspace escape quotes as normal
  • There is a weird % delimeter scheme where you can use other string delimeters. So you can say %{This is a string} or $(This is a string). Ok - bye bye readability.You can control interpolation of the string (see embedding expressions below) using a character after %. See here for table
  • (Interpolated string) You can embed an expression wrapped in #{ } inside a string and it will be evaluated and embedded. Not sure when it is evaluated - I assume when the string is elaborated (so probably not overly useful except for strings defined inside the code).
  • (Here documents) You can use the <<END etc syntax from shell to embed a multi-line string
  • Make an array with Array.new or []. Can initialise an array as you expect. Can use %w in front of array to initialize using spaces as delimeter instead of comma. The question is why?
  • Negative indicies index from end of array. God - readability.
  • Can specify a range with [1..5] etc. If you use three dots it doesn't include the end value. Again. readability.
  • Can have perl-like associate arrays so { "x" => 1, "y"=>2 }
  • Has concept of ranges like 0..5. Can be floats or whatever but must be consistent. Has triple equals (===) operator for testing if a value is in a range. Handy I suppose.

Operators

  • Has most of the assignment operators of C (i.e. a += 10  or a *= 10 etc)
  • No ++ or -- (like python)
  • Can use comma to do multiple assignments. Yuk. So a,b,b = 1,2,3. Also can assign an array
  • Conditional assignment is interesting x = x || "Default" - will assign if x is nil or false (but not empty it seems). Ditto x ||= "default"
  • &&= is opposite - so will assign only if NOT nil. Why?
  • nil behaves like false to logic exprs. Any non-nil value behaves like true. Very C like
  • There is an and/or as well as && and ||. The words variant has lower operator precedence so goes after assignment. Yuk - use brackets. Bye bye readability

Control

  • Conditionals have values. Ok I suppose. I think.
  • The form is if xxx (new line and statements) end. Can use then as separator for single line. What have they got against brackets?
  • There is an unless expression which is basically if not but you can't have an else
  • There is a ternary operator. Of course there is - they seemed to have boiled every other language down to the syntactically most obscure and confusing-to-read elements and included them all.
  • Can use ifs as guards x = 7 if month = 'Feb'
  • Like Moduala II and Ada (I think - God it's been a while!) There is case expression with when for each case. If you say case x you can just say when 22. If you say just case you have to say when x == 22. You say else as the default case.
  • They have while and until as loop constructs. No range iterating or iterating over arrays etc (AFAI can tell)
  •  There is a return statement.

Calls

  • Oh god make it stop. You can call functions without parenthesis. If you want to use the result inside a one-liner you have to wrap the arguments in parenthesis. So result = add 1, 2  is ok but you have to say result = add (1,2).reverse to call the reverse function on the result (ok bad example as you can't reverse an integer).
  • Method definitions are like python so def xxx(params) ... end. Params don't have types and no indication of the return value type. Methods have a value (can be nil).
  • Params can have defaults like in C++
  • Can use * in front of parameter to make a varargs call. If you use * when passing an array it expands the array as arguments (like xargs in shell I suppose)
  • You can use the ampersand & operator in front of a variable to indicate it is a code block.

Scopes

  • As above there is a local, instance, class and global scope. Code that is not in any other scope is in a local scope called the 'main' scope (basically in the scope of the 'main' object). Ok...
  • Umm there is some weirdness about 'procs' that bind to the scope you are in. Not sure what a proc is yet. If you def something it gets a new scope however... So whats a proc?

Procs Blocks and Lambdas

  • Lambda is Ruby are much like Python lambda - tiny functions you can pass around.
  • Procs are a bit different as the content of the proc runs in the same scope as the caller. For example if you use the return keyword in a proc it causes the function that called the proc to return. Also it looks like variables in the scope where the proc was called are available to the proc code. Makes life interesting if the same proc is used in multiple places. Not nice I think.
  • Blocks are confusing but as I understand it they are an anonymous proc. So you can pass a block to a function and have the function execute it. It looks like you don't pass a block as a parameter (like you would a proc or a lambda) and to call it the function uses the yield statement. Again yuk... How do you know if a function can use a block or not?

Dynamic Methods

  • Oh goody - you can dynamically create methods for just a single object
  • Worse - there are calls made on object like method_missing that can be used to dynamically create a method when the method doesn't exist. Have fun debugging that!

Classes

  • Classes must start with a capital (and be CamelCase it seems)
  • Instance variables (i.e. @thing) are ONLY accessible inside the class - so you *must* have accessors
  • There is some weirdness with modifying variables outside a def but within a class. The variables are 'class instance variables' which means they are not a class variable and not an instance variable. Essentially the class is itself an object and they are a member of that but you can't access them using @@. Basically don't do this.
  • You can call a setter method foo= which means you can say this_instance.foo = 10 and it works.
  • There is a attr_accessor for easily creating pairs of accessors.
  • You can make methods private using private keyword private: some_method
  • Private methods are accessible to child classes
  • Private are not accessible to another instance of the same class. Protected ,methods however are
  • Ruby - like most languages these days - only supports single inheritance. So I suppose you can't really define interfaces then...

Modules

  • Like Modula II modules - a namespace basically.
  • One cool thing about modules is you can 'include' them into a class. So you can define functions that refer to class variables and then you can drag those functions into multiple classes as a 'mixin'
Probably much more I can say but this is a start. Hope it's useful!

Sunday, 21 August 2016

Lab Power Supply - Raspberry Pi Power Control

So in order to make shutting down the power supply safe for the Raspberry PI, I need to build a soft power-on circuit so that power to the Pi is only cut when it is has completed its shutdown.

In addition I want:

  • A power light to indicate when the standby power is on and when the main power is on.
  • Push button that can turn the who power supply on or off (safely)
  • A 12V supply and speed control for the heatsink fan
  • An interface to connect temperature sensors so we can monitor the heatsink temperature.


Hardware

As discussed previously, the 5V power for the Raspberry PI is provided by an embedded switch mode supply. This will also act as a stanby power source when the supply is off. The main power supply (i.e. the torroidal transformers) are switched using a different circuit under the control of the Raspberry PI (see previous blog on this).

The power switch circuit is based around a P channel MOSFET (Q2) so that initially when power is applied the MOSFET gate is at the supply rail and no power is passed to the Raspberry PI. When the switch is pressed it activates Q3 which pulls the gate low, powers up the Raspberry Pi and actives Q4 which keeps the MOSFET gate low (latches the power on).

When the PI is ready to shutdown it activates Q5 (via the RC network) which turns off Q4 which turns off Q2. The problem is that deactivating the power turns off Q5 and can cause the power to just glitch and turn back on. The RC network keeps the MOSFET off long enough that it won't come back.

The Raspberry PI monitors the voltage at the power switch and will initiate a shutdown if the level goes low (see be

There is a low-side switching transistor that is driven by a PWM signal from the PI to control the fan speed (Q1). The fan is powered from a small boost converter that is running on a daughter board soldered on using a DIP header to the main board. The 12V boost converter is one of these Pololu jobs off ebay. I had to add a diode to the boost converter input as it was causing problems with turn-off and keeping the power on. Also I found out the hard way that a 1N4148 diode was not beefy enough to supply the boost converter.

There is a header and pull-up to connect a string of DS18B20 temperature sensors. These are a very neat 1-wire bus temperature sensor capable of high precision. There is a Linux driver for the Raspberry Pi that makes super-simple to interface. The plan is to add one of these near the pass element on each heatsink and to use this feedback to control the fan speed.




The board is a very and designed to fit onto the raspberry pi GIO header. I took a simple approach in the design and flood-filled the bottom with ground and the top with 5V. The GPIO header is soldered onto the bottom of the board.


Getting the PI to Signal when it is Done

My first approach was to write a little python program that would set a GPIO pin high and to run this when the operating system is about to go into halt state. I modified the /etc/init.d/halt script and added a call to my program just before it calls the 'halt' program but found this didn't work. For whatever reason by the time we get here we can't control the GPIOs from python anymore.

I did some searching and eventually found that there is a way to control the default state of the GPIO pins. There is a set of configuration files in /boot that enables/disables hardware systems. These are read by the kernal at boot and will usually result in additional drivers being loaded but actually you can change the state of hardware using this config.

Some of these files are .dtb files or Device Tree Blob files. There is a compiled (dtc) used to generate these using a dts (text) file. The language is a bit c-like and uses curly brackets to define data as structures.

Initially I thought I just wanted to set the default state of the power pin when the Pi shutsdown but after some digging I found there is a mechanism here to configure a GPIO pin to be set when the PI shuts down - this is basically intended for signalling shutdown to other hardware.

The complicated part is that the device tree is segmented by raspberry pi hardware type and then there is hardware which is available on multiple devices. Some hardware is optional and then you can add custom configurations for hardware. To handle all this they created an overlay system where you can create an overlay dtb file that gets loaded on top of the device tree to implement a specific change.

There is an overlay called gpio-poweroff that does exactly what I want. To enable this you modify /boot/config.and add a line like this (see below). The option after the comma determines which GPIO pin will be asserted at power-off

dtoverlay=gpio-poweroff,gpio=25

There is more details on device trees and overlays etc here

Detecting a Power Button Press

Now we want to detect a power button press from python and when it occurs we want to trigger a shutdown. Here is a simple python program that (using the GPIO libraries interrupts feature) triggers a function when the pin state changes from high to low. The pin state change is caused by the button press.

#!/usr/bin/env python

from subprocess import call
import RPi.GPIO as gpio
import os
import time

# Define a function to keep script running
def loop():
    while True:
        time.sleep(5)

# Define a function to run when an interrupt is called
def shutdown(pin):
    os.system("sudo shutdown -h now")

gpio.setmode(gpio.BCM)
gpio.setup(4, gpio.IN)
gpio.add_event_detect(4, gpio.FALLING, callback=shutdown, bouncetime=200)

loop() # Run the loop function to keep script running

Now we make this file executable to the world.

We want this program to start when the operating system boots so we need to create a daemon for it.

I created a file in /etc/init.d called powerbutton-watcher and it contains this (taken from a template). The script resides in the /home/pi/LabPSU/LabPowerSupplyCtrl directory.

#!/bin/bash

### BEGIN INIT INFO
# Provides:          powerbutton-watcher
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Watches the state of the power button and initiates shutdown
# Description:       Watches the state of the power button and initiates shutdown
### END INIT INFO

# Change the next 3 lines to suit where you install your script and what you want to call it
DIR=/home/pi/LabPSU/LabPowerSupplyCtrl
DAEMON=$DIR/WatchPowerButton.py
DAEMON_NAME=powerbutton-watcher

# Add any command line options for your daemon here
DAEMON_OPTS=""

# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
DAEMON_USER=root

# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid

. /lib/lsb/init-functions

do_start () {
    log_daemon_msg "Starting system $DAEMON_NAME daemon"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
    log_end_msg $?
}
do_stop () {
    log_daemon_msg "Stopping system $DAEMON_NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    log_end_msg $?
}

case "$1" in

    start|stop)
        do_${1}
        ;;

    restart|reload|force-reload)
        do_stop
        do_start
        ;;

    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
        ;;

    *)
        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
        exit 1
        ;;

esac
exit 0

Finally to get this to start with the OS I used the following command

sudo update-rc.d powerbutton-watcher defaults


Tuesday, 21 June 2016

Lab Power Supply - New Boards, Input Power Control and the Case

So it has been a while since the last update but I've not been idle! The lab power supply is taking shape although there have been a few hiccups. The new boards finally came, I found a hiccup at higher currents and I started some assembly into a case. I designed and fabricated a board to turn on power to the main supply. The problem is that I've been pretty busy so much of this has been done in bits and pieces. I hope I am recording the journey well enough to follow!

New Boards

The new boards look very sexy. They make much better use of space and the layout is more logical. While it still isn't easy to tighten the screws, it is much easier than the previous proto-type. Here it is nearly completed below.


With bated breath I tested it for the first time. I had massive expectations of the new high-precision shunt resistors and... It didn't work. After some head scratching I realized the expensive shunt resistor was 0.1 ohms instead of 0.01 ohms. I changed the amplifier to suit and this work but I also realized that the current limit for this resistor would be around 2A. I gave in, ordered the right resistor and then waited for it to arrive (don't ask how much).

When the right resistor arrived, I fitted it and tested the unit again. I was quite impressed with the current accuracy and overall everything was working well.

Load Testing and Overshoot

While waiting for the replacement shunt-resistor I was experimenting with the simulation and found that I could increase the capacitor in the voltage control circuit to reduce the overshoot. By increasing C20 to 20nF I could completely eliminate the overshoot and not effect the transient performance. I tested this and it did indeed behave this way.


One of the key changes in this version was that I moved the bridge onto the heat sink. I wanted to run some load tests to ensure this was successful.

My dummy load is only capable of 4A, I tried a slightly different approach which was to use a 1 ohm concrete resistor immersed in big cup of water. This worked really well as it takes quite a while for the water to heat up.

I found that with the current limit set over 4.5A that the output would oscillate. I thought it could be the supply is running out of headroom voltage on the capacitor but that wasn't it. I figured out that the additional capacitance I added to control the overshoot was causing the oscillation.

I figured out that if I increase the compensation capacitance on the current control amplifier I could get rid of the oscillation and still have a higher capacitance in C20 to reduce the over-shoot.

This still needs some thought but overall this is looking pretty good. One negative effect of increasing C20 is then the output voltage slew rate is much lower. It can take much longer for the output to hit the set point or for the voltage to recover after an overcurrent limit event. I still need to finesse this a bit but for now I am moving looking at other things.

Other Testing


I also finally got the courage to short the outputs and to verify the current limit behaves correctly and it did. The supply also behaves correctly with the current set to small values (like 10mA) now with the correction to the pre-regulator.

I tested the behaviour of the pre-regulator with a pulsed load. This would still not always recover back to the set voltage - particular in the 15-30 volt range. I ended up increasing the resistor in the peak-detector circuit (R52) to 100K and this worked. The only thing is that now in 30V mode it can get very hot with a pulsed load that drives the output down to low voltages (in the over-current events). In most circumstances this won't happen as either the over-current will lead to a constant state and the pre-regulator will adjust or the over current pulses will be infrequent.


Mains Input

Because the Raspberry Pi must be shutdown and can't just be turned off, I need to control the power to the Raspberry Pi and the main transformers separately. The ideas is there will be a permanent 5V supply but the main transformers will only be turned on when the Raspberry Pi is up.

I also need to deal with the inrush that occurs when the unit is powered up as three torroidal transformers feeding an array of capacitors will suck a considerable startup current.

I really need to keep this circuit stupidly simple, I don't have an isolation transformer or a differential probe and I don't want to have to debug it if I can get away with it. Also I can't proto-type this circuit on bread-boards or matrix board as - well that would be dangerous.

I came up with the idea of using two Solid State Relays (SSRs) and a power resistor to turn on the main supply and control in-rush. One SSR turns on the 240V supply via a power resistor and the second SSR shorts-out the power resistor. Both are activated by the Raspberry Pi when the software starts.

I use simple NPN transistor to switch on the relays using the 5V supply. The Pi generates 3.3V signals which activate the transistors. I designed the circuit to have an input and three output connectors so most of the mains wiring is routed through the PCB. Here is the circuit (below)

Main Input PCB

I didn't want to wait three weeks for this PCB to come back and it is quite simple to implement as a single sided board. I thought I would try my hand at etching the PCB at home.

I bought a small ($3) 80x50mm box from Jaycar that would hold the circuit. I'd have to either not use the lid or cut a lot of holes in it but I thought it would be better to put the mains circuit in a box. For starters it makes it harder to accidentally touch the live circuit and the box insulates the metal case from the PCB.

I designed the PCB to fit comfortably in the box. It looks like this:


The main things were to keep the power tracks fat and to place the low-voltage circuit as far away as possible from the main circuits. It was harder than I thought to get it to all fit in the box but I got it to work in the end.

Etching PCBs at Home

My plan was to use the toner-transfer method to etch the PCB. I don't have a laser printer at home so I used the local Officeworks store.

I printed the bottom side artwork as a PDF and asked KiCAD to include dots for the drill locations. Initially I flipped this but then realized that was wrong. The toner transfer process ends up flipping the artwork so this cancels the flip caused by the bottom side being printed when viewed from the top.

I copied and pasted the PCB so there would be four artworks per page and asked Officeworks to print me a few copies onto shiny (photo paper). I only really used one page but they were 35c each and I felt sorry for the lady as she had to fight the machine to put photo paper into the black and white laser copier.

I cut out the board artwork from the paper, cut out a piece of PCB the correct side and used sticky tape to keep the artwork aligned with the board.  I used a laminator to heat the paper so the ink would transfer but I found even running it through the laminator a few times was not enough to transfer the ink reliably onto the paper.

I tried using a clothes iron and while this transferred the ink quite well it also melted the tape I used to hold it on (but that doesn't matter I think). The problem was that when I peeled the paper off it tended to take the print with it.

I did this a couple of times with no success. I had an idea that if I softened the paper with water it might leave the ink behind. This worked really well. I soaked the paper and gently wiped it off the board while leaving the ink behind.

I etched the board in Aluminium persulfate and the results were excellent! The image below shows the board as it came out of the etchant.



Drilling the holes was not much fun. I bought a set of fine drill bits that have larger shanks so you can hold them in a normal chuck. I used to drill press to do the drilling and mostly the holes lined up. The bigger holes for the power connectors wandered for some reason and some ended up a bit crooked. No matter overall.

Here is the board assembled


And here is the solder side.


Raspberry PI GPIO and Python

Controlling GPIO pins from Python is relatively straight forward. Initially I stupidly followed some guide that told you to install an ancient GPIO RPM which required the python program to run as root. Once I figured out the newer versions don't require this life got better.

You basically have to add an import and then setup the GPIO pins. You have to choose the naming scheme for the pins you are using between board and the naming scheme of the Broadcom chipset (as the pin names change between versions of the PI). Then you can set or read the pins. In addition I found a 'cleanup' method that resets the pins back to what they were when you started. I added a 'finally' exception handler to do this when the program exits so the mains is not left turned on.

Here is a summary of the code (some bits omitted).

import RPi.GPIO as GPIO
...
def setupIOs():
    print "Setting GPIO 23 and 24 to outputs"    GPIO.setmode(GPIO.BCM)
    GPIO.setup(23, GPIO.OUT)
    GPIO.setup(24, GPIO.OUT)

def turnOnSupply():
   print "Enabling 23"   GPIO.output(23,True);
   time.sleep(1)
   print "Enabling 24"   GPIO.output(24,True);
   time.sleep(1)
   pass

def turnOffSupply():
   print "Enabling 23"   GPIO.output(23,False);
   GPIO.output(24,False);

if __name__ == '__main__':
     try:
        setupIOs()
        turnOnSupply()
        LabPowerSupplyCtrlApp().run()
     finally:
        turnOffSupply()
        GPIO.cleanup()

Case

At this point it is time to start figuring out how this thing will look when it is finished. With the new wide but shallow boards I figured out I can make it all fit with a depth of around 300mm and a width of around 420mm. The Raspberry Pi touch screen needs about 130mm.

I found that one of these 3U rackmount cases from Jaycar would pretty much do the job.

The case is built from these rails that look like meccano pieces as they have holes all the way along. The top, bottom, front and back can be unscrewed and the case is held together by the sides and rails.

They provide four extra rails so I decided to use these to support the transformers and the heatsinks. I made small steel plates that screw to the rails and that have a hole for a bolt to support the transformer. I have round metal plates and rubber protectors that I can use to bolt the transformers down. The plates were made from some bracket I bought from the hardware store (Bunnings). This is what they look like in the case (excuse the messy wiring in the way).


I cut holds for the IEC connector, power switch and fuse into the back plate. I drilled holes through the back plate for the small box that holds the power input PCB and mounted that. The PCB just clips into the enclosure.


The I drilled and tapped the heatsinks for the components from the power supply. I figured out that if I measure, drill and tap the holes then screw the components on and then solder them to the board I can get them to line up perfectly. I drilled and tapped holes in the bottom so they could be screwed into the rails and the rails screwed into the case. I fitted a 12V fan onto the end of the heatsink channel and there are enough holes in the case sides that this will work.

I only had two of the long heatsink channels so I bought a third. I cut this channel in half using a drop saw. The cutting worked surprisingly well although did fling aluminium all over my shed. So now I have the big heatsink with two channels and a smaller one for the third channel.


I needed a 5V supply that can deliver 2A to feed the Raspberry PI. I found a Traco supply on ebay at a reasonable price and mounted that to the side wall. Here is the supply:


The 5V supply is tucked behind the heat-sink on the left of the case.


Here is the overall layout of the box at the moment.


Only two channels have been assembled right now and I have two transformers setup. I had to extend the transformer wiring to make it over the heatsink to the PSU boards but that went Ok.

The protective ground is a problem as the black powder-coated plates are pretty non-conductive. Currently it is organised as follows:
  • The ground comes in and ties to the transformer rails where splits to ground the 5V supply and another ground wire goes forward to the front rail.
  • Each of the power supply channels connects to the front rail ground
  • There will be a ground binding post on the front panel connected to the front rail.
At some point I might scratch off some powder coating to ensure the sides and front/back are electrically connected to ground also.
Currently I have cut the hole for the Raspberry PI in the front panel but I think the only practical way to mount it is double-sided tape. The centre part of the display including the rapberry PI itself fits through the hole and the thin edge of the glass display will mount to the front panel with foam double sided tape.
I don't want to do this until the front panel is completely ready as otherwise I can't drill or work with it. I still need to drill holes for the outputs and for the soft-power switch and indicator LED.

Next Steps

I currently have the Raspberry PI running outside of the case with leads going back in for the USB connections to the channels, the 5V supply and the control signals for the power input.

I need to design the soft switch circuit that allows the PI to switch itself off when it is ready and the fan control circuit. I also want to build temperature sensing for each of the channels so the PI can control the fan.

We are getting close though! It is starting to look like something you might have in a Lab!

Monday, 25 April 2016

Lab Power Supply - Board Redesign

So in the last post I was at the point where I felt I had ironed all the faults out of the current design and felt it was time to go for a new board. Since then I rolled all the changes back into the schematics and designed a new board that integrates all those changes.

Schematic Changes

So I will go through each section of the schematic, list the changes and then include the updated schematic.

Power

In this section I made the following changes:
  1. I added a diode across the relay to suppress back-EMF when the relay is switched-off.
  2. I changed the 24V LM317 to be a TO-220 package as the surface-mount version was getting too hot.

Pre-Regulator

In this section I made the following changes:
  1. A buffer for the Ioutmon (U14) and Voutmon (U7D) signals to stop them affecting each other through the summing amplifier. This was causing the voltage to vary under load and causing incorrect current readings at low currents.
  2. A peak-detector (D12, C28, R52) to slow down the decrease in the pre-regulator when the voltage drops. This helps stabilize the output when a pulsed load is driving the system into current limit
  3. A minimum voltage of 0.6 + 3.3V to the pre-regulator (D13, D15, D14, R57). This prevents the pre-regulator going down to zero when the output voltage is very low (for example if you set the current limit to a very low value like 20mA)
  4. A bypass capacitor for U7 (was missing from previous schematic).

Post Regulator

In this section I made the following changes:
  1. The orientation of Q8 was corrected (collector and emitter where the wrong way round due to a footprint change when I changed the component model).
  2. The differential amplifier measuring the output voltage was changed to use 100K resistors and the compensation capacitor was reduced to 4.7pF. This increases the impedance of the amplifier inputs but then the compensation had to be reduced to maintain the same response time.
  3. I removed the four terminal voltage sensing. This wasn't stable and I didn't want to mess around to fix it so for now I am not supporting sense terminals. I will still sense the voltage at the output terminals but you can't connect sense wires to an external load.
  4. The MOV output protection was changed to just 2 MOVs. This is as a result of the change to only have two terminals instead of 4.
  5. The current sense resistor was changed to the Vishay VC1625. While the tempco and accuracy of the Welwyn OAR3 resistor was fine, the thermoelectric effect wasn't. I decided to go with a higher precision four terminal current sense resistor.

PCB Changes

I have been struggling to find a case that will house the project and then the recent discovery that I needed to put the bridge rectifier on the heatsink led me to re-think the problem. In addition to the bridge I need to include another TO-220 for the 24V supply which means I need to make the board much wider.

The heatsink I chose is 254mm wide (10") so there is no option to put three modules on one heatsink. I figured out that if I can reduce the length of the board to below 100mm the project will fit comfortably fit inside a 3U 19" rackmount case. 3U gives me 130mm of height for the Raspberry Pi touch screen which leaves around 30mm of height for binding posts.

The 19" case however is 480mm wide which gives me lots of space. I realized that if I got another heatsink and cut it in half (so 125mm wide) I could fit this in the case also and still get three channels.

Layout Changes

Having the bridge on the back edge makes some things a bit tricky. The bridge needs to be near the relay, the pre-regulator MOSFETs, the capacitors. The VCC bridge also needs to be nearby also but this creates a problem if the power connector is at the edge of the board as some traces would need to be router right over the other side to get to the internal voltage regulators.

Instead I put the internal regulators (for VCC, 5V, 24V) on the left side, the power connector, relay and bridge in the middle and then the pre-regulator MOSFETs and output MOSFET toward the right.

The pre-regulator circuit (except for the opamps) is at the top right near the pre-regulator MOSFETs. The bulk capacitors are between this and the output.

The analog section is at the front near the middle and then the digital section is to the left (section with ground plane). The USB section is below the internal regulators and behind the other digital sections. It wasn't practical to put the USB connector at the edge of the board so this too is in the middle nearer to the power input.

I also used 4mm traces for the heavy current tracks which allowed me to make the board quite compact. 

This worked quite well overall as none of the power traces are very long, the analog bits of the digital section are close to the analog section and the analog section is close to the negative output (which is the ground point for the circuit) and the sense resistor.

The layout is much more dense than before so there are a few more vias. The control lines for the relay transistor, the VCC voltage control and the signal that controls the pass transistor were particularly difficult to route.

I was able to fit nearly everything in with 80mm of width but I couldn't figure out how to rout the Vrelay and Vsetplus lines. I ended up adding an extra few mm of width so the board is 128mm instead of 125mm. This will have a negligible effect except that there will be a slight overhang off the ends of the heatsink.


Here is how the board looks in 3D - not all parts have models.


Other layout corrections:

  1. The ground routing for the analog and digital section all comes off one track that connects right at the sense resistor. Before some of the grounding came off an earlier point on the heavy-current ground path and this created some voltage differences.
  2. The PTC was placed further from the sense resistor to avoid temperature induced errors.
  3. The placement of the capacitors for the AD7705 crystal were too close so these were placed a bit further off.
  4. A bit more space was added between the pass transistor and the pre-regulator MOSFETs to allow the aluminium dioxide insulator pad to fit.

Footprint Changes

  1. The MOV footprint was mirrored before (made sense from bottom of board). This was corrected.
  2. The PTC footprint was mirrored before. This was corrected. Also the footprinted suggested by the manufacturer had the feet too far off the axis of the part so I reduced this a bit to make the part fit more comfortably.
  3. The MCP2200 footprint was the wrong width and this was corrected
  4. The AD7705 was changed to be the SOIC footprint as I got some of these cheaply off ebay.
  5. The pads for the main capacitors were too small. I added bigger pads for better mechanical strength.
  6. The LM317 used for 24V rail got too hot as a surface mount component so this was changed to a TO-220 part.

Next

So I have ordered more boards from Seeed studio and am waiting for these to be manufactured. Next step will be assembling and testing the channel on the new board.

Wednesday, 13 April 2016

Lab Power Supply - Pre-regulator re-visited

A while back I noted some odd behaviour in the pre-regulator when the load is current is switching at 10Hz or less and the power supply is going in and out of current limit. Then more recently I noted a fault with the summing amplifier where it was effecting the current measurement and voltage measurement. While working on the current measurement I noted another fault which is that if the output voltage is driven really low (say if you set the output current to 50mA and connect a dummy load trying to suck amps) the pre-regulator can drive the voltage right down to zero and lock-up the supply.

So I thought it was time to re-visit the pre-regulator design.

Current Affecting Voltage

For a while now I have been chasing problems where the output voltage would dip with the current increased. The problem is that there were multiple reasons for this. One reason is that the ground points were not correctly placed so the larger currents created voltage differences across some conductors which then lead to ground voltage differences across the circuit. I tracked down a few of these in the PCBs already.

I also had problems with poor connections between the sense and output voltage lines causing issues. On top of this there is a 1mV or so drop across the binding posts under more than 1.5A of load.

Earlier I realized that the input impedance of my differential voltage amplifier was too low and was affecting the readings.

The latest problem is that the summing amplifier in the pre-regulator ties together the Vout and Iout lines using 10K resistors. This is enough for the output voltage to affect the current reading by a few mV and for the current reading to affect the voltage reading by a few mV.

So now I have added buffers between the current monitor and the summing amplifier (I have a spare op amp in one of the chips). In the final design I will also add another op amp to the board for the second buffer.

Pulsed Current Limit Load

This was pretty hard to reproduce in the simulator but easy to see on the scope. Here is a shot of the output voltage and capacitor voltage that I took before:

You can see the capacitor voltage has dropped and so the output voltage can't get back to the set voltage before the load hits again (which drives the output voltage low again). The worst part of this is that the output will slowly slew from a state where it is outputting the set-voltage at the peaks then down again.

The problem is that the pre-regulator compares the output voltage with the capacitor voltage at the instant that the capacitor is charging (as the next 100Hz pulse from the bridge is coming in). If this happens to be at a point where the output voltage is low because the supply is in current limit then the capacitor won't be charged to a level sufficient to provide the set-voltage.

I was thinking that what I really want is for the desired capacitor voltage to increase quickly to meet demand but to decrease slowly. 

The solution was to add a peak-detect circuit to the output of the summing amplifier so the pre-regulator set-point moves down slowly enough that it won't get caught short when the load is switching on and off at a slow rate. The peak detect has to be fast enough that it will eventually bring the pre-regulator set-point back down to limit heat dissipation on the pass transistor. I chose to make the time constant about 1 second which seemed to work quite well. Here is what the pre-regulator looks like with this added
.
This worked very well. The pulsed load issue went away entirely with a 50Hz pulsed load (the lowest frequency I can do with my dummy load). 

The only issue is that under extreme conditions it will create a significant heat load. I set the output for 25V, the current limit for 2.5A and the dummy load to draw 3.9A in pulses at 50Hz. This creates a close to optimally bad load as the capacitor voltage is over 25V, the output voltage drops to a very small voltage but 2.5A is flowing. The pass transistor got pretty warm but was well within safe limits after a few minutes of operation (I still have problems with the bridge rectifier over-heating so can't test for too long).

Pre-Regulator Lock-up

I wanted to see how accurate the current measurement and current limiting was at very low currents. I set the current limit to 50mA and enabled my dummy load. The output voltage quickly fell down to somewhere in the mVs but then something odd happened - it fell to zero and so did the output current.

I tried this out in LTSPICE and I was able to reproduce it:

This took some time to figure out but the problem is that to keep the pre-regulator 4V above the output, it uses a zener diode to subtract this voltage from the capacitor voltage and compare the subtracted voltage with the output required. As the output drops below 4V (the zener voltage) the zener diode is no longer in breakdown and so the voltage is less than 4V below the output.

If the output voltage is very low then the pre-regulator can drive itself down to a point where the capacitor falls to zero and then the output is zero so it never turns back on!

Once I had a handle on what was going on I decided to peg the minimum voltage to something sensible. With the peak-detector in place I can add another diode to form a sort of diode-or with another voltage which will become the minimum pre-regulator voltage. To begin with I set the minimum to be 4V but then the pre-regulator will hold at 8V as it subtracts 4V from the output before comparing. In the end I added two diodes in series to create the minimum voltage (which gets droped to one diode voltage because of the diode-or) but this worked a treat. Now the minimum pre-regulator voltage is roughly 4V.

Here is the circuit with the minimum voltage setting.

This worked in simulation. The pre-regulator then behaved more like this:


When I tried this on the PCB it worked similarly. The new circuit had no effect at higher voltages and prevented the lock-up. I think this pretty much concludes my work on the pre-regulator! It works!

Next

At this point I think it is time to re-spin the board to fix all the issues I found. The next phase will be to re-integrate all of these changes into the KiCAD design and re-layout the board.

Friday, 8 April 2016

Lab Power Supply - Thermoelectric effect

Now that I sorted the problem with current measurement due to leakage from the voltage output, I wrote some code to enable me to calibrate the current control and the current measurement. The problem I found was that the current take a long time to settle and seems to drift by as much as a few mA - far more than the 1mA I was aiming for.

Current Drift

My home-made constant current load can (according to my Keysight 34461A) draw a constant current that varies by less than 1mA. At the moment the calibration is off so it always draws 1mA more than it should (but I don't care).

I noticed that when drawing 1A the reading taken by the lab supply varies by as much as 5mA, Similarly when the lab supply is in current limit, the output current will vary by the same amount roughly.

On my board I have the PTC fuse right next to the shunt. I thought this could be heating up the shunt and throwing the readings off. I attached a thermocouple to the shunt and the temperature did vary with load but by no more than a degree C. I removed the PTC and replaced it with a wire for now to see if this changed anything and it did a little although as the shunt resistor is rated at 20ppm/C it should effect it much at all.

With the dummy load set to draw 1A I measured the voltage on the shunt resistors and got this graph:
 
The 25uV variation equates to about 2.5mA on the reading and 250ppm if the temperature is only varying by 1 degree.

With the supply set to limit the output to 1.5A, here is the output current over time
So it is varying by 6mA which is way more than my 1mA budget.

Thermoelectric Effect

The thermoelectric effect is where a small voltage is generated across a metal junction as a result of the temperature of the metal. The heat is effectively converted directly into voltage.

I found out that for some metals this can be as much as 10uV/C. Given I am only seeing 25uV changes this could well be down to this effect.

The metal strip resistor I am using is a Welwyn OAR3 but unfortunately the datasheet doesn't specify the thermoelectric coefficient. 

Hunting for a Solution

My first thought was that if I increase the resistance from 10m Ohms to say 50m or 0.1 ohms then the thermoelectric effect will be greatly decreased as a proportion of the overall voltage. The downside is that then the resistor has a much greater voltage drop but also consumes much more power.

I ordered some other size resistors and began experimenting with the circuit in LTSPICE - mostly to ensure I had the calculations correct for the feedback resistors on the op amp that measure the current.

While fiddling around I noticed that with 50m or 0.1 ohm that when the system went into current limit the output would oscillate - in fact the current measurement would oscillate. I messed around trying to add compensation and slow things down but the only thing that would change the situation was to reduce the output capacitance but that effects the voltage stability.

I considered just living with the problem and calibrating the system for the steady state current with the assumption that it could take 20s to get there. This seemed a real shame as the rest of the system was just so precise. For example I measured the voltage on the current sense resistor in current limit mode and it varied by no more than 2 or 3 microvolt over a short period. The current control loop is *really* impressively good.

Better Shunt Resistors

So then I went looking for some better current sense resistors. There are four terminal surface mount ones but the ones with lower temperature co-efficients also tend to be lower power. Many of these specify a thermoelectric voltage of 2 or 3 uV per degree C.

I found these ones from Vishay which are outstandingly good but not cheap ($10USD from digikey in single quantity). They have 2ppm temperature co-efficient, less than 0.05uV/C thermoelectric voltage. They have 4 wire sensing and are generally totally kick-ass all round.

I will probably have to spin a new board before I can test these however.

Tuesday, 5 April 2016

Lab Power Supply - Raspbery Pi Control

So finally I got round to building some code to implement a basic GUI for the power supply. This showed up an anomaly in the current readings that lead me on a hunt to somewhere I didn't expect.

Control Interface

So a while back I bought a new Raspberry Pi (PI 2B) and the official Raspberry Pi touch screen. The plan has always been to make the Pi a touch based user interface for the power supply. I found a Python GUI library called Kivy that works on Windows. Mac and Raspberry Pi and which provides pretty much everything I might need to implement the user interface.

Kivy is really focused on 2D and 3D graphics more than user-interfaces but it does provide buttons and input fields. I am pretty sure that this is all I need plus some colour and maybe later I might use the 2D primitives to do some current graphs.

Being Python I started by writing the code on my Mac and then moved to the Raspberry Pi.

Kivy comes with its own language that you use to describe the graphical layout (in a .kv file). These then get bound to Python classes at runtime. 

Field Binding

You can bind fields in the display to data members in the classes and if the data member changes, the GUI value will be updated. You can even use conditional expressions in the kv language to control fields. For example in my application I use an expression to change the colour of the text to grey if the power supply channel is disabled or in current limit.

Initially I had some trouble with this as I set the value of fields based on regular functions or data in my class. The problem with this is that the Kivy runtime can't 'observe' those functions and so the values would not change in the GUI when they were updated in the class. Instead you have to create members that are of type ObjectProperty and set/get these.

Layout

So you can create a class that is essentially a canvas that you can compose into a bigger window. I created one panel for each power supply channel and then created an overall window that contained three instances of the channel (one for each PSU channel).

I used quite big text for everything as it had to be fat-finger driveable. I figured the most important things are
  • The output voltage (measured by the PSU)
  • The output current (ditto)
  • The set voltage
  • The current limit
  • Enabling/Disabling the channel
  • An indication of if the channel is enabled/disabled
  • An indication of if the channel is in current limit
  • (Nice to have) The output power
The output voltage, current and power are the biggest font. The text goes green if the channel is enabled, grey if disabled and red if in current limit. This is no good if you are colour blind so I may change this scheme to something else later.

The set voltage/current is in small text with a button to bring up a dialog to set it.

There is a big enable/disable button at the bottom for each channel. If I can I will align the binding posts for each channel with the screen and these buttons.

Here is how it looks running on the Raspberry Pi touchscreen and communicating with the lab supply. The numbers are a bit off as it needs to be calibrated.


Here is the dialog for editing the output voltage. The same dialog with different labels is used to set the current limit.


The dialog needs some work I think :
  • The buttons are too small for fingers
  • The V/mV buttons are not worth it. Just have V
  • You have to back-space to reset the value. If you miss the DEL button is dismisses the dialog and you have to start again.
  • Really need some mechanism to be able to slowly sweep the voltage up. For example either a physical dial or a dial/slider on the GUI somewhere (ideally without leaving the main screen).

Updating

The code uses the Kivy Clock class to schedule updating of the values. The first thing I noticed was how stuttery the dialog was when I enabled the updating. This was a simple fix as I just disable the scheduler when the dialog is open and re-enable it when the dialog is dismissed.

It quickly became apparent that the update rate is pretty slow when running at 9600 baud. I plugged the USB cable into a Windows machine where the MCP2200 utility was present and changed the baud rate to 115200.

I then went to change the baud rate in the firmware and remembered how much trouble I had setting this in the first place, The calculations for setting the baud were pretty far off. After some Googling I figured out that there is a fuse that is set by default and that sets the internal clock speed to the crystal frequency divided by 8. Not even sure why they do this.

This program has been interrupted to bring you this message...

And that's when everything went wrong. I have been having intermittent problems with programming the chip and at some point the device simply stopped responding. It could be that the fault meant the fuse bits got whacked to some bad value. The device was essentially bricked,

One thing I noticed was the output from the micro controller during programming (the MISO line) looked like it was fighting with some other voltage. I figured out the state of the chip select for the AD7705 was low which meant it could be trying to control the bus. I figured out that what I should have done is tied the reset line of the AD7705 (and the AD5689) to the microcontroller's reset line so that during programming they won't try and control the bus regardless of the state of their select lines. I hacked the board to make this the case but still it wouldn't go. The MISO line looked clean however.

I de-soldered the micro-controller and soldered a new one on. A task made much easier with the recent purchase of a new toy (more on this later).

This got me back on the road and I was able to program and communicate with the board again.

Python Serial

Up until this point I have been communicating with the board via Python by simply opening the device (/dev/ttyACM0) as a file. The default baud rate was 9600 baud so this worked fine but now that I changed the baud rate I needed to do a fcntl() on the file descriptor to change the baud when I open it. To make matters worse the fcntl() parameters are different between Mac and Linux (Raspberry Pi) and messy.

I found this cross platform library called pySerial that could do all this for me. Further more, it implemented a timeout mechanism in case the device doesn't respond (which was a problem I hadn't solved with the file interface). The library was available for all the platforms I wanted and this worked quite well.

Weird Current Readings

So with all the soldering/de-soldering, the calibration was a tiny bit off. Furthermore I haven't really tried to calibrate the current yet so I wasn't surprised to see the readings were wrong.

When the power supply was running with 10V output and zero output current the reading was around 22mA. I just assumed this was an offset error but I noticed that as the voltage increased so did the zero reading (pretty much linearly). Interestingly the current measured when a load was placed on the output looked pretty close.

I assumed that I had a current leak but I really couldn't find a possible current path. Sure the differential amplifier measuring the output voltage will take some current but it is tiny compared with this.

I found another small ground problem for the amplifier that measures the current but it was pretty small (few hundred micro-volts). I measured the voltage across the shunt and it was tiny - far smaller than the reported value.

Being quite stumped at this point I went back to the simulation and found that it too had a discrepancy. This was masked by the switch model I used for the output load as even when the switch was off a few mA flowed. When I disabled this I noticed that there was a discrepancy there too.

I was looking at the output of the amplifier that senses the voltage over the shunt. I realized this voltage was also used in the summing amplifier inside the pre-regulator. The summing amplifier boosts the bulk capacitor voltage when there is output load. The summing amplifier looks like this:

Then it clicked - as the resistor in this amplifier were only 10K, the Voutmon was affecting Ioutmon and this is why the current reading was high when the output current was low (or zero).

To test this I cut the track to R46 from Ioutmon and sure enough the offset went away.

I went back to the simulation and tried changing the 10K resistors to 100K and while this massively reduced the effect it created ringing and other dynamic stability problems in the summing amplifier.

I think to fix this I'd have to buffer the output with another amplifier. The current measurement is likely effecting the output voltage measurement when there is load on the supply. I probably need to buffer both.

I have one spare op-amp on one of the LT1639 chips. My plan is to swap the LTC2050 used to measure the output current for a dual opamp package (LTC2051). This unit has the same performance specifications but contains two amplifiers in the same footprint. Then I can use one of these to buffer the current and the spare LT1639 op amp to buffer the voltage and that should be that.

Next

There is still lots of work to do on the GUI but the board is getting to the point where I am not sure I want to hack it any more. It might be time to spin a new PCB to fix all the problems.