From 2d0a54eccc9379f557718548b55a8eb9731c9d52 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 3 Oct 2024 22:54:05 +0200 Subject: [PATCH] Triggering the dfu bootloader works --- Cargo.lock | 102 +++++++++++++++++++++++++++++++++- daemon/Cargo.toml | 3 + daemon/src/bin/trigger-dfu.rs | 16 ++++++ daemon/src/lib.rs | 3 + daemon/src/main.rs | 13 ++--- firmware/.cargo/config.toml | 2 +- firmware/src/bootloader.rs | 59 ++++++++++++++++++++ firmware/src/main.rs | 60 ++++++++++++-------- memory.x | 17 +++++- 9 files changed, 239 insertions(+), 36 deletions(-) create mode 100644 daemon/src/bin/trigger-dfu.rs create mode 100644 daemon/src/lib.rs create mode 100644 firmware/src/bootloader.rs diff --git a/Cargo.lock b/Cargo.lock index 3a5c99c..5a45f58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cortex-m" version = "0.7.7" @@ -367,6 +373,12 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + [[package]] name = "embedded-io" version = "0.6.1" @@ -664,6 +676,16 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-kit-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" +dependencies = [ + "core-foundation-sys", + "mach2", +] + [[package]] name = "is-terminal" version = "0.4.12" @@ -699,6 +721,26 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libudev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "litrs" version = "0.4.1" @@ -721,6 +763,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "matchit" version = "0.7.3" @@ -791,6 +842,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -971,6 +1033,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "portable-atomic" version = "1.7.0" @@ -985,6 +1053,8 @@ checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", "defmt", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless 0.7.17", "serde", ] @@ -1091,7 +1161,10 @@ dependencies = [ "humantime", "log", "nom", + "postcard", + "radomctl-protocol", "serde_json", + "serialport", "tokio", "tokio-macros 0.2.6", "tower-http", @@ -1299,6 +1372,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serialport" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba776acc8c373b9175829206229366273225436845c04f9c20aab8099960e2e" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "core-foundation-sys", + "io-kit-sys", + "libudev", + "mach2", + "nix", + "scopeguard", + "unescaper", + "winapi", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1672,6 +1763,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" +[[package]] +name = "unescaper" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" +dependencies = [ + "thiserror", +] + [[package]] name = "unicase" version = "2.7.0" @@ -1704,7 +1804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" dependencies = [ "embedded-hal 0.2.7", - "embedded-io", + "embedded-io 0.6.1", "nb 1.1.0", "usb-device", ] diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 8f34f13..bdcee7f 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -11,8 +11,11 @@ humantime = "2.1.0" log = "0.4.21" nom = "7.1.3" serde_json = "1.0.118" +serialport = "4.5.1" tokio = {version = "1.37.0", features = ["full"]} tokio-macros = { version = "0.2.0-alpha.6" } tower-http = { version = "0.5.2", features = ["fs", "trace"] } tracing = "0.1.40" tracing-subscriber = "0.3.18" +radomctl-protocol = { path = "../protocol" } +postcard = {version = "1.0.10", features = ["use-std"]} diff --git a/daemon/src/bin/trigger-dfu.rs b/daemon/src/bin/trigger-dfu.rs new file mode 100644 index 0000000..472f081 --- /dev/null +++ b/daemon/src/bin/trigger-dfu.rs @@ -0,0 +1,16 @@ +use std::time::Duration; + +use postcard::{from_bytes_cobs, to_stdvec_cobs}; +use radomctl_protocol::*; +use radomctld::logger::setup_logger; + +pub fn main() -> () { + let mut port = serialport::new("/dev/ttyACM0", 115_200) + .timeout(Duration::from_millis(10)) + .open() + .expect("Failed to open port"); + + let host_msg = HostMessage::TriggerDFUBootloader; + let msg_bytes = to_stdvec_cobs(&host_msg).unwrap(); + port.write_all(&msg_bytes).unwrap(); +} diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs new file mode 100644 index 0000000..85f9e01 --- /dev/null +++ b/daemon/src/lib.rs @@ -0,0 +1,3 @@ +pub mod logger; +pub mod rotctlprotocol; +pub mod rotor; diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 5698a6c..e52eece 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1,7 +1,3 @@ -mod logger; -mod rotctlprotocol; -mod rotor; - use anyhow::Result; use fern::colors::{Color, ColoredLevelConfig}; use log::{debug, error, info, warn, Level}; @@ -26,10 +22,11 @@ use tower_http::{ trace::TraceLayer, }; -use logger::setup_logger; -use rotor::control_rotor; - -use rotctlprotocol::{parse_command, Command}; +use radomctld::{ + logger::setup_logger, + rotctlprotocol::{parse_command, Command}, + rotor::control_rotor, +}; async fn process_socket( socket: TcpStream, diff --git a/firmware/.cargo/config.toml b/firmware/.cargo/config.toml index d190172..f289182 100644 --- a/firmware/.cargo/config.toml +++ b/firmware/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) -runner = "probe-run --chip STM32F401CCU6" +runner = "probe-rs run --chip STM32F401CCUx" rustflags = [ "-C", "linker=flip-link", "-C", "link-arg=-Tlink.x", diff --git a/firmware/src/bootloader.rs b/firmware/src/bootloader.rs new file mode 100644 index 0000000..382e442 --- /dev/null +++ b/firmware/src/bootloader.rs @@ -0,0 +1,59 @@ +use core::ptr::addr_of_mut; + +use stm32f4xx_hal::pac; + +fn jump_to_bootloader() { + unsafe { + cortex_m::interrupt::disable(); + + let address: u32 = 0x1FFF0000; + + let device = pac::Peripherals::steal(); + device.SYSCFG.memrm.modify(|_, w| w.bits(0x01)); + + let mut p = cortex_m::Peripherals::steal(); + p.SCB.invalidate_icache(); + p.SCB.vtor.write(address as u32); + + cortex_m::interrupt::enable(); + + cortex_m::asm::bootload(address as *const u32); + } +} + +const BOOTLOADER_REQUESTED: u32 = 0xdecafbad; + +fn magic_mut_ptr() -> *mut u32 { + extern "C" { + #[link_name = "_dfu_magic"] + static mut magic: u32; + } + + unsafe { addr_of_mut!(magic) } +} + +fn get_bootloader_flag() -> u32 { + unsafe { magic_mut_ptr().read_volatile() } +} + +fn set_bootloader_flag() { + unsafe { magic_mut_ptr().write_volatile(BOOTLOADER_REQUESTED) }; +} + +fn clear_bootloader_flag() { + unsafe { magic_mut_ptr().write_volatile(BOOTLOADER_REQUESTED) }; +} + +pub fn init() { + let requested = get_bootloader_flag() == BOOTLOADER_REQUESTED; + if requested { + clear_bootloader_flag(); + jump_to_bootloader(); + } +} + +pub fn reboot_to_bootloader() -> ! { + defmt::info!("Rebooting into the bootloader"); + set_bootloader_flag(); + cortex_m::peripheral::SCB::sys_reset(); +} diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 210dc03..01f3204 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -6,6 +6,8 @@ use defmt_brtt as _; // global logger use panic_probe as _; use stm32f4xx_hal as _; +mod bootloader; + // same panicking *behavior* as `panic-probe` but doesn't print a panic message // this prevents the panic message being printed *twice* when `defmt::panic` is invoked #[defmt::panic_handler] @@ -21,7 +23,9 @@ mod app { use as5048a::AS5048A; - use heapless::Vec; + use core::fmt::Write; + use heapless::{String, Vec}; + use num_traits::{Float, FloatConst}; use postcard::{from_bytes_cobs, to_vec_cobs}; use stm32f4xx_hal::{ gpio::{gpioa, gpiob, gpioc, Output, PushPull}, @@ -29,14 +33,12 @@ mod app { otg_fs::{UsbBus, UsbBusType, USB}, pac::{I2C1, SPI1}, prelude::*, - spi, + signature, spi, }; - use usb_device::class_prelude::UsbBusAllocator; use usb_device::prelude::*; + use usb_device::{class_prelude::UsbBusAllocator, device}; use usbd_serial::SerialPort; - use num_traits::{Float, FloatConst}; - use xca9548a::{SlaveAddr, Xca9548a}; use qmc5883l::{self, QMC5883L}; @@ -45,6 +47,8 @@ mod app { use rtic_monotonics::systick::prelude::*; + use crate::bootloader; + systick_monotonic!(Mono, 1000); const USB_BUFFER_SIZE: usize = 64; @@ -82,6 +86,8 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local) { + bootloader::init(); + defmt::info!("init"); let rcc = cx.device.RCC.constrain(); @@ -91,11 +97,8 @@ mod app { let clocks = rcc .cfgr .use_hse(25.MHz()) - .require_pll48clk() .sysclk(84.MHz()) - .hclk(84.MHz()) - .pclk1(42.MHz()) - .pclk2(84.MHz()) + .require_pll48clk() .freeze(); Mono::start(cx.core.SYST, clocks.sysclk().to_Hz()); @@ -130,16 +133,23 @@ mod app { let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); - let usb_dev = UsbDeviceBuilder::new( - unsafe { USB_BUS.as_ref().unwrap() }, - UsbVidPid(0x16c0, 0x27dd), - ) - .device_class(usbd_serial::USB_CLASS_CDC) - .strings(&[StringDescriptors::default() - .manufacturer("Amteurfunk Forschungs Gruppe") - .product("Radom Controler")]) - .unwrap() - .build(); + let uid = signature::Uid::get(); + + static mut SERIAL: String<16> = String::new(); + unsafe { + write!(SERIAL, "{}{:x}{:x}", uid.lot_num(), uid.x(), uid.y()).unwrap(); + } + + let usb_dev = unsafe { + UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0x16c0, 0x27dd)) + .device_class(usbd_serial::USB_CLASS_CDC) + .strings(&[StringDescriptors::default() + .manufacturer("Amteurfunk Forschungs Gruppe") + .product("Radom Controler") + .serial_number(SERIAL.as_ref())]) + .unwrap() + .build() + }; defmt::info!("USB Setup done"); @@ -158,7 +168,7 @@ mod app { defmt::info!("I2C Setup done"); let mut i2cmux = Xca9548a::new(i2c, SlaveAddr::default()); - i2cmux.select_channels(0b0000_0001).unwrap(); + //i2cmux.select_channels(0b0000_0001).unwrap(); defmt::info!("I2C MUX Setup done"); @@ -197,8 +207,8 @@ mod app { defmt::info!("Motor Setup done"); - poll_i2c::spawn().ok(); - poll_spi::spawn().ok(); + //poll_i2c::spawn().ok(); + //poll_spi::spawn().ok(); ( Shared { az_angle: 0 }, @@ -402,9 +412,11 @@ mod app { to_vec_cobs::(&device_msg).unwrap(); serial.write(bytes.as_slice()).unwrap(); } - HostMessage::TriggerDFUBootloader => todo!(), + HostMessage::TriggerDFUBootloader => { + bootloader::reboot_to_bootloader(); + } }, - Err(err) => defmt::error!("Unable to parse host message: {}", err), + Err(err) => defmt::error!("Unable to parse host message"), }; *buffer = Vec::::from_slice(rest).unwrap(); diff --git a/memory.x b/memory.x index dfe3ff3..606d5fd 100644 --- a/memory.x +++ b/memory.x @@ -1,10 +1,23 @@ MEMORY { - /* NOTE K = KiBi = 1024 bytes */ FLASH : ORIGIN = 0x08000000, LENGTH = 256K - RAM : ORIGIN = 0x20000000, LENGTH = 64K + UNINIT: ORIGIN = 0x20000000, LENGTH = 0x10 + RAM : ORIGIN = 0x20000010, LENGTH = 64K - LENGTH(UNINIT) } +SECTIONS { + .uninit_flags (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit_flags = .; + _dfu_magic = .; . += 4; + *(.uninit_flags .uninit_flags.*) + . = ALIGN(4); + __euninit_flags = .; + } > UNINIT +} + + /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* NOTE Do NOT modify `_stack_start` unless you know what you are doing */