📄 pcnt.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) 2005 Teridian Semiconductor Corp. All Rights Reserved. *
***************************************************************************/
//**************************************************************************
// DESCRIPTION: 71M652x POWER METER - Pulse counting.
// Note that TDK electric power metering ICs have memory-mapped registers
// that provide the same data as the pulses. Use of this module is
// deprecated because the counting interrupts can add a substantial
// real time burden, especially at high pulse rates. It is provided
// because several customers requested it.
//
// AUTHOR: RGV
//
// HISTORY: See end of file.
//**************************************************************************
// File: pcnt.c
//
#include "options.h"
#if PULSE_CNT
#include "irq.h"
#include "wd.h"
#include "lcd.h"
#include "mmath.h"
#include "meter.h"
#include "pcnt.h"
#include "psoft.h"
/*** Public functions used by this module. ***/
// See "meter.h" & "pcnt.h".
/*** Public variables used by this module. ***/
// None.
/*** Private functions defined by this module. ***/
static void pcnt_w (void); // pulse counts from Watts LED to LCD
static void pcnt_r (void); // pulse counts from VAR LED to LCD
static void pcnt_3 (void); // pulse counts from third LED to LCD
static void pcnt_4 (void); // pulse counts from Watts LED to LCD
static void pcnt_diff_w (void); // differential pulse counts from Watts LED to LCD
static void pcnt_diff_r (void); // differential pulse counts from VAR LED to LCD
static void pcnt_diff_3 (void); // differential pulse counts from Watts LED to LCD
static void pcnt_diff_4 (void); // differential pulse counts from Watts LED to LCD
/*** Private variables defined by this module. ***/
static bool data_ready = 0;
static int16d_t pcnt_w_cache; // Real-time count of pulses on watts led for a second.
static int16d_t pcnt_r_cache; // Real-time count of pulses on VARs led for a second.
static int16x_t pcnt_w_1_sec; // Count of pulses on watts led for a second.
static int16x_t pcnt_r_1_sec; // Count of pulses on VARs led for a second.
#if PULSE_SOFT // software pulse outputs exist
static int16x_t pcnt_3_1_sec; // Count of pulses on output 3 for a second.
static int16x_t pcnt_4_1_sec; // Count of pulses on output 4 for a second.
#endif // software pulse outputs exist
static int16x_t pcnt_duration; // seconds remaining to count pulses
static bool pcnt_enable; // 1 = counting is on
static bool pcnt_continuous; // 1 = counting is always on
//====================================================================//
void pulse_cnt_lcd (uint8_t select_phase, uint8_t select_interval)
{
switch (select_phase)
{
case 0: case 1:
switch (select_interval)
{
case 0:
case 1: pcnt_w (); break;
case 2: pcnt_diff_w (); break;
}
break;
case 2:
switch (select_interval)
{
case 0:
case 1: pcnt_r (); break;
case 2: pcnt_diff_r (); break;
}
break;
case 3:
switch (select_interval)
{
case 0:
case 1: pcnt_3 (); break;
case 2: pcnt_diff_3 (); break;
}
break;
case 4:
switch (select_interval)
{
case 0:
case 1: pcnt_4 (); break;
case 2: pcnt_diff_4 (); break;
}
break;
}
}
#pragma save
#pragma NOAREGS
// counts w pulses (rising edge of the W pulse LED output on DIO 6)
// The trick is that the "read" logic of the DIO pin can "see" the
// output of the pulse logic.
void pcnt_w_isr (void) small reentrant interrupt IO_INT0_IV
{
++pcnt_w_cache; // designed to be fast
}
#pragma restore
#pragma save
#pragma NOAREGS
// counts r pulses (rising edge of the R pulse LED output on DIO 7)
// The trick is that the "read" logic of the DIO pin can "see" the
// output of the pulse logic.
void pcnt_r_isr (void) small reentrant interrupt IO_INT1_IV
{
++pcnt_r_cache; // designed to be fast
}
#pragma restore
// accumulates and times the pulses,
// called from 1 second interrupt by real-time-clock
#pragma save
#pragma NOAREGS
// since this is an interrupt, the saved value of EA is always 1
#define ea 1
// called from the interrupt decode routines in io65??.c
// in order to synchronize the counts with the wall-time clock.
void pcnt_accumulate (void) small reentrant
{
if (pcnt_enable)
{
if (--pcnt_duration < 0) // is pulse-counting interval ended?
{
pcnt_duration = -1;
pcnt_enable = pcnt_continuous; // stop counting
}
// add cached 16-bit counts to main 64-bit counts
IRQ_DISABLE(); // Begin critical section.
pcnt_w_1_sec = pcnt_w_cache;
pcnt_w_cache = 0;
IRQ_ENABLE(); // End critical section.
// add cached 16-bit counts to main 64-bit counts
IRQ_DISABLE(); // Begin critical section.
pcnt_r_1_sec = pcnt_r_cache;
pcnt_r_cache = 0;
IRQ_ENABLE(); // End critical section.
#if PULSE_SOFT // software pulse outputs exist
IRQ_DISABLE(); // Begin critical section.
pcnt_3_1_sec = dPulse3_Cnt; // Count pulses on output 3 for 1 second.
dPulse3_Cnt = 0;
IRQ_ENABLE(); // End critical section.
IRQ_DISABLE(); // Begin critical section.
pcnt_4_1_sec = dPulse4_Cnt; // Count pulses on output 4 for 1 second.
dPulse4_Cnt = 0;
IRQ_ENABLE(); // End critical section.
#endif // software pulse outputs exist
data_ready = TRUE;
}
}
#undef ea
#pragma restore
#define ONE_MILLION 1000000L
int32_t pcnt_w_mod;
int32_t pcnt_r_mod;
#if PULSE_SOFT // software pulse outputs exist
int32_t pcnt_3_mod;
int32_t pcnt_4_mod;
#endif
// keep the pulse output update in the noninterrupting code.
// This keeps the counts from being updated while they're displaying.
// Normally called from meter_run in meter.c
void pcnt_update (void)
{
if (data_ready)
{
data_ready = FALSE;
pcnt_w_mod += pcnt_w_1_sec;
if (pcnt_w_mod < 0)
pcnt_w_mod = 0;
while (pcnt_w_mod > ONE_MILLION)
pcnt_w_mod -= ONE_MILLION;
pcnt_r_mod += pcnt_r_1_sec;
if (pcnt_r_mod < 0)
pcnt_r_mod = 0;
while (pcnt_r_mod > ONE_MILLION)
pcnt_r_mod -= ONE_MILLION;
#if PULSE_SOFT // software pulse outputs exist
pcnt_3_mod += pcnt_3_1_sec;
if (pcnt_3_mod < 0)
pcnt_3_mod = 0;
while (pcnt_3_mod > ONE_MILLION)
pcnt_3_mod -= ONE_MILLION;
pcnt_4_mod += pcnt_4_1_sec;
if (pcnt_4_mod < 0)
pcnt_4_mod = 0;
while (pcnt_4_mod > ONE_MILLION)
pcnt_4_mod -= ONE_MILLION;
#endif // software pulse outputs exist
}
}
// Put out a pulse count
static void pcnt_out (int32_t d)
{
LCD_Number (d, 6, 0); // Display up to six digits.
}
// LCD output of pulse count W
static void pcnt_w (void)
{
pcnt_out (pcnt_w_mod); // Display upto six digits.
}
// LCD output of pulse count R
static void pcnt_r (void)
{
pcnt_out (pcnt_r_mod); // Display upto six digits.
}
#if PULSE_SOFT // software pulse outputs exist
// LCD output of pulse count 3
static void pcnt_3 (void)
{
pcnt_out (pcnt_3_mod); // Display upto six digits.
}
// LCD output of pulse count 4
static void pcnt_4 (void)
{
pcnt_out (pcnt_4_mod); // Display upto six digits.
}
#endif // software pulse outputs
// LCD output of the pulse count for the last counting interval
static void pcnt_differential_out (int32_t d)
{
LCD_Number (d, 6, 0); // Display up to six digits.
}
//
static void pcnt_diff_w (void)
{
pcnt_differential_out ((int32_t) pcnt_w_1_sec); // Display upto six digits.
}
static void pcnt_diff_r (void)
{
pcnt_differential_out ((int32_t) pcnt_r_1_sec); // Display upto six digits.
}
#if PULSE_SOFT // software pulse outputs exist
static void pcnt_diff_3 (void)
{
pcnt_differential_out ((int32_t) pcnt_3_1_sec); // Display upto six digits.
}
static void pcnt_diff_4 (void)
{
pcnt_differential_out ((int32_t) pcnt_4_1_sec); // Display upto six digits.
}
#endif // software pulse outputs
// counts pulses for a count of seconds; if seconds is <= 0, counts forever
// The trick is that the "read" logic of the DIO pin can "see" the
// output of the pulse logic.
void pcnt_start (int16_t seconds)
{
EX_IO_INT0 = FALSE; // disable the pulse counting interrupts
EX_IO_INT1 = FALSE;
#if M6520 || TRACE10
// Assure that the pins are not LCD pins
if ((LCDX & LCD_NUM) > 14)
LCDX = 14 | (LCDX & ~LCD_NUM);
// Enable both pulse R and pulse W outputs
DIO |= (DIO_PW | DIO_PV);
// Assure that the pins' DIO logic is for outputs
DIR0 |= USER0_7_ | USER0_6_;
// Set pulse W pin's DIO's external interrupt to "int0 falling"
// Set pulse R pin's DIO's external interrupt to "int1 falling"
DIO_R[3] = 0x76;
// Make the interrupts edge-triggered
IT0 = 1;
IT1 = 1;
// Interrupt priorities are set in main.
// Clear the interrupts
IE0 = 1;
IE1 = 1;
#else
#error unknown configuration
#endif
pcnt_w_cache = 0;
pcnt_r_cache = 0;
pcnt_duration = seconds;
pcnt_continuous = FALSE;
pcnt_enable = TRUE; // start counting when the next second starts
// enable the pulse counting interrupts
EX_IO_INT0 = TRUE;
EX_IO_INT1 = TRUE;
}
// for starting continuous pulse counting at reset
void pcnt_init (void)
{
pcnt_start (1); // 1 second prevents the interrupt from halting..
pcnt_continuous = TRUE; // before this runs.
}
#endif
/***************************************************************************
* History
* $Log: pcnt.c,v $
* Revision 1.26 2006/10/13 00:51:11 tvander
* Removed compile options for 6530, 6515;
* renamed 6511 and 6513 to trace11 and trace13;
* Binary verified unchanged from previous version.
*
* Revision 1.25 2006/03/10 00:07:04 tvander
* Remove unused 64-bit pulse counting variables.
*
* Revision 1.24 2006/09/20 23:59:21 tvander
* Interrupt priorities set in one place, main.c
*
* Revision 1.23 2006/09/18 19:31:31 tvander
* Sets the pulse count interrupt priority from the centralized priority.
*
* Revision 1.22 2006/09/14 00:39:50 tvander
* spaces for tabs
*
* Revision 1.21 2006/09/13 21:39:02 gmikef
* *** empty log message ***
*
* Revision 1.20 2006/09/12 02:45:06 gmikef
* *** empty log message ***
*
* Revision 1.19 2006/09/09 01:14:27 gmikef
* *** empty log message ***
*
* Revision 1.18 2006/07/25 18:07:48 tvander
* Fixed the pulse counting interrupts
*
* Revision 1.17 2006/06/14 02:46:37 tvander
* Faster LCD display.
*
* Revision 1.16 2006/05/18 23:18:52 tvander
* 16K and 32K
* First cut at new requirements.
* 32K 6521 is grossly tested.
* All others have a clean compile with C51 8.02
*
* Revision 1.15 2006/04/12 21:32:38 tvander
* Check in pcnt_start() to see if pulse outputs is valid was inverted.
*
* Revision 1.14 2006/03/08 03:10:57 gmikef
* *** empty log message ***
*
* Revision 1.13 2006/03/08 00:08:02 tvander
* Multiplexed interrupts are in io65xx.c
* Added stubbed interrupt to io65xx.c
* Clean build
*
* Revision 1.12 2006/03/06 03:39:55 Michael T. Fischer
* More 6530 prep.
*
* Revision 1.11 2006/03/03 11:30:29 Michael T. Fischer
* Prep for 6530 LCD, etc.
*
* Revision 1.10 2006/01/16 20:11:28 tvander
* Clean Keil build, all versions
*
* Revision 1.9 2006/01/04 04:47:53 gmikef
* Switched RMS and VA calculations to use floating point. (and Calibration).
*
* Revision 1.9 2005/12/31 00:13:36 gmikef
* Switched to floating point arithmetic. Decided it is high even precision.
* It is faster and no more code space.
*
* Revision 1.7 2005/10/08 04:41:25 tvander
* Fixed priority inversion.
* Rewrote watchdog to work in brownout, but of course it doesn't work.
* Watchdog can now be defeated by clearing watchdog option to 0.
* Reorganized watt hour modules (at last!).
* Disabled reading of STATUS in 6521_cli because the CE's status is always SAG.
* Tested with 6521_CLI; measurements seem to work.
* Fixed other builds.
*
* Revision 1.6 2005/09/22 23:45:18 tvander
* Clean build all models and unit tests, updated copyright to be fore Teridian
*
* Revision 1.5 2005/08/30 18:18:36 gmikef
* *** empty log message ***
*
* Copyright (C) 2005 Teridian Semiconductor Corp. All Rights Reserved. *
* this program is fully protected by the United States copyright *
* laws and is the property of Teridian Semiconductor Corporation. *
***************************************************************************/
/* pcnt.c */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -