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