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}