Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constructor consistency update #2610

Merged
merged 9 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion documentation/API-GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ In general, the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines
- The peripheral instance type must default to a type that supports any of the peripheral instances.
- The author must to use `crate::any_peripheral` to define the "any" peripheral instance type.
- The driver must implement a `new` constructor that automatically converts the peripheral instance into the any type, and a `new_typed` that preserves the peripheral type.
- If a driver is configurable, configuration options should be implemented as a `Config` struct in the same module where the driver is located.
- The driver's constructor should take the config struct by value, and it should return `Result<Self, ConfigError>`.
- The `ConfigError` enum should be separate from other `Error` enums used by the driver.
- The driver should implement `fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError>`.
- In case the driver's configuration is infallible (all possible combinations of options are supported by the hardware), the `ConfigError` should be implemented as an empty `enum`.
bugadani marked this conversation as resolved.
Show resolved Hide resolved
- If a driver only supports a single peripheral instance, no instance type parameter is necessary.
- If a driver implements both blocking and async operations, or only implements blocking operations, but may support asynchronous ones in the future, the driver's type signature must include a `crate::Mode` type parameter.
- By default, constructors must configure the driver for blocking mode. The driver must implement `into_async` (and a matching `into_blocking`) function that reconfigures the driver.
- `into_async` must configure the driver and/or the associated DMA channels. This most often means enabling an interrupt handler.
- `into_blocking` must undo the configuration done by `into_async`.
- The asynchronous driver implemntation must also expose the blocking methods (except for interrupt related functions).
- The asynchronous driver implementation must also expose the blocking methods (except for interrupt related functions).
- Drivers must have a `Drop` implementation resetting the peripheral to idle state. There are some exceptions to this:
- GPIO where common usage is to "set and drop" so they can't be changed
- Where we don't want to disable the peripheral as it's used internally, for example SYSTIMER is used by `time::now()` API. See `KEEP_ENABLED` in src/system.rs
Expand Down
7 changes: 6 additions & 1 deletion esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586)
- `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586)
- Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614)
- Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610)

### Changed

Expand All @@ -44,6 +45,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `timer::Timer` has new trait requirements of `Into<AnyTimer>`, `'static` and `InterruptConfigurable` (#2586)
- `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586)
- A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614)
- Interrupt handling related functions are only provided for Blocking UART. (#2610)
- Changed how `Spi`, (split or unsplit) `Uart`, `LpUart`, `I8080`, `Camera`, `DPI` and `I2C` drivers are constructed (#2610)
- I8080, camera, DPI: The various standalone configuration options have been merged into `Config` (#2610)

### Fixed

Expand All @@ -58,7 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `FrozenUnit`, `AnyUnit`, `SpecificUnit`, `SpecificComparator`, `AnyComparator` have been removed from `systimer` (#2576)
- `esp_hal::psram::psram_range` (#2546)
- The `Dma` structure has been removed. (#2545)
- Remove `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593)
- Removed `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593)
- Removed `Camera::set_` functions (#2610)

## [0.22.0] - 2024-11-20

Expand Down
48 changes: 47 additions & 1 deletion esp-hal/MIGRATING-0.22.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ is enabled. To retrieve the address and size of the initialized external memory,
The usage of `esp_alloc::psram_allocator!` remains unchanged.


### embedded-hal 0.2.* is not supported anymore.
## embedded-hal 0.2.* is not supported anymore.

As per https://github.com/rust-embedded/embedded-hal/pull/640, our driver no longer implements traits from `embedded-hal 0.2.x`.
Analogs of all traits from the above mentioned version are available in `embedded-hal 1.x.x`
Expand Down Expand Up @@ -221,3 +221,49 @@ https://github.com/rust-embedded/embedded-hal/blob/master/docs/migrating-from-0.
+ use esp_hal::interrupt::InterruptConfigurable;
+ use esp_hal::interrupt::DEFAULT_INTERRUPT_HANDLER;
```

## Driver constructors now take a configuration and are fallible

The old `new_with_config` constructor have been removed, and `new` constructors now always take
a configuration structure. They have also been updated to return a `ConfigError` if the configuration
is not compatible with the hardware.

```diff
-let mut spi = Spi::new_with_config(
+let mut spi = Spi::new(
peripherals.SPI2,
Config {
frequency: 100.kHz(),
mode: SpiMode::Mode0,
..Config::default()
},
-);
+)
+.unwrap();
```

```diff
let mut spi = Spi::new(
peripherals.SPI2,
+ Config::default(),
-);
+)
+.unwrap();
```

### LCD_CAM configuration changes

- `cam` now has a `Config` strurct that contains frequency, bit/byte order, VSync filter options.
- DPI, I8080: `frequency` has been moved into `Config`.

```diff
+let mut cam_config = cam::Config::default();
+cam_config.frequency = 1u32.MHz();
cam::Camera::new(
lcd_cam.cam,
dma_rx_channel,
pins,
- 1u32.MHz(),
+ cam_config,
)
```
8 changes: 5 additions & 3 deletions esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
//! let mosi = peripherals.GPIO4;
//! let cs = peripherals.GPIO5;
//!
//! let mut spi = Spi::new_with_config(
//! let mut spi = Spi::new(
//! peripherals.SPI2,
//! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0)
//! )
//! .unwrap()
//! .with_sck(sclk)
//! .with_mosi(mosi)
//! .with_miso(miso)
Expand Down Expand Up @@ -1688,10 +1689,11 @@ impl<DEG: DmaChannel> DmaChannelConvert<DEG> for DEG {
#[cfg_attr(pdma, doc = "let dma_channel = peripherals.DMA_SPI2;")]
#[cfg_attr(gdma, doc = "let dma_channel = peripherals.DMA_CH0;")]
#[doc = ""]
/// let spi = Spi::new_with_config(
/// let spi = Spi::new(
/// peripherals.SPI2,
/// Config::default(),
/// );
/// )
/// .unwrap();
///
/// let spi_dma = configures_spi_dma(spi, dma_channel);
/// # }
Expand Down
16 changes: 12 additions & 4 deletions esp-hal/src/i2c/master/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
//! peripherals.I2C0,
//! Config::default(),
//! )
//! .unwrap()
//! .with_sda(peripherals.GPIO1)
//! .with_scl(peripherals.GPIO2);
//!
Expand Down Expand Up @@ -421,7 +422,10 @@ impl<'d> I2c<'d, Blocking> {
/// Create a new I2C instance
/// This will enable the peripheral but the peripheral won't get
/// automatically disabled when this gets dropped.
pub fn new(i2c: impl Peripheral<P = impl Instance> + 'd, config: Config) -> Self {
pub fn new(
i2c: impl Peripheral<P = impl Instance> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
Self::new_typed(i2c.map_into(), config)
}
}
Expand All @@ -433,7 +437,10 @@ where
/// Create a new I2C instance
/// This will enable the peripheral but the peripheral won't get
/// automatically disabled when this gets dropped.
pub fn new_typed(i2c: impl Peripheral<P = T> + 'd, config: Config) -> Self {
pub fn new_typed(
i2c: impl Peripheral<P = T> + 'd,
config: Config,
) -> Result<Self, ConfigError> {
crate::into_ref!(i2c);

let guard = PeripheralGuard::new(i2c.info().peripheral);
Expand All @@ -445,8 +452,9 @@ where
guard,
};

unwrap!(i2c.driver().setup(&i2c.config));
i2c
i2c.driver().setup(&i2c.config)?;

Ok(i2c)
}

// TODO: missing interrupt APIs
Expand Down
Loading