Other Parts Discussed in Thread: MSP430I2041
Tool/software: Code Composer Studio
Hello everyone, I'm trying to translate the Arduino Emon code library into the MSP430. Basically the code calculates the RMS voltage, RMS current, active power, reactive power and the power factor.
To simulate a signal from the ZMPT101b voltage sensor, I am using a signal generator with a 60 Hz sinusoid, as shown in the figure below:
This is my code:
#include <msp430g2553.h>
#include <stdlib.h> //Use for rand() function
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#define ADC_BITS 10
#define ADC_COUNTS (1<<ADC_BITS)
const int TIPO_TC = 0; //0-> 0-5 A; 1-> 0-10 A;2-> 0-20A
void ADC_Config_Sequence_Channel_Mode(void);
void Clock_Config(void);
void GPIO_Config(void);
void UART_Config(void);
float offsetV = ADC_COUNTS >> 1;
float filteredV = 0;
float sqV = 0;
float sumV = 0;
float Vrms = 0;
float offsetI = ADC_COUNTS >> 1;
float filteredI = 0;
float sqI = 0;
float sumI = 0;
float Irms = 0;
float phaseShiftedV = 0;
float phaseShiftedI = 0;
float instP = 0;
float sumP = 0;
float realPower = 0;
float apparentPower = 0;
float powerFactor = 0;
//----------------------CALIBRATION-------------------
float VCAL = 681.5;//681.5; //631
float ICAL = 7.6;//7.6; //2.925
float PHASECAL = 0.783;//0.783; //1.9
//----------------------------------------------------
int lastVCross;
int checkVCross;
int SupplyVoltage = 3300;
float ruido = 0;//0.15;// 0.05;
unsigned int contador_1 = 0;
unsigned int contador_2 = 0;
unsigned int crossCount = 0; //Used to measure number of times threshold is crossed.
unsigned int numberOfSamples = 0; //This is now incremented
unsigned int startV;
int crossings = 500;
int timeout = 1000;
float lastFilteredI;
float lastFilteredV;
unsigned int sampleV;
unsigned int sampleI;
float V_RATIO;
float I_RATIO;
V_RATIO = (float) VCAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
I_RATIO = (float) ICAL * ((SupplyVoltage / 1000.0) / (ADC_COUNTS));
unsigned int read_samples[2];
int STATE = 0;
int TXByte = 0;
int main(void)
{
Clock_Config();
GPIO_Config();
UART_Config();
ADC_Config_Sequence_Channel_Mode();
__bis_SR_register(GIE);
while(1)
{
//-------------------------------------------------------------------------------------------------------------------------
// STATE = 0 Read de start voltage by interrupt.
//-------------------------------------------------------------------------------------------------------------------------
if (STATE == 0)
{
ADC10DTC1 = 0x01;
ADC10CTL0 |= ENC + ADC10SC;
STATE = 1;
}
//-------------------------------------------------------------------------------------------------------------------------
// STATE = 2 Waits for the waveform to be close to 'zero' (mid-scale adc) part in sin curve.
//-------------------------------------------------------------------------------------------------------------------------
if (STATE == 2)
{
if ((startV > (ADC_COUNTS * 0.45)) && (startV < (ADC_COUNTS * 0.55)))
{
contador_2 = 0;
STATE = 3;
}
else if (contador_1 > timeout)
{
contador_2 = 0;
STATE = 3;
}
else
STATE = 0;
}
//-------------------------------------------------------------------------------------------------------------------------
// STATE = 3 Main measurement loop
//-------------------------------------------------------------------------------------------------------------------------
if (STATE == 3)
{
if ((crossCount < crossings) && (contador_2 < timeout))
{
numberOfSamples++; // Count number of times looped.
lastFilteredV = filteredV; // Used for delay/phase compensation
//-----------------------------------------------------------------------------
//STATE = 4 Read in raw voltage and current samples by interrupt
//-----------------------------------------------------------------------------
ADC10DTC1 = 0x02;
ADC10CTL0 |= ENC + ADC10SC;
STATE = 4;
}
else
{
//-------------------------------------------------------------------------------------------------------------------------
// Post loop calculations
//-------------------------------------------------------------------------------------------------------------------------
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coefficients applied.
Vrms = (float) V_RATIO *sqrtf( sumV / numberOfSamples);
Irms = (float) I_RATIO *sqrtf( sumI / numberOfSamples);
realPower = (float) V_RATIO * I_RATIO * (sumP / numberOfSamples);
apparentPower = (float) Vrms * Irms;
powerFactor = (float) realPower / apparentPower;
sumV = 0;
sumI = 0;
sumP = 0;
contador_1 = 0;
contador_2 = 0;
STATE = 0; //Return to STATE = 0. Starts new calculations
crossCount = 0;
numberOfSamples = 0;
}
}
if (STATE == 5)
{
//-----------------------------------------------------------------------------
// STATE = 5 Apply digital low pass filters to extract the 2.5 V or 1.65 V dc offset,
// then subtract this - signal is now centred on 0 counts.
//-----------------------------------------------------------------------------
offsetV = offsetV + ((sampleV - offsetV) / 1024);
filteredV = sampleV - offsetV;
offsetI = offsetI + ((sampleI - offsetI) / 1024);
filteredI = sampleI - offsetI;
//-----------------------------------------------------------------------------
// Root-mean-square method voltage and current
//-----------------------------------------------------------------------------
sqV = (float) filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum
sqI = (float) filteredI * filteredI; //1) square current values
sumI += sqI; //2) sum
//-----------------------------------------------------------------------------
// Phase calibration
//-----------------------------------------------------------------------------
phaseShiftedV = (float) lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//-----------------------------------------------------------------------------
// Instantaneous power calc
//-----------------------------------------------------------------------------
instP = (float) phaseShiftedV * filteredI; //Instantaneous Power
sumP += instP; //Sum
//-----------------------------------------------------------------------------
// Find the number of times the voltage has crossed the initial voltage
// - every 2 crosses we will have sampled 1 wavelength
// - so this method allows us to sample an integer number of half wavelengths which increases accuracy
//-----------------------------------------------------------------------------
lastVCross = checkVCross;
if (sampleV > startV)
checkVCross = 1;
else
checkVCross = 0;
if (numberOfSamples == 1)
lastVCross = checkVCross;
if (lastVCross != checkVCross)
crossCount++;
contador_2++;
STATE = 3; //Return to STATE = 3.
}
}
}
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
if (STATE == 1)
{
startV = read_samples[0]; //read raw start voltage
STATE = 2;
contador_1++;
}
else if (STATE == 4)
{
sampleV = read_samples[0]; //read raw votage sample
sampleI = read_samples[1]; //read raw current sample
STATE = 5;
IE2 |= UCA0TXIE;
}
ADC10SA = &read_samples[0];
}
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
if (IFG2 & UCA0TXIFG)
{
if (TXByte == 0)
UCA0TXBUF = (int)Vrms;
if (TXByte == 1)
UCA0TXBUF = (int)Irms;
TXByte++;
if (TXByte == 2)
{
TXByte = 0;
IE2 &= ~UCA0TXIE;
}
}
}
void Clock_Config(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
if (CALBC1_16MHZ==0xFF) // If calibration constant erased
while(1); // do not load, trap CPU!!
DCOCTL = 0; // Select lowest DCOx and MODx settings
BCSCTL1 = CALBC1_16MHZ; // Set DCO
DCOCTL = CALDCO_16MHZ;
}
void ADC_Config_Sequence_Channel_Mode(void)
{
ADC10CTL1 = INCH_5 + CONSEQ_1 + ADC10SSEL_3;
ADC10CTL0 = ADC10SHT_0 + MSC + REFON + ADC10ON + ADC10IE;
ADC10DTC1 = 0x02;
ADC10AE0 |= BIT4 + BIT5;
ADC10SA = &read_samples[0];
}
void GPIO_Config(void)
{
P1DIR |= BIT1 + BIT2;
P1DIR &= ~(BIT4+BIT5);
P1OUT |= BIT1 + BIT2;
}
void UART_Config(void)
{
P1SEL |= BIT1 + BIT2; // P1.1 = RXD, P1.2=TXD
P1SEL2 |= BIT1 + BIT2; // P1.1 = RXD, P1.2=TXD
UCA0CTL1 |= UCSWRST;
UCA0CTL0 |= UCMODE_0;
UCA0CTL1 = UCSSEL_2 + UCSWRST; // UCSSEL_2 = SMCLK; UCSWRST = Reset
UCA0BR0 = 104; // 16MHz 9600
UCA0BR1 = 0; // 16MHz 9600
UCA0MCTL |= UCBRF2 + UCBRF1 + UCOS16; // Modulation UCBRSx = 0, UCBRFx = 3, UCOS16 = 1
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE;
}
But when I start the code, the data sent by the UART is random. And when I simply change the ADC10SHT from ADC10SHT_3 to ADC10SHT_0, the MSP does not send anything else to the UART.
Could someone help me understand where the error is? The code for arduino is in this link: https://community.openenergymonitor.org/t/emon-arduino-program-code/7049