Using the Cirrus Logic CS5464 for AC Current Measurement

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
Goals:

  • 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)

Testing

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:

Input

(Amps)

Shunt Reading

(mV)

Counts

Counts, less

395 offset

Counts

per Amp

0

0.045

395

1.21

12.155

2444

2049

1693

2.41

24.152

4488

4093

1698

3.58

35.96

6500

6105

1705

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.

//  ** updated 6/26/2015 **
// This is written for an Arduino Leonardo.
// - it uses Serial1 instead of Serial, if you change it to Serial it should work for an UNO or other boards
//
// 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); 
 delay(100);
 digitalWrite(resetPin, LOW);
 delay(100);
 digitalWrite(resetPin, HIGH);
 delay(100);
 
 SPI.begin();
 SPI.setBitOrder(MSBFIRST);
 SPI.setDataMode(SPI_MODE3); //I believe it to be Mode3
 SPI.setClockDivider(SPI_CLOCK_DIV16);
 pinMode(slaveSelectPin, OUTPUT); // Do this manually, as the SPI library doesn't work right for all versions.
 digitalWrite(slaveSelectPin, HIGH);

 Serial1.begin(9600);
 delay(5000); // Pause allowing time to open the serial monitor
 Serial1.println("setup...");
 
 //Perform software reset:
 SPI_writeCommand(0x80);
 delay(2000);
 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.
 SPI_writeRegister(data);
 */
}

// Main loop
void loop() {
 delay(1000);
 //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 (" ");
 Serial1.println(voltage);

 // 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
 delay(1000);
 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.
 Serial.print("SPI_writeCommand:");
 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.
 Serial.print(data.bit8[i], HEX);
 }
 Serial.println();
 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.
}

14 comments

  • Hi,
    Very nice video!!!
    3:11 in the video you show 3 ADC on the CS5464. Does this mean you can measure three different AC current on 3 different loads?
    Is it even possible to measure current on different phase (in Sweden we have 3 phase in the houses) with one CS5464?

    Thank you again for a very good video.

    Best Regards.
    /Dennis

    • Thank you.
      The CS5464 has 3 channels, but they are configured as 2 current channels and 1 for voltage measurement. But, Cirrus Logic has an identical chip with 4 channels that is described as “…popular in network meters and commercial three-phase metering applications…” I’m not sure if it will do what you need, but it’s worth a look.

      You might also look at the other chip I reviewed, the ADE7913. That has 3 24-bit ADCs and you could use one for each phase… but you would need another chip or approach to measure the voltage on each phase.

      I have not worked with 3-phase power, but I would think you could just measure the current and voltage on 2 of the phase wires and know the total power. Check out the info about the CS5467 and see if that will do the job.
      http://www.cirrus.com/en/products/cs5467.html

  • Hi, trying to compile you code with errors

    Arduino: 1.5.8 (Mac OS X), Board: “Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)”

    Ciruss_Logic_CS5464_.ino:98:2: error: missing terminating ” character
    Ciruss_Logic_CS5464_.ino: In function ‘void setup()’:
    Ciruss_Logic_CS5464_.ino:49:32: error: ‘SPI_read’ was not declared in this scope
    Ciruss_Logic_CS5464_.ino:57:2: error: ‘check’ was not declared in this scope
    Ciruss_Logic_CS5464_.ino:57:23: error: ‘SPI_read’ was not declared in this scope
    Ciruss_Logic_CS5464_.ino: In function ‘void loop()’:
    Ciruss_Logic_CS5464_.ino:75:45: error: ‘SPI_read’ was not declared in this scope
    Ciruss_Logic_CS5464_.ino: In function ‘void SPI_writeCommand(byte)’:
    Ciruss_Logic_CS5464_.ino:99:27: error: ‘i’ was not declared in this scope
    Ciruss_Logic_CS5464_.ino: At global scope:
    Ciruss_Logic_CS5464_.ino:102:2: error: ‘Serial1’ does not name a type
    Ciruss_Logic_CS5464_.ino:103:14: error: expected constructor, destructor, or type conversion before ‘(‘ token
    Ciruss_Logic_CS5464_.ino:104:1: error: expected declaration before ‘}’ token
    Error compiling.

    This report would have more information with
    “Show verbose output during compilation”
    enabled in File > Preferences.

    • I’ll have to look at that. I was using an older version of the IDE on Windows 7 for an Uno, but I don’t think that should matter. I may have posted the wrong version of the code.

  • The code does not compile, as mentioned above. Any updates?
    Also, you mention this chip is fantastic for your purpose. What is your purpose?

    • I’ll look for the code. I didn’t check-in some of my arduino code to my SVN server… and now it’s a bit of digging.

      The purpose for this is to measure the AC line current on two circuits and measure the AC line voltage. This will be used to monitor the navigation beacons on a 400 foot radio tower.

      • OK. Found what should be the correct code and it compiles just fine… for an Arduino Leonardo. The main thing is that this uses Serial1 not just Serial. You can search/replace all the Serial1 with Serial and it should work just fine. You’ll just get all the output on the same terminal/debug window.

  • Can you post the correct code? Thanks.

    • This is the code I had in SVN for this test. As noted above, it is setup for an Arduino Leonardo, which has both Serial and Serial1.
      Is there a particular problem that you are hitting with the code?

  • Stefan Schreiter

    Will the code also work with the CS5480?

    • I think you’ll have to change the code some, as the CS5484 has a lot more registers, and some of the addresses are different. I haven’t compared the command sets yet, but I would expect some differences just based on the changes to the registers.

  • Hi, I was wondering if you could share the code which you used for the Analog Devices ADE7913 demo which you posted on youtube? I had a look for the page relating to that on corgitronics.com but could not find it.

    I’m working on an application where I need to use the ADE7913 with an Arduino board, and am trying to figure out the SPI comms stuff, and having a working example would help me out a lot.

    So far I’ve failed at the first hurdle, as I had intended to provide the 4MHz clock from the Arduino rather than a crystal oscillator, but it seems the 4MHz clock I’m generating from the Arduino doesn’t make it through the 5V => 3.3V level shifter in tact. Annoying as I checked the rise/fall times of the shifter and they looked as though they’d be fast enough.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s