Compare commits

...

6 Commits

Author SHA1 Message Date
linuswck 7be35fe7f0 thermostat: Reduce DAC calibration time
- 13 out of 18 bit of DAC is used to calibrate the output with 12bit ADC
- Calibration with more than 13 bit does not improve the accuracy
2024-04-23 15:45:37 +08:00
linuswck c5efc6ca57 MAX1968: Improve STM32 ADC calibration consistency 2024-04-23 15:35:23 +08:00
linuswck b330f45260 net: Increase ARP Cache Size from 4 to 16 2024-04-23 11:43:27 +08:00
linuswck 554f292cab net: Disable nagle algo & Set keep_alive duration 2024-04-22 17:06:07 +08:00
linuswck f303ab639a net: Reset tcp socks when new eth link is detected 2024-04-22 17:06:02 +08:00
linuswck ba30575406 GUI: Improve abnormal disconnection handling 2024-04-22 17:05:56 +08:00
6 changed files with 56 additions and 36 deletions

View File

@ -25,7 +25,7 @@ cortex-m-log = { version = "0.7.0", features = ["log-integration", "semihosting"
stm32f4xx-hal = { version = "0.20.0", features = ["stm32f407", "otg-fs", "usb_fs"] }
stm32-eth = { version = "0.6.0", features = ["stm32f407", "smoltcp-phy", "smoltcp"] }
ieee802_3_miim = "0.8.0"
smoltcp = { version = "0.11.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet"] }
smoltcp = { version = "0.11.0", default-features = false, features = ["proto-ipv4", "socket-tcp", "log", "medium-ethernet", "iface-neighbor-cache-count-16"] }
uom = { version = "0.30", default-features = false, features = ["autoconvert", "si", "f32", "use_serde"] }
num-traits = { version = "0.2.15", default-features = false, features = ["libm"] }
usb-device = "0.3.2"

View File

@ -637,21 +637,18 @@ class Kirdy:
Decode newline delimited Json objects and return the latest json received inside the buffer.
- buffer_size: Integer
"""
try:
try:
raw_response = await asyncio.wait_for(self._reader.read(buffer_size), self.timeout)
except asyncio.exceptions.TimeoutError:
return {
"msg_type": "Internal Timeout"
}
response = raw_response.decode('utf-8', errors='ignore').split("\n")
for item in reversed(response):
try:
return json.loads(item)
except json.decoder.JSONDecodeError:
pass
return { "msg_type": "Internal No json object found in response" }
response = raw_response.decode('utf-8', errors='ignore').split("\n")
for item in reversed(response):
try:
return json.loads(item)
except json.decoder.JSONDecodeError as e:
pass
return { "msg_type": "Exception"}
except TimeoutError:
return { "msg_type": "Exception"}
async def _send_raw_cmd_handler(self, cmd, lock=True, msg_type="Acknowledge"):
if lock:
@ -719,12 +716,10 @@ class Kirdy:
response = await self._read_response()
if response["msg_type"] == msg_type:
return response
retry += 1
await asyncio.sleep(0.1)
except asyncio.exceptions.CancelledError:
return None
except Exception as e:
retry += 1
logging.error(f"_send_cmd Exception: {e}")
await asyncio.sleep(0.1)
raise NoAckRecv
async def report_mode(self, report_interval = 0.0, buffer_size=16384):

View File

@ -30,6 +30,8 @@ from dateutil import tz
import math
import socket
COMMON_ERROR_MSG = "Connection Timeout. Disconnecting."
def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ master")
@ -70,12 +72,13 @@ class KirdyDataWatcher(QObject):
task = asyncio.create_task(self.signal_emitter())
while True:
if task.done():
_ = task.result()
task = asyncio.create_task(self.signal_emitter())
await asyncio.sleep(self._update_s)
except asyncio.CancelledError:
pass
task.cancel()
except Exception as e:
logging.error(f"Encountered an error: {e}. disconnecting.", exc_info=True)
logging.error(COMMON_ERROR_MSG)
self._kirdy.stop_report_mode()
self.connection_error_sig.emit()
@ -102,10 +105,11 @@ class KirdyDataWatcher(QObject):
async def report_mode(self):
try:
async for status_report in self._kirdy.report_mode():
if status_report["msg_type"] == "Exception":
raise TimeoutError("Connection Timeout")
self.report_update_sig.emit(status_report)
except Exception as e:
logging.error(f"Encountered an error: {e}, disconnecting", exc_info=True)
self._kirdy.stop_report_mode()
logging.error(f"{COMMON_ERROR_MSG}")
self.connection_error_sig.emit()
@pyqtSlot(float)
@ -216,7 +220,7 @@ class Graphs:
self.tec_i_measure_connector.cb_append_data_point(tec_i_measure, ts)
self.tec_i_target_connector.cb_append_data_point(tec_i_set, ts)
except Exception as e:
logging.error(f"Graph Value cannot be updated. Error:{e}. Data:{report}")
logging.error(f"Graph Value cannot be updated. Data:{report}", exc_info=True)
def clear_data_pts(self):
for connector in self.connectors:
@ -685,7 +689,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.params[1].child('Photodiode Monitor Config', 'Responsitivity').setValue(0)
self.params[1].child('Photodiode Monitor Config', 'Dark Current').setValue(settings["pd_mon_params"]["i_dark"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{settings}")
logging.error(f"Params tree cannot be updated. Data:{settings}", exc_info=True)
@asyncSlot(dict)
async def update_ld_ctrl_panel_readings(self, report):
@ -704,7 +708,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.params[1].child('Readings', 'PD Power').setValue(0)
self.params[1].child('Readings', 'LF Mod Impedance').setValue(report["term_status"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{report}")
logging.error(f"Params tree cannot be updated. Data:{report}", exc_info=True)
@asyncSlot(dict)
async def update_thermostat_ctrl_panel_settings(self, settings):
@ -732,7 +736,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.graphs.set_temp_setpoint_line(temp=round(settings["temperature_setpoint"], 6))
self.graphs.set_temp_setpoint_line(visible=settings['pid_engaged'])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{settings}")
logging.error(f"Params tree cannot be updated. Data:{settings}", exc_info=True)
@asyncSlot(dict)
async def update_thermostat_ctrl_panel_readings(self, report):
@ -745,7 +749,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
self.params[3].child('Readings', 'Temperature').setValue(report["temperature"])
self.params[3].child('Readings', 'Current through TEC').setValue(report["tec_i"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{report}")
logging.error(f"Params tree cannot be updated. Data:{report}", exc_info=True)
@pyqtSlot(int)
def set_max_samples(self, samples: int):

View File

@ -6,7 +6,7 @@ use log::{debug, info};
use smoltcp::{
iface::{
self, Interface, SocketHandle, SocketSet, SocketStorage
}, socket::tcp::{Socket, SocketBuffer, State}, time::Instant, wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}
}, socket::tcp::{Socket, SocketBuffer, State}, time::{Instant, Duration}, wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}
};
use stm32_eth::{
Parts, EthPins, PartsIn,
@ -160,6 +160,8 @@ impl ServerHandle {
for i in 0..NUM_OF_SOCKETS {
let socket = socket_set.get_mut::<Socket>(tcp_handles[i]);
socket.listen(socket_addr).ok();
socket.set_keep_alive(Some(Duration::from_secs(1)));
socket.set_nagle_enabled(false);
}
iface.poll(Instant::from_millis(i64::from(sys_timer::now())), &mut &mut dma, &mut socket_set);
@ -190,7 +192,7 @@ impl ServerHandle {
}
}
pub fn update_link_speed(&mut self) {
pub fn update_link_speed(&mut self)-> bool {
if !self.link_was_up & self.phy.phy_link_up() {
if let Some(speed) = self.phy.speed().map(|s| match s {
PhySpeed::HalfDuplexBase10T => Speed::HalfDuplexBase10T,
@ -200,11 +202,14 @@ impl ServerHandle {
}) {
info!("New eth link is up. Setting detected PhySpeed: {:?}", speed);
self.phy.get_miim().set_speed(speed);
self.link_was_up = self.phy.phy_link_up();
return true;
} else {
debug!("Failed to detect link speed.");
}
}
self.link_was_up = self.phy.phy_link_up();
return false;
}
pub fn poll_iface(&mut self) {
@ -355,8 +360,14 @@ impl<M: Miim> EthernetPhy<M> {
pub fn eth_poll_link_status_and_update_link_speed() -> bool {
unsafe {
if let Some(ref mut server_handle ) = SERVER_HANDLE {
server_handle.update_link_speed();
return server_handle.link_was_up;
let new_link_is_up = server_handle.update_link_speed();
if new_link_is_up {
info!("Resetting TCP Sockets");
for_each(|socket, _| {
eth_close_socket(socket);
});
}
return new_link_is_up;
}
else {
panic!("eth_poll_link_status_and_update_link_speed is called before init");

View File

@ -125,7 +125,17 @@ impl MAX1968 {
.default_sample_time(config::SampleTime::Cycles_480);
// Do not set reset RCCs as it causes other ADCs' clock to be disabled
let mut pins_adc1 = Adc::adc1(adc1, false, adc_config);
pins_adc1.calibrate();
// adc1.calibrate() fn only read REFINT once to assign the calibration value.
// It does not take the STM32F4's ADC Precision Limitation into account.
// AN4073: ADC Reading Dispersion can be reduced through Averaging
let mut vdda_mv: u32 = 0;
for _ in (0..512).rev() {
pins_adc1.calibrate();
vdda_mv += pins_adc1.reference_voltage();
}
vdda_mv = vdda_mv / 512 as u32;
pins_adc1.apply_config(adc_config.reference_voltage(vdda_mv));
let adc_config = AdcConfig::default()
.clock(config::Clock::Pclk2_div_8)

View File

@ -302,12 +302,12 @@ impl Thermostat{
/// VREF measurement can introduce significant noise at the current output, degrading the stabilily performance of the
/// thermostat.
pub fn calibrate_dac_value(&mut self) {
const STEPS: i32 = 18;
let target_voltage = self.max1968.adc_read(AdcReadTarget::VREF, 64);
const DAC_BIT: u32 = 18;
const ADC_BIT: u32 = 12;
let target_voltage = self.max1968.adc_read(AdcReadTarget::VREF, 512);
let mut start_value = 1;
let mut best_error = ElectricPotential::new::<volt>(100.0);
for step in (0..STEPS).rev() {
debug!("TEC VREF Calibrating: Step {}/{}", step, STEPS);
for step in (DAC_BIT-ADC_BIT-1..DAC_BIT).rev() {
let mut prev_value = start_value;
for value in (start_value..=ad5680::MAX_VALUE).step_by(1 << step) {
self.max1968.phy.dac.set(value).unwrap();