diff --git a/NANOVNA_STM32_F072/board.h b/NANOVNA_STM32_F072/board.h index b0a3c1a..b1fa657 100644 --- a/NANOVNA_STM32_F072/board.h +++ b/NANOVNA_STM32_F072/board.h @@ -56,8 +56,8 @@ #define GPIOA_XP 6 #define GPIOA_YP 7 #define GPIOA_MCO 8 -#define GPIOA_TX 9 -#define GPIOA_USB_DISC 10 +#define GPIOA_USART1_TX 9 +#define GPIOA_USART1_RX 10 #define GPIOA_USB_DM 11 #define GPIOA_USB_DP 12 #define GPIOA_JTMS 13 @@ -117,6 +117,8 @@ * GPIOA setup: * * PA8 - MCO (alternate 0). + * PA9 - USART1_TX (alternate 1). + * PA10 - USART1_RX (alternate 1). * PA11 - USB_DM (alternate 14). * PA12 - USB_DP (alternate 14). * PA13 - SWDIO (alternate 0). @@ -131,8 +133,8 @@ PIN_MODE_ANALOG(GPIOA_XP) | \ PIN_MODE_ANALOG(GPIOA_YP) | \ PIN_MODE_ALTERNATE(GPIOA_MCO) | \ - PIN_MODE_ANALOG(9U) | \ - PIN_MODE_ANALOG(GPIOA_USB_DISC) | \ + PIN_MODE_ALTERNATE(GPIOA_USART1_TX) | \ + PIN_MODE_ALTERNATE(GPIOA_USART1_RX) | \ PIN_MODE_INPUT(GPIOA_USB_DM) | \ PIN_MODE_INPUT(GPIOA_USB_DP) | \ PIN_MODE_ALTERNATE(GPIOA_JTMS) | \ @@ -147,8 +149,8 @@ PIN_OTYPE_PUSHPULL(6U) | \ PIN_OTYPE_PUSHPULL(7U) | \ PIN_OTYPE_PUSHPULL(GPIOA_MCO) | \ - PIN_OTYPE_PUSHPULL(9U) | \ - PIN_OTYPE_PUSHPULL(GPIOA_USB_DISC) | \ + PIN_OTYPE_PUSHPULL(GPIOA_USART1_TX) | \ + PIN_OTYPE_PUSHPULL(GPIOA_USART1_RX) | \ PIN_OTYPE_PUSHPULL(GPIOA_USB_DM) | \ PIN_OTYPE_PUSHPULL(GPIOA_USB_DP) | \ PIN_OTYPE_PUSHPULL(GPIOA_JTMS) | \ @@ -163,8 +165,8 @@ PIN_OSPEED_2M(6) | \ PIN_OSPEED_2M(7) | \ PIN_OSPEED_100M(GPIOA_MCO) | \ - PIN_OSPEED_2M(9) | \ - PIN_OSPEED_2M(10) | \ + PIN_OSPEED_100M(GPIOA_USART1_TX) | \ + PIN_OSPEED_100M(GPIOA_USART1_RX) | \ PIN_OSPEED_100M(GPIOA_USB_DM) | \ PIN_OSPEED_100M(GPIOA_USB_DP) | \ PIN_OSPEED_100M(GPIOA_JTMS) | \ @@ -179,8 +181,8 @@ PIN_PUPDR_FLOATING(6) | \ PIN_PUPDR_FLOATING(7) | \ PIN_PUPDR_PULLUP(GPIOA_MCO) | \ - PIN_PUPDR_FLOATING(9) | \ - PIN_PUPDR_FLOATING(GPIOA_USB_DISC) | \ + PIN_PUPDR_FLOATING(GPIOA_USART1_TX) | \ + PIN_PUPDR_FLOATING(GPIOA_USART1_RX) | \ PIN_PUPDR_FLOATING(GPIOA_USB_DM) | \ PIN_PUPDR_FLOATING(GPIOA_USB_DP) | \ PIN_PUPDR_PULLDOWN(GPIOA_JTMS) | \ @@ -195,8 +197,8 @@ PIN_ODR_HIGH(6) | \ PIN_ODR_HIGH(7) | \ PIN_ODR_HIGH(GPIOA_MCO) | \ - PIN_ODR_HIGH(9) | \ - PIN_ODR_HIGH(GPIOA_USB_DISC) | \ + PIN_ODR_LOW(GPIOA_USART1_TX) | \ + PIN_ODR_LOW(GPIOA_USART1_RX) | \ PIN_ODR_HIGH(GPIOA_USB_DM) | \ PIN_ODR_HIGH(GPIOA_USB_DP) | \ PIN_ODR_HIGH(GPIOA_JTMS) | \ @@ -211,8 +213,8 @@ PIN_AFIO_AF(6, 0) | \ PIN_AFIO_AF(7, 0)) #define VAL_GPIOA_AFRH (PIN_AFIO_AF(GPIOA_MCO, 0) | \ - PIN_AFIO_AF(9, 0) | \ - PIN_AFIO_AF(GPIOA_USB_DISC, 0) | \ + PIN_AFIO_AF(GPIOA_USART1_TX, 1) | \ + PIN_AFIO_AF(GPIOA_USART1_RX, 1) | \ PIN_AFIO_AF(GPIOA_USB_DM, 0) | \ PIN_AFIO_AF(GPIOA_USB_DP, 0) | \ PIN_AFIO_AF(GPIOA_JTMS, 0) | \ diff --git a/halconf.h b/halconf.h index f939a50..eda1857 100644 --- a/halconf.h +++ b/halconf.h @@ -136,7 +136,7 @@ * @brief Enables the SERIAL subsystem. */ #if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__) -#define HAL_USE_SERIAL FALSE +#define HAL_USE_SERIAL TRUE #endif /** @@ -302,7 +302,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 64 #endif /*===========================================================================*/ @@ -325,7 +325,7 @@ * @note The default is 2 buffers. */ #if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__) -#define SERIAL_USB_BUFFERS_NUMBER 1 +#define SERIAL_USB_BUFFERS_NUMBER 2 #endif /*===========================================================================*/ diff --git a/main.c b/main.c index d43d65d..3fdb86b 100644 --- a/main.c +++ b/main.c @@ -50,7 +50,7 @@ int32_t frequencyExtra; // enable this need reduce spi_buffer size, by default shell run in main thread // #define VNA_SHELL_THREAD -static BaseSequentialStream *shell_stream = (BaseSequentialStream *)&SDU1; +static BaseSequentialStream *shell_stream; // Shell new line #define VNA_SHELL_NEWLINE_STR "\r\n" @@ -83,6 +83,7 @@ static volatile vna_shellcmd_t shell_function = 0; #define ENABLE_INFO_COMMAND // Enable color command, allow change config color for traces, grid, menu #define ENABLE_COLOR_COMMAND +#define ENABLE_USART_COMMAND #ifdef __VNA__ static void apply_error_term_at(int i); static void apply_edelay_at(int i); @@ -332,6 +333,18 @@ int shell_printf(const char *fmt, ...) return formatted_bytes; } +#ifdef __USE_SERIAL_CONSOLE__ +// Serial Shell commands output +int shell_serial_printf(const char *fmt, ...) +{ + va_list ap; + int formatted_bytes; + va_start(ap, fmt); + formatted_bytes = chvprintf(&SD1, fmt, ap); + va_end(ap); + return formatted_bytes; +} +#endif VNA_SHELL_FUNCTION(cmd_pause) { (void)argc; @@ -857,7 +870,8 @@ config_t config = { .trace_color = { DEFAULT_TRACE_1_COLOR, DEFAULT_TRACE_2_COLOR, DEFAULT_TRACE_3_COLOR}, // .touch_cal = { 693, 605, 124, 171 }, // 2.4 inch LCD panel .touch_cal = { 347, 495, 160, 205 }, // 2.8 inch LCD panel - .freq_mode = FREQ_MODE_START_STOP, + ._mode = _MODE_USB, + ._serial_speed = USART_SPEED_SETTING(SERIAL_DEFAULT_BITRATE), #ifdef __VNA__ .harmonic_freq_threshold = 300000000, #endif @@ -2292,6 +2306,20 @@ VNA_SHELL_FUNCTION(cmd_threads) } #endif +#ifdef ENABLE_USART_COMMAND +VNA_SHELL_FUNCTION(cmd_usart) +{ + uint32_t time = 2000; // 200ms wait answer by default + if (argc == 0 || argc > 2 || (config._mode & _MODE_SERIAL)) return; + if (argc == 2) time = my_atoui(argv[1])*10; + sdWriteTimeout(&SD1, (uint8_t *)argv[0], strlen(argv[0]), time); + sdWriteTimeout(&SD1, (uint8_t *)VNA_SHELL_NEWLINE_STR, sizeof(VNA_SHELL_NEWLINE_STR)-1, time); + uint32_t size; + uint8_t buffer[64]; + while ((size = sdReadTimeout(&SD1, buffer, sizeof(buffer), time))) + streamWrite(&SDU1, buffer, size); +} +#endif #include "sa_cmd.c" //============================================================================= @@ -2351,6 +2379,9 @@ static const VNAShellCommand commands[] = {"trace" , cmd_trace , CMD_WAIT_MUTEX}, {"trigger" , cmd_trigger , 0}, {"marker" , cmd_marker , 0}, +#ifdef ENABLE_USART_COMMAND + {"usart" , cmd_usart , CMD_WAIT_MUTEX}, +#endif #ifdef __VNA__ {"edelay" , cmd_edelay , 0}, #endif @@ -2426,6 +2457,110 @@ VNA_SHELL_FUNCTION(cmd_help) /* * VNA shell functions */ +// Check Serial connection requirements +#ifdef __USE_SERIAL_CONSOLE__ +#if HAL_USE_SERIAL == FALSE +#error "For serial console need HAL_USE_SERIAL as TRUE in halconf.h" +#endif + +// Before start process command from shell, need select input stream +#define PREPARE_STREAM shell_stream = (config._mode&_MODE_SERIAL) ? (BaseSequentialStream *)&SD1 : (BaseSequentialStream *)&SDU1; + +// Update Serial connection speed and settings +void shell_update_speed(void){ + // Update Serial speed settings + SerialConfig s_config = {USART_GET_SPEED(config._serial_speed), 0, USART_CR2_STOP1_BITS, 0 }; + sdStop(&SD1); + sdStart(&SD1, &s_config); // USART config +} + +// Check USB connection status +static bool usb_IsActive(void){ + return usbGetDriverStateI(&USBD1) == USB_ACTIVE; +} +void shell_reset_console(void){ + // Reset I/O queue over USB (for USB need also connect/disconnect) + if (usb_IsActive()){ + if (config._mode & _MODE_SERIAL) + sduDisconnectI(&SDU1); + else + sduConfigureHookI(&SDU1); + } + // Reset I/O queue over Serial + oqResetI(&SD1.oqueue); + iqResetI(&SD1.iqueue); +} + +// Check active connection for Shell +static bool shell_check_connect(void){ + // Serial connection always active + if (config._mode & _MODE_SERIAL) + return true; + // USB connection can be USB_SUSPENDED + return usb_IsActive(); +} + +static void shell_init_connection(void){ +/* + * Initializes and start serial-over-USB CDC driver SDU1, connected to USBD1 + */ + sduObjectInit(&SDU1); + sduStart(&SDU1, &serusbcfg); + +/* + * Set Serial speed settings for SD1 + */ + shell_update_speed(); + +/* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(&USBD1); + chThdSleepMilliseconds(100); + usbStart(&USBD1, &usbcfg); + usbConnectBus(&USBD1); + +/* + * Set I/O stream (SDU1 or SD1) for shell + */ + PREPARE_STREAM; +} + +#else +// Only USB console, shell_stream always on USB +#define PREPARE_STREAM + +// Check connection as Active, if no suspend input +static bool shell_check_connect(void){ + return SDU1.config->usbp->state == USB_ACTIVE; +} + +// Init shell I/O connection over USB +static void shell_init_connection(void){ +/* + * Initializes and start serial-over-USB CDC driver SDU1, connected to USBD1 + */ + sduObjectInit(&SDU1); + sduStart(&SDU1, &serusbcfg); + +/* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(&USBD1); + chThdSleepMilliseconds(100); + usbStart(&USBD1, &usbcfg); + usbConnectBus(&USBD1); + +/* + * Set I/O stream SDU1 for shell + */ + shell_stream = (BaseSequentialStream *)&SDU1; +} +#endif // // Read command line from shell_stream @@ -2435,6 +2570,8 @@ static int VNAShell_readLine(char *line, int max_size) // Read line from input stream uint8_t c; char *ptr = line; + // Prepare I/O for shell_stream + PREPARE_STREAM; while (1) { // Return 0 only if stream not active if (streamRead(shell_stream, &c, 1) == 0) @@ -2656,23 +2793,6 @@ int main(void) si5351_init(); #endif - // MCO on PA8 - //palSetPadMode(GPIOA, 8, PAL_MODE_ALTERNATE(0)); -/* - * Initializes a serial-over-USB CDC driver. - */ - sduObjectInit(&SDU1); - sduStart(&SDU1, &serusbcfg); -/* - * Activates the USB driver and then the USB bus pull-up on D+. - * Note, a delay is inserted in order to not have to disconnect the cable - * after a reset. - */ - usbDisconnectBus(serusbcfg.usbp); - chThdSleepMilliseconds(100); - usbStart(serusbcfg.usbp, &usbcfg); - usbConnectBus(serusbcfg.usbp); - #ifdef __SI4432__ /* * Powercycle the RF part to reset SI4432 @@ -2712,44 +2832,6 @@ int main(void) } #endif - -#if 0 - /* - * UART initialize - */ - uartStart(&UARTD1, &uart_cfg_1); -again: - uartStartSend(&UARTD1, 1, "H"); - uint8_t buf[10]; - uartStartReceive(&UARTD1, 1, buf); -goto again; -#endif - -#if 0 - again: - - palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(1)); // USART1 TX. - palSetPadMode(GPIOA,10, PAL_MODE_ALTERNATE(1)); // USART1 RX. - - - uint8_t buf[10]; - sdStart(&SD1,&default_config); - osalThreadSleepMilliseconds(10); - mySerialWrite("Hallo!?\n"); - - osalThreadSleepMilliseconds(10); - - mySerialReadline(buf, 10); - - sdReadTimeout(&SD1,buf,10, 10); - - sdWrite(&SD1,(const uint8_t *)"Test123",7); - osalThreadSleepMicroseconds(10); - sdReadTimeout(&SD1,buf,10,TIME_IMMEDIATE); - sdReadTimeout(&SD1,buf,10, 10); - int i = sdReadTimeout(&SD1,buf,10,TIME_IMMEDIATE); -goto again; -#endif #ifdef __ULTRA_SA__ ADF4351_Setup(); #endif @@ -2769,6 +2851,12 @@ goto again; if (caldata_recall(0) == -1) { load_default_properties(); } + +/* + * Init Shell console connection data (after load config for settings) + */ + shell_init_connection(); + /* restore frequencies and calibration 0 slot properties from flash memory */ #ifdef __VNA__ dac1cfg1.init = config.dac_value; diff --git a/mcuconf.h b/mcuconf.h index 1f626f0..ab7b855 100644 --- a/mcuconf.h +++ b/mcuconf.h @@ -175,7 +175,7 @@ /* * SERIAL driver system settings. */ -#define STM32_SERIAL_USE_USART1 FALSE +#define STM32_SERIAL_USE_USART1 TRUE #define STM32_SERIAL_USE_USART2 FALSE #define STM32_SERIAL_USART1_PRIORITY 3 #define STM32_SERIAL_USART2_PRIORITY 3 diff --git a/nanovna.h b/nanovna.h index 92ffd63..f320635 100644 --- a/nanovna.h +++ b/nanovna.h @@ -39,7 +39,7 @@ //#define __ULTRA__ // Add harmonics mode on low input. //#define __ULTRA_SA__ // Adds ADF4351 control for extra high 1st IF stage #define __SPUR__ // Does spur reduction by shifting IF - +#define __USE_SERIAL_CONSOLE__ // Enable serial I/O connection (need enable HAL_USE_SERIAL as TRUE in halconf.h) /* * main.c */ @@ -438,6 +438,11 @@ typedef struct trace { #define FREQ_MODE_CENTER_SPAN 0x1 #define FREQ_MODE_DOTTED_GRID 0x2 +// Connection flag +#define _MODE_CONNECTION_MASK 0x04 +#define _MODE_SERIAL 0x04 +#define _MODE_USB 0x00 + typedef struct config { int32_t magic; uint16_t dac_value; @@ -446,7 +451,8 @@ typedef struct config { uint16_t menu_active_color; uint16_t trace_color[TRACES_MAX]; int16_t touch_cal[4]; - int8_t freq_mode; + int8_t _mode; + uint32_t _serial_speed; #ifdef __VNA__ uint32_t harmonic_freq_threshold; #endif @@ -480,6 +486,20 @@ float get_trace_refpos(int t); const char *get_trace_typename(int t); extern int in_selftest; +// +// Shell config functions and macros +// Serial connect definitions not used if Serial mode disabled +// Minimum speed - USART_SPEED_MULTIPLIER +// Maximum speed - USART_SPEED_MULTIPLIER * 256 +// Can be: 19200, 38400, 57600, 76800, 115200, 230400, 460800, 921600, 1843200, 3686400 +#define USART_SPEED_MULTIPLIER 19200 +#define USART_SPEED_SETTING(speed) ((speed)/USART_SPEED_MULTIPLIER - 1) +#define USART_GET_SPEED(idx) (((idx) + 1) * USART_SPEED_MULTIPLIER) +void shell_update_speed(void); +void shell_reset_console(void); +int shell_serial_printf(const char *fmt, ...); + + #ifdef __VNA void set_electrical_delay(float picoseconds); float get_electrical_delay(void); diff --git a/ui_sa.c b/ui_sa.c index 1114588..2f4ebc8 100644 --- a/ui_sa.c +++ b/ui_sa.c @@ -587,30 +587,34 @@ static UI_FUNCTION_ADV_CALLBACK(menu_scanning_speed_acb) ui_mode_normal(); } +#define CONFIG_MENUITEM_TOUCH_CAL 0 +#define CONFIG_MENUITEM_TOUCH_TEST 1 +#define CONFIG_MENUITEM_SELFTEST 2 +#define CONFIG_MENUITEM_VERSION 3 static UI_FUNCTION_CALLBACK(menu_config_cb) { - (void)data; - switch (item) { - case 0: + (void)item; + switch (data) { + case CONFIG_MENUITEM_TOUCH_CAL: touch_cal_exec(); redraw_frame(); request_to_redraw_grid(); draw_menu(); break; - case 1: + case CONFIG_MENUITEM_TOUCH_TEST: touch_draw_test(); redraw_frame(); request_to_redraw_grid(); draw_menu(); break; - case 2: + case CONFIG_MENUITEM_SELFTEST: sweep_mode = 0; // Suspend sweep to save time menu_move_back_and_leave_ui(); setting.test = 0; setting.test_argument = 0; sweep_mode = SWEEP_SELFTEST; break; - case 4: + case CONFIG_MENUITEM_VERSION: show_version(); redraw_frame(); request_to_redraw_grid(); @@ -1263,6 +1267,33 @@ static UI_FUNCTION_ADV_CALLBACK(menu_points_acb){ draw_menu(); } +#ifdef __USE_SERIAL_CONSOLE__ +static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_acb) +{ + (void)item; + if (b){ + b->icon = config._serial_speed == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->param_1.u = USART_GET_SPEED(data); + return; + } + config._serial_speed = data; + shell_update_speed(); + draw_menu(); +} + +static UI_FUNCTION_ADV_CALLBACK(menu_connection_acb) +{ + (void)item; + if (b){ + b->icon = (config._mode&_MODE_CONNECTION_MASK) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + return; + } + config._mode&=~_MODE_CONNECTION_MASK; + config._mode|=data; + shell_reset_console(); + draw_menu(); +} +#endif // ===[MENU DEFINITION]========================================================= #if 0 static const menuitem_t menu_store_preset_high[8] = @@ -1676,12 +1707,57 @@ static const menuitem_t menu_calibrate[] = { MT_FORM | MT_NONE, 0, NULL, NULL } // sentinel }; +#ifdef __USE_SERIAL_CONSOLE__ +//19200, 38400, 57600, 74800, 115200, 230400, 460800, 921600, 1843200, 3686400 +#if 0 +const menuitem_t menu_serial_speed2[] = { + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 460800), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 921600), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING(1843200), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING(3686400), "%u", menu_serial_speed_acb }, + { MT_CANCEL, 0, S_LARROW" BACK", NULL }, + { MT_NONE, 0, NULL, NULL } // sentinel +}; +#endif + +const menuitem_t menu_serial_speed[] = { + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 19200), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 38400), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 57600), "%u", menu_serial_speed_acb }, +// { MT_ADV_CALLBACK, USART_SPEED_SETTING( 76800), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING(115200), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING(230400), "%u", menu_serial_speed_acb }, +// { MT_SUBMENU, 0, S_RARROW" MORE", menu_serial_speed2 }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 460800), "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, USART_SPEED_SETTING( 921600), "%u", menu_serial_speed_acb }, + { MT_CANCEL, 0, S_LARROW" BACK", NULL }, + { MT_NONE, 0, NULL, NULL } // sentinel +}; + +const menuitem_t menu_connection[] = { + { MT_ADV_CALLBACK, _MODE_USB, "USB", menu_connection_acb }, + { MT_ADV_CALLBACK, _MODE_SERIAL, "SERIAL", menu_connection_acb }, + { MT_SUBMENU, 0, "SERIAL\nSPEED", menu_serial_speed }, + { MT_CANCEL, 0, S_LARROW" BACK", NULL }, + { MT_NONE, 0, NULL, NULL } // sentinel +}; +#endif + +const menuitem_t menu_touch[] = { + { MT_CALLBACK, CONFIG_MENUITEM_TOUCH_CAL, "TOUCH CAL", menu_config_cb}, + { MT_CALLBACK, CONFIG_MENUITEM_TOUCH_TEST, "TOUCH TEST", menu_config_cb}, + { MT_CANCEL, 0, S_LARROW" BACK", NULL }, + { MT_NONE, 0, NULL, NULL } // sentinel +}; + static const menuitem_t menu_config[] = { - { MT_CALLBACK, 0, "TOUCH CAL", menu_config_cb}, - { MT_CALLBACK, 0, "TOUCH TEST", menu_config_cb}, - { MT_CALLBACK, 0, "SELF TEST", menu_config_cb}, - { MT_SUBMENU, 0, "LEVEL CAL", menu_calibrate}, - { MT_CALLBACK, 0, "VERSION", menu_config_cb}, + { MT_SUBMENU, 0, "TOUCH", menu_touch}, + { MT_CALLBACK, CONFIG_MENUITEM_SELFTEST, "SELF TEST", menu_config_cb}, + { MT_SUBMENU, 0, "LEVEL CAL", menu_calibrate}, + { MT_CALLBACK, CONFIG_MENUITEM_VERSION, "VERSION", menu_config_cb}, +#ifdef __USE_SERIAL_CONSOLE__ + { MT_SUBMENU, 0, "CONNECTION", menu_connection}, +#endif { MT_SUBMENU, 0, "EXPERT\nCONFIG", menu_settings}, { MT_SUBMENU, 0, S_RARROW" DFU", menu_dfu}, { MT_CANCEL, 0, S_LARROW" BACK", NULL },