pwm_pca9685/
types.rs

1use crate::config::Config;
2use core::convert::TryFrom;
3use core::fmt::{Display, Formatter};
4
5#[cfg(feature = "std")]
6extern crate std;
7
8const DEVICE_BASE_ADDRESS: u8 = 0b100_0000;
9
10/// PCA9685 PWM/Servo/LED controller.
11#[derive(Debug)]
12pub struct Pca9685<I2C> {
13    /// The concrete I²C device implementation.
14    pub(crate) i2c: I2C,
15    /// The I²C device address.
16    pub(crate) address: u8,
17    /// Current device configuration.
18    pub(crate) config: Config,
19}
20
21/// All possible errors in this crate
22#[derive(Debug, PartialEq, Eq)]
23pub enum Error<E> {
24    /// I²C bus error
25    I2C(E),
26    /// Invalid input data provided
27    InvalidInputData,
28}
29
30// Implement Display for Error<E> if E also implements Display
31impl<E: Display> Display for Error<E> {
32    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
33        match self {
34            Error::I2C(e) => write!(f, "I²C bus error: {}", e),
35            Error::InvalidInputData => write!(f, "Invalid input data provided"),
36        }
37    }
38}
39
40#[cfg(feature = "std")]
41impl<E: std::error::Error> std::error::Error for Error<E> {}
42
43/// Output channel selection
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum Channel {
46    /// Channel 0
47    C0,
48    /// Channel 1
49    C1,
50    /// Channel 2
51    C2,
52    /// Channel 3
53    C3,
54    /// Channel 4
55    C4,
56    /// Channel 5
57    C5,
58    /// Channel 6
59    C6,
60    /// Channel 7
61    C7,
62    /// Channel 8
63    C8,
64    /// Channel 9
65    C9,
66    /// Channel 10
67    C10,
68    /// Channel 11
69    C11,
70    /// Channel 12
71    C12,
72    /// Channel 13
73    C13,
74    /// Channel 14
75    C14,
76    /// Channel 15
77    C15,
78    /// All channels
79    All,
80}
81macro_rules! match_channel {
82    ($value:expr, $($v:expr, $C:ident),*) => {
83        match $value {
84            $(
85                $v => Ok(Channel::$C),
86            )*
87            _ => Err(()),
88        }
89    };
90}
91
92macro_rules! impl_try_from_for_channel {
93    ($T:ty) => {
94        impl TryFrom<$T> for Channel {
95            type Error = ();
96
97            /// Will return an empty error for a value outside the range [0-15].
98            fn try_from(value: $T) -> Result<Self, Self::Error> {
99                match_channel!(
100                    value, 0, C0, 1, C1, 2, C2, 3, C3, 4, C4, 5, C5, 6, C6, 7, C7, 8, C8, 9, C9,
101                    10, C10, 11, C11, 12, C12, 13, C13, 14, C14, 15, C15
102                )
103            }
104        }
105    };
106}
107impl_try_from_for_channel!(u8);
108impl_try_from_for_channel!(u16);
109impl_try_from_for_channel!(usize);
110
111/// Output logic state inversion
112#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
113pub enum OutputLogicState {
114    /// Output logic state is not inverted (default).
115    ///
116    /// Value to set when external driver is used. Applicable when `OE = 0`.
117    #[default]
118    Direct,
119    /// Output logic state is inverted.
120    ///
121    /// Value to set when no external driver is used. Applicable when `OE = 0`.
122    Inverted,
123}
124
125/// Output state change behavior
126#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
127pub enum OutputStateChange {
128    /// Outputs change on STOP. (default)
129    ///
130    /// This will update the outputs all at the same time.
131    #[default]
132    OnStop,
133    /// Outputs change on ACK.
134    ///
135    /// This will update the outputs byte by byte.
136    OnAck,
137}
138
139/// Output driver configuration
140#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
141pub enum OutputDriver {
142    /// Totem pole configuration (default).
143    #[default]
144    TotemPole,
145    /// Open-drain configuration
146    OpenDrain,
147}
148
149/// Value set to all outputs when the output drivers are disabled (`OE` = 1).
150#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
151pub enum DisabledOutputValue {
152    /// Set all outputs to 0 (default).
153    #[default]
154    Zero,
155    /// Set all outputs to a value dependent on the `OutputDriver` configuration.
156    ///
157    /// - Set all outputs to 1 for `OutputDriver::TotemPole`.
158    /// - Set all outputs to high-impedance for `OutputDriver::OpenDrain`.
159    OutputDriver,
160    /// Set all outputs to high-impedance.
161    HighImpedance,
162}
163
164/// Additional programmable address types (volatile programming)
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub enum ProgrammableAddress {
167    /// Subaddress 1
168    Subaddress1,
169    /// Subaddress 2
170    Subaddress2,
171    /// Subaddress 3
172    Subaddress3,
173    /// LED all call address
174    AllCall,
175}
176
177/// I2C device address
178#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
179pub struct Address(pub(crate) u8);
180
181/// Default device address
182impl Default for Address {
183    fn default() -> Self {
184        Address(DEVICE_BASE_ADDRESS)
185    }
186}
187
188/// Support custom (integer) addresses
189impl From<u8> for Address {
190    fn from(a: u8) -> Self {
191        Address(a)
192    }
193}
194
195/// Compute device address from address bits
196impl From<(bool, bool, bool, bool, bool, bool)> for Address {
197    fn from(a: (bool, bool, bool, bool, bool, bool)) -> Self {
198        Address(
199            DEVICE_BASE_ADDRESS
200                | ((a.0 as u8) << 5)
201                | ((a.1 as u8) << 4)
202                | ((a.2 as u8) << 3)
203                | ((a.3 as u8) << 2)
204                | ((a.4 as u8) << 1)
205                | a.5 as u8,
206        )
207    }
208}
209
210/// PWM control values for a single channel
211#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
212pub struct ChannelOnOffControl {
213    /// Counter value to switch the channel on during each PWM cycle
214    pub on: u16,
215    /// Counter value to switch the channel off during each PWM cycle
216    pub off: u16,
217    /// Set the channel to full-on. In this case, the `off` value is ignored.
218    pub full_on: bool,
219    /// Set the channel to full-off. Takes precedence over `on` an `full_on`.
220    pub full_off: bool,
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    macro_rules! default_test {
228        ($name:ident, $type:ident, $default:ident) => {
229            #[test]
230            fn $name() {
231                assert_eq!($type::$default, $type::default());
232            }
233        };
234    }
235
236    default_test!(default_out_logic_state, OutputLogicState, Direct);
237    default_test!(default_out_change, OutputStateChange, OnStop);
238    default_test!(default_out_driver, OutputDriver, TotemPole);
239    default_test!(default_disabled_out_value, DisabledOutputValue, Zero);
240
241    #[test]
242    fn can_get_default_address() {
243        let addr = Address::default();
244        assert_eq!(DEVICE_BASE_ADDRESS, addr.0);
245    }
246}
247
248#[cfg(all(test, feature = "std"))]
249mod std_tests {
250    use super::*;
251    use std::format;
252
253    struct TestError;
254    impl Display for TestError {
255        fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
256            write!(f, "test")
257        }
258    }
259
260    #[test]
261    fn test_display_implementation_invalid_input_data() {
262        let expected = "Invalid input data provided";
263        let error = Error::<TestError>::InvalidInputData;
264        let actual = format!("{}", error);
265
266        assert_eq!(expected, actual)
267    }
268
269    #[test]
270    fn test_display_implementation_i2c_error() {
271        let expected = "I²C bus error: test";
272        let error = Error::<TestError>::I2C(TestError);
273        let actual = format!("{}", error);
274
275        assert_eq!(expected, actual)
276    }
277}