/*
 * drv_adcui.c
 *
 *  Created on: 24 февр. 2025 г.
 *      Author: basov.g
 */

#include "drv_adcui.h"
#include "MDR32VF0xI.h"
#include "MDR32VF0xI_adcui.h"
#include "MDR32VF0xI_rst_clk.h"
#include "MDR32VF0xI_dma.h"
#include "MDR32VF0xI_port.h"
#include <stdbool.h>
#include "../utils/utils.h"
#include "drv_led.h"
#include "drv_display.h"
#include "drv_flash.h"
#define METROLOGY_SIGN_TEXT_SECTION __attribute__((section (".metrology_text")))
#define POS_DIF(a, b)               ((a > b) ? (a-b) : (b-a))
#define __ramfunc
enum {
    METROLOGY_FLAG_NO_UPD = 0,
    METROLOGY_FLAG_POSITIVE_UPD = 1,
    METROLOGY_FLAG_NEGATIVE_UPD = 2,
    METROLOGY_FLAG_UPD_MASK     = (METROLOGY_FLAG_POSITIVE_UPD | METROLOGY_FLAG_NEGATIVE_UPD),
    METROLOGY_FLAG_SELF_DRIVEN  = 4
};
typedef enum {
    METROLOGY_FLAG_VARP_UPD		= 0x001,
    METROLOGY_FLAG_VARN_UPD		= 0x002,
    METROLOGY_FLAG_VAR_UPD		= 0x003,
    METROLOGY_FLAG_WATTP_UPD	= 0x004,
    METROLOGY_FLAG_WATTN_UPD	= 0x008,
    METROLOGY_FLAG_WATT_UPD		= 0x00C,
    METROLOGY_FLAG_VR_UPD		= 0x010,
    METROLOGY_FLAG_FREQ_DONE	= 0x020,
} metrologyIrqFlags;
typedef enum {
    chState_INIT = 0x00,
    chState_NORM = 0x01,
    chState_UNDR = 0x02,
    chState_OVER = 0x04,
    chState_LOSS = 0x08,
} channelStates;
typedef enum {
    ME_OVER_START   = 0x01,
    ME_OVER_FIN     = 0x02,
    ME_UNDER_START  = 0x04,
    ME_UNDER_FIN    = 0x08,
    ME_LOSS_START   = 0x10,
    ME_LOSS_FIN     = 0x20,
    ME_MASK         = 0x3F,
} metrologyEvents;
typedef enum {
    UNIT_UNDEFINED = -1,
    UNIT_1 = 0,
    UNIT_1f,

    UNIT_VOLT,
    UNIT_AMPERE,
	UNIT_WATT,
    UNIT_WATT_HOUR,
    UNIT_CENT_WATT_HOUR,
    UNIT_HERTZ,
    UNIT_TESLA,
    UNIT_DEGREE_CELSIUM,

    UNIT_DEGREE,
    UNIT_RADIAN,

    UNIT_INTERNAL_VOLTAGE,
    UNIT_INTERNAL_CURRENT,
    UNIT_INTERNAL_POWER,
    UNIT_INTERNAL_ENERGY,
    UNIT_INTERNAL_FREQUENCY,

	UNIT_PERCENT,
	UNIT_ENUM_COUNT  // for size determination
} BaseUnit;

#define METRLG_FLAGS_ACT_OFFS 2
#define METRLG_FLAGS_REACT_OFFS 0
#define ADCUI_IRQ_PRIORITY 0
#define EVENTS_BUF_SZ 11
#define PHASE_CHNL_IDX 1  // index of phase channel at 1-phase counter
#define ENERGY_DIVIDER_RSH 10
#define SELF_DRIVEN_METROLOGY_COUNT 50  // number of cycles to sharp detect self-driven current
#define METROLOGY_ADCUI                 MDR_ADCUI


#define ADCUI_L_REG_0_BASE              ((uint32_t)(&(METROLOGY_ADCUI->F0CTR)))
#define ADCUI_L_REG_OFFSET(channel)     ((uint32_t)(sizeof(ADCUI_LRegs)*channel))
#define GET_ADCUI_L_REG(channel)        ((ADCUI_LRegs*)(ADCUI_L_REG_0_BASE + ADCUI_L_REG_OFFSET(channel)))

#define ADCUI_M_REG_0_BASE              ((uint32_t)(&(METROLOGY_ADCUI->F0WATTP_L)))
#define ADCUI_M_REG_OFFSET(channel)     ((uint32_t)(sizeof(ADCUI_MRegs)*channel))
#define GET_ADCUI_M_REG(channel)        ((ADCUI_MRegs*)(ADCUI_M_REG_0_BASE + ADCUI_M_REG_OFFSET(channel)))

#define ADCUI_H_REG_0_BASE              ((uint32_t)(&(METROLOGY_ADCUI->F0VRMS_TRUE)))
#define GET_ADCUI_H_REG(channel)        ((uint32_t*)( ADCUI_H_REG_0_BASE + (channel << 2) ))

typedef struct
{   // primary energy accumulator for active energy
    uint64_t positive;
    uint64_t negative;
    uint64_t sdThrdAR;
    uint32_t count;
    uint32_t timeAcc;
} adcuiChActReactEnergyLiveData;
typedef struct
{   // energy accumulator for calculation of apparent power
    uint64_t value;
    uint64_t sdThrdS;
    uint32_t count;
    uint32_t timeAcc;
} adcuiChAppEnergyLiveData;
typedef struct {
    int32_t ADC_VAOFFS[3];
    int32_t ADC_VTROFFS[3];
} SoftCalibValues;
typedef struct {
    __IO uint32_t FxCTR;                /*!< control */
    __IO uint32_t FxWC;                 /*!< control for active energy */
    __IO uint32_t FxWATTP;              /*!< positive active energy */
    __IO uint32_t FxWATTN;              /*!< negative active energy */
    __IO uint32_t FxVC;                 /*!< control for reactive energy */
    __IO uint32_t FxVARP;               /*!< positive reactive energy */
    __IO uint32_t FxVARN;               /*!< negative reactive energy */
    __IO uint32_t FxAC;                 /*!< control for apparent energy */
    __IO uint32_t FxVR;                 /*!< apparent energy */
    __IO uint32_t FxMD0;                /*!< parameters 0 */
    __IO uint32_t FxMD1;                /*!< parameters 1 */
    __IO uint32_t FxVPEAK_MD2;          /*!< v peak (ch0) or parameters 2 (ch1..2) */
    __IO uint32_t FxIPEAK_VPEAK;        /*!< i peak (ch0) or v peak (ch1..2) */
    __IO uint32_t FxVDAT_IPEAK;         /*!< v data (ch0) or i peak (ch1..2) */
    __IO uint32_t FxI0DAT_VDAT;         /*!< i0 data (ch0) or v data (ch1..2) */
    __IO uint32_t FxI3DAT_IDAT;         /*!< i3 data (ch0) or i data (ch1..2) */
    __IO uint32_t FxVRMS;               /*!< voltage RMS */
    __IO uint32_t FxVRMS2;              /*!< square of the voltage RMS */
    __IO uint32_t FxIRMS;               /*!< current RMS */
    __IO uint32_t FxIRMS2;              /*!< square of the current RMS */
    __IO uint32_t FxSTAT;               /*!< state of the channel */
    __IO uint32_t FxMASK;               /*!< interrupt mask */
} ADCUI_LRegs;
typedef struct {
    __IO uint32_t FxWATTP_L;                /*!< lower bits of the positive active energy */
    __IO uint32_t FxWATTN_L;                /*!< lower bits of the negative active energy */
    __IO uint32_t FxVARP_L;             /*!< lower bits of the positive reactive energy */
    __IO uint32_t FxVARN_L;             /*!< lower bits of the negative reactive energy */
    __IO uint32_t FxVR_L;               /*!< lower bits of the apparent energy */
} ADCUI_MRegs;
typedef struct  {
    /* energy accumulators, without self-driven */
    adcuiChActReactEnergyLiveData active;
    adcuiChActReactEnergyLiveData reactive;
    adcuiChAppEnergyLiveData apparent;

    // counter for acquired periods, reset at flush_data() func
    uint32_t perCnt;
    // time accumulator in CPU cycles. To get Frequency (in CPU cycles): timeAccF / perCnt
    uint32_t timeAccF;
    // counter for calculate Urms200
    uint8_t acc10Cnt;
    // counter for calculate Uust, Udev+ and Udev- at 3s interval
    uint8_t acc3sCnt;
    // counter for calculate Uust at 1m interval
    uint8_t acc1mCnt;
    // counter for calculate Udev+ and Udev- at 10m interval
    uint8_t acc10mCnt;
    // zero-crossing non-resetting counter
    uint32_t zCnt;

    // accumulator to calculate mean values of the Vrms and Irms, every IRQ: VrmsAcc += Vrms; IrmsCnt++; same for currents
    uint32_t irmsAcc;
    uint32_t vrmsAcc;
    uint64_t activeP;
    uint64_t activeN;
    uint64_t reactiveP;
    uint64_t reactiveN;

    uint32_t vrmsTrue;
    uint32_t irmsTrue;
    uint32_t vrmsTrueMax; //
    uint32_t vrmsTrueMin;
    uint32_t vrmst10acc;  // accumulator for calculate Urms200
    uint32_t UdevP3sAcc;  // accumulator for calculate Udev+ at 3s interval
    uint32_t UdevN3sAcc;  // accumulator for calculate Udev- at 3s interval
    uint32_t Uust3sAcc;  // accumulator for calculate Uust at 3s interval
    uint32_t UdevP10mAcc;  // accumulator for calculate Udev+ at 10m interval
    uint32_t UdevN10mAcc;  // accumulator for calculate Udev- at 10m interval
    int32_t Uust1mAcc;  // accumulator for calculate Uust at 1m interval
    uint32_t updateStatus;     // flags, see @metrologyIrqFlags

    uint16_t TimeMarkHigh;
    uint16_t TimeMarkLow;
    uint32_t vTimeMark[EVENTS_BUF_SZ];
    uint32_t vCurrVal[EVENTS_BUF_SZ];
    uint32_t vMinMax[EVENTS_BUF_SZ];
    uint8_t vEvents[EVENTS_BUF_SZ];
    uint8_t vEventCnt;
    uint32_t lastCpuCycleF;
    uint8_t chState;  // current channel state

    // next impulse thresholds
    int64_t activeNextThreshold;
    int64_t reactiveNextThreshold;
} adcuiChLiveData;
typedef struct {
    /* impulse steps */
    uint64_t activeImpStep[3];
    uint64_t reactiveImpStep[3];
    /* thresholds for the voltages */
    uint32_t vOverVoltageThrIn;     // enter to overvoltage
    uint32_t vOverVoltageThrOut;    // out from overvoltage
    uint32_t vUnderVoltageThrIn;    // enter to undervoltage
    uint32_t vUnderVoltageThrOut;   // out from undervoltage
    uint32_t vLossVoltageThrIn;     // enter to voltage loss
    uint32_t vLossVoltageThrOut;    // out from voltage loss
    uint32_t Udin;
} adcuiCmnSettings;


static uint32_t lastCpuCycleP[3];
static uint32_t lastCpuCycleQ[3];
static uint32_t lastCpuCycleS[3];

adcuiChLiveData g_chLiveData[3] = {0};
adcuiCmnSettings g_cmnSettings = {0};

#define ADCUI_SAMPLE_BUF_LEN 128
static volatile uint32_t ADCUI_SAMPLES[2][7][ADCUI_SAMPLE_BUF_LEN] = {0};//__ahbram
DMA_CtrlData_TypeDef DMA_CHANNEL_CONFIGS[32 * 2];
const DMA_ValidChannel_TypeDef DMA_ADCUI_LINES[3][3] = {
        {DMA_CHANNEL_ADCUI_I0, DMA_CHANNEL_ADCUI_V0, DMA_CHANNEL_ADCUI_I3},//12 13 18
        {DMA_CHANNEL_ADCUI_I1, DMA_CHANNEL_ADCUI_V1},					//14 15
        {DMA_CHANNEL_ADCUI_I2, DMA_CHANNEL_ADCUI_V2},					//16 17
};

uint32_t DMA_ADCUI_SRC[3][3] = {};

const uint32_t DMA_ADCUI_DMA_REQ_MASK[3] = {
        (1<<DMA_CHANNEL_ADCUI_I0) | (1<<DMA_CHANNEL_ADCUI_V0) | (1<<DMA_CHANNEL_ADCUI_I3),
		(1<<DMA_CHANNEL_ADCUI_I1) | (1<<DMA_CHANNEL_ADCUI_V1),
		(1<<DMA_CHANNEL_ADCUI_I2) | (1<<DMA_CHANNEL_ADCUI_V2)
};

const uint32_t DMA_ADCUI_CTRL1_CH_EN_MASK[3] = {
        ADCUI_CTRL1_V0EN | ADCUI_CTRL1_I0EN | ADCUI_CTRL1_I3EN,
        ADCUI_CTRL1_V1EN | ADCUI_CTRL1_I1EN,
        ADCUI_CTRL1_V2EN | ADCUI_CTRL1_I2EN,
};

uint32_t timeDiff(uint32_t T1, uint32_t T0)
{
    if (T1>=T0)
        return T1-T0;
    else
        return (UINT32_MAX-T0)+T1+1;
}




void drv_adcui_CallibrateClear(void)
{
	METROLOGY_ADCUI->F0AC = 0;
	METROLOGY_ADCUI->F1AC = 0;
	METROLOGY_ADCUI->F2AC = 0;
	METROLOGY_ADCUI->CCAL1 = 0;
	METROLOGY_ADCUI->CCAL2 = 0;
	METROLOGY_ADCUI->CCAL3 = 0;
	METROLOGY_ADCUI->CCAL4 = 0;
	METROLOGY_ADCUI->F0CTR &= ~0xFFF3FC00;
	METROLOGY_ADCUI->F1CTR &= ~0xFFF3FC00;
	METROLOGY_ADCUI->F2CTR &= ~0xFFF3FC00;
	METROLOGY_ADCUI->F0WC = 0;
	METROLOGY_ADCUI->F1WC = 0;
	METROLOGY_ADCUI->F2WC = 0;
	METROLOGY_ADCUI->F0VC = 0;
	METROLOGY_ADCUI->F1VC = 0;
	METROLOGY_ADCUI->F2VC = 0;
	drv_flash_save();
}

int64_t VRMSValueLow[3] = {0};
void drv_adcui_CallibrateVoltageLowPoint(void)
{
	METROLOGY_ADCUI->F0AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->F1AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->F2AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->CCAL1 &= (uint32_t)(~0x0000FFFF);
	METROLOGY_ADCUI->CCAL2 &= (uint32_t)(~0x0000FFFF);
	METROLOGY_ADCUI->CCAL3 &= (uint32_t)(~0x0000FFFF);

	for(int i = 0; i < 100; i++)
	{
		//VRMSValue[0] =
		if((MDR_ADCUI->F0VRMS)&0x800000) VRMSValueLow[0] += (0xFFFFFF - MDR_ADCUI->F0VRMS);
		else VRMSValueLow[0] +=  (MDR_ADCUI->F0VRMS);
		if((MDR_ADCUI->F1VRMS)&0x800000) VRMSValueLow[1] += (0xFFFFFF - MDR_ADCUI->F1VRMS);
		else VRMSValueLow[1] += (MDR_ADCUI->F1VRMS);
		if((MDR_ADCUI->F2VRMS)&0x800000) VRMSValueLow[2] += (0xFFFFFF - MDR_ADCUI->F2VRMS);
		else VRMSValueLow[2] += (MDR_ADCUI->F2VRMS);
		delayMs(20);
	}
	VRMSValueLow[0]/=100;
	VRMSValueLow[1]/=100;
	VRMSValueLow[2]/=100;
}

void drv_adcui_CallibrateVoltageHighPoint(void)
{
	int64_t VRMSValueHigh[3] = {0};
	METROLOGY_ADCUI->F0AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->F1AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->F2AC &= (uint32_t)~0x00000FFF;
	METROLOGY_ADCUI->CCAL1 &= (uint32_t)(~0x0000FFFF);
	METROLOGY_ADCUI->CCAL2 &= (uint32_t)(~0x0000FFFF);
	METROLOGY_ADCUI->CCAL3 &= (uint32_t)(~0x0000FFFF);

	for(int i = 0; i < 100; i++)
	{
		if((MDR_ADCUI->F0VRMS)&0x800000) VRMSValueHigh[0] += (0xFFFFFF - MDR_ADCUI->F0VRMS);
		else VRMSValueHigh[0] +=  (MDR_ADCUI->F0VRMS);
		if((MDR_ADCUI->F1VRMS)&0x800000) VRMSValueHigh[1] += (0xFFFFFF - MDR_ADCUI->F1VRMS);
		else VRMSValueHigh[1] += (MDR_ADCUI->F1VRMS);
		if((MDR_ADCUI->F2VRMS)&0x800000) VRMSValueHigh[2] += (0xFFFFFF - MDR_ADCUI->F2VRMS);
		else VRMSValueHigh[2] += (MDR_ADCUI->F2VRMS);
		delayMs(20);
	}
	VRMSValueHigh[0]/=100;
	VRMSValueHigh[1]/=100;
	VRMSValueHigh[2]/=100;


	int16_t coef[3] = {0};
	int16_t offset[3] = {0};
	#define VoltagePointLow  (190000*9/4)
	#define VoltagePointHigh (240000*9/4)
	int64_t VoltageDiffReal = VoltagePointHigh - VoltagePointLow;
	int64_t VoltageDiffMeas[3] = {	VRMSValueHigh[0] - VRMSValueLow[0],
									VRMSValueHigh[1] - VRMSValueLow[1],
									VRMSValueHigh[2] - VRMSValueLow[2]};
	coef[0] = (int16_t)(VoltageDiffReal*0x8000/VoltageDiffMeas[0]-0x8000);
	coef[1] = (int16_t)(VoltageDiffReal*0x8000/VoltageDiffMeas[1]-0x8000);
	coef[2] = (int16_t)(VoltageDiffReal*0x8000/VoltageDiffMeas[2]-0x8000);

	offset[0] = (int16_t)(VoltagePointHigh * VoltageDiffMeas[0]/VoltageDiffReal - VRMSValueHigh[0]);
	offset[1] = (int16_t)(VoltagePointHigh * VoltageDiffMeas[1]/VoltageDiffReal - VRMSValueHigh[1]);
	offset[2] = (int16_t)(VoltagePointHigh * VoltageDiffMeas[2]/VoltageDiffReal - VRMSValueHigh[2]);

	METROLOGY_ADCUI->CCAL1 |= (uint32_t)coef[0];
	METROLOGY_ADCUI->CCAL2 |= (uint32_t)coef[1];
	METROLOGY_ADCUI->CCAL3 |= (uint32_t)coef[2];
	METROLOGY_ADCUI->F0AC |= (uint32_t)(offset[0]>>4);
	METROLOGY_ADCUI->F1AC |= (uint32_t)(offset[1]>>4);
	METROLOGY_ADCUI->F2AC |= (uint32_t)(offset[2]>>4);
	drv_flash_save();
}

int64_t IRMSValueLow[3] = {0};
void drv_adcui_CallibrateCurrentLowPoint(void)
{
	METROLOGY_ADCUI->F0CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->F1CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->F2CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->CCAL1 &= ~0xFFFF0000;
	METROLOGY_ADCUI->CCAL2 &= ~0xFFFF0000;
	METROLOGY_ADCUI->CCAL3 &= ~0xFFFF0000;

	for(int i = 0; i < 100; i++)
	{
			//VRMSValue[0] =
		if((MDR_ADCUI->F0IRMS)&0x800000) IRMSValueLow[0] += (0xFFFFFF - MDR_ADCUI->F0IRMS);
		else IRMSValueLow[0] +=  (MDR_ADCUI->F0IRMS);
		if((MDR_ADCUI->F1IRMS)&0x800000) IRMSValueLow[1] += (0xFFFFFF - MDR_ADCUI->F1IRMS);
		else IRMSValueLow[1] += (MDR_ADCUI->F1IRMS);
		if((MDR_ADCUI->F2IRMS)&0x800000) IRMSValueLow[2] += (0xFFFFFF - MDR_ADCUI->F2IRMS);
		else IRMSValueLow[2] += (MDR_ADCUI->F2IRMS);
		delayMs(20);
	}
	IRMSValueLow[0]/=100;
	IRMSValueLow[1]/=100;
	IRMSValueLow[2]/=100;
}

void drv_adcui_CallibrateCurrentHighPoint(void)
{
	int64_t IRMSValueHigh[3] = {0};

	METROLOGY_ADCUI->F0CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->F1CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->F2CTR &= ~0xFFF00000;
	METROLOGY_ADCUI->CCAL1 &= ~0xFFFF0000;
	METROLOGY_ADCUI->CCAL2 &= ~0xFFFF0000;
	METROLOGY_ADCUI->CCAL3 &= ~0xFFFF0000;

	for(int i = 0; i < 100; i++)
	{
		if((MDR_ADCUI->F0IRMS)&0x800000) IRMSValueHigh[0] += (0xFFFFFF - MDR_ADCUI->F0IRMS);
		else IRMSValueHigh[0] +=  (MDR_ADCUI->F0IRMS);
		if((MDR_ADCUI->F1IRMS)&0x800000) IRMSValueHigh[1] += (0xFFFFFF - MDR_ADCUI->F1IRMS);
		else IRMSValueHigh[1] += (MDR_ADCUI->F1IRMS);
		if((MDR_ADCUI->F2IRMS)&0x800000) IRMSValueHigh[2] += (0xFFFFFF - MDR_ADCUI->F2IRMS);
		else IRMSValueHigh[2] += (MDR_ADCUI->F2IRMS);
		delayMs(20);
	}
	IRMSValueHigh[0]/=100;
	IRMSValueHigh[1]/=100;
	IRMSValueHigh[2]/=100;


	int16_t coef[3] = {0};
	int16_t offset[3] = {0};
	int64_t CurrentPointLow = 25 * 0x800 / 9;
	int64_t CurrentPointHigh = 2500 * 0x800 / 9;
	int64_t CurrentDiffReal = CurrentPointHigh - CurrentPointLow;
	int64_t CurrentDiffMeas[3] = {	IRMSValueHigh[0] - IRMSValueLow[0],
									IRMSValueHigh[1] - IRMSValueLow[1],
									IRMSValueHigh[2] - IRMSValueLow[2]};
	coef[0] = (int16_t)(CurrentDiffReal*0x8000/CurrentDiffMeas[0]-0x8000);
	coef[1] = (int16_t)(CurrentDiffReal*0x8000/CurrentDiffMeas[1]-0x8000);
	coef[2] = (int16_t)(CurrentDiffReal*0x8000/CurrentDiffMeas[2]-0x8000);

	offset[0] = (int16_t)(CurrentPointHigh * CurrentDiffMeas[0]/CurrentDiffReal - IRMSValueHigh[0]);
	offset[1] = (int16_t)(CurrentPointHigh * CurrentDiffMeas[1]/CurrentDiffReal - IRMSValueHigh[1]);
	offset[2] = (int16_t)(CurrentPointHigh * CurrentDiffMeas[2]/CurrentDiffReal - IRMSValueHigh[2]);

	METROLOGY_ADCUI->F0CTR |= (uint32_t)((offset[0]>>4)<<20) & 0xFFF00000;
	METROLOGY_ADCUI->F1CTR |= (uint32_t)((offset[1]>>4)<<20) & 0xFFF00000;
	METROLOGY_ADCUI->F2CTR |= (uint32_t)((offset[2]>>4)<<20) & 0xFFF00000;
	METROLOGY_ADCUI->CCAL1 |= (uint32_t)coef[0]<<16;
	METROLOGY_ADCUI->CCAL2 |= (uint32_t)coef[1]<<16;
	METROLOGY_ADCUI->CCAL3 |= (uint32_t)coef[2]<<16;
	drv_flash_save();
}

void drv_adcui_CallibratePhase(void)
{
	int64_t ActiveValue[3] = {0};
	int64_t ReactiveValue[3] = {0};
	int16_t corr[3] = {0};
	corr[0] = 0;
	corr[1] = 0;
	corr[2] = 0;
	METROLOGY_ADCUI->F0CTR &= ~((uint32_t)(0xFF<<10));
	METROLOGY_ADCUI->F1CTR &= ~((uint32_t)(0xFF<<10));
	METROLOGY_ADCUI->F2CTR &= ~((uint32_t)(0xFF<<10));
	for(int i = 0; i < 100; i++)
	{
		ActiveValue[0] += (int64_t)g_chLiveData[adcuiChannelA].activeP;
		ActiveValue[0] -= (int64_t)g_chLiveData[adcuiChannelA].activeN;
		ActiveValue[1] += (int64_t)g_chLiveData[adcuiChannelB].activeP;
		ActiveValue[1] -= (int64_t)g_chLiveData[adcuiChannelB].activeN;
		ActiveValue[2] += (int64_t)g_chLiveData[adcuiChannelC].activeP;
		ActiveValue[2] -= (int64_t)g_chLiveData[adcuiChannelC].activeN;
		ReactiveValue[0] += (int64_t)g_chLiveData[adcuiChannelA].reactiveP;
		ReactiveValue[0] -= (int64_t)g_chLiveData[adcuiChannelA].reactiveN;
		ReactiveValue[1] += (int64_t)g_chLiveData[adcuiChannelB].reactiveP;
		ReactiveValue[1] -= (int64_t)g_chLiveData[adcuiChannelB].reactiveN;
		ReactiveValue[2] += (int64_t)g_chLiveData[adcuiChannelC].reactiveP;
		ReactiveValue[2] -= (int64_t)g_chLiveData[adcuiChannelC].reactiveN;

		delayMs(20);
	}
	ActiveValue[0]/=100;
	ActiveValue[1]/=100;
	ActiveValue[2]/=100;
	ReactiveValue[0]/=10;
	ReactiveValue[1]/=10;
	ReactiveValue[2]/=10;


	corr[0] = (int8_t)((ReactiveValue[0]*3260ill/ActiveValue[0]/10ill)) + corr[0];
	corr[1] = (int8_t)((ReactiveValue[1]*3260ill/ActiveValue[1]/10ill)) + corr[1];
	corr[2] = (int8_t)((ReactiveValue[2]*3260ill/ActiveValue[2]/10ill)) + corr[2];
	METROLOGY_ADCUI->F0CTR &= ~((uint32_t)(0xFF<<10));
	METROLOGY_ADCUI->F1CTR &= ~((uint32_t)(0xFF<<10));
	METROLOGY_ADCUI->F2CTR &= ~((uint32_t)(0xFF<<10));
	METROLOGY_ADCUI->F0CTR |= ((uint32_t)(corr[0]&0xFF)<<10);
	METROLOGY_ADCUI->F1CTR |= ((uint32_t)(corr[1]&0xFF)<<10);
	METROLOGY_ADCUI->F2CTR |= ((uint32_t)(corr[2]&0xFF)<<10);
	drv_flash_save();
}

int64_t ActivePowerValueLow[3] = {0};
void drv_adcui_CallibratePowerActiveLowPoint(void)
{
	METROLOGY_ADCUI->F0WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F1WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F2WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F0WC &= 0xF000FFFF;
	METROLOGY_ADCUI->F1WC &= 0xF000FFFF;
	METROLOGY_ADCUI->F2WC &= 0xF000FFFF;
	for(int i = 0; i < 100; i++)
	{
		ActivePowerValueLow[0] += drv_adcui_getActivePower(adcuiChannelA);
		ActivePowerValueLow[1] += drv_adcui_getActivePower(adcuiChannelB);
		ActivePowerValueLow[2] += drv_adcui_getActivePower(adcuiChannelC);
		delayMs(25);
	}
	ActivePowerValueLow[0]/=100;
	ActivePowerValueLow[1]/=100;
	ActivePowerValueLow[2]/=100;

}

void drv_adcui_CallibratePowerActiveHighPoint(void)
{
	int64_t ActivePowerValueHigh[3] = {0};
	METROLOGY_ADCUI->F0WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F1WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F2WC &= 0xFFFF0000;
	METROLOGY_ADCUI->F0WC &= 0xF000FFFF;
	METROLOGY_ADCUI->F1WC &= 0xF000FFFF;
	METROLOGY_ADCUI->F2WC &= 0xF000FFFF;
	for(int i = 0; i < 100; i++)
	{
		ActivePowerValueHigh[0] += drv_adcui_getActivePower(adcuiChannelA);
		ActivePowerValueHigh[1] += drv_adcui_getActivePower(adcuiChannelB);
		ActivePowerValueHigh[2] += drv_adcui_getActivePower(adcuiChannelC);
		delayMs(20);
	}
	ActivePowerValueHigh[0]/=100;
	ActivePowerValueHigh[1]/=100;
	ActivePowerValueHigh[2]/=100;

	int16_t coef[3] = {0};
	int16_t offset[3];
	int64_t ActivePointLow = 4750 * 10000;
	int64_t ActivePointHigh = 600000;
	ActivePointHigh *= 10000;
	int64_t ActiveDiffReal = ActivePointHigh - ActivePointLow;
	int64_t ActiveDiffMeas[3] = {	ActivePowerValueHigh[0] - ActivePowerValueLow[0],
									ActivePowerValueHigh[1] - ActivePowerValueLow[1],
									ActivePowerValueHigh[2] - ActivePowerValueLow[2]};
	coef[0] = (int16_t)(ActiveDiffReal*0x800/ActiveDiffMeas[0]-0x800);
	coef[1] = (int16_t)(ActiveDiffReal*0x800/ActiveDiffMeas[1]-0x800);
	coef[2] = (int16_t)(ActiveDiffReal*0x800/ActiveDiffMeas[2]-0x800);

	offset[0] = (int16_t)(ActivePointHigh * ActiveDiffMeas[0]/ActiveDiffReal - ActivePowerValueHigh[0]);
	offset[1] = (int16_t)(ActivePointHigh * ActiveDiffMeas[1]/ActiveDiffReal - ActivePowerValueHigh[1]);
	offset[2] = (int16_t)(ActivePointHigh * ActiveDiffMeas[2]/ActiveDiffReal - ActivePowerValueHigh[2]);


	METROLOGY_ADCUI->F0WC |= ((int16_t)coef[0]&0x0FFF)<<16;
	METROLOGY_ADCUI->F1WC |= ((int16_t)coef[1]&0x0FFF)<<16;
	METROLOGY_ADCUI->F2WC |= ((int16_t)coef[2]&0x0FFF)<<16;
	drv_flash_save();
}

int64_t ReactivePowerValueLow[3] = {0};
void drv_adcui_CallibratePowerReactiveZero(void)
{
	METROLOGY_ADCUI->F0VC &= 0xFFFF0000;
	METROLOGY_ADCUI->F1VC &= 0xFFFF0000;
	METROLOGY_ADCUI->F2VC &= 0xFFFF0000;
	METROLOGY_ADCUI->F0VC &= 0xF000FFFF;
	METROLOGY_ADCUI->F1VC &= 0xF000FFFF;
	METROLOGY_ADCUI->F2VC &= 0xF000FFFF;
	for(int i = 0; i < 100; i++)
	{
		ReactivePowerValueLow[0] += drv_adcui_getReactivePower(adcuiChannelA);
		ReactivePowerValueLow[1] += drv_adcui_getReactivePower(adcuiChannelB);
		ReactivePowerValueLow[2] += drv_adcui_getReactivePower(adcuiChannelC);
		delayMs(20);
	}
	ReactivePowerValueLow[0]/=100;
	ReactivePowerValueLow[1]/=100;
	ReactivePowerValueLow[2]/=100;
}

void drv_adcui_CallibratePowerReactiveCoef(void)
{
	int64_t ReactivePowerValueHigh[3] = {0};
	METROLOGY_ADCUI->F0VC &= 0xF000FFFF;
	METROLOGY_ADCUI->F1VC &= 0xF000FFFF;
	METROLOGY_ADCUI->F2VC &= 0xF000FFFF;
	for(int i = 0; i < 100; i++)
	{
		ReactivePowerValueHigh[0] += drv_adcui_getReactivePower(adcuiChannelA);
		ReactivePowerValueHigh[1] += drv_adcui_getReactivePower(adcuiChannelB);
		ReactivePowerValueHigh[2] += drv_adcui_getReactivePower(adcuiChannelC);
		delayMs(20);
	}
	ReactivePowerValueHigh[0]/=100;
	ReactivePowerValueHigh[1]/=100;
	ReactivePowerValueHigh[2]/=100;

	int16_t coef[3] = {0};
	int16_t offset[3];
	int64_t ReactivePointLow = 4750 * 10000;
	int64_t ReactivePointHigh = 600000;
	ReactivePointHigh *= 10000;
	int64_t ReactiveDiffReal = ReactivePointHigh - ReactivePointLow;
	int64_t ReactiveDiffMeas[3] = {	 ReactivePowerValueHigh[0] - ReactivePowerValueLow[0],
									 ReactivePowerValueHigh[1] - ReactivePowerValueLow[1],
									 ReactivePowerValueHigh[2] - ReactivePowerValueLow[2]};
	coef[0] = (int16_t)(ReactiveDiffReal*0x800/ReactiveDiffMeas[0]-0x800);
	coef[1] = (int16_t)(ReactiveDiffReal*0x800/ReactiveDiffMeas[1]-0x800);
	coef[2] = (int16_t)(ReactiveDiffReal*0x800/ReactiveDiffMeas[2]-0x800);

	offset[0] = (int16_t)(ReactivePointHigh * ReactiveDiffMeas[0]/ReactiveDiffReal - ReactivePowerValueHigh[0]);
	offset[1] = (int16_t)(ReactivePointHigh * ReactiveDiffMeas[1]/ReactiveDiffReal - ReactivePowerValueHigh[1]);
	offset[2] = (int16_t)(ReactivePointHigh * ReactiveDiffMeas[2]/ReactiveDiffReal - ReactivePowerValueHigh[2]);


	METROLOGY_ADCUI->F0VC |= ((int16_t)coef[0]&0x0FFF)<<16;
	METROLOGY_ADCUI->F1VC |= ((int16_t)coef[1]&0x0FFF)<<16;
	METROLOGY_ADCUI->F2VC |= ((int16_t)coef[2]&0x0FFF)<<16;
	drv_flash_save();
}

void drv_adcui_callibrate(void)
{
	drv_led_setMode(LedError,LedModeOn);
	switch(drv_display_getDisplaySubPage())
	{
		case DisplayPageCalibrateMain				: drv_adcui_CallibrateClear();    		  break;
		case DisplayPageCalibrateVoltageLowPoint	: drv_adcui_CallibrateVoltageLowPoint();  break;
		case DisplayPageCalibrateVoltageHighPoint	: drv_adcui_CallibrateVoltageHighPoint(); break;
		case DisplayPageCalibrateCurrentLowPoint	: drv_adcui_CallibrateCurrentLowPoint();  break;
		case DisplayPageCalibrateCurrentHighPoint	: drv_adcui_CallibrateCurrentHighPoint(); break;
		case DisplayPageCalibratePhase				: drv_adcui_CallibratePhase(); 		 	  break;
		case DisplayPageCalibratePowerActiveZero: drv_adcui_CallibratePowerActiveLowPoint();  break;
		case DisplayPageCalibratePowerActiveCoef: drv_adcui_CallibratePowerActiveHighPoint(); break;
		case DisplayPageCalibratePowerReactiveZero: drv_adcui_CallibratePowerReactiveZero();  break;
		case DisplayPageCalibratePowerReactiveCoef: drv_adcui_CallibratePowerReactiveCoef();  break;
		case DisplayPageCalibrateStartCurrent	: break;
	}
	drv_led_setMode(LedError,LedModeOff);
}


void DMA_EnableADCUIGather(ADCUI_Unit_TypeDef channel,volatile uint32_t *buf1,volatile uint32_t *buf2, uint32_t len) {
    assert_param(ADCUI_IS_CHANNEL(channel));
    assert_param(DMA_IS_TRNCNT_VALID(len));

    int n_lines = channel == ADCUI_F0_UNIT ? 3 : 2;

    assert_param(DMA_IS_AHBMEM((uint32_t)buf1) && DMA_IS_AHBMEM((uint32_t)(buf1 + len * n_lines - 1)));
    assert_param(DMA_IS_AHBMEM((uint32_t)buf2) && DMA_IS_AHBMEM((uint32_t)(buf2 + len * n_lines - 1)));


    uint32_t CFG = DMA_MODE_PING_PONG;
    CFG |= (len - 1) << DMA_CHANNEL_CFG_N_MINUS_1_Pos;
    CFG |= 0x2 << DMA_CHANNEL_CFG_SRC_SIZE_Pos;
    CFG |= 0x2 << DMA_CHANNEL_CFG_DST_SIZE_Pos;
    CFG |= DMA_DATA_INC_NO << DMA_CHANNEL_CFG_SRC_INC_Pos;
    CFG |= DMA_DATA_INC_32BIT << DMA_CHANNEL_CFG_DST_SIZE_Pos;
    for (int i = 0; i < n_lines; i++) {
    	DMA_ValidChannel_TypeDef dmaChannel = DMA_ADCUI_LINES[channel][i];
    	//const DmaLine_t * line = &DMA_LINES[dmaChannel];

    	DMA_CtrlData_TypeDef * channelStructPri = &DMA_CHANNEL_CONFIGS[dmaChannel];
    	DMA_CtrlData_TypeDef * channelStructAlt = &DMA_CHANNEL_CONFIGS[32 + dmaChannel];

        channelStructPri->DMA_SourceEndAddr = (uint32_t) DMA_ADCUI_SRC[channel][i];
        channelStructPri->DMA_DestEndAddr 	= (uint32_t) (buf1 + (i + 1) * len - 1);
        channelStructPri->DMA_Control 		= (DMA_Control_TypeDef)CFG;
        channelStructPri->DMA_Unused    	= CFG;

        channelStructAlt->DMA_SourceEndAddr = (uint32_t) DMA_ADCUI_SRC[channel][i];
        channelStructAlt->DMA_DestEndAddr 	= (uint32_t) (buf2 + (i + 1) * len - 1);
        channelStructAlt->DMA_Control 		= (DMA_Control_TypeDef)CFG;
        channelStructAlt->DMA_Unused 		= CFG;
    }

    MDR_DMA->CHNL_PRI_ALT_CLR = DMA_ADCUI_DMA_REQ_MASK[channel];
    MDR_DMA->CHNL_ENABLE_SET = DMA_ADCUI_DMA_REQ_MASK[channel];
    MDR_ADCUI->CTRL1 |= DMA_ADCUI_CTRL1_CH_EN_MASK[channel];
}


void start_adcui(void)
{
    DMA_EnableADCUIGather(ADCUI_F0_UNIT, ADCUI_SAMPLES[0][0], ADCUI_SAMPLES[1][0], ADCUI_SAMPLE_BUF_LEN);
    DMA_EnableADCUIGather(ADCUI_F1_UNIT, ADCUI_SAMPLES[0][3], ADCUI_SAMPLES[1][3], ADCUI_SAMPLE_BUF_LEN);
    DMA_EnableADCUIGather(ADCUI_F2_UNIT, ADCUI_SAMPLES[0][5], ADCUI_SAMPLES[1][5], ADCUI_SAMPLE_BUF_LEN);

	CLIC_SetPriorityIRQ(ADCUI_F03_IRQn,0);
	CLIC_EnableIRQ(ADCUI_F03_IRQn);
	CLIC_SetPriorityIRQ(ADCUI_F1_IRQn,0);
	CLIC_EnableIRQ(ADCUI_F1_IRQn);
	CLIC_SetPriorityIRQ(ADCUI_F2_IRQn,0);
	CLIC_EnableIRQ(ADCUI_F2_IRQn);

}

void init_gpio(void)
{
	RST_CLK_PCLKCmd(RST_CLK_PCLK_PORTD, ENABLE);
	PORT_InitTypeDef PORT_InitStructure;
	PORT_InitStructure.PORT_Direction    = PORT_DIRECTION_INPUT;
	PORT_InitStructure.PORT_Function  = PORT_FUNCTION_ALTERNATIVE;
	PORT_InitStructure.PORT_Mode  = PORT_MODE_DIGITAL;
	PORT_InitStructure.PORT_Power = PORT_POWER_INCREASED_UPTO_4mA;
	PORT_InitStructure.PORT_PullDown  = PORT_PULL_DOWN_OFF;
	PORT_InitStructure.PORT_PullUp  = PORT_PULL_UP_OFF;
	PORT_InitStructure.PORT_Pin = (PORT_PIN_10 | PORT_PIN_11| PORT_PIN_12);
	PORT_Init(MDR_PORTD, &PORT_InitStructure);

	PORT_InitStructure.PORT_Direction    = PORT_DIRECTION_OUTPUT;
	PORT_InitStructure.PORT_Pin = (PORT_PIN_14);
	PORT_Init(MDR_PORTD, &PORT_InitStructure);//0x1C00  0x5C00
	MDR_PORTD->ANALOG = 0x5C00;
}

void init_adcui(void)
{
	RST_CLK_PCLKCmd(RST_CLK_PCLK_DMA, ENABLE);
	RST_CLK_DMACmd(ENABLE);

	RST_CLK_PCLKCmd(RST_CLK_PCLK_ADCUI, ENABLE);
	RST_CLK_ADCUI_ClkSelection(RST_CLK_ADCUI_CLK_SRC_CPU_C1);
	RST_CLK_ADCUI_SetPrescaler(RST_CLK_ADCUI_PRESCALER_DIV_1);
	RST_CLK_ADCUI_ClkCmd(ENABLE);

	DMA_ADCUI_SRC[0][0] = MDR_ADCUI->F0I0DAT;
	DMA_ADCUI_SRC[0][1] = MDR_ADCUI->F0VDAT;
	DMA_ADCUI_SRC[0][2] = MDR_ADCUI->F0I3DAT;

	DMA_ADCUI_SRC[1][0] = MDR_ADCUI->F1IDAT;
	DMA_ADCUI_SRC[1][1] = MDR_ADCUI->F1VDAT;

	DMA_ADCUI_SRC[2][0] = MDR_ADCUI->F2IDAT;
	DMA_ADCUI_SRC[2][1] = MDR_ADCUI->F2VDAT;

    ADCUI_InitTypeDef initStruct = {};
    ADCUI_FxUnit_InitTypeDef channelInitStruct = {};

    /* General settings */

    initStruct.ADCUI_DataResol = ADCUI_DATA_RESOL_24BIT;
    initStruct.ADCUI_ZeroCrossLowPassFilter = ADCUI_ZXLPF_FILTER_ENABLE;
    initStruct.ADCUI_PeriodLength = ADCUI_PER_LENGTH_128;
    initStruct.ADCUI_ActivePowerNoLoad = ADCUI_APNOLOAD_FULL;
    initStruct.ADCUI_ReactivePowerNoLoad = ADCUI_VARNOLOAD_FULL;
    initStruct.ADCUI_FullPowerNoLoad = ADCUI_VANOLOAD_FULL;
    initStruct.ADCUI_FreqVEnable = ENABLE;
    initStruct.ADCUI_VRefConfig = ADCUI_REFERENCE_VOLTAGE_INTERNAL;
    initStruct.ADCUI_ZeroCrossUpdateRMS = ADCUI_REG_UPDATE_BY_ZERO;
    initStruct.ADCUI_ClockFrequency = ADCUI_CLK_FREQUENCY_4MHz;
    initStruct.ADCUI_ChopperPeriod = ADCUI_CHOPPER_PERIOD_256;
    initStruct.ADCUI_Divider = ADCUI_OVERSAMPLING_DIV_1024;
    initStruct.ADCUI_VoltageDropLevel = 0xFFFF;
    initStruct.ADCUI_VoltageHalfCyclesDrop = 0xFF;
    initStruct.ADCUI_ZeroCrossTimeOut = 0xFF;



    channelInitStruct.ADCUI_Fx_IntegratorEnable = ADCUI_INT_DISABLE;
    channelInitStruct.ADCUI_F0_I3IntegratorEnable = ADCUI_INT_DISABLE;
    channelInitStruct.ADCUI_SourceRegFullPower = ADCUI_FULL_ENERGY;
    channelInitStruct.ADCUI_Fx_IGain = ADCUI_IGAIN_6dB;
    channelInitStruct.ADCUI_Fx_VGain = ADCUI_VGAIN_0dB;
    channelInitStruct.ADCUI_F0_I3Gain = ADCUI_IGAIN_6dB;
    channelInitStruct.ADCUI_Fx_SourceVDAT = ADCUI_Fx_VDAT_U_BEFORE_HPF;
    channelInitStruct.ADCUI_Fx_SourceIDAT = ADCUI_Fx_IDAT_I;
    channelInitStruct.ADCUI_Fx_6dBIGainEnable = DISABLE;
    channelInitStruct.ADCUI_Fx_6dBVGainEnable = DISABLE;
    channelInitStruct.ADCUI_F0_6dBI3GainEnable = DISABLE;
    channelInitStruct.ADCUI_F0_I3SelectDataFilter = ADCUI_F0I3SEL_AFTER_HPF;
    channelInitStruct.ADCUI_Fx_ISelectDataFilter = ADCUI_FxISEL_BEFORE_HPF;
    channelInitStruct.ADCUI_F0_IChannelSelect = ADCUI_F0_I_CH_I0;
    channelInitStruct.ADCUI_Fx_VPeakLevel = 0xFFFFU;
    channelInitStruct.ADCUI_Fx_IPeakLevel = 0xFFFFU;
    /* Call init functions */
    ADCUI_Init(&initStruct);
    ADCUI_Fx_Init(ADCUI_F0_UNIT, &channelInitStruct);
    ADCUI_Fx_Init(ADCUI_F1_UNIT, &channelInitStruct);
    //channelInitStruct.IPGA = adcuiPGA_18dB;
    ADCUI_Fx_Init(ADCUI_F2_UNIT, &channelInitStruct);



    ADCUI_ITConfig(ADCUI_F0_UNIT, ADCUI_Fx_IT_ZEROCRS, ENABLE);
    ADCUI_ITConfig(ADCUI_F1_UNIT, ADCUI_Fx_IT_ZEROCRS, ENABLE);
    ADCUI_ITConfig(ADCUI_F2_UNIT, ADCUI_Fx_IT_ZEROCRS, ENABLE);


    CLIC_SetPriorityIRQ(ADCUI_F03_IRQn, ADCUI_IRQ_PRIORITY);
    CLIC_ClearPendingIRQ (ADCUI_F03_IRQn);
    CLIC_SetPriorityIRQ(ADCUI_F1_IRQn, ADCUI_IRQ_PRIORITY);
    CLIC_ClearPendingIRQ (ADCUI_F1_IRQn);
    CLIC_SetPriorityIRQ(ADCUI_F2_IRQn, ADCUI_IRQ_PRIORITY);
    CLIC_ClearPendingIRQ (ADCUI_F2_IRQn);
}

static volatile int64_t posEnrg, negEnrg, tmpEnrg;
static uint16_t metrologyIncEnergyAccum( __IO uint32_t* posH, __IO uint32_t* posL, __IO uint32_t* negH, __IO uint32_t* negL, adcuiChActReactEnergyLiveData* chnl)
{
    /* get data from registers */
    posEnrg = *posH;
    posEnrg <<= 25;
    posEnrg += *posL;
    tmpEnrg = *negH;
    tmpEnrg <<= 25;
    tmpEnrg += *negL;

    // divide energy accumulator to avoid overflow
    posEnrg>>=ENERGY_DIVIDER_RSH;
    tmpEnrg>>=ENERGY_DIVIDER_RSH;

    /* calculate differences */
    negEnrg = tmpEnrg - posEnrg;
    posEnrg = posEnrg - tmpEnrg;

    /* self-driven detection */
    chnl->count++;
    if (posEnrg > (int64_t)chnl->sdThrdAR)
    {
        chnl->positive += (uint64_t)posEnrg;
        return METROLOGY_FLAG_POSITIVE_UPD;
    }
    else
        if (negEnrg > (int64_t)chnl->sdThrdAR)
        {
            chnl->negative += (uint64_t)negEnrg;
            return METROLOGY_FLAG_NEGATIVE_UPD;
        }
        else
            if (chnl->count >= SELF_DRIVEN_METROLOGY_COUNT)
                return METROLOGY_FLAG_SELF_DRIVEN;
            else
                return METROLOGY_FLAG_NO_UPD;
}

void stateChange(uint8_t newState, uint8_t evt, uint32_t Uadc, uint32_t u32TimeMark, adcuiChLiveData* cdt)
{
    cdt->chState=newState;
    cdt->vEvents[cdt->vEventCnt]=evt;
    cdt->vTimeMark[cdt->vEventCnt]=u32TimeMark;
    cdt->vCurrVal[cdt->vEventCnt]=Uadc;
    switch (evt)
    {
        case ME_OVER_START:
            cdt->vMinMax[cdt->vEventCnt]=Uadc;
            cdt->vrmsTrueMax=Uadc;
            break;
        case ME_OVER_FIN:
            cdt->vMinMax[cdt->vEventCnt]=cdt->vrmsTrueMax;
            break;
        case ME_UNDER_START:
        case ME_LOSS_START:
            cdt->vMinMax[cdt->vEventCnt]=Uadc;
            cdt->vrmsTrueMin=Uadc;
            break;
        case ME_UNDER_FIN:
        case ME_LOSS_FIN:
            cdt->vMinMax[cdt->vEventCnt]=cdt->vrmsTrueMin;
            break;
        default:
            break;
    }
    cdt->vEventCnt++;
}
void processChState(uint32_t Uadc, uint32_t u32TimeMark, uint8_t chIdx)
{
    adcuiChLiveData* cdt = &g_chLiveData[chIdx];

    if (cdt->vEventCnt>EVENTS_BUF_SZ-1)
        return;

    switch (cdt->chState)
    {
        case chState_INIT:  // start state of channels
            if (Uadc>=g_cmnSettings.vUnderVoltageThrOut)
            {
                if (Uadc>g_cmnSettings.vOverVoltageThrIn) // > 110%*Udin -> OVER event starts
                    stateChange(chState_OVER, ME_OVER_START, Uadc, u32TimeMark, cdt);
                else // >= 92%*Udin -> go from INIT to NORMAL
                    cdt->chState=chState_NORM;
            }
            // < 87%*Udin -> INIT event continues
            break;
        case chState_NORM:
            if (Uadc>g_cmnSettings.vOverVoltageThrIn)  // > 110%*Udin -> finish NORM, start OVER
                stateChange(chState_OVER, ME_OVER_START, Uadc, u32TimeMark, cdt);
            else // < 115%*Udin
            {
                if (Uadc<g_cmnSettings.vUnderVoltageThrIn)  // < 90%*Udin -> finish NORM, start UNDER
                {
                    stateChange(chState_UNDR, ME_UNDER_START, Uadc, u32TimeMark, cdt);

                    if (Uadc<g_cmnSettings.vLossVoltageThrIn)  // < 10%*Udin -> finish NORM, start LOSS
                        stateChange(chState_LOSS, ME_LOSS_START, Uadc, u32TimeMark, cdt);
                }
            }
            break;
        case chState_OVER:
            if(Uadc<=g_cmnSettings.vOverVoltageThrOut)  // <= 108%*Udin -> finish OVER
            {
                stateChange(chState_NORM, ME_OVER_FIN, Uadc, u32TimeMark, cdt);

                if (Uadc<g_cmnSettings.vUnderVoltageThrIn)  // < 90%*Udin -> finish NORM, start UNDER
                {
                    stateChange(chState_UNDR, ME_UNDER_START, Uadc, u32TimeMark, cdt);

                    if (Uadc<g_cmnSettings.vLossVoltageThrIn)  // < 10%*Udin -> finish NORM, start LOSS
                        stateChange(chState_LOSS, ME_LOSS_START, Uadc, u32TimeMark, cdt);
                }
            }
            else  // continuing state OVER
                if (Uadc>cdt->vrmsTrueMax)
                    cdt->vrmsTrueMax=Uadc;
            break;
        case chState_UNDR:
            if (Uadc>=g_cmnSettings.vUnderVoltageThrOut)  // >= 92%*Udin -> finish UNDR
            {
                stateChange(chState_NORM, ME_UNDER_FIN, Uadc, u32TimeMark, cdt);

                if (Uadc>g_cmnSettings.vOverVoltageThrIn)  // > 110%*Udin -> start OVER
                    stateChange(chState_OVER, ME_OVER_START, Uadc, u32TimeMark, cdt);
            }
            else  // UNDER continues
            {
                if (Uadc<g_cmnSettings.vLossVoltageThrIn)  // < 10%*Udin -> start LOSS
                    stateChange(chState_LOSS, ME_LOSS_START, Uadc, u32TimeMark, cdt);
                else
                    if (Uadc<cdt->vrmsTrueMin)
                        cdt->vrmsTrueMin=Uadc;
            }
            break;
        case chState_LOSS:
            if (Uadc>=g_cmnSettings.vLossVoltageThrOut) // >= 12%*Udin -> LOSS event ends
            {
                stateChange(chState_UNDR, ME_LOSS_FIN, Uadc, u32TimeMark, cdt);

                if (Uadc>=g_cmnSettings.vUnderVoltageThrOut)  // >= 92%*Udin -> start NORM
                {
                    stateChange(chState_NORM, ME_UNDER_FIN, Uadc, u32TimeMark, cdt);
                    if (Uadc>g_cmnSettings.vOverVoltageThrIn)  // > 110%*Udin -> start OVER
                        stateChange(chState_OVER, ME_OVER_START, Uadc, u32TimeMark, cdt);
                }
            }
            break;
        default:
            break;
    }
}



void __ramfunc metrologyIrqProcessing(uint8_t ch)
{
    ADCUI_LRegs *regL;
    ADCUI_MRegs *regM;
    __IO uint32_t *regH;
    adcuiChLiveData* dt;
    adcuiCmnSettings* chs;
    uint32_t cycleN, timeD;
    uint32_t U200ms, u32TimeMark;
    uint16_t flag, energyFlags=0;
    bool ignoreS=false;

    // point registers and structures
    // METROLOGY_ADCUI
    regL = GET_ADCUI_L_REG(ch);
    regM = GET_ADCUI_M_REG(ch);
    regH = GET_ADCUI_H_REG(ch);

    dt = &g_chLiveData[ch];
    chs = &g_cmnSettings;

    // drop zero-cross flag
    regL->FxSTAT |= (1 << 21);

    // read cycle from core's internal mcycle register
    __asm__("csrr %0, mcycle" : "=r"(cycleN) : );
    dt->zCnt++;

    timeD = cycleN-dt->lastCpuCycleF;
    dt->timeAccF += timeD;
    dt->TimeMarkLow=(uint16_t)(cycleN>>16);
    if (cycleN<dt->lastCpuCycleF)
        dt->TimeMarkHigh++;
    dt->lastCpuCycleF = cycleN;

    dt->perCnt = dt->perCnt + 1;

    dt->vrmsTrue = regL->FxVRMS;
    dt->vrmsAcc += dt->vrmsTrue; // ADCUI_F0VRMS_TRUE - true Vrms
    if ((regL->FxVRMS & 0x00800000ul)!=0)
        ignoreS=true;   // if negative values in voltage then register of full energy contain waste
#ifndef MAXIMIZE_CURRENT
    if ((regL->FxIRMS & 0x00800000ul)==0)  // check sign bit of 24-bit register
    {
        dt->irmsAcc += regL->FxIRMS;  // ADCUI_FxIRMS
    	dt->irmsTrue = regL->FxIRMS;

    }
    else
        ignoreS=true;  // ignore negative values in current register and full energy
#else
    if ((regL->FxIRMS & 0x00800000ul)==0)  // check sign bit of 24-bit register
        dt->irmsAcc += (regL->FxIRMS)<<CURR_MUL;  // ADCUI_FxIRMS*(2^CURR_MUL)
    else
        ignoreS=true;  // ignore negative values in current register and full energy
#endif

    // true RMS and over/undervoltages
    dt->vrmst10acc+=dt->vrmsTrue;
    dt->acc10Cnt++;
    if (dt->acc10Cnt==10)  // end of 10 cycles (base interval) processing
    {
        U200ms=(dt->vrmst10acc+5)/10;
        dt->vrmst10acc=0;
        dt->acc10Cnt=0;
        dt->Uust3sAcc+=U200ms;
        dt->UdevP3sAcc += (U200ms>chs->Udin) ? U200ms : chs->Udin;
        dt->UdevN3sAcc += (U200ms<chs->Udin) ? U200ms : chs->Udin;
        dt->acc3sCnt++;
    }
    if (dt->acc3sCnt==15)  // end of 150 cycles (3s) processing
    {
        dt->Uust1mAcc+=dt->Uust3sAcc/dt->acc3sCnt;
        dt->acc1mCnt++;
        dt->Uust3sAcc=0;
        dt->UdevP10mAcc+=dt->UdevP3sAcc/dt->acc3sCnt;
        dt->UdevN10mAcc+=dt->UdevN3sAcc/dt->acc3sCnt;
        dt->acc10mCnt++;
        dt->UdevP3sAcc=0;
        dt->UdevN3sAcc=0;
        dt->acc3sCnt=0;
    }

    u32TimeMark=dt->zCnt;
    if (ch==PHASE_CHNL_IDX)
        processChState(dt->vrmsTrue, u32TimeMark, ch);

    // proceed self driven detection...
    // for active energies
    flag = metrologyIncEnergyAccum(&regL->FxWATTP, &regM->FxWATTP_L, &regL->FxWATTN, &regM->FxWATTN_L, &dt->active);
    if (flag != METROLOGY_FLAG_NO_UPD)
    {
    	dt->activeP = regL->FxWATTP;
    	dt->activeP <<=25;
    	dt->activeP |= regM->FxWATTP_L;

    	dt->activeN = regL->FxWATTN;
    	dt->activeN <<=25;
    	dt->activeN |= regM->FxWATTN_L;

        regL->FxCTR |= (1 << 3);  // drop active accum
        dt->active.count = 0;
        dt->active.timeAcc += timeDiff(cycleN, lastCpuCycleP[ch]);
        lastCpuCycleP[ch] = cycleN;
    }
    energyFlags |= (uint16_t)((flag & METROLOGY_FLAG_UPD_MASK)<<METRLG_FLAGS_ACT_OFFS);

    // for reactive energies
    flag = metrologyIncEnergyAccum(&regL->FxVARP, &regM->FxVARP_L, &regL->FxVARN, &regM->FxVARN_L, &dt->reactive);
    if (flag != METROLOGY_FLAG_NO_UPD)
    {

    	dt->reactiveP = regL->FxVARP;
    	dt->reactiveP <<=25;
    	dt->reactiveP |= regM->FxVARP_L;

    	dt->reactiveN = regL->FxVARN;
    	dt->reactiveN <<=25;
    	dt->reactiveN |= regM->FxVARN_L;

        regL->FxCTR |= (1 << 4);  // drop reactive accum
        dt->reactive.count = 0;
        dt->reactive.timeAcc += timeDiff(cycleN, lastCpuCycleQ[ch]);
        lastCpuCycleQ[ch] = cycleN;
    }
    energyFlags |= (uint16_t)((flag & METROLOGY_FLAG_UPD_MASK)<<METRLG_FLAGS_REACT_OFFS);

    volatile uint64_t temp;
    temp = regL->FxVR;
    temp <<= 25;
    temp += regM->FxVR_L;
    if (ignoreS)
    {
        temp=0;  // ignore negative values in current register and full energy
        regL->FxCTR |= (1 << 5);  // drop apparent accumulator
    }
    temp>>=ENERGY_DIVIDER_RSH;
    dt->apparent.count++;
    if (temp > dt->apparent.sdThrdS)
    {
        dt->apparent.value += temp;
        regL->FxCTR |= (1 << 5);  // drop apparent accumulator
        dt->apparent.count = 0;
        dt->apparent.timeAcc += cycleN - lastCpuCycleS[ch];
        lastCpuCycleS[ch] = cycleN;
        energyFlags |= METROLOGY_FLAG_VR_UPD;
    }
    else
        if (dt->apparent.count >= SELF_DRIVEN_METROLOGY_COUNT)
        {
            regL->FxCTR |= (1 << 5);  // drop apparent accumulator
            dt->apparent.count = 0;
        }
    dt->updateStatus |= energyFlags;
}

/* channel 1 ADCUI IRQ processing */
void __ramfunc __attribute__ ((interrupt)) ADCUI_F03_IRQHandler(void) {
    metrologyIrqProcessing(0);
    CLIC_ClearPendingIRQ(ADCUI_F03_IRQn);
}

/* channel 2 ADCUI IRQ processing */
void __ramfunc __attribute__ ((interrupt)) ADCUI_F1_IRQHandler(void) {
    metrologyIrqProcessing(1);
    CLIC_ClearPendingIRQ(ADCUI_F1_IRQn);
}

/* channel 3 ADCUI IRQ processing */
void __ramfunc __attribute__ ((interrupt)) ADCUI_F2_IRQHandler(void) {
    metrologyIrqProcessing(2);
    CLIC_ClearPendingIRQ(ADCUI_F2_IRQn);
}


void drv_adcui_init()
{
	RST_CLK_PCLKCmd(RST_CLK_PCLK_DMA, ENABLE);
	DMA_Cmd(ENABLE);
	init_gpio();
	init_adcui();
	start_adcui();
	g_cmnSettings.activeImpStep[0] = 100000;
	g_cmnSettings.reactiveImpStep[0] = 100000;
	g_cmnSettings.activeImpStep[1] = 100000;
	g_cmnSettings.reactiveImpStep[1] = 100000;
	g_cmnSettings.activeImpStep[2] = 1000;
	g_cmnSettings.reactiveImpStep[2] = 1000;
}

METROLOGY_SIGN_TEXT_SECTION __attribute__ ((noinline)) __attribute__((optimize("O1")))
static int64_t valueADCtoDB(int64_t adcVal, BaseUnit unit) {

    switch (unit)
    {
    case UNIT_VOLT:				if(adcVal&0x800000) return 0-(0xFFFFFF - adcVal)*4/9;
    							else return (adcVal*4/9);
    case UNIT_AMPERE: 			return (adcVal * 9 / 0x800);
    case UNIT_WATT:				if(adcVal&0x80000000)
    								return 0-(0xFFFFFFFF - adcVal)/10000;
								else return (adcVal/10000);
    case UNIT_WATT_HOUR:		return (adcVal*8172/3600ull+(1ull<<31))>>32;
    case UNIT_CENT_WATT_HOUR:	return (adcVal*817230/3600ull+(1ull<<31))>>32;

    }
    return 0;
}


int64_t convertValueADCtoDB(int64_t adcVal, BaseUnit unit)
{
    int64_t dbVal = 0;
    int sign = 1;

    if (adcVal==0)
    	return 0;
    if (adcVal<0)
    {
    	sign=-1;
    	adcVal=-adcVal;
    }

    if (unit ==  UNIT_HERTZ || unit ==  UNIT_1f || unit ==  UNIT_DEGREE || unit ==  UNIT_PERCENT)
        dbVal = adcVal;
    else
        dbVal = valueADCtoDB(adcVal, unit);

    if (sign==1)
    	return dbVal;
    else
    	return -dbVal;
}



int64_t drv_adcui_getVoltage(ADCUIChannel channel)
{
	 adcuiChLiveData* dt = &g_chLiveData[channel];
	 return convertValueADCtoDB(dt->vrmsTrue,UNIT_VOLT);
}

int64_t drv_adcui_getCurrent(ADCUIChannel channel)
{
	 adcuiChLiveData* dt = &g_chLiveData[channel];
	 return convertValueADCtoDB(dt->irmsTrue,UNIT_AMPERE);
}

int64_t drv_adcui_getCurrentNull()
{
	uint32_t adcVal = ADCUI_GetRMS(ADCUI_CH_F0_I3);
	return convertValueADCtoDB(adcVal,UNIT_AMPERE)*10;
}

int64_t drv_adcui_getEnergy(ADCUIChannel channel)
{
	 adcuiChLiveData* dt = &g_chLiveData[channel];
	 return convertValueADCtoDB((int64_t)dt->active.positive,UNIT_CENT_WATT_HOUR);
}

void drv_adcui_getChannelData(ADCUIChannel channel, ADCUIChannelDataStruct *_return)
{
	adcuiChLiveData* dt = &g_chLiveData[channel];
	_return->channelNumber = channel;
	_return->voltage = (int32_t)convertValueADCtoDB(dt->vrmsTrue,UNIT_VOLT);
	_return->current = (int32_t)convertValueADCtoDB(dt->irmsTrue,UNIT_AMPERE);
	_return->power = (int32_t)convertValueADCtoDB(dt->vrmsTrue,UNIT_VOLT)*(int32_t)convertValueADCtoDB(dt->irmsTrue,UNIT_AMPERE);
	_return->maxVoltage = (int32_t)convertValueADCtoDB(dt->vrmsTrueMax,UNIT_VOLT); //mV
	_return->maxCurrent = 0; //mA
	_return->powerWH = (int32_t)(convertValueADCtoDB((int64_t)dt->active.positive,UNIT_WATT_HOUR)*100   +
								 convertValueADCtoDB((int64_t)dt->reactive.positive,UNIT_WATT_HOUR)*100 +
								 convertValueADCtoDB((int64_t)dt->reactive.negative,UNIT_WATT_HOUR)*100);	 	//mW
	_return->freq = 0;		//mHz
	_return->activeP = (int32_t)convertValueADCtoDB((int64_t)dt->activeP,UNIT_WATT); 	//W
	_return->activeN = (int32_t)convertValueADCtoDB((int64_t)dt->activeN,UNIT_WATT);	//W
	_return->reactiveP = (int32_t)convertValueADCtoDB((int64_t)dt->reactiveP,UNIT_WATT);//W
	_return->reactiveN = (int32_t)convertValueADCtoDB((int64_t)dt->reactiveN,UNIT_WATT);//W
	_return->activePWH = (int32_t)convertValueADCtoDB((int64_t)dt->active.positive,UNIT_WATT_HOUR); 	//W*Hour
	_return->activeNWH = (int32_t)convertValueADCtoDB((int64_t)dt->active.negative,UNIT_WATT_HOUR);	//W*Hour
	_return->reactivePWH = (int32_t)convertValueADCtoDB((int64_t)dt->reactive.positive,UNIT_WATT_HOUR);//W*Hour
	_return->reactiveNWH = (int32_t)convertValueADCtoDB((int64_t)dt->reactive.negative,UNIT_WATT_HOUR);//W*Hour
}

int32_t drv_adcui_getActiveImp(void)
{
	return (int32_t)(convertValueADCtoDB((int64_t)g_chLiveData[0].active.positive,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[1].active.positive,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[2].active.positive,UNIT_CENT_WATT_HOUR));
}

int32_t drv_adcui_getReactiveImp(void)
{
	return (int32_t)(convertValueADCtoDB((int64_t)g_chLiveData[0].reactive.positive,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[1].reactive.positive,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[2].reactive.positive,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[0].reactive.negative,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[1].reactive.negative,UNIT_CENT_WATT_HOUR) +
					convertValueADCtoDB((int64_t)g_chLiveData[2].reactive.negative,UNIT_CENT_WATT_HOUR));
}

int64_t drv_adcui_getActivePower(ADCUIChannel channel)
{
	return (int64_t)(g_chLiveData[channel].activeP-g_chLiveData[channel].activeN);
}

int64_t drv_adcui_getActivePowerWatt(ADCUIChannel channel)
{
	return (int64_t)convertValueADCtoDB((int64_t)(g_chLiveData[channel].activeP-g_chLiveData[channel].activeN), UNIT_WATT);
}

int64_t drv_adcui_getReactivePower(ADCUIChannel channel)
{
	return (int64_t)(g_chLiveData[channel].reactiveP -g_chLiveData[channel].reactiveN);
}

int64_t drv_adcui_getReactivePowerWatt(ADCUIChannel channel)
{
	return (int64_t)convertValueADCtoDB((int64_t)(g_chLiveData[channel].reactiveP - g_chLiveData[channel].reactiveN), UNIT_WATT);
}


