Compare commits

..

11 Commits

Author SHA1 Message Date
linuswck 7f06fc06fd async driver: fix incorrect timeout handling 2024-03-20 15:35:12 +08:00
linuswck 67f9e65df8 Start tec_readings_conversion after report is sent
- Before this fix, tec_readings only converts when eth_is_pending
2024-03-20 15:35:12 +08:00
linuswck 7d2e14ef2f eth: Poll Iface In ETH IRQ
- Improve responsiveness of TCP packets handling
- Fix a bug when one client is connected and active report is ON, the other client's cmds do not receive Ack from Kirdy
2024-03-20 15:32:28 +08:00
linuswck e525a3f354 fix typo 2024-03-20 12:06:05 +08:00
linuswck 8431e9f43d Update README 2024-03-20 11:55:21 +08:00
linuswck e9d5f4598a Fix wrong USB Product Description 2024-03-20 11:45:09 +08:00
linuswck 1867935047 Send report to connected socket
- Remove active report in flash settings
2024-03-19 17:38:29 +08:00
linuswck 99cf17f7e4 temp_mon: Add support for constant current mode 2024-03-19 14:52:26 +08:00
linuswck 048245f674 set_tec_max_i_pos/neg->...cooling_i/heating_i 2024-03-19 14:52:26 +08:00
linuswck 0a01d299bc Fix wrong reporting of Thermostat Pwr_on status 2024-03-19 14:52:26 +08:00
linuswck dbbd438e92 ld: Fix unable to save & load ld_i flash settings 2024-03-19 14:48:25 +08:00
13 changed files with 154 additions and 94 deletions

View File

@ -41,7 +41,7 @@ gdb target/thumbv7em-none-eabihf/release/kirdy
``` ```
## Flashing ## Flashing
There are several options for flashing kirdy. DFU requires only a USB-C cable or RJ45 cable, whereas OpenOCD needs a JTAG/SWD adapter. If the firmware to be flashed involves an update on the flash settings, it is required to erase the flash settings before flashing the new firmware to avoid unexpected hardware behavior. 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 ### dfu-util on Linux
* Install the DFU USB tool (dfu-util). * Install the DFU USB tool (dfu-util).
@ -64,7 +64,12 @@ On a Windows machine install [st.com](https://st.com) DfuSe USB device firmware
- remove jumper - remove jumper
- cycle power to leave DFU update mode - cycle power to leave DFU update mode
### Erasing Flash Settings ### OpenOCD
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/debug/kirdy verify reset; exit"
```
## Erasing Flash Settings
The flash settings are stored in the last flash sector(ID: 11) of bank 0 of stm32f405. You can erase it with JTAG/SWD adapter or by putting the device in Dfu mode. You may find it useful if you have set network settings incorrectly. The flash settings are stored in the last flash sector(ID: 11) of bank 0 of stm32f405. You can erase it with JTAG/SWD adapter or by putting the device in Dfu mode. You may find it useful if you have set network settings incorrectly.
With JTAG/SWD adapter connected, issue the following command: With JTAG/SWD adapter connected, issue the following command:
@ -78,8 +83,3 @@ With STM32 in DFU Mode, connect the USB Type C cable and then issue the followin
``` ```
dfu-util -a 0 -s 0x080E0000:leave -D erase_flash_settings.bin dfu-util -a 0 -s 0x080E0000:leave -D erase_flash_settings.bin
``` ```
### OpenOCD
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program target/thumbv7em-none-eabihf/debug/kirdy verify reset; exit"
```

View File

@ -358,16 +358,16 @@ class Thermostat:
""" """
return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxV", max_v) return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxV", max_v)
async def set_tec_max_i_pos(self, max_i_pos): async def set_tec_max_cooling_i(self, max_i_pos):
""" """
Set Tec maximum positive output Set Tec maximum cooling current (Settable Range: 0.0 - 1.0)
- max_i_pos: A - max_i_pos: A
""" """
return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxIPos", max_i_pos) return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxIPos", max_i_pos)
async def set_tec_max_i_neg(self, max_i_neg): async def set_tec_max_heating_i(self, max_i_neg):
""" """
Set Tec maximum negative output Set Tec maximum heating current (Settable Range: 0.0 - 1.0)
- max_i_neg: A - max_i_neg: A
""" """
return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxINeg", max_i_neg) return await self._send_cmd(TARGET_THERMOSTAT, "SetTecMaxINeg", max_i_neg)
@ -609,7 +609,7 @@ class Kirdy:
line = await asyncio.shield(self._read_write(cmd)) line = await asyncio.shield(self._read_write(cmd))
async def end_session(self): async def end_session(self):
"""End session to Thermostat if connected, cancel connection if connecting""" """End session to Kirdy if connected, cancel connection if connecting"""
if self._connecting_task is not None: if self._connecting_task is not None:
self._connecting_task.cancel() self._connecting_task.cancel()
@ -625,7 +625,7 @@ class Kirdy:
async def _read_response(self): async def _read_response(self):
try: try:
response = await asyncio.wait_for(self._reader.read(1024), self.timeout) response = await asyncio.wait_for(self._reader.read(1024), self.timeout)
except TimeoutError: except asyncio.exceptions.TimeoutError:
return { return {
"msg_type": "Internal Timeout" "msg_type": "Internal Timeout"
} }
@ -645,12 +645,10 @@ class Kirdy:
self._writer.write(bytes(json.dumps(cmd), "UTF-8")) self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
await self._writer.drain() await self._writer.drain()
response = await self._read_response() response = await self._read_response()
try: if response["msg_type"] == "Acknowledge":
if response["msg_type"] == "Acknowledge": return response
return response retry += 1
except: await asyncio.sleep(0.1)
retry += 1
await asyncio.sleep(0.25)
raise NoAckRecv raise NoAckRecv
async def _send_cmd_handler(self, target, cmd, data=None): async def _send_cmd_handler(self, target, cmd, data=None):
@ -681,12 +679,10 @@ class Kirdy:
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8")) self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
await self._writer.drain() await self._writer.drain()
response = await self._read_response() response = await self._read_response()
try: if response["msg_type"] == "Acknowledge":
if response["msg_type"] == "Acknowledge": return response
return response retry += 1
except: await asyncio.sleep(0.1)
retry += 1
await asyncio.sleep(0.1)
raise NoAckRecv raise NoAckRecv
async def report_mode(self): async def report_mode(self):

View File

@ -278,8 +278,8 @@ async def main():
await kirdy.thermostat.set_temp_mon_upper_limit(target_temperature + 20) await kirdy.thermostat.set_temp_mon_upper_limit(target_temperature + 20)
await kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20) await kirdy.thermostat.set_temp_mon_lower_limit(target_temperature - 20)
await kirdy.thermostat.set_tec_max_i_pos(output_step) await kirdy.thermostat.set_tec_max_cooling_i(output_step)
await kirdy.thermostat.set_tec_max_i_neg(output_step) await kirdy.thermostat.set_tec_max_heating_i(output_step)
# The Polling Rate of Temperature Adc is equal to the PID Update Interval # The Polling Rate of Temperature Adc is equal to the PID Update Interval
await kirdy.thermostat.config_temp_adc_filter("Sinc5Sinc1With50hz60HzRejection", "F16SPS") await kirdy.thermostat.config_temp_adc_filter("Sinc5Sinc1With50hz60HzRejection", "F16SPS")

View File

@ -33,7 +33,7 @@ impl State {
let serial = SerialPort::new(bus); let serial = SerialPort::new(bus);
let dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd)) let dev = UsbDeviceBuilder::new(bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("M-Labs") .manufacturer("M-Labs")
.product("thermostat") .product("Kirdy")
.device_release(0x20) .device_release(0x20)
.self_powered(true) .self_powered(true)
.device_class(usbd_serial::USB_CLASS_CDC) .device_class(usbd_serial::USB_CLASS_CDC)

View File

@ -118,7 +118,7 @@ impl LdDrive{
} }
pub fn power_up(&mut self){ pub fn power_up(&mut self){
let prev_i_set = LdCurrentOutCtrlTimer::get_target_i(); let prev_i_set = self.settings.ld_drive_current;
LdCurrentOutCtrlTimer::reset(); LdCurrentOutCtrlTimer::reset();
let _ = self.ctrl.set_i(ElectricCurrent::new::<milliampere>(0.0), Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX); let _ = self.ctrl.set_i(ElectricCurrent::new::<milliampere>(0.0), Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
LdPwrExcProtector::pwr_on_and_arm_protection(); LdPwrExcProtector::pwr_on_and_arm_protection();
@ -142,14 +142,14 @@ impl LdDrive{
} }
pub fn ld_set_i(&mut self, i: ElectricCurrent){ pub fn ld_set_i(&mut self, i: ElectricCurrent){
LdCurrentOutCtrlTimer::set_target_i_and_listen_irq(i, self.settings.ld_drive_current); self.settings.ld_drive_current = i;
LdCurrentOutCtrlTimer::set_target_i_and_listen_irq(i, self.ctrl.get_i_set());
} }
pub fn poll_and_update_output_current(&mut self) -> ElectricCurrent { pub fn poll_and_update_output_current(&mut self) -> ElectricCurrent {
match LdCurrentOutCtrlTimer::get_irq_status() { match LdCurrentOutCtrlTimer::get_irq_status() {
Some(i_set) => { Some(i_set) => {
let i_set = self.ctrl.set_i(i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX); let i_set = self.ctrl.set_i(i_set, Settings::LD_DRIVE_TRANSIMPEDANCE, Settings::DAC_OUT_V_MAX);
self.settings.ld_drive_current = i_set;
LdCurrentOutCtrlTimer::clear_alarm_and_resume_listening(); LdCurrentOutCtrlTimer::clear_alarm_and_resume_listening();
i_set i_set
} }
@ -195,10 +195,15 @@ impl LdDrive{
} }
pub fn get_status_report(&mut self) -> StatusReport { pub fn get_status_report(&mut self) -> StatusReport {
let ld_i_set = if LdPwrExcProtector::get_status().pwr_engaged {
self.ctrl.get_i_set()
} else {
ElectricCurrent::new::<ampere>(0.0)
};
StatusReport { StatusReport {
pwr_on: LdPwrExcProtector::get_status().pwr_engaged, pwr_on: LdPwrExcProtector::get_status().pwr_engaged,
pwr_excursion: LdPwrExcProtector::get_status().pwr_excursion, pwr_excursion: LdPwrExcProtector::get_status().pwr_excursion,
ld_i_set: self.settings.ld_drive_current, ld_i_set: ld_i_set,
pd_i: self.get_pd_i(), pd_i: self.get_pd_i(),
pd_pwr: self.get_pd_pwr(), pd_pwr: self.get_pd_pwr(),
term_status: self.get_term_status(), term_status: self.get_term_status(),

View File

@ -8,7 +8,8 @@ use stm32f4xx_hal::{
use uom::si::{ use uom::si::{
ratio::ratio, ratio::ratio,
f32::{ElectricPotential, ElectricCurrent}, f32::{ElectricPotential, ElectricCurrent},
electric_current::ampere,
}; };
use crate::laser_diode::max5719::{self, Dac}; use crate::laser_diode::max5719::{self, Dac};
@ -49,12 +50,14 @@ type DacLoad = PB14<Output<PushPull>>;
pub struct LdCtrl{ pub struct LdCtrl{
pub phy: LdCtrlPhy<Channel0>, pub phy: LdCtrlPhy<Channel0>,
i_set: ElectricCurrent,
} }
impl LdCtrl { impl LdCtrl {
pub fn new(phy_ch0: LdCtrlPhy<Channel0>) -> Self { pub fn new(phy_ch0: LdCtrlPhy<Channel0>) -> Self {
LdCtrl { LdCtrl {
phy: phy_ch0, phy: phy_ch0,
i_set: ElectricCurrent::new::<ampere>(0.0)
} }
} }
@ -85,6 +88,11 @@ impl LdCtrl {
} }
pub fn set_i(&mut self, current: ElectricCurrent, transimpedance: TransimpedanceUnit, dac_out_v_max: ElectricPotential) -> ElectricCurrent { pub fn set_i(&mut self, current: ElectricCurrent, transimpedance: TransimpedanceUnit, dac_out_v_max: ElectricPotential) -> ElectricCurrent {
self.set_dac(current * transimpedance, dac_out_v_max) / transimpedance self.i_set = self.set_dac(current * transimpedance, dac_out_v_max) / transimpedance;
self.i_set
}
pub fn get_i_set(&mut self) -> ElectricCurrent{
self.i_set
} }
} }

View File

@ -63,13 +63,6 @@ impl LdCurrentOutCtrlTimer {
} }
} }
pub fn get_target_i() -> ElectricCurrent {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() {
return ld_current_out_ctrl_timer.target_i
}
ElectricCurrent::new::<ampere>(0.0)
}
pub fn set_alarm() { pub fn set_alarm() {
if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() { if let Some(ref mut ld_current_out_ctrl_timer ) = LdCurrentOutCtrlTimer::get() {
ld_current_out_ctrl_timer.timeout = true; ld_current_out_ctrl_timer.timeout = true;

View File

@ -31,7 +31,6 @@ static mut ETH_DATA_BUFFER: [u8; 1024] = [0; 1024];
#[derive(Deserialize, Serialize, Clone, Copy, Debug)] #[derive(Deserialize, Serialize, Clone, Copy, Debug)]
pub struct DeviceSettings{ pub struct DeviceSettings{
report_readings: bool,
ip_settings: IpSettings, ip_settings: IpSettings,
} }
@ -59,10 +58,11 @@ fn main() -> ! {
let (mut wd, mut flash_store, mut laser, mut thermostat,) = bootup(core_perif, perif); let (mut wd, mut flash_store, mut laser, mut thermostat,) = bootup(core_perif, perif);
let mut device_settings = DeviceSettings { let mut device_settings = DeviceSettings {
report_readings: false,
ip_settings: IpSettings::default() ip_settings: IpSettings::default()
}; };
let mut active_report: [bool; net::net::NUM_OF_SOCKETS] = [false; net::net::NUM_OF_SOCKETS];
let mut state = State::default(); let mut state = State::default();
loop { loop {
@ -133,12 +133,10 @@ fn main() -> ! {
} }
State::MainLoop => { State::MainLoop => {
let mut eth_is_pending = false; let mut eth_is_pending = false;
let mut has_temp_reading = false;
laser.poll_and_update_output_current(); laser.poll_and_update_output_current();
if thermostat.poll_adc() { if thermostat.poll_adc() {
has_temp_reading = true;
thermostat.update_pid(); thermostat.update_pid();
if thermostat.get_temp_mon_status().over_temp_alarm { if thermostat.get_temp_mon_status().over_temp_alarm {
laser.power_down(); laser.power_down();
@ -146,43 +144,42 @@ fn main() -> ! {
thermostat.power_down(); thermostat.power_down();
} }
net::net::for_each(|mut socket| { net::net::for_each(|mut socket, id| {
if net::net::eth_is_socket_active(socket) { if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket) {
if device_settings.report_readings { if active_report[id] {
unsafe { unsafe {
net::cmd_handler::send_status_report(&mut ETH_DATA_BUFFER, &mut laser, &mut thermostat, &mut socket); net::cmd_handler::send_status_report(&mut ETH_DATA_BUFFER, &mut laser, &mut thermostat, &mut socket);
} }
} }
} }
else {
active_report[id] = false;
}
}); });
thermostat.start_tec_readings_conversion();
} }
net::net::for_each(|mut socket| { cortex_m::interrupt::free(|cs|
if net::net::eth_is_socket_active(socket) { {
cortex_m::interrupt::free(|cs| eth_is_pending = net::net::is_pending(cs);
{ net::net::clear_pending(cs);
eth_is_pending = net::net::is_pending(cs); }
} );
); if eth_is_pending {
net::net::for_each(|mut socket, id| {
if eth_is_pending { if net::net::eth_is_socket_active(socket) && net::net::eth_is_socket_connected(socket){
unsafe{ unsafe{
cortex_m::interrupt::free(|cs| {
net::net::clear_pending(cs);
});
let bytes = net::net::eth_recv(&mut ETH_DATA_BUFFER, socket); let bytes = net::net::eth_recv(&mut ETH_DATA_BUFFER, socket);
if bytes != 0 { if bytes != 0 {
info!("Ts: {:?}", sys_timer::now()); info!("Ts: {:?}", sys_timer::now());
debug!("Number of bytes recv: {:?}", bytes); debug!("Number of bytes recv: {:?}", bytes);
// State Transition // State Transition
net::cmd_handler::execute_cmd(&mut ETH_DATA_BUFFER, bytes, &mut socket, &mut laser, &mut thermostat, &mut state, &mut device_settings); net::cmd_handler::execute_cmd(&mut ETH_DATA_BUFFER, bytes, &mut socket, &mut laser, &mut thermostat, &mut state, &mut device_settings, &mut active_report[id]);
} }
} }
} }
if has_temp_reading { })
thermostat.start_tec_readings_conversion(); };
}
}
});
} }
State::SaveFlashSettings => { State::SaveFlashSettings => {
// State Transition // State Transition
@ -226,7 +223,7 @@ fn main() -> ! {
wd.feed(); wd.feed();
laser.power_down(); laser.power_down();
thermostat.power_down(); thermostat.power_down();
net::net::for_each(|mut socket| { net::net::for_each(|mut socket, _| {
if net::net::eth_is_socket_active(socket) { if net::net::eth_is_socket_active(socket) {
unsafe { unsafe {
net::cmd_handler::send_response(&mut ETH_DATA_BUFFER, net::cmd_handler::ResponseEnum::HardReset, None, &mut socket); net::cmd_handler::send_response(&mut ETH_DATA_BUFFER, net::cmd_handler::ResponseEnum::HardReset, None, &mut socket);
@ -239,10 +236,10 @@ fn main() -> ! {
laser.power_down(); laser.power_down();
thermostat.power_down(); thermostat.power_down();
let mut any_socket_alive = false; let mut any_socket_alive = false;
net::net::for_each(|socket| { net::net::for_each(|socket, _| {
if net::net::eth_is_socket_active(socket) { if net::net::eth_is_socket_active(socket) {
net::net::eth_close_socket(socket); net::net::eth_close_socket(socket);
any_socket_alive = true; any_socket_alive = true;
} }
}); });

View File

@ -221,7 +221,7 @@ pub struct TecSetICmd {
/// Make sure kirdy's firmware is flashed with release builds. /// Make sure kirdy's firmware is flashed with release builds.
/// The received message must contain only one json cmd. TCP client should set TCP_NODELAY or equivalent flag in its TCP Socket /// The received message must contain only one json cmd. TCP client should set TCP_NODELAY or equivalent flag in its TCP Socket
/// Settings to avoid unwanted buffering on TX Data and minimize TX latency. /// Settings to avoid unwanted buffering on TX Data and minimize TX latency.
pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHandle, laser: &mut LdDrive, thermostat: &mut Thermostat, state: &mut State, device_settings: &mut DeviceSettings){ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHandle, laser: &mut LdDrive, thermostat: &mut Thermostat, state: &mut State, device_settings: &mut DeviceSettings, active_report: &mut bool){
let mut cmd = TecSetICmd { let mut cmd = TecSetICmd {
json: TecSetICmdJson::default() json: TecSetICmdJson::default()
}; };
@ -266,7 +266,7 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
match cmd.json.data_bool{ match cmd.json.data_bool{
Some(val) => { Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket); send_response(buffer, ResponseEnum::Acknowledge, None, socket);
device_settings.report_readings = val; *active_report = val;
} }
None => { None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_BOOL), socket); send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_BOOL), socket);

View File

@ -61,7 +61,7 @@ pub struct EthernetMgmtPins {
} }
pub type EthInterface = Interface; pub type EthInterface = Interface;
const NUM_OF_SOCKETS : usize = 4; pub const NUM_OF_SOCKETS : usize = 4;
const TCP_BUFFER_SIZE: usize = 2048; const TCP_BUFFER_SIZE: usize = 2048;
static mut RX_RING: Option<[RxRingEntry; 8]> = None; static mut RX_RING: Option<[RxRingEntry; 8]> = None;
static mut TX_RING: Option<[TxRingEntry; 2]> = None; static mut TX_RING: Option<[TxRingEntry; 2]> = None;
@ -207,8 +207,11 @@ impl ServerHandle {
self.link_was_up = self.phy.phy_link_up(); self.link_was_up = self.phy.phy_link_up();
} }
pub fn recv(&mut self, buffer: &mut [u8], socket_handles: SocketHandle)-> Result<usize, smoltcp::socket::tcp::RecvError> { pub fn poll_iface(&mut self) {
self.iface.poll(now_fn(), &mut &mut self.dma, &mut self.socket_set); self.iface.poll(now_fn(), &mut &mut self.dma, &mut self.socket_set);
}
pub fn recv(&mut self, buffer: &mut [u8], socket_handles: SocketHandle)-> Result<usize, smoltcp::socket::tcp::RecvError> {
let socket = self.socket_set.get_mut::<Socket>(socket_handles); let socket = self.socket_set.get_mut::<Socket>(socket_handles);
socket.recv_slice(buffer) socket.recv_slice(buffer)
@ -218,11 +221,14 @@ impl ServerHandle {
let socket = self.socket_set.get_mut::<Socket>(socket_handles); let socket = self.socket_set.get_mut::<Socket>(socket_handles);
if num_bytes > 0 { if num_bytes > 0 {
socket.send_slice(&buffer[..num_bytes]).ok(); socket.send_slice(&buffer[..num_bytes]).ok();
self.poll_iface();
info!("Sent {} bytes.", num_bytes); info!("Sent {} bytes.", num_bytes);
} }
}
// Send bytes out
self.iface.poll(now_fn(), &mut &mut self.dma, &mut self.socket_set); pub fn is_socket_connected(&mut self, socket_handles: SocketHandle)->bool {
let socket = self.socket_set.get_mut::<Socket>(socket_handles);
socket.state() == State::Established
} }
pub fn poll_socket_status(&mut self, socket_handles: SocketHandle)-> bool { pub fn poll_socket_status(&mut self, socket_handles: SocketHandle)-> bool {
@ -357,6 +363,17 @@ pub fn eth_poll_and_update_link_speed() {
} }
} }
pub fn eth_poll_iface() {
unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE {
server_handle.poll_iface();
}
else {
panic!("eth_poll_packet is called before init");
}
}
}
pub fn eth_send(buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) { pub fn eth_send(buffer: &mut [u8], num_bytes: usize, socket_handles: SocketHandle) {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle ) = SERVER_HANDLE {
@ -385,6 +402,17 @@ pub fn eth_recv(buffer: &mut [u8], socket_handles: SocketHandle)-> usize{
} }
} }
pub fn eth_is_socket_connected(socket_handles: SocketHandle) -> bool {
unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE {
server_handle.is_socket_connected(socket_handles)
}
else {
panic!("eth_is_socket_connected is called before init");
}
}
}
pub fn eth_is_socket_active(socket_handles: SocketHandle) -> bool { pub fn eth_is_socket_active(socket_handles: SocketHandle) -> bool {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle ) = SERVER_HANDLE {
@ -407,11 +435,11 @@ pub fn eth_close_socket(socket_handles: SocketHandle) {
} }
} }
pub fn for_each<F: FnMut(SocketHandle)>(mut callback: F) { pub fn for_each<F: FnMut(SocketHandle, usize)>(mut callback: F) {
unsafe { unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE { if let Some(ref mut server_handle ) = SERVER_HANDLE {
for i in 0..NUM_OF_SOCKETS { for i in 0..NUM_OF_SOCKETS {
callback(server_handle.socket_handles[i]); callback(server_handle.socket_handles[i], i);
} }
} }
else { else {
@ -426,10 +454,12 @@ pub fn for_each<F: FnMut(SocketHandle)>(mut callback: F) {
fn ETH() { fn ETH() {
let interrupt_reason = stm32_eth::eth_interrupt_handler(); let interrupt_reason = stm32_eth::eth_interrupt_handler();
cortex_m::interrupt::free(|cs| { cortex_m::interrupt::free(|cs| {
*NET_PENDING.borrow(cs) if interrupt_reason.rx {
.borrow_mut() = true; *NET_PENDING.borrow(cs)
.borrow_mut() = true;
eth_poll_iface();
}
}); });
debug!("Ethernet Interrupt{:?}", interrupt_reason); debug!("Ethernet Interrupt{:?}", interrupt_reason);
} }

View File

@ -218,7 +218,7 @@ impl MAX1968 {
} }
pub fn is_powered_on(&mut self) -> bool { pub fn is_powered_on(&mut self) -> bool {
self.phy.shdn.is_set_low() self.phy.shdn.is_set_high()
} }
pub fn power_down(&mut self) { pub fn power_down(&mut self) {

View File

@ -11,6 +11,7 @@ pub enum TempStatusEnum {
OverTemp, OverTemp,
Unstable, Unstable,
Stable, Stable,
ConstantCurrentMode,
} }
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)] #[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
@ -56,7 +57,8 @@ impl Default for TempMon {
#[derive(Default, PartialEq, Debug)] #[derive(Default, PartialEq, Debug)]
enum State { enum State {
#[default] #[default]
PidOff, PwrOff,
ConstantCurrentMode,
PidStartUp, PidStartUp,
PidStable, PidStable,
OverTempAlarm, OverTempAlarm,
@ -95,16 +97,38 @@ impl TempMon {
self.state = State::default(); self.state = State::default();
} }
pub fn update_status(&mut self, pid_engaged: bool, temp: ThermodynamicTemperature) { pub fn update_status(&mut self, pid_engaged: bool, pwr_on: bool, temp: ThermodynamicTemperature) {
match self.state { match self.state {
State::PidOff => { State::PwrOff => {
self.is_set_point_changed = false; self.is_set_point_changed = false;
self.status.status = TempStatusEnum::Off; self.status.status = TempStatusEnum::Off;
self.count = 0; self.count = 0;
// State Transition // State Transition
if pid_engaged { if pwr_on {
if pid_engaged {
self.state = State::PidStartUp;
} else {
self.state = State::ConstantCurrentMode
}
}
}
State::ConstantCurrentMode => {
let is_over_temp = temp > self.upper_limit || temp < self.lower_limit;
self.status.status = TempStatusEnum::ConstantCurrentMode;
if is_over_temp {
self.state = State::OverTempAlarm;
self.status.status = TempStatusEnum::OverTemp;
}
else if !pwr_on {
self.state = State::PwrOff;
self.status.status = TempStatusEnum::Off;
} else if pid_engaged {
self.state = State::PidStartUp; self.state = State::PidStartUp;
self.status.status = TempStatusEnum::Unstable;
self.is_set_point_changed = false;
} }
} }
State::PidStartUp | State::PidStable => { State::PidStartUp | State::PidStable => {
@ -134,13 +158,20 @@ impl TempMon {
} }
// State Transition // State Transition
if self.status.status == TempStatusEnum::OverTemp { if !pwr_on {
self.state = State::OverTempAlarm; self.state = State::PwrOff;
} else if self.is_set_point_changed { }
self.is_set_point_changed = false; else {
self.state = State::PidStartUp; if self.status.status == TempStatusEnum::OverTemp {
} else if self.status.status == TempStatusEnum::Stable { self.state = State::OverTempAlarm;
self.state = State::PidStable; } else if self.is_set_point_changed {
self.is_set_point_changed = false;
self.state = State::PidStartUp;
} else if self.status.status == TempStatusEnum::Stable {
self.state = State::PidStable;
} else if !pid_engaged {
self.state = State::ConstantCurrentMode
}
} }
} }
State::OverTempAlarm => { State::OverTempAlarm => {

View File

@ -186,7 +186,7 @@ impl Thermostat{
state.update(data); state.update(data);
let pid_engaged = state.get_pid_engaged(); let pid_engaged = state.get_pid_engaged();
let temp = self.get_temperature(); let temp = self.get_temperature();
self.temp_mon.update_status(pid_engaged, temp); self.temp_mon.update_status(pid_engaged, self.max1968.is_powered_on(), temp);
debug!("state.get_pid_engaged(): {:?}", pid_engaged); debug!("state.get_pid_engaged(): {:?}", pid_engaged);
debug!("Temperature: {:?} degree", temp.get::<degree_celsius>()); debug!("Temperature: {:?} degree", temp.get::<degree_celsius>());
data_rdy = true; data_rdy = true;