ok, let's stop
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -111,7 +111,8 @@
|
||||
"string.h": "c",
|
||||
"math.h": "c",
|
||||
"cctype": "c",
|
||||
"stdlib.h": "c"
|
||||
"stdlib.h": "c",
|
||||
"float.h": "c"
|
||||
},
|
||||
"cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip"
|
||||
}
|
||||
496
main.c
496
main.c
@@ -1,3 +1,17 @@
|
||||
/*
|
||||
* HP3478A internal USB-GPIB adapter & extension
|
||||
*
|
||||
* DESCRIPTION:
|
||||
* This firmware acts as a bridge between USB-CDC and GPIB while also working
|
||||
* in standalone mode inside HP3478A and MITM-ing GPIB and adding features.
|
||||
*
|
||||
* TODO:
|
||||
* - Implement a VTable for features, to replace the massive switch
|
||||
* to make consistnet entry and exits
|
||||
* - Break main() up into app_process_usb() and app_process_gpib()?
|
||||
* - SCPI-compliant command set for the passthrough mode
|
||||
* - Data logging?
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
@@ -35,6 +49,7 @@
|
||||
#define MENU_COMMIT_DELAY_MS 2400 // Time to hold before entering mode
|
||||
#define MENU_SUBLAYER_DELAY_MS 500 // Time to "hover" before dots start
|
||||
#define MENU_DEBOUNCE_MS 200 // Button press dead-time
|
||||
#define MENU_LOCKOUT_MS 1000 // How long to ignore SRQ for after exiting menu
|
||||
|
||||
// Polling
|
||||
#define POLL_INTERVAL_MS 100 // 10Hz polling when in Passthrough
|
||||
@@ -51,13 +66,6 @@
|
||||
#define RTD_B -5.775e-7
|
||||
#define RTD_R0 1000.0
|
||||
|
||||
// Thermistor
|
||||
// TODO: different ranges? 5k, 10k etc.?
|
||||
// This is a 5k NTC! kinda useless here
|
||||
#define THERM_A 0.001286
|
||||
#define THERM_B 0.00023595
|
||||
#define THERM_C 0.0000000941
|
||||
|
||||
// Thermocouple
|
||||
#define CJC_FALLBACK_TEMP 22.0 // used if !app.env_sensor_present
|
||||
// this is just cursed but.. yeah, the PCB is near a transformer
|
||||
@@ -181,6 +189,32 @@ typedef enum {
|
||||
MENU_MAX_ITEMS
|
||||
} menu_item_t;
|
||||
|
||||
typedef enum {
|
||||
// Generic
|
||||
NTC_10K_3950 = 0, // Generic china 3950
|
||||
NTC_10K_3435, // alt 10k
|
||||
NTC_50K_3950, // alt 50k
|
||||
NTC_100K_3950, // alt 100k
|
||||
|
||||
// YSI 44000
|
||||
NTC_YSI_2252, // YSI 44004 (Mix B)
|
||||
NTC_YSI_3K, // YSI 44005 (Mix B)
|
||||
NTC_YSI_5K, // YSI 44007 (Mix B)
|
||||
NTC_YSI_10K_A, // YSI 44006 (Mix H)
|
||||
NTC_YSI_10K_B, // YSI 44016 (Mix B)
|
||||
NTC_YSI_30K, // YSI 44008 (Mix H)
|
||||
NTC_YSI_100K, // YSI 44011 (Mix H)
|
||||
NTC_YSI_1MEG, // YSI 44015 (Mix H)
|
||||
|
||||
NTC_MAX_ITEMS
|
||||
} ntc_preset_t;
|
||||
|
||||
typedef struct {
|
||||
double r0; // R @ 25C
|
||||
double beta; // beta coefficient (typ. 3000-4500)
|
||||
const char* name; // display name
|
||||
} ntc_def_t;
|
||||
|
||||
// UI Strings
|
||||
static const char* MENU_NAMES[] = {"REL", "TEMP", "DBM", "CONT",
|
||||
"DIODE", "XOHM", "STATS", "EXIT"};
|
||||
@@ -189,11 +223,30 @@ static const char* SENSOR_NAMES[] = {"PT1000", "THERM", "TYPE K"};
|
||||
|
||||
static const char* WIRE_NAMES[] = {"2-WIRE", "4-WIRE"};
|
||||
|
||||
// some common NTC defs
|
||||
static const ntc_def_t NTC_DEFS[] = {
|
||||
// Generic/China
|
||||
{10000.0, 3950.0, "10K 3950"}, // black bead)
|
||||
{10000.0, 3435.0, "10K 3435"}, // euro?
|
||||
{50000.0, 3950.0, "50K 3950"}, // generic
|
||||
{100000.0, 3950.0, "100K 3950"}, // generic
|
||||
// From YSI datasheet
|
||||
{2252.0, 3891.0, "2.252K YSI"}, // 44004
|
||||
{3000.0, 3891.0, "3K YSI"}, // 44005
|
||||
{5000.0, 3891.0, "5K YSI"}, // 44007
|
||||
{10000.0, 3574.0, "10K YSI A"}, // Mix H (YSI 10k)
|
||||
{10000.0, 3891.0, "10K YSI B"}, // Mix B (matches 2.2K curve)
|
||||
{30000.0, 3810.0, "30K YSI"}, // 44008
|
||||
{100000.0, 3988.0, "100K YSI"}, // 44011
|
||||
{1000000.0, 4582.0, "1MEG YSI"} // 44015
|
||||
};
|
||||
|
||||
// Sub-menu states
|
||||
typedef enum {
|
||||
SUBMENU_NONE = 0,
|
||||
SUBMENU_TEMP_SENS, // step 1: sensor Type
|
||||
SUBMENU_TEMP_WIRE // step 2: wire mode (skipped for Type K)
|
||||
SUBMENU_TEMP_WIRE, // step 2a: wire mode (skipped for Type K)
|
||||
SUBMENU_TEMP_NTC // step 2b: NTC Value (Thermistor only)
|
||||
} submenu_state_t;
|
||||
|
||||
// Sensor Types
|
||||
@@ -209,7 +262,6 @@ typedef enum {
|
||||
DIODE_STATE_OPEN = 0, // probes open
|
||||
DIODE_STATE_CHECKING, // voltage @ diode range, checking stability
|
||||
DIODE_STATE_SHORT, // voltage too low, silent
|
||||
DIODE_STATE_PLAYING, // is a diode, chirping
|
||||
DIODE_STATE_DONE // chirped, latched silent
|
||||
} diode_state_t;
|
||||
|
||||
@@ -271,7 +323,8 @@ typedef struct {
|
||||
uint8_t auto_read : 1;
|
||||
uint8_t dmm_online : 1;
|
||||
uint8_t has_saved_state : 1;
|
||||
uint8_t reserved : 2;
|
||||
uint8_t beep_active : 1;
|
||||
uint8_t reserved : 1;
|
||||
|
||||
// Addresses
|
||||
uint8_t target_addr;
|
||||
@@ -283,6 +336,9 @@ typedef struct {
|
||||
uint32_t env_last_read;
|
||||
uint32_t last_poll_time;
|
||||
uint32_t poll_interval;
|
||||
uint32_t ignore_input_start_ts;
|
||||
uint32_t beep_start_ts; // buzzer
|
||||
uint32_t beep_duration;
|
||||
|
||||
// Logic
|
||||
work_mode_t current_mode;
|
||||
@@ -291,6 +347,7 @@ typedef struct {
|
||||
// Config Selection
|
||||
temp_sensor_t temp_sensor;
|
||||
wire_mode_t temp_wire_mode;
|
||||
ntc_preset_t temp_ntc_preset;
|
||||
|
||||
// Environmental Data
|
||||
aht20_data current_env;
|
||||
@@ -298,7 +355,7 @@ typedef struct {
|
||||
// DMM Restore
|
||||
uint8_t saved_state_bytes[5];
|
||||
|
||||
// Display shadow buffer
|
||||
// Display shadow buffer (cache)
|
||||
char last_disp_sent[HP_DISP_BUF_SIZE];
|
||||
|
||||
// Unionized Mode Data
|
||||
@@ -310,19 +367,28 @@ static app_state_t app = {.dmm_addr = DEFAULT_DMM_ADDR,
|
||||
.poll_interval = POLL_INTERVAL_MS,
|
||||
.gpib_timeout_ms = DEFAULT_GPIB_TIMEOUT_MS};
|
||||
|
||||
// Buffers
|
||||
// TODO: buffer usage is very inconsistent
|
||||
// cmd and resp can probably be a union, and what do we even use tmp_buffer
|
||||
// for?..
|
||||
static char cmd_buffer[128];
|
||||
static char resp_buffer[256];
|
||||
static char tmp_buffer[128];
|
||||
// Max theoretic bytes for 12 visual chars is 24
|
||||
static char disp_buffer[HP_DISP_BUF_SIZE];
|
||||
// Display Command Buffer
|
||||
// Size: "D3"(2) + Text(HP_DISP_BUF_SIZE) + "\n"(1) + Null(1)
|
||||
// "D3" + "1.2....2." + "\n" + \0
|
||||
static char disp_cmd_buffer[2 + HP_DISP_BUF_SIZE + 2];
|
||||
typedef union {
|
||||
// command processing
|
||||
struct {
|
||||
char line_buf[128];
|
||||
char fmt_buf[128];
|
||||
} cmd;
|
||||
|
||||
// general io ctx
|
||||
struct {
|
||||
// for gpib
|
||||
char raw_data[256];
|
||||
} io;
|
||||
|
||||
// display ctx
|
||||
struct {
|
||||
char full_cmd[64];
|
||||
} disp;
|
||||
|
||||
uint8_t raw[256];
|
||||
} app_scratchpad_t;
|
||||
|
||||
static app_scratchpad_t scratch;
|
||||
|
||||
// USB Ring Buffer
|
||||
#define USB_RX_BUF_SIZE 512
|
||||
@@ -385,9 +451,9 @@ void double_to_str(char* buf, size_t buf_size, double val, int prec) {
|
||||
val = -val;
|
||||
}
|
||||
|
||||
// 3. Multiplier (Integer Powers of 10)
|
||||
// multiplier
|
||||
if (prec < 0) prec = 0;
|
||||
if (prec > 9) prec = 9; // Limit precision to prevent overflow
|
||||
if (prec > 9) prec = 9; // limit precision
|
||||
|
||||
unsigned long long multiplier = 1;
|
||||
for (int i = 0; i < prec; i++) multiplier *= 10;
|
||||
@@ -395,7 +461,7 @@ void double_to_str(char* buf, size_t buf_size, double val, int prec) {
|
||||
// scale and round
|
||||
val = (val * (double)multiplier) + 0.5;
|
||||
|
||||
// 5. split integer and fractional parts
|
||||
// split integer and fractional parts
|
||||
unsigned long long full_scaled = (unsigned long long)val;
|
||||
unsigned long int_part = (unsigned long)(full_scaled / multiplier);
|
||||
unsigned long long frac_part = full_scaled % multiplier;
|
||||
@@ -1271,7 +1337,6 @@ void TIM2_IRQHandler(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: maybne don't sleep inside it
|
||||
void tone(unsigned int freq_hz, unsigned int duration_ms) {
|
||||
if (freq_hz == 0) {
|
||||
Delay_Ms(duration_ms);
|
||||
@@ -1283,29 +1348,22 @@ void tone(unsigned int freq_hz, unsigned int duration_ms) {
|
||||
buzzer_set(0);
|
||||
}
|
||||
|
||||
void tone_nb(uint16_t freq, uint32_t duration_ms) {
|
||||
buzzer_set(freq);
|
||||
app.beep_start_ts = millis();
|
||||
app.beep_duration = duration_ms;
|
||||
app.beep_active = 1;
|
||||
}
|
||||
|
||||
// "Boot Up"
|
||||
void play_startup_tune() {
|
||||
// "Boot Up"
|
||||
tone(1500, 100);
|
||||
Delay_Ms(20);
|
||||
Delay_Ms(25);
|
||||
tone(2500, 100);
|
||||
Delay_Ms(20);
|
||||
Delay_Ms(25);
|
||||
tone(4000, 100);
|
||||
}
|
||||
|
||||
void play_connected_tune() {
|
||||
// "Device Attached"
|
||||
tone(3000, 100);
|
||||
tone(4000, 100);
|
||||
}
|
||||
|
||||
void play_disconnected_tune() {
|
||||
// "Device Removed"
|
||||
tone(4000, 100);
|
||||
tone(3000, 100);
|
||||
}
|
||||
|
||||
void beep(int ms) { tone(2500, ms); }
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
int HandleSetupCustom(struct _USBState* ctx, int setup_code) {
|
||||
@@ -1437,12 +1495,7 @@ static void handle_usb_state(void) {
|
||||
if (app.usb_online != raw_status) {
|
||||
app.usb_online = raw_status;
|
||||
|
||||
if (app.usb_online) {
|
||||
usb_rx_tail = usb_rx_head = 0;
|
||||
play_connected_tune();
|
||||
} else {
|
||||
play_disconnected_tune();
|
||||
}
|
||||
if (app.usb_online) usb_rx_tail = usb_rx_head = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1472,12 +1525,12 @@ void dmm_display(const char* text, const char* mode) {
|
||||
|
||||
// update shadow buf
|
||||
strncpy(app.last_disp_sent, text, HP_DISP_BUF_SIZE - 1);
|
||||
app.last_disp_sent[HP_DISP_BUF_SIZE - 1] = '\0';
|
||||
// "D[23]" + Text + "\n"
|
||||
snprintf(disp_cmd_buffer, sizeof(disp_cmd_buffer), "%s%s\n", mode,
|
||||
snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd), "%s%s\n", mode,
|
||||
app.last_disp_sent);
|
||||
|
||||
// send it
|
||||
gpib_send(app.dmm_addr, disp_cmd_buffer);
|
||||
gpib_send(app.dmm_addr, scratch.disp.full_cmd);
|
||||
}
|
||||
static inline void dmm_display_normal(void) {
|
||||
gpib_send(app.dmm_addr, HP3478A_DISP_NORMAL);
|
||||
@@ -1507,33 +1560,34 @@ void restore_dmm_state(void) {
|
||||
decode_dmm_state_bytes(app.saved_state_bytes, &state);
|
||||
|
||||
// "D1 Fx Rx Nx Zx" + "T1 M20"
|
||||
build_restoration_string(cmd_buffer, &state);
|
||||
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_ONLY);
|
||||
build_restoration_string(scratch.cmd.line_buf, &state);
|
||||
strcat(scratch.cmd.line_buf, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_ONLY);
|
||||
|
||||
gpib_send(app.dmm_addr, cmd_buffer);
|
||||
gpib_send(app.dmm_addr, scratch.cmd.line_buf);
|
||||
}
|
||||
|
||||
void exit_to_passthrough(void) {
|
||||
buzzer_set(0); // mute
|
||||
buzzer_set(0);
|
||||
restore_dmm_state();
|
||||
gpib_go_to_local(app.dmm_addr);
|
||||
|
||||
app.current_mode = MODE_PASSTHROUGH;
|
||||
app.data.menu.layer = SUBMENU_NONE;
|
||||
app.menu_pos = 0;
|
||||
app.last_poll_time = millis();
|
||||
app.beep_active = 0;
|
||||
|
||||
Delay_Ms(MENU_DEBOUNCE_MS);
|
||||
uint32_t now = millis();
|
||||
app.last_poll_time = now;
|
||||
app.ignore_input_start_ts = now;
|
||||
}
|
||||
|
||||
void enter_feature_mode(menu_item_t item) {
|
||||
// force display refresh
|
||||
app.last_disp_sent[0] = '\0';
|
||||
// clean buffer for cmds
|
||||
cmd_buffer[0] = '\0';
|
||||
scratch.cmd.line_buf[0] = '\0';
|
||||
|
||||
gpib_remote_enable(1); // assert REN
|
||||
Delay_Ms(20);
|
||||
gpib_remote_enable(1); // make sure REN is asserted
|
||||
|
||||
// we will hold the decoded state here for REL/STATS
|
||||
dmm_decoded_state_t saved_cfg;
|
||||
@@ -1542,16 +1596,17 @@ void enter_feature_mode(menu_item_t item) {
|
||||
case MENU_REL:
|
||||
if (app.has_saved_state) {
|
||||
decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg);
|
||||
build_restoration_string(cmd_buffer, &saved_cfg);
|
||||
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
|
||||
HP3478A_CMD_SRQ_CLEAR);
|
||||
build_restoration_string(scratch.cmd.line_buf, &saved_cfg);
|
||||
strcat(scratch.cmd.line_buf,
|
||||
HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
|
||||
HP3478A_CMD_SRQ_CLEAR);
|
||||
strcpy(app.data.rel.unit, saved_cfg.unit_str);
|
||||
} else {
|
||||
// we have no saved state? don't know relative to WHAT
|
||||
dmm_display("ERR NO STATE", HP3478A_DISP_TEXT_FAST);
|
||||
}
|
||||
|
||||
gpib_send(app.dmm_addr, cmd_buffer);
|
||||
gpib_send(app.dmm_addr, scratch.cmd.line_buf);
|
||||
|
||||
app.current_mode = MODE_FEAT_REL;
|
||||
app.data.rel.offset = 0.0;
|
||||
@@ -1579,8 +1634,20 @@ void enter_feature_mode(menu_item_t item) {
|
||||
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
|
||||
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
|
||||
dmm_display("TEMP TYPE-K", HP3478A_DISP_TEXT_FAST);
|
||||
} else if (app.temp_sensor == SENS_THERMISTOR) {
|
||||
// Thermistor: 2-Wire Ohms (Usually standard for NTC)
|
||||
// Range Auto (NTC varies wildy)
|
||||
gpib_send(app.dmm_addr,
|
||||
HP3478A_FUNC_OHMS_2WIRE HP3478A_RANGE_AUTO HP3478A_DIGITS_5_5
|
||||
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
|
||||
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
|
||||
|
||||
char buf[13];
|
||||
snprintf(buf, sizeof(buf), "NTC %s",
|
||||
NTC_DEFS[app.temp_ntc_preset].name);
|
||||
dmm_display(buf, HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
// Resistor Setup (PT1000/Therm):
|
||||
// Resistor Setup (PT1000):
|
||||
// F3=2W / F4=4W
|
||||
// R4=30kOhm Range
|
||||
if (app.temp_wire_mode == WIRE_2W) {
|
||||
@@ -1597,8 +1664,8 @@ void enter_feature_mode(menu_item_t item) {
|
||||
|
||||
if (app.temp_sensor == SENS_PT1000)
|
||||
dmm_display("TEMP PT1000", HP3478A_DISP_TEXT_FAST);
|
||||
else
|
||||
dmm_display("TEMP THERM", HP3478A_DISP_TEXT_FAST);
|
||||
// else
|
||||
// dmm_display("TEMP THERM", HP3478A_DISP_TEXT_FAST);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1645,20 +1712,21 @@ void enter_feature_mode(menu_item_t item) {
|
||||
case MENU_STATS:
|
||||
if (app.has_saved_state) {
|
||||
decode_dmm_state_bytes(app.saved_state_bytes, &saved_cfg);
|
||||
build_restoration_string(cmd_buffer, &saved_cfg);
|
||||
strcat(cmd_buffer, HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
|
||||
HP3478A_CMD_SRQ_CLEAR);
|
||||
build_restoration_string(scratch.cmd.line_buf, &saved_cfg);
|
||||
strcat(scratch.cmd.line_buf,
|
||||
HP3478A_TRIG_INTERNAL HP3478A_CMD_MASK_BTN_DATA
|
||||
HP3478A_CMD_SRQ_CLEAR);
|
||||
strcpy(app.data.stats.unit, saved_cfg.unit_str);
|
||||
} else {
|
||||
// fallback
|
||||
strcpy(cmd_buffer,
|
||||
strcpy(scratch.cmd.line_buf,
|
||||
HP3478A_FUNC_DC_VOLTS HP3478A_RANGE_AUTO HP3478A_DIGITS_5_5
|
||||
HP3478A_AUTOZERO_ON HP3478A_TRIG_INTERNAL
|
||||
HP3478A_CMD_MASK_BTN_DATA HP3478A_CMD_SRQ_CLEAR);
|
||||
strcpy(app.data.stats.unit, "VDC");
|
||||
}
|
||||
|
||||
gpib_send(app.dmm_addr, cmd_buffer);
|
||||
gpib_send(app.dmm_addr, scratch.cmd.line_buf);
|
||||
|
||||
app.current_mode = MODE_FEAT_STATS;
|
||||
|
||||
@@ -1692,7 +1760,6 @@ void enter_menu_mode(void) {
|
||||
|
||||
dmm_display("M: REL", HP3478A_DISP_TEXT_FAST);
|
||||
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
|
||||
Delay_Ms(200);
|
||||
}
|
||||
|
||||
void handle_feature_logic(void) {
|
||||
@@ -1709,7 +1776,8 @@ void handle_feature_logic(void) {
|
||||
if (!(stb & HP3478A_MASK_DATA_READY)) return;
|
||||
|
||||
// read measurement
|
||||
int len = gpib_receive(app.dmm_addr, resp_buffer, sizeof(resp_buffer));
|
||||
int len = gpib_receive(app.dmm_addr, scratch.io.raw_data,
|
||||
sizeof(scratch.io.raw_data));
|
||||
if (len < 0) {
|
||||
// timeout or error
|
||||
// printf("Read Timeout in Feature\n");
|
||||
@@ -1719,7 +1787,7 @@ void handle_feature_logic(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
double val = parse_double(resp_buffer);
|
||||
double val = parse_double(scratch.io.raw_data);
|
||||
// overload (HP 3478A sends +9.99990E+9 for OL)
|
||||
int is_overload = 0;
|
||||
if (val < DMM_OL_NEG_THRESHOLD || val > DMM_OL_THRESHOLD) is_overload = 1;
|
||||
@@ -1738,7 +1806,7 @@ void handle_feature_logic(void) {
|
||||
if (app.data.rel.stable_count >= REL_STABLE_SAMPLES) {
|
||||
app.data.rel.offset = val;
|
||||
dmm_display("NULL SET", HP3478A_DISP_TEXT_FAST);
|
||||
tone(3000, 50);
|
||||
tone_nb(3000, 50);
|
||||
app.data.rel.stable_count = 0;
|
||||
} else {
|
||||
dmm_display("LOCKING...", HP3478A_DISP_TEXT_FAST);
|
||||
@@ -1754,9 +1822,9 @@ void handle_feature_logic(void) {
|
||||
// prepend 'D'
|
||||
snprintf(eff_unit, sizeof(eff_unit), "D%s", app.data.rel.unit);
|
||||
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), diff, eff_unit,
|
||||
1);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd,
|
||||
sizeof(scratch.disp.full_cmd), diff, eff_unit, 1);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1766,20 +1834,21 @@ void handle_feature_logic(void) {
|
||||
dmm_display("O.VLD", HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
// P(mW) = (V^2 / 50) * 1000 = V^2 * 20
|
||||
double p_mw = (val * val * 20.0f);
|
||||
double p_mw = (val * val * 20.0);
|
||||
|
||||
if (p_mw < 1e-9f) {
|
||||
if (p_mw < 1e-9) {
|
||||
// Align -INF to look consistent
|
||||
// Display: "-INF DBM"
|
||||
memset(disp_buffer, ' ', HP_DISP_LEN);
|
||||
disp_buffer[HP_DISP_LEN] = '\0';
|
||||
memcpy(disp_buffer, "-INF", 4);
|
||||
memcpy(&disp_buffer[8], " DBM", 4);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
memset(scratch.disp.full_cmd, ' ', HP_DISP_LEN);
|
||||
scratch.disp.full_cmd[HP_DISP_LEN] = '\0';
|
||||
memcpy(scratch.disp.full_cmd, "-INF", 4);
|
||||
memcpy(&scratch.disp.full_cmd[8], " DBM", 4);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
double dbm = 10.0 * log10(p_mw);
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), dbm, "DBM", 00);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd,
|
||||
sizeof(scratch.disp.full_cmd), dbm, "DBM", 00);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1791,7 +1860,7 @@ void handle_feature_logic(void) {
|
||||
double temp_c = 0.0;
|
||||
const char* unit_str = " C";
|
||||
|
||||
// 1. TYPE K THERMOCOUPLE
|
||||
// TYPE K THERMOCOUPLE
|
||||
if (app.temp_sensor == SENS_TYPE_K) {
|
||||
// 30mV range safety check (Floating input > 50mV)
|
||||
if (fabs(val) > 0.050) {
|
||||
@@ -1817,9 +1886,9 @@ void handle_feature_logic(void) {
|
||||
temp_c = t_amb + (val * TYPE_K_SCALE);
|
||||
}
|
||||
}
|
||||
// 2. PT1000 RTD (Callendar-Van Dusen)
|
||||
// PT1000 RTD (Callendar-Van Dusen)
|
||||
else if (app.temp_sensor == SENS_PT1000) {
|
||||
if (val < 10.0f) {
|
||||
if (val < 10.0) {
|
||||
dmm_display("SHORT", HP3478A_DISP_TEXT_FAST);
|
||||
return;
|
||||
} else {
|
||||
@@ -1829,32 +1898,44 @@ void handle_feature_logic(void) {
|
||||
double disc = (b * b) - (4 * a * c);
|
||||
|
||||
if (disc >= 0)
|
||||
temp_c = (-b + sqrtf(disc)) / (2 * a);
|
||||
temp_c = (-b + sqrt(disc)) / (2 * a);
|
||||
else {
|
||||
dmm_display("RANGE ERR", HP3478A_DISP_TEXT_FAST);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. THERMISTOR (Steinhart-Hart)
|
||||
// THERMISTOR (simplified Steinhart-Hart)
|
||||
else {
|
||||
if (val < 10.0f) {
|
||||
if (val < 10.0) {
|
||||
dmm_display("SHORT", HP3478A_DISP_TEXT_FAST);
|
||||
return;
|
||||
} else {
|
||||
// log(0) ? :)
|
||||
double r = (val < 1.0f) ? 1.0f : val;
|
||||
double lr = log(r);
|
||||
double lr3 = lr * lr * lr;
|
||||
double inv_t = THERM_A + (THERM_B * lr) + (THERM_C * lr3);
|
||||
|
||||
temp_c = (1.0f / inv_t) - 273.15f;
|
||||
}
|
||||
if (val > 4000000.0) {
|
||||
dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
|
||||
return;
|
||||
}
|
||||
|
||||
double r_meas = val;
|
||||
// constants from our preset
|
||||
double r0 = NTC_DEFS[app.temp_ntc_preset].r0;
|
||||
double beta = NTC_DEFS[app.temp_ntc_preset].beta;
|
||||
const double t0_k = 298.15; // 25.0C in Kelvin
|
||||
|
||||
// 1/T = 1/T0 + (1/B * ln(R/R0))
|
||||
double ln_ratio = log(r_meas / r0);
|
||||
double inv_t = (1.0 / t0_k) + ((1.0 / beta) * ln_ratio);
|
||||
|
||||
// convert Kelvin to Celsius
|
||||
temp_c = (1.0 / inv_t) - 273.15;
|
||||
|
||||
unit_str = "C NTC";
|
||||
}
|
||||
|
||||
// Display: "24.5 C" (or "24.5 C (K)")
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), temp_c, unit_str,
|
||||
0);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
|
||||
temp_c, unit_str, 0);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
// CONT MODE
|
||||
@@ -1871,27 +1952,28 @@ void handle_feature_logic(void) {
|
||||
if (is_overload) {
|
||||
dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), val, "OHM", 1);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd,
|
||||
sizeof(scratch.disp.full_cmd), val, "OHM", 1);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (app.current_mode == MODE_FEAT_DIODE) {
|
||||
uint32_t now = millis();
|
||||
float voltage = is_overload ? 9.9f : (val / 1000.0f);
|
||||
double voltage = is_overload ? 9.9 : (val / 1000.0);
|
||||
uint8_t is_valid_signal =
|
||||
(voltage > DIODE_TH_SHORT && voltage < DIODE_TH_OPEN);
|
||||
|
||||
if (voltage < DIODE_TH_OPEN) {
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), voltage, "VDC", 1);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
|
||||
voltage, "VDC", 1);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
} else {
|
||||
dmm_display("OPEN", HP3478A_DISP_TEXT);
|
||||
}
|
||||
|
||||
diode_state_t next_state = app.data.diode.connected;
|
||||
uint8_t request_buzzer = 0;
|
||||
|
||||
switch (app.data.diode.connected) {
|
||||
case DIODE_STATE_OPEN:
|
||||
@@ -1910,15 +1992,8 @@ void handle_feature_logic(void) {
|
||||
if (!is_valid_signal) {
|
||||
next_state = DIODE_STATE_SHORT; // signal lost/glitch
|
||||
} else if ((now - app.data.diode.chirp_start) >= DIODE_STABLE_MS) {
|
||||
next_state = DIODE_STATE_PLAYING;
|
||||
app.data.diode.chirp_start = now;
|
||||
request_buzzer = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case DIODE_STATE_PLAYING:
|
||||
request_buzzer = 1;
|
||||
if ((now - app.data.diode.chirp_start) >= DIODE_CHIRP_MS) {
|
||||
// has been stable long enough
|
||||
tone_nb(2500, DIODE_CHIRP_MS);
|
||||
next_state = DIODE_STATE_DONE;
|
||||
}
|
||||
break;
|
||||
@@ -1936,7 +2011,6 @@ void handle_feature_logic(void) {
|
||||
}
|
||||
|
||||
app.data.diode.connected = next_state;
|
||||
buzzer_set(request_buzzer ? 2500 : 0);
|
||||
}
|
||||
|
||||
// XOHM MODE
|
||||
@@ -1947,9 +2021,7 @@ void handle_feature_logic(void) {
|
||||
if (val > 8.0e6 && val < 12.0e6) {
|
||||
app.data.xohm.r1 = val; // Store R1
|
||||
app.data.xohm.calibrated = 1;
|
||||
tone(3000, 100);
|
||||
dmm_display("READY", HP3478A_DISP_TEXT_FAST);
|
||||
Delay_Ms(500);
|
||||
tone_nb(3000, 100);
|
||||
} else {
|
||||
dmm_display("OPEN PROBES", HP3478A_DISP_TEXT_FAST);
|
||||
}
|
||||
@@ -1958,15 +2030,16 @@ void handle_feature_logic(void) {
|
||||
// R1 = xohm_ref (Internal)
|
||||
// R2 = val (Measured Parallel)
|
||||
else {
|
||||
if (is_overload || val >= (app.data.xohm.r1 - 1000.0f)) {
|
||||
if (is_overload || val >= (app.data.xohm.r1 - 1000.0)) {
|
||||
dmm_display("OPEN", HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
double r1 = app.data.xohm.r1;
|
||||
double r2 = val;
|
||||
double rx = (r1 * r2) / (r1 - r2);
|
||||
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), rx, "OHM", 1);
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
format_metric_value(scratch.disp.full_cmd,
|
||||
sizeof(scratch.disp.full_cmd), rx, "OHM", 1);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
} else if (app.current_mode == MODE_FEAT_STATS) {
|
||||
@@ -2015,7 +2088,8 @@ void handle_feature_logic(void) {
|
||||
// render
|
||||
if (app.data.stats.view_mode == 0) {
|
||||
// live mode
|
||||
format_metric_value(disp_buffer, sizeof(disp_buffer), val_to_show,
|
||||
format_metric_value(scratch.disp.full_cmd,
|
||||
sizeof(scratch.disp.full_cmd), val_to_show,
|
||||
app.data.stats.unit, 1);
|
||||
} else {
|
||||
// stats mode: prefix (4) + number (8)
|
||||
@@ -2038,17 +2112,18 @@ void handle_feature_logic(void) {
|
||||
double_to_str(num_buf, sizeof(num_buf), scaled, 4);
|
||||
|
||||
// combine: prefix + number + suffix
|
||||
snprintf(disp_buffer, sizeof(disp_buffer), "%s%s%c ", prefix_str,
|
||||
num_buf, suffix ? suffix : ' ');
|
||||
snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd),
|
||||
"%s%s%c ", prefix_str, num_buf, suffix ? suffix : ' ');
|
||||
}
|
||||
|
||||
// send it
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gens the base string (e.g., "M: TEMP", "S: TYPE K") into disp_buffer
|
||||
// gens the base string (e.g., "M: TEMP", "S: TYPE K") into
|
||||
// scratch.disp.full_cmd
|
||||
void prepare_menu_base_string(void) {
|
||||
const char* prefix = "";
|
||||
const char* name = "???";
|
||||
@@ -2062,48 +2137,53 @@ void prepare_menu_base_string(void) {
|
||||
} else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) {
|
||||
prefix = "T: ";
|
||||
if (app.menu_pos < WIRE_MAX_ITEMS) name = WIRE_NAMES[app.menu_pos];
|
||||
} else if (app.data.menu.layer == SUBMENU_TEMP_NTC) {
|
||||
prefix = "N: ";
|
||||
if (app.menu_pos < NTC_MAX_ITEMS) name = NTC_DEFS[app.menu_pos].name;
|
||||
}
|
||||
|
||||
snprintf(disp_buffer, sizeof(disp_buffer), "%s%s", prefix, name);
|
||||
snprintf(scratch.disp.full_cmd, sizeof(scratch.disp.full_cmd), "%s%s", prefix,
|
||||
name);
|
||||
}
|
||||
|
||||
void handle_menu_navigation(void) {
|
||||
uint32_t now = millis();
|
||||
uint32_t elapsed = now - app.data.menu.timer;
|
||||
|
||||
// nav: check SRQ (next item)
|
||||
if (gpib_check_srq()) {
|
||||
uint8_t stb = 0;
|
||||
gpib_serial_poll(app.dmm_addr, &stb);
|
||||
if (elapsed > MENU_DEBOUNCE_MS) {
|
||||
// only poll GPIB if physical line is asserted
|
||||
if (gpib_check_srq()) {
|
||||
uint8_t stb = 0;
|
||||
gpib_serial_poll(app.dmm_addr, &stb);
|
||||
|
||||
// check if it was the front panel btn
|
||||
if (stb & HP3478A_MASK_KEYBOARD_SRQ) {
|
||||
// reset timer
|
||||
app.data.menu.timer = now;
|
||||
app.menu_pos++;
|
||||
|
||||
int max_items = 0;
|
||||
if (app.data.menu.layer == SUBMENU_NONE)
|
||||
max_items = MENU_MAX_ITEMS;
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_SENS)
|
||||
max_items = SENS_MAX_ITEMS;
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_WIRE)
|
||||
max_items = WIRE_MAX_ITEMS;
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_NTC)
|
||||
max_items = NTC_MAX_ITEMS;
|
||||
|
||||
if (app.menu_pos >= max_items) app.menu_pos = 0;
|
||||
|
||||
// update display
|
||||
prepare_menu_base_string();
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
|
||||
// re-arm SRQ
|
||||
gpib_send(app.dmm_addr,
|
||||
HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
|
||||
|
||||
// only 4b (front panel button SRQ)
|
||||
if (stb & HP3478A_MASK_KEYBOARD_SRQ) {
|
||||
if (elapsed < MENU_DEBOUNCE_MS) {
|
||||
// we polled, so STB is clear on DMM. Just return.
|
||||
return;
|
||||
}
|
||||
|
||||
app.data.menu.timer = now; // reset the "hover" timer
|
||||
app.menu_pos++;
|
||||
|
||||
int max_items = 0;
|
||||
if (app.data.menu.layer == SUBMENU_NONE)
|
||||
max_items = MENU_MAX_ITEMS;
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_SENS)
|
||||
max_items = SENS_MAX_ITEMS;
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_WIRE)
|
||||
max_items = WIRE_MAX_ITEMS;
|
||||
|
||||
if (app.menu_pos >= max_items) app.menu_pos = 0;
|
||||
|
||||
// update display immediately
|
||||
prepare_menu_base_string();
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
|
||||
// re-arm SRQ
|
||||
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
|
||||
Delay_Ms(MENU_DEBOUNCE_MS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2115,9 +2195,9 @@ void handle_menu_navigation(void) {
|
||||
int dots = dot_time / MENU_DOT_INTERVAL_MS;
|
||||
if (dots > 3) dots = 3;
|
||||
|
||||
for (int i = 0; i < dots; i++) strcat(disp_buffer, ".");
|
||||
for (int i = 0; i < dots; i++) strcat(scratch.disp.full_cmd, ".");
|
||||
}
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
|
||||
if (elapsed > MENU_COMMIT_DELAY_MS) {
|
||||
// L0: main menu
|
||||
@@ -2129,7 +2209,7 @@ void handle_menu_navigation(void) {
|
||||
app.data.menu.timer = now;
|
||||
|
||||
prepare_menu_base_string();
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
return;
|
||||
}
|
||||
// enter standard modes
|
||||
@@ -2144,6 +2224,12 @@ void handle_menu_navigation(void) {
|
||||
if (app.temp_sensor == SENS_TYPE_K) {
|
||||
// Type K is voltage based so skip wire select
|
||||
enter_feature_mode(MENU_TEMP);
|
||||
} else if (app.temp_sensor == SENS_THERMISTOR) {
|
||||
app.data.menu.layer = SUBMENU_TEMP_NTC;
|
||||
app.menu_pos = 0; // default to 10K
|
||||
app.data.menu.timer = now;
|
||||
prepare_menu_base_string();
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
} else {
|
||||
// wire select (for resistive)
|
||||
app.data.menu.layer = SUBMENU_TEMP_WIRE;
|
||||
@@ -2151,16 +2237,20 @@ void handle_menu_navigation(void) {
|
||||
app.data.menu.timer = now;
|
||||
|
||||
prepare_menu_base_string();
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// L2: wire select
|
||||
// L2a: wire select
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_WIRE) {
|
||||
app.temp_wire_mode = (wire_mode_t)app.menu_pos;
|
||||
enter_feature_mode(MENU_TEMP);
|
||||
return;
|
||||
} // L2b: NTC select
|
||||
else if (app.data.menu.layer == SUBMENU_TEMP_NTC) {
|
||||
app.temp_ntc_preset = (ntc_preset_t)app.menu_pos;
|
||||
enter_feature_mode(MENU_TEMP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2168,6 +2258,13 @@ void handle_menu_navigation(void) {
|
||||
void app_loop(void) {
|
||||
uint32_t now = millis();
|
||||
|
||||
if (app.beep_active) {
|
||||
if ((now - app.beep_start_ts) >= app.beep_duration) {
|
||||
buzzer_set(0);
|
||||
app.beep_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Passthrough
|
||||
if (app.current_mode == MODE_PASSTHROUGH) {
|
||||
int srq_asserted = gpib_check_srq();
|
||||
@@ -2202,13 +2299,14 @@ void app_loop(void) {
|
||||
app.dmm_online = 1;
|
||||
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
|
||||
gpib_go_to_local(app.dmm_addr);
|
||||
tone(4000, 50);
|
||||
tone_nb(4000, 50);
|
||||
app.poll_interval = POLL_INTERVAL_MS; // restore fast polling
|
||||
return;
|
||||
}
|
||||
|
||||
// valid and online, check buttons
|
||||
if (stb & HP3478A_MASK_KEYBOARD_SRQ) {
|
||||
if (stb & HP3478A_MASK_KEYBOARD_SRQ &&
|
||||
(now - app.ignore_input_start_ts) > MENU_LOCKOUT_MS) {
|
||||
enter_menu_mode();
|
||||
}
|
||||
|
||||
@@ -2288,7 +2386,7 @@ static void cmd_help(void) {
|
||||
}
|
||||
|
||||
static void cmd_status(void) {
|
||||
snprintf(tmp_buffer, sizeof(tmp_buffer),
|
||||
snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf),
|
||||
"Stat:\r\n"
|
||||
" Target Addr: %d\r\n"
|
||||
" Internal DMM: %d\r\n"
|
||||
@@ -2298,15 +2396,15 @@ static void cmd_status(void) {
|
||||
" FW: " FW_VERSION "\r\n",
|
||||
app.target_addr, app.dmm_addr, app.gpib_timeout_ms,
|
||||
app.auto_read ? "ON" : "OFF", app.current_mode);
|
||||
usb_send_text(tmp_buffer);
|
||||
usb_send_text(scratch.cmd.fmt_buf);
|
||||
}
|
||||
|
||||
static void process_command(void) {
|
||||
if (!get_start_command(cmd_buffer, sizeof(cmd_buffer))) {
|
||||
if (!get_start_command(scratch.cmd.line_buf, sizeof(scratch.cmd.line_buf))) {
|
||||
return;
|
||||
}
|
||||
|
||||
char* p_cmd = skip_spaces(cmd_buffer);
|
||||
char* p_cmd = skip_spaces(scratch.cmd.line_buf);
|
||||
int is_cpp_cmd = (strncmp(p_cmd, "++", 2) == 0);
|
||||
int is_query = (strchr(p_cmd, '?') != NULL);
|
||||
|
||||
@@ -2333,8 +2431,9 @@ static void process_command(void) {
|
||||
usb_send_text("ERR: Invalid Addr\r\n");
|
||||
} else {
|
||||
// if no arg provided, show current
|
||||
snprintf(tmp_buffer, sizeof(tmp_buffer), "%d\r\n", app.target_addr);
|
||||
usb_send_text(tmp_buffer);
|
||||
snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%d\r\n",
|
||||
app.target_addr);
|
||||
usb_send_text(scratch.cmd.fmt_buf);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2381,9 +2480,9 @@ static void process_command(void) {
|
||||
usb_send_text("ERR: Range 1-60000\r\n");
|
||||
}
|
||||
} else {
|
||||
snprintf(tmp_buffer, sizeof(tmp_buffer), "%lu\r\n",
|
||||
snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%lu\r\n",
|
||||
app.gpib_timeout_ms);
|
||||
usb_send_text(tmp_buffer);
|
||||
usb_send_text(scratch.cmd.fmt_buf);
|
||||
}
|
||||
break;
|
||||
case CMD_TRG:
|
||||
@@ -2427,8 +2526,9 @@ static void process_command(void) {
|
||||
uint8_t poll_addr = (*p_args) ? atoi(p_args) : app.target_addr;
|
||||
uint8_t stb;
|
||||
if (gpib_serial_poll(poll_addr, &stb) == 0) {
|
||||
snprintf(tmp_buffer, sizeof(tmp_buffer), "%d\r\n", stb);
|
||||
usb_send_text(tmp_buffer);
|
||||
snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf), "%d\r\n",
|
||||
stb);
|
||||
usb_send_text(scratch.cmd.fmt_buf);
|
||||
} else
|
||||
usb_send_text("ERR: Bus\r\n");
|
||||
break;
|
||||
@@ -2447,10 +2547,10 @@ static void process_command(void) {
|
||||
while (p_args[i] != 0 && i < HP_DISP_LEN) {
|
||||
char c = p_args[i];
|
||||
if (c >= 'a' && c <= 'z') c -= 32;
|
||||
disp_buffer[i++] = c;
|
||||
scratch.disp.full_cmd[i++] = c;
|
||||
}
|
||||
disp_buffer[i] = 0;
|
||||
dmm_display(disp_buffer, HP3478A_DISP_TEXT_FAST);
|
||||
scratch.disp.full_cmd[i] = 0;
|
||||
dmm_display(scratch.disp.full_cmd, HP3478A_DISP_TEXT_FAST);
|
||||
usb_send_text("OK\r\n");
|
||||
break;
|
||||
}
|
||||
@@ -2538,9 +2638,10 @@ static void process_command(void) {
|
||||
return;
|
||||
|
||||
do_read_operation: {
|
||||
int len = gpib_receive(app.target_addr, resp_buffer, sizeof(resp_buffer));
|
||||
int len = gpib_receive(app.target_addr, scratch.io.raw_data,
|
||||
sizeof(scratch.io.raw_data));
|
||||
if (len > 0) {
|
||||
usb_send_text(resp_buffer);
|
||||
usb_send_text(scratch.io.raw_data);
|
||||
} else if (is_cpp_cmd || is_query) {
|
||||
usb_send_text("ERR: Read Timeout\r\n");
|
||||
}
|
||||
@@ -2574,20 +2675,9 @@ int main() {
|
||||
app.usb_online = 0;
|
||||
app.usb_raw_prev = USB_HW_IS_ACTIVE();
|
||||
app.usb_ts = millis();
|
||||
app.last_poll_time = millis();
|
||||
|
||||
while (1) {
|
||||
uint8_t status_byte;
|
||||
if (gpib_serial_poll(app.dmm_addr, &status_byte) == 0) {
|
||||
// printf("Device Found (Stb: 0x%02X)\n", status_byte);
|
||||
gpib_send(app.dmm_addr, HP3478A_CMD_MASK_BTN_ONLY HP3478A_CMD_SRQ_CLEAR);
|
||||
gpib_go_to_local(app.dmm_addr);
|
||||
break;
|
||||
}
|
||||
Delay_Ms(100);
|
||||
}
|
||||
|
||||
app.dmm_online = 1;
|
||||
app.last_poll_time = 0;
|
||||
app.ignore_input_start_ts = millis() - 2000;
|
||||
app.dmm_online = 0;
|
||||
|
||||
while (1) {
|
||||
handle_usb_state();
|
||||
|
||||
Reference in New Issue
Block a user