go faster, add some more prologix cmds, readme
This commit is contained in:
94
README.md
94
README.md
@@ -0,0 +1,94 @@
|
||||
# HP3478A Internal USB-GPIB Extension
|
||||
|
||||
An internal extension board for the HP3478A Multimeter. USB-CDC-GPIB bridge (Prologix-style) and an internal feature controller.
|
||||
|
||||
It enumerates as a standard serial device (e.g., /dev/ttyACM0 or COMx).
|
||||
|
||||
PCB [here](https://git.ayau.me/mira/mhardware/src/branch/master/hp3478a-ch32-ext)
|
||||
|
||||
## Menu System
|
||||
|
||||
Extended features (Continuity, Temp, etc.) can be accessed using the SRQ button of HP3478A without a PC
|
||||
|
||||
1. **Enter Menu:** Press the Front Panel **SRQ**.
|
||||
2. **Nav:** Press the button again to cycle through modes (`M: REL` -> `M: TEMP` -> `M: DBM`...).
|
||||
3. **Selection (Hover):** Stop pressing the button.
|
||||
- The display will animate dots (`...`) after 500ms.
|
||||
- After ~2.5 seconds of inactivity, the selected mode is activated.
|
||||
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
|
||||
|
||||
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 |
|
||||
|
||||
## USB-GPIB Command Reference
|
||||
|
||||
Commands start with `++`. Any other text is sent directly to the GPIB bus.
|
||||
|
||||
### Controller Configuration
|
||||
|
||||
| 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`) |
|
||||
|
||||
there's also `++savecfg` and `++mode` to fake prologix compliance
|
||||
|
||||
### Protocol & Formatting
|
||||
|
||||
| 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`) |
|
||||
|
||||
### Bus Management
|
||||
|
||||
| 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) |
|
||||
|
||||
### 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) |
|
||||
|
||||
## Known Issues
|
||||
|
||||
1. I did NOT use the brain when routing the USBLC6. Either have to DNP the IC and jumper the data lines, or rely on the software hack to check USB `SUSPEND` to guess if the host is connected. I do the latter, works but meh.
|
||||
2. The buzzer is **not on a hardware TIM pin**. It's on a generic GPIO... This means it can't be hardware/timer PWMed. Instead, the firmware is firing a TIM IRQ thousands of times a second just to manually toggle the pin in the ISR..
|
||||
3. There is a microSD card footprint on the board. The fw does absolutely nothing with it. Data logging?
|
||||
4. The feature logic is a massive `switch`.. no VTable, no function pointer structs, it's quite messy
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "ch32fun.h"
|
||||
|
||||
#define GPIB_DEBUG 1
|
||||
// #define GPIB_DEBUG 1
|
||||
|
||||
// Control Lines (Active LOW)
|
||||
#define PIN_EOI PB3
|
||||
@@ -153,39 +153,37 @@
|
||||
#define GPIB_READ(pin) funDigitalRead(pin)
|
||||
|
||||
// Data Lines (DIO1-DIO8)
|
||||
// #define PIN_DIO1 PB9
|
||||
// #define PIN_DIO2 PB8
|
||||
// #define PIN_DIO3 PB5
|
||||
// #define PIN_DIO4 PB4
|
||||
// #define PIN_DIO5 PB15
|
||||
// #define PIN_DIO6 PB14
|
||||
// #define PIN_DIO7 PB13
|
||||
// #define PIN_DIO8 PB12
|
||||
#define PIN_DIO1 PB9
|
||||
#define PIN_DIO2 PB8
|
||||
#define PIN_DIO3 PB5
|
||||
#define PIN_DIO4 PB4
|
||||
#define PIN_DIO5 PB15
|
||||
#define PIN_DIO6 PB14
|
||||
#define PIN_DIO7 PB13
|
||||
#define PIN_DIO8 PB12
|
||||
|
||||
// Physical Pin mappings on PORT B
|
||||
#define PIN_DIO1 9 // PB9
|
||||
#define PIN_DIO2 8 // PB8
|
||||
#define PIN_DIO3 5 // PB5
|
||||
#define PIN_DIO4 4 // PB4
|
||||
#define PIN_DIO5 15 // PB15
|
||||
#define PIN_DIO6 14 // PB14
|
||||
#define PIN_DIO7 13 // PB13
|
||||
#define PIN_DIO8 12 // PB12
|
||||
#define PIN_POS_D1 9 // PB9
|
||||
#define PIN_POS_D2 8 // PB8
|
||||
#define PIN_POS_D3 5 // PB5
|
||||
#define PIN_POS_D4 4 // PB4
|
||||
#define PIN_POS_D5 15 // PB15
|
||||
#define PIN_POS_D6 14 // PB14
|
||||
#define PIN_POS_D7 13 // PB13
|
||||
#define PIN_POS_D8 12 // PB12
|
||||
|
||||
#define SHIFT_DIO1 (PIN_DIO1 - 0) // 9
|
||||
#define SHIFT_DIO2 (PIN_DIO2 - 1) // 7
|
||||
#define SHIFT_DIO3 (PIN_DIO3 - 2) // 3
|
||||
#define SHIFT_DIO4 (PIN_DIO4 - 3) // 1
|
||||
#define SHIFT_DIO5 (PIN_DIO5 - 4) // 11
|
||||
#define SHIFT_DIO6 (PIN_DIO6 - 5) // 9
|
||||
#define SHIFT_DIO7 (PIN_DIO7 - 6) // 7
|
||||
#define SHIFT_DIO8 (PIN_DIO8 - 7) // 5
|
||||
#define SHIFT_GRP_9 9 // D1 (PB9->0) & D6 (PB14->5)
|
||||
#define SHIFT_GRP_7 7 // D2 (PB8->1) & D7 (PB13->6)
|
||||
#define SHIFT_D3 3 // D3 (PB5->2)
|
||||
#define SHIFT_D4 1 // D4 (PB4->3)
|
||||
#define SHIFT_D5 11 // D5 (PB15->4)
|
||||
#define SHIFT_D8 5 // D8 (PB12->7)
|
||||
|
||||
// pins that share the same shift amount
|
||||
// Group A: shift right 9 (PB9->Bit0, PB14->Bit5)
|
||||
#define MASK_GROUP_A ((1 << 0) | (1 << 5))
|
||||
#define MASK_GRP_9 ((1 << 0) | (1 << 5))
|
||||
// Group B: shift right 7 (PB8->Bit1, PB13->Bit6)
|
||||
#define MASK_GROUP_B ((1 << 1) | (1 << 6))
|
||||
#define MASK_GRP_7 ((1 << 1) | (1 << 6))
|
||||
|
||||
#define CALC_PIN_BSHR(val, bit_idx, pin_num) \
|
||||
((val & (1 << bit_idx)) ? (1U << (pin_num + 16)) : (1U << pin_num))
|
||||
@@ -199,7 +197,4 @@
|
||||
#define MASK_DIO7 (1U << 13)
|
||||
#define MASK_DIO8 (1U << 12)
|
||||
|
||||
// static const int DIO_PINS[] = {PIN_DIO1, PIN_DIO2, PIN_DIO3, PIN_DIO4,
|
||||
// PIN_DIO5, PIN_DIO6, PIN_DIO7, PIN_DIO8};
|
||||
|
||||
#endif
|
||||
|
||||
384
main.c
384
main.c
@@ -44,6 +44,13 @@
|
||||
#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
|
||||
@@ -215,7 +222,7 @@ typedef enum {
|
||||
MODE_FEAT_CONT, // Continuity Mode active
|
||||
MODE_FEAT_DIODE, // Diode test mode
|
||||
MODE_FEAT_XOHM, // Extended Ohms active
|
||||
MODE_FEAT_STATS // avg/min/max
|
||||
MODE_FEAT_STATS // AVG/MIN/MAX
|
||||
} work_mode_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -463,14 +470,18 @@ typedef enum { SESSION_WRITE, SESSION_READ } session_mode_t;
|
||||
static uint32_t gpib_write_lut[256];
|
||||
|
||||
// USB Ring Buffers
|
||||
#define USB_RX_BUF_SIZE 512
|
||||
#define USB_TX_BUF_SIZE 1024
|
||||
#define USB_RX_BUF_SIZE 1024
|
||||
#define USB_RX_MASK (USB_RX_BUF_SIZE - 1)
|
||||
|
||||
#define USB_TX_BUF_SIZE 2048
|
||||
#define USB_TX_MASK (USB_TX_BUF_SIZE - 1)
|
||||
|
||||
volatile uint8_t usb_rx_buffer[USB_RX_BUF_SIZE];
|
||||
// volatile uint8_t usb_tx_buffer[USB_TX_BUF_SIZE];
|
||||
volatile int usb_rx_head = 0;
|
||||
// volatile int usb_tx_head = 0;
|
||||
volatile int usb_rx_tail = 0;
|
||||
// volatile int usb_tx_tail = 0;
|
||||
volatile uint8_t usb_tx_buffer[USB_TX_BUF_SIZE];
|
||||
volatile uint32_t usb_rx_head = 0;
|
||||
volatile uint32_t usb_tx_head = 0;
|
||||
volatile uint32_t usb_rx_tail = 0;
|
||||
volatile uint32_t usb_tx_tail = 0;
|
||||
|
||||
static uint8_t cdc_line_coding[7] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08};
|
||||
|
||||
@@ -868,116 +879,20 @@ static void gpib_dump_state(const char* context) {
|
||||
#define gpib_dump_state(x) ((void)0)
|
||||
#endif
|
||||
|
||||
// low level
|
||||
inline static void gpib_calc_lut(void) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint32_t bshr = 0;
|
||||
// bits 0-7 mappings
|
||||
if (i & 0x01)
|
||||
bshr |= (1U << (16 + PIN_DIO1));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO1);
|
||||
if (i & 0x02)
|
||||
bshr |= (1U << (16 + PIN_DIO2));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO2);
|
||||
if (i & 0x04)
|
||||
bshr |= (1U << (16 + PIN_DIO3));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO3);
|
||||
if (i & 0x08)
|
||||
bshr |= (1U << (16 + PIN_DIO4));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO4);
|
||||
if (i & 0x10)
|
||||
bshr |= (1U << (16 + PIN_DIO5));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO5);
|
||||
if (i & 0x20)
|
||||
bshr |= (1U << (16 + PIN_DIO6));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO6);
|
||||
if (i & 0x40)
|
||||
bshr |= (1U << (16 + PIN_DIO7));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO7);
|
||||
if (i & 0x80)
|
||||
bshr |= (1U << (16 + PIN_DIO8));
|
||||
else
|
||||
bshr |= (1U << PIN_DIO8);
|
||||
void gpib_write_data(uint8_t b) { GPIOB->BSHR = gpib_write_lut[b]; }
|
||||
|
||||
gpib_write_lut[i] = bshr;
|
||||
}
|
||||
}
|
||||
|
||||
// void gpib_write_data(uint8_t b) { GPIOB->BSHR = gpib_write_lut[b]; }
|
||||
|
||||
void gpib_write_data(uint8_t b) {
|
||||
uint32_t bshr = 0;
|
||||
|
||||
if (b & 0x01)
|
||||
bshr |= (MASK_DIO1 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO1;
|
||||
if (b & 0x02)
|
||||
bshr |= (MASK_DIO2 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO2;
|
||||
if (b & 0x04)
|
||||
bshr |= (MASK_DIO3 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO3;
|
||||
if (b & 0x08)
|
||||
bshr |= (MASK_DIO4 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO4;
|
||||
if (b & 0x10)
|
||||
bshr |= (MASK_DIO5 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO5;
|
||||
if (b & 0x20)
|
||||
bshr |= (MASK_DIO6 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO6;
|
||||
if (b & 0x40)
|
||||
bshr |= (MASK_DIO7 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO7;
|
||||
if (b & 0x80)
|
||||
bshr |= (MASK_DIO8 << 16);
|
||||
else
|
||||
bshr |= MASK_DIO8;
|
||||
|
||||
GPIOB->BSHR = bshr;
|
||||
}
|
||||
|
||||
// uint8_t gpib_read_data(void) {
|
||||
// // read all 16 pins, invert (gpib is active low)
|
||||
// uint32_t r = ~(GPIOB->INDR);
|
||||
// uint8_t b = 0;
|
||||
|
||||
// // parallel extraction
|
||||
// b |= (r >> SHIFT_DIO1) & MASK_GROUP_A; // handles D1 & D6
|
||||
// b |= (r >> SHIFT_DIO2) & MASK_GROUP_B; // handles D2 & D7
|
||||
// b |= (r >> SHIFT_DIO3) & (1 << 2);
|
||||
// b |= (r >> SHIFT_DIO4) & (1 << 3);
|
||||
// b |= (r >> SHIFT_DIO5) & (1 << 4);
|
||||
// b |= (r >> SHIFT_DIO8) & (1 << 7);
|
||||
|
||||
// return b;
|
||||
// }
|
||||
uint8_t gpib_read_data(void) {
|
||||
uint32_t r = ~(GPIOB->INDR); // active low
|
||||
// read all 16 pins, invert (gpib is active low)
|
||||
uint32_t r = ~(GPIOB->INDR);
|
||||
uint8_t b = 0;
|
||||
|
||||
if (r & MASK_DIO1) b |= 0x01;
|
||||
if (r & MASK_DIO2) b |= 0x02;
|
||||
if (r & MASK_DIO3) b |= 0x04;
|
||||
if (r & MASK_DIO4) b |= 0x08;
|
||||
if (r & MASK_DIO5) b |= 0x10;
|
||||
if (r & MASK_DIO6) b |= 0x20;
|
||||
if (r & MASK_DIO7) b |= 0x40;
|
||||
if (r & MASK_DIO8) b |= 0x80;
|
||||
// parallel extraction
|
||||
b |= (r >> SHIFT_GRP_9) & MASK_GRP_9; // handles D1 & D6
|
||||
b |= (r >> SHIFT_GRP_7) & MASK_GRP_7; // handles D2 & D7
|
||||
b |= (r >> SHIFT_D3) & (1 << 2);
|
||||
b |= (r >> SHIFT_D4) & (1 << 3);
|
||||
b |= (r >> SHIFT_D5) & (1 << 4);
|
||||
b |= (r >> SHIFT_D8) & (1 << 7);
|
||||
|
||||
return b;
|
||||
}
|
||||
@@ -1055,7 +970,7 @@ int gpib_read_byte(uint8_t* data, int* eoi_asserted) {
|
||||
// float data lines
|
||||
gpib_write_data(0x00);
|
||||
|
||||
Delay_Us(10);
|
||||
// Delay_Us(10);
|
||||
|
||||
// signal ready for data
|
||||
GPIB_RELEASE(PIN_NRFD);
|
||||
@@ -1094,7 +1009,7 @@ int gpib_read_byte(uint8_t* data, int* eoi_asserted) {
|
||||
int gpib_start_session(uint8_t target_addr, session_mode_t mode) {
|
||||
printf("gpib_start_session\n");
|
||||
GPIB_ASSERT(PIN_ATN);
|
||||
Delay_Us(20);
|
||||
Delay_Us(1);
|
||||
|
||||
// Unlisten everyone first to clear bus state
|
||||
if (gpib_write_byte(GPIB_CMD_UNL, 0) < 0) {
|
||||
@@ -1116,9 +1031,7 @@ int gpib_start_session(uint8_t target_addr, session_mode_t mode) {
|
||||
GPIB_ASSERT(PIN_NRFD); // Drive LOW (Not Ready)
|
||||
}
|
||||
|
||||
Delay_Us(10);
|
||||
GPIB_RELEASE(PIN_ATN); // Switch to Data Mode
|
||||
Delay_Us(10);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@@ -1131,9 +1044,8 @@ err:
|
||||
// Assert Interface Clear (IFC)
|
||||
void gpib_interface_clear(void) {
|
||||
GPIB_ASSERT(PIN_IFC);
|
||||
Delay_Ms(1); // IEEE-488 requires >100us
|
||||
Delay_Us(150); // IEEE-488 requires >100us
|
||||
GPIB_RELEASE(PIN_IFC);
|
||||
Delay_Ms(1);
|
||||
}
|
||||
|
||||
// Control Remote Enable (REN)
|
||||
@@ -1161,7 +1073,6 @@ int gpib_universal_clear(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Delay_Us(10);
|
||||
GPIB_RELEASE(PIN_ATN);
|
||||
return 0;
|
||||
}
|
||||
@@ -1239,9 +1150,8 @@ err:
|
||||
// Serial Poll
|
||||
// Reads the Status Byte (STB) from the device
|
||||
int gpib_serial_poll(uint8_t addr, uint8_t* status) {
|
||||
printf("gpib_serial_poll :: entry\n");
|
||||
GPIB_ASSERT(PIN_ATN);
|
||||
Delay_Us(20);
|
||||
Delay_Us(1);
|
||||
|
||||
// setupo seq: UNL -> SPE -> LAD(Me) -> TAD(Target)
|
||||
if (gpib_write_byte(GPIB_CMD_UNL, 0) < 0) goto err;
|
||||
@@ -1249,29 +1159,23 @@ int gpib_serial_poll(uint8_t addr, uint8_t* status) {
|
||||
if (gpib_write_byte(GPIB_CMD_LAD | MY_ADDR, 0) < 0) goto err;
|
||||
if (gpib_write_byte(GPIB_CMD_TAD | addr, 0) < 0) goto err;
|
||||
|
||||
printf("gpib_serial_poll :: gpib_write_data(0x00); \n");
|
||||
|
||||
gpib_write_data(0x00); // Float data lines
|
||||
GPIB_ASSERT(PIN_NDAC); // Busy / Not Accepted
|
||||
GPIB_ASSERT(PIN_NRFD); // Busy / Not Ready
|
||||
Delay_Us(5);
|
||||
// gpib_write_data(0x00); // Float data lines
|
||||
// GPIB_ASSERT(PIN_NDAC); // Busy / Not Accepted
|
||||
// GPIB_ASSERT(PIN_NRFD); // Busy / Not Ready
|
||||
// Delay_Us(5);
|
||||
|
||||
// drop ATN to read data
|
||||
GPIB_RELEASE(PIN_ATN);
|
||||
Delay_Us(5);
|
||||
|
||||
int eoi;
|
||||
int res = gpib_read_byte(status, &eoi);
|
||||
|
||||
printf("gpib_serial_poll :: after handshake (res: %d)\n", res);
|
||||
|
||||
// handshake complete, clean up lines
|
||||
GPIB_RELEASE(PIN_NRFD);
|
||||
GPIB_RELEASE(PIN_NDAC);
|
||||
|
||||
// end seq: ATN -> SPD -> UNT
|
||||
GPIB_ASSERT(PIN_ATN);
|
||||
Delay_Us(5);
|
||||
gpib_write_byte(GPIB_CMD_SPD, 0); // disable spoll
|
||||
gpib_write_byte(GPIB_CMD_UNT, 0); // untalk
|
||||
GPIB_RELEASE(PIN_ATN);
|
||||
@@ -1443,7 +1347,7 @@ void gpib_init(void) {
|
||||
funPinMode(PIN_SRQ, GPIO_CNF_IN_PUPD);
|
||||
funDigitalWrite(PIN_SRQ, 1);
|
||||
|
||||
// release all control lines to idle (HIGH)
|
||||
// float all control lines
|
||||
GPIB_RELEASE(PIN_EOI);
|
||||
GPIB_RELEASE(PIN_REN);
|
||||
GPIB_RELEASE(PIN_ATN);
|
||||
@@ -1463,29 +1367,16 @@ void gpib_init(void) {
|
||||
funPinMode(PIN_DIO8, GPIO_Speed_50MHz | GPIO_CNF_OUT_OD);
|
||||
|
||||
// calculate BSHR for the DIO lines
|
||||
gpib_calc_lut();
|
||||
// for (int i = 0; i < 256; i++) {
|
||||
// gpib_write_lut[i] =
|
||||
// CALC_PIN_BSHR(i, 0, PIN_DIO1) | CALC_PIN_BSHR(i, 1, PIN_DIO2) |
|
||||
// CALC_PIN_BSHR(i, 2, PIN_DIO3) | CALC_PIN_BSHR(i, 3, PIN_DIO4) |
|
||||
// CALC_PIN_BSHR(i, 4, PIN_DIO5) | CALC_PIN_BSHR(i, 5, PIN_DIO6) |
|
||||
// CALC_PIN_BSHR(i, 6, PIN_DIO7) | CALC_PIN_BSHR(i, 7, PIN_DIO8);
|
||||
// }
|
||||
for (int i = 0; i < 256; i++) {
|
||||
gpib_write_lut[i] =
|
||||
CALC_PIN_BSHR(i, 0, PIN_POS_D1) | CALC_PIN_BSHR(i, 1, PIN_POS_D2) |
|
||||
CALC_PIN_BSHR(i, 2, PIN_POS_D3) | CALC_PIN_BSHR(i, 3, PIN_POS_D4) |
|
||||
CALC_PIN_BSHR(i, 4, PIN_POS_D5) | CALC_PIN_BSHR(i, 5, PIN_POS_D6) |
|
||||
CALC_PIN_BSHR(i, 6, PIN_POS_D7) | CALC_PIN_BSHR(i, 7, PIN_POS_D8);
|
||||
}
|
||||
|
||||
// float data lines (release to HIGH)
|
||||
// float data lines
|
||||
gpib_write_data(0x00);
|
||||
|
||||
#ifdef GPIB_DEBUG
|
||||
printf("[GPIB] Asserting IFC...\n");
|
||||
#endif
|
||||
|
||||
gpib_interface_clear();
|
||||
|
||||
#ifdef GPIB_DEBUG
|
||||
gpib_dump_state("INIT DONE");
|
||||
// if no device is connected: NRFD/NDAC/DAV should all be 1
|
||||
// if device is connected: NRFD/NDAC might be 0
|
||||
#endif
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
@@ -1598,127 +1489,128 @@ void HandleDataOut(struct _USBState* ctx, int endp, uint8_t* data, int len) {
|
||||
} else if (endp == 2) {
|
||||
// Copy to Ring Buffer
|
||||
for (int i = 0; i < len; i++) {
|
||||
int next_head = (usb_rx_head + 1) % USB_RX_BUF_SIZE;
|
||||
uint32_t next_head = (usb_rx_head + 1) & USB_RX_MASK;
|
||||
|
||||
if (next_head != usb_rx_tail) {
|
||||
usb_rx_buffer[usb_rx_head] = data[i];
|
||||
usb_rx_head = next_head;
|
||||
} else {
|
||||
// buf overflow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void usb_process_tx(void) {
|
||||
// if (!app.usb_online) return;
|
||||
|
||||
// // check HW Busy (endpoint 3)
|
||||
// if (USBFSCTX.USBFS_Endp_Busy[3]) return;
|
||||
// // check buffer empty
|
||||
// if (usb_tx_head == usb_tx_tail) return;
|
||||
|
||||
// // calc contiguous chunk size
|
||||
// int len;
|
||||
// if (usb_tx_head > usb_tx_tail) {
|
||||
// len = usb_tx_head - usb_tx_tail;
|
||||
// } else {
|
||||
// len = USB_TX_BUF_SIZE - usb_tx_tail;
|
||||
// }
|
||||
|
||||
// // cap at USB pkt size
|
||||
// if (len > 64) len = 64;
|
||||
|
||||
// // send to hw, with memcpy
|
||||
// USBFS_SendEndpointNEW(3, (uint8_t*)&usb_tx_buffer[usb_tx_tail], len, 1);
|
||||
// // advance tail
|
||||
// usb_tx_tail = (usb_tx_tail + len) % USB_TX_BUF_SIZE;
|
||||
// }
|
||||
|
||||
// void usb_send_text(const char* str) {
|
||||
// if (!app.usb_online) {
|
||||
// // if offline, just reset buffer
|
||||
// usb_tx_head = usb_tx_tail = 0;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// while (*str) {
|
||||
// int next = (usb_tx_head + 1) % USB_TX_BUF_SIZE;
|
||||
// while (next == usb_tx_tail) {
|
||||
// return; // we just drop it
|
||||
// }
|
||||
|
||||
// usb_tx_buffer[usb_tx_head] = *str++;
|
||||
// usb_tx_head = next;
|
||||
// }
|
||||
// }
|
||||
|
||||
static void usb_send_text(const char* str) {
|
||||
void usb_process_tx(void) {
|
||||
if (!app.usb_online) return;
|
||||
|
||||
int len = strlen(str);
|
||||
int pos = 0;
|
||||
// check hw busy (endp 3)
|
||||
if (USBFSCTX.USBFS_Endp_Busy[3]) return;
|
||||
// check buffer empty
|
||||
if (usb_tx_head == usb_tx_tail) return;
|
||||
|
||||
while (pos < len) {
|
||||
int chunk = len - pos;
|
||||
if (chunk > 64) chunk = 64;
|
||||
// calc contiguous chunk size
|
||||
uint32_t tail = usb_tx_tail;
|
||||
uint32_t head = usb_tx_head;
|
||||
|
||||
uint32_t start_time = millis();
|
||||
int sent = 0;
|
||||
// calc contiguous size linear from tail to end of arr or head
|
||||
int len;
|
||||
if (head > tail) {
|
||||
len = head - tail;
|
||||
} else {
|
||||
len = USB_TX_BUF_SIZE - tail;
|
||||
}
|
||||
|
||||
while ((millis() - start_time) < USB_SEND_TIMEOUT_MS) {
|
||||
int result = USBFS_SendEndpointNEW(3, (uint8_t*)(str + pos), chunk, 1);
|
||||
// cap at USB pkt size
|
||||
if (len > 64) len = 64;
|
||||
|
||||
if (result == 0) {
|
||||
sent = 1;
|
||||
break;
|
||||
// send to hw, with memcpy
|
||||
USBFS_SendEndpointNEW(3, (uint8_t*)&usb_tx_buffer[tail], len, 1);
|
||||
// advance tail
|
||||
usb_tx_tail = (tail + len) & USB_TX_MASK;
|
||||
}
|
||||
|
||||
void usb_send_text(const char* str) {
|
||||
if (!app.usb_online) {
|
||||
// if offline, just reset buffer
|
||||
usb_tx_head = usb_tx_tail = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
uint32_t next = (usb_tx_head + 1) & USB_TX_MASK;
|
||||
|
||||
// buffer full?
|
||||
if (next == usb_tx_tail) {
|
||||
usb_process_tx();
|
||||
|
||||
uint32_t timeout = USB_TIMEOUT_LIMIT; // ~5ms
|
||||
|
||||
while (next == usb_tx_tail) {
|
||||
// this *should* be set by the ISR, so can exit immediately
|
||||
if (!USB_HW_IS_ACTIVE()) return;
|
||||
if (--timeout == 0) {
|
||||
return; // give up and drop
|
||||
}
|
||||
|
||||
usb_process_tx();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sent) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos += chunk;
|
||||
usb_tx_buffer[usb_tx_head] = *str++;
|
||||
usb_tx_head = next;
|
||||
}
|
||||
|
||||
// tx rightg away
|
||||
usb_process_tx();
|
||||
}
|
||||
|
||||
// pull a line from ring buffer
|
||||
int get_start_command(char* dest_buf, int max_len) {
|
||||
if (usb_rx_head == usb_rx_tail) return 0;
|
||||
uint32_t head = usb_rx_head;
|
||||
uint32_t tail = usb_rx_tail;
|
||||
|
||||
if (head == tail) return 0;
|
||||
|
||||
int temp_tail = usb_rx_tail;
|
||||
int len = 0;
|
||||
int found_newline = 0;
|
||||
uint32_t scan = tail;
|
||||
|
||||
// Peek for newline
|
||||
while (temp_tail != usb_rx_head) {
|
||||
char c = usb_rx_buffer[temp_tail];
|
||||
while (scan != head) {
|
||||
char c = usb_rx_buffer[scan];
|
||||
if (c == '\n' || c == '\r') {
|
||||
found_newline = 1;
|
||||
break;
|
||||
}
|
||||
temp_tail = (temp_tail + 1) % USB_RX_BUF_SIZE;
|
||||
len++;
|
||||
if (len >= max_len - 1) break;
|
||||
scan = (scan + 1) & USB_RX_MASK;
|
||||
|
||||
if (++len >= max_len - 1) break;
|
||||
}
|
||||
|
||||
if (found_newline) {
|
||||
// copy out
|
||||
for (int i = 0; i < len; i++) {
|
||||
dest_buf[i] = usb_rx_buffer[usb_rx_tail];
|
||||
usb_rx_tail = (usb_rx_tail + 1) % USB_RX_BUF_SIZE;
|
||||
dest_buf[i] = usb_rx_buffer[tail];
|
||||
tail = (tail + 1) & USB_RX_MASK;
|
||||
}
|
||||
dest_buf[len] = 0;
|
||||
|
||||
// eat newline chars
|
||||
while (usb_rx_tail != usb_rx_head) {
|
||||
char c = usb_rx_buffer[usb_rx_tail];
|
||||
// eat limiters
|
||||
tail = scan;
|
||||
|
||||
while (tail != head) {
|
||||
char c = usb_rx_buffer[tail];
|
||||
if (c == '\r' || c == '\n') {
|
||||
usb_rx_tail = (usb_rx_tail + 1) % USB_RX_BUF_SIZE;
|
||||
tail = (tail + 1) & USB_RX_MASK;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update the global volatile tail
|
||||
usb_rx_tail = tail;
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2636,21 +2528,20 @@ static void cmd_help(void) {
|
||||
}
|
||||
|
||||
static void cmd_status(void) {
|
||||
int srq_is_active = gpib_check_srq();
|
||||
int srq = gpib_check_srq();
|
||||
|
||||
snprintf(scratch.cmd.fmt_buf, sizeof(scratch.cmd.fmt_buf),
|
||||
"Config:\r\n"
|
||||
" Addr: %d\r\n"
|
||||
" Timeout: %lu ms\r\n"
|
||||
" Auto Read: %d\r\n"
|
||||
" EOS Mode: %d (0:CRLF 1:CR 2:LF 3:N)\r\n"
|
||||
" EOR Mode: %d\r\n"
|
||||
" EOI Assert: %d\r\n"
|
||||
" EOT: %s (Char %d)\r\n"
|
||||
" SRQ Line: %d %s\r\n",
|
||||
"ADDR: %d\r\n"
|
||||
"TMO : %lu ms\r\n"
|
||||
"AUTO: %d\r\n"
|
||||
"EOS : %d\r\n"
|
||||
"EOR : %d\r\n"
|
||||
"EOI : %d\r\n"
|
||||
"EOT : %s (%d)\r\n"
|
||||
"SRQ : %d\r\n",
|
||||
app.target_addr, app.gpib_timeout_ms, app.auto_read, app.eos_mode,
|
||||
app.eor_mode, app.eoi_assert, app.eot_enable ? "ON" : "OFF",
|
||||
app.eot_char, srq_is_active, srq_is_active ? "(Active)" : "(Idle)");
|
||||
app.eot_char, srq);
|
||||
|
||||
usb_send_text(scratch.cmd.fmt_buf);
|
||||
}
|
||||
@@ -2992,6 +2883,7 @@ int main() {
|
||||
|
||||
// Buzzer setup
|
||||
buzzer_init();
|
||||
play_startup_tune();
|
||||
|
||||
// I2C sensor
|
||||
i2c_init();
|
||||
@@ -3005,20 +2897,18 @@ int main() {
|
||||
USBFSSetup();
|
||||
// usb_debug = 1;
|
||||
|
||||
// play_startup_tune();
|
||||
|
||||
// app state
|
||||
app.usb_raw_prev = USB_HW_IS_ACTIVE();
|
||||
// init timers
|
||||
uint32_t now = millis();
|
||||
app.usb_ts = now;
|
||||
app.ignore_input_start_ts = now - 2000; /* Allow input immediately */
|
||||
app.ignore_input_start_ts = now - 2000;
|
||||
app.last_poll_time = 0;
|
||||
app.env_last_read = 0;
|
||||
|
||||
while (1) {
|
||||
handle_usb_state();
|
||||
// usb_process_tx();
|
||||
usb_process_tx();
|
||||
app_loop();
|
||||
handle_env_sensor();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user