aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authori509VCB <[email protected]>2025-07-22 16:16:00 +0000
committerGitHub <[email protected]>2025-07-22 16:16:00 +0000
commit908e016524e1802b736664d84ca2da6ea908444d (patch)
tree2a4d3d28329c94a97d59b7208ccb60d28631f83e
parenta5984a8298491ea748693783275d95286a481394 (diff)
parent1ad5d5a771d5109a763361454fb724b85ae25fdd (diff)
Merge pull request #4419 from i509VCB/rt1011
nxp: Add MIMXRT1011 GPIO and time driver
-rw-r--r--.vscode/settings.json1
-rwxr-xr-xci.sh2
-rw-r--r--embassy-nxp/Cargo.toml36
-rw-r--r--embassy-nxp/build.rs136
-rw-r--r--embassy-nxp/build_common.rs94
-rw-r--r--embassy-nxp/src/chips/mimxrt1011.rs113
-rw-r--r--embassy-nxp/src/gpio.rs2
-rw-r--r--embassy-nxp/src/gpio/rt1xxx.rs895
-rw-r--r--embassy-nxp/src/lib.rs87
-rw-r--r--embassy-nxp/src/time_driver/pit.rs187
-rw-r--r--examples/mimxrt1011/.cargo/config.toml8
-rw-r--r--examples/mimxrt1011/Cargo.toml29
-rw-r--r--examples/mimxrt1011/build.rs14
-rw-r--r--examples/mimxrt1011/src/bin/blinky.rs48
-rw-r--r--examples/mimxrt1011/src/bin/button.rs62
-rw-r--r--examples/mimxrt1011/src/lib.rs75
16 files changed, 1780 insertions, 9 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e4814ff27..070e8fbd3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -36,6 +36,7 @@
36 // "examples/nrf52840-rtic/Cargo.toml", 36 // "examples/nrf52840-rtic/Cargo.toml",
37 // "examples/nrf5340/Cargo.toml", 37 // "examples/nrf5340/Cargo.toml",
38 // "examples/nrf-rtos-trace/Cargo.toml", 38 // "examples/nrf-rtos-trace/Cargo.toml",
39 // "examples/mimxrt1011/Cargo.toml",
39 // "examples/rp/Cargo.toml", 40 // "examples/rp/Cargo.toml",
40 // "examples/std/Cargo.toml", 41 // "examples/std/Cargo.toml",
41 // "examples/stm32c0/Cargo.toml", 42 // "examples/stm32c0/Cargo.toml",
diff --git a/ci.sh b/ci.sh
index 9d3e47a41..e225bc7c9 100755
--- a/ci.sh
+++ b/ci.sh
@@ -181,6 +181,7 @@ cargo batch \
181 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ 181 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
182 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ 182 --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
183 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \ 183 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf --features lpc55,defmt \
184 --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv7em-none-eabihf --features mimxrt1011,defmt,time-driver-pit \
184 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \ 185 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0c1104dgs20,defmt,time-driver-any \
185 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \ 186 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3507pm,defmt,time-driver-any \
186 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \ 187 --- build --release --manifest-path embassy-mspm0/Cargo.toml --target thumbv6m-none-eabi --features mspm0g3519pz,defmt,time-driver-any \
@@ -264,6 +265,7 @@ cargo batch \
264 --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \ 265 --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \
265 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \ 266 --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \
266 --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \ 267 --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \
268 --- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \
267 --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \ 269 --- build --release --manifest-path examples/mspm0g3507/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3507 \
268 --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \ 270 --- build --release --manifest-path examples/mspm0g3519/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0g3519 \
269 --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \ 271 --- build --release --manifest-path examples/mspm0l1306/Cargo.toml --target thumbv6m-none-eabi --artifact-dir out/examples/mspm0l1306 \
diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml
index 56d00bfb2..625906183 100644
--- a/embassy-nxp/Cargo.toml
+++ b/embassy-nxp/Cargo.toml
@@ -11,22 +11,50 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", fe
11embassy-sync = { version = "0.7.0", path = "../embassy-sync" } 11embassy-sync = { version = "0.7.0", path = "../embassy-sync" }
12defmt = { version = "1", optional = true } 12defmt = { version = "1", optional = true }
13log = { version = "0.4.27", optional = true } 13log = { version = "0.4.27", optional = true }
14embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
15embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
16embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
14 17
15## Chip dependencies 18## Chip dependencies
16lpc55-pac = { version = "0.5.0", optional = true } 19lpc55-pac = { version = "0.5.0", optional = true }
20nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e" }
21
22imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] }
23
24[build-dependencies]
25cfg_aliases = "0.2.1"
26nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "1e010dbe75ab0e14dd908e4646391403414c8a8e", features = ["metadata"], optional = true }
27proc-macro2 = "1.0.95"
28quote = "1.0.15"
17 29
18[features] 30[features]
19default = ["rt"] 31default = ["rt"]
20# Enable PACs as optional dependencies, since some chip families will use different pac crates. 32# Enable PACs as optional dependencies, since some chip families will use different pac crates (temporarily).
21rt = ["lpc55-pac?/rt"] 33rt = ["lpc55-pac?/rt", "nxp-pac?/rt"]
22 34
23## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers. 35## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
24defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"] 36defmt = ["dep:defmt", "embassy-hal-internal/defmt", "embassy-sync/defmt"]
37
38log = ["dep:log"]
39
40## Use Periodic Interrupt Timer (PIT) as the time driver for `embassy-time`, with a tick rate of 1 MHz
41time-driver-pit = ["_time_driver", "embassy-time?/tick-hz-1_000_000"]
42
25## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable) 43## Reexport the PAC for the currently enabled chip at `embassy_nxp::pac` (unstable)
26unstable-pac = [] 44unstable-pac = []
27# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. 45# This is unstable because semver-minor (non-breaking) releases of embassy-nxp may major-bump (breaking) the PAC version.
28# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. 46# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.
29# There are no plans to make this stable. 47# There are no plans to make this stable.
30 48
49## internal use only
50#
51# This feature is unfortunately a hack around the fact that cfg_aliases cannot apply to the buildscript
52# that creates the aliases.
53_rt1xxx = []
54
55# A timer driver is enabled.
56_time_driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
57
31#! ### Chip selection features 58#! ### Chip selection features
32lpc55 = ["lpc55-pac"] 59lpc55 = ["dep:lpc55-pac"]
60mimxrt1011 = ["nxp-pac/mimxrt1011", "_rt1xxx", "dep:imxrt-rt"]
diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs
new file mode 100644
index 000000000..6c10d0e69
--- /dev/null
+++ b/embassy-nxp/build.rs
@@ -0,0 +1,136 @@
1use std::io::Write;
2use std::path::{Path, PathBuf};
3use std::process::Command;
4use std::{env, fs};
5
6use cfg_aliases::cfg_aliases;
7#[cfg(feature = "_rt1xxx")]
8use nxp_pac::metadata;
9#[allow(unused)]
10use proc_macro2::TokenStream;
11#[allow(unused)]
12use quote::quote;
13
14#[path = "./build_common.rs"]
15mod common;
16
17fn main() {
18 let mut cfgs = common::CfgSet::new();
19 common::set_target_cfgs(&mut cfgs);
20
21 let chip_name = match env::vars()
22 .map(|(a, _)| a)
23 .filter(|x| x.starts_with("CARGO_FEATURE_MIMXRT") || x.starts_with("CARGO_FEATURE_LPC"))
24 .get_one()
25 {
26 Ok(x) => x,
27 Err(GetOneError::None) => panic!("No mimxrt/lpc Cargo feature enabled"),
28 Err(GetOneError::Multiple) => panic!("Multiple mimxrt/lpc Cargo features enabled"),
29 }
30 .strip_prefix("CARGO_FEATURE_")
31 .unwrap()
32 .to_ascii_lowercase();
33
34 cfg_aliases! {
35 rt1xxx: { feature = "mimxrt1011" },
36 gpio1: { feature = "mimxrt1011" },
37 gpio2: { feature = "mimxrt1011" },
38 gpio5: { feature = "mimxrt1011" },
39 }
40
41 eprintln!("chip: {chip_name}");
42
43 generate_code();
44}
45
46#[cfg(feature = "_rt1xxx")]
47fn generate_iomuxc() -> TokenStream {
48 use proc_macro2::{Ident, Span};
49
50 let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| {
51 let name = Ident::new(&registers.name, Span::call_site());
52 let address = registers.pad_ctl;
53
54 quote! {
55 pub const #name: u32 = #address;
56 }
57 });
58
59 let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| {
60 let name = Ident::new(&registers.name, Span::call_site());
61 let address = registers.mux_ctl;
62
63 quote! {
64 pub const #name: u32 = #address;
65 }
66 });
67
68 quote! {
69 pub mod iomuxc {
70 pub mod pads {
71 #(#pads)*
72 }
73
74 pub mod muxes {
75 #(#muxes)*
76 }
77 }
78 }
79}
80
81fn generate_code() {
82 #[allow(unused)]
83 use std::fmt::Write;
84
85 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
86 #[allow(unused_mut)]
87 let mut output = String::new();
88
89 #[cfg(feature = "_rt1xxx")]
90 writeln!(&mut output, "{}", generate_iomuxc()).unwrap();
91
92 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
93 fs::write(&out_file, output).unwrap();
94 rustfmt(&out_file);
95}
96
97/// rustfmt a given path.
98/// Failures are logged to stderr and ignored.
99fn rustfmt(path: impl AsRef<Path>) {
100 let path = path.as_ref();
101 match Command::new("rustfmt").args([path]).output() {
102 Err(e) => {
103 eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
104 }
105 Ok(out) => {
106 if !out.status.success() {
107 eprintln!("rustfmt {:?} failed:", path);
108 eprintln!("=== STDOUT:");
109 std::io::stderr().write_all(&out.stdout).unwrap();
110 eprintln!("=== STDERR:");
111 std::io::stderr().write_all(&out.stderr).unwrap();
112 }
113 }
114 }
115}
116
117enum GetOneError {
118 None,
119 Multiple,
120}
121
122trait IteratorExt: Iterator {
123 fn get_one(self) -> Result<Self::Item, GetOneError>;
124}
125
126impl<T: Iterator> IteratorExt for T {
127 fn get_one(mut self) -> Result<Self::Item, GetOneError> {
128 match self.next() {
129 None => Err(GetOneError::None),
130 Some(res) => match self.next() {
131 Some(_) => Err(GetOneError::Multiple),
132 None => Ok(res),
133 },
134 }
135 }
136}
diff --git a/embassy-nxp/build_common.rs b/embassy-nxp/build_common.rs
new file mode 100644
index 000000000..4f24e6d37
--- /dev/null
+++ b/embassy-nxp/build_common.rs
@@ -0,0 +1,94 @@
1// NOTE: this file is copy-pasted between several Embassy crates, because there is no
2// straightforward way to share this code:
3// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path =
4// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate
5// reside in the crate's directory,
6// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because
7// symlinks don't work on Windows.
8
9use std::collections::HashSet;
10use std::env;
11
12/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
13/// them (`cargo:rust-check-cfg=cfg(X)`).
14#[derive(Debug)]
15pub struct CfgSet {
16 enabled: HashSet<String>,
17 declared: HashSet<String>,
18}
19
20impl CfgSet {
21 pub fn new() -> Self {
22 Self {
23 enabled: HashSet::new(),
24 declared: HashSet::new(),
25 }
26 }
27
28 /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation.
29 ///
30 /// All configs that can potentially be enabled should be unconditionally declared using
31 /// [`Self::declare()`].
32 pub fn enable(&mut self, cfg: impl AsRef<str>) {
33 if self.enabled.insert(cfg.as_ref().to_owned()) {
34 println!("cargo:rustc-cfg={}", cfg.as_ref());
35 }
36 }
37
38 pub fn enable_all(&mut self, cfgs: &[impl AsRef<str>]) {
39 for cfg in cfgs.iter() {
40 self.enable(cfg.as_ref());
41 }
42 }
43
44 /// Declare a valid config for conditional compilation, without enabling it.
45 ///
46 /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
47 pub fn declare(&mut self, cfg: impl AsRef<str>) {
48 if self.declared.insert(cfg.as_ref().to_owned()) {
49 println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
50 }
51 }
52
53 pub fn declare_all(&mut self, cfgs: &[impl AsRef<str>]) {
54 for cfg in cfgs.iter() {
55 self.declare(cfg.as_ref());
56 }
57 }
58
59 pub fn set(&mut self, cfg: impl Into<String>, enable: bool) {
60 let cfg = cfg.into();
61 if enable {
62 self.enable(cfg.clone());
63 }
64 self.declare(cfg);
65 }
66}
67
68/// Sets configs that describe the target platform.
69pub fn set_target_cfgs(cfgs: &mut CfgSet) {
70 let target = env::var("TARGET").unwrap();
71
72 if target.starts_with("thumbv6m-") {
73 cfgs.enable_all(&["cortex_m", "armv6m"]);
74 } else if target.starts_with("thumbv7m-") {
75 cfgs.enable_all(&["cortex_m", "armv7m"]);
76 } else if target.starts_with("thumbv7em-") {
77 cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]);
78 } else if target.starts_with("thumbv8m.base") {
79 cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]);
80 } else if target.starts_with("thumbv8m.main") {
81 cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]);
82 }
83 cfgs.declare_all(&[
84 "cortex_m",
85 "armv6m",
86 "armv7m",
87 "armv7em",
88 "armv8m",
89 "armv8m_base",
90 "armv8m_main",
91 ]);
92
93 cfgs.set("has_fpu", target.ends_with("-eabihf"));
94}
diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs
new file mode 100644
index 000000000..a74d953fc
--- /dev/null
+++ b/embassy-nxp/src/chips/mimxrt1011.rs
@@ -0,0 +1,113 @@
1// This must be imported so that __preinit is defined.
2use imxrt_rt as _;
3pub use nxp_pac as pac;
4
5embassy_hal_internal::peripherals! {
6 // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other
7 // peripheral types (e.g. I2C).
8 GPIO_00,
9 GPIO_01,
10 GPIO_02,
11 GPIO_03,
12 GPIO_04,
13 GPIO_05,
14 GPIO_06,
15 GPIO_07,
16 GPIO_08,
17 GPIO_09,
18 GPIO_10,
19 GPIO_11,
20 GPIO_12,
21 GPIO_13,
22 GPIO_AD_00,
23 GPIO_AD_01,
24 GPIO_AD_02,
25 GPIO_AD_03,
26 GPIO_AD_04,
27 GPIO_AD_05,
28 GPIO_AD_06,
29 GPIO_AD_07,
30 GPIO_AD_08,
31 GPIO_AD_09,
32 GPIO_AD_10,
33 GPIO_AD_11,
34 GPIO_AD_12,
35 GPIO_AD_13,
36 GPIO_AD_14,
37 GPIO_SD_00,
38 GPIO_SD_01,
39 GPIO_SD_02,
40 GPIO_SD_03,
41 GPIO_SD_04,
42 GPIO_SD_05,
43 GPIO_SD_06,
44 GPIO_SD_07,
45 GPIO_SD_08,
46 GPIO_SD_09,
47 GPIO_SD_10,
48 GPIO_SD_11,
49 GPIO_SD_12,
50 GPIO_SD_13,
51 PMIC_ON_REQ,
52}
53
54impl_gpio! {
55 // GPIO Bank 1
56 GPIO_00(Gpio1, 0);
57 GPIO_01(Gpio1, 1);
58 GPIO_02(Gpio1, 2);
59 GPIO_03(Gpio1, 3);
60 GPIO_04(Gpio1, 4);
61 GPIO_05(Gpio1, 5);
62 GPIO_06(Gpio1, 6);
63 GPIO_07(Gpio1, 7);
64 GPIO_08(Gpio1, 8);
65 GPIO_09(Gpio1, 9);
66 GPIO_10(Gpio1, 10);
67 GPIO_11(Gpio1, 11);
68 GPIO_12(Gpio1, 12);
69 GPIO_13(Gpio1, 13);
70 GPIO_AD_00(Gpio1, 14);
71 GPIO_AD_01(Gpio1, 15);
72 GPIO_AD_02(Gpio1, 16);
73 GPIO_AD_03(Gpio1, 17);
74 GPIO_AD_04(Gpio1, 18);
75 GPIO_AD_05(Gpio1, 19);
76 GPIO_AD_06(Gpio1, 20);
77 GPIO_AD_07(Gpio1, 21);
78 GPIO_AD_08(Gpio1, 22);
79 GPIO_AD_09(Gpio1, 23);
80 GPIO_AD_10(Gpio1, 24);
81 GPIO_AD_11(Gpio1, 25);
82 GPIO_AD_12(Gpio1, 26);
83 GPIO_AD_13(Gpio1, 27);
84 GPIO_AD_14(Gpio1, 28);
85
86 // GPIO Bank 2
87 GPIO_SD_00(Gpio2, 0);
88 GPIO_SD_01(Gpio2, 1);
89 GPIO_SD_02(Gpio2, 2);
90 GPIO_SD_03(Gpio2, 3);
91 GPIO_SD_04(Gpio2, 4);
92 GPIO_SD_05(Gpio2, 5);
93 GPIO_SD_06(Gpio2, 6);
94 GPIO_SD_07(Gpio2, 7);
95 GPIO_SD_08(Gpio2, 8);
96 GPIO_SD_09(Gpio2, 9);
97 GPIO_SD_10(Gpio2, 10);
98 GPIO_SD_11(Gpio2, 11);
99 GPIO_SD_12(Gpio2, 12);
100 GPIO_SD_13(Gpio2, 13);
101
102 // GPIO Bank 5
103 PMIC_ON_REQ(Gpio5, 0);
104}
105
106pub(crate) mod _generated {
107 #![allow(dead_code)]
108 #![allow(unused_imports)]
109 #![allow(non_snake_case)]
110 #![allow(missing_docs)]
111
112 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
113}
diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs
index 809903d97..3049cc12d 100644
--- a/embassy-nxp/src/gpio.rs
+++ b/embassy-nxp/src/gpio.rs
@@ -1,5 +1,7 @@
1//! General purpose input/output (GPIO) driver. 1//! General purpose input/output (GPIO) driver.
2#![macro_use]
2 3
3#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")] 4#[cfg_attr(feature = "lpc55", path = "./gpio/lpc55.rs")]
5#[cfg_attr(rt1xxx, path = "./gpio/rt1xxx.rs")]
4mod inner; 6mod inner;
5pub use inner::*; 7pub use inner::*;
diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs
new file mode 100644
index 000000000..9c58e8a7d
--- /dev/null
+++ b/embassy-nxp/src/gpio/rt1xxx.rs
@@ -0,0 +1,895 @@
1#![macro_use]
2
3use core::future::Future;
4use core::ops::Not;
5use core::pin::Pin as FuturePin;
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10use nxp_pac::gpio::vals::Icr;
11use nxp_pac::iomuxc::vals::Pus;
12
13use crate::chip::{mux_address, pad_address};
14use crate::pac::common::{Reg, RW};
15#[cfg(feature = "rt")]
16use crate::pac::interrupt;
17use crate::pac::iomuxc::regs::{Ctl, MuxCtl};
18#[cfg(gpio5)]
19use crate::pac::{self, gpio::Gpio};
20
21/// The GPIO pin level for pins set on "Digital" mode.
22#[derive(Debug, Eq, PartialEq, Clone, Copy)]
23pub enum Level {
24 /// Logical low. Corresponds to 0V.
25 Low,
26 /// Logical high. Corresponds to VDD.
27 High,
28}
29
30impl From<bool> for Level {
31 fn from(val: bool) -> Self {
32 match val {
33 true => Self::High,
34 false => Self::Low,
35 }
36 }
37}
38
39impl From<Level> for bool {
40 fn from(level: Level) -> bool {
41 match level {
42 Level::Low => false,
43 Level::High => true,
44 }
45 }
46}
47
48impl Not for Level {
49 type Output = Self;
50
51 fn not(self) -> Self::Output {
52 match self {
53 Level::Low => Level::High,
54 Level::High => Level::Low,
55 }
56 }
57}
58
59/// Pull setting for a GPIO input set on "Digital" mode.
60#[derive(Debug, Clone, Copy, Eq, PartialEq)]
61pub enum Pull {
62 /// No pull.
63 None,
64
65 // TODO: What Does PUE::KEEPER mean here?
66
67 // 22 kOhm pull-up resistor.
68 Up22K,
69
70 // 47 kOhm pull-up resistor.
71 Up47K,
72
73 // 100 kOhm pull-up resistor.
74 Up100K,
75
76 // 100 kOhm pull-down resistor.
77 Down100K,
78}
79
80/// Drive strength of an output
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum Drive {
84 Disabled,
85 _150R,
86 _75R,
87 _50R,
88 _37R,
89 _30R,
90 _25R,
91 _20R,
92}
93
94/// Slew rate of an output
95#[derive(Copy, Clone, Debug, Eq, PartialEq)]
96#[cfg_attr(feature = "defmt", derive(defmt::Format))]
97pub enum SlewRate {
98 Slow,
99
100 Fast,
101}
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum Bank {
105 /// Bank 1
106 #[cfg(gpio1)]
107 Gpio1,
108
109 /// Bank 2
110 #[cfg(gpio2)]
111 Gpio2,
112
113 /// Bank 5
114 #[cfg(gpio5)]
115 Gpio5,
116}
117
118/// GPIO flexible pin.
119///
120/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
121/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
122/// mode.
123pub struct Flex<'d> {
124 pub(crate) pin: Peri<'d, AnyPin>,
125}
126
127impl<'d> Flex<'d> {
128 /// Wrap the pin in a `Flex`.
129 ///
130 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
131 /// before the pin is put into output mode.
132 #[inline]
133 pub fn new(pin: Peri<'d, impl Pin>) -> Self {
134 Self { pin: pin.into() }
135 }
136
137 /// Set the pin's pull.
138 #[inline]
139 pub fn set_pull(&mut self, pull: Pull) {
140 let (pke, pue, pus) = match pull {
141 Pull::None => (false, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
142 Pull::Up22K => (true, true, Pus::PUS_3_22K_OHM_PULL_UP),
143 Pull::Up47K => (true, true, Pus::PUS_1_47K_OHM_PULL_UP),
144 Pull::Up100K => (true, true, Pus::PUS_2_100K_OHM_PULL_UP),
145 Pull::Down100K => (true, true, Pus::PUS_0_100K_OHM_PULL_DOWN),
146 };
147
148 self.pin.pad().modify(|w| {
149 w.set_pke(pke);
150 w.set_pue(pue);
151 w.set_pus(pus);
152 });
153 }
154
155 // Set the pin's slew rate.
156 #[inline]
157 pub fn set_slewrate(&mut self, rate: SlewRate) {
158 self.pin.pad().modify(|w| {
159 w.set_sre(match rate {
160 SlewRate::Slow => false,
161 SlewRate::Fast => true,
162 });
163 });
164 }
165
166 /// Set the pin's Schmitt trigger.
167 #[inline]
168 pub fn set_schmitt(&mut self, enable: bool) {
169 self.pin.pad().modify(|w| {
170 w.set_hys(enable);
171 });
172 }
173
174 /// Put the pin into input mode.
175 ///
176 /// The pull setting is left unchanged.
177 #[inline]
178 pub fn set_as_input(&mut self) {
179 self.pin.mux().modify(|w| {
180 w.set_mux_mode(GPIO_MUX_MODE);
181 });
182
183 // Setting direction is RMW
184 critical_section::with(|_cs| {
185 self.pin.block().gdir().modify(|w| {
186 w.set_gdir(self.pin.pin_number() as usize, false);
187 });
188 })
189 }
190
191 /// Put the pin into output mode.
192 ///
193 /// The pin level will be whatever was set before (or low by default). If you want it to begin
194 /// at a specific level, call `set_high`/`set_low` on the pin first.
195 #[inline]
196 pub fn set_as_output(&mut self) {
197 self.pin.mux().modify(|w| {
198 w.set_mux_mode(GPIO_MUX_MODE);
199 });
200
201 // Setting direction is RMW
202 critical_section::with(|_cs| {
203 self.pin.block().gdir().modify(|w| {
204 w.set_gdir(self.pin.pin_number() as usize, true);
205 });
206 })
207 }
208
209 /// Put the pin into input + open-drain output mode.
210 ///
211 /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
212 /// it to high, in which case you can read the input to figure out whether another device
213 /// is driving the line low.
214 ///
215 /// The pin level will be whatever was set before (or low by default). If you want it to begin
216 /// at a specific level, call `set_high`/`set_low` on the pin first.
217 ///
218 /// The internal weak pull-up and pull-down resistors will be disabled.
219 #[inline]
220 pub fn set_as_input_output(&mut self) {
221 self.pin.pad().modify(|w| {
222 w.set_ode(true);
223 });
224 }
225
226 /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
227 /// amount of power possible.
228 ///
229 /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
230 /// really. Drivers should `set_as_disconnected()` pins when dropped.
231 ///
232 /// Note that this also disables the pull-up and pull-down resistors.
233 #[inline]
234 pub fn set_as_disconnected(&mut self) {
235 self.pin.pad().modify(|w| {
236 w.set_ode(false);
237 w.set_pke(false);
238 w.set_pue(false);
239 w.set_pus(Pus::PUS_0_100K_OHM_PULL_DOWN);
240 });
241 }
242
243 /// Get whether the pin input level is high.
244 #[inline]
245 pub fn is_high(&self) -> bool {
246 self.pin.block().psr().read().psr(self.pin.pin_number() as usize)
247 }
248
249 /// Get whether the pin input level is low.
250 #[inline]
251 pub fn is_low(&self) -> bool {
252 !self.is_high()
253 }
254
255 /// Returns current pin level
256 #[inline]
257 pub fn get_level(&self) -> Level {
258 self.is_high().into()
259 }
260
261 /// Set the output as high.
262 #[inline]
263 pub fn set_high(&mut self) {
264 self.pin.block().dr_set().write(|w| {
265 w.set_dr_set(self.pin.pin_number() as usize, true);
266 });
267 }
268
269 /// Set the output as low.
270 #[inline]
271 pub fn set_low(&mut self) {
272 self.pin.block().dr_clear().write(|w| {
273 w.set_dr_clear(self.pin.pin_number() as usize, true);
274 });
275 }
276
277 /// Toggle pin output
278 #[inline]
279 pub fn toggle(&mut self) {
280 self.pin.block().dr_toggle().write(|w| {
281 w.set_dr_toggle(self.pin.pin_number() as usize, true);
282 });
283 }
284
285 /// Set the output level.
286 #[inline]
287 pub fn set_level(&mut self, level: Level) {
288 match level {
289 Level::Low => self.set_low(),
290 Level::High => self.set_high(),
291 }
292 }
293
294 /// Get the current pin output level.
295 #[inline]
296 pub fn get_output_level(&self) -> Level {
297 self.is_set_high().into()
298 }
299
300 /// Is the output level high?
301 ///
302 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_high`].
303 #[inline]
304 pub fn is_set_high(&self) -> bool {
305 self.pin.block().dr().read().dr(self.pin.pin_number() as usize)
306 }
307
308 /// Is the output level low?
309 ///
310 /// If the [`Flex`] is set as an input, then this is equivalent to [`Flex::is_low`].
311 #[inline]
312 pub fn is_set_low(&self) -> bool {
313 !self.is_set_high()
314 }
315
316 /// Wait until the pin is high. If it is already high, return immediately.
317 #[inline]
318 pub async fn wait_for_high(&mut self) {
319 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::High).await
320 }
321
322 /// Wait until the pin is low. If it is already low, return immediately.
323 #[inline]
324 pub async fn wait_for_low(&mut self) {
325 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::Low).await
326 }
327
328 /// Wait for the pin to undergo a transition from low to high.
329 #[inline]
330 pub async fn wait_for_rising_edge(&mut self) {
331 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::RisingEdge).await
332 }
333
334 /// Wait for the pin to undergo a transition from high to low.
335 #[inline]
336 pub async fn wait_for_falling_edge(&mut self) {
337 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::FallingEdge).await
338 }
339
340 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
341 #[inline]
342 pub async fn wait_for_any_edge(&mut self) {
343 InputFuture::new(self.pin.reborrow(), InterruptConfiguration::AnyEdge).await
344 }
345}
346
347impl<'d> Drop for Flex<'d> {
348 fn drop(&mut self) {
349 self.set_as_disconnected();
350 }
351}
352
353/// GPIO input driver.
354pub struct Input<'d> {
355 pin: Flex<'d>,
356}
357
358impl<'d> Input<'d> {
359 /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
360 #[inline]
361 pub fn new(pin: Peri<'d, impl Pin>, pull: Pull) -> Self {
362 let mut pin = Flex::new(pin);
363 pin.set_as_input();
364 pin.set_pull(pull);
365 Self { pin }
366 }
367
368 /// Get whether the pin input level is high.
369 #[inline]
370 pub fn is_high(&self) -> bool {
371 self.pin.is_high()
372 }
373
374 /// Get whether the pin input level is low.
375 #[inline]
376 pub fn is_low(&self) -> bool {
377 self.pin.is_low()
378 }
379
380 /// Get the current pin input level.
381 #[inline]
382 pub fn get_level(&self) -> Level {
383 self.pin.get_level()
384 }
385
386 /// Wait until the pin is high. If it is already high, return immediately.
387 #[inline]
388 pub async fn wait_for_high(&mut self) {
389 self.pin.wait_for_high().await
390 }
391
392 /// Wait until the pin is low. If it is already low, return immediately.
393 #[inline]
394 pub async fn wait_for_low(&mut self) {
395 self.pin.wait_for_low().await
396 }
397
398 /// Wait for the pin to undergo a transition from low to high.
399 #[inline]
400 pub async fn wait_for_rising_edge(&mut self) {
401 self.pin.wait_for_rising_edge().await
402 }
403
404 /// Wait for the pin to undergo a transition from high to low.
405 #[inline]
406 pub async fn wait_for_falling_edge(&mut self) {
407 self.pin.wait_for_falling_edge().await
408 }
409
410 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
411 #[inline]
412 pub async fn wait_for_any_edge(&mut self) {
413 self.pin.wait_for_any_edge().await
414 }
415}
416
417/// GPIO output driver.
418///
419/// Note that pins will **return to their floating state** when `Output` is dropped.
420/// If pins should retain their state indefinitely, either keep ownership of the
421/// `Output`, or pass it to [`core::mem::forget`].
422pub struct Output<'d> {
423 pin: Flex<'d>,
424}
425
426impl<'d> Output<'d> {
427 /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
428 #[inline]
429 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
430 let mut pin = Flex::new(pin);
431 pin.set_as_output();
432 pin.set_level(initial_output);
433 Self { pin }
434 }
435
436 /// Set the output as high.
437 #[inline]
438 pub fn set_high(&mut self) {
439 self.pin.set_high();
440 }
441
442 /// Set the output as low.
443 #[inline]
444 pub fn set_low(&mut self) {
445 self.pin.set_low();
446 }
447
448 /// Set the output level.
449 #[inline]
450 pub fn set_level(&mut self, level: Level) {
451 self.pin.set_level(level)
452 }
453
454 /// Is the output pin set as high?
455 #[inline]
456 pub fn is_set_high(&self) -> bool {
457 self.pin.is_set_high()
458 }
459
460 /// Is the output pin set as low?
461 #[inline]
462 pub fn is_set_low(&self) -> bool {
463 self.pin.is_set_low()
464 }
465
466 /// What level output is set to
467 #[inline]
468 pub fn get_output_level(&self) -> Level {
469 self.pin.get_output_level()
470 }
471
472 /// Toggle pin output
473 #[inline]
474 pub fn toggle(&mut self) {
475 self.pin.toggle();
476 }
477}
478
479/// GPIO output open-drain driver.
480///
481/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
482/// If pins should retain their state indefinitely, either keep ownership of the
483/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
484pub struct OutputOpenDrain<'d> {
485 pin: Flex<'d>,
486}
487
488impl<'d> OutputOpenDrain<'d> {
489 /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
490 #[inline]
491 pub fn new(pin: Peri<'d, impl Pin>, initial_output: Level) -> Self {
492 let mut pin = Flex::new(pin);
493 pin.set_level(initial_output);
494 pin.set_as_input_output();
495 Self { pin }
496 }
497
498 /// Get whether the pin input level is high.
499 #[inline]
500 pub fn is_high(&self) -> bool {
501 !self.pin.is_low()
502 }
503
504 /// Get whether the pin input level is low.
505 #[inline]
506 pub fn is_low(&self) -> bool {
507 self.pin.is_low()
508 }
509
510 /// Get the current pin input level.
511 #[inline]
512 pub fn get_level(&self) -> Level {
513 self.pin.get_level()
514 }
515
516 /// Set the output as high.
517 #[inline]
518 pub fn set_high(&mut self) {
519 self.pin.set_high();
520 }
521
522 /// Set the output as low.
523 #[inline]
524 pub fn set_low(&mut self) {
525 self.pin.set_low();
526 }
527
528 /// Set the output level.
529 #[inline]
530 pub fn set_level(&mut self, level: Level) {
531 self.pin.set_level(level);
532 }
533
534 /// Get whether the output level is set to high.
535 #[inline]
536 pub fn is_set_high(&self) -> bool {
537 self.pin.is_set_high()
538 }
539
540 /// Get whether the output level is set to low.
541 #[inline]
542 pub fn is_set_low(&self) -> bool {
543 self.pin.is_set_low()
544 }
545
546 /// Get the current output level.
547 #[inline]
548 pub fn get_output_level(&self) -> Level {
549 self.pin.get_output_level()
550 }
551
552 /// Toggle pin output
553 #[inline]
554 pub fn toggle(&mut self) {
555 self.pin.toggle()
556 }
557
558 /// Wait until the pin is high. If it is already high, return immediately.
559 #[inline]
560 pub async fn wait_for_high(&mut self) {
561 self.pin.wait_for_high().await
562 }
563
564 /// Wait until the pin is low. If it is already low, return immediately.
565 #[inline]
566 pub async fn wait_for_low(&mut self) {
567 self.pin.wait_for_low().await
568 }
569
570 /// Wait for the pin to undergo a transition from low to high.
571 #[inline]
572 pub async fn wait_for_rising_edge(&mut self) {
573 self.pin.wait_for_rising_edge().await
574 }
575
576 /// Wait for the pin to undergo a transition from high to low.
577 #[inline]
578 pub async fn wait_for_falling_edge(&mut self) {
579 self.pin.wait_for_falling_edge().await
580 }
581
582 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
583 #[inline]
584 pub async fn wait_for_any_edge(&mut self) {
585 self.pin.wait_for_any_edge().await
586 }
587}
588
589#[allow(private_bounds)]
590pub trait Pin: PeripheralType + Into<AnyPin> + SealedPin + Sized + 'static {
591 /// Returns the pin number within a bank
592 #[inline]
593 fn pin(&self) -> u8 {
594 self.pin_number()
595 }
596
597 #[inline]
598 fn bank(&self) -> Bank {
599 self._bank()
600 }
601}
602
603/// Type-erased GPIO pin.
604pub struct AnyPin {
605 pub(crate) pin_number: u8,
606 pub(crate) bank: Bank,
607}
608
609impl AnyPin {
610 /// Unsafely create a new type-erased pin.
611 ///
612 /// # Safety
613 ///
614 /// You must ensure that you’re only using one instance of this type at a time.
615 pub unsafe fn steal(bank: Bank, pin_number: u8) -> Peri<'static, Self> {
616 Peri::new_unchecked(Self { pin_number, bank })
617 }
618}
619
620impl_peripheral!(AnyPin);
621
622impl Pin for AnyPin {}
623impl SealedPin for AnyPin {
624 #[inline]
625 fn pin_number(&self) -> u8 {
626 self.pin_number
627 }
628
629 #[inline]
630 fn _bank(&self) -> Bank {
631 self.bank
632 }
633}
634
635// Impl details
636
637/// Mux mode for GPIO pins. This is constant across all RT1xxx parts.
638const GPIO_MUX_MODE: u8 = 0b101;
639
640// FIXME: These don't always need to be 32 entries. GPIO5 on RT1101 contains a single pin and GPIO2 only 14.
641#[cfg(gpio1)]
642static GPIO1_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
643#[cfg(gpio2)]
644static GPIO2_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
645#[cfg(gpio5)]
646static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
647
648/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate.
649pub(crate) trait SealedPin: Sized {
650 fn pin_number(&self) -> u8;
651
652 fn _bank(&self) -> Bank;
653
654 #[inline]
655 fn block(&self) -> Gpio {
656 match self._bank() {
657 #[cfg(gpio1)]
658 Bank::Gpio1 => pac::GPIO1,
659 #[cfg(gpio2)]
660 Bank::Gpio2 => pac::GPIO2,
661 #[cfg(gpio5)]
662 Bank::Gpio5 => pac::GPIO5,
663 }
664 }
665
666 #[inline]
667 fn mux(&self) -> Reg<MuxCtl, RW> {
668 // SAFETY: The generated mux address table is valid since it is generated from the SVD files.
669 let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() };
670
671 // SAFETY: The register at the address is an instance of MuxCtl.
672 unsafe { Reg::from_ptr(address as *mut _) }
673 }
674
675 #[inline]
676 fn pad(&self) -> Reg<Ctl, RW> {
677 // SAFETY: The generated pad address table is valid since it is generated from the SVD files.
678 let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() };
679
680 // SAFETY: The register at the address is an instance of Ctl.
681 unsafe { Reg::from_ptr(address as *mut _) }
682 }
683
684 fn waker(&self) -> &AtomicWaker {
685 match self._bank() {
686 #[cfg(gpio1)]
687 Bank::Gpio1 => &GPIO1_WAKERS[self.pin_number() as usize],
688 #[cfg(gpio2)]
689 Bank::Gpio2 => &GPIO2_WAKERS[self.pin_number() as usize],
690 #[cfg(gpio5)]
691 Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize],
692 }
693 }
694}
695
696/// This enum matches the layout of Icr.
697enum InterruptConfiguration {
698 Low,
699 High,
700 RisingEdge,
701 FallingEdge,
702 AnyEdge,
703}
704
705#[must_use = "futures do nothing unless you `.await` or poll them"]
706struct InputFuture<'d> {
707 pin: Peri<'d, AnyPin>,
708}
709
710impl<'d> InputFuture<'d> {
711 fn new(pin: Peri<'d, AnyPin>, config: InterruptConfiguration) -> Self {
712 let block = pin.block();
713
714 let (icr, edge_sel) = match config {
715 InterruptConfiguration::Low => (Icr::LOW_LEVEL, false),
716 InterruptConfiguration::High => (Icr::HIGH_LEVEL, false),
717 InterruptConfiguration::RisingEdge => (Icr::RISING_EDGE, false),
718 InterruptConfiguration::FallingEdge => (Icr::FALLING_EDGE, false),
719 InterruptConfiguration::AnyEdge => (Icr::FALLING_EDGE, true),
720 };
721
722 let index = if pin.pin_number() > 15 { 1 } else { 0 };
723
724 // Interrupt configuration performs RMW
725 critical_section::with(|_cs| {
726 // Disable interrupt so a level/edge detection change does not cause ISR to be set.
727 block.imr().modify(|w| {
728 w.set_imr(pin.pin_number() as usize, false);
729 });
730
731 block.icr(index).modify(|w| {
732 w.set_pin(pin.pin_number() as usize, icr);
733 });
734
735 block.edge_sel().modify(|w| {
736 w.set_edge_sel(pin.pin_number() as usize, edge_sel);
737 });
738
739 // Clear the previous interrupt.
740 block.isr().modify(|w| {
741 // "Status flags are cleared by writing a 1 to the corresponding bit position."
742 w.set_isr(pin.pin_number() as usize, true);
743 });
744 });
745
746 Self { pin }
747 }
748}
749
750impl<'d> Future for InputFuture<'d> {
751 type Output = ();
752
753 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
754 // We need to register/re-register the waker for each poll because any
755 // calls to wake will deregister the waker.
756 let waker = self.pin.waker();
757 waker.register(cx.waker());
758
759 // Enabling interrupt is RMW
760 critical_section::with(|_cs| {
761 self.pin.block().imr().modify(|w| {
762 w.set_imr(self.pin.pin_number() as usize, true);
763 });
764 });
765
766 let isr = self.pin.block().isr().read();
767
768 if isr.isr(self.pin.pin_number() as usize) {
769 return Poll::Ready(());
770 }
771
772 Poll::Pending
773 }
774}
775
776/// A macro to generate all GPIO pins.
777///
778/// This generates a lookup table for IOMUX register addresses.
779macro_rules! impl_gpio {
780 (
781 $($name: ident($bank: ident, $pin_number: expr);)*
782 ) => {
783 #[inline]
784 pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
785 match (bank, pin) {
786 $(
787 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name),
788 )*
789 _ => None
790 }
791 }
792
793 #[inline]
794 pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option<u32> {
795 match (bank, pin) {
796 $(
797 (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name),
798 )*
799 _ => None
800 }
801 }
802
803 $(
804 impl_pin!($name, $bank, $pin_number);
805 )*
806 };
807}
808
809macro_rules! impl_pin {
810 ($name: ident, $bank: ident, $pin_num: expr) => {
811 impl crate::gpio::Pin for crate::peripherals::$name {}
812 impl crate::gpio::SealedPin for crate::peripherals::$name {
813 #[inline]
814 fn pin_number(&self) -> u8 {
815 $pin_num
816 }
817
818 #[inline]
819 fn _bank(&self) -> crate::gpio::Bank {
820 crate::gpio::Bank::$bank
821 }
822 }
823
824 impl From<peripherals::$name> for crate::gpio::AnyPin {
825 fn from(val: peripherals::$name) -> Self {
826 use crate::gpio::SealedPin;
827
828 Self {
829 pin_number: val.pin_number(),
830 bank: val._bank(),
831 }
832 }
833 }
834 };
835}
836
837pub(crate) fn init() {
838 #[cfg(feature = "rt")]
839 unsafe {
840 use embassy_hal_internal::interrupt::InterruptExt;
841
842 pac::Interrupt::GPIO1_COMBINED_0_15.enable();
843 pac::Interrupt::GPIO1_COMBINED_16_31.enable();
844 pac::Interrupt::GPIO2_COMBINED_0_15.enable();
845 pac::Interrupt::GPIO5_COMBINED_0_15.enable();
846 }
847}
848
849/// IRQ handler for GPIO pins.
850///
851/// If `high_bits` is false, then the interrupt is for pins 0 through 15. If true, then the interrupt
852/// is for pins 16 through 31
853#[cfg(feature = "rt")]
854fn irq_handler(block: Gpio, wakers: &[AtomicWaker; 32], high_bits: bool) {
855 use crate::BitIter;
856
857 let isr = block.isr().read().0;
858 let imr = block.imr().read().0;
859 let mask = if high_bits { 0xFFFF_0000 } else { 0x0000_FFFF };
860 let bits = isr & imr & mask;
861
862 for bit in BitIter(bits) {
863 wakers[bit as usize].wake();
864
865 // Disable further interrupts for this pin. The input future will check ISR (which is kept
866 // until reset).
867 block.imr().modify(|w| {
868 w.set_imr(bit as usize, false);
869 });
870 }
871}
872
873#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
874#[interrupt]
875fn GPIO1_COMBINED_0_15() {
876 irq_handler(pac::GPIO1, &GPIO1_WAKERS, false);
877}
878
879#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
880#[interrupt]
881fn GPIO1_COMBINED_16_31() {
882 irq_handler(pac::GPIO1, &GPIO1_WAKERS, true);
883}
884
885#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
886#[interrupt]
887fn GPIO2_COMBINED_0_15() {
888 irq_handler(pac::GPIO2, &GPIO2_WAKERS, false);
889}
890
891#[cfg(all(feature = "mimxrt1011", feature = "rt"))]
892#[interrupt]
893fn GPIO5_COMBINED_0_15() {
894 irq_handler(pac::GPIO5, &GPIO5_WAKERS, false);
895}
diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs
index 1abaca708..a715770c4 100644
--- a/embassy-nxp/src/lib.rs
+++ b/embassy-nxp/src/lib.rs
@@ -1,12 +1,19 @@
1#![no_std] 1#![no_std]
2 2
3pub mod fmt; 3// This mod MUST go first, so that the others see its macros.
4pub(crate) mod fmt;
5
4pub mod gpio; 6pub mod gpio;
5#[cfg(feature = "lpc55")] 7#[cfg(feature = "lpc55")]
6pub mod pint; 8pub mod pint;
7 9
10#[cfg(feature = "_time_driver")]
11#[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")]
12mod time_driver;
13
8// This mod MUST go last, so that it sees all the `impl_foo!` macros 14// This mod MUST go last, so that it sees all the `impl_foo!` macros
9#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")] 15#[cfg_attr(feature = "lpc55", path = "chips/lpc55.rs")]
16#[cfg_attr(feature = "mimxrt1011", path = "chips/mimxrt1011.rs")]
10mod chip; 17mod chip;
11 18
12#[cfg(feature = "unstable-pac")] 19#[cfg(feature = "unstable-pac")]
@@ -22,13 +29,66 @@ pub use embassy_hal_internal::{Peri, PeripheralType};
22/// 29///
23/// This should only be called once and at startup, otherwise it panics. 30/// This should only be called once and at startup, otherwise it panics.
24pub fn init(_config: config::Config) -> Peripherals { 31pub fn init(_config: config::Config) -> Peripherals {
25 #[cfg(feature = "lpc55")] 32 // Do this first, so that it panics if user is calling `init` a second time
33 // before doing anything important.
34 let peripherals = Peripherals::take();
35
36 #[cfg(feature = "mimxrt1011")]
26 { 37 {
27 gpio::init(); 38 // The RT1010 Reference manual states that core clock root must be switched before
28 pint::init(); 39 // reprogramming PLL2.
40 pac::CCM.cbcdr().modify(|w| {
41 w.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_1);
42 });
43
44 while matches!(
45 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
46 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
47 ) {}
48
49 info!("Core clock root switched");
50
51 // 480 * 18 / 24 = 360
52 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd2_frac(12));
53
54 //480*18/24(pfd0)/4
55 pac::CCM_ANALOG.pfd_480().modify(|x| x.set_pfd0_frac(24));
56 pac::CCM.cscmr1().modify(|x| x.set_flexspi_podf(3.into()));
57
58 // CPU Core
59 pac::CCM_ANALOG.pfd_528().modify(|x| x.set_pfd3_frac(18));
60 cortex_m::asm::delay(500_000);
61
62 // Clock core clock with PLL 2.
63 pac::CCM
64 .cbcdr()
65 .modify(|x| x.set_periph_clk_sel(pac::ccm::vals::PeriphClkSel::PERIPH_CLK_SEL_0)); // false
66
67 while matches!(
68 pac::CCM.cdhipr().read().periph_clk_sel_busy(),
69 pac::ccm::vals::PeriphClkSelBusy::PERIPH_CLK_SEL_BUSY_1
70 ) {}
71
72 pac::CCM
73 .cbcmr()
74 .write(|v| v.set_pre_periph_clk_sel(pac::ccm::vals::PrePeriphClkSel::PRE_PERIPH_CLK_SEL_0));
75
76 // TODO: Some for USB PLLs
77
78 // DCDC clock?
79 pac::CCM.ccgr6().modify(|v| v.set_cg0(1));
29 } 80 }
30 81
31 crate::Peripherals::take() 82 #[cfg(any(feature = "lpc55", rt1xxx))]
83 gpio::init();
84
85 #[cfg(feature = "lpc55")]
86 pint::init();
87
88 #[cfg(feature = "_time_driver")]
89 time_driver::init();
90
91 peripherals
32} 92}
33 93
34/// HAL configuration for the NXP board. 94/// HAL configuration for the NXP board.
@@ -36,3 +96,20 @@ pub mod config {
36 #[derive(Default)] 96 #[derive(Default)]
37 pub struct Config {} 97 pub struct Config {}
38} 98}
99
100#[allow(unused)]
101struct BitIter(u32);
102
103impl Iterator for BitIter {
104 type Item = u32;
105
106 fn next(&mut self) -> Option<Self::Item> {
107 match self.0.trailing_zeros() {
108 32 => None,
109 b => {
110 self.0 &= !(1 << b);
111 Some(b)
112 }
113 }
114 }
115}
diff --git a/embassy-nxp/src/time_driver/pit.rs b/embassy-nxp/src/time_driver/pit.rs
new file mode 100644
index 000000000..985e5e815
--- /dev/null
+++ b/embassy-nxp/src/time_driver/pit.rs
@@ -0,0 +1,187 @@
1//! Time driver using Periodic Interrupt Timer (PIT)
2//!
3//! This driver is used with the iMXRT1xxx parts.
4//!
5//! The PIT is run in lifetime mode. Timer 1 is chained to timer 0 to provide a free-running 64-bit timer.
6//! The 64-bit timer is used to track how many ticks since boot.
7//!
8//! Timer 2 counts how many ticks there are within the current u32::MAX tick period. Timer 2 is restarted when
9//! a new alarm is set (or every u32::MAX ticks). One caveat is that an alarm could be a few ticks late due to
10//! restart. However the Cortex-M7 cores run at 500 MHz easily and the PIT will generally run at 1 MHz or lower.
11//! Along with the fact that scheduling an alarm takes a critical section worst case an alarm may be a few
12//! microseconds late.
13//!
14//! All PIT timers are clocked in lockstep, so the late start will not cause the now() count to drift.
15
16use core::cell::{Cell, RefCell};
17use core::task::Waker;
18
19use critical_section::{CriticalSection, Mutex};
20use embassy_hal_internal::interrupt::InterruptExt;
21use embassy_time_driver::Driver as _;
22use embassy_time_queue_utils::Queue;
23
24use crate::pac::{self, interrupt};
25
26struct Driver {
27 alarm: Mutex<Cell<u64>>,
28 queue: Mutex<RefCell<Queue>>,
29}
30
31impl embassy_time_driver::Driver for Driver {
32 fn now(&self) -> u64 {
33 loop {
34 // Even though reading LTMR64H will latch LTMR64L if another thread preempts between any of the
35 // three reads and calls now() then the value in LTMR64L will be wrong when execution returns to
36 // thread which was preempted.
37 let hi = pac::PIT.ltmr64h().read().lth();
38 let lo = pac::PIT.ltmr64l().read().ltl();
39 let hi2 = pac::PIT.ltmr64h().read().lth();
40
41 if hi == hi2 {
42 // PIT timers always count down.
43 return u64::MAX - ((hi as u64) << 32 | (lo as u64));
44 }
45 }
46 }
47
48 fn schedule_wake(&self, at: u64, waker: &Waker) {
49 critical_section::with(|cs| {
50 let mut queue = self.queue.borrow(cs).borrow_mut();
51
52 if queue.schedule_wake(at, waker) {
53 let mut next = queue.next_expiration(self.now());
54
55 while !self.set_alarm(cs, next) {
56 next = queue.next_expiration(self.now());
57 }
58 }
59 })
60 }
61}
62
63impl Driver {
64 fn init(&'static self) {
65 // Disable PIT clock during mux configuration.
66 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b00));
67
68 // TODO: This forces the PIT to be driven by the oscillator. However that isn't the only option as you
69 // could divide the clock root by up to 64.
70 pac::CCM.cscmr1().modify(|r| {
71 // 1 MHz
72 r.set_perclk_podf(pac::ccm::vals::PerclkPodf::DIVIDE_24);
73 r.set_perclk_clk_sel(pac::ccm::vals::PerclkClkSel::PERCLK_CLK_SEL_1);
74 });
75
76 pac::CCM.ccgr1().modify(|r| r.set_cg6(0b11));
77
78 // Disable clock during init.
79 //
80 // It is important that the PIT clock is prepared to not exceed limit (50 MHz on RT1011), or else
81 // you will need to recover the device with boot mode switches when using any PIT registers.
82 pac::PIT.mcr().modify(|w| {
83 w.set_mdis(true);
84 });
85
86 pac::PIT.timer(0).ldval().write_value(u32::MAX);
87 pac::PIT.timer(1).ldval().write_value(u32::MAX);
88 pac::PIT.timer(2).ldval().write_value(0);
89 pac::PIT.timer(3).ldval().write_value(0);
90
91 pac::PIT.timer(1).tctrl().write(|w| {
92 // In lifetime mode, timer 1 is chained to timer 0 to form a 64-bit timer.
93 w.set_chn(true);
94 w.set_ten(true);
95 w.set_tie(false);
96 });
97
98 pac::PIT.timer(0).tctrl().write(|w| {
99 w.set_chn(false);
100 w.set_ten(true);
101 w.set_tie(false);
102 });
103
104 pac::PIT.timer(2).tctrl().write(|w| {
105 w.set_tie(true);
106 });
107
108 unsafe { interrupt::PIT.enable() };
109
110 pac::PIT.mcr().write(|w| {
111 w.set_mdis(false);
112 });
113 }
114
115 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
116 let alarm = self.alarm.borrow(cs);
117 alarm.set(timestamp);
118
119 let timer = pac::PIT.timer(2);
120 let now = self.now();
121
122 if timestamp <= now {
123 alarm.set(u64::MAX);
124
125 return false;
126 }
127
128 timer.tctrl().modify(|x| x.set_ten(false));
129 timer.tflg().modify(|x| x.set_tif(true));
130
131 // If the next alarm happens in more than u32::MAX cycles then the alarm will be restarted later.
132 timer.ldval().write_value((timestamp - now) as u32);
133 timer.tctrl().modify(|x| x.set_ten(true));
134
135 true
136 }
137
138 fn trigger_alarm(&self, cs: CriticalSection) {
139 let mut next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
140
141 while !self.set_alarm(cs, next) {
142 next = self.queue.borrow_ref_mut(cs).next_expiration(self.now());
143 }
144 }
145
146 fn on_interrupt(&self) {
147 critical_section::with(|cs| {
148 let timer = pac::PIT.timer(2);
149 let alarm = self.alarm.borrow(cs);
150 let interrupted = timer.tflg().read().tif();
151 timer.tflg().write(|r| r.set_tif(true));
152
153 if interrupted {
154 // A new load value will not apply until the next timer expiration.
155 //
156 // The expiry may be up to u32::MAX cycles away, so the timer must be restarted.
157 timer.tctrl().modify(|r| r.set_ten(false));
158
159 let now = self.now();
160 let timestamp = alarm.get();
161
162 if timestamp <= now {
163 self.trigger_alarm(cs);
164 } else {
165 // The alarm is not ready. Wait for u32::MAX cycles and check again or set the next alarm.
166 timer.ldval().write_value((timestamp - now) as u32);
167 timer.tctrl().modify(|r| r.set_ten(true));
168 }
169 }
170 });
171 }
172}
173
174embassy_time_driver::time_driver_impl!(static DRIVER: Driver = Driver {
175 alarm: Mutex::new(Cell::new(0)),
176 queue: Mutex::new(RefCell::new(Queue::new()))
177});
178
179pub(crate) fn init() {
180 DRIVER.init();
181}
182
183#[cfg(feature = "rt")]
184#[interrupt]
185fn PIT() {
186 DRIVER.on_interrupt();
187}
diff --git a/examples/mimxrt1011/.cargo/config.toml b/examples/mimxrt1011/.cargo/config.toml
new file mode 100644
index 000000000..12f4b27b2
--- /dev/null
+++ b/examples/mimxrt1011/.cargo/config.toml
@@ -0,0 +1,8 @@
1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs run --chip MIMXRT1010'
3
4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M7
6
7[env]
8DEFMT_LOG = "trace"
diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml
new file mode 100644
index 000000000..cf4e4c163
--- /dev/null
+++ b/examples/mimxrt1011/Cargo.toml
@@ -0,0 +1,29 @@
1[package]
2name = "embassy-imxrt1011-examples"
3version = "0.1.0"
4edition = "2021"
5license = "MIT or Apache-2.0"
6
7[dependencies]
8cortex-m = { version = "0.7.7", features = ["inline-asm", "critical-section-single-core"] }
9cortex-m-rt = "0.7.3"
10defmt = "1.0.1"
11defmt-rtt = "1.0.0"
12
13embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
14embassy-futures = { version = "0.1.1", path = "../../embassy-futures" }
15embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["defmt", "mimxrt1011", "unstable-pac", "time-driver-pit"] }
16embassy-time = { version = "0.4", path = "../../embassy-time", features = ["defmt", ] } # "defmt-timestamp-uptime" # RT1011 hard faults currently with this enabled.
17embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
18embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
19embedded-hal-async = "1.0.0"
20
21imxrt-boot-gen = { version = "0.3.4", features = ["imxrt1010"] }
22panic-probe = { version = "1.0.0", features = ["print-defmt"] }
23panic-semihosting = "0.6.0"
24
25[build-dependencies]
26imxrt-rt = { version = "0.1.7", features = ["device"] }
27
28[profile.release]
29debug = 2
diff --git a/examples/mimxrt1011/build.rs b/examples/mimxrt1011/build.rs
new file mode 100644
index 000000000..99e172aba
--- /dev/null
+++ b/examples/mimxrt1011/build.rs
@@ -0,0 +1,14 @@
1use imxrt_rt::{Family, RuntimeBuilder};
2
3fn main() {
4 // The IMXRT1010-EVK technically has 128M of flash, but we only ever use 8MB so that the examples
5 // will build fine on the Adafruit Metro M7 boards.
6 RuntimeBuilder::from_flexspi(Family::Imxrt1010, 8 * 1024 * 1024)
7 .build()
8 .unwrap();
9
10 println!("cargo:rustc-link-arg-bins=--nmagic");
11 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
12 // Not link.x, as imxrt-rt needs to do some special things
13 println!("cargo:rustc-link-arg-bins=-Timxrt-link.x");
14}
diff --git a/examples/mimxrt1011/src/bin/blinky.rs b/examples/mimxrt1011/src/bin/blinky.rs
new file mode 100644
index 000000000..a5d5de6b3
--- /dev/null
+++ b/examples/mimxrt1011/src/bin/blinky.rs
@@ -0,0 +1,48 @@
1//! This example works on the following boards:
2//! - IMXRT1010-EVK
3//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button
4//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)
5//!
6//! Although beware you will need to change the GPIO pins being used (scroll down).
7
8#![no_std]
9#![no_main]
10
11use defmt::info;
12use embassy_executor::Spawner;
13use embassy_nxp::gpio::{Level, Output};
14use embassy_time::Timer;
15// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.
16use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) -> ! {
20 let p = embassy_nxp::init(Default::default());
21 info!("Hello world!");
22
23 /* Pick the pins to use depending on your board. */
24
25 // IMXRT1010-EVK
26 //
27 // LED (D25)
28 let led = p.GPIO_11;
29
30 // Adafruit Metro M7 (both microSD and AirLift variants)
31 //
32 // The LED is connected to D13 on the board.
33 // let led = p.GPIO_03;
34
35 // Makerdiary iMX RT1011 Nano Kit
36 //
37 // LED0
38 // let led = p.GPIO_SD_04;
39
40 let mut led = Output::new(led, Level::Low);
41
42 loop {
43 Timer::after_millis(500).await;
44
45 info!("Toggle");
46 led.toggle();
47 }
48}
diff --git a/examples/mimxrt1011/src/bin/button.rs b/examples/mimxrt1011/src/bin/button.rs
new file mode 100644
index 000000000..e63d7171d
--- /dev/null
+++ b/examples/mimxrt1011/src/bin/button.rs
@@ -0,0 +1,62 @@
1//! This example works on the following boards:
2//! - IMXRT1010-EVK
3//! - Adafruit Metro M7 (with microSD or with AirLift), requires an external button
4//! - Makerdiary iMX RT1011 Nano Kit (TODO: currently untested, please change this)
5//!
6//! Although beware you will need to change the GPIO pins being used (scroll down).
7
8#![no_std]
9#![no_main]
10
11use defmt::info;
12use embassy_executor::Spawner;
13use embassy_nxp::gpio::{Input, Level, Output, Pull};
14// Must include `embassy_imxrt1011_examples` to ensure the FCB gets linked.
15use {defmt_rtt as _, embassy_imxrt1011_examples as _, panic_probe as _};
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) -> ! {
19 let p = embassy_nxp::init(Default::default());
20 info!("Hello world!");
21
22 /* Pick the pins to use depending on your board. */
23
24 // IMXRT1010-EVK
25 //
26 // LED (D25) and user button (SW4)
27 let (led, button) = (p.GPIO_11, p.GPIO_SD_05);
28
29 // Adafruit Metro M7 (both microSD and AirLift variants)
30 //
31 // The LED is connected to D13 on the board.
32 //
33 // In particular the Metro M7 has no board user buttons, so you will need to connect a button.
34 // Any other GPIO pin can be used. GPIO_04 is used for example since it is on pin D12.
35 // let (led, button) = (p.GPIO_03, p.GPIO_04);
36
37 // Makerdiary iMX RT1011 Nano Kit
38 //
39 // LED0 and user button.
40 // let (led, button) = (p.GPIO_SD_04, p.GPIO_SD_03);
41
42 let mut button = Input::new(button, Pull::Up100K);
43 let mut led = Output::new(led, Level::Low);
44 led.set_high();
45
46 loop {
47 button.wait_for_falling_edge().await;
48
49 info!("Toggled");
50 led.toggle();
51
52 // The RT1010EVK has a 100 nF debouncing capacitor which results in false positive events
53 // when listening for a falling edge in a loop, wait for the rising edge and then wait for
54 // stabilization.
55 button.wait_for_rising_edge().await;
56
57 // Stabilization.
58 for _ in 0..100_000 {
59 cortex_m::asm::nop();
60 }
61 }
62}
diff --git a/examples/mimxrt1011/src/lib.rs b/examples/mimxrt1011/src/lib.rs
new file mode 100644
index 000000000..f0391ef57
--- /dev/null
+++ b/examples/mimxrt1011/src/lib.rs
@@ -0,0 +1,75 @@
1//! FlexSPI configuration block (FCB) for iMXRT1011 boards.
2//!
3//! This is a generic FCB that should work with most QSPI flash.
4
5#![no_std]
6
7use imxrt_boot_gen::flexspi;
8use imxrt_boot_gen::flexspi::opcodes::sdr::*;
9use imxrt_boot_gen::flexspi::{
10 ColumnAddressWidth, Command, DeviceModeConfiguration, FlashPadType, Instr, LookupTable, Pads,
11 ReadSampleClockSource, Sequence, SequenceBuilder, SerialClockFrequency, SerialFlashRegion,
12 WaitTimeConfigurationCommands,
13};
14use imxrt_boot_gen::serial_flash::nor;
15
16/// While the IMXRT1010-EVK and Makerdiary iMX RT1011 Nano Kit have 128MBit of flash we limit to 64Mbit
17/// to allow the Metro M7 boards to use the same FCB configuration.
18const DENSITY_BITS: u32 = 64 * 1024 * 1024;
19const DENSITY_BYTES: u32 = DENSITY_BITS / 8;
20
21const SEQ_READ: Sequence = SequenceBuilder::new()
22 .instr(Instr::new(CMD, Pads::One, 0xEB))
23 .instr(Instr::new(RADDR, Pads::Four, 0x18))
24 .instr(Instr::new(DUMMY, Pads::Four, 0x06))
25 .instr(Instr::new(READ, Pads::Four, 0x04))
26 .build();
27
28const SEQ_READ_STATUS: Sequence = SequenceBuilder::new()
29 .instr(Instr::new(CMD, Pads::One, 0x05))
30 .instr(Instr::new(READ, Pads::One, 0x01))
31 .build();
32
33const SEQ_WRITE_ENABLE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build();
34
35const SEQ_ERASE_SECTOR: Sequence = SequenceBuilder::new()
36 .instr(Instr::new(CMD, Pads::One, 0x20))
37 .instr(Instr::new(RADDR, Pads::One, 0x18))
38 .build();
39
40const SEQ_PAGE_PROGRAM: Sequence = SequenceBuilder::new()
41 .instr(Instr::new(CMD, Pads::One, 0x02))
42 .instr(Instr::new(RADDR, Pads::One, 0x18))
43 .instr(Instr::new(WRITE, Pads::One, 0x04))
44 .build();
45
46const SEQ_CHIP_ERASE: Sequence = SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x60)).build();
47
48const LUT: LookupTable = LookupTable::new()
49 .command(Command::Read, SEQ_READ)
50 .command(Command::ReadStatus, SEQ_READ_STATUS)
51 .command(Command::WriteEnable, SEQ_WRITE_ENABLE)
52 .command(Command::EraseSector, SEQ_ERASE_SECTOR)
53 .command(Command::PageProgram, SEQ_PAGE_PROGRAM)
54 .command(Command::ChipErase, SEQ_CHIP_ERASE);
55
56const COMMON_CONFIGURATION_BLOCK: flexspi::ConfigurationBlock = flexspi::ConfigurationBlock::new(LUT)
57 .read_sample_clk_src(ReadSampleClockSource::LoopbackFromDQSPad)
58 .cs_hold_time(0x03)
59 .cs_setup_time(0x03)
60 .column_address_width(ColumnAddressWidth::OtherDevices)
61 .device_mode_configuration(DeviceModeConfiguration::Disabled)
62 .wait_time_cfg_commands(WaitTimeConfigurationCommands::disable())
63 .flash_size(SerialFlashRegion::A1, DENSITY_BYTES)
64 .serial_clk_freq(SerialClockFrequency::MHz120)
65 .serial_flash_pad_type(FlashPadType::Quad);
66
67pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock =
68 nor::ConfigurationBlock::new(COMMON_CONFIGURATION_BLOCK)
69 .page_size(256)
70 .sector_size(4096)
71 .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30);
72
73#[unsafe(no_mangle)]
74#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")]
75pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK;