dfu: Add eth cmd, fns to put stm32 into dfu mode

This commit is contained in:
linuswck 2024-02-16 16:47:17 +08:00
parent 6dd1bf9301
commit 0642640da7
7 changed files with 148 additions and 36 deletions

View File

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

View File

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

46
src/device/dfu.rs Normal file
View File

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

View File

@ -6,3 +6,4 @@ pub mod sys_timer;
pub mod usb;
pub mod flash_store;
pub mod hw_rev;
pub mod dfu;

View File

@ -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,41 +78,58 @@ 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!("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!("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, 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;
}
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);
}
// 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();
}
}
}

View File

@ -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<LdCmdEnum>,
thermostat_cmd: Option<ThermostatCmdEnum>,
device_cmd: Option<DeviceCmd>,
data_f32: Option<f32>,
data_f64: Option<f64>,
}
@ -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)
}

View File

@ -207,6 +207,11 @@ impl ServerHandle {
}
return true;
}
pub fn close_socket(&mut self) {
let socket = self.socket_set.get_mut::<Socket>(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]