Merge branch 'master' into rj/fast-dbm

* master:
  adapt to new heapless/serde-json-core after const-generics
  Bump serde-json-core from 0.3.0 to 0.4.0
  build(deps): bump heapless from 0.6.1 to 0.7.1
  setup: cleanup
  itcm: add some comments, make it safe
  build(deps): bump ndarray from 0.15.1 to 0.15.2
  Updating dependencies
  Updating the embedded-nal
  Removing spurious settings updates
  deps: add rationales for git dependencies
  itcm: implement in rust and execute during setup()
  remove duplicate linker option
  gha: install gcc
  fmt
  dependencies: align with master
  bump cortex-m-rt to 0.6.13+git
  memory.x: remove comment about old cortex-m-rt
  enable itcm/dtcm explicitly
  load process into itcm
This commit is contained in:
Robert Jördens 2021-06-01 12:52:05 +02:00
commit 73187ba053
13 changed files with 199 additions and 105 deletions

93
Cargo.lock generated
View File

@ -42,6 +42,15 @@ dependencies = [
"embedded-hal", "embedded-hal",
] ]
[[package]]
name = "atomic-polyfill"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30302dda7a66f8c55932ebf208f7def840743ff64d495e9ceffcd97c18f11d39"
dependencies = [
"cortex-m 0.7.2",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
@ -136,18 +145,15 @@ dependencies = [
[[package]] [[package]]
name = "cortex-m-rt" name = "cortex-m-rt"
version = "0.6.13" version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/rust-embedded/cortex-m-rt.git?rev=a2e3ad5#a2e3ad54478c6b98e519a1b0946395d790c0b6c7"
checksum = "980c9d0233a909f355ed297ef122f257942de5e0a2cb1c39f60684b65bcb90fb"
dependencies = [ dependencies = [
"cortex-m-rt-macros", "cortex-m-rt-macros",
"r0",
] ]
[[package]] [[package]]
name = "cortex-m-rt-macros" name = "cortex-m-rt-macros"
version = "0.1.8" version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/rust-embedded/cortex-m-rt.git?rev=a2e3ad5#a2e3ad54478c6b98e519a1b0946395d790c0b6c7"
checksum = "4717562afbba06e760d34451919f5c3bf3ac15c7bb897e8b04862a7428378647"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -194,7 +200,7 @@ dependencies = [
[[package]] [[package]]
name = "derive_miniconf" name = "derive_miniconf"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/quartiq/miniconf.git?rev=c6f2b28#c6f2b28f735e27b337eaa986846536e904c6f2bd" source = "git+https://github.com/quartiq/miniconf.git?rev=2750533#275053396f0334e9efefa1ab2aae4c19b95a9a53"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -240,11 +246,11 @@ dependencies = [
[[package]] [[package]]
name = "embedded-nal" name = "embedded-nal"
version = "0.1.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae46eb1b02de5a76d9d0ea21d657ff5b0ad2cc47f3a7723608227b1dd1b3eb18" checksum = "db9efecb57ab54fa918730f2874d7d37647169c50fa1357fecb81abee840b113"
dependencies = [ dependencies = [
"heapless 0.5.6", "heapless 0.7.1",
"nb 1.0.0", "nb 1.0.0",
"no-std-net", "no-std-net",
] ]
@ -317,24 +323,21 @@ dependencies = [
"byteorder", "byteorder",
] ]
[[package]]
name = "hash32"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
dependencies = [
"byteorder",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.9.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heapless"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1"
dependencies = [
"as-slice",
"generic-array 0.13.3",
"hash32",
"stable_deref_trait",
]
[[package]] [[package]]
name = "heapless" name = "heapless"
version = "0.6.1" version = "0.6.1"
@ -343,7 +346,18 @@ checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422"
dependencies = [ dependencies = [
"as-slice", "as-slice",
"generic-array 0.14.4", "generic-array 0.14.4",
"hash32", "hash32 0.1.1",
"stable_deref_trait",
]
[[package]]
name = "heapless"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7ee8a997d259962217f40279f34201fdf06e669bafa69d7c1f4c7ff1893b5f6"
dependencies = [
"atomic-polyfill",
"hash32 0.2.1",
"serde", "serde",
"stable_deref_trait", "stable_deref_trait",
] ]
@ -399,7 +413,7 @@ dependencies = [
[[package]] [[package]]
name = "miniconf" name = "miniconf"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/quartiq/miniconf.git?rev=c6f2b28#c6f2b28f735e27b337eaa986846536e904c6f2bd" source = "git+https://github.com/quartiq/miniconf.git?rev=2750533#275053396f0334e9efefa1ab2aae4c19b95a9a53"
dependencies = [ dependencies = [
"derive_miniconf", "derive_miniconf",
"serde", "serde",
@ -409,13 +423,12 @@ dependencies = [
[[package]] [[package]]
name = "minimq" name = "minimq"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/quartiq/minimq.git?rev=d2ec3e8#d2ec3e8351fa403ea96defd98c0b4410cbaa18a4" source = "git+https://github.com/quartiq/minimq.git?rev=dbdbec0#dbdbec0b77d2e134dc6c025018a82c14cbdfbe34"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"embedded-nal", "embedded-nal",
"enum-iterator", "enum-iterator",
"generic-array 0.14.4", "heapless 0.7.1",
"heapless 0.6.1",
] ]
[[package]] [[package]]
@ -441,9 +454,9 @@ checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
[[package]] [[package]]
name = "ndarray" name = "ndarray"
version = "0.15.1" version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc1372704f14bb132a49a6701c2238970a359ee0829fed481b522a63bf25456a" checksum = "02b2e4807aaa21dc6dcc3417e5902dc199c3648043bf27b7af4b202332fe4760"
dependencies = [ dependencies = [
"matrixmultiply", "matrixmultiply",
"num-complex", "num-complex",
@ -454,9 +467,9 @@ dependencies = [
[[package]] [[package]]
name = "no-std-net" name = "no-std-net"
version = "0.4.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2178127478ae4ee9be7180bc9c3bffb6354dd7238400db567102f98c413a9f35" checksum = "1bcece43b12349917e096cddfa66107277f123e6c96a5aea78711dc601a47152"
[[package]] [[package]]
name = "num" name = "num"
@ -572,12 +585,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r0"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.3" version = "0.8.3"
@ -701,11 +708,11 @@ dependencies = [
[[package]] [[package]]
name = "serde-json-core" name = "serde-json-core"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39af17f40c2a28d2c9a7918663ddc8a10f54cc6f109ead5c3f010869761df186" checksum = "8014aeea272bca0f0779778d43253f2f3375b414185b30e6ecc4d3e4a9994781"
dependencies = [ dependencies = [
"heapless 0.6.1", "heapless 0.7.1",
"ryu", "ryu",
"serde", "serde",
] ]
@ -745,10 +752,10 @@ dependencies = [
[[package]] [[package]]
name = "smoltcp-nal" name = "smoltcp-nal"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=4a1711c#4a1711c54cdf79f5ee8c1c99a1e8984f5944270c" source = "git+https://github.com/quartiq/smoltcp-nal.git?rev=5e56576#5e56576fbbc594f0a0e1df9222a20eb35c8e7511"
dependencies = [ dependencies = [
"embedded-nal", "embedded-nal",
"heapless 0.6.1", "heapless 0.7.1",
"nanorand", "nanorand",
"smoltcp", "smoltcp",
] ]
@ -764,7 +771,7 @@ dependencies = [
"cortex-m-rtic", "cortex-m-rtic",
"dsp", "dsp",
"embedded-hal", "embedded-hal",
"heapless 0.6.1", "heapless 0.7.1",
"log", "log",
"mcp23017", "mcp23017",
"miniconf", "miniconf",

View File

@ -33,7 +33,7 @@ cortex-m-rt = { version = "0.6", features = ["device"] }
log = { version = "0.4", features = ["max_level_trace", "release_max_level_info"] } log = { version = "0.4", features = ["max_level_trace", "release_max_level_info"] }
rtt-target = { version = "0.3", features = ["cortex-m"] } rtt-target = { version = "0.3", features = ["cortex-m"] }
serde = { version = "1.0", features = ["derive"], default-features = false } serde = { version = "1.0", features = ["derive"], default-features = false }
heapless = { version = "0.6", features = ["serde"] } heapless = { version = "0.7", features = ["serde"] }
cortex-m-rtic = "0.5.6" cortex-m-rtic = "0.5.6"
embedded-hal = "0.2.5" embedded-hal = "0.2.5"
nb = "1.0.0" nb = "1.0.0"
@ -44,12 +44,14 @@ dsp = { path = "dsp" }
ad9959 = { path = "ad9959" } ad9959 = { path = "ad9959" }
miniconf = "0.1.0" miniconf = "0.1.0"
shared-bus = {version = "0.2.2", features = ["cortex-m"] } shared-bus = {version = "0.2.2", features = ["cortex-m"] }
serde-json-core = "0.3" serde-json-core = "0.4"
# rtt-target bump
[dependencies.rtt-logger] [dependencies.rtt-logger]
git = "https://github.com/quartiq/rtt-logger.git" git = "https://github.com/quartiq/rtt-logger.git"
rev = "70b0eb5" rev = "70b0eb5"
# rewrite
[dependencies.mcp23017] [dependencies.mcp23017]
git = "https://github.com/lucazulian/mcp23017.git" git = "https://github.com/lucazulian/mcp23017.git"
rev = "523d71d" rev = "523d71d"
@ -60,17 +62,22 @@ features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"]
git = "https://github.com/quartiq/stm32h7xx-hal.git" git = "https://github.com/quartiq/stm32h7xx-hal.git"
rev = "cca4ecc" rev = "cca4ecc"
# link.x section start/end
[patch.crates-io.cortex-m-rt]
git = "https://github.com/rust-embedded/cortex-m-rt.git"
rev = "a2e3ad5"
[patch.crates-io.miniconf] [patch.crates-io.miniconf]
git = "https://github.com/quartiq/miniconf.git" git = "https://github.com/quartiq/miniconf.git"
rev = "c6f2b28" rev = "2750533"
[dependencies.smoltcp-nal] [dependencies.smoltcp-nal]
git = "https://github.com/quartiq/smoltcp-nal.git" git = "https://github.com/quartiq/smoltcp-nal.git"
rev = "4a1711c" rev = "5e56576"
[dependencies.minimq] [dependencies.minimq]
git = "https://github.com/quartiq/minimq.git" git = "https://github.com/quartiq/minimq.git"
rev = "d2ec3e8" rev = "dbdbec0"
[features] [features]
nightly = ["cortex-m/inline-asm", "dsp/nightly"] nightly = ["cortex-m/inline-asm", "dsp/nightly"]

3
build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("cargo:rerun-if-changed=memory.x");
}

View File

@ -13,10 +13,6 @@ MEMORY
} }
SECTIONS { SECTIONS {
.itcm : ALIGN(8) {
*(.itcm .itcm.*);
. = ALIGN(8);
} > ITCM
.axisram (NOLOAD) : ALIGN(8) { .axisram (NOLOAD) : ALIGN(8) {
*(.axisram .axisram.*); *(.axisram .axisram.*);
. = ALIGN(8); . = ALIGN(8);
@ -33,4 +29,18 @@ SECTIONS {
*(.sram3 .sram3.*); *(.sram3 .sram3.*);
. = ALIGN(4); . = ALIGN(4);
} > SRAM3 } > SRAM3
} INSERT AFTER .bss; .itcm : ALIGN(8) {
. = ALIGN(8);
__sitcm = .;
*(.itcm .itcm.*);
. = ALIGN(8);
__eitcm = .;
} > ITCM AT>FLASH
__siitcm = LOADADDR(.itcm);
} INSERT AFTER .uninit;
ASSERT(__sitcm % 8 == 0 && __eitcm % 8 == 0, "
BUG(cortex-m-rt): .itcm is not 8-byte aligned");
ASSERT(__siitcm % 4 == 0, "
BUG(cortex-m-rt): the LMA of .itcm is not 4-byte aligned");

View File

@ -15,7 +15,7 @@ use hardware::{
DigitalInput0, DigitalInput1, InputPin, SystemTimer, AFE0, AFE1, DigitalInput0, DigitalInput1, InputPin, SystemTimer, AFE0, AFE1,
}; };
use net::{NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}; use net::{NetworkState, NetworkUsers, Telemetry, TelemetryBuffer};
const SCALE: f32 = i16::MAX as _; const SCALE: f32 = i16::MAX as _;
@ -131,6 +131,8 @@ const APP: () = {
/// Because the ADC and DAC operate at the same rate, these two constraints actually implement /// Because the ADC and DAC operate at the same rate, these two constraints actually implement
/// the same time bounds, meeting one also means the other is also met. /// the same time bounds, meeting one also means the other is also met.
#[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, telemetry], priority=2)] #[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, telemetry], priority=2)]
#[inline(never)]
#[link_section = ".itcm.process"]
fn process(mut c: process::Context) { fn process(mut c: process::Context) {
let process::Resources { let process::Resources {
adcs: (ref mut adc0, ref mut adc1), adcs: (ref mut adc0, ref mut adc1),
@ -194,8 +196,11 @@ const APP: () = {
fn idle(mut c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
loop { loop {
match c.resources.network.lock(|net| net.update()) { match c.resources.network.lock(|net| net.update()) {
UpdateState::Updated => c.spawn.settings_update().unwrap(), NetworkState::SettingsChanged => {
UpdateState::NoChange => cortex_m::asm::wfi(), c.spawn.settings_update().unwrap()
}
NetworkState::Updated => {}
NetworkState::NoChange => cortex_m::asm::wfi(),
} }
} }
} }

View File

@ -17,7 +17,7 @@ use stabilizer::hardware::{
}; };
use miniconf::Miniconf; use miniconf::Miniconf;
use net::{NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}; use net::{NetworkState, NetworkUsers, Telemetry, TelemetryBuffer};
// A constant sinusoid to send on the DAC output. // A constant sinusoid to send on the DAC output.
// Full-scale gives a +/- 10.24V amplitude waveform. Scale it down to give +/- 1V. // Full-scale gives a +/- 10.24V amplitude waveform. Scale it down to give +/- 1V.
@ -157,6 +157,8 @@ const APP: () = {
/// It outputs either I/Q or power/phase on DAC0/DAC1. Data is normalized to full scale. /// It outputs either I/Q or power/phase on DAC0/DAC1. Data is normalized to full scale.
/// PLL bandwidth, filter bandwidth, slope, and x/y or power/phase post-filters are available. /// PLL bandwidth, filter bandwidth, slope, and x/y or power/phase post-filters are available.
#[task(binds=DMA1_STR4, resources=[adcs, dacs, lockin, timestamper, pll, settings, telemetry], priority=2)] #[task(binds=DMA1_STR4, resources=[adcs, dacs, lockin, timestamper, pll, settings, telemetry], priority=2)]
#[inline(never)]
#[link_section = ".itcm.process"]
fn process(c: process::Context) { fn process(c: process::Context) {
let adc_samples = [ let adc_samples = [
c.resources.adcs.0.acquire_buffer(), c.resources.adcs.0.acquire_buffer(),
@ -248,8 +250,11 @@ const APP: () = {
fn idle(mut c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
loop { loop {
match c.resources.network.lock(|net| net.update()) { match c.resources.network.lock(|net| net.update()) {
UpdateState::Updated => c.spawn.settings_update().unwrap(), NetworkState::SettingsChanged => {
UpdateState::NoChange => cortex_m::asm::wfi(), c.spawn.settings_update().unwrap()
}
NetworkState::Updated => {}
NetworkState::NoChange => cortex_m::asm::wfi(),
} }
} }
} }

View File

@ -9,7 +9,7 @@ use stm32h7xx_hal::{
const NUM_SOCKETS: usize = 4; const NUM_SOCKETS: usize = 4;
use heapless::{consts, Vec}; use heapless::Vec;
use smoltcp_nal::smoltcp; use smoltcp_nal::smoltcp;
use embedded_hal::digital::v2::{InputPin, OutputPin}; use embedded_hal::digital::v2::{InputPin, OutputPin};
@ -105,6 +105,52 @@ pub struct PounderDevices {
/// Static storage for the ethernet DMA descriptor ring. /// Static storage for the ethernet DMA descriptor ring.
static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new(); static mut DES_RING: ethernet::DesRing = ethernet::DesRing::new();
/// Setup ITCM and load its code from flash.
///
/// For portability and maintainability this is implemented in Rust.
/// Since this is implemented in Rust the compiler may assume that bss and data are set
/// up already. There is no easy way to ensure this implementation will never need bss
/// or data. Hence we can't safely run this as the cortex-m-rt `pre_init` hook before
/// bss/data is setup.
///
/// Calling (through IRQ or directly) any code in ITCM before having called
/// this method is undefined.
fn load_itcm() {
extern "C" {
static mut __sitcm: u32;
static mut __eitcm: u32;
static mut __siitcm: u32;
}
use core::{ptr, slice, sync::atomic};
// NOTE(unsafe): Assuming the address symbols from the linker as well as
// the source instruction data are all valid, this is safe as it only
// copies linker-prepared data to where the code expects it to be.
// Calling it multiple times is safe as well.
unsafe {
// ITCM is enabled on reset on our CPU but might not be on others.
// Keep for completeness.
const ITCMCR: *mut u32 = 0xE000_EF90usize as _;
ptr::write_volatile(ITCMCR, ptr::read_volatile(ITCMCR) | 1);
// Ensure ITCM is enabled before loading.
atomic::fence(atomic::Ordering::SeqCst);
let len =
(&__eitcm as *const u32).offset_from(&__sitcm as *const _) as usize;
let dst = slice::from_raw_parts_mut(&mut __sitcm as *mut _, len);
let src = slice::from_raw_parts(&__siitcm as *const _, len);
// Load code into ITCM.
dst.copy_from_slice(src);
}
// Ensure ITCM is loaded before potentially executing any instructions from it.
atomic::fence(atomic::Ordering::SeqCst);
cortex_m::asm::dsb();
cortex_m::asm::isb();
}
/// Configure the stabilizer hardware for operation. /// Configure the stabilizer hardware for operation.
/// ///
/// # Args /// # Args
@ -157,9 +203,12 @@ pub fn setup(
log::set_logger(&LOGGER) log::set_logger(&LOGGER)
.map(|()| log::set_max_level(log::LevelFilter::Trace)) .map(|()| log::set_max_level(log::LevelFilter::Trace))
.unwrap(); .unwrap();
log::info!("starting..."); log::info!("Starting");
} }
// Before being able to call any code in ITCM, load that code from flash.
load_itcm();
// Set up the system timer for RTIC scheduling. // Set up the system timer for RTIC scheduling.
{ {
let tim15 = let tim15 =
@ -593,7 +642,7 @@ pub fn setup(
let mut sockets = let mut sockets =
smoltcp::socket::SocketSet::new(&mut store.sockets[..]); smoltcp::socket::SocketSet::new(&mut store.sockets[..]);
let mut handles: Vec<smoltcp::socket::SocketHandle, consts::U64> = let mut handles: Vec<smoltcp::socket::SocketHandle, 64> =
Vec::new(); Vec::new();
for storage in store.socket_storage.iter_mut() { for storage in store.socket_storage.iter_mut() {
let tcp_socket = { let tcp_socket = {
@ -872,6 +921,7 @@ pub fn setup(
#[cfg(feature = "pounder_v1_1")] #[cfg(feature = "pounder_v1_1")]
let pounder_stamper = { let pounder_stamper = {
log::info!("Assuming Pounder v1.1 or later");
let etr_pin = gpioa.pa0.into_alternate_af3(); let etr_pin = gpioa.pa0.into_alternate_af3();
// The frequency in the constructor is dont-care, as we will modify the period + clock // The frequency in the constructor is dont-care, as we will modify the period + clock
@ -934,13 +984,13 @@ pub fn setup(
digital_inputs, digital_inputs,
}; };
// Enable the instruction cache.
core.SCB.enable_icache();
// info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap()); // info!("Version {} {}", build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap());
// info!("Built on {}", build_info::BUILT_TIME_UTC); // info!("Built on {}", build_info::BUILT_TIME_UTC);
// info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET); // info!("{} {}", build_info::RUSTC_VERSION, build_info::TARGET);
log::info!("setup() complete"); log::info!("setup() complete");
// Enable the instruction cache.
core.SCB.enable_icache();
(stabilizer, pounder) (stabilizer, pounder)
} }

View File

@ -90,11 +90,11 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
} }
#[cortex_m_rt::exception] #[cortex_m_rt::exception]
fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { unsafe fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! {
panic!("HardFault at {:#?}", ef); panic!("HardFault at {:#?}", ef);
} }
#[cortex_m_rt::exception] #[cortex_m_rt::exception]
fn DefaultHandler(irqn: i16) { unsafe fn DefaultHandler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn); panic!("Unhandled exception (IRQn = {})", irqn);
} }

View File

@ -1,4 +1,4 @@
use heapless::{consts, String, Vec}; use heapless::{String, Vec};
use serde::Serialize; use serde::Serialize;
use core::fmt::Write; use core::fmt::Write;
@ -12,15 +12,15 @@ pub enum SettingsResponseCode {
/// Represents a generic MQTT message. /// Represents a generic MQTT message.
pub struct MqttMessage<'a> { pub struct MqttMessage<'a> {
pub topic: &'a str, pub topic: &'a str,
pub message: Vec<u8, consts::U128>, pub message: Vec<u8, 128>,
pub properties: Vec<minimq::Property<'a>, consts::U1>, pub properties: Vec<minimq::Property<'a>, 1>,
} }
/// The payload of the MQTT response message to a settings update request. /// The payload of the MQTT response message to a settings update request.
#[derive(Serialize)] #[derive(Serialize)]
pub struct SettingsResponse { pub struct SettingsResponse {
code: u8, code: u8,
msg: String<heapless::consts::U64>, msg: String<64>,
} }
impl<'a> MqttMessage<'a> { impl<'a> MqttMessage<'a> {
@ -48,8 +48,7 @@ impl<'a> MqttMessage<'a> {
.unwrap_or(&default_response); .unwrap_or(&default_response);
// Associate any provided correlation data with the response. // Associate any provided correlation data with the response.
let mut correlation_data: Vec<minimq::Property<'a>, consts::U1> = let mut correlation_data: Vec<minimq::Property<'a>, 1> = Vec::new();
Vec::new();
if let Some(data) = properties if let Some(data) = properties
.iter() .iter()
.find(|prop| matches!(prop, minimq::Property::CorrelationData(_))) .find(|prop| matches!(prop, minimq::Property::CorrelationData(_)))

View File

@ -10,7 +10,7 @@
///! ///!
///! Respones to settings updates are sent without quality-of-service guarantees, so there's no ///! Respones to settings updates are sent without quality-of-service guarantees, so there's no
///! guarantee that the requestee will be informed that settings have been applied. ///! guarantee that the requestee will be informed that settings have been applied.
use heapless::{consts, String}; use heapless::String;
use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState}; use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState};
use crate::hardware::design_parameters::MQTT_BROKER; use crate::hardware::design_parameters::MQTT_BROKER;
@ -20,11 +20,11 @@ pub struct MiniconfClient<S>
where where
S: miniconf::Miniconf + Default + Clone, S: miniconf::Miniconf + Default + Clone,
{ {
default_response_topic: String<consts::U128>, default_response_topic: String<128>,
mqtt: minimq::MqttClient<minimq::consts::U256, NetworkReference>, mqtt: minimq::Minimq<NetworkReference, 256>,
settings: S, settings: S,
subscribed: bool, subscribed: bool,
settings_prefix: String<consts::U64>, settings_prefix: String<64>,
} }
impl<S> MiniconfClient<S> impl<S> MiniconfClient<S>
@ -39,13 +39,12 @@ where
/// * `prefix` - The MQTT device prefix to use for this device. /// * `prefix` - The MQTT device prefix to use for this device.
pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self { pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self {
let mqtt = let mqtt =
minimq::MqttClient::new(MQTT_BROKER.into(), client_id, stack) minimq::Minimq::new(MQTT_BROKER.into(), client_id, stack).unwrap();
.unwrap();
let mut response_topic: String<consts::U128> = String::from(prefix); let mut response_topic: String<128> = String::from(prefix);
response_topic.push_str("/log").unwrap(); response_topic.push_str("/log").unwrap();
let mut settings_prefix: String<consts::U64> = String::from(prefix); let mut settings_prefix: String<64> = String::from(prefix);
settings_prefix.push_str("/settings").unwrap(); settings_prefix.push_str("/settings").unwrap();
Self { Self {
@ -62,7 +61,7 @@ where
/// # Returns /// # Returns
/// An option containing an action that should be completed as a result of network servicing. /// An option containing an action that should be completed as a result of network servicing.
pub fn update(&mut self) -> UpdateState { pub fn update(&mut self) -> UpdateState {
let mqtt_connected = match self.mqtt.is_connected() { let mqtt_connected = match self.mqtt.client.is_connected() {
Ok(connected) => connected, Ok(connected) => connected,
Err(minimq::Error::Network( Err(minimq::Error::Network(
smoltcp_nal::NetworkError::NoIpAddress, smoltcp_nal::NetworkError::NoIpAddress,
@ -82,13 +81,13 @@ where
if !self.subscribed && mqtt_connected { if !self.subscribed && mqtt_connected {
// Note(unwrap): We construct a string with two more characters than the prefix // Note(unwrap): We construct a string with two more characters than the prefix
// strucutre, so we are guaranteed to have space for storage. // strucutre, so we are guaranteed to have space for storage.
let mut settings_topic: String<consts::U66> = let mut settings_topic: String<66> =
String::from(self.settings_prefix.as_str()); String::from(self.settings_prefix.as_str());
settings_topic.push_str("/#").unwrap(); settings_topic.push_str("/#").unwrap();
// We do not currently handle or process potential subscription failures. Instead, this // We do not currently handle or process potential subscription failures. Instead, this
// failure will be logged through the stabilizer logging interface. // failure will be logged through the stabilizer logging interface.
self.mqtt.subscribe(&settings_topic, &[]).unwrap(); self.mqtt.client.subscribe(&settings_topic, &[]).unwrap();
self.subscribed = true; self.subscribed = true;
} }

View File

@ -5,7 +5,7 @@
///! telemetry (via MQTT), configuration of run-time settings (via MQTT + Miniconf), and live data ///! telemetry (via MQTT), configuration of run-time settings (via MQTT + Miniconf), and live data
///! streaming over raw UDP/TCP sockets. This module encompasses the main processing routines ///! streaming over raw UDP/TCP sockets. This module encompasses the main processing routines
///! related to Stabilizer networking operations. ///! related to Stabilizer networking operations.
use heapless::{consts, String}; use heapless::String;
use miniconf::Miniconf; use miniconf::Miniconf;
use serde::Serialize; use serde::Serialize;
@ -33,6 +33,12 @@ pub enum UpdateState {
Updated, Updated,
} }
#[derive(Copy, Clone, PartialEq)]
pub enum NetworkState {
SettingsChanged,
Updated,
NoChange,
}
/// A structure of Stabilizer's default network users. /// A structure of Stabilizer's default network users.
pub struct NetworkUsers<S: Default + Clone + Miniconf, T: Serialize> { pub struct NetworkUsers<S: Default + Clone + Miniconf, T: Serialize> {
pub miniconf: MiniconfClient<S>, pub miniconf: MiniconfClient<S>,
@ -98,15 +104,18 @@ where
/// ///
/// # Returns /// # Returns
/// An indication if any of the network users indicated a state change. /// An indication if any of the network users indicated a state change.
pub fn update(&mut self) -> UpdateState { pub fn update(&mut self) -> NetworkState {
// Poll for incoming data. // Poll for incoming data.
let poll_result = self.processor.update(); let poll_result = match self.processor.update() {
UpdateState::NoChange => NetworkState::NoChange,
UpdateState::Updated => NetworkState::Updated,
};
// Update the MQTT clients. // Update the MQTT clients.
self.telemetry.update(); self.telemetry.update();
match self.miniconf.update() { match self.miniconf.update() {
UpdateState::Updated => UpdateState::Updated, UpdateState::Updated => NetworkState::SettingsChanged,
UpdateState::NoChange => poll_result, UpdateState::NoChange => poll_result,
} }
} }
@ -125,7 +134,7 @@ fn get_client_id(
app: &str, app: &str,
client: &str, client: &str,
mac: smoltcp_nal::smoltcp::wire::EthernetAddress, mac: smoltcp_nal::smoltcp::wire::EthernetAddress,
) -> String<consts::U64> { ) -> String<64> {
let mut identifier = String::new(); let mut identifier = String::new();
write!(&mut identifier, "{}-{}-{}", app, mac, client).unwrap(); write!(&mut identifier, "{}-{}-{}", app, mac, client).unwrap();
identifier identifier
@ -142,10 +151,10 @@ fn get_client_id(
pub fn get_device_prefix( pub fn get_device_prefix(
app: &str, app: &str,
mac: smoltcp_nal::smoltcp::wire::EthernetAddress, mac: smoltcp_nal::smoltcp::wire::EthernetAddress,
) -> String<consts::U128> { ) -> String<128> {
// Note(unwrap): The mac address + binary name must be short enough to fit into this string. If // Note(unwrap): The mac address + binary name must be short enough to fit into this string. If
// they are defined too long, this will panic and the device will fail to boot. // they are defined too long, this will panic and the device will fail to boot.
let mut prefix: String<consts::U128> = String::new(); let mut prefix: String<128> = String::new();
write!(&mut prefix, "dt/sinara/{}/{}", app, mac).unwrap(); write!(&mut prefix, "dt/sinara/{}/{}", app, mac).unwrap();
prefix prefix

View File

@ -47,25 +47,25 @@ impl<'a, S> NetworkStackProxy<'a, S> {
// proxy structure. // proxy structure.
macro_rules! forward { macro_rules! forward {
($func:ident($($v:ident: $IT:ty),*) -> $T:ty) => { ($func:ident($($v:ident: $IT:ty),*) -> $T:ty) => {
fn $func(&self, $($v: $IT),*) -> $T { fn $func(&mut self, $($v: $IT),*) -> $T {
self.mutex.lock(|stack| stack.$func($($v),*)) self.mutex.lock(|stack| stack.$func($($v),*))
} }
} }
} }
// Implement a TCP stack for the proxy if the underlying network stack implements it. // Implement a TCP stack for the proxy if the underlying network stack implements it.
impl<'a, S> embedded_nal::TcpStack for NetworkStackProxy<'a, S> impl<'a, S> embedded_nal::TcpClientStack for NetworkStackProxy<'a, S>
where where
S: embedded_nal::TcpStack, S: embedded_nal::TcpClientStack,
{ {
type TcpSocket = S::TcpSocket; type TcpSocket = S::TcpSocket;
type Error = S::Error; type Error = S::Error;
forward! {open(mode: embedded_nal::Mode) -> Result<S::TcpSocket, S::Error>} forward! {socket() -> Result<S::TcpSocket, S::Error>}
forward! {connect(socket: S::TcpSocket, remote: embedded_nal::SocketAddr) -> Result<S::TcpSocket, S::Error>} forward! {connect(socket: &mut S::TcpSocket, remote: embedded_nal::SocketAddr) -> embedded_nal::nb::Result<(), S::Error>}
forward! {is_connected(socket: &S::TcpSocket) -> Result<bool, S::Error>} forward! {is_connected(socket: &S::TcpSocket) -> Result<bool, S::Error>}
forward! {write(socket: &mut S::TcpSocket, buffer: &[u8]) -> embedded_nal::nb::Result<usize, S::Error>} forward! {send(socket: &mut S::TcpSocket, buffer: &[u8]) -> embedded_nal::nb::Result<usize, S::Error>}
forward! {read(socket: &mut S::TcpSocket, buffer: &mut [u8]) -> embedded_nal::nb::Result<usize, S::Error>} forward! {receive(socket: &mut S::TcpSocket, buffer: &mut [u8]) -> embedded_nal::nb::Result<usize, S::Error>}
forward! {close(socket: S::TcpSocket) -> Result<(), S::Error>} forward! {close(socket: S::TcpSocket) -> Result<(), S::Error>}
} }

View File

@ -10,7 +10,7 @@
///! sampling frequency. Instead, the raw codes are stored and the telemetry is generated as ///! sampling frequency. Instead, the raw codes are stored and the telemetry is generated as
///! required immediately before transmission. This ensures that any slower computation required ///! required immediately before transmission. This ensures that any slower computation required
///! for unit conversion can be off-loaded to lower priority tasks. ///! for unit conversion can be off-loaded to lower priority tasks.
use heapless::{consts, String, Vec}; use heapless::{String, Vec};
use minimq::QoS; use minimq::QoS;
use serde::Serialize; use serde::Serialize;
@ -21,8 +21,8 @@ use crate::hardware::{
/// The telemetry client for reporting telemetry data over MQTT. /// The telemetry client for reporting telemetry data over MQTT.
pub struct TelemetryClient<T: Serialize> { pub struct TelemetryClient<T: Serialize> {
mqtt: minimq::MqttClient<minimq::consts::U256, NetworkReference>, mqtt: minimq::Minimq<NetworkReference, 256>,
telemetry_topic: String<consts::U128>, telemetry_topic: String<128>,
_telemetry: core::marker::PhantomData<T>, _telemetry: core::marker::PhantomData<T>,
} }
@ -97,10 +97,9 @@ impl<T: Serialize> TelemetryClient<T> {
/// A new telemetry client. /// A new telemetry client.
pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self { pub fn new(stack: NetworkReference, client_id: &str, prefix: &str) -> Self {
let mqtt = let mqtt =
minimq::MqttClient::new(MQTT_BROKER.into(), client_id, stack) minimq::Minimq::new(MQTT_BROKER.into(), client_id, stack).unwrap();
.unwrap();
let mut telemetry_topic: String<consts::U128> = String::from(prefix); let mut telemetry_topic: String<128> = String::from(prefix);
telemetry_topic.push_str("/telemetry").unwrap(); telemetry_topic.push_str("/telemetry").unwrap();
Self { Self {
@ -119,9 +118,10 @@ impl<T: Serialize> TelemetryClient<T> {
/// # Args /// # Args
/// * `telemetry` - The telemetry to report /// * `telemetry` - The telemetry to report
pub fn publish(&mut self, telemetry: &T) { pub fn publish(&mut self, telemetry: &T) {
let telemetry: Vec<u8, consts::U256> = let telemetry: Vec<u8, 256> =
serde_json_core::to_vec(telemetry).unwrap(); serde_json_core::to_vec(telemetry).unwrap();
self.mqtt self.mqtt
.client
.publish(&self.telemetry_topic, &telemetry, QoS::AtMostOnce, &[]) .publish(&self.telemetry_topic, &telemetry, QoS::AtMostOnce, &[])
.ok(); .ok();
} }