Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0acf809
Initial mgmt firmware
bifurcation Oct 8, 2025
3bd02cd
Phase 1: Add module structure and GPIO setup
bifurcation Oct 8, 2025
1eebf17
Phase 2: Design UART stream architecture
bifurcation Oct 8, 2025
5a797db
Phase 3: Wire up UART routing in main
bifurcation Oct 8, 2025
fb38a75
Phase 4: Implement command protocol
bifurcation Oct 8, 2025
3e10fc0
Phase 5: Implement main application and state machine
bifurcation Oct 8, 2025
e17a5f5
Phase 6: Final testing and refinement
bifurcation Oct 8, 2025
051f8ba
Add USART3 (NET) UART configuration
bifurcation Oct 8, 2025
55f07eb
Refactor to single-task architecture with fewer statics
bifurcation Oct 8, 2025
4d6e348
Complete flash mode and data forwarding implementation
bifurcation Oct 8, 2025
116887c
Address code review and clean up unused code
bifurcation Oct 8, 2025
a463fcd
Refactor chip control: use blocking delays and consolidate modules
bifurcation Oct 8, 2025
a060bd7
Remove unused TxPath::UiNet and simplify UartRouting initialization
bifurcation Oct 8, 2025
107b8d4
Refactor commands.rs to use modern Rust patterns
bifurcation Oct 8, 2025
f58876f
Remove unused State enum - always use Debug mode
bifurcation Oct 8, 2025
6ba549e
Add lifetime to CommandContext and remove Option wrappers
bifurcation Oct 8, 2025
1001182
Replace interior mutability with mutable references
bifurcation Oct 8, 2025
509ec93
Use set_config instead of reinstantiating
bifurcation Oct 8, 2025
4ce8e37
Make things more compact
bifurcation Oct 8, 2025
38c37d2
RLB refactor
bifurcation Oct 8, 2025
e6a86f4
Cleanup
bifurcation Oct 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions mgmt-embassy/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-rs run --chip STM32F072CBTx"

[build]
target = "thumbv6m-none-eabi"

[env]
DEFMT_LOG = "trace"
26 changes: 26 additions & 0 deletions mgmt-embassy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
edition = "2021"
name = "mgmt-embassy"
version = "0.1.0"
license = "MIT OR Apache-2.0"

[dependencies]
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"

defmt = "1.0.1"
defmt-rtt = "1.0.0"

embassy-executor = { version = "0.8.0", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
embassy-stm32 = { version = "0.3.0", features = ["defmt", "stm32f072cb", "memory-x", "exti", "chrono", "time-driver-any"] }
embassy-sync = { version = "0.7.0", features = ["defmt"] }
embassy-time = { version = "0.4.0", features = ["defmt"] }
heapless = "0.8"
embedded-hal = "1.0"

panic-probe = { version = "1.0.0", features = ["print-defmt"] }
num_enum = { version = "0.7.4", default-features = false }
embedded-io-async = "0.6.1"

[profile.release]
debug = 2
5 changes: 5 additions & 0 deletions mgmt-embassy/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}
30 changes: 30 additions & 0 deletions mgmt-embassy/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use num_enum::TryFromPrimitive;

#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format, TryFromPrimitive)]
pub enum Command {
Version = 0,
WhoAreYou = 1,
HardReset = 2,
Reset = 3,
ResetUi = 4,
ResetNet = 5,
FlashUi = 6,
FlashNet = 7,
EnableLogs = 8,
EnableLogsUi = 9,
EnableLogsNet = 10,
DisableLogs = 11,
DisableLogsUi = 12,
DisableLogsNet = 13,
DefaultLogging = 14,
ToUi = 15,
ToNet = 16,
ToUsb = 17,
}

pub const VERSION: &[u8] = b"v1.0.0\n";
pub const HELLO_I_AM_A_HACTAR_DEVICE: &[u8] = b"HELLO, I AM A HACTAR DEVICE";
pub const OK_ASCII: &[u8] = b"Ok\n";
pub const OK_BYTE: u8 = 0x80;
pub const READY_BYTE: u8 = 0x81;
194 changes: 194 additions & 0 deletions mgmt-embassy/src/drivers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use embassy_stm32::{
gpio::{Flex, Level, Output, Pull, Speed},
Peri,
};
use embassy_time::Delay;
use embedded_hal::delay::DelayNs;

/// RGB LED controller
pub struct RgbLed {
r: Output<'static>,
g: Output<'static>,
b: Output<'static>,
}

impl RgbLed {
pub fn new(
r: Peri<'static, impl embassy_stm32::gpio::Pin>,
g: Peri<'static, impl embassy_stm32::gpio::Pin>,
b: Peri<'static, impl embassy_stm32::gpio::Pin>,
) -> Self {
Self {
r: Output::new(r, Level::High, Speed::Low),
g: Output::new(g, Level::High, Speed::Low),
b: Output::new(b, Level::High, Speed::Low),
}
}

pub fn set_rgb(&mut self, r: bool, g: bool, b: bool) {
// LEDs are active low
self.r.set_level(if r { Level::Low } else { Level::High });
self.g.set_level(if g { Level::Low } else { Level::High });
self.b.set_level(if b { Level::Low } else { Level::High });
}

#[allow(dead_code)]
pub fn off(&mut self) {
self.set_rgb(false, false, false);
}

#[allow(dead_code)]
pub fn set_red(&mut self) {
self.set_rgb(true, false, false);
}

#[allow(dead_code)]
pub fn set_green(&mut self) {
self.set_rgb(false, true, false);
}

#[allow(dead_code)]
pub fn set_blue(&mut self) {
self.set_rgb(false, false, true);
}

#[allow(dead_code)]
pub fn toggle_red(&mut self) {
self.r.toggle();
}

#[allow(dead_code)]
pub fn toggle_green(&mut self) {
self.g.toggle();
}

#[allow(dead_code)]
pub fn toggle_blue(&mut self) {
self.b.toggle();
}
}

/// Control pins for UI chip
pub struct UiControl {
pub nrst: Flex<'static>,
pub boot0: Output<'static>,
pub boot1: Output<'static>,
}

impl UiControl {
pub fn new(
nrst: Peri<'static, impl embassy_stm32::gpio::Pin>,
boot0: Peri<'static, impl embassy_stm32::gpio::Pin>,
boot1: Peri<'static, impl embassy_stm32::gpio::Pin>,
) -> Self {
let mut nrst_flex = Flex::new(nrst);
nrst_flex.set_as_output(Speed::Low);
nrst_flex.set_high();

Self {
nrst: nrst_flex,
boot0: Output::new(boot0, Level::Low, Speed::Low),
boot1: Output::new(boot1, Level::High, Speed::Low),
}
}

/// Put UI chip into bootloader mode (boot0=1, boot1=0)
pub fn bootloader_mode(&mut self) {
self.boot0.set_high();
self.boot1.set_low();

// Power cycle
self.power_cycle();
}

/// Put UI chip into normal mode (boot0=0, boot1=1)
pub fn normal_mode(&mut self) {
self.boot0.set_low();
self.boot1.set_high();

// Power cycle
self.power_cycle();
}

/// Hold UI chip in reset
pub fn hold_in_reset(&mut self) {
self.boot0.set_low();
self.boot1.set_high();

self.nrst.set_as_output(Speed::Low);
self.nrst.set_low();
}

/// Power cycle the UI chip
pub fn power_cycle(&mut self) {
let mut delay = Delay;
// Set nrst as output
self.nrst.set_as_output(Speed::Low);
self.nrst.set_low();
delay.delay_ms(10);

self.nrst.set_high();
delay.delay_ms(10);

self.nrst.set_low();
delay.delay_ms(10);

// Switch to input mode with pull-up (matching C code behavior)
self.nrst.set_as_input(Pull::Up);
}
}

/// Control pins for NET chip
pub struct NetControl {
pub nrst: Output<'static>,
pub boot: Output<'static>,
}

impl NetControl {
pub fn new(
nrst: Peri<'static, impl embassy_stm32::gpio::Pin>,
boot: Peri<'static, impl embassy_stm32::gpio::Pin>,
) -> Self {
Self {
nrst: Output::new(nrst, Level::High, Speed::Low),
boot: Output::new(boot, Level::High, Speed::Low),
}
}

/// Power cycle the NET chip reset pin
fn power_cycle(&mut self, delay_ms: u64) {
let mut delay = Delay;
self.nrst.set_low();
delay.delay_ms(delay_ms as u32);
self.nrst.set_high();
}

/// Put NET chip into bootloader mode
pub fn bootloader_mode(&mut self) {
self.power_cycle(10);

// Bring boot low for ESP bootloader mode
self.boot.set_low();

// Power cycle
self.power_cycle(10);
}

/// Put NET chip into normal mode
pub fn normal_mode(&mut self) {
self.boot.set_high();

// Power cycle
self.power_cycle(10);
}

/// Hold NET chip in reset
pub fn hold_in_reset(&mut self) {
let mut delay = Delay;
self.boot.set_high();

// Reset and hold
self.nrst.set_low();
delay.delay_ms(100);
}
}
51 changes: 51 additions & 0 deletions mgmt-embassy/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#![no_std]
#![no_main]

mod commands;
mod drivers;
mod state;

use defmt::*;
use embassy_executor::Spawner;
use {defmt_rtt as _, panic_probe as _};

use state::{Interface, State};

pub const READ_BUFFER_SIZE: usize = 64;
pub const DMA_BUFFER_SIZE: usize = 1024;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut usb_rx_buf = [0u8; DMA_BUFFER_SIZE];
let mut ui_rx_buf = [0u8; DMA_BUFFER_SIZE];
let mut net_rx_buf = [0u8; DMA_BUFFER_SIZE];

let mut s = State::new(&mut usb_rx_buf, &mut ui_rx_buf, &mut net_rx_buf);

info!("Starting main loop");

let mut buf = [0u8; 64];
let mut led_timer = embassy_time::Instant::now();

// Main loop - handle UART routing and command parsing
loop {
// Blink LED A every second
if led_timer.elapsed().as_millis() >= 1000 {
s.led_a.toggle_green();
led_timer = embassy_time::Instant::now();
}

// USB interface is special because it might be routed or read for commands
if s.routing.usb == Interface::Command {
s.handle_command(&mut buf).await;
} else {
s.route_data(Interface::Usb, &mut buf).await;
}

// Read and route data from the other interfaces
s.route_data(Interface::Ui, &mut buf).await;
s.route_data(Interface::Net, &mut buf).await;

embassy_time::Timer::after(embassy_time::Duration::from_micros(100)).await;
}
}
Loading