This commit is contained in:
yanorei32 2023-10-10 22:58:41 +09:00
commit 49b27a674e
11 changed files with 1187 additions and 0 deletions

8
.cargo/config Normal file
View File

@ -0,0 +1,8 @@
[target.thumbv7m-none-eabi]
runner = 'arm-none-eabi-gdb'
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv7m-none-eabi"

18
.gdbinit Normal file
View File

@ -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

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

515
Cargo.lock generated Normal file
View File

@ -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",
]

44
Cargo.toml Normal file
View File

@ -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

18
build.rs Normal file
View File

@ -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");
}

253
can-rtic.rs Normal file
View File

@ -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<Ordering> {
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<PriorityFrame, Max, 16>, 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<CAN1>>,
can_rx: Rx0<Can<CAN1>>,
}
#[shared]
struct Shared {
can_tx_queue: BinaryHeap<PriorityFrame, Max, 16>,
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.
}
}
}
}

6
memory.x Normal file
View File

@ -0,0 +1,6 @@
/* Linker script for the STM32F103C6T6 */
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 32K
RAM : ORIGIN = 0x20000000, LENGTH = 10K
}

13
openocd.cfg Normal file
View File

@ -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]

40
openocd.gdb Normal file
View File

@ -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

271
src/main.rs Normal file
View File

@ -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<Ordering> {
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<PriorityFrame, Max, 16>, 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<PriorityFrame, Max, 16>,
tx_count: usize,
}
#[local]
struct Local {
can_tx: Tx<Can<CAN1>>,
can_rx: Rx0<Can<CAN1>>,
led: Pin<'C', 13, Output>,
delay: Delay<TIM2, 1000000>,
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.
}
}
}
}