📄 calphased.c
字号:
/***************************************************************************
* This code and information is provided "as is" without warranty of any *
* kind, either expressed or implied, including but not limited to the *
* implied warranties of merchantability and/or fitness for a particular *
* purpose. *
* *
* Copyright (C) 2006 Teridian Semiconductor Corp. All Rights Reserved. *
***************************************************************************/
//**************************************************************************
// DESCRIPTION: 71M65xx POWER METER - Automatic calibration of the meter.
// This includes temperature calibration, electrical calibration, including
// phase compensation. Assumes no reactive power in the test signal.
//
// AUTHOR: RGV / MTF
//
// HISTORY: See end of file.
//**************************************************************************
// File: calphased.c
//
#include "options.h"
// if the meter uses current differences, then use the alternate
// calibration.c, a linear calibration of volts and current.
#if (CAL_SAVE || AUTOCAL) && \
(EQUATION != _1ELEMENT_3WIRE \
&& EQUATION != _2ELEMENT_4WIRE_DELTA \
&& EQUATION != _2ELEMENT_4WIRE_WYE)
#include <math.h> // for sqrt()
#include "mmath.h" // for s2f()
#include "error.h"
#include "meter.h"
#include "library.h"
#if NV_SELECT == NV_EEPROM
#include "eeprom.h"
#endif
#if NV_SELECT == NV_FLASH
#include "flash.h" // Disable CE while writing flash.
#endif
#include "ce.h" // need "samples", disable CE while writing flash
#include "calibration.h"
#define ZERO 0L
/*** External functions referenced by this module ***/
// Refer to include files.
/*** External variables referenced by this module ***/
extern const int32r_t CeData[]; // The default data for the analog front end.
extern const int8r_t NumCeData[2];
/*** Public functions referenced by this module ***/
/* See 'calibration.h' */
/*** Public variables referenced by this module ***/
bool cal_flag; // Keep calibrating.
static int8p_t cntDown; // Remaining accumulation intervals of calibration.
/*** Private functions referenced by this module ***/
static void adjust_element (uint8_t element_index);
static void cal_end (void);
static void compensation (void);
bool memcpy_cepr (int32x_t * pDst, uint16_t pSrc, uint16_t len);
void memcpy_prce (uint16_t pDst, int32x_t * pSrc, uint16_t len);
#if NV_SELECT == NV_FLASH
#define MANY 0xFF
void cal_save (void) // Save the calibration.
{
if (CalibrationCount != MANY)
CalibrationCount++;
// Compute & store checksums.
LRC_Calc_NVR ((uint8x_t *) &Totals.Parms, sizeof (struct Parameters_t), TRUE);
chksum_ce ((int32x_t *) CE_PARM_BASE, CE_PARM_SIZE, TRUE);
CE_DISABLE();
memcpy_rx ((uint8r_t *) r_Parms, (uint8x_t*) &Totals.Parms, sizeof (struct Parameters_t) );
memcpy_rce ((uint8r_t *) CeData, (uint8x_t *) CE_PARM_BASE, CE_PARM_SIZE);
CE_ENABLE();
}
#elif NV_SELECT == NV_EEPROM
#define MANY 0xFF
void cal_save (void) // Save the calibration.
{
if (CalibrationCount != MANY)
CalibrationCount++;
// Compute & store checksums.
LRC_Calc_NVR ((uint8x_t *) &Totals.Parms, sizeof (struct Parameters_t), TRUE);
CE_DISABLE();
lrc_ce ((int32x_t *) CE_PARM_BASE, CE_PARM_SIZE, TRUE );
#if EEPROM
eeprom_enable(); // Enable non-volatile store.
memcpy_prce (
EEPROM_CALIBRATION,
(int32x_t *) CE_PARM_BASE,
CE_PARM_SIZE );
memcpy_prx (
EEPROM_CALIBRATION + (CE_PARM_SIZE * 4),
(uint8x_t *) &Totals.Parms,
sizeof (struct Parameters_t) );
eeprom_disable(); // Disable non-volatile store.
#endif
CE_ENABLE();
}
#else
#error unknown NV type
#endif // Validate the restored calibration.
#if NV_SELECT == NV_FLASH
bool cal_restore (void) // Restore the calibration.
{
bool ok = 1;
// get the CE's data
memcpy_cer (
(int32x_t *) CE_PARM_BASE,
&CeData[0],
CE_PARM_SIZE );
// get the MPU's data
memcpy_xr (
(uint8x_t *) &Totals.Parms,
(uint8r_t *) r_Parms,
sizeof (struct Parameters_t) );
#if CONSTANTS_DBG
// Make CE defaults visible to the AMD-51 ICE.
// Note, if this code is enabled, overlap errors occur in the link,
// but they do not cause defective operation.
get_ce_constants ();
#endif
// test the data to see if it's valid
ok &= LRC_Calc_NVR (
(uint8x_t *) &Totals.Parms,
sizeof (struct Parameters_t),
FALSE);
ok &= lrc_ce (
(int32x_t *) CE_PARM_BASE,
CE_PARM_SIZE,
FALSE);
if (!ok)
Status |= CAL_BAD; // dating, etc. is in the main loop
else
Status &= ~CAL_BAD;
CE_ENABLE();
return ok;
}
#elif NV_SELECT == NV_EEPROM && EEPROM
bool cal_restore (void) // Restore the calibration.
{
bool ok = 1;
enum EEPROM_RC data *state;
#if EEPROM
eeprom_enable(); // Enable non-volatile store.
CE_DISABLE();
// get the CE's data
ok &= memcpy_cepr (
(int32x_t *) CE_PARM_BASE,
EEPROM_CALIBRATION,
CE_PARM_SIZE);
// get the MPU's data
state = memcpy_xpr (
(uint8x_t *) &Totals.Parms,
EEPROM_CALIBRATION + (CE_PARM_SIZE * 4),
sizeof (struct Parameters_t)
);
ok &= eeprom_ok (state);
eeprom_disable(); // Disable non-volatile store.
#endif
#if CONSTANTS_DBG
// Make CE defaults visible to the AMD-51 ICE.
// Note, if this code is enabled, overlap errors occur in the link,
// but they do not cause defective operation.
get_ce_constants ();
#endif
// test the data to see if it's valid
ok &= LRC_Calc_NVR (
(uint8x_t *) &Totals.Parms,
sizeof (struct Parameters_t),
FALSE);
ok &= lrc_ce (
(int32x_t *) CE_PARM_BASE,
CE_PARM_SIZE,
FALSE);
memset_ce(&apulsew,0); // do not restore the pulse rates
memset_ce(&apulser,0); // they cause spurious pulses at start-up
if (!ok)
Status |= CAL_BAD; // dating, etc. is in the main loop
else
Status &= ~CAL_BAD;
CE_ENABLE();
return ok;
}
// copy data from EEPROM to the CE's data area
bool memcpy_cepr (int32x_t * pDst, uint16_t pSrc, uint16_t len)
{
int32_t xdata nv_data;
enum EEPROM_RC data *state;
bool ok = 1;
for(; len != 0; --len)
{
state = memcpy_xpr ((uint8x_t *)&nv_data, pSrc, sizeof (int32_t));
ok &= eeprom_ok (state);
memset_ce (pDst++, nv_data);
pSrc += sizeof (int32_t);
}
return ok;
}
// copy data from the CE's data area to EEPROM
void memcpy_prce (uint16_t pDst, int32x_t * pSrc, uint16_t len)
{
int32_t xdata nv_data;
for(; len != 0; --len)
{
nv_data = memget_ce (pSrc++);
memcpy_prx (pDst, (uint8x_t *)&nv_data, sizeof (int32_t));
pDst += sizeof (int32_t);
}
}
#else
#error unknown NV type
#endif
/*** Private variables referenced by this module ***/
#if AUTOCAL
#if EQUATION != _3ELEMENT_4WIRE_WYE
#define ELEMENT_CNT 2
#else
#define ELEMENT_CNT 3
#endif
struct Element_t
{
uint8x_t *pwh;
uint8x_t *pvarh;
int32x_t *pvsqsum;
int32x_t *pcal_v;
int32x_t *pcal_i;
int32x_t *pphase_adj;
};
// On equations that add currents, it's impossible to calibrate the phase of
// both currents with a single test signal. Equation 1-element-3-wire is tested
// by putting the meter in equation 0, and using the undocumented "B" Wh
// and VARh registers of the CE code, calculated from VA * IB.
#if EQUATION == _1ELEMENT_2WIRE || EQUATION == _1ELEMENT_3WIRE
#define CALIBRATIONS 3
#define ADJUSTS 2
#define ELEMENTS 2
#define CAL_EQUATION _1ELEMENT_2WIRE
static struct Element_t code element_array [ELEMENT_CNT] = {
{&Whn_B, &VARh_B, &v0sqsum, &cal_v0, &cal_i1, &phadj_1},
// The standard CE code calculates "B" as VA * IB for equation 1-element-2-wire
// Element A usually has the best data, so v0 is adjusted last from A's data
{&Whn_A, &VARh_A, &v0sqsum, &cal_v0, &cal_i0, &phadj_0}
};
#elif EQUATION == _2ELEMENT_3WIRE_DELTA
#define CALIBRATIONS 4
#define ADJUSTS 2
#define ELEMENTS 2
#define CAL_EQUATION _2ELEMENT_3WIRE_DELTA
static struct Element_t code element_array [ELEMENT_CNT] = {
{&Whn_A, &VARh_A, &v0sqsum, &cal_v0, &cal_i0, &phadj_0},
{&Whn_B, &VARh_B, &v0sqsum, &cal_v1, &cal_i1, &phadj_1}
};
#elif EQUATION == _2ELEMENT_4WIRE_DELTA
#define CALIBRATIONS 5
#error equation not supported: use the non-phase-adjusting calibration code
// We believe that these two unsupported equations will be rarely used
// with our chip, because many meter manufacturers simply run conductors
// in opposite directions through a shared CT. However, if you need it...
// The alternate calibration code uses the same test setup, but uses
// vsqsum and isqsum, without adjusting the phases of the current. For CTs,
// that version is usually within 0.04%, the typical phase error of a CT.
// The interface to the rest of the code is unchanged.
#elif EQUATION == _2ELEMENT_4WIRE_WYE
#define CALIBRATIONS 5
#error equation not supported: use the non-phase-adjusting calibration code
#elif EQUATION == _3ELEMENT_4WIRE_WYE
#define CALIBRATIONS 6
#define ADJUSTS 3
#define ELEMENTS 3
#define CAL_EQUATION _3ELEMENT_4WIRE_WYE
static struct Element_t code element_array [ELEMENT_CNT] = {
{&Whn_A, &VARh_A, &v0sqsum, &cal_v0, &cal_i0, &phadj_0},
{&Whn_B, &VARh_B, &v1sqsum, &cal_v1, &cal_i1, &phadj_1},
{&Whn_C, &VARh_C, &v2sqsum, &cal_v2, &cal_i2, &phadj_2}
};
#else
#error no such equation
#endif
void cal_begin (void) // Begin calibration.
{
int32x_t *px;
int8_t i;
// MPU calibrations are omitted intentionally, because
// all of them are more like configurations set by a user
// to adapt the meter.
// Sets default calibration for CE, including temperature compensations.
px = &cal_i0;
i = CALIBRATIONS;
do { memset_ce (px++, UNITY); } while (--i);
i = ADJUSTS;
do { memset_ce (px++, ZERO); } while (--i);
// Change to a metering equation that does not sum current sensors.
// If no such equation is possible, try the non-phase-adjusting
// calibration, which still gets within 0.04%, the typical error
// of a CT.
CE0 = (CE0 & ~EQU) | CAL_EQUATION;
#if ADC_COMPENSATION
// Start the calibration process, with 2 accumulation intervals
// before temperature is collected, to clear the data pipeline.
// Then, it collects data for another two seconds with the
// temperature compensation running.
cntDown = 4 + Scal;
#else
// Start the calibration process, with 2 accumulation intervals
// before measurement begins, to clear the data pipeline.
cntDown = 2 + Scal;
#endif
// set the global calibration flag
Config |= CFG_CAL;
}
/* This the calibration state machine.
* 1. Let the CE settle, then
* 2. Measure temperature for calibration, then
* 3. Measure the current for calibration, then
* 4. Perform the calibrations.
* This calibrates gains, and phase for power elements.
* It does not calibrate neutral current sensors,
* or other odd arrangements.
*/
void Calibrate (void)
{
if (0 >= cntDown || cntDown > (Scal + 4) || (Config & CFG_CAL) == 0)
return;
// Scal, Vcal and Ical are defined in 'defaults.c'.
#if ADC_COMPENSATION
/* Need two (2) accumulation intervals before the
* raw temperature will be correct.
* At this point, it can set the nominal temperature,
* and then begin to collect the data with
* temperature compensation enabled. */
if (cntDown == (Scal + 2))
compensation (); // Set temperature compensation values.
#endif
if (cntDown == (Scal + 1)) // On the first accumulation interval of calibration.
Config |= CFG_CLEAR_ACC; // Clear the registers to 0.
if (0 == --cntDown) // Count accumulation intervals.
{ // If the last accumulation interval, calibrate.
cntDown = -1; // Make the count invalid.
Config &= ~CFG_CAL; // Turn off calibration mode.
CE_DISABLE(); // Leave data values for engineer to see.
cal_end (); // Adjust calibration constants.
cal_save(); // Save the new calibration.
#if ERROR_RECORDING
Error_Clear();
#endif
// Turn ON all the pulse outputs to show it's done.
// This is a convenient indication because it works with
// automatic calibration or manual operation.
DIO &= 0xF3; // The pulse outputs are DIOs.
#if 1 == M6520
CONFIG2 = 0x40 | (CONFIG2 & 0x3F); // Force TX_DIS to DIO_2.
USER0 &= 0x3D; // Drive the outputs to ground (LEDs on).
DIR0 |= 0xC4; // Change the DIOs 2, 6, 7 to outputs.
#elif TRACE10
USER0 &= 0x3F; // Drive both outputs to ground (LEDs on).
DIR0 |= 0xC0; // Change the DIOs 6, 7 to outputs.
#else
#error unknown device
#endif
// clear the global calibration flag
Config &= ~CFG_CAL;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -