diff --git a/FatFs/ff.c b/FatFs/ff.c index b658786..5b007e8 100644 --- a/FatFs/ff.c +++ b/FatFs/ff.c @@ -623,6 +623,28 @@ static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); #define st_dword(ptr, val) *((uint32_t*)(ptr)) = ((uint32_t)(val)) #define st_qword(ptr, val) *((uint64_t*)(ptr)) = ((uint64_t)(val)) +#elif _WORD_ACCESS == 2 +// Compiler auto detect where need byte or word access optimisation +struct __attribute__((packed)) T_UINT16 { uint16_t v; }; +struct __attribute__((packed)) T_UINT32 { uint32_t v; }; +struct __attribute__((packed)) T_UINT64 { uint64_t v; }; +// Load a 2-byte little-endian word +static WORD ld_word (const BYTE* ptr) {return (((const struct T_UINT16 *)(const void *)(ptr))->v);} +// Load a 4-byte little-endian word +static DWORD ld_dword (const BYTE* ptr) {return (((const struct T_UINT32 *)(const void *)(ptr))->v);} +#if !FF_FS_READONLY +// Store a 2-byte word in little-endian +static void st_word(BYTE* ptr, WORD val) {(void)((((struct T_UINT16 *)(void *)(ptr))->v) = (val));} +// Store a 4-byte word in little-endian +static void st_dword (BYTE* ptr, DWORD val){(void)((((struct T_UINT32 *)(void *)(ptr))->v) = (val));} +#if FF_FS_EXFAT +// Load an 8-byte little-endian word +static QWORD ld_qword (const BYTE* ptr) {return (((const struct T_UINT64 *)(const void *)(ptr))->v);} +// Store an 8-byte word in little-endian +static void st_qword(BYTE* ptr, QWORD val) {(void)((((struct T_UINT64 *)(void *)(ptr))->v) = (val));} +#endif +#endif // FF_FS_READONLY + #else static WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ diff --git a/FatFs/ff.h b/FatFs/ff.h index 58efb01..14f3a1a 100644 --- a/FatFs/ff.h +++ b/FatFs/ff.h @@ -139,6 +139,7 @@ typedef struct { BYTE n_fats; /* Number of FATs (1 or 2) */ BYTE wflag; /* win[] flag (b0:dirty) */ BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + BYTE reserved; /* align structure */ WORD id; /* Volume mount ID */ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ WORD csize; /* Cluster size [sectors] */ @@ -210,6 +211,7 @@ typedef struct { FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ BYTE flag; /* File status flags */ BYTE err; /* Abort flag (error code) */ + WORD reserved; /* align structure */ FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */ diff --git a/FatFs/ffconf_072.h b/FatFs/ffconf_072.h index 543fc33..9b97758 100644 --- a/FatFs/ffconf_072.h +++ b/FatFs/ffconf_072.h @@ -297,12 +297,13 @@ / included somewhere in the scope of ff.h. */ // STM32F0 processor (Cortex M0) not support read/write unaligned WORD or DWORD data -#define _WORD_ACCESS 0 /* 0 or 1 */ +#define _WORD_ACCESS 0 /* 0 or 1 o 2 */ /* The _WORD_ACCESS option is an only platform dependent option. It defines / which access method is used to the word data on the FAT volume. / / 0: Byte-by-byte access. Always compatible with all platforms. / 1: Word access. Do not choose this unless under both the following conditions. +/ 2: Compiler auto detect where need byte or word access (use structures) / / * Address misaligned memory access is always allowed for ALL instructions. / * Byte order on the memory is little-endian. diff --git a/FatFs/ffconf_303.h b/FatFs/ffconf_303.h index 2f209ee..254d8d5 100644 --- a/FatFs/ffconf_303.h +++ b/FatFs/ffconf_303.h @@ -297,12 +297,13 @@ / included somewhere in the scope of ff.h. */ -#define _WORD_ACCESS 1 /* 0 or 1 */ +#define _WORD_ACCESS 1 /* 0 or 1 o 2 */ /* The _WORD_ACCESS option is an only platform dependent option. It defines / which access method is used to the word data on the FAT volume. / / 0: Byte-by-byte access. Always compatible with all platforms. / 1: Word access. Do not choose this unless under both the following conditions. +/ 2: Compiler auto detect where need byte or word access (use structures) / / * Address misaligned memory access is always allowed for ALL instructions. / * Byte order on the memory is little-endian. diff --git a/Makefile b/Makefile index 98fca41..c2571c7 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ endif # Enable this if you want link time optimizations (LTO) ifeq ($(USE_LTO),) - USE_LTO = no + USE_LTO = yes endif # If enabled, this option allows to compile the application in THUMB mode. @@ -154,8 +154,13 @@ CSRC = $(STARTUPSRC) \ $(STREAMSSRC) \ FatFs/ff.c \ FatFs/ffunicode.c \ + fonts/numfont16x22.c \ + fonts/Font5x7.c \ + fonts/Font6x10.c \ + fonts/Font7x11b.c \ + fonts/Font11x14.c \ usbcfg.c \ - main.c si5351.c tlv320aic3204.c dsp.c plot.c ui.c ili9341.c numfont16x22.c Font5x7.c Font6x10.c Font7x11b.c Font11x14.c data_storage.c hardware.c vna_math.c + main.c common.c si5351.c tlv320aic3204.c dsp.c plot.c ui.c lcd.c data_storage.c hardware.c vna_math.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. diff --git a/NANOVNA_STM32_F072/exti_v1.c b/NANOVNA_STM32_F072/exti_v1.c index 9ae2e82..6cfc550 100644 --- a/NANOVNA_STM32_F072/exti_v1.c +++ b/NANOVNA_STM32_F072/exti_v1.c @@ -72,6 +72,7 @@ OSAL_IRQ_HANDLER(Vector5C) { // EXTI[4]...EXTI[15] interrupt handler uint32_t pr = EXTI->PR & ((1U << 4) | (1U << 5) | (1U << 6) | (1U << 7) | (1U << 8) | (1U << 9) | (1U << 10) | (1U << 11) | (1U << 12) | (1U << 13) | (1U << 14) | (1U << 15)); + EXTI->PR = pr; #ifdef EXT_CH4_HANDLER_FUNC if (pr & (1U << 4)) EXT_CH4_HANDLER_FUNC(4); #endif diff --git a/NANOVNA_STM32_F072/i2c_v2.c b/NANOVNA_STM32_F072/i2c_v2.c index c7d99f6..5707a08 100644 --- a/NANOVNA_STM32_F072/i2c_v2.c +++ b/NANOVNA_STM32_F072/i2c_v2.c @@ -51,6 +51,7 @@ bool i2c_transfer(uint8_t addr, const uint8_t *w, size_t wn) // I2C TX and RX variant bool i2c_receive(uint8_t addr, const uint8_t *w, size_t wn, uint8_t *r, size_t rn) { + while(VNA_I2C->ISR & I2C_ISR_BUSY); // wait last transaction VNA_I2C->CR1|= I2C_CR1_PE; if (wn) { VNA_I2C->CR2 = (addr << I2C_CR2_SADD_7BIT_SHIFT) | (wn << I2C_CR2_NBYTES_SHIFT); diff --git a/NANOVNA_STM32_F072/mcuconf.h b/NANOVNA_STM32_F072/mcuconf.h index 236ee28..5ba2c42 100644 --- a/NANOVNA_STM32_F072/mcuconf.h +++ b/NANOVNA_STM32_F072/mcuconf.h @@ -59,6 +59,8 @@ // Define STM32_I2C1_CLOCK as 48MHz (STM32_I2C1SW is STM32_I2C1SW_SYSCLK) #define STM32_I2C1_CLOCK 48 +// Core clock in MHz +#define STM32_CORE_CLOCK 48 /* * RTC driver system settings for stm32f072 diff --git a/NANOVNA_STM32_F072/rtc_v2.c b/NANOVNA_STM32_F072/rtc_v2.c index f89e23a..68deac0 100644 --- a/NANOVNA_STM32_F072/rtc_v2.c +++ b/NANOVNA_STM32_F072/rtc_v2.c @@ -149,7 +149,9 @@ void rtc_init(void){ // If calendar has not been initialized yet then proceed with the initial setup. if ((RTC->ISR & RTC_ISR_INITS) == 0 || RTC->PRER != rtc_prer) { if (rtc_enter_init()){ - RTC->CR = 0; + RTC->CR = 0 +// | RTC_CR_COSEL // RTC output 1Hz (or 512Hz if disabled) + ; RTC->ISR = RTC_ISR_INIT; // Clearing all but RTC_ISR_INIT. RTC->PRER = rtc_prer; // Prescaler value loaded in registers 2 times RTC->PRER = rtc_prer; @@ -160,3 +162,17 @@ void rtc_init(void){ else RTC->ISR &= ~RTC_ISR_RSF; } + +void rtc_set_cal(float ppm) { + int32_t cal = ppm * (1<<20) / 1000000.0f + 511.5f; + if ((RTC->ISR & RTC_ISR_RECALPF) || (uint32_t)cal > 1024) + return; + RTC->CALR = (511 - cal) & (RTC_CALR_CALP | RTC_CALR_CALM); +} + +float rtc_get_cal(void) { + int32_t cal = -(RTC->CALR & RTC_CALR_CALM); + if (RTC->CALR & RTC_CALR_CALP) + cal += 512; + return cal * (1000000.0f / (1<<20)); +} diff --git a/NANOVNA_STM32_F303/exti_v1.c b/NANOVNA_STM32_F303/exti_v1.c index b99fdfd..91aae55 100644 --- a/NANOVNA_STM32_F303/exti_v1.c +++ b/NANOVNA_STM32_F303/exti_v1.c @@ -97,11 +97,11 @@ OSAL_IRQ_HANDLER(Vector68) { // EXTI[4] interrupt handler. } #endif - #if defined(EXT_CH5_HANDLER_FUNC) || defined(EXT_CH6_HANDLER_FUNC) || defined(EXT_CH7_HANDLER_FUNC) || \ defined(EXT_CH8_HANDLER_FUNC) || defined(EXT_CH9_HANDLER_FUNC) OSAL_IRQ_HANDLER(Vector9C) { // EXTI[5]...EXTI[9] interrupt handler uint32_t pr = EXTI->PR & ((1U << 5) | (1U << 6) | (1U << 7) | (1U << 8) | (1U << 9)); + EXTI->PR = pr; #ifdef EXT_CH5_HANDLER_FUNC if (pr & (1U << 5)) EXT_CH5_HANDLER_FUNC(5); #endif @@ -125,6 +125,7 @@ OSAL_IRQ_HANDLER(Vector9C) { // EXTI[5]...EXTI[9] interrupt handler OSAL_IRQ_HANDLER(VectorE0) { // EXTI[4]...EXTI[15] interrupt handler uint32_t pr = EXTI->PR & ((1U << 10) | (1U << 11) | (1U << 12) | (1U << 13) | (1U << 14) | (1U << 15)); + EXTI->PR = pr; #ifdef EXT_CH10_HANDLER_FUNC if (pr & (1U << 10)) EXT_CH10_HANDLER_FUNC(10); #endif diff --git a/NANOVNA_STM32_F303/i2c_v2.c b/NANOVNA_STM32_F303/i2c_v2.c index fb7cde4..7bed39d 100644 --- a/NANOVNA_STM32_F303/i2c_v2.c +++ b/NANOVNA_STM32_F303/i2c_v2.c @@ -51,6 +51,7 @@ bool i2c_transfer(uint8_t addr, const uint8_t *w, size_t wn) // I2C TX and RX variant bool i2c_receive(uint8_t addr, const uint8_t *w, size_t wn, uint8_t *r, size_t rn) { + while(VNA_I2C->ISR & I2C_ISR_BUSY); // wait last transaction VNA_I2C->CR1|= I2C_CR1_PE; if (wn) { VNA_I2C->CR2 = (addr << I2C_CR2_SADD_7BIT_SHIFT) | (wn << I2C_CR2_NBYTES_SHIFT); diff --git a/NANOVNA_STM32_F303/mcuconf.h b/NANOVNA_STM32_F303/mcuconf.h index dd83c69..a243085 100644 --- a/NANOVNA_STM32_F303/mcuconf.h +++ b/NANOVNA_STM32_F303/mcuconf.h @@ -64,6 +64,8 @@ // Define STM32_I2C1_CLOCK as 72MHz (STM32_I2C1SW is STM32_I2C1SW_SYSCLK) #define STM32_I2C1_CLOCK 72 +// Core clock in MHz +#define STM32_CORE_CLOCK 72 /* * RTC driver system settings for stm32f303 diff --git a/NANOVNA_STM32_F303/rtc_v2.c b/NANOVNA_STM32_F303/rtc_v2.c index f89e23a..bf5fe27 100644 --- a/NANOVNA_STM32_F303/rtc_v2.c +++ b/NANOVNA_STM32_F303/rtc_v2.c @@ -149,7 +149,9 @@ void rtc_init(void){ // If calendar has not been initialized yet then proceed with the initial setup. if ((RTC->ISR & RTC_ISR_INITS) == 0 || RTC->PRER != rtc_prer) { if (rtc_enter_init()){ - RTC->CR = 0; + RTC->CR = 0 +// | RTC_CR_COSEL // RTC output 1Hz (or 512Hz if disabled) + ; RTC->ISR = RTC_ISR_INIT; // Clearing all but RTC_ISR_INIT. RTC->PRER = rtc_prer; // Prescaler value loaded in registers 2 times RTC->PRER = rtc_prer; @@ -160,3 +162,17 @@ void rtc_init(void){ else RTC->ISR &= ~RTC_ISR_RSF; } + +void rtc_set_cal(float ppm) { + int32_t cal = ppm * (1<<20) / 1000000.0f + 511.5f; + if ((RTC->ISR & RTC_ISR_RECALPF) || (uint32_t)cal > 1024) + return; + RTC->CALR = ((511 - cal) & (RTC_CALR_CALP | RTC_CALR_CALM)); +} + +float rtc_get_cal(void) { + int32_t cal = -(RTC->CALR & RTC_CALR_CALM); + if (RTC->CALR & RTC_CALR_CALP) + cal += 512; + return cal * (1000000.0f / (1<<20)); +} diff --git a/common.c b/common.c new file mode 100644 index 0000000..9c2b9f5 --- /dev/null +++ b/common.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2019-2024, Dmitry (DiSlord) dislordlive@gmail.com + * Based on TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include +#include +#include "hal.h" + +// Use size optimization (UI not need fast speed, better have smallest size) +#pragma GCC optimize ("Os") + +// Use macro, std isdigit more big +#define _isdigit(c) (c >= '0' && c <= '9') +// Rewrite universal standart str to value functions to more compact +// +// Convert string to int32 +int32_t my_atoi(const char *p) { + int32_t value = 0; + uint32_t c; + bool neg = false; + + if (*p == '-') {neg = true; p++;} + if (*p == '+') p++; + while ((c = *p++ - '0') < 10) + value = value * 10 + c; + return neg ? -value : value; +} + +// Convert string to uint32 +// 0x - for hex radix +// 0o - for oct radix +// 0b - for bin radix +// default dec radix +uint32_t my_atoui(const char *p) { + uint32_t value = 0, radix = 10, c; + if (*p == '+') p++; + if (*p == '0') { + switch (p[1]) { + case 'x': radix = 16; break; + case 'o': radix = 8; break; + case 'b': radix = 2; break; + default: goto calculate; + } + p+=2; + } +calculate: + while (1) { + c = *p++ - '0'; + // c = to_upper(*p) - 'A' + 10 + if (c >= 'A' - '0') c = (c&(~0x20)) - ('A' - '0') + 10; + if (c >= radix) return value; + value = value * radix + c; + } +} + +float my_atof(const char *p) { + int neg = false; + if (*p == '-') + neg = true; + if (*p == '-' || *p == '+') + p++; + float x = my_atoi(p); + while (_isdigit((int)*p)) + p++; + if (*p == '.' || *p == ',') { + float d = 1.0f; + p++; + while (_isdigit((int)*p)) { + d *= 1e-1f; + x += d * (*p - '0'); + p++; + } + } + if (*p) { + int exp = 0; + if (*p == 'e' || *p == 'E') exp = my_atoi(&p[1]); + else if (*p == 'G') exp = 9; // Giga + else if (*p == 'M') exp = 6; // Mega + else if (*p == 'k') exp = 3; // kilo + else if (*p == 'm') exp = -3; // milli + else if (*p == 'u') exp = -6; // micro + else if (*p == 'n') exp = -9; // nano + else if (*p == 'p') exp =-12; // pico + if (exp > 0) do {x*= 1e+1f;} while (--exp); + if (exp < 0) do {x*= 1e-1f;} while (++exp); + } + return neg ? -x : x; +} + +static char to_lower(char c) {return (c >='A' && c <= 'Z') ? c - 'A' + 'a' : c;} +bool strcmpi(const char *t1, const char *t2) { + int i = 0; + while (1) { + char ch1 = to_lower(t1[i]), ch2 = to_lower(t2[i]); + if (ch1 != ch2) return false; + if (ch1 == 0) return true; + i++; + } +} + +// +// Function used for search substring v in list +// Example need search parameter "center" in "start|stop|center|span|cw" getStringIndex return 2 +// If not found return -1 +// Used for easy parse command arguments +int get_str_index(const char *v, const char *list) { + int i = 0; + while (1) { + const char *p = v; + while (1) { + char c = *list; + if (c == '|') c = 0; + if (c == *p++) { + // Found, return index + if (c == 0) return i; + list++; // Compare next symbol + continue; + } + break; // Not equal, break + } + // Set new substring ptr + while (1) { + // End of string, not found + if (*list == 0) return -1; + if (*list++ == '|') break; + } + i++; + } + return -1; +} + +/* + * Search first symbols (s2) entry in string (s1) + */ +static inline char* _strpbrk(char *s1, const char *s2) { + do { + const char *s = s2; + while(*s) if (*s++ == *s1) return s1; + } while(*++s1); + return s1; +} + +/* + * Split line by arguments, return arguments count + */ +int parse_line(char *line, char* args[], int max_cnt) { + char *lp = line, c; + const char *brk; + uint16_t nargs = 0; + while ((c = *lp) != 0) { // While not end + if (c != ' ' && c != '\t') { // Skipping white space and tabs. + if (c == '"') {lp++; brk = "\""; } // string end is next quote or end + else { brk = " \t";} // string end is tab or space or end + if (nargs < max_cnt) args[nargs] = lp; // Put pointer in args buffer (if possible) + nargs++; // Substring count + lp = _strpbrk(lp, brk); // search end + if (*lp == 0) break; // Stop, end of input string + *lp = 0; // Set zero at the end of substring + } + lp++; + } + return nargs; +} + +/* + * Swap byte order in uint16_t buffer + */ +void swap_bytes(uint16_t *buf, int size) { + for (int i = 0; i < size; i++) + buf[i] = __REVSH(buf[i]); // swap byte order (example 0x10FF to 0xFF10) +} + +/* + * RLE packbits compression algorithm + */ +int packbits(char *source, char *dest, int size) { + int i = 0, rle, l, pk = 0, sz = 0; + while ((l = size - i) > 0) { + if (l > 128) l = 128; // Limit search RLE block size to 128 + char c = source[i++]; // Get next byte and write to block + for (rle = 0; c == source[i + rle] && --l; rle++); // Calculate this byte RLE sequence size = rle + 1 + if (sz && rle < 2) rle = 0; // Ignore (rle + 1) < 3 sequence on run non RLE input + else if (sz == 0 || rle > 0) sz = pk++; // Reset state or RLE sequence found -> start new block + dest[pk++] = c; // Write char to block + if (rle > 0) {i+= rle; dest[sz] = -rle;} // Write RLE sequence size and go to new block + else if ((dest[sz] = pk - sz - 2) < 127) // Continue write non RLE data while 1 + (non_rle + 1) < 127 + continue; + sz = 0; // Block complete + } + return pk; +} + +/* + * Delay 8 core tick function + */ +void _delay_8t(uint32_t cycles) { + if(cycles < 1) return; + __asm ( + "1: \n" + " subs %[cyc], %[cyc], #1 \n" // 1 cycle + " nop \n" // 1 cycle + " nop \n" // 1 cycle + " nop \n" // 1 cycle + " nop \n" // 1 cycle + " nop \n" // 1 cycle + " bne 1b \n" // 2 if taken, 1 otherwise + : [cyc] "+l" (cycles) + : // no inputs + : // No memory + ); +} diff --git a/data_storage.c b/data_storage.c index e118892..32b45eb 100644 --- a/data_storage.c +++ b/data_storage.c @@ -27,6 +27,7 @@ uint16_t lastsaveid = 0; #if SAVEAREA_MAX >= 8 #error "Increase checksum_ok type for save more cache slots" #endif + // properties CRC check cache (max 8 slots) static uint8_t checksum_ok = 0; @@ -115,6 +116,6 @@ void clear_all_config_prop_data(void) { lastsaveid = NO_SAVE_SLOT; checksum_ok = 0; // unlock and erase flash pages - flash_erase_pages(SAVE_CONFIG_ADDR, SAVE_FULL_AREA_SIZE); + flash_erase_pages(SAVE_PROP_CONFIG_ADDR, SAVE_FULL_AREA_SIZE); } diff --git a/doc/NanoVNASharp.zip b/doc/NanoVNASharp.zip new file mode 100644 index 0000000..b39ecd5 Binary files /dev/null and b/doc/NanoVNASharp.zip differ diff --git a/doc/Schematic_NanoVNA-H4_REV4_4.pdf b/doc/Schematic_NanoVNA-H4_REV4_4.pdf new file mode 100644 index 0000000..524b2e7 Binary files /dev/null and b/doc/Schematic_NanoVNA-H4_REV4_4.pdf differ diff --git a/doc/Schematic_nanovna_H_REV3_7.pdf b/doc/Schematic_nanovna_H_REV3_7.pdf new file mode 100644 index 0000000..7e45e78 Binary files /dev/null and b/doc/Schematic_nanovna_H_REV3_7.pdf differ diff --git a/doc/ZEETK_NE602A.pdf b/doc/ZEETK_NE602A.pdf new file mode 100644 index 0000000..c93e92c Binary files /dev/null and b/doc/ZEETK_NE602A.pdf differ diff --git a/Font11x14.c b/fonts/Font11x14.c similarity index 99% rename from Font11x14.c rename to fonts/Font11x14.c index eba7666..d610116 100644 --- a/Font11x14.c +++ b/fonts/Font11x14.c @@ -22,7 +22,7 @@ */ #include -#include "nanovna.h" +#include "../nanovna.h" /* * Check 1 byte of bitmap data for get width diff --git a/Font5x7.c b/fonts/Font5x7.c similarity index 99% rename from Font5x7.c rename to fonts/Font5x7.c index 9e86380..1a46e92 100644 --- a/Font5x7.c +++ b/fonts/Font5x7.c @@ -19,7 +19,7 @@ */ #include -#include "nanovna.h" +#include "../nanovna.h" /* * Most font glyph have width 5 pixels diff --git a/Font6x10.c b/fonts/Font6x10.c similarity index 99% rename from Font6x10.c rename to fonts/Font6x10.c index 794ed15..b0d06ea 100644 --- a/Font6x10.c +++ b/fonts/Font6x10.c @@ -19,7 +19,7 @@ */ #include -#include "nanovna.h" +#include "../nanovna.h" /* * Most font glyph have width 6 pixels diff --git a/Font7x11b.c b/fonts/Font7x11b.c similarity index 99% rename from Font7x11b.c rename to fonts/Font7x11b.c index 5086f72..b4af1aa 100644 --- a/Font7x11b.c +++ b/fonts/Font7x11b.c @@ -19,7 +19,7 @@ */ #include -#include "nanovna.h" +#include "../nanovna.h" /* * Most font glyph have width 7 pixels diff --git a/fonts/numfont16x22.c b/fonts/numfont16x22.c new file mode 100644 index 0000000..c3177d4 --- /dev/null +++ b/fonts/numfont16x22.c @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2019-2020, Dmitry (DiSlord) dislordlive@gmail.com + * All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include "../nanovna.h" + +const uint8_t numfont16x22[] = { + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b0000000011110000), + _BMP16(0b0000000111110000), + _BMP16(0b0000001111110000), + _BMP16(0b0000011111110000), + _BMP16(0b0000111111110000), + _BMP16(0b0000111111110000), + _BMP16(0b0000111011110000), + _BMP16(0b0000110011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000011110000), + _BMP16(0b0000001111111100), + _BMP16(0b0000001111111100), + _BMP16(0b0000001111111100), + + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b0000000000011111), + _BMP16(0b0000000000111111), + _BMP16(0b0000000001111110), + _BMP16(0b0000000011111100), + _BMP16(0b0000000111111000), + _BMP16(0b0000001111110000), + _BMP16(0b0000011111100000), + _BMP16(0b0000111111000000), + _BMP16(0b0001111110000000), + _BMP16(0b0011111100000000), + _BMP16(0b0111111000000000), + _BMP16(0b1111110000000000), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b0000000000011110), + _BMP16(0b0000000000111110), + _BMP16(0b0000000111111100), + _BMP16(0b0000000111111000), + _BMP16(0b0000000111111100), + _BMP16(0b0000000001111110), + _BMP16(0b0000000000011111), + _BMP16(0b0000000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b0000000111111000), + _BMP16(0b0000000111111000), + _BMP16(0b0000001111111000), + _BMP16(0b0000001111111000), + _BMP16(0b0000011111111000), + _BMP16(0b0000011111111000), + _BMP16(0b0000111111111000), + _BMP16(0b0000111101111000), + _BMP16(0b0001111101111000), + _BMP16(0b0001111001111000), + _BMP16(0b0011111001111000), + _BMP16(0b0011110001111000), + _BMP16(0b0111110001111000), + _BMP16(0b0111100001111000), + _BMP16(0b1111100001111000), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b0000000001111000), + _BMP16(0b0000000001111000), + _BMP16(0b0000000001111000), + _BMP16(0b0000000001111000), + + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111011111110000), + _BMP16(0b1111111111111100), + _BMP16(0b1111111111111110), + _BMP16(0b1111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111011111110000), + _BMP16(0b1111111111111100), + _BMP16(0b1111111111111110), + _BMP16(0b1111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b0000000000011111), + _BMP16(0b0000000000111110), + _BMP16(0b0000000001111100), + _BMP16(0b0000000011111000), + _BMP16(0b0000000111110000), + _BMP16(0b0000000111100000), + _BMP16(0b0000001111100000), + _BMP16(0b0000001111000000), + _BMP16(0b0000011111000000), + _BMP16(0b0000011110000000), + _BMP16(0b0000011110000000), + _BMP16(0b0000111110000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + _BMP16(0b0000111100000000), + + _BMP16(0b0000011111100000), + _BMP16(0b0001111111111000), + _BMP16(0b0011111111111100), + _BMP16(0b0111110000111110), + _BMP16(0b0111100000011110), + _BMP16(0b0111100000011110), + _BMP16(0b0111100000011110), + _BMP16(0b0011110000111100), + _BMP16(0b0001111111111000), + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111110000111110), + _BMP16(0b0111100000011110), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b0000111111110000), + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111111), + _BMP16(0b0111111111111111), + _BMP16(0b0011111111111111), + _BMP16(0b0000111111111111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111110), + _BMP16(0b0111111111111110), + _BMP16(0b0011111111111100), + _BMP16(0b0000111111110000), + + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000011111000000), + _BMP16(0b0000011111000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000001000000), + _BMP16(0b0000000011000000), + _BMP16(0b0000000111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000011111000000), + _BMP16(0b0000111111111111), + _BMP16(0b0001111111111111), + _BMP16(0b0011111111111111), + _BMP16(0b0111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b0111111111111111), + _BMP16(0b0011111111111111), + _BMP16(0b0001111111111111), + _BMP16(0b0000111111111111), + _BMP16(0b0000011111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000000111000000), + _BMP16(0b0000000011000000), + _BMP16(0b0000000001000000), + _BMP16(0b0000000000000000), + + _BMP16(0b1111000000000000), // kilo + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000111110), + _BMP16(0b1111000001111100), + _BMP16(0b1111000011111000), + _BMP16(0b1111000111110000), + _BMP16(0b1111001111100000), + _BMP16(0b1111011111000000), + _BMP16(0b1111111110000000), + _BMP16(0b1111111100000000), + _BMP16(0b1111111100000000), + _BMP16(0b1111111110000000), + _BMP16(0b1111011111000000), + _BMP16(0b1111001111100000), + _BMP16(0b1111000111110000), + _BMP16(0b1111000011111000), + _BMP16(0b1111000001111100), + _BMP16(0b1111000000111110), + + _BMP16(0b1111000000001111), // Mega + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b1111100000011111), + _BMP16(0b1111110000111111), + _BMP16(0b1111110000111111), + _BMP16(0b1111111001111111), + _BMP16(0b1111111001111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111011111101111), + _BMP16(0b1111011111101111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111000110001111), + _BMP16(0b1111000110001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + + _BMP16(0b0000111111110000), // Giga + _BMP16(0b0011111111111100), + _BMP16(0b0111111111111110), + _BMP16(0b0111110000111110), + _BMP16(0b1111100000011111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000111111), + _BMP16(0b1111000000111111), + _BMP16(0b1111000000111111), + _BMP16(0b1111000000000111), + _BMP16(0b1111000000001111), + _BMP16(0b1111100000011111), + _BMP16(0b0111110000111111), + _BMP16(0b0111111111111111), + _BMP16(0b0011111111110111), + _BMP16(0b0000111111100111), + + _BMP16(0b0000000000000000), // milli + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b1110111000011100), + _BMP16(0b1111111100111110), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + _BMP16(0b1111001111001111), + + _BMP16(0b0000000000000000), // micro + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111000000011110), + _BMP16(0b1111100000111110), + _BMP16(0b1111110001111110), + _BMP16(0b1111111111111110), + _BMP16(0b1111111111111111), + _BMP16(0b1111011111001111), + _BMP16(0b1111001110000111), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + + _BMP16(0b0000000000000000), // nano + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b1111000111110000), + _BMP16(0b1111011111111100), + _BMP16(0b1111111111111110), + _BMP16(0b1111111000111110), + _BMP16(0b1111110000011111), + _BMP16(0b1111100000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + _BMP16(0b1111000000001111), + + _BMP16(0b0000000000000000), // pico + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b1111001111110000), + _BMP16(0b1111011111111100), + _BMP16(0b1111111111111110), + _BMP16(0b1111111000011111), + _BMP16(0b1111110000001111), + _BMP16(0b1111100000000111), + _BMP16(0b1111100000000111), + _BMP16(0b1111100000000111), + _BMP16(0b1111110000001111), + _BMP16(0b1111111000011111), + _BMP16(0b1111111111111110), + _BMP16(0b1111011111111100), + _BMP16(0b1111001111110000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + _BMP16(0b1111000000000000), + + _BMP16(0b0000000000000011), // x1 + _BMP16(0b0000000000000111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000011111), + _BMP16(0b0000000000111111), + _BMP16(0b0000000001111111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0110000011001111), + _BMP16(0b1111000111101111), + _BMP16(0b0111101111001111), + _BMP16(0b0011111110001111), + _BMP16(0b0001111100001111), + _BMP16(0b0001111100001111), + _BMP16(0b0011111110001111), + _BMP16(0b0111101111001111), + _BMP16(0b1111000111101111), + _BMP16(0b0110000011001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + + _BMP16(0b1111010001011111), // ENTER + _BMP16(0b1000011001000100), + _BMP16(0b1111010101000100), + _BMP16(0b1000010011000100), + _BMP16(0b1111010001000100), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0000000000001111), + _BMP16(0b0001100000001111), + _BMP16(0b0011100000001111), + _BMP16(0b0111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b1111111111111111), + _BMP16(0b0111111111111111), + _BMP16(0b0011100000000000), + _BMP16(0b0001100000000000), + + _BMP16(0b0000000000000000), // % + _BMP16(0b0011110000000000), + _BMP16(0b0111111000000000), + _BMP16(0b1110011100000011), + _BMP16(0b1100001100000111), + _BMP16(0b1100001100001111), + _BMP16(0b1110011100011110), + _BMP16(0b0111111000111100), + _BMP16(0b0011110001111000), + _BMP16(0b0000000011110000), + _BMP16(0b0000000111100000), + _BMP16(0b0000001111000000), + _BMP16(0b0000011110000000), + _BMP16(0b0000111100000000), + _BMP16(0b0001111000111100), + _BMP16(0b0011110001111110), + _BMP16(0b0111100011100111), + _BMP16(0b1111000011000011), + _BMP16(0b1110000011000011), + _BMP16(0b1100000011100111), + _BMP16(0b0000000001111110), + _BMP16(0b0000000000111100), + +#if 0 + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0001111000111100), + _BMP16(0b0011111101111110), + _BMP16(0b0111001111100110), + _BMP16(0b0110000111000011), + _BMP16(0b1110000111000011), + _BMP16(0b1100000110000011), + _BMP16(0b1100000110000011), + _BMP16(0b1100000110000011), + _BMP16(0b1100000110000011), + _BMP16(0b1100001110000111), + _BMP16(0b1100001110000110), + _BMP16(0b0110011111001110), + _BMP16(0b0111111011111100), + _BMP16(0b0011110001111000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000011101111100), + _BMP16(0b0000011101111110), + _BMP16(0b0000001101100110), + _BMP16(0b0000001101100110), + _BMP16(0b0000001101100110), + _BMP16(0b0000001101100110), + _BMP16(0b0000001101100110), + _BMP16(0b0011101101111100), + _BMP16(0b0111111101111110), + _BMP16(0b1110011101100111), + _BMP16(0b1100001101100011), + _BMP16(0b1100001101100011), + _BMP16(0b1100001101100011), + _BMP16(0b1100001101100011), + _BMP16(0b1100001101100011), + _BMP16(0b1100001101100011), + _BMP16(0b1110011101100111), + _BMP16(0b0111111101111110), + _BMP16(0b0011101101111100), + + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0011111111111000), + _BMP16(0b0011111111111000), + _BMP16(0b0011111111111000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000001110000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0011111111111000), + _BMP16(0b0011111111111000), + _BMP16(0b0011111111111000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0000000000000000), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0000000000000000), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0000000000000000), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0111101111011110), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), // Space + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + + _BMP16(0b0000000000000000), // Plus + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0011111111111100), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000001111000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), + _BMP16(0b0000000000000000), +#endif +}; diff --git a/hardware.h b/hardware.h index 3fc7c45..9e4c23d 100644 --- a/hardware.h +++ b/hardware.h @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA. */ +#pragma once #include "halconf.h" /* * adc.c @@ -114,6 +115,14 @@ uint32_t rtc_get_dr_bin(void); uint32_t rtc_get_FAT(void); // Write date and time (need in bcd format!!!) void rtc_set_time(uint32_t dr, uint32_t tr); +// Toggle RTC clock output +#define rtc_clock_output_toggle() RTC->CR^= RTC_CR_COE +// Check RTC clock output +#define rtc_clock_output_enabled() (RTC->CR & RTC_CR_COE) +// Set RTC calibration value in ppm (value rounded by 1e6/(1<<20) +void rtc_set_cal(float ppm); +// Get RTC calibration value in ppm +float rtc_get_cal(void); #endif /* @@ -190,10 +199,10 @@ void initI2S(void *buffer, uint16_t count); // Save config_t and properties_t flash area (see flash7 from *.ld settings) #define SAVE_FULL_AREA_SIZE (SAVE_CONFIG_SIZE + SAVEAREA_MAX * SAVE_PROP_CONFIG_SIZE) // Save setting at end of CPU flash area -// Config at end minus full size -#define SAVE_CONFIG_ADDR (FLASH_START_ADDRESS + FLASH_TOTAL_SIZE - SAVE_FULL_AREA_SIZE) -// Properties save area follow after config -#define SAVE_PROP_CONFIG_ADDR (SAVE_CONFIG_ADDR + SAVE_CONFIG_SIZE) +// Config at end minus config size +#define SAVE_CONFIG_ADDR (FLASH_START_ADDRESS + FLASH_TOTAL_SIZE - SAVE_CONFIG_SIZE) +// Properties save area before config +#define SAVE_PROP_CONFIG_ADDR (FLASH_START_ADDRESS + FLASH_TOTAL_SIZE - SAVE_FULL_AREA_SIZE) // Erase settings on page void flash_erase_pages(uint32_t page_address, uint32_t size); diff --git a/ili9341.c b/lcd.c similarity index 65% rename from ili9341.c rename to lcd.c index e74f231..d634ca3 100644 --- a/ili9341.c +++ b/lcd.c @@ -1,6 +1,5 @@ /* - * Copyright (c) 2019-2023, Dmitry (DiSlord) dislordlive@gmail.com - * Based on TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com + * Copyright (c) 2019-2024, Dmitry (DiSlord) dislordlive@gmail.com * All rights reserved. * * This is free software; you can redistribute it and/or modify @@ -41,31 +40,27 @@ #endif // Custom display definition -#ifdef LCD_DRIVER_ILI9341 -// Set SPI bus speed for LCD -#define LCD_SPI_SPEED SPI_BR_DIV2 -#ifdef DISPLAY_ST7789 -#define LCD_SPI_RX_SPEED SPI_BR_DIV16 -#endif - -// Read speed, need more slow, not define if need use some as Tx speed -//#define LCD_SPI_RX_SPEED SPI_BR_DIV4 -// Allow enable DMA for read display data (can not stable on full speed, on less speed slower) -#define __USE_DISPLAY_DMA_RX__ -#endif - -#ifdef LCD_DRIVER_ST7796S -// Set SPI bus speed for LCD -#define LCD_SPI_SPEED SPI_BR_DIV2 -// Read speed, need more slow, not define if need use some as Tx speed -#define LCD_SPI_RX_SPEED SPI_BR_DIV4 -// Allow enable DMA for read display data -#define __USE_DISPLAY_DMA_RX__ +#if defined(LCD_DRIVER_ILI9341) || defined(LCD_DRIVER_ST7789) + // Set SPI bus speed for LCD + #define LCD_SPI_SPEED SPI_BR_DIV2 + // Read speed, need more slow, not define if need use some as Tx speed + #define ILI9341_SPI_RX_SPEED SPI_BR_DIV2 + // Read speed, need more slow, not define if need use some as Tx speed + #define ST7789V_SPI_RX_SPEED SPI_BR_DIV8 + // Allow enable DMA for read display data (can not stable on full speed, on less speed slower) + #define __USE_DISPLAY_DMA_RX__ +#elif defined(LCD_DRIVER_ST7796S) + // Set SPI bus speed for LCD + #define LCD_SPI_SPEED SPI_BR_DIV2 + // Read speed, need more slow, not define if need use some as Tx speed + #define LCD_SPI_RX_SPEED SPI_BR_DIV4 + // Allow enable DMA for read display data + #define __USE_DISPLAY_DMA_RX__ #endif // Disable DMA rx on disabled DMA tx #ifndef __USE_DISPLAY_DMA__ -#undef __USE_DISPLAY_DMA_RX__ + #undef __USE_DISPLAY_DMA_RX__ #endif // LCD display buffer @@ -164,8 +159,8 @@ static inline void spi_DMARxBuffer(uint8_t *buffer, uint16_t len, bool wait) { #else // Replace DMA function vs no DMA #define dmaChannelWaitCompletionRxTx() {} -#define spi_DMATxBuffer(buffer, len) spi_TxBuffer(buffer, len) -#define spi_DMARxBuffer(buffer, len) spi_RxBuffer(buffer, len) +#define spi_DMATxBuffer(buffer, len, flag) spi_TxBuffer(buffer, len) +#define spi_DMARxBuffer(buffer, len, flag) spi_RxBuffer(buffer, len) #endif // __USE_DISPLAY_DMA__ static void spi_init(void) { @@ -199,107 +194,202 @@ static void spi_init(void) { LCD_SPI->CR1|= SPI_CR1_SPE; //SPI enable } -//***************************************************** -// Display driver functions -//***************************************************** -// Display commands list -#define ILI9341_NOP 0x00 -#define ILI9341_SOFTWARE_RESET 0x01 -#define ILI9341_READ_IDENTIFICATION 0x04 -#define ILI9341_READ_STATUS 0x09 -#define ILI9341_READ_POWER_MODE 0x0A -#define ILI9341_READ_MADCTL 0x0B -#define ILI9341_READ_PIXEL_FORMAT 0x0C -#define ILI9341_READ_IMAGE_FORMAT 0x0D -#define ILI9341_READ_SIGNAL_MODE 0x0E -#define ILI9341_READ_SELF_DIAGNOSTIC 0x0F -#define ILI9341_SLEEP_IN 0x10 -#define ILI9341_SLEEP_OUT 0x11 -#define ILI9341_PARTIAL_MODE_ON 0x12 -#define ILI9341_NORMAL_DISPLAY_MODE_ON 0x13 -#define ILI9341_INVERSION_OFF 0x20 -#define ILI9341_INVERSION_ON 0x21 -#define ILI9341_GAMMA_SET 0x26 -#define ILI9341_DISPLAY_OFF 0x28 -#define ILI9341_DISPLAY_ON 0x29 -#define ILI9341_COLUMN_ADDRESS_SET 0x2A -#define ILI9341_PAGE_ADDRESS_SET 0x2B -#define ILI9341_MEMORY_WRITE 0x2C -#define ILI9341_COLOR_SET 0x2D -#define ILI9341_MEMORY_READ 0x2E -#define ILI9341_PARTIAL_AREA 0x30 -#define ILI9341_VERTICAL_SCROLLING_DEF 0x33 -#define ILI9341_TEARING_LINE_OFF 0x34 -#define ILI9341_TEARING_LINE_ON 0x35 -#define ILI9341_MEMORY_ACCESS_CONTROL 0x36 -#define ILI9341_VERTICAL_SCROLLING 0x37 -#define ILI9341_IDLE_MODE_OFF 0x38 -#define ILI9341_IDLE_MODE_ON 0x39 -#define ILI9341_PIXEL_FORMAT_SET 0x3A -#define ILI9341_WRITE_MEMORY_CONTINUE 0x3C -#define ILI9341_READ_MEMORY_CONTINUE 0x3E -#define ILI9341_SET_TEAR_SCANLINE 0x44 -#define ILI9341_GET_SCANLINE 0x45 -#define ILI9341_WRITE_BRIGHTNESS 0x51 -#define ILI9341_READ_BRIGHTNESS 0x52 -#define ILI9341_WRITE_CTRL_DISPLAY 0x53 -#define ILI9341_READ_CTRL_DISPLAY 0x54 -#define ILI9341_WRITE_CA_BRIGHTNESS 0x55 -#define ILI9341_READ_CA_BRIGHTNESS 0x56 -#define ILI9341_WRITE_CA_MIN_BRIGHTNESS 0x5E -#define ILI9341_READ_CA_MIN_BRIGHTNESS 0x5F -#define ILI9341_READ_ID1 0xDA -#define ILI9341_READ_ID2 0xDB -#define ILI9341_READ_ID3 0xDC -#define ILI9341_RGB_INTERFACE_CONTROL 0xB0 -#define ILI9341_FRAME_RATE_CONTROL_1 0xB1 -#define ILI9341_FRAME_RATE_CONTROL_2 0xB2 -#define ILI9341_FRAME_RATE_CONTROL_3 0xB3 -#define ILI9341_DISPLAY_INVERSION_CONTROL 0xB4 -#define ILI9341_BLANKING_PORCH_CONTROL 0xB5 -#define ILI9341_DISPLAY_FUNCTION_CONTROL 0xB6 -#define ILI9341_ENTRY_MODE_SET 0xB7 -#define ILI9341_BACKLIGHT_CONTROL_1 0xB8 -#define ILI9341_BACKLIGHT_CONTROL_2 0xB9 -#define ILI9341_BACKLIGHT_CONTROL_3 0xBA -#define ILI9341_BACKLIGHT_CONTROL_4 0xBB -#define ILI9341_BACKLIGHT_CONTROL_5 0xBC -#define ILI9341_BACKLIGHT_CONTROL_7 0xBE -#define ILI9341_BACKLIGHT_CONTROL_8 0xBF -#define ILI9341_POWER_CONTROL_1 0xC0 -#define ILI9341_POWER_CONTROL_2 0xC1 -#define ILI9341_VCOM_CONTROL_1 0xC5 -#define ILI9341_VCOM_CONTROL_2 0xC7 -#define ILI9341_POWERA 0xCB -#define ILI9341_POWERB 0xCF -#define ILI9341_NV_MEMORY_WRITE 0xD0 -#define ILI9341_NV_PROTECTION_KEY 0xD1 -#define ILI9341_NV_STATUS_READ 0xD2 -#define ILI9341_READ_ID4 0xD3 -#define ILI9341_POSITIVE_GAMMA_CORRECTION 0xE0 -#define ILI9341_NEGATIVE_GAMMA_CORRECTION 0xE1 -#define ILI9341_DIGITAL_GAMMA_CONTROL_1 0xE2 -#define ILI9341_DIGITAL_GAMMA_CONTROL_2 0xE3 -#define ILI9341_DTCA 0xE8 -#define ILI9341_DTCB 0xEA -#define ILI9341_POWER_SEQ 0xED -#define ILI9341_3GAMMA_EN 0xF2 -#define ILI9341_INTERFACE_CONTROL 0xF6 -#define ILI9341_PUMP_RATIO_CONTROL 0xF7 +//****************************************************************************** +// All LCD (ILI9341, ST7789V, ST9996s) level 1 commands +//****************************************************************************** +#define LCD_NOP 0x00 // No operation +#define LCD_SWRESET 0x01 // Software reset +#define LCD_RDDID 0x04 // Read display ID +#define LCD_RDNUMED 0x05 // Read Number of the Errors on DSI (only ST7796s) +#define LCD_RDDST 0x09 // Read display status +#define LCD_RDDPM 0x0A // Read Display Power Mode +#define LCD_RDD_MADCTL 0x0B // Read Display MADCTL +#define LCD_RDDCOLMOD 0x0C // Read Display Pixel Format +#define LCD_RDDIM 0x0D // Read Display Image Mode +#define LCD_RDDSM 0x0E // Read Display Signal Mode +#define LCD_RDDSDR 0x0F // Read Display Self-Diagnostic Result +#define LCD_SLPIN 0x10 // Sleep in +#define LCD_SLPOUT 0x11 // Sleep Out +#define LCD_PTLON 0x12 // Partial Display Mode On +#define LCD_NORON 0x13 // Normal Display Mode On +#define LCD_INVOFF 0x20 // Display Inversion Off +#define LCD_INVON 0x21 // Display Inversion On +#define LCD_GAMSET 0x26 // Gamma Set (only ILI9341 and ST7789V) +#define LCD_DISPOFF 0x28 // Display Off +#define LCD_DISPON 0x29 // Display On +#define LCD_CASET 0x2A // Column Address Set +#define LCD_RASET 0x2B // Row Address Set +#define LCD_RAMWR 0x2C // Memory Write +#define LCD_RGBSET 0x2D // Color Set (only ILI9341) +#define LCD_RAMRD 0x2E // Memory Read +#define LCD_PTLAR 0x30 // Partial Area +#define LCD_VSCRDEF 0x33 // Vertical Scrolling Definition +#define LCD_TEOFF 0x34 // Tearing Effect Line OFF +#define LCD_TEON 0x35 // Tearing Effect Line On +#define LCD_MADCTL 0x36 // Memory Data Access Control +#define LCD_VSCSAD 0x37 // Vertical Scroll Start Address of RAM +#define LCD_IDMOFF 0x38 // Idle Mode Off +#define LCD_IDMON 0x39 // Idle mode on +#define LCD_COLMOD 0x3A // Interface Pixel Format +#define LCD_WRMEMC 0x3C // Write_Memory_Continue (only ILI9341) +#define LCD_RDMEMC 0x3E // Read Memory Continue +#define LCD_STE 0x44 // Set Tear Scanline +#define LCD_GSCAN 0x45 // Get Scanline +#define LCD_WRDISBV 0x51 // Write Display Brightness +#define LCD_RDDISBV 0x52 // Read Display Brightness Value +#define LCD_WRCTRLD 0x53 // Write CTRL Display +#define LCD_RDCTRLD 0x54 // Read CTRL Value Display +#define LCD_WRCACE 0x55 // Write Content Adaptive Brightness Control and Color Enhancement +#define LCD_RDCABC 0x56 // Read Content Adaptive Brightness Control +#define LCD_WRCABCMB 0x5E // Write CABC Minimum Brightness +#define LCD_RDCABCMB 0x5F // Read CABC Minimum Brightness +#define LCD_RDID1 0xDA // Read ID1 +#define LCD_RDID2 0xDB // Read ID2 +#define LCD_RDID3 0xDC // Read ID3 + +// MEMORY_ACCESS_CONTROL register +#define LCD_MADCTL_MH 0x04 +#define LCD_MADCTL_BGR 0x08 +#define LCD_MADCTL_RGB 0x00 +#define LCD_MADCTL_ML 0x10 +#define LCD_MADCTL_MV 0x20 +#define LCD_MADCTL_MX 0x40 +#define LCD_MADCTL_MY 0x80 +// Display rotation enum +enum { + DISPLAY_ROTATION_0 = 0, + DISPLAY_ROTATION_90, + DISPLAY_ROTATION_180, + DISPLAY_ROTATION_270, +}; -// -// ILI9341_MEMORY_ACCESS_CONTROL registers -// -#define ILI9341_MADCTL_MY 0x80 -#define ILI9341_MADCTL_MX 0x40 -#define ILI9341_MADCTL_MV 0x20 -#define ILI9341_MADCTL_ML 0x10 -#define ILI9341_MADCTL_BGR 0x08 -#define ILI9341_MADCTL_MH 0x04 -#define ILI9341_MADCTL_RGB 0x00 +//****************************************************************************** +// Custom ILI9391 level 2 commands +//****************************************************************************** +#define ILI9341_IFMODE 0xB0 // RGB Interface Signal Control +#define ILI9341_FRMCTR1 0xB1 // Frame Rate Control (In Normal Mode/Full Colors) +#define ILI9341_FRMCTR2 0xB2 // Frame Rate Control (In Idle Mode/8 colors) +#define ILI9341_FRMCTR3 0xB3 // Frame Rate control (In Partial Mode/Full Colors) +#define ILI9341_INVTR 0xB4 // Display Inversion Control +#define ILI9341_PRCTR 0xB5 // Blanking Porch Control +#define ILI9341_DISCTRL 0xB6 // Display Function Control +#define ILI9341_ETMOD 0xB7 // Entry Mode Set +#define ILI9341_BKLTCTRL1 0xB8 // Backlight Control 1 +#define ILI9341_BKLTCTRL2 0xB9 // Backlight Control 2 +#define ILI9341_BKLTCTRL3 0xBA // Backlight Control 3 +#define ILI9341_BKLTCTRL4 0xBB // Backlight Control 4 +#define ILI9341_BKLTCTRL5 0xBC // Backlight Control 5 +#define ILI9341_BKLTCTRL7 0xBE // Backlight Control 7 +#define ILI9341_BKLTCTRL8 0xBF // Backlight Control 8 +#define ILI9341_PWCTRL1 0xC0 // Power Control 1 +#define ILI9341_PWCTRL2 0xC1 // Power Control 2 +#define ILI9341_VMCTRL1 0xC5 // VCOM Control 1 +#define ILI9341_VMCTRL2 0xC7 // VCOM Control 2 +#define ILI9341_NVMWR 0xD0 // NV Memory Write +#define ILI9341_NVMPKEY 0xD1 // NV Memory Protection Key +#define ILI9341_RDNVM 0xD2 // NV Memory Status Read +#define ILI9341_RDID4 0xD3 // Read ID4 +#define ILI9341_PGAMCTRL 0xE0 // Positive Gamma Correction +#define ILI9341_NGAMCTRL 0xE1 // Negative Gamma Correction +#define ILI9341_DGAMCTRL1 0xE2 // Digital Gamma Control 1 +#define ILI9341_DGAMCTRL2 0xE3 // Digital Gamma Control 2 +#define ILI9341_IFCTL 0xF6 // Interface Control +// Extend register commands +#define ILI9341_POWERA 0xCB // Power control A +#define ILI9341_POWERB 0xCF // Power control B +#define ILI9341_DTCA 0xE8 // Driver timing control A +#define ILI9341_DTCB 0xEA // Driver timing control B +#define ILI9341_POWER_SEQ 0xED // Power on sequence control +#define ILI9341_3GAMMA_EN 0xF2 // Enable 3G +#define ILI9341_PUMPCTRL 0xF7 // Pump ratio control + +//****************************************************************************** +// Custom ST7789V level 2 commands +//****************************************************************************** +#define ST7789V_RAMCTRL 0xB0 // RAM Control +#define ST7789V_RGBCTRL 0xB1 // RGB Interface Control +#define ST7789V_PORCTRL 0xB2 // Porch Setting +#define ST7789V_FRCTRL1 0xB3 // Frame Rate Control 1 (In partial mode/ idle colors) +#define ST7789V_INVTR 0xB4 // Display Inversion Control (only ILI9341) +#define ST7789V_PARCTRL 0xB5 // Partial Control +#define ST7789V_GCTRL 0xB7 // Gate Control +#define ST7789V_GTADJ 0xB8 // Gate On Timing Adjustment +#define ST7789V_DGMEN 0xBA // Digital Gamma Enable +#define ST7789V_VCOMS 0xBB // VCOM Setting +#define ST7789V_POWSAVE 0xBC // Power Saving Mode +#define ST7789V_DLPOFFSAVE 0xBD // Display off power save +#define ST7789V_LCMCTRL 0xC0 // LCM Control +#define ST7789V_IDSET 0xC1 // ID Code Setting +#define ST7789V_VDVVRHEN 0xC2 // VDV and VRH Command Enable +#define ST7789V_VRHS 0xC3 // VRH Set +#define ST7789V_VDVS 0xC4 // VDV Set +#define ST7789V_VCMOFSET 0xC5 // VCOM Offset Set +#define ST7789V_FRCTRL2 0xC6 // Frame Rate Control in Normal Mode +#define ST7789V_CABCCTRL 0xC7 // CABC Control +#define ST7789V_REGSEL1 0xC8 // Register Value Selection 1 +#define ST7789V_REGSEL2 0xCA // Register Value Selection 2 +#define ST7789V_PWMFRSEL 0xCC // PWM Frequency Selection +#define ST7789V_PWCTRL1 0xD0 // Power Control 1 +#define ST7789V_VAPVANEN 0xD2 // Enable VAP/VAN signal output +#define ST7789V_CMD2EN 0xDF // Command 2 Enable +#define ST7789V_PVGAMCTRL 0xE0 // Positive Voltage Gamma Control +#define ST7789V_NVGAMCTRL 0xE1 // Negative Voltage Gamma Control +#define ST7789V_DGMLUTR 0xE2 // Digital Gamma Look-up Table for Red +#define ST7789V_DGMLUTB 0xE3 // Digital Gamma Look-up Table for Blue +#define ST7789V_GATECTRL 0xE4 // Gate Control +#define ST7789V_SPI2EN 0xE7 // SPI2 Enable +#define ST7789V_PWCTRL2 0xE8 // Power Control 2 +#define ST7789V_EQCTRL 0xE9 // Equalize time control +#define ST7789V_PROMCTRL 0xEC // Program Mode Control +#define ST7789V_PROMEN 0xFA // Program Mode Enable +#define ST7789V_NVMSET 0xFC // NVM Setting +#define ST7789V_PROMACT 0xFE // Program action + +//****************************************************************************** +// Custom ST7796s level 2 commands +//****************************************************************************** +#define ST7796S_IFMODE 0xB0 // Interface Mode Control +#define ST7796S_FRMCTR1 0xB1 // Frame Rate Control (In Normal Mode/Full Colors) +#define ST7796S_FRMCTR2 0xB2 // Frame Rate Control 2 (In Idle Mode/8 colors) +#define ST7796S_FRMCTR3 0xB3 // Frame Rate Control3 (In Partial Mode/Full Colors) +#define ST7796S_DIC 0xB4 // Display Inversion Control +#define ST7796S_BPC 0xB5 // Blanking Porch Control +#define ST7796S_DFC 0xB6 // Display Function Control +#define ST7796S_EM 0xB7 // Entry Mode Set +#define ST7796S_PWR1 0xC0 // Power Control 1 +#define ST7796S_PWR2 0xC1 // Power Control 2 +#define ST7796S_PWR3 0xC2 // Power Control 3 +#define ST7796S_VCMPCTL 0xC5 // VCOM Control +#define ST7796S_VCMOFFSET 0xC6 // Vcom Offset Registe +#define ST7796S_NVMADW 0xD0 // NVM Address/Data Write +#define ST7796S_NVMBPROG 0xD1 // NVM Byte Program +#define ST7796S_NVMSR 0xD2 // Status Read +#define ST7796S_RDID4 0xD3 // Read ID4 +#define ST7796S_PGC 0xE0 // Positive Gamma Control +#define ST7796S_NGC 0xE1 // Negative Gamma Control +#define ST7796S_DGC1 0xE2 // Digital Gamma Control 1 +#define ST7796S_DGC2 0xE2 // Digital Gamma Control 2 +#define ST7796S_DOCA 0xE8 // Display Output Ctrl Adjust +#define ST7796S_CSCON 0xF0 // Command Set Control +#define ST7796S_SPI 0xFB // Read Control + +//****************************************************************************** +// Low level Display driver functions +//****************************************************************************** +// Used only in double buffer mode +#ifndef lcd_get_cell_buffer +#define LCD_BUFFER_1 0x01 +#define LCD_DMA_RUN 0x02 +static uint8_t LCD_dma_status = 0; + +// Return free buffer for render +pixel_t *lcd_get_cell_buffer(void) { + return &spi_buffer[(LCD_dma_status&LCD_BUFFER_1) ? SPI_BUFFER_SIZE/2 : 0]; +} +#endif // Disable inline for this function -static void ili9341_send_command(uint8_t cmd, uint16_t len, const uint8_t *data) { +static void lcd_send_command(uint8_t cmd, uint16_t len, const uint8_t *data) { // Uncomment on low speed SPI (possible get here before previous tx complete) while (SPI_IS_BUSY(LCD_SPI)); LCD_CS_LOW; @@ -313,16 +403,16 @@ static void ili9341_send_command(uint8_t cmd, uint16_t len, const uint8_t *data) //LCD_CS_HIGH; } -// Disable inline for this function -uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) { +// Send command to LCD and read 32bit answer +// LCD_RDDID command, need shift result right by 7 bit +// 0x00858552 for ST7789V (9.1.3 RDDID (04h): Read Display ID) +// 0x006BFFFF for ST7796S ?? no id description in datasheet +// 0x00000000 for ili9341 ?? no id description in datasheet +uint32_t lcd_send_register(uint8_t cmd, uint8_t len, const uint8_t *data) { lcd_bulk_finish(); - // Set read speed (if need different) - SPI_BR_SET(LCD_SPI, SPI_BR_DIV256); - // Send - ili9341_send_command(cmd, len, data); - - // Skip data from rx buffer - spi_DropRx(); + SPI_BR_SET(LCD_SPI, SPI_BR_DIV16); // Set most safe read speed + lcd_send_command(cmd, len, data); // Send command + spi_DropRx(); // Skip data from rx buffer uint32_t ret; ret = spi_RxByte();ret<<=8; ret|= spi_RxByte();ret<<=8; @@ -333,330 +423,242 @@ uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) { return ret; } -#define ST7789V_NOP 0x00 // No Operation -#define ST7789V_SWRESET 0x01 // Software reset -#define ST7789V_RDDID 0x04 // Read Display ID -#define ST7789V_RDDST 0x09 // Read Display Status -#define ST7789V_RDDPM 0x0A // Read Display Power Mode -#define ST7789V_RDDMADCTL 0x0B // Read Display MADCTL -#define ST7789V_RDDCOLMOD 0x0C // Read Display Pixel Format -#define ST7789V_RDDIM 0x0D // Read Display Image Mode -#define ST7789V_RDDSM 0x0E // Read Display Signal Mode -#define ST7789V_RDDSDR 0x0F // Read Display Self-Diagnostic Result -#define ST7789V_SLPIN 0x10 // Sleep In -#define ST7789V_SLPOUT 0x11 // Sleep Out -#define ST7789V_PTLON 0x12 // Partial Display Mode On -#define ST7789V_NORON 0x13 // Normal Display Mode On -#define ST7789V_INVOFF 0x20 // Display Inversion Off -#define ST7789V_INVON 0x21 // Display Inversion On -#define ST7789V_GAMSET 0x26 // Gamma Set -#define ST7789V_DISPOFF 0x28 // Display Off -#define ST7789V_DISPON 0x29 // Display On -#define ST7789V_CASET 0x2A // Column Address Set -#define ST7789V_RASET 0x2B // Row Address Set -#define ST7789V_RAMWR 0x2C // Memory Write -#define ST7789V_RAMRD 0x2E // Memory Read -#define ST7789V_PTLAR 0x30 // Partial Area -#define ST7789V_VSCRDEF 0x33 // Vertical Scrolling Definition -#define ST7789V_TEOFF 0x34 // Tearing Effect Line OFF -#define ST7789V_TEON 0x35 // Tearing Effect Line ON -#define ST7789V_MADCTL 0x36 // Memory Data Access Control -#define ST7789V_VSCSAD 0x37 // Vertical Scroll Start Address of RAM -#define ST7789V_IDMOFF 0x38 // Idle Mode Off -#define ST7789V_IDMON 0x39 // Idle Mode On -#define ST7789V_COLMOD 0x3A // Interface Pixel Format -#define ST7789V_WRMEMC 0x3C // Write Memory Continue -#define ST7789V_RDMEMC 0x3E // Read Memory Continue -#define ST7789V_STE 0x44 // Set Tear Scanline -#define ST7789V_GSCAN 0x45 // Get Scanline -#define ST7789V_WRDISBV 0x51 // Write Display Brightness -#define ST7789V_RDDISBV 0x52 // Read Display Brightness -#define ST7789V_WRCTRLD 0x53 // Write CTRL Display -#define ST7789V_RDCTRLD 0x54 // Read CTRL Value Display -#define ST7789V_WRCACE 0x55 // Write Content Adaptive Brightness Control and Color Enhancement -#define ST7789V_RDCABC 0x56 // Read Content Adaptive Brightness Control -#define ST7789V_WRCABCMB 0x5E // Write CABC Minimum Brightness -#define ST7789V_RDCABCMB 0x5F // Read CABC Minimum Brightness -#define ST7789V_RDABCSDR 0x68 // Read Automatic Brightness Control Self-Diagnostic Result -#define ST7789V_RDID1 0xDA // Read ID1 Value -#define ST7789V_RDID2 0xDB // Read ID2 Value -#define ST7789V_RDID3 0xDC // Read ID3 Value - -#define ST7789V_RAMCTRL 0xB0 // RAM Control -#define ST7789V_RGBCTRL 0xB1 // RGB Interface Control -#define ST7789V_PORCTRL 0xB2 // Porch Setting -#define ST7789V_FRCTRL1 0xB3 // Frame Rate Control 1 (In partial mode/ idle colors) -#define ST7789V_GCTRL 0xB7 // Gate Control -#define ST7789V_DGMEN 0xBA // Digital Gamma Enable -#define ST7789V_VCOMS 0xBB // VCOM Setting -#define ST7789V_LCMCTRL 0xC0 // LCM Control -#define ST7789V_IDSET 0xC1 // ID Code Setting -#define ST7789V_VDVVRHEN 0xC2 // VDV and VRH Command Enable -#define ST7789V_VRHS 0xC3 // VRH Set -#define ST7789V_VDVS 0xC4 // VDV Set -#define ST7789V_VCMOFSET 0xC5 // VCOM Offset Set -#define ST7789V_FRCTRL2 0xC6 // Frame Rate Control in Normal Mode -#define ST7789V_CABCCTRL 0xC7 // CABC Control -#define ST7789V_REGSEL1 0xC8 // Register Value Selection 1 -#define ST7789V_REGSEL2 0xCA // Register Value Selection 2 -#define ST7789V_PWMFRSEL 0xCC // PWM Frequency Selection -#define ST7789V_PWCTRL1 0xD0 // Power Control 1 -#define ST7789V_VAPVANEN 0xD2 // Enable VAP/VAN signal output -#define ST7789V_CMD2EN 0xDF // Command 2 Enable -#define ST7789V_PVGAMCTRL 0xE0 // Positive Voltage Gamma Control -#define ST7789V_NVGAMCTRL 0xE1 // Negative Voltage Gamma Control -#define ST7789V_DGMLUTR 0xE2 // Digital Gamma Look-up Table for Red -#define ST7789V_DGMLUTB 0xE3 // Digital Gamma Look-up Table for Blue -#define ST7789V_GATECTRL 0xE4 // Gate Control -#define ST7789V_SPI2EN 0xE7 // SPI2 Enable -#define ST7789V_PWCTRL2 0xE8 // Power Control 2 -#define ST7789V_EQCTRL 0xE9 // Equalize time control -#define ST7789V_PROMCTRL 0xEC // Program Mode Control -#define ST7789V_PROMEN 0xFA // Program Mode Enable -#define ST7789V_NVMSET 0xFC // NVM Setting -#define ST7789V_PROMACT 0xFE // Program action - - -#define LCD_MADCTL_MY 0x80 -#define LCD_MADCTL_MX 0x40 -#define LCD_MADCTL_MV 0x20 -#define LCD_MADCTL_ML 0x10 -#define LCD_MADCTL_BGR 0x08 -#define LCD_MADCTL_MH 0x04 -#define LCD_MADCTL_RGB 0x00 - -#ifdef DISPLAY_ST7789 -/* - * ST7789 init - */ -#define DISPLAY_ROTATION_0 (LCD_MADCTL_MX | LCD_MADCTL_MV | LCD_MADCTL_RGB) -#define DISPLAY_ROTATION_90 ( LCD_MADCTL_RGB) -#define DISPLAY_ROTATION_180 (LCD_MADCTL_MY | LCD_MADCTL_MV | LCD_MADCTL_RGB) -#define DISPLAY_ROTATION_270 (LCD_MADCTL_MX | LCD_MADCTL_MY | LCD_MADCTL_RGB) -static const uint8_t ST7796S_init_seq[] = { - // cmd, len, data..., - // SW reset - ST7789V_SWRESET, 0, - // display off - ST7789V_DISPOFF, 0, - ST7789V_MADCTL, 1, DISPLAY_ROTATION_0, - ST7789V_COLMOD, 1, 0x55, -//ST7789V_PORCTRL, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, -//ST7789V_GCTRL, 1, 0x35, - ST7789V_VCOMS, 1, 0x1F, // default 0x20 -//ST7789V_LCMCTRL, 1, 0x2C, - ST7789V_VDVVRHEN, 2, 0x01, 0xC3, // default 0x01, 0xFF !!! why need C3? datasheet say 0xFF -//ST7789V_VDVS, 1, 0x20, -//ST7789V_FRCTRL2, 1, 0x0F, -//ST7789V_PWCTRL1, 2, 0xA4, 0xA1, - ST7789V_SLPOUT, 0, - // display on - ST7789V_DISPON, 0, - 0 // sentinel +//****************************************************************************** +// Display driver init sequence and hardware depend functions +//****************************************************************************** +// ILI9341 and ST7789V Lcd init sequence + lcd depend image rotate function +#if defined(LCD_DRIVER_ILI9341) || defined(LCD_DRIVER_ST7789) +typedef enum {ili9341_type = 0, st7789v} lcd_type_t; +static lcd_type_t lcd_type = ili9341_type; +static const uint8_t ili9341_init_seq[] = { // ILI9341 init sequence + // cmd, len, data..., + LCD_SWRESET, 0, // SW reset + LCD_DISPOFF, 0, // display off +//ILI9341_POWERB, 3, 0x00, 0xC1, 0x30, // Power control B +//ILI9341_POWER_SEQ, 4, 0x64, 0x03, 0x12, 0x81, // Power on sequence control +//ILI9341_DTCA, 3, 0x85, 0x00, 0x78, // Driver timing control A +//ILI9341_POWERA, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, // Power control A +//ILI9341_PUMPCTRL, 1, 0x20, // Pump ratio control +//ILI9341_DTCB, 2, 0x00, 0x00, // Driver timing control B + ILI9341_PWCTRL1, 1, 0x23, // POWER_CONTROL_1 + ILI9341_PWCTRL2, 1, 0x10, // POWER_CONTROL_2 + ILI9341_VMCTRL1, 2, 0x3e, 0x28, // VCOM_CONTROL_1 + ILI9341_VMCTRL2, 1, 0xBE, // VCOM_CONTROL_2 + LCD_MADCTL, 1, LCD_MADCTL_MV | LCD_MADCTL_BGR, // landscape + LCD_COLMOD, 1, 0x55, // COLMOD_PIXEL_FORMAT_SET : 16 bit pixel + ILI9341_FRMCTR1, 2, 0x00, 0x18, // Frame Rate +//ILI9341_3GAMMA_EN, 1, 0x00, // Gamma Function Disable + LCD_GAMSET, 1, 0x01, // gamma set for curve 01/2/04/08 + ILI9341_PGAMCTRL, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // positive gamma correction + ILI9341_NGAMCTRL, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // negative gamma correction +//LCD_CASET, 4, 0x00, 0x00, 0x01, 0x3f, // Column Address Set: x = 0, width 320 +//LCD_RASET, 4, 0x00, 0x00, 0x00, 0xef, // Page Address Set: y = 0, height 240 + ILI9341_ETMOD, 1, 0x06, // entry mode + ILI9341_DISCTRL, 3, 0x08, 0x82, 0x27, // display function control + ILI9341_IFCTL, 3, 0x00, 0x00, 0x00, // Interface Control (set WEMODE=0) + LCD_SLPOUT, 0, // sleep out + LCD_DISPON, 0, // display on + 0 // sentinel }; -#define LCD_INIT ST7796S_init_seq -#else -#define DISPLAY_ROTATION_270 (ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR) -#define DISPLAY_ROTATION_90 (ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR) -#define DISPLAY_ROTATION_0 (ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR) -#define DISPLAY_ROTATION_180 (ILI9341_MADCTL_MX | ILI9341_MADCTL_MY \ - | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR) -static const uint8_t ili9341_init_seq[] = { - // cmd, len, data..., - // SW reset - ILI9341_SOFTWARE_RESET, 0, - // display off - ILI9341_DISPLAY_OFF, 0, - // Power control B - ILI9341_POWERB, 3, 0x00, 0xC1, 0x30, - // Power on sequence control - ILI9341_POWER_SEQ, 4, 0x64, 0x03, 0x12, 0x81, - // Driver timing control A - ILI9341_DTCA, 3, 0x85, 0x00, 0x78, - // Power control A - ILI9341_POWERA, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, - // Pump ratio control - ILI9341_PUMP_RATIO_CONTROL, 1, 0x20, - // Driver timing control B - ILI9341_DTCB, 2, 0x00, 0x00, - // POWER_CONTROL_1 - ILI9341_POWER_CONTROL_1, 1, 0x23, - // POWER_CONTROL_2 - ILI9341_POWER_CONTROL_2, 1, 0x10, - // VCOM_CONTROL_1 - ILI9341_VCOM_CONTROL_1, 2, 0x3e, 0x28, - // VCOM_CONTROL_2 - ILI9341_VCOM_CONTROL_2, 1, 0xBE, - // MEMORY_ACCESS_CONTROL - //ILI9341_MEMORY_ACCESS_CONTROL, 1, 0x48, // portlait - ILI9341_MEMORY_ACCESS_CONTROL, 1, DISPLAY_ROTATION_0, // landscape - // COLMOD_PIXEL_FORMAT_SET : 16 bit pixel - ILI9341_PIXEL_FORMAT_SET, 1, 0x55, - // Frame Rate - ILI9341_FRAME_RATE_CONTROL_1, 2, 0x00, 0x18, - // Gamma Function Disable - ILI9341_3GAMMA_EN, 1, 0x00, - // gamma set for curve 01/2/04/08 - ILI9341_GAMMA_SET, 1, 0x01, - // positive gamma correction - ILI9341_POSITIVE_GAMMA_CORRECTION, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, - // negativ gamma correction - ILI9341_NEGATIVE_GAMMA_CORRECTION, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, - // Column Address Set -// ILI9341_COLUMN_ADDRESS_SET, 4, 0x00, 0x00, 0x01, 0x3f, // width 320 - // Page Address Set -// ILI9341_PAGE_ADDRESS_SET, 4, 0x00, 0x00, 0x00, 0xef, // height 240 - // entry mode - ILI9341_ENTRY_MODE_SET, 1, 0x06, - // display function control - ILI9341_DISPLAY_FUNCTION_CONTROL, 3, 0x08, 0x82, 0x27, - // Interface Control (set WEMODE=0) - ILI9341_INTERFACE_CONTROL, 3, 0x00, 0x00, 0x00, - // sleep out - ILI9341_SLEEP_OUT, 0, - // display on - ILI9341_DISPLAY_ON, 0, - 0 // sentinel +// ST7789 LCD_RDDID read return 0x42C2A97F (need shift right by 7 bit, so ID1 = 0x85, ID2 = 0x85, ID3 = 0x52) +#define ST7789V_ID 0x858552 +static const uint8_t ST7789V_init_seq[] = { // ST7789V init sequence + // cmd, len, data..., + LCD_SWRESET, 0, // SW reset + LCD_DISPOFF, 0, // display off + LCD_MADCTL, 1, LCD_MADCTL_MX | LCD_MADCTL_MV | LCD_MADCTL_RGB, + LCD_COLMOD, 1, 0x55, // COLMOD_PIXEL_FORMAT_SET : 16 bit pixel +//ST7789V_PORCTRL, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, +//ST7789V_GCTRL, 1, 0x35, + ST7789V_VCOMS, 1, 0x1F, // default 0x20 +//ST7789V_LCMCTRL, 1, 0x2C, + ST7789V_VDVVRHEN, 2, 0x01, 0xC3, // default 0x01, 0xFF !!! why need C3? datasheet say 0xFF +//ST7789V_VDVS, 1, 0x20, +//ST7789V_FRCTRL2, 1, 0x0F, +//ST7789V_PWCTRL1, 2, 0xA4, 0xA1, + LCD_SLPOUT, 0, // sleep out + LCD_DISPON, 0, // display on + 0 // sentinel }; -#define LCD_INIT ili9341_init_seq + +// Read display ID and detect type +static const uint8_t *get_lcd_init(void) { + uint32_t id = lcd_send_register(LCD_RDDID, 0, 0) >> 7; + if (id == ST7789V_ID) lcd_type = st7789v; + return lcd_type == ili9341_type ? ili9341_init_seq : ST7789V_init_seq; +} + +void lcd_set_rotation(uint8_t r) { + static const uint8_t lcd_rotation_const[]={ + // ILI9341 LCD_MADCTL rotation settings + (LCD_MADCTL_MV | LCD_MADCTL_BGR), + (LCD_MADCTL_MY | LCD_MADCTL_BGR), + (LCD_MADCTL_MX | LCD_MADCTL_MY | LCD_MADCTL_MV | LCD_MADCTL_BGR), + (LCD_MADCTL_MX | LCD_MADCTL_BGR), + // ST7789 LCD_MADCTL rotation settings + (LCD_MADCTL_MX | LCD_MADCTL_MV | LCD_MADCTL_RGB), + ( LCD_MADCTL_RGB), + (LCD_MADCTL_MY | LCD_MADCTL_MV | LCD_MADCTL_RGB), + (LCD_MADCTL_MX | LCD_MADCTL_MY | LCD_MADCTL_RGB) + }; + lcd_send_command(LCD_MADCTL, 1, &lcd_rotation_const[lcd_type * 4 + r]); +} + #endif #ifdef LCD_DRIVER_ST7796S -static const uint8_t ST7796S_init_seq[] = { - // SW reset - ILI9341_SOFTWARE_RESET, 0, - // display off - ILI9341_DISPLAY_OFF, 0, - // Interface Mode Control - ILI9341_RGB_INTERFACE_CONTROL, 1, 0x00, - // Frame Rate - ILI9341_FRAME_RATE_CONTROL_1, 1, 0xA, - // Display Inversion Control , 2 Dot - ILI9341_DISPLAY_INVERSION_CONTROL, 1, 0x02, - // RGB/MCU Interface Control - ILI9341_DISPLAY_FUNCTION_CONTROL, 3, 0x02, 0x02, 0x3B, - // EntryMode - ILI9341_ENTRY_MODE_SET, 1, 0xC6, - // Power Control 1 - ILI9341_POWER_CONTROL_1, 2, 0x17, 0x15, - // Power Control 2 - ILI9341_POWER_CONTROL_2, 1, 0x41, - // VCOM Control -//ILI9341_VCOM_CONTROL_1, 3, 0x00, 0x4D, 0x90, - ILI9341_VCOM_CONTROL_1, 3, 0x00, 0x12, 0x80, - // Memory Access - ILI9341_MEMORY_ACCESS_CONTROL, 1, 0x28, // landscape, BGR -// ILI9341_MEMORY_ACCESS_CONTROL, 1, 0x20, // landscape, RGB - // Interface Pixel Format, 16bpp DPI and DBI and - ILI9341_PIXEL_FORMAT_SET, 1, 0x55, - // P-Gamma -// ILI9341_POSITIVE_GAMMA_CORRECTION, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, - // N-Gamma -// ILI9341_NEGATIVE_GAMMA_CORRECTION, 15, 0x00, 0X16, 0X19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, - //Set Image Func -// 0xE9, 1, 0x00, - // Set Brightness to Max - ILI9341_WRITE_BRIGHTNESS, 1, 0xFF, - // Adjust Control - ILI9341_PUMP_RATIO_CONTROL, 4, 0xA9, 0x51, 0x2C, 0x82, - //Exit Sleep - ILI9341_SLEEP_OUT, 0x00, - // display on - ILI9341_DISPLAY_ON, 0, - 0 // sentinel +static const uint8_t ST7796S_init_seq[] = { // ST7996s init sequence + // cmd, len, data..., + LCD_SWRESET, 0, // SW reset + LCD_DISPOFF, 0, // display off + ST7796S_IFMODE, 1, 0x00, // Interface Mode Control + ST7796S_FRMCTR1, 1, 0x0A, // Frame Rate + ST7796S_DIC, 1, 0x02, // Display Inversion Control , 2 Dot + ST7796S_DFC, 3, 0x02, 0x02, 0x3B, // RGB/MCU Interface Control + ST7796S_EM, 1, 0xC6, // EntryMode + ST7796S_PWR1, 2, 0x17, 0x15, // Power Control 1 + ST7796S_PWR2, 1, 0x41, // Power Control 2 +//ST7796S_VCMPCTL, 3, 0x00, 0x4D, 0x90, + ST7796S_VCMPCTL, 3, 0x00, 0x12, 0x80, // VCOM Control + LCD_MADCTL, 1, LCD_MADCTL_MV | LCD_MADCTL_BGR, // landscape, BGR + LCD_COLMOD, 1, 0x55, // Interface Pixel Format, 16bpp +//ST7796S_PGC, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, // P-Gamma +//ST7796S_NGC, 15, 0x00, 0X16, 0X19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, // N-Gamma +//0xE9, 1, 0x00, // Set Image Func + LCD_WRDISBV, 1, 0xFF, // Set Brightness to Max +//0xF7, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control ?? + LCD_SLPOUT, 0, // sleep out + LCD_DISPON, 0, // display on + 0 // sentinel }; -#define LCD_INIT ST7796S_init_seq + +static const uint8_t *get_lcd_init(void) { + return ST7796S_init_seq; +} + +void lcd_set_rotation(uint8_t r) { + static const uint8_t ST7796S_rotation_const[]={ + (LCD_MADCTL_MV | LCD_MADCTL_BGR), + (LCD_MADCTL_MY | LCD_MADCTL_BGR), + (LCD_MADCTL_MX | LCD_MADCTL_MY | LCD_MADCTL_MV | LCD_MADCTL_BGR), + (LCD_MADCTL_MX | LCD_MADCTL_BGR) + }; + lcd_send_command(LCD_MADCTL, 1, &ST7796S_rotation_const[r]); +} #endif void lcd_init(void) { spi_init(); LCD_RESET_ASSERT; - chThdSleepMilliseconds(10); + chThdSleepMilliseconds(5); LCD_RESET_NEGATE; - const uint8_t *p; - for (p = LCD_INIT; *p; ) { - ili9341_send_command(p[0], p[1], &p[2]); + chThdSleepMilliseconds(5); // need time before LCD ready after reset + const uint8_t *p = get_lcd_init(); + while (*p) { + lcd_send_command(p[0], p[1], &p[2]); p += 2 + p[1]; chThdSleepMilliseconds(2); } lcd_clear_screen(); } -static void ili9341_setWindow(int x, int y, int w, int h, uint16_t cmd) { +void lcd_setWindow(int x, int y, int w, int h, uint16_t cmd) { // Any LCD exchange start from this dmaChannelWaitCompletionRxTx(); //uint8_t xx[4] = { x >> 8, x, (x+w-1) >> 8, (x+w-1) }; //uint8_t yy[4] = { y >> 8, y, (y+h-1) >> 8, (y+h-1) }; uint32_t xx = __REV16(x | ((x + w - 1) << 16)); uint32_t yy = __REV16(y | ((y + h - 1) << 16)); - ili9341_send_command(ILI9341_COLUMN_ADDRESS_SET, 4, (uint8_t *)&xx); - ili9341_send_command(ILI9341_PAGE_ADDRESS_SET, 4, (uint8_t *)&yy); - ili9341_send_command(cmd, 0, NULL); + lcd_send_command(LCD_CASET, 4, (uint8_t *)&xx); + lcd_send_command(LCD_RASET, 4, (uint8_t *)&yy); + lcd_send_command(cmd, 0, NULL); } -#ifndef __USE_DISPLAY_DMA__ -void lcd_fill(int x, int y, int w, int h) { - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_WRITE); - uint32_t len = w * h; - do { - while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)) - ; -#if LCD_PIXEL_SIZE == 2 - SPI_WRITE_16BIT(LCD_SPI, background_color); +// Set DMA data size, depend from pixel size +#define LCD_DMA_MODE (LCD_PIXEL_SIZE == 2 ? STM32_DMA_CR_HWORD : STM32_DMA_CR_BYTE) + +// +// LCD read data functions (Copy screen data to buffer) +// +#if defined(LCD_DRIVER_ILI9341) || defined(LCD_DRIVER_ST7789) +// ILI9341 or ST7789 send data in RGB888 format, need parse it +void lcd_read_memory(int x, int y, int w, int h, uint16_t *out) { + uint16_t len = w * h; + lcd_setWindow(x, y, w, h, LCD_RAMRD); + // Set read speed (if different from write speed) + if (lcd_type == st7789v && ST7789V_SPI_RX_SPEED != LCD_SPI_SPEED) SPI_BR_SET(LCD_SPI, ST7789V_SPI_RX_SPEED); + else if ( ILI9341_SPI_RX_SPEED != LCD_SPI_SPEED) SPI_BR_SET(LCD_SPI, ILI9341_SPI_RX_SPEED); + spi_DropRx(); // Skip data from SPI rx buffer + spi_RxByte(); // require 8bit dummy clock + uint8_t *rgbbuf = (uint8_t *)out; // receive pixel data to buffer +#ifndef __USE_DISPLAY_DMA_RX__ + spi_RxBuffer(rgbbuf, len * LCD_RX_PIXEL_SIZE); + do { // Parse received data to RGB565 format + *out++ = RGB565(rgbbuf[0], rgbbuf[1], rgbbuf[2]); // read data is always 18bit + rgbbuf+= LCD_RX_PIXEL_SIZE; + } while(--len); #else - SPI_WRITE_8BIT(LCD_SPI, background_color); + len*= LCD_RX_PIXEL_SIZE; // Set data size for DMA read + spi_DMARxBuffer(rgbbuf, len, false); // Start DMA read, and not wait completion + do { // Parse received data to RGB565 format while data receive by DMA + uint16_t left = dmaChannelGetTransactionSize(LCD_DMA_RX)+LCD_RX_PIXEL_SIZE; // Get DMA data left + if (left > len) continue; // Next pixel RGB data not ready + do { // Process completed by DMA data + *out++ = RGB565(rgbbuf[0], rgbbuf[1], rgbbuf[2]); + rgbbuf+= LCD_RX_PIXEL_SIZE; + len -= LCD_RX_PIXEL_SIZE; + } while (left < len); + } while(len); + dmaChannelWaitCompletionRxTx(); // Stop DMA transfer #endif - }while(--len); -#ifdef __REMOTE_DESKTOP__ - if (sweep_mode & SWEEP_REMOTE) { - remote_region_t rd = {"fill\r\n", x, y, w, h}; - send_region(&rd, (uint8_t *)&background_color, sizeof(pixel_t)); - } + SPI_BR_SET(LCD_SPI, LCD_SPI_SPEED); // restore SPI speed + LCD_CS_HIGH; // stop read +} +#elif defined(LCD_DRIVER_ST7796S) +// ST7796S send data in RGB565 format, not need parse +void lcd_read_memory(int x, int y, int w, int h, uint16_t *out) { + uint16_t len = w * h; + lcd_setWindow(x, y, w, h, LCD_RAMRD); + // Set read speed (if need different) + if (LCD_SPI_RX_SPEED != LCD_SPI_SPEED) SPI_BR_SET(LCD_SPI, LCD_SPI_RX_SPEED); + spi_DropRx(); // Skip data from rx buffer + spi_RxByte(); // require 8bit dummy clock + // receive pixel data to buffer +#ifndef __USE_DISPLAY_DMA_RX__ + spi_RxBuffer((uint8_t *)out, len * 2); +#else + spi_DMARxBuffer((uint8_t *)out, len * 2, true); #endif + // restore speed if need + if (LCD_SPI_RX_SPEED != LCD_SPI_SPEED) SPI_BR_SET(LCD_SPI, LCD_SPI_SPEED); + LCD_CS_HIGH; } - -void lcd_bulk(int x, int y, int w, int h) { - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_WRITE); - spi_TxBuffer((uint8_t *)spi_buffer, w * h * sizeof(pixel_t)); -#ifdef __REMOTE_DESKTOP__ - if (sweep_mode & SWEEP_REMOTE) { - remote_region_t rd = {"bulk\r\n", x, y, w, h}; - send_region(&rd, (uint8_t *)spi_buffer, w * h * sizeof(pixel_t)); - } #endif + +void lcd_set_flip(bool flip) { + dmaChannelWaitCompletionRxTx(); + lcd_set_rotation(flip ? DISPLAY_ROTATION_180 : DISPLAY_ROTATION_0); } -#else -// -// Use DMA for send data -// -#define LCD_DMA_MODE (LCD_PIXEL_SIZE == 2 ? STM32_DMA_CR_HWORD : STM32_DMA_CR_BYTE) -// Fill region by some color -void lcd_fill(int x, int y, int w, int h) { - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_WRITE); - dmaChannelSetMemory(LCD_DMA_TX, &background_color); - uint32_t len = w * h, delta; - while(len) { - delta = len > 0xFFFF ? 0xFFFF : len; // DMA can send only 65535 data in one run - dmaChannelSetTransactionSize(LCD_DMA_TX, delta); - dmaChannelSetMode(LCD_DMA_TX, txdmamode | LCD_DMA_MODE | STM32_DMA_CR_EN); - dmaChannelWaitCompletion(LCD_DMA_TX); - len-=delta; - } -#ifdef __REMOTE_DESKTOP__ - if (sweep_mode & SWEEP_REMOTE) { - remote_region_t rd = {"fill\r\n", x, y, w, h}; - send_region(&rd, (uint8_t *)&background_color, sizeof(pixel_t)); - } -#endif +// Wait completion before next data send +#ifndef lcd_bulk_finish +void lcd_bulk_finish(void) { + dmaChannelWaitCompletion(LCD_DMA_TX); // Wait DMA +//while (SPI_IN_TX_RX(LCD_SPI)); // Wait tx } +#endif -static void ili9341_DMA_bulk(int x, int y, int w, int h, pixel_t *buffer) { - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_WRITE); +static void lcd_bulk_buffer(int x, int y, int w, int h, pixel_t *buffer) { + lcd_setWindow(x, y, w, h, LCD_RAMWR); +#ifdef __USE_DISPLAY_DMA__ dmaChannelSetMemory(LCD_DMA_TX, buffer); dmaChannelSetTransactionSize(LCD_DMA_TX, w * h); dmaChannelSetMode(LCD_DMA_TX, txdmamode | LCD_DMA_MODE | STM32_DMA_CR_MINC | STM32_DMA_CR_EN); +#else + spi_TxBuffer((uint8_t *)buffer, w * h * sizeof(pixel_t)); +#endif + #ifdef __REMOTE_DESKTOP__ if (sweep_mode & SWEEP_REMOTE) { remote_region_t rd = {"bulk\r\n", x, y, w, h}; @@ -665,129 +667,56 @@ static void ili9341_DMA_bulk(int x, int y, int w, int h, pixel_t *buffer) { #endif } -// Copy spi_buffer to region, wait completion after -void lcd_bulk(int x, int y, int w, int h) { - ili9341_DMA_bulk(x, y, w, h, spi_buffer); // Send data - dmaChannelWaitCompletion(LCD_DMA_TX); // Wait -} - -// Used only in double buffer mode -#ifndef lcd_get_cell_buffer -#define LCD_BUFFER_1 0x01 -#define LCD_DMA_RUN 0x02 -static uint8_t LCD_dma_status = 0; -// Return free buffer for render -pixel_t *lcd_get_cell_buffer(void) { - return &spi_buffer[(LCD_dma_status&LCD_BUFFER_1) ? SPI_BUFFER_SIZE/2 : 0]; -} -#endif - -// Wait completion before next data send -#ifndef lcd_bulk_finish -void lcd_bulk_finish(void) { - dmaChannelWaitCompletion(LCD_DMA_TX); // Wait DMA -//while (SPI_IN_TX_RX(LCD_SPI)); // Wait tx -} -#endif - // Copy part of spi_buffer to region, no wait completion after if buffer count !=1 #ifndef lcd_bulk_continue void lcd_bulk_continue(int x, int y, int w, int h) { - lcd_bulk_finish(); // Wait DMA - ili9341_DMA_bulk(x, y, w, h, lcd_get_cell_buffer()); // Send new cell data + lcd_bulk_buffer(x, y, w, h, lcd_get_cell_buffer()); // Send new cell data LCD_dma_status^=LCD_BUFFER_1; // Switch buffer } #endif -#endif -#ifdef LCD_DRIVER_ILI9341 -// ILI9341 send data in RGB888 format, need parse it -// Copy ILI9341 screen data to buffer -void lcd_read_memory(int x, int y, int w, int h, uint16_t *out) { - uint16_t len = w * h; - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_READ); - // Skip data from rx buffer - spi_DropRx(); - // Set read speed (if need different) -#ifdef LCD_SPI_RX_SPEED - SPI_BR_SET(LCD_SPI, LCD_SPI_RX_SPEED); -#endif - // require 8bit dummy clock - spi_RxByte(); - // receive pixel data to buffer -#ifndef __USE_DISPLAY_DMA_RX__ - spi_RxBuffer((uint8_t *)out, len * LCD_RX_PIXEL_SIZE); - // Parse received data to RGB565 format - uint8_t *rgbbuf = (uint8_t *)out; - do { - uint8_t r, g, b; - // read data is always 18bit - r = rgbbuf[0]; - g = rgbbuf[1]; - b = rgbbuf[2]; - *out++ = RGB565(r, g, b); - rgbbuf += LCD_RX_PIXEL_SIZE; - }while(--len); -#else - // Set data size for DMA read - len*= LCD_RX_PIXEL_SIZE; - // Start DMA read, and not wait completion - spi_DMARxBuffer((uint8_t *)out, len, false); - // Parse received data to RGB565 format while data receive by DMA - uint8_t *rgbbuf = (uint8_t *)out; - do { - uint16_t left = dmaChannelGetTransactionSize(LCD_DMA_RX)+LCD_RX_PIXEL_SIZE; // Get DMA data left - if (left > len) continue; // Next pixel RGB data not ready - do { // Process completed by DMA data - uint8_t r = rgbbuf[0]; // read data is always 18bit in RGB888 format - uint8_t g = rgbbuf[1]; - uint8_t b = rgbbuf[2]; - *out++ = RGB565(r, g, b); - rgbbuf+= LCD_RX_PIXEL_SIZE; - len -= LCD_RX_PIXEL_SIZE; - } while (left < len); - } while(len); - dmaChannelWaitCompletionRxTx(); // Wait DMA completion and stop it -#endif - // restore speed if need -#ifdef LCD_SPI_RX_SPEED - SPI_BR_SET(LCD_SPI, LCD_SPI_SPEED); -#endif - LCD_CS_HIGH; +// Copy spi_buffer to region, wait completion after +void lcd_bulk(int x, int y, int w, int h) { + lcd_bulk_buffer(x, y, w, h, spi_buffer); // Send data + lcd_bulk_finish(); // Wait } -#endif -#ifdef LCD_DRIVER_ST7796S -// ST7796S send data in RGB565 format, not need parse it -// Copy ST7796S screen data to buffer -void lcd_read_memory(int x, int y, int w, int h, uint16_t *out) { - uint16_t len = w * h; - ili9341_setWindow(x, y, w, h, ILI9341_MEMORY_READ); - // Skip data from rx buffer - spi_DropRx(); - // Set read speed (if need different) -#ifdef LCD_SPI_RX_SPEED - SPI_BR_SET(LCD_SPI, LCD_SPI_RX_SPEED); -#endif - // require 8bit dummy clock - spi_RxByte(); - // receive pixel data to buffer -#ifndef __USE_DISPLAY_DMA_RX__ - spi_RxBuffer((uint8_t *)out, len * 2); +//****************************************************************************** +// Display draw functions +//****************************************************************************** +// Fill region by some color +void lcd_fill(int x, int y, int w, int h) { + lcd_setWindow(x, y, w, h, LCD_RAMWR); + uint32_t len = w * h; +#ifdef __USE_DISPLAY_DMA__ + dmaChannelSetMemory(LCD_DMA_TX, &background_color); + while(len) { + uint32_t delta = len > 0xFFFF ? 0xFFFF : len; // DMA can send only 65535 data in one run + dmaChannelSetTransactionSize(LCD_DMA_TX, delta); + dmaChannelSetMode(LCD_DMA_TX, txdmamode | LCD_DMA_MODE | STM32_DMA_CR_EN); + dmaChannelWaitCompletion(LCD_DMA_TX); + len-=delta; + } #else - spi_DMARxBuffer((uint8_t *)out, len * 2, true); + do { + while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)) + ; + if (LCD_PIXEL_SIZE == 2) SPI_WRITE_16BIT(LCD_SPI, background_color); + else SPI_WRITE_8BIT(LCD_SPI, background_color); + } while(--len); #endif - // restore speed if need -#ifdef LCD_SPI_RX_SPEED - SPI_BR_SET(LCD_SPI, LCD_SPI_SPEED); + +#ifdef __REMOTE_DESKTOP__ + if (sweep_mode & SWEEP_REMOTE) { + remote_region_t rd = {"fill\r\n", x, y, w, h}; + send_region(&rd, (uint8_t *)&background_color, sizeof(pixel_t)); + } #endif - LCD_CS_HIGH; } -#endif #if 0 static void lcd_pixel(int x, int y, uint16_t color) { - ili9341_setWindow(x0, y0, 1, 1, ILI9341_MEMORY_WRITE); + lcd_setWindow(x, y, 1, 1, LCD_RAMWR); while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)); SPI_WRITE_16BIT(LCD_SPI, color); } @@ -800,7 +729,7 @@ void lcd_line(int x0, int y0, int x1, int y1) { int dy = (y1 - y0), sy = 1; if (dy < 0) {dy = -dy; sy = -1;} int err = -((dx + dy) < 0 ? dx : dy) / 2; while (1) { - ili9341_setWindow(x0, y0, LCD_WIDTH-x0, 1, ILI9341_MEMORY_WRITE); // prepare send Horizontal line + lcd_setWindow(x0, y0, LCD_WIDTH-x0, 1, LCD_RAMWR); // prepare send Horizontal line while (1) { while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)); SPI_WRITE_16BIT(LCD_SPI, foreground_color); // Send color @@ -817,7 +746,6 @@ void lcd_clear_screen(void) { lcd_fill(0, 0, LCD_WIDTH, LCD_HEIGHT); } - void lcd_set_foreground(uint16_t fg_idx) { foreground_color = GET_PALTETTE_COLOR(fg_idx); } @@ -831,20 +759,8 @@ void lcd_set_colors(uint16_t fg_idx, uint16_t bg_idx) { background_color = GET_PALTETTE_COLOR(bg_idx); } -void lcd_set_flip(bool flip) { - dmaChannelWaitCompletionRxTx(); - uint8_t memAcc = flip ? DISPLAY_ROTATION_180 : DISPLAY_ROTATION_0; - lcd_send_command(ILI9341_MEMORY_ACCESS_CONTROL, 1, &memAcc); -} - -void ili9341_set_rotation(uint8_t r) { - // static const uint8_t rotation_const[]={DISPLAY_ROTATION_0, DISPLAY_ROTATION_90, - // DISPLAY_ROTATION_180, DISPLAY_ROTATION_270}; - ili9341_send_command(ILI9341_MEMORY_ACCESS_CONTROL, 1, &r); -} - void lcd_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *b) { -#if 1 // Use this for remote desctop (in this case bulk operation send to remote) +#if 1 // Use this for remote desktop (in this case bulk operation send to remote) pixel_t *buf = spi_buffer; uint8_t bits = 0; for (uint32_t c = 0; c < height; c++) { @@ -857,7 +773,7 @@ void lcd_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, con lcd_bulk(x, y, width, height); #else uint8_t bits = 0; - ili9341_setWindow(x, y, width, height, ILI9341_MEMORY_WRITE); + lcd_setWindow(x, y, width, height, LCD_RAMWR); for (uint32_t c = 0; c < height; c++) { for (uint32_t r = 0; r < width; r++) { if ((r&7) == 0) bits = *b++; @@ -890,10 +806,8 @@ void lcd_drawstring(int16_t x, int16_t y, const char *str) typedef struct { const void *vmt; - int16_t start_x; - int16_t start_y; - int16_t x; - int16_t y; + int16_t start_x, start_y; + int16_t x, y; uint16_t state; } lcdPrintStream; @@ -966,19 +880,19 @@ int lcd_printfV(int16_t x, int16_t y, const char *fmt, ...) { lcdPrintStream ps = {&lcd_vmt, x, y, x, y, 0}; lcd_set_foreground(LCD_FG_COLOR); lcd_set_background(LCD_BG_COLOR); - ili9341_set_rotation(DISPLAY_ROTATION_270); + lcd_set_rotation(DISPLAY_ROTATION_270); // Performing the print operation using the common code. va_list ap; va_start(ap, fmt); int retval = chvprintf((BaseSequentialStream *)(void *)&ps, fmt, ap); va_end(ap); - ili9341_set_rotation(DISPLAY_ROTATION_0); + lcd_set_rotation(DISPLAY_ROTATION_0); // Return number of bytes that would have been written. return retval; } void lcd_blitBitmapScale(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t size, const uint8_t *b) { - ili9341_setWindow(x, y, w * size, h * size, ILI9341_MEMORY_WRITE); + lcd_setWindow(x, y, w * size, h * size, LCD_RAMWR); for (int c = 0; c < h; c++) { const uint8_t *ptr = b; uint8_t bits = 0; for (int i = 0; i < size; i++) { @@ -998,7 +912,7 @@ void lcd_blitBitmapScale(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_ int lcd_drawchar_size(uint8_t ch, int x, int y, uint8_t size) { const uint8_t *char_buf = FONT_GET_DATA(ch); uint16_t w = FONT_GET_WIDTH(ch); -#if 1 +#if 1 // Use this for remote desctop (in this case bulk operation send to remote) pixel_t *buf = spi_buffer; for (uint32_t c = 0; c < FONT_GET_HEIGHT; c++, char_buf++) { for (uint32_t i = 0; i < size; i++) { @@ -1010,7 +924,7 @@ int lcd_drawchar_size(uint8_t ch, int x, int y, uint8_t size) { } lcd_bulk(x, y, w * size, FONT_GET_HEIGHT * size); #else - ili9341_setWindow(x, y, w * size, FONT_GET_HEIGHT * size, ILI9341_MEMORY_WRITE); + lcd_setWindow(x, y, w * size, FONT_GET_HEIGHT * size, LCD_RAMWR); for (int c = 0; c < FONT_GET_HEIGHT; c++, char_buf++) { for (int i = 0; i < size; i++) { uint8_t bits = *char_buf; @@ -1071,7 +985,7 @@ void ili9341_test(int mode) { } break; case 2: - //ili9341_send_command(0x55, 0xff00); + //lcd_send_command(0x55, 0xff00); ili9341_pixel(64, 64, 0xaa55); break; #endif diff --git a/main.c b/main.c index 788e91c..5346038 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 2019-2023, Dmitry (DiSlord) dislordlive@gmail.com + * Copyright (c) 2019-2025, zeenko.tech + * Copyright (c) 2019-2025, Dmitry (DiSlord) dislordlive@gmail.com * Based on TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com * All rights reserved. * @@ -117,6 +118,7 @@ static int set_frequency(freq_t freq); static void set_frequencies(freq_t start, freq_t stop, uint16_t points); static bool sweep(bool break_on_operation, uint16_t ch_mask); static void transform_domain(uint16_t ch_mask); +extern void lcd_setBrightness(uint16_t b); uint8_t sweep_mode = SWEEP_ENABLE; // current sweep point (used for continue sweep if user break) @@ -125,12 +127,12 @@ static uint16_t p_sweep = 0; float measured[2][SWEEP_POINTS_MAX][2]; #undef VERSION -#define VERSION "1.2.27" +#define VERSION "1.2.43" // Version text, displayed in Config->Version menu, also send by info command const char *info_about[]={ "Board: " BOARD_NAME, - "2019-2024 Copyright NanoVNA.com", + "2019-2025 Copyright NanoVNA.com", "based on @DiSlord @edy555 ... source", "Licensed under GPL.", "Version: " VERSION " ["\ @@ -495,38 +497,6 @@ int serial_shell_printf(const char *fmt, ...) } #endif -// -// Function used for search substring v in list -// Example need search parameter "center" in "start|stop|center|span|cw" getStringIndex return 2 -// If not found return -1 -// Used for easy parse command arguments -static int get_str_index(const char *v, const char *list) -{ - int i = 0; - while (1) { - const char *p = v; - while (1) { - char c = *list; - if (c == '|') c = 0; - if (c == *p++) { - // Found, return index - if (c == 0) return i; - list++; // Compare next symbol - continue; - } - break; // Not equal, break - } - // Set new substring ptr - while (1) { - // End of string, not found - if (*list == 0) return -1; - if (*list++ == '|') break; - } - i++; - } - return -1; -} - VNA_SHELL_FUNCTION(cmd_pause) { (void)argc; @@ -552,7 +522,7 @@ VNA_SHELL_FUNCTION(cmd_reset) if (argc == 1) { if (get_str_index(argv[0], "dfu") == 0) { shell_printf("Performing reset to DFU mode" VNA_SHELL_NEWLINE_STR); - enter_dfu(); + ui_enter_dfu(); return; } } @@ -561,88 +531,6 @@ VNA_SHELL_FUNCTION(cmd_reset) NVIC_SystemReset(); } -// Use macro, std isdigit more big -#define _isdigit(c) (c >= '0' && c <= '9') -// Rewrite universal standart str to value functions to more compact -// -// Convert string to int32 -int32_t my_atoi(const char *p) -{ - int32_t value = 0; - uint32_t c; - bool neg = false; - - if (*p == '-') {neg = true; p++;} - if (*p == '+') p++; - while ((c = *p++ - '0') < 10) - value = value * 10 + c; - return neg ? -value : value; -} - -// Convert string to uint32 -// 0x - for hex radix -// 0o - for oct radix -// 0b - for bin radix -// default dec radix -uint32_t my_atoui(const char *p) -{ - uint32_t value = 0, radix = 10, c; - if (*p == '+') p++; - if (*p == '0') { - switch (p[1]) { - case 'x': radix = 16; break; - case 'o': radix = 8; break; - case 'b': radix = 2; break; - default: goto calculate; - } - p+=2; - } -calculate: - while (1) { - c = *p++ - '0'; - // c = to_upper(*p) - 'A' + 10 - if (c >= 'A' - '0') c = (c&(~0x20)) - ('A' - '0') + 10; - if (c >= radix) return value; - value = value * radix + c; - } -} - -float -my_atof(const char *p) -{ - int neg = FALSE; - if (*p == '-') - neg = TRUE; - if (*p == '-' || *p == '+') - p++; - float x = my_atoi(p); - while (_isdigit((int)*p)) - p++; - if (*p == '.' || *p == ',') { - float d = 1.0f; - p++; - while (_isdigit((int)*p)) { - d *= 1e-1f; - x += d * (*p - '0'); - p++; - } - } - if (*p) { - int exp = 0; - if (*p == 'e' || *p == 'E') exp = my_atoi(&p[1]); - else if (*p == 'G') exp = 9; // Giga - else if (*p == 'M') exp = 6; // Mega - else if (*p == 'k') exp = 3; // kilo - else if (*p == 'm') exp = -3; // milli - else if (*p == 'u') exp = -6; // micro - else if (*p == 'n') exp = -9; // nano - else if (*p == 'p') exp =-12; // pico - if (exp > 0) do {x*= 1e+1f;} while (--exp); - if (exp < 0) do {x*= 1e-1f;} while (++exp); - } - return neg ? -x : x; -} - #ifdef __USE_SMOOTH__ VNA_SHELL_FUNCTION(cmd_smooth) { @@ -701,6 +589,7 @@ VNA_SHELL_FUNCTION(cmd_config) { "|lcshunt" // Enable LC shunt measure option "|lcseries" // Enable LC series measure option "|xtal" // Enable XTAL measure option + "|filter" // Enable filter measure option #endif #ifdef __S11_CABLE_MEASURE__ "|cable" // Enable S11 cable measure option @@ -769,7 +658,7 @@ VNA_SHELL_FUNCTION(cmd_time) dt_buf[0] = rtc_get_tr_bcd(); // TR should be read first for sync dt_buf[1] = rtc_get_dr_bcd(); // DR should be read second static const uint8_t idx_to_time[] = {6,5,4,2, 1, 0}; - static const char time_cmd[] = "y|m|d|h|min|sec"; + static const char time_cmd[] = "y|m|d|h|min|sec|ppm"; // 0 1 2 4 5 6 // time[] ={sec, min, hr, 0, day, month, year, 0} uint8_t *time = (uint8_t*)dt_buf; @@ -779,6 +668,10 @@ VNA_SHELL_FUNCTION(cmd_time) } if (argc!=2) goto usage; int idx = get_str_index(argv[0], time_cmd); + if(idx == 6) { + rtc_set_cal(my_atof(argv[1])); + return; + } uint32_t val = my_atoui(argv[1]); if (idx < 0 || val > 99) goto usage; @@ -861,26 +754,52 @@ VNA_SHELL_FUNCTION(cmd_data) shell_printf("usage: data [array]" VNA_SHELL_NEWLINE_STR); } +#ifdef __CAPTURE_RLE8__ +void capture_rle8(void) { + static const struct { + uint16_t header; + uint16_t width, height; + uint8_t bit_per_pixel, compression; + } screenshot_header = { + 0x4D42, + LCD_WIDTH, LCD_HEIGHT, + 8, 1 + }; + + uint16_t size = sizeof(config._lcd_palette); + shell_write(&screenshot_header, sizeof(screenshot_header)); // write header + shell_write(&size, sizeof(uint16_t)); // write palette block size + shell_write(config._lcd_palette, size); // write palette block + uint16_t *data = &spi_buffer[32]; // most bad pack situation increase on 1 byte every 128, so put not compressed data on 64 byte offset + for (int y = 0, idx = 0; y < LCD_HEIGHT; y++) { + lcd_read_memory(0, y, LCD_WIDTH, 1, data); // read in 16bpp format + for (int x = 0; x < LCD_WIDTH; x++) { // convert to palette mode + if (config._lcd_palette[idx] != data[x]) { // search color in palette + for (idx = 0; idx < MAX_PALETTE && config._lcd_palette[idx] != data[x]; idx++); + if (idx >= MAX_PALETTE) idx = 0; + } + ((uint8_t*)data)[x] = idx; // put palette index + } + spi_buffer[0] = packbits((char *)data, (char *)&spi_buffer[1], LCD_WIDTH); // pack + shell_write(spi_buffer, spi_buffer[0] + sizeof(uint16_t)); + } +} +#endif + VNA_SHELL_FUNCTION(cmd_capture) { -// read pixel count at one time (PART*2 bytes required for read buffer) (void)argc; (void)argv; - int y; +#ifdef __CAPTURE_RLE8__ + if (argc > 0) { capture_rle8(); return; } +#endif // Check buffer limits, if less possible reduce rows count #define READ_ROWS 2 #if (SPI_BUFFER_SIZE*LCD_PIXEL_SIZE) < (LCD_RX_PIXEL_SIZE*LCD_WIDTH*READ_ROWS) #error "Low size of spi_buffer for cmd_capture" #endif - // Text on screenshot - if (argc > 0) { - lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); - for (int i = 0; i < argc; i++) - lcd_printf(OFFSETX + CELLOFFSETX + 2, AREA_HEIGHT_NORMAL - (argc - i) * FONT_STR_HEIGHT - 2, argv[i]); - request_to_redraw(REDRAW_AREA); - } // read 2 row pixel time - for (y = 0; y < LCD_HEIGHT; y += READ_ROWS) { + for (int y = 0; y < LCD_HEIGHT; y += READ_ROWS) { // use uint16_t spi_buffer[2048] (defined in ili9341) for read buffer lcd_read_memory(0, y, LCD_WIDTH, READ_ROWS, (uint16_t *)spi_buffer); shell_write(spi_buffer, READ_ROWS * LCD_WIDTH * sizeof(uint16_t)); @@ -934,7 +853,7 @@ config_t config = { ._harmonic_freq_threshold = FREQUENCY_THRESHOLD, ._IF_freq = FREQUENCY_OFFSET, ._touch_cal = DEFAULT_TOUCH_CONFIG, - ._vna_mode = 0, // USB mode, search max + ._vna_mode = 0x10, // USB mode, search max, show grid values ._brightness = DEFAULT_BRIGHTNESS, ._dac_value = 1922, ._vbat_offset = 450, @@ -947,6 +866,8 @@ config_t config = { #ifdef __MS5351__ ._band_mode = 1, +#elif defined __ZEETK__ + ._band_mode = 2, #else ._band_mode = 0, #endif @@ -1003,10 +924,12 @@ static void load_default_properties(void) { memcpy(current_props._trace, def_trace, sizeof(def_trace)); memcpy(current_props._markers, def_markers, sizeof(def_markers)); //============================================= - current_props._electrical_delay = 0.0f; + current_props._electrical_delay[0] = 0.0f; + current_props._electrical_delay[1] = 0.0f; current_props._var_delay = 0.0f; current_props._s21_offset = 0.0f; current_props._portz = 50.0f; + current_props._cal_load_r = 50.0f; current_props._velocity_factor = 70; current_props._current_trace = 0; current_props._active_marker = 0; @@ -1074,9 +997,10 @@ static void load_settings(void) { lever_mode = bk.leveler; config._vna_mode = get_backup_data32(4) | (1<=sweep_points || break_on_operation == false) RESET_SWEEP; if (break_on_operation && mask == 0) return false; - float s, c; float data[4]; float c_data[CAL_TYPE_COUNT][2]; // Blink LED while scanning @@ -1233,9 +1160,6 @@ static bool sweep(bool break_on_operation, uint16_t mask) if (mask & (SWEEP_CH0_MEASURE|SWEEP_CH1_MEASURE)) { delay = set_frequency(frequency); interpolation_idx = mask & SWEEP_USE_INTERPOLATION ? -1 : p_sweep; - // Edelay calibration - if (mask & SWEEP_APPLY_EDELAY) - vna_sincosf(electrical_delay * frequency, &s, &c); } // CH0:REFLECTION, reset and begin measure if (mask & SWEEP_CH0_MEASURE) { @@ -1252,8 +1176,8 @@ static bool sweep(bool break_on_operation, uint16_t mask) (*sample_func)(&data[0]); // calculate reflection coefficient if (mask & SWEEP_APPLY_CALIBRATION) // Apply calibration apply_CH0_error_term(data, c_data); - if (mask & SWEEP_APPLY_EDELAY) // Apply e-delay - applyEDelay(&data[0], s, c); + if (mask & SWEEP_APPLY_EDELAY_S11) // Apply e-delay + applyEDelay(electrical_delayS11 * frequency, &data[0]); } // CH1:TRANSMISSION, reset and begin measure if (mask & SWEEP_CH1_MEASURE) { @@ -1269,8 +1193,8 @@ static bool sweep(bool break_on_operation, uint16_t mask) (*sample_func)(&data[2]); // Measure transmission coefficient if (mask & SWEEP_APPLY_CALIBRATION) // Apply calibration apply_CH1_error_term(data, c_data); - if (mask & SWEEP_APPLY_EDELAY) // Apply e-delay - applyEDelay(&data[2], s, c); + if (mask & SWEEP_APPLY_EDELAY_S21) // Apply e-delay + applyEDelay(electrical_delayS21 * frequency, &data[2]); if (mask & SWEEP_APPLY_S21_OFFSET) applyOffset(&data[2], offset); } @@ -1493,7 +1417,8 @@ VNA_SHELL_FUNCTION(cmd_scan) #endif if ((cal_status & CALSTAT_APPLY) && !(mask&SCAN_MASK_NO_CALIBRATION)) sweep_ch|= SWEEP_APPLY_CALIBRATION; - if (electrical_delay && !(mask&SCAN_MASK_NO_EDELAY )) sweep_ch|= SWEEP_APPLY_EDELAY; + if (electrical_delayS11 && !(mask&SCAN_MASK_NO_EDELAY )) sweep_ch|= SWEEP_APPLY_EDELAY_S11; + if (electrical_delayS21 && !(mask&SCAN_MASK_NO_EDELAY )) sweep_ch|= SWEEP_APPLY_EDELAY_S21; if (s21_offset && !(mask&SCAN_MASK_NO_S21OFFS )) sweep_ch|= SWEEP_APPLY_S21_OFFSET; if (needInterpolate(start, stop, sweep_points)) @@ -1905,12 +1830,21 @@ static void apply_CH0_error_term(float data[4], float c_data[CAL_TYPE_COUNT][2]) static void apply_CH1_error_term(float data[4], float c_data[CAL_TYPE_COUNT][2]) { // CAUTION: Et is inversed for efficiency - // S21a = (S21m - Ex) * Et + // S21a = (S21m - Ex) * Et` float s21mr = data[2] - c_data[ETERM_EX][0]; float s21mi = data[3] - c_data[ETERM_EX][1]; // Not made CH1 correction by CH0 data data[2] = s21mr * c_data[ETERM_ET][0] - s21mi * c_data[ETERM_ET][1]; data[3] = s21mi * c_data[ETERM_ET][0] + s21mr * c_data[ETERM_ET][1]; + if (cal_status & CALSTAT_ENHANCED_RESPONSE) { + // S21a*= 1 - Es * S11a + float esr = 1.0f - (c_data[ETERM_ES][0] * data[0] - c_data[ETERM_ES][1] * data[1]); + float esi = 0.0f - (c_data[ETERM_ES][1] * data[0] + c_data[ETERM_ES][0] * data[1]); + float re = data[2]; + float im = data[3]; + data[2] = esr * re - esi * im; + data[3] = esi * re + esr * im; +} } void @@ -1956,7 +1890,8 @@ cal_collect(uint16_t type) // if (sweep_points != POINTS_COUNT) // set_sweep_points(POINTS_COUNT); uint16_t mask = (src == 0) ? SWEEP_CH0_MEASURE : SWEEP_CH1_MEASURE; - if (electrical_delay) mask|= SWEEP_APPLY_EDELAY; +// if (electrical_delayS11) mask|= SWEEP_APPLY_EDELAY_S11; +// if (electrical_delayS21) mask|= SWEEP_APPLY_EDELAY_S21; // Measure calibration data sweep(false, mask); // Copy calibration data @@ -2037,7 +1972,7 @@ static void cal_interpolate(int idx, freq_t f, float data[CAL_TYPE_COUNT][2]){ idx = src_points; goto copy_point; } - // Search k1 + // Calculate k for linear interpolation freq_t span = cal_frequency1 - cal_frequency0; idx = (uint64_t)(f - cal_frequency0) * (uint64_t)src_points / span; uint64_t v = (uint64_t)span * idx + src_points/2; @@ -2048,7 +1983,7 @@ static void cal_interpolate(int idx, freq_t f, float data[CAL_TYPE_COUNT][2]){ // Not need interpolate if (f == src_f0) goto copy_point; - float k1 = (delta == 0) ? 0.0f : (float)(f - src_f0) / delta; + float k = (delta == 0) ? 0.0f : (float)(f - src_f0) / delta; // avoid glitch between freqs in different harmonics mode uint32_t hf0 = si5351_get_harmonic_lvl(src_f0); if (hf0 != si5351_get_harmonic_lvl(src_f1)) { @@ -2056,20 +1991,19 @@ static void cal_interpolate(int idx, freq_t f, float data[CAL_TYPE_COUNT][2]){ if (hf0 == si5351_get_harmonic_lvl(f)){ if (idx < 1) goto copy_point; // point limit idx--; - k1+= 1.0f; + k+= 1.0f; } // f in next harmonic, need extrapolate from next 2 points else { if (idx >= src_points) goto copy_point; // point limit idx++; - k1-= 1.0f; + k-= 1.0f; } } - // Interpolate by k1 - float k0 = 1.0f - k1; + // Interpolate by k for (eterm = 0; eterm < CAL_TYPE_COUNT; eterm++) { - data[eterm][0] = cal_data[eterm][idx][0] * k0 + cal_data[eterm][idx+1][0] * k1; - data[eterm][1] = cal_data[eterm][idx][1] * k0 + cal_data[eterm][idx+1][1] * k1; + data[eterm][0] = cal_data[eterm][idx][0] + k * (cal_data[eterm][idx+1][0] - cal_data[eterm][idx][0]); + data[eterm][1] = cal_data[eterm][idx][1] + k * (cal_data[eterm][idx+1][1] - cal_data[eterm][idx][1]); } return; // Direct point copy @@ -2220,12 +2154,18 @@ void set_trace_enable(int t, bool enable) request_to_redraw(REDRAW_AREA); } -void set_electrical_delay(float seconds) +void set_electrical_delay(int ch, float seconds) { - if (electrical_delay != seconds) { - electrical_delay = seconds; + if (current_props._electrical_delay[ch] == seconds) return; + current_props._electrical_delay[ch] = seconds; request_to_redraw(REDRAW_MARKER); } + +float get_electrical_delay(void) +{ + if (current_trace == TRACE_INVALID) return 0.0f; + int ch = trace[current_trace].channel; + return current_props._electrical_delay[ch]; } void set_s21_offset(float offset) @@ -2317,11 +2257,22 @@ VNA_SHELL_FUNCTION(cmd_trace) VNA_SHELL_FUNCTION(cmd_edelay) { - if (argc != 1) { - shell_printf("%f" VNA_SHELL_NEWLINE_STR, electrical_delay * (1.0f / 1e-12f)); // return in picoseconds + int ch = 0; + float value; + static const char cmd_edelay_list[] = "s11|s21"; + if (argc >= 1) { + int idx = get_str_index(argv[0], cmd_edelay_list); + if (idx == -1) value = my_atof(argv[0]); + else { + ch = idx; + if (argc != 2) goto usage; + value = my_atof(argv[0]); + } + set_electrical_delay(ch, value * 1e-12); // input value in seconds return; } - set_electrical_delay(my_atof(argv[0]) * 1e-12); // input value in seconds +usage: + shell_printf("%f" VNA_SHELL_NEWLINE_STR, current_props._electrical_delay[ch] * (1.0f / 1e-12f)); // return in picoseconds } VNA_SHELL_FUNCTION(cmd_s21offset) @@ -2386,7 +2337,7 @@ VNA_SHELL_FUNCTION(cmd_touchcal) (void)argc; (void)argv; shell_printf("first touch upper left, then lower right..."); - touch_cal_exec(); + ui_touch_cal_exec(); shell_printf("done" VNA_SHELL_NEWLINE_STR \ "touch cal params: %d %d %d %d" VNA_SHELL_NEWLINE_STR, config._touch_cal[0], config._touch_cal[1], config._touch_cal[2], config._touch_cal[3]); @@ -2397,7 +2348,7 @@ VNA_SHELL_FUNCTION(cmd_touchtest) { (void)argc; (void)argv; - touch_draw_test(); + ui_touch_draw_test(); } VNA_SHELL_FUNCTION(cmd_frequencies) @@ -2645,6 +2596,13 @@ VNA_SHELL_FUNCTION(cmd_si5351time) #ifdef ENABLE_SI5351_REG_WRITE VNA_SHELL_FUNCTION(cmd_si5351reg) { +#if 0 + (void) argc; + uint32_t reg = my_atoui(argv[0]); + uint8_t buf[1] = {0xAA}; + if (si5351_bulk_read(reg, buf, 1)) + shell_printf("si reg[%d] = 0x%02x" VNA_SHELL_NEWLINE_STR, reg, buf[0]); +#else if (argc != 2) { shell_printf("usage: si reg data" VNA_SHELL_NEWLINE_STR); return; @@ -2653,6 +2611,7 @@ VNA_SHELL_FUNCTION(cmd_si5351reg) uint8_t dat = my_atoui(argv[1]); uint8_t buf[] = { reg, dat }; si5351_bulk_write(buf, 2); +#endif } #endif @@ -2734,7 +2693,7 @@ VNA_SHELL_FUNCTION(cmd_lcd){ if (argc == 0) return; for (int i=0;i 1) text = argv[1]; if (argc > 2) header = argv[2]; - drawMessageBox(header, text, delay); + ui_message_box(header, text, delay); } #endif @@ -3005,7 +2957,7 @@ static const VNAShellCommand commands[] = #endif {"touchcal" , cmd_touchcal , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, {"touchtest" , cmd_touchtest , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"pause" , cmd_pause , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, + {"pause" , cmd_pause , CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, {"resume" , cmd_resume , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, #ifdef __SD_CARD_LOAD__ {"msg" , cmd_msg , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_LOAD}, @@ -3205,40 +3157,6 @@ static void shell_init_connection(void){ } #endif -static inline char* vna_strpbrk(char *s1, const char *s2) { - do { - const char *s = s2; - do { - if (*s == *s1) return s1; - s++; - } while (*s); - s1++; - } while(*s1); - return s1; -} - -/* - * Split line by arguments, return arguments count - */ -int parse_line(char *line, char* args[], int max_cnt) { - char *lp = line, c; - const char *brk; - uint16_t nargs = 0; - while ((c = *lp) != 0) { // While not end - if (c != ' ' && c != '\t') { // Skipping white space and tabs. - if (c == '"') {lp++; brk = "\""; } // string end is next quote or end - else { brk = " \t";} // string end is tab or space or end - if (nargs < max_cnt) args[nargs] = lp; // Put pointer in args buffer (if possible) - nargs++; // Substring count - lp = vna_strpbrk(lp, brk); // search end - if (*lp == 0) break; // Stop, end of input string - *lp = 0; // Set zero at the end of substring - } - lp++; - } - return nargs; -} - static const VNAShellCommand *VNAShell_parceLine(char *line){ // Parse and execute line shell_nargs = parse_line(line, shell_args, ARRAY_COUNT(shell_args)); @@ -3297,10 +3215,11 @@ static void VNAShell_executeLine(char *line) uint16_t cmd_flag = scp->flags; // Skip wait mutex if process UI if ((cmd_flag & CMD_RUN_IN_UI) && (sweep_mode&SWEEP_UI_MODE)) cmd_flag&=~CMD_WAIT_MUTEX; - // Wait + // Break current sweep operation + if (scp->flags & CMD_BREAK_SWEEP) operation_requested|=OP_CONSOLE; + // Add function for run on sweep end or on break sweep if (cmd_flag & CMD_WAIT_MUTEX) { shell_function = scp->sc_function; - if (scp->flags & CMD_BREAK_SWEEP) operation_requested|=OP_CONSOLE; // Wait execute command in sweep thread osalThreadEnqueueTimeoutS(&shell_thread, TIME_INFINITE); // do { @@ -3457,30 +3376,41 @@ int main(void) * SPI bus and LCD Initialize */ lcd_init(); + lcd_set_colors(LCD_TRACE_2_COLOR, LCD_BG_COLOR); + lcd_drawstring_size(BOARD_NAME, 5 , 5, 3); + lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); + lcd_drawstring(5, sFONT_GET_HEIGHT*4+10, "Starting..."); + // Set LCD display brightness +#ifdef __LCD_BRIGHTNESS__ + lcd_setBrightness(config._brightness); +#endif + /* + * SD Card init (if inserted) allow fix issues + * Some card after insert work in SDIO mode and can corrupt SPI exchange (need switch it to SPI) + */ + #ifdef __USE_SD_CARD__ + disk_initialize(0); + #endif /* * tlv320aic Initialize (audio codec) */ tlv320aic3204_init(); - chThdSleepMilliseconds(200); // Wait for aic codec start + chThdSleepMilliseconds(300); // Wait for aic codec start /* * I2S Initialize */ initI2S(rx_buffer, ARRAY_COUNT(rx_buffer) * sizeof(audio_sample_t) / sizeof(int16_t)); -/* - * SD Card init (if inserted) allow fix issues - * Some card after insert work in SDIO mode and can corrupt SPI exchange (need switch it to SPI) - */ -#ifdef __USE_SD_CARD__ - disk_initialize(0); -#endif + /* * I2C bus run on work speed */ i2c_set_timings(STM32_I2C_TIMINGR); + lcd_clear_screen(); + /* * Startup sweep thread */ @@ -3546,8 +3476,7 @@ void hard_fault_handler_c(uint32_t *sp) uint32_t psr = sp[7]; int y = 0; int x = 20; - lcd_set_background(LCD_BG_COLOR); - lcd_set_foreground(LCD_FG_COLOR); + lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); lcd_printf(x, y+=FONT_STR_HEIGHT, "SP 0x%08x", (uint32_t)sp); lcd_printf(x, y+=FONT_STR_HEIGHT, "R0 0x%08x", r0); lcd_printf(x, y+=FONT_STR_HEIGHT, "R1 0x%08x", r1); diff --git a/measure.c b/measure.c index 34722ce..321fde4 100644 --- a/measure.c +++ b/measure.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, Dmitry (DiSlord) dislordlive@gmail.com + * Copyright (c) 2019-2024, Dmitry (DiSlord) dislordlive@gmail.com * All rights reserved. * * This is free software; you can redistribute it and/or modify @@ -19,6 +19,10 @@ */ #ifdef __VNA_MEASURE_MODULE__ +// Use size optimization (module not need fast speed, better have smallest size) +#pragma GCC push_options +#pragma GCC optimize ("Os") + // Memory for measure cache data static char measure_memory[128]; @@ -44,7 +48,7 @@ typedef float (*get_value_t)(uint16_t idx); // Used bilinear interpolation, return value = frequency of this point #define MEASURE_SEARCH_LEFT -1 #define MEASURE_SEARCH_RIGHT 1 -static float measure_search_value(uint16_t *idx, float y, get_value_t get, int16_t mode) { +static float measure_search_value(uint16_t *idx, float y, get_value_t get, int16_t mode, int16_t marker_idx) { uint16_t x = *idx; float y1, y2, y3; y1 = y2 = y3 = get(x); @@ -58,6 +62,7 @@ static float measure_search_value(uint16_t *idx, float y, get_value_t get, int16 if (x >= sweep_points) return 0; x-=mode; *idx = x; + set_marker_index(marker_idx, x); // Now y1 > y, y2 > y, y3 <= y or y1 < y, y2 < y, y3 >= y const float a = 0.5f * (y1 + y3) - y2; const float b = 0.5f * (y3 - y1); @@ -101,11 +106,11 @@ static float search_peak_value(uint16_t *xp, get_value_t get, bool mode){ return c - b * b / a; } -static float bilinear_interpolation(float y1, float y2, float y3, float k1){ +static float bilinear_interpolation(float y1, float y2, float y3, float x) { const float a = 0.5f * (y1 + y3) - y2; const float b = 0.5f * (y3 - y1); const float c = y2; - return a * k1*k1 + b * k1 + c; + return a * x * x + b * x + c; } static bool measure_get_value(uint16_t ch, freq_t f, float *data){ @@ -135,6 +140,62 @@ static bool measure_get_value(uint16_t ch, freq_t f, float *data){ return true; } +//================================================================================ +// Parabolic regression calculation: +// all matrix points is SUMM(x^n) +// N - points count, so SUMM(x^0) = N +// | x^0 x^1 x^2 | | a | |x^0 * y| +// | x^1 x^2 x^3 | * | b | = |x^1 * y| +// | x^2 x^3 x^4 | | c | |x^2 * y| +// +// f(x) = a + b * x + c * x * x +void parabolic_regression(int N, get_value_t getx, get_value_t gety, float *result) { + float x, y, xx, xy, xxy, xxx, xxxx, _x, _y, _xx, _xy; + x = y = xx = xy = xxy = xxx = xxxx = 0.0f; + for (int i = 0; i < N; ++i) { + _x = getx(i); _y = gety(i); // Get x and y + _xx = _x*_x; _xy = _x*_y; + x += _x; y += _y; // SUMM(x^1) and SUMM(x^0 * y) + xx += _xx; xy += _xy; // SUMM(x^2) and SUMM(x^1 * y) + xxx += _x*_xx; xxy += _x*_xy; // SUMM(x^3) and SUMM(x^2 * y) + xxxx+= _xx*_xx; // SUMM(x^4) + } + float xm = x / N, ym = y / N, xxm = xx / N, a, b, c; + xxxx-= xx*xxm; + xxx -= xx* xm; xxy -= xx* ym; + xx -= x* xm; xy -= x* ym; + c = (xx *xxy - xxx* xy) / (xxxx*xx - xxx*xxx); + b = (xxxx* xy - xxx*xxy) / (xxxx*xx - xxx*xxx); + a = ym - b*xm - c*xxm; + result[0] = a; + result[1] = b; + result[2] = c; +} + +//================================================================================ +// Linear regression calculation +// all matrix points is SUMM(x^n) +// N - points count, so SUMM(x^0) = N +// | x^0 x^1 | | a | |x^0 * y| +// | x^1 x^2 | * | b | = |x^1 * y| +// +// f(x) = a + b * x +void linear_regression(int N, get_value_t getx, get_value_t gety, float *result) { + float x, y, xx, xy, _x, _y, _xx, _xy; + x = y = xx = xy = 0.0f; + for (int i = 0; i < N; ++i) { + _x = getx(i); _y = gety(i); // Get x and y + _xx = _x*_x; _xy = _x*_y; + x += _x; y += _y; // SUMM(x^1) and SUMM(x^0 * y) + xx += _xx; xy += _xy; // SUMM(x^2) and SUMM(x^1 * y) + } + float xm = x / N, ym = y / N, a, b; + b = (xy - x * ym) / (xx - x * xm); + a = ym - b * xm; + result[0] = a; + result[1] = b; +} + #ifdef __USE_LC_MATCHING__ // calculate physical component values to match an impendace to 'ref_impedance' (ie 50R) typedef struct @@ -275,10 +336,7 @@ static void lc_match_x_str(uint32_t FHz, float X, int xp, int yp) } // Render L/C match to cell -static void draw_lc_match(int x0, int y0) -{ - int xp = STR_MEASURE_X - x0; - int yp = STR_MEASURE_Y - y0; +static void draw_lc_match(int xp, int yp) { cell_printf(xp, yp, "L/C match for source Z0 = %0.1f" S_OHM, lc_match_array->R0); #if 0 yp += STR_MEASURE_HEIGHT; @@ -310,12 +368,12 @@ typedef struct { const char *header; freq_t freq; // resonant frequency freq_t freq1; // fp + uint32_t df; // delta f = freq1 - freq float l; float c; float c1; // capacitor parallel float r; float q; // Q factor - // freq_t f1; // freq_t f2; // float tan45; @@ -334,6 +392,10 @@ static float s21tan(uint16_t i) { return im/re; // tan(S21) } +static float s21logmag(uint16_t i) { + return logmag(i, measured[1][i]); +} + // Phase Shift Measurement // https://www.mikrocontroller.net/attachment/473317/Crystal_Motional_Parameters.pdf static void analysis_lcshunt(void) { @@ -351,15 +413,13 @@ static void analysis_lcshunt(void) { // s21_measure->tan45 = tan45; // -45 degree search at left x2 = xp; - float f1 = measure_search_value(&x2, -tan45, s21tan, MEASURE_SEARCH_LEFT); + float f1 = measure_search_value(&x2, -tan45, s21tan, MEASURE_SEARCH_LEFT, 1); if (f1 == 0) return; - set_marker_index(1, x2); // +45 degree search at right x2 = xp; - float f2 = measure_search_value(&x2, tan45, s21tan, MEASURE_SEARCH_RIGHT); + float f2 = measure_search_value(&x2, tan45, s21tan, MEASURE_SEARCH_RIGHT, 2); if (f2 == 0) return; - set_marker_index(2, x2); // L, C, Q calculations float bw = f2 - f1; @@ -384,15 +444,13 @@ static void analysis_lcseries(void) { const float tan45 = 1.0f; // tang(45) = 1.0f // Lookup +45 phase at left of xp index x2 = xp; - float f1 = measure_search_value(&x2, tan45, s21tan, MEASURE_SEARCH_LEFT); + float f1 = measure_search_value(&x2, tan45, s21tan, MEASURE_SEARCH_LEFT, 1); if (f1 == 0) return; // not found - set_marker_index(1, x2); // Lookup -45 phase at right of xp index x2 = xp; - float f2 = measure_search_value(&x2, -tan45, s21tan, MEASURE_SEARCH_RIGHT); + float f2 = measure_search_value(&x2, -tan45, s21tan, MEASURE_SEARCH_RIGHT, 2); if (f2 == 0) return; // not found - set_marker_index(2, x2); // L, C, Q calculation float bw = f2 - f1; @@ -421,34 +479,33 @@ static void analysis_xtalseries(void) { freq_t freq1 = getFrequency(xp); if(freq1 < s21_measure->freq) return; s21_measure->freq1 = freq1; + s21_measure->df = s21_measure->freq1 - s21_measure->freq; // df = f * c / (2 * c1) => c1 = f * c / (2 * df) - s21_measure->c1 = s21_measure->c * s21_measure->freq / (2.0f * (s21_measure->freq1 - s21_measure->freq)); + s21_measure->c1 = s21_measure->c * s21_measure->freq / (2.0f * s21_measure->df); } -static void draw_serial_result(int x0, int y0){ - int xp = STR_MEASURE_X - x0; - int yp = STR_MEASURE_Y - y0; +static void draw_serial_result(int xp, int yp) { cell_printf(xp, yp, s21_measure->header); + yp+=STR_MEASURE_HEIGHT; if (s21_measure->freq == 0 && s21_measure->freq1 == 0) { - cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Not found"); + cell_printf(xp, yp, "Not found"); return; } if (s21_measure->freq) { - cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Fs=%q" S_Hz, s21_measure->freq); + cell_printf(xp, yp , "Fs=%q" S_Hz, s21_measure->freq); cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Lm=%F" S_HENRY " Cm=%F" S_FARAD " Rm=%F" S_OHM, s21_measure->l, s21_measure->c, s21_measure->r); cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Q=%.3f", s21_measure->q); // cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "tan45=%.4f", s21_measure->tan45); // cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "F1=%q" S_Hz " F2=%q" S_Hz, s21_measure->f1, s21_measure->f2); } if (s21_measure->freq1){ - cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Fp=%q" S_Hz, s21_measure->freq1); + cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Fp=%q" S_Hz " " S_DELTA "F=%d", s21_measure->freq1, s21_measure->df); cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Cp=%F" S_FARAD, s21_measure->c1); } } -static void prepare_series(uint8_t type, uint8_t update_mask) -{ +static void prepare_series(uint8_t type, uint8_t update_mask) { (void)update_mask; uint16_t n; // for detect completion @@ -465,13 +522,124 @@ static void prepare_series(uint8_t type, uint8_t update_mask) STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + n * STR_MEASURE_HEIGHT); markmap_all_markers(); } + +enum {_3dB = 0, _6dB, _10dB, _20dB/*, _60dB*/, _end}; +static const float filter_att[_end] = {3.0f , 6.0f, 10.0f, 20.0f/*, 60.0f*/}; +typedef struct { + float f[_end]; // freq array for -3, -6, -10, -20, -60 dB logmag + float decade; + float octave; +} s21_pass; + +typedef struct { + float fmax; + float vmax; + s21_pass lo_pass; + s21_pass hi_pass; + // Band pass filter data + float f_center; + float bw_3dB; + float bw_6dB; + float q; +} s21_filter_measure_t; +static s21_filter_measure_t *s21_filter = (s21_filter_measure_t *)measure_memory; + +static void draw_s21_pass(int xp, int yp, s21_pass *p, const char *name) { + cell_printf(xp, yp, name); + if (p->f[_3dB]) cell_printf(xp, yp + STR_MEASURE_HEIGHT, "%.6F" S_Hz, p->f[_3dB]); + if (p->f[_6dB]) cell_printf(xp, yp + 2*STR_MEASURE_HEIGHT, "%.6F" S_Hz, p->f[_6dB]); + yp+= 3 * STR_MEASURE_HEIGHT; + if (p->decade) { + cell_printf(xp, yp , "%F" S_dB "/dec", p->decade); + cell_printf(xp, yp + STR_MEASURE_HEIGHT, "%F" S_dB "/oct", p->octave); + } +} + +#define S21_MEASURE_FILTER_THRESHOLD -50.0f +static void draw_filter_result(int xp, int yp){ + cell_printf(xp, yp, "S21 FILTER"); + if (s21_filter->vmax < S21_MEASURE_FILTER_THRESHOLD) return; + yp+= STR_MEASURE_HEIGHT; + // f: ___.___MHz (xxxdB) + // Bw(-3dB): ___.___MHz + // Bw(-6dB): ___.___MHz + // Q: xxx + if (s21_filter->f_center) { + cell_printf(xp, yp, "f: %.6F" S_Hz " (%F" S_dB ")", s21_filter->f_center, s21_filter->vmax); + cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Bw (-%d" S_dB "): %.6F" S_Hz, 3, s21_filter->bw_3dB); + cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Bw (-%d" S_dB "): %.6F" S_Hz, 6, s21_filter->bw_6dB); + cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Q: %F", s21_filter->q); + } else { + cell_printf(xp, yp, "f: %.6F" S_Hz " (%F" S_dB ")", s21_filter->fmax, s21_filter->vmax); + } + // Lo/Hi pass data show + const int width0 = 3 * STR_MEASURE_WIDTH * 2 / 10; // 1 column width 20% + const int width1 = 3 * STR_MEASURE_WIDTH * 4 / 10; // 2 and 3 column 40% + // 20% | 40% | 40% + // Low-side High-side + // f(-3) ___.___MHz ___.___MHz + // f(-6) ___.___MHz ___.___MHz + // Roll: ___dB/dec ___dB/oct + // ___dB/dec ___dB/oct + if (s21_filter->lo_pass.f[_3dB] || s21_filter->hi_pass.f[_3dB]) { + yp+= STR_MEASURE_HEIGHT; + cell_printf(xp, yp + 1 * STR_MEASURE_HEIGHT, "f(-%d):", 3); + cell_printf(xp, yp + 2 * STR_MEASURE_HEIGHT, "f(-%d):", 6); + cell_printf(xp, yp + 3 * STR_MEASURE_HEIGHT, "Roll:"); + xp+= width0; + if (s21_filter->hi_pass.f[_3dB]) {draw_s21_pass(xp, yp, &s21_filter->hi_pass, s21_filter->f_center ? "Low-side" : "High-pass"); xp+= width1; } + if (s21_filter->lo_pass.f[_3dB]) {draw_s21_pass(xp, yp, &s21_filter->lo_pass, s21_filter->f_center ? "High-side" : "Low-pass"); } + } +} + +static void find_filter_pass(float max, s21_pass *p, uint16_t idx, int16_t mode) { + // Fill frequency for all in filter_att (-3, -6, -10, -20, -60 dB) logmag + for (int i = 0; i < _end; i++) + p->f[i] = measure_search_value(&idx, max - filter_att[i], s21logmag, mode, i == 0 ? (mode == MEASURE_SEARCH_LEFT ? 1 : 2) : MARKER_INVALID); + // Reset Roll-off data + p->decade = p->octave = 0.0f; + if (p->f[_10dB] != 0 && p->f[_20dB] != 0) { + float k = vna_fabsf(vna_logf(p->f[_20dB]) - vna_logf(p->f[_10dB])); + // decade = delta / log10(f1 / f2) = delta / (log10(f1) - log10(f2)) = delta * log(10) / (log(f1) - log(f2)) + p->decade = (10.0f * logf(10.0f)) / k; + // octave = decade * log10(2) = decade * log(2) / log(10) = delta * log(2) / (log(f1) - log(f2)) + p->octave = (10.0f * logf( 2.0f)) / k; + } +} + +static void prepare_filter(uint8_t type, uint8_t update_mask) { + (void)type; + (void)update_mask; + uint16_t xp = 0; + s21_filter->vmax = search_peak_value(&xp, s21logmag, MEASURE_SEARCH_MAX); // Maximum search + // If maximum < 50dB, no filter detected + if (s21_filter->vmax >= S21_MEASURE_FILTER_THRESHOLD) { + set_marker_index(0, xp); // Put marker on maximum value point + s21_filter->fmax = getFrequency(xp); // Get maximum value frequency + find_filter_pass(s21_filter->vmax, &s21_filter->hi_pass, xp, MEASURE_SEARCH_LEFT); // Search High-pass filter data (or Low side for bandpass) + find_filter_pass(s21_filter->vmax, &s21_filter->lo_pass, xp, MEASURE_SEARCH_RIGHT);// Search Low-pass filter data (or High side for bandpass) + // Calculate Band-pass filter data + s21_filter->f_center = s21_filter->lo_pass.f[_3dB] * s21_filter->hi_pass.f[_3dB]; // Center frequency (if 0, one or both points not found) + if (s21_filter->f_center) { + s21_filter->bw_3dB = s21_filter->lo_pass.f[_3dB] - s21_filter->hi_pass.f[_3dB]; + s21_filter->bw_6dB = s21_filter->lo_pass.f[_6dB] - s21_filter->hi_pass.f[_6dB]; + s21_filter->f_center = vna_sqrtf(s21_filter->f_center); + s21_filter->q = s21_filter->f_center / s21_filter->bw_3dB; + } + } + // Prepare for update + invalidate_rect(STR_MEASURE_X , STR_MEASURE_Y, + STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + 10 * STR_MEASURE_HEIGHT); +} #endif // __S21_MEASURE__ #ifdef __S11_CABLE_MEASURE__ typedef struct { + float freq; float R; float len; float loss; + float mloss; float vf; float C0; float a, b, c; @@ -483,9 +651,15 @@ static float s11imag(uint16_t i) { return measured[0][i][1]; } -static void draw_s11_cable(int x0, int y0){ - int xp = STR_MEASURE_X - x0; - int yp = STR_MEASURE_Y - y0; +static float s11loss(uint16_t i) { + return -0.5f * logmag(i, measured[0][i]); +} + +static float s11index(uint16_t i) { + return vna_sqrtf(getFrequency(i) * 1e-9f); +} + +static void draw_s11_cable(int xp, int yp){ cell_printf(xp, yp, "S11 CABLE"); if (s11_cable->R){ cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Z0 = %F" S_OHM, s11_cable->R); @@ -495,11 +669,14 @@ static void draw_s11_cable(int x0, int y0){ cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "VF=%.2f%% (Length = %F" S_METRE ")", s11_cable->vf, real_cable_len); else if (s11_cable->len) cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Length = %F" S_METRE " (VF=%d%%)", s11_cable->len, velocity_factor); - cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Loss = %F" S_dB, s11_cable->loss); +//cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Loss = %F" S_dB " at %.4F" S_Hz, s11_cable->loss, s11_cable->freq); + cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Loss = %F" S_dB " at %.4F" S_Hz, s11_cable->mloss, s11_cable->freq); + float l = s11_cable->vf ? real_cable_len : s11_cable->len; + if (l) cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Att (dB/100m): %F" S_dB " at %.4F" S_Hz, s11_cable->mloss * 100.0f / l, (float)s11_cable->freq); +// cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "a: %.6F, b: %.6F, c: %.6F", s11_cable->a, s11_cable->b, s11_cable->c); } -static void prepare_s11_cable(uint8_t type, uint8_t update_mask) -{ +static void prepare_s11_cable(uint8_t type, uint8_t update_mask) { (void)type; freq_t f1; if (update_mask & MEASURE_UPD_SWEEP) { @@ -507,7 +684,7 @@ static void prepare_s11_cable(uint8_t type, uint8_t update_mask) s11_cable->len = 0.0f; s11_cable->vf = 0.0f; uint16_t x = 0; - f1 = measure_search_value(&x, 0, s11imag, MEASURE_SEARCH_RIGHT); + f1 = measure_search_value(&x, 0, s11imag, MEASURE_SEARCH_RIGHT, MARKER_INVALID); if (f1){ float electric_lengh = (SPEED_OF_LIGHT / 400.0f) / f1; s11_cable->len = velocity_factor * electric_lengh; @@ -518,14 +695,18 @@ static void prepare_s11_cable(uint8_t type, uint8_t update_mask) // s11_cable->C0 = velocity_factor / (100.0f * SPEED_OF_LIGHT * s11_cable->R); } } + parabolic_regression(sweep_points, s11index, s11loss, &s11_cable->a); } if ((update_mask & MEASURE_UPD_ALL) && active_marker != MARKER_INVALID) { int idx = markers[active_marker].index; - s11_cable->loss = vna_fabsf(logmag(idx, measured[0][idx]) / 2.0f); +// s11_cable->loss = s11loss(idx); + s11_cable->freq = (float)getFrequency(idx); + float f = s11_cable->freq * 1e-9f; + s11_cable->mloss = s11_cable->a + s11_cable->b * vna_sqrtf(f) + s11_cable->c * f; } // Prepare for update invalidate_rect(STR_MEASURE_X , STR_MEASURE_Y, - STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + 4 * STR_MEASURE_HEIGHT); + STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + 6 * STR_MEASURE_HEIGHT); } #endif // __S11_CABLE_MEASURE__ @@ -551,9 +732,7 @@ static float s11_resonance_min(uint16_t i) { return fabsf(reactance(i, measured[0][i])); } -static void draw_s11_resonance(int x0, int y0){ - int xp = STR_MEASURE_X - x0; - int yp = STR_MEASURE_Y - y0; +static void draw_s11_resonance(int xp, int yp) { cell_printf(xp, yp, "S11 RESONANCE"); if (s11_resonance->count == 0) { cell_printf(xp, yp+=STR_MEASURE_HEIGHT, "Not found"); @@ -575,8 +754,7 @@ static bool add_resonance_value(int i, uint16_t x, freq_t f) { return false; } -static void prepare_s11_resonance(uint8_t type, uint8_t update_mask) -{ +static void prepare_s11_resonance(uint8_t type, uint8_t update_mask) { (void)type; if (update_mask & MEASURE_UPD_SWEEP) { int i; @@ -584,7 +762,7 @@ static void prepare_s11_resonance(uint8_t type, uint8_t update_mask) uint16_t x = 0; // Search resonances (X == 0) for (i = 0; i < MEASURE_RESONANCE_COUNT && i < MARKERS_MAX;) { - f = measure_search_value(&x, 0.0f, s11_resonance_value, MEASURE_SEARCH_RIGHT); + f = measure_search_value(&x, 0.0f, s11_resonance_value, MEASURE_SEARCH_RIGHT, MARKER_INVALID); if (f == 0) break; if (add_resonance_value(i, x, f)) i++; @@ -603,5 +781,5 @@ static void prepare_s11_resonance(uint8_t type, uint8_t update_mask) STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + (MEASURE_RESONANCE_COUNT + 1) * STR_MEASURE_HEIGHT); } #endif //__S11_RESONANCE_MEASURE__ - +#pragma GCC pop_options #endif // __VNA_MEASURE_MODULE__ diff --git a/nanovna.h b/nanovna.h index 0b7f01d..8112c00 100644 --- a/nanovna.h +++ b/nanovna.h @@ -18,17 +18,20 @@ * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ +#pragma once #include "ch.h" -#define __MS5351__ +//#define __MS5351__ +#define __ZEETK__ // Define LCD display driver and size #if defined(NANOVNA_F303) #define LCD_DRIVER_ST7796S #define LCD_480x320 #else +// Used auto detect from ILI9341 or ST7789 #define LCD_DRIVER_ILI9341 -//#define DISPLAY_ST7789 +#define LCD_DRIVER_ST7789 #define LCD_320x240 #endif @@ -50,12 +53,16 @@ #define __USE_BACKUP__ // Add SD card support, req enable RTC (additional settings for file system see FatFS lib ffconf.h) #define __USE_SD_CARD__ +// Use unique serial string for USB +#define __USB_UID__ // If enabled serial in halconf.h, possible enable serial console control #define __USE_SERIAL_CONSOLE__ // Add show y grid line values option #define __USE_GRID_VALUES__ // Add remote desktop option #define __REMOTE_DESKTOP__ +// Add RLE8 compression capture image format +#define __CAPTURE_RLE8__ // Allow flip display #define __FLIP_DISPLAY__ // Add shadow on text in plot area (improve readable, but little slowdown render) @@ -150,7 +157,7 @@ // Frequency offset, depend from AUDIO_ADC_FREQ settings (need aligned table) // Use real time build table (undef for use constant, see comments) // Constant tables build only for AUDIO_SAMPLES_COUNT = 48 -//#define USE_VARIABLE_OFFSET +#define USE_VARIABLE_OFFSET // Maximum sweep point count (limit by flash and RAM size) #define SWEEP_POINTS_MAX 101 @@ -167,11 +174,22 @@ * main.c */ // Minimum frequency set +#ifdef __ZEETK__ +#define FREQUENCY_MIN 600 +#else #define FREQUENCY_MIN 1600 +#endif + // Maximum frequency set #define FREQUENCY_MAX 2000000000U + // Frequency threshold (max frequency for si5351, harmonic mode after) -#define FREQUENCY_THRESHOLD 290000100U +#ifdef __ZEETK__ +#define FREQUENCY_THRESHOLD 300000110U +#else +#define FREQUENCY_THRESHOLD 290000110U +#endif + // XTAL frequency on si5351 #define XTALFREQ 26000000U // Define i2c bus speed, add predefined for 400k, 600k, 900k @@ -293,6 +311,7 @@ extern float measured[2][SWEEP_POINTS_MAX][2]; #define CALSTAT_EX CALSTAT_ISOLN #define CALSTAT_APPLY (1<<8) #define CALSTAT_INTERPOLATED (1<<9) +#define CALSTAT_ENHANCED_RESPONSE (1<<10) #define ETERM_ED 0 /* error term directivity */ #define ETERM_ES 1 /* error term source match */ @@ -334,7 +353,14 @@ uint8_t get_smooth_factor(void); int32_t my_atoi(const char *p); uint32_t my_atoui(const char *p); float my_atof(const char *p); +bool strcmpi(const char *t1, const char *t2); +int get_str_index(const char *v, const char *list); int parse_line(char *line, char* args[], int max_cnt); +void swap_bytes(uint16_t *buf, int size); +int packbits(char *source, char *dest, int size); +void _delay_8t(uint32_t cycles); +inline void delayMicroseconds(uint32_t us) {_delay_8t(us*STM32_CORE_CLOCK/8);} +inline void delayMilliseconds(uint32_t ms) {_delay_8t(ms*125*STM32_CORE_CLOCK);} void pause_sweep(void); void toggle_sweep(void); @@ -382,9 +408,9 @@ extern const char *info_about[]; #if defined(NANOVNA_F303) // Generator ready delays, values in us #define DELAY_BAND_1_2 US2ST( 100) // Delay for bands 1-2 -#define DELAY_BAND_3_4 US2ST( 120) // Delay for bands 3-4 -#define DELAY_BANDCHANGE US2ST(2000) // Band changes need set additional delay after reset PLL -#define DELAY_CHANNEL_CHANGE US2ST( 100) // Switch channel delay +#define DELAY_BAND_3_4 US2ST( 200) // Delay for bands 3-4 +#define DELAY_BANDCHANGE US2ST(5000) // Band changes need set additional delay after reset PLL +#define DELAY_CHANNEL_CHANGE US2ST( 400) // Switch channel delay #define DELAY_SWEEP_START US2ST(2000) // Delay at sweep start // Delay after set new PLL values in ms, and send reset #define DELAY_RESET_PLL_BEFORE 0 // 0 (0 for disabled) @@ -392,12 +418,12 @@ extern const char *info_about[]; #else // Generator ready delays, values in us #define DELAY_BAND_1_2 US2ST( 100) // 0 Delay for bands 1-2 -#define DELAY_BAND_3_4 US2ST( 140) // 1 Delay for bands 3-4 -#define DELAY_BANDCHANGE US2ST( 800) // 2 Band changes need set additional delay after reset PLL -#define DELAY_CHANNEL_CHANGE US2ST( 100) // 3 Switch channel delay -#define DELAY_SWEEP_START US2ST( 100) // 4 Delay at sweep start +#define DELAY_BAND_3_4 US2ST( 200) // 1 Delay for bands 3-4 +#define DELAY_BANDCHANGE US2ST( 5000) // 2 Band changes need set additional delay after reset PLL +#define DELAY_CHANNEL_CHANGE US2ST( 400) // 3 Switch channel delay +#define DELAY_SWEEP_START US2ST( 2000) // 4 Delay at sweep start // Delay after before/after set new PLL values in ms -#define DELAY_RESET_PLL_BEFORE 0 // 5 0 (0 for disabled) +#define DELAY_RESET_PLL_BEFORE 0 // 5 0 (0 for disabled) #define DELAY_RESET_PLL_AFTER 4000 // 6 4000 (0 for disabled) #endif @@ -750,8 +776,10 @@ enum { KP_PLUSMINUS, KP_KEYPAD, KP_SPACE, - KP_PLUS + KP_PLUS, #endif + // Special uint8_t buttons + KP_EMPTY = 255 // Empty button }; /* @@ -799,6 +827,7 @@ enum { #define S_METRE "m" // #define S_VOLT "V" // #define S_AMPER "A" // +#define S_PPM "ppm" // // Max palette indexes in config #define MAX_PALETTE 32 @@ -919,12 +948,14 @@ enum { #ifdef __SD_CARD_DUMP_TIFF__ VNA_MODE_TIFF, // Save screenshot format (0: bmp, 1: tiff) #endif +#ifdef __USB_UID__ + VNA_MODE_USB_UID // Use unique serial string for USB +#endif }; + // Update config._vna_mode flags function -#define VNA_MODE_CLR 0 -#define VNA_MODE_SET 1 -#define VNA_MODE_TOGGLE 2 -void apply_VNA_mode(uint16_t idx, uint16_t value); +typedef enum {VNA_MODE_CLR = 0, VNA_MODE_SET, VNA_MODE_TOGGLE} vna_mode_ops; +void apply_VNA_mode(uint16_t idx, vna_mode_ops operation); #ifdef __VNA_MEASURE_MODULE__ // Measure option mode @@ -937,6 +968,7 @@ enum { MEASURE_SHUNT_LC, MEASURE_SERIES_LC, MEASURE_SERIES_XTAL, + MEASURE_FILTER, #endif #ifdef __S11_CABLE_MEASURE__ MEASURE_S11_CABLE, @@ -992,29 +1024,31 @@ typedef struct config { typedef struct properties { uint32_t magic; - freq_t _frequency0; - freq_t _frequency1; - freq_t _cal_frequency0; - freq_t _cal_frequency1; - freq_t _var_freq; + freq_t _frequency0; // sweep start frequency + freq_t _frequency1; // sweep stop frequency + freq_t _cal_frequency0; // calibration start frequency + freq_t _cal_frequency1; // calibration stop frequency + freq_t _var_freq; // frequency step by leveler (0 for auto) uint16_t _mode; // timed domain option flag and some others flags - uint16_t _sweep_points; + uint16_t _sweep_points; // points used in measure sweep int8_t _current_trace; // 0..(TRACES_MAX -1) (TRACE_INVALID for disabled) int8_t _active_marker; // 0..(MARKERS_MAX-1) (MARKER_INVALID for disabled) int8_t _previous_marker; // 0..(MARKERS_MAX-1) (MARKER_INVALID for disabled) - uint8_t _power; - uint8_t _cal_power; - uint8_t _measure; - uint16_t _cal_sweep_points; - uint16_t _cal_status; + uint8_t _power; // 0 ... 3 current output power settings + uint8_t _cal_power; // 0 ... 3 Power used in calibration + uint8_t _measure; // additional trace data calculations + uint16_t _cal_sweep_points; // points used in calibration + uint16_t _cal_status; // calibration data collected flags trace_t _trace[TRACES_MAX]; marker_t _markers[MARKERS_MAX]; uint8_t _reserved; uint8_t _velocity_factor; // 0 .. 100 % - float _electrical_delay; // picoseconds - float _var_delay; - float _s21_offset; - float _portz; + float _electrical_delay[2]; // delays for S11 and S21 traces in seconds + float _var_delay; // electrical delay step by leveler + float _s21_offset; // additional external attenuator for S21 measures + float _portz; // Used for port-z renormalization + float _cal_load_r; // Used as calibration standard LOAD R value (calculated in renormalization procedure) + uint32_t _reserved1[7]; float _cal_data[CAL_TYPE_COUNT][SWEEP_POINTS_MAX][2]; // Put at the end for faster access to others data from struct uint32_t checksum; } properties_t; @@ -1034,8 +1068,8 @@ const char *get_trace_chname(int t); // Shell config functions and macros for Serial connect, not used if Serial mode disabled void shell_update_speed(uint32_t speed); void shell_reset_console(void); - -void set_electrical_delay(float seconds); +void set_electrical_delay(int ch, float seconds); +float get_electrical_delay(void); void set_s21_offset(float offset); float groupdelay_from_array(int i, const float *v); @@ -1094,7 +1128,7 @@ void marker_search_dir(int16_t from, int16_t dir); #endif // Custom display driver panel definitions for ILI9341 -#ifdef LCD_DRIVER_ILI9341 +#if defined(LCD_DRIVER_ILI9341) || defined(LCD_DRIVER_ST7789) // LCD touch settings #define DEFAULT_TOUCH_CONFIG {530, 795, 3460, 3350} // 2.8 inch LCD panel // Define LCD pixel format (8 or 16 bit) @@ -1269,7 +1303,7 @@ void lcd_read_memory(int x, int y, int w, int h, uint16_t* out); void lcd_line(int x0, int y0, int x1, int y1); void lcd_vector_draw(int x, int y, const vector_data *v); -uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data); +uint32_t lcd_send_register(uint8_t cmd, uint8_t len, const uint8_t *data); void lcd_set_flip(bool flip); // SD Card support, discio functions for FatFS lib implemented in ili9341.c @@ -1294,7 +1328,7 @@ void testLog(void); // debug log * flash.c */ #define CONFIG_MAGIC 0x434f4e56 // Config magic value (allow reset on new config version) -#define PROPERTIES_MAGIC 0x434f4e52 // Properties magic value (allow reset on new properties version) +#define PROPERTIES_MAGIC 0x434f4e54 // Properties magic value (allow reset on new properties version) #define NO_SAVE_SLOT ((uint16_t)(-1)) extern uint16_t lastsaveid; @@ -1309,7 +1343,8 @@ extern uint16_t lastsaveid; #define cal_power current_props._cal_power #define cal_status current_props._cal_status #define cal_data current_props._cal_data -#define electrical_delay current_props._electrical_delay +#define electrical_delayS11 current_props._electrical_delay[0] +#define electrical_delayS21 current_props._electrical_delay[1] #define s21_offset current_props._s21_offset #define velocity_factor current_props._velocity_factor #define trace current_props._trace @@ -1317,6 +1352,11 @@ extern uint16_t lastsaveid; #define markers current_props._markers #define active_marker current_props._active_marker #define previous_marker current_props._previous_marker +#ifdef __VNA_Z_RENORMALIZATION__ + #define cal_load_r current_props._cal_load_r +#else + #define cal_load_r 50.0f +#endif #define props_mode current_props._mode #define domain_window (props_mode&TD_WINDOW) @@ -1373,11 +1413,11 @@ void ui_process(void); void handle_touch_interrupt(void); -void touch_cal_exec(void); -void touch_draw_test(void); -void enter_dfu(void); +void ui_touch_cal_exec(void); +void ui_touch_draw_test(void); +void ui_enter_dfu(void); -void drawMessageBox(const char *header, const char *text, uint32_t delay); +void ui_message_box(const char *header, const char *text, uint32_t delay); // Irq operation process set #define OP_NONE 0x00 diff --git a/plot.c b/plot.c index 4a06134..dc4decc 100644 --- a/plot.c +++ b/plot.c @@ -77,15 +77,6 @@ float2int(float v) } #endif -static inline int -circle_inout(int x, int y, int r) -{ - int d = x*x + y*y; - if (d < r*r - r) return 1; // in circle - if (d > r*r + r) return -1; // out circle - return 0; // on circle -} - static bool polar_grid(int x, int y) { @@ -387,8 +378,7 @@ static float reactance(int i, const float *v) { static float mod_z(int i, const float *v) { (void) i; const float z0 = PORT_Z; - const float l = get_l(1.0f - v[0], v[1]); - return z0 * vna_sqrtf(4.0f * v[0] / l + 1.0f); // always >= 0 + return z0 * vna_sqrtf(get_l(1.0f + v[0], v[1]) / get_l(1.0f - v[0], v[1])); // always >= 0 } static float phase_z(int i, const float *v) { @@ -573,7 +563,7 @@ cartesian_scale(const float *v, int16_t *xp, int16_t *yp, float scale) { const trace_info_t trace_info_list[MAX_TRACE_TYPE] = { // Type name format delta format symbol ref scale get value -[TRC_LOGMAG] = {"LOGMAG", "%.2f%s", S_DELTA "%.2f%s", S_dB, NGRIDY-1, 10.0f, logmag }, +[TRC_LOGMAG] = {"LOGMAG", "%.2f%s", S_DELTA "%.3f%s", S_dB, NGRIDY-1, 10.0f, logmag }, [TRC_PHASE] = {"PHASE", "%.2f%s", S_DELTA "%.2f%s", S_DEGREE, NGRIDY/2, 90.0f, phase }, [TRC_DELAY] = {"DELAY", "%.4F%s", "%.4F%s", S_SECOND, NGRIDY/2, 1e-9f, groupdelay_from_array}, [TRC_SMITH] = {"SMITH", NULL, NULL, "", 0, 1.00f, NULL }, // Custom @@ -836,7 +826,7 @@ static int marker_area_max(void) { for (i = 0; i < MARKERS_MAX; i++) if (markers[i].enabled) m_count++; int cnt = t_count > m_count ? t_count : m_count; int extra = 0; - if (electrical_delay != 0.0f) extra+= 2; + if (get_electrical_delay() != 0.0f) extra+= 2; if (s21_offset != 0.0f) extra+= 2; #ifdef __VNA_Z_RENORMALIZATION__ if (current_props._portz != 50.0f) extra+= 2; @@ -894,26 +884,20 @@ cell_drawline(int x0, int y0, int x1, int y1, pixel_t c) } #endif -// Give a little speedup then draw rectangular plot (50 systick on all calls, all render req 700 systick) +// Give a little speedup then draw rectangular plot // Write more difficult algorithm for search indexes not give speedup -static int -search_index_range_x(int x1, int x2, index_t *index, int *i0, int *i1) -{ - int i, j; - int head = 0; - int tail = sweep_points; - int idx_x; - +static int search_index_range_x(int x1, int x2, index_t *index, int *i0, int *i1) { + int i; + int head = 0, tail = sweep_points; // Search index point in cell while (1) { - i = (head + tail) / 2; - idx_x = index[i].x; - if (idx_x >= x2) { // index after cell + i = (head + tail)>>1; + if (index[i].x >= x2) { // index after cell if (tail == i) return false; tail = i; } - else if (idx_x < x1) { // index before cell + else if (index[i].x < x1) { // index before cell if (head == i) return false; head = i; @@ -921,20 +905,9 @@ search_index_range_x(int x1, int x2, index_t *index, int *i0, int *i1) else // index in cell (x =< idx_x < cell_end) break; } - j = i; - // Search index left from point - do { - if (j == 0) break; - j--; - } while (x1 <= index[j].x); - *i0 = j; - // Search index right from point - do { - if (i >=sweep_points-1) break; - i++; - } while (index[i].x < x2); - *i1 = i; - + *i0 = *i1 = i; + while (*i0 > 0 && x1 <= index[--*i0].x); // Search index left from point + while (*i1 < sweep_points-1 && x2 > index[++*i1].x); // Search index right from point return TRUE; } @@ -1068,8 +1041,8 @@ static uint8_t data_update = 0; #define MESAURE_S21 2 // For calculate need only S21 data #define MESAURE_ALL (MESAURE_S11 | MESAURE_S21) // For calculate need S11 and S21 data -#define MEASURE_UPD_SWEEP 1 // Recalculate on sweep done -#define MEASURE_UPD_FREQ 2 // Recalculate on marker change position +#define MEASURE_UPD_SWEEP (1<<0) // Recalculate on sweep done +#define MEASURE_UPD_FREQ (1<<1) // Recalculate on marker change position #define MEASURE_UPD_ALL (MEASURE_UPD_SWEEP | MEASURE_UPD_FREQ) // Include measure functions @@ -1091,6 +1064,7 @@ static const struct { [MEASURE_SHUNT_LC] = {MESAURE_S21, MEASURE_UPD_SWEEP, draw_serial_result, prepare_series }, [MEASURE_SERIES_LC] = {MESAURE_S21, MEASURE_UPD_SWEEP, draw_serial_result, prepare_series }, [MEASURE_SERIES_XTAL] = {MESAURE_S21, MEASURE_UPD_SWEEP, draw_serial_result, prepare_series }, + [MEASURE_FILTER] = {MESAURE_S21, MEASURE_UPD_SWEEP, draw_filter_result, prepare_filter }, #endif #ifdef __S11_CABLE_MEASURE__ [MEASURE_S11_CABLE] = {MESAURE_S11, MEASURE_UPD_ALL, draw_s11_cable, prepare_s11_cable}, @@ -1129,7 +1103,7 @@ static void cell_draw_measure(int x0, int y0){ measure_cell_cb_t measure_draw_cb = measure[current_props._measure].measure_cell; if (measure_draw_cb) { lcd_set_colors(LCD_MEASURE_COLOR, LCD_BG_COLOR); - measure_draw_cb(x0, y0); + measure_draw_cb(STR_MEASURE_X - x0, STR_MEASURE_Y - y0); } } #endif @@ -1710,6 +1684,7 @@ cell_draw_marker_info(int x0, int y0) xpos = 1 + 18 + CELLOFFSETX - x0; ypos = 1 + ((j+1)/2)*FONT_STR_HEIGHT - y0; + float electrical_delay = get_electrical_delay(); if (electrical_delay != 0.0f) { // draw electrical delay char sel = lever_mode == LM_EDELAY ? S_SARROW[0] : ' '; @@ -1789,7 +1764,8 @@ draw_cal_status(void) {'S', 0, CALSTAT_ES}, {'T', 0, CALSTAT_ET}, {'t', 0, CALSTAT_THRU}, - {'X', 0, CALSTAT_EX} + {'X', 0, CALSTAT_EX}, + {'E', 0, CALSTAT_ENHANCED_RESPONSE} }; for (i = 0; i < ARRAY_COUNT(calibration_text); i++) if (cal_status & calibration_text[i].mask) diff --git a/si5351.c b/si5351.c index fd2bfde..e4c81a4 100644 --- a/si5351.c +++ b/si5351.c @@ -25,9 +25,7 @@ // audio codec frequency clock #define CLK2_FREQUENCY AUDIO_CLOCK_REF -// Fixed PLL mode multiplier (used in band 1 for frequency 800-10k) -#define PLL_N_1 8 -// Fixed PLL mode multiplier (used in band 2 for frequency 10k-100M) +// Fixed PLL mode multiplier (used for AUDIO codec frequency generation) #define PLL_N_2 32 // I2C address on bus (only 0x60 for Si5351A in 10-Pin MSOP) @@ -98,14 +96,12 @@ void si5351_bulk_write(const uint8_t *buf, int len) } #if 0 -static bool si5351_bulk_read(uint8_t reg, uint8_t* buf, int len) -{ - i2cAcquireBus(&I2CD1); - msg_t mr = i2cMasterTransmitTimeout(&I2CD1, SI5351_I2C_ADDR, ®, 1, buf, len, 1000); - i2cReleaseBus(&I2CD1); - return mr == MSG_OK; +bool si5351_bulk_read(uint8_t reg, uint8_t* buf, int len) { + return i2c_receive(SI5351_I2C_ADDR, ®, 1, buf, len); } +#endif +#if 0 static void si5351_wait_pll_lock(void) { uint8_t status; @@ -423,9 +419,9 @@ static const band_strategy_t *band_s; /* * Frequency generation divide on band */ - #define THRESHOLD 290000100U - - #if 0 +//#define THRESHOLD 300000110U + +/* // Mode for H board v3.3 and SI5351 installed CONST_BAND band_strategy_t band_strategy_33H_SI5351[] = { { 0U, 0, { 0}, 0, 0, -1, -1, -1, -1, 1}, // 0 @@ -451,8 +447,7 @@ CONST_BAND band_strategy_t band_strategy_33H_SI5351[] = { { 11, SI5351_FIXED_MULT,{ 4},11,12, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 95, 95, 11*12*4} //15 }; - -#endif +*/ // Mode for H4 board v3.4 and SI5351 installed CONST_BAND band_strategy_t band_strategy_H4_SI5351[] = { @@ -495,11 +490,35 @@ CONST_BAND band_strategy_t band_strategy_36H_MS5351[] = { { 11, SI5351_FIXED_MULT,{ 4},11,13, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 95, 95, 11*12*4} // 10 }; +// Mode for board v3.7, the ZeeTK NE602A was used as the mixer and the SJWCH5351 as the signal generator. +CONST_BAND band_strategy_t band_strategy_37H_SJWCH5351[] = { + { 0U, 0, { 0}, 0, 0, -1, -1, -1, -1, 1}, // 0 + { 32000U, SI5351_FIXED_PLL, { 6}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_6MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 1 + { 145000000U, SI5351_FIXED_PLL, {40}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_6MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 5, 5, 1}, // 2 + { 1, SI5351_FIXED_MULT,{ 4}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_6MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 5, 5, 1}, // 3 + { 588000000U, SI5351_FIXED_MULT,{ 6}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_6MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 30, 30, 3*5*4}, // 4 + { 3, SI5351_FIXED_MULT,{ 4}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_6MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 40, 40, 3*5*4}, // 5 + { 5, SI5351_FIXED_MULT,{ 4}, 5, 7, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 50, 50, 5*7*4}, // 6 + { 7, SI5351_FIXED_MULT,{ 4}, 7, 9, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 50, 50, 7*9*4}, // 7 + { 9, SI5351_FIXED_MULT,{ 4}, 9,11, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 50, 50, 9*11*4}, // 8 + { 11, SI5351_FIXED_MULT,{ 4},11,13, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 55, 55, 11*12*4} // 9 + }; + void si5351_set_band_mode(uint16_t t) { - band_s = t ? band_strategy_36H_MS5351 : band_strategy_H4_SI5351; // !!!! no test MS5351 on H4 board + static const band_strategy_t *bs[] = { +/* +#if defined(NANOVNA_F303) +*/ + band_strategy_H4_SI5351, band_strategy_36H_MS5351, band_strategy_37H_SJWCH5351 + /* +#else + band_strategy_33H_SI5351, band_strategy_36H_MS5351, band_strategy_SWC5351 +#endif +*/ + }; + band_s = bs[t]; } - uint32_t si5351_get_harmonic_lvl(uint32_t freq){ uint16_t i; @@ -532,8 +551,8 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) uint32_t fdiv, pll_n; uint32_t ofreq = freq + IF_OFFSET; - // Select optimal band for prepared freq - if (freq < 26000U) { +// Select optimal band for prepared freq (before use predefined 26k value)  + if (freq < band_s[1].freq) { rdiv = SI5351_R_DIV(7); drive_strength = SI5351_CLK_DRIVE_STRENGTH_2MA; // Always use 2ma freq<<= 7; diff --git a/si5351.h b/si5351.h index 8758d35..4a62c1f 100644 --- a/si5351.h +++ b/si5351.h @@ -83,6 +83,7 @@ void si5351_set_band_mode(uint16_t t); // Defug use functions void si5351_bulk_write(const uint8_t *buf, int len); +bool si5351_bulk_read(uint8_t reg, uint8_t* buf, int len); void si5351_set_timing(int i, int v); void si5351_update_band_config(int idx, uint32_t pidx, uint32_t v); void si5351_set_tcxo(uint32_t xtal); diff --git a/tlv320aic3204.c b/tlv320aic3204.c index c35a078..8ee6b3d 100644 --- a/tlv320aic3204.c +++ b/tlv320aic3204.c @@ -319,6 +319,7 @@ static const uint8_t conf_data_ch3_select[] = { 0x37, REG_37_IN3R_TO_RIGHT_P_10k, // Route IN3R to RIGHT_P with input impedance of 10K /*0x38,*/ 0x00, // Reserved /*0x39,*/ REG_39_IN3L_TO_RIGHT_N_10k, // Route IN3L to RIGHT_N with input impedance of 10K +/*0x3A,*/ 0b11000000, // IN1 is weakly driven to common mode }; static const uint8_t conf_data_ch1_select[] = { @@ -327,6 +328,7 @@ static const uint8_t conf_data_ch1_select[] = { 0x37, REG_37_IN1R_TO_RIGHT_P_10k, // Route IN1R to RIGHT_P with input impedance of 10K /*0x38,*/ 0x00, // Reserved /*0x39,*/ REG_39_IN1L_TO_RIGHT_N_10k, // Route IN1L to RIGHT_N with input impedance of 10K +/*0x3A,*/ 0b00001100, // IN3 is weakly driven to common mode }; static void diff --git a/ui.c b/ui.c index 031e1f8..afc64a7 100644 --- a/ui.c +++ b/ui.c @@ -19,11 +19,10 @@ * Boston, MA 02110-1301, USA. */ -#include "ch.h" +#include "nanovna.h" #include "hal.h" #include "chprintf.h" #include -#include "nanovna.h" #include "si5351.h" // Use size optimization (UI not need fast speed, better have smallest size) @@ -90,6 +89,24 @@ typedef struct { char label[32]; } button_t; + +#ifdef __USE_SD_CARD__ +// Save/Load format enum +enum { + FMT_S1P_FILE=0, FMT_S2P_FILE, FMT_BMP_FILE, +#ifdef __SD_CARD_DUMP_TIFF__ + FMT_TIF_FILE, +#endif + FMT_CAL_FILE, +#ifdef __SD_CARD_DUMP_FIRMWARE__ + FMT_BIN_FILE, +#endif +#ifdef __SD_CARD_LOAD__ + FMT_CMD_FILE, +#endif +}; +#endif + // Keypad structures // Enum for keypads_list enum { @@ -108,15 +125,15 @@ enum { #endif #ifdef __VNA_Z_RENORMALIZATION__ KM_Z_PORT, + KM_CAL_LOAD_R, #endif #ifdef __USE_RTC__ KM_RTC_DATE, KM_RTC_TIME, + KM_RTC_CAL, #endif #ifdef __USE_SD_CARD__ - KM_S1P_NAME, - KM_S2P_NAME, - KM_BMP_NAME, + KM_S1P_NAME, KM_S2P_NAME, KM_BMP_NAME, // Must be equal to Save/Load format enum (TODO fix this) #ifdef __SD_CARD_DUMP_TIFF__ KM_TIF_NAME, #endif @@ -136,21 +153,22 @@ typedef struct { } keypad_pos_t; typedef struct { + uint8_t size, type; + struct { uint8_t pos; - int8_t c; + uint8_t c; + } buttons[]; } keypads_t; typedef void (*keyboard_cb_t)(uint16_t data, button_t *b); #define UI_KEYBOARD_CALLBACK(ui_kb_function_name) void ui_kb_function_name(uint16_t data, button_t *b) -#pragma pack(push, 2) typedef struct { uint8_t keypad_type; uint8_t data; const char *name; const keyboard_cb_t cb; -} keypads_list; -#pragma pack(pop) +} __attribute__((packed)) keypads_list; // Max keyboard input length #define NUMINPUT_LEN 12 @@ -168,7 +186,7 @@ static char kp_buf[TXTINPUT_LEN+1]; // !!!!!! WARNING size must be + 2 from static uint8_t ui_mode = UI_NORMAL; static const keypads_t *keypads; static uint8_t keypad_mode; -//static uint8_t keyboard_temp; // Use for custom keyboard processing +static uint8_t keyboard_temp; // Use for custom keyboard processing static uint8_t menu_current_level = 0; static int8_t selection = -1; @@ -211,28 +229,23 @@ typedef void (*menuaction_acb_t)(uint16_t data, button_t *b); #define UI_FUNCTION_ADV_CALLBACK(ui_function_name) void ui_function_name(uint16_t data, button_t *b) // Set structure align as WORD (save flash memory) -#pragma pack(push, 2) typedef struct { uint8_t type; uint8_t data; char *label; const void *reference; -} menuitem_t; -#pragma pack(pop) - -#define KP_CONTINUE 0 -#define KP_DONE 1 -#define KP_CANCEL 2 +} __attribute__((packed)) menuitem_t; static void ui_mode_normal(void); static void ui_mode_menu(void); -static void draw_menu(uint32_t mask); -static void draw_button(uint16_t x, uint16_t y, uint16_t w, uint16_t h, button_t *b); -static void ui_mode_keypad(int _keypad_mode); -static void touch_position(int *x, int *y); +static void menu_draw(uint32_t mask); +static void ui_mode_keypad(int mode); static void menu_move_back(bool leave_ui); static void menu_push_submenu(const menuitem_t *submenu); static void menu_set_submenu(const menuitem_t *submenu); +static void ui_keyboard_cb(uint16_t data, button_t *b); +static void touch_position(int *x, int *y); + // Icons for UI #include "icons_menu.c" @@ -487,18 +500,59 @@ touch_check(void) //******************************************************************************* #endif // end SOFTWARE_TOUCH -static inline void -touch_wait_release(void) -{ +//******************************************************************************* +// UI functions +//******************************************************************************* +static inline void touch_wait_release(void) { while (touch_check() != EVT_TOUCH_RELEASED) ; } -static inline void -touch_wait_pressed(void) -{ - while (touch_check() != EVT_TOUCH_PRESSED) - ; +// Draw button function +static void ui_draw_button(uint16_t x, uint16_t y, uint16_t w, uint16_t h, button_t *b) { + uint16_t type = b->border; + uint16_t bw = type & BUTTON_BORDER_WIDTH_MASK; + // Draw border if width > 0 + if (bw) { + uint16_t br = LCD_RISE_EDGE_COLOR; + uint16_t bd = LCD_FALLEN_EDGE_COLOR; + lcd_set_background(type&BUTTON_BORDER_TOP ? br : bd);lcd_fill(x, y, w, bw); // top + lcd_set_background(type&BUTTON_BORDER_LEFT ? br : bd);lcd_fill(x, y, bw, h); // left + lcd_set_background(type&BUTTON_BORDER_RIGHT ? br : bd);lcd_fill(x + w - bw, y, bw, h); // right + lcd_set_background(type&BUTTON_BORDER_BOTTOM ? br : bd);lcd_fill(x, y + h - bw, w, bw); // bottom +} + // Set colors for button and text + lcd_set_colors(b->fg, b->bg); + if (type & BUTTON_BORDER_NO_FILL) return; + lcd_fill(x + bw, y + bw, w - (bw * 2), h - (bw * 2)); +} + +// Draw message box function +void ui_message_box(const char *header, const char *text, uint32_t delay) { + button_t b; + int x , y; + b.bg = LCD_MENU_COLOR; + b.fg = LCD_MENU_TEXT_COLOR; + b.border = BUTTON_BORDER_FLAT|1; + if (header) {// Draw header + ui_draw_button((LCD_WIDTH-MESSAGE_BOX_WIDTH)/2, LCD_HEIGHT/2-40, MESSAGE_BOX_WIDTH, 60, &b); + x = (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 10; + y = LCD_HEIGHT/2-40 + 5; + lcd_drawstring(x, y, header); + request_to_redraw(REDRAW_AREA); + } + if (text) { // Draw window + lcd_set_colors(LCD_MENU_TEXT_COLOR, LCD_FG_COLOR); + lcd_fill((LCD_WIDTH-MESSAGE_BOX_WIDTH)/2+3, LCD_HEIGHT/2-40+FONT_STR_HEIGHT+8, MESSAGE_BOX_WIDTH-6, 60-FONT_STR_HEIGHT-8-3); + x = (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 20; + y = LCD_HEIGHT/2-40 + FONT_STR_HEIGHT + 8 + 14; + lcd_drawstring(x, y, text); + request_to_redraw(REDRAW_AREA); + } + + do { + chThdSleepMilliseconds(delay == 0 ? 50 : delay); + } while (delay == 0 && btn_check() != EVT_BUTTON_SINGLE_CLICK && touch_check() != EVT_TOUCH_PRESSED); } static void getTouchPoint(uint16_t x, uint16_t y, const char *name, int16_t *data) { @@ -513,9 +567,7 @@ static void getTouchPoint(uint16_t x, uint16_t y, const char *name, int16_t *dat data[1] = last_touch_y; } -void -touch_cal_exec(void) -{ +void ui_touch_cal_exec(void) { const uint16_t x1 = CALIBRATION_OFFSET - TOUCH_MARK_X; const uint16_t y1 = CALIBRATION_OFFSET - TOUCH_MARK_Y; const uint16_t x2 = LCD_WIDTH - 1 - CALIBRATION_OFFSET - TOUCH_MARK_X; @@ -528,9 +580,7 @@ touch_cal_exec(void) getTouchPoint(x2, y2, "LOWER RIGHT", &config._touch_cal[p2]); } -void -touch_draw_test(void) -{ +void ui_touch_draw_test(void) { int x0, y0; int x1, y1; lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); @@ -553,9 +603,7 @@ touch_draw_test(void) } } -static void -touch_position(int *x, int *y) -{ +static void touch_position(int *x, int *y) { #ifdef __REMOTE_DESKTOP__ if (touch_remote != REMOTE_NONE) { *x = last_touch_x; @@ -563,9 +611,10 @@ touch_position(int *x, int *y) return; } #endif - int tx = ((LCD_WIDTH-1-CALIBRATION_OFFSET)*(last_touch_x - config._touch_cal[0]) + CALIBRATION_OFFSET * (config._touch_cal[2] - last_touch_x)) / (config._touch_cal[2] - config._touch_cal[0]); + int tx, ty; + tx = (LCD_WIDTH - 1 - 2 * CALIBRATION_OFFSET)*(last_touch_x - config._touch_cal[0]) / (config._touch_cal[2] - config._touch_cal[0]) + CALIBRATION_OFFSET; if (tx<0) tx = 0; else if (tx>=LCD_WIDTH ) tx = LCD_WIDTH -1; - int ty = ((LCD_HEIGHT-1-CALIBRATION_OFFSET)*(last_touch_y - config._touch_cal[1]) + CALIBRATION_OFFSET * (config._touch_cal[3] - last_touch_y)) / (config._touch_cal[3] - config._touch_cal[1]); + ty = (LCD_HEIGHT- 1 - 2 * CALIBRATION_OFFSET)*(last_touch_y - config._touch_cal[1]) / (config._touch_cal[3] - config._touch_cal[1]) + CALIBRATION_OFFSET; if (ty<0) ty = 0; else if (ty>=LCD_HEIGHT) ty = LCD_HEIGHT-1; #ifdef __FLIP_DISPLAY__ if (VNA_MODE(VNA_MODE_FLIP_DISPLAY)) { @@ -577,9 +626,7 @@ touch_position(int *x, int *y) *y = ty; } -static void -show_version(void) -{ +static void ui_show_version(void) { int x = 5, y = 5, i = 1; int str_height = FONT_STR_HEIGHT + 2; lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); @@ -627,8 +674,7 @@ show_version(void) } #ifdef __DFU_SOFTWARE_MODE__ -void -enter_dfu(void) +void ui_enter_dfu(void) { touch_stop_watchdog(); int x = 5, y = 20; @@ -641,22 +687,19 @@ enter_dfu(void) } #endif -static bool -select_lever_mode(int mode) -{ +static bool select_lever_mode(int mode) { if (lever_mode == mode) return false; lever_mode = mode; request_to_redraw(REDRAW_BACKUP | REDRAW_FREQUENCY | REDRAW_MARKER); return true; } -static UI_FUNCTION_ADV_CALLBACK(menu_calop_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_calop_acb) { static const struct {uint8_t mask, next;} c_list[5]={ [CAL_LOAD] = {CALSTAT_LOAD, 3}, [CAL_OPEN] = {CALSTAT_OPEN, 1}, [CAL_SHORT]= {CALSTAT_SHORT, 2}, - [CAL_THRU] = {CALSTAT_THRU, 5}, // to cal done + [CAL_THRU] = {CALSTAT_THRU, 5}, //to cal done [CAL_ISOLN]= {CALSTAT_ISOLN, 4}, }; if (b){ @@ -669,22 +712,32 @@ static UI_FUNCTION_ADV_CALLBACK(menu_calop_acb) selection = c_list[data].next; } +static UI_FUNCTION_ADV_CALLBACK(menu_cal_enh_acb) { + (void)data; + if (b) { + b->icon = (cal_status&CALSTAT_ENHANCED_RESPONSE) ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; + return; + } + // toggle applying correction + cal_status ^= CALSTAT_ENHANCED_RESPONSE; + request_to_redraw(REDRAW_CAL_STATUS); +} + extern const menuitem_t menu_save[]; -static UI_FUNCTION_CALLBACK(menu_caldone_cb) -{ +static UI_FUNCTION_CALLBACK(menu_caldone_cb) { cal_done(); menu_move_back(false); if (data == 0) menu_push_submenu(menu_save); } -static UI_FUNCTION_CALLBACK(menu_cal_reset_cb) -{ +static UI_FUNCTION_CALLBACK(menu_cal_reset_cb) { (void)data; // RESET - cal_status = 0; + cal_status&= CALSTAT_ENHANCED_RESPONSE; // leave ER state lastsaveid = NO_SAVE_SLOT; - set_power(SI5351_CLK_DRIVE_STRENGTH_AUTO); + //set_power(SI5351_CLK_DRIVE_STRENGTH_AUTO); + request_to_redraw(REDRAW_CAL_STATUS); } static UI_FUNCTION_ADV_CALLBACK(menu_cal_range_acb) { @@ -714,8 +767,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_cal_apply_acb) { request_to_redraw(REDRAW_CAL_STATUS); } -static UI_FUNCTION_ADV_CALLBACK(menu_recall_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_recall_acb) { if (b){ const properties_t *p = get_properties(data); if (p) @@ -728,23 +780,30 @@ static UI_FUNCTION_ADV_CALLBACK(menu_recall_acb) load_properties(data); } -#define MENU_CONFIG_TOUCH_CAL 0 -#define MENU_CONFIG_TOUCH_TEST 1 -#define MENU_CONFIG_VERSION 2 -#define MENU_CONFIG_RESET 3 -#define MENU_CONFIG_LOAD 4 -static UI_FUNCTION_CALLBACK(menu_config_cb) -{ +enum { + MENU_CONFIG_TOUCH_CAL = 0, + MENU_CONFIG_TOUCH_TEST, + MENU_CONFIG_VERSION, + MENU_CONFIG_SAVE, + MENU_CONFIG_RESET, + MENU_CONFIG_LOAD, +}; + +static UI_FUNCTION_CALLBACK(menu_config_cb) { switch (data) { case MENU_CONFIG_TOUCH_CAL: - touch_cal_exec(); + ui_touch_cal_exec(); break; case MENU_CONFIG_TOUCH_TEST: - touch_draw_test(); + ui_touch_draw_test(); break; case MENU_CONFIG_VERSION: - show_version(); + ui_show_version(); break; + case MENU_CONFIG_SAVE: + config_save(); + menu_move_back(true); + return; case MENU_CONFIG_RESET: clear_all_config_prop_data(); NVIC_SystemReset(); @@ -752,7 +811,7 @@ static UI_FUNCTION_CALLBACK(menu_config_cb) #if defined(__SD_CARD_LOAD__) && !defined(__SD_FILE_BROWSER__) case MENU_CONFIG_LOAD: if (!sd_card_load_config()) - drawMessageBox("Error", "No config.ini", 2000); + ui_message_box("Error", "No config.ini", 2000); break; #endif } @@ -760,23 +819,14 @@ static UI_FUNCTION_CALLBACK(menu_config_cb) request_to_redraw(REDRAW_CLRSCR | REDRAW_AREA | REDRAW_BATTERY | REDRAW_CAL_STATUS | REDRAW_FREQUENCY); } -static UI_FUNCTION_CALLBACK(menu_config_save_cb) -{ - (void)data; - config_save(); - menu_move_back(true); -} - #ifdef __DFU_SOFTWARE_MODE__ -static UI_FUNCTION_CALLBACK(menu_dfu_cb) -{ +static UI_FUNCTION_CALLBACK(menu_dfu_cb) { (void)data; - enter_dfu(); + ui_enter_dfu(); } #endif -static UI_FUNCTION_ADV_CALLBACK(menu_save_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_save_acb) { if (b){ const properties_t *p = get_properties(data); if (p) @@ -791,8 +841,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_save_acb) } } -static UI_FUNCTION_ADV_CALLBACK(menu_trace_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_trace_acb) { if (b){ if (trace[data].enabled){ b->bg = LCD_TRACE_1_COLOR + data; @@ -814,8 +863,7 @@ extern const menuitem_t menu_marker_s11smith[]; extern const menuitem_t menu_marker_s21smith[]; static uint8_t get_smith_format(void) {return (current_trace != TRACE_INVALID) ? trace[current_trace].smith_format : 0;} -static UI_FUNCTION_ADV_CALLBACK(menu_marker_smith_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_marker_smith_acb) { if (b) { b->icon = get_smith_format() == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; b->p1.text = get_smith_format_names(data); @@ -828,8 +876,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_smith_acb) #define F_S11 0x00 #define F_S21 0x80 -static UI_FUNCTION_ADV_CALLBACK(menu_format_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_format_acb) { if (current_trace == TRACE_INVALID) return; // Not apply any for invalid traces uint16_t format = data & (~F_S21); uint16_t channel = data & F_S21 ? 1 : 0; @@ -853,8 +900,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_format_acb) set_trace_type(current_trace, format, channel); } -static UI_FUNCTION_ADV_CALLBACK(menu_channel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_channel_acb) { (void)data; if (current_trace == TRACE_INVALID) {if (b) b->p1.text = ""; return;} int ch = trace[current_trace].channel; @@ -865,8 +911,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_channel_acb) set_trace_channel(current_trace, ch^1); } -static UI_FUNCTION_ADV_CALLBACK(menu_transform_window_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_transform_window_acb) { char *text = ""; switch(props_mode & TD_WINDOW){ case TD_WINDOW_MINIMUM: text = "MINIMUM"; data = TD_WINDOW_NORMAL; break; @@ -880,8 +925,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_transform_window_acb) props_mode = (props_mode & ~TD_WINDOW) | data; } -static UI_FUNCTION_ADV_CALLBACK(menu_transform_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_transform_acb) { (void)data; if(b){ if (props_mode & DOMAIN_TIME) b->icon = BUTTON_ICON_CHECK; @@ -893,8 +937,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_transform_acb) request_to_redraw(REDRAW_FREQUENCY | REDRAW_AREA); } -static UI_FUNCTION_ADV_CALLBACK(menu_transform_filter_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_transform_filter_acb) { if(b){ b->icon = (props_mode & TD_FUNC) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; @@ -903,8 +946,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_transform_filter_acb) } const menuitem_t menu_bandwidth[]; -static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_sel_acb) { (void)data; if (b){ b->p1.u = get_bandwidth_frequency(config._bandwidth); @@ -913,8 +955,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_sel_acb) menu_push_submenu(menu_bandwidth); } -static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_acb) { if (b){ b->icon = config._bandwidth == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; b->p1.u = get_bandwidth_frequency(data); @@ -923,12 +964,10 @@ static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_acb) set_bandwidth(data); } -#pragma pack(push, 2) typedef struct { const char* text; uint16_t update_flag; -} vna_mode_data_t; -#pragma pack(pop) +} __attribute__((packed)) vna_mode_data_t; const vna_mode_data_t vna_mode_data[] = { // text (if 0 use checkbox) Redraw flags on change @@ -954,13 +993,16 @@ const vna_mode_data_t vna_mode_data[] = { #ifdef __SD_CARD_DUMP_TIFF__ [VNA_MODE_TIFF] = {"BMP\0TIFF", REDRAW_BACKUP}, #endif +#ifdef __USB_UID__ + [VNA_MODE_USB_UID] = {0, REDRAW_BACKUP}, +#endif }; -void apply_VNA_mode(uint16_t idx, uint16_t value) { +void apply_VNA_mode(uint16_t idx, vna_mode_ops operation) { uint16_t m = 1<icon = get_smooth_factor() == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; b->p1.u = data; @@ -1010,8 +1050,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_smooth_acb) #endif const menuitem_t menu_sweep_points[]; -static UI_FUNCTION_ADV_CALLBACK(menu_points_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_points_sel_acb) { (void)data; if (b){ b->p1.u = sweep_points; @@ -1021,8 +1060,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_points_sel_acb) } static const uint16_t point_counts_set[POINTS_SET_COUNT] = POINTS_SET; -static UI_FUNCTION_ADV_CALLBACK(menu_points_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_points_acb) { uint16_t p_count = point_counts_set[data]; if (b){ b->icon = sweep_points == p_count ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; @@ -1033,8 +1071,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_points_acb) } const menuitem_t menu_power[]; -static UI_FUNCTION_ADV_CALLBACK(menu_power_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_power_sel_acb) { (void)data; if (b){ if (current_props._power != SI5351_CLK_DRIVE_STRENGTH_AUTO) @@ -1044,8 +1081,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_power_sel_acb) menu_push_submenu(menu_power); } -static UI_FUNCTION_ADV_CALLBACK(menu_power_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_power_acb) { if (b){ b->icon = current_props._power == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; b->p1.u = 2+data*2; @@ -1056,32 +1092,29 @@ static UI_FUNCTION_ADV_CALLBACK(menu_power_acb) // Process keyboard button callback, and run keyboard function extern const keypads_list keypads_mode_tbl[]; -static UI_FUNCTION_ADV_CALLBACK(menu_keyboard_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_keyboard_acb) { if (data == KM_VAR && lever_mode == LM_EDELAY) // JOG STEP button auto set (e-delay or frequency step) data = KM_VAR_DELAY; if (b) { - const keyboard_cb_t cb = keypads_mode_tbl[data].cb; - if (cb) cb(keypads_mode_tbl[data].data, b); + ui_keyboard_cb(data, b); return; } ui_mode_keypad(data); } // Custom keyboard menu button callback (depend from current trace) -static UI_FUNCTION_ADV_CALLBACK(menu_scale_keyboard_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_scale_keyboard_acb) { // Not apply amplitude / scale / ref for invalid or polar graph if (current_trace == TRACE_INVALID) return; uint32_t type_mask = 1<p1.text = (sweep_mode & SWEEP_ENABLE) ? "PAUSE" : "RESUME"; @@ -1092,8 +1125,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_pause_acb) } #define UI_MARKER_EDELAY 6 -static UI_FUNCTION_CALLBACK(menu_marker_op_cb) -{ +static UI_FUNCTION_CALLBACK(menu_marker_op_cb) { freq_t freq = get_marker_frequency(active_marker); if (freq == 0) return; // no active marker @@ -1124,18 +1156,18 @@ static UI_FUNCTION_CALLBACK(menu_marker_op_cb) { if (current_trace == TRACE_INVALID) break; - float (*array)[2] = measured[trace[current_trace].channel]; + int ch = trace[current_trace].channel; + float (*array)[2] = measured[ch]; int index = markers[active_marker].index; float v = groupdelay_from_array(index, array[index]); - set_electrical_delay(electrical_delay + v); + set_electrical_delay(ch, current_props._electrical_delay[ch] + v); } break; } ui_mode_normal(); } -static UI_FUNCTION_CALLBACK(menu_marker_search_dir_cb) -{ +static UI_FUNCTION_CALLBACK(menu_marker_search_dir_cb) { marker_search_dir(markers[active_marker].index, data == MK_SEARCH_RIGHT ? MK_SEARCH_RIGHT : MK_SEARCH_LEFT); props_mode&=~TD_MARKER_TRACK; #ifdef UI_USE_LEVELER_SEARCH_MODE @@ -1143,8 +1175,7 @@ static UI_FUNCTION_CALLBACK(menu_marker_search_dir_cb) #endif } -static UI_FUNCTION_ADV_CALLBACK(menu_marker_tracking_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_marker_tracking_acb) { (void)data; if (b){ b->icon = (props_mode & TD_MARKER_TRACK) ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; @@ -1155,9 +1186,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_tracking_acb) #ifdef __VNA_MEASURE_MODULE__ extern const menuitem_t *menu_measure_list[]; -static UI_FUNCTION_ADV_CALLBACK(menu_measure_acb) -{ - (void)data; +static UI_FUNCTION_ADV_CALLBACK(menu_measure_acb) { if (b){ b->icon = current_props._measure == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; @@ -1172,9 +1201,7 @@ static UI_FUNCTION_CALLBACK(menu_measure_cb) { } #endif -static void -active_marker_check(void) -{ +static void active_marker_check(void) { int i; // Auto select active marker if disabled if (active_marker == MARKER_INVALID) @@ -1188,8 +1215,7 @@ active_marker_check(void) } } -static UI_FUNCTION_ADV_CALLBACK(menu_marker_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_marker_sel_acb) { //if (data >= MARKERS_MAX) return; int mk = data; if (b){ @@ -1215,19 +1241,16 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_sel_acb) request_to_redraw(REDRAW_MARKER); } -static UI_FUNCTION_CALLBACK(menu_marker_disable_all_cb) -{ +static UI_FUNCTION_CALLBACK(menu_marker_disable_all_cb) { (void)data; - int i; - for (i = 0; i < MARKERS_MAX; i++) + for (int i = 0; i < MARKERS_MAX; i++) markers[i].enabled = FALSE; // all off previous_marker = MARKER_INVALID; active_marker = MARKER_INVALID; request_to_redraw(REDRAW_AREA); } -static UI_FUNCTION_ADV_CALLBACK(menu_marker_delta_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_marker_delta_acb) { (void)data; if (b){ b->icon = props_mode & TD_MARKER_DELTA ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; @@ -1238,8 +1261,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_delta_acb) } #ifdef __USE_SERIAL_CONSOLE__ -static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_acb) { static const uint32_t usart_speed[] = {19200, 38400, 57600, 115200, 230400, 460800, 921600, 1843200, 2000000, 3000000}; uint32_t speed = usart_speed[data]; if (b){ @@ -1251,8 +1273,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_acb) } extern const menuitem_t menu_serial_speed[]; -static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_sel_acb) { (void)data; if (b){ b->p1.u = config._serial_speed; @@ -1263,8 +1284,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_sel_acb) #endif #ifdef USE_VARIABLE_OFFSET_MENU -static UI_FUNCTION_ADV_CALLBACK(menu_offset_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_offset_acb) { int32_t offset = (data+1) * FREQUENCY_OFFSET_STEP; if (b){ b->icon = IF_OFFSET == offset ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; @@ -1275,8 +1295,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_offset_acb) } const menuitem_t menu_offset[]; -static UI_FUNCTION_ADV_CALLBACK(menu_offset_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_offset_sel_acb) { (void)data; if (b){ b->p1.i = IF_OFFSET; @@ -1288,12 +1307,11 @@ static UI_FUNCTION_ADV_CALLBACK(menu_offset_sel_acb) #ifdef __LCD_BRIGHTNESS__ // Brightness control range 0 - 100 -static void lcd_setBrightness(uint16_t b){ +void lcd_setBrightness(uint16_t b){ dac_setvalue_ch2(700 + b*(4090-700)/100); } -static UI_FUNCTION_ADV_CALLBACK(menu_brightness_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_brightness_acb) { (void)data; if (b){ b->p1.u = config._brightness; @@ -1326,42 +1344,22 @@ static UI_FUNCTION_ADV_CALLBACK(menu_brightness_acb) } #endif +//===================================================================================================== +// SD card save / load functions +//===================================================================================================== #ifdef __USE_SD_CARD__ -// Save/Load format enum -enum { - FMT_S1P_FILE=0, FMT_S2P_FILE, FMT_BMP_FILE, -#ifdef __SD_CARD_DUMP_TIFF__ - FMT_TIF_FILE, -#endif - FMT_CAL_FILE, -#ifdef __SD_CARD_DUMP_FIRMWARE__ - FMT_BIN_FILE, -#endif -#ifdef __SD_CARD_LOAD__ - FMT_CMD_FILE, -#endif -}; - -// Save/Load file extension -static const char *file_ext[] = { - [FMT_S1P_FILE] = "s1p", - [FMT_S2P_FILE] = "s2p", - [FMT_BMP_FILE] = "bmp", -#ifdef __SD_CARD_DUMP_TIFF__ - [FMT_TIF_FILE] = "tif", -#endif - [FMT_CAL_FILE] = "cal", -#ifdef __SD_CARD_DUMP_FIRMWARE__ - [FMT_BIN_FILE] = "bin", -#endif -#ifdef __SD_CARD_LOAD__ - [FMT_CMD_FILE] = "cmd", -#endif -}; - -//******************************************************************************************* +// Save file callback +typedef FRESULT (*file_save_cb_t)(FIL *f, uint8_t format); +#define FILE_SAVE_CALLBACK(save_function_name) FRESULT save_function_name(FIL *f, uint8_t format) +// Load file callback +typedef const char* (*file_load_cb_t)(FIL *f, FILINFO *fno, uint8_t format); +#define FILE_LOAD_CALLBACK(load_function_name) const char* load_function_name(FIL *f, FILINFO *fno, uint8_t format) + +//===================================================================================================== // S1P and S2P file headers, and data structures -//******************************************************************************************* +// Save touchstone file for VNA (use rev 1.1 format) +// https://en.wikipedia.org/wiki/Touchstone_file +//===================================================================================================== static const char s1_file_header[] = "!File created by NanoVNA\r\n"\ "# Hz S RI R 50\r\n"; @@ -1376,6 +1374,78 @@ static const char s2_file_header[] = static const char s2_file_param[] = "%u % f % f % f % f 0 0 0 0\r\n"; +static FILE_SAVE_CALLBACK(save_snp) { + const char *s_file_format; + char *buf_8 = (char *)spi_buffer; + FRESULT res; + UINT size; + // Write SxP file + if (format == FMT_S1P_FILE){ + s_file_format = s1_file_param; + // write sxp header (not write NULL terminate at end) + res = f_write(f, s1_file_header, sizeof(s1_file_header)-1, &size); + } else { + s_file_format = s2_file_param; + // Write s2p header (not write NULL terminate at end) + res = f_write(f, s2_file_header, sizeof(s2_file_header)-1, &size); + } + // Write all points data + for (int i = 0; i < sweep_points && res == FR_OK; i++) { + size = plot_printf(buf_8, 128, s_file_format, getFrequency(i), measured[0][i][0], measured[0][i][1], measured[1][i][0], measured[1][i][1]); + res = f_write(f, buf_8, size, &size); + } + return res; +} + +// Support only NanoVNA format: Hz S RI R 50 +static FILE_LOAD_CALLBACK(load_snp) { + (void)fno; + UINT size; + const int buffer_size = 256; + const int line_size = 128; + char *buf_8 = (char *)spi_buffer; // must be greater then buffer_size + line_size + char *line = buf_8 + buffer_size; + uint16_t j = 0, i, count = 0; + freq_t start = 0, stop = 0, freq; + while (f_read(f, buf_8, buffer_size, &size) == FR_OK && size > 0) { + for (i = 0; i < size; i++) { + uint8_t c = buf_8[i]; + if (c == '\r') { // New line (Enter) + 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 + freq = my_atoui(args[0]); // Get frequency + if (count >= SWEEP_POINTS_MAX || freq > FREQUENCY_MAX) return "Format err"; + if (count == 0) start = freq; // For index 0 set as start + stop = freq; // last set as stop + measured[0][count][0] = my_atof(args[1]); + measured[0][count][1] = my_atof(args[2]); // get S11 data + if (format == FMT_S2P_FILE && nargs >= 4) { + measured[1][count][0] = my_atof(args[3]); + measured[1][count][1] = my_atof(args[4]); // get S11 data + } else { + measured[1][count][0] = 0.0f; + measured[1][count][1] = 0.0f; // get S11 data + } + count++; + } + else if (c < 0x20) continue; // Others (skip) + else if (j < line_size) line[j++] = (char)c; // Store + } + } + if (count != 0) { // Points count not zero, so apply data to traces + pause_sweep(); + current_props._electrical_delay[0] = 0.0f; // Reset delays + current_props._electrical_delay[1] = 0.0f; // Reset delays + current_props._sweep_points = count; + set_sweep_frequency(ST_START, start); + set_sweep_frequency(ST_STOP, stop); + request_to_redraw(REDRAW_PLOT); + } + return NULL; +} + //******************************************************************************************* // Bitmap file header for LCD_WIDTH x LCD_HEIGHT image 16bpp (v4 format allow set RGB mask) //******************************************************************************************* @@ -1425,6 +1495,37 @@ static const uint8_t bmp_header_v4[BMP_H1_SIZE + BMP_V4_SIZE] = { BMP_UINT32(0), // GammaBlue }; +// Save bitmap file (use v4 format allow set RGB mask) +static FILE_SAVE_CALLBACK(save_bmp) { + (void)format; + UINT size; + uint16_t *buf_16 = (uint16_t *)spi_buffer; + FRESULT res = f_write(f, bmp_header_v4, sizeof(bmp_header_v4), &size); // Write header struct + lcd_set_background(LCD_SWEEP_LINE_COLOR); + for (int y = LCD_HEIGHT-1; y >= 0 && res == FR_OK; y--) { + lcd_read_memory(0, y, LCD_WIDTH, 1, buf_16); + swap_bytes(buf_16, LCD_WIDTH); + res = f_write(f, buf_16, LCD_WIDTH*sizeof(uint16_t), &size); + lcd_fill(LCD_WIDTH-1, y, 1, 1); + } + return res; +} + +static FILE_LOAD_CALLBACK(load_bmp) { + (void)format; + UINT size; + uint16_t *buf_16 = (uint16_t *)spi_buffer; // prepare buffer + FRESULT res = f_read(f, (void *)buf_16, sizeof(bmp_header_v4), &size); // read header + if (res != FR_OK || buf_16[9] != LCD_WIDTH || buf_16[11] != LCD_HEIGHT || buf_16[14] != 16) return "Format err"; + for (int y = LCD_HEIGHT-1; y >=0 && res == FR_OK; y--) { + res = f_read(f, (void *)buf_16, LCD_WIDTH * sizeof(uint16_t), &size); + swap_bytes(buf_16, LCD_WIDTH); + lcd_bulk(0, y, LCD_WIDTH, 1); + } + lcd_printf(0, LCD_HEIGHT - 3*FONT_STR_HEIGHT, fno->fname); + return NULL; +} + #ifdef __SD_CARD_DUMP_TIFF__ //******************************************************************************************* // TIFF header for LCD_WIDTH x LCD_HEIGHT image 24bpp and RLE compression (packbits) @@ -1504,33 +1605,172 @@ static const uint8_t tif_header[] = { // After Image data }; -// RLE packbits algorithm -static int packbits(char *source, char *dest, int size) { - int i = 0, rle, l, pk = 0, sz = 0; - while ((l = size - i) > 0) { - if (l > 128) l = 128; // Limit search RLE block size to 128 - char c = source[i++]; // Get next byte and write to block - for (rle = 0; c == source[i + rle] && --l; rle++); // Calculate this byte RLE sequence size = rle + 1 - if (sz && rle < 2) rle = 0; // Ignore (rle + 1) < 3 sequence on run non RLE input - else if (sz == 0 || rle > 0) sz = pk++; // Reset state or RLE sequence found -> start new block - dest[pk++] = c; // Write char to block - if (rle > 0) {i+= rle; dest[sz] = -rle;} // Write RLE sequence size and go to new block - else if ((dest[sz] = pk - sz - 2) < 127) // Continue write non RLE data while 1 + (non_rle + 1) < 127 - continue; - sz = 0; // Block complete +static FILE_SAVE_CALLBACK(save_tiff) { + (void)format; + UINT size; + uint16_t *buf_16 = (uint16_t *)spi_buffer; + char *buf_8; + FRESULT res = f_write(f, tif_header, sizeof(tif_header), &size); // Write header struct + lcd_set_background(LCD_SWEEP_LINE_COLOR); + for (int y = 0; y < LCD_HEIGHT && res == FR_OK; y++) { + // Use 128 bytes offset for RGB888 data + // Use 0 offset for compressed RLE (maximum need WIDTH * 4 + 128 bytes in spi_buffer) + buf_8 = (char *)buf_16 + 128; + // Read LCD line in RGB565 format (swapped bytes) + lcd_read_memory(0, y, LCD_WIDTH, 1, buf_16); + // Convert to RGB888 + for (int x = LCD_WIDTH - 1; x >= 0; x--) { + uint16_t color = (buf_16[x] << 8) | (buf_16[x] >> 8); + buf_8[3*x + 0] = (color>>8) & 0xF8;// if (buf_8[3*x + 0] < 0) buf_8[3*x + 0]+= 7; + buf_8[3*x + 1] = (color>>3) & 0xFC;// if (buf_8[3*x + 1] < 0) buf_8[3*x + 1]+= 3; + buf_8[3*x + 2] = (color<<3) & 0xF8;// if (buf_8[3*x + 2] < 0) buf_8[3*x + 2]+= 7; + } + size = packbits(buf_8, (char *)buf_16, LCD_WIDTH * 3); + res = f_write(f, buf_16, size, &size); + lcd_fill(LCD_WIDTH-1, y, 1, 1); +} + return res; +} + +static FILE_LOAD_CALLBACK(load_tiff) { + (void)format; + UINT size; + uint8_t *buf_8 = (uint8_t *)spi_buffer; // prepare buffer + uint16_t *buf_16 = (uint16_t *)spi_buffer; + FRESULT res = f_read(f, (void *)buf_16, sizeof(tif_header), &size); // read header + // Quick check for valid (not parse TIFF, use hardcoded values, for less code size) + // Check header id, width, height, compression (pass only self saved images) + if (res != FR_OK || + buf_16[0] != 0x4949 || // Check header ID + buf_16[9] != LCD_WIDTH || // Check Width + buf_16[15] != LCD_HEIGHT || // Check Height + buf_16[27] != TIFF_PACKBITS) return "Format err"; + for (int y = 0; y < LCD_HEIGHT && res == FR_OK; y++) { + // Unpack RLE compression sequence + for (int x = 0; x < LCD_WIDTH * 3;) { + int8_t data[2]; res = f_read(f, data, 2, &size); // Read count and value + int count = data[0]; // count + buf_8[x++] = data[1]; // copy first value + if (count > 0) { // if count > 0 need read additional values + res = f_read(f, &buf_8[x], count, &size); + x+= count; + } else while (count++ < 0) buf_8[x++] = data[1]; // if count < 0 need repeat value -count times + } + // Convert from RGB888 to RGB565 and copy to screen + for (int x = 0; x < LCD_WIDTH; x++) + buf_16[x] = RGB565(buf_8[3 * x + 0], buf_8[3 * x + 1], buf_8[3 * x + 2]); + lcd_bulk(0, y, LCD_WIDTH, 1); } - return pk; + lcd_printf(0, LCD_HEIGHT - 3 * FONT_STR_HEIGHT, fno->fname); + return NULL; +} +#endif + +//===================================================================================================== +// Calibration save / load +//===================================================================================================== +static FILE_SAVE_CALLBACK(save_cal) { + (void)format; + UINT size; + const char *src = (char*)¤t_props; + const uint32_t total = sizeof(current_props); + return f_write(f, src, total, &size); +} + +static FILE_LOAD_CALLBACK(load_cal) { + (void)format; + UINT size; + uint32_t magic; + char *src = (char*)¤t_props + sizeof(magic); + uint32_t total = sizeof(current_props) - sizeof(magic); + // Compare file size and try read magic header, if all OK load it + if (fno->fsize != sizeof(current_props) || f_read(f, &magic, sizeof(magic), &size) != FR_OK || + magic != PROPERTIES_MAGIC || f_read(f, src, total, &size) != FR_OK) + return "Format err"; + load_properties(NO_SAVE_SLOT); + return NULL; +} + +//===================================================================================================== +// Dump firmware to SD card as bin file image +//===================================================================================================== +#ifdef __SD_CARD_DUMP_FIRMWARE__ +static FILE_SAVE_CALLBACK(save_bin) { + (void)format; + UINT size; +// START_PROFILE + const char *src = (const char*)FLASH_START_ADDRESS; + const uint32_t total = FLASH_TOTAL_SIZE; + return f_write(f, src, total, &size); +// STOP_PROFILE } #endif -static void swap_bytes(uint16_t *buf, int size) { - for (int i = 0; i < size; i++) - buf[i] = __REVSH(buf[i]); // swap byte order (example 0x10FF to 0xFF10) +//===================================================================================================== +// Load command scripts +//===================================================================================================== +#ifdef __SD_CARD_LOAD__ +static FILE_LOAD_CALLBACK(load_cmd) { + (void)fno; + (void)format; + UINT size; + const int buffer_size = 256; + const int line_size = 128; + char *buf_8 = (char *)spi_buffer; // must be greater then buffer_size + line_size + char *line = buf_8 + buffer_size; + uint16_t j = 0, i; + while (f_read(f, buf_8, buffer_size, &size) == FR_OK && size > 0) { + for (i = 0; i < size; i++) { + uint8_t c = buf_8[i]; + if (c == '\r') { // New line (Enter) + line[j] = 0; j = 0; + VNAShell_executeCMDLine(line); + } + else if (c < 0x20) continue; // Others (skip) + else if (j < line_size) line[j++] = (char)c; // Store + } + }return NULL; } +#endif + +//===================================================================================================== +// SD card save / load file options +//===================================================================================================== +#ifdef __SD_FILE_BROWSER__ + #define FILE_OPTIONS(e, s, l, o) {e, s, l, o} +#else + #define FILE_OPTIONS(e, s, l, o) {e, s, o} +#endif + +#define FILE_OPT_REDRAW (1<<0) // need full screen update before save +#define FILE_OPT_CONTINUE (1<<1) // in browser mode use leveler left/right for see next/prev file + +// Save / Load file options +const struct { + const char *ext; // file extension + file_save_cb_t save; // file save function +#ifdef __SD_FILE_BROWSER__ + file_load_cb_t load; // file load function (use in browser) +#endif + uint32_t opt; +} file_opt[] = { + [FMT_S1P_FILE] = FILE_OPTIONS("s1p", save_snp, load_snp, 0), + [FMT_S2P_FILE] = FILE_OPTIONS("s2p", save_snp, load_snp, 0), + [FMT_BMP_FILE] = FILE_OPTIONS("bmp", save_bmp, load_bmp, FILE_OPT_REDRAW | FILE_OPT_CONTINUE), +#ifdef __SD_CARD_DUMP_TIFF__ + [FMT_TIF_FILE] = FILE_OPTIONS("tif", save_tiff, load_tiff, FILE_OPT_REDRAW | FILE_OPT_CONTINUE), +#endif + [FMT_CAL_FILE] = FILE_OPTIONS("cal", save_cal, load_cal, 0), +#ifdef __SD_CARD_DUMP_FIRMWARE__ + [FMT_BIN_FILE] = FILE_OPTIONS("bin", save_bin, NULL, 0), +#endif +#ifdef __SD_CARD_LOAD__ + [FMT_CMD_FILE] = FILE_OPTIONS("cmd", NULL, load_cmd, 0), +#endif +}; // Create file name from current time -static FRESULT vna_create_file(char *fs_filename) -{ +static FRESULT ui_create_file(char *fs_filename) { // shell_printf("S file\r\n"); FRESULT res = f_mount(fs_volume, "", 1); // shell_printf("Mount = %d\r\n", res); @@ -1541,20 +1781,13 @@ static FRESULT vna_create_file(char *fs_filename) return res; } -static void vna_save_file(char *name, uint8_t format) -{ - char *buf_8; - uint16_t *buf_16; - int i, y; - UINT size; +static void ui_save_file(char *name, uint8_t format) { char fs_filename[FF_LFN_BUF]; + file_save_cb_t save = file_opt[format].save; + if (save == NULL) return; // For screenshot need back to normal mode and redraw screen before capture!! // Redraw use spi_buffer so need do it before any file ops - if (ui_mode != UI_NORMAL && (format == FMT_BMP_FILE -#ifdef __SD_CARD_DUMP_TIFF__ - || format == FMT_TIF_FILE -#endif - )) { + if (ui_mode != UI_NORMAL && (file_opt[format].opt & FILE_OPT_REDRAW)) { ui_mode_normal(); draw_all(); } @@ -1564,119 +1797,26 @@ static void vna_save_file(char *name, uint8_t format) #if FF_USE_LFN >= 1 uint32_t tr = rtc_get_tr_bcd(); // TR read first uint32_t dr = rtc_get_dr_bcd(); // DR read second - plot_printf(fs_filename, FF_LFN_BUF, "VNA_%06x_%06x.%s", dr, tr, file_ext[format]); + plot_printf(fs_filename, FF_LFN_BUF, "VNA_%06x_%06x.%s", dr, tr, file_opt[format].ext); #else - plot_printf(fs_filename, FF_LFN_BUF, "%08x.%s", rtc_get_FAT(), file_ext[format]); + plot_printf(fs_filename, FF_LFN_BUF, "%08x.%s", rtc_get_FAT(), file_opt[format].ext); #endif } else - plot_printf(fs_filename, FF_LFN_BUF, "%s.%s", name, file_ext[format]); + plot_printf(fs_filename, FF_LFN_BUF, "%s.%s", name, file_opt[format].ext); -// UINT total_size = 0; // systime_t time = chVTGetSystemTimeX(); - // Prepare filename = .s1p / .s2p / .bmp .... and open for write - FRESULT res = vna_create_file(fs_filename); + FRESULT res = ui_create_file(fs_filename); if (res == FR_OK) { - const char *s_file_format; - switch(format) { - /* - * Save touchstone file for VNA (use rev 1.1 format) - * https://en.wikipedia.org/wiki/Touchstone_file - */ - case FMT_S1P_FILE: - case FMT_S2P_FILE: - buf_8 = (char *)spi_buffer; - // Write SxP file - if (format == FMT_S1P_FILE){ - s_file_format = s1_file_param; - // write sxp header (not write NULL terminate at end) - res = f_write(fs_file, s1_file_header, sizeof(s1_file_header)-1, &size); -// total_size+=size; - } - else { - s_file_format = s2_file_param; - // Write s2p header (not write NULL terminate at end) - res = f_write(fs_file, s2_file_header, sizeof(s2_file_header)-1, &size); -// total_size+=size; - } - // Write all points data - for (i = 0; i < sweep_points && res == FR_OK; i++) { - size = plot_printf(buf_8, 128, s_file_format, getFrequency(i), measured[0][i][0], measured[0][i][1], measured[1][i][0], measured[1][i][1]); -// total_size+=size; - res = f_write(fs_file, buf_8, size, &size); - } - break; - /* - * Save bitmap file (use v4 format allow set RGB mask) - */ - case FMT_BMP_FILE: - buf_16 = spi_buffer; - res = f_write(fs_file, bmp_header_v4, sizeof(bmp_header_v4), &size); // Write header struct -// total_size+=size; - lcd_set_background(LCD_SWEEP_LINE_COLOR); - for (y = LCD_HEIGHT-1; y >= 0 && res == FR_OK; y--) { - lcd_read_memory(0, y, LCD_WIDTH, 1, buf_16); - swap_bytes(buf_16, LCD_WIDTH); - res = f_write(fs_file, buf_16, LCD_WIDTH*sizeof(uint16_t), &size); -// total_size+=size; - lcd_fill(LCD_WIDTH-1, y, 1, 1); - } - break; -#ifdef __SD_CARD_DUMP_TIFF__ - case FMT_TIF_FILE: - buf_16 = spi_buffer; - lcd_set_background(LCD_SWEEP_LINE_COLOR); - res = f_write(fs_file, tif_header, sizeof(tif_header), &size); // Write header struct - for (y = 0; y < LCD_HEIGHT && res == FR_OK; y++) { - // Use LCD_WIDTH + 128 bytes offset for RGB888 data - // Use 0 offset for compressed RLE (maximum need WIDTH * 4 + 128 bytes in spi_buffer) - buf_8 = (char *)spi_buffer + 128; - // Read LCD line in RGB565 format (swapped bytes) - lcd_read_memory(0, y, LCD_WIDTH, 1, buf_16); - // Convert to RGB888 - for (int x = LCD_WIDTH - 1; x >= 0; x--) { - uint16_t color = (buf_16[x] << 8) | (buf_16[x] >> 8); - buf_8[3*x + 0] = (color>>8) & 0xF8;// if (buf_8[3*x + 0] < 0) buf_8[3*x + 0]+= 7; - buf_8[3*x + 1] = (color>>3) & 0xFC;// if (buf_8[3*x + 1] < 0) buf_8[3*x + 1]+= 3; - buf_8[3*x + 2] = (color<<3) & 0xF8;// if (buf_8[3*x + 2] < 0) buf_8[3*x + 2]+= 7; - } - size = packbits(buf_8, (char *)spi_buffer, LCD_WIDTH * 3); - res = f_write(fs_file, spi_buffer, size, &size); - lcd_fill(LCD_WIDTH-1, y, 1, 1); - } - break; -#endif - /* - * Save calibration - */ - case FMT_CAL_FILE: - { - const char *src = (char*)¤t_props; - const uint32_t total = sizeof(current_props); - res = f_write(fs_file, src, total, &size); - } - break; -#ifdef __SD_CARD_DUMP_FIRMWARE__ - /* - * Dump firmware to SD card as bin file image - */ - case FMT_BIN_FILE: - { - const char *src = (const char*)FLASH_START_ADDRESS; - const uint32_t total = FLASH_TOTAL_SIZE; - res = f_write(fs_file, src, total, &size); - } - break; -#endif - } + // uint32_t t = getSystemTime(); + res = save(fs_file, format); + // log_printf("dump = %d\n", getSystemTime() - t); f_close(fs_file); -// shell_printf("Close = %d\r\n", res); -// testLog(); // time = chVTGetSystemTimeX() - time; // shell_printf("Total time: %dms (write %d byte/sec)\r\n", time/10, total_size*10000/time); } - - drawMessageBox("SD CARD SAVE", res == FR_OK ? fs_filename : " Fail write ", 2000); + if (keyboard_temp == 1) toggle_sweep(); + ui_message_box("SD CARD SAVE", res == FR_OK ? fs_filename : " Fail write ", 2000); request_to_redraw(REDRAW_AREA|REDRAW_FREQUENCY); ui_mode_normal(); } @@ -1688,34 +1828,43 @@ static uint16_t fixScreenshotFormat(uint16_t data) { return data; } -static UI_FUNCTION_CALLBACK(menu_sdcard_cb) -{ +#ifdef __SD_FILE_BROWSER__ +#include "vna_modules/vna_browser.c" + +static UI_FUNCTION_CALLBACK(menu_sdcard_browse_cb) { + data = fixScreenshotFormat(data); + ui_mode_browser(data); +} +#endif + +static UI_FUNCTION_CALLBACK(menu_sdcard_cb) { + keyboard_temp = (sweep_mode & SWEEP_ENABLE) ? 1 : 0; + if (keyboard_temp) toggle_sweep(); data = fixScreenshotFormat(data); if (VNA_MODE(VNA_MODE_AUTO_NAME)) - vna_save_file(NULL, data); + ui_save_file(NULL, data); else ui_mode_keypad(data + KM_S1P_NAME); // If no auto name, call text keyboard input } +#endif // __USE_SD_CARD__ -#ifdef __SD_FILE_BROWSER__ -#include "vna_modules/vna_browser.c" -#endif -#endif // __USE_SD_CARD__ - -static UI_FUNCTION_ADV_CALLBACK(menu_band_sel_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_band_sel_acb) { (void)data; + static const char* gen_names[] = { + "Si5351", + "MS5351", + "ZEETK" + }; if (b){ - b->p1.text = config._band_mode == 0 ? "Si5351" : "MS5351"; + b->p1.text = gen_names[config._band_mode]; return; } - config._band_mode = config._band_mode == 0 ? 1 : 0; + if (++config._band_mode >= ARRAY_COUNT(gen_names)) config._band_mode = 0; si5351_set_band_mode(config._band_mode); } #if STORED_TRACES > 0 -static UI_FUNCTION_ADV_CALLBACK(menu_stored_trace_acb) -{ +static UI_FUNCTION_ADV_CALLBACK(menu_stored_trace_acb) { if (b){ b->p1.text = getStoredTraces() & (1< menu_back }; @@ -1835,6 +1986,10 @@ const menuitem_t menu_cal[] = { { MT_ADV_CALLBACK, 0, "RANGE", menu_cal_range_acb }, { MT_CALLBACK, 0, "RESET", menu_cal_reset_cb }, { MT_ADV_CALLBACK, 0, "APPLY", menu_cal_apply_acb }, + { MT_ADV_CALLBACK, 0, "ENHANCED\nRESPONSE", menu_cal_enh_acb}, +#ifdef __VNA_Z_RENORMALIZATION__ + { MT_ADV_CALLBACK, KM_CAL_LOAD_R, "STANDARD\nLOAD R " R_LINK_COLOR "%bF" S_OHM, menu_keyboard_acb}, +#endif { MT_NEXT, 0, NULL, menu_back } // next-> menu_back }; @@ -1921,7 +2076,7 @@ const menuitem_t menu_scale[] = { { MT_ADV_CALLBACK, KM_BOTTOM, "BOTTOM", menu_scale_keyboard_acb }, { MT_ADV_CALLBACK, KM_SCALE, "SCALE/DIV", menu_scale_keyboard_acb }, { MT_ADV_CALLBACK, KM_REFPOS, "REFERENCE\nPOSITION", menu_scale_keyboard_acb }, - { MT_ADV_CALLBACK, KM_EDELAY, "E-DELAY\n " R_LINK_COLOR "%b.7F" S_SECOND, menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_EDELAY, "E-DELAY", menu_keyboard_acb }, { MT_ADV_CALLBACK, KM_S21OFFSET, "S21 OFFSET\n " R_LINK_COLOR "%b.3F" S_dB, menu_keyboard_acb }, #ifdef __USE_GRID_VALUES__ { MT_ADV_CALLBACK, VNA_MODE_SHOW_GRID, "SHOW GRID\nVALUES", menu_vna_mode_acb }, @@ -2126,6 +2281,12 @@ const menuitem_t menu_measure_s21[] = { { MT_ADV_CALLBACK, KM_MEASURE_R, " Rl = " R_LINK_COLOR "%b.4F" S_OHM, menu_keyboard_acb}, { MT_NEXT, 0, NULL, menu_back } // next-> menu_back }; + +const menuitem_t menu_measure_filter[] = { + { MT_ADV_CALLBACK, MEASURE_NONE, "OFF", menu_measure_acb }, + { MT_ADV_CALLBACK, MEASURE_FILTER, "FILTER\n (S21)", menu_measure_acb }, + { MT_NEXT, 0, NULL, menu_back } // next-> menu_back +}; #endif const menuitem_t menu_measure[] = { @@ -2143,6 +2304,7 @@ const menuitem_t menu_measure[] = { { MT_ADV_CALLBACK, MEASURE_SHUNT_LC, "SHUNT LC\n (S21)", menu_measure_acb }, { MT_ADV_CALLBACK, MEASURE_SERIES_LC, "SERIES LC\n (S21)", menu_measure_acb }, { MT_ADV_CALLBACK, MEASURE_SERIES_XTAL, "SERIES\nXTAL (S21)", menu_measure_acb }, + { MT_ADV_CALLBACK, MEASURE_FILTER, "FILTER\n (S21)", menu_measure_acb }, #endif { MT_NEXT, 0, NULL, menu_back } // next-> menu_back }; @@ -2157,6 +2319,7 @@ const menuitem_t *menu_measure_list[] = { [MEASURE_SHUNT_LC] = menu_measure_s21, [MEASURE_SERIES_LC] = menu_measure_s21, [MEASURE_SERIES_XTAL] = menu_measure_s21, + [MEASURE_FILTER] = menu_measure_filter, #endif #ifdef __S11_CABLE_MEASURE__ [MEASURE_S11_CABLE] = menu_measure_cable, @@ -2230,6 +2393,9 @@ const menuitem_t menu_device1[] = { #ifdef __DIGIT_SEPARATOR__ { MT_ADV_CALLBACK, VNA_MODE_SEPARATOR, "SEPARATOR\n " R_LINK_COLOR "%s", menu_vna_mode_acb }, #endif +#ifdef __USB_UID__ + { MT_ADV_CALLBACK, VNA_MODE_USB_UID, "USB DEVICE\nUID", menu_vna_mode_acb}, +#endif #ifdef __SD_CARD_DUMP_FIRMWARE__ { MT_CALLBACK, FMT_BIN_FILE, "DUMP\nFIRMWARE", menu_sdcard_cb }, #endif @@ -2244,6 +2410,29 @@ const menuitem_t menu_device1[] = { { MT_NEXT, 0, NULL, menu_back } // next-> menu_back }; +#ifdef __USE_RTC__ +static UI_FUNCTION_ADV_CALLBACK(menu_rtc_out_acb) { + (void)data; + if(b) { + if (rtc_clock_output_enabled()) { + b->icon = BUTTON_ICON_CHECK; + b->p1.text = "ON"; + } else + b->p1.text = "OFF"; + return; + } + rtc_clock_output_toggle(); +} + +const menuitem_t menu_rtc[] = { + { MT_ADV_CALLBACK, KM_RTC_DATE, "SET DATE", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_RTC_TIME, "SET TIME", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_RTC_CAL, "RTC CAL\n " R_LINK_COLOR "%+b.3f" S_PPM, menu_keyboard_acb }, + { MT_ADV_CALLBACK, 0, "RTC 512" S_Hz "\n Led2 %s", menu_rtc_out_acb }, + { MT_NEXT, 0, NULL, menu_back } // next-> menu_back +}; +#endif + const menuitem_t menu_device[] = { { MT_ADV_CALLBACK, KM_THRESHOLD, "THRESHOLD\n " R_LINK_COLOR "%.6q", menu_keyboard_acb }, { MT_ADV_CALLBACK, KM_XTAL, "TCXO\n " R_LINK_COLOR "%.6q", menu_keyboard_acb }, @@ -2257,9 +2446,8 @@ const menuitem_t menu_device[] = { #ifdef __FLIP_DISPLAY__ { MT_ADV_CALLBACK, VNA_MODE_FLIP_DISPLAY, "FLIP\nDISPLAY", menu_vna_mode_acb }, #endif -#ifdef __USE_RTC__ - { MT_ADV_CALLBACK, KM_RTC_DATE, "SET DATE", menu_keyboard_acb }, - { MT_ADV_CALLBACK, KM_RTC_TIME, "SET TIME", menu_keyboard_acb }, +#ifdef __DFU_SOFTWARE_MODE__ + { MT_SUBMENU, 0, S_RARROW "DFU", menu_dfu }, #endif { MT_SUBMENU, 0, S_RARROW" MORE", menu_device1 }, { MT_NEXT, 0, NULL, menu_back } // next-> menu_back @@ -2269,7 +2457,7 @@ const menuitem_t menu_config[] = { { MT_CALLBACK, MENU_CONFIG_TOUCH_CAL, "TOUCH CAL", menu_config_cb }, { MT_CALLBACK, MENU_CONFIG_TOUCH_TEST, "TOUCH TEST", menu_config_cb }, { MT_SUBMENU, 0, "EXPERT\nSETTINGS", menu_device }, - { MT_CALLBACK, 0, "SAVE CONFIG", menu_config_save_cb }, + { MT_CALLBACK, MENU_CONFIG_SAVE , "SAVE CONFIG", menu_config_cb }, #ifdef __USE_SERIAL_CONSOLE__ { MT_SUBMENU, 0, "CONNECTION", menu_connection }, #endif @@ -2277,8 +2465,8 @@ const menuitem_t menu_config[] = { #ifdef __LCD_BRIGHTNESS__ { MT_ADV_CALLBACK, 0, "BRIGHTNESS\n " R_LINK_COLOR "%d%%%%", menu_brightness_acb }, #endif -#ifdef __DFU_SOFTWARE_MODE__ - { MT_SUBMENU, 0, S_RARROW "DFU", menu_dfu }, +#ifdef __USE_RTC__ + { MT_SUBMENU, 0, "DATE/TIME", menu_rtc }, #endif { MT_NEXT, 0, NULL, menu_back } // next-> menu_back }; @@ -2332,9 +2520,7 @@ static int get_lines_count(const char *label) { return n; } -static void -ensure_selection(void) -{ +static void ensure_selection(void) { int i = current_menu_get_count(); if (selection < 0) selection = -1; @@ -2345,9 +2531,7 @@ ensure_selection(void) menu_button_height = MENU_BUTTON_HEIGHT(i); } -static void -menu_move_back(bool leave_ui) -{ +static void menu_move_back(bool leave_ui) { if (menu_current_level == 0) return; menu_current_level--; @@ -2356,14 +2540,12 @@ menu_move_back(bool leave_ui) ui_mode_normal(); } -static void -menu_set_submenu(const menuitem_t *submenu) { +static void menu_set_submenu(const menuitem_t *submenu) { menu_stack[menu_current_level] = submenu; ensure_selection(); } -static void -menu_push_submenu(const menuitem_t *submenu) { +static void menu_push_submenu(const menuitem_t *submenu) { if (menu_current_level < MENU_STACK_DEPTH_MAX-1) menu_current_level++; menu_set_submenu(submenu); @@ -2380,9 +2562,7 @@ menu_move_top(void) } */ -static void -menu_invoke(int item) -{ +static void menu_invoke(int item) { const menuitem_t *menu = current_menu_item(item); if (menu == NULL) return; switch (menu->type) { @@ -2400,60 +2580,146 @@ menu_invoke(int item) } // Redraw menu after if UI in menu mode if (ui_mode == UI_MENU) - draw_menu(-1); + menu_draw(-1); } -// Draw button function -static void -draw_button(uint16_t x, uint16_t y, uint16_t w, uint16_t h, button_t *b) { - uint16_t type = b->border; - uint16_t bw = type & BUTTON_BORDER_WIDTH_MASK; - // Draw border if width > 0 - if (bw) { - uint16_t br = LCD_RISE_EDGE_COLOR; - uint16_t bd = LCD_FALLEN_EDGE_COLOR; - lcd_set_background(type&BUTTON_BORDER_TOP ? br : bd);lcd_fill(x, y, w, bw); // top - lcd_set_background(type&BUTTON_BORDER_LEFT ? br : bd);lcd_fill(x, y, bw, h); // left - lcd_set_background(type&BUTTON_BORDER_RIGHT ? br : bd);lcd_fill(x + w - bw, y, bw, h); // right - lcd_set_background(type&BUTTON_BORDER_BOTTOM ? br : bd);lcd_fill(x, y + h - bw, w, bw); // bottom +// +// UI Menu functions +// +static void menu_draw_buttons(const menuitem_t *m, uint32_t mask) { + int i; + int y = MENU_BUTTON_Y_OFFSET; + for (i = 0; i < MENU_BUTTON_MAX && m; i++, m = menu_next_item(m), y+=menu_button_height) { + if ((mask&(1<fg, b->bg); - if (type & BUTTON_BORDER_NO_FILL) return; - lcd_fill(x + bw, y + bw, w - (bw * 2), h - (bw * 2)); + // Custom button, apply custom settings/label from callback + char *text; + uint16_t text_offs; + if (m->type == MT_ADV_CALLBACK) { + button.label[0] = 0; + if (m->reference) ((menuaction_acb_t)m->reference)(m->data, &button); + // Apply custom text, from button label and + if (button.label[0] == 0) + plot_printf(button.label, sizeof(button.label), m->label, button.p1.u); + text = button.label; } - -// Draw message box function -void drawMessageBox(const char *header, const char *text, uint32_t delay) { - button_t b; - int x , y; - b.bg = LCD_MENU_COLOR; - b.fg = LCD_MENU_TEXT_COLOR; - b.border = BUTTON_BORDER_FLAT|1; - if (header) {// Draw header - draw_button((LCD_WIDTH-MESSAGE_BOX_WIDTH)/2, LCD_HEIGHT/2-40, MESSAGE_BOX_WIDTH, 60, &b); - x = (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 10; - y = LCD_HEIGHT/2-40 + 5; - lcd_drawstring(x, y, header); - request_to_redraw(REDRAW_AREA); + else + text = m->label; + // Draw button + ui_draw_button(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, menu_button_height, &button); + // Draw icon if need (and add extra shift for text) + if (button.icon >= 0) { + lcd_blitBitmap(LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_ICON_OFFSET, y+(menu_button_height-ICON_HEIGHT)/2, ICON_WIDTH, ICON_HEIGHT, ICON_GET_DATA(button.icon)); + text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_ICON_OFFSET + ICON_SIZE; + } else + text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_TEXT_OFFSET; + // Draw button text + int lines = get_lines_count(text); +#if _USE_FONT_ != _USE_SMALL_FONT_ + if (menu_button_height < lines * FONT_GET_HEIGHT + 2) { + lcd_set_font(FONT_SMALL); + lcd_drawstring(text_offs, y+(menu_button_height - lines * sFONT_GET_HEIGHT - 1)/2, text); } - if (text) { // Draw window - lcd_set_colors(LCD_MENU_TEXT_COLOR, LCD_FG_COLOR); - lcd_fill((LCD_WIDTH-MESSAGE_BOX_WIDTH)/2+3, LCD_HEIGHT/2-40+FONT_STR_HEIGHT+8, MESSAGE_BOX_WIDTH-6, 60-FONT_STR_HEIGHT-8-3); - x = (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 20; - y = LCD_HEIGHT/2-40 + FONT_STR_HEIGHT + 8 + 14; - lcd_drawstring(x, y, text); - request_to_redraw(REDRAW_AREA); + else { + lcd_set_font(FONT_NORMAL); + lcd_drawstring(text_offs, y+(menu_button_height - lines * FONT_GET_HEIGHT)/2, text); + } +#else + lcd_drawstring(text_offs, y+(menu_button_height - lines * FONT_GET_HEIGHT)/2, text); +#endif + } + // Erase empty buttons + if (AREA_HEIGHT_NORMAL + OFFSETY > y) { + lcd_set_background(LCD_BG_COLOR); + lcd_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, AREA_HEIGHT_NORMAL + OFFSETY - y); + } + lcd_set_font(FONT_NORMAL); +} + +static void menu_draw(uint32_t mask) { + menu_draw_buttons(menu_stack[menu_current_level], mask); +} + +#if 0 +static void erase_menu_buttons(void) { + lcd_set_background(LCD_BG_COLOR); + lcd_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, 0, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT*MENU_BUTTON_MAX); +} +#endif + +// Menu mode processing +static void ui_mode_menu(void) { + if (ui_mode == UI_MENU) + return; + + ui_mode = UI_MENU; + // narrowen plotting area + set_area_size(AREA_WIDTH_NORMAL - MENU_BUTTON_WIDTH, AREA_HEIGHT_NORMAL); + ensure_selection(); + menu_draw(-1); +} + +static void ui_menu_lever(uint16_t status) { + uint16_t count = current_menu_get_count(); + if (status & EVT_BUTTON_SINGLE_CLICK) { + if ((uint16_t)selection >= count) + ui_mode_normal(); + else + menu_invoke(selection); + return; } do { - chThdSleepMilliseconds(delay == 0 ? 50 : delay); - } while (delay == 0 && btn_check() != EVT_BUTTON_SINGLE_CLICK && touch_check() != EVT_TOUCH_PRESSED); + uint32_t mask = 1<= count){ + ui_mode_normal(); + return; +} + menu_draw(mask|(1<p1.f = get_trace_scale(current_trace); return;} + if (b) return; set_trace_scale(current_trace, keyboard_get_float()); } UI_KEYBOARD_CALLBACK(input_ref) { (void)data; - if (b) {b->p1.f = get_trace_refpos(current_trace); return;} + if (b) return; set_trace_refpos(current_trace, keyboard_get_float()); } UI_KEYBOARD_CALLBACK(input_edelay) { (void)data; - if (b) {b->p1.f = electrical_delay; return;} - set_electrical_delay(keyboard_get_float()); + if (current_trace == TRACE_INVALID) return; + int ch = trace[current_trace].channel; + if (b) { + plot_printf(b->label, sizeof(b->label), "E-DELAY S%d1\n " R_LINK_COLOR "%.7F" S_SECOND, ch + 1, current_props._electrical_delay[ch]); + return; +} + set_electrical_delay(ch, keyboard_get_float()); } UI_KEYBOARD_CALLBACK(input_s21_offset) { @@ -2752,9 +3033,9 @@ UI_KEYBOARD_CALLBACK(input_measure_r) { #ifdef __VNA_Z_RENORMALIZATION__ UI_KEYBOARD_CALLBACK(input_portz) { - (void)data; - if (b) {b->p1.f = current_props._portz; return;} - current_props._portz = keyboard_get_float(); + if (b) {b->p1.f = data ? current_props._cal_load_r : current_props._portz; return;} + if (data) current_props._cal_load_r = keyboard_get_float(); + else current_props._portz = keyboard_get_float(); } #endif @@ -2795,12 +3076,21 @@ UI_KEYBOARD_CALLBACK(input_date_time) { } rtc_set_time(dt_buf[1], dt_buf[0]); } + +UI_KEYBOARD_CALLBACK(input_rtc_cal) { + (void)data; + if (b) { + b->p1.f = rtc_get_cal(); + return; + } + rtc_set_cal(keyboard_get_float()); +} #endif #ifdef __USE_SD_CARD__ UI_KEYBOARD_CALLBACK(input_filename) { if (b) return; - vna_save_file(kp_buf, data); + ui_save_file(kp_buf, data); } #endif @@ -2836,10 +3126,12 @@ const keypads_list keypads_mode_tbl[KM_NONE] = { #endif #ifdef __VNA_Z_RENORMALIZATION__ [KM_Z_PORT] = {KEYPAD_UFLOAT, 0, "PORT Z 50" S_RARROW, input_portz }, // Port Z renormalization impedance +[KM_CAL_LOAD_R] = {KEYPAD_UFLOAT, 1, "STANDARD\n LOAD R", input_portz }, // Calibration standard load R #endif #ifdef __USE_RTC__ [KM_RTC_DATE] = {KEYPAD_UFLOAT, KM_RTC_DATE, "SET DATE\nYY MM DD", input_date_time}, // Date [KM_RTC_TIME] = {KEYPAD_UFLOAT, KM_RTC_TIME, "SET TIME\nHH MM SS", input_date_time}, // Time +[KM_RTC_CAL] = {KEYPAD_FLOAT, 0, "RTC CAL", input_rtc_cal }, // RTC calibration in ppm #endif #ifdef __USE_SD_CARD__ [KM_S1P_NAME] = {KEYPAD_TEXT, FMT_S1P_FILE, "S1P", input_filename }, // s1p filename @@ -2855,14 +3147,13 @@ const keypads_list keypads_mode_tbl[KM_NONE] = { #endif }; -static void -keypad_set_value(void) { - const keyboard_cb_t cb = keypads_mode_tbl[keypad_mode].cb; - if (cb) cb(keypads_mode_tbl[keypad_mode].data, NULL); +// Keyboard callback function for UI button +void ui_keyboard_cb(uint16_t data, button_t *b) { + const keyboard_cb_t cb = keypads_mode_tbl[data].cb; + if (cb) cb(keypads_mode_tbl[data].data, b); } -static void -draw_keypad_button(int id) { +static void keypad_draw_button(int id) { if (id < 0) return; button_t button; button.fg = LCD_MENU_TEXT_COLOR; @@ -2875,33 +3166,32 @@ draw_keypad_button(int id) { button.border = KEYBOARD_BUTTON_BORDER|BUTTON_BORDER_RISE; } - const keypad_pos_t *p = &key_pos[keypads[0].c]; - int x = p->x_offs + (keypads[id+1].pos>> 4) * p->width; - int y = p->y_offs + (keypads[id+1].pos&0xF) * p->height; - draw_button(x, y, p->width, p->height, &button); - if (keypads[0].c == NUM_KEYBOARD) { - lcd_drawfont(keypads[id+1].c, + const keypad_pos_t *p = &key_pos[keypads->type]; + int x = p->x_offs + (keypads->buttons[id].pos>> 4) * p->width; + int y = p->y_offs + (keypads->buttons[id].pos&0xF) * p->height; + ui_draw_button(x, y, p->width, p->height, &button); + uint8_t ch = keypads->buttons[id].c; + if (ch == KP_EMPTY) return; // Empty button + if (keypads->type == NUM_KEYBOARD) { + lcd_drawfont(ch, x + (KP_WIDTH - NUM_FONT_GET_WIDTH) / 2, y + (KP_HEIGHT - NUM_FONT_GET_HEIGHT) / 2); } else { #if 0 - lcd_drawchar(keypads[id+1].c, + lcd_drawchar(ch, x + (KPF_WIDTH - FONT_WIDTH) / 2, y + (KPF_HEIGHT - FONT_GET_HEIGHT) / 2); #else - lcd_drawchar_size(keypads[id+1].c, + lcd_drawchar_size(ch, x + KPF_WIDTH/2 - FONT_WIDTH + 1, y + KPF_HEIGHT/2 - FONT_GET_HEIGHT, 2); #endif } } -static void -draw_keypad(void) -{ - int i; - for(i = 0; i < keypads[0].pos; i++) - draw_keypad_button(i); +static void keypad_draw(void) { + for(int i = 0; i < keypads->size; i++) + keypad_draw_button(i); } static int period_pos(void) {int j; for (j = 0; kp_buf[j] && kp_buf[j] != '.'; j++); return j;} @@ -2914,9 +3204,7 @@ static void draw_numeric_area_frame(void) { lcd_drawstring(10, LCD_HEIGHT-(FONT_STR_HEIGHT * lines + NUM_INPUT_HEIGHT)/2, label); } -static void -draw_numeric_input(const char *buf) -{ +static void draw_numeric_input(const char *buf) { uint16_t x = 14 + 10 * FONT_WIDTH; uint16_t y = LCD_HEIGHT-(NUM_FONT_GET_HEIGHT+NUM_INPUT_HEIGHT)/2; uint16_t xsim; @@ -2944,9 +3232,7 @@ draw_numeric_input(const char *buf) lcd_fill(x, y, NUM_FONT_GET_WIDTH+2+10, NUM_FONT_GET_HEIGHT); } -static void -draw_text_input(const char *buf) -{ +static void draw_text_input(const char *buf) { lcd_set_colors(LCD_INPUT_TEXT_COLOR, LCD_INPUT_BG_COLOR); #if 0 uint16_t x = 14 + 5 * FONT_WIDTH; @@ -2962,14 +3248,14 @@ draw_text_input(const char *buf) #endif } -/* - * Keyboard UI processing - */ -static int -num_keypad_click(int c, int kp_index) { +//===================================================================================================== +// Keyboard UI processing +//===================================================================================================== +enum {K_CONTINUE = 0, K_DONE, K_CANCEL}; +static int num_keypad_click(int c, int kp_index) { if (c >= KP_k && c <= KP_PERCENT) { if (kp_index == 0) - return KP_CANCEL; + return K_CANCEL; if (c >= KP_k && c <= KP_G) { // Apply k, M, G input (add zeroes and shift . right) uint16_t scale = c - KP_k + 1; scale+= (scale<<1); @@ -2984,7 +3270,7 @@ num_keypad_click(int c, int kp_index) { kp_buf[kp_index ] = prefix[c - KP_m]; kp_buf[kp_index+1] = 0; } - return KP_DONE; + return K_DONE; } #ifdef __USE_RTC__ int maxlength = (1< 0; i--) kp_buf[i] = kp_buf[i-1]; kp_buf[0] = '-'; if (kp_index < maxlength) ++kp_index;} -// if (kp_index == 0) -// kp_buf[kp_index++] = '-'; } else if (kp_index < maxlength) { if (c <= KP_9) kp_buf[kp_index++] = '0' + c; @@ -3008,245 +3292,97 @@ num_keypad_click(int c, int kp_index) { } kp_buf[kp_index] = '\0'; draw_numeric_input(kp_buf); - return KP_CONTINUE; + return K_CONTINUE; } -static int -txt_keypad_click(int c, int kp_index) -{ +static int txt_keypad_click(int c, int kp_index) { if (c == S_ENTER[0]) { // Enter - return kp_index == 0 ? KP_CANCEL : KP_DONE; + return kp_index == 0 ? K_CANCEL : K_DONE; } if (c == S_LARROW[0]) { // Backspace if (kp_index == 0) - return KP_CANCEL; + return K_CANCEL; --kp_index; } else if (kp_index < TXTINPUT_LEN) { // any other text input kp_buf[kp_index++] = c; } kp_buf[kp_index] = '\0'; draw_text_input(kp_buf); - return KP_CONTINUE; + return K_CONTINUE; } -static void -ui_mode_keypad(int _keypad_mode) -{ +static void ui_mode_keypad(int mode) { if (ui_mode == UI_KEYPAD) return; + ui_mode = UI_KEYPAD; set_area_size(0, 0); // keypads array - keypad_mode = _keypad_mode; - keypads = keypad_type_list[keypads_mode_tbl[keypad_mode].keypad_type]; + keypad_mode = mode; + keypads = keypad_type_list[keypads_mode_tbl[mode].keypad_type]; selection = -1; kp_buf[0] = 0; - ui_mode = UI_KEYPAD; - draw_menu(-1); - draw_keypad(); +//menu_draw(-1); + keypad_draw(); draw_numeric_area_frame(); } -static void -keypad_click(int key) { - int c = keypads[key+1].c; // !!! Use key + 1 (zero key index used or size define) +static void keypad_click(int key) { + int c = keypads->buttons[key].c; // !!! Use key + 1 (zero key index used or size define) int index = strlen(kp_buf); - int result = keypads[0].c == NUM_KEYBOARD ? num_keypad_click(c, index) : txt_keypad_click(c, index); - if (result == KP_DONE) keypad_set_value(); // apply input done + int result = keypads->type == NUM_KEYBOARD ? num_keypad_click(c, index) : txt_keypad_click(c, index); + if (result == K_DONE) ui_keyboard_cb(keypad_mode, NULL); // apply input done // Exit loop on done or cancel - if (result != KP_CONTINUE) + if (result != K_CONTINUE) ui_mode_normal(); } -static void -ui_keypad_touch(int touch_x, int touch_y) -{ - const keypad_pos_t *p = &key_pos[keypads[0].c]; +static void ui_keypad_touch(int touch_x, int touch_y) { + const keypad_pos_t *p = &key_pos[keypads->type]; if (touch_x < p->x_offs || touch_y < p->y_offs) return; // Calculate key position from touch x and y touch_x-= p->x_offs; touch_x/= p->width; touch_y-= p->y_offs; touch_y/= p->height; uint8_t pos = (touch_y & 0x0F) | (touch_x<<4); - for (int i = 0; i < keypads[0].pos; i++) { - if (keypads[i+1].pos != pos) continue; + for (int i = 0; i < keypads->size; i++) { + if (keypads->buttons[i].pos != pos) continue; + if (keypads->buttons[i].c == KP_EMPTY) break; // Empty int old = selection; - draw_keypad_button(selection = i); // draw new focus - draw_keypad_button(old); // Erase old focus + keypad_draw_button(selection = i); // draw new focus + keypad_draw_button(old); // Erase old focus touch_wait_release(); selection = -1; - draw_keypad_button(i); // erase new focus + keypad_draw_button(i); // erase new focus keypad_click(i); // Process input return; } return; } -static void -ui_keypad_lever(uint16_t status) -{ +static void ui_keypad_lever(uint16_t status) { if (status == EVT_BUTTON_SINGLE_CLICK) { if (selection >= 0) // Process input keypad_click(selection); return; } - int keypads_last_index = keypads[0].pos - 1; + int keypads_last_index = keypads->size - 1; do { int old = selection; + do { if ((status & EVT_DOWN) && --selection < 0) selection = keypads_last_index; if ((status & EVT_UP) && ++selection > keypads_last_index) selection = 0; - draw_keypad_button(old); - draw_keypad_button(selection); + } while (keypads->buttons[selection].c == KP_EMPTY); // Skip empty + keypad_draw_button(old); + keypad_draw_button(selection); chThdSleepMilliseconds(100); } while ((status = btn_wait_release()) != 0); } -//========================== end keyboard input ======================= +//==================================== end keyboard input ============================================= -// -// UI Menu functions -// -static void -draw_menu_buttons(const menuitem_t *m, uint32_t mask) -{ - int i; - int y = MENU_BUTTON_Y_OFFSET; - for (i = 0; i < MENU_BUTTON_MAX && m; i++, m = menu_next_item(m), y+=menu_button_height) { - if ((mask&(1<type == MT_ADV_CALLBACK) { - button.label[0] = 0; - if (m->reference) ((menuaction_acb_t)m->reference)(m->data, &button); - // Apply custom text, from button label and - if (button.label[0] == 0) - plot_printf(button.label, sizeof(button.label), m->label, button.p1.u); - text = button.label; - } - else - text = m->label; - // Draw button - draw_button(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, menu_button_height, &button); - // Draw icon if need (and add extra shift for text) - if (button.icon >= 0) { - lcd_blitBitmap(LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_ICON_OFFSET, y+(menu_button_height-ICON_HEIGHT)/2, ICON_WIDTH, ICON_HEIGHT, ICON_GET_DATA(button.icon)); - text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_ICON_OFFSET + ICON_SIZE; - } else - text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + MENU_TEXT_OFFSET; - // Draw button text - int lines = get_lines_count(text); -#if _USE_FONT_ != _USE_SMALL_FONT_ - if (menu_button_height < lines * FONT_GET_HEIGHT + 2) { - lcd_set_font(FONT_SMALL); - lcd_drawstring(text_offs, y+(menu_button_height - lines * sFONT_GET_HEIGHT - 1)/2, text); - } - else { - lcd_set_font(FONT_NORMAL); - lcd_drawstring(text_offs, y+(menu_button_height - lines * FONT_GET_HEIGHT)/2, text); - } -#else - lcd_drawstring(text_offs, y+(menu_button_height - lines * FONT_GET_HEIGHT)/2, text); -#endif - } - // Erase empty buttons - if (AREA_HEIGHT_NORMAL + OFFSETY > y) { - lcd_set_background(LCD_BG_COLOR); - lcd_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, AREA_HEIGHT_NORMAL + OFFSETY - y); - } - lcd_set_font(FONT_NORMAL); -} - -static void -draw_menu(uint32_t mask) -{ - draw_menu_buttons(menu_stack[menu_current_level], mask); -} - -#if 0 -static void -erase_menu_buttons(void) -{ - lcd_set_background(LCD_BG_COLOR); - lcd_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, 0, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT*MENU_BUTTON_MAX); -} -#endif - -// Menu mode processing -static void -ui_mode_menu(void) -{ - if (ui_mode == UI_MENU) - return; - - ui_mode = UI_MENU; - // narrowen plotting area - set_area_size(AREA_WIDTH_NORMAL - MENU_BUTTON_WIDTH, AREA_HEIGHT_NORMAL); - ensure_selection(); - draw_menu(-1); -} - -static void -ui_menu_lever(uint16_t status) -{ - uint16_t count = current_menu_get_count(); - if (status & EVT_BUTTON_SINGLE_CLICK) { - if ((uint16_t)selection >= count) - ui_mode_normal(); - else - menu_invoke(selection); - return; - } - - do { - uint32_t mask = 1<= count){ - ui_mode_normal(); - return; - } - draw_menu(mask|(1< 10000 // 6791 -> 5000 // 341 -> 200 -static freq_t -step_round(freq_t v) { +static freq_t step_round(freq_t v) { // decade step freq_t nx, x = 1; while((nx = x*10) < v) x = nx; @@ -3304,8 +3435,7 @@ step_round(freq_t v) { return x * 5; } -static void -lever_frequency(uint16_t status) { +static void lever_frequency(uint16_t status) { uint16_t mode; freq_t freq; if (lever_mode == LM_FREQ_0) { @@ -3329,10 +3459,9 @@ lever_frequency(uint16_t status) { } #define STEPRATIO 0.2f -static void -lever_edelay(uint16_t status) -{ - float value = electrical_delay; +static void lever_edelay(uint16_t status) { + int ch = current_trace != TRACE_INVALID ? trace[current_trace].channel : 0; + float value = current_props._electrical_delay[ch]; if (current_props._var_delay == 0.0f) { float ratio = value > 0 ? STEPRATIO : -STEPRATIO; if (status & EVT_UP ) value*= (1.0f + ratio); @@ -3341,13 +3470,11 @@ lever_edelay(uint16_t status) if (status & EVT_UP ) value+= current_props._var_delay; if (status & EVT_DOWN) value-= current_props._var_delay; } - set_electrical_delay(value); + set_electrical_delay(ch, value); while (btn_wait_release() != 0); } -static bool -touch_pickup_marker(int touch_x, int touch_y) -{ +static bool touch_pickup_marker(int touch_x, int touch_y) { touch_x -= OFFSETX; touch_y -= OFFSETY; int i = MARKER_INVALID, mt, m, t; @@ -3395,14 +3522,12 @@ touch_pickup_marker(int touch_x, int touch_y) return TRUE; } -static bool -touch_lever_mode_select(int touch_x, int touch_y) -{ +static bool touch_lever_mode_select(int touch_x, int touch_y) { int mode = -1; if (touch_y > HEIGHT && (props_mode & DOMAIN_MODE) == DOMAIN_FREQ) // Only for frequency domain mode = touch_x < FREQUENCIES_XPOS2 ? LM_FREQ_0 : LM_FREQ_1; if (touch_y < UI_MARKER_Y0) - mode = (touch_x < (LCD_WIDTH / 2) && electrical_delay != 0.0) ? LM_EDELAY : LM_MARKER; + mode = (touch_x < (LCD_WIDTH / 2) && get_electrical_delay() != 0.0f) ? LM_EDELAY : LM_MARKER; if (mode == -1) return FALSE; touch_wait_release(); @@ -3417,9 +3542,7 @@ touch_lever_mode_select(int touch_x, int touch_y) return TRUE; } -static void -ui_normal_lever(uint16_t status) -{ +static void ui_normal_lever(uint16_t status) { if (status & EVT_BUTTON_SINGLE_CLICK) { ui_mode_menu(); return; @@ -3435,8 +3558,7 @@ ui_normal_lever(uint16_t status) } } -static bool -touch_apply_ref_scale(int touch_x, int touch_y) { +static bool touch_apply_ref_scale(int touch_x, int touch_y) { int t = current_trace; // do not scale invalid or smith chart if (t == TRACE_INVALID || trace[t].type == TRC_SMITH) return FALSE; @@ -3457,8 +3579,7 @@ touch_apply_ref_scale(int touch_x, int touch_y) { } #ifdef __USE_SD_CARD__ -static bool -touch_made_screenshot(int touch_x, int touch_y) { +static bool touch_made_screenshot(int touch_x, int touch_y) { if (touch_y < HEIGHT || touch_x < FREQUENCIES_XPOS3 || touch_x > FREQUENCIES_XPOS2) return FALSE; touch_wait_release(); @@ -3467,26 +3588,19 @@ touch_made_screenshot(int touch_x, int touch_y) { } #endif -static void -ui_normal_touch(int touch_x, int touch_y) { - // Try drag marker - if (touch_pickup_marker(touch_x, touch_y)) - return; +static void ui_normal_touch(int touch_x, int touch_y) { + if (touch_pickup_marker(touch_x, touch_y)) return; // Try drag marker #ifdef __USE_SD_CARD__ - // Try made screenshot - if (touch_made_screenshot(touch_x, touch_y)) - return; + if (touch_made_screenshot(touch_x, touch_y)) return; // Try made screenshot #endif - if (touch_apply_ref_scale(touch_x, touch_y)) - return; - // Try select lever mode (top and bottom screen) - if (touch_lever_mode_select(touch_x, touch_y)) - return; + if (touch_lever_mode_select(touch_x, touch_y)) return; // Try select lever mode (top and bottom screen) + if (touch_apply_ref_scale(touch_x, touch_y)) return; // Try apply ref / scale // default: switch menu mode after release touch_wait_release(); ui_mode_menu(); } -//========================== end normal plot input ======================= +//================================== end normal plot input ============================================ + static const struct { void (*button)(uint16_t status); void (*touch)(int touch_x, int touch_y); @@ -3499,16 +3613,13 @@ static const struct { #endif }; -static void -ui_process_lever(void) { +static void ui_process_lever(void) { uint16_t status = btn_check(); if (status) ui_handler[ui_mode].button(status); } -static -void ui_process_touch(void) -{ +static void ui_process_touch(void) { int touch_x, touch_y; int status = touch_check(); if (status == EVT_TOUCH_PRESSED || status == EVT_TOUCH_DOWN) { @@ -3517,9 +3628,7 @@ void ui_process_touch(void) } } -void -ui_process(void) -{ +void ui_process(void) { //if (ui_mode >= UI_END) return; // for safe if (operation_requested&OP_LEVER) ui_process_lever(); @@ -3538,8 +3647,7 @@ void handle_button_interrupt(uint16_t channel) { //static systime_t t_time = 0; // Triggered touch interrupt call -void handle_touch_interrupt(void) -{ +void handle_touch_interrupt(void) { operation_requested|= OP_TOUCH; // systime_t n_time = chVTGetSystemTimeX(); // shell_printf("%d\r\n", n_time - t_time); @@ -3547,8 +3655,7 @@ void handle_touch_interrupt(void) } #if HAL_USE_EXT == TRUE // Use ChibiOS EXT code (need lot of flash ~1.5k) -static void handle_button_ext(EXTDriver *extp, expchannel_t channel) -{ +static void handle_button_ext(EXTDriver *extp, expchannel_t channel) { (void)extp; handle_button_interrupt((uint16_t)channel); } @@ -3594,9 +3701,7 @@ static void init_EXT(void) { } #endif -void -ui_init() -{ +void ui_init() { adc_init(); // Activates the EXT driver 1. init_EXT(); diff --git a/usbcfg.c b/usbcfg.c index bfb5d4b..9368aa8 100644 --- a/usbcfg.c +++ b/usbcfg.c @@ -15,10 +15,17 @@ */ #include "hal.h" +#include "nanovna.h" /* Virtual serial port over USB.*/ SerialUSBDriver SDU1; +enum { + STR_LANG_ID = 0, + STR_MANUFACTURER, + STR_PRODUCT, + STR_SERIAL +}; /* * Endpoints to be used for USBD1. */ @@ -38,9 +45,9 @@ static const uint8_t vcom_device_descriptor_data[18] = { 0x0483, /* idVendor (ST). */ 0x5740, /* idProduct. */ 0x0200, /* bcdDevice. */ - 1, /* iManufacturer. */ - 2, /* iProduct. */ - 3, /* iSerialNumber. */ + STR_MANUFACTURER, /* iManufacturer. */ + STR_PRODUCT, /* iProduct. */ + STR_SERIAL, /* iSerialNumber. */ 1) /* bNumConfigurations. */ }; @@ -185,12 +192,41 @@ static const uint8_t vcom_string3[] = { * Strings wrappers array. */ static const USBDescriptor vcom_strings[] = { - {sizeof vcom_string0, vcom_string0}, - {sizeof vcom_string1, vcom_string1}, - {sizeof vcom_string2, vcom_string2}, - {sizeof vcom_string3, vcom_string3} + [STR_LANG_ID] = {sizeof vcom_string0, vcom_string0}, + [STR_MANUFACTURER] = {sizeof vcom_string1, vcom_string1}, + [STR_PRODUCT] = {sizeof vcom_string2, vcom_string2}, + [STR_SERIAL] = {sizeof vcom_string3, vcom_string3} }; +#ifdef __USB_UID__ +// Use unique serial string generated from MCU id +#define UID_RADIX 5 // Radix conversion constant (5 bit, use 0..9 and A..V) +#define USB_SERIAL_STRING_SIZE (64 / UID_RADIX) // Result string size +USBDescriptor *getSerialStringDescriptor(void) { + uint16_t i; + uint16_t *buf = ((uint16_t *)&spi_buffer[ARRAY_COUNT(spi_buffer)]) - USB_SERIAL_STRING_SIZE - 4; // 16 byte align + USBDescriptor *d = ((USBDescriptor *)buf) - 1; + uint32_t id0 = *(uint32_t *)0x1FFFF7AC; // MCU id0 address + uint32_t id1 = *(uint32_t *)0x1FFFF7B0; // MCU id1 address + uint32_t id2 = *(uint32_t *)0x1FFFF7B4; // MCU id2 address + uint64_t uid = id1; + id0+= id2; + uid|= id0 | (uid<<32); // generate unique 64bit ID + // Prepare serial string descriptor from 64 bit ID + for(i = 1; i < USB_SERIAL_STRING_SIZE + 1; i++) { + uint16_t c = uid & ((1<>= UID_RADIX; + } + uint16_t size = i * sizeof(uint16_t); + buf[0] = size | (USB_DESCRIPTOR_STRING << 8); + // Generate USBDescriptor structure + d->ud_size = size; + d->ud_string = (uint8_t *)buf; + return d; +} +#endif + /* * Handles the GET_DESCRIPTOR callback. All required descriptors must be * handled here. @@ -208,6 +244,10 @@ static const USBDescriptor *get_descriptor(USBDriver *usbp, case USB_DESCRIPTOR_CONFIGURATION: return &vcom_configuration_descriptor; case USB_DESCRIPTOR_STRING: +#ifdef __USB_UID__ // send unique USB serial string if need + if (dindex == STR_SERIAL && VNA_MODE(VNA_MODE_USB_UID)) + return getSerialStringDescriptor(); +#endif if (dindex < 4) return &vcom_strings[dindex]; } diff --git a/vna_modules/vna_browser.c b/vna_modules/vna_browser.c index 312d3f9..2877161 100644 --- a/vna_modules/vna_browser.c +++ b/vna_modules/vna_browser.c @@ -26,6 +26,8 @@ static uint16_t browser_mode; // Buttons in browser enum {FILE_BUTTON_LEFT = 0, FILE_BUTTON_RIGHT, FILE_BUTTON_EXIT, FILE_BUTTON_DEL, FILE_BUTTON_FILE}; + +#define SMALL_BUTTON_SIZE (6 * FONT_WIDTH) // Button position on screen typedef struct { uint16_t x; @@ -35,12 +37,12 @@ typedef struct { uint8_t ofs; } browser_btn_t; static const browser_btn_t browser_btn[] = { - [FILE_BUTTON_LEFT] = { 0 + 40, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, LCD_WIDTH/2 - 80, FILE_BOTTOM_HEIGHT, (LCD_WIDTH/2 - 80 - FONT_WIDTH)/2}, // < previous - [FILE_BUTTON_RIGHT]= {LCD_WIDTH/2 + 40, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, LCD_WIDTH/2 - 80, FILE_BOTTOM_HEIGHT, (LCD_WIDTH/2 - 80 - FONT_WIDTH)/2}, // > next - [FILE_BUTTON_EXIT] = {LCD_WIDTH - 40, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, 40, FILE_BOTTOM_HEIGHT, ( 40 - FONT_WIDTH)/2}, // X exit - [FILE_BUTTON_DEL] = { 0 + 0, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, 40, FILE_BOTTOM_HEIGHT, ( 40 - 3*FONT_WIDTH)/2}, // DEL + [FILE_BUTTON_LEFT] = { 0 + SMALL_BUTTON_SIZE, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, LCD_WIDTH/2 - 2*SMALL_BUTTON_SIZE, FILE_BOTTOM_HEIGHT, (LCD_WIDTH/2 - 2*SMALL_BUTTON_SIZE - FONT_WIDTH)/2}, // < previous + [FILE_BUTTON_RIGHT]= {LCD_WIDTH/2 + SMALL_BUTTON_SIZE, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, LCD_WIDTH/2 - 2*SMALL_BUTTON_SIZE, FILE_BOTTOM_HEIGHT, (LCD_WIDTH/2 - 2*SMALL_BUTTON_SIZE - FONT_WIDTH)/2}, // > next + [FILE_BUTTON_EXIT] = {LCD_WIDTH - SMALL_BUTTON_SIZE, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, SMALL_BUTTON_SIZE, FILE_BOTTOM_HEIGHT, ( SMALL_BUTTON_SIZE - FONT_WIDTH)/2}, // X exit + [FILE_BUTTON_DEL] = { 0 + 0, LCD_HEIGHT - FILE_BOTTOM_HEIGHT, SMALL_BUTTON_SIZE, FILE_BOTTOM_HEIGHT, ( SMALL_BUTTON_SIZE - 3*FONT_WIDTH)/2}, // DEL // File button, only size and start position, must be idx = FILE_BUTTON_FILE - [FILE_BUTTON_FILE] = { 0, 0, LCD_WIDTH/FILES_COLUMNS, FILE_BUTTON_HEIGHT, 5}, + [FILE_BUTTON_FILE] = { 0, 0, LCD_WIDTH/FILES_COLUMNS, FILE_BUTTON_HEIGHT, FONT_WIDTH/2 + 3}, }; static void browser_get_button_pos(int idx, browser_btn_t *b) { @@ -71,22 +73,10 @@ static void browser_draw_button(int idx, const char *txt) { b.fg = LCD_MENU_TEXT_COLOR; b.border = (idx == selection) ? BROWSER_BUTTON_BORDER|BUTTON_BORDER_FALLING : BROWSER_BUTTON_BORDER|BUTTON_BORDER_RISE; if (txt == NULL) b.border|= BUTTON_BORDER_NO_FILL; - draw_button(btn.x, btn.y, btn.w, btn.h, &b); + ui_draw_button(btn.x, btn.y, btn.w, btn.h, &b); if (txt) lcd_printf(btn.x + btn.ofs, btn.y + (btn.h - FONT_STR_HEIGHT) / 2, txt); } -static char to_lower(char c) {return (c >='A' && c <= 'Z') ? c - 'A' + 'a' : c;} - -static bool strcmpi(const char *t1, const char *t2) { - int i = 0; - while (1) { - char ch1 = to_lower(t1[i]), ch2 = to_lower(t2[i]); - if (ch1 != ch2) return false; - if (ch1 == 0) return true; - i++; - } -} - static bool compare_ext(const char *name, const char *ext) { int i = 0, j = 0; while (name[i]) if (name[i++] == '.') j = i; // Get last '.' position + 1 @@ -111,14 +101,13 @@ static FRESULT sd_open_dir(DIR* dp, const TCHAR* path, const TCHAR* pattern) { static void browser_open_file(int sel) { FILINFO fno; - FRESULT res; DIR dj; int cnt; if ((uint16_t)sel >= file_count) return; if (f_mount(fs_volume, "", 1) != FR_OK) return; repeat: cnt = sel; - if (sd_open_dir(&dj, "", file_ext[keypad_mode]) != FR_OK) return; // open dir + if (sd_open_dir(&dj, "", file_opt[keypad_mode].ext) != FR_OK) return; // open dir while (sd_findnext(&dj, &fno) == FR_OK && cnt != 0) cnt--; // skip cnt files f_closedir(&dj); if (cnt != 0) return; @@ -126,161 +115,25 @@ static void browser_open_file(int sel) { // Delete file if in delete mode if (browser_mode & BROWSER_DELETE) {f_unlink(fno.fname); return;} - const char *error = NULL; - bool leave_show = true; - UINT size; - if (f_open(fs_file, fno.fname, FA_READ) != FR_OK) return; - + // Load file, get load function + file_load_cb_t load = file_opt[keypad_mode].load; + if (load == NULL) return; + // lcd_set_colors(LCD_FG_COLOR, LCD_BG_COLOR); - switch (keypad_mode) { - /* - * S1P or S2P touchstone file loader - * Support only NanoVNA format: Hz S RI R 50 - */ - case FMT_S1P_FILE: - case FMT_S2P_FILE: - { - const int buffer_size = 256; - const int line_size = 128; - char *buf_8 = (char *)spi_buffer; // must be greater then buffer_size + line_size - char *line = buf_8 + buffer_size; - uint16_t j = 0, i, count = 0; - freq_t start = 0, stop = 0, f; - while (f_read(fs_file, buf_8, buffer_size, &size) == FR_OK && size > 0) { - for (i = 0; i < size; i++) { - uint8_t c = buf_8[i]; - if (c == '\r') { // New line (Enter) - 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 - f = my_atoui(args[0]); // Get frequency - if (count >= SWEEP_POINTS_MAX || f > FREQUENCY_MAX) {error = "Format err"; goto finish;} - if (count == 0) start = f; // For index 0 set as start - stop = f; // last set as stop - measured[0][count][0] = my_atof(args[1]); - measured[0][count][1] = my_atof(args[2]); // get S11 data - if (keypad_mode == FMT_S2P_FILE && nargs >= 4) { - measured[1][count][0] = my_atof(args[3]); - measured[1][count][1] = my_atof(args[4]); // get S11 data - } else { - measured[1][count][0] = 0.0f; - measured[1][count][1] = 0.0f; // get S11 data - } - count++; - } - else if (c < 0x20) continue; // Others (skip) - else if (j < line_size) line[j++] = (char)c; // Store - } - } -finish: - if (count != 0) { // Points count not zero, so apply data to traces - pause_sweep(); - current_props._sweep_points = count; - set_sweep_frequency(ST_START, start); - set_sweep_frequency(ST_STOP, stop); - request_to_redraw(REDRAW_PLOT); - } - break; - } -#ifdef __SD_CARD_LOAD__ - case FMT_CMD_FILE: - { - const int buffer_size = 256; - const int line_size = 128; - char *buf_8 = (char *)spi_buffer; // must be greater then buffer_size + line_size - char *line = buf_8 + buffer_size; - uint16_t j = 0, i; - while (f_read(fs_file, buf_8, buffer_size, &size) == FR_OK && size > 0) { - for (i = 0; i < size; i++) { - uint8_t c = buf_8[i]; - if (c == '\r') { // New line (Enter) - line[j] = 0; j = 0; - VNAShell_executeCMDLine(line); - } - else if (c < 0x20) continue; // Others (skip) - else if (j < line_size) line[j++] = (char)c; // Store - } - } - break; - } -#endif - /* - * BMP file load procedure, load only device screenshots - */ - case FMT_BMP_FILE: - { - int y; - leave_show = false; // allow step up/down load bitmap - uint16_t *buf_16 = spi_buffer; // prepare buffer - res = f_read(fs_file, (void *)buf_16, sizeof(bmp_header_v4), &size); // read header - if (res != FR_OK || buf_16[9] != LCD_WIDTH || buf_16[11] != LCD_HEIGHT || buf_16[14] != 16) {error = "Format err"; break;} - for (y = LCD_HEIGHT-1; y >=0 && res == FR_OK; y--) { - res = f_read(fs_file, (void *)buf_16, LCD_WIDTH * sizeof(uint16_t), &size); - swap_bytes(buf_16, LCD_WIDTH); - lcd_bulk(0, y, LCD_WIDTH, 1); - } - lcd_printf(0, LCD_HEIGHT - 3*FONT_STR_HEIGHT, fno.fname); - } - break; -#ifdef __SD_CARD_DUMP_TIFF__ - case FMT_TIF_FILE: - { - int x, y; - leave_show = false; // allow step up/down load bitmap - uint8_t *buf_8 = (uint8_t *)spi_buffer; // prepare buffer - uint16_t *buf_16 = spi_buffer; - res = f_read(fs_file, (void *)buf_16, sizeof(tif_header), &size); // read header - // Quick check for valid (not parse TIFF, use hardcoded values, for less code size) - // Check header id, width, height, compression (pass only self saved images) - if (res != FR_OK || - buf_16[0] != 0x4949 || // Check header ID - buf_16[9] != LCD_WIDTH || // Check Width - buf_16[15] != LCD_HEIGHT || // Check Height - buf_16[27] != 0x8005) {error = "Format err"; break;} - for (y = 0; y < LCD_HEIGHT && res == FR_OK; y++) { - // Unpack RLE compression sequence - for (x = 0; x < LCD_WIDTH * 3;) { - int8_t data[2]; res = f_read(fs_file, data, 2, &size); // Read count and value - int count = data[0]; // count - buf_8[x++] = data[1]; // copy first value - if (count > 0) { // if count > 0 need read additional values - res = f_read(fs_file, &buf_8[x], count, &size); - x+= count; - } else while (count++ < 0) buf_8[x++] = data[1]; // if count < 0 need repeat value -count times - } - // Convert from RGB888 to RGB565 and copy to screen - for (x = 0; x < LCD_WIDTH; x++) - buf_16[x] = RGB565(buf_8[3 * x + 0], buf_8[3 * x + 1], buf_8[3 * x + 2]); - lcd_bulk(0, y, LCD_WIDTH, 1); - } - lcd_printf(0, LCD_HEIGHT - 3 * FONT_STR_HEIGHT, fno.fname); - } - break; -#endif - /* - * Load calibration - */ - case FMT_CAL_FILE: - { - uint32_t magic; - char *src = (char*)¤t_props + sizeof(magic); - uint32_t total = sizeof(current_props) - sizeof(magic); - // Compare file size and try read magic header, if all OK load it - if (fno.fsize == sizeof(current_props) && f_read(fs_file, &magic, sizeof(magic), &size) == FR_OK && - magic == PROPERTIES_MAGIC && f_read(fs_file, src, total, &size) == FR_OK) - load_properties(NO_SAVE_SLOT); - else error = "Format err"; - } - break; - default: break; - } + + if (f_open(fs_file, fno.fname, FA_READ) != FR_OK) return; + // START_PROFILE; + const char *error = load(fs_file, &fno, keypad_mode); f_close(fs_file); + // STOP_PROFILE; + // Check, need continue load next or previous file + bool need_continue = file_opt[keypad_mode].opt & FILE_OPT_CONTINUE; if (error) { lcd_clear_screen(); - drawMessageBox(error, fno.fname, leave_show ? 2000 : 10); + ui_message_box(error, fno.fname, need_continue ? 100 : 2000); } - if (leave_show) return; + if (!need_continue) return; + // Process input while (1) { uint16_t status = btn_check(); @@ -298,7 +151,8 @@ static void browser_open_file(int sel) { else key = 1; touch_wait_release(); } - chThdSleepMilliseconds(100); + //chThdSleepMilliseconds(100); // Device hang after ~2min in this place, not switch thread back + delayMilliseconds(100); int old_sel = sel; if (key == 0) {if (--sel < 0) sel = file_count - 1;} else if (key == 1) {if (++sel > file_count - 1) sel = 0;} @@ -319,8 +173,8 @@ static void browser_draw_page(int page) { DIR dj; // Mount SD card and open directory if (f_mount(fs_volume, "", 1) != FR_OK || - sd_open_dir(&dj, "", file_ext[keypad_mode]) != FR_OK) { - drawMessageBox("ERROR", "NO CARD", 2000); + sd_open_dir(&dj, "", file_opt[keypad_mode].ext) != FR_OK) { + ui_message_box("ERROR", "NO CARD", 2000); ui_mode_normal(); return; } @@ -404,6 +258,19 @@ static int browser_get_max(void) { return max + FILE_BUTTON_FILE - 1; } +void ui_mode_browser(int mode) { + if (ui_mode == UI_BROWSER) + return; + set_area_size(0, 0); + ui_mode = UI_BROWSER; + keypad_mode = mode; + current_page = 1; + file_count = 0; + selection = -1; + browser_mode = 0; + browser_draw_page(current_page); +} + // Process UI input for browser static void ui_browser_touch(int touch_x, int touch_y) { browser_btn_t btn; @@ -441,16 +308,3 @@ static void ui_browser_lever(uint16_t status) { chThdSleepMilliseconds(100); } while ((status = btn_wait_release()) != 0); } - -static UI_FUNCTION_CALLBACK(menu_sdcard_browse_cb) { - if (ui_mode == UI_BROWSER) - return; - set_area_size(0, 0); - ui_mode = UI_BROWSER; - keypad_mode = fixScreenshotFormat(data); - current_page = 1; - file_count = 0; - selection = -1; - browser_mode = 0; - browser_draw_page(current_page); -}