I'm currently developing a board for my dissertation that should monitor voltage, current and power consumption of a given load. For that purpose I chose to use the Allegro ACS71020. The one I chose was the SPI model with an IPR of 30 amps. To interface with the ACS71020 I'm using the PSoC 5LP but this shouldn't differ much from MCU to MCU. However I am getting incorrect voltage and current readings from the ACS71020.
This is the schematic for my hardware setup. I decided to use pull-up resistors because the development board schematic also uses them when you try to buy the SPI version.
Figure 1: Schematic
I'm working with 230 V @ 50 Hz power source (European), therefore I'm using a 2 kΩ resistor for Rsense giving me a full-scale voltage of 550 V (this value will be needed later on).
After this I connected it to the PSoC using these pins (taking care to use a 3.3 V reference for the PSoC output so that I wouldn't damage the ACS71020).
Figure 2: PSoC Pinout
My problem here is that when measuring voltage, without it connected, it measures around 30 V. And when doing a practical test using a light bulb as the load, the voltage measurements fluctuate from about 140 V to 230 V which is not the correct value. Also the current value is always really low. Why are these incorrect values being reported?
This is data I got when requesting a voltage and current readings every 4 seconds:
Table 1: Voltage and current readings with light bulb ON
Table 2: Voltage and current readings with light bulb OFF
As you can see, if the power is off the readings although incorrect are consistent. When the power is on, the readings are mostly incorrect and inconsistent for both voltage and current.
Here is the code I used to get the readings:
void SPIrequestData32 (_Bool rw, uint8 addr, uint8 p, uint8 s, uint8 t, uint8 q){
/*******************************************************************
* Requests data from and address with read or write permissions
* and stores 32bit answer in SPI Rx buffer
* Inputs int RW, uint8 addr Clears TxRx&FIFO
* RW == 1 READ RW==0 Write
* 32bit answer is stored in SPI_RxDataBuffer
*******************************************************************/
uint8 Address = 0x00;
//clear buffers
SPIM_1_ClearTxBuffer();
SPIM_1_ClearRxBuffer();
SPIM_1_ClearFIFO();
//Sends address with rw bit
//uint8 Address = ((0x1E) | read);
if(rw==1){
Address = ((addr) | read);
}else if (rw == 0){
Address = ((addr) | write);
}
//waits for a 32bit response = 4frames
if(rw==1){
SPIM_1_WriteTxData(Address);
SPIM_1_WriteTxData(0x00);
SPIM_1_WriteTxData(0x00);
SPIM_1_WriteTxData(0x00);
SPIM_1_WriteTxData(0x00);}
if(rw==0){
SPIM_1_WriteTxData(Address);
SPIM_1_WriteTxData(p);
SPIM_1_WriteTxData(s);
SPIM_1_WriteTxData(t);
SPIM_1_WriteTxData(q);}
while(!(SPIM_1_ReadTxStatus() & SPIM_1_STS_SPI_DONE));
}
In this function, I receive the address I wish to read from or write to. If it's a read, I just drive the line low so that the ACS71020 has the CS and CLK active. If it's a write, I proceed to write the byte I wish.
After receiving the data from the MISO line, I call the function to convert the first 15 bytes to a voltage value and convert the next 15 bytes to a current value, with the help of the lackluster datasheet.
void V_I_RMS (){
/*******************************************************************
* Transforms 32bit response in VRMS(V) and IRMS(A) values
* output is global variable VRMs IRMs
*******************************************************************/
uint8 zero = 0;
uint8 primeiro = 0;
uint8 segundo = 0;
uint8 terceiro = 0;
uint8 quarto = 0;
uint16 V = 0;
uint16 I = 0;
uint16 aux = 0;
uint16 aux2 = 0;
for(uint8 i = 0u; i<5u; i++){
//ignorar o primeiro
switch(i){
case 0: zero = SPIM_1_ReadRxData(); break; //The first one is to be ignored
case 1: primeiro = SPIM_1_ReadRxData(); break;
case 2: segundo = SPIM_1_ReadRxData(); break;
case 3: terceiro = SPIM_1_ReadRxData(); break;
case 4: quarto = SPIM_1_ReadRxData(); break;
default:break;
}
}
V = ((segundo<<8) | primeiro); //Voltage is 16bit number with 15 fractional bits
VRMs = (fullscaleV *( (float)V / (float)0x8000) );
I = ((quarto<<8) | terceiro); //current is a 15bit number with 14fractinal bits
aux = I & 0b0100000000000000; //Mask to check if its 1.xxx or 0.xxx
if(aux == 0x4000){ aux = 1;}
else if (aux == 0){aux = 0;}
aux2 = (I & 0b0011111111111111); //checks for fractional bits
IRMs = IPR*(aux + (aux2*Istep)); //currents is given but IPR*(unit+fraction*(2^14))
}
I don't understand if I'm doing anything wrong while converting or requesting data, to give such bad results. If anyone asks, my SPI master block is setup in this way. Referring to the datasheet, it's the correct way. I've tried increasing or decreasing its bit rate, but the results I get are the same.
I've also tried to change some shadow registers and the customer access code, but when I read the register afterwards, it comes all messed up - sometimes showing nothing but zeros, sometimes showing the what I've written into them.
Figure 3: Screenshot of SPI master block setup
This is a printscreen of my digital oscilloscope, showing that by requesting to read register 0x1E with the 7th bit high (which is the read bit) it results in 0x9E (reading mode). By driving the MOSI line high or low, it maintains CS and CLK working as intended.
Figure 4: Screenshot of digital oscilloscope