diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-02-23 00:07:05 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-23 00:07:05 +0100 |
| commit | 9c918f6474cedfa7d6a2d36c416c3c380ee0c747 (patch) | |
| tree | 0fb53f542831216e6e63a00239df115a780f5d5d | |
| parent | 2cceeab56481b873d8a1c41ddf244fc6c34f5dcc (diff) | |
| parent | 3f93105e9fe7c5cbdada46a4367b01448fd61c62 (diff) | |
Merge pull request #2588 from cschuhen/feature/fdcan_buffered
Add FDCAN Buffered mode.
22 files changed, 5084 insertions, 1026 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 389ed0041..d585d2cd6 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -55,10 +55,12 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un | |||
| 55 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 55 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 56 | embedded-hal-async = { version = "1.0" } | 56 | embedded-hal-async = { version = "1.0" } |
| 57 | embedded-hal-nb = { version = "1.0" } | 57 | embedded-hal-nb = { version = "1.0" } |
| 58 | embedded-can = "0.4" | ||
| 58 | 59 | ||
| 59 | embedded-storage = "0.3.1" | 60 | embedded-storage = "0.3.1" |
| 60 | embedded-storage-async = { version = "0.4.1" } | 61 | embedded-storage-async = { version = "0.4.1" } |
| 61 | 62 | ||
| 63 | |||
| 62 | defmt = { version = "0.3", optional = true } | 64 | defmt = { version = "0.3", optional = true } |
| 63 | log = { version = "0.4.14", optional = true } | 65 | log = { version = "0.4.14", optional = true } |
| 64 | cortex-m-rt = ">=0.6.15,<0.8" | 66 | cortex-m-rt = ">=0.6.15,<0.8" |
| @@ -80,7 +82,10 @@ chrono = { version = "^0.4", default-features = false, optional = true} | |||
| 80 | bit_field = "0.10.2" | 82 | bit_field = "0.10.2" |
| 81 | document-features = "0.2.7" | 83 | document-features = "0.2.7" |
| 82 | 84 | ||
| 83 | fdcan = { version = "0.2.0", optional = true } | 85 | static_assertions = { version = "1.1" } |
| 86 | volatile-register = { version = "0.2.1" } | ||
| 87 | |||
| 88 | |||
| 84 | 89 | ||
| 85 | [dev-dependencies] | 90 | [dev-dependencies] |
| 86 | critical-section = { version = "1.1", features = ["std"] } | 91 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -695,373 +700,373 @@ stm32f779ai = [ "stm32-metapac/stm32f779ai" ] | |||
| 695 | stm32f779bi = [ "stm32-metapac/stm32f779bi" ] | 700 | stm32f779bi = [ "stm32-metapac/stm32f779bi" ] |
| 696 | stm32f779ii = [ "stm32-metapac/stm32f779ii" ] | 701 | stm32f779ii = [ "stm32-metapac/stm32f779ii" ] |
| 697 | stm32f779ni = [ "stm32-metapac/stm32f779ni" ] | 702 | stm32f779ni = [ "stm32-metapac/stm32f779ni" ] |
| 698 | stm32g030c6 = [ "stm32-metapac/stm32g030c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 703 | stm32g030c6 = [ "stm32-metapac/stm32g030c6" ] |
| 699 | stm32g030c8 = [ "stm32-metapac/stm32g030c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 704 | stm32g030c8 = [ "stm32-metapac/stm32g030c8" ] |
| 700 | stm32g030f6 = [ "stm32-metapac/stm32g030f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 705 | stm32g030f6 = [ "stm32-metapac/stm32g030f6" ] |
| 701 | stm32g030j6 = [ "stm32-metapac/stm32g030j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 706 | stm32g030j6 = [ "stm32-metapac/stm32g030j6" ] |
| 702 | stm32g030k6 = [ "stm32-metapac/stm32g030k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 707 | stm32g030k6 = [ "stm32-metapac/stm32g030k6" ] |
| 703 | stm32g030k8 = [ "stm32-metapac/stm32g030k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 708 | stm32g030k8 = [ "stm32-metapac/stm32g030k8" ] |
| 704 | stm32g031c4 = [ "stm32-metapac/stm32g031c4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 709 | stm32g031c4 = [ "stm32-metapac/stm32g031c4" ] |
| 705 | stm32g031c6 = [ "stm32-metapac/stm32g031c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 710 | stm32g031c6 = [ "stm32-metapac/stm32g031c6" ] |
| 706 | stm32g031c8 = [ "stm32-metapac/stm32g031c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 711 | stm32g031c8 = [ "stm32-metapac/stm32g031c8" ] |
| 707 | stm32g031f4 = [ "stm32-metapac/stm32g031f4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 712 | stm32g031f4 = [ "stm32-metapac/stm32g031f4" ] |
| 708 | stm32g031f6 = [ "stm32-metapac/stm32g031f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 713 | stm32g031f6 = [ "stm32-metapac/stm32g031f6" ] |
| 709 | stm32g031f8 = [ "stm32-metapac/stm32g031f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 714 | stm32g031f8 = [ "stm32-metapac/stm32g031f8" ] |
| 710 | stm32g031g4 = [ "stm32-metapac/stm32g031g4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 715 | stm32g031g4 = [ "stm32-metapac/stm32g031g4" ] |
| 711 | stm32g031g6 = [ "stm32-metapac/stm32g031g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 716 | stm32g031g6 = [ "stm32-metapac/stm32g031g6" ] |
| 712 | stm32g031g8 = [ "stm32-metapac/stm32g031g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 717 | stm32g031g8 = [ "stm32-metapac/stm32g031g8" ] |
| 713 | stm32g031j4 = [ "stm32-metapac/stm32g031j4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 718 | stm32g031j4 = [ "stm32-metapac/stm32g031j4" ] |
| 714 | stm32g031j6 = [ "stm32-metapac/stm32g031j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 719 | stm32g031j6 = [ "stm32-metapac/stm32g031j6" ] |
| 715 | stm32g031k4 = [ "stm32-metapac/stm32g031k4", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 720 | stm32g031k4 = [ "stm32-metapac/stm32g031k4" ] |
| 716 | stm32g031k6 = [ "stm32-metapac/stm32g031k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 721 | stm32g031k6 = [ "stm32-metapac/stm32g031k6" ] |
| 717 | stm32g031k8 = [ "stm32-metapac/stm32g031k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 722 | stm32g031k8 = [ "stm32-metapac/stm32g031k8" ] |
| 718 | stm32g031y8 = [ "stm32-metapac/stm32g031y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 723 | stm32g031y8 = [ "stm32-metapac/stm32g031y8" ] |
| 719 | stm32g041c6 = [ "stm32-metapac/stm32g041c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 724 | stm32g041c6 = [ "stm32-metapac/stm32g041c6" ] |
| 720 | stm32g041c8 = [ "stm32-metapac/stm32g041c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 725 | stm32g041c8 = [ "stm32-metapac/stm32g041c8" ] |
| 721 | stm32g041f6 = [ "stm32-metapac/stm32g041f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 726 | stm32g041f6 = [ "stm32-metapac/stm32g041f6" ] |
| 722 | stm32g041f8 = [ "stm32-metapac/stm32g041f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 727 | stm32g041f8 = [ "stm32-metapac/stm32g041f8" ] |
| 723 | stm32g041g6 = [ "stm32-metapac/stm32g041g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 728 | stm32g041g6 = [ "stm32-metapac/stm32g041g6" ] |
| 724 | stm32g041g8 = [ "stm32-metapac/stm32g041g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 729 | stm32g041g8 = [ "stm32-metapac/stm32g041g8" ] |
| 725 | stm32g041j6 = [ "stm32-metapac/stm32g041j6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 730 | stm32g041j6 = [ "stm32-metapac/stm32g041j6" ] |
| 726 | stm32g041k6 = [ "stm32-metapac/stm32g041k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 731 | stm32g041k6 = [ "stm32-metapac/stm32g041k6" ] |
| 727 | stm32g041k8 = [ "stm32-metapac/stm32g041k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 732 | stm32g041k8 = [ "stm32-metapac/stm32g041k8" ] |
| 728 | stm32g041y8 = [ "stm32-metapac/stm32g041y8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 733 | stm32g041y8 = [ "stm32-metapac/stm32g041y8" ] |
| 729 | stm32g050c6 = [ "stm32-metapac/stm32g050c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 734 | stm32g050c6 = [ "stm32-metapac/stm32g050c6" ] |
| 730 | stm32g050c8 = [ "stm32-metapac/stm32g050c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 735 | stm32g050c8 = [ "stm32-metapac/stm32g050c8" ] |
| 731 | stm32g050f6 = [ "stm32-metapac/stm32g050f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 736 | stm32g050f6 = [ "stm32-metapac/stm32g050f6" ] |
| 732 | stm32g050k6 = [ "stm32-metapac/stm32g050k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 737 | stm32g050k6 = [ "stm32-metapac/stm32g050k6" ] |
| 733 | stm32g050k8 = [ "stm32-metapac/stm32g050k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 738 | stm32g050k8 = [ "stm32-metapac/stm32g050k8" ] |
| 734 | stm32g051c6 = [ "stm32-metapac/stm32g051c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 739 | stm32g051c6 = [ "stm32-metapac/stm32g051c6" ] |
| 735 | stm32g051c8 = [ "stm32-metapac/stm32g051c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 740 | stm32g051c8 = [ "stm32-metapac/stm32g051c8" ] |
| 736 | stm32g051f6 = [ "stm32-metapac/stm32g051f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 741 | stm32g051f6 = [ "stm32-metapac/stm32g051f6" ] |
| 737 | stm32g051f8 = [ "stm32-metapac/stm32g051f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 742 | stm32g051f8 = [ "stm32-metapac/stm32g051f8" ] |
| 738 | stm32g051g6 = [ "stm32-metapac/stm32g051g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 743 | stm32g051g6 = [ "stm32-metapac/stm32g051g6" ] |
| 739 | stm32g051g8 = [ "stm32-metapac/stm32g051g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 744 | stm32g051g8 = [ "stm32-metapac/stm32g051g8" ] |
| 740 | stm32g051k6 = [ "stm32-metapac/stm32g051k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 745 | stm32g051k6 = [ "stm32-metapac/stm32g051k6" ] |
| 741 | stm32g051k8 = [ "stm32-metapac/stm32g051k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 746 | stm32g051k8 = [ "stm32-metapac/stm32g051k8" ] |
| 742 | stm32g061c6 = [ "stm32-metapac/stm32g061c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 747 | stm32g061c6 = [ "stm32-metapac/stm32g061c6" ] |
| 743 | stm32g061c8 = [ "stm32-metapac/stm32g061c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 748 | stm32g061c8 = [ "stm32-metapac/stm32g061c8" ] |
| 744 | stm32g061f6 = [ "stm32-metapac/stm32g061f6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 749 | stm32g061f6 = [ "stm32-metapac/stm32g061f6" ] |
| 745 | stm32g061f8 = [ "stm32-metapac/stm32g061f8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 750 | stm32g061f8 = [ "stm32-metapac/stm32g061f8" ] |
| 746 | stm32g061g6 = [ "stm32-metapac/stm32g061g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 751 | stm32g061g6 = [ "stm32-metapac/stm32g061g6" ] |
| 747 | stm32g061g8 = [ "stm32-metapac/stm32g061g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 752 | stm32g061g8 = [ "stm32-metapac/stm32g061g8" ] |
| 748 | stm32g061k6 = [ "stm32-metapac/stm32g061k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 753 | stm32g061k6 = [ "stm32-metapac/stm32g061k6" ] |
| 749 | stm32g061k8 = [ "stm32-metapac/stm32g061k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 754 | stm32g061k8 = [ "stm32-metapac/stm32g061k8" ] |
| 750 | stm32g070cb = [ "stm32-metapac/stm32g070cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 755 | stm32g070cb = [ "stm32-metapac/stm32g070cb" ] |
| 751 | stm32g070kb = [ "stm32-metapac/stm32g070kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 756 | stm32g070kb = [ "stm32-metapac/stm32g070kb" ] |
| 752 | stm32g070rb = [ "stm32-metapac/stm32g070rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 757 | stm32g070rb = [ "stm32-metapac/stm32g070rb" ] |
| 753 | stm32g071c6 = [ "stm32-metapac/stm32g071c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 758 | stm32g071c6 = [ "stm32-metapac/stm32g071c6" ] |
| 754 | stm32g071c8 = [ "stm32-metapac/stm32g071c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 759 | stm32g071c8 = [ "stm32-metapac/stm32g071c8" ] |
| 755 | stm32g071cb = [ "stm32-metapac/stm32g071cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 760 | stm32g071cb = [ "stm32-metapac/stm32g071cb" ] |
| 756 | stm32g071eb = [ "stm32-metapac/stm32g071eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 761 | stm32g071eb = [ "stm32-metapac/stm32g071eb" ] |
| 757 | stm32g071g6 = [ "stm32-metapac/stm32g071g6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 762 | stm32g071g6 = [ "stm32-metapac/stm32g071g6" ] |
| 758 | stm32g071g8 = [ "stm32-metapac/stm32g071g8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 763 | stm32g071g8 = [ "stm32-metapac/stm32g071g8" ] |
| 759 | stm32g071gb = [ "stm32-metapac/stm32g071gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 764 | stm32g071gb = [ "stm32-metapac/stm32g071gb" ] |
| 760 | stm32g071k6 = [ "stm32-metapac/stm32g071k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 765 | stm32g071k6 = [ "stm32-metapac/stm32g071k6" ] |
| 761 | stm32g071k8 = [ "stm32-metapac/stm32g071k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 766 | stm32g071k8 = [ "stm32-metapac/stm32g071k8" ] |
| 762 | stm32g071kb = [ "stm32-metapac/stm32g071kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 767 | stm32g071kb = [ "stm32-metapac/stm32g071kb" ] |
| 763 | stm32g071r6 = [ "stm32-metapac/stm32g071r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 768 | stm32g071r6 = [ "stm32-metapac/stm32g071r6" ] |
| 764 | stm32g071r8 = [ "stm32-metapac/stm32g071r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 769 | stm32g071r8 = [ "stm32-metapac/stm32g071r8" ] |
| 765 | stm32g071rb = [ "stm32-metapac/stm32g071rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 770 | stm32g071rb = [ "stm32-metapac/stm32g071rb" ] |
| 766 | stm32g081cb = [ "stm32-metapac/stm32g081cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 771 | stm32g081cb = [ "stm32-metapac/stm32g081cb" ] |
| 767 | stm32g081eb = [ "stm32-metapac/stm32g081eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 772 | stm32g081eb = [ "stm32-metapac/stm32g081eb" ] |
| 768 | stm32g081gb = [ "stm32-metapac/stm32g081gb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 773 | stm32g081gb = [ "stm32-metapac/stm32g081gb" ] |
| 769 | stm32g081kb = [ "stm32-metapac/stm32g081kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 774 | stm32g081kb = [ "stm32-metapac/stm32g081kb" ] |
| 770 | stm32g081rb = [ "stm32-metapac/stm32g081rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 775 | stm32g081rb = [ "stm32-metapac/stm32g081rb" ] |
| 771 | stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 776 | stm32g0b0ce = [ "stm32-metapac/stm32g0b0ce" ] |
| 772 | stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 777 | stm32g0b0ke = [ "stm32-metapac/stm32g0b0ke" ] |
| 773 | stm32g0b0re = [ "stm32-metapac/stm32g0b0re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 778 | stm32g0b0re = [ "stm32-metapac/stm32g0b0re" ] |
| 774 | stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 779 | stm32g0b0ve = [ "stm32-metapac/stm32g0b0ve" ] |
| 775 | stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 780 | stm32g0b1cb = [ "stm32-metapac/stm32g0b1cb" ] |
| 776 | stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 781 | stm32g0b1cc = [ "stm32-metapac/stm32g0b1cc" ] |
| 777 | stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 782 | stm32g0b1ce = [ "stm32-metapac/stm32g0b1ce" ] |
| 778 | stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 783 | stm32g0b1kb = [ "stm32-metapac/stm32g0b1kb" ] |
| 779 | stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 784 | stm32g0b1kc = [ "stm32-metapac/stm32g0b1kc" ] |
| 780 | stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 785 | stm32g0b1ke = [ "stm32-metapac/stm32g0b1ke" ] |
| 781 | stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 786 | stm32g0b1mb = [ "stm32-metapac/stm32g0b1mb" ] |
| 782 | stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 787 | stm32g0b1mc = [ "stm32-metapac/stm32g0b1mc" ] |
| 783 | stm32g0b1me = [ "stm32-metapac/stm32g0b1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 788 | stm32g0b1me = [ "stm32-metapac/stm32g0b1me" ] |
| 784 | stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 789 | stm32g0b1ne = [ "stm32-metapac/stm32g0b1ne" ] |
| 785 | stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 790 | stm32g0b1rb = [ "stm32-metapac/stm32g0b1rb" ] |
| 786 | stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 791 | stm32g0b1rc = [ "stm32-metapac/stm32g0b1rc" ] |
| 787 | stm32g0b1re = [ "stm32-metapac/stm32g0b1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 792 | stm32g0b1re = [ "stm32-metapac/stm32g0b1re" ] |
| 788 | stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 793 | stm32g0b1vb = [ "stm32-metapac/stm32g0b1vb" ] |
| 789 | stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 794 | stm32g0b1vc = [ "stm32-metapac/stm32g0b1vc" ] |
| 790 | stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 795 | stm32g0b1ve = [ "stm32-metapac/stm32g0b1ve" ] |
| 791 | stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 796 | stm32g0c1cc = [ "stm32-metapac/stm32g0c1cc" ] |
| 792 | stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 797 | stm32g0c1ce = [ "stm32-metapac/stm32g0c1ce" ] |
| 793 | stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 798 | stm32g0c1kc = [ "stm32-metapac/stm32g0c1kc" ] |
| 794 | stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 799 | stm32g0c1ke = [ "stm32-metapac/stm32g0c1ke" ] |
| 795 | stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 800 | stm32g0c1mc = [ "stm32-metapac/stm32g0c1mc" ] |
| 796 | stm32g0c1me = [ "stm32-metapac/stm32g0c1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 801 | stm32g0c1me = [ "stm32-metapac/stm32g0c1me" ] |
| 797 | stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 802 | stm32g0c1ne = [ "stm32-metapac/stm32g0c1ne" ] |
| 798 | stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 803 | stm32g0c1rc = [ "stm32-metapac/stm32g0c1rc" ] |
| 799 | stm32g0c1re = [ "stm32-metapac/stm32g0c1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 804 | stm32g0c1re = [ "stm32-metapac/stm32g0c1re" ] |
| 800 | stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 805 | stm32g0c1vc = [ "stm32-metapac/stm32g0c1vc" ] |
| 801 | stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 806 | stm32g0c1ve = [ "stm32-metapac/stm32g0c1ve" ] |
| 802 | stm32g431c6 = [ "stm32-metapac/stm32g431c6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 807 | stm32g431c6 = [ "stm32-metapac/stm32g431c6" ] |
| 803 | stm32g431c8 = [ "stm32-metapac/stm32g431c8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 808 | stm32g431c8 = [ "stm32-metapac/stm32g431c8" ] |
| 804 | stm32g431cb = [ "stm32-metapac/stm32g431cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 809 | stm32g431cb = [ "stm32-metapac/stm32g431cb" ] |
| 805 | stm32g431k6 = [ "stm32-metapac/stm32g431k6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 810 | stm32g431k6 = [ "stm32-metapac/stm32g431k6" ] |
| 806 | stm32g431k8 = [ "stm32-metapac/stm32g431k8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 811 | stm32g431k8 = [ "stm32-metapac/stm32g431k8" ] |
| 807 | stm32g431kb = [ "stm32-metapac/stm32g431kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 812 | stm32g431kb = [ "stm32-metapac/stm32g431kb" ] |
| 808 | stm32g431m6 = [ "stm32-metapac/stm32g431m6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 813 | stm32g431m6 = [ "stm32-metapac/stm32g431m6" ] |
| 809 | stm32g431m8 = [ "stm32-metapac/stm32g431m8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 814 | stm32g431m8 = [ "stm32-metapac/stm32g431m8" ] |
| 810 | stm32g431mb = [ "stm32-metapac/stm32g431mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 815 | stm32g431mb = [ "stm32-metapac/stm32g431mb" ] |
| 811 | stm32g431r6 = [ "stm32-metapac/stm32g431r6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 816 | stm32g431r6 = [ "stm32-metapac/stm32g431r6" ] |
| 812 | stm32g431r8 = [ "stm32-metapac/stm32g431r8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 817 | stm32g431r8 = [ "stm32-metapac/stm32g431r8" ] |
| 813 | stm32g431rb = [ "stm32-metapac/stm32g431rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 818 | stm32g431rb = [ "stm32-metapac/stm32g431rb" ] |
| 814 | stm32g431v6 = [ "stm32-metapac/stm32g431v6", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 819 | stm32g431v6 = [ "stm32-metapac/stm32g431v6" ] |
| 815 | stm32g431v8 = [ "stm32-metapac/stm32g431v8", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 820 | stm32g431v8 = [ "stm32-metapac/stm32g431v8" ] |
| 816 | stm32g431vb = [ "stm32-metapac/stm32g431vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 821 | stm32g431vb = [ "stm32-metapac/stm32g431vb" ] |
| 817 | stm32g441cb = [ "stm32-metapac/stm32g441cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 822 | stm32g441cb = [ "stm32-metapac/stm32g441cb" ] |
| 818 | stm32g441kb = [ "stm32-metapac/stm32g441kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 823 | stm32g441kb = [ "stm32-metapac/stm32g441kb" ] |
| 819 | stm32g441mb = [ "stm32-metapac/stm32g441mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 824 | stm32g441mb = [ "stm32-metapac/stm32g441mb" ] |
| 820 | stm32g441rb = [ "stm32-metapac/stm32g441rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 825 | stm32g441rb = [ "stm32-metapac/stm32g441rb" ] |
| 821 | stm32g441vb = [ "stm32-metapac/stm32g441vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 826 | stm32g441vb = [ "stm32-metapac/stm32g441vb" ] |
| 822 | stm32g471cc = [ "stm32-metapac/stm32g471cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 827 | stm32g471cc = [ "stm32-metapac/stm32g471cc" ] |
| 823 | stm32g471ce = [ "stm32-metapac/stm32g471ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 828 | stm32g471ce = [ "stm32-metapac/stm32g471ce" ] |
| 824 | stm32g471mc = [ "stm32-metapac/stm32g471mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 829 | stm32g471mc = [ "stm32-metapac/stm32g471mc" ] |
| 825 | stm32g471me = [ "stm32-metapac/stm32g471me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 830 | stm32g471me = [ "stm32-metapac/stm32g471me" ] |
| 826 | stm32g471qc = [ "stm32-metapac/stm32g471qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 831 | stm32g471qc = [ "stm32-metapac/stm32g471qc" ] |
| 827 | stm32g471qe = [ "stm32-metapac/stm32g471qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 832 | stm32g471qe = [ "stm32-metapac/stm32g471qe" ] |
| 828 | stm32g471rc = [ "stm32-metapac/stm32g471rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 833 | stm32g471rc = [ "stm32-metapac/stm32g471rc" ] |
| 829 | stm32g471re = [ "stm32-metapac/stm32g471re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 834 | stm32g471re = [ "stm32-metapac/stm32g471re" ] |
| 830 | stm32g471vc = [ "stm32-metapac/stm32g471vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 835 | stm32g471vc = [ "stm32-metapac/stm32g471vc" ] |
| 831 | stm32g471ve = [ "stm32-metapac/stm32g471ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 836 | stm32g471ve = [ "stm32-metapac/stm32g471ve" ] |
| 832 | stm32g473cb = [ "stm32-metapac/stm32g473cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 837 | stm32g473cb = [ "stm32-metapac/stm32g473cb" ] |
| 833 | stm32g473cc = [ "stm32-metapac/stm32g473cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 838 | stm32g473cc = [ "stm32-metapac/stm32g473cc" ] |
| 834 | stm32g473ce = [ "stm32-metapac/stm32g473ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 839 | stm32g473ce = [ "stm32-metapac/stm32g473ce" ] |
| 835 | stm32g473mb = [ "stm32-metapac/stm32g473mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 840 | stm32g473mb = [ "stm32-metapac/stm32g473mb" ] |
| 836 | stm32g473mc = [ "stm32-metapac/stm32g473mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 841 | stm32g473mc = [ "stm32-metapac/stm32g473mc" ] |
| 837 | stm32g473me = [ "stm32-metapac/stm32g473me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 842 | stm32g473me = [ "stm32-metapac/stm32g473me" ] |
| 838 | stm32g473pb = [ "stm32-metapac/stm32g473pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 843 | stm32g473pb = [ "stm32-metapac/stm32g473pb" ] |
| 839 | stm32g473pc = [ "stm32-metapac/stm32g473pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 844 | stm32g473pc = [ "stm32-metapac/stm32g473pc" ] |
| 840 | stm32g473pe = [ "stm32-metapac/stm32g473pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 845 | stm32g473pe = [ "stm32-metapac/stm32g473pe" ] |
| 841 | stm32g473qb = [ "stm32-metapac/stm32g473qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 846 | stm32g473qb = [ "stm32-metapac/stm32g473qb" ] |
| 842 | stm32g473qc = [ "stm32-metapac/stm32g473qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 847 | stm32g473qc = [ "stm32-metapac/stm32g473qc" ] |
| 843 | stm32g473qe = [ "stm32-metapac/stm32g473qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 848 | stm32g473qe = [ "stm32-metapac/stm32g473qe" ] |
| 844 | stm32g473rb = [ "stm32-metapac/stm32g473rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 849 | stm32g473rb = [ "stm32-metapac/stm32g473rb" ] |
| 845 | stm32g473rc = [ "stm32-metapac/stm32g473rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 850 | stm32g473rc = [ "stm32-metapac/stm32g473rc" ] |
| 846 | stm32g473re = [ "stm32-metapac/stm32g473re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 851 | stm32g473re = [ "stm32-metapac/stm32g473re" ] |
| 847 | stm32g473vb = [ "stm32-metapac/stm32g473vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 852 | stm32g473vb = [ "stm32-metapac/stm32g473vb" ] |
| 848 | stm32g473vc = [ "stm32-metapac/stm32g473vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 853 | stm32g473vc = [ "stm32-metapac/stm32g473vc" ] |
| 849 | stm32g473ve = [ "stm32-metapac/stm32g473ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 854 | stm32g473ve = [ "stm32-metapac/stm32g473ve" ] |
| 850 | stm32g474cb = [ "stm32-metapac/stm32g474cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 855 | stm32g474cb = [ "stm32-metapac/stm32g474cb" ] |
| 851 | stm32g474cc = [ "stm32-metapac/stm32g474cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 856 | stm32g474cc = [ "stm32-metapac/stm32g474cc" ] |
| 852 | stm32g474ce = [ "stm32-metapac/stm32g474ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 857 | stm32g474ce = [ "stm32-metapac/stm32g474ce" ] |
| 853 | stm32g474mb = [ "stm32-metapac/stm32g474mb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 858 | stm32g474mb = [ "stm32-metapac/stm32g474mb" ] |
| 854 | stm32g474mc = [ "stm32-metapac/stm32g474mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 859 | stm32g474mc = [ "stm32-metapac/stm32g474mc" ] |
| 855 | stm32g474me = [ "stm32-metapac/stm32g474me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 860 | stm32g474me = [ "stm32-metapac/stm32g474me" ] |
| 856 | stm32g474pb = [ "stm32-metapac/stm32g474pb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 861 | stm32g474pb = [ "stm32-metapac/stm32g474pb" ] |
| 857 | stm32g474pc = [ "stm32-metapac/stm32g474pc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 862 | stm32g474pc = [ "stm32-metapac/stm32g474pc" ] |
| 858 | stm32g474pe = [ "stm32-metapac/stm32g474pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 863 | stm32g474pe = [ "stm32-metapac/stm32g474pe" ] |
| 859 | stm32g474qb = [ "stm32-metapac/stm32g474qb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 864 | stm32g474qb = [ "stm32-metapac/stm32g474qb" ] |
| 860 | stm32g474qc = [ "stm32-metapac/stm32g474qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 865 | stm32g474qc = [ "stm32-metapac/stm32g474qc" ] |
| 861 | stm32g474qe = [ "stm32-metapac/stm32g474qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 866 | stm32g474qe = [ "stm32-metapac/stm32g474qe" ] |
| 862 | stm32g474rb = [ "stm32-metapac/stm32g474rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 867 | stm32g474rb = [ "stm32-metapac/stm32g474rb" ] |
| 863 | stm32g474rc = [ "stm32-metapac/stm32g474rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 868 | stm32g474rc = [ "stm32-metapac/stm32g474rc" ] |
| 864 | stm32g474re = [ "stm32-metapac/stm32g474re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 869 | stm32g474re = [ "stm32-metapac/stm32g474re" ] |
| 865 | stm32g474vb = [ "stm32-metapac/stm32g474vb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 870 | stm32g474vb = [ "stm32-metapac/stm32g474vb" ] |
| 866 | stm32g474vc = [ "stm32-metapac/stm32g474vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 871 | stm32g474vc = [ "stm32-metapac/stm32g474vc" ] |
| 867 | stm32g474ve = [ "stm32-metapac/stm32g474ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 872 | stm32g474ve = [ "stm32-metapac/stm32g474ve" ] |
| 868 | stm32g483ce = [ "stm32-metapac/stm32g483ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 873 | stm32g483ce = [ "stm32-metapac/stm32g483ce" ] |
| 869 | stm32g483me = [ "stm32-metapac/stm32g483me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 874 | stm32g483me = [ "stm32-metapac/stm32g483me" ] |
| 870 | stm32g483pe = [ "stm32-metapac/stm32g483pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 875 | stm32g483pe = [ "stm32-metapac/stm32g483pe" ] |
| 871 | stm32g483qe = [ "stm32-metapac/stm32g483qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 876 | stm32g483qe = [ "stm32-metapac/stm32g483qe" ] |
| 872 | stm32g483re = [ "stm32-metapac/stm32g483re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 877 | stm32g483re = [ "stm32-metapac/stm32g483re" ] |
| 873 | stm32g483ve = [ "stm32-metapac/stm32g483ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 878 | stm32g483ve = [ "stm32-metapac/stm32g483ve" ] |
| 874 | stm32g484ce = [ "stm32-metapac/stm32g484ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 879 | stm32g484ce = [ "stm32-metapac/stm32g484ce" ] |
| 875 | stm32g484me = [ "stm32-metapac/stm32g484me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 880 | stm32g484me = [ "stm32-metapac/stm32g484me" ] |
| 876 | stm32g484pe = [ "stm32-metapac/stm32g484pe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 881 | stm32g484pe = [ "stm32-metapac/stm32g484pe" ] |
| 877 | stm32g484qe = [ "stm32-metapac/stm32g484qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 882 | stm32g484qe = [ "stm32-metapac/stm32g484qe" ] |
| 878 | stm32g484re = [ "stm32-metapac/stm32g484re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 883 | stm32g484re = [ "stm32-metapac/stm32g484re" ] |
| 879 | stm32g484ve = [ "stm32-metapac/stm32g484ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 884 | stm32g484ve = [ "stm32-metapac/stm32g484ve" ] |
| 880 | stm32g491cc = [ "stm32-metapac/stm32g491cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 885 | stm32g491cc = [ "stm32-metapac/stm32g491cc" ] |
| 881 | stm32g491ce = [ "stm32-metapac/stm32g491ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 886 | stm32g491ce = [ "stm32-metapac/stm32g491ce" ] |
| 882 | stm32g491kc = [ "stm32-metapac/stm32g491kc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 887 | stm32g491kc = [ "stm32-metapac/stm32g491kc" ] |
| 883 | stm32g491ke = [ "stm32-metapac/stm32g491ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 888 | stm32g491ke = [ "stm32-metapac/stm32g491ke" ] |
| 884 | stm32g491mc = [ "stm32-metapac/stm32g491mc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 889 | stm32g491mc = [ "stm32-metapac/stm32g491mc" ] |
| 885 | stm32g491me = [ "stm32-metapac/stm32g491me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 890 | stm32g491me = [ "stm32-metapac/stm32g491me" ] |
| 886 | stm32g491rc = [ "stm32-metapac/stm32g491rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 891 | stm32g491rc = [ "stm32-metapac/stm32g491rc" ] |
| 887 | stm32g491re = [ "stm32-metapac/stm32g491re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 892 | stm32g491re = [ "stm32-metapac/stm32g491re" ] |
| 888 | stm32g491vc = [ "stm32-metapac/stm32g491vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 893 | stm32g491vc = [ "stm32-metapac/stm32g491vc" ] |
| 889 | stm32g491ve = [ "stm32-metapac/stm32g491ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 894 | stm32g491ve = [ "stm32-metapac/stm32g491ve" ] |
| 890 | stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 895 | stm32g4a1ce = [ "stm32-metapac/stm32g4a1ce" ] |
| 891 | stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 896 | stm32g4a1ke = [ "stm32-metapac/stm32g4a1ke" ] |
| 892 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 897 | stm32g4a1me = [ "stm32-metapac/stm32g4a1me" ] |
| 893 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 898 | stm32g4a1re = [ "stm32-metapac/stm32g4a1re" ] |
| 894 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 899 | stm32g4a1ve = [ "stm32-metapac/stm32g4a1ve" ] |
| 895 | stm32h503cb = [ "stm32-metapac/stm32h503cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 900 | stm32h503cb = [ "stm32-metapac/stm32h503cb" ] |
| 896 | stm32h503eb = [ "stm32-metapac/stm32h503eb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 901 | stm32h503eb = [ "stm32-metapac/stm32h503eb" ] |
| 897 | stm32h503kb = [ "stm32-metapac/stm32h503kb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 902 | stm32h503kb = [ "stm32-metapac/stm32h503kb" ] |
| 898 | stm32h503rb = [ "stm32-metapac/stm32h503rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 903 | stm32h503rb = [ "stm32-metapac/stm32h503rb" ] |
| 899 | stm32h562ag = [ "stm32-metapac/stm32h562ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 904 | stm32h562ag = [ "stm32-metapac/stm32h562ag" ] |
| 900 | stm32h562ai = [ "stm32-metapac/stm32h562ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 905 | stm32h562ai = [ "stm32-metapac/stm32h562ai" ] |
| 901 | stm32h562ig = [ "stm32-metapac/stm32h562ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 906 | stm32h562ig = [ "stm32-metapac/stm32h562ig" ] |
| 902 | stm32h562ii = [ "stm32-metapac/stm32h562ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 907 | stm32h562ii = [ "stm32-metapac/stm32h562ii" ] |
| 903 | stm32h562rg = [ "stm32-metapac/stm32h562rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 908 | stm32h562rg = [ "stm32-metapac/stm32h562rg" ] |
| 904 | stm32h562ri = [ "stm32-metapac/stm32h562ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 909 | stm32h562ri = [ "stm32-metapac/stm32h562ri" ] |
| 905 | stm32h562vg = [ "stm32-metapac/stm32h562vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 910 | stm32h562vg = [ "stm32-metapac/stm32h562vg" ] |
| 906 | stm32h562vi = [ "stm32-metapac/stm32h562vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 911 | stm32h562vi = [ "stm32-metapac/stm32h562vi" ] |
| 907 | stm32h562zg = [ "stm32-metapac/stm32h562zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 912 | stm32h562zg = [ "stm32-metapac/stm32h562zg" ] |
| 908 | stm32h562zi = [ "stm32-metapac/stm32h562zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 913 | stm32h562zi = [ "stm32-metapac/stm32h562zi" ] |
| 909 | stm32h563ag = [ "stm32-metapac/stm32h563ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 914 | stm32h563ag = [ "stm32-metapac/stm32h563ag" ] |
| 910 | stm32h563ai = [ "stm32-metapac/stm32h563ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 915 | stm32h563ai = [ "stm32-metapac/stm32h563ai" ] |
| 911 | stm32h563ig = [ "stm32-metapac/stm32h563ig", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 916 | stm32h563ig = [ "stm32-metapac/stm32h563ig" ] |
| 912 | stm32h563ii = [ "stm32-metapac/stm32h563ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 917 | stm32h563ii = [ "stm32-metapac/stm32h563ii" ] |
| 913 | stm32h563mi = [ "stm32-metapac/stm32h563mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 918 | stm32h563mi = [ "stm32-metapac/stm32h563mi" ] |
| 914 | stm32h563rg = [ "stm32-metapac/stm32h563rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 919 | stm32h563rg = [ "stm32-metapac/stm32h563rg" ] |
| 915 | stm32h563ri = [ "stm32-metapac/stm32h563ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 920 | stm32h563ri = [ "stm32-metapac/stm32h563ri" ] |
| 916 | stm32h563vg = [ "stm32-metapac/stm32h563vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 921 | stm32h563vg = [ "stm32-metapac/stm32h563vg" ] |
| 917 | stm32h563vi = [ "stm32-metapac/stm32h563vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 922 | stm32h563vi = [ "stm32-metapac/stm32h563vi" ] |
| 918 | stm32h563zg = [ "stm32-metapac/stm32h563zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 923 | stm32h563zg = [ "stm32-metapac/stm32h563zg" ] |
| 919 | stm32h563zi = [ "stm32-metapac/stm32h563zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 924 | stm32h563zi = [ "stm32-metapac/stm32h563zi" ] |
| 920 | stm32h573ai = [ "stm32-metapac/stm32h573ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 925 | stm32h573ai = [ "stm32-metapac/stm32h573ai" ] |
| 921 | stm32h573ii = [ "stm32-metapac/stm32h573ii", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 926 | stm32h573ii = [ "stm32-metapac/stm32h573ii" ] |
| 922 | stm32h573mi = [ "stm32-metapac/stm32h573mi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 927 | stm32h573mi = [ "stm32-metapac/stm32h573mi" ] |
| 923 | stm32h573ri = [ "stm32-metapac/stm32h573ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 928 | stm32h573ri = [ "stm32-metapac/stm32h573ri" ] |
| 924 | stm32h573vi = [ "stm32-metapac/stm32h573vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 929 | stm32h573vi = [ "stm32-metapac/stm32h573vi" ] |
| 925 | stm32h573zi = [ "stm32-metapac/stm32h573zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 930 | stm32h573zi = [ "stm32-metapac/stm32h573zi" ] |
| 926 | stm32h723ve = [ "stm32-metapac/stm32h723ve", "dep:fdcan", "fdcan/fdcan_h7" ] | 931 | stm32h723ve = [ "stm32-metapac/stm32h723ve" ] |
| 927 | stm32h723vg = [ "stm32-metapac/stm32h723vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 932 | stm32h723vg = [ "stm32-metapac/stm32h723vg" ] |
| 928 | stm32h723ze = [ "stm32-metapac/stm32h723ze", "dep:fdcan", "fdcan/fdcan_h7" ] | 933 | stm32h723ze = [ "stm32-metapac/stm32h723ze" ] |
| 929 | stm32h723zg = [ "stm32-metapac/stm32h723zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 934 | stm32h723zg = [ "stm32-metapac/stm32h723zg" ] |
| 930 | stm32h725ae = [ "stm32-metapac/stm32h725ae", "dep:fdcan", "fdcan/fdcan_h7" ] | 935 | stm32h725ae = [ "stm32-metapac/stm32h725ae" ] |
| 931 | stm32h725ag = [ "stm32-metapac/stm32h725ag", "dep:fdcan", "fdcan/fdcan_h7" ] | 936 | stm32h725ag = [ "stm32-metapac/stm32h725ag" ] |
| 932 | stm32h725ie = [ "stm32-metapac/stm32h725ie", "dep:fdcan", "fdcan/fdcan_h7" ] | 937 | stm32h725ie = [ "stm32-metapac/stm32h725ie" ] |
| 933 | stm32h725ig = [ "stm32-metapac/stm32h725ig", "dep:fdcan", "fdcan/fdcan_h7" ] | 938 | stm32h725ig = [ "stm32-metapac/stm32h725ig" ] |
| 934 | stm32h725re = [ "stm32-metapac/stm32h725re", "dep:fdcan", "fdcan/fdcan_h7" ] | 939 | stm32h725re = [ "stm32-metapac/stm32h725re" ] |
| 935 | stm32h725rg = [ "stm32-metapac/stm32h725rg", "dep:fdcan", "fdcan/fdcan_h7" ] | 940 | stm32h725rg = [ "stm32-metapac/stm32h725rg" ] |
| 936 | stm32h725ve = [ "stm32-metapac/stm32h725ve", "dep:fdcan", "fdcan/fdcan_h7" ] | 941 | stm32h725ve = [ "stm32-metapac/stm32h725ve" ] |
| 937 | stm32h725vg = [ "stm32-metapac/stm32h725vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 942 | stm32h725vg = [ "stm32-metapac/stm32h725vg" ] |
| 938 | stm32h725ze = [ "stm32-metapac/stm32h725ze", "dep:fdcan", "fdcan/fdcan_h7" ] | 943 | stm32h725ze = [ "stm32-metapac/stm32h725ze" ] |
| 939 | stm32h725zg = [ "stm32-metapac/stm32h725zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 944 | stm32h725zg = [ "stm32-metapac/stm32h725zg" ] |
| 940 | stm32h730ab = [ "stm32-metapac/stm32h730ab", "dep:fdcan", "fdcan/fdcan_h7" ] | 945 | stm32h730ab = [ "stm32-metapac/stm32h730ab" ] |
| 941 | stm32h730ib = [ "stm32-metapac/stm32h730ib", "dep:fdcan", "fdcan/fdcan_h7" ] | 946 | stm32h730ib = [ "stm32-metapac/stm32h730ib" ] |
| 942 | stm32h730vb = [ "stm32-metapac/stm32h730vb", "dep:fdcan", "fdcan/fdcan_h7" ] | 947 | stm32h730vb = [ "stm32-metapac/stm32h730vb" ] |
| 943 | stm32h730zb = [ "stm32-metapac/stm32h730zb", "dep:fdcan", "fdcan/fdcan_h7" ] | 948 | stm32h730zb = [ "stm32-metapac/stm32h730zb" ] |
| 944 | stm32h733vg = [ "stm32-metapac/stm32h733vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 949 | stm32h733vg = [ "stm32-metapac/stm32h733vg" ] |
| 945 | stm32h733zg = [ "stm32-metapac/stm32h733zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 950 | stm32h733zg = [ "stm32-metapac/stm32h733zg" ] |
| 946 | stm32h735ag = [ "stm32-metapac/stm32h735ag", "dep:fdcan", "fdcan/fdcan_h7" ] | 951 | stm32h735ag = [ "stm32-metapac/stm32h735ag" ] |
| 947 | stm32h735ig = [ "stm32-metapac/stm32h735ig", "dep:fdcan", "fdcan/fdcan_h7" ] | 952 | stm32h735ig = [ "stm32-metapac/stm32h735ig" ] |
| 948 | stm32h735rg = [ "stm32-metapac/stm32h735rg", "dep:fdcan", "fdcan/fdcan_h7" ] | 953 | stm32h735rg = [ "stm32-metapac/stm32h735rg" ] |
| 949 | stm32h735vg = [ "stm32-metapac/stm32h735vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 954 | stm32h735vg = [ "stm32-metapac/stm32h735vg" ] |
| 950 | stm32h735zg = [ "stm32-metapac/stm32h735zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 955 | stm32h735zg = [ "stm32-metapac/stm32h735zg" ] |
| 951 | stm32h742ag = [ "stm32-metapac/stm32h742ag", "dep:fdcan", "fdcan/fdcan_h7" ] | 956 | stm32h742ag = [ "stm32-metapac/stm32h742ag" ] |
| 952 | stm32h742ai = [ "stm32-metapac/stm32h742ai", "dep:fdcan", "fdcan/fdcan_h7" ] | 957 | stm32h742ai = [ "stm32-metapac/stm32h742ai" ] |
| 953 | stm32h742bg = [ "stm32-metapac/stm32h742bg", "dep:fdcan", "fdcan/fdcan_h7" ] | 958 | stm32h742bg = [ "stm32-metapac/stm32h742bg" ] |
| 954 | stm32h742bi = [ "stm32-metapac/stm32h742bi", "dep:fdcan", "fdcan/fdcan_h7" ] | 959 | stm32h742bi = [ "stm32-metapac/stm32h742bi" ] |
| 955 | stm32h742ig = [ "stm32-metapac/stm32h742ig", "dep:fdcan", "fdcan/fdcan_h7" ] | 960 | stm32h742ig = [ "stm32-metapac/stm32h742ig" ] |
| 956 | stm32h742ii = [ "stm32-metapac/stm32h742ii", "dep:fdcan", "fdcan/fdcan_h7" ] | 961 | stm32h742ii = [ "stm32-metapac/stm32h742ii" ] |
| 957 | stm32h742vg = [ "stm32-metapac/stm32h742vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 962 | stm32h742vg = [ "stm32-metapac/stm32h742vg" ] |
| 958 | stm32h742vi = [ "stm32-metapac/stm32h742vi", "dep:fdcan", "fdcan/fdcan_h7" ] | 963 | stm32h742vi = [ "stm32-metapac/stm32h742vi" ] |
| 959 | stm32h742xg = [ "stm32-metapac/stm32h742xg", "dep:fdcan", "fdcan/fdcan_h7" ] | 964 | stm32h742xg = [ "stm32-metapac/stm32h742xg" ] |
| 960 | stm32h742xi = [ "stm32-metapac/stm32h742xi", "dep:fdcan", "fdcan/fdcan_h7" ] | 965 | stm32h742xi = [ "stm32-metapac/stm32h742xi" ] |
| 961 | stm32h742zg = [ "stm32-metapac/stm32h742zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 966 | stm32h742zg = [ "stm32-metapac/stm32h742zg" ] |
| 962 | stm32h742zi = [ "stm32-metapac/stm32h742zi", "dep:fdcan", "fdcan/fdcan_h7" ] | 967 | stm32h742zi = [ "stm32-metapac/stm32h742zi" ] |
| 963 | stm32h743ag = [ "stm32-metapac/stm32h743ag", "dep:fdcan", "fdcan/fdcan_h7" ] | 968 | stm32h743ag = [ "stm32-metapac/stm32h743ag" ] |
| 964 | stm32h743ai = [ "stm32-metapac/stm32h743ai", "dep:fdcan", "fdcan/fdcan_h7" ] | 969 | stm32h743ai = [ "stm32-metapac/stm32h743ai" ] |
| 965 | stm32h743bg = [ "stm32-metapac/stm32h743bg", "dep:fdcan", "fdcan/fdcan_h7" ] | 970 | stm32h743bg = [ "stm32-metapac/stm32h743bg" ] |
| 966 | stm32h743bi = [ "stm32-metapac/stm32h743bi", "dep:fdcan", "fdcan/fdcan_h7" ] | 971 | stm32h743bi = [ "stm32-metapac/stm32h743bi" ] |
| 967 | stm32h743ig = [ "stm32-metapac/stm32h743ig", "dep:fdcan", "fdcan/fdcan_h7" ] | 972 | stm32h743ig = [ "stm32-metapac/stm32h743ig" ] |
| 968 | stm32h743ii = [ "stm32-metapac/stm32h743ii", "dep:fdcan", "fdcan/fdcan_h7" ] | 973 | stm32h743ii = [ "stm32-metapac/stm32h743ii" ] |
| 969 | stm32h743vg = [ "stm32-metapac/stm32h743vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 974 | stm32h743vg = [ "stm32-metapac/stm32h743vg" ] |
| 970 | stm32h743vi = [ "stm32-metapac/stm32h743vi", "dep:fdcan", "fdcan/fdcan_h7" ] | 975 | stm32h743vi = [ "stm32-metapac/stm32h743vi" ] |
| 971 | stm32h743xg = [ "stm32-metapac/stm32h743xg", "dep:fdcan", "fdcan/fdcan_h7" ] | 976 | stm32h743xg = [ "stm32-metapac/stm32h743xg" ] |
| 972 | stm32h743xi = [ "stm32-metapac/stm32h743xi", "dep:fdcan", "fdcan/fdcan_h7" ] | 977 | stm32h743xi = [ "stm32-metapac/stm32h743xi" ] |
| 973 | stm32h743zg = [ "stm32-metapac/stm32h743zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 978 | stm32h743zg = [ "stm32-metapac/stm32h743zg" ] |
| 974 | stm32h743zi = [ "stm32-metapac/stm32h743zi", "dep:fdcan", "fdcan/fdcan_h7" ] | 979 | stm32h743zi = [ "stm32-metapac/stm32h743zi" ] |
| 975 | stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 980 | stm32h745bg-cm7 = [ "stm32-metapac/stm32h745bg-cm7" ] |
| 976 | stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 981 | stm32h745bg-cm4 = [ "stm32-metapac/stm32h745bg-cm4" ] |
| 977 | stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 982 | stm32h745bi-cm7 = [ "stm32-metapac/stm32h745bi-cm7" ] |
| 978 | stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 983 | stm32h745bi-cm4 = [ "stm32-metapac/stm32h745bi-cm4" ] |
| 979 | stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 984 | stm32h745ig-cm7 = [ "stm32-metapac/stm32h745ig-cm7" ] |
| 980 | stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 985 | stm32h745ig-cm4 = [ "stm32-metapac/stm32h745ig-cm4" ] |
| 981 | stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 986 | stm32h745ii-cm7 = [ "stm32-metapac/stm32h745ii-cm7" ] |
| 982 | stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 987 | stm32h745ii-cm4 = [ "stm32-metapac/stm32h745ii-cm4" ] |
| 983 | stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 988 | stm32h745xg-cm7 = [ "stm32-metapac/stm32h745xg-cm7" ] |
| 984 | stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 989 | stm32h745xg-cm4 = [ "stm32-metapac/stm32h745xg-cm4" ] |
| 985 | stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 990 | stm32h745xi-cm7 = [ "stm32-metapac/stm32h745xi-cm7" ] |
| 986 | stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 991 | stm32h745xi-cm4 = [ "stm32-metapac/stm32h745xi-cm4" ] |
| 987 | stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 992 | stm32h745zg-cm7 = [ "stm32-metapac/stm32h745zg-cm7" ] |
| 988 | stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 993 | stm32h745zg-cm4 = [ "stm32-metapac/stm32h745zg-cm4" ] |
| 989 | stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 994 | stm32h745zi-cm7 = [ "stm32-metapac/stm32h745zi-cm7" ] |
| 990 | stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 995 | stm32h745zi-cm4 = [ "stm32-metapac/stm32h745zi-cm4" ] |
| 991 | stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 996 | stm32h747ag-cm7 = [ "stm32-metapac/stm32h747ag-cm7" ] |
| 992 | stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 997 | stm32h747ag-cm4 = [ "stm32-metapac/stm32h747ag-cm4" ] |
| 993 | stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 998 | stm32h747ai-cm7 = [ "stm32-metapac/stm32h747ai-cm7" ] |
| 994 | stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 999 | stm32h747ai-cm4 = [ "stm32-metapac/stm32h747ai-cm4" ] |
| 995 | stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1000 | stm32h747bg-cm7 = [ "stm32-metapac/stm32h747bg-cm7" ] |
| 996 | stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1001 | stm32h747bg-cm4 = [ "stm32-metapac/stm32h747bg-cm4" ] |
| 997 | stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1002 | stm32h747bi-cm7 = [ "stm32-metapac/stm32h747bi-cm7" ] |
| 998 | stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1003 | stm32h747bi-cm4 = [ "stm32-metapac/stm32h747bi-cm4" ] |
| 999 | stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1004 | stm32h747ig-cm7 = [ "stm32-metapac/stm32h747ig-cm7" ] |
| 1000 | stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1005 | stm32h747ig-cm4 = [ "stm32-metapac/stm32h747ig-cm4" ] |
| 1001 | stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1006 | stm32h747ii-cm7 = [ "stm32-metapac/stm32h747ii-cm7" ] |
| 1002 | stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1007 | stm32h747ii-cm4 = [ "stm32-metapac/stm32h747ii-cm4" ] |
| 1003 | stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1008 | stm32h747xg-cm7 = [ "stm32-metapac/stm32h747xg-cm7" ] |
| 1004 | stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1009 | stm32h747xg-cm4 = [ "stm32-metapac/stm32h747xg-cm4" ] |
| 1005 | stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1010 | stm32h747xi-cm7 = [ "stm32-metapac/stm32h747xi-cm7" ] |
| 1006 | stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1011 | stm32h747xi-cm4 = [ "stm32-metapac/stm32h747xi-cm4" ] |
| 1007 | stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1012 | stm32h747zi-cm7 = [ "stm32-metapac/stm32h747zi-cm7" ] |
| 1008 | stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1013 | stm32h747zi-cm4 = [ "stm32-metapac/stm32h747zi-cm4" ] |
| 1009 | stm32h750ib = [ "stm32-metapac/stm32h750ib", "dep:fdcan", "fdcan/fdcan_h7" ] | 1014 | stm32h750ib = [ "stm32-metapac/stm32h750ib" ] |
| 1010 | stm32h750vb = [ "stm32-metapac/stm32h750vb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1015 | stm32h750vb = [ "stm32-metapac/stm32h750vb" ] |
| 1011 | stm32h750xb = [ "stm32-metapac/stm32h750xb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1016 | stm32h750xb = [ "stm32-metapac/stm32h750xb" ] |
| 1012 | stm32h750zb = [ "stm32-metapac/stm32h750zb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1017 | stm32h750zb = [ "stm32-metapac/stm32h750zb" ] |
| 1013 | stm32h753ai = [ "stm32-metapac/stm32h753ai", "dep:fdcan", "fdcan/fdcan_h7" ] | 1018 | stm32h753ai = [ "stm32-metapac/stm32h753ai" ] |
| 1014 | stm32h753bi = [ "stm32-metapac/stm32h753bi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1019 | stm32h753bi = [ "stm32-metapac/stm32h753bi" ] |
| 1015 | stm32h753ii = [ "stm32-metapac/stm32h753ii", "dep:fdcan", "fdcan/fdcan_h7" ] | 1020 | stm32h753ii = [ "stm32-metapac/stm32h753ii" ] |
| 1016 | stm32h753vi = [ "stm32-metapac/stm32h753vi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1021 | stm32h753vi = [ "stm32-metapac/stm32h753vi" ] |
| 1017 | stm32h753xi = [ "stm32-metapac/stm32h753xi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1022 | stm32h753xi = [ "stm32-metapac/stm32h753xi" ] |
| 1018 | stm32h753zi = [ "stm32-metapac/stm32h753zi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1023 | stm32h753zi = [ "stm32-metapac/stm32h753zi" ] |
| 1019 | stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1024 | stm32h755bi-cm7 = [ "stm32-metapac/stm32h755bi-cm7" ] |
| 1020 | stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1025 | stm32h755bi-cm4 = [ "stm32-metapac/stm32h755bi-cm4" ] |
| 1021 | stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1026 | stm32h755ii-cm7 = [ "stm32-metapac/stm32h755ii-cm7" ] |
| 1022 | stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1027 | stm32h755ii-cm4 = [ "stm32-metapac/stm32h755ii-cm4" ] |
| 1023 | stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1028 | stm32h755xi-cm7 = [ "stm32-metapac/stm32h755xi-cm7" ] |
| 1024 | stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1029 | stm32h755xi-cm4 = [ "stm32-metapac/stm32h755xi-cm4" ] |
| 1025 | stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1030 | stm32h755zi-cm7 = [ "stm32-metapac/stm32h755zi-cm7" ] |
| 1026 | stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1031 | stm32h755zi-cm4 = [ "stm32-metapac/stm32h755zi-cm4" ] |
| 1027 | stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1032 | stm32h757ai-cm7 = [ "stm32-metapac/stm32h757ai-cm7" ] |
| 1028 | stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1033 | stm32h757ai-cm4 = [ "stm32-metapac/stm32h757ai-cm4" ] |
| 1029 | stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1034 | stm32h757bi-cm7 = [ "stm32-metapac/stm32h757bi-cm7" ] |
| 1030 | stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1035 | stm32h757bi-cm4 = [ "stm32-metapac/stm32h757bi-cm4" ] |
| 1031 | stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1036 | stm32h757ii-cm7 = [ "stm32-metapac/stm32h757ii-cm7" ] |
| 1032 | stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1037 | stm32h757ii-cm4 = [ "stm32-metapac/stm32h757ii-cm4" ] |
| 1033 | stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1038 | stm32h757xi-cm7 = [ "stm32-metapac/stm32h757xi-cm7" ] |
| 1034 | stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1039 | stm32h757xi-cm4 = [ "stm32-metapac/stm32h757xi-cm4" ] |
| 1035 | stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7", "dep:fdcan", "fdcan/fdcan_h7" ] | 1040 | stm32h757zi-cm7 = [ "stm32-metapac/stm32h757zi-cm7" ] |
| 1036 | stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4", "dep:fdcan", "fdcan/fdcan_h7" ] | 1041 | stm32h757zi-cm4 = [ "stm32-metapac/stm32h757zi-cm4" ] |
| 1037 | stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag", "dep:fdcan", "fdcan/fdcan_h7" ] | 1042 | stm32h7a3ag = [ "stm32-metapac/stm32h7a3ag" ] |
| 1038 | stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai", "dep:fdcan", "fdcan/fdcan_h7" ] | 1043 | stm32h7a3ai = [ "stm32-metapac/stm32h7a3ai" ] |
| 1039 | stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig", "dep:fdcan", "fdcan/fdcan_h7" ] | 1044 | stm32h7a3ig = [ "stm32-metapac/stm32h7a3ig" ] |
| 1040 | stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii", "dep:fdcan", "fdcan/fdcan_h7" ] | 1045 | stm32h7a3ii = [ "stm32-metapac/stm32h7a3ii" ] |
| 1041 | stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg", "dep:fdcan", "fdcan/fdcan_h7" ] | 1046 | stm32h7a3lg = [ "stm32-metapac/stm32h7a3lg" ] |
| 1042 | stm32h7a3li = [ "stm32-metapac/stm32h7a3li", "dep:fdcan", "fdcan/fdcan_h7" ] | 1047 | stm32h7a3li = [ "stm32-metapac/stm32h7a3li" ] |
| 1043 | stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng", "dep:fdcan", "fdcan/fdcan_h7" ] | 1048 | stm32h7a3ng = [ "stm32-metapac/stm32h7a3ng" ] |
| 1044 | stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni", "dep:fdcan", "fdcan/fdcan_h7" ] | 1049 | stm32h7a3ni = [ "stm32-metapac/stm32h7a3ni" ] |
| 1045 | stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1050 | stm32h7a3qi = [ "stm32-metapac/stm32h7a3qi" ] |
| 1046 | stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg", "dep:fdcan", "fdcan/fdcan_h7" ] | 1051 | stm32h7a3rg = [ "stm32-metapac/stm32h7a3rg" ] |
| 1047 | stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri", "dep:fdcan", "fdcan/fdcan_h7" ] | 1052 | stm32h7a3ri = [ "stm32-metapac/stm32h7a3ri" ] |
| 1048 | stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg", "dep:fdcan", "fdcan/fdcan_h7" ] | 1053 | stm32h7a3vg = [ "stm32-metapac/stm32h7a3vg" ] |
| 1049 | stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1054 | stm32h7a3vi = [ "stm32-metapac/stm32h7a3vi" ] |
| 1050 | stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg", "dep:fdcan", "fdcan/fdcan_h7" ] | 1055 | stm32h7a3zg = [ "stm32-metapac/stm32h7a3zg" ] |
| 1051 | stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1056 | stm32h7a3zi = [ "stm32-metapac/stm32h7a3zi" ] |
| 1052 | stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab", "dep:fdcan", "fdcan/fdcan_h7" ] | 1057 | stm32h7b0ab = [ "stm32-metapac/stm32h7b0ab" ] |
| 1053 | stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib", "dep:fdcan", "fdcan/fdcan_h7" ] | 1058 | stm32h7b0ib = [ "stm32-metapac/stm32h7b0ib" ] |
| 1054 | stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1059 | stm32h7b0rb = [ "stm32-metapac/stm32h7b0rb" ] |
| 1055 | stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1060 | stm32h7b0vb = [ "stm32-metapac/stm32h7b0vb" ] |
| 1056 | stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb", "dep:fdcan", "fdcan/fdcan_h7" ] | 1061 | stm32h7b0zb = [ "stm32-metapac/stm32h7b0zb" ] |
| 1057 | stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai", "dep:fdcan", "fdcan/fdcan_h7" ] | 1062 | stm32h7b3ai = [ "stm32-metapac/stm32h7b3ai" ] |
| 1058 | stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii", "dep:fdcan", "fdcan/fdcan_h7" ] | 1063 | stm32h7b3ii = [ "stm32-metapac/stm32h7b3ii" ] |
| 1059 | stm32h7b3li = [ "stm32-metapac/stm32h7b3li", "dep:fdcan", "fdcan/fdcan_h7" ] | 1064 | stm32h7b3li = [ "stm32-metapac/stm32h7b3li" ] |
| 1060 | stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni", "dep:fdcan", "fdcan/fdcan_h7" ] | 1065 | stm32h7b3ni = [ "stm32-metapac/stm32h7b3ni" ] |
| 1061 | stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1066 | stm32h7b3qi = [ "stm32-metapac/stm32h7b3qi" ] |
| 1062 | stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri", "dep:fdcan", "fdcan/fdcan_h7" ] | 1067 | stm32h7b3ri = [ "stm32-metapac/stm32h7b3ri" ] |
| 1063 | stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1068 | stm32h7b3vi = [ "stm32-metapac/stm32h7b3vi" ] |
| 1064 | stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi", "dep:fdcan", "fdcan/fdcan_h7" ] | 1069 | stm32h7b3zi = [ "stm32-metapac/stm32h7b3zi" ] |
| 1065 | stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] | 1070 | stm32l010c6 = [ "stm32-metapac/stm32l010c6" ] |
| 1066 | stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] | 1071 | stm32l010f4 = [ "stm32-metapac/stm32l010f4" ] |
| 1067 | stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] | 1072 | stm32l010k4 = [ "stm32-metapac/stm32l010k4" ] |
| @@ -1388,86 +1393,86 @@ stm32l4s7zi = [ "stm32-metapac/stm32l4s7zi" ] | |||
| 1388 | stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] | 1393 | stm32l4s9ai = [ "stm32-metapac/stm32l4s9ai" ] |
| 1389 | stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] | 1394 | stm32l4s9vi = [ "stm32-metapac/stm32l4s9vi" ] |
| 1390 | stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] | 1395 | stm32l4s9zi = [ "stm32-metapac/stm32l4s9zi" ] |
| 1391 | stm32l552cc = [ "stm32-metapac/stm32l552cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1396 | stm32l552cc = [ "stm32-metapac/stm32l552cc" ] |
| 1392 | stm32l552ce = [ "stm32-metapac/stm32l552ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1397 | stm32l552ce = [ "stm32-metapac/stm32l552ce" ] |
| 1393 | stm32l552me = [ "stm32-metapac/stm32l552me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1398 | stm32l552me = [ "stm32-metapac/stm32l552me" ] |
| 1394 | stm32l552qc = [ "stm32-metapac/stm32l552qc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1399 | stm32l552qc = [ "stm32-metapac/stm32l552qc" ] |
| 1395 | stm32l552qe = [ "stm32-metapac/stm32l552qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1400 | stm32l552qe = [ "stm32-metapac/stm32l552qe" ] |
| 1396 | stm32l552rc = [ "stm32-metapac/stm32l552rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1401 | stm32l552rc = [ "stm32-metapac/stm32l552rc" ] |
| 1397 | stm32l552re = [ "stm32-metapac/stm32l552re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1402 | stm32l552re = [ "stm32-metapac/stm32l552re" ] |
| 1398 | stm32l552vc = [ "stm32-metapac/stm32l552vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1403 | stm32l552vc = [ "stm32-metapac/stm32l552vc" ] |
| 1399 | stm32l552ve = [ "stm32-metapac/stm32l552ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1404 | stm32l552ve = [ "stm32-metapac/stm32l552ve" ] |
| 1400 | stm32l552zc = [ "stm32-metapac/stm32l552zc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1405 | stm32l552zc = [ "stm32-metapac/stm32l552zc" ] |
| 1401 | stm32l552ze = [ "stm32-metapac/stm32l552ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1406 | stm32l552ze = [ "stm32-metapac/stm32l552ze" ] |
| 1402 | stm32l562ce = [ "stm32-metapac/stm32l562ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1407 | stm32l562ce = [ "stm32-metapac/stm32l562ce" ] |
| 1403 | stm32l562me = [ "stm32-metapac/stm32l562me", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1408 | stm32l562me = [ "stm32-metapac/stm32l562me" ] |
| 1404 | stm32l562qe = [ "stm32-metapac/stm32l562qe", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1409 | stm32l562qe = [ "stm32-metapac/stm32l562qe" ] |
| 1405 | stm32l562re = [ "stm32-metapac/stm32l562re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1410 | stm32l562re = [ "stm32-metapac/stm32l562re" ] |
| 1406 | stm32l562ve = [ "stm32-metapac/stm32l562ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1411 | stm32l562ve = [ "stm32-metapac/stm32l562ve" ] |
| 1407 | stm32l562ze = [ "stm32-metapac/stm32l562ze", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1412 | stm32l562ze = [ "stm32-metapac/stm32l562ze" ] |
| 1408 | stm32u535cb = [ "stm32-metapac/stm32u535cb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1413 | stm32u535cb = [ "stm32-metapac/stm32u535cb" ] |
| 1409 | stm32u535cc = [ "stm32-metapac/stm32u535cc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1414 | stm32u535cc = [ "stm32-metapac/stm32u535cc" ] |
| 1410 | stm32u535ce = [ "stm32-metapac/stm32u535ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1415 | stm32u535ce = [ "stm32-metapac/stm32u535ce" ] |
| 1411 | stm32u535je = [ "stm32-metapac/stm32u535je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1416 | stm32u535je = [ "stm32-metapac/stm32u535je" ] |
| 1412 | stm32u535nc = [ "stm32-metapac/stm32u535nc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1417 | stm32u535nc = [ "stm32-metapac/stm32u535nc" ] |
| 1413 | stm32u535ne = [ "stm32-metapac/stm32u535ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1418 | stm32u535ne = [ "stm32-metapac/stm32u535ne" ] |
| 1414 | stm32u535rb = [ "stm32-metapac/stm32u535rb", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1419 | stm32u535rb = [ "stm32-metapac/stm32u535rb" ] |
| 1415 | stm32u535rc = [ "stm32-metapac/stm32u535rc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1420 | stm32u535rc = [ "stm32-metapac/stm32u535rc" ] |
| 1416 | stm32u535re = [ "stm32-metapac/stm32u535re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1421 | stm32u535re = [ "stm32-metapac/stm32u535re" ] |
| 1417 | stm32u535vc = [ "stm32-metapac/stm32u535vc", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1422 | stm32u535vc = [ "stm32-metapac/stm32u535vc" ] |
| 1418 | stm32u535ve = [ "stm32-metapac/stm32u535ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1423 | stm32u535ve = [ "stm32-metapac/stm32u535ve" ] |
| 1419 | stm32u545ce = [ "stm32-metapac/stm32u545ce", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1424 | stm32u545ce = [ "stm32-metapac/stm32u545ce" ] |
| 1420 | stm32u545je = [ "stm32-metapac/stm32u545je", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1425 | stm32u545je = [ "stm32-metapac/stm32u545je" ] |
| 1421 | stm32u545ne = [ "stm32-metapac/stm32u545ne", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1426 | stm32u545ne = [ "stm32-metapac/stm32u545ne" ] |
| 1422 | stm32u545re = [ "stm32-metapac/stm32u545re", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1427 | stm32u545re = [ "stm32-metapac/stm32u545re" ] |
| 1423 | stm32u545ve = [ "stm32-metapac/stm32u545ve", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1428 | stm32u545ve = [ "stm32-metapac/stm32u545ve" ] |
| 1424 | stm32u575ag = [ "stm32-metapac/stm32u575ag", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1429 | stm32u575ag = [ "stm32-metapac/stm32u575ag" ] |
| 1425 | stm32u575ai = [ "stm32-metapac/stm32u575ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1430 | stm32u575ai = [ "stm32-metapac/stm32u575ai" ] |
| 1426 | stm32u575cg = [ "stm32-metapac/stm32u575cg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1431 | stm32u575cg = [ "stm32-metapac/stm32u575cg" ] |
| 1427 | stm32u575ci = [ "stm32-metapac/stm32u575ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1432 | stm32u575ci = [ "stm32-metapac/stm32u575ci" ] |
| 1428 | stm32u575og = [ "stm32-metapac/stm32u575og", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1433 | stm32u575og = [ "stm32-metapac/stm32u575og" ] |
| 1429 | stm32u575oi = [ "stm32-metapac/stm32u575oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1434 | stm32u575oi = [ "stm32-metapac/stm32u575oi" ] |
| 1430 | stm32u575qg = [ "stm32-metapac/stm32u575qg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1435 | stm32u575qg = [ "stm32-metapac/stm32u575qg" ] |
| 1431 | stm32u575qi = [ "stm32-metapac/stm32u575qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1436 | stm32u575qi = [ "stm32-metapac/stm32u575qi" ] |
| 1432 | stm32u575rg = [ "stm32-metapac/stm32u575rg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1437 | stm32u575rg = [ "stm32-metapac/stm32u575rg" ] |
| 1433 | stm32u575ri = [ "stm32-metapac/stm32u575ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1438 | stm32u575ri = [ "stm32-metapac/stm32u575ri" ] |
| 1434 | stm32u575vg = [ "stm32-metapac/stm32u575vg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1439 | stm32u575vg = [ "stm32-metapac/stm32u575vg" ] |
| 1435 | stm32u575vi = [ "stm32-metapac/stm32u575vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1440 | stm32u575vi = [ "stm32-metapac/stm32u575vi" ] |
| 1436 | stm32u575zg = [ "stm32-metapac/stm32u575zg", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1441 | stm32u575zg = [ "stm32-metapac/stm32u575zg" ] |
| 1437 | stm32u575zi = [ "stm32-metapac/stm32u575zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1442 | stm32u575zi = [ "stm32-metapac/stm32u575zi" ] |
| 1438 | stm32u585ai = [ "stm32-metapac/stm32u585ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1443 | stm32u585ai = [ "stm32-metapac/stm32u585ai" ] |
| 1439 | stm32u585ci = [ "stm32-metapac/stm32u585ci", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1444 | stm32u585ci = [ "stm32-metapac/stm32u585ci" ] |
| 1440 | stm32u585oi = [ "stm32-metapac/stm32u585oi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1445 | stm32u585oi = [ "stm32-metapac/stm32u585oi" ] |
| 1441 | stm32u585qi = [ "stm32-metapac/stm32u585qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1446 | stm32u585qi = [ "stm32-metapac/stm32u585qi" ] |
| 1442 | stm32u585ri = [ "stm32-metapac/stm32u585ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1447 | stm32u585ri = [ "stm32-metapac/stm32u585ri" ] |
| 1443 | stm32u585vi = [ "stm32-metapac/stm32u585vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1448 | stm32u585vi = [ "stm32-metapac/stm32u585vi" ] |
| 1444 | stm32u585zi = [ "stm32-metapac/stm32u585zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1449 | stm32u585zi = [ "stm32-metapac/stm32u585zi" ] |
| 1445 | stm32u595ai = [ "stm32-metapac/stm32u595ai", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1450 | stm32u595ai = [ "stm32-metapac/stm32u595ai" ] |
| 1446 | stm32u595aj = [ "stm32-metapac/stm32u595aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1451 | stm32u595aj = [ "stm32-metapac/stm32u595aj" ] |
| 1447 | stm32u595qi = [ "stm32-metapac/stm32u595qi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1452 | stm32u595qi = [ "stm32-metapac/stm32u595qi" ] |
| 1448 | stm32u595qj = [ "stm32-metapac/stm32u595qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1453 | stm32u595qj = [ "stm32-metapac/stm32u595qj" ] |
| 1449 | stm32u595ri = [ "stm32-metapac/stm32u595ri", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1454 | stm32u595ri = [ "stm32-metapac/stm32u595ri" ] |
| 1450 | stm32u595rj = [ "stm32-metapac/stm32u595rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1455 | stm32u595rj = [ "stm32-metapac/stm32u595rj" ] |
| 1451 | stm32u595vi = [ "stm32-metapac/stm32u595vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1456 | stm32u595vi = [ "stm32-metapac/stm32u595vi" ] |
| 1452 | stm32u595vj = [ "stm32-metapac/stm32u595vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1457 | stm32u595vj = [ "stm32-metapac/stm32u595vj" ] |
| 1453 | stm32u595zi = [ "stm32-metapac/stm32u595zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1458 | stm32u595zi = [ "stm32-metapac/stm32u595zi" ] |
| 1454 | stm32u595zj = [ "stm32-metapac/stm32u595zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1459 | stm32u595zj = [ "stm32-metapac/stm32u595zj" ] |
| 1455 | stm32u599bj = [ "stm32-metapac/stm32u599bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1460 | stm32u599bj = [ "stm32-metapac/stm32u599bj" ] |
| 1456 | stm32u599ni = [ "stm32-metapac/stm32u599ni", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1461 | stm32u599ni = [ "stm32-metapac/stm32u599ni" ] |
| 1457 | stm32u599nj = [ "stm32-metapac/stm32u599nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1462 | stm32u599nj = [ "stm32-metapac/stm32u599nj" ] |
| 1458 | stm32u599vi = [ "stm32-metapac/stm32u599vi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1463 | stm32u599vi = [ "stm32-metapac/stm32u599vi" ] |
| 1459 | stm32u599vj = [ "stm32-metapac/stm32u599vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1464 | stm32u599vj = [ "stm32-metapac/stm32u599vj" ] |
| 1460 | stm32u599zi = [ "stm32-metapac/stm32u599zi", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1465 | stm32u599zi = [ "stm32-metapac/stm32u599zi" ] |
| 1461 | stm32u599zj = [ "stm32-metapac/stm32u599zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1466 | stm32u599zj = [ "stm32-metapac/stm32u599zj" ] |
| 1462 | stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1467 | stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ] |
| 1463 | stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1468 | stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ] |
| 1464 | stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1469 | stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ] |
| 1465 | stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1470 | stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ] |
| 1466 | stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1471 | stm32u5a5zj = [ "stm32-metapac/stm32u5a5zj" ] |
| 1467 | stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1472 | stm32u5a9bj = [ "stm32-metapac/stm32u5a9bj" ] |
| 1468 | stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1473 | stm32u5a9nj = [ "stm32-metapac/stm32u5a9nj" ] |
| 1469 | stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1474 | stm32u5a9vj = [ "stm32-metapac/stm32u5a9vj" ] |
| 1470 | stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj", "dep:fdcan", "fdcan/fdcan_g0_g4_l5" ] | 1475 | stm32u5a9zj = [ "stm32-metapac/stm32u5a9zj" ] |
| 1471 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] | 1476 | stm32wb10cc = [ "stm32-metapac/stm32wb10cc" ] |
| 1472 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] | 1477 | stm32wb15cc = [ "stm32-metapac/stm32wb15cc" ] |
| 1473 | stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] | 1478 | stm32wb30ce = [ "stm32-metapac/stm32wb30ce" ] |
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs new file mode 100644 index 000000000..38b409121 --- /dev/null +++ b/embassy-stm32/src/can/fd/config.rs | |||
| @@ -0,0 +1,438 @@ | |||
| 1 | //! Configuration for FDCAN Module | ||
| 2 | //! Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 3 | |||
| 4 | use core::num::{NonZeroU16, NonZeroU8}; | ||
| 5 | |||
| 6 | /// Configures the bit timings. | ||
| 7 | /// | ||
| 8 | /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter | ||
| 9 | /// parameters as follows: | ||
| 10 | /// | ||
| 11 | /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). | ||
| 12 | /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). | ||
| 13 | /// - *Sample Point*: Should normally be left at the default value of 87.5%. | ||
| 14 | /// - *SJW*: Should normally be left at the default value of 1. | ||
| 15 | /// | ||
| 16 | /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` | ||
| 17 | /// parameter to this method. | ||
| 18 | #[derive(Clone, Copy, Debug)] | ||
| 19 | pub struct NominalBitTiming { | ||
| 20 | /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit | ||
| 21 | /// time is built up from a multiple of this quanta. Valid values are 1 to 512. | ||
| 22 | pub prescaler: NonZeroU16, | ||
| 23 | /// Valid values are 1 to 128. | ||
| 24 | pub seg1: NonZeroU8, | ||
| 25 | /// Valid values are 1 to 255. | ||
| 26 | pub seg2: NonZeroU8, | ||
| 27 | /// Valid values are 1 to 128. | ||
| 28 | pub sync_jump_width: NonZeroU8, | ||
| 29 | } | ||
| 30 | impl NominalBitTiming { | ||
| 31 | #[inline] | ||
| 32 | pub(crate) fn nbrp(&self) -> u16 { | ||
| 33 | u16::from(self.prescaler) & 0x1FF | ||
| 34 | } | ||
| 35 | #[inline] | ||
| 36 | pub(crate) fn ntseg1(&self) -> u8 { | ||
| 37 | u8::from(self.seg1) | ||
| 38 | } | ||
| 39 | #[inline] | ||
| 40 | pub(crate) fn ntseg2(&self) -> u8 { | ||
| 41 | u8::from(self.seg2) & 0x7F | ||
| 42 | } | ||
| 43 | #[inline] | ||
| 44 | pub(crate) fn nsjw(&self) -> u8 { | ||
| 45 | u8::from(self.sync_jump_width) & 0x7F | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | impl Default for NominalBitTiming { | ||
| 50 | #[inline] | ||
| 51 | fn default() -> Self { | ||
| 52 | // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP | ||
| 53 | // register value of 0x0600_0A03 | ||
| 54 | Self { | ||
| 55 | prescaler: NonZeroU16::new(1).unwrap(), | ||
| 56 | seg1: NonZeroU8::new(11).unwrap(), | ||
| 57 | seg2: NonZeroU8::new(4).unwrap(), | ||
| 58 | sync_jump_width: NonZeroU8::new(4).unwrap(), | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Configures the data bit timings for the FdCan Variable Bitrates. | ||
| 64 | /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. | ||
| 65 | #[derive(Clone, Copy, Debug)] | ||
| 66 | pub struct DataBitTiming { | ||
| 67 | /// Tranceiver Delay Compensation | ||
| 68 | pub transceiver_delay_compensation: bool, | ||
| 69 | /// The value by which the oscillator frequency is divided to generate the bit time quanta. The bit | ||
| 70 | /// time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1 | ||
| 71 | /// to 31. | ||
| 72 | pub prescaler: NonZeroU16, | ||
| 73 | /// Valid values are 1 to 31. | ||
| 74 | pub seg1: NonZeroU8, | ||
| 75 | /// Valid values are 1 to 15. | ||
| 76 | pub seg2: NonZeroU8, | ||
| 77 | /// Must always be smaller than DTSEG2, valid values are 1 to 15. | ||
| 78 | pub sync_jump_width: NonZeroU8, | ||
| 79 | } | ||
| 80 | impl DataBitTiming { | ||
| 81 | // #[inline] | ||
| 82 | // fn tdc(&self) -> u8 { | ||
| 83 | // let tsd = self.transceiver_delay_compensation as u8; | ||
| 84 | // //TODO: stm32g4 does not export the TDC field | ||
| 85 | // todo!() | ||
| 86 | // } | ||
| 87 | #[inline] | ||
| 88 | pub(crate) fn dbrp(&self) -> u8 { | ||
| 89 | (u16::from(self.prescaler) & 0x001F) as u8 | ||
| 90 | } | ||
| 91 | #[inline] | ||
| 92 | pub(crate) fn dtseg1(&self) -> u8 { | ||
| 93 | u8::from(self.seg1) & 0x1F | ||
| 94 | } | ||
| 95 | #[inline] | ||
| 96 | pub(crate) fn dtseg2(&self) -> u8 { | ||
| 97 | u8::from(self.seg2) & 0x0F | ||
| 98 | } | ||
| 99 | #[inline] | ||
| 100 | pub(crate) fn dsjw(&self) -> u8 { | ||
| 101 | u8::from(self.sync_jump_width) & 0x0F | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | impl Default for DataBitTiming { | ||
| 106 | #[inline] | ||
| 107 | fn default() -> Self { | ||
| 108 | // Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP | ||
| 109 | // register value of 0x0000_0A33 | ||
| 110 | Self { | ||
| 111 | transceiver_delay_compensation: false, | ||
| 112 | prescaler: NonZeroU16::new(1).unwrap(), | ||
| 113 | seg1: NonZeroU8::new(11).unwrap(), | ||
| 114 | seg2: NonZeroU8::new(4).unwrap(), | ||
| 115 | sync_jump_width: NonZeroU8::new(4).unwrap(), | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Configures which modes to use | ||
| 121 | /// Individual headers can contain a desire to be send via FdCan | ||
| 122 | /// or use Bit rate switching. But if this general setting does not allow | ||
| 123 | /// that, only classic CAN is used instead. | ||
| 124 | #[derive(Clone, Copy, Debug)] | ||
| 125 | pub enum FrameTransmissionConfig { | ||
| 126 | /// Only allow Classic CAN message Frames | ||
| 127 | ClassicCanOnly, | ||
| 128 | /// Allow (non-brs) FdCAN Message Frames | ||
| 129 | AllowFdCan, | ||
| 130 | /// Allow FdCAN Message Frames and allow Bit Rate Switching | ||
| 131 | AllowFdCanAndBRS, | ||
| 132 | } | ||
| 133 | |||
| 134 | /// | ||
| 135 | #[derive(Clone, Copy, Debug)] | ||
| 136 | pub enum ClockDivider { | ||
| 137 | /// Divide by 1 | ||
| 138 | _1 = 0b0000, | ||
| 139 | /// Divide by 2 | ||
| 140 | _2 = 0b0001, | ||
| 141 | /// Divide by 4 | ||
| 142 | _4 = 0b0010, | ||
| 143 | /// Divide by 6 | ||
| 144 | _6 = 0b0011, | ||
| 145 | /// Divide by 8 | ||
| 146 | _8 = 0b0100, | ||
| 147 | /// Divide by 10 | ||
| 148 | _10 = 0b0101, | ||
| 149 | /// Divide by 12 | ||
| 150 | _12 = 0b0110, | ||
| 151 | /// Divide by 14 | ||
| 152 | _14 = 0b0111, | ||
| 153 | /// Divide by 16 | ||
| 154 | _16 = 0b1000, | ||
| 155 | /// Divide by 18 | ||
| 156 | _18 = 0b1001, | ||
| 157 | /// Divide by 20 | ||
| 158 | _20 = 0b1010, | ||
| 159 | /// Divide by 22 | ||
| 160 | _22 = 0b1011, | ||
| 161 | /// Divide by 24 | ||
| 162 | _24 = 0b1100, | ||
| 163 | /// Divide by 26 | ||
| 164 | _26 = 0b1101, | ||
| 165 | /// Divide by 28 | ||
| 166 | _28 = 0b1110, | ||
| 167 | /// Divide by 30 | ||
| 168 | _30 = 0b1111, | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Prescaler of the Timestamp counter | ||
| 172 | #[derive(Clone, Copy, Debug)] | ||
| 173 | pub enum TimestampPrescaler { | ||
| 174 | /// 1 | ||
| 175 | _1 = 1, | ||
| 176 | /// 2 | ||
| 177 | _2 = 2, | ||
| 178 | /// 3 | ||
| 179 | _3 = 3, | ||
| 180 | /// 4 | ||
| 181 | _4 = 4, | ||
| 182 | /// 5 | ||
| 183 | _5 = 5, | ||
| 184 | /// 6 | ||
| 185 | _6 = 6, | ||
| 186 | /// 7 | ||
| 187 | _7 = 7, | ||
| 188 | /// 8 | ||
| 189 | _8 = 8, | ||
| 190 | /// 9 | ||
| 191 | _9 = 9, | ||
| 192 | /// 10 | ||
| 193 | _10 = 10, | ||
| 194 | /// 11 | ||
| 195 | _11 = 11, | ||
| 196 | /// 12 | ||
| 197 | _12 = 12, | ||
| 198 | /// 13 | ||
| 199 | _13 = 13, | ||
| 200 | /// 14 | ||
| 201 | _14 = 14, | ||
| 202 | /// 15 | ||
| 203 | _15 = 15, | ||
| 204 | /// 16 | ||
| 205 | _16 = 16, | ||
| 206 | } | ||
| 207 | |||
| 208 | /// Selects the source of the Timestamp counter | ||
| 209 | #[derive(Clone, Copy, Debug)] | ||
| 210 | pub enum TimestampSource { | ||
| 211 | /// The Timestamp counter is disabled | ||
| 212 | None, | ||
| 213 | /// Using the FdCan input clock as the Timstamp counter's source, | ||
| 214 | /// and using a specific prescaler | ||
| 215 | Prescaler(TimestampPrescaler), | ||
| 216 | /// Using TIM3 as a source | ||
| 217 | FromTIM3, | ||
| 218 | } | ||
| 219 | |||
| 220 | /// How to handle frames in the global filter | ||
| 221 | #[derive(Clone, Copy, Debug)] | ||
| 222 | pub enum NonMatchingFilter { | ||
| 223 | /// Frames will go to Fifo0 when they do no match any specific filter | ||
| 224 | IntoRxFifo0 = 0b00, | ||
| 225 | /// Frames will go to Fifo1 when they do no match any specific filter | ||
| 226 | IntoRxFifo1 = 0b01, | ||
| 227 | /// Frames will be rejected when they do not match any specific filter | ||
| 228 | Reject = 0b11, | ||
| 229 | } | ||
| 230 | |||
| 231 | /// How to handle frames which do not match a specific filter | ||
| 232 | #[derive(Clone, Copy, Debug)] | ||
| 233 | pub struct GlobalFilter { | ||
| 234 | /// How to handle non-matching standard frames | ||
| 235 | pub handle_standard_frames: NonMatchingFilter, | ||
| 236 | |||
| 237 | /// How to handle non-matching extended frames | ||
| 238 | pub handle_extended_frames: NonMatchingFilter, | ||
| 239 | |||
| 240 | /// How to handle remote standard frames | ||
| 241 | pub reject_remote_standard_frames: bool, | ||
| 242 | |||
| 243 | /// How to handle remote extended frames | ||
| 244 | pub reject_remote_extended_frames: bool, | ||
| 245 | } | ||
| 246 | impl GlobalFilter { | ||
| 247 | /// Reject all non-matching and remote frames | ||
| 248 | pub const fn reject_all() -> Self { | ||
| 249 | Self { | ||
| 250 | handle_standard_frames: NonMatchingFilter::Reject, | ||
| 251 | handle_extended_frames: NonMatchingFilter::Reject, | ||
| 252 | reject_remote_standard_frames: true, | ||
| 253 | reject_remote_extended_frames: true, | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | /// How to handle non-matching standard frames | ||
| 258 | pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self { | ||
| 259 | self.handle_standard_frames = filter; | ||
| 260 | self | ||
| 261 | } | ||
| 262 | /// How to handle non-matching exteded frames | ||
| 263 | pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self { | ||
| 264 | self.handle_extended_frames = filter; | ||
| 265 | self | ||
| 266 | } | ||
| 267 | /// How to handle remote standard frames | ||
| 268 | pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self { | ||
| 269 | self.reject_remote_standard_frames = filter; | ||
| 270 | self | ||
| 271 | } | ||
| 272 | /// How to handle remote extended frames | ||
| 273 | pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self { | ||
| 274 | self.reject_remote_extended_frames = filter; | ||
| 275 | self | ||
| 276 | } | ||
| 277 | } | ||
| 278 | impl Default for GlobalFilter { | ||
| 279 | #[inline] | ||
| 280 | fn default() -> Self { | ||
| 281 | Self { | ||
| 282 | handle_standard_frames: NonMatchingFilter::IntoRxFifo0, | ||
| 283 | handle_extended_frames: NonMatchingFilter::IntoRxFifo0, | ||
| 284 | reject_remote_standard_frames: false, | ||
| 285 | reject_remote_extended_frames: false, | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /// FdCan Config Struct | ||
| 291 | #[derive(Clone, Copy, Debug)] | ||
| 292 | pub struct FdCanConfig { | ||
| 293 | /// Nominal Bit Timings | ||
| 294 | pub nbtr: NominalBitTiming, | ||
| 295 | /// (Variable) Data Bit Timings | ||
| 296 | pub dbtr: DataBitTiming, | ||
| 297 | /// Enables or disables automatic retransmission of messages | ||
| 298 | /// | ||
| 299 | /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame | ||
| 300 | /// util it can be sent. Otherwise, it will try only once to send each frame. | ||
| 301 | /// | ||
| 302 | /// Automatic retransmission is enabled by default. | ||
| 303 | pub automatic_retransmit: bool, | ||
| 304 | /// Enabled or disables the pausing between transmissions | ||
| 305 | /// | ||
| 306 | /// This feature looses up burst transmissions coming from a single node and it protects against | ||
| 307 | /// "babbling idiot" scenarios where the application program erroneously requests too many | ||
| 308 | /// transmissions. | ||
| 309 | pub transmit_pause: bool, | ||
| 310 | /// Enabled or disables the pausing between transmissions | ||
| 311 | /// | ||
| 312 | /// This feature looses up burst transmissions coming from a single node and it protects against | ||
| 313 | /// "babbling idiot" scenarios where the application program erroneously requests too many | ||
| 314 | /// transmissions. | ||
| 315 | pub frame_transmit: FrameTransmissionConfig, | ||
| 316 | /// Non Isoe Mode | ||
| 317 | /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN | ||
| 318 | /// FD Specification V1.0. | ||
| 319 | pub non_iso_mode: bool, | ||
| 320 | /// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization | ||
| 321 | pub edge_filtering: bool, | ||
| 322 | /// Enables protocol exception handling | ||
| 323 | pub protocol_exception_handling: bool, | ||
| 324 | /// Sets the general clock divider for this FdCAN instance | ||
| 325 | pub clock_divider: ClockDivider, | ||
| 326 | /// Sets the timestamp source | ||
| 327 | pub timestamp_source: TimestampSource, | ||
| 328 | /// Configures the Global Filter | ||
| 329 | pub global_filter: GlobalFilter, | ||
| 330 | } | ||
| 331 | |||
| 332 | impl FdCanConfig { | ||
| 333 | /// Configures the bit timings. | ||
| 334 | #[inline] | ||
| 335 | pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self { | ||
| 336 | self.nbtr = btr; | ||
| 337 | self | ||
| 338 | } | ||
| 339 | |||
| 340 | /// Configures the bit timings. | ||
| 341 | #[inline] | ||
| 342 | pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self { | ||
| 343 | self.dbtr = btr; | ||
| 344 | self | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Enables or disables automatic retransmission of messages | ||
| 348 | /// | ||
| 349 | /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame | ||
| 350 | /// util it can be sent. Otherwise, it will try only once to send each frame. | ||
| 351 | /// | ||
| 352 | /// Automatic retransmission is enabled by default. | ||
| 353 | #[inline] | ||
| 354 | pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self { | ||
| 355 | self.automatic_retransmit = enabled; | ||
| 356 | self | ||
| 357 | } | ||
| 358 | |||
| 359 | /// Enabled or disables the pausing between transmissions | ||
| 360 | /// | ||
| 361 | /// This feature looses up burst transmissions coming from a single node and it protects against | ||
| 362 | /// "babbling idiot" scenarios where the application program erroneously requests too many | ||
| 363 | /// transmissions. | ||
| 364 | #[inline] | ||
| 365 | pub const fn set_transmit_pause(mut self, enabled: bool) -> Self { | ||
| 366 | self.transmit_pause = enabled; | ||
| 367 | self | ||
| 368 | } | ||
| 369 | |||
| 370 | /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN | ||
| 371 | /// FD Specification V1.0. | ||
| 372 | #[inline] | ||
| 373 | pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self { | ||
| 374 | self.non_iso_mode = enabled; | ||
| 375 | self | ||
| 376 | } | ||
| 377 | |||
| 378 | /// Two consecutive dominant tq required to detect an edge for hard synchronization | ||
| 379 | #[inline] | ||
| 380 | pub const fn set_edge_filtering(mut self, enabled: bool) -> Self { | ||
| 381 | self.edge_filtering = enabled; | ||
| 382 | self | ||
| 383 | } | ||
| 384 | |||
| 385 | /// Sets the allowed transmission types for messages. | ||
| 386 | #[inline] | ||
| 387 | pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self { | ||
| 388 | self.frame_transmit = fts; | ||
| 389 | self | ||
| 390 | } | ||
| 391 | |||
| 392 | /// Enables protocol exception handling | ||
| 393 | #[inline] | ||
| 394 | pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self { | ||
| 395 | self.protocol_exception_handling = peh; | ||
| 396 | self | ||
| 397 | } | ||
| 398 | |||
| 399 | /// Sets the general clock divider for this FdCAN instance | ||
| 400 | #[inline] | ||
| 401 | pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self { | ||
| 402 | self.clock_divider = div; | ||
| 403 | self | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Sets the timestamp source | ||
| 407 | #[inline] | ||
| 408 | pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self { | ||
| 409 | self.timestamp_source = tss; | ||
| 410 | self | ||
| 411 | } | ||
| 412 | |||
| 413 | /// Sets the global filter settings | ||
| 414 | #[inline] | ||
| 415 | pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self { | ||
| 416 | self.global_filter = filter; | ||
| 417 | self | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | impl Default for FdCanConfig { | ||
| 422 | #[inline] | ||
| 423 | fn default() -> Self { | ||
| 424 | Self { | ||
| 425 | nbtr: NominalBitTiming::default(), | ||
| 426 | dbtr: DataBitTiming::default(), | ||
| 427 | automatic_retransmit: true, | ||
| 428 | transmit_pause: false, | ||
| 429 | frame_transmit: FrameTransmissionConfig::ClassicCanOnly, | ||
| 430 | non_iso_mode: false, | ||
| 431 | edge_filtering: false, | ||
| 432 | protocol_exception_handling: true, | ||
| 433 | clock_divider: ClockDivider::_1, | ||
| 434 | timestamp_source: TimestampSource::None, | ||
| 435 | global_filter: GlobalFilter::default(), | ||
| 436 | } | ||
| 437 | } | ||
| 438 | } | ||
diff --git a/embassy-stm32/src/can/fd/filter.rs b/embassy-stm32/src/can/fd/filter.rs new file mode 100644 index 000000000..3e2129e6e --- /dev/null +++ b/embassy-stm32/src/can/fd/filter.rs | |||
| @@ -0,0 +1,379 @@ | |||
| 1 | //! Definition of Filter structs for FDCAN Module | ||
| 2 | //! Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 3 | |||
| 4 | use embedded_can::{ExtendedId, StandardId}; | ||
| 5 | |||
| 6 | use crate::can::fd::message_ram; | ||
| 7 | pub use crate::can::fd::message_ram::{EXTENDED_FILTER_MAX, STANDARD_FILTER_MAX}; | ||
| 8 | |||
| 9 | /// A Standard Filter | ||
| 10 | pub type StandardFilter = Filter<StandardId, u16>; | ||
| 11 | /// An Extended Filter | ||
| 12 | pub type ExtendedFilter = Filter<ExtendedId, u32>; | ||
| 13 | |||
| 14 | impl Default for StandardFilter { | ||
| 15 | fn default() -> Self { | ||
| 16 | StandardFilter::disable() | ||
| 17 | } | ||
| 18 | } | ||
| 19 | impl Default for ExtendedFilter { | ||
| 20 | fn default() -> Self { | ||
| 21 | ExtendedFilter::disable() | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | impl StandardFilter { | ||
| 26 | /// Accept all messages in FIFO 0 | ||
| 27 | pub fn accept_all_into_fifo0() -> StandardFilter { | ||
| 28 | StandardFilter { | ||
| 29 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 30 | action: Action::StoreInFifo0, | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Accept all messages in FIFO 1 | ||
| 35 | pub fn accept_all_into_fifo1() -> StandardFilter { | ||
| 36 | StandardFilter { | ||
| 37 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 38 | action: Action::StoreInFifo1, | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Reject all messages | ||
| 43 | pub fn reject_all() -> StandardFilter { | ||
| 44 | StandardFilter { | ||
| 45 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 46 | action: Action::Reject, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Disable the filter | ||
| 51 | pub fn disable() -> StandardFilter { | ||
| 52 | StandardFilter { | ||
| 53 | filter: FilterType::Disabled, | ||
| 54 | action: Action::Disable, | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | impl ExtendedFilter { | ||
| 60 | /// Accept all messages in FIFO 0 | ||
| 61 | pub fn accept_all_into_fifo0() -> ExtendedFilter { | ||
| 62 | ExtendedFilter { | ||
| 63 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 64 | action: Action::StoreInFifo0, | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Accept all messages in FIFO 1 | ||
| 69 | pub fn accept_all_into_fifo1() -> ExtendedFilter { | ||
| 70 | ExtendedFilter { | ||
| 71 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 72 | action: Action::StoreInFifo1, | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// Reject all messages | ||
| 77 | pub fn reject_all() -> ExtendedFilter { | ||
| 78 | ExtendedFilter { | ||
| 79 | filter: FilterType::BitMask { filter: 0x0, mask: 0x0 }, | ||
| 80 | action: Action::Reject, | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Disable the filter | ||
| 85 | pub fn disable() -> ExtendedFilter { | ||
| 86 | ExtendedFilter { | ||
| 87 | filter: FilterType::Disabled, | ||
| 88 | action: Action::Disable, | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Filter Type | ||
| 94 | #[derive(Clone, Copy, Debug)] | ||
| 95 | pub enum FilterType<ID, UNIT> | ||
| 96 | where | ||
| 97 | ID: Copy + Clone + core::fmt::Debug, | ||
| 98 | UNIT: Copy + Clone + core::fmt::Debug, | ||
| 99 | { | ||
| 100 | /// Match with a range between two messages | ||
| 101 | Range { | ||
| 102 | /// First Id of the range | ||
| 103 | from: ID, | ||
| 104 | /// Last Id of the range | ||
| 105 | to: ID, | ||
| 106 | }, | ||
| 107 | /// Match with a bitmask | ||
| 108 | BitMask { | ||
| 109 | /// Filter of the bitmask | ||
| 110 | filter: UNIT, | ||
| 111 | /// Mask of the bitmask | ||
| 112 | mask: UNIT, | ||
| 113 | }, | ||
| 114 | /// Match with a single ID | ||
| 115 | DedicatedSingle(ID), | ||
| 116 | /// Match with one of two ID's | ||
| 117 | DedicatedDual(ID, ID), | ||
| 118 | /// Filter is disabled | ||
| 119 | Disabled, | ||
| 120 | } | ||
| 121 | impl<ID, UNIT> From<FilterType<ID, UNIT>> for message_ram::enums::FilterType | ||
| 122 | where | ||
| 123 | ID: Copy + Clone + core::fmt::Debug, | ||
| 124 | UNIT: Copy + Clone + core::fmt::Debug, | ||
| 125 | { | ||
| 126 | fn from(f: FilterType<ID, UNIT>) -> Self { | ||
| 127 | match f { | ||
| 128 | FilterType::Range { to: _, from: _ } => Self::RangeFilter, | ||
| 129 | FilterType::BitMask { filter: _, mask: _ } => Self::ClassicFilter, | ||
| 130 | FilterType::DedicatedSingle(_) => Self::DualIdFilter, | ||
| 131 | FilterType::DedicatedDual(_, _) => Self::DualIdFilter, | ||
| 132 | FilterType::Disabled => Self::FilterDisabled, | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | /// Filter Action | ||
| 138 | #[derive(Clone, Copy, Debug)] | ||
| 139 | pub enum Action { | ||
| 140 | /// No Action | ||
| 141 | Disable = 0b000, | ||
| 142 | /// Store an matching message in FIFO 0 | ||
| 143 | StoreInFifo0 = 0b001, | ||
| 144 | /// Store an matching message in FIFO 1 | ||
| 145 | StoreInFifo1 = 0b010, | ||
| 146 | /// Reject an matching message | ||
| 147 | Reject = 0b011, | ||
| 148 | /// Flag a matching message (But not store?!?) | ||
| 149 | FlagHighPrio = 0b100, | ||
| 150 | /// Flag a matching message as a High Priority message and store it in FIFO 0 | ||
| 151 | FlagHighPrioAndStoreInFifo0 = 0b101, | ||
| 152 | /// Flag a matching message as a High Priority message and store it in FIFO 1 | ||
| 153 | FlagHighPrioAndStoreInFifo1 = 0b110, | ||
| 154 | } | ||
| 155 | impl From<Action> for message_ram::enums::FilterElementConfig { | ||
| 156 | fn from(a: Action) -> Self { | ||
| 157 | match a { | ||
| 158 | Action::Disable => Self::DisableFilterElement, | ||
| 159 | Action::StoreInFifo0 => Self::StoreInFifo0, | ||
| 160 | Action::StoreInFifo1 => Self::StoreInFifo1, | ||
| 161 | Action::Reject => Self::Reject, | ||
| 162 | Action::FlagHighPrio => Self::SetPriority, | ||
| 163 | Action::FlagHighPrioAndStoreInFifo0 => Self::SetPriorityAndStoreInFifo0, | ||
| 164 | Action::FlagHighPrioAndStoreInFifo1 => Self::SetPriorityAndStoreInFifo1, | ||
| 165 | } | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | /// Filter | ||
| 170 | #[derive(Clone, Copy, Debug)] | ||
| 171 | pub struct Filter<ID, UNIT> | ||
| 172 | where | ||
| 173 | ID: Copy + Clone + core::fmt::Debug, | ||
| 174 | UNIT: Copy + Clone + core::fmt::Debug, | ||
| 175 | { | ||
| 176 | /// How to match an incoming message | ||
| 177 | pub filter: FilterType<ID, UNIT>, | ||
| 178 | /// What to do with a matching message | ||
| 179 | pub action: Action, | ||
| 180 | } | ||
| 181 | |||
| 182 | /// Standard Filter Slot | ||
| 183 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 184 | pub enum StandardFilterSlot { | ||
| 185 | /// 0 | ||
| 186 | _0 = 0, | ||
| 187 | /// 1 | ||
| 188 | _1 = 1, | ||
| 189 | /// 2 | ||
| 190 | _2 = 2, | ||
| 191 | /// 3 | ||
| 192 | _3 = 3, | ||
| 193 | /// 4 | ||
| 194 | _4 = 4, | ||
| 195 | /// 5 | ||
| 196 | _5 = 5, | ||
| 197 | /// 6 | ||
| 198 | _6 = 6, | ||
| 199 | /// 7 | ||
| 200 | _7 = 7, | ||
| 201 | /// 8 | ||
| 202 | _8 = 8, | ||
| 203 | /// 9 | ||
| 204 | _9 = 9, | ||
| 205 | /// 10 | ||
| 206 | _10 = 10, | ||
| 207 | /// 11 | ||
| 208 | _11 = 11, | ||
| 209 | /// 12 | ||
| 210 | _12 = 12, | ||
| 211 | /// 13 | ||
| 212 | _13 = 13, | ||
| 213 | /// 14 | ||
| 214 | _14 = 14, | ||
| 215 | /// 15 | ||
| 216 | _15 = 15, | ||
| 217 | /// 16 | ||
| 218 | _16 = 16, | ||
| 219 | /// 17 | ||
| 220 | _17 = 17, | ||
| 221 | /// 18 | ||
| 222 | _18 = 18, | ||
| 223 | /// 19 | ||
| 224 | _19 = 19, | ||
| 225 | /// 20 | ||
| 226 | _20 = 20, | ||
| 227 | /// 21 | ||
| 228 | _21 = 21, | ||
| 229 | /// 22 | ||
| 230 | _22 = 22, | ||
| 231 | /// 23 | ||
| 232 | _23 = 23, | ||
| 233 | /// 24 | ||
| 234 | _24 = 24, | ||
| 235 | /// 25 | ||
| 236 | _25 = 25, | ||
| 237 | /// 26 | ||
| 238 | _26 = 26, | ||
| 239 | /// 27 | ||
| 240 | _27 = 27, | ||
| 241 | } | ||
| 242 | impl From<u8> for StandardFilterSlot { | ||
| 243 | fn from(u: u8) -> Self { | ||
| 244 | match u { | ||
| 245 | 0 => StandardFilterSlot::_0, | ||
| 246 | 1 => StandardFilterSlot::_1, | ||
| 247 | 2 => StandardFilterSlot::_2, | ||
| 248 | 3 => StandardFilterSlot::_3, | ||
| 249 | 4 => StandardFilterSlot::_4, | ||
| 250 | 5 => StandardFilterSlot::_5, | ||
| 251 | 6 => StandardFilterSlot::_6, | ||
| 252 | 7 => StandardFilterSlot::_7, | ||
| 253 | 8 => StandardFilterSlot::_8, | ||
| 254 | 9 => StandardFilterSlot::_9, | ||
| 255 | 10 => StandardFilterSlot::_10, | ||
| 256 | 11 => StandardFilterSlot::_11, | ||
| 257 | 12 => StandardFilterSlot::_12, | ||
| 258 | 13 => StandardFilterSlot::_13, | ||
| 259 | 14 => StandardFilterSlot::_14, | ||
| 260 | 15 => StandardFilterSlot::_15, | ||
| 261 | 16 => StandardFilterSlot::_16, | ||
| 262 | 17 => StandardFilterSlot::_17, | ||
| 263 | 18 => StandardFilterSlot::_18, | ||
| 264 | 19 => StandardFilterSlot::_19, | ||
| 265 | 20 => StandardFilterSlot::_20, | ||
| 266 | 21 => StandardFilterSlot::_21, | ||
| 267 | 22 => StandardFilterSlot::_22, | ||
| 268 | 23 => StandardFilterSlot::_23, | ||
| 269 | 24 => StandardFilterSlot::_24, | ||
| 270 | 25 => StandardFilterSlot::_25, | ||
| 271 | 26 => StandardFilterSlot::_26, | ||
| 272 | 27 => StandardFilterSlot::_27, | ||
| 273 | _ => panic!("Standard Filter Slot Too High!"), | ||
| 274 | } | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | /// Extended Filter Slot | ||
| 279 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 280 | pub enum ExtendedFilterSlot { | ||
| 281 | /// 0 | ||
| 282 | _0 = 0, | ||
| 283 | /// 1 | ||
| 284 | _1 = 1, | ||
| 285 | /// 2 | ||
| 286 | _2 = 2, | ||
| 287 | /// 3 | ||
| 288 | _3 = 3, | ||
| 289 | /// 4 | ||
| 290 | _4 = 4, | ||
| 291 | /// 5 | ||
| 292 | _5 = 5, | ||
| 293 | /// 6 | ||
| 294 | _6 = 6, | ||
| 295 | /// 7 | ||
| 296 | _7 = 7, | ||
| 297 | } | ||
| 298 | impl From<u8> for ExtendedFilterSlot { | ||
| 299 | fn from(u: u8) -> Self { | ||
| 300 | match u { | ||
| 301 | 0 => ExtendedFilterSlot::_0, | ||
| 302 | 1 => ExtendedFilterSlot::_1, | ||
| 303 | 2 => ExtendedFilterSlot::_2, | ||
| 304 | 3 => ExtendedFilterSlot::_3, | ||
| 305 | 4 => ExtendedFilterSlot::_4, | ||
| 306 | 5 => ExtendedFilterSlot::_5, | ||
| 307 | 6 => ExtendedFilterSlot::_6, | ||
| 308 | 7 => ExtendedFilterSlot::_7, | ||
| 309 | _ => panic!("Extended Filter Slot Too High!"), // Should be unreachable | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | /// Enum over both Standard and Extended Filter ID's | ||
| 315 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 316 | pub enum FilterId { | ||
| 317 | /// Standard Filter Slots | ||
| 318 | Standard(StandardFilterSlot), | ||
| 319 | /// Extended Filter Slots | ||
| 320 | Extended(ExtendedFilterSlot), | ||
| 321 | } | ||
| 322 | |||
| 323 | pub(crate) trait ActivateFilter<ID, UNIT> | ||
| 324 | where | ||
| 325 | ID: Copy + Clone + core::fmt::Debug, | ||
| 326 | UNIT: Copy + Clone + core::fmt::Debug, | ||
| 327 | { | ||
| 328 | fn activate(&mut self, f: Filter<ID, UNIT>); | ||
| 329 | // fn read(&self) -> Filter<ID, UNIT>; | ||
| 330 | } | ||
| 331 | |||
| 332 | impl ActivateFilter<StandardId, u16> for message_ram::StandardFilter { | ||
| 333 | fn activate(&mut self, f: Filter<StandardId, u16>) { | ||
| 334 | let sft = f.filter.into(); | ||
| 335 | |||
| 336 | let (sfid1, sfid2) = match f.filter { | ||
| 337 | FilterType::Range { to, from } => (to.as_raw(), from.as_raw()), | ||
| 338 | FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()), | ||
| 339 | FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()), | ||
| 340 | FilterType::BitMask { filter, mask } => (filter, mask), | ||
| 341 | FilterType::Disabled => (0x0, 0x0), | ||
| 342 | }; | ||
| 343 | let sfec = f.action.into(); | ||
| 344 | self.write(|w| { | ||
| 345 | unsafe { w.sfid1().bits(sfid1).sfid2().bits(sfid2) } | ||
| 346 | .sft() | ||
| 347 | .set_filter_type(sft) | ||
| 348 | .sfec() | ||
| 349 | .set_filter_element_config(sfec) | ||
| 350 | }); | ||
| 351 | } | ||
| 352 | // fn read(&self) -> Filter<StandardId, u16> { | ||
| 353 | // todo!() | ||
| 354 | // } | ||
| 355 | } | ||
| 356 | impl ActivateFilter<ExtendedId, u32> for message_ram::ExtendedFilter { | ||
| 357 | fn activate(&mut self, f: Filter<ExtendedId, u32>) { | ||
| 358 | let eft = f.filter.into(); | ||
| 359 | |||
| 360 | let (efid1, efid2) = match f.filter { | ||
| 361 | FilterType::Range { to, from } => (to.as_raw(), from.as_raw()), | ||
| 362 | FilterType::DedicatedSingle(id) => (id.as_raw(), id.as_raw()), | ||
| 363 | FilterType::DedicatedDual(id1, id2) => (id1.as_raw(), id2.as_raw()), | ||
| 364 | FilterType::BitMask { filter, mask } => (filter, mask), | ||
| 365 | FilterType::Disabled => (0x0, 0x0), | ||
| 366 | }; | ||
| 367 | let efec = f.action.into(); | ||
| 368 | self.write(|w| { | ||
| 369 | unsafe { w.efid1().bits(efid1).efid2().bits(efid2) } | ||
| 370 | .eft() | ||
| 371 | .set_filter_type(eft) | ||
| 372 | .efec() | ||
| 373 | .set_filter_element_config(efec) | ||
| 374 | }); | ||
| 375 | } | ||
| 376 | // fn read(&self) -> Filter<ExtendedId, u32> { | ||
| 377 | // todo!() | ||
| 378 | // } | ||
| 379 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/common.rs b/embassy-stm32/src/can/fd/message_ram/common.rs new file mode 100644 index 000000000..108c1a428 --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/common.rs | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | #![allow(non_camel_case_types)] | ||
| 3 | #![allow(non_snake_case)] | ||
| 4 | #![allow(unused)] | ||
| 5 | |||
| 6 | use super::enums::{ | ||
| 7 | BitRateSwitching, ErrorStateIndicator, FilterElementConfig, FilterType, FrameFormat, IdType, | ||
| 8 | RemoteTransmissionRequest, | ||
| 9 | }; | ||
| 10 | use super::generic; | ||
| 11 | |||
| 12 | #[doc = "Reader of field `ID`"] | ||
| 13 | pub type ID_R = generic::R<u32, u32>; | ||
| 14 | |||
| 15 | #[doc = "Reader of field `RTR`"] | ||
| 16 | pub type RTR_R = generic::R<bool, RemoteTransmissionRequest>; | ||
| 17 | impl RTR_R { | ||
| 18 | pub fn rtr(&self) -> RemoteTransmissionRequest { | ||
| 19 | match self.bits { | ||
| 20 | false => RemoteTransmissionRequest::TransmitDataFrame, | ||
| 21 | true => RemoteTransmissionRequest::TransmitRemoteFrame, | ||
| 22 | } | ||
| 23 | } | ||
| 24 | pub fn is_transmit_remote_frame(&self) -> bool { | ||
| 25 | *self == RemoteTransmissionRequest::TransmitRemoteFrame | ||
| 26 | } | ||
| 27 | pub fn is_transmit_data_frame(&self) -> bool { | ||
| 28 | *self == RemoteTransmissionRequest::TransmitDataFrame | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | #[doc = "Reader of field `XTD`"] | ||
| 33 | pub type XTD_R = generic::R<bool, IdType>; | ||
| 34 | impl XTD_R { | ||
| 35 | pub fn id_type(&self) -> IdType { | ||
| 36 | match self.bits() { | ||
| 37 | false => IdType::StandardId, | ||
| 38 | true => IdType::ExtendedId, | ||
| 39 | } | ||
| 40 | } | ||
| 41 | pub fn is_standard_id(&self) -> bool { | ||
| 42 | *self == IdType::StandardId | ||
| 43 | } | ||
| 44 | pub fn is_exteded_id(&self) -> bool { | ||
| 45 | *self == IdType::ExtendedId | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[doc = "Reader of field `ESI`"] | ||
| 50 | pub type ESI_R = generic::R<bool, ErrorStateIndicator>; | ||
| 51 | impl ESI_R { | ||
| 52 | pub fn error_state(&self) -> ErrorStateIndicator { | ||
| 53 | match self.bits() { | ||
| 54 | false => ErrorStateIndicator::ErrorActive, | ||
| 55 | true => ErrorStateIndicator::ErrorPassive, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | pub fn is_error_active(&self) -> bool { | ||
| 59 | *self == ErrorStateIndicator::ErrorActive | ||
| 60 | } | ||
| 61 | pub fn is_error_passive(&self) -> bool { | ||
| 62 | *self == ErrorStateIndicator::ErrorPassive | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[doc = "Reader of field `DLC`"] | ||
| 67 | pub type DLC_R = generic::R<u8, u8>; | ||
| 68 | |||
| 69 | #[doc = "Reader of field `BRS`"] | ||
| 70 | pub type BRS_R = generic::R<bool, BitRateSwitching>; | ||
| 71 | impl BRS_R { | ||
| 72 | pub fn bit_rate_switching(&self) -> BitRateSwitching { | ||
| 73 | match self.bits() { | ||
| 74 | true => BitRateSwitching::WithBRS, | ||
| 75 | false => BitRateSwitching::WithoutBRS, | ||
| 76 | } | ||
| 77 | } | ||
| 78 | pub fn is_with_brs(&self) -> bool { | ||
| 79 | *self == BitRateSwitching::WithBRS | ||
| 80 | } | ||
| 81 | pub fn is_without_brs(&self) -> bool { | ||
| 82 | *self == BitRateSwitching::WithoutBRS | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[doc = "Reader of field `FDF`"] | ||
| 87 | pub type FDF_R = generic::R<bool, FrameFormat>; | ||
| 88 | impl FDF_R { | ||
| 89 | pub fn frame_format(&self) -> FrameFormat { | ||
| 90 | match self.bits() { | ||
| 91 | false => FrameFormat::Classic, | ||
| 92 | true => FrameFormat::Fdcan, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | pub fn is_classic_format(&self) -> bool { | ||
| 96 | *self == FrameFormat::Classic | ||
| 97 | } | ||
| 98 | pub fn is_fdcan_format(&self) -> bool { | ||
| 99 | *self == FrameFormat::Fdcan | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | #[doc = "Reader of field `(X|S)FT`"] | ||
| 104 | pub type ESFT_R = generic::R<u8, FilterType>; | ||
| 105 | impl ESFT_R { | ||
| 106 | #[doc = r"Gets the Filtertype"] | ||
| 107 | #[inline(always)] | ||
| 108 | pub fn to_filter_type(&self) -> FilterType { | ||
| 109 | match self.bits() { | ||
| 110 | 0b00 => FilterType::RangeFilter, | ||
| 111 | 0b01 => FilterType::DualIdFilter, | ||
| 112 | 0b10 => FilterType::ClassicFilter, | ||
| 113 | 0b11 => FilterType::FilterDisabled, | ||
| 114 | _ => unreachable!(), | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | #[doc = "Reader of field `(E|S)FEC`"] | ||
| 120 | pub type ESFEC_R = generic::R<u8, FilterElementConfig>; | ||
| 121 | impl ESFEC_R { | ||
| 122 | pub fn to_filter_element_config(&self) -> FilterElementConfig { | ||
| 123 | match self.bits() { | ||
| 124 | 0b000 => FilterElementConfig::DisableFilterElement, | ||
| 125 | 0b001 => FilterElementConfig::StoreInFifo0, | ||
| 126 | 0b010 => FilterElementConfig::StoreInFifo1, | ||
| 127 | 0b011 => FilterElementConfig::Reject, | ||
| 128 | 0b100 => FilterElementConfig::SetPriority, | ||
| 129 | 0b101 => FilterElementConfig::SetPriorityAndStoreInFifo0, | ||
| 130 | 0b110 => FilterElementConfig::SetPriorityAndStoreInFifo1, | ||
| 131 | _ => unimplemented!(), | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/enums.rs b/embassy-stm32/src/can/fd/message_ram/enums.rs new file mode 100644 index 000000000..0ec5e0f34 --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/enums.rs | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | /// Datalength is the message length generalised over | ||
| 4 | /// the Standard (Classic) and FDCAN message types | ||
| 5 | |||
| 6 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 7 | pub enum DataLength { | ||
| 8 | Classic(u8), | ||
| 9 | Fdcan(u8), | ||
| 10 | } | ||
| 11 | impl DataLength { | ||
| 12 | /// Creates a DataLength type | ||
| 13 | /// | ||
| 14 | /// Uses the byte length and Type of frame as input | ||
| 15 | pub fn new(len: u8, ff: FrameFormat) -> DataLength { | ||
| 16 | match ff { | ||
| 17 | FrameFormat::Classic => match len { | ||
| 18 | 0..=8 => DataLength::Classic(len), | ||
| 19 | _ => panic!("DataLength > 8"), | ||
| 20 | }, | ||
| 21 | FrameFormat::Fdcan => match len { | ||
| 22 | 0..=64 => DataLength::Fdcan(len), | ||
| 23 | _ => panic!("DataLength > 64"), | ||
| 24 | }, | ||
| 25 | } | ||
| 26 | } | ||
| 27 | /// Specialised function to create classic frames | ||
| 28 | pub fn new_classic(len: u8) -> DataLength { | ||
| 29 | Self::new(len, FrameFormat::Classic) | ||
| 30 | } | ||
| 31 | /// Specialised function to create FDCAN frames | ||
| 32 | pub fn new_fdcan(len: u8) -> DataLength { | ||
| 33 | Self::new(len, FrameFormat::Fdcan) | ||
| 34 | } | ||
| 35 | |||
| 36 | /// returns the length in bytes | ||
| 37 | pub fn len(&self) -> u8 { | ||
| 38 | match self { | ||
| 39 | DataLength::Classic(l) | DataLength::Fdcan(l) => *l, | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | pub(crate) fn dlc(&self) -> u8 { | ||
| 44 | match self { | ||
| 45 | DataLength::Classic(l) => *l, | ||
| 46 | // See RM0433 Rev 7 Table 475. DLC coding | ||
| 47 | DataLength::Fdcan(l) => match l { | ||
| 48 | 0..=8 => *l, | ||
| 49 | 9..=12 => 9, | ||
| 50 | 13..=16 => 10, | ||
| 51 | 17..=20 => 11, | ||
| 52 | 21..=24 => 12, | ||
| 53 | 25..=32 => 13, | ||
| 54 | 33..=48 => 14, | ||
| 55 | 49..=64 => 15, | ||
| 56 | _ => panic!("DataLength > 64"), | ||
| 57 | }, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | impl From<DataLength> for FrameFormat { | ||
| 62 | fn from(dl: DataLength) -> FrameFormat { | ||
| 63 | match dl { | ||
| 64 | DataLength::Classic(_) => FrameFormat::Classic, | ||
| 65 | DataLength::Fdcan(_) => FrameFormat::Fdcan, | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | /// Wheter or not to generate an Tx Event | ||
| 71 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 72 | pub enum Event { | ||
| 73 | /// Do not generate an Tx Event | ||
| 74 | NoEvent, | ||
| 75 | /// Generate an Tx Event with a specified ID | ||
| 76 | Event(u8), | ||
| 77 | } | ||
| 78 | |||
| 79 | impl From<Event> for EventControl { | ||
| 80 | fn from(e: Event) -> Self { | ||
| 81 | match e { | ||
| 82 | Event::NoEvent => EventControl::DoNotStore, | ||
| 83 | Event::Event(_) => EventControl::Store, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl From<Option<u8>> for Event { | ||
| 89 | fn from(mm: Option<u8>) -> Self { | ||
| 90 | match mm { | ||
| 91 | None => Event::NoEvent, | ||
| 92 | Some(mm) => Event::Event(mm), | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl From<Event> for Option<u8> { | ||
| 98 | fn from(e: Event) -> Option<u8> { | ||
| 99 | match e { | ||
| 100 | Event::NoEvent => None, | ||
| 101 | Event::Event(mm) => Some(mm), | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /// TODO | ||
| 107 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 108 | pub enum ErrorStateIndicator { | ||
| 109 | /// TODO | ||
| 110 | ErrorActive = 0, | ||
| 111 | /// TODO | ||
| 112 | ErrorPassive = 1, | ||
| 113 | } | ||
| 114 | impl From<ErrorStateIndicator> for bool { | ||
| 115 | #[inline(always)] | ||
| 116 | fn from(e: ErrorStateIndicator) -> Self { | ||
| 117 | e as u8 != 0 | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | /// Type of frame, standard (classic) or FdCAN | ||
| 122 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 123 | pub enum FrameFormat { | ||
| 124 | Classic = 0, | ||
| 125 | Fdcan = 1, | ||
| 126 | } | ||
| 127 | impl From<FrameFormat> for bool { | ||
| 128 | #[inline(always)] | ||
| 129 | fn from(e: FrameFormat) -> Self { | ||
| 130 | e as u8 != 0 | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// Type of Id, Standard or Extended | ||
| 135 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 136 | pub enum IdType { | ||
| 137 | /// Standard ID | ||
| 138 | StandardId = 0, | ||
| 139 | /// Extended ID | ||
| 140 | ExtendedId = 1, | ||
| 141 | } | ||
| 142 | impl From<IdType> for bool { | ||
| 143 | #[inline(always)] | ||
| 144 | fn from(e: IdType) -> Self { | ||
| 145 | e as u8 != 0 | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | /// Whether the frame contains data or requests data | ||
| 150 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 151 | pub enum RemoteTransmissionRequest { | ||
| 152 | /// Frame contains data | ||
| 153 | TransmitDataFrame = 0, | ||
| 154 | /// frame does not contain data | ||
| 155 | TransmitRemoteFrame = 1, | ||
| 156 | } | ||
| 157 | impl From<RemoteTransmissionRequest> for bool { | ||
| 158 | #[inline(always)] | ||
| 159 | fn from(e: RemoteTransmissionRequest) -> Self { | ||
| 160 | e as u8 != 0 | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Whether BitRateSwitching should be or was enabled | ||
| 165 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 166 | pub enum BitRateSwitching { | ||
| 167 | /// disable bit rate switching | ||
| 168 | WithoutBRS = 0, | ||
| 169 | /// enable bit rate switching | ||
| 170 | WithBRS = 1, | ||
| 171 | } | ||
| 172 | impl From<BitRateSwitching> for bool { | ||
| 173 | #[inline(always)] | ||
| 174 | fn from(e: BitRateSwitching) -> Self { | ||
| 175 | e as u8 != 0 | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /// Whether to store transmit Events | ||
| 180 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 181 | pub enum EventControl { | ||
| 182 | /// do not store an tx event | ||
| 183 | DoNotStore, | ||
| 184 | /// store transmit events | ||
| 185 | Store, | ||
| 186 | } | ||
| 187 | impl From<EventControl> for bool { | ||
| 188 | #[inline(always)] | ||
| 189 | fn from(e: EventControl) -> Self { | ||
| 190 | e as u8 != 0 | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | /// If an received message matched any filters | ||
| 195 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 196 | pub enum FilterFrameMatch { | ||
| 197 | /// This did match filter <id> | ||
| 198 | DidMatch(u8), | ||
| 199 | /// This received frame did not match any specific filters | ||
| 200 | DidNotMatch, | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Type of filter to be used | ||
| 204 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 205 | pub enum FilterType { | ||
| 206 | /// Filter uses the range between two id's | ||
| 207 | RangeFilter = 0b00, | ||
| 208 | /// The filter matches on two specific id's (or one ID checked twice) | ||
| 209 | DualIdFilter = 0b01, | ||
| 210 | /// Filter is using a bitmask | ||
| 211 | ClassicFilter = 0b10, | ||
| 212 | /// Filter is disabled | ||
| 213 | FilterDisabled = 0b11, | ||
| 214 | } | ||
| 215 | |||
| 216 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 217 | pub enum FilterElementConfig { | ||
| 218 | /// Filter is disabled | ||
| 219 | DisableFilterElement = 0b000, | ||
| 220 | /// Store a matching message in FIFO 0 | ||
| 221 | StoreInFifo0 = 0b001, | ||
| 222 | /// Store a matching message in FIFO 1 | ||
| 223 | StoreInFifo1 = 0b010, | ||
| 224 | /// Reject a matching message | ||
| 225 | Reject = 0b011, | ||
| 226 | /// Flag that a priority message has been received, *But do note store!*?? | ||
| 227 | SetPriority = 0b100, | ||
| 228 | /// Flag and store message in FIFO 0 | ||
| 229 | SetPriorityAndStoreInFifo0 = 0b101, | ||
| 230 | /// Flag and store message in FIFO 1 | ||
| 231 | SetPriorityAndStoreInFifo1 = 0b110, | ||
| 232 | //_Unused = 0b111, | ||
| 233 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/extended_filter.rs b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs new file mode 100644 index 000000000..453e9056e --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/extended_filter.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | #![allow(non_camel_case_types)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(unused)] | ||
| 6 | |||
| 7 | use super::common::{ESFEC_R, ESFT_R}; | ||
| 8 | use super::enums::{FilterElementConfig, FilterType}; | ||
| 9 | use super::generic; | ||
| 10 | |||
| 11 | #[doc = "Reader of register ExtendedFilter"] | ||
| 12 | pub(crate) type R = generic::R<super::ExtendedFilterType, super::ExtendedFilter>; | ||
| 13 | #[doc = "Writer for register ExtendedFilter"] | ||
| 14 | pub(crate) type W = generic::W<super::ExtendedFilterType, super::ExtendedFilter>; | ||
| 15 | #[doc = "Register ExtendedFilter `reset()`'s"] | ||
| 16 | impl generic::ResetValue for super::ExtendedFilter { | ||
| 17 | type Type = super::ExtendedFilterType; | ||
| 18 | #[inline(always)] | ||
| 19 | fn reset_value() -> Self::Type { | ||
| 20 | // Sets filter element to Disabled | ||
| 21 | [0x0, 0x0] | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | #[doc = "Reader of field `EFID2`"] | ||
| 26 | pub(crate) type EFID2_R = generic::R<u32, u32>; | ||
| 27 | #[doc = "Write proxy for field `EFID2`"] | ||
| 28 | pub(crate) struct EFID2_W<'a> { | ||
| 29 | w: &'a mut W, | ||
| 30 | } | ||
| 31 | impl<'a> EFID2_W<'a> { | ||
| 32 | #[doc = r"Writes raw bits to the field"] | ||
| 33 | #[inline(always)] | ||
| 34 | pub unsafe fn bits(self, value: u32) -> &'a mut W { | ||
| 35 | self.w.bits[1] = (self.w.bits[1] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF); | ||
| 36 | self.w | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[doc = "Reader of field `EFID1`"] | ||
| 41 | pub(crate) type EFID1_R = generic::R<u32, u32>; | ||
| 42 | #[doc = "Write proxy for field `EFID1`"] | ||
| 43 | pub(crate) struct EFID1_W<'a> { | ||
| 44 | w: &'a mut W, | ||
| 45 | } | ||
| 46 | impl<'a> EFID1_W<'a> { | ||
| 47 | #[doc = r"Writes raw bits to the field"] | ||
| 48 | #[inline(always)] | ||
| 49 | pub unsafe fn bits(self, value: u32) -> &'a mut W { | ||
| 50 | self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF); | ||
| 51 | self.w | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | #[doc = "Write proxy for field `EFEC`"] | ||
| 56 | pub(crate) struct EFEC_W<'a> { | ||
| 57 | w: &'a mut W, | ||
| 58 | } | ||
| 59 | impl<'a> EFEC_W<'a> { | ||
| 60 | #[doc = r"Writes raw bits to the field"] | ||
| 61 | #[inline(always)] | ||
| 62 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 63 | self.w.bits[0] = (self.w.bits[0] & !(0x07 << 29)) | (((value as u32) & 0x07) << 29); | ||
| 64 | self.w | ||
| 65 | } | ||
| 66 | #[doc = r"Sets the field according to FilterElementConfig"] | ||
| 67 | #[inline(always)] | ||
| 68 | pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W { | ||
| 69 | //SAFETY: FilterElementConfig only be valid options | ||
| 70 | unsafe { self.bits(fec as u8) } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | #[doc = "Write proxy for field `EFT`"] | ||
| 75 | pub(crate) struct EFT_W<'a> { | ||
| 76 | w: &'a mut W, | ||
| 77 | } | ||
| 78 | impl<'a> EFT_W<'a> { | ||
| 79 | #[doc = r"Sets the field according the FilterType"] | ||
| 80 | #[inline(always)] | ||
| 81 | pub fn set_filter_type(self, filter: FilterType) -> &'a mut W { | ||
| 82 | //SAFETY: FilterType only be valid options | ||
| 83 | unsafe { self.bits(filter as u8) } | ||
| 84 | } | ||
| 85 | #[doc = r"Writes raw bits to the field"] | ||
| 86 | #[inline(always)] | ||
| 87 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 88 | self.w.bits[1] = (self.w.bits[1] & !(0x03 << 30)) | (((value as u32) & 0x03) << 30); | ||
| 89 | self.w | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | impl R { | ||
| 94 | #[doc = "Byte 0 - Bits 0:28 - EFID1"] | ||
| 95 | #[inline(always)] | ||
| 96 | pub fn sfid1(&self) -> EFID1_R { | ||
| 97 | EFID1_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32) | ||
| 98 | } | ||
| 99 | #[doc = "Byte 0 - Bits 29:31 - EFEC"] | ||
| 100 | #[inline(always)] | ||
| 101 | pub fn efec(&self) -> ESFEC_R { | ||
| 102 | ESFEC_R::new(((self.bits[0] >> 29) & 0x07) as u8) | ||
| 103 | } | ||
| 104 | #[doc = "Byte 1 - Bits 0:28 - EFID2"] | ||
| 105 | #[inline(always)] | ||
| 106 | pub fn sfid2(&self) -> EFID2_R { | ||
| 107 | EFID2_R::new(((self.bits[1]) & 0x1FFFFFFF) as u32) | ||
| 108 | } | ||
| 109 | #[doc = "Byte 1 - Bits 30:31 - EFT"] | ||
| 110 | #[inline(always)] | ||
| 111 | pub fn eft(&self) -> ESFT_R { | ||
| 112 | ESFT_R::new(((self.bits[1] >> 30) & 0x03) as u8) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | impl W { | ||
| 116 | #[doc = "Byte 0 - Bits 0:28 - EFID1"] | ||
| 117 | #[inline(always)] | ||
| 118 | pub fn efid1(&mut self) -> EFID1_W { | ||
| 119 | EFID1_W { w: self } | ||
| 120 | } | ||
| 121 | #[doc = "Byte 0 - Bits 29:31 - EFEC"] | ||
| 122 | #[inline(always)] | ||
| 123 | pub fn efec(&mut self) -> EFEC_W { | ||
| 124 | EFEC_W { w: self } | ||
| 125 | } | ||
| 126 | #[doc = "Byte 1 - Bits 0:28 - EFID2"] | ||
| 127 | #[inline(always)] | ||
| 128 | pub fn efid2(&mut self) -> EFID2_W { | ||
| 129 | EFID2_W { w: self } | ||
| 130 | } | ||
| 131 | #[doc = "Byte 1 - Bits 30:31 - EFT"] | ||
| 132 | #[inline(always)] | ||
| 133 | pub fn eft(&mut self) -> EFT_W { | ||
| 134 | EFT_W { w: self } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/generic.rs b/embassy-stm32/src/can/fd/message_ram/generic.rs new file mode 100644 index 000000000..1a5e121b4 --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/generic.rs | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | use core::marker; | ||
| 4 | |||
| 5 | ///This trait shows that register has `read` method | ||
| 6 | /// | ||
| 7 | ///Registers marked with `Writable` can be also `modify`'ed | ||
| 8 | pub trait Readable {} | ||
| 9 | |||
| 10 | ///This trait shows that register has `write`, `write_with_zero` and `reset` method | ||
| 11 | /// | ||
| 12 | ///Registers marked with `Readable` can be also `modify`'ed | ||
| 13 | pub trait Writable {} | ||
| 14 | |||
| 15 | ///Reset value of the register | ||
| 16 | /// | ||
| 17 | ///This value is initial value for `write` method. | ||
| 18 | ///It can be also directly writed to register by `reset` method. | ||
| 19 | pub trait ResetValue { | ||
| 20 | ///Register size | ||
| 21 | type Type; | ||
| 22 | ///Reset value of the register | ||
| 23 | fn reset_value() -> Self::Type; | ||
| 24 | } | ||
| 25 | |||
| 26 | ///This structure provides volatile access to register | ||
| 27 | pub struct Reg<U, REG> { | ||
| 28 | register: vcell::VolatileCell<U>, | ||
| 29 | _marker: marker::PhantomData<REG>, | ||
| 30 | } | ||
| 31 | |||
| 32 | unsafe impl<U: Send, REG> Send for Reg<U, REG> {} | ||
| 33 | |||
| 34 | impl<U, REG> Reg<U, REG> | ||
| 35 | where | ||
| 36 | Self: Readable, | ||
| 37 | U: Copy, | ||
| 38 | { | ||
| 39 | ///Reads the contents of `Readable` register | ||
| 40 | /// | ||
| 41 | ///You can read the contents of a register in such way: | ||
| 42 | ///```ignore | ||
| 43 | ///let bits = periph.reg.read().bits(); | ||
| 44 | ///``` | ||
| 45 | ///or get the content of a particular field of a register. | ||
| 46 | ///```ignore | ||
| 47 | ///let reader = periph.reg.read(); | ||
| 48 | ///let bits = reader.field1().bits(); | ||
| 49 | ///let flag = reader.field2().bit_is_set(); | ||
| 50 | ///``` | ||
| 51 | #[inline(always)] | ||
| 52 | pub fn read(&self) -> R<U, Self> { | ||
| 53 | R { | ||
| 54 | bits: self.register.get(), | ||
| 55 | _reg: marker::PhantomData, | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | impl<U, REG> Reg<U, REG> | ||
| 61 | where | ||
| 62 | Self: ResetValue<Type = U> + Writable, | ||
| 63 | U: Copy, | ||
| 64 | { | ||
| 65 | ///Writes the reset value to `Writable` register | ||
| 66 | /// | ||
| 67 | ///Resets the register to its initial state | ||
| 68 | #[inline(always)] | ||
| 69 | pub fn reset(&self) { | ||
| 70 | self.register.set(Self::reset_value()) | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<U, REG> Reg<U, REG> | ||
| 75 | where | ||
| 76 | Self: ResetValue<Type = U> + Writable, | ||
| 77 | U: Copy, | ||
| 78 | { | ||
| 79 | ///Writes bits to `Writable` register | ||
| 80 | /// | ||
| 81 | ///You can write raw bits into a register: | ||
| 82 | ///```ignore | ||
| 83 | ///periph.reg.write(|w| unsafe { w.bits(rawbits) }); | ||
| 84 | ///``` | ||
| 85 | ///or write only the fields you need: | ||
| 86 | ///```ignore | ||
| 87 | ///periph.reg.write(|w| w | ||
| 88 | /// .field1().bits(newfield1bits) | ||
| 89 | /// .field2().set_bit() | ||
| 90 | /// .field3().variant(VARIANT) | ||
| 91 | ///); | ||
| 92 | ///``` | ||
| 93 | ///Other fields will have reset value. | ||
| 94 | #[inline(always)] | ||
| 95 | pub fn write<F>(&self, f: F) | ||
| 96 | where | ||
| 97 | F: FnOnce(&mut W<U, Self>) -> &mut W<U, Self>, | ||
| 98 | { | ||
| 99 | self.register.set( | ||
| 100 | f(&mut W { | ||
| 101 | bits: Self::reset_value(), | ||
| 102 | _reg: marker::PhantomData, | ||
| 103 | }) | ||
| 104 | .bits, | ||
| 105 | ); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | ///Register/field reader | ||
| 110 | /// | ||
| 111 | ///Result of the [`read`](Reg::read) method of a register. | ||
| 112 | ///Also it can be used in the [`modify`](Reg::read) method | ||
| 113 | pub struct R<U, T> { | ||
| 114 | pub(crate) bits: U, | ||
| 115 | _reg: marker::PhantomData<T>, | ||
| 116 | } | ||
| 117 | |||
| 118 | impl<U, T> R<U, T> | ||
| 119 | where | ||
| 120 | U: Copy, | ||
| 121 | { | ||
| 122 | ///Create new instance of reader | ||
| 123 | #[inline(always)] | ||
| 124 | pub(crate) fn new(bits: U) -> Self { | ||
| 125 | Self { | ||
| 126 | bits, | ||
| 127 | _reg: marker::PhantomData, | ||
| 128 | } | ||
| 129 | } | ||
| 130 | ///Read raw bits from register/field | ||
| 131 | #[inline(always)] | ||
| 132 | pub fn bits(&self) -> U { | ||
| 133 | self.bits | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<U, T, FI> PartialEq<FI> for R<U, T> | ||
| 138 | where | ||
| 139 | U: PartialEq, | ||
| 140 | FI: Copy + Into<U>, | ||
| 141 | { | ||
| 142 | #[inline(always)] | ||
| 143 | fn eq(&self, other: &FI) -> bool { | ||
| 144 | self.bits.eq(&(*other).into()) | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | impl<FI> R<bool, FI> { | ||
| 149 | ///Value of the field as raw bits | ||
| 150 | #[inline(always)] | ||
| 151 | pub fn bit(&self) -> bool { | ||
| 152 | self.bits | ||
| 153 | } | ||
| 154 | ///Returns `true` if the bit is clear (0) | ||
| 155 | #[inline(always)] | ||
| 156 | pub fn bit_is_clear(&self) -> bool { | ||
| 157 | !self.bit() | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | ///Register writer | ||
| 162 | /// | ||
| 163 | ///Used as an argument to the closures in the [`write`](Reg::write) and [`modify`](Reg::modify) methods of the register | ||
| 164 | pub struct W<U, REG> { | ||
| 165 | ///Writable bits | ||
| 166 | pub(crate) bits: U, | ||
| 167 | _reg: marker::PhantomData<REG>, | ||
| 168 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/mod.rs b/embassy-stm32/src/can/fd/message_ram/mod.rs new file mode 100644 index 000000000..830edf3bb --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/mod.rs | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | use volatile_register::RW; | ||
| 4 | |||
| 5 | pub(crate) mod common; | ||
| 6 | pub(crate) mod enums; | ||
| 7 | pub(crate) mod generic; | ||
| 8 | |||
| 9 | /// Number of Receive Fifos configured by this module | ||
| 10 | pub const RX_FIFOS_MAX: u8 = 2; | ||
| 11 | /// Number of Receive Messages per RxFifo configured by this module | ||
| 12 | pub const RX_FIFO_MAX: u8 = 3; | ||
| 13 | /// Number of Transmit Messages configured by this module | ||
| 14 | pub const TX_FIFO_MAX: u8 = 3; | ||
| 15 | /// Number of Transmit Events configured by this module | ||
| 16 | pub const TX_EVENT_MAX: u8 = 3; | ||
| 17 | /// Number of Standard Filters configured by this module | ||
| 18 | pub const STANDARD_FILTER_MAX: u8 = 28; | ||
| 19 | /// Number of Extended Filters configured by this module | ||
| 20 | pub const EXTENDED_FILTER_MAX: u8 = 8; | ||
| 21 | |||
| 22 | /// MessageRam Overlay | ||
| 23 | #[repr(C)] | ||
| 24 | pub struct RegisterBlock { | ||
| 25 | pub(crate) filters: Filters, | ||
| 26 | pub(crate) receive: [Receive; RX_FIFOS_MAX as usize], | ||
| 27 | pub(crate) transmit: Transmit, | ||
| 28 | } | ||
| 29 | impl RegisterBlock { | ||
| 30 | pub fn reset(&mut self) { | ||
| 31 | self.filters.reset(); | ||
| 32 | self.receive[0].reset(); | ||
| 33 | self.receive[1].reset(); | ||
| 34 | self.transmit.reset(); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | #[repr(C)] | ||
| 39 | pub(crate) struct Filters { | ||
| 40 | pub(crate) flssa: [StandardFilter; STANDARD_FILTER_MAX as usize], | ||
| 41 | pub(crate) flesa: [ExtendedFilter; EXTENDED_FILTER_MAX as usize], | ||
| 42 | } | ||
| 43 | impl Filters { | ||
| 44 | pub fn reset(&mut self) { | ||
| 45 | for sf in &mut self.flssa { | ||
| 46 | sf.reset(); | ||
| 47 | } | ||
| 48 | for ef in &mut self.flesa { | ||
| 49 | ef.reset(); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | #[repr(C)] | ||
| 55 | pub(crate) struct Receive { | ||
| 56 | pub(crate) fxsa: [RxFifoElement; RX_FIFO_MAX as usize], | ||
| 57 | } | ||
| 58 | impl Receive { | ||
| 59 | pub fn reset(&mut self) { | ||
| 60 | for fe in &mut self.fxsa { | ||
| 61 | fe.reset(); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[repr(C)] | ||
| 67 | pub(crate) struct Transmit { | ||
| 68 | pub(crate) efsa: [TxEventElement; TX_EVENT_MAX as usize], | ||
| 69 | pub(crate) tbsa: [TxBufferElement; TX_FIFO_MAX as usize], | ||
| 70 | } | ||
| 71 | impl Transmit { | ||
| 72 | pub fn reset(&mut self) { | ||
| 73 | for ee in &mut self.efsa { | ||
| 74 | ee.reset(); | ||
| 75 | } | ||
| 76 | for be in &mut self.tbsa { | ||
| 77 | be.reset(); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | pub(crate) mod standard_filter; | ||
| 83 | pub(crate) type StandardFilterType = u32; | ||
| 84 | pub(crate) type StandardFilter = generic::Reg<StandardFilterType, _StandardFilter>; | ||
| 85 | pub(crate) struct _StandardFilter; | ||
| 86 | impl generic::Readable for StandardFilter {} | ||
| 87 | impl generic::Writable for StandardFilter {} | ||
| 88 | |||
| 89 | pub(crate) mod extended_filter; | ||
| 90 | pub(crate) type ExtendedFilterType = [u32; 2]; | ||
| 91 | pub(crate) type ExtendedFilter = generic::Reg<ExtendedFilterType, _ExtendedFilter>; | ||
| 92 | pub(crate) struct _ExtendedFilter; | ||
| 93 | impl generic::Readable for ExtendedFilter {} | ||
| 94 | impl generic::Writable for ExtendedFilter {} | ||
| 95 | |||
| 96 | pub(crate) mod txevent_element; | ||
| 97 | pub(crate) type TxEventElementType = [u32; 2]; | ||
| 98 | pub(crate) type TxEventElement = generic::Reg<TxEventElementType, _TxEventElement>; | ||
| 99 | pub(crate) struct _TxEventElement; | ||
| 100 | impl generic::Readable for TxEventElement {} | ||
| 101 | impl generic::Writable for TxEventElement {} | ||
| 102 | |||
| 103 | pub(crate) mod rxfifo_element; | ||
| 104 | #[repr(C)] | ||
| 105 | pub(crate) struct RxFifoElement { | ||
| 106 | pub(crate) header: RxFifoElementHeader, | ||
| 107 | pub(crate) data: [RW<u32>; 16], | ||
| 108 | } | ||
| 109 | impl RxFifoElement { | ||
| 110 | pub(crate) fn reset(&mut self) { | ||
| 111 | self.header.reset(); | ||
| 112 | for byte in self.data.iter_mut() { | ||
| 113 | unsafe { byte.write(0) }; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | pub(crate) type RxFifoElementHeaderType = [u32; 2]; | ||
| 118 | pub(crate) type RxFifoElementHeader = generic::Reg<RxFifoElementHeaderType, _RxFifoElement>; | ||
| 119 | pub(crate) struct _RxFifoElement; | ||
| 120 | impl generic::Readable for RxFifoElementHeader {} | ||
| 121 | impl generic::Writable for RxFifoElementHeader {} | ||
| 122 | |||
| 123 | pub(crate) mod txbuffer_element; | ||
| 124 | #[repr(C)] | ||
| 125 | pub(crate) struct TxBufferElement { | ||
| 126 | pub(crate) header: TxBufferElementHeader, | ||
| 127 | pub(crate) data: [RW<u32>; 16], | ||
| 128 | } | ||
| 129 | impl TxBufferElement { | ||
| 130 | pub(crate) fn reset(&mut self) { | ||
| 131 | self.header.reset(); | ||
| 132 | for byte in self.data.iter_mut() { | ||
| 133 | unsafe { byte.write(0) }; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | pub(crate) type TxBufferElementHeader = generic::Reg<TxBufferElementHeaderType, _TxBufferElement>; | ||
| 138 | pub(crate) type TxBufferElementHeaderType = [u32; 2]; | ||
| 139 | pub(crate) struct _TxBufferElement; | ||
| 140 | impl generic::Readable for TxBufferElementHeader {} | ||
| 141 | impl generic::Writable for TxBufferElementHeader {} | ||
| 142 | |||
| 143 | /// FdCan Message RAM instance. | ||
| 144 | /// | ||
| 145 | /// # Safety | ||
| 146 | /// | ||
| 147 | /// It is only safe to implement this trait, when: | ||
| 148 | /// | ||
| 149 | /// * The implementing type has ownership of the Message RAM, preventing any | ||
| 150 | /// other accesses to the register block. | ||
| 151 | /// * `MSG_RAM` is a pointer to the Message RAM block and can be safely accessed | ||
| 152 | /// for as long as ownership or a borrow of the implementing type is present. | ||
| 153 | pub unsafe trait Instance { | ||
| 154 | const MSG_RAM: *mut RegisterBlock; | ||
| 155 | fn msg_ram(&self) -> &RegisterBlock { | ||
| 156 | unsafe { &*Self::MSG_RAM } | ||
| 157 | } | ||
| 158 | fn msg_ram_mut(&mut self) -> &mut RegisterBlock { | ||
| 159 | unsafe { &mut *Self::MSG_RAM } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | // Ensure the RegisterBlock is the same size as on pg 1957 of RM0440. | ||
| 164 | static_assertions::assert_eq_size!(Filters, [u32; 28 + 16]); | ||
| 165 | static_assertions::assert_eq_size!(Receive, [u32; 54]); | ||
| 166 | static_assertions::assert_eq_size!(Transmit, [u32; 6 + 54]); | ||
| 167 | static_assertions::assert_eq_size!( | ||
| 168 | RegisterBlock, | ||
| 169 | [u32; 28 /*Standard Filters*/ +16 /*Extended Filters*/ +54 /*RxFifo0*/ +54 /*RxFifo1*/ +6 /*TxEvent*/ +54 /*TxFifo */] | ||
| 170 | ); | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs b/embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs new file mode 100644 index 000000000..48fc3a091 --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/rxfifo_element.rs | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | #![allow(non_camel_case_types)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(unused)] | ||
| 6 | |||
| 7 | use super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R}; | ||
| 8 | use super::enums::{DataLength, FilterFrameMatch, FrameFormat}; | ||
| 9 | use super::generic; | ||
| 10 | |||
| 11 | #[doc = "Reader of register RxFifoElement"] | ||
| 12 | pub(crate) type R = generic::R<super::RxFifoElementHeaderType, super::RxFifoElementHeader>; | ||
| 13 | // #[doc = "Writer for register ExtendedFilter"] | ||
| 14 | // pub(crate) type W = generic::W<super::RxFifoElementHeaderType, super::RxFifoElementHeader>; | ||
| 15 | #[doc = "Register ExtendedFilter `reset()`'s"] | ||
| 16 | impl generic::ResetValue for super::RxFifoElementHeader { | ||
| 17 | type Type = super::RxFifoElementHeaderType; | ||
| 18 | #[inline(always)] | ||
| 19 | fn reset_value() -> Self::Type { | ||
| 20 | [0x0, 0x0] | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | #[doc = "Reader of field `RXTS`"] | ||
| 25 | pub(crate) type RXTS_R = generic::R<u16, u16>; | ||
| 26 | |||
| 27 | #[doc = "Reader of field `FIDX`"] | ||
| 28 | pub(crate) type FIDX_R = generic::R<u8, u8>; | ||
| 29 | |||
| 30 | pub(crate) struct _ANMF; | ||
| 31 | #[doc = "Reader of field `ANMF`"] | ||
| 32 | pub(crate) type ANMF_R = generic::R<bool, _ANMF>; | ||
| 33 | impl ANMF_R { | ||
| 34 | pub fn is_matching_frame(&self) -> bool { | ||
| 35 | self.bit_is_clear() | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl R { | ||
| 40 | #[doc = "Byte 0 - Bits 0:28 - ID"] | ||
| 41 | #[inline(always)] | ||
| 42 | pub fn id(&self) -> ID_R { | ||
| 43 | ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32) | ||
| 44 | } | ||
| 45 | #[doc = "Byte 0 - Bit 29 - RTR"] | ||
| 46 | #[inline(always)] | ||
| 47 | pub fn rtr(&self) -> RTR_R { | ||
| 48 | RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0) | ||
| 49 | } | ||
| 50 | #[doc = "Byte 0 - Bit 30 - XTD"] | ||
| 51 | #[inline(always)] | ||
| 52 | pub fn xtd(&self) -> XTD_R { | ||
| 53 | XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0) | ||
| 54 | } | ||
| 55 | #[doc = "Byte 0 - Bit 30 - ESI"] | ||
| 56 | #[inline(always)] | ||
| 57 | pub fn esi(&self) -> ESI_R { | ||
| 58 | ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0) | ||
| 59 | } | ||
| 60 | #[doc = "Byte 1 - Bits 0:15 - RXTS"] | ||
| 61 | #[inline(always)] | ||
| 62 | pub fn txts(&self) -> RXTS_R { | ||
| 63 | RXTS_R::new(((self.bits[1]) & 0xFFFF) as u16) | ||
| 64 | } | ||
| 65 | #[doc = "Byte 1 - Bits 16:19 - DLC"] | ||
| 66 | #[inline(always)] | ||
| 67 | pub fn dlc(&self) -> DLC_R { | ||
| 68 | DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8) | ||
| 69 | } | ||
| 70 | #[doc = "Byte 1 - Bits 20 - BRS"] | ||
| 71 | #[inline(always)] | ||
| 72 | pub fn brs(&self) -> BRS_R { | ||
| 73 | BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0) | ||
| 74 | } | ||
| 75 | #[doc = "Byte 1 - Bits 20 - FDF"] | ||
| 76 | #[inline(always)] | ||
| 77 | pub fn fdf(&self) -> FDF_R { | ||
| 78 | FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0) | ||
| 79 | } | ||
| 80 | #[doc = "Byte 1 - Bits 24:30 - FIDX"] | ||
| 81 | #[inline(always)] | ||
| 82 | pub fn fidx(&self) -> FIDX_R { | ||
| 83 | FIDX_R::new(((self.bits[1] >> 24) & 0xFF) as u8) | ||
| 84 | } | ||
| 85 | #[doc = "Byte 1 - Bits 31 - ANMF"] | ||
| 86 | #[inline(always)] | ||
| 87 | pub fn anmf(&self) -> ANMF_R { | ||
| 88 | ANMF_R::new(((self.bits[1] >> 31) & 0x01) != 0) | ||
| 89 | } | ||
| 90 | pub fn to_data_length(&self) -> DataLength { | ||
| 91 | let dlc = self.dlc().bits(); | ||
| 92 | let ff = self.fdf().frame_format(); | ||
| 93 | let len = if ff == FrameFormat::Fdcan { | ||
| 94 | // See RM0433 Rev 7 Table 475. DLC coding | ||
| 95 | match dlc { | ||
| 96 | 0..=8 => dlc, | ||
| 97 | 9 => 12, | ||
| 98 | 10 => 16, | ||
| 99 | 11 => 20, | ||
| 100 | 12 => 24, | ||
| 101 | 13 => 32, | ||
| 102 | 14 => 48, | ||
| 103 | 15 => 64, | ||
| 104 | _ => panic!("DLC > 15"), | ||
| 105 | } | ||
| 106 | } else { | ||
| 107 | match dlc { | ||
| 108 | 0..=8 => dlc, | ||
| 109 | 9..=15 => 8, | ||
| 110 | _ => panic!("DLC > 15"), | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | DataLength::new(len, ff) | ||
| 114 | } | ||
| 115 | pub fn to_filter_match(&self) -> FilterFrameMatch { | ||
| 116 | if self.anmf().is_matching_frame() { | ||
| 117 | FilterFrameMatch::DidMatch(self.fidx().bits()) | ||
| 118 | } else { | ||
| 119 | FilterFrameMatch::DidNotMatch | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/standard_filter.rs b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs new file mode 100644 index 000000000..3a3bbcf12 --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/standard_filter.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | #![allow(non_camel_case_types)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(unused)] | ||
| 6 | |||
| 7 | use super::common::{ESFEC_R, ESFT_R}; | ||
| 8 | use super::enums::{FilterElementConfig, FilterType}; | ||
| 9 | use super::generic; | ||
| 10 | |||
| 11 | #[doc = "Reader of register StandardFilter"] | ||
| 12 | pub(crate) type R = generic::R<super::StandardFilterType, super::StandardFilter>; | ||
| 13 | #[doc = "Writer for register StandardFilter"] | ||
| 14 | pub(crate) type W = generic::W<super::StandardFilterType, super::StandardFilter>; | ||
| 15 | #[doc = "Register StandardFilter `reset()`'s with value 0xC0000"] | ||
| 16 | impl generic::ResetValue for super::StandardFilter { | ||
| 17 | type Type = super::StandardFilterType; | ||
| 18 | #[inline(always)] | ||
| 19 | fn reset_value() -> Self::Type { | ||
| 20 | // Sets filter element to Disabled | ||
| 21 | 0xC000 | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | #[doc = "Reader of field `SFID2`"] | ||
| 26 | pub(crate) type SFID2_R = generic::R<u16, u16>; | ||
| 27 | #[doc = "Write proxy for field `SFID2`"] | ||
| 28 | pub(crate) struct SFID2_W<'a> { | ||
| 29 | w: &'a mut W, | ||
| 30 | } | ||
| 31 | impl<'a> SFID2_W<'a> { | ||
| 32 | #[doc = r"Writes raw bits to the field"] | ||
| 33 | #[inline(always)] | ||
| 34 | pub unsafe fn bits(self, value: u16) -> &'a mut W { | ||
| 35 | self.w.bits = (self.w.bits & !(0x07ff)) | ((value as u32) & 0x07ff); | ||
| 36 | self.w | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[doc = "Reader of field `SFID1`"] | ||
| 41 | pub(crate) type SFID1_R = generic::R<u16, u16>; | ||
| 42 | #[doc = "Write proxy for field `SFID1`"] | ||
| 43 | pub(crate) struct SFID1_W<'a> { | ||
| 44 | w: &'a mut W, | ||
| 45 | } | ||
| 46 | impl<'a> SFID1_W<'a> { | ||
| 47 | #[doc = r"Writes raw bits to the field"] | ||
| 48 | #[inline(always)] | ||
| 49 | pub unsafe fn bits(self, value: u16) -> &'a mut W { | ||
| 50 | self.w.bits = (self.w.bits & !(0x07ff << 16)) | (((value as u32) & 0x07ff) << 16); | ||
| 51 | self.w | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | #[doc = "Write proxy for field `SFEC`"] | ||
| 56 | pub(crate) struct SFEC_W<'a> { | ||
| 57 | w: &'a mut W, | ||
| 58 | } | ||
| 59 | impl<'a> SFEC_W<'a> { | ||
| 60 | #[doc = r"Writes raw bits to the field"] | ||
| 61 | #[inline(always)] | ||
| 62 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 63 | self.w.bits = (self.w.bits & !(0x07 << 27)) | (((value as u32) & 0x07) << 27); | ||
| 64 | self.w | ||
| 65 | } | ||
| 66 | #[doc = r"Sets the field according to FilterElementConfig"] | ||
| 67 | #[inline(always)] | ||
| 68 | pub fn set_filter_element_config(self, fec: FilterElementConfig) -> &'a mut W { | ||
| 69 | //SAFETY: FilterElementConfig only be valid options | ||
| 70 | unsafe { self.bits(fec as u8) } | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | #[doc = "Write proxy for field `SFT`"] | ||
| 75 | pub(crate) struct SFT_W<'a> { | ||
| 76 | w: &'a mut W, | ||
| 77 | } | ||
| 78 | impl<'a> SFT_W<'a> { | ||
| 79 | #[doc = r"Sets the field according the FilterType"] | ||
| 80 | #[inline(always)] | ||
| 81 | pub fn set_filter_type(self, filter: FilterType) -> &'a mut W { | ||
| 82 | //SAFETY: FilterType only be valid options | ||
| 83 | unsafe { self.bits(filter as u8) } | ||
| 84 | } | ||
| 85 | #[doc = r"Writes raw bits to the field"] | ||
| 86 | #[inline(always)] | ||
| 87 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 88 | self.w.bits = (self.w.bits & !(0x03 << 30)) | (((value as u32) & 0x03) << 30); | ||
| 89 | self.w | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | impl R { | ||
| 94 | #[doc = "Bits 0:10 - SFID2"] | ||
| 95 | #[inline(always)] | ||
| 96 | pub fn sfid2(&self) -> SFID2_R { | ||
| 97 | SFID2_R::new((self.bits & 0x07ff) as u16) | ||
| 98 | } | ||
| 99 | #[doc = "Bits 16:26 - SFID1"] | ||
| 100 | #[inline(always)] | ||
| 101 | pub fn sfid1(&self) -> SFID1_R { | ||
| 102 | SFID1_R::new(((self.bits >> 16) & 0x07ff) as u16) | ||
| 103 | } | ||
| 104 | #[doc = "Bits 27:29 - SFEC"] | ||
| 105 | #[inline(always)] | ||
| 106 | pub fn sfec(&self) -> ESFEC_R { | ||
| 107 | ESFEC_R::new(((self.bits >> 27) & 0x07) as u8) | ||
| 108 | } | ||
| 109 | #[doc = "Bits 30:31 - SFT"] | ||
| 110 | #[inline(always)] | ||
| 111 | pub fn sft(&self) -> ESFT_R { | ||
| 112 | ESFT_R::new(((self.bits >> 30) & 0x03) as u8) | ||
| 113 | } | ||
| 114 | } | ||
| 115 | impl W { | ||
| 116 | #[doc = "Bits 0:10 - SFID2"] | ||
| 117 | #[inline(always)] | ||
| 118 | pub fn sfid2(&mut self) -> SFID2_W { | ||
| 119 | SFID2_W { w: self } | ||
| 120 | } | ||
| 121 | #[doc = "Bits 16:26 - SFID1"] | ||
| 122 | #[inline(always)] | ||
| 123 | pub fn sfid1(&mut self) -> SFID1_W { | ||
| 124 | SFID1_W { w: self } | ||
| 125 | } | ||
| 126 | #[doc = "Bits 27:29 - SFEC"] | ||
| 127 | #[inline(always)] | ||
| 128 | pub fn sfec(&mut self) -> SFEC_W { | ||
| 129 | SFEC_W { w: self } | ||
| 130 | } | ||
| 131 | #[doc = "Bits 30:31 - SFT"] | ||
| 132 | #[inline(always)] | ||
| 133 | pub fn sft(&mut self) -> SFT_W { | ||
| 134 | SFT_W { w: self } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs new file mode 100644 index 000000000..455406a1c --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/txbuffer_element.rs | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | #![allow(non_camel_case_types)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(unused)] | ||
| 6 | |||
| 7 | use super::common::{BRS_R, DLC_R, ESI_R, FDF_R, ID_R, RTR_R, XTD_R}; | ||
| 8 | use super::enums::{ | ||
| 9 | BitRateSwitching, DataLength, ErrorStateIndicator, Event, EventControl, FrameFormat, IdType, | ||
| 10 | RemoteTransmissionRequest, | ||
| 11 | }; | ||
| 12 | use super::generic; | ||
| 13 | |||
| 14 | #[doc = "Reader of register TxBufferElement"] | ||
| 15 | pub(crate) type R = generic::R<super::TxBufferElementHeaderType, super::TxBufferElementHeader>; | ||
| 16 | #[doc = "Writer for register TxBufferElement"] | ||
| 17 | pub(crate) type W = generic::W<super::TxBufferElementHeaderType, super::TxBufferElementHeader>; | ||
| 18 | impl generic::ResetValue for super::TxBufferElementHeader { | ||
| 19 | type Type = super::TxBufferElementHeaderType; | ||
| 20 | |||
| 21 | #[allow(dead_code)] | ||
| 22 | #[inline(always)] | ||
| 23 | fn reset_value() -> Self::Type { | ||
| 24 | [0; 2] | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | #[doc = "Write proxy for field `ESI`"] | ||
| 29 | pub(crate) struct ESI_W<'a> { | ||
| 30 | w: &'a mut W, | ||
| 31 | } | ||
| 32 | impl<'a> ESI_W<'a> { | ||
| 33 | #[doc = r"Writes `variant` to the field"] | ||
| 34 | #[inline(always)] | ||
| 35 | #[allow(dead_code)] | ||
| 36 | pub fn set_error_indicator(self, esi: ErrorStateIndicator) -> &'a mut W { | ||
| 37 | self.bit(esi as u8 != 0) | ||
| 38 | } | ||
| 39 | |||
| 40 | #[doc = r"Sets the field bit"] | ||
| 41 | #[inline(always)] | ||
| 42 | #[allow(dead_code)] | ||
| 43 | pub fn set_bit(self) -> &'a mut W { | ||
| 44 | self.bit(true) | ||
| 45 | } | ||
| 46 | #[doc = r"Clears the field bit"] | ||
| 47 | #[inline(always)] | ||
| 48 | #[allow(dead_code)] | ||
| 49 | pub fn clear_bit(self) -> &'a mut W { | ||
| 50 | self.bit(false) | ||
| 51 | } | ||
| 52 | #[doc = r"Writes raw bits to the field"] | ||
| 53 | #[inline(always)] | ||
| 54 | #[allow(dead_code)] | ||
| 55 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 56 | self.w.bits[0] = (self.w.bits[0] & !(0x01 << 31)) | (((value as u32) & 0x01) << 31); | ||
| 57 | self.w | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | #[doc = "Write proxy for field `XTD`"] | ||
| 62 | pub(crate) struct XTD_W<'a> { | ||
| 63 | w: &'a mut W, | ||
| 64 | } | ||
| 65 | impl<'a> XTD_W<'a> { | ||
| 66 | #[doc = r"Writes `variant` to the field"] | ||
| 67 | #[inline(always)] | ||
| 68 | #[allow(dead_code)] | ||
| 69 | pub fn set_id_type(self, idt: IdType) -> &'a mut W { | ||
| 70 | self.bit(idt as u8 != 0) | ||
| 71 | } | ||
| 72 | |||
| 73 | #[doc = r"Sets the field bit"] | ||
| 74 | #[inline(always)] | ||
| 75 | #[allow(dead_code)] | ||
| 76 | pub fn set_bit(self) -> &'a mut W { | ||
| 77 | self.bit(true) | ||
| 78 | } | ||
| 79 | #[doc = r"Clears the field bit"] | ||
| 80 | #[inline(always)] | ||
| 81 | #[allow(dead_code)] | ||
| 82 | pub fn clear_bit(self) -> &'a mut W { | ||
| 83 | self.bit(false) | ||
| 84 | } | ||
| 85 | #[doc = r"Writes raw bits to the field"] | ||
| 86 | #[inline(always)] | ||
| 87 | #[allow(dead_code)] | ||
| 88 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 89 | self.w.bits[0] = (self.w.bits[0] & !(0x01 << 30)) | (((value as u32) & 0x01) << 30); | ||
| 90 | self.w | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #[doc = "Write proxy for field `RTR`"] | ||
| 95 | pub(crate) struct RTR_W<'a> { | ||
| 96 | w: &'a mut W, | ||
| 97 | } | ||
| 98 | impl<'a> RTR_W<'a> { | ||
| 99 | #[doc = r"Writes `variant` to the field"] | ||
| 100 | #[inline(always)] | ||
| 101 | #[allow(dead_code)] | ||
| 102 | pub fn set_rtr(self, rtr: RemoteTransmissionRequest) -> &'a mut W { | ||
| 103 | self.bit(rtr as u8 != 0) | ||
| 104 | } | ||
| 105 | |||
| 106 | #[doc = r"Sets the field bit"] | ||
| 107 | #[inline(always)] | ||
| 108 | #[allow(dead_code)] | ||
| 109 | pub fn set_bit(self) -> &'a mut W { | ||
| 110 | self.bit(true) | ||
| 111 | } | ||
| 112 | #[doc = r"Clears the field bit"] | ||
| 113 | #[inline(always)] | ||
| 114 | #[allow(dead_code)] | ||
| 115 | pub fn clear_bit(self) -> &'a mut W { | ||
| 116 | self.bit(false) | ||
| 117 | } | ||
| 118 | #[doc = r"Writes raw bits to the field"] | ||
| 119 | #[inline(always)] | ||
| 120 | #[allow(dead_code)] | ||
| 121 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 122 | self.w.bits[0] = (self.w.bits[0] & !(0x01 << 29)) | (((value as u32) & 0x01) << 29); | ||
| 123 | self.w | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | #[doc = "Write proxy for field `ID`"] | ||
| 128 | pub(crate) struct ID_W<'a> { | ||
| 129 | w: &'a mut W, | ||
| 130 | } | ||
| 131 | impl<'a> ID_W<'a> { | ||
| 132 | #[doc = r"Writes raw bits to the field"] | ||
| 133 | #[inline(always)] | ||
| 134 | #[allow(dead_code)] | ||
| 135 | pub unsafe fn bits(self, value: u32) -> &'a mut W { | ||
| 136 | self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF); | ||
| 137 | self.w | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | #[doc = "Write proxy for field `DLC`"] | ||
| 142 | pub(crate) struct DLC_W<'a> { | ||
| 143 | w: &'a mut W, | ||
| 144 | } | ||
| 145 | impl<'a> DLC_W<'a> { | ||
| 146 | #[doc = r"Writes raw bits to the field"] | ||
| 147 | #[inline(always)] | ||
| 148 | #[allow(dead_code)] | ||
| 149 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 150 | self.w.bits[1] = (self.w.bits[1] & !(0x0F << 16)) | (((value as u32) & 0x0F) << 16); | ||
| 151 | self.w | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | #[doc = "Write proxy for field `BRS`"] | ||
| 156 | pub(crate) struct BRS_W<'a> { | ||
| 157 | w: &'a mut W, | ||
| 158 | } | ||
| 159 | impl<'a> BRS_W<'a> { | ||
| 160 | #[doc = r"Writes `variant` to the field"] | ||
| 161 | #[inline(always)] | ||
| 162 | #[allow(dead_code)] | ||
| 163 | pub fn set_brs(self, brs: BitRateSwitching) -> &'a mut W { | ||
| 164 | self.bit(brs as u8 != 0) | ||
| 165 | } | ||
| 166 | |||
| 167 | #[doc = r"Sets the field bit"] | ||
| 168 | #[inline(always)] | ||
| 169 | #[allow(dead_code)] | ||
| 170 | pub fn set_bit(self) -> &'a mut W { | ||
| 171 | self.bit(true) | ||
| 172 | } | ||
| 173 | #[doc = r"Clears the field bit"] | ||
| 174 | #[inline(always)] | ||
| 175 | #[allow(dead_code)] | ||
| 176 | pub fn clear_bit(self) -> &'a mut W { | ||
| 177 | self.bit(false) | ||
| 178 | } | ||
| 179 | #[doc = r"Writes raw bits to the field"] | ||
| 180 | #[inline(always)] | ||
| 181 | #[allow(dead_code)] | ||
| 182 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 183 | self.w.bits[1] = (self.w.bits[1] & !(0x01 << 20)) | (((value as u32) & 0x01) << 20); | ||
| 184 | self.w | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | #[doc = "Write proxy for field `FDF`"] | ||
| 189 | pub(crate) struct FDF_W<'a> { | ||
| 190 | w: &'a mut W, | ||
| 191 | } | ||
| 192 | impl<'a> FDF_W<'a> { | ||
| 193 | #[doc = r"Writes `variant` to the field"] | ||
| 194 | #[inline(always)] | ||
| 195 | #[allow(dead_code)] | ||
| 196 | pub fn set_format(self, fdf: FrameFormat) -> &'a mut W { | ||
| 197 | self.bit(fdf as u8 != 0) | ||
| 198 | } | ||
| 199 | |||
| 200 | #[doc = r"Sets the field bit"] | ||
| 201 | #[inline(always)] | ||
| 202 | #[allow(dead_code)] | ||
| 203 | pub fn set_bit(self) -> &'a mut W { | ||
| 204 | self.bit(true) | ||
| 205 | } | ||
| 206 | #[doc = r"Clears the field bit"] | ||
| 207 | #[inline(always)] | ||
| 208 | #[allow(dead_code)] | ||
| 209 | pub fn clear_bit(self) -> &'a mut W { | ||
| 210 | self.bit(false) | ||
| 211 | } | ||
| 212 | #[doc = r"Writes raw bits to the field"] | ||
| 213 | #[inline(always)] | ||
| 214 | #[allow(dead_code)] | ||
| 215 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 216 | self.w.bits[1] = (self.w.bits[1] & !(0x01 << 21)) | (((value as u32) & 0x01) << 21); | ||
| 217 | self.w | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | #[doc = "Reader of field `EFC`"] | ||
| 222 | pub(crate) type EFC_R = generic::R<bool, EventControl>; | ||
| 223 | impl EFC_R { | ||
| 224 | pub fn to_event_control(&self) -> EventControl { | ||
| 225 | match self.bit() { | ||
| 226 | false => EventControl::DoNotStore, | ||
| 227 | true => EventControl::Store, | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | ||
| 231 | #[doc = "Write proxy for field `EFC`"] | ||
| 232 | pub(crate) struct EFC_W<'a> { | ||
| 233 | w: &'a mut W, | ||
| 234 | } | ||
| 235 | impl<'a> EFC_W<'a> { | ||
| 236 | #[doc = r"Writes `variant` to the field"] | ||
| 237 | #[inline(always)] | ||
| 238 | #[allow(dead_code)] | ||
| 239 | pub fn set_event_control(self, efc: EventControl) -> &'a mut W { | ||
| 240 | self.bit(match efc { | ||
| 241 | EventControl::DoNotStore => false, | ||
| 242 | EventControl::Store => true, | ||
| 243 | }) | ||
| 244 | } | ||
| 245 | |||
| 246 | #[doc = r"Sets the field bit"] | ||
| 247 | #[inline(always)] | ||
| 248 | #[allow(dead_code)] | ||
| 249 | pub fn set_bit(self) -> &'a mut W { | ||
| 250 | self.bit(true) | ||
| 251 | } | ||
| 252 | #[doc = r"Clears the field bit"] | ||
| 253 | #[inline(always)] | ||
| 254 | #[allow(dead_code)] | ||
| 255 | pub fn clear_bit(self) -> &'a mut W { | ||
| 256 | self.bit(false) | ||
| 257 | } | ||
| 258 | #[doc = r"Writes raw bits to the field"] | ||
| 259 | #[inline(always)] | ||
| 260 | #[allow(dead_code)] | ||
| 261 | pub fn bit(self, value: bool) -> &'a mut W { | ||
| 262 | self.w.bits[1] = (self.w.bits[1] & !(0x01 << 23)) | (((value as u32) & 0x01) << 23); | ||
| 263 | self.w | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | struct Marker(u8); | ||
| 268 | impl From<Event> for Marker { | ||
| 269 | fn from(e: Event) -> Marker { | ||
| 270 | match e { | ||
| 271 | Event::NoEvent => Marker(0), | ||
| 272 | Event::Event(mm) => Marker(mm), | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | #[doc = "Reader of field `MM`"] | ||
| 278 | pub(crate) type MM_R = generic::R<u8, u8>; | ||
| 279 | #[doc = "Write proxy for field `MM`"] | ||
| 280 | pub(crate) struct MM_W<'a> { | ||
| 281 | w: &'a mut W, | ||
| 282 | } | ||
| 283 | impl<'a> MM_W<'a> { | ||
| 284 | #[doc = r"Writes raw bits to the field"] | ||
| 285 | #[inline(always)] | ||
| 286 | pub unsafe fn bits(self, value: u8) -> &'a mut W { | ||
| 287 | self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24); | ||
| 288 | self.w | ||
| 289 | } | ||
| 290 | |||
| 291 | fn set_message_marker(self, mm: Marker) -> &'a mut W { | ||
| 292 | unsafe { self.bits(mm.0) } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | impl R { | ||
| 297 | #[doc = "Byte 0 - Bits 0:28 - ID"] | ||
| 298 | #[inline(always)] | ||
| 299 | pub fn id(&self) -> ID_R { | ||
| 300 | ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32) | ||
| 301 | } | ||
| 302 | #[doc = "Byte 0 - Bit 29 - RTR"] | ||
| 303 | #[inline(always)] | ||
| 304 | pub fn rtr(&self) -> RTR_R { | ||
| 305 | RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0) | ||
| 306 | } | ||
| 307 | #[doc = "Byte 0 - Bit 30 - XTD"] | ||
| 308 | #[inline(always)] | ||
| 309 | pub fn xtd(&self) -> XTD_R { | ||
| 310 | XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0) | ||
| 311 | } | ||
| 312 | #[doc = "Byte 0 - Bit 30 - ESI"] | ||
| 313 | #[inline(always)] | ||
| 314 | pub fn esi(&self) -> ESI_R { | ||
| 315 | ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0) | ||
| 316 | } | ||
| 317 | #[doc = "Byte 1 - Bits 16:19 - DLC"] | ||
| 318 | #[inline(always)] | ||
| 319 | pub fn dlc(&self) -> DLC_R { | ||
| 320 | DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8) | ||
| 321 | } | ||
| 322 | #[doc = "Byte 1 - Bits 20 - BRS"] | ||
| 323 | #[inline(always)] | ||
| 324 | pub fn brs(&self) -> BRS_R { | ||
| 325 | BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0) | ||
| 326 | } | ||
| 327 | #[doc = "Byte 1 - Bits 20 - FDF"] | ||
| 328 | #[inline(always)] | ||
| 329 | pub fn fdf(&self) -> FDF_R { | ||
| 330 | FDF_R::new(((self.bits[1] >> 21) & 0x01) != 0) | ||
| 331 | } | ||
| 332 | #[doc = "Byte 1 - Bits 23 - EFC"] | ||
| 333 | #[inline(always)] | ||
| 334 | pub fn efc(&self) -> EFC_R { | ||
| 335 | EFC_R::new(((self.bits[1] >> 23) & 0x01) != 0) | ||
| 336 | } | ||
| 337 | #[doc = "Byte 1 - Bits 24:31 - MM"] | ||
| 338 | #[inline(always)] | ||
| 339 | pub fn mm(&self) -> MM_R { | ||
| 340 | MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8) | ||
| 341 | } | ||
| 342 | pub fn to_data_length(&self) -> DataLength { | ||
| 343 | let dlc = self.dlc().bits(); | ||
| 344 | let ff = self.fdf().frame_format(); | ||
| 345 | let len = if ff == FrameFormat::Fdcan { | ||
| 346 | // See RM0433 Rev 7 Table 475. DLC coding | ||
| 347 | match dlc { | ||
| 348 | 0..=8 => dlc, | ||
| 349 | 9 => 12, | ||
| 350 | 10 => 16, | ||
| 351 | 11 => 20, | ||
| 352 | 12 => 24, | ||
| 353 | 13 => 32, | ||
| 354 | 14 => 48, | ||
| 355 | 15 => 64, | ||
| 356 | _ => panic!("DLC > 15"), | ||
| 357 | } | ||
| 358 | } else { | ||
| 359 | match dlc { | ||
| 360 | 0..=8 => dlc, | ||
| 361 | 9..=15 => 8, | ||
| 362 | _ => panic!("DLC > 15"), | ||
| 363 | } | ||
| 364 | }; | ||
| 365 | DataLength::new(len, ff) | ||
| 366 | } | ||
| 367 | pub fn to_event(&self) -> Event { | ||
| 368 | let mm = self.mm().bits(); | ||
| 369 | let efc = self.efc().to_event_control(); | ||
| 370 | match efc { | ||
| 371 | EventControl::DoNotStore => Event::NoEvent, | ||
| 372 | EventControl::Store => Event::Event(mm), | ||
| 373 | } | ||
| 374 | } | ||
| 375 | } | ||
| 376 | impl W { | ||
| 377 | #[doc = "Byte 0 - Bits 0:28 - ID"] | ||
| 378 | #[inline(always)] | ||
| 379 | pub fn id(&mut self) -> ID_W { | ||
| 380 | ID_W { w: self } | ||
| 381 | } | ||
| 382 | #[doc = "Byte 0 - Bit 29 - RTR"] | ||
| 383 | #[inline(always)] | ||
| 384 | pub fn rtr(&mut self) -> RTR_W { | ||
| 385 | RTR_W { w: self } | ||
| 386 | } | ||
| 387 | #[doc = "Byte 0 - Bit 30 - XTD"] | ||
| 388 | #[inline(always)] | ||
| 389 | pub fn xtd(&mut self) -> XTD_W { | ||
| 390 | XTD_W { w: self } | ||
| 391 | } | ||
| 392 | #[doc = "Byte 0 - Bit 31 - ESI"] | ||
| 393 | #[inline(always)] | ||
| 394 | pub fn esi(&mut self) -> ESI_W { | ||
| 395 | ESI_W { w: self } | ||
| 396 | } | ||
| 397 | #[doc = "Byte 1 - Bit 16:19 - DLC"] | ||
| 398 | #[inline(always)] | ||
| 399 | pub fn dlc(&mut self) -> DLC_W { | ||
| 400 | DLC_W { w: self } | ||
| 401 | } | ||
| 402 | #[doc = "Byte 1 - Bit 20 - BRS"] | ||
| 403 | #[inline(always)] | ||
| 404 | pub fn brs(&mut self) -> BRS_W { | ||
| 405 | BRS_W { w: self } | ||
| 406 | } | ||
| 407 | #[doc = "Byte 1 - Bit 21 - FDF"] | ||
| 408 | #[inline(always)] | ||
| 409 | pub fn fdf(&mut self) -> FDF_W { | ||
| 410 | FDF_W { w: self } | ||
| 411 | } | ||
| 412 | #[doc = "Byte 1 - Bit 23 - EFC"] | ||
| 413 | #[inline(always)] | ||
| 414 | pub fn efc(&mut self) -> EFC_W { | ||
| 415 | EFC_W { w: self } | ||
| 416 | } | ||
| 417 | #[doc = "Byte 1 - Bit 24:31 - MM"] | ||
| 418 | #[inline(always)] | ||
| 419 | pub fn mm(&mut self) -> MM_W { | ||
| 420 | MM_W { w: self } | ||
| 421 | } | ||
| 422 | #[doc = "Convenience function for setting the data length and frame format"] | ||
| 423 | #[inline(always)] | ||
| 424 | pub fn set_len(&mut self, dl: impl Into<DataLength>) -> &mut Self { | ||
| 425 | let dl: DataLength = dl.into(); | ||
| 426 | self.fdf().set_format(dl.into()); | ||
| 427 | unsafe { self.dlc().bits(dl.dlc()) } | ||
| 428 | } | ||
| 429 | pub fn set_event(&mut self, event: Event) -> &mut Self { | ||
| 430 | self.mm().set_message_marker(event.into()); | ||
| 431 | self.efc().set_event_control(event.into()) | ||
| 432 | } | ||
| 433 | } | ||
diff --git a/embassy-stm32/src/can/fd/message_ram/txevent_element.rs b/embassy-stm32/src/can/fd/message_ram/txevent_element.rs new file mode 100644 index 000000000..817a4449f --- /dev/null +++ b/embassy-stm32/src/can/fd/message_ram/txevent_element.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | #![allow(non_camel_case_types)] | ||
| 4 | #![allow(non_snake_case)] | ||
| 5 | #![allow(unused)] | ||
| 6 | |||
| 7 | use super::common::{BRS_R, DLC_R, ESI_R, RTR_R, XTD_R}; | ||
| 8 | use super::generic; | ||
| 9 | |||
| 10 | #[doc = "Reader of register TxEventElement"] | ||
| 11 | pub(crate) type R = generic::R<super::TxEventElementType, super::TxEventElement>; | ||
| 12 | // #[doc = "Writer for register TxEventElement"] | ||
| 13 | // pub(crate) type W = generic::W<super::TxEventElementType, super::TxEventElement>; | ||
| 14 | #[doc = "Register TxEventElement `reset()`'s"] | ||
| 15 | impl generic::ResetValue for super::TxEventElement { | ||
| 16 | type Type = super::TxEventElementType; | ||
| 17 | #[inline(always)] | ||
| 18 | fn reset_value() -> Self::Type { | ||
| 19 | [0, 0] | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | #[doc = "Reader of field `ID`"] | ||
| 24 | pub(crate) type ID_R = generic::R<u32, u32>; | ||
| 25 | |||
| 26 | #[doc = "Reader of field `TXTS`"] | ||
| 27 | pub(crate) type TXTS_R = generic::R<u16, u16>; | ||
| 28 | |||
| 29 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 30 | pub(crate) enum DataLengthFormat { | ||
| 31 | StandardLength = 0, | ||
| 32 | FDCANLength = 1, | ||
| 33 | } | ||
| 34 | impl From<DataLengthFormat> for bool { | ||
| 35 | #[inline(always)] | ||
| 36 | fn from(dlf: DataLengthFormat) -> Self { | ||
| 37 | dlf as u8 != 0 | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | #[doc = "Reader of field `EDL`"] | ||
| 42 | pub(crate) type EDL_R = generic::R<bool, DataLengthFormat>; | ||
| 43 | impl EDL_R { | ||
| 44 | pub fn data_length_format(&self) -> DataLengthFormat { | ||
| 45 | match self.bits() { | ||
| 46 | false => DataLengthFormat::StandardLength, | ||
| 47 | true => DataLengthFormat::FDCANLength, | ||
| 48 | } | ||
| 49 | } | ||
| 50 | pub fn is_standard_length(&self) -> bool { | ||
| 51 | *self == DataLengthFormat::StandardLength | ||
| 52 | } | ||
| 53 | pub fn is_fdcan_length(&self) -> bool { | ||
| 54 | *self == DataLengthFormat::FDCANLength | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | #[derive(Clone, Copy, Debug, PartialEq)] | ||
| 59 | pub(crate) enum EventType { | ||
| 60 | //_Reserved = 0b00, | ||
| 61 | TxEvent = 0b01, | ||
| 62 | TxDespiteAbort = 0b10, | ||
| 63 | //_Reserved = 0b10, | ||
| 64 | } | ||
| 65 | |||
| 66 | #[doc = "Reader of field `EFC`"] | ||
| 67 | pub(crate) type EFC_R = generic::R<u8, EventType>; | ||
| 68 | impl EFC_R { | ||
| 69 | pub fn event_type(&self) -> EventType { | ||
| 70 | match self.bits() { | ||
| 71 | 0b01 => EventType::TxEvent, | ||
| 72 | 0b10 => EventType::TxDespiteAbort, | ||
| 73 | _ => unimplemented!(), | ||
| 74 | } | ||
| 75 | } | ||
| 76 | pub fn is_tx_event(&self) -> bool { | ||
| 77 | self.event_type() == EventType::TxEvent | ||
| 78 | } | ||
| 79 | pub fn is_despite_abort(&self) -> bool { | ||
| 80 | self.event_type() == EventType::TxDespiteAbort | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | #[doc = "Reader of field `MM`"] | ||
| 85 | pub(crate) type MM_R = generic::R<u8, u8>; | ||
| 86 | |||
| 87 | impl R { | ||
| 88 | #[doc = "Byte 0 - Bits 0:28 - ID"] | ||
| 89 | #[inline(always)] | ||
| 90 | pub fn id(&self) -> ID_R { | ||
| 91 | ID_R::new(((self.bits[0]) & 0x1FFFFFFF) as u32) | ||
| 92 | } | ||
| 93 | #[doc = "Byte 0 - Bit 29 - RTR"] | ||
| 94 | #[inline(always)] | ||
| 95 | pub fn rtr(&self) -> RTR_R { | ||
| 96 | RTR_R::new(((self.bits[0] >> 29) & 0x01) != 0) | ||
| 97 | } | ||
| 98 | #[doc = "Byte 0 - Bit 30 - XTD"] | ||
| 99 | #[inline(always)] | ||
| 100 | pub fn xtd(&self) -> XTD_R { | ||
| 101 | XTD_R::new(((self.bits[0] >> 30) & 0x01) != 0) | ||
| 102 | } | ||
| 103 | #[doc = "Byte 0 - Bit 30 - ESI"] | ||
| 104 | #[inline(always)] | ||
| 105 | pub fn esi(&self) -> ESI_R { | ||
| 106 | ESI_R::new(((self.bits[0] >> 31) & 0x01) != 0) | ||
| 107 | } | ||
| 108 | #[doc = "Byte 1 - Bits 0:15 - TXTS"] | ||
| 109 | #[inline(always)] | ||
| 110 | pub fn txts(&self) -> TXTS_R { | ||
| 111 | TXTS_R::new(((self.bits[1]) & 0xFFFF) as u16) | ||
| 112 | } | ||
| 113 | #[doc = "Byte 1 - Bits 16:19 - DLC"] | ||
| 114 | #[inline(always)] | ||
| 115 | pub fn dlc(&self) -> DLC_R { | ||
| 116 | DLC_R::new(((self.bits[1] >> 16) & 0x0F) as u8) | ||
| 117 | } | ||
| 118 | #[doc = "Byte 1 - Bits 20 - BRS"] | ||
| 119 | #[inline(always)] | ||
| 120 | pub fn brs(&self) -> BRS_R { | ||
| 121 | BRS_R::new(((self.bits[1] >> 20) & 0x01) != 0) | ||
| 122 | } | ||
| 123 | #[doc = "Byte 1 - Bits 21 - EDL"] | ||
| 124 | #[inline(always)] | ||
| 125 | pub fn edl(&self) -> EDL_R { | ||
| 126 | EDL_R::new(((self.bits[1] >> 21) & 0x01) != 0) | ||
| 127 | } | ||
| 128 | #[doc = "Byte 1 - Bits 22:23 - EFC"] | ||
| 129 | #[inline(always)] | ||
| 130 | pub fn efc(&self) -> EFC_R { | ||
| 131 | EFC_R::new(((self.bits[1] >> 22) & 0x03) as u8) | ||
| 132 | } | ||
| 133 | #[doc = "Byte 1 - Bits 24:31 - MM"] | ||
| 134 | #[inline(always)] | ||
| 135 | pub fn mm(&self) -> MM_R { | ||
| 136 | MM_R::new(((self.bits[1] >> 24) & 0xFF) as u8) | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/embassy-stm32/src/can/fd/mod.rs b/embassy-stm32/src/can/fd/mod.rs new file mode 100644 index 000000000..271ca0b3c --- /dev/null +++ b/embassy-stm32/src/can/fd/mod.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | //! Module containing that which is specific to fdcan hardware variant | ||
| 2 | |||
| 3 | pub mod config; | ||
| 4 | pub mod filter; | ||
| 5 | pub(crate) mod message_ram; | ||
| 6 | pub(crate) mod peripheral; | ||
diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs new file mode 100644 index 000000000..0771d6fbb --- /dev/null +++ b/embassy-stm32/src/can/fd/peripheral.rs | |||
| @@ -0,0 +1,828 @@ | |||
| 1 | // Note: This file is copied and modified from fdcan crate by Richard Meadows | ||
| 2 | |||
| 3 | use core::convert::Infallible; | ||
| 4 | use core::slice; | ||
| 5 | |||
| 6 | use cfg_if::cfg_if; | ||
| 7 | |||
| 8 | use crate::can::enums::*; | ||
| 9 | use crate::can::fd::config::*; | ||
| 10 | use crate::can::fd::message_ram::enums::*; | ||
| 11 | use crate::can::fd::message_ram::{RegisterBlock, RxFifoElement, TxBufferElement}; | ||
| 12 | use crate::can::frame::*; | ||
| 13 | |||
| 14 | /// Loopback Mode | ||
| 15 | #[derive(Clone, Copy, Debug)] | ||
| 16 | enum LoopbackMode { | ||
| 17 | None, | ||
| 18 | Internal, | ||
| 19 | External, | ||
| 20 | } | ||
| 21 | |||
| 22 | pub struct Registers { | ||
| 23 | pub regs: &'static crate::pac::can::Fdcan, | ||
| 24 | pub msgram: &'static crate::pac::fdcanram::Fdcanram, | ||
| 25 | } | ||
| 26 | |||
| 27 | impl Registers { | ||
| 28 | fn tx_buffer_element(&self, bufidx: usize) -> &mut TxBufferElement { | ||
| 29 | &mut self.msg_ram_mut().transmit.tbsa[bufidx] | ||
| 30 | } | ||
| 31 | pub fn msg_ram_mut(&self) -> &mut RegisterBlock { | ||
| 32 | let ptr = self.msgram.as_ptr() as *mut RegisterBlock; | ||
| 33 | unsafe { &mut (*ptr) } | ||
| 34 | } | ||
| 35 | |||
| 36 | fn rx_fifo_element(&self, fifonr: usize, bufnum: usize) -> &mut RxFifoElement { | ||
| 37 | &mut self.msg_ram_mut().receive[fifonr].fxsa[bufnum] | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn read_classic(&self, fifonr: usize) -> Option<(ClassicFrame, u16)> { | ||
| 41 | // Fill level - do we have a msg? | ||
| 42 | if self.regs.rxfs(fifonr).read().ffl() < 1 { | ||
| 43 | return None; | ||
| 44 | } | ||
| 45 | |||
| 46 | let read_idx = self.regs.rxfs(fifonr).read().fgi(); | ||
| 47 | let mailbox = self.rx_fifo_element(fifonr, read_idx as usize); | ||
| 48 | |||
| 49 | let mut buffer: [u8; 8] = [0; 8]; | ||
| 50 | let maybe_header = extract_frame(mailbox, &mut buffer); | ||
| 51 | |||
| 52 | // Clear FIFO, reduces count and increments read buf | ||
| 53 | self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx)); | ||
| 54 | |||
| 55 | match maybe_header { | ||
| 56 | Some((header, ts)) => { | ||
| 57 | let data = ClassicData::new(&buffer[0..header.len() as usize]); | ||
| 58 | Some((ClassicFrame::new(header, data.unwrap()), ts)) | ||
| 59 | } | ||
| 60 | None => None, | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | pub fn read_fd(&self, fifonr: usize) -> Option<(FdFrame, u16)> { | ||
| 65 | // Fill level - do we have a msg? | ||
| 66 | if self.regs.rxfs(fifonr).read().ffl() < 1 { | ||
| 67 | return None; | ||
| 68 | } | ||
| 69 | |||
| 70 | let read_idx = self.regs.rxfs(fifonr).read().fgi(); | ||
| 71 | let mailbox = self.rx_fifo_element(fifonr, read_idx as usize); | ||
| 72 | |||
| 73 | let mut buffer: [u8; 64] = [0; 64]; | ||
| 74 | let maybe_header = extract_frame(mailbox, &mut buffer); | ||
| 75 | |||
| 76 | // Clear FIFO, reduces count and increments read buf | ||
| 77 | self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx)); | ||
| 78 | |||
| 79 | match maybe_header { | ||
| 80 | Some((header, ts)) => { | ||
| 81 | let data = FdData::new(&buffer[0..header.len() as usize]); | ||
| 82 | Some((FdFrame::new(header, data.unwrap()), ts)) | ||
| 83 | } | ||
| 84 | None => None, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) { | ||
| 89 | // Fill level - do we have a msg? | ||
| 90 | //if self.regs.rxfs(fifonr).read().ffl() < 1 { return None; } | ||
| 91 | |||
| 92 | //let read_idx = self.regs.rxfs(fifonr).read().fgi(); | ||
| 93 | |||
| 94 | let mailbox = self.tx_buffer_element(bufidx); | ||
| 95 | |||
| 96 | mailbox.reset(); | ||
| 97 | put_tx_header(mailbox, header); | ||
| 98 | put_tx_data(mailbox, &buffer[..header.len() as usize]); | ||
| 99 | |||
| 100 | // Set <idx as Mailbox> as ready to transmit | ||
| 101 | self.regs.txbar().modify(|w| w.set_ar(bufidx, true)); | ||
| 102 | } | ||
| 103 | |||
| 104 | fn reg_to_error(value: u8) -> Option<BusError> { | ||
| 105 | match value { | ||
| 106 | //0b000 => None, | ||
| 107 | 0b001 => Some(BusError::Stuff), | ||
| 108 | 0b010 => Some(BusError::Form), | ||
| 109 | 0b011 => Some(BusError::Acknowledge), | ||
| 110 | 0b100 => Some(BusError::BitRecessive), | ||
| 111 | 0b101 => Some(BusError::BitDominant), | ||
| 112 | 0b110 => Some(BusError::Crc), | ||
| 113 | //0b111 => Some(BusError::NoError), | ||
| 114 | _ => None, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | pub fn curr_error(&self) -> Option<BusError> { | ||
| 119 | let err = { self.regs.psr().read() }; | ||
| 120 | if err.bo() { | ||
| 121 | return Some(BusError::BusOff); | ||
| 122 | } else if err.ep() { | ||
| 123 | return Some(BusError::BusPassive); | ||
| 124 | } else if err.ew() { | ||
| 125 | return Some(BusError::BusWarning); | ||
| 126 | } else { | ||
| 127 | cfg_if! { | ||
| 128 | if #[cfg(stm32h7)] { | ||
| 129 | let lec = err.lec(); | ||
| 130 | } else { | ||
| 131 | let lec = err.lec().to_bits(); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | if let Some(err) = Self::reg_to_error(lec) { | ||
| 135 | return Some(err); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | None | ||
| 139 | } | ||
| 140 | /// Returns if the tx queue is able to accept new messages without having to cancel an existing one | ||
| 141 | #[inline] | ||
| 142 | pub fn tx_queue_is_full(&self) -> bool { | ||
| 143 | self.regs.txfqs().read().tfqf() | ||
| 144 | } | ||
| 145 | |||
| 146 | #[inline] | ||
| 147 | pub fn has_pending_frame(&self, idx: usize) -> bool { | ||
| 148 | self.regs.txbrp().read().trp(idx) | ||
| 149 | } | ||
| 150 | |||
| 151 | /// Returns `Ok` when the mailbox is free or if it contains pending frame with a | ||
| 152 | /// lower priority (higher ID) than the identifier `id`. | ||
| 153 | #[inline] | ||
| 154 | pub fn is_available(&self, bufidx: usize, id: &embedded_can::Id) -> bool { | ||
| 155 | if self.has_pending_frame(bufidx) { | ||
| 156 | let mailbox = self.tx_buffer_element(bufidx); | ||
| 157 | |||
| 158 | let header_reg = mailbox.header.read(); | ||
| 159 | let old_id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); | ||
| 160 | |||
| 161 | *id > old_id | ||
| 162 | } else { | ||
| 163 | true | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Attempts to abort the sending of a frame that is pending in a mailbox. | ||
| 168 | /// | ||
| 169 | /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be | ||
| 170 | /// aborted, this function has no effect and returns `false`. | ||
| 171 | /// | ||
| 172 | /// If there is a frame in the provided mailbox, and it is canceled successfully, this function | ||
| 173 | /// returns `true`. | ||
| 174 | #[inline] | ||
| 175 | pub fn abort(&self, bufidx: usize) -> bool { | ||
| 176 | let can = self.regs; | ||
| 177 | |||
| 178 | // Check if there is a request pending to abort | ||
| 179 | if self.has_pending_frame(bufidx) { | ||
| 180 | // Abort Request | ||
| 181 | can.txbcr().write(|w| w.set_cr(bufidx, true)); | ||
| 182 | |||
| 183 | // Wait for the abort request to be finished. | ||
| 184 | loop { | ||
| 185 | if can.txbcf().read().cf(bufidx) { | ||
| 186 | // Return false when a transmission has occured | ||
| 187 | break can.txbto().read().to(bufidx) == false; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } else { | ||
| 191 | false | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | #[inline] | ||
| 196 | //fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R> | ||
| 197 | pub fn abort_pending_mailbox(&self, bufidx: usize) -> Option<ClassicFrame> | ||
| 198 | //where | ||
| 199 | // PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, | ||
| 200 | { | ||
| 201 | if self.abort(bufidx) { | ||
| 202 | let mailbox = self.tx_buffer_element(bufidx); | ||
| 203 | |||
| 204 | let header_reg = mailbox.header.read(); | ||
| 205 | let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); | ||
| 206 | |||
| 207 | let len = match header_reg.to_data_length() { | ||
| 208 | DataLength::Fdcan(len) => len, | ||
| 209 | DataLength::Classic(len) => len, | ||
| 210 | }; | ||
| 211 | if len as usize > ClassicFrame::MAX_DATA_LEN { | ||
| 212 | return None; | ||
| 213 | } | ||
| 214 | |||
| 215 | //let tx_ram = self.tx_msg_ram(); | ||
| 216 | let mut data = [0u8; 64]; | ||
| 217 | data_from_tx_buffer(&mut data, mailbox, len as usize); | ||
| 218 | |||
| 219 | let cd = ClassicData::new(&data).unwrap(); | ||
| 220 | Some(ClassicFrame::new(Header::new(id, len, header_reg.rtr().bit()), cd)) | ||
| 221 | } else { | ||
| 222 | // Abort request failed because the frame was already sent (or being sent) on | ||
| 223 | // the bus. All mailboxes are now free. This can happen for small prescaler | ||
| 224 | // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR | ||
| 225 | // has preempted the execution. | ||
| 226 | None | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | #[inline] | ||
| 231 | //fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R> | ||
| 232 | pub fn abort_pending_fd_mailbox(&self, bufidx: usize) -> Option<FdFrame> | ||
| 233 | //where | ||
| 234 | // PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R, | ||
| 235 | { | ||
| 236 | if self.abort(bufidx) { | ||
| 237 | let mailbox = self.tx_buffer_element(bufidx); | ||
| 238 | |||
| 239 | let header_reg = mailbox.header.read(); | ||
| 240 | let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); | ||
| 241 | |||
| 242 | let len = match header_reg.to_data_length() { | ||
| 243 | DataLength::Fdcan(len) => len, | ||
| 244 | DataLength::Classic(len) => len, | ||
| 245 | }; | ||
| 246 | if len as usize > FdFrame::MAX_DATA_LEN { | ||
| 247 | return None; | ||
| 248 | } | ||
| 249 | |||
| 250 | //let tx_ram = self.tx_msg_ram(); | ||
| 251 | let mut data = [0u8; 64]; | ||
| 252 | data_from_tx_buffer(&mut data, mailbox, len as usize); | ||
| 253 | |||
| 254 | let cd = FdData::new(&data).unwrap(); | ||
| 255 | |||
| 256 | let header = if header_reg.fdf().frame_format() == FrameFormat::Fdcan { | ||
| 257 | Header::new_fd(id, len, header_reg.rtr().bit(), header_reg.brs().bit()) | ||
| 258 | } else { | ||
| 259 | Header::new(id, len, header_reg.rtr().bit()) | ||
| 260 | }; | ||
| 261 | |||
| 262 | Some(FdFrame::new(header, cd)) | ||
| 263 | } else { | ||
| 264 | // Abort request failed because the frame was already sent (or being sent) on | ||
| 265 | // the bus. All mailboxes are now free. This can happen for small prescaler | ||
| 266 | // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR | ||
| 267 | // has preempted the execution. | ||
| 268 | None | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can | ||
| 273 | /// be preserved. | ||
| 274 | //pub fn transmit_preserve<PTX, P>( | ||
| 275 | pub fn write_classic(&self, frame: &ClassicFrame) -> nb::Result<Option<ClassicFrame>, Infallible> { | ||
| 276 | let queue_is_full = self.tx_queue_is_full(); | ||
| 277 | |||
| 278 | let id = frame.header().id(); | ||
| 279 | |||
| 280 | // If the queue is full, | ||
| 281 | // Discard the first slot with a lower priority message | ||
| 282 | let (idx, pending_frame) = if queue_is_full { | ||
| 283 | if self.is_available(0, id) { | ||
| 284 | (0, self.abort_pending_mailbox(0)) | ||
| 285 | } else if self.is_available(1, id) { | ||
| 286 | (1, self.abort_pending_mailbox(1)) | ||
| 287 | } else if self.is_available(2, id) { | ||
| 288 | (2, self.abort_pending_mailbox(2)) | ||
| 289 | } else { | ||
| 290 | // For now we bail when there is no lower priority slot available | ||
| 291 | // Can this lead to priority inversion? | ||
| 292 | return Err(nb::Error::WouldBlock); | ||
| 293 | } | ||
| 294 | } else { | ||
| 295 | // Read the Write Pointer | ||
| 296 | let idx = self.regs.txfqs().read().tfqpi(); | ||
| 297 | |||
| 298 | (idx, None) | ||
| 299 | }; | ||
| 300 | |||
| 301 | self.put_tx_frame(idx as usize, frame.header(), frame.data()); | ||
| 302 | |||
| 303 | Ok(pending_frame) | ||
| 304 | } | ||
| 305 | |||
| 306 | /// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can | ||
| 307 | /// be preserved. | ||
| 308 | //pub fn transmit_preserve<PTX, P>( | ||
| 309 | pub fn write_fd(&self, frame: &FdFrame) -> nb::Result<Option<FdFrame>, Infallible> { | ||
| 310 | let queue_is_full = self.tx_queue_is_full(); | ||
| 311 | |||
| 312 | let id = frame.header().id(); | ||
| 313 | |||
| 314 | // If the queue is full, | ||
| 315 | // Discard the first slot with a lower priority message | ||
| 316 | let (idx, pending_frame) = if queue_is_full { | ||
| 317 | if self.is_available(0, id) { | ||
| 318 | (0, self.abort_pending_fd_mailbox(0)) | ||
| 319 | } else if self.is_available(1, id) { | ||
| 320 | (1, self.abort_pending_fd_mailbox(1)) | ||
| 321 | } else if self.is_available(2, id) { | ||
| 322 | (2, self.abort_pending_fd_mailbox(2)) | ||
| 323 | } else { | ||
| 324 | // For now we bail when there is no lower priority slot available | ||
| 325 | // Can this lead to priority inversion? | ||
| 326 | return Err(nb::Error::WouldBlock); | ||
| 327 | } | ||
| 328 | } else { | ||
| 329 | // Read the Write Pointer | ||
| 330 | let idx = self.regs.txfqs().read().tfqpi(); | ||
| 331 | |||
| 332 | (idx, None) | ||
| 333 | }; | ||
| 334 | |||
| 335 | self.put_tx_frame(idx as usize, frame.header(), frame.data()); | ||
| 336 | |||
| 337 | Ok(pending_frame) | ||
| 338 | } | ||
| 339 | |||
| 340 | #[inline] | ||
| 341 | fn reset_msg_ram(&mut self) { | ||
| 342 | self.msg_ram_mut().reset(); | ||
| 343 | } | ||
| 344 | |||
| 345 | #[inline] | ||
| 346 | fn enter_init_mode(&mut self) { | ||
| 347 | self.regs.cccr().modify(|w| w.set_init(true)); | ||
| 348 | while false == self.regs.cccr().read().init() {} | ||
| 349 | self.regs.cccr().modify(|w| w.set_cce(true)); | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Enables or disables loopback mode: Internally connects the TX and RX | ||
| 353 | /// signals together. | ||
| 354 | #[inline] | ||
| 355 | fn set_loopback_mode(&mut self, mode: LoopbackMode) { | ||
| 356 | let (test, mon, lbck) = match mode { | ||
| 357 | LoopbackMode::None => (false, false, false), | ||
| 358 | LoopbackMode::Internal => (true, true, true), | ||
| 359 | LoopbackMode::External => (true, false, true), | ||
| 360 | }; | ||
| 361 | |||
| 362 | self.set_test_mode(test); | ||
| 363 | self.set_bus_monitoring_mode(mon); | ||
| 364 | |||
| 365 | self.regs.test().modify(|w| w.set_lbck(lbck)); | ||
| 366 | } | ||
| 367 | |||
| 368 | /// Enables or disables silent mode: Disconnects the TX signal from the pin. | ||
| 369 | #[inline] | ||
| 370 | fn set_bus_monitoring_mode(&mut self, enabled: bool) { | ||
| 371 | self.regs.cccr().modify(|w| w.set_mon(enabled)); | ||
| 372 | } | ||
| 373 | |||
| 374 | #[inline] | ||
| 375 | fn set_restricted_operations(&mut self, enabled: bool) { | ||
| 376 | self.regs.cccr().modify(|w| w.set_asm(enabled)); | ||
| 377 | } | ||
| 378 | |||
| 379 | #[inline] | ||
| 380 | fn set_normal_operations(&mut self, _enabled: bool) { | ||
| 381 | self.set_loopback_mode(LoopbackMode::None); | ||
| 382 | } | ||
| 383 | |||
| 384 | #[inline] | ||
| 385 | fn set_test_mode(&mut self, enabled: bool) { | ||
| 386 | self.regs.cccr().modify(|w| w.set_test(enabled)); | ||
| 387 | } | ||
| 388 | |||
| 389 | #[inline] | ||
| 390 | fn set_power_down_mode(&mut self, enabled: bool) { | ||
| 391 | self.regs.cccr().modify(|w| w.set_csr(enabled)); | ||
| 392 | while self.regs.cccr().read().csa() != enabled {} | ||
| 393 | } | ||
| 394 | |||
| 395 | /// Moves out of PoweredDownMode and into ConfigMode | ||
| 396 | #[inline] | ||
| 397 | pub fn into_config_mode(mut self, _config: FdCanConfig) { | ||
| 398 | self.set_power_down_mode(false); | ||
| 399 | self.enter_init_mode(); | ||
| 400 | |||
| 401 | self.reset_msg_ram(); | ||
| 402 | |||
| 403 | // check the FDCAN core matches our expections | ||
| 404 | assert!( | ||
| 405 | self.regs.crel().read().rel() == 3, | ||
| 406 | "Expected FDCAN core major release 3" | ||
| 407 | ); | ||
| 408 | assert!( | ||
| 409 | self.regs.endn().read().etv() == 0x87654321_u32, | ||
| 410 | "Error reading endianness test value from FDCAN core" | ||
| 411 | ); | ||
| 412 | |||
| 413 | // Framework specific settings are set here | ||
| 414 | |||
| 415 | // set TxBuffer to Queue Mode | ||
| 416 | self.regs.txbc().write(|w| w.set_tfqm(true)); | ||
| 417 | |||
| 418 | // set standard filters list size to 28 | ||
| 419 | // set extended filters list size to 8 | ||
| 420 | // REQUIRED: we use the memory map as if these settings are set | ||
| 421 | // instead of re-calculating them. | ||
| 422 | #[cfg(not(stm32h7))] | ||
| 423 | { | ||
| 424 | self.regs.rxgfc().modify(|w| { | ||
| 425 | w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX); | ||
| 426 | w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX); | ||
| 427 | }); | ||
| 428 | } | ||
| 429 | #[cfg(stm32h7)] | ||
| 430 | { | ||
| 431 | self.regs | ||
| 432 | .sidfc() | ||
| 433 | .modify(|w| w.set_lss(crate::can::fd::message_ram::STANDARD_FILTER_MAX)); | ||
| 434 | self.regs | ||
| 435 | .xidfc() | ||
| 436 | .modify(|w| w.set_lse(crate::can::fd::message_ram::EXTENDED_FILTER_MAX)); | ||
| 437 | } | ||
| 438 | |||
| 439 | /* | ||
| 440 | for fid in 0..crate::can::message_ram::STANDARD_FILTER_MAX { | ||
| 441 | self.set_standard_filter((fid as u8).into(), StandardFilter::disable()); | ||
| 442 | } | ||
| 443 | for fid in 0..Ecrate::can::message_ram::XTENDED_FILTER_MAX { | ||
| 444 | self.set_extended_filter(fid.into(), ExtendedFilter::disable()); | ||
| 445 | } | ||
| 446 | */ | ||
| 447 | } | ||
| 448 | |||
| 449 | /// Disables the CAN interface and returns back the raw peripheral it was created from. | ||
| 450 | #[inline] | ||
| 451 | pub fn free(mut self) { | ||
| 452 | //self.disable_interrupts(Interrupts::all()); | ||
| 453 | |||
| 454 | //TODO check this! | ||
| 455 | self.enter_init_mode(); | ||
| 456 | self.set_power_down_mode(true); | ||
| 457 | //self.control.instance | ||
| 458 | } | ||
| 459 | |||
| 460 | /// Applies the settings of a new FdCanConfig See [`FdCanConfig`] | ||
| 461 | #[inline] | ||
| 462 | pub fn apply_config(&mut self, config: FdCanConfig) { | ||
| 463 | self.set_data_bit_timing(config.dbtr); | ||
| 464 | self.set_nominal_bit_timing(config.nbtr); | ||
| 465 | self.set_automatic_retransmit(config.automatic_retransmit); | ||
| 466 | self.set_transmit_pause(config.transmit_pause); | ||
| 467 | self.set_frame_transmit(config.frame_transmit); | ||
| 468 | //self.set_interrupt_line_config(config.interrupt_line_config); | ||
| 469 | self.set_non_iso_mode(config.non_iso_mode); | ||
| 470 | self.set_edge_filtering(config.edge_filtering); | ||
| 471 | self.set_protocol_exception_handling(config.protocol_exception_handling); | ||
| 472 | self.set_global_filter(config.global_filter); | ||
| 473 | } | ||
| 474 | |||
| 475 | #[inline] | ||
| 476 | fn leave_init_mode(&mut self, config: FdCanConfig) { | ||
| 477 | self.apply_config(config); | ||
| 478 | |||
| 479 | self.regs.cccr().modify(|w| w.set_cce(false)); | ||
| 480 | self.regs.cccr().modify(|w| w.set_init(false)); | ||
| 481 | while self.regs.cccr().read().init() == true {} | ||
| 482 | } | ||
| 483 | |||
| 484 | /// Moves out of ConfigMode and into specified mode | ||
| 485 | #[inline] | ||
| 486 | pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) { | ||
| 487 | match mode { | ||
| 488 | crate::can::FdcanOperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), | ||
| 489 | crate::can::FdcanOperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), | ||
| 490 | crate::can::FdcanOperatingMode::NormalOperationMode => self.set_normal_operations(true), | ||
| 491 | crate::can::FdcanOperatingMode::RestrictedOperationMode => self.set_restricted_operations(true), | ||
| 492 | crate::can::FdcanOperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true), | ||
| 493 | } | ||
| 494 | self.leave_init_mode(config); | ||
| 495 | } | ||
| 496 | |||
| 497 | /// Moves out of ConfigMode and into InternalLoopbackMode | ||
| 498 | #[inline] | ||
| 499 | pub fn into_internal_loopback(mut self, config: FdCanConfig) { | ||
| 500 | self.set_loopback_mode(LoopbackMode::Internal); | ||
| 501 | self.leave_init_mode(config); | ||
| 502 | } | ||
| 503 | |||
| 504 | /// Moves out of ConfigMode and into ExternalLoopbackMode | ||
| 505 | #[inline] | ||
| 506 | pub fn into_external_loopback(mut self, config: FdCanConfig) { | ||
| 507 | self.set_loopback_mode(LoopbackMode::External); | ||
| 508 | self.leave_init_mode(config); | ||
| 509 | } | ||
| 510 | |||
| 511 | /// Moves out of ConfigMode and into RestrictedOperationMode | ||
| 512 | #[inline] | ||
| 513 | pub fn into_restricted(mut self, config: FdCanConfig) { | ||
| 514 | self.set_restricted_operations(true); | ||
| 515 | self.leave_init_mode(config); | ||
| 516 | } | ||
| 517 | |||
| 518 | /// Moves out of ConfigMode and into NormalOperationMode | ||
| 519 | #[inline] | ||
| 520 | pub fn into_normal(mut self, config: FdCanConfig) { | ||
| 521 | self.set_normal_operations(true); | ||
| 522 | self.leave_init_mode(config); | ||
| 523 | } | ||
| 524 | |||
| 525 | /// Moves out of ConfigMode and into BusMonitoringMode | ||
| 526 | #[inline] | ||
| 527 | pub fn into_bus_monitoring(mut self, config: FdCanConfig) { | ||
| 528 | self.set_bus_monitoring_mode(true); | ||
| 529 | self.leave_init_mode(config); | ||
| 530 | } | ||
| 531 | |||
| 532 | /// Moves out of ConfigMode and into Testmode | ||
| 533 | #[inline] | ||
| 534 | pub fn into_test_mode(mut self, config: FdCanConfig) { | ||
| 535 | self.set_test_mode(true); | ||
| 536 | self.leave_init_mode(config); | ||
| 537 | } | ||
| 538 | |||
| 539 | /// Moves out of ConfigMode and into PoweredDownmode | ||
| 540 | #[inline] | ||
| 541 | pub fn into_powered_down(mut self, config: FdCanConfig) { | ||
| 542 | self.set_power_down_mode(true); | ||
| 543 | self.leave_init_mode(config); | ||
| 544 | } | ||
| 545 | |||
| 546 | /// Configures the bit timings. | ||
| 547 | /// | ||
| 548 | /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter | ||
| 549 | /// parameters as follows: | ||
| 550 | /// | ||
| 551 | /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). | ||
| 552 | /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). | ||
| 553 | /// - *Sample Point*: Should normally be left at the default value of 87.5%. | ||
| 554 | /// - *SJW*: Should normally be left at the default value of 1. | ||
| 555 | /// | ||
| 556 | /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` | ||
| 557 | /// parameter to this method. | ||
| 558 | #[inline] | ||
| 559 | pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) { | ||
| 560 | //self.control.config.nbtr = btr; | ||
| 561 | |||
| 562 | self.regs.nbtp().write(|w| { | ||
| 563 | w.set_nbrp(btr.nbrp() - 1); | ||
| 564 | w.set_ntseg1(btr.ntseg1() - 1); | ||
| 565 | w.set_ntseg2(btr.ntseg2() - 1); | ||
| 566 | w.set_nsjw(btr.nsjw() - 1); | ||
| 567 | }); | ||
| 568 | } | ||
| 569 | |||
| 570 | /// Configures the data bit timings for the FdCan Variable Bitrates. | ||
| 571 | /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. | ||
| 572 | #[inline] | ||
| 573 | pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) { | ||
| 574 | //self.control.config.dbtr = btr; | ||
| 575 | |||
| 576 | self.regs.dbtp().write(|w| { | ||
| 577 | w.set_dbrp(btr.dbrp() - 1); | ||
| 578 | w.set_dtseg1(btr.dtseg1() - 1); | ||
| 579 | w.set_dtseg2(btr.dtseg2() - 1); | ||
| 580 | w.set_dsjw(btr.dsjw() - 1); | ||
| 581 | }); | ||
| 582 | } | ||
| 583 | |||
| 584 | /// Enables or disables automatic retransmission of messages | ||
| 585 | /// | ||
| 586 | /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame | ||
| 587 | /// util it can be sent. Otherwise, it will try only once to send each frame. | ||
| 588 | /// | ||
| 589 | /// Automatic retransmission is enabled by default. | ||
| 590 | #[inline] | ||
| 591 | pub fn set_automatic_retransmit(&mut self, enabled: bool) { | ||
| 592 | self.regs.cccr().modify(|w| w.set_dar(!enabled)); | ||
| 593 | //self.control.config.automatic_retransmit = enabled; | ||
| 594 | } | ||
| 595 | |||
| 596 | /// Configures the transmit pause feature. See | ||
| 597 | /// [`FdCanConfig::set_transmit_pause`] | ||
| 598 | #[inline] | ||
| 599 | pub fn set_transmit_pause(&mut self, enabled: bool) { | ||
| 600 | self.regs.cccr().modify(|w| w.set_txp(!enabled)); | ||
| 601 | //self.control.config.transmit_pause = enabled; | ||
| 602 | } | ||
| 603 | |||
| 604 | /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] | ||
| 605 | #[inline] | ||
| 606 | pub fn set_non_iso_mode(&mut self, enabled: bool) { | ||
| 607 | self.regs.cccr().modify(|w| w.set_niso(enabled)); | ||
| 608 | //self.control.config.non_iso_mode = enabled; | ||
| 609 | } | ||
| 610 | |||
| 611 | /// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`] | ||
| 612 | #[inline] | ||
| 613 | pub fn set_edge_filtering(&mut self, enabled: bool) { | ||
| 614 | self.regs.cccr().modify(|w| w.set_efbi(enabled)); | ||
| 615 | //self.control.config.edge_filtering = enabled; | ||
| 616 | } | ||
| 617 | |||
| 618 | /// Configures frame transmission mode. See | ||
| 619 | /// [`FdCanConfig::set_frame_transmit`] | ||
| 620 | #[inline] | ||
| 621 | pub fn set_frame_transmit(&mut self, fts: FrameTransmissionConfig) { | ||
| 622 | let (fdoe, brse) = match fts { | ||
| 623 | FrameTransmissionConfig::ClassicCanOnly => (false, false), | ||
| 624 | FrameTransmissionConfig::AllowFdCan => (true, false), | ||
| 625 | FrameTransmissionConfig::AllowFdCanAndBRS => (true, true), | ||
| 626 | }; | ||
| 627 | |||
| 628 | self.regs.cccr().modify(|w| { | ||
| 629 | w.set_fdoe(fdoe); | ||
| 630 | #[cfg(stm32h7)] | ||
| 631 | w.set_bse(brse); | ||
| 632 | #[cfg(not(stm32h7))] | ||
| 633 | w.set_brse(brse); | ||
| 634 | }); | ||
| 635 | |||
| 636 | //self.control.config.frame_transmit = fts; | ||
| 637 | } | ||
| 638 | |||
| 639 | /// Sets the protocol exception handling on/off | ||
| 640 | #[inline] | ||
| 641 | pub fn set_protocol_exception_handling(&mut self, enabled: bool) { | ||
| 642 | self.regs.cccr().modify(|w| w.set_pxhd(!enabled)); | ||
| 643 | |||
| 644 | //self.control.config.protocol_exception_handling = enabled; | ||
| 645 | } | ||
| 646 | |||
| 647 | /// Configures and resets the timestamp counter | ||
| 648 | #[inline] | ||
| 649 | pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) { | ||
| 650 | #[cfg(stm32h7)] | ||
| 651 | let (tcp, tss) = match select { | ||
| 652 | TimestampSource::None => (0, 0), | ||
| 653 | TimestampSource::Prescaler(p) => (p as u8, 1), | ||
| 654 | TimestampSource::FromTIM3 => (0, 2), | ||
| 655 | }; | ||
| 656 | |||
| 657 | #[cfg(not(stm32h7))] | ||
| 658 | let (tcp, tss) = match select { | ||
| 659 | TimestampSource::None => (0, stm32_metapac::can::vals::Tss::ZERO), | ||
| 660 | TimestampSource::Prescaler(p) => (p as u8, stm32_metapac::can::vals::Tss::INCREMENT), | ||
| 661 | TimestampSource::FromTIM3 => (0, stm32_metapac::can::vals::Tss::EXTERNAL), | ||
| 662 | }; | ||
| 663 | |||
| 664 | self.regs.tscc().write(|w| { | ||
| 665 | w.set_tcp(tcp); | ||
| 666 | w.set_tss(tss); | ||
| 667 | }); | ||
| 668 | |||
| 669 | //self.control.config.timestamp_source = select; | ||
| 670 | } | ||
| 671 | |||
| 672 | #[cfg(not(stm32h7))] | ||
| 673 | /// Configures the global filter settings | ||
| 674 | #[inline] | ||
| 675 | pub fn set_global_filter(&mut self, filter: GlobalFilter) { | ||
| 676 | let anfs = match filter.handle_standard_frames { | ||
| 677 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_0, | ||
| 678 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_1, | ||
| 679 | crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfs::REJECT, | ||
| 680 | }; | ||
| 681 | let anfe = match filter.handle_extended_frames { | ||
| 682 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_0, | ||
| 683 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfe::ACCEPT_FIFO_1, | ||
| 684 | crate::can::fd::config::NonMatchingFilter::Reject => stm32_metapac::can::vals::Anfe::REJECT, | ||
| 685 | }; | ||
| 686 | |||
| 687 | self.regs.rxgfc().modify(|w| { | ||
| 688 | w.set_anfs(anfs); | ||
| 689 | w.set_anfe(anfe); | ||
| 690 | w.set_rrfs(filter.reject_remote_standard_frames); | ||
| 691 | w.set_rrfe(filter.reject_remote_extended_frames); | ||
| 692 | }); | ||
| 693 | } | ||
| 694 | |||
| 695 | #[cfg(stm32h7)] | ||
| 696 | /// Configures the global filter settings | ||
| 697 | #[inline] | ||
| 698 | pub fn set_global_filter(&mut self, filter: GlobalFilter) { | ||
| 699 | let anfs = match filter.handle_standard_frames { | ||
| 700 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0, | ||
| 701 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1, | ||
| 702 | crate::can::fd::config::NonMatchingFilter::Reject => 2, | ||
| 703 | }; | ||
| 704 | |||
| 705 | let anfe = match filter.handle_extended_frames { | ||
| 706 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0, | ||
| 707 | crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1, | ||
| 708 | crate::can::fd::config::NonMatchingFilter::Reject => 2, | ||
| 709 | }; | ||
| 710 | |||
| 711 | self.regs.gfc().modify(|w| { | ||
| 712 | w.set_anfs(anfs); | ||
| 713 | w.set_anfe(anfe); | ||
| 714 | w.set_rrfs(filter.reject_remote_standard_frames); | ||
| 715 | w.set_rrfe(filter.reject_remote_extended_frames); | ||
| 716 | }); | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | fn make_id(id: u32, extended: bool) -> embedded_can::Id { | ||
| 721 | if extended { | ||
| 722 | embedded_can::Id::from(unsafe { embedded_can::ExtendedId::new_unchecked(id & 0x1FFFFFFF) }) | ||
| 723 | } else { | ||
| 724 | embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked((id & 0x000007FF) as u16) }) | ||
| 725 | } | ||
| 726 | } | ||
| 727 | |||
| 728 | fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) { | ||
| 729 | let (id, id_type) = match header.id() { | ||
| 730 | embedded_can::Id::Standard(id) => (id.as_raw() as u32, IdType::StandardId), | ||
| 731 | embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId), | ||
| 732 | }; | ||
| 733 | |||
| 734 | // Use FDCAN only for DLC > 8. FDCAN users can revise this if required. | ||
| 735 | let frame_format = if header.len() > 8 || header.fdcan() { | ||
| 736 | FrameFormat::Fdcan | ||
| 737 | } else { | ||
| 738 | FrameFormat::Classic | ||
| 739 | }; | ||
| 740 | let brs = header.len() > 8 || header.bit_rate_switching(); | ||
| 741 | |||
| 742 | mailbox.header.write(|w| { | ||
| 743 | unsafe { w.id().bits(id) } | ||
| 744 | .rtr() | ||
| 745 | .bit(header.len() == 0 && header.rtr()) | ||
| 746 | .xtd() | ||
| 747 | .set_id_type(id_type) | ||
| 748 | .set_len(DataLength::new(header.len(), frame_format)) | ||
| 749 | .set_event(Event::NoEvent) | ||
| 750 | .fdf() | ||
| 751 | .set_format(frame_format) | ||
| 752 | .brs() | ||
| 753 | .bit(brs) | ||
| 754 | //esi.set_error_indicator(//TODO//) | ||
| 755 | }); | ||
| 756 | } | ||
| 757 | |||
| 758 | fn put_tx_data(mailbox: &mut TxBufferElement, buffer: &[u8]) { | ||
| 759 | let mut lbuffer = [0_u32; 16]; | ||
| 760 | let len = buffer.len(); | ||
| 761 | let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) }; | ||
| 762 | data[..len].copy_from_slice(&buffer[..len]); | ||
| 763 | let data_len = ((len) + 3) / 4; | ||
| 764 | for (register, byte) in mailbox.data.iter_mut().zip(lbuffer[..data_len].iter()) { | ||
| 765 | unsafe { register.write(*byte) }; | ||
| 766 | } | ||
| 767 | } | ||
| 768 | |||
| 769 | fn data_from_fifo(buffer: &mut [u8], mailbox: &RxFifoElement, len: usize) { | ||
| 770 | for (i, register) in mailbox.data.iter().enumerate() { | ||
| 771 | let register_value = register.read(); | ||
| 772 | let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) }; | ||
| 773 | let num_bytes = (len) - i * 4; | ||
| 774 | if num_bytes <= 4 { | ||
| 775 | buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]); | ||
| 776 | break; | ||
| 777 | } | ||
| 778 | buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes); | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 782 | fn data_from_tx_buffer(buffer: &mut [u8], mailbox: &TxBufferElement, len: usize) { | ||
| 783 | for (i, register) in mailbox.data.iter().enumerate() { | ||
| 784 | let register_value = register.read(); | ||
| 785 | let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) }; | ||
| 786 | let num_bytes = (len) - i * 4; | ||
| 787 | if num_bytes <= 4 { | ||
| 788 | buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]); | ||
| 789 | break; | ||
| 790 | } | ||
| 791 | buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes); | ||
| 792 | } | ||
| 793 | } | ||
| 794 | |||
| 795 | impl From<&RxFifoElement> for ClassicFrame { | ||
| 796 | fn from(mailbox: &RxFifoElement) -> Self { | ||
| 797 | let header_reg = mailbox.header.read(); | ||
| 798 | |||
| 799 | let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); | ||
| 800 | let dlc = header_reg.to_data_length().len(); | ||
| 801 | let len = dlc as usize; | ||
| 802 | |||
| 803 | let mut buffer: [u8; 64] = [0; 64]; | ||
| 804 | data_from_fifo(&mut buffer, mailbox, len); | ||
| 805 | let data = ClassicData::new(&buffer[0..len]); | ||
| 806 | let header = Header::new(id, dlc, header_reg.rtr().bits()); | ||
| 807 | ClassicFrame::new(header, data.unwrap()) | ||
| 808 | } | ||
| 809 | } | ||
| 810 | |||
| 811 | fn extract_frame(mailbox: &RxFifoElement, buffer: &mut [u8]) -> Option<(Header, u16)> { | ||
| 812 | let header_reg = mailbox.header.read(); | ||
| 813 | |||
| 814 | let id = make_id(header_reg.id().bits(), header_reg.xtd().bits()); | ||
| 815 | let dlc = header_reg.to_data_length().len(); | ||
| 816 | let len = dlc as usize; | ||
| 817 | let timestamp = header_reg.txts().bits; | ||
| 818 | if len > buffer.len() { | ||
| 819 | return None; | ||
| 820 | } | ||
| 821 | data_from_fifo(buffer, mailbox, len); | ||
| 822 | let header = if header_reg.fdf().bits { | ||
| 823 | Header::new_fd(id, dlc, header_reg.rtr().bits(), header_reg.brs().bits()) | ||
| 824 | } else { | ||
| 825 | Header::new(id, dlc, header_reg.rtr().bits()) | ||
| 826 | }; | ||
| 827 | Some((header, timestamp)) | ||
| 828 | } | ||
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index faf4af73f..f1f6f935e 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | #[allow(unused_variables)] | ||
| 1 | use core::future::poll_fn; | 2 | use core::future::poll_fn; |
| 2 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 3 | use core::ops::{Deref, DerefMut}; | ||
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | 5 | ||
| 6 | use cfg_if::cfg_if; | 6 | pub mod fd; |
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
| 8 | pub use fdcan::frame::{FrameFormat, RxFrameInfo, TxFrameHeader}; | 8 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; |
| 9 | pub use fdcan::id::{ExtendedId, Id, StandardId}; | 9 | use embassy_sync::channel::Channel; |
| 10 | use fdcan::message_ram::RegisterBlock; | 10 | use fd::config::*; |
| 11 | use fdcan::{self, LastErrorCode}; | 11 | use fd::filter::*; |
| 12 | pub use fdcan::{config, filter}; | ||
| 13 | 12 | ||
| 13 | use crate::can::fd::peripheral::Registers; | ||
| 14 | use crate::gpio::sealed::AFType; | 14 | use crate::gpio::sealed::AFType; |
| 15 | use crate::interrupt::typelevel::Interrupt; | 15 | use crate::interrupt::typelevel::Interrupt; |
| 16 | use crate::rcc::RccPeripheral; | 16 | use crate::rcc::RccPeripheral; |
| @@ -20,127 +20,14 @@ pub mod enums; | |||
| 20 | use enums::*; | 20 | use enums::*; |
| 21 | pub mod util; | 21 | pub mod util; |
| 22 | 22 | ||
| 23 | /// CAN Frame returned by read | 23 | pub mod frame; |
| 24 | pub struct RxFrame { | 24 | use frame::*; |
| 25 | /// CAN Header info: frame ID, data length and other meta | ||
| 26 | pub header: RxFrameInfo, | ||
| 27 | /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data | ||
| 28 | pub data: Data, | ||
| 29 | /// Reception time. | ||
| 30 | #[cfg(feature = "time")] | ||
| 31 | pub timestamp: embassy_time::Instant, | ||
| 32 | } | ||
| 33 | |||
| 34 | /// CAN frame used for write | ||
| 35 | pub struct TxFrame { | ||
| 36 | /// CAN Header info: frame ID, data length and other meta | ||
| 37 | pub header: TxFrameHeader, | ||
| 38 | /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data | ||
| 39 | pub data: Data, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl TxFrame { | ||
| 43 | /// Create new TX frame from header and data | ||
| 44 | pub fn new(header: TxFrameHeader, data: &[u8]) -> Option<Self> { | ||
| 45 | if data.len() < header.len as usize { | ||
| 46 | return None; | ||
| 47 | } | ||
| 48 | |||
| 49 | let Some(data) = Data::new(data) else { return None }; | ||
| 50 | |||
| 51 | Some(TxFrame { header, data }) | ||
| 52 | } | ||
| 53 | |||
| 54 | fn from_preserved(header: TxFrameHeader, data32: &[u32]) -> Option<Self> { | ||
| 55 | let mut data = [0u8; 64]; | ||
| 56 | |||
| 57 | for i in 0..data32.len() { | ||
| 58 | data[4 * i..][..4].copy_from_slice(&data32[i].to_le_bytes()); | ||
| 59 | } | ||
| 60 | |||
| 61 | let Some(data) = Data::new(&data) else { return None }; | ||
| 62 | |||
| 63 | Some(TxFrame { header, data }) | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Access frame data. Slice length will match header. | ||
| 67 | pub fn data(&self) -> &[u8] { | ||
| 68 | &self.data.bytes[..(self.header.len as usize)] | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | impl RxFrame { | ||
| 73 | pub(crate) fn new( | ||
| 74 | header: RxFrameInfo, | ||
| 75 | data: &[u8], | ||
| 76 | #[cfg(feature = "time")] timestamp: embassy_time::Instant, | ||
| 77 | ) -> Self { | ||
| 78 | let data = Data::new(&data).unwrap_or_else(|| Data::empty()); | ||
| 79 | 25 | ||
| 80 | RxFrame { | 26 | #[cfg(feature = "time")] |
| 81 | header, | 27 | type Timestamp = embassy_time::Instant; |
| 82 | data, | ||
| 83 | #[cfg(feature = "time")] | ||
| 84 | timestamp, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Access frame data. Slice length will match header. | ||
| 89 | pub fn data(&self) -> &[u8] { | ||
| 90 | &self.data.bytes[..(self.header.len as usize)] | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Payload of a (FD)CAN data frame. | ||
| 95 | /// | ||
| 96 | /// Contains 0 to 64 Bytes of data. | ||
| 97 | #[derive(Debug, Copy, Clone)] | ||
| 98 | pub struct Data { | ||
| 99 | pub(crate) bytes: [u8; 64], | ||
| 100 | } | ||
| 101 | |||
| 102 | impl Data { | ||
| 103 | /// Creates a data payload from a raw byte slice. | ||
| 104 | /// | ||
| 105 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or | ||
| 106 | /// cannot be represented with an FDCAN DLC. | ||
| 107 | pub fn new(data: &[u8]) -> Option<Self> { | ||
| 108 | if !Data::is_valid_len(data.len()) { | ||
| 109 | return None; | ||
| 110 | } | ||
| 111 | |||
| 112 | let mut bytes = [0; 64]; | ||
| 113 | bytes[..data.len()].copy_from_slice(data); | ||
| 114 | |||
| 115 | Some(Self { bytes }) | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Raw read access to data. | ||
| 119 | pub fn raw(&self) -> &[u8] { | ||
| 120 | &self.bytes | ||
| 121 | } | ||
| 122 | 28 | ||
| 123 | /// Checks if the length can be encoded in FDCAN DLC field. | 29 | #[cfg(not(feature = "time"))] |
| 124 | pub const fn is_valid_len(len: usize) -> bool { | 30 | type Timestamp = u16; |
| 125 | match len { | ||
| 126 | 0..=8 => true, | ||
| 127 | 12 => true, | ||
| 128 | 16 => true, | ||
| 129 | 20 => true, | ||
| 130 | 24 => true, | ||
| 131 | 32 => true, | ||
| 132 | 48 => true, | ||
| 133 | 64 => true, | ||
| 134 | _ => false, | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Creates an empty data payload containing 0 bytes. | ||
| 139 | #[inline] | ||
| 140 | pub const fn empty() -> Self { | ||
| 141 | Self { bytes: [0; 64] } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | 31 | ||
| 145 | /// Interrupt handler channel 0. | 32 | /// Interrupt handler channel 0. |
| 146 | pub struct IT0InterruptHandler<T: Instance> { | 33 | pub struct IT0InterruptHandler<T: Instance> { |
| @@ -154,14 +41,37 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 154 | 41 | ||
| 155 | let ir = regs.ir().read(); | 42 | let ir = regs.ir().read(); |
| 156 | 43 | ||
| 157 | if ir.tc() { | 44 | { |
| 158 | regs.ir().write(|w| w.set_tc(true)); | 45 | if ir.tc() { |
| 159 | T::state().tx_waker.wake(); | 46 | regs.ir().write(|w| w.set_tc(true)); |
| 160 | } | 47 | } |
| 48 | if ir.tefn() { | ||
| 49 | regs.ir().write(|w| w.set_tefn(true)); | ||
| 50 | } | ||
| 161 | 51 | ||
| 162 | if ir.tefn() { | 52 | match &T::state().tx_mode { |
| 163 | regs.ir().write(|w| w.set_tefn(true)); | 53 | sealed::TxMode::NonBuffered(waker) => waker.wake(), |
| 164 | T::state().tx_waker.wake(); | 54 | sealed::TxMode::ClassicBuffered(buf) => { |
| 55 | if !T::registers().tx_queue_is_full() { | ||
| 56 | match buf.tx_receiver.try_receive() { | ||
| 57 | Ok(frame) => { | ||
| 58 | _ = T::registers().write_classic(&frame); | ||
| 59 | } | ||
| 60 | Err(_) => {} | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | sealed::TxMode::FdBuffered(buf) => { | ||
| 65 | if !T::registers().tx_queue_is_full() { | ||
| 66 | match buf.tx_receiver.try_receive() { | ||
| 67 | Ok(frame) => { | ||
| 68 | _ = T::registers().write_fd(&frame); | ||
| 69 | } | ||
| 70 | Err(_) => {} | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 165 | } | 75 | } |
| 166 | 76 | ||
| 167 | if ir.ped() || ir.pea() { | 77 | if ir.ped() || ir.pea() { |
| @@ -172,13 +82,11 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup | |||
| 172 | } | 82 | } |
| 173 | 83 | ||
| 174 | if ir.rfn(0) { | 84 | if ir.rfn(0) { |
| 175 | regs.ir().write(|w| w.set_rfn(0, true)); | 85 | T::state().rx_mode.on_interrupt::<T>(0); |
| 176 | T::state().rx_waker.wake(); | ||
| 177 | } | 86 | } |
| 178 | 87 | ||
| 179 | if ir.rfn(1) { | 88 | if ir.rfn(1) { |
| 180 | regs.ir().write(|w| w.set_rfn(1, true)); | 89 | T::state().rx_mode.on_interrupt::<T>(1); |
| 181 | T::state().rx_waker.wake(); | ||
| 182 | } | 90 | } |
| 183 | } | 91 | } |
| 184 | } | 92 | } |
| @@ -192,44 +100,56 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1Interrup | |||
| 192 | unsafe fn on_interrupt() {} | 100 | unsafe fn on_interrupt() {} |
| 193 | } | 101 | } |
| 194 | 102 | ||
| 195 | impl BusError { | 103 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
| 196 | fn try_from(lec: LastErrorCode) -> Option<BusError> { | 104 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 197 | match lec { | 105 | /// Different operating modes |
| 198 | LastErrorCode::AckError => Some(BusError::Acknowledge), | 106 | pub enum FdcanOperatingMode { |
| 199 | // `0` data bit encodes a dominant state. `1` data bit is recessive. | 107 | //PoweredDownMode, |
| 200 | // Bit0Error: During transmit, the node wanted to send a 0 but monitored a 1 | 108 | //ConfigMode, |
| 201 | LastErrorCode::Bit0Error => Some(BusError::BitRecessive), | 109 | /// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without |
| 202 | LastErrorCode::Bit1Error => Some(BusError::BitDominant), | 110 | /// affecting a running CAN system connected to the FDCAN_TX and FDCAN_RX pins. In this |
| 203 | LastErrorCode::CRCError => Some(BusError::Crc), | 111 | /// mode, FDCAN_RX pin is disconnected from the FDCAN and FDCAN_TX pin is held |
| 204 | LastErrorCode::FormError => Some(BusError::Form), | 112 | /// recessive. |
| 205 | LastErrorCode::StuffError => Some(BusError::Stuff), | 113 | InternalLoopbackMode, |
| 206 | _ => None, | 114 | /// This mode is provided for hardware self-test. To be independent from external stimulation, |
| 207 | } | 115 | /// the FDCAN ignores acknowledge errors (recessive bit sampled in the acknowledge slot of a |
| 208 | } | 116 | /// data / remote frame) in Loop Back mode. In this mode the FDCAN performs an internal |
| 117 | /// feedback from its transmit output to its receive input. The actual value of the FDCAN_RX | ||
| 118 | /// input pin is disregarded by the FDCAN. The transmitted messages can be monitored at the | ||
| 119 | /// FDCAN_TX transmit pin. | ||
| 120 | ExternalLoopbackMode, | ||
| 121 | /// The normal use of the Fdcan instance after configurations | ||
| 122 | NormalOperationMode, | ||
| 123 | /// In Restricted operation mode the node is able to receive data and remote frames and to give | ||
| 124 | /// acknowledge to valid frames, but it does not send data frames, remote frames, active error | ||
| 125 | /// frames, or overload frames. In case of an error condition or overload condition, it does not | ||
| 126 | /// send dominant bits, instead it waits for the occurrence of bus idle condition to resynchronize | ||
| 127 | /// itself to the CAN communication. The error counters for transmit and receive are frozen while | ||
| 128 | /// error logging (can_errors) is active. TODO: automatically enter in this mode? | ||
| 129 | RestrictedOperationMode, | ||
| 130 | /// In Bus monitoring mode (for more details refer to ISO11898-1, 10.12 Bus monitoring), | ||
| 131 | /// the FDCAN is able to receive valid data frames and valid remote frames, but cannot start a | ||
| 132 | /// transmission. In this mode, it sends only recessive bits on the CAN bus. If the FDCAN is | ||
| 133 | /// required to send a dominant bit (ACK bit, overload flag, active error flag), the bit is | ||
| 134 | /// rerouted internally so that the FDCAN can monitor it, even if the CAN bus remains in recessive | ||
| 135 | /// state. In Bus monitoring mode the TXBRP register is held in reset state. The Bus monitoring | ||
| 136 | /// mode can be used to analyze the traffic on a CAN bus without affecting it by the transmission | ||
| 137 | /// of dominant bits. | ||
| 138 | BusMonitoringMode, | ||
| 139 | //TestMode, | ||
| 209 | } | 140 | } |
| 210 | 141 | ||
| 211 | /// Operating modes trait | ||
| 212 | pub trait FdcanOperatingMode {} | ||
| 213 | impl FdcanOperatingMode for fdcan::PoweredDownMode {} | ||
| 214 | impl FdcanOperatingMode for fdcan::ConfigMode {} | ||
| 215 | impl FdcanOperatingMode for fdcan::InternalLoopbackMode {} | ||
| 216 | impl FdcanOperatingMode for fdcan::ExternalLoopbackMode {} | ||
| 217 | impl FdcanOperatingMode for fdcan::NormalOperationMode {} | ||
| 218 | impl FdcanOperatingMode for fdcan::RestrictedOperationMode {} | ||
| 219 | impl FdcanOperatingMode for fdcan::BusMonitoringMode {} | ||
| 220 | impl FdcanOperatingMode for fdcan::TestMode {} | ||
| 221 | |||
| 222 | /// FDCAN Instance | 142 | /// FDCAN Instance |
| 223 | pub struct Fdcan<'d, T: Instance, M: FdcanOperatingMode> { | 143 | pub struct FdcanConfigurator<'d, T: Instance> { |
| 144 | config: crate::can::fd::config::FdCanConfig, | ||
| 224 | /// Reference to internals. | 145 | /// Reference to internals. |
| 225 | pub can: fdcan::FdCan<FdcanInstance<'d, T>, M>, | 146 | instance: FdcanInstance<'d, T>, |
| 226 | ns_per_timer_tick: u64, // For FDCAN internal timer | ||
| 227 | } | 147 | } |
| 228 | 148 | ||
| 229 | fn calc_ns_per_timer_tick<T: Instance>(mode: config::FrameTransmissionConfig) -> u64 { | 149 | fn calc_ns_per_timer_tick<T: Instance>(mode: crate::can::fd::config::FrameTransmissionConfig) -> u64 { |
| 230 | match mode { | 150 | match mode { |
| 231 | // Use timestamp from Rx FIFO to adjust timestamp reported to user | 151 | // Use timestamp from Rx FIFO to adjust timestamp reported to user |
| 232 | config::FrameTransmissionConfig::ClassicCanOnly => { | 152 | crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => { |
| 233 | let freq = T::frequency(); | 153 | let freq = T::frequency(); |
| 234 | let prescale: u64 = | 154 | let prescale: u64 = |
| 235 | ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; | 155 | ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; |
| @@ -241,42 +161,7 @@ fn calc_ns_per_timer_tick<T: Instance>(mode: config::FrameTransmissionConfig) -> | |||
| 241 | } | 161 | } |
| 242 | } | 162 | } |
| 243 | 163 | ||
| 244 | #[cfg(feature = "time")] | 164 | impl<'d, T: Instance> FdcanConfigurator<'d, T> { |
| 245 | fn calc_timestamp<T: Instance>(ns_per_timer_tick: u64, ts_val: u16) -> embassy_time::Instant { | ||
| 246 | let now_embassy = embassy_time::Instant::now(); | ||
| 247 | if ns_per_timer_tick == 0 { | ||
| 248 | return now_embassy; | ||
| 249 | } | ||
| 250 | let now_can = { T::regs().tscv().read().tsc() }; | ||
| 251 | let delta = now_can.overflowing_sub(ts_val).0 as u64; | ||
| 252 | let ns = ns_per_timer_tick * delta as u64; | ||
| 253 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 254 | } | ||
| 255 | |||
| 256 | fn curr_error<T: Instance>() -> Option<BusError> { | ||
| 257 | let err = { T::regs().psr().read() }; | ||
| 258 | if err.bo() { | ||
| 259 | return Some(BusError::BusOff); | ||
| 260 | } else if err.ep() { | ||
| 261 | return Some(BusError::BusPassive); | ||
| 262 | } else if err.ew() { | ||
| 263 | return Some(BusError::BusWarning); | ||
| 264 | } else { | ||
| 265 | cfg_if! { | ||
| 266 | if #[cfg(stm32h7)] { | ||
| 267 | let lec = err.lec(); | ||
| 268 | } else { | ||
| 269 | let lec = err.lec().to_bits(); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | if let Ok(err) = LastErrorCode::try_from(lec) { | ||
| 273 | return BusError::try_from(err); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | None | ||
| 277 | } | ||
| 278 | |||
| 279 | impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | ||
| 280 | /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. | 165 | /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. |
| 281 | /// You must call [Fdcan::enable_non_blocking] to use the peripheral. | 166 | /// You must call [Fdcan::enable_non_blocking] to use the peripheral. |
| 282 | pub fn new( | 167 | pub fn new( |
| @@ -286,7 +171,7 @@ impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | |||
| 286 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> | 171 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> |
| 287 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | 172 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> |
| 288 | + 'd, | 173 | + 'd, |
| 289 | ) -> Fdcan<'d, T, fdcan::ConfigMode> { | 174 | ) -> FdcanConfigurator<'d, T> { |
| 290 | into_ref!(peri, rx, tx); | 175 | into_ref!(peri, rx, tx); |
| 291 | 176 | ||
| 292 | rx.set_as_af(rx.af_num(), AFType::Input); | 177 | rx.set_as_af(rx.af_num(), AFType::Input); |
| @@ -294,11 +179,12 @@ impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | |||
| 294 | 179 | ||
| 295 | T::enable_and_reset(); | 180 | T::enable_and_reset(); |
| 296 | 181 | ||
| 182 | let mut config = crate::can::fd::config::FdCanConfig::default(); | ||
| 183 | T::registers().into_config_mode(config); | ||
| 184 | |||
| 297 | rx.set_as_af(rx.af_num(), AFType::Input); | 185 | rx.set_as_af(rx.af_num(), AFType::Input); |
| 298 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | 186 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); |
| 299 | 187 | ||
| 300 | let mut can = fdcan::FdCan::new(FdcanInstance(peri)).into_config_mode(); | ||
| 301 | |||
| 302 | T::configure_msg_ram(); | 188 | T::configure_msg_ram(); |
| 303 | unsafe { | 189 | unsafe { |
| 304 | // Enable timestamping | 190 | // Enable timestamping |
| @@ -308,6 +194,7 @@ impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | |||
| 308 | .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT)); | 194 | .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT)); |
| 309 | #[cfg(stm32h7)] | 195 | #[cfg(stm32h7)] |
| 310 | T::regs().tscc().write(|w| w.set_tss(0x01)); | 196 | T::regs().tscc().write(|w| w.set_tss(0x01)); |
| 197 | config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); | ||
| 311 | 198 | ||
| 312 | T::IT0Interrupt::unpend(); // Not unsafe | 199 | T::IT0Interrupt::unpend(); // Not unsafe |
| 313 | T::IT0Interrupt::enable(); | 200 | T::IT0Interrupt::enable(); |
| @@ -320,94 +207,134 @@ impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | |||
| 320 | T::regs().txbtie().write(|w| w.0 = 0xffff_ffff); | 207 | T::regs().txbtie().write(|w| w.0 = 0xffff_ffff); |
| 321 | } | 208 | } |
| 322 | 209 | ||
| 323 | can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo0NewMsg); | 210 | T::regs().ie().modify(|w| { |
| 324 | can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo1NewMsg); | 211 | w.set_rfne(0, true); // Rx Fifo 0 New Msg |
| 325 | can.enable_interrupt(fdcan::interrupt::Interrupt::TxComplete); | 212 | w.set_rfne(1, true); // Rx Fifo 1 New Msg |
| 326 | can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_0, true); | 213 | w.set_tce(true); // Tx Complete |
| 327 | can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_1, true); | 214 | }); |
| 215 | T::regs().ile().modify(|w| { | ||
| 216 | w.set_eint0(true); // Interrupt Line 0 | ||
| 217 | w.set_eint1(true); // Interrupt Line 1 | ||
| 218 | }); | ||
| 219 | |||
| 220 | Self { | ||
| 221 | config, | ||
| 222 | instance: FdcanInstance(peri), | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | /// Get configuration | ||
| 227 | pub fn config(&self) -> crate::can::fd::config::FdCanConfig { | ||
| 228 | return self.config; | ||
| 229 | } | ||
| 328 | 230 | ||
| 329 | let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(can.get_config().frame_transmit); | 231 | /// Set configuration |
| 330 | Self { can, ns_per_timer_tick } | 232 | pub fn set_config(&mut self, config: crate::can::fd::config::FdCanConfig) { |
| 233 | self.config = config; | ||
| 331 | } | 234 | } |
| 332 | 235 | ||
| 333 | /// Configures the bit timings calculated from supplied bitrate. | 236 | /// Configures the bit timings calculated from supplied bitrate. |
| 334 | pub fn set_bitrate(&mut self, bitrate: u32) { | 237 | pub fn set_bitrate(&mut self, bitrate: u32) { |
| 335 | let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); | 238 | let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); |
| 336 | self.can.set_nominal_bit_timing(config::NominalBitTiming { | 239 | |
| 240 | let nbtr = crate::can::fd::config::NominalBitTiming { | ||
| 337 | sync_jump_width: bit_timing.sync_jump_width, | 241 | sync_jump_width: bit_timing.sync_jump_width, |
| 338 | prescaler: bit_timing.prescaler, | 242 | prescaler: bit_timing.prescaler, |
| 339 | seg1: bit_timing.seg1, | 243 | seg1: bit_timing.seg1, |
| 340 | seg2: bit_timing.seg2, | 244 | seg2: bit_timing.seg2, |
| 341 | }); | 245 | }; |
| 246 | self.config = self.config.set_nominal_bit_timing(nbtr); | ||
| 342 | } | 247 | } |
| 343 | } | ||
| 344 | 248 | ||
| 345 | macro_rules! impl_transition { | 249 | /// Configures the bit timings for VBR data calculated from supplied bitrate. This also sets confit to allow can FD and VBR |
| 346 | ($from_mode:ident, $to_mode:ident, $name:ident, $func: ident) => { | 250 | pub fn set_fd_data_bitrate(&mut self, bitrate: u32, transceiver_delay_compensation: bool) { |
| 347 | impl<'d, T: Instance> Fdcan<'d, T, fdcan::$from_mode> { | 251 | let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); |
| 348 | /// Transition from $from_mode:ident mode to $to_mode:ident mode | 252 | // Note, used existing calcluation for normal(non-VBR) bitrate, appears to work for 250k/1M |
| 349 | pub fn $name(self) -> Fdcan<'d, T, fdcan::$to_mode> { | 253 | let nbtr = crate::can::fd::config::DataBitTiming { |
| 350 | let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.can.get_config().frame_transmit); | 254 | transceiver_delay_compensation, |
| 351 | Fdcan { | 255 | sync_jump_width: bit_timing.sync_jump_width, |
| 352 | can: self.can.$func(), | 256 | prescaler: bit_timing.prescaler, |
| 353 | ns_per_timer_tick, | 257 | seg1: bit_timing.seg1, |
| 354 | } | 258 | seg2: bit_timing.seg2, |
| 355 | } | 259 | }; |
| 260 | self.config.frame_transmit = FrameTransmissionConfig::AllowFdCanAndBRS; | ||
| 261 | self.config = self.config.set_data_bit_timing(nbtr); | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Set an Standard Address CAN filter into slot 'id' | ||
| 265 | #[inline] | ||
| 266 | pub fn set_standard_filter(&mut self, slot: StandardFilterSlot, filter: StandardFilter) { | ||
| 267 | T::registers().msg_ram_mut().filters.flssa[slot as usize].activate(filter); | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Set an array of Standard Address CAN filters and overwrite the current set | ||
| 271 | pub fn set_standard_filters(&mut self, filters: &[StandardFilter; STANDARD_FILTER_MAX as usize]) { | ||
| 272 | for (i, f) in filters.iter().enumerate() { | ||
| 273 | T::registers().msg_ram_mut().filters.flssa[i].activate(*f); | ||
| 356 | } | 274 | } |
| 357 | }; | 275 | } |
| 358 | } | ||
| 359 | 276 | ||
| 360 | impl_transition!(PoweredDownMode, ConfigMode, into_config_mode, into_config_mode); | 277 | /// Set an Extended Address CAN filter into slot 'id' |
| 361 | impl_transition!(InternalLoopbackMode, ConfigMode, into_config_mode, into_config_mode); | 278 | #[inline] |
| 279 | pub fn set_extended_filter(&mut self, slot: ExtendedFilterSlot, filter: ExtendedFilter) { | ||
| 280 | T::registers().msg_ram_mut().filters.flesa[slot as usize].activate(filter); | ||
| 281 | } | ||
| 362 | 282 | ||
| 363 | impl_transition!(ConfigMode, NormalOperationMode, into_normal_mode, into_normal); | 283 | /// Set an array of Extended Address CAN filters and overwrite the current set |
| 364 | impl_transition!( | 284 | pub fn set_extended_filters(&mut self, filters: &[ExtendedFilter; EXTENDED_FILTER_MAX as usize]) { |
| 365 | ConfigMode, | 285 | for (i, f) in filters.iter().enumerate() { |
| 366 | ExternalLoopbackMode, | 286 | T::registers().msg_ram_mut().filters.flesa[i].activate(*f); |
| 367 | into_external_loopback_mode, | 287 | } |
| 368 | into_external_loopback | 288 | } |
| 369 | ); | ||
| 370 | impl_transition!( | ||
| 371 | ConfigMode, | ||
| 372 | InternalLoopbackMode, | ||
| 373 | into_internal_loopback_mode, | ||
| 374 | into_internal_loopback | ||
| 375 | ); | ||
| 376 | 289 | ||
| 377 | impl<'d, T: Instance, M: FdcanOperatingMode> Fdcan<'d, T, M> | 290 | /// Start in mode. |
| 378 | where | 291 | pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> { |
| 379 | M: fdcan::Transmit, | 292 | let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.config.frame_transmit); |
| 380 | M: fdcan::Receive, | 293 | critical_section::with(|_| unsafe { |
| 381 | { | 294 | T::mut_state().ns_per_timer_tick = ns_per_timer_tick; |
| 382 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 295 | }); |
| 383 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | 296 | T::registers().into_mode(self.config, mode); |
| 384 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 297 | let ret = Fdcan { |
| 385 | /// transmitted, then tries again. | 298 | config: self.config, |
| 386 | pub async fn write(&mut self, frame: &TxFrame) -> Option<TxFrame> { | 299 | instance: self.instance, |
| 387 | poll_fn(|cx| { | 300 | _mode: mode, |
| 388 | T::state().tx_waker.register(cx.waker()); | 301 | }; |
| 389 | if let Ok(dropped) = self | 302 | ret |
| 390 | .can | 303 | } |
| 391 | .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { | ||
| 392 | TxFrame::from_preserved(hdr, data32) | ||
| 393 | }) | ||
| 394 | { | ||
| 395 | return Poll::Ready(dropped.flatten()); | ||
| 396 | } | ||
| 397 | 304 | ||
| 398 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | 305 | /// Start, entering mode. Does same as start(mode) |
| 399 | // to clear. | 306 | pub fn into_normal_mode(self) -> Fdcan<'d, T> { |
| 400 | Poll::Pending | 307 | self.start(FdcanOperatingMode::NormalOperationMode) |
| 401 | }) | ||
| 402 | .await | ||
| 403 | } | 308 | } |
| 404 | 309 | ||
| 310 | /// Start, entering mode. Does same as start(mode) | ||
| 311 | pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> { | ||
| 312 | self.start(FdcanOperatingMode::InternalLoopbackMode) | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Start, entering mode. Does same as start(mode) | ||
| 316 | pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> { | ||
| 317 | self.start(FdcanOperatingMode::ExternalLoopbackMode) | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | /// FDCAN Instance | ||
| 322 | pub struct Fdcan<'d, T: Instance> { | ||
| 323 | config: crate::can::fd::config::FdCanConfig, | ||
| 324 | /// Reference to internals. | ||
| 325 | instance: FdcanInstance<'d, T>, | ||
| 326 | _mode: FdcanOperatingMode, | ||
| 327 | } | ||
| 328 | |||
| 329 | impl<'d, T: Instance> Fdcan<'d, T> { | ||
| 405 | /// Flush one of the TX mailboxes. | 330 | /// Flush one of the TX mailboxes. |
| 406 | pub async fn flush(&self, mb: fdcan::Mailbox) { | 331 | pub async fn flush(&self, idx: usize) { |
| 407 | poll_fn(|cx| { | 332 | poll_fn(|cx| { |
| 408 | T::state().tx_waker.register(cx.waker()); | 333 | T::state().tx_mode.register(cx.waker()); |
| 409 | 334 | ||
| 410 | let idx: u8 = mb.into(); | 335 | if idx > 3 { |
| 336 | panic!("Bad mailbox"); | ||
| 337 | } | ||
| 411 | let idx = 1 << idx; | 338 | let idx = 1 << idx; |
| 412 | if !T::regs().txbrp().read().trp(idx) { | 339 | if !T::regs().txbrp().read().trp(idx) { |
| 413 | return Poll::Ready(()); | 340 | return Poll::Ready(()); |
| @@ -418,179 +345,454 @@ where | |||
| 418 | .await; | 345 | .await; |
| 419 | } | 346 | } |
| 420 | 347 | ||
| 348 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 349 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 350 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 351 | /// transmitted, then tries again. | ||
| 352 | pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> { | ||
| 353 | T::state().tx_mode.write::<T>(frame).await | ||
| 354 | } | ||
| 355 | |||
| 421 | /// Returns the next received message frame | 356 | /// Returns the next received message frame |
| 422 | pub async fn read(&mut self) -> Result<RxFrame, BusError> { | 357 | pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { |
| 423 | poll_fn(|cx| { | 358 | T::state().rx_mode.read::<T>().await |
| 424 | T::state().err_waker.register(cx.waker()); | 359 | } |
| 425 | T::state().rx_waker.register(cx.waker()); | 360 | |
| 426 | 361 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | |
| 427 | let mut buffer: [u8; 64] = [0; 64]; | 362 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames |
| 428 | if let Ok(rx) = self.can.receive0(&mut buffer) { | 363 | /// can be replaced, this call asynchronously waits for a frame to be successfully |
| 429 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | 364 | /// transmitted, then tries again. |
| 430 | // TODO: report overrun? | 365 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { |
| 431 | // for now we just drop it | 366 | T::state().tx_mode.write_fd::<T>(frame).await |
| 432 | 367 | } | |
| 433 | let frame: RxFrame = RxFrame::new( | 368 | |
| 434 | rx.unwrap(), | 369 | /// Returns the next received message frame |
| 435 | &buffer, | 370 | pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { |
| 436 | #[cfg(feature = "time")] | 371 | T::state().rx_mode.read_fd::<T>().await |
| 437 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 438 | ); | ||
| 439 | return Poll::Ready(Ok(frame)); | ||
| 440 | } else if let Ok(rx) = self.can.receive1(&mut buffer) { | ||
| 441 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 442 | // TODO: report overrun? | ||
| 443 | // for now we just drop it | ||
| 444 | |||
| 445 | let frame: RxFrame = RxFrame::new( | ||
| 446 | rx.unwrap(), | ||
| 447 | &buffer, | ||
| 448 | #[cfg(feature = "time")] | ||
| 449 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 450 | ); | ||
| 451 | return Poll::Ready(Ok(frame)); | ||
| 452 | } else if let Some(err) = curr_error::<T>() { | ||
| 453 | // TODO: this is probably wrong | ||
| 454 | return Poll::Ready(Err(err)); | ||
| 455 | } | ||
| 456 | Poll::Pending | ||
| 457 | }) | ||
| 458 | .await | ||
| 459 | } | 372 | } |
| 460 | 373 | ||
| 461 | /// Split instance into separate Tx(write) and Rx(read) portions | 374 | /// Split instance into separate Tx(write) and Rx(read) portions |
| 462 | pub fn split<'c>(&'c mut self) -> (FdcanTx<'c, 'd, T, M>, FdcanRx<'c, 'd, T, M>) { | 375 | pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) { |
| 463 | let (mut _control, tx, rx0, rx1) = self.can.split_by_ref(); | ||
| 464 | ( | 376 | ( |
| 465 | FdcanTx { _control, tx }, | 377 | FdcanTx { |
| 378 | config: self.config, | ||
| 379 | _instance: self.instance, | ||
| 380 | _mode: self._mode, | ||
| 381 | }, | ||
| 466 | FdcanRx { | 382 | FdcanRx { |
| 467 | rx0, | 383 | _instance1: PhantomData::<T>, |
| 468 | rx1, | 384 | _instance2: T::regs(), |
| 469 | ns_per_timer_tick: self.ns_per_timer_tick, | 385 | _mode: self._mode, |
| 470 | }, | 386 | }, |
| 471 | ) | 387 | ) |
| 472 | } | 388 | } |
| 389 | |||
| 390 | /// Join split rx and tx portions back together | ||
| 391 | pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self { | ||
| 392 | Fdcan { | ||
| 393 | config: tx.config, | ||
| 394 | //_instance2: T::regs(), | ||
| 395 | instance: tx._instance, | ||
| 396 | _mode: rx._mode, | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | /// Return a buffered instance of driver without CAN FD support. User must supply Buffers | ||
| 401 | pub fn buffered<const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>( | ||
| 402 | &self, | ||
| 403 | tx_buf: &'static mut TxBuf<TX_BUF_SIZE>, | ||
| 404 | rxb: &'static mut RxBuf<RX_BUF_SIZE>, | ||
| 405 | ) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { | ||
| 406 | BufferedCan::new(PhantomData::<T>, T::regs(), self._mode, tx_buf, rxb) | ||
| 407 | } | ||
| 408 | |||
| 409 | /// Return a buffered instance of driver with CAN FD support. User must supply Buffers | ||
| 410 | pub fn buffered_fd<const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>( | ||
| 411 | &self, | ||
| 412 | tx_buf: &'static mut TxFdBuf<TX_BUF_SIZE>, | ||
| 413 | rxb: &'static mut RxFdBuf<RX_BUF_SIZE>, | ||
| 414 | ) -> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { | ||
| 415 | BufferedCanFd::new(PhantomData::<T>, T::regs(), self._mode, tx_buf, rxb) | ||
| 416 | } | ||
| 473 | } | 417 | } |
| 474 | 418 | ||
| 475 | /// FDCAN Tx only Instance | 419 | /// User supplied buffer for RX Buffering |
| 476 | pub struct FdcanTx<'c, 'd, T: Instance, M: fdcan::Transmit> { | 420 | pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, (ClassicFrame, Timestamp), BUF_SIZE>; |
| 477 | _control: &'c mut fdcan::FdCanControl<FdcanInstance<'d, T>, M>, | 421 | |
| 478 | tx: &'c mut fdcan::Tx<FdcanInstance<'d, T>, M>, | 422 | /// User supplied buffer for TX buffering |
| 423 | pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, ClassicFrame, BUF_SIZE>; | ||
| 424 | |||
| 425 | /// Buffered FDCAN Instance | ||
| 426 | pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | ||
| 427 | _instance1: PhantomData<T>, | ||
| 428 | _instance2: &'d crate::pac::can::Fdcan, | ||
| 429 | _mode: FdcanOperatingMode, | ||
| 430 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, | ||
| 431 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, | ||
| 479 | } | 432 | } |
| 480 | 433 | ||
| 481 | impl<'c, 'd, T: Instance, M: fdcan::Transmit> FdcanTx<'c, 'd, T, M> { | 434 | impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> |
| 482 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | 435 | BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> |
| 483 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | 436 | { |
| 484 | /// can be replaced, this call asynchronously waits for a frame to be successfully | 437 | fn new( |
| 485 | /// transmitted, then tries again. | 438 | _instance1: PhantomData<T>, |
| 486 | pub async fn write(&mut self, frame: &TxFrame) -> Option<TxFrame> { | 439 | _instance2: &'d crate::pac::can::Fdcan, |
| 487 | poll_fn(|cx| { | 440 | _mode: FdcanOperatingMode, |
| 488 | T::state().tx_waker.register(cx.waker()); | 441 | tx_buf: &'static TxBuf<TX_BUF_SIZE>, |
| 489 | if let Ok(dropped) = self | 442 | rx_buf: &'static RxBuf<RX_BUF_SIZE>, |
| 490 | .tx | 443 | ) -> Self { |
| 491 | .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { | 444 | BufferedCan { |
| 492 | TxFrame::from_preserved(hdr, data32) | 445 | _instance1, |
| 493 | }) | 446 | _instance2, |
| 494 | { | 447 | _mode, |
| 495 | return Poll::Ready(dropped.flatten()); | 448 | tx_buf, |
| 496 | } | 449 | rx_buf, |
| 450 | } | ||
| 451 | .setup() | ||
| 452 | } | ||
| 497 | 453 | ||
| 498 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | 454 | fn setup(self) -> Self { |
| 499 | // to clear. | 455 | // We don't want interrupts being processed while we change modes. |
| 500 | Poll::Pending | 456 | critical_section::with(|_| unsafe { |
| 501 | }) | 457 | let rx_inner = sealed::ClassicBufferedRxInner { |
| 502 | .await | 458 | rx_sender: self.rx_buf.sender().into(), |
| 459 | }; | ||
| 460 | let tx_inner = sealed::ClassicBufferedTxInner { | ||
| 461 | tx_receiver: self.tx_buf.receiver().into(), | ||
| 462 | }; | ||
| 463 | T::mut_state().rx_mode = sealed::RxMode::ClassicBuffered(rx_inner); | ||
| 464 | T::mut_state().tx_mode = sealed::TxMode::ClassicBuffered(tx_inner); | ||
| 465 | }); | ||
| 466 | self | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Async write frame to TX buffer. | ||
| 470 | pub async fn write(&mut self, frame: ClassicFrame) { | ||
| 471 | self.tx_buf.send(frame).await; | ||
| 472 | T::IT0Interrupt::pend(); // Wake for Tx | ||
| 473 | } | ||
| 474 | |||
| 475 | /// Async read frame from RX buffer. | ||
| 476 | pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { | ||
| 477 | Ok(self.rx_buf.receive().await) | ||
| 503 | } | 478 | } |
| 504 | } | 479 | } |
| 505 | 480 | ||
| 506 | /// FDCAN Rx only Instance | 481 | impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop |
| 507 | #[allow(dead_code)] | 482 | for BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> |
| 508 | pub struct FdcanRx<'c, 'd, T: Instance, M: fdcan::Receive> { | 483 | { |
| 509 | rx0: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo0>, | 484 | fn drop(&mut self) { |
| 510 | rx1: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo1>, | 485 | critical_section::with(|_| unsafe { |
| 511 | ns_per_timer_tick: u64, // For FDCAN internal timer | 486 | T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); |
| 487 | T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 488 | }); | ||
| 489 | } | ||
| 512 | } | 490 | } |
| 513 | 491 | ||
| 514 | impl<'c, 'd, T: Instance, M: fdcan::Receive> FdcanRx<'c, 'd, T, M> { | 492 | /// User supplied buffer for RX Buffering |
| 515 | /// Returns the next received message frame | 493 | pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, (FdFrame, Timestamp), BUF_SIZE>; |
| 516 | pub async fn read(&mut self) -> Result<RxFrame, BusError> { | ||
| 517 | poll_fn(|cx| { | ||
| 518 | T::state().err_waker.register(cx.waker()); | ||
| 519 | T::state().rx_waker.register(cx.waker()); | ||
| 520 | |||
| 521 | let mut buffer: [u8; 64] = [0; 64]; | ||
| 522 | if let Ok(rx) = self.rx0.receive(&mut buffer) { | ||
| 523 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 524 | // TODO: report overrun? | ||
| 525 | // for now we just drop it | ||
| 526 | let frame: RxFrame = RxFrame::new( | ||
| 527 | rx.unwrap(), | ||
| 528 | &buffer, | ||
| 529 | #[cfg(feature = "time")] | ||
| 530 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 531 | ); | ||
| 532 | return Poll::Ready(Ok(frame)); | ||
| 533 | } else if let Ok(rx) = self.rx1.receive(&mut buffer) { | ||
| 534 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 535 | // TODO: report overrun? | ||
| 536 | // for now we just drop it | ||
| 537 | let frame: RxFrame = RxFrame::new( | ||
| 538 | rx.unwrap(), | ||
| 539 | &buffer, | ||
| 540 | #[cfg(feature = "time")] | ||
| 541 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 542 | ); | ||
| 543 | return Poll::Ready(Ok(frame)); | ||
| 544 | } else if let Some(err) = curr_error::<T>() { | ||
| 545 | // TODO: this is probably wrong | ||
| 546 | return Poll::Ready(Err(err)); | ||
| 547 | } | ||
| 548 | 494 | ||
| 549 | Poll::Pending | 495 | /// User supplied buffer for TX buffering |
| 550 | }) | 496 | pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>; |
| 551 | .await | 497 | |
| 498 | /// Buffered FDCAN Instance | ||
| 499 | pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { | ||
| 500 | _instance1: PhantomData<T>, | ||
| 501 | _instance2: &'d crate::pac::can::Fdcan, | ||
| 502 | _mode: FdcanOperatingMode, | ||
| 503 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | ||
| 504 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | ||
| 505 | } | ||
| 506 | |||
| 507 | impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> | ||
| 508 | BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> | ||
| 509 | { | ||
| 510 | fn new( | ||
| 511 | _instance1: PhantomData<T>, | ||
| 512 | _instance2: &'d crate::pac::can::Fdcan, | ||
| 513 | _mode: FdcanOperatingMode, | ||
| 514 | tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, | ||
| 515 | rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, | ||
| 516 | ) -> Self { | ||
| 517 | BufferedCanFd { | ||
| 518 | _instance1, | ||
| 519 | _instance2, | ||
| 520 | _mode, | ||
| 521 | tx_buf, | ||
| 522 | rx_buf, | ||
| 523 | } | ||
| 524 | .setup() | ||
| 525 | } | ||
| 526 | |||
| 527 | fn setup(self) -> Self { | ||
| 528 | // We don't want interrupts being processed while we change modes. | ||
| 529 | critical_section::with(|_| unsafe { | ||
| 530 | let rx_inner = sealed::FdBufferedRxInner { | ||
| 531 | rx_sender: self.rx_buf.sender().into(), | ||
| 532 | }; | ||
| 533 | let tx_inner = sealed::FdBufferedTxInner { | ||
| 534 | tx_receiver: self.tx_buf.receiver().into(), | ||
| 535 | }; | ||
| 536 | T::mut_state().rx_mode = sealed::RxMode::FdBuffered(rx_inner); | ||
| 537 | T::mut_state().tx_mode = sealed::TxMode::FdBuffered(tx_inner); | ||
| 538 | }); | ||
| 539 | self | ||
| 540 | } | ||
| 541 | |||
| 542 | /// Async write frame to TX buffer. | ||
| 543 | pub async fn write(&mut self, frame: FdFrame) { | ||
| 544 | self.tx_buf.send(frame).await; | ||
| 545 | T::IT0Interrupt::pend(); // Wake for Tx | ||
| 546 | } | ||
| 547 | |||
| 548 | /// Async read frame from RX buffer. | ||
| 549 | pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> { | ||
| 550 | Ok(self.rx_buf.receive().await) | ||
| 552 | } | 551 | } |
| 553 | } | 552 | } |
| 554 | impl<'d, T: Instance, M: FdcanOperatingMode> Deref for Fdcan<'d, T, M> { | ||
| 555 | type Target = fdcan::FdCan<FdcanInstance<'d, T>, M>; | ||
| 556 | 553 | ||
| 557 | fn deref(&self) -> &Self::Target { | 554 | impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop |
| 558 | &self.can | 555 | for BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> |
| 556 | { | ||
| 557 | fn drop(&mut self) { | ||
| 558 | critical_section::with(|_| unsafe { | ||
| 559 | T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 560 | T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); | ||
| 561 | }); | ||
| 559 | } | 562 | } |
| 560 | } | 563 | } |
| 561 | 564 | ||
| 562 | impl<'d, T: Instance, M: FdcanOperatingMode> DerefMut for Fdcan<'d, T, M> { | 565 | /// FDCAN Rx only Instance |
| 563 | fn deref_mut(&mut self) -> &mut Self::Target { | 566 | pub struct FdcanRx<'d, T: Instance> { |
| 564 | &mut self.can | 567 | _instance1: PhantomData<T>, |
| 568 | _instance2: &'d crate::pac::can::Fdcan, | ||
| 569 | _mode: FdcanOperatingMode, | ||
| 570 | } | ||
| 571 | |||
| 572 | /// FDCAN Tx only Instance | ||
| 573 | pub struct FdcanTx<'d, T: Instance> { | ||
| 574 | config: crate::can::fd::config::FdCanConfig, | ||
| 575 | _instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>); | ||
| 576 | _mode: FdcanOperatingMode, | ||
| 577 | } | ||
| 578 | |||
| 579 | impl<'c, 'd, T: Instance> FdcanTx<'d, T> { | ||
| 580 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 581 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 582 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 583 | /// transmitted, then tries again. | ||
| 584 | pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> { | ||
| 585 | T::state().tx_mode.write::<T>(frame).await | ||
| 586 | } | ||
| 587 | |||
| 588 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 589 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 590 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 591 | /// transmitted, then tries again. | ||
| 592 | pub async fn write_fd(&mut self, frame: &FdFrame) -> Option<FdFrame> { | ||
| 593 | T::state().tx_mode.write_fd::<T>(frame).await | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | impl<'c, 'd, T: Instance> FdcanRx<'d, T> { | ||
| 598 | /// Returns the next received message frame | ||
| 599 | pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { | ||
| 600 | T::state().rx_mode.read::<T>().await | ||
| 601 | } | ||
| 602 | |||
| 603 | /// Returns the next received message frame | ||
| 604 | pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { | ||
| 605 | T::state().rx_mode.read_fd::<T>().await | ||
| 565 | } | 606 | } |
| 566 | } | 607 | } |
| 567 | 608 | ||
| 568 | pub(crate) mod sealed { | 609 | pub(crate) mod sealed { |
| 610 | use core::future::poll_fn; | ||
| 611 | use core::task::Poll; | ||
| 612 | |||
| 613 | use embassy_sync::channel::{DynamicReceiver, DynamicSender}; | ||
| 569 | use embassy_sync::waitqueue::AtomicWaker; | 614 | use embassy_sync::waitqueue::AtomicWaker; |
| 570 | 615 | ||
| 616 | use crate::can::_version::{BusError, Timestamp}; | ||
| 617 | use crate::can::frame::{ClassicFrame, FdFrame}; | ||
| 618 | |||
| 619 | pub struct ClassicBufferedRxInner { | ||
| 620 | pub rx_sender: DynamicSender<'static, (ClassicFrame, Timestamp)>, | ||
| 621 | } | ||
| 622 | pub struct ClassicBufferedTxInner { | ||
| 623 | pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, | ||
| 624 | } | ||
| 625 | |||
| 626 | pub struct FdBufferedRxInner { | ||
| 627 | pub rx_sender: DynamicSender<'static, (FdFrame, Timestamp)>, | ||
| 628 | } | ||
| 629 | pub struct FdBufferedTxInner { | ||
| 630 | pub tx_receiver: DynamicReceiver<'static, FdFrame>, | ||
| 631 | } | ||
| 632 | |||
| 633 | pub enum RxMode { | ||
| 634 | NonBuffered(AtomicWaker), | ||
| 635 | ClassicBuffered(ClassicBufferedRxInner), | ||
| 636 | FdBuffered(FdBufferedRxInner), | ||
| 637 | } | ||
| 638 | |||
| 639 | impl RxMode { | ||
| 640 | pub fn register(&self, arg: &core::task::Waker) { | ||
| 641 | match self { | ||
| 642 | RxMode::NonBuffered(waker) => waker.register(arg), | ||
| 643 | _ => { | ||
| 644 | panic!("Bad Mode") | ||
| 645 | } | ||
| 646 | } | ||
| 647 | } | ||
| 648 | |||
| 649 | pub fn on_interrupt<T: Instance>(&self, fifonr: usize) { | ||
| 650 | T::regs().ir().write(|w| w.set_rfn(fifonr, true)); | ||
| 651 | match self { | ||
| 652 | RxMode::NonBuffered(waker) => { | ||
| 653 | waker.wake(); | ||
| 654 | } | ||
| 655 | RxMode::ClassicBuffered(buf) => { | ||
| 656 | if let Some(r) = T::registers().read_classic(fifonr) { | ||
| 657 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1); | ||
| 658 | let _ = buf.rx_sender.try_send((r.0, ts)); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | RxMode::FdBuffered(buf) => { | ||
| 662 | if let Some(r) = T::registers().read_fd(fifonr) { | ||
| 663 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1); | ||
| 664 | let _ = buf.rx_sender.try_send((r.0, ts)); | ||
| 665 | } | ||
| 666 | } | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | pub async fn read<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> { | ||
| 671 | poll_fn(|cx| { | ||
| 672 | T::state().err_waker.register(cx.waker()); | ||
| 673 | self.register(cx.waker()); | ||
| 674 | |||
| 675 | if let Some((msg, ts)) = T::registers().read_classic(0) { | ||
| 676 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | ||
| 677 | return Poll::Ready(Ok((msg, ts))); | ||
| 678 | } else if let Some((msg, ts)) = T::registers().read_classic(1) { | ||
| 679 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | ||
| 680 | return Poll::Ready(Ok((msg, ts))); | ||
| 681 | } else if let Some(err) = T::registers().curr_error() { | ||
| 682 | // TODO: this is probably wrong | ||
| 683 | return Poll::Ready(Err(err)); | ||
| 684 | } | ||
| 685 | Poll::Pending | ||
| 686 | }) | ||
| 687 | .await | ||
| 688 | } | ||
| 689 | |||
| 690 | pub async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> { | ||
| 691 | poll_fn(|cx| { | ||
| 692 | T::state().err_waker.register(cx.waker()); | ||
| 693 | self.register(cx.waker()); | ||
| 694 | |||
| 695 | if let Some((msg, ts)) = T::registers().read_fd(0) { | ||
| 696 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | ||
| 697 | return Poll::Ready(Ok((msg, ts))); | ||
| 698 | } else if let Some((msg, ts)) = T::registers().read_fd(1) { | ||
| 699 | let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); | ||
| 700 | return Poll::Ready(Ok((msg, ts))); | ||
| 701 | } else if let Some(err) = T::registers().curr_error() { | ||
| 702 | // TODO: this is probably wrong | ||
| 703 | return Poll::Ready(Err(err)); | ||
| 704 | } | ||
| 705 | Poll::Pending | ||
| 706 | }) | ||
| 707 | .await | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 711 | pub enum TxMode { | ||
| 712 | NonBuffered(AtomicWaker), | ||
| 713 | ClassicBuffered(ClassicBufferedTxInner), | ||
| 714 | FdBuffered(FdBufferedTxInner), | ||
| 715 | } | ||
| 716 | |||
| 717 | impl TxMode { | ||
| 718 | pub fn register(&self, arg: &core::task::Waker) { | ||
| 719 | match self { | ||
| 720 | TxMode::NonBuffered(waker) => { | ||
| 721 | waker.register(arg); | ||
| 722 | } | ||
| 723 | _ => { | ||
| 724 | panic!("Bad mode"); | ||
| 725 | } | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 730 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 731 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 732 | /// transmitted, then tries again. | ||
| 733 | pub async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> { | ||
| 734 | poll_fn(|cx| { | ||
| 735 | self.register(cx.waker()); | ||
| 736 | |||
| 737 | if let Ok(dropped) = T::registers().write_classic(frame) { | ||
| 738 | return Poll::Ready(dropped); | ||
| 739 | } | ||
| 740 | |||
| 741 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | ||
| 742 | // to clear. | ||
| 743 | Poll::Pending | ||
| 744 | }) | ||
| 745 | .await | ||
| 746 | } | ||
| 747 | |||
| 748 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 749 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 750 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 751 | /// transmitted, then tries again. | ||
| 752 | pub async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> { | ||
| 753 | poll_fn(|cx| { | ||
| 754 | self.register(cx.waker()); | ||
| 755 | |||
| 756 | if let Ok(dropped) = T::registers().write_fd(frame) { | ||
| 757 | return Poll::Ready(dropped); | ||
| 758 | } | ||
| 759 | |||
| 760 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | ||
| 761 | // to clear. | ||
| 762 | Poll::Pending | ||
| 763 | }) | ||
| 764 | .await | ||
| 765 | } | ||
| 766 | } | ||
| 767 | |||
| 571 | pub struct State { | 768 | pub struct State { |
| 572 | pub tx_waker: AtomicWaker, | 769 | pub rx_mode: RxMode, |
| 770 | pub tx_mode: TxMode, | ||
| 771 | pub ns_per_timer_tick: u64, | ||
| 772 | |||
| 573 | pub err_waker: AtomicWaker, | 773 | pub err_waker: AtomicWaker, |
| 574 | pub rx_waker: AtomicWaker, | ||
| 575 | } | 774 | } |
| 576 | 775 | ||
| 577 | impl State { | 776 | impl State { |
| 578 | pub const fn new() -> Self { | 777 | pub const fn new() -> Self { |
| 579 | Self { | 778 | Self { |
| 580 | tx_waker: AtomicWaker::new(), | 779 | rx_mode: RxMode::NonBuffered(AtomicWaker::new()), |
| 780 | tx_mode: TxMode::NonBuffered(AtomicWaker::new()), | ||
| 781 | ns_per_timer_tick: 0, | ||
| 581 | err_waker: AtomicWaker::new(), | 782 | err_waker: AtomicWaker::new(), |
| 582 | rx_waker: AtomicWaker::new(), | ||
| 583 | } | 783 | } |
| 584 | } | 784 | } |
| 585 | } | 785 | } |
| 586 | 786 | ||
| 587 | pub trait Instance { | 787 | pub trait Instance { |
| 588 | const REGISTERS: *mut fdcan::RegisterBlock; | ||
| 589 | const MSG_RAM: *mut fdcan::message_ram::RegisterBlock; | ||
| 590 | const MSG_RAM_OFFSET: usize; | 788 | const MSG_RAM_OFFSET: usize; |
| 591 | 789 | ||
| 592 | fn regs() -> &'static crate::pac::can::Fdcan; | 790 | fn regs() -> &'static crate::pac::can::Fdcan; |
| 791 | fn registers() -> crate::can::fd::peripheral::Registers; | ||
| 792 | fn ram() -> &'static crate::pac::fdcanram::Fdcanram; | ||
| 593 | fn state() -> &'static State; | 793 | fn state() -> &'static State; |
| 794 | unsafe fn mut_state() -> &'static mut State; | ||
| 795 | fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp; | ||
| 594 | 796 | ||
| 595 | #[cfg(not(stm32h7))] | 797 | #[cfg(not(stm32h7))] |
| 596 | fn configure_msg_ram() {} | 798 | fn configure_msg_ram() {} |
| @@ -599,7 +801,8 @@ pub(crate) mod sealed { | |||
| 599 | fn configure_msg_ram() { | 801 | fn configure_msg_ram() { |
| 600 | let r = Self::regs(); | 802 | let r = Self::regs(); |
| 601 | 803 | ||
| 602 | use fdcan::message_ram::*; | 804 | use crate::can::fd::message_ram::*; |
| 805 | //use fdcan::message_ram::*; | ||
| 603 | let mut offset_words = Self::MSG_RAM_OFFSET as u16; | 806 | let mut offset_words = Self::MSG_RAM_OFFSET as u16; |
| 604 | 807 | ||
| 605 | // 11-bit filter | 808 | // 11-bit filter |
| @@ -677,32 +880,45 @@ pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + ' | |||
| 677 | /// Fdcan Instance struct | 880 | /// Fdcan Instance struct |
| 678 | pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); | 881 | pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); |
| 679 | 882 | ||
| 680 | unsafe impl<'d, T: Instance> fdcan::message_ram::Instance for FdcanInstance<'d, T> { | ||
| 681 | const MSG_RAM: *mut RegisterBlock = T::MSG_RAM; | ||
| 682 | } | ||
| 683 | |||
| 684 | unsafe impl<'d, T: Instance> fdcan::Instance for FdcanInstance<'d, T> | ||
| 685 | where | ||
| 686 | FdcanInstance<'d, T>: fdcan::message_ram::Instance, | ||
| 687 | { | ||
| 688 | const REGISTERS: *mut fdcan::RegisterBlock = T::REGISTERS; | ||
| 689 | } | ||
| 690 | |||
| 691 | macro_rules! impl_fdcan { | 883 | macro_rules! impl_fdcan { |
| 692 | ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { | 884 | ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { |
| 693 | impl sealed::Instance for peripherals::$inst { | 885 | impl sealed::Instance for peripherals::$inst { |
| 694 | const REGISTERS: *mut fdcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; | ||
| 695 | const MSG_RAM: *mut fdcan::message_ram::RegisterBlock = crate::pac::$msg_ram_inst.as_ptr() as *mut _; | ||
| 696 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; | 886 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; |
| 697 | 887 | ||
| 698 | fn regs() -> &'static crate::pac::can::Fdcan { | 888 | fn regs() -> &'static crate::pac::can::Fdcan { |
| 699 | &crate::pac::$inst | 889 | &crate::pac::$inst |
| 700 | } | 890 | } |
| 701 | 891 | fn registers() -> Registers { | |
| 892 | Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst} | ||
| 893 | } | ||
| 894 | fn ram() -> &'static crate::pac::fdcanram::Fdcanram { | ||
| 895 | &crate::pac::$msg_ram_inst | ||
| 896 | } | ||
| 897 | unsafe fn mut_state() -> & 'static mut sealed::State { | ||
| 898 | static mut STATE: sealed::State = sealed::State::new(); | ||
| 899 | & mut STATE | ||
| 900 | } | ||
| 702 | fn state() -> &'static sealed::State { | 901 | fn state() -> &'static sealed::State { |
| 703 | static STATE: sealed::State = sealed::State::new(); | 902 | unsafe { peripherals::$inst::mut_state() } |
| 704 | &STATE | ||
| 705 | } | 903 | } |
| 904 | |||
| 905 | #[cfg(feature = "time")] | ||
| 906 | fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 907 | let now_embassy = embassy_time::Instant::now(); | ||
| 908 | if ns_per_timer_tick == 0 { | ||
| 909 | return now_embassy; | ||
| 910 | } | ||
| 911 | let cantime = { Self::regs().tscv().read().tsc() }; | ||
| 912 | let delta = cantime.overflowing_sub(ts_val).0 as u64; | ||
| 913 | let ns = ns_per_timer_tick * delta as u64; | ||
| 914 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 915 | } | ||
| 916 | |||
| 917 | #[cfg(not(feature = "time"))] | ||
| 918 | fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { | ||
| 919 | ts_val | ||
| 920 | } | ||
| 921 | |||
| 706 | } | 922 | } |
| 707 | 923 | ||
| 708 | impl Instance for peripherals::$inst {} | 924 | impl Instance for peripherals::$inst {} |
diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs new file mode 100644 index 000000000..725a9b1ab --- /dev/null +++ b/embassy-stm32/src/can/frame.rs | |||
| @@ -0,0 +1,370 @@ | |||
| 1 | //! Definition for CAN Frames | ||
| 2 | use bit_field::BitField; | ||
| 3 | |||
| 4 | /// CAN Header, without meta data | ||
| 5 | #[derive(Debug, Copy, Clone)] | ||
| 6 | pub struct Header { | ||
| 7 | id: embedded_can::Id, | ||
| 8 | len: u8, | ||
| 9 | flags: u8, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl Header { | ||
| 13 | const FLAG_RTR: usize = 0; // Remote | ||
| 14 | const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN | ||
| 15 | const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN | ||
| 16 | |||
| 17 | /// Create new CAN Header | ||
| 18 | pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header { | ||
| 19 | let mut flags = 0u8; | ||
| 20 | flags.set_bit(Self::FLAG_RTR, rtr); | ||
| 21 | Header { id, len, flags } | ||
| 22 | } | ||
| 23 | |||
| 24 | /// Create new CAN FD Header | ||
| 25 | pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header { | ||
| 26 | let mut flags = 0u8; | ||
| 27 | flags.set_bit(Self::FLAG_RTR, rtr); | ||
| 28 | flags.set_bit(Self::FLAG_FDCAN, true); | ||
| 29 | flags.set_bit(Self::FLAG_BRS, brs); | ||
| 30 | Header { id, len, flags } | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Return ID | ||
| 34 | pub fn id(&self) -> &embedded_can::Id { | ||
| 35 | &self.id | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Return length as u8 | ||
| 39 | pub fn len(&self) -> u8 { | ||
| 40 | self.len | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Is remote frame | ||
| 44 | pub fn rtr(&self) -> bool { | ||
| 45 | self.flags.get_bit(Self::FLAG_RTR) | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Request/is FDCAN frame | ||
| 49 | pub fn fdcan(&self) -> bool { | ||
| 50 | self.flags.get_bit(Self::FLAG_FDCAN) | ||
| 51 | } | ||
| 52 | |||
| 53 | /// Request/is Flexible Data Rate | ||
| 54 | pub fn bit_rate_switching(&self) -> bool { | ||
| 55 | self.flags.get_bit(Self::FLAG_BRS) | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Payload of a classic CAN data frame. | ||
| 60 | /// | ||
| 61 | /// Contains 0 to 8 Bytes of data. | ||
| 62 | #[derive(Debug, Copy, Clone)] | ||
| 63 | pub struct ClassicData { | ||
| 64 | pub(crate) bytes: [u8; 8], | ||
| 65 | } | ||
| 66 | |||
| 67 | impl ClassicData { | ||
| 68 | /// Creates a data payload from a raw byte slice. | ||
| 69 | /// | ||
| 70 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or | ||
| 71 | /// cannot be represented with an FDCAN DLC. | ||
| 72 | pub fn new(data: &[u8]) -> Option<Self> { | ||
| 73 | if !FdData::is_valid_len(data.len()) { | ||
| 74 | return None; | ||
| 75 | } | ||
| 76 | |||
| 77 | let mut bytes = [0; 8]; | ||
| 78 | bytes[..data.len()].copy_from_slice(data); | ||
| 79 | |||
| 80 | Some(Self { bytes }) | ||
| 81 | } | ||
| 82 | |||
| 83 | /// Raw read access to data. | ||
| 84 | pub fn raw(&self) -> &[u8] { | ||
| 85 | &self.bytes | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Checks if the length can be encoded in FDCAN DLC field. | ||
| 89 | pub const fn is_valid_len(len: usize) -> bool { | ||
| 90 | match len { | ||
| 91 | 0..=8 => true, | ||
| 92 | _ => false, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Creates an empty data payload containing 0 bytes. | ||
| 97 | #[inline] | ||
| 98 | pub const fn empty() -> Self { | ||
| 99 | Self { bytes: [0; 8] } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Frame with up to 8 bytes of data payload as per Classic CAN | ||
| 104 | #[derive(Debug, Copy, Clone)] | ||
| 105 | pub struct ClassicFrame { | ||
| 106 | can_header: Header, | ||
| 107 | data: ClassicData, | ||
| 108 | } | ||
| 109 | |||
| 110 | impl ClassicFrame { | ||
| 111 | pub(crate) const MAX_DATA_LEN: usize = 8; | ||
| 112 | |||
| 113 | /// Create a new CAN classic Frame | ||
| 114 | pub fn new(can_header: Header, data: ClassicData) -> ClassicFrame { | ||
| 115 | ClassicFrame { can_header, data } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Create new extended frame | ||
| 119 | pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Option<Self> { | ||
| 120 | if let Some(id) = embedded_can::ExtendedId::new(raw_id) { | ||
| 121 | match ClassicData::new(raw_data) { | ||
| 122 | Some(data) => Some(ClassicFrame::new( | ||
| 123 | Header::new(id.into(), raw_data.len() as u8, false), | ||
| 124 | data, | ||
| 125 | )), | ||
| 126 | None => None, | ||
| 127 | } | ||
| 128 | } else { | ||
| 129 | None | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Create new standard frame | ||
| 134 | pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Option<Self> { | ||
| 135 | if let Some(id) = embedded_can::StandardId::new(raw_id) { | ||
| 136 | match ClassicData::new(raw_data) { | ||
| 137 | Some(data) => Some(ClassicFrame::new( | ||
| 138 | Header::new(id.into(), raw_data.len() as u8, false), | ||
| 139 | data, | ||
| 140 | )), | ||
| 141 | None => None, | ||
| 142 | } | ||
| 143 | } else { | ||
| 144 | None | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Create new remote frame | ||
| 149 | pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { | ||
| 150 | if len <= 8usize { | ||
| 151 | Some(ClassicFrame::new( | ||
| 152 | Header::new(id.into(), len as u8, true), | ||
| 153 | ClassicData::empty(), | ||
| 154 | )) | ||
| 155 | } else { | ||
| 156 | None | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Get reference to data | ||
| 161 | pub fn header(&self) -> &Header { | ||
| 162 | &self.can_header | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Return ID | ||
| 166 | pub fn id(&self) -> &embedded_can::Id { | ||
| 167 | &self.can_header.id | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Get reference to data | ||
| 171 | pub fn data(&self) -> &[u8] { | ||
| 172 | &self.data.raw() | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | impl embedded_can::Frame for ClassicFrame { | ||
| 177 | fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> { | ||
| 178 | match ClassicData::new(raw_data) { | ||
| 179 | Some(data) => Some(ClassicFrame::new( | ||
| 180 | Header::new(id.into(), raw_data.len() as u8, false), | ||
| 181 | data, | ||
| 182 | )), | ||
| 183 | None => None, | ||
| 184 | } | ||
| 185 | } | ||
| 186 | fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { | ||
| 187 | if len <= 8 { | ||
| 188 | Some(ClassicFrame::new( | ||
| 189 | Header::new(id.into(), len as u8, true), | ||
| 190 | ClassicData::empty(), | ||
| 191 | )) | ||
| 192 | } else { | ||
| 193 | None | ||
| 194 | } | ||
| 195 | } | ||
| 196 | fn is_extended(&self) -> bool { | ||
| 197 | match self.can_header.id { | ||
| 198 | embedded_can::Id::Extended(_) => true, | ||
| 199 | embedded_can::Id::Standard(_) => true, | ||
| 200 | } | ||
| 201 | } | ||
| 202 | fn is_remote_frame(&self) -> bool { | ||
| 203 | self.can_header.rtr() | ||
| 204 | } | ||
| 205 | fn id(&self) -> embedded_can::Id { | ||
| 206 | self.can_header.id | ||
| 207 | } | ||
| 208 | fn dlc(&self) -> usize { | ||
| 209 | self.can_header.len as usize | ||
| 210 | } | ||
| 211 | fn data(&self) -> &[u8] { | ||
| 212 | &self.data.raw() | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Payload of a (FD)CAN data frame. | ||
| 217 | /// | ||
| 218 | /// Contains 0 to 64 Bytes of data. | ||
| 219 | #[derive(Debug, Copy, Clone)] | ||
| 220 | pub struct FdData { | ||
| 221 | pub(crate) bytes: [u8; 64], | ||
| 222 | } | ||
| 223 | |||
| 224 | impl FdData { | ||
| 225 | /// Creates a data payload from a raw byte slice. | ||
| 226 | /// | ||
| 227 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or | ||
| 228 | /// cannot be represented with an FDCAN DLC. | ||
| 229 | pub fn new(data: &[u8]) -> Option<Self> { | ||
| 230 | if !FdData::is_valid_len(data.len()) { | ||
| 231 | return None; | ||
| 232 | } | ||
| 233 | |||
| 234 | let mut bytes = [0; 64]; | ||
| 235 | bytes[..data.len()].copy_from_slice(data); | ||
| 236 | |||
| 237 | Some(Self { bytes }) | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Raw read access to data. | ||
| 241 | pub fn raw(&self) -> &[u8] { | ||
| 242 | &self.bytes | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Checks if the length can be encoded in FDCAN DLC field. | ||
| 246 | pub const fn is_valid_len(len: usize) -> bool { | ||
| 247 | match len { | ||
| 248 | 0..=8 => true, | ||
| 249 | 12 => true, | ||
| 250 | 16 => true, | ||
| 251 | 20 => true, | ||
| 252 | 24 => true, | ||
| 253 | 32 => true, | ||
| 254 | 48 => true, | ||
| 255 | 64 => true, | ||
| 256 | _ => false, | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | /// Creates an empty data payload containing 0 bytes. | ||
| 261 | #[inline] | ||
| 262 | pub const fn empty() -> Self { | ||
| 263 | Self { bytes: [0; 64] } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Frame with up to 8 bytes of data payload as per Fd CAN | ||
| 268 | #[derive(Debug, Copy, Clone)] | ||
| 269 | pub struct FdFrame { | ||
| 270 | can_header: Header, | ||
| 271 | data: FdData, | ||
| 272 | } | ||
| 273 | |||
| 274 | impl FdFrame { | ||
| 275 | pub(crate) const MAX_DATA_LEN: usize = 64; | ||
| 276 | |||
| 277 | /// Create a new CAN classic Frame | ||
| 278 | pub fn new(can_header: Header, data: FdData) -> FdFrame { | ||
| 279 | FdFrame { can_header, data } | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Create new extended frame | ||
| 283 | pub fn new_extended(raw_id: u32, raw_data: &[u8]) -> Option<Self> { | ||
| 284 | if let Some(id) = embedded_can::ExtendedId::new(raw_id) { | ||
| 285 | match FdData::new(raw_data) { | ||
| 286 | Some(data) => Some(FdFrame::new(Header::new(id.into(), raw_data.len() as u8, false), data)), | ||
| 287 | None => None, | ||
| 288 | } | ||
| 289 | } else { | ||
| 290 | None | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Create new standard frame | ||
| 295 | pub fn new_standard(raw_id: u16, raw_data: &[u8]) -> Option<Self> { | ||
| 296 | if let Some(id) = embedded_can::StandardId::new(raw_id) { | ||
| 297 | match FdData::new(raw_data) { | ||
| 298 | Some(data) => Some(FdFrame::new(Header::new(id.into(), raw_data.len() as u8, false), data)), | ||
| 299 | None => None, | ||
| 300 | } | ||
| 301 | } else { | ||
| 302 | None | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Create new remote frame | ||
| 307 | pub fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { | ||
| 308 | if len <= 8 { | ||
| 309 | Some(FdFrame::new(Header::new(id.into(), len as u8, true), FdData::empty())) | ||
| 310 | } else { | ||
| 311 | None | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Get reference to data | ||
| 316 | pub fn header(&self) -> &Header { | ||
| 317 | &self.can_header | ||
| 318 | } | ||
| 319 | |||
| 320 | /// Return ID | ||
| 321 | pub fn id(&self) -> &embedded_can::Id { | ||
| 322 | &self.can_header.id | ||
| 323 | } | ||
| 324 | |||
| 325 | /// Get reference to data | ||
| 326 | pub fn data(&self) -> &[u8] { | ||
| 327 | &self.data.raw() | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | impl embedded_can::Frame for FdFrame { | ||
| 332 | fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> { | ||
| 333 | match FdData::new(raw_data) { | ||
| 334 | Some(data) => Some(FdFrame::new( | ||
| 335 | Header::new_fd(id.into(), raw_data.len() as u8, false, true), | ||
| 336 | data, | ||
| 337 | )), | ||
| 338 | None => None, | ||
| 339 | } | ||
| 340 | } | ||
| 341 | fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { | ||
| 342 | if len <= 8 { | ||
| 343 | Some(FdFrame::new( | ||
| 344 | Header::new_fd(id.into(), len as u8, true, true), | ||
| 345 | FdData::empty(), | ||
| 346 | )) | ||
| 347 | } else { | ||
| 348 | None | ||
| 349 | } | ||
| 350 | } | ||
| 351 | fn is_extended(&self) -> bool { | ||
| 352 | match self.can_header.id { | ||
| 353 | embedded_can::Id::Extended(_) => true, | ||
| 354 | embedded_can::Id::Standard(_) => true, | ||
| 355 | } | ||
| 356 | } | ||
| 357 | fn is_remote_frame(&self) -> bool { | ||
| 358 | self.can_header.rtr() | ||
| 359 | } | ||
| 360 | fn id(&self) -> embedded_can::Id { | ||
| 361 | self.can_header.id | ||
| 362 | } | ||
| 363 | // Returns length in bytes even for CANFD packets which embedded-can does not really mention. | ||
| 364 | fn dlc(&self) -> usize { | ||
| 365 | self.can_header.len as usize | ||
| 366 | } | ||
| 367 | fn data(&self) -> &[u8] { | ||
| 368 | &self.data.raw() | ||
| 369 | } | ||
| 370 | } | ||
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 895ad3e7c..74ccfa3b0 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -20,9 +20,11 @@ defmt-rtt = "0.4" | |||
| 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 20 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 21 | cortex-m-rt = "0.7.0" | 21 | cortex-m-rt = "0.7.0" |
| 22 | embedded-hal = "0.2.6" | 22 | embedded-hal = "0.2.6" |
| 23 | embedded-can = { version = "0.4" } | ||
| 23 | panic-probe = { version = "0.3", features = ["print-defmt"] } | 24 | panic-probe = { version = "0.3", features = ["print-defmt"] } |
| 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 25 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 25 | heapless = { version = "0.8", default-features = false } | 26 | heapless = { version = "0.8", default-features = false } |
| 27 | static_cell = "2.0.0" | ||
| 26 | 28 | ||
| 27 | [profile.release] | 29 | [profile.release] |
| 28 | debug = 2 | 30 | debug = 2 |
diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 727921fba..affa97039 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs | |||
| @@ -5,6 +5,7 @@ use embassy_executor::Spawner; | |||
| 5 | use embassy_stm32::peripherals::*; | 5 | use embassy_stm32::peripherals::*; |
| 6 | use embassy_stm32::{bind_interrupts, can, Config}; | 6 | use embassy_stm32::{bind_interrupts, can, Config}; |
| 7 | use embassy_time::Timer; | 7 | use embassy_time::Timer; |
| 8 | use static_cell::StaticCell; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| 9 | 10 | ||
| 10 | bind_interrupts!(struct Irqs { | 11 | bind_interrupts!(struct Irqs { |
| @@ -18,39 +19,190 @@ async fn main(_spawner: Spawner) { | |||
| 18 | 19 | ||
| 19 | let peripherals = embassy_stm32::init(config); | 20 | let peripherals = embassy_stm32::init(config); |
| 20 | 21 | ||
| 21 | let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); | 22 | let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); |
| 23 | |||
| 24 | can.set_extended_filter( | ||
| 25 | can::fd::filter::ExtendedFilterSlot::_0, | ||
| 26 | can::fd::filter::ExtendedFilter::accept_all_into_fifo1(), | ||
| 27 | ); | ||
| 22 | 28 | ||
| 23 | // 250k bps | 29 | // 250k bps |
| 24 | can.set_bitrate(250_000); | 30 | can.set_bitrate(250_000); |
| 25 | 31 | ||
| 32 | let use_fd = false; | ||
| 33 | |||
| 34 | // 1M bps | ||
| 35 | if use_fd { | ||
| 36 | can.set_fd_data_bitrate(1_000_000, false); | ||
| 37 | } | ||
| 38 | |||
| 26 | info!("Configured"); | 39 | info!("Configured"); |
| 27 | 40 | ||
| 28 | //let mut can = can.into_external_loopback_mode(); | 41 | let mut can = can.start(match use_fd { |
| 29 | let mut can = can.into_normal_mode(); | 42 | true => can::FdcanOperatingMode::InternalLoopbackMode, |
| 43 | false => can::FdcanOperatingMode::NormalOperationMode, | ||
| 44 | }); | ||
| 30 | 45 | ||
| 31 | let mut i = 0; | 46 | let mut i = 0; |
| 47 | let mut last_read_ts = embassy_time::Instant::now(); | ||
| 48 | |||
| 32 | loop { | 49 | loop { |
| 33 | let frame = can::TxFrame::new( | 50 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); |
| 34 | can::TxFrameHeader { | ||
| 35 | len: 1, | ||
| 36 | frame_format: can::FrameFormat::Standard, | ||
| 37 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 38 | bit_rate_switching: false, | ||
| 39 | marker: None, | ||
| 40 | }, | ||
| 41 | &[i], | ||
| 42 | ) | ||
| 43 | .unwrap(); | ||
| 44 | info!("Writing frame"); | 51 | info!("Writing frame"); |
| 52 | |||
| 45 | _ = can.write(&frame).await; | 53 | _ = can.write(&frame).await; |
| 46 | 54 | ||
| 47 | match can.read().await { | 55 | match can.read().await { |
| 48 | Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), | 56 | Ok((rx_frame, ts)) => { |
| 57 | let delta = (ts - last_read_ts).as_millis(); | ||
| 58 | last_read_ts = ts; | ||
| 59 | info!( | ||
| 60 | "Rx: {} {:02x} --- {}ms", | ||
| 61 | rx_frame.header().len(), | ||
| 62 | rx_frame.data()[0..rx_frame.header().len() as usize], | ||
| 63 | delta, | ||
| 64 | ) | ||
| 65 | } | ||
| 49 | Err(_err) => error!("Error in frame"), | 66 | Err(_err) => error!("Error in frame"), |
| 50 | } | 67 | } |
| 51 | 68 | ||
| 52 | Timer::after_millis(250).await; | 69 | Timer::after_millis(250).await; |
| 53 | 70 | ||
| 54 | i += 1; | 71 | i += 1; |
| 72 | if i > 2 { | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | // Use the FD API's even if we don't get FD packets. | ||
| 78 | |||
| 79 | loop { | ||
| 80 | if use_fd { | ||
| 81 | let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap(); | ||
| 82 | info!("Writing frame using FD API"); | ||
| 83 | _ = can.write_fd(&frame).await; | ||
| 84 | } else { | ||
| 85 | let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 8]).unwrap(); | ||
| 86 | info!("Writing frame using FD API"); | ||
| 87 | _ = can.write_fd(&frame).await; | ||
| 88 | } | ||
| 89 | |||
| 90 | match can.read_fd().await { | ||
| 91 | Ok((rx_frame, ts)) => { | ||
| 92 | let delta = (ts - last_read_ts).as_millis(); | ||
| 93 | last_read_ts = ts; | ||
| 94 | info!( | ||
| 95 | "Rx: {} {:02x} --- using FD API {}ms", | ||
| 96 | rx_frame.header().len(), | ||
| 97 | rx_frame.data()[0..rx_frame.header().len() as usize], | ||
| 98 | delta, | ||
| 99 | ) | ||
| 100 | } | ||
| 101 | Err(_err) => error!("Error in frame"), | ||
| 102 | } | ||
| 103 | |||
| 104 | Timer::after_millis(250).await; | ||
| 105 | |||
| 106 | i += 1; | ||
| 107 | if i > 4 { | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | i = 0; | ||
| 112 | let (mut tx, mut rx) = can.split(); | ||
| 113 | // With split | ||
| 114 | loop { | ||
| 115 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); | ||
| 116 | info!("Writing frame"); | ||
| 117 | _ = tx.write(&frame).await; | ||
| 118 | |||
| 119 | match rx.read().await { | ||
| 120 | Ok((rx_frame, ts)) => { | ||
| 121 | let delta = (ts - last_read_ts).as_millis(); | ||
| 122 | last_read_ts = ts; | ||
| 123 | info!( | ||
| 124 | "Rx: {} {:02x} --- {}ms", | ||
| 125 | rx_frame.header().len(), | ||
| 126 | rx_frame.data()[0..rx_frame.header().len() as usize], | ||
| 127 | delta, | ||
| 128 | ) | ||
| 129 | } | ||
| 130 | Err(_err) => error!("Error in frame"), | ||
| 131 | } | ||
| 132 | |||
| 133 | Timer::after_millis(250).await; | ||
| 134 | |||
| 135 | i += 1; | ||
| 136 | |||
| 137 | if i > 2 { | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | let can = can::Fdcan::join(tx, rx); | ||
| 143 | |||
| 144 | info!("\n\n\nBuffered\n"); | ||
| 145 | if use_fd { | ||
| 146 | static TX_BUF: StaticCell<can::TxFdBuf<8>> = StaticCell::new(); | ||
| 147 | static RX_BUF: StaticCell<can::RxFdBuf<10>> = StaticCell::new(); | ||
| 148 | let mut can = can.buffered_fd( | ||
| 149 | TX_BUF.init(can::TxFdBuf::<8>::new()), | ||
| 150 | RX_BUF.init(can::RxFdBuf::<10>::new()), | ||
| 151 | ); | ||
| 152 | loop { | ||
| 153 | let frame = can::frame::FdFrame::new_extended(0x123456F, &[i; 16]).unwrap(); | ||
| 154 | info!("Writing frame"); | ||
| 155 | |||
| 156 | _ = can.write(frame).await; | ||
| 157 | |||
| 158 | match can.read().await { | ||
| 159 | Ok((rx_frame, ts)) => { | ||
| 160 | let delta = (ts - last_read_ts).as_millis(); | ||
| 161 | last_read_ts = ts; | ||
| 162 | info!( | ||
| 163 | "Rx: {} {:02x} --- {}ms", | ||
| 164 | rx_frame.header().len(), | ||
| 165 | rx_frame.data()[0..rx_frame.header().len() as usize], | ||
| 166 | delta, | ||
| 167 | ) | ||
| 168 | } | ||
| 169 | Err(_err) => error!("Error in frame"), | ||
| 170 | } | ||
| 171 | |||
| 172 | Timer::after_millis(250).await; | ||
| 173 | |||
| 174 | i += 1; | ||
| 175 | } | ||
| 176 | } else { | ||
| 177 | static TX_BUF: StaticCell<can::TxBuf<8>> = StaticCell::new(); | ||
| 178 | static RX_BUF: StaticCell<can::RxBuf<10>> = StaticCell::new(); | ||
| 179 | let mut can = can.buffered( | ||
| 180 | TX_BUF.init(can::TxBuf::<8>::new()), | ||
| 181 | RX_BUF.init(can::RxBuf::<10>::new()), | ||
| 182 | ); | ||
| 183 | loop { | ||
| 184 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); | ||
| 185 | info!("Writing frame"); | ||
| 186 | |||
| 187 | _ = can.write(frame).await; | ||
| 188 | |||
| 189 | match can.read().await { | ||
| 190 | Ok((rx_frame, ts)) => { | ||
| 191 | let delta = (ts - last_read_ts).as_millis(); | ||
| 192 | last_read_ts = ts; | ||
| 193 | info!( | ||
| 194 | "Rx: {} {:02x} --- {}ms", | ||
| 195 | rx_frame.header().len(), | ||
| 196 | rx_frame.data()[0..rx_frame.header().len() as usize], | ||
| 197 | delta, | ||
| 198 | ) | ||
| 199 | } | ||
| 200 | Err(_err) => error!("Error in frame"), | ||
| 201 | } | ||
| 202 | |||
| 203 | Timer::after_millis(250).await; | ||
| 204 | |||
| 205 | i += 1; | ||
| 206 | } | ||
| 55 | } | 207 | } |
| 56 | } | 208 | } |
diff --git a/examples/stm32h5/src/bin/can.rs b/examples/stm32h5/src/bin/can.rs index 2906d1576..e5ccfe4f7 100644 --- a/examples/stm32h5/src/bin/can.rs +++ b/examples/stm32h5/src/bin/can.rs | |||
| @@ -16,54 +16,76 @@ bind_interrupts!(struct Irqs { | |||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 18 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 19 | 19 | config.rcc.hse = Some(rcc::Hse { | |
| 20 | // configure FDCAN to use PLL1_Q at 64 MHz | 20 | freq: embassy_stm32::time::Hertz(25_000_000), |
| 21 | config.rcc.pll1 = Some(rcc::Pll { | 21 | mode: rcc::HseMode::Oscillator, |
| 22 | source: rcc::PllSource::HSI, | ||
| 23 | prediv: rcc::PllPreDiv::DIV4, | ||
| 24 | mul: rcc::PllMul::MUL8, | ||
| 25 | divp: None, | ||
| 26 | divq: Some(rcc::PllDiv::DIV2), | ||
| 27 | divr: None, | ||
| 28 | }); | 22 | }); |
| 29 | config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q; | 23 | config.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; |
| 30 | 24 | ||
| 31 | let peripherals = embassy_stm32::init(config); | 25 | let peripherals = embassy_stm32::init(config); |
| 32 | 26 | ||
| 33 | let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); | 27 | let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); |
| 34 | 28 | ||
| 35 | can.can.apply_config( | 29 | // 250k bps |
| 36 | can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming { | 30 | can.set_bitrate(250_000); |
| 37 | sync_jump_width: 1.try_into().unwrap(), | ||
| 38 | prescaler: 8.try_into().unwrap(), | ||
| 39 | seg1: 13.try_into().unwrap(), | ||
| 40 | seg2: 2.try_into().unwrap(), | ||
| 41 | }), | ||
| 42 | ); | ||
| 43 | 31 | ||
| 44 | info!("Configured"); | 32 | //let mut can = can.into_internal_loopback_mode(); |
| 33 | let mut can = can.into_normal_mode(); | ||
| 45 | 34 | ||
| 46 | let mut can = can.into_external_loopback_mode(); | 35 | info!("CAN Configured"); |
| 47 | //let mut can = can.into_normal_mode(); | ||
| 48 | 36 | ||
| 49 | let mut i = 0; | 37 | let mut i = 0; |
| 38 | let mut last_read_ts = embassy_time::Instant::now(); | ||
| 39 | |||
| 50 | loop { | 40 | loop { |
| 51 | let frame = can::TxFrame::new( | 41 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); |
| 52 | can::TxFrameHeader { | ||
| 53 | len: 1, | ||
| 54 | frame_format: can::FrameFormat::Standard, | ||
| 55 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 56 | bit_rate_switching: false, | ||
| 57 | marker: None, | ||
| 58 | }, | ||
| 59 | &[i], | ||
| 60 | ) | ||
| 61 | .unwrap(); | ||
| 62 | info!("Writing frame"); | 42 | info!("Writing frame"); |
| 63 | _ = can.write(&frame).await; | 43 | _ = can.write(&frame).await; |
| 64 | 44 | ||
| 65 | match can.read().await { | 45 | match can.read().await { |
| 66 | Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), | 46 | Ok((rx_frame, ts)) => { |
| 47 | let delta = (ts - last_read_ts).as_millis(); | ||
| 48 | last_read_ts = ts; | ||
| 49 | info!( | ||
| 50 | "Rx: {:x} {:x} {:x} {:x} --- NEW {}", | ||
| 51 | rx_frame.data()[0], | ||
| 52 | rx_frame.data()[1], | ||
| 53 | rx_frame.data()[2], | ||
| 54 | rx_frame.data()[3], | ||
| 55 | delta, | ||
| 56 | ) | ||
| 57 | } | ||
| 58 | Err(_err) => error!("Error in frame"), | ||
| 59 | } | ||
| 60 | |||
| 61 | Timer::after_millis(250).await; | ||
| 62 | |||
| 63 | i += 1; | ||
| 64 | if i > 3 { | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | let (mut tx, mut rx) = can.split(); | ||
| 70 | // With split | ||
| 71 | loop { | ||
| 72 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); | ||
| 73 | info!("Writing frame"); | ||
| 74 | _ = tx.write(&frame).await; | ||
| 75 | |||
| 76 | match rx.read().await { | ||
| 77 | Ok((rx_frame, ts)) => { | ||
| 78 | let delta = (ts - last_read_ts).as_millis(); | ||
| 79 | last_read_ts = ts; | ||
| 80 | info!( | ||
| 81 | "Rx: {:x} {:x} {:x} {:x} --- NEW {}", | ||
| 82 | rx_frame.data()[0], | ||
| 83 | rx_frame.data()[1], | ||
| 84 | rx_frame.data()[2], | ||
| 85 | rx_frame.data()[3], | ||
| 86 | delta, | ||
| 87 | ) | ||
| 88 | } | ||
| 67 | Err(_err) => error!("Error in frame"), | 89 | Err(_err) => error!("Error in frame"), |
| 68 | } | 90 | } |
| 69 | 91 | ||
diff --git a/examples/stm32h7/src/bin/can.rs b/examples/stm32h7/src/bin/can.rs index 2906d1576..e5ccfe4f7 100644 --- a/examples/stm32h7/src/bin/can.rs +++ b/examples/stm32h7/src/bin/can.rs | |||
| @@ -16,54 +16,76 @@ bind_interrupts!(struct Irqs { | |||
| 16 | #[embassy_executor::main] | 16 | #[embassy_executor::main] |
| 17 | async fn main(_spawner: Spawner) { | 17 | async fn main(_spawner: Spawner) { |
| 18 | let mut config = Config::default(); | 18 | let mut config = Config::default(); |
| 19 | 19 | config.rcc.hse = Some(rcc::Hse { | |
| 20 | // configure FDCAN to use PLL1_Q at 64 MHz | 20 | freq: embassy_stm32::time::Hertz(25_000_000), |
| 21 | config.rcc.pll1 = Some(rcc::Pll { | 21 | mode: rcc::HseMode::Oscillator, |
| 22 | source: rcc::PllSource::HSI, | ||
| 23 | prediv: rcc::PllPreDiv::DIV4, | ||
| 24 | mul: rcc::PllMul::MUL8, | ||
| 25 | divp: None, | ||
| 26 | divq: Some(rcc::PllDiv::DIV2), | ||
| 27 | divr: None, | ||
| 28 | }); | 22 | }); |
| 29 | config.rcc.fdcan_clock_source = rcc::FdCanClockSource::PLL1_Q; | 23 | config.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; |
| 30 | 24 | ||
| 31 | let peripherals = embassy_stm32::init(config); | 25 | let peripherals = embassy_stm32::init(config); |
| 32 | 26 | ||
| 33 | let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); | 27 | let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); |
| 34 | 28 | ||
| 35 | can.can.apply_config( | 29 | // 250k bps |
| 36 | can::config::FdCanConfig::default().set_nominal_bit_timing(can::config::NominalBitTiming { | 30 | can.set_bitrate(250_000); |
| 37 | sync_jump_width: 1.try_into().unwrap(), | ||
| 38 | prescaler: 8.try_into().unwrap(), | ||
| 39 | seg1: 13.try_into().unwrap(), | ||
| 40 | seg2: 2.try_into().unwrap(), | ||
| 41 | }), | ||
| 42 | ); | ||
| 43 | 31 | ||
| 44 | info!("Configured"); | 32 | //let mut can = can.into_internal_loopback_mode(); |
| 33 | let mut can = can.into_normal_mode(); | ||
| 45 | 34 | ||
| 46 | let mut can = can.into_external_loopback_mode(); | 35 | info!("CAN Configured"); |
| 47 | //let mut can = can.into_normal_mode(); | ||
| 48 | 36 | ||
| 49 | let mut i = 0; | 37 | let mut i = 0; |
| 38 | let mut last_read_ts = embassy_time::Instant::now(); | ||
| 39 | |||
| 50 | loop { | 40 | loop { |
| 51 | let frame = can::TxFrame::new( | 41 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); |
| 52 | can::TxFrameHeader { | ||
| 53 | len: 1, | ||
| 54 | frame_format: can::FrameFormat::Standard, | ||
| 55 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 56 | bit_rate_switching: false, | ||
| 57 | marker: None, | ||
| 58 | }, | ||
| 59 | &[i], | ||
| 60 | ) | ||
| 61 | .unwrap(); | ||
| 62 | info!("Writing frame"); | 42 | info!("Writing frame"); |
| 63 | _ = can.write(&frame).await; | 43 | _ = can.write(&frame).await; |
| 64 | 44 | ||
| 65 | match can.read().await { | 45 | match can.read().await { |
| 66 | Ok(rx_frame) => info!("Rx: {}", rx_frame.data()[0]), | 46 | Ok((rx_frame, ts)) => { |
| 47 | let delta = (ts - last_read_ts).as_millis(); | ||
| 48 | last_read_ts = ts; | ||
| 49 | info!( | ||
| 50 | "Rx: {:x} {:x} {:x} {:x} --- NEW {}", | ||
| 51 | rx_frame.data()[0], | ||
| 52 | rx_frame.data()[1], | ||
| 53 | rx_frame.data()[2], | ||
| 54 | rx_frame.data()[3], | ||
| 55 | delta, | ||
| 56 | ) | ||
| 57 | } | ||
| 58 | Err(_err) => error!("Error in frame"), | ||
| 59 | } | ||
| 60 | |||
| 61 | Timer::after_millis(250).await; | ||
| 62 | |||
| 63 | i += 1; | ||
| 64 | if i > 3 { | ||
| 65 | break; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | let (mut tx, mut rx) = can.split(); | ||
| 70 | // With split | ||
| 71 | loop { | ||
| 72 | let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); | ||
| 73 | info!("Writing frame"); | ||
| 74 | _ = tx.write(&frame).await; | ||
| 75 | |||
| 76 | match rx.read().await { | ||
| 77 | Ok((rx_frame, ts)) => { | ||
| 78 | let delta = (ts - last_read_ts).as_millis(); | ||
| 79 | last_read_ts = ts; | ||
| 80 | info!( | ||
| 81 | "Rx: {:x} {:x} {:x} {:x} --- NEW {}", | ||
| 82 | rx_frame.data()[0], | ||
| 83 | rx_frame.data()[1], | ||
| 84 | rx_frame.data()[2], | ||
| 85 | rx_frame.data()[3], | ||
| 86 | delta, | ||
| 87 | ) | ||
| 88 | } | ||
| 67 | Err(_err) => error!("Error in frame"), | 89 | Err(_err) => error!("Error in frame"), |
| 68 | } | 90 | } |
| 69 | 91 | ||
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8554682a4..5b28b5849 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml | |||
| @@ -69,6 +69,7 @@ cortex-m-rt = "0.7.0" | |||
| 69 | embedded-hal = "0.2.6" | 69 | embedded-hal = "0.2.6" |
| 70 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } | 70 | embedded-hal-1 = { package = "embedded-hal", version = "1.0" } |
| 71 | embedded-hal-async = { version = "1.0" } | 71 | embedded-hal-async = { version = "1.0" } |
| 72 | embedded-can = { version = "0.4" } | ||
| 72 | micromath = "2.0.0" | 73 | micromath = "2.0.0" |
| 73 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } | 74 | panic-probe = { version = "0.3.0", features = ["print-defmt"] } |
| 74 | rand_core = { version = "0.6", default-features = false } | 75 | rand_core = { version = "0.6", default-features = false } |
diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index 7363eaa16..398e31ffc 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs | |||
| @@ -36,7 +36,7 @@ fn options() -> TestOptions { | |||
| 36 | c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; | 36 | c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; |
| 37 | TestOptions { | 37 | TestOptions { |
| 38 | config: c, | 38 | config: c, |
| 39 | max_latency: Duration::from_micros(3800), | 39 | max_latency: Duration::from_micros(1200), |
| 40 | second_fifo_working: false, | 40 | second_fifo_working: false, |
| 41 | } | 41 | } |
| 42 | } | 42 | } |
| @@ -53,12 +53,12 @@ fn options() -> TestOptions { | |||
| 53 | c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; | 53 | c.rcc.fdcan_clock_source = rcc::FdCanClockSource::HSE; |
| 54 | TestOptions { | 54 | TestOptions { |
| 55 | config: c, | 55 | config: c, |
| 56 | max_latency: Duration::from_micros(5500), | 56 | max_latency: Duration::from_micros(1200), |
| 57 | second_fifo_working: false, | 57 | second_fifo_working: false, |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | #[cfg(any(feature = "stm32g491re"))] | 61 | #[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))] |
| 62 | fn options() -> TestOptions { | 62 | fn options() -> TestOptions { |
| 63 | info!("G4 config"); | 63 | info!("G4 config"); |
| 64 | TestOptions { | 64 | TestOptions { |
| @@ -75,14 +75,14 @@ async fn main(_spawner: Spawner) { | |||
| 75 | let options = options(); | 75 | let options = options(); |
| 76 | let peripherals = embassy_stm32::init(options.config); | 76 | let peripherals = embassy_stm32::init(options.config); |
| 77 | 77 | ||
| 78 | let mut can = can::Fdcan::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs); | 78 | let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs); |
| 79 | 79 | ||
| 80 | // 250k bps | 80 | // 250k bps |
| 81 | can.set_bitrate(250_000); | 81 | can.set_bitrate(250_000); |
| 82 | 82 | ||
| 83 | can.can.set_extended_filter( | 83 | can.set_extended_filter( |
| 84 | can::filter::ExtendedFilterSlot::_0, | 84 | can::fd::filter::ExtendedFilterSlot::_0, |
| 85 | can::filter::ExtendedFilter::accept_all_into_fifo1(), | 85 | can::fd::filter::ExtendedFilter::accept_all_into_fifo1(), |
| 86 | ); | 86 | ); |
| 87 | 87 | ||
| 88 | let mut can = can.into_internal_loopback_mode(); | 88 | let mut can = can.into_internal_loopback_mode(); |
| @@ -91,31 +91,21 @@ async fn main(_spawner: Spawner) { | |||
| 91 | 91 | ||
| 92 | let mut i: u8 = 0; | 92 | let mut i: u8 = 0; |
| 93 | loop { | 93 | loop { |
| 94 | let tx_frame = can::TxFrame::new( | 94 | let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); |
| 95 | can::TxFrameHeader { | ||
| 96 | len: 1, | ||
| 97 | frame_format: can::FrameFormat::Standard, | ||
| 98 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 99 | bit_rate_switching: false, | ||
| 100 | marker: None, | ||
| 101 | }, | ||
| 102 | &[i], | ||
| 103 | ) | ||
| 104 | .unwrap(); | ||
| 105 | 95 | ||
| 106 | info!("Transmitting frame..."); | 96 | info!("Transmitting frame..."); |
| 107 | let tx_ts = Instant::now(); | 97 | let tx_ts = Instant::now(); |
| 108 | can.write(&tx_frame).await; | 98 | can.write(&tx_frame).await; |
| 109 | 99 | ||
| 110 | let envelope = can.read().await.unwrap(); | 100 | let (frame, timestamp) = can.read().await.unwrap(); |
| 111 | info!("Frame received!"); | 101 | info!("Frame received!"); |
| 112 | 102 | ||
| 113 | // Check data. | 103 | // Check data. |
| 114 | assert!(i == envelope.data()[0], "{} == {}", i, envelope.data()[0]); | 104 | assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); |
| 115 | 105 | ||
| 116 | info!("loopback time {}", envelope.header.time_stamp); | 106 | info!("loopback time {}", timestamp); |
| 117 | info!("loopback frame {=u8}", envelope.data()[0]); | 107 | info!("loopback frame {=u8}", frame.data()[0]); |
| 118 | let latency = envelope.timestamp.saturating_duration_since(tx_ts); | 108 | let latency = timestamp.saturating_duration_since(tx_ts); |
| 119 | info!("loopback latency {} us", latency.as_micros()); | 109 | info!("loopback latency {} us", latency.as_micros()); |
| 120 | 110 | ||
| 121 | // Theoretical minimum latency is 55us, actual is usually ~80us | 111 | // Theoretical minimum latency is 55us, actual is usually ~80us |
| @@ -143,47 +133,26 @@ async fn main(_spawner: Spawner) { | |||
| 143 | // in each FIFO so make sure we write enough to fill them both up before reading. | 133 | // in each FIFO so make sure we write enough to fill them both up before reading. |
| 144 | for i in 0..3 { | 134 | for i in 0..3 { |
| 145 | // Try filling up the RX FIFO0 buffers with standard packets | 135 | // Try filling up the RX FIFO0 buffers with standard packets |
| 146 | let tx_frame = can::TxFrame::new( | 136 | let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); |
| 147 | can::TxFrameHeader { | ||
| 148 | len: 1, | ||
| 149 | frame_format: can::FrameFormat::Standard, | ||
| 150 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 151 | bit_rate_switching: false, | ||
| 152 | marker: None, | ||
| 153 | }, | ||
| 154 | &[i], | ||
| 155 | ) | ||
| 156 | .unwrap(); | ||
| 157 | info!("Transmitting frame {}", i); | 137 | info!("Transmitting frame {}", i); |
| 158 | can.write(&tx_frame).await; | 138 | can.write(&tx_frame).await; |
| 159 | } | 139 | } |
| 160 | for i in 3..max_buffered { | 140 | for i in 3..max_buffered { |
| 161 | // Try filling up the RX FIFO0 buffers with extended packets | 141 | // Try filling up the RX FIFO0 buffers with extended packets |
| 162 | let tx_frame = can::TxFrame::new( | 142 | let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); |
| 163 | can::TxFrameHeader { | ||
| 164 | len: 1, | ||
| 165 | frame_format: can::FrameFormat::Standard, | ||
| 166 | id: can::ExtendedId::new(0x1232344).unwrap().into(), | ||
| 167 | bit_rate_switching: false, | ||
| 168 | marker: None, | ||
| 169 | }, | ||
| 170 | &[i], | ||
| 171 | ) | ||
| 172 | .unwrap(); | ||
| 173 | |||
| 174 | info!("Transmitting frame {}", i); | 143 | info!("Transmitting frame {}", i); |
| 175 | can.write(&tx_frame).await; | 144 | can.write(&tx_frame).await; |
| 176 | } | 145 | } |
| 177 | 146 | ||
| 178 | // Try and receive all 6 packets | 147 | // Try and receive all 6 packets |
| 179 | for i in 0..max_buffered { | 148 | for i in 0..max_buffered { |
| 180 | let envelope = can.read().await.unwrap(); | 149 | let (frame, _ts) = can.read().await.unwrap(); |
| 181 | match envelope.header.id { | 150 | match frame.id() { |
| 182 | can::Id::Extended(id) => { | 151 | embedded_can::Id::Extended(id) => { |
| 183 | info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); | 152 | info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); |
| 184 | } | 153 | } |
| 185 | can::Id::Standard(id) => { | 154 | embedded_can::Id::Standard(id) => { |
| 186 | info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); | 155 | info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); |
| 187 | } | 156 | } |
| 188 | } | 157 | } |
| 189 | } | 158 | } |
| @@ -192,48 +161,26 @@ async fn main(_spawner: Spawner) { | |||
| 192 | let (mut tx, mut rx) = can.split(); | 161 | let (mut tx, mut rx) = can.split(); |
| 193 | for i in 0..3 { | 162 | for i in 0..3 { |
| 194 | // Try filling up the RX FIFO0 buffers with standard packets | 163 | // Try filling up the RX FIFO0 buffers with standard packets |
| 195 | let tx_frame = can::TxFrame::new( | 164 | let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); |
| 196 | can::TxFrameHeader { | ||
| 197 | len: 1, | ||
| 198 | frame_format: can::FrameFormat::Standard, | ||
| 199 | id: can::StandardId::new(0x123).unwrap().into(), | ||
| 200 | bit_rate_switching: false, | ||
| 201 | marker: None, | ||
| 202 | }, | ||
| 203 | &[i], | ||
| 204 | ) | ||
| 205 | .unwrap(); | ||
| 206 | |||
| 207 | info!("Transmitting frame {}", i); | 165 | info!("Transmitting frame {}", i); |
| 208 | tx.write(&tx_frame).await; | 166 | tx.write(&tx_frame).await; |
| 209 | } | 167 | } |
| 210 | for i in 3..max_buffered { | 168 | for i in 3..max_buffered { |
| 211 | // Try filling up the RX FIFO0 buffers with extended packets | 169 | // Try filling up the RX FIFO0 buffers with extended packets |
| 212 | let tx_frame = can::TxFrame::new( | 170 | let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); |
| 213 | can::TxFrameHeader { | ||
| 214 | len: 1, | ||
| 215 | frame_format: can::FrameFormat::Standard, | ||
| 216 | id: can::ExtendedId::new(0x1232344).unwrap().into(), | ||
| 217 | bit_rate_switching: false, | ||
| 218 | marker: None, | ||
| 219 | }, | ||
| 220 | &[i], | ||
| 221 | ) | ||
| 222 | .unwrap(); | ||
| 223 | |||
| 224 | info!("Transmitting frame {}", i); | 171 | info!("Transmitting frame {}", i); |
| 225 | tx.write(&tx_frame).await; | 172 | tx.write(&tx_frame).await; |
| 226 | } | 173 | } |
| 227 | 174 | ||
| 228 | // Try and receive all 6 packets | 175 | // Try and receive all 6 packets |
| 229 | for i in 0..max_buffered { | 176 | for i in 0..max_buffered { |
| 230 | let envelope = rx.read().await.unwrap(); | 177 | let (frame, _ts) = rx.read().await.unwrap(); |
| 231 | match envelope.header.id { | 178 | match frame.id() { |
| 232 | can::Id::Extended(id) => { | 179 | embedded_can::Id::Extended(id) => { |
| 233 | info!("Extended received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); | 180 | info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); |
| 234 | } | 181 | } |
| 235 | can::Id::Standard(id) => { | 182 | embedded_can::Id::Standard(id) => { |
| 236 | info!("Standard received! {:x} {} {}", id.as_raw(), envelope.data()[0], i); | 183 | info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); |
| 237 | } | 184 | } |
| 238 | } | 185 | } |
| 239 | } | 186 | } |
