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}