commit 49b27a674e035fc6a833e11fe22631eb2e496ccb Author: yanorei32 Date: Tue Oct 10 22:58:41 2023 +0900 Init diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..34ba36c --- /dev/null +++ b/.cargo/config @@ -0,0 +1,8 @@ +[target.thumbv7m-none-eabi] +runner = 'arm-none-eabi-gdb' +rustflags = [ + "-C", "link-arg=-Tlink.x", +] + +[build] +target = "thumbv7m-none-eabi" diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..098ff6f --- /dev/null +++ b/.gdbinit @@ -0,0 +1,18 @@ +target remote :3333 + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.fifo uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load +step diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f4ab173 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,515 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version 0.2.3", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bxcan" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ac3d0c0a542d0ab5521211f873f62706a7136df415676f676d347e5a41dd80" +dependencies = [ + "bitflags", + "embedded-hal", + "nb 1.1.0", + "vcell", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cortex-m" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "critical-section", + "embedded-hal", + "volatile-register", +] + +[[package]] +name = "cortex-m-rt" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1" +dependencies = [ + "cortex-m-rt-macros", +] + +[[package]] +name = "cortex-m-rt-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cortex-m-rtic" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d696ae7390bdb9f7978f71ca7144256a2c4616240a6df9002da3c451f9fc8f02" +dependencies = [ + "bare-metal 1.0.0", + "cortex-m", + "cortex-m-rtic-macros", + "heapless", + "rtic-core", + "rtic-monotonic", + "version_check", +] + +[[package]] +name = "cortex-m-rtic-macros" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eefb40b1ca901c759d29526e5c8a0a1b246c20caaa5b4cc5d0f0b94debecd4c7" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "rtic-syntax", + "syn", +] + +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "embedded-alloc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8931e47e33c5d3194fbcf9cc82df0919193bd2fa40008f388eb1d28fd9c9ea6b" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + +[[package]] +name = "embedded-dma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "fugit" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7" +dependencies = [ + "gcd", +] + +[[package]] +name = "fugit-timer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9607bfc4c388f9d629704f56ede4a007546cad417b3bcd6fc7c87dc7edce04a" +dependencies = [ + "fugit", + "nb 1.1.0", +] + +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "hello-can-stm32" +version = "0.1.0" +dependencies = [ + "bxcan", + "cortex-m", + "cortex-m-rt", + "cortex-m-rtic", + "cortex-m-semihosting", + "embedded-alloc", + "embedded-hal", + "heapless", + "nb 1.1.0", + "panic-halt", + "shared-bus-rtic", + "stm32f1xx-hal", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rtic-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9369355b04d06a3780ec0f51ea2d225624db777acbc60abd8ca4832da5c1a42" + +[[package]] +name = "rtic-monotonic" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8b0b822d1a366470b9cea83a1d4e788392db763539dc4ba022bcc787fece82" + +[[package]] +name = "rtic-syntax" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5e215601dc467752c2bddc6284a622c6f3d2bab569d992adcd5ab7e4cb9478" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.20", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "shared-bus-rtic" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb1899470d03c5728db375f63be8f2bbfb93d8c35ec932061f3b593434c2273b" +dependencies = [ + "embedded-hal", + "nb 1.1.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stm32-usbd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c94998f166d66b210a164648a0b7866428d8f1e0740bf8a4c5edd89d4750c1" +dependencies = [ + "cortex-m", + "usb-device", + "vcell", +] + +[[package]] +name = "stm32f1" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dc80735831c28fe85384e1e28428fb6d201f67c696e369a239ed9c5eba369d" +dependencies = [ + "bare-metal 1.0.0", + "cortex-m", + "cortex-m-rt", + "vcell", +] + +[[package]] +name = "stm32f1xx-hal" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30845662b9ce46a2ec04da97666a2b32458bee5032bb0452d0caf1536a96a542" +dependencies = [ + "bitflags", + "bxcan", + "cortex-m", + "cortex-m-rt", + "embedded-dma", + "embedded-hal", + "fugit", + "fugit-timer", + "nb 1.1.0", + "stm32-usbd", + "stm32f1", + "void", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "usb-device" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" +dependencies = [ + "vcell", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1facf5c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "hello-can-stm32" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +embedded-hal = "0.2.7" +cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } +cortex-m-rtic = "1.1.3" +panic-halt = "0.2.0" +cortex-m-semihosting = { version = "0.5.0" } +stm32f1xx-hal = { version = "0.10.0", features = ["rt", "stm32f103"] } +shared-bus-rtic = "0.2.2" +embedded-alloc = "0.5.0" +nb = "1.1.0" +cortex-m-rt = "0.7.3" +bxcan = "0.7.0" +heapless = "0.7.16" + +[features] +default = [] +no-debug = ["cortex-m-semihosting/no-semihosting"] + +[profile.dev] +opt-level = "z" +codegen-units = 1 +overflow-checks = false + +[profile.dev.package."*"] +opt-level = "z" +codegen-units = 1 +overflow-checks = false + +[profile.release] +debug = true +opt-level = "z" +codegen-units = 1 + +[profile.release.package."*"] +debug = true +opt-level = "z" +codegen-units = 1 diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..98f603e --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when memory.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/can-rtic.rs b/can-rtic.rs new file mode 100644 index 0000000..d85b9f6 --- /dev/null +++ b/can-rtic.rs @@ -0,0 +1,253 @@ +//! Interrupt driven CAN transmitter with RTIC. +//! +//! CAN frames are allocated from a static memory pool and stored in a priority +//! queue (min heap) for transmisison. To start transmission the CAN TX +//! interrupt has to be triggered manually once. With each successful +//! transmission the interrupt is reentered and more data is fetched from the +//! queue. +//! Received frames are simply echoed back. In contrast to the naive `can-echo` +//! example all messages are also correctly prioritized by the transmit queue. + +#![no_main] +#![no_std] + +use panic_halt as _; + +use bxcan::Frame; +use core::cmp::Ordering; + +use heapless::binary_heap::{BinaryHeap, Max}; + +use stm32f1xx_hal::pac::Interrupt; + +#[derive(Debug)] +pub struct PriorityFrame(Frame); + +/// Ordering is based on the Identifier and frame type (data vs. remote) and can be used to sort +/// frames by priority. +impl Ord for PriorityFrame { + fn cmp(&self, other: &Self) -> Ordering { + self.0.priority().cmp(&other.0.priority()) + } +} + +impl PartialOrd for PriorityFrame { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for PriorityFrame { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for PriorityFrame {} + +fn enqueue_frame(queue: &mut BinaryHeap, frame: Frame) { + queue.push(PriorityFrame(frame)).unwrap(); + rtic::pend(Interrupt::USB_HP_CAN_TX); +} + +#[rtic::app(device = stm32f1xx_hal::pac)] +mod app { + use super::{enqueue_frame, PriorityFrame}; + use bxcan::{filter::Mask32, ExtendedId, Fifo, Frame, Interrupts, Rx0, StandardId, Tx}; + use heapless::binary_heap::{BinaryHeap, Max}; + use stm32f1xx_hal::{can::Can, pac::CAN1, prelude::*}; + + #[local] + struct Local { + can_tx: Tx>, + can_rx: Rx0>, + } + + #[shared] + struct Shared { + can_tx_queue: BinaryHeap, + tx_count: usize, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let mut flash = cx.device.FLASH.constrain(); + let rcc = cx.device.RCC.constrain(); + + let _clocks = rcc + .cfgr + .use_hse(8.MHz()) + .sysclk(64.MHz()) + .hclk(64.MHz()) + .pclk1(16.MHz()) + .pclk2(64.MHz()) + .freeze(&mut flash.acr); + + #[cfg(not(feature = "connectivity"))] + let can = Can::new(cx.device.CAN1, cx.device.USB); + + #[cfg(feature = "connectivity")] + let can = Can::new(cx.device.CAN1); + + // Select pins for CAN1. + let mut gpioa = cx.device.GPIOA.split(); + let can_rx_pin = gpioa.pa11.into_floating_input(&mut gpioa.crh); + let can_tx_pin = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh); + let mut afio = cx.device.AFIO.constrain(); + can.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + + // APB1 (PCLK1): 16MHz, Bit rate: 1000kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + let mut can = bxcan::Can::builder(can) + .set_bit_timing(0x001c_0000) + .leave_disabled(); + + can.modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + // Sync to the bus and start normal operation. + can.enable_interrupts( + Interrupts::TRANSMIT_MAILBOX_EMPTY | Interrupts::FIFO0_MESSAGE_PENDING, + ); + nb::block!(can.enable_non_blocking()).unwrap(); + + let (can_tx, can_rx, _) = can.split(); + + let can_tx_queue = BinaryHeap::new(); + + ( + Shared { + can_tx_queue, + tx_count: 0, + }, + Local { can_tx, can_rx }, + init::Monotonics(), + ) + } + + #[idle(shared = [can_tx_queue, tx_count])] + fn idle(mut cx: idle::Context) -> ! { + let mut tx_queue = cx.shared.can_tx_queue; + + // Enqueue some messages. Higher ID means lower priority. + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(9).unwrap(), []), + ); + enqueue_frame( + &mut tx_queue, + Frame::new_data(ExtendedId::new(9).unwrap(), []), + ); + + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(8).unwrap(), []), + ); + enqueue_frame( + &mut tx_queue, + Frame::new_data(ExtendedId::new(8).unwrap(), []), + ); + + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(0x7FF).unwrap(), []), + ); + enqueue_frame( + &mut tx_queue, + Frame::new_data(ExtendedId::new(0x1FFF_FFFF).unwrap(), []), + ); + }); + + // Add some higher priority messages when 3 messages have been sent. + loop { + let tx_count = cx.shared.tx_count.lock(|tx_count| *tx_count); + + if tx_count >= 3 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(3).unwrap(), []), + ); + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(2).unwrap(), []), + ); + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(1).unwrap(), []), + ); + }); + break; + } + } + + // Expected bus traffic: + // + // 1. ID: 0x00000008 <- proper reordering happens + // 2. ID: 0x00000009 + // 3. ID: 0x008 + // 4. ID: 0x001 <- higher priority messages injected correctly + // 5. ID: 0x002 + // 6. ID: 0x003 + // 7. ID: 0x009 + // 8. ID: 0x7FF + // 9. ID: 0x1FFFFFFF + // + // The output can look different if there are other nodes on bus the sending messages. + + loop { + cortex_m::asm::nop(); + } + } + + // This ISR is triggered by each finished frame transmission. + #[task(binds = USB_HP_CAN_TX, local = [can_tx], shared = [can_tx_queue, tx_count])] + fn can_tx(cx: can_tx::Context) { + let tx = cx.local.can_tx; + let mut tx_queue = cx.shared.can_tx_queue; + let mut tx_count = cx.shared.tx_count; + + tx.clear_interrupt_flags(); + + // There is now a free mailbox. Try to transmit pending frames until either + // the queue is empty or transmission would block the execution of this ISR. + (&mut tx_queue, &mut tx_count).lock(|tx_queue, tx_count| { + while let Some(frame) = tx_queue.peek() { + match tx.transmit(&frame.0) { + Ok(status) => match status.dequeued_frame() { + None => { + // Frame was successfully placed into a transmit buffer. + tx_queue.pop(); + *tx_count += 1; + } + Some(pending_frame) => { + // A lower priority frame was replaced with our high priority frame. + // Put the low priority frame back in the transmit queue. + tx_queue.pop(); + enqueue_frame(tx_queue, pending_frame.clone()); + } + }, + Err(nb::Error::WouldBlock) => break, + Err(_) => unreachable!(), + } + } + }); + } + + #[task(binds = USB_LP_CAN_RX0, local = [can_rx], shared = [can_tx_queue])] + fn can_rx0(mut cx: can_rx0::Context) { + // Echo back received packages with correct priority ordering. + loop { + match cx.local.can_rx.receive() { + Ok(frame) => { + cx.shared.can_tx_queue.lock(|can_tx_queue| { + enqueue_frame(can_tx_queue, frame); + }); + } + Err(nb::Error::WouldBlock) => break, + Err(nb::Error::Other(_)) => {} // Ignore overrun errors. + } + } + } +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..6b059f1 --- /dev/null +++ b/memory.x @@ -0,0 +1,6 @@ +/* Linker script for the STM32F103C6T6 */ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 32K + RAM : ORIGIN = 0x20000000, LENGTH = 10K +} diff --git a/openocd.cfg b/openocd.cfg new file mode 100644 index 0000000..1d835cf --- /dev/null +++ b/openocd.cfg @@ -0,0 +1,13 @@ +# Sample OpenOCD configuration for the STM32F3DISCOVERY development board + +# Depending on the hardware revision you got you'll have to pick ONE of these +# interfaces. At any time only one interface should be commented out. + +# Revision C (newer revision) +#source [find interface/stlink-v2-1.cfg] +source [find interface/stlink-v2.cfg] + +# Revision A and B (older revisions) +# source [find interface/stlink-v2.cfg] + +source [find target/stm32f1x.cfg] diff --git a/openocd.gdb b/openocd.gdb new file mode 100644 index 0000000..7795319 --- /dev/null +++ b/openocd.gdb @@ -0,0 +1,40 @@ +target extended-remote :3333 + +# print demangled symbols +set print asm-demangle on + +# set backtrace limit to not have infinite backtrace loops +set backtrace limit 32 + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind +# # run the next few lines so the panic message is printed immediately +# # the number needs to be adjusted for your panic handler +# commands $bpnum +# next 4 +# end + +# *try* to stop at the user entry point (it might be gone due to inlining) +break main + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.txt uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 8000000 must match the core clock frequency +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load + +# start the process but immediately halt the processor +stepi diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c8ed0ee --- /dev/null +++ b/src/main.rs @@ -0,0 +1,271 @@ +#![no_std] +#![no_main] + +use embedded_alloc::Heap; +use panic_halt as _; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +use bxcan::Frame; +use core::cmp::Ordering; +use heapless::binary_heap::{BinaryHeap, Max}; + +use stm32f1xx_hal::pac::Interrupt; +extern crate alloc; + +#[derive(Debug)] +pub struct PriorityFrame(Frame); + +/// Ordering is based on the Identifier and frame type (data vs. remote) and can be used to sort +/// frames by priority. +impl Ord for PriorityFrame { + fn cmp(&self, other: &Self) -> Ordering { + self.0.priority().cmp(&other.0.priority()) + } +} + +impl PartialOrd for PriorityFrame { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for PriorityFrame { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for PriorityFrame {} + +fn enqueue_frame(queue: &mut BinaryHeap, frame: Frame) { + queue.push(PriorityFrame(frame)).unwrap(); + rtic::pend(Interrupt::USB_HP_CAN_TX); +} + +#[rtic::app(device = stm32f1xx_hal::pac)] +mod app { + use cortex_m_semihosting::hprintln; + use heapless::binary_heap::{BinaryHeap, Max}; + + use super::{enqueue_frame, PriorityFrame}; + use bxcan::{filter::Mask32, Fifo, Frame, Interrupts, Rx0, StandardId, Tx}; + use stm32f1xx_hal::{ + can::Can, + device::TIM2, + gpio::{Output, Pin}, + pac::CAN1, + prelude::*, + timer::Delay, + }; + + #[shared] + struct Shared { + can_tx_queue: BinaryHeap, + tx_count: usize, + } + + #[local] + struct Local { + can_tx: Tx>, + can_rx: Rx0>, + led: Pin<'C', 13, Output>, + delay: Delay, + counter: usize, + } + + #[init] + fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { + let mut flash = ctx.device.FLASH.constrain(); + let rcc = ctx.device.RCC.constrain(); + + // let clocks = rcc.cfgr.freeze(&mut flash.acr); + + let clocks = rcc + .cfgr + .use_hse(8.MHz()) + .sysclk(64.MHz()) + .hclk(64.MHz()) + .pclk1(16.MHz()) + .pclk2(64.MHz()) + .freeze(&mut flash.acr); + + let can = Can::new(ctx.device.CAN1, ctx.device.USB); + let mut gpioa = ctx.device.GPIOA.split(); + let can_rx_pin = gpioa.pa11.into_floating_input(&mut gpioa.crh); + let can_tx_pin = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh); + let mut afio = ctx.device.AFIO.constrain(); + can.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + + // APB1 (PCLK1): 16MHz, Bit rate: 1000kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + let mut can = bxcan::Can::builder(can) + .set_bit_timing(0x001c_0000) + .leave_disabled(); + + can.modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + // Sync to the bus and start normal operation. + can.enable_interrupts( + Interrupts::TRANSMIT_MAILBOX_EMPTY | Interrupts::FIFO0_MESSAGE_PENDING, + ); + + nb::block!(can.enable_non_blocking()).unwrap(); + + let (can_tx, can_rx, _) = can.split(); + + let can_tx_queue = BinaryHeap::new(); + + let delay = ctx.device.TIM2.delay_us(&clocks); + + let mut gpioc = ctx.device.GPIOC.split(); + let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); + + ( + Shared { + can_tx_queue, + tx_count: 0, + }, + Local { + can_tx, + can_rx, + led, + delay, + counter: 0, + }, + init::Monotonics(), + ) + } + + fn speed2array(speed: i16) -> [u8; 8] { + let b = speed.to_be_bytes(); + [b[0], b[1], b[0], b[1], b[0], b[1], b[0], b[1]] + } + + #[idle(shared = [can_tx_queue, tx_count], local = [delay])] + fn idle(mut cx: idle::Context) -> ! { + let mut tx_queue = cx.shared.can_tx_queue; + + loop { + for speed in 0..=30 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(0x1FF).unwrap(), speed2array(speed * 1000)), + ); + }); + + cx.local.delay.delay(100.millis()); + } + + for _ in 0..10 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(0x1FF).unwrap(), speed2array(30000)), + ); + }); + + cx.local.delay.delay(100.millis()); + } + + for speed in 0..=30 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data( + StandardId::new(0x1FF).unwrap(), + speed2array(30000 - (speed * 1000)), + ), + ); + }); + + cx.local.delay.delay(100.millis()); + } + + for speed in 0..=30 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(0x1FF).unwrap(), speed2array(speed * 1000 * -1)), + ); + }); + + cx.local.delay.delay(100.millis()); + } + + for _ in 0..10 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data(StandardId::new(0x1FF).unwrap(), speed2array(30000 * -1)), + ); + }); + + cx.local.delay.delay(100.millis()); + } + + for speed in 0..=30 { + tx_queue.lock(|mut tx_queue| { + enqueue_frame( + &mut tx_queue, + Frame::new_data( + StandardId::new(0x1FF).unwrap(), + speed2array(( 30000 - (speed * 1000) ) * -1), + ), + ); + }); + + cx.local.delay.delay(100.millis()); + } + } + } + + // This ISR is triggered by each finished frame transmission. + #[task(binds = USB_HP_CAN_TX, local = [can_tx, led], shared = [can_tx_queue, tx_count])] + fn can_tx(cx: can_tx::Context) { + let tx = cx.local.can_tx; + let mut tx_queue = cx.shared.can_tx_queue; + let mut tx_count = cx.shared.tx_count; + + tx.clear_interrupt_flags(); + + // There is now a free mailbox. Try to transmit pending frames until either + // the queue is empty or transmission would block the execution of this ISR. + (&mut tx_queue, &mut tx_count).lock(|tx_queue, tx_count| { + while let Some(frame) = tx_queue.peek() { + match tx.transmit(&frame.0) { + Ok(status) => match status.dequeued_frame() { + None => { + tx_queue.pop(); + *tx_count += 1; + cx.local.led.toggle(); + } + Some(pending_frame) => { + tx_queue.pop(); + enqueue_frame(tx_queue, pending_frame.clone()); + } + }, + Err(nb::Error::WouldBlock) => break, + Err(_) => unreachable!(), + } + } + }); + } + + #[task(binds = USB_LP_CAN_RX0, local = [can_rx], shared = [can_tx_queue])] + fn can_rx0(mut cx: can_rx0::Context) { + // Echo back received packages with correct priority ordering. + loop { + match cx.local.can_rx.receive() { + Ok(frame) => { + // let data = frame.data().unwrap(); + } + Err(nb::Error::WouldBlock) => break, + Err(nb::Error::Other(_)) => {} // Ignore overrun errors. + } + } + } +}