1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#![doc = include_str!("../README.md")]
#![no_std]

use core::marker::PhantomData;

use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::blocking::spi;
use embedded_hal::digital::v2::OutputPin;

pub use register::*;

mod ade791x;
pub mod poly;
mod register;

/// Represents a single ADE7912/ADE7913 3-Channel, Isolated, Sigma-Delta ADC with SPI.
pub struct Ade791x<SPI, CS> {
    adc: poly::Ade791x<SPI, CS, 1>,
}

impl<SPI, CS, S, P> Ade791x<SPI, CS>
where
    SPI: spi::Transfer<u8, Error = S>,
    CS: OutputPin<Error = P>,
{
    /// Creates a new [`Ade791x`] instance representing a ADE7912 chip, given the SPI peripheral and
    /// the CS output pin. The newly created instance must be initialized using [`Self::init()`].
    /// # Arguments
    /// * `spi` - The SPI interface implementing the [`spi::Transfer`] trait.
    /// * `cs` - The CS output pin implementing the [`OutputPin`] trait.
    pub fn new_ade7912(spi: SPI, cs: CS) -> Self {
        Self {
            adc: poly::Ade791x::new(spi, [(cs, Chip::ADE7912)]),
        }
    }

    /// Creates a new [`Ade791x`] instance representing a ADE7913 chip, given the SPI peripheral and
    /// the CS output pin. The newly created instance must be initialized using [`Self::init()`].
    /// # Arguments
    /// * `spi` - The SPI interface implementing the [`spi::Transfer`] trait.
    /// * `cs` - The CS output pin implementing the [`OutputPin`] trait.
    pub fn new_ade7913(spi: SPI, cs: CS) -> Self {
        Self {
            adc: poly::Ade791x::new(spi, [(cs, Chip::ADE7913)]),
        }
    }

    /// Initializes the ADC, applying the given configuration. After this method, the ADC is ready
    /// to use.
    /// # Arguments
    /// * `delay` - The delay source implementing the [`DelayMs`] trait.
    /// * `config` - The [`Config`] struct containing the configuration for the ADC.
    /// * `calibration` - The [`Calibration`] struct containing the calibration values for the ADC.
    pub fn init(
        &mut self,
        delay: &mut dyn DelayMs<u32>,
        config: Config,
        calibration: Calibration,
    ) -> Result<(), Error<S, P>> {
        self.adc
            .init(delay, [config], [calibration], [EmiCtrl::default()])
    }

    /// Performs a hardware reset of the ADC. During a hardware reset, all the registers are set to
    /// their default values and the dc-to-dc converter is shut down. After a hardware reset, the
    /// ADC needs to be initialized again, using [`Self::init()`].
    pub fn hard_reset(&mut self) -> Result<(), Error<S, P>> {
        self.adc.hard_reset()
    }

    /// Performs a software reset of the ADC. During a software reset, all the internal registers
    /// are reset to their default values. The dc-to-dc converter continues to function. After a
    /// software reset, the ADC needs to be initialized again, using [`Self::init()`].
    pub fn soft_reset(&mut self) -> Result<(), Error<S, P>> {
        self.adc.soft_reset()
    }

    /// Powers-down the ADC by turning off the dc-to-dc converter and shutting down the Σ-Δ
    /// modulators. Although the ADE7912/ADE7913 configuration registers maintain their values, the
    /// `iwv`, `v1wv`, and `v2wv` [`Measurement`] fields are in an undefined state.
    pub fn powerdown(&mut self) -> Result<(), Error<S, P>> {
        self.adc.powerdown()
    }

    /// Wakes-up the ADC by turning on the dc-to-dc converter and activating the Σ-Δ modulators.
    pub fn wakeup(&mut self) -> Result<(), Error<S, P>> {
        self.adc.wakeup()
    }

    /// Returns the latest available measurement from the ADC as a [`RawMeasurement`] struct. Call
    /// this method inside the ISR from the DREADY pin to get a new measurement as soon as it's
    /// ready. This method does not convert the received data. To get converted metrics, use
    /// [`Self::get_measurement()`] instead. This method does not perform CRC checks on received
    /// data.
    pub fn get_raw_measurement(&mut self) -> Result<RawMeasurement, Error<S, P>> {
        self.adc.get_raw_measurement().map(|m| m[0])
    }

    /// Returns the latest available measurement from the ADC as a [`Measurement`] struct. Call this
    /// method inside the ISR from the DREADY pin to get a new measurement as soon as it's ready.
    /// This method converts raw data to voltage, current and temperature measurements using the
    /// provided calibration values. This method does not perform CRC checks on received data.
    pub fn get_measurement(&mut self) -> Result<Measurement, Error<S, P>> {
        self.adc.get_measurement().map(|m| m[0])
    }
}

/// Contains the raw values coming from the ADC.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RawMeasurement {
    /// Raw current channel value.
    pub iwv: i32,
    /// Raw voltage 1 channel value.
    pub v1wv: i32,
    /// Raw voltage 2 channel value.
    pub v2wv: i32,
}

/// Contains the converted metrics coming from the ADC.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Measurement {
    /// Current value in Amperes.
    pub current: f32,
    /// Voltage value in Volts.
    pub voltage: f32,
    /// Auxiliary metric value as a [`MeasurementAux`]. This field can be a second voltage
    /// measurement in Volts for the ADE7913 or a temperature measurement in °C for the ADE7912 or
    /// the ADE7913, if `temp_en = true` in [`Config`].
    pub aux: MeasurementAux,
}

/// Represents the possible auxiliary measurement metrics.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MeasurementAux {
    Voltage(f32),
    Temperature(f32),
}

/// Contains the calibration values for the ADC.
#[derive(Default, Debug, Copy, Clone)]
pub struct Calibration {
    /// Calibration offset as a [`CalibrationOffset`].
    pub offset: CalibrationOffset,
    /// Calibration gain as a [`CalibrationGain`].
    pub gain: CalibrationGain,
}

/// Contains the calibration offsets, that can be obtained by reading the ADC measurements with the
/// default calibration values and no load applied.
#[derive(Default, Debug, Copy, Clone)]
pub struct CalibrationOffset {
    /// Calibration offset for the current channel.
    pub current: f32,
    /// Calibration offset for the voltage channel.
    pub voltage: f32,
    /// Calibration offset for the auxiliary channel. Set this field to [`None`] to automatically
    /// set the auxiliary offset based on the internal values.
    pub aux: Option<f32>,
}

/// Contains the calibration multipliers, that can be obtained by applying a reference load and
/// dividing it with the ADC measurements with only the offset values set, while leaving the
/// multipliers to their default values.
#[derive(Debug, Copy, Clone)]
pub struct CalibrationGain {
    /// Calibration gain for the current channel.
    pub current: f32,
    /// Calibration voltage for the voltage channel.
    pub voltage: f32,
    /// Calibration gain for the auxiliary channel. Set this field to [`None`] to automatically
    /// set the auxiliary offset based on the internal values.
    pub aux: Option<f32>,
}

impl Default for CalibrationGain {
    fn default() -> Self {
        Self {
            current: 1.0,
            voltage: 1.0,
            aux: None,
        }
    }
}

/// Represents the chips of the ADE791x family.
#[derive(PartialEq, Eq)]
pub enum Chip {
    ADE7912,
    ADE7913,
}

/// Represents the possible errors.
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum Error<S, P> {
    SpiError(S),
    PinError(P),
    ResetTimeout,
    ReadOnlyRegister,
    WriteOnlyRegister,
    BurstReadNotPermitted,
    RegisterContentMismatch,
}