#include #include "ADE7953_reg.h" void ADE7953_SPI_WRITE(uint16_t Address , uint32_t Data , uint8_t Number_of_bytes); uint32_t ADE7953_SPI_READ (uint16_t Address, uint8_t Number_of_bytes); void ade7953_cal_example(); void ADE_SETUP_SPI(); void READ_RMS_ENERGIES_PRINT(); //outputs #define PM_0 PB15 #define PM_1 PA8 #define RESET_B PB3 #define SSB_A PA4 #define SSB_B PB5 #define SSB_C PB4 //inputs #define CF1 PB6 #define CF2 PB7 #define CF3 PC14 #define IRQ0 PB14 #define IRQ1 PB13 #define DREADY PB12 #define SSB_A PA4 //SPI_1 Chip Select pin is PA4. You can change it to the STM32 pin you want. //LED1=PC_13 SERIAL_TX=PA_2 I2C_SCL=PB_8 SPI_MOSI=PA_7 PWM_OUT=PB_3 // SERIAL_RX=PA_3 I2C_SDA=PB_9 SPI_MISO=PA_6 // SPI_SCK =PA_5 // bluepill ADE7953 //PA4 > SSB_A >> SSB //PB5 > SSB_B //PB4 > SSB_C //PA2 > tx >> TX //PA3 > rx >> RX //PA5 > sclk >> SCLK //PA6 > miso >> MISO //PA7 > mosi >> MOSI //PA8 > PM1 >> //PB15 > PM0 >> //PB14 > IRQ0 >> IRQ //PB13 > IRQ1 >> ZX //PB9 > sda >> SDA //PB8 > scl >> SCL //PB3 > RSTB >>RESETB //PB12 > DREADY >> ZX_I //PB6 > CF1 //PB7 > CF2 //PC14 > CF3 byte data; void setup() { // put your setup code here, to run once: Serial.begin(115200); //while (!Serial); // wait for serial to be configured pinMode(SSB_A, OUTPUT); pinMode(RESET_B, OUTPUT); pinMode(MOSI, OUTPUT); pinMode(MISO, OUTPUT); digitalWrite(SSB_A, LOW); // sets up I/o state for spi coms before reset digitalWrite(MISO, LOW); digitalWrite(MOSI, LOW); //reset part delay(1000); // fixed delay will addin IRQ monitor later pinMode(MOSI, INPUT); // change mode for spi not sure if needed pinMode(MISO, INPUT); // change mode for spi not sure if needed pinMode(IRQ0, INPUT); digitalWrite(SSB_A, HIGH); SPI.begin(); //Initialize the SPI_1 port SPI.setBitOrder(MSBFIRST); // Set the SPI_1 bit order SPI.setDataMode(SPI_MODE3); //Set the SPI_2 data mode 0 SPI.setClockDivider(SPI_CLOCK_DIV16); // Slow speed (72 / 16 = 4.5 MHz SPI_1 speed) digitalWrite(RESET_B, LOW); delay(100); digitalWrite(RESET_B, HIGH); while(digitalRead(IRQ0)); //wait for power up IRQ low JP20 needs to be in eval board ADE7953_SPI_READ(RSTIRQSTATA_24,3); //clr startup IRQ ADE7953_reg_config(); // set waveforem sample IRQ //attachInterrupt(digitalPinToInterrupt(IRQ0), IRQ_irq, FALLING ); //attachInterrupt(digitalPinToInterrupt(IRQ1), ZX_irq, FALLING ); } void loop() { //ADE7953_CF_GEN(); ade7953_cal_example(); // READ_RMS_ENERGIES_PRINT(); } void ADE7953_CF_GEN(){ ADE7953_SPI_WRITE(AIGAIN_24, 0x0000, 3); Serial.print("AIGAIN_24 = "); Serial.println(ADE7953_SPI_READ(AIGAIN_24, 3), HEX); ADE7953_SPI_WRITE(CF1DEN, 0x0001, 2); Serial.print("CF1DEN = "); Serial.println(ADE7953_SPI_READ(CF1DEN, 2), HEX); //ADE7953_SPI_WRITE(AVGAIN_24, 0x0000, 3); This can be left default you will get zero crosses but power and energy will be 0 Serial.print("AVGAIN_24 = "); Serial.println(ADE7953_SPI_READ(AVGAIN_24, 3), HEX); ADE7953_SPI_WRITE(AWATTOS_24, 0x7FFFFF, 3); // wattos = energy / 2^9 Serial.print("AIGAIN_24 = "); Serial.println(ADE7953_SPI_READ(AWATTOS_24, 3), HEX); ADE7953_SPI_WRITE(LINECYC,300, 2); // 300 half linecycles or 3 sec at 50Hz 360 half line cycles at 60hz for 3 sec while(1){ if(((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x40000)) == 0x40000){ Serial.println(" "); Serial.print(" IRMS reg = "); Serial.println(ADE7953_SPI_READ(IRMSA_32, 4)); Serial.print(" VRMS reg = "); Serial.println(ADE7953_SPI_READ(VRMS_32, 4)); Serial.print(" AWATT reg = "); Serial.println(ADE7953_SPI_READ(AWATT_32, 4)); Serial.print(" AENERGYA reg = "); Serial.println(ADE7953_SPI_READ(AENERGYA_32, 4),DEC); } } } /////////////////////////////////////////////////////////////////////////////////////////////// void ade7953_cal_example(){ delay(10000); //wait for RMS to settle // enter values uint16_t V_TEST = 220;//volts RMS uint16_t I_TEST = 2;//Amps RMS double Power_factor = 1.00; int meter_constant = 1000; //1000 imp/Kwh for cf output rate int Line_Freq = 50; int accumulation_time = 5; //sec int i = 0; #define CT double R1 = 1000000; //big resistor or sum of upper resisters double R2 = 1000; //small resistor #ifdef CT double CT_Ratio = 3000; // to 1 double burden = 20; // 2 10 ohm differential burdens or 1 20 Ohm single ended. double transfer_ratio = burden/CT_Ratio; // #endif #ifdef SHUNT double Shunt_val = .0002; //Ohms double transfer_ratio = .0002; // IxR = 1Arms * .0002ohms = .0002 vrms at ADC #endif //constants #define PMAX 4862401 // maxpower with fullscale V and fullscale I #define Sample_Rate 6990 #define WTHR_Value 1 // I dont know what the internal threshold is #define V_PGA 1 #define I_PGA 1 #define I_channel_ADC_FS (.3535 / I_PGA) #define V_channel_ADC_FS (.3535 / V_PGA) #define RMS_FS_Codes 9032007 //50hz and 60HZ #define CF_FULLSCALE 207488.00 // cf output with cfden = 1 and fullscale current X voltage // if you write 0x0 to Igain and Vgain and 0x7FFFFF to Awattos power will be 0x7FFFFF / 2^9 = 16384 // cf with cfden=1 is 699.137Hz (pmax / 16384)* 699.137Hz = 207488hz fullscale CF //calculated double Percent_FS_Voltage = (((R2 / (R1 + R2)) * V_TEST) / V_channel_ADC_FS); double Percent_FS_Current = (I_TEST * transfer_ratio ) / I_channel_ADC_FS; //double Percent_FS_Current = ((I_TEST / CT_Ratio) * burden ) / I_channel_ADC_FS; double V_Fullscale = V_TEST / Percent_FS_Voltage; double I_Fullscale = I_TEST / Percent_FS_Current; double Expected_CF_Freq = (meter_constant / 1000 * V_TEST * I_TEST * Power_factor) / 3600; double IRMS_LSB = 0; double VRMS_LSB = 0; double WATT_LSB = 0; double WATTHR_LSB = 0; double AVERAGE = 0; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> //https://www.analog.com/media/en/technical-documentation/data-sheets/ADE7953.pdf pg5 (spec table) , pg 25 (active energy), pg33(pulse output) also mention in reactive and apparent sections // ADE7953_CF_GEN() setsup the part to cal cf at fullscale // note the ADE7953 datasheet specs CF freq with fullscale inputs and cfden=1 or 0, 0 is the same as 1, is 206.9Khz this may be incorrect and will cause errors in cf output after cfden is written // cf output with cfden = 1 and fullscale current X voltage // if you write 0x0 to Igain and Vgain and 0x7FFFFF to Awattos power will be 0x7FFFFF / 2^9 = 16384 // cf with cfden=1 is 699.137Hz (pmax / 16384)* 699.137Hz = 207488hz fullscale CF // using CF_FULLSCALE = 207.488Khz instead of 206.9Khz will calculate CF to the expected value without needed pgain register //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> uint16_t CFDEN_CAL_VALUE = (uint16_t)(( CF_FULLSCALE * Power_factor * Percent_FS_Voltage * Percent_FS_Current) / Expected_CF_Freq); //CFDEN_CAL_VALUE = 0x9B81; //Serial.println(((uint16_t)( CF_FULLSCALE * Power_factor * Percent_FS_Voltage * Percent_FS_Current) / Expected_CF_Freq),6); Serial.println(""); Serial.print(" Averaging Current RMS register..."); // averageing make calibration more accurate AVERAGE = 0; ADE7953_SPI_READ(RSTIRQSTATA_32,4); // clear status bits for (i=0;i<100;i++) { while((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x8000) != 0x8000); // wait for zx flag then clear AVERAGE = AVERAGE + (double)ADE7953_SPI_READ(IRMSA_32, 4); } AVERAGE = AVERAGE / 100; Serial.print("AVERAGE IRMS CODES=");Serial.println(AVERAGE); int32_t AIGAIN_CAL_VALUE = (int32_t)((((RMS_FS_Codes * Percent_FS_Current) / AVERAGE)) * 0x400000); Serial.println(""); Serial.print(" Averaging Voltage RMS register..."); // averageing make calibration more accurate AVERAGE = 0; ADE7953_SPI_READ(RSTIRQSTATA_32,4); // clear status bits for (i=0;i<100;i++) { while((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x8000) != 0x8000); // wait for zx flag then clear AVERAGE = AVERAGE + (double)ADE7953_SPI_READ(VRMS_32, 4); } AVERAGE = AVERAGE/ 100; Serial.print("AVERAGE VRMS CODES=");Serial.println(AVERAGE); int32_t AVGAIN_CAL_VALUE = (int32_t)((((RMS_FS_Codes * Percent_FS_Voltage) / AVERAGE)) * 0x400000); Serial.println(""); Serial.print(" V_TEST Used For Calibration = "); Serial.println(V_TEST); Serial.print(" I_TEST Used For Calibration = "); Serial.println(I_TEST); Serial.print(" Power_factor Used For Calibration = "); Serial.println(Power_factor); Serial.print(" Line_Freq Used For Calibration = "); Serial.println(Line_Freq); Serial.print(" Accumulation Time Used For Calibration = "); Serial.println(accumulation_time); Serial.println(""); Serial.print(" V_TEST = "); Serial.print(Percent_FS_Voltage * 100, 2); Serial.print("% Fullscale"); Serial.print(" = "); Serial.print(V_channel_ADC_FS * Percent_FS_Voltage,4);Serial.print(" VRMS at ADC input and ");Serial.print(V_Fullscale, 2); Serial.println(" VRMS Fullscale"); Serial.print(" I_TEST = "); Serial.print(Percent_FS_Current * 100, 2); Serial.print("% Fullscale"); Serial.print(" = "); Serial.print(I_channel_ADC_FS * Percent_FS_Current,4);Serial.print(" VRMS at ADC input and ");Serial.print(I_Fullscale, 2); Serial.println(" IRMS Fullscale"); Serial.println(""); Serial.print(" Expected_CF_Freq = "); Serial.println(Expected_CF_Freq, 6); Serial.print(" CFDEN_CAL_VALUE = "); Serial.println(CFDEN_CAL_VALUE,HEX); Serial.println(""); ADE7953_SPI_WRITE(CF1DEN, CFDEN_CAL_VALUE, 2); ADE7953_SPI_WRITE(CF2DEN, CFDEN_CAL_VALUE, 2); Serial.print(" AIRMS Expected = "); Serial.println(Percent_FS_Current * RMS_FS_Codes ,0); Serial.print(" AVRMS Expected = "); Serial.println(Percent_FS_Voltage * RMS_FS_Codes ,0); Serial.print(" AIRMS RAW = "); Serial.println(ADE7953_SPI_READ(IRMSA_32, 4), DEC); Serial.print(" AVRMS RAW = "); Serial.println(ADE7953_SPI_READ(VRMS_32, 4), DEC); Serial.println(""); Serial.print(" AIGAIN_CAL_VALUE "); Serial.println(AIGAIN_CAL_VALUE,HEX); Serial.print(" AVGAIN_CAL_VALUE "); Serial.println(AVGAIN_CAL_VALUE,HEX); Serial.println(""); ADE7953_SPI_WRITE(AIGAIN_24, AIGAIN_CAL_VALUE, 3); ADE7953_SPI_WRITE(AVGAIN_24, AVGAIN_CAL_VALUE, 3); delay(5000); //allow for settling after gain changes ADE7953_SPI_WRITE(LINECYC, (accumulation_time / (1.00 / Line_Freq / 2)), 2); // 300 half linecycles or 3 sec at 50Hz 360 half line cycles at 60hz for 3 sec Serial.print(" Line cycle value = "); Serial.println(ADE7953_SPI_READ(LINECYC, 2), DEC); IRMS_LSB = I_TEST / (RMS_FS_Codes * Percent_FS_Current); VRMS_LSB = V_TEST / (RMS_FS_Codes * Percent_FS_Voltage); AVERAGE = 0; for (i=0;i<1000;i++) AVERAGE = AVERAGE + (double)ADE7953_SPI_READ(AWATT_32, 4); AVERAGE = AVERAGE/ 1000; //WATT_LSB = (V_TEST * I_TEST * Power_factor) / AVERAGE; // calculate using register readings WATT_LSB = (V_TEST * I_TEST * Power_factor) / (PMAX * Percent_FS_Voltage * Percent_FS_Current * Power_factor); // calculate using expected values WATTHR_LSB =(V_TEST * I_TEST ) / (CFDEN_CAL_VALUE * Expected_CF_Freq * 3600); // this method uses fact that cf * cfden is related to the AENERGY reg lsb in reg = number of cf pulses * cfden in 1 hr Serial.print(" IRMS_LSB = "); Serial.println(IRMS_LSB, DEC); Serial.print(" VRMS_LSB = "); Serial.println(VRMS_LSB, DEC); Serial.print(" WATT_LSB = "); Serial.println(WATT_LSB, DEC); Serial.print(" WATTHR_LSB = "); Serial.println(WATTHR_LSB, DEC); Serial.println(" "); Serial.print(" IRMS reg * IRMS_LSB = "); ADE7953_SPI_READ(RSTIRQSTATA_32,4); while((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x8000) != 0x8000); Serial.println(ADE7953_SPI_READ(IRMSA_32, 4)* IRMS_LSB,2); Serial.print(" VRMS reg * VRMS_LSB = "); ADE7953_SPI_READ(RSTIRQSTATA_32,4); while((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x8000) != 0x8000); Serial.println(ADE7953_SPI_READ(VRMS_32, 4) * VRMS_LSB,2); Serial.print(" AWATT reg * WATT_LSB = "); Serial.println(ADE7953_SPI_READ(AWATT_32, 4) * WATT_LSB,2); ADE7953_SPI_READ(RSTIRQSTATA_32,4); // clear status bits while(1){ if(((ADE7953_SPI_READ(RSTIRQSTATA_32,4) & 0x40000)) == 0x40000){ Serial.println(" "); Serial.print(" IRMS reg * IRMS_LSB = "); Serial.println(ADE7953_SPI_READ(IRMSA_32, 4)* IRMS_LSB,2); Serial.print(" VRMS reg * VRMS_LSB = "); Serial.println(ADE7953_SPI_READ(VRMS_32, 4) * VRMS_LSB,2); Serial.print(" AWATT reg = "); Serial.print((int32_t)ADE7953_SPI_READ(AWATT_32, 4),DEC); Serial.print(" AWATT reg * WATT_LSB = "); Serial.println((int32_t)ADE7953_SPI_READ(AWATT_32, 4) * WATT_LSB,2); Serial.print(" AVAR reg = "); Serial.print((int32_t)ADE7953_SPI_READ(AVAR_32, 4),DEC); Serial.print(" AVAR reg * WATT_LSB = "); Serial.println((int32_t)ADE7953_SPI_READ(AVAR_32, 4) * WATT_LSB,2); Serial.print(" AVA reg = "); Serial.print((int32_t)ADE7953_SPI_READ(AVA_32, 4),DEC); Serial.print(" AVA reg * WATT_LSB = "); Serial.println((int32_t)ADE7953_SPI_READ(AVA_32, 4) * WATT_LSB,2); Serial.print(" AENERGYA reg = "); Serial.print((int32_t)ADE7953_SPI_READ(AENERGYA_32, 4),DEC);Serial.print(" AENERGYA reg * WATTHR_LSB = "); Serial.print((int32_t)ADE7953_SPI_READ(AENERGYA_32, 4) * WATTHR_LSB,6); Serial.print(" WATTHR in "); Serial.print(accumulation_time,DEC);Serial.println("seconds "); Serial.print(" RENERGYA reg = "); Serial.print((int32_t)ADE7953_SPI_READ(RENERGYA_32, 4),DEC);Serial.print(" RENERGYA reg * WATTHR_LSB = "); Serial.print((int32_t)ADE7953_SPI_READ(RENERGYA_32, 4) * WATTHR_LSB,6); Serial.print(" VARHR in "); Serial.print(accumulation_time,DEC);Serial.println("seconds "); Serial.print(" APENERGYA reg = "); Serial.print((int32_t)ADE7953_SPI_READ(APENERGYA_32, 4),DEC);Serial.print(" APENERGYA_32 reg * WATTHR_LSB = "); Serial.print((int32_t)ADE7953_SPI_READ(APENERGYA_32, 4) * WATTHR_LSB,6); Serial.print(" VARHR in "); Serial.print(accumulation_time,DEC);Serial.println("seconds "); Serial.print(" PF = "); Serial.println(((int32_t)ADE7953_SPI_READ(AENERGYA_32, 4) * WATTHR_LSB )/((int32_t)ADE7953_SPI_READ(APENERGYA_32,4) * WATTHR_LSB),2); Serial.print(" ANGLE_A = "); Serial.print((int16_t)ADE7953_SPI_READ(ANGLE_A, 2)*( 360.00 * Line_Freq / 223000.00 ),2); Serial.print(" resolution = .0807 at 50Hz and 0.0969 at 60Hz"); } } } void IRQ_irq(){ ADE7953_SPI_READ(RSTIRQSTATA_24,3); //resset status register to clear Irq } void ZX_irq(){ Serial.println("ZX Detected"); } void ADE7953_reg_config() { ADE7953_SPI_WRITE(CFMODE, 0x00A0, 2); Serial.print("CFMODE = "); Serial.println(ADE7953_SPI_READ(CFMODE, 2), HEX); //ADE7953_SPI_WRITE(LCYCMODE, 0x001, 1); // enable linecycle accumulation on phase A active datapath ADE7953_SPI_WRITE(LCYCMODE, 0x03F, 1); // enable linecycle accumulation on phase A active datapath Serial.print("LCYCMODE = "); Serial.println(ADE7953_SPI_READ(LCYCMODE, 1), HEX); } void ADE7953_SPI_WRITE(uint16_t Address , uint32_t Data , uint8_t Number_of_bytes) { uint8_t mhb = 0, hb = 0, mb = 0, lb = 0, p = 0; uint32_t j; j = Data & 0xff; lb = j; j = (Data & 0xff00) >> 8; mb = j; j = (Data & 0xff0000) >> 16; hb = j; j = (Data & 0xff000000) >> 24; mhb = j; digitalWrite(SSB_A, LOW); //set csb low data = SPI.transfer((Address & 0xFF00) >> 8); // send upper address data = SPI.transfer(Address & 0xFF); // send lower address data = SPI.transfer(0x00); // 0x80=read 0x00=write //send requested number of bytes switch (Number_of_bytes) { case 4: data = SPI.transfer(mhb); case 3: data = SPI.transfer(hb); case 2: data = SPI.transfer(mb); case 1: data = SPI.transfer(lb); default: break; } digitalWrite(SSB_A, HIGH); //set csb high } uint32_t ADE7953_SPI_READ (uint16_t Address, uint8_t Number_of_bytes) { uint8_t mhb = 0, hb = 0, mb = 0, lb = 0; uint32_t j; digitalWrite(SSB_A, LOW); //set csb low data = SPI.transfer((Address & 0xFF00) >> 8); // send upper address data = SPI.transfer(Address & 0xFF); // send lower address data = SPI.transfer(0x80); // 0x80 read 0x00 write switch (Number_of_bytes) { case 4: mhb = SPI.transfer(0x0); // dummy write case 3: hb = SPI.transfer(0x0); case 2: mb = SPI.transfer(0x0); case 1: lb = SPI.transfer(0x0); default: break; } digitalWrite(SSB_A, HIGH); //set csb high j = mhb << 24; j = j + (hb << 16); j = j + (mb << 8); j = j + lb; return j; }