HLW8110: the target use case
Single-phase AC energy metering from the datasheet's typical-application circuit: the load current is sensed through a low-value shunt on the IAP/IAN inputs, the mains voltage through a resistor divider into VP, and the HLW8110 computes VRMS, IRMS, active power, active energy, power factor and line frequency in hardware, reporting results over UART (or SPI) to a host MCU. Stated accuracy: <0.1% active-energy error over a 5000:1 dynamic range — but only after you load calibration coefficients, which the part forgets on every reset.
HLW8110: numbers that actually constrain the design
Before the register sequence, these are the figures engineers most often misread:
- UART baud is fixed at 9600 on the HLW8110. The mode-select table lists 38400/19200/9600 depending on SCLK/SCSN pin levels when SPIEN=0, but the 8110 is locked to 9600. If your host isn't talking, check the baud before anything else. SPI is selected by SPIEN=1.
- UART frames are 9 bits: 8 data + 1 check bit. Multi-byte registers go high byte first. Every UART transaction is
0xA5preamble, command byte, data, checksum, where the command byte isbit7 | REG_ADR[6:0](bit7=1 write, bit7=0 read) and the checksum is~(0xA5 + command + data...)masked to 8 bits. - Every register returns to factory defaults on every reset. There is no non-volatile storage. The
RSTflag in SysStatus exists specifically so the host knows to re-request the calibration table after each boot. - Writes are refused until write-enable is opened with
0xEA 0xE5, and closed again with0xEA 0xDC. This applies to the interrupt-enable register IE too — the datasheet states explicitly that write-enable must be opened before configuring IE. - Full-scale input depends on PGA gain (Table 7, Vref = 1.25 V): gain 1 → 800 mV peak, 2 → 400 mV, 4 → 200 mV, 8 → 100 mV, 16 → 50 mV peak. The RMS limit is (800 mV/PGA)/√2. Size your shunt/divider so the peak input at maximum load stays under the limit for the gain you pick. The default divider is ~1 kΩ/(1 MΩ + 1 kΩ); the shunt is normalized as K1 · 1 mΩ, where K1 is a magnification/reduction multiple relative to a 1 mΩ base — e.g. a real 2 mΩ shunt gives K1=2, a 0.5 mΩ shunt gives K1=0.5. (The divider ratio is likewise normalized as K2 · 1 kΩ/1 MΩ.)
- Comparator and Channel-B current measurement are mutually exclusive (EMUCON bit 12
comp_off). Choose B-channel metering and you lose the comparator/overload/zero-cross functions.
HLW8110: startup sequence
The only ordering the datasheet enforces is unlock → select channel → write calibration → relock, with an optional software reset first. Register reads/writes use UART frames of the form 0xA5 | command | data... | checksum; the special commands (0xEA 0x96 reset, 0xEA 0xE5 write-enable, 0xEA 0xDC relock, 0xEA 0x5A/0xEA 0xA5 channel select) are a distinct two-byte opcode form — they are not 0xA5-framed and carry no checksum.
Required steps
-
(Optional but recommended) Software reset — send
0xEA 0x96. The chip resets within ~2 system clocks and all registers return to initial values; theRSTflag in SysStatus (0x43H) is set. This guarantees a known state on a re-boot of the host.Wait: reset completes after 2 system clocks. The built-in clock MCLK is typ 3.579 MHz (3.507–3.65 MHz per Table 5); the datasheet's frequency-register text uses 3.579545 MHz nominal, so 2 clocks ≈ 0.56 µs (0.55–0.57 µs across the clock tolerance band). This is the only between-writes wait the datasheet states.
-
Read SysStatus (0x43H) and confirm
RST=1. TheRSTflag is cleared by reading it. This both verifies you are talking to the part and marks that a fresh calibration load is required. -
Open write-enable — send
0xEA 0xE5. Register writes are refused until this is issued. -
Select the metering current channel — send
0xEA 0x5Afor Channel A (the built-in current-sense path; the typical design's choice) or0xEA 0xA5for Channel B (external amplifier). This command selects which current signal feeds apparent power, power factor, phase angle, instantaneous apparent power, and active-power overload. If you never issue it, or pick the channel your transducer isn't wired to, those results report the wrong channel. -
Write the system control register SYSICON @ 0x00H (default 0x0A04H) to set the PGA gains:
PGAIA[2:0]bits 2–0 (current channel A),PGAU[2:0]bits 5–3 (voltage),PGAIB[2:0]bits 8–6 (current channel B). Gain decode for all three:000=1,001=2,010=4,011=8,1XX=16. The default 0x0A04H decodes to PGAIA=16, PGAU=1, PGAIB=1. -
Write EMUCON @ 0x01H (default 0000H) if you need to change comparator mode (
comp_off, bit 12: 0 = comparator active, 1 = comparator closed) or the temperature sensor (tensor_en, bit 13, plusTsensor_Step[1:0], bits 15–14). For basic single-phase metering with the comparator kept, leave at default. Honest caveat: the middle of the EMUCON/EMUCON2 (0x13H, default 0001H) bitfield tables were not fully readable from the datasheet text available, so I cannot cite a complete canned word for every EMUCON configuration — verify the remaining bit offsets against Table 11 in your copy of the datasheet before writing a non-default value. -
Load calibration coefficients. This is the step that makes the measurement accurate. Confirmed registers:
Register Addr Default Format PAGain(active-power gain, ch A + voltage)0x05H 0x0000H 16-bit two's complement, bit15 = sign; calibration range ±100% RmsIAOS(current-A RMS offset)0x0EH 0x0000H 16-bit two's complement, bit15 = sign The datasheet's register survey also lists
PBGain(0x06H),PSGain(0x07H),PAOS(0x0AH),PBOS(0x0BH),RmsIBOS(0x0FH) in the same calibration block. There is no fixed working value:PAGainis computed asINT[2^16 + PAGain·2^15]from your measured gain error. For the RMS offset: with the current input zeroed, readRmsIArepeatedly (~3.4 Hz update rate), average, then negate (bitwise-invert + 1) to get the offset code. Note there is no on-chip RMS gain register — RMS gain scaling (KiA = Ib / RmsIA_reg,Ku = Ub / RmsU_reg) is done on the host MCU after reading the raw RMS registers. -
(If using interrupts) Configure IE @ 0x40H — still inside the write-enable window. The datasheet states write-enable must be opened before configuring IE. Setting an enable bit and having that event occur drives the IRQ_N (INT1/INT2) pin low.
-
Close write-enable — send
0xEA 0xDC. All further writes are refused until the next0xEA 0xE5. If you lock before writing calibration, no coefficient lands — this is a common failure.
Design choices and their stated trade-offs
- PGA gain (SYSICON): higher gain gives more resolution for small signals but clips at a proportionally lower input (Table 7); lower gain accepts a large shunt/CT signal but wastes resolution at small loads. Match the gain so full-scale aligns with your maximum expected current.
- Channel A vs B: Channel B as the metering current means giving up the comparator module (overload/zero-cross) — the datasheet states "Comparator function and B-channel current measurement can only be one of two choices." Staying on Channel A keeps the comparator but forgoes the second current input.
- Temperature sensor (
tensor_en+Tsensor_Step[1:0]): the temperature module is measured through Channel B's ADC in four stepped acquisitions (00..11) that are averaged for the result. Enabling it consumes B-channel ADC timeslots and adds a measurement procedure you must sequence; leave it closed if you don't need on-chip temperature. - Calibration effort vs accuracy class: the coefficients are computed from a measured gain/offset error during a calibration run; skipping them leaves the part reading default (uncalibrated) data.
HLW8110: required waits and timing
-
After
0xEA 0x96reset: wait ≥2 system clocks (~0.56 µs at the 3.579545 MHz nominal clock) before the next command. -
No per-write settle delay is stated. The datasheet does not program a hard wait between ordinary register writes, and I found no datasheet-stated "data-ready" poll between two consecutive writes. Do not invent one.
-
Results refresh on fixed cadences — time your reads to these, not to your writes:
Data Cadence Period RMS registers (RmsIA/RmsIB/RmsU) selectable 3.4 / 6.8 / 13.6 / 27.2 Hz ~294 / 147 / 74 / 37 ms Instantaneous power / RMS-instantaneous 6991 Hz ~143 µs Voltage-frequency measured value 0.64 s @ 50 Hz; 0.533 s @ 60 Hz — After changing a calibration or gain coefficient, the corrected result only appears on the next cadence tick — up to ~294 ms at 3.4 Hz. Reading instantly returns the pre-change value.
HLW8110: reading results back
The metering results live in the metrological parameter registers. Representative: RmsIA (0x24H), RmsIB (0x25H), RmsU (0x26H) — each 24-bit signed, MSB 0 = valid data, MSB 1 = treat as zero. Active power, apparent power, energy and power factor are the same register class, read over the UART read frame (0xA5, command byte with bit7=0, then receive data high byte first, then check byte).
Two runtime patterns:
- Polling (usual starting point): poll at or a bit faster than the selected RMS update rate. Clean hooks are the
DUPDIF(data-update, bit 0) andINSTANIF(instantaneous-value update, bit 6) flags in the Interrupt Status Register IF @ 0x41H — poll those to time your read to a fresh sample. Other IF bits include bit 7OIAIF(current-A overcurrent), bit 3PEAOIF(channel-A active-power register overflow), bit 4PEBOIF(channel-B overflow), bit 1PAFIF(PFA pulse-output event), and bit 2PFBIF(PFB pulse-output event). - Interrupt: with IE configured (at init, inside the write-enable window), the event drives IRQ_N low; the ISR reads IF to identify the event, reads the data registers, and re-arms.
Read-to-clear discipline: flags such as RST are cleared by being read. The standard loop is read IF/status → act → the read itself clears the pending state → poll again next interval.
HLW8110: verifying the configuration took
- Boot state: read SysStatus (0x43H) right after power-up; expect
RST=1, which the read clears. This confirms you are talking to a freshly-booted, uncalibrated part. - Every write: read the SPI Write Check Register WDATA @ 0x45H (read-only, default 0000H) and confirm it equals the 2-byte value you wrote. A mismatch means the write was refused — usually write-enable was closed, or IE was written before unlock.
- Every read: the RDATA register holds the last read 4 bytes and can be used to verify the read data returned over the bus.
- Metering config committed: read the Meter Status register (EMUStatus). Reading it is one of the triggers that restarts the check-and-calculation, and the datasheet notes two system clock cycles are required for the checksum calculation — so wait ≥2 system clocks (~0.56 µs) after reading EMUStatus before reading the result registers. Then confirm the RMS register MSBs are 0 (valid data).
HLW8110: troubleshooting
- Writes silently do nothing: write-enable (
0xEA 0xE5) was not issued, or you sent0xEA 0xDCbefore the calibration writes. WDATA read-back catches this immediately. - IE configuration has no effect: IE was written outside the write-enable window — the datasheet requires the unlock first.
- Metering "works" but PF/phase/apparent-power are garbage: the
0xEA 0x5A/0xEA 0xA5channel-select was never issued, or selects the channel your transducer is not wired to. - Comparator/overload/zero-cross dead: Channel-B current measurement is enabled (
comp_off=1); the two functions are mutually exclusive by design. - Results drift back to uncalibrated values after a brownout or host-initiated reset: every reset restores factory defaults. Check that the
RSTflag path fired and the calibration table was re-loaded. TheRSTflag is cleared by reading it, so a read-twice-and-ignore pattern will not behave as expected. - Host sees nothing at all: the HLW8110's UART baud is fixed at 9600; verify the host is at 9600, 9-bit frames (8 data + check bit), high byte first, and that the checksum is
~(0xA5 + command + data...)masked to 8 bits. - Stale-looking RMS after a calibration write: the register only refreshes on the next cadence tick (up to ~294 ms at 3.4 Hz). Read after the period, not instantly.
Part page: HLW8110.