aboutsummaryrefslogtreecommitdiff
path: root/embassy-mspm0
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mspm0')
-rw-r--r--embassy-mspm0/Cargo.toml131
-rw-r--r--embassy-mspm0/build.rs611
-rw-r--r--embassy-mspm0/build_common.rs94
-rw-r--r--embassy-mspm0/src/fmt.rs270
-rw-r--r--embassy-mspm0/src/gpio.rs1060
-rw-r--r--embassy-mspm0/src/int_group/c110x.rs25
-rw-r--r--embassy-mspm0/src/int_group/g350x.rs51
-rw-r--r--embassy-mspm0/src/int_group/g351x.rs52
-rw-r--r--embassy-mspm0/src/int_group/l130x.rs46
-rw-r--r--embassy-mspm0/src/int_group/l222x.rs49
-rw-r--r--embassy-mspm0/src/lib.rs107
-rw-r--r--embassy-mspm0/src/time_driver.rs423
-rw-r--r--embassy-mspm0/src/timer.rs48
13 files changed, 2967 insertions, 0 deletions
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
new file mode 100644
index 000000000..cfe0c85a9
--- /dev/null
+++ b/embassy-mspm0/Cargo.toml
@@ -0,0 +1,131 @@
1[package]
2name = "embassy-mspm0"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "Embassy Hardware Abstraction Layer (HAL) for Texas Instruments MSPM0 series microcontrollers"
7keywords = ["embedded", "async", "mspm0", "hal", "embedded-hal"]
8categories = ["embedded", "hardware-support", "no-std", "asynchronous"]
9repository = "https://github.com/embassy-rs/embassy"
10documentation = "https://docs.embassy.dev/embassy-mspm0"
11
12[package.metadata.embassy_docs]
13src_base = "https://github.com/embassy-rs/embassy/blob/embassy-mspm0-v$VERSION/embassy-mspm0/src/"
14src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-mspm0/src/"
15
16features = ["defmt", "unstable-pac", "time-driver-any"]
17
18[package.metadata.docs.rs]
19features = ["defmt", "unstable-pac", "time-driver-any", "time", "mspm0g3507"]
20rustdoc-args = ["--cfg", "docsrs"]
21
22[dependencies]
23embassy-sync = { version = "0.6.2", path = "../embassy-sync" }
24embassy-time = { version = "0.4.0", path = "../embassy-time", optional = true }
25# TODO: Support other tick rates
26embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] }
27embassy-time-queue-utils = { version = "0.1", path = "../embassy-time-queue-utils", optional = true }
28embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
29embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
30embassy-embedded-hal = { version = "0.3.0", path = "../embassy-embedded-hal", default-features = false }
31embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true }
32
33embedded-hal = { version = "1.0" }
34embedded-hal-async = { version = "1.0" }
35
36defmt = { version = "0.3", optional = true }
37log = { version = "0.4.14", optional = true }
38cortex-m-rt = ">=0.6.15,<0.8"
39cortex-m = "0.7.6"
40critical-section = "1.2.0"
41
42# mspm0-metapac = { version = "" }
43mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-9faa5239a8eab04946086158f2a7fdff5a6a179d" }
44
45[build-dependencies]
46proc-macro2 = "1.0.94"
47quote = "1.0.40"
48
49# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
50mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-9faa5239a8eab04946086158f2a7fdff5a6a179d", default-features = false, features = ["metadata"] }
51
52[features]
53default = ["rt"]
54
55## Enable `mspm0-metapac`'s `rt` feature
56rt = ["mspm0-metapac/rt"]
57
58## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
59defmt = [
60 "dep:defmt",
61 "embassy-sync/defmt",
62 "embassy-embedded-hal/defmt",
63 "embassy-hal-internal/defmt",
64 "embassy-time?/defmt",
65]
66
67## Re-export mspm0-metapac at `mspm0::pac`.
68## This is unstable because semver-minor (non-breaking) releases of embassy-mspm0 may major-bump (breaking) the mspm0-metapac version.
69## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC.
70## There are no plans to make this stable.
71unstable-pac = []
72
73#! ## Time
74
75# Features starting with `_` are for internal use only. They're not intended
76# to be enabled by other crates, and are not covered by semver guarantees.
77_time-driver = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
78
79# Use any time driver
80time-driver-any = ["_time-driver"]
81## Use TIMG0 as time driver
82time-driver-timg0 = ["_time-driver"]
83## Use TIMG1 as time driver
84time-driver-timg1 = ["_time-driver"]
85## Use TIMG2 as time driver
86time-driver-timg2 = ["_time-driver"]
87## Use TIMG3 as time driver
88time-driver-timg3 = ["_time-driver"]
89## Use TIMG4 as time driver
90time-driver-timg4 = ["_time-driver"]
91## Use TIMG5 as time driver
92time-driver-timg5 = ["_time-driver"]
93## Use TIMG6 as time driver
94time-driver-timg6 = ["_time-driver"]
95## Use TIMG7 as time driver
96time-driver-timg7 = ["_time-driver"]
97## Use TIMG8 as time driver
98time-driver-timg8 = ["_time-driver"]
99## Use TIMG9 as time driver
100time-driver-timg9 = ["_time-driver"]
101## Use TIMG10 as time driver
102time-driver-timg10 = ["_time-driver"]
103## Use TIMG11 as time driver
104time-driver-timg11 = ["_time-driver"]
105# TODO: Support TIMG12 and TIMG13
106## Use TIMG14 as time driver
107time-driver-timg14 = ["_time-driver"]
108## Use TIMA0 as time driver
109time-driver-tima0 = ["_time-driver"]
110## Use TIMA1 as time driver
111time-driver-tima1 = ["_time-driver"]
112
113#! ## Chip-selection features
114#! Select your chip by specifying the model as a feature, e.g. `mspm0g350x`.
115#! Check the `Cargo.toml` for the latest list of supported chips.
116#!
117#! **Important:** Do not forget to adapt the target chip in your toolchain,
118#! e.g. in `.cargo/config.toml`.
119
120mspm0c110x = [ "mspm0-metapac/mspm0c110x" ]
121mspm0g110x = [ "mspm0-metapac/mspm0g110x" ]
122mspm0g150x = [ "mspm0-metapac/mspm0g150x" ]
123mspm0g151x = [ "mspm0-metapac/mspm0g151x" ]
124mspm0g310x = [ "mspm0-metapac/mspm0g310x" ]
125mspm0g350x = [ "mspm0-metapac/mspm0g350x" ]
126mspm0g351x = [ "mspm0-metapac/mspm0g351x" ]
127mspm0l110x = [ "mspm0-metapac/mspm0l110x" ]
128mspm0l122x = [ "mspm0-metapac/mspm0l122x" ]
129mspm0l130x = [ "mspm0-metapac/mspm0l130x" ]
130mspm0l134x = [ "mspm0-metapac/mspm0l134x" ]
131mspm0l222x = [ "mspm0-metapac/mspm0l222x" ]
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
new file mode 100644
index 000000000..8c6dd4a5c
--- /dev/null
+++ b/embassy-mspm0/build.rs
@@ -0,0 +1,611 @@
1use std::collections::HashMap;
2use std::io::Write;
3use std::path::{Path, PathBuf};
4use std::process::Command;
5use std::sync::LazyLock;
6use std::{env, fs};
7
8use common::CfgSet;
9use mspm0_metapac::metadata::METADATA;
10use proc_macro2::{Ident, Literal, Span, TokenStream};
11use quote::{format_ident, quote};
12
13#[path = "./build_common.rs"]
14mod common;
15
16fn main() {
17 generate_code();
18}
19
20fn generate_code() {
21 let mut cfgs = common::CfgSet::new();
22 common::set_target_cfgs(&mut cfgs);
23
24 cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
25
26 let mut singletons = Vec::new();
27
28 // Generate singletons for GPIO pins. To only consider pins available on a family, use the name of
29 // the pins from the pincm mappings.
30 for pincm_mapping in METADATA.pincm_mappings.iter() {
31 singletons.push(pincm_mapping.pin.to_string());
32 }
33
34 for peri in METADATA.peripherals {
35 match peri.kind {
36 // Specially generated.
37 "gpio" => match peri.name {
38 "GPIOB" => cfgs.enable("gpio_pb"),
39 "GPIOC" => cfgs.enable("gpio_pc"),
40 _ => (),
41 },
42
43 // These peripherals are managed internally by the hal.
44 "iomux" | "cpuss" => {}
45
46 _ => singletons.push(peri.name.to_string()),
47 }
48 }
49
50 time_driver(&singletons, &mut cfgs);
51
52 // ========
53 // Write singletons
54 let mut g = TokenStream::new();
55
56 let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
57
58 g.extend(quote! {
59 embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*);
60 });
61
62 g.extend(quote! {
63 embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*);
64 });
65
66 // ========
67 // Generate GPIO pincm lookup tables.
68 let pincms = METADATA.pincm_mappings.iter().map(|mapping| {
69 let port_letter = mapping.pin.strip_prefix("P").unwrap();
70 let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
71 // This assumes all ports are single letter length.
72 // This is fine unless TI releases a part with 833+ GPIO pins.
73 let pin_number = mapping.pin[2..].parse::<u8>().unwrap();
74
75 let num = port_base + pin_number;
76
77 // But subtract 1 since pincm indices start from 0, not 1.
78 let pincm = Literal::u8_unsuffixed(mapping.pincm - 1);
79 quote! {
80 #num => #pincm
81 }
82 });
83
84 g.extend(quote! {
85 #[doc = "Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts."]
86 pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {
87 match pin_port {
88 #(#pincms),*,
89 _ => unreachable!(),
90 }
91 }
92 });
93
94 for pincm_mapping in METADATA.pincm_mappings.iter() {
95 let name = Ident::new(&pincm_mapping.pin, Span::call_site());
96 let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap();
97 let port_letter = port_letter.chars().next().unwrap();
98 let pin_number = Literal::u8_unsuffixed(pincm_mapping.pin[2..].parse::<u8>().unwrap());
99
100 let port = Ident::new(&format!("Port{}", port_letter), Span::call_site());
101
102 // TODO: Feature gate pins that can be used as NRST
103
104 g.extend(quote! {
105 impl_pin!(#name, crate::gpio::Port::#port, #pin_number);
106 });
107 }
108
109 // Generate timers
110 for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("TIM")) {
111 let name = Ident::new(&peripheral.name, Span::call_site());
112 let timers = &*TIMERS;
113
114 let timer = timers.get(peripheral.name).expect("Timer does not exist");
115 assert!(timer.bits == 16 || timer.bits == 32);
116 let bits = if timer.bits == 16 {
117 quote! { Bits16 }
118 } else {
119 quote! { Bits32 }
120 };
121
122 g.extend(quote! {
123 impl_timer!(#name, #bits);
124 });
125 }
126
127 // Generate interrupt module
128 let interrupts: Vec<Ident> = METADATA
129 .interrupts
130 .iter()
131 .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
132 .collect();
133
134 g.extend(quote! {
135 embassy_hal_internal::interrupt_mod! {
136 #(#interrupts),*
137 }
138 });
139
140 let group_interrupt_enables = METADATA
141 .interrupts
142 .iter()
143 .filter(|interrupt| interrupt.name.contains("GROUP"))
144 .map(|interrupt| {
145 let name = Ident::new(interrupt.name, Span::call_site());
146
147 quote! {
148 crate::interrupt::typelevel::#name::enable();
149 }
150 });
151
152 // Generate interrupt enables for groups
153 g.extend(quote! {
154 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
155 use crate::interrupt::typelevel::Interrupt;
156
157 unsafe {
158 #(#group_interrupt_enables)*
159 }
160 }
161 });
162
163 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
164 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
165 fs::write(&out_file, g.to_string()).unwrap();
166 rustfmt(&out_file);
167}
168
169fn time_driver(singletons: &[String], cfgs: &mut CfgSet) {
170 // Timer features
171 for (timer, desc) in TIMERS.iter() {
172 if desc.bits != 16 {
173 continue;
174 }
175
176 let name = timer.to_lowercase();
177 cfgs.declare(&format!("time_driver_{}", name));
178 }
179
180 let time_driver = match env::vars()
181 .map(|(a, _)| a)
182 .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
183 .get_one()
184 {
185 Ok(x) => Some(
186 x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
187 .unwrap()
188 .to_ascii_lowercase(),
189 ),
190 Err(GetOneError::None) => None,
191 Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"),
192 };
193
194 // Verify the selected timer is available
195 let singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
196 None => "",
197 Some("timg0") => "TIMG0",
198 Some("timg1") => "TIMG1",
199 Some("timg2") => "TIMG2",
200 Some("timg3") => "TIMG3",
201 Some("timg4") => "TIMG4",
202 Some("timg5") => "TIMG5",
203 Some("timg6") => "TIMG6",
204 Some("timg7") => "TIMG7",
205 Some("timg8") => "TIMG8",
206 Some("timg9") => "TIMG9",
207 Some("timg10") => "TIMG10",
208 Some("timg11") => "TIMG11",
209 Some("timg14") => "TIMG14",
210 Some("tima0") => "TIMA0",
211 Some("tima1") => "TIMA1",
212 Some("any") => {
213 // Order of timer candidates:
214 // 1. 16-bit, 2 channel
215 // 2. 16-bit, 2 channel with shadow registers
216 // 3. 16-bit, 4 channel
217 // 4. 16-bit with QEI
218 // 5. Advanced timers
219 //
220 // TODO: Select RTC first if available
221 // TODO: 32-bit timers are not considered yet
222 [
223 // 16-bit, 2 channel
224 "TIMG0", "TIMG1", "TIMG2", "TIMG3", // 16-bit, 2 channel with shadow registers
225 "TIMG4", "TIMG5", "TIMG6", "TIMG7", // 16-bit, 4 channel
226 "TIMG14", // 16-bit with QEI
227 "TIMG8", "TIMG9", "TIMG10", "TIMG11", // Advanced timers
228 "TIMA0", "TIMA1",
229 ]
230 .iter()
231 .find(|tim| singletons.contains(&tim.to_string()))
232 .expect("Could not find any timer")
233 }
234 _ => panic!("unknown time_driver {:?}", time_driver),
235 };
236
237 if !singleton.is_empty() {
238 cfgs.enable(format!("time_driver_{}", singleton.to_lowercase()));
239 }
240}
241
242/// rustfmt a given path.
243/// Failures are logged to stderr and ignored.
244fn rustfmt(path: impl AsRef<Path>) {
245 let path = path.as_ref();
246 match Command::new("rustfmt").args([path]).output() {
247 Err(e) => {
248 eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
249 }
250 Ok(out) => {
251 if !out.status.success() {
252 eprintln!("rustfmt {:?} failed:", path);
253 eprintln!("=== STDOUT:");
254 std::io::stderr().write_all(&out.stdout).unwrap();
255 eprintln!("=== STDERR:");
256 std::io::stderr().write_all(&out.stderr).unwrap();
257 }
258 }
259 }
260}
261
262#[allow(dead_code)]
263struct TimerDesc {
264 bits: u8,
265 /// Is there an 8-bit prescaler
266 prescaler: bool,
267 /// Is there a repeat counter
268 repeat_counter: bool,
269 ccp_channels_internal: u8,
270 ccp_channels_external: u8,
271 external_pwm_channels: u8,
272 phase_load: bool,
273 shadow_load: bool,
274 shadow_ccs: bool,
275 deadband: bool,
276 fault_handler: bool,
277 qei_hall: bool,
278}
279
280/// Description of all timer instances.
281const TIMERS: LazyLock<HashMap<String, TimerDesc>> = LazyLock::new(|| {
282 let mut map = HashMap::new();
283 map.insert(
284 "TIMG0".into(),
285 TimerDesc {
286 bits: 16,
287 prescaler: true,
288 repeat_counter: false,
289 ccp_channels_internal: 2,
290 ccp_channels_external: 2,
291 external_pwm_channels: 2,
292 phase_load: false,
293 shadow_load: false,
294 shadow_ccs: false,
295 deadband: false,
296 fault_handler: false,
297 qei_hall: false,
298 },
299 );
300
301 map.insert(
302 "TIMG1".into(),
303 TimerDesc {
304 bits: 16,
305 prescaler: true,
306 repeat_counter: false,
307 ccp_channels_internal: 2,
308 ccp_channels_external: 2,
309 external_pwm_channels: 2,
310 phase_load: false,
311 shadow_load: false,
312 shadow_ccs: false,
313 deadband: false,
314 fault_handler: false,
315 qei_hall: false,
316 },
317 );
318
319 map.insert(
320 "TIMG2".into(),
321 TimerDesc {
322 bits: 16,
323 prescaler: true,
324 repeat_counter: false,
325 ccp_channels_internal: 2,
326 ccp_channels_external: 2,
327 external_pwm_channels: 2,
328 phase_load: false,
329 shadow_load: false,
330 shadow_ccs: false,
331 deadband: false,
332 fault_handler: false,
333 qei_hall: false,
334 },
335 );
336
337 map.insert(
338 "TIMG3".into(),
339 TimerDesc {
340 bits: 16,
341 prescaler: true,
342 repeat_counter: false,
343 ccp_channels_internal: 2,
344 ccp_channels_external: 2,
345 external_pwm_channels: 2,
346 phase_load: false,
347 shadow_load: false,
348 shadow_ccs: false,
349 deadband: false,
350 fault_handler: false,
351 qei_hall: false,
352 },
353 );
354
355 map.insert(
356 "TIMG4".into(),
357 TimerDesc {
358 bits: 16,
359 prescaler: true,
360 repeat_counter: false,
361 ccp_channels_internal: 2,
362 ccp_channels_external: 2,
363 external_pwm_channels: 2,
364 phase_load: false,
365 shadow_load: true,
366 shadow_ccs: true,
367 deadband: false,
368 fault_handler: false,
369 qei_hall: false,
370 },
371 );
372
373 map.insert(
374 "TIMG5".into(),
375 TimerDesc {
376 bits: 16,
377 prescaler: true,
378 repeat_counter: false,
379 ccp_channels_internal: 2,
380 ccp_channels_external: 2,
381 external_pwm_channels: 2,
382 phase_load: false,
383 shadow_load: true,
384 shadow_ccs: true,
385 deadband: false,
386 fault_handler: false,
387 qei_hall: false,
388 },
389 );
390
391 map.insert(
392 "TIMG6".into(),
393 TimerDesc {
394 bits: 16,
395 prescaler: true,
396 repeat_counter: false,
397 ccp_channels_internal: 2,
398 ccp_channels_external: 2,
399 external_pwm_channels: 2,
400 phase_load: false,
401 shadow_load: true,
402 shadow_ccs: true,
403 deadband: false,
404 fault_handler: false,
405 qei_hall: false,
406 },
407 );
408
409 map.insert(
410 "TIMG7".into(),
411 TimerDesc {
412 bits: 16,
413 prescaler: true,
414 repeat_counter: false,
415 ccp_channels_internal: 2,
416 ccp_channels_external: 2,
417 external_pwm_channels: 2,
418 phase_load: false,
419 shadow_load: true,
420 shadow_ccs: true,
421 deadband: false,
422 fault_handler: false,
423 qei_hall: false,
424 },
425 );
426
427 map.insert(
428 "TIMG8".into(),
429 TimerDesc {
430 bits: 16,
431 prescaler: true,
432 repeat_counter: false,
433 ccp_channels_internal: 2,
434 ccp_channels_external: 2,
435 external_pwm_channels: 2,
436 phase_load: false,
437 shadow_load: false,
438 shadow_ccs: false,
439 deadband: false,
440 fault_handler: false,
441 qei_hall: true,
442 },
443 );
444
445 map.insert(
446 "TIMG9".into(),
447 TimerDesc {
448 bits: 16,
449 prescaler: true,
450 repeat_counter: false,
451 ccp_channels_internal: 2,
452 ccp_channels_external: 2,
453 external_pwm_channels: 2,
454 phase_load: false,
455 shadow_load: false,
456 shadow_ccs: false,
457 deadband: false,
458 fault_handler: false,
459 qei_hall: true,
460 },
461 );
462
463 map.insert(
464 "TIMG10".into(),
465 TimerDesc {
466 bits: 16,
467 prescaler: true,
468 repeat_counter: false,
469 ccp_channels_internal: 2,
470 ccp_channels_external: 2,
471 external_pwm_channels: 2,
472 phase_load: false,
473 shadow_load: false,
474 shadow_ccs: false,
475 deadband: false,
476 fault_handler: false,
477 qei_hall: true,
478 },
479 );
480
481 map.insert(
482 "TIMG11".into(),
483 TimerDesc {
484 bits: 16,
485 prescaler: true,
486 repeat_counter: false,
487 ccp_channels_internal: 2,
488 ccp_channels_external: 2,
489 external_pwm_channels: 2,
490 phase_load: false,
491 shadow_load: false,
492 shadow_ccs: false,
493 deadband: false,
494 fault_handler: false,
495 qei_hall: true,
496 },
497 );
498
499 map.insert(
500 "TIMG12".into(),
501 TimerDesc {
502 bits: 32,
503 prescaler: false,
504 repeat_counter: false,
505 ccp_channels_internal: 2,
506 ccp_channels_external: 2,
507 external_pwm_channels: 2,
508 phase_load: false,
509 shadow_load: false,
510 shadow_ccs: true,
511 deadband: false,
512 fault_handler: false,
513 qei_hall: false,
514 },
515 );
516
517 map.insert(
518 "TIMG13".into(),
519 TimerDesc {
520 bits: 32,
521 prescaler: false,
522 repeat_counter: false,
523 ccp_channels_internal: 2,
524 ccp_channels_external: 2,
525 external_pwm_channels: 2,
526 phase_load: false,
527 shadow_load: false,
528 shadow_ccs: true,
529 deadband: false,
530 fault_handler: false,
531 qei_hall: false,
532 },
533 );
534
535 map.insert(
536 "TIMG14".into(),
537 TimerDesc {
538 bits: 16,
539 prescaler: true,
540 repeat_counter: false,
541 ccp_channels_internal: 4,
542 ccp_channels_external: 4,
543 external_pwm_channels: 4,
544 phase_load: false,
545 shadow_load: false,
546 shadow_ccs: false,
547 deadband: false,
548 fault_handler: false,
549 qei_hall: false,
550 },
551 );
552
553 map.insert(
554 "TIMA0".into(),
555 TimerDesc {
556 bits: 16,
557 prescaler: true,
558 repeat_counter: true,
559 ccp_channels_internal: 4,
560 ccp_channels_external: 2,
561 external_pwm_channels: 8,
562 phase_load: true,
563 shadow_load: true,
564 shadow_ccs: true,
565 deadband: true,
566 fault_handler: true,
567 qei_hall: false,
568 },
569 );
570
571 map.insert(
572 "TIMA1".into(),
573 TimerDesc {
574 bits: 16,
575 prescaler: true,
576 repeat_counter: true,
577 ccp_channels_internal: 2,
578 ccp_channels_external: 2,
579 external_pwm_channels: 4,
580 phase_load: true,
581 shadow_load: true,
582 shadow_ccs: true,
583 deadband: true,
584 fault_handler: true,
585 qei_hall: false,
586 },
587 );
588
589 map
590});
591
592enum GetOneError {
593 None,
594 Multiple,
595}
596
597trait IteratorExt: Iterator {
598 fn get_one(self) -> Result<Self::Item, GetOneError>;
599}
600
601impl<T: Iterator> IteratorExt for T {
602 fn get_one(mut self) -> Result<Self::Item, GetOneError> {
603 match self.next() {
604 None => Err(GetOneError::None),
605 Some(res) => match self.next() {
606 Some(_) => Err(GetOneError::Multiple),
607 None => Ok(res),
608 },
609 }
610 }
611}
diff --git a/embassy-mspm0/build_common.rs b/embassy-mspm0/build_common.rs
new file mode 100644
index 000000000..4f24e6d37
--- /dev/null
+++ b/embassy-mspm0/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-mspm0/src/fmt.rs b/embassy-mspm0/src/fmt.rs
new file mode 100644
index 000000000..8ca61bc39
--- /dev/null
+++ b/embassy-mspm0/src/fmt.rs
@@ -0,0 +1,270 @@
1#![macro_use]
2#![allow(unused)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9#[collapse_debuginfo(yes)]
10macro_rules! assert {
11 ($($x:tt)*) => {
12 {
13 #[cfg(not(feature = "defmt"))]
14 ::core::assert!($($x)*);
15 #[cfg(feature = "defmt")]
16 ::defmt::assert!($($x)*);
17 }
18 };
19}
20
21#[collapse_debuginfo(yes)]
22macro_rules! assert_eq {
23 ($($x:tt)*) => {
24 {
25 #[cfg(not(feature = "defmt"))]
26 ::core::assert_eq!($($x)*);
27 #[cfg(feature = "defmt")]
28 ::defmt::assert_eq!($($x)*);
29 }
30 };
31}
32
33#[collapse_debuginfo(yes)]
34macro_rules! assert_ne {
35 ($($x:tt)*) => {
36 {
37 #[cfg(not(feature = "defmt"))]
38 ::core::assert_ne!($($x)*);
39 #[cfg(feature = "defmt")]
40 ::defmt::assert_ne!($($x)*);
41 }
42 };
43}
44
45#[collapse_debuginfo(yes)]
46macro_rules! debug_assert {
47 ($($x:tt)*) => {
48 {
49 #[cfg(not(feature = "defmt"))]
50 ::core::debug_assert!($($x)*);
51 #[cfg(feature = "defmt")]
52 ::defmt::debug_assert!($($x)*);
53 }
54 };
55}
56
57#[collapse_debuginfo(yes)]
58macro_rules! debug_assert_eq {
59 ($($x:tt)*) => {
60 {
61 #[cfg(not(feature = "defmt"))]
62 ::core::debug_assert_eq!($($x)*);
63 #[cfg(feature = "defmt")]
64 ::defmt::debug_assert_eq!($($x)*);
65 }
66 };
67}
68
69#[collapse_debuginfo(yes)]
70macro_rules! debug_assert_ne {
71 ($($x:tt)*) => {
72 {
73 #[cfg(not(feature = "defmt"))]
74 ::core::debug_assert_ne!($($x)*);
75 #[cfg(feature = "defmt")]
76 ::defmt::debug_assert_ne!($($x)*);
77 }
78 };
79}
80
81#[collapse_debuginfo(yes)]
82macro_rules! todo {
83 ($($x:tt)*) => {
84 {
85 #[cfg(not(feature = "defmt"))]
86 ::core::todo!($($x)*);
87 #[cfg(feature = "defmt")]
88 ::defmt::todo!($($x)*);
89 }
90 };
91}
92
93#[collapse_debuginfo(yes)]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 {
97 #[cfg(not(feature = "defmt"))]
98 ::core::unreachable!($($x)*);
99 #[cfg(feature = "defmt")]
100 ::defmt::unreachable!($($x)*);
101 }
102 };
103}
104
105#[collapse_debuginfo(yes)]
106macro_rules! panic {
107 ($($x:tt)*) => {
108 {
109 #[cfg(not(feature = "defmt"))]
110 ::core::panic!($($x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::panic!($($x)*);
113 }
114 };
115}
116
117#[collapse_debuginfo(yes)]
118macro_rules! trace {
119 ($s:literal $(, $x:expr)* $(,)?) => {
120 {
121 #[cfg(feature = "log")]
122 ::log::trace!($s $(, $x)*);
123 #[cfg(feature = "defmt")]
124 ::defmt::trace!($s $(, $x)*);
125 #[cfg(not(any(feature = "log", feature="defmt")))]
126 let _ = ($( & $x ),*);
127 }
128 };
129}
130
131#[collapse_debuginfo(yes)]
132macro_rules! debug {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::debug!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::debug!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145#[collapse_debuginfo(yes)]
146macro_rules! info {
147 ($s:literal $(, $x:expr)* $(,)?) => {
148 {
149 #[cfg(feature = "log")]
150 ::log::info!($s $(, $x)*);
151 #[cfg(feature = "defmt")]
152 ::defmt::info!($s $(, $x)*);
153 #[cfg(not(any(feature = "log", feature="defmt")))]
154 let _ = ($( & $x ),*);
155 }
156 };
157}
158
159#[collapse_debuginfo(yes)]
160macro_rules! warn {
161 ($s:literal $(, $x:expr)* $(,)?) => {
162 {
163 #[cfg(feature = "log")]
164 ::log::warn!($s $(, $x)*);
165 #[cfg(feature = "defmt")]
166 ::defmt::warn!($s $(, $x)*);
167 #[cfg(not(any(feature = "log", feature="defmt")))]
168 let _ = ($( & $x ),*);
169 }
170 };
171}
172
173#[collapse_debuginfo(yes)]
174macro_rules! error {
175 ($s:literal $(, $x:expr)* $(,)?) => {
176 {
177 #[cfg(feature = "log")]
178 ::log::error!($s $(, $x)*);
179 #[cfg(feature = "defmt")]
180 ::defmt::error!($s $(, $x)*);
181 #[cfg(not(any(feature = "log", feature="defmt")))]
182 let _ = ($( & $x ),*);
183 }
184 };
185}
186
187#[cfg(feature = "defmt")]
188#[collapse_debuginfo(yes)]
189macro_rules! unwrap {
190 ($($x:tt)*) => {
191 ::defmt::unwrap!($($x)*)
192 };
193}
194
195#[cfg(not(feature = "defmt"))]
196#[collapse_debuginfo(yes)]
197macro_rules! unwrap {
198 ($arg:expr) => {
199 match $crate::fmt::Try::into_result($arg) {
200 ::core::result::Result::Ok(t) => t,
201 ::core::result::Result::Err(e) => {
202 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
203 }
204 }
205 };
206 ($arg:expr, $($msg:expr),+ $(,)? ) => {
207 match $crate::fmt::Try::into_result($arg) {
208 ::core::result::Result::Ok(t) => t,
209 ::core::result::Result::Err(e) => {
210 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
211 }
212 }
213 }
214}
215
216#[derive(Debug, Copy, Clone, Eq, PartialEq)]
217pub struct NoneError;
218
219pub trait Try {
220 type Ok;
221 type Error;
222 fn into_result(self) -> Result<Self::Ok, Self::Error>;
223}
224
225impl<T> Try for Option<T> {
226 type Ok = T;
227 type Error = NoneError;
228
229 #[inline]
230 fn into_result(self) -> Result<T, NoneError> {
231 self.ok_or(NoneError)
232 }
233}
234
235impl<T, E> Try for Result<T, E> {
236 type Ok = T;
237 type Error = E;
238
239 #[inline]
240 fn into_result(self) -> Self {
241 self
242 }
243}
244
245pub(crate) struct Bytes<'a>(pub &'a [u8]);
246
247impl<'a> Debug for Bytes<'a> {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 write!(f, "{:#02x?}", self.0)
250 }
251}
252
253impl<'a> Display for Bytes<'a> {
254 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255 write!(f, "{:#02x?}", self.0)
256 }
257}
258
259impl<'a> LowerHex for Bytes<'a> {
260 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
261 write!(f, "{:#02x?}", self.0)
262 }
263}
264
265#[cfg(feature = "defmt")]
266impl<'a> defmt::Format for Bytes<'a> {
267 fn format(&self, fmt: defmt::Formatter) {
268 defmt::write!(fmt, "{:02x}", self.0)
269 }
270}
diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs
new file mode 100644
index 000000000..e1eb7eecf
--- /dev/null
+++ b/embassy-mspm0/src/gpio.rs
@@ -0,0 +1,1060 @@
1#![macro_use]
2
3use core::convert::Infallible;
4use core::future::Future;
5use core::pin::Pin as FuturePin;
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10
11use crate::pac::gpio::vals::*;
12use crate::pac::gpio::{self};
13#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
14use crate::pac::interrupt;
15use crate::pac::{self};
16
17/// Represents a digital input or output level.
18#[derive(Debug, Eq, PartialEq, Clone, Copy)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Level {
21 /// Logical low.
22 Low,
23 /// Logical high.
24 High,
25}
26
27impl From<bool> for Level {
28 fn from(val: bool) -> Self {
29 match val {
30 true => Self::High,
31 false => Self::Low,
32 }
33 }
34}
35
36impl From<Level> for bool {
37 fn from(level: Level) -> bool {
38 match level {
39 Level::Low => false,
40 Level::High => true,
41 }
42 }
43}
44
45/// Represents a pull setting for an input.
46#[derive(Debug, Clone, Copy, Eq, PartialEq)]
47pub enum Pull {
48 /// No pull.
49 None,
50 /// Internal pull-up resistor.
51 Up,
52 /// Internal pull-down resistor.
53 Down,
54}
55
56/// A GPIO bank with up to 32 pins.
57#[derive(Debug, Clone, Copy, Eq, PartialEq)]
58pub enum Port {
59 /// Port A.
60 PortA = 0,
61
62 /// Port B.
63 #[cfg(gpio_pb)]
64 PortB = 1,
65
66 /// Port C.
67 #[cfg(gpio_pc)]
68 PortC = 2,
69}
70
71/// GPIO flexible pin.
72///
73/// This pin can either be a disconnected, input, or output pin, or both. The level register bit will remain
74/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
75/// mode.
76pub struct Flex<'d> {
77 pin: PeripheralRef<'d, AnyPin>,
78}
79
80impl<'d> Flex<'d> {
81 /// Wrap the pin in a `Flex`.
82 ///
83 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
84 /// before the pin is put into output mode.
85 #[inline]
86 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd) -> Self {
87 into_ref!(pin);
88
89 // Pin will be in disconnected state.
90 Self { pin: pin.map_into() }
91 }
92
93 /// Set the pin's pull.
94 #[inline]
95 pub fn set_pull(&mut self, pull: Pull) {
96 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
97
98 pincm.modify(|w| {
99 w.set_pipd(matches!(pull, Pull::Down));
100 w.set_pipu(matches!(pull, Pull::Up));
101 });
102 }
103
104 /// Put the pin into input mode.
105 ///
106 /// The pull setting is left unchanged.
107 #[inline]
108 pub fn set_as_input(&mut self) {
109 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
110
111 pincm.modify(|w| {
112 w.set_pf(GPIO_PF);
113 w.set_hiz1(false);
114 w.set_pc(true);
115 w.set_inena(true);
116 });
117
118 self.pin.block().doeclr31_0().write(|w| {
119 w.set_dio(self.pin.bit_index(), true);
120 });
121 }
122
123 /// Put the pin into output mode.
124 ///
125 /// The pin level will be whatever was set before (or low by default). If you want it to begin
126 /// at a specific level, call `set_high`/`set_low` on the pin first.
127 #[inline]
128 pub fn set_as_output(&mut self) {
129 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
130
131 pincm.modify(|w| {
132 w.set_pf(GPIO_PF);
133 w.set_hiz1(false);
134 w.set_pc(true);
135 w.set_inena(false);
136 });
137
138 self.pin.block().doeset31_0().write(|w| {
139 w.set_dio(self.pin.bit_index(), true);
140 });
141 }
142
143 /// Put the pin into input + open-drain output mode.
144 ///
145 /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
146 /// it to high, in which case you can read the input to figure out whether another device
147 /// is driving the line low.
148 ///
149 /// The pin level will be whatever was set before (or low by default). If you want it to begin
150 /// at a specific level, call `set_high`/`set_low` on the pin first.
151 ///
152 /// The internal weak pull-up and pull-down resistors will be disabled.
153 #[inline]
154 pub fn set_as_input_output(&mut self) {
155 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
156
157 pincm.modify(|w| {
158 w.set_pf(GPIO_PF);
159 w.set_hiz1(true);
160 w.set_pc(true);
161 w.set_inena(false);
162 });
163
164 self.set_pull(Pull::None);
165 }
166
167 /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
168 /// amount of power possible.
169 ///
170 /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
171 /// really. Drivers should `set_as_disconnected()` pins when dropped.
172 ///
173 /// Note that this also disables the internal weak pull-up and pull-down resistors.
174 #[inline]
175 pub fn set_as_disconnected(&mut self) {
176 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
177
178 pincm.modify(|w| {
179 w.set_pf(DISCONNECT_PF);
180 w.set_hiz1(false);
181 w.set_pc(false);
182 w.set_inena(false);
183 });
184
185 self.set_pull(Pull::None);
186 self.set_inversion(false);
187 }
188
189 /// Configure the logic inversion of this pin.
190 ///
191 /// Logic inversion applies to both the input and output path of this pin.
192 #[inline]
193 pub fn set_inversion(&mut self, invert: bool) {
194 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
195
196 pincm.modify(|w| {
197 w.set_inv(invert);
198 });
199 }
200
201 // TODO: drive strength, hysteresis, wakeup enable, wakeup compare
202
203 /// Put the pin into the PF mode, unchecked.
204 ///
205 /// This puts the pin into the PF mode, with the request number. This is completely unchecked,
206 /// it can attach the pin to literally any peripheral, so use with care. In addition the pin
207 /// peripheral is connected in the iomux.
208 ///
209 /// The peripheral attached to the pin depends on the part in use. Consult the datasheet
210 /// or technical reference manual for additional details.
211 #[inline]
212 pub fn set_pf_unchecked(&mut self, pf: u8) {
213 // Per SLAU893, PF is only 5 bits
214 assert!((pf & 0x3F) != 0, "PF is out of range");
215
216 let pincm = pac::IOMUX.pincm(self.pin.pin_cm() as usize);
217
218 pincm.modify(|w| {
219 w.set_pf(pf);
220 // If the PF is manually set, connect the pin
221 w.set_pc(true);
222 });
223 }
224
225 /// Get whether the pin input level is high.
226 #[inline]
227 pub fn is_high(&self) -> bool {
228 !self.is_low()
229 }
230
231 /// Get whether the pin input level is low.
232 #[inline]
233 pub fn is_low(&self) -> bool {
234 self.pin.block().din31_0().read().dio(self.pin.bit_index())
235 }
236
237 /// Returns current pin level
238 #[inline]
239 pub fn get_level(&self) -> Level {
240 self.is_high().into()
241 }
242
243 /// Set the output as high.
244 #[inline]
245 pub fn set_high(&mut self) {
246 self.pin.block().doutset31_0().write(|w| {
247 w.set_dio(self.pin.bit_index() as usize, true);
248 });
249 }
250
251 /// Set the output as low.
252 #[inline]
253 pub fn set_low(&mut self) {
254 self.pin.block().doutclr31_0().write(|w| {
255 w.set_dio(self.pin.bit_index(), true);
256 });
257 }
258
259 /// Toggle pin output
260 #[inline]
261 pub fn toggle(&mut self) {
262 self.pin.block().douttgl31_0().write(|w| {
263 w.set_dio(self.pin.bit_index(), true);
264 })
265 }
266
267 /// Set the output level.
268 #[inline]
269 pub fn set_level(&mut self, level: Level) {
270 match level {
271 Level::Low => self.set_low(),
272 Level::High => self.set_high(),
273 }
274 }
275
276 /// Get the current pin input level.
277 #[inline]
278 pub fn get_output_level(&self) -> Level {
279 self.is_high().into()
280 }
281
282 /// Is the output level high?
283 #[inline]
284 pub fn is_set_high(&self) -> bool {
285 !self.is_set_low()
286 }
287
288 /// Is the output level low?
289 #[inline]
290 pub fn is_set_low(&self) -> bool {
291 (self.pin.block().dout31_0().read().0 & self.pin.bit_index() as u32) == 0
292 }
293
294 /// Wait until the pin is high. If it is already high, return immediately.
295 #[inline]
296 pub async fn wait_for_high(&mut self) {
297 if self.is_high() {
298 return;
299 }
300
301 self.wait_for_rising_edge().await
302 }
303
304 /// Wait until the pin is low. If it is already low, return immediately.
305 #[inline]
306 pub async fn wait_for_low(&mut self) {
307 if self.is_low() {
308 return;
309 }
310
311 self.wait_for_falling_edge().await
312 }
313
314 /// Wait for the pin to undergo a transition from low to high.
315 #[inline]
316 pub async fn wait_for_rising_edge(&mut self) {
317 InputFuture::new(self.pin.reborrow(), Polarity::RISE).await
318 }
319
320 /// Wait for the pin to undergo a transition from high to low.
321 #[inline]
322 pub async fn wait_for_falling_edge(&mut self) {
323 InputFuture::new(self.pin.reborrow(), Polarity::FALL).await
324 }
325
326 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
327 #[inline]
328 pub async fn wait_for_any_edge(&mut self) {
329 InputFuture::new(self.pin.reborrow(), Polarity::RISE_FALL).await
330 }
331}
332
333impl<'d> Drop for Flex<'d> {
334 #[inline]
335 fn drop(&mut self) {
336 self.set_as_disconnected();
337 }
338}
339
340/// GPIO input driver.
341pub struct Input<'d> {
342 pin: Flex<'d>,
343}
344
345impl<'d> Input<'d> {
346 /// Create GPIO input driver for a [Pin] with the provided [Pull] configuration.
347 #[inline]
348 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, pull: Pull) -> Self {
349 let mut pin = Flex::new(pin);
350 pin.set_as_input();
351 pin.set_pull(pull);
352 Self { pin }
353 }
354
355 /// Get whether the pin input level is high.
356 #[inline]
357 pub fn is_high(&self) -> bool {
358 self.pin.is_high()
359 }
360
361 /// Get whether the pin input level is low.
362 #[inline]
363 pub fn is_low(&self) -> bool {
364 self.pin.is_low()
365 }
366
367 /// Get the current pin input level.
368 #[inline]
369 pub fn get_level(&self) -> Level {
370 self.pin.get_level()
371 }
372
373 /// Configure the logic inversion of this pin.
374 ///
375 /// Logic inversion applies to the input path of this pin.
376 #[inline]
377 pub fn set_inversion(&mut self, invert: bool) {
378 self.pin.set_inversion(invert)
379 }
380
381 /// Wait until the pin is high. If it is already high, return immediately.
382 #[inline]
383 pub async fn wait_for_high(&mut self) {
384 self.pin.wait_for_high().await
385 }
386
387 /// Wait until the pin is low. If it is already low, return immediately.
388 #[inline]
389 pub async fn wait_for_low(&mut self) {
390 self.pin.wait_for_low().await
391 }
392
393 /// Wait for the pin to undergo a transition from low to high.
394 #[inline]
395 pub async fn wait_for_rising_edge(&mut self) {
396 self.pin.wait_for_rising_edge().await
397 }
398
399 /// Wait for the pin to undergo a transition from high to low.
400 #[inline]
401 pub async fn wait_for_falling_edge(&mut self) {
402 self.pin.wait_for_falling_edge().await
403 }
404
405 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
406 #[inline]
407 pub async fn wait_for_any_edge(&mut self) {
408 self.pin.wait_for_any_edge().await
409 }
410}
411
412/// GPIO output driver.
413///
414/// Note that pins will **return to their floating state** when `Output` is dropped.
415/// If pins should retain their state indefinitely, either keep ownership of the
416/// `Output`, or pass it to [`core::mem::forget`].
417pub struct Output<'d> {
418 pin: Flex<'d>,
419}
420
421impl<'d> Output<'d> {
422 /// Create GPIO output driver for a [Pin] with the provided [Level] configuration.
423 #[inline]
424 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
425 let mut pin = Flex::new(pin);
426 pin.set_as_output();
427 pin.set_level(initial_output);
428 Self { pin }
429 }
430
431 /// Set the output as high.
432 #[inline]
433 pub fn set_high(&mut self) {
434 self.pin.set_high();
435 }
436
437 /// Set the output as low.
438 #[inline]
439 pub fn set_low(&mut self) {
440 self.pin.set_low();
441 }
442
443 /// Set the output level.
444 #[inline]
445 pub fn set_level(&mut self, level: Level) {
446 self.pin.set_level(level)
447 }
448
449 /// Is the output pin set as high?
450 #[inline]
451 pub fn is_set_high(&self) -> bool {
452 self.pin.is_set_high()
453 }
454
455 /// Is the output pin set as low?
456 #[inline]
457 pub fn is_set_low(&self) -> bool {
458 self.pin.is_set_low()
459 }
460
461 /// What level output is set to
462 #[inline]
463 pub fn get_output_level(&self) -> Level {
464 self.pin.get_output_level()
465 }
466
467 /// Toggle pin output
468 #[inline]
469 pub fn toggle(&mut self) {
470 self.pin.toggle();
471 }
472
473 /// Configure the logic inversion of this pin.
474 ///
475 /// Logic inversion applies to the input path of this pin.
476 #[inline]
477 pub fn set_inversion(&mut self, invert: bool) {
478 self.pin.set_inversion(invert)
479 }
480}
481
482/// GPIO output open-drain driver.
483///
484/// Note that pins will **return to their floating state** when `OutputOpenDrain` is dropped.
485/// If pins should retain their state indefinitely, either keep ownership of the
486/// `OutputOpenDrain`, or pass it to [`core::mem::forget`].
487pub struct OutputOpenDrain<'d> {
488 pin: Flex<'d>,
489}
490
491impl<'d> OutputOpenDrain<'d> {
492 /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level].
493 #[inline]
494 pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level) -> Self {
495 let mut pin = Flex::new(pin);
496 pin.set_level(initial_output);
497 pin.set_as_input_output();
498 Self { pin }
499 }
500
501 /// Get whether the pin input level is high.
502 #[inline]
503 pub fn is_high(&self) -> bool {
504 !self.pin.is_low()
505 }
506
507 /// Get whether the pin input level is low.
508 #[inline]
509 pub fn is_low(&self) -> bool {
510 self.pin.is_low()
511 }
512
513 /// Get the current pin input level.
514 #[inline]
515 pub fn get_level(&self) -> Level {
516 self.pin.get_level()
517 }
518
519 /// Set the output as high.
520 #[inline]
521 pub fn set_high(&mut self) {
522 self.pin.set_high();
523 }
524
525 /// Set the output as low.
526 #[inline]
527 pub fn set_low(&mut self) {
528 self.pin.set_low();
529 }
530
531 /// Set the output level.
532 #[inline]
533 pub fn set_level(&mut self, level: Level) {
534 self.pin.set_level(level);
535 }
536
537 /// Get whether the output level is set to high.
538 #[inline]
539 pub fn is_set_high(&self) -> bool {
540 self.pin.is_set_high()
541 }
542
543 /// Get whether the output level is set to low.
544 #[inline]
545 pub fn is_set_low(&self) -> bool {
546 self.pin.is_set_low()
547 }
548
549 /// Get the current output level.
550 #[inline]
551 pub fn get_output_level(&self) -> Level {
552 self.pin.get_output_level()
553 }
554
555 /// Toggle pin output
556 #[inline]
557 pub fn toggle(&mut self) {
558 self.pin.toggle()
559 }
560
561 /// Configure the logic inversion of this pin.
562 ///
563 /// Logic inversion applies to the input path of this pin.
564 #[inline]
565 pub fn set_inversion(&mut self, invert: bool) {
566 self.pin.set_inversion(invert)
567 }
568
569 /// Wait until the pin is high. If it is already high, return immediately.
570 #[inline]
571 pub async fn wait_for_high(&mut self) {
572 self.pin.wait_for_high().await
573 }
574
575 /// Wait until the pin is low. If it is already low, return immediately.
576 #[inline]
577 pub async fn wait_for_low(&mut self) {
578 self.pin.wait_for_low().await
579 }
580
581 /// Wait for the pin to undergo a transition from low to high.
582 #[inline]
583 pub async fn wait_for_rising_edge(&mut self) {
584 self.pin.wait_for_rising_edge().await
585 }
586
587 /// Wait for the pin to undergo a transition from high to low.
588 #[inline]
589 pub async fn wait_for_falling_edge(&mut self) {
590 self.pin.wait_for_falling_edge().await
591 }
592
593 /// Wait for the pin to undergo any transition, i.e low to high OR high to low.
594 #[inline]
595 pub async fn wait_for_any_edge(&mut self) {
596 self.pin.wait_for_any_edge().await
597 }
598}
599
600/// Type-erased GPIO pin
601pub struct AnyPin {
602 pin_port: u8,
603}
604
605impl AnyPin {
606 /// Create an [AnyPin] for a specific pin.
607 ///
608 /// # Safety
609 /// - `pin_port` should not in use by another driver.
610 #[inline]
611 pub unsafe fn steal(pin_port: u8) -> Self {
612 Self { pin_port }
613 }
614}
615
616impl_peripheral!(AnyPin);
617
618impl Pin for AnyPin {}
619impl SealedPin for AnyPin {
620 #[inline]
621 fn pin_port(&self) -> u8 {
622 self.pin_port
623 }
624}
625
626/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an [AnyPin].
627#[allow(private_bounds)]
628pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static {
629 fn degrade(self) -> AnyPin {
630 AnyPin {
631 pin_port: self.pin_port(),
632 }
633 }
634
635 /// The index of this pin in PINCM (pin control management) registers.
636 #[inline]
637 fn pin_cm(&self) -> u8 {
638 self._pin_cm()
639 }
640}
641
642impl<'d> embedded_hal::digital::ErrorType for Flex<'d> {
643 type Error = Infallible;
644}
645
646impl<'d> embedded_hal::digital::InputPin for Flex<'d> {
647 #[inline]
648 fn is_high(&mut self) -> Result<bool, Self::Error> {
649 Ok((*self).is_high())
650 }
651
652 #[inline]
653 fn is_low(&mut self) -> Result<bool, Self::Error> {
654 Ok((*self).is_low())
655 }
656}
657
658impl<'d> embedded_hal::digital::OutputPin for Flex<'d> {
659 #[inline]
660 fn set_low(&mut self) -> Result<(), Self::Error> {
661 Ok(self.set_low())
662 }
663
664 #[inline]
665 fn set_high(&mut self) -> Result<(), Self::Error> {
666 Ok(self.set_high())
667 }
668}
669
670impl<'d> embedded_hal::digital::StatefulOutputPin for Flex<'d> {
671 #[inline]
672 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
673 Ok((*self).is_set_high())
674 }
675
676 #[inline]
677 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
678 Ok((*self).is_set_low())
679 }
680}
681
682impl<'d> embedded_hal_async::digital::Wait for Flex<'d> {
683 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
684 self.wait_for_high().await;
685 Ok(())
686 }
687
688 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
689 self.wait_for_low().await;
690 Ok(())
691 }
692
693 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
694 self.wait_for_rising_edge().await;
695 Ok(())
696 }
697
698 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
699 self.wait_for_falling_edge().await;
700 Ok(())
701 }
702
703 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
704 self.wait_for_any_edge().await;
705 Ok(())
706 }
707}
708
709impl<'d> embedded_hal::digital::ErrorType for Input<'d> {
710 type Error = Infallible;
711}
712
713impl<'d> embedded_hal::digital::InputPin for Input<'d> {
714 #[inline]
715 fn is_high(&mut self) -> Result<bool, Self::Error> {
716 Ok((*self).is_high())
717 }
718
719 #[inline]
720 fn is_low(&mut self) -> Result<bool, Self::Error> {
721 Ok((*self).is_low())
722 }
723}
724
725impl<'d> embedded_hal_async::digital::Wait for Input<'d> {
726 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
727 self.wait_for_high().await;
728 Ok(())
729 }
730
731 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
732 self.wait_for_low().await;
733 Ok(())
734 }
735
736 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
737 self.wait_for_rising_edge().await;
738 Ok(())
739 }
740
741 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
742 self.wait_for_falling_edge().await;
743 Ok(())
744 }
745
746 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
747 self.wait_for_any_edge().await;
748 Ok(())
749 }
750}
751
752impl<'d> embedded_hal::digital::ErrorType for Output<'d> {
753 type Error = Infallible;
754}
755
756impl<'d> embedded_hal::digital::OutputPin for Output<'d> {
757 #[inline]
758 fn set_low(&mut self) -> Result<(), Self::Error> {
759 Ok(self.set_low())
760 }
761
762 #[inline]
763 fn set_high(&mut self) -> Result<(), Self::Error> {
764 Ok(self.set_high())
765 }
766}
767
768impl<'d> embedded_hal::digital::StatefulOutputPin for Output<'d> {
769 #[inline]
770 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
771 Ok((*self).is_set_high())
772 }
773
774 #[inline]
775 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
776 Ok((*self).is_set_low())
777 }
778}
779
780impl<'d> embedded_hal::digital::ErrorType for OutputOpenDrain<'d> {
781 type Error = Infallible;
782}
783
784impl<'d> embedded_hal::digital::InputPin for OutputOpenDrain<'d> {
785 #[inline]
786 fn is_high(&mut self) -> Result<bool, Self::Error> {
787 Ok((*self).is_high())
788 }
789
790 #[inline]
791 fn is_low(&mut self) -> Result<bool, Self::Error> {
792 Ok((*self).is_low())
793 }
794}
795
796impl<'d> embedded_hal::digital::OutputPin for OutputOpenDrain<'d> {
797 #[inline]
798 fn set_low(&mut self) -> Result<(), Self::Error> {
799 Ok(self.set_low())
800 }
801
802 #[inline]
803 fn set_high(&mut self) -> Result<(), Self::Error> {
804 Ok(self.set_high())
805 }
806}
807
808impl<'d> embedded_hal::digital::StatefulOutputPin for OutputOpenDrain<'d> {
809 #[inline]
810 fn is_set_high(&mut self) -> Result<bool, Self::Error> {
811 Ok((*self).is_set_high())
812 }
813
814 #[inline]
815 fn is_set_low(&mut self) -> Result<bool, Self::Error> {
816 Ok((*self).is_set_low())
817 }
818}
819
820impl<'d> embedded_hal_async::digital::Wait for OutputOpenDrain<'d> {
821 async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
822 self.wait_for_high().await;
823 Ok(())
824 }
825
826 async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
827 self.wait_for_low().await;
828 Ok(())
829 }
830
831 async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
832 self.wait_for_rising_edge().await;
833 Ok(())
834 }
835
836 async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
837 self.wait_for_falling_edge().await;
838 Ok(())
839 }
840
841 async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
842 self.wait_for_any_edge().await;
843 Ok(())
844 }
845}
846
847/// The pin function to disconnect peripherals from the pin.
848///
849/// This is also the pin function used to connect to analog peripherals, such as an ADC.
850const DISCONNECT_PF: u8 = 0;
851
852/// The pin function for the GPIO peripheral.
853///
854/// This is fixed to `1` for every part.
855const GPIO_PF: u8 = 1;
856
857macro_rules! impl_pin {
858 ($name: ident, $port: expr, $pin_num: expr) => {
859 impl crate::gpio::Pin for crate::peripherals::$name {}
860 impl crate::gpio::SealedPin for crate::peripherals::$name {
861 #[inline]
862 fn pin_port(&self) -> u8 {
863 ($port as u8) * 32 + $pin_num
864 }
865 }
866
867 impl From<crate::peripherals::$name> for crate::gpio::AnyPin {
868 fn from(val: crate::peripherals::$name) -> Self {
869 crate::gpio::Pin::degrade(val)
870 }
871 }
872 };
873}
874
875// TODO: Possible micro-op for C110X, not every pin is instantiated even on the 20 pin parts.
876// This would mean cfg guarding to just cfg guarding every pin instance.
877static PORTA_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
878#[cfg(gpio_pb)]
879static PORTB_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
880#[cfg(gpio_pc)]
881static PORTC_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32];
882
883pub(crate) trait SealedPin {
884 fn pin_port(&self) -> u8;
885
886 fn port(&self) -> Port {
887 match self.pin_port() / 32 {
888 0 => Port::PortA,
889 #[cfg(gpio_pb)]
890 1 => Port::PortB,
891 #[cfg(gpio_pc)]
892 2 => Port::PortC,
893 _ => unreachable!(),
894 }
895 }
896
897 fn waker(&self) -> &AtomicWaker {
898 match self.port() {
899 Port::PortA => &PORTA_WAKERS[self.bit_index()],
900 #[cfg(gpio_pb)]
901 Port::PortB => &PORTB_WAKERS[self.bit_index()],
902 #[cfg(gpio_pc)]
903 Port::PortC => &PORTC_WAKERS[self.bit_index()],
904 }
905 }
906
907 fn _pin_cm(&self) -> u8 {
908 // Some parts like the MSPM0L222x have pincm mappings all over the place.
909 crate::gpio_pincm(self.pin_port())
910 }
911
912 fn bit_index(&self) -> usize {
913 (self.pin_port() % 32) as usize
914 }
915
916 #[inline]
917 fn block(&self) -> gpio::Gpio {
918 match self.pin_port() / 32 {
919 0 => pac::GPIOA,
920 #[cfg(gpio_pb)]
921 1 => pac::GPIOB,
922 #[cfg(gpio_pc)]
923 2 => pac::GPIOC,
924 _ => unreachable!(),
925 }
926 }
927}
928
929#[must_use = "futures do nothing unless you `.await` or poll them"]
930struct InputFuture<'d> {
931 pin: PeripheralRef<'d, AnyPin>,
932}
933
934impl<'d> InputFuture<'d> {
935 fn new(pin: PeripheralRef<'d, AnyPin>, polarity: Polarity) -> Self {
936 let block = pin.block();
937
938 // First clear the bit for this event. Otherwise previous edge events may be recorded.
939 block.cpu_int().iclr().write(|w| {
940 w.set_dio(pin.bit_index(), true);
941 });
942
943 // Selecting which polarity events happens is a RMW operation.
944 //
945 // Guard with a critical section in case two different threads try to select events at the
946 // same time.
947 critical_section::with(|_cs| {
948 // Tell the hardware which pin event we want to receive.
949 if pin.bit_index() >= 16 {
950 block.polarity31_16().modify(|w| {
951 w.set_dio(pin.bit_index() - 16, polarity);
952 });
953 } else {
954 block.polarity15_0().modify(|w| {
955 w.set_dio(pin.bit_index(), polarity);
956 });
957 };
958 });
959
960 Self { pin }
961 }
962}
963
964impl<'d> Future for InputFuture<'d> {
965 type Output = ();
966
967 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
968 // We need to register/re-register the waker for each poll because any
969 // calls to wake will deregister the waker.
970 let waker = self.pin.waker();
971 waker.register(cx.waker());
972
973 // The interrupt handler will mask the interrupt if the event has occurred.
974 if self.pin.block().cpu_int().ris().read().dio(self.pin.bit_index()) {
975 return Poll::Ready(());
976 }
977
978 // Unmasking the interrupt is a RMW operation.
979 //
980 // Guard with a critical section in case two different threads try to unmask at the same time.
981 critical_section::with(|_cs| {
982 self.pin.block().cpu_int().imask().modify(|w| {
983 w.set_dio(self.pin.bit_index(), true);
984 });
985 });
986
987 Poll::Pending
988 }
989}
990
991pub(crate) fn init(gpio: gpio::Gpio) {
992 gpio.gprcm().rstctl().write(|w| {
993 w.set_resetstkyclr(true);
994 w.set_resetassert(true);
995 w.set_key(ResetKey::KEY);
996 });
997
998 gpio.gprcm().pwren().write(|w| {
999 w.set_enable(true);
1000 w.set_key(PwrenKey::KEY);
1001 });
1002
1003 gpio.evt_mode().modify(|w| {
1004 // The CPU will clear it's own interrupts
1005 w.set_cpu_cfg(EvtCfg::SOFTWARE);
1006 });
1007}
1008
1009#[cfg(feature = "rt")]
1010fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) {
1011 // Only consider pins which have interrupts unmasked.
1012 let bits = gpio.cpu_int().mis().read().0;
1013
1014 for i in BitIter(bits) {
1015 wakers[i as usize].wake();
1016
1017 // Notify the future that an edge event has occurred by masking the interrupt for this pin.
1018 gpio.cpu_int().imask().modify(|w| {
1019 w.set_dio(i as usize, false);
1020 });
1021 }
1022}
1023
1024struct BitIter(u32);
1025
1026impl Iterator for BitIter {
1027 type Item = u32;
1028
1029 fn next(&mut self) -> Option<Self::Item> {
1030 match self.0.trailing_zeros() {
1031 32 => None,
1032 b => {
1033 self.0 &= !(1 << b);
1034 Some(b)
1035 }
1036 }
1037 }
1038}
1039
1040// C110x has a dedicated interrupt just for GPIOA, as it does not have a GROUP1 interrupt.
1041#[cfg(all(feature = "rt", feature = "mspm0c110x"))]
1042#[interrupt]
1043fn GPIOA() {
1044 gpioa_interrupt();
1045}
1046
1047#[cfg(feature = "rt")]
1048pub(crate) fn gpioa_interrupt() {
1049 irq_handler(pac::GPIOA, &PORTA_WAKERS);
1050}
1051
1052#[cfg(all(feature = "rt", gpio_pb))]
1053pub(crate) fn gpiob_interrupt() {
1054 irq_handler(pac::GPIOB, &PORTB_WAKERS);
1055}
1056
1057#[cfg(all(feature = "rt", gpio_pc))]
1058pub(crate) fn gpioc_interrupt() {
1059 irq_handler(pac::GPIOC, &PORTC_WAKERS);
1060}
diff --git a/embassy-mspm0/src/int_group/c110x.rs b/embassy-mspm0/src/int_group/c110x.rs
new file mode 100644
index 000000000..e6a9ddb99
--- /dev/null
+++ b/embassy-mspm0/src/int_group/c110x.rs
@@ -0,0 +1,25 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // TODO: Decompose to direct u8
12 let iidx = group.iidx().read().stat().to_bits();
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
diff --git a/embassy-mspm0/src/int_group/g350x.rs b/embassy-mspm0/src/int_group/g350x.rs
new file mode 100644
index 000000000..706ba2078
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g350x.rs
@@ -0,0 +1,51 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::WWDT1 => todo!("implement WWDT1"),
22 Group0::DEBUGSS => todo!("implement DEBUGSS"),
23 Group0::FLASHCTL => todo!("implement FLASHCTL"),
24 Group0::SYSCTL => todo!("implement SYSCTL"),
25 }
26}
27
28#[cfg(feature = "rt")]
29#[interrupt]
30fn GROUP1() {
31 use mspm0_metapac::Group1;
32
33 let group = pac::CPUSS.int_group(1);
34
35 // Must subtract by 1 since NO_INTR is value 0
36 let iidx = group.iidx().read().stat().to_bits() - 1;
37
38 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
39 debug!("Invalid IIDX for group 1: {}", iidx);
40 return;
41 };
42
43 match group {
44 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
45 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
46 Group1::COMP0 => todo!("implement COMP0"),
47 Group1::COMP1 => todo!("implement COMP1"),
48 Group1::COMP2 => todo!("implement COMP2"),
49 Group1::TRNG => todo!("implement TRNG"),
50 }
51}
diff --git a/embassy-mspm0/src/int_group/g351x.rs b/embassy-mspm0/src/int_group/g351x.rs
new file mode 100644
index 000000000..e785018a7
--- /dev/null
+++ b/embassy-mspm0/src/int_group/g351x.rs
@@ -0,0 +1,52 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::WWDT1 => todo!("implement WWDT1"),
22 Group0::DEBUGSS => todo!("implement DEBUGSS"),
23 Group0::FLASHCTL => todo!("implement FLASHCTL"),
24 Group0::SYSCTL => todo!("implement SYSCTL"),
25 }
26}
27
28#[cfg(feature = "rt")]
29#[interrupt]
30fn GROUP1() {
31 use mspm0_metapac::Group1;
32
33 let group = pac::CPUSS.int_group(1);
34
35 // Must subtract by 1 since NO_INTR is value 0
36 let iidx = group.iidx().read().stat().to_bits() - 1;
37
38 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
39 debug!("Invalid IIDX for group 1: {}", iidx);
40 return;
41 };
42
43 match group {
44 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
45 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
46 Group1::COMP0 => todo!("implement COMP0"),
47 Group1::COMP1 => todo!("implement COMP1"),
48 Group1::COMP2 => todo!("implement COMP2"),
49 Group1::TRNG => todo!("implement TRNG"),
50 Group1::GPIOC => crate::gpio::gpioc_interrupt(),
51 }
52}
diff --git a/embassy-mspm0/src/int_group/l130x.rs b/embassy-mspm0/src/int_group/l130x.rs
new file mode 100644
index 000000000..8be5adcad
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l130x.rs
@@ -0,0 +1,46 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
26
27#[cfg(feature = "rt")]
28#[interrupt]
29fn GROUP1() {
30 use mspm0_metapac::Group1;
31
32 let group = pac::CPUSS.int_group(1);
33
34 // Must subtract by 1 since NO_INTR is value 0
35 let iidx = group.iidx().read().stat().to_bits() - 1;
36
37 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
38 debug!("Invalid IIDX for group 1: {}", iidx);
39 return;
40 };
41
42 match group {
43 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
44 Group1::COMP0 => todo!("implement COMP0"),
45 }
46}
diff --git a/embassy-mspm0/src/int_group/l222x.rs b/embassy-mspm0/src/int_group/l222x.rs
new file mode 100644
index 000000000..eeb2ce70d
--- /dev/null
+++ b/embassy-mspm0/src/int_group/l222x.rs
@@ -0,0 +1,49 @@
1use crate::pac;
2use crate::pac::interrupt;
3
4#[cfg(feature = "rt")]
5#[interrupt]
6fn GROUP0() {
7 use mspm0_metapac::Group0;
8
9 let group = pac::CPUSS.int_group(0);
10
11 // Must subtract by 1 since NO_INTR is value 0
12 let iidx = group.iidx().read().stat().to_bits() - 1;
13
14 let Ok(group) = pac::Group0::try_from(iidx as u8) else {
15 debug!("Invalid IIDX for group 0: {}", iidx);
16 return;
17 };
18
19 match group {
20 Group0::WWDT0 => todo!("implement WWDT0"),
21 Group0::DEBUGSS => todo!("implement DEBUGSS"),
22 Group0::FLASHCTL => todo!("implement FLASHCTL"),
23 Group0::SYSCTL => todo!("implement SYSCTL"),
24 }
25}
26
27#[cfg(feature = "rt")]
28#[interrupt]
29fn GROUP1() {
30 use mspm0_metapac::Group1;
31
32 let group = pac::CPUSS.int_group(1);
33
34 // Must subtract by 1 since NO_INTR is value 0
35 let iidx = group.iidx().read().stat().to_bits() - 1;
36
37 let Ok(group) = pac::Group1::try_from(iidx as u8) else {
38 debug!("Invalid IIDX for group 1: {}", iidx);
39 return;
40 };
41
42 match group {
43 Group1::GPIOA => crate::gpio::gpioa_interrupt(),
44 Group1::GPIOB => crate::gpio::gpiob_interrupt(),
45 Group1::COMP0 => todo!("implement COMP0"),
46 Group1::TRNG => todo!("implement TRNG"),
47 Group1::GPIOC => crate::gpio::gpioc_interrupt(),
48 }
49}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
new file mode 100644
index 000000000..1191b1010
--- /dev/null
+++ b/embassy-mspm0/src/lib.rs
@@ -0,0 +1,107 @@
1#![no_std]
2// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
3#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
4
5// This mod MUST go first, so that the others see its macros.
6pub(crate) mod fmt;
7
8pub mod gpio;
9pub mod timer;
10
11#[cfg(feature = "_time-driver")]
12mod time_driver;
13
14// Interrupt group handlers.
15#[cfg_attr(feature = "mspm0c110x", path = "int_group/c110x.rs")]
16#[cfg_attr(feature = "mspm0g110x", path = "int_group/g110x.rs")]
17#[cfg_attr(feature = "mspm0g150x", path = "int_group/g150x.rs")]
18#[cfg_attr(feature = "mspm0g151x", path = "int_group/g151x.rs")]
19#[cfg_attr(feature = "mspm0g310x", path = "int_group/g310x.rs")]
20#[cfg_attr(feature = "mspm0g350x", path = "int_group/g350x.rs")]
21#[cfg_attr(feature = "mspm0g351x", path = "int_group/g351x.rs")]
22#[cfg_attr(feature = "mspm0l110x", path = "int_group/l110x.rs")]
23#[cfg_attr(feature = "mspm0l122x", path = "int_group/l122x.rs")]
24#[cfg_attr(feature = "mspm0l130x", path = "int_group/l130x.rs")]
25#[cfg_attr(feature = "mspm0l134x", path = "int_group/l134x.rs")]
26#[cfg_attr(feature = "mspm0l222x", path = "int_group/l222x.rs")]
27mod int_group;
28
29pub(crate) mod _generated {
30 #![allow(dead_code)]
31 #![allow(unused_imports)]
32 #![allow(non_snake_case)]
33 #![allow(missing_docs)]
34
35 include!(concat!(env!("OUT_DIR"), "/_generated.rs"));
36}
37
38// Reexports
39pub(crate) use _generated::gpio_pincm;
40pub use _generated::{peripherals, Peripherals};
41pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
42#[cfg(feature = "unstable-pac")]
43pub use mspm0_metapac as pac;
44#[cfg(not(feature = "unstable-pac"))]
45pub(crate) use mspm0_metapac as pac;
46
47pub use crate::_generated::interrupt;
48
49/// `embassy-mspm0` global configuration.
50#[non_exhaustive]
51#[derive(Clone, Copy)]
52pub struct Config {
53 // TODO
54}
55
56impl Default for Config {
57 fn default() -> Self {
58 Self {
59 // TODO
60 }
61 }
62}
63
64pub fn init(_config: Config) -> Peripherals {
65 critical_section::with(|cs| {
66 let peripherals = Peripherals::take_with_cs(cs);
67
68 // TODO: Further clock configuration
69
70 pac::SYSCTL.mclkcfg().modify(|w| {
71 // Enable MFCLK
72 w.set_usemftick(true);
73 // MDIV must be disabled if MFCLK is enabled.
74 w.set_mdiv(0);
75 });
76
77 // Enable MFCLK for peripheral use
78 //
79 // TODO: Optional?
80 pac::SYSCTL.genclken().modify(|w| {
81 w.set_mfpclken(true);
82 });
83
84 pac::SYSCTL.borthreshold().modify(|w| {
85 w.set_level(0);
86 });
87
88 gpio::init(pac::GPIOA);
89 #[cfg(gpio_pb)]
90 gpio::init(pac::GPIOB);
91 #[cfg(gpio_pc)]
92 gpio::init(pac::GPIOC);
93
94 _generated::enable_group_interrupts(cs);
95
96 #[cfg(feature = "mspm0c110x")]
97 unsafe {
98 use crate::_generated::interrupt::typelevel::Interrupt;
99 crate::interrupt::typelevel::GPIOA::enable();
100 }
101
102 #[cfg(feature = "_time-driver")]
103 time_driver::init(cs);
104
105 peripherals
106 })
107}
diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs
new file mode 100644
index 000000000..937ce58d4
--- /dev/null
+++ b/embassy-mspm0/src/time_driver.rs
@@ -0,0 +1,423 @@
1use core::cell::{Cell, RefCell};
2use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
3use core::task::Waker;
4
5use critical_section::{CriticalSection, Mutex};
6use embassy_time_driver::Driver;
7use embassy_time_queue_utils::Queue;
8use mspm0_metapac::interrupt;
9use mspm0_metapac::tim::vals::{Cm, Cvae, CxC, EvtCfg, PwrenKey, Ratio, Repeat, ResetKey};
10use mspm0_metapac::tim::{Counterregs16, Tim};
11
12use crate::peripherals;
13use crate::timer::SealedTimer;
14
15// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.
16#[cfg(time_driver_timg0)]
17type T = peripherals::TIMG0;
18#[cfg(time_driver_timg1)]
19type T = peripherals::TIMG1;
20#[cfg(time_driver_timg2)]
21type T = peripherals::TIMG2;
22#[cfg(time_driver_timg3)]
23type T = peripherals::TIMG3;
24#[cfg(time_driver_timg4)]
25type T = peripherals::TIMG4;
26#[cfg(time_driver_timg5)]
27type T = peripherals::TIMG5;
28#[cfg(time_driver_timg6)]
29type T = peripherals::TIMG6;
30#[cfg(time_driver_timg7)]
31type T = peripherals::TIMG7;
32#[cfg(time_driver_timg8)]
33type T = peripherals::TIMG8;
34#[cfg(time_driver_timg9)]
35type T = peripherals::TIMG9;
36#[cfg(time_driver_timg10)]
37type T = peripherals::TIMG10;
38#[cfg(time_driver_timg11)]
39type T = peripherals::TIMG11;
40#[cfg(time_driver_timg14)]
41type T = peripherals::TIMG14;
42#[cfg(time_driver_tima0)]
43type T = peripherals::TIMA0;
44#[cfg(time_driver_tima1)]
45type T = peripherals::TIMA1;
46
47// TODO: RTC
48
49fn regs() -> Tim {
50 unsafe { Tim::from_ptr(T::regs()) }
51}
52
53fn regs_counter(tim: Tim) -> Counterregs16 {
54 unsafe { Counterregs16::from_ptr(tim.counterregs(0).as_ptr()) }
55}
56
57/// Clock timekeeping works with something we call "periods", which are time intervals
58/// of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
59fn calc_now(period: u32, counter: u16) -> u64 {
60 ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64)
61}
62
63/// The TIMx driver uses one of the `TIMG` or `TIMA` timer instances to implement a timer with a 32.768 kHz
64/// tick rate. (TODO: Allow setting the tick rate)
65///
66/// This driver defines a period to be 2^15 ticks. 16-bit timers of course count to 2^16 ticks.
67///
68/// To generate a period every 2^15 ticks, the CC0 value is set to 2^15 and the load value set to 2^16.
69/// Incrementing the period on a CCU0 and load results in the a period of 2^15 ticks.
70///
71/// For a specific timestamp, load the lower 16 bits into the CC1 value. When the period where the timestamp
72/// should be enabled is reached, then the CCU1 (CC1 up) interrupt runs to actually wake the timer.
73///
74/// TODO: Compensate for per part variance. This can supposedly be done with the FCC system.
75/// TODO: Allow using 32-bit timers (TIMG12 and TIMG13).
76struct TimxDriver {
77 /// Number of 2^15 periods elapsed since boot.
78 period: AtomicU32,
79 /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
80 alarm: Mutex<Cell<u64>>,
81 queue: Mutex<RefCell<Queue>>,
82}
83
84impl TimxDriver {
85 #[inline(never)]
86 fn init(&'static self, _cs: CriticalSection) {
87 // Clock config
88 // TODO: Configurable tick rate up to 4 MHz (32 kHz for now)
89 let regs = regs();
90
91 // Reset timer
92 regs.gprcm(0).rstctl().write(|w| {
93 w.set_resetassert(true);
94 w.set_key(ResetKey::KEY);
95 w.set_resetstkyclr(true);
96 });
97
98 // Power up timer
99 regs.gprcm(0).pwren().write(|w| {
100 w.set_enable(true);
101 w.set_key(PwrenKey::KEY);
102 });
103
104 // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration
105
106 // 1. Select TIMCLK source
107 regs.clksel().modify(|w| {
108 // Use LFCLK for a 32.768kHz tick rate
109 w.set_lfclk_sel(true);
110 // TODO: Allow MFCLK for configurable tick rate up to 4 MHz
111 // w.set_mfclk_sel(ClkSel::ENABLE);
112 });
113
114 // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate
115 regs.clkdiv().modify(|w| {
116 w.set_ratio(Ratio::DIV_BY_1);
117 });
118
119 // 3. To be generic across timer instances, we do not use the prescaler.
120 // TODO: mspm0-sdk always sets this, regardless of timer width?
121 regs.commonregs(0).cps().modify(|w| {
122 w.set_pcnt(0);
123 });
124
125 regs.pdbgctl().modify(|w| {
126 w.set_free(true);
127 });
128
129 // 4. Enable the TIMCLK.
130 regs.commonregs(0).cclkctl().modify(|w| {
131 w.set_clken(true);
132 });
133
134 regs.counterregs(0).ctrctl().modify(|w| {
135 // allow counting during debug
136 w.set_repeat(Repeat::REPEAT_3);
137 w.set_cvae(Cvae::ZEROVAL);
138 w.set_cm(Cm::UP);
139
140 // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count.
141 //
142 // The reset value of these registers is 0x07, which is a reserved value.
143 //
144 // Looking at a bit representation of the reset value, this appears to be an AND
145 // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI
146 // and 4 capture and compare channels, this works by accident for those timer units.
147 w.set_czc(CxC::CCTL0);
148 w.set_cac(CxC::CCTL0);
149 w.set_clc(CxC::CCTL0);
150 });
151
152 // Setup the period
153 let ctr = regs_counter(regs);
154
155 // Middle
156 ctr.cc(0).modify(|w| {
157 w.set_ccval(0x7FFF);
158 });
159
160 ctr.load().modify(|w| {
161 w.set_ld(u16::MAX);
162 });
163
164 // Enable the period interrupts
165 //
166 // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed.
167 regs.evt_mode().modify(|w| {
168 w.set_evt_cfg(0, EvtCfg::SOFTWARE);
169 });
170
171 regs.int_event(0).imask().modify(|w| {
172 w.set_l(true);
173 w.set_ccu0(true);
174 });
175
176 unsafe { T::enable_interrupt() };
177
178 // Allow the counter to start counting.
179 regs.counterregs(0).ctrctl().modify(|w| {
180 w.set_en(true);
181 });
182 }
183
184 #[inline(never)]
185 fn next_period(&self) {
186 let r = regs();
187
188 // We only modify the period from the timer interrupt, so we know this can't race.
189 let period = self.period.load(Ordering::Relaxed) + 1;
190 self.period.store(period, Ordering::Relaxed);
191 let t = (period as u64) << 15;
192
193 critical_section::with(move |cs| {
194 r.int_event(0).imask().modify(move |w| {
195 let alarm = self.alarm.borrow(cs);
196 let at = alarm.get();
197
198 if at < t + 0xC000 {
199 // just enable it. `set_alarm` has already set the correct CC1 val.
200 w.set_ccu1(true);
201 }
202 })
203 });
204 }
205
206 #[inline(never)]
207 fn on_interrupt(&self) {
208 let r = regs();
209
210 critical_section::with(|cs| {
211 let mis = r.int_event(0).mis().read();
212
213 // Advance to next period if overflowed
214 if mis.l() {
215 self.next_period();
216
217 r.int_event(0).iclr().write(|w| {
218 w.set_l(true);
219 });
220 }
221
222 if mis.ccu0() {
223 self.next_period();
224
225 r.int_event(0).iclr().write(|w| {
226 w.set_ccu0(true);
227 });
228 }
229
230 if mis.ccu1() {
231 r.int_event(0).iclr().write(|w| {
232 w.set_ccu1(true);
233 });
234
235 self.trigger_alarm(cs);
236 }
237 });
238 }
239
240 fn trigger_alarm(&self, cs: CriticalSection) {
241 let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
242
243 while !self.set_alarm(cs, next) {
244 next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
245 }
246 }
247
248 fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
249 let r = regs();
250 let ctr = regs_counter(r);
251
252 self.alarm.borrow(cs).set(timestamp);
253
254 let t = self.now();
255
256 if timestamp <= t {
257 // If alarm timestamp has passed the alarm will not fire.
258 // Disarm the alarm and return `false` to indicate that.
259 r.int_event(0).imask().modify(|w| w.set_ccu1(false));
260
261 self.alarm.borrow(cs).set(u64::MAX);
262
263 return false;
264 }
265
266 // Write the CC1 value regardless of whether we're going to enable it now or not.
267 // This way, when we enable it later, the right value is already set.
268 ctr.cc(1).write(|w| {
269 w.set_ccval(timestamp as u16);
270 });
271
272 // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
273 let diff = timestamp - t;
274 r.int_event(0).imask().modify(|w| w.set_ccu1(diff < 0xC000));
275
276 // Reevaluate if the alarm timestamp is still in the future
277 let t = self.now();
278 if timestamp <= t {
279 // If alarm timestamp has passed since we set it, we have a race condition and
280 // the alarm may or may not have fired.
281 // Disarm the alarm and return `false` to indicate that.
282 // It is the caller's responsibility to handle this ambiguity.
283 r.int_event(0).imask().modify(|w| w.set_ccu1(false));
284
285 self.alarm.borrow(cs).set(u64::MAX);
286
287 return false;
288 }
289
290 // We're confident the alarm will ring in the future.
291 true
292 }
293}
294
295impl Driver for TimxDriver {
296 fn now(&self) -> u64 {
297 let regs = regs();
298
299 let period = self.period.load(Ordering::Relaxed);
300 // Ensure the compiler does not read the counter before the period.
301 compiler_fence(Ordering::Acquire);
302
303 let counter = regs_counter(regs).ctr().read().cctr() as u16;
304
305 calc_now(period, counter)
306 }
307
308 fn schedule_wake(&self, at: u64, waker: &Waker) {
309 critical_section::with(|cs| {
310 let mut queue = self.queue.borrow(cs).borrow_mut();
311
312 if queue.schedule_wake(at, waker) {
313 let mut next = queue.next_expiration(self.now());
314
315 while !self.set_alarm(cs, next) {
316 next = queue.next_expiration(self.now());
317 }
318 }
319 });
320 }
321}
322
323embassy_time_driver::time_driver_impl!(static DRIVER: TimxDriver = TimxDriver {
324 period: AtomicU32::new(0),
325 alarm: Mutex::new(Cell::new(u64::MAX)),
326 queue: Mutex::new(RefCell::new(Queue::new()))
327});
328
329pub(crate) fn init(cs: CriticalSection) {
330 DRIVER.init(cs);
331}
332
333#[cfg(time_driver_timg0)]
334#[interrupt]
335fn TIMG0() {
336 DRIVER.on_interrupt();
337}
338
339#[cfg(time_driver_timg1)]
340#[interrupt]
341fn TIMG1() {
342 DRIVER.on_interrupt();
343}
344
345#[cfg(time_driver_timg2)]
346#[interrupt]
347fn TIMG2() {
348 DRIVER.on_interrupt();
349}
350
351#[cfg(time_driver_timg3)]
352#[interrupt]
353fn TIMG3() {
354 DRIVER.on_interrupt();
355}
356
357#[cfg(time_driver_timg4)]
358#[interrupt]
359fn TIMG4() {
360 DRIVER.on_interrupt();
361}
362
363#[cfg(time_driver_timg5)]
364#[interrupt]
365fn TIMG5() {
366 DRIVER.on_interrupt();
367}
368
369#[cfg(time_driver_timg6)]
370#[interrupt]
371fn TIMG6() {
372 DRIVER.on_interrupt();
373}
374
375#[cfg(time_driver_timg7)]
376#[interrupt]
377fn TIMG7() {
378 DRIVER.on_interrupt();
379}
380
381#[cfg(time_driver_timg8)]
382#[interrupt]
383fn TIMG8() {
384 DRIVER.on_interrupt();
385}
386
387#[cfg(time_driver_timg9)]
388#[interrupt]
389fn TIMG9() {
390 DRIVER.on_interrupt();
391}
392
393#[cfg(time_driver_timg10)]
394#[interrupt]
395fn TIMG10() {
396 DRIVER.on_interrupt();
397}
398
399#[cfg(time_driver_timg11)]
400#[interrupt]
401fn TIMG11() {
402 DRIVER.on_interrupt();
403}
404
405// TODO: TIMG12 and TIMG13
406
407#[cfg(time_driver_timg14)]
408#[interrupt]
409fn TIMG14() {
410 DRIVER.on_interrupt();
411}
412
413#[cfg(time_driver_tima0)]
414#[interrupt]
415fn TIMA0() {
416 DRIVER.on_interrupt();
417}
418
419#[cfg(time_driver_tima1)]
420#[interrupt]
421fn TIMA1() {
422 DRIVER.on_interrupt();
423}
diff --git a/embassy-mspm0/src/timer.rs b/embassy-mspm0/src/timer.rs
new file mode 100644
index 000000000..4441e5640
--- /dev/null
+++ b/embassy-mspm0/src/timer.rs
@@ -0,0 +1,48 @@
1#![macro_use]
2
3/// Amount of bits of a timer.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum TimerBits {
7 /// 16 bits.
8 Bits16,
9 /// 32 bits.
10 Bits32,
11}
12
13#[allow(private_bounds)]
14pub trait Timer: SealedTimer + 'static {
15 /// Amount of bits this timer has.
16 const BITS: TimerBits;
17}
18
19pub(crate) trait SealedTimer {
20 /// Registers for this timer.
21 ///
22 /// This is a raw pointer to the register block. The actual register block layout varies depending on the
23 /// timer type.
24 fn regs() -> *mut ();
25
26 /// Enable the interrupt corresponding to this timer.
27 unsafe fn enable_interrupt();
28}
29
30macro_rules! impl_timer {
31 ($name: ident, $bits: ident) => {
32 impl crate::timer::SealedTimer for crate::peripherals::$name {
33 fn regs() -> *mut () {
34 crate::pac::$name.as_ptr()
35 }
36
37 unsafe fn enable_interrupt() {
38 use embassy_hal_internal::interrupt::InterruptExt;
39 crate::interrupt::$name.unpend();
40 crate::interrupt::$name.enable();
41 }
42 }
43
44 impl crate::timer::Timer for crate::peripherals::$name {
45 const BITS: crate::timer::TimerBits = crate::timer::TimerBits::$bits;
46 }
47 };
48}