diff --git a/changelog.txt b/changelog.txt index e364dc2..f3c0d0b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,12 @@ +V1.4284 2026-11-02 For all tinySA models +- Every tinySA has its own USB serial number (from the MCU unique ID, on the tinySA Ultra the same as its DFU serial number), so Windows can tell several tinySAs apart. Windows assigns a new COM port once +- The battery stays visible when the sweep is paused with the "pause" console command +- "ext_gain" without a value shows the current external gain +- A preset loaded from SD now also uses its RBW (Ultra only) +- Band names are stored in .bnd files and loaded from them (Ultra only) +- Self test: when there is no signal, "Cable connected?" is shown in the test list instead of a hint that was overwritten +- Version screen: copyright 2026, aligned thread table (Ultra only) + V1.4273 2026-11-02 For all tinySA Ultra models - The tinySA Ultra has its own USB IDs: VID 0x1781 PID 0x0A44 (was 0x0483 0x5740, the ST virtual COM port). Windows assigns a new COM port once, programs that look for the tinySA by its USB IDs need an update (tinySA-App accepts both) diff --git a/main.c b/main.c index fc8cd3b..f4cbcfb 100644 --- a/main.c +++ b/main.c @@ -96,7 +96,7 @@ uint16_t redraw_request = 0; // contains REDRAW_XXX flags // Version text, displayed in Config->Version menu, also send by info command const char * const info_about[]={ BOARD_NAME, - "2019-2024 Copyright @Erik Kaashoek", + "2019-2026 Copyright @Erik Kaashoek", "2016-2020 Copyright @edy555", "SW licensed under GPL. See: https://github.com/erikkaashoek/tinySA", "Version: " VERSION, diff --git a/sa_cmd.c b/sa_cmd.c index b4c7ce3..c09c070 100644 --- a/sa_cmd.c +++ b/sa_cmd.c @@ -398,7 +398,7 @@ VNA_SHELL_FUNCTION(cmd_sweeptime) VNA_SHELL_FUNCTION(cmd_ext_gain) { if (argc != 1 || argv[0][0] == '?') { - usage_printf("ext_gain -100.0..+100.0\r\n"); + usage_printf("ext_gain -100.0..+100.0\r\n%.1f\r\n", setting.external_gain); return; } float o = my_atof(argv[0]); diff --git a/sa_core.c b/sa_core.c index 0b54688..a38f77c 100644 --- a/sa_core.c +++ b/sa_core.c @@ -6476,6 +6476,7 @@ static const char *const test_text [4] = static const char *(test_fail_cause [TEST_COUNT]); static int test_status[TEST_COUNT]; static int show_test_info = FALSE; +static const char *test_hint; // last line of the test list while waiting for a touch static volatile int test_wait = false; static float test_value; @@ -6584,7 +6585,7 @@ void cell_draw_test_info(int x0, int y0) cell_printf(xpos, ypos, FONT_s"Self test status:"); } else if (test_case[i].kind == TC_END) { if (test_wait) - cell_printf(xpos, ypos, FONT_s"Touch screen to continue"); + cell_printf(xpos, ypos, FONT_s"%s", test_hint); continue; } else { cell_printf(xpos, ypos, FONT_s"Test %d: %s%s", i+1, test_fail_cause[i], test_text[test_status[i]] ); @@ -7153,6 +7154,7 @@ void selftest(int test) test_fail_cause[i] = ""; } show_test_info = TRUE; + test_hint = "Touch screen to continue"; test_step=0; test_step = setting.test_argument; if (test_step != 0) { @@ -7170,9 +7172,7 @@ void selftest(int test) if (test_step == 2) { if (peakLevel < -60) { test_step = TEST_END; - ili9341_set_foreground(LCD_BRIGHT_COLOR_RED); - ili9341_drawstring_7x13("Signal level too low", 30, 140); - ili9341_drawstring_7x13("Did you connect high and low ports with cable?", 0, 210); + test_hint = "Cable connected? Touch screen to continue"; // drawn in the list, a direct draw was overwritten goto resume2; } diff --git a/ui.c b/ui.c index a294d1e..e38ee8b 100644 --- a/ui.c +++ b/ui.c @@ -707,10 +707,12 @@ extern const char *states[]; #else uint32_t stklimit = 0U; #endif - lcd_printf(x, y+=bFONT_STR_HEIGHT, "%08x|%08x|%08x|%08x|%4u|%4u|%9s|%12s", + // The font is proportional (a space is narrower than a digit), padding with spaces does not align: + // zero padded numbers, the name after them, the state at a fixed position + lcd_printf(x, y+=bFONT_STR_HEIGHT, "%08x|%08x|%08x|%08x|%u|%03u|%s", stklimit, (uint32_t)tp->ctx.sp, max_stack_use, (uint32_t)tp, - (uint32_t)tp->refs - 1, (uint32_t)tp->prio, states[tp->state], - tp->name == NULL ? "" : tp->name); + (uint32_t)tp->refs - 1, (uint32_t)tp->prio, tp->name == NULL ? "" : tp->name); + lcd_printf(x + 330, y, "%s", states[tp->state]); tp = chRegNextThread(tp); } while (tp != NULL); #endif @@ -6495,6 +6497,7 @@ redraw_cal_status: #endif return; } + redraw_request |= REDRAW_BATTERY; // the fill erased it, while paused no completed sweep redraws it // if (current_menu_is_form() && !in_selftest) // return; @@ -8448,8 +8451,7 @@ static void sa_save_file(uint8_t format) { char *buf = (char *)spi_buffer; buf += plot_printf(buf, 100, "%U, ", setting.bands[i].start); buf += plot_printf(buf, 100, "%U, ", setting.bands[i].end); - buf += plot_printf(buf, 100, "%f ", setting.bands[i].level); - buf += plot_printf(buf, 100, "\r\n"); + buf += plot_printf(buf, 100, "%f, \"%s\"\r\n", setting.bands[i].level, setting.bands[i].name); // name: older firmware ignores it res = f_write(fs_file, (char *)spi_buffer, buf - (char *)spi_buffer, &size); } } diff --git a/usbcfg.c b/usbcfg.c index 928a4ec..7333816 100644 --- a/usbcfg.c +++ b/usbcfg.c @@ -248,74 +248,28 @@ static const uint8_t vcom_string2[] = { }; #endif -#ifdef TINYSA4 /* - * Serial Number string. VERSION = 'tinySA4_v1.3-nnn-gxxxxxxx' - * 01234567890123456789012 - * skip last two 'xx' char due to 'tinySA4_v1.3-n-gxxxxxxx' + * Serial Number string: 12 hex digits made from the MCU unique ID the way the STM32F303 DFU + * bootloader makes its serial number (UID0 + UID2, then the upper half of UID2), so every tinySA + * has its own. It was the kernel version "400" on all of them: Windows could not tell two units + * apart and gave one unit's COM port to the other. Filled at every USB reset, before it is requested. */ -static const uint8_t vcom_string3[] = +static uint8_t vcom_string3[2 + 2 * 12]; // the upper bytes of the UTF-16 characters stay 0 + +static void fill_serial_string(void) { -#if 1 - USB_DESC_BYTE(8), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - '0' + CH_KERNEL_MAJOR, 0, - '0' + CH_KERNEL_MINOR, 0, - '0' + CH_KERNEL_PATCH, 0 -#else - USB_DESC_BYTE(32), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - VERSION[8], 0, /* 'v' */ - VERSION[9], 0, /* '1' */ - VERSION[10], 0, /* '.' */ - VERSION[11], 0, /* '3' */ - VERSION[12], 0, /* '-' */ - VERSION[13], 0, /* 'n' */ - VERSION[14], 0, /* 'n' */ - VERSION[15], 0, /* 'n' */ - VERSION[16], 0, /* '-' */ - VERSION[17], 0, /* 'g' */ - VERSION[18], 0, /* 'x' */ - VERSION[19], 0, /* 'x' */ - VERSION[20], 0, /* 'x' */ - VERSION[21], 0, /* 'x' */ - VERSION[22], 0, /* 'x' */ -#endif -}; -#else -/* - * Serial Number string. VERSION = 'tinySA_v1.3-nnn-gxxxxxxx' - * 0123456789012345678901 - * skip last two 'xx' char due to 'tinySA_v1.3-n-gxxxxxxx' - */ -static const uint8_t vcom_string3[] = { -#if 1 - USB_DESC_BYTE(8), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - '0' + CH_KERNEL_MAJOR, 0, - '0' + CH_KERNEL_MINOR, 0, - '0' + CH_KERNEL_PATCH, 0 -#else - USB_DESC_BYTE(32), /* bLength. */ - USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */ - VERSION[7], 0, /* 'v' */ - VERSION[8], 0, /* '1' */ - VERSION[9], 0, /* '.' */ - VERSION[10], 0, /* '3' */ - VERSION[11], 0, /* '-' */ - VERSION[12], 0, /* 'n' */ - VERSION[13], 0, /* 'n' */ - VERSION[14], 0, /* 'n' */ - VERSION[15], 0, /* '-' */ - VERSION[16], 0, /* 'g' */ - VERSION[17], 0, /* 'x' */ - VERSION[18], 0, /* 'x' */ - VERSION[19], 0, /* 'x' */ - VERSION[20], 0, /* 'x' */ - VERSION[21], 0, /* 'x' */ -#endif -}; -#endif + const uint32_t *uid = (const uint32_t *)UID_BASE; + uint32_t v = uid[0] + uid[2]; + vcom_string3[0] = sizeof vcom_string3; + vcom_string3[1] = USB_DESCRIPTOR_STRING; + for (int i = 2; i < (int)sizeof vcom_string3; i += 2) { + if (i == 2 + 2 * 8) // 8 digits done: then the upper half of UID2 + v = uid[2]; + uint8_t c = v >> 28; + v <<= 4; + vcom_string3[i] = c + (c < 10 ? '0' : 'A' - 10); + } +} /* * Strings wrappers array. @@ -406,6 +360,7 @@ static void usb_event(USBDriver *usbp, usbevent_t event) { switch (event) { case USB_EVENT_RESET: + fill_serial_string(); #ifdef __USE_USB_MSC__ { /* usb_lld_reset() has just allocated the EP0 buffers.*/ diff --git a/vna_browser.c b/vna_browser.c index df9c56e..f991d1d 100644 --- a/vna_browser.c +++ b/vna_browser.c @@ -177,11 +177,13 @@ finish2: line[j] = 0; j = 0; char *args[16]; int nargs = parse_line(line, args, 16); // Parse line to 16 args - if (nargs < 2 || args[0][0] == '#' || args[0][0] == '!') continue; // No data or comment or settings + if (nargs < 3 || args[0][0] == '#' || args[0][0] == '!') continue; // No data or comment or settings if (count >= BANDS_MAX) {error = "Format err"; goto finish3;} setting.bands[count].start = my_atoui(args[0]);// Get frequency setting.bands[count].end = my_atoui(args[1]);// Get frequency setting.bands[count].level = my_atof(args[2]); // Get frequency + if (nargs > 3) // Name, files of older firmware have none + plot_printf(setting.bands[count].name, BAND_NAME_SIZE, "%s", args[3]); setting.bands[count].enabled = true; // Get frequency count++; } @@ -334,6 +336,7 @@ static char cmd_buffer[256+128]; set_scale(setting.scale); set_reflevel(setting.reflevel); set_level_meter_or_waterfall(); + update_rbw(); // as caldata_recall(), else the RBW of the loaded preset is not used // Remember where this preset came from so storing it back can default to it plot_printf(sd_preset_path, sizeof(sd_preset_path), "%s", fno.fname); }