Latest Entries »


Continuing the series on examining devices to measure AC current, this time we’ll try out the CS5464 from Cirrus Logic.  I initially built it up on  a breadboard, but I’ll skip writing up this test and instead build up a prototype and run AC line power through it.

This is a Three-channel, Single-phase Power/Energy monitoring chip, and also can use current shunts and is intended for power meters.

This device provides no direct isolation, instead the entire device (input and output) directly coupled to the AC power line.  Any isolation must be provided separately.

Taking a look at the project

  • Monitor AC line voltage  from 80 to 150 VAC RMS
  • check for low and high voltage conditions
  • stretch goal:  identify short duration brown-outs and voltage spikes, the type caused by sudden switching of loads

Monitor AC current on 2 separate loads on the same AC circuit (phase)

  • Measure instantaneous current from 0 to 10 Amps, with 0.5 Amp accuracy
  • Detect low current and overcurrent conditions
  • Stretch goal: identify surges during load switching

The CS5464 chip is really a standalone ASIC device that reads multiple ADC inputs and tracks the values on its own without external intervention.
It has a built-in voltage reference, and temperature sensor for automatic compensation.
It simultaneously tracks a variety of values for each channel, including:

  • Instantaneous Current
  • Instantaneous Voltage
  • Instantaneous Power
  • Active Power Channel
  • RMS Current Channel
  • RMS Voltage Channel

That’s just fantastic for my purposes!

This device should meet the goals of identifying brownouts, voltage spikes, and current surges without lots of reads or external processing.  It should really lower the demands on the main microcontroller, allowing the core processor to focus on control and reporting functions… it should only need to ping the CS5464 occasionally to get the necessary data.

The CS5464 does indeed have a lot of functionality, but it also comes with a lot more complexity, it requires being reset properly after power-up and various configuration commands before you can get the data you’re after.

Cirrus application note

Prototyping with the CS5464 device can be quite simple, if you just want to simulate the AC power/current flowing through a small shunt using a function generator or other safe, low-voltage source.

But, if you want to use it safely with non-isolated AC line voltage, in a real-world situation, then it gets a little more complex.

To provide some margin of safety, I have separated the CS5464 measurement device from the microcontroller (here it’s an Arduino), using the ADuM41xx series galvanic digital isolators from Analog Devices.

But, the CS5464 and the “line side” of the couplers need power… This could be provided by a non-isolated capacitive drop approach, like this one in the application notes:

But, since I already have nice clean power for my microcontroller, I can just use a 5010 Iso-Power device.  It’s small and needs few components.

Here’s the test setup:

Even if I have to power all 8 channels on the ADum7441 devices, the total power will be 25ma, which should be okay for the IsoPower chip to run.

Here’s the prototype, built up on perfboard.  Sections of the board are marked:
Red is the AC input/shunt section
Green indicates the isolated, low voltage section that connects to the microcontroller
The unmarked area is logic-level signalling, but is tied directly to the AC line (no isolation)



I tested the prototype board measuring AC (line) current using a set of 150W lamps.

 The test load is connected to the AC power and shunt via the IEC plug that is broken out to insulated spade connectors.


Test Results:



Shunt Reading



Counts, less

395 offset


per Amp



















The results are very linear, with only a little, steady increase in counts as the current rose.  This could have been caused by thermal drift as the shunt warmed up under load.
Here’s some sample code for working with the CS5464.  This code is not really my own, as much of it is a collection of snippets that I came across.  I’ll see if I can find the original sources and reference those.

// For CS5464 chip from Cirrus
// Read the peak current on channel I2
// Check for zero value coming back from CS5464, if so, then reset the device
#include <SPI.h>
// Pin configurations
// set pin 12 as the slave select for the digital pot:
const int slaveSelectPin = 12;
// set pin 11 as the Reset pin for the CS5464:
const int resetPin = 11;
// Create a data type for the 4 Byte data and command packet
union FourByte {
 struct {
   unsigned long value: 24; //24bit register values go in here
   byte command: 8; //8bit command goes in here.
 byte bit8[4]; //this is just used for efficient conversion of the above into 4 bytes.
// Initialize SPI and pins
void setup() {
 // reset the CS5464 device
 pinMode(resetPin, OUTPUT); 
 digitalWrite(resetPin, LOW);
 digitalWrite(resetPin, HIGH);
 SPI.setDataMode(SPI_MODE3); //I believe it to be Mode3
 pinMode(slaveSelectPin, OUTPUT); 
 digitalWrite(slaveSelectPin, HIGH);
 delay(5000);  // Pause allowing time to open the serial monitor
//Perform software reset:
 unsigned long status;
 do {
   status = SPI_read(0b00011110); //read the status register
   status &= (1UL << 23);
 } while (!status);
 // SPI_writeCommand(0xE0); //Set single conversion mode
 SPI_writeCommand(0xE8); //Set continuous conversion mode
// Print the configuration register, to confirm communication with the CS5464
 check = SPI_read(0x00); //read the config register.
 Serial1.print("Config = ");
 Serial1.println(check, HEX);
   //example of writing a register
   union FourByte data;
   data.command = 0b01000000; //Write to config register
   data.value = 1; //This is the default value from datasheet, just using it as an example.
// Main loop
void loop() {
 //example of reading data
 //  unsigned long voltage = SPI_read(0b00001110); //read Register 7  Instantaneous Current Channel 2
 unsigned long voltage = SPI_read(0b00101100); //read Register 22  Peak Current Channel 2
 voltage = voltage >> 8;
 Serial1.print("Peak Current = ");
 Serial1.print(voltage, HEX);
 Serial1.print ("  ");
 // check if the device has accidentally reset
 // Found that noise from switching AC loads can cause the CS5464 to lock-up.
 //   need to improve power or signal filtering, but this is a patch for initial testing.
 if (voltage == 0) { // If we get a 0 reading, then reconfigure the device
   SPI_writeCommand(0xE8); //Set continuous conversion mode
   unsigned long check = SPI_read(0x00); //read the config register.
   Serial1.print("Config = ");
   Serial1.println(check, HEX);
// Send a Command to the CS5464 - see the data sheet for commands
void SPI_writeCommand(byte command) {
 digitalWrite(slaveSelectPin, LOW); //SS goes low to mark start of transmission
 union FourByte data = {0xFEFEFE, command}; //generate the data to be sent, i.e. your command plus the Sync bytes.
 for (char i = 3; i >= 0; i--) {
   SPI.transfer(data.bit8[i]); //transfer all 4 bytes of data - command first, then Big Endian transfer of the 24bit value.
   Serial1.print(data.bit8[i], HEX);
 digitalWrite(slaveSelectPin, HIGH);
// Read a register from the CS5464 - just supply a command byte (see the datasheet)
unsigned long SPI_read(byte command) {
 digitalWrite(slaveSelectPin, LOW); //SS goes low to mark start of transmission
 union FourByte data = {0xFEFEFE, command}; //generate the data to be sent, i.e. your command plus the Sync bytes.
 // Serial.print("SPI_Read:");
 for (char i = 3; i >= 0; i--) {
   data.bit8[i] = SPI.transfer(data.bit8[i]); //send the data whilst reading in the result
   //   Serial.print(data.bit8[i], HEX);
 //  Serial.println();
 digitalWrite(slaveSelectPin, HIGH); //SS goes high to mark end of transmission
 return data.value; //return the 24bit value recieved.

variacAdding a digital volt meter to a variac, adding a missing ground strap, and insulation testing of the chassis powder coat.

The display I installed is available from

Panel Meter, Snap-in 3 Digit 300VAC


Fume extractor

I’ve used the usual free-standing table top fume extractors, but they’re noisy and always seem to be in the way. 

So, when I was reconfiguring my bench, I built the extractor into it.


It consists of a cheap $10, bathroom vent fan with a couple of PVC connections from the intake that run through the front of the bench.  From the exhaust side, a long pvc pipe runs along the back of the bench, and blows it against the floor 6 feet away.  Sorry, no pictures of the steup on the back of the bench… although it is on wheels, the fully loaded bench weighs a few hundred pounds, so I don’t move it often.


The intake is Loc-Line anti-static vacuum tubing, from  This is a series of 2.5 inch diameter, hard plastic segments that interlock to make a flexible tube, and capped with a rectangular nozzle.  Part numbers:  81302AS, 81304AS.

Nothing fancy, there’s no trim ring around the intake.  The switch on the left of the intake controls the fan.


The flex-tubing is connected to a PVC elbow, that elbow is just press-fit into the intake.  This has worked well, it is easy to remove and to adjust.


I was using the Hakko FX-951 units, but upgraded to JBC.
Here’s the JBC DIT and JBC compact with micro-tweezers.JBC

At the back-right is a DS-983A solder dispenser.
On the left is the intake for the fume extractor. Soldering_setup

The rework equipment, a CSI-474A desoldering gun, and a CSI-825A+ hot air unit.


Nav Beacon – Control Board

The control board design is complete, ready to etch it and see if it works… With 127 components (352 connections) there’s probably a Vdd or Gnd trace missing somewhere.


And the top and bottom layouts, without the copper pours.  To control noise on the digital lines, the bottom layer will be as continuous a copper pour as possible.






New Mantis Compact inspection scope arrived, with articulated boom arm.  The Mantis on the left with the standard binocular AmScope on the right.


This scope provides a great 3D view, without peering through little eye pieces.  The Mantis provides a much sharper image than the Amscope’s optics.  And you can shift sligtly while looking through the Mantis and change your perspective of the board, this is very handy for getting a better perspective when working on small devices.

The AmScope is still handy for high magnification (up to 200x), but most inspection work is in the 4x –  8x range, and the Mantis is great for that.

The optional articulating arm provides good reach across the workbench, and was a worthwhile option.


Nav Beacon – breadboarded

Here’s the main controller, on the breadboard, and the readings from the Vref and SPI ADC.

Uses a PIC18F26K22 as the main controller, reads settings from BCD switches and controls SSD relays via a pair of MCP23S08 SPI chips, beacon and marker lamp currents are measured using 50 Amp ACD756 hall-effect sensors, their output is digitized using an MCP3004 ADC set to take differential readings.




As I continue to use the Microchip C18 compiler I find more and more issues. Microchip’s idiots appear to have left the peripherals lib out of the linker for the PIC18F2x/45K50 chips.
After changing over to use the K22 series of microcontrollers (specifically the PIC18F26K22), I found that the SPI libraries are missing from the linker. The USART libraries are there, but not the SPI.
Programs using SPI compile just fine, but won’t link…

Keep it up Microchip, and you may yet convince everyone to use Atmel.

So, I switched to an older compiler, version 3.40, and it works fine.

Here’s the code:

* Uses SPI to read an MCP3004, and writes the results to the serial port.

#include <p18F26K22.h>
#include <usart.h>
#include <stdio.h>
#include <stdlib.h>
#include <spi.h>
#include <delays.h>

#pragma config FOSC = INTIO7 //, MCLRE = ON
#pragma  WDTEN = 0;  // Disable the watchdog timer

IRCF<2:0>: Internal RC Oscillator Frequency Select bits(2)
111 = HFINTOSC ? (16 MHz)
110 = HFINTOSC/2 ? (8 MHz)
101 = HFINTOSC/4 ? (4 MHz)
100 = HFINTOSC/8 ? (2 MHz)
011 = HFINTOSC/16 ? (1 MHz)(3)
unsigned char msb1, msb2, lsb1, lsb2;

int ch_data;

/* SPI pins
* 14 SCK
* 15 SDI = MISO
* 16 SDO = MOSI */
#define ADC_CS PORTCbits.RC2 // pin 13 = chip select for ADC

void main(void)
OSCCONbits.IRCF = 0b101; //change Fosc to 4Mhz

ANSELC = 0; //Analog ports set to digital

// Have to explicitly set the IO on the SPI pins
TRISCbits.RC2 = 0; // Chip Select ADC_CS
TRISCbits.RC3 = 0; // SCK 1
TRISCbits.RC4 = 1; // SDI 1
TRISCbits.RC5 = 0; // SDO 1

ADC_CS = 1; // disable chip

// Open the USART configured as 8N1, 2400 baud, in polled mode


// write a break and Hello to the serial port.
putrs1USART(“\n\n\n\n\n\n\r !!! hello !!! \n\r”);

while (1) {

ADC_CS = 0; // pull chip select low RA4

msb1 = ReadSPI1();
lsb1 = ReadSPI1();

ADC_CS = 1; // disable chip
ch_data = msb1;
ch_data = ch_data << 8;
ch_data = ch_data + lsb1;

// write the data to the serial port
printf(“MSB,LSB %d,%d combined: %d\r\n”,msb1,lsb1,ch_data);


PIC18F26K22 – Blink

Here’s a fancier version of the usual blink routine that also demonstrates the various options for setting the internal oscillator.

* A more complicated Blink program for the PIC18F26K22
* Set the clock:
* 111 = HFINTOSC ? (16 MHz)
* 110 = HFINTOSC/2 ? (8 MHz)
* 101 = HFINTOSC/4 ? (4 MHz)
* 100 = HFINTOSC/8 ? (2 MHz)
* 011 = HFINTOSC/16 ? (1 MHz)(3)
* Use the PLL to quadruple the frequency.
* OSCTUNEbits.PLLEN = 1;
* connect LEDs to RA0 - RA5
* The program clock speed can be measured on pin RA6
#include /* MCU header file ***********/

#pragma config WDTEN = OFF
#pragma config FOSC=INTIO7 // ;Internal oscillator block
#pragma config PLLCFG = ON
#pragma config PRICLKEN = ON
#pragma config IESO = OFF
#pragma config PWRTEN = OFF // ;Power up timer disabled

int counter;
void main (void)
OSCCON = 0x62; // Fosc = 8MHz
OSCCONbits.SCS0 = 0;
OSCCONbits.SCS1 = 0;
OSCTUNEbits.PLLEN = 1; // Use the PLL to up the clock to 32Mhz

counter = 1;
TRISA = 0; /* configure PORTB for output */
while (counter <= 255)
PORTA = counter; /* display value of ‘counter’ on the LEDs */


After having compiler issues using the new PIC18F25K50 chip, I switched to the K22 series.  Specifically the 28 pin PIC18F26K22.  The dual USART, dual SPI/I2C interfaces will be handy and allow for more flexible board layouts.

But, I had some difficulty getting the serial output to work.  I read through a variety of examples, and questions/problems posted by others, most without responses.  I did find several examples, but none of these worked.  With some additional messing about, I managed to come up with this minimal Hello World example.

Hopefully the following will save someone else some time.

/* Serial Hello World
* This works!
* Clock set to 4Mhz, no PLL, transmits at 2400 Baud
* PIC18F26K22
* Complier: C18 v 3.45


#pragma config FOSC = INTIO7 // internal clock, clock output on RA6

IRCF: Internal RC Oscillator Frequency Select bits(2)
111 = HFINTOSC (16 MHz)
110 = HFINTOSC/2 (8 MHz)
101 = HFINTOSC/4 (4 MHz)
100 = HFINTOSC/8 (2 MHz)
011 = HFINTOSC/16 (1 MHz)

void main (void)
OSCCONbits.IRCF = 0b101; //change Fosc to 4Mhz
// wait until IOFS = 1 (osc. stable)
while (!OSCCONbits.IOFS)
* Open the USART configured as
* 8N1, 2400 baud, in polled mode

while (1)
putrs1USART(“Hello World!\n\r”);


Get every new post delivered to your Inbox.