Skip to main content

variegated_nau7802/
lib.rs

1//! # NAU7802 Embassy Driver
2//!
3//! Embassy-based driver for the Nuvoton NAU7802 24-bit precision ADC.
4//! This driver provides async support for I2C communication with the NAU7802,
5//! designed specifically for load cell and weight measurement applications.
6//!
7//! The NAU7802 is a precision, 24-bit, analog-to-digital converter (ADC) with
8//! programmable gain amplifier (PGA), voltage reference, and calibration capabilities.
9//! It is particularly well-suited for bridge sensor applications like load cells.
10//!
11//! ## Usage Example
12//!
13//! ```no_run
14//! use variegated_nau7802::{Nau7802, Nau7802DataAvailableStrategy, Gain, SamplesPerSecond, Ldo};
15//! 
16//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
17//! # let i2c = todo!();
18//! # let delay = todo!();
19//! // Create the driver with I2C bus and delay provider
20//! let data_strategy = Nau7802DataAvailableStrategy::Polling;
21//! let mut nau = Nau7802::new(i2c, data_strategy, delay, Some(0x2A)); // Default I2C address
22//!
23//! // Initialize the device with high-level init function
24//! nau.init(Ldo::L3v3, Gain::G128, SamplesPerSecond::SPS80).await?;
25//!
26//! // Read weight measurement
27//! nau.wait_for_data_available().await?;
28//! let reading = nau.read().await?;
29//! // Convert to weight using your calibration factor
30//! let weight_grams = (reading as f32) * 0.001; // Example calibration
31//! }
32//! # Ok(())
33//! # }
34//! ```
35//!
36//! Based on [amiraeva/nau7802-rs](https://github.com/amiraeva/nau7802-rs).
37//! See the datasheet in `docs/datasheet.pdf` for complete technical specifications.
38
39#![no_std]
40#![warn(missing_docs)]
41
42use byteorder::ByteOrder as _;
43use core::{fmt, slice};
44use embedded_hal_async::digital::Wait;
45use embedded_hal_async::i2c::I2c;
46use embedded_hal_async::delay::DelayNs;
47
48mod constants;
49pub use constants::*;
50
51/// Result type alias for NAU7802 operations
52pub type Result<T> = core::result::Result<T, Error>;
53
54/// Errors that can occur when communicating with the NAU7802
55#[derive(Debug)]
56pub enum Error {
57    /// I2C communication error
58    I2cError,
59    /// Device power-up sequence failed
60    PowerupFailed,
61}
62
63/// Strategy for detecting when new ADC data is available
64pub enum Nau7802DataAvailableStrategy<W: Wait>{
65    /// Use polling to check for data ready
66    Polling,
67    /// Use the dedicated DRDY pin to detect when data is ready
68    DrdyPin(W),
69}
70
71/// NAU7802 ADC driver with Embassy async support
72pub struct Nau7802<I2C: I2c, W: Wait, D: DelayNs> {
73    i2c: I2C,
74    address: u8,
75    wait_strategy: Nau7802DataAvailableStrategy<W>,
76    delay: D
77}
78
79impl<I2C: I2c, W: Wait, D: DelayNs> Nau7802<I2C, W, D> 
80where 
81    I2C::Error: fmt::Debug,
82{
83    /// Create a new NAU7802 driver instance
84    pub fn new(i2c: I2C, wait_strategy: Nau7802DataAvailableStrategy<W>, delay: D, address: Option<u8>) -> Self {
85        let addr = if let Some(a) = address {
86            a
87        } else {
88            0x2A
89        };
90
91        Self {
92            i2c,
93            wait_strategy,
94            delay,
95            address: addr
96        }
97    }
98
99    /// Initialize the NAU7802 with the specified configuration
100    pub async fn init(
101        &mut self,
102        ldo: Ldo,
103        gain: Gain,
104        sps: SamplesPerSecond,
105    ) -> Result<()> {
106        self.start_reset().await?;
107        self.finish_reset().await?;
108        self.power_up().await?;
109        self.set_ldo(ldo).await?;
110        self.set_gain(gain).await?;
111        self.set_sample_rate(sps).await?;
112        self.misc_init().await?;
113        self.begin_afe_calibration().await?;
114
115        while self.poll_afe_calibration_status().await? != AfeCalibrationStatus::Success {}
116
117        Ok(())
118    }
119
120    /// Wait for new ADC data to become available
121    pub async fn wait_for_data_available(&mut self) -> Result<()> {
122        match self.wait_strategy {
123            Nau7802DataAvailableStrategy::Polling => self.wait_for_data_available_i2c().await?,
124            Nau7802DataAvailableStrategy::DrdyPin(ref mut w) => w.wait_for_high().await.map_err(|_| Error::I2cError)?
125        }
126        
127        Ok(())
128    }
129
130    async fn wait_for_data_available_i2c(&mut self) -> Result<()> {
131        loop {
132            if self.get_bit(Register::PuCtrl, PuCtrlBits::CR).await? {
133                return Ok(());
134            }
135            self.delay.delay_ms(1).await;
136        }
137    }
138    
139    /// Read a 24-bit signed conversion result
140    pub async fn read(&mut self) -> Result<i32> {
141        self.wait_for_data_available().await?;
142
143        self.request_register(Register::AdcoB2).await?;
144
145        let mut buf = [0u8; 3]; // will hold an i24
146        self.i2c
147            .read(self.address, &mut buf)
148            .await
149            .map_err(|_| Error::I2cError)?;
150
151        let adc_result = byteorder::BigEndian::read_i24(&buf);
152        Ok(adc_result)
153    }
154
155    /// Begin analog front-end calibration
156    pub async fn begin_afe_calibration(&mut self) -> Result<()> {
157        self.set_bit(Register::Ctrl2, Ctrl2RegisterBits::Cals).await
158    }
159
160    /// Check the status of analog front-end calibration
161    pub async fn poll_afe_calibration_status(&mut self) -> Result<AfeCalibrationStatus> {
162        if self.get_bit(Register::Ctrl2, Ctrl2RegisterBits::Cals).await? {
163            return Ok(AfeCalibrationStatus::InProgress);
164        }
165
166        if self.get_bit(Register::Ctrl2, Ctrl2RegisterBits::CalError).await? {
167            return Ok(AfeCalibrationStatus::Failure);
168        }
169
170        Ok(AfeCalibrationStatus::Success)
171    }
172
173    /// Set the ADC sample rate
174    pub async fn set_sample_rate(&mut self, sps: SamplesPerSecond) -> Result<()> {
175        const SPS_MASK: u8 = 0b10001111;
176        const SPS_START_BIT_IDX: u8 = 4;
177
178        self.set_function_helper(Register::Ctrl2, SPS_MASK, SPS_START_BIT_IDX, sps as _).await
179    }
180
181    /// Set the programmable gain amplifier gain
182    pub async fn set_gain(&mut self, gain: Gain) -> Result<()> {
183        const GAIN_MASK: u8 = 0b11111000;
184        const GAIN_START_BIT: u8 = 0;
185
186        self.set_function_helper(Register::Ctrl1, GAIN_MASK, GAIN_START_BIT, gain as _).await
187    }
188
189    /// Set the low-dropout regulator voltage
190    pub async fn set_ldo(&mut self, ldo: Ldo) -> Result<()> {
191        const LDO_MASK: u8 = 0b11000111;
192        const LDO_START_BIT: u8 = 3;
193
194        self.set_function_helper(Register::Ctrl1, LDO_MASK, LDO_START_BIT, ldo as _).await?;
195
196        self.set_bit(Register::PuCtrl, PuCtrlBits::AVDDS).await
197    }
198
199    /// Power up the analog and digital circuitry
200    pub async fn power_up(&mut self) -> Result<()> {
201        const NUM_ATTEMPTS: usize = 100;
202
203        self.set_bit(Register::PuCtrl, PuCtrlBits::PUD).await?;
204        self.set_bit(Register::PuCtrl, PuCtrlBits::PUA).await?;
205
206        let mut powered_up = false;
207        let mut attempts = 0;
208        while !powered_up && attempts < NUM_ATTEMPTS {
209            let res = self.get_bit(Register::PuCtrl, PuCtrlBits::PUR).await;
210            if let Ok(rdy) = res {
211                powered_up = rdy;
212            }
213
214            attempts += 1;
215        }
216        
217        if powered_up {
218            Ok(())
219        } else {
220            Err(Error::PowerupFailed)
221        }
222    }
223
224    /// Start the device reset sequence
225    pub async fn start_reset(&mut self) -> Result<()> {
226        self.set_bit(Register::PuCtrl, PuCtrlBits::RR).await
227    }
228
229    /// Finish the device reset sequence
230    pub async fn finish_reset(&mut self) -> Result<()> {
231        self.clear_bit(Register::PuCtrl, PuCtrlBits::RR).await
232    }
233
234
235    /// Perform miscellaneous initialization steps
236    pub async fn misc_init(&mut self) -> Result<()> {
237        const TURN_OFF_CLK_CHPL: u8 = 0x30;
238
239        // Turn off CLK_CHP. From 9.1 power on sequencing
240        self.set_register(Register::Adc, TURN_OFF_CLK_CHPL).await?;
241
242        // Enable 330pF decoupling cap on chan 2. From 9.14 application circuit note
243        self.set_bit(Register::PgaPwr, PgaPwrRegisterBits::CapEn).await
244    }
245
246    async fn set_function_helper(
247        &mut self,
248        reg: Register,
249        mask: u8,
250        start_idx: u8,
251        new_val: u8,
252    ) -> Result<()> {
253        let mut val = self.get_register(reg).await?;
254        val &= mask;
255        val |= new_val << start_idx;
256
257        self.set_register(reg, val).await
258    }
259
260    async fn set_bit<B: RegisterBits>(&mut self, addr: Register, bit_idx: B) -> Result<()> {
261        let mut val = self.get_register(addr).await?;
262        val |= 1 << bit_idx.get();
263        self.set_register(addr, val).await
264    }
265
266    async fn clear_bit<B: RegisterBits>(&mut self, addr: Register, bit_idx: B) -> Result<()> {
267        let mut val = self.get_register(addr).await?;
268        val &= !(1 << bit_idx.get());
269        self.set_register(addr, val).await
270    }
271
272    async fn get_bit<B: RegisterBits>(&mut self, addr: Register, bit_idx: B) -> Result<bool> {
273        let mut val = self.get_register(addr).await?;
274        val &= 1 << bit_idx.get();
275        Ok(val != 0)
276    }
277
278    async fn set_register(&mut self, reg: Register, val: u8) -> Result<()> {
279        let transaction = [reg as _, val];
280
281        self.i2c
282            .write(self.address, &transaction)
283            .await
284            .map_err(|_| Error::I2cError)
285    }
286
287    async fn get_register(&mut self, reg: Register) -> Result<u8> {
288        self.request_register(reg).await?;
289
290        let mut val = 0;
291        self.i2c
292            .read(self.address, slice::from_mut(&mut val))
293            .await
294            .map_err(|_| Error::I2cError)?;
295
296        Ok(val)
297    }
298
299    async fn request_register(&mut self, reg: Register) -> Result<()> {
300        let reg = reg as u8;
301
302        self.i2c
303            .write(self.address, slice::from_ref(&reg))
304            .await
305            .map_err(|_| Error::I2cError)
306    }
307}