diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 70d4daf09..6c7591f57 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -80,6 +80,8 @@ chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" document-features = "0.2.7" +fdcan = { version = "0.2.0", optional = true } + [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -693,373 +695,373 @@ stm32f779ai = [ "stm32-metapac/stm32f779ai" ] stm32f779bi = [ "stm32-metapac/stm32f779bi" ] stm32f779ii = [ "stm32-metapac/stm32f779ii" ] stm32f779ni = [ "stm32-metapac/stm32f779ni" ] -stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] -stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] -stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] -stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] -stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] -stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] -stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] -stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] -stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] -stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] -stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] -stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] -stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] -stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] -stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] -stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] -stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] -stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] -stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] -stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] -stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] -stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] -stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] -stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] -stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] -stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] -stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] -stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] -stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] -stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] -stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] -stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] -stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] -stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] -stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] -stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] -stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] -stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] -stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] -stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] -stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] -stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] -stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] -stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] -stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] -stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] -stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] -stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] -stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] -stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] -stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] -stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] -stm32g070cb = [ "stm32-metapac/stm32g070cb" ] -stm32g070kb = [ "stm32-metapac/stm32g070kb" ] -stm32g070rb = [ "stm32-metapac/stm32g070rb" ] -stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] -stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] -stm32g071cb = [ "stm32-metapac/stm32g071cb" ] -stm32g071eb = [ "stm32-metapac/stm32g071eb" ] -stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] -stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] -stm32g071gb = [ "stm32-metapac/stm32g071gb" ] -stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] -stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] -stm32g071kb = [ "stm32-metapac/stm32g071kb" ] -stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] -stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] -stm32g071rb = [ "stm32-metapac/stm32g071rb" ] -stm32g081cb = [ "stm32-metapac/stm32g081cb" ] -stm32g081eb = [ "stm32-metapac/stm32g081eb" ] -stm32g081gb = [ "stm32-metapac/stm32g081gb" ] -stm32g081kb = [ "stm32-metapac/stm32g081kb" ] -stm32g081rb = [ "stm32-metapac/stm32g081rb" ] -stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] -stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] -stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] -stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] -stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] -stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] -stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] -stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] -stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] -stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] -stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] -stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] -stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] -stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] -stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] -stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] -stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] -stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] -stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] -stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] -stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] -stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] -stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] -stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] -stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] -stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] -stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] -stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] -stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] -stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] -stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] -stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] -stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] -stm32g431cb = [ "stm32-metapac/stm32g431cb" ] -stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] -stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] -stm32g431kb = [ "stm32-metapac/stm32g431kb" ] -stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] -stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] -stm32g431mb = [ "stm32-metapac/stm32g431mb" ] -stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] -stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] -stm32g431rb = [ "stm32-metapac/stm32g431rb" ] -stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] -stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] -stm32g431vb = [ "stm32-metapac/stm32g431vb" ] -stm32g441cb = [ "stm32-metapac/stm32g441cb" ] -stm32g441kb = [ "stm32-metapac/stm32g441kb" ] -stm32g441mb = [ "stm32-metapac/stm32g441mb" ] -stm32g441rb = [ "stm32-metapac/stm32g441rb" ] -stm32g441vb = [ "stm32-metapac/stm32g441vb" ] -stm32g471cc = [ "stm32-metapac/stm32g471cc" ] -stm32g471ce = [ "stm32-metapac/stm32g471ce" ] -stm32g471mc = [ "stm32-metapac/stm32g471mc" ] -stm32g471me = [ "stm32-metapac/stm32g471me" ] -stm32g471qc = [ "stm32-metapac/stm32g471qc" ] -stm32g471qe = [ "stm32-metapac/stm32g471qe" ] -stm32g471rc = [ "stm32-metapac/stm32g471rc" ] -stm32g471re = [ "stm32-metapac/stm32g471re" ] -stm32g471vc = [ "stm32-metapac/stm32g471vc" ] -stm32g471ve = [ "stm32-metapac/stm32g471ve" ] -stm32g473cb = [ "stm32-metapac/stm32g473cb" ] -stm32g473cc = [ "stm32-metapac/stm32g473cc" ] -stm32g473ce = [ "stm32-metapac/stm32g473ce" ] -stm32g473mb = [ "stm32-metapac/stm32g473mb" ] -stm32g473mc = [ "stm32-metapac/stm32g473mc" ] -stm32g473me = [ "stm32-metapac/stm32g473me" ] -stm32g473pb = [ "stm32-metapac/stm32g473pb" ] -stm32g473pc = [ "stm32-metapac/stm32g473pc" ] -stm32g473pe = [ "stm32-metapac/stm32g473pe" ] -stm32g473qb = [ "stm32-metapac/stm32g473qb" ] -stm32g473qc = [ "stm32-metapac/stm32g473qc" ] -stm32g473qe = [ "stm32-metapac/stm32g473qe" ] -stm32g473rb = [ "stm32-metapac/stm32g473rb" ] -stm32g473rc = [ "stm32-metapac/stm32g473rc" ] -stm32g473re = [ "stm32-metapac/stm32g473re" ] -stm32g473vb = [ "stm32-metapac/stm32g473vb" ] -stm32g473vc = [ "stm32-metapac/stm32g473vc" ] -stm32g473ve = [ "stm32-metapac/stm32g473ve" ] -stm32g474cb = [ "stm32-metapac/stm32g474cb" ] -stm32g474cc = [ "stm32-metapac/stm32g474cc" ] -stm32g474ce = [ "stm32-metapac/stm32g474ce" ] -stm32g474mb = [ "stm32-metapac/stm32g474mb" ] -stm32g474mc = [ "stm32-metapac/stm32g474mc" ] -stm32g474me = [ "stm32-metapac/stm32g474me" ] -stm32g474pb = [ "stm32-metapac/stm32g474pb" ] -stm32g474pc = [ "stm32-metapac/stm32g474pc" ] -stm32g474pe = [ "stm32-metapac/stm32g474pe" ] -stm32g474qb = [ "stm32-metapac/stm32g474qb" ] -stm32g474qc = [ "stm32-metapac/stm32g474qc" ] -stm32g474qe = [ "stm32-metapac/stm32g474qe" ] -stm32g474rb = [ "stm32-metapac/stm32g474rb" ] -stm32g474rc = [ "stm32-metapac/stm32g474rc" ] -stm32g474re = [ "stm32-metapac/stm32g474re" ] -stm32g474vb = [ "stm32-metapac/stm32g474vb" ] -stm32g474vc = [ "stm32-metapac/stm32g474vc" ] -stm32g474ve = [ "stm32-metapac/stm32g474ve" ] -stm32g483ce = [ "stm32-metapac/stm32g483ce" ] -stm32g483me = [ "stm32-metapac/stm32g483me" ] -stm32g483pe = [ "stm32-metapac/stm32g483pe" ] -stm32g483qe = [ "stm32-metapac/stm32g483qe" ] -stm32g483re = [ "stm32-metapac/stm32g483re" ] -stm32g483ve = [ "stm32-metapac/stm32g483ve" ] -stm32g484ce = [ "stm32-metapac/stm32g484ce" ] -stm32g484me = [ "stm32-metapac/stm32g484me" ] -stm32g484pe = [ "stm32-metapac/stm32g484pe" ] -stm32g484qe = [ "stm32-metapac/stm32g484qe" ] -stm32g484re = [ "stm32-metapac/stm32g484re" ] -stm32g484ve = [ "stm32-metapac/stm32g484ve" ] -stm32g491cc = [ "stm32-metapac/stm32g491cc" ] -stm32g491ce = [ "stm32-metapac/stm32g491ce" ] -stm32g491kc = [ "stm32-metapac/stm32g491kc" ] -stm32g491ke = [ "stm32-metapac/stm32g491ke" ] -stm32g491mc = [ "stm32-metapac/stm32g491mc" ] -stm32g491me = [ "stm32-metapac/stm32g491me" ] -stm32g491rc = [ "stm32-metapac/stm32g491rc" ] -stm32g491re = [ "stm32-metapac/stm32g491re" ] -stm32g491vc = [ "stm32-metapac/stm32g491vc" ] -stm32g491ve = [ "stm32-metapac/stm32g491ve" ] -stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] -stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] -stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] -stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] -stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] -stm32h503cb = [ "stm32-metapac/stm32h503cb" ] -stm32h503eb = [ "stm32-metapac/stm32h503eb" ] -stm32h503kb = [ "stm32-metapac/stm32h503kb" ] -stm32h503rb = [ "stm32-metapac/stm32h503rb" ] -stm32h562ag = [ "stm32-metapac/stm32h562ag" ] -stm32h562ai = [ "stm32-metapac/stm32h562ai" ] -stm32h562ig = [ "stm32-metapac/stm32h562ig" ] -stm32h562ii = [ "stm32-metapac/stm32h562ii" ] -stm32h562rg = [ "stm32-metapac/stm32h562rg" ] -stm32h562ri = [ "stm32-metapac/stm32h562ri" ] -stm32h562vg = [ "stm32-metapac/stm32h562vg" ] -stm32h562vi = [ "stm32-metapac/stm32h562vi" ] -stm32h562zg = [ "stm32-metapac/stm32h562zg" ] -stm32h562zi = [ "stm32-metapac/stm32h562zi" ] -stm32h563ag = [ "stm32-metapac/stm32h563ag" ] -stm32h563ai = [ "stm32-metapac/stm32h563ai" ] -stm32h563ig = [ "stm32-metapac/stm32h563ig" ] -stm32h563ii = [ "stm32-metapac/stm32h563ii" ] -stm32h563mi = [ "stm32-metapac/stm32h563mi" ] -stm32h563rg = [ "stm32-metapac/stm32h563rg" ] -stm32h563ri = [ "stm32-metapac/stm32h563ri" ] -stm32h563vg = [ "stm32-metapac/stm32h563vg" ] -stm32h563vi = [ "stm32-metapac/stm32h563vi" ] -stm32h563zg = [ "stm32-metapac/stm32h563zg" ] -stm32h563zi = [ "stm32-metapac/stm32h563zi" ] -stm32h573ai = [ "stm32-metapac/stm32h573ai" ] -stm32h573ii = [ "stm32-metapac/stm32h573ii" ] -stm32h573mi = [ "stm32-metapac/stm32h573mi" ] -stm32h573ri = [ "stm32-metapac/stm32h573ri" ] -stm32h573vi = [ "stm32-metapac/stm32h573vi" ] -stm32h573zi = [ "stm32-metapac/stm32h573zi" ] -stm32h723ve = [ "stm32-metapac/stm32h723ve" ] -stm32h723vg = [ "stm32-metapac/stm32h723vg" ] -stm32h723ze = [ "stm32-metapac/stm32h723ze" ] -stm32h723zg = [ "stm32-metapac/stm32h723zg" ] -stm32h725ae = [ "stm32-metapac/stm32h725ae" ] -stm32h725ag = [ "stm32-metapac/stm32h725ag" ] -stm32h725ie = [ "stm32-metapac/stm32h725ie" ] -stm32h725ig = [ "stm32-metapac/stm32h725ig" ] -stm32h725re = [ "stm32-metapac/stm32h725re" ] -stm32h725rg = [ "stm32-metapac/stm32h725rg" ] -stm32h725ve = [ "stm32-metapac/stm32h725ve" ] -stm32h725vg = [ "stm32-metapac/stm32h725vg" ] -stm32h725ze = [ "stm32-metapac/stm32h725ze" ] -stm32h725zg = [ "stm32-metapac/stm32h725zg" ] -stm32h730ab = [ "stm32-metapac/stm32h730ab" ] -stm32h730ib = [ "stm32-metapac/stm32h730ib" ] -stm32h730vb = [ "stm32-metapac/stm32h730vb" ] -stm32h730zb = [ "stm32-metapac/stm32h730zb" ] -stm32h733vg = [ "stm32-metapac/stm32h733vg" ] -stm32h733zg = [ "stm32-metapac/stm32h733zg" ] -stm32h735ag = [ "stm32-metapac/stm32h735ag" ] -stm32h735ig = [ "stm32-metapac/stm32h735ig" ] -stm32h735rg = [ "stm32-metapac/stm32h735rg" ] -stm32h735vg = [ "stm32-metapac/stm32h735vg" ] -stm32h735zg = [ "stm32-metapac/stm32h735zg" ] -stm32h742ag = [ "stm32-metapac/stm32h742ag" ] -stm32h742ai = [ "stm32-metapac/stm32h742ai" ] -stm32h742bg = [ "stm32-metapac/stm32h742bg" ] -stm32h742bi = [ "stm32-metapac/stm32h742bi" ] -stm32h742ig = [ "stm32-metapac/stm32h742ig" ] -stm32h742ii = [ "stm32-metapac/stm32h742ii" ] -stm32h742vg = [ "stm32-metapac/stm32h742vg" ] -stm32h742vi = [ "stm32-metapac/stm32h742vi" ] -stm32h742xg = [ "stm32-metapac/stm32h742xg" ] -stm32h742xi = [ "stm32-metapac/stm32h742xi" ] -stm32h742zg = [ "stm32-metapac/stm32h742zg" ] -stm32h742zi = [ "stm32-metapac/stm32h742zi" ] -stm32h743ag = [ "stm32-metapac/stm32h743ag" ] -stm32h743ai = [ "stm32-metapac/stm32h743ai" ] -stm32h743bg = [ "stm32-metapac/stm32h743bg" ] -stm32h743bi = [ "stm32-metapac/stm32h743bi" ] -stm32h743ig = [ "stm32-metapac/stm32h743ig" ] -stm32h743ii = [ "stm32-metapac/stm32h743ii" ] -stm32h743vg = [ "stm32-metapac/stm32h743vg" ] -stm32h743vi = [ "stm32-metapac/stm32h743vi" ] -stm32h743xg = [ "stm32-metapac/stm32h743xg" ] -stm32h743xi = [ "stm32-metapac/stm32h743xi" ] -stm32h743zg = [ "stm32-metapac/stm32h743zg" ] -stm32h743zi = [ "stm32-metapac/stm32h743zi" ] -stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] -stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] -stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] -stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] -stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] -stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] -stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] -stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] -stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] -stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] -stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] -stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] -stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] -stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] -stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] -stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] -stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] -stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] -stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] -stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] -stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] -stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] -stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] -stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] -stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] -stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] -stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] -stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] -stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] -stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] -stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] -stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] -stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] -stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] -stm32h750ib = [ "stm32-metapac/stm32h750ib" ] -stm32h750vb = [ "stm32-metapac/stm32h750vb" ] -stm32h750xb = [ "stm32-metapac/stm32h750xb" ] -stm32h750zb = [ "stm32-metapac/stm32h750zb" ] -stm32h753ai = [ "stm32-metapac/stm32h753ai" ] -stm32h753bi = [ "stm32-metapac/stm32h753bi" ] -stm32h753ii = [ "stm32-metapac/stm32h753ii" ] -stm32h753vi = [ "stm32-metapac/stm32h753vi" ] -stm32h753xi = [ "stm32-metapac/stm32h753xi" ] -stm32h753zi = [ "stm32-metapac/stm32h753zi" ] -stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] -stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] -stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] -stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] -stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] -stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] -stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] -stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] -stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] -stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] -stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] -stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] -stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] -stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] -stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] -stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] -stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] -stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] -stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] -stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] -stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] -stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] -stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] -stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] -stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] -stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] -stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] -stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] -stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] -stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] -stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] -stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] -stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] -stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] -stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] -stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] -stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] -stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] -stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] -stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] -stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] -stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] -stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] -stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] -stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] -stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] +stm32g030c6 = [ "stm32-metapac/stm32g030c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g030c8 = [ "stm32-metapac/stm32g030c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g030f6 = [ "stm32-metapac/stm32g030f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g030j6 = [ "stm32-metapac/stm32g030j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g030k6 = [ "stm32-metapac/stm32g030k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g030k8 = [ "stm32-metapac/stm32g030k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031c4 = [ "stm32-metapac/stm32g031c4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031c6 = [ "stm32-metapac/stm32g031c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031c8 = [ "stm32-metapac/stm32g031c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031f4 = [ "stm32-metapac/stm32g031f4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031f6 = [ "stm32-metapac/stm32g031f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031f8 = [ "stm32-metapac/stm32g031f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031g4 = [ "stm32-metapac/stm32g031g4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031g6 = [ "stm32-metapac/stm32g031g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031g8 = [ "stm32-metapac/stm32g031g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031j4 = [ "stm32-metapac/stm32g031j4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031j6 = [ "stm32-metapac/stm32g031j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031k4 = [ "stm32-metapac/stm32g031k4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031k6 = [ "stm32-metapac/stm32g031k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031k8 = [ "stm32-metapac/stm32g031k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g031y8 = [ "stm32-metapac/stm32g031y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041c6 = [ "stm32-metapac/stm32g041c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041c8 = [ "stm32-metapac/stm32g041c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041f6 = [ "stm32-metapac/stm32g041f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041f8 = [ "stm32-metapac/stm32g041f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041g6 = [ "stm32-metapac/stm32g041g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041g8 = [ "stm32-metapac/stm32g041g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041j6 = [ "stm32-metapac/stm32g041j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041k6 = [ "stm32-metapac/stm32g041k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041k8 = [ "stm32-metapac/stm32g041k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g041y8 = [ "stm32-metapac/stm32g041y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g050c6 = [ "stm32-metapac/stm32g050c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g050c8 = [ "stm32-metapac/stm32g050c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g050f6 = [ "stm32-metapac/stm32g050f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g050k6 = [ "stm32-metapac/stm32g050k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g050k8 = [ "stm32-metapac/stm32g050k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051c6 = [ "stm32-metapac/stm32g051c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051c8 = [ "stm32-metapac/stm32g051c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051f6 = [ "stm32-metapac/stm32g051f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051f8 = [ "stm32-metapac/stm32g051f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051g6 = [ "stm32-metapac/stm32g051g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051g8 = [ "stm32-metapac/stm32g051g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051k6 = [ "stm32-metapac/stm32g051k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g051k8 = [ "stm32-metapac/stm32g051k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061c6 = [ "stm32-metapac/stm32g061c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061c8 = [ "stm32-metapac/stm32g061c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061f6 = [ "stm32-metapac/stm32g061f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061f8 = [ "stm32-metapac/stm32g061f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061g6 = [ "stm32-metapac/stm32g061g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061g8 = [ "stm32-metapac/stm32g061g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061k6 = [ "stm32-metapac/stm32g061k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g061k8 = [ "stm32-metapac/stm32g061k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g070cb = [ "stm32-metapac/stm32g070cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g070kb = [ "stm32-metapac/stm32g070kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g070rb = [ "stm32-metapac/stm32g070rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071c6 = [ "stm32-metapac/stm32g071c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071c8 = [ "stm32-metapac/stm32g071c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071cb = [ "stm32-metapac/stm32g071cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071eb = [ "stm32-metapac/stm32g071eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071g6 = [ "stm32-metapac/stm32g071g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071g8 = [ "stm32-metapac/stm32g071g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071gb = [ "stm32-metapac/stm32g071gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071k6 = [ "stm32-metapac/stm32g071k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071k8 = [ "stm32-metapac/stm32g071k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071kb = [ "stm32-metapac/stm32g071kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071r6 = [ "stm32-metapac/stm32g071r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071r8 = [ "stm32-metapac/stm32g071r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g071rb = [ "stm32-metapac/stm32g071rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g081cb = [ "stm32-metapac/stm32g081cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g081eb = [ "stm32-metapac/stm32g081eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g081gb = [ "stm32-metapac/stm32g081gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g081kb = [ "stm32-metapac/stm32g081kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g081rb = [ "stm32-metapac/stm32g081rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b0re = [ "stm32-metapac/stm32g0b0re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1me = [ "stm32-metapac/stm32g0b1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1re = [ "stm32-metapac/stm32g0b1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1me = [ "stm32-metapac/stm32g0c1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1re = [ "stm32-metapac/stm32g0c1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431c6 = [ "stm32-metapac/stm32g431c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431c8 = [ "stm32-metapac/stm32g431c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431cb = [ "stm32-metapac/stm32g431cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431k6 = [ "stm32-metapac/stm32g431k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431k8 = [ "stm32-metapac/stm32g431k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431kb = [ "stm32-metapac/stm32g431kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431m6 = [ "stm32-metapac/stm32g431m6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431m8 = [ "stm32-metapac/stm32g431m8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431mb = [ "stm32-metapac/stm32g431mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431r6 = [ "stm32-metapac/stm32g431r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431r8 = [ "stm32-metapac/stm32g431r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431rb = [ "stm32-metapac/stm32g431rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431v6 = [ "stm32-metapac/stm32g431v6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431v8 = [ "stm32-metapac/stm32g431v8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g431vb = [ "stm32-metapac/stm32g431vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g441cb = [ "stm32-metapac/stm32g441cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g441kb = [ "stm32-metapac/stm32g441kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g441mb = [ "stm32-metapac/stm32g441mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g441rb = [ "stm32-metapac/stm32g441rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g441vb = [ "stm32-metapac/stm32g441vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471cc = [ "stm32-metapac/stm32g471cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471ce = [ "stm32-metapac/stm32g471ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471mc = [ "stm32-metapac/stm32g471mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471me = [ "stm32-metapac/stm32g471me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471qc = [ "stm32-metapac/stm32g471qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471qe = [ "stm32-metapac/stm32g471qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471rc = [ "stm32-metapac/stm32g471rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471re = [ "stm32-metapac/stm32g471re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471vc = [ "stm32-metapac/stm32g471vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g471ve = [ "stm32-metapac/stm32g471ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473cb = [ "stm32-metapac/stm32g473cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473cc = [ "stm32-metapac/stm32g473cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473ce = [ "stm32-metapac/stm32g473ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473mb = [ "stm32-metapac/stm32g473mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473mc = [ "stm32-metapac/stm32g473mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473me = [ "stm32-metapac/stm32g473me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473pb = [ "stm32-metapac/stm32g473pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473pc = [ "stm32-metapac/stm32g473pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473pe = [ "stm32-metapac/stm32g473pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473qb = [ "stm32-metapac/stm32g473qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473qc = [ "stm32-metapac/stm32g473qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473qe = [ "stm32-metapac/stm32g473qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473rb = [ "stm32-metapac/stm32g473rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473rc = [ "stm32-metapac/stm32g473rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473re = [ "stm32-metapac/stm32g473re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473vb = [ "stm32-metapac/stm32g473vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473vc = [ "stm32-metapac/stm32g473vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g473ve = [ "stm32-metapac/stm32g473ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474cb = [ "stm32-metapac/stm32g474cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474cc = [ "stm32-metapac/stm32g474cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474ce = [ "stm32-metapac/stm32g474ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474mb = [ "stm32-metapac/stm32g474mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474mc = [ "stm32-metapac/stm32g474mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474me = [ "stm32-metapac/stm32g474me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474pb = [ "stm32-metapac/stm32g474pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474pc = [ "stm32-metapac/stm32g474pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474pe = [ "stm32-metapac/stm32g474pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474qb = [ "stm32-metapac/stm32g474qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474qc = [ "stm32-metapac/stm32g474qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474qe = [ "stm32-metapac/stm32g474qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474rb = [ "stm32-metapac/stm32g474rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474rc = [ "stm32-metapac/stm32g474rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474re = [ "stm32-metapac/stm32g474re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474vb = [ "stm32-metapac/stm32g474vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474vc = [ "stm32-metapac/stm32g474vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g474ve = [ "stm32-metapac/stm32g474ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483ce = [ "stm32-metapac/stm32g483ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483me = [ "stm32-metapac/stm32g483me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483pe = [ "stm32-metapac/stm32g483pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483qe = [ "stm32-metapac/stm32g483qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483re = [ "stm32-metapac/stm32g483re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g483ve = [ "stm32-metapac/stm32g483ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484ce = [ "stm32-metapac/stm32g484ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484me = [ "stm32-metapac/stm32g484me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484pe = [ "stm32-metapac/stm32g484pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484qe = [ "stm32-metapac/stm32g484qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484re = [ "stm32-metapac/stm32g484re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g484ve = [ "stm32-metapac/stm32g484ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491cc = [ "stm32-metapac/stm32g491cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491ce = [ "stm32-metapac/stm32g491ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491kc = [ "stm32-metapac/stm32g491kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491ke = [ "stm32-metapac/stm32g491ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491mc = [ "stm32-metapac/stm32g491mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491me = [ "stm32-metapac/stm32g491me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491rc = [ "stm32-metapac/stm32g491rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491re = [ "stm32-metapac/stm32g491re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491vc = [ "stm32-metapac/stm32g491vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g491ve = [ "stm32-metapac/stm32g491ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g4a1me = [ "stm32-metapac/stm32g4a1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g4a1re = [ "stm32-metapac/stm32g4a1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h503cb = [ "stm32-metapac/stm32h503cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h503eb = [ "stm32-metapac/stm32h503eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h503kb = [ "stm32-metapac/stm32h503kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h503rb = [ "stm32-metapac/stm32h503rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562ag = [ "stm32-metapac/stm32h562ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562ai = [ "stm32-metapac/stm32h562ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562ig = [ "stm32-metapac/stm32h562ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562ii = [ "stm32-metapac/stm32h562ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562rg = [ "stm32-metapac/stm32h562rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562ri = [ "stm32-metapac/stm32h562ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562vg = [ "stm32-metapac/stm32h562vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562vi = [ "stm32-metapac/stm32h562vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562zg = [ "stm32-metapac/stm32h562zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h562zi = [ "stm32-metapac/stm32h562zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563ag = [ "stm32-metapac/stm32h563ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563ai = [ "stm32-metapac/stm32h563ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563ig = [ "stm32-metapac/stm32h563ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563ii = [ "stm32-metapac/stm32h563ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563mi = [ "stm32-metapac/stm32h563mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563rg = [ "stm32-metapac/stm32h563rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563ri = [ "stm32-metapac/stm32h563ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563vg = [ "stm32-metapac/stm32h563vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563vi = [ "stm32-metapac/stm32h563vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563zg = [ "stm32-metapac/stm32h563zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h563zi = [ "stm32-metapac/stm32h563zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573ai = [ "stm32-metapac/stm32h573ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573ii = [ "stm32-metapac/stm32h573ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573mi = [ "stm32-metapac/stm32h573mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573ri = [ "stm32-metapac/stm32h573ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573vi = [ "stm32-metapac/stm32h573vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h573zi = [ "stm32-metapac/stm32h573zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32h723ve = [ "stm32-metapac/stm32h723ve", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h723vg = [ "stm32-metapac/stm32h723vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h723ze = [ "stm32-metapac/stm32h723ze", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h723zg = [ "stm32-metapac/stm32h723zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ae = [ "stm32-metapac/stm32h725ae", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ag = [ "stm32-metapac/stm32h725ag", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ie = [ "stm32-metapac/stm32h725ie", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ig = [ "stm32-metapac/stm32h725ig", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725re = [ "stm32-metapac/stm32h725re", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725rg = [ "stm32-metapac/stm32h725rg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ve = [ "stm32-metapac/stm32h725ve", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725vg = [ "stm32-metapac/stm32h725vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725ze = [ "stm32-metapac/stm32h725ze", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h725zg = [ "stm32-metapac/stm32h725zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h730ab = [ "stm32-metapac/stm32h730ab", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h730ib = [ "stm32-metapac/stm32h730ib", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h730vb = [ "stm32-metapac/stm32h730vb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h730zb = [ "stm32-metapac/stm32h730zb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h733vg = [ "stm32-metapac/stm32h733vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h733zg = [ "stm32-metapac/stm32h733zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h735ag = [ "stm32-metapac/stm32h735ag", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h735ig = [ "stm32-metapac/stm32h735ig", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h735rg = [ "stm32-metapac/stm32h735rg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h735vg = [ "stm32-metapac/stm32h735vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h735zg = [ "stm32-metapac/stm32h735zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742ag = [ "stm32-metapac/stm32h742ag", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742ai = [ "stm32-metapac/stm32h742ai", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742bg = [ "stm32-metapac/stm32h742bg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742bi = [ "stm32-metapac/stm32h742bi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742ig = [ "stm32-metapac/stm32h742ig", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742ii = [ "stm32-metapac/stm32h742ii", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742vg = [ "stm32-metapac/stm32h742vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742vi = [ "stm32-metapac/stm32h742vi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742xg = [ "stm32-metapac/stm32h742xg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742xi = [ "stm32-metapac/stm32h742xi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742zg = [ "stm32-metapac/stm32h742zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h742zi = [ "stm32-metapac/stm32h742zi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743ag = [ "stm32-metapac/stm32h743ag", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743ai = [ "stm32-metapac/stm32h743ai", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743bg = [ "stm32-metapac/stm32h743bg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743bi = [ "stm32-metapac/stm32h743bi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743ig = [ "stm32-metapac/stm32h743ig", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743ii = [ "stm32-metapac/stm32h743ii", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743vg = [ "stm32-metapac/stm32h743vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743vi = [ "stm32-metapac/stm32h743vi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743xg = [ "stm32-metapac/stm32h743xg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743xi = [ "stm32-metapac/stm32h743xi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743zg = [ "stm32-metapac/stm32h743zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h743zi = [ "stm32-metapac/stm32h743zi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h750ib = [ "stm32-metapac/stm32h750ib", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h750vb = [ "stm32-metapac/stm32h750vb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h750xb = [ "stm32-metapac/stm32h750xb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h750zb = [ "stm32-metapac/stm32h750zb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753ai = [ "stm32-metapac/stm32h753ai", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753bi = [ "stm32-metapac/stm32h753bi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753ii = [ "stm32-metapac/stm32h753ii", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753vi = [ "stm32-metapac/stm32h753vi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753xi = [ "stm32-metapac/stm32h753xi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h753zi = [ "stm32-metapac/stm32h753zi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3li = [ "stm32-metapac/stm32h7a3li", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3li = [ "stm32-metapac/stm32h7b3li", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi", "dep:fdcan", "fdcan/fdcan_h7" ] +stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi", "dep:fdcan", "fdcan/fdcan_h7" ] stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] @@ -1386,86 +1388,86 @@ stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] -stm32l552cc = [ "stm32-metapac/stm32l552cc" ] -stm32l552ce = [ "stm32-metapac/stm32l552ce" ] -stm32l552me = [ "stm32-metapac/stm32l552me" ] -stm32l552qc = [ "stm32-metapac/stm32l552qc" ] -stm32l552qe = [ "stm32-metapac/stm32l552qe" ] -stm32l552rc = [ "stm32-metapac/stm32l552rc" ] -stm32l552re = [ "stm32-metapac/stm32l552re" ] -stm32l552vc = [ "stm32-metapac/stm32l552vc" ] -stm32l552ve = [ "stm32-metapac/stm32l552ve" ] -stm32l552zc = [ "stm32-metapac/stm32l552zc" ] -stm32l552ze = [ "stm32-metapac/stm32l552ze" ] -stm32l562ce = [ "stm32-metapac/stm32l562ce" ] -stm32l562me = [ "stm32-metapac/stm32l562me" ] -stm32l562qe = [ "stm32-metapac/stm32l562qe" ] -stm32l562re = [ "stm32-metapac/stm32l562re" ] -stm32l562ve = [ "stm32-metapac/stm32l562ve" ] -stm32l562ze = [ "stm32-metapac/stm32l562ze" ] -stm32u535cb = [ "stm32-metapac/stm32u535cb" ] -stm32u535cc = [ "stm32-metapac/stm32u535cc" ] -stm32u535ce = [ "stm32-metapac/stm32u535ce" ] -stm32u535je = [ "stm32-metapac/stm32u535je" ] -stm32u535nc = [ "stm32-metapac/stm32u535nc" ] -stm32u535ne = [ "stm32-metapac/stm32u535ne" ] -stm32u535rb = [ "stm32-metapac/stm32u535rb" ] -stm32u535rc = [ "stm32-metapac/stm32u535rc" ] -stm32u535re = [ "stm32-metapac/stm32u535re" ] -stm32u535vc = [ "stm32-metapac/stm32u535vc" ] -stm32u535ve = [ "stm32-metapac/stm32u535ve" ] -stm32u545ce = [ "stm32-metapac/stm32u545ce" ] -stm32u545je = [ "stm32-metapac/stm32u545je" ] -stm32u545ne = [ "stm32-metapac/stm32u545ne" ] -stm32u545re = [ "stm32-metapac/stm32u545re" ] -stm32u545ve = [ "stm32-metapac/stm32u545ve" ] -stm32u575ag = [ "stm32-metapac/stm32u575ag" ] -stm32u575ai = [ "stm32-metapac/stm32u575ai" ] -stm32u575cg = [ "stm32-metapac/stm32u575cg" ] -stm32u575ci = [ "stm32-metapac/stm32u575ci" ] -stm32u575og = [ "stm32-metapac/stm32u575og" ] -stm32u575oi = [ "stm32-metapac/stm32u575oi" ] -stm32u575qg = [ "stm32-metapac/stm32u575qg" ] -stm32u575qi = [ "stm32-metapac/stm32u575qi" ] -stm32u575rg = [ "stm32-metapac/stm32u575rg" ] -stm32u575ri = [ "stm32-metapac/stm32u575ri" ] -stm32u575vg = [ "stm32-metapac/stm32u575vg" ] -stm32u575vi = [ "stm32-metapac/stm32u575vi" ] -stm32u575zg = [ "stm32-metapac/stm32u575zg" ] -stm32u575zi = [ "stm32-metapac/stm32u575zi" ] -stm32u585ai = [ "stm32-metapac/stm32u585ai" ] -stm32u585ci = [ "stm32-metapac/stm32u585ci" ] -stm32u585oi = [ "stm32-metapac/stm32u585oi" ] -stm32u585qi = [ "stm32-metapac/stm32u585qi" ] -stm32u585ri = [ "stm32-metapac/stm32u585ri" ] -stm32u585vi = [ "stm32-metapac/stm32u585vi" ] -stm32u585zi = [ "stm32-metapac/stm32u585zi" ] -stm32u595ai = [ "stm32-metapac/stm32u595ai" ] -stm32u595aj = [ "stm32-metapac/stm32u595aj" ] -stm32u595qi = [ "stm32-metapac/stm32u595qi" ] -stm32u595qj = [ "stm32-metapac/stm32u595qj" ] -stm32u595ri = [ "stm32-metapac/stm32u595ri" ] -stm32u595rj = [ "stm32-metapac/stm32u595rj" ] -stm32u595vi = [ "stm32-metapac/stm32u595vi" ] -stm32u595vj = [ "stm32-metapac/stm32u595vj" ] -stm32u595zi = [ "stm32-metapac/stm32u595zi" ] -stm32u595zj = [ "stm32-metapac/stm32u595zj" ] -stm32u599bj = [ "stm32-metapac/stm32u599bj" ] -stm32u599ni = [ "stm32-metapac/stm32u599ni" ] -stm32u599nj = [ "stm32-metapac/stm32u599nj" ] -stm32u599vi = [ "stm32-metapac/stm32u599vi" ] -stm32u599vj = [ "stm32-metapac/stm32u599vj" ] -stm32u599zi = [ "stm32-metapac/stm32u599zi" ] -stm32u599zj = [ "stm32-metapac/stm32u599zj" ] -stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] -stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] -stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] -stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] -stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] -stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] -stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] -stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] -stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] +stm32l552cc = [ "stm32-metapac/stm32l552cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552ce = [ "stm32-metapac/stm32l552ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552me = [ "stm32-metapac/stm32l552me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552qc = [ "stm32-metapac/stm32l552qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552qe = [ "stm32-metapac/stm32l552qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552rc = [ "stm32-metapac/stm32l552rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552re = [ "stm32-metapac/stm32l552re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552vc = [ "stm32-metapac/stm32l552vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552ve = [ "stm32-metapac/stm32l552ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552zc = [ "stm32-metapac/stm32l552zc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l552ze = [ "stm32-metapac/stm32l552ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562ce = [ "stm32-metapac/stm32l562ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562me = [ "stm32-metapac/stm32l562me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562qe = [ "stm32-metapac/stm32l562qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562re = [ "stm32-metapac/stm32l562re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562ve = [ "stm32-metapac/stm32l562ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32l562ze = [ "stm32-metapac/stm32l562ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535cb = [ "stm32-metapac/stm32u535cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535cc = [ "stm32-metapac/stm32u535cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535ce = [ "stm32-metapac/stm32u535ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535je = [ "stm32-metapac/stm32u535je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535nc = [ "stm32-metapac/stm32u535nc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535ne = [ "stm32-metapac/stm32u535ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535rb = [ "stm32-metapac/stm32u535rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535rc = [ "stm32-metapac/stm32u535rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535re = [ "stm32-metapac/stm32u535re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535vc = [ "stm32-metapac/stm32u535vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u535ve = [ "stm32-metapac/stm32u535ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u545ce = [ "stm32-metapac/stm32u545ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u545je = [ "stm32-metapac/stm32u545je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u545ne = [ "stm32-metapac/stm32u545ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u545re = [ "stm32-metapac/stm32u545re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u545ve = [ "stm32-metapac/stm32u545ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575ag = [ "stm32-metapac/stm32u575ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575ai = [ "stm32-metapac/stm32u575ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575cg = [ "stm32-metapac/stm32u575cg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575ci = [ "stm32-metapac/stm32u575ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575og = [ "stm32-metapac/stm32u575og", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575oi = [ "stm32-metapac/stm32u575oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575qg = [ "stm32-metapac/stm32u575qg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575qi = [ "stm32-metapac/stm32u575qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575rg = [ "stm32-metapac/stm32u575rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575ri = [ "stm32-metapac/stm32u575ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575vg = [ "stm32-metapac/stm32u575vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575vi = [ "stm32-metapac/stm32u575vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575zg = [ "stm32-metapac/stm32u575zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u575zi = [ "stm32-metapac/stm32u575zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585ai = [ "stm32-metapac/stm32u585ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585ci = [ "stm32-metapac/stm32u585ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585oi = [ "stm32-metapac/stm32u585oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585qi = [ "stm32-metapac/stm32u585qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585ri = [ "stm32-metapac/stm32u585ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585vi = [ "stm32-metapac/stm32u585vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u585zi = [ "stm32-metapac/stm32u585zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595ai = [ "stm32-metapac/stm32u595ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595aj = [ "stm32-metapac/stm32u595aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595qi = [ "stm32-metapac/stm32u595qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595qj = [ "stm32-metapac/stm32u595qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595ri = [ "stm32-metapac/stm32u595ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595rj = [ "stm32-metapac/stm32u595rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595vi = [ "stm32-metapac/stm32u595vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595vj = [ "stm32-metapac/stm32u595vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595zi = [ "stm32-metapac/stm32u595zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u595zj = [ "stm32-metapac/stm32u595zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599bj = [ "stm32-metapac/stm32u599bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599ni = [ "stm32-metapac/stm32u599ni", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599nj = [ "stm32-metapac/stm32u599nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599vi = [ "stm32-metapac/stm32u599vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599vj = [ "stm32-metapac/stm32u599vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599zi = [ "stm32-metapac/stm32u599zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u599zj = [ "stm32-metapac/stm32u599zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] +stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..414723573 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -449,7 +449,7 @@ fn main() { // ======== // Generate RccPeripheral impls - let refcounted_peripherals = HashSet::from(["usart", "adc"]); + let refcounted_peripherals = HashSet::from(["usart", "adc", "can"]); let mut refcount_statics = BTreeSet::new(); for p in METADATA.peripherals { diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index cc87b2565..7e00eca6f 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -13,9 +13,12 @@ use crate::gpio::sealed::AFType; use crate::interrupt::typelevel::Interrupt; use crate::pac::can::vals::{Ide, Lec}; use crate::rcc::RccPeripheral; -use crate::time::Hertz; use crate::{interrupt, peripherals, Peripheral}; +pub mod enums; +use enums::*; +pub mod util; + /// Contains CAN frame and additional metadata. /// /// Timestamp is available if `time` feature is enabled. @@ -93,23 +96,6 @@ pub struct Can<'d, T: Instance> { can: bxcan::Can>, } -/// CAN bus error -#[allow(missing_docs)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum BusError { - Stuff, - Form, - Acknowledge, - BitRecessive, - BitDominant, - Crc, - Software, - BusOff, - BusPassive, - BusWarning, -} - /// Error returned by `try_read` #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -186,8 +172,15 @@ impl<'d, T: Instance> Can<'d, T> { /// Set CAN bit rate. pub fn set_bitrate(&mut self, bitrate: u32) { - let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); - self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let sjw = u8::from(bit_timing.sync_jump_width) as u32; + let seg1 = u8::from(bit_timing.seg1) as u32; + let seg2 = u8::from(bit_timing.seg2) as u32; + let prescaler = u16::from(bit_timing.prescaler) as u32; + self.can + .modify_config() + .set_bit_timing((sjw - 1) << 24 | (seg1 - 1) << 16 | (seg2 - 1) << 20 | (prescaler - 1)) + .leave_disabled(); } /// Enables the peripheral and synchronizes with the bus. @@ -302,97 +295,6 @@ impl<'d, T: Instance> Can<'d, T> { } } - const fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { - const BS1_MAX: u8 = 16; - const BS2_MAX: u8 = 8; - const MAX_SAMPLE_POINT_PERMILL: u16 = 900; - - let periph_clock = periph_clock.0; - - if can_bitrate < 1000 { - return None; - } - - // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG - // CAN in Automation, 2003 - // - // According to the source, optimal quanta per bit are: - // Bitrate Optimal Maximum - // 1000 kbps 8 10 - // 500 kbps 16 17 - // 250 kbps 16 17 - // 125 kbps 16 17 - let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; - - // Computing (prescaler * BS): - // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual - // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified - // let: - // BS = 1 + BS1 + BS2 -- Number of time quanta per bit - // PRESCALER_BS = PRESCALER * BS - // ==> - // PRESCALER_BS = PCLK / BITRATE - let prescaler_bs = periph_clock / can_bitrate; - - // Searching for such prescaler value so that the number of quanta per bit is highest. - let mut bs1_bs2_sum = max_quanta_per_bit - 1; - while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { - if bs1_bs2_sum <= 2 { - return None; // No solution - } - bs1_bs2_sum -= 1; - } - - let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; - if (prescaler < 1) || (prescaler > 1024) { - return None; // No solution - } - - // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. - // We need to find such values so that the sample point is as close as possible to the optimal value, - // which is 87.5%, which is 7/8. - // - // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) - // {{bs2 -> (1 + bs1)/7}} - // - // Hence: - // bs2 = (1 + bs1) / 7 - // bs1 = (7 * bs1_bs2_sum - 1) / 8 - // - // Sample point location can be computed as follows: - // Sample point location = (1 + bs1) / (1 + bs1 + bs2) - // - // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: - // - With rounding to nearest - // - With rounding to zero - let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first - let mut bs2 = bs1_bs2_sum - bs1; - core::assert!(bs1_bs2_sum > bs1); - - let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; - if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { - // Nope, too far; now rounding to zero - bs1 = (7 * bs1_bs2_sum - 1) / 8; - bs2 = bs1_bs2_sum - bs1; - } - - // Check is BS1 and BS2 are in range - if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { - return None; - } - - // Check if final bitrate matches the requested - if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { - return None; - } - - // One is recommended by DS-015, CANOpen, and DeviceNet - let sjw = 1; - - // Pack into BTR register values - Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1)) - } - /// Split the CAN driver into transmit and receive halves. /// /// Useful for doing separate transmit/receive tasks. diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs new file mode 100644 index 000000000..36139a45c --- /dev/null +++ b/embassy-stm32/src/can/enums.rs @@ -0,0 +1,30 @@ +//! Enums shared between CAN controller types. + +/// Bus error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BusError { + /// Bit stuffing error - more than 5 equal bits + Stuff, + /// Form error - A fixed format part of a received message has wrong format + Form, + /// The message transmitted by the FDCAN was not acknowledged by another node. + Acknowledge, + /// Bit0Error: During the transmission of a message the device wanted to send a dominant level + /// but the monitored bus value was recessive. + BitRecessive, + /// Bit1Error: During the transmission of a message the device wanted to send a recessive level + /// but the monitored bus value was dominant. + BitDominant, + /// The CRC check sum of a received message was incorrect. The CRC of an + /// incoming message does not match with the CRC calculated from the received data. + Crc, + /// A software error occured + Software, + /// The FDCAN is in Bus_Off state. + BusOff, + /// The FDCAN is in the Error_Passive state. + BusPassive, + /// At least one of error counter has reached the Error_Warning limit of 96. + BusWarning, +} diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 0cc2559cf..faf4af73f 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -1,14 +1,577 @@ -use crate::peripherals; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::task::Poll; + +use cfg_if::cfg_if; +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use fdcan::frame::{FrameFormat, RxFrameInfo, TxFrameHeader}; +pub use fdcan::id::{ExtendedId, Id, StandardId}; +use fdcan::message_ram::RegisterBlock; +use fdcan::{self, LastErrorCode}; +pub use fdcan::{config, filter}; + +use crate::gpio::sealed::AFType; +use crate::interrupt::typelevel::Interrupt; +use crate::rcc::RccPeripheral; +use crate::{interrupt, peripherals, Peripheral}; + +pub mod enums; +use enums::*; +pub mod util; + +/// CAN Frame returned by read +pub struct RxFrame { + /// CAN Header info: frame ID, data length and other meta + pub header: RxFrameInfo, + /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data + pub data: Data, + /// Reception time. + #[cfg(feature = "time")] + pub timestamp: embassy_time::Instant, +} + +/// CAN frame used for write +pub struct TxFrame { + /// CAN Header info: frame ID, data length and other meta + pub header: TxFrameHeader, + /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data + pub data: Data, +} + +impl TxFrame { + /// Create new TX frame from header and data + pub fn new(header: TxFrameHeader, data: &[u8]) -> Option { + if data.len() < header.len as usize { + return None; + } + + let Some(data) = Data::new(data) else { return None }; + + Some(TxFrame { header, data }) + } + + fn from_preserved(header: TxFrameHeader, data32: &[u32]) -> Option { + let mut data = [0u8; 64]; + + for i in 0..data32.len() { + data[4 * i..][..4].copy_from_slice(&data32[i].to_le_bytes()); + } + + let Some(data) = Data::new(&data) else { return None }; + + Some(TxFrame { header, data }) + } + + /// Access frame data. Slice length will match header. + pub fn data(&self) -> &[u8] { + &self.data.bytes[..(self.header.len as usize)] + } +} + +impl RxFrame { + pub(crate) fn new( + header: RxFrameInfo, + data: &[u8], + #[cfg(feature = "time")] timestamp: embassy_time::Instant, + ) -> Self { + let data = Data::new(&data).unwrap_or_else(|| Data::empty()); + + RxFrame { + header, + data, + #[cfg(feature = "time")] + timestamp, + } + } + + /// Access frame data. Slice length will match header. + pub fn data(&self) -> &[u8] { + &self.data.bytes[..(self.header.len as usize)] + } +} + +/// Payload of a (FD)CAN data frame. +/// +/// Contains 0 to 64 Bytes of data. +#[derive(Debug, Copy, Clone)] +pub struct Data { + pub(crate) bytes: [u8; 64], +} + +impl Data { + /// Creates a data payload from a raw byte slice. + /// + /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or + /// cannot be represented with an FDCAN DLC. + pub fn new(data: &[u8]) -> Option { + if !Data::is_valid_len(data.len()) { + return None; + } + + let mut bytes = [0; 64]; + bytes[..data.len()].copy_from_slice(data); + + Some(Self { bytes }) + } + + /// Raw read access to data. + pub fn raw(&self) -> &[u8] { + &self.bytes + } + + /// Checks if the length can be encoded in FDCAN DLC field. + pub const fn is_valid_len(len: usize) -> bool { + match len { + 0..=8 => true, + 12 => true, + 16 => true, + 20 => true, + 24 => true, + 32 => true, + 48 => true, + 64 => true, + _ => false, + } + } + + /// Creates an empty data payload containing 0 bytes. + #[inline] + pub const fn empty() -> Self { + Self { bytes: [0; 64] } + } +} + +/// Interrupt handler channel 0. +pub struct IT0InterruptHandler { + _phantom: PhantomData, +} + +// We use IT0 for everything currently +impl interrupt::typelevel::Handler for IT0InterruptHandler { + unsafe fn on_interrupt() { + let regs = T::regs(); + + let ir = regs.ir().read(); + + if ir.tc() { + regs.ir().write(|w| w.set_tc(true)); + T::state().tx_waker.wake(); + } + + if ir.tefn() { + regs.ir().write(|w| w.set_tefn(true)); + T::state().tx_waker.wake(); + } + + if ir.ped() || ir.pea() { + regs.ir().write(|w| { + w.set_ped(true); + w.set_pea(true); + }); + } + + if ir.rfn(0) { + regs.ir().write(|w| w.set_rfn(0, true)); + T::state().rx_waker.wake(); + } + + if ir.rfn(1) { + regs.ir().write(|w| w.set_rfn(1, true)); + T::state().rx_waker.wake(); + } + } +} + +/// Interrupt handler channel 1. +pub struct IT1InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for IT1InterruptHandler { + unsafe fn on_interrupt() {} +} + +impl BusError { + fn try_from(lec: LastErrorCode) -> Option { + match lec { + LastErrorCode::AckError => Some(BusError::Acknowledge), + // `0` data bit encodes a dominant state. `1` data bit is recessive. + // Bit0Error: During transmit, the node wanted to send a 0 but monitored a 1 + LastErrorCode::Bit0Error => Some(BusError::BitRecessive), + LastErrorCode::Bit1Error => Some(BusError::BitDominant), + LastErrorCode::CRCError => Some(BusError::Crc), + LastErrorCode::FormError => Some(BusError::Form), + LastErrorCode::StuffError => Some(BusError::Stuff), + _ => None, + } + } +} + +/// Operating modes trait +pub trait FdcanOperatingMode {} +impl FdcanOperatingMode for fdcan::PoweredDownMode {} +impl FdcanOperatingMode for fdcan::ConfigMode {} +impl FdcanOperatingMode for fdcan::InternalLoopbackMode {} +impl FdcanOperatingMode for fdcan::ExternalLoopbackMode {} +impl FdcanOperatingMode for fdcan::NormalOperationMode {} +impl FdcanOperatingMode for fdcan::RestrictedOperationMode {} +impl FdcanOperatingMode for fdcan::BusMonitoringMode {} +impl FdcanOperatingMode for fdcan::TestMode {} + +/// FDCAN Instance +pub struct Fdcan<'d, T: Instance, M: FdcanOperatingMode> { + /// Reference to internals. + pub can: fdcan::FdCan, M>, + ns_per_timer_tick: u64, // For FDCAN internal timer +} + +fn calc_ns_per_timer_tick(mode: config::FrameTransmissionConfig) -> u64 { + match mode { + // Use timestamp from Rx FIFO to adjust timestamp reported to user + config::FrameTransmissionConfig::ClassicCanOnly => { + let freq = T::frequency(); + let prescale: u64 = + ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; + 1_000_000_000 as u64 / (freq.0 as u64 * prescale) + } + // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use + // timer3 instead which is too hard to do from this module. + _ => 0, + } +} + +#[cfg(feature = "time")] +fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> embassy_time::Instant { + let now_embassy = embassy_time::Instant::now(); + if ns_per_timer_tick == 0 { + return now_embassy; + } + let now_can = { T::regs().tscv().read().tsc() }; + let delta = now_can.overflowing_sub(ts_val).0 as u64; + let ns = ns_per_timer_tick * delta as u64; + now_embassy - embassy_time::Duration::from_nanos(ns) +} + +fn curr_error() -> Option { + let err = { T::regs().psr().read() }; + if err.bo() { + return Some(BusError::BusOff); + } else if err.ep() { + return Some(BusError::BusPassive); + } else if err.ew() { + return Some(BusError::BusWarning); + } else { + cfg_if! { + if #[cfg(stm32h7)] { + let lec = err.lec(); + } else { + let lec = err.lec().to_bits(); + } + } + if let Ok(err) = LastErrorCode::try_from(lec) { + return BusError::try_from(err); + } + } + None +} + +impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { + /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. + /// You must call [Fdcan::enable_non_blocking] to use the peripheral. + pub fn new( + peri: impl Peripheral

+ 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + _irqs: impl interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + 'd, + ) -> Fdcan<'d, T, fdcan::ConfigMode> { + into_ref!(peri, rx, tx); + + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + T::enable_and_reset(); + + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + let mut can = fdcan::FdCan::new(FdcanInstance(peri)).into_config_mode(); + + T::configure_msg_ram(); + unsafe { + // Enable timestamping + #[cfg(not(stm32h7))] + T::regs() + .tscc() + .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT)); + #[cfg(stm32h7)] + T::regs().tscc().write(|w| w.set_tss(0x01)); + + T::IT0Interrupt::unpend(); // Not unsafe + T::IT0Interrupt::enable(); + + T::IT1Interrupt::unpend(); // Not unsafe + T::IT1Interrupt::enable(); + + // this isn't really documented in the reference manual + // but corresponding txbtie bit has to be set for the TC (TxComplete) interrupt to fire + T::regs().txbtie().write(|w| w.0 = 0xffff_ffff); + } + + can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo0NewMsg); + can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo1NewMsg); + can.enable_interrupt(fdcan::interrupt::Interrupt::TxComplete); + can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_0, true); + can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_1, true); + + let ns_per_timer_tick = calc_ns_per_timer_tick::(can.get_config().frame_transmit); + Self { can, ns_per_timer_tick } + } + + /// Configures the bit timings calculated from supplied bitrate. + pub fn set_bitrate(&mut self, bitrate: u32) { + let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + self.can.set_nominal_bit_timing(config::NominalBitTiming { + sync_jump_width: bit_timing.sync_jump_width, + prescaler: bit_timing.prescaler, + seg1: bit_timing.seg1, + seg2: bit_timing.seg2, + }); + } +} + +macro_rules! impl_transition { + ($from_mode:ident, $to_mode:ident, $name:ident, $func: ident) => { + impl<'d, T: Instance> Fdcan<'d, T, fdcan::$from_mode> { + /// Transition from $from_mode:ident mode to $to_mode:ident mode + pub fn $name(self) -> Fdcan<'d, T, fdcan::$to_mode> { + let ns_per_timer_tick = calc_ns_per_timer_tick::(self.can.get_config().frame_transmit); + Fdcan { + can: self.can.$func(), + ns_per_timer_tick, + } + } + } + }; +} + +impl_transition!(PoweredDownMode, ConfigMode, into_config_mode, into_config_mode); +impl_transition!(InternalLoopbackMode, ConfigMode, into_config_mode, into_config_mode); + +impl_transition!(ConfigMode, NormalOperationMode, into_normal_mode, into_normal); +impl_transition!( + ConfigMode, + ExternalLoopbackMode, + into_external_loopback_mode, + into_external_loopback +); +impl_transition!( + ConfigMode, + InternalLoopbackMode, + into_internal_loopback_mode, + into_internal_loopback +); + +impl<'d, T: Instance, M: FdcanOperatingMode> Fdcan<'d, T, M> +where + M: fdcan::Transmit, + M: fdcan::Receive, +{ + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + pub async fn write(&mut self, frame: &TxFrame) -> Option { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(dropped) = self + .can + .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { + TxFrame::from_preserved(hdr, data32) + }) + { + return Poll::Ready(dropped.flatten()); + } + + // Couldn't replace any lower priority frames. Need to wait for some mailboxes + // to clear. + Poll::Pending + }) + .await + } + + /// Flush one of the TX mailboxes. + pub async fn flush(&self, mb: fdcan::Mailbox) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let idx: u8 = mb.into(); + let idx = 1 << idx; + if !T::regs().txbrp().read().trp(idx) { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Returns the next received message frame + pub async fn read(&mut self) -> Result { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + T::state().rx_waker.register(cx.waker()); + + let mut buffer: [u8; 64] = [0; 64]; + if let Ok(rx) = self.can.receive0(&mut buffer) { + // rx: fdcan::ReceiveOverrun + // TODO: report overrun? + // for now we just drop it + + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Ok(rx) = self.can.receive1(&mut buffer) { + // rx: fdcan::ReceiveOverrun + // TODO: report overrun? + // for now we just drop it + + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Some(err) = curr_error::() { + // TODO: this is probably wrong + return Poll::Ready(Err(err)); + } + Poll::Pending + }) + .await + } + + /// Split instance into separate Tx(write) and Rx(read) portions + pub fn split<'c>(&'c mut self) -> (FdcanTx<'c, 'd, T, M>, FdcanRx<'c, 'd, T, M>) { + let (mut _control, tx, rx0, rx1) = self.can.split_by_ref(); + ( + FdcanTx { _control, tx }, + FdcanRx { + rx0, + rx1, + ns_per_timer_tick: self.ns_per_timer_tick, + }, + ) + } +} + +/// FDCAN Tx only Instance +pub struct FdcanTx<'c, 'd, T: Instance, M: fdcan::Transmit> { + _control: &'c mut fdcan::FdCanControl, M>, + tx: &'c mut fdcan::Tx, M>, +} + +impl<'c, 'd, T: Instance, M: fdcan::Transmit> FdcanTx<'c, 'd, T, M> { + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + pub async fn write(&mut self, frame: &TxFrame) -> Option { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(dropped) = self + .tx + .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { + TxFrame::from_preserved(hdr, data32) + }) + { + return Poll::Ready(dropped.flatten()); + } + + // Couldn't replace any lower priority frames. Need to wait for some mailboxes + // to clear. + Poll::Pending + }) + .await + } +} + +/// FDCAN Rx only Instance +#[allow(dead_code)] +pub struct FdcanRx<'c, 'd, T: Instance, M: fdcan::Receive> { + rx0: &'c mut fdcan::Rx, M, fdcan::Fifo0>, + rx1: &'c mut fdcan::Rx, M, fdcan::Fifo1>, + ns_per_timer_tick: u64, // For FDCAN internal timer +} + +impl<'c, 'd, T: Instance, M: fdcan::Receive> FdcanRx<'c, 'd, T, M> { + /// Returns the next received message frame + pub async fn read(&mut self) -> Result { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + T::state().rx_waker.register(cx.waker()); + + let mut buffer: [u8; 64] = [0; 64]; + if let Ok(rx) = self.rx0.receive(&mut buffer) { + // rx: fdcan::ReceiveOverrun + // TODO: report overrun? + // for now we just drop it + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Ok(rx) = self.rx1.receive(&mut buffer) { + // rx: fdcan::ReceiveOverrun + // TODO: report overrun? + // for now we just drop it + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Some(err) = curr_error::() { + // TODO: this is probably wrong + return Poll::Ready(Err(err)); + } + + Poll::Pending + }) + .await + } +} +impl<'d, T: Instance, M: FdcanOperatingMode> Deref for Fdcan<'d, T, M> { + type Target = fdcan::FdCan, M>; + + fn deref(&self) -> &Self::Target { + &self.can + } +} + +impl<'d, T: Instance, M: FdcanOperatingMode> DerefMut for Fdcan<'d, T, M> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.can + } +} pub(crate) mod sealed { - use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; - use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; pub struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel, + pub rx_waker: AtomicWaker, } impl State { @@ -16,25 +579,122 @@ pub(crate) mod sealed { Self { tx_waker: AtomicWaker::new(), err_waker: AtomicWaker::new(), - rx_queue: Channel::new(), + rx_waker: AtomicWaker::new(), } } } pub trait Instance { + const REGISTERS: *mut fdcan::RegisterBlock; + const MSG_RAM: *mut fdcan::message_ram::RegisterBlock; + const MSG_RAM_OFFSET: usize; + fn regs() -> &'static crate::pac::can::Fdcan; fn state() -> &'static State; + + #[cfg(not(stm32h7))] + fn configure_msg_ram() {} + + #[cfg(stm32h7)] + fn configure_msg_ram() { + let r = Self::regs(); + + use fdcan::message_ram::*; + let mut offset_words = Self::MSG_RAM_OFFSET as u16; + + // 11-bit filter + r.sidfc().modify(|w| w.set_flssa(offset_words)); + offset_words += STANDARD_FILTER_MAX as u16; + + // 29-bit filter + r.xidfc().modify(|w| w.set_flesa(offset_words)); + offset_words += 2 * EXTENDED_FILTER_MAX as u16; + + // Rx FIFO 0 and 1 + for i in 0..=1 { + r.rxfc(i).modify(|w| { + w.set_fsa(offset_words); + w.set_fs(RX_FIFO_MAX); + w.set_fwm(RX_FIFO_MAX); + }); + offset_words += 18 * RX_FIFO_MAX as u16; + } + + // Rx buffer - see below + // Tx event FIFO + r.txefc().modify(|w| { + w.set_efsa(offset_words); + w.set_efs(TX_EVENT_MAX); + w.set_efwm(TX_EVENT_MAX); + }); + offset_words += 2 * TX_EVENT_MAX as u16; + + // Tx buffers + r.txbc().modify(|w| { + w.set_tbsa(offset_words); + w.set_tfqs(TX_FIFO_MAX); + }); + offset_words += 18 * TX_FIFO_MAX as u16; + + // Rx Buffer - not used + r.rxbc().modify(|w| { + w.set_rbsa(offset_words); + }); + + // TX event FIFO? + // Trigger memory? + + // Set the element sizes to 16 bytes + r.rxesc().modify(|w| { + w.set_rbds(0b111); + for i in 0..=1 { + w.set_fds(i, 0b111); + } + }); + r.txesc().modify(|w| { + w.set_tbds(0b111); + }) + } } } -/// Interruptable FDCAN instance. -pub trait InterruptableInstance {} -/// FDCAN instance. -pub trait Instance: sealed::Instance + InterruptableInstance + 'static {} +/// Trait for FDCAN interrupt channel 0 +pub trait IT0Instance { + /// Type for FDCAN interrupt channel 0 + type IT0Interrupt: crate::interrupt::typelevel::Interrupt; +} -foreach_peripheral!( - (can, $inst:ident) => { +/// Trait for FDCAN interrupt channel 1 +pub trait IT1Instance { + /// Type for FDCAN interrupt channel 1 + type IT1Interrupt: crate::interrupt::typelevel::Interrupt; +} + +/// InterruptableInstance trait +pub trait InterruptableInstance: IT0Instance + IT1Instance {} +/// Instance trait +pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + 'static {} +/// Fdcan Instance struct +pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); + +unsafe impl<'d, T: Instance> fdcan::message_ram::Instance for FdcanInstance<'d, T> { + const MSG_RAM: *mut RegisterBlock = T::MSG_RAM; +} + +unsafe impl<'d, T: Instance> fdcan::Instance for FdcanInstance<'d, T> +where + FdcanInstance<'d, T>: fdcan::message_ram::Instance, +{ + const REGISTERS: *mut fdcan::RegisterBlock = T::REGISTERS; +} + +macro_rules! impl_fdcan { + ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { impl sealed::Instance for peripherals::$inst { + const REGISTERS: *mut fdcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; + const MSG_RAM: *mut fdcan::message_ram::RegisterBlock = crate::pac::$msg_ram_inst.as_ptr() as *mut _; + const MSG_RAM_OFFSET: usize = $msg_ram_offset; + fn regs() -> &'static crate::pac::can::Fdcan { &crate::pac::$inst } @@ -47,8 +707,40 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} + foreach_interrupt!( + ($inst,can,FDCAN,IT0,$irq:ident) => { + impl IT0Instance for peripherals::$inst { + type IT0Interrupt = crate::interrupt::typelevel::$irq; + } + }; + ($inst,can,FDCAN,IT1,$irq:ident) => { + impl IT1Instance for peripherals::$inst { + type IT1Interrupt = crate::interrupt::typelevel::$irq; + } + }; + ); + impl InterruptableInstance for peripherals::$inst {} }; + + ($inst:ident, $msg_ram_inst:ident) => { + impl_fdcan!($inst, $msg_ram_inst, 0); + }; +} + +#[cfg(not(stm32h7))] +foreach_peripheral!( + (can, FDCAN) => { impl_fdcan!(FDCAN, FDCANRAM); }; + (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM1); }; + (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM2); }; + (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM3); }; +); + +#[cfg(stm32h7)] +foreach_peripheral!( + (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM, 0x0000); }; + (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM, 0x0C00); }; + (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM, 0x1800); }; ); pin_trait!(RxPin, Instance); diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs new file mode 100644 index 000000000..fcdbbad62 --- /dev/null +++ b/embassy-stm32/src/can/util.rs @@ -0,0 +1,117 @@ +//! Utility functions shared between CAN controller types. + +use core::num::{NonZeroU16, NonZeroU8}; + +/// Shared struct to represent bit timings used by calc_can_timings. +#[derive(Clone, Copy, Debug)] +pub struct NominalBitTiming { + /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit + /// time is built up from a multiple of this quanta. Valid values are 1 to 512. + pub prescaler: NonZeroU16, + /// Valid values are 1 to 128. + pub seg1: NonZeroU8, + /// Valid values are 1 to 255. + pub seg2: NonZeroU8, + /// Valid values are 1 to 128. + pub sync_jump_width: NonZeroU8, +} + +/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency +pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option { + const BS1_MAX: u8 = 16; + const BS2_MAX: u8 = 8; + const MAX_SAMPLE_POINT_PERMILL: u16 = 900; + + let periph_clock = periph_clock.0; + + if can_bitrate < 1000 { + return None; + } + + // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG + // CAN in Automation, 2003 + // + // According to the source, optimal quanta per bit are: + // Bitrate Optimal Maximum + // 1000 kbps 8 10 + // 500 kbps 16 17 + // 250 kbps 16 17 + // 125 kbps 16 17 + let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; + + // Computing (prescaler * BS): + // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual + // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified + // let: + // BS = 1 + BS1 + BS2 -- Number of time quanta per bit + // PRESCALER_BS = PRESCALER * BS + // ==> + // PRESCALER_BS = PCLK / BITRATE + let prescaler_bs = periph_clock / can_bitrate; + + // Searching for such prescaler value so that the number of quanta per bit is highest. + let mut bs1_bs2_sum = max_quanta_per_bit - 1; + while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { + if bs1_bs2_sum <= 2 { + return None; // No solution + } + bs1_bs2_sum -= 1; + } + + let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; + if (prescaler < 1) || (prescaler > 1024) { + return None; // No solution + } + + // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. + // We need to find such values so that the sample point is as close as possible to the optimal value, + // which is 87.5%, which is 7/8. + // + // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) + // {{bs2 -> (1 + bs1)/7}} + // + // Hence: + // bs2 = (1 + bs1) / 7 + // bs1 = (7 * bs1_bs2_sum - 1) / 8 + // + // Sample point location can be computed as follows: + // Sample point location = (1 + bs1) / (1 + bs1 + bs2) + // + // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: + // - With rounding to nearest + // - With rounding to zero + let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first + let mut bs2 = bs1_bs2_sum - bs1; + core::assert!(bs1_bs2_sum > bs1); + + let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; + if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { + // Nope, too far; now rounding to zero + bs1 = (7 * bs1_bs2_sum - 1) / 8; + bs2 = bs1_bs2_sum - bs1; + } + + // Check is BS1 and BS2 are in range + if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { + return None; + } + + // Check if final bitrate matches the requested + if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { + return None; + } + + // One is recommended by DS-015, CANOpen, and DeviceNet + let sync_jump_width = core::num::NonZeroU8::new(1)?; + + let seg1 = core::num::NonZeroU8::new(bs1)?; + let seg2 = core::num::NonZeroU8::new(bs2)?; + let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; + + Some(NominalBitTiming { + sync_jump_width, + prescaler: nz_prescaler, + seg1, + seg2, + }) +} diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index fca364c21..891f0490b 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -3,8 +3,8 @@ use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; use stm32_metapac::FLASH; pub use crate::pac::rcc::vals::{ - Adcsel as AdcClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, Pllp as PllP, Pllq as PllQ, - Pllr as PllR, Ppre as APBPrescaler, + Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, + Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler, }; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -87,6 +87,7 @@ pub struct Config { pub clock_48mhz_src: Option, pub adc12_clock_source: AdcClockSource, pub adc345_clock_source: AdcClockSource, + pub fdcan_clock_source: FdCanClockSource, pub ls: super::LsConfig, } @@ -104,6 +105,7 @@ impl Default for Config { clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), adc12_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE, + fdcan_clock_source: FdCanClockSource::PCLK1, ls: Default::default(), } } @@ -282,6 +284,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); + RCC.ccipr().modify(|w| w.set_fdcansel(config.fdcan_clock_source)); let adc12_ck = match config.adc12_clock_source { AdcClockSource::DISABLE => None, diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 15b51a398..dcaf2dced 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -7,8 +7,8 @@ pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; #[cfg(stm32h7)] pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; pub use crate::pac::rcc::vals::{ - Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, - Pllsrc as PllSource, Sw as Sysclk, + Ckpersel as PerClockSource, Fdcansel as FdCanClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, + Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk, }; use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre}; use crate::pac::{FLASH, PWR, RCC}; @@ -212,6 +212,8 @@ pub struct Config { pub per_clock_source: PerClockSource, pub adc_clock_source: AdcClockSource, + pub fdcan_clock_source: FdCanClockSource, + pub timer_prescaler: TimerPrescaler, pub voltage_scale: VoltageScale, pub ls: super::LsConfig, @@ -248,6 +250,8 @@ impl Default for Config { #[cfg(stm32h7)] adc_clock_source: AdcClockSource::PER, + fdcan_clock_source: FdCanClockSource::from_bits(0), // HSE + timer_prescaler: TimerPrescaler::DefaultX2, voltage_scale: VoltageScale::Scale0, ls: Default::default(), @@ -585,7 +589,8 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr5().modify(|w| { w.set_ckpersel(config.per_clock_source); - w.set_adcdacsel(config.adc_clock_source) + w.set_adcdacsel(config.adc_clock_source); + w.set_fdcan12sel(config.fdcan_clock_source) }); } diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs new file mode 100644 index 000000000..727921fba --- /dev/null +++ b/examples/stm32g4/src/bin/can.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::peripherals::*; +use embassy_stm32::{bind_interrupts, can, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FDCAN1_IT0 => can::IT0InterruptHandler; + FDCAN1_IT1 => can::IT1InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Config::default(); + + let peripherals = embassy_stm32::init(config); + + let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + + // 250k bps + can.set_bitrate(250_000); + + info!("Configured"); + + //let mut can = can.into_external_loopback_mode(); + let mut can = can.into_normal_mode(); + + let mut i = 0; + loop { + let frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + info!("Writing frame"); + _ = can.write(&frame).await; + + match can.read().await { + Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), + Err(_err) => error!("Error in frame"), + } + + Timer::after_millis(250).await; + + i += 1; + } +} diff --git a/examples/stm32h5/src/bin/can.rs b/examples/stm32h5/src/bin/can.rs new file mode 100644 index 000000000..2906d1576 --- /dev/null +++ b/examples/stm32h5/src/bin/can.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::peripherals::*; +use embassy_stm32::{bind_interrupts, can, rcc, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FDCAN1_IT0 => can::IT0InterruptHandler; + FDCAN1_IT1 => can::IT1InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + // configure FDCAN to use PLL1_Q at 64 MHz + config.rcc.pll1 = Some(rcc::Pll { + source: rcc::PllSource::HSI, + prediv: rcc::PllPreDiv::DIV4, + mul: rcc::PllMul::MUL8, + divp: None, + divq: Some(rcc::PllDiv::DIV2), + divr: None, + }); + config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q; + + let peripherals = embassy_stm32::init(config); + + let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + + can.can.apply_config( + can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming { + sync_jump_width: 1.try_into().unwrap(), + prescaler: 8.try_into().unwrap(), + seg1: 13.try_into().unwrap(), + seg2: 2.try_into().unwrap(), + }), + ); + + info!("Configured"); + + let mut can = can.into_external_loopback_mode(); + //let mut can = can.into_normal_mode(); + + let mut i = 0; + loop { + let frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + info!("Writing frame"); + _ = can.write(&frame).await; + + match can.read().await { + Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), + Err(_err) => error!("Error in frame"), + } + + Timer::after_millis(250).await; + + i += 1; + } +} diff --git a/examples/stm32h7/src/bin/can.rs b/examples/stm32h7/src/bin/can.rs new file mode 100644 index 000000000..2906d1576 --- /dev/null +++ b/examples/stm32h7/src/bin/can.rs @@ -0,0 +1,74 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::peripherals::*; +use embassy_stm32::{bind_interrupts, can, rcc, Config}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FDCAN1_IT0 => can::IT0InterruptHandler; + FDCAN1_IT1 => can::IT1InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + + // configure FDCAN to use PLL1_Q at 64 MHz + config.rcc.pll1 = Some(rcc::Pll { + source: rcc::PllSource::HSI, + prediv: rcc::PllPreDiv::DIV4, + mul: rcc::PllMul::MUL8, + divp: None, + divq: Some(rcc::PllDiv::DIV2), + divr: None, + }); + config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q; + + let peripherals = embassy_stm32::init(config); + + let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + + can.can.apply_config( + can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming { + sync_jump_width: 1.try_into().unwrap(), + prescaler: 8.try_into().unwrap(), + seg1: 13.try_into().unwrap(), + seg2: 2.try_into().unwrap(), + }), + ); + + info!("Configured"); + + let mut can = can.into_external_loopback_mode(); + //let mut can = can.into_normal_mode(); + + let mut i = 0; + loop { + let frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + info!("Writing frame"); + _ = can.write(&frame).await; + + match can.read().await { + Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), + Err(_err) => error!("Error in frame"), + } + + Timer::after_millis(250).await; + + i += 1; + } +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index bf85f05d2..cb1bd9a50 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -14,11 +14,11 @@ stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] -stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"] +stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] -stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng"] -stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"] +stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"] +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"] +stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] @@ -37,6 +37,7 @@ sdmmc = [] stop = ["embassy-stm32/low-power", "embassy-stm32/low-power-debug-with-sleep"] chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] +fdcan = [] ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] @@ -96,6 +97,11 @@ name = "eth" path = "src/bin/eth.rs" required-features = [ "eth",] +[[bin]] +name = "fdcan" +path = "src/bin/fdcan.rs" +required-features = [ "fdcan",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs new file mode 100644 index 000000000..7363eaa16 --- /dev/null +++ b/tests/stm32/src/bin/fdcan.rs @@ -0,0 +1,243 @@ +#![no_std] +#![no_main] + +// required-features: fdcan + +#[path = "../common.rs"] +mod common; +use common::*; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::peripherals::*; +use embassy_stm32::{bind_interrupts, can, Config}; +use embassy_time::{Duration, Instant}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FDCAN1_IT0 => can::IT0InterruptHandler; + FDCAN1_IT1 => can::IT1InterruptHandler; +}); + +struct TestOptions { + config: Config, + max_latency: Duration, + second_fifo_working: bool, +} + +#[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi", feature = "stm32h563zi"))] +fn options() -> TestOptions { + use embassy_stm32::rcc; + info!("H75 config"); + let mut c = config(); + c.rcc.hse = Some(rcc::Hse { + freq: embassy_stm32::time::Hertz(25_000_000), + mode: rcc::HseMode::Oscillator, + }); + c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; + TestOptions { + config: c, + max_latency: Duration::from_micros(3800), + second_fifo_working: false, + } +} + +#[cfg(any(feature = "stm32h7a3zi"))] +fn options() -> TestOptions { + use embassy_stm32::rcc; + info!("H7a config"); + let mut c = config(); + c.rcc.hse = Some(rcc::Hse { + freq: embassy_stm32::time::Hertz(25_000_000), + mode: rcc::HseMode::Oscillator, + }); + c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; + TestOptions { + config: c, + max_latency: Duration::from_micros(5500), + second_fifo_working: false, + } +} + +#[cfg(any(feature = "stm32g491re"))] +fn options() -> TestOptions { + info!("G4 config"); + TestOptions { + config: config(), + max_latency: Duration::from_micros(500), + second_fifo_working: true, + } +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + //let peripherals = embassy_stm32::init(config()); + + let options = options(); + let peripherals = embassy_stm32::init(options.config); + + let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs); + + // 250k bps + can.set_bitrate(250_000); + + can.can.set_extended_filter( + can::filter::ExtendedFilterSlot::_0, + can::filter::ExtendedFilter::accept_all_into_fifo1(), + ); + + let mut can = can.into_internal_loopback_mode(); + + info!("CAN Configured"); + + let mut i: u8 = 0; + loop { + let tx_frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + + info!("Transmitting frame..."); + let tx_ts = Instant::now(); + can.write(&tx_frame).await; + + let envelope = can.read().await.unwrap(); + info!("Frame received!"); + + // Check data. + assert!(i == envelope.data()[0], "{} == {}", i, envelope.data()[0]); + + info!("loopback time {}", envelope.header.time_stamp); + info!("loopback frame {=u8}", envelope.data()[0]); + let latency = envelope.timestamp.saturating_duration_since(tx_ts); + info!("loopback latency {} us", latency.as_micros()); + + // Theoretical minimum latency is 55us, actual is usually ~80us + const MIN_LATENCY: Duration = Duration::from_micros(50); + // Was failing at 150 but we are not getting a real time stamp. I'm not + // sure if there are other delays + assert!( + MIN_LATENCY <= latency && latency <= options.max_latency, + "{} <= {} <= {}", + MIN_LATENCY, + latency, + options.max_latency + ); + + i += 1; + if i > 10 { + break; + } + } + + let max_buffered = if options.second_fifo_working { 6 } else { 3 }; + + // Below here, check that we can receive from both FIFO0 and FIFO0 + // Above we configured FIFO1 for extended ID packets. There are only 3 slots + // in each FIFO so make sure we write enough to fill them both up before reading. + for i in 0..3 { + // Try filling up the RX FIFO0 buffers with standard packets + let tx_frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + info!("Transmitting frame {}", i); + can.write(&tx_frame).await; + } + for i in 3..max_buffered { + // Try filling up the RX FIFO0 buffers with extended packets + let tx_frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::ExtendedId::new(0x1232344).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + + info!("Transmitting frame {}", i); + can.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for i in 0..max_buffered { + let envelope = can.read().await.unwrap(); + match envelope.header.id { + can::Id::Extended(id) => { + info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); + } + can::Id::Standard(id) => { + info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); + } + } + } + + // Test again with a split + let (mut tx, mut rx) = can.split(); + for i in 0..3 { + // Try filling up the RX FIFO0 buffers with standard packets + let tx_frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::StandardId::new(0x123).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + + info!("Transmitting frame {}", i); + tx.write(&tx_frame).await; + } + for i in 3..max_buffered { + // Try filling up the RX FIFO0 buffers with extended packets + let tx_frame = can::TxFrame::new( + can::TxFrameHeader { + len: 1, + frame_format: can::FrameFormat::Standard, + id: can::ExtendedId::new(0x1232344).unwrap().into(), + bit_rate_switching: false, + marker: None, + }, + &[i], + ) + .unwrap(); + + info!("Transmitting frame {}", i); + tx.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for i in 0..max_buffered { + let envelope = rx.read().await.unwrap(); + match envelope.header.id { + can::Id::Extended(id) => { + info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); + } + can::Id::Standard(id) => { + info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); + } + } + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +}