Skip to main content

rppal_shift/
lib.rs

1//! This crate provides an interface, `Shifter` that makes it trivially easy to
2//! manipulate [shift registers][4] with a Raspberry Pi (thanks to [CuPi][1]).
3//! Internally it keeps track of each shift register's state, allowing you to
4//! manipulate each pin individually as if it were a regular GPIO pin!
5//!
6//! Why would you want to do this?  **The Raspberry Pi only has 17 usable GPIO
7//! pins**.  Pin expanders like the [MCP23017][2] can add up to 16 more per chip
8//! (at a cost of about ~$2-3/each) but they work over I2C which is *slow* (on
9//! the Raspberry Pi anyway).  With shift registers like the [74HC595][3]
10//! (~$0.05-0.10/each) you can add a *nearly infinite* amount of output pins and
11//! *refresh them as fast as the hardware supports*.  You can even use many
12//! sets of 3 pins to run multiple chains of shift registers in parallel.
13//!
14//! Realize your dream of controlling an enormous holiday lights display with a
15//! single Raspberry Pi using cupi_shift!
16//!
17//! # Example
18//!
19//! ```
20//! extern crate cupi_shift;
21//! use cupi_shift::Shifter;
22//!
23//! fn main() {
24//!     // First define which pins you're using for your shift register(s)
25//!     let (data_pin, latch_pin, clock_pin) = (29, 28, 27);
26//!
27//!     // Now create a new Shifter instance using those pins
28//!     let mut shifter = Shifter::new(data_pin, latch_pin, clock_pin);
29//!
30//!     // Next we need to call `add()` for each shift register and tell it how
31//!     // many pins they have
32//!     let pins = 8;
33//!     let sr0 = shifter.add(pins); // Starts tracking a new shift register
34//!
35//!     // Now we can set the state (aka data) of our shift register
36//!     shifter.set(sr0, 0b11111111, true); // Set all pins HIGH
37//! }
38//!
39//! ```
40//! # Note about pin numbering
41//!
42//! [CuPi][1] currently uses GPIO pin numbering.  So pin 40 (very last pin on
43//! the Raspberry Pi 2) is actually pin 29.  You can refer to this image to
44//! figure out which pin is which:
45//!
46//! http://pi4j.com/images/j8header-2b-large.png
47//!
48//! # Controlling individual pins
49//!
50//! That's all well and good (setting the state of all pins at once) but what if
51//! you want to control just one pin at a time?  You can do that too:
52//!
53//! ```
54//! // Set the 8th pin (aka pin 7) HIGH and apply this change immediately
55//! shifter.set_pin_high(sr0, 7, true); // NOTE: 3rd arg is 'apply'
56//! // Set the first pin (aka pin 0) LOW but don't apply just yet
57//! shifter.set_pin_low(sr0, 0, false);
58//! shifter.apply(); // Apply the change (the other way to apply changes)
59//! ```
60//!
61//! # Controlling multiple shift registers
62//!
63//! Every time you call `Shifter.add()` it will start tracking/controlling an
64//! additional shift register.  So if you have two shift registers chained
65//! together you can add and control them individually like so:
66//!
67//! ```
68//! let last = shifter.add(8); // Add an 8-pin shift register (sr_index: 0)
69//! let first = shifter.add(8); // Add another (sr_index: 1)
70//! // Set pin 0 HIGH on shift register 0 (all others LOW) but don't apply the change yet
71//! shifter.set(last, 0b00000001, false);
72//! // Set pin 7 HIGH on shift register 1 (all others LOW) and apply the change
73//! shifter.set(first, 0b10000000, true);
74//! ```
75//!
76//! **Note:** Shift registers need to be added in the order in which they are
77//! chained with the *last* shift register being added first.  Why is the order
78//! reversed like this?  That's how the logic of shift registers works:  Every
79//! time data is "shifted out" to a shift register it dumps its memory to the
80//! the next shift register in the chain.
81//!
82//! You can also apply changes to individual pins on individual shift registers:
83//!
84//! ```
85//! shifter.set_pin_high(sr1, 2, false); // Set pin 2 HIGH on shift register 1
86//! shifter.set_pin_low(sr0, 3, true); // Set pin 3 LOW on shift register 0 (and apply)
87//! ```
88//!
89//! In the above example we didn't set the *apply* (3rd) argument to `true`
90//! until the we were done making our changes.  If we set *apply* to `true` on
91//! each we could wind up with some flickering.  The more shift registers you
92//! have in your chain the more flickering you can get if you call `apply()`
93//! with every state (aka data) change.
94//!
95//!
96//! [1]: https://crates.io/crates/cupi
97//! [2]: https://www.adafruit.com/product/732
98//! [3]: https://www.sparkfun.com/datasheets/IC/SN74HC595.pdf
99//! [4]: https://en.wikipedia.org/wiki/Shift_register
100
101#![allow(dead_code, unused_variables)]
102
103extern crate rppal;
104
105// Using a singly-linked list to represent the chain of shift registers since
106// it accurately represents how they're physically linked together.;
107use std::cell::RefCell;
108use rppal::gpio::{Gpio, OutputPin};
109
110
111struct ShiftRegister {
112    data: usize, // e.g. 0b01010101
113    pins: u8, // Not aware of any shift registers that have more than 255 output pins
114}
115
116// This is great for debugging; displays the Shift Register data in binary:
117impl std::fmt::Display for ShiftRegister {
118    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
119        let string = format!("{:b}", self.data);
120        let pad = (self.pins as usize) - string.len();
121        let _ = f.write_str("0b");
122        for _ in 0..pad { let _ = f.write_str("0").unwrap(); }
123        f.pad_integral(true, "", &string)
124    }
125}
126
127impl ShiftRegister {
128
129    fn set(&mut self, data: usize) {
130        self.data = data;
131    }
132
133    fn get_ref(self) -> RefCell<ShiftRegister> {
134        RefCell::new(self)
135    }
136}
137
138pub struct Shifter {
139    pub data: OutputPin,
140    pub latch: OutputPin,
141    pub clock: OutputPin,
142    shift_registers: Vec<ShiftRegister>,
143    invert: bool,
144}
145
146impl Shifter {
147
148    /// Returns a new `Shifter` object that will shift out data using the given
149    /// *data_pin*, *latch_pin*, and *clock_pin*.  To use a `Shifter` instance
150    /// you must first call the `add()` method for each shift register you
151    /// have connected in sequence.
152    ///
153    /// # Note about pin numbering
154    ///
155    /// `cupi` currently uses GPIO pin numbering.  So pin 40 (very last pin on
156    /// the Raspberry Pi 2) is actually pin 29.  You can refer to this image to
157    /// figure out which pin is which:
158    ///
159    /// http://pi4j.com/images/j8header-2b-large.png
160    pub fn new(data_pin: u8, latch_pin: u8, clock_pin: u8, num_shift_registers: u8) -> Shifter {
161        let gpio = Gpio::new().unwrap();
162        let shift_registers: Vec<ShiftRegister> = Vec::with_capacity(num_shift_registers as usize);
163        Shifter {
164            data: gpio.get(data_pin).unwrap().into_output_low(),
165            latch: gpio.get(latch_pin).unwrap().into_output_low(),
166            clock: gpio.get(clock_pin).unwrap().into_output_low(),
167            shift_registers: shift_registers,
168            invert: false,
169        }
170    }
171
172    /// Adds a new shift register to this Shifter and returns a reference to it.
173    /// You must specify the number of pins.
174    pub fn add(&mut self, pins: u8) -> usize {
175        let sr = ShiftRegister { data: 0, pins: pins };
176        self.shift_registers.push(sr);
177        self.shift_registers.len() - 1
178    }
179
180    /// Sets the *data* on the shift register at the given *sr_index*.
181    /// If *apply* is `true` the change will be applied immediately.
182    pub fn set(&mut self, sr_index: usize, data: usize, apply: bool) {
183        
184        // let mut _sr = self.shift_registers.get_mut(sr_index).unwrap();
185        // _sr.set(data);
186        for (i, sr) in self.shift_registers.iter_mut().enumerate() {
187            if i == sr_index {
188                sr.set(data);
189                break;
190            }
191        }
192        if apply { self.apply(); }
193    }
194
195    /// Sets the given *pin* HIGH on the shift register at the given *sr_index*.
196    /// If *apply* is `true` the change will be applied immediately.
197    pub fn set_pin_high(&mut self, sr_index: usize, pin: u8, apply: bool) {
198        for (i, sr) in self.shift_registers.iter_mut().enumerate() {
199            if i == sr_index {
200                let new_state = sr.data | 1 << pin;
201                sr.set(new_state);
202                break;
203            }
204        }
205        if apply { self.apply(); }
206    }
207
208    /// Sets the given *pin* LOW on the shift register at the given *sr_index*.
209    /// If *apply* is `true` the change will be applied immediately.
210    pub fn set_pin_low(&mut self, sr_index: usize, pin: u8, apply: bool) {
211        for (i, sr) in self.shift_registers.iter_mut().enumerate() {
212            if i == sr_index {
213                let new_state = sr.data & !(1 << pin);
214                sr.set(new_state);
215                break;
216            }
217        }
218        if apply { self.apply(); }
219    }
220
221    /// This function will invert all logic so that HIGH is LOW and LOW is HIGH.
222    /// Very convenient if you made a (very common) mistake in your wiring or
223    /// you need reversed logic for other reasons.
224    pub fn invert(&mut self) {
225        match self.invert {
226            true => self.invert = false,
227            false => self.invert = true,
228        }
229    }
230
231    /// Applies all current shift register states by shifting out all the stored
232    /// data in each ShiftRegister object.
233    pub fn apply(&mut self) {
234        self.latch.set_low();
235        self.clock.set_low();
236        std::thread::sleep(std::time::Duration::from_millis(1));
237        for sr in self.shift_registers.iter() {
238            for n in 0..sr.pins {
239                
240 
241                if self.invert {
242                    match sr.data >> n & 1 {
243                        1 => self.data.set_low(),
244                        0 => self.data.set_high(),
245                        _ => unreachable!(),
246                    }
247                } else {
248                    match sr.data >> n & 1 {
249                        0 => self.data.set_low(),
250                        1 => self.data.set_high(),
251                        _ => unreachable!(),
252                    }
253                }
254                self.clock.set_high();
255                std::thread::sleep(std::time::Duration::from_micros(100)); 
256                self.clock.set_low();
257                std::thread::sleep(std::time::Duration::from_micros(100));
258                if self.invert{self.data.set_low(); } else {self.data.set_high();}  // Forcing it to come down. 
259            }     
260        }
261        self.latch.set_high();
262    }
263
264}
265
266#[cfg(test)]
267mod tests {
268    #[test]
269    fn it_works() {
270    }
271}