feat: autohold, persistent config (ext flash), a little cleanup? maybe

This commit is contained in:
2025-12-04 02:18:47 +06:00
parent 054bc38683
commit 9fed882f50
9 changed files with 1376 additions and 720 deletions

View File

@@ -112,7 +112,9 @@
"math.h": "c",
"cctype": "c",
"stdlib.h": "c",
"float.h": "c"
"float.h": "c",
"stdbool.h": "c",
"initializer_list": "c"
},
"cmake.sourceDirectory": "/home/mira/src/embedded/ch32v208_sens/lwip"
}

147
README.md
View File

@@ -22,73 +22,112 @@ Extended features (Continuity, Temp, etc.) can be accessed using the SRQ button
4. **Sub-Menus:** Some modes (like Temp) have sub-menus for sensor type and 2W/4W mode. Same cycle logic to select these.
5. **Exit:** Press the SRQ button to exit back to initial mode (one we entered menu from).
## Extended Feature Modes
## Features
These can be triggered via the Menu or the serial commands below.
These can be triggered via the Menu or the serial commands below. Although sub-menu features probably don't work through GPIB commands.
Relative and Statistics depend on the mode you enter the menu from, i.e. if you enter relative from DCV it saves your state and you get relative voltage, from 2W relative 2W etc.
| Feature | Command | Description |
| :-------------- | :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Continuity** | `++cont` | Fast, latched continuity beeper (2W Ohm, R0: 30 Ohm Range, N3: 3.5 Digits) |
| **Temperature** | `++temp` | Supports **PT1000**, **Type K** (via shit CJC... because the AHT20 is near the transformer :)), and **Thermistors** (Generic 10k/50k/100k & YSI 44000 series) |
| **Relative** | `++rel` | Set NULL/0 offset |
| **dBm** | `++dbm` | Power measurement calculated against 50Ω reference |
| **Diode** | `++diode` | Diode test with 2.5V open circuit voltage and audible latch |
| **Ext Ohms** | `++xohm` | Measures high resistance (>30MΩ) by calculating against internal 10MΩ parallel resistance |
| **Statistics** | `++math` | Cycles display between Live / Min / Max / Avg |
| **Environment** | `++env` | Reads internal AHT20 sensor (Temp/Humidity) |
| **Display** | `++disp` | write custom string to LCD |
| **Normal** | `++norm` | Resets DMM to standard DC Volts state |
```
++help
## USB-GPIB Command Reference
HP3478A Internal USB-GPIB 1.2.0
Commands start with `++`. Any other text is sent directly to the GPIB bus.
[GPIB Setup]
++addr <0-30> Target Address
++auto <0-2> 0:Off, 1:Read-After-Write, 2:Query-Only
++read_tmo_ms <t> Timeout in ms
++eoi <0|1> Assert hardware EOI on write end
++eos <0-3> Write Term: 0:CRLF, 1:CR, 2:LF, 3:None
++eor <0-7> Read Stop: 0:CRLF ... 7:EOI-Only
++eot_enable <B> Append extra char to read output
++eot_char <dec> The char to append
### Controller Configuration
[System Configuration]
++config List all configurable parameters
++get <name> Get parameter value
++set <name> <v> Set parameter value
++savecfg Save config to flash
++ver Firmware Version
++rst System Reset
| Command | Arguments | Description |
| :--------------- | :-------- | :---------------------------------------------------- |
| `++ver` | | Get Firmware Version |
| `++help` / `++?` | | List available commands |
| `++rst` | | Reset the controller |
| `++addr` | `0-30` | Set target GPIB address (Default: 18) |
| `++tmo` | `ms` | Set USB-to-GPIB read timeout (alias: `++read_tmo_ms`) |
[GPIB Bus Operations]
++read Read from target
++write <data> Write to target
++trg Device Trigger (GET)
++clr Device Clear (SDC)
++dcl Universal Device Clear (DCL)
++ifc Interface Clear (Bus Reset)
++spoll [addr] Serial Poll
++srq Query SRQ Line (0=High/Idle, 1=Low/Active)
++loc Local (Drop REN)
++llo Local Lockout
there's also `++savecfg` and `++mode` to fake prologix compliance
[Internal HP3478A Features]
++cont, ++hold, ++rel, ++xohm
++dbm, ++diode, ++math, ++norm
++temp <l1> <l2> Temperature sensor mode
++env [temp|hum] Internal AHT20 Sensor
++disp <text> Write text to LCD
```
### Protocol & Formatting
There's also a configuration which can be saved using `++savecfg` if you want to make it persist (it's saved to v203's "undocumented" flash).
Also see [inc/config.h](./inc/config.h).
| Command | Arguments | Description |
| :------------- | :-------- | :------------------------------------------------------------------- |
| `++auto` | `0-2` | **0**: Off, **1**: Read-after-Write, **2**: Query-Only (ends in `?`) |
| `++eoi` | `0`\|`1` | Assert hardware EOI line on write end |
| `++eos` | `0-3` | Append to Write: 0:CRLF, 1:CR, 2:LF, 3:None |
| `++eor` | `0-7` | Stop Read on: 0:CRLF ... 7:EOI-Only |
| `++eot_enable` | `0`\|`1` | Append character to USB output stream? |
| `++eot_char` | `dec` | The decimal char to append (e.g., 10 for `\n`) |
```
++config
my_addr: 0
dmm_addr: 18
target_addr: 18
eot_char: 0
eot_enable: 0
eoi_assert: 1
eos_mode: 0
eor_mode: 0
auto_read: 0
gpib_timeout_ms: 1200
poll_interval_ms: 100
env_sensor_read_interval_ms: 1000
dmm_recovery_delay_ms: 1000
usb_debounce_connect_ms: 50
usb_debounce_disconnect_ms: 200
usb_timeout_target_ms: 5
menu_dot_interval_ms: 500
menu_commit_delay_ms: 2400
menu_sublayer_delay_ms: 500
menu_debounce_ms: 100
menu_lockout_ms: 1000
stats_cycle_time_ms: 3000
cont_disp_update_ms: 150
diode_stable_ms: 20
buzzer_chirp_hz: 3000
buzzer_chirp_ms: 75
buzzer_cont_hz: 2500
rtd_a: 0.003908
rtd_b: -0.000001
rtd_r0: 1000.000000
cjc_fallback_temp: 22.000000
cjc_self_heating_offset: 4.000000
type_k_scale: 24390.240000
dbm_ref_z: 50.000000
diode_th_short: 0.050000
diode_th_open: 2.500000
autohold_threshold: 1.000000
autohold_change_req: 2.000000
autohold_min_val: 0.050000
cont_threshold_ohms: 10.000000
autohold_stable_count: 3
rel_stable_count: 3
```
### Bus Management
i.e.
| Command | Description |
| :-------- | :------------------------------------------------ |
| `++read` | Read data from target immediately |
| `++write` | Write data to target manually |
| `++trg` | Issue Group Execute Trigger (GET) |
| `++clr` | Selected Device Clear (SDC) |
| `++dcl` | Universal Device Clear (resets entire bus) |
| `++spoll` | Serial Poll target (returns Status Byte) |
| `++srq` | Query SRQ Line status (0=Idle, 1=Active) |
| `++stat` | Print controller internal status (Addr, Tmo, etc) |
```
++set dbm_ref_z 60
++savecfg
```
### Lower Level Control
| Command | Description |
| :------ | :------------------------------------------ |
| `++loc` | Go To Local (Release Remote Lock) |
| `++gtl` | Send "Go To Local" GPIB message |
| `++llo` | Local Lockout (Disable front panel buttons) |
| `++ren` | Toggle Remote Enable line |
| `++ifc` | Interface Clear (Hard Bus Reset) |
Now your dBm feature will use 600 ohm ref instead of 50.
## Known Issues

View File

@@ -5,5 +5,10 @@
// #define FUNCONF_SYSTEM_CORE_CLOCK 120000000
// #define FUNCONF_PLL_MULTIPLIER 15
#define FUNCONF_SYSTICK_USE_HCLK 1
#define FUNCONF_USE_DEBUGPRINTF 0
#define FUNCONF_USE_UARTPRINTF 0
#define FUNCONF_USE_USBPRINTF 0
#define FUNCONF_NULL_PRINTF 1
// #define GPIB_DEBUG 1
#endif

154
inc/config.h Normal file
View File

@@ -0,0 +1,154 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <stdbool.h>
#include <stdint.h>
#define ERASE_PAGE_SIZE 64
#define CONFIG_MAGIC 0x1234ABCD
#define CONFIG_VERSION 1
#define MY_ADDR 0
#define DEFAULT_DMM_ADDR 18 // the HP3478A addr
// Timing Config
#define USB_DEBOUNCE_CONNECT_MS 50
#define USB_DEBOUNCE_DISCONNECT_MS 200
#define ENV_SENSOR_READ_INTERVAL_MS 1000
#define DEFAULT_GPIB_TIMEOUT_MS 1200
// This kind of ass but.. yeah, I don't want to access systick there
// assume if the PC hasn't read after ~5ms it's stalled
#define USB_TIMEOUT_TARGET_MS 5
#define CYCLES_PER_LOOP 50
#define USB_TIMEOUT_LIMIT \
((FUNCONF_SYSTEM_CORE_CLOCK / 1000 * USB_TIMEOUT_TARGET_MS) / CYCLES_PER_LOOP)
// Menu Animation Timings
#define MENU_DOT_INTERVAL_MS 500 // Speed of "..." addition
#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 100 // 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
#define DMM_RECOVERY_DELAY_MS 1000 // Backoff if DMM vanishes
// Diode sound
#define DIODE_TH_SHORT 0.050 // Volts (below this = SHORT)
#define DIODE_TH_OPEN 2.500 // Volts (above this = OPEN/OL)
#define DIODE_STABLE_MS 20 // wait X ms for voltage to settle
#define DIODE_CHIRP_MS 50
// PT1000 (DIN 43760 / IEC 751 Standard)
#define RTD_A 3.9083e-3
#define RTD_B -5.775e-7
#define RTD_R0 1000.0
// 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
// ideally, the temp should be measured right at the binding posts..
#define CJC_SELF_HEATING_OFFSET 4.0
#define TYPE_K_SCALE 24390.24 // 1 / 41uV
// dBm
#define DBM_REF_Z 50.0
// Stats
#define STATS_CYCLE_TIME_MS 3000 // time per screen (Live -> Avg -> Min...)
#define STATS_INIT_MIN_VAL 1.0e9
#define STATS_INIT_MAX_VAL -1.0e9
// Autohold
#define AUTOHOLD_THRESHOLD 1.0 // deviation % allowed for stability check
#define AUTOHOLD_CHANGE_REQ 2.0 // % new value must differ by to trigger upd
// XXX: ideally, we should have different sample requirements for diff ranges
// we don't need as many counts at N5 range as at N3
#define AUTOHOLD_STABLE_COUNT 3 // num samples required
#define AUTOHOLD_MIN_VAL 0.05 // noise floor
#define CONT_DISP_UPDATE_MS 150 // display throttling
#define CONT_THRESHOLD_OHMS 10.0 // continuity beep threshold
#define REL_STABLE_COUNT 3 // filter depth for Relative NULL
#define BUZZER_CHIRP_HZ 3000
#define BUZZER_CHIRP_MS 75
#define BUZZER_CONT_HZ 2500
// #define LOCAL_COMMIT 1
typedef struct {
uint32_t magic;
uint32_t version;
// addressing
uint8_t my_addr; // controller
uint8_t dmm_addr; // hp3478a
uint8_t target_addr; // target
uint8_t eot_char;
uint8_t eot_enable;
uint8_t eoi_assert;
uint8_t eos_mode;
uint8_t eor_mode;
uint8_t auto_read;
uint8_t padding; // align
// timings (ms)
uint32_t gpib_timeout_ms;
uint32_t poll_interval_ms;
uint32_t env_sensor_read_interval_ms;
uint32_t dmm_recovery_delay_ms;
uint32_t usb_debounce_connect_ms;
uint32_t usb_debounce_disconnect_ms;
uint32_t usb_timeout_target_ms;
// timings
uint32_t menu_dot_interval_ms;
uint32_t menu_commit_delay_ms;
uint32_t menu_sublayer_delay_ms;
uint32_t menu_debounce_ms;
uint32_t menu_lockout_ms;
uint32_t stats_cycle_time_ms;
uint32_t cont_disp_update_ms;
uint32_t diode_stable_ms;
// buzzer
uint32_t buzzer_chirp_hz;
uint32_t buzzer_chirp_ms;
uint32_t buzzer_cont_hz;
// math & cal
double rtd_a;
double rtd_b;
double rtd_r0;
double cjc_fallback_temp;
double cjc_self_heating_offset;
double type_k_scale;
double dbm_ref_z;
double diode_th_short;
double diode_th_open;
// thresholds
double autohold_threshold;
double autohold_change_req;
double autohold_min_val;
double cont_threshold_ohms;
// logic counts
uint32_t autohold_stable_count;
uint32_t rel_stable_count;
} fw_config_t;
typedef enum { CFG_TYPE_UINT8, CFG_TYPE_UINT32, CFG_TYPE_DOUBLE } cfg_type_t;
typedef struct {
const char* name;
size_t offset;
cfg_type_t type;
} cfg_field_t;
#endif // CONFIG_H

View File

@@ -3,8 +3,6 @@
#include "ch32fun.h"
// #define GPIB_DEBUG 1
// Control Lines (Active LOW)
#define PIN_EOI PB3
#define PIN_REN PB11
@@ -147,6 +145,7 @@
#define HP3478A_CMD_MASK_BTN_DATA "M21"
// "M00" -> Clear Mask (Disable SRQ)
#define HP3478A_CMD_MASK_CLEAR "M00"
#define HP3478A_CMD_MASK_BTN_SYNERR "M24"
#define GPIB_ASSERT(pin) funDigitalWrite(pin, 0)
#define GPIB_RELEASE(pin) funDigitalWrite(pin, 1)

View File

@@ -9,11 +9,7 @@
#define SYSTICK_ONE_MILLISECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000)
#define SYSTICK_ONE_MICROSECOND ((uint32_t)FUNCONF_SYSTEM_CORE_CLOCK / 1000000)
extern volatile uint32_t systick_millis;
#define millis() (systick_millis)
#define micros() (SysTick->CNT / SYSTICK_ONE_MICROSECOND)
void systick_init(void);
#endif // SYSTICK_H

View File

@@ -22,10 +22,10 @@
#define FUSB_USB_VID 0x1209
#define FUSB_USB_PID 0x3478
#define FUSB_USB_REV 0x0110
#define FUSB_USB_REV 0x0120
#define FUSB_STR_MANUFACTURER u"Open Source GPIB"
#define FUSB_STR_PRODUCT u"HP3478A Internal Adapter"
#define FUSB_STR_SERIAL u"3478A-USB-110"
#define FUSB_STR_SERIAL u"3478A-USB-120"
//Taken from http://www.usbmadesimple.co.uk/ums_ms_desc_dev.htm
static const uint8_t device_descriptor[] = {

1754
main.c

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
#include "systick.h"
volatile uint32_t systick_millis;
void systick_init(void) {
SysTick->CTLR = 0x0000;
SysTick->CMP = SysTick->CNT + SYSTICK_ONE_MILLISECOND;
systick_millis = 0;
SysTick->CTLR = SYSTICK_CTLR_STE | // Enable Counter
SYSTICK_CTLR_STIE | // Enable Interrupts
SYSTICK_CTLR_STCLK; // Set Clock Source to HCLK/1
NVIC_EnableIRQ(SysTick_IRQn);
}
void SysTick_Handler(void) __attribute__((interrupt));
void SysTick_Handler(void) {
SysTick->CMP = SysTick->CNT + SYSTICK_ONE_MILLISECOND;
SysTick->SR = 0;
systick_millis++;
}