axp192/
lib.rs

1//! AXP192 power management chip interface
2//!
3//! [`Axp192::new`] is the starting-point interface for this crate.
4//!
5//! Some devices which include this IC:
6//!
7//!  * [M5Stack Core 2](https://docs.m5stack.com/en/core/core2) (including the [Core 2 for
8//!    AWS](https://docs.m5stack.com/en/core/core2_for_aws) variant)
9//!  * [M5Stack Tough](https://docs.m5stack.com/en/core/tough)
10//!  * [M5StickC](https://docs.m5stack.com/en/core/m5stickc)
11//!  * [M5StickC PLUS](https://docs.m5stack.com/en/core/m5stickc_plus)
12//!
13//! ***Warning!*** This chip probably controls power to the microcontroller you are running the
14//! code on, and bricking the entire device is a possibility!
15//!
16//! This implementation does not yet completely cover the functionality of the IC. If there's a
17//! feature you'd like to see implemented, either [open an
18//! issue](https://github.com/rcloran/axp192-rs/issues) or create a pull request :)
19//!
20//! Datasheet:
21//! [https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf
22//! ](https://github.com/m5stack/M5-Schematic/blob/master/Core/AXP192%20Datasheet_v1.1_en_draft_2211.pdf)
23
24#![warn(rust_2018_idioms)]
25#![no_std]
26
27use embedded_hal::i2c;
28
29const AXP192_ADDRESS: u8 = 0x34;
30
31/// The Axp192 struct is the main interface for this crate
32pub struct Axp192<I2C> {
33    i2c: I2C,
34}
35
36/// GPIO0 function setting
37///
38/// Used with [`Axp192::set_gpio0_mode`]
39pub enum GpioMode0 {
40    /// 000: NMOS Open drain output
41    NmosOpenDrainOutput = 0b000,
42    /// 001: Universal input function
43    UniversalInput = 0b001,
44    /// 010:PWM1 Output, high level is VINT ,Do not Can be less than 100K Pull-down resistor
45    LowNoiseLdo = 0b010,
46    /// 011: Keep
47    Keep = 0b011,
48    /// 100: ADC enter
49    AdcEnter = 0b100,
50    /// 101: Low output
51    LowOutput = 0b101,
52    /// 11X: Floating
53    Floating = 0b110,
54}
55
56/// GPIO1 and GPIO2 function setting
57///
58/// Used with [`Axp192::set_gpio1_mode`] and [`Axp192::set_gpio2_mode`]
59pub enum GpioMode12 {
60    /// 000: NMOS Open drain output
61    NmosOpenDrainOutput = 0b000,
62    /// 001: Universal input function
63    UniversalInput = 0b001,
64    /// 010:PWM1 Output, high level is VINT ,Do not Can be less than 100K Pull-down resistor
65    PWMOutput = 0b010,
66    /// 011: Keep
67    Keep = 0b011,
68    /// 100: ADC enter
69    AdcEnter = 0b100,
70    /// 101: Low output
71    LowOutput = 0b101,
72    /// 11X: Floating
73    Floating = 0b110,
74}
75
76/// GPIO3 and GPIO4 function setting
77///
78/// Used with [`Axp192::set_gpio3_mode`] and [`Axp192::set_gpio4_mode`]
79pub enum GpioMode34 {
80    /// 00: External charging control
81    ExternalChargeControl = 0b00,
82    /// 01: NMOS Open drain output
83    NmosOpenDrainOutput = 0b01,
84    /// 10: Universal input
85    UniversalInput = 0b10,
86    /// GPIO3: 11: ADC enter
87    /// GPIO4: 11: Undefined
88    AdcEnter = 0b11,
89}
90
91/// Boot time setting
92///
93/// Used with [`Axp192::set_key_mode`]
94pub enum BootTime {
95    Boot128ms = 0b00,
96    Boot512ms = 0b01,
97    Boot1s = 0b10,
98    Boot2s = 0b11,
99}
100
101/// Long press time setting
102///
103/// Used with [`Axp192::set_key_mode`]
104pub enum LongPress {
105    Lp1000ms = 0b00,
106    Lp1500ms = 0b01,
107    Lp2000ms = 0b10,
108    Lp2500ms = 0b11,
109}
110
111/// After the power is started PWROK Signal delay
112///
113/// Used with [`Axp192::set_key_mode`]
114pub enum PowerOkDelay {
115    Delay32ms = 0b0,
116    Delay64ms = 0b1,
117}
118
119/// Shutdown duration setting
120///
121/// Used with [`Axp192::set_key_mode`]
122pub enum ShutdownDuration {
123    Sd4s = 0b00,
124    Sd6s = 0b01,
125    Sd8s = 0b10,
126    Sd10s = 0b11,
127}
128
129impl<I2C, E> Axp192<I2C>
130where
131    I2C: i2c::I2c<Error = E>,
132{
133    /// Construct a new [`Axp192`]
134    ///
135    /// `i2c` must be an object which implements the I2C trait from embedded-hal
136    pub fn new(i2c: I2C) -> Self {
137        Self { i2c }
138    }
139
140    // Utility internal functions
141    fn get(&mut self, reg_addr: u8, buff: &mut [u8]) -> Result<(), E> {
142        self.i2c.write_read(AXP192_ADDRESS, &[reg_addr], buff)
143    }
144
145    fn get_8(&mut self, reg_addr: u8) -> Result<u8, E> {
146        let mut buff = [0u8];
147        self.get(reg_addr, &mut buff)?;
148        Ok(buff[0])
149    }
150
151    fn get_12(&mut self, reg_addr: u8) -> Result<u16, E> {
152        let mut buff = [0; 2];
153        self.get(reg_addr, &mut buff)?;
154        Ok(((buff[0] as u16) << 4) + (buff[1] as u16))
155    }
156
157    fn get_13(&mut self, reg_addr: u8) -> Result<u16, E> {
158        let mut buff = [0; 2];
159        self.get(reg_addr, &mut buff)?;
160        Ok(((buff[0] as u16) << 5) + (buff[1] as u16))
161    }
162
163    fn get_flag(&mut self, reg_addr: u8, flag: u8) -> Result<bool, E> {
164        Ok(self.get_8(reg_addr)? & flag != 0)
165    }
166
167    fn set_8(&mut self, addr: u8, v: u8) -> Result<(), E> {
168        self.i2c.write(AXP192_ADDRESS, &[addr, v])
169    }
170
171    fn set_flag(&mut self, addr: u8, bit: u8, state: bool) -> Result<(), E> {
172        let mut v = self.get_8(addr)?;
173        if state {
174            v |= bit;
175        } else {
176            v &= !bit;
177        }
178
179        self.set_8(addr, v)
180    }
181
182    fn voltage_value(value: u16, min: u16, max: u16, step: u16) -> u8 {
183        let value = value.clamp(min, max);
184        ((value - min) / step) as u8
185    }
186
187    // Public functions in register order
188
189    /// Get input power status register (00)
190    pub fn get_power_status(&mut self) -> Result<u8, E> {
191        self.get_8(0x00)
192    }
193
194    /// Indicates the direction of battery current
195    pub fn get_charging(&mut self) -> Result<bool, E> {
196        self.get_flag(0x00, 0b0000_0100)
197    }
198
199    /// Instructions VBUS it's usable or not
200    pub fn get_vbus_usable(&mut self) -> Result<bool, E> {
201        self.get_flag(0x00, 0b0001_0000)
202    }
203
204    /// VBUS Presence indication
205    pub fn get_vbus_present(&mut self) -> Result<bool, E> {
206        self.get_flag(0x00, 0b0010_0000)
207    }
208
209    /// Instructions ACIN it's usable or not
210    pub fn get_acin_usable(&mut self) -> Result<bool, E> {
211        self.get_flag(0x00, 0b0100_0000)
212    }
213
214    /// ACIN Presence indication
215    pub fn get_acin_present(&mut self) -> Result<bool, E> {
216        self.get_flag(0x00, 0b1000_0000)
217    }
218
219    /// EXTEN Switch status
220    pub fn get_exten_on(&mut self) -> Result<bool, E> {
221        self.get_flag(0x10, 0b0000_0100)
222    }
223
224    /// EXTEN Switch control
225    pub fn set_exten_on(&mut self, state: bool) -> Result<(), E> {
226        self.set_flag(0x10, 0b0000_0100, state)
227    }
228
229    /// DC-DC1 Switch status
230    pub fn get_dcdc1_on(&mut self) -> Result<bool, E> {
231        self.get_flag(0x12, 0b0000_0001)
232    }
233
234    /// DC-DC1 Switch control
235    ///
236    /// ***Warning!*** This output is often connected to the microcontroller that you are
237    /// controlling the AXP192 from!
238    pub fn set_dcdc1_on(&mut self, state: bool) -> Result<(), E> {
239        self.set_flag(0x12, 0b0000_0001, state)
240    }
241
242    /// DC-DC3 Switch status
243    pub fn get_dcdc3_on(&mut self) -> Result<bool, E> {
244        self.get_flag(0x12, 0b0000_0010)
245    }
246
247    /// DC-DC3 Switch control
248    pub fn set_dcdc3_on(&mut self, state: bool) -> Result<(), E> {
249        self.set_flag(0x12, 0b0000_0010, state)
250    }
251
252    /// LDO2 Switch status
253    pub fn get_ldo2_on(&mut self) -> Result<bool, E> {
254        self.get_flag(0x12, 0b0000_0100)
255    }
256
257    /// LDO2 Switch control
258    pub fn set_ldo2_on(&mut self, state: bool) -> Result<(), E> {
259        self.set_flag(0x12, 0b0000_0100, state)
260    }
261
262    /// LDO3 Switch status
263    pub fn get_ldo3_on(&mut self) -> Result<bool, E> {
264        self.get_flag(0x12, 0b0000_1000)
265    }
266
267    /// LDO3 Switch control
268    pub fn set_ldo3_on(&mut self, state: bool) -> Result<(), E> {
269        self.set_flag(0x12, 0b0000_1000, state)
270    }
271
272    /// DC-DC2 Switch status
273    pub fn get_dcdc2_on(&mut self) -> Result<bool, E> {
274        self.get_flag(0x12, 0b0001_0000)
275    }
276
277    /// DC-DC2 Switch control
278    pub fn set_dcdc2_on(&mut self, state: bool) -> Result<(), E> {
279        self.set_flag(0x12, 0b0001_0000, state)
280    }
281
282    /// DC-DC1 output voltage setting
283    ///
284    /// 0.7-3.5V , 25mV/step
285    ///
286    /// ***Warning!*** This output is often connected to the microcontroller that you are
287    /// controlling the AXP192 from!
288    pub fn set_dcdc1_voltage(&mut self, voltage: u16) -> Result<(), E> {
289        let v = Self::voltage_value(voltage, 700, 3500, 25) & 0x7f;
290        self.set_8(0x26, v)
291    }
292
293    /// DC-DC3 output voltage setting
294    ///
295    /// 0.7-3.5V , 25mV/step
296    pub fn set_dcdc3_voltage(&mut self, voltage: u16) -> Result<(), E> {
297        let v = Self::voltage_value(voltage, 700, 3500, 25) & 0x7f;
298        self.set_8(0x27, v)
299    }
300
301    /// LDO3 Output voltage setting
302    ///
303    /// 1.8-3.3V , 100mV/step
304    pub fn set_ldo3_voltage(&mut self, voltage: u16) -> Result<(), E> {
305        let v = Self::voltage_value(voltage, 1800, 3300, 100) & 0x0f;
306        let existing = self.get_8(0x28)?;
307        self.set_8(0x28, (existing & 0xf0) | v)
308    }
309
310    /// LDO2 Output voltage setting
311    ///
312    /// 1.8-3.3V , 100mV/step
313    pub fn set_ldo2_voltage(&mut self, voltage: u16) -> Result<(), E> {
314        let v = Self::voltage_value(voltage, 1800, 3300, 100) & 0x0f;
315        let existing = self.get_8(0x28)?;
316        self.set_8(0x28, (existing & 0x0f) | (v << 4))
317    }
318
319    /// VBUS-IPSOUT channel management
320    /// VBUS When available VBUS-IPSOUT Path selection control signal
321    /// false: by N_VBUSEN pin Decide whether to open this channel
322    /// true: :VBUS-IPSOUT Access can be selected to open, regardless of N_VBUSEN status
323    pub fn set_ipsout_always(&mut self, state: bool) -> Result<(), E> {
324        self.set_flag(0x30, 0b1000_0000, state)
325    }
326
327    /// PEK key parameter setting
328    pub fn set_key_mode(
329        &mut self,
330        shutdown_duration: ShutdownDuration,
331        powerok_delay: PowerOkDelay,
332        automatic_shutdown: bool,
333        longpress_duration: LongPress,
334        boot_time: BootTime,
335    ) -> Result<(), E> {
336        let v = (shutdown_duration as u8)
337            | ((powerok_delay as u8) << 2)
338            | ((automatic_shutdown as u8) << 3)
339            | ((longpress_duration as u8) << 4)
340            | ((boot_time as u8) << 6);
341        self.set_8(0x36, v)
342    }
343
344    /// ACIN Voltage ADC
345    ///
346    /// Return unit: volts
347    pub fn get_acin_voltage(&mut self) -> Result<f32, E> {
348        let v = self.get_12(0x56)?;
349        Ok(v as f32 * 0.0017)
350    }
351
352    /// ACIN Current ADC
353    ///
354    /// Return unit: amps
355    pub fn get_acin_current(&mut self) -> Result<f32, E> {
356        let v = self.get_12(0x58)?;
357        Ok(v as f32 * 0.000625)
358    }
359
360    /// VBUS Voltage ADC
361    ///
362    /// Return unit: volts
363    pub fn get_vbus_voltage(&mut self) -> Result<f32, E> {
364        let v = self.get_12(0x5a)?;
365        Ok(v as f32 * 0.0017)
366    }
367
368    /// VBUS Current ADC
369    ///
370    /// Return unit: amps
371    pub fn get_vbus_current(&mut self) -> Result<f32, E> {
372        let v = self.get_12(0x5c)?;
373        Ok(v as f32 * 0.000375)
374    }
375
376    /// AXP192 Internal temperature monitoring
377    ///
378    /// Return unit: °C
379    pub fn get_internal_temperature(&mut self) -> Result<f32, E> {
380        let v = self.get_12(0x5e)?;
381        Ok((v as f32 * 0.1) - 144.7)
382    }
383
384    /// Battery voltage
385    ///
386    /// Return unit: volts
387    pub fn get_battery_voltage(&mut self) -> Result<f32, E> {
388        let v = self.get_12(0x78)?;
389        Ok(v as f32 * 0.0011)
390    }
391
392    /// battery charging current
393    ///
394    /// Return unit: amps
395    pub fn get_battery_charge_current(&mut self) -> Result<f32, E> {
396        let v = self.get_13(0x7a)?;
397        Ok(v as f32 * 0.0005)
398    }
399
400    /// battery discharge current
401    ///
402    /// Return unit: amps
403    pub fn get_battery_discharge_current(&mut self) -> Result<f32, E> {
404        let v = self.get_13(0x7c)?;
405        Ok(v as f32 * 0.0005)
406    }
407
408    /// TS Pin ADC Function enable
409    pub fn set_ts_adc_enable(&mut self, state: bool) -> Result<(), E> {
410        self.set_flag(0x82, 0b0000_0001, state)
411    }
412
413    /// APS Voltage ADC Enable
414    pub fn set_aps_voltage_adc_enable(&mut self, state: bool) -> Result<(), E> {
415        self.set_flag(0x82, 0b0000_0010, state)
416    }
417
418    /// VBUS Current ADC Enable
419    pub fn set_vbus_current_adc_enable(&mut self, state: bool) -> Result<(), E> {
420        self.set_flag(0x82, 0b0000_0100, state)
421    }
422
423    /// VBUS Voltage ADC Enable
424    pub fn set_vbus_voltage_adc_enable(&mut self, state: bool) -> Result<(), E> {
425        self.set_flag(0x82, 0b0000_1000, state)
426    }
427
428    /// ACIN Current ADC Enable
429    pub fn set_acin_current_adc_enable(&mut self, state: bool) -> Result<(), E> {
430        self.set_flag(0x82, 0b0001_0000, state)
431    }
432
433    /// ACIN Voltage ADC Enable
434    pub fn set_acin_voltage_adc_enable(&mut self, state: bool) -> Result<(), E> {
435        self.set_flag(0x82, 0b0010_0000, state)
436    }
437
438    /// Battery current ADC Enable
439    pub fn set_battery_current_adc_enable(&mut self, state: bool) -> Result<(), E> {
440        self.set_flag(0x82, 0b0100_0000, state)
441    }
442
443    /// battery voltage ADC Enable
444    pub fn set_battery_voltage_adc_enable(&mut self, state: bool) -> Result<(), E> {
445        self.set_flag(0x82, 0b1000_0000, state)
446    }
447
448    /// GPIO0 Pin function setting
449    pub fn set_gpio0_mode(&mut self, mode: GpioMode0) -> Result<(), E> {
450        self.set_8(0x90, mode as u8)
451    }
452
453    /// Output voltage setting when GPIO0 is in LDO mode
454    ///
455    /// The input is clamped to between 1800mV and 3300mV, in steps of 100mV
456    pub fn set_gpio0_ldo_voltage(&mut self, voltage: u16) -> Result<(), E> {
457        let v = Self::voltage_value(voltage, 1800, 3300, 100) << 4;
458        self.set_8(0x91, v)
459    }
460
461    /// GPIO1 function setting
462    pub fn set_gpio1_mode(&mut self, mode: GpioMode12) -> Result<(), E> {
463        self.set_8(0x92, mode as u8)
464    }
465
466    /// GPIO2 function setting
467    pub fn set_gpio2_mode(&mut self, mode: GpioMode12) -> Result<(), E> {
468        self.set_8(0x93, mode as u8)
469    }
470
471    /// GPIO0 Output settings
472    ///
473    ///  - false: Output low level, ground NMOS turn on
474    ///  - true: Output floating, grounded NMOS shut down
475    pub fn set_gpio0_output(&mut self, state: bool) -> Result<(), E> {
476        self.set_flag(0x94, 0b0000_0001, state)
477    }
478
479    /// GPIO1 Output settings
480    ///
481    ///  - false: Output low level, ground NMOS turn on
482    ///  - true: Output floating, grounded NMOS shut down
483    pub fn set_gpio1_output(&mut self, state: bool) -> Result<(), E> {
484        self.set_flag(0x94, 0b0000_0010, state)
485    }
486
487    /// GPIO2 Output settings
488    ///
489    ///  - false: Output low level, ground NMOS turn on
490    ///  - true: Output floating, grounded NMOS shut down
491    pub fn set_gpio2_output(&mut self, state: bool) -> Result<(), E> {
492        self.set_flag(0x94, 0b0000_0100, state)
493    }
494
495    /// GPIO3 Pin function setting
496    pub fn set_gpio3_mode(&mut self, mode: GpioMode34) -> Result<(), E> {
497        let existing = self.get_8(0x95)?;
498        self.set_8(0x95, (existing & 0b1111_1100) | 0x80 | (mode as u8))
499    }
500
501    /// GPIO4 Pin function setting
502    pub fn set_gpio4_mode(&mut self, mode: GpioMode34) -> Result<(), E> {
503        let existing = self.get_8(0x95)?;
504        self.set_8(0x95, (existing & 0b1111_0011) | 0x80 | ((mode as u8) << 2))
505    }
506
507    /// GPIO3 Output settings
508    ///
509    ///  - false: Output low level, NMOS turn on
510    ///  - true: Floating, NMOS shut down
511    pub fn set_gpio3_output(&mut self, state: bool) -> Result<(), E> {
512        self.set_flag(0x96, 0b0000_0001, state)
513    }
514
515    /// GPIO4 Output settings
516    ///
517    ///  - false: Output low level, NMOS turn on
518    ///  - true: Floating, NMOS shut down
519    pub fn set_gpio4_output(&mut self, state: bool) -> Result<(), E> {
520        self.set_flag(0x96, 0b0000_0010, state)
521    }
522}