diff --git a/.cargo/config.toml b/.cargo/config.toml index 35049cbcb13..9f81e304413 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,3 @@ [alias] xtask = "run --package xtask --" +xfmt = "xtask fmt-packages" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 4a7d24d845d..2f59525cc2f 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -2,16 +2,13 @@ name: Changelog check on: pull_request: - # We will not track changes for the following packages. + # We will not track changes for the following packages/directories. paths-ignore: - - "/xtask/" - - "/esp-build/" - - "/esp-hal-procmacros/" - - "/esp-metadata/" - "/examples/" - - "/hil-tests/" - "/extras/" + - "/hil-tests/" - "/resources/" + - "/xtask/" # Run on labeled/unlabeled in addition to defaults to detect # adding/removing skip-changelog labels. types: [opened, reopened, labeled, unlabeled, synchronize] @@ -33,24 +30,34 @@ jobs: - 'esp-alloc/**' esp-backtrace: - 'esp-backtrace/**' + esp-build: + - 'esp-build/**' + esp-config: + - 'esp-config/**' esp-hal: - 'esp-hal/**' esp-hal-embassy: - 'esp-hal-embassy/**' + esp-hal-procmacros: + - 'esp-hal-procmacros/**' esp-ieee802154: - 'esp-ieee802154/**' esp-lp-hal: - 'esp-lp-hal/**' + esp-metadata: + - 'esp-metadata/**' esp-println: - 'esp-println/**' esp-riscv-rt: - 'esp-riscv-rt/**' - xtensa-lx-rt: - - 'xtensa-lx-rt/**' esp-storage: - 'esp-storage/**' esp-wifi: - 'esp-wifi/**' + xtensa-lx: + - 'xtensa-lx/**' + xtensa-lx-rt: + - 'xtensa-lx-rt/**' - name: Check that changelog updated (esp-alloc) if: steps.changes.outputs.esp-alloc == 'true' @@ -68,6 +75,22 @@ jobs: skipLabels: "skip-changelog" missingUpdateErrorMessage: "Please add a changelog entry in the esp-backtrace/CHANGELOG.md file." + - name: Check that changelog updated (esp-build) + if: steps.changes.outputs.esp-build == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: esp-build/CHANGELOG.md + skipLabels: "skip-changelog" + missingUpdateErrorMessage: "Please add a changelog entry in the esp-build/CHANGELOG.md file." + + - name: Check that changelog updated (esp-config) + if: steps.changes.outputs.esp-config == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: esp-config/CHANGELOG.md + skipLabels: "skip-changelog" + missingUpdateErrorMessage: "Please add a changelog entry in the esp-config/CHANGELOG.md file." + - name: Check that changelog updated (esp-hal) if: steps.changes.outputs.esp-hal == 'true' uses: dangoslen/changelog-enforcer@v3 @@ -84,6 +107,14 @@ jobs: skipLabels: "skip-changelog" missingUpdateErrorMessage: "Please add a changelog entry in the esp-hal-embassy/CHANGELOG.md file." + - name: Check that changelog updated (esp-hal-procmacros) + if: steps.changes.outputs.esp-hal-procmacros == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: esp-hal-procmacros/CHANGELOG.md + skipLabels: "skip-changelog" + missingUpdateErrorMessage: "Please add a changelog entry in the esp-hal-procmacros/CHANGELOG.md file." + - name: Check that changelog updated (esp-ieee802154) if: steps.changes.outputs.esp-ieee802154 == 'true' uses: dangoslen/changelog-enforcer@v3 @@ -131,3 +162,19 @@ jobs: changeLogPath: esp-wifi/CHANGELOG.md skipLabels: "skip-changelog" missingUpdateErrorMessage: "Please add a changelog entry in the esp-wifi/CHANGELOG.md file." + + - name: Check that changelog updated (xtensa-lx) + if: steps.changes.outputs.xtensa-lx == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: xtensa-lx/CHANGELOG.md + skipLabels: "skip-changelog" + missingUpdateErrorMessage: "Please add a changelog entry in the xtensa-lx/CHANGELOG.md file." + + - name: Check that changelog updated (xtensa-lx-rt) + if: steps.changes.outputs.xtensa-lx-rt == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: xtensa-lx-rt/CHANGELOG.md + skipLabels: "skip-changelog" + missingUpdateErrorMessage: "Please add a changelog entry in the xtensa-lx-rt/CHANGELOG.md file." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79f6b994eb0..e8bef8ccac6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,10 +137,10 @@ jobs: - name: msrv RISCV (esp-wifi) run: | - cargo xtask build-package --features=esp32c2,wifi,ble,async --target=riscv32imc-unknown-none-elf esp-wifi - cargo xtask build-package --features=esp32c3,wifi,ble,async --target=riscv32imc-unknown-none-elf esp-wifi - cargo xtask build-package --features=esp32c6,wifi,ble,async --target=riscv32imac-unknown-none-elf esp-wifi - cargo xtask build-package --features=esp32h2,ble,async --target=riscv32imac-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32c2,wifi,ble --target=riscv32imc-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32c3,wifi,ble --target=riscv32imc-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32c6,wifi,ble --target=riscv32imac-unknown-none-elf esp-wifi + cargo xtask build-package --features=esp32h2,ble --target=riscv32imac-unknown-none-elf esp-wifi # Verify the MSRV for all Xtensa chips: - name: msrv Xtensa (esp-hal) @@ -151,9 +151,9 @@ jobs: - name: msrv Xtensa (esp-wifi) run: | - cargo xtask build-package --toolchain=esp --features=esp32,wifi,ble,async --target=xtensa-esp32-none-elf esp-wifi - cargo xtask build-package --toolchain=esp --features=esp32s2,wifi,async --target=xtensa-esp32s2-none-elf esp-wifi - cargo xtask build-package --toolchain=esp --features=esp32s3,wifi,ble,async --target=xtensa-esp32s3-none-elf esp-wifi + cargo xtask build-package --toolchain=esp --features=esp32,wifi,ble --target=xtensa-esp32-none-elf esp-wifi + cargo xtask build-package --toolchain=esp --features=esp32s2,wifi --target=xtensa-esp32s2-none-elf esp-wifi + cargo xtask build-package --toolchain=esp --features=esp32s3,wifi,ble --target=xtensa-esp32s3-none-elf esp-wifi - name: msrv (esp-lp-hal) run: | diff --git a/documentation/API-GUIDELINES.md b/documentation/API-GUIDELINES.md index b2e18e13eda..c0336070789 100644 --- a/documentation/API-GUIDELINES.md +++ b/documentation/API-GUIDELINES.md @@ -15,9 +15,21 @@ The following paragraphs contain additional recommendations. ## Construction and Destruction of Drivers -- Drivers take peripherals and pins via the `PeripheralRef` pattern - they don't consume peripherals/pins. +- Drivers should take peripherals via the `PeripheralRef` pattern - they don't consume peripherals directly. +- If a driver requires pins, those pins should be configured using `fn with_signal_name(self, pin: impl Peripheral

+ 'd) -> Self)` or `fn with_signal_name(self, pin: impl Peripheral

+ 'd) -> Self)` +- If a driver supports multiple peripheral instances (for example, I2C0 is one such instance): + - The peripheral instance type should be positioned as the last type parameter of the driver type. + - The peripheral instance type should default to a type that supports any of the peripheral instances. + - The author is encouraged to use `crate::any_peripheral` to define the "any" peripheral instance type. + - The driver should 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 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 should include a `crate::Mode` type parameter. +- By default, constructors should configure the driver for blocking mode. The driver should implement `into_async` (and a matching `into_blocking`) function that reconfigures the driver. + - `into_async` should configure the driver and/or the associated DMA channels. This most often means enabling an interrupt handler. + - `into_blocking` should undo the configuration done by `into_async`. +- The asynchronous driver implemntation should also expose the blocking methods (except for interrupt related functions). - Consider adding a `Drop` implementation resetting the peripheral to idle state. -- Consider using a builder-like pattern for configuration which must be done during initialization. +- Consider using a builder-like pattern for driver construction. ## Interoperability @@ -25,6 +37,7 @@ The following paragraphs contain additional recommendations. - see [this example](https://github.com/esp-rs/esp-hal/blob/df2b7bd8472cc1d18db0d9441156575570f59bb3/esp-hal/src/spi/mod.rs#L15) - e.g. `#[cfg_attr(feature = "defmt", derive(defmt::Format))]` - Don't use `log::XXX!` macros directly - use the wrappers in `fmt.rs` (e.g. just `info!` instead of `log::info!` or importing `log::*`)! +- Consider implementing common ecosystem traits, like the ones in `embedded-hal` or `embassy-embedded-hal`. ## API Surface @@ -45,6 +58,9 @@ The following paragraphs contain additional recommendations. - For example starting a timer is fine for `&self`, worst case a timer will be started twice if two parts of the program call it. You can see a real example of this [here](https://github.com/esp-rs/esp-hal/pull/1500#pullrequestreview-2015911974) - Maintain order consistency in the API, such as in the case of pairs like RX/TX. - If your driver provides a way to listen for interrupts, the interrupts should be listed in a `derive(EnumSetType)` enum as opposed to one function per interrupt flag. +- If a driver only implements a subset of a peripheral's capabilities, it should be placed in the `peripheral::subcategory` module. + - For example, if a driver implements the slave-mode I2C driver, it should be placed into `i2c::slave`. + - This helps us reducing the need of introducing breaking changes if we implement additional functionalities. ## Maintainability @@ -56,6 +72,15 @@ The following paragraphs contain additional recommendations. - All `Future` objects (public or private) must be marked with ``#[must_use = "futures do nothing unless you `.await` or poll them"]``. - Prefer `cfg_if!` over multiple exclusive `#[cfg]` attributes. `cfg_if!` visually divides the options, often results in simpler conditions and simplifies adding new branches in the future. +## Driver implementation + +- If a common `Instance` trait is used for multiple peripherals, those traits should not have any logic implemented in them. +- The `Instance` traits should only be used to access information about a peripheral instance. +- The internal implementation of the driver should be non-generic over the peripheral instance. This helps the compiler produce smaller code. +- The author is encouraged to return a static shared reference to an `Info` and a `State` structure from the `Instance` trait. + - The `Info` struct should describe the peripheral. Do not use any interior mutability. + - The `State` struct should contain counters, wakers and other, mutable state. As this is accessed via a shared reference, interior mutability and atomic variables are preferred. + ## Modules Documentation Modules should have the following documentation format: @@ -88,4 +113,5 @@ Modules should have the following documentation format: ``` #![doc = concat!("[ESP-IDF documentation](https://docs.espressif.com/projects/esp-idf/en/latest/", crate::soc::chip!(), "/api-reference/peripherals/etm.html)")] ``` -- Documentation examples should be short and basic, for more complex scenarios, create an example. + - In case of referencing a TRM chapter, use the `crate::trm_markdown_link!()` macro. If you are referring to a particular chapter, you may use `crate::trm_markdown_link!("#chapter_anchor")`. +- Documentation examples should be short and basic. For more complex scenarios, create an example. diff --git a/esp-build/CHANGELOG.md b/esp-build/CHANGELOG.md new file mode 100644 index 00000000000..50b75908901 --- /dev/null +++ b/esp-build/CHANGELOG.md @@ -0,0 +1,24 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed + +### Changed + +- Use `panic` instead of `process::exit` in esp-build (#2402 ) + +### Removed + +## [0.1.0] - 2024-04-17 + +- Initial release + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-build?since=2024-04-17 diff --git a/esp-config/CHANGELOG.md b/esp-config/CHANGELOG.md new file mode 100644 index 00000000000..4066e9b0323 --- /dev/null +++ b/esp-config/CHANGELOG.md @@ -0,0 +1,22 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed + +### Changed + +### Removed + +## [0.1.0] - 2024-10-10 + +- Initial release + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-config?since=2024-10-10 diff --git a/esp-config/src/generate.rs b/esp-config/src/generate.rs index 1afa1c92591..ede216f5d27 100644 --- a/esp-config/src/generate.rs +++ b/esp-config/src/generate.rs @@ -1,77 +1,246 @@ -use core::fmt::Write; -use std::{collections::HashMap, env, fs, path::PathBuf}; +use std::{ + collections::HashMap, + env, + fmt::{self, Write as _}, + fs, + ops::Range, + path::PathBuf, +}; const DOC_TABLE_HEADER: &str = r#" | Name | Description | Default value | |------|-------------|---------------| "#; -const CHOSEN_TABLE_HEADER: &str = r#" + +const SELECTED_TABLE_HEADER: &str = r#" | Name | Selected value | |------|----------------| "#; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ParseError(String); +/// Configuration errors. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Error { + /// Parse errors. + Parse(String), + /// Validation errors. + Validation(String), +} + +impl Error { + /// Convenience function for creating parse errors. + pub fn parse(message: S) -> Self + where + S: Into, + { + Self::Parse(message.into()) + } + + /// Convenience function for creating validation errors. + pub fn validation(message: S) -> Self + where + S: Into, + { + Self::Validation(message.into()) + } +} -#[derive(Clone, Debug, PartialEq, Eq)] +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Parse(message) => write!(f, "{message}"), + Error::Validation(message) => write!(f, "{message}"), + } + } +} + +/// Supported configuration value types. +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Value { - SignedInteger(isize), - UnsignedInteger(usize), + /// Booleans. Bool(bool), + /// Integers. + Integer(i128), + /// Strings. String(String), } +// TODO: Do we want to handle negative values for non-decimal values? impl Value { - fn parse_in_place(&mut self, s: &str) -> Result<(), ParseError> { + fn parse_in_place(&mut self, s: &str) -> Result<(), Error> { *self = match self { - Value::Bool(_) => match s.to_lowercase().as_ref() { - "false" | "no" | "n" | "f" => Value::Bool(false), - "true" | "yes" | "y" | "t" => Value::Bool(true), - _ => return Err(ParseError(format!("Invalid boolean value: {}", s))), - }, - Value::SignedInteger(_) => Value::SignedInteger( - match s.as_bytes() { - [b'0', b'x', ..] => isize::from_str_radix(&s[2..], 16), - [b'0', b'o', ..] => isize::from_str_radix(&s[2..], 8), - [b'0', b'b', ..] => isize::from_str_radix(&s[2..], 2), - _ => isize::from_str_radix(&s, 10), + Value::Bool(_) => match s { + "true" => Value::Bool(true), + "false" => Value::Bool(false), + _ => { + return Err(Error::parse(format!( + "Expected 'true' or 'false', found: '{s}'" + ))) } - .map_err(|_| ParseError(format!("Invalid signed numerical value: {}", s)))?, - ), - Value::UnsignedInteger(_) => Value::UnsignedInteger( - match s.as_bytes() { - [b'0', b'x', ..] => usize::from_str_radix(&s[2..], 16), - [b'0', b'o', ..] => usize::from_str_radix(&s[2..], 8), - [b'0', b'b', ..] => usize::from_str_radix(&s[2..], 2), - _ => usize::from_str_radix(&s, 10), + }, + Value::Integer(_) => { + let inner = match s.as_bytes() { + [b'0', b'x', ..] => i128::from_str_radix(&s[2..], 16), + [b'0', b'o', ..] => i128::from_str_radix(&s[2..], 8), + [b'0', b'b', ..] => i128::from_str_radix(&s[2..], 2), + _ => i128::from_str_radix(&s, 10), } - .map_err(|_| ParseError(format!("Invalid unsigned numerical value: {}", s)))?, - ), - Value::String(_) => Value::String(String::from(s)), + .map_err(|_| Error::parse(format!("Expected valid intger value, found: '{s}'")))?; + + Value::Integer(inner) + } + Value::String(_) => Value::String(s.into()), }; + Ok(()) } - fn as_string(&self) -> String { + /// Convert the value to a [bool]. + pub fn as_bool(&self) -> bool { + match self { + Value::Bool(value) => *value, + _ => panic!("attempted to convert non-bool value to a bool"), + } + } + + /// Convert the value to an [i128]. + pub fn as_integer(&self) -> i128 { + match self { + Value::Integer(value) => *value, + _ => panic!("attempted to convert non-integer value to an integer"), + } + } + + /// Convert the value to a [String]. + pub fn as_string(&self) -> String { + match self { + Value::String(value) => value.to_owned(), + _ => panic!("attempted to convert non-string value to a string"), + } + } + + /// Is the value a bool? + pub fn is_bool(&self) -> bool { + matches!(self, Value::Bool(_)) + } + + /// Is the value an integer? + pub fn is_integer(&self) -> bool { + matches!(self, Value::Integer(_)) + } + + /// Is the value a string? + pub fn is_string(&self) -> bool { + matches!(self, Value::String(_)) + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Value::Bool(b) => write!(f, "{b}"), + Value::Integer(i) => write!(f, "{i}"), + Value::String(s) => write!(f, "{s}"), + } + } +} + +/// Configuration value validation functions. +pub enum Validator { + /// Only allow negative integers, i.e. any values less than 0. + NegativeInteger, + /// Only allow non-negative integers, i.e. any values greater than or equal + /// to 0. + NonNegativeInteger, + /// Only allow positive integers, i.e. any values greater than to 0. + PositiveInteger, + /// Ensure that an integer value falls within the specified range. + IntegerInRange(Range), + /// A custom validation function to run against any supported value type. + Custom(Box Result<(), Error>>), +} + +impl Validator { + fn validate(&self, value: &Value) -> Result<(), Error> { match self { - Value::Bool(value) => String::from(if *value { "true" } else { "false" }), - Value::SignedInteger(value) => format!("{}", value), - Value::UnsignedInteger(value) => format!("{}", value), - Value::String(value) => value.clone(), + Validator::NegativeInteger => negative_integer(value)?, + Validator::NonNegativeInteger => non_negative_integer(value)?, + Validator::PositiveInteger => positive_integer(value)?, + Validator::IntegerInRange(range) => integer_in_range(range, value)?, + Validator::Custom(validator_fn) => validator_fn(value)?, } + + Ok(()) } } -/// Generate and parse config from a prefix, and array of key, default, -/// description tuples. +fn negative_integer(value: &Value) -> Result<(), Error> { + if !value.is_integer() { + return Err(Error::validation( + "Validator::NegativeInteger can only be used with integer values", + )); + } else if value.as_integer() >= 0 { + return Err(Error::validation(format!( + "Expected negative integer, found '{}'", + value.as_integer() + ))); + } + + Ok(()) +} + +fn non_negative_integer(value: &Value) -> Result<(), Error> { + if !value.is_integer() { + return Err(Error::validation( + "Validator::NonNegativeInteger can only be used with integer values", + )); + } else if value.as_integer() < 0 { + return Err(Error::validation(format!( + "Expected non-negative integer, found '{}'", + value.as_integer() + ))); + } + + Ok(()) +} + +fn positive_integer(value: &Value) -> Result<(), Error> { + if !value.is_integer() { + return Err(Error::validation( + "Validator::PositiveInteger can only be used with integer values", + )); + } else if value.as_integer() <= 0 { + return Err(Error::validation(format!( + "Expected positive integer, found '{}'", + value.as_integer() + ))); + } + + Ok(()) +} + +fn integer_in_range(range: &Range, value: &Value) -> Result<(), Error> { + if !value.is_integer() || !range.contains(&value.as_integer()) { + Err(Error::validation(format!( + "Value '{}' does not fall within range '{:?}'", + value, range + ))) + } else { + Ok(()) + } +} + +/// Generate and parse config from a prefix, and an array tuples containing the +/// name, description, default value, and an optional validator. /// /// This function will parse any `SCREAMING_SNAKE_CASE` environment variables -/// that match the given prefix. It will then attempt to parse the [`Value`]. +/// that match the given prefix. It will then attempt to parse the [`Value`] and +/// run any validators which have been specified. +/// /// Once the config has been parsed, this function will emit `snake_case` cfg's /// _without_ the prefix which can be used in the dependant crate. After that, /// it will create a markdown table in the `OUT_DIR` under the name -/// `{prefix}_config_table.md` where prefix has also been converted -/// to `snake_case`. This can be included in crate documentation to outline the +/// `{prefix}_config_table.md` where prefix has also been converted to +/// `snake_case`. This can be included in crate documentation to outline the /// available configuration options for the crate. /// /// Passing a value of true for the `emit_md_tables` argument will create and @@ -81,22 +250,46 @@ impl Value { /// Unknown keys with the supplied prefix will cause this function to panic. pub fn generate_config( prefix: &str, - config: &[(&str, Value, &str)], + config: &[(&str, &str, Value, Option)], emit_md_tables: bool, ) -> HashMap { - // only rebuild if build.rs changed. Otherwise Cargo will rebuild if any + // Only rebuild if `build.rs` changed. Otherwise, Cargo will rebuild if any // other file changed. println!("cargo:rerun-if-changed=build.rs"); + #[cfg(not(test))] env_change_work_around(); - // ensure that the prefix is `SCREAMING_SNAKE_CASE` - let prefix = format!("{}_", screaming_snake_case(prefix)); let mut doc_table = String::from(DOC_TABLE_HEADER); - let mut selected_config = String::from(CHOSEN_TABLE_HEADER); + let mut selected_config = String::from(SELECTED_TABLE_HEADER); + + // Ensure that the prefix is `SCREAMING_SNAKE_CASE`: + let prefix = screaming_snake_case(prefix); + + // Build a lookup table for any provided validators; we must prefix the + // name of the config and transform it to SCREAMING_SNAKE_CASE so that + // it matches the keys in the hash table produced by `create_config`. + let config_validators = config + .iter() + .flat_map(|(name, _description, _default, validator)| { + if let Some(validator) = validator { + let name = format!("{prefix}_{}", screaming_snake_case(name)); + Some((name, validator)) + } else { + None + } + }) + .collect::>(); let mut configs = create_config(&prefix, config, &mut doc_table); capture_from_env(&prefix, &mut configs); + + for (name, value) in configs.iter() { + if let Some(validator) = config_validators.get(name) { + validator.validate(value).unwrap(); + } + } + emit_configuration(&prefix, &configs, &mut selected_config); if emit_md_tables { @@ -118,8 +311,7 @@ fn env_change_work_around() { // target while !out_dir.ends_with("target") { if !out_dir.pop() { - // We ran out of directories... - return; + return; // We ran out of directories... } } out_dir.pop(); @@ -141,44 +333,42 @@ fn env_change_work_around() { } } -fn emit_configuration( +fn create_config( prefix: &str, - configs: &HashMap, - selected_config: &mut String, -) { - // emit cfgs and set envs - for (name, value) in configs.into_iter() { - let cfg_name = snake_case(name.trim_start_matches(prefix)); - println!("cargo:rustc-check-cfg=cfg({cfg_name})"); - match value { - Value::Bool(true) => { - println!("cargo:rustc-cfg={cfg_name}") - } - _ => {} - } + config: &[(&str, &str, Value, Option)], + doc_table: &mut String, +) -> HashMap { + let mut configs = HashMap::new(); - let value = value.as_string(); - // values that haven't been seen will be output here with the default value - println!("cargo:rustc-env={}={}", name, value); + for (name, description, default, _validator) in config { + let name = format!("{prefix}_{}", screaming_snake_case(name)); + configs.insert(name.clone(), default.clone()); - writeln!(selected_config, "|**{name}**|{value}|").unwrap(); + // Write documentation table line: + let default = default.to_string(); + writeln!(doc_table, "|**{name}**|{description}|{default}|").unwrap(); + + // Rebuild if config environment variable changed: + println!("cargo:rerun-if-env-changed={name}"); } + + configs } fn capture_from_env(prefix: &str, configs: &mut HashMap) { let mut unknown = Vec::new(); let mut failed = Vec::new(); - // Try and capture input from the environment + // Try and capture input from the environment: for (var, value) in env::vars() { - if let Some(_) = var.strip_prefix(prefix) { + if var.strip_prefix(prefix).is_some() { let Some(cfg) = configs.get_mut(&var) else { unknown.push(var); continue; }; if let Err(e) = cfg.parse_in_place(&value) { - failed.push(format!("{}: {e:?}", var)); + failed.push(format!("{var}: {e}")); } } } @@ -192,61 +382,54 @@ fn capture_from_env(prefix: &str, configs: &mut HashMap) { } } -fn create_config( +fn emit_configuration( prefix: &str, - config: &[(&str, Value, &str)], - doc_table: &mut String, -) -> HashMap { - // Generate the template for the config - let mut configs = HashMap::new(); - for (name, default, desc) in config { - let name = format!("{prefix}{}", screaming_snake_case(&name)); - configs.insert(name.clone(), default.clone()); + configs: &HashMap, + selected_config: &mut String, +) { + for (name, value) in configs.iter() { + let cfg_name = snake_case(name.trim_start_matches(&format!("{prefix}_"))); + println!("cargo:rustc-check-cfg=cfg({cfg_name})"); - // write doc table line - let default = default.as_string(); - writeln!(doc_table, "|**{name}**|{desc}|{default}|").unwrap(); + if let Value::Bool(true) = value { + println!("cargo:rustc-cfg={cfg_name}"); + } - // Rebuild if config envvar changed. - println!("cargo:rerun-if-env-changed={name}"); - } + let value = value.to_string(); - configs + // Values that haven't been seen will be output here with the default value: + println!("cargo:rustc-env={}={}", name, value); + writeln!(selected_config, "|**{name}**|{value}|").unwrap(); + } } fn write_config_tables(prefix: &str, doc_table: String, selected_config: String) { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let out_file = out_dir - .join(format!("{prefix}config_table.md")) - .to_string_lossy() + .join(format!("{prefix}_config_table.md")) + .display() .to_string(); fs::write(out_file, doc_table).unwrap(); - let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let out_file = out_dir - .join(format!("{prefix}selected_config.md")) - .to_string_lossy() + .join(format!("{prefix}_selected_config.md")) + .display() .to_string(); fs::write(out_file, selected_config).unwrap(); } -// Converts a symbol name like -// "PLACE-spi_DRIVER-IN_ram" -// to -// "place_spi_driver_in_ram" fn snake_case(name: &str) -> String { let mut name = name.replace("-", "_"); name.make_ascii_lowercase(); + name } -// Converts a symbol name like -// "PLACE-spi_DRIVER-IN_ram" -// to -// "PLACE_SPI_DRIVER_IN_RAM" fn screaming_snake_case(name: &str) -> String { let mut name = name.replace("-", "_"); name.make_ascii_uppercase(); + name } @@ -257,32 +440,24 @@ mod test { #[test] fn value_number_formats() { const INPUTS: &[&str] = &["0xAA", "0o252", "0b0000000010101010", "170"]; - let mut v = Value::SignedInteger(0); + let mut v = Value::Integer(0); for input in INPUTS { v.parse_in_place(input).unwrap(); // no matter the input format, the output format should be decimal - assert_eq!(v.as_string(), "170"); + assert_eq!(format!("{v}"), "170"); } } #[test] fn value_bool_inputs() { - const TRUE_INPUTS: &[&str] = &["true", "y", "yes"]; - const FALSE_INPUTS: &[&str] = &["false", "n", "no"]; let mut v = Value::Bool(false); - for input in TRUE_INPUTS { - v.parse_in_place(input).unwrap(); - // no matter the input variant, the output format should be "true" - assert_eq!(v.as_string(), "true"); - } + v.parse_in_place("true").unwrap(); + assert_eq!(format!("{v}"), "true"); - for input in FALSE_INPUTS { - v.parse_in_place(input).unwrap(); - // no matter the input variant, the output format should be "false" - assert_eq!(v.as_string(), "false"); - } + v.parse_in_place("false").unwrap(); + assert_eq!(format!("{v}"), "false"); } #[test] @@ -298,13 +473,18 @@ mod test { let configs = generate_config( "esp-test", &[ - ("number", Value::UnsignedInteger(999), "NA"), - ("number_signed", Value::SignedInteger(-777), "NA"), - ("string", Value::String("Demo".to_owned()), "NA"), - ("bool", Value::Bool(false), "NA"), - ("number_default", Value::UnsignedInteger(999), "NA"), - ("string_default", Value::String("Demo".to_owned()), "NA"), - ("bool_default", Value::Bool(false), "NA"), + ("number", "NA", Value::Integer(999), None), + ("number_signed", "NA", Value::Integer(-777), None), + ("string", "NA", Value::String("Demo".to_owned()), None), + ("bool", "NA", Value::Bool(false), None), + ("number_default", "NA", Value::Integer(999), None), + ( + "string_default", + "NA", + Value::String("Demo".to_owned()), + None, + ), + ("bool_default", "NA", Value::Bool(false), None), ], false, ); @@ -312,14 +492,14 @@ mod test { // some values have changed assert_eq!( match configs.get("ESP_TEST_NUMBER").unwrap() { - Value::UnsignedInteger(num) => *num, + Value::Integer(num) => *num, _ => unreachable!(), }, 0xaa ); assert_eq!( match configs.get("ESP_TEST_NUMBER_SIGNED").unwrap() { - Value::SignedInteger(num) => *num, + Value::Integer(num) => *num, _ => unreachable!(), }, -999 @@ -342,7 +522,7 @@ mod test { // the rest are the defaults assert_eq!( match configs.get("ESP_TEST_NUMBER_DEFAULT").unwrap() { - Value::UnsignedInteger(num) => *num, + Value::Integer(num) => *num, _ => unreachable!(), }, 999 @@ -365,6 +545,114 @@ mod test { ) } + #[test] + fn builtin_validation_passes() { + temp_env::with_vars( + [ + ("ESP_TEST_POSITIVE_NUMBER", Some("7")), + ("ESP_TEST_NEGATIVE_NUMBER", Some("-1")), + ("ESP_TEST_NON_NEGATIVE_NUMBER", Some("0")), + ("ESP_TEST_RANGE", Some("9")), + ], + || { + generate_config( + "esp-test", + &[ + ( + "positive_number", + "NA", + Value::Integer(-1), + Some(Validator::PositiveInteger), + ), + ( + "negative_number", + "NA", + Value::Integer(1), + Some(Validator::NegativeInteger), + ), + ( + "non_negative_number", + "NA", + Value::Integer(-1), + Some(Validator::NonNegativeInteger), + ), + ( + "range", + "NA", + Value::Integer(0), + Some(Validator::IntegerInRange(5..10)), + ), + ], + false, + ) + }, + ); + } + + #[test] + fn custom_validation_passes() { + temp_env::with_vars([("ESP_TEST_NUMBER", Some("13"))], || { + generate_config( + "esp-test", + &[( + "number", + "NA", + Value::Integer(-1), + Some(Validator::Custom(Box::new(|value| { + let range = 10..20; + if !value.is_integer() || !range.contains(&value.as_integer()) { + Err(Error::validation("value does not fall within range")) + } else { + Ok(()) + } + }))), + )], + false, + ) + }); + } + + #[test] + #[should_panic] + fn builtin_validation_bails() { + temp_env::with_vars([("ESP_TEST_POSITIVE_NUMBER", Some("-99"))], || { + generate_config( + "esp-test", + &[( + "positive_number", + "NA", + Value::Integer(-1), + Some(Validator::PositiveInteger), + )], + false, + ) + }); + } + + #[test] + #[should_panic] + fn custom_validation_bails() { + temp_env::with_vars([("ESP_TEST_NUMBER", Some("37"))], || { + generate_config( + "esp-test", + &[( + "number", + "NA", + Value::Integer(-1), + Some(Validator::Custom(Box::new(|value| { + let range = 10..20; + if !value.is_integer() || !range.contains(&value.as_integer()) { + Err(Error::validation("value does not fall within range")) + } else { + Ok(()) + } + }))), + )], + false, + ) + }); + } + #[test] #[should_panic] fn env_unknown_bails() { @@ -376,7 +664,7 @@ mod test { || { generate_config( "esp-test", - &[("number", Value::UnsignedInteger(999), "NA")], + &[("number", "NA", Value::Integer(999), None)], false, ); }, @@ -389,7 +677,7 @@ mod test { temp_env::with_vars([("ESP_TEST_NUMBER", Some("Hello world"))], || { generate_config( "esp-test", - &[("number", Value::UnsignedInteger(999), "NA")], + &[("number", "NA", Value::Integer(999), None)], false, ); }); diff --git a/esp-config/src/lib.rs b/esp-config/src/lib.rs index 941c420692e..2ef0c18f9a9 100644 --- a/esp-config/src/lib.rs +++ b/esp-config/src/lib.rs @@ -3,17 +3,30 @@ #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] #![cfg_attr(not(feature = "build"), no_std)] +#![deny(missing_docs, rust_2018_idioms)] #[cfg(feature = "build")] mod generate; #[cfg(feature = "build")] -pub use generate::*; +pub use generate::{generate_config, Error, Validator, Value}; +/// Parse the value of an environment variable as a [bool] at compile time. +#[macro_export] +macro_rules! esp_config_bool { + ( $var:expr ) => { + match env!($var).as_bytes() { + b"true" => true, + b"false" => false, + _ => ::core::panic!("boolean value must be either 'true' or 'false'"), + } + }; +} + +// TODO: From 1.82 on, we can use `<$ty>::from_str_radix(env!($var), 10)` +/// Parse the value of an environment variable as an integer at compile time. #[macro_export] -// TODO from 1.82 we can use <$ty>::from_str_radix(env!($var), 10) instead -/// Parse the value of a `env` variable as an integer at compile time macro_rules! esp_config_int { - ($ty:ty, $var:expr) => { + ( $ty:ty, $var:expr ) => { const { const BYTES: &[u8] = env!($var).as_bytes(); esp_config_int_parse!($ty, BYTES) @@ -21,63 +34,53 @@ macro_rules! esp_config_int { }; } +/// Get the string value of an environment variable at compile time. #[macro_export] -/// Get the string value of an `env` variable at compile time macro_rules! esp_config_str { - ($var:expr) => { + ( $var:expr ) => { env!($var) }; } -#[macro_export] -/// Parse the value of a `env` variable as an bool at compile time -macro_rules! esp_config_bool { - ($var:expr) => { - match env!($var).as_bytes() { - b"false" => false, - _ => true, - } - }; -} - -#[macro_export] -#[doc(hidden)] // to avoid confusion with esp_config_int, let's hide this /// Parse a string like "777" into an integer, which _can_ be used in a `const` /// context +#[doc(hidden)] // To avoid confusion with `esp_config_int`, hide this in the docs +#[macro_export] macro_rules! esp_config_int_parse { - ($ty:ty, $bytes:expr) => {{ + ( $ty:ty, $bytes:expr ) => {{ let mut bytes = $bytes; let mut val: $ty = 0; let mut sign_seen = false; let mut is_negative = false; + while let [byte, rest @ ..] = bytes { match *byte { b'0'..=b'9' => { val = val * 10 + (*byte - b'0') as $ty; } b'-' | b'+' if !sign_seen => { - if *byte == b'-' { - is_negative = true; - } + is_negative = *byte == b'-'; sign_seen = true; } - _ => ::core::panic!("invalid digit"), + _ => ::core::panic!("invalid character encountered while parsing integer"), } + bytes = rest; } + if is_negative { let original = val; - // subtract twice to get the negative + // Subtract the value twice to get a negative: val -= original; val -= original; } + val }}; } #[cfg(test)] mod test { - // We can only test success in the const context const _: () = { core::assert!(esp_config_int_parse!(i64, "-77777".as_bytes()) == -77777); @@ -98,4 +101,10 @@ mod test { fn test_expect_positive() { esp_config_int_parse!(u8, "-5".as_bytes()); } + + #[test] + #[should_panic] + fn test_invalid_digit() { + esp_config_int_parse!(u32, "a".as_bytes()); + } } diff --git a/esp-hal-embassy/CHANGELOG.md b/esp-hal-embassy/CHANGELOG.md index c3cad863938..3449b640ed2 100644 --- a/esp-hal-embassy/CHANGELOG.md +++ b/esp-hal-embassy/CHANGELOG.md @@ -9,12 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `ESP_HAL_EMBASSY_LOW_POWER_WAIT` configuration option. (#2329) + ### Changed - Reduce memory footprint by 4 bytes on multi-core MCUs. ### Fixed +- Alarm interrupts are now handled on the core that allocated them. (For executors created on the second core after calling `esp_hal_embassy::init`) (#2451) + ### Removed ## 0.4.0 - 2024-10-10 diff --git a/esp-hal-embassy/Cargo.toml b/esp-hal-embassy/Cargo.toml index ffac4445831..d4827afca27 100644 --- a/esp-hal-embassy/Cargo.toml +++ b/esp-hal-embassy/Cargo.toml @@ -25,6 +25,7 @@ static_cell = "2.1.0" [build-dependencies] esp-build = { version = "0.1.0", path = "../esp-build" } +esp-config = { version = "0.1.0", path = "../esp-config", features = ["build"] } esp-metadata = { version = "0.4.0", path = "../esp-metadata" } [features] diff --git a/esp-hal-embassy/build.rs b/esp-hal-embassy/build.rs index 1c7ecfc2ff6..5a4cf918864 100644 --- a/esp-hal-embassy/build.rs +++ b/esp-hal-embassy/build.rs @@ -1,6 +1,7 @@ use std::{error::Error, str::FromStr}; use esp_build::assert_unique_used_features; +use esp_config::{generate_config, Value}; use esp_metadata::{Chip, Config}; fn main() -> Result<(), Box> { @@ -37,5 +38,17 @@ fn main() -> Result<(), Box> { // Define all necessary configuration symbols for the configured device: config.define_symbols(); + // emit config + generate_config( + "esp_hal_embassy", + &[( + "low-power-wait", + "Enables the lower-power wait if no tasks are ready to run on the thread-mode executor. This allows the MCU to use less power if the workload allows. Recommended for battery-powered systems. May impact analog performance.", + Value::Bool(true), + None + )], + true, + ); + Ok(()) } diff --git a/esp-hal-embassy/src/executor/interrupt.rs b/esp-hal-embassy/src/executor/interrupt.rs index 28ce6ccd574..7f3599127c2 100644 --- a/esp-hal-embassy/src/executor/interrupt.rs +++ b/esp-hal-embassy/src/executor/interrupt.rs @@ -4,8 +4,8 @@ use core::{cell::UnsafeCell, mem::MaybeUninit}; use embassy_executor::{raw, SendSpawner}; use esp_hal::{ - get_core, interrupt::{self, software::SoftwareInterrupt, InterruptHandler}, + Cpu, }; use portable_atomic::{AtomicUsize, Ordering}; @@ -87,7 +87,7 @@ impl InterruptExecutor { .core .compare_exchange( usize::MAX, - get_core() as usize, + Cpu::current() as usize, Ordering::Acquire, Ordering::Relaxed, ) diff --git a/esp-hal-embassy/src/executor/thread.rs b/esp-hal-embassy/src/executor/thread.rs index 4af62ba3ecb..3539a36d204 100644 --- a/esp-hal-embassy/src/executor/thread.rs +++ b/esp-hal-embassy/src/executor/thread.rs @@ -3,9 +3,10 @@ use core::marker::PhantomData; use embassy_executor::{raw, Spawner}; -use esp_hal::{get_core, Cpu}; +use esp_hal::Cpu; #[cfg(multi_core)] use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler}; +#[cfg(low_power_wait)] use portable_atomic::{AtomicBool, Ordering}; pub(crate) const THREAD_MODE_CONTEXT: usize = 16; @@ -15,7 +16,7 @@ pub(crate) const THREAD_MODE_CONTEXT: usize = 16; static SIGNAL_WORK_THREAD_MODE: [AtomicBool; Cpu::COUNT] = [const { AtomicBool::new(false) }; Cpu::COUNT]; -#[cfg(multi_core)] +#[cfg(all(multi_core, low_power_wait))] #[handler] fn software3_interrupt() { // This interrupt is fired when the thread-mode executor's core needs to be @@ -25,30 +26,33 @@ fn software3_interrupt() { unsafe { SoftwareInterrupt::<3>::steal().reset() }; } -pub(crate) fn pend_thread_mode(core: usize) { - // Signal that there is work to be done. - SIGNAL_WORK_THREAD_MODE[core].store(true, Ordering::SeqCst); - - // If we are pending a task on the current core, we're done. Otherwise, we - // need to make sure the other core wakes up. - #[cfg(multi_core)] - if core != get_core() as usize { - // We need to clear the interrupt from software. We don't actually - // need it to trigger and run the interrupt handler, we just need to - // kick waiti to return. - unsafe { SoftwareInterrupt::<3>::steal().raise() }; +pub(crate) fn pend_thread_mode(_core: usize) { + #[cfg(low_power_wait)] + { + // Signal that there is work to be done. + SIGNAL_WORK_THREAD_MODE[_core].store(true, Ordering::SeqCst); + + // If we are pending a task on the current core, we're done. Otherwise, we + // need to make sure the other core wakes up. + #[cfg(multi_core)] + if _core != Cpu::current() as usize { + // We need to clear the interrupt from software. We don't actually + // need it to trigger and run the interrupt handler, we just need to + // kick waiti to return. + unsafe { SoftwareInterrupt::<3>::steal().raise() }; + } } } -/// A thread aware Executor +/// Thread mode executor. +/// +/// This is the simplest and most common kind of executor. It runs on thread +/// mode (at the lowest priority level). +#[cfg_attr(multi_core, doc = "")] #[cfg_attr( multi_core, - doc = r#" -This executor is capable of waking an -executor running on another core if work -needs to be completed there for a task to -progress on this core. -"# + doc = "This executor is safe to use on multiple cores. You need to +create one instance per core. The executors don't steal tasks from each other." )] pub struct Executor { inner: raw::Executor, @@ -64,13 +68,13 @@ impl Executor { This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."# )] pub fn new() -> Self { - #[cfg(multi_core)] + #[cfg(all(multi_core, low_power_wait))] unsafe { SoftwareInterrupt::<3>::steal().set_interrupt_handler(software3_interrupt); } Self { - inner: raw::Executor::new((THREAD_MODE_CONTEXT + get_core() as usize) as *mut ()), + inner: raw::Executor::new((THREAD_MODE_CONTEXT + Cpu::current() as usize) as *mut ()), not_send: PhantomData, } } @@ -90,7 +94,7 @@ This will use software-interrupt 3 which isn't available for anything else to wa /// you mutable access. There's a few ways to do this: /// /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) - /// - a `static mut` (unsafe) + /// - a `static mut` (unsafe, not recommended) /// - a local variable in a function you know never returns (like `fn main() /// -> !`), upgrading its lifetime with `transmute`. (unsafe) /// @@ -98,20 +102,19 @@ This will use software-interrupt 3 which isn't available for anything else to wa pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { init(self.inner.spawner()); - let cpu = get_core() as usize; + #[cfg(low_power_wait)] + let cpu = Cpu::current() as usize; loop { - unsafe { - self.inner.poll(); + unsafe { self.inner.poll() }; - Self::wait_impl(cpu); - } + #[cfg(low_power_wait)] + Self::wait_impl(cpu); } } - #[doc(hidden)] - #[cfg(xtensa)] - pub fn wait_impl(cpu: usize) { + #[cfg(all(xtensa, low_power_wait))] + fn wait_impl(cpu: usize) { // Manual critical section implementation that only masks interrupts handlers. // We must not acquire the cross-core on dual-core systems because that would // prevent the other core from doing useful work while this core is sleeping. @@ -140,9 +143,8 @@ This will use software-interrupt 3 which isn't available for anything else to wa } } - #[doc(hidden)] - #[cfg(riscv)] - pub fn wait_impl(cpu: usize) { + #[cfg(all(riscv, low_power_wait))] + fn wait_impl(cpu: usize) { // we do not care about race conditions between the load and store operations, // interrupts will only set this value to true. critical_section::with(|_| { diff --git a/esp-hal-embassy/src/lib.rs b/esp-hal-embassy/src/lib.rs index 9b8a8980a0c..795f4cf13b3 100644 --- a/esp-hal-embassy/src/lib.rs +++ b/esp-hal-embassy/src/lib.rs @@ -149,6 +149,9 @@ impl_array!(4); /// - A mutable static array of `OneShotTimer` instances /// - A 2, 3, 4 element array of `AnyTimer` instances /// +/// Note that if you use the `integrated-timers` feature, +/// you need to pass as many timers as you start executors. +/// /// # Examples /// /// ```rust, no_run diff --git a/esp-hal-embassy/src/time_driver.rs b/esp-hal-embassy/src/time_driver.rs index 99708077812..6b73f800104 100644 --- a/esp-hal-embassy/src/time_driver.rs +++ b/esp-hal-embassy/src/time_driver.rs @@ -49,48 +49,20 @@ impl EmbassyTimer { ); } - static HANDLERS: [InterruptHandler; MAX_SUPPORTED_ALARM_COUNT] = [ - handler0, handler1, handler2, handler3, handler4, handler5, handler6, - ]; - critical_section::with(|cs| { timers.iter_mut().enumerate().for_each(|(n, timer)| { timer.enable_interrupt(false); timer.stop(); - timer.set_interrupt_handler(HANDLERS[n]); + + if DRIVER.alarms.borrow(cs)[n].allocated.get() { + // FIXME: we should track which core allocated an alarm and bind the interrupt + // to that core. + timer.set_interrupt_handler(HANDLERS[n]); + } }); TIMERS.replace(cs, Some(timers)); }); - - #[handler(priority = Priority::max())] - fn handler0() { - DRIVER.on_interrupt(0); - } - #[handler(priority = Priority::max())] - fn handler1() { - DRIVER.on_interrupt(1); - } - #[handler(priority = Priority::max())] - fn handler2() { - DRIVER.on_interrupt(2); - } - #[handler(priority = Priority::max())] - fn handler3() { - DRIVER.on_interrupt(3); - } - #[handler(priority = Priority::max())] - fn handler4() { - DRIVER.on_interrupt(4); - } - #[handler(priority = Priority::max())] - fn handler5() { - DRIVER.on_interrupt(5); - } - #[handler(priority = Priority::max())] - fn handler6() { - DRIVER.on_interrupt(6); - } } fn on_interrupt(&self, id: usize) { @@ -128,11 +100,26 @@ impl Driver for EmbassyTimer { unsafe fn allocate_alarm(&self) -> Option { critical_section::with(|cs| { for (i, alarm) in self.alarms.borrow(cs).iter().enumerate() { - if !alarm.allocated.get() { - // set alarm so it is not overwritten - alarm.allocated.set(true); - return Some(AlarmHandle::new(i as u8)); + if alarm.allocated.get() { + continue; } + let mut timer = TIMERS.borrow_ref_mut(cs); + // `allocate_alarm` may be called before `esp_hal_embassy::init()`, so + // we need to check if we have timers. + if let Some(timer) = &mut *timer { + // If we do, bind the interrupt handler to the timer. + // This ensures that alarms allocated after init are correctly bound to the + // core that created the executor. + let timer = unwrap!( + timer.get_mut(i), + "There are not enough timers to allocate a new alarm. Call `esp_hal_embassy::init()` with the correct number of timers." + ); + timer.set_interrupt_handler(HANDLERS[i]); + } + + // set alarm so it is not overwritten + alarm.allocated.set(true); + return Some(AlarmHandle::new(i as u8)); } None }) @@ -172,3 +159,36 @@ impl Driver for EmbassyTimer { true } } + +static HANDLERS: [InterruptHandler; MAX_SUPPORTED_ALARM_COUNT] = [ + handler0, handler1, handler2, handler3, handler4, handler5, handler6, +]; + +#[handler(priority = Priority::max())] +fn handler0() { + DRIVER.on_interrupt(0); +} +#[handler(priority = Priority::max())] +fn handler1() { + DRIVER.on_interrupt(1); +} +#[handler(priority = Priority::max())] +fn handler2() { + DRIVER.on_interrupt(2); +} +#[handler(priority = Priority::max())] +fn handler3() { + DRIVER.on_interrupt(3); +} +#[handler(priority = Priority::max())] +fn handler4() { + DRIVER.on_interrupt(4); +} +#[handler(priority = Priority::max())] +fn handler5() { + DRIVER.on_interrupt(5); +} +#[handler(priority = Priority::max())] +fn handler6() { + DRIVER.on_interrupt(6); +} diff --git a/esp-hal-procmacros/CHANGELOG.md b/esp-hal-procmacros/CHANGELOG.md new file mode 100644 index 00000000000..84d7f1c0ed0 --- /dev/null +++ b/esp-hal-procmacros/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed + +### Changed + +### Removed + +## [0.14.0] - 2024-10-10 + +## [0.13.0] - 2024-08-29 + +## [0.12.0] - 2024-07-15 + +## [0.11.0] - 2024-06-04 + +## [0.10.0] - 2024-04-18 + +## [0.9.0] - 2024-03-18 + +## [0.8.0] - 2023-12-12 + +## [0.7.0] - 2023-10-31 + +## [0.6.1] - 2023-09-05 + +## [0.6.0] - 2023-07-04 + +## [0.5.0] - 2023-03-27 + +## [0.4.0] - 2023-02-21 + +## [0.2.0] - 2023-01-26 + +## [0.1.0] - 2022-08-25 + +- Initial release + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-hal-procmacros?since=2024-10-10 diff --git a/esp-hal-procmacros/src/lp_core.rs b/esp-hal-procmacros/src/lp_core.rs index 5fa639d7e92..60a3b351cc1 100644 --- a/esp-hal-procmacros/src/lp_core.rs +++ b/esp-hal-procmacros/src/lp_core.rs @@ -31,7 +31,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { res } - pub(crate) fn get_simplename(t: &Type) -> String { + pub(crate) fn simplename(t: &Type) -> String { match t { Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(), _ => String::new(), @@ -125,7 +125,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { .into(); } FnArg::Typed(t) => { - match get_simplename(&t.ty).as_str() { + match simplename(&t.ty).as_str() { "Output" => { let pin = extract_pin(&t.ty); if used_pins.contains(&pin) { diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 4690eb3b58d..90ea7f5edb3 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -22,6 +22,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Spi::half_duplex_read` and `Spi::half_duplex_write` (#2373) - `Cpu::COUNT` and `Cpu::current()` (#2411) - `UartInterrupt` and related functions (#2406) +- I2S Parallel output driver for ESP32. (#2348, #2436, #2472) +- Add an option to configure `WDT` action (#2330) +- `DmaDescriptor` is now `Send` (#2456) +- `into_async` and `into_blocking` functions for most peripherals (#2430, #2461) +- API mode type parameter (currently always `Blocking`) to `master::Spi` and `slave::Spi` (#2430) +- `gpio::{GpioPin, AnyPin, Flex, Output, OutputOpenDrain}::split()` to obtain peripheral interconnect signals. (#2418) +- `gpio::Input::{split(), into_peripheral_output()}` when used with output pins. (#2418) +- `gpio::Output::peripheral_input()` (#2418) +- `{Uart, UartRx, UartTx}::apply_config()` (#2449) +- `{Uart, UartRx, UartTx}` now implement `embassy_embedded_hal::SetConfig` (#2449) +- GPIO ETM tasks and events now accept `InputSignal` and `OutputSignal` (#2427) +- `spi::master::Config` and `{Spi, SpiDma, SpiDmaBus}::apply_config` (#2448) +- `embassy_embedded_hal::SetConfig` is now implemented for `spi::master::{Spi, SpiDma, SpiDmaBus}`, `i2c::master::I2c` (#2448, #2477) +- `slave::Spi::{with_mosi(), with_miso(), with_sclk(), with_cs()}` functions (#2485) +- I8080: Added `set_8bits_order()` to set the byte order in 8-bit mode (#2487) +- `I2c::{apply_config(), with_sda(), with_scl()}` (#2477) +- ESP32-S2: Added missing GPIO alternate functions (#2512) ### Changed @@ -33,17 +50,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Renamed `SpiDma` functions: `dma_transfer` to `transfer`, `dma_write` to `write`, `dma_read` to `read`. (#2373) - Peripheral type erasure for UART (#2381) - Changed listening for UART events (#2406) +- Circular DMA transfers now correctly error, `available` returns `Result` now (#2409) +- Interrupt listen/unlisten/clear functions now accept any type that converts into `EnumSet` (i.e. single interrupt flags). (#2442) +- SPI interrupt listening is now only available in Blocking mode. The `set_interrupt_handler` is available via `InterruptConfigurable` (#2442) +- Allow users to create DMA `Preparation`s (#2455) +- The `rmt::asynch::RxChannelAsync` and `rmt::asynch::TxChannelAsync` traits have been moved to `rmt` (#2430) +- Calling `AnyPin::output_signals` on an input-only pin (ESP32 GPIO 34-39) will now result in a panic. (#2418) +- UART configuration types have been moved to `esp_hal::uart` (#2449) +- `spi::master::Spi::new()` no longer takes `frequency` and `mode` as a parameter. (#2448) +- Peripheral interconnections via GPIO pins now use the GPIO matrix. (#2419) +- The I2S driver has been moved to `i2s::master` (#2472) +- `slave::Spi` constructors no longer take pins (#2485) +- The `I2c` master driver has been moved from `esp_hal::i2c` to `esp_hal::i2c::master`. (#2476) +- `I2c` SCL timeout is now defined in bus clock cycles. (#2477) +- Trying to send a single-shot RMT transmission will result in an error now, `RMT` deals with `u32` now, `PulseCode` is a convenience trait now (#2463) +- Removed `get_` prefixes from functions (#2528) ### Fixed - Fix conflict between `RtcClock::get_xtal_freq` and `Rtc::disable_rom_message_printing` (#2360) - Fixed an issue where interrupts enabled before `esp_hal::init` were disabled. This issue caused the executor created by `#[esp_hal_embassy::main]` to behave incorrectly in multi-core applications. (#2377) +- Fixed `TWAI::transmit_async`: bus-off state is not reached when CANH and CANL are shorted. (#2421) +- ESP32: added UART-specific workaround for https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html (#2441) +- Fixed some SysTimer race conditions and panics (#2451) +- TWAI: accept all messages by default (#2467) +- I8080: `set_byte_order()` now works correctly in 16-bit mode (#2487) +- ESP32-C6/ESP32-H2: Make higher LEDC frequencies work (#2520) ### Removed - The `i2s::{I2sWrite, I2sWriteDma, I2sRead, I2sReadDma, I2sWriteDmaAsync, I2sReadDmaAsync}` traits have been removed. (#2316) - The `ledc::ChannelHW` trait is no longer generic. (#2387) - The `I2c::new_with_timeout` constructors have been removed (#2361) +- `I2c::new()` no longer takes `frequency` and pins as parameters. (#2477) - The `spi::master::HalfDuplexReadWrite` trait has been removed. (#2373) - The `Spi::with_pins` methods have been removed. (#2373) - The `Spi::new_half_duplex` constructor have been removed. (#2373) @@ -56,12 +95,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the pin type parameters from `parl_io::{RxOneBit, RxTwoBits, RxFourBits, RxEightBits, RxSixteenBits}` (#2388) - Removed the pin type parameters from `lcd_cam::lcd::i8080::{TxEightBits, TxSixteenBits}` (#2388) - Removed the pin type parameters from `lcd_cam::cam::{RxEightBits, RxSixteenBits}` (#2388) +- Most of the async-specific constructors (`new_async`, `new_async_no_transceiver`) have been removed. (#2430) +- The `configure_for_async` DMA functions have been removed (#2430) +- The `Uart::{change_baud, change_stop_bits}` functions have been removed (#2449) +- `gpio::{Input, Output, OutputOpenDrain, Flex, GpioPin}::{peripheral_input, into_peripheral_output}` have been removed. (#2418) +- The `GpioEtm` prefix has been removed from `gpio::etm` types (#2427) +- The `TimerEtm` prefix has been removed from `timer::timg::etm` types (#2427) +- The `SysTimerEtm` prefix has been removed from `timer::systimer::etm` types (#2427) +- The `GpioEtmEventRising`, `GpioEtmEventFalling`, `GpioEtmEventAny` types have been replaced with `Event` (#2427) +- The `TaskSet`, `TaskClear`, `TaskToggle` types have been replaced with `Task` (#2427) +- `{Spi, SpiDma, SpiDmaBus}` configuration methods (#2448) +- `Io::new_with_priority` and `Io::new_no_bind_interrupt`. (#2486) +- `parl_io::{no_clk_pin(), NoClkPin}` (#2531) +- Removed `get_core` function in favour of `Cpu::current` (#2533) ## [0.21.1] ### Fixed - Restored blocking `embedded_hal` compatibility for async I2C driver (#2343) +- I2c::transaction is now able to transmit data of arbitrary length (#2481) ## [0.21.0] @@ -126,6 +179,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SPI transactions are now cancelled if the transfer object (or async Future) is dropped. (#2216) - The DMA channel types have been removed from peripherals (#2261) - `I2C` driver renamed to `I2c` (#2320) +- The GPIO pins are now accessible via `Peripherals` and are no longer part of the `Io` struct (#2508) ### Fixed @@ -150,6 +204,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - TWAI should no longer panic when receiving a non-compliant frame (#2255) - OneShotTimer: fixed `delay_nanos` behaviour (#2256) - Fixed unsoundness around `Efuse` (#2259) +- Empty I2C writes to unknown addresses now correctly fail with `AckCheckFailed`. (#2506) ### Removed @@ -170,6 +225,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `esp_hal::spi::slave::WithDmaSpiN` traits (#2260) - The `WithDmaAes` trait has been removed (#2261) - The `I2s::new_i2s1` constructor has been removed (#2261) +- `Peripherals.GPIO` has been removed (#2508) ## [0.20.1] - 2024-08-30 diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 399c4e7222b..a92c64af117 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -25,6 +25,7 @@ defmt = { version = "0.3.8", optional = true } delegate = "0.12.0" digest = { version = "0.10.7", default-features = false, optional = true } document-features = "0.2.10" +embassy-embedded-hal = "0.2.0" embassy-futures = "0.1.1" embassy-sync = "0.6.0" embassy-usb-driver = { version = "0.1.0", optional = true } @@ -55,13 +56,13 @@ xtensa-lx = { version = "0.9.0", optional = true } # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. -esp32 = { version = "0.33.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32c2 = { version = "0.22.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32c3 = { version = "0.25.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32c6 = { version = "0.16.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32h2 = { version = "0.12.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32s2 = { version = "0.24.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } -esp32s3 = { version = "0.28.0", git = "https://github.com/esp-rs/esp-pacs.git", rev = "48fd400", features = ["critical-section", "rt"], optional = true } +esp32 = { version = "0.34.0", features = ["critical-section", "rt"], optional = true } +esp32c2 = { version = "0.23.0", features = ["critical-section", "rt"], optional = true } +esp32c3 = { version = "0.26.0", features = ["critical-section", "rt"], optional = true } +esp32c6 = { version = "0.17.0", features = ["critical-section", "rt"], optional = true } +esp32h2 = { version = "0.13.0", features = ["critical-section", "rt"], optional = true } +esp32s2 = { version = "0.25.0", features = ["critical-section", "rt"], optional = true } +esp32s3 = { version = "0.29.0", features = ["critical-section", "rt"], optional = true } [target.'cfg(target_arch = "riscv32")'.dependencies] esp-riscv-rt = { version = "0.9.0", path = "../esp-riscv-rt" } diff --git a/esp-hal/MIGRATING-0.20.md b/esp-hal/MIGRATING-0.20.md index 543cb03185a..0064d8a1d45 100644 --- a/esp-hal/MIGRATING-0.20.md +++ b/esp-hal/MIGRATING-0.20.md @@ -230,7 +230,7 @@ We've replaced some usage of features with [esp-config](https://docs.rs/esp-conf # feature in Cargo.toml - esp-hal = { version = "0.20", features = ["place-spi-driver-in-ram"] } # key in .cargo/config.toml [env] section -+ ESP_HAL_PLACE_SPI_DRIVER_IN_RAM=true ++ ESP_HAL_PLACE_SPI_DRIVER_IN_RAM="true" ``` ## `Camera` driver now uses `DmaRxBuffer` and moves the driver into the transfer object. diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index a693ab966f2..6211533dba6 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -1,28 +1,58 @@ # Migration Guide from 0.21.x to v0.22.x -## Removed `i2s` traits +## IO changes -The following traits have been removed: +### GPIO pins are now accessible via `Peripherals` -- `I2sWrite` -- `I2sWriteDma` -- `I2sRead` -- `I2sReadDma` -- `I2sWriteDmaAsync` -- `I2sReadDmaAsync` +```diff + let peripherals = esp_hal::init(Default::default()); +-let io = Io::new(peripherals.GPIO, peripherals.IOMUX); +-let pin = io.pins.gpio5; ++let pin = peripherals.GPIO5; +``` -You no longer have to import these to access their respective APIs. If you used these traits -in your functions as generic parameters, you can use the `I2s` type directly instead. +### `Io` constructor changes -For example: +- `new_with_priority` and `new_no_bind_interrupts` have been removed. + Use `set_priority` to configure the GPIO interrupt priority. + We no longer overwrite interrupt handlers set by user code during initialization. +- `new` no longer takes `peripherals.GPIO` + +## Removed `async`-specific constructors + +The following async-specific constuctors have been removed: + +- `configure_for_async` DMA channel constructors +- `TwaiConfiguration::new_async` and `TwaiConfiguration::new_async_no_transceiver` +- `I2c::new_async` +- `LcdCam::new_async` +- `UsbSerialJtag::new_async` +- `Rsa::new_async` +- `Rmt::new_async` +- `Uart::new_async`, `Uart::new_async_with_config` +- `UartRx::new_async`, `UartRx::new_async_with_config` +- `UartTx::new_async`, `UartTx::new_async_with_config` + +You can use the blocking counterparts, then call `into_async` on the returned peripheral instead. ```diff --fn foo(i2s: &mut impl I2sWrite) { -+fn foo(i2s: &mut I2s<'_, I2S0, Blocking>) { - // ... - } +-let mut config = twai::TwaiConfiguration::new_async( ++let mut config = twai::TwaiConfiguration::new( + peripherals.TWAI0, + loopback_pin.peripheral_input(), + loopback_pin, + twai::BaudRate::B1000K, + TwaiMode::SelfTest, +-); ++).into_async(); ``` +Some drivers were implicitly configured to the asyncness of the DMA channel used to construct them. +This is no longer the case, and the following drivers will always be created in blocking mode: + +- `i2s::master::I2s` +- `spi::master::SpiDma` and `spi::master::SpiDmaBus` + ## Peripheral types are now optional You no longer have to specify the peripheral instance in the driver's type for the following @@ -53,15 +83,46 @@ the peripheral instance has been moved to the last generic parameter position. let spi: Spi<'static, FullDuplexMode, SPI2> = Spi::new_typed(peripherals.SPI2, 1.MHz(), SpiMode::Mode0); ``` -## I2C constructor changes +## I2C changes + +The I2C master driver and related types have been moved to `esp_hal::i2c::master`. -The `with_timeout` constructors have been removed in favour of `set_timeout` or `with_timeout`. +The `with_timeout` constructors have been removed. `new` and `new_typed` now take a `Config` struct +with the available configuration options. + +- The default configuration is now: + - bus frequency: 100 kHz + - timeout: about 10 bus clock cycles + +The constructors no longer take pins. Use `with_sda` and `with_scl` instead. ```diff +-use esp_hal::i2c::I2c; ++use esp_hal::i2c::{Config, I2c}; -let i2c = I2c::new_with_timeout(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz(), timeout); -+let i2c = I2c::new(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz()).with_timeout(timeout); ++I2c::new_with_config( ++ peripherals.I2C0, ++ { ++ let mut config = Config::default(); ++ config.frequency = 100.kHz(); ++ config.timeout = timeout; ++ config ++ }, ++) ++.with_sda(io.pins.gpio4) ++.with_scl(io.pins.gpio5); ``` +### The calculation of I2C timeout has changed + +Previously, I2C timeouts were counted in increments of I2C peripheral clock cycles. This meant that +the configure value meant different lengths of time depending on the device. With this update, the +I2C configuration now expects the timeout value in number of bus clock cycles, which is consistent +between devices. + +ESP32 and ESP32-S2 use an exact number of clock cycles for its timeout. Other MCUs, however, use +the `2^timeout` value internally, and the HAL rounds up the timeout to the next appropriate value. + ## Changes to half-duplex SPI The `HalfDuplexMode` and `FullDuplexMode` type parameters have been removed from SPI master and slave @@ -72,7 +133,6 @@ drivers. It is now possible to execute half-duplex and full-duplex operations on - The `Spi::new_half_duplex` constructor has been removed. Use `new` (or `new_typed`) instead. - The `with_pins` methods have been removed. Use the individual `with_*` functions instead. - The `with_mosi` and `with_miso` functions now take input-output peripheral signals to support half-duplex mode. - > TODO(danielb): this means they are currently only usable with GPIO pins, but upcoming GPIO changes should allow using any output signal. ```diff - let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) @@ -119,6 +179,28 @@ The `Spi<'_, SPI, HalfDuplexMode>::read` and `Spi<'_, SPI, HalfDuplexMode>::writ .unwrap(); ``` +## Slave-mode SPI + +### Driver construction + +The constructors no longer accept pins. Use the `with_pin_name` setters instead. + +```diff + let mut spi = Spi::new( + peripherals.SPI2, +- sclk, +- mosi, +- miso, +- cs, + SpiMode::Mode0, +-); ++) ++.with_sclk(sclk) ++.with_mosi(mosi) ++.with_miso(miso) ++.with_cs(cs); +``` + ## UART event listening The following functions have been removed: @@ -141,6 +223,7 @@ You can now use the `UartInterrupt` enum and the corresponding `listen`, `unlist Use `interrupts` in place of `_interrupt_set` and `clear_interrupts` in place of the old `reset_` functions. `UartInterrupt`: + - `AtCmd` - `TxDone` - `RxFifoFull` @@ -159,4 +242,187 @@ You can now listen/unlisten multiple interrupt bits at once: -uart0.listen_at_cmd(); -uart0.listen_rx_fifo_full(); +uart0.listen(UartInterrupt::AtCmd | UartConterrupt::RxFifoFull); -```Ë› +``` + +## I2S changes + +### The I2S driver has been moved to `i2s::master` + +```diff +-use esp_hal::i2s::{DataFormat, I2s, Standard}; ++use esp_hal::i2s::master::{DataFormat, I2s, Standard}; +``` + +### Removed `i2s` traits + +The following traits have been removed: + +- `I2sWrite` +- `I2sWriteDma` +- `I2sRead` +- `I2sReadDma` +- `I2sWriteDmaAsync` +- `I2sReadDmaAsync` + +You no longer have to import these to access their respective APIs. If you used these traits +in your functions as generic parameters, you can use the `I2s` type directly instead. + +For example: + +```diff +-fn foo(i2s: &mut impl I2sWrite) { ++fn foo(i2s: &mut I2s<'_, I2S0, Blocking>) { + // ... + } +``` + +## Circular DMA transfer's `available` returns `Result` now + +In case of any error you should drop the transfer and restart it. + +```diff + loop { +- let avail = transfer.available(); ++ let avail = match transfer.available() { ++ Ok(avail) => avail, ++ Err(_) => { ++ core::mem::drop(transfer); ++ transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap(); ++ continue; ++ }, ++ }; +``` + +## Removed `peripheral_input` and `into_peripheral_output` from GPIO pin types + +Creating peripheral interconnect signals now consume the GPIO pin used for the connection. + +The previous signal function have been replaced by `split`. This change affects the following APIs: + +- `GpioPin` +- `AnyPin` + +```diff +-let input_signal = gpioN.peripheral_input(); +-let output_signal = gpioN.into_peripheral_output(); ++let (input_signal, output_signal) = gpioN.split(); +``` + +`into_peripheral_output`, `split` (for output pins only) and `peripheral_input` have been added to +the GPIO drivers (`Input`, `Output`, `OutputOpenDrain` and `Flex`) instead. + +## ETM changes + +- The types are no longer prefixed with `GpioEtm`, `TimerEtm` or `SysTimerEtm`. You can still use + import aliasses in case you need to differentiate due to name collisions + (e.g. `use esp_hal::gpio::etm::Event as GpioEtmEvent`). +- The old task and event types have been replaced by `Task` and `Event`. +- GPIO tasks and events are no longer generic. + +## Changes to peripheral configuration + +### The `uart::config` module has been removed + +The module's contents have been moved into `uart`. + +```diff +-use esp_hal::uart::config::Config; ++use esp_hal::uart::Config; +``` + +If you work with multiple configurable peripherals, you may want to import the `uart` module and +refer to the `Config` struct as `uart::Config`. + +### SPI drivers can now be configured using `spi::master::Config` + +- The old methods to change configuration have been removed. +- The `new` and `new_typed` constructor no longer takes `frequency` and `mode`. +- The default configuration is now: + - bus frequency: 1 MHz + - bit order: MSB first + - mode: SPI mode 0 +- There are new constructors (`new_with_config`, `new_typed_with_config`) and a new `apply_config` method to apply custom configuration. + +```diff +-use esp_hal::spi::{master::Spi, SpiMode}; ++use esp_hal::spi::{master::{Config, Spi}, SpiMode}; +-Spi::new(SPI2, 100.kHz(), SpiMode::Mode1); ++Spi::new_with_config( ++ SPI2, ++ Config { ++ frequency: 100.kHz(), ++ mode: SpiMode::Mode0, ++ ..Config::default() ++ }, ++) +``` + +## I8080 driver split `set_byte_order()` into `set_8bits_order()` and `set_byte_order()`. + +If you were using an 8-bit bus. + +```diff +- i8080.set_byte_order(ByteOrder::default()); ++ i8080.set_8bits_order(ByteOrder::default()); +``` + +If you were using an 16-bit bus, you don't need to change anything, `set_byte_order()` now works correctly. + +If you were sharing the bus between an 8-bit and 16-bit device, you will have to call the corresponding method when +you switch between devices. Be sure to read the documentation of the new methods. + +## `rmt::Channel::transmit` now returns `Result`, `PulseCode` is now `u32` + +When trying to send a one-shot transmission will fail if it doesn't end with an end-marker. + +```diff +- let mut data = [PulseCode { +- level1: true, +- length1: 200, +- level2: false, +- length2: 50, +- }; 20]; +- +- data[data.len() - 2] = PulseCode { +- level1: true, +- length1: 3000, +- level2: false, +- length2: 500, +- }; +- data[data.len() - 1] = PulseCode::default(); ++ let mut data = [PulseCode::new(true, 200, false, 50); 20]; ++ data[data.len() - 2] = PulseCode::new(true, 3000, false, 500); ++ data[data.len() - 1] = PulseCode::empty(); + +- let transaction = channel.transmit(&data); ++ let transaction = channel.transmit(&data).unwrap(); +``` + + +## The `parl_io::NoClkPin` and `no_clk_pin()` have been removed + +You can use `gpio::NoPin` instead. + +```diff + use esp_hal:: { +- parl_io::no_clk_pin, ++ gpio::NoPin, + } + +-parl_io.rx.with_config(&mut rx_pins, no_clk_pin(), BitPackOrder::Msb, Some(0xfff)) ++let mut rx_clk_pin = NoPin; ++parl_io.rx.with_config(&mut rx_pins, &mut rx_clk_pin, BitPackOrder::Msb, Some(0xfff)) +``` + +## `get_` prefixes have been removed from functions + +In order to better comply with the Rust API Guidelines [getter names convention], we have removed the `get_` prefixes from all functions which previously had it. Due to the number of changes it's not practical to list all changes here, however if a function previous began with `get_`, you can simply remove this prefix. + +[getter names convention]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter + +## The `get_core()` function has been removed in favour of `Cpu::current()` + +```diff +- let core = esp_hal::get_core(); ++ let core = esp_hal::Cpu::current(); +``` diff --git a/esp-hal/build.rs b/esp-hal/build.rs index c507a97bd13..9e0f49684c0 100644 --- a/esp-hal/build.rs +++ b/esp-hal/build.rs @@ -80,23 +80,27 @@ fn main() -> Result<(), Box> { &[ ( "place-spi-driver-in-ram", - Value::Bool(false), "Places the SPI driver in RAM for better performance", + Value::Bool(false), + None ), ( "spi-address-workaround", - Value::Bool(true), "(ESP32 only) Enables a workaround for the issue where SPI in half-duplex mode incorrectly transmits the address on a single line if the data buffer is empty.", + Value::Bool(true), + None ), ( "place-switch-tables-in-ram", - Value::Bool(true), "Places switch-tables, some lookup tables and constants related to interrupt handling into RAM - resulting in better performance but slightly more RAM consumption.", + Value::Bool(true), + None ), ( "place-anon-in-ram", - Value::Bool(false), "Places anonymous symbols into RAM - resulting in better performance at the cost of significant more RAM consumption. Best to be combined with `place-switch-tables-in-ram`.", + Value::Bool(false), + None ), ], true, diff --git a/esp-hal/ld/esp32/rom/additional.ld b/esp-hal/ld/esp32/rom/additional.ld index ee815fb3970..d8afbed1267 100644 --- a/esp-hal/ld/esp32/rom/additional.ld +++ b/esp-hal/ld/esp32/rom/additional.ld @@ -9,3 +9,12 @@ PROVIDE ( strncpy = 0x400015d4 ); PROVIDE ( strncmp = 0x4000c5f4 ); PROVIDE ( bzero = 0x4000c1f4 ); + +PROVIDE ( strcat = 0x4000c518 ); +PROVIDE ( strcmp = 0x40001274 ); +PROVIDE ( strchr = 0x4000c53c ); +PROVIDE ( strlcpy = 0x4000c584 ); +PROVIDE ( strstr = 0x4000c674 ); +PROVIDE ( strcasecmp = 0x400011cc ); +PROVIDE ( strdup = 0x4000143c ); +PROVIDE ( atoi = 0x400566c4 ); \ No newline at end of file diff --git a/esp-hal/ld/esp32c2/rom/additional.ld b/esp-hal/ld/esp32c2/rom/additional.ld index 15f88e99bdc..46ef79d8f36 100644 --- a/esp-hal/ld/esp32c2/rom/additional.ld +++ b/esp-hal/ld/esp32c2/rom/additional.ld @@ -6,3 +6,12 @@ memcmp = 0x40000494; strcpy = 0x40000498; strncpy = 0x4000049c; strncmp = 0x400004a4; + +PROVIDE ( strcat = 0x4000050c ); +PROVIDE ( strcmp = 0x400004a0 ); +PROVIDE ( strchr = 0x40000514 ); +PROVIDE ( strlcpy = 0x40000524 ); +PROVIDE ( strstr = 0x400004ac ); +PROVIDE ( strcasecmp = 0x40000504 ); +PROVIDE ( strdup = 0x40000510 ); +PROVIDE ( atoi = 0x40000580 ); diff --git a/esp-hal/ld/esp32c3/rom/additional.ld b/esp-hal/ld/esp32c3/rom/additional.ld index 70032e9e921..f45bec9b54e 100644 --- a/esp-hal/ld/esp32c3/rom/additional.ld +++ b/esp-hal/ld/esp32c3/rom/additional.ld @@ -10,3 +10,12 @@ strcpy = 0x40000364; abs = 0x40000424; PROVIDE(cache_dbus_mmu_set = 0x40000564); + +PROVIDE( strcat = 0x400003d8 ); +PROVIDE( strcmp = 0x4000036c ); +PROVIDE( strchr = 0x400003e0 ); +PROVIDE( strlcpy = 0x400003f0 ); +PROVIDE( strstr = 0x40000378 ); +PROVIDE( strcasecmp = 0x400003d0 ); +PROVIDE( strdup = 0x400003dc ); +PROVIDE( atoi = 0x4000044c ); \ No newline at end of file diff --git a/esp-hal/ld/esp32c6/rom/additional.ld b/esp-hal/ld/esp32c6/rom/additional.ld index 315c01f769a..a9c1801e3a8 100644 --- a/esp-hal/ld/esp32c6/rom/additional.ld +++ b/esp-hal/ld/esp32c6/rom/additional.ld @@ -8,3 +8,12 @@ strncpy = 0x400004bc; strcpy = 0x400004b8; abs = 0x40000578; + +PROVIDE(strcat = 0x4000052c); +PROVIDE(strcmp = 0x400004c0); +PROVIDE(strchr = 0x40000534); +PROVIDE(strlcpy = 0x40000544); +PROVIDE(strstr = 0x400004cc); +PROVIDE(strcasecmp = 0x40000524); +PROVIDE(strdup = 0x40000530); +PROVIDE(atoi = 0x400005a0); diff --git a/esp-hal/ld/esp32h2/rom/additional.ld b/esp-hal/ld/esp32h2/rom/additional.ld index ae1a956ca95..7c1c27ace8e 100644 --- a/esp-hal/ld/esp32h2/rom/additional.ld +++ b/esp-hal/ld/esp32h2/rom/additional.ld @@ -8,3 +8,12 @@ strncpy = 0x400004b4; strcpy = 0x400004b0; abs = 0x40000570; + +PROVIDE( strcat = 0x40000524 ); +PROVIDE( strcmp = 0x400004b8 ); +PROVIDE( strchr = 0x4000052c ); +PROVIDE( strlcpy = 0x4000053c ); +PROVIDE( strstr = 0x400004c4 ); +PROVIDE( strcasecmp = 0x4000051c ); +PROVIDE( strdup = 0x40000528 ); +PROVIDE( atoi = 0x40000598 ); diff --git a/esp-hal/ld/esp32s2/rom/additional.ld b/esp-hal/ld/esp32s2/rom/additional.ld index 41f18abbe25..ea535add6f0 100644 --- a/esp-hal/ld/esp32s2/rom/additional.ld +++ b/esp-hal/ld/esp32s2/rom/additional.ld @@ -10,3 +10,11 @@ strncmp = 0x4001ae64; bzero = 0x400078c8; PROVIDE ( cache_dbus_mmu_set = 0x40018eb0 ); + +PROVIDE ( strcat = 0x4001ad90 ); +PROVIDE ( strcmp = 0x40007be4 ); +PROVIDE ( strchr = 0x4001adb0 ); +PROVIDE ( strlcpy = 0x4001adf8 ); +PROVIDE ( strstr = 0x4001aee8 ); +PROVIDE ( strcasecmp = 0x40007b38 ); +PROVIDE ( strdup = 0x40007d84 ); diff --git a/esp-hal/ld/esp32s3/rom/additional.ld b/esp-hal/ld/esp32s3/rom/additional.ld index 4d8c27b5974..4e926c8adde 100644 --- a/esp-hal/ld/esp32s3/rom/additional.ld +++ b/esp-hal/ld/esp32s3/rom/additional.ld @@ -12,3 +12,12 @@ bzero = 0x40001260; PROVIDE(cache_dbus_mmu_set = 0x400019b0); PROVIDE( Cache_Suspend_DCache_Autoload = 0x40001734 ); PROVIDE( Cache_Suspend_DCache = 0x400018b4 ); + +PROVIDE( strcat = 0x40001374 ); +PROVIDE( strcmp = 0x40001230 ); +PROVIDE( strchr = 0x4000138c ); +PROVIDE( strlcpy = 0x400013bc ); +PROVIDE( strstr = 0x40001254 ); +PROVIDE( strcasecmp = 0x4000135c ); +PROVIDE( strdup = 0x40001380 ); +PROVIDE( atoi = 0x400014d0 ); diff --git a/esp-hal/src/aes/esp32.rs b/esp-hal/src/aes/esp32.rs index f9920a9a4d3..dd69d4c5439 100644 --- a/esp-hal/src/aes/esp32.rs +++ b/esp-hal/src/aes/esp32.rs @@ -56,7 +56,7 @@ impl<'d> Aes<'d> { } pub(super) fn write_start(&mut self) { - self.aes.start().write(|w| w.start().set_bit()) + self.aes.start().write(|w| w.start().set_bit()); } pub(super) fn read_idle(&mut self) -> bool { diff --git a/esp-hal/src/aes/esp32cX.rs b/esp-hal/src/aes/esp32cX.rs index bd653da4a84..9477b17579b 100644 --- a/esp-hal/src/aes/esp32cX.rs +++ b/esp-hal/src/aes/esp32cX.rs @@ -3,7 +3,7 @@ use crate::{ system::{Peripheral as PeripheralEnable, PeripheralClockControl}, }; -impl<'d> Aes<'d> { +impl Aes<'_> { pub(super) fn init(&mut self) { PeripheralClockControl::enable(PeripheralEnable::Aes); self.write_dma(false); @@ -13,7 +13,7 @@ impl<'d> Aes<'d> { match enable_dma { true => self.aes.dma_enable().write(|w| w.dma_enable().set_bit()), false => self.aes.dma_enable().write(|w| w.dma_enable().clear_bit()), - } + }; } pub(super) fn write_key(&mut self, key: &[u8]) { @@ -34,7 +34,7 @@ impl<'d> Aes<'d> { } pub(super) fn write_start(&mut self) { - self.aes.trigger().write(|w| w.trigger().set_bit()) + self.aes.trigger().write(|w| w.trigger().set_bit()); } pub(super) fn read_idle(&mut self) -> bool { diff --git a/esp-hal/src/aes/esp32s2.rs b/esp-hal/src/aes/esp32s2.rs index 926612821c4..d00c34d20e8 100644 --- a/esp-hal/src/aes/esp32s2.rs +++ b/esp-hal/src/aes/esp32s2.rs @@ -21,7 +21,7 @@ impl<'d> Aes<'d> { match enable_dma { true => self.aes.dma_enable().write(|w| w.dma_enable().set_bit()), false => self.aes.dma_enable().write(|w| w.dma_enable().clear_bit()), - } + }; } pub(super) fn write_key(&mut self, key: &[u8]) { @@ -67,7 +67,7 @@ impl<'d> Aes<'d> { } pub(super) fn write_start(&mut self) { - self.aes.trigger().write(|w| w.trigger().set_bit()) + self.aes.trigger().write(|w| w.trigger().set_bit()); } pub(super) fn read_idle(&mut self) -> bool { diff --git a/esp-hal/src/aes/esp32s3.rs b/esp-hal/src/aes/esp32s3.rs index 6adc108449f..3e8085348a8 100644 --- a/esp-hal/src/aes/esp32s3.rs +++ b/esp-hal/src/aes/esp32s3.rs @@ -13,7 +13,7 @@ impl<'d> Aes<'d> { match enable_dma { true => self.aes.dma_enable().write(|w| w.dma_enable().set_bit()), false => self.aes.dma_enable().write(|w| w.dma_enable().clear_bit()), - } + }; } pub(super) fn write_key(&mut self, key: &[u8]) { @@ -39,7 +39,7 @@ impl<'d> Aes<'d> { } pub(super) fn write_start(&mut self) { - self.aes.trigger().write(|w| w.trigger().set_bit()) + self.aes.trigger().write(|w| w.trigger().set_bit()); } pub(super) fn read_idle(&mut self) -> bool { diff --git a/esp-hal/src/aes/mod.rs b/esp-hal/src/aes/mod.rs index bbf18752534..e6815b8877b 100644 --- a/esp-hal/src/aes/mod.rs +++ b/esp-hal/src/aes/mod.rs @@ -166,7 +166,7 @@ impl<'d> Aes<'d> { self.set_block(block); self.start(); while !(self.is_idle()) {} - self.get_block(block); + self.block(block); } fn set_mode(&mut self, mode: u8) { @@ -181,7 +181,7 @@ impl<'d> Aes<'d> { self.write_block(block); } - fn get_block(&self, block: &mut [u8; 16]) { + fn block(&self, block: &mut [u8; 16]) { self.read_block(block); } @@ -250,6 +250,7 @@ pub mod dma { WriteBuffer, }, peripherals::AES, + Blocking, }; const ALIGN_SIZE: usize = core::mem::size_of::(); @@ -275,7 +276,7 @@ pub mod dma { /// The underlying [`Aes`](super::Aes) driver pub aes: super::Aes<'d>, - channel: Channel<'d, ::Dma, crate::Blocking>, + channel: Channel<'d, ::Dma, Blocking>, rx_chain: DescriptorChain, tx_chain: DescriptorChain, } @@ -284,12 +285,11 @@ pub mod dma { /// Enable DMA for the current instance of the AES driver pub fn with_dma( self, - channel: Channel<'d, C, crate::Blocking>, + channel: Channel<'d, C, Blocking>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> AesDma<'d> where - Self: Sized, C: DmaChannelConvert<::Dma>, { AesDma { @@ -301,13 +301,13 @@ pub mod dma { } } - impl<'d> core::fmt::Debug for AesDma<'d> { + impl core::fmt::Debug for AesDma<'_> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("AesDma").finish() } } - impl<'d> DmaSupport for AesDma<'d> { + impl DmaSupport for AesDma<'_> { fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) { while self.aes.aes.state().read().state().bits() != 2 // DMA status DONE == 2 && !self.channel.tx.is_done() @@ -347,7 +347,7 @@ pub mod dma { } } - impl<'d> AesDma<'d> { + impl AesDma<'_> { /// Writes the encryption key to the AES hardware, checking that its /// length matches expected constraints. pub fn write_key(&mut self, key: K) diff --git a/esp-hal/src/analog/adc/calibration/basic.rs b/esp-hal/src/analog/adc/calibration/basic.rs index 4943d6648e8..643653d5178 100644 --- a/esp-hal/src/analog/adc/calibration/basic.rs +++ b/esp-hal/src/analog/adc/calibration/basic.rs @@ -36,7 +36,7 @@ where fn new_cal(atten: Attenuation) -> Self { // Try to get init code (Dout0) from efuse // Dout0 means mean raw ADC value when zero voltage applied to input. - let cal_val = ADCI::get_init_code(atten).unwrap_or_else(|| { + let cal_val = ADCI::init_code(atten).unwrap_or_else(|| { // As a fallback try to calibrate via connecting input to ground internally. AdcConfig::::adc_calibrate(atten, AdcCalSource::Gnd) }); diff --git a/esp-hal/src/analog/adc/calibration/line.rs b/esp-hal/src/analog/adc/calibration/line.rs index 64b0105bc6f..ea6bf66f3b1 100644 --- a/esp-hal/src/analog/adc/calibration/line.rs +++ b/esp-hal/src/analog/adc/calibration/line.rs @@ -59,8 +59,8 @@ where // Try get the reference point (Dout, Vin) from efuse // Dout means mean raw ADC value when specified Vin applied to input. - let (code, mv) = ADCI::get_cal_code(atten) - .map(|code| (code, ADCI::get_cal_mv(atten))) + let (code, mv) = ADCI::cal_code(atten) + .map(|code| (code, ADCI::cal_mv(atten))) .unwrap_or_else(|| { // As a fallback try to calibrate using reference voltage source. // This method is not too good because actual reference voltage may varies diff --git a/esp-hal/src/analog/adc/mod.rs b/esp-hal/src/analog/adc/mod.rs index b7d4e58d2a8..a0b3193cdcc 100644 --- a/esp-hal/src/analog/adc/mod.rs +++ b/esp-hal/src/analog/adc/mod.rs @@ -31,13 +31,11 @@ //! # use esp_hal::analog::adc::Attenuation; //! # use esp_hal::analog::adc::Adc; //! # use esp_hal::delay::Delay; -//! # use esp_hal::gpio::Io; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -#![cfg_attr(esp32, doc = "let analog_pin = io.pins.gpio32;")] -#![cfg_attr(any(esp32s2, esp32s3), doc = "let analog_pin = io.pins.gpio3;")] +#![cfg_attr(esp32, doc = "let analog_pin = peripherals.GPIO32;")] +#![cfg_attr(any(esp32s2, esp32s3), doc = "let analog_pin = peripherals.GPIO3;")] #![cfg_attr( not(any(esp32, esp32s2, esp32s3)), - doc = "let analog_pin = io.pins.gpio2;" + doc = "let analog_pin = peripherals.GPIO2;" )] //! let mut adc1_config = AdcConfig::new(); //! let mut pin = adc1_config.enable_pin( @@ -237,17 +235,17 @@ trait AdcCalEfuse { /// Get ADC calibration init code /// /// Returns digital value for zero voltage for a given attenuation - fn get_init_code(atten: Attenuation) -> Option; + fn init_code(atten: Attenuation) -> Option; /// Get ADC calibration reference point voltage /// /// Returns reference voltage (millivolts) for a given attenuation - fn get_cal_mv(atten: Attenuation) -> u16; + fn cal_mv(atten: Attenuation) -> u16; /// Get ADC calibration reference point digital value /// /// Returns digital value for reference voltage for a given attenuation - fn get_cal_code(atten: Attenuation) -> Option; + fn cal_code(atten: Attenuation) -> Option; } macro_rules! impl_adc_interface { diff --git a/esp-hal/src/analog/adc/riscv.rs b/esp-hal/src/analog/adc/riscv.rs index b25eaa2fc86..c9c4bcdd655 100644 --- a/esp-hal/src/analog/adc/riscv.rs +++ b/esp-hal/src/analog/adc/riscv.rs @@ -502,36 +502,36 @@ where #[cfg(any(esp32c2, esp32c3, esp32c6))] impl super::AdcCalEfuse for crate::peripherals::ADC1 { - fn get_init_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_init_code(1, atten) + fn init_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_init_code(1, atten) } - fn get_cal_mv(atten: Attenuation) -> u16 { - Efuse::get_rtc_calib_cal_mv(1, atten) + fn cal_mv(atten: Attenuation) -> u16 { + Efuse::rtc_calib_cal_mv(1, atten) } - fn get_cal_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_cal_code(1, atten) + fn cal_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_cal_code(1, atten) } } #[cfg(esp32c3)] impl super::AdcCalEfuse for crate::peripherals::ADC2 { - fn get_init_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_init_code(2, atten) + fn init_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_init_code(2, atten) } - fn get_cal_mv(atten: Attenuation) -> u16 { - Efuse::get_rtc_calib_cal_mv(2, atten) + fn cal_mv(atten: Attenuation) -> u16 { + Efuse::rtc_calib_cal_mv(2, atten) } - fn get_cal_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_cal_code(2, atten) + fn cal_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_cal_code(2, atten) } } -impl<'d, ADCI, PIN, CS> embedded_hal_02::adc::OneShot> - for Adc<'d, ADCI> +impl embedded_hal_02::adc::OneShot> + for Adc<'_, ADCI> where PIN: embedded_hal_02::adc::Channel + super::AdcChannel, ADCI: RegisterAccess, diff --git a/esp-hal/src/analog/adc/xtensa.rs b/esp-hal/src/analog/adc/xtensa.rs index bc9d6ef3202..324c79462db 100644 --- a/esp-hal/src/analog/adc/xtensa.rs +++ b/esp-hal/src/analog/adc/xtensa.rs @@ -562,31 +562,31 @@ where #[cfg(esp32s3)] impl super::AdcCalEfuse for crate::peripherals::ADC1 { - fn get_init_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_init_code(1, atten) + fn init_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_init_code(1, atten) } - fn get_cal_mv(atten: Attenuation) -> u16 { - Efuse::get_rtc_calib_cal_mv(1, atten) + fn cal_mv(atten: Attenuation) -> u16 { + Efuse::rtc_calib_cal_mv(1, atten) } - fn get_cal_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_cal_code(1, atten) + fn cal_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_cal_code(1, atten) } } #[cfg(esp32s3)] impl super::AdcCalEfuse for crate::peripherals::ADC2 { - fn get_init_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_init_code(2, atten) + fn init_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_init_code(2, atten) } - fn get_cal_mv(atten: Attenuation) -> u16 { - Efuse::get_rtc_calib_cal_mv(2, atten) + fn cal_mv(atten: Attenuation) -> u16 { + Efuse::rtc_calib_cal_mv(2, atten) } - fn get_cal_code(atten: Attenuation) -> Option { - Efuse::get_rtc_calib_cal_code(2, atten) + fn cal_code(atten: Attenuation) -> Option { + Efuse::rtc_calib_cal_code(2, atten) } } diff --git a/esp-hal/src/analog/dac.rs b/esp-hal/src/analog/dac.rs index 8cb1e859db3..246dfa796c0 100644 --- a/esp-hal/src/analog/dac.rs +++ b/esp-hal/src/analog/dac.rs @@ -17,14 +17,11 @@ //! ### Write a value to a DAC channel //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::gpio::Io; //! # use esp_hal::analog::dac::Dac; //! # use esp_hal::delay::Delay; //! # use embedded_hal::delay::DelayNs; -//! -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -#![cfg_attr(esp32, doc = "let dac1_pin = io.pins.gpio25;")] -#![cfg_attr(esp32s2, doc = "let dac1_pin = io.pins.gpio17;")] +#![cfg_attr(esp32, doc = "let dac1_pin = peripherals.GPIO25;")] +#![cfg_attr(esp32s2, doc = "let dac1_pin = peripherals.GPIO17;")] //! let mut dac1 = Dac::new(peripherals.DAC1, dac1_pin); //! //! let mut delay = Delay::new(); diff --git a/esp-hal/src/assist_debug.rs b/esp-hal/src/assist_debug.rs index 4e5fe1e8c90..d99a0334613 100644 --- a/esp-hal/src/assist_debug.rs +++ b/esp-hal/src/assist_debug.rs @@ -26,7 +26,7 @@ use crate::{ interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, - peripherals::ASSIST_DEBUG, + peripherals::{Interrupt, ASSIST_DEBUG}, InterruptConfigurable, }; @@ -47,26 +47,23 @@ impl<'d> DebugAssist<'d> { } } -impl<'d> crate::private::Sealed for DebugAssist<'d> {} +impl crate::private::Sealed for DebugAssist<'_> {} -impl<'d> InterruptConfigurable for DebugAssist<'d> { +impl InterruptConfigurable for DebugAssist<'_> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::ASSIST_DEBUG, - handler.handler(), - ); - crate::interrupt::enable( - crate::peripherals::Interrupt::ASSIST_DEBUG, - handler.priority(), - ) - .unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::ASSIST_DEBUG); } + unsafe { crate::interrupt::bind_interrupt(Interrupt::ASSIST_DEBUG, handler.handler()) }; + unwrap!(crate::interrupt::enable( + Interrupt::ASSIST_DEBUG, + handler.priority() + )); } } #[cfg(assist_debug_sp_monitor)] -impl<'d> DebugAssist<'d> { +impl DebugAssist<'_> { /// Enable SP monitoring on main core. When the SP exceeds the /// `lower_bound` or `upper_bound` threshold, the module will record the PC /// pointer and generate an interrupt. @@ -139,7 +136,7 @@ impl<'d> DebugAssist<'d> { } /// Get SP monitoring PC value on main core. - pub fn get_sp_monitor_pc(&self) -> u32 { + pub fn sp_monitor_pc(&self) -> u32 { self.debug_assist .core_0_sp_pc() .read() @@ -222,13 +219,13 @@ impl<'d> DebugAssist<'d> { } /// Get SP monitoring PC value on secondary core. - pub fn get_core1_sp_monitor_pc(&self) -> u32 { + pub fn core1_sp_monitor_pc(&self) -> u32 { self.debug_assist.core_1_sp_pc.read().core_1_sp_pc().bits() } } #[cfg(assist_debug_region_monitor)] -impl<'d> DebugAssist<'d> { +impl DebugAssist<'_> { /// Enable region monitoring of read/write performed by the main CPU in a /// certain memory region0. Whenever the bus reads or writes in the /// specified memory region, an interrupt will be triggered. Two memory @@ -385,7 +382,7 @@ impl<'d> DebugAssist<'d> { } /// Get region monitoring PC value on main core. - pub fn get_region_monitor_pc(&self) -> u32 { + pub fn region_monitor_pc(&self) -> u32 { self.debug_assist .core_0_area_pc() .read() @@ -551,7 +548,7 @@ impl<'d> DebugAssist<'d> { } /// Get region monitoring PC value on secondary core. - pub fn get_core1_region_monitor_pc(&self) -> u32 { + pub fn core1_region_monitor_pc(&self) -> u32 { self.debug_assist .core_1_area_pc() .read() diff --git a/esp-hal/src/clock/clocks_ll/esp32c6.rs b/esp-hal/src/clock/clocks_ll/esp32c6.rs index ef349384ba7..71cec2c84f9 100644 --- a/esp-hal/src/clock/clocks_ll/esp32c6.rs +++ b/esp-hal/src/clock/clocks_ll/esp32c6.rs @@ -244,7 +244,7 @@ fn clk_ll_mspi_fast_set_hs_divider(divider: u32) { .mspi_clk_conf() .modify(|_, w| w.mspi_fast_hs_div_num().bits(5)), _ => panic!("Unsupported HS MSPI_FAST divider"), - } + }; } } diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index b3f2d43351f..c00b9c46912 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -7,11 +7,12 @@ use crate::soc::is_slice_in_psram; /// Holds all the information needed to configure a DMA channel for a transfer. pub struct Preparation { - pub(super) start: *mut DmaDescriptor, + /// The descriptor the DMA will start from. + pub start: *mut DmaDescriptor, - /// block size for PSRAM transfers + /// Block size for PSRAM transfers #[cfg_attr(not(esp32s3), allow(dead_code))] - pub(super) block_size: Option, + pub block_size: Option, /// Specifies whether descriptor linked list specified in `start` conforms /// to the alignment requirements required to enable burst transfers. @@ -22,8 +23,35 @@ pub struct Preparation { /// There are no additional alignment requirements for TX burst transfers, /// but RX transfers require all descriptors to have buffer pointers and /// sizes that are a multiple of 4 (word aligned). - pub(super) is_burstable: bool, - // alignment, check_owner, etc. + pub is_burstable: bool, + + /// Configures the "check owner" feature of the DMA channel. + /// + /// Most DMA channels allow software to configure whether the hardware + /// checks that [DmaDescriptor::owner] is set to [Owner::Dma] before + /// consuming the descriptor. If this check fails, the channel stops + /// operating and fires + /// [DmaRxInterrupt::DescriptorError]/[DmaTxInterrupt::DescriptorError]. + /// + /// This field allows buffer implementation to configure this behaviour. + /// - `Some(true)`: DMA channel must check the owner bit. + /// - `Some(false)`: DMA channel must NOT check the owner bit. + /// - `None`: DMA channel should check the owner bit if it is supported. + /// + /// Some buffer implementations may require that the DMA channel performs + /// this check before consuming the descriptor to ensure correct + /// behaviour. e.g. To prevent wrap-around in a circular transfer. + /// + /// Some buffer implementations may require that the DMA channel does NOT + /// perform this check as the ownership bit will not be set before the + /// channel tries to consume the descriptor. + /// + /// Most implementations won't have any such requirements and will work + /// correctly regardless of whether the DMA channel checks or not. + /// + /// Note: If the DMA channel doesn't support the provided option, + /// preparation will fail. + pub check_owner: Option, } /// [DmaTxBuffer] is a DMA descriptor + memory combo that can be used for @@ -303,6 +331,7 @@ unsafe impl DmaTxBuffer for DmaTxBuf { block_size: self.block_size, // This is TX, the DMA channel is free to do a burst transfer. is_burstable: true, + check_owner: None, } } @@ -453,6 +482,7 @@ unsafe impl DmaRxBuffer for DmaRxBuf { // In the future, it could either enforce the alignment or calculate if the alignment // requirements happen to be met. is_burstable: false, + check_owner: None, } } @@ -580,6 +610,7 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { // This is TX, the DMA channel is free to do a burst transfer. is_burstable: true, + check_owner: None, } } @@ -611,6 +642,7 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { // DmaRxTxBuf doesn't currently enforce the alignment requirements required for // bursting. is_burstable: false, + check_owner: None, } } @@ -751,6 +783,12 @@ unsafe impl DmaRxBuffer for DmaRxStreamBuf { // DmaRxStreamBuf doesn't currently enforce the alignment requirements required for // bursting. is_burstable: false, + + // Whilst we give ownership of the descriptors the DMA, the correctness of this buffer + // implementation doesn't rely on the DMA checking for descriptor ownership. + // No descriptor is added back to the end of the stream before it's ready for the DMA + // to consume it. + check_owner: None, } } @@ -958,6 +996,10 @@ unsafe impl DmaTxBuffer for EmptyBuf { // This is TX, the DMA channel is free to do a burst transfer. is_burstable: true, + + // As we don't give ownership of the descriptor to the DMA, it's important that the DMA + // channel does *NOT* check for ownership, otherwise the channel will return an error. + check_owner: Some(false), } } @@ -985,6 +1027,10 @@ unsafe impl DmaRxBuffer for EmptyBuf { // As much as bursting is meaningless here, the descriptor does meet the requirements. is_burstable: true, + + // As we don't give ownership of the descriptor to the DMA, it's important that the DMA + // channel does *NOT* check for ownership, otherwise the channel will return an error. + check_owner: Some(false), } } diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index 59d19ecdb80..8ac8b6b4731 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -17,7 +17,9 @@ use crate::{ dma::*, peripheral::PeripheralRef, + peripherals::Interrupt, system::{Peripheral, PeripheralClockControl}, + Blocking, }; #[doc(hidden)] @@ -33,6 +35,36 @@ impl crate::private::Sealed for AnyGdmaChannel {} impl DmaChannel for AnyGdmaChannel { type Rx = ChannelRxImpl; type Tx = ChannelTxImpl; + + fn async_handler(ch: &Channel<'_, Self, M>) -> InterruptHandler { + match ch.tx.tx_impl.0.number() { + 0 => DmaChannel0::handler(), + #[cfg(not(esp32c2))] + 1 => DmaChannel1::handler(), + #[cfg(not(esp32c2))] + 2 => DmaChannel2::handler(), + #[cfg(esp32s3)] + 3 => DmaChannel3::handler(), + #[cfg(esp32s3)] + 4 => DmaChannel4::handler(), + _ => unreachable!(), + } + } + + fn interrupts(ch: &Channel<'_, Self, M>) -> &'static [Interrupt] { + match ch.tx.tx_impl.0.number() { + 0 => DmaChannel0::isrs(), + #[cfg(not(esp32c2))] + 1 => DmaChannel1::isrs(), + #[cfg(not(esp32c2))] + 2 => DmaChannel2::isrs(), + #[cfg(esp32s3)] + 3 => DmaChannel3::isrs(), + #[cfg(esp32s3)] + 4 => DmaChannel4::isrs(), + _ => unreachable!(), + } + } } #[non_exhaustive] @@ -142,6 +174,12 @@ impl RegisterAccess for ChannelTxImpl { .modify(|_, w| w.outlink_restart().set_bit()); } + fn set_check_owner(&self, check_owner: Option) { + self.ch() + .out_conf1() + .modify(|_, w| w.out_check_owner().bit(check_owner.unwrap_or(true))); + } + #[cfg(esp32s3)] fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { self.ch() @@ -151,6 +189,12 @@ impl RegisterAccess for ChannelTxImpl { } impl TxRegisterAccess for ChannelTxImpl { + fn set_auto_write_back(&self, enable: bool) { + self.ch() + .out_conf0() + .modify(|_, w| w.out_auto_wrback().bit(enable)); + } + fn last_dscr_address(&self) -> usize { self.ch() .out_eof_des_addr() @@ -172,7 +216,7 @@ impl InterruptAccess for ChannelTxImpl { }; } w - }) + }); } fn is_listening(&self) -> EnumSet { @@ -206,7 +250,7 @@ impl InterruptAccess for ChannelTxImpl { }; } w - }) + }); } fn pending_interrupts(&self) -> EnumSet { @@ -321,6 +365,12 @@ impl RegisterAccess for ChannelRxImpl { .modify(|_, w| w.inlink_restart().set_bit()); } + fn set_check_owner(&self, check_owner: Option) { + self.ch() + .in_conf1() + .modify(|_, w| w.in_check_owner().bit(check_owner.unwrap_or(true))); + } + #[cfg(esp32s3)] fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { self.ch() @@ -388,7 +438,7 @@ impl InterruptAccess for ChannelRxImpl { }; } w - }) + }); } fn pending_interrupts(&self) -> EnumSet { @@ -425,10 +475,7 @@ pub struct ChannelCreator {} impl Channel<'_, CH, M> { /// Asserts that the channel is compatible with the given peripheral. - pub fn runtime_ensure_compatible( - &self, - _peripheral: &PeripheralRef<'_, P>, - ) { + pub fn runtime_ensure_compatible(&self, _peripheral: &PeripheralRef<'_, P>) { // No runtime checks; GDMA channels are compatible with any peripheral } } @@ -442,9 +489,27 @@ macro_rules! impl_channel { impl crate::private::Sealed for [] {} + impl [] { + fn handler() -> InterruptHandler { + $async_handler + } + + fn isrs() -> &'static [Interrupt] { + &[$(Interrupt::$interrupt),*] + } + } + impl DmaChannel for [] { type Rx = ChannelRxImpl>; type Tx = ChannelTxImpl>; + + fn async_handler(_ch: &Channel<'_, Self, M>) -> InterruptHandler { + Self::handler() + } + + fn interrupts(_ch: &Channel<'_, Self, M>,) -> &'static [Interrupt] { + Self::isrs() + } } impl DmaChannelConvert for [] { @@ -457,71 +522,29 @@ macro_rules! impl_channel { } impl DmaChannelExt for [] { - fn get_rx_interrupts() -> impl InterruptAccess { + fn rx_interrupts() -> impl InterruptAccess { ChannelRxImpl(SpecificGdmaChannel::<$num> {}) } - fn get_tx_interrupts() -> impl InterruptAccess { + fn tx_interrupts() -> impl InterruptAccess { ChannelTxImpl(SpecificGdmaChannel::<$num> {}) } - - fn set_isr(handler: $crate::interrupt::InterruptHandler) { - let mut dma = unsafe { crate::peripherals::DMA::steal() }; - $( - dma.[< bind_ $interrupt:lower _interrupt >](handler.handler()); - $crate::interrupt::enable($crate::peripherals::Interrupt::$interrupt, handler.priority()).unwrap(); - )* - } } impl ChannelCreator<$num> { - fn do_configure<'a, M: crate::Mode>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> crate::dma::Channel<'a, [], M> { - let tx_impl = ChannelTxImpl(SpecificGdmaChannel::<$num> {}); - tx_impl.set_burst_mode(burst_mode); - tx_impl.set_priority(priority); - - let rx_impl = ChannelRxImpl(SpecificGdmaChannel::<$num> {}); - rx_impl.set_burst_mode(burst_mode); - rx_impl.set_priority(priority); - // clear the mem2mem mode to avoid failed DMA if this - // channel was previously used for a mem2mem transfer. - rx_impl.set_mem2mem_mode(false); - - crate::dma::Channel { - tx: ChannelTx::new(tx_impl, burst_mode), - rx: ChannelRx::new(rx_impl, burst_mode), - phantom: PhantomData, - } - } - /// Configure the channel for use with blocking APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. pub fn configure<'a>( self, burst_mode: bool, priority: DmaPriority, - ) -> crate::dma::Channel<'a, [], crate::Blocking> { - self.do_configure(burst_mode, priority) - } - - /// Configure the channel for use with async APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. - pub fn configure_for_async<'a>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> crate::dma::Channel<'a, [], $crate::Async> { - let this = self.do_configure(burst_mode, priority); + ) -> Channel<'a, [], Blocking> { + let mut this = Channel { + tx: ChannelTx::new(ChannelTxImpl(SpecificGdmaChannel::<$num> {})), + rx: ChannelRx::new(ChannelRxImpl(SpecificGdmaChannel::<$num> {})), + phantom: PhantomData, + }; - []::set_isr($async_handler); + this.configure(burst_mode, priority); this } diff --git a/esp-hal/src/dma/m2m.rs b/esp-hal/src/dma/m2m.rs index 0e0aee187d1..b1ff0f60bc5 100644 --- a/esp-hal/src/dma/m2m.rs +++ b/esp-hal/src/dma/m2m.rs @@ -18,6 +18,8 @@ use crate::{ Tx, WriteBuffer, }, + Async, + Blocking, Mode, }; @@ -36,19 +38,18 @@ where peripheral: DmaPeripheral, } -impl<'d, M> Mem2Mem<'d, M> -where - M: Mode, -{ +impl<'d> Mem2Mem<'d, Blocking> { /// Create a new Mem2Mem instance. - pub fn new( - channel: Channel<'d, CH, M>, + pub fn new( + channel: Channel<'d, CH, DM>, peripheral: impl DmaEligible, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> Result where CH: DmaChannelConvert, + DM: Mode, + Channel<'d, CH, Blocking>: From>, { unsafe { Self::new_unsafe( @@ -62,8 +63,8 @@ where } /// Create a new Mem2Mem instance with specific chunk size. - pub fn new_with_chunk_size( - channel: Channel<'d, CH, M>, + pub fn new_with_chunk_size( + channel: Channel<'d, CH, DM>, peripheral: impl DmaEligible, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], @@ -71,6 +72,8 @@ where ) -> Result where CH: DmaChannelConvert, + DM: Mode, + Channel<'d, CH, Blocking>: From>, { unsafe { Self::new_unsafe( @@ -89,8 +92,8 @@ where /// /// You must ensure that your not using DMA for the same peripheral and /// that your the only one using the DmaPeripheral. - pub unsafe fn new_unsafe( - channel: Channel<'d, CH, M>, + pub unsafe fn new_unsafe( + channel: Channel<'d, CH, DM>, peripheral: DmaPeripheral, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], @@ -98,6 +101,8 @@ where ) -> Result where CH: DmaChannelConvert, + DM: Mode, + Channel<'d, CH, Blocking>: From>, { if !(1..=4092).contains(&chunk_size) { return Err(DmaError::InvalidChunkSize); @@ -106,13 +111,28 @@ where return Err(DmaError::OutOfDescriptors); } Ok(Mem2Mem { - channel: channel.degrade(), + channel: Channel::<_, Blocking>::from(channel).degrade(), peripheral, rx_chain: DescriptorChain::new_with_chunk_size(rx_descriptors, chunk_size), tx_chain: DescriptorChain::new_with_chunk_size(tx_descriptors, chunk_size), }) } + /// Convert Mem2Mem to an async Mem2Mem. + pub fn into_async(self) -> Mem2Mem<'d, Async> { + Mem2Mem { + channel: self.channel.into_async(), + rx_chain: self.rx_chain, + tx_chain: self.tx_chain, + peripheral: self.peripheral, + } + } +} + +impl Mem2Mem<'_, M> +where + M: Mode, +{ /// Start a memory to memory transfer. pub fn start_transfer<'t, TXBUF, RXBUF>( &mut self, @@ -157,7 +177,7 @@ where } } -impl<'d, MODE> DmaSupport for Mem2Mem<'d, MODE> +impl DmaSupport for Mem2Mem<'_, MODE> where MODE: Mode, { diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index c228517e691..6deca41d657 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -18,22 +18,23 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::dma_buffers; -//! # use esp_hal::gpio::Io; -//! # use esp_hal::spi::{master::Spi, SpiMode}; +//! # use esp_hal::spi::{master::{Config, Spi}, SpiMode}; //! # use esp_hal::dma::{Dma, DmaPriority}; //! let dma = Dma::new(peripherals.DMA); #![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.spi2channel;")] #![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let sclk = io.pins.gpio0; -//! let miso = io.pins.gpio2; -//! let mosi = io.pins.gpio4; -//! let cs = io.pins.gpio5; +//! let sclk = peripherals.GPIO0; +//! let miso = peripherals.GPIO2; +//! let mosi = peripherals.GPIO4; +//! let cs = peripherals.GPIO5; //! -//! let mut spi = Spi::new( +//! let mut spi = Spi::new_with_config( //! peripherals.SPI2, -//! 100.kHz(), -//! SpiMode::Mode0, +//! Config { +//! frequency: 100.kHz(), +//! mode: SpiMode::Mode0, +//! ..Config::default() +//! }, //! ) //! .with_sck(sclk) //! .with_mosi(mosi) @@ -58,6 +59,25 @@ use core::{cmp::min, fmt::Debug, marker::PhantomData, sync::atomic::compiler_fence}; +use enumset::{EnumSet, EnumSetType}; + +pub use self::buffers::*; +#[cfg(gdma)] +pub use self::gdma::*; +#[cfg(gdma)] +pub use self::m2m::*; +#[cfg(pdma)] +pub use self::pdma::*; +use crate::{ + interrupt::InterruptHandler, + peripherals::Interrupt, + soc::is_slice_in_dram, + Async, + Blocking, + Cpu, + Mode, +}; + trait Word: crate::private::Sealed {} macro_rules! impl_word { @@ -351,16 +371,10 @@ impl DmaDescriptor { } } -use enumset::{EnumSet, EnumSetType}; - -pub use self::buffers::*; -#[cfg(gdma)] -pub use self::gdma::*; -#[cfg(gdma)] -pub use self::m2m::*; -#[cfg(pdma)] -pub use self::pdma::*; -use crate::{interrupt::InterruptHandler, soc::is_slice_in_dram, Mode}; +// The pointers in the descriptor can be Sent. +// Marking this Send also allows DmaBuffer implementations to automatically be +// Send (where the compiler sees fit). +unsafe impl Send for DmaDescriptor {} mod buffers; #[cfg(gdma)] @@ -781,6 +795,9 @@ pub enum DmaError { UnsupportedMemoryRegion, /// Invalid DMA chunk size InvalidChunkSize, + /// Indicates writing to or reading from a circular DMA transaction is done + /// too late and the DMA buffers already overrun / underrun. + Late, } impl From for DmaError { @@ -930,12 +947,6 @@ macro_rules! impl_dma_eligible { }; } -/// Marker trait -#[doc(hidden)] -pub trait PeripheralMarker { - fn peripheral(&self) -> crate::system::Peripheral; -} - #[doc(hidden)] #[derive(Debug)] pub struct DescriptorChain { @@ -1313,7 +1324,7 @@ impl TxCircularState { } } - pub(crate) fn update(&mut self, channel: &T) + pub(crate) fn update(&mut self, channel: &T) -> Result<(), DmaError> where T: Tx, { @@ -1323,6 +1334,23 @@ impl TxCircularState { { channel.clear_out(DmaTxInterrupt::Eof); + // check if all descriptors are owned by CPU - this indicates we failed to push + // data fast enough in future we can enable `check_owner` and check + // the interrupt instead + let mut current = self.last_seen_handled_descriptor_ptr; + loop { + let descr = unsafe { current.read_volatile() }; + if descr.owner() == Owner::Cpu { + current = descr.next; + } else { + break; + } + + if current == self.last_seen_handled_descriptor_ptr { + return Err(DmaError::Late); + } + } + let descr_address = channel.last_out_dscr_address() as *mut DmaDescriptor; let mut ptr = self.last_seen_handled_descriptor_ptr; @@ -1376,6 +1404,8 @@ impl TxCircularState { self.last_seen_handled_descriptor_ptr = descr_address; } + + Ok(()) } pub(crate) fn push(&mut self, data: &[u8]) -> Result { @@ -1404,6 +1434,8 @@ impl TxCircularState { &mut self, f: impl FnOnce(&mut [u8]) -> usize, ) -> Result { + // this might write less than available in case of a wrap around + // caller needs to check and write the remaining part let written = unsafe { let dst = self.buffer_start.add(self.write_offset).cast_mut(); let block_size = usize::min(self.available, self.buffer_len - self.write_offset); @@ -1414,12 +1446,15 @@ impl TxCircularState { let mut forward = written; loop { unsafe { - let dw0 = self.write_descr_ptr.read_volatile(); - let segment_len = dw0.len(); - self.write_descr_ptr = if dw0.next.is_null() { + let mut descr = self.write_descr_ptr.read_volatile(); + descr.set_owner(Owner::Dma); + self.write_descr_ptr.write_volatile(descr); + + let segment_len = descr.len(); + self.write_descr_ptr = if descr.next.is_null() { self.first_desc_ptr } else { - dw0.next + descr.next }; if forward <= segment_len { @@ -1454,7 +1489,7 @@ impl RxCircularState { } } - pub(crate) fn update(&mut self) { + pub(crate) fn update(&mut self) -> Result<(), DmaError> { if self.last_seen_handled_descriptor_ptr.is_null() { // initially start at last descriptor (so that next will be the first // descriptor) @@ -1465,6 +1500,7 @@ impl RxCircularState { unsafe { self.last_seen_handled_descriptor_ptr.read_volatile() }.next; let mut current_in_descr = unsafe { current_in_descr_ptr.read_volatile() }; + let last_seen_ptr = self.last_seen_handled_descriptor_ptr; while current_in_descr.owner() == Owner::Cpu { self.available += current_in_descr.len(); self.last_seen_handled_descriptor_ptr = current_in_descr_ptr; @@ -1472,7 +1508,13 @@ impl RxCircularState { current_in_descr_ptr = unsafe { self.last_seen_handled_descriptor_ptr.read_volatile() }.next; current_in_descr = unsafe { current_in_descr_ptr.read_volatile() }; + + if current_in_descr_ptr == last_seen_ptr { + return Err(DmaError::Late); + } } + + Ok(()) } pub(crate) fn pop(&mut self, data: &mut [u8]) -> Result { @@ -1523,23 +1565,31 @@ impl RxCircularState { } /// A description of a DMA Channel. -pub trait DmaChannel: crate::private::Sealed { +pub trait DmaChannel: crate::private::Sealed + Sized { /// A description of the RX half of a DMA Channel. type Rx: RxRegisterAccess + InterruptAccess; /// A description of the TX half of a DMA Channel. type Tx: TxRegisterAccess + InterruptAccess; + + /// Returns the async interrupt handler. + fn async_handler(ch: &Channel<'_, Self, M>) -> InterruptHandler; + + /// Returns the interrupt. + fn interrupts(ch: &Channel<'_, Self, M>) -> &'static [Interrupt]; } #[doc(hidden)] pub trait DmaChannelExt: DmaChannel { - fn get_rx_interrupts() -> impl InterruptAccess; - fn get_tx_interrupts() -> impl InterruptAccess; - - #[doc(hidden)] - fn set_isr(handler: InterruptHandler); + fn rx_interrupts() -> impl InterruptAccess; + fn tx_interrupts() -> impl InterruptAccess; } +#[diagnostic::on_unimplemented( + message = "The DMA channel isn't suitable for this peripheral", + label = "This DMA channel", + note = "Not all channels are useable with all peripherals" +)] #[doc(hidden)] pub trait DmaChannelConvert: DmaChannel { fn degrade_rx(rx: Self::Rx) -> DEG::Rx; @@ -1629,9 +1679,14 @@ impl<'a, CH> ChannelRx<'a, CH> where CH: DmaChannel, { - fn new(rx_impl: CH::Rx, burst_mode: bool) -> Self { + fn new(rx_impl: CH::Rx) -> Self { + #[cfg(gdma)] + // clear the mem2mem mode to avoid failed DMA if this + // channel was previously used for a mem2mem transfer. + rx_impl.set_mem2mem_mode(false); + Self { - burst_mode, + burst_mode: false, rx_impl, _phantom: PhantomData, } @@ -1649,11 +1704,17 @@ where _phantom: PhantomData, } } + + /// Configure the channel. + pub fn configure(&mut self, burst_mode: bool, priority: DmaPriority) { + self.burst_mode = burst_mode; + self.rx_impl.configure(burst_mode, priority); + } } -impl<'a, CH> crate::private::Sealed for ChannelRx<'a, CH> where CH: DmaChannel {} +impl crate::private::Sealed for ChannelRx<'_, CH> where CH: DmaChannel {} -impl<'a, CH> Rx for ChannelRx<'a, CH> +impl Rx for ChannelRx<'_, CH> where CH: DmaChannel, { @@ -1709,6 +1770,8 @@ where self.rx_impl .set_burst_mode(self.burst_mode && preparation.is_burstable); + self.rx_impl.set_check_owner(preparation.check_owner); + compiler_fence(core::sync::atomic::Ordering::SeqCst); self.rx_impl.clear_all(); @@ -1845,9 +1908,9 @@ impl<'a, CH> ChannelTx<'a, CH> where CH: DmaChannel, { - fn new(tx_impl: CH::Tx, burst_mode: bool) -> Self { + fn new(tx_impl: CH::Tx) -> Self { Self { - burst_mode, + burst_mode: false, tx_impl, _phantom: PhantomData, } @@ -1865,11 +1928,17 @@ where _phantom: PhantomData, } } + + /// Configure the channel. + pub fn configure(&mut self, burst_mode: bool, priority: DmaPriority) { + self.burst_mode = burst_mode; + self.tx_impl.configure(burst_mode, priority); + } } -impl<'a, CH> crate::private::Sealed for ChannelTx<'a, CH> where CH: DmaChannel {} +impl crate::private::Sealed for ChannelTx<'_, CH> where CH: DmaChannel {} -impl<'a, CH> Tx for ChannelTx<'a, CH> +impl Tx for ChannelTx<'_, CH> where CH: DmaChannel, { @@ -1902,6 +1971,10 @@ where self.tx_impl.set_link_addr(chain.first() as u32); self.tx_impl.set_peripheral(peri as u8); + // enable descriptor write back in circular mode + self.tx_impl + .set_auto_write_back(!(*chain.last()).next.is_null()); + Ok(()) } @@ -1927,6 +2000,8 @@ where self.tx_impl .set_burst_mode(self.burst_mode && preparation.is_burstable); + self.tx_impl.set_check_owner(preparation.check_owner); + compiler_fence(core::sync::atomic::Ordering::SeqCst); self.tx_impl.clear_all(); @@ -2020,11 +2095,21 @@ pub trait RegisterAccess: crate::private::Sealed { /// Mount a new descriptor. fn restart(&self); + /// Configure the bit to enable checking the owner attribute of the + /// descriptor. + fn set_check_owner(&self, check_owner: Option); + #[cfg(esp32s3)] fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize); #[cfg(pdma)] - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; + + /// Configure the channel. + fn configure(&self, burst_mode: bool, priority: DmaPriority) { + self.set_burst_mode(burst_mode); + self.set_priority(priority); + } } #[doc(hidden)] @@ -2035,6 +2120,9 @@ pub trait RxRegisterAccess: RegisterAccess { #[doc(hidden)] pub trait TxRegisterAccess: RegisterAccess { + /// Enable/disable outlink-writeback + fn set_auto_write_back(&self, enable: bool); + /// Outlink descriptor address when EOF occurs of Tx channel. fn last_dscr_address(&self) -> usize; } @@ -2060,36 +2148,43 @@ pub trait InterruptAccess: crate::private::Sealed { } /// DMA Channel -pub struct Channel<'d, CH, MODE> +pub struct Channel<'d, CH, M> where CH: DmaChannel, - MODE: Mode, + M: Mode, { /// RX half of the channel pub rx: ChannelRx<'d, CH>, /// TX half of the channel pub tx: ChannelTx<'d, CH>, - phantom: PhantomData, + pub(crate) phantom: PhantomData, } -impl<'d, C> Channel<'d, C, crate::Blocking> +impl<'d, C> Channel<'d, C, Blocking> where C: DmaChannel, { - /// Sets the interrupt handler for RX and TX interrupts, enables them - /// with [crate::interrupt::Priority::max()] + /// Sets the interrupt handler for RX and TX interrupts. /// /// Interrupts are not enabled at the peripheral level here. pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) where - C: DmaChannelExt, + C: DmaChannel, { - C::set_isr(handler); + self.unlisten(EnumSet::all()); + self.clear_interrupts(EnumSet::all()); + for interrupt in C::interrupts(self).iter().copied() { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); + } + unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(interrupt, handler.priority())); + } } /// Listen for the given interrupts - pub fn listen(&mut self, interrupts: EnumSet) { - for interrupt in interrupts { + pub fn listen(&mut self, interrupts: impl Into>) { + for interrupt in interrupts.into() { match interrupt { DmaInterrupt::RxDone => self.rx.listen_in(DmaRxInterrupt::Done), DmaInterrupt::TxDone => self.tx.listen_out(DmaTxInterrupt::Done), @@ -2098,8 +2193,8 @@ where } /// Unlisten the given interrupts - pub fn unlisten(&mut self, interrupts: EnumSet) { - for interrupt in interrupts { + pub fn unlisten(&mut self, interrupts: impl Into>) { + for interrupt in interrupts.into() { match interrupt { DmaInterrupt::RxDone => self.rx.unlisten_in(DmaRxInterrupt::Done), DmaInterrupt::TxDone => self.tx.unlisten_out(DmaTxInterrupt::Done), @@ -2120,14 +2215,61 @@ where } /// Resets asserted interrupts - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - for interrupt in interrupts { + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { + for interrupt in interrupts.into() { match interrupt { DmaInterrupt::RxDone => self.rx.clear_in(DmaRxInterrupt::Done), DmaInterrupt::TxDone => self.tx.clear_out(DmaTxInterrupt::Done), } } } + + /// Configure the channel. + pub fn configure(&mut self, burst_mode: bool, priority: DmaPriority) { + self.tx.configure(burst_mode, priority); + self.rx.configure(burst_mode, priority); + } + + /// Converts a blocking channel to an async channel. + pub fn into_async(mut self) -> Channel<'d, C, Async> { + self.set_interrupt_handler(C::async_handler(&self)); + + Channel { + tx: self.tx, + rx: self.rx, + phantom: PhantomData, + } + } +} + +impl<'d, C> Channel<'d, C, Async> +where + C: DmaChannel, +{ + /// Converts an async channel to a blocking channel. + pub fn into_blocking(self) -> Channel<'d, C, Blocking> { + for interrupt in C::interrupts(&self).iter().copied() { + crate::interrupt::disable(Cpu::current(), interrupt); + } + + Channel { + tx: self.tx, + rx: self.rx, + phantom: PhantomData, + } + } +} + +impl<'d, C: DmaChannel> From> for Channel<'d, C, Async> { + fn from(channel: Channel<'d, C, Blocking>) -> Self { + channel.into_async() + } +} + +impl<'d, C: DmaChannel> From> for Channel<'d, C, Blocking> { + fn from(channel: Channel<'d, C, Async>) -> Self { + channel.into_blocking() + } } impl<'d, CH, M: Mode> Channel<'d, CH, M> @@ -2231,7 +2373,7 @@ where } } -impl<'a, I> Drop for DmaTransferTx<'a, I> +impl Drop for DmaTransferTx<'_, I> where I: dma_private::DmaSupportTx, { @@ -2284,7 +2426,7 @@ where } } -impl<'a, I> Drop for DmaTransferRx<'a, I> +impl Drop for DmaTransferRx<'_, I> where I: dma_private::DmaSupportRx, { @@ -2343,7 +2485,7 @@ where } } -impl<'a, I> Drop for DmaTransferRxTx<'a, I> +impl Drop for DmaTransferRxTx<'_, I> where I: dma_private::DmaSupportTx + dma_private::DmaSupportRx, { @@ -2378,14 +2520,14 @@ where } /// Amount of bytes which can be pushed. - pub fn available(&mut self) -> usize { - self.state.update(self.instance.tx()); - self.state.available + pub fn available(&mut self) -> Result { + self.state.update(self.instance.tx())?; + Ok(self.state.available) } /// Push bytes into the DMA buffer. pub fn push(&mut self, data: &[u8]) -> Result { - self.state.update(self.instance.tx()); + self.state.update(self.instance.tx())?; self.state.push(data) } @@ -2394,7 +2536,7 @@ where /// The closure *might* get called with a slice which is smaller than the /// total available buffer. pub fn push_with(&mut self, f: impl FnOnce(&mut [u8]) -> usize) -> Result { - self.state.update(self.instance.tx()); + self.state.update(self.instance.tx())?; self.state.push_with(f) } @@ -2416,7 +2558,7 @@ where } } -impl<'a, I> Drop for DmaTransferTxCircular<'a, I> +impl Drop for DmaTransferTxCircular<'_, I> where I: dma_private::DmaSupportTx, { @@ -2454,9 +2596,9 @@ where /// /// It's expected to call this before trying to [DmaTransferRxCircular::pop] /// data. - pub fn available(&mut self) -> usize { - self.state.update(); - self.state.available + pub fn available(&mut self) -> Result { + self.state.update()?; + Ok(self.state.available) } /// Get available data. @@ -2468,12 +2610,12 @@ where /// Fails with [DmaError::BufferTooSmall] if the given buffer is too small /// to hold all available data pub fn pop(&mut self, data: &mut [u8]) -> Result { - self.state.update(); + self.state.update()?; self.state.pop(data) } } -impl<'a, I> Drop for DmaTransferRxCircular<'a, I> +impl Drop for DmaTransferRxCircular<'_, I> where I: dma_private::DmaSupportRx, { @@ -2504,7 +2646,7 @@ pub(crate) mod asynch { } } - impl<'a, TX> core::future::Future for DmaTxFuture<'a, TX> + impl core::future::Future for DmaTxFuture<'_, TX> where TX: Tx, { @@ -2533,7 +2675,7 @@ pub(crate) mod asynch { } } - impl<'a, TX> Drop for DmaTxFuture<'a, TX> + impl Drop for DmaTxFuture<'_, TX> where TX: Tx, { @@ -2560,7 +2702,7 @@ pub(crate) mod asynch { } } - impl<'a, RX> core::future::Future for DmaRxFuture<'a, RX> + impl core::future::Future for DmaRxFuture<'_, RX> where RX: Rx, { @@ -2593,7 +2735,7 @@ pub(crate) mod asynch { } } - impl<'a, RX> Drop for DmaRxFuture<'a, RX> + impl Drop for DmaRxFuture<'_, RX> where RX: Rx, { @@ -2626,7 +2768,7 @@ pub(crate) mod asynch { } #[cfg(any(i2s0, i2s1))] - impl<'a, TX> core::future::Future for DmaTxDoneChFuture<'a, TX> + impl core::future::Future for DmaTxDoneChFuture<'_, TX> where TX: Tx, { @@ -2660,7 +2802,7 @@ pub(crate) mod asynch { } #[cfg(any(i2s0, i2s1))] - impl<'a, TX> Drop for DmaTxDoneChFuture<'a, TX> + impl Drop for DmaTxDoneChFuture<'_, TX> where TX: Tx, { @@ -2690,7 +2832,7 @@ pub(crate) mod asynch { } #[cfg(any(i2s0, i2s1))] - impl<'a, RX> core::future::Future for DmaRxDoneChFuture<'a, RX> + impl core::future::Future for DmaRxDoneChFuture<'_, RX> where RX: Rx, { @@ -2728,7 +2870,7 @@ pub(crate) mod asynch { } #[cfg(any(i2s0, i2s1))] - impl<'a, RX> Drop for DmaRxDoneChFuture<'a, RX> + impl Drop for DmaRxDoneChFuture<'_, RX> where RX: Rx, { @@ -2743,8 +2885,8 @@ pub(crate) mod asynch { } fn handle_interrupt() { - let rx = CH::get_rx_interrupts(); - let tx = CH::get_tx_interrupts(); + let rx = CH::rx_interrupts(); + let tx = CH::tx_interrupts(); if rx.pending_interrupts().is_disjoint( DmaRxInterrupt::DescriptorError @@ -2852,13 +2994,13 @@ pub(crate) mod asynch { #[cfg(i2s0)] #[handler(priority = crate::interrupt::Priority::max())] - pub(crate) fn interrupt_handler_i2s0() { + pub(crate) fn interrupt_handler_i2s0_dma() { handle_interrupt::(); } #[cfg(i2s1)] #[handler(priority = crate::interrupt::Priority::max())] - pub(crate) fn interrupt_handler_i2s1() { + pub(crate) fn interrupt_handler_i2s1_dma() { handle_interrupt::(); } } diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index de853696aee..bfbaee48dfc 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -16,7 +16,9 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::{ dma::*, peripheral::PeripheralRef, + peripherals::Interrupt, system::{Peripheral, PeripheralClockControl}, + Blocking, }; type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock; @@ -29,7 +31,7 @@ pub trait PdmaChannel: crate::private::Sealed { fn register_block(&self) -> &Self::RegisterBlock; fn tx_waker(&self) -> &'static AtomicWaker; fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; } #[doc(hidden)] @@ -84,12 +86,23 @@ impl> RegisterAccess for SpiDma .modify(|_, w| w.outlink_restart().set_bit()); } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("SPI DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { self.0.is_compatible_with(peripheral) } } impl> TxRegisterAccess for SpiDmaTxChannelImpl { + fn set_auto_write_back(&self, enable: bool) { + // there is no `auto_wrback` for SPI + assert!(!enable); + } + fn last_dscr_address(&self) -> usize { let spi = self.0.register_block(); spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize @@ -111,7 +124,7 @@ impl> InterruptAccess EnumSet { @@ -147,7 +160,7 @@ impl> InterruptAccess EnumSet { @@ -217,7 +230,13 @@ impl> RegisterAccess for SpiDma .modify(|_, w| w.inlink_restart().set_bit()); } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("SPI DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { self.0.is_compatible_with(peripheral) } } @@ -240,7 +259,7 @@ impl> InterruptAccess EnumSet { @@ -280,7 +299,7 @@ impl> InterruptAccess EnumSet { @@ -324,26 +343,36 @@ macro_rules! ImplSpiChannel { #[non_exhaustive] pub struct [] {} + impl [] { + fn handler() -> InterruptHandler { + super::asynch::interrupt::[< interrupt_handler_spi $num _dma >] + } + + fn isrs() -> &'static [Interrupt] { + &[Interrupt::[< SPI $num _DMA >]] + } + } + impl DmaChannel for [] { type Rx = SpiDmaRxChannelImpl; type Tx = SpiDmaTxChannelImpl; + + fn async_handler(_ch: &Channel<'_, Self, M>) -> InterruptHandler { + Self::handler() + } + + fn interrupts(_ch: &Channel<'_, Self, M>) -> &'static [Interrupt] { + Self::isrs() + } } impl DmaChannelExt for [] { - fn get_rx_interrupts() -> impl InterruptAccess { + fn rx_interrupts() -> impl InterruptAccess { SpiDmaRxChannelImpl(Self {}) } - fn get_tx_interrupts() -> impl InterruptAccess { + fn tx_interrupts() -> impl InterruptAccess { SpiDmaTxChannelImpl(Self {}) } - - fn set_isr(handler: InterruptHandler) { - let interrupt = $crate::peripherals::Interrupt::[< SPI $num _DMA >]; - unsafe { - crate::interrupt::bind_interrupt(interrupt, handler.handler()); - } - crate::interrupt::enable(interrupt, handler.priority()).unwrap(); - } } impl PdmaChannel for [] { @@ -361,8 +390,8 @@ macro_rules! ImplSpiChannel { &WAKER } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { - peripheral.peripheral() == crate::system::Peripheral::[] + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + peripheral == DmaPeripheral::[] } } @@ -382,59 +411,19 @@ macro_rules! ImplSpiChannel { pub struct [] {} impl [] { - fn do_configure<'a, M: $crate::Mode>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> Channel<'a, [], M> { - #[cfg(esp32)] - { - // (only) on ESP32 we need to configure DPORT for the SPI DMA channels - let dport = unsafe { &*crate::peripherals::DPORT::PTR }; - dport - .spi_dma_chan_sel() - .modify(|_, w| unsafe { w.[< spi $num _dma_chan_sel>]().bits($num - 1) }); - } - - let tx_impl = SpiDmaTxChannelImpl([] {}); - tx_impl.set_burst_mode(burst_mode); - tx_impl.set_priority(priority); - - let rx_impl = SpiDmaRxChannelImpl([] {}); - rx_impl.set_burst_mode(burst_mode); - rx_impl.set_priority(priority); - - Channel { - tx: ChannelTx::new(tx_impl, burst_mode), - rx: ChannelRx::new(rx_impl, burst_mode), - phantom: PhantomData, - } - } - /// Configure the channel for use with blocking APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. pub fn configure<'a>( self, burst_mode: bool, priority: DmaPriority, - ) -> Channel<'a, [], $crate::Blocking> { - Self::do_configure(self, burst_mode, priority) - } - - /// Configure the channel for use with async APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. - pub fn configure_for_async<'a>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> Channel<'a, [], $crate::Async> { - let this = Self::do_configure(self, burst_mode, priority); + ) -> Channel<'a, [], Blocking> { + let mut this = Channel { + tx: ChannelTx::new(SpiDmaTxChannelImpl([] {})), + rx: ChannelRx::new(SpiDmaRxChannelImpl([] {})), + phantom: PhantomData, + }; - []::set_isr(super::asynch::interrupt::[< interrupt_handler_spi $num _dma >]); + this.configure(burst_mode, priority); this } @@ -501,12 +490,26 @@ impl> RegisterAccess for I2sDma .modify(|_, w| w.outlink_restart().set_bit()); } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { + fn set_check_owner(&self, check_owner: Option) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { self.0.is_compatible_with(peripheral) } } impl> TxRegisterAccess for I2sDmaTxChannelImpl { + fn set_auto_write_back(&self, enable: bool) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.out_auto_wrback().bit(enable)); + } + fn last_dscr_address(&self) -> usize { let reg_block = self.0.register_block(); reg_block @@ -532,7 +535,7 @@ impl> InterruptAccess EnumSet { @@ -589,7 +592,7 @@ impl> InterruptAccess &'static AtomicWaker { @@ -643,7 +646,14 @@ impl> RegisterAccess for I2sDma .modify(|_, w| w.inlink_restart().set_bit()); } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { + fn set_check_owner(&self, check_owner: Option) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { self.0.is_compatible_with(peripheral) } } @@ -666,7 +676,7 @@ impl> InterruptAccess EnumSet { @@ -730,7 +740,7 @@ impl> InterruptAccess &'static AtomicWaker { @@ -746,26 +756,36 @@ macro_rules! ImplI2sChannel { impl $crate::private::Sealed for [] {} + impl [] { + fn handler() -> InterruptHandler { + super::asynch::interrupt::[< interrupt_handler_i2s $num _dma >] + } + + fn isrs() -> &'static [Interrupt] { + &[Interrupt::[< I2S $num >]] + } + } + impl DmaChannel for [] { type Rx = I2sDmaRxChannelImpl; type Tx = I2sDmaTxChannelImpl; + + fn async_handler(_ch: &Channel<'_, Self, M>) -> InterruptHandler { + Self::handler() + } + + fn interrupts(_ch: &Channel<'_, Self, M>) -> &'static [Interrupt] { + Self::isrs() + } } impl DmaChannelExt for [] { - fn get_rx_interrupts() -> impl InterruptAccess { + fn rx_interrupts() -> impl InterruptAccess { I2sDmaRxChannelImpl(Self {}) } - fn get_tx_interrupts() -> impl InterruptAccess { + fn tx_interrupts() -> impl InterruptAccess { I2sDmaTxChannelImpl(Self {}) } - - fn set_isr(handler: InterruptHandler) { - let interrupt = $crate::peripherals::Interrupt::[< I2S $num >]; - unsafe { - crate::interrupt::bind_interrupt(interrupt, handler.handler()); - } - crate::interrupt::enable(interrupt, handler.priority()).unwrap(); - } } impl PdmaChannel for [] { @@ -782,8 +802,8 @@ macro_rules! ImplI2sChannel { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool { - peripheral.peripheral() == crate::system::Peripheral::[] + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + peripheral == DmaPeripheral::[] } } @@ -800,50 +820,19 @@ macro_rules! ImplI2sChannel { pub struct [] {} impl [] { - fn do_configure<'a, M: $crate::Mode>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> Channel<'a, [], M> { - let tx_impl = I2sDmaTxChannelImpl([] {}); - tx_impl.set_burst_mode(burst_mode); - tx_impl.set_priority(priority); - - let rx_impl = I2sDmaRxChannelImpl([] {}); - rx_impl.set_burst_mode(burst_mode); - rx_impl.set_priority(priority); - - Channel { - tx: ChannelTx::new(tx_impl, burst_mode), - rx: ChannelRx::new(rx_impl, burst_mode), - phantom: PhantomData, - } - } - /// Configure the channel for use with blocking APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. pub fn configure<'a>( self, burst_mode: bool, priority: DmaPriority, - ) -> Channel<'a, [], $crate::Blocking> { - Self::do_configure(self, burst_mode, priority) - } - - /// Configure the channel for use with async APIs - /// - /// Descriptors should be sized as `(CHUNK_SIZE + 4091) / 4092`. I.e., to - /// transfer buffers of size `1..=4092`, you need 1 descriptor. - pub fn configure_for_async<'a>( - self, - burst_mode: bool, - priority: DmaPriority, - ) -> Channel<'a, [], $crate::Async> { - let this = Self::do_configure(self, burst_mode, priority); + ) -> Channel<'a, [], Blocking> { + let mut this = Channel { + tx: ChannelTx::new(I2sDmaTxChannelImpl([] {})), + rx: ChannelRx::new(I2sDmaRxChannelImpl([] {})), + phantom: PhantomData, + }; - []::set_isr(super::asynch::interrupt::[< interrupt_handler_i2s $num >]); + this.configure(burst_mode, priority); this } @@ -891,6 +880,19 @@ impl<'d> Dma<'d> { ) -> Dma<'d> { PeripheralClockControl::enable(Peripheral::Dma); + #[cfg(esp32)] + { + // (only) on ESP32 we need to configure DPORT for the SPI DMA channels + // This assignes the DMA channels to the SPI peripherals, which is more + // restrictive than necessary but we currently support the same + // number of SPI peripherals as SPI DMA channels so it's not a big + // deal. + let dport = unsafe { &*crate::peripherals::DPORT::PTR }; + dport.spi_dma_chan_sel().modify(|_, w| unsafe { + w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) + }); + } + Dma { _inner: dma.into_ref(), spi2channel: Spi2DmaChannelCreator {}, @@ -907,11 +909,13 @@ where C: DmaChannel, { /// Asserts that the channel is compatible with the given peripheral. - pub fn runtime_ensure_compatible(&self, peripheral: &PeripheralRef<'_, impl PeripheralMarker>) { + pub fn runtime_ensure_compatible(&self, peripheral: &PeripheralRef<'_, impl DmaEligible>) { assert!( - self.tx.tx_impl.is_compatible_with(&**peripheral), + self.tx + .tx_impl + .is_compatible_with(peripheral.dma_peripheral()), "This DMA channel is not compatible with {:?}", - peripheral.peripheral() + peripheral.dma_peripheral() ); } } @@ -924,6 +928,20 @@ impl crate::private::Sealed for AnySpiDmaChannel {} impl DmaChannel for AnySpiDmaChannel { type Rx = SpiDmaRxChannelImpl; type Tx = SpiDmaTxChannelImpl; + + fn async_handler(ch: &Channel<'_, Self, M>) -> InterruptHandler { + match &ch.tx.tx_impl.0 { + AnySpiDmaChannelInner::Spi2(_) => Spi2DmaChannel::handler(), + AnySpiDmaChannelInner::Spi3(_) => Spi3DmaChannel::handler(), + } + } + + fn interrupts(ch: &Channel<'_, Self, M>) -> &'static [Interrupt] { + match &ch.tx.tx_impl.0 { + AnySpiDmaChannelInner::Spi2(_) => Spi2DmaChannel::isrs(), + AnySpiDmaChannelInner::Spi3(_) => Spi3DmaChannel::isrs(), + } + } } crate::any_enum! { @@ -947,7 +965,7 @@ impl PdmaChannel for AnySpiDmaChannelInner { fn register_block(&self) -> &SpiRegisterBlock; fn tx_waker(&self) -> &'static AtomicWaker; fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; } } } @@ -960,6 +978,22 @@ impl crate::private::Sealed for AnyI2sDmaChannel {} impl DmaChannel for AnyI2sDmaChannel { type Rx = I2sDmaRxChannelImpl; type Tx = I2sDmaTxChannelImpl; + + fn async_handler(ch: &Channel<'_, Self, M>) -> InterruptHandler { + match &ch.tx.tx_impl.0 { + AnyI2sDmaChannelInner::I2s0(_) => I2s0DmaChannel::handler(), + #[cfg(i2s1)] + AnyI2sDmaChannelInner::I2s1(_) => I2s1DmaChannel::handler(), + } + } + + fn interrupts(ch: &Channel<'_, Self, M>) -> &'static [Interrupt] { + match &ch.tx.tx_impl.0 { + AnyI2sDmaChannelInner::I2s0(_) => I2s0DmaChannel::isrs(), + #[cfg(i2s1)] + AnyI2sDmaChannelInner::I2s1(_) => I2s1DmaChannel::isrs(), + } + } } crate::any_enum! { @@ -985,7 +1019,7 @@ impl PdmaChannel for AnyI2sDmaChannelInner { fn register_block(&self) -> &I2sRegisterBlock; fn tx_waker(&self) -> &'static AtomicWaker; fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: &impl PeripheralMarker) -> bool; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; } } } diff --git a/esp-hal/src/ecc.rs b/esp-hal/src/ecc.rs index 10d0a17fac5..7c6ccfcc2b9 100644 --- a/esp-hal/src/ecc.rs +++ b/esp-hal/src/ecc.rs @@ -30,7 +30,7 @@ use core::marker::PhantomData; use crate::{ interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, - peripherals::ECC, + peripherals::{Interrupt, ECC}, reg_access::{AlignmentHelper, SocDependentEndianess}, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, InterruptConfigurable, @@ -113,19 +113,19 @@ impl<'d> Ecc<'d, crate::Blocking> { } } -impl<'d> crate::private::Sealed for Ecc<'d, crate::Blocking> {} +impl crate::private::Sealed for Ecc<'_, crate::Blocking> {} -impl<'d> InterruptConfigurable for Ecc<'d, crate::Blocking> { +impl InterruptConfigurable for Ecc<'_, crate::Blocking> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::ECC, handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::ECC, handler.priority()) - .unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::ECC); } + unsafe { crate::interrupt::bind_interrupt(Interrupt::ECC, handler.handler()) }; + unwrap!(crate::interrupt::enable(Interrupt::ECC, handler.priority())); } } -impl<'d, DM: crate::Mode> Ecc<'d, DM> { +impl Ecc<'_, DM> { /// Resets the ECC peripheral. pub fn reset(&mut self) { self.ecc.mult_conf().reset() diff --git a/esp-hal/src/etm.rs b/esp-hal/src/etm.rs index 1e8322c1852..5276eadcccf 100644 --- a/esp-hal/src/etm.rs +++ b/esp-hal/src/etm.rs @@ -23,31 +23,27 @@ //! ## Examples //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::gpio::Io; -//! # use esp_hal::gpio::etm::GpioEtmChannels; +//! # use esp_hal::gpio::etm::{Channels, InputConfig, OutputConfig}; //! # use esp_hal::etm::Etm; -//! # use esp_hal::gpio::etm::GpioEtmInputConfig; -//! # use esp_hal::gpio::etm::GpioEtmOutputConfig; //! # use esp_hal::gpio::Pull; //! # use esp_hal::gpio::Level; //! -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let mut led = io.pins.gpio1; -//! let button = io.pins.gpio9; +//! let mut led = peripherals.GPIO1; +//! let button = peripherals.GPIO9; //! //! // setup ETM -//! let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD); +//! let gpio_ext = Channels::new(peripherals.GPIO_SD); //! let led_task = gpio_ext.channel0_task.toggle( -//! &mut led, -//! GpioEtmOutputConfig { -//! open_drain: false, -//! pull: Pull::None, -//! initial_state: Level::Low, -//! }, +//! &mut led, +//! OutputConfig { +//! open_drain: false, +//! pull: Pull::None, +//! initial_state: Level::Low, +//! }, //! ); //! let button_event = gpio_ext //! .channel0_event -//! .falling_edge(button, GpioEtmInputConfig { pull: Pull::Down }); +//! .falling_edge(button, InputConfig { pull: Pull::Down }); //! //! let etm = Etm::new(peripherals.SOC_ETM); //! let channel0 = etm.channel0; @@ -123,7 +119,7 @@ where _task: &'a T, } -impl<'a, E, T, const C: u8> Drop for EtmConfiguredChannel<'a, E, T, C> +impl Drop for EtmConfiguredChannel<'_, E, T, C> where E: EtmEvent, T: EtmTask, diff --git a/esp-hal/src/gpio/etm.rs b/esp-hal/src/gpio/etm.rs index bc5958256a7..4ff74e67804 100644 --- a/esp-hal/src/gpio/etm.rs +++ b/esp-hal/src/gpio/etm.rs @@ -25,22 +25,20 @@ //! ### Toggle an LED When a Button is Pressed //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::gpio::Io; -//! # use esp_hal::gpio::etm::GpioEtmChannels; +//! # use esp_hal::gpio::etm::Channels; //! # use esp_hal::etm::Etm; -//! # use esp_hal::gpio::etm::GpioEtmInputConfig; -//! # use esp_hal::gpio::etm::GpioEtmOutputConfig; +//! # use esp_hal::gpio::etm::InputConfig; +//! # use esp_hal::gpio::etm::OutputConfig; //! # use esp_hal::gpio::Pull; //! # use esp_hal::gpio::Level; +//! # +//! # let mut led = peripherals.GPIO1; +//! # let button = peripherals.GPIO9; //! -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut led = io.pins.gpio1; -//! # let button = io.pins.gpio9; -//! -//! let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD); +//! let gpio_ext = Channels::new(peripherals.GPIO_SD); //! let led_task = gpio_ext.channel0_task.toggle( //! &mut led, -//! GpioEtmOutputConfig { +//! OutputConfig { //! open_drain: false, //! pull: Pull::None, //! initial_state: Level::Low, @@ -48,77 +46,84 @@ //! ); //! let button_event = gpio_ext //! .channel0_event -//! .falling_edge(button, GpioEtmInputConfig { pull: Pull::Down }); +//! .falling_edge(button, InputConfig { pull: Pull::Down }); //! # } //! ``` +use core::marker::PhantomData; + use crate::{ - gpio::{Level, Pull}, + gpio::{ + interconnect::{InputSignal, OutputSignal}, + Level, + Pull, + }, peripheral::{Peripheral, PeripheralRef}, + peripherals::GPIO_SD, private, }; /// All the GPIO ETM channels #[non_exhaustive] -pub struct GpioEtmChannels<'d> { - _gpio_sd: PeripheralRef<'d, crate::peripherals::GPIO_SD>, +pub struct Channels<'d> { + _gpio_sd: PeripheralRef<'d, GPIO_SD>, /// Task channel 0 for triggering GPIO tasks. - pub channel0_task: GpioEtmTaskChannel<0>, + pub channel0_task: TaskChannel<0>, /// Event channel 0 for handling GPIO events. - pub channel0_event: GpioEtmEventChannel<0>, + pub channel0_event: EventChannel<0>, /// Task channel 1 for triggering GPIO tasks. - pub channel1_task: GpioEtmTaskChannel<1>, + pub channel1_task: TaskChannel<1>, /// Event channel 1 for handling GPIO events. - pub channel1_event: GpioEtmEventChannel<1>, + pub channel1_event: EventChannel<1>, /// Task channel 2 for triggering GPIO tasks. - pub channel2_task: GpioEtmTaskChannel<2>, + pub channel2_task: TaskChannel<2>, /// Event channel 2 for handling GPIO events. - pub channel2_event: GpioEtmEventChannel<2>, + pub channel2_event: EventChannel<2>, /// Task channel 3 for triggering GPIO tasks. - pub channel3_task: GpioEtmTaskChannel<3>, + pub channel3_task: TaskChannel<3>, /// Event channel 3 for handling GPIO events. - pub channel3_event: GpioEtmEventChannel<3>, + pub channel3_event: EventChannel<3>, /// Task channel 4 for triggering GPIO tasks. - pub channel4_task: GpioEtmTaskChannel<4>, + pub channel4_task: TaskChannel<4>, /// Event channel 4 for handling GPIO events. - pub channel4_event: GpioEtmEventChannel<4>, + pub channel4_event: EventChannel<4>, /// Task channel 5 for triggering GPIO tasks. - pub channel5_task: GpioEtmTaskChannel<5>, + pub channel5_task: TaskChannel<5>, /// Event channel 5 for handling GPIO events. - pub channel5_event: GpioEtmEventChannel<5>, + pub channel5_event: EventChannel<5>, /// Task channel 6 for triggering GPIO tasks. - pub channel6_task: GpioEtmTaskChannel<6>, + pub channel6_task: TaskChannel<6>, /// Event channel 6 for handling GPIO events. - pub channel6_event: GpioEtmEventChannel<6>, + pub channel6_event: EventChannel<6>, /// Task channel 7 for triggering GPIO tasks. - pub channel7_task: GpioEtmTaskChannel<7>, + pub channel7_task: TaskChannel<7>, /// Event channel 7 for handling GPIO events. - pub channel7_event: GpioEtmEventChannel<7>, + pub channel7_event: EventChannel<7>, } -impl<'d> GpioEtmChannels<'d> { +impl<'d> Channels<'d> { /// Create a new instance - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { crate::into_ref!(peripheral); Self { _gpio_sd: peripheral, - channel0_task: GpioEtmTaskChannel {}, - channel0_event: GpioEtmEventChannel {}, - channel1_task: GpioEtmTaskChannel {}, - channel1_event: GpioEtmEventChannel {}, - channel2_task: GpioEtmTaskChannel {}, - channel2_event: GpioEtmEventChannel {}, - channel3_task: GpioEtmTaskChannel {}, - channel3_event: GpioEtmEventChannel {}, - channel4_task: GpioEtmTaskChannel {}, - channel4_event: GpioEtmEventChannel {}, - channel5_task: GpioEtmTaskChannel {}, - channel5_event: GpioEtmEventChannel {}, - channel6_task: GpioEtmTaskChannel {}, - channel6_event: GpioEtmEventChannel {}, - channel7_task: GpioEtmTaskChannel {}, - channel7_event: GpioEtmEventChannel {}, + channel0_task: TaskChannel {}, + channel0_event: EventChannel {}, + channel1_task: TaskChannel {}, + channel1_event: EventChannel {}, + channel2_task: TaskChannel {}, + channel2_event: EventChannel {}, + channel3_task: TaskChannel {}, + channel3_event: EventChannel {}, + channel4_task: TaskChannel {}, + channel4_event: EventChannel {}, + channel5_task: TaskChannel {}, + channel5_event: EventChannel {}, + channel6_task: TaskChannel {}, + channel6_event: EventChannel {}, + channel7_task: TaskChannel {}, + channel7_event: EventChannel {}, } } } @@ -126,146 +131,102 @@ impl<'d> GpioEtmChannels<'d> { /// Configuration for an ETM controlled GPIO input pin #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct GpioEtmInputConfig { +pub struct InputConfig { /// Configuration for the internal pull-up resistors pub pull: Pull, } -impl Default for GpioEtmInputConfig { +impl Default for InputConfig { fn default() -> Self { Self { pull: Pull::None } } } /// An ETM controlled GPIO event -pub struct GpioEtmEventChannel {} +pub struct EventChannel {} -impl GpioEtmEventChannel { +impl EventChannel { /// Trigger at rising edge - pub fn rising_edge<'d, PIN>( + pub fn rising_edge<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmInputConfig, - ) -> GpioEtmEventChannelRising<'d, PIN, C> - where - PIN: super::InputPin, - { - crate::into_ref!(pin); - - pin.init_input(pin_config.pull, private::Internal); - - enable_event_channel(C, pin.number()); - GpioEtmEventChannelRising { _pin: pin } + pin: impl Peripheral

> + 'd, + pin_config: InputConfig, + ) -> Event<'d> { + self.into_event(pin, pin_config, EventKind::Rising) } /// Trigger at falling edge - pub fn falling_edge<'d, PIN>( + pub fn falling_edge<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmInputConfig, - ) -> GpioEtmEventChannelFalling<'d, PIN, C> - where - PIN: super::InputPin, - { - crate::into_ref!(pin); - - pin.init_input(pin_config.pull, private::Internal); - - enable_event_channel(C, pin.number()); - GpioEtmEventChannelFalling { _pin: pin } + pin: impl Peripheral

> + 'd, + pin_config: InputConfig, + ) -> Event<'d> { + self.into_event(pin, pin_config, EventKind::Falling) } /// Trigger at any edge - pub fn any_edge<'d, PIN>( + pub fn any_edge<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmInputConfig, - ) -> GpioEtmEventChannelAny<'d, PIN, C> - where - PIN: super::InputPin, - { - crate::into_ref!(pin); - - pin.init_input(pin_config.pull, private::Internal); - - enable_event_channel(C, pin.number()); - GpioEtmEventChannelAny { _pin: pin } + pin: impl Peripheral

> + 'd, + pin_config: InputConfig, + ) -> Event<'d> { + self.into_event(pin, pin_config, EventKind::Any) } -} -/// Event for rising edge -#[non_exhaustive] -pub struct GpioEtmEventChannelRising<'d, PIN, const C: u8> -where - PIN: super::Pin, -{ - _pin: PeripheralRef<'d, PIN>, -} + fn into_event<'d>( + self, + pin: impl Peripheral

> + 'd, + pin_config: InputConfig, + kind: EventKind, + ) -> Event<'d> { + crate::into_mapped_ref!(pin); -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmEventChannelRising<'d, PIN, C> where - PIN: super::Pin -{ -} + pin.init_input(pin_config.pull, private::Internal); -impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelRising<'d, PIN, C> -where - PIN: super::Pin, -{ - fn id(&self) -> u8 { - 1 + C + enable_event_channel(C, pin.number()); + Event { + id: kind.id() + C, + _pin: PhantomData, + } } } -/// Event for falling edge -#[non_exhaustive] -pub struct GpioEtmEventChannelFalling<'d, PIN, const C: u8> -where - PIN: super::Pin, -{ - _pin: PeripheralRef<'d, PIN>, -} - -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmEventChannelFalling<'d, PIN, C> where - PIN: super::Pin -{ +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum EventKind { + Rising, + Falling, + Any, } -impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelFalling<'d, PIN, C> -where - PIN: super::Pin, -{ +impl EventKind { fn id(&self) -> u8 { - 9 + C + match self { + EventKind::Rising => 1, + EventKind::Falling => 9, + EventKind::Any => 17, + } } } -/// Event for any edge -#[non_exhaustive] -pub struct GpioEtmEventChannelAny<'d, PIN, const C: u8> -where - PIN: super::Pin, -{ - _pin: PeripheralRef<'d, PIN>, +/// Event for rising edge +pub struct Event<'d> { + _pin: PhantomData<&'d mut ()>, + id: u8, } -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmEventChannelAny<'d, PIN, C> where - PIN: super::Pin -{ -} +impl private::Sealed for Event<'_> {} -impl<'d, PIN, const C: u8> crate::etm::EtmEvent for GpioEtmEventChannelAny<'d, PIN, C> -where - PIN: super::Pin, -{ +impl crate::etm::EtmEvent for Event<'_> { fn id(&self) -> u8 { - 17 + C + self.id } } /// Configuration for an ETM controlled GPIO output pin #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct GpioEtmOutputConfig { +pub struct OutputConfig { /// Set to open-drain output pub open_drain: bool, /// Only used when open-drain @@ -274,7 +235,7 @@ pub struct GpioEtmOutputConfig { pub initial_state: Level, } -impl Default for GpioEtmOutputConfig { +impl Default for OutputConfig { fn default() -> Self { Self { open_drain: false, @@ -285,70 +246,48 @@ impl Default for GpioEtmOutputConfig { } /// An ETM controlled GPIO task -pub struct GpioEtmTaskChannel {} +pub struct TaskChannel {} -impl GpioEtmTaskChannel { +impl TaskChannel { // In theory we could have multiple pins assigned to the same task. Not sure how // useful that would be. If we want to support it, the easiest would be // to offer additional functions like `set2`, `set3` etc. where the // number is the pin-count /// Task to set a high level - pub fn set<'d, PIN>( + pub fn set<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmOutputConfig, - ) -> GpioEtmTaskSet<'d, PIN, C> - where - PIN: super::OutputPin, - { - crate::into_ref!(pin); - - pin.set_output_high(pin_config.initial_state.into(), private::Internal); - if pin_config.open_drain { - pin.pull_direction(pin_config.pull, private::Internal); - pin.set_to_open_drain_output(private::Internal); - } else { - pin.set_to_push_pull_output(private::Internal); - } - - enable_task_channel(C, pin.number()); - GpioEtmTaskSet { _pin: pin } + pin: impl Peripheral

> + 'd, + pin_config: OutputConfig, + ) -> Task<'d> { + self.into_task(pin, pin_config, TaskKind::Set) } /// Task to set a low level - pub fn clear<'d, PIN>( + pub fn clear<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmOutputConfig, - ) -> GpioEtmTaskClear<'d, PIN, C> - where - PIN: super::OutputPin, - { - crate::into_ref!(pin); - - pin.set_output_high(pin_config.initial_state.into(), private::Internal); - if pin_config.open_drain { - pin.pull_direction(pin_config.pull, private::Internal); - pin.set_to_open_drain_output(private::Internal); - } else { - pin.set_to_push_pull_output(private::Internal); - } - - enable_task_channel(C, pin.number()); - GpioEtmTaskClear { _pin: pin } + pin: impl Peripheral

> + 'd, + pin_config: OutputConfig, + ) -> Task<'d> { + self.into_task(pin, pin_config, TaskKind::Clear) } /// Task to toggle the level - pub fn toggle<'d, PIN>( + pub fn toggle<'d>( + self, + pin: impl Peripheral

> + 'd, + pin_config: OutputConfig, + ) -> Task<'d> { + self.into_task(pin, pin_config, TaskKind::Toggle) + } + + fn into_task<'d>( self, - pin: impl Peripheral

+ 'd, - pin_config: GpioEtmOutputConfig, - ) -> GpioEtmTaskToggle<'d, PIN, C> - where - PIN: super::OutputPin, - { - crate::into_ref!(pin); + pin: impl Peripheral

> + 'd, + pin_config: OutputConfig, + kind: TaskKind, + ) -> Task<'d> { + crate::into_mapped_ref!(pin); pin.set_output_high(pin_config.initial_state.into(), private::Internal); if pin_config.open_drain { @@ -359,66 +298,47 @@ impl GpioEtmTaskChannel { } enable_task_channel(C, pin.number()); - GpioEtmTaskToggle { _pin: pin } - } -} - -/// Task for set operation -#[non_exhaustive] -pub struct GpioEtmTaskSet<'d, PIN, const C: u8> -where - PIN: super::Pin, -{ - _pin: PeripheralRef<'d, PIN>, -} - -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmTaskSet<'d, PIN, C> where PIN: super::Pin {} - -impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskSet<'d, PIN, C> -where - PIN: super::Pin, -{ - fn id(&self) -> u8 { - 1 + C + Task { + id: kind.id() + C, + _pin: PhantomData, + } } } -/// Task for clear operation -#[non_exhaustive] -pub struct GpioEtmTaskClear<'d, PIN, const C: u8> { - _pin: PeripheralRef<'d, PIN>, +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +enum TaskKind { + Set, + Clear, + Toggle, } -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmTaskClear<'d, PIN, C> where PIN: super::Pin {} - -impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskClear<'d, PIN, C> -where - PIN: super::Pin, -{ +impl TaskKind { fn id(&self) -> u8 { - 9 + C + match self { + TaskKind::Set => 1, + TaskKind::Clear => 9, + TaskKind::Toggle => 17, + } } } -/// Task for toggle operation -#[non_exhaustive] -pub struct GpioEtmTaskToggle<'d, PIN, const C: u8> { - _pin: PeripheralRef<'d, PIN>, +/// Task for set operation +pub struct Task<'d> { + _pin: PhantomData<&'d mut ()>, + id: u8, } -impl<'d, PIN, const C: u8> private::Sealed for GpioEtmTaskToggle<'d, PIN, C> where PIN: super::Pin {} +impl private::Sealed for Task<'_> {} -impl<'d, PIN, const C: u8> crate::etm::EtmTask for GpioEtmTaskToggle<'d, PIN, C> -where - PIN: super::Pin, -{ +impl crate::etm::EtmTask for Task<'_> { fn id(&self) -> u8 { - 17 + C + self.id } } fn enable_task_channel(channel: u8, pin: u8) { - let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() }; + let gpio_sd = unsafe { GPIO_SD::steal() }; let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) }; let shift = 8 * (pin as usize % 4); // bit 0 = en, bit 1-3 = channel @@ -430,7 +350,7 @@ fn enable_task_channel(channel: u8, pin: u8) { } fn enable_event_channel(channel: u8, pin: u8) { - let gpio_sd = unsafe { crate::peripherals::GPIO_SD::steal() }; + let gpio_sd = unsafe { GPIO_SD::steal() }; gpio_sd .etm_event_ch_cfg(channel as usize) .modify(|_, w| w.event_en().clear_bit()); diff --git a/esp-hal/src/gpio/interconnect.rs b/esp-hal/src/gpio/interconnect.rs index 126b66394a5..c63c37ecc68 100644 --- a/esp-hal/src/gpio/interconnect.rs +++ b/esp-hal/src/gpio/interconnect.rs @@ -5,7 +5,9 @@ use crate::{ self, AlternateFunction, AnyPin, + Flex, InputPin, + InputSignalType, Level, NoPin, OutputPin, @@ -34,28 +36,180 @@ pub trait PeripheralInput: Into + 'static {} /// [`PeripheralInput`] as arguments instead of pin types. pub trait PeripheralOutput: Into + 'static {} +// Pins impl PeripheralInput for P {} impl PeripheralOutput for P {} -impl PeripheralInput for InputSignal {} -impl PeripheralInput for OutputSignal {} -impl PeripheralOutput for OutputSignal {} +// Pin drivers +impl PeripheralInput for Flex<'static, P> {} +impl PeripheralOutput for Flex<'static, P> {} +// Placeholders impl PeripheralInput for NoPin {} impl PeripheralOutput for NoPin {} impl PeripheralInput for Level {} impl PeripheralOutput for Level {} -impl PeripheralInput for InputConnection {} +// Split signals +impl PeripheralInput for InputSignal {} +impl PeripheralInput for OutputSignal {} +impl PeripheralOutput for OutputSignal {} +// Type-erased signals +impl PeripheralInput for InputConnection {} impl PeripheralInput for OutputConnection {} impl PeripheralOutput for OutputConnection {} -/// A configurable input signal between a peripheral and a GPIO pin. +impl gpio::InputSignal { + fn can_use_gpio_matrix(self) -> bool { + self as InputSignalType <= INPUT_SIGNAL_MAX + } + + /// Connects a peripheral input signal to a GPIO or a constant level. + /// + /// Note that connecting multiple GPIOs to a single peripheral input is not + /// possible and the previous connection will be replaced. + /// + /// Also note that a peripheral input must always be connected to something, + /// so if you want to disconnect it from GPIOs, you should connect it to a + /// constant level. + #[inline] + pub fn connect_to(self, pin: impl Peripheral

) { + crate::into_mapped_ref!(pin); + + pin.connect_input_to_peripheral(self); + } +} + +impl gpio::OutputSignal { + fn can_use_gpio_matrix(self) -> bool { + self as OutputSignalType <= OUTPUT_SIGNAL_MAX + } + + /// Connects a peripheral output signal to a GPIO. + /// + /// Note that connecting multiple output signals to a single GPIO is not + /// possible and the previous connection will be replaced. + /// + /// Also note that it is possible to connect a peripheral output signal to + /// multiple GPIOs, and old connections will not be cleared automatically. + #[inline] + pub fn connect_to(self, pin: impl Peripheral

) { + crate::into_mapped_ref!(pin); + + pin.connect_peripheral_to_output(self); + } + + /// Disconnects a peripheral output signal from a GPIO. + #[inline] + pub fn disconnect_from(self, pin: impl Peripheral

) { + crate::into_mapped_ref!(pin); + + pin.disconnect_from_peripheral_output(self); + } +} + +/// Connects a peripheral input (`signal`, e.g. SPI MISO) to a GPIO or a +/// constant level (`input`). /// -/// Obtained by calling [`super::GpioPin::peripheral_input()`], -/// [`super::Flex::peripheral_input()`] or [`super::Input::peripheral_input()`]. +/// - `signal`: The input signal to connect to the pin +/// - `input`: The GPIO (or constant level) number to connect to the input +/// signal. +/// - `invert`: Configures whether or not to invert the input value +/// - `use_gpio_matrix`: true to route through the GPIO matrix +pub(crate) fn connect_input_signal( + signal: gpio::InputSignal, + input: u8, + invert: bool, + use_gpio_matrix: bool, +) { + assert!( + signal.can_use_gpio_matrix() || !use_gpio_matrix, + "{:?} cannot be routed through the GPIO matrix", + signal + ); + unsafe { GPIO::steal() } + .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) + .write(|w| unsafe { + w.sel().bit(use_gpio_matrix); + w.in_inv_sel().bit(invert); + w.in_sel().bits(input) // Connect to GPIO or constant level + }); +} + +fn connect_pin_to_input_signal( + pin: &mut AnyPin, + signal: gpio::InputSignal, + is_inverted: bool, + force_gpio: bool, +) { + let af = if is_inverted || force_gpio { + GPIO_FUNCTION + } else { + pin.input_signals(private::Internal) + .iter() + .find(|(_af, s)| *s == signal) + .map(|(af, _)| *af) + .unwrap_or(GPIO_FUNCTION) + }; + + pin.set_alternate_function(af, private::Internal); + + connect_input_signal(signal, pin.number(), is_inverted, af == GPIO_FUNCTION); +} + +fn connect_peripheral_to_output( + pin: &mut AnyPin, + signal: gpio::OutputSignal, + is_inverted: bool, + force_gpio: bool, + peripheral_control_output_enable: bool, + invert_output_enable: bool, +) { + let af = if is_inverted || force_gpio { + GPIO_FUNCTION + } else { + pin.output_signals(private::Internal) + .iter() + .find(|(_af, s)| *s == signal) + .map(|(af, _)| *af) + .unwrap_or(GPIO_FUNCTION) + }; + + assert!( + signal.can_use_gpio_matrix() || af != GPIO_FUNCTION, + "{:?} cannot be routed through the GPIO matrix", + signal + ); + + pin.set_alternate_function(af, private::Internal); + + // Inlined because output signals can only be connected to pins or nothing, so + // there is no other user. + unsafe { GPIO::steal() } + .func_out_sel_cfg(pin.number() as usize) + .write(|w| unsafe { + if af == GPIO_FUNCTION { + // Ignored if the signal is not routed through the GPIO matrix - alternate + // function selects peripheral signal directly. + w.out_sel().bits(signal as _); + w.inv_sel().bit(is_inverted); + } + w.oen_sel().bit(!peripheral_control_output_enable); + w.oen_inv_sel().bit(invert_output_enable) + }); +} + +fn disconnect_peripheral_output_from_pin(pin: &mut AnyPin, signal: gpio::OutputSignal) { + pin.set_alternate_function(GPIO_FUNCTION, private::Internal); + + unsafe { GPIO::steal() } + .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) + .write(|w| w.sel().clear_bit()); +} + +/// A configurable input signal between a peripheral and a GPIO pin. /// /// Multiple input signals can be connected to one pin. pub struct InputSignal { @@ -63,6 +217,24 @@ pub struct InputSignal { is_inverted: bool, } +impl

From

for InputSignal +where + P: InputPin, +{ + fn from(input: P) -> Self { + Self::new(input.degrade()) + } +} + +impl

From> for InputSignal +where + P: InputPin, +{ + fn from(input: Flex<'static, P>) -> Self { + Self::new(input.degrade()) + } +} + impl Clone for InputSignal { fn clone(&self) -> Self { Self { @@ -90,8 +262,13 @@ impl InputSignal { } } + /// Returns the GPIO number of the underlying pin. + pub fn number(&self) -> u8 { + self.pin.number() + } + /// Returns the current signal level. - pub fn get_level(&self) -> Level { + pub fn level(&self) -> Level { self.is_input_high(private::Internal).into() } @@ -102,7 +279,7 @@ impl InputSignal { self.is_inverted = !self.is_inverted; } - /// Consumed the signal and returns a new one that inverts the peripheral's + /// Consumes the signal and returns a new one that inverts the peripheral's /// input signal. /// /// Calling this function multiple times toggles the setting. @@ -111,66 +288,13 @@ impl InputSignal { self } - /// - signal: The input signal to connect to the pin - /// - invert: Configures whether or not to invert the input value - /// - input: The GPIO number to connect to the input signal - fn connect(&self, signal: usize, invert: bool, input: u8) { - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal - FUNC_IN_SEL_OFFSET) - .modify(|_, w| unsafe { - w.sel().set_bit(); - w.in_inv_sel().bit(invert); - w.in_sel().bits(input) - }); - } -} - -impl InputSignal { /// Connect the pin to a peripheral input signal. /// /// Since there can only be one input signal connected to a peripheral at a /// time, this function will disconnect any previously connected input /// signals. - fn connect_input_to_peripheral(&mut self, signal: gpio::InputSignal, _: private::Internal) { - let signal_nr = signal as usize; - - let af = if self.is_inverted { - GPIO_FUNCTION - } else { - self.input_signals(private::Internal) - .iter() - .find(|(_af, s)| *s == signal) - .map(|(af, _)| *af) - .unwrap_or(GPIO_FUNCTION) - }; - - if af == GPIO_FUNCTION && signal_nr > INPUT_SIGNAL_MAX as usize { - panic!("Cannot connect GPIO to this peripheral"); - } - - self.pin.set_alternate_function(af, private::Internal); - - if signal_nr <= INPUT_SIGNAL_MAX as usize { - self.connect(signal_nr, self.is_inverted, self.pin.number()); - } - } - - /// Remove this pin from a connected peripheral input. - /// - /// Clears the entry in the GPIO matrix / Io mux that associates this input - /// pin with the given [input `signal`](`InputSignal`). Any other - /// connected signals remain intact. - fn disconnect_input_from_peripheral( - &mut self, - signal: gpio::InputSignal, - _: private::Internal, - ) { - self.pin - .set_alternate_function(GPIO_FUNCTION, private::Internal); - - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) - .modify(|_, w| w.sel().clear_bit()); + fn connect_input_to_peripheral(&mut self, signal: gpio::InputSignal) { + connect_pin_to_input_signal(&mut self.pin, signal, self.is_inverted, true); } delegate::delegate! { @@ -181,23 +305,74 @@ impl InputSignal { pub fn init_input(&self, pull: Pull, _internal: private::Internal); pub fn is_input_high(&self, _internal: private::Internal) -> bool; pub fn enable_input(&mut self, on: bool, _internal: private::Internal); - pub fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); + } + } +} + +/// A limited, private version of [InputSignal] that allows bypassing the GPIO +/// matrix. This is only usable when the GPIO pin connected to the signal is not +/// split. +struct DirectInputSignal { + pin: AnyPin, +} + +impl Clone for DirectInputSignal { + fn clone(&self) -> Self { + Self::new(unsafe { self.pin.clone_unchecked() }) + } +} + +impl DirectInputSignal { + pub(crate) fn new(pin: AnyPin) -> Self { + Self { pin } + } + + /// Connect the pin to a peripheral input signal. + /// + /// Since there can only be one input signal connected to a peripheral at a + /// time, this function will disconnect any previously connected input + /// signals. + fn connect_input_to_peripheral(&mut self, signal: gpio::InputSignal) { + connect_pin_to_input_signal(&mut self.pin, signal, false, false); + } + + delegate::delegate! { + to self.pin { + fn pull_direction(&self, pull: Pull, _internal: private::Internal); + fn input_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::InputSignal)]; + fn init_input(&self, pull: Pull, _internal: private::Internal); + fn is_input_high(&self, _internal: private::Internal) -> bool; + fn enable_input(&mut self, on: bool, _internal: private::Internal); } } } /// A configurable output signal between a peripheral and a GPIO pin. /// -/// Obtained by calling [`super::GpioPin::into_peripheral_output()`], -/// [`super::Flex::into_peripheral_output()`] or -/// [`super::Output::into_peripheral_output()`]. -/// /// Multiple pins can be connected to one output signal. pub struct OutputSignal { pin: AnyPin, is_inverted: bool, } +impl

From

for OutputSignal +where + P: OutputPin, +{ + fn from(input: P) -> Self { + Self::new(input.degrade()) + } +} + +impl

From> for OutputSignal +where + P: OutputPin, +{ + fn from(input: Flex<'static, P>) -> Self { + Self::new(input.degrade()) + } +} + impl Peripheral for OutputSignal { type P = Self; @@ -219,6 +394,11 @@ impl OutputSignal { } } + /// Returns the GPIO number of the underlying pin. + pub fn number(&self) -> u8 { + self.pin.number() + } + /// Inverts the peripheral's output signal. /// /// Calling this function multiple times toggles the setting. @@ -226,7 +406,7 @@ impl OutputSignal { self.is_inverted = !self.is_inverted; } - /// Consumed the signal and returns a new one that inverts the peripheral's + /// Consumes the signal and returns a new one that inverts the peripheral's /// output signal. /// /// Calling this function multiple times toggles the setting. @@ -235,141 +415,18 @@ impl OutputSignal { self } - /// - signal: The input signal to connect to the pin - /// - invert: Configures whether or not to invert the input value - /// - input: The GPIO number to connect to the input signal - fn connect_input(&self, signal: usize, invert: bool, input: u8) { - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal - FUNC_IN_SEL_OFFSET) - .modify(|_, w| unsafe { - w.sel().set_bit(); - w.in_inv_sel().bit(invert); - w.in_sel().bits(input) - }); - } - - /// - signal: The output signal to connect to the pin - /// - invert: Configures whether or not to invert the output value - /// - invert_enable: Configures whether or not to invert the output enable - /// signal - /// - enable_from_gpio: Configures to select the source of output enable - /// signal. - /// - false: Use output enable signal from peripheral - /// - true: Force the output enable signal to be sourced from bit n of - /// GPIO_ENABLE_REG - /// - output: The GPIO number to connect to the output signal - fn connect_output( - &self, - signal: OutputSignalType, - invert: bool, - invert_enable: bool, - enable_from_gpio: bool, - output: u8, - ) { - unsafe { GPIO::steal() } - .func_out_sel_cfg(output as usize) - .modify(|_, w| unsafe { - w.out_sel().bits(signal); - w.inv_sel().bit(invert); - w.oen_sel().bit(enable_from_gpio); - w.oen_inv_sel().bit(invert_enable) - }); - } -} - -impl OutputSignal { - /// Connect the pin to a peripheral input signal. - /// - /// Since there can only be one signal connected to a peripheral input at a - /// time, this function will disconnect any previously connected input - /// signals. - fn connect_input_to_peripheral(&mut self, signal: gpio::InputSignal, _: private::Internal) { - let signal_nr = signal as usize; - - let af = if self.is_inverted { - GPIO_FUNCTION - } else { - self.input_signals(private::Internal) - .iter() - .find(|(_af, s)| *s == signal) - .map(|(af, _)| *af) - .unwrap_or(GPIO_FUNCTION) - }; - - if af == GPIO_FUNCTION && signal_nr > INPUT_SIGNAL_MAX as usize { - panic!("Cannot connect GPIO to this peripheral"); - } - - self.pin.set_alternate_function(af, private::Internal); - - if signal_nr <= INPUT_SIGNAL_MAX as usize { - self.connect_input(signal_nr, self.is_inverted, self.pin.number()); - } - } - - /// Remove this pin from a connected peripheral input. - /// - /// Clears the entry in the GPIO matrix / Io mux that associates this input - /// pin with the given [input `signal`](`InputSignal`). Any other - /// connected signals remain intact. - fn disconnect_input_from_peripheral( - &mut self, - signal: gpio::InputSignal, - _: private::Internal, - ) { - self.pin - .set_alternate_function(GPIO_FUNCTION, private::Internal); - - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) - .modify(|_, w| w.sel().clear_bit()); + /// Connect the pin to a peripheral output signal. + fn connect_peripheral_to_output(&mut self, signal: gpio::OutputSignal) { + connect_peripheral_to_output(&mut self.pin, signal, self.is_inverted, true, true, false); } - /// Connect the pin to a peripheral output signal. - fn connect_peripheral_to_output(&mut self, signal: gpio::OutputSignal, _: private::Internal) { - let af = if self.is_inverted { - GPIO_FUNCTION - } else { - self.output_signals(private::Internal) - .iter() - .find(|(_af, s)| *s == signal) - .map(|(af, _)| *af) - .unwrap_or(GPIO_FUNCTION) - }; - - self.pin.set_alternate_function(af, private::Internal); - - let clipped_signal = if signal as usize <= OUTPUT_SIGNAL_MAX as usize { - signal as OutputSignalType - } else { - OUTPUT_SIGNAL_MAX - }; - - self.connect_output( - clipped_signal, - self.is_inverted, - false, - false, - self.pin.number(), - ); - } - - /// Remove this output pin from a connected [signal](`OutputSignal`). + /// Remove this output pin from a connected [signal](`gpio::OutputSignal`). /// /// Clears the entry in the GPIO matrix / Io mux that associates this output - /// pin with a previously connected [signal](`OutputSignal`). Any other - /// outputs connected to the peripheral remain intact. - fn disconnect_from_peripheral_output( - &mut self, - signal: gpio::OutputSignal, - _: private::Internal, - ) { - self.pin - .set_alternate_function(GPIO_FUNCTION, private::Internal); - - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) - .modify(|_, w| w.sel().clear_bit()); + /// pin with a previously connected [signal](`gpio::OutputSignal`). Any + /// other outputs connected to the peripheral remain intact. + fn disconnect_from_peripheral_output(&mut self, signal: gpio::OutputSignal) { + disconnect_peripheral_output_from_pin(&mut self.pin, signal); } delegate::delegate! { @@ -380,7 +437,6 @@ impl OutputSignal { pub fn init_input(&self, pull: Pull, _internal: private::Internal); pub fn is_input_high(&self, _internal: private::Internal) -> bool; pub fn enable_input(&mut self, on: bool, _internal: private::Internal); - pub fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::OutputSignal)]; pub fn set_to_open_drain_output(&mut self, _internal: private::Internal); @@ -389,7 +445,6 @@ impl OutputSignal { pub fn set_output_high(&mut self, on: bool, _internal: private::Internal); pub fn set_drive_strength(&mut self, strength: gpio::DriveStrength, _internal: private::Internal); pub fn enable_open_drain(&mut self, on: bool, _internal: private::Internal); - pub fn enable_output_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn is_set_high(&self, _internal: private::Internal) -> bool; @@ -397,13 +452,62 @@ impl OutputSignal { } } +/// A limited, private version of [OutputSignal] that allows bypassing the GPIO +/// matrix. This is only usable when the GPIO pin connected to the signal is not +/// split. +struct DirectOutputSignal { + pin: AnyPin, +} + +impl DirectOutputSignal { + pub(crate) fn new(pin: AnyPin) -> Self { + Self { pin } + } + + /// Connect the pin to a peripheral output signal. + fn connect_peripheral_to_output(&mut self, signal: gpio::OutputSignal) { + connect_peripheral_to_output(&mut self.pin, signal, false, false, true, false); + } + + /// Remove this output pin from a connected [signal](`gpio::OutputSignal`). + /// + /// Clears the entry in the GPIO matrix / Io mux that associates this output + /// pin with a previously connected [signal](`gpio::OutputSignal`). Any + /// other outputs connected to the peripheral remain intact. + fn disconnect_from_peripheral_output(&mut self, signal: gpio::OutputSignal) { + disconnect_peripheral_output_from_pin(&mut self.pin, signal); + } + + delegate::delegate! { + to self.pin { + fn pull_direction(&self, pull: Pull, _internal: private::Internal); + fn input_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::InputSignal)]; + fn init_input(&self, pull: Pull, _internal: private::Internal); + fn is_input_high(&self, _internal: private::Internal) -> bool; + fn enable_input(&mut self, on: bool, _internal: private::Internal); + + fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::OutputSignal)]; + fn set_to_open_drain_output(&mut self, _internal: private::Internal); + fn set_to_push_pull_output(&mut self, _internal: private::Internal); + fn enable_output(&mut self, on: bool, _internal: private::Internal); + fn set_output_high(&mut self, on: bool, _internal: private::Internal); + fn set_drive_strength(&mut self, strength: gpio::DriveStrength, _internal: private::Internal); + fn enable_open_drain(&mut self, on: bool, _internal: private::Internal); + fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); + fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); + fn is_set_high(&self, _internal: private::Internal) -> bool; + } + } +} + #[derive(Clone)] enum InputConnectionInner { Input(InputSignal), + DirectInput(DirectInputSignal), Constant(Level), } -/// A type-erased peripheral input signal connection. +/// A peripheral input signal connection. /// /// This is mainly intended for internal use, but it can be used to connect /// peripherals within the MCU without external hardware. @@ -441,9 +545,7 @@ where P: InputPin, { fn from(input: P) -> Self { - Self(InputConnectionInner::Input( - input.degrade().peripheral_input(), - )) + Self(InputConnectionInner::Input(InputSignal::from(input))) } } @@ -456,15 +558,33 @@ impl From for InputConnection { } } +impl From for InputConnection { + fn from(output_signal: DirectOutputSignal) -> Self { + Self(InputConnectionInner::DirectInput(DirectInputSignal::new( + output_signal.pin, + ))) + } +} + impl From for InputConnection { fn from(conn: OutputConnection) -> Self { match conn.0 { OutputConnectionInner::Output(inner) => inner.into(), + OutputConnectionInner::DirectOutput(inner) => inner.into(), OutputConnectionInner::Constant(inner) => inner.into(), } } } +impl

From> for InputConnection +where + P: InputPin, +{ + fn from(pin: Flex<'static, P>) -> Self { + pin.peripheral_input().into() + } +} + impl Sealed for InputConnection {} impl InputConnection { @@ -472,6 +592,7 @@ impl InputConnection { #[doc(hidden)] to match &self.0 { InputConnectionInner::Input(pin) => pin, + InputConnectionInner::DirectInput(pin) => pin, InputConnectionInner::Constant(level) => level, } { pub fn pull_direction(&self, pull: Pull, _internal: private::Internal); @@ -483,22 +604,22 @@ impl InputConnection { #[doc(hidden)] to match &mut self.0 { InputConnectionInner::Input(pin) => pin, + InputConnectionInner::DirectInput(pin) => pin, InputConnectionInner::Constant(level) => level, } { pub fn enable_input(&mut self, on: bool, _internal: private::Internal); - pub fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); - pub fn connect_input_to_peripheral(&mut self, signal: crate::gpio::InputSignal, _internal: private::Internal); - pub fn disconnect_input_from_peripheral(&mut self, signal: crate::gpio::InputSignal, _internal: private::Internal); + fn connect_input_to_peripheral(&mut self, signal: gpio::InputSignal); } } } enum OutputConnectionInner { Output(OutputSignal), + DirectOutput(DirectOutputSignal), Constant(Level), } -/// A type-erased peripheral (input and) output signal connection. +/// A peripheral (input and) output signal connection. /// /// This is mainly intended for internal use, but it can be used to connect /// peripherals within the MCU without external hardware. @@ -512,6 +633,9 @@ impl Peripheral for OutputConnection { unsafe fn clone_unchecked(&self) -> Self::P { match self { Self(OutputConnectionInner::Output(signal)) => Self::from(signal.clone_unchecked()), + Self(OutputConnectionInner::DirectOutput(signal)) => { + Self::from(DirectOutputSignal::new(signal.pin.clone_unchecked())) + } Self(OutputConnectionInner::Constant(level)) => Self::from(*level), } } @@ -534,9 +658,7 @@ where P: OutputPin, { fn from(input: P) -> Self { - Self(OutputConnectionInner::Output( - input.degrade().into_peripheral_output(), - )) + Self(OutputConnectionInner::Output(OutputSignal::from(input))) } } @@ -546,54 +668,55 @@ impl From for OutputConnection { } } +impl

From> for OutputConnection +where + P: OutputPin, +{ + fn from(pin: Flex<'static, P>) -> Self { + pin.into_peripheral_output().into() + } +} + +impl From for OutputConnection { + fn from(signal: DirectOutputSignal) -> Self { + Self(OutputConnectionInner::DirectOutput(signal)) + } +} + impl OutputConnection { delegate::delegate! { #[doc(hidden)] to match &self.0 { OutputConnectionInner::Output(pin) => pin, + OutputConnectionInner::DirectOutput(pin) => pin, OutputConnectionInner::Constant(level) => level, } { pub fn is_input_high(&self, _internal: private::Internal) -> bool; pub fn input_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::InputSignal)]; + + pub fn is_set_high(&self, _internal: private::Internal) -> bool; + pub fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::OutputSignal)]; } #[doc(hidden)] to match &mut self.0 { OutputConnectionInner::Output(pin) => pin, + OutputConnectionInner::DirectOutput(pin) => pin, OutputConnectionInner::Constant(level) => level, } { pub fn pull_direction(&mut self, pull: Pull, _internal: private::Internal); pub fn init_input(&mut self, pull: Pull, _internal: private::Internal); pub fn enable_input(&mut self, on: bool, _internal: private::Internal); - pub fn enable_input_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); - pub fn connect_input_to_peripheral(&mut self, signal: crate::gpio::InputSignal, _internal: private::Internal); - pub fn disconnect_input_from_peripheral(&mut self, signal: crate::gpio::InputSignal, _internal: private::Internal); - } - - #[doc(hidden)] - to match &self.0 { - OutputConnectionInner::Output(pin) => pin, - OutputConnectionInner::Constant(level) => level, - } { - pub fn is_set_high(&self, _internal: private::Internal) -> bool; - pub fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, gpio::OutputSignal)]; - } - #[doc(hidden)] - to match &mut self.0 { - OutputConnectionInner::Output(pin) => pin, - OutputConnectionInner::Constant(level) => level, - } { pub fn set_to_open_drain_output(&mut self, _internal: private::Internal); pub fn set_to_push_pull_output(&mut self, _internal: private::Internal); pub fn enable_output(&mut self, on: bool, _internal: private::Internal); pub fn set_output_high(&mut self, on: bool, _internal: private::Internal); pub fn set_drive_strength(&mut self, strength: gpio::DriveStrength, _internal: private::Internal); pub fn enable_open_drain(&mut self, on: bool, _internal: private::Internal); - pub fn enable_output_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); pub fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _internal: private::Internal); - pub fn connect_peripheral_to_output(&mut self, signal: gpio::OutputSignal, _internal: private::Internal); - pub fn disconnect_from_peripheral_output(&mut self, signal: gpio::OutputSignal, _internal: private::Internal); + fn connect_peripheral_to_output(&mut self, signal: gpio::OutputSignal); + fn disconnect_from_peripheral_output(&mut self, signal: gpio::OutputSignal); } } } diff --git a/esp-hal/src/gpio/lp_io.rs b/esp-hal/src/gpio/lp_io.rs index 4eb849f83c0..947a9efc7e8 100644 --- a/esp-hal/src/gpio/lp_io.rs +++ b/esp-hal/src/gpio/lp_io.rs @@ -15,14 +15,15 @@ //! chip from Deep-sleep. //! //! # Example +//! //! ## Configure a LP Pin as Output +//! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! use esp_hal::gpio::Io; //! use esp_hal::gpio::lp_io::LowPowerOutput; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! // configure GPIO 1 as LP output pin -//! let lp_pin: LowPowerOutput<'_, 1> = LowPowerOutput::new(io.pins.gpio1); +//! let lp_pin: LowPowerOutput<'_, 1> = +//! LowPowerOutput::new(peripherals.GPIO1); //! # } //! ``` diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 1631aa655c1..1a329590450 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1,39 +1,50 @@ -//! # General Purpose I/Os (GPIO) +//! # General Purpose Input/Output (GPIO) //! //! ## Overview //! //! Each pin can be used as a general-purpose I/O, or be connected to one or //! more internal peripheral signals. +#![cfg_attr( + soc_etm, + doc = "The GPIO pins also provide tasks and events via the ETM interconnect system. For more information, see the [etm] module." +)] +#![doc = ""] +//! ## Working with pins //! -//! ## Configuration +//! Before use, the GPIO module must be initialized by creating an instance of +//! the [`Io`] struct. This struct provides access to the pins on the chip. //! -//! This driver supports various operations on GPIO pins, including setting the -//! pin mode, direction, and manipulating the pin state (setting high/low, -//! toggling). It provides an interface to interact with GPIO pins on ESP chips, -//! allowing developers to control and read the state of the pins. +//! The [`Io`] struct can also be used to configure the interrupt handler for +//! GPIO interrupts. For more information, see the +//! [`Io::set_interrupt_handler`]. //! -//! ## Usage +//! The pins are accessible via the [`crate::Peripherals`] struct returned by +//! [`crate::init`]. These pins can then be passed to peripherals (such as +//! SPI, UART, I2C, etc.), to pin drivers or can be [`GpioPin::split`] into +//! peripheral signals. //! -//! This module also implements a number of traits from [embedded-hal] to -//! provide a common interface for GPIO pins. +//! Each pin is a different type initially. Internally, `esp-hal` will often +//! erase their types automatically, but they can also be converted into +//! [`AnyPin`] manually by calling [`Pin::degrade`]. //! -//! To get access to the pins, you first need to convert them into a HAL -//! designed struct from the pac struct `GPIO` and `IO_MUX` using [`Io::new`]. +//! Pin drivers can be created using [`Flex::new`], [`Input::new`], +//! [`Output::new`] and [`OutputOpenDrain::new`]. If you need the pin drivers to +//! carry the type of the pin, you can use the [`Flex::new_typed`], +//! [`Input::new_typed`], [`Output::new_typed`], and +//! [`OutputOpenDrain::new_typed`] functions. //! -//! ### Pin Types +//! ## GPIO interconnect //! -//! - [Input] pins can be used as digital inputs. -//! - [Output] and [OutputOpenDrain] pins can be used as digital outputs. -//! - [Flex] pin is a pin that can be used as an input and output pin. -//! - [AnyPin] is a type-erased GPIO pin with support for inverted signalling. -//! - [NoPin] is a useful for cases where peripheral driver requires a pin, but -//! real pin cannot be used. +//! Sometimes you may want to connect peripherals together without using +//! external hardware. The [`interconnect`] module provides tools to achieve +//! this using GPIO pins. //! -//! ### GPIO interconnect -//! -//! Each GPIO can be connected to one output signal and any number of input -//! signals. This allows connections inside of the MCU without allocating and -//! connecting multiple pins for loopback functionality. +//! To obtain peripheral signals, use the [`GpioPin::split`] method to split a +//! pin into an input and output signal. Alternatively, you may use +//! [`Flex::split`], [`Flex::into_peripheral_output`], +//! [`Flex::peripheral_input`], and similar methods to split a pin driver into +//! an input and output signal. You can then pass these signals to the +//! peripheral drivers similar to how you would pass a pin. //! //! ## Examples //! @@ -42,32 +53,39 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::gpio::{Io, Level, Output}; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let mut led = Output::new(io.pins.gpio5, Level::High); +//! let mut led = Output::new(peripherals.GPIO5, Level::High); //! # } //! ``` //! //! ### Blink an LED //! -//! See the [Blinky] section of the crate documentation. +//! See the [Blinky][crate#blinky] section of the crate documentation. +//! +//! ### Inverting peripheral signals //! -//! ### Inverting a signal using `AnyPin` //! See the [Inverting TX and RX Pins] example of the UART documentation. //! //! [embedded-hal]: https://docs.rs/embedded-hal/latest/embedded_hal/ -//! [Blinky]: ../index.html#blinky -//! [Inverting TX and RX Pins]: ../uart/index.html#inverting-tx-and-rx-pins +//! [Inverting TX and RX Pins]: crate::uart#inverting-rx-and-tx-pins use portable_atomic::{AtomicPtr, Ordering}; use procmacros::ram; +#[cfg(any(lp_io, rtc_cntl))] +use crate::peripherals::gpio::{handle_rtcio, handle_rtcio_with_resistors}; pub use crate::soc::gpio::*; use crate::{ - interrupt::InterruptHandler, + interrupt::{self, InterruptHandler, Priority}, peripheral::{Peripheral, PeripheralRef}, - peripherals::{GPIO, IO_MUX}, + peripherals::{ + gpio::{handle_gpio_input, handle_gpio_output, AnyPinInner}, + Interrupt, + GPIO, + IO_MUX, + }, private::{self, Sealed}, InterruptConfigurable, + DEFAULT_INTERRUPT_HANDLER, }; pub mod interconnect; @@ -83,7 +101,6 @@ pub mod lp_io; pub mod rtc_io; /// Convenience constant for `Option::None` pin - static USER_INTERRUPT_HANDLER: CFnPtr = CFnPtr::new(); struct CFnPtr(AtomicPtr<()>); @@ -218,9 +235,13 @@ pub enum DriveStrength { /// /// GPIO pins can be configured for various functions, such as GPIO /// or being directly connected to a peripheral's signal like UART, SPI, etc. -/// The `AlternateFunction` enum allows to select one of several functions that +/// The `AlternateFunction` enum allows selecting one of several functions that /// a pin can perform, rather than using it as a general-purpose input or /// output. +/// +/// The different variants correspond to different functionality depending on +/// the chip and the specific pin. For more information, refer to your chip's +#[doc = crate::trm_markdown_link!("iomuxgpio")] #[derive(Debug, Eq, PartialEq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AlternateFunction { @@ -322,13 +343,13 @@ pub trait Pin: Sealed { /// Enable/disable sleep-mode #[doc(hidden)] fn sleep_mode(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.slp_sel().bit(on)); + io_mux_reg(self.number()).modify(|_, w| w.slp_sel().bit(on)); } /// Configure the alternate function #[doc(hidden)] fn set_alternate_function(&mut self, alternate: AlternateFunction, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) }); + io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8) }); } /// Enable or disable the GPIO pin output buffer. @@ -341,7 +362,7 @@ pub trait Pin: Sealed { /// Enable input for the pin #[doc(hidden)] fn enable_input(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on)); + io_mux_reg(self.number()).modify(|_, w| w.fun_ie().bit(on)); } #[doc(hidden)] @@ -361,7 +382,7 @@ pub trait Pin: Sealed { #[cfg(esp32)] crate::soc::gpio::errata36(self.degrade_pin(private::Internal), pull_up, pull_down); - get_io_mux_reg(self.number()).modify(|_, w| { + io_mux_reg(self.number()).modify(|_, w| { w.fun_wpd().bit(pull_down); w.fun_wpu().bit(pull_up) }); @@ -378,19 +399,13 @@ pub trait InputPin: Pin + Into + 'static { #[cfg(usb_device)] disable_usb_pads(self.number()); - get_io_mux_reg(self.number()).modify(|_, w| unsafe { + io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(GPIO_FUNCTION as u8); w.fun_ie().set_bit(); w.slp_sel().clear_bit() }); } - /// Enable input in sleep mode for the pin - #[doc(hidden)] - fn enable_input_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.mcu_ie().bit(on)); - } - /// The current state of the input #[doc(hidden)] fn is_input_high(&self, _: private::Internal) -> bool { @@ -406,6 +421,7 @@ pub trait OutputPin: Pin + Into + 'static { &mut self, alternate: AlternateFunction, open_drain: bool, + input_enable: Option, _: private::Internal, ) { self.enable_output(true, private::Internal); @@ -421,9 +437,11 @@ pub trait OutputPin: Pin + Into + 'static { #[cfg(any(esp32c3, esp32s3))] disable_usb_pads(self.number()); - get_io_mux_reg(self.number()).modify(|_, w| unsafe { + io_mux_reg(self.number()).modify(|_, w| unsafe { w.mcu_sel().bits(alternate as u8); - w.fun_ie().bit(open_drain); + if let Some(input_enable) = input_enable { + w.fun_ie().bit(input_enable); + } w.fun_drv().bits(DriveStrength::I20mA as u8); w.slp_sel().clear_bit() }); @@ -432,13 +450,13 @@ pub trait OutputPin: Pin + Into + 'static { /// Configure open-drain mode #[doc(hidden)] fn set_to_open_drain_output(&mut self, _: private::Internal) { - self.init_output(GPIO_FUNCTION, true, private::Internal); + self.init_output(GPIO_FUNCTION, true, Some(true), private::Internal); } /// Configure output mode #[doc(hidden)] fn set_to_push_pull_output(&mut self, _: private::Internal) { - self.init_output(GPIO_FUNCTION, false, private::Internal); + self.init_output(GPIO_FUNCTION, false, None, private::Internal); } /// Set the pin's level to high or low @@ -451,7 +469,7 @@ pub trait OutputPin: Pin + Into + 'static { /// Configure the [DriveStrength] of the pin #[doc(hidden)] fn set_drive_strength(&mut self, strength: DriveStrength, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| unsafe { w.fun_drv().bits(strength as u8) }); + io_mux_reg(self.number()).modify(|_, w| unsafe { w.fun_drv().bits(strength as u8) }); } /// Enable/disable open-drain mode @@ -462,22 +480,16 @@ pub trait OutputPin: Pin + Into + 'static { .modify(|_, w| w.pad_driver().bit(on)); } - /// Enable/disable output in sleep mode - #[doc(hidden)] - fn enable_output_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.mcu_oe().bit(on)); - } - /// Configure internal pull-up resistor in sleep mode #[doc(hidden)] fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.mcu_wpu().bit(on)); + io_mux_reg(self.number()).modify(|_, w| w.mcu_wpu().bit(on)); } /// Configure internal pull-down resistor in sleep mode #[doc(hidden)] fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { - get_io_mux_reg(self.number()).modify(|_, w| w.mcu_wpd().bit(on)); + io_mux_reg(self.number()).modify(|_, w| w.mcu_wpd().bit(on)); } /// Is the output set to high @@ -502,11 +514,11 @@ pub trait TouchPin: Pin { /// Reads the pin's touch measurement register #[doc(hidden)] - fn get_touch_measurement(&self, _: private::Internal) -> u16; + fn touch_measurement(&self, _: private::Internal) -> u16; /// Maps the pin nr to the touch pad nr #[doc(hidden)] - fn get_touch_nr(&self, _: private::Internal) -> u8; + fn touch_nr(&self, _: private::Internal) -> u8; /// Set a pins touch threshold for interrupts. #[doc(hidden)] @@ -711,6 +723,9 @@ impl Bank1GpioRegisterAccess { /// GPIO pin pub struct GpioPin; +/// Type-erased GPIO pin +pub struct AnyPin(pub(crate) AnyPinInner); + impl GpioPin where Self: Pin, @@ -724,13 +739,15 @@ where Self } - /// Returns a peripheral [input][interconnect::InputSignal] connected to - /// this pin. + /// Split the pin into an input and output signal. /// - /// The input signal can be passed to peripherals in place of an input pin. - #[inline] - pub fn peripheral_input(&self) -> interconnect::InputSignal { - interconnect::InputSignal::new(self.degrade_pin(private::Internal)) + /// Peripheral signals allow connecting peripherals together without using + /// external hardware. + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + ( + interconnect::InputSignal::new(self.degrade_pin(private::Internal)), + interconnect::OutputSignal::new(self.degrade_pin(private::Internal)), + ) } } @@ -778,59 +795,45 @@ where impl private::Sealed for GpioPin {} -impl GpioPin -where - Self: OutputPin, -{ - /// Turns the pin object into a peripheral - /// [output][interconnect::OutputSignal]. - /// - /// The output signal can be passed to peripherals in place of an output - /// pin. - #[inline] - pub fn into_peripheral_output(self) -> interconnect::OutputSignal { - interconnect::OutputSignal::new(self.degrade_pin(private::Internal)) +pub(crate) fn bind_default_interrupt_handler() { + // We first check if a handler is set in the vector table. + if let Some(handler) = interrupt::bound_handler(Interrupt::GPIO) { + let handler = handler as *const unsafe extern "C" fn(); + + // We only allow binding the default handler if nothing else is bound. + // This prevents silently overwriting RTIC's interrupt handler, if using GPIO. + if !core::ptr::eq(handler, DEFAULT_INTERRUPT_HANDLER.handler() as _) { + // The user has configured an interrupt handler they wish to use. + info!("Not using default GPIO interrupt handler: already bound in vector table"); + return; + } + } + // The vector table doesn't contain a custom entry.Still, the + // peripheral interrupt may already be bound to something else. + if interrupt::bound_cpu_interrupt_for(crate::Cpu::current(), Interrupt::GPIO).is_some() { + info!("Not using default GPIO interrupt handler: peripheral interrupt already in use"); + return; } + + unsafe { interrupt::bind_interrupt(Interrupt::GPIO, default_gpio_interrupt_handler) }; + // By default, we use lowest priority + unwrap!(interrupt::enable(Interrupt::GPIO, Priority::min())); } /// General Purpose Input/Output driver pub struct Io { _io_mux: IO_MUX, - /// The pins available on this chip - pub pins: Pins, } impl Io { /// Initialize the I/O driver. - pub fn new(gpio: GPIO, io_mux: IO_MUX) -> Self { - Self::new_with_priority(gpio, io_mux, crate::interrupt::Priority::min()) - } - - /// Initialize the I/O driver with a interrupt priority. - /// - /// This decides the priority for the interrupt when using async. - pub fn new_with_priority( - mut gpio: GPIO, - io_mux: IO_MUX, - prio: crate::interrupt::Priority, - ) -> Self { - gpio.bind_gpio_interrupt(gpio_interrupt_handler); - crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, prio).unwrap(); - - Self::new_no_bind_interrupt(gpio, io_mux) + pub fn new(_io_mux: IO_MUX) -> Self { + Io { _io_mux } } - /// Initialize the I/O driver without enabling the GPIO interrupt or - /// binding an interrupt handler to it. - /// - /// *Note:* You probably don't want to use this, it is intended to be used - /// in very specific use cases. Async GPIO functionality will not work - /// when instantiating `Io` using this constructor. - pub fn new_no_bind_interrupt(_gpio: GPIO, _io_mux: IO_MUX) -> Self { - Io { - _io_mux, - pins: unsafe { Pins::steal() }, - } + /// Set the interrupt priority for GPIO interrupts. + pub fn set_interrupt_priority(&self, prio: Priority) { + unwrap!(interrupt::enable(Interrupt::GPIO, prio)); } } @@ -849,15 +852,24 @@ impl InterruptConfigurable for Io { /// corresponding pin's async API. /// - You will not be notified if you make a mistake. fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - crate::interrupt::enable(crate::peripherals::Interrupt::GPIO, handler.priority()).unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::GPIO); + } + self.set_interrupt_priority(handler.priority()); + unsafe { interrupt::bind_interrupt(Interrupt::GPIO, user_gpio_interrupt_handler) }; USER_INTERRUPT_HANDLER.store(handler.handler()); } } #[ram] -extern "C" fn gpio_interrupt_handler() { +extern "C" fn user_gpio_interrupt_handler() { USER_INTERRUPT_HANDLER.call(); + default_gpio_interrupt_handler(); +} + +#[ram] +extern "C" fn default_gpio_interrupt_handler() { handle_pin_interrupts(on_pin_irq); } @@ -904,20 +916,20 @@ macro_rules! if_rtcio_pin { #[macro_export] macro_rules! io_type { (Input, $gpionum:literal) => { - impl $crate::gpio::InputPin for GpioPin<$gpionum> {} + impl $crate::gpio::InputPin for $crate::gpio::GpioPin<$gpionum> {} }; (Output, $gpionum:literal) => { - impl $crate::gpio::OutputPin for GpioPin<$gpionum> {} + impl $crate::gpio::OutputPin for $crate::gpio::GpioPin<$gpionum> {} }; (Analog, $gpionum:literal) => { // FIXME: the implementation shouldn't be in the GPIO module #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2))] - impl $crate::gpio::AnalogPin for GpioPin<$gpionum> { + impl $crate::gpio::AnalogPin for $crate::gpio::GpioPin<$gpionum> { /// Configures the pin for analog mode. fn set_analog(&self, _: $crate::private::Internal) { use $crate::peripherals::GPIO; - get_io_mux_reg($gpionum).modify(|_, w| unsafe { + $crate::gpio::io_mux_reg($gpionum).modify(|_, w| unsafe { w.mcu_sel().bits(1); w.fun_ie().clear_bit(); w.fun_wpu().clear_bit(); @@ -949,91 +961,70 @@ macro_rules! gpio { )+ ) => { paste::paste! { - /// Pins available on this chip - pub struct Pins { - $( - #[doc = concat!("GPIO pin number ", $gpionum, ".")] - pub [< gpio $gpionum >] : GpioPin<$gpionum>, - )+ - } - - impl Pins { - /// Unsafely create GPIO pins. - /// - /// # Safety - /// - /// The caller must ensure that only one instance of a pin is in use at one time. - pub unsafe fn steal() -> Self { - Self { - $( - [< gpio $gpionum >]: GpioPin::steal(), - )+ - } - } - } - $( $( $crate::io_type!($type, $gpionum); )* - impl $crate::gpio::Pin for GpioPin<$gpionum> { + impl $crate::gpio::Pin for $crate::gpio::GpioPin<$gpionum> { fn number(&self) -> u8 { $gpionum } - fn degrade_pin(&self, _: $crate::private::Internal) -> AnyPin { - AnyPin($crate::gpio::AnyPinInner::[< Gpio $gpionum >](unsafe { Self::steal() })) + fn degrade_pin(&self, _: $crate::private::Internal) -> $crate::gpio::AnyPin { + $crate::gpio::AnyPin(AnyPinInner::[< Gpio $gpionum >](unsafe { Self::steal() })) } fn gpio_bank(&self, _: $crate::private::Internal) -> $crate::gpio::GpioRegisterAccess { $crate::gpio::GpioRegisterAccess::from($gpionum) } - fn output_signals(&self, _: $crate::private::Internal) -> &[(AlternateFunction, OutputSignal)] { + fn output_signals(&self, _: $crate::private::Internal) -> &[($crate::gpio::AlternateFunction, $crate::gpio::OutputSignal)] { &[ $( $( - (AlternateFunction::[< Function $af_output_num >], OutputSignal::$af_output_signal ), + ( + $crate::gpio::AlternateFunction::[< Function $af_output_num >], + $crate::gpio::OutputSignal::$af_output_signal + ), )* )? ] } - fn input_signals(&self, _: $crate::private::Internal) -> &[(AlternateFunction, InputSignal)] { + fn input_signals(&self, _: $crate::private::Internal) -> &[($crate::gpio::AlternateFunction, $crate::gpio::InputSignal)] { &[ $( $( - (AlternateFunction::[< Function $af_input_num >], InputSignal::$af_input_signal ), + ( + $crate::gpio::AlternateFunction::[< Function $af_input_num >], + $crate::gpio::InputSignal::$af_input_signal + ), )* )? ] } } - impl From> for AnyPin { - fn from(pin: GpioPin<$gpionum>) -> Self { - use $crate::gpio::Pin; - pin.degrade() + impl From<$crate::gpio::GpioPin<$gpionum>> for $crate::gpio::AnyPin { + fn from(pin: $crate::gpio::GpioPin<$gpionum>) -> Self { + $crate::gpio::Pin::degrade(pin) } } )+ pub(crate) enum AnyPinInner { $( - [](GpioPin<$gpionum>), + []($crate::gpio::GpioPin<$gpionum>), )+ } - /// Type-erased GPIO pin - pub struct AnyPin(pub(crate) AnyPinInner); - - impl $crate::peripheral::Peripheral for AnyPin { - type P = AnyPin; + impl $crate::peripheral::Peripheral for $crate::gpio::AnyPin { + type P = $crate::gpio::AnyPin; unsafe fn clone_unchecked(&self) -> Self { match self.0 { $(AnyPinInner::[](_) => { - Self(AnyPinInner::[< Gpio $gpionum >](unsafe { GpioPin::steal() })) + Self(AnyPinInner::[< Gpio $gpionum >](unsafe { $crate::gpio::GpioPin::steal() })) })+ } } @@ -1042,7 +1033,6 @@ macro_rules! gpio { // These macros call the code block on the actually contained GPIO pin. #[doc(hidden)] - #[macro_export] macro_rules! handle_gpio_output { ($this:expr, $inner:ident, $code:tt) => { match $this { @@ -1059,7 +1049,6 @@ macro_rules! gpio { } #[doc(hidden)] - #[macro_export] macro_rules! handle_gpio_input { ($this:expr, $inner:ident, $code:tt) => { match $this { @@ -1076,7 +1065,6 @@ macro_rules! gpio { cfg_if::cfg_if! { if #[cfg(any(lp_io, rtc_cntl))] { #[doc(hidden)] - #[macro_export] macro_rules! handle_rtcio { ($this:expr, $inner:ident, $code:tt) => { match $this { @@ -1093,7 +1081,6 @@ macro_rules! gpio { } #[doc(hidden)] - #[macro_export] macro_rules! handle_rtcio_with_resistors { ($this:expr, $inner:ident, $code:tt) => { match $this { @@ -1122,312 +1109,6 @@ macro_rules! gpio { }; } -#[cfg(xtensa)] -#[doc(hidden)] -#[macro_export] -macro_rules! rtcio_analog { - ( @ignore $rue:literal ) => {}; - - ( - $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $prefix:pat, $hold:ident $(, $rue:literal)? - ) => { - impl $crate::gpio::RtcPin for GpioPin<$pin_num> - { - fn rtc_number(&self) -> u8 { - $rtc_pin - } - - /// Set the RTC properties of the pin. If `mux` is true then then pin is - /// routed to RTC, when false it is routed to IO_MUX. - fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) { - use $crate::peripherals::RTC_IO; - - let rtcio = unsafe{ &*RTC_IO::ptr() }; - - $crate::gpio::enable_iomux_clk_gate(); - - // disable input - paste::paste!{ - rtcio.$pin_reg.modify(|_,w| unsafe { - w.[<$prefix fun_ie>]().bit(input_enable); - w.[<$prefix mux_sel>]().bit(mux); - w.[<$prefix fun_sel>]().bits(func as u8) - }); - } - } - - fn rtcio_pad_hold(&mut self, enable: bool) { - let rtc_ctrl = unsafe { $crate::peripherals::LPWR::steal() }; - - cfg_if::cfg_if! { - if #[cfg(esp32)] { - let pad_hold = rtc_ctrl.hold_force(); - } else { - let pad_hold = rtc_ctrl.pad_hold(); - } - }; - - pad_hold.modify(|_, w| w.$hold().bit(enable)); - } - } - - $( - // FIXME: replace with $(ignore($rue)) once stable - $crate::rtcio_analog!(@ignore $rue); - impl $crate::gpio::RtcPinWithResistors for GpioPin<$pin_num> - { - fn rtcio_pullup(&mut self, enable: bool) { - let rtcio = unsafe { $crate::peripherals::RTC_IO::steal() }; - - paste::paste! { - rtcio.$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit(enable)); - } - } - - fn rtcio_pulldown(&mut self, enable: bool) { - let rtcio = unsafe { $crate::peripherals::RTC_IO::steal() }; - - paste::paste! { - rtcio.$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit(enable)); - } - } - } - )? - - #[cfg(any(adc, dac))] - impl $crate::gpio::AnalogPin for GpioPin<$pin_num> { - /// Configures the pin for analog mode. - fn set_analog(&self, _: $crate::private::Internal) { - let rtcio = unsafe{ &*$crate::peripherals::RTC_IO::ptr() }; - - #[cfg(esp32s2)] - $crate::gpio::enable_iomux_clk_gate(); - - // We need `paste` (and a [< >] in it) to rewrite the token stream to - // handle indexed pins. - paste::paste! { - // disable input - rtcio.$pin_reg.modify(|_,w| w.[<$prefix fun_ie>]().bit(false)); - - // disable output - rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); - - // disable open drain - rtcio.pin($rtc_pin).modify(|_,w| w.pad_driver().bit(false)); - - rtcio.$pin_reg.modify(|_,w| { - w.[<$prefix fun_ie>]().clear_bit(); - - // Connect pin to analog / RTC module instead of standard GPIO - w.[<$prefix mux_sel>]().set_bit(); - - // Select function "RTC function 1" (GPIO) for analog use - unsafe { w.[<$prefix fun_sel>]().bits(0b00) }; - - // Disable pull-up and pull-down resistors on the pin, if it has them - $( - // FIXME: replace with $(ignore($rue)) once stable - $crate::rtcio_analog!( @ignore $rue ); - w.[<$prefix rue>]().bit(false); - w.[<$prefix rde>]().bit(false); - )? - - w - }); - } - } - } - }; - - ( - $( ( $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $prefix:pat, $hold:ident $(, $rue:literal )? ) )+ - ) => { - $( - $crate::rtcio_analog!($pin_num, $rtc_pin, $pin_reg, $prefix, $hold $(, $rue )?); - )+ - - #[cfg(esp32)] - pub(crate) fn errata36(mut pin: AnyPin, pull_up: bool, pull_down: bool) { - use $crate::gpio::{Pin, RtcPinWithResistors}; - - let has_pullups = match pin.number() { - $( - $( $pin_num => $rue, )? - )+ - _ => false, - }; - - if has_pullups { - pin.rtcio_pullup(pull_up); - pin.rtcio_pulldown(pull_down); - } - } - }; -} - -#[cfg(any(esp32c2, esp32c3))] -#[doc(hidden)] -#[macro_export] -macro_rules! rtc_pins { - ( $( $pin_num:expr )+ ) => { - $( - impl $crate::gpio::RtcPin for GpioPin<$pin_num> { - unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { - let rtc_cntl = unsafe { &*$crate::peripherals::RTC_CNTL::ptr() }; - cfg_if::cfg_if! { - if #[cfg(esp32c2)] { - let gpio_wakeup = rtc_cntl.cntl_gpio_wakeup(); - } else { - let gpio_wakeup = rtc_cntl.gpio_wakeup(); - } - } - - paste::paste! { - gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); - gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); - } - } - - fn rtcio_pad_hold(&mut self, enable: bool) { - let rtc_cntl = unsafe { &*$crate::peripherals::RTC_CNTL::ptr() }; - paste::paste! { - rtc_cntl.pad_hold().modify(|_, w| w.[< gpio_pin $pin_num _hold >]().bit(enable)); - } - } - } - - impl $crate::gpio::RtcPinWithResistors for GpioPin<$pin_num> { - fn rtcio_pullup(&mut self, enable: bool) { - let io_mux = unsafe { &*$crate::peripherals::IO_MUX::ptr() }; - io_mux.gpio($pin_num).modify(|_, w| w.fun_wpu().bit(enable)); - } - - fn rtcio_pulldown(&mut self, enable: bool) { - let io_mux = unsafe { &*$crate::peripherals::IO_MUX::ptr() }; - io_mux.gpio($pin_num).modify(|_, w| w.fun_wpd().bit(enable)); - } - } - )+ - }; -} - -#[doc(hidden)] -pub fn enable_iomux_clk_gate() { - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - let sensors = unsafe { &*crate::peripherals::SENS::ptr() }; - sensors - .sar_io_mux_conf() - .modify(|_, w| w.iomux_clk_gate_en().set_bit()); - } else if #[cfg(esp32s3)] { - let sensors = unsafe { &*crate::peripherals::SENS::ptr() }; - sensors - .sar_peri_clk_gate_conf() - .modify(|_,w| w.iomux_clk_en().set_bit()); - } - } -} - -/// Common functionality for all touch pads -#[doc(hidden)] -#[macro_export] -macro_rules! touch { - (@pin_specific $touch_num:expr, true) => { - paste::paste! { - unsafe { &*RTC_IO::ptr() }.[< touch_pad $touch_num >]().write(|w| unsafe { - w.xpd().set_bit(); - // clear input_enable - w.fun_ie().clear_bit(); - // Connect pin to analog / RTC module instead of standard GPIO - w.mux_sel().set_bit(); - // Disable pull-up and pull-down resistors on the pin - w.rue().clear_bit(); - w.rde().clear_bit(); - w.tie_opt().clear_bit(); - // Select function "RTC function 1" (GPIO) for analog use - w.fun_sel().bits(0b00) - }); - } - }; - - (@pin_specific $touch_num:expr, false) => { - paste::paste! { - unsafe { &*RTC_IO::ptr() }.[< touch_pad $touch_num >]().write(|w| { - w.xpd().set_bit(); - w.tie_opt().clear_bit() - }); - } - }; - - ( - $( - ( - $touch_num:literal, $pin_num:literal, $rtc_pin:literal, $touch_out_reg:expr, $meas_field: expr, $touch_thres_reg:expr, $touch_thres_field:expr, $normal_pin:literal - ) - )+ - ) => { - $( - impl $crate::gpio::TouchPin for GpioPin<$pin_num> { - fn set_touch(&self, _: $crate::private::Internal) { - use $crate::peripherals::{GPIO, RTC_IO, SENS}; - - let gpio = unsafe { &*GPIO::ptr() }; - let rtcio = unsafe { &*RTC_IO::ptr() }; - let sens = unsafe { &*SENS::ptr() }; - - // Pad to normal mode (not open-drain) - gpio.pin($rtc_pin).write(|w| w.pad_driver().clear_bit()); - - // clear output - rtcio - .enable_w1tc() - .write(|w| unsafe { w.enable_w1tc().bits(1 << $rtc_pin) }); - paste::paste! { - sens . $touch_thres_reg () - .write(|w| unsafe { - w. $touch_thres_field ().bits( - 0b0 // Default: 0 for esp32 gets overridden later anyway. - ) - }); - - $crate::touch!( @pin_specific $touch_num, $normal_pin ); - - // enable the pin - sens.sar_touch_enable().modify(|r, w| unsafe { - w.touch_pad_worken().bits( - r.touch_pad_worken().bits() | ( 1 << [< $touch_num >] ) - ) - }); - } - } - - fn get_touch_measurement(&self, _: $crate::private::Internal) -> u16 { - paste::paste! { - unsafe { &* $crate::peripherals::SENS::ptr() } - . $touch_out_reg () - .read() - . $meas_field () - .bits() - } - } - - fn get_touch_nr(&self, _: $crate::private::Internal) -> u8 { - $touch_num - } - - fn set_threshold(&self, threshold: u16, _: $crate::private::Internal) { - paste::paste! { - unsafe { &* $crate::peripherals::SENS::ptr() } - . $touch_thres_reg () - .write(|w| unsafe { - w. $touch_thres_field ().bits(threshold) - }); - } - } - })+ - }; -} - /// GPIO output driver. pub struct Output<'d, P = AnyPin> { pin: Flex<'d, P>, @@ -1436,8 +1117,8 @@ pub struct Output<'d, P = AnyPin> { impl

private::Sealed for Output<'_, P> {} impl<'d, P> Peripheral for Output<'d, P> { - type P = P; - unsafe fn clone_unchecked(&self) -> P { + type P = Flex<'d, P>; + unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } @@ -1458,18 +1139,39 @@ where /// Create GPIO output driver for a [GpioPin] with the provided level #[inline] pub fn new_typed(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { - let pin = Flex::new_typed(pin); + let mut pin = Flex::new_typed(pin); + + pin.set_level(initial_output); + pin.set_as_output(); - Self::new_inner(pin, initial_output) + Self { pin } } - fn new_inner(mut pin: Flex<'d, P>, initial_output: Level) -> Self { - pin.pin - .set_output_high(initial_output.into(), private::Internal); + /// Split the pin into an input and output signal. + /// + /// Peripheral signals allow connecting peripherals together without using + /// external hardware. + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + self.pin.split() + } - pin.set_as_output(); + /// Returns a peripheral [input][interconnect::InputSignal] connected to + /// this pin. + /// + /// The input signal can be passed to peripherals in place of an input pin. + #[inline] + pub fn peripheral_input(&self) -> interconnect::InputSignal { + self.pin.peripheral_input() + } - Self { pin } + /// Turns the pin object into a peripheral + /// [output][interconnect::OutputSignal]. + /// + /// The output signal can be passed to peripherals in place of an output + /// pin. + #[inline] + pub fn into_peripheral_output(self) -> interconnect::OutputSignal { + self.pin.into_peripheral_output() } /// Set the output as high. @@ -1493,19 +1195,19 @@ where /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.get_output_level() == Level::High + self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - self.get_output_level() == Level::Low + self.output_level() == Level::Low } /// What level output is set to #[inline] - pub fn get_output_level(&self) -> Level { - self.pin.get_output_level() + pub fn output_level(&self) -> Level { + self.pin.output_level() } /// Toggle pin output @@ -1519,16 +1221,6 @@ where pub fn set_drive_strength(&mut self, strength: DriveStrength) { self.pin.set_drive_strength(strength); } - - /// Turns the pin object into a peripheral - /// [output][interconnect::OutputSignal]. - /// - /// The output signal can be passed to peripherals in place of an output - /// pin. - #[inline] - pub fn into_peripheral_output(self) -> interconnect::OutputSignal { - self.pin.into_peripheral_output() - } } /// GPIO input driver. @@ -1539,8 +1231,8 @@ pub struct Input<'d, P = AnyPin> { impl

private::Sealed for Input<'_, P> {} impl<'d, P> Peripheral for Input<'d, P> { - type P = P; - unsafe fn clone_unchecked(&self) -> P { + type P = Flex<'d, P>; + unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } @@ -1569,22 +1261,31 @@ where Self { pin } } + /// Returns a peripheral [input][interconnect::InputSignal] connected to + /// this pin. + /// + /// The input signal can be passed to peripherals in place of an input pin. + #[inline] + pub fn peripheral_input(&self) -> interconnect::InputSignal { + self.pin.peripheral_input() + } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.get_level() == Level::High + self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - self.get_level() == Level::Low + self.level() == Level::Low } /// Get the current pin input level. #[inline] - pub fn get_level(&self) -> Level { - self.pin.get_level() + pub fn level(&self) -> Level { + self.pin.level() } /// Listen for interrupts @@ -1618,14 +1319,28 @@ where pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { self.pin.wakeup_enable(enable, event); } +} - /// Returns a peripheral [input][interconnect::InputSignal] connected to - /// this pin. +impl

Input<'_, P> +where + P: InputPin + OutputPin, +{ + /// Split the pin into an input and output signal. /// - /// The input signal can be passed to peripherals in place of an input pin. + /// Peripheral signals allow connecting peripherals together without using + /// external hardware. + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + self.pin.split() + } + + /// Turns the pin object into a peripheral + /// [output][interconnect::OutputSignal]. + /// + /// The output signal can be passed to peripherals in place of an output + /// pin. #[inline] - pub fn peripheral_input(&self) -> interconnect::InputSignal { - self.pin.peripheral_input() + pub fn into_peripheral_output(self) -> interconnect::OutputSignal { + self.pin.into_peripheral_output() } } @@ -1637,8 +1352,8 @@ pub struct OutputOpenDrain<'d, P = AnyPin> { impl

private::Sealed for OutputOpenDrain<'_, P> {} impl<'d, P> Peripheral for OutputOpenDrain<'d, P> { - type P = P; - unsafe fn clone_unchecked(&self) -> P { + type P = Flex<'d, P>; + unsafe fn clone_unchecked(&self) -> Self::P { self.pin.clone_unchecked() } } @@ -1672,22 +1387,49 @@ where Self { pin } } + /// Split the pin into an input and output signal. + /// + /// Peripheral signals allow connecting peripherals together without using + /// external hardware. + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + self.pin.split() + } + + /// Returns a peripheral [input][interconnect::InputSignal] connected to + /// this pin. + /// + /// The input signal can be passed to peripherals in place of an input pin. + #[inline] + pub fn peripheral_input(&self) -> interconnect::InputSignal { + self.pin.peripheral_input() + } + + /// Turns the pin object into a peripheral + /// [output][interconnect::OutputSignal]. + /// + /// The output signal can be passed to peripherals in place of an output + /// pin. + #[inline] + pub fn into_peripheral_output(self) -> interconnect::OutputSignal { + self.pin.into_peripheral_output() + } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.get_level() == Level::High + self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - self.get_level() == Level::Low + self.level() == Level::Low } /// Get the current pin input level. #[inline] - pub fn get_level(&self) -> Level { - self.pin.get_level() + pub fn level(&self) -> Level { + self.pin.level() } /// Listen for interrupts @@ -1723,19 +1465,19 @@ where /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.get_output_level() == Level::High + self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - self.get_output_level() == Level::Low + self.output_level() == Level::Low } /// What level output is set to #[inline] - pub fn get_output_level(&self) -> Level { - self.pin.get_output_level() + pub fn output_level(&self) -> Level { + self.pin.output_level() } /// Toggle pin output @@ -1748,16 +1490,6 @@ where pub fn set_drive_strength(&mut self, strength: DriveStrength) { self.pin.set_drive_strength(strength); } - - /// Turns the pin object into a peripheral - /// [output][interconnect::OutputSignal]. - /// - /// The output signal can be passed to peripherals in place of an output - /// pin. - #[inline] - pub fn into_peripheral_output(self) -> interconnect::OutputSignal { - self.pin.into_peripheral_output() - } } /// Flexible pin driver. @@ -1768,9 +1500,11 @@ pub struct Flex<'d, P = AnyPin> { impl

private::Sealed for Flex<'_, P> {} impl<'d, P> Peripheral for Flex<'d, P> { - type P = P; - unsafe fn clone_unchecked(&self) -> P { - core::ptr::read(&*self.pin as *const _) + type P = Self; + unsafe fn clone_unchecked(&self) -> Self::P { + Self { + pin: PeripheralRef::new(core::ptr::read(&*self.pin as *const P)), + } } } @@ -1794,9 +1528,18 @@ where crate::into_ref!(pin); Self { pin } } + + /// Returns a peripheral [input][interconnect::InputSignal] connected to + /// this pin. + /// + /// The input signal can be passed to peripherals in place of an input pin. + #[inline] + pub fn peripheral_input(&self) -> interconnect::InputSignal { + self.pin.degrade_pin(private::Internal).split().0 + } } -impl<'d, P> Flex<'d, P> +impl

Flex<'_, P> where P: InputPin, { @@ -1809,18 +1552,18 @@ where /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - self.get_level() == Level::High + self.level() == Level::High } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - self.get_level() == Level::Low + self.level() == Level::Low } /// Get the current pin input level. #[inline] - pub fn get_level(&self) -> Level { + pub fn level(&self) -> Level { self.pin.is_input_high(private::Internal).into() } @@ -1884,18 +1627,9 @@ where pub fn wakeup_enable(&mut self, enable: bool, event: WakeEvent) { self.listen_with_options(event.into(), false, false, enable); } - - /// Returns a peripheral [input][interconnect::InputSignal] connected to - /// this pin. - /// - /// The input signal can be passed to peripherals in place of an input pin. - #[inline] - pub fn peripheral_input(&self) -> interconnect::InputSignal { - interconnect::InputSignal::new(self.pin.degrade_pin(private::Internal)) - } } -impl<'d, P> Flex<'d, P> +impl

Flex<'_, P> where P: OutputPin, { @@ -1925,25 +1659,25 @@ where /// Is the output pin set as high? #[inline] pub fn is_set_high(&self) -> bool { - self.get_output_level() == Level::High + self.output_level() == Level::High } /// Is the output pin set as low? #[inline] pub fn is_set_low(&self) -> bool { - self.get_output_level() == Level::Low + self.output_level() == Level::Low } /// What level output is set to #[inline] - pub fn get_output_level(&self) -> Level { + pub fn output_level(&self) -> Level { self.pin.is_set_high(private::Internal).into() } /// Toggle pin output #[inline] pub fn toggle(&mut self) { - let level = !self.get_output_level(); + let level = !self.output_level(); self.set_level(level); } @@ -1953,6 +1687,20 @@ where self.pin.set_drive_strength(strength, private::Internal); } + /// Set the GPIO to open-drain mode. + pub fn set_as_open_drain(&mut self, pull: Pull) { + self.pin.set_to_open_drain_output(private::Internal); + self.pin.pull_direction(pull, private::Internal); + } + + /// Split the pin into an input and output signal. + /// + /// Peripheral signals allow connecting peripherals together without using + /// external hardware. + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + self.pin.degrade_pin(private::Internal).split() + } + /// Turns the pin object into a peripheral /// [output][interconnect::OutputSignal]. /// @@ -1960,18 +1708,43 @@ where /// pin. #[inline] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { - interconnect::OutputSignal::new(self.pin.degrade_pin(private::Internal)) + self.split().1 } } -impl<'d, P> Flex<'d, P> -where - P: InputPin + OutputPin, -{ - /// Set the GPIO to open-drain mode. - pub fn set_as_open_drain(&mut self, pull: Pull) { - self.pin.set_to_open_drain_output(private::Internal); - self.pin.pull_direction(pull, private::Internal); +// Unfortunate implementation details responsible for: +// - making pin drivers work with the peripheral signal system +// - making the pin drivers work with the sleep API +impl Pin for Flex<'_, P> { + delegate::delegate! { + to self.pin { + fn number(&self) -> u8; + fn degrade_pin(&self, _internal: private::Internal) -> AnyPin; + fn output_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, OutputSignal)]; + fn input_signals(&self, _internal: private::Internal) -> &[(AlternateFunction, InputSignal)]; + fn gpio_bank(&self, _internal: private::Internal) -> GpioRegisterAccess; + } + } +} +impl RtcPin for Flex<'_, P> { + delegate::delegate! { + to self.pin { + #[cfg(xtensa)] + fn rtc_number(&self) -> u8; + #[cfg(any(xtensa, esp32c6))] + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: RtcFunction); + fn rtcio_pad_hold(&mut self, enable: bool); + #[cfg(any(esp32c3, esp32c2, esp32c6))] + unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8); + } + } +} +impl RtcPinWithResistors for Flex<'_, P> { + delegate::delegate! { + to self.pin { + fn rtcio_pullup(&mut self, enable: bool); + fn rtcio_pulldown(&mut self, enable: bool); + } } } @@ -1981,24 +1754,13 @@ pub(crate) mod internal { impl private::Sealed for AnyPin {} impl AnyPin { - /// Returns a peripheral [input][interconnect::InputSignal] connected to - /// this pin. - /// - /// The input signal can be passed to peripherals in place of an input - /// pin. - #[inline] - pub fn peripheral_input(&self) -> interconnect::InputSignal { - handle_gpio_input!(&self.0, target, { target.peripheral_input() }) - } - - /// Turns the pin object into a peripheral - /// [output][interconnect::OutputSignal]. + /// Split the pin into an input and output signal. /// - /// The output signal can be passed to peripherals in place of an output - /// pin. + /// Peripheral signals allow connecting peripherals together without + /// using external hardware. #[inline] - pub fn into_peripheral_output(self) -> interconnect::OutputSignal { - handle_gpio_output!(self.0, target, { target.into_peripheral_output() }) + pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { + handle_gpio_input!(self.0, target, { target.split() }) } } @@ -2024,7 +1786,7 @@ pub(crate) mod internal { } fn output_signals(&self, _: private::Internal) -> &[(AlternateFunction, OutputSignal)] { - handle_gpio_input!(&self.0, target, { + handle_gpio_output!(&self.0, target, { Pin::output_signals(target, private::Internal) }) } @@ -2043,7 +1805,75 @@ pub(crate) mod internal { } impl InputPin for AnyPin {} - impl OutputPin for AnyPin {} + + // Need to forward these one by one because not all pins support all functions. + impl OutputPin for AnyPin { + fn init_output( + &mut self, + alternate: AlternateFunction, + open_drain: bool, + input_enable: Option, + _: private::Internal, + ) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::init_output( + target, + alternate, + open_drain, + input_enable, + private::Internal, + ) + }) + } + + fn set_to_open_drain_output(&mut self, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::set_to_open_drain_output(target, private::Internal) + }) + } + + fn set_to_push_pull_output(&mut self, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::set_to_push_pull_output(target, private::Internal) + }) + } + + fn set_output_high(&mut self, high: bool, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::set_output_high(target, high, private::Internal) + }) + } + + fn set_drive_strength(&mut self, strength: DriveStrength, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::set_drive_strength(target, strength, private::Internal) + }) + } + + fn enable_open_drain(&mut self, on: bool, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::enable_open_drain(target, on, private::Internal) + }) + } + + fn internal_pull_up_in_sleep_mode(&mut self, on: bool, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::internal_pull_up_in_sleep_mode(target, on, private::Internal) + }) + } + + fn internal_pull_down_in_sleep_mode(&mut self, on: bool, _: private::Internal) { + handle_gpio_output!(&mut self.0, target, { + OutputPin::internal_pull_down_in_sleep_mode(target, on, private::Internal) + }) + } + + fn is_set_high(&self, _: private::Internal) -> bool { + handle_gpio_output!(&self.0, target, { + OutputPin::is_set_high(target, private::Internal) + }) + } + } #[cfg(any(xtensa, esp32c2, esp32c3, esp32c6))] impl RtcPin for AnyPin { @@ -2100,12 +1930,13 @@ fn is_listening(pin_num: u8) -> bool { } fn set_int_enable(gpio_num: u8, int_ena: u8, int_type: u8, wake_up_from_light_sleep: bool) { - let gpio = unsafe { GPIO::steal() }; - gpio.pin(gpio_num as usize).modify(|_, w| unsafe { - w.int_ena().bits(int_ena); - w.int_type().bits(int_type); - w.wakeup_enable().bit(wake_up_from_light_sleep) - }); + unsafe { GPIO::steal() } + .pin(gpio_num as usize) + .modify(|_, w| unsafe { + w.int_ena().bits(int_ena); + w.int_type().bits(int_type); + w.wakeup_enable().bit(wake_up_from_light_sleep) + }); } #[ram] @@ -2148,7 +1979,7 @@ mod asynch { pub(super) static PIN_WAKERS: [AtomicWaker; NUM_PINS] = [const { AtomicWaker::new() }; NUM_PINS]; - impl<'d, P> Flex<'d, P> + impl

Flex<'_, P> where P: InputPin, { @@ -2185,7 +2016,7 @@ mod asynch { } } - impl<'d, P> Input<'d, P> + impl

Input<'_, P> where P: InputPin, { @@ -2250,7 +2081,7 @@ mod embedded_hal_02_impls { use super::*; - impl<'d, P> digital::InputPin for Input<'d, P> + impl

digital::InputPin for Input<'_, P> where P: InputPin, { @@ -2264,7 +2095,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::OutputPin for Output<'d, P> + impl

digital::OutputPin for Output<'_, P> where P: OutputPin, { @@ -2280,7 +2111,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::StatefulOutputPin for Output<'d, P> + impl

digital::StatefulOutputPin for Output<'_, P> where P: OutputPin, { @@ -2292,7 +2123,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::ToggleableOutputPin for Output<'d, P> + impl

digital::ToggleableOutputPin for Output<'_, P> where P: OutputPin, { @@ -2304,7 +2135,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::InputPin for OutputOpenDrain<'d, P> + impl

digital::InputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2318,7 +2149,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::OutputPin for OutputOpenDrain<'d, P> + impl

digital::OutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2335,7 +2166,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::StatefulOutputPin for OutputOpenDrain<'d, P> + impl

digital::StatefulOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2347,7 +2178,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::ToggleableOutputPin for OutputOpenDrain<'d, P> + impl

digital::ToggleableOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2359,7 +2190,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::InputPin for Flex<'d, P> + impl

digital::InputPin for Flex<'_, P> where P: InputPin + OutputPin, { @@ -2373,7 +2204,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::OutputPin for Flex<'d, P> + impl

digital::OutputPin for Flex<'_, P> where P: InputPin + OutputPin, { @@ -2389,7 +2220,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::StatefulOutputPin for Flex<'d, P> + impl

digital::StatefulOutputPin for Flex<'_, P> where P: InputPin + OutputPin, { @@ -2401,7 +2232,7 @@ mod embedded_hal_02_impls { } } - impl<'d, P> digital::ToggleableOutputPin for Flex<'d, P> + impl

digital::ToggleableOutputPin for Flex<'_, P> where P: InputPin + OutputPin, { @@ -2419,14 +2250,14 @@ mod embedded_hal_impls { use super::*; - impl<'d, P> digital::ErrorType for Input<'d, P> + impl

digital::ErrorType for Input<'_, P> where P: InputPin, { type Error = core::convert::Infallible; } - impl<'d, P> digital::InputPin for Input<'d, P> + impl

digital::InputPin for Input<'_, P> where P: InputPin, { @@ -2439,14 +2270,14 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::ErrorType for Output<'d, P> + impl

digital::ErrorType for Output<'_, P> where P: OutputPin, { type Error = core::convert::Infallible; } - impl<'d, P> digital::OutputPin for Output<'d, P> + impl

digital::OutputPin for Output<'_, P> where P: OutputPin, { @@ -2461,7 +2292,7 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::StatefulOutputPin for Output<'d, P> + impl

digital::StatefulOutputPin for Output<'_, P> where P: OutputPin, { @@ -2474,7 +2305,7 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::InputPin for OutputOpenDrain<'d, P> + impl

digital::InputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2487,14 +2318,14 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::ErrorType for OutputOpenDrain<'d, P> + impl

digital::ErrorType for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { type Error = core::convert::Infallible; } - impl<'d, P> digital::OutputPin for OutputOpenDrain<'d, P> + impl

digital::OutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2509,7 +2340,7 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::StatefulOutputPin for OutputOpenDrain<'d, P> + impl

digital::StatefulOutputPin for OutputOpenDrain<'_, P> where P: InputPin + OutputPin, { @@ -2522,7 +2353,7 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::InputPin for Flex<'d, P> + impl

digital::InputPin for Flex<'_, P> where P: InputPin, { @@ -2535,11 +2366,11 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::ErrorType for Flex<'d, P> { + impl

digital::ErrorType for Flex<'_, P> { type Error = core::convert::Infallible; } - impl<'d, P> digital::OutputPin for Flex<'d, P> + impl

digital::OutputPin for Flex<'_, P> where P: OutputPin, { @@ -2554,7 +2385,7 @@ mod embedded_hal_impls { } } - impl<'d, P> digital::StatefulOutputPin for Flex<'d, P> + impl

digital::StatefulOutputPin for Flex<'_, P> where P: OutputPin, { @@ -2573,7 +2404,7 @@ mod embedded_hal_async_impls { use super::*; - impl<'d, P> Wait for Flex<'d, P> + impl

Wait for Flex<'_, P> where P: InputPin, { @@ -2603,7 +2434,7 @@ mod embedded_hal_async_impls { } } - impl<'d, P> Wait for Input<'d, P> + impl

Wait for Input<'_, P> where P: InputPin, { diff --git a/esp-hal/src/gpio/placeholder.rs b/esp-hal/src/gpio/placeholder.rs index 84c918fce76..bf269c4d262 100644 --- a/esp-hal/src/gpio/placeholder.rs +++ b/esp-hal/src/gpio/placeholder.rs @@ -6,6 +6,7 @@ // polluting the main module. use super::*; +use crate::gpio::interconnect::connect_input_signal; impl crate::peripheral::Peripheral for Level { type P = Self; @@ -29,45 +30,26 @@ impl Level { pub(crate) fn enable_input(&mut self, _on: bool, _: private::Internal) {} - pub(crate) fn enable_input_in_sleep_mode(&mut self, _on: bool, _: private::Internal) {} - pub(crate) fn is_input_high(&self, _: private::Internal) -> bool { *self == Level::High } #[doc(hidden)] - pub(crate) fn connect_input_to_peripheral( - &mut self, - signal: InputSignal, - _: private::Internal, - ) { + pub(crate) fn connect_input_to_peripheral(&mut self, signal: InputSignal) { let value = match self { Level::High => ONE_INPUT, Level::Low => ZERO_INPUT, }; - unsafe { GPIO::steal() } - .func_in_sel_cfg(signal as usize - FUNC_IN_SEL_OFFSET) - .modify(|_, w| unsafe { - w.sel().set_bit(); - w.in_inv_sel().bit(false); - w.in_sel().bits(value) - }); + connect_input_signal(signal, value, false, true); } - pub(crate) fn disconnect_input_from_peripheral( - &mut self, - _signal: InputSignal, - _: private::Internal, - ) { - } pub(crate) fn set_to_open_drain_output(&mut self, _: private::Internal) {} pub(crate) fn set_to_push_pull_output(&mut self, _: private::Internal) {} pub(crate) fn enable_output(&mut self, _on: bool, _: private::Internal) {} pub(crate) fn set_output_high(&mut self, _on: bool, _: private::Internal) {} pub(crate) fn set_drive_strength(&mut self, _strength: DriveStrength, _: private::Internal) {} pub(crate) fn enable_open_drain(&mut self, _on: bool, _: private::Internal) {} - pub(crate) fn enable_output_in_sleep_mode(&mut self, _on: bool, _: private::Internal) {} pub(crate) fn internal_pull_up_in_sleep_mode(&mut self, _on: bool, _: private::Internal) {} pub(crate) fn internal_pull_down_in_sleep_mode(&mut self, _on: bool, _: private::Internal) {} @@ -82,19 +64,9 @@ impl Level { &[] } - pub(crate) fn connect_peripheral_to_output( - &mut self, - _signal: OutputSignal, - _: private::Internal, - ) { - } + pub(crate) fn connect_peripheral_to_output(&mut self, _signal: OutputSignal) {} - pub(crate) fn disconnect_from_peripheral_output( - &mut self, - _signal: OutputSignal, - _: private::Internal, - ) { - } + pub(crate) fn disconnect_from_peripheral_output(&mut self, _signal: OutputSignal) {} } /// Placeholder pin, used when no pin is required when using a peripheral. diff --git a/esp-hal/src/gpio/rtc_io.rs b/esp-hal/src/gpio/rtc_io.rs index 2eb87a1714e..beebc37e3d4 100644 --- a/esp-hal/src/gpio/rtc_io.rs +++ b/esp-hal/src/gpio/rtc_io.rs @@ -23,10 +23,8 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::gpio::rtc_io::LowPowerOutput; -//! # use esp_hal::gpio::Io; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! // configure GPIO 1 as ULP output pin -//! let lp_pin = LowPowerOutput::<'static, 1>::new(io.pins.gpio1); +//! let lp_pin = LowPowerOutput::<'static, 1>::new(peripherals.GPIO1); //! # } //! ``` diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c.rs deleted file mode 100644 index 9eb4219e3f4..00000000000 --- a/esp-hal/src/i2c.rs +++ /dev/null @@ -1,2713 +0,0 @@ -//! # Inter-Integrated Circuit (I2C) -//! -//! I2C is a serial, synchronous, multi-device, half-duplex communication -//! protocol that allows co-existence of multiple masters and slaves on the -//! same bus. I2C uses two bidirectional open-drain lines: serial data line -//! (SDA) and serial clock line (SCL), pulled up by resistors. -//! -//! Espressif devices sometimes have more than one I2C controller, responsible -//! for handling communication on the I2C bus. A single I2C controller can be -//! a master or a slave. -//! -//! Typically, an I2C slave device has a 7-bit address or 10-bit address. -//! Devices supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can -//! go up to 100KHz and 400KHz respectively. -//! -//! ## Configuration -//! -//! Each I2C controller is individually configurable, and the usual setting -//! such as frequency, timeout, and SDA/SCL pins can easily be configured. -//! -//! ## Usage -//! -//! The I2C driver implements a number of third-party traits, with the -//! intention of making the HAL inter-compatible with various device drivers -//! from the community. This includes the [`embedded-hal`] for both 0.2.x and -//! 1.0.x versions. -//! -//! ## Examples -//! -//! ### Read Data from a BMP180 Sensor -//! -//! ```rust, no_run -#![doc = crate::before_snippet!()] -//! # use esp_hal::i2c::I2c; -//! # use esp_hal::gpio::Io; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! -//! // Create a new peripheral object with the described wiring -//! // and standard I2C clock speed. -//! let mut i2c = I2c::new( -//! peripherals.I2C0, -//! io.pins.gpio1, -//! io.pins.gpio2, -//! 100.kHz(), -//! ); -//! -//! loop { -//! let mut data = [0u8; 22]; -//! i2c.write_read(0x77, &[0xaa], &mut data).ok(); -//! } -//! # } -//! ``` -//! [`embedded-hal`]: https://crates.io/crates/embedded-hal - -use core::marker::PhantomData; - -use fugit::HertzU32; - -use crate::{ - clock::Clocks, - dma::PeripheralMarker, - gpio::{interconnect::PeripheralOutput, InputSignal, OutputSignal, Pull}, - interrupt::InterruptHandler, - peripheral::{Peripheral, PeripheralRef}, - peripherals::i2c0::{RegisterBlock, COMD}, - system::PeripheralClockControl, - Async, - Blocking, - InterruptConfigurable, - Mode, -}; - -cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - const I2C_LL_INTR_MASK: u32 = 0x1ffff; - } else { - const I2C_LL_INTR_MASK: u32 = 0x3ffff; - } -} - -// Chunk writes/reads by this size -#[cfg(any(esp32, esp32s2))] -const I2C_CHUNK_SIZE: usize = 32; - -#[cfg(not(any(esp32, esp32s2)))] -const I2C_CHUNK_SIZE: usize = 254; - -// on ESP32 there is a chance to get trapped in `wait_for_completion` forever -const MAX_ITERATIONS: u32 = 1_000_000; - -/// I2C-specific transmission errors -#[derive(Debug, Clone, Copy, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// The transmission exceeded the FIFO size. - ExceedingFifo, - /// The acknowledgment check failed. - AckCheckFailed, - /// A timeout occurred during transmission. - TimeOut, - /// The arbitration for the bus was lost. - ArbitrationLost, - /// The execution of the I2C command was incomplete. - ExecIncomplete, - /// The number of commands issued exceeded the limit. - CommandNrExceeded, - /// Zero length read or write operation. - InvalidZeroLength, -} - -#[derive(PartialEq)] -// This enum is used to keep track of the last/next operation that was/will be -// performed in an embedded-hal(-async) I2c::transaction. It is used to -// determine whether a START condition should be issued at the start of the -// current operation and whether a read needs an ack or a nack for the final -// byte. -enum OpKind { - Write, - Read, -} - -/// I2C operation. -/// -/// Several operations can be combined as part of a transaction. -pub enum Operation<'a> { - /// Write data from the provided buffer. - Write(&'a [u8]), - - /// Read data into the provided buffer. - Read(&'a mut [u8]), -} - -impl<'a, 'b> From<&'a mut embedded_hal::i2c::Operation<'b>> for Operation<'a> { - fn from(value: &'a mut embedded_hal::i2c::Operation<'b>) -> Self { - match value { - embedded_hal::i2c::Operation::Write(buffer) => Operation::Write(buffer), - embedded_hal::i2c::Operation::Read(buffer) => Operation::Read(buffer), - } - } -} - -impl<'a, 'b> From<&'a mut Operation<'b>> for Operation<'a> { - fn from(value: &'a mut Operation<'b>) -> Self { - match value { - Operation::Write(buffer) => Operation::Write(buffer), - Operation::Read(buffer) => Operation::Read(buffer), - } - } -} - -impl Operation<'_> { - fn is_write(&self) -> bool { - matches!(self, Operation::Write(_)) - } - - fn kind(&self) -> OpKind { - match self { - Operation::Write(_) => OpKind::Write, - Operation::Read(_) => OpKind::Read, - } - } - - fn is_empty(&self) -> bool { - match self { - Operation::Write(buffer) => buffer.is_empty(), - Operation::Read(buffer) => buffer.is_empty(), - } - } -} - -impl embedded_hal::i2c::Error for Error { - fn kind(&self) -> embedded_hal::i2c::ErrorKind { - use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource}; - - match self { - Self::ExceedingFifo => ErrorKind::Overrun, - Self::ArbitrationLost => ErrorKind::ArbitrationLoss, - Self::AckCheckFailed => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown), - _ => ErrorKind::Other, - } - } -} - -/// A generic I2C Command -#[cfg_attr(feature = "debug", derive(Debug))] -enum Command { - Start, - Stop, - End, - Write { - /// This bit is to set an expected ACK value for the transmitter. - ack_exp: Ack, - /// Enables checking the ACK value received against the ack_exp value. - ack_check_en: bool, - /// Length of data (in bytes) to be written. The maximum length is 255, - /// while the minimum is 1. - length: u8, - }, - Read { - /// Indicates whether the receiver will send an ACK after this byte has - /// been received. - ack_value: Ack, - /// Length of data (in bytes) to be read. The maximum length is 255, - /// while the minimum is 1. - length: u8, - }, -} - -enum OperationType { - Write = 0, - Read = 1, -} - -#[derive(Eq, PartialEq, Copy, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -enum Ack { - Ack = 0, - Nack = 1, -} -impl From for Ack { - fn from(ack: u32) -> Self { - match ack { - 0 => Ack::Ack, - 1 => Ack::Nack, - _ => unreachable!(), - } - } -} -impl From for u32 { - fn from(ack: Ack) -> u32 { - ack as u32 - } -} - -/// I2C driver -pub struct I2c<'d, DM: Mode, T = AnyI2c> { - i2c: PeripheralRef<'d, T>, - phantom: PhantomData, - frequency: HertzU32, - timeout: Option, -} - -impl I2c<'_, DM, T> -where - T: Instance, - DM: Mode, -{ - fn transaction_impl<'a>( - &mut self, - address: u8, - operations: impl Iterator>, - ) -> Result<(), Error> { - let mut last_op: Option = None; - // filter out 0 length read operations - let mut op_iter = operations - .filter(|op| op.is_write() || !op.is_empty()) - .peekable(); - - while let Some(op) = op_iter.next() { - let next_op = op_iter.peek().map(|v| v.kind()); - let kind = op.kind(); - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - match op { - Operation::Write(buffer) => { - // execute a write operation: - // - issue START/RSTART if op is different from previous - // - issue STOP if op is the last one - self.i2c - .write_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Write)), - next_op.is_none(), - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - Operation::Read(buffer) => { - // execute a read operation: - // - issue START/RSTART if op is different from previous - // - issue STOP if op is the last one - // - will_continue is true if there is another read operation next - self.i2c - .read_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Read)), - next_op.is_none(), - matches!(next_op, Some(OpKind::Read)), - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - } - - last_op = Some(kind); - } - - Ok(()) - } -} - -impl I2c<'_, Blocking, T> -where - T: Instance, -{ - /// Reads enough bytes from slave with `address` to fill `buffer` - pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); - for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.i2c - .read_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - idx < chunk_count - 1, - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - - Ok(()) - } - - /// Writes bytes to slave with address `address` - pub fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); - for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.i2c - .write_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - - Ok(()) - } - - /// Writes bytes to slave with address `address` and then reads enough bytes - /// to fill `buffer` *in a single transaction* - pub fn write_read( - &mut self, - address: u8, - write_buffer: &[u8], - read_buffer: &mut [u8], - ) -> Result<(), Error> { - let write_count = write_buffer.len().div_ceil(I2C_CHUNK_SIZE); - let read_count = read_buffer.len().div_ceil(I2C_CHUNK_SIZE); - - for (idx, chunk) in write_buffer.chunks(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.i2c - .write_operation( - address, - chunk, - idx == 0, - idx == write_count - 1 && read_count == 0, - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - - for (idx, chunk) in read_buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.i2c - .read_operation( - address, - chunk, - idx == 0, - idx == read_count - 1, - idx < read_count - 1, - cmd_iterator, - ) - .inspect_err(|_| self.internal_recover())?; - } - - Ok(()) - } - - /// Execute the provided operations on the I2C bus. - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. This - /// is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each - /// other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W is - /// sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an - /// acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 - /// to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - pub fn transaction<'a>( - &mut self, - address: u8, - operations: impl IntoIterator>, - ) -> Result<(), Error> { - self.transaction_impl(address, operations.into_iter().map(Operation::from)) - } -} - -impl embedded_hal_02::blocking::i2c::Read for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, buffer) - } -} - -impl embedded_hal_02::blocking::i2c::Write for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - self.write(addr, bytes) - } -} - -impl embedded_hal_02::blocking::i2c::WriteRead for I2c<'_, Blocking, T> -where - T: Instance, -{ - type Error = Error; - - fn write_read( - &mut self, - address: u8, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - self.write_read(address, bytes, buffer) - } -} - -impl embedded_hal::i2c::ErrorType for I2c<'_, DM, T> { - type Error = Error; -} - -impl embedded_hal::i2c::I2c for I2c<'_, DM, T> -where - T: Instance, -{ - fn transaction( - &mut self, - address: u8, - operations: &mut [embedded_hal::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - self.transaction_impl(address, operations.iter_mut().map(Operation::from)) - } -} - -impl<'d, T, DM: Mode> I2c<'d, DM, T> -where - T: Instance, -{ - fn new_internal( - i2c: impl Peripheral

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Self { - crate::into_ref!(i2c); - crate::into_mapped_ref!(sda, scl); - - let i2c = I2c { - i2c, - phantom: PhantomData, - frequency, - timeout: None, - }; - - PeripheralClockControl::reset(i2c.i2c.peripheral()); - PeripheralClockControl::enable(i2c.i2c.peripheral()); - - // TODO: implement with_pins et. al. - // avoid SCL/SDA going low during configuration - scl.set_output_high(true, crate::private::Internal); - sda.set_output_high(true, crate::private::Internal); - - scl.set_to_open_drain_output(crate::private::Internal); - scl.enable_input(true, crate::private::Internal); - scl.pull_direction(Pull::Up, crate::private::Internal); - - scl.connect_input_to_peripheral(i2c.i2c.scl_input_signal(), crate::private::Internal); - scl.connect_peripheral_to_output(i2c.i2c.scl_output_signal(), crate::private::Internal); - - sda.set_to_open_drain_output(crate::private::Internal); - sda.enable_input(true, crate::private::Internal); - sda.pull_direction(Pull::Up, crate::private::Internal); - - sda.connect_input_to_peripheral(i2c.i2c.sda_input_signal(), crate::private::Internal); - sda.connect_peripheral_to_output(i2c.i2c.sda_output_signal(), crate::private::Internal); - - i2c.i2c.setup(frequency, None); - i2c - } - - fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { crate::interrupt::bind_interrupt(self.i2c.interrupt(), handler.handler()) }; - unwrap!(crate::interrupt::enable( - self.i2c.interrupt(), - handler.priority() - )); - } - - fn internal_recover(&self) { - PeripheralClockControl::reset(self.i2c.peripheral()); - PeripheralClockControl::enable(self.i2c.peripheral()); - - self.i2c.setup(self.frequency, self.timeout); - } - - /// Set the I2C timeout. - // TODO: explain this function better - what's the unit, what happens on - // timeout, and just what exactly is a timeout in this context? - pub fn with_timeout(mut self, timeout: Option) -> Self { - self.timeout = timeout; - self.i2c.setup(self.frequency, self.timeout); - self - } -} - -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

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Self { - Self::new_typed(i2c.map_into(), sda, scl, frequency) - } -} - -impl<'d, T> I2c<'d, Blocking, T> -where - T: Instance, -{ - /// 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

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Self { - Self::new_internal(i2c, sda, scl, frequency) - } -} - -impl<'d, T> crate::private::Sealed for I2c<'d, Blocking, T> where T: Instance {} - -impl<'d, T> InterruptConfigurable for I2c<'d, Blocking, T> -where - T: Instance, -{ - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - self.internal_set_interrupt_handler(handler); - } -} - -impl<'d> I2c<'d, Async> { - /// 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_async( - i2c: impl Peripheral

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Self { - Self::new_async_typed(i2c.map_into(), sda, scl, frequency) - } -} - -impl<'d, T> I2c<'d, Async, T> -where - T: Instance, -{ - /// 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_async_typed( - i2c: impl Peripheral

+ 'd, - sda: impl Peripheral

+ 'd, - scl: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Self { - let mut this = Self::new_internal(i2c, sda, scl, frequency); - - this.internal_set_interrupt_handler(this.i2c.async_handler()); - - this - } -} - -mod asynch { - #[cfg(not(esp32))] - use core::{ - pin::Pin, - task::{Context, Poll}, - }; - - use embassy_sync::waitqueue::AtomicWaker; - use embedded_hal::i2c::Operation as EhalOperation; - use procmacros::handler; - - use super::*; - - const NUM_I2C: usize = 1 + cfg!(i2c1) as usize; - static WAKERS: [AtomicWaker; NUM_I2C] = [const { AtomicWaker::new() }; NUM_I2C]; - - #[cfg_attr(esp32, allow(dead_code))] - pub(crate) enum Event { - EndDetect, - TxComplete, - #[cfg(not(any(esp32, esp32s2)))] - TxFifoWatermark, - } - - #[cfg(not(esp32))] - #[must_use = "futures do nothing unless you `.await` or poll them"] - struct I2cFuture<'a, T> - where - T: Instance, - { - event: Event, - instance: &'a T, - } - - #[cfg(not(esp32))] - impl<'a, T> I2cFuture<'a, T> - where - T: Instance, - { - pub fn new(event: Event, instance: &'a T) -> Self { - instance.register_block().int_ena().modify(|_, w| { - let w = match event { - Event::EndDetect => w.end_detect().set_bit(), - Event::TxComplete => w.trans_complete().set_bit(), - #[cfg(not(any(esp32, esp32s2)))] - Event::TxFifoWatermark => w.txfifo_wm().set_bit(), - }; - - w.arbitration_lost().set_bit(); - w.time_out().set_bit(); - - #[cfg(esp32)] - w.ack_err().set_bit(); - #[cfg(not(esp32))] - w.nack().set_bit(); - - w - }); - - Self { event, instance } - } - - fn event_bit_is_clear(&self) -> bool { - let r = self.instance.register_block().int_ena().read(); - - match self.event { - Event::EndDetect => r.end_detect().bit_is_clear(), - Event::TxComplete => r.trans_complete().bit_is_clear(), - #[cfg(not(any(esp32, esp32s2)))] - Event::TxFifoWatermark => r.txfifo_wm().bit_is_clear(), - } - } - - fn check_error(&self) -> Result<(), Error> { - let r = self.instance.register_block().int_raw().read(); - - if r.arbitration_lost().bit_is_set() { - return Err(Error::ArbitrationLost); - } - - if r.time_out().bit_is_set() { - return Err(Error::TimeOut); - } - - #[cfg(not(esp32))] - if r.nack().bit_is_set() { - return Err(Error::AckCheckFailed); - } - - #[cfg(esp32)] - if r.ack_err().bit_is_set() { - return Err(Error::AckCheckFailed); - } - - #[cfg(not(esp32))] - if r.trans_complete().bit_is_set() - && self - .instance - .register_block() - .sr() - .read() - .resp_rec() - .bit_is_clear() - { - return Err(Error::AckCheckFailed); - } - - Ok(()) - } - } - - #[cfg(not(esp32))] - impl<'a, T> core::future::Future for I2cFuture<'a, T> - where - T: Instance, - { - type Output = Result<(), Error>; - - fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - WAKERS[self.instance.i2c_number()].register(ctx.waker()); - - let error = self.check_error(); - - if error.is_err() { - return Poll::Ready(error); - } - - if self.event_bit_is_clear() { - Poll::Ready(Ok(())) - } else { - Poll::Pending - } - } - } - - impl I2c<'_, Async, T> - where - T: Instance, - { - #[cfg(any(esp32, esp32s2))] - async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - if buffer.len() > 32 { - panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); - } - - self.wait_for_completion(false).await?; - - for byte in buffer.iter_mut() { - *byte = read_fifo(self.i2c.register_block()); - } - - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - self.i2c.read_all_from_fifo(buffer) - } - - #[cfg(any(esp32, esp32s2))] - async fn write_remaining_tx_fifo( - &self, - start_index: usize, - bytes: &[u8], - ) -> Result<(), Error> { - if start_index >= bytes.len() { - return Ok(()); - } - - for b in bytes { - write_fifo(self.i2c.register_block(), *b); - self.i2c.check_errors()?; - } - - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - async fn write_remaining_tx_fifo( - &self, - start_index: usize, - bytes: &[u8], - ) -> Result<(), Error> { - let mut index = start_index; - loop { - self.i2c.check_errors()?; - - I2cFuture::new(Event::TxFifoWatermark, &*self.i2c).await?; - - self.i2c - .register_block() - .int_clr() - .write(|w| w.txfifo_wm().clear_bit_by_one()); - - I2cFuture::new(Event::TxFifoWatermark, &*self.i2c).await?; - - if index >= bytes.len() { - break Ok(()); - } - - write_fifo(self.i2c.register_block(), bytes[index]); - index += 1; - } - } - - #[cfg(not(esp32))] - async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { - self.i2c.check_errors()?; - - if end_only { - I2cFuture::new(Event::EndDetect, &*self.i2c).await?; - } else { - let res = embassy_futures::select::select( - I2cFuture::new(Event::TxComplete, &*self.i2c), - I2cFuture::new(Event::EndDetect, &*self.i2c), - ) - .await; - - match res { - embassy_futures::select::Either::First(res) => res?, - embassy_futures::select::Either::Second(res) => res?, - } - } - self.i2c.check_all_commands_done()?; - - Ok(()) - } - - #[cfg(esp32)] - async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { - // for ESP32 we need a timeout here but wasting a timer seems unnecessary - // given the short time we spend here - - let mut tout = MAX_ITERATIONS / 10; // adjust the timeout because we are yielding in the loop - loop { - let interrupts = self.i2c.register_block().int_raw().read(); - - self.i2c.check_errors()?; - - // Handle completion cases - // A full transmission was completed (either a STOP condition or END was - // processed) - if (!end_only && interrupts.trans_complete().bit_is_set()) - || interrupts.end_detect().bit_is_set() - { - break; - } - - tout -= 1; - if tout == 0 { - return Err(Error::TimeOut); - } - - embassy_futures::yield_now().await; - } - self.i2c.check_all_commands_done()?; - Ok(()) - } - - /// Executes an async I2C write operation. - /// - `addr` is the address of the slave device. - /// - `bytes` is the data two be sent. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `cmd_iterator` is an iterator over the command registers. - async fn write_operation<'a, I>( - &self, - address: u8, - bytes: &[u8], - start: bool, - stop: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length writes without start or end as that would be an - // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 - if bytes.is_empty() && !start && !stop { - return Ok(()); - } - - // Reset FIFO and command list - self.i2c.reset_fifo(); - self.i2c.reset_command_list(); - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - self.i2c.setup_write(address, bytes, start, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - let index = self.i2c.fill_tx_fifo(bytes); - self.i2c.start_transmission(); - - // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes).await?; - self.wait_for_completion(!stop).await?; - Ok(()) - } - - /// Executes an async I2C read operation. - /// - `addr` is the address of the slave device. - /// - `buffer` is the buffer to store the read data. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `will_continue` indicates whether there is another read operation - /// following this one and we should not nack the last byte. - /// - `cmd_iterator` is an iterator over the command registers. - async fn read_operation<'a, I>( - &self, - address: u8, - buffer: &mut [u8], - start: bool, - stop: bool, - will_continue: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length reads as that would be an invalid operation - // read lengths in the TRM (at least for ESP32-S3) are 1-255 - if buffer.is_empty() { - return Ok(()); - } - - // Reset FIFO and command list - self.i2c.reset_fifo(); - self.i2c.reset_command_list(); - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - self.i2c - .setup_read(address, buffer, start, will_continue, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - self.i2c.start_transmission(); - self.read_all_from_fifo(buffer).await?; - self.wait_for_completion(!stop).await?; - Ok(()) - } - - /// Writes bytes to slave with address `address` - pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); - for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.write_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - cmd_iterator, - ) - .await?; - } - - Ok(()) - } - - /// Reads enough bytes from slave with `address` to fill `buffer` - pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); - for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.read_operation( - address, - chunk, - idx == 0, - idx == chunk_count - 1, - idx < chunk_count - 1, - cmd_iterator, - ) - .await?; - } - - Ok(()) - } - - /// Writes bytes to slave with address `address` and then reads enough - /// bytes to fill `buffer` *in a single transaction* - pub async fn write_read( - &mut self, - address: u8, - write_buffer: &[u8], - read_buffer: &mut [u8], - ) -> Result<(), Error> { - let write_count = write_buffer.len().div_ceil(I2C_CHUNK_SIZE); - let read_count = read_buffer.len().div_ceil(I2C_CHUNK_SIZE); - for (idx, chunk) in write_buffer.chunks(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.write_operation( - address, - chunk, - idx == 0, - idx == write_count - 1 && read_count == 0, - cmd_iterator, - ) - .await?; - } - - for (idx, chunk) in read_buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - - self.read_operation( - address, - chunk, - idx == 0, - idx == read_count - 1, - idx < read_count - 1, - cmd_iterator, - ) - .await?; - } - - Ok(()) - } - - /// Execute the provided operations on the I2C bus as a single - /// transaction. - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. - /// This is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each - /// other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W - /// is sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an - /// acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or - /// 0 to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - pub async fn transaction<'a>( - &mut self, - address: u8, - operations: impl IntoIterator>, - ) -> Result<(), Error> { - self.transaction_impl_async(address, operations.into_iter().map(Operation::from)) - .await - } - - async fn transaction_impl_async<'a>( - &mut self, - address: u8, - operations: impl Iterator>, - ) -> Result<(), Error> { - let mut last_op: Option = None; - // filter out 0 length read operations - let mut op_iter = operations - .filter(|op| op.is_write() || !op.is_empty()) - .peekable(); - - while let Some(op) = op_iter.next() { - let next_op = op_iter.peek().map(|v| v.kind()); - let kind = op.kind(); - // Clear all I2C interrupts - self.i2c.clear_all_interrupts(); - - let cmd_iterator = &mut self.i2c.register_block().comd_iter(); - match op { - Operation::Write(buffer) => { - // execute a write operation: - // - issue START/RSTART if op is different from previous - // - issue STOP if op is the last one - self.write_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Write)), - next_op.is_none(), - cmd_iterator, - ) - .await?; - } - Operation::Read(buffer) => { - // execute a read operation: - // - issue START/RSTART if op is different from previous - // - issue STOP if op is the last one - // - will_continue is true if there is another read operation next - self.read_operation( - address, - buffer, - !matches!(last_op, Some(OpKind::Read)), - next_op.is_none(), - matches!(next_op, Some(OpKind::Read)), - cmd_iterator, - ) - .await?; - } - } - - last_op = Some(kind); - } - - Ok(()) - } - } - - impl<'d, T> embedded_hal_async::i2c::I2c for I2c<'d, Async, T> - where - T: Instance, - { - async fn transaction( - &mut self, - address: u8, - operations: &mut [EhalOperation<'_>], - ) -> Result<(), Self::Error> { - self.transaction_impl_async(address, operations.iter_mut().map(Operation::from)) - .await - } - } - - fn handler(regs: &RegisterBlock) { - regs.int_ena().modify(|_, w| { - w.end_detect().clear_bit(); - w.trans_complete().clear_bit(); - w.arbitration_lost().clear_bit(); - w.time_out().clear_bit() - }); - - #[cfg(not(any(esp32, esp32s2)))] - regs.int_ena().modify(|_, w| w.txfifo_wm().clear_bit()); - - #[cfg(not(esp32))] - regs.int_ena().modify(|_, w| w.nack().clear_bit()); - - #[cfg(esp32)] - regs.int_ena().modify(|_, w| w.ack_err().clear_bit()); - } - - #[handler] - pub(super) fn i2c0_handler() { - let regs = unsafe { &*crate::peripherals::I2C0::PTR }; - handler(regs); - - WAKERS[0].wake(); - } - - #[cfg(i2c1)] - #[handler] - pub(super) fn i2c1_handler() { - let regs = unsafe { &*crate::peripherals::I2C1::PTR }; - handler(regs); - - WAKERS[1].wake(); - } -} - -/// I2C Peripheral Instance -#[doc(hidden)] -pub trait Instance: Peripheral

+ PeripheralMarker + Into + 'static { - /// Returns the interrupt associated with this I2C peripheral. - fn interrupt(&self) -> crate::peripherals::Interrupt; - - fn async_handler(&self) -> InterruptHandler; - - /// Returns the SCL output signal for this I2C peripheral. - fn scl_output_signal(&self) -> OutputSignal; - /// Returns the SCL input signal for this I2C peripheral. - fn scl_input_signal(&self) -> InputSignal; - /// Returns the SDA output signal for this I2C peripheral. - fn sda_output_signal(&self) -> OutputSignal; - /// Returns the SDA input signal for this I2C peripheral. - fn sda_input_signal(&self) -> InputSignal; - - /// Returns a reference to the register block of the I2C peripheral. - fn register_block(&self) -> &RegisterBlock; - - /// Returns the I2C peripheral's index number. - fn i2c_number(&self) -> usize; - - /// Configures the I2C peripheral with the specified frequency, clocks, and - /// optional timeout. - fn setup(&self, frequency: HertzU32, timeout: Option) { - self.register_block().ctr().write(|w| { - // Set I2C controller to master mode - w.ms_mode().set_bit(); - // Use open drain output for SDA and SCL - w.sda_force_out().set_bit(); - w.scl_force_out().set_bit(); - // Use Most Significant Bit first for sending and receiving data - w.tx_lsb_first().clear_bit(); - w.rx_lsb_first().clear_bit(); - // Ensure that clock is enabled - w.clk_en().set_bit() - }); - - #[cfg(esp32s2)] - self.register_block() - .ctr() - .modify(|_, w| w.ref_always_on().set_bit()); - - // Configure filter - // FIXME if we ever change this we need to adapt `set_frequency` for ESP32 - self.set_filter(Some(7), Some(7)); - - // Configure frequency - let clocks = Clocks::get(); - cfg_if::cfg_if! { - if #[cfg(esp32)] { - let clock = clocks.i2c_clock.convert(); - } else if #[cfg(esp32s2)] { - let clock = clocks.apb_clock.convert(); - } else { - let clock = clocks.xtal_clock.convert(); - } - } - self.set_frequency(clock, frequency, timeout); - - self.update_config(); - - // Reset entire peripheral (also resets fifo) - self.reset(); - } - - /// Resets the I2C controller (FIFO + FSM + command list) - fn reset(&self) { - // Reset the FSM - // (the option to reset the FSM is not available - // for the ESP32) - #[cfg(not(esp32))] - self.register_block() - .ctr() - .modify(|_, w| w.fsm_rst().set_bit()); - - // Clear all I2C interrupts - self.register_block() - .int_clr() - .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); - - // Reset fifo - self.reset_fifo(); - - // Reset the command list - self.reset_command_list(); - } - - /// Resets the I2C peripheral's command registers - fn reset_command_list(&self) { - // Confirm that all commands that were configured were actually executed - for cmd in self.register_block().comd_iter() { - cmd.reset(); - } - } - - /// Sets the filter with a supplied threshold in clock cycles for which a - /// pulse must be present to pass the filter - fn set_filter(&self, sda_threshold: Option, scl_threshold: Option) { - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s2))] { - let sda_register = &self.register_block().sda_filter_cfg(); - let scl_register = &self.register_block().scl_filter_cfg(); - } else { - let sda_register = &self.register_block().filter_cfg(); - let scl_register = &self.register_block().filter_cfg(); - } - } - - match sda_threshold { - Some(threshold) => { - sda_register.modify(|_, w| unsafe { w.sda_filter_thres().bits(threshold) }); - sda_register.modify(|_, w| w.sda_filter_en().set_bit()); - } - None => sda_register.modify(|_, w| w.sda_filter_en().clear_bit()), - } - - match scl_threshold { - Some(threshold) => { - scl_register.modify(|_, w| unsafe { w.scl_filter_thres().bits(threshold) }); - scl_register.modify(|_, w| w.scl_filter_en().set_bit()); - } - None => scl_register.modify(|_, w| w.scl_filter_en().clear_bit()), - } - } - - #[cfg(esp32)] - /// Sets the frequency of the I2C interface by calculating and applying the - /// associated timings - corresponds to i2c_ll_cal_bus_clk and - /// i2c_ll_set_bus_timing in ESP-IDF - fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { - let source_clk = source_clk.raw(); - let bus_freq = bus_freq.raw(); - - let half_cycle: u32 = source_clk / bus_freq / 2; - let scl_low = half_cycle; - let scl_high = half_cycle; - let sda_hold = half_cycle / 2; - let sda_sample = scl_high / 2; - let setup = half_cycle; - let hold = half_cycle; - // default we set the timeout value to 10 bus cycles - let time_out_value = timeout.unwrap_or(half_cycle * 20); - - // SCL period. According to the TRM, we should always subtract 1 to SCL low - // period - let scl_low = scl_low - 1; - // Still according to the TRM, if filter is not enbled, we have to subtract 7, - // if SCL filter is enabled, we have to subtract: - // 8 if SCL filter is between 0 and 2 (included) - // 6 + SCL threshold if SCL filter is between 3 and 7 (included) - // to SCL high period - let mut scl_high = scl_high; - // In the "worst" case, we will subtract 13, make sure the result will still be - // correct - - // FIXME since we always set the filter threshold to 7 we don't need conditional - // code here once that changes we need the conditional code here - scl_high -= 7 + 6; - - // if (filter_cfg_en) { - // if (thres <= 2) { - // scl_high -= 8; - // } else { - // assert(hw->scl_filter_cfg.thres <= 7); - // scl_high -= thres + 6; - // } - // } else { - // scl_high -= 7; - //} - - let scl_high_period = scl_high; - let scl_low_period = scl_low; - // sda sample - let sda_hold_time = sda_hold; - let sda_sample_time = sda_sample; - // setup - let scl_rstart_setup_time = setup; - let scl_stop_setup_time = setup; - // hold - let scl_start_hold_time = hold; - let scl_stop_hold_time = hold; - - self.configure_clock( - 0, - scl_low_period, - scl_high_period, - 0, - sda_hold_time, - sda_sample_time, - scl_rstart_setup_time, - scl_stop_setup_time, - scl_start_hold_time, - scl_stop_hold_time, - time_out_value, - true, - ); - } - - #[cfg(esp32s2)] - /// Sets the frequency of the I2C interface by calculating and applying the - /// associated timings - corresponds to i2c_ll_cal_bus_clk and - /// i2c_ll_set_bus_timing in ESP-IDF - fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { - let source_clk = source_clk.raw(); - let bus_freq = bus_freq.raw(); - - let half_cycle: u32 = source_clk / bus_freq / 2; - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - let scl_high = half_cycle / 2 + 2; - let scl_wait_high = half_cycle - scl_high; - let sda_hold = half_cycle / 2; - // scl_wait_high < sda_sample <= scl_high - let sda_sample = half_cycle / 2 - 1; - let setup = half_cycle; - let hold = half_cycle; - // default we set the timeout value to 10 bus cycles - let time_out_value = timeout.unwrap_or(half_cycle * 20); - - // scl period - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold; - let sda_sample_time = sda_sample; - // setup - let scl_rstart_setup_time = setup; - let scl_stop_setup_time = setup; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold; - let time_out_en = true; - - self.configure_clock( - 0, - scl_low_period, - scl_high_period, - scl_wait_high_period, - sda_hold_time, - sda_sample_time, - scl_rstart_setup_time, - scl_stop_setup_time, - scl_start_hold_time, - scl_stop_hold_time, - time_out_value, - time_out_en, - ); - } - - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] - /// Sets the frequency of the I2C interface by calculating and applying the - /// associated timings - corresponds to i2c_ll_cal_bus_clk and - /// i2c_ll_set_bus_timing in ESP-IDF - fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { - let source_clk = source_clk.raw(); - let bus_freq = bus_freq.raw(); - - let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; - let sclk_freq: u32 = source_clk / clkm_div; - let half_cycle: u32 = sclk_freq / bus_freq / 2; - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - // Make 80KHz as a boundary here, because when working at lower frequency, too - // much scl_wait_high will faster the frequency according to some - // hardware behaviors. - let scl_wait_high = if bus_freq >= 80 * 1000 { - half_cycle / 2 - 2 - } else { - half_cycle / 4 - }; - let scl_high = half_cycle - scl_wait_high; - let sda_hold = half_cycle / 4; - let sda_sample = half_cycle / 2 + scl_wait_high; - let setup = half_cycle; - let hold = half_cycle; - - let time_out_value = if let Some(timeout) = timeout { - timeout - } else { - // default we set the timeout value to about 10 bus cycles - // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2) - (4 * 8 - (5 * half_cycle).leading_zeros()) + 2 - }; - - // According to the Technical Reference Manual, the following timings must be - // subtracted by 1. However, according to the practical measurement and - // some hardware behaviour, if wait_high_period and scl_high minus one. - // The SCL frequency would be a little higher than expected. Therefore, the - // solution here is not to minus scl_high as well as scl_wait high, and - // the frequency will be absolutely accurate to all frequency - // to some extent. - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold - 1; - let sda_sample_time = sda_sample - 1; - // setup - let scl_rstart_setup_time = setup - 1; - let scl_stop_setup_time = setup - 1; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold - 1; - let time_out_en = true; - - self.configure_clock( - clkm_div, - scl_low_period, - scl_high_period, - scl_wait_high_period, - sda_hold_time, - sda_sample_time, - scl_rstart_setup_time, - scl_stop_setup_time, - scl_start_hold_time, - scl_stop_hold_time, - time_out_value, - time_out_en, - ); - } - - #[allow(clippy::too_many_arguments, unused)] - /// Configures the clock and timing parameters for the I2C peripheral. - fn configure_clock( - &self, - sclk_div: u32, - scl_low_period: u32, - scl_high_period: u32, - scl_wait_high_period: u32, - sda_hold_time: u32, - sda_sample_time: u32, - scl_rstart_setup_time: u32, - scl_stop_setup_time: u32, - scl_start_hold_time: u32, - scl_stop_hold_time: u32, - time_out_value: u32, - time_out_en: bool, - ) { - let register_block = self.register_block(); - unsafe { - // divider - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] - register_block.clk_conf().modify(|_, w| { - w.sclk_sel().clear_bit(); - w.sclk_div_num().bits((sclk_div - 1) as u8) - }); - - // scl period - register_block - .scl_low_period() - .write(|w| w.scl_low_period().bits(scl_low_period as u16)); - - register_block.scl_high_period().write(|w| { - #[cfg(not(esp32))] // ESP32 does not have a wait_high field - w.scl_wait_high_period() - .bits(scl_wait_high_period.try_into().unwrap()); - w.scl_high_period().bits(scl_high_period as u16) - }); - - // sda sample - register_block - .sda_hold() - .write(|w| w.time().bits(sda_hold_time as u16)); - register_block - .sda_sample() - .write(|w| w.time().bits(sda_sample_time as u16)); - - // setup - register_block - .scl_rstart_setup() - .write(|w| w.time().bits(scl_rstart_setup_time as u16)); - register_block - .scl_stop_setup() - .write(|w| w.time().bits(scl_stop_setup_time as u16)); - - // hold - register_block - .scl_start_hold() - .write(|w| w.time().bits(scl_start_hold_time as u16)); - register_block - .scl_stop_hold() - .write(|w| w.time().bits(scl_stop_hold_time as u16)); - - // The ESP32 variant does not have an enable flag for the - // timeout mechanism - cfg_if::cfg_if! { - if #[cfg(esp32)] { - // timeout - register_block - .to() - .write(|w| w.time_out().bits(time_out_value)); - } else { - // timeout - // FIXME: Enable timout for other chips! - #[allow(clippy::useless_conversion)] - register_block - .to() - .write(|w| w.time_out_en().bit(time_out_en) - .time_out_value() - .bits(time_out_value.try_into().unwrap()) - ); - } - } - } - } - - /// Configures the I2C peripheral for a write operation. - /// - `addr` is the address of the slave device. - /// - `bytes` is the data two be sent. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `cmd_iterator` is an iterator over the command registers. - fn setup_write<'a, I>( - &self, - addr: u8, - bytes: &[u8], - start: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // if start is true we can only send 254 additional bytes with the address as - // the first - let max_len = if start { 254usize } else { 255usize }; - if bytes.len() > max_len { - // we could support more by adding multiple write operations - return Err(Error::ExceedingFifo); - } - - let write_len = if start { bytes.len() + 1 } else { bytes.len() }; - // don't issue write if there is no data to write - if write_len > 0 { - // WRITE command - add_cmd( - cmd_iterator, - Command::Write { - ack_exp: Ack::Ack, - ack_check_en: true, - length: write_len as u8, - }, - )?; - } - - self.update_config(); - - if start { - // Load address and R/W bit into FIFO - write_fifo( - self.register_block(), - addr << 1 | OperationType::Write as u8, - ); - } - Ok(()) - } - - /// Configures the I2C peripheral for a read operation. - /// - `addr` is the address of the slave device. - /// - `buffer` is the buffer to store the read data. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `will_continue` indicates whether there is another read operation - /// following this one and we should not nack the last byte. - /// - `cmd_iterator` is an iterator over the command registers. - fn setup_read<'a, I>( - &self, - addr: u8, - buffer: &mut [u8], - start: bool, - will_continue: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - if buffer.is_empty() { - return Err(Error::InvalidZeroLength); - } - let (max_len, initial_len) = if will_continue { - (255usize, buffer.len()) - } else { - (254usize, buffer.len() - 1) - }; - if buffer.len() > max_len { - // we could support more by adding multiple read operations - return Err(Error::ExceedingFifo); - } - - if start { - // WRITE command - add_cmd( - cmd_iterator, - Command::Write { - ack_exp: Ack::Ack, - ack_check_en: true, - length: 1, - }, - )?; - } - - if initial_len > 0 { - // READ command - add_cmd( - cmd_iterator, - Command::Read { - ack_value: Ack::Ack, - length: initial_len as u8, - }, - )?; - } - - if !will_continue { - // this is the last read so we need to nack the last byte - // READ w/o ACK - add_cmd( - cmd_iterator, - Command::Read { - ack_value: Ack::Nack, - length: 1, - }, - )?; - } - - self.update_config(); - - if start { - // Load address and R/W bit into FIFO - write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8); - } - Ok(()) - } - - #[cfg(not(any(esp32, esp32s2)))] - /// Reads all bytes from the RX FIFO. - fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - // Read bytes from FIFO - // FIXME: Handle case where less data has been provided by the slave than - // requested? Or is this prevented from a protocol perspective? - for byte in buffer.iter_mut() { - loop { - self.check_errors()?; - - let reg = self.register_block().fifo_st().read(); - if reg.rxfifo_raddr().bits() != reg.rxfifo_waddr().bits() { - break; - } - } - - *byte = read_fifo(self.register_block()); - } - - Ok(()) - } - - #[cfg(any(esp32, esp32s2))] - /// Reads all bytes from the RX FIFO. - fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { - // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the - // FIFO apparently it would be possible by using non-fifo mode - // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 - - if buffer.len() > 32 { - panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); - } - - // wait for completion - then we can just read the data from FIFO - // once we change to non-fifo mode to support larger transfers that - // won't work anymore - self.wait_for_completion(false)?; - - // Read bytes from FIFO - // FIXME: Handle case where less data has been provided by the slave than - // requested? Or is this prevented from a protocol perspective? - for byte in buffer.iter_mut() { - *byte = read_fifo(self.register_block()); - } - - Ok(()) - } - - /// Clears all pending interrupts for the I2C peripheral. - fn clear_all_interrupts(&self) { - self.register_block() - .int_clr() - .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); - } - - /// Waits for the completion of an I2C transaction. - fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { - let mut tout = MAX_ITERATIONS; - loop { - let interrupts = self.register_block().int_raw().read(); - - self.check_errors()?; - - // Handle completion cases - // A full transmission was completed (either a STOP condition or END was - // processed) - if (!end_only && interrupts.trans_complete().bit_is_set()) - || interrupts.end_detect().bit_is_set() - { - break; - } - - tout -= 1; - if tout == 0 { - return Err(Error::TimeOut); - } - } - self.check_all_commands_done()?; - Ok(()) - } - - /// Checks whether all I2C commands have completed execution. - fn check_all_commands_done(&self) -> Result<(), Error> { - // NOTE: on esp32 executing the end command generates the end_detect interrupt - // but does not seem to clear the done bit! So we don't check the done - // status of an end command - for cmd_reg in self.register_block().comd_iter() { - let cmd = cmd_reg.read(); - - if cmd.bits() != 0x0 && !cmd.opcode().is_end() && !cmd.command_done().bit_is_set() { - return Err(Error::ExecIncomplete); - } - } - - Ok(()) - } - - /// Checks for I2C transmission errors and handles them. - /// - /// This function inspects specific I2C-related interrupts to detect errors - /// during communication, such as timeouts, failed acknowledgments, or - /// arbitration loss. If an error is detected, the function handles it - /// by resetting the I2C peripheral to clear the error condition and then - /// returns an appropriate error. - fn check_errors(&self) -> Result<(), Error> { - let interrupts = self.register_block().int_raw().read(); - - // The ESP32 variant has a slightly different interrupt naming - // scheme! - cfg_if::cfg_if! { - if #[cfg(esp32)] { - // Handle error cases - let retval = if interrupts.time_out().bit_is_set() { - Err(Error::TimeOut) - } else if interrupts.ack_err().bit_is_set() { - Err(Error::AckCheckFailed) - } else if interrupts.arbitration_lost().bit_is_set() { - Err(Error::ArbitrationLost) - } else { - Ok(()) - }; - } else { - // Handle error cases - let retval = if interrupts.time_out().bit_is_set() { - Err(Error::TimeOut) - } else if interrupts.nack().bit_is_set() { - Err(Error::AckCheckFailed) - } else if interrupts.arbitration_lost().bit_is_set() { - Err(Error::ArbitrationLost) - } else if interrupts.trans_complete().bit_is_set() && self.register_block().sr().read().resp_rec().bit_is_clear() { - Err(Error::AckCheckFailed) - } else { - Ok(()) - }; - } - } - - if retval.is_err() { - self.reset(); - } - - retval - } - - /// Updates the configuration of the I2C peripheral. - /// - /// This function ensures that the configuration values, such as clock - /// settings, SDA/SCL filtering, timeouts, and other operational - /// parameters, which are configured in other functions, are properly - /// propagated to the I2C hardware. This step is necessary to synchronize - /// the software-configured settings with the peripheral's internal - /// registers, ensuring that the hardware behaves according to the - /// current configuration. - fn update_config(&self) { - // Ensure that the configuration of the peripheral is correctly propagated - // (only necessary for C2, C3, C6, H2 and S3 variant) - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] - self.register_block() - .ctr() - .modify(|_, w| w.conf_upgate().set_bit()); - } - - /// Starts an I2C transmission. - fn start_transmission(&self) { - // Start transmission - self.register_block() - .ctr() - .modify(|_, w| w.trans_start().set_bit()); - } - - #[cfg(not(any(esp32, esp32s2)))] - /// Fills the TX FIFO with data from the provided slice. - fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { - let mut index = 0; - while index < bytes.len() - && !self - .register_block() - .int_raw() - .read() - .txfifo_ovf() - .bit_is_set() - { - write_fifo(self.register_block(), bytes[index]); - index += 1; - } - if self - .register_block() - .int_raw() - .read() - .txfifo_ovf() - .bit_is_set() - { - index -= 1; - self.register_block() - .int_clr() - .write(|w| w.txfifo_ovf().clear_bit_by_one()); - } - index - } - - #[cfg(not(any(esp32, esp32s2)))] - /// Writes remaining data from byte slice to the TX FIFO from the specified - /// index. - fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - let mut index = start_index; - loop { - self.check_errors()?; - - while !self - .register_block() - .int_raw() - .read() - .txfifo_wm() - .bit_is_set() - { - self.check_errors()?; - } - - self.register_block() - .int_clr() - .write(|w| w.txfifo_wm().clear_bit_by_one()); - - while !self - .register_block() - .int_raw() - .read() - .txfifo_wm() - .bit_is_set() - { - self.check_errors()?; - } - - if index >= bytes.len() { - break Ok(()); - } - - write_fifo(self.register_block(), bytes[index]); - index += 1; - } - } - - #[cfg(any(esp32, esp32s2))] - /// Fills the TX FIFO with data from the provided slice. - fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { - // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the - // FIFO apparently it would be possible by using non-fifo mode - // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 - - if bytes.len() > 31 { - panic!("On ESP32 and ESP32-S2 the max I2C transfer is limited to 31 bytes"); - } - - for b in bytes { - write_fifo(self.register_block(), *b); - } - - bytes.len() - } - - #[cfg(any(esp32, esp32s2))] - /// Writes remaining data from byte slice to the TX FIFO from the specified - /// index. - fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { - // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the - // FIFO apparently it would be possible by using non-fifo mode - // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 - - if start_index >= bytes.len() { - return Ok(()); - } - - // this is only possible when writing the I2C address in release mode - // from [perform_write_read] - for b in bytes { - write_fifo(self.register_block(), *b); - self.check_errors()?; - } - - Ok(()) - } - - /// Resets the transmit and receive FIFO buffers - #[cfg(not(esp32))] - fn reset_fifo(&self) { - // First, reset the fifo buffers - self.register_block().fifo_conf().modify(|_, w| unsafe { - w.tx_fifo_rst().set_bit(); - w.rx_fifo_rst().set_bit(); - w.nonfifo_en().clear_bit(); - w.fifo_prt_en().set_bit(); - w.rxfifo_wm_thrhd().bits(1); - w.txfifo_wm_thrhd().bits(8) - }); - - self.register_block().fifo_conf().modify(|_, w| { - w.tx_fifo_rst().clear_bit(); - w.rx_fifo_rst().clear_bit() - }); - - self.register_block().int_clr().write(|w| { - w.rxfifo_wm().clear_bit_by_one(); - w.txfifo_wm().clear_bit_by_one() - }); - - self.update_config(); - } - - /// Resets the transmit and receive FIFO buffers - #[cfg(esp32)] - fn reset_fifo(&self) { - // First, reset the fifo buffers - self.register_block().fifo_conf().modify(|_, w| unsafe { - w.tx_fifo_rst().set_bit(); - w.rx_fifo_rst().set_bit(); - w.nonfifo_en().clear_bit(); - w.nonfifo_rx_thres().bits(1); - w.nonfifo_tx_thres().bits(32) - }); - - self.register_block().fifo_conf().modify(|_, w| { - w.tx_fifo_rst().clear_bit(); - w.rx_fifo_rst().clear_bit() - }); - - self.register_block() - .int_clr() - .write(|w| w.rxfifo_full().clear_bit_by_one()); - } - - /// Executes an I2C write operation. - /// - `addr` is the address of the slave device. - /// - `bytes` is the data two be sent. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `cmd_iterator` is an iterator over the command registers. - fn write_operation<'a, I>( - &self, - address: u8, - bytes: &[u8], - start: bool, - stop: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length writes without start or end as that would be an - // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 - if bytes.is_empty() && !start && !stop { - return Ok(()); - } - - // Reset FIFO and command list - self.reset_fifo(); - self.reset_command_list(); - - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - self.setup_write(address, bytes, start, cmd_iterator)?; - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - let index = self.fill_tx_fifo(bytes); - self.start_transmission(); - - // Fill the FIFO with the remaining bytes: - self.write_remaining_tx_fifo(index, bytes)?; - self.wait_for_completion(!stop)?; - Ok(()) - } - - /// Executes an I2C read operation. - /// - `addr` is the address of the slave device. - /// - `buffer` is the buffer to store the read data. - /// - `start` indicates whether the operation should start by a START - /// condition and sending the address. - /// - `stop` indicates whether the operation should end with a STOP - /// condition. - /// - `will_continue` indicates whether there is another read operation - /// following this one and we should not nack the last byte. - /// - `cmd_iterator` is an iterator over the command registers. - fn read_operation<'a, I>( - &self, - address: u8, - buffer: &mut [u8], - start: bool, - stop: bool, - will_continue: bool, - cmd_iterator: &mut I, - ) -> Result<(), Error> - where - I: Iterator, - { - // Short circuit for zero length reads as that would be an invalid operation - // read lengths in the TRM (at least for ESP32-S3) are 1-255 - if buffer.is_empty() { - return Ok(()); - } - - // Reset FIFO and command list - self.reset_fifo(); - self.reset_command_list(); - - if start { - add_cmd(cmd_iterator, Command::Start)?; - } - - self.setup_read(address, buffer, start, will_continue, cmd_iterator)?; - - add_cmd( - cmd_iterator, - if stop { Command::Stop } else { Command::End }, - )?; - self.start_transmission(); - self.read_all_from_fifo(buffer)?; - self.wait_for_completion(!stop)?; - Ok(()) - } -} - -/// Adds a command to the I2C command sequence. -fn add_cmd<'a, I>(cmd_iterator: &mut I, command: Command) -> Result<(), Error> -where - I: Iterator, -{ - let cmd = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?; - - cmd.write(|w| match command { - Command::Start => w.opcode().rstart(), - Command::Stop => w.opcode().stop(), - Command::End => w.opcode().end(), - Command::Write { - ack_exp, - ack_check_en, - length, - } => unsafe { - w.opcode().write(); - w.ack_exp().bit(ack_exp == Ack::Nack); - w.ack_check_en().bit(ack_check_en); - w.byte_num().bits(length); - w - }, - Command::Read { ack_value, length } => unsafe { - w.opcode().read(); - w.ack_value().bit(ack_value == Ack::Nack); - w.byte_num().bits(length); - w - }, - }); - - Ok(()) -} - -#[cfg(not(esp32s2))] -fn read_fifo(register_block: &RegisterBlock) -> u8 { - register_block.data().read().fifo_rdata().bits() -} - -#[cfg(not(esp32))] -fn write_fifo(register_block: &RegisterBlock, data: u8) { - register_block - .data() - .write(|w| unsafe { w.fifo_rdata().bits(data) }); -} - -#[cfg(esp32s2)] -fn read_fifo(register_block: &RegisterBlock) -> u8 { - let base_addr = register_block.scl_low_period().as_ptr(); - let fifo_ptr = (if base_addr as u32 == 0x3f413000 { - 0x6001301c - } else { - 0x6002701c - }) as *mut u32; - unsafe { (fifo_ptr.read_volatile() & 0xff) as u8 } -} - -#[cfg(esp32)] -fn write_fifo(register_block: &RegisterBlock, data: u8) { - let base_addr = register_block.scl_low_period().as_ptr(); - let fifo_ptr = (if base_addr as u32 == 0x3FF53000 { - 0x6001301c - } else { - 0x6002701c - }) as *mut u32; - unsafe { - fifo_ptr.write_volatile(data as u32); - } -} - -impl PeripheralMarker for crate::peripherals::I2C0 { - #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::I2cExt0 - } -} - -impl Instance for crate::peripherals::I2C0 { - #[inline(always)] - fn async_handler(&self) -> InterruptHandler { - asynch::i2c0_handler - } - - #[inline(always)] - fn scl_output_signal(&self) -> OutputSignal { - OutputSignal::I2CEXT0_SCL - } - - #[inline(always)] - fn scl_input_signal(&self) -> InputSignal { - InputSignal::I2CEXT0_SCL - } - - #[inline(always)] - fn sda_output_signal(&self) -> OutputSignal { - OutputSignal::I2CEXT0_SDA - } - - #[inline(always)] - fn sda_input_signal(&self) -> InputSignal { - InputSignal::I2CEXT0_SDA - } - - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn i2c_number(&self) -> usize { - 0 - } - - #[inline(always)] - fn interrupt(&self) -> crate::peripherals::Interrupt { - crate::peripherals::Interrupt::I2C_EXT0 - } -} - -#[cfg(i2c1)] -impl PeripheralMarker for crate::peripherals::I2C1 { - #[inline(always)] - fn peripheral(&self) -> crate::system::Peripheral { - crate::system::Peripheral::I2cExt1 - } -} - -#[cfg(i2c1)] -impl Instance for crate::peripherals::I2C1 { - #[inline(always)] - fn async_handler(&self) -> InterruptHandler { - asynch::i2c1_handler - } - - #[inline(always)] - fn scl_output_signal(&self) -> OutputSignal { - OutputSignal::I2CEXT1_SCL - } - - #[inline(always)] - fn scl_input_signal(&self) -> InputSignal { - InputSignal::I2CEXT1_SCL - } - - #[inline(always)] - fn sda_output_signal(&self) -> OutputSignal { - OutputSignal::I2CEXT1_SDA - } - - #[inline(always)] - fn sda_input_signal(&self) -> InputSignal { - InputSignal::I2CEXT1_SDA - } - - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn i2c_number(&self) -> usize { - 1 - } - - #[inline(always)] - fn interrupt(&self) -> crate::peripherals::Interrupt { - crate::peripherals::Interrupt::I2C_EXT1 - } -} - -crate::any_peripheral! { - /// Represents any I2C peripheral. - pub peripheral AnyI2c { - #[cfg(i2c0)] - I2c0(crate::peripherals::I2C0), - #[cfg(i2c1)] - I2c1(crate::peripherals::I2C1), - } -} - -impl Instance for AnyI2c { - delegate::delegate! { - to match &self.0 { - AnyI2cInner::I2c0(i2c) => i2c, - #[cfg(i2c1)] - AnyI2cInner::I2c1(i2c) => i2c, - } { - fn interrupt(&self) -> crate::peripherals::Interrupt; - fn async_handler(&self) -> InterruptHandler; - fn scl_output_signal(&self) -> OutputSignal; - fn scl_input_signal(&self) -> InputSignal; - fn sda_output_signal(&self) -> OutputSignal; - fn sda_input_signal(&self) -> InputSignal; - fn register_block(&self) -> &RegisterBlock; - fn i2c_number(&self) -> usize; - } - } -} - -#[cfg(lp_i2c0)] -pub mod lp_i2c { - //! Low-power I2C driver - - use fugit::HertzU32; - - use crate::{ - gpio::lp_io::LowPowerOutputOpenDrain, - peripherals::{LP_CLKRST, LP_I2C0}, - }; - - const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7; - - /// I2C-specific transmission errors - #[derive(Debug, Clone, Copy, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Error { - /// The transmission exceeded the FIFO size. - ExceedingFifo, - /// The acknowledgment check failed. - AckCheckFailed, - /// A timeout occurred during transmission. - TimeOut, - /// The arbitration for the bus was lost. - ArbitrationLost, - /// The execution of the I2C command was incomplete. - ExecIncomplete, - /// The number of commands issued exceeded the limit. - CommandNrExceeded, - /// The response received from the I2C device was invalid. - InvalidResponse, - } - - #[allow(unused)] - enum OperationType { - Write = 0, - Read = 1, - } - - #[allow(unused)] - #[derive(Eq, PartialEq, Copy, Clone)] - enum Ack { - Ack, - Nack, - } - - #[derive(PartialEq)] - #[allow(unused)] - enum Command { - Start, - Stop, - End, - Write { - /// This bit is to set an expected ACK value for the transmitter. - ack_exp: Ack, - /// Enables checking the ACK value received against the ack_exp - /// value. - ack_check_en: bool, - /// Length of data (in bytes) to be written. The maximum length is - /// 255, while the minimum is 1. - length: u8, - }, - Read { - /// Indicates whether the receiver will send an ACK after this byte - /// has been received. - ack_value: Ack, - /// Length of data (in bytes) to be read. The maximum length is 255, - /// while the minimum is 1. - length: u8, - }, - } - - // https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122 - // TX/RX RAM size is 16*8 bit - // TX RX FIFO has 16 bit depth - // The clock source of APB_CLK in LP_I2C is CLK_AON_FAST. - // Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK. - // When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source, - // and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source. - // Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK. - // Adjust the timing registers accordingly when the clock frequency changes. - - /// Represents a Low-Power I2C peripheral. - pub struct LpI2c { - i2c: LP_I2C0, - } - - impl LpI2c { - /// Creates a new instance of the `LpI2c` peripheral. - pub fn new( - i2c: LP_I2C0, - _sda: LowPowerOutputOpenDrain<'_, 6>, - _scl: LowPowerOutputOpenDrain<'_, 7>, - frequency: HertzU32, - ) -> Self { - let me = Self { i2c }; - - // Configure LP I2C GPIOs - // Initialize IO Pins - let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; - let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; - let lp_peri = unsafe { &*crate::peripherals::LP_PERI::PTR }; - - unsafe { - // FIXME: use GPIO APIs to configure pins - lp_aon - .gpio_mux() - .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 6))); - lp_aon - .gpio_mux() - .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 7))); - lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); // TODO - - lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); - - // Set output mode to Normal - lp_io.pin(6).modify(|_, w| w.pad_driver().set_bit()); - // Enable output (writing to write-1-to-set register, then internally the - // `GPIO_OUT_REG` will be set) - lp_io - .out_enable_w1ts() - .write(|w| w.enable_w1ts().bits(1 << 6)); - // Enable input - lp_io.gpio(6).modify(|_, w| w.fun_ie().set_bit()); - - // Disable pulldown (enable internal weak pull-down) - lp_io.gpio(6).modify(|_, w| w.fun_wpd().clear_bit()); - // Enable pullup - lp_io.gpio(6).modify(|_, w| w.fun_wpu().set_bit()); - - // Same process for SCL pin - lp_io.pin(7).modify(|_, w| w.pad_driver().set_bit()); - // Enable output (writing to write-1-to-set register, then internally the - // `GPIO_OUT_REG` will be set) - lp_io - .out_enable_w1ts() - .write(|w| w.enable_w1ts().bits(1 << 7)); - // Enable input - lp_io.gpio(7).modify(|_, w| w.fun_ie().set_bit()); - // Disable pulldown (enable internal weak pull-down) - lp_io.gpio(7).modify(|_, w| w.fun_wpd().clear_bit()); - // Enable pullup - lp_io.gpio(7).modify(|_, w| w.fun_wpu().set_bit()); - - // Select LP I2C function for the SDA and SCL pins - lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); - lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); - } - - // Initialize LP I2C HAL */ - me.i2c.clk_conf().modify(|_, w| w.sclk_active().set_bit()); - - // Enable LP I2C controller clock - lp_peri - .clk_en() - .modify(|_, w| w.lp_ext_i2c_ck_en().set_bit()); - - lp_peri - .reset_en() - .modify(|_, w| w.lp_ext_i2c_reset_en().set_bit()); - lp_peri - .reset_en() - .modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit()); - - // Set LP I2C source clock - unsafe { &*LP_CLKRST::PTR } - .lpperi() - .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); - - // Initialize LP I2C Master mode - me.i2c.ctr().modify(|_, w| unsafe { - // Clear register - w.bits(0); - // Use open drain output for SDA and SCL - w.sda_force_out().set_bit(); - w.scl_force_out().set_bit(); - // Ensure that clock is enabled - w.clk_en().set_bit() - }); - - // First, reset the fifo buffers - me.i2c.fifo_conf().modify(|_, w| w.nonfifo_en().clear_bit()); - - me.i2c.ctr().modify(|_, w| { - w.tx_lsb_first().clear_bit(); - w.rx_lsb_first().clear_bit() - }); - - me.reset_fifo(); - - // Set LP I2C source clock - unsafe { &*LP_CLKRST::PTR } - .lpperi() - .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); - - // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this - // call - - let source_clk = 16_000_000; - let bus_freq = frequency.raw(); - - let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; - let sclk_freq: u32 = source_clk / clkm_div; - let half_cycle: u32 = sclk_freq / bus_freq / 2; - - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - // Make 80KHz as a boundary here, because when working at lower frequency, too - // much scl_wait_high will faster the frequency according to some - // hardware behaviors. - let scl_wait_high = if bus_freq >= 80 * 1000 { - half_cycle / 2 - 2 - } else { - half_cycle / 4 - }; - let scl_high = half_cycle - scl_wait_high; - let sda_hold = half_cycle / 4; - let sda_sample = half_cycle / 2; // TODO + scl_wait_high; - let setup = half_cycle; - let hold = half_cycle; - // default we set the timeout value to about 10 bus cycles - // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2) - let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2; - - // According to the Technical Reference Manual, the following timings must be - // subtracted by 1. However, according to the practical measurement and - // some hardware behaviour, if wait_high_period and scl_high minus one. - // The SCL frequency would be a little higher than expected. Therefore, the - // solution here is not to minus scl_high as well as scl_wait high, and - // the frequency will be absolutely accurate to all frequency - // to some extent. - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold - 1; - let sda_sample_time = sda_sample - 1; - // setup - let scl_rstart_setup_time = setup - 1; - let scl_stop_setup_time = setup - 1; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold - 1; - let time_out_value = tout; - let time_out_en = true; - - // Write data to registers - unsafe { - me.i2c.clk_conf().modify(|_, w| { - w.sclk_sel().clear_bit(); - w.sclk_div_num().bits((clkm_div - 1) as u8) - }); - - // scl period - me.i2c - .scl_low_period() - .write(|w| w.scl_low_period().bits(scl_low_period as u16)); - - me.i2c.scl_high_period().write(|w| { - w.scl_high_period().bits(scl_high_period as u16); - w.scl_wait_high_period().bits(scl_wait_high_period as u8) - }); - // sda sample - me.i2c - .sda_hold() - .write(|w| w.time().bits(sda_hold_time as u16)); - me.i2c - .sda_sample() - .write(|w| w.time().bits(sda_sample_time as u16)); - - // setup - me.i2c - .scl_rstart_setup() - .write(|w| w.time().bits(scl_rstart_setup_time as u16)); - me.i2c - .scl_stop_setup() - .write(|w| w.time().bits(scl_stop_setup_time as u16)); - - // hold - me.i2c - .scl_start_hold() - .write(|w| w.time().bits(scl_start_hold_time as u16)); - me.i2c - .scl_stop_hold() - .write(|w| w.time().bits(scl_stop_hold_time as u16)); - - me.i2c.to().write(|w| { - w.time_out_en().bit(time_out_en); - w.time_out_value().bits(time_out_value.try_into().unwrap()) - }); - } - - // Enable SDA and SCL filtering. This configuration matches the HP I2C filter - // config - - me.i2c - .filter_cfg() - .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); - me.i2c - .filter_cfg() - .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); - - me.i2c - .filter_cfg() - .modify(|_, w| w.sda_filter_en().set_bit()); - me.i2c - .filter_cfg() - .modify(|_, w| w.scl_filter_en().set_bit()); - - // Configure the I2C master to send a NACK when the Rx FIFO count is full - me.i2c.ctr().modify(|_, w| w.rx_full_ack_level().set_bit()); - - // Synchronize the config register values to the LP I2C peripheral clock - me.lp_i2c_update(); - - me - } - - /// Update I2C configuration - fn lp_i2c_update(&self) { - self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); - } - - /// Resets the transmit and receive FIFO buffers. - fn reset_fifo(&self) { - self.i2c - .fifo_conf() - .modify(|_, w| w.tx_fifo_rst().set_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.tx_fifo_rst().clear_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.rx_fifo_rst().set_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.rx_fifo_rst().clear_bit()); - } - } -} diff --git a/esp-hal/src/i2c/lp_i2c.rs b/esp-hal/src/i2c/lp_i2c.rs new file mode 100644 index 00000000000..2a16c94809e --- /dev/null +++ b/esp-hal/src/i2c/lp_i2c.rs @@ -0,0 +1,341 @@ +//! Low-power I2C driver + +use fugit::HertzU32; + +use crate::{ + gpio::lp_io::LowPowerOutputOpenDrain, + peripherals::{LP_CLKRST, LP_I2C0}, +}; + +const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7; + +/// I2C-specific transmission errors +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The transmission exceeded the FIFO size. + ExceedingFifo, + /// The acknowledgment check failed. + AckCheckFailed, + /// A timeout occurred during transmission. + TimeOut, + /// The arbitration for the bus was lost. + ArbitrationLost, + /// The execution of the I2C command was incomplete. + ExecIncomplete, + /// The number of commands issued exceeded the limit. + CommandNrExceeded, + /// The response received from the I2C device was invalid. + InvalidResponse, +} + +#[allow(unused)] +enum OperationType { + Write = 0, + Read = 1, +} + +#[allow(unused)] +#[derive(Eq, PartialEq, Copy, Clone)] +enum Ack { + Ack, + Nack, +} + +#[derive(PartialEq)] +#[allow(unused)] +enum Command { + Start, + Stop, + End, + Write { + /// This bit is to set an expected ACK value for the transmitter. + ack_exp: Ack, + /// Enables checking the ACK value received against the ack_exp + /// value. + ack_check_en: bool, + /// Length of data (in bytes) to be written. The maximum length is + /// 255, while the minimum is 1. + length: u8, + }, + Read { + /// Indicates whether the receiver will send an ACK after this byte + /// has been received. + ack_value: Ack, + /// Length of data (in bytes) to be read. The maximum length is 255, + /// while the minimum is 1. + length: u8, + }, +} + +// https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122 +// TX/RX RAM size is 16*8 bit +// TX RX FIFO has 16 bit depth +// The clock source of APB_CLK in LP_I2C is CLK_AON_FAST. +// Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK. +// When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source, +// and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source. +// Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK. +// Adjust the timing registers accordingly when the clock frequency changes. + +/// Represents a Low-Power I2C peripheral. +pub struct LpI2c { + i2c: LP_I2C0, +} + +impl LpI2c { + /// Creates a new instance of the `LpI2c` peripheral. + pub fn new( + i2c: LP_I2C0, + _sda: LowPowerOutputOpenDrain<'_, 6>, + _scl: LowPowerOutputOpenDrain<'_, 7>, + frequency: HertzU32, + ) -> Self { + let me = Self { i2c }; + + // Configure LP I2C GPIOs + // Initialize IO Pins + let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; + let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; + let lp_peri = unsafe { &*crate::peripherals::LP_PERI::PTR }; + + unsafe { + // FIXME: use GPIO APIs to configure pins + lp_aon + .gpio_mux() + .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 6))); + lp_aon + .gpio_mux() + .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 7))); + lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); // TODO + + lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); + + // Set output mode to Normal + lp_io.pin(6).modify(|_, w| w.pad_driver().set_bit()); + // Enable output (writing to write-1-to-set register, then internally the + // `GPIO_OUT_REG` will be set) + lp_io + .out_enable_w1ts() + .write(|w| w.enable_w1ts().bits(1 << 6)); + // Enable input + lp_io.gpio(6).modify(|_, w| w.fun_ie().set_bit()); + + // Disable pulldown (enable internal weak pull-down) + lp_io.gpio(6).modify(|_, w| w.fun_wpd().clear_bit()); + // Enable pullup + lp_io.gpio(6).modify(|_, w| w.fun_wpu().set_bit()); + + // Same process for SCL pin + lp_io.pin(7).modify(|_, w| w.pad_driver().set_bit()); + // Enable output (writing to write-1-to-set register, then internally the + // `GPIO_OUT_REG` will be set) + lp_io + .out_enable_w1ts() + .write(|w| w.enable_w1ts().bits(1 << 7)); + // Enable input + lp_io.gpio(7).modify(|_, w| w.fun_ie().set_bit()); + // Disable pulldown (enable internal weak pull-down) + lp_io.gpio(7).modify(|_, w| w.fun_wpd().clear_bit()); + // Enable pullup + lp_io.gpio(7).modify(|_, w| w.fun_wpu().set_bit()); + + // Select LP I2C function for the SDA and SCL pins + lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); + lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); + } + + // Initialize LP I2C HAL */ + me.i2c.clk_conf().modify(|_, w| w.sclk_active().set_bit()); + + // Enable LP I2C controller clock + lp_peri + .clk_en() + .modify(|_, w| w.lp_ext_i2c_ck_en().set_bit()); + + lp_peri + .reset_en() + .modify(|_, w| w.lp_ext_i2c_reset_en().set_bit()); + lp_peri + .reset_en() + .modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit()); + + // Set LP I2C source clock + unsafe { &*LP_CLKRST::PTR } + .lpperi() + .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); + + // Initialize LP I2C Master mode + me.i2c.ctr().modify(|_, w| unsafe { + // Clear register + w.bits(0); + // Use open drain output for SDA and SCL + w.sda_force_out().set_bit(); + w.scl_force_out().set_bit(); + // Ensure that clock is enabled + w.clk_en().set_bit() + }); + + // First, reset the fifo buffers + me.i2c.fifo_conf().modify(|_, w| w.nonfifo_en().clear_bit()); + + me.i2c.ctr().modify(|_, w| { + w.tx_lsb_first().clear_bit(); + w.rx_lsb_first().clear_bit() + }); + + me.reset_fifo(); + + // Set LP I2C source clock + unsafe { &*LP_CLKRST::PTR } + .lpperi() + .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); + + // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this + // call + + let source_clk = 16_000_000; + let bus_freq = frequency.raw(); + + let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; + let sclk_freq: u32 = source_clk / clkm_div; + let half_cycle: u32 = sclk_freq / bus_freq / 2; + + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + // Make 80KHz as a boundary here, because when working at lower frequency, too + // much scl_wait_high will faster the frequency according to some + // hardware behaviors. + let scl_wait_high = if bus_freq >= 80 * 1000 { + half_cycle / 2 - 2 + } else { + half_cycle / 4 + }; + let scl_high = half_cycle - scl_wait_high; + let sda_hold = half_cycle / 4; + let sda_sample = half_cycle / 2; // TODO + scl_wait_high; + let setup = half_cycle; + let hold = half_cycle; + // default we set the timeout value to about 10 bus cycles + // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2) + let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2; + + // According to the Technical Reference Manual, the following timings must be + // subtracted by 1. However, according to the practical measurement and + // some hardware behaviour, if wait_high_period and scl_high minus one. + // The SCL frequency would be a little higher than expected. Therefore, the + // solution here is not to minus scl_high as well as scl_wait high, and + // the frequency will be absolutely accurate to all frequency + // to some extent. + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold - 1; + let sda_sample_time = sda_sample - 1; + // setup + let scl_rstart_setup_time = setup - 1; + let scl_stop_setup_time = setup - 1; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold - 1; + let time_out_value = tout; + let time_out_en = true; + + // Write data to registers + unsafe { + me.i2c.clk_conf().modify(|_, w| { + w.sclk_sel().clear_bit(); + w.sclk_div_num().bits((clkm_div - 1) as u8) + }); + + // scl period + me.i2c + .scl_low_period() + .write(|w| w.scl_low_period().bits(scl_low_period as u16)); + + me.i2c.scl_high_period().write(|w| { + w.scl_high_period().bits(scl_high_period as u16); + w.scl_wait_high_period().bits(scl_wait_high_period as u8) + }); + // sda sample + me.i2c + .sda_hold() + .write(|w| w.time().bits(sda_hold_time as u16)); + me.i2c + .sda_sample() + .write(|w| w.time().bits(sda_sample_time as u16)); + + // setup + me.i2c + .scl_rstart_setup() + .write(|w| w.time().bits(scl_rstart_setup_time as u16)); + me.i2c + .scl_stop_setup() + .write(|w| w.time().bits(scl_stop_setup_time as u16)); + + // hold + me.i2c + .scl_start_hold() + .write(|w| w.time().bits(scl_start_hold_time as u16)); + me.i2c + .scl_stop_hold() + .write(|w| w.time().bits(scl_stop_hold_time as u16)); + + me.i2c.to().write(|w| { + w.time_out_en().bit(time_out_en); + w.time_out_value().bits(time_out_value.try_into().unwrap()) + }); + } + + // Enable SDA and SCL filtering. This configuration matches the HP I2C filter + // config + + me.i2c + .filter_cfg() + .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); + me.i2c + .filter_cfg() + .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); + + me.i2c + .filter_cfg() + .modify(|_, w| w.sda_filter_en().set_bit()); + me.i2c + .filter_cfg() + .modify(|_, w| w.scl_filter_en().set_bit()); + + // Configure the I2C master to send a NACK when the Rx FIFO count is full + me.i2c.ctr().modify(|_, w| w.rx_full_ack_level().set_bit()); + + // Synchronize the config register values to the LP I2C peripheral clock + me.lp_i2c_update(); + + me + } + + /// Update I2C configuration + fn lp_i2c_update(&self) { + self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); + } + + /// Resets the transmit and receive FIFO buffers. + fn reset_fifo(&self) { + self.i2c + .fifo_conf() + .modify(|_, w| w.tx_fifo_rst().set_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.tx_fifo_rst().clear_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.rx_fifo_rst().set_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.rx_fifo_rst().clear_bit()); + } +} diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs new file mode 100644 index 00000000000..167a1b855f7 --- /dev/null +++ b/esp-hal/src/i2c/master/mod.rs @@ -0,0 +1,2295 @@ +//! # Inter-Integrated Circuit (I2C) - Master mode +//! +//! ## Configuration +//! +//! Each I2C controller is individually configurable, and the usual setting +//! such as frequency, timeout, and SDA/SCL pins can easily be configured. +//! +//! ## Usage +//! +//! The I2C driver implements a number of third-party traits, with the +//! intention of making the HAL inter-compatible with various device drivers +//! from the community. This includes the [`embedded-hal`] for both 0.2.x and +//! 1.0.x versions. +//! +//! ## Examples +//! +//! ### Read Data from a BMP180 Sensor +//! +//! ```rust, no_run +#![doc = crate::before_snippet!()] +//! # use esp_hal::i2c::master::{Config, I2c}; +//! +//! // Create a new peripheral object with the described wiring +//! // and standard I2C clock speed. +//! let mut i2c = I2c::new( +//! peripherals.I2C0, +//! Config::default(), +//! ) +//! .with_sda(peripherals.GPIO1) +//! .with_scl(peripherals.GPIO2); +//! +//! loop { +//! let mut data = [0u8; 22]; +//! i2c.write_read(0x77, &[0xaa], &mut data).ok(); +//! } +//! # } +//! ``` +//! [`embedded-hal`]: https://crates.io/crates/embedded-hal + +use core::marker::PhantomData; +#[cfg(not(esp32))] +use core::{ + pin::Pin, + task::{Context, Poll}, +}; + +use embassy_embedded_hal::SetConfig; +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal::i2c::Operation as EhalOperation; +use fugit::HertzU32; + +use crate::{ + clock::Clocks, + gpio::{interconnect::PeripheralOutput, InputSignal, OutputSignal, Pull}, + interrupt::InterruptHandler, + peripheral::{Peripheral, PeripheralRef}, + peripherals::{ + i2c0::{RegisterBlock, COMD}, + Interrupt, + }, + private, + system::PeripheralClockControl, + Async, + Blocking, + Cpu, + InterruptConfigurable, + Mode, +}; + +cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + const I2C_LL_INTR_MASK: u32 = 0x1ffff; + } else { + const I2C_LL_INTR_MASK: u32 = 0x3ffff; + } +} + +// Chunk writes/reads by this size +#[cfg(any(esp32, esp32s2))] +const I2C_CHUNK_SIZE: usize = 32; + +#[cfg(not(any(esp32, esp32s2)))] +const I2C_CHUNK_SIZE: usize = 254; + +// on ESP32 there is a chance to get trapped in `wait_for_completion` forever +const MAX_ITERATIONS: u32 = 1_000_000; + +/// I2C-specific transmission errors +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The transmission exceeded the FIFO size. + ExceedingFifo, + /// The acknowledgment check failed. + AckCheckFailed, + /// A timeout occurred during transmission. + TimeOut, + /// The arbitration for the bus was lost. + ArbitrationLost, + /// The execution of the I2C command was incomplete. + ExecIncomplete, + /// The number of commands issued exceeded the limit. + CommandNrExceeded, + /// Zero length read or write operation. + InvalidZeroLength, +} + +/// I2C-specific configuration errors +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError {} + +#[derive(PartialEq)] +// This enum is used to keep track of the last/next operation that was/will be +// performed in an embedded-hal(-async) I2c::transaction. It is used to +// determine whether a START condition should be issued at the start of the +// current operation and whether a read needs an ack or a nack for the final +// byte. +enum OpKind { + Write, + Read, +} + +/// I2C operation. +/// +/// Several operations can be combined as part of a transaction. +pub enum Operation<'a> { + /// Write data from the provided buffer. + Write(&'a [u8]), + + /// Read data into the provided buffer. + Read(&'a mut [u8]), +} + +impl<'a, 'b> From<&'a mut embedded_hal::i2c::Operation<'b>> for Operation<'a> { + fn from(value: &'a mut embedded_hal::i2c::Operation<'b>) -> Self { + match value { + embedded_hal::i2c::Operation::Write(buffer) => Operation::Write(buffer), + embedded_hal::i2c::Operation::Read(buffer) => Operation::Read(buffer), + } + } +} + +impl<'a, 'b> From<&'a mut Operation<'b>> for Operation<'a> { + fn from(value: &'a mut Operation<'b>) -> Self { + match value { + Operation::Write(buffer) => Operation::Write(buffer), + Operation::Read(buffer) => Operation::Read(buffer), + } + } +} + +impl Operation<'_> { + fn is_write(&self) -> bool { + matches!(self, Operation::Write(_)) + } + + fn kind(&self) -> OpKind { + match self { + Operation::Write(_) => OpKind::Write, + Operation::Read(_) => OpKind::Read, + } + } + + fn is_empty(&self) -> bool { + match self { + Operation::Write(buffer) => buffer.is_empty(), + Operation::Read(buffer) => buffer.is_empty(), + } + } +} + +impl embedded_hal::i2c::Error for Error { + fn kind(&self) -> embedded_hal::i2c::ErrorKind { + use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource}; + + match self { + Self::ExceedingFifo => ErrorKind::Overrun, + Self::ArbitrationLost => ErrorKind::ArbitrationLoss, + Self::AckCheckFailed => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown), + _ => ErrorKind::Other, + } + } +} + +/// A generic I2C Command +#[cfg_attr(feature = "debug", derive(Debug))] +enum Command { + Start, + Stop, + End, + Write { + /// This bit is to set an expected ACK value for the transmitter. + ack_exp: Ack, + /// Enables checking the ACK value received against the ack_exp value. + ack_check_en: bool, + /// Length of data (in bytes) to be written. The maximum length is 255, + /// while the minimum is 1. + length: u8, + }, + Read { + /// Indicates whether the receiver will send an ACK after this byte has + /// been received. + ack_value: Ack, + /// Length of data (in bytes) to be read. The maximum length is 255, + /// while the minimum is 1. + length: u8, + }, +} + +enum OperationType { + Write = 0, + Read = 1, +} + +#[derive(Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "debug", derive(Debug))] +enum Ack { + Ack = 0, + Nack = 1, +} +impl From for Ack { + fn from(ack: u32) -> Self { + match ack { + 0 => Ack::Ack, + 1 => Ack::Nack, + _ => unreachable!(), + } + } +} +impl From for u32 { + fn from(ack: Ack) -> u32 { + ack as u32 + } +} + +/// I2C driver configuration +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// The I2C clock frequency. + pub frequency: HertzU32, + + /// I2C SCL timeout period. + /// + /// When the level of SCL remains unchanged for more than `timeout` bus + /// clock cycles, the bus goes to idle state. + /// + /// The default value is about 10 bus clock cycles. + #[doc = ""] + #[cfg_attr( + not(esp32), + doc = "Note that the effective timeout may be longer than the value configured here." + )] + #[cfg_attr(not(esp32), doc = "Configuring `None` disables timeout control.")] + #[cfg_attr(esp32, doc = "Configuring `None` equals to the maximum timeout value.")] + // TODO: when supporting interrupts, document that SCL = high also triggers an interrupt. + pub timeout: Option, +} + +impl Default for Config { + fn default() -> Self { + use fugit::RateExtU32; + Config { + frequency: 100.kHz(), + timeout: Some(10), + } + } +} + +/// I2C driver +pub struct I2c<'d, DM: Mode, T = AnyI2c> { + i2c: PeripheralRef<'d, T>, + phantom: PhantomData, + config: Config, +} + +impl SetConfig for I2c<'_, DM, T> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } +} + +impl embedded_hal_02::blocking::i2c::Read for I2c<'_, Blocking, T> +where + T: Instance, +{ + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read(address, buffer) + } +} + +impl embedded_hal_02::blocking::i2c::Write for I2c<'_, Blocking, T> +where + T: Instance, +{ + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(addr, bytes) + } +} + +impl embedded_hal_02::blocking::i2c::WriteRead for I2c<'_, Blocking, T> +where + T: Instance, +{ + type Error = Error; + + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + self.write_read(address, bytes, buffer) + } +} + +impl embedded_hal::i2c::ErrorType for I2c<'_, DM, T> { + type Error = Error; +} + +impl embedded_hal::i2c::I2c for I2c<'_, DM, T> +where + T: Instance, +{ + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.transaction_impl(address, operations.iter_mut().map(Operation::from)) + .inspect_err(|_| self.internal_recover()) + } +} + +impl<'d, T, DM: Mode> I2c<'d, DM, T> +where + T: Instance, +{ + fn driver(&self) -> Driver<'_> { + Driver { + info: self.i2c.info(), + state: self.i2c.state(), + } + } + + fn internal_recover(&self) { + PeripheralClockControl::reset(self.driver().info.peripheral); + PeripheralClockControl::enable(self.driver().info.peripheral); + + // We know the configuration is valid, we can ignore the result. + _ = self.driver().setup(&self.config); + } + + /// Applies a new configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.driver().setup(config)?; + self.config = *config; + Ok(()) + } + + fn transaction_impl<'a>( + &mut self, + address: u8, + operations: impl Iterator>, + ) -> Result<(), Error> { + let mut last_op: Option = None; + // filter out 0 length read operations + let mut op_iter = operations + .filter(|op| op.is_write() || !op.is_empty()) + .peekable(); + + while let Some(op) = op_iter.next() { + let next_op = op_iter.peek().map(|v| v.kind()); + let kind = op.kind(); + match op { + Operation::Write(buffer) => { + // execute a write operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + self.driver().write_blocking( + address, + buffer, + !matches!(last_op, Some(OpKind::Write)), + next_op.is_none(), + )?; + } + Operation::Read(buffer) => { + // execute a read operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + // - will_continue is true if there is another read operation next + self.driver().read_blocking( + address, + buffer, + !matches!(last_op, Some(OpKind::Read)), + next_op.is_none(), + matches!(next_op, Some(OpKind::Read)), + )?; + } + } + + last_op = Some(kind); + } + + Ok(()) + } + + /// Connect a pin to the I2C SDA signal. + pub fn with_sda(self, sda: impl Peripheral

+ 'd) -> Self { + let info = self.driver().info; + let input = info.sda_input; + let output = info.sda_output; + self.with_pin(sda, input, output) + } + + /// Connect a pin to the I2C SCL signal. + pub fn with_scl(self, scl: impl Peripheral

+ 'd) -> Self { + let info = self.driver().info; + let input = info.scl_input; + let output = info.scl_output; + self.with_pin(scl, input, output) + } + + fn with_pin( + self, + pin: impl Peripheral

+ 'd, + input: InputSignal, + output: OutputSignal, + ) -> Self { + crate::into_mapped_ref!(pin); + // avoid the pin going low during configuration + pin.set_output_high(true, private::Internal); + + pin.set_to_open_drain_output(private::Internal); + pin.enable_input(true, private::Internal); + pin.pull_direction(Pull::Up, private::Internal); + + input.connect_to(&mut pin); + output.connect_to(&mut pin); + + self + } +} + +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

+ 'd, config: Config) -> Self { + Self::new_typed(i2c.map_into(), config) + } +} + +impl<'d, T> I2c<'d, Blocking, T> +where + T: Instance, +{ + /// 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

+ 'd, config: Config) -> Self { + crate::into_ref!(i2c); + + let i2c = I2c { + i2c, + phantom: PhantomData, + config, + }; + + PeripheralClockControl::reset(i2c.driver().info.peripheral); + PeripheralClockControl::enable(i2c.driver().info.peripheral); + + unwrap!(i2c.driver().setup(&i2c.config)); + i2c + } + + // TODO: missing interrupt APIs + + /// Configures the I2C peripheral to operate in asynchronous mode. + pub fn into_async(mut self) -> I2c<'d, Async, T> { + self.set_interrupt_handler(self.driver().info.async_handler); + + I2c { + i2c: self.i2c, + phantom: PhantomData, + config: self.config, + } + } + + /// Writes bytes to slave with address `address` + pub fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { + self.driver() + .write_blocking(address, buffer, true, true) + .inspect_err(|_| self.internal_recover()) + } + + /// Reads enough bytes from slave with `address` to fill `buffer` + pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.driver() + .read_blocking(address, buffer, true, true, false) + .inspect_err(|_| self.internal_recover()) + } + + /// Writes bytes to slave with address `address` and then reads enough bytes + /// to fill `buffer` *in a single transaction* + pub fn write_read( + &mut self, + address: u8, + write_buffer: &[u8], + read_buffer: &mut [u8], + ) -> Result<(), Error> { + self.driver() + .write_blocking(address, write_buffer, true, read_buffer.is_empty()) + .inspect_err(|_| self.internal_recover())?; + + self.driver() + .read_blocking(address, read_buffer, true, true, false) + .inspect_err(|_| self.internal_recover())?; + + Ok(()) + } + + /// Execute the provided operations on the I2C bus. + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This + /// is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each + /// other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is + /// sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an + /// acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 + /// to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + pub fn transaction<'a>( + &mut self, + address: u8, + operations: impl IntoIterator>, + ) -> Result<(), Error> { + self.transaction_impl(address, operations.into_iter().map(Operation::from)) + .inspect_err(|_| self.internal_recover()) + } +} + +impl private::Sealed for I2c<'_, Blocking, T> where T: Instance {} + +impl InterruptConfigurable for I2c<'_, Blocking, T> +where + T: Instance, +{ + fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { + let interrupt = self.driver().info.interrupt; + for core in Cpu::other() { + crate::interrupt::disable(core, interrupt); + } + unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(interrupt, handler.priority())); + } +} + +#[cfg_attr(esp32, allow(dead_code))] +pub(crate) enum Event { + EndDetect, + TxComplete, + #[cfg(not(any(esp32, esp32s2)))] + TxFifoWatermark, +} + +#[cfg(not(esp32))] +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct I2cFuture<'a> { + event: Event, + info: &'a Info, + state: &'a State, +} + +#[cfg(not(esp32))] +impl<'a> I2cFuture<'a> { + pub fn new(event: Event, info: &'a Info, state: &'a State) -> Self { + info.register_block().int_ena().modify(|_, w| { + let w = match event { + Event::EndDetect => w.end_detect().set_bit(), + Event::TxComplete => w.trans_complete().set_bit(), + #[cfg(not(any(esp32, esp32s2)))] + Event::TxFifoWatermark => w.txfifo_wm().set_bit(), + }; + + w.arbitration_lost().set_bit(); + w.time_out().set_bit(); + + #[cfg(esp32)] + w.ack_err().set_bit(); + #[cfg(not(esp32))] + w.nack().set_bit(); + + w + }); + + Self { event, state, info } + } + + fn event_bit_is_clear(&self) -> bool { + let r = self.info.register_block().int_ena().read(); + + match self.event { + Event::EndDetect => r.end_detect().bit_is_clear(), + Event::TxComplete => r.trans_complete().bit_is_clear(), + #[cfg(not(any(esp32, esp32s2)))] + Event::TxFifoWatermark => r.txfifo_wm().bit_is_clear(), + } + } + + fn check_error(&self) -> Result<(), Error> { + let r = self.info.register_block().int_raw().read(); + + if r.arbitration_lost().bit_is_set() { + return Err(Error::ArbitrationLost); + } + + if r.time_out().bit_is_set() { + return Err(Error::TimeOut); + } + + #[cfg(not(esp32))] + if r.nack().bit_is_set() { + return Err(Error::AckCheckFailed); + } + + #[cfg(esp32)] + if r.ack_err().bit_is_set() { + return Err(Error::AckCheckFailed); + } + + #[cfg(not(esp32))] + if r.trans_complete().bit_is_set() + && self + .info + .register_block() + .sr() + .read() + .resp_rec() + .bit_is_clear() + { + return Err(Error::AckCheckFailed); + } + + Ok(()) + } +} + +#[cfg(not(esp32))] +impl core::future::Future for I2cFuture<'_> { + type Output = Result<(), Error>; + + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + self.state.waker.register(ctx.waker()); + + let error = self.check_error(); + + if error.is_err() { + return Poll::Ready(error); + } + + if self.event_bit_is_clear() { + Poll::Ready(Ok(())) + } else { + Poll::Pending + } + } +} + +impl<'d, T> I2c<'d, Async, T> +where + T: Instance, +{ + /// Configure the I2C peripheral to operate in blocking mode. + pub fn into_blocking(self) -> I2c<'d, Blocking, T> { + crate::interrupt::disable(Cpu::current(), self.driver().info.interrupt); + + I2c { + i2c: self.i2c, + phantom: PhantomData, + config: self.config, + } + } + + /// Writes bytes to slave with address `address` + pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { + self.driver() + .write(address, buffer, true, true) + .await + .inspect_err(|_| self.internal_recover()) + } + + /// Reads enough bytes from slave with `address` to fill `buffer` + pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + self.driver() + .read(address, buffer, true, true, false) + .await + .inspect_err(|_| self.internal_recover()) + } + + /// Writes bytes to slave with address `address` and then reads enough + /// bytes to fill `buffer` *in a single transaction* + pub async fn write_read( + &mut self, + address: u8, + write_buffer: &[u8], + read_buffer: &mut [u8], + ) -> Result<(), Error> { + self.driver() + .write(address, write_buffer, true, read_buffer.is_empty()) + .await + .inspect_err(|_| self.internal_recover())?; + + self.driver() + .read(address, read_buffer, true, true, false) + .await + .inspect_err(|_| self.internal_recover())?; + + Ok(()) + } + + /// Execute the provided operations on the I2C bus as a single + /// transaction. + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This + /// is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each + /// other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is + /// sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an + /// acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 + /// to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + pub async fn transaction<'a>( + &mut self, + address: u8, + operations: impl IntoIterator>, + ) -> Result<(), Error> { + self.transaction_impl_async(address, operations.into_iter().map(Operation::from)) + .await + .inspect_err(|_| self.internal_recover()) + } + + async fn transaction_impl_async<'a>( + &mut self, + address: u8, + operations: impl Iterator>, + ) -> Result<(), Error> { + let mut last_op: Option = None; + // filter out 0 length read operations + let mut op_iter = operations + .filter(|op| op.is_write() || !op.is_empty()) + .peekable(); + + while let Some(op) = op_iter.next() { + let next_op = op_iter.peek().map(|v| v.kind()); + let kind = op.kind(); + match op { + Operation::Write(buffer) => { + // execute a write operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + self.driver() + .write( + address, + buffer, + !matches!(last_op, Some(OpKind::Write)), + next_op.is_none(), + ) + .await?; + } + Operation::Read(buffer) => { + // execute a read operation: + // - issue START/RSTART if op is different from previous + // - issue STOP if op is the last one + // - will_continue is true if there is another read operation next + self.driver() + .read( + address, + buffer, + !matches!(last_op, Some(OpKind::Read)), + next_op.is_none(), + matches!(next_op, Some(OpKind::Read)), + ) + .await?; + } + } + + last_op = Some(kind); + } + + Ok(()) + } +} + +impl<'d, T> embedded_hal_async::i2c::I2c for I2c<'d, Async, T> +where + T: Instance, +{ + async fn transaction( + &mut self, + address: u8, + operations: &mut [EhalOperation<'_>], + ) -> Result<(), Self::Error> { + self.transaction_impl_async(address, operations.iter_mut().map(Operation::from)) + .await + .inspect_err(|_| self.internal_recover()) + } +} + +fn async_handler(info: &Info, state: &State) { + let regs = info.register_block(); + regs.int_ena().modify(|_, w| { + w.end_detect().clear_bit(); + w.trans_complete().clear_bit(); + w.arbitration_lost().clear_bit(); + w.time_out().clear_bit(); + + #[cfg(not(any(esp32, esp32s2)))] + w.txfifo_wm().clear_bit(); + + w.nack().clear_bit() + }); + + state.waker.wake(); +} + +/// Sets the filter with a supplied threshold in clock cycles for which a +/// pulse must be present to pass the filter +fn set_filter( + register_block: &RegisterBlock, + sda_threshold: Option, + scl_threshold: Option, +) { + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s2))] { + register_block.sda_filter_cfg().modify(|_, w| { + if let Some(threshold) = sda_threshold { + unsafe { w.sda_filter_thres().bits(threshold) }; + } + w.sda_filter_en().bit(sda_threshold.is_some()) + }); + register_block.scl_filter_cfg().modify(|_, w| { + if let Some(threshold) = scl_threshold { + unsafe { w.scl_filter_thres().bits(threshold) }; + } + w.scl_filter_en().bit(scl_threshold.is_some()) + }); + } else { + register_block.filter_cfg().modify(|_, w| { + if let Some(threshold) = sda_threshold { + unsafe { w.sda_filter_thres().bits(threshold) }; + } + if let Some(threshold) = scl_threshold { + unsafe { w.scl_filter_thres().bits(threshold) }; + } + w.sda_filter_en().bit(sda_threshold.is_some()); + w.scl_filter_en().bit(scl_threshold.is_some()) + }); + } + } +} + +#[allow(clippy::too_many_arguments, unused)] +/// Configures the clock and timing parameters for the I2C peripheral. +fn configure_clock( + register_block: &RegisterBlock, + sclk_div: u32, + scl_low_period: u32, + scl_high_period: u32, + scl_wait_high_period: u32, + sda_hold_time: u32, + sda_sample_time: u32, + scl_rstart_setup_time: u32, + scl_stop_setup_time: u32, + scl_start_hold_time: u32, + scl_stop_hold_time: u32, + timeout: Option, +) { + unsafe { + // divider + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] + register_block.clk_conf().modify(|_, w| { + w.sclk_sel().clear_bit(); + w.sclk_div_num().bits((sclk_div - 1) as u8) + }); + + // scl period + register_block + .scl_low_period() + .write(|w| w.scl_low_period().bits(scl_low_period as u16)); + + register_block.scl_high_period().write(|w| { + #[cfg(not(esp32))] // ESP32 does not have a wait_high field + w.scl_wait_high_period() + .bits(scl_wait_high_period.try_into().unwrap()); + w.scl_high_period().bits(scl_high_period as u16) + }); + + // sda sample + register_block + .sda_hold() + .write(|w| w.time().bits(sda_hold_time as u16)); + register_block + .sda_sample() + .write(|w| w.time().bits(sda_sample_time as u16)); + + // setup + register_block + .scl_rstart_setup() + .write(|w| w.time().bits(scl_rstart_setup_time as u16)); + register_block + .scl_stop_setup() + .write(|w| w.time().bits(scl_stop_setup_time as u16)); + + // hold + register_block + .scl_start_hold() + .write(|w| w.time().bits(scl_start_hold_time as u16)); + register_block + .scl_stop_hold() + .write(|w| w.time().bits(scl_stop_hold_time as u16)); + + // The ESP32 variant does not have an enable flag for the + // timeout mechanism + cfg_if::cfg_if! { + if #[cfg(esp32)] { + register_block + .to() + .write(|w| w.time_out().bits(unwrap!(timeout))); + } else { + register_block + .to() + .write(|w| w.time_out_en().bit(timeout.is_some()) + .time_out_value() + .bits(timeout.unwrap_or(1) as _) + ); + } + } + } +} + +/// Peripheral data describing a particular I2C instance. +#[non_exhaustive] +pub struct Info { + /// Pointer to the register block for this I2C instance. + /// + /// Use [Self::register_block] to access the register block. + pub register_block: *const RegisterBlock, + + /// System peripheral marker. + pub peripheral: crate::system::Peripheral, + + /// Interrupt handler for the asynchronous operations of this I2C instance. + pub async_handler: InterruptHandler, + + /// Interrupt for this I2C instance. + pub interrupt: Interrupt, + + /// SCL output signal. + pub scl_output: OutputSignal, + + /// SCL input signal. + pub scl_input: InputSignal, + + /// SDA output signal. + pub sda_output: OutputSignal, + + /// SDA input signal. + pub sda_input: InputSignal, +} + +impl Info { + /// Returns the register block for this I2C instance. + pub fn register_block(&self) -> &RegisterBlock { + unsafe { &*self.register_block } + } +} + +#[allow(dead_code)] // Some versions don't need `state` +struct Driver<'a> { + info: &'a Info, + state: &'a State, +} + +impl Driver<'_> { + fn register_block(&self) -> &RegisterBlock { + self.info.register_block() + } + + /// Configures the I2C peripheral with the specified frequency, clocks, and + /// optional timeout. + fn setup(&self, config: &Config) -> Result<(), ConfigError> { + self.register_block().ctr().write(|w| { + // Set I2C controller to master mode + w.ms_mode().set_bit(); + // Use open drain output for SDA and SCL + w.sda_force_out().set_bit(); + w.scl_force_out().set_bit(); + // Use Most Significant Bit first for sending and receiving data + w.tx_lsb_first().clear_bit(); + w.rx_lsb_first().clear_bit(); + // Ensure that clock is enabled + w.clk_en().set_bit() + }); + + #[cfg(esp32s2)] + self.register_block() + .ctr() + .modify(|_, w| w.ref_always_on().set_bit()); + + // Configure filter + // FIXME if we ever change this we need to adapt `set_frequency` for ESP32 + set_filter(self.register_block(), Some(7), Some(7)); + + // Configure frequency + let clocks = Clocks::get(); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let clock = clocks.i2c_clock.convert(); + } else if #[cfg(esp32s2)] { + let clock = clocks.apb_clock.convert(); + } else { + let clock = clocks.xtal_clock.convert(); + } + } + self.set_frequency(clock, config.frequency, config.timeout); + + self.update_config(); + + // Reset entire peripheral (also resets fifo) + self.reset(); + + Ok(()) + } + + /// Resets the I2C controller (FIFO + FSM + command list) + fn reset(&self) { + // Reset the FSM + // (the option to reset the FSM is not available + // for the ESP32) + #[cfg(not(esp32))] + self.register_block() + .ctr() + .modify(|_, w| w.fsm_rst().set_bit()); + + // Clear all I2C interrupts + self.register_block() + .int_clr() + .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); + + // Reset fifo + self.reset_fifo(); + + // Reset the command list + self.reset_command_list(); + } + + /// Resets the I2C peripheral's command registers + fn reset_command_list(&self) { + // Confirm that all commands that were configured were actually executed + for cmd in self.register_block().comd_iter() { + cmd.reset(); + } + } + + #[cfg(esp32)] + /// Sets the frequency of the I2C interface by calculating and applying the + /// associated timings - corresponds to i2c_ll_cal_bus_clk and + /// i2c_ll_set_bus_timing in ESP-IDF + fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { + let source_clk = source_clk.raw(); + let bus_freq = bus_freq.raw(); + + let half_cycle: u32 = source_clk / bus_freq / 2; + let scl_low = half_cycle; + let scl_high = half_cycle; + let sda_hold = half_cycle / 2; + let sda_sample = scl_high / 2; + let setup = half_cycle; + let hold = half_cycle; + let timeout = timeout.map_or(Some(0xF_FFFF), |to_bus| { + Some((to_bus * 2 * half_cycle).min(0xF_FFFF)) + }); + + // SCL period. According to the TRM, we should always subtract 1 to SCL low + // period + let scl_low = scl_low - 1; + // Still according to the TRM, if filter is not enbled, we have to subtract 7, + // if SCL filter is enabled, we have to subtract: + // 8 if SCL filter is between 0 and 2 (included) + // 6 + SCL threshold if SCL filter is between 3 and 7 (included) + // to SCL high period + let mut scl_high = scl_high; + // In the "worst" case, we will subtract 13, make sure the result will still be + // correct + + // FIXME since we always set the filter threshold to 7 we don't need conditional + // code here once that changes we need the conditional code here + scl_high -= 7 + 6; + + // if (filter_cfg_en) { + // if (thres <= 2) { + // scl_high -= 8; + // } else { + // assert(hw->scl_filter_cfg.thres <= 7); + // scl_high -= thres + 6; + // } + // } else { + // scl_high -= 7; + //} + + let scl_high_period = scl_high; + let scl_low_period = scl_low; + // sda sample + let sda_hold_time = sda_hold; + let sda_sample_time = sda_sample; + // setup + let scl_rstart_setup_time = setup; + let scl_stop_setup_time = setup; + // hold + let scl_start_hold_time = hold; + let scl_stop_hold_time = hold; + + configure_clock( + self.register_block(), + 0, + scl_low_period, + scl_high_period, + 0, + sda_hold_time, + sda_sample_time, + scl_rstart_setup_time, + scl_stop_setup_time, + scl_start_hold_time, + scl_stop_hold_time, + timeout, + ); + } + + #[cfg(esp32s2)] + /// Sets the frequency of the I2C interface by calculating and applying the + /// associated timings - corresponds to i2c_ll_cal_bus_clk and + /// i2c_ll_set_bus_timing in ESP-IDF + fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { + let source_clk = source_clk.raw(); + let bus_freq = bus_freq.raw(); + + let half_cycle: u32 = source_clk / bus_freq / 2; + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + let scl_high = half_cycle / 2 + 2; + let scl_wait_high = half_cycle - scl_high; + let sda_hold = half_cycle / 2; + // scl_wait_high < sda_sample <= scl_high + let sda_sample = half_cycle / 2 - 1; + let setup = half_cycle; + let hold = half_cycle; + + // scl period + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold; + let sda_sample_time = sda_sample; + // setup + let scl_rstart_setup_time = setup; + let scl_stop_setup_time = setup; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold; + + configure_clock( + self.register_block(), + 0, + scl_low_period, + scl_high_period, + scl_wait_high_period, + sda_hold_time, + sda_sample_time, + scl_rstart_setup_time, + scl_stop_setup_time, + scl_start_hold_time, + scl_stop_hold_time, + timeout.map(|to_bus| (to_bus * 2 * half_cycle).min(0xFF_FFFF)), + ); + } + + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] + /// Sets the frequency of the I2C interface by calculating and applying the + /// associated timings - corresponds to i2c_ll_cal_bus_clk and + /// i2c_ll_set_bus_timing in ESP-IDF + fn set_frequency(&self, source_clk: HertzU32, bus_freq: HertzU32, timeout: Option) { + let source_clk = source_clk.raw(); + let bus_freq = bus_freq.raw(); + + let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; + let sclk_freq: u32 = source_clk / clkm_div; + let half_cycle: u32 = sclk_freq / bus_freq / 2; + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + // Make 80KHz as a boundary here, because when working at lower frequency, too + // much scl_wait_high will faster the frequency according to some + // hardware behaviors. + let scl_wait_high = if bus_freq >= 80 * 1000 { + half_cycle / 2 - 2 + } else { + half_cycle / 4 + }; + let scl_high = half_cycle - scl_wait_high; + let sda_hold = half_cycle / 4; + let sda_sample = half_cycle / 2 + scl_wait_high; + let setup = half_cycle; + let hold = half_cycle; + + // According to the Technical Reference Manual, the following timings must be + // subtracted by 1. However, according to the practical measurement and + // some hardware behaviour, if wait_high_period and scl_high minus one. + // The SCL frequency would be a little higher than expected. Therefore, the + // solution here is not to minus scl_high as well as scl_wait high, and + // the frequency will be absolutely accurate to all frequency + // to some extent. + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold - 1; + let sda_sample_time = sda_sample - 1; + // setup + let scl_rstart_setup_time = setup - 1; + let scl_stop_setup_time = setup - 1; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold - 1; + + configure_clock( + self.register_block(), + clkm_div, + scl_low_period, + scl_high_period, + scl_wait_high_period, + sda_hold_time, + sda_sample_time, + scl_rstart_setup_time, + scl_stop_setup_time, + scl_start_hold_time, + scl_stop_hold_time, + timeout.map(|to_bus| { + let to_peri = (to_bus * 2 * half_cycle).max(1); + let log2 = to_peri.ilog2(); + // Round up so that we don't shorten timeouts. + let raw = if to_peri != 1 << log2 { log2 + 1 } else { log2 }; + raw.min(0x1F) + }), + ); + } + + #[cfg(any(esp32, esp32s2))] + async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { + if buffer.len() > 32 { + panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); + } + + self.wait_for_completion(false).await?; + + for byte in buffer.iter_mut() { + *byte = read_fifo(self.register_block()); + } + + Ok(()) + } + + #[cfg(not(any(esp32, esp32s2)))] + async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> { + self.read_all_from_fifo_blocking(buffer) + } + + /// Configures the I2C peripheral for a write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `cmd_iterator` is an iterator over the command registers. + fn setup_write<'a, I>( + &self, + addr: u8, + bytes: &[u8], + start: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { + // if start is true we can only send 254 additional bytes with the address as + // the first + let max_len = if start { 254usize } else { 255usize }; + if bytes.len() > max_len { + // we could support more by adding multiple write operations + return Err(Error::ExceedingFifo); + } + + let write_len = if start { bytes.len() + 1 } else { bytes.len() }; + // don't issue write if there is no data to write + if write_len > 0 { + // WRITE command + add_cmd( + cmd_iterator, + Command::Write { + ack_exp: Ack::Ack, + ack_check_en: true, + length: write_len as u8, + }, + )?; + } + + self.update_config(); + + if start { + // Load address and R/W bit into FIFO + write_fifo( + self.register_block(), + addr << 1 | OperationType::Write as u8, + ); + } + Ok(()) + } + + /// Configures the I2C peripheral for a read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. + fn setup_read<'a, I>( + &self, + addr: u8, + buffer: &mut [u8], + start: bool, + will_continue: bool, + cmd_iterator: &mut I, + ) -> Result<(), Error> + where + I: Iterator, + { + if buffer.is_empty() { + return Err(Error::InvalidZeroLength); + } + let (max_len, initial_len) = if will_continue { + (255usize, buffer.len()) + } else { + (254usize, buffer.len() - 1) + }; + if buffer.len() > max_len { + // we could support more by adding multiple read operations + return Err(Error::ExceedingFifo); + } + + if start { + // WRITE command + add_cmd( + cmd_iterator, + Command::Write { + ack_exp: Ack::Ack, + ack_check_en: true, + length: 1, + }, + )?; + } + + if initial_len > 0 { + // READ command + add_cmd( + cmd_iterator, + Command::Read { + ack_value: Ack::Ack, + length: initial_len as u8, + }, + )?; + } + + if !will_continue { + // this is the last read so we need to nack the last byte + // READ w/o ACK + add_cmd( + cmd_iterator, + Command::Read { + ack_value: Ack::Nack, + length: 1, + }, + )?; + } + + self.update_config(); + + if start { + // Load address and R/W bit into FIFO + write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8); + } + Ok(()) + } + + #[cfg(not(any(esp32, esp32s2)))] + /// Reads all bytes from the RX FIFO. + fn read_all_from_fifo_blocking(&self, buffer: &mut [u8]) -> Result<(), Error> { + // Read bytes from FIFO + // FIXME: Handle case where less data has been provided by the slave than + // requested? Or is this prevented from a protocol perspective? + for byte in buffer.iter_mut() { + loop { + self.check_errors()?; + + let reg = self.register_block().fifo_st().read(); + if reg.rxfifo_raddr().bits() != reg.rxfifo_waddr().bits() { + break; + } + } + + *byte = read_fifo(self.register_block()); + } + + Ok(()) + } + + #[cfg(any(esp32, esp32s2))] + /// Reads all bytes from the RX FIFO. + fn read_all_from_fifo_blocking(&self, buffer: &mut [u8]) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if buffer.len() > 32 { + panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes"); + } + + // wait for completion - then we can just read the data from FIFO + // once we change to non-fifo mode to support larger transfers that + // won't work anymore + self.wait_for_completion_blocking(false)?; + + // Read bytes from FIFO + // FIXME: Handle case where less data has been provided by the slave than + // requested? Or is this prevented from a protocol perspective? + for byte in buffer.iter_mut() { + *byte = read_fifo(self.register_block()); + } + + Ok(()) + } + + /// Clears all pending interrupts for the I2C peripheral. + fn clear_all_interrupts(&self) { + self.register_block() + .int_clr() + .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) }); + } + + #[cfg(any(esp32, esp32s2))] + async fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { + if start_index >= bytes.len() { + return Ok(()); + } + + for b in bytes { + write_fifo(self.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + #[cfg(not(any(esp32, esp32s2)))] + async fn write_remaining_tx_fifo(&self, start_index: usize, bytes: &[u8]) -> Result<(), Error> { + let mut index = start_index; + loop { + self.check_errors()?; + + I2cFuture::new(Event::TxFifoWatermark, self.info, self.state).await?; + + self.register_block() + .int_clr() + .write(|w| w.txfifo_wm().clear_bit_by_one()); + + I2cFuture::new(Event::TxFifoWatermark, self.info, self.state).await?; + + if index >= bytes.len() { + break Ok(()); + } + + write_fifo(self.register_block(), bytes[index]); + index += 1; + } + } + + #[cfg(not(esp32))] + async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { + self.check_errors()?; + + if end_only { + I2cFuture::new(Event::EndDetect, self.info, self.state).await?; + } else { + let res = embassy_futures::select::select( + I2cFuture::new(Event::TxComplete, self.info, self.state), + I2cFuture::new(Event::EndDetect, self.info, self.state), + ) + .await; + + match res { + embassy_futures::select::Either::First(res) => res?, + embassy_futures::select::Either::Second(res) => res?, + } + } + self.check_all_commands_done()?; + + Ok(()) + } + + #[cfg(esp32)] + async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> { + // for ESP32 we need a timeout here but wasting a timer seems unnecessary + // given the short time we spend here + + let mut tout = MAX_ITERATIONS / 10; // adjust the timeout because we are yielding in the loop + loop { + let interrupts = self.register_block().int_raw().read(); + + self.check_errors()?; + + // Handle completion cases + // A full transmission was completed (either a STOP condition or END was + // processed) + if (!end_only && interrupts.trans_complete().bit_is_set()) + || interrupts.end_detect().bit_is_set() + { + break; + } + + tout -= 1; + if tout == 0 { + return Err(Error::TimeOut); + } + + embassy_futures::yield_now().await; + } + self.check_all_commands_done()?; + Ok(()) + } + + /// Waits for the completion of an I2C transaction. + fn wait_for_completion_blocking(&self, end_only: bool) -> Result<(), Error> { + let mut tout = MAX_ITERATIONS; + loop { + let interrupts = self.register_block().int_raw().read(); + + self.check_errors()?; + + // Handle completion cases + // A full transmission was completed (either a STOP condition or END was + // processed) + if (!end_only && interrupts.trans_complete().bit_is_set()) + || interrupts.end_detect().bit_is_set() + { + break; + } + + tout -= 1; + if tout == 0 { + return Err(Error::TimeOut); + } + } + self.check_all_commands_done()?; + Ok(()) + } + + /// Checks whether all I2C commands have completed execution. + fn check_all_commands_done(&self) -> Result<(), Error> { + // NOTE: on esp32 executing the end command generates the end_detect interrupt + // but does not seem to clear the done bit! So we don't check the done + // status of an end command + for cmd_reg in self.register_block().comd_iter() { + let cmd = cmd_reg.read(); + + if cmd.bits() != 0x0 && !cmd.opcode().is_end() && !cmd.command_done().bit_is_set() { + return Err(Error::ExecIncomplete); + } + } + + Ok(()) + } + + /// Checks for I2C transmission errors and handles them. + /// + /// This function inspects specific I2C-related interrupts to detect errors + /// during communication, such as timeouts, failed acknowledgments, or + /// arbitration loss. If an error is detected, the function handles it + /// by resetting the I2C peripheral to clear the error condition and then + /// returns an appropriate error. + fn check_errors(&self) -> Result<(), Error> { + let interrupts = self.register_block().int_raw().read(); + + // The ESP32 variant has a slightly different interrupt naming + // scheme! + cfg_if::cfg_if! { + if #[cfg(esp32)] { + // Handle error cases + let retval = if interrupts.time_out().bit_is_set() { + Err(Error::TimeOut) + } else if interrupts.nack().bit_is_set() { + Err(Error::AckCheckFailed) + } else if interrupts.arbitration_lost().bit_is_set() { + Err(Error::ArbitrationLost) + } else { + Ok(()) + }; + } else { + // Handle error cases + let retval = if interrupts.time_out().bit_is_set() { + Err(Error::TimeOut) + } else if interrupts.nack().bit_is_set() { + Err(Error::AckCheckFailed) + } else if interrupts.arbitration_lost().bit_is_set() { + Err(Error::ArbitrationLost) + } else if interrupts.trans_complete().bit_is_set() && self.register_block().sr().read().resp_rec().bit_is_clear() { + Err(Error::AckCheckFailed) + } else { + Ok(()) + }; + } + } + + if retval.is_err() { + self.reset(); + } + + retval + } + + /// Updates the configuration of the I2C peripheral. + /// + /// This function ensures that the configuration values, such as clock + /// settings, SDA/SCL filtering, timeouts, and other operational + /// parameters, which are configured in other functions, are properly + /// propagated to the I2C hardware. This step is necessary to synchronize + /// the software-configured settings with the peripheral's internal + /// registers, ensuring that the hardware behaves according to the + /// current configuration. + fn update_config(&self) { + // Ensure that the configuration of the peripheral is correctly propagated + // (only necessary for C2, C3, C6, H2 and S3 variant) + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] + self.register_block() + .ctr() + .modify(|_, w| w.conf_upgate().set_bit()); + } + + /// Starts an I2C transmission. + fn start_transmission(&self) { + // Start transmission + self.register_block() + .ctr() + .modify(|_, w| w.trans_start().set_bit()); + } + + #[cfg(not(any(esp32, esp32s2)))] + /// Fills the TX FIFO with data from the provided slice. + fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { + let mut index = 0; + while index < bytes.len() + && !self + .register_block() + .int_raw() + .read() + .txfifo_ovf() + .bit_is_set() + { + write_fifo(self.register_block(), bytes[index]); + index += 1; + } + if self + .register_block() + .int_raw() + .read() + .txfifo_ovf() + .bit_is_set() + { + index -= 1; + self.register_block() + .int_clr() + .write(|w| w.txfifo_ovf().clear_bit_by_one()); + } + index + } + + #[cfg(not(any(esp32, esp32s2)))] + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. + fn write_remaining_tx_fifo_blocking( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + let mut index = start_index; + loop { + self.check_errors()?; + + while !self + .register_block() + .int_raw() + .read() + .txfifo_wm() + .bit_is_set() + { + self.check_errors()?; + } + + self.register_block() + .int_clr() + .write(|w| w.txfifo_wm().clear_bit_by_one()); + + while !self + .register_block() + .int_raw() + .read() + .txfifo_wm() + .bit_is_set() + { + self.check_errors()?; + } + + if index >= bytes.len() { + break Ok(()); + } + + write_fifo(self.register_block(), bytes[index]); + index += 1; + } + } + + #[cfg(any(esp32, esp32s2))] + /// Fills the TX FIFO with data from the provided slice. + fn fill_tx_fifo(&self, bytes: &[u8]) -> usize { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if bytes.len() > 31 { + panic!("On ESP32 and ESP32-S2 the max I2C transfer is limited to 31 bytes"); + } + + for b in bytes { + write_fifo(self.register_block(), *b); + } + + bytes.len() + } + + #[cfg(any(esp32, esp32s2))] + /// Writes remaining data from byte slice to the TX FIFO from the specified + /// index. + fn write_remaining_tx_fifo_blocking( + &self, + start_index: usize, + bytes: &[u8], + ) -> Result<(), Error> { + // on ESP32/ESP32-S2 we currently don't support I2C transactions larger than the + // FIFO apparently it would be possible by using non-fifo mode + // see https://github.com/espressif/arduino-esp32/blob/7e9afe8c5ed7b5bf29624a5cd6e07d431c027b97/cores/esp32/esp32-hal-i2c.c#L615 + + if start_index >= bytes.len() { + return Ok(()); + } + + // this is only possible when writing the I2C address in release mode + // from [perform_write_read] + for b in bytes { + write_fifo(self.register_block(), *b); + self.check_errors()?; + } + + Ok(()) + } + + /// Resets the transmit and receive FIFO buffers + #[cfg(not(esp32))] + fn reset_fifo(&self) { + // First, reset the fifo buffers + self.register_block().fifo_conf().modify(|_, w| unsafe { + w.tx_fifo_rst().set_bit(); + w.rx_fifo_rst().set_bit(); + w.nonfifo_en().clear_bit(); + w.fifo_prt_en().set_bit(); + w.rxfifo_wm_thrhd().bits(1); + w.txfifo_wm_thrhd().bits(8) + }); + + self.register_block().fifo_conf().modify(|_, w| { + w.tx_fifo_rst().clear_bit(); + w.rx_fifo_rst().clear_bit() + }); + + self.register_block().int_clr().write(|w| { + w.rxfifo_wm().clear_bit_by_one(); + w.txfifo_wm().clear_bit_by_one() + }); + + self.update_config(); + } + + /// Resets the transmit and receive FIFO buffers + #[cfg(esp32)] + fn reset_fifo(&self) { + // First, reset the fifo buffers + self.register_block().fifo_conf().modify(|_, w| unsafe { + w.tx_fifo_rst().set_bit(); + w.rx_fifo_rst().set_bit(); + w.nonfifo_en().clear_bit(); + w.nonfifo_rx_thres().bits(1); + w.nonfifo_tx_thres().bits(32) + }); + + self.register_block().fifo_conf().modify(|_, w| { + w.tx_fifo_rst().clear_bit(); + w.rx_fifo_rst().clear_bit() + }); + + self.register_block() + .int_clr() + .write(|w| w.rxfifo_full().clear_bit_by_one()); + } + + fn start_write_operation( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + ) -> Result { + self.reset_fifo(); + self.reset_command_list(); + let cmd_iterator = &mut self.register_block().comd_iter(); + + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + + self.setup_write(address, bytes, start, cmd_iterator)?; + + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + let index = self.fill_tx_fifo(bytes); + self.start_transmission(); + + Ok(index) + } + + /// Executes an I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. + fn start_read_operation( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + ) -> Result<(), Error> { + self.reset_fifo(); + self.reset_command_list(); + + let cmd_iterator = &mut self.register_block().comd_iter(); + + if start { + add_cmd(cmd_iterator, Command::Start)?; + } + + self.setup_read(address, buffer, start, will_continue, cmd_iterator)?; + + add_cmd( + cmd_iterator, + if stop { Command::Stop } else { Command::End }, + )?; + self.start_transmission(); + Ok(()) + } + + /// Executes an I2C write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `cmd_iterator` is an iterator over the command registers. + fn write_operation_blocking( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + ) -> Result<(), Error> { + self.clear_all_interrupts(); + + // Short circuit for zero length writes without start or end as that would be an + // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 + if bytes.is_empty() && !start && !stop { + return Ok(()); + } + + let index = self.start_write_operation(address, bytes, start, stop)?; + // Fill the FIFO with the remaining bytes: + self.write_remaining_tx_fifo_blocking(index, bytes)?; + self.wait_for_completion_blocking(!stop)?; + Ok(()) + } + + /// Executes an I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. + fn read_operation_blocking( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + ) -> Result<(), Error> { + self.clear_all_interrupts(); + + // Short circuit for zero length reads as that would be an invalid operation + // read lengths in the TRM (at least for ESP32-S3) are 1-255 + if buffer.is_empty() { + return Ok(()); + } + + self.start_read_operation(address, buffer, start, stop, will_continue)?; + self.read_all_from_fifo_blocking(buffer)?; + self.wait_for_completion_blocking(!stop)?; + Ok(()) + } + + /// Executes an async I2C write operation. + /// - `addr` is the address of the slave device. + /// - `bytes` is the data two be sent. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `cmd_iterator` is an iterator over the command registers. + async fn write_operation( + &self, + address: u8, + bytes: &[u8], + start: bool, + stop: bool, + ) -> Result<(), Error> { + self.clear_all_interrupts(); + + // Short circuit for zero length writes without start or end as that would be an + // invalid operation write lengths in the TRM (at least for ESP32-S3) are 1-255 + if bytes.is_empty() && !start && !stop { + return Ok(()); + } + + let index = self.start_write_operation(address, bytes, start, stop)?; + // Fill the FIFO with the remaining bytes: + self.write_remaining_tx_fifo(index, bytes).await?; + self.wait_for_completion(!stop).await?; + Ok(()) + } + + /// Executes an async I2C read operation. + /// - `addr` is the address of the slave device. + /// - `buffer` is the buffer to store the read data. + /// - `start` indicates whether the operation should start by a START + /// condition and sending the address. + /// - `stop` indicates whether the operation should end with a STOP + /// condition. + /// - `will_continue` indicates whether there is another read operation + /// following this one and we should not nack the last byte. + /// - `cmd_iterator` is an iterator over the command registers. + async fn read_operation( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + ) -> Result<(), Error> { + self.clear_all_interrupts(); + + // Short circuit for zero length reads as that would be an invalid operation + // read lengths in the TRM (at least for ESP32-S3) are 1-255 + if buffer.is_empty() { + return Ok(()); + } + + self.start_read_operation(address, buffer, start, stop, will_continue)?; + self.read_all_from_fifo(buffer).await?; + self.wait_for_completion(!stop).await?; + Ok(()) + } + + fn read_blocking( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + ) -> Result<(), Error> { + let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); + for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { + self.read_operation_blocking( + address, + chunk, + start && idx == 0, + stop && idx == chunk_count - 1, + will_continue || idx < chunk_count - 1, + )?; + } + + Ok(()) + } + + fn write_blocking( + &self, + address: u8, + buffer: &[u8], + start: bool, + stop: bool, + ) -> Result<(), Error> { + if buffer.is_empty() { + return self.write_operation_blocking(address, &[], start, stop); + } + let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); + for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { + self.write_operation_blocking( + address, + chunk, + start && idx == 0, + stop && idx == chunk_count - 1, + )?; + } + + Ok(()) + } + + async fn read( + &self, + address: u8, + buffer: &mut [u8], + start: bool, + stop: bool, + will_continue: bool, + ) -> Result<(), Error> { + let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); + for (idx, chunk) in buffer.chunks_mut(I2C_CHUNK_SIZE).enumerate() { + self.read_operation( + address, + chunk, + start && idx == 0, + stop && idx == chunk_count - 1, + will_continue || idx < chunk_count - 1, + ) + .await?; + } + + Ok(()) + } + + async fn write( + &self, + address: u8, + buffer: &[u8], + start: bool, + stop: bool, + ) -> Result<(), Error> { + if buffer.is_empty() { + return self.write_operation(address, &[], start, stop).await; + } + let chunk_count = buffer.len().div_ceil(I2C_CHUNK_SIZE); + for (idx, chunk) in buffer.chunks(I2C_CHUNK_SIZE).enumerate() { + self.write_operation( + address, + chunk, + start && idx == 0, + stop && idx == chunk_count - 1, + ) + .await?; + } + + Ok(()) + } +} + +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.register_block == other.register_block + } +} + +unsafe impl Sync for Info {} + +/// Peripheral state for an I2C instance. +#[non_exhaustive] +pub struct State { + /// Waker for the asynchronous operations. + pub waker: AtomicWaker, +} + +/// I2C Peripheral Instance +pub trait Instance: Peripheral

+ Into + 'static { + /// Returns the peripheral data and state describing this instance. + fn parts(&self) -> (&Info, &State); + + /// Returns the peripheral data describing this instance. + #[inline(always)] + fn info(&self) -> &Info { + self.parts().0 + } + + /// Returns the peripheral state for this instance. + #[inline(always)] + fn state(&self) -> &State { + self.parts().1 + } +} + +/// Adds a command to the I2C command sequence. +fn add_cmd<'a, I>(cmd_iterator: &mut I, command: Command) -> Result<(), Error> +where + I: Iterator, +{ + let cmd = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?; + + cmd.write(|w| match command { + Command::Start => w.opcode().rstart(), + Command::Stop => w.opcode().stop(), + Command::End => w.opcode().end(), + Command::Write { + ack_exp, + ack_check_en, + length, + } => unsafe { + w.opcode().write(); + w.ack_exp().bit(ack_exp == Ack::Nack); + w.ack_check_en().bit(ack_check_en); + w.byte_num().bits(length); + w + }, + Command::Read { ack_value, length } => unsafe { + w.opcode().read(); + w.ack_value().bit(ack_value == Ack::Nack); + w.byte_num().bits(length); + w + }, + }); + + Ok(()) +} + +#[cfg(not(esp32s2))] +fn read_fifo(register_block: &RegisterBlock) -> u8 { + register_block.data().read().fifo_rdata().bits() +} + +#[cfg(not(esp32))] +fn write_fifo(register_block: &RegisterBlock, data: u8) { + register_block + .data() + .write(|w| unsafe { w.fifo_rdata().bits(data) }); +} + +#[cfg(esp32s2)] +fn read_fifo(register_block: &RegisterBlock) -> u8 { + let base_addr = register_block.scl_low_period().as_ptr(); + let fifo_ptr = (if base_addr as u32 == 0x3f413000 { + 0x6001301c + } else { + 0x6002701c + }) as *mut u32; + unsafe { (fifo_ptr.read_volatile() & 0xff) as u8 } +} + +#[cfg(esp32)] +fn write_fifo(register_block: &RegisterBlock, data: u8) { + let base_addr = register_block.scl_low_period().as_ptr(); + let fifo_ptr = (if base_addr as u32 == 0x3FF53000 { + 0x6001301c + } else { + 0x6002701c + }) as *mut u32; + unsafe { + fifo_ptr.write_volatile(data as u32); + } +} + +macro_rules! instance { + ($inst:ident, $peri:ident, $scl:ident, $sda:ident, $interrupt:ident) => { + impl Instance for crate::peripherals::$inst { + fn parts(&self) -> (&Info, &State) { + #[crate::macros::handler] + pub(super) fn irq_handler() { + async_handler(&PERIPHERAL, &STATE); + } + + static STATE: State = State { + waker: AtomicWaker::new(), + }; + + static PERIPHERAL: Info = Info { + register_block: crate::peripherals::$inst::ptr(), + peripheral: crate::system::Peripheral::$peri, + async_handler: irq_handler, + interrupt: Interrupt::$interrupt, + scl_output: OutputSignal::$scl, + scl_input: InputSignal::$scl, + sda_output: OutputSignal::$sda, + sda_input: InputSignal::$sda, + }; + (&PERIPHERAL, &STATE) + } + } + }; +} + +#[cfg(i2c0)] +instance!(I2C0, I2cExt0, I2CEXT0_SCL, I2CEXT0_SDA, I2C_EXT0); +#[cfg(i2c1)] +instance!(I2C1, I2cExt1, I2CEXT1_SCL, I2CEXT1_SDA, I2C_EXT1); + +crate::any_peripheral! { + /// Represents any I2C peripheral. + pub peripheral AnyI2c { + #[cfg(i2c0)] + I2c0(crate::peripherals::I2C0), + #[cfg(i2c1)] + I2c1(crate::peripherals::I2C1), + } +} + +impl Instance for AnyI2c { + delegate::delegate! { + to match &self.0 { + AnyI2cInner::I2c0(i2c) => i2c, + #[cfg(i2c1)] + AnyI2cInner::I2c1(i2c) => i2c, + } { + fn parts(&self) -> (&Info, &State); + } + } +} diff --git a/esp-hal/src/i2c/mod.rs b/esp-hal/src/i2c/mod.rs new file mode 100644 index 00000000000..69d84bc5576 --- /dev/null +++ b/esp-hal/src/i2c/mod.rs @@ -0,0 +1,11 @@ +//! # Inter-Integrated Circuit (I2C) +//! +//! I2C is a serial, synchronous, multi-device, half-duplex communication +//! protocol that allows co-existence of multiple masters and slaves on the +//! same bus. I2C uses two bidirectional open-drain lines: serial data line +//! (SDA) and serial clock line (SCL), pulled up by resistors. + +pub mod master; + +#[cfg(lp_i2c0)] +pub mod lp_i2c; diff --git a/esp-hal/src/i2s.rs b/esp-hal/src/i2s/master.rs similarity index 92% rename from esp-hal/src/i2s.rs rename to esp-hal/src/i2s/master.rs index 577390040fa..95a33d62399 100644 --- a/esp-hal/src/i2s.rs +++ b/esp-hal/src/i2s/master.rs @@ -29,13 +29,9 @@ //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::i2s::I2s; -//! # use esp_hal::i2s::Standard; -//! # use esp_hal::i2s::DataFormat; -//! # use esp_hal::gpio::Io; +//! # use esp_hal::i2s::master::{I2s, Standard, DataFormat}; //! # use esp_hal::dma_buffers; //! # use esp_hal::dma::{Dma, DmaPriority}; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! let dma = Dma::new(peripherals.DMA); #![cfg_attr(any(esp32, esp32s2), doc = "let dma_channel = dma.i2s0channel;")] #![cfg_attr(not(any(esp32, esp32s2)), doc = "let dma_channel = dma.channel0;")] @@ -54,17 +50,17 @@ //! rx_descriptors, //! tx_descriptors, //! ); -#![cfg_attr(not(esp32), doc = "let i2s = i2s.with_mclk(io.pins.gpio0);")] +#![cfg_attr(not(esp32), doc = "let i2s = i2s.with_mclk(peripherals.GPIO0);")] //! let mut i2s_rx = i2s.i2s_rx -//! .with_bclk(io.pins.gpio1) -//! .with_ws(io.pins.gpio2) -//! .with_din(io.pins.gpio5) +//! .with_bclk(peripherals.GPIO1) +//! .with_ws(peripherals.GPIO2) +//! .with_din(peripherals.GPIO5) //! .build(); //! //! let mut transfer = i2s_rx.read_dma_circular(&mut rx_buffer).unwrap(); //! //! loop { -//! let avail = transfer.available(); +//! let avail = transfer.available().unwrap(); //! //! if avail > 0 { //! let mut rcv = [0u8; 5000]; @@ -75,7 +71,7 @@ //! ``` //! //! ## Implementation State -//! - Only master mode is supported. +//! //! - Only TDM Philips standard is supported. use core::marker::PhantomData; @@ -107,6 +103,8 @@ use crate::{ interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, system::PeripheralClockControl, + Async, + Blocking, InterruptConfigurable, Mode, }; @@ -128,12 +126,12 @@ pub enum I2sInterrupt { } #[cfg(any(esp32, esp32s2, esp32s3))] -const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6; +pub(crate) const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 6; #[cfg(any(esp32c3, esp32c6, esp32h2))] -const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 9; +pub(crate) const I2S_LL_MCLK_DIVIDER_BIT_WIDTH: usize = 9; -const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1; +pub(crate) const I2S_LL_MCLK_DIVIDER_MAX: usize = (1 << I2S_LL_MCLK_DIVIDER_BIT_WIDTH) - 1; /// Data types that the I2S peripheral can work with. pub trait AcceptedWord: crate::private::Sealed {} @@ -253,16 +251,16 @@ impl DataFormat { } /// Instance of the I2S peripheral driver -pub struct I2s<'d, DmaMode, T = AnyI2s> +pub struct I2s<'d, M, T = AnyI2s> where T: RegisterAccess, - DmaMode: Mode, + M: Mode, { /// Handles the reception (RX) side of the I2S peripheral. - pub i2s_rx: RxCreator<'d, DmaMode, T>, + pub i2s_rx: RxCreator<'d, M, T>, /// Handles the transmission (TX) side of the I2S peripheral. - pub i2s_tx: TxCreator<'d, DmaMode, T>, - phantom: PhantomData, + pub i2s_tx: TxCreator<'d, M, T>, + phantom: PhantomData, } impl<'d, DmaMode, T> I2s<'d, DmaMode, T> @@ -314,7 +312,7 @@ where } } -impl<'d, DmaMode, T> I2s<'d, DmaMode, T> +impl I2s<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -328,15 +326,15 @@ where } /// Listen for the given interrupts - pub fn listen(&mut self, interrupts: EnumSet) { + pub fn listen(&mut self, interrupts: impl Into>) { // tx.i2s and rx.i2s is the same, we could use either one - self.i2s_tx.i2s.listen(interrupts); + self.i2s_tx.i2s.enable_listen(interrupts.into(), true); } /// Unlisten the given interrupts - pub fn unlisten(&mut self, interrupts: EnumSet) { + pub fn unlisten(&mut self, interrupts: impl Into>) { // tx.i2s and rx.i2s is the same, we could use either one - self.i2s_tx.i2s.unlisten(interrupts); + self.i2s_tx.i2s.enable_listen(interrupts.into(), false); } /// Gets asserted interrupts @@ -346,20 +344,20 @@ where } /// Resets asserted interrupts - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { // tx.i2s and rx.i2s is the same, we could use either one - self.i2s_tx.i2s.clear_interrupts(interrupts); + self.i2s_tx.i2s.clear_interrupts(interrupts.into()); } } -impl<'d, DmaMode, I> crate::private::Sealed for I2s<'d, DmaMode, I> +impl crate::private::Sealed for I2s<'_, DmaMode, I> where I: RegisterAccess, DmaMode: Mode, { } -impl<'d, DmaMode, I> InterruptConfigurable for I2s<'d, DmaMode, I> +impl InterruptConfigurable for I2s<'_, DmaMode, I> where I: RegisterAccess, DmaMode: Mode, @@ -369,24 +367,23 @@ where } } -impl<'d, DmaMode> I2s<'d, DmaMode> -where - DmaMode: Mode, -{ +impl<'d> I2s<'d, Blocking> { /// Construct a new I2S peripheral driver instance for the first I2S /// peripheral #[allow(clippy::too_many_arguments)] - pub fn new( + pub fn new( i2s: impl Peripheral

+ 'd, standard: Standard, data_format: DataFormat, sample_rate: impl Into, - channel: Channel<'d, CH, DmaMode>, + channel: Channel<'d, CH, DM>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where CH: DmaChannelConvert<::Dma>, + DM: Mode, + Channel<'d, CH, Blocking>: From>, { Self::new_typed( i2s.map_into(), @@ -400,25 +397,26 @@ where } } -impl<'d, DmaMode, T> I2s<'d, DmaMode, T> +impl<'d, T> I2s<'d, Blocking, T> where T: RegisterAccess, - DmaMode: Mode, { /// Construct a new I2S peripheral driver instance for the first I2S /// peripheral #[allow(clippy::too_many_arguments)] - pub fn new_typed( + pub fn new_typed( i2s: impl Peripheral

+ 'd, standard: Standard, data_format: DataFormat, sample_rate: impl Into, - channel: Channel<'d, CH, DmaMode>, + channel: Channel<'d, CH, DM>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], ) -> Self where CH: DmaChannelConvert, + DM: Mode, + Channel<'d, CH, Blocking>: From>, { crate::into_ref!(i2s); Self::new_internal( @@ -426,17 +424,48 @@ where standard, data_format, sample_rate, - channel, + channel.into(), rx_descriptors, tx_descriptors, ) } + /// Converts the SPI instance into async mode. + pub fn into_async(self) -> I2s<'d, Async, T> { + let channel = Channel { + rx: self.i2s_rx.rx_channel, + tx: self.i2s_tx.tx_channel, + phantom: PhantomData::, + }; + let channel = channel.into_async(); + I2s { + i2s_rx: RxCreator { + i2s: self.i2s_rx.i2s, + rx_channel: channel.rx, + descriptors: self.i2s_rx.descriptors, + phantom: PhantomData, + }, + i2s_tx: TxCreator { + i2s: self.i2s_tx.i2s, + tx_channel: channel.tx, + descriptors: self.i2s_tx.descriptors, + phantom: PhantomData, + }, + phantom: PhantomData, + } + } +} + +impl<'d, M, T> I2s<'d, M, T> +where + T: RegisterAccess, + M: Mode, +{ /// Configures the I2S peripheral to use a master clock (MCLK) output pin. pub fn with_mclk(self, pin: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(crate::private::Internal); - pin.connect_peripheral_to_output(self.i2s_tx.i2s.mclk_signal(), crate::private::Internal); + self.i2s_tx.i2s.mclk_signal().connect_to(pin); self } @@ -453,7 +482,7 @@ where phantom: PhantomData, } -impl<'d, DmaMode, T> core::fmt::Debug for I2sTx<'d, DmaMode, T> +impl core::fmt::Debug for I2sTx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -463,7 +492,7 @@ where } } -impl<'d, DmaMode, T> DmaSupport for I2sTx<'d, DmaMode, T> +impl DmaSupport for I2sTx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -493,7 +522,7 @@ where } } -impl<'d, DmaMode, T> I2sTx<'d, DmaMode, T> +impl I2sTx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -586,7 +615,7 @@ where phantom: PhantomData, } -impl<'d, DmaMode, T> core::fmt::Debug for I2sRx<'d, DmaMode, T> +impl core::fmt::Debug for I2sRx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -596,7 +625,7 @@ where } } -impl<'d, DmaMode, T> DmaSupport for I2sRx<'d, DmaMode, T> +impl DmaSupport for I2sRx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -626,7 +655,7 @@ where } } -impl<'d, DmaMode, T> I2sRx<'d, DmaMode, T> +impl I2sRx<'_, DmaMode, T> where T: RegisterAccess, DmaMode: Mode, @@ -742,14 +771,7 @@ mod private { #[cfg(i2s1)] use crate::peripherals::{i2s1::RegisterBlock, I2S1}; use crate::{ - dma::{ - ChannelRx, - ChannelTx, - DescriptorChain, - DmaDescriptor, - DmaEligible, - PeripheralMarker, - }, + dma::{ChannelRx, ChannelTx, DescriptorChain, DmaDescriptor, DmaEligible}, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -757,20 +779,20 @@ mod private { }, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, - peripherals::I2S0, + peripherals::{Interrupt, I2S0}, private, Mode, }; - pub struct TxCreator<'d, DmaMode, T> + pub struct TxCreator<'d, M, T> where T: RegisterAccess, - DmaMode: Mode, + M: Mode, { pub i2s: PeripheralRef<'d, T>, pub tx_channel: ChannelTx<'d, T::Dma>, pub descriptors: &'static mut [DmaDescriptor], - pub(crate) phantom: PhantomData, + pub(crate) phantom: PhantomData, } impl<'d, DmaMode, T> TxCreator<'d, DmaMode, T> @@ -793,7 +815,7 @@ mod private { { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(private::Internal); - pin.connect_peripheral_to_output(self.i2s.bclk_signal(), private::Internal); + self.i2s.bclk_signal().connect_to(pin); self } @@ -804,7 +826,7 @@ mod private { { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(private::Internal); - pin.connect_peripheral_to_output(self.i2s.ws_signal(), private::Internal); + self.i2s.ws_signal().connect_to(pin); self } @@ -815,29 +837,29 @@ mod private { { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(private::Internal); - pin.connect_peripheral_to_output(self.i2s.dout_signal(), private::Internal); + self.i2s.dout_signal().connect_to(pin); self } } - pub struct RxCreator<'d, DmaMode, T> + pub struct RxCreator<'d, M, T> where T: RegisterAccess, - DmaMode: Mode, + M: Mode, { pub i2s: PeripheralRef<'d, T>, pub rx_channel: ChannelRx<'d, T::Dma>, pub descriptors: &'static mut [DmaDescriptor], - pub(crate) phantom: PhantomData, + pub(crate) phantom: PhantomData, } - impl<'d, DmaMode, T> RxCreator<'d, DmaMode, T> + impl<'d, M, T> RxCreator<'d, M, T> where T: RegisterAccess, - DmaMode: Mode, + M: Mode, { - pub fn build(self) -> I2sRx<'d, DmaMode, T> { + pub fn build(self) -> I2sRx<'d, M, T> { I2sRx { i2s: self.i2s, rx_channel: self.rx_channel, @@ -852,7 +874,7 @@ mod private { { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(private::Internal); - pin.connect_peripheral_to_output(self.i2s.bclk_rx_signal(), private::Internal); + self.i2s.bclk_rx_signal().connect_to(pin); self } @@ -863,7 +885,7 @@ mod private { { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(private::Internal); - pin.connect_peripheral_to_output(self.i2s.ws_rx_signal(), private::Internal); + self.i2s.ws_rx_signal().connect_to(pin); self } @@ -874,16 +896,15 @@ mod private { { crate::into_mapped_ref!(pin); pin.init_input(crate::gpio::Pull::None, private::Internal); - pin.connect_input_to_peripheral(self.i2s.din_signal(), private::Internal); + self.i2s.din_signal().connect_to(pin); self } } - pub trait RegBlock: - Peripheral

+ PeripheralMarker + DmaEligible + Into + 'static - { + pub trait RegBlock: Peripheral

+ DmaEligible + Into + 'static { fn register_block(&self) -> &RegisterBlock; + fn peripheral(&self) -> crate::system::Peripheral; } pub trait Signals: RegBlock { @@ -914,14 +935,6 @@ mod private { }); } - fn listen(&self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), true); - } - - fn unlisten(&self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), false); - } - fn interrupts(&self) -> EnumSet { let mut res = EnumSet::new(); let reg_block = self.register_block(); @@ -1545,6 +1558,9 @@ mod private { fn reset_rx(&self) { let i2s = self.register_block(); + + i2s.rx_conf().modify(|_, w| w.rx_start().clear_bit()); + i2s.rx_conf().modify(|_, w| { w.rx_reset().set_bit(); w.rx_fifo_reset().set_bit() @@ -1583,13 +1599,22 @@ mod private { fn register_block(&self) -> &RegisterBlock { unsafe { &*I2S0::PTR.cast::() } } + + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::I2s0 + } } impl RegisterAccessPrivate for I2S0 { fn set_interrupt_handler(&self, handler: InterruptHandler) { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::I2S0); + } unsafe { crate::peripherals::I2S0::steal() }.bind_i2s0_interrupt(handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::I2S0, handler.priority()) - .unwrap(); + unwrap!(crate::interrupt::enable( + Interrupt::I2S0, + handler.priority() + )); } } @@ -1682,14 +1707,23 @@ mod private { fn register_block(&self) -> &RegisterBlock { unsafe { &*I2S1::PTR.cast::() } } + + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::I2s1 + } } #[cfg(i2s1)] impl RegisterAccessPrivate for I2S1 { fn set_interrupt_handler(&self, handler: InterruptHandler) { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::I2S1); + } unsafe { crate::peripherals::I2S1::steal() }.bind_i2s1_interrupt(handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::I2S1, handler.priority()) - .unwrap(); + unwrap!(crate::interrupt::enable( + Interrupt::I2S1, + handler.priority() + )); } } @@ -1750,6 +1784,7 @@ mod private { super::AnyI2sInner::I2s1(i2s) => i2s, } { fn register_block(&self) -> &RegisterBlock; + fn peripheral(&self) -> crate::system::Peripheral; } } } @@ -1946,7 +1981,7 @@ pub mod asynch { _buffer: BUFFER, } - impl<'d, T, BUFFER> I2sWriteDmaTransferAsync<'d, BUFFER, T> + impl I2sWriteDmaTransferAsync<'_, BUFFER, T> where T: RegisterAccess, { @@ -1954,7 +1989,7 @@ pub mod asynch { /// Will wait for more than 0 bytes available. pub async fn available(&mut self) -> Result { loop { - self.state.update(&self.i2s_tx.tx_channel); + self.state.update(&self.i2s_tx.tx_channel)?; let res = self.state.available; if res != 0 { @@ -2069,7 +2104,7 @@ pub mod asynch { _buffer: BUFFER, } - impl<'d, T, BUFFER> I2sReadDmaTransferAsync<'d, BUFFER, T> + impl I2sReadDmaTransferAsync<'_, BUFFER, T> where T: RegisterAccess, { @@ -2077,7 +2112,7 @@ pub mod asynch { /// Will wait for more than 0 bytes available. pub async fn available(&mut self) -> Result { loop { - self.state.update(); + self.state.update()?; let res = self.state.available; diff --git a/esp-hal/src/i2s/mod.rs b/esp-hal/src/i2s/mod.rs new file mode 100644 index 00000000000..2b0bfd6eb6a --- /dev/null +++ b/esp-hal/src/i2s/mod.rs @@ -0,0 +1,6 @@ +//! # Inter-IC Sound (I2S) + +pub mod master; + +#[cfg(esp32)] +pub mod parallel; diff --git a/esp-hal/src/i2s/parallel.rs b/esp-hal/src/i2s/parallel.rs new file mode 100644 index 00000000000..865346fb648 --- /dev/null +++ b/esp-hal/src/i2s/parallel.rs @@ -0,0 +1,743 @@ +//! # Parallel Interface (via I2S) +//! +//! ## Overview +//! The I2S parallel interface allows for high-speed data transfer between the +//! ESP32 and external devices. It is commonly used to external devices such as +//! LED matrix, LCD display, and Printer. Only TX is implemented. Each +//! unit can have up to 8 or 16 data signals (depending on your target hardware) +//! plus 1 clock signal. +//! +//! ## Notes +//! +//! Data output is interleaved: +//! - 8bit: [A, B, C, D] is output as [C, D, A, B] (i.e., swapped as 16bit +//! words) +//! - 16bit: [A, B, C, D] is output as [B, A, D, C] (i.e., 16bit words are +//! swapped) +#![cfg_attr(esp32, doc = "")] +#![cfg_attr( + esp32, + doc = "I2S0 does not support true 8bit parallel output, so if you want to do 8bit" +)] +#![cfg_attr( + esp32, + doc = "you should use I2S1. If you have to use I2S0, it will only output the even" +)] +#![cfg_attr(esp32, doc = "bytes! so [A, B, C, D] will be output as [A, C]!!!!")] +#![cfg_attr(esp32, doc = "")] +//! ## Configuration +//! +//! The driver uses DMA (Direct Memory Access) for efficient data transfer and +//! supports various configurations, such as different data formats, standards +//! (e.g., Philips) and pin configurations. It relies on other peripheral +//! modules, such as +//! - `GPIO` +//! - `DMA` +//! - `system` (to configure and enable the I2S peripheral) +use core::{ + marker::PhantomData, + mem::ManuallyDrop, + ops::{Deref, DerefMut}, +}; + +use fugit::HertzU32; + +use crate::{ + dma::{ + asynch::DmaTxFuture, + Channel, + ChannelTx, + DmaChannelConvert, + DmaEligible, + DmaError, + DmaPeripheral, + DmaTxBuffer, + Tx, + }, + gpio::{ + interconnect::{OutputConnection, PeripheralOutput}, + OutputSignal, + }, + peripheral::{Peripheral, PeripheralRef}, + peripherals::{i2s0::RegisterBlock, I2S0, I2S1}, + private::Internal, + system::PeripheralClockControl, + Async, + Mode, +}; + +#[doc(hidden)] +pub trait TxPins<'d> { + fn bus_width(&self) -> u8; + fn configure(&mut self, instance: impl Peripheral

); +} + +/// Represents a group of 16 output pins configured for 16-bit parallel data +/// transmission. +pub struct TxSixteenBits<'d> { + pins: [PeripheralRef<'d, OutputConnection>; 16], +} + +impl<'d> TxSixteenBits<'d> { + #[allow(clippy::too_many_arguments)] + /// Creates a new `TxSixteenBits` instance with the provided output pins. + pub fn new( + pin_0: impl Peripheral

+ 'd, + pin_1: impl Peripheral

+ 'd, + pin_2: impl Peripheral

+ 'd, + pin_3: impl Peripheral

+ 'd, + pin_4: impl Peripheral

+ 'd, + pin_5: impl Peripheral

+ 'd, + pin_6: impl Peripheral

+ 'd, + pin_7: impl Peripheral

+ 'd, + pin_8: impl Peripheral

+ 'd, + pin_9: impl Peripheral

+ 'd, + pin_10: impl Peripheral

+ 'd, + pin_11: impl Peripheral

+ 'd, + pin_12: impl Peripheral

+ 'd, + pin_13: impl Peripheral

+ 'd, + pin_14: impl Peripheral

+ 'd, + pin_15: impl Peripheral

+ 'd, + ) -> Self { + crate::into_mapped_ref!( + pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7, pin_8, pin_9, pin_10, pin_11, + pin_12, pin_13, pin_14, pin_15 + ); + + Self { + pins: [ + pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7, pin_8, pin_9, pin_10, + pin_11, pin_12, pin_13, pin_14, pin_15, + ], + } + } +} + +impl<'d> TxPins<'d> for TxSixteenBits<'d> { + fn bus_width(&self) -> u8 { + self.pins.len() as u8 + } + + fn configure(&mut self, instance: impl Peripheral

) { + crate::into_ref!(instance); + let bits = self.bus_width(); + for (i, pin) in self.pins.iter_mut().enumerate() { + pin.set_to_push_pull_output(Internal); + instance.data_out_signal(i, bits).connect_to(pin); + } + } +} + +/// Represents a group of 8 output pins configured for 8-bit parallel data +/// transmission. +pub struct TxEightBits<'d> { + pins: [PeripheralRef<'d, OutputConnection>; 8], +} + +impl<'d> TxEightBits<'d> { + #[allow(clippy::too_many_arguments)] + /// Creates a new `TxSEightBits` instance with the provided output pins. + pub fn new( + pin_0: impl Peripheral

+ 'd, + pin_1: impl Peripheral

+ 'd, + pin_2: impl Peripheral

+ 'd, + pin_3: impl Peripheral

+ 'd, + pin_4: impl Peripheral

+ 'd, + pin_5: impl Peripheral

+ 'd, + pin_6: impl Peripheral

+ 'd, + pin_7: impl Peripheral

+ 'd, + ) -> Self { + crate::into_mapped_ref!(pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7); + + Self { + pins: [pin_0, pin_1, pin_2, pin_3, pin_4, pin_5, pin_6, pin_7], + } + } +} + +impl<'d> TxPins<'d> for TxEightBits<'d> { + fn bus_width(&self) -> u8 { + self.pins.len() as u8 + } + + fn configure(&mut self, instance: impl Peripheral

) { + crate::into_ref!(instance); + let bits = self.bus_width(); + for (i, pin) in self.pins.iter_mut().enumerate() { + pin.set_to_push_pull_output(Internal); + instance.data_out_signal(i, bits).connect_to(pin); + } + } +} + +/// I2S Parallel Interface +pub struct I2sParallel<'d, DM, I = AnyI2s> +where + DM: Mode, + I: Instance, +{ + instance: PeripheralRef<'d, I>, + tx_channel: ChannelTx<'d, I::Dma>, + mode: PhantomData, +} + +impl<'d, DM> I2sParallel<'d, DM> +where + DM: Mode, +{ + /// Create a new I2S Parallel Interface + pub fn new( + i2s: impl Peripheral

+ 'd, + channel: Channel<'d, CH, DM>, + frequency: impl Into, + pins: impl TxPins<'d>, + clock_pin: impl Peripheral

+ 'd, + ) -> Self + where + CH: DmaChannelConvert<::Dma>, + { + Self::new_typed(i2s.map_into(), channel, frequency, pins, clock_pin) + } +} + +impl<'d, I, DM> I2sParallel<'d, DM, I> +where + I: Instance, + DM: Mode, +{ + /// Create a new I2S Parallel Interface + pub fn new_typed( + i2s: impl Peripheral

+ 'd, + channel: Channel<'d, CH, DM>, + frequency: impl Into, + mut pins: impl TxPins<'d>, + clock_pin: impl Peripheral

+ 'd, + ) -> Self + where + CH: DmaChannelConvert, + { + crate::into_ref!(i2s); + crate::into_mapped_ref!(clock_pin); + + channel.runtime_ensure_compatible(&i2s); + let channel = channel.degrade(); + + PeripheralClockControl::reset(i2s.peripheral()); + PeripheralClockControl::enable(i2s.peripheral()); + // configure the I2S peripheral for parallel mode + i2s.setup(frequency, pins.bus_width()); + // setup the clock pin + clock_pin.set_to_push_pull_output(Internal); + i2s.ws_signal().connect_to(clock_pin); + + pins.configure(i2s.reborrow()); + Self { + instance: i2s, + tx_channel: channel.tx, + mode: PhantomData, + } + } + + /// Write data to the I2S peripheral + pub fn send( + mut self, + mut data: BUF, + ) -> Result, (DmaError, Self, BUF)> { + self.instance.tx_reset(); + self.instance.tx_fifo_reset(); + let result = unsafe { + self.tx_channel + .prepare_transfer(self.instance.dma_peripheral(), &mut data) + } + .and_then(|_| self.tx_channel.start_transfer()); + if let Err(err) = result { + return Err((err, self, data)); + } + crate::rom::ets_delay_us(1); + self.instance.tx_start(); + Ok(I2sParallelTransfer { + i2s: ManuallyDrop::new(self), + buf_view: ManuallyDrop::new(data.into_view()), + }) + } +} + +/// Represents an ongoing (or potentially finished) transfer using the i2s +/// parallel interface +pub struct I2sParallelTransfer<'d, BUF, DM, I = AnyI2s> +where + I: Instance, + BUF: DmaTxBuffer, + DM: Mode, +{ + i2s: ManuallyDrop>, + buf_view: ManuallyDrop, +} + +impl<'d, I, BUF, DM> I2sParallelTransfer<'d, BUF, DM, I> +where + I: Instance, + BUF: DmaTxBuffer, + DM: Mode, +{ + /// Returns true when [Self::wait] will not block. + pub fn is_done(&self) -> bool { + self.i2s.instance.is_tx_done() + } + + /// Wait for the transfer to finish + pub fn wait(mut self) -> (I2sParallel<'d, DM, I>, BUF) { + self.i2s.instance.tx_wait_done(); + let i2s = unsafe { ManuallyDrop::take(&mut self.i2s) }; + let view = unsafe { ManuallyDrop::take(&mut self.buf_view) }; + (i2s, BUF::from_view(view)) + } + + fn stop_peripherals(&mut self) { + self.i2s.instance.tx_stop(); + self.i2s.tx_channel.stop_transfer(); + } +} + +impl<'d, I, BUF> I2sParallelTransfer<'d, BUF, Async, I> +where + I: Instance, + BUF: DmaTxBuffer, +{ + /// Wait for the transfer to finish + pub async fn wait_for_done(&mut self) -> Result<(), DmaError> { + DmaTxFuture::new(&mut self.i2s.tx_channel).await + } +} + +impl<'d, I, BUF, DM> Deref for I2sParallelTransfer<'d, BUF, DM, I> +where + I: Instance, + BUF: DmaTxBuffer, + DM: Mode, +{ + type Target = BUF::View; + + fn deref(&self) -> &Self::Target { + &self.buf_view + } +} + +impl<'d, I, BUF, DM> DerefMut for I2sParallelTransfer<'d, BUF, DM, I> +where + I: Instance, + BUF: DmaTxBuffer, + DM: Mode, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buf_view + } +} + +impl<'d, I, BUF, DM> Drop for I2sParallelTransfer<'d, BUF, DM, I> +where + I: Instance, + BUF: DmaTxBuffer, + DM: Mode, +{ + fn drop(&mut self) { + self.stop_peripherals(); + + // SAFETY: This is Drop, we know that self.i2s and self.buf_view + // won't be touched again. + let view = unsafe { + ManuallyDrop::drop(&mut self.i2s); + ManuallyDrop::take(&mut self.buf_view) + }; + let _ = BUF::from_view(view); + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct I2sClockDividers { + pub mclk_divider: u32, + pub bclk_divider: u32, + pub denominator: u32, + pub numerator: u32, +} + +fn calculate_clock(sample_rate: impl Into, data_bits: u8) -> I2sClockDividers { + // this loosely corresponds to `i2s_std_calculate_clock` and + // `i2s_ll_tx_set_mclk` in esp-idf + // + // main difference is we are using fixed-point arithmetic here + // plus adjusted for parallel interface clocking + + let sclk = crate::soc::constants::I2S_SCLK; // for now it's fixed 160MHz and 96MHz (just H2) + + let rate_hz: HertzU32 = sample_rate.into(); + let rate = rate_hz.raw(); + + let mclk = rate * 2; + let bclk_divider: u32 = if data_bits == 8 { 2 } else { 1 }; + let mut mclk_divider = sclk / mclk; + + let mut ma: u32; + let mut mb: u32; + let mut denominator: u32 = 0; + let mut numerator: u32 = 0; + + let freq_diff = sclk.abs_diff(mclk * mclk_divider); + + if freq_diff != 0 { + let decimal = freq_diff as u64 * 10000 / mclk as u64; + // Carry bit if the decimal is greater than 1.0 - 1.0 / (63.0 * 2) = 125.0 / + // 126.0 + if decimal > 1250000 / 126 { + mclk_divider += 1; + } else { + let mut min: u32 = !0; + + for a in 2..=crate::i2s::master::I2S_LL_MCLK_DIVIDER_MAX { + let b = (a as u64) * (freq_diff as u64 * 10000u64 / mclk as u64) + 5000; + ma = ((freq_diff as u64 * 10000u64 * a as u64) / 10000) as u32; + mb = (mclk as u64 * (b / 10000)) as u32; + + if ma == mb { + denominator = a as u32; + numerator = (b / 10000) as u32; + break; + } + + if mb.abs_diff(ma) < min { + denominator = a as u32; + numerator = b as u32; + min = mb.abs_diff(ma); + } + } + } + } + + I2sClockDividers { + mclk_divider, + bclk_divider, + denominator, + numerator, + } +} + +#[doc(hidden)] +pub trait Instance: Peripheral

+ DmaEligible + Into + 'static { + fn register_block(&self) -> &RegisterBlock; + fn peripheral(&self) -> crate::system::Peripheral; + fn ws_signal(&self) -> OutputSignal; + fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal; + + fn rx_reset(&self) { + let r = self.register_block(); + r.conf().modify(|_, w| w.rx_reset().set_bit()); + r.conf().modify(|_, w| w.rx_reset().clear_bit()); + } + + fn rx_dma_reset(&self) { + let r = self.register_block(); + r.lc_conf().modify(|_, w| w.in_rst().set_bit()); + r.lc_conf().modify(|_, w| w.in_rst().clear_bit()); + } + + fn rx_fifo_reset(&self) { + let r = self.register_block(); + r.conf().modify(|_, w| w.rx_fifo_reset().set_bit()); + r.conf().modify(|_, w| w.rx_fifo_reset().clear_bit()); + } + + fn tx_reset(&self) { + let r = self.register_block(); + r.conf().modify(|_, w| w.tx_reset().set_bit()); + // without this delay starting a subsequent transfer will hang waiting + // for tx_idle to clear (the transfer does not start). + // While 20 clocks works for 80MHz cpu but 100 is needed for 240MHz! + xtensa_lx::timer::delay(100); + r.conf().modify(|_, w| w.tx_reset().clear_bit()); + } + + fn tx_dma_reset(&self) { + let r = self.register_block(); + r.lc_conf().modify(|_, w| w.out_rst().set_bit()); + r.lc_conf().modify(|_, w| w.out_rst().clear_bit()); + } + + fn tx_fifo_reset(&self) { + let r = self.register_block(); + r.conf().modify(|_, w| w.tx_fifo_reset().set_bit()); + r.conf().modify(|_, w| w.tx_fifo_reset().clear_bit()); + } + + fn tx_clear_interrupts(&self) { + let r = self.register_block(); + r.int_clr().write(|w| { + w.out_done().clear_bit_by_one(); + w.out_total_eof().clear_bit_by_one() + }); + } + + fn tx_start(&self) { + let r = self.register_block(); + + // wait for data to show up in the fifo + while r.int_raw().read().tx_rempty().bit_is_clear() { + // wait + } + + // without this transfers are not reliable! + xtensa_lx::timer::delay(1); + + r.conf().modify(|_, w| w.tx_start().set_bit()); + + while r.state().read().tx_idle().bit_is_set() { + // wait + } + } + + fn tx_stop(&self) { + let r = self.register_block(); + r.conf().modify(|_, w| w.tx_start().clear_bit()); + } + + fn is_tx_done(&self) -> bool { + self.register_block().state().read().tx_idle().bit_is_set() + } + + fn tx_wait_done(&self) { + let r = self.register_block(); + while r.state().read().tx_idle().bit_is_clear() { + // wait + } + + r.conf().modify(|_, w| w.tx_start().clear_bit()); + r.int_clr().write(|w| { + w.out_done().clear_bit_by_one(); + w.out_total_eof().clear_bit_by_one() + }); + } + + fn set_clock(&self, clock_settings: I2sClockDividers) { + let r = self.register_block(); + + r.clkm_conf().modify(|r, w| unsafe { + w.bits(r.bits() | (crate::soc::constants::I2S_DEFAULT_CLK_SRC << 21)) + // select PLL_160M + }); + + #[cfg(esp32)] + r.clkm_conf().modify(|_, w| w.clka_ena().clear_bit()); + + r.clkm_conf().modify(|_, w| unsafe { + w.clk_en().set_bit(); + w.clkm_div_num().bits(clock_settings.mclk_divider as u8) + }); + + r.clkm_conf().modify(|_, w| unsafe { + w.clkm_div_a().bits(clock_settings.denominator as u8); + w.clkm_div_b().bits(clock_settings.numerator as u8) + }); + + r.sample_rate_conf().modify(|_, w| unsafe { + w.tx_bck_div_num().bits(clock_settings.bclk_divider as u8); + w.rx_bck_div_num().bits(clock_settings.bclk_divider as u8) + }); + } + + fn setup(&self, frequency: impl Into, bits: u8) { + let frequency: HertzU32 = frequency.into(); + + self.set_clock(calculate_clock(frequency, bits)); + + // Initialize I2S dev + self.rx_reset(); + self.tx_reset(); + self.rx_fifo_reset(); + self.tx_fifo_reset(); + self.rx_dma_reset(); + self.tx_dma_reset(); + + let r = self.register_block(); + + // clear all bits and enable lcd mode + r.conf2().write(|w| { + // 8 bit mode needs this or it updates on half clocks! + w.lcd_tx_wrx2_en().bit(bits == 8); + w.lcd_en().set_bit() + }); + + r.sample_rate_conf().modify(|_, w| unsafe { + w.rx_bits_mod().bits(bits); + w.tx_bits_mod().bits(bits) + }); + + r.fifo_conf().write(|w| unsafe { + w.rx_fifo_mod_force_en().set_bit(); + w.tx_fifo_mod_force_en().set_bit(); + w.rx_fifo_mod().bits(1); + w.tx_fifo_mod().bits(1); + w.rx_data_num().bits(32); + w.tx_data_num().bits(32); + w.dscr_en().set_bit() + }); + + r.conf1().write(|w| { + w.tx_stop_en().set_bit(); + w.rx_pcm_bypass().set_bit(); + w.tx_pcm_bypass().set_bit() + }); + + r.conf_chan().write(|w| unsafe { + w.rx_chan_mod().bits(1); + w.tx_chan_mod().bits(1) + }); + + r.conf().modify(|_, w| { + w.rx_mono().set_bit(); + w.tx_mono().set_bit(); + w.rx_right_first().set_bit(); + w.tx_right_first().set_bit() + }); + r.timing().reset(); + + r.pd_conf().modify(|_, w| { + w.fifo_force_pu().set_bit(); + w.fifo_force_pd().clear_bit() + }); + } +} + +impl Instance for I2S0 { + fn register_block(&self) -> &RegisterBlock { + unsafe { &*I2S0::PTR.cast::() } + } + + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::I2s0 + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S0O_WS + } + fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal { + assert!( + bits == 8 || bits == 16, + "Number of bits must be 8 or 16, got {}", + bits + ); + + // signals for 8bit and 16bit both start at an offset of 8 for I2S0 + // https://github.com/espressif/esp-idf/blob/9106c43accd9f5e75379f62f12597677213f5023/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c#L701 + match i + 8 { + 0 => OutputSignal::I2S0O_DATA_0, + 1 => OutputSignal::I2S0O_DATA_1, + 2 => OutputSignal::I2S0O_DATA_2, + 3 => OutputSignal::I2S0O_DATA_3, + 4 => OutputSignal::I2S0O_DATA_4, + 5 => OutputSignal::I2S0O_DATA_5, + 6 => OutputSignal::I2S0O_DATA_6, + 7 => OutputSignal::I2S0O_DATA_7, + 8 => OutputSignal::I2S0O_DATA_8, + 9 => OutputSignal::I2S0O_DATA_9, + 10 => OutputSignal::I2S0O_DATA_10, + 11 => OutputSignal::I2S0O_DATA_11, + 12 => OutputSignal::I2S0O_DATA_12, + 13 => OutputSignal::I2S0O_DATA_13, + 14 => OutputSignal::I2S0O_DATA_14, + 15 => OutputSignal::I2S0O_DATA_15, + 16 => OutputSignal::I2S0O_DATA_16, + 17 => OutputSignal::I2S0O_DATA_17, + 18 => OutputSignal::I2S0O_DATA_18, + 19 => OutputSignal::I2S0O_DATA_19, + 20 => OutputSignal::I2S0O_DATA_20, + 21 => OutputSignal::I2S0O_DATA_21, + 22 => OutputSignal::I2S0O_DATA_22, + 23 => OutputSignal::I2S0O_DATA_23, + other => panic!("Invalid I2S0 Dout pin {}", other), + } + } +} + +impl Instance for I2S1 { + fn register_block(&self) -> &RegisterBlock { + unsafe { &*I2S1::PTR.cast::() } + } + + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::I2s1 + } + + fn ws_signal(&self) -> OutputSignal { + OutputSignal::I2S1O_WS + } + fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal { + assert!( + bits == 8 || bits == 16, + "Number of bits must be 8 or 16, got {}", + bits + ); + + // signals for 8bit and 16bit both start at an offset of 8 for I2S1 + // https://github.com/espressif/esp-idf/blob/9106c43accd9f5e75379f62f12597677213f5023/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c#L701 + match i + 8 { + 0 => OutputSignal::I2S1O_DATA_0, + 1 => OutputSignal::I2S1O_DATA_1, + 2 => OutputSignal::I2S1O_DATA_2, + 3 => OutputSignal::I2S1O_DATA_3, + 4 => OutputSignal::I2S1O_DATA_4, + 5 => OutputSignal::I2S1O_DATA_5, + 6 => OutputSignal::I2S1O_DATA_6, + 7 => OutputSignal::I2S1O_DATA_7, + 8 => OutputSignal::I2S1O_DATA_8, + 9 => OutputSignal::I2S1O_DATA_9, + 10 => OutputSignal::I2S1O_DATA_10, + 11 => OutputSignal::I2S1O_DATA_11, + 12 => OutputSignal::I2S1O_DATA_12, + 13 => OutputSignal::I2S1O_DATA_13, + 14 => OutputSignal::I2S1O_DATA_14, + 15 => OutputSignal::I2S1O_DATA_15, + 16 => OutputSignal::I2S1O_DATA_16, + 17 => OutputSignal::I2S1O_DATA_17, + 18 => OutputSignal::I2S1O_DATA_18, + 19 => OutputSignal::I2S1O_DATA_19, + 20 => OutputSignal::I2S1O_DATA_20, + 21 => OutputSignal::I2S1O_DATA_21, + 22 => OutputSignal::I2S1O_DATA_22, + 23 => OutputSignal::I2S1O_DATA_23, + other => panic!("Invalid I2S1 Dout pin {}", other), + } + } +} + +crate::any_peripheral! { + /// Any SPI peripheral. + pub peripheral AnyI2s { + I2s0(crate::peripherals::I2S0), + I2s1(crate::peripherals::I2S1), + } +} + +impl DmaEligible for AnyI2s { + type Dma = crate::dma::AnyI2sDmaChannel; + + fn dma_peripheral(&self) -> DmaPeripheral { + match &self.0 { + AnyI2sInner::I2s0(_) => DmaPeripheral::I2s0, + AnyI2sInner::I2s1(_) => DmaPeripheral::I2s1, + } + } +} + +impl Instance for AnyI2s { + delegate::delegate! { + to match &self.0 { + AnyI2sInner::I2s0(i2s) => i2s, + AnyI2sInner::I2s1(i2s) => i2s, + } { + fn register_block(&self) -> &RegisterBlock; + fn peripheral(&self) -> crate::system::Peripheral; + fn ws_signal(&self) -> OutputSignal; + fn data_out_signal(&self, i: usize, bits: u8) -> OutputSignal ; + } + } +} diff --git a/esp-hal/src/interrupt/riscv.rs b/esp-hal/src/interrupt/riscv.rs index 151fa622471..8516dca4c4f 100644 --- a/esp-hal/src/interrupt/riscv.rs +++ b/esp-hal/src/interrupt/riscv.rs @@ -239,8 +239,8 @@ pub fn enable_direct( return Err(Error::InvalidInterruptPriority); } unsafe { - map(crate::get_core(), interrupt, cpu_interrupt); - set_priority(crate::get_core(), cpu_interrupt, level); + map(Cpu::current(), interrupt, cpu_interrupt); + set_priority(Cpu::current(), cpu_interrupt, level); enable_cpu_interrupt(cpu_interrupt); } Ok(()) @@ -262,7 +262,7 @@ pub fn disable(_core: Cpu, interrupt: Interrupt) { /// Get status of peripheral interrupts #[inline] -pub fn get_status(_core: Cpu) -> InterruptStatus { +pub fn status(_core: Cpu) -> InterruptStatus { #[cfg(large_intr_status)] unsafe { InterruptStatus::from( @@ -341,12 +341,12 @@ pub unsafe fn map(_core: Cpu, interrupt: Interrupt, which: CpuInterrupt) { /// Get cpu interrupt assigned to peripheral interrupt #[inline] -unsafe fn get_assigned_cpu_interrupt(interrupt: Interrupt) -> Option { +unsafe fn assigned_cpu_interrupt(interrupt: Interrupt) -> Option { let interrupt_number = interrupt as isize; let intr_map_base = crate::soc::registers::INTERRUPT_MAP_BASE as *mut u32; let cpu_intr = intr_map_base.offset(interrupt_number).read_volatile(); - if cpu_intr > 0 { + if cpu_intr > 0 && cpu_intr != DISABLED_CPU_INTERRUPT { Some(core::mem::transmute::( cpu_intr - EXTERNAL_INTERRUPT_OFFSET, )) @@ -355,6 +355,10 @@ unsafe fn get_assigned_cpu_interrupt(interrupt: Interrupt) -> Option Option { + unsafe { assigned_cpu_interrupt(interrupt) } +} + mod vectored { use procmacros::ram; @@ -365,12 +369,12 @@ mod vectored { pub(crate) unsafe fn init_vectoring() { for (prio, num) in PRIORITY_TO_INTERRUPT.iter().enumerate() { set_kind( - crate::get_core(), + Cpu::current(), core::mem::transmute::(*num as u32), InterruptKind::Level, ); set_priority( - crate::get_core(), + Cpu::current(), core::mem::transmute::(*num as u32), core::mem::transmute::((prio as u8) + 1), ); @@ -381,7 +385,7 @@ mod vectored { /// Get the interrupts configured for the core at the given priority /// matching the given status #[inline] - fn get_configured_interrupts( + fn configured_interrupts( core: Cpu, status: InterruptStatus, priority: Priority, @@ -392,11 +396,11 @@ mod vectored { for interrupt_nr in status.iterator() { // safety: cast is safe because of repr(u16) if let Some(cpu_interrupt) = - get_assigned_cpu_interrupt(core::mem::transmute::( + assigned_cpu_interrupt(core::mem::transmute::( interrupt_nr as u16, )) { - if get_priority_by_core(core, cpu_interrupt) == priority { + if priority_by_core(core, cpu_interrupt) == priority { res.set(interrupt_nr as u16); } } @@ -417,35 +421,47 @@ mod vectored { let cpu_interrupt = core::mem::transmute::( PRIORITY_TO_INTERRUPT[(level as usize) - 1] as u32, ); - map(crate::get_core(), interrupt, cpu_interrupt); + map(Cpu::current(), interrupt, cpu_interrupt); enable_cpu_interrupt(cpu_interrupt); } Ok(()) } - /// Bind the given interrupt to the given handler + /// Binds the given interrupt to the given handler. /// /// # Safety /// /// This will replace any previously bound interrupt handler - pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) { + pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) { let ptr = &peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler as *const _ - as *mut unsafe extern "C" fn() -> (); + as *mut unsafe extern "C" fn(); ptr.write_volatile(handler); } + /// Returns the currently bound interrupt handler. + pub fn bound_handler(interrupt: Interrupt) -> Option { + unsafe { + let addr = peripherals::__EXTERNAL_INTERRUPTS[interrupt as usize]._handler; + if addr as usize == 0 { + return None; + } + + Some(addr) + } + } + #[no_mangle] #[ram] unsafe fn handle_interrupts(cpu_intr: CpuInterrupt, context: &mut TrapFrame) { - let core = crate::get_core(); - let status = get_status(core); + let core = Cpu::current(); + let status = status(core); // this has no effect on level interrupts, but the interrupt may be an edge one // so we clear it anyway clear(core, cpu_intr); let prio: Priority = unsafe { core::mem::transmute(INTERRUPT_TO_PRIORITY[cpu_intr as usize - 1] as u8) }; - let configured_interrupts = get_configured_interrupts(core, status, prio); + let configured_interrupts = configured_interrupts(core, status, prio); for interrupt_nr in configured_interrupts.iterator() { // Don't use `Interrupt::try_from`. It's slower and placed in flash @@ -623,13 +639,13 @@ mod classic { /// Get interrupt priority #[inline] - pub(super) fn get_priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority { - unsafe { get_priority(cpu_interrupt) } + pub(super) fn priority_by_core(_core: Cpu, cpu_interrupt: CpuInterrupt) -> Priority { + unsafe { priority(cpu_interrupt) } } /// Get interrupt priority - called by assembly code #[inline] - pub(super) unsafe extern "C" fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { + pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority { let intr = &*crate::peripherals::INTERRUPT_CORE0::PTR; core::mem::transmute::( intr.cpu_int_pri(cpu_interrupt as usize).read().map().bits(), @@ -755,16 +771,16 @@ mod plic { /// Get interrupt priority #[inline] - pub(super) unsafe extern "C" fn get_priority_by_core( + pub(super) unsafe extern "C" fn priority_by_core( _core: Cpu, cpu_interrupt: CpuInterrupt, ) -> Priority { - unsafe { get_priority(cpu_interrupt) } + unsafe { priority(cpu_interrupt) } } /// Get interrupt priority - called by assembly code #[inline] - pub(super) unsafe extern "C" fn get_priority(cpu_interrupt: CpuInterrupt) -> Priority { + pub(super) unsafe extern "C" fn priority(cpu_interrupt: CpuInterrupt) -> Priority { let plic = &*crate::peripherals::PLIC_MX::PTR; let prio = plic .mxint_pri(cpu_interrupt as usize) diff --git a/esp-hal/src/interrupt/software.rs b/esp-hal/src/interrupt/software.rs index 64156a68d91..92dfb32d4fa 100644 --- a/esp-hal/src/interrupt/software.rs +++ b/esp-hal/src/interrupt/software.rs @@ -62,10 +62,11 @@ impl SoftwareInterrupt { _ => unreachable!(), }; - unsafe { - crate::interrupt::bind_interrupt(interrupt, handler.handler()); - crate::interrupt::enable(interrupt, handler.priority()).unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); } + unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(interrupt, handler.priority())); } /// Trigger this software-interrupt @@ -92,7 +93,7 @@ impl SoftwareInterrupt { .cpu_intr_from_cpu_3() .write(|w| w.cpu_intr_from_cpu_3().set_bit()), _ => unreachable!(), - } + }; } /// Resets this software-interrupt @@ -119,7 +120,7 @@ impl SoftwareInterrupt { .cpu_intr_from_cpu_3() .write(|w| w.cpu_intr_from_cpu_3().clear_bit()), _ => unreachable!(), - } + }; } /// Unsafely create an instance of this peripheral out of thin air. diff --git a/esp-hal/src/interrupt/xtensa.rs b/esp-hal/src/interrupt/xtensa.rs index 5ed4138c440..f0aaa54e6f6 100644 --- a/esp-hal/src/interrupt/xtensa.rs +++ b/esp-hal/src/interrupt/xtensa.rs @@ -94,6 +94,32 @@ pub enum CpuInterrupt { Interrupt31EdgePriority5, } +impl CpuInterrupt { + fn from_u32(n: u32) -> Option { + if n < 32 { + Some(unsafe { core::mem::transmute::(n) }) + } else { + None + } + } + + fn is_internal(self) -> bool { + matches!( + self, + Self::Interrupt6Timer0Priority1 + | Self::Interrupt7SoftwarePriority1 + | Self::Interrupt11ProfilingPriority3 + | Self::Interrupt15Timer1Priority3 + | Self::Interrupt16Timer2Priority5 + | Self::Interrupt29SoftwarePriority3 + ) + } + + fn is_peripheral(self) -> bool { + !self.is_internal() + } +} + /// The interrupts reserved by the HAL #[cfg_attr(place_switch_tables_in_ram, link_section = ".rwtext")] pub static RESERVED_INTERRUPTS: &[usize] = &[ @@ -130,7 +156,7 @@ pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Resul return Err(Error::CpuInterruptReserved); } unsafe { - map(crate::get_core(), interrupt, cpu_interrupt); + map(Cpu::current(), interrupt, cpu_interrupt); xtensa_lx::interrupt::enable_mask( xtensa_lx::interrupt::get_mask() | 1 << cpu_interrupt as u32, @@ -160,6 +186,25 @@ pub unsafe fn map(core: Cpu, interrupt: Interrupt, which: CpuInterrupt) { .write_volatile(cpu_interrupt_number as u32); } +/// Get cpu interrupt assigned to peripheral interrupt +pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option { + let interrupt_number = interrupt as isize; + + let intr_map_base = match cpu { + Cpu::ProCpu => unsafe { (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr() }, + #[cfg(multi_core)] + Cpu::AppCpu => unsafe { (*core1_interrupt_peripheral()).app_mac_intr_map().as_ptr() }, + }; + let cpu_intr = unsafe { intr_map_base.offset(interrupt_number).read_volatile() }; + let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?; + + if cpu_intr.is_peripheral() { + Some(cpu_intr) + } else { + None + } +} + /// Disable the given peripheral interrupt pub fn disable(core: Cpu, interrupt: Interrupt) { unsafe { @@ -185,7 +230,7 @@ pub fn clear(_core: Cpu, which: CpuInterrupt) { /// Get status of peripheral interrupts #[cfg(large_intr_status)] -pub fn get_status(core: Cpu) -> InterruptStatus { +pub fn status(core: Cpu) -> InterruptStatus { unsafe { match core { Cpu::ProCpu => InterruptStatus::from( @@ -223,7 +268,7 @@ pub fn get_status(core: Cpu) -> InterruptStatus { /// Get status of peripheral interrupts #[cfg(very_large_intr_status)] -pub fn get_status(core: Cpu) -> InterruptStatus { +pub fn status(core: Cpu) -> InterruptStatus { unsafe { match core { Cpu::ProCpu => InterruptStatus::from( @@ -293,7 +338,6 @@ mod vectored { use procmacros::ram; use super::*; - use crate::get_core; /// Interrupt priority levels. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -369,11 +413,7 @@ mod vectored { /// Get the interrupts configured for the core #[inline(always)] - fn get_configured_interrupts( - core: Cpu, - status: InterruptStatus, - level: u32, - ) -> InterruptStatus { + fn configured_interrupts(core: Cpu, status: InterruptStatus, level: u32) -> InterruptStatus { unsafe { let intr_map_base = match core { Cpu::ProCpu => (*core0_interrupt_peripheral()).pro_mac_intr_map().as_ptr(), @@ -406,7 +446,7 @@ mod vectored { interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?; unsafe { - map(get_core(), interrupt, cpu_interrupt); + map(Cpu::current(), interrupt, cpu_interrupt); xtensa_lx::interrupt::enable_mask( xtensa_lx::interrupt::get_mask() | 1 << cpu_interrupt as u32, @@ -415,17 +455,29 @@ mod vectored { Ok(()) } - /// Bind the given interrupt to the given handler + /// Binds the given interrupt to the given handler. /// /// # Safety /// /// This will replace any previously bound interrupt handler - pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn() -> ()) { + pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) { let ptr = &peripherals::__INTERRUPTS[interrupt as usize]._handler as *const _ - as *mut unsafe extern "C" fn() -> (); + as *mut unsafe extern "C" fn(); ptr.write_volatile(handler); } + /// Returns the currently bound interrupt handler. + pub fn bound_handler(interrupt: Interrupt) -> Option { + unsafe { + let addr = peripherals::__INTERRUPTS[interrupt as usize]._handler; + if addr as usize == 0 { + return None; + } + + Some(addr) + } + } + fn interrupt_level_to_cpu_interrupt( level: Priority, is_edge: bool, @@ -488,7 +540,7 @@ mod vectored { #[no_mangle] #[ram] unsafe fn handle_interrupts(level: u32, save_frame: &mut Context) { - let core = crate::get_core(); + let core = Cpu::current(); let cpu_interrupt_mask = interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[level as usize]; @@ -526,10 +578,10 @@ mod vectored { } else { // Finally, check level-triggered peripheral sources. // These interrupts are cleared by the peripheral. - get_status(core) + status(core) }; - let configured_interrupts = get_configured_interrupts(core, status, level); + let configured_interrupts = configured_interrupts(core, status, level); for interrupt_nr in configured_interrupts.iterator() { // Don't use `Interrupt::try_from`. It's slower and placed in flash let interrupt: Interrupt = unsafe { core::mem::transmute(interrupt_nr as u16) }; diff --git a/esp-hal/src/lcd_cam/cam.rs b/esp-hal/src/lcd_cam/cam.rs index abf7b5d398f..5125d8b343d 100644 --- a/esp-hal/src/lcd_cam/cam.rs +++ b/esp-hal/src/lcd_cam/cam.rs @@ -16,12 +16,10 @@ //! master mode. //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::gpio::Io; //! # use esp_hal::lcd_cam::{cam::{Camera, RxEightBits}, LcdCam}; //! # use fugit::RateExtU32; //! # use esp_hal::dma_rx_stream_buffer; //! # use esp_hal::dma::{Dma, DmaPriority}; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! //! # let dma = Dma::new(peripherals.DMA); //! # let channel = dma.channel0; @@ -33,19 +31,19 @@ //! # DmaPriority::Priority0, //! # ); //! -//! let mclk_pin = io.pins.gpio15; -//! let vsync_pin = io.pins.gpio6; -//! let href_pin = io.pins.gpio7; -//! let pclk_pin = io.pins.gpio13; +//! let mclk_pin = peripherals.GPIO15; +//! let vsync_pin = peripherals.GPIO6; +//! let href_pin = peripherals.GPIO7; +//! let pclk_pin = peripherals.GPIO13; //! let data_pins = RxEightBits::new( -//! io.pins.gpio11, -//! io.pins.gpio9, -//! io.pins.gpio8, -//! io.pins.gpio10, -//! io.pins.gpio12, -//! io.pins.gpio18, -//! io.pins.gpio17, -//! io.pins.gpio16, +//! peripherals.GPIO11, +//! peripherals.GPIO9, +//! peripherals.GPIO8, +//! peripherals.GPIO10, +//! peripherals.GPIO12, +//! peripherals.GPIO18, +//! peripherals.GPIO17, +//! peripherals.GPIO16, //! ); //! //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); @@ -81,7 +79,7 @@ use crate::{ OutputSignal, Pull, }, - lcd_cam::{cam::private::RxPins, private::calculate_clkm, BitOrder, ByteOrder}, + lcd_cam::{calculate_clkm, BitOrder, ByteOrder}, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, }; @@ -243,8 +241,10 @@ impl<'d> Camera<'d> { mclk: impl Peripheral

+ 'd, ) -> Self { crate::into_mapped_ref!(mclk); + mclk.set_to_push_pull_output(crate::private::Internal); - mclk.connect_peripheral_to_output(OutputSignal::CAM_CLK, crate::private::Internal); + OutputSignal::CAM_CLK.connect_to(mclk); + self } @@ -256,7 +256,7 @@ impl<'d> Camera<'d> { crate::into_mapped_ref!(pclk); pclk.init_input(Pull::None, crate::private::Internal); - pclk.connect_input_to_peripheral(InputSignal::CAM_PCLK, crate::private::Internal); + InputSignal::CAM_PCLK.connect_to(pclk); self } @@ -271,9 +271,9 @@ impl<'d> Camera<'d> { crate::into_mapped_ref!(vsync, h_enable); vsync.init_input(Pull::None, crate::private::Internal); - vsync.connect_input_to_peripheral(InputSignal::CAM_V_SYNC, crate::private::Internal); + InputSignal::CAM_V_SYNC.connect_to(vsync); h_enable.init_input(Pull::None, crate::private::Internal); - h_enable.connect_input_to_peripheral(InputSignal::CAM_H_ENABLE, crate::private::Internal); + InputSignal::CAM_H_ENABLE.connect_to(h_enable); self.lcd_cam .cam_ctrl1() @@ -297,11 +297,11 @@ impl<'d> Camera<'d> { crate::into_mapped_ref!(vsync, hsync, h_enable); vsync.init_input(Pull::None, crate::private::Internal); - vsync.connect_input_to_peripheral(InputSignal::CAM_V_SYNC, crate::private::Internal); + InputSignal::CAM_V_SYNC.connect_to(vsync); hsync.init_input(Pull::None, crate::private::Internal); - hsync.connect_input_to_peripheral(InputSignal::CAM_H_SYNC, crate::private::Internal); + InputSignal::CAM_H_SYNC.connect_to(hsync); h_enable.init_input(Pull::None, crate::private::Internal); - h_enable.connect_input_to_peripheral(InputSignal::CAM_H_ENABLE, crate::private::Internal); + InputSignal::CAM_H_ENABLE.connect_to(h_enable); self.lcd_cam .cam_ctrl1() @@ -514,9 +514,9 @@ impl RxEightBits { (pin_7, InputSignal::CAM_DATA_7), ]; - for (mut pin, signal) in pairs.into_iter() { + for (pin, signal) in pairs.into_iter() { pin.init_input(Pull::None, crate::private::Internal); - pin.connect_input_to_peripheral(signal, crate::private::Internal); + signal.connect_to(pin); } Self { _pins: () } @@ -591,9 +591,9 @@ impl RxSixteenBits { (pin_15, InputSignal::CAM_DATA_15), ]; - for (mut pin, signal) in pairs.into_iter() { + for (pin, signal) in pairs.into_iter() { pin.init_input(Pull::None, crate::private::Internal); - pin.connect_input_to_peripheral(signal, crate::private::Internal); + signal.connect_to(pin); } Self { _pins: () } @@ -604,8 +604,7 @@ impl RxPins for RxSixteenBits { const BUS_WIDTH: usize = 2; } -mod private { - pub trait RxPins { - const BUS_WIDTH: usize; - } +#[doc(hidden)] +pub trait RxPins { + const BUS_WIDTH: usize; } diff --git a/esp-hal/src/lcd_cam/lcd/i8080.rs b/esp-hal/src/lcd_cam/lcd/i8080.rs index 72ab45dea80..109b658aa39 100644 --- a/esp-hal/src/lcd_cam/lcd/i8080.rs +++ b/esp-hal/src/lcd_cam/lcd/i8080.rs @@ -15,11 +15,9 @@ //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::gpio::Io; //! # use esp_hal::lcd_cam::{LcdCam, lcd::i8080::{Config, I8080, TxEightBits}}; //! # use esp_hal::dma_tx_buffer; //! # use esp_hal::dma::{Dma, DmaPriority, DmaTxBuf}; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! //! # let dma = Dma::new(peripherals.DMA); //! # let channel = dma.channel0; @@ -32,14 +30,14 @@ //! # ); //! //! let tx_pins = TxEightBits::new( -//! io.pins.gpio9, -//! io.pins.gpio46, -//! io.pins.gpio3, -//! io.pins.gpio8, -//! io.pins.gpio18, -//! io.pins.gpio17, -//! io.pins.gpio16, -//! io.pins.gpio15, +//! peripherals.GPIO9, +//! peripherals.GPIO46, +//! peripherals.GPIO3, +//! peripherals.GPIO8, +//! peripherals.GPIO18, +//! peripherals.GPIO17, +//! peripherals.GPIO16, +//! peripherals.GPIO15, //! ); //! let lcd_cam = LcdCam::new(peripherals.LCD_CAM); //! @@ -50,7 +48,7 @@ //! 20.MHz(), //! Config::default(), //! ) -//! .with_ctrl_pins(io.pins.gpio0, io.pins.gpio47); +//! .with_ctrl_pins(peripherals.GPIO0, peripherals.GPIO47); //! //! dma_buf.fill(&[0x55]); //! let transfer = i8080.send(0x3Au8, 0, dma_buf).unwrap(); // RGB565 @@ -75,12 +73,13 @@ use crate::{ OutputSignal, }, lcd_cam::{ - asynch::LCD_DONE_WAKER, - lcd::{i8080::private::TxPins, ClockMode, DelayMode, Phase, Polarity}, - private::{calculate_clkm, Instance}, + calculate_clkm, + lcd::{ClockMode, DelayMode, Phase, Polarity}, BitOrder, ByteOrder, + Instance, Lcd, + LCD_DONE_WAKER, }, peripheral::{Peripheral, PeripheralRef}, peripherals::LCD_CAM, @@ -216,13 +215,25 @@ impl<'d, DM: Mode> I8080<'d, DM> { } impl<'d, DM: Mode> I8080<'d, DM> { - /// Configures the byte order for data transmission. + /// Configures the byte order for data transmission in 16-bit mode. + /// This must be set to [ByteOrder::default()] when transmitting in 8-bit + /// mode. pub fn set_byte_order(&mut self, byte_order: ByteOrder) -> &mut Self { let is_inverted = byte_order != ByteOrder::default(); - self.lcd_cam.lcd_user().modify(|_, w| { - w.lcd_byte_order().bit(is_inverted); - w.lcd_8bits_order().bit(is_inverted) - }); + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_byte_order().bit(is_inverted)); + self + } + + /// Configures the byte order for data transmission in 8-bit mode. + /// This must be set to [ByteOrder::default()] when transmitting in 16-bit + /// mode. + pub fn set_8bits_order(&mut self, byte_order: ByteOrder) -> &mut Self { + let is_inverted = byte_order != ByteOrder::default(); + self.lcd_cam + .lcd_user() + .modify(|_, w| w.lcd_8bits_order().bit(is_inverted)); self } @@ -238,7 +249,7 @@ impl<'d, DM: Mode> I8080<'d, DM> { pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cs); cs.set_to_push_pull_output(crate::private::Internal); - cs.connect_peripheral_to_output(OutputSignal::LCD_CS, crate::private::Internal); + OutputSignal::LCD_CS.connect_to(cs); self } @@ -252,10 +263,10 @@ impl<'d, DM: Mode> I8080<'d, DM> { crate::into_mapped_ref!(dc, wrx); dc.set_to_push_pull_output(crate::private::Internal); - dc.connect_peripheral_to_output(OutputSignal::LCD_DC, crate::private::Internal); + OutputSignal::LCD_DC.connect_to(dc); wrx.set_to_push_pull_output(crate::private::Internal); - wrx.connect_peripheral_to_output(OutputSignal::LCD_PCLK, crate::private::Internal); + OutputSignal::LCD_PCLK.connect_to(wrx); self } @@ -627,9 +638,9 @@ impl<'d> TxPins for TxEightBits<'d> { OutputSignal::LCD_DATA_7, ]; - for (pin, signal) in self.pins.iter_mut().zip(SIGNALS.iter()) { + for (pin, signal) in self.pins.iter_mut().zip(SIGNALS.into_iter()) { pin.set_to_push_pull_output(crate::private::Internal); - pin.connect_peripheral_to_output(*signal, crate::private::Internal); + signal.connect_to(pin); } } } @@ -696,15 +707,14 @@ impl<'d> TxPins for TxSixteenBits<'d> { OutputSignal::LCD_DATA_15, ]; - for (pin, signal) in self.pins.iter_mut().zip(SIGNALS.iter()) { + for (pin, signal) in self.pins.iter_mut().zip(SIGNALS.into_iter()) { pin.set_to_push_pull_output(crate::private::Internal); - pin.connect_peripheral_to_output(*signal, crate::private::Internal); + signal.connect_to(pin); } } } -mod private { - pub trait TxPins { - fn configure(&mut self); - } +#[doc(hidden)] +pub trait TxPins { + fn configure(&mut self); } diff --git a/esp-hal/src/lcd_cam/mod.rs b/esp-hal/src/lcd_cam/mod.rs index 38a29f83b4a..b442b2450ef 100644 --- a/esp-hal/src/lcd_cam/mod.rs +++ b/esp-hal/src/lcd_cam/mod.rs @@ -10,12 +10,18 @@ pub mod lcd; use core::marker::PhantomData; +use embassy_sync::waitqueue::AtomicWaker; + use crate::{ interrupt::InterruptHandler, lcd_cam::{cam::Cam, lcd::Lcd}, + macros::handler, peripheral::Peripheral, - peripherals::LCD_CAM, + peripherals::{Interrupt, LCD_CAM}, system::{self, PeripheralClockControl}, + Async, + Blocking, + Cpu, InterruptConfigurable, }; @@ -27,7 +33,7 @@ pub struct LcdCam<'d, DM: crate::Mode> { pub cam: Cam<'d>, } -impl<'d> LcdCam<'d, crate::Blocking> { +impl<'d> LcdCam<'d, Blocking> { /// Creates a new `LcdCam` instance. pub fn new(lcd_cam: impl Peripheral

+ 'd) -> Self { crate::into_ref!(lcd_cam); @@ -40,56 +46,49 @@ impl<'d> LcdCam<'d, crate::Blocking> { lcd_cam: unsafe { lcd_cam.clone_unchecked() }, _mode: PhantomData, }, - cam: Cam { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + cam: Cam { lcd_cam }, + } + } + + /// Reconfigures the peripheral for asynchronous operation. + pub fn into_async(mut self) -> LcdCam<'d, Async> { + self.set_interrupt_handler(interrupt_handler); + LcdCam { + lcd: Lcd { + lcd_cam: self.lcd.lcd_cam, + _mode: PhantomData, }, + cam: self.cam, } } } -impl<'d> crate::private::Sealed for LcdCam<'d, crate::Blocking> {} +impl crate::private::Sealed for LcdCam<'_, Blocking> {} // TODO: This interrupt is shared with the Camera module, we should handle this // in a similar way to the gpio::IO -impl<'d> InterruptConfigurable for LcdCam<'d, crate::Blocking> { +impl InterruptConfigurable for LcdCam<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::LCD_CAM, - handler.handler(), - ); - crate::interrupt::enable(crate::peripherals::Interrupt::LCD_CAM, handler.priority()) - .unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::LCD_CAM); } + unsafe { crate::interrupt::bind_interrupt(Interrupt::LCD_CAM, handler.handler()) }; + unwrap!(crate::interrupt::enable( + Interrupt::LCD_CAM, + handler.priority() + )); } } -impl<'d> LcdCam<'d, crate::Async> { - /// Creates a new `LcdCam` instance for asynchronous operation. - pub fn new_async(lcd_cam: impl Peripheral

+ 'd) -> Self { - crate::into_ref!(lcd_cam); - - PeripheralClockControl::enable(system::Peripheral::LcdCam); - - unsafe { - crate::interrupt::bind_interrupt( - crate::peripherals::Interrupt::LCD_CAM, - asynch::interrupt_handler.handler(), - ); - } - crate::interrupt::enable( - crate::peripherals::Interrupt::LCD_CAM, - asynch::interrupt_handler.priority(), - ) - .unwrap(); - - Self { +impl<'d> LcdCam<'d, Async> { + /// Reconfigures the peripheral for blocking operation. + pub fn into_blocking(self) -> LcdCam<'d, Blocking> { + crate::interrupt::disable(Cpu::current(), Interrupt::LCD_CAM); + LcdCam { lcd: Lcd { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, + lcd_cam: self.lcd.lcd_cam, _mode: PhantomData, }, - cam: Cam { - lcd_cam: unsafe { lcd_cam.clone_unchecked() }, - }, + cam: self.cam, } } } @@ -109,212 +108,200 @@ pub enum BitOrder { #[derive(Debug, Clone, Copy, PartialEq, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ByteOrder { - /// Do not change bit order. + /// Do not change byte order. #[default] Native = 0, /// Invert byte order. Inverted = 1, } -#[doc(hidden)] -pub mod asynch { - use embassy_sync::waitqueue::AtomicWaker; - use procmacros::handler; - - use super::private::Instance; - - pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new(); +pub(crate) static LCD_DONE_WAKER: AtomicWaker = AtomicWaker::new(); - #[handler] - pub(crate) fn interrupt_handler() { - // TODO: this is a shared interrupt with Camera and here we ignore that! - if Instance::is_lcd_done_set() { - Instance::unlisten_lcd_done(); - LCD_DONE_WAKER.wake() - } +#[handler] +fn interrupt_handler() { + // TODO: this is a shared interrupt with Camera and here we ignore that! + if Instance::is_lcd_done_set() { + Instance::unlisten_lcd_done(); + LCD_DONE_WAKER.wake() } } -mod private { - use crate::peripherals::LCD_CAM; - - pub(crate) struct Instance; - - // NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and - // this is only implemented for the LCD side, when the Camera is implemented a - // CriticalSection will be needed to protect these shared registers. - impl Instance { - pub(crate) fn listen_lcd_done() { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_ena() - .modify(|_, w| w.lcd_trans_done_int_ena().set_bit()); - } - - pub(crate) fn unlisten_lcd_done() { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_ena() - .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); - } +pub(crate) struct Instance; + +// NOTE: the LCD_CAM interrupt registers are shared between LCD and Camera and +// this is only implemented for the LCD side, when the Camera is implemented a +// CriticalSection will be needed to protect these shared registers. +impl Instance { + pub(crate) fn listen_lcd_done() { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().set_bit()); + } - pub(crate) fn is_lcd_done_set() -> bool { - let lcd_cam = unsafe { LCD_CAM::steal() }; - lcd_cam - .lc_dma_int_raw() - .read() - .lcd_trans_done_int_raw() - .bit() - } + pub(crate) fn unlisten_lcd_done() { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_ena() + .modify(|_, w| w.lcd_trans_done_int_ena().clear_bit()); } - pub struct ClockDivider { - // Integral LCD clock divider value. (8 bits) - // Value 0 is treated as 256 - // Value 1 is treated as 2 - // Value N is treated as N - pub div_num: usize, - - // Fractional clock divider numerator value. (6 bits) - pub div_b: usize, - - // Fractional clock divider denominator value. (6 bits) - pub div_a: usize, + + pub(crate) fn is_lcd_done_set() -> bool { + let lcd_cam = unsafe { LCD_CAM::steal() }; + lcd_cam + .lc_dma_int_raw() + .read() + .lcd_trans_done_int_raw() + .bit() } +} +pub(crate) struct ClockDivider { + // Integral LCD clock divider value. (8 bits) + // Value 0 is treated as 256 + // Value 1 is treated as 2 + // Value N is treated as N + pub div_num: usize, + + // Fractional clock divider numerator value. (6 bits) + pub div_b: usize, + + // Fractional clock divider denominator value. (6 bits) + pub div_a: usize, +} - pub fn calculate_clkm( - desired_frequency: usize, - source_frequencies: &[usize], - ) -> (usize, ClockDivider) { - let mut result_freq = 0; - let mut result = None; - - for (i, &source_frequency) in source_frequencies.iter().enumerate() { - let div = calculate_closest_divider(source_frequency, desired_frequency); - if let Some(div) = div { - let freq = calculate_output_frequency(source_frequency, &div); - if result.is_none() || freq > result_freq { - result = Some((i, div)); - result_freq = freq; - } +pub(crate) fn calculate_clkm( + desired_frequency: usize, + source_frequencies: &[usize], +) -> (usize, ClockDivider) { + let mut result_freq = 0; + let mut result = None; + + for (i, &source_frequency) in source_frequencies.iter().enumerate() { + let div = calculate_closest_divider(source_frequency, desired_frequency); + if let Some(div) = div { + let freq = calculate_output_frequency(source_frequency, &div); + if result.is_none() || freq > result_freq { + result = Some((i, div)); + result_freq = freq; } } - - result.expect("Desired frequency was too low for the dividers to divide to") } - fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize { - let n = match divider.div_num { - 0 => 256, - 1 => 2, - _ => divider.div_num.min(256), - }; + result.expect("Desired frequency was too low for the dividers to divide to") +} - if divider.div_b != 0 && divider.div_a != 0 { - // OUTPUT = SOURCE / (N + B/A) - // OUTPUT = SOURCE / ((NA + B)/A) - // OUTPUT = (SOURCE * A) / (NA + B) +fn calculate_output_frequency(source_frequency: usize, divider: &ClockDivider) -> usize { + let n = match divider.div_num { + 0 => 256, + 1 => 2, + _ => divider.div_num.min(256), + }; - // u64 is required to fit the numbers from this arithmetic. + if divider.div_b != 0 && divider.div_a != 0 { + // OUTPUT = SOURCE / (N + B/A) + // OUTPUT = SOURCE / ((NA + B)/A) + // OUTPUT = (SOURCE * A) / (NA + B) - let source = source_frequency as u64; - let n = n as u64; - let a = divider.div_b as u64; - let b = divider.div_a as u64; + // u64 is required to fit the numbers from this arithmetic. - ((source * a) / (n * a + b)) as _ - } else { - source_frequency / n - } - } + let source = source_frequency as u64; + let n = n as u64; + let a = divider.div_b as u64; + let b = divider.div_a as u64; - fn calculate_closest_divider( - source_frequency: usize, - desired_frequency: usize, - ) -> Option { - let div_num = source_frequency / desired_frequency; - if div_num < 2 { - // Source clock isn't fast enough to reach the desired frequency. - // Return max output. - return Some(ClockDivider { - div_num: 1, - div_b: 0, - div_a: 0, - }); - } - if div_num > 256 { - // Source is too fast to divide to the desired frequency. Return None. - return None; - } + ((source * a) / (n * a + b)) as _ + } else { + source_frequency / n + } +} - let div_num = if div_num == 256 { 0 } else { div_num }; +fn calculate_closest_divider( + source_frequency: usize, + desired_frequency: usize, +) -> Option { + let div_num = source_frequency / desired_frequency; + if div_num < 2 { + // Source clock isn't fast enough to reach the desired frequency. + // Return max output. + return Some(ClockDivider { + div_num: 1, + div_b: 0, + div_a: 0, + }); + } + if div_num > 256 { + // Source is too fast to divide to the desired frequency. Return None. + return None; + } - let div_fraction = { - let div_remainder = source_frequency % desired_frequency; - let gcd = hcf(div_remainder, desired_frequency); - Fraction { - numerator: div_remainder / gcd, - denominator: desired_frequency / gcd, - } - }; + let div_num = if div_num == 256 { 0 } else { div_num }; - let divider = if div_fraction.numerator == 0 { - ClockDivider { - div_num, - div_b: 0, - div_a: 0, - } - } else { - let target = div_fraction; - let closest = farey_sequence(63) - .find(|curr| { - // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities - - let new_curr_num = curr.numerator * target.denominator; - let new_target_num = target.numerator * curr.denominator; - new_curr_num >= new_target_num - }) - .expect("The fraction must be between 0 and 1"); - - ClockDivider { - div_num, - div_b: closest.numerator, - div_a: closest.denominator, - } - }; - Some(divider) - } + let div_fraction = { + let div_remainder = source_frequency % desired_frequency; + let gcd = hcf(div_remainder, desired_frequency); + Fraction { + numerator: div_remainder / gcd, + denominator: desired_frequency / gcd, + } + }; - // https://en.wikipedia.org/wiki/Euclidean_algorithm - const fn hcf(a: usize, b: usize) -> usize { - if b != 0 { - hcf(b, a % b) - } else { - a + let divider = if div_fraction.numerator == 0 { + ClockDivider { + div_num, + div_b: 0, + div_a: 0, } - } + } else { + let target = div_fraction; + let closest = farey_sequence(63) + .find(|curr| { + // https://en.wikipedia.org/wiki/Fraction#Adding_unlike_quantities + + let new_curr_num = curr.numerator * target.denominator; + let new_target_num = target.numerator * curr.denominator; + new_curr_num >= new_target_num + }) + .expect("The fraction must be between 0 and 1"); + + ClockDivider { + div_num, + div_b: closest.numerator, + div_a: closest.denominator, + } + }; + Some(divider) +} - struct Fraction { - pub numerator: usize, - pub denominator: usize, +// https://en.wikipedia.org/wiki/Euclidean_algorithm +const fn hcf(a: usize, b: usize) -> usize { + if b != 0 { + hcf(b, a % b) + } else { + a } +} - // https://en.wikipedia.org/wiki/Farey_sequence#Next_term - fn farey_sequence(denominator: usize) -> impl Iterator { - let mut a = 0; - let mut b = 1; - let mut c = 1; - let mut d = denominator; - core::iter::from_fn(move || { - if a > denominator { - return None; - } - let next = Fraction { - numerator: a, - denominator: b, - }; - let k = (denominator + b) / d; - (a, b, c, d) = (c, d, k * c - a, k * d - b); - Some(next) - }) - } +struct Fraction { + pub numerator: usize, + pub denominator: usize, +} + +// https://en.wikipedia.org/wiki/Farey_sequence#Next_term +fn farey_sequence(denominator: usize) -> impl Iterator { + let mut a = 0; + let mut b = 1; + let mut c = 1; + let mut d = denominator; + core::iter::from_fn(move || { + if a > denominator { + return None; + } + let next = Fraction { + numerator: a, + denominator: b, + }; + let k = (denominator + b) / d; + (a, b, c, d) = (c, d, k * c - a, k * d - b); + Some(next) + }) } diff --git a/esp-hal/src/ledc/channel.rs b/esp-hal/src/ledc/channel.rs index fa27cc3689b..73267fc9a5b 100644 --- a/esp-hal/src/ledc/channel.rs +++ b/esp-hal/src/ledc/channel.rs @@ -189,7 +189,7 @@ where fn set_duty(&self, duty_pct: u8) -> Result<(), Error> { let duty_exp; if let Some(timer) = self.timer { - if let Some(timer_duty) = timer.get_duty() { + if let Some(timer_duty) = timer.duty() { duty_exp = timer_duty as u32; } else { return Err(Error::Timer); @@ -238,10 +238,10 @@ where return Err(Error::Fade(FadeError::EndDuty)); } if let Some(timer) = self.timer { - if let Some(timer_duty) = timer.get_duty() { - if timer.get_frequency() > 0 { + if let Some(timer_duty) = timer.duty() { + if timer.frequency() > 0 { duty_exp = timer_duty as u32; - frequency = timer.get_frequency(); + frequency = timer.frequency(); } else { return Err(Error::Timer); } @@ -312,7 +312,7 @@ mod ehal1 { } } - impl<'a, S: TimerSpeed> ErrorType for Channel<'a, S> { + impl ErrorType for Channel<'_, S> { type Error = Error; } @@ -323,7 +323,7 @@ mod ehal1 { fn max_duty_cycle(&self) -> u16 { let duty_exp; - if let Some(timer_duty) = self.timer.and_then(|timer| timer.get_duty()) { + if let Some(timer_duty) = self.timer.and_then(|timer| timer.duty()) { duty_exp = timer_duty as u32; } else { return 0; @@ -343,7 +343,7 @@ mod ehal1 { } } -impl<'a, S: crate::ledc::timer::TimerSpeed> Channel<'a, S> { +impl Channel<'_, S> { #[cfg(esp32)] fn set_channel(&mut self, timer_number: u8) { if S::IS_HS { @@ -369,6 +369,13 @@ impl<'a, S: crate::ledc::timer::TimerSpeed> Channel<'a, S> { unsafe { w.timer_sel().bits(timer_number) } }); } + + // this is needed to make low duty-resolutions / high frequencies work + #[cfg(any(esp32h2, esp32c6))] + self.ledc + .ch_gamma_wr_addr(self.number as usize) + .write(|w| unsafe { w.bits(0) }); + self.start_duty_without_fading(); } @@ -541,7 +548,7 @@ impl<'a, S: crate::ledc::timer::TimerSpeed> Channel<'a, S> { } } -impl<'a, S> ChannelHW for Channel<'a, S> +impl ChannelHW for Channel<'_, S> where S: crate::ledc::timer::TimerSpeed, { @@ -564,7 +571,7 @@ where .set_to_open_drain_output(crate::private::Internal), }; - let timer_number = timer.get_number() as u8; + let timer_number = timer.number() as u8; self.set_channel(timer_number); self.update_channel(); @@ -610,8 +617,7 @@ where Number::Channel7 => OutputSignal::LEDC_LS_SIG7, }; - self.output_pin - .connect_peripheral_to_output(signal, crate::private::Internal); + signal.connect_to(&mut self.output_pin); } else { return Err(Error::Timer); } diff --git a/esp-hal/src/ledc/mod.rs b/esp-hal/src/ledc/mod.rs index 3b0a61cfefc..98b95ff72cd 100644 --- a/esp-hal/src/ledc/mod.rs +++ b/esp-hal/src/ledc/mod.rs @@ -29,14 +29,12 @@ //! # use esp_hal::ledc::timer; //! # use esp_hal::ledc::LowSpeed; //! # use esp_hal::ledc::channel; -//! # use esp_hal::gpio::Io; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let led = io.pins.gpio0; +//! # let led = peripherals.GPIO0; //! //! let mut ledc = Ledc::new(peripherals.LEDC); //! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); //! -//! let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); +//! let mut lstimer0 = ledc.timer::(timer::Number::Timer0); //! lstimer0 //! .configure(timer::config::Config { //! duty: timer::config::Duty::Duty5Bit, @@ -45,7 +43,7 @@ //! }) //! .unwrap(); //! -//! let mut channel0 = ledc.get_channel(channel::Number::Channel0, led); +//! let mut channel0 = ledc.channel(channel::Number::Channel0, led); //! channel0 //! .configure(channel::config::Config { //! timer: &lstimer0, @@ -160,12 +158,12 @@ impl<'d> Ledc<'d> { } /// Return a new timer - pub fn get_timer(&self, number: timer::Number) -> Timer<'d, S> { + pub fn timer(&self, number: timer::Number) -> Timer<'d, S> { Timer::new(self.ledc, number) } /// Return a new channel - pub fn get_channel( + pub fn channel( &self, number: channel::Number, output_pin: impl Peripheral

+ 'd, diff --git a/esp-hal/src/ledc/timer.rs b/esp-hal/src/ledc/timer.rs index 6b6c0f8c13b..30bf8082652 100644 --- a/esp-hal/src/ledc/timer.rs +++ b/esp-hal/src/ledc/timer.rs @@ -187,7 +187,7 @@ impl TimerSpeed for HighSpeed { /// Interface for Timers pub trait TimerIFace { /// Return the frequency of the timer - fn get_freq(&self) -> Option; + fn freq(&self) -> Option; /// Configure the timer fn configure(&mut self, config: config::Config) -> Result<(), Error>; @@ -196,19 +196,19 @@ pub trait TimerIFace { fn is_configured(&self) -> bool; /// Return the duty resolution of the timer - fn get_duty(&self) -> Option; + fn duty(&self) -> Option; /// Return the timer number - fn get_number(&self) -> Number; + fn number(&self) -> Number; /// Return the timer frequency, or 0 if not configured - fn get_frequency(&self) -> u32; + fn frequency(&self) -> u32; } /// Interface for HW configuration of timer pub trait TimerHW { /// Get the current source timer frequency from the HW - fn get_freq_hw(&self) -> Option; + fn freq_hw(&self) -> Option; /// Configure the HW for the timer fn configure_hw(&self, divisor: u32); @@ -233,8 +233,8 @@ where Timer<'a, S>: TimerHW, { /// Return the frequency of the timer - fn get_freq(&self) -> Option { - self.get_freq_hw() + fn freq(&self) -> Option { + self.freq_hw() } /// Configure the timer @@ -243,7 +243,7 @@ where self.clock_source = Some(config.clock_source); // TODO: we should return some error here if `unwrap()` fails - let src_freq: u32 = self.get_freq().unwrap().to_Hz(); + let src_freq: u32 = self.freq().unwrap().to_Hz(); let precision = 1 << config.duty as u32; let frequency: u32 = config.frequency.raw(); self.frequency = frequency; @@ -275,17 +275,17 @@ where } /// Return the duty resolution of the timer - fn get_duty(&self) -> Option { + fn duty(&self) -> Option { self.duty } /// Return the timer number - fn get_number(&self) -> Number { + fn number(&self) -> Number { self.number } /// Return the timer frequency - fn get_frequency(&self) -> u32 { + fn frequency(&self) -> u32 { self.frequency } } @@ -306,9 +306,9 @@ impl<'a, S: TimerSpeed> Timer<'a, S> { } /// Timer HW implementation for LowSpeed timers -impl<'a> TimerHW for Timer<'a, LowSpeed> { +impl TimerHW for Timer<'_, LowSpeed> { /// Get the current source timer frequency from the HW - fn get_freq_hw(&self) -> Option { + fn freq_hw(&self) -> Option { self.clock_source.map(|source| match source { LSClockSource::APBClk => { let clocks = Clocks::get(); @@ -371,7 +371,7 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { /// Timer HW implementation for HighSpeed timers impl<'a> TimerHW for Timer<'a, HighSpeed> { /// Get the current source timer frequency from the HW - fn get_freq_hw(&self) -> Option { + fn freq_hw(&self) -> Option { self.clock_source.map(|source| match source { HSClockSource::APBClk => { let clocks = Clocks::get(); diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 42c504a34b7..2743efd5457 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -81,8 +81,7 @@ //! }); //! //! // Set GPIO0 as an output, and set its state high initially. -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let mut led = Output::new(io.pins.gpio0, Level::High); +//! let mut led = Output::new(peripherals.GPIO0, Level::High); //! //! let delay = Delay::new(); //! @@ -374,24 +373,33 @@ impl Cpu { /// Returns the core the application is currently executing on #[inline(always)] pub fn current() -> Self { - get_core() + // This works for both RISCV and Xtensa because both + // get_raw_core functions return zero, _or_ something + // greater than zero; 1 in the case of RISCV and 0x2000 + // in the case of Xtensa. + match raw_core() { + 0 => Cpu::ProCpu, + #[cfg(all(multi_core, riscv))] + 1 => Cpu::AppCpu, + #[cfg(all(multi_core, xtensa))] + 0x2000 => Cpu::AppCpu, + _ => unreachable!(), + } } -} -/// Which core the application is currently executing on -#[inline(always)] -pub fn get_core() -> Cpu { - // This works for both RISCV and Xtensa because both - // get_raw_core functions return zero, _or_ something - // greater than zero; 1 in the case of RISCV and 0x2000 - // in the case of Xtensa. - match get_raw_core() { - 0 => Cpu::ProCpu, - #[cfg(all(multi_core, riscv))] - 1 => Cpu::AppCpu, - #[cfg(all(multi_core, xtensa))] - 0x2000 => Cpu::AppCpu, - _ => unreachable!(), + /// Returns an iterator over the "other" cores. + #[inline(always)] + pub fn other() -> impl Iterator { + cfg_if::cfg_if! { + if #[cfg(multi_core)] { + match Self::current() { + Cpu::ProCpu => [Cpu::AppCpu].into_iter(), + Cpu::AppCpu => [Cpu::ProCpu].into_iter(), + } + } else { + [].into_iter() + } + } } } @@ -400,7 +408,7 @@ pub fn get_core() -> Cpu { /// Safety: This method should never return UNUSED_THREAD_ID_VALUE #[cfg(riscv)] #[inline(always)] -fn get_raw_core() -> usize { +fn raw_core() -> usize { #[cfg(multi_core)] { riscv::register::mhartid::read() @@ -419,7 +427,7 @@ fn get_raw_core() -> usize { /// Safety: This method should never return UNUSED_THREAD_ID_VALUE #[cfg(xtensa)] #[inline(always)] -fn get_raw_core() -> usize { +fn raw_core() -> usize { (xtensa_lx::get_processor_id() & 0x2000) as usize } @@ -513,7 +521,8 @@ pub fn init(config: Config) -> Peripherals { match config.watchdog.rwdt { WatchdogStatus::Enabled(duration) => { rtc.rwdt.enable(); - rtc.rwdt.set_timeout(duration); + rtc.rwdt + .set_timeout(crate::rtc_cntl::RwdtStage::Stage0, duration); } WatchdogStatus::Disabled => { rtc.rwdt.disable(); @@ -524,7 +533,7 @@ pub fn init(config: Config) -> Peripherals { WatchdogStatus::Enabled(duration) => { let mut timg0_wd = crate::timer::timg::Wdt::::new(); timg0_wd.enable(); - timg0_wd.set_timeout(duration); + timg0_wd.set_timeout(crate::timer::timg::MwdtStage::Stage0, duration); } WatchdogStatus::Disabled => { crate::timer::timg::Wdt::::new().disable(); @@ -536,7 +545,7 @@ pub fn init(config: Config) -> Peripherals { WatchdogStatus::Enabled(duration) => { let mut timg1_wd = crate::timer::timg::Wdt::::new(); timg1_wd.enable(); - timg1_wd.set_timeout(duration); + timg1_wd.set_timeout(crate::timer::timg::MwdtStage::Stage0, duration); } WatchdogStatus::Disabled => { crate::timer::timg::Wdt::::new().disable(); @@ -548,5 +557,7 @@ pub fn init(config: Config) -> Peripherals { #[cfg(esp32)] crate::time::time_init(); + crate::gpio::bind_default_interrupt_handler(); + peripherals } diff --git a/esp-hal/src/macros.rs b/esp-hal/src/macros.rs index 4f2fa8748b1..f4274d8033d 100644 --- a/esp-hal/src/macros.rs +++ b/esp-hal/src/macros.rs @@ -26,6 +26,23 @@ macro_rules! before_snippet { }; } +#[doc(hidden)] +#[macro_export] +macro_rules! trm_markdown_link { + () => { + concat!("[Technical Reference Manual](", $crate::trm_link!(), ")") + }; + ($anchor:literal) => { + concat!( + "[Technical Reference Manual](", + $crate::trm_link!(), + "#", + $anchor, + ")" + ) + }; +} + #[doc(hidden)] /// Shorthand to define enums with From implementations. #[macro_export] @@ -70,18 +87,6 @@ macro_rules! any_peripheral { $vis struct $name([< $name Inner >]); impl $crate::private::Sealed for $name {} - impl $crate::dma::PeripheralMarker for $name { - #[inline(always)] - fn peripheral(&self) -> $crate::system::Peripheral { - match &self.0 { - $( - $(#[cfg($variant_meta)])* - [<$name Inner>]::$variant(inner) => inner.peripheral(), - )* - } - } - } - impl $crate::peripheral::Peripheral for $name { type P = $name; diff --git a/esp-hal/src/mcpwm/mod.rs b/esp-hal/src/mcpwm/mod.rs index a4907be376f..dc981794a96 100644 --- a/esp-hal/src/mcpwm/mod.rs +++ b/esp-hal/src/mcpwm/mod.rs @@ -52,10 +52,7 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::mcpwm::{operator::{DeadTimeCfg, PWMStream, PwmPinConfig}, timer::PwmWorkingMode, McPwm, PeripheralClockConfig}; -//! # use esp_hal::gpio::Io; -//! -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let pin = io.pins.gpio0; +//! # let pin = peripherals.GPIO0; //! //! // initialize peripheral #![cfg_attr( diff --git a/esp-hal/src/mcpwm/operator.rs b/esp-hal/src/mcpwm/operator.rs index 0b20764b364..8c99ee03ecd 100644 --- a/esp-hal/src/mcpwm/operator.rs +++ b/esp-hal/src/mcpwm/operator.rs @@ -298,9 +298,7 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, pin.set_actions(config.actions); pin.set_update_method(config.update_method); - let output_signal = PWM::output_signal::(); - pin.pin - .connect_peripheral_to_output(output_signal, private::Internal); + PWM::output_signal::().connect_to(&mut pin.pin); pin.pin.enable_output(true, private::Internal); pin @@ -315,7 +313,7 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, // SAFETY: // `bits` is a valid bit pattern - ch.gen((!IS_A) as usize).write(|w| unsafe { w.bits(bits) }) + ch.gen((!IS_A) as usize).write(|w| unsafe { w.bits(bits) }); } /// Set how a new timestamp syncs with the timer @@ -336,7 +334,7 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, } else { w.b_upmethod().bits(bits) } - }) + }); } /// Write a new timestamp. @@ -349,23 +347,23 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, #[cfg(esp32s3)] if IS_A { - ch.cmpr_value0().write(|w| unsafe { w.a().bits(value) }) + ch.cmpr_value0().write(|w| unsafe { w.a().bits(value) }); } else { - ch.cmpr_value1().write(|w| unsafe { w.b().bits(value) }) + ch.cmpr_value1().write(|w| unsafe { w.b().bits(value) }); } #[cfg(any(esp32, esp32c6, esp32h2))] if IS_A { - ch.gen_tstmp_a().write(|w| unsafe { w.a().bits(value) }) + ch.gen_tstmp_a().write(|w| unsafe { w.a().bits(value) }); } else { - ch.gen_tstmp_b().write(|w| unsafe { w.b().bits(value) }) + ch.gen_tstmp_b().write(|w| unsafe { w.b().bits(value) }); } } /// Get the old timestamp. /// The value of the timestamp will take effect according to the set /// [`PwmUpdateMethod`]. - pub fn get_timestamp(&self) -> u16 { + pub fn timestamp(&self) -> u16 { // SAFETY: // We only read to our GENx_TSTMP_x register let ch = unsafe { Self::ch() }; @@ -386,7 +384,7 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, } /// Get the period of the timer. - pub fn get_period(&self) -> u16 { + pub fn period(&self) -> u16 { // SAFETY: // We only grant access to our CFG0 register with the lifetime of &mut self let block = unsafe { &*PWM::block() }; @@ -413,8 +411,8 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> PwmPin<'d, PWM, OP, } } -impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal_02::PwmPin - for PwmPin<'d, PWM, OP, IS_A> +impl embedded_hal_02::PwmPin + for PwmPin<'_, PWM, OP, IS_A> { type Duty = u16; @@ -432,12 +430,12 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal_02::Pw /// Get the duty of the pin fn get_duty(&self) -> Self::Duty { - self.get_timestamp() + self.timestamp() } /// Get the max duty of the pin fn get_max_duty(&self) -> Self::Duty { - self.get_period() + self.period() } /// Set the duty of the pin @@ -447,19 +445,19 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal_02::Pw } /// Implement no error type for the PwmPin because the method are infallible -impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::ErrorType - for PwmPin<'d, PWM, OP, IS_A> +impl embedded_hal::pwm::ErrorType + for PwmPin<'_, PWM, OP, IS_A> { type Error = core::convert::Infallible; } /// Implement the trait SetDutyCycle for PwmPin -impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm::SetDutyCycle - for PwmPin<'d, PWM, OP, IS_A> +impl embedded_hal::pwm::SetDutyCycle + for PwmPin<'_, PWM, OP, IS_A> { /// Get the max duty of the PwmPin fn max_duty_cycle(&self) -> u16 { - self.get_period() + self.period() } /// Set the max duty of the PwmPin @@ -481,8 +479,6 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm:: /// # use esp_hal::{mcpwm, prelude::*}; /// # use esp_hal::mcpwm::{McPwm, PeripheralClockConfig}; /// # use esp_hal::mcpwm::operator::{DeadTimeCfg, PwmPinConfig, PWMStream}; -/// # use esp_hal::gpio::Io; -/// # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); /// // active high complementary using PWMA input /// let bridge_active = DeadTimeCfg::new_ahc(); /// // use PWMB as input for both outputs @@ -499,9 +495,9 @@ impl<'d, PWM: PwmPeripheral, const OP: u8, const IS_A: bool> embedded_hal::pwm:: /// let mut mcpwm = McPwm::new(peripherals.MCPWM0, clock_cfg); /// /// let mut pins = mcpwm.operator0.with_linked_pins( -/// io.pins.gpio0, +/// peripherals.GPIO0, /// PwmPinConfig::UP_DOWN_ACTIVE_HIGH, // use PWMA as our main input -/// io.pins.gpio1, +/// peripherals.GPIO1, /// PwmPinConfig::EMPTY, // keep PWMB "low" /// bridge_off, /// ); diff --git a/esp-hal/src/otg_fs.rs b/esp-hal/src/otg_fs.rs index 095126468dd..97e0d246f0c 100644 --- a/esp-hal/src/otg_fs.rs +++ b/esp-hal/src/otg_fs.rs @@ -43,7 +43,6 @@ use crate::{ gpio::InputSignal, peripheral::{Peripheral, PeripheralRef}, peripherals, - private::Internal, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, }; @@ -99,10 +98,10 @@ impl<'d> Usb<'d> { use crate::gpio::Level; - Level::High.connect_input_to_peripheral(InputSignal::USB_OTG_IDDIG, Internal); // connected connector is mini-B side - Level::High.connect_input_to_peripheral(InputSignal::USB_SRP_BVALID, Internal); // HIGH to force USB device mode - Level::High.connect_input_to_peripheral(InputSignal::USB_OTG_VBUSVALID, Internal); // receiving a valid Vbus from device - Level::Low.connect_input_to_peripheral(InputSignal::USB_OTG_AVALID, Internal); + InputSignal::USB_OTG_IDDIG.connect_to(Level::High); // connected connector is mini-B side + InputSignal::USB_SRP_BVALID.connect_to(Level::High); // HIGH to force USB device mode + InputSignal::USB_OTG_VBUSVALID.connect_to(Level::High); // receiving a valid Vbus from device + InputSignal::USB_OTG_AVALID.connect_to(Level::Low); } } diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs index 4e698a5a259..2d95e1427ec 100644 --- a/esp-hal/src/parl_io.rs +++ b/esp-hal/src/parl_io.rs @@ -49,11 +49,15 @@ use crate::{ Tx, WriteBuffer, }, - gpio::interconnect::{InputConnection, OutputConnection, PeripheralInput, PeripheralOutput}, + gpio::{ + interconnect::{InputConnection, OutputConnection, PeripheralInput, PeripheralOutput}, + NoPin, + }, interrupt::InterruptHandler, peripheral::{self, Peripheral}, - peripherals::{self, PARL_IO}, + peripherals::{self, Interrupt, PARL_IO}, system::PeripheralClockControl, + Async, Blocking, InterruptConfigurable, Mode, @@ -259,24 +263,17 @@ pub enum EofMode { } /// Used to configure no pin as clock output -pub struct NoClkPin; -impl TxClkPin for NoClkPin { +impl TxClkPin for NoPin { fn configure(&mut self) { - // nothing + crate::gpio::OutputSignal::PARL_TX_CLK.connect_to(self); } } -impl RxClkPin for NoClkPin { +impl RxClkPin for NoPin { fn configure(&mut self) { - // nothing + crate::gpio::InputSignal::PARL_RX_CLK.connect_to(self); } } -/// This can be used to pass to the `with_config` functions -pub fn no_clk_pin() -> &'static mut NoClkPin { - static mut NO_CLK: NoClkPin = NoClkPin; - unsafe { &mut *core::ptr::addr_of_mut!(NO_CLK) } -} - /// Wraps a GPIO pin which will be used as the clock output signal pub struct ClkOutPin<'d> { pin: PeripheralRef<'d, OutputConnection>, @@ -291,10 +288,7 @@ impl<'d> ClkOutPin<'d> { impl TxClkPin for ClkOutPin<'_> { fn configure(&mut self) { self.pin.set_to_push_pull_output(crate::private::Internal); - self.pin.connect_peripheral_to_output( - crate::gpio::OutputSignal::PARL_TX_CLK, - crate::private::Internal, - ); + crate::gpio::OutputSignal::PARL_TX_CLK.connect_to(&mut self.pin); } } @@ -317,10 +311,7 @@ impl TxClkPin for ClkInPin<'_> { self.pin .init_input(crate::gpio::Pull::None, crate::private::Internal); - self.pin.connect_input_to_peripheral( - crate::gpio::InputSignal::PARL_TX_CLK, - crate::private::Internal, - ); + crate::gpio::InputSignal::PARL_TX_CLK.connect_to(&mut self.pin); } } @@ -339,7 +330,7 @@ impl<'d> RxClkInPin<'d> { Self { pin, sample_edge } } } -impl<'d> RxClkPin for RxClkInPin<'d> { +impl RxClkPin for RxClkInPin<'_> { fn configure(&mut self) { let pcr = unsafe { &*crate::peripherals::PCR::PTR }; pcr.parl_clk_rx_conf() @@ -347,10 +338,7 @@ impl<'d> RxClkPin for RxClkInPin<'d> { self.pin .init_input(crate::gpio::Pull::None, crate::private::Internal); - self.pin.connect_input_to_peripheral( - crate::gpio::InputSignal::PARL_RX_CLK, - crate::private::Internal, - ); + crate::gpio::InputSignal::PARL_RX_CLK.connect_to(&mut self.pin); Instance::set_rx_clk_edge_sel(self.sample_edge); } @@ -376,12 +364,12 @@ where } } -impl<'d, P> TxPins for TxPinConfigWithValidPin<'d, P> where +impl

TxPins for TxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins { } -impl<'d, P> ConfigurePins for TxPinConfigWithValidPin<'d, P> +impl

ConfigurePins for TxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + TxPins + ConfigurePins, { @@ -389,10 +377,7 @@ where self.tx_pins.configure()?; self.valid_pin .set_to_push_pull_output(crate::private::Internal); - self.valid_pin.connect_peripheral_to_output( - Instance::tx_valid_pin_signal(), - crate::private::Internal, - ); + Instance::tx_valid_pin_signal().connect_to(&mut self.valid_pin); Instance::set_tx_hw_valid_en(true); Ok(()) } @@ -463,7 +448,7 @@ macro_rules! tx_pins { fn configure(&mut self) -> Result<(), Error>{ $( self.[< pin_ $pin:lower >].set_to_push_pull_output(crate::private::Internal); - self.[< pin_ $pin:lower >].connect_peripheral_to_output(crate::gpio::OutputSignal::$signal, crate::private::Internal); + crate::gpio::OutputSignal::$signal.connect_to(&mut self.[< pin_ $pin:lower >]); )+ private::Instance::set_tx_bit_width( private::WidSel::[< Bits $width >]); @@ -570,12 +555,12 @@ where } } -impl<'d, P> RxPins for RxPinConfigWithValidPin<'d, P> where +impl

RxPins for RxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins { } -impl<'d, P> ConfigurePins for RxPinConfigWithValidPin<'d, P> +impl

ConfigurePins for RxPinConfigWithValidPin<'_, P> where P: NotContainsValidSignalPin + RxPins + ConfigurePins, { @@ -583,8 +568,7 @@ where self.rx_pins.configure()?; self.valid_pin .init_input(crate::gpio::Pull::None, crate::private::Internal); - self.valid_pin - .connect_input_to_peripheral(Instance::rx_valid_pin_signal(), crate::private::Internal); + Instance::rx_valid_pin_signal().connect_to(&mut self.valid_pin); Instance::set_rx_sw_en(false); if let Some(sel) = self.enable_mode.pulse_submode_sel() { Instance::set_rx_pulse_submode_sel(sel); @@ -683,7 +667,7 @@ macro_rules! rx_pins { fn configure(&mut self) -> Result<(), Error> { $( self.[< pin_ $pin:lower >].init_input(crate::gpio::Pull::None, crate::private::Internal); - self.[< pin_ $pin:lower >].connect_input_to_peripheral(crate::gpio::InputSignal::$signal, crate::private::Internal); + crate::gpio::InputSignal::$signal.connect_to(&mut self.[< pin_ $pin:lower >]); )+ private::Instance::set_rx_bit_width( private::WidSel::[< Bits $width >]); @@ -832,7 +816,7 @@ where phantom: PhantomData, } -impl<'d, DM> core::fmt::Debug for ParlIoTx<'d, DM> +impl core::fmt::Debug for ParlIoTx<'_, DM> where DM: Mode, { @@ -911,7 +895,7 @@ where phantom: PhantomData, } -impl<'d, DM> core::fmt::Debug for ParlIoRx<'d, DM> +impl core::fmt::Debug for ParlIoRx<'_, DM> where DM: Mode, { @@ -923,42 +907,52 @@ where fn internal_set_interrupt_handler(handler: InterruptHandler) { #[cfg(esp32c6)] { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::PARL_IO); + } + internal_listen(EnumSet::all(), false); + internal_clear_interrupts(EnumSet::all()); unsafe { PARL_IO::steal() }.bind_parl_io_interrupt(handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::PARL_IO, handler.priority()) - .unwrap(); + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO, + handler.priority() + )); } #[cfg(esp32h2)] { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::PARL_IO_RX); + crate::interrupt::disable(core, Interrupt::PARL_IO_TX); + } + internal_listen(EnumSet::all(), false); + internal_clear_interrupts(EnumSet::all()); unsafe { PARL_IO::steal() }.bind_parl_io_tx_interrupt(handler.handler()); unsafe { PARL_IO::steal() }.bind_parl_io_rx_interrupt(handler.handler()); - crate::interrupt::enable( - crate::peripherals::Interrupt::PARL_IO_TX, + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO_TX, handler.priority(), - ) - .unwrap(); - crate::interrupt::enable( - crate::peripherals::Interrupt::PARL_IO_RX, + )); + unwrap!(crate::interrupt::enable( + Interrupt::PARL_IO_RX, handler.priority(), - ) - .unwrap(); + )); } } fn internal_listen(interrupts: EnumSet, enable: bool) { let parl_io = unsafe { PARL_IO::steal() }; - for interrupt in interrupts { - match interrupt { - ParlIoInterrupt::TxFifoReEmpty => parl_io - .int_ena() - .modify(|_, w| w.tx_fifo_rempty().bit(enable)), - ParlIoInterrupt::RxFifoWOvf => parl_io - .int_ena() - .modify(|_, w| w.rx_fifo_wovf().bit(enable)), - ParlIoInterrupt::TxEof => parl_io.int_ena().write(|w| w.tx_eof().bit(enable)), - } - } + parl_io.int_ena().write(|w| { + for interrupt in interrupts { + match interrupt { + ParlIoInterrupt::TxFifoReEmpty => w.tx_fifo_rempty().bit(enable), + ParlIoInterrupt::RxFifoWOvf => w.rx_fifo_wovf().bit(enable), + ParlIoInterrupt::TxEof => w.tx_eof().bit(enable), + }; + } + w + }); } fn internal_interrupts() -> EnumSet { @@ -980,17 +974,16 @@ fn internal_interrupts() -> EnumSet { fn internal_clear_interrupts(interrupts: EnumSet) { let parl_io = unsafe { PARL_IO::steal() }; - for interrupt in interrupts { - match interrupt { - ParlIoInterrupt::TxFifoReEmpty => parl_io - .int_clr() - .write(|w| w.tx_fifo_rempty().clear_bit_by_one()), - ParlIoInterrupt::RxFifoWOvf => parl_io - .int_clr() - .write(|w| w.rx_fifo_wovf().clear_bit_by_one()), - ParlIoInterrupt::TxEof => parl_io.int_clr().write(|w| w.tx_eof().clear_bit_by_one()), - } - } + parl_io.int_clr().write(|w| { + for interrupt in interrupts { + match interrupt { + ParlIoInterrupt::TxFifoReEmpty => w.tx_fifo_rempty().clear_bit_by_one(), + ParlIoInterrupt::RxFifoWOvf => w.rx_fifo_wovf().clear_bit_by_one(), + ParlIoInterrupt::TxEof => w.tx_eof().clear_bit_by_one(), + }; + } + w + }); } /// Parallel IO in full duplex mode @@ -1008,12 +1001,9 @@ where pub rx: RxCreatorFullDuplex<'d, DM>, } -impl<'d, DM> ParlIoFullDuplex<'d, DM> -where - DM: Mode, -{ +impl<'d> ParlIoFullDuplex<'d, Blocking> { /// Create a new instance of [ParlIoFullDuplex] - pub fn new( + pub fn new( _parl_io: impl Peripheral

+ 'd, dma_channel: Channel<'d, CH, DM>, tx_descriptors: &'static mut [DmaDescriptor], @@ -1021,8 +1011,11 @@ where frequency: HertzU32, ) -> Result where + DM: Mode, CH: DmaChannelConvert<::Dma>, + Channel<'d, CH, Blocking>: From>, { + let dma_channel = Channel::<'d, CH, Blocking>::from(dma_channel); internal_init(frequency)?; Ok(Self { @@ -1038,9 +1031,29 @@ where }, }) } -} -impl<'d> ParlIoFullDuplex<'d, Blocking> { + /// Convert to an async version. + pub fn into_async(self) -> ParlIoFullDuplex<'d, Async> { + let channel = Channel { + tx: self.tx.tx_channel, + rx: self.rx.rx_channel, + phantom: PhantomData::, + }; + let channel = channel.into_async(); + ParlIoFullDuplex { + tx: TxCreatorFullDuplex { + tx_channel: channel.tx, + descriptors: self.tx.descriptors, + phantom: PhantomData, + }, + rx: RxCreatorFullDuplex { + rx_channel: channel.rx, + descriptors: self.rx.descriptors, + phantom: PhantomData, + }, + } + } + /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// @@ -1070,14 +1083,38 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> { } } -impl<'d> crate::private::Sealed for ParlIoFullDuplex<'d, Blocking> {} +impl crate::private::Sealed for ParlIoFullDuplex<'_, Blocking> {} -impl<'d> InterruptConfigurable for ParlIoFullDuplex<'d, Blocking> { +impl InterruptConfigurable for ParlIoFullDuplex<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoFullDuplex::set_interrupt_handler(self, handler); } } +impl<'d> ParlIoFullDuplex<'d, Async> { + /// Convert to a blocking version. + pub fn into_blocking(self) -> ParlIoFullDuplex<'d, Blocking> { + let channel = Channel { + tx: self.tx.tx_channel, + rx: self.rx.rx_channel, + phantom: PhantomData::, + }; + let channel = channel.into_blocking(); + ParlIoFullDuplex { + tx: TxCreatorFullDuplex { + tx_channel: channel.tx, + descriptors: self.tx.descriptors, + phantom: PhantomData, + }, + rx: RxCreatorFullDuplex { + rx_channel: channel.rx, + descriptors: self.rx.descriptors, + phantom: PhantomData, + }, + } + } +} + /// Parallel IO in half duplex / TX only mode pub struct ParlIoTxOnly<'d, DM> where @@ -1115,7 +1152,7 @@ where } } -impl<'d> ParlIoTxOnly<'d, Blocking> { +impl ParlIoTxOnly<'_, Blocking> { /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// @@ -1125,13 +1162,13 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { } /// Listen for the given interrupts - pub fn listen(&mut self, interrupts: EnumSet) { - internal_listen(interrupts, true); + pub fn listen(&mut self, interrupts: impl Into>) { + internal_listen(interrupts.into(), true); } /// Unlisten the given interrupts - pub fn unlisten(&mut self, interrupts: EnumSet) { - internal_listen(interrupts, false); + pub fn unlisten(&mut self, interrupts: impl Into>) { + internal_listen(interrupts.into(), false); } /// Gets asserted interrupts @@ -1140,14 +1177,14 @@ impl<'d> ParlIoTxOnly<'d, Blocking> { } /// Resets asserted interrupts - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - internal_clear_interrupts(interrupts); + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { + internal_clear_interrupts(interrupts.into()); } } -impl<'d> crate::private::Sealed for ParlIoTxOnly<'d, Blocking> {} +impl crate::private::Sealed for ParlIoTxOnly<'_, Blocking> {} -impl<'d> InterruptConfigurable for ParlIoTxOnly<'d, Blocking> { +impl InterruptConfigurable for ParlIoTxOnly<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoTxOnly::set_interrupt_handler(self, handler); } @@ -1190,7 +1227,7 @@ where } } -impl<'d> ParlIoRxOnly<'d, Blocking> { +impl ParlIoRxOnly<'_, Blocking> { /// Sets the interrupt handler, enables it with /// [crate::interrupt::Priority::min()] /// @@ -1200,13 +1237,13 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { } /// Listen for the given interrupts - pub fn listen(&mut self, interrupts: EnumSet) { - internal_listen(interrupts, true); + pub fn listen(&mut self, interrupts: impl Into>) { + internal_listen(interrupts.into(), true); } /// Unlisten the given interrupts - pub fn unlisten(&mut self, interrupts: EnumSet) { - internal_listen(interrupts, false); + pub fn unlisten(&mut self, interrupts: impl Into>) { + internal_listen(interrupts.into(), false); } /// Gets asserted interrupts @@ -1215,14 +1252,14 @@ impl<'d> ParlIoRxOnly<'d, Blocking> { } /// Resets asserted interrupts - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - internal_clear_interrupts(interrupts); + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { + internal_clear_interrupts(interrupts.into()); } } -impl<'d> crate::private::Sealed for ParlIoRxOnly<'d, Blocking> {} +impl crate::private::Sealed for ParlIoRxOnly<'_, Blocking> {} -impl<'d> InterruptConfigurable for ParlIoRxOnly<'d, Blocking> { +impl InterruptConfigurable for ParlIoRxOnly<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { ParlIoRxOnly::set_interrupt_handler(self, handler); } @@ -1261,7 +1298,7 @@ fn internal_init(frequency: HertzU32) -> Result<(), Error> { Ok(()) } -impl<'d, DM> ParlIoTx<'d, DM> +impl ParlIoTx<'_, DM> where DM: Mode, { @@ -1316,7 +1353,7 @@ where } } -impl<'d, DM> DmaSupport for ParlIoTx<'d, DM> +impl DmaSupport for ParlIoTx<'_, DM> where DM: Mode, { @@ -1405,7 +1442,7 @@ where } } -impl<'d, DM> DmaSupport for ParlIoRx<'d, DM> +impl DmaSupport for ParlIoRx<'_, DM> where DM: Mode, { @@ -1546,11 +1583,11 @@ pub mod asynch { } } - impl<'d> ParlIoTx<'d, crate::Async> { + impl ParlIoTx<'_, crate::Async> { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. - pub async fn write_dma_async<'t, TXBUF>(&mut self, words: &'t TXBUF) -> Result<(), Error> + pub async fn write_dma_async(&mut self, words: &TXBUF) -> Result<(), Error> where TXBUF: ReadBuffer, { @@ -1568,7 +1605,7 @@ pub mod asynch { } } - impl<'d> ParlIoRx<'d, crate::Async> { + impl ParlIoRx<'_, crate::Async> { /// Perform a DMA write. /// /// The maximum amount of data to be sent is 32736 bytes. diff --git a/esp-hal/src/pcnt/channel.rs b/esp-hal/src/pcnt/channel.rs index 34b1f971258..c1c565b38e2 100644 --- a/esp-hal/src/pcnt/channel.rs +++ b/esp-hal/src/pcnt/channel.rs @@ -22,7 +22,7 @@ pub struct Channel<'d, const UNIT: usize, const NUM: usize> { _not_send: PhantomData<*const ()>, } -impl<'d, const UNIT: usize, const NUM: usize> Channel<'d, UNIT, NUM> { +impl Channel<'_, UNIT, NUM> { /// return a new Channel pub(super) fn new() -> Self { Self { @@ -118,7 +118,7 @@ impl<'d, const UNIT: usize, const NUM: usize> Channel<'d, UNIT, NUM> { if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize { crate::into_mapped_ref!(source); source.enable_input(true, crate::private::Internal); - source.connect_input_to_peripheral(signal, crate::private::Internal); + signal.connect_to(source); } self } @@ -176,7 +176,7 @@ impl<'d, const UNIT: usize, const NUM: usize> Channel<'d, UNIT, NUM> { if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize { crate::into_mapped_ref!(source); source.enable_input(true, crate::private::Internal); - source.connect_input_to_peripheral(signal, crate::private::Internal); + signal.connect_to(source); } self } diff --git a/esp-hal/src/pcnt/mod.rs b/esp-hal/src/pcnt/mod.rs index 4c4817c46cf..742404b3f17 100644 --- a/esp-hal/src/pcnt/mod.rs +++ b/esp-hal/src/pcnt/mod.rs @@ -109,13 +109,14 @@ impl<'d> Pcnt<'d> { } } -impl<'d> crate::private::Sealed for Pcnt<'d> {} +impl crate::private::Sealed for Pcnt<'_> {} -impl<'d> InterruptConfigurable for Pcnt<'d> { +impl InterruptConfigurable for Pcnt<'_> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - interrupt::bind_interrupt(Interrupt::PCNT, handler.handler()); - interrupt::enable(Interrupt::PCNT, handler.priority()).unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::PCNT); } + unsafe { interrupt::bind_interrupt(Interrupt::PCNT, handler.handler()) }; + unwrap!(interrupt::enable(Interrupt::PCNT, handler.priority())); } } diff --git a/esp-hal/src/pcnt/unit.rs b/esp-hal/src/pcnt/unit.rs index 1f0006f64e6..fb1a0cf4914 100644 --- a/esp-hal/src/pcnt/unit.rs +++ b/esp-hal/src/pcnt/unit.rs @@ -84,7 +84,7 @@ pub struct Unit<'d, const NUM: usize> { pub channel1: Channel<'d, NUM, 1>, } -impl<'d, const NUM: usize> Unit<'d, NUM> { +impl Unit<'_, NUM> { /// return a new Unit pub(super) fn new() -> Self { Self { @@ -243,7 +243,7 @@ impl<'d, const NUM: usize> Unit<'d, NUM> { } /// Get the latest events for this unit. - pub fn get_events(&self) -> Events { + pub fn events(&self) -> Events { let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() }; let status = pcnt.u_status(NUM).read(); @@ -257,7 +257,7 @@ impl<'d, const NUM: usize> Unit<'d, NUM> { } /// Get the mode of the last zero crossing - pub fn get_zero_mode(&self) -> ZeroMode { + pub fn zero_mode(&self) -> ZeroMode { let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() }; pcnt.u_status(NUM).read().zero_mode().bits().into() } @@ -296,19 +296,19 @@ impl<'d, const NUM: usize> Unit<'d, NUM> { } /// Get the current counter value. - pub fn get_value(&self) -> i16 { + pub fn value(&self) -> i16 { self.counter.get() } } -impl<'d, const NUM: usize> Drop for Unit<'d, NUM> { +impl Drop for Unit<'_, NUM> { fn drop(&mut self) { // This is here to prevent the destructuring of Unit. } } // The entire Unit is Send but the individual channels are not. -unsafe impl<'d, const NUM: usize> Send for Unit<'d, NUM> {} +unsafe impl Send for Unit<'_, NUM> {} /// Represents the counter within a pulse counter unit. #[derive(Clone)] @@ -316,7 +316,7 @@ pub struct Counter<'d, const NUM: usize> { _phantom: PhantomData<&'d ()>, } -impl<'d, const NUM: usize> Counter<'d, NUM> { +impl Counter<'_, NUM> { fn new() -> Self { Self { _phantom: PhantomData, diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 7d5b665e2ea..c2c10a417fe 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -82,7 +82,7 @@ impl<'a, T> PeripheralRef<'a, T> { } } -impl<'a, T> Deref for PeripheralRef<'a, T> { +impl Deref for PeripheralRef<'_, T> { type Target = T; #[inline] @@ -91,7 +91,7 @@ impl<'a, T> Deref for PeripheralRef<'a, T> { } } -impl<'a, T> DerefMut for PeripheralRef<'a, T> { +impl DerefMut for PeripheralRef<'_, T> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner @@ -121,14 +121,14 @@ impl<'a, T> DerefMut for PeripheralRef<'a, T> { /// live forever (`'static`): /// /// ```rust, ignore -/// let mut uart: Uart<'static, ...> = Uart::new(p.UART0, pins.gpio0, pins.gpio1); +/// let mut uart: Uart<'static, ...> = Uart::new(p.UART0, p.GPIO0, p.GPIO1); /// ``` /// /// Or you may call it with borrowed peripherals, which yields an instance that /// can only live for as long as the borrows last: /// /// ```rust, ignore -/// let mut uart: Uart<'_, ...> = Uart::new(&mut p.UART0, &mut pins.gpio0, &mut pins.gpio1); +/// let mut uart: Uart<'_, ...> = Uart::new(&mut p.UART0, &mut p.GPIO0, &mut p.GPIO1); /// ``` /// /// # Implementation details, for HAL authors @@ -210,10 +210,7 @@ mod peripheral_macros { /// Creates a new `Peripherals` struct and its associated methods. /// /// The macro has a few fields doing different things, in the form of - /// `[first] second <= third (fourth)`. - /// - The first field is the name of the `Peripherals` enum variant. This is - /// optional and used to create `PeripheralMarker` implementations for - /// DMA-eligible peripherals. + /// `second <= third (fourth)`. /// - The second field is the name of the peripheral, as it appears in the /// `Peripherals` struct. /// - The third field is the name of the peripheral as it appears in the @@ -225,55 +222,77 @@ mod peripheral_macros { #[macro_export] macro_rules! peripherals { ( - $( - $([$enum_variant:ident])? $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? - ), *$(,)? + peripherals: [ + $( + $name:ident <= $from_pac:tt $(($($interrupt:ident),*))? + ), *$(,)? + ], + pins: [ + $( ( $pin:literal, $($pin_tokens:tt)* ) )* + ] ) => { /// Contains the generated peripherals which implement [`Peripheral`] mod peripherals { pub use super::pac::*; $( - $crate::create_peripheral!($([$enum_variant])? $name <= $from_pac); + $crate::create_peripheral!($name <= $from_pac); )* } - /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip. - #[allow(non_snake_case)] - pub struct Peripherals { - $( - /// Each field represents a hardware peripheral. - pub $name: peripherals::$name, - )* + pub(crate) mod gpio { + $crate::gpio! { + $( ($pin, $($pin_tokens)* ) )* + } } - impl Peripherals { - /// Returns all the peripherals *once* - #[inline] - pub(crate) fn take() -> Self { - #[no_mangle] - static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false; - - critical_section::with(|_| unsafe { - if _ESP_HAL_DEVICE_PERIPHERALS { - panic!("init called more than once!") - } - _ESP_HAL_DEVICE_PERIPHERALS = true; - Self::steal() - }) + paste::paste! { + /// The `Peripherals` struct provides access to all of the hardware peripherals on the chip. + #[allow(non_snake_case)] + pub struct Peripherals { + $( + #[doc = concat!("The ", stringify!($name), " peripheral.")] + pub $name: peripherals::$name, + )* + + $( + #[doc = concat!("GPIO", stringify!($pin))] + pub []: $crate::gpio::GpioPin<$pin>, + )* } - /// Unsafely create an instance of this peripheral out of thin air. - /// - /// # Safety - /// - /// You must ensure that you're only using one instance of this type at a time. - #[inline] - pub unsafe fn steal() -> Self { - Self { - $( - $name: peripherals::$name::steal(), - )* + impl Peripherals { + /// Returns all the peripherals *once* + #[inline] + pub(crate) fn take() -> Self { + #[no_mangle] + static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false; + + critical_section::with(|_| unsafe { + if _ESP_HAL_DEVICE_PERIPHERALS { + panic!("init called more than once!") + } + _ESP_HAL_DEVICE_PERIPHERALS = true; + Self::steal() + }) + } + + /// Unsafely create an instance of this peripheral out of thin air. + /// + /// # Safety + /// + /// You must ensure that you're only using one instance of this type at a time. + #[inline] + pub unsafe fn steal() -> Self { + Self { + $( + $name: peripherals::$name::steal(), + )* + + $( + []: $crate::gpio::GpioPin::<$pin>::steal(), + )* + } } } } @@ -297,8 +316,7 @@ mod peripheral_macros { } )* )* - - } + }; } #[doc(hidden)] @@ -327,7 +345,7 @@ mod peripheral_macros { #[macro_export] /// Macro to create a peripheral structure. macro_rules! create_peripheral { - ($([$enum_variant:ident])? $name:ident <= virtual) => { + ($name:ident <= virtual) => { #[derive(Debug)] #[allow(non_camel_case_types)] /// Represents a virtual peripheral with no associated hardware. @@ -358,15 +376,6 @@ mod peripheral_macros { } impl $crate::private::Sealed for $name {} - - $( - impl $crate::dma::PeripheralMarker for $crate::peripherals::$name { - #[inline(always)] - fn peripheral(&self) -> $crate::system::Peripheral { - $crate::system::Peripheral::$enum_variant - } - } - )? }; ($([$enum_variant:ident])? $name:ident <= $base:ident) => { diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 06dd5855cad..5a4d41a8505 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -26,7 +26,7 @@ mod imp { Pin as _esp_hal_gpio_Pin, }; #[cfg(any(i2c0, i2c1))] - pub use crate::i2c::Instance as _esp_hal_i2c_Instance; + pub use crate::i2c::master::Instance as _esp_hal_i2c_master_Instance; #[cfg(ledc)] pub use crate::ledc::{ channel::{ diff --git a/esp-hal/src/reset.rs b/esp-hal/src/reset.rs index 5b3486769d6..eeed2b09296 100644 --- a/esp-hal/src/reset.rs +++ b/esp-hal/src/reset.rs @@ -102,11 +102,11 @@ pub fn software_reset_cpu() { /// Retrieves the reason for the last reset as a SocResetReason enum value. /// Returns `None` if the reset reason cannot be determined. -pub fn get_reset_reason() -> Option { - crate::rtc_cntl::get_reset_reason(crate::get_core()) +pub fn reset_reason() -> Option { + crate::rtc_cntl::reset_reason(crate::Cpu::current()) } /// Retrieves the cause of the last wakeup event as a SleepSource enum value. -pub fn get_wakeup_cause() -> SleepSource { - crate::rtc_cntl::get_wakeup_cause() +pub fn wakeup_cause() -> SleepSource { + crate::rtc_cntl::wakeup_cause() } diff --git a/esp-hal/src/rmt.rs b/esp-hal/src/rmt.rs index ef61e03e5f9..dc2d4e916e3 100644 --- a/esp-hal/src/rmt.rs +++ b/esp-hal/src/rmt.rs @@ -54,16 +54,14 @@ //! # use esp_hal::peripherals::Peripherals; //! # use esp_hal::rmt::TxChannelConfig; //! # use esp_hal::rmt::Rmt; -//! # use esp_hal::gpio::Io; //! # use crate::esp_hal::rmt::TxChannelCreator; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); #![cfg_attr(esp32h2, doc = "let freq = 32.MHz();")] #![cfg_attr(not(esp32h2), doc = "let freq = 80.MHz();")] //! let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); //! let mut channel = rmt //! .channel0 //! .configure( -//! io.pins.gpio1, +//! peripherals.GPIO1, //! TxChannelConfig { //! clk_divider: 1, //! idle_output_level: false, @@ -80,17 +78,25 @@ //! //! > Note: on ESP32 and ESP32-S2 you cannot specify a base frequency other than 80 MHz -use core::marker::PhantomData; +use core::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; +use embassy_sync::waitqueue::AtomicWaker; +use enumset::{EnumSet, EnumSetType}; use fugit::HertzU32; use crate::{ gpio::interconnect::{PeripheralInput, PeripheralOutput}, - interrupt::InterruptHandler, + macros::handler, peripheral::Peripheral, - rmt::private::CreateInstance, + peripherals::Interrupt, soc::constants, system::PeripheralClockControl, + Async, + Blocking, InterruptConfigurable, }; @@ -106,61 +112,62 @@ pub enum Error { InvalidArgument, /// An error occurred during transmission TransmissionError, + /// No transmission end marker found + EndMarkerMissing, } -/// Convenience representation of a pulse code entry. -/// -/// Allows for the assignment of two levels and their lengths -#[derive(Clone, Copy, Debug, Default, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PulseCode { +/// Convenience trait to work with pulse codes. +pub trait PulseCode: crate::private::Sealed { + /// Create a new instance + fn new(level1: bool, length1: u16, level2: bool, length2: u16) -> Self; + + /// Create a new empty instance + fn empty() -> Self; + + /// Set all levels and lengths to 0 + fn reset(&mut self); + /// Logical output level in the first pulse code interval - pub level1: bool, + fn level1(&self) -> bool; + /// Length of the first pulse code interval (in clock cycles) - pub length1: u16, + fn length1(&self) -> u16; + /// Logical output level in the second pulse code interval - pub level2: bool, + fn level2(&self) -> bool; + /// Length of the second pulse code interval (in clock cycles) - pub length2: u16, + fn length2(&self) -> u16; } -impl From for PulseCode { - fn from(value: u32) -> Self { - Self { - level1: value & (1 << 15) != 0, - length1: (value & 0b111_1111_1111_1111) as u16, - level2: value & (1 << 31) != 0, - length2: ((value >> 16) & 0b111_1111_1111_1111) as u16, - } +impl PulseCode for u32 { + fn new(level1: bool, length1: u16, level2: bool, length2: u16) -> Self { + (((level1 as u32) << 15) | length1 as u32 & 0b111_1111_1111_1111) + | (((level2 as u32) << 15) | length2 as u32 & 0b111_1111_1111_1111) << 16 } -} -/// Convert a pulse code structure into a u32 value that can be written -/// into the data registers -impl From for u32 { - #[inline(always)] - fn from(p: PulseCode) -> u32 { - // The length1 value resides in bits [14:0] - let mut entry: u32 = p.length1 as u32; - - // If level1 is high, set bit 15, otherwise clear it - if p.level1 { - entry |= 1 << 15; - } else { - entry &= !(1 << 15); - } + fn empty() -> Self { + 0 + } - // If level2 is high, set bit 31, otherwise clear it - if p.level2 { - entry |= 1 << 31; - } else { - entry &= !(1 << 31); - } + fn reset(&mut self) { + *self = 0 + } + + fn level1(&self) -> bool { + self & (1 << 15) != 0 + } - // The length2 value resides in bits [30:16] - entry |= (p.length2 as u32) << 16; + fn length1(&self) -> u16 { + (self & 0b111_1111_1111_1111) as u16 + } + + fn level2(&self) -> bool { + self & (1 << 31) != 0 + } - entry + fn length2(&self) -> u16 { + ((self >> 16) & 0b111_1111_1111_1111) as u16 } } @@ -206,8 +213,6 @@ pub struct RxChannelConfig { pub use impl_for_chip::{ChannelCreator, Rmt}; -use self::asynch::{RxChannelAsync, TxChannelAsync}; - impl<'d, M> Rmt<'d, M> where M: crate::Mode, @@ -237,14 +242,6 @@ where Ok(me) } - pub(crate) fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::RMT, handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::RMT, handler.priority()) - .unwrap(); - } - } - #[cfg(not(any(esp32, esp32s2)))] fn configure_clock(&self, frequency: HertzU32) -> Result<(), Error> { let src_clock = crate::soc::constants::RMT_CLOCK_SRC_FREQ; @@ -265,7 +262,7 @@ where } } -impl<'d> Rmt<'d, crate::Blocking> { +impl<'d> Rmt<'d, Blocking> { /// Create a new RMT instance pub fn new( peripheral: impl Peripheral

+ 'd, @@ -273,34 +270,27 @@ impl<'d> Rmt<'d, crate::Blocking> { ) -> Result { Self::new_internal(peripheral, frequency) } -} -impl<'d> crate::private::Sealed for Rmt<'d, crate::Blocking> {} - -impl<'d> InterruptConfigurable for Rmt<'d, crate::Blocking> { - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - self.internal_set_interrupt_handler(handler); + /// Reconfigures the driver for asynchronous operation. + pub fn into_async(mut self) -> Rmt<'d, Async> { + self.set_interrupt_handler(async_interrupt_handler); + Rmt::create(self.peripheral) } } -impl<'d> Rmt<'d, crate::Async> { - /// Create a new RMT instance - pub fn new_async( - peripheral: impl Peripheral

+ 'd, - frequency: HertzU32, - ) -> Result { - let mut this = Self::new_internal(peripheral, frequency)?; - this.internal_set_interrupt_handler(asynch::async_interrupt_handler); - Ok(this) +impl crate::private::Sealed for Rmt<'_, Blocking> {} + +impl InterruptConfigurable for Rmt<'_, Blocking> { + fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::RMT); + } + unsafe { crate::interrupt::bind_interrupt(Interrupt::RMT, handler.handler()) }; + unwrap!(crate::interrupt::enable(Interrupt::RMT, handler.priority())); } } -fn configure_rx_channel< - 'd, - P: PeripheralInput, - T: private::RxChannelInternal, - M: crate::Mode, ->( +fn configure_rx_channel<'d, P: PeripheralInput, T: RxChannelInternal, M: crate::Mode>( pin: impl Peripheral

+ 'd, config: RxChannelConfig, ) -> Result { @@ -322,7 +312,7 @@ fn configure_rx_channel< crate::into_mapped_ref!(pin); pin.init_input(crate::gpio::Pull::None, crate::private::Internal); - pin.connect_input_to_peripheral(T::input_signal(), crate::private::Internal); + T::input_signal().connect_to(pin); T::set_divider(config.clk_divider); T::set_carrier( @@ -337,18 +327,13 @@ fn configure_rx_channel< Ok(T::new()) } -fn configure_tx_channel< - 'd, - P: PeripheralOutput, - T: private::TxChannelInternal, - M: crate::Mode, ->( +fn configure_tx_channel<'d, P: PeripheralOutput, T: TxChannelInternal, M: crate::Mode>( pin: impl Peripheral

+ 'd, config: TxChannelConfig, ) -> Result { crate::into_mapped_ref!(pin); pin.set_to_push_pull_output(crate::private::Internal); - pin.connect_peripheral_to_output(T::output_signal(), crate::private::Internal); + T::output_signal().connect_to(pin); T::set_divider(config.clk_divider); T::set_carrier( @@ -439,23 +424,23 @@ where } /// An in-progress transaction for a single shot TX transaction. -pub struct SingleShotTxTransaction<'a, C, T: Into + Copy> +pub struct SingleShotTxTransaction<'a, C> where C: TxChannel, { channel: C, index: usize, - data: &'a [T], + data: &'a [u32], } -impl<'a, C, T: Into + Copy> SingleShotTxTransaction<'a, C, T> +impl SingleShotTxTransaction<'_, C> where C: TxChannel, { /// Wait for the transaction to complete pub fn wait(mut self) -> Result { loop { - if >::is_error() { + if >::is_error() { return Err((Error::TransmissionError, self.channel)); } @@ -464,8 +449,8 @@ where } // wait for TX-THR - while !>::is_threshold_set() {} - >::reset_threshold_set(); + while !>::is_threshold_set() {} + >::reset_threshold_set(); // re-fill TX RAM let ram_index = (((self.index - constants::RMT_CHANNEL_RAM_SIZE) @@ -482,7 +467,7 @@ where .enumerate() { unsafe { - ptr.add(idx).write_volatile((*entry).into()); + ptr.add(idx).write_volatile(*entry); } } @@ -490,11 +475,11 @@ where } loop { - if >::is_error() { + if >::is_error() { return Err((Error::TransmissionError, self.channel)); } - if >::is_done() { + if >::is_done() { break; } } @@ -517,15 +502,15 @@ where { /// Stop transaction when the current iteration ends. pub fn stop_next(self) -> Result { - >::set_continuous(false); - >::update(); + >::set_continuous(false); + >::update(); loop { - if >::is_error() { + if >::is_error() { return Err((Error::TransmissionError, self.channel)); } - if >::is_done() { + if >::is_done() { break; } } @@ -535,8 +520,8 @@ where /// Stop transaction as soon as possible. pub fn stop(self) -> Result { - >::set_continuous(false); - >::update(); + >::set_continuous(false); + >::update(); let ptr = (constants::RMT_RAM_START + C::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) @@ -548,11 +533,11 @@ where } loop { - if >::is_error() { + if >::is_error() { return Err((Error::TransmissionError, self.channel)); } - if >::is_done() { + if >::is_done() { break; } } @@ -562,13 +547,14 @@ where /// Check if the `loopcount` interrupt bit is set pub fn is_loopcount_interrupt_set(&self) -> bool { - >::is_loopcount_interrupt_set() + >::is_loopcount_interrupt_set() } } macro_rules! impl_tx_channel_creator { ($channel:literal) => { - impl<'d, P> $crate::rmt::TxChannelCreator<'d, $crate::rmt::Channel<$crate::Blocking, $channel>, P> + impl<'d, P> + $crate::rmt::TxChannelCreator<'d, $crate::rmt::Channel<$crate::Blocking, $channel>, P> for ChannelCreator<$crate::Blocking, $channel> where P: $crate::gpio::interconnect::PeripheralOutput, @@ -577,20 +563,22 @@ macro_rules! impl_tx_channel_creator { impl $crate::rmt::TxChannel for $crate::rmt::Channel<$crate::Blocking, $channel> {} - impl<'d, P> $crate::rmt::TxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> + impl<'d, P> + $crate::rmt::TxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> for ChannelCreator<$crate::Async, $channel> where P: $crate::gpio::interconnect::PeripheralOutput, { } - impl $crate::rmt::asynch::TxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} + impl $crate::rmt::TxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} }; } macro_rules! impl_rx_channel_creator { ($channel:literal) => { - impl<'d, P> $crate::rmt::RxChannelCreator<'d, $crate::rmt::Channel<$crate::Blocking, $channel>, P> + impl<'d, P> + $crate::rmt::RxChannelCreator<'d, $crate::rmt::Channel<$crate::Blocking, $channel>, P> for ChannelCreator<$crate::Blocking, $channel> where P: $crate::gpio::interconnect::PeripheralInput, @@ -599,14 +587,15 @@ macro_rules! impl_rx_channel_creator { impl $crate::rmt::RxChannel for $crate::rmt::Channel<$crate::Blocking, $channel> {} - impl<'d, P> $crate::rmt::RxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> - for ChannelCreator<$crate::Async, $channel> + impl<'d, P> + $crate::rmt::RxChannelCreatorAsync<'d, $crate::rmt::Channel<$crate::Async, $channel>, P> + for ChannelCreator<$crate::Async, $channel> where P: $crate::gpio::interconnect::PeripheralInput, { } - impl $crate::rmt::asynch::RxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} + impl $crate::rmt::RxChannelAsync for $crate::rmt::Channel<$crate::Async, $channel> {} }; } @@ -614,7 +603,6 @@ macro_rules! impl_rx_channel_creator { mod impl_for_chip { use core::marker::PhantomData; - use super::private::CreateInstance; use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance @@ -622,7 +610,7 @@ mod impl_for_chip { where M: crate::Mode, { - _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + pub(super) peripheral: PeripheralRef<'d, crate::peripherals::RMT>, /// RMT Channel 0. pub channel0: ChannelCreator, /// RMT Channel 1. @@ -634,15 +622,17 @@ mod impl_for_chip { phantom: PhantomData, } - impl<'d, M> CreateInstance<'d> for Rmt<'d, M> + impl<'d, M> Rmt<'d, M> where M: crate::Mode, { - fn create(peripheral: impl Peripheral

+ 'd) -> Self { + pub(super) fn create( + peripheral: impl Peripheral

+ 'd, + ) -> Self { crate::into_ref!(peripheral); Self { - _peripheral: peripheral, + peripheral, channel0: ChannelCreator { phantom: PhantomData, }, @@ -674,18 +664,17 @@ mod impl_for_chip { impl_rx_channel_creator!(2); impl_rx_channel_creator!(3); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_1, 1); + super::chip_specific::impl_tx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_tx_channel!(RMT_SIG_1, 1); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_0, 2, 0); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_1, 3, 1); + super::chip_specific::impl_rx_channel!(RMT_SIG_0, 2, 0); + super::chip_specific::impl_rx_channel!(RMT_SIG_1, 3, 1); } #[cfg(esp32)] mod impl_for_chip { use core::marker::PhantomData; - use super::private::CreateInstance; use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance @@ -693,7 +682,7 @@ mod impl_for_chip { where M: crate::Mode, { - _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + pub(super) peripheral: PeripheralRef<'d, crate::peripherals::RMT>, /// RMT Channel 0. pub channel0: ChannelCreator, /// RMT Channel 1. @@ -713,15 +702,17 @@ mod impl_for_chip { phantom: PhantomData, } - impl<'d, M> CreateInstance<'d> for Rmt<'d, M> + impl<'d, M> Rmt<'d, M> where M: crate::Mode, { - fn create(peripheral: impl Peripheral

+ 'd) -> Self { + pub(super) fn create( + peripheral: impl Peripheral

+ 'd, + ) -> Self { crate::into_ref!(peripheral); Self { - _peripheral: peripheral, + peripheral, channel0: ChannelCreator { phantom: PhantomData, }, @@ -777,30 +768,29 @@ mod impl_for_chip { impl_rx_channel_creator!(6); impl_rx_channel_creator!(7); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_1, 1); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_2, 2); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_3, 3); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_4, 4); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_5, 5); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_6, 6); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_7, 7); - - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_1, 1); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_2, 2); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_3, 3); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_4, 4); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_5, 5); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_6, 6); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_7, 7); + super::chip_specific::impl_tx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_tx_channel!(RMT_SIG_1, 1); + super::chip_specific::impl_tx_channel!(RMT_SIG_2, 2); + super::chip_specific::impl_tx_channel!(RMT_SIG_3, 3); + super::chip_specific::impl_tx_channel!(RMT_SIG_4, 4); + super::chip_specific::impl_tx_channel!(RMT_SIG_5, 5); + super::chip_specific::impl_tx_channel!(RMT_SIG_6, 6); + super::chip_specific::impl_tx_channel!(RMT_SIG_7, 7); + + super::chip_specific::impl_rx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_rx_channel!(RMT_SIG_1, 1); + super::chip_specific::impl_rx_channel!(RMT_SIG_2, 2); + super::chip_specific::impl_rx_channel!(RMT_SIG_3, 3); + super::chip_specific::impl_rx_channel!(RMT_SIG_4, 4); + super::chip_specific::impl_rx_channel!(RMT_SIG_5, 5); + super::chip_specific::impl_rx_channel!(RMT_SIG_6, 6); + super::chip_specific::impl_rx_channel!(RMT_SIG_7, 7); } #[cfg(esp32s2)] mod impl_for_chip { use core::marker::PhantomData; - use super::private::CreateInstance; use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance @@ -808,7 +798,7 @@ mod impl_for_chip { where M: crate::Mode, { - _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + pub(super) peripheral: PeripheralRef<'d, crate::peripherals::RMT>, /// RMT Channel 0. pub channel0: ChannelCreator, /// RMT Channel 1. @@ -820,15 +810,17 @@ mod impl_for_chip { phantom: PhantomData, } - impl<'d, M> CreateInstance<'d> for Rmt<'d, M> + impl<'d, M> Rmt<'d, M> where M: crate::Mode, { - fn create(peripheral: impl Peripheral

+ 'd) -> Self { + pub(super) fn create( + peripheral: impl Peripheral

+ 'd, + ) -> Self { crate::into_ref!(peripheral); Self { - _peripheral: peripheral, + peripheral, channel0: ChannelCreator { phantom: PhantomData, }, @@ -864,22 +856,21 @@ mod impl_for_chip { impl_rx_channel_creator!(2); impl_rx_channel_creator!(3); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_1, 1); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_2, 2); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_3, 3); + super::chip_specific::impl_tx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_tx_channel!(RMT_SIG_1, 1); + super::chip_specific::impl_tx_channel!(RMT_SIG_2, 2); + super::chip_specific::impl_tx_channel!(RMT_SIG_3, 3); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_1, 1); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_2, 2); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_3, 3); + super::chip_specific::impl_rx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_rx_channel!(RMT_SIG_1, 1); + super::chip_specific::impl_rx_channel!(RMT_SIG_2, 2); + super::chip_specific::impl_rx_channel!(RMT_SIG_3, 3); } #[cfg(esp32s3)] mod impl_for_chip { use core::marker::PhantomData; - use super::private::CreateInstance; use crate::peripheral::{Peripheral, PeripheralRef}; /// RMT Instance @@ -887,7 +878,7 @@ mod impl_for_chip { where M: crate::Mode, { - _peripheral: PeripheralRef<'d, crate::peripherals::RMT>, + pub(super) peripheral: PeripheralRef<'d, crate::peripherals::RMT>, /// RMT Channel 0. pub channel0: ChannelCreator, /// RMT Channel 1. @@ -907,15 +898,17 @@ mod impl_for_chip { phantom: PhantomData, } - impl<'d, M> CreateInstance<'d> for Rmt<'d, M> + impl<'d, M> Rmt<'d, M> where M: crate::Mode, { - fn create(peripheral: impl Peripheral

+ 'd) -> Self { + pub(super) fn create( + peripheral: impl Peripheral

+ 'd, + ) -> Self { crate::into_ref!(peripheral); Self { - _peripheral: peripheral, + peripheral, channel0: ChannelCreator { phantom: PhantomData, }, @@ -963,15 +956,15 @@ mod impl_for_chip { impl_rx_channel_creator!(6); impl_rx_channel_creator!(7); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_0, 0); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_1, 1); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_2, 2); - super::chip_specific::impl_tx_channel!(Channel, RMT_SIG_3, 3); + super::chip_specific::impl_tx_channel!(RMT_SIG_0, 0); + super::chip_specific::impl_tx_channel!(RMT_SIG_1, 1); + super::chip_specific::impl_tx_channel!(RMT_SIG_2, 2); + super::chip_specific::impl_tx_channel!(RMT_SIG_3, 3); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_0, 4, 0); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_1, 5, 1); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_2, 6, 2); - super::chip_specific::impl_rx_channel!(Channel, RMT_SIG_3, 7, 3); + super::chip_specific::impl_rx_channel!(RMT_SIG_0, 4, 0); + super::chip_specific::impl_rx_channel!(RMT_SIG_1, 5, 1); + super::chip_specific::impl_rx_channel!(RMT_SIG_2, 6, 2); + super::chip_specific::impl_rx_channel!(RMT_SIG_3, 7, 3); } /// RMT Channel @@ -985,31 +978,28 @@ where } /// Channel in TX mode -pub trait TxChannel: private::TxChannelInternal { +pub trait TxChannel: TxChannelInternal { /// Start transmitting the given pulse code sequence. /// This returns a [`SingleShotTxTransaction`] which can be used to wait for /// the transaction to complete and get back the channel for further /// use. - fn transmit + Copy>(self, data: &[T]) -> SingleShotTxTransaction<'_, Self, T> + fn transmit(self, data: &[u32]) -> Result, Error> where Self: Sized, { - let index = Self::send_raw(data, false, 0); - SingleShotTxTransaction { + let index = Self::send_raw(data, false, 0)?; + Ok(SingleShotTxTransaction { channel: self, index, data, - } + }) } /// Start transmitting the given pulse code continuously. /// This returns a [`ContinuousTxTransaction`] which can be used to stop the /// ongoing transmission and get back the channel for further use. /// The length of sequence cannot exceed the size of the allocated RMT RAM. - fn transmit_continuously + Copy>( - self, - data: &[T], - ) -> Result, Error> + fn transmit_continuously(self, data: &[u32]) -> Result, Error> where Self: Sized, { @@ -1019,10 +1009,10 @@ pub trait TxChannel: private::TxChannelInternal { /// Like [`Self::transmit_continuously`] but also sets a loop count. /// [`ContinuousTxTransaction`] can be used to check if the loop count is /// reached. - fn transmit_continuously_with_loopcount + Copy>( + fn transmit_continuously_with_loopcount( self, loopcount: u16, - data: &[T], + data: &[u32], ) -> Result, Error> where Self: Sized, @@ -1031,46 +1021,46 @@ pub trait TxChannel: private::TxChannelInternal { return Err(Error::Overflow); } - let _index = Self::send_raw(data, true, loopcount); + let _index = Self::send_raw(data, true, loopcount)?; Ok(ContinuousTxTransaction { channel: self }) } } /// RX transaction instance -pub struct RxTransaction<'a, C, T: From + Copy> +pub struct RxTransaction<'a, C> where C: RxChannel, { channel: C, - data: &'a mut [T], + data: &'a mut [u32], } -impl<'a, C, T: From + Copy> RxTransaction<'a, C, T> +impl RxTransaction<'_, C> where C: RxChannel, { /// Wait for the transaction to complete pub fn wait(self) -> Result { loop { - if >::is_error() { + if >::is_error() { return Err((Error::TransmissionError, self.channel)); } - if >::is_done() { + if >::is_done() { break; } } - >::stop(); - >::clear_interrupts(); - >::update(); + >::stop(); + >::clear_interrupts(); + >::update(); let ptr = (constants::RMT_RAM_START + C::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) as *mut u32; let len = self.data.len(); for (idx, entry) in self.data.iter_mut().take(len).enumerate() { - *entry = unsafe { ptr.add(idx).read_volatile().into() }; + *entry = unsafe { ptr.add(idx).read_volatile() }; } Ok(self.channel) @@ -1078,15 +1068,12 @@ where } /// Channel is RX mode -pub trait RxChannel: private::RxChannelInternal { +pub trait RxChannel: RxChannelInternal { /// Start receiving pulse codes into the given buffer. /// This returns a [RxTransaction] which can be used to wait for receive to /// complete and get back the channel for further use. /// The length of the received data cannot exceed the allocated RMT RAM. - fn receive + Copy>( - self, - data: &mut [T], - ) -> Result, Error> + fn receive(self, data: &mut [u32]) -> Result, Error> where Self: Sized, { @@ -1103,497 +1090,417 @@ pub trait RxChannel: private::RxChannelInternal { } } -/// Async functionality -pub mod asynch { - use core::{ - pin::Pin, - task::{Context, Poll}, - }; +#[cfg(any(esp32, esp32s3))] +const NUM_CHANNELS: usize = 8; +#[cfg(not(any(esp32, esp32s3)))] +const NUM_CHANNELS: usize = 4; - use embassy_sync::waitqueue::AtomicWaker; - use procmacros::handler; +static WAKER: [AtomicWaker; NUM_CHANNELS] = [const { AtomicWaker::new() }; NUM_CHANNELS]; - use super::{private::Event, *}; +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub(crate) struct RmtTxFuture +where + T: TxChannelAsync, +{ + _phantom: PhantomData, +} - #[cfg(any(esp32, esp32s3))] - const NUM_CHANNELS: usize = 8; - #[cfg(not(any(esp32, esp32s3)))] - const NUM_CHANNELS: usize = 4; +impl RmtTxFuture +where + T: TxChannelAsync, +{ + pub fn new(_instance: &T) -> Self { + Self { + _phantom: PhantomData, + } + } +} - static WAKER: [AtomicWaker; NUM_CHANNELS] = [const { AtomicWaker::new() }; NUM_CHANNELS]; +impl core::future::Future for RmtTxFuture +where + T: TxChannelAsync, +{ + type Output = (); - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct RmtTxFuture - where - T: TxChannelAsync, - { - _phantom: PhantomData, - } + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + WAKER[T::CHANNEL as usize].register(ctx.waker()); - impl RmtTxFuture - where - T: TxChannelAsync, - { - pub fn new(_instance: &T) -> Self { - Self { - _phantom: PhantomData, - } + if T::is_error() || T::is_done() { + Poll::Ready(()) + } else { + Poll::Pending } } +} - impl core::future::Future for RmtTxFuture +/// TX channel in async mode +pub trait TxChannelAsync: TxChannelInternal { + /// Start transmitting the given pulse code sequence. + /// The length of sequence cannot exceed the size of the allocated RMT + /// RAM. + async fn transmit<'a>(&mut self, data: &'a [u32]) -> Result<(), Error> where - T: TxChannelAsync, + Self: Sized, { - type Output = (); + if data.len() > constants::RMT_CHANNEL_RAM_SIZE { + return Err(Error::InvalidArgument); + } - fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - WAKER[T::CHANNEL as usize].register(ctx.waker()); + Self::clear_interrupts(); + Self::listen_interrupt(Event::End); + Self::listen_interrupt(Event::Error); + Self::send_raw(data, false, 0)?; - if T::is_error() || T::is_done() { - Poll::Ready(()) - } else { - Poll::Pending - } + RmtTxFuture::new(self).await; + + if Self::is_error() { + Err(Error::TransmissionError) + } else { + Ok(()) } } +} - /// TX channel in async mode - pub trait TxChannelAsync: private::TxChannelInternal { - /// Start transmitting the given pulse code sequence. - /// The length of sequence cannot exceed the size of the allocated RMT - /// RAM. - async fn transmit<'a, T: Into + Copy>(&mut self, data: &'a [T]) -> Result<(), Error> - where - Self: Sized, - { - if data.len() > constants::RMT_CHANNEL_RAM_SIZE { - return Err(Error::InvalidArgument); - } - - Self::clear_interrupts(); - Self::listen_interrupt(super::private::Event::End); - Self::listen_interrupt(super::private::Event::Error); - Self::send_raw(data, false, 0); - - RmtTxFuture::new(self).await; +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub(crate) struct RmtRxFuture +where + T: RxChannelAsync, +{ + _phantom: PhantomData, +} - if Self::is_error() { - Err(Error::TransmissionError) - } else { - Ok(()) - } +impl RmtRxFuture +where + T: RxChannelAsync, +{ + pub fn new(_instance: &T) -> Self { + Self { + _phantom: PhantomData, } } +} - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct RmtRxFuture - where - T: RxChannelAsync, - { - _phantom: PhantomData, - } +impl core::future::Future for RmtRxFuture +where + T: RxChannelAsync, +{ + type Output = (); - impl RmtRxFuture - where - T: RxChannelAsync, - { - pub fn new(_instance: &T) -> Self { - Self { - _phantom: PhantomData, - } + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + WAKER[T::CHANNEL as usize].register(ctx.waker()); + if T::is_error() || T::is_done() { + Poll::Ready(()) + } else { + Poll::Pending } } +} - impl core::future::Future for RmtRxFuture +/// RX channel in async mode +pub trait RxChannelAsync: RxChannelInternal { + /// Start receiving a pulse code sequence. + /// The length of sequence cannot exceed the size of the allocated RMT + /// RAM. + async fn receive<'a, T: From + Copy>(&mut self, data: &'a mut [T]) -> Result<(), Error> where - T: RxChannelAsync, + Self: Sized, { - type Output = (); - - fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { - WAKER[T::CHANNEL as usize].register(ctx.waker()); - if T::is_error() || T::is_done() { - Poll::Ready(()) - } else { - Poll::Pending - } + if data.len() > constants::RMT_CHANNEL_RAM_SIZE { + return Err(Error::InvalidArgument); } - } - - /// RX channel in async mode - pub trait RxChannelAsync: private::RxChannelInternal { - /// Start receiving a pulse code sequence. - /// The length of sequence cannot exceed the size of the allocated RMT - /// RAM. - async fn receive<'a, T: From + Copy>(&mut self, data: &'a mut [T]) -> Result<(), Error> - where - Self: Sized, - { - if data.len() > constants::RMT_CHANNEL_RAM_SIZE { - return Err(Error::InvalidArgument); - } - - Self::clear_interrupts(); - Self::listen_interrupt(super::private::Event::End); - Self::listen_interrupt(super::private::Event::Error); - Self::start_receive_raw(); - - RmtRxFuture::new(self).await; - if Self::is_error() { - Err(Error::TransmissionError) - } else { - Self::stop(); - Self::clear_interrupts(); - Self::update(); - - let ptr = (constants::RMT_RAM_START - + Self::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) - as *mut u32; - let len = data.len(); - for (idx, entry) in data.iter_mut().take(len).enumerate() { - *entry = unsafe { ptr.add(idx).read_volatile().into() }; - } - - Ok(()) - } - } - } + Self::clear_interrupts(); + Self::listen_interrupt(Event::End); + Self::listen_interrupt(Event::Error); + Self::start_receive_raw(); - #[cfg(not(any(esp32, esp32s2)))] - #[handler] - pub(super) fn async_interrupt_handler() { - if let Some(channel) = super::chip_specific::pending_interrupt_for_channel() { - use crate::rmt::private::{RxChannelInternal, TxChannelInternal}; - - match channel { - 0 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - 1 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - 2 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - 3 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } + RmtRxFuture::new(self).await; - #[cfg(any(esp32, esp32s3))] - 4 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - #[cfg(any(esp32, esp32s3))] - 5 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - #[cfg(any(esp32, esp32s3))] - 6 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } - #[cfg(any(esp32, esp32s3))] - 7 => { - super::Channel::::unlisten_interrupt(Event::End); - super::Channel::::unlisten_interrupt(Event::Error); - } + if Self::is_error() { + Err(Error::TransmissionError) + } else { + Self::stop(); + Self::clear_interrupts(); + Self::update(); - _ => unreachable!(), + let ptr = (constants::RMT_RAM_START + + Self::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) + as *mut u32; + let len = data.len(); + for (idx, entry) in data.iter_mut().take(len).enumerate() { + *entry = unsafe { ptr.add(idx).read_volatile().into() }; } - WAKER[channel].wake(); + Ok(()) } } +} - #[cfg(any(esp32, esp32s2))] - #[handler] - pub(super) fn async_interrupt_handler() { - if let Some(channel) = super::chip_specific::pending_interrupt_for_channel() { - match channel { - 0 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - 1 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - 2 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - 3 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - #[cfg(esp32)] - 4 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - #[cfg(any(esp32, esp32s3))] - 5 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - #[cfg(any(esp32, esp32s3))] - 6 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - #[cfg(any(esp32, esp32s3))] - 7 => { - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::TxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::End, - ); - as super::private::RxChannelInternal>::unlisten_interrupt( - Event::Error, - ); - } - - _ => unreachable!(), - } - - WAKER[channel].wake(); - } +#[cfg(not(any(esp32, esp32s2)))] +#[handler] +fn async_interrupt_handler() { + let Some(channel) = chip_specific::pending_interrupt_for_channel() else { + return; + }; + match channel { + 0 => Channel::::unlisten_interrupt(Event::End | Event::Error), + 1 => Channel::::unlisten_interrupt(Event::End | Event::Error), + 2 => Channel::::unlisten_interrupt(Event::End | Event::Error), + 3 => Channel::::unlisten_interrupt(Event::End | Event::Error), + + #[cfg(any(esp32, esp32s3))] + 4 => Channel::::unlisten_interrupt(Event::End | Event::Error), + #[cfg(any(esp32, esp32s3))] + 5 => Channel::::unlisten_interrupt(Event::End | Event::Error), + #[cfg(any(esp32, esp32s3))] + 6 => Channel::::unlisten_interrupt(Event::End | Event::Error), + #[cfg(any(esp32, esp32s3))] + 7 => Channel::::unlisten_interrupt(Event::End | Event::Error), + + _ => unreachable!(), } + + WAKER[channel].wake(); } -mod private { - use crate::{peripheral::Peripheral, soc::constants}; +#[cfg(any(esp32, esp32s2))] +#[handler] +fn async_interrupt_handler() { + let Some(channel) = chip_specific::pending_interrupt_for_channel() else { + return; + }; + match channel { + 0 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + 1 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + 2 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + 3 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + #[cfg(esp32)] + 4 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 5 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 6 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 7 => { + as TxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + as RxChannelInternal>::unlisten_interrupt( + Event::End | Event::Error, + ); + } - pub enum Event { - Error, - Threshold, - End, + _ => unreachable!(), } - pub trait CreateInstance<'d> { - fn create(peripheral: impl Peripheral

+ 'd) -> Self; - } + WAKER[channel].wake(); +} - pub trait TxChannelInternal - where - M: crate::Mode, - { - const CHANNEL: u8; +#[derive(Debug, EnumSetType)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[doc(hidden)] +pub enum Event { + Error, + Threshold, + End, +} - fn new() -> Self; +#[doc(hidden)] +pub trait TxChannelInternal +where + M: crate::Mode, +{ + const CHANNEL: u8; - fn output_signal() -> crate::gpio::OutputSignal; + fn new() -> Self; - fn set_divider(divider: u8); + fn output_signal() -> crate::gpio::OutputSignal; - fn update(); + fn set_divider(divider: u8); - fn set_generate_repeat_interrupt(repeats: u16); + fn update(); - fn clear_interrupts(); + fn set_generate_repeat_interrupt(repeats: u16); - fn set_continuous(continuous: bool); + fn clear_interrupts(); - fn set_wrap_mode(wrap: bool); + fn set_continuous(continuous: bool); - fn set_carrier(carrier: bool, high: u16, low: u16, level: bool); + fn set_wrap_mode(wrap: bool); - fn set_idle_output(enable: bool, level: bool); + fn set_carrier(carrier: bool, high: u16, low: u16, level: bool); - fn set_memsize(memsize: u8); + fn set_idle_output(enable: bool, level: bool); - fn start_tx(); + fn set_memsize(memsize: u8); - fn is_done() -> bool; + fn start_tx(); - fn is_error() -> bool; + fn is_done() -> bool; - fn is_threshold_set() -> bool; + fn is_error() -> bool; - fn reset_threshold_set(); + fn is_threshold_set() -> bool; - fn set_threshold(threshold: u8); + fn reset_threshold_set(); - fn is_loopcount_interrupt_set() -> bool; + fn set_threshold(threshold: u8); - fn send_raw + Copy>(data: &[T], continuous: bool, repeat: u16) -> usize { - Self::clear_interrupts(); + fn is_loopcount_interrupt_set() -> bool; - let ptr = (constants::RMT_RAM_START - + Self::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) - as *mut u32; - for (idx, entry) in data - .iter() - .take(constants::RMT_CHANNEL_RAM_SIZE) - .enumerate() - { - unsafe { - ptr.add(idx).write_volatile((*entry).into()); - } - } + fn send_raw(data: &[u32], continuous: bool, repeat: u16) -> Result { + Self::clear_interrupts(); - Self::set_threshold((constants::RMT_CHANNEL_RAM_SIZE / 2) as u8); - Self::set_continuous(continuous); - Self::set_generate_repeat_interrupt(repeat); - Self::set_wrap_mode(true); - Self::set_memsize(1); - Self::update(); - Self::start_tx(); - Self::update(); + if let Some(last) = data.last() { + if !continuous && last.length2() != 0 && last.length1() != 0 { + return Err(Error::EndMarkerMissing); + } + } else { + return Err(Error::InvalidArgument); + } - if data.len() >= constants::RMT_CHANNEL_RAM_SIZE { - constants::RMT_CHANNEL_RAM_SIZE - } else { - data.len() + let ptr = (constants::RMT_RAM_START + + Self::CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) + as *mut u32; + for (idx, entry) in data + .iter() + .take(constants::RMT_CHANNEL_RAM_SIZE) + .enumerate() + { + unsafe { + ptr.add(idx).write_volatile(*entry); } } - fn stop(); + Self::set_threshold((constants::RMT_CHANNEL_RAM_SIZE / 2) as u8); + Self::set_continuous(continuous); + Self::set_generate_repeat_interrupt(repeat); + Self::set_wrap_mode(true); + Self::set_memsize(1); + Self::update(); + Self::start_tx(); + Self::update(); + + if data.len() >= constants::RMT_CHANNEL_RAM_SIZE { + Ok(constants::RMT_CHANNEL_RAM_SIZE) + } else { + Ok(data.len()) + } + } + + fn stop(); - fn enable_listen_interrupt(event: Event, enable: bool); + fn enable_listen_interrupt(event: EnumSet, enable: bool); - fn listen_interrupt(event: Event) { - Self::enable_listen_interrupt(event, true); - } + fn listen_interrupt(event: impl Into>) { + Self::enable_listen_interrupt(event.into(), true); + } - fn unlisten_interrupt(event: Event) { - Self::enable_listen_interrupt(event, false); - } + fn unlisten_interrupt(event: impl Into>) { + Self::enable_listen_interrupt(event.into(), false); } +} - pub trait RxChannelInternal - where - M: crate::Mode, - { - const CHANNEL: u8; +#[doc(hidden)] +pub trait RxChannelInternal +where + M: crate::Mode, +{ + const CHANNEL: u8; - fn new() -> Self; + fn new() -> Self; - fn input_signal() -> crate::gpio::InputSignal; + fn input_signal() -> crate::gpio::InputSignal; - fn set_divider(divider: u8); + fn set_divider(divider: u8); - fn update(); + fn update(); - fn clear_interrupts(); + fn clear_interrupts(); - fn set_wrap_mode(wrap: bool); + fn set_wrap_mode(wrap: bool); - fn set_carrier(carrier: bool, high: u16, low: u16, level: bool); + fn set_carrier(carrier: bool, high: u16, low: u16, level: bool); - fn set_memsize(memsize: u8); + fn set_memsize(memsize: u8); - fn start_rx(); + fn start_rx(); - fn is_done() -> bool; + fn is_done() -> bool; - fn is_error() -> bool; + fn is_error() -> bool; - fn start_receive_raw() { - Self::clear_interrupts(); - Self::set_wrap_mode(false); - Self::set_memsize(1); - Self::start_rx(); - Self::update(); - } + fn start_receive_raw() { + Self::clear_interrupts(); + Self::set_wrap_mode(false); + Self::set_memsize(1); + Self::start_rx(); + Self::update(); + } - fn stop(); + fn stop(); - fn set_filter_threshold(value: u8); + fn set_filter_threshold(value: u8); - fn set_idle_threshold(value: u16); + fn set_idle_threshold(value: u16); - fn enable_listen_interrupt(event: Event, enable: bool); + fn enable_listen_interrupt(event: EnumSet, enable: bool); - fn listen_interrupt(event: Event) { - Self::enable_listen_interrupt(event, true); - } + fn listen_interrupt(event: impl Into>) { + Self::enable_listen_interrupt(event.into(), true); + } - fn unlisten_interrupt(event: Event) { - Self::enable_listen_interrupt(event, false); - } + fn unlisten_interrupt(event: impl Into>) { + Self::enable_listen_interrupt(event.into(), false); } } @@ -1681,9 +1588,9 @@ mod chip_specific { } macro_rules! impl_tx_channel { - ($channel:ident, $signal:ident, $ch_num:literal) => { + ($signal:ident, $ch_num:literal) => { paste::paste! { - impl $crate::rmt::private::TxChannelInternal for $crate::rmt::$channel where M: $crate::Mode { + impl $crate::rmt::TxChannelInternal for $crate::rmt::Channel where M: $crate::Mode { const CHANNEL: u8 = $ch_num; fn new() -> Self { @@ -1823,19 +1730,15 @@ mod chip_specific { Self::update(); } - fn enable_listen_interrupt(event: $crate::rmt::private::Event, enable: bool) { + fn enable_listen_interrupt(events: enumset::EnumSet<$crate::rmt::Event>, enable: bool) { let rmt = unsafe { &*crate::peripherals::RMT::PTR }; rmt.int_ena().modify(|_, w| { - match event { - $crate::rmt::private::Event::Error => { - w.[< ch $ch_num _tx_err >]().bit(enable); - } - $crate::rmt::private::Event::End => { - w.[< ch $ch_num _tx_end >]().bit(enable); - } - $crate::rmt::private::Event::Threshold => { - w.[< ch $ch_num _tx_thr_event >]().bit(enable); - } + for event in events { + match event { + $crate::rmt::Event::Error => w.[< ch $ch_num _tx_err >]().bit(enable), + $crate::rmt::Event::End => w.[< ch $ch_num _tx_end >]().bit(enable), + $crate::rmt::Event::Threshold => w.[< ch $ch_num _tx_thr_event >]().bit(enable), + }; } w }); @@ -1846,9 +1749,9 @@ mod chip_specific { } macro_rules! impl_rx_channel { - ($channel:ident, $signal:ident, $ch_num:literal, $ch_index:literal) => { + ($signal:ident, $ch_num:literal, $ch_index:literal) => { paste::paste! { - impl $crate::rmt::private::RxChannelInternal for $crate::rmt::$channel where M: $crate::Mode { + impl $crate::rmt::RxChannelInternal for $crate::rmt::Channel where M: $crate::Mode { const CHANNEL: u8 = $ch_num; fn new() -> Self { @@ -1943,19 +1846,15 @@ mod chip_specific { rmt.[< ch $ch_num _rx_conf0 >]().modify(|_, w| unsafe { w.idle_thres().bits(value) }); } - fn enable_listen_interrupt(event: $crate::rmt::private::Event, enable: bool) { + fn enable_listen_interrupt(events: enumset::EnumSet<$crate::rmt::Event>, enable: bool) { let rmt = unsafe { &*crate::peripherals::RMT::PTR }; rmt.int_ena().modify(|_, w| { - match event { - $crate::rmt::private::Event::Error => { - w.[< ch $ch_num _rx_err >]().bit(enable); - } - $crate::rmt::private::Event::End => { - w.[< ch $ch_num _rx_end >]().bit(enable); - } - $crate::rmt::private::Event::Threshold => { - w.[< ch $ch_num _rx_thr_event >]().bit(enable); - } + for event in events { + match event { + $crate::rmt::Event::Error => w.[< ch $ch_num _rx_err >]().bit(enable), + $crate::rmt::Event::End => w.[< ch $ch_num _rx_end >]().bit(enable), + $crate::rmt::Event::Threshold => w.[< ch $ch_num _rx_thr_event >]().bit(enable), + }; } w }); @@ -2039,9 +1938,9 @@ mod chip_specific { } macro_rules! impl_tx_channel { - ($channel:ident, $signal:ident, $ch_num:literal) => { + ($signal:ident, $ch_num:literal) => { paste::paste! { - impl super::private::TxChannelInternal for super::$channel where M: $crate::Mode { + impl super::TxChannelInternal for $crate::rmt::Channel where M: $crate::Mode { const CHANNEL: u8 = $ch_num; fn new() -> Self { @@ -2171,40 +2070,28 @@ mod chip_specific { } } - fn enable_listen_interrupt(event: $crate::rmt::private::Event, enable: bool) { + fn enable_listen_interrupt(events: enumset::EnumSet<$crate::rmt::Event>, enable: bool) { let rmt = unsafe { &*crate::peripherals::RMT::PTR }; rmt.int_ena().modify(|_,w| { - match event { - $crate::rmt::private::Event::Error => { - w.[< ch $ch_num _err >]().bit(enable) - } - $crate::rmt::private::Event::End => { - w.[< ch $ch_num _tx_end >]().bit(enable) - } - $crate::rmt::private::Event::Threshold => { - w.[< ch $ch_num _tx_thr_event >]().bit(enable) - } - }; + for event in events { + match event { + $crate::rmt::Event::Error => w.[< ch $ch_num _err >]().bit(enable), + $crate::rmt::Event::End => w.[< ch $ch_num _tx_end >]().bit(enable), + $crate::rmt::Event::Threshold => w.[< ch $ch_num _tx_thr_event >]().bit(enable), + }; + } w }); } - - fn listen_interrupt(event: $crate::rmt::private::Event) { - Self::enable_listen_interrupt(event, true) - } - - fn unlisten_interrupt(event: $crate::rmt::private::Event) { - Self::enable_listen_interrupt(event, false) - } } } } } macro_rules! impl_rx_channel { - ($channel:ident, $signal:ident, $ch_num:literal) => { + ($signal:ident, $ch_num:literal) => { paste::paste! { - impl super::private::RxChannelInternal for super::$channel where M: $crate::Mode { + impl super::RxChannelInternal for $crate::rmt::Channel where M: $crate::Mode { const CHANNEL: u8 = $ch_num; fn new() -> Self { @@ -2304,31 +2191,19 @@ mod chip_specific { rmt.[< ch $ch_num conf0 >]().modify(|_, w| unsafe { w.idle_thres().bits(value) }); } - fn enable_listen_interrupt(event: $crate::rmt::private::Event, enable: bool) { + fn enable_listen_interrupt(events: enumset::EnumSet<$crate::rmt::Event>, enable: bool) { let rmt = unsafe { &*crate::peripherals::RMT::PTR }; rmt.int_ena().modify(|_, w| { - match event { - $crate::rmt::private::Event::Error => { - w.[< ch $ch_num _err >]().bit(enable) - } - $crate::rmt::private::Event::End => { - w.[< ch $ch_num _rx_end >]().bit(enable) - } - $crate::rmt::private::Event::Threshold => { - w.[< ch $ch_num _tx_thr_event >]().bit(enable) - } - }; + for event in events { + match event { + $crate::rmt::Event::Error => w.[< ch $ch_num _err >]().bit(enable), + $crate::rmt::Event::End => w.[< ch $ch_num _rx_end >]().bit(enable), + $crate::rmt::Event::Threshold => w.[< ch $ch_num _tx_thr_event >]().bit(enable), + }; + } w }); } - - fn listen_interrupt(event: $crate::rmt::private::Event) { - Self::enable_listen_interrupt(event, true) - } - - fn unlisten_interrupt(event: $crate::rmt::private::Event) { - Self::enable_listen_interrupt(event, false) - } } } } diff --git a/esp-hal/src/rng.rs b/esp-hal/src/rng.rs index 8e469bd43d5..6266b9fddc9 100644 --- a/esp-hal/src/rng.rs +++ b/esp-hal/src/rng.rs @@ -130,10 +130,7 @@ impl rand_core::RngCore for Rng { /// the randomness from the hardware RNG and an ADC. This struct provides /// methods to generate random numbers and fill buffers with random bytes. /// Due to pulling the entropy source from the ADC, it uses the associated -/// regiters, so to use TRNG we need to "occupy" the ADC peripheral. -/// -/// For now, even after calling `core::mem::drop()` on `TRNG` ADC1 will not be -/// usable (details in esp-hal/#1750) +/// registers, so to use TRNG we need to "occupy" the ADC peripheral. /// /// ```rust, no_run #[doc = crate::before_snippet!()] @@ -141,9 +138,7 @@ impl rand_core::RngCore for Rng { /// # use esp_hal::peripherals::Peripherals; /// # use esp_hal::peripherals::ADC1; /// # use esp_hal::analog::adc::{AdcConfig, Attenuation, Adc}; -/// # use esp_hal::gpio::Io; /// -/// let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); /// let mut buf = [0u8; 16]; /// /// // ADC is not available from now @@ -152,19 +147,20 @@ impl rand_core::RngCore for Rng { /// let mut true_rand = trng.random(); /// let mut rng = trng.downgrade(); /// // ADC is available now -#[cfg_attr(esp32, doc = "let analog_pin = io.pins.gpio32;")] -#[cfg_attr(not(esp32), doc = "let analog_pin = io.pins.gpio3;")] +#[cfg_attr(esp32, doc = "let analog_pin = peripherals.GPIO32;")] +#[cfg_attr(not(esp32), doc = "let analog_pin = peripherals.GPIO3;")] /// let mut adc1_config = AdcConfig::new(); -/// let mut adc1_pin = adc1_config.enable_pin(analog_pin, -/// Attenuation::Attenuation11dB); let mut adc1 = -/// Adc::::new(peripherals.ADC1, adc1_config); let pin_value: u16 = -/// nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); +/// let mut adc1_pin = adc1_config.enable_pin( +/// analog_pin, +/// Attenuation::Attenuation11dB +/// ); +/// let mut adc1 = Adc::::new(peripherals.ADC1, adc1_config); +/// let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); /// rng.read(&mut buf); /// true_rand = rng.random(); /// let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap(); /// # } /// ``` - pub struct Trng<'d> { /// The hardware random number generator instance. pub rng: Rng, @@ -211,7 +207,7 @@ impl<'d> Trng<'d> { } } -impl<'d> Drop for Trng<'d> { +impl Drop for Trng<'_> { fn drop(&mut self) { crate::soc::trng::revert_trng(); } diff --git a/esp-hal/src/rom/md5.rs b/esp-hal/src/rom/md5.rs index 985334104d0..1eb3e9a7480 100644 --- a/esp-hal/src/rom/md5.rs +++ b/esp-hal/src/rom/md5.rs @@ -32,11 +32,9 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::rom::md5; //! # use esp_hal::uart::Uart; -//! # use esp_hal::gpio::Io; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut uart0 = Uart::new(peripherals.UART0, io.pins.gpio1, io.pins.gpio2).unwrap(); +//! # let mut uart0 = Uart::new(peripherals.UART0, peripherals.GPIO1, peripherals.GPIO2).unwrap(); //! # let data = "Dummy"; //! let d: md5::Digest = md5::compute(&data); //! writeln!(uart0, "{}", d); @@ -48,11 +46,9 @@ #![doc = crate::before_snippet!()] //! # use esp_hal::rom::md5; //! # use esp_hal::uart::Uart; -//! # use esp_hal::gpio::Io; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut uart0 = Uart::new(peripherals.UART0, io.pins.gpio1, io.pins.gpio2).unwrap(); +//! # let mut uart0 = Uart::new(peripherals.UART0, peripherals.GPIO1, peripherals.GPIO2).unwrap(); //! # let data0 = "Dummy"; //! # let data1 = "Dummy"; //! # diff --git a/esp-hal/src/rsa/esp32cX.rs b/esp-hal/src/rsa/esp32cX.rs index 084ba5473f9..e74eac7fd7f 100644 --- a/esp-hal/src/rsa/esp32cX.rs +++ b/esp-hal/src/rsa/esp32cX.rs @@ -10,7 +10,7 @@ use crate::rsa::{ RsaMultiplication, }; -impl<'d, DM: crate::Mode> Rsa<'d, DM> { +impl Rsa<'_, DM> { /// After the RSA Accelerator is released from reset, the memory blocks /// needs to be initialized, only after that peripheral should be used. /// This function would return without an error if the memory is initialized @@ -26,7 +26,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { /// When enabled rsa peripheral would generate an interrupt when a operation /// is finished. pub fn enable_disable_interrupt(&mut self, enable: bool) { - self.rsa.int_ena().write(|w| w.int_ena().bit(enable)) + self.rsa.int_ena().write(|w| w.int_ena().bit(enable)); } fn write_mode(&mut self, mode: u32) { @@ -52,7 +52,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { .rsa .search_enable() .write(|w| w.search_enable().clear_bit()), - } + }; } /// Checks if the search functionality is enabled in the RSA hardware. @@ -87,7 +87,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { .rsa .constant_time() .write(|w| w.constant_time().set_bit()), - } + }; } /// Starts the modular exponentiation operation. @@ -229,7 +229,7 @@ pub mod operand_sizes { ); } -impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiation<'a, 'd, T, DM> +impl<'d, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularExponentiation<'_, 'd, T, DM> where T: RsaMode, { @@ -249,7 +249,7 @@ where } } -impl<'a, 'd, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplication<'a, 'd, T, DM> +impl<'d, T: RsaMode, DM: crate::Mode, const N: usize> RsaModularMultiplication<'_, 'd, T, DM> where T: RsaMode, { @@ -262,7 +262,7 @@ where } } -impl<'a, 'd, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplication<'a, 'd, T, DM> +impl<'d, T: RsaMode + Multi, DM: crate::Mode, const N: usize> RsaMultiplication<'_, 'd, T, DM> where T: RsaMode, { diff --git a/esp-hal/src/rsa/esp32sX.rs b/esp-hal/src/rsa/esp32sX.rs index bbdad42cac4..f4404bfa471 100644 --- a/esp-hal/src/rsa/esp32sX.rs +++ b/esp-hal/src/rsa/esp32sX.rs @@ -27,7 +27,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { /// When enabled rsa peripheral would generate an interrupt when a operation /// is finished. pub fn enable_disable_interrupt(&mut self, enable: bool) { - self.rsa.int_ena().write(|w| w.int_ena().bit(enable)) + self.rsa.int_ena().write(|w| w.int_ena().bit(enable)); } fn write_mode(&mut self, mode: u32) { @@ -53,7 +53,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { .rsa .search_enable() .write(|w| w.search_enable().clear_bit()), - } + }; } /// Checks if the search functionality is enabled in the RSA hardware. @@ -88,7 +88,7 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { .rsa .constant_time() .write(|w| w.constant_time().set_bit()), - } + }; } /// Starts the modular exponentiation operation. diff --git a/esp-hal/src/rsa/mod.rs b/esp-hal/src/rsa/mod.rs index d71668c0df5..cde24f4e88c 100644 --- a/esp-hal/src/rsa/mod.rs +++ b/esp-hal/src/rsa/mod.rs @@ -26,8 +26,11 @@ use core::{marker::PhantomData, ptr::copy_nonoverlapping}; use crate::{ interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, - peripherals::RSA, + peripherals::{Interrupt, RSA}, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, + Async, + Blocking, + Cpu, InterruptConfigurable, }; @@ -47,29 +50,44 @@ pub struct Rsa<'d, DM: crate::Mode> { phantom: PhantomData, } -impl<'d> Rsa<'d, crate::Blocking> { +impl<'d> Rsa<'d, Blocking> { /// Create a new instance in [crate::Blocking] mode. /// /// Optionally an interrupt handler can be bound. pub fn new(rsa: impl Peripheral

+ 'd) -> Self { Self::new_internal(rsa) } + + /// Reconfigures the RSA driver to operate in asynchronous mode. + pub fn into_async(mut self) -> Rsa<'d, Async> { + self.set_interrupt_handler(asynch::rsa_interrupt_handler); + Rsa { + rsa: self.rsa, + phantom: PhantomData, + } + } } -impl<'d> crate::private::Sealed for Rsa<'d, crate::Blocking> {} +impl crate::private::Sealed for Rsa<'_, Blocking> {} -impl<'d> InterruptConfigurable for Rsa<'d, crate::Blocking> { +impl InterruptConfigurable for Rsa<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - self.internal_set_interrupt_handler(handler); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::RSA); + } + unsafe { crate::interrupt::bind_interrupt(Interrupt::RSA, handler.handler()) }; + unwrap!(crate::interrupt::enable(Interrupt::RSA, handler.priority())); } } -impl<'d> Rsa<'d, crate::Async> { +impl<'d> Rsa<'d, Async> { /// Create a new instance in [crate::Blocking] mode. - pub fn new_async(rsa: impl Peripheral

+ 'd) -> Self { - let mut this = Self::new_internal(rsa); - this.internal_set_interrupt_handler(asynch::rsa_interrupt_handler); - this + pub fn into_blocking(self) -> Rsa<'d, Blocking> { + crate::interrupt::disable(Cpu::current(), Interrupt::RSA); + Rsa { + rsa: self.rsa, + phantom: PhantomData, + } } } @@ -129,15 +147,6 @@ impl<'d, DM: crate::Mode> Rsa<'d, DM> { ); } } - - fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::RSA, handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::RSA, handler.priority()) - .unwrap(); - } - } - fn wait_for_idle(&mut self) { while !self.is_idle() {} self.clear_interrupt(); @@ -435,7 +444,7 @@ pub(crate) mod asynch { } } - impl<'a, 'd, T: RsaMode, const N: usize> RsaModularExponentiation<'a, 'd, T, Async> + impl RsaModularExponentiation<'_, '_, T, Async> where T: RsaMode, { @@ -454,7 +463,7 @@ pub(crate) mod asynch { } } - impl<'a, 'd, T: RsaMode, const N: usize> RsaModularMultiplication<'a, 'd, T, Async> + impl RsaModularMultiplication<'_, '_, T, Async> where T: RsaMode, { @@ -483,12 +492,12 @@ pub(crate) mod asynch { } } - impl<'a, 'd, T: RsaMode + Multi, const N: usize> RsaMultiplication<'a, 'd, T, Async> + impl RsaMultiplication<'_, '_, T, Async> where T: RsaMode, { /// Asynchronously performs an RSA multiplication operation. - pub async fn multiplication<'b, const O: usize>( + pub async fn multiplication( &mut self, operand_b: &T::InputType, outbuf: &mut T::OutputType, diff --git a/esp-hal/src/rtc_cntl/mod.rs b/esp-hal/src/rtc_cntl/mod.rs index 4a51ca11b19..772790fd343 100644 --- a/esp-hal/src/rtc_cntl/mod.rs +++ b/esp-hal/src/rtc_cntl/mod.rs @@ -26,6 +26,7 @@ //! # use esp_hal::delay::Delay; //! # use esp_hal::rtc_cntl::Rtc; //! # use esp_hal::rtc_cntl::Rwdt; +//! # use esp_hal::rtc_cntl::RwdtStage; //! # use crate::esp_hal::InterruptConfigurable; //! static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); //! let mut delay = Delay::new(); @@ -33,7 +34,7 @@ //! let mut rtc = Rtc::new(peripherals.LPWR); //! //! rtc.set_interrupt_handler(interrupt_handler); -//! rtc.rwdt.set_timeout(2000.millis()); +//! rtc.rwdt.set_timeout(RwdtStage::Stage0, 2000.millis()); //! rtc.rwdt.listen(); //! //! critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt)); @@ -44,6 +45,7 @@ //! //! # use critical_section::Mutex; //! # use esp_hal::rtc_cntl::Rwdt; +//! # use esp_hal::rtc_cntl::RwdtStage; //! //! static RWDT: Mutex>> = Mutex::new(RefCell::new(None)); //! @@ -59,7 +61,7 @@ //! //! // esp_println::println!("Restarting in 5 seconds..."); //! -//! rwdt.set_timeout(5000u64.millis()); +//! rwdt.set_timeout(RwdtStage::Stage0, 5000u64.millis()); //! rwdt.unlisten(); //! }); //! } @@ -225,7 +227,7 @@ impl<'d> Rtc<'d> { let this = Self { _inner: rtc_cntl.into_ref(), - rwdt: Rwdt::default(), + rwdt: Rwdt::new(), #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] swd: Swd::new(), }; @@ -281,7 +283,7 @@ impl<'d> Rtc<'d> { pub fn time_since_boot(&self) -> MicrosDurationU64 { MicrosDurationU64::micros( self.time_since_boot_raw() * 1_000_000 - / RtcClock::get_slow_freq().frequency().to_Hz() as u64, + / RtcClock::slow_freq().frequency().to_Hz() as u64, ) } @@ -428,27 +430,22 @@ impl<'d> Rtc<'d> { .modify(|r, w| unsafe { w.bits(r.bits() | Self::RTC_DISABLE_ROM_LOG) }); } } -impl<'d> crate::private::Sealed for Rtc<'d> {} +impl crate::private::Sealed for Rtc<'_> {} -impl<'d> InterruptConfigurable for Rtc<'d> { +impl InterruptConfigurable for Rtc<'_> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - interrupt::bind_interrupt( - #[cfg(any(esp32c6, esp32h2))] - Interrupt::LP_WDT, - #[cfg(not(any(esp32c6, esp32h2)))] - Interrupt::RTC_CORE, - handler.handler(), - ); - interrupt::enable( - #[cfg(any(esp32c6, esp32h2))] - Interrupt::LP_WDT, - #[cfg(not(any(esp32c6, esp32h2)))] - Interrupt::RTC_CORE, - handler.priority(), - ) - .unwrap(); + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let interrupt = Interrupt::LP_WDT; + } else { + let interrupt = Interrupt::RTC_CORE; + } + } + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); } + unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(interrupt::enable(interrupt, handler.priority())); } } @@ -519,7 +516,7 @@ impl RtcClock { /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the /// bootloader, as passed to rtc_clk_init function. #[cfg(not(any(esp32c6, esp32h2)))] - pub fn get_xtal_freq() -> XtalClock { + pub fn xtal_freq() -> XtalClock { match Self::read_xtal_freq_mhz() { None | Some(40) => XtalClock::RtcXtalFreq40M, #[cfg(any(esp32c3, esp32s3))] @@ -532,7 +529,7 @@ impl RtcClock { /// Get the RTC_SLOW_CLK source. #[cfg(not(any(esp32c6, esp32h2)))] - pub fn get_slow_freq() -> RtcSlowClock { + pub fn slow_freq() -> RtcSlowClock { let rtc_cntl = unsafe { &*LPWR::PTR }; let slow_freq = rtc_cntl.clk_conf().read().ana_clk_rtc_sel().bits(); match slow_freq { @@ -593,7 +590,7 @@ impl RtcClock { // The following code emulates ESP32 behavior for the other chips: #[cfg(not(esp32))] let cal_clk = match cal_clk { - RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() { + RtcCalSel::RtcCalRtcMux => match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, RtcSlowClock::RtcSlowClock8mD256 => RtcCalSel::RtcCal8mD256, _ => cal_clk, @@ -728,7 +725,7 @@ impl RtcClock { /// Measure ratio between XTAL frequency and RTC slow clock frequency. #[cfg(not(any(esp32c6, esp32h2)))] - fn get_calibration_ratio(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + fn calibration_ratio(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; let ratio = (xtal_cycles << RtcClock::CAL_FRACT) / slowclk_cycles as u64; @@ -744,7 +741,7 @@ impl RtcClock { /// issue, or lack of 32 XTAL on board). #[cfg(not(any(esp32c6, esp32h2)))] fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { - let xtal_freq = RtcClock::get_xtal_freq(); + let xtal_freq = RtcClock::xtal_freq(); let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64; let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider; @@ -756,7 +753,7 @@ impl RtcClock { #[cfg(not(any(esp32c6, esp32h2)))] fn cycles_to_1ms() -> u16 { let period_13q19 = RtcClock::calibrate( - match RtcClock::get_slow_freq() { + match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClockRtc => RtcCalSel::RtcCalRtcMux, RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, #[cfg(not(any(esp32c6, esp32h2)))] @@ -785,7 +782,7 @@ impl RtcClock { RtcClock::enable_8m(true, true); } - let ratio = RtcClock::get_calibration_ratio(RtcCalSel::RtcCal8mD256, XTAL_FREQ_EST_CYCLES); + let ratio = RtcClock::calibration_ratio(RtcCalSel::RtcCal8mD256, XTAL_FREQ_EST_CYCLES); let freq_mhz = ((ratio as u64 * RtcFastClock::RtcFastClock8m.hz() as u64 / 1_000_000u64 / 256u64) >> RtcClock::CAL_FRACT) as u32; @@ -799,36 +796,55 @@ impl RtcClock { /// Behavior of the RWDT stage if it times out. #[allow(unused)] #[derive(Debug, Clone, Copy)] -enum RwdtStageAction { +pub enum RwdtStageAction { + /// No effect on the system. Off = 0, + /// Trigger an interrupt. Interrupt = 1, + /// Reset the CPU core. ResetCpu = 2, - ResetSystem = 3, - ResetRtc = 4, + /// Reset the main system. + /// The power management unit and RTC peripherals will not be reset. + ResetCore = 3, + /// Reset the main system, power management unit and RTC peripherals. + ResetSystem = 4, } -/// RTC Watchdog Timer. -pub struct Rwdt { - stg0_action: RwdtStageAction, - stg1_action: RwdtStageAction, - stg2_action: RwdtStageAction, - stg3_action: RwdtStageAction, +/// RWDT stages. +/// +/// Timer stages allow for a timer to have a series of different timeout values +/// and corresponding expiry action. +#[derive(Debug, Clone, Copy)] +pub enum RwdtStage { + /// RWDT stage 0. + Stage0, + /// RWDT stage 1. + Stage1, + /// RWDT stage 2. + Stage2, + /// RWDT stage 3. + Stage3, } +/// RTC Watchdog Timer. +pub struct Rwdt; + impl Default for Rwdt { fn default() -> Self { - Self { - stg0_action: RwdtStageAction::ResetRtc, - stg1_action: RwdtStageAction::Off, - stg2_action: RwdtStageAction::Off, - stg3_action: RwdtStageAction::Off, - } + Self::new() } } /// RTC Watchdog Timer driver. impl Rwdt { + /// Create a new RTC watchdog timer instance + pub fn new() -> Self { + Self + } + /// Enable the watchdog timer instance. + /// Watchdog starts with default settings (`stage 0` resets the system, the + /// others are deactivated) pub fn enable(&mut self) { self.set_enabled(true); } @@ -838,36 +854,32 @@ impl Rwdt { self.set_enabled(false); } - /// Listen for interrupts. + /// Listen for interrupts on stage 0. pub fn listen(&mut self) { let rtc_cntl = unsafe { lp_wdt() }; - self.stg0_action = RwdtStageAction::Interrupt; - self.set_write_protection(false); // Configure STAGE0 to trigger an interrupt upon expiration rtc_cntl .wdtconfig0() - .modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) }); + .modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::Interrupt as u8) }); rtc_cntl.int_ena().modify(|_, w| w.wdt().set_bit()); self.set_write_protection(true); } - /// Stop listening for interrupts. + /// Stop listening for interrupts on stage 0. pub fn unlisten(&mut self) { let rtc_cntl = unsafe { lp_wdt() }; - self.stg0_action = RwdtStageAction::ResetRtc; - self.set_write_protection(false); // Configure STAGE0 to reset the main system and the RTC upon expiration. rtc_cntl .wdtconfig0() - .modify(|_, w| unsafe { w.wdt_stg0().bits(self.stg0_action as u8) }); + .modify(|_, w| unsafe { w.wdt_stg0().bits(RwdtStageAction::ResetSystem as u8) }); rtc_cntl.int_ena().modify(|_, w| w.wdt().clear_bit()); @@ -914,15 +926,43 @@ impl Rwdt { self.set_write_protection(false); - rtc_cntl - .wdtconfig0() - .modify(|_, w| w.wdt_en().bit(enable).wdt_flashboot_mod_en().bit(enable)); + if !enable { + rtc_cntl.wdtconfig0().modify(|_, w| unsafe { w.bits(0) }); + } else { + rtc_cntl + .wdtconfig0() + .write(|w| w.wdt_flashboot_mod_en().bit(false)); + + rtc_cntl + .wdtconfig0() + .modify(|_, w| w.wdt_en().bit(enable).wdt_pause_in_slp().bit(enable)); + + // Apply default settings for WDT + unsafe { + rtc_cntl.wdtconfig0().modify(|_, w| { + w.wdt_stg0() + .bits(RwdtStageAction::ResetSystem as u8) + .wdt_cpu_reset_length() + .bits(7) + .wdt_sys_reset_length() + .bits(7) + .wdt_stg1() + .bits(RwdtStageAction::Off as u8) + .wdt_stg2() + .bits(RwdtStageAction::Off as u8) + .wdt_stg3() + .bits(RwdtStageAction::Off as u8) + .wdt_en() + .set_bit() + }); + } + } self.set_write_protection(true); } - /// Configure timeout value in ms. - pub fn set_timeout(&mut self, timeout: MicrosDurationU64) { + /// Configure timeout value in ms for the selected stage. + pub fn set_timeout(&mut self, stage: RwdtStage, timeout: MicrosDurationU64) { let rtc_cntl = unsafe { lp_wdt() }; let timeout_raw = (timeout.to_millis() * (RtcClock::cycles_to_1ms() as u64)) as u32; @@ -930,42 +970,88 @@ impl Rwdt { unsafe { #[cfg(esp32)] - rtc_cntl - .wdtconfig1() - .modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)); + match stage { + RwdtStage::Stage0 => rtc_cntl + .wdtconfig1() + .modify(|_, w| w.wdt_stg0_hold().bits(timeout_raw)), + RwdtStage::Stage1 => rtc_cntl + .wdtconfig2() + .modify(|_, w| w.wdt_stg1_hold().bits(timeout_raw)), + RwdtStage::Stage2 => rtc_cntl + .wdtconfig3() + .modify(|_, w| w.wdt_stg2_hold().bits(timeout_raw)), + RwdtStage::Stage3 => rtc_cntl + .wdtconfig4() + .modify(|_, w| w.wdt_stg3_hold().bits(timeout_raw)), + }; #[cfg(any(esp32c6, esp32h2))] - rtc_cntl.config1().modify(|_, w| { - w.wdt_stg0_hold() - .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) - }); + match stage { + RwdtStage::Stage0 => rtc_cntl.config1().modify(|_, w| { + w.wdt_stg0_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage1 => rtc_cntl.config2().modify(|_, w| { + w.wdt_stg1_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage2 => rtc_cntl.config3().modify(|_, w| { + w.wdt_stg2_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage3 => rtc_cntl.config4().modify(|_, w| { + w.wdt_stg3_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + }; #[cfg(not(any(esp32, esp32c6, esp32h2)))] - rtc_cntl.wdtconfig1().modify(|_, w| { - w.wdt_stg0_hold() - .bits(timeout_raw >> (1 + Efuse::get_rwdt_multiplier())) - }); - - rtc_cntl.wdtconfig0().modify(|_, w| { - w.wdt_stg0() - .bits(self.stg0_action as u8) - .wdt_cpu_reset_length() - .bits(7) - .wdt_sys_reset_length() - .bits(7) - .wdt_stg1() - .bits(self.stg1_action as u8) - .wdt_stg2() - .bits(self.stg2_action as u8) - .wdt_stg3() - .bits(self.stg3_action as u8) - .wdt_en() - .set_bit() - }); + match stage { + RwdtStage::Stage0 => rtc_cntl.wdtconfig1().modify(|_, w| { + w.wdt_stg0_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage1 => rtc_cntl.wdtconfig2().modify(|_, w| { + w.wdt_stg1_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage2 => rtc_cntl.wdtconfig3().modify(|_, w| { + w.wdt_stg2_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + RwdtStage::Stage3 => rtc_cntl.wdtconfig4().modify(|_, w| { + w.wdt_stg3_hold() + .bits(timeout_raw >> (1 + Efuse::rwdt_multiplier())) + }), + }; } self.set_write_protection(true); } + + /// Set the action for a specific stage. + pub fn set_stage_action(&mut self, stage: RwdtStage, action: RwdtStageAction) { + let rtc_cntl = unsafe { lp_wdt() }; + + self.set_write_protection(false); + + match stage { + RwdtStage::Stage0 => rtc_cntl + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) }), + RwdtStage::Stage1 => rtc_cntl + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) }), + RwdtStage::Stage2 => rtc_cntl + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) }), + RwdtStage::Stage3 => rtc_cntl + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) }), + }; + + self.set_write_protection(true); + } } impl embedded_hal_02::watchdog::WatchdogDisable for Rwdt { @@ -981,7 +1067,7 @@ impl embedded_hal_02::watchdog::WatchdogEnable for Rwdt { where T: Into, { - self.set_timeout(period.into()); + self.set_timeout(RwdtStage::Stage0, period.into()); } } @@ -1053,15 +1139,15 @@ impl embedded_hal_02::watchdog::WatchdogDisable for Swd { } /// Return reset reason. -pub fn get_reset_reason(cpu: Cpu) -> Option { +pub fn reset_reason(cpu: Cpu) -> Option { let reason = crate::rom::rtc_get_reset_reason(cpu as u32); SocResetReason::from_repr(reason as usize) } /// Return wakeup reason. -pub fn get_wakeup_cause() -> SleepSource { - if get_reset_reason(Cpu::ProCpu) != Some(SocResetReason::CoreDeepSleep) { +pub fn wakeup_cause() -> SleepSource { + if reset_reason(Cpu::ProCpu) != Some(SocResetReason::CoreDeepSleep) { return SleepSource::Undefined; } @@ -1135,6 +1221,6 @@ pub fn get_wakeup_cause() -> SleepSource { // so that either ieee or esp-wifi gets it for free without duplicating in both #[no_mangle] extern "C" fn rtc_clk_xtal_freq_get() -> i32 { - let xtal = RtcClock::get_xtal_freq(); + let xtal = RtcClock::xtal_freq(); xtal.mhz() as i32 } diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c3.rs b/esp-hal/src/rtc_cntl/rtc/esp32c3.rs index 57719925832..fce8684fbb8 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c3.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c3.rs @@ -86,10 +86,7 @@ pub(crate) fn init() { } pub(crate) fn configure_clock() { - assert!(matches!( - RtcClock::get_xtal_freq(), - XtalClock::RtcXtalFreq40M - )); + assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M)); unsafe { // from esp_clk_init: diff --git a/esp-hal/src/rtc_cntl/rtc/esp32c6.rs b/esp-hal/src/rtc_cntl/rtc/esp32c6.rs index a30929f62a6..66d72abf62c 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32c6.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32c6.rs @@ -267,7 +267,7 @@ fn modem_clock_hal_deselect_all_wifi_lpclk_source() { .clear_bit() .clk_wifipwr_lp_sel_xtal() .clear_bit() - }) + }); } } @@ -299,7 +299,7 @@ fn modem_lpcon_ll_set_wifi_lpclk_divisor_value(divider: u16) { unsafe { modem_lpcon() .wifi_lp_clk_conf() - .modify(|_, w| w.clk_wifipwr_lp_div_num().bits(divider)) + .modify(|_, w| w.clk_wifipwr_lp_div_num().bits(divider)); } } @@ -1200,10 +1200,7 @@ pub(crate) fn init() { } pub(crate) fn configure_clock() { - assert!(matches!( - RtcClock::get_xtal_freq(), - XtalClock::RtcXtalFreq40M - )); + assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M)); RtcClock::set_fast_freq(RtcFastClock::RtcFastClockRcFast); @@ -1423,22 +1420,22 @@ pub(crate) enum RtcCaliClkSel { /// RTC Watchdog Timer driver impl RtcClock { // rtc_clk_xtal_freq_get - pub(crate) fn get_xtal_freq_mhz() -> u32 { + pub(crate) fn xtal_freq_mhz() -> u32 { Self::read_xtal_freq_mhz().unwrap_or(40) } /// Get main XTAL frequency /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the /// bootloader, as passed to rtc_clk_init function. - pub fn get_xtal_freq() -> XtalClock { - match Self::get_xtal_freq_mhz() { + pub fn xtal_freq() -> XtalClock { + match Self::xtal_freq_mhz() { 40 => XtalClock::RtcXtalFreq40M, other => XtalClock::RtcXtalFreqOther(other), } } /// Get the RTC_SLOW_CLK source - pub fn get_slow_freq() -> RtcSlowClock { + pub fn slow_freq() -> RtcSlowClock { let lp_clrst = unsafe { lp_clkrst() }; let slow_freq = lp_clrst.lp_clk_conf().read().slow_clk_sel().bits(); @@ -1492,7 +1489,7 @@ impl RtcClock { if cal_clk == RtcCalSel::RtcCalRtcMux { cal_clk = match cal_clk { - RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() { + RtcCalSel::RtcCalRtcMux => match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, _ => cal_clk, @@ -1506,7 +1503,7 @@ impl RtcClock { let pcr = unsafe { pcr() }; let pmu = unsafe { pmu() }; - let clk_src = RtcClock::get_slow_freq(); + let clk_src = RtcClock::slow_freq(); if cal_clk == RtcCalSel::RtcCalRtcMux { cal_clk = match clk_src { @@ -1534,16 +1531,19 @@ impl RtcClock { } else { cali_clk_sel = RtcCaliClkSel::CaliClk32k; match cal_clk { - RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => (), - RtcCalSel::RtcCal32kRc => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }), - RtcCalSel::RtcCal32kXtal => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }), - RtcCalSel::RtcCal32kOscSlow => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }), + RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => {} + RtcCalSel::RtcCal32kRc => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }); + } + RtcCalSel::RtcCal32kXtal => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }); + } + RtcCalSel::RtcCal32kOscSlow => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }); + } } } @@ -1753,7 +1753,7 @@ impl RtcClock { /// not started up (due to incorrect loading capacitance, board design /// issue, or lack of 32 XTAL on board). fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { - let xtal_freq = RtcClock::get_xtal_freq(); + let xtal_freq = RtcClock::xtal_freq(); let mut slowclk_cycles = slowclk_cycles; @@ -1774,7 +1774,7 @@ impl RtcClock { /// Calculate the necessary RTC_SLOW_CLK cycles to complete 1 millisecond. pub(crate) fn cycles_to_1ms() -> u16 { let period_13q19 = RtcClock::calibrate( - match RtcClock::get_slow_freq() { + match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRtcMux, RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, @@ -1818,7 +1818,7 @@ impl RtcClock { pub(crate) fn rtc_clk_cpu_freq_set_xtal() { // rtc_clk_cpu_set_to_default_config - let freq = RtcClock::get_xtal_freq_mhz(); + let freq = RtcClock::xtal_freq_mhz(); esp32c6_rtc_update_to_xtal_raw(freq, 1); @@ -1886,7 +1886,7 @@ impl SavedClockConfig { match source { CpuClockSource::Xtal => { div = esp32c6_cpu_get_ls_divider(); - source_freq_mhz = RtcClock::get_xtal_freq_mhz(); + source_freq_mhz = RtcClock::xtal_freq_mhz(); } CpuClockSource::Pll => { div = esp32c6_cpu_get_hs_divider(); @@ -1920,7 +1920,7 @@ impl SavedClockConfig { if old_source != CpuClockSource::Pll { rtc_clk_bbpll_enable(); esp32c6_rtc_bbpll_configure_raw( - RtcClock::get_xtal_freq_mhz(), + RtcClock::xtal_freq_mhz(), self.source_freq_mhz, ); } diff --git a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs index 61301d68131..5fd0a72ec12 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32h2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32h2.rs @@ -120,10 +120,7 @@ pub(crate) fn init() { } pub(crate) fn configure_clock() { - assert!(matches!( - RtcClock::get_xtal_freq(), - XtalClock::RtcXtalFreq32M - )); + assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq32M)); RtcClock::set_fast_freq(RtcFastClock::RtcFastClockRcFast); @@ -270,7 +267,7 @@ impl RtcClock { /// Get main XTAL frequency. /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the /// bootloader, as passed to rtc_clk_init function. - pub fn get_xtal_freq() -> XtalClock { + pub fn xtal_freq() -> XtalClock { match Self::read_xtal_freq_mhz() { None | Some(32) => XtalClock::RtcXtalFreq32M, Some(other) => XtalClock::RtcXtalFreqOther(other), @@ -309,7 +306,7 @@ impl RtcClock { } /// Get the RTC_SLOW_CLK source - pub fn get_slow_freq() -> RtcSlowClock { + pub fn slow_freq() -> RtcSlowClock { let lp_clrst = unsafe { &*LPWR::ptr() }; let slow_freq = lp_clrst.lp_clk_conf().read().slow_clk_sel().bits(); @@ -323,7 +320,7 @@ impl RtcClock { } fn calibrate(cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { - let xtal_freq = RtcClock::get_xtal_freq(); + let xtal_freq = RtcClock::xtal_freq(); let xtal_cycles = RtcClock::calibrate_internal(cal_clk, slowclk_cycles) as u64; let divider = xtal_freq.mhz() as u64 * slowclk_cycles as u64; let period_64 = ((xtal_cycles << RtcClock::CAL_FRACT) + divider / 2u64 - 1u64) / divider; @@ -341,7 +338,7 @@ impl RtcClock { if cal_clk == RtcCalSel::RtcCalRtcMux { cal_clk = match cal_clk { - RtcCalSel::RtcCalRtcMux => match RtcClock::get_slow_freq() { + RtcCalSel::RtcCalRtcMux => match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, _ => cal_clk, @@ -355,7 +352,7 @@ impl RtcClock { let pcr = unsafe { &*PCR::ptr() }; let pmu = unsafe { &*PMU::ptr() }; - let clk_src = RtcClock::get_slow_freq(); + let clk_src = RtcClock::slow_freq(); if cal_clk == RtcCalSel::RtcCalRtcMux { cal_clk = match clk_src { @@ -383,16 +380,19 @@ impl RtcClock { } else { cali_clk_sel = RtcCaliClkSel::CaliClk32k; match cal_clk { - RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => (), - RtcCalSel::RtcCal32kRc => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }), - RtcCalSel::RtcCal32kXtal => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }), - RtcCalSel::RtcCal32kOscSlow => pcr - .ctrl_32k_conf() - .modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }), + RtcCalSel::RtcCalRtcMux | RtcCalSel::RtcCalRcSlow | RtcCalSel::RtcCalRcFast => {} + RtcCalSel::RtcCal32kRc => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(0) }); + } + RtcCalSel::RtcCal32kXtal => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(1) }); + } + RtcCalSel::RtcCal32kOscSlow => { + pcr.ctrl_32k_conf() + .modify(|_, w| unsafe { w.clk_32k_sel().bits(2) }); + } } } @@ -584,7 +584,7 @@ impl RtcClock { pub(crate) fn cycles_to_1ms() -> u16 { let period_13q19 = RtcClock::calibrate( - match RtcClock::get_slow_freq() { + match RtcClock::slow_freq() { RtcSlowClock::RtcSlowClockRcSlow => RtcCalSel::RtcCalRtcMux, RtcSlowClock::RtcSlowClock32kXtal => RtcCalSel::RtcCal32kXtal, RtcSlowClock::RtcSlowClock32kRc => RtcCalSel::RtcCal32kRc, diff --git a/esp-hal/src/rtc_cntl/rtc/esp32s2.rs b/esp-hal/src/rtc_cntl/rtc/esp32s2.rs index 51250e35e05..cf679877770 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32s2.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32s2.rs @@ -9,10 +9,7 @@ use crate::{ pub(crate) fn init() {} pub(crate) fn configure_clock() { - assert!(matches!( - RtcClock::get_xtal_freq(), - XtalClock::RtcXtalFreq40M - )); + assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M)); RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m); diff --git a/esp-hal/src/rtc_cntl/rtc/esp32s3.rs b/esp-hal/src/rtc_cntl/rtc/esp32s3.rs index 35c12f8bde3..37a682d7a34 100644 --- a/esp-hal/src/rtc_cntl/rtc/esp32s3.rs +++ b/esp-hal/src/rtc_cntl/rtc/esp32s3.rs @@ -9,10 +9,7 @@ use crate::{ pub(crate) fn init() {} pub(crate) fn configure_clock() { - assert!(matches!( - RtcClock::get_xtal_freq(), - XtalClock::RtcXtalFreq40M - )); + assert!(matches!(RtcClock::xtal_freq(), XtalClock::RtcXtalFreq40M)); RtcClock::set_fast_freq(RtcFastClock::RtcFastClock8m); diff --git a/esp-hal/src/rtc_cntl/sleep/esp32.rs b/esp-hal/src/rtc_cntl/sleep/esp32.rs index f8c3446939c..c43cf8ad19f 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32.rs @@ -76,7 +76,7 @@ impl WakeSource for TimerWakeupSource { ) { triggers.set_timer(true); let rtc_cntl = unsafe { &*esp32::RTC_CNTL::ptr() }; - let clock_freq = RtcClock::get_slow_freq(); + let clock_freq = RtcClock::slow_freq(); // TODO: maybe add sleep time adjustlemnt like idf // TODO: maybe add check to prevent overflow? let clock_hz = clock_freq.frequency().to_Hz() as u64; diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c2.rs b/esp-hal/src/rtc_cntl/sleep/esp32c2.rs index 7fb003fd11b..9609ea5141f 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c2.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c2.rs @@ -130,7 +130,7 @@ impl WakeSource for TimerWakeupSource { ) { triggers.set_timer(true); let rtc_cntl = unsafe { &*esp32c2::RTC_CNTL::ptr() }; - let clock_freq = RtcClock::get_slow_freq(); + let clock_freq = RtcClock::slow_freq(); // TODO: maybe add sleep time adjustlemnt like idf // TODO: maybe add check to prevent overflow? let clock_hz = clock_freq.frequency().to_Hz() as u64; @@ -157,7 +157,7 @@ impl WakeSource for TimerWakeupSource { } } -impl<'a, 'b> RtcioWakeupSource<'a, 'b> { +impl RtcioWakeupSource<'_, '_> { fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) { // The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare let level = match level { diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c3.rs b/esp-hal/src/rtc_cntl/sleep/esp32c3.rs index 39d165ce29c..48a23fcec69 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c3.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c3.rs @@ -130,7 +130,7 @@ impl WakeSource for TimerWakeupSource { ) { triggers.set_timer(true); let rtc_cntl = unsafe { &*esp32c3::RTC_CNTL::ptr() }; - let clock_freq = RtcClock::get_slow_freq(); + let clock_freq = RtcClock::slow_freq(); // TODO: maybe add sleep time adjustlemnt like idf // TODO: maybe add check to prevent overflow? let clock_hz = clock_freq.frequency().to_Hz() as u64; @@ -157,7 +157,7 @@ impl WakeSource for TimerWakeupSource { } } -impl<'a, 'b> RtcioWakeupSource<'a, 'b> { +impl RtcioWakeupSource<'_, '_> { fn apply_pin(&self, pin: &mut dyn RtcPinWithResistors, level: WakeupLevel) { // The pullup/pulldown part is like in gpio_deep_sleep_wakeup_prepare let level = match level { diff --git a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs index 19e737c487c..e874617b7a8 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32c6.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32c6.rs @@ -3,7 +3,7 @@ use core::ops::Not; use crate::{ clock::Clock, efuse::Efuse, - gpio::{Pins, RtcFunction}, + gpio::RtcFunction, rtc_cntl::{ rtc::{ rtc_clk_cpu_freq_set_xtal, @@ -38,7 +38,7 @@ impl WakeSource for TimerWakeupSource { triggers.set_timer(true); let lp_timer = unsafe { &*esp32c6::LP_TIMER::ptr() }; - let clock_freq = RtcClock::get_slow_freq(); + let clock_freq = RtcClock::slow_freq(); // TODO: maybe add sleep time adjustment like idf // TODO: maybe add check to prevent overflow? let clock_hz = clock_freq.frequency().to_Hz() as u64; @@ -71,10 +71,10 @@ impl Ext1WakeupSource<'_, '_> { unsafe { lp_aon().ext_wakeup_cntl().read().ext_wakeup_sel().bits() } } - fn wake_io_reset(pins: &mut Pins) { - use crate::gpio::RtcPin; + fn wake_io_reset() { + use crate::gpio::{GpioPin, RtcPin}; - fn uninit_pin(pin: &mut impl RtcPin, wakeup_pins: u8) { + fn uninit_pin(mut pin: impl RtcPin, wakeup_pins: u8) { if wakeup_pins & (1 << pin.number()) != 0 { pin.rtcio_pad_hold(false); pin.rtc_set_config(false, false, RtcFunction::Rtc); @@ -82,14 +82,14 @@ impl Ext1WakeupSource<'_, '_> { } let wakeup_pins = Ext1WakeupSource::wakeup_pins(); - uninit_pin(&mut pins.gpio0, wakeup_pins); - uninit_pin(&mut pins.gpio1, wakeup_pins); - uninit_pin(&mut pins.gpio2, wakeup_pins); - uninit_pin(&mut pins.gpio3, wakeup_pins); - uninit_pin(&mut pins.gpio4, wakeup_pins); - uninit_pin(&mut pins.gpio5, wakeup_pins); - uninit_pin(&mut pins.gpio6, wakeup_pins); - uninit_pin(&mut pins.gpio7, wakeup_pins); + uninit_pin(unsafe { GpioPin::<0>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<1>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<2>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<3>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<4>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<5>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<6>::steal() }, wakeup_pins); + uninit_pin(unsafe { GpioPin::<7>::steal() }, wakeup_pins); } } @@ -898,15 +898,7 @@ impl RtcSleepConfig { fn wake_io_reset() { // loosely based on esp_deep_sleep_wakeup_io_reset - - let mut pins = unsafe { - // We're stealing pins to do some uninitialization after waking up from - // deep sleep. We have to be careful to only touch settings that were enabled - // by deep sleep setup. - Pins::steal() - }; - - Ext1WakeupSource::wake_io_reset(&mut pins); + Ext1WakeupSource::wake_io_reset(); } /// Finalize power-down flags, apply configuration based on the flags. diff --git a/esp-hal/src/rtc_cntl/sleep/esp32s3.rs b/esp-hal/src/rtc_cntl/sleep/esp32s3.rs index e4ebfeb05e8..3fbb349a264 100644 --- a/esp-hal/src/rtc_cntl/sleep/esp32s3.rs +++ b/esp-hal/src/rtc_cntl/sleep/esp32s3.rs @@ -116,7 +116,7 @@ impl WakeSource for TimerWakeupSource { ) { triggers.set_timer(true); let rtc_cntl = unsafe { &*esp32s3::RTC_CNTL::ptr() }; - let clock_freq = RtcClock::get_slow_freq(); + let clock_freq = RtcClock::slow_freq(); // TODO: maybe add sleep time adjustlemnt like idf // TODO: maybe add check to prevent overflow? let clock_hz = clock_freq.frequency().to_Hz() as u64; diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 6758ea2ec48..48e85227628 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -62,6 +62,8 @@ use core::{borrow::BorrowMut, convert::Infallible, marker::PhantomData, mem::siz #[cfg(feature = "digest")] pub use digest::Digest; +#[cfg(not(esp32))] +use crate::peripherals::Interrupt; use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::SHA, @@ -98,16 +100,16 @@ impl<'d> Sha<'d> { } } -impl<'d> crate::private::Sealed for Sha<'d> {} +impl crate::private::Sealed for Sha<'_> {} #[cfg(not(esp32))] -impl<'d> crate::InterruptConfigurable for Sha<'d> { +impl crate::InterruptConfigurable for Sha<'_> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::SHA, handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::SHA, handler.priority()) - .unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::SHA); } + unsafe { crate::interrupt::bind_interrupt(Interrupt::SHA, handler.handler()) }; + unwrap!(crate::interrupt::enable(Interrupt::SHA, handler.priority())); } } diff --git a/esp-hal/src/soc/esp32/cpu_control.rs b/esp-hal/src/soc/esp32/cpu_control.rs index b7d5cc0503b..d0a0a143202 100644 --- a/esp-hal/src/soc/esp32/cpu_control.rs +++ b/esp-hal/src/soc/esp32/cpu_control.rs @@ -328,7 +328,7 @@ impl<'d> CpuControl<'d> { let entry = unsafe { ManuallyDrop::take(&mut *entry.cast::>()) }; entry(); loop { - unsafe { internal_park_core(crate::get_core()) }; + unsafe { internal_park_core(Cpu::current()) }; } } None => panic!("No start function set"), diff --git a/esp-hal/src/soc/esp32/efuse/mod.rs b/esp-hal/src/soc/esp32/efuse/mod.rs index 79773ff0ddd..9b7467240bc 100644 --- a/esp-hal/src/soc/esp32/efuse/mod.rs +++ b/esp-hal/src/soc/esp32/efuse/mod.rs @@ -24,12 +24,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -84,7 +82,7 @@ impl Efuse { /// While ESP32 chips usually come with two mostly equivalent CPUs (protocol /// CPU and application CPU), the application CPU is unavailable on /// some. - pub fn get_core_count() -> u32 { + pub fn core_count() -> u32 { if Self::read_bit(DISABLE_APP_CPU) { 1 } else { @@ -96,7 +94,7 @@ impl Efuse { /// /// Note that the actual clock may be lower, depending on the current power /// configuration of the chip, clock source, and other settings. - pub fn get_max_cpu_frequency() -> HertzU32 { + pub fn max_cpu_frequency() -> HertzU32 { let has_rating = Self::read_bit(CHIP_CPU_FREQ_RATED); let has_low_rating = Self::read_bit(CHIP_CPU_FREQ_LOW); @@ -113,7 +111,7 @@ impl Efuse { } /// Returns the CHIP_VER_PKG eFuse value. - pub fn get_chip_type() -> ChipType { + pub fn chip_type() -> ChipType { let chip_ver = Self::read_field_le::(CHIP_PACKAGE) | Self::read_field_le::(CHIP_PACKAGE_4BIT) << 4; @@ -129,7 +127,7 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(FLASH_CRYPT_CNT).count_ones() % 2) != 0 } } diff --git a/esp-hal/src/soc/esp32/gpio.rs b/esp-hal/src/soc/esp32/gpio.rs index e33368f0aa2..21e85a6f423 100644 --- a/esp-hal/src/soc/esp32/gpio.rs +++ b/esp-hal/src/soc/esp32/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: +//! - `io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: //! * This function returns a reference to the GPIO register associated //! with the given GPIO number. It uses unsafe code and transmutation to //! access the GPIO registers based on the provided GPIO number. @@ -53,6 +53,7 @@ pub const NUM_PINS: usize = 40; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u16; pub(crate) type OutputSignalType = u16; pub(crate) const OUTPUT_SIGNAL_MAX: u16 = 548; pub(crate) const INPUT_SIGNAL_MAX: u16 = 539; @@ -62,7 +63,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x30; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function2; -pub(crate) fn get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { +pub(crate) fn io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { unsafe { let iomux = &*IO_MUX::PTR; @@ -109,7 +110,7 @@ pub(crate) fn get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { } pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { - match crate::get_core() { + match Cpu::current() { Cpu::AppCpu => int_enable as u8 | ((nmi_enable as u8) << 1), // this should be bits 3 & 4 respectively, according to the TRM, but it doesn't seem to // work. This does though. @@ -119,7 +120,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { SPICLK = 0, @@ -310,7 +312,8 @@ pub enum InputSignal { /// Peripheral output signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { SPICLK = 0, @@ -527,46 +530,221 @@ pub enum OutputSignal { MTDO, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_TX_CLK) (1 => CLK_OUT1)) - (1, [Input, Output] (5 => EMAC_RXD2) (0 => U0TXD 1 => CLK_OUT3)) - (2, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIWP 3 => HS2_DATA0 4 => SD_DATA0) (3 => HS2_DATA0 4 => SD_DATA0)) - (3, [Input, Output] (0 => U0RXD) (1 => CLK_OUT2)) - (4, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIHD 3 => HS2_DATA1 4 => SD_DATA1 5 => EMAC_TX_ER) (3 => HS2_DATA1 4 => SD_DATA1)) - (5, [Input, Output] (1 => VSPICS0 3 => HS1_DATA6 5 => EMAC_RX_CLK) (3 => HS1_DATA6)) - (6, [Input, Output] (4 => U1CTS) (0 => SD_CLK 1 => SPICLK 3 => HS1_CLK)) - (7, [Input, Output] (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0) (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0 4 => U2RTS)) - (8, [Input, Output] (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1 4 => U2CTS) (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1)) - (9, [Input, Output] (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2 4 => U1RXD) (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2)) - (10, [Input, Output] ( 0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3) (0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3 4 => U1TXD)) - (11, [Input, Output] ( 1 => SPICS0) (0 => SD_CMD 1 => SPICS0 3 => HS1_CMD 4 => U1RTS)) - (12, [Input, Output, Analog, RtcIo, Touch] (0 => MTDI 1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2) (1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2 5 => EMAC_TXD3)) - (13, [Input, Output, Analog, RtcIo, Touch] (0 => MTCK 1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3) (1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3 5 => EMAC_RX_ER)) - (14, [Input, Output, Analog, RtcIo, Touch] (0 => MTMS 1 => HSPICLK) (1 => HSPICLK 3 => HS2_CLK 4 => SD_CLK 5 => EMAC_TXD2)) - (15, [Input, Output, Analog, RtcIo, Touch] (1 => HSPICS0 5 => EMAC_RXD3) (0 => MTDO 1 => HSPICS0 3 => HS2_CMD 4 => SD_CMD)) - (16, [Input, Output] (3 => HS1_DATA4 4 => U2RXD) (3 => HS1_DATA4 5 => EMAC_CLK_OUT)) - (17, [Input, Output] (3 => HS1_DATA5) (3 => HS1_DATA5 4 => U2TXD 5 => EMAC_CLK_180)) - (18, [Input, Output] (1 => VSPICLK 3 => HS1_DATA7) (1 => VSPICLK 3 => HS1_DATA7)) - (19, [Input, Output] (1 => VSPIQ 3 => U0CTS) (1 => VSPIQ 5 => EMAC_TXD0)) - (20, [Input, Output]) - (21, [Input, Output] (1 => VSPIHD) (1 => VSPIHD 5 => EMAC_TX_EN)) - (22, [Input, Output] (1 => VSPIWP) (1 => VSPIWP 3 => U0RTS 5 => EMAC_TXD1)) - (23, [Input, Output] (1 => VSPID) (1 => VSPID 3 => HS1_STROBE)) - (24, [Input, Output]) - (25, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD0) ()) - (26, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD1) ()) - (27, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_RX_DV) ()) - (32, [Input, Output, Analog, RtcIo, Touch]) - (33, [Input, Output, Analog, RtcIo, Touch]) - (34, [Input, Analog, RtcIoInput]) - (35, [Input, Analog, RtcIoInput]) - (36, [Input, Analog, RtcIoInput]) - (37, [Input, Analog, RtcIoInput]) - (38, [Input, Analog, RtcIoInput]) - (39, [Input, Analog, RtcIoInput]) +macro_rules! rtcio_analog { + ( @ignore $rue:literal ) => {}; + + ( + $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $prefix:pat, $hold:ident $(, $rue:literal)? + ) => { + impl $crate::gpio::RtcPin for $crate::gpio::GpioPin<$pin_num> { + fn rtc_number(&self) -> u8 { + $rtc_pin + } + + /// Set the RTC properties of the pin. If `mux` is true then then pin is + /// routed to RTC, when false it is routed to IO_MUX. + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) { + // disable input + paste::paste!{ + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_,w| unsafe { + w.[<$prefix fun_ie>]().bit(input_enable); + w.[<$prefix mux_sel>]().bit(mux); + w.[<$prefix fun_sel>]().bits(func as u8) + }); + } + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + unsafe { $crate::peripherals::LPWR::steal() } + .hold_force() + .modify(|_, w| w.$hold().bit(enable)); + } + } + + $( + // FIXME: replace with $(ignore($rue)) once stable + rtcio_analog!(@ignore $rue); + impl $crate::gpio::RtcPinWithResistors for $crate::gpio::GpioPin<$pin_num> { + fn rtcio_pullup(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit(enable)); + } + } + + fn rtcio_pulldown(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit(enable)); + } + } + } + )? + + impl $crate::gpio::AnalogPin for $crate::gpio::GpioPin<$pin_num> { + /// Configures the pin for analog mode. + fn set_analog(&self, _: $crate::private::Internal) { + use $crate::gpio::RtcPin; + let rtcio = unsafe{ $crate::peripherals::RTC_IO::steal() }; + + paste::paste! { + // disable input + rtcio.$pin_reg.modify(|_,w| w.[<$prefix fun_ie>]().bit(false)); + + // disable output + rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << self.rtc_number()) }); + + // disable open drain + rtcio.pin(self.rtc_number() as usize).modify(|_,w| w.pad_driver().bit(false)); + + rtcio.$pin_reg.modify(|_,w| { + w.[<$prefix fun_ie>]().clear_bit(); + + // Connect pin to analog / RTC module instead of standard GPIO + w.[<$prefix mux_sel>]().set_bit(); + + // Select function "RTC function 1" (GPIO) for analog use + unsafe { w.[<$prefix fun_sel>]().bits(0b00) }; + + // Disable pull-up and pull-down resistors on the pin, if it has them + $( + // FIXME: replace with $(ignore($rue)) once stable + rtcio_analog!( @ignore $rue ); + w.[<$prefix rue>]().bit(false); + w.[<$prefix rde>]().bit(false); + )? + + w + }); + } + } + } + }; + + ( + $( ( $pin_num:expr, $rtc_pin:expr, $pin_reg:expr, $prefix:pat, $hold:ident $(, $rue:literal )? ) )+ + ) => { + $( + rtcio_analog!($pin_num, $rtc_pin, $pin_reg, $prefix, $hold $(, $rue )?); + )+ + + pub(crate) fn errata36(mut pin: $crate::gpio::AnyPin, pull_up: bool, pull_down: bool) { + use $crate::gpio::{Pin, RtcPinWithResistors}; + + let has_pullups = match pin.number() { + $( + $( $pin_num => $rue, )? + )+ + _ => false, + }; + + if has_pullups { + pin.rtcio_pullup(pull_up); + pin.rtcio_pulldown(pull_down); + } + } + }; +} + +/// Common functionality for all touch pads +macro_rules! touch { + (@pin_specific $touch_num:expr, true) => { + paste::paste! { + unsafe { RTC_IO::steal() }.[< touch_pad $touch_num >]().write(|w| unsafe { + w.xpd().set_bit(); + // clear input_enable + w.fun_ie().clear_bit(); + // Connect pin to analog / RTC module instead of standard GPIO + w.mux_sel().set_bit(); + // Disable pull-up and pull-down resistors on the pin + w.rue().clear_bit(); + w.rde().clear_bit(); + w.tie_opt().clear_bit(); + // Select function "RTC function 1" (GPIO) for analog use + w.fun_sel().bits(0b00) + }); + } + }; + + (@pin_specific $touch_num:expr, false) => { + paste::paste! { + unsafe { RTC_IO::steal() }.[< touch_pad $touch_num >]().write(|w| { + w.xpd().set_bit(); + w.tie_opt().clear_bit() + }); + } + }; + + ( + $( + ( + $touch_num:literal, $pin_num:literal, $touch_out_reg:expr, $touch_thres_reg:expr, $normal_pin:literal + ) + )+ + ) => { + $( + impl $crate::gpio::TouchPin for GpioPin<$pin_num> { + fn set_touch(&self, _: $crate::private::Internal) { + use $crate::peripherals::{GPIO, RTC_IO, SENS}; + use $crate::gpio::RtcPin; + + let gpio = unsafe { GPIO::steal() }; + let rtcio = unsafe { RTC_IO::steal() }; + let sens = unsafe { SENS::steal() }; + + // Pad to normal mode (not open-drain) + gpio.pin(self.rtc_number() as usize).write(|w| w.pad_driver().clear_bit()); + + // clear output + rtcio + .enable_w1tc() + .write(|w| unsafe { w.enable_w1tc().bits(1 << self.rtc_number()) }); + paste::paste! { + sens . $touch_thres_reg () + .write(|w| unsafe { + w. [] ().bits( + 0b0 // Default: 0 for esp32 gets overridden later anyway. + ) + }); + + touch!( @pin_specific $touch_num, $normal_pin ); + + // enable the pin + sens.sar_touch_enable().modify(|r, w| unsafe { + w.touch_pad_worken().bits( + r.touch_pad_worken().bits() | ( 1 << $touch_num ) + ) + }); + } + } + + fn touch_measurement(&self, _: $crate::private::Internal) -> u16 { + paste::paste! { + unsafe { $crate::peripherals::SENS::steal() } + . $touch_out_reg ().read() + . [] ().bits() + } + } + + fn touch_nr(&self, _: $crate::private::Internal) -> u8 { + $touch_num + } + + fn set_threshold(&self, threshold: u16, _: $crate::private::Internal) { + paste::paste! { + unsafe { $crate::peripherals::SENS::steal() } + . $touch_thres_reg () + .write(|w| unsafe { + w. [] ().bits(threshold) + }); + } + } + })+ + }; } -crate::rtcio_analog! { +rtcio_analog! { (36, 0, sensor_pads(), sense1_, sense1_hold_force ) (37, 1, sensor_pads(), sense2_, sense2_hold_force ) (38, 2, sensor_pads(), sense3_, sense3_hold_force ) @@ -587,19 +765,19 @@ crate::rtcio_analog! { (27, 17, touch_pad7(), "", touch_pad7_hold_force, true) } -crate::touch! { - // touch_nr, pin_nr, rtc_pin, touch_out_reg, meas_field, touch_thres_reg, touch_thres_field, normal_pin - (0, 4, 10, sar_touch_out1, touch_meas_out0, sar_touch_thres1, touch_out_th0, true) - (1, 0, 11, sar_touch_out1, touch_meas_out1, sar_touch_thres1, touch_out_th1, true) - (2, 2, 12, sar_touch_out2, touch_meas_out2, sar_touch_thres2, touch_out_th2, true) - (3, 15, 13, sar_touch_out2, touch_meas_out3, sar_touch_thres2, touch_out_th3, true) - (4, 13, 14, sar_touch_out3, touch_meas_out4, sar_touch_thres3, touch_out_th4, true) - (5, 12, 15, sar_touch_out3, touch_meas_out5, sar_touch_thres3, touch_out_th5, true) - (6, 14, 16, sar_touch_out4, touch_meas_out6, sar_touch_thres4, touch_out_th6, true) - (7, 27, 17, sar_touch_out4, touch_meas_out7, sar_touch_thres4, touch_out_th7, true) +touch! { + // touch_nr, pin_nr, touch_out_reg, touch_thres_reg, normal_pin + (0, 4, sar_touch_out1, sar_touch_thres1, true) + (1, 0, sar_touch_out1, sar_touch_thres1, true) + (2, 2, sar_touch_out2, sar_touch_thres2, true) + (3, 15, sar_touch_out2, sar_touch_thres2, true) + (4, 13, sar_touch_out3, sar_touch_thres3, true) + (5, 12, sar_touch_out3, sar_touch_thres3, true) + (6, 14, sar_touch_out4, sar_touch_thres4, true) + (7, 27, sar_touch_out4, sar_touch_thres4, true) // --- - (8, 33, 8, sar_touch_out5, touch_meas_out8, sar_touch_thres5, touch_out_th8, false) - (9, 32, 9, sar_touch_out5, touch_meas_out9, sar_touch_thres5, touch_out_th9, false) + (8, 33, sar_touch_out5, sar_touch_thres5, false) + (9, 32, sar_touch_out5, sar_touch_thres5, false) } #[derive(Clone, Copy)] @@ -611,13 +789,13 @@ pub(crate) enum InterruptStatusRegisterAccess { impl InterruptStatusRegisterAccess { pub(crate) fn interrupt_status_read(self) -> u32 { match self { - Self::Bank0 => match crate::get_core() { - crate::Cpu::ProCpu => unsafe { &*GPIO::PTR }.pcpu_int().read().bits(), - crate::Cpu::AppCpu => unsafe { &*GPIO::PTR }.acpu_int().read().bits(), + Self::Bank0 => match Cpu::current() { + Cpu::ProCpu => unsafe { GPIO::steal() }.pcpu_int().read().bits(), + Cpu::AppCpu => unsafe { GPIO::steal() }.acpu_int().read().bits(), }, - Self::Bank1 => match crate::get_core() { - crate::Cpu::ProCpu => unsafe { &*GPIO::PTR }.pcpu_int1().read().bits(), - crate::Cpu::AppCpu => unsafe { &*GPIO::PTR }.acpu_int1().read().bits(), + Self::Bank1 => match Cpu::current() { + Cpu::ProCpu => unsafe { GPIO::steal() }.pcpu_int1().read().bits(), + Cpu::AppCpu => unsafe { GPIO::steal() }.acpu_int1().read().bits(), }, } } diff --git a/esp-hal/src/soc/esp32/mod.rs b/esp-hal/src/soc/esp32/mod.rs index 6ce747d8335..aeeffa81ee8 100644 --- a/esp-hal/src/soc/esp32/mod.rs +++ b/esp-hal/src/soc/esp32/mod.rs @@ -26,6 +26,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf" }; +} + pub use chip; pub(crate) mod constants { @@ -87,7 +93,7 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { addr_of_mut!(_rtc_slow_bss_end), ); if matches!( - crate::reset::get_reset_reason(), + crate::reset::reset_reason(), None | Some(SocResetReason::ChipPowerOn) ) { xtensa_lx_rt::zero_bss( diff --git a/esp-hal/src/soc/esp32/peripherals.rs b/esp-hal/src/soc/esp32/peripherals.rs index c0bd6f2732b..2bfa72fc05e 100644 --- a/esp-hal/src/soc/esp32/peripherals.rs +++ b/esp-hal/src/soc/esp32/peripherals.rs @@ -20,58 +20,97 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - ADC2 <= virtual, - AES <= AES, - APB_CTRL <= APB_CTRL, - BB <= BB, - BT <= virtual, - CPU_CTRL <= virtual, - DAC1 <= virtual, - DAC2 <= virtual, - DMA <= virtual, - EFUSE <= EFUSE, - FLASH_ENCRYPTION <= FLASH_ENCRYPTION, - FRC_TIMER <= FRC_TIMER, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HINF <= HINF, - I2C0 <= I2C0, - I2C1 <= I2C1, - [I2s0] I2S0 <= I2S0 (I2S0), - [I2s1] I2S1 <= I2S1 (I2S1), - IO_MUX <= IO_MUX, - LEDC <= LEDC, - MCPWM0 <= MCPWM0, - MCPWM1 <= MCPWM1, - NRX <= NRX, - PCNT <= PCNT, - PSRAM <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - LPWR <= RTC_CNTL, - RADIO_CLK <= virtual, - RTC_IO <= RTC_IO, - RTC_I2C <= RTC_I2C, - SDHOST <= SDHOST, - SHA <= SHA, - SLC <= SLC, - SLCHOST <= SLCHOST, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2_DMA, SPI2), - [Spi3] SPI3 <= SPI3 (SPI3_DMA, SPI3), - SYSTEM <= DPORT, - SW_INTERRUPT <= virtual, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - TOUCH <= virtual, - [Twai0] TWAI0 <= TWAI0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - [Uart2] UART2 <= UART2, - UHCI0 <= UHCI0, - UHCI1 <= UHCI1, - WIFI <= virtual, + peripherals: [ + ADC1 <= virtual, + ADC2 <= virtual, + AES <= AES, + APB_CTRL <= APB_CTRL, + BB <= BB, + BT <= virtual, + CPU_CTRL <= virtual, + DAC1 <= virtual, + DAC2 <= virtual, + DMA <= virtual, + EFUSE <= EFUSE, + FLASH_ENCRYPTION <= FLASH_ENCRYPTION, + FRC_TIMER <= FRC_TIMER, + GPIO_SD <= GPIO_SD, + HINF <= HINF, + I2C0 <= I2C0, + I2C1 <= I2C1, + I2S0 <= I2S0 (I2S0), + I2S1 <= I2S1 (I2S1), + IO_MUX <= IO_MUX, + LEDC <= LEDC, + MCPWM0 <= MCPWM0, + MCPWM1 <= MCPWM1, + NRX <= NRX, + PCNT <= PCNT, + PSRAM <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + LPWR <= RTC_CNTL, + RADIO_CLK <= virtual, + RTC_IO <= RTC_IO, + RTC_I2C <= RTC_I2C, + SDHOST <= SDHOST, + SHA <= SHA, + SLC <= SLC, + SLCHOST <= SLCHOST, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2_DMA, SPI2), + SPI3 <= SPI3 (SPI3_DMA, SPI3), + SYSTEM <= DPORT, + SW_INTERRUPT <= virtual, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TOUCH <= virtual, + TWAI0 <= TWAI0, + UART0 <= UART0, + UART1 <= UART1, + UART2 <= UART2, + UHCI0 <= UHCI0, + UHCI1 <= UHCI1, + WIFI <= virtual, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_TX_CLK) (1 => CLK_OUT1)) + (1, [Input, Output] (5 => EMAC_RXD2) (0 => U0TXD 1 => CLK_OUT3)) + (2, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIWP 3 => HS2_DATA0 4 => SD_DATA0) (3 => HS2_DATA0 4 => SD_DATA0)) + (3, [Input, Output] (0 => U0RXD) (1 => CLK_OUT2)) + (4, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIHD 3 => HS2_DATA1 4 => SD_DATA1 5 => EMAC_TX_ER) (3 => HS2_DATA1 4 => SD_DATA1)) + (5, [Input, Output] (1 => VSPICS0 3 => HS1_DATA6 5 => EMAC_RX_CLK) (3 => HS1_DATA6)) + (6, [Input, Output] (4 => U1CTS) (0 => SD_CLK 1 => SPICLK 3 => HS1_CLK)) + (7, [Input, Output] (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0) (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0 4 => U2RTS)) + (8, [Input, Output] (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1 4 => U2CTS) (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1)) + (9, [Input, Output] (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2 4 => U1RXD) (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2)) + (10, [Input, Output] ( 0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3) (0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3 4 => U1TXD)) + (11, [Input, Output] ( 1 => SPICS0) (0 => SD_CMD 1 => SPICS0 3 => HS1_CMD 4 => U1RTS)) + (12, [Input, Output, Analog, RtcIo, Touch] (0 => MTDI 1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2) (1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2 5 => EMAC_TXD3)) + (13, [Input, Output, Analog, RtcIo, Touch] (0 => MTCK 1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3) (1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3 5 => EMAC_RX_ER)) + (14, [Input, Output, Analog, RtcIo, Touch] (0 => MTMS 1 => HSPICLK) (1 => HSPICLK 3 => HS2_CLK 4 => SD_CLK 5 => EMAC_TXD2)) + (15, [Input, Output, Analog, RtcIo, Touch] (1 => HSPICS0 5 => EMAC_RXD3) (0 => MTDO 1 => HSPICS0 3 => HS2_CMD 4 => SD_CMD)) + (16, [Input, Output] (3 => HS1_DATA4 4 => U2RXD) (3 => HS1_DATA4 5 => EMAC_CLK_OUT)) + (17, [Input, Output] (3 => HS1_DATA5) (3 => HS1_DATA5 4 => U2TXD 5 => EMAC_CLK_180)) + (18, [Input, Output] (1 => VSPICLK 3 => HS1_DATA7) (1 => VSPICLK 3 => HS1_DATA7)) + (19, [Input, Output] (1 => VSPIQ 3 => U0CTS) (1 => VSPIQ 5 => EMAC_TXD0)) + (20, [Input, Output]) + (21, [Input, Output] (1 => VSPIHD) (1 => VSPIHD 5 => EMAC_TX_EN)) + (22, [Input, Output] (1 => VSPIWP) (1 => VSPIWP 3 => U0RTS 5 => EMAC_TXD1)) + (23, [Input, Output] (1 => VSPID) (1 => VSPID 3 => HS1_STROBE)) + (24, [Input, Output]) + (25, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD0) ()) + (26, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD1) ()) + (27, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_RX_DV) ()) + (32, [Input, Output, Analog, RtcIo, Touch]) + (33, [Input, Output, Analog, RtcIo, Touch]) + (34, [Input, Analog, RtcIoInput]) + (35, [Input, Analog, RtcIoInput]) + (36, [Input, Analog, RtcIoInput]) + (37, [Input, Analog, RtcIoInput]) + (38, [Input, Analog, RtcIoInput]) + (39, [Input, Analog, RtcIoInput]) + ] } diff --git a/esp-hal/src/soc/esp32/psram.rs b/esp-hal/src/soc/esp32/psram.rs index 3e46c79f91a..7843d1cc171 100644 --- a/esp-hal/src/soc/esp32/psram.rs +++ b/esp-hal/src/soc/esp32/psram.rs @@ -304,7 +304,7 @@ pub(crate) mod utils { #[ram] pub(crate) fn psram_init(config: &PsramConfig) { - let chip = crate::efuse::Efuse::get_chip_type(); + let chip = crate::efuse::Efuse::chip_type(); let mode = config.cache_speed; let mut psram_io = PsramIo::default(); @@ -1061,7 +1061,7 @@ pub(crate) mod utils { fn configure_gpio(gpio: u8, field: Field, bits: u8) { unsafe { - let ptr = crate::gpio::get_io_mux_reg(gpio); + let ptr = crate::gpio::io_mux_reg(gpio); ptr.modify(|_, w| apply_to_field!(w, field, bits)); } } diff --git a/esp-hal/src/soc/esp32c2/efuse/mod.rs b/esp-hal/src/soc/esp32c2/efuse/mod.rs index 3aeb5fc0a94..124d3bb29d5 100644 --- a/esp-hal/src/soc/esp32c2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c2/efuse/mod.rs @@ -21,12 +21,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -56,19 +54,19 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } /// Get efuse block version /// /// see - pub fn get_block_version() -> (u8, u8) { + pub fn block_version() -> (u8, u8) { // see // ( @@ -80,8 +78,8 @@ impl Efuse { /// Get version of RTC calibration block /// /// see - pub fn get_rtc_calib_version() -> u8 { - let (major, _minor) = Self::get_block_version(); + pub fn rtc_calib_version() -> u8 { + let (major, _minor) = Self::block_version(); if major == 0 { 1 } else { @@ -92,8 +90,8 @@ impl Efuse { /// Get ADC initial code for specified attenuation from efuse /// /// see - pub fn get_rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; @@ -121,7 +119,7 @@ impl Efuse { /// Get ADC reference point voltage for specified attenuation in millivolts /// /// see - pub fn get_rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { + pub fn rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { match atten { Attenuation::Attenuation0dB => 400, Attenuation::Attenuation11dB => 1370, @@ -131,8 +129,8 @@ impl Efuse { /// Get ADC reference point digital code for specified attenuation /// /// see - pub fn get_rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; diff --git a/esp-hal/src/soc/esp32c2/gpio.rs b/esp-hal/src/soc/esp32c2/gpio.rs index 81d3a0463df..4416b324c4a 100644 --- a/esp-hal/src/soc/esp32c2/gpio.rs +++ b/esp-hal/src/soc/esp32c2/gpio.rs @@ -8,8 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static -//! crate::peripherals::io_mux::GPIO`: +//! - `io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO`: //! * Returns the IO_MUX register for the specified GPIO pin number. //! - `gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8`: //! * This function enables or disables GPIO interrupts and Non-Maskable @@ -45,6 +44,7 @@ pub const NUM_PINS: usize = 21; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u8; pub(crate) type OutputSignalType = u8; pub(crate) const OUTPUT_SIGNAL_MAX: u8 = 128; pub(crate) const INPUT_SIGNAL_MAX: u8 = 100; @@ -54,7 +54,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x1f; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { unsafe { (*crate::peripherals::IO_MUX::PTR).gpio(gpio_num as usize) } } @@ -64,7 +64,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { SPIQ = 0, @@ -104,7 +105,8 @@ pub enum InputSignal { /// Peripheral output signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { SPIQ = 0, @@ -171,24 +173,48 @@ pub enum OutputSignal { GPIO = 128, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (2 => FSPIWP)) - (6, [Input, Output] (2 => FSPICLK) (2 => FSPICLK_MUX)) - (7, [Input, Output] (2 => FSPID) (2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) - (18, [Input, Output]) - (19, [Input, Output]) - (20, [Input, Output] (0 => U0RXD) ()) +macro_rules! rtc_pins { + ( $( $pin_num:expr )+ ) => { + $( + impl $crate::gpio::RtcPin for GpioPin<$pin_num> { + unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { + let rtc_cntl = unsafe { $crate::peripherals::RTC_CNTL::steal() }; + let gpio_wakeup = rtc_cntl.cntl_gpio_wakeup(); + paste::paste! { + gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); + gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); + } + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_CNTL::steal() } + .pad_hold().modify(|_, w| w.[< gpio_pin $pin_num _hold >]().bit(enable)); + } + } + } + )+ + }; +} + +impl crate::gpio::RtcPinWithResistors for GpioPin +where + Self: crate::gpio::RtcPin, +{ + fn rtcio_pullup(&mut self, enable: bool) { + unsafe { crate::peripherals::IO_MUX::steal() } + .gpio(N as usize) + .modify(|_, w| w.fun_wpu().bit(enable)); + } + + fn rtcio_pulldown(&mut self, enable: bool) { + unsafe { crate::peripherals::IO_MUX::steal() } + .gpio(N as usize) + .modify(|_, w| w.fun_wpd().bit(enable)); + } } -crate::rtc_pins! { +rtc_pins! { 0 1 2 diff --git a/esp-hal/src/soc/esp32c2/mod.rs b/esp-hal/src/soc/esp32c2/mod.rs index f479a7e767c..da4a41fa440 100644 --- a/esp-hal/src/soc/esp32c2/mod.rs +++ b/esp-hal/src/soc/esp32c2/mod.rs @@ -19,6 +19,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp8684_technical_reference_manual_en.pdf" }; +} + pub use chip; #[allow(unused)] diff --git a/esp-hal/src/soc/esp32c2/peripherals.rs b/esp-hal/src/soc/esp32c2/peripherals.rs index 310c62416dc..6748f4e6ce5 100644 --- a/esp-hal/src/soc/esp32c2/peripherals.rs +++ b/esp-hal/src/soc/esp32c2/peripherals.rs @@ -20,40 +20,57 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - APB_CTRL <= APB_CTRL, - ASSIST_DEBUG <= ASSIST_DEBUG, - BT <= virtual, - DMA <= DMA (DMA_CH0), - ECC <= ECC, - EFUSE <= EFUSE, - EXTMEM <= EXTMEM, - GPIO <= GPIO (GPIO,GPIO_NMI), - I2C0 <= I2C0, - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - IO_MUX <= IO_MUX, - LEDC <= LEDC, - LPWR <= RTC_CNTL, - RADIO_CLK <= virtual, - RNG <= RNG, - SENSITIVE <= SENSITIVE, - SHA <= SHA, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2), - SYSTEM <= SYSTEM, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TIMG0 <= TIMG0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - WIFI <= virtual, - XTS_AES <= XTS_AES, - MEM2MEM1 <= virtual, - MEM2MEM2 <= virtual, - MEM2MEM3 <= virtual, - MEM2MEM4 <= virtual, - MEM2MEM5 <= virtual, - MEM2MEM6 <= virtual, - MEM2MEM8 <= virtual, + peripherals: [ + ADC1 <= virtual, + APB_CTRL <= APB_CTRL, + ASSIST_DEBUG <= ASSIST_DEBUG, + BT <= virtual, + DMA <= DMA (DMA_CH0), + ECC <= ECC, + EFUSE <= EFUSE, + EXTMEM <= EXTMEM, + I2C0 <= I2C0, + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + IO_MUX <= IO_MUX, + LEDC <= LEDC, + LPWR <= RTC_CNTL, + RADIO_CLK <= virtual, + RNG <= RNG, + SENSITIVE <= SENSITIVE, + SHA <= SHA, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2), + SYSTEM <= SYSTEM, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TIMG0 <= TIMG0, + UART0 <= UART0, + UART1 <= UART1, + WIFI <= virtual, + XTS_AES <= XTS_AES, + MEM2MEM1 <= virtual, + MEM2MEM2 <= virtual, + MEM2MEM3 <= virtual, + MEM2MEM4 <= virtual, + MEM2MEM5 <= virtual, + MEM2MEM6 <= virtual, + MEM2MEM8 <= virtual, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo]) + (1, [Input, Output, Analog, RtcIo]) + (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) + (3, [Input, Output, Analog, RtcIo]) + (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (2 => FSPIHD)) + (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (2 => FSPIWP)) + (6, [Input, Output] (2 => FSPICLK) (2 => FSPICLK_MUX)) + (7, [Input, Output] (2 => FSPID) (2 => FSPID)) + (8, [Input, Output]) + (9, [Input, Output]) + (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) + (18, [Input, Output]) + (19, [Input, Output]) + (20, [Input, Output] (0 => U0RXD) ()) + ] } diff --git a/esp-hal/src/soc/esp32c3/efuse/mod.rs b/esp-hal/src/soc/esp32c3/efuse/mod.rs index f1f3b0e2fb5..d7f62114e9b 100644 --- a/esp-hal/src/soc/esp32c3/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c3/efuse/mod.rs @@ -22,12 +22,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -57,19 +55,19 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } /// Get efuse block version /// /// see - pub fn get_block_version() -> (u8, u8) { + pub fn block_version() -> (u8, u8) { // see // // @@ -82,8 +80,8 @@ impl Efuse { /// Get version of RTC calibration block /// /// see - pub fn get_rtc_calib_version() -> u8 { - let (major, _minor) = Self::get_block_version(); + pub fn rtc_calib_version() -> u8 { + let (major, _minor) = Self::block_version(); if major == 1 { 1 } else { @@ -94,8 +92,8 @@ impl Efuse { /// Get ADC initial code for specified attenuation from efuse /// /// see - pub fn get_rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; @@ -115,7 +113,7 @@ impl Efuse { /// Get ADC reference point voltage for specified attenuation in millivolts /// /// see - pub fn get_rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { + pub fn rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { match atten { Attenuation::Attenuation0dB => 400, Attenuation::Attenuation2p5dB => 550, @@ -127,8 +125,8 @@ impl Efuse { /// Get ADC reference point digital code for specified attenuation /// /// see - pub fn get_rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; diff --git a/esp-hal/src/soc/esp32c3/gpio.rs b/esp-hal/src/soc/esp32c3/gpio.rs index edef675d1b6..11b99a08534 100644 --- a/esp-hal/src/soc/esp32c3/gpio.rs +++ b/esp-hal/src/soc/esp32c3/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static +//! - `io_mux_reg(gpio_num: u8) -> &'static //! crate::peripherals::io_mux::GPIO0:`: //! * Returns the IO_MUX register for the specified GPIO pin number. //! - `gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8`: @@ -46,6 +46,7 @@ pub const NUM_PINS: usize = 22; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u8; pub(crate) type OutputSignalType = u8; pub(crate) const OUTPUT_SIGNAL_MAX: u8 = 128; pub(crate) const INPUT_SIGNAL_MAX: u8 = 100; @@ -55,7 +56,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x1f; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { unsafe { (*crate::peripherals::IO_MUX::PTR).gpio(gpio_num as usize) } } @@ -65,7 +66,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { SPIQ = 0, @@ -114,7 +116,8 @@ pub enum InputSignal { /// Peripheral output signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { SPIQ = 0, @@ -198,33 +201,50 @@ pub enum OutputSignal { GPIO = 128, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) - (6, [Input, Output] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) - (7, [Input, Output] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) - (11, [Input, Output]) - (12, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (13, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (14, [Input, Output] () (0 => SPICS0)) - (15, [Input, Output] () (0 => SPICLK_MUX)) - (16, [Input, Output] (0 => SPID) (0 => SPID)) - (17, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (18, [Input, Output]) - (19, [Input, Output]) - (20, [Input, Output] (0 => U0RXD) ()) - (21, [Input, Output] () (0 => U0TXD)) +macro_rules! rtc_pins { + ( $( $pin_num:expr )+ ) => { + $( + impl $crate::gpio::RtcPin for GpioPin<$pin_num> { + unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { + let rtc_cntl = unsafe { $crate::peripherals::RTC_CNTL::steal() }; + let gpio_wakeup = rtc_cntl.gpio_wakeup(); + + paste::paste! { + gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _wakeup_enable >]().bit(wakeup)); + gpio_wakeup.modify(|_, w| w.[< gpio_pin $pin_num _int_type >]().bits(level)); + } + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_CNTL::steal() } + .pad_hold().modify(|_, w| w.[< gpio_pin $pin_num _hold >]().bit(enable)); + } + } + } + )+ + }; +} + +impl crate::gpio::RtcPinWithResistors for GpioPin +where + Self: crate::gpio::RtcPin, +{ + fn rtcio_pullup(&mut self, enable: bool) { + unsafe { crate::peripherals::IO_MUX::steal() } + .gpio(N as usize) + .modify(|_, w| w.fun_wpu().bit(enable)); + } + + fn rtcio_pulldown(&mut self, enable: bool) { + unsafe { crate::peripherals::IO_MUX::steal() } + .gpio(N as usize) + .modify(|_, w| w.fun_wpd().bit(enable)); + } } // RTC pins 0 through 5 (inclusive) support GPIO wakeup -crate::rtc_pins! { +rtc_pins! { 0 1 2 diff --git a/esp-hal/src/soc/esp32c3/mod.rs b/esp-hal/src/soc/esp32c3/mod.rs index 0b3d43f1b60..6955b5c78c7 100644 --- a/esp-hal/src/soc/esp32c3/mod.rs +++ b/esp-hal/src/soc/esp32c3/mod.rs @@ -23,6 +23,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf" }; +} + pub use chip; #[allow(unused)] diff --git a/esp-hal/src/soc/esp32c3/peripherals.rs b/esp-hal/src/soc/esp32c3/peripherals.rs index 12296386d6a..f4a52d4fc08 100644 --- a/esp-hal/src/soc/esp32c3/peripherals.rs +++ b/esp-hal/src/soc/esp32c3/peripherals.rs @@ -20,45 +20,70 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - ADC2 <= virtual, - AES <= AES, - APB_CTRL <= APB_CTRL, - ASSIST_DEBUG <= ASSIST_DEBUG, - BT <= virtual, - DMA <= DMA (DMA_CH0,DMA_CH1,DMA_CH2), - DS <= DS, - EFUSE <= EFUSE, - EXTMEM <= EXTMEM, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HMAC <= HMAC, - I2C0 <= I2C0, - [I2s0] I2S0 <= I2S0 (I2S0), - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - IO_MUX <= IO_MUX, - LEDC <= LEDC, - LPWR <= RTC_CNTL, - RADIO_CLK <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - SENSITIVE <= SENSITIVE, - SHA <= SHA, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2), - SYSTEM <= SYSTEM, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - [Twai0] TWAI0 <= TWAI0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - UHCI0 <= UHCI0, - UHCI1 <= UHCI1, - USB_DEVICE <= USB_DEVICE, - WIFI <= virtual, - XTS_AES <= XTS_AES, + peripherals: [ + ADC1 <= virtual, + ADC2 <= virtual, + AES <= AES, + APB_CTRL <= APB_CTRL, + ASSIST_DEBUG <= ASSIST_DEBUG, + BT <= virtual, + DMA <= DMA (DMA_CH0,DMA_CH1,DMA_CH2), + DS <= DS, + EFUSE <= EFUSE, + EXTMEM <= EXTMEM, + GPIO_SD <= GPIO_SD, + HMAC <= HMAC, + I2C0 <= I2C0, + I2S0 <= I2S0 (I2S0), + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + IO_MUX <= IO_MUX, + LEDC <= LEDC, + LPWR <= RTC_CNTL, + RADIO_CLK <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + SENSITIVE <= SENSITIVE, + SHA <= SHA, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2), + SYSTEM <= SYSTEM, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TWAI0 <= TWAI0, + UART0 <= UART0, + UART1 <= UART1, + UHCI0 <= UHCI0, + UHCI1 <= UHCI1, + USB_DEVICE <= USB_DEVICE, + WIFI <= virtual, + XTS_AES <= XTS_AES, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo]) + (1, [Input, Output, Analog, RtcIo]) + (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) + (3, [Input, Output, Analog, RtcIo]) + (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) + (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) + (6, [Input, Output] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) + (7, [Input, Output] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) + (8, [Input, Output]) + (9, [Input, Output]) + (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) + (11, [Input, Output]) + (12, [Input, Output] (0 => SPIHD) (0 => SPIHD)) + (13, [Input, Output] (0 => SPIWP) (0 => SPIWP)) + (14, [Input, Output] () (0 => SPICS0)) + (15, [Input, Output] () (0 => SPICLK_MUX)) + (16, [Input, Output] (0 => SPID) (0 => SPID)) + (17, [Input, Output] (0 => SPIQ) (0 => SPIQ)) + (18, [Input, Output]) + (19, [Input, Output]) + (20, [Input, Output] (0 => U0RXD) ()) + (21, [Input, Output] () (0 => U0TXD)) + ] } diff --git a/esp-hal/src/soc/esp32c6/efuse/mod.rs b/esp-hal/src/soc/esp32c6/efuse/mod.rs index 0caa975a253..0ec84d7c0e4 100644 --- a/esp-hal/src/soc/esp32c6/efuse/mod.rs +++ b/esp-hal/src/soc/esp32c6/efuse/mod.rs @@ -22,12 +22,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -57,19 +55,19 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } /// Get efuse block version /// /// see - pub fn get_block_version() -> (u8, u8) { + pub fn block_version() -> (u8, u8) { // see // ( @@ -81,8 +79,8 @@ impl Efuse { /// Get version of RTC calibration block /// /// see - pub fn get_rtc_calib_version() -> u8 { - let (_major, minor) = Self::get_block_version(); + pub fn rtc_calib_version() -> u8 { + let (_major, minor) = Self::block_version(); if minor >= 1 { 1 } else { @@ -93,8 +91,8 @@ impl Efuse { /// Get ADC initial code for specified attenuation from efuse /// /// see - pub fn get_rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_init_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; @@ -114,7 +112,7 @@ impl Efuse { /// Get ADC reference point voltage for specified attenuation in millivolts /// /// see - pub fn get_rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { + pub fn rtc_calib_cal_mv(_unit: u8, atten: Attenuation) -> u16 { match atten { Attenuation::Attenuation0dB => 400, Attenuation::Attenuation2p5dB => 550, @@ -126,8 +124,8 @@ impl Efuse { /// Get ADC reference point digital code for specified attenuation /// /// see - pub fn get_rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_cal_code(_unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; diff --git a/esp-hal/src/soc/esp32c6/gpio.rs b/esp-hal/src/soc/esp32c6/gpio.rs index 5844ec08eb8..1ae8f62b86d 100644 --- a/esp-hal/src/soc/esp32c6/gpio.rs +++ b/esp-hal/src/soc/esp32c6/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static +//! - `io_mux_reg(gpio_num: u8) -> &'static //! crate::peripherals::io_mux::GPIO0:`: //! * Returns the IO_MUX register for the specified GPIO pin number. //! - `gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8`: @@ -46,6 +46,7 @@ pub const NUM_PINS: usize = 31; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u8; pub(crate) type OutputSignalType = u8; pub(crate) const OUTPUT_SIGNAL_MAX: u8 = 128; pub(crate) const INPUT_SIGNAL_MAX: u8 = 124; @@ -55,7 +56,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x3c; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { unsafe { (*crate::peripherals::IO_MUX::PTR).gpio(gpio_num as usize) } } @@ -65,7 +66,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { EXT_ADC_START = 0, @@ -169,7 +171,8 @@ pub enum InputSignal { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { LEDC_LS_SIG0 = 0, @@ -284,40 +287,6 @@ pub enum OutputSignal { GPIO = 128, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) - (6, [Input, Output, Analog, RtcIo] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) - (7, [Input, Output, Analog, RtcIo] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output]) - (11, [Input, Output]) - (12, [Input, Output]) - (13, [Input, Output]) - (14, [Input, Output]) - (15, [Input, Output]) - (16, [Input, Output] (0 => U0RXD) (2 => FSPICS0)) - (17, [Input, Output] () (0 => U0TXD 2 => FSPICS1)) - (18, [Input, Output] () (2 => FSPICS2)) // 0 => SDIO_CMD but there are no signals since it's a fixed pin - (19, [Input, Output] () (2 => FSPICS3)) // 0 => SDIO_CLK but there are no signals since it's a fixed pin - (20, [Input, Output] () (2 => FSPICS4)) // 0 => SDIO_DATA0 but there are no signals since it's a fixed pin - (21, [Input, Output] () (2 => FSPICS5)) // 0 => SDIO_DATA1 but there are no signals since it's a fixed pin - (22, [Input, Output] () ()) // 0 => SDIO_DATA2 but there are no signals since it's a fixed pin - (23, [Input, Output] () ()) // 0 => SDIO_DATA3 but there are no signals since it's a fixed pin - (24, [Input, Output] () (0 => SPICS0)) - (25, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (26, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (27, [Input, Output]) - (28, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (29, [Input, Output] () (0 => SPICLK_MUX)) - (30, [Input, Output] (0 => SPID) (0 => SPID)) -} - crate::lp_gpio! { 0 1 diff --git a/esp-hal/src/soc/esp32c6/lp_core.rs b/esp-hal/src/soc/esp32c6/lp_core.rs index 151e47268f1..ac7674071fc 100644 --- a/esp-hal/src/soc/esp32c6/lp_core.rs +++ b/esp-hal/src/soc/esp32c6/lp_core.rs @@ -63,12 +63,12 @@ impl<'d> LpCore<'d> { LpCoreClockSource::RcFastClk => unsafe { (*crate::soc::peripherals::LP_CLKRST::PTR) .lp_clk_conf() - .modify(|_, w| w.fast_clk_sel().clear_bit()) + .modify(|_, w| w.fast_clk_sel().clear_bit()); }, LpCoreClockSource::XtalD2Clk => unsafe { (*crate::soc::peripherals::LP_CLKRST::PTR) .lp_clk_conf() - .modify(|_, w| w.fast_clk_sel().set_bit()) + .modify(|_, w| w.fast_clk_sel().set_bit()); }, } diff --git a/esp-hal/src/soc/esp32c6/mod.rs b/esp-hal/src/soc/esp32c6/mod.rs index 461b5e55163..1ed7e74db87 100644 --- a/esp-hal/src/soc/esp32c6/mod.rs +++ b/esp-hal/src/soc/esp32c6/mod.rs @@ -25,6 +25,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf" }; +} + pub use chip; #[allow(unused)] diff --git a/esp-hal/src/soc/esp32c6/peripherals.rs b/esp-hal/src/soc/esp32c6/peripherals.rs index 8c5e2de92c7..b521161faf9 100644 --- a/esp-hal/src/soc/esp32c6/peripherals.rs +++ b/esp-hal/src/soc/esp32c6/peripherals.rs @@ -20,81 +20,115 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - AES <= AES, - ASSIST_DEBUG <= ASSIST_DEBUG, - ATOMIC <= ATOMIC, - BT <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), - DS <= DS, - ECC <= ECC, - EFUSE <= EFUSE, - EXTMEM <= EXTMEM, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HINF <= HINF, - HMAC <= HMAC, - HP_APM <= HP_APM, - HP_SYS <= HP_SYS, - I2C0 <= I2C0, - [I2s0] I2S0 <= I2S0 (I2S0), - IEEE802154 <= IEEE802154, - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - INTPRI <= INTPRI, - IO_MUX <= IO_MUX, - LEDC <= LEDC, - LPWR <= LP_CLKRST, - LP_CORE <= virtual, - LP_PERI <= LP_PERI, - LP_ANA <= LP_ANA, - LP_AON <= LP_AON, - LP_APM <= LP_APM, - LP_APM0 <= LP_APM0, - LP_I2C0 <= LP_I2C0, - LP_I2C_ANA_MST <= LP_I2C_ANA_MST, - LP_IO <= LP_IO, - LP_TEE <= LP_TEE, - LP_TIMER <= LP_TIMER, - LP_UART <= LP_UART, - LP_WDT <= LP_WDT, - MCPWM0 <= MCPWM0, - MEM_MONITOR <= MEM_MONITOR, - OTP_DEBUG <= OTP_DEBUG, - PARL_IO <= PARL_IO (PARL_IO), - PAU <= PAU, - PCNT <= PCNT, - PMU <= PMU, - RADIO_CLK <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - SHA <= SHA, - SLCHOST <= SLCHOST, - SOC_ETM <= SOC_ETM, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2), - SYSTEM <= PCR, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TEE <= TEE, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - TRACE0 <= TRACE, - [Twai0] TWAI0 <= TWAI0, - [Twai1] TWAI1 <= TWAI1, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - UHCI0 <= UHCI0, - USB_DEVICE <= USB_DEVICE, - WIFI <= virtual, - MEM2MEM1 <= virtual, - MEM2MEM4 <= virtual, - MEM2MEM5 <= virtual, - MEM2MEM10 <= virtual, - MEM2MEM11 <= virtual, - MEM2MEM12 <= virtual, - MEM2MEM13 <= virtual, - MEM2MEM14 <= virtual, - MEM2MEM15 <= virtual, + peripherals: [ + ADC1 <= virtual, + AES <= AES, + ASSIST_DEBUG <= ASSIST_DEBUG, + ATOMIC <= ATOMIC, + BT <= virtual, + DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), + DS <= DS, + ECC <= ECC, + EFUSE <= EFUSE, + EXTMEM <= EXTMEM, + GPIO_SD <= GPIO_SD, + HINF <= HINF, + HMAC <= HMAC, + HP_APM <= HP_APM, + HP_SYS <= HP_SYS, + I2C0 <= I2C0, + I2S0 <= I2S0 (I2S0), + IEEE802154 <= IEEE802154, + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + INTPRI <= INTPRI, + IO_MUX <= IO_MUX, + LEDC <= LEDC, + LPWR <= LP_CLKRST, + LP_CORE <= virtual, + LP_PERI <= LP_PERI, + LP_ANA <= LP_ANA, + LP_AON <= LP_AON, + LP_APM <= LP_APM, + LP_APM0 <= LP_APM0, + LP_I2C0 <= LP_I2C0, + LP_I2C_ANA_MST <= LP_I2C_ANA_MST, + LP_IO <= LP_IO, + LP_TEE <= LP_TEE, + LP_TIMER <= LP_TIMER, + LP_UART <= LP_UART, + LP_WDT <= LP_WDT, + MCPWM0 <= MCPWM0, + MEM_MONITOR <= MEM_MONITOR, + OTP_DEBUG <= OTP_DEBUG, + PARL_IO <= PARL_IO (PARL_IO), + PAU <= PAU, + PCNT <= PCNT, + PMU <= PMU, + RADIO_CLK <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + SHA <= SHA, + SLCHOST <= SLCHOST, + SOC_ETM <= SOC_ETM, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2), + SYSTEM <= PCR, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TEE <= TEE, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TRACE0 <= TRACE, + TWAI0 <= TWAI0, + TWAI1 <= TWAI1, + UART0 <= UART0, + UART1 <= UART1, + UHCI0 <= UHCI0, + USB_DEVICE <= USB_DEVICE, + WIFI <= virtual, + MEM2MEM1 <= virtual, + MEM2MEM4 <= virtual, + MEM2MEM5 <= virtual, + MEM2MEM10 <= virtual, + MEM2MEM11 <= virtual, + MEM2MEM12 <= virtual, + MEM2MEM13 <= virtual, + MEM2MEM14 <= virtual, + MEM2MEM15 <= virtual, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo]) + (1, [Input, Output, Analog, RtcIo]) + (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) + (3, [Input, Output, Analog, RtcIo]) + (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) + (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) + (6, [Input, Output, Analog, RtcIo] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) + (7, [Input, Output, Analog, RtcIo] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) + (8, [Input, Output]) + (9, [Input, Output]) + (10, [Input, Output]) + (11, [Input, Output]) + (12, [Input, Output]) + (13, [Input, Output]) + (14, [Input, Output]) + (15, [Input, Output]) + (16, [Input, Output] (0 => U0RXD) (2 => FSPICS0)) + (17, [Input, Output] () (0 => U0TXD 2 => FSPICS1)) + (18, [Input, Output] () (2 => FSPICS2)) // 0 => SDIO_CMD but there are no signals since it's a fixed pin + (19, [Input, Output] () (2 => FSPICS3)) // 0 => SDIO_CLK but there are no signals since it's a fixed pin + (20, [Input, Output] () (2 => FSPICS4)) // 0 => SDIO_DATA0 but there are no signals since it's a fixed pin + (21, [Input, Output] () (2 => FSPICS5)) // 0 => SDIO_DATA1 but there are no signals since it's a fixed pin + (22, [Input, Output] () ()) // 0 => SDIO_DATA2 but there are no signals since it's a fixed pin + (23, [Input, Output] () ()) // 0 => SDIO_DATA3 but there are no signals since it's a fixed pin + (24, [Input, Output] () (0 => SPICS0)) + (25, [Input, Output] (0 => SPIQ) (0 => SPIQ)) + (26, [Input, Output] (0 => SPIWP) (0 => SPIWP)) + (27, [Input, Output]) + (28, [Input, Output] (0 => SPIHD) (0 => SPIHD)) + (29, [Input, Output] () (0 => SPICLK_MUX)) + (30, [Input, Output] (0 => SPID) (0 => SPID)) + ] } diff --git a/esp-hal/src/soc/esp32h2/efuse/mod.rs b/esp-hal/src/soc/esp32h2/efuse/mod.rs index 73bdcd57b31..d0ec6794f33 100644 --- a/esp-hal/src/soc/esp32h2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32h2/efuse/mod.rs @@ -22,12 +22,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -57,12 +55,12 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } } diff --git a/esp-hal/src/soc/esp32h2/gpio.rs b/esp-hal/src/soc/esp32h2/gpio.rs index 44d7c4ce6cd..e1931c1371a 100644 --- a/esp-hal/src/soc/esp32h2/gpio.rs +++ b/esp-hal/src/soc/esp32h2/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static +//! - `io_mux_reg(gpio_num: u8) -> &'static //! crate::peripherals::io_mux::GPIO0:`: //! * Returns the IO_MUX register for the specified GPIO pin number. //! - `gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8`: @@ -36,10 +36,7 @@ //! registers for both the `PRO CPU` and `APP CPU`. The implementation uses the //! `gpio` peripheral to access the appropriate registers. -use crate::{ - gpio::{AlternateFunction, GpioPin}, - peripherals::GPIO, -}; +use crate::{gpio::AlternateFunction, peripherals::GPIO}; // https://github.com/espressif/esp-idf/blob/df9310a/components/soc/esp32h2/gpio_periph.c#L42 /// The total number of GPIO pins available. @@ -47,6 +44,7 @@ pub const NUM_PINS: usize = 28; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u8; pub(crate) type OutputSignalType = u8; pub(crate) const OUTPUT_SIGNAL_MAX: u8 = 128; pub(crate) const INPUT_SIGNAL_MAX: u8 = 124; @@ -56,7 +54,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x3c; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { unsafe { (*crate::peripherals::IO_MUX::PTR).gpio(gpio_num as usize) } } @@ -66,7 +64,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { EXT_ADC_START = 0, @@ -151,7 +150,8 @@ pub enum InputSignal { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { LEDC_LS_SIG0 = 0, @@ -249,37 +249,6 @@ pub enum OutputSignal { GPIO = 128, } -crate::gpio! { - (0, [Input, Output, Analog] (2 => FSPIQ) (2 => FSPIQ)) - (1, [Input, Output, Analog] (2 => FSPICS0) (2 => FSPICS0)) - (2, [Input, Output, Analog] (2 => FSPIWP) (2 => FSPIWP)) - (3, [Input, Output, Analog] (2 => FSPIHD) (2 => FSPIHD)) - (4, [Input, Output, Analog] (2 => FSPICLK) (2 => FSPICLK_MUX)) - (5, [Input, Output, Analog] (2 => FSPID) (2 => FSPID)) - (6, [Input, Output]) - (7, [Input, Output]) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output]) - (11, [Input, Output]) - (12, [Input, Output]) - (13, [Input, Output]) - (14, [Input, Output]) - (15, [Input, Output] () (0 => SPICS0)) - (16, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (17, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (18, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (19, [Input, Output] () (0 => SPICLK)) - (20, [Input, Output] (0 => SPID) (0 => SPID)) - (21, [Input, Output]) - (22, [Input, Output]) - (23, [Input, Output] () (2 => FSPICS1)) - (24, [Input, Output] () (2 => FSPICS2)) - (25, [Input, Output] () (2 => FSPICS3)) - (26, [Input, Output] () (2 => FSPICS4)) - (27, [Input, Output] () (2 => FSPICS5)) -} - #[derive(Clone, Copy)] pub(crate) enum InterruptStatusRegisterAccess { Bank0, diff --git a/esp-hal/src/soc/esp32h2/mod.rs b/esp-hal/src/soc/esp32h2/mod.rs index d87720b4247..9997f73fb2e 100644 --- a/esp-hal/src/soc/esp32h2/mod.rs +++ b/esp-hal/src/soc/esp32h2/mod.rs @@ -24,6 +24,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32-h2_technical_reference_manual_en.pdf" }; +} + pub use chip; #[allow(unused)] diff --git a/esp-hal/src/soc/esp32h2/peripherals.rs b/esp-hal/src/soc/esp32h2/peripherals.rs index 37eb1285f68..9f2c0cdb7df 100644 --- a/esp-hal/src/soc/esp32h2/peripherals.rs +++ b/esp-hal/src/soc/esp32h2/peripherals.rs @@ -20,71 +20,102 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - AES <= AES, - ASSIST_DEBUG <= ASSIST_DEBUG, - BT <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), - DS <= DS, - ECC <= ECC, - EFUSE <= EFUSE, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HMAC <= HMAC, - HP_APM <= HP_APM, - HP_SYS <= HP_SYS, - I2C0 <= I2C0, - I2C1 <= I2C1, - [I2s0] I2S0 <= I2S0 (I2S0), - IEEE802154 <= IEEE802154, - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - INTPRI <= INTPRI, - IO_MUX <= IO_MUX, - LEDC <= LEDC, - LPWR <= LP_CLKRST, - LP_ANA <= LP_ANA, - LP_AON <= LP_AON, - LP_APM <= LP_APM, - LP_PERI <= LP_PERI, - LP_TIMER <= LP_TIMER, - LP_WDT <= LP_WDT, - MCPWM0 <= MCPWM0, - MEM_MONITOR <= MEM_MONITOR, - MODEM_LPCON <= MODEM_LPCON, - MODEM_SYSCON <= MODEM_SYSCON, - OTP_DEBUG <= OTP_DEBUG, - PARL_IO <= PARL_IO (PARL_IO_TX, PARL_IO_RX), - PAU <= PAU, - PCNT <= PCNT, - PMU <= PMU, - RADIO_CLK <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - SHA <= SHA, - SOC_ETM <= SOC_ETM, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2), - SYSTEM <= PCR, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TEE <= TEE, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - TRACE0 <= TRACE, - [Twai0] TWAI0 <= TWAI0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - UHCI0 <= UHCI0, - USB_DEVICE <= USB_DEVICE, - MEM2MEM1 <= virtual, - MEM2MEM4 <= virtual, - MEM2MEM5 <= virtual, - MEM2MEM10 <= virtual, - MEM2MEM11 <= virtual, - MEM2MEM12 <= virtual, - MEM2MEM13 <= virtual, - MEM2MEM14 <= virtual, - MEM2MEM15 <= virtual, + peripherals: [ + ADC1 <= virtual, + AES <= AES, + ASSIST_DEBUG <= ASSIST_DEBUG, + BT <= virtual, + DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2), + DS <= DS, + ECC <= ECC, + EFUSE <= EFUSE, + GPIO_SD <= GPIO_SD, + HMAC <= HMAC, + HP_APM <= HP_APM, + HP_SYS <= HP_SYS, + I2C0 <= I2C0, + I2C1 <= I2C1, + I2S0 <= I2S0 (I2S0), + IEEE802154 <= IEEE802154, + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + INTPRI <= INTPRI, + IO_MUX <= IO_MUX, + LEDC <= LEDC, + LPWR <= LP_CLKRST, + LP_ANA <= LP_ANA, + LP_AON <= LP_AON, + LP_APM <= LP_APM, + LP_PERI <= LP_PERI, + LP_TIMER <= LP_TIMER, + LP_WDT <= LP_WDT, + MCPWM0 <= MCPWM0, + MEM_MONITOR <= MEM_MONITOR, + MODEM_LPCON <= MODEM_LPCON, + MODEM_SYSCON <= MODEM_SYSCON, + OTP_DEBUG <= OTP_DEBUG, + PARL_IO <= PARL_IO (PARL_IO_TX, PARL_IO_RX), + PAU <= PAU, + PCNT <= PCNT, + PMU <= PMU, + RADIO_CLK <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + SHA <= SHA, + SOC_ETM <= SOC_ETM, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2), + SYSTEM <= PCR, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TEE <= TEE, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TRACE0 <= TRACE, + TWAI0 <= TWAI0, + UART0 <= UART0, + UART1 <= UART1, + UHCI0 <= UHCI0, + USB_DEVICE <= USB_DEVICE, + MEM2MEM1 <= virtual, + MEM2MEM4 <= virtual, + MEM2MEM5 <= virtual, + MEM2MEM10 <= virtual, + MEM2MEM11 <= virtual, + MEM2MEM12 <= virtual, + MEM2MEM13 <= virtual, + MEM2MEM14 <= virtual, + MEM2MEM15 <= virtual, + ], + pins: [ + (0, [Input, Output, Analog] (2 => FSPIQ) (2 => FSPIQ)) + (1, [Input, Output, Analog] (2 => FSPICS0) (2 => FSPICS0)) + (2, [Input, Output, Analog] (2 => FSPIWP) (2 => FSPIWP)) + (3, [Input, Output, Analog] (2 => FSPIHD) (2 => FSPIHD)) + (4, [Input, Output, Analog] (2 => FSPICLK) (2 => FSPICLK_MUX)) + (5, [Input, Output, Analog] (2 => FSPID) (2 => FSPID)) + (6, [Input, Output]) + (7, [Input, Output]) + (8, [Input, Output]) + (9, [Input, Output]) + (10, [Input, Output]) + (11, [Input, Output]) + (12, [Input, Output]) + (13, [Input, Output]) + (14, [Input, Output]) + (15, [Input, Output] () (0 => SPICS0)) + (16, [Input, Output] (0 => SPIQ) (0 => SPIQ)) + (17, [Input, Output] (0 => SPIWP) (0 => SPIWP)) + (18, [Input, Output] (0 => SPIHD) (0 => SPIHD)) + (19, [Input, Output] () (0 => SPICLK)) + (20, [Input, Output] (0 => SPID) (0 => SPID)) + (21, [Input, Output]) + (22, [Input, Output]) + (23, [Input, Output] () (2 => FSPICS1)) + (24, [Input, Output] () (2 => FSPICS2)) + (25, [Input, Output] () (2 => FSPICS3)) + (26, [Input, Output] () (2 => FSPICS4)) + (27, [Input, Output] () (2 => FSPICS5)) + ] } diff --git a/esp-hal/src/soc/esp32s2/efuse/mod.rs b/esp-hal/src/soc/esp32s2/efuse/mod.rs index 6dd75ceca7c..1f675505f1e 100644 --- a/esp-hal/src/soc/esp32s2/efuse/mod.rs +++ b/esp-hal/src/soc/esp32s2/efuse/mod.rs @@ -24,12 +24,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -59,12 +57,12 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } } diff --git a/esp-hal/src/soc/esp32s2/gpio.rs b/esp-hal/src/soc/esp32s2/gpio.rs index 8a760a95b1f..52c3deee7e9 100644 --- a/esp-hal/src/soc/esp32s2/gpio.rs +++ b/esp-hal/src/soc/esp32s2/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: +//! - `io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0:`: //! * This function returns a reference to the GPIO register associated //! with the given GPIO number. It uses unsafe code and transmutation to //! access the GPIO registers based on the provided GPIO number. @@ -59,6 +59,7 @@ pub const NUM_PINS: usize = 47; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u16; pub(crate) type OutputSignalType = u16; pub(crate) const OUTPUT_SIGNAL_MAX: u16 = 256; pub(crate) const INPUT_SIGNAL_MAX: u16 = 204; @@ -68,7 +69,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x3c; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static io_mux::GPIO0 { unsafe { let iomux = &*IO_MUX::PTR; @@ -126,7 +127,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { SPIQ = 0, @@ -212,7 +214,8 @@ pub enum InputSignal { /// Peripheral output signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum OutputSignal { SPIQ = 0, @@ -314,76 +317,127 @@ pub enum OutputSignal { GPIO = 256, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo]) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo]) - (5, [Input, Output, Analog, RtcIo]) - (6, [Input, Output, Analog, RtcIo]) - (7, [Input, Output, Analog, RtcIo]) - (8, [Input, Output, Analog, RtcIo]) - (9, [Input, Output, Analog, RtcIo]) - (10, [Input, Output, Analog, RtcIo]) - (11, [Input, Output, Analog, RtcIo]) - (12, [Input, Output, Analog, RtcIo]) - (13, [Input, Output, Analog, RtcIo]) - (14, [Input, Output, Analog, RtcIo]) - (15, [Input, Output, Analog, RtcIo]) - (16, [Input, Output, Analog, RtcIo]) - (17, [Input, Output, Analog, RtcIo]) - (18, [Input, Output, Analog, RtcIo]) - (19, [Input, Output, Analog, RtcIo]) - (20, [Input, Output, Analog, RtcIo]) - (21, [Input, Output, Analog, RtcIo]) - - (26, [Input, Output]) - (27, [Input, Output]) - (28, [Input, Output]) - (29, [Input, Output]) - (30, [Input, Output]) - (31, [Input, Output]) - (32, [Input, Output]) - (33, [Input, Output]) - (34, [Input, Output]) - (35, [Input, Output]) - (36, [Input, Output]) - (37, [Input, Output]) - (38, [Input, Output]) - (39, [Input, Output]) - (40, [Input, Output]) - (41, [Input, Output]) - (42, [Input, Output]) - (43, [Input, Output]) - (44, [Input, Output]) - (45, [Input, Output]) - (46, [Input, Output]) +macro_rules! rtcio_analog { + ( @ignore $rue:literal ) => {}; + + ( + $pin_num:expr, $pin_reg:expr, $prefix:pat, $hold:ident + ) => { + impl $crate::gpio::RtcPin for GpioPin<$pin_num> { + fn rtc_number(&self) -> u8 { + $pin_num + } + + /// Set the RTC properties of the pin. If `mux` is true then then pin is + /// routed to RTC, when false it is routed to IO_MUX. + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) { + enable_iomux_clk_gate(); + + // disable input + paste::paste!{ + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_,w| unsafe { + w.[<$prefix fun_ie>]().bit(input_enable); + w.[<$prefix mux_sel>]().bit(mux); + w.[<$prefix fun_sel>]().bits(func as u8) + }); + } + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + unsafe { $crate::peripherals::LPWR::steal() } + .pad_hold() + .modify(|_, w| w.$hold().bit(enable)); + } + } + + impl $crate::gpio::RtcPinWithResistors for GpioPin<$pin_num> + { + fn rtcio_pullup(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit(enable)); + } + } + + fn rtcio_pulldown(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit(enable)); + } + } + } + + impl $crate::gpio::AnalogPin for GpioPin<$pin_num> { + /// Configures the pin for analog mode. + fn set_analog(&self, _: $crate::private::Internal) { + use $crate::gpio::RtcPin; + enable_iomux_clk_gate(); + + let rtcio = unsafe{ $crate::peripherals::RTC_IO::steal() }; + + paste::paste! { + // disable input + rtcio.$pin_reg.modify(|_,w| w.[<$prefix fun_ie>]().bit(false)); + + // disable output + rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << self.rtc_number()) }); + + // disable open drain + rtcio.pin(self.rtc_number() as usize).modify(|_,w| w.pad_driver().bit(false)); + + rtcio.$pin_reg.modify(|_,w| { + w.[<$prefix fun_ie>]().clear_bit(); + + // Connect pin to analog / RTC module instead of standard GPIO + w.[<$prefix mux_sel>]().set_bit(); + + // Select function "RTC function 1" (GPIO) for analog use + unsafe { w.[<$prefix fun_sel>]().bits(0b00) }; + + // Disable pull-up and pull-down resistors on the pin + w.[<$prefix rue>]().bit(false); + w.[<$prefix rde>]().bit(false); + + w + }); + } + } + } + }; + + ( + $( ( $pin_num:expr, $pin_reg:expr, $prefix:pat, $hold:ident ) )+ + ) => { + $( + rtcio_analog!($pin_num, $pin_reg, $prefix, $hold); + )+ + }; } -crate::rtcio_analog! { - ( 0, 0, touch_pad(0), "", touch_pad0_hold, true) - ( 1, 1, touch_pad(1), "", touch_pad1_hold, true) - ( 2, 2, touch_pad(2), "", touch_pad2_hold, true) - ( 3, 3, touch_pad(3), "", touch_pad3_hold, true) - ( 4, 4, touch_pad(4), "", touch_pad4_hold, true) - ( 5, 5, touch_pad(5), "", touch_pad5_hold, true) - ( 6, 6, touch_pad(6), "", touch_pad6_hold, true) - ( 7, 7, touch_pad(7), "", touch_pad7_hold, true) - ( 8, 8, touch_pad(8), "", touch_pad8_hold, true) - ( 9, 9, touch_pad(9), "", touch_pad9_hold, true) - (10, 10, touch_pad(10), "", touch_pad10_hold, true) - (11, 11, touch_pad(11), "", touch_pad11_hold, true) - (12, 12, touch_pad(12), "", touch_pad12_hold, true) - (13, 13, touch_pad(13), "", touch_pad13_hold, true) - (14, 14, touch_pad(14), "", touch_pad14_hold, true) - (15, 15, xtal_32p_pad(), x32p_, x32p_hold, true) - (16, 16, xtal_32n_pad(), x32n_, x32n_hold, true) - (17, 17, pad_dac1(), "", pdac1_hold, true) - (18, 18, pad_dac2(), "", pdac2_hold, true) - (19, 19, rtc_pad19(), "", pad19_hold, true) - (20, 20, rtc_pad20(), "", pad20_hold, true) - (21, 21, rtc_pad21(), "", pad21_hold, true) +rtcio_analog! { + ( 0, touch_pad(0), "", touch_pad0_hold ) + ( 1, touch_pad(1), "", touch_pad1_hold ) + ( 2, touch_pad(2), "", touch_pad2_hold ) + ( 3, touch_pad(3), "", touch_pad3_hold ) + ( 4, touch_pad(4), "", touch_pad4_hold ) + ( 5, touch_pad(5), "", touch_pad5_hold ) + ( 6, touch_pad(6), "", touch_pad6_hold ) + ( 7, touch_pad(7), "", touch_pad7_hold ) + ( 8, touch_pad(8), "", touch_pad8_hold ) + ( 9, touch_pad(9), "", touch_pad9_hold ) + (10, touch_pad(10), "", touch_pad10_hold) + (11, touch_pad(11), "", touch_pad11_hold) + (12, touch_pad(12), "", touch_pad12_hold) + (13, touch_pad(13), "", touch_pad13_hold) + (14, touch_pad(14), "", touch_pad14_hold) + (15, xtal_32p_pad(), x32p_, x32p_hold ) + (16, xtal_32n_pad(), x32n_, x32n_hold ) + (17, pad_dac1(), "", pdac1_hold ) + (18, pad_dac2(), "", pdac2_hold ) + (19, rtc_pad19(), "", pad19_hold ) + (20, rtc_pad20(), "", pad20_hold ) + (21, rtc_pad21(), "", pad21_hold ) } #[derive(Clone, Copy)] @@ -395,8 +449,8 @@ pub(crate) enum InterruptStatusRegisterAccess { impl InterruptStatusRegisterAccess { pub(crate) fn interrupt_status_read(self) -> u32 { match self { - Self::Bank0 => unsafe { &*GPIO::PTR }.pcpu_int().read().bits(), - Self::Bank1 => unsafe { &*GPIO::PTR }.pcpu_int1().read().bits(), + Self::Bank0 => unsafe { GPIO::steal() }.pcpu_int().read().bits(), + Self::Bank1 => unsafe { GPIO::steal() }.pcpu_int1().read().bits(), } } } @@ -404,3 +458,9 @@ impl InterruptStatusRegisterAccess { // implement marker traits on USB pins impl crate::otg_fs::UsbDm for GpioPin<19> {} impl crate::otg_fs::UsbDp for GpioPin<20> {} + +fn enable_iomux_clk_gate() { + unsafe { crate::peripherals::SENS::steal() } + .sar_io_mux_conf() + .modify(|_, w| w.iomux_clk_gate_en().set_bit()); +} diff --git a/esp-hal/src/soc/esp32s2/mod.rs b/esp-hal/src/soc/esp32s2/mod.rs index 476b4b99c5e..ed39d35546f 100644 --- a/esp-hal/src/soc/esp32s2/mod.rs +++ b/esp-hal/src/soc/esp32s2/mod.rs @@ -31,6 +31,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf" }; +} + pub use chip; pub(crate) mod constants { @@ -92,7 +98,7 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { addr_of_mut!(_rtc_slow_bss_end), ); if matches!( - crate::reset::get_reset_reason(), + crate::reset::reset_reason(), None | Some(SocResetReason::ChipPowerOn) ) { xtensa_lx_rt::zero_bss( diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index d6bfb5b831f..fc0cf5917fd 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -20,53 +20,100 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - ADC2 <= virtual, - AES <= AES, - DAC1 <= virtual, - DAC2 <= virtual, - DMA <= virtual, - DEDICATED_GPIO <= DEDICATED_GPIO, - DS <= DS, - EFUSE <= EFUSE, - EXTMEM <= EXTMEM, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HMAC <= HMAC, - I2C0 <= I2C0, - I2C1 <= I2C1, - [I2s0] I2S0 <= I2S0 (I2S0), - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - IO_MUX <= IO_MUX, - LEDC <= LEDC, - LPWR <= RTC_CNTL, - PCNT <= PCNT, - PMS <= PMS, - PSRAM <= virtual, - RADIO_CLK <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - RTC_IO <= RTC_IO, - RTC_I2C <= RTC_I2C, - SHA <= SHA, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2_DMA, SPI2), - [Spi3] SPI3 <= SPI3 (SPI3_DMA, SPI3), - SYSCON <= SYSCON, - SYSTEM <= SYSTEM, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - [Twai0] TWAI0 <= TWAI0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - UHCI0 <= UHCI0, - ULP_RISCV_CORE <= virtual, - USB0 <= USB0, - USB_WRAP <= USB_WRAP, - WIFI <= virtual, - XTS_AES <= XTS_AES, + peripherals: [ + ADC1 <= virtual, + ADC2 <= virtual, + AES <= AES, + DAC1 <= virtual, + DAC2 <= virtual, + DMA <= virtual, + DEDICATED_GPIO <= DEDICATED_GPIO, + DS <= DS, + EFUSE <= EFUSE, + EXTMEM <= EXTMEM, + GPIO_SD <= GPIO_SD, + HMAC <= HMAC, + I2C0 <= I2C0, + I2C1 <= I2C1, + I2S0 <= I2S0 (I2S0), + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + IO_MUX <= IO_MUX, + LEDC <= LEDC, + LPWR <= RTC_CNTL, + PCNT <= PCNT, + PMS <= PMS, + PSRAM <= virtual, + RADIO_CLK <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + RTC_IO <= RTC_IO, + RTC_I2C <= RTC_I2C, + SHA <= SHA, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2_DMA, SPI2), + SPI3 <= SPI3 (SPI3_DMA, SPI3), + SYSCON <= SYSCON, + SYSTEM <= SYSTEM, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TWAI0 <= TWAI0, + UART0 <= UART0, + UART1 <= UART1, + UHCI0 <= UHCI0, + ULP_RISCV_CORE <= virtual, + USB0 <= USB0, + USB_WRAP <= USB_WRAP, + WIFI <= virtual, + XTS_AES <= XTS_AES, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo]) + (1, [Input, Output, Analog, RtcIo]) + (2, [Input, Output, Analog, RtcIo]) + (3, [Input, Output, Analog, RtcIo]) + (4, [Input, Output, Analog, RtcIo]) + (5, [Input, Output, Analog, RtcIo]) + (6, [Input, Output, Analog, RtcIo]) + (7, [Input, Output, Analog, RtcIo]) + (8, [Input, Output, Analog, RtcIo] ( ) ( 3 => SUBSPICS1 )) + (9, [Input, Output, Analog, RtcIo] ( 3 => SUBSPIHD 4 => FSPIHD ) ( 3 => SUBSPIHD 4 => FSPIHD )) + (10, [Input, Output, Analog, RtcIo] (2 => FSPIIO4 4 => FSPICS0) (2 => FSPIIO4 3 => SUBSPICS0 4 => FSPICS0)) + (11, [Input, Output, Analog, RtcIo] (2 => FSPIIO5 3 => SUBSPID 4 => FSPID ) (2 => FSPIIO5 3 => SUBSPID 4 => FSPID )) + (12, [Input, Output, Analog, RtcIo] (2 => FSPIIO6 4 => FSPICLK) (2 => FSPIIO6 3 => SUBSPICLK 4 => FSPICLK)) + (13, [Input, Output, Analog, RtcIo] (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ ) (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ )) + (14, [Input, Output, Analog, RtcIo] ( 3 => SUBSPIWP 4 => FSPIWP ) (2 => FSPIDQS 3 => SUBSPIWP 4 => FSPIWP )) + (15, [Input, Output, Analog, RtcIo] ( ) (2 => U0RTS)) + (16, [Input, Output, Analog, RtcIo] (2 => U0CTS) ( )) + (17, [Input, Output, Analog, RtcIo] ( ) (2 => U1TXD)) + (18, [Input, Output, Analog, RtcIo] (2 => U1RXD) ( )) + (19, [Input, Output, Analog, RtcIo] ( ) (2 => U1RTS)) + (20, [Input, Output, Analog, RtcIo] (2 => U1CTS) ( )) + (21, [Input, Output, Analog, RtcIo]) + + (26, [Input, Output]) + (27, [Input, Output]) + (28, [Input, Output]) + (29, [Input, Output]) + (30, [Input, Output]) + (31, [Input, Output]) + (32, [Input, Output]) + (33, [Input, Output] (2 => FSPIHD 3 => SUBSPIHD ) (2 => FSPIHD 3 => SUBSPIHD )) + (34, [Input, Output] (2 => FSPICS0 ) (2 => FSPICS0 3 => SUBSPICS0 )) + (35, [Input, Output] (2 => FSPID 3 => SUBSPID ) (2 => FSPID 3 => SUBSPID )) + (36, [Input, Output] (2 => FSPICLK ) (2 => FSPICLK 3 => SUBSPICLK )) + (37, [Input, Output] (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS) (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS)) + (38, [Input, Output] (2 => FSPIWP 3 => SUBSPIWP ) (2 => FSPIWP 3 => SUBSPIWP )) + (39, [Input, Output] ( ) ( 3 => SUBSPICS1 )) + (40, [Input, Output]) + (41, [Input, Output]) + (42, [Input, Output]) + (43, [Input, Output]) + (44, [Input, Output]) + (45, [Input, Output]) + (46, [Input, Output]) + ] } diff --git a/esp-hal/src/soc/esp32s3/cpu_control.rs b/esp-hal/src/soc/esp32s3/cpu_control.rs index cc9fe6f415a..9e086bed5e2 100644 --- a/esp-hal/src/soc/esp32s3/cpu_control.rs +++ b/esp-hal/src/soc/esp32s3/cpu_control.rs @@ -264,7 +264,7 @@ impl<'d> CpuControl<'d> { let entry = unsafe { ManuallyDrop::take(&mut *entry.cast::>()) }; entry(); loop { - unsafe { internal_park_core(crate::get_core()) }; + unsafe { internal_park_core(Cpu::current()) }; } } None => panic!("No start function set"), diff --git a/esp-hal/src/soc/esp32s3/efuse/mod.rs b/esp-hal/src/soc/esp32s3/efuse/mod.rs index d4303daf85c..2762aae95c2 100644 --- a/esp-hal/src/soc/esp32s3/efuse/mod.rs +++ b/esp-hal/src/soc/esp32s3/efuse/mod.rs @@ -22,12 +22,10 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::efuse::Efuse; -//! # use esp_hal::gpio::Io; //! # use esp_hal::uart::Uart; //! # use core::writeln; //! # use core::fmt::Write; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! # let mut serial_tx = Uart::new(peripherals.UART0, io.pins.gpio4, io.pins.gpio5).unwrap(); +//! # let mut serial_tx = Uart::new(peripherals.UART0, peripherals.GPIO4, peripherals.GPIO5).unwrap(); //! let mac_address = Efuse::read_base_mac_address(); //! writeln!( //! serial_tx, @@ -57,19 +55,19 @@ impl Efuse { } /// Get status of SPI boot encryption. - pub fn get_flash_encryption() -> bool { + pub fn flash_encryption() -> bool { (Self::read_field_le::(SPI_BOOT_CRYPT_CNT).count_ones() % 2) != 0 } /// Get the multiplier for the timeout value of the RWDT STAGE 0 register. - pub fn get_rwdt_multiplier() -> u8 { + pub fn rwdt_multiplier() -> u8 { Self::read_field_le::(WDT_DELAY_SEL) } /// Get efuse block version /// /// see - pub fn get_block_version() -> (u8, u8) { + pub fn block_version() -> (u8, u8) { // see // ( @@ -81,8 +79,8 @@ impl Efuse { /// Get version of RTC calibration block /// /// see - pub fn get_rtc_calib_version() -> u8 { - let (major, _minor) = Self::get_block_version(); + pub fn rtc_calib_version() -> u8 { + let (major, _minor) = Self::block_version(); if major == 1 { 1 @@ -94,8 +92,8 @@ impl Efuse { /// Get ADC initial code for specified attenuation from efuse /// /// see - pub fn get_rtc_calib_init_code(unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_init_code(unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; @@ -145,15 +143,15 @@ impl Efuse { /// Get ADC reference point voltage for specified attenuation in millivolts /// /// see - pub fn get_rtc_calib_cal_mv(_unit: u8, _atten: Attenuation) -> u16 { + pub fn rtc_calib_cal_mv(_unit: u8, _atten: Attenuation) -> u16 { 850 } /// Get ADC reference point digital code for specified attenuation /// /// see - pub fn get_rtc_calib_cal_code(unit: u8, atten: Attenuation) -> Option { - let version = Self::get_rtc_calib_version(); + pub fn rtc_calib_cal_code(unit: u8, atten: Attenuation) -> Option { + let version = Self::rtc_calib_version(); if version != 1 { return None; diff --git a/esp-hal/src/soc/esp32s3/gpio.rs b/esp-hal/src/soc/esp32s3/gpio.rs index a7daf2c6ec2..f202b064875 100644 --- a/esp-hal/src/soc/esp32s3/gpio.rs +++ b/esp-hal/src/soc/esp32s3/gpio.rs @@ -8,7 +8,7 @@ //! //! Let's get through the functionality and configurations provided by this GPIO //! module: -//! - `get_io_mux_reg(gpio_num: u8) -> &'static +//! - `io_mux_reg(gpio_num: u8) -> &'static //! crate::peripherals::io_mux::GPIO0:`: //! * Returns the IO_MUX register for the specified GPIO pin number. //! - `gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8`: @@ -46,6 +46,7 @@ pub const NUM_PINS: usize = 49; pub(crate) const FUNC_IN_SEL_OFFSET: usize = 0; +pub(crate) type InputSignalType = u16; pub(crate) type OutputSignalType = u16; pub(crate) const OUTPUT_SIGNAL_MAX: u16 = 256; pub(crate) const INPUT_SIGNAL_MAX: u16 = 189; @@ -55,7 +56,7 @@ pub(crate) const ZERO_INPUT: u8 = 0x3c; pub(crate) const GPIO_FUNCTION: AlternateFunction = AlternateFunction::Function1; -pub(crate) const fn get_io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { +pub(crate) const fn io_mux_reg(gpio_num: u8) -> &'static crate::peripherals::io_mux::GPIO { unsafe { (*crate::peripherals::IO_MUX::PTR).gpio(gpio_num as usize) } } @@ -65,7 +66,8 @@ pub(crate) fn gpio_intr_enable(int_enable: bool, nmi_enable: bool) -> u8 { /// Peripheral input signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[doc(hidden)] pub enum InputSignal { SPIQ = 0, @@ -203,8 +205,10 @@ pub enum InputSignal { /// Peripheral output signals for the GPIO mux #[allow(non_camel_case_types)] -#[derive(PartialEq, Copy, Clone)] -#[doc(hidden)] +#[derive(Debug, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[doc(hidden)] // TODO connection operations are now public on these, we might want to publish + // them pub enum OutputSignal { SPIQ = 0, SPID = 1, @@ -345,77 +349,127 @@ pub enum OutputSignal { GPIO = 256, } -crate::gpio! { - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo]) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo]) - (5, [Input, Output, Analog, RtcIo]) - (6, [Input, Output, Analog, RtcIo]) - (7, [Input, Output, Analog, RtcIo]) - (8, [Input, Output, Analog, RtcIo] () (3 => SUBSPICS1)) - (9, [Input, Output, Analog, RtcIo] (3 => SUBSPIHD 4 => FSPIHD) (3 => SUBSPIHD 4 => FSPIHD)) - (10, [Input, Output, Analog, RtcIo] (2 => FSPIIO4 4 => FSPICS0) (2 => FSPIIO4 3 => SUBSPICS0 4 => FSPICS0)) - (11, [Input, Output, Analog, RtcIo] (2 => FSPIIO5 3 => SUBSPID 4 => FSPID) (2 => FSPIIO5 3 => SUBSPID 4 => FSPID)) - (12, [Input, Output, Analog, RtcIo] (2 => FSPIIO6 4 => FSPICLK) (2 => FSPIIO6 3=> SUBSPICLK 4 => FSPICLK)) - (13, [Input, Output, Analog, RtcIo] (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ) (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ)) - (14, [Input, Output, Analog, RtcIo] (3 => SUBSPIWP 4 => FSPIWP) (2 => FSPIDQS 3 => SUBSPIWP 4 => FSPIWP)) - (15, [Input, Output, Analog, RtcIo] () (2 => U0RTS)) - (16, [Input, Output, Analog, RtcIo] (2 => U0CTS) ()) - (17, [Input, Output, Analog, RtcIo] () (2 => U1TXD)) - (18, [Input, Output, Analog, RtcIo] (2 => U1RXD) ()) - (19, [Input, Output, Analog, RtcIo] () (2 => U1RTS)) - (20, [Input, Output, Analog, RtcIo] (2 => U1CTS) ()) - (21, [Input, Output, Analog, RtcIo]) - (26, [Input, Output]) - (27, [Input, Output]) - (28, [Input, Output]) - (29, [Input, Output]) - (30, [Input, Output]) - (31, [Input, Output]) - (32, [Input, Output]) - (33, [Input, Output] (2 => FSPIHD 3 => SUBSPIHD) (2 => FSPIHD 3 => SUBSPIHD)) - (34, [Input, Output] (2 => FSPICS0) (2 => FSPICS0 3 => SUBSPICS0)) - (35, [Input, Output] (2 => FSPID 3 => SUBSPID) (2 => FSPID 3 => SUBSPID)) - (36, [Input, Output] (2 => FSPICLK) (2 => FSPICLK 3 => SUBSPICLK)) - (37, [Input, Output] (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS) (2 => FSPIQ 3=> SUBSPIQ 4 => SPIDQS)) - (38, [Input, Output] (2 => FSPIWP 3 => SUBSPIWP) (3 => FSPIWP 3 => SUBSPIWP)) - (39, [Input, Output] () (4 => SUBSPICS1)) - (40, [Input, Output]) - (41, [Input, Output]) - (42, [Input, Output]) - (43, [Input, Output]) - (44, [Input, Output]) - (45, [Input, Output]) - (46, [Input, Output]) - (47, [Input, Output]) - (48, [Input, Output]) +macro_rules! rtcio_analog { + ( @ignore $rue:literal ) => {}; + + ( + $pin_num:expr, $pin_reg:expr, $prefix:pat, $hold:ident + ) => { + impl $crate::gpio::RtcPin for GpioPin<$pin_num> { + fn rtc_number(&self) -> u8 { + $pin_num + } + + /// Set the RTC properties of the pin. If `mux` is true then then pin is + /// routed to RTC, when false it is routed to IO_MUX. + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: $crate::gpio::RtcFunction) { + enable_iomux_clk_gate(); + + // disable input + paste::paste!{ + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_,w| unsafe { + w.[<$prefix fun_ie>]().bit(input_enable); + w.[<$prefix mux_sel>]().bit(mux); + w.[<$prefix fun_sel>]().bits(func as u8) + }); + } + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + unsafe { $crate::peripherals::LPWR::steal() } + .pad_hold() + .modify(|_, w| w.$hold().bit(enable)); + } + } + + impl $crate::gpio::RtcPinWithResistors for GpioPin<$pin_num> + { + fn rtcio_pullup(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rue >]().bit(enable)); + } + } + + fn rtcio_pulldown(&mut self, enable: bool) { + paste::paste! { + unsafe { $crate::peripherals::RTC_IO::steal() } + .$pin_reg.modify(|_, w| w.[< $prefix rde >]().bit(enable)); + } + } + } + + impl $crate::gpio::AnalogPin for GpioPin<$pin_num> { + /// Configures the pin for analog mode. + fn set_analog(&self, _: $crate::private::Internal) { + use $crate::gpio::RtcPin; + enable_iomux_clk_gate(); + + let rtcio = unsafe{ $crate::peripherals::RTC_IO::steal() }; + + paste::paste! { + // disable input + rtcio.$pin_reg.modify(|_,w| w.[<$prefix fun_ie>]().bit(false)); + + // disable output + rtcio.enable_w1tc().write(|w| unsafe { w.enable_w1tc().bits(1 << self.rtc_number()) }); + + // disable open drain + rtcio.pin(self.rtc_number() as usize).modify(|_,w| w.pad_driver().bit(false)); + + rtcio.$pin_reg.modify(|_,w| { + w.[<$prefix fun_ie>]().clear_bit(); + + // Connect pin to analog / RTC module instead of standard GPIO + w.[<$prefix mux_sel>]().set_bit(); + + // Select function "RTC function 1" (GPIO) for analog use + unsafe { w.[<$prefix fun_sel>]().bits(0b00) }; + + // Disable pull-up and pull-down resistors on the pin + w.[<$prefix rue>]().bit(false); + w.[<$prefix rde>]().bit(false); + + w + }); + } + } + } + }; + + ( + $( ( $pin_num:expr, $pin_reg:expr, $prefix:pat, $hold:ident ) )+ + ) => { + $( + rtcio_analog!($pin_num, $pin_reg, $prefix, $hold); + )+ + }; } -crate::rtcio_analog! { - ( 0, 0, touch_pad(0), "", touch_pad0_hold, true) - ( 1, 1, touch_pad(1), "", touch_pad1_hold, true) - ( 2, 2, touch_pad(2), "", touch_pad2_hold, true) - ( 3, 3, touch_pad(3), "", touch_pad3_hold, true) - ( 4, 4, touch_pad(4), "", touch_pad4_hold, true) - ( 5, 5, touch_pad(5), "", touch_pad5_hold, true) - ( 6, 6, touch_pad(6), "", touch_pad6_hold, true) - ( 7, 7, touch_pad(7), "", touch_pad7_hold, true) - ( 8, 8, touch_pad(8), "", touch_pad8_hold, true) - ( 9, 9, touch_pad(9), "", touch_pad9_hold, true) - (10, 10, touch_pad(10), "", touch_pad10_hold, true) - (11, 11, touch_pad(11), "", touch_pad11_hold, true) - (12, 12, touch_pad(12), "", touch_pad12_hold, true) - (13, 13, touch_pad(13), "", touch_pad13_hold, true) - (14, 14, touch_pad(14), "", touch_pad14_hold, true) - (15, 15, xtal_32p_pad(), x32p_, x32p_hold, true) - (16, 16, xtal_32n_pad(), x32n_, x32n_hold, true) - (17, 17, pad_dac1(), pdac1_, pdac1_hold, true) - (18, 18, pad_dac2(), pdac2_, pdac2_hold, true) - (19, 19, rtc_pad19(), "", pad19_hold, true) - (20, 20, rtc_pad20(), "", pad20_hold, true) - (21, 21, rtc_pad21(), "", pad21_hold, true) +rtcio_analog! { + ( 0, touch_pad(0), "", touch_pad0_hold ) + ( 1, touch_pad(1), "", touch_pad1_hold ) + ( 2, touch_pad(2), "", touch_pad2_hold ) + ( 3, touch_pad(3), "", touch_pad3_hold ) + ( 4, touch_pad(4), "", touch_pad4_hold ) + ( 5, touch_pad(5), "", touch_pad5_hold ) + ( 6, touch_pad(6), "", touch_pad6_hold ) + ( 7, touch_pad(7), "", touch_pad7_hold ) + ( 8, touch_pad(8), "", touch_pad8_hold ) + ( 9, touch_pad(9), "", touch_pad9_hold ) + (10, touch_pad(10), "", touch_pad10_hold) + (11, touch_pad(11), "", touch_pad11_hold) + (12, touch_pad(12), "", touch_pad12_hold) + (13, touch_pad(13), "", touch_pad13_hold) + (14, touch_pad(14), "", touch_pad14_hold) + (15, xtal_32p_pad(), x32p_, x32p_hold ) + (16, xtal_32n_pad(), x32n_, x32n_hold ) + (17, pad_dac1(), pdac1_, pdac1_hold ) + (18, pad_dac2(), pdac2_, pdac2_hold ) + (19, rtc_pad19(), "", pad19_hold ) + (20, rtc_pad20(), "", pad20_hold ) + (21, rtc_pad21(), "", pad21_hold ) } // Whilst the S3 is a dual core chip, it shares the enable registers between @@ -429,8 +483,8 @@ pub(crate) enum InterruptStatusRegisterAccess { impl InterruptStatusRegisterAccess { pub(crate) fn interrupt_status_read(self) -> u32 { match self { - Self::Bank0 => unsafe { &*GPIO::PTR }.pcpu_int().read().bits(), - Self::Bank1 => unsafe { &*GPIO::PTR }.pcpu_int1().read().bits(), + Self::Bank0 => unsafe { GPIO::steal() }.pcpu_int().read().bits(), + Self::Bank1 => unsafe { GPIO::steal() }.pcpu_int1().read().bits(), } } } @@ -438,3 +492,9 @@ impl InterruptStatusRegisterAccess { // implement marker traits on USB pins impl crate::otg_fs::UsbDm for GpioPin<19> {} impl crate::otg_fs::UsbDp for GpioPin<20> {} + +fn enable_iomux_clk_gate() { + unsafe { crate::peripherals::SENS::steal() } + .sar_peri_clk_gate_conf() + .modify(|_, w| w.iomux_clk_en().set_bit()); +} diff --git a/esp-hal/src/soc/esp32s3/mod.rs b/esp-hal/src/soc/esp32s3/mod.rs index be34da1364b..f0b5171c4f0 100644 --- a/esp-hal/src/soc/esp32s3/mod.rs +++ b/esp-hal/src/soc/esp32s3/mod.rs @@ -32,6 +32,12 @@ macro_rules! chip { }; } +/// A link to the Technical Reference Manual (TRM) for the chip. +#[macro_export] +macro_rules! trm_link { + () => { "https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf" }; +} + pub use chip; pub(crate) mod constants { @@ -131,7 +137,7 @@ pub unsafe extern "C" fn ESP32Reset() -> ! { addr_of_mut!(_rtc_slow_bss_end), ); if matches!( - crate::reset::get_reset_reason(), + crate::reset::reset_reason(), None | Some(SocResetReason::ChipPowerOn) ) { xtensa_lx_rt::zero_bss( diff --git a/esp-hal/src/soc/esp32s3/peripherals.rs b/esp-hal/src/soc/esp32s3/peripherals.rs index 30159a65367..c6ab886c9bf 100644 --- a/esp-hal/src/soc/esp32s3/peripherals.rs +++ b/esp-hal/src/soc/esp32s3/peripherals.rs @@ -20,63 +20,111 @@ pub(crate) use self::peripherals::*; // peripheral (no `PSRAM`, `RADIO`, etc. peripheral in the PACs), so we're // creating "virtual peripherals" for them. crate::peripherals! { - ADC1 <= virtual, - ADC2 <= virtual, - AES <= AES, - APB_CTRL <= APB_CTRL, - ASSIST_DEBUG <= ASSIST_DEBUG, - BT <= virtual, - CPU_CTRL <= virtual, - DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_IN_CH3,DMA_IN_CH4,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2,DMA_OUT_CH3,DMA_OUT_CH4), - DS <= DS, - EFUSE <= EFUSE, - EXTMEM <= EXTMEM, - GPIO <= GPIO (GPIO,GPIO_NMI), - GPIO_SD <= GPIO_SD, - HMAC <= HMAC, - I2C0 <= I2C0, - I2C1 <= I2C1, - [I2s0] I2S0 <= I2S0 (I2S0), - [I2s1] I2S1 <= I2S1 (I2S1), - INTERRUPT_CORE0 <= INTERRUPT_CORE0, - INTERRUPT_CORE1 <= INTERRUPT_CORE1, - IO_MUX <= IO_MUX, - [LcdCam] LCD_CAM <= LCD_CAM, - LEDC <= LEDC, - LPWR <= RTC_CNTL, - PCNT <= PCNT, - PERI_BACKUP <= PERI_BACKUP, - PSRAM <= virtual, - MCPWM0 <= MCPWM0, - MCPWM1 <= MCPWM1, - RADIO_CLK <= virtual, - RMT <= RMT, - RNG <= RNG, - RSA <= RSA, - RTC_I2C <= RTC_I2C, - RTC_IO <= RTC_IO, - SENSITIVE <= SENSITIVE, - SHA <= SHA, - SPI0 <= SPI0, - SPI1 <= SPI1, - [Spi2] SPI2 <= SPI2 (SPI2), - [Spi3] SPI3 <= SPI3 (SPI3), - SYSTEM <= SYSTEM, - SYSTIMER <= SYSTIMER, - SW_INTERRUPT <= virtual, - TIMG0 <= TIMG0, - TIMG1 <= TIMG1, - [Twai0] TWAI0 <= TWAI0, - [Uart0] UART0 <= UART0, - [Uart1] UART1 <= UART1, - [Uart2] UART2 <= UART2, - UHCI0 <= UHCI0, - UHCI1 <= UHCI1, - ULP_RISCV_CORE <= virtual, - USB0 <= USB0, - USB_DEVICE <= USB_DEVICE, - USB_WRAP <= USB_WRAP, - WCL <= WCL, - WIFI <= virtual, - XTS_AES <= XTS_AES, + peripherals: [ + ADC1 <= virtual, + ADC2 <= virtual, + AES <= AES, + APB_CTRL <= APB_CTRL, + ASSIST_DEBUG <= ASSIST_DEBUG, + BT <= virtual, + CPU_CTRL <= virtual, + DMA <= DMA (DMA_IN_CH0,DMA_IN_CH1,DMA_IN_CH2,DMA_IN_CH3,DMA_IN_CH4,DMA_OUT_CH0,DMA_OUT_CH1,DMA_OUT_CH2,DMA_OUT_CH3,DMA_OUT_CH4), + DS <= DS, + EFUSE <= EFUSE, + EXTMEM <= EXTMEM, + GPIO_SD <= GPIO_SD, + HMAC <= HMAC, + I2C0 <= I2C0, + I2C1 <= I2C1, + I2S0 <= I2S0 (I2S0), + I2S1 <= I2S1 (I2S1), + INTERRUPT_CORE0 <= INTERRUPT_CORE0, + INTERRUPT_CORE1 <= INTERRUPT_CORE1, + IO_MUX <= IO_MUX, + LCD_CAM <= LCD_CAM, + LEDC <= LEDC, + LPWR <= RTC_CNTL, + PCNT <= PCNT, + PERI_BACKUP <= PERI_BACKUP, + PSRAM <= virtual, + MCPWM0 <= MCPWM0, + MCPWM1 <= MCPWM1, + RADIO_CLK <= virtual, + RMT <= RMT, + RNG <= RNG, + RSA <= RSA, + RTC_I2C <= RTC_I2C, + RTC_IO <= RTC_IO, + SENSITIVE <= SENSITIVE, + SHA <= SHA, + SPI0 <= SPI0, + SPI1 <= SPI1, + SPI2 <= SPI2 (SPI2), + SPI3 <= SPI3 (SPI3), + SYSTEM <= SYSTEM, + SYSTIMER <= SYSTIMER, + SW_INTERRUPT <= virtual, + TIMG0 <= TIMG0, + TIMG1 <= TIMG1, + TWAI0 <= TWAI0, + UART0 <= UART0, + UART1 <= UART1, + UART2 <= UART2, + UHCI0 <= UHCI0, + UHCI1 <= UHCI1, + ULP_RISCV_CORE <= virtual, + USB0 <= USB0, + USB_DEVICE <= USB_DEVICE, + USB_WRAP <= USB_WRAP, + WCL <= WCL, + WIFI <= virtual, + XTS_AES <= XTS_AES, + ], + pins: [ + (0, [Input, Output, Analog, RtcIo]) + (1, [Input, Output, Analog, RtcIo]) + (2, [Input, Output, Analog, RtcIo]) + (3, [Input, Output, Analog, RtcIo]) + (4, [Input, Output, Analog, RtcIo]) + (5, [Input, Output, Analog, RtcIo]) + (6, [Input, Output, Analog, RtcIo]) + (7, [Input, Output, Analog, RtcIo]) + (8, [Input, Output, Analog, RtcIo] () (3 => SUBSPICS1)) + (9, [Input, Output, Analog, RtcIo] (3 => SUBSPIHD 4 => FSPIHD) (3 => SUBSPIHD 4 => FSPIHD)) + (10, [Input, Output, Analog, RtcIo] (2 => FSPIIO4 4 => FSPICS0) (2 => FSPIIO4 3 => SUBSPICS0 4 => FSPICS0)) + (11, [Input, Output, Analog, RtcIo] (2 => FSPIIO5 3 => SUBSPID 4 => FSPID) (2 => FSPIIO5 3 => SUBSPID 4 => FSPID)) + (12, [Input, Output, Analog, RtcIo] (2 => FSPIIO6 4 => FSPICLK) (2 => FSPIIO6 3=> SUBSPICLK 4 => FSPICLK)) + (13, [Input, Output, Analog, RtcIo] (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ) (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ)) + (14, [Input, Output, Analog, RtcIo] (3 => SUBSPIWP 4 => FSPIWP) (2 => FSPIDQS 3 => SUBSPIWP 4 => FSPIWP)) + (15, [Input, Output, Analog, RtcIo] () (2 => U0RTS)) + (16, [Input, Output, Analog, RtcIo] (2 => U0CTS) ()) + (17, [Input, Output, Analog, RtcIo] () (2 => U1TXD)) + (18, [Input, Output, Analog, RtcIo] (2 => U1RXD) ()) + (19, [Input, Output, Analog, RtcIo] () (2 => U1RTS)) + (20, [Input, Output, Analog, RtcIo] (2 => U1CTS) ()) + (21, [Input, Output, Analog, RtcIo]) + (26, [Input, Output]) + (27, [Input, Output]) + (28, [Input, Output]) + (29, [Input, Output]) + (30, [Input, Output]) + (31, [Input, Output]) + (32, [Input, Output]) + (33, [Input, Output] (2 => FSPIHD 3 => SUBSPIHD) (2 => FSPIHD 3 => SUBSPIHD)) + (34, [Input, Output] (2 => FSPICS0) (2 => FSPICS0 3 => SUBSPICS0)) + (35, [Input, Output] (2 => FSPID 3 => SUBSPID) (2 => FSPID 3 => SUBSPID)) + (36, [Input, Output] (2 => FSPICLK) (2 => FSPICLK 3 => SUBSPICLK)) + (37, [Input, Output] (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS) (2 => FSPIQ 3=> SUBSPIQ 4 => SPIDQS)) + (38, [Input, Output] (2 => FSPIWP 3 => SUBSPIWP) (3 => FSPIWP 3 => SUBSPIWP)) + (39, [Input, Output] () (4 => SUBSPICS1)) + (40, [Input, Output]) + (41, [Input, Output]) + (42, [Input, Output]) + (43, [Input, Output]) + (44, [Input, Output]) + (45, [Input, Output]) + (46, [Input, Output]) + (47, [Input, Output]) + (48, [Input, Output]) + ] } diff --git a/esp-hal/src/soc/esp32s3/psram.rs b/esp-hal/src/soc/esp32s3/psram.rs index 27067c93724..2a6d00a4812 100644 --- a/esp-hal/src/soc/esp32s3/psram.rs +++ b/esp-hal/src/soc/esp32s3/psram.rs @@ -379,9 +379,9 @@ pub(crate) mod utils { /// or `calculate_best_flash_tuning_config` #[ram] fn mspi_timing_enter_high_speed_mode(control_spi1: bool, config: &PsramConfig) { - let core_clock: SpiTimingConfigCoreClock = get_mspi_core_clock(config); - let flash_div: u32 = get_flash_clock_divider(config); - let psram_div: u32 = get_psram_clock_divider(config); + let core_clock: SpiTimingConfigCoreClock = mspi_core_clock(config); + let flash_div: u32 = flash_clock_divider(config); + let psram_div: u32 = psram_clock_divider(config); info!( "PSRAM core_clock {:?}, flash_div = {}, psram_div = {}", @@ -466,17 +466,17 @@ pub(crate) mod utils { } #[ram] - fn get_mspi_core_clock(config: &PsramConfig) -> SpiTimingConfigCoreClock { + fn mspi_core_clock(config: &PsramConfig) -> SpiTimingConfigCoreClock { config.core_clock } #[ram] - fn get_flash_clock_divider(config: &PsramConfig) -> u32 { + fn flash_clock_divider(config: &PsramConfig) -> u32 { config.core_clock as u32 / config.flash_frequency as u32 } #[ram] - fn get_psram_clock_divider(config: &PsramConfig) -> u32 { + fn psram_clock_divider(config: &PsramConfig) -> u32 { config.core_clock as u32 / config.ram_frequency as u32 } @@ -708,7 +708,7 @@ pub(crate) mod utils { let iomux = &*esp32s3::IO_MUX::PTR; iomux .gpio(cs1_io as usize) - .modify(|_, w| w.mcu_sel().bits(FUNC_SPICS1_SPICS1)) + .modify(|_, w| w.mcu_sel().bits(FUNC_SPICS1_SPICS1)); } } else { unsafe { @@ -717,7 +717,7 @@ pub(crate) mod utils { let iomux = &*esp32s3::IO_MUX::PTR; iomux .gpio(cs1_io as usize) - .modify(|_, w| w.mcu_sel().bits(PIN_FUNC_GPIO)) + .modify(|_, w| w.mcu_sel().bits(PIN_FUNC_GPIO)); } } @@ -1058,7 +1058,7 @@ pub(crate) mod utils { init_psram_mode_reg(1, &mode_reg); // Print PSRAM info - get_psram_mode_reg(1, &mut mode_reg); + psram_mode_reg(1, &mut mode_reg); print_psram_info(&mode_reg); @@ -1156,7 +1156,7 @@ pub(crate) mod utils { spi.sram_cmd().modify(|_, w| w.sdout_oct().set_bit()); spi.sram_cmd().modify(|_, w| w.sdin_oct().set_bit()); - spi.cache_sctrl().modify(|_, w| w.sram_oct().set_bit()) + spi.cache_sctrl().modify(|_, w| w.sram_oct().set_bit()); } } @@ -1164,7 +1164,7 @@ pub(crate) mod utils { fn spi_flash_set_rom_required_regs() { // Disable the variable dummy mode when doing timing tuning let spi = unsafe { &*crate::peripherals::SPI1::PTR }; - spi.ddr().modify(|_, w| w.spi_fmem_var_dummy().clear_bit()) + spi.ddr().modify(|_, w| w.spi_fmem_var_dummy().clear_bit()); // STR /DTR mode setting is done every time when // `esp_rom_opiflash_exec_cmd` is called // @@ -1199,7 +1199,7 @@ pub(crate) mod utils { let pins = &[27usize, 28, 31, 32, 33, 34, 35, 36, 37]; for pin in pins { let iomux = &*esp32s3::IO_MUX::PTR; - iomux.gpio(*pin).modify(|_, w| w.fun_drv().bits(3)) + iomux.gpio(*pin).modify(|_, w| w.fun_drv().bits(3)); } } } @@ -1238,11 +1238,11 @@ pub(crate) mod utils { // This function should always be called after `spi_timing_flash_tuning` or // `calculate_best_flash_tuning_config` fn spi_timing_enter_mspi_high_speed_mode(control_spi1: bool, config: &PsramConfig) { - // spi_timing_config_core_clock_t core_clock = get_mspi_core_clock(); + // spi_timing_config_core_clock_t core_clock = mspi_core_clock(); let core_clock = SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m; - let flash_div: u32 = get_flash_clock_divider(config); - let psram_div: u32 = get_psram_clock_divider(config); + let flash_div: u32 = flash_clock_divider(config); + let psram_div: u32 = psram_clock_divider(config); // Set SPI01 core clock spi0_timing_config_set_core_clock(core_clock); // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. @@ -1287,7 +1287,7 @@ pub(crate) mod utils { let iomux = &*esp32s3::IO_MUX::PTR; iomux .gpio(OCT_PSRAM_CS1_IO as usize) - .modify(|_, w| w.mcu_sel().bits(FUNC_SPICS1_SPICS1)) + .modify(|_, w| w.mcu_sel().bits(FUNC_SPICS1_SPICS1)); } // Set mspi cs1 drive strength @@ -1295,7 +1295,7 @@ pub(crate) mod utils { let iomux = &*esp32s3::IO_MUX::PTR; iomux .gpio(OCT_PSRAM_CS1_IO as usize) - .modify(|_, w| w.fun_drv().bits(3)) + .modify(|_, w| w.fun_drv().bits(3)); } // Set psram clock pin drive strength @@ -1306,7 +1306,7 @@ pub(crate) mod utils { } } - fn get_psram_mode_reg(spi_num: u32, out_reg: &mut OpiPsramModeReg) { + fn psram_mode_reg(spi_num: u32, out_reg: &mut OpiPsramModeReg) { let mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE; let cmd_len: u32 = 16; let addr_bit_len: u32 = 32; @@ -1601,12 +1601,12 @@ pub(crate) mod utils { } #[ram] - fn get_flash_clock_divider(config: &PsramConfig) -> u32 { + fn flash_clock_divider(config: &PsramConfig) -> u32 { config.core_clock as u32 / config.flash_frequency as u32 } #[ram] - fn get_psram_clock_divider(config: &PsramConfig) -> u32 { + fn psram_clock_divider(config: &PsramConfig) -> u32 { config.core_clock as u32 / config.ram_frequency as u32 } } diff --git a/esp-hal/src/soc/mod.rs b/esp-hal/src/soc/mod.rs index 67f068643cb..600b6629bdb 100644 --- a/esp-hal/src/soc/mod.rs +++ b/esp-hal/src/soc/mod.rs @@ -87,7 +87,7 @@ impl self::efuse::Efuse { /// /// By default this reads the base mac address from eFuse, but it can be /// overridden by `set_mac_address`. - pub fn get_mac_address() -> [u8; 6] { + pub fn mac_address() -> [u8; 6] { if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 { unsafe { MAC_OVERRIDE } } else { diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index dab8b9eb8d8..1430a5269c3 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -24,8 +24,9 @@ //! ### Shared SPI access //! //! If you have multiple devices on the same SPI bus that each have their own CS -//! line, you may want to have a look at the implementations provided by -//! [`embedded-hal-bus`] and [`embassy-embedded-hal`]. +//! line (and optionally, configuration), you may want to have a look at the +//! implementations provided by [`embedded-hal-bus`] and +//! [`embassy-embedded-hal`]. //! //! ## Usage //! @@ -38,18 +39,19 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::spi::SpiMode; -//! # use esp_hal::spi::master::Spi; -//! # use esp_hal::gpio::Io; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let sclk = io.pins.gpio0; -//! let miso = io.pins.gpio2; -//! let mosi = io.pins.gpio1; -//! let cs = io.pins.gpio5; +//! # use esp_hal::spi::master::{Config, Spi}; +//! let sclk = peripherals.GPIO0; +//! let miso = peripherals.GPIO2; +//! let mosi = peripherals.GPIO1; +//! let cs = peripherals.GPIO5; //! -//! let mut spi = Spi::new( +//! let mut spi = Spi::new_with_config( //! peripherals.SPI2, -//! 100.kHz(), -//! SpiMode::Mode0, +//! Config { +//! frequency: 100.kHz(), +//! mode: SpiMode::Mode0, +//! ..Config::default() +//! }, //! ) //! .with_sck(sclk) //! .with_mosi(mosi) @@ -61,7 +63,10 @@ //! [`embedded-hal-bus`]: https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/spi/index.html //! [`embassy-embedded-hal`]: https://docs.embassy.dev/embassy-embedded-hal/git/default/shared_bus/index.html +use core::marker::PhantomData; + pub use dma::*; +use embassy_embedded_hal::SetConfig; #[cfg(gdma)] use enumset::EnumSet; #[cfg(gdma)] @@ -73,19 +78,16 @@ use procmacros::ram; use super::{DmaError, Error, SpiBitOrder, SpiDataMode, SpiMode}; use crate::{ clock::Clocks, - dma::{DmaChannelConvert, DmaEligible, DmaRxBuffer, DmaTxBuffer, PeripheralMarker, Rx, Tx}, - gpio::{ - interconnect::{OutputConnection, PeripheralOutput}, - InputSignal, - NoPin, - OutputSignal, - }, + dma::{Channel, DmaChannelConvert, DmaEligible, DmaRxBuffer, DmaTxBuffer, Rx, Tx}, + gpio::{interconnect::PeripheralOutput, InputSignal, NoPin, OutputSignal}, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, peripherals::spi2::RegisterBlock, private, spi::AnySpi, system::PeripheralClockControl, + Async, + Blocking, Mode, }; @@ -422,48 +424,86 @@ impl Address { } } +/// SPI peripheral configuration +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// SPI clock frequency + pub frequency: HertzU32, + + /// SPI mode + pub mode: SpiMode, + + /// Bit order of the read data. + pub read_bit_order: SpiBitOrder, + + /// Bit order of the written data. + pub write_bit_order: SpiBitOrder, +} + +impl Default for Config { + fn default() -> Self { + use fugit::RateExtU32; + Config { + frequency: 1_u32.MHz(), + mode: SpiMode::Mode0, + read_bit_order: SpiBitOrder::MSBFirst, + write_bit_order: SpiBitOrder::MSBFirst, + } + } +} + +/// Configuration errors. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ConfigError {} + /// SPI peripheral driver -pub struct Spi<'d, T = AnySpi> { +pub struct Spi<'d, M, T = AnySpi> { spi: PeripheralRef<'d, T>, + _mode: PhantomData, } -impl<'d, T> Spi<'d, T> +impl<'d, M, T> Spi<'d, M, T> where - T: InstanceDma, + M: Mode, + T: Instance, { /// Configures the SPI instance to use DMA with the specified channel. /// /// This method prepares the SPI instance for DMA transfers using SPI /// and returns an instance of `SpiDma` that supports DMA /// operations. - pub fn with_dma( - self, - channel: crate::dma::Channel<'d, CH, DmaMode>, - ) -> SpiDma<'d, DmaMode, T> + pub fn with_dma(self, channel: Channel<'d, CH, DM>) -> SpiDma<'d, M, T> where CH: DmaChannelConvert, - DmaMode: Mode, + DM: Mode, + Channel<'d, CH, M>: From>, { - SpiDma::new(self.spi, channel) + SpiDma::new(self.spi, channel.into()) } } -impl<'d, T> Spi<'d, T> +impl Spi<'_, M, T> where T: Instance, { + fn driver(&self) -> &'static Info { + self.spi.info() + } + /// Read a byte from SPI. /// /// Sends out a stuffing byte for every byte to read. This function doesn't /// perform flushing. If you want to read the response to something you /// have written before, consider using [`Self::transfer`] instead. pub fn read_byte(&mut self) -> nb::Result { - self.spi.read_byte() + self.driver().read_byte() } /// Write a byte to SPI. pub fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { - self.spi.write_byte(word) + self.driver().write_byte(word) } /// Write bytes to SPI. @@ -472,77 +512,93 @@ where /// transmission FIFO. If `words` is longer than 64 bytes, multiple /// sequential transfers are performed. pub fn write_bytes(&mut self, words: &[u8]) -> Result<(), Error> { - self.spi.write_bytes(words)?; - self.spi.flush()?; + self.driver().write_bytes(words)?; + self.driver().flush()?; Ok(()) } /// Sends `words` to the slave. Returns the `words` received from the slave pub fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error> { - self.spi.transfer(words) + self.driver().transfer(words) } } -impl<'d> Spi<'d> { +impl<'d> Spi<'d, Blocking> { + /// Constructs an SPI instance in 8bit dataframe mode. + pub fn new(spi: impl Peripheral

+ 'd) -> Spi<'d, Blocking> { + Self::new_with_config(spi, Config::default()) + } + /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new( + pub fn new_with_config( spi: impl Peripheral

+ 'd, - frequency: HertzU32, - mode: SpiMode, - ) -> Spi<'d> { - Self::new_typed(spi.map_into(), frequency, mode) + config: Config, + ) -> Spi<'d, Blocking> { + Self::new_typed_with_config(spi.map_into(), config) + } + + /// Converts the SPI instance into async mode. + pub fn into_async(self) -> Spi<'d, Async> { + Spi { + spi: self.spi, + _mode: PhantomData, + } + } +} + +impl<'d> Spi<'d, Async> { + /// Converts the SPI instance into blocking mode. + pub fn into_blocking(self) -> Spi<'d, Blocking> { + Spi { + spi: self.spi, + _mode: PhantomData, + } } } -impl<'d, T> Spi<'d, T> +impl<'d, M, T> Spi<'d, M, T> where T: Instance, { /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new_typed( + pub fn new_typed(spi: impl Peripheral

+ 'd) -> Spi<'d, M, T> { + Self::new_typed_with_config(spi, Config::default()) + } + + /// Constructs an SPI instance in 8bit dataframe mode. + pub fn new_typed_with_config( spi: impl Peripheral

+ 'd, - frequency: HertzU32, - mode: SpiMode, - ) -> Spi<'d, T> { + config: Config, + ) -> Spi<'d, M, T> { crate::into_ref!(spi); - let mut spi = Spi { spi }; - spi.spi.reset_peripheral(); - spi.spi.enable_peripheral(); - spi.spi.setup(frequency); - spi.spi.init(); - spi.spi.set_data_mode(mode); + let mut this = Spi { + spi, + _mode: PhantomData, + }; + + PeripheralClockControl::enable(this.driver().peripheral); + PeripheralClockControl::reset(this.driver().peripheral); + + this.driver().init(); + unwrap!(this.apply_config(&config)); // FIXME: update based on the resolution of https://github.com/esp-rs/esp-hal/issues/2416 - let spi = spi + let this = this .with_mosi(NoPin) .with_miso(NoPin) .with_sck(NoPin) .with_cs(NoPin); - let is_qspi = spi.spi.sio2_input_signal().is_some(); + let is_qspi = this.driver().sio2_input.is_some(); if is_qspi { - let mut signal = OutputConnection::from(NoPin); - - signal.connect_input_to_peripheral( - unwrap!(spi.spi.sio2_input_signal()), - private::Internal, - ); - signal.connect_peripheral_to_output( - unwrap!(spi.spi.sio2_output_signal()), - private::Internal, - ); - signal.connect_input_to_peripheral( - unwrap!(spi.spi.sio3_input_signal()), - private::Internal, - ); - signal.connect_peripheral_to_output( - unwrap!(spi.spi.sio3_output_signal()), - private::Internal, - ); + unwrap!(this.driver().sio2_input).connect_to(NoPin); + unwrap!(this.driver().sio2_output).connect_to(NoPin); + unwrap!(this.driver().sio3_input).connect_to(NoPin); + unwrap!(this.driver().sio3_output).connect_to(NoPin); } - spi + this } /// Assign the MOSI (Master Out Slave In) pin for the SPI instance. @@ -552,10 +608,10 @@ where pub fn with_mosi(self, mosi: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(mosi); mosi.enable_output(true, private::Internal); - mosi.connect_peripheral_to_output(self.spi.mosi_signal(), private::Internal); - mosi.enable_input(true, private::Internal); - mosi.connect_input_to_peripheral(self.spi.sio0_input_signal(), private::Internal); + + self.driver().mosi.connect_to(&mut mosi); + self.driver().sio0_input.connect_to(&mut mosi); self } @@ -567,10 +623,10 @@ where pub fn with_miso(self, miso: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(miso); miso.enable_input(true, private::Internal); - miso.connect_input_to_peripheral(self.spi.miso_signal(), private::Internal); - miso.enable_output(true, private::Internal); - miso.connect_peripheral_to_output(self.spi.sio1_output_signal(), private::Internal); + + self.driver().miso.connect_to(&mut miso); + self.driver().sio1_output.connect_to(&mut miso); self } @@ -582,7 +638,7 @@ where pub fn with_sck(self, sclk: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(sclk); sclk.set_to_push_pull_output(private::Internal); - sclk.connect_peripheral_to_output(self.spi.sclk_signal(), private::Internal); + self.driver().sclk.connect_to(sclk); self } @@ -594,29 +650,31 @@ where pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cs); cs.set_to_push_pull_output(private::Internal); - cs.connect_peripheral_to_output(self.spi.cs_signal(), private::Internal); + self.driver().cs.connect_to(cs); self } - /// Change the bus frequency of the SPI instance in half-duplex mode. - /// - /// This method allows you to update the bus frequency for the SPI - /// communication after the instance has been created. - pub fn change_bus_frequency(&mut self, frequency: HertzU32) { - self.spi.ch_bus_freq(frequency); + /// Change the bus configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.driver().apply_config(config) } +} - /// Set the bit order for the SPI instance. - /// - /// The default is MSB first for both read and write. - pub fn with_bit_order(mut self, read_order: SpiBitOrder, write_order: SpiBitOrder) -> Self { - self.spi.set_bit_order(read_order, write_order); - self +impl SetConfig for Spi<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) } } -impl<'d, T> Spi<'d, T> +impl<'d, M, T> Spi<'d, M, T> where T: QspiInstance, { @@ -632,11 +690,8 @@ where sio2.enable_input(true, private::Internal); sio2.enable_output(true, private::Internal); - sio2.connect_input_to_peripheral(unwrap!(self.spi.sio2_input_signal()), private::Internal); - sio2.connect_peripheral_to_output( - unwrap!(self.spi.sio2_output_signal()), - private::Internal, - ); + unwrap!(self.driver().sio2_input).connect_to(&mut sio2); + unwrap!(self.driver().sio2_output).connect_to(&mut sio2); self } @@ -653,17 +708,14 @@ where sio3.enable_input(true, private::Internal); sio3.enable_output(true, private::Internal); - sio3.connect_input_to_peripheral(unwrap!(self.spi.sio3_input_signal()), private::Internal); - sio3.connect_peripheral_to_output( - unwrap!(self.spi.sio3_output_signal()), - private::Internal, - ); + unwrap!(self.driver().sio3_input).connect_to(&mut sio3); + unwrap!(self.driver().sio3_output).connect_to(&mut sio3); self } } -impl Spi<'_, T> +impl Spi<'_, M, T> where T: Instance, { @@ -684,7 +736,7 @@ where return Err(Error::Unsupported); } - self.spi.setup_half_duplex( + self.driver().setup_half_duplex( false, cmd, address, @@ -694,10 +746,10 @@ where data_mode, ); - self.spi.configure_datalen(buffer.len(), 0); - self.spi.start_operation(); - self.spi.flush()?; - self.spi.read_bytes_from_fifo(buffer) + self.driver().configure_datalen(buffer.len(), 0); + self.driver().start_operation(); + self.driver().flush()?; + self.driver().read_bytes_from_fifo(buffer) } /// Half-duplex write. @@ -733,7 +785,7 @@ where } } - self.spi.setup_half_duplex( + self.driver().setup_half_duplex( true, cmd, address, @@ -745,16 +797,16 @@ where if !buffer.is_empty() { // re-using the full-duplex write here - self.spi.write_bytes(buffer)?; + self.driver().write_bytes(buffer)?; } else { - self.spi.start_operation(); + self.driver().start_operation(); } - self.spi.flush() + self.driver().flush() } } -impl embedded_hal_02::spi::FullDuplex for Spi<'_, T> +impl embedded_hal_02::spi::FullDuplex for Spi<'_, M, T> where T: Instance, { @@ -769,7 +821,7 @@ where } } -impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, T> +impl embedded_hal_02::blocking::spi::Transfer for Spi<'_, M, T> where T: Instance, { @@ -780,7 +832,7 @@ where } } -impl embedded_hal_02::blocking::spi::Write for Spi<'_, T> +impl embedded_hal_02::blocking::spi::Write for Spi<'_, M, T> where T: Instance, { @@ -788,7 +840,7 @@ where fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.write_bytes(words)?; - self.spi.flush() + self.driver().flush() } } @@ -813,7 +865,6 @@ mod dma { Tx, }, InterruptConfigurable, - Mode, }; /// A DMA capable SPI instance. @@ -825,7 +876,7 @@ mod dma { /// embedded-hal traits. pub struct SpiDma<'d, M, T = AnySpi> where - T: InstanceDma, + T: Instance, M: Mode, { pub(crate) spi: PeripheralRef<'d, T>, @@ -836,17 +887,50 @@ mod dma { address_buffer: DmaTxBuf, } - #[cfg(all(esp32, spi_address_workaround))] - unsafe impl<'d, M, T> Send for SpiDma<'d, M, T> + impl crate::private::Sealed for SpiDma<'_, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { } - impl<'d, M, T> core::fmt::Debug for SpiDma<'d, M, T> + impl<'d, T> SpiDma<'d, Blocking, T> + where + T: Instance, + { + /// Converts the SPI instance into async mode. + pub fn into_async(self) -> SpiDma<'d, Async, T> { + SpiDma { + spi: self.spi, + channel: self.channel.into_async(), + tx_transfer_in_progress: self.tx_transfer_in_progress, + rx_transfer_in_progress: self.rx_transfer_in_progress, + #[cfg(all(esp32, spi_address_workaround))] + address_buffer: self.address_buffer, + } + } + } + + impl<'d, T> SpiDma<'d, Async, T> + where + T: Instance, + { + /// Converts the SPI instance into async mode. + pub fn into_blocking(self) -> SpiDma<'d, Blocking, T> { + SpiDma { + spi: self.spi, + channel: self.channel.into_blocking(), + tx_transfer_in_progress: self.tx_transfer_in_progress, + rx_transfer_in_progress: self.rx_transfer_in_progress, + #[cfg(all(esp32, spi_address_workaround))] + address_buffer: self.address_buffer, + } + } + } + + impl core::fmt::Debug for SpiDma<'_, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { /// Formats the `SpiDma` instance for debugging purposes. @@ -858,9 +942,52 @@ mod dma { } } + impl InterruptConfigurable for SpiDma<'_, Blocking, T> + where + T: Instance, + { + /// Sets the interrupt handler + /// + /// Interrupts are not enabled at the peripheral level here. + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + let interrupt = self.driver().interrupt; + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); + } + unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(interrupt, handler.priority())); + } + } + + #[cfg(gdma)] + impl SpiDma<'_, Blocking, T> + where + T: Instance, + { + /// Listen for the given interrupts + pub fn listen(&mut self, interrupts: impl Into>) { + self.driver().enable_listen(interrupts.into(), true); + } + + /// Unlisten the given interrupts + pub fn unlisten(&mut self, interrupts: impl Into>) { + self.driver().enable_listen(interrupts.into(), false); + } + + /// Gets asserted interrupts + pub fn interrupts(&mut self) -> EnumSet { + self.driver().interrupts() + } + + /// Resets asserted interrupts + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { + self.driver().clear_interrupts(interrupts.into()); + } + } + impl<'d, M, T> SpiDma<'d, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { pub(super) fn new(spi: PeripheralRef<'d, T>, channel: Channel<'d, CH, M>) -> Self @@ -876,7 +1003,11 @@ mod dma { [[DmaDescriptor::EMPTY]; SPI_NUM]; static mut BUFFERS: [[u32; 1]; SPI_NUM] = [[0; 1]; SPI_NUM]; - let id = spi.spi_num() as usize - 2; + let id = if spi.info() == unsafe { crate::peripherals::SPI2::steal().info() } { + 0 + } else { + 1 + }; unwrap!(DmaTxBuf::new( unsafe { &mut DESCRIPTORS[id] }, @@ -894,42 +1025,22 @@ mod dma { } } - /// Sets the interrupt handler - /// - /// Interrupts are not enabled at the peripheral level here. - pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - self.spi.set_interrupt_handler(handler); - } - - /// Listen for the given interrupts - #[cfg(gdma)] - pub fn listen(&mut self, interrupts: EnumSet) { - self.spi.listen(interrupts); - } - - /// Unlisten the given interrupts - #[cfg(gdma)] - pub fn unlisten(&mut self, interrupts: EnumSet) { - self.spi.unlisten(interrupts); - } - - /// Gets asserted interrupts - #[cfg(gdma)] - pub fn interrupts(&mut self) -> EnumSet { - self.spi.interrupts() + fn driver(&self) -> &'static Info { + self.spi.info() } - /// Resets asserted interrupts - #[cfg(gdma)] - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - self.spi.clear_interrupts(interrupts); + fn dma_driver(&self) -> DmaDriver { + DmaDriver { + info: self.driver(), + dma_peripheral: self.spi.dma_peripheral(), + } } fn is_done(&self) -> bool { if self.tx_transfer_in_progress && !self.channel.tx.is_done() { return false; } - if self.spi.busy() { + if self.driver().busy() { return false; } if self.rx_transfer_in_progress { @@ -991,10 +1102,17 @@ mod dma { rx_buffer: &mut RX, tx_buffer: &mut TX, ) -> Result<(), Error> { - self.rx_transfer_in_progress = rx_buffer.length() > 0; - self.tx_transfer_in_progress = tx_buffer.length() > 0; + let bytes_to_read = rx_buffer.length(); + let bytes_to_write = tx_buffer.length(); + + if bytes_to_read > MAX_DMA_SIZE || bytes_to_write > MAX_DMA_SIZE { + return Err(Error::MaxDmaTransferSizeExceeded); + } + + self.rx_transfer_in_progress = bytes_to_read > 0; + self.tx_transfer_in_progress = bytes_to_write > 0; unsafe { - self.spi.start_transfer_dma( + self.dma_driver().start_transfer_dma( full_duplex, rx_buffer, tx_buffer, @@ -1018,7 +1136,7 @@ mod dma { let addr_bytes = &addr_bytes[4 - bytes_to_write..][..bytes_to_write]; self.address_buffer.fill(addr_bytes); - self.spi.setup_half_duplex( + self.driver().setup_half_duplex( true, cmd, Address::None, @@ -1028,9 +1146,11 @@ mod dma { address.mode(), ); + // FIXME: we could use self.start_transfer_dma if the address buffer was part of + // the (yet-to-be-created) State struct. self.tx_transfer_in_progress = true; unsafe { - self.spi.start_transfer_dma( + self.dma_driver().start_transfer_dma( false, &mut EmptyBuf, &mut self.address_buffer, @@ -1048,8 +1168,7 @@ mod dma { // 1 seems to stop after transmitting the current byte which is somewhat less // impolite. if self.tx_transfer_in_progress || self.rx_transfer_in_progress { - self.spi.configure_datalen(1, 1); - self.spi.update(); + self.dma_driver().abort_transfer(); // We need to stop the DMA transfer, too. if self.tx_transfer_in_progress { @@ -1062,34 +1181,10 @@ mod dma { } } } - } - - impl<'d, M, T> crate::private::Sealed for SpiDma<'d, M, T> - where - T: InstanceDma, - M: Mode, - { - } - - impl<'d, M, T> InterruptConfigurable for SpiDma<'d, M, T> - where - T: InstanceDma, - M: Mode, - { - /// Configures the interrupt handler for the DMA-enabled SPI instance. - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - SpiDma::set_interrupt_handler(self, handler); - } - } - impl<'d, M, T> SpiDma<'d, M, T> - where - T: InstanceDma, - M: Mode, - { - /// Changes the SPI bus frequency for the DMA-enabled SPI instance. - pub fn change_bus_frequency(&mut self, frequency: HertzU32) { - self.spi.ch_bus_freq(frequency); + /// Change the bus configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.driver().apply_config(config) } /// Configures the DMA buffers for the SPI instance. @@ -1106,13 +1201,26 @@ mod dma { } } + impl SetConfig for SpiDma<'_, M, T> + where + T: Instance, + M: Mode, + { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } + } + /// A structure representing a DMA transfer for SPI. /// /// This structure holds references to the SPI instance, DMA buffers, and /// transfer status. pub struct SpiDmaTransfer<'d, M, Buf, T = AnySpi> where - T: InstanceDma, + T: Instance, M: Mode, { spi_dma: ManuallyDrop>, @@ -1121,7 +1229,7 @@ mod dma { impl<'d, M, T, Buf> SpiDmaTransfer<'d, M, Buf, T> where - T: InstanceDma, + T: Instance, M: Mode, { fn new(spi_dma: SpiDma<'d, M, T>, dma_buf: Buf) -> Self { @@ -1163,9 +1271,9 @@ mod dma { } } - impl<'d, M, T, Buf> Drop for SpiDmaTransfer<'d, M, Buf, T> + impl Drop for SpiDmaTransfer<'_, M, Buf, T> where - T: InstanceDma, + T: Instance, M: Mode, { fn drop(&mut self) { @@ -1181,9 +1289,9 @@ mod dma { } } - impl<'d, T, Buf> SpiDmaTransfer<'d, crate::Async, Buf, T> + impl SpiDmaTransfer<'_, Async, Buf, T> where - T: InstanceDma, + T: Instance, { /// Waits for the DMA transfer to complete asynchronously. /// @@ -1195,7 +1303,7 @@ mod dma { impl<'d, M, T> SpiDma<'d, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { /// # Safety: @@ -1204,18 +1312,7 @@ mod dma { /// transfer is in progress. Moving the buffers is allowed. #[cfg_attr(place_spi_driver_in_ram, ram)] unsafe fn start_dma_write(&mut self, buffer: &mut impl DmaTxBuffer) -> Result<(), Error> { - let bytes_to_write = buffer.length(); - if bytes_to_write > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - - self.spi.start_transfer_dma( - true, - &mut EmptyBuf, - buffer, - &mut self.channel.rx, - &mut self.channel.tx, - ) + self.start_dma_transfer(&mut EmptyBuf, buffer) } /// Perform a DMA write. @@ -1243,18 +1340,7 @@ mod dma { /// transfer is in progress. Moving the buffers is allowed. #[cfg_attr(place_spi_driver_in_ram, ram)] unsafe fn start_dma_read(&mut self, buffer: &mut impl DmaRxBuffer) -> Result<(), Error> { - let bytes_to_read = buffer.length(); - if bytes_to_read > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - - self.spi.start_transfer_dma( - false, - buffer, - &mut EmptyBuf, - &mut self.channel.rx, - &mut self.channel.tx, - ) + self.start_dma_transfer(buffer, &mut EmptyBuf) } /// Perform a DMA read. @@ -1285,13 +1371,6 @@ mod dma { rx_buffer: &mut impl DmaRxBuffer, tx_buffer: &mut impl DmaTxBuffer, ) -> Result<(), Error> { - let bytes_to_read = rx_buffer.length(); - let bytes_to_write = tx_buffer.length(); - - if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - self.start_transfer_dma(true, rx_buffer, tx_buffer) } @@ -1313,13 +1392,7 @@ mod dma { Err(e) => Err((e, self, rx_buffer, tx_buffer)), } } - } - impl<'d, M, T> SpiDma<'d, M, T> - where - T: InstanceDma, - M: Mode, - { /// # Safety: /// /// The caller must ensure that the buffers are not accessed while the @@ -1334,11 +1407,8 @@ mod dma { buffer: &mut impl DmaRxBuffer, ) -> Result<(), Error> { let bytes_to_read = buffer.length(); - if bytes_to_read > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } - self.spi.setup_half_duplex( + self.driver().setup_half_duplex( false, cmd, address, @@ -1348,13 +1418,7 @@ mod dma { data_mode, ); - self.spi.start_transfer_dma( - false, - buffer, - &mut EmptyBuf, - &mut self.channel.rx, - &mut self.channel.tx, - ) + self.start_transfer_dma(false, buffer, &mut EmptyBuf) } /// Perform a half-duplex read operation using DMA. @@ -1392,9 +1456,6 @@ mod dma { buffer: &mut impl DmaTxBuffer, ) -> Result<(), Error> { let bytes_to_write = buffer.length(); - if bytes_to_write > MAX_DMA_SIZE { - return Err(Error::MaxDmaTransferSizeExceeded); - } #[cfg(all(esp32, spi_address_workaround))] { @@ -1405,7 +1466,7 @@ mod dma { } } - self.spi.setup_half_duplex( + self.driver().setup_half_duplex( true, cmd, address, @@ -1415,13 +1476,7 @@ mod dma { data_mode, ); - self.spi.start_transfer_dma( - false, - &mut EmptyBuf, - buffer, - &mut self.channel.rx, - &mut self.channel.tx, - ) + self.start_transfer_dma(false, &mut EmptyBuf, buffer) } /// Perform a half-duplex write operation using DMA. @@ -1452,7 +1507,7 @@ mod dma { /// buffers. pub struct SpiDmaBus<'d, M, T = AnySpi> where - T: InstanceDma, + T: Instance, M: Mode, { @@ -1461,9 +1516,44 @@ mod dma { tx_buf: DmaTxBuf, } + impl crate::private::Sealed for SpiDmaBus<'_, M, T> + where + T: Instance, + M: Mode, + { + } + + impl<'d, T> SpiDmaBus<'d, Blocking, T> + where + T: Instance, + { + /// Converts the SPI instance into async mode. + pub fn into_async(self) -> SpiDmaBus<'d, Async, T> { + SpiDmaBus { + spi_dma: self.spi_dma.into_async(), + rx_buf: self.rx_buf, + tx_buf: self.tx_buf, + } + } + } + + impl<'d, T> SpiDmaBus<'d, Async, T> + where + T: Instance, + { + /// Converts the SPI instance into async mode. + pub fn into_blocking(self) -> SpiDmaBus<'d, Blocking, T> { + SpiDmaBus { + spi_dma: self.spi_dma.into_blocking(), + rx_buf: self.rx_buf, + tx_buf: self.tx_buf, + } + } + } + impl<'d, M, T> SpiDmaBus<'d, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { /// Creates a new `SpiDmaBus` with the specified SPI instance and DMA @@ -1475,71 +1565,60 @@ mod dma { tx_buf, } } + } - fn wait_for_idle(&mut self) { - self.spi_dma.wait_for_idle(); - } - + impl InterruptConfigurable for SpiDmaBus<'_, Blocking, T> + where + T: Instance, + { /// Sets the interrupt handler /// /// Interrupts are not enabled at the peripheral level here. - pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { self.spi_dma.set_interrupt_handler(handler); } + } + #[cfg(gdma)] + impl SpiDmaBus<'_, Blocking, T> + where + T: Instance, + { /// Listen for the given interrupts - #[cfg(gdma)] - pub fn listen(&mut self, interrupts: EnumSet) { - self.spi_dma.listen(interrupts); + pub fn listen(&mut self, interrupts: impl Into>) { + self.spi_dma.listen(interrupts.into()); } /// Unlisten the given interrupts - #[cfg(gdma)] - pub fn unlisten(&mut self, interrupts: EnumSet) { - self.spi_dma.unlisten(interrupts); + pub fn unlisten(&mut self, interrupts: impl Into>) { + self.spi_dma.unlisten(interrupts.into()); } /// Gets asserted interrupts - #[cfg(gdma)] pub fn interrupts(&mut self) -> EnumSet { self.spi_dma.interrupts() } /// Resets asserted interrupts - #[cfg(gdma)] - pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - self.spi_dma.clear_interrupts(interrupts); - } - - /// Changes the SPI bus frequency for the DMA-enabled SPI instance. - pub fn change_bus_frequency(&mut self, frequency: HertzU32) { - self.spi_dma.change_bus_frequency(frequency); + pub fn clear_interrupts(&mut self, interrupts: impl Into>) { + self.spi_dma.clear_interrupts(interrupts.into()); } } - impl<'d, M, T> InterruptConfigurable for SpiDmaBus<'d, M, T> + impl SpiDmaBus<'_, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { - /// Configures the interrupt handler for the DMA-enabled SPI instance. - fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - SpiDma::set_interrupt_handler(&mut self.spi_dma, handler); + fn wait_for_idle(&mut self) { + self.spi_dma.wait_for_idle(); } - } - impl<'d, M, T> crate::private::Sealed for SpiDmaBus<'d, M, T> - where - T: InstanceDma, - M: Mode, - { - } + /// Change the bus configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> { + self.spi_dma.apply_config(config) + } - impl<'d, M, T> SpiDmaBus<'d, M, T> - where - T: InstanceDma, - M: Mode, - { /// Reads data from the SPI bus using DMA. pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> { self.wait_for_idle(); @@ -1633,13 +1712,7 @@ mod dma { Ok(()) } - } - impl<'d, M, T> SpiDmaBus<'d, M, T> - where - T: InstanceDma, - M: Mode, - { /// Half-duplex read. pub fn half_duplex_read( &mut self, @@ -1704,9 +1777,22 @@ mod dma { } } - impl<'d, T> embedded_hal_02::blocking::spi::Transfer for SpiDmaBus<'d, crate::Blocking, T> + impl SetConfig for SpiDmaBus<'_, M, T> + where + T: Instance, + M: Mode, + { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } + } + + impl embedded_hal_02::blocking::spi::Transfer for SpiDmaBus<'_, Blocking, T> where - T: InstanceDma, + T: Instance, { type Error = Error; @@ -1716,9 +1802,9 @@ mod dma { } } - impl<'d, T> embedded_hal_02::blocking::spi::Write for SpiDmaBus<'d, crate::Blocking, T> + impl embedded_hal_02::blocking::spi::Write for SpiDmaBus<'_, Blocking, T> where - T: InstanceDma, + T: Instance, { type Error = Error; @@ -1736,6 +1822,7 @@ mod dma { }; use super::*; + use crate::Async; struct DropGuard { inner: ManuallyDrop, @@ -1775,9 +1862,9 @@ mod dma { } } - impl<'d, T> SpiDmaBus<'d, crate::Async, T> + impl SpiDmaBus<'_, Async, T> where - T: InstanceDma, + T: Instance, { /// Fill the given buffer with data from the bus. pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> { @@ -1889,9 +1976,9 @@ mod dma { } } - impl<'d, T> embedded_hal_async::spi::SpiBus for SpiDmaBus<'d, crate::Async, T> + impl embedded_hal_async::spi::SpiBus for SpiDmaBus<'_, Async, T> where - T: InstanceDma, + T: Instance, { async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.read_async(words).await @@ -1921,17 +2008,17 @@ mod dma { use super::*; - impl<'d, M, T> ErrorType for SpiDmaBus<'d, M, T> + impl ErrorType for SpiDmaBus<'_, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { type Error = Error; } - impl<'d, M, T> SpiBus for SpiDmaBus<'d, M, T> + impl SpiBus for SpiDmaBus<'_, M, T> where - T: InstanceDma, + T: Instance, M: Mode, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { @@ -1964,33 +2051,33 @@ mod ehal1 { use super::*; - impl embedded_hal::spi::ErrorType for Spi<'_, T> { + impl embedded_hal::spi::ErrorType for Spi<'_, M, T> { type Error = Error; } - impl FullDuplex for Spi<'_, T> + impl FullDuplex for Spi<'_, M, T> where T: Instance, { fn read(&mut self) -> nb::Result { - self.spi.read_byte() + self.driver().read_byte() } fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - self.spi.write_byte(word) + self.driver().write_byte(word) } } - impl SpiBus for Spi<'_, T> + impl SpiBus for Spi<'_, M, T> where T: Instance, { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { - self.spi.read_bytes(words) + self.driver().read_bytes(words) } fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { - self.spi.write_bytes(words) + self.driver().write_bytes(words) } fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { @@ -2029,7 +2116,7 @@ mod ehal1 { if read_inc > 0 { SpiBus::flush(self)?; - self.spi + self.driver() .read_bytes_from_fifo(&mut read[read_from..read_to])?; } @@ -2044,29 +2131,92 @@ mod ehal1 { for chunk in words.chunks_mut(FIFO_SIZE) { SpiBus::write(self, chunk)?; SpiBus::flush(self)?; - self.spi.read_bytes_from_fifo(chunk)?; + self.driver().read_bytes_from_fifo(chunk)?; } Ok(()) } fn flush(&mut self) -> Result<(), Self::Error> { - self.spi.flush() + self.driver().flush() } } } -#[doc(hidden)] -pub trait InstanceDma: Instance + DmaEligible { +/// SPI peripheral instance. +pub trait Instance: private::Sealed + Into + DmaEligible + 'static { + /// Returns the peripheral data describing this SPI instance. + fn info(&self) -> &'static Info; +} + +/// Marker trait for QSPI-capable SPI peripherals. +pub trait QspiInstance: Instance {} + +/// Peripheral data describing a particular SPI instance. +#[non_exhaustive] +pub struct Info { + /// Pointer to the register block for this SPI instance. + /// + /// Use [Self::register_block] to access the register block. + pub register_block: *const RegisterBlock, + + /// The system peripheral marker. + pub peripheral: crate::system::Peripheral, + + /// Interrupt for this SPI instance. + pub interrupt: crate::peripherals::Interrupt, + + /// SCLK signal. + pub sclk: OutputSignal, + + /// MOSI signal. + pub mosi: OutputSignal, + + /// MISO signal. + pub miso: InputSignal, + + /// Chip select signal. + pub cs: OutputSignal, + + /// SIO0 (MOSI) input signal for half-duplex mode. + pub sio0_input: InputSignal, + + /// SIO1 (MISO) output signal for half-duplex mode. + pub sio1_output: OutputSignal, + + /// SIO2 output signal for QSPI mode. + pub sio2_output: Option, + + /// SIO2 input signal for QSPI mode. + pub sio2_input: Option, + + /// SIO3 output signal for QSPI mode. + pub sio3_output: Option, + + /// SIO3 input signal for QSPI mode. + pub sio3_input: Option, +} + +struct DmaDriver { + info: &'static Info, + dma_peripheral: crate::dma::DmaPeripheral, +} + +impl DmaDriver { + fn abort_transfer(&self) { + self.info.configure_datalen(1, 1); + self.info.update(); + } + #[cfg_attr(place_spi_driver_in_ram, ram)] unsafe fn start_transfer_dma( - &mut self, + &self, _full_duplex: bool, rx_buffer: &mut impl DmaRxBuffer, tx_buffer: &mut impl DmaTxBuffer, rx: &mut RX, tx: &mut TX, ) -> Result<(), Error> { - let reg_block = self.register_block(); + let reg_block = self.info.register_block(); #[cfg(esp32s2)] { @@ -2077,7 +2227,7 @@ pub trait InstanceDma: Instance + DmaEligible { let rx_len = rx_buffer.length(); let tx_len = tx_buffer.length(); - self.configure_datalen(rx_len, tx_len); + self.info.configure_datalen(rx_len, tx_len); // enable the MISO and MOSI if needed reg_block @@ -2087,7 +2237,7 @@ pub trait InstanceDma: Instance + DmaEligible { self.enable_dma(); if rx_len > 0 { - rx.prepare_transfer(self.dma_peripheral(), rx_buffer) + rx.prepare_transfer(self.dma_peripheral, rx_buffer) .and_then(|_| rx.start_transfer())?; } else { #[cfg(esp32)] @@ -2105,14 +2255,14 @@ pub trait InstanceDma: Instance + DmaEligible { } } if tx_len > 0 { - tx.prepare_transfer(self.dma_peripheral(), tx_buffer) + tx.prepare_transfer(self.dma_peripheral, tx_buffer) .and_then(|_| tx.start_transfer())?; } #[cfg(gdma)] self.reset_dma(); - self.start_operation(); + self.info.start_operation(); Ok(()) } @@ -2121,7 +2271,7 @@ pub trait InstanceDma: Instance + DmaEligible { #[cfg(gdma)] { // for non GDMA this is done in `assign_tx_device` / `assign_rx_device` - let reg_block = self.register_block(); + let reg_block = self.info.register_block(); reg_block.dma_conf().modify(|_, w| { w.dma_tx_ena().set_bit(); w.dma_rx_ena().set_bit() @@ -2147,7 +2297,7 @@ pub trait InstanceDma: Instance + DmaEligible { w.dma_afifo_rst().bit(bit) }); } - let reg_block = self.register_block(); + let reg_block = self.info.register_block(); set_reset_bit(reg_block, true); set_reset_bit(reg_block, false); self.clear_dma_interrupts(); @@ -2155,7 +2305,7 @@ pub trait InstanceDma: Instance + DmaEligible { #[cfg(gdma)] fn clear_dma_interrupts(&self) { - let reg_block = self.register_block(); + let reg_block = self.info.register_block(); reg_block.dma_int_clr().write(|w| { w.dma_infifo_full_err().clear_bit_by_one(); w.dma_outfifo_empty_err().clear_bit_by_one(); @@ -2167,7 +2317,7 @@ pub trait InstanceDma: Instance + DmaEligible { #[cfg(pdma)] fn clear_dma_interrupts(&self) { - let reg_block = self.register_block(); + let reg_block = self.info.register_block(); reg_block.dma_int_clr().write(|w| { w.inlink_dscr_empty().clear_bit_by_one(); w.outlink_dscr_error().clear_bit_by_one(); @@ -2182,41 +2332,16 @@ pub trait InstanceDma: Instance + DmaEligible { } } -impl InstanceDma for crate::peripherals::SPI2 {} - -#[cfg(spi3)] -impl InstanceDma for crate::peripherals::SPI3 {} - -#[doc(hidden)] -pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static { - fn register_block(&self) -> &RegisterBlock; - - fn sclk_signal(&self) -> OutputSignal; - fn mosi_signal(&self) -> OutputSignal; - fn miso_signal(&self) -> InputSignal; - fn cs_signal(&self) -> OutputSignal; - - fn sio0_input_signal(&self) -> InputSignal; - fn sio1_output_signal(&self) -> OutputSignal; - fn sio2_output_signal(&self) -> Option; - fn sio2_input_signal(&self) -> Option; - fn sio3_output_signal(&self) -> Option; - fn sio3_input_signal(&self) -> Option; - - #[inline(always)] - fn enable_peripheral(&self) { - PeripheralClockControl::enable(self.peripheral()); - } - - #[inline(always)] - fn reset_peripheral(&self) { - PeripheralClockControl::reset(self.peripheral()); +// private implementation bits +// FIXME: split this up into peripheral versions +impl Info { + /// Returns the register block for this SPI instance. + pub fn register_block(&self) -> &RegisterBlock { + unsafe { &*self.register_block } } - fn spi_num(&self) -> u8; - /// Initialize for full-duplex 1 bit mode - fn init(&mut self) { + fn init(&self) { let reg_block = self.register_block(); reg_block.user().modify(|_, w| { w.usr_miso_highpart().clear_bit(); @@ -2241,7 +2366,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static unsafe { // use default clock source PLL_F80M_CLK (ESP32-C6) and // PLL_F48M_CLK (ESP32-H2) - (*crate::peripherals::PCR::PTR) + crate::peripherals::PCR::steal() .spi2_clkm_conf() .modify(|_, w| w.spi2_clkm_sel().bits(1)); } @@ -2271,7 +2396,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static #[cfg(not(esp32))] fn init_spi_data_mode( - &mut self, + &self, cmd_mode: SpiDataMode, address_mode: SpiDataMode, data_mode: SpiDataMode, @@ -2293,7 +2418,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static #[cfg(esp32)] fn init_spi_data_mode( - &mut self, + &self, cmd_mode: SpiDataMode, address_mode: SpiDataMode, data_mode: SpiDataMode, @@ -2341,7 +2466,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static } // taken from https://github.com/apache/incubator-nuttx/blob/8267a7618629838231256edfa666e44b5313348e/arch/risc-v/src/esp32c3/esp32c3_spi.c#L496 - fn setup(&mut self, frequency: HertzU32) { + fn setup(&self, frequency: HertzU32) { let clocks = Clocks::get(); cfg_if::cfg_if! { if #[cfg(esp32h2)] { @@ -2428,12 +2553,9 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static .write(|w| unsafe { w.bits(reg_val) }); } - /// Set the interrupt handler - fn set_interrupt_handler(&mut self, handler: InterruptHandler); - /// Enable or disable listening for the given interrupts. #[cfg(gdma)] - fn enable_listen(&mut self, interrupts: EnumSet, enable: bool) { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { let reg_block = self.register_block(); reg_block.dma_int_ena().modify(|_, w| { @@ -2446,21 +2568,9 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static }); } - /// Listen for the given interrupts - #[cfg(gdma)] - fn listen(&mut self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), true); - } - - /// Unlisten the given interrupts - #[cfg(gdma)] - fn unlisten(&mut self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), false); - } - /// Gets asserted interrupts #[cfg(gdma)] - fn interrupts(&mut self) -> EnumSet { + fn interrupts(&self) -> EnumSet { let mut res = EnumSet::new(); let reg_block = self.register_block(); @@ -2475,7 +2585,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static /// Resets asserted interrupts #[cfg(gdma)] - fn clear_interrupts(&mut self, interrupts: EnumSet) { + fn clear_interrupts(&self, interrupts: EnumSet) { let reg_block = self.register_block(); reg_block.dma_int_clr().write(|w| { @@ -2488,7 +2598,14 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static }); } - fn set_data_mode(&mut self, data_mode: SpiMode) -> &mut Self { + fn apply_config(&self, config: &Config) -> Result<(), ConfigError> { + self.ch_bus_freq(config.frequency); + self.set_bit_order(config.read_bit_order, config.write_bit_order); + self.set_data_mode(config.mode); + Ok(()) + } + + fn set_data_mode(&self, data_mode: SpiMode) { let reg_block = self.register_block(); cfg_if::cfg_if! { @@ -2507,11 +2624,9 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static w.ck_out_edge() .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode2)) }); - - self } - fn ch_bus_freq(&mut self, frequency: HertzU32) { + fn ch_bus_freq(&self, frequency: HertzU32) { fn enable_clocks(_reg_block: &RegisterBlock, _enable: bool) { #[cfg(gdma)] _reg_block.clk_gate().modify(|_, w| { @@ -2530,7 +2645,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static } #[cfg(not(any(esp32, esp32c3, esp32s2)))] - fn set_bit_order(&mut self, read_order: SpiBitOrder, write_order: SpiBitOrder) { + fn set_bit_order(&self, read_order: SpiBitOrder, write_order: SpiBitOrder) { let reg_block = self.register_block(); let read_value = match read_order { @@ -2547,8 +2662,9 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static w }); } + #[cfg(any(esp32, esp32c3, esp32s2))] - fn set_bit_order(&mut self, read_order: SpiBitOrder, write_order: SpiBitOrder) { + fn set_bit_order(&self, read_order: SpiBitOrder, write_order: SpiBitOrder) { let reg_block = self.register_block(); let read_value = match read_order { @@ -2566,7 +2682,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static }); } - fn read_byte(&mut self) -> nb::Result { + fn read_byte(&self) -> nb::Result { if self.busy() { return Err(nb::Error::WouldBlock); } @@ -2575,7 +2691,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static Ok(u32::try_into(reg_block.w(0).read().bits()).unwrap_or_default()) } - fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { + fn write_byte(&self, word: u8) -> nb::Result<(), Error> { if self.busy() { return Err(nb::Error::WouldBlock); } @@ -2599,7 +2715,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static /// you must ensure that the whole messages was written correctly, use /// [`Self::flush`]. #[cfg_attr(place_spi_driver_in_ram, ram)] - fn write_bytes(&mut self, words: &[u8]) -> Result<(), Error> { + fn write_bytes(&self, words: &[u8]) -> Result<(), Error> { let num_chunks = words.len() / FIFO_SIZE; // Flush in case previous writes have not completed yet, required as per @@ -2656,7 +2772,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static /// perform flushing. If you want to read the response to something you /// have written before, consider using [`Self::transfer`] instead. #[cfg_attr(place_spi_driver_in_ram, ram)] - fn read_bytes(&mut self, words: &mut [u8]) -> Result<(), Error> { + fn read_bytes(&self, words: &mut [u8]) -> Result<(), Error> { let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE]; for chunk in words.chunks_mut(FIFO_SIZE) { @@ -2674,7 +2790,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static /// something you have written before, consider using [`Self::transfer`] /// instead. #[cfg_attr(place_spi_driver_in_ram, ram)] - fn read_bytes_from_fifo(&mut self, words: &mut [u8]) -> Result<(), Error> { + fn read_bytes_from_fifo(&self, words: &mut [u8]) -> Result<(), Error> { let reg_block = self.register_block(); for chunk in words.chunks_mut(FIFO_SIZE) { @@ -2698,7 +2814,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static } // Check if the bus is busy and if it is wait for it to be idle - fn flush(&mut self) -> Result<(), Error> { + fn flush(&self) -> Result<(), Error> { while self.busy() { // wait for bus to be clear } @@ -2706,7 +2822,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static } #[cfg_attr(place_spi_driver_in_ram, ram)] - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error> { + fn transfer<'w>(&self, words: &'w mut [u8]) -> Result<&'w [u8], Error> { for chunk in words.chunks_mut(FIFO_SIZE) { self.write_bytes(chunk)?; self.flush()?; @@ -2724,7 +2840,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static #[allow(clippy::too_many_arguments)] fn setup_half_duplex( - &mut self, + &self, is_write: bool, cmd: Command, address: Address, @@ -2758,7 +2874,7 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static #[cfg(any(esp32c6, esp32h2))] unsafe { - let pcr = &*crate::peripherals::PCR::PTR; + let pcr = crate::peripherals::PCR::steal(); // use default clock source PLL_F80M_CLK pcr.spi2_clkm_conf() @@ -2773,7 +2889,37 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static self.update(); // set cmd, address, dummy cycles - set_up_common_phases(reg_block, cmd, address, dummy); + self.set_up_common_phases(cmd, address, dummy); + } + + fn set_up_common_phases(&self, cmd: Command, address: Address, dummy: u8) { + let reg_block = self.register_block(); + if !cmd.is_none() { + reg_block.user2().modify(|_, w| unsafe { + w.usr_command_bitlen().bits((cmd.width() - 1) as u8); + w.usr_command_value().bits(cmd.value()) + }); + } + + if !address.is_none() { + reg_block + .user1() + .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) }); + + let addr = address.value() << (32 - address.width()); + #[cfg(not(esp32))] + reg_block + .addr() + .write(|w| unsafe { w.usr_addr_value().bits(addr) }); + #[cfg(esp32)] + reg_block.addr().write(|w| unsafe { w.bits(addr) }); + } + + if dummy > 0 { + reg_block + .user1() + .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); + } } fn update(&self) { @@ -2831,38 +2977,14 @@ pub trait Instance: private::Sealed + PeripheralMarker + Into + 'static } } -#[doc(hidden)] -pub trait QspiInstance: Instance {} - -fn set_up_common_phases(reg_block: &RegisterBlock, cmd: Command, address: Address, dummy: u8) { - if !cmd.is_none() { - reg_block.user2().modify(|_, w| unsafe { - w.usr_command_bitlen().bits((cmd.width() - 1) as u8); - w.usr_command_value().bits(cmd.value()) - }); - } - - if !address.is_none() { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) }); - - let addr = address.value() << (32 - address.width()); - #[cfg(not(esp32))] - reg_block - .addr() - .write(|w| unsafe { w.usr_addr_value().bits(addr) }); - #[cfg(esp32)] - reg_block.addr().write(|w| unsafe { w.bits(addr) }); - } - - if dummy > 0 { - reg_block - .user1() - .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) }); +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.register_block == other.register_block } } +unsafe impl Sync for Info {} + macro_rules! spi_instance { (@ignore $sio2:ident) => {}; @@ -2870,69 +2992,24 @@ macro_rules! spi_instance { paste::paste! { impl Instance for crate::peripherals::[] { #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn spi_num(&self) -> u8 { - $num - } - - #[inline(always)] - fn set_interrupt_handler(&mut self, handler: InterruptHandler) { - self.[](handler.handler()); - crate::interrupt::enable(crate::peripherals::Interrupt::[], handler.priority()).unwrap(); - } - - #[inline(always)] - fn sclk_signal(&self) -> OutputSignal { - OutputSignal::$sclk - } - - #[inline(always)] - fn mosi_signal(&self) -> OutputSignal { - OutputSignal::$mosi - } - - #[inline(always)] - fn miso_signal(&self) -> InputSignal { - InputSignal::$miso - } - - #[inline(always)] - fn cs_signal(&self) -> OutputSignal { - OutputSignal::$cs - } - - #[inline(always)] - fn sio0_input_signal(&self) -> InputSignal { - InputSignal::$mosi - } - - #[inline(always)] - fn sio1_output_signal(&self) -> OutputSignal { - OutputSignal::$miso - } - - #[inline(always)] - fn sio2_output_signal(&self) -> Option { - if_set!($(Some(OutputSignal::$sio2))?, None) - } - - #[inline(always)] - fn sio2_input_signal(&self) -> Option { - if_set!($(Some(InputSignal::$sio2))?, None) - } - - #[inline(always)] - fn sio3_output_signal(&self) -> Option { - if_set!($(Some(OutputSignal::$sio3))?, None) - } - - #[inline(always)] - fn sio3_input_signal(&self) -> Option { - if_set!($(Some(InputSignal::$sio3))?, None) + fn info(&self) -> &'static Info { + static INFO: Info = Info { + register_block: crate::peripherals::[]::PTR, + peripheral: crate::system::Peripheral::[], + interrupt: crate::peripherals::Interrupt::[], + sclk: OutputSignal::$sclk, + mosi: OutputSignal::$mosi, + miso: InputSignal::$miso, + cs: OutputSignal::$cs, + sio0_input: InputSignal::$mosi, + sio1_output: OutputSignal::$miso, + sio2_output: if_set!($(Some(OutputSignal::$sio2))?, None), + sio2_input: if_set!($(Some(InputSignal::$sio2))?, None), + sio3_output: if_set!($(Some(OutputSignal::$sio3))?, None), + sio3_input: if_set!($(Some(InputSignal::$sio3))?, None), + }; + + &INFO } } @@ -2985,42 +3062,9 @@ impl Instance for super::AnySpi { #[cfg(spi3)] super::AnySpiInner::Spi3(spi) => spi, } { - fn register_block(&self) -> &RegisterBlock; - fn spi_num(&self) -> u8; - fn sclk_signal(&self) -> OutputSignal; - fn mosi_signal(&self) -> OutputSignal; - fn miso_signal(&self) -> InputSignal; - fn cs_signal(&self) -> OutputSignal; - - fn sio0_input_signal(&self) -> InputSignal; - fn sio1_output_signal(&self) -> OutputSignal; - fn sio2_output_signal(&self) -> Option; - fn sio2_input_signal(&self) -> Option; - fn sio3_output_signal(&self) -> Option; - fn sio3_input_signal(&self) -> Option; - } - } - delegate::delegate! { - to match &mut self.0 { - super::AnySpiInner::Spi2(spi) => spi, - #[cfg(spi3)] - super::AnySpiInner::Spi3(spi) => spi, - } { - fn set_interrupt_handler(&mut self, handler: InterruptHandler); + fn info(&self) -> &'static Info; } } } impl QspiInstance for super::AnySpi {} - -impl InstanceDma for super::AnySpi { - delegate::delegate! { - to match &self.0 { - super::AnySpiInner::Spi2(spi) => spi, - #[cfg(spi3)] - super::AnySpiInner::Spi3(spi) => spi, - } { - fn clear_dma_interrupts(&self); - } - } -} diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 97315ab6f92..0ffd707d1eb 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -20,29 +20,32 @@ //! # use esp_hal::spi::SpiMode; //! # use esp_hal::spi::slave::Spi; //! # use esp_hal::dma::Dma; -//! # use esp_hal::gpio::Io; //! let dma = Dma::new(peripherals.DMA); #![cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")] #![cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")] -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let sclk = io.pins.gpio0; -//! let miso = io.pins.gpio1; -//! let mosi = io.pins.gpio2; -//! let cs = io.pins.gpio3; +//! let sclk = peripherals.GPIO0; +//! let miso = peripherals.GPIO1; +//! let mosi = peripherals.GPIO2; +//! let cs = peripherals.GPIO3; //! //! let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = -//! dma_buffers!(32000); let mut spi = Spi::new( +//! dma_buffers!(32000); +//! let mut spi = Spi::new( //! peripherals.SPI2, -//! sclk, -//! mosi, -//! miso, -//! cs, //! SpiMode::Mode0, //! ) -//! .with_dma(dma_channel.configure( -//! false, -//! DmaPriority::Priority0, -//! ), rx_descriptors, tx_descriptors); +//! .with_sck(sclk) +//! .with_mosi(mosi) +//! .with_miso(miso) +//! .with_cs(cs) +//! .with_dma( +//! dma_channel.configure( +//! false, +//! DmaPriority::Priority0, +//! ), +//! rx_descriptors, +//! tx_descriptors, +//! ); //! //! let mut receive = rx_buffer; //! let mut send = tx_buffer; @@ -70,12 +73,15 @@ //! //! See [tracking issue](https://github.com/esp-rs/esp-hal/issues/469) for more information. +use core::marker::PhantomData; + use super::{Error, SpiMode}; use crate::{ - dma::{DescriptorChain, DmaChannelConvert, DmaEligible, PeripheralMarker, Rx, Tx}, + dma::{DmaChannelConvert, DmaEligible}, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, + NoPin, OutputSignal, }, peripheral::{Peripheral, PeripheralRef}, @@ -83,6 +89,7 @@ use crate::{ private, spi::AnySpi, system::PeripheralClockControl, + Blocking, }; const MAX_DMA_SIZE: usize = 32768 - 32; @@ -90,84 +97,76 @@ const MAX_DMA_SIZE: usize = 32768 - 32; /// SPI peripheral driver. /// /// See the [module-level documentation][self] for more details. -pub struct Spi<'d, T = AnySpi> { +pub struct Spi<'d, M, T = AnySpi> { spi: PeripheralRef<'d, T>, #[allow(dead_code)] data_mode: SpiMode, + _mode: PhantomData, } -impl<'d> Spi<'d> { +impl<'d> Spi<'d, Blocking> { /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new< - SCK: PeripheralInput, - MOSI: PeripheralInput, - MISO: PeripheralOutput, - CS: PeripheralInput, - >( - spi: impl Peripheral

+ 'd, - sclk: impl Peripheral

+ 'd, - mosi: impl Peripheral

+ 'd, - miso: impl Peripheral

+ 'd, - cs: impl Peripheral

+ 'd, - mode: SpiMode, - ) -> Spi<'d> { - Self::new_typed(spi.map_into(), sclk, mosi, miso, cs, mode) + pub fn new(spi: impl Peripheral

+ 'd, mode: SpiMode) -> Spi<'d, Blocking> { + Self::new_typed(spi.map_into(), mode) } } -impl<'d, T> Spi<'d, T> +impl<'d, M, T> Spi<'d, M, T> where T: Instance, { /// Constructs an SPI instance in 8bit dataframe mode. - pub fn new_typed< - SCK: PeripheralInput, - MOSI: PeripheralInput, - MISO: PeripheralOutput, - CS: PeripheralInput, - >( - spi: impl Peripheral

+ 'd, - sclk: impl Peripheral

+ 'd, - mosi: impl Peripheral

+ 'd, - miso: impl Peripheral

+ 'd, - cs: impl Peripheral

+ 'd, - mode: SpiMode, - ) -> Spi<'d, T> { - crate::into_mapped_ref!(sclk, mosi, miso, cs); - - let this = Self::new_internal(spi, mode); - - // TODO: with_pins et. al. - sclk.enable_input(true, private::Internal); - sclk.connect_input_to_peripheral(this.spi.sclk_signal(), private::Internal); + pub fn new_typed(spi: impl Peripheral

+ 'd, mode: SpiMode) -> Spi<'d, M, T> { + crate::into_ref!(spi); - mosi.enable_input(true, private::Internal); - mosi.connect_input_to_peripheral(this.spi.mosi_signal(), private::Internal); + let this = Spi { + spi, + data_mode: mode, + _mode: PhantomData, + }; - miso.set_to_push_pull_output(private::Internal); - miso.connect_peripheral_to_output(this.spi.miso_signal(), private::Internal); + PeripheralClockControl::reset(this.spi.info().peripheral); + PeripheralClockControl::enable(this.spi.info().peripheral); - cs.enable_input(true, private::Internal); - cs.connect_input_to_peripheral(this.spi.cs_signal(), private::Internal); + this.spi.info().init(); + this.spi.info().set_data_mode(mode, false); - this + this.with_mosi(NoPin) + .with_miso(NoPin) + .with_sck(NoPin) + .with_cs(NoPin) } - pub(crate) fn new_internal(spi: impl Peripheral

+ 'd, mode: SpiMode) -> Spi<'d, T> { - crate::into_ref!(spi); - - let mut spi = Spi { - spi, - data_mode: mode, - }; + /// Assign the SCK (Serial Clock) pin for the SPI instance. + pub fn with_sck(self, sclk: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(sclk); + sclk.enable_input(true, private::Internal); + self.spi.info().sclk.connect_to(sclk); + self + } - PeripheralClockControl::reset(spi.spi.peripheral()); - PeripheralClockControl::enable(spi.spi.peripheral()); + /// Assign the MOSI (Master Out Slave In) pin for the SPI instance. + pub fn with_mosi(self, mosi: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(mosi); + mosi.enable_input(true, private::Internal); + self.spi.info().mosi.connect_to(mosi); + self + } - spi.spi.init(); - spi.spi.set_data_mode(mode, false); + /// Assign the MISO (Master In Slave Out) pin for the SPI instance. + pub fn with_miso(self, miso: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(miso); + miso.set_to_push_pull_output(private::Internal); + self.spi.info().miso.connect_to(miso); + self + } - spi + /// Assign the CS (Chip Select) pin for the SPI instance. + pub fn with_cs(self, cs: impl Peripheral

+ 'd) -> Self { + crate::into_mapped_ref!(cs); + cs.enable_input(true, private::Internal); + self.spi.info().cs.connect_to(cs); + self } } @@ -193,41 +192,43 @@ pub mod dma { Mode, }; - impl<'d, T> Spi<'d, T> + impl<'d, M, T> Spi<'d, M, T> where T: InstanceDma, + M: Mode, { /// Configures the SPI3 peripheral with the provided DMA channel and /// descriptors. #[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")] - pub fn with_dma( - mut self, - channel: Channel<'d, CH, DmaMode>, + pub fn with_dma( + self, + channel: Channel<'d, CH, DM>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], - ) -> SpiDma<'d, DmaMode, T> + ) -> SpiDma<'d, M, T> where CH: DmaChannelConvert, - DmaMode: Mode, + DM: Mode, + Channel<'d, CH, M>: From>, { - self.spi.set_data_mode(self.data_mode, true); - SpiDma::new(self.spi, channel, rx_descriptors, tx_descriptors) + self.spi.info().set_data_mode(self.data_mode, true); + SpiDma::new(self.spi, channel.into(), rx_descriptors, tx_descriptors) } } /// A DMA capable SPI instance. - pub struct SpiDma<'d, DmaMode, T = AnySpi> + pub struct SpiDma<'d, M, T = AnySpi> where T: InstanceDma, - DmaMode: Mode, + M: Mode, { pub(crate) spi: PeripheralRef<'d, T>, - pub(crate) channel: Channel<'d, T::Dma, DmaMode>, + pub(crate) channel: Channel<'d, T::Dma, M>, rx_chain: DescriptorChain, tx_chain: DescriptorChain, } - impl<'d, DmaMode, T> core::fmt::Debug for SpiDma<'d, DmaMode, T> + impl core::fmt::Debug for SpiDma<'_, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -237,7 +238,7 @@ pub mod dma { } } - impl<'d, DmaMode, T> DmaSupport for SpiDma<'d, DmaMode, T> + impl DmaSupport for SpiDma<'_, DmaMode, T> where T: InstanceDma, DmaMode: Mode, @@ -245,10 +246,10 @@ pub mod dma { fn peripheral_wait_dma(&mut self, is_rx: bool, is_tx: bool) { while !((!is_tx || self.channel.tx.is_done()) && (!is_rx || self.channel.rx.is_done()) - && !self.spi.is_bus_busy()) + && !self.spi.info().is_bus_busy()) {} - self.spi.flush().ok(); + self.spi.info().flush().ok(); } fn peripheral_dma_stop(&mut self) { @@ -310,6 +311,14 @@ pub mod dma { tx_chain: DescriptorChain::new(tx_descriptors), } } + + fn driver(&self) -> DmaDriver { + DmaDriver { + info: self.spi.info(), + dma_peripheral: self.spi.dma_peripheral(), + } + } + /// Register a buffer for a DMA write. /// /// This will return a [DmaTransferTx]. The maximum amount of data to be @@ -330,7 +339,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -365,7 +374,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -404,7 +413,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -419,163 +428,200 @@ pub mod dma { } } } -} -#[doc(hidden)] -pub trait InstanceDma: Instance + DmaEligible { - #[allow(clippy::too_many_arguments)] - unsafe fn start_transfer_dma( - &mut self, - rx_chain: &mut DescriptorChain, - tx_chain: &mut DescriptorChain, - read_buffer_ptr: *mut u8, - read_buffer_len: usize, - write_buffer_ptr: *const u8, - write_buffer_len: usize, - rx: &mut RX, - tx: &mut TX, - ) -> Result<(), Error> - where - RX: Rx, - TX: Tx, - { - let reg_block = self.register_block(); + struct DmaDriver { + info: &'static Info, + dma_peripheral: crate::dma::DmaPeripheral, + } - self.enable_dma(); + impl DmaDriver { + #[allow(clippy::too_many_arguments)] + unsafe fn start_transfer_dma( + &self, + rx_chain: &mut DescriptorChain, + tx_chain: &mut DescriptorChain, + read_buffer_ptr: *mut u8, + read_buffer_len: usize, + write_buffer_ptr: *const u8, + write_buffer_len: usize, + rx: &mut RX, + tx: &mut TX, + ) -> Result<(), Error> + where + RX: Rx, + TX: Tx, + { + self.enable_dma(); - reset_spi(reg_block); + self.info.reset_spi(); - if read_buffer_len > 0 { - rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; - } + if read_buffer_len > 0 { + rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; + rx.prepare_transfer_without_start(self.dma_peripheral, rx_chain)?; + } - if write_buffer_len > 0 { - tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; - } + if write_buffer_len > 0 { + tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; + tx.prepare_transfer_without_start(self.dma_peripheral, tx_chain)?; + } - #[cfg(esp32)] - self.prepare_length_and_lines(read_buffer_len, write_buffer_len); + #[cfg(esp32)] + self.info + .prepare_length_and_lines(read_buffer_len, write_buffer_len); - reset_dma_before_usr_cmd(reg_block); + self.reset_dma_before_usr_cmd(); - #[cfg(not(esp32))] - reg_block - .dma_conf() - .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); + let reg_block = self.info.register_block(); + #[cfg(not(esp32))] + reg_block + .dma_conf() + .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - self.clear_dma_interrupts(); - self.setup_for_flush(); - reg_block.cmd().modify(|_, w| w.usr().set_bit()); + self.clear_dma_interrupts(); + self.info.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); - if read_buffer_len > 0 { - rx.start_transfer()?; - } + if read_buffer_len > 0 { + rx.start_transfer()?; + } - if write_buffer_len > 0 { - tx.start_transfer()?; - } + if write_buffer_len > 0 { + tx.start_transfer()?; + } - Ok(()) - } + Ok(()) + } - fn enable_dma(&self) { - let reg_block = self.register_block(); - #[cfg(gdma)] - { + fn reset_dma_before_usr_cmd(&self) { + let reg_block = self.info.register_block(); + #[cfg(gdma)] reg_block.dma_conf().modify(|_, w| { - w.dma_tx_ena().set_bit(); - w.dma_rx_ena().set_bit(); - w.rx_eof_en().clear_bit() + w.rx_afifo_rst().set_bit(); + w.buf_afifo_rst().set_bit(); + w.dma_afifo_rst().set_bit() }); + + #[cfg(pdma)] + let _ = reg_block; } - #[cfg(pdma)] - { - fn set_rst_bit(reg_block: &RegisterBlock, bit: bool) { + fn enable_dma(&self) { + let reg_block = self.info.register_block(); + #[cfg(gdma)] + { reg_block.dma_conf().modify(|_, w| { - w.in_rst().bit(bit); - w.out_rst().bit(bit); - w.ahbm_fifo_rst().bit(bit); - w.ahbm_rst().bit(bit) + w.dma_tx_ena().set_bit(); + w.dma_rx_ena().set_bit(); + w.rx_eof_en().clear_bit() }); + } - #[cfg(esp32s2)] - reg_block - .dma_conf() - .modify(|_, w| w.dma_infifo_full_clr().bit(bit)); + #[cfg(pdma)] + { + fn set_rst_bit(reg_block: &RegisterBlock, bit: bool) { + reg_block.dma_conf().modify(|_, w| { + w.in_rst().bit(bit); + w.out_rst().bit(bit); + w.ahbm_fifo_rst().bit(bit); + w.ahbm_rst().bit(bit) + }); + + #[cfg(esp32s2)] + reg_block + .dma_conf() + .modify(|_, w| w.dma_infifo_full_clr().bit(bit)); + } + set_rst_bit(reg_block, true); + set_rst_bit(reg_block, false); } - set_rst_bit(reg_block, true); - set_rst_bit(reg_block, false); } - } - fn clear_dma_interrupts(&self) { - let reg_block = self.register_block(); + fn clear_dma_interrupts(&self) { + let reg_block = self.info.register_block(); - #[cfg(gdma)] - reg_block.dma_int_clr().write(|w| { - w.dma_infifo_full_err().clear_bit_by_one(); - w.dma_outfifo_empty_err().clear_bit_by_one(); - w.trans_done().clear_bit_by_one(); - w.mst_rx_afifo_wfull_err().clear_bit_by_one(); - w.mst_tx_afifo_rempty_err().clear_bit_by_one() - }); + #[cfg(gdma)] + reg_block.dma_int_clr().write(|w| { + w.dma_infifo_full_err().clear_bit_by_one(); + w.dma_outfifo_empty_err().clear_bit_by_one(); + w.trans_done().clear_bit_by_one(); + w.mst_rx_afifo_wfull_err().clear_bit_by_one(); + w.mst_tx_afifo_rempty_err().clear_bit_by_one() + }); - #[cfg(pdma)] - reg_block.dma_int_clr().write(|w| { - w.inlink_dscr_empty().clear_bit_by_one(); - w.outlink_dscr_error().clear_bit_by_one(); - w.inlink_dscr_error().clear_bit_by_one(); - w.in_done().clear_bit_by_one(); - w.in_err_eof().clear_bit_by_one(); - w.in_suc_eof().clear_bit_by_one(); - w.out_done().clear_bit_by_one(); - w.out_eof().clear_bit_by_one(); - w.out_total_eof().clear_bit_by_one() - }); + #[cfg(pdma)] + reg_block.dma_int_clr().write(|w| { + w.inlink_dscr_empty().clear_bit_by_one(); + w.outlink_dscr_error().clear_bit_by_one(); + w.inlink_dscr_error().clear_bit_by_one(); + w.in_done().clear_bit_by_one(); + w.in_err_eof().clear_bit_by_one(); + w.in_suc_eof().clear_bit_by_one(); + w.out_done().clear_bit_by_one(); + w.out_eof().clear_bit_by_one(); + w.out_total_eof().clear_bit_by_one() + }); + } } } -fn reset_spi(reg_block: &RegisterBlock) { - #[cfg(esp32)] - { - reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); - reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); - } - - #[cfg(not(esp32))] - { - reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); - reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); - } +/// SPI peripheral instance. +pub trait Instance: Peripheral

+ Into + 'static { + /// Returns the peripheral data describing this SPI instance. + fn info(&self) -> &'static Info; } -fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { - #[cfg(gdma)] - reg_block.dma_conf().modify(|_, w| { - w.rx_afifo_rst().set_bit(); - w.buf_afifo_rst().set_bit(); - w.dma_afifo_rst().set_bit() - }); - - #[cfg(pdma)] - let _ = reg_block; -} +/// A marker for DMA-capable SPI peripheral instances. +pub trait InstanceDma: Instance + DmaEligible {} impl InstanceDma for crate::peripherals::SPI2 {} #[cfg(spi3)] impl InstanceDma for crate::peripherals::SPI3 {} -#[doc(hidden)] -pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'static { - fn register_block(&self) -> &RegisterBlock; +/// Peripheral data describing a particular SPI instance. +#[non_exhaustive] +pub struct Info { + /// Pointer to the register block for this SPI instance. + /// + /// Use [Self::register_block] to access the register block. + pub register_block: *const RegisterBlock, + + /// System peripheral marker. + pub peripheral: crate::system::Peripheral, + + /// SCLK signal. + pub sclk: InputSignal, - fn sclk_signal(&self) -> InputSignal; - fn mosi_signal(&self) -> InputSignal; - fn miso_signal(&self) -> OutputSignal; - fn cs_signal(&self) -> InputSignal; + /// MOSI signal. + pub mosi: InputSignal, + + /// MISO signal. + pub miso: OutputSignal, + + /// Chip select signal. + pub cs: InputSignal, +} + +impl Info { + /// Returns the register block for this SPI instance. + pub fn register_block(&self) -> &RegisterBlock { + unsafe { &*self.register_block } + } + + fn reset_spi(&self) { + let reg_block = self.register_block(); + + #[cfg(esp32)] + { + reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); + reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); + } + + #[cfg(not(esp32))] + { + reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); + reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); + } + } #[cfg(esp32)] fn prepare_length_and_lines(&self, rx_len: usize, tx_len: usize) { @@ -596,7 +642,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } /// Initialize for full-duplex 1 bit mode - fn init(&mut self) { + fn init(&self) { let reg_block = self.register_block(); reg_block.clock().write(|w| unsafe { w.bits(0) }); @@ -609,7 +655,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st w.mode().set_bit() }); - reset_spi(reg_block); + self.reset_spi(); reg_block.user().modify(|_, w| { w.doutdin().set_bit(); @@ -620,7 +666,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st reg_block.misc().write(|w| unsafe { w.bits(0) }); } - fn set_data_mode(&mut self, data_mode: SpiMode, dma: bool) -> &mut Self { + fn set_data_mode(&self, data_mode: SpiMode, dma: bool) { let reg_block = self.register_block(); #[cfg(esp32)] { @@ -691,8 +737,6 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) }); } - - self } fn is_bus_busy(&self) -> bool { @@ -709,7 +753,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } // Check if the bus is busy and if it is wait for it to be idle - fn flush(&mut self) -> Result<(), Error> { + fn flush(&self) -> Result<(), Error> { while self.is_bus_busy() { // Wait for bus to be clear } @@ -730,107 +774,47 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } } -#[cfg(spi2)] -impl Instance for crate::peripherals::SPI2 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPICLK - } else { - InputSignal::FSPICLK - } - } - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPID - } else { - InputSignal::FSPID - } - } - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - OutputSignal::HSPIQ - } else { - OutputSignal::FSPIQ - } - } - } - - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPICS0 - } else { - InputSignal::FSPICS0 - } - } +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.register_block == other.register_block } } -#[cfg(spi3)] -impl Instance for crate::peripherals::SPI3 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPICLK - } else { - InputSignal::SPI3_CLK - } - } - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPID - } else { - InputSignal::SPI3_D - } - } - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - OutputSignal::VSPIQ - } else { - OutputSignal::SPI3_Q +unsafe impl Sync for Info {} + +macro_rules! spi_instance { + ($num:literal, $sclk:ident, $mosi:ident, $miso:ident, $cs:ident) => { + paste::paste! { + impl Instance for crate::peripherals::[] { + #[inline(always)] + fn info(&self) -> &'static Info { + static INFO: Info = Info { + register_block: crate::peripherals::[]::PTR, + peripheral: crate::system::Peripheral::[], + sclk: InputSignal::$sclk, + mosi: InputSignal::$mosi, + miso: OutputSignal::$miso, + cs: InputSignal::$cs, + }; + + &INFO + } } } - } + }; +} - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPICS0 - } else { - InputSignal::SPI3_CS0 - } - } +cfg_if::cfg_if! { + if #[cfg(esp32)] { + #[cfg(spi2)] + spi_instance!(2, HSPICLK, HSPID, HSPIQ, HSPICS0); + #[cfg(spi3)] + spi_instance!(3, VSPICLK, VSPID, VSPIQ, VSPICS0); + } else { + #[cfg(spi2)] + spi_instance!(2, FSPICLK, FSPID, FSPIQ, FSPICS0); + #[cfg(spi3)] + spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, SPI3_CS0); } } @@ -841,11 +825,7 @@ impl Instance for super::AnySpi { #[cfg(spi3)] super::AnySpiInner::Spi3(spi) => spi, } { - fn register_block(&self) -> &RegisterBlock; - fn sclk_signal(&self) -> InputSignal; - fn mosi_signal(&self) -> InputSignal; - fn miso_signal(&self) -> OutputSignal; - fn cs_signal(&self) -> InputSignal; + fn info(&self) -> &'static Info; } } } diff --git a/esp-hal/src/sync.rs b/esp-hal/src/sync.rs index 0b50edfba4d..111bd6f8edd 100644 --- a/esp-hal/src/sync.rs +++ b/esp-hal/src/sync.rs @@ -56,13 +56,13 @@ mod single_core { mod multicore { use portable_atomic::{AtomicUsize, Ordering}; - // Safety: Ensure that when adding new chips `get_raw_core` doesn't return this + // Safety: Ensure that when adding new chips `raw_core` doesn't return this // value. // FIXME: ensure in HIL tests this is the case! const UNUSED_THREAD_ID_VALUE: usize = 0x100; pub fn thread_id() -> usize { - crate::get_raw_core() + crate::raw_core() } pub(super) struct AtomicLock { @@ -248,7 +248,7 @@ pub(crate) fn lock(lock: &Lock, f: impl FnOnce() -> T) -> T { } } - impl<'a> Drop for LockGuard<'a> { + impl Drop for LockGuard<'_> { fn drop(&mut self) { unsafe { self.lock.release(self.token) }; } diff --git a/esp-hal/src/timer/mod.rs b/esp-hal/src/timer/mod.rs index d0ffb517c27..de0c4e4da6b 100644 --- a/esp-hal/src/timer/mod.rs +++ b/esp-hal/src/timer/mod.rs @@ -201,9 +201,9 @@ where } } -impl<'d, T> crate::private::Sealed for OneShotTimer<'d, T> where T: Timer {} +impl crate::private::Sealed for OneShotTimer<'_, T> where T: Timer {} -impl<'d, T> InterruptConfigurable for OneShotTimer<'d, T> +impl InterruptConfigurable for OneShotTimer<'_, T> where T: Timer, { @@ -212,7 +212,7 @@ where } } -impl<'d, T, UXX> embedded_hal_02::blocking::delay::DelayMs for OneShotTimer<'d, T> +impl embedded_hal_02::blocking::delay::DelayMs for OneShotTimer<'_, T> where T: Timer, UXX: Into, @@ -222,7 +222,7 @@ where } } -impl<'d, T, UXX> embedded_hal_02::blocking::delay::DelayUs for OneShotTimer<'d, T> +impl embedded_hal_02::blocking::delay::DelayUs for OneShotTimer<'_, T> where T: Timer, UXX: Into, @@ -232,7 +232,7 @@ where } } -impl<'d, T> embedded_hal::delay::DelayNs for OneShotTimer<'d, T> +impl embedded_hal::delay::DelayNs for OneShotTimer<'_, T> where T: Timer, { @@ -315,9 +315,9 @@ where } } -impl<'d, T> crate::private::Sealed for PeriodicTimer<'d, T> where T: Timer {} +impl crate::private::Sealed for PeriodicTimer<'_, T> where T: Timer {} -impl<'d, T> InterruptConfigurable for PeriodicTimer<'d, T> +impl InterruptConfigurable for PeriodicTimer<'_, T> where T: Timer, { @@ -326,7 +326,7 @@ where } } -impl<'d, T> embedded_hal_02::timer::CountDown for PeriodicTimer<'d, T> +impl embedded_hal_02::timer::CountDown for PeriodicTimer<'_, T> where T: Timer, { @@ -344,7 +344,7 @@ where } } -impl<'d, T> embedded_hal_02::timer::Cancel for PeriodicTimer<'d, T> +impl embedded_hal_02::timer::Cancel for PeriodicTimer<'_, T> where T: Timer, { @@ -355,7 +355,7 @@ where } } -impl<'d, T> embedded_hal_02::timer::Periodic for PeriodicTimer<'d, T> where T: Timer {} +impl embedded_hal_02::timer::Periodic for PeriodicTimer<'_, T> where T: Timer {} /// An enum of all timer types enum AnyTimerInner { diff --git a/esp-hal/src/timer/systimer.rs b/esp-hal/src/timer/systimer.rs index 7ecbd9f5ec2..d640502041e 100644 --- a/esp-hal/src/timer/systimer.rs +++ b/esp-hal/src/timer/systimer.rs @@ -170,13 +170,7 @@ impl<'d> SystemTimer<'d> { // an older time stamp let unit = unsafe { SpecificUnit::<'_, 0>::conjure() }; - - unit.update(); - loop { - if let Some(value) = unit.poll_count() { - break value; - } - } + unit.read_count() } } @@ -248,37 +242,29 @@ pub trait Unit { _ => unreachable!(), }, UnitConfig::DisabledIfCpuIsStalled(cpu) => match self.channel() { - 0 => w - .timer_unit0_work_en() - .set_bit() - .timer_unit0_core0_stall_en() - .bit(cpu == Cpu::ProCpu) - .timer_unit0_core1_stall_en() - .bit(cpu != Cpu::ProCpu), - 1 => w - .timer_unit1_work_en() - .set_bit() - .timer_unit1_core0_stall_en() - .bit(cpu == Cpu::ProCpu) - .timer_unit1_core1_stall_en() - .bit(cpu != Cpu::ProCpu), + 0 => { + w.timer_unit0_work_en().set_bit(); + w.timer_unit0_core0_stall_en().bit(cpu == Cpu::ProCpu); + w.timer_unit0_core1_stall_en().bit(cpu != Cpu::ProCpu) + } + 1 => { + w.timer_unit1_work_en().set_bit(); + w.timer_unit1_core0_stall_en().bit(cpu == Cpu::ProCpu); + w.timer_unit1_core1_stall_en().bit(cpu != Cpu::ProCpu) + } _ => unreachable!(), }, UnitConfig::Enabled => match self.channel() { - 0 => w - .timer_unit0_work_en() - .set_bit() - .timer_unit0_core0_stall_en() - .clear_bit() - .timer_unit0_core1_stall_en() - .clear_bit(), - 1 => w - .timer_unit1_work_en() - .set_bit() - .timer_unit1_core0_stall_en() - .clear_bit() - .timer_unit1_core1_stall_en() - .clear_bit(), + 0 => { + w.timer_unit0_work_en().set_bit(); + w.timer_unit0_core0_stall_en().clear_bit(); + w.timer_unit0_core1_stall_en().clear_bit() + } + 1 => { + w.timer_unit1_work_en().set_bit(); + w.timer_unit1_core0_stall_en().clear_bit(); + w.timer_unit1_core1_stall_en().clear_bit() + } _ => unreachable!(), }, }); @@ -317,48 +303,30 @@ pub trait Unit { } } - /// Update the value returned by [Self::poll_count] to be the current value - /// of the counter. - /// - /// This can be used to read the current value of the timer. - fn update(&self) { - let systimer = unsafe { &*SYSTIMER::ptr() }; - systimer - .unit_op(self.channel() as _) - .modify(|_, w| w.update().set_bit()); - } - - /// Return the count value at the time of the last call to [Self::update]. - /// - /// Returns None if the update isn't ready to read if update has never been - /// called. - fn poll_count(&self) -> Option { - let systimer = unsafe { &*SYSTIMER::ptr() }; - if systimer - .unit_op(self.channel() as _) - .read() - .value_valid() - .bit_is_set() - { - let unit_value = systimer.unit_value(self.channel() as _); - - let lo = unit_value.lo().read().bits(); - let hi = unit_value.hi().read().bits(); - - Some(((hi as u64) << 32) | lo as u64) - } else { - None - } - } - - /// Convenience method to call [Self::update] and [Self::poll_count]. + /// Reads the current counter value. fn read_count(&self) -> u64 { // This can be a shared reference as long as this type isn't Sync. - self.update(); + let channel = self.channel() as usize; + let systimer = unsafe { SYSTIMER::steal() }; + + systimer.unit_op(channel).write(|w| w.update().set_bit()); + while !systimer.unit_op(channel).read().value_valid().bit_is_set() {} + + // Read LO, HI, then LO again, check that LO returns the same value. + // This accounts for the case when an interrupt may happen between reading + // HI and LO values (or the other core updates the counter mid-read), and this + // function may get called from the ISR. In this case, the repeated read + // will return consistent values. + let unit_value = systimer.unit_value(channel); + let mut lo_prev = unit_value.lo().read().bits(); loop { - if let Some(count) = self.poll_count() { - break count; + let lo = lo_prev; + let hi = unit_value.hi().read().bits(); + lo_prev = unit_value.lo().read().bits(); + + if lo == lo_prev { + return ((hi as u64) << 32) | lo as u64; } } } @@ -368,13 +336,13 @@ pub trait Unit { #[derive(Debug)] pub struct SpecificUnit<'d, const CHANNEL: u8>(PhantomData<&'d ()>); -impl<'d, const CHANNEL: u8> SpecificUnit<'d, CHANNEL> { +impl SpecificUnit<'_, CHANNEL> { fn new() -> Self { Self(PhantomData) } } -impl<'d, const CHANNEL: u8> Unit for SpecificUnit<'d, CHANNEL> { +impl Unit for SpecificUnit<'_, CHANNEL> { fn channel(&self) -> u8 { CHANNEL } @@ -384,7 +352,7 @@ impl<'d, const CHANNEL: u8> Unit for SpecificUnit<'d, CHANNEL> { #[derive(Debug)] pub struct AnyUnit<'d>(PhantomData<&'d ()>, u8); -impl<'d> Unit for AnyUnit<'d> { +impl Unit for AnyUnit<'_> { fn channel(&self) -> u8 { self.1 } @@ -486,7 +454,7 @@ pub trait Comparator { /// Get the current mode of the comparator, which is either target or /// periodic. - fn get_mode(&self) -> ComparatorMode { + fn mode(&self) -> ComparatorMode { let tconf = unsafe { let systimer = &*SYSTIMER::ptr(); systimer.target_conf(self.channel() as usize) @@ -529,7 +497,7 @@ pub trait Comparator { } /// Get the actual target value of the comparator. - fn get_actual_target(&self) -> u64 { + fn actual_target(&self) -> u64 { let target = unsafe { let systimer = &*SYSTIMER::ptr(); systimer.trgt(self.channel() as usize) @@ -549,6 +517,10 @@ pub trait Comparator { _ => unreachable!(), }; + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); + } + #[cfg(not(esp32s2))] unsafe { interrupt::bind_interrupt(interrupt, handler.handler()); @@ -598,13 +570,13 @@ pub trait Comparator { #[derive(Debug)] pub struct SpecificComparator<'d, const CHANNEL: u8>(PhantomData<&'d ()>); -impl<'d, const CHANNEL: u8> SpecificComparator<'d, CHANNEL> { +impl SpecificComparator<'_, CHANNEL> { fn new() -> Self { Self(PhantomData) } } -impl<'d, const CHANNEL: u8> Comparator for SpecificComparator<'d, CHANNEL> { +impl Comparator for SpecificComparator<'_, CHANNEL> { fn channel(&self) -> u8 { CHANNEL } @@ -614,7 +586,7 @@ impl<'d, const CHANNEL: u8> Comparator for SpecificComparator<'d, CHANNEL> { #[derive(Debug)] pub struct AnyComparator<'d>(PhantomData<&'d ()>, u8); -impl<'d> Comparator for AnyComparator<'d> { +impl Comparator for AnyComparator<'_> { fn channel(&self) -> u8 { self.1 } @@ -737,7 +709,7 @@ where _pd: PhantomData<(MODE, DM)>, } -impl<'d, T, DM, COMP: Comparator, UNIT: Unit> Debug for Alarm<'d, T, DM, COMP, UNIT> +impl Debug for Alarm<'_, T, DM, COMP, UNIT> where DM: Mode, { @@ -771,9 +743,7 @@ impl<'d, T, COMP: Comparator, UNIT: Unit> Alarm<'d, T, Async, COMP, UNIT> { } } -impl<'d, T, COMP: Comparator, UNIT: Unit> InterruptConfigurable - for Alarm<'d, T, Blocking, COMP, UNIT> -{ +impl InterruptConfigurable for Alarm<'_, T, Blocking, COMP, UNIT> { fn set_interrupt_handler(&mut self, handler: InterruptHandler) { self.comparator.set_interrupt_handler(handler) } @@ -851,14 +821,12 @@ where } } -impl<'d, T, DM, COMP: Comparator, UNIT: Unit> crate::private::Sealed - for Alarm<'d, T, DM, COMP, UNIT> -where - DM: Mode, +impl crate::private::Sealed for Alarm<'_, T, DM, COMP, UNIT> where + DM: Mode { } -impl<'d, T, DM, COMP: Comparator, UNIT: Unit> super::Timer for Alarm<'d, T, DM, COMP, UNIT> +impl super::Timer for Alarm<'_, T, DM, COMP, UNIT> where DM: Mode, { @@ -895,13 +863,7 @@ where // This should be safe to access from multiple contexts; worst case // scenario the second accessor ends up reading an older time stamp. - self.unit.update(); - - let ticks = loop { - if let Some(value) = self.unit.poll_count() { - break value; - } - }; + let ticks = self.unit.read_count(); let us = ticks / (SystemTimer::ticks_per_second() / 1_000_000); @@ -909,7 +871,7 @@ where } fn load_value(&self, value: MicrosDurationU64) -> Result<(), Error> { - let mode = self.comparator.get_mode(); + let mode = self.comparator.mode(); let us = value.ticks(); let ticks = us * (SystemTimer::ticks_per_second() / 1_000_000); @@ -933,11 +895,6 @@ where } else { // Target mode - self.unit.update(); - while self.unit.poll_count().is_none() { - // Wait for value registers to update - } - // The counters/comparators are 52-bits wide (except on ESP32-S2, // which is 64-bits), so we must ensure that the provided value // is not too wide: @@ -946,7 +903,7 @@ where return Err(Error::InvalidTimeout); } - let v = self.unit.poll_count().unwrap(); + let v = self.unit.read_count(); let t = v + ticks; self.comparator.set_target(t); @@ -996,7 +953,7 @@ where } } -impl<'d, T, DM, COMP: Comparator, UNIT: Unit> Peripheral for Alarm<'d, T, DM, COMP, UNIT> +impl Peripheral for Alarm<'_, T, DM, COMP, UNIT> where DM: Mode, { @@ -1063,7 +1020,7 @@ mod asynch { } } - impl<'a, COMP: Comparator, UNIT: Unit> core::future::Future for AlarmFuture<'a, COMP, UNIT> { + impl core::future::Future for AlarmFuture<'_, COMP, UNIT> { type Output = (); fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { @@ -1077,8 +1034,8 @@ mod asynch { } } - impl<'d, COMP: Comparator, UNIT: Unit> embedded_hal_async::delay::DelayNs - for Alarm<'d, Target, crate::Async, COMP, UNIT> + impl embedded_hal_async::delay::DelayNs + for Alarm<'_, Target, crate::Async, COMP, UNIT> { async fn delay_ns(&mut self, nanos: u32) { self.set_target( @@ -1147,27 +1104,25 @@ pub mod etm { //! ## Example //! ```rust, no_run #![doc = crate::before_snippet!()] - //! # use esp_hal::timer::systimer::{etm::SysTimerEtmEvent, SystemTimer}; + //! # use esp_hal::timer::systimer::{etm::Event, SystemTimer}; //! # use fugit::ExtU32; //! let syst = SystemTimer::new(peripherals.SYSTIMER); //! let syst_alarms = syst.split(); //! let mut alarm0 = syst_alarms.alarm0.into_periodic(); //! alarm0.set_period(1u32.secs()); //! - //! let timer_event = SysTimerEtmEvent::new(&mut alarm0); + //! let timer_event = Event::new(&mut alarm0); //! # } //! ``` use super::*; /// An ETM controlled SYSTIMER event - pub struct SysTimerEtmEvent<'a, 'd, M, DM: crate::Mode, COMP, UNIT> { + pub struct Event<'a, 'd, M, DM: crate::Mode, COMP, UNIT> { alarm: &'a mut Alarm<'d, M, DM, COMP, UNIT>, } - impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> - SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> - { + impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> Event<'a, 'd, M, DM, COMP, UNIT> { /// Creates an ETM event from the given [Alarm] pub fn new(alarm: &'a mut Alarm<'d, M, DM, COMP, UNIT>) -> Self { Self { alarm } @@ -1180,13 +1135,13 @@ pub mod etm { } } - impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> crate::private::Sealed - for SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> + impl crate::private::Sealed + for Event<'_, '_, M, DM, COMP, UNIT> { } - impl<'a, 'd, M, DM: crate::Mode, COMP: Comparator, UNIT: Unit> crate::etm::EtmEvent - for SysTimerEtmEvent<'a, 'd, M, DM, COMP, UNIT> + impl crate::etm::EtmEvent + for Event<'_, '_, M, DM, COMP, UNIT> { fn id(&self) -> u8 { 50 + self.alarm.comparator.channel() diff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs index 96a7bb1c629..d51eec9e25e 100644 --- a/esp-hal/src/timer/timg.rs +++ b/esp-hal/src/timer/timg.rs @@ -50,11 +50,12 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::timer::timg::TimerGroup; +//! # use esp_hal::timer::timg::MwdtStage; //! //! let timg0 = TimerGroup::new(peripherals.TIMG0); //! let mut wdt = timg0.wdt; //! -//! wdt.set_timeout(5_000.millis()); +//! wdt.set_timeout(MwdtStage::Stage0, 5_000.millis()); //! wdt.enable(); //! //! loop { @@ -80,7 +81,7 @@ use crate::{ peripherals::{timg0::RegisterBlock, Interrupt, TIMG0}, private::Sealed, sync::{lock, Lock}, - system::{Peripheral as PeripheralEnable, PeripheralClockControl}, + system::PeripheralClockControl, Async, Blocking, InterruptConfigurable, @@ -151,7 +152,7 @@ impl TimerGroupInstance for TIMG0 { } fn enable_peripheral() { - crate::system::PeripheralClockControl::enable(crate::system::Peripheral::Timg0) + PeripheralClockControl::enable(crate::system::Peripheral::Timg0) } fn reset_peripheral() { @@ -214,11 +215,11 @@ impl TimerGroupInstance for crate::peripherals::TIMG1 { } fn enable_peripheral() { - crate::system::PeripheralClockControl::enable(crate::system::Peripheral::Timg1) + PeripheralClockControl::enable(crate::system::Peripheral::Timg1) } fn reset_peripheral() { - crate::system::PeripheralClockControl::reset(crate::system::Peripheral::Timg1) + PeripheralClockControl::reset(crate::system::Peripheral::Timg1) } fn configure_wdt_src_clk() { @@ -376,7 +377,6 @@ where { /// Construct a new instance of [`Timer`] pub fn new(timg: T, apb_clk_freq: HertzU32) -> Self { - timg.enable_peripheral(); timg.set_counter_active(true); Self { @@ -552,10 +552,11 @@ where _ => unreachable!(), }; - unsafe { - interrupt::bind_interrupt(interrupt, handler.handler()); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, interrupt); } - interrupt::enable(interrupt, handler.priority()).unwrap(); + unsafe { interrupt::bind_interrupt(interrupt, handler.handler()) }; + unwrap!(interrupt::enable(interrupt, handler.priority())); } fn is_interrupt_set(&self) -> bool { @@ -597,7 +598,7 @@ where } #[doc(hidden)] -pub trait Instance: Sealed + Enable { +pub trait Instance: Sealed { fn register_block(&self) -> &RegisterBlock; fn timer_group(&self) -> u8; @@ -635,11 +636,6 @@ pub trait Instance: Sealed + Enable { fn is_interrupt_set(&self) -> bool; } -#[doc(hidden)] -pub trait Enable: Sealed { - fn enable_peripheral(&self); -} - /// A timer within a Timer Group. pub struct TimerX { phantom: PhantomData, @@ -672,7 +668,6 @@ where impl Instance for TimerX where TG: TimerGroupInstance, - Self: Enable, { fn register_block(&self) -> &RegisterBlock { unsafe { &*TG::register_block() } @@ -802,7 +797,7 @@ where fn set_divider(&self, divider: u16) { unsafe { Self::t() } .config() - .modify(|_, w| unsafe { w.divider().bits(divider) }) + .modify(|_, w| unsafe { w.divider().bits(divider) }); } } @@ -813,25 +808,6 @@ pub type Timer0 = TimerX; #[cfg(timg_timer1)] pub type Timer1 = TimerX; -impl Enable for Timer0 -where - TG: TimerGroupInstance, -{ - fn enable_peripheral(&self) { - PeripheralClockControl::enable(PeripheralEnable::Timg0); - } -} - -#[cfg(timg_timer1)] -impl Enable for Timer1 -where - TG: TimerGroupInstance, -{ - fn enable_peripheral(&self) { - PeripheralClockControl::enable(PeripheralEnable::Timg1); - } -} - fn ticks_to_timeout(ticks: u64, clock: F, divider: u32) -> u64 where F: Into, @@ -911,6 +887,36 @@ where { } +/// Behavior of the MWDT stage if it times out. +#[allow(unused)] +#[derive(Debug, Clone, Copy)] +pub enum MwdtStageAction { + /// No effect on the system. + Off = 0, + /// Trigger an interrupt. + Interrupt = 1, + /// Reset the CPU core. + ResetCpu = 2, + /// Reset the main system, power management unit and RTC peripherals. + ResetSystem = 3, +} + +/// MWDT stages. +/// +/// Timer stages allow for a timer to have a series of different timeout values +/// and corresponding expiry action. +#[derive(Debug, Clone, Copy)] +pub enum MwdtStage { + /// MWDT stage 0. + Stage0, + /// MWDT stage 1. + Stage1, + /// MWDT stage 2. + Stage2, + /// MWDT stage 3. + Stage3, +} + /// Watchdog timer pub struct Wdt { phantom: PhantomData, @@ -924,7 +930,7 @@ where /// Construct a new instance of [`Wdt`] pub fn new() -> Self { #[cfg(lp_wdt)] - PeripheralClockControl::enable(PeripheralEnable::Wdt); + PeripheralClockControl::enable(crate::system::Peripheral::Wdt); TG::configure_wdt_src_clk(); @@ -937,14 +943,14 @@ where pub fn enable(&mut self) { // SAFETY: The `TG` instance being modified is owned by `self`, which is behind // a mutable reference. - unsafe { Self::set_wdt_enabled(true) }; + unsafe { self.set_wdt_enabled(true) }; } /// Disable the watchdog timer instance pub fn disable(&mut self) { // SAFETY: The `TG` instance being modified is owned by `self`, which is behind // a mutable reference. - unsafe { Self::set_wdt_enabled(false) }; + unsafe { self.set_wdt_enabled(false) }; } /// Forcibly enable or disable the watchdog timer @@ -954,83 +960,140 @@ where /// This bypasses the usual ownership rules for the peripheral, so users /// must take care to ensure that no driver instance is active for the /// timer. - pub unsafe fn set_wdt_enabled(enabled: bool) { + pub unsafe fn set_wdt_enabled(&mut self, enabled: bool) { let reg_block = unsafe { &*TG::register_block() }; - reg_block - .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0x50D8_3AA1u32) }); + self.set_write_protection(false); if !enabled { reg_block.wdtconfig0().write(|w| unsafe { w.bits(0) }); } else { reg_block.wdtconfig0().write(|w| w.wdt_en().bit(true)); + + reg_block + .wdtconfig0() + .write(|w| w.wdt_flashboot_mod_en().bit(false)); + + #[cfg_attr(esp32, allow(unused_unsafe))] + reg_block.wdtconfig0().write(|w| unsafe { + w.wdt_en() + .bit(true) + .wdt_stg0() + .bits(MwdtStageAction::ResetSystem as u8) + .wdt_cpu_reset_length() + .bits(7) + .wdt_sys_reset_length() + .bits(7) + .wdt_stg1() + .bits(MwdtStageAction::Off as u8) + .wdt_stg2() + .bits(MwdtStageAction::Off as u8) + .wdt_stg3() + .bits(MwdtStageAction::Off as u8) + }); + + #[cfg(any(esp32c2, esp32c3, esp32c6))] + reg_block + .wdtconfig0() + .modify(|_, w| w.wdt_conf_update_en().set_bit()); } - reg_block - .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0u32) }); + self.set_write_protection(true); } /// Feed the watchdog timer pub fn feed(&mut self) { let reg_block = unsafe { &*TG::register_block() }; - reg_block - .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0x50D8_3AA1u32) }); + self.set_write_protection(false); reg_block.wdtfeed().write(|w| unsafe { w.bits(1) }); + self.set_write_protection(true); + } + + fn set_write_protection(&mut self, enable: bool) { + let reg_block = unsafe { &*TG::register_block() }; + + let wkey = if enable { 0u32 } else { 0x50D8_3AA1u32 }; + reg_block .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0u32) }); + .write(|w| unsafe { w.wdt_wkey().bits(wkey) }); } /// Set the timeout, in microseconds, of the watchdog timer - pub fn set_timeout(&mut self, timeout: MicrosDurationU64) { + pub fn set_timeout(&mut self, stage: MwdtStage, timeout: MicrosDurationU64) { let timeout_raw = (timeout.to_nanos() * 10 / 125) as u32; let reg_block = unsafe { &*TG::register_block() }; - reg_block - .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0x50D8_3AA1u32) }); + self.set_write_protection(false); reg_block .wdtconfig1() .write(|w| unsafe { w.wdt_clk_prescale().bits(1) }); - reg_block - .wdtconfig2() - .write(|w| unsafe { w.wdt_stg0_hold().bits(timeout_raw) }); - - #[cfg_attr(esp32, allow(unused_unsafe))] - reg_block.wdtconfig0().write(|w| unsafe { - w.wdt_en() - .bit(true) - .wdt_stg0() - .bits(3) - .wdt_cpu_reset_length() - .bits(1) - .wdt_sys_reset_length() - .bits(1) - .wdt_stg1() - .bits(0) - .wdt_stg2() - .bits(0) - .wdt_stg3() - .bits(0) - }); + unsafe { + match stage { + MwdtStage::Stage0 => reg_block + .wdtconfig2() + .write(|w| w.wdt_stg0_hold().bits(timeout_raw)), + MwdtStage::Stage1 => reg_block + .wdtconfig3() + .write(|w| w.wdt_stg1_hold().bits(timeout_raw)), + MwdtStage::Stage2 => reg_block + .wdtconfig4() + .write(|w| w.wdt_stg2_hold().bits(timeout_raw)), + MwdtStage::Stage3 => reg_block + .wdtconfig5() + .write(|w| w.wdt_stg3_hold().bits(timeout_raw)), + }; + } #[cfg(any(esp32c2, esp32c3, esp32c6))] reg_block .wdtconfig0() .modify(|_, w| w.wdt_conf_update_en().set_bit()); - reg_block - .wdtwprotect() - .write(|w| unsafe { w.wdt_wkey().bits(0u32) }); + self.set_write_protection(true); + } + + /// Set the stage action of the MWDT for a specific stage. + /// + /// This function modifies MWDT behavior only if a custom bootloader with + /// the following modifications is used: + /// - `ESP_TASK_WDT_EN` parameter **disabled** + /// - `ESP_INT_WDT` parameter **disabled** + pub fn set_stage_action(&mut self, stage: MwdtStage, action: MwdtStageAction) { + let reg_block = unsafe { &*TG::register_block() }; + + self.set_write_protection(false); + + match stage { + MwdtStage::Stage0 => { + reg_block + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg0().bits(action as u8) }); + } + MwdtStage::Stage1 => { + reg_block + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg1().bits(action as u8) }); + } + MwdtStage::Stage2 => { + reg_block + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg2().bits(action as u8) }); + } + MwdtStage::Stage3 => { + reg_block + .wdtconfig0() + .modify(|_, w| unsafe { w.wdt_stg3().bits(action as u8) }); + } + } + + self.set_write_protection(true); } } @@ -1078,7 +1141,7 @@ where T: Into, { self.enable(); - self.set_timeout(period.into()); + self.set_timeout(MwdtStage::Stage0, period.into()); } } @@ -1144,7 +1207,7 @@ mod asynch { } } - impl<'a, T> core::future::Future for TimerFuture<'a, T> + impl core::future::Future for TimerFuture<'_, T> where T: Instance, { @@ -1162,7 +1225,7 @@ mod asynch { } } - impl<'a, T> Drop for TimerFuture<'a, T> + impl Drop for TimerFuture<'_, T> where T: Instance, { @@ -1260,87 +1323,87 @@ pub mod etm { use crate::etm::{EtmEvent, EtmTask}; /// Event Task Matrix event for a timer. - pub struct TimerEtmEvent { + pub struct Event { id: u8, } /// Event Task Matrix task for a timer. - pub struct TimerEtmTask { + pub struct Task { id: u8, } - impl EtmEvent for TimerEtmEvent { + impl EtmEvent for Event { fn id(&self) -> u8 { self.id } } - impl Sealed for TimerEtmEvent {} + impl Sealed for Event {} - impl EtmTask for TimerEtmTask { + impl EtmTask for Task { fn id(&self) -> u8 { self.id } } - impl Sealed for TimerEtmTask {} + impl Sealed for Task {} /// General purpose timer ETM events. - pub trait TimerEtmEvents { + pub trait Events { /// ETM event triggered on alarm - fn on_alarm(&self) -> TimerEtmEvent; + fn on_alarm(&self) -> Event; } /// General purpose timer ETM tasks - pub trait TimerEtmTasks { + pub trait Tasks { /// ETM task to start the counter - fn cnt_start(&self) -> TimerEtmTask; + fn cnt_start(&self) -> Task; /// ETM task to start the alarm - fn cnt_stop(&self) -> TimerEtmTask; + fn cnt_stop(&self) -> Task; /// ETM task to stop the counter - fn cnt_reload(&self) -> TimerEtmTask; + fn cnt_reload(&self) -> Task; /// ETM task to reload the counter - fn cnt_cap(&self) -> TimerEtmTask; + fn cnt_cap(&self) -> Task; /// ETM task to load the counter with the value stored when the last /// `now()` was called - fn alarm_start(&self) -> TimerEtmTask; + fn alarm_start(&self) -> Task; } - impl TimerEtmEvents for Timer0 + impl Events for Timer0 where TG: TimerGroupInstance, { - fn on_alarm(&self) -> TimerEtmEvent { - TimerEtmEvent { id: 48 + TG::id() } + fn on_alarm(&self) -> Event { + Event { id: 48 + TG::id() } } } - impl TimerEtmTasks for Timer0 + impl Tasks for Timer0 where TG: TimerGroupInstance, { - fn cnt_start(&self) -> TimerEtmTask { - TimerEtmTask { id: 88 + TG::id() } + fn cnt_start(&self) -> Task { + Task { id: 88 + TG::id() } } - fn alarm_start(&self) -> TimerEtmTask { - TimerEtmTask { id: 90 + TG::id() } + fn alarm_start(&self) -> Task { + Task { id: 90 + TG::id() } } - fn cnt_stop(&self) -> TimerEtmTask { - TimerEtmTask { id: 92 + TG::id() } + fn cnt_stop(&self) -> Task { + Task { id: 92 + TG::id() } } - fn cnt_reload(&self) -> TimerEtmTask { - TimerEtmTask { id: 94 + TG::id() } + fn cnt_reload(&self) -> Task { + Task { id: 94 + TG::id() } } - fn cnt_cap(&self) -> TimerEtmTask { - TimerEtmTask { id: 96 + TG::id() } + fn cnt_cap(&self) -> Task { + Task { id: 96 + TG::id() } } } } diff --git a/esp-hal/src/touch.rs b/esp-hal/src/touch.rs index be397dc10ba..fdf05bd532d 100644 --- a/esp-hal/src/touch.rs +++ b/esp-hal/src/touch.rs @@ -10,9 +10,7 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::touch::{Touch, TouchPad}; -//! # use esp_hal::gpio::Io; -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! let touch_pin0 = io.pins.gpio2; +//! let touch_pin0 = peripherals.GPIO2; //! let touch = Touch::continuous_mode(peripherals.TOUCH, None); //! let mut touchpad = TouchPad::new(touch_pin0, &touch); //! // ... give the peripheral some time for the measurement @@ -396,7 +394,7 @@ impl TouchPad .touch_meas_done() .bit_is_set() { - Some(self.pin.get_touch_measurement(Internal)) + Some(self.pin.touch_measurement(Internal)) } else { None } @@ -422,7 +420,7 @@ impl TouchPad { .touch_meas_done() .bit_is_clear() {} - self.pin.get_touch_measurement(Internal) + self.pin.touch_measurement(Internal) } /// Enables the touch_pad interrupt. @@ -441,7 +439,7 @@ impl TouchPad { /// ## Example pub fn enable_interrupt(&mut self, threshold: u16) { self.pin.set_threshold(threshold, Internal); - internal_enable_interrupt(self.pin.get_touch_nr(Internal)) + internal_enable_interrupt(self.pin.touch_nr(Internal)) } /// Disables the touch pad's interrupt. @@ -449,7 +447,7 @@ impl TouchPad { /// If no other touch pad interrupts are active, the touch interrupt is /// disabled completely. pub fn disable_interrupt(&mut self) { - internal_disable_interrupt(self.pin.get_touch_nr(Internal)) + internal_disable_interrupt(self.pin.touch_nr(Internal)) } /// Clears a pending touch interrupt. @@ -466,7 +464,7 @@ impl TouchPad { /// Checks if the pad is touched, based on the configured threshold value. pub fn is_interrupt_set(&mut self) -> bool { - internal_is_interrupt_set(self.pin.get_touch_nr(Internal)) + internal_is_interrupt_set(self.pin.touch_nr(Internal)) } } @@ -589,7 +587,7 @@ mod asynch { /// Wait for the pad to be touched. pub async fn wait_for_touch(&mut self, threshold: u16) { self.pin.set_threshold(threshold, Internal); - let touch_nr = self.pin.get_touch_nr(Internal); + let touch_nr = self.pin.touch_nr(Internal); internal_enable_interrupt(touch_nr); TouchFuture::new(touch_nr).await; } diff --git a/esp-hal/src/twai/mod.rs b/esp-hal/src/twai/mod.rs index eaed7d1424e..cfbb1f7ead0 100644 --- a/esp-hal/src/twai/mod.rs +++ b/esp-hal/src/twai/mod.rs @@ -32,14 +32,12 @@ //! # use esp_hal::twai::TwaiConfiguration; //! # use esp_hal::twai::BaudRate; //! # use esp_hal::twai::TwaiMode; -//! # use esp_hal::gpio::Io; //! # use embedded_can::Frame; //! # use nb::block; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI //! // transceiver. -//! let can_rx_pin = io.pins.gpio3; -//! let can_tx_pin = io.pins.gpio2; +//! let can_rx_pin = peripherals.GPIO3; +//! let can_tx_pin = peripherals.GPIO2; //! //! // The speed of the TWAI bus. //! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K; @@ -86,14 +84,12 @@ //! # use esp_hal::twai::EspTwaiFrame; //! # use esp_hal::twai::StandardId; //! # use esp_hal::twai::TwaiMode; -//! # use esp_hal::gpio::Io; //! # use embedded_can::Frame; //! # use nb::block; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! // Use GPIO pins 2 and 3 to connect to the respective pins on the TWAI //! // transceiver. -//! let can_rx_pin = io.pins.gpio3; -//! let can_tx_pin = io.pins.gpio2; +//! let can_rx_pin = peripherals.GPIO3; +//! let can_tx_pin = peripherals.GPIO2; //! //! // The speed of the TWAI bus. //! const TWAI_BAUDRATE: twai::BaudRate = BaudRate::B1000K; @@ -129,7 +125,6 @@ use core::marker::PhantomData; use self::filter::{Filter, FilterType}; use crate::{ - dma::PeripheralMarker, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -140,6 +135,7 @@ use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::twai0::RegisterBlock, system::PeripheralClockControl, + twai::filter::SingleStandardFilter, Async, Blocking, InterruptConfigurable, @@ -752,6 +748,7 @@ impl BaudRate { /// An inactive TWAI peripheral in the "Reset"/configuration state. pub struct TwaiConfiguration<'d, DM: crate::Mode, T = AnyTwai> { twai: PeripheralRef<'d, T>, + filter: Option<(FilterType, [u8; 8])>, phantom: PhantomData, mode: TwaiMode, } @@ -778,10 +775,16 @@ where let mut this = TwaiConfiguration { twai, + filter: None, // We'll immediately call `set_filter` phantom: PhantomData, mode, }; + // Accept all messages by default. + this.set_filter( + const { SingleStandardFilter::new(b"xxxxxxxxxxx", b"x", [b"xxxxxxxx", b"xxxxxxxx"]) }, + ); + // Set RESET bit to 1 this.twai .register_block() @@ -806,13 +809,13 @@ where tx_pin.set_to_push_pull_output(crate::private::Internal); Pull::None }; - tx_pin.connect_peripheral_to_output(this.twai.output_signal(), crate::private::Internal); + this.twai.output_signal().connect_to(tx_pin); // Setting up RX pin later allows us to use a single pin in tests. // `set_to_push_pull_output` disables input, here we re-enable it if rx_pin // uses the same GPIO. rx_pin.init_input(rx_pull, crate::private::Internal); - rx_pin.connect_input_to_peripheral(this.twai.input_signal(), crate::private::Internal); + this.twai.input_signal().connect_to(rx_pin); // Freeze REC by changing to LOM mode this.set_mode(TwaiMode::ListenOnly); @@ -840,10 +843,15 @@ where } fn internal_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(self.twai.interrupt(), handler.handler()); - crate::interrupt::enable(self.twai.interrupt(), handler.priority()).unwrap(); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, self.twai.interrupt()); } + unsafe { crate::interrupt::bind_interrupt(self.twai.interrupt(), handler.handler()) }; + + unwrap!(crate::interrupt::enable( + self.twai.interrupt(), + handler.priority() + )); } /// Set the bitrate of the bus. @@ -931,22 +939,30 @@ where /// You may use a `const {}` block to ensure that the filter is parsed /// during program compilation. /// + /// The filter is not applied to the peripheral until [`Self::start`] is + /// called. + /// /// [ESP32C3 Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#subsubsection.29.4.6) pub fn set_filter(&mut self, filter: impl Filter) { + // Convert the filter into values for the registers and store them for later + // use. + self.filter = Some((filter.filter_type(), filter.to_registers())); + } + + fn apply_filter(&self) { + let Some((filter_type, registers)) = self.filter.as_ref() else { + return; + }; + + let register_block = self.twai.register_block(); // Set or clear the rx filter mode bit depending on the filter type. - let filter_mode_bit = filter.filter_type() == FilterType::Single; - self.twai - .register_block() + register_block .mode() - .modify(|_, w| w.rx_filter_mode().bit(filter_mode_bit)); - - // Convert the filter into values for the registers and store them to the - // registers. - let registers = filter.to_registers(); + .modify(|_, w| w.rx_filter_mode().bit(*filter_type == FilterType::Single)); // Copy the filter to the peripheral. unsafe { - copy_to_data_register(self.twai.register_block().data_0().as_ptr(), ®isters); + copy_to_data_register(register_block.data_0().as_ptr(), registers); } } @@ -975,6 +991,7 @@ where /// Put the peripheral into Operation Mode, allowing the transmission and /// reception of packets using the new object. pub fn start(self) -> Twai<'d, DM, T> { + self.apply_filter(); self.set_mode(self.mode); // Clear the TEC and REC @@ -1027,17 +1044,6 @@ where phantom: PhantomData, } } - - /// Convert the configuration into an async configuration. - fn into_async(self) -> TwaiConfiguration<'d, Async, T> { - let mut this = TwaiConfiguration { - twai: self.twai, - phantom: PhantomData, - mode: self.mode, - }; - this.internal_set_interrupt_handler(this.twai.async_handler()); - this - } } impl<'d> TwaiConfiguration<'d, Blocking> { @@ -1101,35 +1107,16 @@ where ) -> Self { Self::new_internal(peripheral, rx_pin, tx_pin, baud_rate, true, mode) } -} -impl<'d> TwaiConfiguration<'d, Async> { - /// Create a new instance of [TwaiConfiguration] - /// - /// You will need to use a transceiver to connect to the TWAI bus - pub fn new_async( - peripheral: impl Peripheral

+ 'd, - rx_pin: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, - baud_rate: BaudRate, - mode: TwaiMode, - ) -> Self { - Self::new_async_typed(peripheral.map_into(), rx_pin, tx_pin, baud_rate, mode) - } - - /// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s - /// directly - /// - /// You don't need a transceiver by following the description in the - /// `twai.rs` example - pub fn new_async_no_transceiver( - peripheral: impl Peripheral

+ 'd, - rx_pin: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, - baud_rate: BaudRate, - mode: TwaiMode, - ) -> Self { - Self::new_async_no_transceiver_typed(peripheral.map_into(), rx_pin, tx_pin, baud_rate, mode) + /// Convert the configuration into an async configuration. + pub fn into_async(mut self) -> TwaiConfiguration<'d, Async, T> { + self.set_interrupt_handler(self.twai.async_handler()); + TwaiConfiguration { + twai: self.twai, + filter: self.filter, + phantom: PhantomData, + mode: self.mode, + } } } @@ -1137,33 +1124,19 @@ impl<'d, T> TwaiConfiguration<'d, Async, T> where T: Instance, { - /// Create a new instance of [TwaiConfiguration] in async mode - /// - /// You will need to use a transceiver to connect to the TWAI bus - pub fn new_async_typed( - peripheral: impl Peripheral

+ 'd, - rx_pin: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, - baud_rate: BaudRate, - mode: TwaiMode, - ) -> Self { - TwaiConfiguration::new_typed(peripheral, rx_pin, tx_pin, baud_rate, mode).into_async() - } + /// Convert the configuration into a blocking configuration. + pub fn into_blocking(self) -> TwaiConfiguration<'d, Blocking, T> { + use crate::{interrupt, Cpu}; - /// Create a new instance of [TwaiConfiguration] meant to connect two ESP32s - /// directly in async mode - /// - /// You don't need a transceiver by following the description in the - /// `twai.rs` example - pub fn new_async_no_transceiver_typed( - peripheral: impl Peripheral

+ 'd, - rx_pin: impl Peripheral

+ 'd, - tx_pin: impl Peripheral

+ 'd, - baud_rate: BaudRate, - mode: TwaiMode, - ) -> Self { - TwaiConfiguration::new_no_transceiver_typed(peripheral, rx_pin, tx_pin, baud_rate, mode) - .into_async() + interrupt::disable(Cpu::current(), self.twai.interrupt()); + + // Re-create in blocking mode + TwaiConfiguration { + twai: self.twai, + filter: self.filter, + phantom: PhantomData, + mode: self.mode, + } } } @@ -1220,6 +1193,7 @@ where TwaiConfiguration { twai: self.twai, + filter: None, // filter already applied, no need to restore it phantom: PhantomData, mode, } @@ -1444,7 +1418,7 @@ unsafe fn copy_to_data_register(dest: *mut u32, src: &[u8]) { } } -impl<'d, DM, T> embedded_hal_02::can::Can for Twai<'d, DM, T> +impl embedded_hal_02::can::Can for Twai<'_, DM, T> where T: Instance, DM: crate::Mode, @@ -1468,7 +1442,7 @@ where } } -impl<'d, DM, T> embedded_can::nb::Can for Twai<'d, DM, T> +impl embedded_can::nb::Can for Twai<'_, DM, T> where T: Instance, DM: crate::Mode, @@ -1493,10 +1467,13 @@ where } /// TWAI peripheral instance. -pub trait Instance: Peripheral

+ PeripheralMarker + Into + 'static { +pub trait Instance: Peripheral

+ Into + 'static { /// The identifier number for this TWAI instance. fn number(&self) -> usize; + /// Returns the system peripheral marker for this instance. + fn peripheral(&self) -> crate::system::Peripheral; + /// Input signal. fn input_signal(&self) -> InputSignal; /// Output signal. @@ -1681,6 +1658,10 @@ impl Instance for crate::peripherals::TWAI0 { 0 } + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::Twai0 + } + fn input_signal(&self) -> InputSignal { cfg_if::cfg_if! { if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] { @@ -1726,6 +1707,10 @@ impl Instance for crate::peripherals::TWAI1 { 1 } + fn peripheral(&self) -> crate::system::Peripheral { + crate::system::Peripheral::Twai1 + } + fn input_signal(&self) -> InputSignal { InputSignal::TWAI1_RX } @@ -1772,6 +1757,7 @@ impl Instance for AnyTwai { AnyTwaiInner::Twai1(twai) => twai, } { fn number(&self) -> usize; + fn peripheral(&self) -> crate::system::Peripheral; fn input_signal(&self) -> InputSignal; fn output_signal(&self) -> OutputSignal; fn interrupt(&self) -> crate::peripherals::Interrupt; @@ -1943,6 +1929,18 @@ mod asynch { } if intr_status.bits() & 0b11111100 > 0 { + let err_capture = register_block.err_code_cap().read(); + let status = register_block.status().read(); + + // Read error code direction (transmitting or receiving) + let ecc_direction = err_capture.ecc_direction().bit_is_set(); + + // If the error comes from Tx and Tx request is pending + if !ecc_direction && !status.tx_buf_st().bit_is_set() { + // Cancel a pending transmission request + register_block.cmd().write(|w| w.abort_tx().set_bit()); + } + async_state.err_waker.wake(); } diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index e22355c1122..b67f6b97427 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -23,14 +23,11 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::Uart; -//! use esp_hal::gpio::Io; -//! -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! //! let mut uart1 = Uart::new( //! peripherals.UART1, -//! io.pins.gpio1, -//! io.pins.gpio2, +//! peripherals.GPIO1, +//! peripherals.GPIO2, //! ).unwrap(); //! # } //! ``` @@ -55,14 +52,12 @@ //! ### Sending and Receiving Data //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, Uart}; -//! # use esp_hal::gpio::Io; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! # use esp_hal::uart::{Config, Uart}; //! # let mut uart1 = Uart::new_with_config( //! # peripherals.UART1, //! # Config::default(), -//! # io.pins.gpio1, -//! # io.pins.gpio2, +//! # peripherals.GPIO1, +//! # peripherals.GPIO2, //! # ).unwrap(); //! // Write bytes out over the UART: //! uart1.write_bytes(b"Hello, world!").expect("write error!"); @@ -72,14 +67,12 @@ //! ### Splitting the UART into RX and TX Components //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, Uart}; -//! # use esp_hal::gpio::Io; -//! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); +//! # use esp_hal::uart::{Config, Uart}; //! # let mut uart1 = Uart::new_with_config( //! # peripherals.UART1, //! # Config::default(), -//! # io.pins.gpio1, -//! # io.pins.gpio2, +//! # peripherals.GPIO1, +//! # peripherals.GPIO2, //! # ).unwrap(); //! // The UART can be split into separate Transmit and Receive components: //! let (mut rx, mut tx) = uart1.split(); @@ -94,16 +87,13 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::Uart; -//! use esp_hal::gpio::Io; -//! -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! -//! let rx = io.pins.gpio2.peripheral_input().inverted(); -//! let tx = io.pins.gpio1.into_peripheral_output().inverted(); +//! let (rx, _) = peripherals.GPIO2.split(); +//! let (_, tx) = peripherals.GPIO1.split(); //! let mut uart1 = Uart::new( //! peripherals.UART1, -//! rx, -//! tx, +//! rx.inverted(), +//! tx.inverted(), //! ).unwrap(); //! # } //! ``` @@ -112,12 +102,9 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::uart::{UartTx, UartRx}; -//! use esp_hal::gpio::Io; //! -//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); -//! -//! let tx = UartTx::new(peripherals.UART0, io.pins.gpio1).unwrap(); -//! let rx = UartRx::new(peripherals.UART1, io.pins.gpio2).unwrap(); +//! let tx = UartTx::new(peripherals.UART0, peripherals.GPIO1).unwrap(); +//! let rx = UartRx::new(peripherals.UART1, peripherals.GPIO2).unwrap(); //! # } //! ``` //! @@ -126,14 +113,15 @@ //! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/ //! [embedded-io-async]: https://docs.rs/embedded-io-async/latest/embedded_io_async/ -use core::marker::PhantomData; +use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll}; +use embassy_embedded_hal::SetConfig; +use embassy_sync::waitqueue::AtomicWaker; use enumset::{EnumSet, EnumSetType}; +use portable_atomic::AtomicBool; -use self::config::Config; use crate::{ clock::Clocks, - dma::PeripheralMarker, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -145,19 +133,14 @@ use crate::{ peripherals::{uart0::RegisterBlock, Interrupt}, private::Internal, system::PeripheralClockControl, + Async, Blocking, InterruptConfigurable, Mode, }; -const CONSOLE_UART_NUM: usize = 0; const UART_FIFO_SIZE: u16 = 128; -#[cfg(not(any(esp32, esp32s2)))] -use crate::soc::constants::RC_FAST_CLK; -#[cfg(any(esp32, esp32s2))] -use crate::soc::constants::REF_TICK; - /// UART Error #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -224,219 +207,220 @@ pub enum ClockSource { RefTick, } -/// UART Configuration -pub mod config { +// see +const UART_FULL_THRESH_DEFAULT: u16 = 120; +// see +const UART_TOUT_THRESH_DEFAULT: u8 = 10; - // see - const UART_FULL_THRESH_DEFAULT: u16 = 120; - // see - const UART_TOUT_THRESH_DEFAULT: u8 = 10; +/// Number of data bits +/// +/// This enum represents the various configurations for the number of data +/// bits used in UART communication. The number of data bits defines the +/// length of each transmitted or received data frame. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum DataBits { + /// 5 data bits per frame. + DataBits5 = 0, + /// 6 data bits per frame. + DataBits6 = 1, + /// 7 data bits per frame. + DataBits7 = 2, + /// 8 data bits per frame (most common). + DataBits8 = 3, +} - /// Number of data bits - /// - /// This enum represents the various configurations for the number of data - /// bits used in UART communication. The number of data bits defines the - /// length of each transmitted or received data frame. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum DataBits { - /// 5 data bits per frame. - DataBits5 = 0, - /// 6 data bits per frame. - DataBits6 = 1, - /// 7 data bits per frame. - DataBits7 = 2, - /// 8 data bits per frame (most common). - DataBits8 = 3, - } - - /// Parity check - /// - /// Parity is a form of error detection in UART communication, used to - /// ensure that the data has not been corrupted during transmission. The - /// parity bit is added to the data bits to make the number of 1-bits - /// either even or odd. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Parity { - /// No parity bit is used (most common). - ParityNone, - /// Even parity: the parity bit is set to make the total number of - /// 1-bits even. - ParityEven, - /// Odd parity: the parity bit is set to make the total number of 1-bits - /// odd. - ParityOdd, - } - - /// Number of stop bits - /// - /// The stop bit(s) signal the end of a data packet in UART communication. - /// This enum defines the possible configurations for the number of stop - /// bits. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum StopBits { - /// 1 stop bit. - STOP1 = 1, - /// 1.5 stop bits. - STOP1P5 = 2, - /// 2 stop bits. - STOP2 = 3, - } - - /// UART Configuration - #[derive(Debug, Copy, Clone)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct Config { - /// The baud rate (speed) of the UART communication in bits per second - /// (bps). - pub baudrate: u32, - /// Number of data bits in each frame (5, 6, 7, or 8 bits). - pub data_bits: DataBits, - /// Parity setting (None, Even, or Odd). - pub parity: Parity, - /// Number of stop bits in each frame (1, 1.5, or 2 bits). - pub stop_bits: StopBits, - /// Clock source used by the UART peripheral. - pub clock_source: super::ClockSource, - /// Threshold level at which the RX FIFO is considered full. - pub rx_fifo_full_threshold: u16, - /// Optional timeout value for RX operations. - pub rx_timeout: Option, - } - - impl Config { - /// Sets the baud rate for the UART configuration. - pub fn baudrate(mut self, baudrate: u32) -> Self { - self.baudrate = baudrate; - self - } +/// Parity check +/// +/// Parity is a form of error detection in UART communication, used to +/// ensure that the data has not been corrupted during transmission. The +/// parity bit is added to the data bits to make the number of 1-bits +/// either even or odd. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Parity { + /// No parity bit is used (most common). + ParityNone, + /// Even parity: the parity bit is set to make the total number of + /// 1-bits even. + ParityEven, + /// Odd parity: the parity bit is set to make the total number of 1-bits + /// odd. + ParityOdd, +} - /// Configures the UART to use no parity check. - pub fn parity_none(mut self) -> Self { - self.parity = Parity::ParityNone; - self - } +/// Number of stop bits +/// +/// The stop bit(s) signal the end of a data packet in UART communication. +/// This enum defines the possible configurations for the number of stop +/// bits. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum StopBits { + /// 1 stop bit. + STOP1 = 1, + /// 1.5 stop bits. + STOP1P5 = 2, + /// 2 stop bits. + STOP2 = 3, +} - /// Configures the UART to use even parity check. - pub fn parity_even(mut self) -> Self { - self.parity = Parity::ParityEven; - self - } +/// UART Configuration +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// The baud rate (speed) of the UART communication in bits per second + /// (bps). + pub baudrate: u32, + /// Number of data bits in each frame (5, 6, 7, or 8 bits). + pub data_bits: DataBits, + /// Parity setting (None, Even, or Odd). + pub parity: Parity, + /// Number of stop bits in each frame (1, 1.5, or 2 bits). + pub stop_bits: StopBits, + /// Clock source used by the UART peripheral. + pub clock_source: ClockSource, + /// Threshold level at which the RX FIFO is considered full. + pub rx_fifo_full_threshold: u16, + /// Optional timeout value for RX operations. + pub rx_timeout: Option, +} - /// Configures the UART to use odd parity check. - pub fn parity_odd(mut self) -> Self { - self.parity = Parity::ParityOdd; - self - } +impl Config { + /// Sets the baud rate for the UART configuration. + pub fn baudrate(mut self, baudrate: u32) -> Self { + self.baudrate = baudrate; + self + } - /// Sets the number of data bits for the UART configuration. - pub fn data_bits(mut self, data_bits: DataBits) -> Self { - self.data_bits = data_bits; - self - } + /// Configures the UART to use no parity check. + pub fn parity_none(mut self) -> Self { + self.parity = Parity::ParityNone; + self + } - /// Sets the number of stop bits for the UART configuration. - pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { - self.stop_bits = stop_bits; - self - } + /// Configures the UART to use even parity check. + pub fn parity_even(mut self) -> Self { + self.parity = Parity::ParityEven; + self + } - /// Sets the clock source for the UART configuration. - pub fn clock_source(mut self, source: super::ClockSource) -> Self { - self.clock_source = source; - self - } + /// Configures the UART to use odd parity check. + pub fn parity_odd(mut self) -> Self { + self.parity = Parity::ParityOdd; + self + } - /// Calculates the total symbol length in bits based on the configured - /// data bits, parity, and stop bits. - pub fn symbol_length(&self) -> u8 { - let mut length: u8 = 1; // start bit - length += match self.data_bits { - DataBits::DataBits5 => 5, - DataBits::DataBits6 => 6, - DataBits::DataBits7 => 7, - DataBits::DataBits8 => 8, - }; - length += match self.parity { - Parity::ParityNone => 0, - _ => 1, - }; - length += match self.stop_bits { - StopBits::STOP1 => 1, - _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits - }; - length - } + /// Sets the number of data bits for the UART configuration. + pub fn data_bits(mut self, data_bits: DataBits) -> Self { + self.data_bits = data_bits; + self + } - /// Sets the RX FIFO full threshold for the UART configuration. - pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { - self.rx_fifo_full_threshold = threshold; - self - } + /// Sets the number of stop bits for the UART configuration. + pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { + self.stop_bits = stop_bits; + self + } - /// Sets the RX timeout for the UART configuration. - pub fn rx_timeout(mut self, timeout: Option) -> Self { - self.rx_timeout = timeout; - self - } + /// Sets the clock source for the UART configuration. + pub fn clock_source(mut self, source: ClockSource) -> Self { + self.clock_source = source; + self } - impl Default for Config { - fn default() -> Config { - Config { - baudrate: 115_200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - #[cfg(any(esp32c6, esp32h2, lp_uart))] - clock_source: super::ClockSource::Xtal, - #[cfg(not(any(esp32c6, esp32h2, lp_uart)))] - clock_source: super::ClockSource::Apb, - rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, - rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), - } + /// Calculates the total symbol length in bits based on the configured + /// data bits, parity, and stop bits. + pub fn symbol_length(&self) -> u8 { + let mut length: u8 = 1; // start bit + length += match self.data_bits { + DataBits::DataBits5 => 5, + DataBits::DataBits6 => 6, + DataBits::DataBits7 => 7, + DataBits::DataBits8 => 8, + }; + length += match self.parity { + Parity::ParityNone => 0, + _ => 1, + }; + length += match self.stop_bits { + StopBits::STOP1 => 1, + _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits + }; + length + } + + /// Sets the RX FIFO full threshold for the UART configuration. + pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { + self.rx_fifo_full_threshold = threshold; + self + } + + /// Sets the RX timeout for the UART configuration. + pub fn rx_timeout(mut self, timeout: Option) -> Self { + self.rx_timeout = timeout; + self + } +} + +impl Default for Config { + fn default() -> Config { + Config { + baudrate: 115_200, + data_bits: DataBits::DataBits8, + parity: Parity::ParityNone, + stop_bits: StopBits::STOP1, + clock_source: { + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, lp_uart))] { + ClockSource::Xtal + } else { + ClockSource::Apb + } + } + }, + rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, + rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), } } +} - /// Configuration for the AT-CMD detection functionality - pub struct AtCmdConfig { - /// Optional idle time before the AT command detection begins, in clock - /// cycles. - pub pre_idle_count: Option, - /// Optional idle time after the AT command detection ends, in clock - /// cycles. - pub post_idle_count: Option, - /// Optional timeout between characters in the AT command, in clock - /// cycles. - pub gap_timeout: Option, - /// The character that triggers the AT command detection. - pub cmd_char: u8, - /// Optional number of characters to detect as part of the AT command. - pub char_num: Option, - } - - impl AtCmdConfig { - /// Creates a new `AtCmdConfig` with the specified configuration. - /// - /// This function sets up the AT command detection parameters, including - /// pre- and post-idle times, a gap timeout, the triggering command - /// character, and the number of characters to detect. - pub fn new( - pre_idle_count: Option, - post_idle_count: Option, - gap_timeout: Option, - cmd_char: u8, - char_num: Option, - ) -> AtCmdConfig { - Self { - pre_idle_count, - post_idle_count, - gap_timeout, - cmd_char, - char_num, - } +/// Configuration for the AT-CMD detection functionality +pub struct AtCmdConfig { + /// Optional idle time before the AT command detection begins, in clock + /// cycles. + pub pre_idle_count: Option, + /// Optional idle time after the AT command detection ends, in clock + /// cycles. + pub post_idle_count: Option, + /// Optional timeout between characters in the AT command, in clock + /// cycles. + pub gap_timeout: Option, + /// The character that triggers the AT command detection. + pub cmd_char: u8, + /// Optional number of characters to detect as part of the AT command. + pub char_num: Option, +} + +impl AtCmdConfig { + /// Creates a new `AtCmdConfig` with the specified configuration. + /// + /// This function sets up the AT command detection parameters, including + /// pre- and post-idle times, a gap timeout, the triggering command + /// character, and the number of characters to detect. + pub fn new( + pre_idle_count: Option, + post_idle_count: Option, + gap_timeout: Option, + cmd_char: u8, + char_num: Option, + ) -> AtCmdConfig { + Self { + pre_idle_count, + post_idle_count, + gap_timeout, + cmd_char, + char_num, } } } @@ -459,20 +443,20 @@ where } } - fn with_rx(self, rx: impl Peripheral

+ 'd) -> Self { + fn with_rx(self, rx: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(rx); rx.init_input(Pull::Up, Internal); - rx.connect_input_to_peripheral(self.uart.rx_signal(), Internal); + self.uart.info().rx_signal.connect_to(rx); self } - fn with_tx(self, tx: impl Peripheral

+ 'd) -> Self { + fn with_tx(self, tx: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(tx); // Make sure we don't cause an unexpected low pulse on the pin. tx.set_output_high(true, Internal); tx.set_to_push_pull_output(Internal); - tx.connect_peripheral_to_output(self.uart.tx_signal(), Internal); + self.uart.info().tx_signal.connect_to(tx); self } @@ -482,10 +466,6 @@ where rx: UartRx { uart: unsafe { self.uart.clone_unchecked() }, phantom: PhantomData, - at_cmd_config: None, - rx_timeout_config: None, - #[cfg(not(esp32))] - symbol_len: config.symbol_length(), }, tx: UartTx { uart: self.uart, @@ -514,10 +494,45 @@ pub struct UartTx<'d, M, T = AnyUart> { pub struct UartRx<'d, M, T = AnyUart> { uart: PeripheralRef<'d, T>, phantom: PhantomData, - at_cmd_config: Option, - rx_timeout_config: Option, - #[cfg(not(esp32))] - symbol_len: u8, +} + +impl SetConfig for Uart<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } +} + +impl SetConfig for UartRx<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } +} + +impl SetConfig for UartTx<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } } impl<'d, M, T> UartTx<'d, M, T> @@ -526,14 +541,22 @@ where M: Mode, { /// Configure RTS pin - pub fn with_rts(self, rts: impl Peripheral

+ 'd) -> Self { + pub fn with_rts(self, rts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(rts); rts.set_to_push_pull_output(Internal); - rts.connect_peripheral_to_output(self.uart.rts_signal(), Internal); + self.uart.info().rts_signal.connect_to(rts); self } + /// Change the configuration. + /// + /// Note that this also changes the configuration of the RX half. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.uart.info().apply_config(config)?; + Ok(()) + } + /// Writes bytes pub fn write_bytes(&mut self, data: &[u8]) -> Result { let count = data.len(); @@ -545,9 +568,8 @@ where } fn write_byte(&mut self, word: u8) -> nb::Result<(), Error> { - let register_block = self.uart.register_block(); - if get_tx_fifo_count(register_block) < UART_FIFO_SIZE { - register_block + if self.tx_fifo_count() < UART_FIFO_SIZE { + self.register_block() .fifo() .write(|w| unsafe { w.rxfifo_rd_byte().bits(word) }); @@ -557,31 +579,81 @@ where } } + #[allow(clippy::useless_conversion)] + /// Returns the number of bytes currently in the TX FIFO for this UART + /// instance. + fn tx_fifo_count(&self) -> u16 { + self.register_block() + .status() + .read() + .txfifo_cnt() + .bits() + .into() + } + /// Flush the transmit buffer of the UART pub fn flush_tx(&mut self) -> nb::Result<(), Error> { - if self.uart.is_tx_idle() { + if self.is_tx_idle() { Ok(()) } else { Err(nb::Error::WouldBlock) } } + + /// Checks if the TX line is idle for this UART instance. + /// + /// Returns `true` if the transmit line is idle, meaning no data is + /// currently being transmitted. + fn is_tx_idle(&self) -> bool { + #[cfg(esp32)] + let status = self.register_block().status(); + #[cfg(not(esp32))] + let status = self.register_block().fsm_status(); + + status.read().st_utx_out().bits() == 0x0 + } + + /// Disables all TX-related interrupts for this UART instance. + /// + /// This function clears and disables the `transmit FIFO empty` interrupt, + /// `transmit break done`, `transmit break idle done`, and `transmit done` + /// interrupts. + fn disable_tx_interrupts(&self) { + self.register_block().int_clr().write(|w| { + w.txfifo_empty().clear_bit_by_one(); + w.tx_brk_done().clear_bit_by_one(); + w.tx_brk_idle_done().clear_bit_by_one(); + w.tx_done().clear_bit_by_one() + }); + + self.register_block().int_ena().write(|w| { + w.txfifo_empty().clear_bit(); + w.tx_brk_done().clear_bit(); + w.tx_brk_idle_done().clear_bit(); + w.tx_done().clear_bit() + }); + } + + fn register_block(&self) -> &RegisterBlock { + self.uart.info().register_block() + } } impl<'d> UartTx<'d, Blocking> { /// Create a new UART TX instance in [`Blocking`] mode. - pub fn new( + pub fn new( uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_typed(uart.map_into(), tx) } /// Create a new UART TX instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config( + pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - tx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_with_config_typed(uart.map_into(), config, tx) } @@ -592,19 +664,19 @@ where T: Instance, { /// Create a new UART TX instance in [`Blocking`] mode. - pub fn new_typed( + pub fn new_typed( uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_with_config_typed(uart, Config::default(), tx) } /// Create a new UART TX instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config_typed( + pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, - tx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { let (_, uart_tx) = UartBuilder::<'d, Blocking, T>::new(uart) .with_tx(tx) @@ -613,96 +685,62 @@ where Ok(uart_tx) } -} -#[allow(clippy::useless_conversion)] -/// Returns the number of bytes currently in the TX FIFO for this UART -/// instance. -fn get_tx_fifo_count(register_block: &RegisterBlock) -> u16 { - register_block.status().read().txfifo_cnt().bits().into() -} + /// Reconfigures the driver to operate in [`Async`] mode. + pub fn into_async(self) -> UartTx<'d, Async, T> { + if !self.uart.state().is_rx_async.load(Ordering::Acquire) { + self.uart + .info() + .set_interrupt_handler(self.uart.info().async_handler); + } + self.uart.state().is_tx_async.store(true, Ordering::Release); -#[allow(clippy::useless_conversion)] -fn get_rx_fifo_count(register_block: &RegisterBlock) -> u16 { - let fifo_cnt: u16 = register_block.status().read().rxfifo_cnt().bits().into(); - // Calculate the real count based on the FIFO read and write offset address: - // https://www.espressif.com/sites/default/files/documentation/esp32_errata_en.pdf - // section 3.17 - #[cfg(esp32)] - { - let status = register_block.mem_rx_status().read(); - let rd_addr = status.mem_rx_rd_addr().bits(); - let wr_addr = status.mem_rx_wr_addr().bits(); - - if wr_addr > rd_addr { - wr_addr - rd_addr - } else if wr_addr < rd_addr { - (wr_addr + UART_FIFO_SIZE) - rd_addr - } else if fifo_cnt > 0 { - UART_FIFO_SIZE - } else { - 0 + UartTx { + uart: self.uart, + phantom: PhantomData, } } - - #[cfg(not(esp32))] - fifo_cnt } -fn read_bytes(register_block: &RegisterBlock, buf: &mut [u8]) -> Result<(), Error> { - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: - let fifo = unsafe { - &*((register_block.fifo().as_ptr() as *mut u8).add(0x20C00000) - as *mut crate::peripherals::uart0::FIFO) - }; - } else { - let fifo = register_block.fifo(); +impl<'d, T> UartTx<'d, Async, T> +where + T: Instance, +{ + /// Reconfigures the driver to operate in [`Blocking`] mode. + pub fn into_blocking(self) -> UartTx<'d, Blocking, T> { + self.uart + .state() + .is_tx_async + .store(false, Ordering::Release); + if !self.uart.state().is_rx_async.load(Ordering::Acquire) { + self.uart.info().disable_interrupts(); } - } - for byte in buf.iter_mut() { - while get_rx_fifo_count(register_block) == 0 { - // Block until we received at least one byte + UartTx { + uart: self.uart, + phantom: PhantomData, } - *byte = fifo.read().rxfifo_rd_byte().bits(); } - - Ok(()) } -fn read_byte(register_block: &RegisterBlock) -> nb::Result { - cfg_if::cfg_if! { - if #[cfg(esp32s2)] { - // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: - let fifo = unsafe { - &*((register_block.fifo().as_ptr() as *mut u8).add(0x20C00000) - as *mut crate::peripherals::uart0::FIFO) - }; - } else { - let fifo = register_block.fifo(); +#[inline(always)] +fn sync_regs(_register_block: &RegisterBlock) { + #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] + { + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let update_reg = _register_block.reg_update(); + } else { + let update_reg = _register_block.id(); + } } - } - if get_rx_fifo_count(register_block) > 0 { - Ok(fifo.read().rxfifo_rd_byte().bits()) - } else { - Err(nb::Error::WouldBlock) - } -} + update_reg.modify(|_, w| w.reg_update().set_bit()); -fn drain_fifo(register_block: &RegisterBlock, buf: &mut [u8]) -> usize { - let mut count = 0; - while count < buf.len() { - if let Ok(byte) = read_byte(register_block) { - buf[count] = byte; - count += 1; - } else { - break; + while update_reg.read().reg_update().bit_is_set() { + // wait } } - count } impl<'d, M, T> UartRx<'d, M, T> @@ -711,143 +749,167 @@ where M: Mode, { /// Configure CTS pin - pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { + pub fn with_cts(self, cts: impl Peripheral

+ 'd) -> Self { crate::into_mapped_ref!(cts); cts.init_input(Pull::None, Internal); - cts.connect_input_to_peripheral(self.uart.cts_signal(), Internal); + self.uart.info().cts_signal.connect_to(cts); self } - /// Fill a buffer with received bytes - pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { - read_bytes(self.uart.register_block(), buf) - } - - /// Read a byte from the UART - pub fn read_byte(&mut self) -> nb::Result { - read_byte(self.uart.register_block()) + /// Change the configuration. + /// + /// Note that this also changes the configuration of the TX half. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.uart.info().apply_config(config)?; + Ok(()) } - /// Read all available bytes from the RX FIFO into the provided buffer and - /// returns the number of read bytes. Never blocks - pub fn drain_fifo(&mut self, buf: &mut [u8]) -> usize { - drain_fifo(self.uart.register_block(), buf) - } + /// Fill a buffer with received bytes + pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: + let fifo = unsafe { + &*((self.register_block().fifo().as_ptr() as *mut u8).add(0x20C00000) + as *mut crate::peripherals::uart0::FIFO) + }; + } else { + let fifo = self.register_block().fifo(); + } + } - /// Configures the RX-FIFO threshold - /// - /// # Errors - /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value - /// for SOC : - /// - `esp32` **0x7F** - /// - `esp32c6`, `esp32h2` **0xFF** - /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** - /// - `esp32s3` **0x3FF** - fn set_rx_fifo_full_threshold(&mut self, threshold: u16) -> Result<(), Error> { - #[cfg(esp32)] - const MAX_THRHD: u16 = 0x7F; - #[cfg(any(esp32c6, esp32h2))] - const MAX_THRHD: u16 = 0xFF; - #[cfg(any(esp32c3, esp32c2, esp32s2))] - const MAX_THRHD: u16 = 0x1FF; - #[cfg(esp32s3)] - const MAX_THRHD: u16 = 0x3FF; + for byte in buf.iter_mut() { + while self.rx_fifo_count() == 0 { + // Block until we received at least one byte + } - if threshold > MAX_THRHD { - return Err(Error::InvalidArgument); + cfg_if::cfg_if! { + if #[cfg(esp32)] { + // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html + xtensa_lx::interrupt::free(|_| { + *byte = fifo.read().rxfifo_rd_byte().bits(); + }); + } else { + *byte = fifo.read().rxfifo_rd_byte().bits(); + } + } } - self.uart - .register_block() - .conf1() - .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); - Ok(()) } - /// Configures the Receive Timeout detection setting - /// - /// # Arguments - /// `timeout` - the number of symbols ("bytes") to wait for before - /// triggering a timeout. Pass None to disable the timeout. - /// - /// # Errors - /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum - /// value for SOC : - /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. - /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The - /// value you pass times the symbol size must be <= **0x3FF** - fn set_rx_timeout(&mut self, timeout: Option) -> Result<(), Error> { + /// Read a byte from the UART + pub fn read_byte(&mut self) -> nb::Result { cfg_if::cfg_if! { - if #[cfg(esp32)] { - const MAX_THRHD: u8 = 0x7F; // 7 bits + if #[cfg(esp32s2)] { + // On the ESP32-S2 we need to use PeriBus2 to read the FIFO: + let fifo = unsafe { + &*((self.register_block().fifo().as_ptr() as *mut u8).add(0x20C00000) + as *mut crate::peripherals::uart0::FIFO) + }; } else { - const MAX_THRHD: u16 = 0x3FF; // 10 bits + let fifo = self.register_block().fifo(); } } - cfg_if::cfg_if! { - if #[cfg(esp32)] { - let reg_thrhd = self.uart.register_block().conf1(); - } else if #[cfg(any(esp32c6, esp32h2))] { - let reg_thrhd = self.uart.register_block().tout_conf(); - } else { - let reg_thrhd = self.uart.register_block().mem_conf(); - } + if self.rx_fifo_count() > 0 { + Ok(fifo.read().rxfifo_rd_byte().bits()) + } else { + Err(nb::Error::WouldBlock) } + } - cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32h2))] { - let reg_en = self.uart.register_block().tout_conf(); + /// Read all available bytes from the RX FIFO into the provided buffer and + /// returns the number of read bytes. Never blocks + pub fn drain_fifo(&mut self, buf: &mut [u8]) -> usize { + let mut count = 0; + while count < buf.len() { + if let Ok(byte) = self.read_byte() { + buf[count] = byte; + count += 1; } else { - let reg_en = self.uart.register_block().conf1(); + break; } } + count + } - match timeout { - None => { - reg_en.modify(|_, w| w.rx_tout_en().clear_bit()); - } - Some(timeout) => { - // the esp32 counts directly in number of symbols (symbol len fixed to 8) - #[cfg(esp32)] - let timeout_reg = timeout; - // all other count in bits, so we need to multiply by the symbol len. - #[cfg(not(esp32))] - let timeout_reg = timeout as u16 * self.symbol_len as u16; - - if timeout_reg > MAX_THRHD { - return Err(Error::InvalidArgument); - } - - reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) }); - reg_en.modify(|_, w| w.rx_tout_en().set_bit()); + #[allow(clippy::useless_conversion)] + fn rx_fifo_count(&self) -> u16 { + let fifo_cnt: u16 = self + .register_block() + .status() + .read() + .rxfifo_cnt() + .bits() + .into(); + + // Calculate the real count based on the FIFO read and write offset address: + // https://www.espressif.com/sites/default/files/documentation/esp32_errata_en.pdf + // section 3.17 + #[cfg(esp32)] + { + let status = self.register_block().mem_rx_status().read(); + let rd_addr = status.mem_rx_rd_addr().bits(); + let wr_addr = status.mem_rx_wr_addr().bits(); + + if wr_addr > rd_addr { + wr_addr - rd_addr + } else if wr_addr < rd_addr { + (wr_addr + UART_FIFO_SIZE) - rd_addr + } else if fifo_cnt > 0 { + UART_FIFO_SIZE + } else { + 0 } } - self.rx_timeout_config = timeout; + #[cfg(not(esp32))] + fifo_cnt + } - self.uart.sync_regs(); - Ok(()) + /// Disables all RX-related interrupts for this UART instance. + /// + /// This function clears and disables the `receive FIFO full` interrupt, + /// `receive FIFO overflow`, `receive FIFO timeout`, and `AT command + /// character detection` interrupts. + fn disable_rx_interrupts(&self) { + self.register_block().int_clr().write(|w| { + w.rxfifo_full().clear_bit_by_one(); + w.rxfifo_ovf().clear_bit_by_one(); + w.rxfifo_tout().clear_bit_by_one(); + w.at_cmd_char_det().clear_bit_by_one() + }); + + self.register_block().int_ena().write(|w| { + w.rxfifo_full().clear_bit(); + w.rxfifo_ovf().clear_bit(); + w.rxfifo_tout().clear_bit(); + w.at_cmd_char_det().clear_bit() + }); + } + + fn register_block(&self) -> &RegisterBlock { + self.uart.info().register_block() } } impl<'d> UartRx<'d, Blocking> { /// Create a new UART RX instance in [`Blocking`] mode. - pub fn new( + pub fn new( uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, ) -> Result { UartRx::new_typed(uart.map_into(), rx) } /// Create a new UART RX instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config( + pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, ) -> Result { UartRx::new_with_config_typed(uart.map_into(), config, rx) } @@ -858,43 +920,79 @@ where T: Instance, { /// Create a new UART RX instance in [`Blocking`] mode. - pub fn new_typed( + pub fn new_typed( uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, ) -> Result { Self::new_with_config_typed(uart, Config::default(), rx) } /// Create a new UART RX instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config_typed( + pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, ) -> Result { let (uart_rx, _) = UartBuilder::new(uart).with_rx(rx).init(config)?.split(); Ok(uart_rx) } + + /// Reconfigures the driver to operate in [`Async`] mode. + pub fn into_async(self) -> UartRx<'d, Async, T> { + if !self.uart.state().is_tx_async.load(Ordering::Acquire) { + self.uart + .info() + .set_interrupt_handler(self.uart.info().async_handler); + } + self.uart.state().is_rx_async.store(true, Ordering::Release); + + UartRx { + uart: self.uart, + phantom: PhantomData, + } + } +} + +impl<'d, T> UartRx<'d, Async, T> +where + T: Instance, +{ + /// Reconfigures the driver to operate in [`Blocking`] mode. + pub fn into_blocking(self) -> UartRx<'d, Blocking, T> { + self.uart + .state() + .is_rx_async + .store(false, Ordering::Release); + if !self.uart.state().is_tx_async.load(Ordering::Acquire) { + self.uart.info().disable_interrupts(); + } + + UartRx { + uart: self.uart, + phantom: PhantomData, + } + } } impl<'d> Uart<'d, Blocking> { /// Create a new UART instance in [`Blocking`] mode. - pub fn new( + pub fn new( uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_typed(uart.map_into(), rx, tx) } /// Create a new UART instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config( + pub fn new_with_config( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_with_config_typed(uart.map_into(), config, rx, tx) } @@ -905,24 +1003,45 @@ where T: Instance, { /// Create a new UART instance in [`Blocking`] mode. - pub fn new_typed( + pub fn new_typed( uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { Self::new_with_config_typed(uart, Config::default(), rx, tx) } /// Create a new UART instance with configuration options in /// [`Blocking`] mode. - pub fn new_with_config_typed( + pub fn new_with_config_typed( uart: impl Peripheral

+ 'd, config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, + rx: impl Peripheral

+ 'd, + tx: impl Peripheral

+ 'd, ) -> Result { UartBuilder::new(uart).with_tx(tx).with_rx(rx).init(config) } + + /// Reconfigures the driver to operate in [`Async`] mode. + pub fn into_async(self) -> Uart<'d, Async, T> { + Uart { + rx: self.rx.into_async(), + tx: self.tx.into_async(), + } + } +} + +impl<'d, T> Uart<'d, Async, T> +where + T: Instance, +{ + /// Reconfigures the driver to operate in [`Blocking`] mode. + pub fn into_blocking(self) -> Uart<'d, Blocking, T> { + Uart { + rx: self.rx.into_blocking(), + tx: self.tx.into_blocking(), + } + } } /// List of exposed UART events. @@ -946,34 +1065,21 @@ where T: Instance, M: Mode, { - fn inner_set_interrupt_handler(&mut self, handler: InterruptHandler) { - // `self.tx.uart` and `self.rx.uart` are the same - unsafe { - crate::interrupt::bind_interrupt(self.tx.uart.interrupt(), handler.handler()); - crate::interrupt::enable(self.tx.uart.interrupt(), handler.priority()).unwrap(); - } - } - /// Configure CTS pin - pub fn with_cts(mut self, cts: impl Peripheral

+ 'd) -> Self { + pub fn with_cts(mut self, cts: impl Peripheral

+ 'd) -> Self { self.rx = self.rx.with_cts(cts); self } /// Configure RTS pin - pub fn with_rts(mut self, rts: impl Peripheral

+ 'd) -> Self { + pub fn with_rts(mut self, rts: impl Peripheral

+ 'd) -> Self { self.tx = self.tx.with_rts(rts); self } fn register_block(&self) -> &RegisterBlock { // `self.tx.uart` and `self.rx.uart` are the same - self.tx.uart.register_block() - } - - fn sync_regs(&self) { - // `self.tx.uart` and `self.rx.uart` are the same - self.tx.uart.sync_regs(); + self.tx.uart.info().register_block() } /// Split the UART into a transmitter and receiver @@ -995,7 +1101,7 @@ where } /// Configures the AT-CMD detection settings - pub fn set_at_cmd(&mut self, config: config::AtCmdConfig) { + pub fn set_at_cmd(&mut self, config: AtCmdConfig) { let register_block = self.register_block(); #[cfg(not(any(esp32, esp32s2)))] @@ -1031,71 +1137,27 @@ where .clk_conf() .modify(|_, w| w.sclk_en().set_bit()); - self.sync_regs(); - - self.rx.at_cmd_config = Some(config); - } - - /// Listen for the given interrupts - fn enable_listen(&mut self, interrupts: EnumSet, enable: bool) { - let reg_block = self.register_block(); - - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts { - match interrupt { - UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), - UartInterrupt::TxDone => w.tx_done().bit(enable), - UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), - }; - } - w - }); + sync_regs(register_block); } /// Listen for the given interrupts pub fn listen(&mut self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), true); + self.tx.uart.info().enable_listen(interrupts.into(), true) } /// Unlisten the given interrupts pub fn unlisten(&mut self, interrupts: impl Into>) { - self.enable_listen(interrupts.into(), false); + self.tx.uart.info().enable_listen(interrupts.into(), false) } /// Gets asserted interrupts pub fn interrupts(&mut self) -> EnumSet { - let mut res = EnumSet::new(); - let reg_block = self.register_block(); - - let ints = reg_block.int_raw().read(); - - if ints.at_cmd_char_det().bit_is_set() { - res.insert(UartInterrupt::AtCmd); - } - if ints.tx_done().bit_is_set() { - res.insert(UartInterrupt::TxDone); - } - if ints.rxfifo_full().bit_is_set() { - res.insert(UartInterrupt::RxFifoFull); - } - - res + self.tx.uart.info().interrupts() } /// Resets asserted interrupts pub fn clear_interrupts(&mut self, interrupts: EnumSet) { - let reg_block = self.register_block(); - - reg_block.int_clr().write(|w| { - for interrupt in interrupts { - match interrupt { - UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), - UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), - UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), - }; - } - w - }); + self.tx.uart.info().clear_interrupts(interrupts) } /// Write a byte out over the UART @@ -1113,185 +1175,10 @@ where self.rx.read_byte() } - /// Change the number of stop bits - pub fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { - // workaround for hardware issue, when UART stop bit set as 2-bit mode. - #[cfg(esp32)] - if stop_bits == config::StopBits::STOP2 { - self.register_block() - .rs485_conf() - .modify(|_, w| w.dl1_en().bit(true)); - - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(1) }); - } else { - self.register_block() - .rs485_conf() - .modify(|_, w| w.dl1_en().bit(false)); - - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); - } - - #[cfg(not(esp32))] - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); - - self - } - - fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); - - self - } - - fn change_parity(&mut self, parity: config::Parity) -> &mut Self { - self.register_block().conf0().modify(|_, w| match parity { - config::Parity::ParityNone => w.parity_en().clear_bit(), - config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), - }); - - self - } - - #[cfg(any(esp32c2, esp32c3, esp32s3))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => RC_FAST_CLK.to_Hz(), - }; - - if clock_source == ClockSource::RcFast { - let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() }; - rtc_cntl - .clk_conf() - .modify(|_, w| w.dig_clk8m_en().variant(true)); - // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); - crate::rom::ets_delay_us(5); - } - - let max_div = 0b1111_1111_1111 - 1; - let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); - self.register_block().clk_conf().write(|w| unsafe { - w.sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.sclk_div_a().bits(0); - w.sclk_div_b().bits(0); - w.sclk_div_num().bits(clk_div as u8 - 1); - w.rx_sclk_en().bit(true); - w.tx_sclk_en().bit(true) - }); - - let divider = (clk << 4) / (baudrate * clk_div); - let divider_integer = (divider >> 4) as u16; - let divider_frag = (divider & 0xf) as u8; - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); - } - - #[cfg(any(esp32c6, esp32h2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => RC_FAST_CLK.to_Hz(), - }; - - let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); - - // UART clocks are configured via PCR - let pcr = unsafe { &*crate::peripherals::PCR::PTR }; - - match self.tx.uart.uart_number() { - 0 => { - pcr.uart0_conf() - .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); - - pcr.uart0_sclk_conf().modify(|_, w| unsafe { - w.uart0_sclk_div_a().bits(0); - w.uart0_sclk_div_b().bits(0); - w.uart0_sclk_div_num().bits(clk_div as u8 - 1); - w.uart0_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.uart0_sclk_en().set_bit() - }); - } - 1 => { - pcr.uart1_conf() - .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); - - pcr.uart1_sclk_conf().modify(|_, w| unsafe { - w.uart1_sclk_div_a().bits(0); - w.uart1_sclk_div_b().bits(0); - w.uart1_sclk_div_num().bits(clk_div as u8 - 1); - w.uart1_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.uart1_sclk_en().set_bit() - }); - } - _ => unreachable!(), // ESP32-C6 only has 2 UART instances - } - - let clk = clk / clk_div; - let divider = clk / baudrate; - let divider = divider as u16; - - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); - - self.sync_regs(); - } - - #[cfg(any(esp32, esp32s2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::RefTick => REF_TICK.to_Hz(), /* ESP32(/-S2) TRM, section 3.2.4.2 - * (6.2.4.2 for S2) */ - }; - - self.register_block().conf0().modify(|_, w| { - w.tick_ref_always_on().bit(match clock_source { - ClockSource::Apb => true, - ClockSource::RefTick => false, - }) - }); - - let divider = clk / baudrate; - - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); - } - - /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource) { - self.change_baud_internal(baudrate, clock_source); - self.txfifo_reset(); - self.rxfifo_reset(); + /// Change the configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.rx.apply_config(config)?; + Ok(()) } #[inline(always)] @@ -1300,8 +1187,7 @@ where if #[cfg(any(esp32, esp32s2))] { // Nothing to do } else if #[cfg(any(esp32c2, esp32c3, esp32s3))] { - let system = unsafe { crate::peripherals::SYSTEM::steal() }; - system + unsafe { crate::peripherals::SYSTEM::steal() } .perip_clk_en0() .modify(|_, w| w.uart_mem_clk_en().set_bit()); } else { @@ -1311,18 +1197,12 @@ where } }; - PeripheralClockControl::enable(self.tx.uart.peripheral()); + PeripheralClockControl::enable(self.tx.uart.info().peripheral); self.uart_peripheral_reset(); - self.tx.uart.disable_rx_interrupts(); - self.tx.uart.disable_tx_interrupts(); + self.rx.disable_rx_interrupts(); + self.tx.disable_tx_interrupts(); - self.rx - .set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; - self.rx.set_rx_timeout(config.rx_timeout)?; - self.change_baud_internal(config.baudrate, config.clock_source); - self.change_data_bits(config.data_bits); - self.change_parity(config.parity); - self.change_stop_bits(config.stop_bits); + self.rx.uart.info().apply_config(&config)?; // Setting err_wr_mask stops uart from storing data when data is wrong according // to reference manual @@ -1330,9 +1210,6 @@ where .conf0() .modify(|_, w| w.err_wr_mask().set_bit()); - // Reset Tx/Rx FIFOs - self.rxfifo_reset(); - self.txfifo_reset(); crate::rom::ets_delay_us(15); // Make sure we are starting in a "clean state" - previous operations might have @@ -1344,6 +1221,10 @@ where Ok(()) } + fn is_instance(&self, other: impl Instance) -> bool { + self.tx.uart.info().is_instance(other) + } + #[inline(always)] fn uart_peripheral_reset(&self) { // don't reset the console UART - this will cause trouble (i.e. the UART will @@ -1354,239 +1235,37 @@ where // already enough to make this a no-go. (i.e. one needs to mute the ROM // code via efuse / strapping pin AND use a silent bootloader) // - // Ideally this should be configurable once we have a solution for https://github.com/esp-rs/esp-hal/issues/1111 + // TODO: make this configurable // see https://github.com/espressif/esp-idf/blob/5f4249357372f209fdd57288265741aaba21a2b1/components/esp_driver_uart/src/uart.c#L179 - if self.tx.uart.uart_number() == CONSOLE_UART_NUM { + if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { return; } - #[cfg(not(any(esp32, esp32s2)))] - self.register_block() - .clk_conf() - .modify(|_, w| w.rst_core().set_bit()); - - PeripheralClockControl::reset(self.tx.uart.peripheral()); + fn rst_core(_reg_block: &RegisterBlock, _enable: bool) { + #[cfg(not(any(esp32, esp32s2)))] + _reg_block + .clk_conf() + .modify(|_, w| w.rst_core().bit(_enable)); + } - #[cfg(not(any(esp32, esp32s2)))] - self.register_block() - .clk_conf() - .modify(|_, w| w.rst_core().clear_bit()); + rst_core(self.register_block(), true); + PeripheralClockControl::reset(self.tx.uart.info().peripheral); + rst_core(self.register_block(), false); } +} - fn rxfifo_reset(&mut self) { - self.register_block() - .conf0() - .modify(|_, w| w.rxfifo_rst().set_bit()); - self.sync_regs(); - - self.register_block() - .conf0() - .modify(|_, w| w.rxfifo_rst().clear_bit()); - self.sync_regs(); - } - - fn txfifo_reset(&mut self) { - self.register_block() - .conf0() - .modify(|_, w| w.txfifo_rst().set_bit()); - self.sync_regs(); - - self.register_block() - .conf0() - .modify(|_, w| w.txfifo_rst().clear_bit()); - self.sync_regs(); - } -} - -impl crate::private::Sealed for Uart<'_, Blocking, T> where T: Instance {} +impl crate::private::Sealed for Uart<'_, Blocking, T> where T: Instance {} impl InterruptConfigurable for Uart<'_, Blocking, T> where T: Instance, { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - self.inner_set_interrupt_handler(handler); - } -} - -/// UART Peripheral Instance -pub trait Instance: Peripheral

+ PeripheralMarker + Into + 'static { - /// Returns a reference to the UART register block for the specific - /// instance. - /// - /// # Safety - /// This function returns a reference to the raw hardware registers, so - /// direct interaction with the registers may require careful handling - /// to avoid unintended side effects. - fn register_block(&self) -> &RegisterBlock; - - /// Returns the UART number associated with this instance (e.g., UART0, - /// UART1, etc.). - fn uart_number(&self) -> usize; - - /// Returns the interrupt handler for this UART instance. - fn async_handler(&self) -> InterruptHandler; - - /// Returns the interrupt associated with this UART instance. - fn interrupt(&self) -> Interrupt; - - #[doc(hidden)] - #[inline(always)] - fn sync_regs(&self) { - #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] - { - cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32h2))] { - let update_reg = self.register_block().reg_update(); - } else { - let update_reg = self.register_block().id(); - } - } - - update_reg.modify(|_, w| w.reg_update().set_bit()); - - while update_reg.read().reg_update().bit_is_set() { - // wait - } - } - } - - /// Disables all TX-related interrupts for this UART instance. - /// - /// This function clears and disables the `transmit FIFO empty` interrupt, - /// `transmit break done`, `transmit break idle done`, and `transmit done` - /// interrupts. - fn disable_tx_interrupts(&self) { - self.register_block().int_clr().write(|w| { - w.txfifo_empty().clear_bit_by_one(); - w.tx_brk_done().clear_bit_by_one(); - w.tx_brk_idle_done().clear_bit_by_one(); - w.tx_done().clear_bit_by_one() - }); - - self.register_block().int_ena().write(|w| { - w.txfifo_empty().clear_bit(); - w.tx_brk_done().clear_bit(); - w.tx_brk_idle_done().clear_bit(); - w.tx_done().clear_bit() - }); - } - - /// Disables all RX-related interrupts for this UART instance. - /// - /// This function clears and disables the `receive FIFO full` interrupt, - /// `receive FIFO overflow`, `receive FIFO timeout`, and `AT command - /// character detection` interrupts. - fn disable_rx_interrupts(&self) { - self.register_block().int_clr().write(|w| { - w.rxfifo_full().clear_bit_by_one(); - w.rxfifo_ovf().clear_bit_by_one(); - w.rxfifo_tout().clear_bit_by_one(); - w.at_cmd_char_det().clear_bit_by_one() - }); - - self.register_block().int_ena().write(|w| { - w.rxfifo_full().clear_bit(); - w.rxfifo_ovf().clear_bit(); - w.rxfifo_tout().clear_bit(); - w.at_cmd_char_det().clear_bit() - }); - } - - /// Checks if the TX line is idle for this UART instance. - /// - /// Returns `true` if the transmit line is idle, meaning no data is - /// currently being transmitted. - fn is_tx_idle(&self) -> bool { - #[cfg(esp32)] - let status = self.register_block().status(); - #[cfg(not(esp32))] - let status = self.register_block().fsm_status(); - - status.read().st_utx_out().bits() == 0x0 - } - - /// Checks if the RX line is idle for this UART instance. - /// - /// Returns `true` if the receive line is idle, meaning no data is currently - /// being received. - fn is_rx_idle(&self) -> bool { - #[cfg(esp32)] - let status = self.register_block().status(); - #[cfg(not(esp32))] - let status = self.register_block().fsm_status(); - - status.read().st_urx_out().bits() == 0x0 + // `self.tx.uart` and `self.rx.uart` are the same + self.tx.uart.info().set_interrupt_handler(handler); } - - /// Returns the output signal identifier for the TX pin of this UART - /// instance. - fn tx_signal(&self) -> OutputSignal; - - /// Returns the input signal identifier for the RX pin of this UART - /// instance. - fn rx_signal(&self) -> InputSignal; - - /// Returns the input signal identifier for the CTS (Clear to Send) pin of - /// this UART instance. - fn cts_signal(&self) -> InputSignal; - - /// Returns the output signal identifier for the RTS (Request to Send) pin - /// of this UART instance. - fn rts_signal(&self) -> OutputSignal; -} - -macro_rules! impl_instance { - ($inst:ident, $num:expr, $txd:ident, $rxd:ident, $cts:ident, $rts:ident, $async_handler:path) => { - impl Instance for crate::peripherals::$inst { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - unsafe { &*crate::peripherals::$inst::PTR } - } - - #[inline(always)] - fn uart_number(&self) -> usize { - $num - } - - #[inline(always)] - fn async_handler(&self) -> InterruptHandler { - $async_handler - } - - #[inline(always)] - fn interrupt(&self) -> Interrupt { - Interrupt::$inst - } - - #[inline(always)] - fn tx_signal(&self) -> OutputSignal { - OutputSignal::$txd - } - - #[inline(always)] - fn rx_signal(&self) -> InputSignal { - InputSignal::$rxd - } - - #[inline(always)] - fn cts_signal(&self) -> InputSignal { - InputSignal::$cts - } - - #[inline(always)] - fn rts_signal(&self) -> OutputSignal { - OutputSignal::$rts - } - } - }; } -impl_instance!(UART0, 0, U0TXD, U0RXD, U0CTS, U0RTS, asynch::uart0); -impl_instance!(UART1, 1, U1TXD, U1RXD, U1CTS, U1RTS, asynch::uart1); -#[cfg(uart2)] -impl_instance!(UART2, 2, U2TXD, U2RXD, U2CTS, U2RTS, asynch::uart2); - impl ufmt_write::uWrite for Uart<'_, M, T> where T: Instance, @@ -1791,7 +1470,7 @@ where return Ok(0); } - while get_rx_fifo_count(self.uart.register_block()) == 0 { + while self.rx_fifo_count() == 0 { // Block until we received at least one byte } @@ -1815,7 +1494,7 @@ where M: Mode, { fn read_ready(&mut self) -> Result { - Ok(get_rx_fifo_count(self.uart.register_block()) > 0) + Ok(self.rx_fifo_count() > 0) } } @@ -1855,605 +1534,416 @@ where } } -mod asynch { - use core::task::Poll; - - use embassy_sync::waitqueue::AtomicWaker; - use enumset::{EnumSet, EnumSetType}; - use procmacros::handler; - - use super::*; - use crate::Async; - - const NUM_UART: usize = 1 + cfg!(uart1) as usize + cfg!(uart2) as usize; - - static TX_WAKERS: [AtomicWaker; NUM_UART] = [const { AtomicWaker::new() }; NUM_UART]; - static RX_WAKERS: [AtomicWaker; NUM_UART] = [const { AtomicWaker::new() }; NUM_UART]; - - #[derive(EnumSetType, Debug)] - pub(crate) enum TxEvent { - TxDone, - TxFiFoEmpty, - } - #[derive(EnumSetType, Debug)] - pub(crate) enum RxEvent { - FifoFull, - CmdCharDetected, - FifoOvf, - FifoTout, - GlitchDetected, - FrameError, - ParityError, - } +#[derive(EnumSetType, Debug)] +pub(crate) enum TxEvent { + TxDone, + TxFiFoEmpty, +} +#[derive(EnumSetType, Debug)] +pub(crate) enum RxEvent { + FifoFull, + CmdCharDetected, + FifoOvf, + FifoTout, + GlitchDetected, + FrameError, + ParityError, +} - /// A future that resolves when the passed interrupt is triggered, - /// or has been triggered in the meantime (flag set in INT_RAW). - /// Upon construction the future enables the passed interrupt and when it - /// is dropped it disables the interrupt again. The future returns the event - /// that was initially passed, when it resolves. - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct UartRxFuture<'d, T> - where - T: Instance, - { - events: EnumSet, - uart: &'d mut T, - registered: bool, - } - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct UartTxFuture<'d, T> - where - T: Instance, - { - events: EnumSet, - uart: &'d mut T, - registered: bool, - } +/// A future that resolves when the passed interrupt is triggered, +/// or has been triggered in the meantime (flag set in INT_RAW). +/// Upon construction the future enables the passed interrupt and when it +/// is dropped it disables the interrupt again. The future returns the event +/// that was initially passed, when it resolves. +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct UartRxFuture { + events: EnumSet, + uart: &'static Info, + state: &'static State, + registered: bool, +} - impl<'d, T: Instance> UartRxFuture<'d, T> { - pub fn new(uart: &'d mut T, events: impl Into>) -> Self { - Self { - events: events.into(), - uart, - registered: false, +impl UartRxFuture { + fn new(uart: impl Peripheral

, events: impl Into>) -> Self { + crate::into_ref!(uart); + Self { + events: events.into(), + uart: uart.info(), + state: uart.state(), + registered: false, + } + } + + fn triggered_events(&self) -> EnumSet { + let interrupts_enabled = self.uart.register_block().int_ena().read(); + let mut events_triggered = EnumSet::new(); + for event in self.events { + let event_triggered = match event { + RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), + RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), + + RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), + RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), + RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), + RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_clear(), + RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_clear(), + }; + if event_triggered { + events_triggered |= event; } } + events_triggered + } - fn get_triggered_events(&self) -> EnumSet { - let interrupts_enabled = self.uart.register_block().int_ena().read(); - let mut events_triggered = EnumSet::new(); + fn enable_listen(&self, enable: bool) { + self.uart.register_block().int_ena().modify(|_, w| { for event in self.events { - let event_triggered = match event { - RxEvent::FifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), - RxEvent::CmdCharDetected => interrupts_enabled.at_cmd_char_det().bit_is_clear(), - - RxEvent::FifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), - RxEvent::FifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), - RxEvent::GlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(), - RxEvent::FrameError => interrupts_enabled.frm_err().bit_is_clear(), - RxEvent::ParityError => interrupts_enabled.parity_err().bit_is_clear(), + match event { + RxEvent::FifoFull => w.rxfifo_full().bit(enable), + RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), + RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), + RxEvent::FifoTout => w.rxfifo_tout().bit(enable), + RxEvent::GlitchDetected => w.glitch_det().bit(enable), + RxEvent::FrameError => w.frm_err().bit(enable), + RxEvent::ParityError => w.parity_err().bit(enable), }; - if event_triggered { - events_triggered |= event; - } } - events_triggered - } - - fn enable_listen(&self, enable: bool) { - self.uart.register_block().int_ena().modify(|_, w| { - for event in self.events { - match event { - RxEvent::FifoFull => w.rxfifo_full().bit(enable), - RxEvent::CmdCharDetected => w.at_cmd_char_det().bit(enable), - RxEvent::FifoOvf => w.rxfifo_ovf().bit(enable), - RxEvent::FifoTout => w.rxfifo_tout().bit(enable), - RxEvent::GlitchDetected => w.glitch_det().bit(enable), - RxEvent::FrameError => w.frm_err().bit(enable), - RxEvent::ParityError => w.parity_err().bit(enable), - }; - } - w - }); - } + w + }); } +} - impl<'d, T: Instance> core::future::Future for UartRxFuture<'d, T> { - type Output = EnumSet; - - fn poll( - mut self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - if !self.registered { - RX_WAKERS[self.uart.uart_number()].register(cx.waker()); - self.enable_listen(true); - self.registered = true; - } - let events = self.get_triggered_events(); - if !events.is_empty() { - Poll::Ready(events) - } else { - Poll::Pending - } +impl core::future::Future for UartRxFuture { + type Output = EnumSet; + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + if !self.registered { + self.state.rx_waker.register(cx.waker()); + self.enable_listen(true); + self.registered = true; + } + let events = self.triggered_events(); + if !events.is_empty() { + Poll::Ready(events) + } else { + Poll::Pending } } +} - impl<'d, T: Instance> Drop for UartRxFuture<'d, T> { - fn drop(&mut self) { - // Although the isr disables the interrupt that occurred directly, we need to - // disable the other interrupts (= the ones that did not occur), as - // soon as this future goes out of scope. - self.enable_listen(false); - } +impl Drop for UartRxFuture { + fn drop(&mut self) { + // Although the isr disables the interrupt that occurred directly, we need to + // disable the other interrupts (= the ones that did not occur), as + // soon as this future goes out of scope. + self.enable_listen(false); } +} - impl<'d, T: Instance> UartTxFuture<'d, T> { - pub fn new(uart: &'d mut T, events: impl Into>) -> Self { - Self { - events: events.into(), - uart, - registered: false, - } +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct UartTxFuture { + events: EnumSet, + uart: &'static Info, + state: &'static State, + registered: bool, +} +impl UartTxFuture { + fn new(uart: impl Peripheral

, events: impl Into>) -> Self { + crate::into_ref!(uart); + Self { + events: events.into(), + uart: uart.info(), + state: uart.state(), + registered: false, } + } - fn get_triggered_events(&self) -> bool { - let interrupts_enabled = self.uart.register_block().int_ena().read(); - let mut event_triggered = false; - for event in self.events { - event_triggered |= match event { - TxEvent::TxDone => interrupts_enabled.tx_done().bit_is_clear(), - TxEvent::TxFiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), - } + fn triggered_events(&self) -> bool { + let interrupts_enabled = self.uart.register_block().int_ena().read(); + let mut event_triggered = false; + for event in self.events { + event_triggered |= match event { + TxEvent::TxDone => interrupts_enabled.tx_done().bit_is_clear(), + TxEvent::TxFiFoEmpty => interrupts_enabled.txfifo_empty().bit_is_clear(), } - event_triggered - } - - fn enable_listen(&self, enable: bool) { - self.uart.register_block().int_ena().modify(|_, w| { - for event in self.events { - match event { - TxEvent::TxDone => w.tx_done().bit(enable), - TxEvent::TxFiFoEmpty => w.txfifo_empty().bit(enable), - }; - } - w - }); } + event_triggered } - impl<'d, T: Instance> core::future::Future for UartTxFuture<'d, T> { - type Output = (); - - fn poll( - mut self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - if !self.registered { - TX_WAKERS[self.uart.uart_number()].register(cx.waker()); - self.enable_listen(true); - self.registered = true; - } - - if self.get_triggered_events() { - Poll::Ready(()) - } else { - Poll::Pending + fn enable_listen(&self, enable: bool) { + self.uart.register_block().int_ena().modify(|_, w| { + for event in self.events { + match event { + TxEvent::TxDone => w.tx_done().bit(enable), + TxEvent::TxFiFoEmpty => w.txfifo_empty().bit(enable), + }; } - } + w + }); } +} - impl<'d, T: Instance> Drop for UartTxFuture<'d, T> { - fn drop(&mut self) { - // Although the isr disables the interrupt that occurred directly, we need to - // disable the other interrupts (= the ones that did not occur), as - // soon as this future goes out of scope. - self.enable_listen(false); - } - } +impl core::future::Future for UartTxFuture { + type Output = (); - impl<'d> Uart<'d, Async> { - /// Create a new UART instance with defaults in [`Async`] mode. - pub fn new_async( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Uart::new_async_typed(uart.map_into(), rx, tx) + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + if !self.registered { + self.state.tx_waker.register(cx.waker()); + self.enable_listen(true); + self.registered = true; } - /// Create a new UART instance with configuration options in [`Async`] - /// mode. - pub fn new_async_with_config( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Uart::new_async_with_config_typed(uart.map_into(), config, rx, tx) + if self.triggered_events() { + Poll::Ready(()) + } else { + Poll::Pending } } +} - impl<'d, T> Uart<'d, Async, T> - where - T: Instance, - { - /// Create a new UART instance with defaults in [`Async`] mode. - pub fn new_async_typed( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_async_with_config_typed(uart, Config::default(), rx, tx) - } - - /// Create a new UART instance with configuration options in [`Async`] - /// mode. - pub fn new_async_with_config_typed( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - // FIXME: at the time of writing, the order of the pin assignments matters: - // first binding RX, then TX makes tests fail. This is bad and needs to be - // figured out. - let mut this = UartBuilder::new(uart) - .with_tx(tx) - .with_rx(rx) - .init(config)?; - - this.inner_set_interrupt_handler(this.tx.uart.async_handler()); - - Ok(this) - } - } - - impl Uart<'_, Async, T> - where - T: Instance, - { - /// Asynchronously reads data from the UART receive buffer into the - /// provided buffer. - pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { - self.rx.read_async(buf).await - } - - /// Asynchronously writes data to the UART transmit buffer. - pub async fn write_async(&mut self, words: &[u8]) -> Result { - self.tx.write_async(words).await - } - - /// Asynchronously flushes the UART transmit buffer. - pub async fn flush_async(&mut self) -> Result<(), Error> { - self.tx.flush_async().await - } +impl Drop for UartTxFuture { + fn drop(&mut self) { + // Although the isr disables the interrupt that occurred directly, we need to + // disable the other interrupts (= the ones that did not occur), as + // soon as this future goes out of scope. + self.enable_listen(false); } +} - impl<'d> UartTx<'d, Async> { - /// Create a new UART TX instance in [`Async`] mode. - pub fn new_async( - uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - UartTx::new_async_typed(uart.map_into(), tx) - } - - /// Create a new UART TX instance with configuration options in - /// [`Async`] mode. - pub fn new_async_with_config( - uart: impl Peripheral

+ 'd, - config: Config, - tx: impl Peripheral

+ 'd, - ) -> Result { - UartTx::new_async_with_config_typed(uart.map_into(), config, tx) - } +impl Uart<'_, Async, T> +where + T: Instance, +{ + /// Asynchronously reads data from the UART receive buffer into the + /// provided buffer. + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + self.rx.read_async(buf).await } - impl<'d, T> UartTx<'d, Async, T> - where - T: Instance, - { - /// Create a new UART TX instance in [`Async`] mode. - pub fn new_async_typed( - uart: impl Peripheral

+ 'd, - tx: impl Peripheral

+ 'd, - ) -> Result { - UartTx::new_async_with_config_typed(uart, Config::default(), tx) - } - - /// Create a new UART TX instance with configuration options in - /// [`Async`] mode. - pub fn new_async_with_config_typed( - uart: impl Peripheral

+ 'd, - config: Config, - tx: impl Peripheral

+ 'd, - ) -> Result { - let mut this = UartBuilder::new(uart).with_tx(tx).init(config)?; - - this.inner_set_interrupt_handler(this.tx.uart.async_handler()); - - let (_, uart_tx) = this.split(); - Ok(uart_tx) - } - - /// Asynchronously writes data to the UART transmit buffer in chunks. - /// - /// This function sends the contents of the provided buffer `words` over - /// the UART. Data is written in chunks to avoid overflowing the - /// transmit FIFO, and the function waits asynchronously when - /// necessary for space in the buffer to become available. - pub async fn write_async(&mut self, words: &[u8]) -> Result { - let mut count = 0; - let mut offset: usize = 0; - loop { - let register_block = self.uart.register_block(); - let mut next_offset = - offset + (UART_FIFO_SIZE - get_tx_fifo_count(register_block)) as usize; - if next_offset > words.len() { - next_offset = words.len(); - } - - for byte in &words[offset..next_offset] { - self.write_byte(*byte).unwrap(); // should never fail - count += 1; - } + /// Asynchronously writes data to the UART transmit buffer. + pub async fn write_async(&mut self, words: &[u8]) -> Result { + self.tx.write_async(words).await + } - if next_offset >= words.len() { - break; - } + /// Asynchronously flushes the UART transmit buffer. + pub async fn flush_async(&mut self) -> Result<(), Error> { + self.tx.flush_async().await + } +} - offset = next_offset; - UartTxFuture::::new(&mut *self.uart, TxEvent::TxFiFoEmpty).await; +impl UartTx<'_, Async, T> +where + T: Instance, +{ + /// Asynchronously writes data to the UART transmit buffer in chunks. + /// + /// This function sends the contents of the provided buffer `words` over + /// the UART. Data is written in chunks to avoid overflowing the + /// transmit FIFO, and the function waits asynchronously when + /// necessary for space in the buffer to become available. + pub async fn write_async(&mut self, words: &[u8]) -> Result { + let mut count = 0; + let mut offset: usize = 0; + loop { + let mut next_offset = offset + (UART_FIFO_SIZE - self.tx_fifo_count()) as usize; + if next_offset > words.len() { + next_offset = words.len(); } - Ok(count) - } + for byte in &words[offset..next_offset] { + self.write_byte(*byte).unwrap(); // should never fail + count += 1; + } - /// Asynchronously flushes the UART transmit buffer. - /// - /// This function ensures that all pending data in the transmit FIFO has - /// been sent over the UART. If the FIFO contains data, it waits - /// for the transmission to complete before returning. - pub async fn flush_async(&mut self) -> Result<(), Error> { - let count = get_tx_fifo_count(self.uart.register_block()); - if count > 0 { - UartTxFuture::::new(&mut *self.uart, TxEvent::TxDone).await; + if next_offset >= words.len() { + break; } - Ok(()) + offset = next_offset; + UartTxFuture::new(self.uart.reborrow(), TxEvent::TxFiFoEmpty).await; } + + Ok(count) } - impl<'d> UartRx<'d, Async> { - /// Create a new UART RX instance in [`Async`] mode. - pub fn new_async( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_async_typed(uart.map_into(), rx) + /// Asynchronously flushes the UART transmit buffer. + /// + /// This function ensures that all pending data in the transmit FIFO has + /// been sent over the UART. If the FIFO contains data, it waits + /// for the transmission to complete before returning. + pub async fn flush_async(&mut self) -> Result<(), Error> { + let count = self.tx_fifo_count(); + if count > 0 { + UartTxFuture::new(self.uart.reborrow(), TxEvent::TxDone).await; } - /// Create a new UART RX instance with configuration options in - /// [`Async`] mode. - pub fn new_async_with_config( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_async_with_config_typed(uart.map_into(), config, rx) - } + Ok(()) } +} - impl<'d, T> UartRx<'d, Async, T> - where - T: Instance, - { - /// Create a new UART RX instance in [`Async`] mode. - pub fn new_async_typed( - uart: impl Peripheral

+ 'd, - rx: impl Peripheral

+ 'd, - ) -> Result { - Self::new_async_with_config_typed(uart, Config::default(), rx) - } - - /// Create a new UART RX instance with configuration options in - /// [`Async`] mode. - pub fn new_async_with_config_typed( - uart: impl Peripheral

+ 'd, - config: Config, - rx: impl Peripheral

+ 'd, - ) -> Result { - let mut this = UartBuilder::new(uart).with_rx(rx).init(config)?; - - this.inner_set_interrupt_handler(this.tx.uart.async_handler()); - - let (uart_rx, _) = this.split(); - Ok(uart_rx) - } - - /// Read async to buffer slice `buf`. - /// Waits until at least one byte is in the Rx FiFo - /// and one of the following interrupts occurs: - /// - `RXFIFO_FULL` - /// - `RXFIFO_OVF` - /// - `AT_CMD_CHAR_DET` (only if `set_at_cmd` was called) - /// - `RXFIFO_TOUT` (only if `set_rx_timeout was called) - /// - /// The interrupts in question are enabled during the body of this - /// function. The method immediately returns when the interrupt - /// has already occurred before calling this method (e.g. status - /// bit set, but interrupt not enabled) - /// - /// # Params - /// - `buf` buffer slice to write the bytes into - /// - /// - /// # Ok - /// When successful, returns the number of bytes written to buf. - /// This method will never return Ok(0) - pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Err(Error::InvalidArgument); - } - - loop { - let mut events = RxEvent::FifoFull - | RxEvent::FifoOvf - | RxEvent::FrameError - | RxEvent::GlitchDetected - | RxEvent::ParityError; +impl UartRx<'_, Async, T> +where + T: Instance, +{ + /// Read async to buffer slice `buf`. + /// Waits until at least one byte is in the Rx FiFo + /// and one of the following interrupts occurs: + /// - `RXFIFO_FULL` + /// - `RXFIFO_OVF` + /// - `AT_CMD_CHAR_DET` (only if `set_at_cmd` was called) + /// - `RXFIFO_TOUT` (only if `set_rx_timeout was called) + /// + /// The interrupts in question are enabled during the body of this + /// function. The method immediately returns when the interrupt + /// has already occurred before calling this method (e.g. status + /// bit set, but interrupt not enabled) + /// + /// # Params + /// - `buf` buffer slice to write the bytes into + /// + /// + /// # Ok + /// When successful, returns the number of bytes written to buf. + /// This method will never return Ok(0) + pub async fn read_async(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Err(Error::InvalidArgument); + } - if self.at_cmd_config.is_some() { - events |= RxEvent::CmdCharDetected; - } + loop { + let mut events = RxEvent::FifoFull + | RxEvent::FifoOvf + | RxEvent::FrameError + | RxEvent::GlitchDetected + | RxEvent::ParityError; + + let register_block = self.uart.info().register_block(); + if register_block.at_cmd_char().read().char_num().bits() > 0 { + events |= RxEvent::CmdCharDetected; + } - if self.rx_timeout_config.is_some() { - events |= RxEvent::FifoTout; - } - let events_happened = UartRxFuture::::new(&mut self.uart, events).await; - // always drain the fifo, if an error has occurred the data is lost - let read_bytes = self.drain_fifo(buf); - // check error events - for event_happened in events_happened { - match event_happened { - RxEvent::FifoOvf => return Err(Error::RxFifoOvf), - RxEvent::GlitchDetected => return Err(Error::RxGlitchDetected), - RxEvent::FrameError => return Err(Error::RxFrameError), - RxEvent::ParityError => return Err(Error::RxParityError), - RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => { - continue - } - } + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let reg_en = register_block.tout_conf(); + } else { + let reg_en = register_block.conf1(); } - // Unfortunately, the uart's rx-timeout counter counts up whenever there is - // data in the fifo, even if the interrupt is disabled and the status bit - // cleared. Since we do not drain the fifo in the interrupt handler, we need to - // reset the counter here, after draining the fifo. - self.uart - .register_block() - .int_clr() - .write(|w| w.rxfifo_tout().clear_bit_by_one()); + }; + if reg_en.read().rx_tout_en().bit_is_set() { + events |= RxEvent::FifoTout; + } - if read_bytes > 0 { - return Ok(read_bytes); + let events_happened = UartRxFuture::new(self.uart.reborrow(), events).await; + // always drain the fifo, if an error has occurred the data is lost + let read_bytes = self.drain_fifo(buf); + // check error events + for event_happened in events_happened { + match event_happened { + RxEvent::FifoOvf => return Err(Error::RxFifoOvf), + RxEvent::GlitchDetected => return Err(Error::RxGlitchDetected), + RxEvent::FrameError => return Err(Error::RxFrameError), + RxEvent::ParityError => return Err(Error::RxParityError), + RxEvent::FifoFull | RxEvent::CmdCharDetected | RxEvent::FifoTout => continue, } } - } - } + // Unfortunately, the uart's rx-timeout counter counts up whenever there is + // data in the fifo, even if the interrupt is disabled and the status bit + // cleared. Since we do not drain the fifo in the interrupt handler, we need to + // reset the counter here, after draining the fifo. + self.register_block() + .int_clr() + .write(|w| w.rxfifo_tout().clear_bit_by_one()); - impl embedded_io_async::Read for Uart<'_, Async, T> - where - T: Instance, - { - /// In contrast to the documentation of embedded_io_async::Read, this - /// method blocks until an uart interrupt occurs. - /// See UartRx::read_async for more details. - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_async(buf).await + if read_bytes > 0 { + return Ok(read_bytes); + } } } +} - impl embedded_io_async::Read for UartRx<'_, Async, T> - where - T: Instance, - { - /// In contrast to the documentation of embedded_io_async::Read, this - /// method blocks until an uart interrupt occurs. - /// See UartRx::read_async for more details. - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_async(buf).await - } +impl embedded_io_async::Read for Uart<'_, Async, T> +where + T: Instance, +{ + /// In contrast to the documentation of embedded_io_async::Read, this + /// method blocks until an uart interrupt occurs. + /// See UartRx::read_async for more details. + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_async(buf).await } +} - impl embedded_io_async::Write for Uart<'_, Async, T> - where - T: Instance, - { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write_async(buf).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_async().await - } +impl embedded_io_async::Read for UartRx<'_, Async, T> +where + T: Instance, +{ + /// In contrast to the documentation of embedded_io_async::Read, this + /// method blocks until an uart interrupt occurs. + /// See UartRx::read_async for more details. + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_async(buf).await } +} - impl embedded_io_async::Write for UartTx<'_, Async, T> - where - T: Instance, - { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write_async(buf).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_async().await - } +impl embedded_io_async::Write for Uart<'_, Async, T> +where + T: Instance, +{ + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_async(buf).await } - /// Interrupt handler for all UART instances - /// Clears and disables interrupts that have occurred and have their enable - /// bit set. The fact that an interrupt has been disabled is used by the - /// futures to detect that they should indeed resolve after being woken up - fn intr_handler(uart: &RegisterBlock) -> (bool, bool) { - let interrupts = uart.int_st().read(); - let interrupt_bits = interrupts.bits(); // = int_raw & int_ena - if interrupt_bits == 0 { - return (false, false); - } - let rx_wake = interrupts.rxfifo_full().bit_is_set() - || interrupts.rxfifo_ovf().bit_is_set() - || interrupts.rxfifo_tout().bit_is_set() - || interrupts.at_cmd_char_det().bit_is_set() - || interrupts.glitch_det().bit_is_set() - || interrupts.frm_err().bit_is_set() - || interrupts.parity_err().bit_is_set(); - let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); - uart.int_clr().write(|w| unsafe { w.bits(interrupt_bits) }); - uart.int_ena() - .modify(|r, w| unsafe { w.bits(r.bits() & !interrupt_bits) }); - - (rx_wake, tx_wake) + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_async().await } +} - #[cfg(uart0)] - #[handler] - pub(super) fn uart0() { - let uart = unsafe { &*crate::peripherals::UART0::ptr() }; - let (rx, tx) = intr_handler(uart); - if rx { - RX_WAKERS[0].wake(); - } - if tx { - TX_WAKERS[0].wake(); - } +impl embedded_io_async::Write for UartTx<'_, Async, T> +where + T: Instance, +{ + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_async(buf).await } - #[cfg(uart1)] - #[handler] - pub(super) fn uart1() { - let uart = unsafe { &*crate::peripherals::UART1::ptr() }; - let (rx, tx) = intr_handler(uart); - if rx { - RX_WAKERS[1].wake(); - } - if tx { - TX_WAKERS[1].wake(); - } + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_async().await } +} - #[cfg(uart2)] - #[handler] - pub(super) fn uart2() { - let uart = unsafe { &*crate::peripherals::UART2::ptr() }; - let (rx, tx) = intr_handler(uart); - if rx { - RX_WAKERS[2].wake(); - } - if tx { - TX_WAKERS[2].wake(); - } +/// Interrupt handler for all UART instances +/// Clears and disables interrupts that have occurred and have their enable +/// bit set. The fact that an interrupt has been disabled is used by the +/// futures to detect that they should indeed resolve after being woken up +pub(super) fn intr_handler(uart: &Info, state: &State) { + let interrupts = uart.register_block().int_st().read(); + let interrupt_bits = interrupts.bits(); // = int_raw & int_ena + let rx_wake = interrupts.rxfifo_full().bit_is_set() + || interrupts.rxfifo_ovf().bit_is_set() + || interrupts.rxfifo_tout().bit_is_set() + || interrupts.at_cmd_char_det().bit_is_set() + || interrupts.glitch_det().bit_is_set() + || interrupts.frm_err().bit_is_set() + || interrupts.parity_err().bit_is_set(); + let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); + uart.register_block() + .int_clr() + .write(|w| unsafe { w.bits(interrupt_bits) }); + uart.register_block() + .int_ena() + .modify(|r, w| unsafe { w.bits(r.bits() & !interrupt_bits) }); + + if tx_wake { + state.tx_waker.wake(); + } + if rx_wake { + state.rx_waker.wake(); } } @@ -2463,7 +1953,7 @@ pub mod lp_uart { use crate::{ gpio::lp_io::{LowPowerInput, LowPowerOutput}, peripherals::{LP_CLKRST, LP_UART}, - uart::config::{self, Config}, + uart::{Config, DataBits, Parity, StopBits}, }; /// LP-UART driver /// @@ -2476,8 +1966,8 @@ pub mod lp_uart { /// Initialize the UART driver using the default configuration // TODO: CTS and RTS pins pub fn new(uart: LP_UART, _tx: LowPowerOutput<'_, 5>, _rx: LowPowerInput<'_, 4>) -> Self { - let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; - let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; + let lp_io = unsafe { crate::peripherals::LP_IO::steal() }; + let lp_aon = unsafe { crate::peripherals::LP_AON::steal() }; // FIXME: use GPIO APIs to configure pins lp_aon @@ -2521,7 +2011,7 @@ pub mod lp_uart { // default == SOC_MOD_CLK_RTC_FAST == 2 // LP_CLKRST.lpperi.lp_uart_clk_sel = 0; - unsafe { &*LP_CLKRST::PTR } + unsafe { LP_CLKRST::steal() } .lpperi() .modify(|_, w| w.lp_uart_clk_sel().clear_bit()); @@ -2605,23 +2095,23 @@ pub mod lp_uart { self.rxfifo_reset(); } - fn change_parity(&mut self, parity: config::Parity) -> &mut Self { - if parity != config::Parity::ParityNone { + fn change_parity(&mut self, parity: Parity) -> &mut Self { + if parity != Parity::ParityNone { self.uart .conf0() .modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0)); } self.uart.conf0().modify(|_, w| match parity { - config::Parity::ParityNone => w.parity_en().clear_bit(), - config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + Parity::ParityNone => w.parity_en().clear_bit(), + Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), }); self } - fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { + fn change_data_bits(&mut self, data_bits: DataBits) -> &mut Self { self.uart .conf0() .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); @@ -2631,7 +2121,7 @@ pub mod lp_uart { self } - fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { + fn change_stop_bits(&mut self, stop_bits: StopBits) -> &mut Self { self.uart .conf0() .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); @@ -2651,6 +2141,476 @@ pub mod lp_uart { } } +/// UART Peripheral Instance +pub trait Instance: Peripheral

+ Into + 'static { + /// Returns the peripheral data and state describing this UART instance. + fn parts(&self) -> (&'static Info, &'static State); + + /// Returns the peripheral data describing this UART instance. + #[inline(always)] + fn info(&self) -> &'static Info { + self.parts().0 + } + + /// Returns the peripheral state for this UART instance. + #[inline(always)] + fn state(&self) -> &'static State { + self.parts().1 + } +} + +/// Peripheral data describing a particular UART instance. +#[non_exhaustive] +pub struct Info { + /// Pointer to the register block for this UART instance. + /// + /// Use [Self::register_block] to access the register block. + pub register_block: *const RegisterBlock, + + /// The system peripheral marker. + pub peripheral: crate::system::Peripheral, + + /// Interrupt handler for the asynchronous operations of this UART instance. + pub async_handler: InterruptHandler, + + /// Interrupt for this UART instance. + pub interrupt: Interrupt, + + /// TX pin + pub tx_signal: OutputSignal, + + /// RX pin + pub rx_signal: InputSignal, + + /// CTS (Clear to Send) pin + pub cts_signal: InputSignal, + + /// RTS (Request to Send) pin + pub rts_signal: OutputSignal, +} + +/// Peripheral state for a UART instance. +#[non_exhaustive] +pub struct State { + /// Waker for the asynchronous RX operations. + pub rx_waker: AtomicWaker, + + /// Waker for the asynchronous TX operations. + pub tx_waker: AtomicWaker, + + /// Stores whether the TX half is configured for async operation. + pub is_rx_async: AtomicBool, + + /// Stores whether the RX half is configured for async operation. + pub is_tx_async: AtomicBool, +} + +impl Info { + /// Returns the register block for this UART instance. + pub fn register_block(&self) -> &RegisterBlock { + unsafe { &*self.register_block } + } + + /// Listen for the given interrupts + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.register_block(); + + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + UartInterrupt::AtCmd => w.at_cmd_char_det().bit(enable), + UartInterrupt::TxDone => w.tx_done().bit(enable), + UartInterrupt::RxFifoFull => w.rxfifo_full().bit(enable), + }; + } + w + }); + } + + fn interrupts(&self) -> EnumSet { + let mut res = EnumSet::new(); + let reg_block = self.register_block(); + + let ints = reg_block.int_raw().read(); + + if ints.at_cmd_char_det().bit_is_set() { + res.insert(UartInterrupt::AtCmd); + } + if ints.tx_done().bit_is_set() { + res.insert(UartInterrupt::TxDone); + } + if ints.rxfifo_full().bit_is_set() { + res.insert(UartInterrupt::RxFifoFull); + } + + res + } + + fn clear_interrupts(&self, interrupts: EnumSet) { + let reg_block = self.register_block(); + + reg_block.int_clr().write(|w| { + for interrupt in interrupts { + match interrupt { + UartInterrupt::AtCmd => w.at_cmd_char_det().clear_bit_by_one(), + UartInterrupt::TxDone => w.tx_done().clear_bit_by_one(), + UartInterrupt::RxFifoFull => w.rxfifo_full().clear_bit_by_one(), + }; + } + w + }); + } + + fn set_interrupt_handler(&self, handler: InterruptHandler) { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, self.interrupt); + } + self.enable_listen(EnumSet::all(), false); + self.clear_interrupts(EnumSet::all()); + unsafe { crate::interrupt::bind_interrupt(self.interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(self.interrupt, handler.priority())); + } + + fn disable_interrupts(&self) { + crate::interrupt::disable(crate::Cpu::current(), self.interrupt); + } + + fn apply_config(&self, config: &Config) -> Result<(), Error> { + self.set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; + self.set_rx_timeout(config.rx_timeout, config.symbol_length())?; + self.change_baud(config.baudrate, config.clock_source); + self.change_data_bits(config.data_bits); + self.change_parity(config.parity); + self.change_stop_bits(config.stop_bits); + + // Reset Tx/Rx FIFOs + self.rxfifo_reset(); + self.txfifo_reset(); + + Ok(()) + } + + /// Configures the RX-FIFO threshold + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value + /// for SOC : + /// - `esp32` **0x7F** + /// - `esp32c6`, `esp32h2` **0xFF** + /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** + /// - `esp32s3` **0x3FF** + fn set_rx_fifo_full_threshold(&self, threshold: u16) -> Result<(), Error> { + #[cfg(esp32)] + const MAX_THRHD: u16 = 0x7F; + #[cfg(any(esp32c6, esp32h2))] + const MAX_THRHD: u16 = 0xFF; + #[cfg(any(esp32c3, esp32c2, esp32s2))] + const MAX_THRHD: u16 = 0x1FF; + #[cfg(esp32s3)] + const MAX_THRHD: u16 = 0x3FF; + + if threshold > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + self.register_block() + .conf1() + .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); + + Ok(()) + } + + /// Configures the Receive Timeout detection setting + /// + /// # Arguments + /// `timeout` - the number of symbols ("bytes") to wait for before + /// triggering a timeout. Pass None to disable the timeout. + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum + /// value for SOC : + /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. + /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The + /// value you pass times the symbol size must be <= **0x3FF** + fn set_rx_timeout(&self, timeout: Option, _symbol_len: u8) -> Result<(), Error> { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + const MAX_THRHD: u8 = 0x7F; // 7 bits + } else { + const MAX_THRHD: u16 = 0x3FF; // 10 bits + } + } + + let register_block = self.register_block(); + + if let Some(timeout) = timeout { + // the esp32 counts directly in number of symbols (symbol len fixed to 8) + #[cfg(esp32)] + let timeout_reg = timeout; + // all other count in bits, so we need to multiply by the symbol len. + #[cfg(not(esp32))] + let timeout_reg = timeout as u16 * _symbol_len as u16; + + if timeout_reg > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let reg_thrhd = register_block.conf1(); + } else if #[cfg(any(esp32c6, esp32h2))] { + let reg_thrhd = register_block.tout_conf(); + } else { + let reg_thrhd = register_block.mem_conf(); + } + } + reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) }); + } + + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let reg_en = register_block.tout_conf(); + } else { + let reg_en = register_block.conf1(); + } + } + reg_en.modify(|_, w| w.rx_tout_en().bit(timeout.is_some())); + + self.sync_regs(); + + Ok(()) + } + + #[cfg(any(esp32c2, esp32c3, esp32s3))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), + }; + + if clock_source == ClockSource::RcFast { + unsafe { crate::peripherals::RTC_CNTL::steal() } + .clk_conf() + .modify(|_, w| w.dig_clk8m_en().variant(true)); + // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); + crate::rom::ets_delay_us(5); + } + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = clk.div_ceil(max_div * baudrate); + self.register_block().clk_conf().write(|w| unsafe { + w.sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.sclk_div_a().bits(0); + w.sclk_div_b().bits(0); + w.sclk_div_num().bits(clk_div as u8 - 1); + w.rx_sclk_en().bit(true); + w.tx_sclk_en().bit(true) + }); + + let divider = (clk << 4) / (baudrate * clk_div); + let divider_integer = (divider >> 4) as u16; + let divider_frag = (divider & 0xf) as u8; + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); + } + + fn is_instance(&self, other: impl Instance) -> bool { + self == other.info() + } + + fn sync_regs(&self) { + sync_regs(self.register_block()); + } + + #[cfg(any(esp32c6, esp32h2))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), + }; + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = clk.div_ceil(max_div * baudrate); + + // UART clocks are configured via PCR + let pcr = unsafe { crate::peripherals::PCR::steal() }; + + if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { + pcr.uart0_conf() + .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); + + pcr.uart0_sclk_conf().modify(|_, w| unsafe { + w.uart0_sclk_div_a().bits(0); + w.uart0_sclk_div_b().bits(0); + w.uart0_sclk_div_num().bits(clk_div as u8 - 1); + w.uart0_sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.uart0_sclk_en().set_bit() + }); + } else { + pcr.uart1_conf() + .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); + + pcr.uart1_sclk_conf().modify(|_, w| unsafe { + w.uart1_sclk_div_a().bits(0); + w.uart1_sclk_div_b().bits(0); + w.uart1_sclk_div_num().bits(clk_div as u8 - 1); + w.uart1_sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.uart1_sclk_en().set_bit() + }); + } + + let clk = clk / clk_div; + let divider = clk / baudrate; + let divider = divider as u16; + + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + + self.sync_regs(); + } + + #[cfg(any(esp32, esp32s2))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clk = match clock_source { + ClockSource::Apb => Clocks::get().apb_clock.to_Hz(), + // ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2) + ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(), + }; + + self.register_block() + .conf0() + .modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb)); + + let divider = clk / baudrate; + + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + } + + fn change_data_bits(&self, data_bits: DataBits) { + self.register_block() + .conf0() + .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); + } + + fn change_parity(&self, parity: Parity) { + self.register_block().conf0().modify(|_, w| match parity { + Parity::ParityNone => w.parity_en().clear_bit(), + Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + }); + } + + fn change_stop_bits(&self, stop_bits: StopBits) { + #[cfg(esp32)] + { + // workaround for hardware issue, when UART stop bit set as 2-bit mode. + if stop_bits == StopBits::STOP2 { + self.register_block() + .rs485_conf() + .modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::STOP2)); + + self.register_block().conf0().modify(|_, w| { + if stop_bits == StopBits::STOP2 { + unsafe { w.stop_bit_num().bits(1) } + } else { + unsafe { w.stop_bit_num().bits(stop_bits as u8) } + } + }); + } + } + + #[cfg(not(esp32))] + self.register_block() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + } + + fn rxfifo_reset(&self) { + fn rxfifo_rst(reg_block: &RegisterBlock, enable: bool) { + reg_block.conf0().modify(|_, w| w.rxfifo_rst().bit(enable)); + sync_regs(reg_block); + } + + rxfifo_rst(self.register_block(), true); + rxfifo_rst(self.register_block(), false); + } + + fn txfifo_reset(&self) { + fn txfifo_rst(reg_block: &RegisterBlock, enable: bool) { + reg_block.conf0().modify(|_, w| w.txfifo_rst().bit(enable)); + sync_regs(reg_block); + } + + txfifo_rst(self.register_block(), true); + txfifo_rst(self.register_block(), false); + } +} + +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.register_block == other.register_block + } +} + +unsafe impl Sync for Info {} + +macro_rules! impl_instance { + ($inst:ident, $peri:ident, $txd:ident, $rxd:ident, $cts:ident, $rts:ident) => { + impl Instance for crate::peripherals::$inst { + fn parts(&self) -> (&'static Info, &'static State) { + #[crate::macros::handler] + pub(super) fn irq_handler() { + intr_handler(&PERIPHERAL, &STATE); + } + + static STATE: State = State { + tx_waker: AtomicWaker::new(), + rx_waker: AtomicWaker::new(), + is_rx_async: AtomicBool::new(false), + is_tx_async: AtomicBool::new(false), + }; + + static PERIPHERAL: Info = Info { + register_block: crate::peripherals::$inst::ptr(), + peripheral: crate::system::Peripheral::$peri, + async_handler: irq_handler, + interrupt: Interrupt::$inst, + tx_signal: OutputSignal::$txd, + rx_signal: InputSignal::$rxd, + cts_signal: InputSignal::$cts, + rts_signal: OutputSignal::$rts, + }; + (&PERIPHERAL, &STATE) + } + } + }; +} + +impl_instance!(UART0, Uart0, U0TXD, U0RXD, U0CTS, U0RTS); +impl_instance!(UART1, Uart1, U1TXD, U1RXD, U1CTS, U1RTS); +#[cfg(uart2)] +impl_instance!(UART2, Uart2, U2TXD, U2RXD, U2CTS, U2RTS); + crate::any_peripheral! { /// Any UART peripheral. pub peripheral AnyUart { @@ -2664,23 +2624,15 @@ crate::any_peripheral! { } impl Instance for AnyUart { - delegate::delegate! { - to match &self.0 { + #[inline] + fn parts(&self) -> (&'static Info, &'static State) { + match &self.0 { #[cfg(uart0)] - AnyUartInner::Uart0(uart) => uart, + AnyUartInner::Uart0(uart) => uart.parts(), #[cfg(uart1)] - AnyUartInner::Uart1(uart) => uart, + AnyUartInner::Uart1(uart) => uart.parts(), #[cfg(uart2)] - AnyUartInner::Uart2(uart) => uart, - } { - fn register_block(&self) -> &RegisterBlock; - fn uart_number(&self) -> usize; - fn async_handler(&self) -> InterruptHandler; - fn interrupt(&self) -> Interrupt; - fn tx_signal(&self) -> OutputSignal; - fn rx_signal(&self) -> InputSignal; - fn cts_signal(&self) -> InputSignal; - fn rts_signal(&self) -> OutputSignal; + AnyUartInner::Uart2(uart) => uart.parts(), } } } diff --git a/esp-hal/src/usb_serial_jtag.rs b/esp-hal/src/usb_serial_jtag.rs index ea604f8767d..c94c2be6c93 100644 --- a/esp-hal/src/usb_serial_jtag.rs +++ b/esp-hal/src/usb_serial_jtag.rs @@ -75,14 +75,18 @@ //! [embedded-hal-async]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/ //! [embedded-io-async]: https://docs.rs/embedded-io-async/latest/embedded_io_async/ -use core::{convert::Infallible, marker::PhantomData}; +use core::{convert::Infallible, marker::PhantomData, task::Poll}; + +use embassy_sync::waitqueue::AtomicWaker; +use procmacros::handler; use crate::{ - interrupt::InterruptHandler, - peripheral::Peripheral, + peripheral::{Peripheral, PeripheralRef}, peripherals::{usb_device::RegisterBlock, Interrupt, USB_DEVICE}, system::PeripheralClockControl, + Async, Blocking, + Cpu, InterruptConfigurable, Mode, }; @@ -98,20 +102,24 @@ pub struct UsbSerialJtag<'d, M> { /// USB Serial/JTAG (Transmit) pub struct UsbSerialJtagTx<'d, M> { - phantom: PhantomData<(&'d mut USB_DEVICE, M)>, + peripheral: PeripheralRef<'d, USB_DEVICE>, + phantom: PhantomData, } /// USB Serial/JTAG (Receive) pub struct UsbSerialJtagRx<'d, M> { - phantom: PhantomData<(&'d mut USB_DEVICE, M)>, + peripheral: PeripheralRef<'d, USB_DEVICE>, + phantom: PhantomData, } impl<'d, M> UsbSerialJtagTx<'d, M> where M: Mode, { - fn new_inner() -> Self { + fn new_inner(peripheral: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(peripheral); Self { + peripheral, phantom: PhantomData, } } @@ -187,8 +195,10 @@ impl<'d, M> UsbSerialJtagRx<'d, M> where M: Mode, { - fn new_inner() -> Self { + fn new_inner(peripheral: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(peripheral); Self { + peripheral, phantom: PhantomData, } } @@ -254,7 +264,7 @@ where pub fn reset_rx_packet_recv_interrupt(&mut self) { USB_DEVICE::register_block() .int_clr() - .write(|w| w.serial_out_recv_pkt().clear_bit_by_one()) + .write(|w| w.serial_out_recv_pkt().clear_bit_by_one()); } } @@ -263,13 +273,37 @@ impl<'d> UsbSerialJtag<'d, Blocking> { pub fn new(usb_device: impl Peripheral

+ 'd) -> Self { Self::new_inner(usb_device) } + + /// Reconfigure the USB Serial JTAG peripheral to operate in asynchronous + /// mode. + pub fn into_async(mut self) -> UsbSerialJtag<'d, Async> { + self.set_interrupt_handler(async_interrupt_handler); + + UsbSerialJtag { + rx: UsbSerialJtagRx { + peripheral: self.rx.peripheral, + phantom: PhantomData, + }, + tx: UsbSerialJtagTx { + peripheral: self.tx.peripheral, + phantom: PhantomData, + }, + } + } } -impl<'d> crate::private::Sealed for UsbSerialJtag<'d, Blocking> {} +impl crate::private::Sealed for UsbSerialJtag<'_, Blocking> {} -impl<'d> InterruptConfigurable for UsbSerialJtag<'d, Blocking> { +impl InterruptConfigurable for UsbSerialJtag<'_, Blocking> { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { - self.inner_set_interrupt_handler(handler); + for core in crate::Cpu::other() { + crate::interrupt::disable(core, Interrupt::USB_DEVICE); + } + unsafe { crate::interrupt::bind_interrupt(Interrupt::USB_DEVICE, handler.handler()) }; + unwrap!(crate::interrupt::enable( + Interrupt::USB_DEVICE, + handler.priority() + )); } } @@ -277,7 +311,7 @@ impl<'d, M> UsbSerialJtag<'d, M> where M: Mode, { - fn new_inner(_usb_device: impl Peripheral

+ 'd) -> Self { + fn new_inner(usb_device: impl Peripheral

+ 'd) -> Self { // Do NOT reset the peripheral. Doing so will result in a broken USB JTAG // connection. PeripheralClockControl::enable(crate::system::Peripheral::UsbDevice); @@ -293,29 +327,20 @@ where // doesn't swap the pullups too, this works around that. if Efuse::read_bit(USB_EXCHG_PINS) { USB_DEVICE::register_block().conf0().modify(|_, w| { - w.pad_pull_override() - .set_bit() - .dm_pullup() - .clear_bit() - .dp_pullup() - .set_bit() + w.pad_pull_override().set_bit(); + w.dm_pullup().clear_bit(); + w.dp_pullup().set_bit() }); } } - Self { - rx: UsbSerialJtagRx::new_inner(), - tx: UsbSerialJtagTx::new_inner(), - } - } + crate::into_ref!(usb_device); - fn inner_set_interrupt_handler(&mut self, handler: InterruptHandler) { - unsafe { - crate::interrupt::bind_interrupt(Interrupt::USB_DEVICE, handler.handler()); - crate::interrupt::enable(Interrupt::USB_DEVICE, handler.priority()).unwrap(); + Self { + rx: UsbSerialJtagRx::new_inner(unsafe { usb_device.clone_unchecked() }), + tx: UsbSerialJtagTx::new_inner(usb_device), } } - /// Split the USB Serial JTAG peripheral into a transmitter and receiver, /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. @@ -383,7 +408,7 @@ pub trait Instance: crate::private::Sealed { Self::register_block() .int_clr() - .write(|w| w.serial_in_empty().clear_bit_by_one()) + .write(|w| w.serial_in_empty().clear_bit_by_one()); } /// Disable all receive interrupts for the peripheral @@ -394,7 +419,7 @@ pub trait Instance: crate::private::Sealed { Self::register_block() .int_clr() - .write(|w| w.serial_out_recv_pkt().clear_bit_by_one()) + .write(|w| w.serial_out_recv_pkt().clear_bit_by_one()); } } @@ -652,224 +677,221 @@ where } } -mod asynch { - use core::{marker::PhantomData, task::Poll}; - - use embassy_sync::waitqueue::AtomicWaker; - use procmacros::handler; +// Static instance of the waker for each component of the peripheral: +static WAKER_TX: AtomicWaker = AtomicWaker::new(); +static WAKER_RX: AtomicWaker = AtomicWaker::new(); - use super::{Error, Instance, UsbSerialJtag, UsbSerialJtagRx, UsbSerialJtagTx}; - use crate::{peripheral::Peripheral, peripherals::USB_DEVICE, Async}; +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct UsbSerialJtagWriteFuture<'d> { + _peripheral: PeripheralRef<'d, USB_DEVICE>, +} - // Static instance of the waker for each component of the peripheral: - static WAKER_TX: AtomicWaker = AtomicWaker::new(); - static WAKER_RX: AtomicWaker = AtomicWaker::new(); +impl<'d> UsbSerialJtagWriteFuture<'d> { + fn new(_peripheral: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(_peripheral); + // Set the interrupt enable bit for the USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT + // interrupt + USB_DEVICE::register_block() + .int_ena() + .modify(|_, w| w.serial_in_empty().set_bit()); - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct UsbSerialJtagWriteFuture<'d> { - phantom: PhantomData<&'d mut USB_DEVICE>, + Self { _peripheral } } - impl<'d> UsbSerialJtagWriteFuture<'d> { - pub fn new() -> Self { - // Set the interrupt enable bit for the USB_SERIAL_JTAG_SERIAL_IN_EMPTY_INT - // interrupt - USB_DEVICE::register_block() - .int_ena() - .modify(|_, w| w.serial_in_empty().set_bit()); - - Self { - phantom: PhantomData, - } - } - - fn event_bit_is_clear(&self) -> bool { - USB_DEVICE::register_block() - .int_ena() - .read() - .serial_in_empty() - .bit_is_clear() - } + fn event_bit_is_clear(&self) -> bool { + USB_DEVICE::register_block() + .int_ena() + .read() + .serial_in_empty() + .bit_is_clear() } +} - impl<'d> core::future::Future for UsbSerialJtagWriteFuture<'d> { - type Output = (); +impl core::future::Future for UsbSerialJtagWriteFuture<'_> { + type Output = (); - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - WAKER_TX.register(cx.waker()); - if self.event_bit_is_clear() { - Poll::Ready(()) - } else { - Poll::Pending - } + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + WAKER_TX.register(cx.waker()); + if self.event_bit_is_clear() { + Poll::Ready(()) + } else { + Poll::Pending } } +} - #[must_use = "futures do nothing unless you `.await` or poll them"] - pub(crate) struct UsbSerialJtagReadFuture<'d> { - phantom: PhantomData<&'d mut USB_DEVICE>, - } +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct UsbSerialJtagReadFuture<'d> { + _peripheral: PeripheralRef<'d, USB_DEVICE>, +} - impl<'d> UsbSerialJtagReadFuture<'d> { - pub fn new() -> Self { - // Set the interrupt enable bit for the USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT - // interrupt - USB_DEVICE::register_block() - .int_ena() - .modify(|_, w| w.serial_out_recv_pkt().set_bit()); +impl<'d> UsbSerialJtagReadFuture<'d> { + fn new(_peripheral: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(_peripheral); + // Set the interrupt enable bit for the USB_SERIAL_JTAG_SERIAL_OUT_RECV_PKT + // interrupt + USB_DEVICE::register_block() + .int_ena() + .modify(|_, w| w.serial_out_recv_pkt().set_bit()); - Self { - phantom: PhantomData, - } - } + Self { _peripheral } + } - fn event_bit_is_clear(&self) -> bool { - USB_DEVICE::register_block() - .int_ena() - .read() - .serial_out_recv_pkt() - .bit_is_clear() - } + fn event_bit_is_clear(&self) -> bool { + USB_DEVICE::register_block() + .int_ena() + .read() + .serial_out_recv_pkt() + .bit_is_clear() } +} - impl<'d> core::future::Future for UsbSerialJtagReadFuture<'d> { - type Output = (); +impl core::future::Future for UsbSerialJtagReadFuture<'_> { + type Output = (); - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_>, - ) -> core::task::Poll { - WAKER_RX.register(cx.waker()); - if self.event_bit_is_clear() { - Poll::Ready(()) - } else { - Poll::Pending - } + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + WAKER_RX.register(cx.waker()); + if self.event_bit_is_clear() { + Poll::Ready(()) + } else { + Poll::Pending } } +} - impl<'d> UsbSerialJtag<'d, Async> { - /// Create a new USB serial/JTAG instance in asynchronous mode - pub fn new_async(usb_device: impl Peripheral

+ 'd) -> Self { - let mut this = Self::new_inner(usb_device); - this.inner_set_interrupt_handler(async_interrupt_handler); - this +impl<'d> UsbSerialJtag<'d, Async> { + /// Reconfigure the USB Serial JTAG peripheral to operate in blocking + /// mode. + pub fn into_blocking(self) -> UsbSerialJtag<'d, Blocking> { + crate::interrupt::disable(Cpu::current(), Interrupt::USB_DEVICE); + UsbSerialJtag { + rx: UsbSerialJtagRx { + peripheral: self.rx.peripheral, + phantom: PhantomData, + }, + tx: UsbSerialJtagTx { + peripheral: self.tx.peripheral, + phantom: PhantomData, + }, } } +} - impl UsbSerialJtagTx<'_, Async> { - async fn write_bytes_async(&mut self, words: &[u8]) -> Result<(), Error> { - let reg_block = USB_DEVICE::register_block(); - - for chunk in words.chunks(64) { - for byte in chunk { - reg_block - .ep1() - .write(|w| unsafe { w.rdwr_byte().bits(*byte) }); - } - reg_block.ep1_conf().modify(|_, w| w.wr_done().set_bit()); +impl UsbSerialJtagTx<'_, Async> { + async fn write_bytes_async(&mut self, words: &[u8]) -> Result<(), Error> { + let reg_block = USB_DEVICE::register_block(); - UsbSerialJtagWriteFuture::new().await; + for chunk in words.chunks(64) { + for byte in chunk { + reg_block + .ep1() + .write(|w| unsafe { w.rdwr_byte().bits(*byte) }); } + reg_block.ep1_conf().modify(|_, w| w.wr_done().set_bit()); - Ok(()) + UsbSerialJtagWriteFuture::new(self.peripheral.reborrow()).await; } - async fn flush_tx_async(&mut self) -> Result<(), Error> { - if USB_DEVICE::register_block() - .jfifo_st() - .read() - .out_fifo_empty() - .bit_is_clear() - { - UsbSerialJtagWriteFuture::new().await; - } + Ok(()) + } - Ok(()) + async fn flush_tx_async(&mut self) -> Result<(), Error> { + if USB_DEVICE::register_block() + .jfifo_st() + .read() + .out_fifo_empty() + .bit_is_clear() + { + UsbSerialJtagWriteFuture::new(self.peripheral.reborrow()).await; } + + Ok(()) } +} - impl UsbSerialJtagRx<'_, Async> { - async fn read_bytes_async(&mut self, buf: &mut [u8]) -> Result { - if buf.is_empty() { - return Ok(0); - } +impl UsbSerialJtagRx<'_, Async> { + async fn read_bytes_async(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } - loop { - let read_bytes = self.drain_rx_fifo(buf); - if read_bytes > 0 { - return Ok(read_bytes); - } - UsbSerialJtagReadFuture::new().await; + loop { + let read_bytes = self.drain_rx_fifo(buf); + if read_bytes > 0 { + return Ok(read_bytes); } + UsbSerialJtagReadFuture::new(self.peripheral.reborrow()).await; } } +} - impl embedded_io_async::Write for UsbSerialJtag<'_, Async> { - async fn write(&mut self, buf: &[u8]) -> Result { - embedded_io_async::Write::write(&mut self.tx, buf).await - } +impl embedded_io_async::Write for UsbSerialJtag<'_, Async> { + async fn write(&mut self, buf: &[u8]) -> Result { + embedded_io_async::Write::write(&mut self.tx, buf).await + } - async fn flush(&mut self) -> Result<(), Self::Error> { - embedded_io_async::Write::flush(&mut self.tx).await - } + async fn flush(&mut self) -> Result<(), Self::Error> { + embedded_io_async::Write::flush(&mut self.tx).await } +} - impl embedded_io_async::Write for UsbSerialJtagTx<'_, Async> { - async fn write(&mut self, buf: &[u8]) -> Result { - self.write_bytes_async(buf).await?; +impl embedded_io_async::Write for UsbSerialJtagTx<'_, Async> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write_bytes_async(buf).await?; - Ok(buf.len()) - } + Ok(buf.len()) + } - async fn flush(&mut self) -> Result<(), Self::Error> { - self.flush_tx_async().await - } + async fn flush(&mut self) -> Result<(), Self::Error> { + self.flush_tx_async().await } +} - impl embedded_io_async::Read for UsbSerialJtag<'_, Async> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - embedded_io_async::Read::read(&mut self.rx, buf).await - } +impl embedded_io_async::Read for UsbSerialJtag<'_, Async> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + embedded_io_async::Read::read(&mut self.rx, buf).await } +} - impl embedded_io_async::Read for UsbSerialJtagRx<'_, Async> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read_bytes_async(buf).await - } +impl embedded_io_async::Read for UsbSerialJtagRx<'_, Async> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read_bytes_async(buf).await } +} - #[handler] - fn async_interrupt_handler() { - let usb = USB_DEVICE::register_block(); - let interrupts = usb.int_st().read(); +#[handler] +fn async_interrupt_handler() { + let usb = USB_DEVICE::register_block(); + let interrupts = usb.int_st().read(); - let tx = interrupts.serial_in_empty().bit_is_set(); - let rx = interrupts.serial_out_recv_pkt().bit_is_set(); + let tx = interrupts.serial_in_empty().bit_is_set(); + let rx = interrupts.serial_out_recv_pkt().bit_is_set(); - if tx { - usb.int_ena().modify(|_, w| w.serial_in_empty().clear_bit()); - } - if rx { - usb.int_ena() - .modify(|_, w| w.serial_out_recv_pkt().clear_bit()); - } + if tx { + usb.int_ena().modify(|_, w| w.serial_in_empty().clear_bit()); + } + if rx { + usb.int_ena() + .modify(|_, w| w.serial_out_recv_pkt().clear_bit()); + } - usb.int_clr().write(|w| { - w.serial_in_empty() - .clear_bit_by_one() - .serial_out_recv_pkt() - .clear_bit_by_one() - }); + usb.int_clr().write(|w| { + w.serial_in_empty() + .clear_bit_by_one() + .serial_out_recv_pkt() + .clear_bit_by_one() + }); - if rx { - WAKER_RX.wake(); - } - if tx { - WAKER_TX.wake(); - } + if rx { + WAKER_RX.wake(); + } + if tx { + WAKER_TX.wake(); } } diff --git a/esp-ieee802154/CHANGELOG.md b/esp-ieee802154/CHANGELOG.md index c1b96f7a120..b44d9109499 100644 --- a/esp-ieee802154/CHANGELOG.md +++ b/esp-ieee802154/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Removed `get_` prefixes from functions (#2528) + ### Fixed ### Removed diff --git a/esp-ieee802154/Cargo.toml b/esp-ieee802154/Cargo.toml index 67367b17521..079a1954133 100644 --- a/esp-ieee802154/Cargo.toml +++ b/esp-ieee802154/Cargo.toml @@ -20,7 +20,7 @@ byte = "0.2.7" critical-section = "1.1.3" document-features = "0.2.10" esp-hal = { version = "0.21.0", path = "../esp-hal" } -esp-wifi-sys = { version = "0.6.0" } +esp-wifi-sys = { version = "0.6.0", git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "a3e2ed64e9095f120bbcebe6287ddf62760774db" } heapless = "0.8.0" ieee802154 = "0.6.1" cfg-if = "1.0.0" diff --git a/esp-ieee802154/build.rs b/esp-ieee802154/build.rs index f20eeeab3aa..ee788fa6433 100644 --- a/esp-ieee802154/build.rs +++ b/esp-ieee802154/build.rs @@ -1,6 +1,6 @@ use std::{env, path::PathBuf}; -use esp_config::{generate_config, Value}; +use esp_config::{generate_config, Validator, Value}; fn main() { let out = PathBuf::from(env::var_os("OUT_DIR").unwrap()); @@ -11,8 +11,9 @@ fn main() { "esp_ieee802154", &[( "rx_queue_size", - Value::UnsignedInteger(50), "Size of the RX queue in frames", + Value::Integer(50), + Some(Validator::PositiveInteger), )], true, ); diff --git a/esp-ieee802154/src/hal.rs b/esp-ieee802154/src/hal.rs index c43cb379adf..e3387ced8ca 100644 --- a/esp-ieee802154/src/hal.rs +++ b/esp-ieee802154/src/hal.rs @@ -244,7 +244,7 @@ pub(crate) fn set_tx_addr(addr: *const u8) { pub(crate) fn set_cmd(cmd: Command) { unsafe { &*IEEE802154::PTR } .command() - .modify(|_, w| unsafe { w.opcode().bits(cmd as u8) }) + .modify(|_, w| unsafe { w.opcode().bits(cmd as u8) }); } #[inline(always)] @@ -255,7 +255,7 @@ pub(crate) fn set_freq(freq: u8) { } #[inline(always)] -pub(crate) fn get_freq() -> u8 { +pub(crate) fn freq() -> u8 { unsafe { &*IEEE802154::PTR }.channel().read().hop().bits() } @@ -271,7 +271,7 @@ pub(crate) fn set_multipan_enable_mask(mask: u8) { // apparently the REGS are garbage and the struct is right? unsafe { &*IEEE802154::PTR } .ctrl_cfg() - .modify(|r, w| unsafe { w.bits(r.bits() & !(0b1111 << 29) | (mask as u32) << 29) }) + .modify(|r, w| unsafe { w.bits(r.bits() & !(0b1111 << 29) | (mask as u32) << 29) }); } #[inline(always)] @@ -333,7 +333,7 @@ pub(crate) fn set_cca_mode(cca_mode: CcaMode) { pub(crate) fn set_cca_threshold(cca_threshold: i8) { unsafe { &*IEEE802154::PTR } .ed_scan_cfg() - .modify(|_, w| unsafe { w.cca_ed_threshold().bits(cca_threshold as u8) }) + .modify(|_, w| unsafe { w.cca_ed_threshold().bits(cca_threshold as u8) }); } #[inline(always)] @@ -344,7 +344,7 @@ pub(crate) fn set_tx_auto_ack(enable: bool) { } #[inline(always)] -pub(crate) fn get_tx_auto_ack() -> bool { +pub(crate) fn tx_auto_ack() -> bool { unsafe { &*IEEE802154::PTR } .ctrl_cfg() .read() @@ -367,7 +367,7 @@ pub(crate) fn set_tx_enhance_ack(enable: bool) { } #[inline(always)] -pub(crate) fn get_tx_enhance_ack() -> bool { +pub(crate) fn tx_enhance_ack() -> bool { unsafe { &*IEEE802154::PTR } .ctrl_cfg() .read() @@ -397,7 +397,7 @@ pub(crate) fn set_pending_mode(enable: bool) { } #[inline(always)] -pub(crate) fn get_events() -> u16 { +pub(crate) fn events() -> u16 { unsafe { &*IEEE802154::PTR }.event_status().read().bits() as u16 } diff --git a/esp-ieee802154/src/lib.rs b/esp-ieee802154/src/lib.rs index 810056b226f..15d8bf3ad33 100644 --- a/esp-ieee802154/src/lib.rs +++ b/esp-ieee802154/src/lib.rs @@ -160,12 +160,12 @@ impl<'a> Ieee802154<'a> { } /// Return the raw data of a received frame - pub fn get_raw_received(&mut self) -> Option { + pub fn raw_received(&mut self) -> Option { ieee802154_poll() } /// Get a received frame, if available - pub fn get_received(&mut self) -> Option> { + pub fn received(&mut self) -> Option> { if let Some(raw) = ieee802154_poll() { let maybe_decoded = if raw.data[0] as usize > raw.data.len() { // try to decode up to data.len() @@ -305,7 +305,7 @@ impl<'a> Ieee802154<'a> { } } -impl<'a> Drop for Ieee802154<'a> { +impl Drop for Ieee802154<'_> { fn drop(&mut self) { self.clear_tx_done_callback(); self.clear_tx_done_callback_fn(); diff --git a/esp-ieee802154/src/raw.rs b/esp-ieee802154/src/raw.rs index 73fb9b7175e..53f104743c2 100644 --- a/esp-ieee802154/src/raw.rs +++ b/esp-ieee802154/src/raw.rs @@ -252,7 +252,7 @@ fn enable_rx() { } fn stop_current_operation() { - let events = get_events(); + let events = events(); set_cmd(Command::Stop); clear_events(events); } @@ -359,7 +359,7 @@ fn next_operation() { fn ZB_MAC() { trace!("ZB_MAC interrupt"); - let events = get_events(); + let events = events(); clear_events(events); trace!("events = {:032b}", events); @@ -390,7 +390,7 @@ fn ZB_MAC() { if !queue.is_full() { let item = RawReceived { data: RX_BUFFER, - channel: freq_to_channel(get_freq()), + channel: freq_to_channel(freq()), }; queue.enqueue(item).ok(); } else { @@ -440,11 +440,9 @@ fn freq_to_channel(freq: u8) -> u8 { } fn will_auto_send_ack(frame: &[u8]) -> bool { - frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_1 && get_tx_auto_ack() + frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_1 && tx_auto_ack() } fn should_send_enhanced_ack(frame: &[u8]) -> bool { - frame_is_ack_required(frame) - && frame_get_version(frame) <= FRAME_VERSION_2 - && get_tx_enhance_ack() + frame_is_ack_required(frame) && frame_get_version(frame) <= FRAME_VERSION_2 && tx_enhance_ack() } diff --git a/esp-lp-hal/src/uart.rs b/esp-lp-hal/src/uart.rs index 8ffd2ba0f06..dc8664267fb 100644 --- a/esp-lp-hal/src/uart.rs +++ b/esp-lp-hal/src/uart.rs @@ -170,7 +170,7 @@ pub struct LpUart { impl LpUart { /// Read a single byte from the UART in a non-blocking manner. pub fn read_byte(&mut self) -> nb::Result { - if self.get_rx_fifo_count() > 0 { + if self.rx_fifo_count() > 0 { let byte = self.uart.fifo().read().rxfifo_rd_byte().bits(); Ok(byte) } else { @@ -180,7 +180,7 @@ impl LpUart { /// Write a single byte to the UART in a non-blocking manner. pub fn write_byte(&mut self, byte: u8) -> nb::Result<(), Error> { - if self.get_tx_fifo_count() < UART_FIFO_SIZE { + if self.tx_fifo_count() < UART_FIFO_SIZE { self.uart .fifo() .write(|w| unsafe { w.rxfifo_rd_byte().bits(byte) }); @@ -210,11 +210,11 @@ impl LpUart { } } - fn get_rx_fifo_count(&mut self) -> u16 { + fn rx_fifo_count(&mut self) -> u16 { self.uart.status().read().rxfifo_cnt().bits().into() } - fn get_tx_fifo_count(&mut self) -> u16 { + fn tx_fifo_count(&mut self) -> u16 { self.uart.status().read().txfifo_cnt().bits().into() } @@ -288,12 +288,12 @@ impl embedded_io::Read for LpUart { return Ok(0); } - while self.get_rx_fifo_count() == 0 { + while self.rx_fifo_count() == 0 { // Block until we have received at least one byte } let mut count = 0; - while self.get_rx_fifo_count() > 0 && count < buf.len() { + while self.rx_fifo_count() > 0 && count < buf.len() { buf[count] = self.uart.fifo().read().rxfifo_rd_byte().bits(); count += 1; } @@ -305,7 +305,7 @@ impl embedded_io::Read for LpUart { #[cfg(feature = "embedded-io")] impl embedded_io::ReadReady for LpUart { fn read_ready(&mut self) -> Result { - Ok(self.get_rx_fifo_count() > 0) + Ok(self.rx_fifo_count() > 0) } } diff --git a/esp-metadata/CHANGELOG.md b/esp-metadata/CHANGELOG.md new file mode 100644 index 00000000000..ea6930f68c0 --- /dev/null +++ b/esp-metadata/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed + +### Changed + +### Removed + +## [0.4.0] - 2024-10-10 + +## [0.3.0] - 2024-08-29 + +## [0.2.0] - 2024-07-15 + +## [0.1.1] - 2024-06-04 + +## [0.1.0] - 2024-04-17 + +- Initial release + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-metadata?since=2024-10-10 diff --git a/esp-println/Cargo.toml b/esp-println/Cargo.toml index 75f7c09d50a..06a0cba29f6 100644 --- a/esp-println/Cargo.toml +++ b/esp-println/Cargo.toml @@ -52,3 +52,6 @@ defmt-espflash = ["dep:defmt", "defmt?/encoding-rzcobs"] # logging sub-features colors = [] + +[lints.rust] +static_mut_refs = "allow" diff --git a/esp-storage/CHANGELOG.md b/esp-storage/CHANGELOG.md index 54b50d25c1c..e23c880369b 100644 --- a/esp-storage/CHANGELOG.md +++ b/esp-storage/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added trait MultiwriteNorFlash to FlashStorage (#2478) + ### Changed ### Fixed diff --git a/esp-storage/src/nor_flash.rs b/esp-storage/src/nor_flash.rs index 16190bc8eea..6c37234f48f 100644 --- a/esp-storage/src/nor_flash.rs +++ b/esp-storage/src/nor_flash.rs @@ -2,6 +2,7 @@ use core::mem::MaybeUninit; use embedded_storage::nor_flash::{ ErrorType, + MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, @@ -203,6 +204,8 @@ impl NorFlash for FlashStorage { } } +impl MultiwriteNorFlash for FlashStorage {} + #[cfg(test)] mod test { use core::mem::MaybeUninit; diff --git a/esp-wifi/CHANGELOG.md b/esp-wifi/CHANGELOG.md index 5597d41884c..fec981690a2 100644 --- a/esp-wifi/CHANGELOG.md +++ b/esp-wifi/CHANGELOG.md @@ -10,10 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `serde` support through the `serde` feature (#2346) +- Added `PowerSaveMode` and `set_power_saving` methods on `EspNowManager` & `WifiController` (#2446) +- Added CSI support (#2422) +- Enable setting event handlers for wifi events (#2453) ### Changed +- `esp_wifi::init` no longer requires `EspWifiInitFor`, and now returns `EspWifiController`, see the migration guide for more details (#2301) - No need to add `rom_functions.x` manually anymore (#2374) +- esp-now: Data is now private in `ReceivedData` - use `data()`(#2396) +- Changed the async APIs to have a `_async` postfix to avoid name collisions (#2446) +- `phy_enable_usb` is enabled by default (#2446) +- Removed `get_` prefixes from functions (#2528) ### Fixed @@ -22,6 +30,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- Feature `have-strchr` is removed (#2462) +- Features `async`, `embassy-net` have been removed (#2446) +- Features `phy-enable-usb` & `dump-packets` have been turned into configuration options `phy_enable_usb` & `dump_packets` (#2446) +- Features `ps-min-modem` & `ps-max-modem` have been removed in favour of a runtime config (#2446) + +- The blocking networking stack is removed (#2488) + ## 0.10.1 - 2024-10-10 ### Changed diff --git a/esp-wifi/Cargo.toml b/esp-wifi/Cargo.toml index a0193547fcf..2199f704bec 100644 --- a/esp-wifi/Cargo.toml +++ b/esp-wifi/Cargo.toml @@ -25,20 +25,21 @@ smoltcp = { version = "0.11.0", default-features = false, features = [ critical-section = "1.1.3" enumset = { version = "1.1.5", default-features = false, optional = true } embedded-io = { version = "0.6.1", default-features = false } -embedded-io-async = { version = "0.6.1", optional = true } +embedded-io-async = { version = "0.6.1" } fugit = "0.3.7" heapless = { version = "0.8.0", default-features = false, features = [ "portable-atomic", ] } num-derive = { version = "0.4.2" } num-traits = { version = "0.2.19", default-features = false } -esp-wifi-sys = { version = "0.6.0" } +esp-wifi-sys = { version = "0.6.0", git = "https://github.com/esp-rs/esp-wifi-sys.git", rev = "a3e2ed64e9095f120bbcebe6287ddf62760774db"} embassy-sync = { version = "0.6.0", optional = true } embassy-net-driver = { version = "0.2.0", optional = true } libm = "0.2.8" cfg-if = "1.0.0" portable-atomic = { version = "1.9.0", default-features = false } portable_atomic_enum = { version = "0.3.1", features = ["portable-atomic"] } +rand_core = "0.6.4" bt-hci = { version = "0.1.1", optional = true } esp-config = { version = "0.1.0", path = "../esp-config" } @@ -100,87 +101,36 @@ esp32s3 = [ "xtensa-lx-rt/float-save-restore", ] -## Enable Async support -async = [ - "dep:embassy-sync", - "dep:embedded-io-async", - "dep:bt-hci", -] - -## Enable `embassy-net` support -embassy-net = ["dep:embassy-net-driver", "async"] - ## Enable WiFi-BLE coexistence support coex = [] ## Logs the WiFi logs from the driver at log level info (needs a nightly-compiler) sys-logs = ["esp-wifi-sys/sys-logs"] -## Dumps packet info at log level info -dump-packets = [] - -## Provide implementations of smoltcp traits -smoltcp = ["dep:smoltcp"] +## Enable support for `defmt` +defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt", "bt-hci?/defmt", "esp-wifi-sys/defmt", "heapless/defmt-03"] -## Provide utilities for smoltcp initialization. Adds smoltcp dependency -utils = ["smoltcp"] +## Enable support for the `log` crate +log = ["dep:log", "esp-hal/log", "esp-wifi-sys/log"] ## Enable WiFi support -wifi = ["dep:enumset"] - -## Enable BLE support -ble = ["esp-hal/bluetooth"] - -## See USB-SERIAL-JTAG below -phy-enable-usb = [] - -## Enable minimum modem sleep. Only affects STA mode -ps-min-modem = [] - -## Enable maximum modem sleep. Only affects STA mode -ps-max-modem = [] +wifi = ["dep:enumset", "dep:embassy-net-driver", "dep:embassy-sync"] ## Enable esp-now support esp-now = ["wifi"] -## IPv6 support. Includes utils feature -ipv6 = ["wifi", "utils", "smoltcp?/proto-ipv6"] - -## IPv4 support. Includes utils feature -ipv4 = ["wifi", "utils", "smoltcp?/proto-ipv4"] - -## TCP socket support. Includes ipv4 feature -tcp = ["ipv4", "smoltcp?/socket-tcp"] - -## UDP socket support. Includes ipv4 feature -udp = ["ipv4", "smoltcp?/socket-udp"] - -## ICMP socket support. Includes ipv4 feature -icmp = ["ipv4", "smoltcp?/socket-icmp"] - -## IGMP (multicast) support. Includes ipv4 featu -igmp = ["ipv4", "smoltcp?/proto-igmp"] - -## DNS support. Includes udp feature -dns = ["udp", "smoltcp?/proto-dns", "smoltcp?/socket-dns"] - -## DHCPv4 support, both creating sockets and autoconfiguring network settings. Includes utils feature -dhcpv4 = ["wifi", "utils", "smoltcp?/proto-dhcpv4", "smoltcp?/socket-dhcpv4"] - -## Convenience to enable "ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4" -wifi-default = ["ipv4", "tcp", "udp", "icmp", "igmp", "dns", "dhcpv4"] +## Enable sniffer mode support +sniffer = ["wifi"] -## Enable support for `defmt` -defmt = ["dep:defmt", "smoltcp?/defmt", "esp-hal/defmt", "bt-hci?/defmt", "esp-wifi-sys/defmt"] +## Enable BLE support +ble = ["esp-hal/bluetooth", "dep:bt-hci", "dep:embassy-sync"] -## Enable support for the `log` crate -log = ["dep:log", "esp-hal/log", "esp-wifi-sys/log"] +## Provide implementations of smoltcp traits +smoltcp = ["dep:smoltcp"] -## Enable sniffer mode support -sniffer = ["wifi"] +## Provide utilities for smoltcp initialization. Adds smoltcp dependency +utils = ["smoltcp"] -# Don't include `strchr` - not shown in docs -have-strchr = [] # Implement serde Serialize / Deserialize serde = ["dep:serde", "enumset?/serde", "heapless/serde"] @@ -191,8 +141,6 @@ features = [ "wifi", "ble", "coex", - "async", - "embassy-net", "esp-hal/default", ] -default-target = "riscv32imc-unknown-none-elf" +default-target = "riscv32imc-unknown-none-elf" \ No newline at end of file diff --git a/esp-wifi/MIGRATING-0.10.md b/esp-wifi/MIGRATING-0.10.md index 6bfc390fd6b..190311d46a9 100644 --- a/esp-wifi/MIGRATING-0.10.md +++ b/esp-wifi/MIGRATING-0.10.md @@ -1,5 +1,22 @@ # Migration Guide from 0.10.x to v0.11.x +## Initialization changes + +`EspWifiInitFor` has been removed, individual drivers such as `Wifi` and `BleConnector` handle the initialization and de-initialization of the radio stack. + +`EspWifiInit` has been removed in favour of `EspWifiController`, each radio driver takes reference to this object. If no driver is borrowing `EspWifiController`, +you can safely call `EspWifiController::deinit()` to completely deinit the stack and return the peripherals used in `esp_wifi::init`. + +`esp-wifi::init` now takes all peripherals using the `PeripheralRef` pattern, with the exception of the rng source. + +`esp_wifi::init` now accepts `esp_hal::rng::Rng` or `esp_hal::rng::Trng`. + +The following error enum variants have been removed from `InitializationError`: + +- `Timer(hal::timer::Error)` +- `TimerUnavailable` +- `RadioClockUnavailable` + ## No need to include `rom_functions.x` manually Don't include `rom_functions.x` from esp-wifi @@ -10,3 +27,58 @@ rustflags = [ - "-C", "link-arg=-Trom_functions.x", ] ``` + +## ESP-NOW: Use `data` to access the received payload + +Previously `data` and `len` were public - use the previously already existing `data()` function. + +Accessing `data` or `len` was never encouraged. + +## Async features have been removed and async functionality is always available + +The cost of this is that we need to rename the various `async` methods on `WifiController`. + +```diff +- controller.start().await.unwrap(); ++ controller.start_async().await.unwrap(); +``` + +## The blocking networking stack was removed + +The blocking networking stack is not included anymore. You can use e.g. `smoltcp-nal` or even use `smoltcp` directly. + +For an easy migration path there is https://github.com/bjoernQ/blocking-network-stack.git which is basically the previously included networking stack as it's +own crate. + +The `create_network_interface` function doesn't take `&mut SocketSet[..]` anymore. + +```diff ++use blocking_network_stack::Stack; +use esp_wifi::{ +- wifi_interface::WifiStack, +}; + ++ let mut rng = Rng::new(peripherals.RNG); +- let init = init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); ++ let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + + let mut wifi = peripherals.WIFI; + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); ++ let (iface, device, mut controller) = ++ create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); ++ +- let (iface, device, mut controller, sockets) = +- create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); ++ let socket_set = SocketSet::new(&mut socket_set_entries[..]); + let now = || time::now().duration_since_epoch().to_millis(); +- let wifi_stack = WifiStack::new(iface, device, sockets, now); ++ let wifi_stack = Stack::new(iface, device, socket_set, now, rng.random()); +``` + +The related features are removed from `esp-wifi`: wifi-default, ipv6, ipv4, tcp, udp, icmp, igmp, dns, dhcpv4 + +## `get_` prefixes have been removed from functions + +In order to better comply with the Rust API Guidelines [getter names convention], we have removed the `get_` prefixes from all functions which previously had it. Due to the number of changes it's not practical to list all changes here, however if a function previous began with `get_`, you can simply remove this prefix. + +[getter names convention]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter diff --git a/esp-wifi/build.rs b/esp-wifi/build.rs index 5197c6ffb41..e552a8e2ec4 100644 --- a/esp-wifi/build.rs +++ b/esp-wifi/build.rs @@ -1,7 +1,7 @@ use std::{error::Error, str::FromStr}; use esp_build::assert_unique_used_features; -use esp_config::{generate_config, Value}; +use esp_config::{generate_config, Validator, Value}; use esp_metadata::{Chip, Config}; fn main() -> Result<(), Box> { @@ -99,34 +99,134 @@ fn main() -> Result<(), Box> { generate_config( "esp_wifi", &[ - ("rx_queue_size", Value::UnsignedInteger(5), "Size of the RX queue in frames"), - ("tx_queue_size", Value::UnsignedInteger(3), "Size of the TX queue in frames"), - ("static_rx_buf_num", Value::UnsignedInteger(10), "WiFi static RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("dynamic_rx_buf_num", Value::UnsignedInteger(32), "WiFi dynamic RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("static_tx_buf_num", Value::UnsignedInteger(0), "WiFi static TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("dynamic_tx_buf_num", Value::UnsignedInteger(32), "WiFi dynamic TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("ampdu_rx_enable", Value::Bool(true), "WiFi AMPDU RX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("ampdu_tx_enable", Value::Bool(true), "WiFi AMPDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("amsdu_tx_enable", Value::Bool(false), "WiFi AMSDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("rx_ba_win", Value::UnsignedInteger(6), "WiFi Block Ack RX window size. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)"), - ("max_burst_size", Value::UnsignedInteger(1), "See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size)"), + ("rx_queue_size", "Size of the RX queue in frames", Value::Integer(5), Some(Validator::PositiveInteger)), + ("tx_queue_size", "Size of the TX queue in frames", Value::Integer(3), Some(Validator::PositiveInteger)), + ( + "static_rx_buf_num", + "WiFi static RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Integer(10), + None + ), + ( + "dynamic_rx_buf_num", + "WiFi dynamic RX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Integer(32), + None + ), + ( + "static_tx_buf_num", + "WiFi static TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Integer(0), + None + ), + ( + "dynamic_tx_buf_num", + "WiFi dynamic TX buffer number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Integer(32), + None + ), + ( + "csi_enable", + "WiFi channel state information enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Bool(false), + None + ), + ( + "ampdu_rx_enable", + "WiFi AMPDU RX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Bool(true), + None + ), + ( + "ampdu_tx_enable", + "WiFi AMPDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Bool(true), + None + ), + ( + "amsdu_tx_enable", + "WiFi AMSDU TX feature enable flag. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Bool(false), + None + ), + ( + "rx_ba_win", + "WiFi Block Ack RX window size. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv418wifi_init_config_t)", + Value::Integer(6), + None + ), + ( + "max_burst_size", + "See [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_burst_size)", + Value::Integer(1), + None + ), ( "country_code", - Value::String("CN".to_owned()), "Country code. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)", + Value::String("CN".to_owned()), + None ), ( "country_code_operating_class", - Value::UnsignedInteger(0), "If not 0: Operating Class table number. See [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-country-code)", + Value::Integer(0), + None + ), + ( + "mtu", + "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)", + Value::Integer(1492), + None + ), + ( + "tick_rate_hz", + "Tick rate of the internal task scheduler in hertz", + Value::Integer(100), + None + ), + ( + "listen_interval", + "Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen to beacon is 300 ms", + Value::Integer(3), + None + ), + ( + "beacon_timeout", + "For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. Default 6s. Range 6-30", + Value::Integer(6), + None + ), + ( + "ap_beacon_timeout", + "For SoftAP, If the SoftAP doesn't receive any data from the connected STA during inactive time, the SoftAP will force deauth the STA. Default is 300s", + Value::Integer(300), + None + ), + ( + "failure_retry_cnt", + "Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. Defaults to 1", + Value::Integer(1), + None + ), + ( + "scan_method", + "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0", + Value::Integer(0), + None + ), + ( + "dump_packets", + "Dump packets via an info log statement", + Value::Bool(false), + None + ), + ( + "phy_enable_usb", + "Keeps USB running when using WiFi. This allows debugging and log messages via USB Serial JTAG. Turn off for best WiFi performance.", + Value::Bool(true), + None ), - ("mtu", Value::UnsignedInteger(1492), "MTU, see [smoltcp's documentation](https://docs.rs/smoltcp/0.10.0/smoltcp/phy/struct.DeviceCapabilities.html#structfield.max_transmission_unit)"), - ("tick_rate_hz", Value::UnsignedInteger(100), "Tick rate of the internal task scheduler in hertz"), - ("listen_interval", Value::UnsignedInteger(3), "Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval. For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen to beacon is 300 ms"), - ("beacon_timeout", Value::UnsignedInteger(6), "For Station, If the station does not receive a beacon frame from the connected SoftAP during the inactive time, disconnect from SoftAP. Default 6s. Range 6-30"), - ("ap_beacon_timeout", Value::UnsignedInteger(300), "For SoftAP, If the SoftAP doesn’t receive any data from the connected STA during inactive time, the SoftAP will force deauth the STA. Default is 300s"), - ("failure_retry_cnt", Value::UnsignedInteger(1), "Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config. Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. Defaults to 1"), - ("scan_method", Value::UnsignedInteger(0), "0 = WIFI_FAST_SCAN, 1 = WIFI_ALL_CHANNEL_SCAN, defaults to 0"), ], true ); diff --git a/esp-wifi/src/ble/btdm.rs b/esp-wifi/src/ble/btdm.rs index 3c8a90d43cd..6d444c2d464 100644 --- a/esp-wifi/src/ble/btdm.rs +++ b/esp-wifi/src/ble/btdm.rs @@ -1,8 +1,10 @@ -use core::{cell::RefCell, ptr::addr_of}; +use alloc::boxed::Box; +use core::ptr::{addr_of, addr_of_mut}; -use critical_section::Mutex; +use esp_wifi_sys::c_types::c_void; use portable_atomic::{AtomicBool, Ordering}; +use super::ReceivedPacket; use crate::{ binary::include::*, ble::{ @@ -10,10 +12,8 @@ use crate::{ HciOutCollector, HCI_OUT_COLLECTOR, }, - compat::{common::str_from_c, queue::SimpleQueue}, + compat::common::{self, str_from_c, ConcurrentQueue}, hal::macros::ram, - memory_fence::memory_fence, - timer::yield_task, }; #[cfg_attr(esp32c3, path = "os_adapter_esp32c3.rs")] @@ -21,23 +21,10 @@ use crate::{ #[cfg_attr(esp32, path = "os_adapter_esp32.rs")] pub(crate) mod ble_os_adapter_chip_specific; -static BT_RECEIVE_QUEUE: Mutex>> = - Mutex::new(RefCell::new(SimpleQueue::new())); - -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ReceivedPacket { - pub len: u8, - pub data: [u8; 256], -} - -static BT_INTERNAL_QUEUE: Mutex>> = - Mutex::new(RefCell::new(SimpleQueue::new())); - static PACKET_SENT: AtomicBool = AtomicBool::new(true); #[repr(C)] -struct vhci_host_callback_s { +struct VhciHostCallbacks { notify_host_send_available: extern "C" fn(), /* callback used to notify that the host can * send packet to controller */ notify_host_recv: extern "C" fn(*mut u8, u16) -> i32, /* callback used to notify that the @@ -62,10 +49,10 @@ extern "C" { fn API_vhci_host_check_send_available() -> bool; fn API_vhci_host_send_packet(data: *const u8, len: u16); - fn API_vhci_host_register_callback(vhci_host_callbac: *const vhci_host_callback_s) -> i32; + fn API_vhci_host_register_callback(vhci_host_callbac: *const VhciHostCallbacks) -> i32; } -static VHCI_HOST_CALLBACK: vhci_host_callback_s = vhci_host_callback_s { +static VHCI_HOST_CALLBACK: VhciHostCallbacks = VhciHostCallbacks { notify_host_send_available, notify_host_recv, }; @@ -79,27 +66,20 @@ extern "C" fn notify_host_send_available() { extern "C" fn notify_host_recv(data: *mut u8, len: u16) -> i32 { trace!("notify_host_recv {:?} {}", data, len); - unsafe { - let mut buf = [0u8; 256]; - buf[..len as usize].copy_from_slice(core::slice::from_raw_parts(data, len as usize)); - - let packet = ReceivedPacket { - len: len as u8, - data: buf, - }; - - critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - if queue.enqueue(packet).is_err() { - warn!("Dropping BLE packet"); - } - }); + let data = unsafe { core::slice::from_raw_parts(data, len as usize) }; + + let packet = ReceivedPacket { + data: Box::from(data), + }; - dump_packet_info(core::slice::from_raw_parts(data as *const u8, len as usize)); + critical_section::with(|cs| { + let mut queue = super::BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + queue.push_back(packet); + }); - #[cfg(feature = "async")] - crate::ble::controller::asynch::hci_read_data_available(); - } + super::dump_packet_info(data); + + crate::ble::controller::asynch::hci_read_data_available(); 0 } @@ -178,38 +158,21 @@ unsafe extern "C" fn mutex_unlock(_mutex: *const ()) -> i32 { } unsafe extern "C" fn queue_create(len: u32, item_size: u32) -> *const () { - if len != 5 && item_size != 8 { - panic!("Unexpected queue spec {} {}", len, item_size); - } - &BT_INTERNAL_QUEUE as *const _ as *const () + let ptr = common::create_queue(len as i32, item_size as i32); + ptr.cast() } unsafe extern "C" fn queue_delete(queue: *const ()) { - trace!("Unimplemented queue_delete {:?}", queue); + common::delete_queue(queue as *mut ConcurrentQueue) } #[ram] -unsafe extern "C" fn queue_send(queue: *const (), item: *const (), _block_time_ms: u32) -> i32 { - if queue == &BT_INTERNAL_QUEUE as *const _ as *const () { - critical_section::with(|_| { - // assume the size is 8 - shouldn't rely on that - let message = item as *const u8; - let mut data = [0u8; 8]; - for (i, data) in data.iter_mut().enumerate() { - *data = *message.add(i); - } - trace!("queue posting {:?}", data); - - critical_section::with(|cs| { - let mut queue = BT_INTERNAL_QUEUE.borrow_ref_mut(cs); - unwrap!(queue.enqueue(data)); - }); - memory_fence(); - }); - } else { - panic!("Unknown queue"); - } - 1 +unsafe extern "C" fn queue_send(queue: *const (), item: *const (), block_time_ms: u32) -> i32 { + common::send_queued( + queue as *mut ConcurrentQueue, + item as *mut c_void, + block_time_ms, + ) } #[ram] @@ -225,53 +188,11 @@ unsafe extern "C" fn queue_send_from_isr( } unsafe extern "C" fn queue_recv(queue: *const (), item: *const (), block_time_ms: u32) -> i32 { - trace!( - "queue_recv {:?} item {:?} block_time_tick {}", - queue, - item, - block_time_ms - ); - - let forever = block_time_ms == crate::compat::common::OSI_FUNCS_TIME_BLOCKING; - let start = crate::timer::get_systimer_count(); - let block_ticks = crate::timer::millis_to_ticks(block_time_ms as u64); - - // handle the BT_QUEUE - if queue == &BT_INTERNAL_QUEUE as *const _ as *const () { - loop { - let res = critical_section::with(|_| { - memory_fence(); - - critical_section::with(|cs| { - let mut queue = BT_INTERNAL_QUEUE.borrow_ref_mut(cs); - if let Some(message) = queue.dequeue() { - let item = item as *mut u8; - for i in 0..8 { - item.offset(i).write_volatile(message[i as usize]); - } - trace!("received {:?}", message); - 1 - } else { - 0 - } - }) - }); - - if res == 1 { - trace!("queue_recv returns"); - return res; - } - - if !forever && crate::timer::elapsed_time_since(start) > block_ticks { - trace!("queue_recv returns with timeout"); - return -1; - } - - yield_task(); - } - } else { - panic!("Unknown queue to handle in queue_recv"); - } + common::receive_queued( + queue as *mut ConcurrentQueue, + item as *mut c_void, + block_time_ms, + ) } #[ram] @@ -444,8 +365,9 @@ unsafe extern "C" fn custom_queue_create( pub(crate) fn ble_init() { unsafe { - *(HCI_OUT_COLLECTOR.as_mut_ptr()) = HciOutCollector::new(); + (*addr_of_mut!(HCI_OUT_COLLECTOR)).write(HciOutCollector::new()); // turn on logging + #[allow(static_mut_refs)] #[cfg(feature = "sys-logs")] { extern "C" { @@ -519,6 +441,7 @@ pub(crate) fn ble_init() { API_vhci_host_register_callback(&VHCI_HOST_CALLBACK); } + crate::flags::BLE.store(true, Ordering::Release); } pub(crate) fn ble_deinit() { @@ -530,69 +453,11 @@ pub(crate) fn ble_deinit() { btdm_controller_deinit(); crate::common_adapter::chip_specific::phy_disable(); } + crate::flags::BLE.store(false, Ordering::Release); } -static mut BLE_HCI_READ_DATA: [u8; 256] = [0u8; 256]; -static mut BLE_HCI_READ_DATA_INDEX: usize = 0; -static mut BLE_HCI_READ_DATA_LEN: usize = 0; - -#[cfg(feature = "async")] -pub(crate) fn have_hci_read_data() -> bool { - critical_section::with(|cs| { - let queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - !queue.is_empty() - || unsafe { - BLE_HCI_READ_DATA_LEN > 0 && (BLE_HCI_READ_DATA_LEN >= BLE_HCI_READ_DATA_INDEX) - } - }) -} - -pub(crate) fn read_next(data: &mut [u8]) -> usize { - critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - - match queue.dequeue() { - Some(packet) => { - data[..packet.len as usize].copy_from_slice(&packet.data[..packet.len as usize]); - packet.len as usize - } - None => 0, - } - }) -} - -pub(crate) fn read_hci(data: &mut [u8]) -> usize { - unsafe { - if BLE_HCI_READ_DATA_LEN == 0 { - critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - - if let Some(packet) = queue.dequeue() { - BLE_HCI_READ_DATA[..packet.len as usize] - .copy_from_slice(&packet.data[..packet.len as usize]); - BLE_HCI_READ_DATA_LEN = packet.len as usize; - BLE_HCI_READ_DATA_INDEX = 0; - } - }); - } - - if BLE_HCI_READ_DATA_LEN > 0 { - data[0] = BLE_HCI_READ_DATA[BLE_HCI_READ_DATA_INDEX]; - BLE_HCI_READ_DATA_INDEX += 1; - - if BLE_HCI_READ_DATA_INDEX >= BLE_HCI_READ_DATA_LEN { - BLE_HCI_READ_DATA_LEN = 0; - BLE_HCI_READ_DATA_INDEX = 0; - } - return 1; - } - } - - 0 -} - -pub(crate) fn send_hci(data: &[u8]) { - let hci_out = unsafe { &mut *HCI_OUT_COLLECTOR.as_mut_ptr() }; +pub fn send_hci(data: &[u8]) { + let hci_out = unsafe { (*addr_of_mut!(HCI_OUT_COLLECTOR)).assume_init_mut() }; hci_out.push(data); if hci_out.is_ready() { @@ -611,7 +476,7 @@ pub(crate) fn send_hci(data: &[u8]) { API_vhci_host_send_packet(packet.as_ptr(), packet.len() as u16); trace!("sent vhci host packet"); - dump_packet_info(packet); + super::dump_packet_info(packet); break; } @@ -623,13 +488,3 @@ pub(crate) fn send_hci(data: &[u8]) { hci_out.reset(); } } - -#[allow(unreachable_code, unused_variables)] -fn dump_packet_info(buffer: &[u8]) { - #[cfg(not(feature = "dump-packets"))] - return; - - critical_section::with(|cs| { - info!("@HCIFRAME {:?}", buffer); - }); -} diff --git a/esp-wifi/src/ble/controller/mod.rs b/esp-wifi/src/ble/controller/mod.rs index 9ca3cc76177..41a6ae188cb 100644 --- a/esp-wifi/src/ble/controller/mod.rs +++ b/esp-wifi/src/ble/controller/mod.rs @@ -3,7 +3,7 @@ use embedded_io::{Error, ErrorType, Read, Write}; use super::{read_hci, read_next, send_hci}; use crate::{ hal::peripheral::{Peripheral, PeripheralRef}, - EspWifiInitialization, + EspWifiController, }; /// A blocking HCI connector @@ -11,21 +11,25 @@ pub struct BleConnector<'d> { _device: PeripheralRef<'d, crate::hal::peripherals::BT>, } +impl<'d> Drop for BleConnector<'d> { + fn drop(&mut self) { + crate::ble::ble_deinit(); + } +} + impl<'d> BleConnector<'d> { pub fn new( - init: &EspWifiInitialization, + _init: &'d EspWifiController<'d>, device: impl Peripheral

+ 'd, ) -> BleConnector<'d> { - if !init.is_ble() { - panic!("Not initialized for BLE use"); - } + crate::ble::ble_init(); Self { _device: device.into_ref(), } } - pub fn get_next(&mut self, buf: &mut [u8]) -> Result { + pub fn next(&mut self, buf: &mut [u8]) -> Result { Ok(read_next(buf)) } } @@ -79,8 +83,7 @@ impl Write for BleConnector<'_> { } /// Async Interface -#[cfg(feature = "async")] -pub mod asynch { +pub(crate) mod asynch { use core::task::Poll; use bt_hci::{ @@ -91,14 +94,9 @@ pub mod asynch { WriteHci, }; use embassy_sync::waitqueue::AtomicWaker; - use embedded_io::ErrorType; - use super::{read_hci, send_hci, BleConnectorError}; - use crate::{ - ble::ble::have_hci_read_data, - hal::peripheral::{Peripheral, PeripheralRef}, - EspWifiInitialization, - }; + use super::*; + use crate::ble::have_hci_read_data; static HCI_WAKER: AtomicWaker = AtomicWaker::new(); @@ -106,30 +104,6 @@ pub mod asynch { HCI_WAKER.wake(); } - /// Async HCI connector - pub struct BleConnector<'d> { - _device: PeripheralRef<'d, crate::hal::peripherals::BT>, - } - - impl<'d> BleConnector<'d> { - pub fn new( - init: &EspWifiInitialization, - device: impl Peripheral

+ 'd, - ) -> BleConnector<'d> { - if !init.is_ble() { - panic!("Not initialized for BLE use"); - } - - Self { - _device: device.into_ref(), - } - } - } - - impl ErrorType for BleConnector<'_> { - type Error = BleConnectorError; - } - impl embedded_io_async::Read for BleConnector<'_> { async fn read(&mut self, buf: &mut [u8]) -> Result { if !have_hci_read_data() { @@ -211,7 +185,7 @@ pub mod asynch { } } - impl<'d> Transport for BleConnector<'d> { + impl Transport for BleConnector<'_> { /// Read a complete HCI packet into the rx buffer async fn read<'a>( &self, diff --git a/esp-wifi/src/ble/mod.rs b/esp-wifi/src/ble/mod.rs index bf26fc3d913..22994478b53 100644 --- a/esp-wifi/src/ble/mod.rs +++ b/esp-wifi/src/ble/mod.rs @@ -6,9 +6,11 @@ pub(crate) mod btdm; #[cfg(any(esp32c2, esp32c6, esp32h2))] pub(crate) mod npl; -use core::mem::MaybeUninit; +use alloc::{boxed::Box, collections::vec_deque::VecDeque, vec::Vec}; +use core::{cell::RefCell, mem::MaybeUninit}; -pub(crate) use ble::{ble_init, read_hci, read_next, send_hci}; +pub(crate) use ble::{ble_deinit, ble_init, send_hci}; +use critical_section::Mutex; #[cfg(any(esp32, esp32c3, esp32s3))] use self::btdm as ble; @@ -30,6 +32,10 @@ pub(crate) unsafe extern "C" fn free(ptr: *mut crate::binary::c_types::c_void) { crate::compat::malloc::free(ptr.cast()) } +// Stores received packets until the the BLE stack dequeues them +static BT_RECEIVE_QUEUE: Mutex>> = + Mutex::new(RefCell::new(VecDeque::new())); + static mut HCI_OUT_COLLECTOR: MaybeUninit = MaybeUninit::uninit(); #[derive(PartialEq, Debug)] @@ -96,3 +102,65 @@ impl HciOutCollector { &self.data[0..self.index] } } + +static BLE_HCI_READ_DATA: Mutex>> = Mutex::new(RefCell::new(Vec::new())); + +#[derive(Debug, Clone)] +pub struct ReceivedPacket { + pub data: Box<[u8]>, +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ReceivedPacket { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "ReceivedPacket {}", &self.data[..]) + } +} + +pub fn have_hci_read_data() -> bool { + critical_section::with(|cs| { + let queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + let hci_read_data = BLE_HCI_READ_DATA.borrow_ref(cs); + !queue.is_empty() || !hci_read_data.is_empty() + }) +} + +pub(crate) fn read_next(data: &mut [u8]) -> usize { + critical_section::with(|cs| { + let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + + match queue.pop_front() { + Some(packet) => { + data[..packet.data.len()].copy_from_slice(&packet.data[..packet.data.len()]); + packet.data.len() + } + None => 0, + } + }) +} + +pub fn read_hci(data: &mut [u8]) -> usize { + critical_section::with(|cs| { + let mut hci_read_data = BLE_HCI_READ_DATA.borrow_ref_mut(cs); + + if hci_read_data.is_empty() { + let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + + if let Some(packet) = queue.pop_front() { + hci_read_data.extend_from_slice(&packet.data); + } + } + + let l = usize::min(hci_read_data.len(), data.len()); + data[..l].copy_from_slice(&hci_read_data[..l]); + hci_read_data.drain(..l); + l + }) +} + +fn dump_packet_info(_buffer: &[u8]) { + #[cfg(dump_packets)] + critical_section::with(|_cs| { + info!("@HCIFRAME {:?}", _buffer); + }); +} diff --git a/esp-wifi/src/ble/npl.rs b/esp-wifi/src/ble/npl.rs index 8deaadc5bc3..f08516f95da 100644 --- a/esp-wifi/src/ble/npl.rs +++ b/esp-wifi/src/ble/npl.rs @@ -1,18 +1,20 @@ +use alloc::boxed::Box; use core::{ - cell::RefCell, + mem::size_of_val, ptr::{addr_of, addr_of_mut}, + sync::atomic::Ordering, }; -use critical_section::Mutex; - use super::*; use crate::{ binary::{ c_types::{c_char, c_void}, include::*, }, - compat, - compat::{common::str_from_c, queue::SimpleQueue}, + compat::{ + self, + common::{str_from_c, ConcurrentQueue}, + }, timer::yield_task, }; @@ -21,6 +23,8 @@ use crate::{ #[cfg_attr(esp32h2, path = "os_adapter_esp32h2.rs")] pub(crate) mod ble_os_adapter_chip_specific; +const EVENT_QUEUE_SIZE: usize = 16; + const TIME_FOREVER: u32 = crate::compat::common::OSI_FUNCS_TIME_BLOCKING; #[cfg(esp32c2)] @@ -41,41 +45,25 @@ const BLE_HCI_TRANS_BUF_CMD: i32 = 3; // ACL_DATA_MBUF_LEADINGSPCAE: The leadingspace in user info header for ACL data const ACL_DATA_MBUF_LEADINGSPACE: usize = 4; +#[repr(C)] #[derive(Copy, Clone)] struct Callout { - _callout: *const ble_npl_callout, eventq: *const ble_npl_eventq, timer_handle: ets_timer, events: ble_npl_event, } -static mut CALLOUTS: [Option; 18] = [None; 18]; - +#[repr(C)] #[derive(Copy, Clone)] struct Event { - event: *const ble_npl_event, event_fn_ptr: *const ble_npl_event_fn, ev_arg_ptr: *const c_void, queued: bool, } -static mut EVENTS: [Option; 95] = [None; 95]; - -static mut EVENT_QUEUE: SimpleQueue = SimpleQueue::new(); - -static BT_RECEIVE_QUEUE: Mutex>> = - Mutex::new(RefCell::new(SimpleQueue::new())); - #[cfg(esp32c2)] type OsMembufT = u32; -#[derive(Debug, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ReceivedPacket { - pub len: u8, - pub data: [u8; 256], -} - /// Memory pool #[repr(C)] pub(crate) struct OsMempool { @@ -711,14 +699,12 @@ unsafe extern "C" fn ble_npl_callout_deinit(callout: *const ble_npl_callout) { unsafe extern "C" fn ble_npl_callout_stop(callout: *const ble_npl_callout) { trace!("ble_npl_callout_stop {:?}", callout); - if (*callout).dummy == 0 { - panic!("Trying to stop an uninitialzed callout"); - } + assert!((*callout).dummy != 0); - let co = unwrap!(CALLOUTS[((*callout).dummy - 1) as usize].as_mut()); + let co = (*callout).dummy as *mut Callout; // stop timer - compat::timer_compat::compat_timer_disarm(addr_of_mut!(co.timer_handle)); + compat::timer_compat::compat_timer_disarm(addr_of_mut!((*co).timer_handle)); } unsafe extern "C" fn ble_npl_callout_reset( @@ -727,10 +713,10 @@ unsafe extern "C" fn ble_npl_callout_reset( ) -> ble_npl_error_t { trace!("ble_npl_callout_reset {:?} {}", callout, time); - let co = unwrap!(CALLOUTS[((*callout).dummy - 1) as usize].as_mut()); + let co = (*callout).dummy as *mut Callout; // start timer - compat::timer_compat::compat_timer_arm(addr_of_mut!(co.timer_handle), time, false); + compat::timer_compat::compat_timer_arm(addr_of_mut!((*co).timer_handle), time, false); 0 } @@ -775,20 +761,20 @@ unsafe extern "C" fn ble_npl_mutex_deinit(_mutex: *const ble_npl_mutex) -> ble_n unsafe extern "C" fn ble_npl_event_set_arg(event: *const ble_npl_event, arg: *const c_void) { trace!("ble_npl_event_set_arg {:?} {:?}", event, arg); - if (*event).dummy == 0 { - panic!("Call set_arg on uninitialized event"); - } - unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).ev_arg_ptr = arg; + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); + + (*evt).ev_arg_ptr = arg; } unsafe extern "C" fn ble_npl_event_get_arg(event: *const ble_npl_event) -> *const c_void { trace!("ble_npl_event_get_arg {:?}", event); - if (*event).dummy == 0 { - panic!("Call get_arg on uninitialized event"); - } - let arg_ptr = unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).ev_arg_ptr; + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); + + let arg_ptr = (*evt).ev_arg_ptr; trace!("returning arg {:x}", arg_ptr as usize); @@ -797,36 +783,31 @@ unsafe extern "C" fn ble_npl_event_get_arg(event: *const ble_npl_event) -> *cons unsafe extern "C" fn ble_npl_event_is_queued(event: *const ble_npl_event) -> bool { trace!("ble_npl_event_is_queued {:?}", event); - if (*event).dummy == 0 { - panic!("Call is_queued on uninitialized event"); - } - unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).queued + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); + + (*evt).queued } unsafe extern "C" fn ble_npl_event_reset(event: *const ble_npl_event) { trace!("ble_npl_event_reset {:?}", event); - let event = event as *mut ble_npl_event; - if (*event).dummy == 0 { - panic!("Trying to reset an uninitialized event"); - } else { - unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).queued = false; - } + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); + + (*evt).queued = false } unsafe extern "C" fn ble_npl_event_deinit(event: *const ble_npl_event) { trace!("ble_npl_event_deinit {:?}", event); - let event = event.cast_mut(); + let event = event as *mut ble_npl_event; + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); - if (*event).dummy == 0 { - panic!("Trying to deinitialize an uninitialized event"); - } else { - let idx = ((*event).dummy - 1) as usize; - EVENTS[idx] = None; - (*event).dummy = 0; - } + crate::compat::malloc::free(evt.cast()); + (*event).dummy = 0; } unsafe extern "C" fn ble_npl_event_init( @@ -837,41 +818,34 @@ unsafe extern "C" fn ble_npl_event_init( trace!("ble_npl_event_init {:?} {:?} {:?}", event, func, arg); if (*event).dummy == 0 { - let idx = unwrap!(EVENTS.iter().position(|item| item.is_none())); - EVENTS[idx] = Some(Event { - event, - event_fn_ptr: func, - ev_arg_ptr: arg, - queued: false, - }); + let evt = crate::compat::malloc::calloc(1, core::mem::size_of::()) as *mut Event; + + (*evt).event_fn_ptr = func; + (*evt).ev_arg_ptr = arg; + (*evt).queued = false; let event = event.cast_mut(); - (*event).dummy = (idx + 1) as i32; + (*event).dummy = evt as i32; } } unsafe extern "C" fn ble_npl_eventq_is_empty(queue: *const ble_npl_eventq) -> bool { trace!("ble_npl_eventq_is_empty {:?}", queue); - if (*queue).dummy == 0 { - panic!("Try to use uninitialized queue"); - } - - critical_section::with(|_| EVENT_QUEUE.is_empty()) + let queue = (*queue).dummy as *mut ConcurrentQueue; + assert!(!queue.is_null()); + (*queue).count() == 0 } unsafe extern "C" fn ble_npl_event_run(event: *const ble_npl_event) { trace!("ble_npl_event_run {:?}", event); - let event = event as *mut ble_npl_event; - if (*event).dummy == 0 { - panic!("Trying to run an uninitialized event"); - } else { - let ev = unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()); - trace!("info {:?} with arg {:x}", ev.event_fn_ptr, event as u32); - let func: unsafe extern "C" fn(u32) = core::mem::transmute(ev.event_fn_ptr); - func(event as u32); - } + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); + + trace!("info {:?} with arg {:x}", (*evt).event_fn_ptr, event as u32); + let func: unsafe extern "C" fn(u32) = core::mem::transmute((*evt).event_fn_ptr); + func(event as u32); trace!("ble_npl_event_run done"); } @@ -880,36 +854,35 @@ unsafe extern "C" fn ble_npl_eventq_remove( queue: *const ble_npl_eventq, event: *const ble_npl_event, ) { - trace!("ble_npl_eventq_remove {:?} {:?}", queue, event); + info!("ble_npl_eventq_remove {:?} {:?}", queue, event); - if (*queue).dummy == 0 { - panic!("Try to use uninitialized queue"); - } + assert!((*queue).dummy != 0); + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); - if (*event).dummy == 0 { - panic!("Try to use uninitialized event"); + if !(*evt).queued { + return; } - critical_section::with(|_| { - unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).queued = false; - }); + let queue = (*queue).dummy as *mut ConcurrentQueue; + (*queue).remove(addr_of!(event) as *mut _); + + (*evt).queued = false; } unsafe extern "C" fn ble_npl_eventq_put(queue: *const ble_npl_eventq, event: *const ble_npl_event) { trace!("ble_npl_eventq_put {:?} {:?}", queue, event); - if (*queue).dummy == 0 { - panic!("Try to use uninitialized queue"); - } + assert!((*queue).dummy != 0); - if (*event).dummy == 0 { - panic!("Try to use uninitialized event"); - } + let evt = (*event).dummy as *mut Event; + assert!(!evt.is_null()); - critical_section::with(|_| { - unwrap!(EVENTS[((*event).dummy - 1) as usize].as_mut()).queued = true; - unwrap!(EVENT_QUEUE.enqueue((*event).dummy as usize)); - }); + (*evt).queued = true; + + let queue = (*queue).dummy as *mut ConcurrentQueue; + let mut event = event as usize; + (*queue).enqueue(addr_of_mut!(event).cast()); } unsafe extern "C" fn ble_npl_eventq_get( @@ -918,16 +891,18 @@ unsafe extern "C" fn ble_npl_eventq_get( ) -> *const ble_npl_event { trace!("ble_npl_eventq_get {:?} {}", queue, time); + let queue = (*queue).dummy as *mut ConcurrentQueue; + + let mut event: usize = 0; if time == TIME_FOREVER { loop { - let dequeued = critical_section::with(|_| EVENT_QUEUE.dequeue()); - - if let Some(event_idx) = dequeued { - let evt = unwrap!(EVENTS[event_idx - 1].as_mut()); - if evt.queued { - trace!("got {:x}", evt.event as usize); - evt.queued = false; - return evt.event; + if (*queue).try_dequeue(addr_of_mut!(event).cast()) { + let event = event as *mut ble_npl_event; + let evt = (*event).dummy as *mut Event; + if (*evt).queued { + trace!("got {:x}", evt as usize); + (*evt).queued = false; + return event as *const ble_npl_event; } } @@ -942,19 +917,13 @@ unsafe extern "C" fn ble_npl_eventq_deinit(queue: *const ble_npl_eventq) { trace!("ble_npl_eventq_deinit {:?}", queue); let queue = queue.cast_mut(); - if (*queue).dummy == 0 { - panic!("Trying to deinitialize an uninitialized queue"); - } else { - critical_section::with(|_| { - while let Some(event_idx) = EVENT_QUEUE.dequeue() { - if let Some(event) = EVENTS[event_idx - 1].as_mut() { - event.queued = false; - } - } - }); + assert!((*queue).dummy != 0); - (*queue).dummy = 0; + let real_queue = (*queue).dummy as *mut ConcurrentQueue; + unsafe { + core::ptr::drop_in_place(real_queue); } + (*queue).dummy = 0; } unsafe extern "C" fn ble_npl_callout_init( @@ -973,58 +942,46 @@ unsafe extern "C" fn ble_npl_callout_init( if (*callout).dummy == 0 { let callout = callout.cast_mut(); - let idx = unwrap!(CALLOUTS.iter().position(|item| item.is_none())); - - let new_callout = CALLOUTS[idx].insert(Callout { - _callout: callout, - eventq, - timer_handle: ets_timer { - next: core::ptr::null_mut(), - expire: 0, - period: 0, - func: None, - priv_: core::ptr::null_mut(), - }, - events: ble_npl_event { dummy: 0 }, - }); - ble_npl_event_init(addr_of_mut!(new_callout.events), func, args); + let new_callout = + crate::compat::malloc::calloc(1, core::mem::size_of::()) as *mut Callout; + ble_npl_event_init(addr_of_mut!((*new_callout).events), func, args); + (*callout).dummy = new_callout as i32; crate::compat::timer_compat::compat_timer_setfn( - addr_of_mut!(new_callout.timer_handle), + addr_of_mut!((*new_callout).timer_handle), callout_timer_callback_wrapper, - idx as *mut c_void, + callout as *mut c_void, ); - - (*callout).dummy = (idx + 1) as i32; } 0 } unsafe extern "C" fn callout_timer_callback_wrapper(arg: *mut c_void) { - info!("callout_timer_callback_wrapper {:?}", arg); - let co = unwrap!(CALLOUTS[arg as usize].as_mut()); + trace!("callout_timer_callback_wrapper {:?}", arg); + let co = (*(arg as *mut ble_npl_callout)).dummy as *mut Callout; - if co.eventq.is_null() { - ble_npl_eventq_put(addr_of!(co.events).cast(), addr_of!(co.events)); + if !(*co).eventq.is_null() { + ble_npl_eventq_put(addr_of!((*co).eventq).cast(), addr_of!((*co).events)); } else { - ble_npl_event_run(addr_of!(co.events)); + ble_npl_event_run(addr_of!((*co).events)); } } unsafe extern "C" fn ble_npl_eventq_init(queue: *const ble_npl_eventq) { trace!("ble_npl_eventq_init {:?}", queue); - critical_section::with(|_cs| { - let queue = queue as *mut ble_npl_eventq; + let queue = queue as *mut ble_npl_eventq; - if (*queue).dummy == 0 { - (*queue).dummy = 1; - } else { - panic!("Only one emulated queue supported"); - } - }); + let raw_queue = ConcurrentQueue::new(EVENT_QUEUE_SIZE, 4); + let ptr = + unsafe { crate::compat::malloc::malloc(size_of_val(&raw_queue)) as *mut ConcurrentQueue }; + unsafe { + ptr.write(raw_queue); + } + + (*queue).dummy = ptr as i32; } unsafe extern "C" fn ble_npl_mutex_init(_mutex: *const ble_npl_mutex) -> u32 { @@ -1050,9 +1007,10 @@ pub struct BleNplCountInfoT { pub(crate) fn ble_init() { unsafe { - *(HCI_OUT_COLLECTOR.as_mut_ptr()) = HciOutCollector::new(); + (*addr_of_mut!(HCI_OUT_COLLECTOR)).write(HciOutCollector::new()); // turn on logging + #[allow(static_mut_refs)] #[cfg(all(feature = "sys-logs", esp32c2))] { extern "C" { @@ -1222,6 +1180,7 @@ pub(crate) fn ble_init() { debug!("The ble_controller_init was initialized"); } + crate::flags::BLE.store(true, Ordering::Release); } pub(crate) fn ble_deinit() { @@ -1247,11 +1206,8 @@ pub(crate) fn ble_deinit() { npl::esp_unregister_ext_funcs(); crate::common_adapter::chip_specific::phy_disable(); - - CALLOUTS.iter_mut().for_each(|item| { - item.take(); - }); } + crate::flags::BLE.store(false, Ordering::Release); } #[cfg(esp32c2)] @@ -1325,7 +1281,7 @@ unsafe extern "C" fn ble_hs_hci_rx_evt(cmd: *const u8, arg: *const c_void) -> i3 trace!("$ pld = {:?}", payload); critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + let mut queue = super::BT_RECEIVE_QUEUE.borrow_ref_mut(cs); let mut data = [0u8; 256]; data[0] = 0x04; // this is an event @@ -1333,22 +1289,15 @@ unsafe extern "C" fn ble_hs_hci_rx_evt(cmd: *const u8, arg: *const c_void) -> i3 data[2] = len as u8; data[3..][..len].copy_from_slice(payload); - if queue - .enqueue(ReceivedPacket { - len: (len + 3) as u8, - data, - }) - .is_err() - { - warn!("Dropping BLE packet"); - } + queue.push_back(ReceivedPacket { + data: Box::from(&data[..len + 3]), + }); dump_packet_info(&data[..(len + 3)]); }); r_ble_hci_trans_buf_free(cmd); - #[cfg(feature = "async")] crate::ble::controller::asynch::hci_read_data_available(); 0 @@ -1362,94 +1311,28 @@ unsafe extern "C" fn ble_hs_rx_data(om: *const OsMbuf, arg: *const c_void) -> i3 let data_slice = core::slice::from_raw_parts(data_ptr, len as usize); critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); + let mut queue = super::BT_RECEIVE_QUEUE.borrow_ref_mut(cs); let mut data = [0u8; 256]; data[0] = 0x02; // ACL data[1..][..data_slice.len()].copy_from_slice(data_slice); - if queue - .enqueue(ReceivedPacket { - len: (len + 1) as u8, - data, - }) - .is_err() - { - warn!("Dropping BLE packet"); - } + queue.push_back(ReceivedPacket { + data: Box::from(&data[..data_slice.len() + 1]), + }); - dump_packet_info(&data[..(len + 1) as usize]); + super::dump_packet_info(&data[..(len + 1) as usize]); }); r_os_mbuf_free_chain(om as *mut _); - #[cfg(feature = "async")] crate::ble::controller::asynch::hci_read_data_available(); 0 } -static mut BLE_HCI_READ_DATA: [u8; 256] = [0u8; 256]; -static mut BLE_HCI_READ_DATA_INDEX: usize = 0; -static mut BLE_HCI_READ_DATA_LEN: usize = 0; - -#[cfg(feature = "async")] -pub fn have_hci_read_data() -> bool { - critical_section::with(|cs| { - let queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - !queue.is_empty() - || unsafe { - BLE_HCI_READ_DATA_LEN > 0 && (BLE_HCI_READ_DATA_LEN >= BLE_HCI_READ_DATA_INDEX) - } - }) -} - -pub(crate) fn read_next(data: &mut [u8]) -> usize { - critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - - match queue.dequeue() { - Some(packet) => { - data[..packet.len as usize].copy_from_slice(&packet.data[..packet.len as usize]); - packet.len as usize - } - None => 0, - } - }) -} - -pub fn read_hci(data: &mut [u8]) -> usize { - unsafe { - if BLE_HCI_READ_DATA_LEN == 0 { - critical_section::with(|cs| { - let mut queue = BT_RECEIVE_QUEUE.borrow_ref_mut(cs); - - if let Some(packet) = queue.dequeue() { - BLE_HCI_READ_DATA[..packet.len as usize] - .copy_from_slice(&packet.data[..packet.len as usize]); - BLE_HCI_READ_DATA_LEN = packet.len as usize; - BLE_HCI_READ_DATA_INDEX = 0; - } - }); - } - - if BLE_HCI_READ_DATA_LEN > 0 { - data[0] = BLE_HCI_READ_DATA[BLE_HCI_READ_DATA_INDEX]; - BLE_HCI_READ_DATA_INDEX += 1; - - if BLE_HCI_READ_DATA_INDEX >= BLE_HCI_READ_DATA_LEN { - BLE_HCI_READ_DATA_LEN = 0; - BLE_HCI_READ_DATA_INDEX = 0; - } - return 1; - } - } - - 0 -} - pub fn send_hci(data: &[u8]) { - let hci_out = unsafe { &mut *HCI_OUT_COLLECTOR.as_mut_ptr() }; + let hci_out = unsafe { (*addr_of_mut!(HCI_OUT_COLLECTOR)).assume_init_mut() }; hci_out.push(data); if hci_out.is_ready() { @@ -1503,13 +1386,3 @@ pub fn send_hci(data: &[u8]) { hci_out.reset(); } } - -#[allow(unreachable_code, unused_variables)] -fn dump_packet_info(buffer: &[u8]) { - #[cfg(not(feature = "dump-packets"))] - return; - - critical_section::with(|cs| { - info!("@HCIFRAME {:?}", buffer); - }); -} diff --git a/esp-wifi/src/ble/os_adapter_esp32.rs b/esp-wifi/src/ble/os_adapter_esp32.rs index 39f55cadc2b..f49c2ac05ba 100644 --- a/esp-wifi/src/ble/os_adapter_esp32.rs +++ b/esp-wifi/src/ble/os_adapter_esp32.rs @@ -586,7 +586,6 @@ const BTDM_ASYNC_WAKEUP_REQ_COEX: i32 = 1; /// true if request lock is needed, false otherwise /// /// ************************************************************************* - #[cfg(coex)] fn async_wakeup_request(event: i32) -> bool { let mut do_wakeup_request = false; @@ -623,7 +622,6 @@ fn async_wakeup_request(event: i32) -> bool { /// true if request lock is needed, false otherwise /// /// ************************************************************************* - #[cfg(coex)] fn async_wakeup_request_end(event: i32) { let request_lock = match event { diff --git a/esp-wifi/src/ble/os_adapter_esp32c2.rs b/esp-wifi/src/ble/os_adapter_esp32c2.rs index 06307c58ac3..8a24212b7b1 100644 --- a/esp-wifi/src/ble/os_adapter_esp32c2.rs +++ b/esp-wifi/src/ble/os_adapter_esp32c2.rs @@ -1,8 +1,4 @@ -use crate::{ - binary::include::esp_bt_controller_config_t, - common_adapter::RADIO_CLOCKS, - hal::system::RadioClockController, -}; +use crate::{binary::include::esp_bt_controller_config_t, hal::system::RadioClockController}; pub(crate) static mut ISR_INTERRUPT_4: ( *mut crate::binary::c_types::c_void, @@ -103,14 +99,16 @@ pub(super) unsafe extern "C" fn esp_intr_alloc( } pub(super) fn ble_rtc_clk_init() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).ble_rtc_clk_init(); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.ble_rtc_clk_init(); } pub(super) unsafe extern "C" fn esp_reset_rpa_moudle() { trace!("esp_reset_rpa_moudle"); - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).reset_rpa(); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.reset_rpa(); } diff --git a/esp-wifi/src/ble/os_adapter_esp32c6.rs b/esp-wifi/src/ble/os_adapter_esp32c6.rs index 50b1127e058..49cc0c033cd 100644 --- a/esp-wifi/src/ble/os_adapter_esp32c6.rs +++ b/esp-wifi/src/ble/os_adapter_esp32c6.rs @@ -1,6 +1,5 @@ use crate::{ binary::include::esp_bt_controller_config_t, - common_adapter::RADIO_CLOCKS, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -66,9 +65,10 @@ pub(crate) static BLE_CONFIG: esp_bt_controller_config_t = esp_bt_controller_con }; pub(crate) fn bt_periph_module_enable() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Bt); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Bt); } pub(crate) fn disable_sleep_mode() { @@ -101,16 +101,18 @@ pub(super) unsafe extern "C" fn esp_intr_alloc( } pub(super) fn ble_rtc_clk_init() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).ble_rtc_clk_init(); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.ble_rtc_clk_init(); } pub(super) unsafe extern "C" fn esp_reset_rpa_moudle() { trace!("esp_reset_rpa_moudle"); - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).reset_rpa(); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.reset_rpa(); } #[allow(improper_ctypes_definitions)] diff --git a/esp-wifi/src/ble/os_adapter_esp32h2.rs b/esp-wifi/src/ble/os_adapter_esp32h2.rs index f0f8d49b1f1..5c310927023 100644 --- a/esp-wifi/src/ble/os_adapter_esp32h2.rs +++ b/esp-wifi/src/ble/os_adapter_esp32h2.rs @@ -1,6 +1,5 @@ use crate::{ binary::include::esp_bt_controller_config_t, - common_adapter::RADIO_CLOCKS, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -66,9 +65,10 @@ pub(crate) static BLE_CONFIG: esp_bt_controller_config_t = esp_bt_controller_con }; pub(crate) fn bt_periph_module_enable() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Bt); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Bt); } pub(crate) fn disable_sleep_mode() { @@ -101,16 +101,18 @@ pub(super) unsafe extern "C" fn esp_intr_alloc( } pub(super) fn ble_rtc_clk_init() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).ble_rtc_clk_init(); - } + // stealing RADIO_CLK is safe since it is passed (as reference or by value) into + // `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.ble_rtc_clk_init(); } pub(super) unsafe extern "C" fn esp_reset_rpa_moudle() { trace!("esp_reset_rpa_moudle"); - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).reset_rpa(); - } + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.reset_rpa(); } #[allow(improper_ctypes_definitions)] diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32.rs b/esp-wifi/src/common_adapter/common_adapter_esp32.rs index cdc16fa0434..82e215fbe9b 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, hal::{ prelude::ram, system::{RadioClockController, RadioPeripherals}, @@ -16,8 +15,8 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); -static mut PHY_CLOCK_ENABLE_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_CLOCK_ENABLE_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { unsafe { @@ -35,7 +34,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -123,9 +122,10 @@ pub(crate) unsafe fn phy_enable_clock() { let count = PHY_CLOCK_ENABLE_REF.fetch_add(1, Ordering::SeqCst); if count == 0 { - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); } } @@ -135,9 +135,10 @@ pub(crate) unsafe fn phy_disable_clock() { let count = PHY_CLOCK_ENABLE_REF.fetch_sub(1, Ordering::SeqCst); if count == 1 { - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); } } @@ -154,7 +155,6 @@ pub(crate) unsafe fn phy_disable_clock() { /// Register value /// /// ************************************************************************* - #[ram] #[no_mangle] unsafe extern "C" fn esp_dport_access_reg_read(reg: u32) -> u32 { @@ -209,8 +209,9 @@ unsafe extern "C" fn phy_exit_critical(level: u32) { #[ram] #[no_mangle] unsafe extern "C" fn rtc_get_xtal() -> u32 { - use crate::hal::clock::Clock; - let xtal = crate::hal::rtc_cntl::RtcClock::get_xtal_freq(); + use esp_hal::clock::Clock; + + let xtal = crate::hal::rtc_cntl::RtcClock::xtal_freq(); xtal.mhz() } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs index 53265bb42a2..99a8f74da20 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c2.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, compat::common::str_from_c, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -14,7 +13,7 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { // In esp-idf, neither SOC_PM_SUPPORT_MODEM_PD or SOC_PM_SUPPORT_WIFI_PD are @@ -23,7 +22,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -114,9 +113,10 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { trace!("phy_enable_clock"); - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } @@ -124,9 +124,10 @@ pub(crate) unsafe fn phy_enable_clock() { #[allow(unused)] pub(crate) unsafe fn phy_disable_clock() { trace!("phy_disable_clock"); - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs index b901b3423b0..eaa7903a1fe 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c3.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, compat::common::str_from_c, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -14,7 +13,7 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { const SYSTEM_WIFIBB_RST: u32 = 1 << 0; @@ -58,7 +57,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -77,7 +76,7 @@ pub(crate) unsafe fn phy_enable() { let init_data = &PHY_INIT_DATA_DEFAULT; - #[cfg(feature = "phy-enable-usb")] + #[cfg(phy_enable_usb)] { extern "C" { fn phy_bbpll_en_usb(param: bool); @@ -156,19 +155,20 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { trace!("phy_enable_clock"); - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } #[allow(unused)] pub(crate) unsafe fn phy_disable_clock() { trace!("phy_disable_clock"); - const SYSTEM_WIFI_CLK_EN_REG: u32 = 0x60026000 + 0x014; - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); - }); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs b/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs index 215879d7f6c..bd0e30cfe66 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32c6.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, compat::common::str_from_c, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -14,7 +13,7 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { // In esp-idf, SOC_PMU_SUPPORTED is set which makes @@ -23,7 +22,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -42,7 +41,7 @@ pub(crate) unsafe fn phy_enable() { let init_data = &PHY_INIT_DATA_DEFAULT; - #[cfg(feature = "phy-enable-usb")] + #[cfg(phy_enable_usb)] { extern "C" { fn phy_bbpll_en_usb(param: bool); @@ -120,13 +119,19 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { trace!("phy_enable_clock"); - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } #[allow(unused)] pub(crate) unsafe fn phy_disable_clock() { trace!("phy_disable_clock"); - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs b/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs index 8b2e4646d90..0186c2759fa 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32h2.rs @@ -4,7 +4,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, compat::common::str_from_c, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -15,7 +14,7 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { // In esp-idf, SOC_PMU_SUPPORTED is set which makes @@ -24,7 +23,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -45,7 +44,7 @@ pub(crate) unsafe fn phy_enable() { // this would cause linker errors when the feature is activated - see https://github.com/esp-rs/esp-wifi/issues/457 - // #[cfg(feature = "phy-enable-usb")] + // #[cfg(phy_enable_usb)] // { // extern "C" { // pub fn phy_bbpll_en_usb(param: bool); @@ -123,13 +122,19 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { trace!("phy_enable_clock"); - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } #[allow(unused)] pub(crate) unsafe fn phy_disable_clock() { trace!("phy_disable_clock"); - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32s2.rs b/esp-wifi/src/common_adapter/common_adapter_esp32s2.rs index a48a28870f6..b87536dbf75 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32s2.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32s2.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, hal::{ prelude::ram, system::{RadioClockController, RadioPeripherals}, @@ -16,8 +15,8 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); -static mut PHY_CLOCK_ENABLE_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_CLOCK_ENABLE_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn enable_wifi_power_domain() { const DPORT_WIFIBB_RST: u32 = 1 << 0; @@ -58,7 +57,7 @@ pub(crate) fn enable_wifi_power_domain() { pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -137,10 +136,10 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { let count = PHY_CLOCK_ENABLE_REF.fetch_add(1, Ordering::SeqCst); if count == 0 { - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); - }); - + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } } @@ -149,10 +148,10 @@ pub(crate) unsafe fn phy_enable_clock() { pub(crate) unsafe fn phy_disable_clock() { let count = PHY_CLOCK_ENABLE_REF.fetch_sub(1, Ordering::SeqCst); if count == 1 { - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); - }); - + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } } diff --git a/esp-wifi/src/common_adapter/common_adapter_esp32s3.rs b/esp-wifi/src/common_adapter/common_adapter_esp32s3.rs index 49b9aabc52f..f4213be87dd 100644 --- a/esp-wifi/src/common_adapter/common_adapter_esp32s3.rs +++ b/esp-wifi/src/common_adapter/common_adapter_esp32s3.rs @@ -3,7 +3,6 @@ use portable_atomic::{AtomicU32, Ordering}; use super::phy_init_data::PHY_INIT_DATA_DEFAULT; use crate::{ binary::include::*, - common_adapter::RADIO_CLOCKS, hal::system::{RadioClockController, RadioPeripherals}, }; @@ -13,11 +12,11 @@ static mut SOC_PHY_DIG_REGS_MEM: [u8; SOC_PHY_DIG_REGS_MEM_SIZE] = [0u8; SOC_PHY static mut G_IS_PHY_CALIBRATED: bool = false; static mut G_PHY_DIGITAL_REGS_MEM: *mut u32 = core::ptr::null_mut(); static mut S_IS_PHY_REG_STORED: bool = false; -static mut PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); +static PHY_ACCESS_REF: AtomicU32 = AtomicU32::new(0); pub(crate) fn phy_mem_init() { unsafe { - G_PHY_DIGITAL_REGS_MEM = SOC_PHY_DIG_REGS_MEM.as_ptr() as *mut u32; + G_PHY_DIGITAL_REGS_MEM = core::ptr::addr_of_mut!(SOC_PHY_DIG_REGS_MEM).cast(); } } @@ -73,7 +72,7 @@ pub(crate) unsafe fn phy_enable() { let init_data = &PHY_INIT_DATA_DEFAULT; - #[cfg(feature = "phy-enable-usb")] + #[cfg(phy_enable_usb)] { extern "C" { fn phy_bbpll_en_usb(param: bool); @@ -148,20 +147,20 @@ fn phy_digital_regs_store() { pub(crate) unsafe fn phy_enable_clock() { trace!("phy_enable_clock"); - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Phy); - }); - + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Phy); trace!("phy_enable_clock done!"); } #[allow(unused)] pub(crate) unsafe fn phy_disable_clock() { trace!("phy_disable_clock"); - critical_section::with(|_| { - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Phy); - }); - + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Phy); trace!("phy_disable_clock done!"); } diff --git a/esp-wifi/src/common_adapter/mod.rs b/esp-wifi/src/common_adapter/mod.rs index be2b5d52422..67bf299889e 100644 --- a/esp-wifi/src/common_adapter/mod.rs +++ b/esp-wifi/src/common_adapter/mod.rs @@ -1,7 +1,7 @@ use core::ptr::addr_of; use esp_wifi_sys::include::timeval; -use hal::{macros::ram, rng::Rng}; +use hal::macros::ram; use crate::{ binary::include::{esp_event_base_t, esp_timer_get_time}, @@ -27,22 +27,6 @@ pub(crate) mod chip_specific; #[cfg_attr(esp32s2, path = "phy_init_data_esp32s2.rs")] pub(crate) mod phy_init_data; -pub(crate) static mut RANDOM_GENERATOR: Option = None; - -pub(crate) static mut RADIO_CLOCKS: Option = None; - -pub(crate) fn init_rng(rng: Rng) { - unsafe { RANDOM_GENERATOR = Some(rng) }; -} - -pub(crate) fn init_radio_clock_control(rcc: hal::peripherals::RADIO_CLK) { - unsafe { RADIO_CLOCKS = Some(rcc) }; -} - -pub(crate) fn deinit_radio_clock_control() -> Option { - unsafe { RADIO_CLOCKS.take() } -} - /// ************************************************************************** /// Name: esp_semphr_create /// @@ -131,11 +115,9 @@ pub unsafe extern "C" fn semphr_give(semphr: *mut crate::binary::c_types::c_void pub unsafe extern "C" fn random() -> crate::binary::c_types::c_ulong { trace!("random"); - if let Some(ref mut rng) = RANDOM_GENERATOR { - rng.random() - } else { - 0 - } + // stealing RNG is safe since we own it (passed into `init`) + let mut rng = esp_hal::rng::Rng::new(unsafe { esp_hal::peripherals::RNG::steal() }); + rng.random() } /// ************************************************************************** @@ -155,7 +137,7 @@ pub unsafe extern "C" fn random() -> crate::binary::c_types::c_ulong { pub unsafe extern "C" fn read_mac(mac: *mut u8, type_: u32) -> crate::binary::c_types::c_int { trace!("read_mac {:?} {}", mac, type_); - let base_mac = crate::hal::efuse::Efuse::get_mac_address(); + let base_mac = crate::hal::efuse::Efuse::mac_address(); for (i, &byte) in base_mac.iter().enumerate() { mac.add(i).write_volatile(byte); @@ -308,11 +290,11 @@ pub unsafe extern "C" fn esp_fill_random(dst: *mut u8, len: u32) { trace!("esp_fill_random"); let dst = core::slice::from_raw_parts_mut(dst, len as usize); - if let Some(ref mut rng) = RANDOM_GENERATOR { - for chunk in dst.chunks_mut(4) { - let bytes = rng.random().to_le_bytes(); - chunk.copy_from_slice(&bytes[..chunk.len()]); - } + // stealing RNG is safe since we own it (passed into `init`) + let mut rng = esp_hal::rng::Rng::new(unsafe { esp_hal::peripherals::RNG::steal() }); + for chunk in dst.chunks_mut(4) { + let bytes = rng.random().to_le_bytes(); + chunk.copy_from_slice(&bytes[..chunk.len()]); } } diff --git a/esp-wifi/src/compat/common.rs b/esp-wifi/src/compat/common.rs index d8d29aa2245..784c0366ceb 100644 --- a/esp-wifi/src/compat/common.rs +++ b/esp-wifi/src/compat/common.rs @@ -1,9 +1,10 @@ #![allow(unused)] use core::{ + cell::RefCell, fmt::Write, mem::size_of_val, - ptr::{addr_of, addr_of_mut}, + ptr::{self, addr_of, addr_of_mut}, }; use esp_wifi_sys::include::malloc; @@ -25,10 +26,52 @@ struct Mutex { recursive: bool, } +pub(crate) struct ConcurrentQueue { + raw_queue: critical_section::Mutex>, +} + +impl ConcurrentQueue { + pub(crate) fn new(count: usize, item_size: usize) -> Self { + Self { + raw_queue: critical_section::Mutex::new(RefCell::new(RawQueue::new(count, item_size))), + } + } + + fn release_storage(&mut self) { + critical_section::with(|cs| unsafe { + self.raw_queue.borrow_ref_mut(cs).release_storage(); + }) + } + + pub(crate) fn enqueue(&mut self, item: *mut c_void) -> i32 { + critical_section::with(|cs| unsafe { self.raw_queue.borrow_ref_mut(cs).enqueue(item) }) + } + + pub(crate) fn try_dequeue(&mut self, item: *mut c_void) -> bool { + critical_section::with(|cs| unsafe { self.raw_queue.borrow_ref_mut(cs).try_dequeue(item) }) + } + + pub(crate) fn remove(&mut self, item: *mut c_void) { + critical_section::with(|cs| unsafe { self.raw_queue.borrow_ref_mut(cs).remove(item) }); + } + + pub(crate) fn count(&self) -> usize { + critical_section::with(|cs| unsafe { self.raw_queue.borrow_ref(cs).count() }) + } +} + +impl Drop for ConcurrentQueue { + fn drop(&mut self) { + self.release_storage(); + } +} + /// A naive and pretty much unsafe queue to back the queues used in drivers and -/// supplicant code -struct RawQueue { - count: usize, +/// supplicant code. +/// +/// The [ConcurrentQueue] wrapper should be used. +pub struct RawQueue { + capacity: usize, item_size: usize, current_read: usize, current_write: usize, @@ -36,10 +79,13 @@ struct RawQueue { } impl RawQueue { - fn new(count: usize, item_size: usize) -> Self { - let storage = unsafe { malloc((count * item_size) as u32) as *mut u8 }; + /// This allocates underlying storage. See [release_storage] + pub fn new(capacity: usize, item_size: usize) -> Self { + let storage = unsafe { malloc((capacity * item_size) as u32) as *mut u8 }; + assert!(!storage.is_null()); + Self { - count, + capacity, item_size, current_read: 0, current_write: 0, @@ -47,19 +93,20 @@ impl RawQueue { } } - fn free_storage(&mut self) { + /// Call `release_storage` to deallocate the underlying storage + unsafe fn release_storage(&mut self) { unsafe { free(self.storage); } self.storage = core::ptr::null_mut(); } - fn enqueue(&mut self, item: *mut c_void) -> i32 { - if self.count() < self.count { + unsafe fn enqueue(&mut self, item: *mut c_void) -> i32 { + if self.count() < self.capacity { unsafe { let p = self.storage.byte_add(self.item_size * self.current_write); p.copy_from(item as *mut u8, self.item_size); - self.current_write = (self.current_write + 1) % self.count; + self.current_write = (self.current_write + 1) % self.capacity; } 1 @@ -68,12 +115,12 @@ impl RawQueue { } } - fn try_dequeue(&mut self, item: *mut c_void) -> bool { + unsafe fn try_dequeue(&mut self, item: *mut c_void) -> bool { if self.count() > 0 { unsafe { let p = self.storage.byte_add(self.item_size * self.current_read) as *const c_void; item.copy_from(p, self.item_size); - self.current_read = (self.current_read + 1) % self.count; + self.current_read = (self.current_read + 1) % self.capacity; } true } else { @@ -81,11 +128,40 @@ impl RawQueue { } } - fn count(&self) -> usize { + unsafe fn remove(&mut self, item: *mut c_void) { + // do what the ESP-IDF implementations does ... + // just remove all elements and add them back except the one we need to remove - + // good enough for now + let item_slice = core::slice::from_raw_parts_mut(item as *mut u8, self.item_size); + let count = self.count(); + + if count == 0 { + return; + } + + let tmp_item = crate::compat::malloc::malloc(self.item_size); + + if tmp_item.is_null() { + panic!("Out of memory"); + } + + for _ in 0..count { + if self.try_dequeue(tmp_item as *mut c_void) { + let tmp_slice = core::slice::from_raw_parts_mut(tmp_item, self.item_size); + if tmp_slice != item_slice { + self.enqueue(tmp_item as *mut c_void); + } + } + } + + crate::compat::malloc::free(tmp_item); + } + + unsafe fn count(&self) -> usize { if self.current_write >= self.current_read { self.current_write - self.current_read } else { - self.count - self.current_read + self.current_write + self.capacity - self.current_read + self.current_write } } } @@ -131,7 +207,7 @@ pub(crate) fn sem_take(semphr: *mut c_void, tick: u32) -> i32 { let forever = tick == OSI_FUNCS_TIME_BLOCKING; let timeout = tick as u64; - let start = crate::timer::get_systimer_count(); + let start = crate::timer::systimer_count(); let sem = semphr as *mut u32; @@ -248,31 +324,34 @@ pub(crate) fn unlock_mutex(mutex: *mut c_void) -> i32 { }) } -pub(crate) fn create_queue(queue_len: c_int, item_size: c_int) -> *mut c_void { +pub(crate) fn create_queue(queue_len: c_int, item_size: c_int) -> *mut ConcurrentQueue { trace!("wifi_create_queue len={} size={}", queue_len, item_size,); - let queue = RawQueue::new(queue_len as usize, item_size as usize); - let ptr = unsafe { malloc(size_of_val(&queue) as u32) as *mut RawQueue }; + let queue = ConcurrentQueue::new(queue_len as usize, item_size as usize); + let ptr = unsafe { malloc(size_of_val(&queue) as u32) as *mut ConcurrentQueue }; unsafe { ptr.write(queue); } trace!("created queue @{:?}", ptr); - ptr.cast() + ptr } -pub(crate) fn delete_queue(queue: *mut c_void) { +pub(crate) fn delete_queue(queue: *mut ConcurrentQueue) { trace!("delete_queue {:?}", queue); - let queue: *mut RawQueue = queue.cast(); unsafe { - (*queue).free_storage(); - free(queue.cast()); + ptr::drop_in_place(queue); + crate::compat::malloc::free(queue.cast()); } } -pub(crate) fn send_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u32) -> i32 { +pub(crate) fn send_queued( + queue: *mut ConcurrentQueue, + item: *mut c_void, + block_time_tick: u32, +) -> i32 { trace!( "queue_send queue {:?} item {:x} block_time_tick {}", queue, @@ -280,11 +359,15 @@ pub(crate) fn send_queued(queue: *mut c_void, item: *mut c_void, block_time_tick block_time_tick ); - let queue: *mut RawQueue = queue.cast(); - critical_section::with(|_| unsafe { (*queue).enqueue(item) }) + let queue: *mut ConcurrentQueue = queue.cast(); + unsafe { (*queue).enqueue(item) } } -pub(crate) fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_tick: u32) -> i32 { +pub(crate) fn receive_queued( + queue: *mut ConcurrentQueue, + item: *mut c_void, + block_time_tick: u32, +) -> i32 { trace!( "queue_recv {:?} item {:?} block_time_tick {}", queue, @@ -294,12 +377,10 @@ pub(crate) fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_t let forever = block_time_tick == OSI_FUNCS_TIME_BLOCKING; let timeout = block_time_tick as u64; - let start = crate::timer::get_systimer_count(); - - let queue: *mut RawQueue = queue.cast(); + let start = crate::timer::systimer_count(); loop { - if critical_section::with(|_| unsafe { (*queue).try_dequeue(item) }) { + if unsafe { (*queue).try_dequeue(item) } { trace!("received"); return 1; } @@ -313,11 +394,11 @@ pub(crate) fn receive_queued(queue: *mut c_void, item: *mut c_void, block_time_t } } -pub(crate) fn number_of_messages_in_queue(queue: *const c_void) -> u32 { +pub(crate) fn number_of_messages_in_queue(queue: *const ConcurrentQueue) -> u32 { trace!("queue_msg_waiting {:?}", queue); - let queue: *const RawQueue = queue.cast(); - critical_section::with(|_| unsafe { (*queue).count() as u32 }) + let queue: *const ConcurrentQueue = queue.cast(); + unsafe { (*queue).count() as u32 } } /// Implementation of sleep() from newlib in esp-idf. diff --git a/esp-wifi/src/compat/misc.rs b/esp-wifi/src/compat/misc.rs index 91035ffe268..8e586cb2cdc 100644 --- a/esp-wifi/src/compat/misc.rs +++ b/esp-wifi/src/compat/misc.rs @@ -1,4 +1,4 @@ -use crate::compat::malloc::*; +// these are not called but needed for linking #[no_mangle] unsafe extern "C" fn fwrite(ptr: *const (), size: usize, count: usize, stream: *const ()) -> usize { @@ -20,140 +20,8 @@ unsafe extern "C" fn fclose(stream: *const ()) -> i32 { todo!("fclose {:?}", stream); } -#[no_mangle] -unsafe extern "C" fn strcat(destination: *mut u8, source: *const u8) -> *const u8 { - trace!("strcat {:?} {:?}", destination, source); - - let dst: *mut u8 = strchr(destination.cast(), 0) as *mut u8; - let len = strchr(source.cast(), 0) as usize - source as usize; - core::ptr::copy(source, dst, len); - destination -} - -#[no_mangle] -unsafe extern "C" fn strcmp(str1: *const i8, str2: *const i8) -> i32 { - trace!("strcmp {:?} {:?}", str1, str2); - - let s1 = core::ffi::CStr::from_ptr(str1).to_str().unwrap(); - let s2 = core::ffi::CStr::from_ptr(str2).to_str().unwrap(); - - let x = s1.cmp(s2); - - match x { - core::cmp::Ordering::Less => -1, - core::cmp::Ordering::Equal => 0, - core::cmp::Ordering::Greater => 1, - } -} - -#[cfg(feature = "have-strchr")] -extern "C" { - fn strchr(str: *const i8, c: i32) -> *const i8; -} - -#[cfg(not(feature = "have-strchr"))] -#[no_mangle] -unsafe extern "C" fn strchr(str: *const i8, c: i32) -> *const i8 { - trace!("strchr {:?} {}", str, c); - - unsafe { - let mut p = str; - loop { - if *p == c as i8 { - return p; - } - - if *p == 0 { - return core::ptr::null(); - } - - p = p.add(1); - } - } -} - -#[no_mangle] -unsafe extern "C" fn strlcpy(dst: *mut u8, src: *const u8, size: usize) -> usize { - trace!("strlcpy {:?} {:?} {}", dst, src, size); - - let mut dst = dst; - let mut src = src; - let mut cnt = 0; - loop { - dst.write_volatile(0); - - let c = src.read_volatile(); - - if c == 0 || cnt >= size { - break; - } - - dst.write_volatile(c); - dst = dst.add(1); - src = src.add(1); - cnt += 1; - } - - cnt -} - -#[no_mangle] -unsafe extern "C" fn strstr(str1: *const i8, str2: *const i8) -> *const i8 { - trace!("strstr {:?} {:?}", str1, str2); - - let s1 = core::ffi::CStr::from_ptr(str1).to_str().unwrap(); - let s2 = core::ffi::CStr::from_ptr(str2).to_str().unwrap(); - - let idx = s1.find(s2); - - match idx { - Some(offset) => str1.add(offset), - None => core::ptr::null(), - } -} - -#[no_mangle] -unsafe extern "C" fn strcasecmp(str1: *const u8, str2: *const u8) -> i32 { - trace!("strcasecmp {:?} {:?}", str1, str2); - - let mut str1 = str1; - let mut str2 = str2; - - let mut c1 = *str1 as char; - let mut c2 = *str2 as char; - - while c1 != '\0' && c2 != '\0' { - c1 = c1.to_ascii_lowercase(); - c2 = c2.to_ascii_lowercase(); - - if c1 != c2 { - return c1 as i32 - c2 as i32; - } - - str1 = str1.add(1); - str2 = str2.add(1); - - c1 = *str1 as char; - c2 = *str2 as char; - } - - c1 as i32 - c2 as i32 -} - -#[no_mangle] -unsafe extern "C" fn strdup(str: *const i8) -> *const u8 { - trace!("strdup {:?}", str); - - unsafe { - let s = core::ffi::CStr::from_ptr(str); - let s = s.to_str().unwrap(); - - let p = malloc(s.len() + 1); - core::ptr::copy_nonoverlapping(str, p as *mut i8, s.len() + 1); - p as *const u8 - } -} - +// not available in ROM on ESP32-S2 +#[cfg(feature = "esp32s2")] #[no_mangle] unsafe extern "C" fn atoi(str: *const i8) -> i32 { trace!("atoi {:?}", str); diff --git a/esp-wifi/src/compat/mod.rs b/esp-wifi/src/compat/mod.rs index 25e6d6a1ccb..dc68d68e125 100644 --- a/esp-wifi/src/compat/mod.rs +++ b/esp-wifi/src/compat/mod.rs @@ -3,16 +3,13 @@ pub mod malloc; pub mod misc; pub mod timer_compat; -pub mod queue { - pub use heapless::spsc::Queue as SimpleQueue; -} - #[no_mangle] unsafe extern "C" fn _putchar(c: u8) { static mut BUFFER: [u8; 256] = [0u8; 256]; static mut IDX: usize = 0; - if c == 0 || c == b'\n' || IDX == BUFFER.len() - 1 { + let buffer = core::ptr::addr_of_mut!(BUFFER); + if c == 0 || c == b'\n' || IDX == (*buffer).len() - 1 { if c != 0 { BUFFER[IDX] = c; } else { diff --git a/esp-wifi/src/compat/timer_compat.rs b/esp-wifi/src/compat/timer_compat.rs index b44cade7625..62204f12bea 100644 --- a/esp-wifi/src/compat/timer_compat.rs +++ b/esp-wifi/src/compat/timer_compat.rs @@ -1,3 +1,8 @@ +use alloc::boxed::Box; +use core::cell::RefCell; + +use critical_section::Mutex; + use crate::binary::{ c_types, include::{esp_timer_create_args_t, ets_timer}, @@ -25,7 +30,8 @@ impl From<&esp_timer_create_args_t> for TimerCallback { } } -#[derive(Debug, Clone, Copy)] +#[repr(C)] +#[derive(Debug, Clone)] pub(crate) struct Timer { pub ets_timer: *mut ets_timer, pub started: u64, @@ -33,6 +39,8 @@ pub(crate) struct Timer { pub active: bool, pub periodic: bool, pub callback: TimerCallback, + + next: Option>, } impl Timer { @@ -40,17 +48,104 @@ impl Timer { self.ets_timer as usize } } -#[cfg(coex)] -pub(crate) static mut TIMERS: heapless::Vec = heapless::Vec::new(); -#[cfg(not(coex))] -pub(crate) static mut TIMERS: heapless::Vec = heapless::Vec::new(); + +pub(crate) struct TimerQueue { + head: Option>, +} + +impl TimerQueue { + const fn new() -> Self { + Self { head: None } + } + + fn find(&mut self, ets_timer: *mut ets_timer) -> Option<&mut Box> { + let mut current = self.head.as_mut(); + while let Some(timer) = current { + if timer.ets_timer == ets_timer { + return Some(timer); + } + current = timer.next.as_mut(); + } + + None + } + + pub(crate) unsafe fn find_next_due( + &mut self, + current_timestamp: u64, + ) -> Option<&mut Box> { + let mut current = self.head.as_mut(); + while let Some(timer) = current { + if timer.active + && crate::timer::time_diff(timer.started, current_timestamp) >= timer.timeout + { + return Some(timer); + } + current = timer.next.as_mut(); + } + + None + } + + fn remove(&mut self, ets_timer: *mut ets_timer) { + if let Some(head) = self.head.as_mut() { + if head.ets_timer == ets_timer { + self.head = head.next.take(); + return; + } + } + + let timer = self.find(ets_timer); + if let Some(to_remove) = timer { + let tail = to_remove.next.take(); + + let mut current = self.head.as_mut(); + let before = { + let mut found = None; + while let Some(before) = current { + if before.next.as_mut().unwrap().ets_timer == ets_timer { + found = Some(before); + break; + } + current = before.next.as_mut(); + } + found + }; + + if let Some(before) = before { + before.next = tail; + } + } + } + + fn push(&mut self, to_add: Box) -> Result<(), ()> { + if self.head.is_none() { + self.head = Some(to_add); + return Ok(()); + } + + let mut current = self.head.as_mut(); + while let Some(timer) = current { + if timer.next.is_none() { + timer.next = Some(to_add); + break; + } + current = timer.next.as_mut(); + } + Ok(()) + } +} + +unsafe impl Send for TimerQueue {} + +pub(crate) static TIMERS: Mutex> = Mutex::new(RefCell::new(TimerQueue::new())); pub(crate) fn compat_timer_arm(ets_timer: *mut ets_timer, tmout: u32, repeat: bool) { compat_timer_arm_us(ets_timer, tmout * 1000, repeat); } pub(crate) fn compat_timer_arm_us(ets_timer: *mut ets_timer, us: u32, repeat: bool) { - let systick = crate::timer::get_systimer_count(); + let systick = crate::timer::systimer_count(); let ticks = crate::timer::micros_to_ticks(us as u64); trace!( @@ -61,8 +156,8 @@ pub(crate) fn compat_timer_arm_us(ets_timer: *mut ets_timer, us: u32, repeat: bo repeat ); - critical_section::with(|_| unsafe { - if let Some(timer) = TIMERS.iter_mut().find(|t| t.ets_timer == ets_timer) { + critical_section::with(|cs| { + if let Some(timer) = TIMERS.borrow_ref_mut(cs).find(ets_timer) { timer.started = systick; timer.timeout = ticks; timer.active = true; @@ -73,9 +168,10 @@ pub(crate) fn compat_timer_arm_us(ets_timer: *mut ets_timer, us: u32, repeat: bo }) } -pub(crate) fn compat_timer_disarm(ets_timer: *mut ets_timer) { - critical_section::with(|_| unsafe { - if let Some(timer) = TIMERS.iter_mut().find(|t| t.ets_timer == ets_timer) { +pub fn compat_timer_disarm(ets_timer: *mut ets_timer) { + trace!("timer disarm"); + critical_section::with(|cs| { + if let Some(timer) = TIMERS.borrow_ref_mut(cs).find(ets_timer) { trace!("timer_disarm {:x}", timer.id()); timer.active = false; } else { @@ -84,20 +180,20 @@ pub(crate) fn compat_timer_disarm(ets_timer: *mut ets_timer) { }) } -pub(crate) fn compat_timer_done(ets_timer: *mut ets_timer) { - critical_section::with(|_| unsafe { - if let Some((idx, timer)) = TIMERS - .iter_mut() - .enumerate() - .find(|(_, t)| t.ets_timer == ets_timer) - { +pub fn compat_timer_done(ets_timer: *mut ets_timer) { + trace!("timer done"); + critical_section::with(|cs| { + let mut timers = TIMERS.borrow_ref_mut(cs); + if let Some(timer) = timers.find(ets_timer) { trace!("timer_done {:x}", timer.id()); timer.active = false; - (*ets_timer).priv_ = core::ptr::null_mut(); - (*ets_timer).expire = 0; + unsafe { + (*ets_timer).priv_ = core::ptr::null_mut(); + (*ets_timer).expire = 0; + } - TIMERS.swap_remove(idx); + timers.remove(ets_timer); } else { trace!("timer_done {:x} not found", ets_timer as usize); } @@ -115,9 +211,9 @@ pub(crate) fn compat_timer_setfn( pfunction, parg ); - - let set = critical_section::with(|_| unsafe { - if let Some(timer) = TIMERS.iter_mut().find(|t| t.ets_timer == ets_timer) { + let set = critical_section::with(|cs| unsafe { + let mut timers = TIMERS.borrow_ref_mut(cs); + if let Some(timer) = timers.find(ets_timer) { timer.callback = TimerCallback::new(pfunction, parg); timer.active = false; @@ -129,16 +225,18 @@ pub(crate) fn compat_timer_setfn( (*ets_timer).period = 0; (*ets_timer).func = None; (*ets_timer).priv_ = core::ptr::null_mut(); - TIMERS - .push(Timer { - ets_timer, - started: 0, - timeout: 0, - active: false, - periodic: false, - callback: TimerCallback::new(pfunction, parg), - }) - .is_ok() + + let timer = + crate::compat::malloc::calloc(1, core::mem::size_of::()) as *mut Timer; + (*timer).next = None; + (*timer).ets_timer = ets_timer; + (*timer).started = 0; + (*timer).timeout = 0; + (*timer).active = false; + (*timer).periodic = false; + (*timer).callback = TimerCallback::new(pfunction, parg); + + timers.push(Box::from_raw(timer)).is_ok() } }); diff --git a/esp-wifi/src/config.rs b/esp-wifi/src/config.rs new file mode 100644 index 00000000000..0b9c77fecb4 --- /dev/null +++ b/esp-wifi/src/config.rs @@ -0,0 +1,48 @@ +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Tunable parameters for the WiFi driver +#[allow(unused)] // currently there are no ble tunables +pub(crate) struct EspWifiConfig { + pub(crate) rx_queue_size: usize, + pub(crate) tx_queue_size: usize, + pub(crate) static_rx_buf_num: usize, + pub(crate) dynamic_rx_buf_num: usize, + pub(crate) static_tx_buf_num: usize, + pub(crate) dynamic_tx_buf_num: usize, + pub(crate) csi_enable: bool, + pub(crate) ampdu_rx_enable: bool, + pub(crate) ampdu_tx_enable: bool, + pub(crate) amsdu_tx_enable: bool, + pub(crate) rx_ba_win: usize, + pub(crate) max_burst_size: usize, + pub(crate) country_code: &'static str, + pub(crate) country_code_operating_class: u8, + pub(crate) mtu: usize, + pub(crate) tick_rate_hz: u32, + pub(crate) listen_interval: u16, + pub(crate) beacon_timeout: u16, + pub(crate) ap_beacon_timeout: u16, + pub(crate) failure_retry_cnt: u8, + pub(crate) scan_method: u32, +} + +#[cfg(not(coex))] +#[non_exhaustive] +#[derive(Default)] +pub enum PowerSaveMode { + None, + #[default] + Minimum, + Maximum, +} + +#[cfg(not(coex))] +impl From for esp_wifi_sys::include::wifi_ps_type_t { + fn from(s: PowerSaveMode) -> Self { + match s { + PowerSaveMode::None => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_NONE, + PowerSaveMode::Minimum => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM, + PowerSaveMode::Maximum => esp_wifi_sys::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM, + } + } +} diff --git a/esp-wifi/src/esp_now/mod.rs b/esp-wifi/src/esp_now/mod.rs index 5099fe78cd4..4ee12c909e1 100644 --- a/esp-wifi/src/esp_now/mod.rs +++ b/esp-wifi/src/esp_now/mod.rs @@ -9,28 +9,36 @@ //! //! For more information see https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html +use alloc::{boxed::Box, collections::vec_deque::VecDeque}; use core::{cell::RefCell, fmt::Debug, marker::PhantomData}; use critical_section::Mutex; use enumset::EnumSet; use portable_atomic::{AtomicBool, AtomicU8, Ordering}; +#[cfg(not(coex))] +use crate::config::PowerSaveMode; +#[cfg(csi_enable)] +use crate::wifi::CsiConfig; use crate::{ binary::include::*, - compat::queue::SimpleQueue, hal::peripheral::{Peripheral, PeripheralRef}, - wifi::{Protocol, RxControlInfo}, - EspWifiInitialization, + wifi::{Protocol, RxControlInfo, WifiError}, + EspWifiController, }; +const RECEIVE_QUEUE_SIZE: usize = 10; + /// Maximum payload length pub const ESP_NOW_MAX_DATA_LEN: usize = 250; /// Broadcast address pub const BROADCAST_ADDRESS: [u8; 6] = [0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8]; -static RECEIVE_QUEUE: Mutex>> = - Mutex::new(RefCell::new(SimpleQueue::new())); +// Stores received packets until dequeued by the user +static RECEIVE_QUEUE: Mutex>> = + Mutex::new(RefCell::new(VecDeque::new())); + /// This atomic behaves like a guard, so we need strict memory ordering when /// operating it. /// @@ -109,6 +117,14 @@ pub enum EspNowError { SendFailed, /// Attempt to create `EspNow` instance twice. DuplicateInstance, + /// Initialization error + Initialization(WifiError), +} + +impl From for EspNowError { + fn from(f: WifiError) -> Self { + Self::Initialization(f) + } } /// Holds the count of peers in an ESP-NOW communication context. @@ -231,30 +247,30 @@ pub struct ReceiveInfo { /// Stores information about the received data, including the packet content and /// associated information. -#[derive(Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone)] pub struct ReceivedData { - /// The length of the received data. - pub len: u8, - - /// The buffer containing the received data. - pub data: [u8; 256], - - /// Information about a received packet. + data: Box<[u8]>, pub info: ReceiveInfo, } impl ReceivedData { /// Returns the received payload. - pub fn get_data(&self) -> &[u8] { - &self.data[..self.len as usize] + pub fn data(&self) -> &[u8] { + &self.data + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ReceivedData { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!(fmt, "ReceivedData {}, Info {}", &self.data[..], &self.info,) } } impl Debug for ReceivedData { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ReceivedData") - .field("data", &self.get_data()) + .field("data", &self.data()) .field("info", &self.info) .finish() } @@ -277,7 +293,7 @@ pub struct EspNowManager<'d> { _rc: EspNowRc<'d>, } -impl<'d> EspNowManager<'d> { +impl EspNowManager<'_> { /// Set the wifi protocol. /// /// This will set the wifi protocol to the desired protocol @@ -323,6 +339,12 @@ impl<'d> EspNowManager<'d> { Ok(()) } + #[cfg(not(coex))] + /// Configures modem power saving + pub fn set_power_saving(&self, ps: PowerSaveMode) -> Result<(), WifiError> { + crate::wifi::apply_power_saving(ps) + } + /// Set primary WiFi channel. /// Should only be used when using ESP-NOW without AP or STA. pub fn set_channel(&self, channel: u8) -> Result<(), EspNowError> { @@ -330,7 +352,7 @@ impl<'d> EspNowManager<'d> { } /// Get the version of ESP-NOW. - pub fn get_version(&self) -> Result { + pub fn version(&self) -> Result { let mut version = 0u32; check_error!({ esp_now_get_version(&mut version as *mut u32) })?; Ok(version) @@ -349,6 +371,20 @@ impl<'d> EspNowManager<'d> { check_error!({ esp_now_add_peer(&raw_peer as *const _) }) } + /// Set CSI configuration and register the receiving callback. + #[cfg(csi_enable)] + pub fn set_csi( + &mut self, + mut csi: CsiConfig, + cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send, + ) -> Result<(), WifiError> { + csi.apply_config()?; + csi.set_receive_cb(cb)?; + csi.set_csi(true)?; + + Ok(()) + } + /// Remove the given peer. pub fn remove_peer(&self, peer_address: &[u8; 6]) -> Result<(), EspNowError> { check_error!({ esp_now_del_peer(peer_address.as_ptr()) }) @@ -368,7 +404,7 @@ impl<'d> EspNowManager<'d> { } /// Get peer by MAC address. - pub fn get_peer(&self, peer_address: &[u8; 6]) -> Result { + pub fn peer(&self, peer_address: &[u8; 6]) -> Result { let mut raw_peer = esp_now_peer_info_t { peer_addr: [0u8; 6], lmk: [0u8; 16], @@ -465,6 +501,21 @@ impl<'d> EspNowManager<'d> { } } +impl<'d> Drop for EspNowManager<'d> { + fn drop(&mut self) { + if unwrap!( + crate::flags::WIFI.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + Some(x.saturating_sub(1)) + }) + ) == 0 + { + if let Err(e) = crate::wifi::wifi_deinit() { + warn!("Failed to cleanly deinit wifi: {:?}", e); + } + } + } +} + /// This is the sender part of ESP-NOW. You can get this sender by splitting /// a `EspNow` instance. /// @@ -476,7 +527,7 @@ pub struct EspNowSender<'d> { _rc: EspNowRc<'d>, } -impl<'d> EspNowSender<'d> { +impl EspNowSender<'_> { /// Send data to peer /// /// The peer needs to be added to the peer list first. @@ -506,7 +557,7 @@ impl<'d> EspNowSender<'d> { #[must_use] pub struct SendWaiter<'s>(PhantomData<&'s mut EspNowSender<'s>>); -impl<'s> SendWaiter<'s> { +impl SendWaiter<'_> { /// Wait for the previous sending to complete, i.e. the send callback is /// invoked with status of the sending. pub fn wait(self) -> Result<(), EspNowError> { @@ -523,7 +574,7 @@ impl<'s> SendWaiter<'s> { } } -impl<'s> Drop for SendWaiter<'s> { +impl Drop for SendWaiter<'_> { /// wait for the send to complete to prevent the lock on `EspNowSender` get /// unlocked before a callback is invoked. fn drop(&mut self) { @@ -537,12 +588,12 @@ pub struct EspNowReceiver<'d> { _rc: EspNowRc<'d>, } -impl<'d> EspNowReceiver<'d> { +impl EspNowReceiver<'_> { /// Receives data from the ESP-NOW queue. pub fn receive(&self) -> Option { critical_section::with(|cs| { let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs); - queue.dequeue() + queue.pop_front() }) } } @@ -554,7 +605,7 @@ struct EspNowRc<'d> { inner: PhantomData>, } -impl<'d> EspNowRc<'d> { +impl EspNowRc<'_> { fn new() -> Result { static ESP_NOW_RC: AtomicU8 = AtomicU8::new(0); // The reference counter is not 0, which means there is another instance of @@ -570,7 +621,7 @@ impl<'d> EspNowRc<'d> { } } -impl<'d> Clone for EspNowRc<'d> { +impl Clone for EspNowRc<'_> { fn clone(&self) -> Self { self.rc.fetch_add(1, Ordering::Release); Self { @@ -580,7 +631,7 @@ impl<'d> Clone for EspNowRc<'d> { } } -impl<'d> Drop for EspNowRc<'d> { +impl Drop for EspNowRc<'_> { fn drop(&mut self) { if self.rc.fetch_sub(1, Ordering::AcqRel) == 1 { unsafe { @@ -603,16 +654,16 @@ impl<'d> Drop for EspNowRc<'d> { /// Currently this implementation (when used together with traditional Wi-Fi) /// ONLY support STA mode. pub struct EspNow<'d> { - _device: Option>, manager: EspNowManager<'d>, sender: EspNowSender<'d>, receiver: EspNowReceiver<'d>, + _phantom: PhantomData<&'d ()>, } impl<'d> EspNow<'d> { /// Creates an `EspNow` instance. pub fn new( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, device: impl Peripheral

+ 'd, ) -> Result, EspNowError> { EspNow::new_internal(inited, Some(device.into_ref())) @@ -620,7 +671,7 @@ impl<'d> EspNow<'d> { /// Creates an `EspNow` instance with support for Wi-Fi coexistence. pub fn new_with_wifi( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, _token: EspNowWithWifiCreateToken, ) -> Result, EspNowError> { EspNow::new_internal( @@ -630,16 +681,17 @@ impl<'d> EspNow<'d> { } fn new_internal( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, device: Option>, ) -> Result, EspNowError> { - if !inited.is_wifi() { - return Err(EspNowError::Error(Error::NotInitialized)); + if !inited.wifi() { + // if wifi isn't already enabled, and we try to coexist - panic + assert!(device.is_some()); + crate::wifi::wifi_init()?; } let espnow_rc = EspNowRc::new()?; let esp_now = EspNow { - _device: device, manager: EspNowManager { _rc: espnow_rc.clone(), }, @@ -647,31 +699,13 @@ impl<'d> EspNow<'d> { _rc: espnow_rc.clone(), }, receiver: EspNowReceiver { _rc: espnow_rc }, + _phantom: PhantomData, }; check_error!({ esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_STA) })?; check_error!({ esp_wifi_start() })?; check_error!({ esp_wifi_set_inactive_time(wifi_interface_t_WIFI_IF_STA, crate::CONFIG.beacon_timeout) })?; - cfg_if::cfg_if! { - if #[cfg(feature = "ps-min-modem")] { - check_error!({esp_wifi_set_ps( - crate::binary::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM - )})?; - } else if #[cfg(feature = "ps-max-modem")] { - check_error!({esp_wifi_set_ps( - crate::binary::include::wifi_ps_type_t_WIFI_PS_MAX_MODEM - )})?; - } else if #[cfg(coex)] { - check_error!({esp_wifi_set_ps( - crate::binary::include::wifi_ps_type_t_WIFI_PS_MIN_MODEM - )})?; - } else { - check_error!({esp_wifi_set_ps( - crate::binary::include::wifi_ps_type_t_WIFI_PS_NONE - )})?; - } - }; check_error!({ esp_now_init() })?; check_error!({ esp_now_register_recv_cb(Some(rcv_cb)) })?; check_error!({ esp_now_register_send_cb(Some(send_cb)) })?; @@ -710,8 +744,8 @@ impl<'d> EspNow<'d> { } /// Get the version of ESP-NOW. - pub fn get_version(&self) -> Result { - self.manager.get_version() + pub fn version(&self) -> Result { + self.manager.version() } /// Add a peer to the list of known peers. @@ -730,8 +764,8 @@ impl<'d> EspNow<'d> { } /// Get peer by MAC address. - pub fn get_peer(&self, peer_address: &[u8; 6]) -> Result { - self.manager.get_peer(peer_address) + pub fn peer(&self, peer_address: &[u8; 6]) -> Result { + self.manager.peer(peer_address) } /// Fetch a peer from peer list. @@ -795,7 +829,6 @@ unsafe extern "C" fn send_cb(_mac_addr: *const u8, status: esp_now_send_status_t ESP_NOW_SEND_CB_INVOKED.store(true, Ordering::Release); - #[cfg(feature = "async")] asynch::ESP_NOW_TX_WAKER.wake(); }) } @@ -834,28 +867,20 @@ unsafe extern "C" fn rcv_cb( let slice = core::slice::from_raw_parts(data, data_len as usize); critical_section::with(|cs| { let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs); - let mut data = [0u8; 256]; - data[..slice.len()].copy_from_slice(slice); + let data = Box::from(slice); - if queue.is_full() { - queue.dequeue(); + if queue.len() >= RECEIVE_QUEUE_SIZE { + queue.pop_front(); } - unwrap!(queue.enqueue(ReceivedData { - len: slice.len() as u8, - data, - info, - })); + queue.push_back(ReceivedData { data, info }); - #[cfg(feature = "async")] asynch::ESP_NOW_RX_WAKER.wake(); }); } -#[cfg(feature = "async")] pub use asynch::SendFuture; -#[cfg(feature = "async")] mod asynch { use core::task::{Context, Poll}; @@ -866,7 +891,7 @@ mod asynch { pub(super) static ESP_NOW_TX_WAKER: AtomicWaker = AtomicWaker::new(); pub(super) static ESP_NOW_RX_WAKER: AtomicWaker = AtomicWaker::new(); - impl<'d> EspNowReceiver<'d> { + impl EspNowReceiver<'_> { /// This function takes mutable reference to self because the /// implementation of `ReceiveFuture` is not logically thread /// safe. @@ -875,7 +900,7 @@ mod asynch { } } - impl<'d> EspNowSender<'d> { + impl EspNowSender<'_> { /// Sends data asynchronously to a peer (using its MAC) using ESP-NOW. pub fn send_async<'s, 'r>( &'s mut self, @@ -891,7 +916,7 @@ mod asynch { } } - impl<'d> EspNow<'d> { + impl EspNow<'_> { /// This function takes mutable reference to self because the /// implementation of `ReceiveFuture` is not logically thread /// safe. @@ -920,7 +945,7 @@ mod asynch { sent: bool, } - impl<'s, 'r> core::future::Future for SendFuture<'s, 'r> { + impl core::future::Future for SendFuture<'_, '_> { type Output = Result<(), EspNowError>; fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -953,7 +978,7 @@ mod asynch { #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ReceiveFuture<'r>(PhantomData<&'r mut EspNowReceiver<'r>>); - impl<'r> core::future::Future for ReceiveFuture<'r> { + impl core::future::Future for ReceiveFuture<'_> { type Output = ReceivedData; fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -961,7 +986,7 @@ mod asynch { if let Some(data) = critical_section::with(|cs| { let mut queue = RECEIVE_QUEUE.borrow_ref_mut(cs); - queue.dequeue() + queue.pop_front() }) { Poll::Ready(data) } else { diff --git a/esp-wifi/src/lib.rs b/esp-wifi/src/lib.rs index 55d60a9c3fa..d370bdd707f 100644 --- a/esp-wifi/src/lib.rs +++ b/esp-wifi/src/lib.rs @@ -48,19 +48,12 @@ //! Within this crate, `CCOMPARE0` CPU timer is used for timing, ensure that in //! your application you are not using this CPU timer. //! -//! ## USB-SERIAL-JTAG -//! -//! When using USB-SERIAL-JTAG (for example by selecting `jtag-serial` in [`esp-println`](https://crates.io/crates/esp-println)) you have to activate the feature `phy-enable-usb`. -//! -//! Don't use this feature if you are _not_ using USB-SERIAL-JTAG as it might -//! reduce WiFi performance. -//! //! # Features flags //! //! Note that not all features are available on every MCU. For example, `ble` //! (and thus, `coex`) is not available on ESP32-S2. //! -//! When using the `dump-packets` feature you can use the extcap in +//! When using the `dump_packets` config you can use the extcap in //! `extras/esp-wifishark` to analyze the frames in Wireshark. //! For more information see //! [extras/esp-wifishark/README.md](../extras/esp-wifishark/README.md) @@ -93,32 +86,26 @@ extern crate alloc; // MUST be the first module mod fmt; -use common_adapter::{ - chip_specific::phy_mem_init, - deinit_radio_clock_control, - init_radio_clock_control, - RADIO_CLOCKS, -}; +use core::marker::PhantomData; + +use common_adapter::chip_specific::phy_mem_init; use esp_config::*; use esp_hal as hal; +use esp_hal::peripheral::Peripheral; #[cfg(not(feature = "esp32"))] use esp_hal::timer::systimer::Alarm; use fugit::MegahertzU32; use hal::{ clock::Clocks, + rng::{Rng, Trng}, system::RadioClockController, timer::{timg::Timer as TimgTimer, AnyTimer, PeriodicTimer}, }; -#[cfg(feature = "wifi")] -use num_traits::FromPrimitive; +use portable_atomic::Ordering; #[cfg(feature = "wifi")] +use crate::wifi::WifiError; use crate::{ - binary::include::{self, esp_supplicant_deinit, esp_wifi_deinit_internal, esp_wifi_stop}, - wifi::WifiError, -}; -use crate::{ - common_adapter::init_rng, tasks::init_tasks, timer::{setup_timer_isr, shutdown_timer_isr}, }; @@ -140,6 +127,8 @@ pub mod ble; #[cfg(feature = "esp-now")] pub mod esp_now; +pub mod config; + pub(crate) mod common_adapter; #[doc(hidden)] @@ -147,17 +136,6 @@ pub mod tasks; pub(crate) mod memory_fence; -#[cfg(all(feature = "wifi", any(feature = "tcp", feature = "udp")))] -pub mod wifi_interface; - -// [esp_hal::time::now()] as a smoltcp [`Instant]` -#[cfg(feature = "smoltcp")] -fn timestamp() -> smoltcp::time::Instant { - smoltcp::time::Instant::from_micros( - esp_hal::time::now().duration_since_epoch().to_micros() as i64 - ) -} - // this is just to verify that we use the correct defaults in `build.rs` #[allow(clippy::assertions_on_constants)] // TODO: try assert_eq once it's usable in const context const _: () = { @@ -186,6 +164,7 @@ struct Config { dynamic_rx_buf_num: usize, static_tx_buf_num: usize, dynamic_tx_buf_num: usize, + csi_enable: bool, ampdu_rx_enable: bool, ampdu_tx_enable: bool, amsdu_tx_enable: bool, @@ -202,13 +181,14 @@ struct Config { scan_method: u32, } -pub(crate) const CONFIG: Config = Config { +pub(crate) const CONFIG: config::EspWifiConfig = config::EspWifiConfig { rx_queue_size: esp_config_int!(usize, "ESP_WIFI_RX_QUEUE_SIZE"), tx_queue_size: esp_config_int!(usize, "ESP_WIFI_TX_QUEUE_SIZE"), static_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_STATIC_RX_BUF_NUM"), dynamic_rx_buf_num: esp_config_int!(usize, "ESP_WIFI_DYNAMIC_RX_BUF_NUM"), static_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_STATIC_TX_BUF_NUM"), dynamic_tx_buf_num: esp_config_int!(usize, "ESP_WIFI_DYNAMIC_TX_BUF_NUM"), + csi_enable: esp_config_bool!("ESP_WIFI_CSI_ENABLE"), ampdu_rx_enable: esp_config_bool!("ESP_WIFI_AMPDU_RX_ENABLE"), ampdu_tx_enable: esp_config_bool!("ESP_WIFI_AMPDU_TX_ENABLE"), amsdu_tx_enable: esp_config_bool!("ESP_WIFI_AMSDU_TX_ENABLE"), @@ -239,73 +219,53 @@ const _: () = { type TimeBase = PeriodicTimer<'static, AnyTimer>; -#[derive(Debug, PartialEq, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -/// An internal struct designed to make [`EspWifiInitialization`] uncreatable -/// outside of this crate. -pub struct EspWifiInitializationInternal; +pub(crate) mod flags { + use portable_atomic::{AtomicBool, AtomicUsize}; + + pub(crate) static ESP_WIFI_INITIALIZED: AtomicBool = AtomicBool::new(false); + pub(crate) static WIFI: AtomicUsize = AtomicUsize::new(0); + pub(crate) static BLE: AtomicBool = AtomicBool::new(false); +} #[derive(Debug, PartialEq, PartialOrd)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Initialized the driver for WiFi, Bluetooth or both. -pub enum EspWifiInitialization { - #[cfg(feature = "wifi")] - Wifi(EspWifiInitializationInternal), - #[cfg(feature = "ble")] - Ble(EspWifiInitializationInternal), - #[cfg(coex)] - WifiBle(EspWifiInitializationInternal), +pub struct EspWifiController<'d> { + _inner: PhantomData<&'d ()>, } -impl EspWifiInitialization { - #[allow(unused)] - fn is_wifi(&self) -> bool { - match self { - #[cfg(feature = "ble")] - EspWifiInitialization::Ble(_) => false, - _ => true, - } +impl<'d> EspWifiController<'d> { + /// Is the WiFi part of the radio running + pub fn wifi(&self) -> bool { + crate::flags::WIFI.load(Ordering::Acquire) > 0 } - #[allow(unused)] - fn is_ble(&self) -> bool { - match self { - #[cfg(feature = "wifi")] - EspWifiInitialization::Wifi(_) => false, - _ => true, - } + /// Is the BLE part of the radio running + pub fn ble(&self) -> bool { + crate::flags::BLE.load(Ordering::Acquire) } -} -#[derive(Debug, PartialEq, PartialOrd)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Initialize the driver for WiFi, Bluetooth or both. -pub enum EspWifiInitFor { - #[cfg(feature = "wifi")] - Wifi, - #[cfg(feature = "ble")] - Ble, - #[cfg(coex)] - WifiBle, -} + /// De-initialize the radio + pub fn deinit(self) -> Result<(), InitializationError> { + if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) { + // safety: no other driver can be using this if this is callable + unsafe { deinit_unchecked() } + } else { + Ok(()) + } + } -impl EspWifiInitFor { - #[allow(unused)] - fn is_wifi(&self) -> bool { - match self { - #[cfg(feature = "ble")] - EspWifiInitFor::Ble => false, - _ => true, + pub(crate) unsafe fn conjure() -> Self { + Self { + _inner: PhantomData, } } +} - #[allow(unused)] - fn is_ble(&self) -> bool { - match self { - #[cfg(feature = "wifi")] - EspWifiInitFor::Wifi => false, - _ => true, +impl<'d> Drop for EspWifiController<'d> { + fn drop(&mut self) { + if crate::flags::ESP_WIFI_INITIALIZED.load(Ordering::Acquire) { + // safety: no other driver can be using this if this is callable + unsafe { deinit_unchecked().ok() }; } } } @@ -314,7 +274,7 @@ impl EspWifiInitFor { /// /// This trait is meant to be used only for the `init` function. /// Calling `timers()` multiple times may panic. -pub trait EspWifiTimerSource { +pub trait EspWifiTimerSource: private::Sealed { /// Returns the timer source. fn timer(self) -> TimeBase; } @@ -344,7 +304,7 @@ impl IntoAnyTimer for AnyTimer {} impl EspWifiTimerSource for T where - T: IntoAnyTimer, + T: IntoAnyTimer + private::Sealed, { fn timer(self) -> TimeBase { TimeBase::new(self.into()).timer() @@ -357,6 +317,29 @@ impl EspWifiTimerSource for TimeBase { } } +impl private::Sealed for TimeBase {} +impl private::Sealed for TimgTimer +where + DM: esp_hal::Mode, + Self: Into, +{ +} +#[cfg(not(feature = "esp32"))] +impl private::Sealed for Alarm<'_, T, DM, COMP, UNIT> +where + DM: esp_hal::Mode, + Self: Into, +{ +} + +/// A marker trait for suitable Rng sources for esp-wifi +pub trait EspWifiRngSource: rand_core::RngCore + private::Sealed {} + +impl EspWifiRngSource for Rng {} +impl private::Sealed for Rng {} +impl EspWifiRngSource for Trng<'_> {} +impl private::Sealed for Trng<'_> {} + /// Initialize for using WiFi and or BLE. /// /// # The `timer` argument @@ -374,11 +357,9 @@ impl EspWifiTimerSource for TimeBase { /// ```rust, no_run #[doc = esp_hal::before_snippet!()] /// use esp_hal::{rng::Rng, timg::TimerGroup}; -/// use esp_wifi::EspWifiInitFor; /// /// let timg0 = TimerGroup::new(peripherals.TIMG0); /// let init = esp_wifi::init( -/// EspWifiInitFor::Wifi, /// timg0.timer0, /// Rng::new(peripherals.RNG), /// peripherals.RADIO_CLK, @@ -386,12 +367,11 @@ impl EspWifiTimerSource for TimeBase { /// .unwrap(); /// # } /// ``` -pub fn init( - init_for: EspWifiInitFor, - timer: impl EspWifiTimerSource, - rng: hal::rng::Rng, - radio_clocks: hal::peripherals::RADIO_CLK, -) -> Result { +pub fn init<'d, T: EspWifiTimerSource>( + timer: impl Peripheral

+ 'd, + _rng: impl EspWifiRngSource, + _radio_clocks: impl Peripheral

+ 'd, +) -> Result, InitializationError> { // A minimum clock of 80MHz is required to operate WiFi module. const MIN_CLOCK: u32 = 80; let clocks = Clocks::get(); @@ -402,10 +382,8 @@ pub fn init( info!("esp-wifi configuration {:?}", crate::CONFIG); crate::common_adapter::chip_specific::enable_wifi_power_domain(); phy_mem_init(); - init_radio_clock_control(radio_clocks); - init_rng(rng); init_tasks(); - setup_timer_isr(timer.timer())?; + setup_timer_isr(unsafe { timer.clone_unchecked() }.timer()); wifi_set_log_verbose(); init_clocks(); @@ -416,60 +394,23 @@ pub fn init( error => return Err(InitializationError::General(error)), } - #[cfg(feature = "wifi")] - if init_for.is_wifi() { - debug!("wifi init"); - // wifi init - crate::wifi::wifi_init()?; - } - - #[cfg(feature = "ble")] - if init_for.is_ble() { - // ble init - // for some reason things don't work when initializing things the other way - // around while the original implementation in NuttX does it like that - debug!("ble init"); - crate::ble::ble_init(); - } + crate::flags::ESP_WIFI_INITIALIZED.store(true, Ordering::Release); - match init_for { - #[cfg(feature = "wifi")] - EspWifiInitFor::Wifi => Ok(EspWifiInitialization::Wifi(EspWifiInitializationInternal)), - #[cfg(feature = "ble")] - EspWifiInitFor::Ble => Ok(EspWifiInitialization::Ble(EspWifiInitializationInternal)), - #[cfg(coex)] - EspWifiInitFor::WifiBle => Ok(EspWifiInitialization::WifiBle( - EspWifiInitializationInternal, - )), - } + Ok(EspWifiController { + _inner: PhantomData, + }) } -/// Deinitializes WiFi and/or BLE -/// -/// After user calls this function, WiFi and/or BLE (depending on what has been -/// initialized) are fully stopped and deinitialized. After that, they should -/// not be used until they have been reinitialized with the `init` function. +/// Deinitializes the entire radio stack /// -/// The function also disables the corresponding interrupts, deinitializes -/// the timer and radio clock, freeing these resources and returning them. -/// -/// Calling this while still using WiFi/BLE will cause crashes or undefined -/// behavior. +/// This can be useful to shutdown the stack before going to sleep for example. /// /// # Safety -/// Actual implementation assumes that the user takes responsibility for how the -/// function is used. For example, after using this function, user should not -/// use BLE or WiFi stack or controller instances (it is possible to -/// reinitialize communication using the `init` function), not to call -/// `deinit_unsafe` before the first initialization, and so on. Also, there is -/// currently no way to track whether a peripheral has been initialized, -/// so deinitialization is done based on the activated feature (`wifi`, `ble` -/// and/or `coex`). -/// Before deinitializing, chips with NPL bluetooth (esp32c2, esp32c6, esp32h2) -/// users must make sure to stop BLE advertising before. -pub unsafe fn deinit_unchecked( - init: EspWifiInitialization, -) -> Result<(TimeBase, hal::peripherals::RADIO_CLK), InitializationError> { +/// +/// The user must ensure that any use of the radio via the WIFI/BLE/ESP-NOW +/// drivers are complete, else undefined behavour may occur within those +/// drivers. +pub unsafe fn deinit_unchecked() -> Result<(), InitializationError> { // Disable coexistence #[cfg(coex)] { @@ -477,34 +418,35 @@ pub unsafe fn deinit_unchecked( unsafe { crate::wifi::os_adapter::coex_deinit() }; } - // Deinitialize WiFi - #[cfg(feature = "wifi")] - if init.is_wifi() { - esp_wifi_result!(unsafe { esp_wifi_stop() })?; - esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?; - esp_wifi_result!(esp_supplicant_deinit())?; - } + let controller = unsafe { EspWifiController::conjure() }; - // Deinitialize BLE - #[cfg(feature = "ble")] - if init.is_ble() { - #[cfg(any(esp32, esp32c3, esp32s3))] - crate::ble::btdm::ble_deinit(); + // Peripheral drivers should already take care of shutting these down + // we have to check this in the case where a user calls `deinit_unchecked` + // directly. + if controller.wifi() { + #[cfg(feature = "wifi")] + crate::wifi::wifi_deinit()?; + crate::flags::WIFI.store(0, Ordering::Release); + } - #[cfg(any(esp32c2, esp32c6, esp32h2))] - crate::ble::npl::ble_deinit(); + if controller.ble() { + #[cfg(feature = "ble")] + crate::ble::ble_deinit(); + crate::flags::BLE.store(false, Ordering::Release); } - shutdown_timer_isr().unwrap(); + shutdown_timer_isr(); crate::preempt::delete_all_tasks(); - let timer = critical_section::with(|cs| crate::timer::TIMER.borrow_ref_mut(cs).take()) - .ok_or(InitializationError::TimerUnavailable)?; + critical_section::with(|cs| crate::timer::TIMER.borrow_ref_mut(cs).take()); + + crate::flags::ESP_WIFI_INITIALIZED.store(false, Ordering::Release); - let radio_clocks = - deinit_radio_clock_control().ok_or(InitializationError::RadioClockUnavailable)?; + Ok(()) +} - Ok((timer, radio_clocks)) +pub(crate) mod private { + pub trait Sealed {} } #[derive(Debug, Clone, Copy)] @@ -515,15 +457,6 @@ pub enum InitializationError { #[cfg(feature = "wifi")] WifiError(WifiError), WrongClockConfig, - Timer(hal::timer::Error), - TimerUnavailable, - RadioClockUnavailable, -} - -impl From for InitializationError { - fn from(value: hal::timer::Error) -> Self { - InitializationError::Timer(value) - } } #[cfg(feature = "wifi")] @@ -548,7 +481,6 @@ pub fn wifi_set_log_verbose() { } fn init_clocks() { - unsafe { - unwrap!(RADIO_CLOCKS.as_mut()).init_clocks(); - } + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.init_clocks(); } diff --git a/esp-wifi/src/preempt/mod.rs b/esp-wifi/src/preempt/mod.rs index edf76b6caae..18e2ffe7e68 100644 --- a/esp-wifi/src/preempt/mod.rs +++ b/esp-wifi/src/preempt/mod.rs @@ -10,21 +10,27 @@ use esp_wifi_sys::include::malloc; use crate::{compat::malloc::free, hal::trapframe::TrapFrame, memory_fence::memory_fence}; -static mut CTX_NOW: Mutex> = Mutex::new(RefCell::new(core::ptr::null_mut())); +#[repr(transparent)] +struct ContextWrapper(*mut Context); + +unsafe impl Send for ContextWrapper {} + +static CTX_NOW: Mutex> = + Mutex::new(RefCell::new(ContextWrapper(core::ptr::null_mut()))); static mut SCHEDULED_TASK_TO_DELETE: *mut Context = core::ptr::null_mut(); pub(crate) fn allocate_main_task() -> *mut Context { critical_section::with(|cs| unsafe { let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); - if !(*ctx_now).is_null() { + if !ctx_now.0.is_null() { panic!("Tried to allocate main task multiple times"); } let ptr = malloc(size_of::() as u32) as *mut Context; core::ptr::write(ptr, Context::new()); (*ptr).next = ptr; - *ctx_now = ptr; + ctx_now.0 = ptr; ptr }) } @@ -32,14 +38,14 @@ pub(crate) fn allocate_main_task() -> *mut Context { fn allocate_task() -> *mut Context { critical_section::with(|cs| unsafe { let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); - if (*ctx_now).is_null() { + if ctx_now.0.is_null() { panic!("Called `allocate_task` before allocating main task"); } let ptr = malloc(size_of::() as u32) as *mut Context; core::ptr::write(ptr, Context::new()); - (*ptr).next = (**ctx_now).next; - (**ctx_now).next = ptr; + (*ptr).next = (*ctx_now.0).next; + (*ctx_now.0).next = ptr; ptr }) } @@ -47,7 +53,7 @@ fn allocate_task() -> *mut Context { fn next_task() { critical_section::with(|cs| unsafe { let mut ctx_now = CTX_NOW.borrow_ref_mut(cs); - *ctx_now = (**ctx_now).next; + ctx_now.0 = (*ctx_now.0).next; }); } @@ -56,7 +62,7 @@ fn next_task() { /// This will also free the memory (stack and context) allocated for it. pub(crate) fn delete_task(task: *mut Context) { critical_section::with(|cs| unsafe { - let mut ptr = *CTX_NOW.borrow_ref_mut(cs); + let mut ptr = CTX_NOW.borrow_ref_mut(cs).0; let initial = ptr; loop { if (*ptr).next == task { @@ -81,7 +87,7 @@ pub(crate) fn delete_task(task: *mut Context) { pub(crate) fn delete_all_tasks() { critical_section::with(|cs| unsafe { let mut ctx_now_ref = CTX_NOW.borrow_ref_mut(cs); - let current_task = *ctx_now_ref; + let current_task = ctx_now_ref.0; if current_task.is_null() { return; @@ -102,14 +108,14 @@ pub(crate) fn delete_all_tasks() { task_to_delete = next_task; } - *ctx_now_ref = core::ptr::null_mut(); + ctx_now_ref.0 = core::ptr::null_mut(); memory_fence(); }); } pub(crate) fn current_task() -> *mut Context { - critical_section::with(|cs| unsafe { *CTX_NOW.borrow_ref(cs) }) + critical_section::with(|cs| CTX_NOW.borrow_ref(cs).0) } pub(crate) fn schedule_task_deletion(task: *mut Context) { diff --git a/esp-wifi/src/tasks.rs b/esp-wifi/src/tasks.rs index 18040173f79..7dae1e046b6 100644 --- a/esp-wifi/src/tasks.rs +++ b/esp-wifi/src/tasks.rs @@ -1,8 +1,7 @@ use crate::{ - compat::{queue::SimpleQueue, timer_compat::TIMERS}, - memory_fence::memory_fence, + compat::timer_compat::TIMERS, preempt::arch_specific::task_create, - timer::{get_systimer_count, yield_task}, + timer::{systimer_count, yield_task}, }; /// Initializes the `main` and `timer` tasks for the Wi-Fi driver. @@ -18,34 +17,31 @@ pub(crate) fn init_tasks() { /// events. pub(crate) extern "C" fn timer_task(_param: *mut esp_wifi_sys::c_types::c_void) { loop { - let mut to_run = SimpleQueue::<_, 20>::new(); + let current_timestamp = systimer_count(); + let to_run = critical_section::with(|cs| unsafe { + let mut timers = TIMERS.borrow_ref_mut(cs); + let to_run = timers.find_next_due(current_timestamp); - let current_timestamp = get_systimer_count(); - critical_section::with(|_| unsafe { - memory_fence(); - for timer in TIMERS.iter_mut() { - if timer.active - && crate::timer::time_diff(timer.started, current_timestamp) >= timer.timeout - { - trace!("timer is due.... {:x}", timer.id()); + if let Some(to_run) = to_run { + to_run.active = to_run.periodic; - if to_run.enqueue(timer.callback).is_err() { - break; - } + if to_run.periodic { + to_run.started = current_timestamp; + } - timer.active = timer.periodic; - }; + Some(to_run.callback) + } else { + None } - memory_fence(); }); - // run the due timer callbacks NOT in an interrupt free context - while let Some(callback) = to_run.dequeue() { + // run the due timer callback NOT in an interrupt free context + if let Some(to_run) = to_run { trace!("trigger timer...."); - callback.call(); + to_run.call(); trace!("timer callback called"); + } else { + yield_task(); } - - yield_task(); } } diff --git a/esp-wifi/src/timer/mod.rs b/esp-wifi/src/timer/mod.rs index f6531158ac9..0cd4d541ef2 100644 --- a/esp-wifi/src/timer/mod.rs +++ b/esp-wifi/src/timer/mod.rs @@ -22,24 +22,22 @@ use crate::TimeBase; pub(crate) static TIMER: Mutex>> = Mutex::new(RefCell::new(None)); -pub(crate) fn setup_timer_isr(timebase: TimeBase) -> Result<(), esp_hal::timer::Error> { +pub(crate) fn setup_timer_isr(timebase: TimeBase) { setup_radio_isr(); - setup_timer(timebase)?; + setup_timer(timebase); setup_multitasking(); yield_task(); - Ok(()) } -pub(crate) fn shutdown_timer_isr() -> Result<(), esp_hal::timer::Error> { +pub(crate) fn shutdown_timer_isr() { shutdown_radio_isr(); - disable_timer()?; + disable_timer(); disable_multitasking(); - Ok(()) } #[allow(unused)] @@ -64,6 +62,6 @@ pub(crate) fn ticks_to_millis(ticks: u64) -> u64 { /// Do not call this in a critical section! pub(crate) fn elapsed_time_since(start: u64) -> u64 { - let now = get_systimer_count(); + let now = systimer_count(); time_diff(start, now) } diff --git a/esp-wifi/src/timer/riscv.rs b/esp-wifi/src/timer/riscv.rs index 46eafa16220..b0a4bb2635f 100644 --- a/esp-wifi/src/timer/riscv.rs +++ b/esp-wifi/src/timer/riscv.rs @@ -23,28 +23,24 @@ pub const TICKS_PER_SECOND: u64 = 1_000_000; use super::TIMER; -pub(crate) fn setup_timer(mut alarm0: TimeBase) -> Result<(), esp_hal::timer::Error> { +pub(crate) fn setup_timer(mut alarm0: TimeBase) { // make sure the scheduling won't start before everything is setup riscv::interrupt::disable(); let cb: extern "C" fn() = unsafe { core::mem::transmute(handler as *const ()) }; alarm0.set_interrupt_handler(InterruptHandler::new(cb, interrupt::Priority::Priority1)); - alarm0.start(TIMESLICE_FREQUENCY.into_duration())?; + unwrap!(alarm0.start(TIMESLICE_FREQUENCY.into_duration())); critical_section::with(|cs| { alarm0.enable_interrupt(true); TIMER.borrow_ref_mut(cs).replace(alarm0); }); - - Ok(()) } -pub(crate) fn disable_timer() -> Result<(), esp_hal::timer::Error> { +pub(crate) fn disable_timer() { critical_section::with(|cs| { unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).enable_interrupt(false); - unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).cancel().unwrap(); + unwrap!(unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).cancel()); }); - - Ok(()) } pub(crate) fn setup_multitasking() { @@ -99,7 +95,7 @@ pub(crate) fn yield_task() { /// Current systimer count value /// A tick is 1 / 1_000_000 seconds -pub(crate) fn get_systimer_count() -> u64 { +pub(crate) fn systimer_count() -> u64 { esp_hal::time::now().ticks() } diff --git a/esp-wifi/src/timer/xtensa.rs b/esp-wifi/src/timer/xtensa.rs index 6c123edaefb..3cff547ac06 100644 --- a/esp-wifi/src/timer/xtensa.rs +++ b/esp-wifi/src/timer/xtensa.rs @@ -17,30 +17,27 @@ pub const TICKS_PER_SECOND: u64 = 1_000_000; /// This function must not be called in a critical section. Doing so may return /// an incorrect value. -pub(crate) fn get_systimer_count() -> u64 { +pub(crate) fn systimer_count() -> u64 { esp_hal::time::now().ticks() } -pub(crate) fn setup_timer(mut timer1: TimeBase) -> Result<(), esp_hal::timer::Error> { +pub(crate) fn setup_timer(mut timer1: TimeBase) { timer1.set_interrupt_handler(InterruptHandler::new( unsafe { core::mem::transmute::<*const (), extern "C" fn()>(handler as *const ()) }, interrupt::Priority::Priority2, )); - timer1.start(TIMESLICE_FREQUENCY.into_duration())?; + unwrap!(timer1.start(TIMESLICE_FREQUENCY.into_duration())); critical_section::with(|cs| { timer1.enable_interrupt(true); TIMER.borrow_ref_mut(cs).replace(timer1); }); - Ok(()) } -pub(crate) fn disable_timer() -> Result<(), esp_hal::timer::Error> { +pub(crate) fn disable_timer() { critical_section::with(|cs| { unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).enable_interrupt(false); - unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).cancel().unwrap(); + unwrap!(unwrap!(TIMER.borrow_ref_mut(cs).as_mut()).cancel()); }); - - Ok(()) } pub(crate) fn setup_multitasking() { diff --git a/esp-wifi/src/wifi/event.rs b/esp-wifi/src/wifi/event.rs new file mode 100644 index 00000000000..f0daf78d4fa --- /dev/null +++ b/esp-wifi/src/wifi/event.rs @@ -0,0 +1,348 @@ +use alloc::boxed::Box; +use core::cell::RefCell; + +use critical_section::Mutex; + +use super::WifiEvent; + +pub(crate) mod sealed { + use super::*; + + pub trait Event { + /// Get the static reference to the handler for this event. + fn handler() -> &'static Mutex>>>>; + /// # Safety + /// `ptr` must be a valid for casting to this event's inner event data. + unsafe fn from_raw_event_data(ptr: *mut crate::binary::c_types::c_void) -> Self; + } +} +/// The type of handlers of events. +pub type Handler = dyn FnMut(critical_section::CriticalSection<'_>, &T) + Sync + Send; + +fn default_handler() -> Box> { + fn drop_ref(_: critical_section::CriticalSection<'_>, _: &T) {} + // perf: `drop_ref` is a ZST [function item](https://doc.rust-lang.org/reference/types/function-item.html) + // so this doesn't actually allocate. + Box::new(drop_ref) +} + +/// Extension trait for setting handlers for an event. +/// +/// Register a new event handler like: +/// ``` +/// # use esp_wifi::wifi::event::{self, *}; +/// # fn new_handler(_: critical_section::CriticalSection<'_>, _: &ApStaconnected) {} +/// event::ApStaconnected::update_handler(|_cs, event| { +/// new_handler(event); +/// }) +/// ``` +// Implemented like this instead of free functions because the syntax would be +// ``` +// event::update_handler::(...) +// ``` +pub trait EventExt: sealed::Event + Sized + 'static { + /// Get the handler for this event, replacing it with the default handler. + fn take_handler() -> Box> { + critical_section::with(|cs| { + Self::handler() + .borrow_ref_mut(cs) + .take() + .unwrap_or_else(default_handler::) + }) + } + /// Set the handler for this event, returning the old handler. + fn replace_handler< + F: FnMut(critical_section::CriticalSection<'_>, &Self) + Sync + Send + 'static, + >( + f: F, + ) -> Box> { + critical_section::with(|cs| { + Self::handler() + .borrow_ref_mut(cs) + .replace(Box::new(f)) + .unwrap_or_else(default_handler::) + }) + } + /// Atomic combination of [`take_handler`] and [`replace_handler`]. Use this + /// to add a new handler which runs after the previously registered + /// handlers. + fn update_handler< + F: FnMut(critical_section::CriticalSection<'_>, &Self) + Sync + Send + 'static, + >( + mut f: F, + ) { + critical_section::with(move |cs| { + let mut handler: Box> = Self::handler() + .borrow_ref_mut(cs) + .take() + .unwrap_or_else(default_handler::); + Self::handler().borrow_ref_mut(cs).replace(Box::new( + move |cs: critical_section::CriticalSection<'_>, event| { + handler(cs, event); + f(cs, event) + }, + )); + }); + } +} +impl EventExt for T {} + +macro_rules! impl_wifi_event { + // no data + ($newtype:ident) => { + /// See [`WifiEvent`]. + #[derive(Copy, Clone)] + pub struct $newtype; + impl sealed::Event for $newtype { + unsafe fn from_raw_event_data(_: *mut crate::binary::c_types::c_void) -> Self { + Self + } + fn handler() -> &'static Mutex>>>> { + static HANDLE: Mutex>>>> = + Mutex::new(RefCell::new(None)); + &HANDLE + } + } + }; + // data + ($newtype:ident, $data:ident) => { + pub use esp_wifi_sys::include::$data; + /// See [`WifiEvent`]. + #[derive(Copy, Clone)] + pub struct $newtype(pub $data); + impl sealed::Event for $newtype { + unsafe fn from_raw_event_data(ptr: *mut crate::binary::c_types::c_void) -> Self { + Self(unsafe { *ptr.cast() }) + } + fn handler() -> &'static Mutex>>>> { + static HANDLE: Mutex>>>> = + Mutex::new(RefCell::new(None)); + &HANDLE + } + } + }; +} + +impl_wifi_event!(WifiReady); +impl_wifi_event!(ScanDone); +impl_wifi_event!(StaStart); +impl_wifi_event!(StaStop); +impl_wifi_event!(StaConnected, wifi_event_sta_connected_t); +impl_wifi_event!(StaDisconnected, wifi_event_sta_disconnected_t); +impl_wifi_event!(StaAuthmodeChange, wifi_event_sta_authmode_change_t); +impl_wifi_event!(StaWpsErSuccess, wifi_event_sta_wps_er_success_t); +impl_wifi_event!(StaWpsErFailed); +impl_wifi_event!(StaWpsErTimeout); +impl_wifi_event!(StaWpsErPin, wifi_event_sta_wps_er_pin_t); +impl_wifi_event!(StaWpsErPbcOverlap); +impl_wifi_event!(ApStart); +impl_wifi_event!(ApStop); +impl_wifi_event!(ApStaconnected, wifi_event_ap_staconnected_t); +impl_wifi_event!(ApStadisconnected, wifi_event_ap_stadisconnected_t); +impl_wifi_event!(ApProbereqrecved, wifi_event_ap_probe_req_rx_t); +impl_wifi_event!(FtmReport, wifi_event_ftm_report_t); +impl_wifi_event!(StaBssRssiLow, wifi_event_bss_rssi_low_t); +impl_wifi_event!(ActionTxStatus, wifi_event_action_tx_status_t); +impl_wifi_event!(RocDone, wifi_event_roc_done_t); +impl_wifi_event!(StaBeaconTimeout); +impl_wifi_event!(ConnectionlessModuleWakeIntervalStart); +impl_wifi_event!(ApWpsRgSuccess, wifi_event_ap_wps_rg_success_t); +impl_wifi_event!(ApWpsRgFailed, wifi_event_ap_wps_rg_fail_reason_t); +impl_wifi_event!(ApWpsRgTimeout); +impl_wifi_event!(ApWpsRgPin, wifi_event_ap_wps_rg_pin_t); +impl_wifi_event!(ApWpsRgPbcOverlap); +impl_wifi_event!(ItwtSetup); +impl_wifi_event!(ItwtTeardown); +impl_wifi_event!(ItwtProbe); +impl_wifi_event!(ItwtSuspend); +impl_wifi_event!(TwtWakeup); +impl_wifi_event!(BtwtSetup); +impl_wifi_event!(BtwtTeardown); +impl_wifi_event!(NanStarted); +impl_wifi_event!(NanStopped); +impl_wifi_event!(NanSvcMatch, wifi_event_nan_svc_match_t); +impl_wifi_event!(NanReplied, wifi_event_nan_replied_t); +impl_wifi_event!(NanReceive, wifi_event_nan_receive_t); +impl_wifi_event!(NdpIndication, wifi_event_ndp_indication_t); +impl_wifi_event!(NdpConfirm, wifi_event_ndp_confirm_t); +impl_wifi_event!(NdpTerminated, wifi_event_ndp_terminated_t); +impl_wifi_event!(HomeChannelChange, wifi_event_home_channel_change_t); +impl_wifi_event!(StaNeighborRep, wifi_event_neighbor_report_t); + +/// Handle the given event using the registered event handlers. +pub fn handle( + cs: critical_section::CriticalSection<'_>, + event_data: &Event, +) -> bool { + if let Some(handler) = &mut *Event::handler().borrow_ref_mut(cs) { + handler(cs, event_data); + true + } else { + false + } +} + +/// Handle an event given the raw pointers. +/// # Safety +/// The pointer should be valid to cast to `Event`'s inner type (if it has one) +pub(crate) unsafe fn handle_raw( + cs: critical_section::CriticalSection<'_>, + event_data: *mut crate::binary::c_types::c_void, + event_data_size: usize, +) -> bool { + debug_assert_eq!( + event_data_size, + core::mem::size_of::(), + "wrong size event data" + ); + handle::(cs, unsafe { &Event::from_raw_event_data(event_data) }) +} + +/// Handle event regardless of its type. +/// # Safety +/// Arguments should be self-consistent. +#[rustfmt::skip] +pub(crate) unsafe fn dispatch_event_handler( + cs: critical_section::CriticalSection<'_>, + event: WifiEvent, + event_data: *mut crate::binary::c_types::c_void, + event_data_size: usize, +) -> bool { + match event { + WifiEvent::WifiReady => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ScanDone => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaStart => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaStop => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaConnected => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaDisconnected => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaAuthmodeChange => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaWpsErSuccess => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaWpsErFailed => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaWpsErTimeout => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaWpsErPin => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaWpsErPbcOverlap => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApStart => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApStop => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApStaconnected => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApStadisconnected => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApProbereqrecved => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::FtmReport => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaBssRssiLow => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ActionTxStatus => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::RocDone => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaBeaconTimeout => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ConnectionlessModuleWakeIntervalStart => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApWpsRgSuccess => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApWpsRgFailed => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApWpsRgTimeout => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApWpsRgPin => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ApWpsRgPbcOverlap => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ItwtSetup => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ItwtTeardown => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ItwtProbe => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::ItwtSuspend => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::TwtWakeup => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::BtwtSetup => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::BtwtTeardown => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NanStarted => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NanStopped => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NanSvcMatch => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NanReplied => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NanReceive => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NdpIndication => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NdpConfirm => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::NdpTerminated => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::HomeChannelChange => { + handle_raw::(cs, event_data, event_data_size) + } + WifiEvent::StaNeighborRep => { + handle_raw::(cs, event_data, event_data_size) + } + } +} diff --git a/esp-wifi/src/wifi/mod.rs b/esp-wifi/src/wifi/mod.rs index 16d92c0d2bb..7bb5ce6ce06 100644 --- a/esp-wifi/src/wifi/mod.rs +++ b/esp-wifi/src/wifi/mod.rs @@ -1,8 +1,10 @@ //! WiFi +pub mod event; pub(crate) mod os_adapter; pub(crate) mod state; +use alloc::collections::vec_deque::VecDeque; use core::{ cell::{RefCell, RefMut}, fmt::Debug, @@ -49,7 +51,6 @@ use esp_wifi_sys::include::{ wifi_promiscuous_pkt_type_t, }; use num_derive::FromPrimitive; -use num_traits::FromPrimitive; #[doc(hidden)] pub(crate) use os_adapter::*; #[cfg(feature = "sniffer")] @@ -61,6 +62,8 @@ use serde::{Deserialize, Serialize}; use smoltcp::phy::{Device, DeviceCapabilities, RxToken, TxToken}; pub use state::*; +#[cfg(not(coex))] +use crate::config::PowerSaveMode; use crate::{ common_adapter::*, esp_wifi_result, @@ -68,7 +71,7 @@ use crate::{ macros::ram, peripheral::{Peripheral, PeripheralRef}, }, - EspWifiInitialization, + EspWifiController, }; const ETHERNET_FRAME_HEADER_SIZE: usize = 18; @@ -81,65 +84,74 @@ pub mod utils; #[cfg(coex)] use include::{coex_adapter_funcs_t, coex_pre_init, esp_coex_adapter_register}; -use crate::{ - binary::{ - c_types, - include::{ - self, - __BindgenBitfieldUnit, - esp_err_t, - esp_interface_t_ESP_IF_WIFI_AP, - esp_interface_t_ESP_IF_WIFI_STA, - esp_supplicant_init, - esp_wifi_connect, - esp_wifi_disconnect, - esp_wifi_get_mode, - esp_wifi_init_internal, - esp_wifi_internal_free_rx_buffer, - esp_wifi_internal_reg_rxcb, - esp_wifi_internal_tx, - esp_wifi_scan_start, - esp_wifi_set_config, - esp_wifi_set_country, - esp_wifi_set_mode, - esp_wifi_set_protocol, - esp_wifi_set_ps, - esp_wifi_set_tx_done_cb, - esp_wifi_start, - esp_wifi_stop, - g_wifi_default_wpa_crypto_funcs, - wifi_active_scan_time_t, - wifi_ap_config_t, - wifi_auth_mode_t, - wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP, - wifi_config_t, - wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL, - wifi_country_t, - wifi_init_config_t, - wifi_interface_t, - wifi_interface_t_WIFI_IF_AP, - wifi_interface_t_WIFI_IF_STA, - wifi_mode_t, - wifi_mode_t_WIFI_MODE_AP, - wifi_mode_t_WIFI_MODE_APSTA, - wifi_mode_t_WIFI_MODE_NULL, - wifi_mode_t_WIFI_MODE_STA, - wifi_osi_funcs_t, - wifi_pmf_config_t, - wifi_scan_config_t, - wifi_scan_threshold_t, - wifi_scan_time_t, - wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE, - wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE, - wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL, - wifi_sta_config_t, - wpa_crypto_funcs_t, - ESP_WIFI_OS_ADAPTER_MAGIC, - ESP_WIFI_OS_ADAPTER_VERSION, - WIFI_INIT_CONFIG_MAGIC, - }, +#[cfg(all(csi_enable, esp32c6))] +use crate::binary::include::wifi_csi_acquire_config_t; +#[cfg(csi_enable)] +pub use crate::binary::include::wifi_csi_info_t; +#[cfg(csi_enable)] +use crate::binary::include::{ + esp_wifi_set_csi, + esp_wifi_set_csi_config, + esp_wifi_set_csi_rx_cb, + wifi_csi_config_t, +}; +use crate::binary::{ + c_types, + include::{ + self, + __BindgenBitfieldUnit, + esp_err_t, + esp_interface_t_ESP_IF_WIFI_AP, + esp_interface_t_ESP_IF_WIFI_STA, + esp_supplicant_deinit, + esp_supplicant_init, + esp_wifi_connect, + esp_wifi_deinit_internal, + esp_wifi_disconnect, + esp_wifi_get_mode, + esp_wifi_init_internal, + esp_wifi_internal_free_rx_buffer, + esp_wifi_internal_reg_rxcb, + esp_wifi_internal_tx, + esp_wifi_scan_start, + esp_wifi_set_config, + esp_wifi_set_country, + esp_wifi_set_mode, + esp_wifi_set_protocol, + esp_wifi_set_tx_done_cb, + esp_wifi_start, + esp_wifi_stop, + g_wifi_default_wpa_crypto_funcs, + wifi_active_scan_time_t, + wifi_ap_config_t, + wifi_auth_mode_t, + wifi_cipher_type_t_WIFI_CIPHER_TYPE_CCMP, + wifi_config_t, + wifi_country_policy_t_WIFI_COUNTRY_POLICY_MANUAL, + wifi_country_t, + wifi_init_config_t, + wifi_interface_t, + wifi_interface_t_WIFI_IF_AP, + wifi_interface_t_WIFI_IF_STA, + wifi_mode_t, + wifi_mode_t_WIFI_MODE_AP, + wifi_mode_t_WIFI_MODE_APSTA, + wifi_mode_t_WIFI_MODE_NULL, + wifi_mode_t_WIFI_MODE_STA, + wifi_osi_funcs_t, + wifi_pmf_config_t, + wifi_scan_config_t, + wifi_scan_threshold_t, + wifi_scan_time_t, + wifi_scan_type_t_WIFI_SCAN_TYPE_ACTIVE, + wifi_scan_type_t_WIFI_SCAN_TYPE_PASSIVE, + wifi_sort_method_t_WIFI_CONNECT_AP_BY_SIGNAL, + wifi_sta_config_t, + wpa_crypto_funcs_t, + ESP_WIFI_OS_ADAPTER_MAGIC, + ESP_WIFI_OS_ADAPTER_VERSION, + WIFI_INIT_CONFIG_MAGIC, }, - compat::queue::SimpleQueue, }; /// Supported Wi-Fi authentication methods. @@ -283,7 +295,7 @@ pub struct AccessPointConfiguration { impl Default for AccessPointConfiguration { fn default() -> Self { Self { - ssid: "iot-device".try_into().unwrap(), + ssid: unwrap!("iot-device".try_into()), ssid_hidden: false, channel: 1, secondary_channel: None, @@ -340,6 +352,191 @@ impl Default for ClientConfiguration { } } +#[cfg(csi_enable)] +pub(crate) trait CsiCallback: FnMut(crate::binary::include::wifi_csi_info_t) {} + +#[cfg(csi_enable)] +impl CsiCallback for T where T: FnMut(crate::binary::include::wifi_csi_info_t) {} + +#[cfg(csi_enable)] +unsafe extern "C" fn csi_rx_cb( + ctx: *mut crate::wifi::c_types::c_void, + data: *mut crate::binary::include::wifi_csi_info_t, +) { + let csi_callback = unsafe { &mut *(ctx as *mut C) }; + csi_callback(*data); +} + +#[derive(Clone, PartialEq, Eq)] +// https://github.com/esp-rs/esp-wifi-sys/blob/main/esp-wifi-sys/headers/local/esp_wifi_types_native.h#L94 +/// Channel state information(CSI) configuration +#[cfg(all(not(esp32c6), csi_enable))] +pub struct CsiConfig { + /// Enable to receive legacy long training field(lltf) data. + pub lltf_en: bool, + /// Enable to receive HT long training field(htltf) data. + pub htltf_en: bool, + /// Enable to receive space time block code HT long training + /// field(stbc-htltf2) data. + pub stbc_htltf2_en: bool, + /// Enable to generate htlft data by averaging lltf and ht_ltf data when + /// receiving HT packet. Otherwise, use ht_ltf data directly. + pub ltf_merge_en: bool, + /// Enable to turn on channel filter to smooth adjacent sub-carrier. Disable + /// it to keep independence of adjacent sub-carrier. + pub channel_filter_en: bool, + /// Manually scale the CSI data by left shifting or automatically scale the + /// CSI data. If set true, please set the shift bits. false: automatically. + /// true: manually. + pub manu_scale: bool, + /// Manually left shift bits of the scale of the CSI data. The range of the + /// left shift bits is 0~15. + pub shift: u8, + /// Enable to dump 802.11 ACK frame. + pub dump_ack_en: bool, +} + +#[derive(Clone, PartialEq, Eq)] +#[cfg(all(esp32c6, csi_enable))] +// See https://github.com/esp-rs/esp-wifi-sys/blob/2a466d96fe8119d49852fc794aea0216b106ba7b/esp-wifi-sys/src/include/esp32c6.rs#L5702-L5705 +pub struct CsiConfig { + /// Enable to acquire CSI. + pub enable: u32, + /// Enable to acquire L-LTF when receiving a 11g PPDU. + pub acquire_csi_legacy: u32, + /// Enable to acquire HT-LTF when receiving an HT20 PPDU. + pub acquire_csi_ht20: u32, + /// Enable to acquire HT-LTF when receiving an HT40 PPDU. + pub acquire_csi_ht40: u32, + /// Enable to acquire HE-LTF when receiving an HE20 SU PPDU. + pub acquire_csi_su: u32, + /// Enable to acquire HE-LTF when receiving an HE20 MU PPDU. + pub acquire_csi_mu: u32, + /// Enable to acquire HE-LTF when receiving an HE20 DCM applied PPDU. + pub acquire_csi_dcm: u32, + /// Enable to acquire HE-LTF when receiving an HE20 Beamformed applied PPDU. + pub acquire_csi_beamformed: u32, + /// Wwhen receiving an STBC applied HE PPDU, 0- acquire the complete + /// HE-LTF1, 1- acquire the complete HE-LTF2, 2- sample evenly among the + /// HE-LTF1 and HE-LTF2. + pub acquire_csi_he_stbc: u32, + /// Vvalue 0-3. + pub val_scale_cfg: u32, + /// Enable to dump 802.11 ACK frame, default disabled. + pub dump_ack_en: u32, + /// Reserved. + pub reserved: u32, +} + +#[cfg(csi_enable)] +impl Default for CsiConfig { + #[cfg(not(esp32c6))] + fn default() -> Self { + Self { + lltf_en: true, + htltf_en: true, + stbc_htltf2_en: true, + ltf_merge_en: true, + channel_filter_en: true, + manu_scale: false, + shift: 0, + dump_ack_en: false, + } + } + + #[cfg(esp32c6)] + fn default() -> Self { + // https://github.com/esp-rs/esp-wifi-sys/blob/2a466d96fe8119d49852fc794aea0216b106ba7b/esp-wifi-sys/headers/esp_wifi_he_types.h#L67-L82 + Self { + enable: 1, + acquire_csi_legacy: 1, + acquire_csi_ht20: 1, + acquire_csi_ht40: 1, + acquire_csi_su: 1, + acquire_csi_mu: 1, + acquire_csi_dcm: 1, + acquire_csi_beamformed: 1, + acquire_csi_he_stbc: 2, + val_scale_cfg: 2, + dump_ack_en: 1, + reserved: 19, + } + } +} + +#[cfg(csi_enable)] +impl From for wifi_csi_config_t { + fn from(config: CsiConfig) -> Self { + #[cfg(not(esp32c6))] + { + wifi_csi_config_t { + lltf_en: config.lltf_en, + htltf_en: config.htltf_en, + stbc_htltf2_en: config.stbc_htltf2_en, + ltf_merge_en: config.ltf_merge_en, + channel_filter_en: config.channel_filter_en, + manu_scale: config.manu_scale, + shift: config.shift, + dump_ack_en: config.dump_ack_en, + } + } + #[cfg(esp32c6)] + { + wifi_csi_acquire_config_t { + _bitfield_align_1: [0; 0], + _bitfield_1: wifi_csi_acquire_config_t::new_bitfield_1( + config.enable, + config.acquire_csi_legacy, + config.acquire_csi_ht20, + config.acquire_csi_ht40, + config.acquire_csi_su, + config.acquire_csi_mu, + config.acquire_csi_dcm, + config.acquire_csi_beamformed, + config.acquire_csi_he_stbc, + config.val_scale_cfg, + config.dump_ack_en, + config.reserved, + ), + } + } + } +} + +#[cfg(csi_enable)] +impl CsiConfig { + /// Set CSI data configuration + pub(crate) fn apply_config(&self) -> Result<(), WifiError> { + let conf: wifi_csi_config_t = self.clone().into(); + + unsafe { + esp_wifi_result!(esp_wifi_set_csi_config(&conf))?; + } + Ok(()) + } + + /// Register the RX callback function of CSI data. Each time a CSI data is + /// received, the callback function will be called. + pub(crate) fn set_receive_cb(&mut self, cb: C) -> Result<(), WifiError> { + let cb = alloc::boxed::Box::new(cb); + let cb_ptr = alloc::boxed::Box::into_raw(cb) as *mut crate::wifi::c_types::c_void; + + unsafe { + esp_wifi_result!(esp_wifi_set_csi_rx_cb(Some(csi_rx_cb::), cb_ptr))?; + } + Ok(()) + } + + /// Enable or disable CSI + pub(crate) fn set_csi(&self, enable: bool) -> Result<(), WifiError> { + // https://github.com/esp-rs/esp-wifi-sys/blob/2a466d96fe8119d49852fc794aea0216b106ba7b/esp-wifi-sys/headers/esp_wifi.h#L1241 + unsafe { + esp_wifi_result!(esp_wifi_set_csi(enable))?; + } + Ok(()) + } +} + /// Configuration for EAP-FAST authentication protocol. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -633,282 +830,6 @@ impl Configuration { } } -/// IPv4 network configurations. -pub mod ipv4 { - pub use core::net::Ipv4Addr; - use core::{fmt::Display, str::FromStr}; - - #[cfg(feature = "serde")] - use serde::{Deserialize, Serialize}; - - /// Represents a subnet mask. - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct Mask(pub u8); - - impl FromStr for Mask { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - s.parse::() - .map_err(|_| "Invalid subnet mask") - .map_or_else(Err, |mask| { - if (1..=32).contains(&mask) { - Ok(Mask(mask)) - } else { - Err("Mask should be a number between 1 and 32") - } - }) - } - } - - impl Display for Mask { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", self.0) - } - } - - impl TryFrom for Mask { - type Error = (); - - fn try_from(ip: Ipv4Addr) -> Result { - let octets = ip.octets(); - let addr: u32 = ((octets[0] as u32 & 0xff) << 24) - | ((octets[1] as u32 & 0xff) << 16) - | ((octets[2] as u32 & 0xff) << 8) - | (octets[3] as u32 & 0xff); - - if addr.leading_ones() + addr.trailing_zeros() == 32 { - Ok(Mask(addr.leading_ones() as u8)) - } else { - Err(()) - } - } - } - - impl From for Ipv4Addr { - fn from(mask: Mask) -> Self { - let addr: u32 = ((1 << (32 - mask.0)) - 1) ^ 0xffffffffu32; - - let (a, b, c, d) = ( - ((addr >> 24) & 0xff) as u8, - ((addr >> 16) & 0xff) as u8, - ((addr >> 8) & 0xff) as u8, - (addr & 0xff) as u8, - ); - - Ipv4Addr::new(a, b, c, d) - } - } - - /// Represents a subnet consisting of a gateway and a mask. - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct Subnet { - /// The gateway IP address of the subnet. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub gateway: Ipv4Addr, - /// The subnet mask associated with the subnet. - pub mask: Mask, - } - - impl Display for Subnet { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}/{}", self.gateway, self.mask) - } - } - - impl FromStr for Subnet { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let mut split = s.split('/'); - if let Some(gateway_str) = split.next() { - if let Some(mask_str) = split.next() { - if split.next().is_none() { - if let Ok(gateway) = gateway_str.parse::() { - return mask_str.parse::().map(|mask| Self { gateway, mask }); - } else { - return Err("Invalid IP address format, expected XXX.XXX.XXX.XXX"); - } - } - } - } - - Err("Expected /") - } - } - - /// Settings for a client in an IPv4 network. - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct ClientSettings { - /// The client's IPv4 address. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub ip: Ipv4Addr, - - /// The subnet associated with the client's IP address. - pub subnet: Subnet, - - /// The primary DNS server for name resolution. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub dns: Option, - - /// The secondary DNS server for name resolution. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub secondary_dns: Option, - } - - impl Default for ClientSettings { - fn default() -> ClientSettings { - ClientSettings { - ip: Ipv4Addr::new(192, 168, 71, 200), - subnet: Subnet { - gateway: Ipv4Addr::new(192, 168, 71, 1), - mask: Mask(24), - }, - dns: Some(Ipv4Addr::new(8, 8, 8, 8)), - secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)), - } - } - } - - /// Settings for the DHCP client. - /// - /// This struct contains the configuration for a DHCP client, including a - /// hostname that can be sent during DHCP negotiations. - #[derive(Default, Clone, Debug, PartialEq, Eq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct DHCPClientSettings { - pub hostname: Option>, - } - - /// Configuration for the client in an IPv4 network. - /// - /// This enum defines how the client's IP settings are obtained: either - /// through DHCP, or as a fixed (static) configuration. - #[derive(Clone, Debug, PartialEq, Eq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub enum ClientConfiguration { - /// Use DHCP to obtain network settings. - DHCP(DHCPClientSettings), - - /// Use a fixed configuration for network settings. - Fixed(ClientSettings), - } - - impl ClientConfiguration { - /// Returns a reference to the fixed settings if the client is using a - /// static configuration, `None` otherwise. - pub fn as_fixed_settings_ref(&self) -> Option<&ClientSettings> { - match self { - Self::Fixed(client_settings) => Some(client_settings), - _ => None, - } - } - - /// Returns a mutable reference to the fixed settings, creating a - /// default fixed configuration if necessary. - pub fn as_fixed_settings_mut(&mut self) -> &mut ClientSettings { - match self { - Self::Fixed(client_settings) => client_settings, - _ => { - *self = ClientConfiguration::Fixed(Default::default()); - self.as_fixed_settings_mut() - } - } - } - } - - impl Default for ClientConfiguration { - fn default() -> ClientConfiguration { - ClientConfiguration::DHCP(Default::default()) - } - } - - /// Router configuration in an IPv4 network. - #[derive(Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct RouterConfiguration { - /// The subnet the router is responsible for. - pub subnet: Subnet, - - /// Indicates whether DHCP is enabled on the router. - pub dhcp_enabled: bool, - - /// The primary DNS server for the router. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub dns: Option, - - /// The secondary DNS server for the router. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub secondary_dns: Option, - } - - impl Default for RouterConfiguration { - fn default() -> RouterConfiguration { - RouterConfiguration { - subnet: Subnet { - gateway: Ipv4Addr::new(192, 168, 71, 1), - mask: Mask(24), - }, - dhcp_enabled: true, - dns: Some(Ipv4Addr::new(8, 8, 8, 8)), - secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)), - } - } - } - - /// Represents the network configuration for a device. - /// - /// Holds either a client configuration (for devices connecting to a - /// network) or a router configuration (for devices providing a network - /// to other clients). - #[derive(Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub enum Configuration { - /// Configuration for a device acting as a client in the network. - Client(ClientConfiguration), - - /// Configuration for a device acting as a router in the network. - Router(RouterConfiguration), - } - - impl Default for Configuration { - fn default() -> Self { - Self::Client(Default::default()) - } - } - - /// Represents IPv4 information for a device. - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] - pub struct IpInfo { - /// The IPv4 address of the device. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub ip: Ipv4Addr, - - /// The subnet mask associated with the device's IP address. - pub subnet: Subnet, - - /// The primary DNS server for the device. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub dns: Option, - - /// The secondary DNS server for the device. - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] - pub secondary_dns: Option, - } -} - trait AuthMethodExt { fn to_raw(&self) -> wifi_auth_mode_t; fn from_raw(raw: wifi_auth_mode_t) -> Self; @@ -1031,13 +952,11 @@ const DATA_FRAME_SIZE: usize = MTU + ETHERNET_FRAME_HEADER_SIZE; const RX_QUEUE_SIZE: usize = crate::CONFIG.rx_queue_size; const TX_QUEUE_SIZE: usize = crate::CONFIG.tx_queue_size; -pub(crate) static DATA_QUEUE_RX_AP: Mutex< - RefCell>, -> = Mutex::new(RefCell::new(SimpleQueue::new())); +pub(crate) static DATA_QUEUE_RX_AP: Mutex>> = + Mutex::new(RefCell::new(VecDeque::new())); -pub(crate) static DATA_QUEUE_RX_STA: Mutex< - RefCell>, -> = Mutex::new(RefCell::new(SimpleQueue::new())); +pub(crate) static DATA_QUEUE_RX_STA: Mutex>> = + Mutex::new(RefCell::new(VecDeque::new())); /// Common errors. #[derive(Debug, Clone, Copy)] @@ -1526,7 +1445,7 @@ static mut G_CONFIG: wifi_init_config_t = wifi_init_config_t { rx_mgmt_buf_type: esp_wifi_sys::include::CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF as i32, rx_mgmt_buf_num: esp_wifi_sys::include::CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF as i32, cache_tx_buf_num: esp_wifi_sys::include::WIFI_CACHE_TX_BUFFER_NUM as i32, - csi_enable: esp_wifi_sys::include::WIFI_CSI_ENABLED as i32, + csi_enable: crate::CONFIG.csi_enable as i32, ampdu_rx_enable: crate::CONFIG.ampdu_rx_enable as i32, ampdu_tx_enable: crate::CONFIG.ampdu_tx_enable as i32, amsdu_tx_enable: crate::CONFIG.amsdu_tx_enable as i32, @@ -1546,14 +1465,14 @@ static mut G_CONFIG: wifi_init_config_t = wifi_init_config_t { }; /// Get the STA MAC address -pub fn get_sta_mac(mac: &mut [u8; 6]) { +pub fn sta_mac(mac: &mut [u8; 6]) { unsafe { read_mac(mac as *mut u8, 0); } } /// Get the AP MAC address -pub fn get_ap_mac(mac: &mut [u8; 6]) { +pub fn ap_mac(mac: &mut [u8; 6]) { unsafe { read_mac(mac as *mut u8, 1); } @@ -1591,10 +1510,19 @@ pub(crate) fn wifi_init() -> Result<(), WifiError> { chip_specific::g_misc_nvs = addr_of!(NVS_STRUCT) as u32; } + crate::flags::WIFI.fetch_add(1, Ordering::SeqCst); + Ok(()) } } +pub(crate) fn wifi_deinit() -> Result<(), crate::InitializationError> { + esp_wifi_result!(unsafe { esp_wifi_stop() })?; + esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?; + esp_wifi_result!(unsafe { esp_supplicant_deinit() })?; + Ok(()) +} + unsafe extern "C" fn recv_cb_sta( buffer: *mut c_types::c_void, len: u16, @@ -1607,16 +1535,20 @@ unsafe extern "C" fn recv_cb_sta( // which will try to lock an internal mutex. If the mutex is already taken, // the function will try to trigger a context switch, which will fail if we // are in a critical section. - match critical_section::with(|cs| DATA_QUEUE_RX_STA.borrow_ref_mut(cs).enqueue(packet)) { - Ok(_) => { - #[cfg(feature = "embassy-net")] - embassy::STA_RECEIVE_WAKER.wake(); - include::ESP_OK as esp_err_t - } - Err(_) => { - debug!("RX QUEUE FULL"); - include::ESP_ERR_NO_MEM as esp_err_t + if critical_section::with(|cs| { + let mut queue = DATA_QUEUE_RX_STA.borrow_ref_mut(cs); + if queue.len() < RX_QUEUE_SIZE { + queue.push_back(packet); + true + } else { + false } + }) { + embassy::STA_RECEIVE_WAKER.wake(); + include::ESP_OK as esp_err_t + } else { + debug!("RX QUEUE FULL"); + include::ESP_ERR_NO_MEM as esp_err_t } } @@ -1632,27 +1564,31 @@ unsafe extern "C" fn recv_cb_ap( // which will try to lock an internal mutex. If the mutex is already taken, // the function will try to trigger a context switch, which will fail if we // are in a critical section. - match critical_section::with(|cs| DATA_QUEUE_RX_AP.borrow_ref_mut(cs).enqueue(packet)) { - Ok(_) => { - #[cfg(feature = "embassy-net")] - embassy::AP_RECEIVE_WAKER.wake(); - include::ESP_OK as esp_err_t - } - Err(_) => { - debug!("RX QUEUE FULL"); - include::ESP_ERR_NO_MEM as esp_err_t + if critical_section::with(|cs| { + let mut queue = DATA_QUEUE_RX_AP.borrow_ref_mut(cs); + if queue.len() < RX_QUEUE_SIZE { + queue.push_back(packet); + true + } else { + false } + }) { + embassy::AP_RECEIVE_WAKER.wake(); + include::ESP_OK as esp_err_t + } else { + debug!("RX QUEUE FULL"); + include::ESP_ERR_NO_MEM as esp_err_t } } pub(crate) static WIFI_TX_INFLIGHT: AtomicUsize = AtomicUsize::new(0); fn decrement_inflight_counter() { - WIFI_TX_INFLIGHT - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + unwrap!( + WIFI_TX_INFLIGHT.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { Some(x.saturating_sub(1)) }) - .unwrap(); + ); } #[ram] @@ -1666,7 +1602,6 @@ unsafe extern "C" fn esp_wifi_tx_done_cb( decrement_inflight_counter(); - #[cfg(feature = "embassy-net")] embassy::TRANSMIT_WAKER.wake(); } @@ -1690,20 +1625,6 @@ pub(crate) fn wifi_start() -> Result<(), WifiError> { ))?; }; - cfg_if::cfg_if! { - if #[cfg(feature = "ps-min-modem")] { - let ps_mode = include::wifi_ps_type_t_WIFI_PS_MIN_MODEM; - } else if #[cfg(feature = "ps-max-modem")] { - let ps_mode = include::wifi_ps_type_t_WIFI_PS_MAX_MODEM; - } else if #[cfg(coex)] { - let ps_mode = include::wifi_ps_type_t_WIFI_PS_MIN_MODEM; - } else { - let ps_mode = include::wifi_ps_type_t_WIFI_PS_NONE; - } - }; - - esp_wifi_result!(esp_wifi_set_ps(ps_mode))?; - let mut cntry_code = [0u8; 3]; cntry_code[..crate::CONFIG.country_code.len()] .copy_from_slice(crate::CONFIG.country_code.as_bytes()); @@ -1872,7 +1793,7 @@ pub(crate) fn wifi_start_scan( /// /// If you want to use AP-STA mode, use `[new_ap_sta]`. pub fn new_with_config<'d, MODE: WifiDeviceMode>( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, device: impl Peripheral

+ 'd, config: MODE::Config, ) -> Result<(WifiDevice<'d, MODE>, WifiController<'d>), WifiError> { @@ -1890,8 +1811,8 @@ pub fn new_with_config<'d, MODE: WifiDeviceMode>( /// This function will panic if the mode is [`WifiMode::ApSta`]. /// If you want to use AP-STA mode, use `[new_ap_sta]`. pub fn new_with_mode<'d, MODE: WifiDeviceMode>( - inited: &EspWifiInitialization, - device: impl crate::hal::peripheral::Peripheral

+ 'd, + inited: &'d EspWifiController<'d>, + device: impl Peripheral

+ 'd, _mode: MODE, ) -> Result<(WifiDevice<'d, MODE>, WifiController<'d>), WifiError> { new_with_config(inited, device, ::Config::default()) @@ -1902,7 +1823,7 @@ pub fn new_with_mode<'d, MODE: WifiDeviceMode>( /// /// Returns a tuple of `(AP device, STA device, controller)`. pub fn new_ap_sta<'d>( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, device: impl Peripheral

+ 'd, ) -> Result< ( @@ -1919,7 +1840,7 @@ pub fn new_ap_sta<'d>( /// /// Returns a tuple of `(AP device, STA device, controller)`. pub fn new_ap_sta_with_config<'d>( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, device: impl Peripheral

+ 'd, sta_config: crate::wifi::ClientConfiguration, ap_config: crate::wifi::AccessPointConfiguration, @@ -1984,10 +1905,7 @@ mod sealed { fn wrap_config(config: Self::Config) -> Configuration; - fn data_queue_rx( - self, - cs: CriticalSection, - ) -> RefMut<'_, SimpleQueue>; + fn data_queue_rx(self, cs: CriticalSection) -> RefMut<'_, VecDeque>; fn can_send(self) -> bool { WIFI_TX_INFLIGHT.load(Ordering::SeqCst) < TX_QUEUE_SIZE @@ -2027,18 +1945,14 @@ mod sealed { fn interface(self) -> wifi_interface_t; - #[cfg(feature = "embassy-net")] fn register_transmit_waker(self, cx: &mut core::task::Context) { embassy::TRANSMIT_WAKER.register(cx.waker()) } - #[cfg(feature = "embassy-net")] fn register_receive_waker(self, cx: &mut core::task::Context); - #[cfg(feature = "embassy-net")] fn register_link_state_waker(self, cx: &mut core::task::Context); - #[cfg(feature = "embassy-net")] fn link_state(self) -> embassy_net_driver::LinkState; } @@ -2053,10 +1967,7 @@ mod sealed { Configuration::Client(config) } - fn data_queue_rx( - self, - cs: CriticalSection, - ) -> RefMut<'_, SimpleQueue> { + fn data_queue_rx(self, cs: CriticalSection) -> RefMut<'_, VecDeque> { DATA_QUEUE_RX_STA.borrow_ref_mut(cs) } @@ -2064,19 +1975,16 @@ mod sealed { wifi_interface_t_WIFI_IF_STA } - #[cfg(feature = "embassy-net")] fn register_receive_waker(self, cx: &mut core::task::Context) { embassy::STA_RECEIVE_WAKER.register(cx.waker()); } - #[cfg(feature = "embassy-net")] fn register_link_state_waker(self, cx: &mut core::task::Context) { embassy::STA_LINK_STATE_WAKER.register(cx.waker()); } - #[cfg(feature = "embassy-net")] fn link_state(self) -> embassy_net_driver::LinkState { - if matches!(get_sta_state(), WifiState::StaConnected) { + if matches!(sta_state(), WifiState::StaConnected) { embassy_net_driver::LinkState::Up } else { embassy_net_driver::LinkState::Down @@ -2095,10 +2003,7 @@ mod sealed { Configuration::AccessPoint(config) } - fn data_queue_rx( - self, - cs: CriticalSection, - ) -> RefMut<'_, SimpleQueue> { + fn data_queue_rx(self, cs: CriticalSection) -> RefMut<'_, VecDeque> { DATA_QUEUE_RX_AP.borrow_ref_mut(cs) } @@ -2106,19 +2011,16 @@ mod sealed { wifi_interface_t_WIFI_IF_AP } - #[cfg(feature = "embassy-net")] fn register_receive_waker(self, cx: &mut core::task::Context) { embassy::AP_RECEIVE_WAKER.register(cx.waker()); } - #[cfg(feature = "embassy-net")] fn register_link_state_waker(self, cx: &mut core::task::Context) { embassy::AP_LINK_STATE_WAKER.register(cx.waker()); } - #[cfg(feature = "embassy-net")] fn link_state(self) -> embassy_net_driver::LinkState { - if matches!(get_ap_state(), WifiState::ApStarted) { + if matches!(ap_state(), WifiState::ApStarted) { embassy_net_driver::LinkState::Up } else { embassy_net_driver::LinkState::Down @@ -2150,7 +2052,7 @@ impl WifiDeviceMode for WifiStaDevice { fn mac_address(self) -> [u8; 6] { let mut mac = [0; 6]; - get_sta_mac(&mut mac); + sta_mac(&mut mac); mac } } @@ -2167,7 +2069,7 @@ impl WifiDeviceMode for WifiApDevice { fn mac_address(self) -> [u8; 6] { let mut mac = [0; 6]; - get_ap_mac(&mut mac); + ap_mac(&mut mac); mac } } @@ -2451,8 +2353,9 @@ impl Sniffer { pub(crate) fn new() -> Self { // This shouldn't fail, since the way this is created, means that wifi will // always be initialized. - esp_wifi_result!(unsafe { esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb)) }) - .unwrap(); + unwrap!(esp_wifi_result!(unsafe { + esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb)) + })); Self { promiscuous_mode_enabled: AtomicBool::new(false), } @@ -2496,14 +2399,29 @@ pub struct WifiController<'d> { sniffer_taken: AtomicBool, } +impl<'d> Drop for WifiController<'d> { + fn drop(&mut self) { + if unwrap!( + crate::flags::WIFI.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + Some(x.saturating_sub(1)) + }) + ) == 0 + { + if let Err(e) = crate::wifi::wifi_deinit() { + warn!("Failed to cleanly deinit wifi: {:?}", e); + } + } + } +} + impl<'d> WifiController<'d> { pub(crate) fn new_with_config( - inited: &EspWifiInitialization, + inited: &'d EspWifiController<'d>, _device: PeripheralRef<'d, crate::hal::peripherals::WIFI>, config: Configuration, ) -> Result { - if !inited.is_wifi() { - return Err(WifiError::NotInitialized); + if !inited.wifi() { + crate::wifi::wifi_init()?; } // We set up the controller with the default config because we need to call @@ -2539,6 +2457,20 @@ impl<'d> WifiController<'d> { } } + /// Set CSI configuration and register the receiving callback. + #[cfg(csi_enable)] + pub fn set_csi( + &mut self, + mut csi: CsiConfig, + cb: impl FnMut(crate::wifi::wifi_csi_info_t) + Send, + ) -> Result<(), WifiError> { + csi.apply_config()?; + csi.set_receive_cb(cb)?; + csi.set_csi(true)?; + + Ok(()) + } + /// Set the wifi protocol. /// /// This will set the wifi protocol to the desired protocol, the default for @@ -2595,6 +2527,12 @@ impl<'d> WifiController<'d> { Ok(()) } + #[cfg(not(coex))] + /// Configures modem power saving + pub fn set_power_saving(&mut self, ps: PowerSaveMode) -> Result<(), WifiError> { + apply_power_saving(ps) + } + /// Checks if Wi-Fi is enabled as a station. pub fn is_sta_enabled(&self) -> Result { WifiMode::try_from(&self.config).map(|m| m.is_sta()) @@ -2660,6 +2598,33 @@ impl<'d> WifiController<'d> { Ok(scanned) } + + /// A blocking wifi network scan with default scanning options. + pub fn scan_n( + &mut self, + ) -> Result<(heapless::Vec, usize), WifiError> { + self.scan_with_config_sync(Default::default()) + } + + /// Starts the WiFi controller. + pub fn start(&mut self) -> Result<(), WifiError> { + crate::wifi::wifi_start() + } + + /// Stops the WiFi controller. + pub fn stop(&mut self) -> Result<(), WifiError> { + self.stop_impl() + } + + /// Connects the WiFi controller to a network. + pub fn connect(&mut self) -> Result<(), WifiError> { + self.connect_impl() + } + + /// Disconnects the WiFi controller from a network. + pub fn disconnect(&mut self) -> Result<(), WifiError> { + self.disconnect_impl() + } } // see https://docs.rs/smoltcp/0.7.1/smoltcp/phy/index.html @@ -2714,7 +2679,7 @@ impl WifiRxToken { let mut queue = self.mode.data_queue_rx(cs); unwrap!( - queue.dequeue(), + queue.pop_front(), "unreachable: transmit()/receive() ensures there is a packet to process" ) }); @@ -3021,7 +2986,7 @@ fn apply_sta_eap_config(config: &EapClientConfiguration) -> Result<(), WifiError impl WifiController<'_> { /// Get the supported capabilities of the controller. - pub fn get_capabilities(&self) -> Result, WifiError> { + pub fn capabilities(&self) -> Result, WifiError> { let caps = match self.config { Configuration::None => unreachable!(), Configuration::Client(_) => enumset::enum_set! { Capability::Client }, @@ -3036,7 +3001,7 @@ impl WifiController<'_> { } /// Get the currently used configuration. - pub fn get_configuration(&self) -> Result { + pub fn configuration(&self) -> Result { Ok(self.config.clone()) } @@ -3106,12 +3071,12 @@ impl WifiController<'_> { /// WiFi has started successfully. pub fn is_started(&self) -> Result { if matches!( - crate::wifi::get_sta_state(), + crate::wifi::sta_state(), WifiState::StaStarted | WifiState::StaConnected | WifiState::StaDisconnected ) { return Ok(true); } - if matches!(crate::wifi::get_ap_state(), WifiState::ApStarted) { + if matches!(crate::wifi::ap_state(), WifiState::ApStarted) { return Ok(true); } Ok(false) @@ -3122,7 +3087,7 @@ impl WifiController<'_> { /// This function should be called after the `connect` method to verify if /// the connection was successful. pub fn is_connected(&self) -> Result { - match crate::wifi::get_sta_state() { + match crate::wifi::sta_state() { crate::wifi::WifiState::StaConnected => Ok(true), crate::wifi::WifiState::StaDisconnected => Err(WifiError::Disconnected), // FIXME: Should any other enum value trigger an error instead of returning false? @@ -3131,38 +3096,8 @@ impl WifiController<'_> { } } -#[cfg(not(feature = "async"))] -impl WifiController<'_> { - /// A blocking wifi network scan with default scanning options. - pub fn scan_n( - &mut self, - ) -> Result<(heapless::Vec, usize), WifiError> { - self.scan_with_config_sync(Default::default()) - } - - /// Starts the WiFi controller. - pub fn start(&mut self) -> Result<(), WifiError> { - crate::wifi::wifi_start() - } - - /// Stops the WiFi controller. - pub fn stop(&mut self) -> Result<(), WifiError> { - self.stop_impl() - } - - /// Connects the WiFi controller to a network. - pub fn connect(&mut self) -> Result<(), WifiError> { - self.connect_impl() - } - - /// Disconnects the WiFi controller from a network. - pub fn disconnect(&mut self) -> Result<(), WifiError> { - self.disconnect_impl() - } -} - fn dump_packet_info(_buffer: &[u8]) { - #[cfg(feature = "dump-packets")] + #[cfg(dump_packets)] { info!("@WIFIFRAME {:?}", _buffer); } @@ -3172,8 +3107,9 @@ fn dump_packet_info(_buffer: &[u8]) { #[macro_export] macro_rules! esp_wifi_result { ($value:expr) => {{ + use num_traits::FromPrimitive; let result = $value; - if result != include::ESP_OK as i32 { + if result != esp_wifi_sys::include::ESP_OK as i32 { warn!("{} returned an error: {}", stringify!($value), result); Err(WifiError::InternalError(unwrap!(FromPrimitive::from_i32( result @@ -3184,7 +3120,6 @@ macro_rules! esp_wifi_result { }}; } -#[cfg(feature = "embassy-net")] pub(crate) mod embassy { use embassy_net_driver::{Capabilities, Driver, HardwareAddress, RxToken, TxToken}; use embassy_sync::waitqueue::AtomicWaker; @@ -3265,7 +3200,12 @@ pub(crate) mod embassy { } } -#[cfg(feature = "async")] +#[cfg(not(coex))] +pub(crate) fn apply_power_saving(ps: PowerSaveMode) -> Result<(), WifiError> { + esp_wifi_result!(unsafe { esp_wifi_sys::include::esp_wifi_set_ps(ps.into()) })?; + Ok(()) +} + mod asynch { use core::task::Poll; @@ -3274,16 +3214,16 @@ mod asynch { use super::*; // TODO assumes STA mode only - impl<'d> WifiController<'d> { + impl WifiController<'_> { /// Async version of [`crate::wifi::WifiController`]'s `scan_n` method - pub async fn scan_n( + pub async fn scan_n_async( &mut self, ) -> Result<(heapless::Vec, usize), WifiError> { - self.scan_with_config(Default::default()).await + self.scan_with_config_async(Default::default()).await } /// An async wifi network scan with caller-provided scanning options. - pub async fn scan_with_config( + pub async fn scan_with_config_async( &mut self, config: ScanConfig<'_>, ) -> Result<(heapless::Vec, usize), WifiError> { @@ -3303,7 +3243,7 @@ mod asynch { } /// Async version of [`crate::wifi::WifiController`]'s `start` method - pub async fn start(&mut self) -> Result<(), WifiError> { + pub async fn start_async(&mut self) -> Result<(), WifiError> { let mode = WifiMode::try_from(&self.config)?; let mut events = enumset::enum_set! {}; @@ -3324,7 +3264,7 @@ mod asynch { } /// Async version of [`crate::wifi::WifiController`]'s `stop` method - pub async fn stop(&mut self) -> Result<(), WifiError> { + pub async fn stop_async(&mut self) -> Result<(), WifiError> { let mode = WifiMode::try_from(&self.config)?; let mut events = enumset::enum_set! {}; @@ -3348,7 +3288,7 @@ mod asynch { } /// Async version of [`crate::wifi::WifiController`]'s `connect` method - pub async fn connect(&mut self) -> Result<(), WifiError> { + pub async fn connect_async(&mut self) -> Result<(), WifiError> { Self::clear_events(WifiEvent::StaConnected | WifiEvent::StaDisconnected); let err = crate::wifi::WifiController::connect_impl(self).err(); @@ -3365,7 +3305,7 @@ mod asynch { /// Async version of [`crate::wifi::WifiController`]'s `Disconnect` /// method - pub async fn disconnect(&mut self) -> Result<(), WifiError> { + pub async fn disconnect_async(&mut self) -> Result<(), WifiError> { // If not connected, this will do nothing. // It will also wait forever for a `StaDisconnected` event that will never come. // Return early instead of hanging. diff --git a/esp-wifi/src/wifi/os_adapter.rs b/esp-wifi/src/wifi/os_adapter.rs index cbed1db74cb..1fe92992fda 100644 --- a/esp-wifi/src/wifi/os_adapter.rs +++ b/esp-wifi/src/wifi/os_adapter.rs @@ -14,7 +14,6 @@ use enumset::EnumSet; use super::WifiEvent; use crate::{ - common_adapter::RADIO_CLOCKS, compat::{ common::{ create_queue, @@ -27,6 +26,7 @@ use crate::{ str_from_c, thread_sem_get, unlock_mutex, + ConcurrentQueue, }, malloc::calloc, }, @@ -35,7 +35,7 @@ use crate::{ timer::yield_task, }; -static mut QUEUE_HANDLE: *mut crate::binary::c_types::c_void = core::ptr::null_mut(); +static mut QUEUE_HANDLE: *mut ConcurrentQueue = core::ptr::null_mut(); // useful for waiting for events - clear and wait for the event bit to be set // again @@ -391,7 +391,7 @@ pub unsafe extern "C" fn queue_create( (3, 8) }; - create_queue(queue_len as i32, item_size as i32) + create_queue(queue_len as i32, item_size as i32).cast() } /// ************************************************************************** @@ -408,7 +408,7 @@ pub unsafe extern "C" fn queue_create( /// /// ************************************************************************* pub unsafe extern "C" fn queue_delete(queue: *mut crate::binary::c_types::c_void) { - delete_queue(queue); + delete_queue(queue.cast()); } /// ************************************************************************** @@ -431,7 +431,7 @@ pub unsafe extern "C" fn queue_send( item: *mut crate::binary::c_types::c_void, block_time_tick: u32, ) -> i32 { - send_queued(queue, item, block_time_tick) + send_queued(queue.cast(), item, block_time_tick) } /// ************************************************************************** @@ -526,7 +526,7 @@ pub unsafe extern "C" fn queue_recv( item: *mut crate::binary::c_types::c_void, block_time_tick: u32, ) -> i32 { - receive_queued(queue, item, block_time_tick) + receive_queued(queue.cast(), item, block_time_tick) } /// ************************************************************************** @@ -543,7 +543,7 @@ pub unsafe extern "C" fn queue_recv( /// /// ************************************************************************* pub unsafe extern "C" fn queue_msg_waiting(queue: *mut crate::binary::c_types::c_void) -> u32 { - number_of_messages_in_queue(queue) + number_of_messages_in_queue(queue.cast()) } /// ************************************************************************** @@ -734,7 +734,7 @@ pub unsafe extern "C" fn task_delete(task_handle: *mut crate::binary::c_types::c /// ************************************************************************* pub unsafe extern "C" fn task_delay(tick: u32) { trace!("task_delay tick {}", tick); - let start_time = crate::timer::get_systimer_count(); + let start_time = crate::timer::systimer_count(); while crate::timer::elapsed_time_since(start_time) < tick as u64 { yield_task(); } @@ -866,14 +866,17 @@ pub unsafe extern "C" fn event_post( let event = unwrap!(WifiEvent::from_i32(event_id)); trace!("EVENT: {:?}", event); - critical_section::with(|cs| WIFI_EVENTS.borrow_ref_mut(cs).insert(event)); - super::state::update_state(event); + let mut handled = false; + critical_section::with(|cs| { + WIFI_EVENTS.borrow_ref_mut(cs).insert(event); + handled = super::event::dispatch_event_handler(cs, event, event_data, event_data_size); + }); + + super::state::update_state(event, handled); - #[cfg(feature = "async")] event.waker().wake(); - #[cfg(feature = "embassy-net")] match event { WifiEvent::StaConnected | WifiEvent::StaDisconnected => { crate::wifi::embassy::STA_LINK_STATE_WAKER.wake(); @@ -1040,7 +1043,10 @@ pub unsafe extern "C" fn phy_update_country_info( /// ************************************************************************* pub unsafe extern "C" fn wifi_reset_mac() { trace!("wifi_reset_mac"); - unwrap!(RADIO_CLOCKS.as_mut()).reset_mac(); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.reset_mac(); } /// ************************************************************************** @@ -1058,7 +1064,10 @@ pub unsafe extern "C" fn wifi_reset_mac() { /// ************************************************************************* pub unsafe extern "C" fn wifi_clock_enable() { trace!("wifi_clock_enable"); - unwrap!(RADIO_CLOCKS.as_mut()).enable(RadioPeripherals::Wifi); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.enable(RadioPeripherals::Wifi); } /// ************************************************************************** @@ -1076,7 +1085,10 @@ pub unsafe extern "C" fn wifi_clock_enable() { /// ************************************************************************* pub unsafe extern "C" fn wifi_clock_disable() { trace!("wifi_clock_disable"); - unwrap!(RADIO_CLOCKS.as_mut()).disable(RadioPeripherals::Wifi); + // stealing RADIO_CLK is safe since it is passed (as mutable reference or by + // value) into `init` + let mut radio_clocks = unsafe { esp_hal::peripherals::RADIO_CLK::steal() }; + radio_clocks.disable(RadioPeripherals::Wifi); } /// ************************************************************************** @@ -1114,7 +1126,7 @@ pub unsafe extern "C" fn wifi_rtc_disable_iso() { #[no_mangle] pub unsafe extern "C" fn esp_timer_get_time() -> i64 { trace!("esp_timer_get_time"); - crate::timer::ticks_to_micros(crate::timer::get_systimer_count()) as i64 + crate::timer::ticks_to_micros(crate::timer::systimer_count()) as i64 } /// ************************************************************************** @@ -1394,18 +1406,8 @@ pub unsafe extern "C" fn nvs_erase_key( /// ************************************************************************* pub unsafe extern "C" fn get_random(buf: *mut u8, len: usize) -> crate::binary::c_types::c_int { trace!("get_random"); - if let Some(ref mut rng) = crate::common_adapter::RANDOM_GENERATOR { - let buffer = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - - for chunk in buffer.chunks_mut(4) { - let bytes = rng.random().to_le_bytes(); - chunk.copy_from_slice(&bytes[..chunk.len()]); - } - - 0 - } else { - -1 - } + crate::common_adapter::esp_fill_random(buf, len as u32); + 0 } /// ************************************************************************** diff --git a/esp-wifi/src/wifi/state.rs b/esp-wifi/src/wifi/state.rs index 58d8d4441f9..5180bbf18be 100644 --- a/esp-wifi/src/wifi/state.rs +++ b/esp-wifi/src/wifi/state.rs @@ -38,16 +38,16 @@ pub(crate) static STA_STATE: AtomicWifiState = AtomicWifiState::new(WifiState::I pub(crate) static AP_STATE: AtomicWifiState = AtomicWifiState::new(WifiState::Invalid); /// Get the current state of the AP -pub fn get_ap_state() -> WifiState { +pub fn ap_state() -> WifiState { AP_STATE.load(Ordering::Relaxed) } /// Get the current state of the STA -pub fn get_sta_state() -> WifiState { +pub fn sta_state() -> WifiState { STA_STATE.load(Ordering::Relaxed) } -pub(crate) fn update_state(event: WifiEvent) { +pub(crate) fn update_state(event: WifiEvent, handled: bool) { match event { WifiEvent::StaConnected | WifiEvent::StaDisconnected @@ -58,29 +58,31 @@ pub(crate) fn update_state(event: WifiEvent) { AP_STATE.store(WifiState::from(event), Ordering::Relaxed) } - other => debug!("Unhandled event: {:?}", other), + other => { + if !handled { + debug!("Unhandled event: {:?}", other) + } + } } } -#[cfg(feature = "async")] pub(crate) fn reset_ap_state() { AP_STATE.store(WifiState::Invalid, Ordering::Relaxed) } -#[cfg(feature = "async")] pub(crate) fn reset_sta_state() { STA_STATE.store(WifiState::Invalid, Ordering::Relaxed) } /// Returns the current state of the WiFi stack. /// -/// This does not support AP-STA mode. Use one of `get_sta_state` or -/// `get_ap_state` instead. -pub fn get_wifi_state() -> WifiState { +/// This does not support AP-STA mode. Use one of `sta_state` or +/// `ap_state` instead. +pub fn wifi_state() -> WifiState { use super::WifiMode; match WifiMode::current() { - Ok(WifiMode::Sta) => get_sta_state(), - Ok(WifiMode::Ap) => get_ap_state(), + Ok(WifiMode::Sta) => sta_state(), + Ok(WifiMode::Ap) => ap_state(), _ => WifiState::Invalid, } } diff --git a/esp-wifi/src/wifi/utils.rs b/esp-wifi/src/wifi/utils.rs index 1eb5da3a247..b7d8831bfd7 100644 --- a/esp-wifi/src/wifi/utils.rs +++ b/esp-wifi/src/wifi/utils.rs @@ -1,83 +1,59 @@ //! Convenience utilities for non-async code -#[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4::Socket as Dhcpv4Socket; use smoltcp::{ - iface::{Config, Interface, SocketSet, SocketStorage}, + iface::{Config, Interface}, wire::{EthernetAddress, HardwareAddress}, }; use super::{WifiApDevice, WifiController, WifiDevice, WifiDeviceMode, WifiError, WifiStaDevice}; -use crate::{timestamp, EspWifiInitialization}; +use crate::EspWifiController; -fn setup_iface<'a, MODE: WifiDeviceMode>( - device: &mut WifiDevice<'_, MODE>, - mode: MODE, - storage: &'a mut [SocketStorage<'a>], -) -> (Interface, SocketSet<'a>) { +// [esp_hal::time::now()] as a smoltcp [`Instant]` +#[cfg(feature = "smoltcp")] +fn timestamp() -> smoltcp::time::Instant { + smoltcp::time::Instant::from_micros( + esp_hal::time::now().duration_since_epoch().to_micros() as i64 + ) +} + +fn setup_iface(device: &mut WifiDevice<'_, MODE>, mode: MODE) -> Interface { let mac = mode.mac_address(); let hw_address = HardwareAddress::Ethernet(EthernetAddress::from_bytes(&mac)); let config = Config::new(hw_address); let iface = Interface::new(config, device, timestamp()); - - #[allow(unused_mut)] - let mut socket_set = SocketSet::new(storage); - - #[cfg(feature = "dhcpv4")] - if mode.mode().is_sta() { - // only add DHCP client in STA mode - let dhcp_socket = Dhcpv4Socket::new(); - socket_set.add(dhcp_socket); - } - - (iface, socket_set) + iface } /// Convenient way to create an `smoltcp` ethernet interface -/// You can use the provided macros to create and pass a suitable backing -/// storage. -pub fn create_network_interface<'a, 'd, MODE: WifiDeviceMode>( - inited: &EspWifiInitialization, +pub fn create_network_interface<'d, MODE: WifiDeviceMode>( + inited: &'d EspWifiController<'d>, device: impl crate::hal::peripheral::Peripheral

+ 'd, mode: MODE, - storage: &'a mut [SocketStorage<'a>], -) -> Result< - ( - Interface, - WifiDevice<'d, MODE>, - WifiController<'d>, - SocketSet<'a>, - ), - WifiError, -> { +) -> Result<(Interface, WifiDevice<'d, MODE>, WifiController<'d>), WifiError> { let (mut device, controller) = crate::wifi::new_with_mode(inited, device, mode)?; - let (iface, socket_set) = setup_iface(&mut device, mode, storage); + let iface = setup_iface(&mut device, mode); - Ok((iface, device, controller, socket_set)) + Ok((iface, device, controller)) } -pub struct ApStaInterface<'a, 'd> { +pub struct ApStaInterface<'d> { pub ap_interface: Interface, pub sta_interface: Interface, pub ap_device: WifiDevice<'d, WifiApDevice>, pub sta_device: WifiDevice<'d, WifiStaDevice>, pub controller: WifiController<'d>, - pub ap_socket_set: SocketSet<'a>, - pub sta_socket_set: SocketSet<'a>, } -pub fn create_ap_sta_network_interface<'a, 'd>( - inited: &EspWifiInitialization, +pub fn create_ap_sta_network_interface<'d>( + inited: &'d EspWifiController<'d>, device: impl crate::hal::peripheral::Peripheral

+ 'd, - ap_storage: &'a mut [SocketStorage<'a>], - sta_storage: &'a mut [SocketStorage<'a>], -) -> Result, WifiError> { +) -> Result, WifiError> { let (mut ap_device, mut sta_device, controller) = crate::wifi::new_ap_sta(inited, device)?; - let (ap_interface, ap_socket_set) = setup_iface(&mut ap_device, WifiApDevice, ap_storage); - let (sta_interface, sta_socket_set) = setup_iface(&mut sta_device, WifiStaDevice, sta_storage); + let ap_interface = setup_iface(&mut ap_device, WifiApDevice); + let sta_interface = setup_iface(&mut sta_device, WifiStaDevice); Ok(ApStaInterface { ap_interface, @@ -85,7 +61,5 @@ pub fn create_ap_sta_network_interface<'a, 'd>( ap_device, sta_device, controller, - ap_socket_set, - sta_socket_set, }) } diff --git a/esp-wifi/src/wifi_interface.rs b/esp-wifi/src/wifi_interface.rs deleted file mode 100644 index ddc4ed26164..00000000000 --- a/esp-wifi/src/wifi_interface.rs +++ /dev/null @@ -1,995 +0,0 @@ -//! Non-async Networking primitives for TCP/UDP communication. - -use core::{borrow::BorrowMut, cell::RefCell, fmt::Display}; - -#[cfg(feature = "dhcpv4")] -use smoltcp::socket::dhcpv4::Socket as Dhcpv4Socket; -#[cfg(feature = "tcp")] -use smoltcp::socket::tcp::Socket as TcpSocket; -#[cfg(feature = "dns")] -use smoltcp::wire::DnsQueryType; -#[cfg(feature = "udp")] -use smoltcp::wire::IpEndpoint; -use smoltcp::{ - iface::{Interface, SocketHandle, SocketSet}, - time::Instant, - wire::{IpAddress, IpCidr, Ipv4Address}, -}; - -use crate::{ - timestamp, - wifi::{ipv4, WifiDevice, WifiDeviceMode}, -}; - -#[cfg(feature = "tcp")] -const LOCAL_PORT_MIN: u16 = 41000; -#[cfg(feature = "tcp")] -const LOCAL_PORT_MAX: u16 = 65535; - -/// Non-async TCP/IP network stack -/// -/// Mostly a convenience wrapper for `smoltcp` -pub struct WifiStack<'a, MODE: WifiDeviceMode> { - device: RefCell>, - network_interface: RefCell, - sockets: RefCell>, - current_millis_fn: fn() -> u64, - #[cfg(feature = "tcp")] - local_port: RefCell, - pub(crate) network_config: RefCell, - pub(crate) ip_info: RefCell>, - #[cfg(feature = "dhcpv4")] - pub(crate) dhcp_socket_handle: RefCell>, - #[cfg(feature = "dhcpv4")] - pub(crate) old_connected: RefCell, - #[cfg(feature = "dns")] - dns_socket_handle: RefCell>, -} - -impl<'a, MODE: WifiDeviceMode> WifiStack<'a, MODE> { - /// Creates new `WifiStack` instance. - /// - /// Handles optional DHCP/DNS features and sets up the - /// configuration for the network interface. - pub fn new( - network_interface: Interface, - device: WifiDevice<'a, MODE>, - #[allow(unused_mut)] mut sockets: SocketSet<'a>, - current_millis_fn: fn() -> u64, - ) -> WifiStack<'a, MODE> { - #[cfg(feature = "dhcpv4")] - let mut dhcp_socket_handle: Option = None; - #[cfg(feature = "dns")] - let mut dns_socket_handle: Option = None; - - #[cfg(any(feature = "dhcpv4", feature = "dns"))] - for (handle, socket) in sockets.iter_mut() { - match socket { - #[cfg(feature = "dhcpv4")] - smoltcp::socket::Socket::Dhcpv4(_) => dhcp_socket_handle = Some(handle), - #[cfg(feature = "dns")] - smoltcp::socket::Socket::Dns(_) => dns_socket_handle = Some(handle), - _ => {} - } - } - - let this = Self { - device: RefCell::new(device), - network_interface: RefCell::new(network_interface), - network_config: RefCell::new(ipv4::Configuration::Client( - ipv4::ClientConfiguration::DHCP(ipv4::DHCPClientSettings { - // FIXME: smoltcp currently doesn't have a way of giving a hostname through DHCP - hostname: Some(unwrap!("Espressif".try_into().ok())), - }), - )), - ip_info: RefCell::new(None), - #[cfg(feature = "dhcpv4")] - dhcp_socket_handle: RefCell::new(dhcp_socket_handle), - #[cfg(feature = "dhcpv4")] - old_connected: RefCell::new(false), - sockets: RefCell::new(sockets), - current_millis_fn, - #[cfg(feature = "tcp")] - local_port: RefCell::new( - (unsafe { crate::common_adapter::random() } - % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u32) as u16 - + LOCAL_PORT_MIN, - ), - #[cfg(feature = "dns")] - dns_socket_handle: RefCell::new(dns_socket_handle), - }; - - this.reset(); - - this - } - - /// Update the interface configuration - pub fn update_iface_configuration( - &self, - conf: &ipv4::Configuration, - ) -> Result<(), WifiStackError> { - let mac = self.device.borrow().mac_address(); - let hw_address = smoltcp::wire::HardwareAddress::Ethernet( - smoltcp::wire::EthernetAddress::from_bytes(&mac), - ); - self.network_interface - .borrow_mut() - .set_hardware_addr(hw_address); - info!("Set hardware address: {:?}", hw_address); - - self.reset(); // reset IP address - - #[cfg(feature = "dhcpv4")] - { - let mut dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut(); - let mut sockets_ref = self.sockets.borrow_mut(); - - if let Some(dhcp_handle) = *dhcp_socket_handle_ref { - // remove the DHCP client if we use a static IP - if matches!( - conf, - ipv4::Configuration::Client(ipv4::ClientConfiguration::Fixed(_)) - ) { - sockets_ref.remove(dhcp_handle); - *dhcp_socket_handle_ref = None; - } - } - - // re-add the DHCP client if we use DHCP and it has been removed before - if matches!( - conf, - ipv4::Configuration::Client(ipv4::ClientConfiguration::DHCP(_)) - ) && dhcp_socket_handle_ref.is_none() - { - let dhcp_socket = Dhcpv4Socket::new(); - let dhcp_socket_handle = sockets_ref.add(dhcp_socket); - *dhcp_socket_handle_ref = Some(dhcp_socket_handle); - } - - if let Some(dhcp_handle) = *dhcp_socket_handle_ref { - let dhcp_socket = sockets_ref.get_mut::(dhcp_handle); - info!("Reset DHCP client"); - dhcp_socket.reset(); - } - } - - *self.network_config.borrow_mut() = conf.clone(); - Ok(()) - } - - /// Reset the stack - pub fn reset(&self) { - debug!("Reset TCP stack"); - - #[cfg(feature = "dhcpv4")] - { - let dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut(); - if let Some(dhcp_handle) = *dhcp_socket_handle_ref { - self.with_mut(|_, _, sockets| { - let dhcp_socket = sockets.get_mut::(dhcp_handle); - debug!("Reset DHCP client"); - dhcp_socket.reset(); - }); - } - } - - self.with_mut(|interface, _, _| { - interface.routes_mut().remove_default_ipv4_route(); - interface.update_ip_addrs(|addrs| { - addrs.clear(); - }); - - #[cfg(feature = "ipv6")] - { - unwrap!(interface.routes_mut().add_default_ipv6_route( - smoltcp::wire::Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0,) - )); - - let mut mac = [0u8; 6]; - match interface.hardware_addr() { - smoltcp::wire::HardwareAddress::Ethernet(hw_address) => { - mac.copy_from_slice(hw_address.as_bytes()); - } - } - - let a4 = ((mac[0] ^ 2) as u16) << 8 | mac[1] as u16; - let a5 = (mac[2] as u16) << 8 | 0xff; - let a6 = 0xfe << 8 | mac[3] as u16; - let a7 = (mac[4] as u16) << 8 | mac[5] as u16; - - info!( - "IPv6 link-local address fe80::{:x}:{:x}:{:x}:{:x}", - a4, a5, a6, a7 - ); - - interface.update_ip_addrs(|addrs| { - unwrap!(addrs.push(IpCidr::new( - smoltcp::wire::IpAddress::v6(0xfe80, 0, 0, 0, a4, a5, a6, a7), - 64, - ))); - }); - } - }); - } - - /// Retrieve all current IP addresses - pub fn get_ip_addresses(&self, f: impl FnOnce(&[smoltcp::wire::IpCidr])) { - self.with_mut(|interface, _, _| f(interface.ip_addrs())) - } - - /// Convenience function to poll the DHCP socket. - #[cfg(feature = "dhcpv4")] - pub fn poll_dhcp( - &self, - interface: &mut Interface, - sockets: &mut SocketSet<'a>, - ) -> Result<(), WifiStackError> { - let dhcp_socket_handle_ref = self.dhcp_socket_handle.borrow_mut(); - if let Some(dhcp_handle) = *dhcp_socket_handle_ref { - let dhcp_socket = sockets.get_mut::(dhcp_handle); - - let connected = matches!( - crate::wifi::get_sta_state(), - crate::wifi::WifiState::StaConnected - ); - - if connected && !*self.old_connected.borrow() { - dhcp_socket.reset(); - } - - *self.old_connected.borrow_mut() = connected; - - let event = dhcp_socket.poll(); - if let Some(event) = event { - match event { - smoltcp::socket::dhcpv4::Event::Deconfigured => { - *self.ip_info.borrow_mut() = None; - interface.routes_mut().remove_default_ipv4_route(); - } - smoltcp::socket::dhcpv4::Event::Configured(config) => { - let dns = config.dns_servers.first(); - *self.ip_info.borrow_mut() = Some(ipv4::IpInfo { - ip: config.address.address().0.into(), - subnet: ipv4::Subnet { - gateway: unwrap!(config.router).0.into(), - mask: ipv4::Mask(config.address.prefix_len()), - }, - dns: dns.map(|x| x.0.into()), - secondary_dns: config.dns_servers.get(1).map(|x| x.0.into()), - }); - - let address = config.address; - interface.borrow_mut().update_ip_addrs(|addrs| { - unwrap!(addrs.push(smoltcp::wire::IpCidr::Ipv4(address))); - }); - if let Some(route) = config.router { - unwrap!(interface.routes_mut().add_default_ipv4_route(route)); - } - - #[cfg(feature = "dns")] - if let (Some(&dns), Some(dns_handle)) = - (dns, *self.dns_socket_handle.borrow()) - { - sockets - .get_mut::(dns_handle) - .update_servers(&[dns.into()]); - } - } - } - } - } - - Ok(()) - } - - /// Create a new [Socket] - #[cfg(feature = "tcp")] - pub fn get_socket<'s>( - &'s self, - rx_buffer: &'a mut [u8], - tx_buffer: &'a mut [u8], - ) -> Socket<'s, 'a, MODE> - where - 'a: 's, - { - let socket = TcpSocket::new( - smoltcp::socket::tcp::SocketBuffer::new(rx_buffer), - smoltcp::socket::tcp::SocketBuffer::new(tx_buffer), - ); - - let socket_handle = - self.with_mut(|_interface, _device, sockets| sockets.borrow_mut().add(socket)); - - Socket { - socket_handle, - network: self, - } - } - - /// Create a new [UdpSocket] - #[cfg(feature = "udp")] - pub fn get_udp_socket<'s>( - &'s self, - rx_meta: &'a mut [smoltcp::socket::udp::PacketMetadata], - rx_buffer: &'a mut [u8], - tx_meta: &'a mut [smoltcp::socket::udp::PacketMetadata], - tx_buffer: &'a mut [u8], - ) -> UdpSocket<'s, 'a, MODE> - where - 'a: 's, - { - let socket = smoltcp::socket::udp::Socket::new( - smoltcp::socket::udp::PacketBuffer::new(rx_meta, rx_buffer), - smoltcp::socket::udp::PacketBuffer::new(tx_meta, tx_buffer), - ); - - let socket_handle = - self.with_mut(|_interface, _device, sockets| sockets.borrow_mut().add(socket)); - - UdpSocket { - socket_handle, - network: self, - } - } - - /// Check if DNS is configured - #[cfg(feature = "dns")] - pub fn is_dns_configured(&self) -> bool { - self.dns_socket_handle.borrow().is_some() - } - - /// Configure DNS - #[cfg(feature = "dns")] - pub fn configure_dns( - &'a self, - servers: &[IpAddress], - query_storage: &'a mut [Option], - ) { - if let Some(old_handle) = self.dns_socket_handle.take() { - self.with_mut(|_interface, _device, sockets| sockets.remove(old_handle)); - // the returned socket get dropped and frees a slot for the new one - } - - let dns = smoltcp::socket::dns::Socket::new(servers, query_storage); - let handle = self.with_mut(|_interface, _device, sockets| sockets.add(dns)); - self.dns_socket_handle.replace(Some(handle)); - } - - /// Update the DNS servers - #[cfg(feature = "dns")] - pub fn update_dns_servers(&self, servers: &[IpAddress]) { - if let Some(dns_handle) = *self.dns_socket_handle.borrow_mut() { - self.with_mut(|_interface, _device, sockets| { - sockets - .get_mut::(dns_handle) - .update_servers(servers); - }); - } - } - - /// Perform a DNS query - #[cfg(feature = "dns")] - pub fn dns_query( - &self, - name: &str, - query_type: DnsQueryType, - ) -> Result, WifiStackError> - { - use smoltcp::socket::dns; - - match query_type { - // check if name is already an IP - DnsQueryType::A => { - if let Ok(ip) = name.parse::() { - return Ok([ip.into()].into_iter().collect()); - } - } - #[cfg(feature = "ipv6")] - DnsQueryType::Aaaa => { - if let Ok(ip) = name.parse::() { - return Ok([ip.into()].into_iter().collect()); - } - } - _ => {} - } - - let Some(dns_handle) = *self.dns_socket_handle.borrow() else { - return Err(WifiStackError::DnsNotConfigured); - }; - - let query = self.with_mut(|interface, _device, sockets| { - sockets - .get_mut::(dns_handle) - .start_query(interface.context(), name, query_type) - .map_err(WifiStackError::DnsQueryError) - })?; - - loop { - self.work(); - - let result = self.with_mut(|_interface, _device, sockets| { - sockets - .get_mut::(dns_handle) - .get_query_result(query) - }); - - match result { - Ok(addrs) => return Ok(addrs), // query finished - Err(dns::GetQueryResultError::Pending) => {} // query not finished - Err(_) => return Err(WifiStackError::DnsQueryFailed), - } - } - } - - /// Let the stack make progress - /// - /// Make sure to regularly call this function. - pub fn work(&self) { - loop { - let did_work = self.with_mut(|interface, device, sockets| { - let network_config = self.network_config.borrow().clone(); - if let ipv4::Configuration::Client(ipv4::ClientConfiguration::DHCP(_)) = - network_config - { - #[cfg(feature = "dhcpv4")] - self.poll_dhcp(interface, sockets).ok(); - } else if let ipv4::Configuration::Client(ipv4::ClientConfiguration::Fixed( - settings, - )) = network_config - { - let addr = Ipv4Address::from_bytes(&settings.ip.octets()); - if !interface.has_ip_addr(addr) { - let gateway = Ipv4Address::from_bytes(&settings.subnet.gateway.octets()); - interface.routes_mut().add_default_ipv4_route(gateway).ok(); - interface.update_ip_addrs(|addrs| { - unwrap!(addrs.push(IpCidr::new(addr.into(), settings.subnet.mask.0))); - }); - } - } - interface.poll( - Instant::from_millis((self.current_millis_fn)() as i64), - device, - sockets, - ) - }); - - if !did_work { - break; - } - } - } - - #[cfg(feature = "tcp")] - fn next_local_port(&self) -> u16 { - self.local_port.replace_with(|local_port| { - if *local_port == LOCAL_PORT_MAX { - LOCAL_PORT_MIN - } else { - *local_port + 1 - } - }); - *self.local_port.borrow() - } - - #[allow(unused)] - fn with(&self, f: impl FnOnce(&Interface, &WifiDevice, &SocketSet<'a>) -> R) -> R { - f( - &self.network_interface.borrow(), - &self.device.borrow(), - &self.sockets.borrow(), - ) - } - - fn with_mut( - &self, - f: impl FnOnce(&mut Interface, &mut WifiDevice, &mut SocketSet<'a>) -> R, - ) -> R { - f( - &mut self.network_interface.borrow_mut(), - &mut self.device.borrow_mut(), - &mut self.sockets.borrow_mut(), - ) - } -} - -/// Errors returned by functions in this module -#[derive(Debug, Copy, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum WifiStackError { - /// An unknown error occurred, with the associated error code. - Unknown(i32), - - /// An error occurred during Wi-Fi stack initialization. - InitializationError(crate::InitializationError), - - /// A common Wi-Fi error occured. - DeviceError(crate::wifi::WifiError), - - /// Couldn't get the device's IP. - MissingIp, - - /// DNS is not configured. - #[cfg(feature = "dns")] - DnsNotConfigured, - - /// An error occurred when starting a DNS query. - #[cfg(feature = "dns")] - DnsQueryError(smoltcp::socket::dns::StartQueryError), - - /// Cannot get result from a DNS query. - #[cfg(feature = "dns")] - DnsQueryFailed, -} - -impl Display for WifiStackError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:?}", self) - } -} - -impl WifiStack<'_, MODE> { - /// Retrieves the current interface configuration. - pub fn get_iface_configuration(&self) -> Result { - Ok(self.network_config.borrow().clone()) - } - - /// Sets a new interface configuration using the provided IPv4 - /// configuration. - pub fn set_iface_configuration( - &mut self, - conf: &ipv4::Configuration, - ) -> Result<(), WifiStackError> { - self.update_iface_configuration(conf) - } - - /// Checks if the interface is up (has IP information). - pub fn is_iface_up(&self) -> bool { - self.ip_info.borrow().is_some() - } - - /// Retrieves the current IP information (IP address, subnet info). - pub fn get_ip_info(&self) -> Result { - self.ip_info.borrow().ok_or(WifiStackError::MissingIp) - } -} - -/// A TCP socket -#[cfg(feature = "tcp")] -pub struct Socket<'s, 'n: 's, MODE: WifiDeviceMode> { - socket_handle: SocketHandle, - network: &'s WifiStack<'n, MODE>, -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> Socket<'s, 'n, MODE> { - /// Connect the socket - pub fn open<'i>(&'i mut self, addr: IpAddress, port: u16) -> Result<(), IoError> - where - 's: 'i, - { - { - let res = self.network.with_mut(|interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - let cx = interface.context(); - let remote_endpoint = (addr, port); - sock.set_ack_delay(Some(smoltcp::time::Duration::from_millis(100))); - sock.connect(cx, remote_endpoint, self.network.next_local_port()) - }); - - res.map_err(IoError::ConnectError)?; - } - - loop { - let can_send = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.can_send() - }); - - if can_send { - break; - } - - self.work(); - } - - Ok(()) - } - - /// Listen on the given port. This blocks until there is a peer connected - pub fn listen<'i>(&'i mut self, port: u16) -> Result<(), IoError> - where - 's: 'i, - { - { - let res = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.listen(port) - }); - - res.map_err(IoError::ListenError)?; - } - - loop { - let can_send = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.can_send() - }); - - if can_send { - break; - } - - self.work(); - } - - Ok(()) - } - - /// Listen on the given port. This doesn't block - pub fn listen_unblocking<'i>(&'i mut self, port: u16) -> Result<(), IoError> - where - 's: 'i, - { - { - let res = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.listen(port) - }); - - res.map_err(IoError::ListenError)?; - } - - self.work(); - Ok(()) - } - - /// Closes the socket - pub fn close(&mut self) { - self.network.with_mut(|_interface, _device, sockets| { - sockets.get_mut::(self.socket_handle).close(); - }); - - self.work(); - } - - /// Disconnect the socket - pub fn disconnect(&mut self) { - self.network.with_mut(|_interface, _device, sockets| { - sockets.get_mut::(self.socket_handle).abort(); - }); - - self.work(); - } - - /// Checks if the socket is currently open - pub fn is_open(&mut self) -> bool { - self.network.with_mut(|_interface, _device, sockets| { - sockets.get_mut::(self.socket_handle).is_open() - }) - } - - /// Checks if the socket is currently connected - pub fn is_connected(&mut self) -> bool { - self.network.with_mut(|_interface, _device, sockets| { - let socket = sockets.get_mut::(self.socket_handle); - - socket.may_recv() && socket.may_send() - }) - } - - /// Delegates to [WifiStack::work] - pub fn work(&mut self) { - self.network.work() - } -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for Socket<'s, 'n, MODE> { - fn drop(&mut self) { - self.network - .with_mut(|_interface, _device, sockets| sockets.remove(self.socket_handle)); - } -} - -/// IO Errors -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum IoError { - SocketClosed, - #[cfg(feature = "igmp")] - MultiCastError(smoltcp::iface::MulticastError), - #[cfg(feature = "tcp")] - TcpRecvError, - #[cfg(feature = "udp")] - UdpRecvError(smoltcp::socket::udp::RecvError), - #[cfg(feature = "tcp")] - TcpSendError(smoltcp::socket::tcp::SendError), - #[cfg(feature = "udp")] - UdpSendError(smoltcp::socket::udp::SendError), - #[cfg(feature = "tcp")] - ConnectError(smoltcp::socket::tcp::ConnectError), - #[cfg(feature = "udp")] - BindError(smoltcp::socket::udp::BindError), - #[cfg(feature = "tcp")] - ListenError(smoltcp::socket::tcp::ListenError), -} - -impl embedded_io::Error for IoError { - fn kind(&self) -> embedded_io::ErrorKind { - embedded_io::ErrorKind::Other - } -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ErrorType for Socket<'s, 'n, MODE> { - type Error = IoError; -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Read for Socket<'s, 'n, MODE> { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.network.with_mut(|interface, device, sockets| { - use smoltcp::socket::tcp::RecvError; - - loop { - interface.poll(timestamp(), device, sockets); - let socket = sockets.get_mut::(self.socket_handle); - - match socket.recv_slice(buf) { - Ok(0) => continue, // no data - Ok(n) => return Ok(n), - Err(RecvError::Finished) => return Err(IoError::SocketClosed), // eof - Err(RecvError::InvalidState) => return Err(IoError::TcpRecvError), - } - } - }) - } -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::ReadReady for Socket<'s, 'n, MODE> { - fn read_ready(&mut self) -> Result { - self.network.with_mut(|interface, device, sockets| { - use smoltcp::socket::tcp::RecvError; - - interface.poll(timestamp(), device, sockets); - let socket = sockets.get_mut::(self.socket_handle); - - match socket.peek(1) { - Ok(s) => Ok(!s.is_empty()), - Err(RecvError::Finished) => Err(IoError::SocketClosed), - Err(RecvError::InvalidState) => Err(IoError::TcpRecvError), - } - }) - } -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::Write for Socket<'s, 'n, MODE> { - fn write(&mut self, buf: &[u8]) -> Result { - loop { - let (may_send, is_open, can_send) = - self.network.with_mut(|interface, device, sockets| { - interface.poll( - Instant::from_millis((self.network.current_millis_fn)() as i64), - device, - sockets, - ); - - let socket = sockets.get_mut::(self.socket_handle); - - (socket.may_send(), socket.is_open(), socket.can_send()) - }); - - if may_send { - break; - } - - if !is_open || !can_send { - return Err(IoError::SocketClosed); - } - } - - let mut written = 0; - loop { - self.flush()?; - - self.network.with_mut(|_interface, _device, sockets| { - sockets - .get_mut::(self.socket_handle) - .send_slice(&buf[written..]) - .map(|len| written += len) - .map_err(IoError::TcpSendError) - })?; - - if written >= buf.len() { - break; - } - } - - Ok(written) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - loop { - let res = self.network.with_mut(|interface, device, sockets| { - interface.poll( - Instant::from_millis((self.network.current_millis_fn)() as i64), - device, - sockets, - ) - }); - - if !res { - break; - } - } - - Ok(()) - } -} - -#[cfg(feature = "tcp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> embedded_io::WriteReady for Socket<'s, 'n, MODE> { - fn write_ready(&mut self) -> Result { - let (may_send, is_open, can_send) = self.network.with_mut(|interface, device, sockets| { - interface.poll( - Instant::from_millis((self.network.current_millis_fn)() as i64), - device, - sockets, - ); - - let socket = sockets.get_mut::(self.socket_handle); - - (socket.may_send(), socket.is_open(), socket.can_send()) - }); - - if !is_open || !can_send { - return Err(IoError::SocketClosed); - } - - Ok(may_send) - } -} - -/// A UDP socket -#[cfg(feature = "udp")] -pub struct UdpSocket<'s, 'n: 's, MODE: WifiDeviceMode> { - socket_handle: SocketHandle, - network: &'s WifiStack<'n, MODE>, -} - -#[cfg(feature = "udp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> UdpSocket<'s, 'n, MODE> { - /// Binds the socket to the given port - pub fn bind<'i>(&'i mut self, port: u16) -> Result<(), IoError> - where - 's: 'i, - { - self.work(); - - { - let res = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.bind(port) - }); - - if let Err(err) = res { - return Err(IoError::BindError(err)); - } - } - - loop { - let can_send = self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - sock.can_send() - }); - - if can_send { - break; - } - - self.work(); - } - - Ok(()) - } - - /// Close the socket - pub fn close(&mut self) { - self.network.with_mut(|_interface, _device, sockets| { - sockets - .get_mut::(self.socket_handle) - .close(); - }); - - self.work(); - } - - /// Sends data on the socket to the given address - pub fn send(&mut self, addr: IpAddress, port: u16, data: &[u8]) -> Result<(), IoError> { - loop { - self.work(); - - let (can_send, packet_capacity, payload_capacity) = - self.network.with_mut(|_interface, _device, sockets| { - let sock = sockets.get_mut::(self.socket_handle); - ( - sock.can_send(), - sock.packet_send_capacity(), - sock.payload_send_capacity(), - ) - }); - - if can_send && packet_capacity > 0 && payload_capacity > data.len() { - break; - } - } - - self.network - .with_mut(|_interface, _device, sockets| { - let endpoint = (addr, port); - let endpoint: IpEndpoint = endpoint.into(); - - sockets - .get_mut::(self.socket_handle) - .send_slice(data, endpoint) - }) - .map_err(IoError::UdpSendError)?; - - self.work(); - - Ok(()) - } - - /// Receives a single datagram message on the socket - pub fn receive(&mut self, data: &mut [u8]) -> Result<(usize, IpAddress, u16), IoError> { - self.work(); - - let res = self.network.with_mut(|_interface, _device, sockets| { - sockets - .get_mut::(self.socket_handle) - .recv_slice(data) - }); - - match res { - Ok((len, endpoint)) => { - let addr = endpoint.endpoint.addr; - Ok((len, addr, endpoint.endpoint.port)) - } - Err(e) => Err(IoError::UdpRecvError(e)), - } - } - - /// This function specifies a new multicast group for this socket to join - #[cfg(feature = "igmp")] - pub fn join_multicast_group(&mut self, addr: IpAddress) -> Result { - self.work(); - - let res = self.network.with_mut(|interface, device, _| { - interface.join_multicast_group( - device, - addr, - Instant::from_millis((self.network.current_millis_fn)() as i64), - ) - }); - - self.work(); - - res.map_err(IoError::MultiCastError) - } - - /// Delegates to [WifiStack::work] - pub fn work(&mut self) { - self.network.work() - } -} - -#[cfg(feature = "udp")] -impl<'s, 'n: 's, MODE: WifiDeviceMode> Drop for UdpSocket<'s, 'n, MODE> { - fn drop(&mut self) { - self.network - .with_mut(|_, _, sockets| sockets.borrow_mut().remove(self.socket_handle)); - } -} diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml index 7fcbcbfad75..600f74b2ea4 100644 --- a/examples/.cargo/config.toml +++ b/examples/.cargo/config.toml @@ -33,6 +33,7 @@ PASSWORD = "PASSWORD" STATIC_IP = "1.1.1.1 " GATEWAY_IP = "1.1.1.1" HOST_IP = "1.1.1.1" +ESP_WIFI_CSI_ENABLE = "true" [unstable] build-std = ["alloc", "core"] diff --git a/examples/Cargo.toml b/examples/Cargo.toml index d3aeba47443..d8751a2f2aa 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -8,6 +8,7 @@ publish = false [dependencies] aligned = { version = "0.4.2", optional = true } bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8ae679e021b78f53fd33afb8bb35d0b62e", features = [ "macros", "async"] } +blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "1c581661d78e0cf0f17b936297179b993fb149d7" } bt-hci = "0.1.1" cfg-if = "1.0.0" critical-section = "1.1.3" @@ -30,7 +31,7 @@ esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-ieee802154 = { path = "../esp-ieee802154", optional = true } esp-println = { path = "../esp-println", features = ["log"] } esp-storage = { path = "../esp-storage", optional = true } -esp-wifi = { path = "../esp-wifi", optional = true } +esp-wifi = { path = "../esp-wifi", features = ["log"], optional = true } fugit = "0.3.7" heapless = "0.8.0" hmac = { version = "0.12.1", default-features = false } @@ -42,6 +43,8 @@ nb = "1.1.0" portable-atomic = { version = "1.9.0", default-features = false } sha2 = { version = "0.10.8", default-features = false } smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw"] } +smoltcp-nal = "0.5.1" +embedded-time = "=0.12.1" ssd1306 = "0.8.4" static_cell = { version = "2.1.0", features = ["nightly"] } trouble-host = { git = "https://github.com/embassy-rs/trouble", package = "trouble-host", rev = "4f1114ce58e96fe54f5ed7e726f66e1ad8d9ce54", features = [ "log", "gatt" ] } diff --git a/examples/src/bin/adc.rs b/examples/src/bin/adc.rs index 08fe4b3786a..e4c8eb2b423 100644 --- a/examples/src/bin/adc.rs +++ b/examples/src/bin/adc.rs @@ -20,7 +20,6 @@ use esp_backtrace as _; use esp_hal::{ analog::adc::{Adc, AdcConfig, Attenuation}, delay::Delay, - gpio::Io, prelude::*, }; use esp_println::println; @@ -29,14 +28,13 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let analog_pin = io.pins.gpio32; + let analog_pin = peripherals.GPIO32; } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - let analog_pin = io.pins.gpio3; + let analog_pin = peripherals.GPIO3; } else { - let analog_pin = io.pins.gpio2; + let analog_pin = peripherals.GPIO2; } } diff --git a/examples/src/bin/adc_cal.rs b/examples/src/bin/adc_cal.rs index a025a6ba52f..e7d6ceb859d 100644 --- a/examples/src/bin/adc_cal.rs +++ b/examples/src/bin/adc_cal.rs @@ -16,7 +16,6 @@ use esp_backtrace as _; use esp_hal::{ analog::adc::{Adc, AdcConfig, Attenuation}, delay::Delay, - gpio::Io, prelude::*, }; use esp_println::println; @@ -25,12 +24,11 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { if #[cfg(feature = "esp32s3")] { - let analog_pin = io.pins.gpio3; + let analog_pin = peripherals.GPIO3; } else { - let analog_pin = io.pins.gpio2; + let analog_pin = peripherals.GPIO2; } } diff --git a/examples/src/bin/advanced_serial.rs b/examples/src/bin/advanced_serial.rs index b0c4e8d90b3..6012abc4abc 100644 --- a/examples/src/bin/advanced_serial.rs +++ b/examples/src/bin/advanced_serial.rs @@ -13,7 +13,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{delay::Delay, gpio::Io, prelude::*, uart::Uart}; +use esp_hal::{delay::Delay, prelude::*, uart::Uart}; use esp_println::println; use nb::block; @@ -21,9 +21,7 @@ use nb::block; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let mut serial1 = Uart::new(peripherals.UART1, io.pins.gpio4, io.pins.gpio5).unwrap(); + let mut serial1 = Uart::new(peripherals.UART1, peripherals.GPIO4, peripherals.GPIO5).unwrap(); let delay = Delay::new(); diff --git a/examples/src/bin/blinky.rs b/examples/src/bin/blinky.rs index f863b8b19b8..f804dda603f 100644 --- a/examples/src/bin/blinky.rs +++ b/examples/src/bin/blinky.rs @@ -11,7 +11,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::{Io, Level, Output}, + gpio::{Level, Output}, prelude::*, }; @@ -20,8 +20,8 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); // Set GPIO0 as an output, and set its state high initially. - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = Output::new(io.pins.gpio0, Level::High); + + let mut led = Output::new(peripherals.GPIO0, Level::High); let delay = Delay::new(); diff --git a/examples/src/bin/blinky_erased_pins.rs b/examples/src/bin/blinky_erased_pins.rs index 48d9309f088..140e728e1aa 100644 --- a/examples/src/bin/blinky_erased_pins.rs +++ b/examples/src/bin/blinky_erased_pins.rs @@ -14,7 +14,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::{Input, Io, Level, Output, Pin, Pull}, + gpio::{Input, Level, Output, Pin, Pull}, prelude::*, }; @@ -22,18 +22,16 @@ use esp_hal::{ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Set LED GPIOs as an output: - let led1 = Output::new(io.pins.gpio2.degrade(), Level::Low); - let led2 = Output::new(io.pins.gpio4.degrade(), Level::Low); - let led3 = Output::new(io.pins.gpio5.degrade(), Level::Low); + let led1 = Output::new(peripherals.GPIO2.degrade(), Level::Low); + let led2 = Output::new(peripherals.GPIO4.degrade(), Level::Low); + let led3 = Output::new(peripherals.GPIO5.degrade(), Level::Low); // Use boot button as an input: #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - let button = io.pins.gpio0.degrade(); + let button = peripherals.GPIO0.degrade(); #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] - let button = io.pins.gpio9.degrade(); + let button = peripherals.GPIO9.degrade(); let button = Input::new(button, Pull::Up); diff --git a/examples/src/bin/dac.rs b/examples/src/bin/dac.rs index d83740d97da..4bd42b29860 100644 --- a/examples/src/bin/dac.rs +++ b/examples/src/bin/dac.rs @@ -19,21 +19,19 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{analog::dac::Dac, delay::Delay, gpio::Io, prelude::*}; +use esp_hal::{analog::dac::Dac, delay::Delay, prelude::*}; #[entry] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let dac1_pin = io.pins.gpio25; - let dac2_pin = io.pins.gpio26; + let dac1_pin = peripherals.GPIO25; + let dac2_pin = peripherals.GPIO26; } else if #[cfg(feature = "esp32s2")] { - let dac1_pin = io.pins.gpio17; - let dac2_pin = io.pins.gpio18; + let dac1_pin = peripherals.GPIO17; + let dac2_pin = peripherals.GPIO18; } } diff --git a/examples/src/bin/debug_assist.rs b/examples/src/bin/debug_assist.rs index 639037f5cfe..1fa35ae63c7 100644 --- a/examples/src/bin/debug_assist.rs +++ b/examples/src/bin/debug_assist.rs @@ -87,7 +87,7 @@ fn interrupt_handler() { if da.is_sp_monitor_interrupt_set() { println!("SP MONITOR TRIGGERED"); da.clear_sp_monitor_interrupt(); - let pc = da.get_sp_monitor_pc(); + let pc = da.sp_monitor_pc(); println!("PC = 0x{:x}", pc); } @@ -95,7 +95,7 @@ fn interrupt_handler() { if da.is_region0_monitor_interrupt_set() { println!("REGION0 MONITOR TRIGGERED"); da.clear_region0_monitor_interrupt(); - let pc = da.get_region_monitor_pc(); + let pc = da.region_monitor_pc(); println!("PC = 0x{:x}", pc); } @@ -103,7 +103,7 @@ fn interrupt_handler() { if da.is_region1_monitor_interrupt_set() { println!("REGION1 MONITOR TRIGGERED"); da.clear_region1_monitor_interrupt(); - let pc = da.get_region_monitor_pc(); + let pc = da.region_monitor_pc(); println!("PC = 0x{:x}", pc); } diff --git a/examples/src/bin/embassy_i2c.rs b/examples/src/bin/embassy_i2c.rs index d6b10e04bf3..2b7f467ee53 100644 --- a/examples/src/bin/embassy_i2c.rs +++ b/examples/src/bin/embassy_i2c.rs @@ -19,7 +19,11 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*, timer::timg::TimerGroup}; +use esp_hal::{ + i2c::master::{Config, I2c}, + prelude::*, + timer::timg::TimerGroup, +}; use lis3dh_async::{Lis3dh, Range, SlaveAddr}; #[esp_hal_embassy::main] @@ -29,9 +33,14 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let i2c0 = I2c::new_async(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 400.kHz()); + let i2c0 = I2c::new(peripherals.I2C0, { + let mut config = Config::default(); + config.frequency = 400.kHz(); + config + }) + .with_sda(peripherals.GPIO4) + .with_scl(peripherals.GPIO5) + .into_async(); let mut lis3dh = Lis3dh::new_i2c(i2c0, SlaveAddr::Alternate).await.unwrap(); lis3dh.set_range(Range::G8).await.unwrap(); diff --git a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs index 737cc84ab2f..1f7ff1525af 100644 --- a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -20,7 +20,11 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*, timer::timg::TimerGroup}; +use esp_hal::{ + i2c::master::{Config, I2c}, + prelude::*, + timer::timg::TimerGroup, +}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { @@ -29,9 +33,14 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let mut i2c = I2c::new_async(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 400.kHz()); + let mut i2c = I2c::new(peripherals.I2C0, { + let mut config = Config::default(); + config.frequency = 400.kHz(); + config + }) + .with_sda(peripherals.GPIO4) + .with_scl(peripherals.GPIO5) + .into_async(); loop { let mut data = [0u8; 22]; diff --git a/examples/src/bin/embassy_i2s_parallel.rs b/examples/src/bin/embassy_i2s_parallel.rs new file mode 100644 index 00000000000..927b95ded17 --- /dev/null +++ b/examples/src/bin/embassy_i2s_parallel.rs @@ -0,0 +1,92 @@ +//! This shows using Parallel IO to output 8 bit parallel data at 1MHz clock +//! rate with a delay of 10ms between each transfer. +//! +//! The following wiring is assumed: +//! - Data pins => GPIO16, GPIO4, GPIO17, GPIO18, GPIO5, GPIO19, GPIO12, and +//! GPIO14 +//! - Clock output pin => GPIO25 +//! +//! You can use a logic analyzer to see how the pins are used. + +//% CHIPS: esp32 +//% FEATURES: embassy embassy-generic-timers + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use esp_backtrace as _; +use esp_hal::{ + dma::{Dma, DmaPriority, DmaTxBuf}, + dma_buffers, + i2s::parallel::{I2sParallel, TxEightBits}, + prelude::*, + timer::timg::TimerGroup, +}; +use log::info; + +const BUFFER_SIZE: usize = 256; + +#[main] +async fn main(_spawner: Spawner) { + esp_println::logger::init_logger(log::LevelFilter::Info); + info!("Starting!"); + let peripherals = esp_hal::init(esp_hal::Config::default()); + let dma = Dma::new(peripherals.DMA); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + + info!("init embassy"); + esp_hal_embassy::init(timg0.timer0); + + let dma_channel = dma.i2s1channel; + let i2s = peripherals.I2S1; + let clock = peripherals.GPIO25; + + let pins = TxEightBits::new( + peripherals.GPIO16, + peripherals.GPIO4, + peripherals.GPIO17, + peripherals.GPIO18, + peripherals.GPIO5, + peripherals.GPIO19, + peripherals.GPIO12, + peripherals.GPIO14, + ); + + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, BUFFER_SIZE); + let mut parallel = I2sParallel::new( + i2s, + dma_channel + .configure(false, DmaPriority::Priority0) + .into_async(), + 1.MHz(), + pins, + clock, + ); + + for (i, data) in tx_buffer.chunks_mut(4).enumerate() { + let offset = i * 4; + // i2s parallel driver expects the buffer to be interleaved + data[0] = (offset + 2) as u8; + data[1] = (offset + 3) as u8; + data[2] = offset as u8; + data[3] = (offset + 1) as u8; + } + + let mut tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).expect("DmaTxBuf::new failed"); + + info!("Sending {} bytes!", BUFFER_SIZE); + loop { + let mut xfer = match parallel.send(tx_buf) { + Ok(xfer) => xfer, + Err(_) => { + panic!("Failed to send buffer"); + } + }; + xfer.wait_for_done().await.expect("Failed to send buffer"); + (parallel, tx_buf) = xfer.wait(); + Timer::after_millis(10).await; + } +} diff --git a/examples/src/bin/embassy_i2s_read.rs b/examples/src/bin/embassy_i2s_read.rs index 17c48383c48..612ee4ad9f5 100644 --- a/examples/src/bin/embassy_i2s_read.rs +++ b/examples/src/bin/embassy_i2s_read.rs @@ -22,8 +22,7 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - i2s::{DataFormat, I2s, Standard}, + i2s::master::{DataFormat, I2s, Standard}, prelude::*, timer::timg::TimerGroup, }; @@ -37,8 +36,6 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); #[cfg(any(feature = "esp32", feature = "esp32s2"))] let dma_channel = dma.i2s0channel; @@ -52,19 +49,20 @@ async fn main(_spawner: Spawner) { Standard::Philips, DataFormat::Data16Channel16, 44100u32.Hz(), - dma_channel.configure_for_async(false, DmaPriority::Priority0), + dma_channel.configure(false, DmaPriority::Priority0), rx_descriptors, tx_descriptors, - ); + ) + .into_async(); #[cfg(not(feature = "esp32"))] - let i2s = i2s.with_mclk(io.pins.gpio0); + let i2s = i2s.with_mclk(peripherals.GPIO0); let i2s_rx = i2s .i2s_rx - .with_bclk(io.pins.gpio2) - .with_ws(io.pins.gpio4) - .with_din(io.pins.gpio5) + .with_bclk(peripherals.GPIO2) + .with_ws(peripherals.GPIO4) + .with_din(peripherals.GPIO5) .build(); let buffer = rx_buffer; diff --git a/examples/src/bin/embassy_i2s_sound.rs b/examples/src/bin/embassy_i2s_sound.rs index 50660d81840..fd06eff7a00 100644 --- a/examples/src/bin/embassy_i2s_sound.rs +++ b/examples/src/bin/embassy_i2s_sound.rs @@ -36,8 +36,7 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - i2s::{DataFormat, I2s, Standard}, + i2s::master::{DataFormat, I2s, Standard}, prelude::*, timer::timg::TimerGroup, }; @@ -59,8 +58,6 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); #[cfg(any(feature = "esp32", feature = "esp32s2"))] let dma_channel = dma.i2s0channel; @@ -74,16 +71,17 @@ async fn main(_spawner: Spawner) { Standard::Philips, DataFormat::Data16Channel16, 44100u32.Hz(), - dma_channel.configure_for_async(false, DmaPriority::Priority0), + dma_channel.configure(false, DmaPriority::Priority0), rx_descriptors, tx_descriptors, - ); + ) + .into_async(); let i2s_tx = i2s .i2s_tx - .with_bclk(io.pins.gpio2) - .with_ws(io.pins.gpio4) - .with_dout(io.pins.gpio5) + .with_bclk(peripherals.GPIO2) + .with_ws(peripherals.GPIO4) + .with_dout(peripherals.GPIO5) .build(); let data = diff --git a/examples/src/bin/embassy_multicore.rs b/examples/src/bin/embassy_multicore.rs index 741b196bb2c..5c5c1f645aa 100644 --- a/examples/src/bin/embassy_multicore.rs +++ b/examples/src/bin/embassy_multicore.rs @@ -20,9 +20,9 @@ use embassy_time::{Duration, Ticker}; use esp_backtrace as _; use esp_hal::{ cpu_control::{CpuControl, Stack}, - get_core, - gpio::{Io, Level, Output}, + gpio::{Level, Output}, timer::{timg::TimerGroup, AnyTimer}, + Cpu, }; use esp_hal_embassy::Executor; use esp_println::println; @@ -37,7 +37,7 @@ async fn control_led( mut led: Output<'static>, control: &'static Signal, ) { - println!("Starting control_led() on core {}", get_core() as usize); + println!("Starting control_led() on core {}", Cpu::current() as usize); loop { if control.wait().await { esp_println::println!("LED on"); @@ -53,8 +53,6 @@ async fn control_led( async fn main(_spawner: Spawner) { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0: AnyTimer = timg0.timer0.into(); let timer1: AnyTimer = timg0.timer1.into(); @@ -65,7 +63,7 @@ async fn main(_spawner: Spawner) { static LED_CTRL: StaticCell> = StaticCell::new(); let led_ctrl_signal = &*LED_CTRL.init(Signal::new()); - let led = Output::new(io.pins.gpio0, Level::Low); + let led = Output::new(peripherals.GPIO0, Level::Low); let _guard = cpu_control .start_app_core(unsafe { &mut *addr_of_mut!(APP_CORE_STACK) }, move || { @@ -80,7 +78,7 @@ async fn main(_spawner: Spawner) { // Sends periodic messages to control_led, enabling or disabling it. println!( "Starting enable_disable_led() on core {}", - get_core() as usize + Cpu::current() as usize ); let mut ticker = Ticker::every(Duration::from_secs(1)); loop { diff --git a/examples/src/bin/embassy_multicore_interrupt.rs b/examples/src/bin/embassy_multicore_interrupt.rs index 1b427ebe7b5..6056c541edb 100644 --- a/examples/src/bin/embassy_multicore_interrupt.rs +++ b/examples/src/bin/embassy_multicore_interrupt.rs @@ -19,11 +19,11 @@ use embassy_time::{Duration, Ticker}; use esp_backtrace as _; use esp_hal::{ cpu_control::{CpuControl, Stack}, - get_core, - gpio::{Io, Level, Output}, + gpio::{Level, Output}, interrupt::{software::SoftwareInterruptControl, Priority}, prelude::*, timer::{timg::TimerGroup, AnyTimer}, + Cpu, }; use esp_hal_embassy::InterruptExecutor; use esp_println::println; @@ -38,7 +38,7 @@ async fn control_led( mut led: Output<'static>, control: &'static Signal, ) { - println!("Starting control_led() on core {}", get_core() as usize); + println!("Starting control_led() on core {}", Cpu::current() as usize); loop { if control.wait().await { esp_println::println!("LED on"); @@ -55,7 +55,7 @@ async fn control_led( async fn enable_disable_led(control: &'static Signal) { println!( "Starting enable_disable_led() on core {}", - get_core() as usize + Cpu::current() as usize ); let mut ticker = Ticker::every(Duration::from_secs(1)); loop { @@ -75,8 +75,6 @@ fn main() -> ! { let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let timg0 = TimerGroup::new(peripherals.TIMG0); let timer0: AnyTimer = timg0.timer0.into(); let timer1: AnyTimer = timg0.timer1.into(); @@ -87,7 +85,7 @@ fn main() -> ! { static LED_CTRL: StaticCell> = StaticCell::new(); let led_ctrl_signal = &*LED_CTRL.init(Signal::new()); - let led = Output::new(io.pins.gpio0, Level::Low); + let led = Output::new(peripherals.GPIO0, Level::Low); static EXECUTOR_CORE_1: StaticCell> = StaticCell::new(); let executor_core1 = InterruptExecutor::new(sw_ints.software_interrupt1); diff --git a/examples/src/bin/embassy_parl_io_rx.rs b/examples/src/bin/embassy_parl_io_rx.rs index 83fccaec51d..33749c91f36 100644 --- a/examples/src/bin/embassy_parl_io_rx.rs +++ b/examples/src/bin/embassy_parl_io_rx.rs @@ -16,8 +16,8 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - parl_io::{no_clk_pin, BitPackOrder, ParlIoRxOnly, RxFourBits}, + gpio::NoPin, + parl_io::{BitPackOrder, ParlIoRxOnly, RxFourBits}, prelude::*, timer::systimer::{SystemTimer, Target}, }; @@ -31,18 +31,24 @@ async fn main(_spawner: Spawner) { let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); esp_hal_embassy::init(systimer.alarm0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let mut rx_pins = RxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4); + let mut rx_pins = RxFourBits::new( + peripherals.GPIO1, + peripherals.GPIO2, + peripherals.GPIO3, + peripherals.GPIO4, + ); + let mut rx_clk_pin = NoPin; let parl_io = ParlIoRxOnly::new( peripherals.PARL_IO, - dma_channel.configure_for_async(false, DmaPriority::Priority0), + dma_channel + .configure(false, DmaPriority::Priority0) + .into_async(), rx_descriptors, 1.MHz(), ) @@ -50,7 +56,12 @@ async fn main(_spawner: Spawner) { let mut parl_io_rx = parl_io .rx - .with_config(&mut rx_pins, no_clk_pin(), BitPackOrder::Msb, Some(0xfff)) + .with_config( + &mut rx_pins, + &mut rx_clk_pin, + BitPackOrder::Msb, + Some(0xfff), + ) .unwrap(); let buffer = rx_buffer; diff --git a/examples/src/bin/embassy_parl_io_tx.rs b/examples/src/bin/embassy_parl_io_tx.rs index 9a1ca372fb3..44302552bde 100644 --- a/examples/src/bin/embassy_parl_io_tx.rs +++ b/examples/src/bin/embassy_parl_io_tx.rs @@ -20,7 +20,6 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, parl_io::{ BitPackOrder, ClkOutPin, @@ -42,26 +41,31 @@ async fn main(_spawner: Spawner) { let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); esp_hal_embassy::init(systimer.alarm0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let tx_pins = TxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4); + let tx_pins = TxFourBits::new( + peripherals.GPIO1, + peripherals.GPIO2, + peripherals.GPIO3, + peripherals.GPIO4, + ); - let mut pin_conf = TxPinConfigWithValidPin::new(tx_pins, io.pins.gpio5); + let mut pin_conf = TxPinConfigWithValidPin::new(tx_pins, peripherals.GPIO5); let parl_io = ParlIoTxOnly::new( peripherals.PARL_IO, - dma_channel.configure_for_async(false, DmaPriority::Priority0), + dma_channel + .configure(false, DmaPriority::Priority0) + .into_async(), tx_descriptors, 1.MHz(), ) .unwrap(); - let mut clock_pin = ClkOutPin::new(io.pins.gpio8); + let mut clock_pin = ClkOutPin::new(peripherals.GPIO8); let mut parl_io_tx = parl_io .tx diff --git a/examples/src/bin/embassy_rmt_rx.rs b/examples/src/bin/embassy_rmt_rx.rs index 0befacef8ff..81faf25cd11 100644 --- a/examples/src/bin/embassy_rmt_rx.rs +++ b/examples/src/bin/embassy_rmt_rx.rs @@ -13,9 +13,9 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - gpio::{Io, Level, Output}, + gpio::{Level, Output}, prelude::*, - rmt::{asynch::RxChannelAsync, PulseCode, Rmt, RxChannelConfig, RxChannelCreatorAsync}, + rmt::{PulseCode, Rmt, RxChannelAsync, RxChannelConfig, RxChannelCreatorAsync}, timer::timg::TimerGroup, }; use esp_println::{print, println}; @@ -44,8 +44,6 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { let freq = 32.MHz(); @@ -54,7 +52,7 @@ async fn main(spawner: Spawner) { } }; - let rmt = Rmt::new_async(peripherals.RMT, freq).unwrap(); + let rmt = Rmt::new(peripherals.RMT, freq).unwrap().into_async(); let rx_config = RxChannelConfig { clk_divider: 255, idle_threshold: 10000, @@ -63,58 +61,53 @@ async fn main(spawner: Spawner) { cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let mut channel = rmt.channel0.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel0.configure(peripherals.GPIO4, rx_config).unwrap(); } else if #[cfg(feature = "esp32s3")] { - let mut channel = rmt.channel7.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel7.configure(peripherals.GPIO4, rx_config).unwrap(); } else { - let mut channel = rmt.channel2.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel2.configure(peripherals.GPIO4, rx_config).unwrap(); } } spawner - .spawn(signal_task(Output::new(io.pins.gpio5, Level::Low))) + .spawn(signal_task(Output::new(peripherals.GPIO5, Level::Low))) .unwrap(); - let mut data = [PulseCode { - level1: true, - length1: 1, - level2: false, - length2: 1, - }; 48]; + let mut data: [u32; 48] = [PulseCode::empty(); 48]; loop { println!("receive"); channel.receive(&mut data).await.unwrap(); let mut total = 0usize; for entry in &data[..data.len()] { - if entry.length1 == 0 { + if entry.length1() == 0 { break; } - total += entry.length1 as usize; + total += entry.length1() as usize; - if entry.length2 == 0 { + if entry.length2() == 0 { break; } - total += entry.length2 as usize; + total += entry.length2() as usize; } for entry in &data[..data.len()] { - if entry.length1 == 0 { + if entry.length1() == 0 { break; } - let count = WIDTH / (total / entry.length1 as usize); - let c = if entry.level1 { '-' } else { '_' }; + let count = WIDTH / (total / entry.length1() as usize); + let c = if entry.level1() { '-' } else { '_' }; for _ in 0..count + 1 { print!("{}", c); } - if entry.length2 == 0 { + if entry.length2() == 0 { break; } - let count = WIDTH / (total / entry.length2 as usize); - let c = if entry.level2 { '-' } else { '_' }; + let count = WIDTH / (total / entry.length2() as usize); + let c = if entry.level2() { '-' } else { '_' }; for _ in 0..count + 1 { print!("{}", c); } diff --git a/examples/src/bin/embassy_rmt_tx.rs b/examples/src/bin/embassy_rmt_tx.rs index 04ddc45d5cf..20e14cfa574 100644 --- a/examples/src/bin/embassy_rmt_tx.rs +++ b/examples/src/bin/embassy_rmt_tx.rs @@ -15,9 +15,8 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - gpio::Io, prelude::*, - rmt::{asynch::TxChannelAsync, PulseCode, Rmt, TxChannelConfig, TxChannelCreatorAsync}, + rmt::{PulseCode, Rmt, TxChannelAsync, TxChannelConfig, TxChannelCreatorAsync}, timer::timg::TimerGroup, }; use esp_println::println; @@ -30,8 +29,6 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { let freq = 32.MHz(); @@ -40,12 +37,12 @@ async fn main(_spawner: Spawner) { } }; - let rmt = Rmt::new_async(peripherals.RMT, freq).unwrap(); + let rmt = Rmt::new(peripherals.RMT, freq).unwrap().into_async(); let mut channel = rmt .channel0 .configure( - io.pins.gpio4, + peripherals.GPIO4, TxChannelConfig { clk_divider: 255, ..TxChannelConfig::default() @@ -53,20 +50,10 @@ async fn main(_spawner: Spawner) { ) .unwrap(); - let mut data = [PulseCode { - level1: true, - length1: 200, - level2: false, - length2: 50, - }; 20]; + let mut data = [PulseCode::new(true, 200, false, 50); 20]; - data[data.len() - 2] = PulseCode { - level1: true, - length1: 3000, - level2: false, - length2: 500, - }; - data[data.len() - 1] = PulseCode::default(); + data[data.len() - 2] = PulseCode::new(true, 3000, false, 500); + data[data.len() - 1] = PulseCode::empty(); loop { println!("transmit"); diff --git a/examples/src/bin/embassy_serial.rs b/examples/src/bin/embassy_serial.rs index e03136d7f70..643bac9285c 100644 --- a/examples/src/bin/embassy_serial.rs +++ b/examples/src/bin/embassy_serial.rs @@ -13,14 +13,8 @@ use embassy_executor::Spawner; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, signal::Signal}; use esp_backtrace as _; use esp_hal::{ - gpio::Io, timer::timg::TimerGroup, - uart::{ - config::{AtCmdConfig, Config}, - Uart, - UartRx, - UartTx, - }, + uart::{AtCmdConfig, Config, Uart, UartRx, UartTx}, Async, }; use static_cell::StaticCell; @@ -76,28 +70,28 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Default pins for Uart/Serial communication cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); + let (tx_pin, rx_pin) = (peripherals.GPIO1, peripherals.GPIO3); } else if #[cfg(feature = "esp32c2")] { - let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); + let (tx_pin, rx_pin) = (peripherals.GPIO20, peripherals.GPIO19); } else if #[cfg(feature = "esp32c3")] { - let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); + let (tx_pin, rx_pin) = (peripherals.GPIO21, peripherals.GPIO20); } else if #[cfg(feature = "esp32c6")] { - let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); + let (tx_pin, rx_pin) = (peripherals.GPIO16, peripherals.GPIO17); } else if #[cfg(feature = "esp32h2")] { - let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); + let (tx_pin, rx_pin) = (peripherals.GPIO24, peripherals.GPIO23); } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + let (tx_pin, rx_pin) = (peripherals.GPIO43, peripherals.GPIO44); } } let config = Config::default().rx_fifo_full_threshold(READ_BUF_SIZE as u16); - let mut uart0 = Uart::new_async_with_config(peripherals.UART0, config, rx_pin, tx_pin).unwrap(); + let mut uart0 = Uart::new_with_config(peripherals.UART0, config, rx_pin, tx_pin) + .unwrap() + .into_async(); uart0.set_at_cmd(AtCmdConfig::new(None, None, None, AT_CMD, None)); let (rx, tx) = uart0.split(); diff --git a/examples/src/bin/embassy_spi.rs b/examples/src/bin/embassy_spi.rs index 334cd713764..82a7813f884 100644 --- a/examples/src/bin/embassy_spi.rs +++ b/examples/src/bin/embassy_spi.rs @@ -24,9 +24,11 @@ use esp_backtrace as _; use esp_hal::{ dma::*, dma_buffers, - gpio::Io, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{Config, Spi}, + SpiMode, + }, timer::timg::TimerGroup, }; @@ -38,11 +40,10 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio4; - let cs = io.pins.gpio5; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO2; + let mosi = peripherals.GPIO4; + let cs = peripherals.GPIO5; let dma = Dma::new(peripherals.DMA); @@ -58,13 +59,21 @@ async fn main(_spawner: Spawner) { let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_miso(miso) - .with_cs(cs) - .with_dma(dma_channel.configure_for_async(false, DmaPriority::Priority0)) - .with_buffers(dma_rx_buf, dma_tx_buf); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_cs(cs) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf) + .into_async(); let send_buffer = [0, 1, 2, 3, 4, 5, 6, 7]; loop { diff --git a/examples/src/bin/embassy_touch.rs b/examples/src/bin/embassy_touch.rs index 808caf2a869..2849d178343 100644 --- a/examples/src/bin/embassy_touch.rs +++ b/examples/src/bin/embassy_touch.rs @@ -17,7 +17,6 @@ use embassy_futures::select::{select, Either}; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - gpio::Io, rtc_cntl::Rtc, timer::timg::TimerGroup, touch::{Touch, TouchConfig, TouchPad}, @@ -32,11 +31,10 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let mut rtc = Rtc::new(peripherals.LPWR); - let touch_pin0 = io.pins.gpio2; - let touch_pin1 = io.pins.gpio4; + let touch_pin0 = peripherals.GPIO2; + let touch_pin1 = peripherals.GPIO4; let touch_cfg = Some(TouchConfig { measurement_duration: Some(0x2000), diff --git a/examples/src/bin/embassy_twai.rs b/examples/src/bin/embassy_twai.rs index 40128f04abd..d788c75301f 100644 --- a/examples/src/bin/embassy_twai.rs +++ b/examples/src/bin/embassy_twai.rs @@ -35,7 +35,6 @@ use embassy_sync::{blocking_mutex::raw::NoopRawMutex, channel::Channel}; use embedded_can::{Frame, Id}; use esp_backtrace as _; use esp_hal::{ - gpio::Io, timer::timg::TimerGroup, twai::{self, EspTwaiFrame, StandardId, TwaiMode, TwaiRx, TwaiTx}, }; @@ -92,28 +91,27 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let tx_pin = io.pins.gpio2; - // let rx_pin = io.pins.gpio0; // Uncomment if you want to use an external transceiver. - // Without an external transceiver, we only need a single line between the two MCUs. - let rx_pin = tx_pin.peripheral_input(); // Comment this line if you want to use an external transceiver. + let (rx_pin, tx_pin) = peripherals.GPIO2.split(); + // Use these if you want to use an external transceiver: + // let tx_pin = peripherals.GPIO2; + // let rx_pin = peripherals.GPIO0; // The speed of the bus. const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::B125K; - // !!! Use `new_async` when using a transceiver. `new_async_no_transceiver` sets TX to open-drain + // !!! Use `new` when using a transceiver. `new_no_transceiver` sets TX to open-drain // Begin configuring the TWAI peripheral. The peripheral is in a reset like // state that prevents transmission but allows configuration. - let mut twai_config = twai::TwaiConfiguration::new_async_no_transceiver( + let mut twai_config = twai::TwaiConfiguration::new_no_transceiver( peripherals.TWAI0, rx_pin, tx_pin, TWAI_BAUDRATE, TwaiMode::Normal, - ); + ) + .into_async(); // Partially filter the incoming messages to reduce overhead of receiving // undesired messages. Note that due to how the hardware filters messages, diff --git a/examples/src/bin/embassy_usb_serial.rs b/examples/src/bin/embassy_usb_serial.rs index e2054358dd3..207ae1132a1 100644 --- a/examples/src/bin/embassy_usb_serial.rs +++ b/examples/src/bin/embassy_usb_serial.rs @@ -21,7 +21,6 @@ use embassy_usb::{ }; use esp_backtrace as _; use esp_hal::{ - gpio::Io, otg_fs::{ asynch::{Config, Driver}, Usb, @@ -37,9 +36,7 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let usb = Usb::new(peripherals.USB0, io.pins.gpio20, io.pins.gpio19); + let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19); // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 1024]; diff --git a/examples/src/bin/embassy_usb_serial_jtag.rs b/examples/src/bin/embassy_usb_serial_jtag.rs index 8944ed2dc92..f1855834d8a 100644 --- a/examples/src/bin/embassy_usb_serial_jtag.rs +++ b/examples/src/bin/embassy_usb_serial_jtag.rs @@ -68,7 +68,9 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let (rx, tx) = UsbSerialJtag::new_async(peripherals.USB_DEVICE).split(); + let (rx, tx) = UsbSerialJtag::new(peripherals.USB_DEVICE) + .into_async() + .split(); static SIGNAL: StaticCell>> = StaticCell::new(); diff --git a/examples/src/bin/embassy_wait.rs b/examples/src/bin/embassy_wait.rs index 9f80b2dbc9d..b78a85ff3c4 100644 --- a/examples/src/bin/embassy_wait.rs +++ b/examples/src/bin/embassy_wait.rs @@ -12,7 +12,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; use esp_hal::{ - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, timer::timg::TimerGroup, }; @@ -24,13 +24,11 @@ async fn main(_spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let mut input = Input::new(io.pins.gpio0, Pull::Down); + let mut input = Input::new(peripherals.GPIO0, Pull::Down); } else { - let mut input = Input::new(io.pins.gpio9, Pull::Down); + let mut input = Input::new(peripherals.GPIO9, Pull::Down); } } diff --git a/examples/src/bin/etm_blinky_systimer.rs b/examples/src/bin/etm_blinky_systimer.rs index 0a295c14ab1..1cdb361e2ef 100644 --- a/examples/src/bin/etm_blinky_systimer.rs +++ b/examples/src/bin/etm_blinky_systimer.rs @@ -12,13 +12,12 @@ use esp_backtrace as _; use esp_hal::{ etm::Etm, gpio::{ - etm::{GpioEtmChannels, GpioEtmOutputConfig}, - Io, + etm::{Channels, OutputConfig}, Level, Pull, }, prelude::*, - timer::systimer::{etm::SysTimerEtmEvent, Periodic, SystemTimer}, + timer::systimer::{etm::Event, Periodic, SystemTimer}, }; use fugit::ExtU32; @@ -31,21 +30,20 @@ fn main() -> ! { let mut alarm0 = syst_alarms.alarm0; alarm0.set_period(1u32.secs()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = io.pins.gpio1; + let mut led = peripherals.GPIO1; // setup ETM - let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD); + let gpio_ext = Channels::new(peripherals.GPIO_SD); let led_task = gpio_ext.channel0_task.toggle( &mut led, - GpioEtmOutputConfig { + OutputConfig { open_drain: false, pull: Pull::None, initial_state: Level::High, }, ); - let timer_event = SysTimerEtmEvent::new(&mut alarm0); + let timer_event = Event::new(&mut alarm0); let etm = Etm::new(peripherals.SOC_ETM); let channel0 = etm.channel0; diff --git a/examples/src/bin/etm_gpio.rs b/examples/src/bin/etm_gpio.rs index 112fa2db789..d28b1bc3683 100644 --- a/examples/src/bin/etm_gpio.rs +++ b/examples/src/bin/etm_gpio.rs @@ -12,8 +12,7 @@ use esp_backtrace as _; use esp_hal::{ etm::Etm, gpio::{ - etm::{GpioEtmChannels, GpioEtmInputConfig, GpioEtmOutputConfig}, - Io, + etm::{Channels, InputConfig, OutputConfig}, Level, Output, Pull, @@ -25,17 +24,16 @@ use esp_hal::{ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut led = Output::new(io.pins.gpio1, Level::Low); - let button = io.pins.gpio9; + let mut led = Output::new(peripherals.GPIO1, Level::Low); + let button = peripherals.GPIO9; led.set_high(); // setup ETM - let gpio_ext = GpioEtmChannels::new(peripherals.GPIO_SD); + let gpio_ext = Channels::new(peripherals.GPIO_SD); let led_task = gpio_ext.channel0_task.toggle( - &mut led, - GpioEtmOutputConfig { + led, + OutputConfig { open_drain: false, pull: Pull::None, initial_state: Level::Low, @@ -43,7 +41,7 @@ fn main() -> ! { ); let button_event = gpio_ext .channel0_event - .falling_edge(button, GpioEtmInputConfig { pull: Pull::Down }); + .falling_edge(button, InputConfig { pull: Pull::Down }); let etm = Etm::new(peripherals.SOC_ETM); let channel0 = etm.channel0; diff --git a/examples/src/bin/etm_timer.rs b/examples/src/bin/etm_timer.rs index d382c2755d3..34552926236 100644 --- a/examples/src/bin/etm_timer.rs +++ b/examples/src/bin/etm_timer.rs @@ -1,5 +1,5 @@ //! This shows how to use the general purpose timers ETM tasks and events -//! Notice you need to import the traits esp_hal::timer::etm::{TimerEtmEvents, TimerEtmTasks} +//! Notice you need to import the traits esp_hal::timer::etm::{Events, Tasks} //% CHIPS: esp32c6 esp32h2 @@ -16,7 +16,7 @@ use esp_hal::{ peripherals::TIMG0, prelude::*, timer::timg::{ - etm::{TimerEtmEvents, TimerEtmTasks}, + etm::{Events, Tasks}, Timer, Timer0, TimerGroup, diff --git a/examples/src/bin/gpio_interrupt.rs b/examples/src/bin/gpio_interrupt.rs index 07e77725e12..befe9d6dc84 100644 --- a/examples/src/bin/gpio_interrupt.rs +++ b/examples/src/bin/gpio_interrupt.rs @@ -30,15 +30,15 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); // Set GPIO2 as an output, and set its state high initially. - let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let mut io = Io::new(peripherals.IO_MUX); io.set_interrupt_handler(handler); - let mut led = Output::new(io.pins.gpio2, Level::Low); + let mut led = Output::new(peripherals.GPIO2, Level::Low); cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let button = io.pins.gpio0; + let button = peripherals.GPIO0; } else { - let button = io.pins.gpio9; + let button = peripherals.GPIO9; } } diff --git a/examples/src/bin/hello_world.rs b/examples/src/bin/hello_world.rs index b4e280c1f0a..a90ebb234c2 100644 --- a/examples/src/bin/hello_world.rs +++ b/examples/src/bin/hello_world.rs @@ -15,7 +15,7 @@ use core::fmt::Write; use esp_backtrace as _; -use esp_hal::{delay::Delay, gpio::Io, prelude::*, uart::Uart}; +use esp_hal::{delay::Delay, prelude::*, uart::Uart}; #[entry] fn main() -> ! { @@ -23,22 +23,20 @@ fn main() -> ! { let delay = Delay::new(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Default pins for Uart/Serial communication cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio1, io.pins.gpio3); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO1, peripherals.GPIO3); } else if #[cfg(feature = "esp32c2")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio20, io.pins.gpio19); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO20, peripherals.GPIO19); } else if #[cfg(feature = "esp32c3")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio21, io.pins.gpio20); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO21, peripherals.GPIO20); } else if #[cfg(feature = "esp32c6")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO16, peripherals.GPIO17); } else if #[cfg(feature = "esp32h2")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO24, peripherals.GPIO23); } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio43, io.pins.gpio44); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO43, peripherals.GPIO44); } } diff --git a/examples/src/bin/i2c_bmp180_calibration_data.rs b/examples/src/bin/i2c_bmp180_calibration_data.rs index 582a9454ae8..2d765fc31c2 100644 --- a/examples/src/bin/i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/i2c_bmp180_calibration_data.rs @@ -12,18 +12,21 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*}; +use esp_hal::{ + i2c::master::{Config, I2c}, + prelude::*, +}; use esp_println::println; #[entry] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Create a new peripheral object with the described wiring and standard // I2C clock speed: - let mut i2c = I2c::new(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz()); + let mut i2c = I2c::new(peripherals.I2C0, Config::default()) + .with_sda(peripherals.GPIO4) + .with_scl(peripherals.GPIO5); loop { let mut data = [0u8; 22]; diff --git a/examples/src/bin/i2c_display.rs b/examples/src/bin/i2c_display.rs index f31a1a84c7e..c950def05fb 100644 --- a/examples/src/bin/i2c_display.rs +++ b/examples/src/bin/i2c_display.rs @@ -22,7 +22,11 @@ use embedded_graphics::{ text::{Alignment, Text}, }; use esp_backtrace as _; -use esp_hal::{delay::Delay, gpio::Io, i2c::I2c, prelude::*}; +use esp_hal::{ + delay::Delay, + i2c::master::{Config, I2c}, + prelude::*, +}; use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; #[entry] @@ -30,11 +34,12 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let delay = Delay::new(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); // Create a new peripheral object with the described wiring // and standard I2C clock speed - let i2c = I2c::new(peripherals.I2C0, io.pins.gpio4, io.pins.gpio5, 100.kHz()); + let i2c = I2c::new(peripherals.I2C0, Config::default()) + .with_sda(peripherals.GPIO4) + .with_scl(peripherals.GPIO5); // Initialize display let interface = I2CDisplayInterface::new(i2c); diff --git a/examples/src/bin/i2s_parallel.rs b/examples/src/bin/i2s_parallel.rs new file mode 100644 index 00000000000..5f1bd400a3f --- /dev/null +++ b/examples/src/bin/i2s_parallel.rs @@ -0,0 +1,84 @@ +//! This shows using Parallel IO to output 8 bit parallel data at 1MHz clock +//! rate with a delay of 10ms between each transfer. +//! +//! The following wiring is assumed: +//! - Data pins => GPIO16, GPIO4, GPIO17, GPIO18, GPIO5, GPIO19, GPIO12, and +//! GPIO14 +//! - Clock output pin => GPIO25 +//! +//! You can use a logic analyzer to see how the pins are used. + +//% CHIPS: esp32 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + dma::{Dma, DmaPriority, DmaTxBuf}, + dma_buffers, + i2s::parallel::{I2sParallel, TxEightBits}, + prelude::*, +}; +use log::info; + +const BUFFER_SIZE: usize = 256; + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger(log::LevelFilter::Info); + info!("Starting!"); + let peripherals = esp_hal::init(esp_hal::Config::default()); + let dma = Dma::new(peripherals.DMA); + + let delay = Delay::new(); + + let dma_channel = dma.i2s1channel; + let i2s = peripherals.I2S1; + let clock = peripherals.GPIO25; + + let pins = TxEightBits::new( + peripherals.GPIO16, + peripherals.GPIO4, + peripherals.GPIO17, + peripherals.GPIO18, + peripherals.GPIO5, + peripherals.GPIO19, + peripherals.GPIO12, + peripherals.GPIO14, + ); + + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, BUFFER_SIZE); + let mut parallel = I2sParallel::new( + i2s, + dma_channel.configure(false, DmaPriority::Priority0), + 1.MHz(), + pins, + clock, + ); + + for (i, data) in tx_buffer.chunks_mut(4).enumerate() { + let offset = i * 4; + // i2s parallel driver expects the buffer to be interleaved + data[0] = (offset + 2) as u8; + data[1] = (offset + 3) as u8; + data[2] = offset as u8; + data[3] = (offset + 1) as u8; + } + + let mut tx_buf: DmaTxBuf = + DmaTxBuf::new(tx_descriptors, tx_buffer).expect("DmaTxBuf::new failed"); + + info!("Sending {} bytes!", BUFFER_SIZE); + loop { + let xfer = match parallel.send(tx_buf) { + Ok(xfer) => xfer, + Err(_) => { + panic!("Failed to send buffer"); + } + }; + (parallel, tx_buf) = xfer.wait(); + delay.delay_millis(10); + } +} diff --git a/examples/src/bin/i2s_read.rs b/examples/src/bin/i2s_read.rs index 30f45361264..6697053cedb 100644 --- a/examples/src/bin/i2s_read.rs +++ b/examples/src/bin/i2s_read.rs @@ -20,8 +20,7 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - i2s::{DataFormat, I2s, Standard}, + i2s::master::{DataFormat, I2s, Standard}, prelude::*, }; use esp_println::println; @@ -30,8 +29,6 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); #[cfg(any(feature = "esp32", feature = "esp32s2"))] let dma_channel = dma.i2s0channel; @@ -55,20 +52,20 @@ fn main() -> ! { ); #[cfg(not(feature = "esp32"))] - let i2s = i2s.with_mclk(io.pins.gpio0); + let i2s = i2s.with_mclk(peripherals.GPIO0); let mut i2s_rx = i2s .i2s_rx - .with_bclk(io.pins.gpio2) - .with_ws(io.pins.gpio4) - .with_din(io.pins.gpio5) + .with_bclk(peripherals.GPIO2) + .with_ws(peripherals.GPIO4) + .with_din(peripherals.GPIO5) .build(); let mut transfer = i2s_rx.read_dma_circular(&mut rx_buffer).unwrap(); println!("Started transfer"); loop { - let avail = transfer.available(); + let avail = transfer.available().unwrap(); if avail > 0 { let mut rcv = [0u8; 5000]; diff --git a/examples/src/bin/i2s_sound.rs b/examples/src/bin/i2s_sound.rs index e998928b7d2..de24c709b18 100644 --- a/examples/src/bin/i2s_sound.rs +++ b/examples/src/bin/i2s_sound.rs @@ -34,8 +34,7 @@ use esp_backtrace as _; use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - i2s::{DataFormat, I2s, Standard}, + i2s::master::{DataFormat, I2s, Standard}, prelude::*, }; @@ -51,8 +50,6 @@ const SINE: [i16; 64] = [ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); #[cfg(any(feature = "esp32", feature = "esp32s2"))] let dma_channel = dma.i2s0channel; @@ -73,9 +70,9 @@ fn main() -> ! { let mut i2s_tx = i2s .i2s_tx - .with_bclk(io.pins.gpio2) - .with_ws(io.pins.gpio4) - .with_dout(io.pins.gpio5) + .with_bclk(peripherals.GPIO2) + .with_ws(peripherals.GPIO4) + .with_dout(peripherals.GPIO5) .build(); let data = @@ -96,7 +93,7 @@ fn main() -> ! { let mut transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap(); loop { - let avail = transfer.available(); + let avail = transfer.available().unwrap(); if avail > 0 { let avail = usize::min(10000, avail); for bidx in 0..avail { diff --git a/examples/src/bin/ieee802154_receive_all_frames.rs b/examples/src/bin/ieee802154_receive_all_frames.rs index f92ff43b2f0..a4ea1a5b017 100644 --- a/examples/src/bin/ieee802154_receive_all_frames.rs +++ b/examples/src/bin/ieee802154_receive_all_frames.rs @@ -26,7 +26,7 @@ fn main() -> ! { ieee802154.start_receive(); loop { - if let Some(frame) = ieee802154.get_received() { + if let Some(frame) = ieee802154.received() { println!("Received {:?}\n", &frame); } } diff --git a/examples/src/bin/ieee802154_receive_frame.rs b/examples/src/bin/ieee802154_receive_frame.rs index e1ee7e164df..ca47f53c35b 100644 --- a/examples/src/bin/ieee802154_receive_frame.rs +++ b/examples/src/bin/ieee802154_receive_frame.rs @@ -28,7 +28,7 @@ fn main() -> ! { ieee802154.start_receive(); loop { - if let Some(frame) = ieee802154.get_received() { + if let Some(frame) = ieee802154.received() { println!("Received {:?}\n", &frame); } } diff --git a/examples/src/bin/ieee802154_sniffer.rs b/examples/src/bin/ieee802154_sniffer.rs index f8789241c4a..e8ac85cffdc 100644 --- a/examples/src/bin/ieee802154_sniffer.rs +++ b/examples/src/bin/ieee802154_sniffer.rs @@ -8,7 +8,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{gpio::Io, prelude::*, reset::software_reset, uart::Uart}; +use esp_hal::{prelude::*, reset::software_reset, uart::Uart}; use esp_ieee802154::{Config, Ieee802154}; use esp_println::println; @@ -16,14 +16,12 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Default pins for Uart/Serial communication cfg_if::cfg_if! { if #[cfg(feature = "esp32c6")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio16, io.pins.gpio17); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO16, peripherals.GPIO17); } else if #[cfg(feature = "esp32h2")] { - let (mut tx_pin, mut rx_pin) = (io.pins.gpio24, io.pins.gpio23); + let (mut tx_pin, mut rx_pin) = (peripherals.GPIO24, peripherals.GPIO23); } } @@ -64,7 +62,7 @@ fn main() -> ! { ieee802154.start_receive(); loop { - if let Some(frame) = ieee802154.get_raw_received() { + if let Some(frame) = ieee802154.raw_received() { println!("@RAW {:02x?}", &frame.data); } diff --git a/examples/src/bin/lcd_cam_ov2640.rs b/examples/src/bin/lcd_cam_ov2640.rs index cdc6d64c1b2..4a419a5d3f2 100644 --- a/examples/src/bin/lcd_cam_ov2640.rs +++ b/examples/src/bin/lcd_cam_ov2640.rs @@ -28,9 +28,10 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_rx_stream_buffer, - gpio::Io, - i2c, - i2c::I2c, + i2c::{ + self, + master::{Config, I2c}, + }, lcd_cam::{ cam::{Camera, RxEightBits}, LcdCam, @@ -44,8 +45,6 @@ use esp_println::{print, println}; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); let channel = dma.channel0; @@ -53,21 +52,21 @@ fn main() -> ! { let channel = channel.configure(false, DmaPriority::Priority0); - let cam_siod = io.pins.gpio4; - let cam_sioc = io.pins.gpio5; - let cam_xclk = io.pins.gpio15; - let cam_vsync = io.pins.gpio6; - let cam_href = io.pins.gpio7; - let cam_pclk = io.pins.gpio13; + let cam_siod = peripherals.GPIO4; + let cam_sioc = peripherals.GPIO5; + let cam_xclk = peripherals.GPIO15; + let cam_vsync = peripherals.GPIO6; + let cam_href = peripherals.GPIO7; + let cam_pclk = peripherals.GPIO13; let cam_data_pins = RxEightBits::new( - io.pins.gpio11, - io.pins.gpio9, - io.pins.gpio8, - io.pins.gpio10, - io.pins.gpio12, - io.pins.gpio18, - io.pins.gpio17, - io.pins.gpio16, + peripherals.GPIO11, + peripherals.GPIO9, + peripherals.GPIO8, + peripherals.GPIO10, + peripherals.GPIO12, + peripherals.GPIO18, + peripherals.GPIO17, + peripherals.GPIO16, ); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); @@ -80,7 +79,9 @@ fn main() -> ! { delay.delay_millis(500u32); - let i2c = I2c::new(peripherals.I2C0, cam_siod, cam_sioc, 100u32.kHz()); + let i2c = I2c::new(peripherals.I2C0, Config::default()) + .with_sda(cam_siod) + .with_scl(cam_sioc); let mut sccb = Sccb::new(i2c); @@ -170,17 +171,17 @@ pub struct Sccb<'d, T> { impl<'d, T> Sccb<'d, T> where - T: i2c::Instance, + T: i2c::master::Instance, { pub fn new(i2c: I2c<'d, Blocking, T>) -> Self { Self { i2c } } - pub fn probe(&mut self, address: u8) -> Result<(), i2c::Error> { + pub fn probe(&mut self, address: u8) -> Result<(), i2c::master::Error> { self.i2c.write(address, &[]) } - pub fn read(&mut self, address: u8, reg: u8) -> Result { + pub fn read(&mut self, address: u8, reg: u8) -> Result { self.i2c.write(address, &[reg])?; let mut bytes = [0u8; 1]; @@ -188,7 +189,7 @@ where Ok(bytes[0]) } - pub fn write(&mut self, address: u8, reg: u8, data: u8) -> Result<(), i2c::Error> { + pub fn write(&mut self, address: u8, reg: u8, data: u8) -> Result<(), i2c::master::Error> { self.i2c.write(address, &[reg, data]) } } diff --git a/examples/src/bin/lcd_i8080.rs b/examples/src/bin/lcd_i8080.rs index 2937f427cae..749c5a546ee 100644 --- a/examples/src/bin/lcd_i8080.rs +++ b/examples/src/bin/lcd_i8080.rs @@ -27,7 +27,7 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority, DmaTxBuf}, dma_tx_buffer, - gpio::{Input, Io, Level, Output, Pull}, + gpio::{Input, Level, Output, Pull}, lcd_cam::{ lcd::i8080::{Config, TxEightBits, I8080}, LcdCam, @@ -41,13 +41,11 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let lcd_backlight = io.pins.gpio45; - let lcd_reset = io.pins.gpio4; - let lcd_rs = io.pins.gpio0; // Command/Data selection - let lcd_wr = io.pins.gpio47; // Write clock - let lcd_te = io.pins.gpio48; // Frame sync + let lcd_backlight = peripherals.GPIO45; + let lcd_reset = peripherals.GPIO4; + let lcd_rs = peripherals.GPIO0; // Command/Data selection + let lcd_wr = peripherals.GPIO47; // Write clock + let lcd_te = peripherals.GPIO48; // Frame sync let dma = Dma::new(peripherals.DMA); let channel = dma.channel0; @@ -63,14 +61,14 @@ fn main() -> ! { let tear_effect = Input::new(lcd_te, Pull::None); let tx_pins = TxEightBits::new( - io.pins.gpio9, - io.pins.gpio46, - io.pins.gpio3, - io.pins.gpio8, - io.pins.gpio18, - io.pins.gpio17, - io.pins.gpio16, - io.pins.gpio15, + peripherals.GPIO9, + peripherals.GPIO46, + peripherals.GPIO3, + peripherals.GPIO8, + peripherals.GPIO18, + peripherals.GPIO17, + peripherals.GPIO16, + peripherals.GPIO15, ); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); diff --git a/examples/src/bin/ledc.rs b/examples/src/bin/ledc.rs index a9e8225a290..c2358b3b5ad 100644 --- a/examples/src/bin/ledc.rs +++ b/examples/src/bin/ledc.rs @@ -11,7 +11,6 @@ use esp_backtrace as _; use esp_hal::{ - gpio::Io, ledc::{ channel::{self, ChannelIFace}, timer::{self, TimerIFace}, @@ -26,14 +25,13 @@ use esp_hal::{ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let led = io.pins.gpio0; + let led = peripherals.GPIO0; let mut ledc = Ledc::new(peripherals.LEDC); ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); - let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); + let mut lstimer0 = ledc.timer::(timer::Number::Timer0); lstimer0 .configure(timer::config::Config { @@ -43,7 +41,7 @@ fn main() -> ! { }) .unwrap(); - let mut channel0 = ledc.get_channel(channel::Number::Channel0, led); + let mut channel0 = ledc.channel(channel::Number::Channel0, led); channel0 .configure(channel::config::Config { timer: &lstimer0, diff --git a/examples/src/bin/lp_core_basic.rs b/examples/src/bin/lp_core_basic.rs index d0f38c45e14..b26acf09107 100644 --- a/examples/src/bin/lp_core_basic.rs +++ b/examples/src/bin/lp_core_basic.rs @@ -15,7 +15,7 @@ use esp_backtrace as _; use esp_hal::{ - gpio::{lp_io::LowPowerOutput, Io}, + gpio::lp_io::LowPowerOutput, lp_core::{LpCore, LpCoreWakeupSource}, prelude::*, }; @@ -26,8 +26,8 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); // configure GPIO 1 as LP output pin - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let lp_pin = LowPowerOutput::new(io.pins.gpio1); + + let lp_pin = LowPowerOutput::new(peripherals.GPIO1); let mut lp_core = LpCore::new(peripherals.LP_CORE); lp_core.stop(); diff --git a/examples/src/bin/lp_core_i2c.rs b/examples/src/bin/lp_core_i2c.rs index 55aa731c1bd..c3bbe3c6bcc 100644 --- a/examples/src/bin/lp_core_i2c.rs +++ b/examples/src/bin/lp_core_i2c.rs @@ -16,7 +16,7 @@ use esp_backtrace as _; use esp_hal::{ - gpio::{lp_io::LowPowerOutputOpenDrain, Io}, + gpio::lp_io::LowPowerOutputOpenDrain, i2c::lp_i2c::LpI2c, lp_core::{LpCore, LpCoreWakeupSource}, prelude::*, @@ -27,10 +27,8 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let lp_sda = LowPowerOutputOpenDrain::new(io.pins.gpio6); - let lp_scl = LowPowerOutputOpenDrain::new(io.pins.gpio7); + let lp_sda = LowPowerOutputOpenDrain::new(peripherals.GPIO6); + let lp_scl = LowPowerOutputOpenDrain::new(peripherals.GPIO7); let lp_i2c = LpI2c::new(peripherals.LP_I2C0, lp_sda, lp_scl, 100.kHz()); diff --git a/examples/src/bin/lp_core_uart.rs b/examples/src/bin/lp_core_uart.rs index 4ebe0ff1560..15c3d98da71 100644 --- a/examples/src/bin/lp_core_uart.rs +++ b/examples/src/bin/lp_core_uart.rs @@ -16,13 +16,10 @@ use esp_backtrace as _; use esp_hal::{ - gpio::{ - lp_io::{LowPowerInput, LowPowerOutput}, - Io, - }, + gpio::lp_io::{LowPowerInput, LowPowerOutput}, lp_core::{LpCore, LpCoreWakeupSource}, prelude::*, - uart::{config::Config, lp_uart::LpUart, Uart}, + uart::{lp_uart::LpUart, Config, Uart}, }; use esp_println::println; @@ -30,21 +27,19 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Set up (HP) UART1: let mut uart1 = Uart::new_with_config( peripherals.UART1, Config::default(), - io.pins.gpio6, - io.pins.gpio7, + peripherals.GPIO6, + peripherals.GPIO7, ) .unwrap(); // Set up (LP) UART: - let lp_tx = LowPowerOutput::new(io.pins.gpio5); - let lp_rx = LowPowerInput::new(io.pins.gpio4); + let lp_tx = LowPowerOutput::new(peripherals.GPIO5); + let lp_rx = LowPowerInput::new(peripherals.GPIO4); let lp_uart = LpUart::new(peripherals.LP_UART, lp_tx, lp_rx); let mut lp_core = LpCore::new(peripherals.LP_CORE); diff --git a/examples/src/bin/mcpwm.rs b/examples/src/bin/mcpwm.rs index 17e08e8c801..8e3281159e9 100644 --- a/examples/src/bin/mcpwm.rs +++ b/examples/src/bin/mcpwm.rs @@ -11,7 +11,6 @@ use esp_backtrace as _; use esp_hal::{ - gpio::Io, mcpwm::{operator::PwmPinConfig, timer::PwmWorkingMode, McPwm, PeripheralClockConfig}, prelude::*, }; @@ -20,8 +19,7 @@ use esp_hal::{ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let pin = io.pins.gpio0; + let pin = peripherals.GPIO0; // initialize peripheral cfg_if::cfg_if! { diff --git a/examples/src/bin/parl_io_rx.rs b/examples/src/bin/parl_io_rx.rs index 8f66c376ea4..2a0390878ce 100644 --- a/examples/src/bin/parl_io_rx.rs +++ b/examples/src/bin/parl_io_rx.rs @@ -14,8 +14,8 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, - parl_io::{no_clk_pin, BitPackOrder, ParlIoRxOnly, RxFourBits}, + gpio::NoPin, + parl_io::{BitPackOrder, ParlIoRxOnly, RxFourBits}, prelude::*, }; use esp_println::println; @@ -24,14 +24,18 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let mut rx_pins = RxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4); + let mut rx_pins = RxFourBits::new( + peripherals.GPIO1, + peripherals.GPIO2, + peripherals.GPIO3, + peripherals.GPIO4, + ); + let mut rx_clk_pin = NoPin; let parl_io = ParlIoRxOnly::new( peripherals.PARL_IO, @@ -43,7 +47,12 @@ fn main() -> ! { let mut parl_io_rx = parl_io .rx - .with_config(&mut rx_pins, no_clk_pin(), BitPackOrder::Msb, Some(0xfff)) + .with_config( + &mut rx_pins, + &mut rx_clk_pin, + BitPackOrder::Msb, + Some(0xfff), + ) .unwrap(); let mut buffer = rx_buffer; diff --git a/examples/src/bin/parl_io_tx.rs b/examples/src/bin/parl_io_tx.rs index 0df17ad398e..fc790349e78 100644 --- a/examples/src/bin/parl_io_tx.rs +++ b/examples/src/bin/parl_io_tx.rs @@ -18,7 +18,6 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::Io, parl_io::{ BitPackOrder, ClkOutPin, @@ -35,16 +34,19 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let tx_pins = TxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4); + let tx_pins = TxFourBits::new( + peripherals.GPIO1, + peripherals.GPIO2, + peripherals.GPIO3, + peripherals.GPIO4, + ); - let mut pin_conf = TxPinConfigWithValidPin::new(tx_pins, io.pins.gpio5); + let mut pin_conf = TxPinConfigWithValidPin::new(tx_pins, peripherals.GPIO5); let parl_io = ParlIoTxOnly::new( peripherals.PARL_IO, @@ -54,7 +56,7 @@ fn main() -> ! { ) .unwrap(); - let mut clock_pin = ClkOutPin::new(io.pins.gpio6); + let mut clock_pin = ClkOutPin::new(peripherals.GPIO6); let mut parl_io_tx = parl_io .tx diff --git a/examples/src/bin/pcnt_encoder.rs b/examples/src/bin/pcnt_encoder.rs index 8f2344dc098..35b716df94c 100644 --- a/examples/src/bin/pcnt_encoder.rs +++ b/examples/src/bin/pcnt_encoder.rs @@ -20,7 +20,7 @@ use core::{cell::RefCell, cmp::min, sync::atomic::Ordering}; use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, interrupt::Priority, pcnt::{channel, unit, Pcnt}, prelude::*, @@ -35,8 +35,6 @@ static VALUE: AtomicI32 = AtomicI32::new(0); fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Set up a pulse counter: println!("setup pulse counter unit 0"); let mut pcnt = Pcnt::new(peripherals.PCNT); @@ -49,18 +47,22 @@ fn main() -> ! { println!("setup channel 0"); let ch0 = &u0.channel0; - let pin_a = Input::new(io.pins.gpio4, Pull::Up); - let pin_b = Input::new(io.pins.gpio5, Pull::Up); - ch0.set_ctrl_signal(pin_a.peripheral_input()); - ch0.set_edge_signal(pin_b.peripheral_input()); + let pin_a = Input::new(peripherals.GPIO4, Pull::Up); + let pin_b = Input::new(peripherals.GPIO5, Pull::Up); + + let (input_a, _) = pin_a.split(); + let (input_b, _) = pin_b.split(); + + ch0.set_ctrl_signal(input_a.clone()); + ch0.set_edge_signal(input_b.clone()); ch0.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep); ch0.set_input_mode(channel::EdgeMode::Increment, channel::EdgeMode::Decrement); println!("setup channel 1"); let ch1 = &u0.channel1; - ch1.set_ctrl_signal(pin_b.peripheral_input()); - ch1.set_edge_signal(pin_a.peripheral_input()); + ch1.set_ctrl_signal(input_b); + ch1.set_edge_signal(input_a); ch1.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep); ch1.set_input_mode(channel::EdgeMode::Decrement, channel::EdgeMode::Increment); @@ -89,7 +91,7 @@ fn interrupt_handler() { let mut u0 = UNIT0.borrow_ref_mut(cs); let u0 = u0.as_mut().unwrap(); if u0.interrupt_is_set() { - let events = u0.get_events(); + let events = u0.events(); if events.high_limit { VALUE.fetch_add(100, Ordering::SeqCst); } else if events.low_limit { diff --git a/examples/src/bin/qspi_flash.rs b/examples/src/bin/qspi_flash.rs index 687b28ba18f..a7e9df83b54 100644 --- a/examples/src/bin/qspi_flash.rs +++ b/examples/src/bin/qspi_flash.rs @@ -32,10 +32,9 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::Io, prelude::*, spi::{ - master::{Address, Command, Spi}, + master::{Address, Command, Config, Spi}, SpiDataMode, SpiMode, }, @@ -46,22 +45,21 @@ use esp_println::{print, println}; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio4; - let sio2 = io.pins.gpio5; - let sio3 = io.pins.gpio13; - let cs = io.pins.gpio14; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO2; + let mosi = peripherals.GPIO4; + let sio2 = peripherals.GPIO5; + let sio3 = peripherals.GPIO13; + let cs = peripherals.GPIO14; } else { - let sclk = io.pins.gpio0; - let miso = io.pins.gpio1; - let mosi = io.pins.gpio2; - let sio2 = io.pins.gpio3; - let sio3 = io.pins.gpio4; - let cs = io.pins.gpio5; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO1; + let mosi = peripherals.GPIO2; + let sio2 = peripherals.GPIO3; + let sio3 = peripherals.GPIO4; + let cs = peripherals.GPIO5; } } @@ -79,14 +77,21 @@ fn main() -> ! { let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_miso(miso) - .with_sio2(sio2) - .with_sio3(sio3) - .with_cs(cs) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_sio2(sio2) + .with_sio3(sio3) + .with_cs(cs) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); let delay = Delay::new(); diff --git a/examples/src/bin/ram.rs b/examples/src/bin/ram.rs index c8c802fcc9b..70c14731ab8 100644 --- a/examples/src/bin/ram.rs +++ b/examples/src/bin/ram.rs @@ -13,6 +13,7 @@ //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +#![allow(static_mut_refs)] #![no_std] #![no_main] diff --git a/examples/src/bin/read_efuse.rs b/examples/src/bin/read_efuse.rs index b3d7163ec6f..d1f2024d146 100644 --- a/examples/src/bin/read_efuse.rs +++ b/examples/src/bin/read_efuse.rs @@ -14,15 +14,15 @@ use esp_println::println; #[entry] fn main() -> ! { - println!("MAC address {:02x?}", Efuse::get_mac_address()); - println!("Flash Encryption {:?}", Efuse::get_flash_encryption()); + println!("MAC address {:02x?}", Efuse::mac_address()); + println!("Flash Encryption {:?}", Efuse::flash_encryption()); #[cfg(feature = "esp32")] { - println!("Core Count {}", Efuse::get_core_count()); + println!("Core Count {}", Efuse::core_count()); println!("Bluetooth enabled {}", Efuse::is_bluetooth_enabled()); - println!("Chip type {:?}", Efuse::get_chip_type()); - println!("Max CPU clock {:?}", Efuse::get_max_cpu_frequency()); + println!("Chip type {:?}", Efuse::chip_type()); + println!("Max CPU clock {:?}", Efuse::max_cpu_frequency()); } loop {} diff --git a/examples/src/bin/rmt_rx.rs b/examples/src/bin/rmt_rx.rs index 948b6c8c6ef..39869dd3741 100644 --- a/examples/src/bin/rmt_rx.rs +++ b/examples/src/bin/rmt_rx.rs @@ -14,7 +14,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::{Io, Level, Output}, + gpio::{Level, Output}, prelude::*, rmt::{PulseCode, Rmt, RxChannel, RxChannelConfig, RxChannelCreator}, }; @@ -26,8 +26,7 @@ const WIDTH: usize = 80; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut out = Output::new(io.pins.gpio5, Level::Low); + let mut out = Output::new(peripherals.GPIO5, Level::Low); cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { @@ -47,27 +46,21 @@ fn main() -> ! { cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2"))] { - let mut channel = rmt.channel0.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel0.configure(peripherals.GPIO4, rx_config).unwrap(); } else if #[cfg(feature = "esp32s3")] { - let mut channel = rmt.channel7.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel7.configure(peripherals.GPIO4, rx_config).unwrap(); } else { - let mut channel = rmt.channel2.configure(io.pins.gpio4, rx_config).unwrap(); + let mut channel = rmt.channel2.configure(peripherals.GPIO4, rx_config).unwrap(); } } let delay = Delay::new(); - let mut data = [PulseCode { - level1: true, - length1: 1, - level2: false, - length2: 1, - }; 48]; + let mut data: [u32; 48] = [PulseCode::empty(); 48]; loop { for x in data.iter_mut() { - x.length1 = 0; - x.length2 = 0; + x.reset() } let transaction = channel.receive(&mut data).unwrap(); @@ -85,34 +78,34 @@ fn main() -> ! { channel = channel_res; let mut total = 0usize; for entry in &data[..data.len()] { - if entry.length1 == 0 { + if entry.length1() == 0 { break; } - total += entry.length1 as usize; + total += entry.length1() as usize; - if entry.length2 == 0 { + if entry.length2() == 0 { break; } - total += entry.length2 as usize; + total += entry.length2() as usize; } for entry in &data[..data.len()] { - if entry.length1 == 0 { + if entry.length1() == 0 { break; } - let count = WIDTH / (total / entry.length1 as usize); - let c = if entry.level1 { '-' } else { '_' }; + let count = WIDTH / (total / entry.length1() as usize); + let c = if entry.level1() { '-' } else { '_' }; for _ in 0..count + 1 { print!("{}", c); } - if entry.length2 == 0 { + if entry.length2() == 0 { break; } - let count = WIDTH / (total / entry.length2 as usize); - let c = if entry.level2 { '-' } else { '_' }; + let count = WIDTH / (total / entry.length2() as usize); + let c = if entry.level2() { '-' } else { '_' }; for _ in 0..count + 1 { print!("{}", c); } diff --git a/examples/src/bin/rmt_tx.rs b/examples/src/bin/rmt_tx.rs index 6d16c26298f..7689ce47c4a 100644 --- a/examples/src/bin/rmt_tx.rs +++ b/examples/src/bin/rmt_tx.rs @@ -13,7 +13,6 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::Io, prelude::*, rmt::{PulseCode, Rmt, TxChannel, TxChannelConfig, TxChannelCreator}, }; @@ -22,8 +21,6 @@ use esp_hal::{ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { let freq = 32.MHz(); @@ -39,27 +36,19 @@ fn main() -> ! { ..TxChannelConfig::default() }; - let mut channel = rmt.channel0.configure(io.pins.gpio4, tx_config).unwrap(); + let mut channel = rmt + .channel0 + .configure(peripherals.GPIO4, tx_config) + .unwrap(); let delay = Delay::new(); - let mut data = [PulseCode { - level1: true, - length1: 200, - level2: false, - length2: 50, - }; 20]; - - data[data.len() - 2] = PulseCode { - level1: true, - length1: 3000, - level2: false, - length2: 500, - }; - data[data.len() - 1] = PulseCode::default(); + let mut data = [PulseCode::new(true, 200, false, 50); 20]; + data[data.len() - 2] = PulseCode::new(true, 3000, false, 500); + data[data.len() - 1] = PulseCode::empty(); loop { - let transaction = channel.transmit(&data); + let transaction = channel.transmit(&data).unwrap(); channel = transaction.wait().unwrap(); delay.delay_millis(500); } diff --git a/examples/src/bin/rtc_watchdog.rs b/examples/src/bin/rtc_watchdog.rs index 255f0bf1225..58dc04619cd 100644 --- a/examples/src/bin/rtc_watchdog.rs +++ b/examples/src/bin/rtc_watchdog.rs @@ -16,7 +16,7 @@ use esp_backtrace as _; use esp_hal::{ interrupt::Priority, prelude::*, - rtc_cntl::{Rtc, Rwdt}, + rtc_cntl::{Rtc, Rwdt, RwdtStage}, }; use esp_println::println; @@ -28,7 +28,9 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); rtc.set_interrupt_handler(interrupt_handler); - rtc.rwdt.set_timeout(2000.millis()); + + rtc.rwdt.enable(); + rtc.rwdt.set_timeout(RwdtStage::Stage0, 2.secs()); rtc.rwdt.listen(); critical_section::with(|cs| RWDT.borrow_ref_mut(cs).replace(rtc.rwdt)); @@ -47,7 +49,7 @@ fn interrupt_handler() { println!("Restarting in 5 seconds..."); - rwdt.set_timeout(5000.millis()); + rwdt.set_timeout(RwdtStage::Stage0, 5.secs()); rwdt.unlisten(); }); } diff --git a/examples/src/bin/serial_interrupts.rs b/examples/src/bin/serial_interrupts.rs index 1ccfb60eda5..725f5814d24 100644 --- a/examples/src/bin/serial_interrupts.rs +++ b/examples/src/bin/serial_interrupts.rs @@ -13,13 +13,8 @@ use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::Io, prelude::*, - uart::{ - config::{AtCmdConfig, Config}, - Uart, - UartInterrupt, - }, + uart::{AtCmdConfig, Config, Uart, UartInterrupt}, Blocking, }; @@ -31,22 +26,20 @@ fn main() -> ! { let delay = Delay::new(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - // Default pins for Uart/Serial communication cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let (tx_pin, rx_pin) = (io.pins.gpio1, io.pins.gpio3); + let (tx_pin, rx_pin) = (peripherals.GPIO1, peripherals.GPIO3); } else if #[cfg(feature = "esp32c2")] { - let (tx_pin, rx_pin) = (io.pins.gpio20, io.pins.gpio19); + let (tx_pin, rx_pin) = (peripherals.GPIO20, peripherals.GPIO19); } else if #[cfg(feature = "esp32c3")] { - let (tx_pin, rx_pin) = (io.pins.gpio21, io.pins.gpio20); + let (tx_pin, rx_pin) = (peripherals.GPIO21, peripherals.GPIO20); } else if #[cfg(feature = "esp32c6")] { - let (tx_pin, rx_pin) = (io.pins.gpio16, io.pins.gpio17); + let (tx_pin, rx_pin) = (peripherals.GPIO16, peripherals.GPIO17); } else if #[cfg(feature = "esp32h2")] { - let (tx_pin, rx_pin) = (io.pins.gpio24, io.pins.gpio23); + let (tx_pin, rx_pin) = (peripherals.GPIO24, peripherals.GPIO23); } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { - let (tx_pin, rx_pin) = (io.pins.gpio43, io.pins.gpio44); + let (tx_pin, rx_pin) = (peripherals.GPIO43, peripherals.GPIO44); } } let config = Config::default().rx_fifo_full_threshold(30); diff --git a/examples/src/bin/sleep_timer.rs b/examples/src/bin/sleep_timer.rs index b742c9c5c84..18eccba9eac 100644 --- a/examples/src/bin/sleep_timer.rs +++ b/examples/src/bin/sleep_timer.rs @@ -11,7 +11,7 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, entry, - rtc_cntl::{get_reset_reason, get_wakeup_cause, sleep::TimerWakeupSource, Rtc, SocResetReason}, + rtc_cntl::{reset_reason, sleep::TimerWakeupSource, wakeup_cause, Rtc, SocResetReason}, Cpu, }; use esp_println::println; @@ -24,9 +24,9 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); println!("up and runnning!"); - let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + let reason = reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); println!("reset reason: {:?}", reason); - let wake_reason = get_wakeup_cause(); + let wake_reason = wakeup_cause(); println!("wake reason: {:?}", wake_reason); let timer = TimerWakeupSource::new(Duration::from_secs(5)); diff --git a/examples/src/bin/sleep_timer_ext0.rs b/examples/src/bin/sleep_timer_ext0.rs index 8803d2b4955..a76973af8bf 100644 --- a/examples/src/bin/sleep_timer_ext0.rs +++ b/examples/src/bin/sleep_timer_ext0.rs @@ -14,11 +14,11 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, entry, - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, rtc_cntl::{ - get_reset_reason, - get_wakeup_cause, + reset_reason, sleep::{Ext0WakeupSource, TimerWakeupSource, WakeupLevel}, + wakeup_cause, Rtc, SocResetReason, }, @@ -32,13 +32,12 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let ext0_pin = Input::new(io.pins.gpio4, Pull::None); + let ext0_pin = Input::new(peripherals.GPIO4, Pull::None); println!("up and runnning!"); - let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + let reason = reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); println!("reset reason: {:?}", reason); - let wake_reason = get_wakeup_cause(); + let wake_reason = wakeup_cause(); println!("wake reason: {:?}", wake_reason); let delay = Delay::new(); diff --git a/examples/src/bin/sleep_timer_ext1.rs b/examples/src/bin/sleep_timer_ext1.rs index 83a046c6e3b..300759c5c5b 100644 --- a/examples/src/bin/sleep_timer_ext1.rs +++ b/examples/src/bin/sleep_timer_ext1.rs @@ -14,12 +14,12 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, entry, - gpio::{Input, Io, Pull, RtcPin}, + gpio::{Input, Pull, RtcPin}, peripheral::Peripheral, rtc_cntl::{ - get_reset_reason, - get_wakeup_cause, + reset_reason, sleep::{Ext1WakeupSource, TimerWakeupSource, WakeupLevel}, + wakeup_cause, Rtc, SocResetReason, }, @@ -33,14 +33,13 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let pin_0 = Input::new(io.pins.gpio4, Pull::None); - let mut pin_2 = io.pins.gpio2; + let pin_0 = Input::new(peripherals.GPIO4, Pull::None); + let mut pin_2 = peripherals.GPIO2; println!("up and runnning!"); - let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + let reason = reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); println!("reset reason: {:?}", reason); - let wake_reason = get_wakeup_cause(); + let wake_reason = wakeup_cause(); println!("wake reason: {:?}", wake_reason); let delay = Delay::new(); diff --git a/examples/src/bin/sleep_timer_lpio.rs b/examples/src/bin/sleep_timer_lpio.rs index 73e6a8f7a75..6b432bd940e 100644 --- a/examples/src/bin/sleep_timer_lpio.rs +++ b/examples/src/bin/sleep_timer_lpio.rs @@ -15,12 +15,12 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, entry, - gpio::{Input, Io, Pull, RtcPinWithResistors}, + gpio::{Input, Pull, RtcPinWithResistors}, peripheral::Peripheral, rtc_cntl::{ - get_reset_reason, - get_wakeup_cause, + reset_reason, sleep::{Ext1WakeupSource, TimerWakeupSource, WakeupLevel}, + wakeup_cause, Rtc, SocResetReason, }, @@ -34,14 +34,13 @@ fn main() -> ! { let mut rtc = Rtc::new(peripherals.LPWR); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let pin2 = Input::new(io.pins.gpio2, Pull::None); - let mut pin3 = io.pins.gpio3; + let pin2 = Input::new(peripherals.GPIO2, Pull::None); + let mut pin3 = peripherals.GPIO3; println!("up and runnning!"); - let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + let reason = reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); println!("reset reason: {:?}", reason); - let wake_reason = get_wakeup_cause(); + let wake_reason = wakeup_cause(); println!("wake reason: {:?}", wake_reason); let delay = Delay::new(); diff --git a/examples/src/bin/sleep_timer_rtcio.rs b/examples/src/bin/sleep_timer_rtcio.rs index d857d4a06f8..f2af3eb9a3b 100644 --- a/examples/src/bin/sleep_timer_rtcio.rs +++ b/examples/src/bin/sleep_timer_rtcio.rs @@ -19,12 +19,12 @@ use esp_hal::{ delay::Delay, entry, gpio, - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, peripheral::Peripheral, rtc_cntl::{ - get_reset_reason, - get_wakeup_cause, + reset_reason, sleep::{RtcioWakeupSource, TimerWakeupSource, WakeupLevel}, + wakeup_cause, Rtc, SocResetReason, }, @@ -36,13 +36,12 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); let mut rtc = Rtc::new(peripherals.LPWR); println!("up and runnning!"); - let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + let reason = reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); println!("reset reason: {:?}", reason); - let wake_reason = get_wakeup_cause(); + let wake_reason = wakeup_cause(); println!("wake reason: {:?}", wake_reason); let delay = Delay::new(); @@ -50,16 +49,16 @@ fn main() -> ! { cfg_if::cfg_if! { if #[cfg(any(feature = "esp32c3", feature = "esp32c2"))] { - let pin2 = Input::new(io.pins.gpio2, Pull::None); - let mut pin3 = io.pins.gpio3; + let pin2 = Input::new(peripherals.GPIO2, Pull::None); + let mut pin3 = peripherals.GPIO3; let wakeup_pins: &mut [(&mut dyn gpio::RtcPinWithResistors, WakeupLevel)] = &mut [ (&mut *pin2.into_ref(), WakeupLevel::Low), (&mut pin3, WakeupLevel::High), ]; } else if #[cfg(feature = "esp32s3")] { - let pin17 = Input::new(io.pins.gpio17, Pull::None); - let mut pin18 = io.pins.gpio18; + let pin17 = Input::new(peripherals.GPIO17, Pull::None); + let mut pin18 = peripherals.GPIO18; let wakeup_pins: &mut [(&mut dyn gpio::RtcPin, WakeupLevel)] = &mut [ (&mut *pin17.into_ref(), WakeupLevel::Low), diff --git a/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs b/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs index c6dba002afc..6ea46778f86 100644 --- a/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs +++ b/examples/src/bin/spi_halfduplex_read_manufacturer_id.rs @@ -30,10 +30,9 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::Io, prelude::*, spi::{ - master::{Address, Command, Spi}, + master::{Address, Command, Config, Spi}, SpiDataMode, SpiMode, }, @@ -44,32 +43,38 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio4; - let sio2 = io.pins.gpio5; - let sio3 = io.pins.gpio13; - let cs = io.pins.gpio14; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO2; + let mosi = peripherals.GPIO4; + let sio2 = peripherals.GPIO5; + let sio3 = peripherals.GPIO13; + let cs = peripherals.GPIO14; } else { - let sclk = io.pins.gpio0; - let miso = io.pins.gpio1; - let mosi = io.pins.gpio2; - let sio2 = io.pins.gpio3; - let sio3 = io.pins.gpio4; - let cs = io.pins.gpio5; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO1; + let mosi = peripherals.GPIO2; + let sio2 = peripherals.GPIO3; + let sio3 = peripherals.GPIO4; + let cs = peripherals.GPIO5; } } - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_miso(miso) - .with_sio2(sio2) - .with_sio3(sio3) - .with_cs(cs); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_sio2(sio2) + .with_sio3(sio3) + .with_cs(cs); let delay = Delay::new(); diff --git a/examples/src/bin/spi_loopback.rs b/examples/src/bin/spi_loopback.rs index 0900d031d9f..22e92a29ca5 100644 --- a/examples/src/bin/spi_loopback.rs +++ b/examples/src/bin/spi_loopback.rs @@ -18,10 +18,12 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::Io, peripheral::Peripheral, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{Config, Spi}, + SpiMode, + }, }; use esp_println::println; @@ -29,18 +31,24 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso_mosi = io.pins.gpio2; - let cs = io.pins.gpio5; + let sclk = peripherals.GPIO0; + let miso_mosi = peripherals.GPIO2; + let cs = peripherals.GPIO5; let miso = unsafe { miso_mosi.clone_unchecked() }; - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(miso_mosi) - .with_miso(miso) - .with_cs(cs); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(miso_mosi) + .with_miso(miso) + .with_cs(cs); let delay = Delay::new(); diff --git a/examples/src/bin/spi_loopback_dma.rs b/examples/src/bin/spi_loopback_dma.rs index f081478da36..639ac030927 100644 --- a/examples/src/bin/spi_loopback_dma.rs +++ b/examples/src/bin/spi_loopback_dma.rs @@ -23,9 +23,11 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::Io, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{Config, Spi}, + SpiMode, + }, }; use esp_println::println; @@ -33,11 +35,10 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let miso = io.pins.gpio2; - let mosi = io.pins.gpio4; - let cs = io.pins.gpio5; + let sclk = peripherals.GPIO0; + let miso = peripherals.GPIO2; + let mosi = peripherals.GPIO4; + let cs = peripherals.GPIO5; let dma = Dma::new(peripherals.DMA); @@ -53,12 +54,19 @@ fn main() -> ! { let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_miso(miso) - .with_cs(cs) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_cs(cs) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); let delay = Delay::new(); diff --git a/examples/src/bin/spi_loopback_dma_psram.rs b/examples/src/bin/spi_loopback_dma_psram.rs index 1ae30408518..0a8fe54445b 100644 --- a/examples/src/bin/spi_loopback_dma_psram.rs +++ b/examples/src/bin/spi_loopback_dma_psram.rs @@ -26,10 +26,12 @@ use esp_backtrace as _; use esp_hal::{ delay::Delay, dma::{Dma, DmaBufBlkSize, DmaPriority, DmaRxBuf, DmaTxBuf}, - gpio::Io, peripheral::Peripheral, prelude::*, - spi::{master::Spi, SpiMode}, + spi::{ + master::{Config, Spi}, + SpiMode, + }, }; extern crate alloc; use log::*; @@ -60,11 +62,10 @@ fn main() -> ! { esp_alloc::psram_allocator!(peripherals.PSRAM, esp_hal::psram); let delay = Delay::new(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio42; - let mosi = io.pins.gpio48; + let sclk = peripherals.GPIO42; + let mosi = peripherals.GPIO48; let miso = unsafe { mosi.clone_unchecked() }; - let cs = io.pins.gpio38; + let cs = peripherals.GPIO38; let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; @@ -90,12 +91,19 @@ fn main() -> ! { let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); // Need to set miso first so that mosi can overwrite the // output connection (because we are using the same pin to loop back) - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_miso(miso) - .with_mosi(mosi) - .with_cs(cs) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_miso(miso) + .with_mosi(mosi) + .with_cs(cs) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); delay.delay_millis(100); // delay to let the above messages display diff --git a/examples/src/bin/spi_slave_dma.rs b/examples/src/bin/spi_slave_dma.rs index 5352c71db8b..142816166f9 100644 --- a/examples/src/bin/spi_slave_dma.rs +++ b/examples/src/bin/spi_slave_dma.rs @@ -34,7 +34,7 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::{Input, Io, Level, Output, Pull}, + gpio::{Input, Level, Output, Pull}, prelude::*, spi::{slave::Spi, SpiMode}, }; @@ -44,17 +44,15 @@ use esp_println::println; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let mut master_sclk = Output::new(peripherals.GPIO4, Level::Low); + let master_miso = Input::new(peripherals.GPIO5, Pull::None); + let mut master_mosi = Output::new(peripherals.GPIO8, Level::Low); + let mut master_cs = Output::new(peripherals.GPIO9, Level::High); - let mut master_sclk = Output::new(io.pins.gpio4, Level::Low); - let master_miso = Input::new(io.pins.gpio5, Pull::None); - let mut master_mosi = Output::new(io.pins.gpio8, Level::Low); - let mut master_cs = Output::new(io.pins.gpio9, Level::High); - - let slave_sclk = io.pins.gpio0; - let slave_miso = io.pins.gpio1; - let slave_mosi = io.pins.gpio2; - let slave_cs = io.pins.gpio3; + let slave_sclk = peripherals.GPIO0; + let slave_miso = peripherals.GPIO1; + let slave_mosi = peripherals.GPIO2; + let slave_cs = peripherals.GPIO3; let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { @@ -67,19 +65,16 @@ fn main() -> ! { let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); - let mut spi = Spi::new( - peripherals.SPI2, - slave_sclk, - slave_mosi, - slave_miso, - slave_cs, - SpiMode::Mode0, - ) - .with_dma( - dma_channel.configure(false, DmaPriority::Priority0), - tx_descriptors, - rx_descriptors, - ); + let mut spi = Spi::new(peripherals.SPI2, SpiMode::Mode0) + .with_sck(slave_sclk) + .with_mosi(slave_mosi) + .with_miso(slave_miso) + .with_cs(slave_cs) + .with_dma( + dma_channel.configure(false, DmaPriority::Priority0), + rx_descriptors, + tx_descriptors, + ); let delay = Delay::new(); diff --git a/examples/src/bin/touch.rs b/examples/src/bin/touch.rs index 22a324c28e7..a36207a8a75 100644 --- a/examples/src/bin/touch.rs +++ b/examples/src/bin/touch.rs @@ -17,7 +17,7 @@ use critical_section::Mutex; use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::{GpioPin, Io}, + gpio::GpioPin, macros::ram, prelude::*, rtc_cntl::Rtc, @@ -49,13 +49,11 @@ fn main() -> ! { esp_println::logger::init_logger_from_env(); let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut rtc = Rtc::new(peripherals.LPWR); rtc.set_interrupt_handler(interrupt_handler); - let touch_pin0 = io.pins.gpio2; - let touch_pin1 = io.pins.gpio4; + let touch_pin0 = peripherals.GPIO2; + let touch_pin1 = peripherals.GPIO4; let touch_cfg = Some(TouchConfig { measurement_duration: Some(0x2000), diff --git a/examples/src/bin/twai.rs b/examples/src/bin/twai.rs index 672ab25c50c..b59a39cda55 100644 --- a/examples/src/bin/twai.rs +++ b/examples/src/bin/twai.rs @@ -29,7 +29,6 @@ const IS_FIRST_SENDER: bool = true; use esp_backtrace as _; use esp_hal::{ delay::Delay, - gpio::Io, prelude::*, twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId, TwaiMode}, }; @@ -40,13 +39,11 @@ use nb::block; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let tx_pin = io.pins.gpio2; - // let rx_pin = io.pins.gpio0; // Uncomment if you want to use an external transceiver. - // Without an external transceiver, we only need a single line between the two MCUs. - let rx_pin = tx_pin.peripheral_input(); // Comment this line if you want to use an external transceiver. + let (rx_pin, tx_pin) = peripherals.GPIO2.split(); + // Use these if you want to use an external transceiver: + // let tx_pin = peripherals.GPIO2; + // let rx_pin = peripherals.GPIO0; // The speed of the bus. const TWAI_BAUDRATE: twai::BaudRate = twai::BaudRate::B125K; diff --git a/examples/src/bin/ulp_riscv_core_basic.rs b/examples/src/bin/ulp_riscv_core_basic.rs index d89eda74521..6d062b7f3c1 100644 --- a/examples/src/bin/ulp_riscv_core_basic.rs +++ b/examples/src/bin/ulp_riscv_core_basic.rs @@ -12,19 +12,14 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - gpio::{rtc_io::*, Io}, - prelude::*, - ulp_core, -}; +use esp_hal::{gpio::rtc_io::*, prelude::*, ulp_core}; use esp_println::{print, println}; #[entry] fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let pin = LowPowerOutput::new(io.pins.gpio1); + let pin = LowPowerOutput::new(peripherals.GPIO1); let mut ulp_core = ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE); diff --git a/examples/src/bin/usb_serial.rs b/examples/src/bin/usb_serial.rs index aee87614333..1bb93ae1a56 100644 --- a/examples/src/bin/usb_serial.rs +++ b/examples/src/bin/usb_serial.rs @@ -15,7 +15,6 @@ use core::ptr::addr_of_mut; use esp_backtrace as _; use esp_hal::{ - gpio::Io, otg_fs::{Usb, UsbBus}, prelude::*, }; @@ -28,9 +27,7 @@ static mut EP_MEMORY: [u32; 1024] = [0; 1024]; fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let usb = Usb::new(peripherals.USB0, io.pins.gpio20, io.pins.gpio19); + let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19); let usb_bus = UsbBus::new(usb, unsafe { &mut *addr_of_mut!(EP_MEMORY) }); let mut serial = SerialPort::new(&usb_bus); diff --git a/examples/src/bin/watchdog.rs b/examples/src/bin/watchdog.rs index ee01618c217..f6c6213795b 100644 --- a/examples/src/bin/watchdog.rs +++ b/examples/src/bin/watchdog.rs @@ -9,7 +9,11 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{delay::Delay, prelude::*, timer::timg::TimerGroup}; +use esp_hal::{ + delay::Delay, + prelude::*, + timer::timg::{MwdtStage, TimerGroup}, +}; use esp_println::println; #[entry] @@ -21,7 +25,7 @@ fn main() -> ! { let timg0 = TimerGroup::new_async(peripherals.TIMG0); let mut wdt0 = timg0.wdt; wdt0.enable(); - wdt0.set_timeout(2u64.secs()); + wdt0.set_timeout(MwdtStage::Stage0, 2u64.secs()); loop { wdt0.feed(); diff --git a/examples/src/bin/wifi_80211_tx.rs b/examples/src/bin/wifi_80211_tx.rs index 8cc6deb75b4..d05782790de 100644 --- a/examples/src/bin/wifi_80211_tx.rs +++ b/examples/src/bin/wifi_80211_tx.rs @@ -1,9 +1,9 @@ //! WiFi frame injection example //! //! Periodically transmits a beacon frame. -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -13,13 +13,8 @@ use core::marker::PhantomData; use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - prelude::*, - rng::Rng, - timer::{timg::TimerGroup, AnyTimer, PeriodicTimer}, -}; -use esp_wifi::{init, wifi, EspWifiInitFor}; +use esp_hal::{delay::Delay, prelude::*, rng::Rng, timer::timg::TimerGroup}; +use esp_wifi::{init, wifi}; use ieee80211::{ common::{CapabilitiesInformation, FCFFlags}, element_chain, @@ -47,12 +42,9 @@ fn main() -> ! { let delay = Delay::new(); let timg0 = TimerGroup::new(peripherals.TIMG0); - let timer0: AnyTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); let init = init( - EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, ) diff --git a/examples/src/bin/wifi_access_point.rs b/examples/src/bin/wifi_access_point.rs index fec2923ff79..249c3b32332 100644 --- a/examples/src/bin/wifi_access_point.rs +++ b/examples/src/bin/wifi_access_point.rs @@ -6,14 +6,15 @@ //! Open http://192.168.2.1:8080/ in your browser //! //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -27,15 +28,14 @@ use esp_println::{print, println}; use esp_wifi::{ init, wifi::{ + event::{self, EventExt}, utils::create_network_interface, AccessPointConfiguration, Configuration, WifiApDevice, }, - wifi_interface::WifiStack, - EspWifiInitFor, }; -use smoltcp::iface::SocketStorage; +use smoltcp::iface::{SocketSet, SocketStorage}; #[entry] fn main() -> ! { @@ -50,20 +50,36 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + // Set event handlers for wifi before init to avoid missing any. + let mut connections = 0u32; + _ = event::ApStart::replace_handler(|_, _| esp_println::println!("ap start event")); + event::ApStaconnected::update_handler(move |_, event| { + connections += 1; + esp_println::println!("connected {}, mac: {:?}", connections, event.0.mac); + }); + event::ApStaconnected::update_handler(|_, event| { + esp_println::println!("connected aid: {}", event.0.aid); + }); + event::ApStadisconnected::update_handler(|_, event| { + esp_println::println!( + "disconnected mac: {:?}, reason: {:?}", + event.0.mac, + event.0.reason + ); + }); + + let mut rng = Rng::new(peripherals.RNG); + + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; - let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let (iface, device, mut controller, sockets) = - create_network_interface(&init, &mut wifi, WifiApDevice, &mut socket_set_entries).unwrap(); + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiApDevice).unwrap(); let now = || time::now().duration_since_epoch().to_millis(); - let mut wifi_stack = WifiStack::new(iface, device, sockets, now); + + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); + let socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::AccessPoint(AccessPointConfiguration { ssid: "esp-wifi".try_into().unwrap(), @@ -75,16 +91,18 @@ fn main() -> ! { controller.start().unwrap(); println!("is wifi started: {:?}", controller.is_started()); - println!("{:?}", controller.get_capabilities()); - - wifi_stack - .set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client( - esp_wifi::wifi::ipv4::ClientConfiguration::Fixed( - esp_wifi::wifi::ipv4::ClientSettings { - ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), - subnet: esp_wifi::wifi::ipv4::Subnet { - gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), - mask: esp_wifi::wifi::ipv4::Mask(24), + println!("{:?}", controller.capabilities()); + + stack + .set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client( + blocking_network_stack::ipv4::ClientConfiguration::Fixed( + blocking_network_stack::ipv4::ClientSettings { + ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), + subnet: blocking_network_stack::ipv4::Subnet { + gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip( + "192.168.2.1", + )), + mask: blocking_network_stack::ipv4::Mask(24), }, dns: None, secondary_dns: None, @@ -98,7 +116,7 @@ fn main() -> ! { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); socket.listen(8080).unwrap(); diff --git a/examples/src/bin/wifi_access_point_with_sta.rs b/examples/src/bin/wifi_access_point_with_sta.rs index 03f963c4c7c..4321ca14259 100644 --- a/examples/src/bin/wifi_access_point_with_sta.rs +++ b/examples/src/bin/wifi_access_point_with_sta.rs @@ -7,14 +7,15 @@ //! Open http://192.168.2.1:8080/ in your browser - the example will perform an HTTP get request to some "random" server //! //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -33,11 +34,9 @@ use esp_wifi::{ ClientConfiguration, Configuration, }, - wifi_interface::WifiStack, - EspWifiInitFor, }; use smoltcp::{ - iface::SocketStorage, + iface::{SocketSet, SocketStorage}, wire::{IpAddress, Ipv4Address}, }; @@ -57,18 +56,11 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let mut rng = Rng::new(peripherals.RNG); - let wifi = peripherals.WIFI; + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); - let mut ap_socket_set_entries: [SocketStorage; 3] = Default::default(); - let mut sta_socket_set_entries: [SocketStorage; 3] = Default::default(); + let wifi = peripherals.WIFI; let ApStaInterface { ap_interface, @@ -76,19 +68,17 @@ fn main() -> ! { ap_device, sta_device, mut controller, - ap_socket_set, - sta_socket_set, - } = create_ap_sta_network_interface( - &init, - wifi, - &mut ap_socket_set_entries, - &mut sta_socket_set_entries, - ) - .unwrap(); + } = create_ap_sta_network_interface(&init, wifi).unwrap(); let now = || time::now().duration_since_epoch().to_millis(); - let mut wifi_ap_stack = WifiStack::new(ap_interface, ap_device, ap_socket_set, now); - let wifi_sta_stack = WifiStack::new(sta_interface, sta_device, sta_socket_set, now); + let mut ap_socket_set_entries: [SocketStorage; 3] = Default::default(); + let ap_socket_set = SocketSet::new(&mut ap_socket_set_entries[..]); + let mut ap_stack = Stack::new(ap_interface, ap_device, ap_socket_set, now, rng.random()); + + let mut sta_socket_set_entries: [SocketStorage; 3] = Default::default(); + let mut sta_socket_set = SocketSet::new(&mut sta_socket_set_entries[..]); + sta_socket_set.add(smoltcp::socket::dhcpv4::Socket::new()); + let sta_stack = Stack::new(sta_interface, sta_device, sta_socket_set, now, rng.random()); let client_config = Configuration::Mixed( ClientConfiguration { @@ -107,16 +97,18 @@ fn main() -> ! { controller.start().unwrap(); println!("is wifi started: {:?}", controller.is_started()); - println!("{:?}", controller.get_capabilities()); - - wifi_ap_stack - .set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client( - esp_wifi::wifi::ipv4::ClientConfiguration::Fixed( - esp_wifi::wifi::ipv4::ClientSettings { - ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), - subnet: esp_wifi::wifi::ipv4::Subnet { - gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), - mask: esp_wifi::wifi::ipv4::Mask(24), + println!("{:?}", controller.capabilities()); + + ap_stack + .set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client( + blocking_network_stack::ipv4::ClientConfiguration::Fixed( + blocking_network_stack::ipv4::ClientSettings { + ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip("192.168.2.1")), + subnet: blocking_network_stack::ipv4::Subnet { + gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip( + "192.168.2.1", + )), + mask: blocking_network_stack::ipv4::Mask(24), }, dns: None, secondary_dns: None, @@ -130,10 +122,10 @@ fn main() -> ! { // wait for STA getting an ip address println!("Wait to get an ip address"); loop { - wifi_sta_stack.work(); + sta_stack.work(); - if wifi_sta_stack.is_iface_up() { - println!("got ip {:?}", wifi_sta_stack.get_ip_info()); + if sta_stack.is_iface_up() { + println!("got ip {:?}", sta_stack.get_ip_info()); break; } } @@ -143,11 +135,11 @@ fn main() -> ! { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut ap_socket = wifi_ap_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut ap_socket = ap_stack.get_socket(&mut rx_buffer, &mut tx_buffer); let mut sta_rx_buffer = [0u8; 1536]; let mut sta_tx_buffer = [0u8; 1536]; - let mut sta_socket = wifi_sta_stack.get_socket(&mut sta_rx_buffer, &mut sta_tx_buffer); + let mut sta_socket = sta_stack.get_socket(&mut sta_rx_buffer, &mut sta_tx_buffer); ap_socket.listen(8080).unwrap(); diff --git a/examples/src/bin/wifi_bench.rs b/examples/src/bin/wifi_bench.rs index 4ab9e4fa17c..e561c0f2de6 100644 --- a/examples/src/bin/wifi_bench.rs +++ b/examples/src/bin/wifi_bench.rs @@ -6,14 +6,15 @@ //! cargo run --release //! ``` //! Ensure you have set the IP of your local machine in the `HOST_IP` env variable. E.g `HOST_IP="192.168.0.24"` and also set SSID and PASSWORD env variable before running this example. -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -35,12 +36,10 @@ use esp_wifi::{ WifiError, WifiStaDevice, }, - wifi_interface::WifiStack, - EspWifiInitFor, }; use smoltcp::{ - iface::SocketStorage, - wire::{IpAddress, Ipv4Address}, + iface::{SocketSet, SocketStorage}, + wire::{DhcpOption, IpAddress, Ipv4Address}, }; const SSID: &str = env!("SSID"); @@ -70,20 +69,26 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let mut rng = Rng::new(peripherals.RNG); + + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let (iface, device, mut controller, sockets) = - create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-wifi", + }]); + socket_set.add(dhcp_socket); + let now = || time::now().duration_since_epoch().to_millis(); - let wifi_stack = WifiStack::new(iface, device, sockets, now); + let stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), @@ -104,7 +109,7 @@ fn main() -> ! { } } - println!("{:?}", controller.get_capabilities()); + println!("{:?}", controller.capabilities()); println!("wifi_connect {:?}", controller.connect()); // wait to get connected @@ -124,17 +129,17 @@ fn main() -> ! { // wait for getting an ip address println!("Wait to get an ip address"); loop { - wifi_stack.work(); + stack.work(); - if wifi_stack.is_iface_up() { - println!("got ip {:?}", wifi_stack.get_ip_info()); + if stack.is_iface_up() { + println!("got ip {:?}", stack.get_ip_info()); break; } } let mut rx_buffer = [0u8; RX_BUFFER_SIZE]; let mut tx_buffer = [0u8; TX_BUFFER_SIZE]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); let delay = Delay::new(); @@ -151,9 +156,9 @@ fn main() -> ! { } } -fn test_download<'a>( +fn test_download<'a, D: smoltcp::phy::Device>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, + socket: &mut blocking_network_stack::Socket<'a, 'a, D>, ) { println!("Testing download..."); socket.work(); @@ -185,9 +190,9 @@ fn test_download<'a>( socket.disconnect(); } -fn test_upload<'a>( +fn test_upload<'a, D: smoltcp::phy::Device>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, + socket: &mut blocking_network_stack::Socket<'a, 'a, D>, ) { println!("Testing upload..."); socket.work(); @@ -219,9 +224,9 @@ fn test_upload<'a>( socket.disconnect(); } -fn test_upload_download<'a>( +fn test_upload_download<'a, D: smoltcp::phy::Device>( server_address: Ipv4Address, - socket: &mut esp_wifi::wifi_interface::Socket<'a, 'a, WifiStaDevice>, + socket: &mut blocking_network_stack::Socket<'a, 'a, D>, ) { println!("Testing upload+download..."); socket.work(); diff --git a/examples/src/bin/wifi_ble.rs b/examples/src/bin/wifi_ble.rs index 4ba170f332a..84e383e0787 100644 --- a/examples/src/bin/wifi_ble.rs +++ b/examples/src/bin/wifi_ble.rs @@ -25,14 +25,14 @@ use bleps::{ use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, prelude::*, rng::Rng, time, timer::timg::TimerGroup, }; use esp_println::println; -use esp_wifi::{ble::controller::BleConnector, init, EspWifiInitFor}; +use esp_wifi::{ble::controller::BleConnector, init}; #[entry] fn main() -> ! { @@ -48,20 +48,17 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let init = init( - EspWifiInitFor::Ble, timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, ) .unwrap(); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let button = Input::new(io.pins.gpio0, Pull::Down); + let button = Input::new(peripherals.GPIO0, Pull::Down); } else { - let button = Input::new(io.pins.gpio9, Pull::Down); + let button = Input::new(peripherals.GPIO9, Pull::Down); } } diff --git a/examples/src/bin/wifi_coex.rs b/examples/src/bin/wifi_coex.rs index 02aa7c8f8f3..c6926b3bc09 100644 --- a/examples/src/bin/wifi_coex.rs +++ b/examples/src/bin/wifi_coex.rs @@ -6,11 +6,12 @@ //! - does BLE advertising (you cannot connect to it - it's just not implemented in the example) //! //! Note: On ESP32-C2 and ESP32-C3 you need a wifi-heap size of 70000, on ESP32-C6 you need 80000 and a tx_queue_size of 10 -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/ble esp-wifi/coex +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/ble esp-wifi/coex //% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 +#![allow(static_mut_refs)] #![no_std] #![no_main] @@ -25,6 +26,7 @@ use bleps::{ Ble, HciConnector, }; +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -39,12 +41,10 @@ use esp_wifi::{ ble::controller::BleConnector, init, wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, - wifi_interface::WifiStack, - EspWifiInitFor, }; use smoltcp::{ - iface::SocketStorage, - wire::{IpAddress, Ipv4Address}, + iface::{SocketSet, SocketStorage}, + wire::{DhcpOption, IpAddress, Ipv4Address}, }; const SSID: &str = env!("SSID"); @@ -81,23 +81,28 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::WifiBle, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let mut rng = Rng::new(peripherals.RNG); + + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; let bluetooth = peripherals.BT; - let mut socket_set_entries: [SocketStorage; 2] = Default::default(); - let (iface, device, mut controller, sockets) = - create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-wifi", + }]); + socket_set.add(dhcp_socket); let now = || time::now().duration_since_epoch().to_millis(); - let wifi_stack = WifiStack::new(iface, device, sockets, now); + let stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), @@ -109,7 +114,7 @@ fn main() -> ! { controller.start().unwrap(); println!("is wifi started: {:?}", controller.is_started()); - println!("{:?}", controller.get_capabilities()); + println!("{:?}", controller.capabilities()); println!("wifi_connect {:?}", controller.connect()); // wait to get connected @@ -129,10 +134,10 @@ fn main() -> ! { // wait for getting an ip address println!("Wait to get an ip address"); loop { - wifi_stack.work(); + stack.work(); - if wifi_stack.is_iface_up() { - println!("got ip {:?}", wifi_stack.get_ip_info()); + if stack.is_iface_up() { + println!("got ip {:?}", stack.get_ip_info()); break; } } @@ -162,7 +167,7 @@ fn main() -> ! { let mut rx_buffer = [0u8; 128]; let mut tx_buffer = [0u8; 128]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); loop { println!("Making HTTP request"); diff --git a/examples/src/bin/wifi_csi.rs b/examples/src/bin/wifi_csi.rs new file mode 100644 index 00000000000..fd06cfe4987 --- /dev/null +++ b/examples/src/bin/wifi_csi.rs @@ -0,0 +1,142 @@ +//! CSI Example +//! +//! +//! Set SSID and PASSWORD env variable before running this example. +//! + +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/log +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 + +#![no_std] +#![no_main] + +extern crate alloc; + +use blocking_network_stack::Stack; +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{ + prelude::*, + rng::Rng, + time::{self}, + timer::timg::TimerGroup, +}; +use esp_println::println; +use esp_wifi::{ + init, + wifi::{ + utils::create_network_interface, + AccessPointInfo, + ClientConfiguration, + Configuration, + CsiConfig, + WifiError, + WifiStaDevice, + }, +}; +use smoltcp::{ + iface::{SocketSet, SocketStorage}, + wire::DhcpOption, +}; + +const SSID: &str = env!("SSID"); +const PASSWORD: &str = env!("PASSWORD"); + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + esp_alloc::heap_allocator!(72 * 1024); + + let mut rng = Rng::new(peripherals.RNG); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); + + let mut wifi = peripherals.WIFI; + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-wifi", + }]); + socket_set.add(dhcp_socket); + + let now = || time::now().duration_since_epoch().to_millis(); + let stack = Stack::new(iface, device, socket_set, now, rng.random()); + + let client_config = Configuration::Client(ClientConfiguration { + ssid: SSID.try_into().unwrap(), + password: PASSWORD.try_into().unwrap(), + ..Default::default() + }); + let res = controller.set_configuration(&client_config); + println!("wifi_set_configuration returned {:?}", res); + + controller.start().unwrap(); + println!("is wifi started: {:?}", controller.is_started()); + + let csi = CsiConfig::default(); + controller + .set_csi(csi, |data: esp_wifi::wifi::wifi_csi_info_t| { + let rx_ctrl = data.rx_ctrl; + // Signed bitfields are broken in rust-bingen, see https://github.com/esp-rs/esp-wifi-sys/issues/482 + let rssi = if rx_ctrl.rssi() > 127 { + rx_ctrl.rssi() - 256 + } else { + rx_ctrl.rssi() + }; + println!("rssi: {:?} rate: {}", rssi, rx_ctrl.rate()); + }) + .unwrap(); + + println!("Waiting for CSI data..."); + println!("Start Wifi Scan"); + let res: Result<(heapless::Vec, usize), WifiError> = controller.scan_n(); + if let Ok((res, _count)) = res { + for ap in res { + println!("{:?}", ap); + } + } + + println!("{:?}", controller.capabilities()); + println!("wifi_connect {:?}", controller.connect()); + + // wait to get connected + println!("Wait to get connected"); + loop { + match controller.is_connected() { + Ok(true) => break, + Ok(false) => {} + Err(err) => { + println!("{:?}", err); + loop {} + } + } + } + println!("{:?}", controller.is_connected()); + + // wait for getting an ip address + println!("Wait to get an ip address"); + loop { + stack.work(); + + if stack.is_iface_up() { + println!("got ip {:?}", stack.get_ip_info()); + break; + } + } + + println!("Start busy loop on main"); + loop {} +} diff --git a/examples/src/bin/wifi_dhcp.rs b/examples/src/bin/wifi_dhcp.rs index c223de675e5..81b9541575f 100644 --- a/examples/src/bin/wifi_dhcp.rs +++ b/examples/src/bin/wifi_dhcp.rs @@ -4,9 +4,9 @@ //! Set SSID and PASSWORD env variable before running this example. //! //! This gets an ip address via DHCP then performs an HTTP get request to some "random" server -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -14,6 +14,7 @@ extern crate alloc; +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -34,12 +35,10 @@ use esp_wifi::{ WifiError, WifiStaDevice, }, - wifi_interface::WifiStack, - EspWifiInitFor, }; use smoltcp::{ - iface::SocketStorage, - wire::{IpAddress, Ipv4Address}, + iface::{SocketSet, SocketStorage}, + wire::{DhcpOption, IpAddress, Ipv4Address}, }; const SSID: &str = env!("SSID"); @@ -58,20 +57,26 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let mut rng = Rng::new(peripherals.RNG); + + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let (iface, device, mut controller, sockets) = - create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-wifi", + }]); + socket_set.add(dhcp_socket); + let now = || time::now().duration_since_epoch().to_millis(); - let wifi_stack = WifiStack::new(iface, device, sockets, now); + let stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), @@ -92,7 +97,7 @@ fn main() -> ! { } } - println!("{:?}", controller.get_capabilities()); + println!("{:?}", controller.capabilities()); println!("wifi_connect {:?}", controller.connect()); // wait to get connected @@ -112,10 +117,10 @@ fn main() -> ! { // wait for getting an ip address println!("Wait to get an ip address"); loop { - wifi_stack.work(); + stack.work(); - if wifi_stack.is_iface_up() { - println!("got ip {:?}", wifi_stack.get_ip_info()); + if stack.is_iface_up() { + println!("got ip {:?}", stack.get_ip_info()); break; } } @@ -124,7 +129,7 @@ fn main() -> ! { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); loop { println!("Making HTTP request"); diff --git a/examples/src/bin/wifi_dhcp_smoltcp_nal.rs b/examples/src/bin/wifi_dhcp_smoltcp_nal.rs new file mode 100644 index 00000000000..a4ad4ca6da7 --- /dev/null +++ b/examples/src/bin/wifi_dhcp_smoltcp_nal.rs @@ -0,0 +1,224 @@ +//! DHCP Example using [smoltcp-nal](https://crates.io/crates/smoltcp-nal) +//! +//! +//! Set SSID and PASSWORD env variable before running this example. +//! +//! This gets an ip address via DHCP then performs an HTTP get request to some "random" server +//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. + +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils +//% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 + +#![no_std] +#![no_main] + +extern crate alloc; + +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{ + prelude::*, + rng::Rng, + time::{self, Duration}, + timer::timg::TimerGroup, +}; +use esp_println::{print, println}; +use esp_wifi::{ + init, + wifi::{ + utils::create_network_interface, + AccessPointInfo, + ClientConfiguration, + Configuration, + WifiError, + WifiStaDevice, + }, +}; +use smoltcp::{ + iface::{SocketSet, SocketStorage}, + wire::DhcpOption, +}; +use smoltcp_nal::{ + embedded_nal::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpClientStack}, + NetworkStack, +}; + +const SSID: &str = env!("SSID"); +const PASSWORD: &str = env!("PASSWORD"); + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init({ + let mut config = esp_hal::Config::default(); + config.cpu_clock = CpuClock::max(); + config + }); + + esp_alloc::heap_allocator!(72 * 1024); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + + let init = init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap(); + + let mut wifi = peripherals.WIFI; + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); + let mut sockets = SocketSet::new(&mut socket_set_entries[..]); + + let mut rx_buffer = [0u8; 128]; + let mut tx_buffer = [0u8; 128]; + sockets.add(smoltcp::socket::tcp::Socket::new( + smoltcp::socket::tcp::SocketBuffer::new(&mut rx_buffer[..]), + smoltcp::socket::tcp::SocketBuffer::new(&mut tx_buffer[..]), + )); + let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); + // we can set a hostname here (or add other DHCP options) + dhcp_socket.set_outgoing_options(&[DhcpOption { + kind: 12, + data: b"esp-wifi", + }]); + sockets.add(dhcp_socket); + let mut network_stack = NetworkStack::new(iface, device, sockets, StackClock); + + let client_config = Configuration::Client(ClientConfiguration { + ssid: SSID.try_into().unwrap(), + password: PASSWORD.try_into().unwrap(), + ..Default::default() + }); + let res = controller.set_configuration(&client_config); + println!("wifi_set_configuration returned {:?}", res); + + controller.start().unwrap(); + println!("is wifi started: {:?}", controller.is_started()); + + println!("Start Wifi Scan"); + let res: Result<(heapless::Vec, usize), WifiError> = controller.scan_n(); + if let Ok((res, _count)) = res { + for ap in res { + println!("{:?}", ap); + } + } + + println!("{:?}", controller.capabilities()); + println!("wifi_connect {:?}", controller.connect()); + + // wait to get connected + println!("Wait to get connected"); + loop { + match controller.is_connected() { + Ok(true) => break, + Ok(false) => {} + Err(err) => { + println!("{:?}", err); + loop {} + } + } + } + println!("{:?}", controller.is_connected()); + + // wait for getting an ip address + println!("Wait to get an ip address"); + loop { + network_stack.poll().unwrap(); + + if let Some(ip) = network_stack.interface().ipv4_addr() { + if !ip.is_unspecified() { + println!("got ip {:?}", ip); + break; + } + } + } + + println!("Start busy loop on main"); + + let mut socket = network_stack.socket().unwrap(); + + loop { + println!("Making HTTP request"); + while network_stack.poll().unwrap() {} + + with_network_stack(&mut network_stack, |network_stack| { + network_stack.connect( + &mut socket, + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(142, 250, 185, 115), 80)), + ) + }) + .unwrap(); + + println!("connected"); + + with_network_stack(&mut network_stack, |network_stack| { + network_stack.send( + &mut socket, + b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n", + ) + }) + .unwrap(); + + let deadline = time::now() + Duration::secs(20); + let mut buffer = [0u8; 512]; + while let Ok(len) = with_network_stack(&mut network_stack, |network_stack| { + network_stack.receive(&mut socket, &mut buffer) + }) { + let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) }; + print!("{}", to_print); + + if time::now() > deadline { + println!("Timeout"); + break; + } + + network_stack.poll().unwrap(); + } + println!(); + + network_stack.close_sockets(); + + let deadline = time::now() + Duration::secs(5); + while time::now() < deadline { + network_stack.poll().unwrap(); + } + } +} + +fn with_network_stack<'a, D: smoltcp::phy::Device, C: embedded_time::Clock, R>( + network_stack: &mut smoltcp_nal::NetworkStack<'a, D, C>, + mut f: impl FnMut( + &mut smoltcp_nal::NetworkStack<'a, D, C>, + ) -> smoltcp_nal::embedded_nal::nb::Result, +) -> smoltcp_nal::embedded_nal::nb::Result { + let res = loop { + let res = f(network_stack); + if let nb::Result::Err(nb::Error::WouldBlock) = res { + network_stack.poll().unwrap(); + continue; + } + + break res; + }; + + res +} + +struct StackClock; + +impl embedded_time::Clock for StackClock { + type T = u32; + + const SCALING_FACTOR: embedded_time::rate::Fraction = + embedded_time::rate::Fraction::new(1, 1_000_000); + + fn try_now(&self) -> Result, embedded_time::clock::Error> { + Ok(embedded_time::Instant::new( + esp_hal::time::now().ticks() as u32 + )) + } +} diff --git a/examples/src/bin/wifi_embassy_access_point.rs b/examples/src/bin/wifi_embassy_access_point.rs index d99dd331c2c..6296dbc598b 100644 --- a/examples/src/bin/wifi_embassy_access_point.rs +++ b/examples/src/bin/wifi_embassy_access_point.rs @@ -7,9 +7,9 @@ //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -41,7 +41,7 @@ use esp_wifi::{ WifiEvent, WifiState, }, - EspWifiInitFor, + EspWifiController, }; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html @@ -67,13 +67,15 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let (wifi_interface, controller) = @@ -201,9 +203,9 @@ async fn main(spawner: Spawner) -> ! { #[embassy_executor::task] async fn connection(mut controller: WifiController<'static>) { println!("start connection task"); - println!("Device capabilities: {:?}", controller.get_capabilities()); + println!("Device capabilities: {:?}", controller.capabilities()); loop { - match esp_wifi::wifi::get_wifi_state() { + match esp_wifi::wifi::wifi_state() { WifiState::ApStarted => { // wait until we're no longer connected controller.wait_for_event(WifiEvent::ApStop).await; @@ -218,7 +220,7 @@ async fn connection(mut controller: WifiController<'static>) { }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); - controller.start().await.unwrap(); + controller.start_async().await.unwrap(); println!("Wifi started!"); } } diff --git a/examples/src/bin/wifi_embassy_access_point_with_sta.rs b/examples/src/bin/wifi_embassy_access_point_with_sta.rs index 8beb0f16389..15eea34e088 100644 --- a/examples/src/bin/wifi_embassy_access_point_with_sta.rs +++ b/examples/src/bin/wifi_embassy_access_point_with_sta.rs @@ -10,9 +10,9 @@ //! On Android you might need to choose _Keep Accesspoint_ when it tells you the WiFi has no internet connection, Chrome might not want to load the URL - you can use a shell and try `curl` and `ping` //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -46,7 +46,7 @@ use esp_wifi::{ WifiStaDevice, WifiState, }, - EspWifiInitFor, + EspWifiController, }; const SSID: &str = env!("SSID"); @@ -75,13 +75,15 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let (wifi_ap_interface, wifi_sta_interface, mut controller) = @@ -310,18 +312,18 @@ async fn main(spawner: Spawner) -> ! { #[embassy_executor::task] async fn connection(mut controller: WifiController<'static>) { println!("start connection task"); - println!("Device capabilities: {:?}", controller.get_capabilities()); + println!("Device capabilities: {:?}", controller.capabilities()); println!("Starting wifi"); - controller.start().await.unwrap(); + controller.start_async().await.unwrap(); println!("Wifi started!"); loop { - match esp_wifi::wifi::get_ap_state() { + match esp_wifi::wifi::ap_state() { WifiState::ApStarted => { println!("About to connect..."); - match controller.connect().await { + match controller.connect_async().await { Ok(_) => { // wait until we're no longer connected controller.wait_for_event(WifiEvent::StaDisconnected).await; diff --git a/examples/src/bin/wifi_embassy_bench.rs b/examples/src/bin/wifi_embassy_bench.rs index 3214804fa88..cb3b843654d 100644 --- a/examples/src/bin/wifi_embassy_bench.rs +++ b/examples/src/bin/wifi_embassy_bench.rs @@ -8,11 +8,12 @@ //! Ensure you have set the IP of your local machine in the `HOST_IP` env variable. E.g `HOST_IP="192.168.0.24"` and also set SSID and PASSWORD env variable before running this example. //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 and ESP32-C2 -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 +#![allow(static_mut_refs)] #![no_std] #![no_main] @@ -35,7 +36,7 @@ use esp_wifi::{ WifiStaDevice, WifiState, }, - EspWifiInitFor, + EspWifiController, }; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html @@ -97,13 +98,15 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let (wifi_interface, controller) = @@ -172,9 +175,9 @@ async fn main(spawner: Spawner) -> ! { #[embassy_executor::task] async fn connection(mut controller: WifiController<'static>) { println!("start connection task"); - println!("Device capabilities: {:?}", controller.get_capabilities()); + println!("Device capabilities: {:?}", controller.capabilities()); loop { - match esp_wifi::wifi::get_wifi_state() { + match esp_wifi::wifi::wifi_state() { WifiState::StaConnected => { // wait until we're no longer connected controller.wait_for_event(WifiEvent::StaDisconnected).await; @@ -190,12 +193,12 @@ async fn connection(mut controller: WifiController<'static>) { }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); - controller.start().await.unwrap(); + controller.start_async().await.unwrap(); println!("Wifi started!"); } println!("About to connect..."); - match controller.connect().await { + match controller.connect_async().await { Ok(_) => println!("Wifi connected!"), Err(e) => { println!("Failed to connect to wifi: {e:?}"); diff --git a/examples/src/bin/wifi_embassy_ble.rs b/examples/src/bin/wifi_embassy_ble.rs index f9ab2b322e1..22ad06076d4 100644 --- a/examples/src/bin/wifi_embassy_ble.rs +++ b/examples/src/bin/wifi_embassy_ble.rs @@ -4,7 +4,7 @@ //! - offers one service with three characteristics (one is read/write, one is write only, one is read/write/notify) //! - pressing the boot-button on a dev-board will send a notification if it is subscribed -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/ble +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/ble //% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2 #![no_std] @@ -28,14 +28,24 @@ use embassy_executor::Spawner; use esp_alloc as _; use esp_backtrace as _; use esp_hal::{ - gpio::{Input, Io, Pull}, + gpio::{Input, Pull}, prelude::*, rng::Rng, time, timer::timg::TimerGroup, }; use esp_println::println; -use esp_wifi::{ble::controller::asynch::BleConnector, init, EspWifiInitFor}; +use esp_wifi::{ble::controller::BleConnector, init, EspWifiController}; + +// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { @@ -50,20 +60,21 @@ async fn main(_spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Ble, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); cfg_if::cfg_if! { if #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] { - let button = Input::new(io.pins.gpio0, Pull::Down); + let button = Input::new(peripherals.GPIO0, Pull::Down); } else { - let button = Input::new(io.pins.gpio9, Pull::Down); + let button = Input::new(peripherals.GPIO9, Pull::Down); } } diff --git a/examples/src/bin/wifi_embassy_dhcp.rs b/examples/src/bin/wifi_embassy_dhcp.rs index 65f4443cefa..2f896c422cd 100644 --- a/examples/src/bin/wifi_embassy_dhcp.rs +++ b/examples/src/bin/wifi_embassy_dhcp.rs @@ -6,9 +6,8 @@ //! This gets an ip address via DHCP then performs an HTTP get request to some "random" server //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//! When using USB-SERIAL-JTAG you have to activate the feature `phy-enable-usb` in the esp-wifi crate. -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -32,7 +31,7 @@ use esp_wifi::{ WifiStaDevice, WifiState, }, - EspWifiInitFor, + EspWifiController, }; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html @@ -61,13 +60,15 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let (wifi_interface, controller) = @@ -166,9 +167,9 @@ async fn main(spawner: Spawner) -> ! { #[embassy_executor::task] async fn connection(mut controller: WifiController<'static>) { println!("start connection task"); - println!("Device capabilities: {:?}", controller.get_capabilities()); + println!("Device capabilities: {:?}", controller.capabilities()); loop { - match esp_wifi::wifi::get_wifi_state() { + match esp_wifi::wifi::wifi_state() { WifiState::StaConnected => { // wait until we're no longer connected controller.wait_for_event(WifiEvent::StaDisconnected).await; @@ -184,12 +185,12 @@ async fn connection(mut controller: WifiController<'static>) { }); controller.set_configuration(&client_config).unwrap(); println!("Starting wifi"); - controller.start().await.unwrap(); + controller.start_async().await.unwrap(); println!("Wifi started!"); } println!("About to connect..."); - match controller.connect().await { + match controller.connect_async().await { Ok(_) => println!("Wifi connected!"), Err(e) => { println!("Failed to connect to wifi: {e:?}"); diff --git a/examples/src/bin/wifi_embassy_esp_now.rs b/examples/src/bin/wifi_embassy_esp_now.rs index ada75152122..a99b3c74a02 100644 --- a/examples/src/bin/wifi_embassy_esp_now.rs +++ b/examples/src/bin/wifi_embassy_esp_now.rs @@ -4,7 +4,7 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -20,9 +20,19 @@ use esp_println::println; use esp_wifi::{ esp_now::{PeerInfo, BROADCAST_ADDRESS}, init, - EspWifiInitFor, + EspWifiController, }; +// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + #[esp_hal_embassy::main] async fn main(_spawner: Spawner) -> ! { esp_println::logger::init_logger_from_env(); @@ -36,17 +46,19 @@ async fn main(_spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let mut esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); - println!("esp-now version {}", esp_now.get_version().unwrap()); + println!("esp-now version {}", esp_now.version().unwrap()); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { diff --git a/examples/src/bin/wifi_embassy_esp_now_duplex.rs b/examples/src/bin/wifi_embassy_esp_now_duplex.rs index c8bf8e4e078..f75d5d65ea5 100644 --- a/examples/src/bin/wifi_embassy_esp_now_duplex.rs +++ b/examples/src/bin/wifi_embassy_esp_now_duplex.rs @@ -4,7 +4,7 @@ //! //! Because of the huge task-arena size configured this won't work on ESP32-S2 -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/embassy-net esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -20,7 +20,7 @@ use esp_println::println; use esp_wifi::{ esp_now::{EspNowManager, EspNowReceiver, EspNowSender, PeerInfo, BROADCAST_ADDRESS}, init, - EspWifiInitFor, + EspWifiController, }; // When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html @@ -46,17 +46,19 @@ async fn main(spawner: Spawner) -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); let wifi = peripherals.WIFI; let esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); - println!("esp-now version {}", esp_now.get_version().unwrap()); + println!("esp-now version {}", esp_now.version().unwrap()); cfg_if::cfg_if! { if #[cfg(feature = "esp32")] { @@ -117,7 +119,7 @@ async fn broadcaster(sender: &'static Mutex> async fn listener(manager: &'static EspNowManager<'static>, mut receiver: EspNowReceiver<'static>) { loop { let r = receiver.receive_async().await; - println!("Received {:?}", r.get_data()); + println!("Received {:?}", r.data()); if r.info.dst_address == BROADCAST_ADDRESS { if !manager.peer_exists(&r.info.src_address) { manager diff --git a/examples/src/bin/wifi_embassy_trouble.rs b/examples/src/bin/wifi_embassy_trouble.rs index 2e3d6672465..a5f55a02750 100644 --- a/examples/src/bin/wifi_embassy_trouble.rs +++ b/examples/src/bin/wifi_embassy_trouble.rs @@ -5,7 +5,7 @@ //! - automatically notifies subscribers every second //! -//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/async esp-wifi/ble +//% FEATURES: embassy embassy-generic-timers esp-wifi esp-wifi/ble //% CHIPS: esp32 esp32s3 esp32c2 esp32c3 esp32c6 esp32h2 #![no_std] @@ -18,8 +18,8 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_time::{Duration, Timer}; use esp_alloc as _; use esp_backtrace as _; -use esp_hal::{prelude::*, timer::timg::TimerGroup}; -use esp_wifi::ble::controller::asynch::BleConnector; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; +use esp_wifi::{ble::controller::BleConnector, init, EspWifiController}; use log::*; use static_cell::StaticCell; use trouble_host::{ @@ -31,6 +31,16 @@ use trouble_host::{ PacketQos, }; +// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html +macro_rules! mk_static { + ($t:ty,$val:expr) => {{ + static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); + #[deny(unused_attributes)] + let x = STATIC_CELL.uninit().write(($val)); + x + }}; +} + #[esp_hal_embassy::main] async fn main(_s: Spawner) { esp_println::logger::init_logger_from_env(); @@ -44,13 +54,15 @@ async fn main(_s: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = esp_wifi::init( - esp_wifi::EspWifiInitFor::Ble, - timg0.timer0, - esp_hal::rng::Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let init = &*mk_static!( + EspWifiController<'static>, + init( + timg0.timer0, + Rng::new(peripherals.RNG), + peripherals.RADIO_CLK, + ) + .unwrap() + ); #[cfg(feature = "esp32")] { diff --git a/examples/src/bin/wifi_esp_now.rs b/examples/src/bin/wifi_esp_now.rs index 294bddc1e52..dc32a787900 100644 --- a/examples/src/bin/wifi_esp_now.rs +++ b/examples/src/bin/wifi_esp_now.rs @@ -2,7 +2,7 @@ //! //! Broadcasts, receives and sends messages via esp-now -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/esp-now //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -20,7 +20,6 @@ use esp_println::println; use esp_wifi::{ esp_now::{PeerInfo, BROADCAST_ADDRESS}, init, - EspWifiInitFor, }; #[entry] @@ -37,7 +36,6 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); let init = init( - EspWifiInitFor::Wifi, timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, @@ -47,7 +45,7 @@ fn main() -> ! { let wifi = peripherals.WIFI; let mut esp_now = esp_wifi::esp_now::EspNow::new(&init, wifi).unwrap(); - println!("esp-now version {}", esp_now.get_version().unwrap()); + println!("esp-now version {}", esp_now.version().unwrap()); let mut next_send_time = time::now() + Duration::secs(5); loop { diff --git a/examples/src/bin/wifi_sniffer.rs b/examples/src/bin/wifi_sniffer.rs index 2e82c66f57a..d6c26840c88 100644 --- a/examples/src/bin/wifi_sniffer.rs +++ b/examples/src/bin/wifi_sniffer.rs @@ -1,9 +1,9 @@ //! WiFi sniffer example //! //! Sniffs for beacon frames. -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-wifi/sniffer //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] @@ -19,13 +19,9 @@ use core::cell::RefCell; use critical_section::Mutex; use esp_backtrace as _; -use esp_hal::{ - prelude::*, - rng::Rng, - timer::{timg::TimerGroup, AnyTimer, PeriodicTimer}, -}; +use esp_hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; use esp_println::println; -use esp_wifi::{init, wifi, EspWifiInitFor}; +use esp_wifi::{init, wifi}; use ieee80211::{match_frames, mgmt_frame::BeaconFrame}; static KNOWN_SSIDS: Mutex>> = Mutex::new(RefCell::new(BTreeSet::new())); @@ -42,12 +38,8 @@ fn main() -> ! { esp_alloc::heap_allocator!(72 * 1024); let timg0 = TimerGroup::new(peripherals.TIMG0); - let timer0: AnyTimer = timg0.timer0.into(); - let timer = PeriodicTimer::new(timer0); - let init = init( - EspWifiInitFor::Wifi, - timer, + timg0.timer0, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, ) diff --git a/examples/src/bin/wifi_static_ip.rs b/examples/src/bin/wifi_static_ip.rs index 4e0742e3049..8344a610305 100644 --- a/examples/src/bin/wifi_static_ip.rs +++ b/examples/src/bin/wifi_static_ip.rs @@ -5,14 +5,15 @@ //! - might be necessary to configure your WiFi access point accordingly //! - uses the given static IP //! - responds with some HTML content when connecting to port 8080 -//! When using USB-SERIAL-JTAG you may have to activate the feature `phy-enable-usb` in the esp-wifi crate. +//! -//% FEATURES: esp-wifi esp-wifi/wifi-default esp-wifi/wifi esp-wifi/utils +//% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] +use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; @@ -33,10 +34,8 @@ use esp_wifi::{ WifiError, WifiStaDevice, }, - wifi_interface::WifiStack, - EspWifiInitFor, }; -use smoltcp::iface::SocketStorage; +use smoltcp::iface::{SocketSet, SocketStorage}; const SSID: &str = env!("SSID"); const PASSWORD: &str = env!("PASSWORD"); @@ -56,21 +55,19 @@ fn main() -> ! { let timg0 = TimerGroup::new(peripherals.TIMG0); - let init = init( - EspWifiInitFor::Wifi, - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap(); + let mut rng = Rng::new(peripherals.RNG); + + let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; + let (iface, device, mut controller) = + create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); + let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let (iface, device, mut controller, sockets) = - create_network_interface(&init, &mut wifi, WifiStaDevice, &mut socket_set_entries).unwrap(); + let socket_set = SocketSet::new(&mut socket_set_entries[..]); let now = || time::now().duration_since_epoch().to_millis(); - let mut wifi_stack = WifiStack::new(iface, device, sockets, now); + let mut stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), @@ -91,7 +88,7 @@ fn main() -> ! { } } - println!("{:?}", controller.get_capabilities()); + println!("{:?}", controller.capabilities()); println!("wifi_connect {:?}", controller.connect()); // wait to get connected @@ -110,14 +107,14 @@ fn main() -> ! { println!("Setting static IP {}", STATIC_IP); - wifi_stack - .set_iface_configuration(&esp_wifi::wifi::ipv4::Configuration::Client( - esp_wifi::wifi::ipv4::ClientConfiguration::Fixed( - esp_wifi::wifi::ipv4::ClientSettings { - ip: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)), - subnet: esp_wifi::wifi::ipv4::Subnet { - gateway: esp_wifi::wifi::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)), - mask: esp_wifi::wifi::ipv4::Mask(24), + stack + .set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client( + blocking_network_stack::ipv4::ClientConfiguration::Fixed( + blocking_network_stack::ipv4::ClientSettings { + ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)), + subnet: blocking_network_stack::ipv4::Subnet { + gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)), + mask: blocking_network_stack::ipv4::Mask(24), }, dns: None, secondary_dns: None, @@ -133,7 +130,7 @@ fn main() -> ! { let mut rx_buffer = [0u8; 1536]; let mut tx_buffer = [0u8; 1536]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); + let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer); socket.listen(8080).unwrap(); diff --git a/extras/esp-wifishark/README.md b/extras/esp-wifishark/README.md index 1a10a828f45..887a3e057c2 100644 --- a/extras/esp-wifishark/README.md +++ b/extras/esp-wifishark/README.md @@ -1,6 +1,6 @@ # esp-wifishark -This is an extcap to be used with esp-wifi and the `dump-packets` feature. +This is an extcap to be used with esp-wifi and the `dump_packets` config option. To use it build via `cargo build --release` and copy the resulting executable to the Wireshark's `extcap` folder. @@ -8,6 +8,6 @@ Then you should see two new capture interfaces in Wireshark - esp-wifi HCI capture (for Bluetooth HCI) - esp-wifi Ethernet capture (for WiFi traffic) -If you are running an application using esp-wifi's `dump-packets` feature and logging at INFO level active these capture interfaces can connect via serialport to give you insights on what is going on. +If you are running an application using esp-wifi's `dump_packets` config and logging at INFO level active these capture interfaces can connect via serialport to give you insights on what is going on. By default it tries to identify exactly one serialport. If that doesn't work for you, you can configure the serialport via the Wireshark UI. diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 300190c173a..0915ffed01e 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -55,6 +55,10 @@ harness = false name = "gpio" harness = false +[[test]] +name = "gpio_custom_handler" +harness = false + [[test]] name = "interrupt" harness = false @@ -235,50 +239,46 @@ esp32 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32", "esp-hal/esp32", - "esp-hal-embassy/esp32", + "esp-hal-embassy?/esp32", "esp-wifi?/esp32", ] esp32c2 = [ "esp-backtrace/esp32c2", "esp-hal/esp32c2", - "esp-hal-embassy/esp32c2", + "esp-hal-embassy?/esp32c2", "esp-wifi?/esp32c2", ] esp32c3 = [ "esp-backtrace/esp32c3", "esp-hal/esp32c3", - "esp-hal-embassy/esp32c3", + "esp-hal-embassy?/esp32c3", "esp-wifi?/esp32c3", - "esp-wifi?/phy-enable-usb", ] esp32c6 = [ "esp-backtrace/esp32c6", "esp-hal/esp32c6", - "esp-hal-embassy/esp32c6", + "esp-hal-embassy?/esp32c6", "esp-wifi?/esp32c6", - "esp-wifi?/phy-enable-usb", ] esp32h2 = [ "esp-backtrace/esp32h2", "esp-hal/esp32h2", - "esp-hal-embassy/esp32h2", + "esp-hal-embassy?/esp32h2", "esp-wifi?/esp32h2", - "esp-wifi?/phy-enable-usb", ] esp32s2 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32s2", "esp-hal/esp32s2", - "esp-hal-embassy/esp32s2", + "esp-hal-embassy?/esp32s2", "esp-wifi?/esp32s2", ] esp32s3 = [ "embedded-test/xtensa-semihosting", "esp-backtrace/esp32s3", "esp-hal/esp32s3", - "esp-hal-embassy/esp32s3", + "esp-hal-embassy?/esp32s3", "esp-wifi?/esp32s3", - "esp-wifi?/phy-enable-usb", ] # Async & Embassy: embassy = [ diff --git a/hil-test/src/lib.rs b/hil-test/src/lib.rs index 8dbf121735e..660e8697cbe 100644 --- a/hil-test/src/lib.rs +++ b/hil-test/src/lib.rs @@ -27,21 +27,21 @@ use esp_backtrace as _; #[macro_export] macro_rules! i2c_pins { - ($io:expr) => {{ + ($peripherals:expr) => {{ // Order: (SDA, SCL) cfg_if::cfg_if! { if #[cfg(any(esp32s2, esp32s3))] { - ($io.pins.gpio2, $io.pins.gpio3) + ($peripherals.GPIO2, $peripherals.GPIO3) } else if #[cfg(esp32)] { - ($io.pins.gpio32, $io.pins.gpio33) + ($peripherals.GPIO32, $peripherals.GPIO33) } else if #[cfg(esp32c6)] { - ($io.pins.gpio6, $io.pins.gpio7) + ($peripherals.GPIO6, $peripherals.GPIO7) } else if #[cfg(esp32h2)] { - ($io.pins.gpio12, $io.pins.gpio22) + ($peripherals.GPIO12, $peripherals.GPIO22) } else if #[cfg(esp32c2)] { - ($io.pins.gpio18, $io.pins.gpio9) + ($peripherals.GPIO18, $peripherals.GPIO9) } else { - ($io.pins.gpio4, $io.pins.gpio5) + ($peripherals.GPIO4, $peripherals.GPIO5) } } }}; @@ -49,14 +49,14 @@ macro_rules! i2c_pins { #[macro_export] macro_rules! common_test_pins { - ($io:expr) => {{ + ($peripherals:expr) => {{ cfg_if::cfg_if! { if #[cfg(any(esp32s2, esp32s3))] { - ($io.pins.gpio9, $io.pins.gpio10) + ($peripherals.GPIO9, $peripherals.GPIO10) } else if #[cfg(esp32)] { - ($io.pins.gpio26, $io.pins.gpio27) + ($peripherals.GPIO26, $peripherals.GPIO27) } else { - ($io.pins.gpio2, $io.pins.gpio3) + ($peripherals.GPIO2, $peripherals.GPIO3) } } }}; @@ -66,18 +66,18 @@ macro_rules! common_test_pins { // beware: it has a pullup. #[macro_export] macro_rules! unconnected_pin { - ($io:expr) => {{ + ($peripherals:expr) => {{ cfg_if::cfg_if! { if #[cfg(any(esp32, esp32s2, esp32s3))] { - $io.pins.gpio0 + $peripherals.GPIO0 } else if #[cfg(esp32c6)] { - $io.pins.gpio9 + $peripherals.GPIO9 } else if #[cfg(esp32h2)] { - $io.pins.gpio9 + $peripherals.GPIO9 } else if #[cfg(esp32c2)] { - $io.pins.gpio8 + $peripherals.GPIO8 } else { - $io.pins.gpio9 + $peripherals.GPIO9 } } }}; diff --git a/hil-test/tests/embassy_interrupt_executor.rs b/hil-test/tests/embassy_interrupt_executor.rs index 4aad1a38650..c21fed86dc4 100644 --- a/hil-test/tests/embassy_interrupt_executor.rs +++ b/hil-test/tests/embassy_interrupt_executor.rs @@ -16,7 +16,7 @@ use esp_hal::{ software::{SoftwareInterrupt, SoftwareInterruptControl}, Priority, }, - timer::timg::TimerGroup, + timer::AnyTimer, }; use esp_hal_embassy::InterruptExecutor; use hil_test as _; @@ -56,8 +56,31 @@ mod test { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_hal_embassy::init(timg0.timer0); + cfg_if::cfg_if! { + if #[cfg(timg_timer1)] { + use esp_hal::timer::timg::TimerGroup; + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init([ + AnyTimer::from(timg0.timer0), + AnyTimer::from(timg0.timer1), + ]); + } else if #[cfg(timg1)] { + use esp_hal::timer::timg::TimerGroup; + let timg0 = TimerGroup::new(peripherals.TIMG0); + let timg1 = TimerGroup::new(peripherals.TIMG1); + esp_hal_embassy::init([ + AnyTimer::from(timg0.timer0), + AnyTimer::from(timg1.timer0), + ]); + } else if #[cfg(systimer)] { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init([ + AnyTimer::from(systimer.alarm0), + AnyTimer::from(systimer.alarm1), + ]); + } + } let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); Context { diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index 65499ab4a5b..082fd2dceca 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -1,6 +1,6 @@ //! Reproduction and regression test for a sneaky issue. -//% CHIPS: esp32 esp32s2 esp32s3 +//% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 esp32h2 //% FEATURES: integrated-timers //% FEATURES: generic-queue @@ -12,16 +12,18 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, interrupt::{software::SoftwareInterruptControl, Priority}, + peripheral::Peripheral, prelude::*, spi::{ - master::{Spi, SpiDma}, + master::{Config, Spi}, SpiMode, }, - timer::{timg::TimerGroup, AnyTimer}, + timer::AnyTimer, Async, }; use esp_hal_embassy::InterruptExecutor; use hil_test as _; +use portable_atomic::AtomicBool; macro_rules! mk_static { ($t:ty,$val:expr) => {{ @@ -32,8 +34,11 @@ macro_rules! mk_static { }}; } +static INTERRUPT_TASK_WORKING: AtomicBool = AtomicBool::new(false); + +#[cfg(any(esp32, esp32s2, esp32s3))] #[embassy_executor::task] -async fn interrupt_driven_task(spi: SpiDma<'static, Async>) { +async fn interrupt_driven_task(spi: esp_hal::spi::master::SpiDma<'static, Async>) { let mut ticker = Ticker::every(Duration::from_millis(1)); let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(128); @@ -45,7 +50,25 @@ async fn interrupt_driven_task(spi: SpiDma<'static, Async>) { loop { let mut buffer: [u8; 8] = [0; 8]; + INTERRUPT_TASK_WORKING.fetch_not(portable_atomic::Ordering::Release); spi.transfer_in_place_async(&mut buffer).await.unwrap(); + INTERRUPT_TASK_WORKING.fetch_not(portable_atomic::Ordering::Acquire); + + ticker.next().await; + } +} + +#[cfg(not(any(esp32, esp32s2, esp32s3)))] +#[embassy_executor::task] +async fn interrupt_driven_task(mut i2s_tx: esp_hal::i2s::master::I2sTx<'static, Async>) { + let mut ticker = Ticker::every(Duration::from_millis(1)); + + loop { + let mut buffer: [u8; 8] = [0; 8]; + + INTERRUPT_TASK_WORKING.fetch_not(portable_atomic::Ordering::Release); + i2s_tx.write_dma_async(&mut buffer).await.unwrap(); + INTERRUPT_TASK_WORKING.fetch_not(portable_atomic::Ordering::Acquire); ticker.next().await; } @@ -60,12 +83,26 @@ mod test { #[timeout(3)] async fn dma_does_not_lock_up_when_used_in_different_executors() { let peripherals = esp_hal::init(esp_hal::Config::default()); - - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_hal_embassy::init([AnyTimer::from(timg0.timer0), AnyTimer::from(timg0.timer1)]); - let dma = Dma::new(peripherals.DMA); + cfg_if::cfg_if! { + if #[cfg(systimer)] { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init([ + AnyTimer::from(systimer.alarm0), + AnyTimer::from(systimer.alarm1), + ]); + } else { + use esp_hal::timer::timg::TimerGroup; + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init([ + AnyTimer::from(timg0.timer0), + AnyTimer::from(timg0.timer1), + ]); + } + } + cfg_if::cfg_if! { if #[cfg(pdma)] { let dma_channel1 = dma.spi2channel; @@ -80,12 +117,53 @@ mod test { let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_dma(dma_channel1.configure_for_async(false, DmaPriority::Priority0)) - .with_buffers(dma_rx_buf, dma_tx_buf); - - let spi2 = Spi::new(peripherals.SPI3, 100.kHz(), SpiMode::Mode0) - .with_dma(dma_channel2.configure_for_async(false, DmaPriority::Priority0)); + let (_, mosi) = hil_test::common_test_pins!(peripherals); + + let mut spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_miso(unsafe { mosi.clone_unchecked() }) + .with_mosi(mosi) + .with_dma(dma_channel1.configure(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf) + .into_async(); + + #[cfg(any(esp32, esp32s2, esp32s3))] + let other_peripheral = Spi::new_with_config( + peripherals.SPI3, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_dma(dma_channel2.configure(false, DmaPriority::Priority0)) + .into_async(); + + #[cfg(not(any(esp32, esp32s2, esp32s3)))] + let other_peripheral = { + let (_, rx_descriptors, _, tx_descriptors) = dma_buffers!(128); + + let other_peripheral = esp_hal::i2s::master::I2s::new( + peripherals.I2S0, + esp_hal::i2s::master::Standard::Philips, + esp_hal::i2s::master::DataFormat::Data8Channel8, + 8u32.kHz(), + dma_channel2.configure(false, DmaPriority::Priority0), + rx_descriptors, + tx_descriptors, + ) + .into_async() + .i2s_tx + .build(); + + other_peripheral + }; let sw_ints = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); @@ -96,16 +174,28 @@ mod test { let spawner = interrupt_executor.start(Priority::Priority3); - spawner.spawn(interrupt_driven_task(spi2)).unwrap(); + spawner + .spawn(interrupt_driven_task(other_peripheral)) + .unwrap(); let start = Instant::now(); let mut buffer: [u8; 1024] = [0; 1024]; + let mut dst_buffer: [u8; 1024] = [0; 1024]; + let mut i = 0; loop { - spi.transfer_in_place_async(&mut buffer).await.unwrap(); + buffer.fill(i); + dst_buffer.fill(i.wrapping_add(1)); + spi.transfer_async(&mut dst_buffer, &buffer).await.unwrap(); + // make sure the transfer didn't end prematurely + assert!(dst_buffer.iter().all(|&v| v == i)); if start.elapsed() > Duration::from_secs(1) { + // make sure the other peripheral didn't get stuck + while INTERRUPT_TASK_WORKING.load(portable_atomic::Ordering::Acquire) {} break; } + + i = i.wrapping_add(1); } } @@ -140,13 +230,21 @@ mod test { let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); - let mut spi = Spi::new(peripherals.spi, 100.kHz(), SpiMode::Mode0) - .with_dma( - peripherals - .dma_channel - .configure_for_async(false, DmaPriority::Priority0), - ) - .with_buffers(dma_rx_buf, dma_tx_buf); + let mut spi = Spi::new_with_config( + peripherals.spi, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_dma( + peripherals + .dma_channel + .configure(false, DmaPriority::Priority0), + ) + .with_buffers(dma_rx_buf, dma_tx_buf) + .into_async(); let send_buffer = mk_static!([u8; BUFFER_SIZE], [0u8; BUFFER_SIZE]); loop { @@ -161,8 +259,23 @@ mod test { let peripherals = esp_hal::init(esp_hal::Config::default()); let dma = Dma::new(peripherals.DMA); - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_hal_embassy::init(timg0.timer0); + cfg_if::cfg_if! { + if #[cfg(systimer)] { + use esp_hal::timer::systimer::{SystemTimer, Target}; + let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + esp_hal_embassy::init([ + AnyTimer::from(systimer.alarm0), + AnyTimer::from(systimer.alarm1), + ]); + } else { + use esp_hal::timer::timg::TimerGroup; + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init([ + AnyTimer::from(timg0.timer0), + AnyTimer::from(timg0.timer1), + ]); + } + } cfg_if::cfg_if! { if #[cfg(pdma)] { diff --git a/hil-test/tests/esp_wifi_floats.rs b/hil-test/tests/esp_wifi_floats.rs index 0b9a82c8ac7..f04fe728336 100644 --- a/hil-test/tests/esp_wifi_floats.rs +++ b/hil-test/tests/esp_wifi_floats.rs @@ -17,7 +17,6 @@ use esp_hal::{ rng::Rng, timer::timg::TimerGroup, }; -use esp_wifi::{init, EspWifiInitFor}; use hil_test as _; #[inline(never)] @@ -101,8 +100,7 @@ mod tests { #[timeout(3)] fn fpu_stays_enabled_with_wifi(peripherals: Peripherals) { let timg0 = TimerGroup::new(peripherals.TIMG0); - let _init = init( - EspWifiInitFor::Wifi, + let _init = esp_wifi::init( timg0.timer1, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, @@ -138,8 +136,7 @@ mod tests { unsafe { &mut *core::ptr::addr_of_mut!(APP_CORE_STACK) }, move || { let timg0 = TimerGroup::new(peripherals.TIMG0); - let _init = init( - EspWifiInitFor::Wifi, + let _init = esp_wifi::init( timg0.timer1, Rng::new(peripherals.RNG), peripherals.RADIO_CLK, diff --git a/hil-test/tests/gpio.rs b/hil-test/tests/gpio.rs index eaed133de9e..0d197b2d17d 100644 --- a/hil-test/tests/gpio.rs +++ b/hil-test/tests/gpio.rs @@ -51,12 +51,12 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let mut io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let mut io = Io::new(peripherals.IO_MUX); io.set_interrupt_handler(interrupt_handler); let delay = Delay::new(); - let (gpio1, gpio2) = hil_test::common_test_pins!(io); + let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals); let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); @@ -387,8 +387,8 @@ mod tests { #[cfg(esp32)] #[test] fn can_configure_rtcio_pins_as_input() { - let pins = unsafe { esp_hal::gpio::Pins::steal() }; + let pin = unsafe { esp_hal::gpio::GpioPin::<37>::steal() }; - _ = Input::new(pins.gpio37, Pull::Down); + _ = Input::new(pin, Pull::Down); } } diff --git a/hil-test/tests/gpio_custom_handler.rs b/hil-test/tests/gpio_custom_handler.rs new file mode 100644 index 00000000000..67f2d529870 --- /dev/null +++ b/hil-test/tests/gpio_custom_handler.rs @@ -0,0 +1,97 @@ +//! GPIO interrupt handler tests +//! +//! This test checks that during HAL initialization we do not overwrite custom +//! GPIO interrupt handlers. We also check that binding a custom interrupt +//! handler explicitly overwrites the handler set by the user, as well as the +//! async API works for user handlers automatically. + +//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: integrated-timers + +#![no_std] +#![no_main] + +use embassy_time::{Duration, Timer}; +use esp_hal::{ + gpio::{AnyPin, Input, Io, Level, Output, Pull}, + macros::handler, + timer::timg::TimerGroup, + InterruptConfigurable, +}; +use hil_test as _; +use portable_atomic::{AtomicUsize, Ordering}; + +#[no_mangle] +unsafe extern "C" fn GPIO() { + // do nothing, prevents binding the default handler +} + +#[handler] +pub fn interrupt_handler() { + // Do nothing +} + +async fn drive_pins(gpio1: impl Into, gpio2: impl Into) -> usize { + let counter = AtomicUsize::new(0); + let mut test_gpio1 = Input::new(gpio1.into(), Pull::Down); + let mut test_gpio2 = Output::new(gpio2.into(), Level::Low); + embassy_futures::select::select( + async { + loop { + test_gpio1.wait_for_rising_edge().await; + counter.fetch_add(1, Ordering::SeqCst); + } + }, + async { + for _ in 0..5 { + test_gpio2.set_high(); + Timer::after(Duration::from_millis(25)).await; + test_gpio2.set_low(); + Timer::after(Duration::from_millis(25)).await; + } + }, + ) + .await; + + counter.load(Ordering::SeqCst) +} + +#[cfg(test)] +#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +mod tests { + + use super::*; + + #[test] + async fn default_handler_does_not_run_because_gpio_is_defined() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + let counter = drive_pins(gpio1, gpio2).await; + + // GPIO is bound to something else, so we don't expect the async API to work. + assert_eq!(counter, 0); + } + + #[test] + async fn default_handler_runs_because_handler_is_set() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let mut io = Io::new(peripherals.IO_MUX); + io.set_interrupt_handler(interrupt_handler); + + let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + + let counter = drive_pins(gpio1, gpio2).await; + + // We expect the async API to keep working even if a user handler is set. + assert_eq!(counter, 5); + } +} diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs index b0d37c72470..8f786717c9b 100644 --- a/hil-test/tests/i2c.rs +++ b/hil-test/tests/i2c.rs @@ -6,9 +6,7 @@ #![no_main] use esp_hal::{ - gpio::Io, - i2c::{I2c, Operation}, - prelude::*, + i2c::master::{Config, Error, I2c, Operation}, Async, Blocking, }; @@ -26,6 +24,9 @@ fn _async_driver_is_compatible_with_blocking_ehal() { fn _with_ehal(_: impl embedded_hal::i2c::I2c) {} } +const DUT_ADDRESS: u8 = 0x77; +const NON_EXISTENT_ADDRESS: u8 = 0x6b; + #[cfg(test)] #[embedded_test::tests] mod tests { @@ -34,17 +35,28 @@ mod tests { #[init] fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (sda, scl) = hil_test::i2c_pins!(io); + let (sda, scl) = hil_test::i2c_pins!(peripherals); // Create a new peripheral object with the described wiring and standard // I2C clock speed: - let i2c = I2c::new(peripherals.I2C0, sda, scl, 100.kHz()); + let i2c = I2c::new(peripherals.I2C0, Config::default()) + .with_sda(sda) + .with_scl(scl); Context { i2c } } + #[test] + #[timeout(3)] + fn empty_write_returns_ack_error_for_unknown_address(mut ctx: Context) { + assert_eq!( + ctx.i2c.write(NON_EXISTENT_ADDRESS, &[]), + Err(Error::AckCheckFailed) + ); + assert_eq!(ctx.i2c.write(DUT_ADDRESS, &[]), Ok(())); + } + #[test] #[timeout(3)] fn test_read_cali(mut ctx: Context) { @@ -52,10 +64,14 @@ mod tests { // have a failing read which might could leave the peripheral in an undesirable // state - ctx.i2c.write_read(0x55, &[0xaa], &mut read_data).ok(); + ctx.i2c + .write_read(NON_EXISTENT_ADDRESS, &[0xaa], &mut read_data) + .ok(); // do the real read which should succeed - ctx.i2c.write_read(0x77, &[0xaa], &mut read_data).ok(); + ctx.i2c + .write_read(DUT_ADDRESS, &[0xaa], &mut read_data) + .ok(); assert_ne!(read_data, [0u8; 22]) } @@ -68,7 +84,7 @@ mod tests { // do the real read which should succeed ctx.i2c .transaction( - 0x77, + DUT_ADDRESS, &mut [Operation::Write(&[0xaa]), Operation::Read(&mut read_data)], ) .ok(); diff --git a/hil-test/tests/i2s.rs b/hil-test/tests/i2s.rs index 2420264edc5..5fd85ca9e5f 100644 --- a/hil-test/tests/i2s.rs +++ b/hil-test/tests/i2s.rs @@ -3,8 +3,9 @@ //! This test uses I2S TX to transmit known data to I2S RX (forced to slave mode //! with loopback mode enabled). -//% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 //% FEATURES: generic-queue +// FIXME: re-enable on ESP32 when it no longer fails spuriously #![no_std] #![no_main] @@ -13,8 +14,8 @@ use esp_hal::{ delay::Delay, dma::{Dma, DmaPriority}, dma_buffers, - gpio::{Io, NoPin}, - i2s::{DataFormat, I2s, I2sTx, Standard}, + gpio::{AnyPin, NoPin, Pin}, + i2s::master::{DataFormat, I2s, I2sTx, Standard}, peripherals::I2S0, prelude::*, Async, @@ -102,7 +103,7 @@ mod tests { use super::*; struct Context { - io: Io, + dout: AnyPin, dma_channel: DmaChannel0Creator, i2s: I2S0, } @@ -111,8 +112,6 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let dma = Dma::new(peripherals.DMA); cfg_if::cfg_if! { @@ -123,8 +122,10 @@ mod tests { } } + let (_, dout) = hil_test::common_test_pins!(peripherals); + Context { - io, + dout: dout.degrade(), dma_channel, i2s: peripherals.I2S0, } @@ -142,15 +143,13 @@ mod tests { Standard::Philips, DataFormat::Data16Channel16, 16000.Hz(), - ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), + ctx.dma_channel.configure(false, DmaPriority::Priority0), rx_descriptors, tx_descriptors, - ); + ) + .into_async(); - let (_, dout) = hil_test::common_test_pins!(ctx.io); - - let din = dout.peripheral_input(); + let (din, dout) = ctx.dout.split(); let i2s_tx = i2s .i2s_tx @@ -202,9 +201,7 @@ mod tests { tx_descriptors, ); - let (_, dout) = hil_test::common_test_pins!(ctx.io); - - let din = dout.peripheral_input(); + let (din, dout) = ctx.dout.split(); let mut i2s_tx = i2s .i2s_tx @@ -235,7 +232,7 @@ mod tests { assert_eq!(0, rx_transfer.pop(&mut rcv[..100]).unwrap()); // no data available yet - assert_eq!(0, rx_transfer.available()); + assert_eq!(0, rx_transfer.available().unwrap()); let mut tx_transfer = i2s_tx.write_dma_circular(tx_buffer).unwrap(); @@ -243,7 +240,7 @@ mod tests { let mut sample_idx = 0; let mut check_samples = SampleSource::new(); loop { - let tx_avail = tx_transfer.available(); + let tx_avail = tx_transfer.available().unwrap(); // make sure there are more than one descriptor buffers ready to push if tx_avail > 5000 { @@ -254,13 +251,13 @@ mod tests { } // test calling available multiple times doesn't break anything - rx_transfer.available(); - rx_transfer.available(); - rx_transfer.available(); - rx_transfer.available(); - rx_transfer.available(); - rx_transfer.available(); - let rx_avail = rx_transfer.available(); + rx_transfer.available().unwrap(); + rx_transfer.available().unwrap(); + rx_transfer.available().unwrap(); + rx_transfer.available().unwrap(); + rx_transfer.available().unwrap(); + rx_transfer.available().unwrap(); + let rx_avail = rx_transfer.available().unwrap(); // make sure there are more than one descriptor buffers ready to pop if rx_avail > 0 { @@ -298,4 +295,64 @@ mod tests { } } } + + #[test] + fn test_i2s_push_too_late(ctx: Context) { + let (_, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(0, 16000); + + let i2s = I2s::new( + ctx.i2s, + Standard::Philips, + DataFormat::Data16Channel16, + 16000.Hz(), + ctx.dma_channel.configure(false, DmaPriority::Priority0), + rx_descriptors, + tx_descriptors, + ); + + let mut i2s_tx = i2s + .i2s_tx + .with_bclk(NoPin) + .with_ws(NoPin) + .with_dout(ctx.dout) + .build(); + + let mut tx_transfer = i2s_tx.write_dma_circular(tx_buffer).unwrap(); + + let delay = esp_hal::delay::Delay::new(); + delay.delay_millis(300); + + assert!(matches!(tx_transfer.push(&[0; 128]), Err(_))); + } + + #[test] + #[timeout(1)] + fn test_i2s_read_too_late(ctx: Context) { + let (rx_buffer, rx_descriptors, _, tx_descriptors) = dma_buffers!(16000, 0); + + let i2s = I2s::new( + ctx.i2s, + Standard::Philips, + DataFormat::Data16Channel16, + 16000.Hz(), + ctx.dma_channel.configure(false, DmaPriority::Priority0), + rx_descriptors, + tx_descriptors, + ); + + let mut i2s_rx = i2s + .i2s_rx + .with_bclk(NoPin) + .with_ws(NoPin) + .with_din(ctx.dout) // not a typo + .build(); + + let mut buffer = [0u8; 1024]; + let mut rx_transfer = i2s_rx.read_dma_circular(rx_buffer).unwrap(); + + let delay = esp_hal::delay::Delay::new(); + delay.delay_millis(300); + + assert!(matches!(rx_transfer.pop(&mut buffer), Err(_))); + } } diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index 85d5c8493b5..987a0ad543a 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -8,7 +8,7 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaTxBuf}, dma_buffers, - gpio::{Io, NoPin}, + gpio::{GpioPin, NoPin}, lcd_cam::{ lcd::i8080::{Command, Config, TxEightBits, TxSixteenBits, I8080}, BitOrder, @@ -19,15 +19,25 @@ use esp_hal::{ Pcnt, }, prelude::*, + Blocking, }; use hil_test as _; const DATA_SIZE: usize = 1024 * 10; +#[allow(non_snake_case)] +struct Pins { + pub GPIO8: GpioPin<8>, + pub GPIO11: GpioPin<11>, + pub GPIO12: GpioPin<12>, + pub GPIO16: GpioPin<16>, + pub GPIO17: GpioPin<17>, +} + struct Context<'d> { - lcd_cam: LcdCam<'d, esp_hal::Blocking>, + lcd_cam: LcdCam<'d, Blocking>, pcnt: Pcnt<'d>, - io: Io, + pins: Pins, dma: Dma<'d>, dma_buf: DmaTxBuf, } @@ -43,7 +53,7 @@ mod tests { let dma = Dma::new(peripherals.DMA); let lcd_cam = LcdCam::new(peripherals.LCD_CAM); let pcnt = Pcnt::new(peripherals.PCNT); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); @@ -51,7 +61,13 @@ mod tests { lcd_cam, dma, pcnt, - io, + pins: Pins { + GPIO8: peripherals.GPIO8, + GPIO11: peripherals.GPIO11, + GPIO12: peripherals.GPIO12, + GPIO16: peripherals.GPIO16, + GPIO17: peripherals.GPIO17, + }, dma_buf, } } @@ -79,7 +95,8 @@ mod tests { let channel = ctx .dma .channel0 - .configure_for_async(false, DmaPriority::Priority0); + .configure(false, DmaPriority::Priority0) + .into_async(); let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); let i8080 = I8080::new( @@ -100,20 +117,14 @@ mod tests { // issue with configuring pins as outputs after inputs have been sorted // out. See https://github.com/esp-rs/esp-hal/pull/2173#issue-2529323702 - let cs_signal = ctx.io.pins.gpio8; - let unit0_signal = ctx.io.pins.gpio11; - let unit1_signal = ctx.io.pins.gpio12; - let unit2_signal = ctx.io.pins.gpio16; - let unit3_signal = ctx.io.pins.gpio17; + let (unit_ctrl, cs_signal) = ctx.pins.GPIO8.split(); + let (unit0_input, unit0_signal) = ctx.pins.GPIO11.split(); + let (unit1_input, unit1_signal) = ctx.pins.GPIO12.split(); + let (unit2_input, unit2_signal) = ctx.pins.GPIO16.split(); + let (unit3_input, unit3_signal) = ctx.pins.GPIO17.split(); let pcnt = ctx.pcnt; - let unit_ctrl = cs_signal.peripheral_input(); - let unit0_input = unit0_signal.peripheral_input(); - let unit1_input = unit1_signal.peripheral_input(); - let unit2_input = unit2_signal.peripheral_input(); - let unit3_input = unit3_signal.peripheral_input(); - pcnt.unit0 .channel0 .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); @@ -201,10 +212,10 @@ mod tests { xfer.wait().0.unwrap(); let actual = [ - pcnt.unit0.get_value(), - pcnt.unit1.get_value(), - pcnt.unit2.get_value(), - pcnt.unit3.get_value(), + pcnt.unit0.value(), + pcnt.unit1.value(), + pcnt.unit2.value(), + pcnt.unit3.value(), ]; let expected = [5, 3, 2, 1]; @@ -217,20 +228,14 @@ mod tests { // issue with configuring pins as outputs after inputs have been sorted // out. See https://github.com/esp-rs/esp-hal/pull/2173#issue-2529323702 - let cs_signal = ctx.io.pins.gpio8; - let unit0_signal = ctx.io.pins.gpio11; - let unit1_signal = ctx.io.pins.gpio12; - let unit2_signal = ctx.io.pins.gpio16; - let unit3_signal = ctx.io.pins.gpio17; + let (unit_ctrl, cs_signal) = ctx.pins.GPIO8.split(); + let (unit0_input, unit0_signal) = ctx.pins.GPIO11.split(); + let (unit1_input, unit1_signal) = ctx.pins.GPIO12.split(); + let (unit2_input, unit2_signal) = ctx.pins.GPIO16.split(); + let (unit3_input, unit3_signal) = ctx.pins.GPIO17.split(); let pcnt = ctx.pcnt; - let unit_ctrl = cs_signal.peripheral_input(); - let unit0_input = unit0_signal.peripheral_input(); - let unit1_input = unit1_signal.peripheral_input(); - let unit2_input = unit2_signal.peripheral_input(); - let unit3_input = unit3_signal.peripheral_input(); - pcnt.unit0 .channel0 .set_ctrl_mode(CtrlMode::Keep, CtrlMode::Disable); @@ -334,10 +339,10 @@ mod tests { xfer.wait().0.unwrap(); let actual = [ - pcnt.unit0.get_value(), - pcnt.unit1.get_value(), - pcnt.unit2.get_value(), - pcnt.unit3.get_value(), + pcnt.unit0.value(), + pcnt.unit1.value(), + pcnt.unit2.value(), + pcnt.unit3.value(), ]; let expected = [5, 3, 2, 1]; diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index c5c36d209d7..df44d002289 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -15,13 +15,14 @@ use esp_hal::{ LcdCam, }, prelude::*, + Async, }; use hil_test as _; const DATA_SIZE: usize = 1024 * 10; struct Context<'d> { - lcd_cam: LcdCam<'d, esp_hal::Async>, + lcd_cam: LcdCam<'d, Async>, dma: Dma<'d>, dma_buf: DmaTxBuf, } @@ -36,7 +37,7 @@ mod tests { let peripherals = esp_hal::init(esp_hal::Config::default()); let dma = Dma::new(peripherals.DMA); - let lcd_cam = LcdCam::new_async(peripherals.LCD_CAM); + let lcd_cam = LcdCam::new(peripherals.LCD_CAM).into_async(); let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, DATA_SIZE); let dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); @@ -75,7 +76,8 @@ mod tests { let channel = ctx .dma .channel0 - .configure_for_async(false, DmaPriority::Priority0); + .configure(false, DmaPriority::Priority0) + .into_async(); let pins = TxEightBits::new(NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin); let i8080 = I8080::new( diff --git a/hil-test/tests/parl_io_tx.rs b/hil-test/tests/parl_io_tx.rs index 3bec7b56c86..a43659bdc8c 100644 --- a/hil-test/tests/parl_io_tx.rs +++ b/hil-test/tests/parl_io_tx.rs @@ -8,7 +8,10 @@ use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ dma::{ChannelCreator, Dma, DmaPriority}, - gpio::{interconnect::InputSignal, AnyPin, Io, NoPin}, + gpio::{ + interconnect::{InputSignal, OutputSignal}, + NoPin, + }, parl_io::{ BitPackOrder, ClkOutPin, @@ -30,8 +33,8 @@ use hil_test as _; struct Context { parl_io: PARL_IO, dma_channel: ChannelCreator<0>, - clock: AnyPin, - valid: AnyPin, + clock: OutputSignal, + valid: OutputSignal, clock_loopback: InputSignal, valid_loopback: InputSignal, pcnt_unit: Unit<'static, 0>, @@ -48,12 +51,10 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (clock, _) = hil_test::common_test_pins!(io); - let valid = io.pins.gpio0.degrade(); - let clock_loopback = clock.peripheral_input(); - let valid_loopback = valid.peripheral_input(); - let clock = clock.degrade(); + let (clock, _) = hil_test::common_test_pins!(peripherals); + let valid = hil_test::unconnected_pin!(peripherals); + let (clock_loopback, clock) = clock.split(); + let (valid_loopback, valid) = valid.split(); let pcnt = Pcnt::new(peripherals.PCNT); let pcnt_unit = pcnt.unit0; let dma = Dma::new(peripherals.DMA); @@ -121,8 +122,8 @@ mod tests { clock_unit.clear(); let xfer = pio.write_dma(&tx_buffer).unwrap(); xfer.wait().unwrap(); - info!("clock count: {}", clock_unit.get_value()); - assert_eq!(clock_unit.get_value(), BUFFER_SIZE as _); + info!("clock count: {}", clock_unit.value()); + assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } } @@ -188,8 +189,8 @@ mod tests { clock_unit.clear(); let xfer = pio.write_dma(&tx_buffer).unwrap(); xfer.wait().unwrap(); - info!("clock count: {}", clock_unit.get_value()); - assert_eq!(clock_unit.get_value(), BUFFER_SIZE as _); + info!("clock count: {}", clock_unit.value()); + assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } } } diff --git a/hil-test/tests/parl_io_tx_async.rs b/hil-test/tests/parl_io_tx_async.rs index cdb177a4828..240a805c0e6 100644 --- a/hil-test/tests/parl_io_tx_async.rs +++ b/hil-test/tests/parl_io_tx_async.rs @@ -10,7 +10,10 @@ use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits}; use esp_hal::{ dma::{ChannelCreator, Dma, DmaPriority}, - gpio::{interconnect::InputSignal, AnyPin, Io, NoPin}, + gpio::{ + interconnect::{InputSignal, OutputSignal}, + NoPin, + }, parl_io::{ BitPackOrder, ClkOutPin, @@ -32,8 +35,8 @@ use hil_test as _; struct Context { parl_io: PARL_IO, dma_channel: ChannelCreator<0>, - clock: AnyPin, - valid: AnyPin, + clock: OutputSignal, + valid: OutputSignal, clock_loopback: InputSignal, valid_loopback: InputSignal, pcnt_unit: Unit<'static, 0>, @@ -50,12 +53,10 @@ mod tests { async fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let (clock, _) = hil_test::common_test_pins!(io); - let valid = io.pins.gpio0.degrade(); - let clock_loopback = clock.peripheral_input(); - let valid_loopback = valid.peripheral_input(); - let clock = clock.degrade(); + let (clock, _) = hil_test::common_test_pins!(peripherals); + let valid = hil_test::unconnected_pin!(peripherals); + let (clock_loopback, clock) = clock.split(); + let (valid_loopback, valid) = valid.split(); let pcnt = Pcnt::new(peripherals.PCNT); let pcnt_unit = pcnt.unit0; let dma = Dma::new(peripherals.DMA); @@ -92,7 +93,8 @@ mod tests { let pio = ParlIoTxOnly::new( ctx.parl_io, ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), + .configure(false, DmaPriority::Priority0) + .into_async(), tx_descriptors, 10.MHz(), ) @@ -123,8 +125,8 @@ mod tests { for _ in 0..100 { clock_unit.clear(); pio.write_dma_async(&tx_buffer).await.unwrap(); - info!("clock count: {}", clock_unit.get_value()); - assert_eq!(clock_unit.get_value(), BUFFER_SIZE as _); + info!("clock count: {}", clock_unit.value()); + assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } } @@ -159,7 +161,8 @@ mod tests { let pio = ParlIoTxOnly::new( ctx.parl_io, ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), + .configure(false, DmaPriority::Priority0) + .into_async(), tx_descriptors, 10.MHz(), ) @@ -190,8 +193,8 @@ mod tests { for _ in 0..100 { clock_unit.clear(); pio.write_dma_async(&tx_buffer).await.unwrap(); - info!("clock count: {}", clock_unit.get_value()); - assert_eq!(clock_unit.get_value(), BUFFER_SIZE as _); + info!("clock count: {}", clock_unit.value()); + assert_eq!(clock_unit.value(), BUFFER_SIZE as _); } } } diff --git a/hil-test/tests/pcnt.rs b/hil-test/tests/pcnt.rs index d1857be61a8..482e7e256a3 100644 --- a/hil-test/tests/pcnt.rs +++ b/hil-test/tests/pcnt.rs @@ -7,7 +7,7 @@ use esp_hal::{ delay::Delay, - gpio::{AnyPin, Input, Io, Level, Output, Pin, Pull}, + gpio::{AnyPin, Input, Level, Output, Pin, Pull}, pcnt::{channel::EdgeMode, Pcnt}, }; use hil_test as _; @@ -28,9 +28,7 @@ mod tests { fn init() -> Context<'static> { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (din, dout) = hil_test::common_test_pins!(io); + let (din, dout) = hil_test::common_test_pins!(peripherals); let din = din.degrade(); let dout = dout.degrade(); @@ -57,27 +55,27 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); output.set_low(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); output.set_low(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); } #[test] @@ -94,27 +92,27 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); output.set_low(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); output.set_low(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); } #[test] @@ -133,29 +131,29 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(0, unit.get_value()); - assert!(unit.get_events().high_limit); + assert_eq!(0, unit.value()); + assert!(unit.events().high_limit); assert!(unit.interrupt_is_set()); output.set_low(); @@ -163,16 +161,16 @@ mod tests { output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); // high limit event remains after next increment. - assert!(unit.get_events().high_limit); + assert!(unit.events().high_limit); unit.reset_interrupt(); assert!(!unit.interrupt_is_set()); // high limit event remains after interrupt is cleared. - assert!(unit.get_events().high_limit); + assert!(unit.events().high_limit); } #[test] @@ -194,27 +192,27 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(1, unit.get_value()); + assert_eq!(1, unit.value()); assert!(!unit.interrupt_is_set()); - assert!(!unit.get_events().threshold0); - assert!(!unit.get_events().threshold1); + assert!(!unit.events().threshold0); + assert!(!unit.events().threshold1); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(2, unit.get_value()); + assert_eq!(2, unit.value()); assert!(unit.interrupt_is_set()); - assert!(unit.get_events().threshold0); - assert!(!unit.get_events().threshold1); + assert!(unit.events().threshold0); + assert!(!unit.events().threshold1); unit.reset_interrupt(); @@ -223,22 +221,22 @@ mod tests { output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(3, unit.get_value()); + assert_eq!(3, unit.value()); assert!(!unit.interrupt_is_set()); // threshold event remains after next increment and after interrupt is cleared. - assert!(unit.get_events().threshold0); - assert!(!unit.get_events().threshold1); + assert!(unit.events().threshold0); + assert!(!unit.events().threshold1); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(4, unit.get_value()); + assert_eq!(4, unit.value()); assert!(unit.interrupt_is_set()); // threshold event cleared after new event occurs. - assert!(!unit.get_events().threshold0); - assert!(unit.get_events().threshold1); + assert!(!unit.events().threshold0); + assert!(unit.events().threshold1); } #[test] @@ -259,29 +257,29 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(-1, unit.get_value()); + assert_eq!(-1, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(-2, unit.get_value()); + assert_eq!(-2, unit.value()); output.set_low(); ctx.delay.delay_micros(1); output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(0, unit.get_value()); - assert!(unit.get_events().low_limit); + assert_eq!(0, unit.value()); + assert!(unit.events().low_limit); assert!(unit.interrupt_is_set()); output.set_low(); @@ -289,16 +287,16 @@ mod tests { output.set_high(); ctx.delay.delay_micros(1); - assert_eq!(-1, unit.get_value()); + assert_eq!(-1, unit.value()); // low limit event remains after next increment. - assert!(unit.get_events().low_limit); + assert!(unit.events().low_limit); unit.reset_interrupt(); assert!(!unit.interrupt_is_set()); // low limit event remains after interrupt is cleared. - assert!(unit.get_events().low_limit); + assert!(unit.events().low_limit); } #[test] @@ -315,10 +313,10 @@ mod tests { unit.resume(); - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); for i in (0..=i16::MAX).chain(i16::MIN..0) { - assert_eq!(i, unit.get_value()); + assert_eq!(i, unit.value()); output.set_low(); ctx.delay.delay_micros(1); @@ -326,14 +324,14 @@ mod tests { ctx.delay.delay_micros(1); } - assert_eq!(0, unit.get_value()); + assert_eq!(0, unit.value()); // Channel 1 should now decrement. unit.channel1 .set_input_mode(EdgeMode::Decrement, EdgeMode::Hold); for i in (0..=i16::MAX).chain(i16::MIN..=0).rev() { - assert_eq!(i, unit.get_value()); + assert_eq!(i, unit.value()); output.set_low(); ctx.delay.delay_micros(1); diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index 641098f1a62..c2f69b9ebdf 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -10,10 +10,10 @@ use esp_hal::pcnt::{channel::EdgeMode, unit::Unit, Pcnt}; use esp_hal::{ dma::{Channel, Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::{AnyPin, Input, Io, Level, Output, Pull}, + gpio::{AnyPin, Input, Level, Output, Pull}, prelude::*, spi::{ - master::{Address, Command, Spi, SpiDma}, + master::{Address, Command, Config, Spi, SpiDma}, SpiDataMode, SpiMode, }, @@ -40,7 +40,7 @@ cfg_if::cfg_if! { type SpiUnderTest = SpiDma<'static, Blocking>; struct Context { - spi: esp_hal::peripherals::SPI2, + spi: Spi<'static, Blocking>, #[cfg(pcnt)] pcnt: esp_hal::peripherals::PCNT, dma_channel: Channel<'static, DmaChannel0, Blocking>, @@ -143,15 +143,15 @@ fn execute_write( unit0.clear(); unit1.clear(); (spi, dma_tx_buf) = transfer_write(spi, dma_tx_buf, write, command_data_mode); - assert_eq!(unit0.get_value() + unit1.get_value(), 8); + assert_eq!(unit0.value() + unit1.value(), 8); if data_on_multiple_pins { if command_data_mode == SpiDataMode::Single { - assert_eq!(unit0.get_value(), 1); - assert_eq!(unit1.get_value(), 7); + assert_eq!(unit0.value(), 1); + assert_eq!(unit1.value(), 7); } else { - assert_eq!(unit0.get_value(), 0); - assert_eq!(unit1.get_value(), 8); + assert_eq!(unit0.value(), 0); + assert_eq!(unit1.value(), 8); } } @@ -161,15 +161,15 @@ fn execute_write( unit0.clear(); unit1.clear(); (spi, dma_tx_buf) = transfer_write(spi, dma_tx_buf, write, command_data_mode); - assert_eq!(unit0.get_value() + unit1.get_value(), 4); + assert_eq!(unit0.value() + unit1.value(), 4); if data_on_multiple_pins { if command_data_mode == SpiDataMode::Single { - assert_eq!(unit0.get_value(), 1); - assert_eq!(unit1.get_value(), 3); + assert_eq!(unit0.value(), 1); + assert_eq!(unit1.value(), 3); } else { - assert_eq!(unit0.get_value(), 0); - assert_eq!(unit1.get_value(), 4); + assert_eq!(unit0.value(), 0); + assert_eq!(unit1.value(), 4); } } } @@ -184,10 +184,8 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (mut pin, mut pin_mirror) = hil_test::common_test_pins!(io); - let mut unconnected_pin = hil_test::unconnected_pin!(io); + let (mut pin, mut pin_mirror) = hil_test::common_test_pins!(peripherals); + let mut unconnected_pin = hil_test::unconnected_pin!(peripherals); // Make sure pins have no pullups let _ = Input::new(&mut pin, Pull::Down); @@ -205,9 +203,17 @@ mod tests { } let dma_channel = dma_channel.configure(false, DmaPriority::Priority0); + let spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ); Context { - spi: peripherals.SPI2, + spi, #[cfg(pcnt)] pcnt: peripherals.PCNT, dma_channel, @@ -225,9 +231,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_mosi(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_mosi(pin).with_dma(ctx.dma_channel); super::execute_read(spi, pin_mirror, 0b0001_0001); } @@ -238,9 +242,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_miso(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_miso(pin).with_dma(ctx.dma_channel); super::execute_read(spi, pin_mirror, 0b0010_0010); } @@ -251,9 +253,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_sio2(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_sio2(pin).with_dma(ctx.dma_channel); super::execute_read(spi, pin_mirror, 0b0100_0100); } @@ -264,9 +264,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_sio3(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_sio3(pin).with_dma(ctx.dma_channel); super::execute_read(spi, pin_mirror, 0b1000_1000); } @@ -277,9 +275,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_mosi(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_mosi(pin).with_dma(ctx.dma_channel); super::execute_write_read(spi, pin_mirror, 0b0001_0001); } @@ -290,9 +286,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_miso(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_miso(pin).with_dma(ctx.dma_channel); super::execute_write_read(spi, pin_mirror, 0b0010_0010); } @@ -303,9 +297,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_sio2(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_sio2(pin).with_dma(ctx.dma_channel); super::execute_write_read(spi, pin_mirror, 0b0100_0100); } @@ -316,9 +308,7 @@ mod tests { let [pin, pin_mirror, _] = ctx.gpios; let pin_mirror = Output::new(pin_mirror, Level::High); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_sio3(pin) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_sio3(pin).with_dma(ctx.dma_channel); super::execute_write_read(spi, pin_mirror, 0b1000_1000); } @@ -335,14 +325,14 @@ mod tests { let unit0 = pcnt.unit0; let unit1 = pcnt.unit1; - unit0.channel0.set_edge_signal(mosi.peripheral_input()); + let (mosi_loopback, mosi) = mosi.split(); + + unit0.channel0.set_edge_signal(mosi_loopback); unit0 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) - .with_mosi(mosi) - .with_dma(ctx.dma_channel); + let spi = ctx.spi.with_mosi(mosi).with_dma(ctx.dma_channel); super::execute_write(unit0, unit1, spi, 0b0000_0001, false); } @@ -359,17 +349,21 @@ mod tests { let unit0 = pcnt.unit0; let unit1 = pcnt.unit1; - unit0.channel0.set_edge_signal(mosi.peripheral_input()); + let (mosi_loopback, mosi) = mosi.split(); + let (gpio_loopback, gpio) = gpio.split(); + + unit0.channel0.set_edge_signal(mosi_loopback); unit0 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - unit1.channel0.set_edge_signal(gpio.peripheral_input()); + unit1.channel0.set_edge_signal(gpio_loopback); unit1 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) + let spi = ctx + .spi .with_mosi(mosi) .with_miso(gpio) .with_dma(ctx.dma_channel); @@ -389,17 +383,21 @@ mod tests { let unit0 = pcnt.unit0; let unit1 = pcnt.unit1; - unit0.channel0.set_edge_signal(mosi.peripheral_input()); + let (mosi_loopback, mosi) = mosi.split(); + let (gpio_loopback, gpio) = gpio.split(); + + unit0.channel0.set_edge_signal(mosi_loopback); unit0 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - unit1.channel0.set_edge_signal(gpio.peripheral_input()); + unit1.channel0.set_edge_signal(gpio_loopback); unit1 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) + let spi = ctx + .spi .with_mosi(mosi) .with_sio2(gpio) .with_dma(ctx.dma_channel); @@ -419,17 +417,21 @@ mod tests { let unit0 = pcnt.unit0; let unit1 = pcnt.unit1; - unit0.channel0.set_edge_signal(mosi.peripheral_input()); + let (mosi_loopback, mosi) = mosi.split(); + let (gpio_loopback, gpio) = gpio.split(); + + unit0.channel0.set_edge_signal(mosi_loopback); unit0 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - unit1.channel0.set_edge_signal(gpio.peripheral_input()); + unit1.channel0.set_edge_signal(gpio_loopback); unit1 .channel0 .set_input_mode(EdgeMode::Hold, EdgeMode::Increment); - let spi = Spi::new(ctx.spi, 100.kHz(), SpiMode::Mode0) + let spi = ctx + .spi .with_mosi(mosi) .with_sio3(gpio) .with_dma(ctx.dma_channel); diff --git a/hil-test/tests/rmt.rs b/hil-test/tests/rmt.rs index 5ad82d4af71..8d7622f2661 100644 --- a/hil-test/tests/rmt.rs +++ b/hil-test/tests/rmt.rs @@ -6,7 +6,6 @@ #![no_main] use esp_hal::{ - gpio::Io, prelude::*, rmt::{PulseCode, Rmt, RxChannel, RxChannelConfig, TxChannel, TxChannelConfig}, }; @@ -15,6 +14,8 @@ use hil_test as _; #[cfg(test)] #[embedded_test::tests] mod tests { + use esp_hal::rmt::Error; + use super::*; #[init] @@ -25,8 +26,6 @@ mod tests { fn rmt_loopback() { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - cfg_if::cfg_if! { if #[cfg(feature = "esp32h2")] { let freq = 32.MHz(); @@ -37,7 +36,7 @@ mod tests { let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); - let (rx, tx) = hil_test::common_test_pins!(io); + let (rx, tx) = hil_test::common_test_pins!(peripherals); let tx_config = TxChannelConfig { clk_divider: 255, @@ -79,30 +78,15 @@ mod tests { } } - let mut tx_data = [PulseCode { - level1: true, - length1: 200, - level2: false, - length2: 50, - }; 20]; - - tx_data[tx_data.len() - 2] = PulseCode { - level1: true, - length1: 3000, - level2: false, - length2: 500, - }; - tx_data[tx_data.len() - 1] = PulseCode::default(); + let mut tx_data = [PulseCode::new(true, 200, false, 50); 20]; - let mut rcv_data = [PulseCode { - level1: false, - length1: 0, - level2: false, - length2: 0, - }; 20]; + tx_data[tx_data.len() - 2] = PulseCode::new(true, 3000, false, 500); + tx_data[tx_data.len() - 1] = PulseCode::empty(); + + let mut rcv_data: [u32; 20] = [PulseCode::empty(); 20]; let rx_transaction = rx_channel.receive(&mut rcv_data).unwrap(); - let tx_transaction = tx_channel.transmit(&tx_data); + let tx_transaction = tx_channel.transmit(&tx_data).unwrap(); rx_transaction.wait().unwrap(); tx_transaction.wait().unwrap(); @@ -111,4 +95,39 @@ mod tests { // they can't be equal assert_eq!(&tx_data[..18], &rcv_data[..18]); } + + #[test] + #[timeout(1)] + fn rmt_single_shot_fails_without_end_marker() { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32h2")] { + let freq = 32.MHz(); + } else { + let freq = 80.MHz(); + } + }; + + let rmt = Rmt::new(peripherals.RMT, freq).unwrap(); + + let (_, tx) = hil_test::common_test_pins!(peripherals); + + let tx_config = TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }; + + let tx_channel = { + use esp_hal::rmt::TxChannelCreator; + rmt.channel0.configure(tx, tx_config).unwrap() + }; + + let tx_data = [PulseCode::new(true, 200, false, 50); 20]; + + let tx_transaction = tx_channel.transmit(&tx_data); + + assert!(tx_transaction.is_err()); + assert!(matches!(tx_transaction, Err(Error::EndMarkerMissing))); + } } diff --git a/hil-test/tests/rsa_async.rs b/hil-test/tests/rsa_async.rs index 105f424e207..671c853a8f4 100644 --- a/hil-test/tests/rsa_async.rs +++ b/hil-test/tests/rsa_async.rs @@ -56,7 +56,7 @@ mod tests { #[init] fn init() -> Context<'static> { let peripherals = esp_hal::init(esp_hal::Config::default()); - let mut rsa = Rsa::new_async(peripherals.RSA); + let mut rsa = Rsa::new(peripherals.RSA).into_async(); nb::block!(rsa.ready()).unwrap(); Context { rsa } diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 1af4146fff1..1b893dcb132 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -14,10 +14,11 @@ use embedded_hal_async::spi::SpiBus as SpiBusAsync; use esp_hal::{ dma::{Dma, DmaDescriptor, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::{Io, Level, NoPin}, + gpio::{Level, NoPin}, peripheral::Peripheral, prelude::*, - spi::{master::Spi, SpiMode}, + spi::master::{Config, Spi}, + Blocking, }; #[cfg(pcnt)] use esp_hal::{ @@ -35,7 +36,7 @@ cfg_if::cfg_if! { } struct Context { - spi: Spi<'static>, + spi: Spi<'static, Blocking>, dma_channel: DmaChannelCreator, // Reuse the really large buffer so we don't run out of DRAM with many tests rx_buffer: &'static mut [u8], @@ -57,9 +58,8 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let (_, mosi) = hil_test::common_test_pins!(io); + let sclk = peripherals.GPIO0; + let (_, mosi) = hil_test::common_test_pins!(peripherals); let dma = Dma::new(peripherals.DMA); @@ -72,13 +72,19 @@ mod tests { } #[cfg(pcnt)] - let mosi_loopback_pcnt = mosi.peripheral_input(); + let (mosi_loopback_pcnt, mosi) = mosi.split(); // Need to set miso first so that mosi can overwrite the // output connection (because we are using the same pin to loop back) - let spi = Spi::new(peripherals.SPI2, 10000.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_miso(unsafe { mosi.clone_unchecked() }) - .with_mosi(mosi); + let spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 10.MHz(), + ..Config::default() + }, + ) + .with_sck(sclk) + .with_miso(unsafe { mosi.clone_unchecked() }) + .with_mosi(mosi); let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(32000); @@ -137,7 +143,7 @@ mod tests { // Flush because we're not reading, so the write may happen in the background ctx.spi.flush().expect("Flush failed"); - assert_eq!(unit.get_value(), 9); + assert_eq!(unit.value(), 9); } #[test] @@ -156,7 +162,7 @@ mod tests { // Flush because we're not reading, so the write may happen in the background ctx.spi.flush().expect("Flush failed"); - assert_eq!(unit.get_value(), 9); + assert_eq!(unit.value(), 9); } #[test] @@ -217,7 +223,7 @@ mod tests { let transfer = spi.write(dma_tx_buf).map_err(|e| e.0).unwrap(); (spi, dma_tx_buf) = transfer.wait(); - assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (i * 3 * DMA_BUFFER_SIZE) as _); } } @@ -253,7 +259,7 @@ mod tests { .map_err(|e| e.0) .unwrap(); (spi, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); - assert_eq!(unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (i * 3 * DMA_BUFFER_SIZE) as _); } } @@ -392,11 +398,9 @@ mod tests { let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); let mut spi = ctx .spi - .with_dma( - ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), - ) - .with_buffers(dma_rx_buf, dma_tx_buf); + .with_dma(ctx.dma_channel.configure(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf) + .into_async(); ctx.pcnt_unit.channel0.set_edge_signal(ctx.pcnt_source); ctx.pcnt_unit @@ -414,7 +418,7 @@ mod tests { assert_eq!(receive, [0, 0, 0, 0, 0]); SpiBusAsync::write(&mut spi, &transmit).await.unwrap(); - assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + assert_eq!(ctx.pcnt_unit.value(), (i * 3 * DMA_BUFFER_SIZE) as _); } } @@ -428,11 +432,9 @@ mod tests { let dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap(); let mut spi = ctx .spi - .with_dma( - ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), - ) - .with_buffers(dma_rx_buf, dma_tx_buf); + .with_dma(ctx.dma_channel.configure(false, DmaPriority::Priority0)) + .with_buffers(dma_rx_buf, dma_tx_buf) + .into_async(); ctx.pcnt_unit.channel0.set_edge_signal(ctx.pcnt_source); ctx.pcnt_unit @@ -452,7 +454,7 @@ mod tests { SpiBusAsync::transfer(&mut spi, &mut receive, &transmit) .await .unwrap(); - assert_eq!(ctx.pcnt_unit.get_value(), (i * 3 * DMA_BUFFER_SIZE) as _); + assert_eq!(ctx.pcnt_unit.value(), (i * 3 * DMA_BUFFER_SIZE) as _); } } @@ -494,7 +496,12 @@ mod tests { // Slow down. At 80kHz, the transfer is supposed to take a bit over 3 seconds. // This means that without working cancellation, the test case should // fail. - ctx.spi.change_bus_frequency(80.kHz()); + ctx.spi + .apply_config(&Config { + frequency: 80.kHz(), + ..Config::default() + }) + .unwrap(); // Set up a large buffer that would trigger a timeout let dma_rx_buf = DmaRxBuf::new(ctx.rx_descriptors, ctx.rx_buffer).unwrap(); @@ -517,7 +524,12 @@ mod tests { #[timeout(3)] fn can_transmit_after_cancel(mut ctx: Context) { // Slow down. At 80kHz, the transfer is supposed to take a bit over 3 seconds. - ctx.spi.change_bus_frequency(80.kHz()); + ctx.spi + .apply_config(&Config { + frequency: 80.kHz(), + ..Config::default() + }) + .unwrap(); // Set up a large buffer that would trigger a timeout let mut dma_rx_buf = DmaRxBuf::new(ctx.rx_descriptors, ctx.rx_buffer).unwrap(); @@ -535,7 +547,11 @@ mod tests { transfer.cancel(); (spi, (dma_rx_buf, dma_tx_buf)) = transfer.wait(); - spi.change_bus_frequency(10000.kHz()); + spi.apply_config(&Config { + frequency: 10.MHz(), + ..Config::default() + }) + .unwrap(); let transfer = spi .transfer(dma_rx_buf, dma_tx_buf) @@ -557,10 +573,10 @@ mod tests { let dma_rx_buf = DmaRxBuf::new(ctx.rx_descriptors, ctx.rx_buffer).unwrap(); let dma_tx_buf = DmaTxBuf::new(ctx.tx_descriptors, ctx.tx_buffer).unwrap(); - let spi = ctx.spi.with_dma( - ctx.dma_channel - .configure_for_async(false, DmaPriority::Priority0), - ); + let spi = ctx + .spi + .with_dma(ctx.dma_channel.configure(false, DmaPriority::Priority0)) + .into_async(); let mut transfer = spi .transfer(dma_rx_buf, dma_tx_buf) diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index a50fa0dc807..212e764424a 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -8,10 +8,10 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::{Io, Level, Output}, + gpio::{Level, Output}, prelude::*, spi::{ - master::{Address, Command, Spi, SpiDma}, + master::{Address, Command, Config, Spi, SpiDma}, SpiDataMode, SpiMode, }, @@ -33,9 +33,8 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let (miso, miso_mirror) = hil_test::common_test_pins!(io); + let sclk = peripherals.GPIO0; + let (miso, miso_mirror) = hil_test::common_test_pins!(peripherals); let miso_mirror = Output::new(miso_mirror, Level::High); @@ -49,10 +48,17 @@ mod tests { } } - let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_miso(miso) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_miso(miso) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); Context { spi, miso_mirror } } diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index a44dda26070..1db9ad2cb13 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -8,11 +8,11 @@ use esp_hal::{ dma::{Dma, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, - gpio::{interconnect::InputSignal, Io}, + gpio::interconnect::InputSignal, pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, prelude::*, spi::{ - master::{Address, Command, Spi, SpiDma}, + master::{Address, Command, Config, Spi, SpiDma}, SpiDataMode, SpiMode, }, @@ -35,9 +35,8 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let (mosi, _) = hil_test::common_test_pins!(io); + let sclk = peripherals.GPIO0; + let (mosi, _) = hil_test::common_test_pins!(peripherals); let pcnt = Pcnt::new(peripherals.PCNT); let dma = Dma::new(peripherals.DMA); @@ -50,12 +49,19 @@ mod tests { } } - let mosi_loopback = mosi.peripheral_input(); + let (mosi_loopback, mosi) = mosi.split(); - let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); Context { spi, @@ -94,7 +100,7 @@ mod tests { .unwrap(); (spi, dma_tx_buf) = transfer.wait(); - assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _); let transfer = spi .half_duplex_write( @@ -108,7 +114,7 @@ mod tests { .unwrap(); transfer.wait(); - assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _); } #[test] @@ -138,7 +144,7 @@ mod tests { ) .unwrap(); - assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _); spi.half_duplex_write( SpiDataMode::Single, @@ -149,6 +155,6 @@ mod tests { ) .unwrap(); - assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _); } } diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index eea6b6254a6..91f29804adb 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -10,11 +10,11 @@ use esp_hal::{ dma::{Dma, DmaBufBlkSize, DmaPriority, DmaRxBuf, DmaTxBuf}, dma_buffers, dma_descriptors_chunk_size, - gpio::{interconnect::InputSignal, Io}, + gpio::interconnect::InputSignal, pcnt::{channel::EdgeMode, unit::Unit, Pcnt}, prelude::*, spi::{ - master::{Address, Command, Spi, SpiDma}, + master::{Address, Command, Config, Spi, SpiDma}, SpiDataMode, SpiMode, }, @@ -53,21 +53,27 @@ mod tests { let peripherals = esp_hal::init(esp_hal::Config::default()); esp_alloc::psram_allocator!(peripherals.PSRAM, esp_hal::psram); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let sclk = io.pins.gpio0; - let (mosi, _) = hil_test::common_test_pins!(io); + let sclk = peripherals.GPIO0; + let (mosi, _) = hil_test::common_test_pins!(peripherals); let pcnt = Pcnt::new(peripherals.PCNT); let dma = Dma::new(peripherals.DMA); let dma_channel = dma.channel0; - let mosi_loopback = mosi.peripheral_input(); + let (mosi_loopback, mosi) = mosi.split(); - let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0) - .with_sck(sclk) - .with_mosi(mosi) - .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); + let spi = Spi::new_with_config( + peripherals.SPI2, + Config { + frequency: 100.kHz(), + mode: SpiMode::Mode0, + ..Config::default() + }, + ) + .with_sck(sclk) + .with_mosi(mosi) + .with_dma(dma_channel.configure(false, DmaPriority::Priority0)); Context { spi, @@ -109,7 +115,7 @@ mod tests { .unwrap(); (spi, dma_tx_buf) = transfer.wait(); - assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _); let transfer = spi .half_duplex_write( @@ -123,7 +129,7 @@ mod tests { .unwrap(); transfer.wait(); - assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _); } #[test] @@ -159,7 +165,7 @@ mod tests { ) .unwrap(); - assert_eq!(unit.get_value(), (3 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (3 * DMA_BUFFER_SIZE) as _); spi.half_duplex_write( SpiDataMode::Single, @@ -170,6 +176,6 @@ mod tests { ) .unwrap(); - assert_eq!(unit.get_value(), (6 * DMA_BUFFER_SIZE) as _); + assert_eq!(unit.value(), (6 * DMA_BUFFER_SIZE) as _); } } diff --git a/hil-test/tests/spi_slave.rs b/hil-test/tests/spi_slave.rs index f39791b6833..0ccda85e86f 100644 --- a/hil-test/tests/spi_slave.rs +++ b/hil-test/tests/spi_slave.rs @@ -11,8 +11,10 @@ use esp_hal::{ dma::{Dma, DmaPriority}, dma_buffers, - gpio::{interconnect::InputSignal, Io, Level, Output}, + gpio::{Input, Level, Output, Pull}, + peripheral::Peripheral, spi::{slave::Spi, SpiMode}, + Blocking, }; use hil_test as _; @@ -25,7 +27,7 @@ cfg_if::cfg_if! { } struct Context { - spi: Spi<'static>, + spi: Spi<'static, Blocking>, dma_channel: DmaChannelCreator, bitbang_spi: BitbangSpi, } @@ -33,7 +35,7 @@ struct Context { struct BitbangSpi { sclk: Output<'static>, mosi: Output<'static>, - miso: InputSignal, + miso: Input<'static>, cs: Output<'static>, } @@ -41,7 +43,7 @@ impl BitbangSpi { fn new( sclk: Output<'static>, mosi: Output<'static>, - miso: InputSignal, + miso: Input<'static>, cs: Output<'static>, ) -> Self { Self { @@ -67,7 +69,7 @@ impl BitbangSpi { self.mosi.set_level(Level::from(bit)); self.sclk.set_level(Level::High); - let miso = self.miso.get_level().into(); + let miso = self.miso.level().into(); self.sclk.set_level(Level::Low); miso @@ -101,11 +103,9 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (mosi_pin, miso_pin) = hil_test::i2c_pins!(io); - let (sclk_pin, sclk_gpio) = hil_test::common_test_pins!(io); - let cs_pin = hil_test::unconnected_pin!(io); + let (mosi_pin, miso_pin) = hil_test::i2c_pins!(peripherals); + let (sclk_pin, _) = hil_test::common_test_pins!(peripherals); + let cs_pin = hil_test::unconnected_pin!(peripherals); let dma = Dma::new(peripherals.DMA); @@ -117,30 +117,24 @@ mod tests { } } - let cs = cs_pin.peripheral_input(); - let mosi = mosi_pin.peripheral_input(); - let mut miso = miso_pin.peripheral_input(); - let sclk_signal = sclk_pin.peripheral_input(); - let mosi_gpio = Output::new(mosi_pin, Level::Low); let cs_gpio = Output::new(cs_pin, Level::High); - let sclk_gpio = Output::new(sclk_gpio, Level::Low); - - let spi = Spi::new( - peripherals.SPI2, - sclk_signal, - mosi, - miso_pin, - cs, - SpiMode::Mode1, - ); + let sclk_gpio = Output::new(sclk_pin, Level::Low); + let miso_gpio = Input::new(miso_pin, Pull::None); - miso.enable_input(true, unsafe { esp_hal::Internal::conjure() }); + let cs = cs_gpio.peripheral_input(); + let sclk = sclk_gpio.peripheral_input(); + let mosi = mosi_gpio.peripheral_input(); + let miso = unsafe { miso_gpio.clone_unchecked() }.into_peripheral_output(); Context { - spi, + spi: Spi::new(peripherals.SPI2, SpiMode::Mode1) + .with_sck(sclk) + .with_mosi(mosi) + .with_miso(miso) + .with_cs(cs), + bitbang_spi: BitbangSpi::new(sclk_gpio, mosi_gpio, miso_gpio, cs_gpio), dma_channel, - bitbang_spi: BitbangSpi::new(sclk_gpio, mosi_gpio, miso, cs_gpio), } } diff --git a/hil-test/tests/twai.rs b/hil-test/tests/twai.rs index da41f1dc258..14a871d7b36 100644 --- a/hil-test/tests/twai.rs +++ b/hil-test/tests/twai.rs @@ -7,7 +7,6 @@ use embedded_hal_02::can::Frame; use esp_hal::{ - gpio::Io, prelude::*, twai::{self, filter::SingleStandardFilter, EspTwaiFrame, StandardId, TwaiMode}, Blocking, @@ -28,14 +27,14 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (loopback_pin, _) = hil_test::common_test_pins!(peripherals); - let (loopback_pin, _) = hil_test::common_test_pins!(io); + let (rx, tx) = loopback_pin.split(); let mut config = twai::TwaiConfiguration::new( peripherals.TWAI0, - loopback_pin.peripheral_input(), - loopback_pin, + rx, + tx, twai::BaudRate::B1000K, TwaiMode::SelfTest, ); diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index f653873cfd1..d9cddaa4180 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -7,9 +7,8 @@ use embedded_hal_02::serial::{Read, Write}; use esp_hal::{ - gpio::Io, prelude::*, - uart::{ClockSource, Uart}, + uart::{self, ClockSource, Uart}, Blocking, }; use hil_test as _; @@ -28,16 +27,11 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (_, pin) = hil_test::common_test_pins!(peripherals); - let (_, pin) = hil_test::common_test_pins!(io); + let (rx, tx) = pin.split(); - let uart = Uart::new( - peripherals.UART1, - pin.peripheral_input(), - pin.into_peripheral_output(), - ) - .unwrap(); + let uart = Uart::new(peripherals.UART1, rx, tx).unwrap(); Context { uart } } @@ -94,8 +88,14 @@ mod tests { ]; let mut byte_to_write = 0xA5; - for (baud, clock_source) in &configs { - ctx.uart.change_baud(*baud, *clock_source); + for (baudrate, clock_source) in configs { + ctx.uart + .apply_config(&uart::Config { + baudrate, + clock_source, + ..Default::default() + }) + .unwrap(); ctx.uart.write(byte_to_write).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(byte_to_write)); diff --git a/hil-test/tests/uart_async.rs b/hil-test/tests/uart_async.rs index 5ebc7f00675..7a36307c80d 100644 --- a/hil-test/tests/uart_async.rs +++ b/hil-test/tests/uart_async.rs @@ -6,7 +6,7 @@ #![no_std] #![no_main] -use esp_hal::{gpio::Io, uart::Uart, Async}; +use esp_hal::{uart::Uart, Async}; use hil_test as _; struct Context { @@ -22,11 +22,9 @@ mod tests { async fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (rx, tx) = hil_test::common_test_pins!(peripherals); - let (rx, tx) = hil_test::common_test_pins!(io); - - let uart = Uart::new_async(peripherals.UART0, rx, tx).unwrap(); + let uart = Uart::new(peripherals.UART0, rx, tx).unwrap().into_async(); Context { uart } } diff --git a/hil-test/tests/uart_regression.rs b/hil-test/tests/uart_regression.rs index 1892ddaeadf..f2118afaa26 100644 --- a/hil-test/tests/uart_regression.rs +++ b/hil-test/tests/uart_regression.rs @@ -9,7 +9,6 @@ #[embedded_test::tests] mod tests { use esp_hal::{ - gpio::Io, prelude::*, uart::{UartRx, UartTx}, }; @@ -21,9 +20,7 @@ mod tests { fn test_that_creating_tx_does_not_cause_a_pulse() { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (rx, mut tx) = hil_test::common_test_pins!(io); + let (rx, mut tx) = hil_test::common_test_pins!(peripherals); let mut rx = UartRx::new(peripherals.UART1, rx).unwrap(); diff --git a/hil-test/tests/uart_tx_rx.rs b/hil-test/tests/uart_tx_rx.rs index 7e99e5e9f70..8274a9043db 100644 --- a/hil-test/tests/uart_tx_rx.rs +++ b/hil-test/tests/uart_tx_rx.rs @@ -6,7 +6,6 @@ #![no_main] use esp_hal::{ - gpio::Io, prelude::*, uart::{UartRx, UartTx}, Blocking, @@ -28,9 +27,7 @@ mod tests { fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - - let (rx, tx) = hil_test::common_test_pins!(io); + let (rx, tx) = hil_test::common_test_pins!(peripherals); let tx = UartTx::new(peripherals.UART0, tx).unwrap(); let rx = UartRx::new(peripherals.UART1, rx).unwrap(); diff --git a/hil-test/tests/uart_tx_rx_async.rs b/hil-test/tests/uart_tx_rx_async.rs index d02f0a38768..4f02cf63bda 100644 --- a/hil-test/tests/uart_tx_rx_async.rs +++ b/hil-test/tests/uart_tx_rx_async.rs @@ -7,7 +7,6 @@ #![no_main] use esp_hal::{ - gpio::Io, uart::{UartRx, UartTx}, Async, }; @@ -27,12 +26,10 @@ mod tests { async fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let (rx, tx) = hil_test::common_test_pins!(peripherals); - let (rx, tx) = hil_test::common_test_pins!(io); - - let tx = UartTx::new_async(peripherals.UART0, tx).unwrap(); - let rx = UartRx::new_async(peripherals.UART1, rx).unwrap(); + let tx = UartTx::new(peripherals.UART0, tx).unwrap().into_async(); + let rx = UartRx::new(peripherals.UART1, rx).unwrap().into_async(); Context { rx, tx } } diff --git a/hil-test/tests/usb_serial_jtag.rs b/hil-test/tests/usb_serial_jtag.rs index c5e5bf44556..4f7cdb32813 100644 --- a/hil-test/tests/usb_serial_jtag.rs +++ b/hil-test/tests/usb_serial_jtag.rs @@ -18,6 +18,8 @@ mod tests { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); - _ = UsbSerialJtag::new_async(peripherals.USB_DEVICE).split(); + _ = UsbSerialJtag::new(peripherals.USB_DEVICE) + .into_async() + .split(); } } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 043cdda2e77..a7d72b76cfd 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -159,11 +159,11 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec { let mut features = vec![]; if config.contains("wifi") { features.push("wifi".to_owned()); - features.push("wifi-default".to_owned()); features.push("esp-now".to_owned()); features.push("sniffer".to_owned()); features.push("utils".to_owned()); - features.push("embassy-net".to_owned()); + features.push("smoltcp/proto-ipv4".to_owned()); + features.push("smoltcp/proto-ipv6".to_owned()); } if config.contains("ble") { features.push("ble".to_owned()); @@ -171,7 +171,6 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec { if config.contains("wifi") && config.contains("ble") { features.push("coex".to_owned()); } - features.push("async".to_owned()); features } _ => vec![], @@ -433,6 +432,33 @@ pub fn bump_version(workspace: &Path, package: Package, amount: Version) -> Resu manifest["package"]["version"] = toml_edit::value(version.to_string()); fs::write(manifest_path, manifest.to_string())?; + for pkg in + Package::iter().filter(|p| ![package, Package::Examples, Package::HilTest].contains(p)) + { + let manifest_path = workspace.join(pkg.to_string()).join("Cargo.toml"); + let manifest = fs::read_to_string(&manifest_path) + .with_context(|| format!("Could not read {}", manifest_path.display()))?; + + let mut manifest = manifest.parse::()?; + + if manifest["dependencies"] + .as_table() + .unwrap() + .contains_key(&package.to_string()) + { + log::info!( + " Bumping {package} version for package {pkg}: ({prev_version} -> {version})" + ); + + manifest["dependencies"].as_table_mut().map(|table| { + table[&package.to_string()]["version"] = toml_edit::value(version.to_string()) + }); + + fs::write(&manifest_path, manifest.to_string()) + .with_context(|| format!("Could not write {}", manifest_path.display()))?; + } + } + Ok(()) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4a43062fab2..a1c1e18e61e 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -697,11 +697,10 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { } Package::EspWifi => { - let mut features = - format!("--features={chip},async,ps-min-modem,defmt,dump-packets,sys-logs"); + let mut features = format!("--features={chip},defmt,sys-logs"); if device.contains("wifi") { - features.push_str(",wifi-default,esp-now,embassy-net,sniffer") + features.push_str(",esp-now,sniffer") } if device.contains("bt") { features.push_str(",ble") diff --git a/xtensa-lx/CHANGELOG.md b/xtensa-lx/CHANGELOG.md new file mode 100644 index 00000000000..4161f06a347 --- /dev/null +++ b/xtensa-lx/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +### Fixed + +### Changed + +### Removed + +## [0.9.0] - 2024-02-21 + +## [0.8.0] - 2023-02-23 + +## [0.7.0] - 2022-04-20 + +## [0.6.0] - 2022-01-16 + +## [0.5.0] - 2022-01-15 + +## [0.4.0] - 2021-08-11 + +## [0.3.0] - 2020-09-19 + +[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/xtensa-lx?since=2024-02-21