bontor humala / ADE7758

Fork of ADE7758 by Emma

ade7758.cpp

Committer:
bonchenko
Date:
2015-04-22
Revision:
7:54310bde2e53
Parent:
4:5547bb32eb32

File content as of revision 7:54310bde2e53:

#include "mbed.h"
#include "ade7758.h"
#include "SWSPI.h"

// public
ADE7758::ADE7758(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName interrupt):
    _ADE7758SPI(mosi, miso, sclk), _ADESS(cs), _ADEINT(interrupt)
{
    _ADESS = 1;
}

void ADE7758::begin()
{
    enableChip();
    //normal mode
    _ADE7758SPI.format(8, 1); // pha 0, pol 1
    _ADE7758SPI.frequency(1000000);    
    write8bits(OPMODE, 0x04);
}

void ADE7758::calibrateVI(uint8_t numSamples) 
{    
    write8bits(LCYCMODE, 0x38);
    write24bits(MASK, 0xE00);
    long AVRMSBuffer = 0, BVRMSBuffer = 0, CVRMSBuffer = 0;
    long AIRMSBuffer = 0, BIRMSBuffer = 0, CIRMSBuffer = 0;
    for (uint8_t i=0; i<numSamples; i++) {
        read24bits(RSTATUS);
        while ( _ADEINT != 0 ) { } // wait until INT go low
        AVRMSBuffer += VRMS(PHASE_A);
        BVRMSBuffer += VRMS(PHASE_B);
        CVRMSBuffer += VRMS(PHASE_C);
        AIRMSBuffer += IRMS(PHASE_A);
        BIRMSBuffer += IRMS(PHASE_B);
        CIRMSBuffer += IRMS(PHASE_C);
    }
    AVRMSCalib = AVRMSBuffer/numSamples;
    BVRMSCalib = BVRMSBuffer/numSamples;
    CVRMSCalib = CVRMSBuffer/numSamples;
    AIRMSCalib = AIRMSBuffer/numSamples;
    BIRMSCalib = BIRMSBuffer/numSamples;
    CIRMSCalib = CIRMSBuffer/numSamples;
}

void ADE7758::writeRMSOffset(uint16_t AIRMSOSValue, uint16_t BIRMSOSValue, uint16_t CIRMSOSValue, uint16_t AVRMSOSValue, uint16_t BVRMSOSValue, uint16_t CVRMSOSValue) {
    // Collect for ITEST & VNOM, IMIN & VMIN
    // Calculate these values in an Excel sheet according to page
    // Write the results to the xIRMSOS and xVRMSOS registers
    write16bits(AIRMSOS, AIRMSOSValue); // Current channel A
    write16bits(BIRMSOS, BIRMSOSValue); // Current channel B
    write16bits(CIRMSOS, CIRMSOSValue); // Current channel C
    write16bits(AVRMSOS, AVRMSOSValue); // Voltage channel A
    write16bits(BVRMSOS, BVRMSOSValue); // Voltage channel B
    write16bits(CVRMSOS, CVRMSOSValue); // Voltage channel C
}

void ADE7758::calibrateGain(char phase) { // see datasheet ADE 7758 page 49 of 72
    // 1. Clear xWG, xVARG, xVAG
    write16bits(AWG, 0x0000);
    write16bits(BWG, 0x0000);
    write16bits(CWG, 0x0000);
    write16bits(AVARG, 0x0000);
    write16bits(BVARG, 0x0000);
    write16bits(CVARG, 0x0000);
    write16bits(AVAG, 0x0000);
    write16bits(BVAG, 0x0000);
    write16bits(CVAG, 0x0000);
    
    // 2. Select phase for line period measurement
    lineFreq(phase);

    // 3. Configure LCYCMODE register with 0xBF
    write8bits(LCYCMODE, 0xBF);
    
    // 4. Set number of half line cycles
    write16bits(LINECYC, 0x800);
    
    // 5. Set LENERGY bit in MASK register for interrupt
    uint16_t mask;
    mask = read16bits(MASK);
    write24bits(MASK, mask | (1<<LENERGY));
    wait_ms(10);
    
    // 6. Environment setting
    // set environment - ITEST and VNOM
    
    // 7. Reset RSTATUS
    read24bits(RSTATUS);
    
    // 8. Wait LENERGY interrupt and read 6 energy registers
    while ( _ADEINT != 0 ) { } // wait until INT go low
    int AWATTHRTemp = getWattHR(PHASE_A);
    int AVAHRTemp = getVAHR(PHASE_A);
    int BWATTHRTemp = getWattHR(PHASE_B);
    int BVAHRTemp = getVAHR(PHASE_B);
    int CWATTHRTemp = getWattHR(PHASE_C);
    int CVAHRTemp = getVAHR(PHASE_C);

    // 9. Calculate Wh/LSB and VAh/LSB
    float accumTime = getAccumulationTime(PHASE_A);
    float tempEnergy = ITEST*VNOM*accumTime;
    AWhLSB = tempEnergy/(3600*AWATTHRTemp);
    BWhLSB = tempEnergy/(3600*BWATTHRTemp);
    CWhLSB = tempEnergy/(3600*CWATTHRTemp);
    AVAhLSB = tempEnergy/(3600*AVAHRTemp);
    BVAhLSB = tempEnergy/(3600*BVAHRTemp);
    CVAhLSB = tempEnergy/(3600*CVAHRTemp);

    // 10. Clear and wait LENERGY interrupt and read 6 energy registers
    wait_ms(10);
    read24bits(RSTATUS);
    while ( _ADEINT != 0 ) { } // wait until INT go low
    int AWHREXPECTED = getWattHR(PHASE_A);
    int AVAHREXPECTED = getVAHR(PHASE_A);
    int BWHREXPECTED = getWattHR(PHASE_B);
    int BVAHREXPECTED = getVAHR(PHASE_B);
    int CWHREXPECTED = getWattHR(PHASE_C);
    int CVAHREXPECTED = getVAHR(PHASE_C);

    // 11. Calculate xWG and xVAG values
    AWGCalib = ((float)(AWHREXPECTED/AWATTHRTemp) - 1)*4096;
    BWGCalib = ((float)(BWHREXPECTED/BWATTHRTemp) - 1)*4096;
    CWGCalib = ((float)(CWHREXPECTED/CWATTHRTemp) - 1)*4096;
    AVAGCalib = ((float)(AVAHREXPECTED/AVAHRTemp) - 1)*4096;
    BVAGCalib = ((float)(BVAHREXPECTED/BVAHRTemp) - 1)*4096;
    CVAGCalib = ((float)(CVAHREXPECTED/CVAHRTemp) - 1)*4096;

    // 10. Write the values to xWG and xVAG
    write16bits(AWG, AWGCalib);
    write16bits(BWG, BWGCalib);
    write16bits(CWG, CWGCalib);
    write16bits(AVAG, AVAGCalib);
    write16bits(BVAG, BVAGCalib);
    write16bits(CVAG, CVAGCalib);
}

float ADE7758::getAccumulationTime(char phase) {
    uint16_t frequency = lineFreq(phase);
    uint16_t LineCYC = read16bits(LINECYC);
    write8bits(LCYCMODE, 0xBF);
    float LineFrequency = 1/(frequency*0.0000096);
    return LineCYC/(2*LineFrequency*3);
}

int ADE7758::getWattHR(char phase)
{
    return read16bits(AWATTHR+phase);
}

int ADE7758::getVARHR(char phase)
{
    return read16bits(AVARHR+phase);
}

int ADE7758::getVAHR(char phase)
{
    return read16bits(AVAHR+phase);
}

int ADE7758::WattHR(char phase)
{
    return getWattHR(phase);
}

int ADE7758::VARHR(char phase)
{
    return getVARHR(phase);
}

int ADE7758::VAHR(char phase)
{
    return getVAHR(phase);
}

long ADE7758::VRMS(char phase)
{
    char i=0;
    long volts=0;
    getVRMS(phase);//Ignore first reading
    for(i=0;i<10;++i){
        volts+=getVRMS(phase);
        wait_us(50);
    }
    //average
    return volts/10;
}

long ADE7758::IRMS(char phase)
{
  char i=0;
  long current=0;
  getIRMS(phase);//Ignore first reading
  for(i=0;i<10;++i){
    current+=getIRMS(phase);
    wait_us(50);
  }
  //average
  return current/10;
}


float ADE7758::calculateIRMS(char phase) {
    long IRMSBuffer = 0;
    float AvgIRMS = 0;
    for (char i=0; i<NUMSAMPLES; i++) {
        IRMSBuffer += IRMS(phase);
    }
    AvgIRMS = IRMSBuffer/NUMSAMPLES;

    if ( phase == PHASE_A ) {
        return AvgIRMS * 0.0000125;
    }
    else if ( phase == PHASE_B ) {
        return AvgIRMS * 0.0000123;
    }    
    else if ( phase == PHASE_C ) {
        return AvgIRMS * 0.0000124;
    }
    else { return 0; }
}

float ADE7758::calculateVRMS(char phase) {
    long VRMSBuffer = 0;
    float AvgVRMS = 0;
    for (char i=0; i<NUMSAMPLES; i++) {
        VRMSBuffer += VRMS(phase);
    }
    AvgVRMS = VRMSBuffer/NUMSAMPLES;

    if ( phase == PHASE_A ) {
        return AvgVRMS * 0.000158;
    }
    else if ( phase == PHASE_B ) {
        return AvgVRMS * 0.000157;
    }
    else if ( phase == PHASE_C ) {
        return AvgVRMS * 0.000156;
    }
    else { return 0; }
}

void ADE7758::getEnergy(char phase, uint8_t samplingPeriod, float *AWattHr, float *BWattHr, float *CWattHr, float *AVAHr, float *BVAHr, float *CVAHr) {    
    float period = 0;
    long AWattHrSum = 0;
    long BWattHrSum = 0;    
    long CWattHrSum = 0;
    long AVAHrSum = 0;
    long BVAHrSum = 0;    
    long CVAHrSum = 0;
        
    uint16_t AWattHrValue, BWattHrValue, CWattHrValue, AVAHrValue, BVAHrValue, CVAHrValue;
    while (period < samplingPeriod*60.0) {
        period += ADE.getAccumulationTime(PHASE_A);
        ADE7758::getAccumulatedEnergy(phase, &AWattHrValue, &BWattHrValue, &CWattHrValue, &AVAHrValue, &BVAHrValue, &CVAHrValue);
        AWattHrSum += AWattHrValue;
        BWattHrSum += BWattHrValue;
        CWattHrSum += CWattHrValue;
        AVAHrSum += AVAHrValue;
        BVAHrSum += BVAHrValue; 
        CVAHrSum += CVAHrValue;
    }
    *AWattHr = AWattHrSum * AWhLSB;
    *BWattHr = BWattHrSum * BWhLSB;
    *CWattHr = CWattHrSum * CWhLSB;
    
    *AVAHr = AVAHrSum * AVAhLSB;
    *BVAHr = BVAHrSum * BVAhLSB;
    *CVAHr = CVAHrSum * CVAhLSB;
}

void ADE7758::getAccumulatedEnergy(char phase, uint16_t *AWattHr, uint16_t *BWattHr, uint16_t *CWattHr, uint16_t *AVAHr, uint16_t *BVAHr, uint16_t *CVAHr)
{
    // 1. Set phase used for line zero cross detection
    lineFreq(phase);

    // 2. Configure LCYCMODE register with 0xBF
    write8bits(LCYCMODE, 0xBF);    

    // 3. Set number of half line cycles
    write16bits(LINECYC, 0x800);    

    // 4. Set LENERGY bit in MASK register for interrupt
    uint16_t mask;
    mask = read16bits(MASK);
    write24bits(MASK, mask | (1<<LENERGY));
    wait_ms(10);

    // 5. Reset RSTATUS
    read24bits(RSTATUS);
    
    // 6. Wait LENERGY interrupt and read 6 energy registers
    while ( _ADEINT != 0 ) { } // wait until INT go low
    *AWattHr = getWattHR(PHASE_A);
    *AVAHr = getVAHR(PHASE_A);
    *BWattHr = getWattHR(PHASE_B);
    *BVAHr = getVAHR(PHASE_B);
    *CWattHr = getWattHR(PHASE_C);
    *CVAHr = getVAHR(PHASE_C);
}

long ADE7758::waveform(char phase,char source)
{
  return 1;
}

void ADE7758::powerOff()
{

}

void ADE7758::powerON()
{

}

void ADE7758::sleep()
{

}

void ADE7758::wakeUp()
{

}

long ADE7758::getInterruptStatus(void){
        return read24bits(STATUS);
}

long ADE7758::getResetInterruptStatus(void){
        return read24bits(RSTATUS);
}

int ADE7758::lineFreq(char phase){
    uint8_t mmode;
    mmode = read8bits(MMODE);
    write8bits(MMODE,( mmode&0xFC )| phase);
    wait_ms(10);
    return read16bits(FREQ);
}
// private

void ADE7758::enableChip()
{
    _ADESS = 0;
}

void ADE7758::disableChip()
{
    _ADESS = 1;
}

void ADE7758::write8bits(char reg, unsigned char data)
{
    enableChip();
        
    wait_ms(10);
    _ADE7758SPI.write(REG_WRITE(reg));
    wait_ms(2);

    _ADE7758SPI.write(data);

    wait_ms(1);
    
    disableChip();
}

void ADE7758::write16bits(char reg, unsigned int data)
{
    enableChip();
    
    wait_ms(10);
    _ADE7758SPI.write(REG_WRITE(reg));
    wait_ms(2);
    _ADE7758SPI.write((unsigned char)((data>>8)&0xFF));
    wait_ms(2);
    _ADE7758SPI.write((unsigned char)(data&0xFF));
    wait_ms(1);

    disableChip();
}

void ADE7758::write24bits(char reg, unsigned int data)
{
    enableChip();
    
    wait_ms(10);
    _ADE7758SPI.write(REG_WRITE(reg));
    wait_ms(2);
    _ADE7758SPI.write((unsigned char)((data>>16)&0xFF));
    wait_ms(2);
    _ADE7758SPI.write((unsigned char)((data>>8)&0xFF));
    wait_ms(2);
    _ADE7758SPI.write((unsigned char)(data&0xFF));
    wait_ms(1);

    disableChip();
}

unsigned char ADE7758::read8bits(char reg)
{
    enableChip();
    
    unsigned char ret;
    wait_ms(10);
    _ADE7758SPI.write(REG_READ(reg));
    wait_ms(2);
    ret=_ADE7758SPI.write(0x00);
    wait_ms(1);
    
    disableChip();

    return ret;
}

unsigned int ADE7758::read16bits(char reg)
{
    enableChip();
    unsigned int ret=0;
    unsigned char ret0=0;
    wait_ms(10);
    _ADE7758SPI.write(REG_READ(reg));
    wait_ms(2);
    ret=_ADE7758SPI.write(0x00);
    wait_ms(2);
    ret0=_ADE7758SPI.write(0x00);
    wait_ms(1);
    
    disableChip();
    ret= (ret<<8)|ret0;
    return ret;
}

unsigned long ADE7758::read24bits(char reg)
{
    enableChip();
    unsigned long ret=0;
    unsigned int ret1=0;
    unsigned char ret0=0;
    wait_ms(10);
    _ADE7758SPI.write(REG_READ(reg));
    wait_ms(2);
    ret=_ADE7758SPI.write(0x00);
    wait_ms(2);
    ret1=_ADE7758SPI.write(0x00);
    wait_ms(2);
    ret0=_ADE7758SPI.write(0x00);
    wait_ms(1);
    
    disableChip();
    ret= (ret<<16)|(ret1<<8)| ret0;
    return ret;
}

long ADE7758::getIRMS(char phase)
{
    return read24bits(AIRMS+phase);
}

long ADE7758::getVRMS(char phase)
{          
    return read24bits(AVRMS+phase);
}

// ADE7758 ADE(mosi, miso, sclk, cs);