embedded_devices/devices/belling/bl0942/
registers.rs

1use embedded_interfaces::codegen::interface_objects;
2use uom::si::f64::Frequency as QuantFrequency;
3use uom::si::frequency::hertz;
4
5// BL0942 uses a custom SPI protocol - you'll need to implement a custom codec
6// For now, using UnsupportedCodec as placeholder
7pub type BL0942I2cCodec = embedded_interfaces::registers::i2c::codecs::unsupported_codec::UnsupportedCodec<
8    crate::devices::belling::ChecksumError,
9>;
10pub type BL0942SpiCodec = crate::devices::belling::BellingSpiCodec<0x58, 0xa8>;
11
12interface_objects! {
13    register_defaults {
14        codec_error = crate::devices::belling::ChecksumError,
15        i2c_codec = BL0942I2cCodec,
16        spi_codec = BL0942SpiCodec,
17    }
18
19    register_devices [ super::BL0942 ]
20
21    // ========================================================================
22    // Enumerations
23    // ========================================================================
24
25    /// Current channel gain selection
26    #[allow(non_camel_case_types)]
27    enum CurrentGain: u8{2} {
28        /// Gain = 1x
29        0b00 X_1,
30        /// Gain = 4x
31        0b01 X_4,
32        /// Gain = 16x
33        0b10 X_16,
34        /// Gain = 24x
35        0b11 X_24,
36    }
37
38    /// UART baud rate selection
39    enum UartBaudRate: u8{2} {
40        /// External pin SCLK_BPS defines 4800/9600 bps
41        0b00 ExternalPin,
42        /// Reserved
43        0b01 Reserved,
44        /// 19200 bps
45        0b10 Baud19200,
46        /// 38400 bps
47        0b11 Baud38400,
48    }
49
50    /// Output function selection for CF1/CF2/ZX pins
51    enum OutputFunction: u8{2} {
52        /// Active energy pulse output (CF)
53        0b00 ActiveEnergyPulse,
54        /// Over-current logic output (O_C)
55        0b01 OverCurrent,
56        /// Zero-cross voltage (ZX_V)
57        0b10 ZeroCrossVoltage,
58        /// Zero-cross current (ZX_I)
59        0b11 ZeroCrossCurrent,
60    }
61
62    /// Line cycle selection for fast RMS measurement
63    enum FastRmsCycles: u8{3} {
64        /// 0.5 cycles
65        0b000 HalfCycle,
66        /// 1 cycle
67        0b001 OneCycle,
68        /// 2 cycles
69        0b010 TwoCycles,
70        /// 4 cycles
71        0b011 FourCycles,
72        /// 8 cycles
73        0b100 EightCycles,
74        /// 8 cycles
75        0b101 EightCyclesAlt1,
76        /// 8 cycles
77        0b110 EightCyclesAlt2,
78        /// 8 cycles
79        0b111 EightCyclesAlt3,
80    }
81
82    /// Number of line cycles until frequency measurement is updated
83    #[allow(non_camel_case_types)]
84    enum FrequencyUpdateCycles: u8{2} {
85        /// 2 cycles
86        0b00 N_2,
87        /// 4 cycles
88        0b01 N_4,
89        /// 8 cycles
90        0b10 N_8,
91        /// 16 cycles
92        0b11 N_16,
93    }
94
95    /// Power flow direction
96    enum PowerDirection: u8{1} {
97        /// Forward power flow
98        0 Forward,
99        /// Reverse power flow
100        1 Reverse,
101    }
102
103    /// Anti-creep status
104    enum CreepStatus: u8{1} {
105        /// Anti-creep mechanism inactive
106        0 Inactive,
107        /// Anti-creep mechanism active
108        1 Active,
109    }
110
111    /// RMS update rate selection
112    #[allow(non_camel_case_types)]
113    enum RmsUpdateRate: u8{1} {
114        /// 400ms update period
115        0 T_400ms,
116        /// 800ms update period
117        1 T_800ms,
118    }
119
120    /// Fast RMS calculation mode
121    enum FastRmsMode: u8{1} {
122        /// Full-wave RMS calculation
123        0 FullWave,
124        /// AC wave RMS calculation
125        1 AcWave,
126    }
127
128    /// AC line frequency selection
129    #[allow(non_camel_case_types)]
130    enum AcFrequency: u8{1} {
131        /// 50 Hz operation
132        0 F_50Hz,
133        /// 60 Hz operation
134        1 F_60Hz,
135    }
136
137    /// CF_CNT accumulation mode
138    enum AccumulationMode: u8{1} {
139        /// Signed accumulation (can be negative)
140        0 Signed,
141        /// Absolute value accumulation
142        1 Absolute,
143    }
144
145    /// Reset magic
146    enum ResetMagic: u32{24} {
147        /// Magic to reset the chip
148        0x5a5a5a Reset,
149        /// Any other value is ignored
150        _ Ignored,
151    }
152
153    /// Write protection key
154    enum ProtectionKey: u8 {
155        /// User mode write is allowed
156        0x55 Unlocked,
157        /// User-mode writes are locked
158        _ Locked,
159    }
160
161    // ========================================================================
162    // Read-Only Registers
163    // ========================================================================
164
165    /// Current waveform data.
166    ///
167    /// Updated at 7.8 kSPS, 20-bit signed value
168    register CurrentWaveform(addr = 0x01, mode = r, size = 3) {
169        _: u8{4},
170        /// Current waveform data.
171        /// Updated at 7.8 kSPS sample rate
172        ///
173        /// Note: This is instantaneous waveform data, not suitable for direct unit conversion
174        value: i32{20} = 0x00000,
175    }
176
177    /// Voltage waveform data.
178    ///
179    /// Updated at 7.8 kSPS, 20-bit signed value
180    register VoltageWaveform(addr = 0x02, mode = r, size = 3) {
181        /// Reserved bits.
182        _: u8{4},
183        /// Voltage waveform data.
184        /// Updated at 7.8 kSPS sample rate
185        ///
186        /// Note: This is instantaneous waveform data, not suitable for direct unit conversion
187        value: i32{20} = 0x00000,
188    }
189
190    /// Current RMS.
191    register CurrentRms(addr = 0x03, mode = r, size = 3) {
192        /// Current RMS value.
193        /// Update frequency depends on the selected update rate in [`UserMode`]
194        value: u32{24} = 0,
195    }
196
197    /// Voltage RMS.
198    register VoltageRms(addr = 0x04, mode = r, size = 3) {
199        /// Voltage RMS value.
200        /// Update frequency depends on the selected update rate in [`UserMode`]
201        value: u32{24} = 0,
202    }
203
204    /// Current fast RMS.
205    register CurrentFastRms(addr = 0x05, mode = r, size = 3) {
206        /// Fast RMS current value.
207        /// Updated at faster rate for over-current detection
208        value: u32{24} = 0,
209    }
210
211    /// Active power.
212    ///
213    /// Equation: WATT = 3537 * I(A) * V(V) * cos(φ) / Vref²
214    /// Conversion: P(W) = WATT * Vref² / 3537
215    /// Signed value, negative indicates reverse power flow
216    register ActivePower(addr = 0x06, mode = r, size = 3) {
217        /// Active power value.
218        /// Negative value indicates reverse power flow
219        value: i32{24} = 0,
220    }
221
222    /// Active energy pulse counter.
223    ///
224    /// Integrates active power over time.
225    register EnergyCounter(addr = 0x07, mode = r, size = 3) {
226        /// Active energy pulse counter.
227        /// Accumulates energy pulses, can be auto-cleared after read depending on user mode
228        /// register configuration. Unit conversion requires calibration based on CF pulse configuration.
229        ///
230        /// Value in active power register determines pulse time as WATT = (1638.4 * 256) / t_CF
231        value: u32{24} = 0,
232    }
233
234    /// Line voltage frequency.
235    ///
236    /// The FREQ register is updated once every FREQ_CYC cycle.
237    register Frequency(addr = 0x08, mode = r, size = 3) {
238        /// Reserved bits.
239        _: u8{8},
240        /// Frequency measurement
241        raw_frequency: u16{16} = 0x4e20 => {
242            quantity: QuantFrequency,
243            unit: hertz,
244            from_raw: |x| 1_000_000f64 / (x as f64),
245            into_raw: |x| 1_000_000f64 / x,
246        },
247    }
248
249    /// System status register
250    register Status(addr = 0x09, mode = r, size = 3) {
251        /// Reserved bits.
252        _: u16{14},
253        /// Voltage zero-cross detection validity
254        voltage_zx_invalid: bool = false,
255        /// Current zero-cross detection validity
256        current_zx_invalid: bool = false,
257        /// Reserved bits
258        _: u8{6},
259        /// Anti-creep mechanism status
260        creep_status: CreepStatus = CreepStatus::Inactive,
261        /// Power flow direction
262        power_direction: PowerDirection = PowerDirection::Forward,
263    }
264
265    // ========================================================================
266    // Read-Write Registers
267    // ========================================================================
268
269    /// Current RMS offset.
270    ///
271    /// Used to compensate for DC offset in current measurement
272    register CurrentRmsOffset(addr = 0x12, mode = rw, size = 3) {
273        /// Reserved bits.
274        _: u16{16},
275        /// Current RMS offset value.
276        /// Applies offset correction to measured current RMS
277        value: u8 = 0x00,
278    }
279
280    /// Active power no-load threshold.
281    ///
282    /// Anti-creep threshold to suppress noise when no load
283    /// Equation: WA_CREEP = WATT * (256 / 3125)
284    register NoLoadThreshold(addr = 0x14, mode = rw, size = 3) {
285        /// Reserved bits.
286        _: u16{16},
287        /// No-load threshold value.
288        /// When active power < threshold, forces output to zero
289        threshold: u8 = 0x0b,
290    }
291
292    /// Current fast RMS threshold.
293    ///
294    /// Threshold for over-current detection
295    register OverCurrentThreshold(addr = 0x15, mode = rw, size = 3) {
296        /// Reserved bits.
297        _: u8{8},
298        /// Over-current threshold value.
299        /// Compares against upper 16 bits of I_FAST_RMS
300        threshold: u16 = 0xffff,
301    }
302
303    /// Line cycle for fast RMS measurement.
304    register FastRmsCycleConfig(addr = 0x16, mode = rw, size = 3) {
305        /// Reserved bits
306        _: u32{21},
307        /// Number of line cycles for fast RMS averaging
308        cycles: FastRmsCycles = FastRmsCycles::OneCycle,
309    }
310
311    /// Line cycle for frequency measurement.
312    register FreqMeasurementConfig(addr = 0x17, mode = rw, size = 3) {
313        /// Reserved bits
314        _: u32{22},
315        /// Number of line cycles until frequency measurement is updated
316        update_cycles: FrequencyUpdateCycles = FrequencyUpdateCycles::N_16,
317    }
318
319    /// Logic output configuration.
320    ///
321    /// Configures what signals are output on CF1, CF2, and ZX pins
322    register OutputConfig(addr = 0x18, mode = rw, size = 3) {
323        /// Reserved bits
324        _: u32{18},
325        /// ZX pin output function selection
326        zx_function: OutputFunction = OutputFunction::ZeroCrossVoltage,
327        /// CF2 pin output function selection
328        cf2_function: OutputFunction = OutputFunction::OverCurrent,
329        /// CF1 pin output function selection
330        cf1_function: OutputFunction = OutputFunction::ActiveEnergyPulse,
331    }
332
333    /// User mode selection.
334    register UserMode(addr = 0x19, mode = rw, size = 3) {
335        /// Reserved bits
336        _: u16{14},
337        /// UART baud rate selection
338        uart_baud_rate: UartBaudRate = UartBaudRate::ExternalPin,
339        /// CF_CNT accumulation mode
340        accumulation_mode: AccumulationMode = AccumulationMode::Absolute,
341        /// Auto-clear CF_CNT after read
342        cf_cnt_auto_clear: bool = false,
343        /// AC frequency selection
344        ac_frequency: AcFrequency = AcFrequency::F_50Hz,
345        /// Fast RMS calculation mode
346        fast_rms_mode: FastRmsMode = FastRmsMode::FullWave,
347        /// RMS update rate selection
348        rms_update_rate: RmsUpdateRate = RmsUpdateRate::T_400ms,
349        /// Enables active energy pulse output
350        cf_enable: bool = true,
351        /// Reserved bits
352        _: u8{2} = 0b11,
353    }
354
355    /// Current channel gain.
356    register CurrentGainConfig(addr = 0x1A, mode = rw, size = 3) {
357        /// Reserved bits
358        _: u32{22},
359        /// Current channel gain setting
360        /// Adjusts sensitivity of current measurement
361        gain: CurrentGain = CurrentGain::X_16,
362    }
363
364    /// Software reset.
365    register SoftReset(addr = 0x1C, mode = rw, size = 3) {
366        /// Reset command: write 0x5A5A5A to trigger reset
367        magic: ResetMagic = ResetMagic::Ignored,
368    }
369
370    /// User write protection.
371    register WriteProtection(addr = 0x1D, mode = rw, size = 3) {
372        /// Reserved bits
373        _: u16{16} = 0,
374        key: ProtectionKey = ProtectionKey::Locked,
375    }
376}
377
378impl RmsUpdateRate {
379    pub fn as_us(&self) -> u32 {
380        match self {
381            RmsUpdateRate::T_400ms => 400_000,
382            RmsUpdateRate::T_800ms => 800_000,
383        }
384    }
385}
386
387impl CurrentGain {
388    pub fn factor(&self) -> u8 {
389        match self {
390            CurrentGain::X_1 => 1,
391            CurrentGain::X_4 => 4,
392            CurrentGain::X_16 => 16,
393            CurrentGain::X_24 => 24,
394        }
395    }
396}