📄 meter.c
字号:
else
Status &= ~CREEP;
#elif EQUATION == _3ELEMENT_4WIRE_WYE
bool creepA = FALSE, creepB = FALSE, creepC = FALSE;
if (Vrms_A > VThrshld)
{
Status &= ~MINVA;
if(Irms_A < IThrshld)
creepA = TRUE;
}
else
{
Status |= MINVA;
creepA = TRUE;
Vrms_A = v0sqsum = 0;
}
if (Vrms_B > VThrshld)
{
Status &= ~MINVB;
if(Irms_B < IThrshld)
creepB = TRUE;
}
else
{
Status |= MINVB;
creepB = TRUE;
Vrms_B = v1sqsum = 0;
}
if (Vrms_C > VThrshld)
{
Status &= ~MINVC;
if(Irms_C < IThrshld)
creepC = TRUE;
}
else
{
Status |= MINVC;
creepC = TRUE;
Vrms_C = v2sqsum = 0;
}
if (creepA)
{
Irms_A = w0sum = var0sum = i0sqsum = 0;
}
if (creepB)
{
Irms_B = w1sum = var1sum = i1sqsum = 0;
}
if (creepC)
{
Irms_C = w2sum = var2sum = i2sqsum = 0;
}
if (creepA && creepB && creepC)
{
Status |= CREEP;
#if TRACE13
memset_ce (&wsum_accum, 0xC0000000L);
memset_ce (&vsum_accum, 0xC0000000L);
#else
#error unknown device
#endif
}
else
Status &= ~CREEP;
#else
#error missing combination of equation and device
#endif
}
}
// Adjust the values for the second IMAX
#if IMAX2
// It's tempting to reuse the values that record the CE values,
// but they could be overwritten in real time, so that unscaled
// and scaled values would appear and disappear.
int32p_t w1sum, var1sum, i1sqsum;
static void RescalePhaseB(void)
{
if (0 == Imax2)
{
Imax2 = Imax;
}
if (Imax == Imax2)
{
// if they're the same, no scaling is needed
w1sum = w1sum_ce;
var1sum = var1sum_ce;
i1sqsum = i1sqsum_ce;
}
else
{
float rescale;
// The object is to scale phase B to use the same units as Imax.
// This is normally done because phase B has an alternate current
// sensor, such as a shunt.
// By putting everything in the same units, all the other math
// in the meter remains in terms of Imax, simplifying it.
// Precision: at the highest powers, two bits of precision
// are lost. This is less than 0.000007% error, 2 orders of
// magnitude less than the quantization error in the
// calibration (0.003%). At half power, one bit is lost.
// Below a quarter power, no precision is lost.
rescale = ((float)Imax2)/((float)Imax);
w1sum = (int32_t)(rescale * (float)w1sum_ce);
var1sum = (int32_t)(rescale * (float)var1sum_ce);
i1sqsum = (int32_t)((rescale*rescale) * (float)i1sqsum_ce);
}
}
#endif
// This speeds and slows the meter based on temperature.
static void Gain_Compensation (void)
{
float gain, dt;
// Algebraic simplification of formula in datasheets.
// temp_x = (Temp_Nom - Temp_Raw_X) * degscale
// deltaT (aka Temp_X) always less than 2^11.
// DEG_SCALE is a constant defined in ce652x.h, or ce651x.h
dt = DEG_SCALE * (float)(temp_nom - temp_raw);
deltaT = dt; // temperature for display by M1; LSB = 0.1C
// Algebraic simplification of formula in datasheets.
gain = ((float)ppmc2 * dt)/(512.); // squared term
gain = ((gain + (float)ppmc1) * (float)dt)/(16384.); // linear term
gain += 16385.; // constant term
// The gain is a signed 16-bit number. Prevent out-of-range values.
// The arithmetic permits negative gains, but negative gains
// are not physically possible.
gain = min(32767., max(gain, 0.));
memset_ce (&gain_adj, (int32_t)gain); // set gain for all channels
}
//===========================================================================//
#if FLAG
// The whole object of this routine is so that FLAG and the meter communicate
// safely in real time. The meter updates its data about once per second.
// The FLAG interface runs from a serial interrupt that can occur at any
// instant. This routine implements a synchronous "rendezvous" operation.
// When FLAG reads the data, 99% of the time, it gets a snapshot that makes sense,
// because this routine copies the data to FLAG's private copy of the data registers.
// Likewise, when FLAG writes the data, FLAG's setup is set all at once from
// FLAG's private registers.
// But, what if a FLAG read or write request happens during the copy?
// The "register available" boolean tells the FLAG interrupt that
// the data is not available during the copy operation. FLAG then returns a
// NAK to the hand-held unit, then the hand-held unit retries the request,
// and by then the private copy of the data is complete and the request finishes.
// This all really works. If one watches a continuous series of data requests
// on a serial monitor, occasionally one sees a NAK and a short flurry of retries.
void Update_register (void) // copy flag data to registers
{
register_available = FALSE;
if (register_write)
{
memcpy_xx ((uint8x_t *) &Totals, (uint8x_t *)&Registers, sizeof(struct Totals_t));
#if CONSTANTS_DBG
get_ce_constants (); // The meter configuration readable by ICE.
#endif
#if REAL_TIME_DATE
RTClk_Write (); // Set the clock from the revised totals.
#endif
register_write = FALSE;
}
else
memcpy_xx ((uint8x_t *)&Registers, (uint8x_t *)&Totals, sizeof(struct Totals_t));
register_available = TRUE;
}
#endif
#if TEMPERATURE
static void temperature_lcd (void)
{
LCD_Number (deltaT, 6, 1); // Display upto six digits.
LCD_1DP (); // One (1) decimal place.
}
#endif
//--------------------------------------------------//
void meter_lcd (void) // Meter display.
{
#if CE_OFF && REAL_TIME_DATE
if (!CE_ACTIVE && RTC_Tic ())
{
LCD_CE_Off ();
return;
}
#endif /* CE_OFF and REAL_TIME_DATE. */
if (ce_totals_ready)
{ // Outputs updated, display.
ce_totals_ready = FALSE;
#if SCROLL_METER
scroll_meter ();
#endif
meter_totals (select_total, select_phase);
#if HEART_BEAT
LCD_Heart_Beat ();
#endif
}
#if BROWNOUT_BATMODE
if (batmode_is_brownout ())
LCD_Brownout ();
#endif
}
#if AUTOSLEEP
// gets and displays the brownout data
// This runs in the brownout mode logic, with a (very slow) 32kHz clock,
// so the size of EEPROM data read is intentionally as small as possible.
// That's also why the number is precalculated, and the validity
// test is so simple.
//
// If this routine is modified to use hardware other than the eeprom and
// LCD, please update batmode_display() in main\batmodes.c to initialize
// the needed hardware.
void meter_brownout_lcd (void)
{
// get a valid value for the brownout LCD value
// Is the current RAM image of the brownout cache valid?
if (brownout_cache_valid != YES)
{
enum EEPROM_RC data *pstatus;
// no, so try the first eeprom image, in Acc
pstatus = memcpy_xpr (
(uint8x_t*)&brownout_cache,
(EEPROM_REGISTERS + brownout_cache_offset),
brownout_cache_len);
if (!eeprom_ok (pstatus) || brownout_cache_valid != YES)
{
// no, so try the second eeprom image, in AccB
// Perhaps the mains sagged while it was being calculated.
// In that case, the second copy will be ok.
pstatus = memcpy_xpr (
(uint8x_t*)&brownout_cache,
(EEPROM_REGISTERS
+ brownout_cache_offset
+ sizeof(struct Accumulators_t)),
brownout_cache_len);
if (!eeprom_ok (pstatus) || brownout_cache_valid != YES)
{
// no, so the EEPROM is bad, the value not set or
// the value is overwritten by bad data; substitute
// a default.
brownout_cache = 0L;
brownout_cache_valid = YES;
#if MODE_DISPLAY
brownout_cache_mode = M_WH;
#endif
}
}
}
// display it
wh_brownout_to_lcd (brownout_cache); // watts, total, all phases
#if MODE_DISPLAY
LCD_Mode (brownout_cache_mode);
#endif
}
#endif // autosleep
// Data sources for the LCD; replace entries with nulls to disable
// entries. The table is fixed-size so meanings of the indices do
// not change.
const uint8x_t * code LcdSrc[] =
{
// Note: The register names route the data from the calculations,
// to the revenue registers. "Wh" is the name of the revenue
// registers, however they are calculated. For example, meter.h
// may define Totals.Acc.Whr.a as "Whi", so that it is calculated
// as "imported watt-hours". Then, it defines "Wh" as Totals.Acc.Whr.a
// so that the default display will be the same. Other calculations
// can be used, therefore, such as VAh or absolute-value watt-hours.
(uint8x_t *) &Wh[0], (uint8x_t *) &Wh_A[0], (uint8x_t *) &Wh_B[0], (uint8x_t *) &Wh_C[0], // 12-15
(uint8x_t *) &Whe[0], (uint8x_t *) &Whe_A[0], (uint8x_t *) &Whe_B[0], (uint8x_t *) &Whe_C[0], // 24-27
(uint8x_t *) &VARhi[0], (uint8x_t *) &VARhi_A[0], (uint8x_t *) &VARhi_B[0], (uint8x_t *) &VARhi_C[0], // 16-19
(uint8x_t *) &VARhe[0], (uint8x_t *) &VARhe_A[0], (uint8x_t *) &VARhe_B[0], (uint8x_t *) &VARhe_C[0], // 28-31
(uint8x_t *) &VAh[0], (uint8x_t *) &VAh_A[0], (uint8x_t *) &VAh_B[0], (uint8x_t *) &VAh_C[0], // 8-11
};
#if DEMAND
#if PHASE_C_PRESENT
const uint32x_t * code LcdSrcP[] =
{
&wsum, &w0sum, &w1sum, &w2sum,
&varsum, &var0sum, &var1sum, &var2sum,
&vasum, &va0sum, &va1sum, &va2sum
};
#define NUM_SUMS 4
#else
const uint32x_t * code LcdSrcP[] =
{
&w0sum, &w1sum,
&var0sum, &var1sum,
&va0sum, &va1sum
};
#define NUM_SUMS 2
#endif
#endif // DEMAND.
void meter_totals (uint8_t select, uint8_t phase)
{
#if MODE_DISPLAY
bool show_mode = TRUE;
#endif
switch (select)
{
// default case is wh, phase0, below
#if TEMPERATURE
case M_TEMP: // Temperature.
temperature_lcd ();
break;
#endif
#if FREQUENCY
case M_FREQ: // Frequency.
frequency_lcd ();
break;
#endif
#if POWER_FACTOR
case M_PF:
power_factor_lcd (phase);
break;
#endif // POWER_FACTOR.
#if PHASE_ANGLES
case M_VI_ANGLE: // V/I Phase angle.
phase_angle_lcd (phase);
break;
#endif // PHASE_ANGLES.
#if VOLTAGE_PHASES
case M_VPHASE:
voltage_phase_lcd (phase);
break;
#endif // VOLTAGE_PHASES.
#if OPERATING_TIME
case M_HOURS: // Operating Hours.
operating_lcd ();
break;
#endif
#if REAL_TIME_DATE
case M_TIME: // Time.
time_lcd ();
break;
case M_DATE: // Date.
date_lcd ();
#if MODE_DISPLAY
show_mode = FALSE;
#endif
break;
#endif // REAL TIME/DATE.
#if MAIN_EDGE_COUNT
case M_EDGE_CNT:
main_edge_cnt_lcd (phase);
break;
#endif // MAIN_EDGE_COUNT.
#if PULSE_CNT
case M_PULSE:
pulse_cnt_lcd (select_pulse, select_interval);
break;
#endif
#if WATT_ELEMENT
case M_NONE: // Commanded to display nothing.
default:
phase = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -