1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4use embedded_hal::i2c::I2c;
20use embedded_hal::delay::DelayNs;
21use heapless::Vec;
22
23pub const AP33772S_ADDR: u8 = 0x52;
25
26pub const MAX_PDO_COUNT: usize = 13;
28
29pub const REG_STATUS: u8 = 0x01;
31pub const REG_MASK: u8 = 0x02;
32pub const REG_OPMODE: u8 = 0x03;
33pub const REG_CONFIG: u8 = 0x04;
34pub const REG_SYSTEM: u8 = 0x06;
35pub const REG_VOLTAGE: u8 = 0x11;
36pub const REG_CURRENT: u8 = 0x12;
37pub const REG_TEMP: u8 = 0x13;
38pub const REG_VREQ: u8 = 0x14;
39pub const REG_IREQ: u8 = 0x15;
40pub const REG_SRCPDO: u8 = 0x20;
41pub const REG_PD_REQMSG: u8 = 0x31;
42pub const REG_PD_CMDMSG: u8 = 0x32;
43pub const REG_PD_MSGRLT: u8 = 0x33;
44
45pub const STATUS_STARTED: u8 = 0x01;
47pub const STATUS_READY: u8 = 0x02;
48pub const STATUS_NEWPDO: u8 = 0x04;
49pub const STATUS_UVP: u8 = 0x08;
50pub const STATUS_OVP: u8 = 0x10;
51pub const STATUS_OCP: u8 = 0x20;
52pub const STATUS_OTP: u8 = 0x40;
53
54pub const CONFIG_UVP_EN: u8 = 0x08;
56pub const CONFIG_OVP_EN: u8 = 0x10;
57pub const CONFIG_OCP_EN: u8 = 0x20;
58pub const CONFIG_OTP_EN: u8 = 0x40;
59pub const CONFIG_DR_EN: u8 = 0x80;
60
61pub const SYSTEM_VOUTCTL_AUTO: u8 = 0x00;
63pub const SYSTEM_VOUTCTL_OFF: u8 = 0x01;
64pub const SYSTEM_VOUTCTL_ON: u8 = 0x02;
65pub const CC_STATUS_MASK: u8 = 0x30;
66
67pub const CMDMSG_HRST: u8 = 0x01;
69
70pub const MSGRLT_BUSY: u8 = 0x00;
72pub const MSGRLT_SUCCESS: u8 = 0x01;
73pub const MSGRLT_INVALID: u8 = 0x02;
74pub const MSGRLT_UNSUPPORTED: u8 = 0x03;
75pub const MSGRLT_FAILED: u8 = 0x04;
76pub const MSGRLT_MASK: u8 = 0x0F;
77
78pub const SRCPDO_DETECT: u16 = 0x8000;
80pub const SRCPDO_TYPE: u16 = 0x4000;
81pub const SRCPDO_CURRENT_MAX_MASK: u16 = 0x3C00;
82pub const SRCPDO_CURRENT_MAX_SHIFT: u8 = 10;
83pub const SRCPDO_VOLTAGE_MAX_MASK: u16 = 0x00FF;
84
85pub const REQMSG_VOLTAGE_SEL_MASK: u16 = 0x00FF;
87pub const REQMSG_MAX_CURRENT: u8 = 0x0F;
88pub const REQMSG_MAX_VOLTAGE: u8 = 0xFF;
89
90pub const SRCPDO_CURRENT_VALUES: [u16; 16] = [
92 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750,
93 3000, 3250, 3500, 3750, 4000, 4250, 4500, 5000
94];
95
96#[derive(Debug, Clone, Copy, PartialEq)]
98pub enum Error<E> {
99 I2c(E),
101 Timeout,
103 NegotiationFailed,
105 InvalidParameter,
107 NotInitialized,
109 ProtectionFault,
111}
112
113impl<E> From<E> for Error<E> {
114 fn from(error: E) -> Self {
115 Error::I2c(error)
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq)]
121pub enum PDVoltage {
122 V5 = 0,
124 V9 = 1,
126 V12 = 2,
128 V15 = 3,
130 V20 = 4,
132 V28 = 5,
134 Custom = 0xFF,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq)]
140pub enum PDFault {
141 None,
143 UnderVoltage,
145 OverVoltage,
147 OverCurrent,
149 OverTemperature,
151 Unknown,
153}
154
155#[derive(Debug, Clone, Copy)]
157pub struct PDOInfo {
158 pub voltage_mv: u16,
160 pub current_ma: u16,
162 pub max_power_mw: u32,
164 pub is_fixed: bool,
166 pub pdo_index: u8,
168}
169
170#[derive(Debug, Clone, Copy)]
172pub struct PDStatus {
173 pub status: u8,
175 pub op_mode: u8,
177 pub voltage_mv: u16,
179 pub current_ma: u16,
181 pub temperature: i8,
183 pub requested_voltage_mv: u16,
185 pub requested_current_ma: u16,
187 pub is_attached: bool,
189 pub is_busy: bool,
191 pub has_fault: bool,
193 pub fault_type: PDFault,
195 pub cc_status: u8,
197 pub pdp_limit_w: u8,
199}
200
201impl Default for PDStatus {
202 fn default() -> Self {
203 PDStatus {
204 status: 0,
205 op_mode: 0,
206 voltage_mv: 0,
207 current_ma: 0,
208 temperature: 0,
209 requested_voltage_mv: 0,
210 requested_current_ma: 0,
211 is_attached: false,
212 is_busy: false,
213 has_fault: false,
214 fault_type: PDFault::None,
215 cc_status: 0,
216 pdp_limit_w: 0,
217 }
218 }
219}
220
221pub struct AP33772S {
223 initialized: bool,
224 pdo_list: Vec<PDOInfo, MAX_PDO_COUNT>,
225}
226
227impl AP33772S {
228 pub fn new() -> Self {
230 AP33772S {
231 initialized: false,
232 pdo_list: Vec::new(),
233 }
234 }
235
236 pub fn init<I2C, E>(&mut self, i2c: &mut I2C) -> Result<(), Error<E>>
238 where
239 I2C: I2c<Error = E>,
240 {
241 let _status = self.read_register(i2c, REG_STATUS)?;
243
244 self.read_full_srcpdo(i2c)?;
246 self.initialized = true;
247 Ok(())
248 }
249
250 pub fn request_voltage<I2C, D, E>(&self, i2c: &mut I2C, delay: &mut D, voltage: PDVoltage) -> Result<(), Error<E>>
252 where
253 I2C: I2c<Error = E>,
254 D: DelayNs,
255 {
256 if !self.initialized {
257 return Err(Error::NotInitialized);
258 }
259
260 let voltage_mv: u16 = match voltage {
261 PDVoltage::V5 => 5000,
262 PDVoltage::V9 => 9000,
263 PDVoltage::V12 => 12000,
264 PDVoltage::V15 => 15000,
265 PDVoltage::V20 => 20000,
266 PDVoltage::V28 => 28000, PDVoltage::Custom => 0,
268 };
269
270 let mut pdo_index = 0;
271 let mut best_pdo: Option<&PDOInfo> = None;
272 let mut best_diff = u32::MAX;
273
274 for pdo in &self.pdo_list {
276 if pdo.voltage_mv >= voltage_mv {
277 let diff = (pdo.voltage_mv as u32) - (voltage_mv as u32);
278 if diff == 0 {
279 best_pdo = Some(pdo);
281 pdo_index = pdo.pdo_index;
282 break;
283 } else if diff < best_diff {
284 best_diff = diff;
286 best_pdo = Some(pdo);
287 pdo_index = pdo.pdo_index;
288 }
289 }
290 }
291
292 if pdo_index == 0 {
293 return Err(Error::InvalidParameter);
294 }
295
296 if let Some(_pdo) = best_pdo {
297 }
300
301 let mut request_msg = (pdo_index as u16 & 0x0F) << 12;
304 request_msg |= (REQMSG_MAX_CURRENT as u16 & 0x0F) << 8;
305 request_msg |= REQMSG_MAX_VOLTAGE as u16 & 0xFF;
306
307 self.write_word(i2c, REG_PD_REQMSG, request_msg)?;
308 self.wait_for_negotiation(i2c, delay)
309 }
310
311 pub fn request_custom_voltage<I2C, D, E>(&self, i2c: &mut I2C, delay: &mut D, voltage_mv: u16, _current_ma: u16) -> Result<(), Error<E>>
317 where
318 I2C: I2c<Error = E>,
319 D: DelayNs,
320 {
321 if !self.initialized {
322 return Err(Error::NotInitialized);
323 }
324
325 let mut best_pdo: Option<&PDOInfo> = None;
330 let mut best_diff = u32::MAX;
331 let mut best_is_variable = false;
332
333 for pdo in &self.pdo_list {
334 let is_variable = !pdo.is_fixed;
335 let can_provide_voltage = if is_variable {
336 pdo.voltage_mv >= voltage_mv
338 } else {
339 pdo.voltage_mv >= voltage_mv
341 };
342
343 if can_provide_voltage {
344 let diff = if is_variable && pdo.voltage_mv >= voltage_mv {
345 0u32
347 } else {
348 (pdo.voltage_mv as u32).saturating_sub(voltage_mv as u32)
350 };
351
352 let should_select = match (best_pdo, is_variable, best_is_variable) {
357 (None, _, _) => true, (Some(_), true, false) => true, (Some(_), false, true) => false, (Some(current_best), _, _) => {
361 if diff < best_diff {
363 true } else if diff == best_diff {
365 pdo.max_power_mw > current_best.max_power_mw
367 } else {
368 false }
370 }
371 };
372
373 if should_select {
374 best_diff = diff;
375 best_pdo = Some(pdo);
376 best_is_variable = is_variable;
377 }
378 }
379 }
380
381 if let Some(pdo) = best_pdo {
382 let mut request_msg = (pdo.pdo_index as u16 & 0x0F) << 12;
386 request_msg |= (REQMSG_MAX_CURRENT as u16 & 0x0F) << 8;
387
388 if !pdo.is_fixed {
389 let voltage_units = match pdo.pdo_index {
397 1..=7 => {
398 let voltage_units = (voltage_mv / 100).min(0xFF as u16);
400 request_msg |= voltage_units & 0xFF;
401 voltage_units
402 },
403 8..=13 => {
404 let voltage_units = (voltage_mv / 200).min(0xFF as u16);
406 request_msg |= voltage_units & 0xFF;
407 voltage_units
408 },
409 _ => {
410 request_msg |= REQMSG_MAX_VOLTAGE as u16 & 0xFF;
412 REQMSG_MAX_VOLTAGE as u16
413 }
414 };
415 request_msg |= voltage_units & 0xFF;
416 } else {
417 request_msg |= REQMSG_MAX_VOLTAGE as u16 & 0xFF;
419 }
420
421 self.write_word(i2c, REG_PD_REQMSG, request_msg)?;
422 self.wait_for_negotiation(i2c, delay)
423 } else {
424 Err(Error::InvalidParameter)
425 }
426 }
427
428 pub fn get_status<I2C, E>(&self, i2c: &mut I2C) -> Result<PDStatus, Error<E>>
430 where
431 I2C: I2c<Error = E>,
432 {
433 if !self.initialized {
434 return Err(Error::NotInitialized);
435 }
436
437 let mut status = PDStatus::default();
438
439 status.status = self.read_register(i2c, REG_STATUS)?;
440 status.op_mode = self.read_register(i2c, REG_OPMODE)?;
441
442 let system_reg = self.read_register(i2c, REG_SYSTEM)?;
443 status.cc_status = system_reg & CC_STATUS_MASK;
444
445 status.is_attached = (status.status & STATUS_READY) != 0;
446 status.is_busy = (status.status & STATUS_NEWPDO) != 0;
447
448 let has_uvp = (status.status & STATUS_UVP) != 0;
449 let has_ovp = (status.status & STATUS_OVP) != 0;
450 let has_ocp = (status.status & STATUS_OCP) != 0;
451 let has_otp = (status.status & STATUS_OTP) != 0;
452
453 status.has_fault = has_uvp || has_ovp || has_ocp || has_otp;
454
455 if status.has_fault {
456 if has_uvp {
457 status.fault_type = PDFault::UnderVoltage;
458 } else if has_ovp {
459 status.fault_type = PDFault::OverVoltage;
460 } else if has_ocp {
461 status.fault_type = PDFault::OverCurrent;
462 } else if has_otp {
463 status.fault_type = PDFault::OverTemperature;
464 } else {
465 status.fault_type = PDFault::Unknown;
466 }
467 } else {
468 status.fault_type = PDFault::None;
469 }
470
471 let voltage_word = self.read_word(i2c, REG_VOLTAGE)?;
472 status.voltage_mv = voltage_word * 80;
473
474 let current_byte = self.read_register(i2c, REG_CURRENT)?;
475 status.current_ma = (current_byte as u16) * 24;
476
477 status.temperature = self.read_register(i2c, REG_TEMP)? as i8;
478
479 let req_voltage = self.read_word(i2c, REG_VREQ)?;
480 status.requested_voltage_mv = req_voltage * 50;
481
482 let req_current = self.read_word(i2c, REG_IREQ)?;
483 status.requested_current_ma = req_current * 10;
484
485 let pdp = ((status.requested_voltage_mv as u32) * (status.requested_current_ma as u32)) / 1_000_000;
486 status.pdp_limit_w = pdp as u8;
487
488 Ok(status)
489 }
490
491 pub fn get_pdo_list(&self) -> &[PDOInfo] {
493 &self.pdo_list
494 }
495
496 pub fn get_max_voltage(&self) -> u16 {
498 self.pdo_list.iter()
499 .map(|pdo| pdo.voltage_mv)
500 .max()
501 .unwrap_or(0)
502 }
503
504 pub fn hard_reset<I2C, E>(&self, i2c: &mut I2C) -> Result<(), Error<E>>
506 where
507 I2C: I2c<Error = E>,
508 {
509 self.write_register(i2c, REG_PD_CMDMSG, CMDMSG_HRST)?;
510 Ok(())
511 }
512
513 pub fn set_vout_auto_control<I2C, E>(&self, i2c: &mut I2C) -> Result<(), Error<E>>
515 where
516 I2C: I2c<Error = E>,
517 {
518 self.write_register(i2c, REG_SYSTEM, SYSTEM_VOUTCTL_AUTO)
519 }
520
521 pub fn force_vout_off<I2C, E>(&self, i2c: &mut I2C) -> Result<(), Error<E>>
523 where
524 I2C: I2c<Error = E>,
525 {
526 self.write_register(i2c, REG_SYSTEM, SYSTEM_VOUTCTL_OFF)
527 }
528
529 pub fn force_vout_on<I2C, E>(&self, i2c: &mut I2C) -> Result<(), Error<E>>
531 where
532 I2C: I2c<Error = E>,
533 {
534 self.write_register(i2c, REG_SYSTEM, SYSTEM_VOUTCTL_ON)
535 }
536
537 pub fn configure_protections<I2C, E>(
539 &self,
540 i2c: &mut I2C,
541 enable_uvp: bool,
542 enable_ovp: bool,
543 enable_ocp: bool,
544 enable_otp: bool,
545 enable_dr: bool,
546 ) -> Result<(), Error<E>>
547 where
548 I2C: I2c<Error = E>,
549 {
550 let mut config = self.read_register(i2c, REG_CONFIG)?;
551
552 config &= !(CONFIG_UVP_EN | CONFIG_OVP_EN | CONFIG_OCP_EN | CONFIG_OTP_EN | CONFIG_DR_EN);
553
554 if enable_uvp { config |= CONFIG_UVP_EN; }
555 if enable_ovp { config |= CONFIG_OVP_EN; }
556 if enable_ocp { config |= CONFIG_OCP_EN; }
557 if enable_otp { config |= CONFIG_OTP_EN; }
558 if enable_dr { config |= CONFIG_DR_EN; }
559
560 self.write_register(i2c, REG_CONFIG, config)
561 }
562
563 fn wait_for_negotiation<I2C, D, E>(&self, i2c: &mut I2C, delay: &mut D) -> Result<(), Error<E>>
567 where
568 I2C: I2c<Error = E>,
569 D: DelayNs,
570 {
571 for _ in 0..50 { let result = self.read_register(i2c, REG_PD_MSGRLT)?;
573 let result_code = result & MSGRLT_MASK;
574
575 let status = self.read_register(i2c, REG_STATUS)?;
576
577 let has_fault = (status & (STATUS_UVP | STATUS_OVP | STATUS_OCP | STATUS_OTP)) != 0;
578 if has_fault {
579 return Err(Error::ProtectionFault);
580 }
581
582 if result_code == MSGRLT_SUCCESS {
583 return Ok(());
584 } else if result_code != 0 {
585 return Err(Error::NegotiationFailed);
586 }
587
588 delay.delay_ms(50);
590 }
591
592 Err(Error::Timeout)
593 }
594
595 fn read_full_srcpdo<I2C, E>(&mut self, i2c: &mut I2C) -> Result<(), Error<E>>
597 where
598 I2C: I2c<Error = E>,
599 {
600 self.pdo_list.clear();
601
602 let mut buffer = [0u8; MAX_PDO_COUNT * 2];
603 i2c.write(AP33772S_ADDR, &[REG_SRCPDO])?;
604 i2c.read(AP33772S_ADDR, &mut buffer)?;
605
606 for i in 0..MAX_PDO_COUNT {
607 let offset = i * 2;
608 let word = ((buffer[offset + 1] as u16) << 8) | buffer[offset] as u16;
609
610 let is_epr = i >= 7;
611 let pdo_index = if is_epr { (i - 7) as u8 + 8 } else { i as u8 + 1 };
612
613 if let Some(pdo_info) = self.parse_pdo_word(word, is_epr, pdo_index) {
614 let _ = self.pdo_list.push(pdo_info); }
616 }
617
618 Ok(())
619 }
620
621 fn parse_pdo_word(&self, pdo_word: u16, is_epr: bool, pdo_index: u8) -> Option<PDOInfo> {
623 if (pdo_word & SRCPDO_DETECT) == 0 {
624 return None;
625 }
626
627 let is_apdo = (pdo_word & SRCPDO_TYPE) != 0;
628 let is_fixed = !is_apdo;
629
630 let current_idx = ((pdo_word & SRCPDO_CURRENT_MAX_MASK) >> SRCPDO_CURRENT_MAX_SHIFT) as usize;
631 let current_ma = if current_idx < SRCPDO_CURRENT_VALUES.len() {
632 SRCPDO_CURRENT_VALUES[current_idx]
633 } else {
634 5000
635 };
636
637 let voltage_unit = (pdo_word & SRCPDO_VOLTAGE_MAX_MASK) as u16;
638 let voltage_mv = if is_epr {
639 voltage_unit * 200
640 } else {
641 voltage_unit * 100
642 };
643
644 let max_power_mw = (voltage_mv as u32 * current_ma as u32) / 1000;
645
646 Some(PDOInfo {
647 voltage_mv,
648 current_ma,
649 max_power_mw,
650 is_fixed,
651 pdo_index,
652 })
653 }
654
655 fn read_register<I2C, E>(&self, i2c: &mut I2C, reg: u8) -> Result<u8, Error<E>>
657 where
658 I2C: I2c<Error = E>,
659 {
660 let mut buf = [0u8; 1];
661 i2c.write(AP33772S_ADDR, &[reg])?;
662 i2c.read(AP33772S_ADDR, &mut buf)?;
663 Ok(buf[0])
664 }
665
666 fn read_word<I2C, E>(&self, i2c: &mut I2C, reg: u8) -> Result<u16, Error<E>>
668 where
669 I2C: I2c<Error = E>,
670 {
671 let mut buf = [0u8; 2];
672 i2c.write(AP33772S_ADDR, &[reg])?;
673 i2c.read(AP33772S_ADDR, &mut buf)?;
674 Ok(((buf[1] as u16) << 8) | (buf[0] as u16))
675 }
676
677 fn write_register<I2C, E>(&self, i2c: &mut I2C, reg: u8, value: u8) -> Result<(), Error<E>>
679 where
680 I2C: I2c<Error = E>,
681 {
682 i2c.write(AP33772S_ADDR, &[reg, value])?;
683 Ok(())
684 }
685
686 fn write_word<I2C, E>(&self, i2c: &mut I2C, reg: u8, value: u16) -> Result<(), Error<E>>
688 where
689 I2C: I2c<Error = E>,
690 {
691 let lsb = (value & 0xFF) as u8;
692 let msb = ((value >> 8) & 0xFF) as u8;
693 i2c.write(AP33772S_ADDR, &[reg, lsb, msb])?;
694 Ok(())
695 }
696}
697
698impl Default for AP33772S {
699 fn default() -> Self {
700 Self::new()
701 }
702}