diff --git a/README.md b/README.md index e203116..0603568 100644 --- a/README.md +++ b/README.md @@ -41,17 +41,19 @@ gdb target/thumbv7em-none-eabihf/release/kirdy ``` ## Flashing -There are several options for flashing kirdy. DFU requires only a USB-C connector, whereas OpenOCD needs a JTAG/SWD adapter. +There are several options for flashing kirdy. DFU requires only a USB-C cable or RJ45 cable, whereas OpenOCD needs a JTAG/SWD adapter. ### dfu-util on Linux * Install the DFU USB tool (dfu-util). * Convert firmware from ELF to BIN: `arm-none-eabi-objcopy -O binary kirdy kirdy.bin` (you can skip this step if using the BIN from Hydra) -* Connect to the USB Type C connector to kirdy above the RJ45. -* Add jumper to kirdy v2.0 across 2-pin jumper adjacent to JTAG connector. -* Cycle board power to put it in DFU update mode +* Put STM32 into DFU Mode. You can either + * Connect to the USB Type C cable to kirdy next to the RJ45 Jack. After that, add BOOT0 jumper to kirdy near programming headers and then cycle board power to put it in DFU mode. OR + * Plug in RJ45 cable, which connect to a network that is accessible by your computer and send the corresponding dfu json command via TCP Socket to kirdy. Please see the python test script for the command. * Push firmware to flash: `dfu-util -a 0 -s 0x08000000:leave -D kirdy.bin` -* Remove jumper -* Cycle power to leave DFU update mode +* If you plugged in the BOOT0 jumper, you will need to + 1. Remove BOOT0 jumper + 2. Cycle power to leave DFU update mode +* If you plugged in the RJ45 cable, the MCU would start its application code automatically. No power cycle is needed. ### st.com DfuSe tool on Windows On a Windows machine install [st.com](https://st.com) DfuSe USB device firmware upgrade (DFU) software. [link](https://www.st.com/en/development-tools/stsw-stm32080.html). diff --git a/eth_cmd_test.py b/eth_cmd_test.py index 3b74d33..17c290c 100644 --- a/eth_cmd_test.py +++ b/eth_cmd_test.py @@ -14,6 +14,10 @@ import signal HOST = "192.168.1.132" PORT = 1337 +dfu_cmd = { + "device_cmd": "Dfu", +} + ld_cmd = { "laser_diode_cmd": "SetI", "data_f64": 0.0, diff --git a/src/device/dfu.rs b/src/device/dfu.rs new file mode 100644 index 0000000..47fa53f --- /dev/null +++ b/src/device/dfu.rs @@ -0,0 +1,46 @@ +use cortex_m_rt::pre_init; +use stm32f4xx_hal::pac::{RCC, SYSCFG}; +use core::arch::asm; + +const DFU_TRIG_MSG: u32 = 0xDECAFBAD; + +extern "C" { + // This symbol comes from memory.x + static mut _dfu_msg: u32; +} + +pub unsafe fn set_dfu_trigger() { + _dfu_msg = DFU_TRIG_MSG; +} + +/// Called by reset handler in lib.rs immediately after reset. +/// This function should not be called outside of reset handler as +/// bootloader expects MCU to be in reset state when called. +#[cfg(target_arch = "arm")] +#[pre_init] +unsafe fn __pre_init() { + if _dfu_msg == DFU_TRIG_MSG { + _dfu_msg = 0x00000000; + + // Enable system config controller clock + let rcc = &*RCC::ptr(); + rcc.apb2enr.modify(|_, w| w.syscfgen().set_bit()); + + // Bypass BOOT pins and remap bootloader to 0x00000000 + let syscfg = &*SYSCFG::ptr() ; + syscfg.memrm.write(|w| w.mem_mode().bits(0b01)); + + // Impose instruction and memory barriers + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + + asm!( + // Set stack pointer to bootloader location + "LDR R0, =0x1FFF0000", + "LDR SP,[R0, #0]", + // Jump to bootloader + "LDR R0,[R0, #4]", + "BX R0", + ); + } +} diff --git a/src/device/mod.rs b/src/device/mod.rs index 6255ad6..d7132d5 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -6,3 +6,4 @@ pub mod sys_timer; pub mod usb; pub mod flash_store; pub mod hw_rev; +pub mod dfu; diff --git a/src/main.rs b/src/main.rs index 1be7abc..0b84742 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use uom::si::power::milliwatt; use uom::si::f64::{ElectricPotential, ElectricCurrent, Power, ThermodynamicTemperature}; use serde::{Serialize, Deserialize}; +use stm32f4xx_hal::pac::SCB; // If RTT is used, print panic info through RTT #[cfg(all(feature = "RTT", not(test)))] use {core::panic::PanicInfo, rtt_target::rprintln}; @@ -77,42 +78,59 @@ fn main() -> ! { let milli_amp_fmt = ElectricCurrent::format_args(milliampere, Abbreviation); let milli_watt_fmt = Power::format_args(milliwatt, Abbreviation); + let mut should_reset = false; + loop { wd.feed(); - let mut eth_is_pending = false; + if !should_reset { + let mut eth_is_pending = false; - if thermostat.poll_adc_and_update_pid() { - info!("curr_dac_vfb: {:?}", volt_fmt.with(thermostat.get_dac_vfb())); - info!("curr_vref: {:?}", volt_fmt.with(thermostat.get_vref())); - info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i())); - info!("curr_tec_v: {:?}", volt_fmt.with(thermostat.get_tec_v())); + if thermostat.poll_adc_and_update_pid() { + info!("curr_dac_vfb: {:?}", volt_fmt.with(thermostat.get_dac_vfb())); + info!("curr_vref: {:?}", volt_fmt.with(thermostat.get_vref())); + info!("curr_tec_i: {:?}", amp_fmt.with(thermostat.get_tec_i())); + info!("curr_tec_v: {:?}", volt_fmt.with(thermostat.get_tec_v())); - info!("curr_ld_drive_cuurent: {:?}", milli_amp_fmt.with(laser.get_ld_drive_current())); + info!("curr_ld_drive_cuurent: {:?}", milli_amp_fmt.with(laser.get_ld_drive_current())); + + info!("pd_mon_v: {:?}", volt_fmt.with(laser.pd_mon_status().v)); + info!("power_excursion: {:?}", laser.pd_mon_status().pwr_excursion); + + info!("Termination Status: {:?}", laser.get_term_status()); + } - info!("pd_mon_v: {:?}", volt_fmt.with(laser.pd_mon_status().v)); - info!("power_excursion: {:?}", laser.pd_mon_status().pwr_excursion); - - info!("Termination Status: {:?}", laser.get_term_status()); - } - - if net::net::eth_is_socket_active() { - cortex_m::interrupt::free(|cs| - { - eth_is_pending = net::net::is_pending(cs); - } - ); + if net::net::eth_is_socket_active() { + cortex_m::interrupt::free(|cs| + { + eth_is_pending = net::net::is_pending(cs); + } + ); - if eth_is_pending { - unsafe{ - cortex_m::interrupt::free(|cs| { - net::net::clear_pending(cs); - }); - let bytes = net::net::eth_recv(&mut ETH_DATA_BUFFER); - debug!("Number of bytes recv: {:?}", bytes); - (laser, thermostat) = net::cmd_handler::execute_cmd(&mut ETH_DATA_BUFFER, bytes, laser, thermostat); + if eth_is_pending { + unsafe{ + cortex_m::interrupt::free(|cs| { + net::net::clear_pending(cs); + }); + let bytes = net::net::eth_recv(&mut ETH_DATA_BUFFER); + debug!("Number of bytes recv: {:?}", bytes); + (laser, thermostat, should_reset) = net::cmd_handler::execute_cmd(&mut ETH_DATA_BUFFER, bytes, laser, thermostat); + } } } + } else { + // Should reset, close all TCP sockets. + let mut any_socket_alive = false; + if net::net::eth_is_socket_active() { + net::net::eth_close_socket(); + any_socket_alive = true; + } + + // Must let loop run for one more cycle to poll server for RST to be sent, + // this makes sure system does not reset right after socket.abort() is called. + if !any_socket_alive { + SCB::sys_reset(); + } } } } diff --git a/src/net/cmd_handler.rs b/src/net/cmd_handler.rs index 958698b..54f20f6 100644 --- a/src/net/cmd_handler.rs +++ b/src/net/cmd_handler.rs @@ -1,4 +1,4 @@ -use core::fmt::Debug; +use core::{default, fmt::Debug}; use miniconf::{JsonCoreSlash, Tree}; use serde::{Deserialize, Serialize}; use uom::si::{ @@ -10,8 +10,16 @@ use uom::si::{ use crate::{laser_diode::laser_diode::LdDrive, net::net, thermostat::thermostat::StatusReport}; use crate::thermostat::thermostat::Thermostat; use crate::thermostat::pid_state::PidSettings::*; +use crate::device::dfu; use log::info; +#[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)] +enum DeviceCmd { + #[default] + Reserved, + Dfu +} + #[derive(Deserialize, Serialize, Copy, Clone, Default, Debug)] enum LdCmdEnum { #[default] @@ -70,6 +78,7 @@ enum ThermostatCmdEnum { pub struct CmdJsonObj{ laser_diode_cmd: Option, thermostat_cmd: Option, + device_cmd: Option, data_f32: Option, data_f64: Option, } @@ -83,7 +92,9 @@ pub struct StatusReportStruct { json: StatusReport } -pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mut tec: Thermostat)->(LdDrive, Thermostat){ +pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mut tec: Thermostat)->(LdDrive, Thermostat, bool){ + let mut should_reset = false; + let mut cmd = Cmd { json: CmdJsonObj::default() }; @@ -91,6 +102,20 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu Ok(_) => { info!("############ Laser Diode Command Received {:?}", cmd.json.laser_diode_cmd); info!("############ Thermostat Command Received {:?}", cmd.json.thermostat_cmd); + info!("############ Device Command Received {:?}", cmd.json.device_cmd); + + match cmd.json.device_cmd { + Some(DeviceCmd::Dfu) => { + unsafe { + dfu::set_dfu_trigger(); + } + should_reset = true; + } + None => { /* Do Nothing */} + _ => { + info!("Unimplemented Command") + } + } match cmd.json.laser_diode_cmd { Some(LdCmdEnum::PowerUp) => { @@ -331,5 +356,5 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, mut laser: LdDrive, mu info!("Invalid Command: {:?}", err); } } - (laser, tec) + (laser, tec, should_reset) } diff --git a/src/net/net.rs b/src/net/net.rs index ed34cda..2fdc2cf 100644 --- a/src/net/net.rs +++ b/src/net/net.rs @@ -207,6 +207,11 @@ impl ServerHandle { } return true; } + + pub fn close_socket(&mut self) { + let socket = self.socket_set.get_mut::(self.socket_handle); + socket.abort(); + } } use ieee802_3_miim::{ @@ -349,6 +354,17 @@ pub fn eth_is_socket_active() -> bool { } } +pub fn eth_close_socket() { + unsafe { + if let Some(ref mut server_handle ) = SERVER_HANDLE { + server_handle.close_socket() + } + else { + panic!("eth_close_socket is called before init"); + } + } +} + /// Potentially wake up from `wfi()`, set the interrupt pending flag, /// clear interrupt flags. #[interrupt]