rustberry/peripherals/
gpio.rs

1// Copyright (C) 2018  Adam Gausmann
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Frontend for the GPIO register map.
17
18use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
19use std::thread;
20use std::time::Duration;
21
22use error::{Error, ErrorKind};
23use peripherals::consts::*;
24use system::System;
25use util::mem::RegisterMap;
26
27static RESERVED: AtomicBool = ATOMIC_BOOL_INIT; //false
28
29pub use peripherals::consts::GP_PINS as GPIO_PINS;
30
31/// Low-level (but safe) access to the GPIO register segment.
32pub struct Gpio {
33    registers: RegisterMap,
34}
35
36impl Gpio {
37
38    /// Attempts to reserve the GPIO register segment, returning it upon
39    /// success.
40    ///
41    /// # Errors
42    ///
43    /// This struct is a singleton object to enforce single ownership of the
44    /// GPIO pins. Returns `Error::Reserved` if another object exists.
45    pub fn new() -> Result<Gpio, Error> {
46        if RESERVED.fetch_or(true, Ordering::SeqCst) {
47            return Err(Error::new(ErrorKind::Reserved));
48        }
49
50        Ok(Gpio {
51            registers: unsafe { RegisterMap::map(
52                System::detect()?.peripheral_offset() + GP_PAGE_OFFSET,
53                PAGE_SIZE,
54            )}?,
55        })
56    }
57
58    /// Checks whether the pin number corresponds to a valid pin, returning
59    /// `true` if so.
60    pub fn is_valid_pin(pin: usize) -> bool {
61        pin < GPIO_PINS
62    }
63
64    /// Convenience wrapper around `is_valid_pin` for use with `try!`, mapping
65    /// `true` to `Ok` and `false` to `Err` with the kind `OutOfRange`.
66    pub fn check_pin(pin: usize) -> Result<(), Error> {
67        if Gpio::is_valid_pin(pin) {
68            Ok(())
69        } else {
70            Err(Error::new(ErrorKind::OutOfRange))
71        }
72    }
73
74    /// Sets the pin function using the `GP_FSEL` register.
75    ///
76    /// # Panics
77    ///
78    /// Panics if the given pin number is out of range.
79    pub fn set_function(&mut self, pin: usize, function: PinFunction) {
80        let mut reg = self.registers.load(GP_FSEL[pin]);
81        reg &= !GP_FSEL_MASK[pin];
82        reg |= function.value() << GP_FSEL_SHIFT[pin];
83        self.registers.store(GP_FSEL[pin], reg);
84    }
85
86    /// Sets the pin's pull value using `GP_PUD` and `GP_PUDCLK`.
87    ///
88    /// The input level corresponds to the pin's physical voltage level,
89    /// with `Some(High)` being pulled to source voltage (3.3v) and
90    /// `Some(Low)` being pulled to ground (0v). `None` is used if no pull
91    /// is desired, leaving the pin floating if disconnected.
92    ///
93    /// # Panics
94    ///
95    /// Panics if the given pin number is out of range.
96    pub fn set_pull(&mut self, pin: usize, pull: Option<PinLevel>) {
97        let pull_value = match pull {
98            None                 => 0b00,
99            Some(PinLevel::Low)  => 0b01,
100            Some(PinLevel::High) => 0b10,
101        };
102
103        self.registers.store(GP_PUD, pull_value);
104        thread::sleep(Duration::from_micros(5));
105
106        self.registers.store(GP_PUDCLK[pin], GP_PIN[pin]);
107        thread::sleep(Duration::from_micros(5));
108
109        self.registers.store(GP_PUD, 0);
110        self.registers.store(GP_PUDCLK[pin], GP_PIN[pin]);
111    }
112
113    /// Gets the pin's current level by reading the `GP_LEV` register.
114    ///
115    /// This is only guaranteed to work properly if the pin's function is set
116    /// to `In`.
117    ///
118    /// The resulting level corresponds to the pin's physical voltage level,
119    /// with `High` being the source voltage (3.3v) and `Low` being ground
120    /// (0v).
121    ///
122    /// # Panics
123    ///
124    /// Panics if the given pin number is out of range.
125    pub fn get_level(&self, pin: usize) -> PinLevel {
126        let mut reg = self.registers.load(GP_LEV[pin]);
127        reg &= GP_PIN[pin];
128
129        if reg == 0 {
130            PinLevel::Low
131        } else {
132            PinLevel::High
133        }
134    }
135
136    /// Sets the pin's level by wrapping the lower-level `set_pin` and
137    /// `clear_pin` methods.
138    ///
139    /// This is only guaranteed to work properly if the pin's function is set
140    /// to `Out`.
141    ///
142    /// The input level corresponds to the pin's resulting physical voltage
143    /// level, with `High` being the source voltage (3.3v) and `Low` being
144    /// ground (0v).
145    ///
146    /// # Panics
147    ///
148    /// Panics if the given pin number is out of range.
149    pub fn set_level(&mut self, pin: usize, level: PinLevel){
150        match level {
151            PinLevel::High => self.set_pin(pin),
152            PinLevel::Low  => self.clear_pin(pin),
153        };
154    }
155
156    /// Sets the pin's level to high (the source voltage, 3.3v) by writing to
157    /// the `GP_SET` register.
158    ///
159    /// This is only guaranteed to work properly if the pin's function is set
160    /// to `Out`.
161    ///
162    /// # Panics
163    ///
164    /// Panics if the given pin number is out of range.
165    pub fn set_pin(&mut self, pin: usize) {
166        self.registers.store(GP_SET[pin], GP_PIN[pin]);
167    }
168
169    /// Sets the pin's level to low (ground, 0v) by writing to the `GP_CLR`
170    /// register.
171    ///
172    /// This is only guaranteed to work properly if the pin's function is set
173    /// to `Out`.
174    ///
175    /// # Panics
176    ///
177    /// Panics if the given pin number is out of range.
178    pub fn clear_pin(&mut self, pin: usize) {
179        self.registers.store(GP_CLR[pin], GP_PIN[pin]);
180    }
181}
182
183/// Defines the two primary states a pin can be in.
184/// 
185/// The value of this enum is not directly tied to logic level or physical
186/// voltage; its meaning as a parameter or return value should be documented in
187/// the function that uses it.
188#[derive(Debug, Copy, Clone, PartialEq, Eq)]
189pub enum PinLevel {
190
191    /// A logic 0 or physical low (0v).
192    Low,
193
194    /// A logic 1 or physical high (3.3v).
195    High,
196}
197
198impl From<bool> for PinLevel {
199    fn from(x: bool) -> PinLevel {
200        match x {
201            false => PinLevel::Low,
202            true => PinLevel::High,
203        }
204    }
205}
206
207impl From<PinLevel> for bool {
208    fn from(x: PinLevel) -> bool {
209        match x {
210            PinLevel::Low => false,
211            PinLevel::High => true,
212        }
213    }
214}
215
216/// Enumeration of the functions that pins can have.
217///
218/// This is a low-level interface to pin functions, and the meaning of each
219/// `Alt*` value varies depending on which pin it is applied to. This is
220/// managed automatically by the higher-level `Peripherals` interface.
221///
222/// See [this comprehensive table][table] compiled and published by eLinux.org
223/// for details about how each function applies to each pin.
224///
225/// [table]: https://elinux.org/RPi_BCM2835_GPIOs
226#[derive(Debug, Copy, Clone, PartialEq, Eq)]
227#[allow(dead_code)]
228#[allow(missing_docs)] // Nothing meaningful to put there.
229pub enum PinFunction {
230    In,
231    Out,
232    Alt0,
233    Alt1,
234    Alt2,
235    Alt3,
236    Alt4,
237    Alt5,
238}
239
240impl PinFunction {
241
242    /// Maps the function to its value that is used in the `GP_FSEL*`
243    /// registers.
244    ///
245    /// This value is not shifted to account for a pin's specific location in
246    /// the register; that must be done manually with the constants provided by
247    /// `GP_FSEL_SHIFT`.
248    fn value(self) -> u32 {
249        match self {
250            PinFunction::In   => GP_FSEL_IN,
251            PinFunction::Out  => GP_FSEL_OUT,
252            PinFunction::Alt0 => GP_FSEL_ALT0,
253            PinFunction::Alt1 => GP_FSEL_ALT1,
254            PinFunction::Alt2 => GP_FSEL_ALT2,
255            PinFunction::Alt3 => GP_FSEL_ALT3,
256            PinFunction::Alt4 => GP_FSEL_ALT4,
257            PinFunction::Alt5 => GP_FSEL_ALT5,
258        }
259    }
260}