aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-07-09 02:42:38 +0200
committerGitHub <[email protected]>2022-07-09 02:42:38 +0200
commit921bc4d97ba290045e026389d7ab68399bb1ceab (patch)
treee325a0d1151022e14563311c708366d709253375
parentd2a622b3d0ed4dfd2932f9059a70dc82a64d761e (diff)
parentccf57cfab63d9562d617a667ef6dfa2c9edd572f (diff)
Merge pull request #852 from embassy-rs/rp-flex
rp: add Flex gpio
-rwxr-xr-xci.sh1
-rw-r--r--embassy-rp/src/gpio.rs376
-rw-r--r--tests/rp/.cargo/config.toml20
-rw-r--r--tests/rp/Cargo.toml48
-rw-r--r--tests/rp/build.rs16
-rw-r--r--tests/rp/link_ram.x255
-rw-r--r--tests/rp/src/bin/gpio.rs192
7 files changed, 804 insertions, 104 deletions
diff --git a/ci.sh b/ci.sh
index 6d7c9b046..315b60ef7 100755
--- a/ci.sh
+++ b/ci.sh
@@ -104,6 +104,7 @@ cargo batch \
104 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ 104 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \
105 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ 105 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \
106 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ 106 --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \
107 --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
107 108
108 109
109function run_elf { 110function run_elf {
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index ae771e849..131330e79 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -30,156 +30,147 @@ pub enum Bank {
30} 30}
31 31
32pub struct Input<'d, T: Pin> { 32pub struct Input<'d, T: Pin> {
33 pin: T, 33 pin: Flex<'d, T>,
34 phantom: PhantomData<&'d mut T>,
35} 34}
36 35
37impl<'d, T: Pin> Input<'d, T> { 36impl<'d, T: Pin> Input<'d, T> {
38 pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { 37 pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self {
39 unborrow!(pin); 38 let mut pin = Flex::new(pin);
40 39 pin.set_as_input();
41 unsafe { 40 pin.set_pull(pull);
42 pin.pad_ctrl().write(|w| { 41 Self { pin }
43 w.set_ie(true);
44 match pull {
45 Pull::Up => w.set_pue(true),
46 Pull::Down => w.set_pde(true),
47 Pull::None => {}
48 }
49 });
50
51 // disable output in SIO, to use it as input
52 pin.sio_oe().value_clr().write_value(1 << pin.pin());
53
54 pin.io().ctrl().write(|w| {
55 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0);
56 });
57 }
58
59 Self {
60 pin,
61 phantom: PhantomData,
62 }
63 } 42 }
64 43
65 pub fn is_high(&self) -> bool { 44 pub fn is_high(&self) -> bool {
66 !self.is_low() 45 self.pin.is_high()
67 } 46 }
68 47
69 pub fn is_low(&self) -> bool { 48 pub fn is_low(&self) -> bool {
70 let val = 1 << self.pin.pin(); 49 self.pin.is_low()
71 unsafe { self.pin.sio_in().read() & val == 0 }
72 }
73}
74
75impl<'d, T: Pin> Drop for Input<'d, T> {
76 fn drop(&mut self) {
77 // todo
78 } 50 }
79} 51}
80 52
81pub struct Output<'d, T: Pin> { 53pub struct Output<'d, T: Pin> {
82 pin: T, 54 pin: Flex<'d, T>,
83 phantom: PhantomData<&'d mut T>,
84} 55}
85 56
86impl<'d, T: Pin> Output<'d, T> { 57impl<'d, T: Pin> Output<'d, T> {
58 #[inline]
87 pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { 59 pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self {
88 unborrow!(pin); 60 let mut pin = Flex::new(pin);
89 61 match initial_output {
90 unsafe { 62 Level::High => pin.set_high(),
91 match initial_output { 63 Level::Low => pin.set_low(),
92 Level::High => pin.sio_out().value_set().write_value(1 << pin.pin()),
93 Level::Low => pin.sio_out().value_clr().write_value(1 << pin.pin()),
94 }
95 pin.sio_oe().value_set().write_value(1 << pin.pin());
96
97 pin.io().ctrl().write(|w| {
98 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0);
99 });
100 } 64 }
101 65
102 Self { 66 pin.set_as_output();
103 pin, 67 Self { pin }
104 phantom: PhantomData,
105 }
106 } 68 }
107 69
108 /// Set the output as high. 70 /// Set the output as high.
71 #[inline]
109 pub fn set_high(&mut self) { 72 pub fn set_high(&mut self) {
110 let val = 1 << self.pin.pin(); 73 self.pin.set_high()
111 unsafe { self.pin.sio_out().value_set().write_value(val) };
112 } 74 }
113 75
114 /// Set the output as low. 76 /// Set the output as low.
77 #[inline]
115 pub fn set_low(&mut self) { 78 pub fn set_low(&mut self) {
116 let val = 1 << self.pin.pin(); 79 self.pin.set_low()
117 unsafe { self.pin.sio_out().value_clr().write_value(val) };
118 } 80 }
119 81
120 /// Is the output pin set as high? 82 /// Is the output pin set as high?
83 #[inline]
121 pub fn is_set_high(&self) -> bool { 84 pub fn is_set_high(&self) -> bool {
122 !self.is_set_low() 85 self.pin.is_set_high()
123 } 86 }
124 87
125 /// Is the output pin set as low? 88 /// Is the output pin set as low?
89 #[inline]
126 pub fn is_set_low(&self) -> bool { 90 pub fn is_set_low(&self) -> bool {
127 // Reading from SIO: GPIO_OUT gives the last value written. 91 self.pin.is_set_low()
128 let val = 1 << self.pin.pin();
129 unsafe { (self.pin.sio_out().value().read() & val) == 0 }
130 } 92 }
131 93
132 /// Toggle pin output 94 /// Toggle pin output
133 #[inline] 95 #[inline]
134 pub fn toggle(&mut self) { 96 pub fn toggle(&mut self) {
135 let val = 1 << self.pin.pin(); 97 self.pin.toggle()
136 unsafe {
137 self.pin.sio_out().value_xor().write_value(val);
138 }
139 } 98 }
140} 99}
141 100
142impl<'d, T: Pin> Drop for Output<'d, T> { 101/// GPIO output open-drain.
143 fn drop(&mut self) { 102pub struct OutputOpenDrain<'d, T: Pin> {
144 let val = 1 << self.pin.pin(); 103 pin: Flex<'d, T>,
145 unsafe { 104}
146 self.pin.sio_out().value_clr().write_value(val); 105
147 self.pin.sio_oe().value_clr().write_value(val); 106impl<'d, T: Pin> OutputOpenDrain<'d, T> {
148 self.pin.io().ctrl().write(|w| { 107 #[inline]
149 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); 108 pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self {
150 }); 109 let mut pin = Flex::new(pin);
151 }; 110 pin.set_low();
111 match initial_output {
112 Level::High => pin.set_as_input(),
113 Level::Low => pin.set_as_output(),
114 }
115 Self { pin }
116 }
117
118 /// Set the output as high.
119 #[inline]
120 pub fn set_high(&mut self) {
121 // For Open Drain High, disable the output pin.
122 self.pin.set_as_input()
123 }
124
125 /// Set the output as low.
126 #[inline]
127 pub fn set_low(&mut self) {
128 // For Open Drain Low, enable the output pin.
129 self.pin.set_as_output()
130 }
131
132 /// Is the output level high?
133 #[inline]
134 pub fn is_set_high(&self) -> bool {
135 !self.is_set_low()
136 }
137
138 /// Is the output level low?
139 #[inline]
140 pub fn is_set_low(&self) -> bool {
141 self.pin.is_set_as_output()
142 }
143
144 /// Toggle pin output
145 #[inline]
146 pub fn toggle(&mut self) {
147 self.pin.toggle_set_as_output()
152 } 148 }
153} 149}
154 150
155/// GPIO output open-drain. 151/// GPIO flexible pin.
156pub struct OutputOpenDrain<'d, T: Pin> { 152///
153/// This pin can be either an input or output pin. The output level register bit will remain
154/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
155/// mode.
156pub struct Flex<'d, T: Pin> {
157 pin: T, 157 pin: T,
158 phantom: PhantomData<&'d mut T>, 158 phantom: PhantomData<&'d mut T>,
159} 159}
160 160
161impl<'d, T: Pin> OutputOpenDrain<'d, T> { 161impl<'d, T: Pin> Flex<'d, T> {
162 #[inline] 162 #[inline]
163 pub fn new(pin: impl Unborrow<Target = T> + 'd, initial_output: Level) -> Self { 163 pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self {
164 unborrow!(pin); 164 unborrow!(pin);
165 165
166 unsafe { 166 unsafe {
167 let val = 1 << pin.pin(); 167 pin.pad_ctrl().write(|w| {
168 w.set_ie(true);
169 });
170
168 pin.io().ctrl().write(|w| { 171 pin.io().ctrl().write(|w| {
169 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); 172 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0);
170 }); 173 });
171 pin.sio_out().value_clr().write_value(val);
172
173 match initial_output {
174 Level::High => {
175 // For Open Drain High, disable the output pin.
176 pin.sio_oe().value_clr().write_value(val);
177 }
178 Level::Low => {
179 // For Open Drain Low, enable the output pin.
180 pin.sio_oe().value_set().write_value(val);
181 }
182 }
183 } 174 }
184 175
185 Self { 176 Self {
@@ -188,29 +179,79 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
188 } 179 }
189 } 180 }
190 181
191 /// Set the output as high.
192 #[inline] 182 #[inline]
193 pub fn set_high(&mut self) { 183 fn bit(&self) -> u32 {
194 // For Open Drain High, disable the output pin. 184 1 << self.pin.pin()
185 }
186
187 /// Set the pin's pull.
188 #[inline]
189 pub fn set_pull(&mut self, pull: Pull) {
195 unsafe { 190 unsafe {
196 self.pin.sio_oe().value_clr().write_value(1 << self.pin.pin()); 191 self.pin.pad_ctrl().write(|w| {
192 w.set_ie(true);
193 match pull {
194 Pull::Up => w.set_pue(true),
195 Pull::Down => w.set_pde(true),
196 Pull::None => {}
197 }
198 });
197 } 199 }
198 } 200 }
199 201
202 /// Put the pin into input mode.
203 ///
204 /// The pull setting is left unchanged.
205 #[inline]
206 pub fn set_as_input(&mut self) {
207 unsafe { self.pin.sio_oe().value_clr().write_value(self.bit()) }
208 }
209
210 /// Put the pin into output mode.
211 ///
212 /// The pin level will be whatever was set before (or low by default). If you want it to begin
213 /// at a specific level, call `set_high`/`set_low` on the pin first.
214 #[inline]
215 pub fn set_as_output(&mut self) {
216 unsafe { self.pin.sio_oe().value_set().write_value(self.bit()) }
217 }
218
219 #[inline]
220 fn is_set_as_output(&self) -> bool {
221 unsafe { (self.pin.sio_oe().value().read() & self.bit()) != 0 }
222 }
223
224 #[inline]
225 pub fn toggle_set_as_output(&mut self) {
226 unsafe { self.pin.sio_oe().value_xor().write_value(self.bit()) }
227 }
228
229 #[inline]
230 pub fn is_high(&self) -> bool {
231 !self.is_low()
232 }
233
234 #[inline]
235 pub fn is_low(&self) -> bool {
236 unsafe { self.pin.sio_in().read() & self.bit() == 0 }
237 }
238
239 /// Set the output as high.
240 #[inline]
241 pub fn set_high(&mut self) {
242 unsafe { self.pin.sio_out().value_set().write_value(self.bit()) }
243 }
244
200 /// Set the output as low. 245 /// Set the output as low.
201 #[inline] 246 #[inline]
202 pub fn set_low(&mut self) { 247 pub fn set_low(&mut self) {
203 // For Open Drain Low, enable the output pin. 248 unsafe { self.pin.sio_out().value_clr().write_value(self.bit()) }
204 unsafe {
205 self.pin.sio_oe().value_set().write_value(1 << self.pin.pin());
206 }
207 } 249 }
208 250
209 /// Is the output level high? 251 /// Is the output level high?
210 #[inline] 252 #[inline]
211 pub fn is_set_high(&self) -> bool { 253 pub fn is_set_high(&self) -> bool {
212 let val = 1 << self.pin.pin(); 254 unsafe { (self.pin.sio_out().value().read() & self.bit()) == 0 }
213 unsafe { (self.pin.sio_oe().value().read() & val) == 0 }
214 } 255 }
215 256
216 /// Is the output level low? 257 /// Is the output level low?
@@ -222,9 +263,18 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> {
222 /// Toggle pin output 263 /// Toggle pin output
223 #[inline] 264 #[inline]
224 pub fn toggle(&mut self) { 265 pub fn toggle(&mut self) {
225 let val = 1 << self.pin.pin(); 266 unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) }
267 }
268}
269
270impl<'d, T: Pin> Drop for Flex<'d, T> {
271 #[inline]
272 fn drop(&mut self) {
226 unsafe { 273 unsafe {
227 self.pin.sio_out().value_xor().write_value(val); 274 self.pin.pad_ctrl().write(|_| {});
275 self.pin.io().ctrl().write(|w| {
276 w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0);
277 });
228 } 278 }
229 } 279 }
230} 280}
@@ -428,6 +478,48 @@ mod eh02 {
428 Ok(self.toggle()) 478 Ok(self.toggle())
429 } 479 }
430 } 480 }
481
482 impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> {
483 type Error = Infallible;
484
485 fn is_high(&self) -> Result<bool, Self::Error> {
486 Ok(self.is_high())
487 }
488
489 fn is_low(&self) -> Result<bool, Self::Error> {
490 Ok(self.is_low())
491 }
492 }
493
494 impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> {
495 type Error = Infallible;
496
497 fn set_high(&mut self) -> Result<(), Self::Error> {
498 Ok(self.set_high())
499 }
500
501 fn set_low(&mut self) -> Result<(), Self::Error> {
502 Ok(self.set_low())
503 }
504 }
505
506 impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> {
507 fn is_set_high(&self) -> Result<bool, Self::Error> {
508 Ok(self.is_set_high())
509 }
510
511 fn is_set_low(&self) -> Result<bool, Self::Error> {
512 Ok(self.is_set_low())
513 }
514 }
515
516 impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> {
517 type Error = Infallible;
518 #[inline]
519 fn toggle(&mut self) -> Result<(), Self::Error> {
520 Ok(self.toggle())
521 }
522 }
431} 523}
432 524
433#[cfg(feature = "unstable-traits")] 525#[cfg(feature = "unstable-traits")]
@@ -471,4 +563,80 @@ mod eh1 {
471 Ok(self.is_set_low()) 563 Ok(self.is_set_low())
472 } 564 }
473 } 565 }
566
567 impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> {
568 fn toggle(&mut self) -> Result<(), Self::Error> {
569 Ok(self.toggle())
570 }
571 }
572
573 impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d, T> {
574 type Error = Infallible;
575 }
576
577 impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> {
578 fn set_high(&mut self) -> Result<(), Self::Error> {
579 Ok(self.set_high())
580 }
581
582 fn set_low(&mut self) -> Result<(), Self::Error> {
583 Ok(self.set_low())
584 }
585 }
586
587 impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> {
588 fn is_set_high(&self) -> Result<bool, Self::Error> {
589 Ok(self.is_set_high())
590 }
591
592 fn is_set_low(&self) -> Result<bool, Self::Error> {
593 Ok(self.is_set_low())
594 }
595 }
596
597 impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> {
598 fn toggle(&mut self) -> Result<(), Self::Error> {
599 Ok(self.toggle())
600 }
601 }
602
603 impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> {
604 type Error = Infallible;
605 }
606
607 impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
608 fn is_high(&self) -> Result<bool, Self::Error> {
609 Ok(self.is_high())
610 }
611
612 fn is_low(&self) -> Result<bool, Self::Error> {
613 Ok(self.is_low())
614 }
615 }
616
617 impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
618 fn set_high(&mut self) -> Result<(), Self::Error> {
619 Ok(self.set_high())
620 }
621
622 fn set_low(&mut self) -> Result<(), Self::Error> {
623 Ok(self.set_low())
624 }
625 }
626
627 impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
628 fn is_set_high(&self) -> Result<bool, Self::Error> {
629 Ok(self.is_set_high())
630 }
631
632 fn is_set_low(&self) -> Result<bool, Self::Error> {
633 Ok(self.is_set_low())
634 }
635 }
636
637 impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> {
638 fn toggle(&mut self) -> Result<(), Self::Error> {
639 Ok(self.toggle())
640 }
641 }
474} 642}
diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml
new file mode 100644
index 000000000..0330025e4
--- /dev/null
+++ b/tests/rp/.cargo/config.toml
@@ -0,0 +1,20 @@
1[unstable]
2build-std = ["core"]
3build-std-features = ["panic_immediate_abort"]
4
5[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf"
7runner = "teleprobe local run --chip RP2040 --elf"
8
9rustflags = [
10 # Code-size optimizations.
11 "-Z", "trap-unreachable=no",
12 "-C", "inline-threshold=5",
13 "-C", "no-vectorize-loops",
14]
15
16[build]
17target = "thumbv6m-none-eabi"
18
19[env]
20DEFMT_LOG = "trace"
diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml
new file mode 100644
index 000000000..b3067fffd
--- /dev/null
+++ b/tests/rp/Cargo.toml
@@ -0,0 +1,48 @@
1[package]
2edition = "2021"
3name = "embassy-rp-tests"
4version = "0.1.0"
5
6[dependencies]
7embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] }
8embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] }
9
10defmt = "0.3.0"
11defmt-rtt = "0.3.0"
12
13cortex-m = "0.7.3"
14cortex-m-rt = "0.7.0"
15embedded-hal = "0.2.6"
16embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
17embedded-hal-async = { version = "0.1.0-alpha.1" }
18panic-probe = { version = "0.3.0", features = ["print-defmt"] }
19
20[profile.dev]
21debug = 2
22debug-assertions = true
23opt-level = 's'
24overflow-checks = true
25
26[profile.release]
27codegen-units = 1
28debug = 2
29debug-assertions = false
30incremental = false
31lto = "fat"
32opt-level = 's'
33overflow-checks = false
34
35# do not optimize proc-macro crates = faster builds from scratch
36[profile.dev.build-override]
37codegen-units = 8
38debug = false
39debug-assertions = false
40opt-level = 0
41overflow-checks = false
42
43[profile.release.build-override]
44codegen-units = 8
45debug = false
46debug-assertions = false
47opt-level = 0
48overflow-checks = false
diff --git a/tests/rp/build.rs b/tests/rp/build.rs
new file mode 100644
index 000000000..6f4872249
--- /dev/null
+++ b/tests/rp/build.rs
@@ -0,0 +1,16 @@
1use std::error::Error;
2use std::path::PathBuf;
3use std::{env, fs};
4
5fn main() -> Result<(), Box<dyn Error>> {
6 let out = PathBuf::from(env::var("OUT_DIR").unwrap());
7 fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap();
8 println!("cargo:rustc-link-search={}", out.display());
9 println!("cargo:rerun-if-changed=link_ram.x");
10
11 println!("cargo:rustc-link-arg-bins=--nmagic");
12 println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
13 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
14
15 Ok(())
16}
diff --git a/tests/rp/link_ram.x b/tests/rp/link_ram.x
new file mode 100644
index 000000000..86a11e875
--- /dev/null
+++ b/tests/rp/link_ram.x
@@ -0,0 +1,255 @@
1/* ##### EMBASSY NOTE
2 Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in
3 Adjusted to put everything in RAM
4*/
5
6/* # Developer notes
7
8- Symbols that start with a double underscore (__) are considered "private"
9
10- Symbols that start with a single underscore (_) are considered "semi-public"; they can be
11 overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" {
12 static mut __sbss }`).
13
14- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a
15 symbol if not dropped if it appears in or near the front of the linker arguments and "it's not
16 needed" by any of the preceding objects (linker arguments)
17
18- `PROVIDE` is used to provide default values that can be overridden by a user linker script
19
20- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and*
21 the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization
22 routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see
23 "Address (..) is out of bounds" in the disassembly produced by `objdump`.
24*/
25
26/* Provides information about the memory layout of the device */
27MEMORY {
28 RAM : ORIGIN = 0x20000000, LENGTH = 256K
29}
30
31/* # Entry point = reset vector */
32EXTERN(__RESET_VECTOR);
33EXTERN(Reset);
34ENTRY(Reset);
35
36/* # Exception vectors */
37/* This is effectively weak aliasing at the linker level */
38/* The user can override any of these aliases by defining the corresponding symbol themselves (cf.
39 the `exception!` macro) */
40EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */
41
42EXTERN(DefaultHandler);
43
44PROVIDE(NonMaskableInt = DefaultHandler);
45EXTERN(HardFaultTrampoline);
46PROVIDE(MemoryManagement = DefaultHandler);
47PROVIDE(BusFault = DefaultHandler);
48PROVIDE(UsageFault = DefaultHandler);
49PROVIDE(SecureFault = DefaultHandler);
50PROVIDE(SVCall = DefaultHandler);
51PROVIDE(DebugMonitor = DefaultHandler);
52PROVIDE(PendSV = DefaultHandler);
53PROVIDE(SysTick = DefaultHandler);
54
55PROVIDE(DefaultHandler = DefaultHandler_);
56PROVIDE(HardFault = HardFault_);
57
58/* # Interrupt vectors */
59EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */
60
61/* # Pre-initialization function */
62/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function,
63 then the function this points to will be called before the RAM is initialized. */
64PROVIDE(__pre_init = DefaultPreInit);
65
66/* # Sections */
67SECTIONS
68{
69 PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
70
71 /* ## Sections in RAM */
72 /* ### Vector table */
73 .vector_table ORIGIN(RAM) :
74 {
75 /* Initial Stack Pointer (SP) value */
76 LONG(_stack_start);
77
78 /* Reset vector */
79 KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */
80 __reset_vector = .;
81
82 /* Exceptions */
83 KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */
84 __eexceptions = .;
85
86 /* Device specific interrupts */
87 KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */
88 } > RAM
89
90 PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table));
91
92 /* ### .text */
93 .text _stext :
94 {
95 __stext = .;
96 *(.Reset);
97
98 *(.text .text.*);
99
100 /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`,
101 so must be placed close to it. */
102 *(.HardFaultTrampoline);
103 *(.HardFault.*);
104
105 . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */
106 __etext = .;
107 } > RAM
108
109 /* ### .rodata */
110 .rodata : ALIGN(4)
111 {
112 . = ALIGN(4);
113 __srodata = .;
114 *(.rodata .rodata.*);
115
116 /* 4-byte align the end (VMA) of this section.
117 This is required by LLD to ensure the LMA of the following .data
118 section will have the correct alignment. */
119 . = ALIGN(4);
120 __erodata = .;
121 } > RAM
122
123 /* ## Sections in RAM */
124 /* ### .data */
125 .data : ALIGN(4)
126 {
127 . = ALIGN(4);
128 __sdata = .;
129 __edata = .;
130 *(.data .data.*);
131 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
132 } > RAM
133 /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to
134 * use the .data loading mechanism by pushing __edata. Note: do not change
135 * output region or load region in those user sections! */
136 . = ALIGN(4);
137
138 /* LMA of .data */
139 __sidata = LOADADDR(.data);
140
141 /* ### .gnu.sgstubs
142 This section contains the TrustZone-M veneers put there by the Arm GNU linker. */
143 /* Security Attribution Unit blocks must be 32 bytes aligned. */
144 /* Note that this pads the RAM usage to 32 byte alignment. */
145 .gnu.sgstubs : ALIGN(32)
146 {
147 . = ALIGN(32);
148 __veneer_base = .;
149 *(.gnu.sgstubs*)
150 . = ALIGN(32);
151 __veneer_limit = .;
152 } > RAM
153
154 /* ### .bss */
155 .bss (NOLOAD) : ALIGN(4)
156 {
157 . = ALIGN(4);
158 __sbss = .;
159 *(.bss .bss.*);
160 *(COMMON); /* Uninitialized C statics */
161 . = ALIGN(4); /* 4-byte align the end (VMA) of this section */
162 } > RAM
163 /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to
164 * use the .bss zeroing mechanism by pushing __ebss. Note: do not change
165 * output region or load region in those user sections! */
166 . = ALIGN(4);
167 __ebss = .;
168
169 /* ### .uninit */
170 .uninit (NOLOAD) : ALIGN(4)
171 {
172 . = ALIGN(4);
173 __suninit = .;
174 *(.uninit .uninit.*);
175 . = ALIGN(4);
176 __euninit = .;
177 } > RAM
178
179 /* Place the heap right after `.uninit` in RAM */
180 PROVIDE(__sheap = __euninit);
181
182 /* ## .got */
183 /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in
184 the input files and raise an error if relocatable code is found */
185 .got (NOLOAD) :
186 {
187 KEEP(*(.got .got.*));
188 }
189
190 /* ## Discarded sections */
191 /DISCARD/ :
192 {
193 /* Unused exception related info that only wastes space */
194 *(.ARM.exidx);
195 *(.ARM.exidx.*);
196 *(.ARM.extab.*);
197 }
198}
199
200/* Do not exceed this mark in the error messages below | */
201/* # Alignment checks */
202ASSERT(ORIGIN(RAM) % 4 == 0, "
203ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned");
204
205ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, "
206BUG(cortex-m-rt): .data is not 4-byte aligned");
207
208ASSERT(__sidata % 4 == 0, "
209BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned");
210
211ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, "
212BUG(cortex-m-rt): .bss is not 4-byte aligned");
213
214ASSERT(__sheap % 4 == 0, "
215BUG(cortex-m-rt): start of .heap is not 4-byte aligned");
216
217/* # Position checks */
218
219/* ## .vector_table */
220ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, "
221BUG(cortex-m-rt): the reset vector is missing");
222
223ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, "
224BUG(cortex-m-rt): the exception vectors are missing");
225
226ASSERT(SIZEOF(.vector_table) > 0x40, "
227ERROR(cortex-m-rt): The interrupt vectors are missing.
228Possible solutions, from most likely to less likely:
229- Link to a svd2rust generated device crate
230- Check that you actually use the device/hal/bsp crate in your code
231- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency
232may be enabling it)
233- Supply the interrupt handlers yourself. Check the documentation for details.");
234
235/* ## .text */
236ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, "
237ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section
238Set _stext to an address greater than the end of .vector_table (See output of `nm`)");
239
240ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), "
241ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory.
242Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'");
243
244/* # Other checks */
245ASSERT(SIZEOF(.got) == 0, "
246ERROR(cortex-m-rt): .got section detected in the input object files
247Dynamic relocations are not supported. If you are linking to C code compiled using
248the 'cc' crate then modify your build script to compile the C code _without_
249the -fPIC flag. See the documentation of the `cc::Build.pic` method for details.");
250/* Do not exceed this mark in the error messages above | */
251
252
253/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */
254/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */
255INCLUDE device.x \ No newline at end of file
diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs
new file mode 100644
index 000000000..0be9d9f24
--- /dev/null
+++ b/tests/rp/src/bin/gpio.rs
@@ -0,0 +1,192 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{assert, *};
6use embassy::executor::Spawner;
7use embassy_rp::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull};
8use embassy_rp::Peripherals;
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy::main]
12async fn main(_spawner: Spawner, p: Peripherals) {
13 info!("Hello World!");
14
15 let (mut a, mut b) = (p.PIN_0, p.PIN_1);
16
17 // Test initial output
18 {
19 let b = Input::new(&mut b, Pull::None);
20
21 {
22 let _a = Output::new(&mut a, Level::Low);
23 delay();
24 assert!(b.is_low());
25 }
26 {
27 let _a = Output::new(&mut a, Level::High);
28 delay();
29 assert!(b.is_high());
30 }
31 }
32
33 // Test input no pull
34 {
35 let b = Input::new(&mut b, Pull::None);
36 // no pull, the status is undefined
37
38 let mut a = Output::new(&mut a, Level::Low);
39 delay();
40 assert!(b.is_low());
41 a.set_high();
42 delay();
43 assert!(b.is_high());
44 }
45
46 // Test input pulldown
47 {
48 let b = Input::new(&mut b, Pull::Down);
49 delay();
50 assert!(b.is_low());
51
52 let mut a = Output::new(&mut a, Level::Low);
53 delay();
54 assert!(b.is_low());
55 a.set_high();
56 delay();
57 assert!(b.is_high());
58 }
59
60 // Test input pullup
61 {
62 let b = Input::new(&mut b, Pull::Up);
63 delay();
64 assert!(b.is_high());
65
66 let mut a = Output::new(&mut a, Level::Low);
67 delay();
68 assert!(b.is_low());
69 a.set_high();
70 delay();
71 assert!(b.is_high());
72 }
73
74 // OUTPUT OPEN DRAIN
75 {
76 let mut b = OutputOpenDrain::new(&mut b, Level::High);
77 let mut a = Flex::new(&mut a);
78 a.set_as_input();
79
80 // When an OutputOpenDrain is high, it doesn't drive the pin.
81 a.set_pull(Pull::Up);
82 delay();
83 assert!(a.is_high());
84 a.set_pull(Pull::Down);
85 delay();
86 assert!(a.is_low());
87
88 b.set_low();
89
90 // When an OutputOpenDrain is low, it drives the pin low.
91 a.set_pull(Pull::Up);
92 delay();
93 assert!(a.is_low());
94 a.set_pull(Pull::Down);
95 delay();
96 assert!(a.is_low());
97
98 b.set_high();
99
100 a.set_pull(Pull::Up);
101 delay();
102 assert!(a.is_high());
103 a.set_pull(Pull::Down);
104 delay();
105 assert!(a.is_low());
106 }
107
108 // FLEX
109 // Test initial output
110 {
111 //Flex pin configured as input
112 let mut b = Flex::new(&mut b);
113 b.set_as_input();
114
115 {
116 //Flex pin configured as output
117 let mut a = Flex::new(&mut a); //Flex pin configured as output
118 a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state
119 a.set_as_output();
120 delay();
121 assert!(b.is_low());
122 }
123 {
124 //Flex pin configured as output
125 let mut a = Flex::new(&mut a);
126 a.set_high();
127 a.set_as_output();
128
129 delay();
130 assert!(b.is_high());
131 }
132 }
133
134 // Test input no pull
135 {
136 let mut b = Flex::new(&mut b);
137 b.set_as_input(); // no pull by default.
138
139 let mut a = Flex::new(&mut a);
140 a.set_low();
141 a.set_as_output();
142
143 delay();
144 assert!(b.is_low());
145 a.set_high();
146 delay();
147 assert!(b.is_high());
148 }
149
150 // Test input pulldown
151 {
152 let mut b = Flex::new(&mut b);
153 b.set_as_input();
154 b.set_pull(Pull::Down);
155 delay();
156 assert!(b.is_low());
157
158 let mut a = Flex::new(&mut a);
159 a.set_low();
160 a.set_as_output();
161 delay();
162 assert!(b.is_low());
163 a.set_high();
164 delay();
165 assert!(b.is_high());
166 }
167
168 // Test input pullup
169 {
170 let mut b = Flex::new(&mut b);
171 b.set_as_input();
172 b.set_pull(Pull::Up);
173 delay();
174 assert!(b.is_high());
175
176 let mut a = Flex::new(&mut a);
177 a.set_high();
178 a.set_as_output();
179 delay();
180 assert!(b.is_high());
181 a.set_low();
182 delay();
183 assert!(b.is_low());
184 }
185
186 info!("Test OK");
187 cortex_m::asm::bkpt();
188}
189
190fn delay() {
191 cortex_m::asm::delay(10000);
192}