stm32_extras/
lib.rs

1//! Extra API on top of STM32 device crates (`stm32f103xx`)
2//!
3//! # Examples
4//!
5//! Configuring GPIO pins without disturbing other pins (no read-modify-write which could lead to
6//! data races):
7//!
8//! ```rust,no_run
9//! # extern crate stm32_extras;
10//! # extern crate stm32f103xx;
11//! use stm32_extras::GPIOExtras;
12//! # fn main() {
13//! let gpioc = unsafe { &*stm32f103xx::GPIOC.get() }; // Get GPIOC somehow...
14//!
15//! // Set pin to 2Mhz, open-drain.
16//! // Modifies corresponding GPIO configuration bits without reads
17//! gpioc.pin_config(13).output2().open_drain();
18//! # }
19//! ```
20//!
21//! Generalized interface to GPIO pins:
22//!
23//! ```rust,no_run
24//! # extern crate stm32_extras;
25//! # extern crate stm32f103xx;
26//! use stm32_extras::GPIOExtras;
27//! # fn main() {
28//! let gpioc = unsafe { &*stm32f103xx::GPIOC.get() }; // Get GPIOC somehow...
29//!
30//! // Set pins 13, 14 and 15 on GPIOC to 1, 0 and 1.
31//! gpioc.write_pin_range(13, 3, 0b101);
32//! # }
33//! ```
34#![deny(missing_docs)]
35#![deny(warnings)]
36#![no_std]
37
38/// Convenient access to the bit blocks on GPIO ports.
39pub trait GPIOExtras<T> {
40    /// Set `count` bits on the GPIO port starting from the bit number `offset`. Other bits are not
41    /// affected. Uses BSRR register to set/clear individual bits.
42    /// Bits must fit into 16 bits of the GPIO port.
43    fn write_pin_range(&self, offset: usize, count: usize, data: u16);
44
45    /// Set single bit at the given `offset` in GPIO port. `offset` must be in the range 0..16.
46    fn write_pin(&self, offset: usize, bit: bool) {
47        self.write_pin_range(offset, 1, if bit { 1 } else { 0 });
48    }
49
50    /// Get `count` bits on the GPIO port starting from the bit number `offset`.
51    fn read_pin_range(&self, offset: usize, count: usize) -> u16;
52
53    /// Get single bit at the given `offset` in GPIO port. `offset` must be in the range 0..16.
54    fn read_pin(&self, offset: usize) -> bool {
55        self.read_pin_range(offset, 1) != 0
56    }
57
58    /// Get access to configuration bits for `pin` of GPIO port.
59    fn pin_config(&self, pin: usize) -> &T;
60}
61
62/// Common features for STM32F1/STM32W1 series.
63#[cfg(feature = "stm32f103xx")]
64mod stm32f1xx {
65    extern crate vcell;
66    use self::vcell::VolatileCell;
67
68    /// Pin configuration registers for STM32F1/STM32W1
69    pub struct GPIOBitbandConfigBlock {
70        mode_low: VolatileCell<u32>,
71        mode_high: VolatileCell<u32>,
72        cnf_low: VolatileCell<u32>,
73        cnf_high: VolatileCell<u32>,
74    }
75
76    impl GPIOBitbandConfigBlock {
77        /// Input mode (reset state)
78        pub fn input(&self) -> &Self {
79            self.mode_low.set(0);
80            self.mode_high.set(0);
81            self
82        }
83
84        /// Output mode, max speed 2 MHz.
85        pub fn output2(&self) -> &Self {
86            self.mode_low.set(0);
87            self.mode_high.set(1);
88            self
89        }
90
91        /// Output mode, max speed 10 MHz.
92        pub fn output10(&self) -> &Self {
93            self.mode_low.set(1);
94            self.mode_high.set(0);
95            self
96        }
97
98        /// Output mode, max speed 50 MHz.
99        pub fn output50(&self) -> &Self {
100            self.mode_low.set(1);
101            self.mode_high.set(1);
102            self
103        }
104
105        // Output config
106
107        /// Push-pull
108        pub fn push_pull(&self) -> &Self {
109            self.cnf_low.set(0);
110            self
111        }
112
113        /// Open-drain
114        pub fn open_drain(&self) -> &Self {
115            self.cnf_low.set(1);
116            self
117        }
118
119        /// General purpose
120        pub fn general(&self) -> &Self {
121            self.cnf_high.set(0);
122            self
123        }
124
125        /// Alternate function
126        pub fn alternate(&self) -> &Self {
127            self.cnf_high.set(1);
128            self
129        }
130
131        // Input config
132
133        /// Analog mode
134        pub fn analog(&self) -> &Self {
135            self.cnf_low.set(0);
136            self.cnf_high.set(0);
137            self
138        }
139
140        /// Floating input (reset state)
141        pub fn floating(&self) -> &Self {
142            // Ordering is important: should never get reserved value of `11`
143            self.cnf_high.set(0);
144            self.cnf_low.set(1);
145            self
146        }
147
148        /// Input with pull-up / pull-down
149        pub fn pull_up_down(&self) -> &Self {
150            self.cnf_low.set(0);
151            self.cnf_high.set(1);
152            self
153        }
154    }
155
156    /// GPIO port configuration bits
157    #[repr(C)]
158    pub struct GPIOBitbandRegisterBlock {
159        config: [GPIOBitbandConfigBlock; 16],
160    }
161
162    impl GPIOBitbandRegisterBlock {
163        /// Get pin configuration bits
164        pub fn config(&self, pin: usize) -> &GPIOBitbandConfigBlock {
165            &self.config[pin]
166        }
167    }
168}
169
170#[cfg(feature = "stm32f103xx")]
171mod stm32f103 {
172    extern crate stm32f103xx;
173    use self::stm32f103xx::gpioa;
174    use super::stm32f1xx::GPIOBitbandRegisterBlock;
175    use super::stm32f1xx::GPIOBitbandConfigBlock;
176    use super::GPIOExtras;
177
178    const PERIPHERALS_BASE: usize = 0x4000_0000;
179    const PERIPHERALS_ALIAS: usize = 0x4200_0000;
180
181    fn to_bitband_address<S, T>(port: &T) -> &'static S {
182        let byte_offset = (port as *const T as usize) - PERIPHERALS_BASE;
183        let address = PERIPHERALS_ALIAS + byte_offset * 32;
184        let ptr = address as *const S;
185        unsafe { &*ptr }
186    }
187
188    impl GPIOExtras<GPIOBitbandConfigBlock> for gpioa::RegisterBlock {
189        fn write_pin_range(&self, offset: usize, count: usize, data: u16) {
190            let mask = (1 << count) - 1;
191            let bits = u32::from(data & mask) | // Set '1's
192                (u32::from(!data & mask) << 16); // Clear '0's
193            self.bsrr.write(|w| unsafe { w.bits(bits << offset) });
194        }
195
196        fn read_pin_range(&self, offset: usize, count: usize) -> u16 {
197            let mask = (1 << count) - 1;
198            ((self.idr.read().bits() >> offset) as u16) & mask
199        }
200
201        fn pin_config(&self, pin: usize) -> &GPIOBitbandConfigBlock {
202            let registers: &GPIOBitbandRegisterBlock = to_bitband_address(self);
203            &registers.config(pin)
204        }
205    }
206}