Customization Guide for the MAXQ3120 Electricity Meter Reference Design

By: Ben Smith

Abstract: The MAXQ3120 Electricity Meter Reference Design presents many opportunities for customization and enhancement by modifying the source code. This application note describes how to perform the most common customization tasks.

MAXQ3120 Electric Meter Reference Design


The MAXQ3120 Electricity Meter (EM) Reference Design implements a fully operational multifunction, multirate electric meter that complies with all applicable standards worldwide. The Reference Design can form the foundation of an electricity meter suitable for many locales and levels of performance. This document guides the software engineer who wants to customize the code for a particular set of requirements.

Intended Audience

This document presumes a familiarity with the C programming language and with the MAXQ20 microcontroller architecture and assembly language. The reader should also be familiar with the principles behind all-electronic electric meters.


The EM Reference Design was compiled using the IAR Embedded Workbench tools. With one exception, IAR-specific language features have been avoided to ease porting to other toolsets. The exception is in the assembly language files where there are IAR-specific extensions to the standard set of assembly-language pseudo-ops. These IAR-specific extensions inform both the linker about the disposition of the various segments, and the debugger about assumptions that can be made concerning the volatility of certain machine resources. These pseudo-ops can be removed to build the project under other environments.

High-Level Hardware Description

The core of the hardware is a MAXQ3120 microcontroller. The MAXQ3120 contains nearly everything required to implement a multifunction, multirate electricity meter including two precision A/D channels (ADCs), a multiply-accumulate unit (MAC), communication facilities, and a display controller. Only a few external components are required to complete a meter design.

In the Reference Design, two communication channels are included: an infrared channel consisting of a receiver module that decodes a 38kHz carrier and an IR LED directly driven from the microcontroller; a fully isolated RS-485 channel. Nonvolatile memory is provided by a 128kb I2C EEPROM. Meter pulses are available through a visible LED and an isolated optocoupler channel. A pushbutton assists in setting network addresses, and an LCD serves as a display device.

There are several implications to this choice of hardware components. Selecting an I2C external EEPROM means that I2C software must be included in the system; there is no hardware I2C master. Meter-pulse hardware means that the software must be capable of generating extremely precise pulse timing. Two communication ports dictate that the microcontroller's limited resources be shared between two channels.

Overview of the Software System

The software system must track several processes simultaneously. First and most important, the software system must monitor the ADCs, calculate energy usage, and report other ancillary information such as RMS volts and current, power factor, and peak usage. This basic process is critical, and no other process can be permitted to interfere with this most fundamental task. While maintaining the energy monitoring process, the software must also drive the display, monitor two communication ports for activity, monitor a pushbutton for activity, monitor the power line for power-fail events, fulfill requests for information from the external EEPROM, and track scheduled changes in tariff rates.

Choice of Task Management
At first glance, the above potpourri of simultaneous tasks argues strongly for some type of real-time operating system (RTOS) to manage scheduling and resource assignment. On closer inspection, however, there are two good reasons to reject a classic RTOS.

Firstly, the ADC interrupts need immediate service. When the ADC has samples available, the samples must be retrieved in less than 48µs. Furthermore, when a zero crossing is detected, the line-cycle processing routine must have virtually exclusive CPU access to complete its task before the next line cycle. (The line-cycle processing routine consumes between 25% and 30% of the total CPU horsepower.) While an RTOS could be configured to meet these requirements, it is not the most efficient use of resources.

Secondly, the space for storing task context is extremely limited. Most RTOSs want to provide each task with a complete virtual machine in which to operate, and this requires saving context between tasks. With 256 16-bit words of RAM available, only a few tasks would entirely exhaust the available memory.

Consequently, this Reference Design chose a simple task wheel. In this arrangement, tasks are called consecutively, and each task must relinquish control of the CPU when any locking event occurs. A locking event is any event upon which the task must wait: retrieving data from the EEPROM, waiting for a line cycle, or waiting for a character on the communication channel. A locking event also occurs when a current task waits for another task to complete a process before it can complete its job. In any of these locking events, the task must store its state and return to the task wheel. This cooperative multitasking results in a much lighter weight task controller than would otherwise be possible.

Tasks communicate with one another through a set of public data structures that are modified according to a strict set of rules. The most important of these data structures is the message board, a set of bits set by one task to notify a second task when an event has occurred. If a message is received and properly decoded, for example, the Message Decoder task will notify another task (such as the Register Manager) that a message intended for that task has been received, and that the second task must take some action.

Default Task List
Here is the list of default tasks implemented in the Reference Design:

  • DSP: For every line cycle, this routine calculates all power-line parameters and accumulates energy used in the line cycle.
  • Serial Port Driver: Checks both communication channels for activity, and declares the first channel with a received character as the 'active' channel. The channel remains active until the message checker task determines that the message is complete, or when a timeout occurs.
  • Message Checker: Verifies that the incoming character stream meets protocol, and notifies the message decoder when a complete message is received.
  • Message Decoder: Interprets received messages and executes the message request.
  • Asynchronous Event Manager: Performs tasks related to events that do not occur on schedule, such as peak detection and energy accumulation.
  • Schedule Manager: Periodically checks the clock and adjusts the tariff registers on schedule.
  • Display Manager: Updates the LCD according to time and other events.
  • Message Formatter: Prepares replies to messages interpreted by the message decoder task.
  • Message Builder: Receives formatted messages and appends the header and trailer for transmission.
  • Register Manager: Fulfills read and write requests to and from the EEPROM.
  • Time-of-Day Manager: Provides notifications for tasks that operate on a fixed-interval basis.
  • Load Curve Logger: When notified, begins logging usage to the EEPROM for subsequent reporting.

Adding Tasks
A task as defined in the Electricity Meter Reference Design is a single thread of code that performs a function required by the electric meter and returns quickly to the calling function; typically, in only a few milliseconds. Yet most real-world tasks require more time than this to complete. For example, sending a message at any reasonable bit rate will require many cycles. Consequently, most tasks will require a state variable so that a job can be broken into several subtasks.

Once the task has been written, you can add the call to the list of tasks in the spintaskwheel.c file. Note that you can add the task anywhere in the execution stream, and call the task as often as you need. You will see that the DSP task is called very often, and that the SerialPortDriver task is called several times. This is because the DSP task must not be starved for cycles to maintain energy measurement integrity, and the SerialPortDriver task must not be allowed to drop incoming characters.

Finally, test your code. Your new task will be called, along with all the other tasks, as the task wheel makes its rounds.

Global Variables
The lack of a real multitasking OS means that there is no opportunity for real message passing, semaphore, or other mechanisms that programmers assume as normal. Instead, communication is done with the message board described above and a set of global variables that are set and read according to strict rules by various tasks. These global variables are given below:

  • g_CommSystemState: This variable contains a collection of bits that control the communication channels. In particular, each channel has: an active bit, for designating a particular channel as active (and so that characters arriving on the other channel can be discarded); a TBE bit, used to "prime the pump" for an inactive channel; and a data loss bit, set when characters arrive on an inactive channel when the other channel is busy.
  • g_TransmitByte; g_ReceiveByte: Contains the next byte to transmit and the most recently received byte, respectively.
  • g_CommBuffer: A 50-byte array, containing the message that has been received or a message to be transmitted. Notice that there is only a single communications buffer in the system. It is not only shared between channels, but must be shared by the transmit and receive paths as well.
  • g_MeterAddress: A six-byte array that contains the electricity meter's network address. This is read from EEPROM once at initialization, and is kept in RAM.
  • g_MessageFormatterData; g_DispFormatterData; g_ScheduleManagerData; g_AEMData; g_LCLRegData: These registers carry data between the Register Manager and the various tasks. For example, the contents of a register requested for transmission will be placed in g_MessageFormatterData by the Register Manager.
  • g_AEMRegisterNeeded; g_DispFormatterRegRequest; g_RequestScheduleManager; g_LCLRegRequest: These registers contain the register which the indicated task needs to read or write. Note that there is no global address register for the Message Decoder: the Register Manager has the intelligence to decode that directly from the Message Buffer.
  • g_LCDMode: Contains the mode byte for the display. See the section below on Display Customization.
  • g_TariffInEffect: Contains the number of the tariff currently in effect. This function is given its own global variable so that each time energy is accumulated, multiple reads to the EEPROM do not have to be performed to determine where the sample should be stored.
  • g_PW: Contains the number of the password currently in effect for each communication channel.
  • g_irTimer; g_rsTimer: Timers for aging the password on each channel. Once a password is received, it is good for sixty seconds. When one of these passwords expires, the associated nybble in g_PW is zeroed.
  • g_LoadCurveUsage; g_LoadCurvePeak; g_LoadCurveTimeStamp: Variables associated with the load curve logger task. g_LoadCurveUsage accumulates the energy usage that will ultimately be reported to the load curve logger task. Periodically, the load curve logger will write this value to the EEPROM and then clear this variable. g_LoadCurvePeak and g_LoadCurveTimeStamp track the maximum power recorded during one load curve, recording the interval and the time when the peak occurred.
  • AEMState: Contains a collection of variables related to asynchronous events. If a Set Meter Address message is received, the msg_rx flag is set. The variable timer contains the number of seconds before the display reverts to normal when the address set logic has been activated. The DSPState and Register variables track the transfer of register usage information from the DSP logic to the usage-reporting functions. In general, the Register variable cycles through all possible types of usage (real, reactive, positive, negative, etc.)
  • g_new_baud: The protocol specification, DL/T 645, offers a mechanism to change the baud rate for one message only. When the baud-rate change request is received and acknowledged, the next message is processed at the higher baud rate. The baud rate then reverts to the nominal rate (1,200bps in this design.) g_new_baud always holds the baud rate for the next message.
  • g_TransmitDelay: Some RS-485 converters use a fixed delay from the last transmitted character to switch the converter back to receive mode. Consequently, when a host has finished transmitting a request, it can miss the first few characters transmitted by the meter because the RS-485 converter attached to its serial port is still in transmit mode. This variable holds transmission for a fixed delay so the host's RS-485 converter time can switch back to receive mode.
  • current_temp: If included, contains the most recent reading from the DS3231 RTC/temp sensor.

Opportunities for Customization

The Reference Design is based around DL/T 645, Multi-Function Watt-Hour Meter Communication Protocol. This document describes, however, much more than just a communication protocol. DL/T 645 really defines the operation for much of the measurement, scheduling, and reporting that a multifunction meter performs. Consequently, if you want to select another meter protocol, you must replace, or at least significantly modify, the Register Manager and all message functions except the serial-port manager. The details of such modification are beyond the scope of this document.

There are, instead, three customization areas where this document will focus: customization of the display, customization of the register map, and customization of the DSP functions.

Display Customization

The display is completely controlled by the Display Manager. No other task may write to the LCD registers in the MAXQ3120. The Display Manager and its main subroutine, UpdateLCD, are contained in the module DisplayFormatter.c.

If you only want to use a different LCD module with the meter, you may only need to modify UpdateLCD. We will thus cover customization of this module first. If you want to change the type of information that is displayed, you will need to change DisplayManager, and possibly provide additional hooks into the remainder of the meter.

Customizing UpdateLCD
UpdateLCD accepts two parameters: a 32-bit numeric value to be displayed, and an 8-bit annunciator value. The 32-bit display value consists of eight, 4-bit digit values. Thus, UpdateLCD will support up to 8-digit, 7-segment displays. Note that the MAXQ3120 supports up to 112 segments, so this routine could be customized for larger displays.

If you want to use a different display, you will almost certainly modify the LCDFont structure. It is defined as a static const. All structures so defined are compiled and linked to reside in the program space rather than data space.

Assignments in the LCD space follow:

Value Character
0 0
1 1
2 2
3 3
4 4
5 5 (or S)
6 6
7 7
8 8
9 9
A Blank
B -
C n
E t
F Unused (blank)

One critical assumption is made here: each character will fit into one LCD register. If the LCD is structured so that some segments belonging to one digit reside in more than one LCD register, then the entire UpdateLCD routine needs to be modified.

What Is the Presentation Order for Digits?
The routine assumes that the rightmost digit to be displayed is in the low-order four bits of the 32-bit display parameter. This is the most natural order; if you pass '123456' in the parameter, the display will indicate '123456'.

If you have custom annunciators to be lighted when certain items are displayed, they are carried in a separate 8-bit variable. The UpdateLCD routine illuminates these annunciators through a switch structure immediately after the digits are displayed.

Special Circumstances Display
There is a collection of other routines at the end of the displayformatter.c file. These routines handle special display circumstances, such as meter initialization, EEPROM initialization, and program fault (panic). These write directly to the LCD registers, and must be customized for different displays.

Customizing Display Manager
If you want to display items other than energy usage, time and date, you need to modify the Display Manager.

The first section of Display Manager handles the display of meter address-setting information. It only takes effect when the Address Set button is pressed; there should be no reason to modify it.

The remainder of Display Manager takes its cue from a global variable called g_LCDMode. This variable aggregates all the information needed to determine what item to display next in one byte. Its format is shown below:

7 6 5 4 3 2 1 0
Rates to Display
Months to Display
Special Peak High Standard Off-peak Last This Time/Date

Total energy accumulation over the life of the meter is always displayed, then any items specified in the g_LCDMode byte. In the current implementation, this variable is fixed at 1—only time and date are displayed in addition to the total energy accumulation.

Control Variables
Display Manager is controlled by a state variable called disp, which has two elements: Item and State. As is implied by the name, disp.State maintains the current state of the display control machine, and disp.Item tracks which piece of information is to be displayed:

Value disp.Item disp.State
0 Total Energy Usage Select next item to display
1 Time of day Begin retrieving next display item
2 Date Request usage beginning value
3 Total use this month Retrieve beginning value
4 Peak-rate use this month Request places following decimal
5 High-rate use this month Retrieve decimal places
6 Standard-rate use this month Unused
7 Off-peak use this month Unused
8 Total use last month Request display period
9 Peak-rate use last month Retrieve display period
A High-rate use last month Unused
B Standard-rate use last month Unused
C Off-peak use last month Unused
D Unused Update LCD with new data
E Unused Wait for display timeout
F Unused Unused

You have two options to customize this routine. You can elect to change assignments for disp.Item and change the order in which they are selected in the routine, or you can elect to replace the routine entirely. This latter choice may be the better option. It would clearly be more flexible to have an item selection structure that assigns either a separate bit for each particle that might be displayed, or a list with references to displayable items. The above structure was chosen because it has the smallest RAM footprint.

Adding Registers

DL/T 645 specifies a large number of registers that control various aspects of meter operation. Each register is designated by a 16-bit register number. In the Reference Design, a number of registers were added to govern various aspects of meter operation; these registers are documented in the code. The discussion here gives you the information needed either to extend the register map to retrieve additional information from the meter or to control new aspects of meter operation.

How Register Manager Works
Register Manager is the one task that has difficulty obeying the rule that tasks must not suspend normal task-wheel operation. This is because the Register Manager has the exclusive responsibility for reading and writing the EEPROM, and EEPROM writes take a (comparatively) long time: a few milliseconds. Since the DSP routines must be given processor time every 20ms (every 16.7ms in a 60Hz environment), Register Manager cannot be allowed to suspend the system for some tens of milliseconds while the EEPROM write-cycle completes.

An obvious way to resolve the EEPROM write timing issue would be to put the I2C routines on an interrupt. In this way the Register Manager could start an EEPROM transaction and then return to main(); each time it is subsequently called, the Register Manager could check the EEPROM subsystem status to see if it has completed its tasks. The problem with this approach is that the ADC period is so short that the ADC interrupt service routine requires exclusive access to the interrupt subsystem. Consequently, some other mechanism must be provided.

The solution was a global flag: EEPROMOpPending. When this flag is clear, the task wheel is essentially an infinite loop, repeatedly calling every task in the system. When the flag is set, however, the task wheel, when called, executes exactly once and returns without calling the Register Manager. How does this help?

When the Register Manager needs to perform a function that requires a long time to complete, it starts the function and then polls the function to determine when it is complete. During the polling, the Register Manager sets EEPROMOpPending and recursively calls the task wheel. A practical example is given in the code below:

01: uint8 ReadEEPROM(uint16 Address, uint8 Length, uint8 *pData)
02: {
03:   int i;
04:   g_MessageBoard.EEPROMOpPending = 1;
05:   for(i=0; i<Length; i++)
06:   {
07:     if(i>0)SpinTaskWheel();
08:     eeprom_address = Address++;
09:     while(eeprom_read_byte())
10:       SpinTaskWheel();
11:     *pData++ = eeprom_data;
12:   } // for
13:   g_MessageBoard.EEPROMOpPending = 0;
14:   return 1;
15: }

In line 4 above, the EEPROMOpPending flag is set. In lines 7 and 10, SpinTaskWheel is called. When called with the EEPROM flag set, the SpinTaskWheel function runs once and returns without calling the Register Manager. In this way, the rest of the meter continues to run even when the Register Manager is stalled waiting for the EEPROM to complete an operation.

Who Knows About Registers?
Only two tasks know about register numbers: the Register Manager and the Message Decoder. Of these routines, as a general rule only the Register Manager will need to be modified. The Message Decoder recognizes registers involved with password management and other supervisory functions that must be intercepted before normal processing rules are applied. Therefore, it is only necessary to become familiar with the Register Manager to implement your own registers.

Three Types of Registers
In general, there are three types of registers: read-only, read-write, and read-write with side effects. An example of a read-only register is B611, RMS Volts, phase A. A write to this register from the host would be meaningless; it would, in fact, be discarded if received by the meter. Also, many read-only registers do not reside in EEPROM: often, they are calculated on-the-fly and reported only on demand.

An example of a read-write register is C032, Meter Number. Writing this value has no effect on meter operation, but can be retrieved at any time. Finally, an example of a read-write register with side effects is C030, Meter Constant, active. When this register is written, the Register Manager must update not only the EEPROM, but also the value used by the DSP routines for the meter constant.

Who Asks for Registers?
The following table shows the tasks that can request registers.

Task Read Write
Schedule Manager
Load Curve Logger
Display Formatter
Asynchronous Event Manager
Message Decoder

In general, you are mainly concerned with adding registers that can be accessed from the Message Decoder. You can add registers for display (or for any other task, but as a rule, you are mostly concerned with registers that will be retrieved through the communications ports.

Read-Write Registers
The first case to consider is the storage and retrieval of read-write registers that have no side effects. To add a register that will be stored in the EEPROM, you must make additions in two places: the MAXQ3120RD.h file, and the ProcessRegisterNumber routine in the Register Manager.

MAXQ3120RD.h contains a typedef called EEPROM_DATA. This definition is never actually instantiated; instead, it serves only as a template that defines how data is stored in the EEPROM. Below the EEPROM_DATA definition, two macros are defined to return the offset of an item in the structure, and to return the size of a member of the structure. The first step in defining a new register is to add an entry in this structure (at the end is best) to allocate EEPROM storage for the register.

The next step is to define the register number. To do this, edit the RegParmTable structure defined in the Register Manager. This table contains an entry for each register defined in the meter, in numerical order. Each entry consists of:

  • The register number, as a 16-bit unsigned value.
  • The number of physical data elements that must be retrieved to calculate the actual register value. For example, register 9110 requests total reactive energy usage in the current month in the positive direction. This is the sum of two energy accumulators: quadrant 1 usage and quadrant 4 usage. Consequently, the number of physical elements is two. The Register Manager must retrieve the designated element (CurrentQuadrant1AccumTariff) and the next element (CurrentQuadrant4AccumTariff), and add them to obtain the requested information.
  • The length of each element, in bytes.
  • The type of data stored: INT_REG, if the register contains binary data to be interpreted as an integer; BCD_REG, if the register contains BCD data that does not need to be further translated before transmission; or MDH_REG, if the register contains date information (month:day:hour).
  • The byte offset of the data in the EEPROM.

To save processing time, the ProcessRegisterNumber routine performs a binary search for the register entries. It is, therefore, critical that the table remain sorted. If the register table becomes unsorted, the results can be unpredictable.

Once this table has been updated, the new register can be read and written from the communication channel. What the meter actually does with that information is the subject of the next section.

Read-Write Registers with Side Effect
Suppose you want to have a write event trigger another effect. To do this you must have Register Manager either send a message to that effect, or update a RAM location that performs the desired function. To see an example of this, search for C030 in Register Manager and you will find this bit of code:

                case 0xC030:    // Meter constant, real
                  action_value = 0;
                  for(i=4; i>1; i--)
                    action_value *= 100;
                    action_value += (g_CommBuffer.Message[i] & 0xf) +
                    (g_CommBuffer.Message[i] >> 4) * 10;
                  set_E_pulse(action_value); // this will set E_pulse

This code runs after the register data has been updated in the EEPROM. In this case, the host requested a change to the meter constant. After the meter-constant register in EEPROM is updated, the millisecond value passed in the communications buffer is converted to internal meter units and sent to the DSP routine through the set_E_pulse function.

Read-Only registers
Some read-only registers are simply read from EEPROM (energy usage, for example) and updated by other processes in the meter. However, other read-only registers (RMS volts, for example) have no EEPROM representation. It would make no sense and would quickly wear out the EEPROM if these values were continuously updated! You can find these registers as comments in the table in ProcessRegisterNumber tagged with the note, "not stored in EEPROM."

These registers are handled by the GetSpecialRegister routine in Register Manager. For each read-only register, there is a case in the switch statement in the routine. For example:

    case 0xB611:// voltage (phase A)
      g_MessageBoard.EEPROMOpPending = 1;
      while(!(DSP_CTRL & 0x20))
      *value = Get_RMS() / 1000;
      g_MessageBoard.EEPROMOpPending = 0;
      *size = 2;

This example illustrates the important fact that no task is permitted suspend the task wheel. The first statement of the case sets the EEPROMOpPending flag in the message board. It then requests that the DSP functions calculate the RMS voltage value, and the recursively calls the task wheel while the DSP function is busy. When the EEPROMOpPending flag is set, the task wheel executes only one pass without calling the Register Manager, thus preventing infinite recursion. Once the DSP function completes, the RMS value is retrieved and the EEPROMOpPending flag is cleared.

Note that for this type of read-only register, it is not necessary to reserve space in the EEPROM by making an entry in the MAXQ3120RD.h file. Nor is it necessary to add an entry to the ProcessRegisterNumber table. The main register manager routine always calls GetSpecialRegister before processing EEPROM-based registers.

Customizing the DSP Routines

The reference-design DSP routines are a set of assembly-language modules that handle the signal flow from the ADCs through to pulse generation and voltage, current, power, and energy reporting. Most of these routines will never need modification, but you may wish to:
  • Use a different current or voltage transducer that requires a different gain factor.
  • Change the way the system generates meter pulses.
  • Change front-end filtering.

The next section describes at a high level how the DSP routines work and what elements you can safely change.

Note: The DSP module is publicly distributed as a pre-compiled object file. Assembly language source code is available only under a nondisclosure agreement (NDA). Please contact Dallas Semiconductor/Maxim for more information.

The DSP routines use the lowest part of the RAM area. Search for "Data Memory Map" in the DSP module to see the set of RAM variables used by the DSP routines. The first two bytes are a set of bits that control the operation of the DSP functions.

Two constants can be adjusted to configure the full-scale readings for the voltage and current channels. These are W_V_Scale and W_I_Scale, respectively. By default, these constants are set to 400V and 250A. The voltage is set to a level unlikely to be exceeded under normal circumstances (over 280VRMS), and the current is set to be consistent with the probable shunt values for the meter (typically, 250µΩ to 500µΩ.)

Interface Routines
Several built-in routines return values that can be directly used by user routines. If possible, you should use these routines to interface to the DSP functions rather than attempting to interface directly to the internal variables used by the DSP functions.

  • Get_and_Clear_Usage: This is the main routine that the C code uses to retrieve energy accumulation. In general, the DSP routines will notify the Asynchronous Event Manager that energy needs to be accumulated; this routine can, however, be called at any time to obtain an accurate, up-to-the-moment energy reading. Note that the IAR compiler will pass the function argument in A[0] automatically, and will return the result in A[0] as well.
  • Get_Frequency: Returns the line frequency in 0.1mHz steps. Note that by default this subroutine is conditioned out of the load; frequency is not required by the DL/T 645 standard.
  • Get_Power_Factor: Returns the power factor of energy delivered to the load.
  • Get_Power: Returns either real or reactive power, depending on the argument.
  • Get_MaxD: Returns the maximum demand (power) recorded by the meter since the last time the function was called.
  • Request_RMS: Instructs the DSP to accumulate the RMS current or voltage, depending on the parameter.
  • Get_RMS: Returns the RMS value most recently requested.
  • set_E_pulse: Accepts a meter constant, and sets the appropriate DSP variables to enforce this meter constant.

Interrupt Service Routine
The Reference Design enables only one interrupt: the AFE interrupt that occurs when a new set of samples is available on the ADCs. Because the ADC sample period is 48µs, it is essential that the interrupt service routine concludes its work quickly and returns to the mainline code-there are only 384 instruction cycles between interrupts!

The interrupt service routine performs the following functions:

  • Pulse output generation: If a pulse is called for, start it. If a pulse is in progress, decrement duration counter and conclude pulse if count goes to zero.
  • Accumulate integral sums: Accumulate the newest energy sample to all the appropriate registers.
  • Accumulate RMS values: If requested, accumulate I2 or V2.
  • Check below-threshold voltage: If voltage is below threshold, increment a counter.
  • Zero crossing detection: If the voltage signal crosses zero in the positive direction, set a flag.