analog_multiplexer/
lib.rs

1#![no_std]
2//! This crate provides an interface, `Multiplexer` that makes it trivially easy
3//! to select channels on any given 74HC4051 or 74HC4067 series analog multiplexer.
4//! Internally it keeps track of each multiplexer's state, allowing you to
5//! check what channel is presently active or to enable/disable the multiplexer
6//! at will.
7//!
8//! # Example using a 74HC4067 with a Blue Pill (stm32f104) board
9//!
10//! ```
11//! // NOTE: This is pseudocode. It's just meant to get the concept across :)
12//! use analog_multiplexer::Multiplexer; // Important part
13//!
14//! use stm32f1xx_hal::gpio::State;
15//! // The pins we're using:
16//! use stm32f1xx_hal::gpio::gpiob::{PB0, PB5, PB12, PB13, PB14, PB15};
17//! use stm32f1xx_hal::{adc}; // So we can read an analog pin (PB0)
18//!
19//! fn main() {
20//!     // stm32f1xx_hal boilerplate...
21//!     let device = pac::Peripherals::take().unwrap();
22//!     let mut flash = device.FLASH.constrain();
23//!     let mut rcc = device.RCC.constrain();
24//!     let mut _afio = device.AFIO.constrain(&mut rcc.apb2);
25//!     let _clocks = rcc
26//!         .cfgr
27//!         .use_hse(8.mhz())
28//!         .sysclk(72.mhz())
29//!         .pclk1(36.mhz())
30//!         .freeze(&mut flash.acr);
31//!     // Setup ADC (we're using ADC1 for this example since we're reading PB0)
32//!     let adc1 = adc::Adc::adc1(device.ADC1, &mut rcc.apb2, _clocks);
33//!     // Setup GPIOB (so we can access the ADC via PB0)
34//!     let mut gpiob = device.GPIOB.split(&mut rcc.apb2);
35//!     // Configure PB0 as an analog input (all channels lead to this analog input pin!)
36//!     let analog_pin = gpiob.pb0.into_analog(&mut gpiob.crl);
37//!     // Setup PB12-PB15 for accessing S0-S3 on the 74HC4067 multiplexer
38//!     let s0 = gpiob
39//!         .pb12
40//!         .into_push_pull_output_with_state(&mut gpiob.crh, State::Low);
41//!     let s1 = gpiob
42//!         .pb13
43//!         .into_push_pull_output_with_state(&mut gpiob.crh, State::Low);
44//!     let s2 = gpiob
45//!         .pb14
46//!         .into_push_pull_output_with_state(&mut gpiob.crh, State::Low);
47//!     let s3 = gpiob
48//!         .pb15
49//!         .into_push_pull_output_with_state(&mut gpiob.crh, State::Low);
50//!     // NOTE: On some multiplexers the S0-S3 pins are labeled A, B, C, D
51//!     // Enable pin...  If you want to be able to enable/disable the multiplexer on-the-fly
52//!     let en = gpiob
53//!         .pb5
54//!         .into_push_pull_output_with_state(&mut gpiob.crl, State::Low);
55//!     // TIP: Just run a wire from EN to GND to keep it enabled all the time
56//!     // Multiplexer pins are given as a tuple in the order S0-S3 then enable pin (EN):
57//!     let pins = (s0,s1,s2,s3,en); // For 16-channel
58//!     // let pins = (s0,s1,s2,en); // For 8-channel
59//!     let mut multiplexer = Multiplexer::new(pins); // The important part!
60//!     multiplexer.enable(); // Make sure it's enabled (if using EN pin)
61//!     loop {
62//!         for chan in 0..multiplexer.num_channels {
63//!             multiplexer.set_channel(chan); // Change the channel
64//!             let data: u16 = adc1.read(&mut *analog_pin).unwrap();
65//!             // Do something with the data here
66//!         }
67//!     }
68//! }
69//!
70//! ```
71//!
72//! **NOTE:** There's a working Blue Pill/RTIC example in the `examples` directory.
73//!
74
75extern crate embedded_hal as hal;
76
77use core::convert::Infallible;
78use hal::digital::v2::OutputPin;
79
80/// Provides an interface for setting the active channel
81/// and enabling/disabling an 8-channel (74HC4051) or
82/// 16-channel (74HC4067) analog multiplexer.  It also
83/// keeps track of which channel is currently active
84/// (`active_channel`) and provides a convenient
85/// `num_channels` field that can be used to iterate
86/// over all the multiplexer's channels.
87pub struct Multiplexer<Pins> {
88    pub pins: Pins,
89    pub num_channels: u8,
90    pub active_channel: u8,
91    pub enabled: bool,
92}
93
94/// A trait so we can support both 8-channel and 16-channel
95/// multiplexers simultaneously by merely instantiating them
96/// with a 5 (16-channel) or 4 (8-channel) member tuple of
97/// `OutputPin`s.
98pub trait Output {
99    fn set_channel(&mut self, channel: u8);
100    fn enable(&mut self);
101    fn disable(&mut self);
102    fn num_channels(&self) -> u8;
103}
104
105/// A 5-pin implementation to support 16-channel multiplexers (e.g. 74HC4067)
106impl<
107        E,
108        S0: OutputPin<Error = E>, // aka "A"
109        S1: OutputPin<Error = E>, // aka "B"
110        S2: OutputPin<Error = E>, // aka "C"
111        S3: OutputPin<Error = E>, // aka "D"
112        EN: OutputPin<Error = E>, // aka "Inhibit"
113    > Output for (S0, S1, S2, S3, EN)
114{
115    /// Sets the current active channel on the multiplexer (0-15)
116    fn set_channel(&mut self, channel: u8) {
117        // NOTE: Figuring out the binary math on this was not fun.  Not fun at all!
118        // Thanks to @grantm11235:matrix.org for showing me the way =)
119        if channel & (1 << 0) == 0 {
120            self.0.set_low().ok();
121        } else {
122            self.0.set_high().ok();
123        }
124
125        if channel & (1 << 1) == 0 {
126            self.1.set_low().ok();
127        } else {
128            self.1.set_high().ok();
129        }
130
131        if channel & (1 << 2) == 0 {
132            self.2.set_low().ok();
133        } else {
134            self.2.set_high().ok();
135        }
136
137        if channel & (1 << 3) == 0 {
138            self.3.set_low().ok();
139        } else {
140            self.3.set_high().ok();
141        }
142    }
143
144    /// Brings the `EN` pin low to enable the multiplexer
145    fn enable(&mut self) {
146        self.4.set_low().ok();
147    }
148
149    /// Brings the `EN` pin high to disable the multiplexer
150    fn disable(&mut self) {
151        self.4.set_high().ok();
152    }
153
154    /// Returns the number of channels supported by this multiplexer
155    /// (so you can easily iterate over them).
156    fn num_channels(&self) -> u8 {
157        16
158    }
159}
160
161/// A 4-pin implementation to support 8-channel multiplexers (e.g. 74HC4051)
162impl<
163        E,
164        S0: OutputPin<Error = E>,
165        S1: OutputPin<Error = E>,
166        S2: OutputPin<Error = E>,
167        EN: OutputPin<Error = E>,
168    > Output for (S0, S1, S2, EN)
169{
170    /// Sets the current active channel on the multiplexer (0-7)
171    fn set_channel(&mut self, channel: u8) {
172        if channel & (1 << 0) == 0 {
173            self.0.set_low().ok();
174        } else {
175            self.0.set_high().ok();
176        }
177
178        if channel & (1 << 1) == 0 {
179            self.1.set_low().ok();
180        } else {
181            self.1.set_high().ok();
182        }
183
184        if channel & (1 << 2) == 0 {
185            self.2.set_low().ok();
186        } else {
187            self.2.set_high().ok();
188        }
189    }
190
191    /// Brings the `EN` pin low to enable the multiplexer
192    fn enable(&mut self) {
193        self.3.set_low().ok();
194    }
195
196    /// Brings the `EN` pin high to disable the multiplexer
197    fn disable(&mut self) {
198        self.3.set_high().ok();
199    }
200
201    /// Returns the number of channels supported by this multiplexer
202    /// (so you can easily iterate over them).
203    fn num_channels(&self) -> u8 {
204        8
205    }
206}
207
208impl<Pins: Output> Multiplexer<Pins> {
209    /// Given a 5 or 4-member tuple, `(s0, s1, s2, s3, en)` or
210    /// `(s0, s1, s2, en)` where every member is an `OutputPin`,
211    /// returns a new instance of `Multiplexer` for a
212    /// 16-channel or 8-channel analog multiplexer, respectively.
213    ///
214    /// **NOTE:** Some multiplexers label S0-S3 as A-D. They're
215    /// the same thing.
216    pub fn new(mut pins: Pins) -> Self {
217        // Default to enabled on channel 0
218        let active_channel = 0;
219        let enabled = true;
220        pins.enable();
221        pins.set_channel(0);
222        // For quick reference later:
223        let num_channels = pins.num_channels();
224
225        Self {
226            pins,
227            num_channels,
228            active_channel,
229            enabled,
230        }
231    }
232
233    /// Sets the current active channel on the multiplexer
234    /// (0 up to `num_channels`) and records that state in
235    /// `self.active_channel`
236    pub fn set_channel(&mut self, channel: u8) {
237        self.pins.set_channel(channel);
238        self.active_channel = channel;
239    }
240
241    /// Enables the multiplexer and sets `self.enabled = true`
242    pub fn enable(&mut self) {
243        self.pins.enable();
244        self.enabled = true;
245    }
246
247    /// Disables the multiplexer and sets `self.enabled = false`
248    pub fn disable(&mut self) {
249        self.pins.enable();
250        self.enabled = false;
251    }
252}
253
254/// A DummyPin for when you've got your EN (enable) pin run to GND
255/// (the analog multiplexer is always enabled)
256pub struct DummyPin;
257impl OutputPin for DummyPin {
258    type Error = Infallible;
259    fn set_low(&mut self) -> Result<(), Self::Error> {
260        Ok(())
261    }
262    fn set_high(&mut self) -> Result<(), Self::Error> {
263        Ok(())
264    }
265}