|
15 | 15 | - [Fallibility](#fallibility)
|
16 | 16 | - [SPI transfer return type](#spi-transfer-return-type)
|
17 | 17 | - [Error type bounds](#error-type-bounds)
|
| 18 | +- [GPIO traits now require `&mut self`](#gpio-traits-now-require-mut-self) |
18 | 19 | - [Prelude](#prelude)
|
19 | 20 | - [Removed blanket implementations](#removed-blanket-implementations)
|
20 | 21 | - [Cargo Features](#cargo-features)
|
@@ -330,6 +331,95 @@ fn set_some_parameter(&mut self) -> Result<(), Self::Error> {
|
330 | 331 | }
|
331 | 332 | ```
|
332 | 333 |
|
| 334 | +## GPIO traits now require `&mut self` |
| 335 | + |
| 336 | +Methods on `InputPin` and `State` now take `&mut self` instead of `&self`, to allow implementations to |
| 337 | +have mutable state or access exclusive resources. |
| 338 | + |
| 339 | +**For HAL implementors**: You should not need to do any changes, since `&mut self` is strictly more permissive |
| 340 | +for implementations. |
| 341 | + |
| 342 | +For ease of use, you might want to provide inherent methods that take `&self` if the hardware permits it. In this case, |
| 343 | +you might need to do `&*self` to call them from the trait methods. Otherwise Rust will resolve the |
| 344 | +method call to the trait method, causing infinite recursion. |
| 345 | + |
| 346 | +```rust |
| 347 | +struct HalPin; |
| 348 | + |
| 349 | +impl HalPin { |
| 350 | + fn is_high(&self) -> bool { |
| 351 | + true |
| 352 | + } |
| 353 | + |
| 354 | + fn is_low(&self) -> bool { |
| 355 | + true |
| 356 | + } |
| 357 | +} |
| 358 | + |
| 359 | +impl InputPin for HalPin { |
| 360 | + fn is_high(&mut self) -> Result<bool, Self::Error> { |
| 361 | + // Needs `&*self` so that the inherent `is_high` is picked. |
| 362 | + Ok((&*self).is_high()) |
| 363 | + } |
| 364 | + |
| 365 | + fn is_low(&mut self) -> Result<bool, Self::Error> { |
| 366 | + Ok((&*self).is_low()) |
| 367 | + } |
| 368 | +} |
| 369 | +``` |
| 370 | + |
| 371 | +**For driver authors**: If your driver does not need sharing input pins, you should be able to upgrade without any changes. |
| 372 | +If you do need to share input pins, the recommended solution is wrapping them with a `RefCell`. |
| 373 | + |
| 374 | +Note that if you need to share multiple objects, you should prefer using a single `RefCell` wherever possible to reduce RAM |
| 375 | +usage. Make an "inner" struct with all the objects that need sharing, and wrap it in a single `RefCell`. Below is an example |
| 376 | +skeleton of a keypad driver using row/column multiplexing, sharing multiple `InputPin`s and `OutputPin`s with a single `RefCell`: |
| 377 | + |
| 378 | +```rust |
| 379 | +use core::cell::RefCell; |
| 380 | + |
| 381 | +use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; |
| 382 | + |
| 383 | +pub struct Keypad<O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> { |
| 384 | + inner: RefCell<KeypadInner<O, I, NCOLS, NROWS>>, |
| 385 | +} |
| 386 | + |
| 387 | +struct KeypadInner<O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> { |
| 388 | + cols: [O; NCOLS], |
| 389 | + rows: [I; NROWS], |
| 390 | +} |
| 391 | + |
| 392 | +pub struct KeypadInput<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> { |
| 393 | + inner: &'a RefCell<KeypadInner<O, I, NCOLS, NROWS>>, |
| 394 | + row: usize, |
| 395 | + col: usize, |
| 396 | +} |
| 397 | + |
| 398 | +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> ErrorType for KeypadInput<'a, O, I, NCOLS, NROWS> { |
| 399 | + type Error = core::convert::Infallible; |
| 400 | +} |
| 401 | + |
| 402 | +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> InputPin for KeypadInput<'a, O, I, NCOLS, NROWS> { |
| 403 | + fn is_high(&mut self) -> Result<bool, Self::Error> { |
| 404 | + Ok(!self.is_low()?) |
| 405 | + } |
| 406 | + |
| 407 | + fn is_low(&mut self) -> Result<bool, Self::Error> { |
| 408 | + let inner = &mut *self.inner.borrow_mut(); |
| 409 | + let row = &mut inner.rows[self.row]; |
| 410 | + let col = &mut inner.cols[self.col]; |
| 411 | + |
| 412 | + // using unwrap for demo purposes, you should propagate errors up instead. |
| 413 | + col.set_low().unwrap(); |
| 414 | + let out = row.is_low().unwrap(); |
| 415 | + col.set_high().unwrap(); |
| 416 | + |
| 417 | + Ok(out) |
| 418 | + } |
| 419 | +} |
| 420 | +``` |
| 421 | + |
| 422 | + |
333 | 423 | ## Prelude
|
334 | 424 |
|
335 | 425 | The prelude has been removed because it could make method calls ambiguous, since the method names are now
|
|
0 commit comments