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}