use crate::error::{EmbeddedError, Result};
use core::sync::atomic::{AtomicU8, AtomicU16, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum PowerMode {
HighPerformance = 0,
Balanced = 1,
LowPower = 2,
UltraLowPower = 3,
Sleep = 4,
DeepSleep = 5,
}
impl PowerMode {
pub const fn from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(Self::HighPerformance),
1 => Some(Self::Balanced),
2 => Some(Self::LowPower),
3 => Some(Self::UltraLowPower),
4 => Some(Self::Sleep),
5 => Some(Self::DeepSleep),
_ => None,
}
}
pub const fn cpu_freq_factor(&self) -> f32 {
match self {
Self::HighPerformance => 1.0,
Self::Balanced => 0.75,
Self::LowPower => 0.5,
Self::UltraLowPower => 0.25,
Self::Sleep => 0.0,
Self::DeepSleep => 0.0,
}
}
pub const fn allows_execution(&self) -> bool {
!matches!(self, Self::Sleep | Self::DeepSleep)
}
}
pub struct PowerManager {
current_mode: AtomicU8,
}
impl PowerManager {
pub const fn new() -> Self {
Self {
current_mode: AtomicU8::new(PowerMode::HighPerformance as u8),
}
}
pub fn current_mode(&self) -> PowerMode {
let mode_u8 = self.current_mode.load(Ordering::Relaxed);
PowerMode::from_u8(mode_u8).unwrap_or(PowerMode::HighPerformance)
}
pub fn request_mode(&self, mode: PowerMode) -> Result<()> {
let current = self.current_mode();
if !self.is_transition_allowed(current, mode) {
return Err(EmbeddedError::PowerModeTransitionFailed);
}
self.current_mode.store(mode as u8, Ordering::Release);
self.apply_mode(mode)?;
Ok(())
}
fn is_transition_allowed(&self, from: PowerMode, to: PowerMode) -> bool {
let _ = from;
let _ = to;
true
}
fn apply_mode(&self, mode: PowerMode) -> Result<()> {
match mode {
PowerMode::HighPerformance => self.apply_high_performance(),
PowerMode::Balanced => self.apply_balanced(),
PowerMode::LowPower => self.apply_low_power(),
PowerMode::UltraLowPower => self.apply_ultra_low_power(),
PowerMode::Sleep => self.apply_sleep(),
PowerMode::DeepSleep => self.apply_deep_sleep(),
}
}
fn apply_high_performance(&self) -> Result<()> {
Ok(())
}
fn apply_balanced(&self) -> Result<()> {
Ok(())
}
fn apply_low_power(&self) -> Result<()> {
Ok(())
}
fn apply_ultra_low_power(&self) -> Result<()> {
Ok(())
}
fn apply_sleep(&self) -> Result<()> {
#[cfg(feature = "esp32")]
{
}
#[cfg(feature = "riscv")]
{
use crate::target::riscv::power;
power::wait_for_interrupt()?;
}
Ok(())
}
fn apply_deep_sleep(&self) -> Result<()> {
Ok(())
}
}
impl Default for PowerManager {
fn default() -> Self {
Self::new()
}
}
pub struct PowerEstimator {
current_ma: u32,
voltage_mv: u32,
}
impl PowerEstimator {
pub const fn new(voltage_mv: u32) -> Self {
Self {
current_ma: 0,
voltage_mv,
}
}
pub fn set_current(&mut self, current_ma: u32) {
self.current_ma = current_ma;
}
pub const fn current_ma(&self) -> u32 {
self.current_ma
}
pub const fn power_mw(&self) -> u32 {
(self.current_ma as u64 * self.voltage_mv as u64 / 1000) as u32
}
pub fn battery_life_hours(&self, battery_mah: u32) -> f32 {
if self.current_ma == 0 {
return f32::INFINITY;
}
battery_mah as f32 / self.current_ma as f32
}
pub fn energy_per_op(&self, operation_time_us: u32) -> f32 {
let power_w = self.power_mw() as f32 / 1000.0;
let time_s = operation_time_us as f32 / 1_000_000.0;
power_w * time_s * 1000.0 }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WakeSource {
Timer,
Gpio,
Uart,
Touch,
Any,
}
pub struct SleepConfig {
pub wake_sources: heapless::Vec<WakeSource, 8>,
pub duration_us: Option<u64>,
pub keep_rtc: bool,
}
impl Default for SleepConfig {
fn default() -> Self {
Self {
wake_sources: heapless::Vec::new(),
duration_us: None,
keep_rtc: true,
}
}
}
impl SleepConfig {
pub const fn new() -> Self {
Self {
wake_sources: heapless::Vec::new(),
duration_us: None,
keep_rtc: true,
}
}
pub fn add_wake_source(&mut self, source: WakeSource) -> Result<()> {
self.wake_sources
.push(source)
.map_err(|_| EmbeddedError::BufferTooSmall {
required: 1,
available: 0,
})
}
pub fn set_timer_wake(&mut self, duration_us: u64) -> Result<()> {
self.duration_us = Some(duration_us);
self.add_wake_source(WakeSource::Timer)
}
}
pub struct VoltageRegulator {
voltage_mv: AtomicU16, }
impl VoltageRegulator {
pub const fn new(nominal_voltage_mv: u16) -> Self {
Self {
voltage_mv: AtomicU16::new(nominal_voltage_mv),
}
}
pub fn voltage_mv(&self) -> u16 {
self.voltage_mv.load(Ordering::Relaxed)
}
pub fn set_voltage(&self, voltage_mv: u16) -> Result<()> {
if !(500..=5000).contains(&voltage_mv) {
return Err(EmbeddedError::InvalidParameter);
}
self.voltage_mv.store(voltage_mv, Ordering::Release);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_power_mode_ordering() {
assert!(PowerMode::HighPerformance < PowerMode::LowPower);
assert!(PowerMode::LowPower < PowerMode::Sleep);
}
#[test]
fn test_power_manager() {
let pm = PowerManager::new();
assert_eq!(pm.current_mode(), PowerMode::HighPerformance);
pm.request_mode(PowerMode::LowPower)
.expect("mode change failed");
assert_eq!(pm.current_mode(), PowerMode::LowPower);
}
#[test]
fn test_power_estimator() {
let mut estimator = PowerEstimator::new(3300); estimator.set_current(100);
assert_eq!(estimator.power_mw(), 330);
let battery_life = estimator.battery_life_hours(1000); assert_eq!(battery_life, 10.0); }
#[test]
fn test_sleep_config() {
let mut config = SleepConfig::new();
config
.add_wake_source(WakeSource::Gpio)
.expect("add failed");
config.set_timer_wake(1_000_000).expect("set timer failed");
assert_eq!(config.wake_sources.len(), 2);
}
#[test]
fn test_voltage_regulator() {
let regulator = VoltageRegulator::new(3300);
assert_eq!(regulator.voltage_mv(), 3300);
regulator.set_voltage(1800).expect("voltage change failed");
assert_eq!(regulator.voltage_mv(), 1800);
assert!(regulator.set_voltage(6000).is_err());
}
}