From 91bd82eae65c7e5c8010c80ca4a264e839dbe246 Mon Sep 17 00:00:00 2001 From: hugen79 Date: Thu, 16 Sep 2021 14:49:09 +0800 Subject: [PATCH] Based on the source code of DiSlord version 1.0.69, merge the source code of NanoVNA-H and NanoVNA-H4, which can be specified by TARGET when compiling. NanoVNA-H uses larger fonts, the display layout is adjusted for larger fonts, and you can set whether to use large fonts on NanoVNA-H by _USE_FONT_. NanoVNA-H hardware updated to Rev3.6, SD card added current limiting resistor, thanks to DiSlord's suggestion. Translated with www.DeepL.com/Translator (free version) --- FatFs/diskio.c | 2 +- FatFs/ff.c | 1259 ++++++++------ FatFs/ff.h | 76 +- FatFs/ffconf.h | 62 +- Makefile | 84 +- NANOVNA_STM32_F072/mcuconf.h | 2 +- NanoVNA_DAP.cfg | 2 +- README.md | 6 + STM32F303xC.ld | 96 ++ adc.c | 177 +- adc_F303.h | 26 + chconf.h | 2 +- chprintf.c | 209 ++- doc/Schematic_nanovna_H_REV3_6.pdf | Bin 0 -> 230982 bytes dsp.c | 32 +- dsp.h | 18 +- flash.c | 17 +- hal_adc_lld.c.fixed | 938 +++++++++++ halconf.h | 35 +- ili9341.c | 809 +++++---- lc_matching.c | 185 +- main.c | 1733 +++++++++++-------- mcuconf_F303.h | 233 +++ nanovna.h | 847 +++++++--- plot.c | 2038 ++++++++++++---------- si5351.c | 135 +- si5351.h | 2 + tlv320aic3204.c | 5 +- ui.c | 2518 +++++++++++++++------------- usbcfg.c | 2 +- vna_math.c | 795 +++++++++ vna_math.h | 75 + 32 files changed, 7936 insertions(+), 4484 deletions(-) create mode 100644 STM32F303xC.ld create mode 100644 adc_F303.h create mode 100644 doc/Schematic_nanovna_H_REV3_6.pdf create mode 100644 hal_adc_lld.c.fixed create mode 100644 mcuconf_F303.h create mode 100644 vna_math.c create mode 100644 vna_math.h diff --git a/FatFs/diskio.c b/FatFs/diskio.c index 27f1331..179e387 100644 --- a/FatFs/diskio.c +++ b/FatFs/diskio.c @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------*/ -/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */ +/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ /*-----------------------------------------------------------------------*/ /* If a working storage control module is available, it should be */ /* attached to the FatFs via a glue function rather than modifying it. */ diff --git a/FatFs/ff.c b/FatFs/ff.c index 7e6cfac..580a916 100644 --- a/FatFs/ff.c +++ b/FatFs/ff.c @@ -1,8 +1,8 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem Module R0.14 / +/ FatFs - Generic FAT Filesystem Module R0.14b / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2021, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -29,7 +29,7 @@ ---------------------------------------------------------------------------*/ -#if FF_DEFINED != 86606 /* Revision ID */ +#if FF_DEFINED != 86631 /* Revision ID */ #error Wrong include file (ff.h). #endif @@ -47,6 +47,8 @@ #define IsUpper(c) ((c) >= 'A' && (c) <= 'Z') #define IsLower(c) ((c) >= 'a' && (c) <= 'z') #define IsDigit(c) ((c) >= '0' && (c) <= '9') +#define IsSeparator(c) ((c) == '/' || (c) == '\\') +#define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!')) #define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF) #define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF) #define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF) @@ -61,7 +63,8 @@ /* Additional file attribute bits for internal use */ #define AM_VOL 0x08 /* Volume label */ #define AM_LFN 0x0F /* LFN entry */ -#define AM_MASK 0x3F /* Mask of defined bits */ +#define AM_MASK 0x3F /* Mask of defined bits in FAT */ +#define AM_MASKX 0x37 /* Mask of defined bits in exFAT */ /* Name status flags in fn[11] */ @@ -233,7 +236,7 @@ /* Re-entrancy related */ #if FF_FS_REENTRANT #if FF_USE_LFN == 1 -#error Static LFN work area cannot be used at thread-safe configuration +#error Static LFN work area cannot be used in thread-safe configuration #endif #define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } #else @@ -244,10 +247,10 @@ /* Definitions of logical drive - physical location conversion */ #if FF_MULTI_PARTITION #define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ -#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition number (0:auto search, 1..:forced partition number) */ #else #define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */ -#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#define LD2PT(vol) 0 /* Auto partition search */ #endif @@ -556,7 +559,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */ /* Code conversion tables */ /*--------------------------------*/ -#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ +#if FF_CODE_PAGE == 0 /* Run-time code page configuration */ #define CODEPAGE CodePage static WORD CodePage; /* Current code page */ static const BYTE *ExCvt, *DbcTbl; /* Pointer to current SBCS up-case table and DBCS code range table below */ @@ -610,13 +613,13 @@ static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); // Use define for word/dword/qword read write in little-endian processors // allow save code size and speedup // Also processor should support read/write unaligned word/dword data -static inline uint16_t ld_word (const uint8_t* ptr){return *((uint16_t*)ptr);} -static inline uint32_t ld_dword (const uint8_t* ptr){return *((uint32_t*)ptr);} -static inline uint64_t ld_qword (const uint8_t* ptr){return *((uint64_t*)ptr);} +#define ld_word(ptr) *((uint16_t*)(ptr)) +#define ld_dword(ptr) *((uint32_t*)(ptr)) +#define ld_qword(ptr) *((uint64_t*)(ptr)) -static inline void st_word (uint8_t* ptr, uint16_t val){*((uint16_t*)ptr) = val;} -static inline void st_dword (uint8_t* ptr, uint32_t val){*((uint32_t*)ptr) = val;} -static inline void st_qword (uint8_t* ptr, uint64_t val){*((uint64_t*)ptr) = val;} +#define st_word(ptr, val) *((uint16_t*)(ptr)) = ((uint16_t)(val)) +#define st_dword(ptr, val) *((uint32_t*)(ptr)) = ((uint32_t)(val)) +#define st_qword(ptr, val) *((uint64_t*)(ptr)) = ((uint64_t)(val)) #else @@ -693,7 +696,14 @@ static void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-en /*-----------------------------------------------------------------------*/ /* String functions */ /*-----------------------------------------------------------------------*/ - +#if FF_USE_STRINGLIB == 1 +#include +#else +// Redefine default tu use self string functions +#define memcpy mem_cpy +#define memset mem_set +#define memcmp mem_cmp +#define strchr chk_chr /* Copy memory to memory */ static void mem_cpy (void* dst, const void* src, UINT cnt) { @@ -707,7 +717,6 @@ static void mem_cpy (void* dst, const void* src, UINT cnt) } } - /* Fill memory block */ static void mem_set (void* dst, int val, UINT cnt) { @@ -732,14 +741,13 @@ static int mem_cmp (const void* dst, const void* src, UINT cnt) /* ZR:same, NZ:d return r; } - /* Check if chr is contained in the string */ static int chk_chr (const char* str, int chr) /* NZ:contained, ZR:not contained */ { while (*str && *str != chr) str++; return *str; } - +#endif /* Test if the byte is DBC 1st byte */ static int dbc_1st (BYTE c) @@ -809,18 +817,14 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on uc = (BYTE)*p++; /* Get an encoding unit */ if (uc & 0x80) { /* Multiple byte code? */ - if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ + if ((uc & 0xE0) == 0xC0) { /* 2-byte sequence? */ uc &= 0x1F; nf = 1; - } else { - if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ - uc &= 0x0F; nf = 2; - } else { - if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ - uc &= 0x07; nf = 3; - } else { /* Wrong sequence */ - return 0xFFFFFFFF; - } - } + } else if ((uc & 0xF0) == 0xE0) { /* 3-byte sequence? */ + uc &= 0x0F; nf = 2; + } else if ((uc & 0xF8) == 0xF0) { /* 4-byte sequence? */ + uc &= 0x07; nf = 3; + } else { /* Wrong sequence */ + return 0xFFFFFFFF; } do { /* Get trailing bytes */ b = (BYTE)*p++; @@ -858,8 +862,8 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on } -/* Output a TCHAR string in defined API encoding */ -static BYTE put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ +/* Store a Unicode char in defined API encoding */ +static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ TCHAR* buf, /* Output buffer */ UINT szb /* Size of the buffer */ @@ -1025,7 +1029,7 @@ static UINT inc_lock ( /* Increment object open counter and returns its index (0 && Files[i].ofs == dp->dptr) break; } - if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ + if (i == FF_FS_LOCK) { /* Not opened. Register it as new. */ for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ; if (i == FF_FS_LOCK) return 0; /* No free entry to register (int err) */ Files[i].fs = dp->obj.fs; @@ -1052,13 +1056,13 @@ static FRESULT dec_lock ( /* Decrement object open counter */ if (--i < FF_FS_LOCK) { /* Index number origin from 0 */ n = Files[i].ctr; - if (n == 0x100) n = 0; /* If write mode open, delete the entry */ - if (n > 0) n--; /* Decrement read mode open count */ + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ Files[i].ctr = n; if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ res = FR_OK; } else { - res = FR_INT_ERR; /* Invalid index nunber */ + res = FR_INT_ERR; /* Invalid index nunber */ } return res; } @@ -1147,14 +1151,13 @@ static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */ if (res == FR_OK) { if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { /* FAT32: Update FSInfo sector if needed */ /* Create FSInfo structure */ - mem_set(fs->win, 0, sizeof fs->win); - st_word(fs->win + BS_55AA, 0xAA55); - st_dword(fs->win + FSI_LeadSig, 0x41615252); - st_dword(fs->win + FSI_StrucSig, 0x61417272); - st_dword(fs->win + FSI_Free_Count, fs->free_clst); - st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); - /* Write it into the FSInfo sector */ - fs->winsect = fs->volbase + 1; + memset(fs->win, 0, sizeof fs->win); + st_word(fs->win + BS_55AA, 0xAA55); /* Boot signature */ + st_dword(fs->win + FSI_LeadSig, 0x41615252); /* Leading signature */ + st_dword(fs->win + FSI_StrucSig, 0x61417272); /* Structure signature */ + st_dword(fs->win + FSI_Free_Count, fs->free_clst); /* Number of free clusters */ + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); /* Last allocated culuster */ + fs->winsect = fs->volbase + 1; /* Write it into the FSInfo sector (Next to VBR) */ disk_write(fs->pdrv, fs->win, fs->winsect, 1); fs->fsi_flag = 0; } @@ -1187,7 +1190,7 @@ static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ /*-----------------------------------------------------------------------*/ -/* FAT access - Read value of a FAT entry */ +/* FAT access - Read value of an FAT entry */ /*-----------------------------------------------------------------------*/ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ @@ -1249,7 +1252,8 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF break; } } - /* go to default */ + val = 1; /* Internal error */ + break; #endif default: val = 1; /* Internal error */ @@ -1264,7 +1268,7 @@ static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFF #if !FF_FS_READONLY /*-----------------------------------------------------------------------*/ -/* FAT access - Change value of a FAT entry */ +/* FAT access - Change value of an FAT entry */ /*-----------------------------------------------------------------------*/ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ @@ -1280,12 +1284,12 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ switch (fs->fs_type) { - case FS_FAT12 : + case FS_FAT12: bc = (UINT)clst; bc += bc / 2; /* bc: byte offset of the entry */ res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; p = fs->win + bc++ % SS(fs); - *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; /* Update 1st byte */ fs->wflag = 1; res = move_window(fs, fs->fatbase + (bc / SS(fs))); if (res != FR_OK) break; @@ -1294,16 +1298,16 @@ static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ fs->wflag = 1; break; - case FS_FAT16 : + case FS_FAT16: res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); if (res != FR_OK) break; st_word(fs->win + clst * 2 % SS(fs), (WORD)val); /* Simple WORD array */ fs->wflag = 1; break; - case FS_FAT32 : + case FS_FAT32: #if FF_FS_EXFAT - case FS_EXFAT : + case FS_EXFAT: #endif res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); if (res != FR_OK) break; @@ -1698,12 +1702,12 @@ static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */ if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ sect = clst2sect(fs, clst); /* Top of the cluster */ fs->winsect = sect; /* Set window to top of the cluster */ - mem_set(fs->win, 0, sizeof fs->win); /* Clear window buffer */ + memset(fs->win, 0, sizeof fs->win); /* Clear window buffer */ #if FF_USE_LFN == 3 /* Quick table clear by using multi-secter write */ /* Allocate a temporary buffer */ for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ; if (szb > SS(fs)) { /* Buffer allocated? */ - mem_set(ibuf, 0, szb); + memset(ibuf, 0, szb); szb /= SS(fs); /* Bytes -> Sectors */ for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ; /* Fill the cluster with 0 */ ff_memfree(ibuf); @@ -1835,7 +1839,7 @@ static FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DEN static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ DIR* dp, /* Pointer to the directory object */ - UINT nent /* Number of contiguous entries to allocate */ + UINT n_ent /* Number of contiguous entries to allocate */ ) { FRESULT res; @@ -1850,16 +1854,16 @@ static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ res = move_window(fs, dp->sect); if (res != FR_OK) break; #if FF_FS_EXFAT - if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { /* Is the entry free? */ #else - if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { /* Is the entry free? */ #endif - if (++n == nent) break; /* A block of contiguous free entries is found */ + if (++n == n_ent) break; /* Is a block of contiguous free entries found? */ } else { - n = 0; /* Not a blank entry. Restart to search */ + n = 0; /* Not a free entry, restart to search */ } - res = dir_next(dp, 1); - } while (res == FR_OK); /* Next entry with table stretch enabled */ + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); } if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ @@ -2007,7 +2011,7 @@ static void put_lfn ( do { if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ st_word(dir + LfnOfs[s], wc); /* Put it */ - if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for following items */ } while (++s < 13); if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ dir[LDIR_Ord] = ord; /* Set the LFN order */ @@ -2025,7 +2029,7 @@ static void put_lfn ( static void gen_numname ( BYTE* dst, /* Pointer to the buffer to store numbered SFN */ - const BYTE* src, /* Pointer to SFN */ + const BYTE* src, /* Pointer to SFN in directory form */ const WCHAR* lfn, /* Pointer to LFN */ UINT seq /* Sequence number */ ) @@ -2036,7 +2040,7 @@ static void gen_numname ( DWORD sreg; - mem_cpy(dst, src, 11); + memcpy(dst, src, 11); /* Prepare the SFN to be modified */ if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ sreg = seq; @@ -2051,24 +2055,23 @@ static void gen_numname ( seq = (UINT)sreg; } - /* itoa (hexdecimal) */ + /* Make suffix (~ + hexdecimal) */ i = 7; do { - c = (BYTE)((seq % 16) + '0'); + c = (BYTE)((seq % 16) + '0'); seq /= 16; if (c > '9') c += 7; ns[i--] = c; - seq /= 16; - } while (seq); + } while (i && seq); ns[i] = '~'; - /* Append the number to the SFN body */ - for (j = 0; j < i && dst[j] != ' '; j++) { - if (dbc_1st(dst[j])) { + /* Append the suffix to the SFN body */ + for (j = 0; j < i && dst[j] != ' '; j++) { /* Find the offset to append */ + if (dbc_1st(dst[j])) { /* To avoid DBC break up */ if (j == i - 1) break; j++; } } - do { + do { /* Append the suffix */ dst[j++] = (i < 8) ? ns[i++] : ' '; } while (j < 8); } @@ -2153,47 +2156,6 @@ static DWORD xsum32 ( /* Returns 32-bit checksum */ #endif -#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 -/*------------------------------------------------------*/ -/* exFAT: Get object information from a directory block */ -/*------------------------------------------------------*/ - -static void get_xfileinfo ( - BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ - FILINFO* fno /* Buffer to store the extracted file information */ -) -{ - WCHAR wc, hs; - UINT di, si, nc; - - /* Get file name from the entry block */ - si = SZDIRE * 2; /* 1st C1 entry */ - nc = 0; hs = 0; di = 0; - while (nc < dirb[XDIR_NumName]) { - if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ - if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ - wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */ - if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ - hs = wc; continue; /* Get low surrogate */ - } - wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ - if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */ - di += wc; - hs = 0; - } - if (hs != 0) di = 0; /* Broken surrogate pair? */ - if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ - fno->fname[di] = 0; /* Terminate the name */ - fno->altname[0] = 0; /* exFAT does not support SFN */ - - fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ - fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ - fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ - fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ -} - -#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */ - /*-----------------------------------*/ /* exFAT: Get a directry entry block */ @@ -2205,28 +2167,28 @@ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ { FRESULT res; UINT i, sz_ent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + BYTE *dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ - /* Load file-directory entry */ + /* Load file directory entry */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR; /* Invalid order */ - mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); + memcpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE); sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; - /* Load stream-extension entry */ + /* Load stream extension entry */ res = dir_next(dp, 0); if (res == FR_NO_FILE) res = FR_INT_ERR; /* It cannot be */ if (res != FR_OK) return res; res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */ - mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); + memcpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE); if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; - /* Load file-name entries */ + /* Load file name entries */ i = 2 * SZDIRE; /* Name offset to load */ do { res = dir_next(dp, 0); @@ -2235,7 +2197,7 @@ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) return res; if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR; /* Invalid order */ - if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + if (i < MAXDIRB(FF_MAX_LFN)) memcpy(dirb + i, dp->dir, SZDIRE); } while ((i += SZDIRE) < sz_ent); /* Sanity check (do it for only accessible object) */ @@ -2303,7 +2265,7 @@ static FRESULT store_xdir ( { FRESULT res; UINT nent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + BYTE *dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ /* Create set sum */ st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); @@ -2314,7 +2276,7 @@ static FRESULT store_xdir ( while (res == FR_OK) { res = move_window(dp->obj.fs, dp->sect); if (res != FR_OK) break; - mem_cpy(dp->dir, dirb, SZDIRE); + memcpy(dp->dir, dirb, SZDIRE); dp->obj.fs->wflag = 1; if (--nent == 0) break; dirb += SZDIRE; @@ -2340,7 +2302,7 @@ static void create_xdir ( /* Create file-directory and stream-extension entry */ - mem_set(dirb, 0, 2 * SZDIRE); + memset(dirb, 0, 2 * SZDIRE); dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR; dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM; @@ -2351,7 +2313,7 @@ static void create_xdir ( dirb[i++] = ET_FILENAME; dirb[i++] = 0; do { /* Fill name field */ if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++; /* Get a character if exist */ - st_word(dirb + i, wc); /* Store it */ + st_word(dirb + i, wc); /* Store it */ i += 2; } while (i % SZDIRE != 0); nc1++; @@ -2416,17 +2378,17 @@ static FRESULT dir_read ( if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ ord = 0xFF; } else { - if (attr == AM_LFN) { /* An LFN entry is found */ - if (b & LLEF) { /* Is it start of an LFN sequence? */ + if (attr == AM_LFN) { /* An LFN entry is found */ + if (b & LLEF) { /* Is it start of an LFN sequence? */ sum = dp->dir[LDIR_Chksum]; b &= (BYTE)~LLEF; ord = b; dp->blk_ofs = dp->dptr; } /* Check LFN validity and capture it */ ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; - } else { /* An SFN entry is found */ + } else { /* An SFN entry is found */ if (ord != 0 || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ - dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ } break; } @@ -2474,7 +2436,7 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ while ((res = DIR_READ_FILE(dp)) == FR_OK) { /* Read an item */ #if FF_MAX_LFN < 255 - if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ + if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue; /* Skip comparison if inaccessible object name */ #endif if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ @@ -2512,13 +2474,13 @@ static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ } } else { /* An SFN entry is found */ if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ - if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !memcmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ } } #else /* Non LFN configuration */ dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; - if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ + if (!(dp->dir[DIR_Attr] & AM_VOL) && !memcmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ #endif res = dir_next(dp, 0); /* Next entry */ } while (res == FR_OK); @@ -2541,19 +2503,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too FRESULT res; FATFS *fs = dp->obj.fs; #if FF_USE_LFN /* LFN configuration */ - UINT n, nlen, nent; + UINT n, len, n_ent; BYTE sn[12], sum; if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ - for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + for (len = 0; fs->lfnbuf[len]; len++) ; /* Get lfn length */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ - res = dir_alloc(dp, nent); /* Allocate directory entries */ + n_ent = (len + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, n_ent); /* Allocate directory entries */ if (res != FR_OK) return res; - dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + dp->blk_ofs = dp->dptr - SZDIRE * (n_ent - 1); /* Set the allocated entry block offset */ if (dp->obj.stat & 4) { /* Has the directory been stretched by new allocation? */ dp->obj.stat &= ~4; @@ -2566,10 +2528,10 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too res = load_obj_xdir(&dj, &dp->obj); /* Load the object status */ if (res != FR_OK) return res; - dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); - fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */ + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; /* Update the allocation status */ res = store_xdir(&dj); /* Store the object status */ if (res != FR_OK) return res; } @@ -2580,7 +2542,7 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too } #endif /* On the FAT/FAT32 volume */ - mem_cpy(sn, dp->fn, 12); + memcpy(sn, dp->fn, 12); if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ for (n = 1; n < 100; n++) { @@ -2594,19 +2556,19 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too } /* Create an SFN with/without LFNs. */ - nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ - res = dir_alloc(dp, nent); /* Allocate entries */ - if (res == FR_OK && --nent) { /* Set LFN entry if needed */ - res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + n_ent = (sn[NSFLAG] & NS_LFN) ? (len + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, n_ent); /* Allocate entries */ + if (res == FR_OK && --n_ent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - n_ent * SZDIRE); if (res == FR_OK) { sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ do { /* Store LFN entries in bottom first */ res = move_window(fs, dp->sect); if (res != FR_OK) break; - put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + put_lfn(fs->lfnbuf, dp->dir, (BYTE)n_ent, sum); fs->wflag = 1; res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK && --nent); + } while (res == FR_OK && --n_ent); } } @@ -2619,8 +2581,8 @@ static FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too if (res == FR_OK) { res = move_window(fs, dp->sect); if (res == FR_OK) { - mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ - mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ + memset(dp->dir, 0, SZDIRE); /* Clean the entry */ + memcpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ #if FF_USE_LFN dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ #endif @@ -2656,7 +2618,7 @@ static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ if (res != FR_OK) break; if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ dp->dir[XDIR_Type] &= 0x7F; /* Clear the entry InUse flag. */ - } else { /* On the FAT/FAT32 volume */ + } else { /* On the FAT/FAT32 volume */ dp->dir[DIR_Name] = DDEM; /* Mark the entry 'deleted'. */ } fs->wflag = 1; @@ -2696,6 +2658,7 @@ static void get_fileinfo ( BYTE lcf; WCHAR wc, hs; FATFS *fs = dp->obj.fs; + UINT nw; #else TCHAR c; #endif @@ -2706,22 +2669,47 @@ static void get_fileinfo ( #if FF_USE_LFN /* LFN configuration */ #if FF_FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - get_xfileinfo(fs->dirbuf, fno); + if (fs->fs_type == FS_EXFAT) { /* exFAT volume */ + UINT nc = 0; + + si = SZDIRE * 2; di = 0; /* 1st C1 entry in the entry block */ + hs = 0; + while (nc < fs->dirbuf[XDIR_NumName]) { + if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; } /* Truncated directory block? */ + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + wc = ld_word(fs->dirbuf + si); si += 2; nc++; /* Get a character */ + if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ + hs = wc; continue; /* Get low surrogate */ + } + nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */ + di += nw; + hs = 0; + } + if (hs != 0) di = 0; /* Broken surrogate pair? */ + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate the name */ + fno->altname[0] = 0; /* exFAT does not support SFN */ + + fno->fattrib = fs->dirbuf[XDIR_Attr] & AM_MASKX; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(fs->dirbuf + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(fs->dirbuf + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(fs->dirbuf + XDIR_ModTime + 2); /* Date */ return; } else #endif - { /* On the FAT/FAT32 volume */ + { /* FAT/FAT32 volume */ if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ - si = di = hs = 0; + si = di = 0; + hs = 0; while (fs->lfnbuf[si] != 0) { wc = fs->lfnbuf[si++]; /* Get an LFN character (UTF-16) */ if (hs == 0 && IsSurrogate(wc)) { /* Is it a surrogate? */ hs = wc; continue; /* Get low surrogate */ } - wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in UTF-16 or UTF-8 encoding */ - if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */ - di += wc; + nw = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow or wrong char? */ + di += nw; hs = 0; } if (hs != 0) di = 0; /* Broken surrogate pair? */ @@ -2741,9 +2729,9 @@ static void get_fileinfo ( } wc = ff_oem2uni(wc, CODEPAGE); /* ANSI/OEM -> Unicode */ if (wc == 0) { di = 0; break; } /* Wrong char in the current code page? */ - wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in Unicode */ - if (wc == 0) { di = 0; break; } /* Buffer overflow? */ - di += wc; + nw = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Buffer overflow? */ + di += nw; #else /* ANSI/OEM output */ fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ #endif @@ -2774,10 +2762,10 @@ static void get_fileinfo ( if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ fno->fname[di++] = c; } - fno->fname[di] = 0; + fno->fname[di] = 0; /* Terminate the SFN */ #endif - fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fattrib = dp->dir[DIR_Attr] & AM_MASK; /* Attribute */ fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); /* Time */ fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); /* Date */ @@ -2792,7 +2780,10 @@ static void get_fileinfo ( /* Pattern matching */ /*-----------------------------------------------------------------------*/ -static DWORD get_achar ( /* Get a character and advances ptr */ +#define FIND_RECURS 4 /* Maximum number of wildcard terms in the pattern to limit recursion */ + + +static DWORD get_achar ( /* Get a character and advance ptr */ const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ ) { @@ -2823,41 +2814,43 @@ static DWORD get_achar ( /* Get a character and advances ptr */ } -static int pattern_matching ( /* 0:not matched, 1:matched */ +static int pattern_match ( /* 0:mismatched, 1:matched */ const TCHAR* pat, /* Matching pattern */ const TCHAR* nam, /* String to be tested */ - int skip, /* Number of pre-skip chars (number of ?s) */ - int inf /* Infinite search (* specified) */ + UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */ + UINT recur /* Recursion count */ ) { - const TCHAR *pp, *np; - DWORD pc, nc; - int nm, nx; + const TCHAR *pptr, *nptr; + DWORD pchr, nchr; + UINT sk; - while (skip--) { /* Pre-skip name chars */ + while ((skip & 0xFF) != 0) { /* Pre-skip name chars */ if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + skip--; } - if (*pat == 0 && inf) return 1; /* (short circuit) */ + if (*pat == 0 && skip) return 1; /* Matched? (short circuit) */ do { - pp = pat; np = nam; /* Top of pattern and name to match */ + pptr = pat; nptr = nam; /* Top of pattern and name to match */ for (;;) { - if (*pp == '?' || *pp == '*') { /* Wildcard? */ - nm = nx = 0; - do { /* Analyze the wildcard block */ - if (*pp++ == '?') nm++; else nx = 1; - } while (*pp == '?' || *pp == '*'); - if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ - nc = *np; break; /* Branch mismatched */ - } - pc = get_achar(&pp); /* Get a pattern char */ - nc = get_achar(&np); /* Get a name char */ - if (pc != nc) break; /* Branch mismatched? */ - if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + if (*pptr == '?' || *pptr == '*') { /* Wildcard term? */ + if (recur == 0) return 0; /* Too many wildcard terms? */ + sk = 0; + do { /* Analyze the wildcard term */ + if (*pptr++ == '?') sk++; else sk |= 0x100; + } while (*pptr == '?' || *pptr == '*'); + if (pattern_match(pptr, nptr, sk, recur - 1)) return 1; /* Test new branch (recursive call) */ + nchr = *nptr; break; /* Branch mismatched */ + } + pchr = get_achar(&pptr); /* Get a pattern char */ + nchr = get_achar(&nptr); /* Get a name char */ + if (pchr != nchr) break; /* Branch mismatched? */ + if (pchr == 0) return 1; /* Branch matched? (matched at end of both strings) */ } get_achar(&nam); /* nam++ */ - } while (inf && nc); /* Retry until end of name if infinite search is specified */ + } while (skip && nchr); /* Retry until end of name if infinite search is specified */ return 0; } @@ -2890,16 +2883,17 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (uc == 0xFFFFFFFF) return FR_INVALID_NAME; /* Invalid code or UTF decode error */ if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16); /* Store high surrogate if needed */ wc = (WCHAR)uc; - if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */ - if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + if (wc < ' ' || IsSeparator(wc)) break; /* Break if end of the path or a separator is found */ + if (wc < 0x80 && strchr("*:<>|\"\?\x7F", (int)wc)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ if (di >= FF_MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ - lfn[di++] = wc; /* Store the Unicode character */ + lfn[di++] = wc; /* Store the Unicode character */ } - if (wc < ' ') { /* End of path? */ - cf = NS_LAST; /* Set last segment flag */ - } else { - cf = 0; /* Next segment follows */ - while (*p == '/' || *p == '\\') p++; /* Skip duplicated separators if exist */ + if (wc < ' ') { /* Stopped at end of the path? */ + cf = NS_LAST; /* Last segment */ + } else { /* Stopped at a separator */ + while (IsSeparator(*p)) p++; /* Skip duplicated separators if exist */ + cf = 0; /* Next segment may follow */ + if (IsTerminator(*p)) cf = NS_LAST; /* Ignore terminating separator */ } *path = p; /* Return pointer to the next segment */ @@ -2907,14 +2901,14 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if ((di == 1 && lfn[di - 1] == '.') || (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ lfn[di] = 0; - for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ + for (i = 0; i < 11; i++) { /* Create dot name for SFN entry */ dp->fn[i] = (i < di) ? '.' : ' '; } - dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ return FR_OK; } #endif - while (di) { /* Snip off trailing spaces and dots if exist */ + while (di) { /* Snip off trailing spaces and dots if exist */ wc = lfn[di - 1]; if (wc != ' ' && wc != '.') break; di--; @@ -2927,7 +2921,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN; /* Is there any leading space or dot? */ while (di > 0 && lfn[di - 1] != '.') di--; /* Find last dot (di<=si: no extension) */ - mem_set(dp->fn, ' ', 11); + memset(dp->fn, ' ', 11); i = b = 0; ni = 8; for (;;) { wc = lfn[si++]; /* Get an LFN character */ @@ -2948,20 +2942,20 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr continue; } - if (wc >= 0x80) { /* Is this a non-ASCII character? */ + if (wc >= 0x80) { /* Is this an extended character? */ cf |= NS_LFN; /* LFN entry needs to be created */ #if FF_CODE_PAGE == 0 - if (ExCvt) { /* At SBCS */ + if (ExCvt) { /* In SBCS cfg */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ - } else { /* At DBCS */ - wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ + } else { /* In DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */ } -#elif FF_CODE_PAGE < 900 /* SBCS cfg */ +#elif FF_CODE_PAGE < 900 /* In SBCS cfg */ wc = ff_uni2oem(wc, CODEPAGE); /* Unicode ==> ANSI/OEM code */ if (wc & 0x80) wc = ExCvt[wc & 0x7F]; /* Convert extended character to upper (SBCS) */ -#else /* DBCS cfg */ - wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Upper convert ==> ANSI/OEM code */ +#else /* In DBCS cfg */ + wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE); /* Unicode ==> Up-convert ==> ANSI/OEM code */ #endif } @@ -2972,7 +2966,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr } dp->fn[i++] = (BYTE)(wc >> 8); /* Put 1st byte */ } else { /* SBC */ - if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */ + if (wc == 0 || strchr("+,;=[]", (int)wc)) { /* Replace illegal characters for SFN */ wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ } else { if (IsUpper(wc)) { /* ASCII upper case? */ @@ -3007,7 +3001,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr /* Create file name in directory form */ p = *path; sfn = dp->fn; - mem_set(sfn, ' ', 11); + memset(sfn, ' ', 11); si = i = 0; ni = 8; #if FF_FS_RPATH != 0 if (p[si] == '.') { /* Is this a dot entry? */ @@ -3016,8 +3010,8 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr if (c != '.' || si >= 3) break; sfn[i++] = c; } - if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; - *path = p + si; /* Return pointer to the next segment */ + if (!IsSeparator(c) && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ return FR_OK; } @@ -3025,8 +3019,8 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr for (;;) { c = (BYTE)p[si++]; /* Get a byte */ if (c <= ' ') break; /* Break if end of the path name */ - if (c == '/' || c == '\\') { /* Break if a separator is found */ - while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + if (IsSeparator(c)) { /* Break if a separator is found */ + while (IsSeparator(p[si])) si++; /* Skip duplicated separator if exist */ break; } if (c == '.' || i >= ni) { /* End of body or field overflow? */ @@ -3049,16 +3043,16 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr sfn[i++] = c; sfn[i++] = d; } else { /* SBC */ - if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (strchr("*+,:;<=>[]|\"\?\x7F", (int)c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ if (IsLower(c)) c -= 0x20; /* To upper */ sfn[i++] = c; } } - *path = p + si; /* Return pointer to the next segment */ + *path = &p[si]; /* Return pointer to the next segment */ if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ - sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + sfn[NSFLAG] = (c <= ' ' || p[si] <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ return FR_OK; #endif /* FF_USE_LFN */ @@ -3082,13 +3076,13 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ #if FF_FS_RPATH != 0 - if (*path != '/' && *path != '\\') { /* Without heading separator */ - dp->obj.sclust = fs->cdir; /* Start from current directory */ + if (!IsSeparator(*path) && (FF_STR_VOLUME_ID != 2 || !IsTerminator(*path))) { /* Without heading separator */ + dp->obj.sclust = fs->cdir; /* Start at the current directory */ } else #endif { /* With heading separator */ - while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ - dp->obj.sclust = 0; /* Start from root directory */ + while (IsSeparator(*path)) path++; /* Strip separators */ + dp->obj.sclust = 0; /* Start from the root directory */ } #if FF_FS_EXFAT dp->obj.n_frag = 0; /* Invalidate last fragment counter of the object */ @@ -3129,13 +3123,13 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ } break; } - if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ /* Get into the sub-directory */ - if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + if (!(dp->obj.attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ res = FR_NO_PATH; break; } #if FF_FS_EXFAT - if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ dp->obj.c_scl = dp->obj.sclust; dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat; dp->obj.c_ofs = dp->blk_ofs; @@ -3164,7 +3158,8 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb { const TCHAR *tp, *tt; TCHAR tc; - int i, vol = -1; + int i; + int vol = -1; #if FF_STR_VOLUME_ID /* Find string volume ID */ const char *sp; char c; @@ -3172,7 +3167,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb tt = tp = *path; if (!tp) return vol; /* Invalid path name? */ - do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); /* Find a colon in the path */ + do tc = *tt++; while (!IsTerminator(tc) && tc != ':'); /* Find a colon in the path */ if (tc == ':') { /* DOS/Windows style volume ID? */ i = FF_VOLUMES; @@ -3199,21 +3194,22 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb return vol; } #if FF_STR_VOLUME_ID == 2 /* Unix style volume ID is enabled */ - if (*tp == '/') { + if (*tp == '/') { /* Is there a volume ID? */ + while (*(tp + 1) == '/') tp++; /* Skip duplicated separator */ i = 0; do { - sp = VolumeStr[i]; tp = *path; /* This string volume ID and path name */ + tt = tp; sp = VolumeStr[i]; /* Path name and this string volume ID */ do { /* Compare the volume ID with path name */ - c = *sp++; tc = *(++tp); + c = *sp++; tc = *(++tt); if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; } while (c && (TCHAR)c == tc); - } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ + } while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ vol = i; /* Drive number */ - *path = tp; /* Snip the drive prefix off */ - return vol; + *path = tt; /* Snip the drive prefix off */ } + return vol; } #endif /* No drive prefix is found */ @@ -3262,7 +3258,7 @@ static int test_gpt_header ( /* 0:Invalid, 1:Valid */ DWORD bcc; - if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */ + if (memcmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0; /* Check sign, version (1.0) and length (92) */ for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) { /* Check header BCC */ bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]); } @@ -3304,23 +3300,40 @@ static DWORD make_rand ( /* Check what the sector is */ -static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */ +static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */ FATFS* fs, /* Filesystem object */ LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */ ) { - fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */ - if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */ - - if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot signature (always here regardless of the sector size) */ + WORD w, sign; + BYTE b; - if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* Check if exFAT VBR */ - if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) { /* Valid JumpBoot code? */ - if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0; /* Is it an FAT VBR? */ - if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0; /* Is it an FAT32 VBR? */ - } - return 2; /* Valid BS but not FAT */ + fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load the boot sector */ + sign = ld_word(fs->win + BS_55AA); +#if FF_FS_EXFAT + if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */ +#endif + b = fs->win[BS_JmpBoot]; + if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */ + if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) { + return 0; /* It is an FAT32 VBR */ + } + /* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */ + w = ld_word(fs->win + BPB_BytsPerSec); + b = fs->win[BPB_SecPerClus]; + if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */ + && b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */ + && ld_word(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */ + && (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */ + && ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */ + && (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */ + && ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */ + return 0; /* It can be presumed an FAT VBR */ + } + } + return sign == 0xAA55 ? 2 : 3; /* Not an FAT VBR (valid or invalid BS) */ } @@ -3336,8 +3349,8 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ DWORD mbr_pt[4]; - fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD */ - if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */ + fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format */ + if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */ /* Sector 0 is not an FAT VBR or forced partition number wants a partition */ @@ -3353,7 +3366,7 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */ if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector */ ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector */ - if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */ + if (!memcmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */ v_ent++; fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status */ if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */ @@ -3421,7 +3434,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ } /* The filesystem object is not valid. */ - /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */ + /* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */ fs->fs_type = 0; /* Clear the filesystem object */ fs->pdrv = LD2PD(vol); /* Volume hosting physical drive */ @@ -3441,7 +3454,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ fmt = find_volume(fs, LD2PT(vol)); if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ - bsect = fs->winsect; /* Volume location */ + bsect = fs->winsect; /* Volume offset */ /* An FAT volume is found (bsect). Following code initializes the filesystem object */ @@ -3459,8 +3472,8 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ return FR_NO_FILESYSTEM; } - maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ - if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */ + if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed in 32-bit LBA) */ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ @@ -3468,7 +3481,7 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ - if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768 sectors) */ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ @@ -3489,11 +3502,11 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR; so++; } - if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ + if (fs->win[i] == ET_BITMAP) break; /* Is it a bitmap entry? */ i = (i + SZDIRE) % SS(fs); /* Next entry */ } - bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ - if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; + bcl = ld_dword(fs->win + i + 20); /* Bitmap cluster */ + if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM; /* (Wrong cluster#) */ fs->bitbase = fs->database + fs->csize * (bcl - 2); /* Bitmap sector */ for (;;) { /* Check if bitmap is contiguous */ if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR; @@ -3655,9 +3668,9 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ /*-----------------------------------------------------------------------*/ FRESULT f_mount ( - FATFS* fs, /* Pointer to the filesystem object (NULL:unmount)*/ + FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/ const TCHAR* path, /* Logical drive number to be mounted/unmounted */ - BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ + BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */ ) { FATFS *cfs; @@ -3705,14 +3718,14 @@ FRESULT f_mount ( FRESULT f_open ( FIL* fp, /* Pointer to the blank file object */ const TCHAR* path, /* Pointer to the file name */ - BYTE mode /* Access mode and file open mode flags */ + BYTE mode /* Access mode and open mode flags */ ) { FRESULT res; DIR dj; FATFS *fs; #if !FF_FS_READONLY - DWORD cl, bcs, clst; + DWORD cl, bcs, clst, tm; LBA_t sc; FSIZE_t ofs; #endif @@ -3765,8 +3778,8 @@ FRESULT f_open ( fp->obj.fs = fs; init_alloc_info(fs, &fp->obj); /* Set directory entry block initial state */ - mem_set(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ - mem_set(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ + memset(fs->dirbuf + 2, 0, 30); /* Clear 85 entry except for NumSec */ + memset(fs->dirbuf + 38, 0, 26); /* Clear C0 entry except for NumName and NameHash */ fs->dirbuf[XDIR_Attr] = AM_ARC; st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME()); fs->dirbuf[XDIR_GenFlags] = 1; @@ -3779,8 +3792,10 @@ FRESULT f_open ( #endif { /* Set directory entry initial state */ + tm = GET_FATTIME(); /* Set created time */ + st_dword(dj.dir + DIR_CrtTime, tm); + st_dword(dj.dir + DIR_ModTime, tm); cl = ld_clust(fs, dj.dir); /* Get current cluster chain */ - st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); /* Set created time */ dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ st_clust(fs, dj.dir, 0); /* Reset file allocation info */ st_dword(dj.dir + DIR_FileSize, 0); @@ -3842,17 +3857,17 @@ FRESULT f_open ( fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); } #if FF_USE_FASTSEEK - fp->cltbl = 0; /* Disable fast seek mode */ + fp->cltbl = 0; /* Disable fast seek mode */ #endif - fp->obj.fs = fs; /* Validate the file object */ + fp->obj.fs = fs; /* Validate the file object */ fp->obj.id = fs->id; - fp->flag = mode; /* Set file access mode */ - fp->err = 0; /* Clear error flag */ - fp->sect = 0; /* Invalidate current data sector */ - fp->fptr = 0; /* Set file pointer top of the file */ + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ #if !FF_FS_READONLY #if !FF_FS_TINY - mem_set(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ + memset(fp->buf, 0, sizeof fp->buf); /* Clear sector buffer */ #endif if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ fp->fptr = fp->obj.objsize; /* Offset to seek */ @@ -3875,6 +3890,9 @@ FRESULT f_open ( #endif } } +#if FF_FS_LOCK != 0 + if (res != FR_OK) dec_lock(fp->obj.lockid); /* Decrement file open counter if seek failed */ +#endif } #endif } @@ -3895,10 +3913,10 @@ FRESULT f_open ( /*-----------------------------------------------------------------------*/ FRESULT f_read ( - FIL* fp, /* Pointer to the file object */ - void* buff, /* Pointer to data buffer */ + FIL* fp, /* Open file to be read */ + void* buff, /* Data buffer to store the read data */ UINT btr, /* Number of bytes to read */ - UINT* br /* Pointer to number of bytes read */ + UINT* br /* Number of bytes read */ ) { FRESULT res; @@ -3917,8 +3935,7 @@ FRESULT f_read ( remain = fp->obj.objsize - fp->fptr; if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ - for ( ; btr; /* Repeat until btr bytes read */ - btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { + for ( ; btr > 0; btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) { /* Repeat until btr bytes read */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ @@ -3950,11 +3967,11 @@ FRESULT f_read ( #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if FF_FS_TINY if (fs->wflag && fs->winsect - sect < cc) { - mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + memcpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); } #else if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { - mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + memcpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); } #endif #endif @@ -3978,9 +3995,9 @@ FRESULT f_read ( if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ #if FF_FS_TINY if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ - mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ + memcpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #else - mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ + memcpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ #endif } @@ -3996,10 +4013,10 @@ FRESULT f_read ( /*-----------------------------------------------------------------------*/ FRESULT f_write ( - FIL* fp, /* Pointer to the file object */ - const void* buff, /* Pointer to the data to be written */ + FIL* fp, /* Open file to be written */ + const void* buff, /* Data to be written */ UINT btw, /* Number of bytes to write */ - UINT* bw /* Pointer to number of bytes written */ + UINT* bw /* Number of bytes written */ ) { FRESULT res; @@ -4020,8 +4037,7 @@ FRESULT f_write ( btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); } - for ( ; btw; /* Repeat until all data written */ - btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { + for ( ; btw > 0; btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) { /* Repeat until all data written */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ if (csect == 0) { /* On the cluster boundary? */ @@ -4066,12 +4082,12 @@ FRESULT f_write ( #if FF_FS_MINIMIZE <= 2 #if FF_FS_TINY if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + memcpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); fs->wflag = 0; } #else if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + memcpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); fp->flag &= (BYTE)~FA_DIRTY; } #endif @@ -4097,10 +4113,10 @@ FRESULT f_write ( if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ #if FF_FS_TINY if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ - mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + memcpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fs->wflag = 1; #else - mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + memcpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ fp->flag |= FA_DIRTY; #endif } @@ -4118,7 +4134,7 @@ FRESULT f_write ( /*-----------------------------------------------------------------------*/ FRESULT f_sync ( - FIL* fp /* Pointer to the file object */ + FIL* fp /* Open file to be synced */ ) { FRESULT res; @@ -4199,7 +4215,7 @@ FRESULT f_sync ( /*-----------------------------------------------------------------------*/ FRESULT f_close ( - FIL* fp /* Pointer to the file object to be closed */ + FIL* fp /* Open file to be closed */ ) { FRESULT res; @@ -4300,7 +4316,7 @@ FRESULT f_chdir ( } FREE_NAMBUF(); if (res == FR_NO_FILE) res = FR_NO_PATH; -#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed at Unix style volume ID */ +#if FF_STR_VOLUME_ID == 2 /* Also current drive is changed if in Unix style volume ID */ if (res == FR_OK) { for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ; /* Set current drive */ CurrVol = (BYTE)i; @@ -4424,7 +4440,8 @@ FRESULT f_lseek ( LBA_t nsect; FSIZE_t ifptr; #if FF_USE_FASTSEEK - DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl; + DWORD cl, pcl, ncl, tcl, tlen, ulen; + DWORD *tbl; LBA_t dsc; #endif @@ -4719,9 +4736,9 @@ FRESULT f_findnext ( for (;;) { res = f_readdir(dp, fno); /* Get a directory item */ if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ - if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ + if (pattern_match(dp->pat, fno->fname, 0, FIND_RECURS)) break; /* Test for the file name */ #if FF_USE_LFN && FF_USE_FIND == 2 - if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ + if (pattern_match(dp->pat, fno->altname, 0, FIND_RECURS)) break; /* Test for alternative name if exist */ #endif } return res; @@ -4869,9 +4886,11 @@ FRESULT f_getfree ( } while (--clst); } } - *nclst = nfree; /* Return the free clusters */ - fs->free_clst = nfree; /* Now free_clst is valid */ - fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + if (res == FR_OK) { /* Update parameters if succeeded */ + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FAT32: FSInfo is to be updated */ + } } } @@ -4982,12 +5001,12 @@ FRESULT f_unlink ( } if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ #if FF_FS_RPATH != 0 - if (dclst == fs->cdir) { /* Is it the current directory? */ + if (dclst == fs->cdir) { /* Is it the current directory? */ res = FR_DENIED; } else #endif { - sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.fs = fs; /* Open the sub-directory */ sdj.obj.sclust = dclst; #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { @@ -5062,12 +5081,12 @@ FRESULT f_mkdir ( res = dir_clear(fs, dcl); /* Clean up the new table */ if (res == FR_OK) { if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* Create dot entries (FAT only) */ - mem_set(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ + memset(fs->win + DIR_Name, ' ', 11); /* Create "." entry */ fs->win[DIR_Name] = '.'; fs->win[DIR_Attr] = AM_DIR; st_dword(fs->win + DIR_ModTime, tm); st_clust(fs, fs->win, dcl); - mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ + memcpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */ fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; st_clust(fs, fs->win + SZDIRE, pcl); fs->wflag = 1; @@ -5131,21 +5150,21 @@ FRESULT f_rename ( if (res == FR_OK) { djo.obj.fs = fs; INIT_NAMBUF(fs); - res = follow_path(&djo, path_old); /* Check old object */ + res = follow_path(&djo, path_old); /* Check old object */ if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ #if FF_FS_LOCK != 0 if (res == FR_OK) { res = chk_lock(&djo, 2); } #endif - if (res == FR_OK) { /* Object to be renamed is found */ + if (res == FR_OK) { /* Object to be renamed is found */ #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* At exFAT volume */ BYTE nf, nn; WORD nh; - mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ - mem_cpy(&djn, &djo, sizeof djo); + memcpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + memcpy(&djn, &djo, sizeof djo); res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ if (res == FR_OK) { /* Is new name already in use by any other object? */ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; @@ -5155,7 +5174,7 @@ FRESULT f_rename ( if (res == FR_OK) { nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; nh = ld_word(fs->dirbuf + XDIR_NameHash); - mem_cpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ + memcpy(fs->dirbuf, buf, SZDIRE * 2); /* Restore 85+C0 entry */ fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; st_word(fs->dirbuf + XDIR_NameHash, nh); if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ @@ -5166,8 +5185,8 @@ FRESULT f_rename ( } else #endif { /* At FAT/FAT32 volume */ - mem_cpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ - mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + memcpy(buf, djo.dir, SZDIRE); /* Save directory entry of the object */ + memcpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ if (res == FR_OK) { /* Is new name already in use by any other object? */ res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; @@ -5176,7 +5195,7 @@ FRESULT f_rename ( res = dir_register(&djn); /* Register the new entry */ if (res == FR_OK) { dir = djn.dir; /* Copy directory entry of the object except name */ - mem_cpy(dir + 13, buf + 13, SZDIRE - 13); + memcpy(dir + 13, buf + 13, SZDIRE - 13); dir[DIR_Attr] = buf[DIR_Attr]; if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */ fs->wflag = 1; @@ -5342,15 +5361,16 @@ FRESULT f_getlabel ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { WCHAR hs; + UINT nw; for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ wc = ld_word(dj.dir + XDIR_Label + si * 2); if (hs == 0 && IsSurrogate(wc)) { /* Is the code a surrogate? */ hs = wc; continue; } - wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4); - if (wc == 0) { di = 0; break; } - di += wc; + nw = put_utf((DWORD)hs << 16 | wc, &label[di], 4); /* Store it in API encoding */ + if (nw == 0) { di = 0; break; } /* Encode error? */ + di += nw; hs = 0; } if (hs != 0) di = 0; /* Broken surrogate pair? */ @@ -5363,10 +5383,9 @@ FRESULT f_getlabel ( wc = dj.dir[si++]; #if FF_USE_LFN && FF_LFN_UNICODE >= 1 /* Unicode output */ if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++]; /* Is it a DBC? */ - wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ - if (wc != 0) wc = put_utf(wc, &label[di], 4); /* Put it in Unicode */ - if (wc == 0) { di = 0; break; } - di += wc; + wc = ff_oem2uni(wc, CODEPAGE); /* Convert it into Unicode */ + if (wc == 0) { di = 0; break; } /* Invalid char in current code page? */ + di += put_utf(wc, &label[di], 4); /* Store it in Unicode */ #else /* ANSI/OEM output */ label[di++] = (TCHAR)wc; #endif @@ -5390,10 +5409,12 @@ FRESULT f_getlabel ( if (res == FR_OK) { switch (fs->fs_type) { case FS_EXFAT: - di = BPB_VolIDEx; break; + di = BPB_VolIDEx; + break; case FS_FAT32: - di = BS_VolID32; break; + di = BS_VolID32; + break; default: di = BS_VolID; @@ -5422,7 +5443,7 @@ FRESULT f_setlabel ( BYTE dirvn[22]; UINT di; WCHAR wc; - static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */ + static const char badchr[18] = "+.,;=[]" "/*:<>|\\\"\?\x7F"; /* [0..16] for FAT, [7..16] for exFAT */ #if FF_USE_LFN DWORD dc; #endif @@ -5433,7 +5454,7 @@ FRESULT f_setlabel ( #if FF_FS_EXFAT if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ - mem_set(dirvn, 0, 22); + memset(dirvn, 0, 22); di = 0; while ((UINT)*label >= ' ') { /* Create volume label */ dc = tchar2uni(&label); /* Get a Unicode character */ @@ -5444,7 +5465,7 @@ FRESULT f_setlabel ( st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++; } } - if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) { /* Check validity of the volume label */ + if (dc == 0 || strchr(&badchr[7], (int)dc) || di >= 11) { /* Check validity of the volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } st_word(dirvn + di * 2, (WCHAR)dc); di++; @@ -5452,7 +5473,7 @@ FRESULT f_setlabel ( } else #endif { /* On the FAT/FAT32 volume */ - mem_set(dirvn, ' ', 11); + memset(dirvn, ' ', 11); di = 0; while ((UINT)*label >= ' ') { /* Create volume label */ #if FF_USE_LFN @@ -5468,7 +5489,7 @@ FRESULT f_setlabel ( if (wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */ #endif #endif - if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + if (wc == 0 || strchr(&badchr[0], (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ LEAVE_FF(fs, FR_INVALID_NAME); } if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8); @@ -5486,10 +5507,10 @@ FRESULT f_setlabel ( if (res == FR_OK) { if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_NumLabel] = (BYTE)di; /* Change the volume label */ - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { if (di != 0) { - mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + memcpy(dj.dir, dirvn, 11); /* Change the volume label */ } else { dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ } @@ -5502,14 +5523,14 @@ FRESULT f_setlabel ( if (di != 0) { /* Create a volume label entry */ res = dir_alloc(&dj, 1); /* Allocate an entry */ if (res == FR_OK) { - mem_set(dj.dir, 0, SZDIRE); /* Clean the entry */ + memset(dj.dir, 0, SZDIRE); /* Clean the entry */ if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) { dj.dir[XDIR_Type] = ET_VLABEL; /* Create volume label entry */ dj.dir[XDIR_NumLabel] = (BYTE)di; - mem_cpy(dj.dir + XDIR_Label, dirvn, 22); + memcpy(dj.dir + XDIR_Label, dirvn, 22); } else { dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ - mem_cpy(dj.dir, dirvn, 11); + memcpy(dj.dir, dirvn, 11); } fs->wflag = 1; res = sync_fs(fs); @@ -5646,8 +5667,7 @@ FRESULT f_forward ( remain = fp->obj.objsize - fp->fptr; if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ - for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ - fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + for ( ; btf > 0 && (*func)(0, 0); fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { /* Repeat until all data transferred or stream goes busy */ csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ if (csect == 0) { /* On the cluster boundary? */ @@ -5691,7 +5711,7 @@ FRESULT f_forward ( #if !FF_FS_READONLY && FF_USE_MKFS /*-----------------------------------------------------------------------*/ -/* Create an FAT/exFAT volume */ +/* Create FAT/exFAT volume (with sub-functions) */ /*-----------------------------------------------------------------------*/ #define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */ @@ -5699,29 +5719,30 @@ FRESULT f_forward ( #define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */ -/* Create partitions on the physical drive */ +/* Create partitions on the physical drive in format of MBR or GPT */ static FRESULT create_partition ( BYTE drv, /* Physical drive number */ const LBA_t plst[], /* Partition list */ - UINT sys, /* System ID (for only MBR, temp setting) and bit8:GPT */ + BYTE sys, /* System ID (for only MBR, temp setting) */ BYTE* buf /* Working buffer for a sector */ ) { UINT i, cy; LBA_t sz_drv; - DWORD sz_drv32, s_lba32, n_lba32; - BYTE *pte, hd, n_hd, sc, n_sc; + DWORD sz_drv32, nxt_alloc32, sz_part32; + BYTE *pte; + BYTE hd, n_hd, sc, n_sc; - /* Get drive size */ + /* Get physical drive size */ if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR; #if FF_LBA64 - if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT */ + if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT format */ WORD ss; - UINT sz_pt, pi, si, ofs; + UINT sz_ptbl, pi, si, ofs; DWORD bcc, rnd, align; - QWORD s_lba64, n_lba64, sz_pool, s_bpt; + QWORD nxt_alloc, sz_part, sz_pool, top_bpt; static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; #if FF_MAX_SS != FF_MIN_SS @@ -5730,103 +5751,103 @@ static FRESULT create_partition ( #else ss = FF_MAX_SS; #endif - rnd = GET_FATTIME(); /* Random seed */ - align = GPT_ALIGN / ss; /* Partition alignment [sector] */ - sz_pt = GPT_ITEMS * SZ_GPTE / ss; /* Size of PT [sector] */ - s_bpt = sz_drv - sz_pt - 1; /* Backup PT start sector */ - s_lba64 = 2 + sz_pt; /* First allocatable sector */ - sz_pool = s_bpt - s_lba64; /* Size of allocatable area */ - bcc = 0xFFFFFFFF; n_lba64 = 1; + rnd = (DWORD)sz_drv + GET_FATTIME(); /* Random seed */ + align = GPT_ALIGN / ss; /* Partition alignment for GPT [sector] */ + sz_ptbl = GPT_ITEMS * SZ_GPTE / ss; /* Size of partition table [sector] */ + top_bpt = sz_drv - sz_ptbl - 1; /* Backup partiiton table start sector */ + nxt_alloc = 2 + sz_ptbl; /* First allocatable sector */ + sz_pool = top_bpt - nxt_alloc; /* Size of allocatable area */ + bcc = 0xFFFFFFFF; sz_part = 1; pi = si = 0; /* partition table index, size table index */ do { - if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss); /* Clean the buffer if needed */ - if (n_lba64 != 0) { /* Is the size table not termintated? */ - s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition start */ - n_lba64 = plst[si++]; /* Get a partition size */ - if (n_lba64 <= 100) { /* Is the size in percentage? */ - n_lba64 = sz_pool * n_lba64 / 100; - n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */ + if (pi * SZ_GPTE % ss == 0) memset(buf, 0, ss); /* Clean the buffer if needed */ + if (sz_part != 0) { /* Is the size table not termintated? */ + nxt_alloc = (nxt_alloc + align - 1) & ((QWORD)0 - align); /* Align partition start */ + sz_part = plst[si++]; /* Get a partition size */ + if (sz_part <= 100) { /* Is the size in percentage? */ + sz_part = sz_pool * sz_part / 100; + sz_part = (sz_part + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */ } - if (s_lba64 + n_lba64 > s_bpt) { /* Clip at end of the pool */ - n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0; + if (nxt_alloc + sz_part > top_bpt) { /* Clip the size at end of the pool */ + sz_part = (nxt_alloc < top_bpt) ? top_bpt - nxt_alloc : 0; } } - if (n_lba64 != 0) { /* Add a partition? */ + if (sz_part != 0) { /* Add a partition? */ ofs = pi * SZ_GPTE % ss; - mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Partition GUID (Microsoft Basic Data) */ - rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Unique partition GUID */ - st_qword(buf + ofs + GPTE_FstLba, s_lba64); /* Partition start LBA */ - st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 - 1); /* Partition end LBA */ - s_lba64 += n_lba64; /* Next partition LBA */ + memcpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Set partition GUID (Microsoft Basic Data) */ + rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Set unique partition GUID */ + st_qword(buf + ofs + GPTE_FstLba, nxt_alloc); /* Set partition start sector */ + st_qword(buf + ofs + GPTE_LstLba, nxt_alloc + sz_part - 1); /* Set partition end sector */ + nxt_alloc += sz_part; /* Next allocatable sector */ } if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the buffer if it is filled up */ for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */ - if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Primary table */ - if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Secondary table */ + if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to primary table */ + if (disk_write(drv, buf, top_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to secondary table */ } } while (++pi < GPT_ITEMS); /* Create primary GPT header */ - mem_set(buf, 0, ss); - mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */ - st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */ - st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */ - st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of another header */ - st_qword(buf + GPTH_FstLba, 2 + sz_pt); /* LBA of first allocatable sector */ - st_qword(buf + GPTH_LstLba, s_bpt - 1); /* LBA of last allocatable sector */ - st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */ - st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */ - st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */ + memset(buf, 0, ss); + memcpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */ + st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */ + st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of secondary header */ + st_qword(buf + GPTH_FstLba, 2 + sz_ptbl); /* LBA of first allocatable sector */ + st_qword(buf + GPTH_LstLba, top_bpt - 1); /* LBA of last allocatable sector */ + st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */ + st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */ + st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */ rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ - st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR; /* Create secondary GPT header */ - st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */ - st_qword(buf + GPTH_BakLba, 1); /* LBA of another header */ - st_qword(buf + GPTH_PtOfs, s_bpt); /* LBA of this table */ + st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */ + st_qword(buf + GPTH_BakLba, 1); /* LBA of primary header */ + st_qword(buf + GPTH_PtOfs, top_bpt); /* LBA of this table */ st_dword(buf + GPTH_Bcc, 0); for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */ - st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ + st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */ if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR; /* Create protective MBR */ - mem_set(buf, 0, ss); - mem_cpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */ + memset(buf, 0, ss); + memcpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */ st_word(buf + BS_55AA, 0xAA55); if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; } else #endif - { /* Create partitions in MBR */ + { /* Create partitions in MBR format */ sz_drv32 = (DWORD)sz_drv; - n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ + n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */ for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ; - if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ + if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */ - mem_set(buf, 0, FF_MAX_SS); /* Clear MBR */ + memset(buf, 0, FF_MAX_SS); /* Clear MBR */ pte = buf + MBR_Table; /* Partition table in the MBR */ - for (i = 0, s_lba32 = n_sc; i < 4 && s_lba32 != 0 && s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) { - n_lba32 = (DWORD)plst[i]; /* Get partition size */ - if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 : sz_drv32 / 100 * n_lba32; /* Size in percentage? */ - if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < s_lba32) n_lba32 = sz_drv32 - s_lba32; /* Clip at drive size */ - if (n_lba32 == 0) break; /* End of table or no sector to allocate? */ - - st_dword(pte + PTE_StLba, s_lba32); /* Start LBA */ - st_dword(pte + PTE_SizLba, n_lba32); /* Number of sectors */ - pte[PTE_System] = (BYTE)sys; /* System type */ - - cy = (UINT)(s_lba32 / n_sc / n_hd); /* Start cylinder */ - hd = (BYTE)(s_lba32 / n_sc % n_hd); /* Start head */ - sc = (BYTE)(s_lba32 % n_sc + 1); /* Start sector */ + for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) { + sz_part32 = (DWORD)plst[i]; /* Get partition size */ + if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */ + if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */ + if (sz_part32 == 0) break; /* End of table or no sector to allocate? */ + + st_dword(pte + PTE_StLba, nxt_alloc32); /* Start LBA */ + st_dword(pte + PTE_SizLba, sz_part32); /* Number of sectors */ + pte[PTE_System] = sys; /* System type */ + + cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Start cylinder */ + hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Start head */ + sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Start sector */ pte[PTE_StHead] = hd; pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_StCyl] = (BYTE)cy; - cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd); /* End cylinder */ - hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd); /* End head */ - sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1); /* End sector */ + cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* End cylinder */ + hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* End head */ + sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* End sector */ pte[PTE_EdHead] = hd; pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc); pte[PTE_EdCyl] = (BYTE)cy; @@ -5855,7 +5876,7 @@ FRESULT f_mkfs ( static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0}; /* Default parameter */ BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart; WORD ss; /* Sector size */ - DWORD sz_buf, sz_blk, n_clst, pau, nsect, n; + DWORD sz_buf, sz_blk, n_clst, pau, nsect, n, vsn; LBA_t sz_vol, b_vol, b_fat, b_data; /* Size of volume, Base LBA of volume, fat, data */ LBA_t sect, lba[2]; DWORD sz_rsv, sz_fat, sz_dir, sz_au; /* Size of reserved, fat, dir, data, cluster */ @@ -5921,7 +5942,7 @@ FRESULT f_mkfs ( ofs = i = 0; while (n_ent) { /* Find MS Basic partition with order of ipart */ if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Get PT sector */ - if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ + if (!memcmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) { /* MS basic data partition? */ b_vol = ld_qword(buf + ofs + GPTE_FstLba); sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - b_vol + 1; break; @@ -5957,7 +5978,7 @@ FRESULT f_mkfs ( } if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if volume size is >=128s */ - /* Now start to create a FAT volume at b_vol and sz_vol */ + /* Now start to create an FAT volume at b_vol and sz_vol */ do { /* Pre-determine the FAT type */ if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) { /* exFAT possible? */ @@ -5978,12 +5999,13 @@ FRESULT f_mkfs ( fsty = FS_FAT16; } while (0); + vsn = (DWORD)sz_vol + GET_FATTIME(); /* VSN generated from current time and partitiion size */ + #if FF_FS_EXFAT if (fsty == FS_EXFAT) { /* Create an exFAT volume */ - DWORD szb_bit, szb_case, sum, nb, cl, tbl[3]; + DWORD szb_bit, szb_case, sum, nbit, clu, clen[3]; WCHAR ch, si; UINT j, st; - BYTE b; if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too small volume for exFAT? */ #if FF_USE_TRIM @@ -6004,12 +6026,12 @@ FRESULT f_mkfs ( if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too few clusters? */ if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too many clusters? */ - szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ - tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */ + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + clen[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss); /* Number of allocation bitmap clusters */ /* Create a compressed up-case table */ - sect = b_data + sz_au * tbl[0]; /* Table start sector */ - sum = 0; /* Table checksum to be stored in the 82 entry */ + sect = b_data + sz_au * clen[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ st = 0; si = 0; i = 0; j = 0; szb_case = 0; do { switch (st) { @@ -6020,10 +6042,10 @@ FRESULT f_mkfs ( } for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ if (j >= 128) { - ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 chars */ } st = 1; /* Do not compress short run */ - /* go to next case */ + /* FALLTHROUGH */ case 1: ch = si++; /* Fill the short run */ if (--j == 0) st = 0; @@ -6033,7 +6055,7 @@ FRESULT f_mkfs ( ch = (WCHAR)j; si += (WCHAR)j; /* Number of chars to skip */ st = 0; } - sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); i += 2; szb_case += 2; if (si == 0 || i == sz_buf * ss) { /* Write buffered data when buffer full or end of process */ @@ -6042,16 +6064,15 @@ FRESULT f_mkfs ( sect += n; i = 0; } } while (si); - tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */ - tbl[2] = 1; /* Number of root dir clusters */ + clen[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss); /* Number of up-case table clusters */ + clen[2] = 1; /* Number of root dir clusters */ /* Initialize the allocation bitmap */ - sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ - nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of bitmap sectors */ + nbit = clen[0] + clen[1] + clen[2]; /* Number of clusters in-use by system (bitmap, up-case and root-dir) */ do { - mem_set(buf, 0, sz_buf * ss); - for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ; - for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ; + memset(buf, 0, sz_buf * ss); /* Initialize bitmap buffer */ + for (i = 0; nbit != 0 && i / 8 < sz_buf * ss; buf[i / 8] |= 1 << (i % 8), i++, nbit--) ; /* Mark used clusters */ n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; @@ -6059,40 +6080,40 @@ FRESULT f_mkfs ( /* Initialize the FAT */ sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ - j = nb = cl = 0; + j = nbit = clu = 0; do { - mem_set(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write index */ - if (cl == 0) { /* Set FAT [0] and FAT[1] */ - st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; - st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + memset(buf, 0, sz_buf * ss); i = 0; /* Clear work area and reset write offset */ + if (clu == 0) { /* Initialize FAT [0] and FAT[1] */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; clu++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; clu++; } do { /* Create chains of bitmap, up-case and root dir */ - while (nb != 0 && i < sz_buf * ss) { /* Create a chain */ - st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); - i += 4; cl++; nb--; + while (nbit != 0 && i < sz_buf * ss) { /* Create a chain */ + st_dword(buf + i, (nbit > 1) ? clu + 1 : 0xFFFFFFFF); + i += 4; clu++; nbit--; } - if (nb == 0 && j < 3) nb = tbl[j++]; /* Next chain */ - } while (nb != 0 && i < sz_buf * ss); + if (nbit == 0 && j < 3) nbit = clen[j++]; /* Get next chain length */ + } while (nbit != 0 && i < sz_buf * ss); n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); sect += n; nsect -= n; } while (nsect); /* Initialize the root directory */ - mem_set(buf, 0, sz_buf * ss); - buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */ - buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ - st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ - st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ - buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ - st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ - st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); /* cluster */ - st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ - sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au; /* Start of the root directory and number of sectors */ + memset(buf, 0, sz_buf * ss); + buf[SZDIRE * 0 + 0] = ET_VLABEL; /* Volume label entry (no label) */ + buf[SZDIRE * 1 + 0] = ET_BITMAP; /* Bitmap entry */ + st_dword(buf + SZDIRE * 1 + 20, 2); /* cluster */ + st_dword(buf + SZDIRE * 1 + 24, szb_bit); /* size */ + buf[SZDIRE * 2 + 0] = ET_UPCASE; /* Up-case table entry */ + st_dword(buf + SZDIRE * 2 + 4, sum); /* sum */ + st_dword(buf + SZDIRE * 2 + 20, 2 + clen[0]); /* cluster */ + st_dword(buf + SZDIRE * 2 + 24, szb_case); /* size */ + sect = b_data + sz_au * (clen[0] + clen[1]); nsect = sz_au; /* Start of the root directory and number of sectors */ do { /* Fill root directory sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); - mem_set(buf, 0, ss); + memset(buf, 0, ss); /* Rest of entries are filled with zero */ sect += n; nsect -= n; } while (nsect); @@ -6100,16 +6121,16 @@ FRESULT f_mkfs ( sect = b_vol; for (n = 0; n < 2; n++) { /* Main record (+0) */ - mem_set(buf, 0, ss); - mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + memset(buf, 0, ss); + memcpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */ st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */ st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ - st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ - st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, vsn); /* VSN */ st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */ for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ @@ -6122,14 +6143,14 @@ FRESULT f_mkfs ( } if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Extended bootstrap record (+1..+8) */ - mem_set(buf, 0, ss); + memset(buf, 0, ss); st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ for (j = 1; j < 9; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); } /* OEM/Reserved record (+9..+10) */ - mem_set(buf, 0, ss); + memset(buf, 0, ss); for ( ; j < 11; j++) { for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); @@ -6197,7 +6218,7 @@ FRESULT f_mkfs ( if (fsty == FS_FAT16) { if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ if (sz_au == 0 && (pau * 2) <= 64) { - sz_au = pau * 2; continue; /* Adjust cluster size and retry */ + sz_au = pau * 2; continue; /* Adjust cluster size and retry */ } if ((fsopt & FM_FAT32)) { fsty = FS_FAT32; continue; /* Switch type to FAT32 and retry */ @@ -6221,8 +6242,8 @@ FRESULT f_mkfs ( disk_ioctl(pdrv, CTRL_TRIM, lba); #endif /* Create FAT VBR */ - mem_set(buf, 0, ss); - mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + memset(buf, 0, ss); + memcpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11); /* Boot jump code (x86), OEM name */ st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ @@ -6238,20 +6259,20 @@ FRESULT f_mkfs ( st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */ if (fsty == FS_FAT32) { - st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID32, vsn); /* VSN */ st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig32] = 0x29; /* Extended boot signature */ - mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ } else { - st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_dword(buf + BS_VolID, vsn); /* VSN */ st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ buf[BS_BootSig] = 0x29; /* Extended boot signature */ - mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + memcpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ } st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */ @@ -6259,7 +6280,7 @@ FRESULT f_mkfs ( /* Create FSINFO record if needed */ if (fsty == FS_FAT32) { disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ - mem_set(buf, 0, ss); + memset(buf, 0, ss); st_dword(buf + FSI_LeadSig, 0x41615252); st_dword(buf + FSI_StrucSig, 0x61417272); st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ @@ -6270,7 +6291,7 @@ FRESULT f_mkfs ( } /* Initialize FAT area */ - mem_set(buf, 0, sz_buf * ss); + memset(buf, 0, sz_buf * ss); sect = b_fat; /* FAT start sector */ for (i = 0; i < n_fat; i++) { /* Initialize FATs each */ if (fsty == FS_FAT32) { @@ -6284,7 +6305,7 @@ FRESULT f_mkfs ( do { /* Fill FAT sectors */ n = (nsect > sz_buf) ? sz_buf : nsect; if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); - mem_set(buf, 0, ss); /* Rest of FAT all are cleared */ + memset(buf, 0, ss); /* Rest of FAT all are cleared */ sect += n; nsect -= n; } while (nsect); } @@ -6324,8 +6345,8 @@ FRESULT f_mkfs ( if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it back to the MBR */ } } else { /* Volume as a new single partition */ - if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ - lba[0] = sz_vol, lba[1] = 0; + if (!(fsopt & FM_SFD)) { /* Create partition table if not in SFD */ + lba[0] = sz_vol; lba[1] = 0; fr = create_partition(pdrv, lba, sys, buf); if (fr != FR_OK) LEAVE_MKFS(fr); } @@ -6408,12 +6429,12 @@ TCHAR* f_gets ( if (rc != 1) break; /* EOF? */ wc = s[0]; if (dbc_1st((BYTE)wc)) { /* DBC 1st byte? */ - f_read(fp, s, 1, &rc); /* Get DBC 2nd byte */ + f_read(fp, s, 1, &rc); /* Get 2nd byte */ if (rc != 1 || !dbc_2nd(s[0])) continue; /* Wrong code? */ wc = wc << 8 | s[0]; } - dc = ff_oem2uni(wc, CODEPAGE); /* OEM --> */ - if (dc == 0) continue; + dc = ff_oem2uni(wc, CODEPAGE); /* Convert ANSI/OEM into Unicode */ + if (dc == 0) continue; /* Conversion error? */ #elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2 /* Read a character in UTF-16LE/BE */ f_read(fp, s, 2, &rc); /* Get a code unit */ if (rc != 2) break; /* EOF? */ @@ -6436,7 +6457,7 @@ TCHAR* f_gets ( if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; } /* 3-byte sequence? */ if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; } /* 4-byte sequence? */ if (ct == 0) continue; - f_read(fp, s, ct, &rc); /* Get trailing bytes */ + f_read(fp, s, ct, &rc); /* Get trailing bytes */ if (rc != ct) break; rc = 0; do { /* Merge the byte sequence */ @@ -6505,11 +6526,14 @@ TCHAR* f_gets ( #if !FF_FS_READONLY #include +#define SZ_PUTC_BUF 64 +#define SZ_NUM_BUF 32 + /*-----------------------------------------------------------------------*/ -/* Put a Character to the File (sub-functions) */ +/* Put a Character to the File (with sub-functions) */ /*-----------------------------------------------------------------------*/ -/* Putchar output buffer and work area */ +/* Output buffer and work area */ typedef struct { FIL *fp; /* Ptr to the writing file */ @@ -6520,11 +6544,11 @@ typedef struct { BYTE bs[4]; UINT wi, ct; #endif - BYTE buf[64]; /* Write buffer */ + BYTE buf[SZ_PUTC_BUF]; /* Write buffer */ } putbuff; -/* Buffered write with code conversion */ +/* Buffered file write with code conversion */ static void putc_bfd (putbuff* pb, TCHAR c) { @@ -6534,7 +6558,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) WCHAR hs, wc; #if FF_LFN_UNICODE == 2 DWORD dc; - TCHAR *tp; + const TCHAR *tp; #endif #endif @@ -6543,7 +6567,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) } i = pb->idx; /* Write index of pb->buf[] */ - if (i < 0) return; + if (i < 0) return; /* In write error? */ nc = pb->nchr; /* Write unit counter */ #if FF_USE_LFN && FF_LFN_UNICODE @@ -6576,7 +6600,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) return; } } - tp = (TCHAR*)pb->bs; + tp = (const TCHAR*)pb->bs; dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ if (dc == 0xFFFFFFFF) return; /* Wrong code? */ wc = (WCHAR)dc; @@ -6642,7 +6666,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) pb->buf[i++] = (BYTE)wc; #endif -#else /* ANSI/OEM input (without re-encoding) */ +#else /* ANSI/OEM input (without re-encoding) */ pb->buf[i++] = (BYTE)c; #endif @@ -6664,7 +6688,7 @@ static int putc_flush (putbuff* pb) if ( pb->idx >= 0 /* Flush buffered characters to the file */ && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK && (UINT)pb->idx == nw) return pb->nchr; - return EOF; + return -1; } @@ -6672,7 +6696,7 @@ static int putc_flush (putbuff* pb) static void putc_init (putbuff* pb, FIL* fp) { - mem_set(pb, 0, sizeof (putbuff)); + memset(pb, 0, sizeof (putbuff)); pb->fp = fp; } @@ -6715,8 +6739,129 @@ int f_puts ( /*-----------------------------------------------------------------------*/ -/* Put a Formatted String to the File */ +/* Put a Formatted String to the File (with sub-functions) */ /*-----------------------------------------------------------------------*/ +#if FF_PRINT_FLOAT && FF_INTDEF == 2 +#include + +static int ilog10 (double n) /* Calculate log10(n) in integer output */ +{ + int rv = 0; + + while (n >= 10) { /* Decimate digit in right shift */ + if (n >= 100000) { + n /= 100000; rv += 5; + } else { + n /= 10; rv++; + } + } + while (n < 1) { /* Decimate digit in left shift */ + if (n < 0.00001) { + n *= 100000; rv -= 5; + } else { + n *= 10; rv--; + } + } + return rv; +} + + +static double i10x (int n) /* Calculate 10^n in integer input */ +{ + double rv = 1; + + while (n > 0) { /* Left shift */ + if (n >= 5) { + rv *= 100000; n -= 5; + } else { + rv *= 10; n--; + } + } + while (n < 0) { /* Right shift */ + if (n <= -5) { + rv /= 100000; n += 5; + } else { + rv /= 10; n++; + } + } + return rv; +} + + +static void ftoa ( + char* buf, /* Buffer to output the floating point string */ + double val, /* Value to output */ + int prec, /* Number of fractional digits */ + TCHAR fmt /* Notation */ +) +{ + int d; + int e = 0, m = 0; + char sign = 0; + double w; + const char *er = 0; + const char ds = FF_PRINT_FLOAT == 2 ? ',' : '.'; + + + if (isnan(val)) { /* Not a number? */ + er = "NaN"; + } else { + if (prec < 0) prec = 6; /* Default precision? (6 fractional digits) */ + if (val < 0) { /* Nagative? */ + val = 0 - val; sign = '-'; + } else { + sign = '+'; + } + if (isinf(val)) { /* Infinite? */ + er = "INF"; + } else { + if (fmt == 'f') { /* Decimal notation? */ + val += i10x(0 - prec) / 2; /* Round (nearest) */ + m = ilog10(val); + if (m < 0) m = 0; + if (m + prec + 3 >= SZ_NUM_BUF) er = "OV"; /* Buffer overflow? */ + } else { /* E notation */ + if (val != 0) { /* Not a true zero? */ + val += i10x(ilog10(val) - prec) / 2; /* Round (nearest) */ + e = ilog10(val); + if (e > 99 || prec + 7 >= SZ_NUM_BUF) { /* Buffer overflow or E > +99? */ + er = "OV"; + } else { + if (e < -99) e = -99; + val /= i10x(e); /* Normalize */ + } + } + } + } + if (!er) { /* Not error condition */ + if (sign == '-') *buf++ = sign; /* Add a - if negative value */ + do { /* Put decimal number */ + if (m == -1) *buf++ = ds; /* Insert a decimal separator when get into fractional part */ + w = i10x(m); /* Snip the highest digit d */ + d = (int)(val / w); val -= d * w; + *buf++ = (char)('0' + d); /* Put the digit */ + } while (--m >= -prec); /* Output all digits specified by prec */ + if (fmt != 'f') { /* Put exponent if needed */ + *buf++ = (char)fmt; + if (e < 0) { + e = 0 - e; *buf++ = '-'; + } else { + *buf++ = '+'; + } + *buf++ = (char)('0' + e / 10); + *buf++ = (char)('0' + e % 10); + } + } + } + if (er) { /* Error condition */ + if (sign) *buf++ = sign; /* Add sign if needed */ + do *buf++ = *er++; while (*er); /* Put error symbol */ + } + *buf = 0; /* Term */ +} +#endif /* FF_PRINT_FLOAT && FF_INTDEF == 2 */ + + int f_printf ( FIL* fp, /* Pointer to the file object */ @@ -6726,10 +6871,16 @@ int f_printf ( { va_list arp; putbuff pb; - BYTE f, r; - UINT i, j, w; + UINT i, j, w, f, r; + int prec; +#if FF_PRINT_LLI && FF_INTDEF == 2 + QWORD v; +#else DWORD v; - TCHAR c, d, str[32], *p; +#endif + TCHAR tc, pad, *tp; + TCHAR nul = 0; + char d, str[SZ_NUM_BUF]; putc_init(&pb, fp); @@ -6737,88 +6888,122 @@ int f_printf ( va_start(arp, fmt); for (;;) { - c = *fmt++; - if (c == 0) break; /* End of string */ - if (c != '%') { /* Non escape character */ - putc_bfd(&pb, c); + tc = *fmt++; + if (tc == 0) break; /* End of format string */ + if (tc != '%') { /* Not an escape character (pass-through) */ + putc_bfd(&pb, tc); continue; } - w = f = 0; - c = *fmt++; - if (c == '0') { /* Flag: '0' padding */ - f = 1; c = *fmt++; - } else { - if (c == '-') { /* Flag: left justified */ - f = 2; c = *fmt++; - } + f = w = 0; pad = ' '; prec = -1; /* Initialize parms */ + tc = *fmt++; + if (tc == '0') { /* Flag: '0' padded */ + pad = '0'; tc = *fmt++; + } else if (tc == '-') { /* Flag: Left aligned */ + f = 2; tc = *fmt++; } - if (c == '*') { /* Minimum width by argument */ + if (tc == '*') { /* Minimum width from an argument */ w = va_arg(arp, int); - c = *fmt++; + tc = *fmt++; } else { - while (IsDigit(c)) { /* Minimum width */ - w = w * 10 + c - '0'; - c = *fmt++; - } - } - if (c == 'l' || c == 'L') { /* Type prefix: Size is long int */ - f |= 4; c = *fmt++; - } - if (c == 0) break; - d = c; - if (IsLower(d)) d -= 0x20; - switch (d) { /* Atgument type is... */ - case 'S' : /* String */ - p = va_arg(arp, TCHAR*); - for (j = 0; p[j]; j++) ; - if (!(f & 2)) { /* Right padded */ - while (j++ < w) putc_bfd(&pb, ' ') ; - } - while (*p) putc_bfd(&pb, *p++) ; /* String body */ - while (j++ < w) putc_bfd(&pb, ' ') ; /* Left padded */ - continue; - - case 'C' : /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; - - case 'B' : /* Unsigned binary */ + while (IsDigit(tc)) { /* Minimum width */ + w = w * 10 + tc - '0'; + tc = *fmt++; + } + } + if (tc == '.') { /* Precision */ + tc = *fmt++; + if (tc == '*') { /* Precision from an argument */ + prec = va_arg(arp, int); + tc = *fmt++; + } else { + prec = 0; + while (IsDigit(tc)) { /* Precision */ + prec = prec * 10 + tc - '0'; + tc = *fmt++; + } + } + } + if (tc == 'l') { /* Size: long int */ + f |= 4; tc = *fmt++; +#if FF_PRINT_LLI && FF_INTDEF == 2 + if (tc == 'l') { /* Size: long long int */ + f |= 8; tc = *fmt++; + } +#endif + } + if (tc == 0) break; /* End of format string */ + switch (tc) { /* Atgument type is... */ + case 'b': /* Unsigned binary */ r = 2; break; - - case 'O' : /* Unsigned octal */ + case 'o': /* Unsigned octal */ r = 8; break; - - case 'D' : /* Signed decimal */ - case 'U' : /* Unsigned decimal */ + case 'd': /* Signed decimal */ + case 'u': /* Unsigned decimal */ r = 10; break; - - case 'X' : /* Unsigned hexdecimal */ + case 'x': /* Unsigned hexdecimal (lower case) */ + case 'X': /* Unsigned hexdecimal (upper case) */ r = 16; break; - + case 'c': /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); + continue; + case 's': /* String */ + tp = va_arg(arp, TCHAR*); /* Get a pointer argument */ + if (!tp) tp = &nul; /* Null ptr generates a null string */ + for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */ + if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */ + for ( ; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + while (*tp && prec--) putc_bfd(&pb, *tp++); /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ + continue; +#if FF_PRINT_FLOAT && FF_INTDEF == 2 + case 'f': /* Floating point (decimal) */ + case 'e': /* Floating point (e) */ + case 'E': /* Floating point (E) */ + ftoa(str, va_arg(arp, double), prec, tc); /* Make a flaoting point string */ + for (j = strlen(str); !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + for (i = 0; str[i]; putc_bfd(&pb, str[i++])) ; /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ + continue; +#endif default: /* Unknown type (pass-through) */ - putc_bfd(&pb, c); continue; + putc_bfd(&pb, tc); continue; } - /* Get an argument and put it in numeral */ - v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); - if (d == 'D' && (v & 0x80000000)) { - v = 0 - v; - f |= 8; + /* Get an integer argument and put it in numeral */ +#if FF_PRINT_LLI && FF_INTDEF == 2 + if (f & 8) { /* long long argument? */ + v = (QWORD)va_arg(arp, LONGLONG); + } else { + if (f & 4) { /* long argument? */ + v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, long) : (QWORD)va_arg(arp, unsigned long); + } else { /* int/short/char argument */ + v = (tc == 'd') ? (QWORD)(LONGLONG)va_arg(arp, int) : (QWORD)va_arg(arp, unsigned int); + } } + if (tc == 'd' && (v & 0x8000000000000000)) { /* Negative value? */ + v = 0 - v; f |= 1; + } +#else + if (f & 4) { /* long argument? */ + v = (DWORD)va_arg(arp, long); + } else { /* int/short/char argument */ + v = (tc == 'd') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int); + } + if (tc == 'd' && (v & 0x80000000)) { /* Negative value? */ + v = 0 - v; f |= 1; + } +#endif i = 0; - do { - d = (TCHAR)(v % r); v /= r; - if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + do { /* Make an integer number string */ + d = (char)(v % r); v /= r; + if (d > 9) d += (tc == 'x') ? 0x27 : 0x07; str[i++] = d + '0'; - } while (v && i < sizeof str / sizeof *str); - if (f & 8) str[i++] = '-'; - j = i; d = (f & 1) ? '0' : ' '; - if (!(f & 2)) { - while (j++ < w) putc_bfd(&pb, d); /* Right pad */ - } - do { - putc_bfd(&pb, str[--i]); /* Number body */ - } while (i); - while (j++ < w) putc_bfd(&pb, d); /* Left pad */ + } while (v && i < SZ_NUM_BUF); + if (f & 1) str[i++] = '-'; /* Sign */ + /* Write it */ + for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ + do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */ + while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ } va_end(arp); @@ -6840,8 +7025,8 @@ FRESULT f_setcp ( WORD cp /* Value to be set as active code page */ ) { - static const WORD validcp[] = { 437, 720, 737, 771, 775, 850, 852, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; - static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; + static const WORD validcp[22] = { 437, 720, 737, 771, 775, 850, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 932, 936, 949, 950, 0}; + static const BYTE* const tables[22] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct855, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0}; UINT i; diff --git a/FatFs/ff.h b/FatFs/ff.h index 5225041..4866576 100644 --- a/FatFs/ff.h +++ b/FatFs/ff.h @@ -1,8 +1,8 @@ /*----------------------------------------------------------------------------/ -/ FatFs - Generic FAT Filesystem module R0.14 / +/ FatFs - Generic FAT Filesystem module R0.14b / /-----------------------------------------------------------------------------/ / -/ Copyright (C) 2019, ChaN, all right reserved. +/ Copyright (C) 2021, ChaN, all right reserved. / / FatFs module is an open source software. Redistribution and use of FatFs in / source and binary forms, with or without modification, are permitted provided @@ -20,7 +20,7 @@ #ifndef FF_DEFINED -#define FF_DEFINED 86606 /* Revision ID */ +#define FF_DEFINED 86631 /* Revision ID */ #ifdef __cplusplus extern "C" { @@ -35,10 +35,14 @@ extern "C" { /* Integer types used for FatFs API */ -#if defined(_WIN32) /* Main development platform */ +#if defined(_WIN32) /* Windows VC++ (for development only) */ #define FF_INTDEF 2 #include typedef unsigned __int64 QWORD; +#include +#define isnan(v) _isnan(v) +#define isinf(v) (!_finite(v)) + #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ #define FF_INTDEF 2 #include @@ -48,6 +52,7 @@ typedef uint16_t WORD; /* 16-bit unsigned integer */ typedef uint32_t DWORD; /* 32-bit unsigned integer */ typedef uint64_t QWORD; /* 64-bit unsigned integer */ typedef WORD WCHAR; /* UTF-16 character type */ + #else /* Earlier than C99 */ #define FF_INTDEF 1 typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ @@ -58,28 +63,29 @@ typedef WORD WCHAR; /* UTF-16 character type */ #endif -/* Definitions of volume management */ +/* Type of file size and LBA variables */ -#if FF_MULTI_PARTITION /* Multiple partition configuration */ -typedef struct { - BYTE pd; /* Physical drive number */ - BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ -} PARTITION; -extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ +#if FF_FS_EXFAT +#if FF_INTDEF != 2 +#error exFAT feature wants C99 or later #endif - -#if FF_STR_VOLUME_ID -#ifndef FF_VOLUME_STRS -extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ +typedef QWORD FSIZE_t; +#if FF_LBA64 +typedef QWORD LBA_t; +#else +typedef DWORD LBA_t; #endif +#else +#if FF_LBA64 +#error exFAT needs to be enabled when enable 64-bit LBA +#endif +typedef DWORD FSIZE_t; +typedef DWORD LBA_t; #endif -/* Type of path name strings on FatFs API */ - -#ifndef _INC_TCHAR -#define _INC_TCHAR +/* Type of path name strings on FatFs API (TCHAR) */ #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ typedef WCHAR TCHAR; @@ -101,28 +107,22 @@ typedef char TCHAR; #define _TEXT(x) x #endif -#endif +/* Definitions of volume management */ -/* Type of file size and LBA variables */ - -#if FF_FS_EXFAT -#if FF_INTDEF != 2 -#error exFAT feature wants C99 or later -#endif -typedef QWORD FSIZE_t; -#if FF_LBA64 -typedef QWORD LBA_t; -#else -typedef DWORD LBA_t; +#if FF_MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ #endif -#else -#if FF_LBA64 -#error exFAT needs to be enabled when enable 64-bit LBA + +#if FF_STR_VOLUME_ID +#ifndef FF_VOLUME_STRS +extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ #endif -typedef DWORD FSIZE_t; -typedef DWORD LBA_t; #endif @@ -345,10 +345,6 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil #define f_rmdir(path) f_unlink(path) #define f_unmount(path) f_mount(0, path, 0) -#ifndef EOF -#define EOF (-1) -#endif - diff --git a/FatFs/ffconf.h b/FatFs/ffconf.h index 5e33c31..0847dfd 100644 --- a/FatFs/ffconf.h +++ b/FatFs/ffconf.h @@ -2,11 +2,12 @@ / FatFs Functional Configurations /---------------------------------------------------------------------------*/ -#define FFCONF_DEF 86606 /* Revision ID */ +#define FFCONF_DEF 86631 /* Revision ID */ /*---------------------------------------------------------------------------/ / Function Configurations /---------------------------------------------------------------------------*/ + #define FF_FS_READONLY 0 /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) / Read-only configuration removes writing API functions, f_write(), f_sync(), @@ -24,14 +25,6 @@ / 3: f_lseek() function is removed in addition to 2. */ -#define FF_USE_STRFUNC 0 -/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf(). -/ -/ 0: Disable string functions. -/ 1: Enable without LF-CRLF conversion. -/ 2: Enable with LF-CRLF conversion. */ - - #define FF_USE_FIND 1 /* This option switches filtered directory read functions, f_findfirst() and / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ @@ -63,6 +56,30 @@ /* This option switches f_forward() function. (0:Disable or 1:Enable) */ +#define FF_USE_STRFUNC 0 +#define FF_PRINT_LLI 0 +#define FF_PRINT_FLOAT 0 +#define FF_STRF_ENCODE 0 +/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. +/ +/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 + makes f_printf() support floating point argument. These features want C99 or later. +/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character +/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE +/ to be read/written via those functions. +/ +/ 0: ANSI/OEM in current CP +/ 1: Unicode in UTF-16LE +/ 2: Unicode in UTF-16BE +/ 3: Unicode in UTF-8 +*/ + + /*---------------------------------------------------------------------------/ / Locale and Namespace Configurations /---------------------------------------------------------------------------*/ @@ -136,19 +153,6 @@ / on character encoding. When LFN is not enabled, these options have no effect. */ -#define FF_STRF_ENCODE 0 -/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(), -/ f_putc(), f_puts and f_printf() convert the character encoding in it. -/ This option selects assumption of character encoding ON THE FILE to be -/ read/written via those functions. -/ -/ 0: ANSI/OEM in current CP -/ 1: Unicode in UTF-16LE -/ 2: Unicode in UTF-16BE -/ 3: Unicode in UTF-8 -*/ - - #define FF_FS_RPATH 0 /* This option configures support for relative path. / @@ -193,7 +197,7 @@ #define FF_MAX_SS 512 /* This set of options configures the range of sector size to be supported. (512, / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and -/ harddisk. But a larger value may be required for on-board flash memory and some +/ harddisk, but a larger value may be required for on-board flash memory and some / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured / for variable sector size mode and disk_ioctl() function needs to implement / GET_SECTOR_SIZE command. */ @@ -204,8 +208,8 @@ / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ -#define FF_MIN_GPT 0x100000000 -/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and +#define FF_MIN_GPT 0x10000000 +/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ @@ -236,7 +240,7 @@ #define FF_FS_NORTC 0 #define FF_NORTC_MON 1 #define FF_NORTC_MDAY 1 -#define FF_NORTC_YEAR 2019 +#define FF_NORTC_YEAR 2020 /* The option FF_FS_NORTC switches timestamp functiton. If the system does not have / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable / the timestamp function. Every object modified by FatFs will have a fixed timestamp @@ -316,4 +320,10 @@ / PIC32 0 H8/300H 0 x86 0/1 */ +#define FF_USE_STRINGLIB 1 /* 0 or 1 */ +/* + * if 1 Use standard string.h for memcpy, memcmp, memset, strchr + * if 0 use own + */ + /*--- End of configuration options ---*/ diff --git a/Makefile b/Makefile index e302016..08399f2 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,20 @@ # NOTE: Can be overridden externally. # +#Build target +ifeq ($(TARGET),) + TARGET = F072 +endif +TARGET=F303 + # Compiler options here. ifeq ($(USE_OPT),) - USE_OPT = -O2 -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage + ifeq ($(TARGET),F303) +USE_OPT = -O2 -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage -std=c11 -DLCD_DRIVER_ST7796S -DLCD_480x320 -D__VNA_ENABLE_DAC__ -D__LCD_BRIGHTNESS__ -DPOINTS_COUNT=401 +#USE_OPT+=-fstack-protector-strong + else +USE_OPT = -O2 -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage -std=c11 -DLCD_DRIVER_ILI9341 -DLCD_320x240 -D__DFU_SOFTWARE_MODE__ -DPOINTS_COUNT=101 + endif endif # C specific options here (added to USE_OPT). @@ -60,19 +71,20 @@ endif ############################################################################## # Architecture or project specific options # +ifeq ($(TARGET),F303) + USE_FPU = hard +endif -# Stack size to be allocated to the Cortex-M process stack. This stack is -# the stack used by the main() thread. -ifeq ($(USE_PROCESS_STACKSIZE),) +# Stack size to the allocated to the Cortex-M main/exceptions stack. This +# stack is used for processing interrupts and exceptions. +ifeq ($(USE_EXCEPTIONS_STACKSIZE),) USE_PROCESS_STACKSIZE = 0x200 endif - # Stack size to the allocated to the Cortex-M main/exceptions stack. This # stack is used for processing interrupts and exceptions. ifeq ($(USE_EXCEPTIONS_STACKSIZE),) USE_EXCEPTIONS_STACKSIZE = 0x100 endif - # # Architecture or project specific options ############################################################################## @@ -81,10 +93,6 @@ endif # Project, sources and paths # -# Dvice node to flash -DEVICE = /dev/cu.usbmodem401 -#DEVICE = /dev/ttyACM0 - # Define project name here PROJECT = ch @@ -93,24 +101,38 @@ PROJECT = ch CHIBIOS = ChibiOS PROJ = . # Startup files. -include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f0xx.mk # HAL-OSAL files (optional). -include $(CHIBIOS)/os/hal/hal.mk -include $(CHIBIOS)/os/hal/ports/STM32/STM32F0xx/platform.mk -#include $(CHIBIOS)/os/hal/boards/ST_STM32F072B_DISCOVERY/board.mk -include NANOVNA_STM32_F072/board.mk +ifeq ($(TARGET),F303) + include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f3xx.mk + include $(CHIBIOS)/os/hal/hal.mk + include $(CHIBIOS)/os/hal/ports/STM32/STM32F3xx/platform.mk + include NANOVNA_STM32_F303/board.mk +else + include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32f0xx.mk + include $(CHIBIOS)/os/hal/hal.mk + include $(CHIBIOS)/os/hal/ports/STM32/STM32F0xx/platform.mk + include NANOVNA_STM32_F072/board.mk +endif + include $(CHIBIOS)/os/hal/osal/rt/osal.mk # RTOS files (optional). include $(CHIBIOS)/os/rt/rt.mk +ifeq ($(TARGET),F303) +include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk +else include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v6m.mk +endif # Other files (optional). #include $(CHIBIOS)/test/rt/test.mk include $(CHIBIOS)/os/hal/lib/streams/streams.mk #include $(CHIBIOS)/os/various/shell/shell.mk # Define linker script file here -#LDSCRIPT= $(STARTUPLD)/STM32F072xB.ld -LDSCRIPT= NANOVNA_STM32_F072/STM32F072xB.ld +ifeq ($(TARGET),F303) + LDSCRIPT= NANOVNA_STM32_F303/STM32F303xC.ld +else + LDSCRIPT= NANOVNA_STM32_F072/STM32F072xB.ld +endif # C sources that can be compiled in ARM or THUMB mode depending on the global # setting. @@ -125,7 +147,7 @@ CSRC = $(STARTUPSRC) \ FatFs/ff.c \ FatFs/ffunicode.c \ usbcfg.c \ - main.c si5351.c tlv320aic3204.c dsp.c plot.c ui.c ili9341.c numfont20x22.c Font5x7.c Font7x13b.c Font10x14.c flash.c adc.c rtc.c + main.c si5351.c tlv320aic3204.c dsp.c plot.c ui.c ili9341.c numfont20x22.c Font5x7.c Font7x13b.c Font10x14.c flash.c adc.c rtc.c vna_math.c # C++ sources that can be compiled in ARM or THUMB mode depending on the global # setting. @@ -166,7 +188,11 @@ INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ # Compiler settings # -MCU = cortex-m0 +ifeq ($(TARGET),F303) + MCU = cortex-m4 +else + MCU = cortex-m0 +endif #TRGT = arm-elf- TRGT = arm-none-eabi- @@ -178,12 +204,13 @@ CPPC = $(TRGT)g++ LD = $(TRGT)gcc #LD = $(TRGT)g++ CP = $(TRGT)objcopy -AS = $(TRGT)gcc -x assembler-with-cpp -ggdb +AS = $(TRGT)gcc -x assembler-with-cpp AR = $(TRGT)ar OD = $(TRGT)objdump SZ = $(TRGT)size HEX = $(CP) -O ihex BIN = $(CP) -O binary +ELF = $(CP) -O elf # ARM-specific options here AOPT = @@ -206,7 +233,11 @@ CPPWARN = -Wall -Wextra -Wundef # # List all user C define here, like -D_DEBUG=1 -UDEFS = -DSHELL_CMD_TEST_ENABLED=FALSE -DSHELL_CMD_MEM_ENABLED=FALSE -DARM_MATH_CM0 -DVERSION=\"$(VERSION)\" +ifeq ($(TARGET),F303) + UDEFS = -DARM_MATH_CM4 -DVERSION=\"$(VERSION)\" -DNANOVNA_F303 +else + UDEFS = -DARM_MATH_CM0 -DVERSION=\"$(VERSION)\" +endif #Enable if use RTC and need auto select source LSE or LSI UDEFS+= -DVNA_AUTO_SELECT_RTC_SOURCE #Enable if install external 32.768kHz clock quartz on PC14 and PC15 pins on STM32 CPU and no VNA_AUTO_SELECT_RTC_SOURCE @@ -235,4 +266,13 @@ flash: build/ch.bin dfu-util -d 0483:df11 -a 0 -s 0x08000000:leave -D build/ch.bin dfu: - -printf "reset dfu\r" >$(DEVICE) && sleep 1 + -@printf "reset dfu\r" >/dev/cu.usbmodem401 + +TAGS: Makefile +ifeq ($(TARGET),F303) + @etags *.[ch] NANOVNA_STM32_F303/*.[ch] $(shell find ChibiOS/os/hal/ports/STM32/STM32F3xx ChibiOS/os -name \*.\[ch\] -print) +else + @etags *.[ch] NANOVNA_STM32_F072/*.[ch] $(shell find ChibiOS/os/hal/ports/STM32/STM32F0xx ChibiOS/os -name \*.\[ch\] -print) +endif + @ls -l TAGS + diff --git a/NANOVNA_STM32_F072/mcuconf.h b/NANOVNA_STM32_F072/mcuconf.h index 2cc46d9..b5d4cc9 100644 --- a/NANOVNA_STM32_F072/mcuconf.h +++ b/NANOVNA_STM32_F072/mcuconf.h @@ -215,7 +215,7 @@ /* * SPI driver system settings. */ -#define STM32_SPI_USE_SPI1 TRUE +#define STM32_SPI_USE_SPI1 FALSE #define STM32_SPI_USE_SPI2 FALSE #define STM32_SPI_SPI1_DMA_PRIORITY 1 #define STM32_SPI_SPI2_DMA_PRIORITY 1 diff --git a/NanoVNA_DAP.cfg b/NanoVNA_DAP.cfg index 72f4ca8..5723773 100644 --- a/NanoVNA_DAP.cfg +++ b/NanoVNA_DAP.cfg @@ -1,4 +1,4 @@ interface cmsis-dap transport select swd -source [find target/stm32f0x.cfg] +source [find target/stm32f3x.cfg] adapter_khz 8000 diff --git a/README.md b/README.md index bf36c75..21195d7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,12 @@ We remade NanoVNA based on edy555 (https://github.com/ttrftech/NanoVNA) , but m ## 编译 ## Build firmware +NanoVNA-H `TARGET = F072` + +NanoVNA-H4 `TARGET = F303` + + + It is recommended to compile with gcc-arm-none-eabi 8, and exceptions may occur with other versions of the compiler. Please sync the CibiOS submodule before compiling. diff --git a/STM32F303xC.ld b/STM32F303xC.ld new file mode 100644 index 0000000..c804623 --- /dev/null +++ b/STM32F303xC.ld @@ -0,0 +1,96 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + * STM32F303xC memory setup. + */ +MEMORY +{ + flash0 : org = 0x08000000, len = 192k + flash1 : org = 0x00000000, len = 0 + flash2 : org = 0x00000000, len = 0 + flash3 : org = 0x00000000, len = 0 + flash4 : org = 0x00000000, len = 0 + flash5 : org = 0x00000000, len = 0 + flash6 : org = 0x00000000, len = 0 + flash7 : org = 0x08030000, len = 64k + ram0 : org = 0x20000000, len = 40k + ram1 : org = 0x00000000, len = 0 + ram2 : org = 0x00000000, len = 0 + ram3 : org = 0x00000000, len = 0 + ram4 : org = 0x10000000, len = 8k + ram5 : org = 0x00000000, len = 0 + ram6 : org = 0x00000000, len = 0 + ram7 : org = 0x00000000, len = 0 +} + +/* For each data/text section two region are defined, a virtual region + and a load region (_LMA suffix).*/ + +/* Flash region to be used for exception vectors.*/ +REGION_ALIAS("VECTORS_FLASH", flash0); +REGION_ALIAS("VECTORS_FLASH_LMA", flash0); + +/* Flash region to be used for constructors and destructors.*/ +REGION_ALIAS("XTORS_FLASH", flash0); +REGION_ALIAS("XTORS_FLASH_LMA", flash0); + +/* Flash region to be used for code text.*/ +REGION_ALIAS("TEXT_FLASH", flash0); +REGION_ALIAS("TEXT_FLASH_LMA", flash0); + +/* Flash region to be used for read only data.*/ +REGION_ALIAS("RODATA_FLASH", flash0); +REGION_ALIAS("RODATA_FLASH_LMA", flash0); + +/* Flash region to be used for various.*/ +REGION_ALIAS("VARIOUS_FLASH", flash0); +REGION_ALIAS("VARIOUS_FLASH_LMA", flash0); + +/* Flash region to be used for RAM(n) initialization data.*/ +REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0); + +/* Flash region to be saved calibration data */ +REGION_ALIAS("CALDATA_FLASH", flash7); + +/* RAM region to be used for Main stack. This stack accommodates the processing + of all exceptions and interrupts.*/ +REGION_ALIAS("MAIN_STACK_RAM", ram0); + +/* RAM region to be used for the process stack. This is the stack used by + the main() function.*/ +REGION_ALIAS("PROCESS_STACK_RAM", ram0); + +/* RAM region to be used for data segment.*/ +REGION_ALIAS("DATA_RAM", ram0); +REGION_ALIAS("DATA_RAM_LMA", flash0); + +/* RAM region to be used for BSS segment.*/ +REGION_ALIAS("BSS_RAM", ram0); + +/* RAM region to be used for the default heap.*/ +REGION_ALIAS("HEAP_RAM", ram0); + +/* Generic rules inclusion.*/ +INCLUDE rules.ld + +SECTIONS +{ + .calsave (NOLOAD) : ALIGN(4) + { + *(.calsave) + } > CALDATA_FLASH +} diff --git a/adc.c b/adc.c index ca09d0f..7f2de1c 100644 --- a/adc.c +++ b/adc.c @@ -22,172 +22,15 @@ #include "hal.h" #include "nanovna.h" +#ifndef VNA_ADC_H +#define VNA_ADC_H -#define ADC_TR(low, high) (((uint32_t)(high) << 16U) | \ - (uint32_t)(low)) -#define ADC_SMPR_SMP_1P5 0U /**< @brief 14 cycles conversion time */ -#define ADC_SMPR_SMP_239P5 7U /**< @brief 252 cycles conversion time. */ -#define ADC_CFGR1_RES_12BIT (0U << 3U) - -// External Event Select for regular group -#define ADC_TIM1_TRGO 0 // 0b000 -#define ADC_TIM1_CC4 (ADC_CFGR1_EXTSEL_0) // 0b001 -#define ADC_TIM2_TRGO (ADC_CFGR1_EXTSEL_1) // 0b010 -#define ADC_TIM3_TRGO (ADC_CFGR1_EXTSEL_1|ADC_CFGR1_EXTSEL_0) // 0b011 -#define ADC_TIM15_TRGO (ADC_CFGR1_EXTSEL_2) // 0b100 - -#define VNA_ADC ADC1 - -void adc_init(void) -{ - rccEnableADC1(FALSE); - - /* Ensure flag states */ - VNA_ADC->IER = 0; - - /* Calibration procedure.*/ - ADC->CCR = 0; - if (VNA_ADC->CR & ADC_CR_ADEN) { - VNA_ADC->CR |= ~ADC_CR_ADDIS; /* Disable ADC */ - } - while (VNA_ADC->CR & ADC_CR_ADEN) - ; - VNA_ADC->CFGR1 &= ~ADC_CFGR1_DMAEN; - VNA_ADC->CR |= ADC_CR_ADCAL; - while (VNA_ADC->CR & ADC_CR_ADCAL) - ; - - if (VNA_ADC->ISR & ADC_ISR_ADRDY) { - VNA_ADC->ISR |= ADC_ISR_ADRDY; /* clear ADRDY */ - } - /* Enable ADC */ - VNA_ADC->CR |= ADC_CR_ADEN; - while (!(VNA_ADC->ISR & ADC_ISR_ADRDY)) - ; -} - -uint16_t adc_single_read(uint32_t chsel) -{ - /* ADC setup */ - VNA_ADC->ISR = VNA_ADC->ISR; - VNA_ADC->IER = 0; - VNA_ADC->TR = ADC_TR(0, 0); - VNA_ADC->SMPR = ADC_SMPR_SMP_239P5; - VNA_ADC->CFGR1 = ADC_CFGR1_RES_12BIT; - VNA_ADC->CHSELR = chsel; - - VNA_ADC->CR |= ADC_CR_ADSTART; // ADC conversion start - while (VNA_ADC->CR & ADC_CR_ADSTART) - ; - - return VNA_ADC->DR; -} - -int16_t adc_vbat_read(void) -{ -// Vbat measure averange count = 2^VBAT_AVERAGE -#define VBAT_AVERAGE 4 // Measure vbat every 5 second -#define VBAT_MEASURE_INTERVAL 50000 - - static int16_t vbat_raw = 0; - static systime_t vbat_time = -VBAT_MEASURE_INTERVAL-1; - systime_t _time = chVTGetSystemTimeX(); - if (_time - vbat_time < VBAT_MEASURE_INTERVAL) - goto return_cached; - vbat_time = _time; -// 13.9 Temperature sensor and internal reference voltage -// VREFINT_CAL calibrated on 3.3V, need get value in mV -#define ADC_FULL_SCALE 3300 -#define VREFINT_CAL (*((uint16_t*)0x1FFFF7BA)) - uint32_t vrefint = 0; - uint32_t vbat = 0; - - uint8_t restart_touch = 0; - if (VNA_ADC->CR & ADC_CR_ADSTART){ - adc_stop_analog_watchdog(); - restart_touch = 1; - } - ADC->CCR |= ADC_CCR_VREFEN | ADC_CCR_VBATEN; - for (uint16_t i = 0; i < 1<>=VBAT_AVERAGE; - vrefint>>=VBAT_AVERAGE; - ADC->CCR &= ~(ADC_CCR_VREFEN | ADC_CCR_VBATEN); - - if (restart_touch) - adc_start_analog_watchdog(); - - // vbat_raw = (3300 * 2 * vbat / 4095) * (VREFINT_CAL / vrefint) - // uint16_t vbat_raw = (ADC_FULL_SCALE * VREFINT_CAL * (float)vbat * 2 / (vrefint * ((1<<12)-1))); - // For speed divide not on 4095, divide on 4096, get little error, but no matter - vbat_raw = ((ADC_FULL_SCALE * 2 * vbat)>>12) * VREFINT_CAL / vrefint; -return_cached: - if (vbat_raw < 100) { - // maybe D2 is not installed - return -1; - } - return vbat_raw + config.vbat_offset; -} - -void adc_start_analog_watchdog(void) -{ - // ADC setup, if it is defined a callback for the analog watch dog then it is enabled. - VNA_ADC->ISR = VNA_ADC->ISR; - VNA_ADC->IER = ADC_IER_AWDIE; - VNA_ADC->TR = ADC_TR(0, TOUCH_THRESHOLD); - VNA_ADC->SMPR = ADC_SMPR_SMP_1P5; - VNA_ADC->CHSELR = ADC_TOUCH_Y; - - /* ADC configuration and start.*/ - VNA_ADC->CFGR1 = ADC_CFGR1_RES_12BIT | ADC_CFGR1_AWDEN - | ADC_CFGR1_EXTEN_0 // rising edge of external trigger - | ADC_TIM3_TRGO; // External trigger is timer TIM3 - /* ADC conversion start.*/ - VNA_ADC->CR |= ADC_CR_ADSTART; -} - -void adc_stop_analog_watchdog(void) -{ - if (VNA_ADC->CR & ADC_CR_ADEN) { - if (VNA_ADC->CR & ADC_CR_ADSTART) { - VNA_ADC->CR |= ADC_CR_ADSTP; - while (VNA_ADC->CR & ADC_CR_ADSTP) - ; - } - - /* VNA_ADC->CR |= ADC_CR_ADDIS; - while (VNA_ADC->CR & ADC_CR_ADDIS) - ;*/ - } -} - -static void adc_interrupt(void) -{ - uint32_t isr = VNA_ADC->ISR; - VNA_ADC->ISR = isr; - - if (isr & ADC_ISR_OVR) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - - } - if (isr & ADC_ISR_AWD) { - /* Analog watchdog error.*/ - handle_touch_interrupt(); - } -} - -OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) -{ - OSAL_IRQ_PROLOGUE(); - - adc_interrupt(); - - OSAL_IRQ_EPILOGUE(); -} +#define VBAT_MEASURE_INTERVAL S2ST(5) + +#ifdef NANOVNA_F303 +#include "NANOVNA_STM32_F303/adc.c" +#else +#include "NANOVNA_STM32_F072/adc.c" +#endif +#endif diff --git a/adc_F303.h b/adc_F303.h new file mode 100644 index 0000000..8fca2ac --- /dev/null +++ b/adc_F303.h @@ -0,0 +1,26 @@ + +// F303 related ADC defines + + +#define ADC_SMPR_SMP_247P5 6 /**< @brief 260 cycles conversion time. */ +#define ADC_SMPR_SMP_24P5 3 /**< @brief 37 cycles conversion time. */ + + +#define rccEnableWWDG(lp) rccEnableAPB1(RCC_APB1ENR_WWDGEN, lp) +#define ADC_CHSELR_CHSEL6 ADC_CHANNEL_IN3 +#define ADC_CHSELR_CHSEL7 ADC_CHANNEL_IN4 +#define ADC_SMPR_SMP_239P5 7U +#define ADC_SMPR_SMP_28P5 3U /**< @brief 41 cycles conversion time. */ +#define ADC_CFGR_RES_12BIT (0 << 3) +/* +msg_t adcConvert(ADCDriver *adcp, + const ADCConversionGroup *grpp, + adcsample_t *samples, + size_t depth); +*/ +#define ADC_CR1_AWDEN ((uint32_t)0x00800000) /*!< Analog watchdog enable on regular channels */ +//ADC_Common_TypeDef *adcc; +#define ADC_CHSELR_VREFINT ADC_CHANNEL_IN18 +#define ADC_CHSELR_VBAT ADC_CHANNEL_IN17 + + diff --git a/chconf.h b/chconf.h index d4f1b6b..16f1217 100644 --- a/chconf.h +++ b/chconf.h @@ -48,7 +48,7 @@ * @details Frequency of the system timer that drives the system ticks. This * setting also defines the system tick time unit. */ -#define CH_CFG_ST_FREQUENCY 10000 +#define CH_CFG_ST_FREQUENCY 100000 /** * @brief Time delta constant for the tick-less mode. diff --git a/chprintf.c b/chprintf.c index 49be7de..9c5c2ad 100644 --- a/chprintf.c +++ b/chprintf.c @@ -28,19 +28,36 @@ */ #include "hal.h" +#include "nanovna.h" #include "chprintf.h" -//#include "memstreams.h" #include // Enable [flags], support: // ' ' Prepends a space for positive signed-numeric types. positive = ' ', negative = '-'. This flag is ignored if the + flag exists. #define CHPRINTF_USE_SPACE_FLAG +//#define CHPRINTF_FREQUENCY_SIZE_64 +//#define CHPRINTF_USE_INT_64 // Force putting trailing zeros on float value #define CHPRINTF_FORCE_TRAILING_ZEROS #define MAX_FILLER 11 -#define FLOAT_PRECISION 9 +#define FLOAT_PRECISION 9 +#define FLOAT_PREFIX_PRECISION 3 + +#ifdef CHPRINTF_USE_INT_64 +typedef uint64_t pfreq_t; +#else +typedef uint32_t pfreq_t; +#endif + +#ifdef CHPRINTF_FREQUENCY_SIZE_64 +typedef uint64_t ulongval_t; +typedef int64_t longval_t; +#else +typedef uint32_t ulongval_t; +typedef int32_t longval_t; +#endif #pragma pack(push, 2) @@ -48,18 +65,18 @@ static const uint32_t pow10[FLOAT_PRECISION+1] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; // Prefixes for values bigger then 1000.0 -// 1 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24 -static char bigPrefix[] = {' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0}; +// 1 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24 +static const char bigPrefix[] = {' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0}; // Prefixes for values less then 1.0 -// 1e-3, 1e-6, 1e-9, 1e-12, 1e-15, 1e-18, 1e-21, 1e-24 -static char smallPrefix[] = {'m', 0x1d, 'n', 'p', 'f', 'a', 'z', 'y', 0}; +// 1e-3, 1e-6, 1e-9, 1e-12, 1e-15, 1e-18, 1e-21, 1e-24 +static const char smallPrefix[] = { 'm', S_MICRO, 'n', 'p', 'f', 'a', 'z', 'y', 0}; #pragma pack(pop) static char *long_to_string_with_divisor(char *p, - uint32_t num, + longval_t num, uint32_t radix, - uint32_t precision) { + int precision) { char *q = p + MAX_FILLER; char *b = q; // convert to string from end buffer to begin @@ -80,11 +97,10 @@ static char *long_to_string_with_divisor(char *p, // g.mmm kkk hhh #define MAX_FREQ_PRESCISION 13 #define FREQ_PSET 1 -#define FREQ_NO_SPACE 2 -#define FREQ_PREFIX_SPACE 4 +#define FREQ_PREFIX_SPACE 2 static char * -ulong_freq(char *p, uint32_t freq, uint32_t precision) +ulong_freq(char *p, pfreq_t freq, int precision) { uint8_t flag = FREQ_PSET; if (precision == 0) @@ -96,24 +112,24 @@ ulong_freq(char *p, uint32_t freq, uint32_t precision) // Prefix counter uint32_t s = 0; // Set format (every 3 digits add ' ' up to GHz) - uint32_t format = 0b00100100100; + uint32_t format = 0b100100100100100; do { -#if 0 - uint8_t c = freq % 10; +#ifdef ARM_MATH_CM4 + uint32_t c = freq % 10; freq/= 10; #else // Fast and compact division uint32_t on 10, using shifts, result: // c = freq % 10 // freq = freq / 10; - uint32_t c = freq; + pfreq_t c = freq; freq >>= 1; freq += freq >> 1; freq += freq >> 4; freq += freq >> 8; freq += freq >> 16; // freq = 858993459*freq/1073741824 = freq * // 0,799999999813735485076904296875 - freq >>= 3; // freq/=8; freq = freq * 0,09999999997671693563461303710938 - c -= freq * 10; // freq*10 = (freq*4+freq)*2 = ((freq<<2)+freq)<<1 + freq >>= 3; // freq/=8; freq = freq * 0,09999999997671693563461303710938 + c -= freq * 10; // freq*10 = (freq*4+freq)*2 = ((freq<<2)+freq)<<1 while (c >= 10) { freq++; c -= 10; @@ -131,14 +147,9 @@ ulong_freq(char *p, uint32_t freq, uint32_t precision) s = bigPrefix[s]; // Get string size - uint32_t i = (b - q); - // Limit string size, max size is - precision - if (precision && i > precision) { - i = precision; - flag |= FREQ_NO_SPACE; - } + int i = (b - q); // copy string - // Replace first ' ' by '.', remove ' ' if size too big + // Replace first ' ' by '.' do { char c = *q++; // replace first ' ' on '.' @@ -146,20 +157,21 @@ ulong_freq(char *p, uint32_t freq, uint32_t precision) if (flag & FREQ_PSET) { c = '.'; flag &= ~FREQ_PSET; - } else if (flag & FREQ_NO_SPACE) + } else if (!(flag & FREQ_PREFIX_SPACE)) c = *q++; } + if (!(flag & FREQ_PSET) && precision-- < 0) break; *p++ = c; } while (--i); // Put pref (amd space before it if need) - if (flag & FREQ_PREFIX_SPACE && s != ' ') + if ((flag & FREQ_PREFIX_SPACE) && s != ' ') *p++ = ' '; *p++ = s; return p; } #if CHPRINTF_USE_FLOAT -static char *ftoa(char *p, float num, uint32_t precision) { +static char *ftoa(char *p, float num, int precision) { // Check precision limit if (precision > FLOAT_PRECISION) precision = FLOAT_PRECISION; @@ -171,39 +183,47 @@ static char *ftoa(char *p, float num, uint32_t precision) { if (k>=multi){k-=multi;l++;} p = long_to_string_with_divisor(p, l, 10, 0); if (precision) { - *p++ = '.'; + *p++ = DIGIT_SEPARATOR; p=long_to_string_with_divisor(p, k, 10, precision); #ifndef CHPRINTF_FORCE_TRAILING_ZEROS // remove zeros at end while (p[-1]=='0') p--; - if (p[-1]=='.') p--; + if (p[-1]==DIGIT_SEPARATOR) p--; #endif } return p; } -static char *ftoaS(char *p, float num, uint32_t precision) { +static char *ftoaS(char *p, float num, int16_t precision) { char prefix=0; - char *ptr; - if (num > 1000.0){ - for (ptr = bigPrefix+1; *ptr && num > 1000.0; num/=1000, ptr++) + const char *ptr; + if (num >= 1000.0f){ + for (ptr = bigPrefix+1; *ptr && num >= 1000.0f; num/=1000.0f, ptr++) ; prefix = ptr[-1]; } else if (num < 1){ - for (ptr = smallPrefix; *ptr && num < 1.0; num*=1000, ptr++) + for (ptr = smallPrefix; *ptr && num < 1.0f; num*=1000.0f, ptr++) ; prefix = num > 1e-3 ? ptr[-1] : 0; } - else - precision++; // Add additional digit in place of prefix + if (prefix) + precision--; + // Auto set precision in place of value uint32_t l = num; - if (l < 10) - precision+=2; - else if (l < 100) - precision+=1; + if (l >= 100) + precision-=2; + else if (l >= 10) + precision-=1; + if (precision < 0) + precision=0; p=ftoa(p, num, precision); + // remove zeros at end + if (precision){ + while (p[-1]=='0') p--; + if (p[-1]==DIGIT_SEPARATOR) p--; + } if (prefix) *p++ = prefix; return p; @@ -216,15 +236,15 @@ static char *ftoaS(char *p, float num, uint32_t precision) { * with output on a @p BaseSequentialStream. * The general parameters format is: %[-][width|*][.precision|*][l|L]p. * The following parameter types (p) are supported: - * - x hexadecimal integer. - * - X hexadecimal long. - * - o octal integer. - * - O octal long. - * - d decimal signed integer. - * - D decimal signed long. - * - u decimal unsigned integer. - * - U decimal unsigned long. - * - c character. + * - x hexadecimal int32. + * - X hexadecimal int64. + * - o octal int32. + * - O octal int64. + * - d decimal signed int32. + * - D decimal signed int64. + * - u decimal unsigned int32. + * - U decimal unsigned int64. + * - c char. * - s string. * . * @@ -236,14 +256,15 @@ static char *ftoaS(char *p, float num, uint32_t precision) { * * @api */ -#define IS_LONG 1 -#define LEFT_ALIGN 2 -#define POSITIVE 4 -#define NEGATIVE 8 -#define PAD_ZERO 16 -#define PLUS_SPACE 32 -#define DEFAULT_PRESCISION 64 -#define COMPLEX 128 +#define IS_LONG 0x0001 +#define LEFT_ALIGN 0x0002 +#define POSITIVE 0x0004 +#define NEGATIVE 0x0008 +#define PAD_ZERO 0x0010 +#define PLUS_SPACE 0x0020 +#define DEFAULT_PRESCISION 0x0040 +#define COMPLEX 0x0080 +#define SHORT_FLOAT 0x0100 int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { char *p, *s, c, filler=' '; @@ -251,9 +272,9 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { int n = 0; uint32_t state; union { - uint32_t u; - int32_t l; - float f; + ulongval_t u; + longval_t l; + float f; }value; #if CHPRINTF_USE_FLOAT char tmpbuf[2*MAX_FILLER + 1]; @@ -289,12 +310,14 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { state|=POSITIVE; else if (*fmt == '0') state|=PAD_ZERO; -// else if (*fmt == 'j') -// state|=COMPLEX; + else if (*fmt == 'j') + state|=COMPLEX; #ifdef CHPRINTF_USE_SPACE_FLAG else if (*fmt == ' ') state|=PLUS_SPACE; #endif + else if (*fmt == 'b') + state|=SHORT_FLOAT; else break; fmt++; @@ -320,22 +343,22 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { else if (c == '*') c = va_arg(ap, int); else - break; + break; precision = precision * 10 + c; } } else state|=DEFAULT_PRESCISION; //Get [length] - /* +#ifdef CHPRINTF_FREQUENCY_SIZE_64 if (c == 'l' || c == 'L') { state|=IS_LONG; if (*fmt) c = *fmt++; } else if ((c >= 'A') && (c <= 'Z')) - state|=IS_LONG; - */ + state|=IS_LONG; +#endif // Parse type switch (c) { case 'c': @@ -345,7 +368,7 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { case 's': state&=~PAD_ZERO; if ((s = va_arg(ap, char *)) == 0) - s = "(null)"; + s = (char*)"(null)"; if (state&DEFAULT_PRESCISION) precision = 32767; for (p = s; *p && (--precision >= 0); p++) @@ -354,11 +377,13 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { case 'D': case 'd': case 'I': - case 'i':/* + case 'i': +#ifdef CHPRINTF_FREQUENCY_SIZE_64 if (state & IS_LONG) - value.l = va_arg(ap, long); - else*/ - value.l = va_arg(ap, uint32_t); + value.l = va_arg(ap, int64_t); + else +#endif + value.l = va_arg(ap, int32_t); if (value.l < 0) { state|=NEGATIVE; *p++ = '-'; @@ -370,18 +395,24 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { else if (state & PLUS_SPACE) *p++ = ' '; #endif -// if (state & COMPLEX) -// *p++ = 'j'; + if (state & COMPLEX) + *p++ = 'j'; p = long_to_string_with_divisor(p, value.l, 10, 0); break; case 'q': - value.u = va_arg(ap, uint32_t); - p=ulong_freq(p, value.u, precision); +#ifdef CHPRINTF_FREQUENCY_SIZE_64 + p=ulong_freq(p, va_arg(ap, uint64_t), precision); +#else + p=ulong_freq(p, va_arg(ap, uint32_t), precision); +#endif break; #if CHPRINTF_USE_FLOAT case 'F': case 'f': - value.f = va_arg(ap, double); + if (state & SHORT_FLOAT) + value.u = va_arg(ap, uint32_t); + else + value.f = va_arg(ap, double); if (value.f < 0) { state|=NEGATIVE; *p++ = '-'; @@ -393,13 +424,16 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { else if (state & PLUS_SPACE) *p++ = ' '; #endif -// if (state & COMPLEX) -// *p++ = 'j'; + if (state & COMPLEX) + *p++ = 'j'; if (value.f == INFINITY){ *p++ = 0x19; break; } - p = (c=='F') ? ftoaS(p, value.f, precision) : ftoa(p, value.f, state&DEFAULT_PRESCISION ? FLOAT_PRECISION : precision); + // Set default precision + if (state&DEFAULT_PRESCISION) + precision = (c=='F') ? FLOAT_PREFIX_PRECISION : FLOAT_PRECISION; + p = (c=='F') ? ftoaS(p, value.f, precision) : ftoa(p, value.f, precision); break; #endif case 'X': @@ -413,10 +447,12 @@ int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) { case 'O': case 'o': c = 8; -unsigned_common:/* +unsigned_common: +#ifdef CHPRINTF_FREQUENCY_SIZE_64 if (state & IS_LONG) - value.u = va_arg(ap, unsigned long); - else*/ + value.u = va_arg(ap, int64_t); + else +#endif value.u = va_arg(ap, uint32_t); p = long_to_string_with_divisor(p, value.u, c, 0); break; @@ -577,11 +613,6 @@ static msg_t put(void *ip, uint8_t b) { } static const struct printStreamVMT vmt = {NULL, NULL, put, NULL}; -void printObjectInit(printStream *ps, int size, uint8_t *buffer){ - ps->vmt = &vmt; - ps->buffer = buffer; - ps->size = size; -} // Simple print in buffer function int plot_printf(char *str, int size, const char *fmt, ...) { va_list ap; @@ -589,7 +620,9 @@ int plot_printf(char *str, int size, const char *fmt, ...) { int retval; if (size <= 0) return 0; // Init small memory stream for print - printObjectInit(&ps, size, (uint8_t *)str); + ps.vmt = &vmt; + ps.buffer = (uint8_t *)str; + ps.size = size; // Performing the print operation using the common code. va_start(ap, fmt); retval = chvprintf((BaseSequentialStream *)(void *)&ps, fmt, ap); diff --git a/doc/Schematic_nanovna_H_REV3_6.pdf b/doc/Schematic_nanovna_H_REV3_6.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8b34e886f98da181e0c2fde67a47059b0e9c839a GIT binary patch literal 230982 zcmdSC>#ih6jwbm3JVgMD0W_G+)bU$f28{+XPo`8km6CEZQ|hYiX%uQzDs?qfQbSdZ zo}OpgC)k(SeU$x3<3}<)Zf@qG0_KlW&J#}O%}5%JX7-=nz547~_k4W#Pyf&V`+xs` z55wkh^V9$JFAsnBcb|UzkAMAc^Xa?afB)SB{`>QHKmK*o;jix>K7Ie)Uw-?@A0Gkewp0K75&w3(NBsLw51&5$^y6Rg{$Dnu{{HX({^7eH z{~(`{=kmFSPv3s`wi?pyP5_AahV)>kg-4?*bUu2us)q3T(sSRP+MkNQ3t(U)EMX(avEgqbwZk;faPX%L`Qf444u$;uqwRPMg9(=RcGGT# ziI9YeAQ*E;OoTb6Y1_>(5t1+wlra(Jn1<~%!$e5JL{P>=m}Ba;J_=1vgd|J^WlV(q z@bCdH0}{rdfBEs@)6criVe|2i4+jB#e5;P2c!J^z9ydRK|ImpbHr?Z-|9F}|+^PyhUawZu zbsuC(SHG39tMYoiny&jG>%`X~`oZ*NfIwnhJ7tDUJl9H8Lnndjh_1ewE+TWf+EGo# zw`6$@&2$l&)76e@_bBo7NM2(zT}0+|wWB&bDv~U(shKV!bGq75b&rP=!{0U&=GdIF z6}bAtiRtg!DI+$gYz40Ipn)|jbKg!Gu{mXq>rCo@ErU0rG=IlzK-2IIW&^#(e9Q(u zoSRHs!RTXLauHzE^h_|~6BuMI82f?)nXsT7HJ9Ez+bijc@8*0(f9f-!t{{BeY<)uF zX{3QP6S_4Ex`1^&#cH4ytdq_WAxY(4r&t|fD~O$J!j(io&nL@Wu)*BhP=5LQAAkDh z%NNgnbqVVdLZ?Z6<8CC@_piUXNM^5OmO*?ey`J_uaCV-R4bLw*$QntGo)y3P{a?RZ zMPa{J-2}$|vG1Ne?w|FSV)7zFr*4M!!#{ub-CtKxI&D?fL238trHpM6iDs3(%kuS$ z55Il=>c!@p>G}A)yOb&|!aWv?58SVQ{{6rIxOw>>>m*Qeiop8M|MvZNKkg20@^hz$ zLHBv78HNAUP=>+Si;a6_*X;W~s4DJ=pyjAafn}eH z69;i7LRE2X%TXCONbg-%;_86PK$lSALsoV}#qBuO^ftfW$Bf@WR*J)vpE@~(*lP`6 zCerQ^8GTm$e*X1O_!hU-g%}!B`BDAEv^zi;@7R#9wkq`**|2Q06Hgb_;Td>)yZYkm@gM zJsdhU%avLcKkUZbjMT^iQVOk|P&ddf8KjBGKw<(0@$dL+4yopb195L2S7dZD;OntI zc7@jNF=T%*IXUcyhXA>Gb@8gh)_gyPczn!9Re=yo^)I$4BH&55Z@1OvR z5wUqDL$s`q=<`8!4wr~py`-0ae)!~@S5Z$p<9g;k{;;DaFsC(78?b#{oITix0zHAu zVz0v*9CC;&smloubvft;-B!P@W0AjzDHLIA3^Rs0KaVdEae(gf zt>p225QfipkbnL>UzbhGh!kJwDyl_h-p!>e$e`wu5!{f%31t{{_M)%U9QKm5>kPQS zF4^e`vRz5EK$$PG9Co%oMc8s=&XTW29d@=5_xM;~mylgC>l3*j>Eev89prv#wnihe zDhWejA8RxREYxnt&3@@X)b5(j7%K1d#s7fS3Hy+*5W;QGL#Y(U5>QlI~M<<}!}ap(ZBEb>MR9FmUWm zj|yF{&P>tOg$3}%xG;EJ0FZ&ffXk`dI$s_cfR3ur6>V`i=VSAg(9rcLR29A>89%3$ z1oQj~MQ#fMj}EJ+ptDJDMXCd_Ws9XhA%YYwY$CqP+7 z=wnRdZZ`wt6;XmE-mjQPJrB-)Q`h<0#WMI@1B;fJ(~lo>7eElzn^5)sP3czXNm zWzHoihiRNr-k+g7^3Ozh%0Mb>C?2yI1|;a(jFX^-f#N74Iy%VBF~04fY72F&sL7iE z4=AOImJbF(VnK+<<@mINsz1GF5h#X+gBpib5c7292 zvXsJ!E{_Ri%@d8TH@nOeHEa9S$rY!t4xg&p2Ca#2|fMH^P8W8qp;sIf8gvSI#2!B}}%l0t|anH5)X$U|i2Jce>#Slzb z8{siA6~h0ap75BP2H`$A4Z&#&hHFnwN4@&tA%g={|+0YAGKgvwZsmF`ra^&sJJoq=z%9<>555 zJe)}^32U%SR2{l{yw~W^Sdv;`tYXC ztPf}0-{I8#9nQEv!rBOrl752t>clY;C>2y2sglt6#`tWwrUDCRNpaO(S*dmoXUd|$mU(fYU6k$zK4GSaIP zl2HM-REyD(Ytu(0`{IqKUkmb)$46|D+HaB{`OSY>CS|T3W*_3P@F9=d+l)jw`H)B3 zhD2DMVUXnT*uSkMIeBX*IlQ%#oV>M@oT>n|!K;V2)tP6)OIQR&r2XY*0jiIaJ>1O6 zrwo?kWHSpXJtMucqF{FFpFaairOBpQf=;V92bAmDmT6I>GufaF$g$=yC-}U~Pv?F< zfzQaS!eAe$GH;!wOq+e=*&n}H7}aUYImLEZxN4d=(`j)cG;hKxNd~`%%|2q|$ESrA zt{^Y`p2H$6I>{+4I>{|z@qj%u{jf>XFiD@@zy9!g;XEveJVSUSB2IWpM4a#&Wx2Vn zS(nrn4%N(-5NqobrJWnlb3|4m&WK6@gOJ6D%3MvF&`DA0-y>u(qLSfk!!JWW;=c%4 zjK~-Lu7RgX3H45}m-S;b*9mDYN-iiHw(a-$XcFFPI_R!T*~G&<@w~IU;)?w{ z5d+KaB8e31u||9RV4a!U8=>_Ity_x-L;;PBGtea$b_MHTi*G}ctF<5y^uxnIh*!MX zzdAUiTK4Re9z#k5-2v;M`){;Y=^j{Z;VIK7Bc|bu zz^#Y)k^!9rc^k4vsLB8(XgR8q^bN6VL-uCNSSM&XsuJV}3=0N%%limgj>@=+T^no~ z8N3!zZStc<4f+-t!WO{^!bvCw8GS*M-unzBjKWZdnVhF?uF1icF9aS!H3P~Xs7UU{R* z=S1u_fTqzWUyMbO-!Igll^>2!74Z?Ym_bk@f|)S$rmS#aN(L=QRRlAEI!9F!Zi1Gh zGHz`CE@HRPKlzk?nXEXrhM4CNG*-@YM(aSzsE{~`tO#1l;E3XIdC#qVw3g?ga-K6< zSztm1TCh@CJlq7xazFT!FtwvUhG%$jmc7pF$#XJfgAp23D&5DI@TL{y@u6l zaf#N+N+bmR@Zb=yzD#uB4yl(Cza0EH*7f`_v4ciQJwd^b9DN`I7ZBqoAkF;rk(D`C z16?x91^VIXMTsARf}hTtzWC*U)bqoHIAzV&@fwnB#+sxw7*>KBq{OgH6p&JHStda! zH=Sg&r;zjfoJ2OvI>nhu@rI6{pc|8)6I3+3gbG%fv0swMDXQXP`zS5)=rV#9qp~7| z?6-9WOF}`=a#Y4mloyA6ak2f4`r@3y(GrRzb_Z38eYAw)2o=Rgr?AWzMoTCtV~HV_ zP!P0KR^w=b39*kj7OogW(Zq68#!ZNQw1nc8*spk_fmR-~IR~}-I9Fm|CFqu?nVSf9 z2b46!CVNnZgDeeQDnV>*I7MYiOyyJ(=fW5&&pA>1I|41I%F+|eg=}P^3EoiEnFvl( zEbrOUi^{50^9EmySvCeb@49q{=%5=lrV|au-dtzUW?0yotDxnwDh$dV70SwHSOhIb zWiza!9V^^RQ7Lw~DjCsMx>l(doq93*ywIAL!p9v5fgR5oV&)9`R9jnX4AvfaGHR`z$ z)=916N07 zecV3JIM4JITIbPNv09)_YciE1+t&@!T+&jNt?qn1TQB!0!a4{ySE>pfZFX z+k8O~zGIMah1{$#Mil8g1T~D~tx-=w3szfGBWMLyiWOLQoEzWB&901N(#qkLXe9`e zz902zMUd1J^ut~FnH97|N>p-tjcZG+;g=k%`=xp-XU!nuBeAZiUy)W8Op*i$YOy}% zkm1Ql>x$YGX=NIkXeFqD);;UCrf^{0ueSlm8a6No{Se=2w+bSq2?+JJqmiH%nV3)N zLx1}jNT@FnytgDWLA)y9qw`ltO7Oq^4p#jC<9#*+bJrmK-vLdKf>~go_ry5iFgI-d|+`LygD5WR641da|||MmQqkRT>L-99Pv zPe&3gn}U@ZPl5>o(qW(2B1|V2)4S(B<3#`ip&du7FiKL9Cq? z85|)q_d(dDVSP%O<+XAtf`yCj(Gpw2mLp?dLbyY)Y27B34iPU}k`*oyFw3%TM4&4c zeIj@DiZrWx7>td!BfLHW{qwZyrNTW}Noz_Yks!x@-puDp-&46x9KH@e3+XT;vRq3|fw=n9dZf&j&m5il9rVu3C=8&dEkBG`#mU2__(f zM2q}+rCj-tt2(@egKjvBD_GqtvZ^n>+>%~`a2lLJY>O9L z@gWzA1sP%JWJoweK1lq_ClG0EE=;?y0#vcR3@#c6)9kSw&d;Y=e(WJb=LFMok4(#p z!!RCgYB$-Y3UxQuPfeI8Z}~3CN0gDUD}0J0N9xYFa>7CE3E^?@h!bwYJ|f%|P)9fp z-0e1YTaN7Jt2^V$3CG0ygzz}y$_Wp1gPEAqN_kFSuLlV_Z~yL6MM+b8Hfp?UQ7 zBZTtepShVi3!AV?E0Lu4uo)Zn(o)WH8{AyZqc=?i&EfuB&atVe#z2O0Sjx;mmLEGe zSov0ZrbxKM!axRO4;rqLfedyM;*<>JTwMW(FYUk&T}L9!C^LB;?2tP|h_uvk;KHxR z1481{)d9{A7#+fS5iLQ85GS8r@gZB!Qu}AJXE2$r5ac`5T_(%)2{P`)Jw|3`=F`rFl*#5#wNH3>n(?SxppmT^>{7fxLs?11r{wKN+$&H%P{v6G zGGocYgs5p`KIoz2Je$`>4hJdIj)J$$+6jaDsj{52E%)L>IZXv;PD_PUj~A#sPGz)$ zIZ$}++a(|?K!6nu9-VJJ@>+WydVNXk*CfR@?HOj9{oe<39uUs)4I^Gga~jAle2YY9b@)Ztu-{Kj`@(rjYjutavr#dF6a5Uyur>NSoKYWPtMHyt-eHCADB(l&8oPiA6M8G$9U{eUhieha zK`?sON7b2%VUw^cHzyxldUL2uJd?LO@*E}Z;QL{BHPq`xEr|F>&U)#=vOzmChEv+H z#Yccgv>$m8(w;m>4ek5qok(6!9PmK1^t9HX9mV5Q+7ZQ0Xg~6+q&@l78rpH>815u{ z#A3Mx$-1*ogZfxDcS{D^Gt?gqQBt2gYy{A$|#7mE)-4eHVAdn$Y1kDqM*$PtkG zi)xWqtUorjXlQBL)YA-?of zPpU~uL~|`2P6sywNeM1RM})PJ9HV3Dsh&>qnPG7_9TkjGMM{tHVe-}j2j(R(3{f~ z$|hwg)mkSGTa`MZH$~xdcuyQHErTcM9U;51>~)IFyIxAcydW!ccz!^J<}%7)mQpp? z$H9qEw|JF+RUj*FSk}1Qx3K@kU=_&7QgKR!%~J8*-#ArdF0;w{>&7q=2US*uCd-vG zDmWLB7ePx|oUlYw-t&^s#G{KUD?$^GE{cNC#G{K0w3sThh%w4R6OS%xuLj9UKEkW; zY^FgC5QHsXRMQQeT=6sf4(sc%8pnF0I6F6*(U!C1gefE0L01@-p3T&8VYwfDm5-U5Lm$PeNpl#~_y^JU479zN)g^ zg0|*%tdU?bB6gX({cO$YSf;idk(rNM&(>Uyr+4RNbVaFKtC5Avr`$a&AmD}~7JMX< zCF1msI0F2uUhk=zJ(fenHhUZ?(>QtLF<1Lo_vsv#)_#}e4K{T=LS{2Z!Y*n5iafg! zMR>7IX|DGa!b+foNvccG4Z0V7D!fbJ^+6t>H3d;}m8&AwMYvBBz;mKs#DYyOG)E%d zH4_ffn`_R*TU%wIu68j<77KOmx;mbvcZ3Y-g4hgUm+bBgvf>DLc8r0F&yLe0Y&kOP zM%bkVb{?olSOqd;DGoS*%u05>^VRwWpIAm0n?=F!)>dC5dF96YGZ4?9WZ_Xn&NOQ& zV|0dG*cAd8AAOPgJX>?^ap!wR#dzo|$xBf&AfAbcbvf0RAu-D99up0;TviqotthXu zDkd;^oqi#MCgVQ*55?Ylg0m2nWg!`riJhRu3|b!2#~GqbLC2L2$iE(jjVkc-ZRVH>U;sh$`m6Mx2+pHZc zI*!URK!O%iWz$aF_#LQYsxhM`rHNAvaRf`N$F!#y?CHcJj~m{>k}rp$UC2Q$ z)${J;imrSE@?%W61LiJ0C<>5&-9gn~_g2;DA9GOq{J`5?6IQdoDPgU#B;>`oqe*Wc z)>X*GcQm;n#dP1(sl89q0Ww26%KglcQtnqnGu$99X^O>)#g{ZW%?MyV9iZ{2)e6#l zlm{El?RhYd(n;6!5u}Zh*TTvbL-$EeIw_!=tj&?I24@#|&233O>v-LjRS%888^I^= z-Y8!$kwcf(1V}GoYvjPA46+;uYr<<~JnBgB%AG*scl$N+C5|NDd*Y=mkLX>aH=~!b z%y{{&HS+Cp44ZxH65e^jW1|yB9k8U@__=YKvsM|ZdppmEDJN(C1h6yx?d0$IfR35Oh2w*?X29y zOMS!&4HP$#b*Axa5T|9ukg)kM57PoIyo!UMHF8VEF!Iwbw^LFrw_QPLG?k~(#)}Fg zuZ~{t_B2rpU5qf32G3vV@)pDK9sKzt1ZwGMHqf(jrIAu%UZ0)E`;tmxtyr0 zL;leh1NbFOpUTffEDeoIJFG=wKh*(tGY*@Ne}4Gnn^z}yl*_v8=HDNLh|S!4?#$v2 zQHtN0$q{r(V;50b-5295um_j50*w|bEAomj;eq-PZHXXDiS+8`E~n5oN7>y-N9P=S zihhKQ70;B9pX2!qGOKvT;r7Vx%H)4%unIQIYWxnVtA`%DLWHmiWF_?M;-vMcc%~EX zYo;C8;C0ySqT%T47w(Wi7X6KDSj@oUXMr#l^xD- zxRLjqQdN>~Y4kJ~yR)k-2wLi4cUB{j!(kj%Y4jp!IjZ9L4_1K@S;7BkR^~++ADlJI z+>(O!xXKigw_M}DOyA7?^|f4sr#W-WCrEQpD!46CBTGFQ6}y<$d0W}N2`*)SVH=n1 za!Pwe9aL%eyk_7CRVir@lpJl1F2&%$r7Q%rak=xduFv2J^6rZB#jKNIWZ?uAYqx!i zCImGy*$r?G$d2JI_s{oZOM09V&6F>;XP<+)E%oZm%Xnp=122vBD-&*Z)G%q+vBk;Q z^%VIE)}8g~B&_auy=TTD?fo9}Sw5(DkjykK$8e_r_x6IZUD;X4lUT-KM^98%8@>pwBf&4)P7 z6A!Yp^_Uwj@MiZKOgRMEz$gmycBLyAEb=g)d(2)J} z_c+l^-`Q&HD62|;`q%%uc{gnS^q0-IKmGXQci;Z?yFYCH`qSpa$1jHdv&UV(`T7H9 zQ{Df*`H!FeZS(E#f86}}r$7AZkN>gx>py?D`Qx8{{`0^8{^##DzyI+Md|m(W-T(O4 z-~afRKmDY?{fR#PU z1Vu0On$c~Rov(`^$UAv<*3Z$MKwOa3Vq>5%!Hd+a0i|6Pylx_z$VmB$fy)_XxC@g; zJMZqr!ZfLGUNjEnS{}*Xr4}PHN|Q@f67Rrz>71giwGVh-f;ioPQQN%X6+b%#rAh5M1H1Bd za+x<06Sjh>(M>qAYsl4f>q$F7E0%w_+K$aZUk0^}d(#hsW~HLpMP*3ojX zLKAeyJLw3zWD@gHFNfHR>F=YZ!a+9zMUsyLVkLbBp>85+2M`9`g&?#nK?8_R7Au%?anjM}b1@Tif4DP{T-K zKEP%UU>zAXeYJs*ozJlAt%gk_(VfD>2?HyY8<6Vn5XC;eLr})rU<(5d4c}rN6N^{7 zNvq_Q3p?aDiP}N#gwHcPw*~IT&ZxNI&9!zO)s@)ewh&&GnwJ|dBv*m=rgiv)obuf} zr25NpSg~eT@nr|OXy`Wg4ypeRt0frp`7h7a#7^K{jD82*7)mFo zEI=HgDk&*Jmq!07s**JB;>`XsVZ{E!%KJ+u+abS$@rikeJkf_Qsw8MRRpiEZ&rEjs z?e5xf{r%(S@%diYTh!uJ|JfOJ-}>%k zEfx8@vQjzw?$OVT|cL$zOZ|6+OcL$rOPuB8*ZO0DEKjW+ZvzG5B0s2Q=fbre@ zGk({9*7DtSzk`1lOC}~~2j!pfyZ*D5@7e(_M{>4dkma0z#_#&i8os-Y-DM$Eqy{s7 zkABAQ`p;Uv8<&;&cP0Ore>eZkz8gQQ`0k?({zKwd3KSY9oDoep1$6u~n_&CTD!vQm z$~V&ZmAh*6yD@$pbo4WO)ql49yIY&K!y&o<+ghyx4vBIOQ2Lp_>OWil-Of&_NR2SE z8$d;hxev8#km#22_xwi<-^^pfzTiW{sYpK}t^T8mZ&I%)@VS6HY7u7Syaw#(hefsN zhs(d+8{ONeughK=k+Bpwi;M%}rJQ(=KU)6Ht(oy?=W@bLZOw{Dte}-|Iwb$7Fd9Fq z|K^bD*!IoAmn8VjqaT%T#*ga1*@c<#3!R&ZL-LPIq5h*Y--Lp$q|33MRB~e#amizO zfFD{9Y_@y`;`M7THpW4C^|Y`BB`sL8-sUToTSC?(>gEZrC(uQ<(I^meUe@fZIppMI zXW2ib&7OCHP;{_fiI^fRonc+kvwu07AxB#b-gz^mL%d~Xw6%+!L)v+ck`AX|I|Idp zETnM$JM(D(>D$^&1Og@K7Re{5Y;tmhsu&7EOQ!)mhHD$A`Hi}o1z%~P<*3RGU~6fB zGFT=j1T9BJZaj93mY;0Rr(8mH#jvDDRDu9C2Ld_MBuKC})GDNIwcP`;H!i-Y-0{L@ zEfONM+2A7R6327v2voi>7RbJNOtHGh9ef>*{D_fl@H zYy+$g^6r7ox)bCql9CmjSNgT@z`=;5*@uC^X4)a z-X3|Tj>d$_{3)@w2#*TD6w#e)*BN35q8<@2uVG^LQ)ohaz;%ar&GrLHBxar5)-Bwk z7lxPvM?ow z2$io!Ve05-oEY(EXTA&Kui1+ooh_GfQM)#+Bj~L4-@s}?kSL(M-li`EwP3YbQWxC- zI89#FLr@D=dbhyZ(~+gs-gDh4bEn)B)niQU0_Lvkypm3kq53A4hXRw=1A5y`>99-u zo+2yW1~2-yx!!g;J8UsBJJ%5}7P}Mm+Ae2@Ek{-c7+!S$(tB^E!7d@YV%R5g7b1Nh z&<$N&!P~LDO-_a4h;f)laU2<6)Hgb4nc4$?^xXxl8kt$-n2}+qi(wIVNw{+x>ZCWj zyy!h^h+?H_ZL|{$mm|Zmy#-nE(@+oCENyEtDJX7lvCNF6IH3^8%5;4+m{4{t>nn3- z`Eb9%1`v7N2#K3#`rq!ab!7tcdhvU7KTm(tYy*t3Xx^Q{)uL%A}mI3S?x-y(eT=h88on<;YBP z+?Js-Bh1=omm{-gq9bHTKt+y(RUj*D4t5KKQq~vOL)da;mT?WpQd|L8ODU99x*j&K z4{OIBY7XN$IC+I4;Zn^}Ltw?52ny(iT{c2)Ah?ridd6PnpJOQw?SxV)QkH7m3H! zu$8yoQR24Bbws#LOfuooat~%q;>l2#6E0V0bbB1G?Z?G_c=fx*w$ZLqUG=E4jX2+a@Y1AJ`Sk0GtYTBT(!{OD(Vl|MTZ_HKQbnNQJD`H1Wj6?c-p zd;GKV-T2v=@0LM)Y2VGf04iU%cD!5@y`}p?2NE%qn#9k`c>?;c+;;f#r=R}%xNNME z9K2pQ7kUEd=O}eDg%tmU!>xS2g&!6=9Jpk>Iq3)6H&qy@Bm7`-R}L97|L-6VD+lhJ zicIgool|M(K;+}*i_(2BJ@gd3ek$5It@N=?(_+*a zW%v^W2Z;qQ9M$5G&Z-Anq!wsF+r)F34uAXL5R5@{vU~@l8BNxk4qw4taOl%1S)DxV zSMB-R2U2E$A0FU4^BZ)fKqy4`WBDT9FIm>s=i?42RAbtmnd0Y65Z@x55cj1A7tUoj zlO}w#HOC^PtK`%_{~3q6^Kn0HkQEFAWGK@U9C$7NwFl6OBe8aT)Or9O`sebN5+M&j ztUuHbfcBmIP2Up3bd36dAKKT--;9p(mJ_8IU-B@Bi&pGj95{C%TE~hS@r%!%KK=Yl zcZ%v=yUWAD5uwFyw+AAb_9-Ga-V>)W7rhC3wzddgLG8k)7K^iVa0xxen6^?WJu8?} z=UIk0M`SHi;aJI3a;uAlj4G8Jt-cMezWUntDlf-^aVwS@Yz?(+F~{H;vf|k34O$tS zz*yMfH?eo9nz1m#v6(Tmxy)EH!-i;qaC*S;ERU#P^jkG1?5*Ez_rLzt|J-~T`=si_ z28v#ToJfT;=M3?0u8h42b5xu#sQ)>w+R3pXB~MIk0KFAEt}lHec3s76Yr2j^B->%H z*wN?CPC;&AcA7PGmPNe8$dwJpbIjZi5lgbG#XyuNYs$+1TqweSU9g7?rMf2i*Tg|1 z!86HC9ydRKClL?1Lnpa|Ks0N(p(xkMkvLsZNu1TT3cuCM7|;|#$H3PgUjFXmZ{NLs z_VjG%o_+DG>wUylW5-Nn+-?q7TkWyu3<(*K6I-QFwNMR0b+%OU5R}SwIk8A}xlhuK z@0$Y(_7)gFy!_gkT#g1u&|))YIch?gd$}iXry90|TqPKJp&xpkx<{-cpEpG=r^#5u zf|jxN^yHprITqn;gn%?CiP~OF&79c+fQ&72mevH8(`1}so?x84`m$YTI>{|M+ck3* zlVIm;J_%lu7Ins1%raiRYu8yxBvfbHX3k<8qjP2;pD5RGhB)gwYv)WC=~QPg+I40N zvDq5a+HAEoq5?q9-nCm}q*yQ8br!4kuCug;Rn1w%131TarA1o&qTM;>^*M2%=wkBr z@rwBe2O4U-?i|~drscijLfV~UD*e$pras`sO6zfJ+<-V6Zog$}RoYOF+?AGQKI+LeC?iC`e)KGY>cY|pf4AF$G!%kMS?P<{7!uQq>G6y0 z=N@rXVil_!(n^KNH1D66$#mzLN8Mz4)F}s~7o1Pn^eUMZL2~|WS&txOLYi8Bfz4T1 z3W(>@r1b5BL$DBCs>1lSa!frOkuP9!9lHb}_?XO0my_xC#_Mu{t zLP)?Wg%D`D^fxVa?-A=X@L>DMTdKYJnH^oO|3yj5_bN|eM*Dg+HC*JW@uHCrdow9q z_QOz$pYGx`M=jH7PP_?&DAfUVlkHvmA*r6M0s3e&KvYj^%u{PW#(Q63|O@_~D{Cccox$XQ%raNoSCR~Q)!>+^81Gz)OX!VsOY z7DJrSGqXjE3b*j|Uz!ybPNBLg;(WzS#`#K4p*plt(r0tIn8l`8wq-DiLn#8oP zeUK#{BpqdGBiaMa-yiJYZ(GfK;8SNLi`jDo2+NiKZ{)I_B<1*7>Gxh8rVED6oLMvNpFb;sL2?NAD(=pNd=Rb+>8?VV=yK>4J@7+jCwG@tPin< zMKpeyv3_{<=D&aP#aAER{P+LdpidYrrgP|{a-#)4;%Q(twFyUToNpbf@vJz=BC+}v zSjDcVHdaTuOA`1Jt6!~PH8u4GtEsK8!r*AQ*luT9J?&UrVjWt&1dCX3v?u~RX!QMN z1)F&e=Gl$`+meA$=x^qwgC$I2;(@&jhfrf+sWw<14tNwN*XZyNv#Q^`qtq)b0Tq#M z+8j`hH3bHidgji|sSXBf(_DC+-!^IvRRZa$Gv)$IJ#%L~4X^HKV=g8dzirgqw8J8O zFy~n6nL9JZIJ)sn3lJ46FkOcY1-Q^&H#=DI=j}H*_VdM?C;WGO^Zr*a-_u;{>py}W zn%!b?hUFh$!Z+i;y?X$&P_6WDqg^7OxmAHjwmxIGM0D`<^;3mM?jVSE2@qHcQ`?q) z31=^wcwVKW0qfa=npnDbVK25cXU~?-#Q{2dEz9X-Z^9hZ$olG=H?QW2^b#|`+A!{Z zmXk{jkxw_O*_+bgYh;hG*U;Hx)ZLV@lXDnwP>r(AbpsM>?ybS63w)?}h%Lo; zB&?0Y2oP8{{Pe@?x38b(g`_3nw2VVp%OaEQaYClw^Eg3R80oqhqaJ ze`n(0xF!zD`3$$jx6dc1)Xbq&= zi4HLn9NLvAXHL$8AYpC%MWp}y-J7qFjZU8q9;EBJj8Gp~s!eq`6Y2?T<1#|wYcJT< zGZ%4iXpi&5nK(#T8*?d6eD%$T4=s6&7AuB`6ZJb7nTrl*!USP$>_wP((a4=oJD+DDZ{$-oO5=-ZXp6B~kiH zW!|oyy8*J1_Ij3dWhU6KD)S0=IGbY|%6t1&!vuodCEdC2UF1Z_N#{J1G$Syd|&T2$sG&2S$BlhS|(2Q0(X(V{OVmhcXFMrjo-fBtvGinZ=voc zgVl0}ao2593Hw*?+ATBHulxE&CFd?>EzVt20lUZ9Y3OB~B5V2L&C|;_FB*cLS?3fP zOIKfGat>3*;v5>RQh%7$9`>6liW9G}a|L;sJBH0*m z*ZFwhlWq@i=(UA<4kI(`>XU0%V#>^%MT3bF>o&2W$RWqIA?l>UKyOF76NY2;dJJ>f z>qSsRCdf$-)*~%joaH6SKTg&osMp<_dVXnu%3d}gE}Plp1M->zgdhTdhcprddZd6T z``HNcVvT%2>0ybHA6GmUVU0w?Q`k2)9zp97qx?8I*IjM3i{2BCkrU>mo3*TYe|WL& z`WLsI$?AJa+D{pdvJo6w0( zigrFk^}JAyB2~RdJ;p&fTIIM>5&X4cb=P!akZjJP`1` zjCGD8r4xPHxYmf3C{o}jsDV~S4x&g{k0g-QUXe}=y7OXfI#Kds#j+-y==FXEMbn&4 z^s&jyMR&SWZ*ij3%chHxZh^D#436rgx<~2(v`q*#boSp7{y3LuT6G>;Q z(ut(B=5!({jboFbs#=1j6G>^S(ut(C=5!)y%~@;FnvU-a=>!Y?ICf~cqh4e^9ao2QW#FUfi#B%F74*c|+?7~TbpP+^i&2dc>3F}~u+AFXUMGpM! zSSf)sSV{hg+AGqD(*CnHojB?PYMAI+&@FcutxG4^?~|lYkjQDVTeRxLQHM->Ix(i@ zv(|KioiLfRmbEiWCz8%qr4vbM&FKU+lm;R}RZ6pTA}MWEI+3*2oK7UIx!Y;c+O>2- zPAHRPbxk_KK2A}AWfRvp1`RwzL?)z%9 z6h%8?-=MF^eeFZCZqcj9&e*c( zyV?>s>>as&{xI%0f^)>b@+@^gewEMPK9Dp*egNe0=p7!OEME~i^0VL+9Hg{RR&Zq6 zC5Z|5TSdKF5ZrMvH>#bOwnDlndw)oGJh^IJ^gQ6fv?YZ&@ z?yc;`1%ntKg;iUvj>p66yLfP>VrKww#>|V)AkoKQQ6eQ?Z&BgD4*;p7W`` zErvrWDs$CY86`))l0^{I#F(7=QY_-bt7cP|`H*4p0I;+$=ZwW!FRroElU{BY<_4_4 z@36sJPV6a+VY{QRJ}&m50~XU@ENEOHn(FLmpx2MXdKiATr$_bN=8SG#PLXi8?uV2t z=8VQ@`6+gHRF}J4XHoWWof)Xg7^RsN7d=eDSvqNMF=M8(DHy9x6sfW)7^BJvs${Gf zWB6SdqgUl?Ig`ae(|mQy89C#XneZRvjG!jYG!(021ZTbhqnfjbmG%1>oi&S$nY9!w zs~dW-AGmwImkaMSycF;M@r$AV3>$@?UVaRRP@6d8*hnO|Sh2@xJYJ6Tkg!;IdK4DU zPDgi?XW;LiVlUxxM7Z0n9NmRh6ddNQTc3SeurNErjl{OL`WSr;IEQowtg=;%#24PTpa2L(f?_Si$Fy{_V6*mIt zu2RRc*gl=R)Hvv5nK-8I3vQ;bC(>a zbJylKjXUhq>&RV0TzEuW=yC03?9z+qQqP?=Nga8W)Hqckbn_~)(d*p{&!{TU$R=(% zS}n$%W2xsZy28R|w(Zihh!7#~U0m;2!(E985%5~}ATlDPfajLk81FP9?Bl?ecX1D5 zJ$Fcg&1d%2^BRwL`+S<0#XG`k16Lry23kU>e)l8xzFiJWEyqs$=E|diDS?6 zg6gQSKa;NzR?nUoC#HI#JtwS=J<(nyEJOW7h^h4cOu|A~J$s_PI9LD^g~p6Uw1=~R zslDn-Uc`kn84K@et7H$Jd^{Y@@?JK~@;YQZs4VAMUOjiLwd7eIC$WUpbBB%hd6xIm z=V!}Jlia0QUVTh=?vmeh?%HN~rn@xDd+GDD8t!(gyO`zmZUKw^igTCTrgPUe%QNnh z+kDwD%ae^2Vwo|^8&>Epc}?f8ZI*`(u{iTJ&+=Y2I8EUW^|3U|8&~KqIZfxTZI%b_ z1_(XP@?JLhOdR4Y7ZRme-sq#Z-ZGQVbne<_d9X~GTBliFTn1k=?sUUtn&pi?dh5AM zvpnanZI)-_F3s{@dLXXhu0({G<&8di>$yv_Jm;=$mIv+-5%MhWrAMlI?qZfVx_wu3 z#~GbW6v!Q6_3;kTJkRpt-t-#o)Muty-dJCMU_{`I?ufgl<2+>D1YKzRG|PM0GRw1& zu#Z_@!%=e1UYg~(^4ey3GB*c%X_oi0WtL|#ADT*Sst|)v%KD;OMP58ld9jSNX{?Jj95Wb7OaUmA*fwY8NQ_A{SX|?5WR28%p6aJw!D5;>2a9bJXR;VGXRx@& z*`&F%&%hTOYOt6l&%t8b?76U*!ag`$<7*=7w|i3tco~S~kFd&dh^DTCxWE{PI7#bw z4Od!7XP&y67>M;o$}|`BM9^nipQMHXE6q1eb5gU(>M$k(>TOf zfIusbu9MAWk9s{V+6{`M`1o$b4eJaX?HHr;`D^!Jb;A&BuTP5iPq%ptx#B_M_NLCXNan}5{=qURqLjl<6m8`Zx4=l$moGsw?=D9~7% z?aRH$*h=Nklk~}s$ZXIEEUQ7FQl3Nu<>}i8*))ljIsSD-+Ae=BZ}LetWPv~It}B1V z!2`REzo|Ra9Irm%xBRO8RW~{uw&l$udcS>mxqLs|XqEri$@cWC_n-f7H15SYgBXY~ z8o9gY9ZN&a2pWn#xSI;IYshJ3qc@IRKE%m5EjuU3SR{KsR;w9{NtZL$QsNtjLGD>T z#Ll9YJeG{bVauLlR5KPQ#W`awrP*QHOJ_=A?DRU{+k1fqj4GUgb_mA8S1;_>A7s0# zjOt*_JoU>RycFxk=xH_nzxk&bdjE{&Rb}GvkFP`}& z;tlu^EPyZai;usdyl$GOdSv(J*h%sn=ZJ2G*hPm4mVsj5V=LA;4h+Sag4Op|Rv#RP z0+ykWT`P7!rO~QIva(0BmR7QBC^W^r1Fgp>6f0?!WN9U>1%+azP|jh+=7N8phjIjW zJBC38isg_oHX4el;y7@Dz$&nV>3Mg+6B-+l=Bcf0hEkB!?7>~ek`L{UtxP{8c8AU; z`;eZ2fI=QWq*>+hhak9X9O;|@=9~;qhgX0km%nKu6cEv+(Lr=+kEw!c^5z4)3G!yS zZXBDGr2qWmI}YHwB=>+*Bh(q^S`c=!W|$m~4Si~wYd-_PoH*h^Ib_=bOmyQnwryVV z*lQ5XfD{+Mv0T@t5em?c8{pdqC(&mb62mmvHHbXxJHRT$#50p!hu}Q^GIjz|7&yr4 zg1l=S`d97w`auZaKOA5&b_2Z4WG?4uu)#EjtZaNp3nebyMV%e-@t6}|&ZEwMJk4JH z7a7$A1nRiU9^yJ5LVdvs3 zd5;To#i&&Km?iUB8jH8uULmtDoFE9w_j;^1pT2$ALwtrVo6xJQck1TYKI; z-=neD-Yn}B#9N-^w^&fbSNT_?(B91S$}G8o=W@Ee=>t65v$S-AXIaja8E4^HT|S>c zo9y)j&-`oQif@*uqG$Q7Jr~OD&9cI_H@xLZep|lEzZNdH_NG0l{WS7ydRF^QdPWOW z(lc81lAg_*Ry?Dorn@9Aj zo;_~`ZRf`|{@|0OJ)EBAC&^39K#Q_U6~%R|7BuWbEm*bZfSyMuN$zA*D7xt&XS5P) zcHJhF_aM7eXC*-VT&Ww&@PI$Ow5K*!9uF};n&Z){?%ju?3>S{ z?pBQ#N!b=Dtm4Hvy@K~2BDE8@bZ+SLh{AFf8YZUdDgV6L8UV{W;@Y=}$8d9)s;Y0_ zmP$7tzkS$ZVg*M>z!q}H2k}_mwnxJ*Ket{ORP*7c`SxR(YZJRY;^+2t|GWA-5L@0p zru}N2pX{P+D)a@r1|Y%cw#w|Hd|cR3?m^u=1(sad z;t9ujQZc8UkylpN_zdje1eBEJ4wHphu35Pg9O|ue=XG&Jb*n~rt4Ci=D z-TMcy@JowE=MJLqt$G_B#Qb^#S0%00mR9FVCL$~GT6y*eZ!gYrE#5QqB1EmnD=s0? zc03j+-xv#Ngs=0Pa<{8~I9z=7x}V%UTc{5gAF#WTx&`(bsnrX4TNKXbqy{XJszLBt z7KkZ&Q5ds80?IU9Za$MdL9Uj>OuT%r#96oTbA)5LDkzH|32u39`|chbcP`MV~) z{z!rXpalEdg&oIEcKz%8JBclCAJeW`;H@yLIfhTjPb@@+CoKs~Sg>%LKLg5{j)s|< zRiSReD_G($H-PAb<;~NF_fH6+``>-|)tk4kUu~Y=zVVf!_7A`^w&k?G4=tO!CR~XK zL+YdB+w;Z6ZVJpEWV+X53W<>;vol9>Gn=ubQw1jDyth6oD6E?-xge}*PKqlLbvdY) zwwp31y|XGt7}l=a^iYMd|G`_qmkV)3iA;pJN)tkg2yu-jMq9$8)I}5QSt4wmCU7v) zFRO{tZ>D7eZosAa<7iBHI6+j_;a5>Hqj%~MSAS7WbO-Ih)G^_W#}z^> zn&{%J@ODk`($=Op!7(!d8um4(krToF?S@;S?u49j@2JJ>tZgYo|NcM2gt1 z3Gaq(855ICREJHkEf>E`@`MkJn_fC*EP3)d)N)NQcUl!Em^-bB6OwFd?v&z$=T58Q zglGaMlk|Eq!!M-?`2gYsHQ}YTdQGf~6V$|-IN_Q|al$pRDo&Ut%4OBRpe7_vP!nE{ zt=7c4IANMt6(^_(#)LedAgnD;P!oEg%`d5mJhfv>VZA0+#R+O+&3wW&k>Ui$M7JtV zU_i;){OBRMPcJq`zTiVDW{Kka_Fn7>D~l)(CZ4;#fY8_`$-Ek0T~$MNdMVbZ+Tkqo zBTx%gw2LZM6u>ykAgu;!!8$`BPzUw7Km5{!YO*OJ2dxbFSf`ZPil6@#jYhELX&l}GW!r9(u-{$g2~}N zoMJwkPFX^Ua)F#!ov|C}x@>q>SU1oj6Pcq+bX(gcSy6P=lPSc1j4bIF?kDxkknEbh z83i4du?*e&h9k$cB^5+vV<_obRa*!}XXvoFU5c1$s|6n#Xj$0&@KD!+3l^ErD(9P$V z&<S@Vo+O*!F}1~x4q2_z zp)z<4bg^weT7%e-y_RhjB&`Z2mXJroQO z+nv>bPT)JLA+Wt4yac*bZmx#NSzHat-7iJwYDnm)hJ>z74QP@^)V=1eszyT(!VY#_ z&-&4ju)QC={<&0cR71b>B@wEDpc=nKHSpau8VITn;x-z3G-%va4e(8OR6}5UKX~!8 zS`D!|)74P3E7-DHS3^Q)qoGgennpuk7C!Da8szf%`>7#edtZP30!Kr-%${l>sAV+p z-832qY90+EX72YK4RY$m9n}!n-q&C9;cAGjo34hubMscUXEqwfgw93-LG5ZlgY^wH zTuqQVN#OR~W<>2`&u$}mdBHfWoo(O*)hK7HVfYb5dHTi{S%T_SQ!oJMx}hVxf}j}@ z+uuer@g-=+yJME7VARakihfBm1hr^}?+Q9AXA#t_8EHIU6LhE<6vzHXn(;3|Gu2Z? z*GybS%`vmhmr*ZF2RLT3ZBj5I`a3fPycU^QG+M{XwZp zR)MS2qvfzJq9v^QGwTN{IPnk>NX0T<#^p?;~*Re z$+f$>(a{&hbc*1rVqDbMHH6aX0ZV-8P?%ITuU+Qqkr>OB?^1*BY zVrA9U(6F*9#WmGxLPs?ubZu%VeT&x$DOU%Xy2hy!3mrneo{Rn-jAef+mkjecsT znlkN?<*tRQ;iNIC#z76L@pnTt4r*4-fL7^ys7BWF&B(6l0 zUs^K*T(4`UdQF$dpALEqoVqh=hM*SB@ZF>tf|@lm;82Advy!Wt8OzXbSWbf-QXCGPHjYbj&H7xM@yHOW#P;(|WqB#xDY%Cr5*ZPANDaPA^ zLNVC6c28$U6G$CHUG+rX6p~l2&E_rWvK@>Qv>bW~A>9snQl} z56I0N9co8JS2t>8W4hDR z9%xK8}brqnuUihpUf?8C=cav%eYF15GI_p=fX27a3uH-=+D9bC61V9S+Oq^w_C+ZFH zrBB{h7Xf-}kw{1PqtII1S!(B6JZ5wSDUSUYcV_Fz{6hUyvtU4<^t!2LCpAW#u6{rj zU_RHx?PzyxxWKnmHwS!@3uDz~03a!vIK=!gU}=K5R6Fr<%)BN zdGE~;J!)UtvIIZM$!B#{pzF;a@uE5SBi!z1O0x^uYD#I4!12q1kn>M|q~60B3h4#A z)B4%oh9vga+4|Xv-F1o#juQdWkiD<(2Dc8Cw7G6ikWD~q6v21BO$>?%So7w(J<5X^ zC`)$NU7gin-5tj(>rEcNeDft%3(y&!||Y!F<)0Bg@?*T66{%GhmI1 z(0@96TLuePYd~(K?6J=dtERR&ZiPZdartmy zKt^>`PyD$PGTk^NjqNh=C#XghN>X|kx|IzEb98lTC>X%!%_wqpI1G@Jk1kLhM~9hDV_oXKe)x(ivR`{fA6|6{pAE?&$td2|V( zPKeUl0P~32Un$Nc5Rc{E{IlGOv#{A<;^7C6uwX$6-A#UkFj%tB^m3A>gn0+yGYigG zP-*(frNNYyF!XDTd0y5yA!?DExb!L9Q%=TQrf!w-sWTwwbp@m8>z*1Itc5Gbk*vbG zGT09fCvtB2l%qxUmjd+98>|Jd-TJ5ZvpZfnB3H}{ZR*A1>P7Cf*{n}&D`4fA@+Nv| zpJ{|RULS}n=rx{?=(X8w3%xOV(}Ea~0p&IHrph#qma?;tD<0C! zKpDRx8zUy?M~wQ%O+b2x5C+NFlgKXyZhT0V?IAyHo%*xTXMkRAp2y=}uAUch%8*ps zk)P|M$pDwn$KPcz;*oFLqhi;e8vy-NI0i|W(6`mp0=quy&+Y5{yV1tCGwoHE(Cepg zD)IFBM3PHS~MWk<_zhu>#>RSWEGvf+}MT)nKE_ zi#!!bj5Rp8Ig)zTA~XhT2GdC279Iwi9py#K6nLgP1RlAg(fEGnHs2U{Z#-qTqw^Ih;&KP+1n0c4PF@a`*XK%CJ__zVg=}J)HZ$J+N4?(Un*%5yf zc=&O}Q~V4`G(3u$Y%uURqYOL*tP4E*dEgORQsA*`Fu{(tI@+Vj0HGuPuBwl>?Qs@( zFz~dS6Bdtjy*7hk%s$1PG48mf2H+ltLMNHDL!` z+|lV=fF8?qPQEA8IUe+hmkI#{2p%JpAVfpintA-1Z4x}l)*L0s0TK%yah9vTWDFM? z4UxgfiIl_z15eiQ?>nS1vf5$&K%+4bbGm?FPdFxZJ$jfhsZ+6vX5_hONdGliAiAbs zC@wq`1E2L@eS;9>8+cQsSultW^EdQa-yjwIg+^6;gBbX1->3<*!V)GySVQz->ju1h z?Dr^7VS)#e+=Dol#e=`Masy})hRV{bZ~7&$&a)YN2TT-g)O(a{;10`Yb1c=|u{%Os z8<``lZMx8lOY0$gZ%6s$<=d}cukdQ9=?sFJHo zu;|myqpgPeGO}vg>+ZJTtg61XoTD?xBt3X{qgD*j2FP5bZJ4p{FT#0@Q*N{k?MPya zFjEX?SM7T*v9A(?8&RnP9t&9KwhNa!ub7Kds(UU?)~8xT8Ky{F zY|OI_;IG>=lM!wmLs(b(%QioZ;N>p)qFlHk>@d0L%SJmnNTnF%4-X^S5jQg3JYL9doQ@u%>fjLI;upEHr+y&hFY+VH<5r^eWcNusGmqd9rR<>Oqs` zg+yL93a7CIh_f1VMr@U@b!6N>k;W2PMM^P4A}aWuXhhW?B9&4G4=siHYf))Wlw z+gD#jn1$)oEmFuC639<>$}HCW{aJHSk%g>KU{C^akqn={eUNs4Sz+K`wR2_%?)&6P}+MAhPS(m7rGKu_Tc!^%j#@OXR5LV5x=Uo?s z>_@!EMOHLqX5!DbIE#B^Er9R5eKH{Ms+b+gS9HkJWbX>*J&>NmDr9+0vU(`(vweOE;BwyV$4L9&I^z?=Ph$xSjj~q z3;FUY%r#xiNRr#P&_lKhzP^0At(Q5HqMcpv<;z}6`I;=y?}9IPY)yP&IxH*yY4CjY z^~Yw;V)S_Nv6{0OJwC%N=p8(z0=B^@Fl1vw-iq*v5^q*&gCWK z9)QPlS-<@D!6Cl#5fBta)YMDdKUjm9f7PCaZ6nQF$i#-UG-?nYwJFWXLPtE44u|+0 zIUt30IrAd(=3h1L+v2{PjjH8;MKf9)CAO$)Bkmuti^&RlEpcBDdEEnx7Z+m?&jaUC zMeM!DP@b}Sh;8()Yy(}7q2I&g3NNrT^0F-MlYER+dvRIZcba46-)T0`b(+gS^tew3 z5MzywhPY2=Fx@aD)p6hXiiKY1%gYQEaes#zCgOgni|E;n4RK#w5EwcHU)6D6W;*Km zf-iS$O?)Aa$`Vl;JS}mboW*=lBgCkZK^f z2}^iHkqm!BA$h{4qMdLl4@eVGdxx>&hw_Nz1ZjeFb(RO+#HSCk6$u~54;R#}ETP_E z?e!p^z?&db$VSeH?HWRMj|38F1!|QbTOM#;fIjSdB^yy3k02X=>vgHj$Oh-QO~c8S zM_?>u+e0DSekdQWBO4JK+mkZc8na0@HWEOSB>Ny!K#{hREt>&~w1p)^ZI?D4#w2Zd zbZOf|A=`dfPc|n1drA02PkWWN6eB=QF%o!#QF2-}5|n#z44jayMuN%_>Re_dNP|T5 z#Yn*U0S`11Jc^|iY1=~~+kPk?uah=BoO&s|389OTSPexO(Uh*j;;c|_uL?3)azZZw zs@g4#SU89M${r@jR~o|CyM+-8xrzx}7{pa11_XqQbB%!si;mE*Goaog7?71l(||Cm{(=nP z2@WF|kmt|p$Q$Bst+|G5d>U^Ga%1ujH&^DQU?6D)h5I}AId=SAIbogf`4_)FU-ri&Xv%K zd5Z@jsL6$FVFxg9k=NZ^$QE@N7`#Y;E~Gm5l`B`oh3Kw^;z9%j7b2D`6k~`&YoZ{y zkm;ZI-_##YJ8IY@P)P;}%2m&r7 z7?5YdTNo%$WT5=EP*D~CXqi)P)!W@&p&+ z4Cu3Kz@Cf&`)vyYXyXtUqOX?3V=Eg>g(x*4xfKL1L>cICX^pxNWk5`5D`&IFU)6-R zX!cWms0oQ|;6j1{JgYIP2IQ}JA_L{Og^D^2SSN+P3QZtuPzI$B+TAjxIkzEezlZBOn-%=fa+f!k-Q(+w!-|vtDNarN%?j0Ej|X-kApE3Fq0G z28037<{FS^h(0ucLBFA+WCcC>p@iC=Op35);C+8snj0@+UDN{O#nXJQwzafq0S;$lo?FfWc!fgbX14g$p4A=(2;T$pDIN>O#oCPUAiq zK%o#W#2JtgZRpOgVS)j4s&a}XDTC}}Q48Gvj6feTRvJHO7NgI@d zN*ChqS`!I^nuB29?vbQKj8#WnK|$?k^YM=ln9JeHp|=n6r$=q&mH#5A++eZ3>rgA| z&>P=jDr2jd-IpV_>HdQ^9aI-saI73YDo7E_G53%}q@wTG%8hYg6xPU;Lt&by)1k$c z?KJVj$JAw9p{h<7Q?+f@TOiJTr~+kbCrv0}rJ{pG`vR1z(&!#e0+3}~&2NKPimEA| z3`pseJQQf{F1?r2zB{Zi1xFbtB31OSnkDcq7PNf3Faw7C#La(USs-4vqugOx0rPd^ zYUPyk-9^FCE;-2LNu_GAA8?V3gNk7Tgn6I+>I|qTT7Fd~9>7n~A{viU{pjNla!rQk z#SmY0QEMY`wXd0ASCE9Xx0zS?_}S*&*B^exW|MkPLzD=ShVtpf`>?ROM#OG}ibM-j zBx=4NnK0~b;wc;i`L1SZ5yH${j?*ZvUTRljoX80X8c$E*ki!a(PT_$6)z|P+X~8A{ zct|5bvWXd)vh*epqyXVV5Gy_Mivo~hkkUgCNi;l#ePcr|`H}_&XOtf&pshoplZHpF zNy%`=^uPfy0L<9%i1Z4T4wOnjtUlsjdsG>q%p;{T`@1%%NA3F*(T@Zm{v#V0 z5kuu|9ZZ3}Jj&1Q>;88m*4{p%z487iI0qGSM~WO2bYCHIV=kI&p5Fe3QKtfp zF&C{i)ocI3T(sQ;bBp?JlCt2bOv>LZYZjvGkbZu9^YSs&7$Dx^6m&=XNzmQWf8rJw z9iwm-eiuW%R^kYS`lHX?Sy=LSG@epISV5JUT(o0@J9pC+c+BSD%}inSC+|CVk>`c( zJgIB(_0h-tbk$j8Z&MtH`WzIhQE(We*FCV*yTW{q0`nb%Rrz^!N9vuPg^%KiEG2#0 zeEc)+8Lzk1OQ?0;HDYNCODD&umy5xw3vyJqhdaV7pS*(@CT+k#W~r-+N^8AQpBIB9 zm2f#(432uS7`T9(#o(~Ji$Rves4~w`7LV<#Vkjwgu?9z7Ee4sY&>AQPChvE!20NxG zi@|f5m12+-4c3sw;HVdiK@vz!a+4SwmaL(A;yIykAZsruaM?l6-Q&NtcNI=36$?5n z7Qr9N#NcUn+4i@2TR!q8wIi<(O(=p2&wI?aP4yEx!!k%Tzf^{k6|GPQ3Uu;CIghJ= z&Z4_PB(Y-E%84|r*Y=g(!fJdP3Pf?2#x<-*^^r(Ko%J(B`ewc}lDtKv@tO5wptOId z+9XHBVpMYFyfZ7_tuo0`id0)QNjtIr$u3GgcMrpLmPhG95`)otu+B9{I|-Mdvo>@= zFT!cIKd70YomYjc^+FW;v=5FSLQjNP=~*IguueV=h0scAT*G>c^)p18l{OJc`*WA! z(#T_6epR!&Fd+v<`Dh<=%2g&gp>U|^;4{iK6i?gBSA<>H%QTs*(9;>!Bpsv`o(tN( zpqB|VF*V63D083Q-arj{nIO11R@<4h2CJheRxMtVAd|*fS}(DFhDclRUPC1HlLR`G zu@YHpl5)YPEKNlXr&|%X9h*e461VMY+;dKHE~l$p#@dHH6ZJ=cT@C{?Y` z(W-CNEcExt3$aFob$-teV;@^}vWXSv(y)@yJx!NNy{J-NowDHcr6RcQ5iKV)zyR8T zK`lz^ePQ(oa}hGuwyqfWkqJphiTba{RJY~bdk|`r+)osKgok%5`YU2Kyw}fxZJeH^ zkb=EN;Z5yOT$y*Z8CmPjnCs0FjR9(wNV;s=vc@cXED1^<)nlN0nwHlMl>CUuavow) zI(pOGEFIW>1r3f2EHfeynlL-?8s=kjvK>I~5$pREX(G{K!pFJvw7jNwY?OnScidCT zRl;JXdra9ILv>&+4iQQUtP6eQz?zOt85M^RFBAVOYR-n}IHKg@pmZ3`iI2VR!5l@@ zupG2PX5<%DJ97?!HSt?(eA^xNS)e}wak^ZEhAqoKw$~Q7ICxQ%o8@1zD_7ZY}pqE0i~lhsbQcnUGl~R)g$# z^v=k^5TOKD;4}$dr?b`y%UMYqx%JfSW}O+zA|Eu9@I6hwfOb*i@cqa@4|F=au-1jL3B=$Vzn7a z4Uu#GnAj5QoX2IXwKh4aNHMyx%ewfobv8L^vXJpTjh73G*L~xJL{42#T6-YKJ3r49 zrEg&^lAJPD1Jz(Pn`A+8itt)w3YU4Ud^{(T=uD}67uLf$*!q_cSi=_&DZSX%nW$H2D>J z6?SdzJ6HG&FLT3;*I_ky&5-=Q?lH|Ud0jVQi%f|Kc`-L%h7on>u`QhE8Ec&t`5odJ zuS;|VUYA}Hz*%~0?Ndto6OMpE`G+gjE6h_D3by5Vmy(=5jmx{Oj;%H9rpD_$ceb^A8_Ttw?1BT& z1T9kcu}wPGSpRmc23mxb&dp{yvNhKxp4=5AyRsLjzjJh{bBtfeJu;P^k|lv6;mdOw&tA_Q-yS9Ih{ze`6f@3Z9v@e@!%>c9Dd*A|zNfKuMaJufb~H4s z#zD3;(BKih#+k7eY#~?j9Xfv#v6iWO0u}EY`TC*I=C+QLJ2e zJ-TK&gy^ugIa-=YYiFeG5Jk?azgP<)(+)dJfB;y@RJi9 zF8pM|_cXSy+IR(4&bWh~FuDnnqd*tWIga(?b@z<5=%)&+84Gts!GIPz~0Z5lu0>rV$ae$cTuJt2POpIk_ryGn+KCCJMDllVq+I*4iW` zSk`V6)e0c>NXlToMH zSAs|eaCgChw_!a|FE7@Zo0cu)LgO5VYiu(RMzR*Lv=RGeqIztdfg=KLf~}A!nKkLD zrX04SB8eq2WnscO*!QteMTqlaaki~nXzSVy*1<~8nVi6DjuY!^Rw*~wBE`@2M7(l# z?;I~-i}0GW1EIxT$WgRJ=&*Fo$xVZrBb@Tl8}<=7Ub!)51fi)6AB47BC~;7FVxa*` z^r|K*;=0`9s(osHy~et348ezk_;BJFkDh!QTGw$7?1{2#gu%32VJdEEGSDI`I=0Th z>b3a5T2XW|QW6S-DHylWj$BgBvB-rbWpoZItrKMfuKxltxd|A+weQGs=?%PMT z25Et+O9n|$i$U7E$smbsg+UTUGDrut8>FKMK@yZ{tu{!l+-_u}-d#MTe!s`YKKGEV zYdd>uXY6AYW-ljWz@HP;VvvqC2d9K4ijek|a@tB+t>R7Ev+Z-k049_p(UCmMyY%WoIu}sU-6gIif)?gIeV1SVNAE zwZS5_;tLZ>7D-TxMe^Nbkp!)Fxf zvTt42*<1Z*i2Bdo>OTavSfpbOIXcz`i?oWz5c8TnK@Ia7e>WL8(dD^9Wg1BoF|Y9t zwby?FMM#n#%CuHnq=x>xS)^gh7TLO{v$rkpG(Yu`C2AAYVv&wDo>4YucJ5mts- zJMb)9Btb0}$#;`Q5|p)EX^}*cEYd+Md}N8*6q}~`2FkQnTcmC}!ZGuATK$&8)MVx! z_@4UKRh+#o*mSYG&_TAIwa9vuB0n}`Z+j>*R)T7+sz7E9j_ zsyUtRf+Q1G3bzL9+$NJ_qXihK$tDvz+GGv78P79{vQ4hQO0nIbwcaN6js9Ne4U|IF zCgFSPTUT!OScBKeOq@61pqi5bX8PINtSMtPPz_eMNkNbKP3!>J+X*EE)!F1JI@=0S zg>J_4j3Ti~3b%dl1MaImVf`GfN=ucW)Hiwzr2F{EGQ&yup8D368y8#bRUl|+-vHr~ z%vg(XtFX>(GIj&FP1dwZc#zJ2YctZ`S^p1}{TvPn#r@8lO!om18St+mN=SMCPKg5Sp`VJJ{<#`n~sXTIS~ZZEc8uvw zYeIEJmTERx4mK2Yp6K)bVS*MJ5z(;(pU~MJmnw8Ko^1)5C}5K=$(o*}8CHtz2Ce#5 zMLtQJ#PZ8s{3LBM@I7^{>o_hpyU`;{(ry!3r0xrnOjs%08mu%Tb8P9Z4!6m?z;-JE zBs$upy(>J^CTq~mc&1GztQ_zwuu^OZ>p5B*Z8Fuk*X_VDn@sqgy4H0Z+GHQqZ3kgW z-Wa>-Co@(PZeN3yMr1b0inb1%5-ABNsvApImki_}eY zW|Mq3*(5=W(4DtQqR5i0*;F~hO0gx@%}??}N^7G{-mPYCelp>E>RQ)vXp=0ECrPr5 z-l89qWX4L6mC+Yrr4eyb5;b$$x}QIYhA|?XDB5aIX2j0 zw}Wb`+sFDDZKvW9)>5C`)?hW8oXmnYT%OF*wXWmv&N?}{&HQ9<9pMBmazV%X39MzB1pNfoikXuc5pO}9 zA}l&6Z%A2~Px`x2^K(%9%Cw`1?%Mzr;WkOKSo&l`StMyAbjq|=+oa|%IA!f#HYxay zZL)P4hZlL${=jl)O@9Qn2+{_35@cYlH_6WWc#=svsKF%t-7rZ9Wi{Wj@Z(|VD8eKI z)NGQ0!Uwm5GOe{HdC;B{xs3BpHmL|ZAVfu)j57x|syKz0+4_Hl6+MI8^9Nsrz?VaI)Bg)(f)@+^$M@ZC#lIg8BP+h0Jr-2H> znSr)0>KyFCDp-3MD0<8O|Msr6N3Pq*e(zt=UxGMTNYjtouSF0*4=XV?yEY^xS-{AL z$*66QSDG*f7u1I=ZpU(!H>;T;3L?{<$bw<=?Vf%+b*O1qDNYN-P}syaKq_b^bc zJ&p{t((YrRMcf%^rS%Q|kp@~kIs~;cN8&h20b2&_t zr~>3l>TTz)t^bUWR=sN@LFAaTjvuDa)&!ohQs{Oe+&Lz4>?_p zJDpICi-9#m-|3brC*8v!8`rAF#`WV3a+lxF!SA*av67~o&t27JkVV^8cq&Og_&z#Y zNJ)gQU0hYUpTxDRNgCv#mo?4!BaT{bV`+52Ap47W$Spb{JmdjuM)r{R-S3Gl!RT(b zK2+mY_xL&K9@W^mRy8)RA8(NU(55rUO1g(Z7Hu=gO6nW==N#_Xmlca` z)#NW=kQ+Gd6@p0gqTP}~BsPBDym_$tYX0{6;=hi!<>5Zo);GA2Vzt8r3SY7+qI~@5 zrP-}etBd*f@%Y31-)Bo0-~7K3M!v^)_(A1EyS8zItBcZNmGpYH!L>jLsprnL(K_hi zuz$EkdV?8*UVws|k2a{_;gtPD?D2zU`4D%qpn^zKr3wZQyYjkhBOZz$(dY@113Y}n zQ80tL;Gw3}(*Tg8QcQzB-s|R4rTxY1#rrGqPPN{&Ks}#~K!t@};>_U2@b2My4QTyv z58FWXp>_D6ks<00uT$9gW&n^V1ts6eD;ST*;t40no|KpmcYG6yyed`L*vAE5Q3?{@ z$Fs*TXJ^lr^TYh*H?u>qd-=zlj4X0~g5|`t5ojE2cVL7)a`1ckYW7*V$`9v(50!R% z2!jf}!{aA2r1u%JK5RO)i_xm7l@xyV!$p!>*pHA%bqeLfc}OkXH*lB7N2}G=NlPsy zog6;UV9hvxGLOWqMeG+tJIRa3L&7ldK~s0B2KpNtoU_ts?tEKtzRpZx*@ia{_j^Ea zNqb;p%^6U-HbJmJ419Rw*w~yT0V1WLG{ndoegW9^!{rLw+@g%ZwDC;{d0nco;mM2A z@`>*^u;RNq4f$!poTsaxlVP|D=rrJ)FX%L+V*r%hN6^J=fPm1cuvvDTs7p#hIp)nH zui$;?WXk-oxA+o@ye!q&tc?L5PbH#7Lf>We;9VZn@ zX}A0g?Z6HNQ;#7xRAC2$N}-FokRg@L*IR&P7cynaTzsWal`1T&g_I@VgiZ&vDJ0Qq zBpRm@;QY`$2OPnJR?9_RtpG3^$l%57fSq~h<%9#P2%xA_9&?ZEPDryWYF0~ecdb?F z##>lX8=SDcWq#&4-?J~jnHy$LU*a)BsK+~%flI$9=mIAfveAsiIR;eKUX+6DBjP+l zVA<16NiSf&_7N`C*sK#%j^sVFPiMRZpTc8zc370eDJO4^8x~MJaOESomIkymi~|Dw!V zE4~Ru-c)PNN-T*)*~@#n(c;NDl+AZQp_5=N!G&ZSTn!e62!Wu}hR10IVNAodS7C_8 zrqo55koZPk!CBp+j85J73W~g0YGEazD0_L|qf>rG-RVTwlEwB=(4fiKP~Hq|->=(>g7OSSUlU$;TJd+Ne#HdX& zq>NMqJkrr{Q_?AJrpAG40=}asQbvjbwkByPhMTHR>7*RBCh!x@dEki3%caqph7n<* zF%Jy8_=sdKAD~GE!BsQtGL{}HX1+lwD3=Cc*9Fs!i;^}F%M!tt5b~;2W7FQ&Ab|l` zKJz^}Xj3O1-EBH;G(eGi98pD~DqB$LWGuBXrJx$w4EH+46U7~0LZwq-Q>P93decer zzQ-yTj6BvAX*ofW)`GD}eBFxTvEr7&h(K7&f-yzIvsR0mpkpCqS3*iy)QYssonUh3 z@K*TrT5CmG7S-HOH5!T_O>XgpUvUCXVuxR`g6a1l!Jn?avq`-59<<-^`c`3Y{3d8=GavHLPgt zc+3fdsQ5-+!RgtGGEKesCKP$o6jyKpp)qS?g^3h;T0s*YQDqH|jt$d$>~XmuqLLU9 zl6!hCK`0x0^BBC1<@!ks@u8C-dyj(IPNIak9bZDB6H7HVYa=3s)Jf1pL>!fl`sA%H z=v#)_IaZp5vgbq-K!hcsi&<11bP^oZ8k|f?hm z)BS11`sCFmp_5=--h$d*ak*-V$p@5)8v;WoLMWRHR)wY1rX-Ym#~8U{ZW*&p;8cQuqDQKddVh9Yo_sUhl*(=tJw^v+bSu@HPbTL~q$}en69oL4SHr9;l z#W(T_o4ukav#yG7LXnsKiFH>JiL#gXJ?*4fl6}O=a7wn~YTTM$V8zuqL2&9^Qxm+T z6~`B>(5WCtP{yWYMalLzU+Gk(3Y)dDrjIE3CVa#SZW`^hE}aZhr}z{Cl`fs&reS-f z(+cht{mEF3demuUNM#qCIu#|QHec)HQiUDqWcem^TCTW~zt4@i7=V><;2aYmXD*yZ z0!+PPr4Z_&tybJaPy)cTjiM$f?wLLDd(O&C%`=-(c5)XO*;#tSr+!5}lKr$H#ui_6 zRXvQBiaWthLF1*7sSQpj z=tb{;s6}uXG`by6AB9^HtmS#Gi^ck9cDC$ug3#-^8dunCpo1w1)xti5q-iI)O=~dL^p4pKK?)d zb!o?m)#Z(s2&NY3P_Y~aBfg?$I9S{rEmrBqt9ndraH0jv0eaS42@KGy2uAZIf`j`K z(RgWOYJraLOF_(5dY8`_zPJa8kySQgo2p=qzpX!75-5Ph*Po~0aiay;I)MHWjCzI zE9NHI51j)B#YjgS2&)LVzoiqh8387JRBbR;0RkgCC1`j_Eb5U0r(>@;z5$7NV;Gby zI0NPZr>r7ihJQf$akvA21Yzq09z6nPVd1lr6DbWTupJC%z+9B+i;HhU;OkO_m4u`m z$$MHSPonKx3~L&xaEG45lV3tH`K4hQn`KA6hu2l$Xs=Q*{hlc4Gt8Gz@MU)}mUcjg zV)@MXl*9BS+U~@#ru&*y(^+`(YhU82v9Z+2l!WX^?7is;Oi4FkzV;%XR2xg3Oetuh zhsC4vuo&J92HP{txP8xc)A#}eM#ncW@$!mnWY1O8!loAS-2mDtjvF+-SfnwW?PN+? zt@#p)yxC5?nWiM-Ou_O^h5CC==xU*$;OrH9P6f3Tz!#OVsF1xY%Fg~|ENe##1uDB1 z3JT6%QIxDJPC!?-=_*y&teq8)xRiVo6VN@K5?}m_b`s2JVvjGbBN|}rp_jx2K_`6S z5FXH2TAL{eIZ%)+OU4nVWRPLLjzL_iv9vZ*3YxIJwuqgXx>aosK((sP;R#@lU)m;) zKvm23g->19MNQCaipZ`6x@J>zyejSRJtoHxp>&EDfnbQnqR)YoDG62Cd&6`Xq?FX+TRN%K!uGW> zmiP2v<=w%9m#kVsx(tA+mdAMSOS6Vyh~Af0jNpA~Oh?epz^0<&p4pXg{Ok1M)!uD=)~gb7Sy5HcB+NekzHat(dRW(jnTyW}6qKpovD?vXb3o2D8Do+{a;a=%pVw)|Pum#-+e)DBch_ zcQYAGT}+k2o1-*wX~L!GY#a+Up~it~VB#w;UkN(xMOD%}hri;P6N7MARL2PiL{Mk} zfOMh;y&_R~E_D<&S2Kz`!Qjr3b76cc2VvC2?wXoCmbV9?`rMxnF5P%(WNL#`3c8s~K_^H=8;KOK5tV{AGULYz+9FHA zN*J4{6fpNv(4`x1jYMs5N=oD=HYsj2UpD91S%Aw@K`6fk#!!Cg+q zcnd3PgHsAB_ADnVST06Ut6U^ktlME?#>zfStZ3MjshSQcLsVhq|3oR@A-;qnZ>qI1 zHr3deQqYu6j9l6QX~B|=VV$LB$klYhJOiVGu{38NkQUU&ypd$0l+@x|I;m7+RXc2` zk-VozyE+-Z&M_a8wm1$7(_HZ!Fu<&|*x|%z2QVjX0wXr$8coBe1dUptA_`G^3e;SbwkJkHD!43sYP9qrRf{^&f-8DwefEmOFNQO8Uh%llh84)GOwr9!*ErCk zZkMoD_%>L0h_y#7vcb^BH}VQj#fmb`qWH=%t5jjLO5*EPOe0FZ37rreY_Irv&SwCg=eU^h%&znVSB+xZD3%>4lfDrtW1wZIwDTaD0)91Dh!+xA|HvmuhTQi76SxN#4^sX~!D7 zaydxAS8DZIn}GU+3ZX9>jYoYAqU-P^uoLuk7QmeP6zH|PmcVK^wLG!8d-<+PH$K(# z7GjBN?xz-m{HX>%FmE4|u)p}T4)!g7^g$>-`ba;pHkS5gNU7)mTX=%3j{nol8p(Ha&y=@(4{!iy5{%Ep!-1fkj0? z#-ZgA+MKD_>hCH;(jzKp8d{)em z+_;pS>y10c?&KI5iA#Y@m1>w{0+BHrgv*YWLsXrN?`aKMbNj5=Fl$yTm~^DP^#lug z8gsV{SOu%j^jUF1)hHCj%TGC4$808hqgE}Y(AK32%X$!{<(p_4?FrUN7Q^h%+D+7X z740?FVHWLS7M5k3QZ1sXgZ3~FI=jK>DsI_>PUdS*;8KlcZJ1Kf#FS+4)ZDJ164;{$ zo+R{lGXo@s6b9j4QB*Eg(H#ts1J(!N0-FYfjURl4z6Q8-0N>apyn z4KY6CgG76GsDjfFH#||RgOc${9JH%*l<^T9loPCZ0PcJV_g#<-reF>%rc6^SzB0rr zRai-wlqKJUA+9l5>vbwPC0ld(L{i7w+PzL|F5MJXWs9<}E1YI#1@B>j2uq7Hr7phG zsY(@A5{j~y_dV@|CJw*HZtvpwkKHDB}zk% zVGCxrpzX!Y4F?^w)0OcdfnGvKITlz3hQY~9fYpE{D{_KN ztWl@M0aTZ6c=mXGUlEt_j<~C5$qGR)e5waf)CQ*<1{)2IQxgx>iv6@I&~9DzYp@#5 z32K{0Ed}l63r|0-#zW~@U&WnZaL;bm@Y2ZC1}Cg?#Rd8{Gz-=M%N27EzFe+vLo?Ug z3rqiCNqiwA%-!}tm50)fLZlx z-D1L-74L`wPQ}L;t}!$Vi=N+63UZ_AGn_pe&6I2w^L3-SRAVI}ozO_$(~YJ8XXhgx zg7-Y)F>toH$GCu=KJapUK@m&kY_YH`+ms23Z{!tBmru$aqkr)g6nV3M6*lW?$0HtN zc~9%Kal6QQ80NC|M(xDe&xWfCb=%BF?If&PMwD(fb!=VJpgUtsN$WLV8@Ed}mUYFY zc+2PI@KY{-;LNnO6JfU#*96@X2xie-t(~|gXe@oJDG9Z9GR$@&rCWl0TRXAT!j4*a zB=6~V+A>^gb1uW|@cJT2b1nw*ZO+9w-g7RPe*|MXCSb^(!Z5qKDd`8ySN(MBQiV;O zbP3d0-qSke-M%4-d|<(;(@x8P)QLC3wt3`E%YedShTnAB8In!~r%pwgQWsz8RHX{r z>s0bh_&tsPWqX2UK^varmvwFU89~|JhF=jF%S|y;5-Qt^3)-Qvdb%iOUE*7|r&0^s z+um5-)9vJ2>KwisW?1O^l*Icv zK3iOH`f^jElfm}Pmk_ofw5w!nws>^GZhyyoPqmZA!mV{OOv!Ya(3Vo`3L`<7w({fV z!e-f9QxbC7y25Bn$xKP9&DT1)RAbYUic-)-f3gm~DyHD{<>rn~hWd2bgqlvqQVVpt znBrp>2sX_JJ!3h*aQbrVgihA;mG7xig{2ngylAJAZ_1Y=`a#)yk53VoBuvTB;jxk) zXIHcAyt`IV$hpfH*4Vu6cF!?4f}{aM#x^fB@}DlT!9sq9`nGX0A9N#-^iP_9nUbul);oKb@5d@RjI;eiP?YV|@5~ZXTUqWrJ zVXP$7ETxzCJ#mEew%>C*(R;bZ==WTogiyBHNm#XvCeBNajljd}STR zvUWr{lJ~Su8*UAk?HOimZy3X>Wj5T(u6EimhBcN^t86b0DYW)Szc-wl!9#c@vt{^)Z9HDUJ1V^>k?n< zG^kW#QzuhuerLX?`jalnsB-m#QH8yoBN_|Gm`Gyg(rI(zK!Q@^7XVSgL8xv6pV9miXpg+PU!sP!P@;f>+I<6d5b>UGf#xo39a*~yBU{;s&&9aibaOIT5l zWPhK6Ug-92&r-Vw-mn3z4GH8-9eu!`a7gVl`jsQ)=r|a*FVW6{w{1EO(xl2LRZ9xz z$eQXn7@qR~rJD~@H*L8J+qC5xEnuF&)aMz!wv|KL6$imyaRm>j$nsoYT(50qcs;jC zQ8}a?*K~n5JkdK<>_601Q!CDHhB^o-SjQQ8re)AUNFMGM6sr&!FB!}E)*wnj-b7v* z&K5Hz=cX(1B?P`I)!1z8K}RS@@}BO4<7>lkB9|NGx~!}&9LwJK|LmHxcDS?k0j?%7 z&iCO>oh>z3hgO}SbEyDH&Dh@DBfI(oXJtCL3Op-~(+Gpe?$V7{{}i>m>tuMWZSsxR z1(uaS->!}7cq}Enrnboi!i{K~yx~)Z4HpN16=;uH3H0qnO>bD-?JlcyqQz*i(O^hqCa&+NjF6y$r8Reb_8JD0P$dDvt# zb?RH(PflB<*?l5V0cPfEqYexG)&{uclhd>7y+POoD`;(@Z6YI3sxn?nd2~9R^rx+ zYX#HnOvz4XzV4GQRoJY$IN6NlzWAO4abfX2X|I~BdNIh8S1##zY7Fa&-pKDpqLWc4 zO~{SS(zLMeiJcdN3TAzaQoUS!y%^+Djm@Uf!oKA*-;+AgfTTBU(y(fq_a&&Ec%-AX z6Hj!wofr|hoebH18fI;ql67Uit}C7-iFMwnNlT;_{(yGU35%iT43$8wbS>(jIe~6X z6^LG@OH~^_%N#Uk&;_jGp4o+`ghfs9i@UX}(v8nH8+ec#Wec1d8MF+=*T~}DsY;-N z107cErbP|%^)hU9x`PdLc@0kmxYm#C0^N3%Kn07M3KVxMSfv|Jjd-%TiCPLCuU@^q zy*Zx0nc-j8f5Y>P^Tj;={`R+LFMs^uI6wR9{M+N(+1bBczIr?V8n1xQpU?j~J9~2d z?&@~Foc->1CfpR^?Ck0B?ZpxJ2YBEHdC!0N!}0Rl?{4S2)oyn7;`aFF&-2Ah{GVN4 z-A4YK%O7s9Z}7O^>+^5lVuKa`0_O4K>%Y&xez4gb<`343HR^NN#wSb*$PoDdIDZ2f z|9rf8b$)d|JNx4H{PpFCf~nUa!wQ$Ty(Ef*$id%8$HJM$qJM6&2;mbos#tPky2zqO#-PeM`7v zmzxz83c)ewJ|snG6qKfBb48P(RHO$rs~7*kSI{Y5a5(HCQV@nns2UrAi}T;Ca?lo| zHmZ00O`8b2 z&@XL8HW7Bw|3cn`VHaT={)O`KJnA;uJybdI!(vfZ+$Nr8?S5es{cpPrwIN^F#GPN- zTBLRI$G^iaXm88EHb1s2@6dawS^0zc*~`l}$F~ojU%xrOf;XcFK`PwGa#ju@I{C;W zz+ZJ9VK(vzopaiE#g`k!(U0DZ;vl!(=>FsU=ytn-ABEvzDviNuvDl9M=%bs<^Vbtz zblVAfC_!BZlU_7g-}RxP?5FzB6&`4f-#RP>-YGrQ{xHWORz!mEuXxxte@NTFnlmrb zOlO*(`&;?JBlzSG<>9O{Oc;g5Z?}SL#4ll(j~OiuwJK;}hy(kWljiN^um zJ=20e_*sbfJA@;G0HIt7k23u4EK?PNmFf7FIAw^x`<_O5k`L&M4*w1rLvH_YixOOA z+;XG}h&-|J_<#5dc}I0%ogt^|XSd$r;Z@ZQ8ZYuk8tzcQ1&2r<4w)9|$t(PI&u2l&MncW(E)&U2xZQ&RRWzL%{@kG>ubYj*aR`PX=Y z8s-q@AE$bjh)m&8A#jZcPri5>;bRO7h-Cr2`0*_w-!HDdxkhk3JNxqH)$s6MIq0l(6j&i@^9ZCFK&Zw ztb9yDI7OMAL5^4NE{-?z&%S>fl3+FC;R1E{1aq3(%j>JB=eNiCXHWkX%gW2eVY$T( zbGu>tFN^)J7mHv2rc-V?bX=qM=CB{kCa%9lAu$D-ht)vvFMl{*J-P_qo(^GPzx~7f z>fP(tP&M-Y@%+c@cc_mquZ~ZyU%z{E)s^%2na)aD4P`8V*Rnz~jFvcZKI*(N1^L zws4Pk(h!vYgccKkyZP1&Je_-o7V|o|*)c!dc)aZD;P&SH^7Zj1JI;&C|2aneojt$4 z#uPN#4b2k$YL0G5;OYG9`ES=xcl$@r9&aBVo(;pZ!{_Vm_Vdr5J$ #include "nanovna.h" -#ifdef ENABLED_DUMP -int16_t samp_buf[SAMPLE_LEN]; -int16_t ref_buf[SAMPLE_LEN]; -#endif - #ifdef USE_VARIABLE_OFFSET static int16_t sincos_tbl[AUDIO_SAMPLES_COUNT][2]; void generate_DSP_Table(int offset){ @@ -38,9 +32,9 @@ void generate_DSP_Table(int offset){ float w = step/2; for (int i=0; i>4; - samp_c+= ((int32_t)(smp * cos))>>4; - ref_s += ((int32_t)(ref * sin))>>4; - ref_c += ((int32_t)(ref * cos))>>4; + int32_t sin = ((int16_t *)sincos_tbl)[i+0]; + int32_t cos = ((int16_t *)sincos_tbl)[i+1]; + samp_s+= (smp * sin)/16; + samp_c+= (smp * cos)/16; + ref_s += (ref * sin)/16; + ref_c += (ref * cos)/16; i+=2; }while (i < length); acc_samp_s += samp_s; @@ -227,7 +217,7 @@ calculate_gamma(float gamma[2]) measure_t rs = acc_ref_s; measure_t rc = acc_ref_c; measure_t rr = rs * rs + rc * rc; - //rr = sqrtf(rr) * 1e8; + //rr = vna_sqrtf(rr) * 1e8; measure_t ss = acc_samp_s; measure_t sc = acc_samp_c; gamma[0] = (sc * rc + ss * rs) / rr; diff --git a/dsp.h b/dsp.h index a257652..68c851f 100644 --- a/dsp.h +++ b/dsp.h @@ -26,7 +26,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __smlabb(int32_t x, int32_t y, int32_t acc) { register int32_t r; - __ASM volatile ("smlabb %[r], %[x], %[y], %[a]" + __asm__ ("smlabb %[r], %[x], %[y], %[a]" : [r] "=r" (r) : [x] "r" (x), [y] "r" (y), [a] "r" (acc) : ); return r; } @@ -37,7 +37,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __smlabb(int32_t x, int32 __attribute__((always_inline)) __STATIC_INLINE int32_t __smlabt(int32_t x, int32_t y, int32_t acc) { register int32_t r; - __ASM volatile ("smlabt %[r], %[x], %[y], %[a]" + __asm__ ("smlabt %[r], %[x], %[y], %[a]" : [r] "=r" (r) : [x] "r" (x), [y] "r" (y), [a] "r" (acc) : ); return r; } @@ -48,7 +48,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __smlabt(int32_t x, int32 __attribute__((always_inline)) __STATIC_INLINE int32_t __smlatb(int32_t x, int32_t y, int32_t acc) { register int32_t r; - __ASM volatile ("smlatb %[r], %[x], %[y], %[a]" + __asm__ ("smlatb %[r], %[x], %[y], %[a]" : [r] "=r" (r) : [x] "r" (x), [y] "r" (y), [a] "r" (acc) : ); return r; } @@ -59,7 +59,7 @@ __attribute__((always_inline)) __STATIC_INLINE int32_t __smlatb(int32_t x, int32 __attribute__((always_inline)) __STATIC_INLINE int32_t __smlatt(int32_t x, int32_t y, int32_t acc) { register int32_t r; - __ASM volatile ("smlatt %[r], %[x], %[y], %[a]" + __asm__ ("smlatt %[r], %[x], %[y], %[a]" : [r] "=r" (r) : [x] "r" (x), [y] "r" (y), [a] "r" (acc) : ); return r; } @@ -74,7 +74,7 @@ __attribute__((always_inline)) __STATIC_INLINE int64_t __smlalbb(int64_t acc, in int64_t i_rep; } r; r.i_rep = acc; - __ASM volatile ("smlalbb %[r_lo], %[r_hi], %[x], %[y]" + __asm__ ("smlalbb %[r_lo], %[r_hi], %[x], %[y]" : [r_lo] "+r" (r.s_rep.lo), [r_hi] "+r" (r.s_rep.hi) : [x] "r" (x), [y] "r" (y) : ); return r.i_rep; @@ -90,7 +90,7 @@ __attribute__((always_inline)) __STATIC_INLINE int64_t __smlalbt(int64_t acc, in int64_t i_rep; } r; r.i_rep = acc; - __ASM volatile ("smlalbt %[r_lo], %[r_hi], %[x], %[y]" + __asm__ ("smlalbt %[r_lo], %[r_hi], %[x], %[y]" : [r_lo] "+r" (r.s_rep.lo), [r_hi] "+r" (r.s_rep.hi) : [x] "r" (x), [y] "r" (y) : ); return r.i_rep; @@ -106,7 +106,7 @@ __attribute__((always_inline)) __STATIC_INLINE int64_t __smlaltb(int64_t acc, in int64_t i_rep; } r; r.i_rep = acc; - __ASM volatile ("smlaltb %[r_lo], %[r_hi], %[x], %[y]" + __asm__ ("smlaltb %[r_lo], %[r_hi], %[x], %[y]" : [r_lo] "+r" (r.s_rep.lo), [r_hi] "+r" (r.s_rep.hi) : [x] "r" (x), [y] "r" (y) : ); return r.i_rep; @@ -115,14 +115,14 @@ __attribute__((always_inline)) __STATIC_INLINE int64_t __smlaltb(int64_t acc, in // __smlaltt inserts a SMLALTT instruction. __smlaltt returns the equivalent of // int64_t res = x[1] * y[1] + acc // where [0] is the lower 16 bits and [1] is the upper 16 bits. -static inline int64_t __smlaltt(int64_t acc, int32_t x, int32_t y) +__attribute__((always_inline)) __STATIC_INLINE int64_t __smlaltt(int64_t acc, int32_t x, int32_t y) { register union { struct { uint32_t lo; uint32_t hi; } s_rep; int64_t i_rep; } r; r.i_rep = acc; - __ASM volatile ("smlaltt %[r_lo], %[r_hi], %[x], %[y]" + __asm__ ("smlaltt %[r_lo], %[r_hi], %[x], %[y]" : [r_lo] "+r" (r.s_rep.lo), [r_hi] "+r" (r.s_rep.hi) : [x] "r" (x), [y] "r" (y) : ); return r.i_rep; diff --git a/flash.c b/flash.c index 7f744df..f5173c9 100644 --- a/flash.c +++ b/flash.c @@ -127,7 +127,7 @@ caldata_save(uint32_t id) return -1; // Apply magic word and calculate checksum - current_props.magic = CONFIG_MAGIC; + current_props.magic = PROPS_MAGIC; current_props.checksum = checksum(¤t_props, sizeof current_props - sizeof current_props.checksum); // write to flash @@ -138,25 +138,28 @@ caldata_save(uint32_t id) return 0; } -static properties_t *get_properties(uint32_t id){ +const properties_t * +get_properties(uint32_t id) +{ if (id >= SAVEAREA_MAX) return NULL; // point to saved area on the flash memory properties_t *src = (properties_t*)(SAVE_PROP_CONFIG_ADDR + id * SAVE_PROP_CONFIG_SIZE); // Check crc cache mask (made it only 1 time) - if (checksum_ok&(1<magic != CONFIG_MAGIC || checksum(src, sizeof *src - sizeof src->checksum) != src->checksum) + if (src->magic != PROPS_MAGIC || checksum(src, sizeof *src - sizeof src->checksum) != src->checksum) return NULL; - checksum_ok|=1<adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN_0; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = ADC_CR_ADVREGEN_0; +#endif + osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 10)); +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR = 0; /* RM 16.3.6.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = ADC_CR_ADVREGEN; +#endif + osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 20)); +#endif +} + +/** + * @brief Disables the ADC voltage regulator. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_vreg_off(ADCDriver *adcp) { + +#if defined(STM32F3XX) + adcp->adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN_1; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = 0; + adcp->adcs->CR = ADC_CR_ADVREGEN_1; +#endif +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_DEEPPWD; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = 0; + adcp->adcs->CR = ADC_CR_DEEPPWD; +#endif +#endif +} + +/** + * @brief Enables the ADC analog circuit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_analog_on(ADCDriver *adcp) { + +#if defined(STM32F3XX) + adcp->adcm->CR |= ADC_CR_ADEN; + while ((adcp->adcm->ISR & ADC_ISR_ADRD) == 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADEN; + while ((adcp->adcs->ISR & ADC_ISR_ADRD) == 0) + ; +#endif +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR |= ADC_CR_ADEN; + while ((adcp->adcm->ISR & ADC_ISR_ADRDY) == 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADEN; + while ((adcp->adcs->ISR & ADC_ISR_ADRDY) == 0) + ; +#endif +#endif +} + +/** + * @brief Disables the ADC analog circuit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_analog_off(ADCDriver *adcp) { + + adcp->adcm->CR |= ADC_CR_ADDIS; + while ((adcp->adcm->CR & ADC_CR_ADDIS) != 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADDIS; + while ((adcp->adcs->CR & ADC_CR_ADDIS) != 0) + ; +#endif +} + +/** + * @brief Calibrates and ADC unit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_calibrate(ADCDriver *adcp) { + +#if defined(STM32F3XX) + osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN_0, "invalid register state"); + adcp->adcm->CR |= ADC_CR_ADCAL; + while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) + ; +#if STM32_ADC_DUAL_MODE + osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN_0, "invalid register state"); + adcp->adcs->CR |= ADC_CR_ADCAL; + while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) + ; +#endif +#endif + +#if defined(STM32L4XX) + osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN, "invalid register state"); + adcp->adcm->CR |= ADC_CR_ADCAL; + while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) + ; +#if STM32_ADC_DUAL_MODE + osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN, "invalid register state"); + adcp->adcs->CR |= ADC_CR_ADCAL; + while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) + ; +#endif +#endif +} + +/** + * @brief Stops an ongoing conversion, if any. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_stop_adc(ADCDriver *adcp) { + + if (adcp->adcm->CR & ADC_CR_ADSTART) { + adcp->adcm->CR |= ADC_CR_ADSTP; + while (adcp->adcm->CR & ADC_CR_ADSTP) + ; + } +} + +/** + * @brief ADC DMA ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void adc_lld_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) { + + /* DMA errors handling.*/ + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA, this could help only if the DMA tries to access an unmapped + address space or violates alignment rules.*/ + _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); + } + else { + /* It is possible that the conversion group has already be reset by the + ADC error handler, in this case this interrupt is spurious.*/ + if (adcp->grpp != NULL) { + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _adc_isr_half_code(adcp); + } + } + } +} + +/** + * @brief ADC ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] isr content of the ISR register + */ +static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) { + + /* It could be a spurious interrupt caused by overflows after DMA disabling, + just ignore it in this case.*/ + if (adcp->grpp != NULL) { + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((isr & ADC_ISR_OVR) && + (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); + } + if (isr & ADC_ISR_AWD1) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD1); + } + if (isr & ADC_ISR_AWD2) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD2); + } + if (isr & ADC_ISR_AWD3) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD3); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) +/** + * @brief ADC1/ADC2 interrupt handler. + * + * @isr + */ +__attribute__((weak)) OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + +#if STM32_ADC_DUAL_MODE + isr = ADC1->ISR; + isr |= ADC2->ISR; + ADC1->ISR = isr; + ADC2->ISR = isr; + + adc_lld_serve_interrupt(&ADCD1, isr); +#else /* !STM32_ADC_DUAL_MODE */ +#if STM32_ADC_USE_ADC1 + isr = ADC1->ISR; + ADC1->ISR = isr; + + adc_lld_serve_interrupt(&ADCD1, isr); +#endif +#if STM32_ADC_USE_ADC2 + isr = ADC2->ISR; + ADC2->ISR = isr; + + adc_lld_serve_interrupt(&ADCD2, isr); +#endif +#endif /* !STM32_ADC_DUAL_MODE */ + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) +/** + * @brief ADC3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC3_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC3->ISR; + ADC3->ISR = isr; + + adc_lld_serve_interrupt(&ADCD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#if STM32_ADC_DUAL_MODE +/** + * @brief ADC4 interrupt handler (as ADC3 slave). + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC4->ISR; + ADC4->ISR = isr; + + adc_lld_serve_interrupt(&ADCD3, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_DUAL_MODE */ +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 || defined(__DOXYGEN__) +/** + * @brief ADC4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC4->ISR; + ADC4->ISR = isr; + + adc_lld_serve_interrupt(&ADCD4, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_USE_ADC4 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + + clkmask = 0; + +#if STM32_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); +#if defined(ADC1_2_COMMON) + ADCD1.adcc = ADC1_2_COMMON; +#elif defined(ADC123_COMMON) + ADCD1.adcc = ADC123_COMMON; +#else + ADCD1.adcc = ADC1_COMMON; +#endif + ADCD1.adcm = ADC1; +#if STM32_ADC_DUAL_MODE + ADCD1.adcs = ADC2; +#endif + ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); + ADCD1.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + /* Driver initialization.*/ + adcObjectInit(&ADCD2); +#if defined(ADC1_2_COMMON) + ADCD2.adcc = ADC1_2_COMMON; +#elif defined(ADC123_COMMON) + ADCD2.adcc = ADC123_COMMON; +#endif + ADCD2.adcm = ADC2; + ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); + ADCD2.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + /* Driver initialization.*/ + adcObjectInit(&ADCD3); +#if defined(ADC3_4_COMMON) + ADCD3.adcc = ADC3_4_COMMON; +#elif defined(ADC123_COMMON) + ADCD1.adcc = ADC123_COMMON; +#else + ADCD3.adcc = ADC3_COMMON; +#endif + ADCD3.adcm = ADC3; +#if STM32_ADC_DUAL_MODE + ADCD3.adcs = ADC4; +#endif + ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); + ADCD3.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 + /* Driver initialization.*/ + adcObjectInit(&ADCD4); + ADCD4.adcc = ADC3_4_COMMON; + ADCD4.adcm = ADC4; + ADCD4.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC4_DMA_STREAM); + ADCD4.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC4 */ + + /* IRQs setup.*/ +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 + nvicEnableVector(STM32_ADC1_NUMBER, STM32_ADC_ADC12_IRQ_PRIORITY); +#endif +#if STM32_ADC_USE_ADC3 + nvicEnableVector(STM32_ADC3_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#if STM32_ADC_DUAL_MODE + nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#endif +#endif +#if STM32_ADC_USE_ADC4 + nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#endif + + /* ADC units pre-initializations.*/ +#if defined(STM32F3XX) +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 + rccEnableADC12(FALSE); + rccResetADC12(); + ADC1_2_COMMON->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC12(FALSE); +#endif +#if STM32_ADC_USE_ADC3 || STM32_ADC_USE_ADC4 + rccEnableADC34(FALSE); + rccResetADC34(); + ADC3_4_COMMON->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC34(FALSE); +#endif +#endif + +#if defined(STM32L4XX) + rccEnableADC123(FALSE); + rccResetADC123(); + ADC123_COMMON->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC123(FALSE); +#endif +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* Handling the default configuration.*/ + if (adcp->config == NULL) { + adcp->config = &default_config; + } + + /* If in stopped state then enables the ADC and DMA clocks.*/ + if (adcp->state == ADC_STOP) { +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 0); +#if defined(STM32F3XX) + rccEnableADC12(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + if (&ADCD2 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC2_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 1); +#if defined(STM32F3XX) + rccEnableADC12(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + if (&ADCD3 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC3_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 2); +#if defined(STM32F3XX) + rccEnableADC34(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 + if (&ADCD4 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC4_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 3); +#if defined(STM32F3XX) + rccEnableADC34(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC4 */ + + /* Setting DMA peripheral-side pointer.*/ +#if STM32_ADC_DUAL_MODE + dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcc->CDR); +#else + dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcm->DR); +#endif + + /* Differential channels setting.*/ +#if STM32_ADC_DUAL_MODE + adcp->adcm->DIFSEL = adcp->config->difsel; + adcp->adcs->DIFSEL = adcp->config->difsel; +#else + adcp->adcm->DIFSEL = adcp->config->difsel; +#endif + + /* Master ADC calibration.*/ + adc_lld_vreg_on(adcp); + adc_lld_calibrate(adcp); + + /* Master ADC enabled here in order to reduce conversions latencies.*/ + adc_lld_analog_on(adcp); + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock and analog part.*/ + if (adcp->state == ADC_READY) { + + /* Releasing the associated DMA channel.*/ + dmaStreamRelease(adcp->dmastp); + + /* Stopping the ongoing conversion, if any.*/ + adc_lld_stop_adc(adcp); + + /* Disabling ADC analog circuit and regulator.*/ + adc_lld_analog_off(adcp); + adc_lld_vreg_off(adcp); + +#if defined(STM32L4XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; +#endif + +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 0); + } +#endif + +#if STM32_ADC_USE_ADC2 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 1); + } +#endif + +#if STM32_ADC_USE_ADC3 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 2); + } +#endif + +#if STM32_ADC_USE_ADC4 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 3); + } +#endif + +#if defined(STM32F3XX) + if ((clkmask & 0x3) == 0) { + rccDisableADC12(FALSE); + } + if ((clkmask & 0xC) == 0) { + rccDisableADC34(FALSE); + } +#endif + +#if defined(STM32L4XX) + if ((clkmask & 0x7) == 0) { + rccDisableADC123(FALSE); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + uint32_t dmamode, cfgr; + const ADCConversionGroup *grpp = adcp->grpp; +#if STM32_ADC_DUAL_MODE + uint32_t ccr = grpp->ccr & ~(ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK); +#endif + + osalDbgAssert(!STM32_ADC_DUAL_MODE || ((grpp->num_channels & 1) == 0), + "odd number of channels in dual mode"); + + /* Calculating control registers values.*/ + dmamode = adcp->dmamode; + cfgr = grpp->cfgr | ADC_CFGR_DMAEN; + if (grpp->circular) { + dmamode |= STM32_DMA_CR_CIRC; +#if STM32_ADC_DUAL_MODE + ccr |= ADC_CCR_DMACFG_CIRCULAR; +#else + cfgr |= ADC_CFGR_DMACFG_CIRCULAR; +#endif + if (adcp->depth > 1) { + /* If circular buffer depth > 1, then the half transfer interrupt + is enabled in order to allow streaming processing.*/ + dmamode |= STM32_DMA_CR_HTIE; + } + } + + /* DMA setup.*/ + dmaStreamSetMemory0(adcp->dmastp, adcp->samples); +#if STM32_ADC_DUAL_MODE + dmaStreamSetTransactionSize(adcp->dmastp, ((uint32_t)grpp->num_channels/2) * + (uint32_t)adcp->depth); +#else + dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * + (uint32_t)adcp->depth); +#endif + dmaStreamSetMode(adcp->dmastp, dmamode); + dmaStreamEnable(adcp->dmastp); + + /* ADC setup, if it is defined a callback for the analog watch dog then it + is enabled.*/ + adcp->adcm->ISR = adcp->adcm->ISR; + adcp->adcm->IER = ADC_IER_OVR | ADC_IER_AWD1; + adcp->adcm->TR1 = grpp->tr1; +#if STM32_ADC_DUAL_MODE + + /* Configuring the CCR register with the user-specified settings + in the conversion group configuration structure, static settings are + preserved.*/ + adcp->adcc->CCR = (adcp->adcc->CCR & + (ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK)) | ccr; + + adcp->adcm->SMPR1 = grpp->smpr[0]; + adcp->adcm->SMPR2 = grpp->smpr[1]; + adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); + adcp->adcm->SQR2 = grpp->sqr[1]; + adcp->adcm->SQR3 = grpp->sqr[2]; + adcp->adcm->SQR4 = grpp->sqr[3]; + adcp->adcs->SMPR1 = grpp->ssmpr[0]; + adcp->adcs->SMPR2 = grpp->ssmpr[1]; + adcp->adcs->SQR1 = grpp->ssqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); + adcp->adcs->SQR2 = grpp->ssqr[1]; + adcp->adcs->SQR3 = grpp->ssqr[2]; + adcp->adcs->SQR4 = grpp->ssqr[3]; + +#else /* !STM32_ADC_DUAL_MODE */ + adcp->adcm->SMPR1 = grpp->smpr[0]; + adcp->adcm->SMPR2 = grpp->smpr[1]; + adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels); + adcp->adcm->SQR2 = grpp->sqr[1]; + adcp->adcm->SQR3 = grpp->sqr[2]; + adcp->adcm->SQR4 = grpp->sqr[3]; +#endif /* !STM32_ADC_DUAL_MODE */ + + /* ADC configuration.*/ + adcp->adcm->CFGR = cfgr; + + /* Starting conversion.*/ + adcp->adcm->CR |= ADC_CR_ADSTART; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + dmaStreamDisable(adcp->dmastp); + adc_lld_stop_adc(adcp); +} + +/** + * @brief Enables the VREFEN bit. + * @details The VREFEN bit is required in order to sample the VREF channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableVREF(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC12_CCR_VREFEN; +} + +/** + * @brief Disables the VREFEN bit. + * @details The VREFEN bit is required in order to sample the VREF channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableVREF(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC12_CCR_VREFEN; +} + +/** + * @brief Enables the TSEN bit. + * @details The TSEN bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableTS(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC_CCR_TSEN; +} + +/** + * @brief Disables the TSEN bit. + * @details The TSEN bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableTS(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC_CCR_TSEN; +} + +/** + * @brief Enables the VBATEN bit. + * @details The VBATEN bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableVBAT(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC12_CCR_VBATEN; +} + +/** + * @brief Disables the VBATEN bit. + * @details The VBATEN bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableVBAT(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC12_CCR_VBATEN; +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/halconf.h b/halconf.h index 7473c22..1df73a0 100644 --- a/halconf.h +++ b/halconf.h @@ -41,8 +41,12 @@ * @brief Enables the ADC subsystem. */ #if !defined(HAL_USE_ADC) || defined(__DOXYGEN__) +#if defined(NANOVNA_F303) +#define HAL_USE_ADC TRUE +#else #define HAL_USE_ADC FALSE #endif +#endif /** * @brief Enables the CAN subsystem. @@ -146,7 +150,7 @@ * @brief Enables the SPI subsystem. */ #if !defined(HAL_USE_SPI) || defined(__DOXYGEN__) -#define HAL_USE_SPI TRUE +#define HAL_USE_SPI FALSE #endif /** @@ -179,8 +183,12 @@ * @note Disabling this option saves both code and data space. */ #if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__) +#if defined(NANOVNA_F303) +#define ADC_USE_WAIT TRUE +#else #define ADC_USE_WAIT FALSE #endif +#endif /** * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs. @@ -297,10 +305,12 @@ * @note The default is 64 bytes for both the transmission and receive * buffers. */ -#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 64 +#if !defined(SERIAL_RX_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_RX_BUFFERS_SIZE 64 +#endif +#if !defined(SERIAL_TX_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_TX_BUFFERS_SIZE 64 #endif - /*===========================================================================*/ /* SERIAL_USB driver related setting. */ /*===========================================================================*/ @@ -312,18 +322,25 @@ * @note The default is 64 bytes for both the transmission and receive * buffers. */ -#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_USB_BUFFERS_SIZE 64 +#if !defined(SERIAL_USB_RX_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_USB_RX_BUFFERS_SIZE 64 +#endif +#if !defined(SERIAL_USB_TX_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define SERIAL_USB_TX_BUFFERS_SIZE 64 #endif - /** * @brief Serial over USB number of buffers. * @note The default is 2 buffers. */ -#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__) -#define SERIAL_USB_BUFFERS_NUMBER 2 +#if !defined(SERIAL_RX_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__) +#define SERIAL_USB_RX_BUFFERS_NUMBER 1 +#endif +#if !defined(SERIAL_TX_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__) +#define SERIAL_USB_TX_BUFFERS_NUMBER 2 #endif +//#define STM32_USB_USE_FAST_COPY TRUE +//#define STM32_USB_USE_ISOCHRONOUS TRUE /*===========================================================================*/ /* SPI driver related settings. */ /*===========================================================================*/ diff --git a/ili9341.c b/ili9341.c index 30f9c48..2c9a979 100644 --- a/ili9341.c +++ b/ili9341.c @@ -21,11 +21,8 @@ #include "ch.h" #include "hal.h" #include "nanovna.h" - +#include "chprintf.h" #include "spi.h" -// Allow enable DMA for read display data -//#define __USE_DISPLAY_DMA_RX__ -//#undef __USE_DISPLAY_DMA__ // Pin macros for LCD #define LCD_CS_LOW palClearPad(GPIOB, GPIOB_LCD_CS) @@ -35,122 +32,83 @@ #define LCD_DC_CMD palClearPad(GPIOB, GPIOB_LCD_CD) #define LCD_DC_DATA palSetPad(GPIOB, GPIOB_LCD_CD) +// SPI bus for LCD #define LCD_SPI SPI1 + +// Custom display definition +#ifdef LCD_DRIVER_ILI9341 // Set SPI bus speed for LCD #define LCD_SPI_SPEED SPI_BR_DIV2 -//Not define if need use some as Tx speed +// 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__ +#endif +// Disable DMA rx on disabled DMA tx +#ifndef __USE_DISPLAY_DMA__ +#undef __USE_DISPLAY_DMA_RX__ +#endif + +// LCD display buffer pixel_t spi_buffer[SPI_BUFFER_SIZE]; // Default foreground & background colors pixel_t foreground_color = 0; pixel_t background_color = 0; -// Display width and height definition -#define ILI9341_WIDTH 320 -#define ILI9341_HEIGHT 240 +//***************************************************** +// SPI functions, settings and data +//***************************************************** +// SPI transmit byte to SPI (no wait complete transmit) +inline void spi_TxByte(uint8_t data) { + SPI_WRITE_8BIT(LCD_SPI, data); +} -// 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 +// Transmit word to SPI bus (if SPI in 8 bit mode LSB send first!!!!!) +inline void spi_TxWord(uint16_t data) { + SPI_WRITE_16BIT(LCD_SPI, data); +} -// -// 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 +// Transmit buffer to SPI bus (len should be > 0) +void spi_TxBuffer(const uint8_t *buffer, uint16_t len) { + do { + while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)); + SPI_WRITE_8BIT(LCD_SPI, *buffer++); + }while(--len); +} -#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) +// Receive byte from SPI bus +static uint8_t spi_RxByte(void) { + // Start RX clock (by sending data) + SPI_WRITE_8BIT(LCD_SPI, 0xFF); + while (SPI_RX_IS_EMPTY(LCD_SPI)||SPI_IS_BUSY(LCD_SPI)); + return SPI_READ_8BIT(LCD_SPI); +} + +// Receive buffer from SPI bus (len should be > 0) +void spi_RxBuffer(uint8_t *buffer, uint16_t len) { + do{ + SPI_WRITE_8BIT(LCD_SPI, 0xFF); + while (SPI_RX_IS_EMPTY(LCD_SPI)); + *buffer++ = SPI_READ_8BIT(LCD_SPI); + }while(--len); +} + +void spi_DropRx(void){ + // Drop Rx buffer after tx and wait tx complete + while (SPI_RX_IS_NOT_EMPTY(LCD_SPI)||SPI_IS_BUSY(LCD_SPI)) + (void)SPI_READ_8BIT(LCD_SPI); + (void)SPI_READ_8BIT(LCD_SPI); +} //***************************************************** // SPI DMA settings and data @@ -172,7 +130,6 @@ static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) } #endif -#ifdef __USE_DISPLAY_DMA_RX__ static const stm32_dma_stream_t *dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM); static const uint32_t rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) // Select SPI1 Rx DMA @@ -187,7 +144,6 @@ static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) (void)flags; } #endif -#endif // Send prepared DMA data, and wait completion static void dmaStreamFlush(uint32_t len) @@ -201,51 +157,7 @@ static void dmaStreamFlush(uint32_t len) dmaWaitCompletion(dmatx); } } -#endif - -// SPI transmit byte to SPI (no wait complete transmit) -void spi_TxByte(uint8_t data) { - SPI_WRITE_8BIT(LCD_SPI, data); -} - -// Transmit word to SPI bus (if SPI in 8 bit mode LSB send first!!!!!) -void spi_TxWord(uint16_t data) { - SPI_WRITE_16BIT(LCD_SPI, data); -} -// Transmit buffer to SPI bus (len should be > 0) -void spi_TxBuffer(const uint8_t *buffer, uint16_t len) { - do { - while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)); - SPI_WRITE_8BIT(LCD_SPI, *buffer++); - }while(--len); -} - -// Receive byte from SPI bus -static uint8_t spi_RxByte(void) { - // Start RX clock (by sending data) - SPI_WRITE_8BIT(LCD_SPI, 0xFF); - while (SPI_RX_IS_EMPTY(LCD_SPI)||SPI_IS_BUSY(LCD_SPI)); - return SPI_READ_8BIT(LCD_SPI); -} - -// Receive buffer from SPI bus (len should be > 0) -void spi_RxBuffer(uint8_t *buffer, uint16_t len) { - do{ - SPI_WRITE_8BIT(LCD_SPI, 0xFF); - while (SPI_RX_IS_EMPTY(LCD_SPI)); - *buffer++ = SPI_READ_8BIT(LCD_SPI); - }while(--len); -} - -void spi_DropRx(void){ - // Drop Rx buffer after tx and wait tx complete - while (SPI_RX_IS_NOT_EMPTY(LCD_SPI)||SPI_IS_BUSY(LCD_SPI)) - (void)SPI_READ_8BIT(LCD_SPI); - (void)SPI_READ_8BIT(LCD_SPI); -} - -#ifdef __USE_DISPLAY_DMA__ // SPI receive byte buffer use DMA void spi_DMATxBuffer(uint8_t *buffer, uint16_t len) { dmaStreamSetMemory0(dmatx, buffer); @@ -253,9 +165,14 @@ void spi_DMATxBuffer(uint8_t *buffer, uint16_t len) { dmaStreamFlush(len); } -#ifdef __USE_DISPLAY_DMA_RX__ +// Wait DMA Rx completion +static void dmaWaitCompletionRxTx(void){ + dmaWaitCompletion(dmatx); + dmaWaitCompletion(dmarx); +} + // SPI transmit byte buffer use DMA -static void spi_DMARxBuffer(uint8_t *buffer, uint16_t len) { +static void spi_DMARxBuffer(uint8_t *buffer, uint16_t len, bool wait) { uint8_t dummy_tx = 0xFF; // Init Rx DMA buffer, size, mode (spi and mem data size is 8 bit) dmaStreamSetMemory0(dmarx, buffer); @@ -270,12 +187,15 @@ static void spi_DMARxBuffer(uint8_t *buffer, uint16_t len) { // Start DMA exchange dmaStreamEnable(dmarx); dmaStreamEnable(dmatx); - // Wait DMA completion - dmaWaitCompletion(dmatx); - dmaWaitCompletion(dmarx); + if (wait) + dmaWaitCompletionRxTx(); } -#endif -#endif +#else +// Replace DMA function vs no DMA +#define dmaWaitCompletionRxTx() {} +#define spi_DMATxBuffer(buffer, len) spi_TxBuffer(buffer, len) +#define spi_DMARxBuffer(buffer, len, wait) spi_RxBuffer(buffer, len) +#endif // __USE_DISPLAY_DMA__ static void spi_init(void) { @@ -309,14 +229,117 @@ 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 + +// +// 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 + +#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) + // Disable inline for this function static void ili9341_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) { // Uncomment on low speed SPI (possible get here before previous tx complete) // while (SPI_IN_TX_RX(LCD_SPI)) // ; -// This only test code -// ili9341_bulk_finish(); LCD_CS_LOW; LCD_DC_CMD; SPI_WRITE_8BIT(LCD_SPI, cmd); @@ -338,7 +361,7 @@ static void ili9341_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) // Disable inline for this function uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) { - ili9341_bulk_finish(); + lcd_bulk_finish(); // Set read speed (if need different) SPI_BR_SET(LCD_SPI, SPI_BR_DIV256); // Send @@ -356,6 +379,7 @@ uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data) return ret; } +#ifdef LCD_DRIVER_ILI9341 static const uint8_t ili9341_init_seq[] = { // cmd, len, data..., // SW reset @@ -413,31 +437,74 @@ static const uint8_t ili9341_init_seq[] = { ILI9341_DISPLAY_ON, 0, 0 // sentinel }; - -#ifdef __LCD_BRIGHTNESS__ -#if HAL_USE_DAC == FALSE -#error "Need set HAL_USE_DAC in halconf.h for use __LCD_BRIGHTNESS__" +#define LCD_INIT ili9341_init_seq #endif -static const DACConfig dac1cfg1 = { - init: 0, - datamode: DAC_DHRM_12BIT_RIGHT +#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 }; +#define LCD_INIT ST7796S_init_seq +#endif +#ifdef __LCD_BRIGHTNESS__ +#if HAL_USE_DAC == TRUE +#error "Need disable HAL_USE_DAC in halconf.h for use __LCD_BRIGHTNESS__" +#endif static void lcd_initBrightness(void){ - dacStart(&DACD2, &dac1cfg1); + rccEnableDAC1(false); // Enable DAC1 + DAC->CR|= DAC_CR_EN2; // Use DAC1 CH2 } - // Brightness control range 0 - 100 void lcd_setBrightness(uint16_t b){ - b = 700 + b*((3300-700)/100); - dacPutChannelX(&DACD2, 0, b); + uint32_t v = 700 + b*(4000-700)/100; + DAC->DHR12R2 = v; } #else #define lcd_initBrightness() #endif -void ili9341_init(void) +void lcd_init(void) { spi_init(); // Init Brightness if LCD support @@ -448,15 +515,17 @@ void ili9341_init(void) chThdSleepMilliseconds(10); LCD_RESET_NEGATE; const uint8_t *p; - for (p = ili9341_init_seq; *p; ) { + for (p = LCD_INIT; *p; ) { ili9341_send_command(p[0], p[1], &p[2]); p += 2 + p[1]; - chThdSleepMilliseconds(5); + chThdSleepMilliseconds(2); } - ili9341_clear_screen(); + lcd_clear_screen(); } static void ili9341_setWindow(int x, int y, int w, int h){ +// Any LCD exchange start from this + dmaWaitCompletionRxTx(); //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)); @@ -465,125 +534,156 @@ static void ili9341_setWindow(int x, int y, int w, int h){ ili9341_send_command(ILI9341_PAGE_ADDRESS_SET, 4, (uint8_t *)&yy); } -#if 0 -// Test code for palette mode -void ili9341_bulk_8bit(int x, int y, int w, int h, uint16_t *palette) -{ - ili9341_setWindow(x, y ,w, h); - ili9341_send_command(ILI9341_MEMORY_WRITE, 0, NULL); - uint8_t *buf = (uint8_t *)spi_buffer; - int32_t len = w * h; - do { - spi_TxWord(palette[*buf++]); - }while(--len); -} -#endif - -#if DISPLAY_CELL_BUFFER_COUNT != 1 -#define LCD_BUFFER_1 0x01 -#define LCD_DMA_RUN 0x02 -static uint8_t LCD_dma_status = 0; -#endif - -pixel_t *ili9341_get_cell_buffer(void){ -#if DISPLAY_CELL_BUFFER_COUNT == 1 - return spi_buffer; -#else - return &spi_buffer[(LCD_dma_status&LCD_BUFFER_1) ? SPI_BUFFER_SIZE/2 : 0]; -#endif -} - #ifndef __USE_DISPLAY_DMA__ -void ili9341_fill(int x, int y, int w, int h, pixel_t color) +void lcd_fill(int x, int y, int w, int h) { - ili9341_setWindow(x, y ,w, h); + ili9341_setWindow(x, y, w, h); ili9341_send_command(ILI9341_MEMORY_WRITE, 0, NULL); uint32_t len = w * h; do { while (SPI_TX_IS_NOT_EMPTY(LCD_SPI)) ; #if LCD_PIXEL_SIZE == 2 - SPI_WRITE_16BIT(LCD_SPI, color); + SPI_WRITE_16BIT(LCD_SPI, background_color); #else - SPI_WRITE_8BIT(LCD_SPI, color); + SPI_WRITE_8BIT(LCD_SPI, background_color); #endif }while(--len); } -void ili9341_bulk(int x, int y, int w, int h) +void lcd_bulk(int x, int y, int w, int h) { - ili9341_setWindow(x, y ,w, h); + ili9341_setWindow(x, y, w, h); ili9341_send_command(ILI9341_MEMORY_WRITE, 0, NULL); spi_TxBuffer((uint8_t *)spi_buffer, w * h * sizeof(pixel_t)); } -void ili9341_bulk_continue(int x, int y, int w, int h){ - ili9341_bulk(x, y, w, h); -} - -void ili9341_bulk_finish(void){ - while (SPI_IS_BUSY(LCD_SPI)); // Wait tx -} - #else // // Use DMA for send data // +#define LCD_DMA_MODE (LCD_PIXEL_SIZE == 2 ? (STM32_DMA_CR_PSIZE_HWORD|STM32_DMA_CR_MSIZE_HWORD) : (STM32_DMA_CR_PSIZE_BYTE|STM32_DMA_CR_MSIZE_BYTE)) + // Fill region by some color -void ili9341_fill(int x, int y, int w, int h) +void lcd_fill(int x, int y, int w, int h) { - ili9341_setWindow(x, y ,w, h); + ili9341_setWindow(x, y, w, h); ili9341_send_command(ILI9341_MEMORY_WRITE, 0, NULL); dmaStreamSetMemory0(dmatx, &background_color); -#if LCD_PIXEL_SIZE == 2 - dmaStreamSetMode(dmatx, txdmamode | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD); -#else - dmaStreamSetMode(dmatx, txdmamode | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE); -#endif + dmaStreamSetMode(dmatx, txdmamode | LCD_DMA_MODE); dmaStreamFlush(w * h); } -void ili9341_bulk_finish(void){ - dmaWaitCompletion(dmatx); // Wait DMA - while (SPI_IN_TX_RX(LCD_SPI)); // Wait tx -} - static void ili9341_DMA_bulk(int x, int y, int w, int h, pixel_t *buffer){ - ili9341_setWindow(x, y ,w, h); + ili9341_setWindow(x, y, w, h); ili9341_send_command(ILI9341_MEMORY_WRITE, 0, NULL); dmaStreamSetMemory0(dmatx, buffer); -#if LCD_PIXEL_SIZE == 2 - dmaStreamSetMode(dmatx, txdmamode | STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_MINC); -#else - dmaStreamSetMode(dmatx, txdmamode | STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_MINC); -#endif + dmaStreamSetMode(dmatx, txdmamode | LCD_DMA_MODE | STM32_DMA_CR_MINC); dmaStreamSetTransactionSize(dmatx, w * h); dmaStreamEnable(dmatx); } // Copy spi_buffer to region, wait completion after -void ili9341_bulk(int x, int y, int w, int h) +void lcd_bulk(int x, int y, int w, int h) { - ili9341_DMA_bulk(x, y ,w, h, spi_buffer); // Send data - ili9341_bulk_finish(); // Wait + ili9341_DMA_bulk(x, y, w, h, spi_buffer); // Send data + dmaWaitCompletion(dmatx); // 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){ + dmaWaitCompletion(dmatx); // 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 -void ili9341_bulk_continue(int x, int y, int w, int h) +#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_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) { -#if DISPLAY_CELL_BUFFER_COUNT == 1 - ili9341_bulk(x, y, w, h); + uint16_t len = w * h; + ili9341_setWindow(x, y, w, h); + ili9341_send_command(ILI9341_MEMORY_READ, 0, NULL); + // 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 - ili9341_bulk_finish(); // Wait DMA - ili9341_DMA_bulk(x, y , w, h, ili9341_get_cell_buffer()); // Send new cell data - LCD_dma_status^=LCD_BUFFER_1; // Switch buffer + // 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 = dmaStreamGetTransactionSize(dmarx)+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); + dmaWaitCompletionRxTx(); // 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; } #endif -// Copy screen data to buffer -void ili9341_read_memory(int x, int y, int w, int h, uint16_t *out) +#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); @@ -598,39 +698,29 @@ void ili9341_read_memory(int x, int y, int w, int h, uint16_t *out) spi_RxByte(); // receive pixel data to buffer #ifndef __USE_DISPLAY_DMA_RX__ - spi_RxBuffer((uint8_t *)out, len * 3); + spi_RxBuffer((uint8_t *)out, len * 2); #else - spi_DMARxBuffer((uint8_t *)out, len * 3); + spi_DMARxBuffer((uint8_t *)out, len * 2, true); #endif // restore speed if need #ifdef LCD_SPI_RX_SPEED SPI_BR_SET(LCD_SPI, LCD_SPI_SPEED); #endif LCD_CS_HIGH; - // Parse received data to RGB565 format - uint8_t *rgbbuf = (uint8_t *)out; - while (len-- > 0) { - 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 += 3; - } } +#endif -void ili9341_clear_screen(void) +void lcd_clear_screen(void) { - ili9341_fill(0, 0, ILI9341_WIDTH, ILI9341_HEIGHT); + lcd_fill(0, 0, LCD_WIDTH, LCD_HEIGHT); } -void ili9341_set_foreground(uint16_t fg_idx) +void lcd_set_foreground(uint16_t fg_idx) { foreground_color = GET_PALTETTE_COLOR(fg_idx); } -void ili9341_set_background(uint16_t bg_idx) +void lcd_set_background(uint16_t bg_idx) { background_color = GET_PALTETTE_COLOR(bg_idx); } @@ -642,8 +732,7 @@ void ili9341_set_rotation(uint8_t r) ili9341_send_command(ILI9341_MEMORY_ACCESS_CONTROL, 1, &r); } -//static uint8_t bit_align = 0; -void ili9341_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *b) +void lcd_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *b) { pixel_t *buf = spi_buffer; uint8_t bits = 0; @@ -653,32 +742,17 @@ void ili9341_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, *buf++ = (0x80 & bits) ? foreground_color : background_color; bits <<= 1; } -// if (bit_align) b+=bit_align; } - ili9341_bulk(x, y, width, height); + lcd_bulk(x, y, width, height); } -#if 0 -void blit16BitWidthBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint16_t *bitmap) -{ - pixel_t *buf = (pixel_t *)spi_buffer; - for (uint32_t c = 0; c < height; c++) { - uint16_t bits = *bitmap++; - for (uint32_t r = 0; r < width; r++) { - *buf++ = (0x8000 & bits) ? foreground_color : background_color; - bits <<= 1; - } - } - ili9341_bulk(x, y, width, height); -} -#endif - -void ili9341_drawchar(uint8_t ch, int x, int y) +void lcd_drawchar(uint8_t ch, int x, int y) { - ili9341_blitBitmap(x, y, FONT_GET_WIDTH(ch), FONT_GET_HEIGHT, FONT_GET_DATA(ch)); + lcd_blitBitmap(x, y, FONT_GET_WIDTH(ch), FONT_GET_HEIGHT, FONT_GET_DATA(ch)); } -void ili9341_drawstring(const char *str, int x, int y) +#ifndef lcd_drawstring +void lcd_drawstring(int16_t x, int16_t y, const char *str) { int x_pos = x; while (*str) { @@ -686,32 +760,53 @@ void ili9341_drawstring(const char *str, int x, int y) if (ch == '\n') {x = x_pos; y+=FONT_STR_HEIGHT; continue;} const uint8_t *char_buf = FONT_GET_DATA(ch); uint16_t w = FONT_GET_WIDTH(ch); - ili9341_blitBitmap(x, y, w, FONT_GET_HEIGHT, char_buf); - x += w; - } -} - -void ili9341_drawstring_7x13(const char *str, int x, int y) -{ - int x_pos = x; - while (*str) { - uint8_t ch = *str++; - if (ch == '\n') {x = x_pos; y+=bFONT_STR_HEIGHT; continue;} - const uint8_t *char_buf = bFONT_GET_DATA(ch); - uint16_t w = bFONT_GET_WIDTH(ch); - ili9341_blitBitmap(x, y, w, bFONT_GET_HEIGHT, char_buf); + lcd_blitBitmap(x, y, w, FONT_GET_HEIGHT, char_buf); x += w; } } +#endif -void ili9341_drawstringV(const char *str, int x, int y) +typedef struct { + const void *vmt; + int16_t start_x; + int16_t start_y; + int16_t x; + int16_t y; +} lcdPrintStream; + +static msg_t lcd_put(void *ip, uint8_t ch) { + lcdPrintStream *ps = ip; + if (ch == '\n') {ps->x = ps->start_x; ps->y+=FONT_STR_HEIGHT; return MSG_OK;} + uint16_t w = FONT_GET_WIDTH(ch); + lcd_blitBitmap(ps->x, ps->y, w, FONT_GET_HEIGHT, FONT_GET_DATA(ch)); + ps->x+= w; + return MSG_OK; +} + +// Simple print in buffer function +int lcd_printf(int16_t x, int16_t y, const char *fmt, ...) { + // Init small lcd print stream + struct lcd_printStreamVMT { + _base_sequential_stream_methods + } lcd_vmt = {NULL, NULL, lcd_put, NULL}; + lcdPrintStream ps = {&lcd_vmt, x, y, x, y}; + // 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); + // Return number of bytes that would have been written. + return retval; +} + +void lcd_drawstringV(const char *str, int x, int y) { ili9341_set_rotation(DISPLAY_ROTATION_270); - ili9341_drawstring(str, ILI9341_HEIGHT-y, x); + lcd_drawstring(LCD_HEIGHT-y, x, str); ili9341_set_rotation(DISPLAY_ROTATION_0); } -int ili9341_drawchar_size(uint8_t ch, int x, int y, uint8_t size) +int lcd_drawchar_size(uint8_t ch, int x, int y, uint8_t size) { pixel_t *buf = spi_buffer; const uint8_t *char_buf = FONT_GET_DATA(ch); @@ -724,22 +819,22 @@ int ili9341_drawchar_size(uint8_t ch, int x, int y, uint8_t size) *buf++ = (0x80 & bits) ? foreground_color : background_color; } } - ili9341_bulk(x, y, w * size, FONT_GET_HEIGHT * size); + lcd_bulk(x, y, w * size, FONT_GET_HEIGHT * size); return w*size; } -void ili9341_drawfont(uint8_t ch, int x, int y) +void lcd_drawfont(uint8_t ch, int x, int y) { - ili9341_blitBitmap(x, y, NUM_FONT_GET_WIDTH, NUM_FONT_GET_HEIGHT, NUM_FONT_GET_DATA(ch)); + lcd_blitBitmap(x, y, NUM_FONT_GET_WIDTH, NUM_FONT_GET_HEIGHT, NUM_FONT_GET_DATA(ch)); } -void ili9341_drawstring_size(const char *str, int x, int y, uint8_t size) +void lcd_drawstring_size(const char *str, int x, int y, uint8_t size) { while (*str) - x += ili9341_drawchar_size(*str++, x, y, size); + x += lcd_drawchar_size(*str++, x, y, size); } #if 0 -static void ili9341_pixel(int x, int y, uint16_t color) +static void lcd_pixel(int x, int y, uint16_t color) { uint32_t xx = __REV16(x|((x)<<16)); uint32_t yy = __REV16(y|((y)<<16)); @@ -749,9 +844,7 @@ static void ili9341_pixel(int x, int y, uint16_t color) } #endif -#define SWAP(x, y) { int z = x; x = y; y = z; } - -void ili9341_line(int x0, int y0, int x1, int y1) +void lcd_line(int x0, int y0, int x1, int y1) { #if 0 // modifed Bresenham's line algorithm, see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm @@ -767,10 +860,10 @@ void ili9341_line(int x0, int y0, int x1, int y1) if (e2 < dy) { err += dx; y0 += sy; } } #endif - SWAP(foreground_color, background_color); + SWAP(uint16_t, foreground_color, background_color); if (x0 > x1) { - SWAP(x0, x1); - SWAP(y0, y1); + SWAP(int, x0, x1); + SWAP(int, y0, y1); } while (x0 <= x1) { @@ -792,13 +885,13 @@ void ili9341_line(int x0, int y0, int x1, int y1) } } if (dy > 0) - ili9341_fill(x0, y0, dx, dy); + lcd_fill(x0, y0, dx, dy); else - ili9341_fill(x0, y0+dy, dx, -dy); + lcd_fill(x0, y0+dy, dx, -dy); x0 += dx; y0 += dy; } - SWAP(foreground_color, background_color); + SWAP(uint16_t, foreground_color, background_color); } #if 0 @@ -814,13 +907,13 @@ void ili9341_test(int mode) switch (mode) { default: #if 1 - ili9341_fill(0, 0, LCD_WIDTH, LCD_HEIGHT, 0); + lcd_fill(0, 0, LCD_WIDTH, LCD_HEIGHT, 0); for (y = 0; y < LCD_HEIGHT; y++) { - ili9341_fill(0, y, LCD_WIDTH, 1, RGB(LCD_HEIGHT-y, y, (y + 120) % 256)); + lcd_fill(0, y, LCD_WIDTH, 1, RGB(LCD_HEIGHT-y, y, (y + 120) % 256)); } break; case 1: - ili9341_fill(0, 0, LCD_WIDTH, LCD_HEIGHT, 0); + lcd_fill(0, 0, LCD_WIDTH, LCD_HEIGHT, 0); for (y = 0; y < LCD_HEIGHT; y++) { for (x = 0; x < LCD_WIDTH; x++) { ili9341_pixel(x, y, (y<<8)|x); @@ -835,7 +928,7 @@ void ili9341_test(int mode) #if 1 case 3: for (i = 0; i < 10; i++) - ili9341_drawfont(i, i*20, 120); + lcd_drawfont(i, i*20, 120); break; #endif #if 0 @@ -844,10 +937,10 @@ void ili9341_test(int mode) break; #endif case 4: - ili9341_line(0, 0, 15, 100); - ili9341_line(0, 0, 100, 100); - ili9341_line(0, 15, 100, 0); - ili9341_line(0, 100, 100, 0); + lcd_line(0, 0, 15, 100); + lcd_line(0, 0, 100, 100); + lcd_line(0, 15, 100, 0); + lcd_line(0, 100, 100, 0); break; } } @@ -1119,19 +1212,18 @@ static inline uint8_t SD_ReadR1(uint32_t cnt) { return r1; } -// Wait SD ready token answer (wait time in systick, 10 systick = 1ms) +// Wait SD ready token answer (wait time in systick) static inline bool SD_WaitDataToken(uint8_t token, uint32_t wait_time) { uint8_t res; uint32_t time = chVTGetSystemTimeX(); - uint32_t count = 0; + uint8_t count = 0; do{ if ((res = spi_RxByte()) == token) return true; count++; - // Check timeout only every 65536 bytes read (~50ms interval) - if ((count&0xFFFF) == 0) - if ((chVTGetSystemTimeX() - time) > wait_time) - break; + // Check timeout only every 256 bytes read (~8ms) + if (count == 0 && (chVTGetSystemTimeX() - time) > wait_time) + break; }while (res == 0xFF); return false; } @@ -1143,19 +1235,18 @@ static inline uint8_t SD_WaitDataAccept(uint32_t cnt) { return res&0x1F; } -// Wait no Busy answer from SD (wait time in systick, 10 systick = 1ms) +// Wait no Busy answer from SD (wait time in systick) static uint8_t SD_WaitNotBusy(uint32_t wait_time) { uint8_t res; uint32_t time = chVTGetSystemTimeX(); - uint32_t count = 0; + uint8_t count = 0; do{ if ((res = spi_RxByte()) == 0xFF) return res; count++; - // Check timeout only every 65536 bytes read (~50ms interval) - if ((count&0xFFFF) == 0) - if ((chVTGetSystemTimeX() - time) > wait_time) - break; + // Check timeout only every 256 bytes read (~8ms) + if (count == 0 && (chVTGetSystemTimeX() - time) > wait_time) + break; }while (1); return 0; } @@ -1163,13 +1254,13 @@ static uint8_t SD_WaitNotBusy(uint32_t wait_time) { // Receive data block from SD static bool SD_RxDataBlock(uint8_t *buff, uint16_t len, uint8_t token) { // loop until receive read response token or timeout ~50ms - if (!SD_WaitDataToken(token, 500)) { + if (!SD_WaitDataToken(token, MS2ST(50))) { DEBUG_PRINT(" rx SD_WaitDataToken err\r\n"); return FALSE; } // Receive data (Not use rx DMA) #ifdef __USE_SDCARD_DMA_RX__ - spi_DMARxBuffer(buff, len); + spi_DMARxBuffer(buff, len, true); #else spi_RxBuffer(buff, len); #endif @@ -1215,7 +1306,7 @@ static bool SD_TxDataBlock(const uint8_t *buff, uint8_t token) { } #if 0 // Wait busy (recommended timeout is 250ms (500ms for SDXC) set 250ms - resp = SD_WaitNotBusy(2500); + resp = SD_WaitNotBusy(MS2ST(250)); if (resp == 0xFF) return TRUE; #else @@ -1234,7 +1325,7 @@ static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg) { uint8_t buf[6]; uint8_t r1; // wait SD ready after last Tx (recommended timeout is 250ms (500ms for SDXC) set 250ms - if ((r1 = SD_WaitNotBusy(2500)) != 0xFF) { + if ((r1 = SD_WaitNotBusy(MS2ST(250))) != 0xFF) { DEBUG_PRINT(" SD_WaitNotBusy CMD%d err, %02x\r\n", cmd-0x40, (uint32_t)r1); return 0xFF; } @@ -1280,9 +1371,6 @@ static void SD_PowerOn(void) { // Set SD card to idle state if (SD_SendCmd(CMD0, 0) == SD_R1_IDLE) Stat|= STA_POWER_ON; - else{ - Stat = STA_NOINIT; - } SD_Unselect_SPI(); } @@ -1318,8 +1406,11 @@ DSTATUS disk_initialize(BYTE pdrv) { total_time = chVTGetSystemTimeX(); #endif if (pdrv != 0) return STA_NOINIT; + // Start init SD card + Stat = STA_NOINIT; // power on, try detect on bus, set card to idle state SD_PowerOn(); + if (!SD_CheckPower()) return Stat; // check disk type uint8_t type = 0; uint32_t cnt = 100; @@ -1350,7 +1441,7 @@ DSTATUS disk_initialize(BYTE pdrv) { if (cnt && SD_SendCmd(CMD58, 0) == 0) { DWORD ocr; spi_RxBuffer((uint8_t *)&ocr, 4); - DEBUG_PRINT(" CMD58 OCR = 0x%08X\r\n", _OCR(ocr)); + DEBUG_PRINT(" CMD58 OCR = 0x%08x\r\n", _OCR(ocr)); // Check CCS bit, SDv2 (HC or SC) type = (ocr & _OCR(SD_OCR_CAPACITY)) ? CT_SD2 | CT_BLOCK : CT_SD2; } @@ -1364,7 +1455,7 @@ DSTATUS disk_initialize(BYTE pdrv) { // if (SD_SendCmd(CMD9, 0) == 0 && SD_RxDataBlock(csd, 16, SD_TOKEN_START_BLOCK)){ // DEBUG_PRINT(" CSD ="); // for (int i = 0; i<16; i++) -// DEBUG_PRINT(" %02X", csd[i]); +// DEBUG_PRINT(" %02x", csd[i]); // DEBUG_PRINT("\r\n"); // } } else { @@ -1389,11 +1480,9 @@ DSTATUS disk_initialize(BYTE pdrv) { SD_Unselect_SPI(); CardType = type; DEBUG_PRINT("CardType %d\r\n", type); - // Clear STA_NOINIT and set Power on - if (type){ - Stat&= ~STA_NOINIT; - Stat|= STA_POWER_ON; - } + // Clear STA_NOINIT + if (type) + Stat&=~STA_NOINIT; else // Initialization failed SD_PowerOff(); return Stat; @@ -1431,10 +1520,10 @@ DRESULT disk_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { #if DEBUG == 1 r_time+= chVTGetSystemTimeX(); if (count) - DEBUG_PRINT(" err READ_BLOCK %d 0x%08X\r\n", count, sector); + DEBUG_PRINT(" err READ_BLOCK %d 0x%08x\r\n", count, sector); #if 0 else{ - DEBUG_PRINT("Sector read 0x%08X %d \r\n", sector, cnt); + DEBUG_PRINT("Sector read 0x%08x %d \r\n", sector, cnt); for (UINT j = 0; j < 32; j++){ for (UINT i = 0; i < 16; i++) DEBUG_PRINT(" 0x%02x", buff[j*16 + i]); @@ -1458,10 +1547,10 @@ DRESULT disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { #if DEBUG == 1 #if 0 - DEBUG_PRINT("Sector write 0x%08X, %d\r\n", sector, count); + DEBUG_PRINT("Sector write 0x%08x, %d\r\n", sector, count); for (UINT j = 0; j < 32; j++){ for (UINT i = 0; i < 16; i++) - DEBUG_PRINT(" 0x%02X", buff[j*16 + i]); + DEBUG_PRINT(" 0x%02x", buff[j*16 + i]); DEBUG_PRINT("\r\n"); } #endif @@ -1483,7 +1572,7 @@ DRESULT disk_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { #if DEBUG == 1 w_time+= chVTGetSystemTimeX(); if (count) - DEBUG_PRINT(" WRITE_BLOCK %d 0x%08X\r\n", count, sector); + DEBUG_PRINT(" WRITE_BLOCK %d 0x%08x\r\n", count, sector); #endif return count ? RES_ERROR : RES_OK; @@ -1504,7 +1593,7 @@ DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff) { // Nothing to do for this command if each write operation to the media is completed // within the disk_write function. case CTRL_SYNC: - if (SD_WaitNotBusy(2000) == 0xFF) res = RES_OK; + if (SD_WaitNotBusy(MS2ST(200)) == 0xFF) res = RES_OK; break; #if FF_USE_TRIM == 1 // Informs the device the data on the block of sectors is no longer needed and it can be erased. diff --git a/lc_matching.c b/lc_matching.c index 64379e4..3be7f7e 100644 --- a/lc_matching.c +++ b/lc_matching.c @@ -8,10 +8,27 @@ * DiSlord adaptation to use on NanoVNA */ -// calculate physical component values to match an impendace to 'ref_impedance' (ie 50R) +#ifdef __VNA_MEASURE_MODULE__ +// Memory for measure cache data +static char measure_memory[64]; -#ifdef __USE_LC_MATCHING__ +// Measure math functions +// quadratic function solver +static void match_quadratic_equation(float a, float b, float c, float *x) +{ + const float a_x_2 = 2.0f * a; + const float d = (b * b) - (2.0f * a_x_2 * c); + if (d < 0){ + x[0] = x[1] = 0.0f; + return; + } + const float sd = vna_sqrtf(d); + x[0] = (-b + sd) / a_x_2; + x[1] = (-b - sd) / a_x_2; +} +#ifdef __USE_LC_MATCHING__ +// calculate physical component values to match an impendace to 'ref_impedance' (ie 50R) typedef struct { float xps; // Reactance parallel to source (can be NAN if not applicable) @@ -21,28 +38,15 @@ typedef struct typedef struct { - uint32_t Hz; - float R0; + freq_t Hz; + float R0; // L-Network solution structure t_lc_match matches[4]; int16_t num_matches; - uint16_t sweep_n; -} t_lc_match_array; +} lc_match_array_t; -static t_lc_match_array lc_match_array; - -static void lc_match_quadratic_equation(float a, float b, float c, float *x) -{ - const float d = (b * b) - (4.0f * a * c); - if (d < 0){ - x[0] = x[1] = 0.0f; - return; - } - const float sd = sqrtf(d); - const float a2 = 2.0f * a; - x[0] = (-b + sd) / a2; - x[1] = (-b - sd) / a2; -} +// Size = 60 bytes +static lc_match_array_t *lc_match_array = (lc_match_array_t *)measure_memory; // Calculate two solutions for ZL where (R + X * X / R) > R0 static void lc_match_calc_hi(float R0, float RL, float XL, t_lc_match *matches) @@ -51,25 +55,25 @@ static void lc_match_calc_hi(float R0, float RL, float XL, t_lc_match *matches) const float a = R0 - RL; const float b = 2.0f * XL * R0; - const float c = R0 * ((XL * XL) + (RL * RL)); - lc_match_quadratic_equation(a, b, c, xp); + const float c = R0 * (XL * XL + RL * RL); + match_quadratic_equation(a, b, c, xp); // found two impedances parallel to load // // now calculate serial impedances const float RL1 = -XL * xp[0]; const float XL1 = RL * xp[0]; - const float RL2 = RL + 0.0f; + const float RL2 = RL;// + 0.0f; const float XL2 = XL + xp[0]; - matches[0].xs = ((RL1 * XL2) - (RL2 * XL1)) / ((RL2 * RL2) + (XL2 * XL2)); + matches[0].xs = (RL1 * XL2 - RL2 * XL1) / (RL2 * RL2 + XL2 * XL2); matches[0].xps = 0.0f; matches[0].xpl = xp[0]; const float RL3 = -XL * xp[1]; const float XL3 = RL * xp[1]; - const float RL4 = RL + 0.0f; + const float RL4 = RL;// + 0.0f; const float XL4 = XL + xp[1]; - matches[1].xs = ((RL3 * XL4) - (RL4 * XL3)) / ((RL4 * RL4) + (XL4 * XL4)); + matches[1].xs = (RL3 * XL4 - RL4 * XL3) / (RL4 * RL4 + XL4 * XL4); matches[1].xps = 0.0f; matches[1].xpl = xp[1]; } @@ -81,53 +85,53 @@ static void lc_match_calc_lo(float R0, float RL, float XL, t_lc_match *matches) // Calculate Xs const float a = 1.0f; const float b = 2.0f * XL; - const float c = (RL * RL) + (XL * XL) - (R0 * RL); - lc_match_quadratic_equation(a, b, c, xs); + const float c = RL * RL + XL * XL - R0 * RL; + match_quadratic_equation(a, b, c, xs); // got two serial impedances that change ZL to the Y.real = 1/R0 // // now calculate impedances parallel to source - const float RL1 = RL + 0.0f; + const float RL1 = RL;// + 0.0f; const float XL1 = XL + xs[0]; const float RL3 = RL1 * R0; const float XL3 = XL1 * R0; const float RL5 = RL1 - R0; - const float XL5 = XL1 - 0.0f; + const float XL5 = XL1;// - 0.0f; matches[0].xs = xs[0]; - matches[0].xps = ((RL5 * XL3) - (RL3 * XL5)) / ((RL5 * RL5) + (XL5 * XL5)); + matches[0].xps = (RL5 * XL3 - RL3 * XL5) / (RL5 * RL5 + XL5 * XL5); matches[0].xpl = 0.0f; - const float RL2 = RL + 0.0f; + const float RL2 = RL;// + 0.0f; const float XL2 = XL + xs[1]; const float RL4 = RL2 * R0; const float XL4 = XL2 * R0; const float RL6 = RL2 - R0; - const float XL6 = XL2 - 0.0f; + const float XL6 = XL2;// - 0.0f; matches[1].xs = xs[1]; - matches[1].xps = ((RL6 * XL4) - (RL4 * XL6)) / ((RL6 * RL6) + (XL6 * XL6)); + matches[1].xps = (RL6 * XL4 - RL4 * XL6) / (RL6 * RL6 + XL6 * XL6); matches[1].xpl = 0.0f; } static int lc_match_calc(int index) { - const float R0 = lc_match_array.R0; + const float R0 = lc_match_array->R0; // compute the impedance at the chosen frequency const float *coeff = measured[0][index]; - const float RL = resistance(coeff); - const float XL = reactance(coeff); + const float RL = resistance(index, coeff); + const float XL = reactance(index, coeff); if (RL <= 0.5f) return -1; const float q_factor = XL / RL; - const float vswr = swr(coeff); + const float vswr = swr(index, coeff); // no need for any matching if (vswr <= 1.1f || q_factor >= 100.0f) return 0; // only one solution is enough: just a serial reactance // this gives SWR < 1.1 if R is within the range 0.91 .. 1.1 of R0 - t_lc_match *matches = lc_match_array.matches; + t_lc_match *matches = lc_match_array->matches; if ((RL * 1.1f) > R0 && RL < (R0 * 1.1f)){ matches[0].xpl = 0.0f; matches[0].xps = 0.0f; @@ -151,35 +155,26 @@ static int lc_match_calc(int index) return 4; } -// Mark to redraw area under L/C match text -static void lc_match_mark_area(void){ - // Update area - int n = lc_match_array.num_matches; if (n < 0) n = 0; - invalidate_rect(STR_LC_MATH_X , STR_LC_MATH_Y, - STR_LC_MATH_X + 3 * STR_LC_MATH_WIDTH, STR_LC_MATH_Y + (n + 2)*STR_LC_MATH_HEIGHT); -} - -static void lc_match_process(void) +static void prepare_lc_match(uint8_t mode, uint8_t update_mask) { - const uint32_t am = (uint32_t)active_marker; - if (am >=MARKERS_MAX || current_props._markers[am].enabled == false) - return; - - const uint32_t index = current_props._markers[am].index; - if (index >= sweep_points || frequencies[index] == 0) - return; - + (void)mode; + (void)update_mask; // Made calculation only one time for current sweep and frequency - if (lc_match_array.sweep_n == sweep_count && lc_match_array.Hz == frequencies[index]) + freq_t freq = get_marker_frequency(active_marker); + if (freq == 0)// || lc_match_array->Hz == freq) return; - lc_match_array.R0 = 50.0f; - lc_match_array.Hz = frequencies[index]; - lc_match_array.sweep_n = sweep_count; + lc_match_array->R0 = 50.0f; + lc_match_array->Hz = freq; // compute the possible LC matches - lc_match_array.num_matches = lc_match_calc(index); - lc_match_mark_area(); + lc_match_array->num_matches = lc_match_calc(markers[active_marker].index); + + // Mark to redraw area under L/C match text + int n = lc_match_array->num_matches; + if (n < 0) n = 0; + invalidate_rect(STR_MEASURE_X , STR_MEASURE_Y, + STR_MEASURE_X + 3 * STR_MEASURE_WIDTH, STR_MEASURE_Y + (n + 2)*STR_MEASURE_HEIGHT); } // @@ -189,7 +184,6 @@ static void lc_match_x_str(uint32_t FHz, float X, int xp, int yp) return; char type; - char str[12]; #if 0 float val; if (X < 0.0f) {val = 1.0f / (2.0f * VNA_PI * FHz * -X); type = 'F';} @@ -197,57 +191,40 @@ static void lc_match_x_str(uint32_t FHz, float X, int xp, int yp) #else if (X < 0.0f) {X = -1.0 / X; type = 'F';} else { type = 'H';} - float val = X / (2.0f * VNA_PI * FHz); + float val = X / ((2.0f * VNA_PI) * FHz); #endif - plot_printf(str, sizeof(str), "%4.2F%c", val, type); - cell_drawstring_7x13(str, xp, yp); + cell_printf(xp, yp, "%4.2F%c", val, type); } // Render L/C match to cell -static void cell_draw_lc_match(int x0, int y0) +static void draw_lc_match(int x0, int y0) { - char s[32]; - lc_match_process(); - - int xp = STR_LC_MATH_X - x0; - int yp = STR_LC_MATH_Y - y0; - - ili9341_set_background(LCD_BG_COLOR); - ili9341_set_foreground(LCD_LC_MATCH_COLOR); - - if (yp > -bFONT_GET_HEIGHT && yp < CELLHEIGHT) - { - plot_printf(s, sizeof(s), "L/C match for source Z0 = %0.1f"S_OHM, lc_match_array.R0); - cell_drawstring_7x13(s, xp, yp); - } + int xp = STR_MEASURE_X - x0; + int yp = STR_MEASURE_Y - y0; + cell_printf(xp, yp, "L/C match for source Z0 = %0.1f"S_OHM, lc_match_array->R0); #if 0 - yp += STR_LC_MATH_HEIGHT; - if (yp > -bFONT_GET_HEIGHT && yp < CELLHEIGHT) - { - plot_printf(s, sizeof(s), "%qHz %0.1f %c j%0.1f"S_OHM, match_array->Hz, match_array->RL, (match_array->XL >= 0) ? '+' : '-', fabsf(match_array->XL)); - cell_drawstring_7x13(s, xp, yp); - } + yp += STR_MEASURE_HEIGHT; + cell_printf(xp, yp, "%qHz %0.1f %c j%0.1f"S_OHM, match_array->Hz, match_array->RL, (match_array->XL >= 0) ? '+' : '-', vna_fabsf(match_array->XL)); #endif - - yp += STR_LC_MATH_HEIGHT; + yp += STR_MEASURE_HEIGHT; if (yp >= CELLHEIGHT) return; - if (lc_match_array.num_matches < 0) - cell_drawstring_7x13("No LC match for this", xp, yp); - else if (lc_match_array.num_matches == 0) - cell_drawstring_7x13("No need for LC match", xp, yp); + if (lc_match_array->num_matches < 0) + cell_printf(xp, yp, "No LC match for this"); + else if (lc_match_array->num_matches == 0) + cell_printf(xp, yp, "No need for LC match"); else { - cell_drawstring_7x13("Src shunt" , xp , yp); - cell_drawstring_7x13("Series" , xp + STR_LC_MATH_WIDTH, yp); - cell_drawstring_7x13("Load shunt", xp + 2*STR_LC_MATH_WIDTH, yp); - for (int i = 0; i < lc_match_array.num_matches; i++){ - yp += STR_LC_MATH_HEIGHT; + cell_printf(xp , yp, "Src shunt" ); + cell_printf(xp + STR_MEASURE_WIDTH, yp, "Series" ); + cell_printf(xp + 2*STR_MEASURE_WIDTH, yp, "Load shunt"); + for (int i = 0; i < lc_match_array->num_matches; i++){ + yp += STR_MEASURE_HEIGHT; if (yp >= CELLHEIGHT) return; - if (yp > -bFONT_GET_HEIGHT){ - lc_match_x_str(lc_match_array.Hz, lc_match_array.matches[i].xps, xp , yp); - lc_match_x_str(lc_match_array.Hz, lc_match_array.matches[i].xs , xp + STR_LC_MATH_WIDTH, yp); - lc_match_x_str(lc_match_array.Hz, lc_match_array.matches[i].xpl, xp + 2*STR_LC_MATH_WIDTH, yp); - } + lc_match_x_str(lc_match_array->Hz, lc_match_array->matches[i].xps, xp , yp); + lc_match_x_str(lc_match_array->Hz, lc_match_array->matches[i].xs , xp + STR_MEASURE_WIDTH, yp); + lc_match_x_str(lc_match_array->Hz, lc_match_array->matches[i].xpl, xp + 2*STR_MEASURE_WIDTH, yp); } } } -#endif +#endif // __USE_LC_MATCHING__ + +#endif // __VNA_MEASURE__ diff --git a/main.c b/main.c index d062ed7..a835102 100644 --- a/main.c +++ b/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, Dmitry (DiSlord) dislordlive@gmail.com + * Copyright (c) 2019-2021, Dmitry (DiSlord) dislordlive@gmail.com * Based on TAKAHASHI Tomohiro (TTRFTECH) edy555@gmail.com * All rights reserved. * @@ -24,11 +24,9 @@ #include "usbcfg.h" #include "si5351.h" #include "nanovna.h" -#include "fft.h" #include #include -#include /* * Shell settings @@ -37,7 +35,7 @@ // enable this need reduce spi_buffer size, by default shell run in main thread // #define VNA_SHELL_THREAD -static BaseSequentialStream *shell_stream; +static BaseSequentialStream *shell_stream = 0; // Shell new line #define VNA_SHELL_NEWLINE_STR "\r\n" @@ -86,8 +84,6 @@ static volatile vna_shellcmd_t shell_function = 0; //#define ENABLE_GAIN_COMMAND // Enable port command, used for debug //#define ENABLE_PORT_COMMAND -// Enable si5351 timing command, used for debug -//#define ENABLE_SI5351_TIMINGS // Enable si5351 register write, used for debug //#define ENABLE_SI5351_REG_WRITE // Enable i2c timing command, used for debug @@ -99,25 +95,24 @@ static volatile vna_shellcmd_t shell_function = 0; // Enable debug for console command //#define DEBUG_CONSOLE_SHOW // Enable usart command -//#define ENABLE_USART_COMMAND +#define ENABLE_USART_COMMAND +#ifdef __USE_SD_CARD__ // Enable SD card console command -//#define ENABLE_SD_CARD_CMD +#define ENABLE_SD_CARD_CMD +#endif -static void apply_CH0_error_term_at(int i); -static void apply_CH1_error_term_at(int i); -static void apply_edelay(void); +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]); +static void cal_interpolate(int idx, freq_t f, float data[CAL_TYPE_COUNT][2]); static uint16_t get_sweep_mask(void); -static void cal_interpolate(void); -static void update_frequencies(bool interpolate); -static int set_frequency(uint32_t freq); -static void set_frequencies(uint32_t start, uint32_t stop, uint16_t points); +static void update_frequencies(void); +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(void); -extern void show_version(bool mode); +static void transform_domain(uint16_t ch_mask); uint8_t sweep_mode = SWEEP_ENABLE; -uint8_t redraw_request = 0; // contains REDRAW_XXX flags // sweep operation variables volatile uint16_t wait_count = 0; @@ -127,16 +122,21 @@ static uint16_t p_sweep = 0; static int16_t rx_buffer[AUDIO_BUFFER_LEN * 2]; // Sweep measured data float measured[2][POINTS_COUNT][2]; -uint32_t frequencies[POINTS_COUNT]; + +//Buffer for fast apply FFT window function +#ifdef USE_FFT_WINDOW_BUFFER +static float kaiser_data[FFT_SIZE]; +#endif #undef VERSION -#define VERSION "1.0.45" +#define VERSION "1.0.69" // Version text, displayed in Config->Version menu, also send by info command const char *info_about[]={ "Board: " BOARD_NAME, - "2019-2020 Copyright @hugen NANOVNA.COM", - "Based on @DiSlord @edy555 source, licensed under GPL.", + "2019-2021 Copyright NanoVNA.com", + "based on @DiSlord @edy555 ... source", + "Licensed under GPL.", "Version: " VERSION " ["\ "p:"define_to_STR(POINTS_COUNT)", "\ "IF:"define_to_STR(FREQUENCY_IF_K)"k, "\ @@ -155,10 +155,10 @@ const char *info_about[]={ #ifdef DEBUG_CONSOLE_SHOW void my_debug_log(int offs, char *log){ static uint16_t shell_line_y = 0; - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); - ili9341_fill(FREQUENCIES_XPOS1, shell_line_y, LCD_WIDTH-FREQUENCIES_XPOS1, 2 * FONT_GET_HEIGHT); - ili9341_drawstring(log, FREQUENCIES_XPOS1 + offs, shell_line_y); + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); + lcd_fill(FREQUENCIES_XPOS1, shell_line_y, LCD_WIDTH-FREQUENCIES_XPOS1, 2 * FONT_GET_HEIGHT); + lcd_drawstring(FREQUENCIES_XPOS1 + offs, shell_line_y, log); shell_line_y+=FONT_STR_HEIGHT; if (shell_line_y >= LCD_HEIGHT - FONT_STR_HEIGHT*4) shell_line_y=0; } @@ -167,16 +167,84 @@ void my_debug_log(int offs, char *log){ #define DEBUG_LOG(offs, text) #endif +#ifdef __USE_SMOOTH__ +static float arifmetic_mean(float v0, float v1, float v2){ + return (v0+2*v1+v2)/4; +} + +static float geometry_mean(float v0, float v1, float v2){ + float v = vna_cbrtf(vna_fabsf(v0*v1*v2)); + if (v0+v1+v2 < 0) v = -v; + return v; +} + +uint8_t smooth_factor = 0; +void set_smooth_factor(uint8_t factor){ + if (factor > 8) factor = 8; + smooth_factor = factor; + request_to_redraw(REDRAW_CAL_STATUS); +} +uint8_t get_smooth_factor(void) { + return smooth_factor; +} + +// Allow smooth complex data point array (this remove noise, smooth power depend form count) +// see https://terpconnect.umd.edu/~toh/spectrum/Smoothing.html +static void measurementDataSmooth(uint16_t ch_mask){ + int j; +// ch_mask = 2; +// memcpy(measured[0], measured[1], sizeof(measured[0])); + float (*smooth_func)(float v0, float v1, float v2) = (config._vna_mode&VNA_SMOOTH_FUNCTION) ? arifmetic_mean : geometry_mean; + for (int ch = 0; ch < 2; ch++,ch_mask>>=1) { + if ((ch_mask&1)==0) continue; + int count = 1<<(smooth_factor-1), n; + float *data = measured[ch][0]; + for (n = 0; n < count; n++){ + float prev_re = data[2*0 ]; + float prev_im = data[2*0+1]; +// first point smooth (use first and second points), disabled it made phase shift +// data[0] = smooth_func(prev_re, prev_re, data[2 ]); +// data[1] = smooth_func(prev_im, prev_im, data[2+1]); +// simple data smooth on 3 points + for (j = 1; j < sweep_points - 1; j++){ + float old_re = data[2*j ]; // save current data point for next point smooth + float old_im = data[2*j+1]; + data[2*j ] = smooth_func(prev_re, data[2*j ], data[2*j+2]); + data[2*j+1] = smooth_func(prev_im, data[2*j+1], data[2*j+3]); + prev_re = old_re; + prev_im = old_im; + } +// last point smooth, disabled it made phase shift +// data[2*j ] = smooth_func(data[2*j ], data[2*j ], prev_re); +// data[2*j+1] = smooth_func(data[2*j+1], data[2*j+1], prev_im); + } + } +} +#endif + static THD_WORKING_AREA(waThread1, 768); static THD_FUNCTION(Thread1, arg) { (void)arg; chRegSetThreadName("sweep"); +/* + * UI (menu, touch, buttons) and plot initialize + */ + ui_init(); + //Initialize graph plotting + plot_init(); +/* + * Set LCD display brightness + */ +#ifdef __LCD_BRIGHTNESS__ + lcd_setBrightness(config._brightness); +#endif while (1) { bool completed = false; + uint16_t mask = get_sweep_mask(); if (sweep_mode&(SWEEP_ENABLE|SWEEP_ONCE)) { - completed = sweep(true, get_sweep_mask()); + completed = sweep(true, mask); sweep_mode&=~SWEEP_ONCE; } else { __WFI(); @@ -189,16 +257,24 @@ static THD_FUNCTION(Thread1, arg) continue; } // Process UI inputs + sweep_mode|= SWEEP_UI_MODE; ui_process(); + sweep_mode&=~SWEEP_UI_MODE; // Process collected data, calculate trace coordinates and plot only if scan completed if ((sweep_mode & SWEEP_ENABLE) && completed) { - if (electrical_delay != 0) apply_edelay(); - if ((domain_mode & DOMAIN_MODE) == DOMAIN_TIME) transform_domain(); - +#ifdef __USE_SMOOTH__ +// START_PROFILE; + if (smooth_factor) + measurementDataSmooth(mask); +// STOP_PROFILE; +#endif +// START_PROFILE + if ((props_mode & DOMAIN_MODE) == DOMAIN_TIME) transform_domain(mask); +// STOP_PROFILE; // Prepare draw graphics, cache all lines, mark screen cells for redraw plot_into_index(measured); - redraw_request |= REDRAW_CELLS | REDRAW_BATTERY; } + request_to_redraw(REDRAW_BATTERY); #ifndef DEBUG_CONSOLE_SHOW // plot trace and other indications as raster draw_all(completed); // flush markmap only if scan completed to prevent remaining traces @@ -224,6 +300,10 @@ toggle_sweep(void) sweep_mode ^= SWEEP_ENABLE; } +#if 0 +// +// Original kaiser_window functions +// static float bessel0(float x) { @@ -246,113 +326,167 @@ kaiser_window(float k, float n, float beta) { if (beta == 0.0) return 1.0; float r = (2 * k) / (n - 1) - 1; - return bessel0(beta * sqrt(1 - r * r)) / bessel0(beta); + return bessel0(beta * vna_sqrtf(1 - r * r)) / bessel0(beta); +} +#else +// Zero-order Bessel function +// (x/2)^(2n) +// 1 + ---------- +// (n!)^2 +// set input as (x/2)^2 (input range 0 .. beta*beta/4) +// x^n x x^2 x^3 x^4 x^5 +// 1 + ------ = 1 + ------ + ------ + ------ + ------ + ------ ...... +// (n!)^2 1 4 36 576 14400 +// first 2 (0 and 1) precalculated as simple. + +// Precalculated multiplier step for (n!)^2 max SIZE 16 (first 2 use as init) +static const float div[] = {/*0, 1,*/ 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256}; +float bessel0_ext(float x_pow_2) +{ +// set calculated count, more SIZE - less error but longer (bigger beta also need more size for less error) +// For beta = 6 SIZE = (11-2) no error +// For beta = 13 SIZE = (13-2) max error 0.0002 (use as default, use constant size faster then check every time limits in float) +#define SIZE (13-2) + int i = SIZE; + float term = x_pow_2; + float ret = 1.0f + term; + do { + term*= x_pow_2 / div[SIZE - i]; + ret += term; + }while(--i); + return ret; +} +// Move out constant divider: bessel0(beta) +// Made calculation optimization (in integer) +// x = (2*k)/(n-1) - 1 = (set n=n-1) = 2*k/n - 1 = (2*k-n)/n +// calculate kaiser window vs bessel0(w) there: +// n*n - (2*k-n)*(2*k-n) 4*k*(n-k) +// w = beta*sqrt(1 - x*x) = beta*sqrt(---------------------) = beta*sqrt(---------) +// n*n n*n +// bessel0(w) = bessel0_ext(z) (there z = (w/2)^2 for speed) +// return = bessel0_ext(z) +static float +kaiser_window_ext(uint32_t k, uint32_t n, uint16_t beta) +{ + if (beta == 0) return 1.0; + n = n - 1; + k = k * (n - k) * beta * beta; + n = n * n; + return bessel0_ext((float)k / n); } +#endif static void -transform_domain(void) +transform_domain(uint16_t ch_mask) { // use spi_buffer as temporary buffer and calculate ifft for time domain // Need 2 * sizeof(float) * FFT_SIZE bytes for work #if 2*4*FFT_SIZE > (SPI_BUFFER_SIZE * LCD_PIXEL_SIZE) #error "Need increase spi_buffer or use less FFT_SIZE value" #endif - float* tmp = (float*)spi_buffer; - - uint16_t window_size = sweep_points, offset = 0; + int i; + uint16_t offset = 0; uint8_t is_lowpass = FALSE; - uint8_t td_func = domain_mode & TD_FUNC; - switch (td_func) { - case TD_FUNC_BANDPASS: - offset = 0; - window_size = sweep_points; - break; + switch (domain_func) { +// case TD_FUNC_BANDPASS: +// break; case TD_FUNC_LOWPASS_IMPULSE: case TD_FUNC_LOWPASS_STEP: is_lowpass = TRUE; offset = sweep_points; - window_size = sweep_points * 2; break; } - - float beta = 0.0f; - switch (domain_mode & TD_WINDOW) { - case TD_WINDOW_MINIMUM: -// beta = 0.0f; // this is rectangular - break; + uint16_t window_size = sweep_points + offset; + uint16_t beta = 0; + switch (domain_window) { +// case TD_WINDOW_MINIMUM: +// beta = 0; // this is rectangular +// break; case TD_WINDOW_NORMAL: - beta = 6.0f; + beta = 6; break; case TD_WINDOW_MAXIMUM: - beta = 13.0f; + beta = 13; break; } - -#if 1 - // recalculate the scale factor if any window details are changed. - // the scale factor is to compensate for windowing. - static float window_scale = 1.0f; + // Add amplitude correction for not full size FFT data and also add computed default scale + // recalculate the scale factor if any window details are changed. The scale factor is to compensate for windowing. + // Add constant multiplier for kaiser_window_ext use 1.0f / bessel0_ext(beta*beta/4.0f) + // Add constant multiplier 1.0f / FFT_SIZE + static float window_scale = 0; static uint16_t td_cache = 0; - uint16_t td_check = (domain_mode & (TD_WINDOW|TD_FUNC))|(sweep_points<<5); + // Check mode cache data + uint16_t td_check = (props_mode & (TD_WINDOW|TD_FUNC))|(sweep_points<<5); if (td_cache!=td_check){ - td_cache=td_check; - if (td_func == TD_FUNC_LOWPASS_STEP) - window_scale = 1.0f; + td_cache = td_check; + if (domain_func == TD_FUNC_LOWPASS_STEP) + window_scale = 1.0f / (FFT_SIZE * bessel0_ext(beta*beta/4.0f)); else { window_scale = 0.0f; for (int i = 0; i < sweep_points; i++) - window_scale += kaiser_window(i + offset, window_size, beta); - window_scale = (FFT_SIZE/2) / window_scale; - if (td_func == TD_FUNC_BANDPASS) - window_scale *= 2; + window_scale += kaiser_window_ext(i + offset, window_size, beta); + if (domain_func == TD_FUNC_BANDPASS) window_scale = 1.0f / ( window_scale); + else window_scale = 1.0f / (2*window_scale); +// window_scale*= FFT_SIZE // add correction from kaiser_window +// window_scale/= FFT_SIZE // add defaut from FFT_SIZE +// window_scale*= bessel0_ext(beta*beta/4.0f) // for get result as kaiser_window +// window_scale/= bessel0_ext(beta*beta/4.0f) // for set correction on calculated kaiser_window for value } - } -#else - // Disable compensation - #define window_scale 1 +#ifdef USE_FFT_WINDOW_BUFFER + // Cache window function data to buffer + for (i = 0; i < sweep_points; i++) + kaiser_data[i] = kaiser_window_ext(i + offset, window_size, beta) * window_scale; #endif - - uint16_t ch_mask = get_sweep_mask(); + } + // Made Time Domain Calculations for (int ch = 0; ch < 2; ch++,ch_mask>>=1) { if ((ch_mask&1)==0) continue; - memcpy(tmp, measured[ch], sizeof(measured[0])); - for (int i = 0; i < sweep_points; i++) { - float w = kaiser_window(i + offset, window_size, beta) * window_scale; - tmp[i * 2 + 0] *= w; - tmp[i * 2 + 1] *= w; + // Prepare data in tmp buffer (use spi_buffer), apply window function and constant correction factor + float* tmp = (float*)spi_buffer; + float *data = measured[ch][0]; + for (i = 0; i < sweep_points; i++) { +#ifdef USE_FFT_WINDOW_BUFFER + float w = kaiser_data[i]; +#else + float w = kaiser_window_ext(i + offset, window_size, beta) * window_scale; +#endif + tmp[i * 2 + 0] = data[i * 2 + 0] * w; + tmp[i * 2 + 1] = data[i * 2 + 1] * w; } - for (int i = sweep_points; i < FFT_SIZE; i++) { + // Fill zeroes last + for (; i < FFT_SIZE; i++) { tmp[i * 2 + 0] = 0.0; tmp[i * 2 + 1] = 0.0; } + // For lowpass mode swap if (is_lowpass) { - for (int i = 1; i < sweep_points; i++) { - tmp[(FFT_SIZE - i) * 2 + 0] = tmp[i * 2 + 0]; + for (i = 1; i < sweep_points; i++) { + tmp[(FFT_SIZE - i) * 2 + 0] = tmp[i * 2 + 0]; tmp[(FFT_SIZE - i) * 2 + 1] = -tmp[i * 2 + 1]; } } - + // Made iFFT in temp buffer fft_inverse((float(*)[2])tmp); - memcpy(measured[ch], tmp, sizeof(measured[0])); - for (int i = 0; i < sweep_points; i++) { - measured[ch][i][0] /= (float)FFT_SIZE; - if (is_lowpass) { - measured[ch][i][1] = 0.0; - } else { - measured[ch][i][1] /= (float)FFT_SIZE; - } + // set img part as zero + if (is_lowpass){ + for (i = 0; i < sweep_points; i++) + tmp[i*2+1] = 0.0f; } - if ((domain_mode & TD_FUNC) == TD_FUNC_LOWPASS_STEP) { - for (int i = 1; i < sweep_points; i++) { - measured[ch][i][0] += measured[ch][i - 1][0]; + if (domain_func == TD_FUNC_LOWPASS_STEP) { + for (i = 1; i < sweep_points; i++) { + tmp[i*2+0]+= tmp[i*2+0-2]; +// tmp[i*2+1]+= tmp[i*2+1-2]; // already zero as is_lowpass } } + // Copy data back + memcpy(measured[ch], tmp, sizeof(measured[0])); } } // Shell commands output -static int shell_printf(const char *fmt, ...) +int shell_printf(const char *fmt, ...) { + if (shell_stream == NULL) return 0; va_list ap; int formatted_bytes; va_start(ap, fmt); @@ -374,6 +508,38 @@ 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; @@ -387,7 +553,7 @@ VNA_SHELL_FUNCTION(cmd_resume) (void)argv; // restore frequencies array and cal - update_frequencies(cal_status & CALSTAT_APPLY); + update_frequencies(); resume_sweep(); } @@ -395,14 +561,15 @@ VNA_SHELL_FUNCTION(cmd_reset) { (void)argc; (void)argv; - +#ifdef __DFU_SOFTWARE_MODE__ if (argc == 1) { - if (strcmp(argv[0], "dfu") == 0) { + if (get_str_index(argv[0], "dfu") == 0) { shell_printf("Performing reset to DFU mode\r\n"); enter_dfu(); return; } } +#endif shell_printf("Performing reset\r\n"); rccEnableWWDG(FALSE); @@ -460,7 +627,7 @@ uint32_t my_atoui(const char *p) } } -double +float my_atof(const char *p) { int neg = FALSE; @@ -468,14 +635,14 @@ my_atof(const char *p) neg = TRUE; if (*p == '-' || *p == '+') p++; - double x = my_atoi(p); + float x = my_atoi(p); while (_isdigit((int)*p)) p++; if (*p == '.') { - double d = 1.0f; + float d = 1.0f; p++; while (_isdigit((int)*p)) { - d /= 10; + d /= 10.0f; x += d * (*p - '0'); p++; } @@ -497,37 +664,16 @@ my_atof(const char *p) return x; } -// -// 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(char *v, const char *list) +#ifdef __USE_SMOOTH__ +VNA_SHELL_FUNCTION(cmd_smooth) { - int i = 0; - while (1) { - 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++; + if (argc == 1) { + set_smooth_factor(my_atoui(argv[0])); + return; } - return -1; + shell_printf("smooth = %d\r\n", smooth_factor); } +#endif #ifdef USE_VARIABLE_OFFSET VNA_SHELL_FUNCTION(cmd_offset) @@ -536,9 +682,7 @@ VNA_SHELL_FUNCTION(cmd_offset) shell_printf("usage: offset {frequency offset(Hz)}\r\n"); return; } - int32_t offset = my_atoi(argv[0]); - generate_DSP_Table(offset); - si5351_set_frequency_offset(offset); + si5351_set_frequency_offset(my_atoi(argv[0])); } #endif @@ -557,12 +701,12 @@ VNA_SHELL_FUNCTION(cmd_freq) } void set_power(uint8_t value){ + request_to_redraw(REDRAW_CAL_STATUS); if (value > SI5351_CLK_DRIVE_STRENGTH_8MA) value = SI5351_CLK_DRIVE_STRENGTH_AUTO; if (current_props._power == value) return; current_props._power = value; - // Update power if pause + // Update power if pause, need for generation in CW mode if (!(sweep_mode&SWEEP_ENABLE)) si5351_set_power(value); - redraw_request|=REDRAW_CAL_STATUS; } VNA_SHELL_FUNCTION(cmd_power) @@ -606,34 +750,30 @@ VNA_SHELL_FUNCTION(cmd_time) rtc_set_time(dt_buf[1], dt_buf[0]); return; usage: - shell_printf("20%02X/%02X/%02X %02X:%02X:%02X\r\n"\ + shell_printf("20%02x/%02x/%02x %02x:%02x:%02x\r\n"\ "usage: time {[%s] 0-99} or {b 0xYYMMDD 0xHHMMSS}\r\n", time[6], time[5], time[4], time[2], time[1], time[0], time_cmd); } #endif #ifdef __VNA_ENABLE_DAC__ // Check DAC enabled in ChibiOS -#if HAL_USE_DAC == FALSE -#error "Need set HAL_USE_DAC in halconf.h for use DAC" +#if HAL_USE_DAC == TRUE +#error "Need disable HAL_USE_DAC in halconf.h for use VNA_DAC" #endif -static const DACConfig dac1cfg1 = { - //init: 1922U, - init: 0, - datamode: DAC_DHRM_12BIT_RIGHT -}; +static void dac_init(void) { + rccEnableDAC1(false); // Use DAC1 + DAC->CR|= DAC_CR_EN2; // Enable DAC1 ch2 +} VNA_SHELL_FUNCTION(cmd_dac) { - int value; if (argc != 1) { shell_printf("usage: dac {value(0-4095)}\r\n"\ - "current value: %d\r\n", config.dac_value); + "current value: %d\r\n", config._dac_value); return; } - value = my_atoui(argv[0]); - config.dac_value = value; - dacPutChannelX(&DACD2, 0, value); + DAC->DHR12R2 = my_atoui(argv[0])&0xFFF; } #endif @@ -642,11 +782,11 @@ VNA_SHELL_FUNCTION(cmd_threshold) uint32_t value; if (argc != 1) { shell_printf("usage: threshold {frequency in harmonic mode}\r\n"\ - "current: %d\r\n", config.harmonic_freq_threshold); + "current: %d\r\n", config._harmonic_freq_threshold); return; } value = my_atoui(argv[0]); - config.harmonic_freq_threshold = value; + config._harmonic_freq_threshold = value; } VNA_SHELL_FUNCTION(cmd_saveconfig) @@ -664,7 +804,7 @@ VNA_SHELL_FUNCTION(cmd_clearconfig) return; } - if (strcmp(argv[0], "1234") != 0) { + if (get_str_index(argv[0], "1234") != 0) { shell_printf("Key unmatched.\r\n"); return; } @@ -693,44 +833,22 @@ VNA_SHELL_FUNCTION(cmd_data) shell_printf("usage: data [array]\r\n"); } -#ifdef ENABLED_DUMP_COMMAND -VNA_SHELL_FUNCTION(cmd_dump) -{ - int i, j; - int len; - - if (argc == 1) - dump_selection = my_atoi(argv[0]); - - dsp_start(3); - dsp_wait(); - - len = AUDIO_BUFFER_LEN; - if (dump_selection == 1 || dump_selection == 2) - len /= 2; - for (i = 0; i < len; ) { - for (j = 0; j < 16; j++, i++) { - shell_printf("%04x ", 0xffff & (int)dump_buffer[i]); - } - shell_printf("\r\n"); - } -} -#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; -#if (SPI_BUFFER_SIZE*LCD_PIXEL_SIZE) < (3*LCD_WIDTH*2) +// 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 - // read 2 row pixel time (read buffer limit by 2/3 + 1 from spi_buffer size) - for (y = 0; y < LCD_HEIGHT; y += 2) { + // read 2 row pixel time + for (y = 0; y < LCD_HEIGHT; y += READ_ROWS) { // use uint16_t spi_buffer[2048] (defined in ili9341) for read buffer - ili9341_read_memory(0, y, LCD_WIDTH, 2, (uint16_t *)spi_buffer); - streamWrite(shell_stream, (void*)spi_buffer, 2 * LCD_WIDTH * sizeof(uint16_t)); + lcd_read_memory(0, y, LCD_WIDTH, READ_ROWS, (uint16_t *)spi_buffer); + streamWrite(shell_stream, (void*)spi_buffer, READ_ROWS * LCD_WIDTH * sizeof(uint16_t)); } } @@ -778,15 +896,20 @@ VNA_SHELL_FUNCTION(cmd_sample) config_t config = { .magic = CONFIG_MAGIC, - .dac_value = 1922, - .lcd_palette = LCD_DEFAULT_PALETTE, - .touch_cal = DEFAULT_TOUCH_CONFIG, - ._mode = VNA_MODE_START_STOP, - .harmonic_freq_threshold = FREQUENCY_THRESHOLD, - ._serial_speed = SERIAL_DEFAULT_BITRATE, - .vbat_offset = 320, + ._harmonic_freq_threshold = FREQUENCY_THRESHOLD, + ._IF_freq = FREQUENCY_OFFSET, + ._touch_cal = DEFAULT_TOUCH_CONFIG, + ._vna_mode = VNA_MODE_USB | VNA_MODE_SEARCH_MAX, ._brightness = DEFAULT_BRIGHTNESS, - .bandwidth = BANDWIDTH_1000 + ._dac_value = 1922, + ._vbat_offset = 320, + ._bandwidth = BANDWIDTH_1000, + ._lcd_palette = LCD_DEFAULT_PALETTE, + ._serial_speed = SERIAL_DEFAULT_BITRATE, + ._xtal_freq = XTALFREQ, + ._measure_r = MEASURE_DEFAULT_R, + ._lever_mode = LM_MARKER, + ._digit_separator = '.', }; properties_t current_props; @@ -800,7 +923,28 @@ static const trace_t def_trace[TRACES_MAX] = {//enable, type, channel, reserved, }; static const marker_t def_markers[MARKERS_MAX] = { - { 1, 0, 30, 0 }, { 0, 0, 40, 0 }, { 0, 0, 60, 0 }, { 0, 0, 80, 0 } + { 1, 0, 30*POINTS_COUNT/100-1, 0 }, +#if MARKERS_MAX > 1 + { 0, 0, 40*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 2 + { 0, 0, 50*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 3 + { 0, 0, 60*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 4 + { 0, 0, 70*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 5 + { 0, 0, 80*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 6 + { 0, 0, 90*POINTS_COUNT/100-1, 0 }, +#endif +#if MARKERS_MAX > 7 + { 0, 0,100*POINTS_COUNT/100-1, 0 }, +#endif }; // Load propeties default settings @@ -808,42 +952,59 @@ void load_default_properties(void) { //Magic add on caldata_save //current_props.magic = CONFIG_MAGIC; - current_props._frequency0 = 50000; // start = 50kHz - current_props._frequency1 = 900000000; // end = 900MHz - current_props._sweep_points = POINTS_COUNT_DEFAULT; // Set default points count + current_props._frequency0 = 50000; // start = 50kHz + current_props._frequency1 = 900000000; // end = 900MHz + current_props._var_freq = 0; + current_props._sweep_points = POINTS_COUNT_DEFAULT; // Set default points count + current_props._cal_frequency0 = 50000; // calibration start = 50kHz + current_props._cal_frequency1 = 900000000; // calibration end = 900MHz + current_props._cal_sweep_points = POINTS_COUNT_DEFAULT; // Set calibration default points count current_props._cal_status = 0; -//This data not loaded by default -//current_props._cal_data[5][POINTS_COUNT][2]; //============================================= - current_props._electrical_delay = 0.0; memcpy(current_props._trace, def_trace, sizeof(def_trace)); memcpy(current_props._markers, def_markers, sizeof(def_markers)); - current_props._velocity_factor = 0.7; +//============================================= + current_props._electrical_delay = 0.0; + current_props._portz = 50.0f; + current_props._velocity_factor = 70; + current_props._current_trace = 0; current_props._active_marker = 0; - current_props._domain_mode = 0; + current_props._previous_marker = MARKER_INVALID; + current_props._mode = 0; current_props._marker_smith_format = MS_RLC; - current_props._power = SI5351_CLK_DRIVE_STRENGTH_AUTO; + current_props._power = SI5351_CLK_DRIVE_STRENGTH_AUTO; + current_props._cal_power = SI5351_CLK_DRIVE_STRENGTH_AUTO; + current_props._measure = MEASURE_NONE; +//This data not loaded by default +//current_props._cal_data[5][POINTS_COUNT][2]; //Checksum add on caldata_save //current_props.checksum = 0; } int load_properties(uint32_t id){ int r = caldata_recall(id); - update_frequencies(false); + update_frequencies(); +#ifdef __VNA_MEASURE_MODULE__ + plot_set_measure_mode(current_props._measure); +#endif return r; } #ifdef ENABLED_DUMP_COMMAND -int16_t dump_buffer[AUDIO_BUFFER_LEN]; +int16_t *dump_buffer; +volatile int16_t dump_len = 0; int16_t dump_selection = 0; static void -duplicate_buffer_to_dump(int16_t *p) +duplicate_buffer_to_dump(int16_t *p, size_t n) { - if (dump_selection == 1) - p = samp_buf; - else if (dump_selection == 2) - p = ref_buf; - memcpy(dump_buffer, p, sizeof dump_buffer); + p+=dump_selection; + while (n) { + if (dump_len == 0) return; + dump_len--; + *dump_buffer++ = *p; + p+=2; + n-=2; + } } #endif @@ -857,12 +1018,12 @@ void i2s_end_callback(I2SDriver *i2sp, size_t offset, size_t n) int16_t *p = &rx_buffer[offset]; (void)i2sp; if (wait_count == 0 || chVTGetSystemTimeX() < ready_time) return; - if (wait_count == config.bandwidth+2) // At this moment in buffer exist noise data, reset and wait next clean buffer + if (wait_count == config._bandwidth+2) // At this moment in buffer exist noise data, reset and wait next clean buffer reset_dsp_accumerator(); - else if (wait_count <= config.bandwidth+1) // Clean data ready, process it + else if (wait_count <= config._bandwidth+1) // Clean data ready, process it dsp_process(p, n); #ifdef ENABLED_DUMP_COMMAND - duplicate_buffer_to_dump(p); + duplicate_buffer_to_dump(p, n); #endif --wait_count; // stat.callback_count++; @@ -880,21 +1041,22 @@ static const I2SConfig i2sconfig = { #ifdef ENABLE_SI5351_TIMINGS extern uint16_t timings[16]; -#define DELAY_CHANNEL_CHANGE timings[6] -#define DELAY_SWEEP_START timings[7] - -#else -// Use x 100us settings -#define DELAY_CHANNEL_CHANGE 3 // Delay for switch ADC channel -#define DELAY_SWEEP_START 50 // Sweep start delay, allow remove noise at 1 point +#undef DELAY_CHANNEL_CHANGE +#undef DELAY_SWEEP_START +#define DELAY_CHANNEL_CHANGE timings[3] +#define DELAY_SWEEP_START timings[4] #endif -#define DSP_START(delay) {ready_time = chVTGetSystemTimeX() + delay; wait_count = config.bandwidth+2;} +#define DSP_START(delay) {ready_time = chVTGetSystemTimeX() + delay; wait_count = config._bandwidth+2;} #define DSP_WAIT while (wait_count) {__WFI();} #define RESET_SWEEP {p_sweep = 0;} -#define SWEEP_CH0_MEASURE 1 -#define SWEEP_CH1_MEASURE 2 +#define SWEEP_CH0_MEASURE 1 +#define SWEEP_CH1_MEASURE 2 +#define SWEEP_APPLY_EDELAY 4 +#define SWEEP_APPLY_CALIBRATION 8 +#define SWEEP_USE_INTERPOLATION 16 +#define SWEEP_USE_RENORMALIZATION 32 static uint16_t get_sweep_mask(void){ uint16_t ch_mask = 0; @@ -905,72 +1067,156 @@ static uint16_t get_sweep_mask(void){ if (trace[t].channel == 0) ch_mask|=SWEEP_CH0_MEASURE; if (trace[t].channel == 1) ch_mask|=SWEEP_CH1_MEASURE; } +#ifdef __VNA_MEASURE_MODULE__ + // For measure calculations need data + ch_mask|= plot_get_measure_channels(); +#endif +#ifdef __VNA_Z_RENORMALIZATION__ + if (current_props._portz != 50.0f) + ch_mask|= SWEEP_USE_RENORMALIZATION; +#endif + if (cal_status & CALSTAT_APPLY) ch_mask|= SWEEP_APPLY_CALIBRATION; + if (cal_status & CALSTAT_INTERPOLATED) ch_mask|= SWEEP_USE_INTERPOLATION; + if (electrical_delay) ch_mask|= SWEEP_APPLY_EDELAY; return ch_mask; } +static void applyEDelay(float data[2], float s, float c){ + float real = data[0]; + float imag = data[1]; + data[0] = real * c - imag * s; + data[1] = imag * c + real * s; +} + +#ifdef __VNA_Z_RENORMALIZATION__ +#include "vna_modules/vna_renorm.c" +#endif + // main loop for measurement -static bool sweep(bool break_on_operation, uint16_t ch_mask) +static bool sweep(bool break_on_operation, uint16_t mask) { - int delay; if (p_sweep>=sweep_points || break_on_operation == false) RESET_SWEEP; - if (break_on_operation && ch_mask == 0) + 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 palClearPad(GPIOC, GPIOC_LED); + int delay = 0; // START_PROFILE; - ili9341_set_background(LCD_SWEEP_LINE_COLOR); + lcd_set_background(LCD_SWEEP_LINE_COLOR); // Wait some time for stable power int st_delay = DELAY_SWEEP_START; + int bar_start = 0; + int interpolation_idx; + for (; p_sweep < sweep_points; p_sweep++) { - delay = set_frequency(frequencies[p_sweep]); + freq_t frequency = getFrequency(p_sweep); + // Need made measure - set frequency + 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 * 1E-12, &s, &c); + // Set invalid value for check + c_data[0][0] = INFINITY; + } // CH0:REFLECTION, reset and begin measure - if (ch_mask & SWEEP_CH0_MEASURE){ + if (mask & SWEEP_CH0_MEASURE){ tlv320aic3204_select(0); DSP_START(delay+st_delay); delay = DELAY_CHANNEL_CHANGE; + // Get calibration data + if (mask & SWEEP_APPLY_CALIBRATION) + cal_interpolate(interpolation_idx, frequency, c_data); //================================================ // Place some code thats need execute while delay //================================================ DSP_WAIT; - (*sample_func)(measured[0][p_sweep]); // calculate reflection coefficient - if (APPLY_CALIBRATION_AFTER_SWEEP == 0 && (cal_status & CALSTAT_APPLY)) - apply_CH0_error_term_at(p_sweep); + (*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); } // CH1:TRANSMISSION, reset and begin measure - if (ch_mask & SWEEP_CH1_MEASURE){ + if (mask & SWEEP_CH1_MEASURE){ tlv320aic3204_select(1); DSP_START(delay+st_delay); + // Get calibration data + if ((mask & SWEEP_APPLY_CALIBRATION) && c_data[0][0] == INFINITY) + cal_interpolate(interpolation_idx, frequency, c_data); //================================================ // Place some code thats need execute while delay //================================================ DSP_WAIT; - (*sample_func)(measured[1][p_sweep]); // Measure transmission coefficient - if (APPLY_CALIBRATION_AFTER_SWEEP == 0 && (cal_status & CALSTAT_APPLY)) - apply_CH1_error_term_at(p_sweep); + (*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); + } +#ifdef __VNA_Z_RENORMALIZATION__ + if (mask & SWEEP_USE_RENORMALIZATION) + apply_renormalization(data, mask); +#endif + if (p_sweep < POINTS_COUNT){ + if (mask & SWEEP_CH0_MEASURE){ + measured[0][p_sweep][0] = data[0]; + measured[0][p_sweep][1] = data[1]; + } + if (mask & SWEEP_CH1_MEASURE){ + measured[1][p_sweep][0] = data[2]; + measured[1][p_sweep][1] = data[3]; + } } if (operation_requested && break_on_operation) break; st_delay = 0; -// Display SPI made noise on measurement (can see in CW mode) - if (config.bandwidth >= BANDWIDTH_100) - ili9341_fill(OFFSETX+CELLOFFSETX, OFFSETY, (p_sweep * WIDTH)/(sweep_points-1), 1); - } - ili9341_set_background(LCD_GRID_COLOR); - if (config.bandwidth >= BANDWIDTH_100) - ili9341_fill(OFFSETX+CELLOFFSETX, OFFSETY, WIDTH, 1); - // Apply calibration at end if need - if (APPLY_CALIBRATION_AFTER_SWEEP && (cal_status & CALSTAT_APPLY) && p_sweep == sweep_points){ - uint16_t start_sweep; - for (start_sweep = 0; start_sweep < p_sweep; start_sweep++){ - if (ch_mask & SWEEP_CH0_MEASURE) apply_CH0_error_term_at(start_sweep); - if (ch_mask & SWEEP_CH1_MEASURE) apply_CH1_error_term_at(start_sweep); + // Display SPI made noise on measurement (can see in CW mode), use reduced update + if (config._bandwidth >= BANDWIDTH_100){ + int current_bar = (p_sweep * WIDTH)/(sweep_points-1); + if (current_bar - bar_start > 0){ + lcd_fill(OFFSETX+CELLOFFSETX + bar_start, OFFSETY, current_bar - bar_start, 1); + bar_start = current_bar; + } } } + if (bar_start){ + lcd_set_background(LCD_GRID_COLOR); + lcd_fill(OFFSETX+CELLOFFSETX, OFFSETY, bar_start, 1); + } // STOP_PROFILE; // blink LED while scanning palSetPad(GPIOC, GPIOC_LED); return p_sweep == sweep_points; } +#ifdef ENABLED_DUMP_COMMAND +VNA_SHELL_FUNCTION(cmd_dump) +{ + int i, j; + int16_t dump[96*2]; + dump_buffer = dump; + dump_len = sizeof(dump) / sizeof(int16_t); + int len = dump_len; + if (argc == 1) + dump_selection = my_atoi(argv[0]) == 1 ? 0 : 1; + + tlv320aic3204_select(0); + DSP_START(DELAY_SWEEP_START); + while (dump_len > 0) {__WFI();} + for (i = 0, j = 0; i < len; i++) { + shell_printf("%6d ", dump[i]); + if (++j == 12) { + shell_printf("\r\n"); + j = 0; + } + } +} +#endif + #ifdef ENABLE_GAIN_COMMAND VNA_SHELL_FUNCTION(cmd_gain) { @@ -987,14 +1233,14 @@ VNA_SHELL_FUNCTION(cmd_gain) } #endif -static int set_frequency(uint32_t freq) +static int set_frequency(freq_t freq) { return si5351_set_frequency(freq, current_props._power); } void set_bandwidth(uint16_t bw_count){ - config.bandwidth = bw_count&0x1FF; - redraw_request|=REDRAW_FREQUENCY; + config._bandwidth = bw_count&0x1FF; + request_to_redraw(REDRAW_FREQUENCY); } uint32_t get_bandwidth_frequency(uint16_t bw_freq){ @@ -1019,7 +1265,7 @@ VNA_SHELL_FUNCTION(cmd_bandwidth) goto result; set_bandwidth(user_bw); result: - shell_printf("bandwidth %d (%uHz)\r\n", config.bandwidth, get_bandwidth_frequency(config.bandwidth)); + shell_printf("bandwidth %d (%uHz)\r\n", config._bandwidth, get_bandwidth_frequency(config._bandwidth)); } void set_sweep_points(uint16_t points){ @@ -1027,21 +1273,70 @@ void set_sweep_points(uint16_t points){ return; sweep_points = points; - update_frequencies(cal_status & CALSTAT_APPLY); + update_frequencies(); +} + +/* + * Frequency list functions + */ +#ifdef __USE_FREQ_TABLE__ +static freq_t frequencies[POINTS_COUNT]; +static void +set_frequencies(freq_t start, freq_t stop, uint16_t points) +{ + uint32_t i; + freq_t step = (points - 1); + freq_t span = stop - start; + freq_t delta = span / step; + freq_t error = span % step; + freq_t f = start, df = step>>1; + for (i = 0; i <= step; i++, f+=delta) { + frequencies[i] = f; + if ((df+=error) >= step) {f++; df-= step;} + } + // disable at out of sweep range + for (; i < POINTS_COUNT; i++) + frequencies[i] = 0; +} +#define _c_start frequencies[0] +#define _c_stop frequencies[sweep_points-1] +#define _c_points (sweep_points) + +freq_t getFrequency(uint16_t idx) {return frequencies[idx];} +#else +static freq_t _f_start; +static freq_t _f_delta; +static freq_t _f_error; +static uint16_t _f_points; + +static void +set_frequencies(freq_t start, freq_t stop, uint16_t points) +{ + freq_t span = stop - start; + _f_start = start; + _f_points = (points - 1); + _f_delta = span / _f_points; + _f_error = span % _f_points; +} +freq_t getFrequency(uint16_t idx) {return _f_start + _f_delta * idx + (_f_points / 2 + _f_error * idx) / _f_points;} +freq_t getFrequencyStep(void) {return _f_delta;} +#endif +static bool needInterpolate(freq_t start, freq_t stop, uint16_t points){ + return start != cal_frequency0 || stop != cal_frequency1 || points != cal_sweep_points; } #define SCAN_MASK_OUT_FREQ 0b00000001 #define SCAN_MASK_OUT_DATA0 0b00000010 #define SCAN_MASK_OUT_DATA1 0b00000100 #define SCAN_MASK_NO_CALIBRATION 0b00001000 +#define SCAN_MASK_NO_EDELAY 0b00010000 #define SCAN_MASK_BINARY 0b10000000 VNA_SHELL_FUNCTION(cmd_scan) { - uint32_t start, stop; + freq_t start, stop; uint16_t points = sweep_points; - int i; if (argc < 2 || argc > 4) { shell_printf("usage: scan {start(Hz)} {stop(Hz)} [points] [outmask]\r\n"); return; @@ -1078,35 +1373,30 @@ VNA_SHELL_FUNCTION(cmd_scan) } #endif - uint32_t old_cal_status = cal_status; - if (mask&SCAN_MASK_NO_CALIBRATION) cal_status&=~CALSTAT_APPLY; - // Rebuild frequency table if need - if (frequencies[0]!=start || frequencies[points-1]!=stop){ - set_frequencies(start, stop, points); - if (cal_status & CALSTAT_APPLY) - cal_interpolate(); - } + 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 (needInterpolate(start, stop, sweep_points)) + sweep_ch|= SWEEP_USE_INTERPOLATION; + sweep_points = points; + set_frequencies(start, stop, points); if (sweep_ch & (SWEEP_CH0_MEASURE|SWEEP_CH1_MEASURE)) sweep(false, sweep_ch); - - cal_status = old_cal_status; // restore - pause_sweep(); // Output data after if set (faster data receive) if (mask) { if (mask&SCAN_MASK_BINARY){ streamWrite(shell_stream, (void *)&mask, sizeof(uint16_t)); streamWrite(shell_stream, (void *)&points, sizeof(uint16_t)); - for (i = 0; i < points; i++) { - if (mask & SCAN_MASK_OUT_FREQ ) streamWrite(shell_stream, (void *)&frequencies[i], sizeof(uint32_t)); // 4 bytes .. frequency + for (int i = 0; i < points; i++) { + if (mask & SCAN_MASK_OUT_FREQ ) {freq_t f = getFrequency(i); streamWrite(shell_stream, (void *)&f, sizeof(freq_t));} // 4 bytes .. frequency if (mask & SCAN_MASK_OUT_DATA0) streamWrite(shell_stream, (void *)&measured[0][i][0], sizeof(float)* 2); // 4+4 bytes .. S11 real/imag if (mask & SCAN_MASK_OUT_DATA1) streamWrite(shell_stream, (void *)&measured[1][i][0], sizeof(float)* 2); // 4+4 bytes .. S21 real/imag } } else{ - for (i = 0; i < points; i++) { - if (mask & SCAN_MASK_OUT_FREQ ) shell_printf("%u ", frequencies[i]); + for (int i = 0; i < points; i++) { + if (mask & SCAN_MASK_OUT_FREQ ) shell_printf("%u ", getFrequency(i)); if (mask & SCAN_MASK_OUT_DATA0) shell_printf("%f %f ", measured[0][i][0], measured[0][i][1]); if (mask & SCAN_MASK_OUT_DATA1) shell_printf("%f %f ", measured[1][i][0], measured[1][i][1]); shell_printf("\r\n"); @@ -1124,28 +1414,41 @@ VNA_SHELL_FUNCTION(cmd_scan_bin) } #endif +VNA_SHELL_FUNCTION(cmd_tcxo) +{ + if (argc == 1) + si5351_set_tcxo(my_atoui(argv[0])); + shell_printf("tcxo = %u Hz\r\n", config._xtal_freq); +} + void set_marker_index(int m, int idx) { - if (m == MARKER_INVALID || idx < 0 || idx >= sweep_points) return; + if (m == MARKER_INVALID || (uint32_t)idx >= sweep_points) return; markers[m].index = idx; - markers[m].frequency = frequencies[idx]; + markers[m].frequency = getFrequency(idx); +} + +freq_t get_marker_frequency(int marker) +{ + if ((uint32_t)marker >= MARKERS_MAX) + return 0; + return markers[marker].frequency; } static void update_marker_index(void) { int m, idx; - uint32_t fstart = get_sweep_frequency(ST_START); - uint32_t fstop = get_sweep_frequency(ST_STOP); + freq_t fstart = get_sweep_frequency(ST_START); + freq_t fstop = get_sweep_frequency(ST_STOP); for (m = 0; m < MARKERS_MAX; m++) { - if (!markers[m].enabled) - continue; + // Update index for all markers !! uint32_t f = markers[m].frequency; if (f == 0) idx = markers[m].index; // Not need update index in no freq else if (f < fstart) idx = 0; else if (f >= fstop) idx = sweep_points-1; else { // Search frequency index for marker frequency -#if 1 +#if 0 for (idx = 1; idx < sweep_points; idx++) { if (frequencies[idx] <= f) continue; if (f < (frequencies[idx-1]/2 + frequencies[idx]/2)) idx--; // Correct closest idx @@ -1161,70 +1464,50 @@ update_marker_index(void) } static void -set_frequencies(uint32_t start, uint32_t stop, uint16_t points) -{ - uint32_t i; - uint32_t step = (points - 1); - uint32_t span = stop - start; - uint32_t delta = span / step; - uint32_t error = span % step; - uint32_t f = start, df = step>>1; - for (i = 0; i <= step; i++, f+=delta) { - frequencies[i] = f; - df+=error; - if (df >=step) { - f++; - df -= step; - } - } - // disable at out of sweep range - for (; i < POINTS_COUNT; i++) - frequencies[i] = 0; -} - -static void -update_frequencies(bool interpolate) +update_frequencies(void) { - uint32_t start, stop; - start = get_sweep_frequency(ST_START); - stop = get_sweep_frequency(ST_STOP); + freq_t start = get_sweep_frequency(ST_START); + freq_t stop = get_sweep_frequency(ST_STOP); set_frequencies(start, stop, sweep_points); - // operation_requested|= OP_FREQCHANGE; update_marker_index(); // set grid layout update_grid(); - if (interpolate) - cal_interpolate(); + // Update interpolation flag + if (needInterpolate(start, stop, sweep_points)) + cal_status|= CALSTAT_INTERPOLATED; + else + cal_status&= ~CALSTAT_INTERPOLATED; + + request_to_redraw(REDRAW_CAL_STATUS); + request_to_redraw(REDRAW_FREQUENCY | REDRAW_AREA); RESET_SWEEP; } void -set_sweep_frequency(int type, uint32_t freq) +set_sweep_frequency(int type, freq_t freq) { - int cal_applied = cal_status & CALSTAT_APPLY; - // Check frequency for out of bounds (minimum SPAN can be any value) if (type != ST_SPAN && freq < START_MIN) freq = START_MIN; if (freq > STOP_MAX) freq = STOP_MAX; - uint32_t center, span; + freq_t center, span; switch (type) { case ST_START: - config._mode &= ~VNA_MODE_CENTER_SPAN; + FREQ_STARTSTOP(); frequency0 = freq; // if start > stop then make start = stop if (frequency1 < freq) frequency1 = freq; break; case ST_STOP: - config._mode &= ~VNA_MODE_CENTER_SPAN; + FREQ_STARTSTOP() frequency1 = freq; // if start > stop then make start = stop if (frequency0 > freq) frequency0 = freq; break; case ST_CENTER: - config._mode |= VNA_MODE_CENTER_SPAN; + FREQ_CENTERSPAN(); center = freq; span = (frequency1 - frequency0)>>1; if (span > center - START_MIN) @@ -1235,7 +1518,7 @@ set_sweep_frequency(int type, uint32_t freq) frequency1 = center + span; break; case ST_SPAN: - config._mode |= VNA_MODE_CENTER_SPAN; + FREQ_CENTERSPAN(); center = (frequency0>>1) + (frequency1>>1); span = freq>>1; if (center < START_MIN + span) @@ -1246,23 +1529,26 @@ set_sweep_frequency(int type, uint32_t freq) frequency1 = center + span; break; case ST_CW: - config._mode |= VNA_MODE_CENTER_SPAN; + FREQ_CENTERSPAN(); frequency0 = freq; frequency1 = freq; break; } - update_frequencies(cal_applied); + update_frequencies(); } -uint32_t +void reset_sweep_frequency(void){ + frequency0 = cal_frequency0; + frequency1 = cal_frequency1; + sweep_points = cal_sweep_points; + update_frequencies(); +} + +freq_t get_sweep_frequency(int type) { - // Obsolete, ensure correct start/stop, start always must be < stop - if (frequency0 > frequency1) { - uint32_t t = frequency0; - frequency0 = frequency1; - frequency1 = t; - } +// Obsolete, ensure correct start/stop, start always must be < stop +// if (frequency0 > frequency1) SWAP(freq_t, frequency0, frequency1); switch (type) { case ST_START: return frequency0; case ST_STOP: return frequency1; @@ -1281,8 +1567,8 @@ VNA_SHELL_FUNCTION(cmd_sweep) } else if (argc > 3) { goto usage; } - uint32_t value0 = 0; - uint32_t value1 = 0; + freq_t value0 = 0; + freq_t value1 = 0; uint32_t value2 = 0; if (argc >= 1) value0 = my_atoui(argv[0]); if (argc >= 2) value1 = my_atoui(argv[1]); @@ -1365,6 +1651,7 @@ eterm_calc_es(void) // z=1/(jwc*z0) = 1/(2*pi*f*c*z0) Note: normalized with Z0 // s11ao = (z-1)/(z+1) = (1-1/z)/(1+1/z) = (1-jwcz0)/(1+jwcz0) // prepare 1/s11ao for effeiciency +#if 0 float c = 50e-15; //float c = 1.707e-12; float z0 = 50; @@ -1372,7 +1659,10 @@ eterm_calc_es(void) float sq = 1 + z*z; float s11aor = (1 - z*z) / sq; float s11aoi = 2*z / sq; - +#else + float s11aor = 1.0f; + float s11aoi = 0.0f; +#endif // S11mo’= S11mo - Ed // S11ms’= S11ms - Ed float s11or = cal_data[CAL_OPEN][i][0] - cal_data[ETERM_ED][i][0]; @@ -1384,9 +1674,9 @@ eterm_calc_es(void) float numi = s11si + s11oi * s11aor + s11or * s11aoi; float denomr = s11or - s11sr; float denomi = s11oi - s11si; - sq = denomr*denomr+denomi*denomi; - cal_data[ETERM_ES][i][0] = (numr*denomr + numi*denomi)/sq; - cal_data[ETERM_ES][i][1] = (numi*denomr - numr*denomi)/sq; + float d = denomr*denomr+denomi*denomi; + cal_data[ETERM_ES][i][0] = (numr*denomr + numi*denomi)/d; + cal_data[ETERM_ES][i][1] = (numi*denomr - numr*denomi)/d; } cal_status &= ~CALSTAT_OPEN; cal_status |= CALSTAT_ES; @@ -1430,7 +1720,7 @@ eterm_calc_et(void) float etr = cal_data[CAL_THRU][i][0] - cal_data[CAL_ISOLN][i][0]; float eti = cal_data[CAL_THRU][i][1] - cal_data[CAL_ISOLN][i][1]; float sq = etr*etr + eti*eti; - float invr = etr / sq; + float invr = etr / sq; float invi = -eti / sq; cal_data[ETERM_ET][i][0] = invr; cal_data[ETERM_ET][i][1] = invi; @@ -1508,233 +1798,195 @@ static void apply_error_term_at(int i) } #endif -static void apply_CH0_error_term_at(int i) -{ - // S11m' = S11m - Ed - // S11a = S11m' / (Er + Es S11m') - float s11mr = measured[0][i][0] - cal_data[ETERM_ED][i][0]; - float s11mi = measured[0][i][1] - cal_data[ETERM_ED][i][1]; - float err = cal_data[ETERM_ER][i][0] + s11mr * cal_data[ETERM_ES][i][0] - s11mi * cal_data[ETERM_ES][i][1]; - float eri = cal_data[ETERM_ER][i][1] + s11mr * cal_data[ETERM_ES][i][1] + s11mi * cal_data[ETERM_ES][i][0]; - float sq = err*err + eri*eri; - float s11ar = (s11mr * err + s11mi * eri) / sq; - float s11ai = (s11mi * err - s11mr * eri) / sq; - measured[0][i][0] = s11ar; - measured[0][i][1] = s11ai; -} - -static void apply_CH1_error_term_at(int i) +static void apply_CH0_error_term(float data[4], float c_data[CAL_TYPE_COUNT][2]) { - // CAUTION: Et is inversed for efficiency - // S21a = (S21m - Ex) * Et - float s21mr = measured[1][i][0] - cal_data[ETERM_EX][i][0]; - float s21mi = measured[1][i][1] - cal_data[ETERM_EX][i][1]; - // Not made CH1 correction by CH0 data - float s21ar = s21mr * cal_data[ETERM_ET][i][0] - s21mi * cal_data[ETERM_ET][i][1]; - float s21ai = s21mi * cal_data[ETERM_ET][i][0] + s21mr * cal_data[ETERM_ET][i][1]; - measured[1][i][0] = s21ar; - measured[1][i][1] = s21ai; + // S11m' = S11m - Ed + // S11a = S11m' / (Er + Es S11m') + float s11mr = data[0] - c_data[ETERM_ED][0]; + float s11mi = data[1] - c_data[ETERM_ED][1]; + float err = c_data[ETERM_ER][0] + s11mr * c_data[ETERM_ES][0] - s11mi * c_data[ETERM_ES][1]; + float eri = c_data[ETERM_ER][1] + s11mr * c_data[ETERM_ES][1] + s11mi * c_data[ETERM_ES][0]; + float sq = err*err + eri*eri; + data[0] = (s11mr * err + s11mi * eri) / sq; + data[1] = (s11mi * err - s11mr * eri) / sq; } -static void apply_edelay(void) +static void apply_CH1_error_term(float data[4], float c_data[CAL_TYPE_COUNT][2]) { - int i; - float real, imag; - float s, c; - uint16_t ch_mask = get_sweep_mask(); - for (i=0;i= ARRAY_COUNT(calibration_set)) return; - cal_status|=calibration_set[type].set_flag; + + // reset old calibration if frequency range/points not some + if (needInterpolate(frequency0, frequency1, sweep_points)){ + cal_status = 0; + cal_frequency0 = frequency0; + cal_frequency1 = frequency1; + cal_sweep_points = sweep_points; + } + cal_power = current_props._power; + cal_status&=calibration_set[type].clr_flag; + cal_status|=calibration_set[type].set_flag; dst = calibration_set[type].dst; src = calibration_set[type].src; -#else - switch (type) { -// type set data flag destination source reset flag - case CAL_LOAD: cal_status|= CALSTAT_LOAD; dst = CAL_LOAD; src = 0; break; - case CAL_OPEN: cal_status|= CALSTAT_OPEN; dst = CAL_OPEN; src = 0; cal_status&= ~(CALSTAT_ES); break; - case CAL_SHORT: cal_status|= CALSTAT_SHORT; dst = CAL_SHORT; src = 0; cal_status&= ~(CALSTAT_ER); break; - case CAL_THRU: cal_status|= CALSTAT_THRU; dst = CAL_THRU; src = 1; cal_status&= ~(CALSTAT_ET); break; - case CAL_ISOLN: cal_status|= CALSTAT_ISOLN; dst = CAL_ISOLN; src = 1; break; - default: - return; - } - // Disable calibration apply - cal_status&= ~(CALSTAT_APPLY); -#endif + // Run sweep for collect data (use minimum BANDWIDTH_30, or bigger if set) - uint8_t bw = config.bandwidth; // store current setting + uint8_t bw = config._bandwidth; // store current setting if (bw < BANDWIDTH_100) - config.bandwidth = BANDWIDTH_100; + config._bandwidth = BANDWIDTH_100; // Set MAX settings for sweep_points on calibrate // if (sweep_points != POINTS_COUNT) // set_sweep_points(POINTS_COUNT); - sweep(false, (src == 0) ? SWEEP_CH0_MEASURE : SWEEP_CH1_MEASURE); - config.bandwidth = bw; // restore + // Measure calibration data + sweep(false, (src == 0) ? SWEEP_CH0_MEASURE : SWEEP_CH1_MEASURE); // Copy calibration data memcpy(cal_data[dst], measured[src], sizeof measured[0]); - redraw_request |= REDRAW_CAL_STATUS; + + // Made average if need + int count = 1, i, j; + for (i = 1; i < count; i ++){ + sweep(false, (src == 0) ? SWEEP_CH0_MEASURE : SWEEP_CH1_MEASURE); + for (j = 0; j < sweep_points; j++){ + cal_data[dst][j][0]+=measured[src][j][0]; + cal_data[dst][j][1]+=measured[src][j][1]; + } + } + if (i!=1){ + for (j = 0; j < sweep_points; j++){ + cal_data[dst][j][0]/=i; + cal_data[dst][j][1]/=i; + } + } + + config._bandwidth = bw; // restore + request_to_redraw(REDRAW_CAL_STATUS); } void cal_done(void) { + // Set Load/Ed to default if not calculated if (!(cal_status & CALSTAT_LOAD)) eterm_set(ETERM_ED, 0.0, 0.0); + // Set Isoln/Ex to default if not measured + if (!(cal_status & CALSTAT_ISOLN)) + eterm_set(ETERM_EX, 0.0, 0.0); + //adjust_ed(); + // Precalculate Es and Er from Short and Open (and use Load/Ed data) if ((cal_status & CALSTAT_SHORT) && (cal_status & CALSTAT_OPEN)) { eterm_calc_es(); eterm_calc_er(-1); } else if (cal_status & CALSTAT_OPEN) { eterm_copy(CAL_SHORT, CAL_OPEN); + cal_status &=~ CALSTAT_OPEN; eterm_set(ETERM_ES, 0.0, 0.0); eterm_calc_er(1); } else if (cal_status & CALSTAT_SHORT) { eterm_set(ETERM_ES, 0.0, 0.0); - cal_status &= ~CALSTAT_SHORT; eterm_calc_er(-1); - } else if (!(cal_status & CALSTAT_ER)){ - eterm_set(ETERM_ER, 1.0, 0.0); - } else if (!(cal_status & CALSTAT_ES)) { - eterm_set(ETERM_ES, 0.0, 0.0); } - - if (!(cal_status & CALSTAT_ISOLN)) - eterm_set(ETERM_EX, 0.0, 0.0); - if (cal_status & CALSTAT_THRU) { + + // Apply Et + if (cal_status & CALSTAT_THRU) eterm_calc_et(); - } else if (!(cal_status & CALSTAT_ET)) { + + // Set other fields to default if not set + if (!(cal_status & CALSTAT_ET)) eterm_set(ETERM_ET, 1.0, 0.0); - } + if (!(cal_status & CALSTAT_ER)) + eterm_set(ETERM_ER, 1.0, 0.0); + if (!(cal_status & CALSTAT_ES)) + eterm_set(ETERM_ES, 0.0, 0.0); - cal_status |= CALSTAT_APPLY; - redraw_request |= REDRAW_CAL_STATUS; + cal_status|= CALSTAT_APPLY; + lastsaveid = NO_SAVE_SLOT; + request_to_redraw(REDRAW_CAL_STATUS); } -static void -cal_interpolate(void) -{ - const properties_t *src = caldata_reference(); - uint32_t i, j; +static void cal_interpolate(int idx, freq_t f, float data[CAL_TYPE_COUNT][2]){ int eterm; - if (src == NULL) - return; - - // Upload not interpolated if some - if (frequencies[0] == src->_frequency0 && frequencies[src->_sweep_points-1] == src->_frequency1){ - memcpy(current_props._cal_data, src->_cal_data, sizeof(src->_cal_data)); - cal_status = src->_cal_status; - redraw_request |= REDRAW_CAL_STATUS; - return; - } - uint32_t src_f = src->_frequency0; - // lower than start freq of src range - for (i = 0; i < sweep_points; i++) { - if (frequencies[i] >= src_f) - break; - - // fill cal_data at head of src range - for (eterm = 0; eterm < 5; eterm++) { - cal_data[eterm][i][0] = src->_cal_data[eterm][0][0]; - cal_data[eterm][i][1] = src->_cal_data[eterm][0][1]; + uint16_t src_points = cal_sweep_points - 1; + if (idx >= 0) + goto copy_point; + if (f <= cal_frequency0){ + idx = 0; + goto copy_point; + } + if (f >= cal_frequency1){ + idx = src_points; + goto copy_point; + } + // Search k1 + 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; + freq_t src_f0 = cal_frequency0 + (v ) / src_points; + freq_t src_f1 = cal_frequency0 + (v + span) / src_points; + + freq_t delta = src_f1 - src_f0; + // Not need interpolate + if (f == src_f0) goto copy_point; + + float k1 = (delta == 0) ? 0.0 : (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)) { + // f in prev harmonic, need extrapolate from prev 2 points + if (hf0 == si5351_get_harmonic_lvl(f)){ + if (idx < 1) goto copy_point; // point limit + idx--; + k1+= 1.0f; } - } - - // ReBuild src freq list - uint32_t src_points = (src->_sweep_points - 1); - uint32_t span = src->_frequency1 - src->_frequency0; - uint32_t delta = span / src_points; - uint32_t error = span % src_points; - uint32_t df = src_points>>1; - j = 0; - for (; i < sweep_points; i++) { - uint32_t f = frequencies[i]; - if (f == 0) goto interpolate_finish; - for (; j < src_points; j++) { - if (src_f <= f && f < src_f + delta) { - // found f between freqs at j and j+1 - float k1 = (delta == 0) ? 0.0 : (float)(f - src_f) / delta; - // avoid glitch between freqs in different harmonics mode - uint32_t idx = j; - if (si5351_get_harmonic_lvl(src_f) != si5351_get_harmonic_lvl(src_f+delta)) { - // f in prev harmonic, need extrapolate from prev 2 points - if (si5351_get_harmonic_lvl(f) == si5351_get_harmonic_lvl(src_f)){ - if (idx >= 1){ - idx--; k1+=1.0; - } - else // point limit - k1 = 0.0; - } - // f in next harmonic, need extrapolate from next 2 points - else { - if (idx < src_points){ - idx++; k1-=1.0; - } - else // point limit - k1 = 1.0; - } - } - float k0 = 1.0 - k1; - for (eterm = 0; eterm < 5; eterm++) { - cal_data[eterm][i][0] = src->_cal_data[eterm][idx][0] * k0 + src->_cal_data[eterm][idx+1][0] * k1; - cal_data[eterm][i][1] = src->_cal_data[eterm][idx][1] * k0 + src->_cal_data[eterm][idx+1][1] * k1; - } - break; - } - df+=error;if (df >=src_points) {src_f++;df -= src_points;} - src_f+=delta; + // f in next harmonic, need extrapolate from next 2 points + else { + if (idx >= src_points) goto copy_point; // point limit + idx++; + k1-= 1.0f; } - if (j == src_points) - break; } - - // upper than end freq of src range - for (; i < sweep_points; i++) { - // fill cal_data at tail of src - for (eterm = 0; eterm < 5; eterm++) { - cal_data[eterm][i][0] = src->_cal_data[eterm][src_points][0]; - cal_data[eterm][i][1] = src->_cal_data[eterm][src_points][1]; - } + // Interpolate by k1 + float k0 = 1.0 - k1; + 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; } -interpolate_finish: - cal_status = src->_cal_status | CALSTAT_INTERPOLATED; - redraw_request |= REDRAW_CAL_STATUS; + return; + // Direct point copy +copy_point: + for (eterm = 0; eterm < CAL_TYPE_COUNT; eterm++) { + data[eterm][0] = cal_data[eterm][idx][0]; + data[eterm][1] = cal_data[eterm][idx][1]; + } + return; } VNA_SHELL_FUNCTION(cmd_cal) @@ -1750,7 +2002,7 @@ VNA_SHELL_FUNCTION(cmd_cal) shell_printf("\r\n"); return; } - redraw_request|=REDRAW_CAL_STATUS; + request_to_redraw(REDRAW_CAL_STATUS); // 0 1 2 3 4 5 6 7 8 static const char cmd_cal_list[] = "load|open|short|thru|isoln|done|on|off|reset"; switch (get_str_index(argv[0], cmd_cal_list)) { @@ -1796,7 +2048,7 @@ VNA_SHELL_FUNCTION(cmd_save) if (id < 0 || id >= SAVEAREA_MAX) goto usage; caldata_save(id); - redraw_request |= REDRAW_CAL_STATUS; + request_to_redraw(REDRAW_CAL_STATUS); return; usage: @@ -1814,38 +2066,18 @@ VNA_SHELL_FUNCTION(cmd_recall) // Check for success if (load_properties(id)) shell_printf("Err, default load\r\n"); - redraw_request |= REDRAW_CAL_STATUS; return; usage: shell_printf("recall {id}\r\n"); } -static const struct { - const char *name; - uint16_t refpos; - float scale_unit; -} trace_info[MAX_TRACE_TYPE-1] = { - { "LOGMAG", NGRIDY-1, 10.0 }, - { "PHASE", NGRIDY/2, 90.0 }, - { "DELAY", NGRIDY/2, 1e-9 }, - { "SMITH", 0, 1.00 }, - { "POLAR", 0, 1.00 }, - { "LINEAR", 0, 0.125}, - { "SWR", 0, 0.25 }, - { "REAL", NGRIDY/2, 0.25 }, - { "IMAG", NGRIDY/2, 0.25 }, - { "R", NGRIDY/2, 100.0 }, - { "X", NGRIDY/2, 100.0 }, - { "Q", 0, 10.0 } -}; - static const char * const trc_channel_name[] = { - "CH0", "CH1" + "S11", "S21" }; const char *get_trace_typename(int t) { - return trace_info[trace[t].type].name; + return trace_info_list[trace[t].type].name; } const char *get_trace_chname(int t) @@ -1865,15 +2097,14 @@ void set_trace_type(int t, int type) if (trace[t].type != type && enabled) { trace[t].type = type; // Set default trace refpos - trace[t].refpos = trace_info[type].refpos; + trace[t].refpos = trace_info_list[type].refpos; // Set default trace scale - trace[t].scale = trace_info[type].scale_unit; + trace[t].scale = trace_info_list[type].scale_unit; force = TRUE; } - if (force) { + if (force) plot_into_index(measured); - request_to_redraw_grid(); - } + request_to_redraw(REDRAW_AREA); } void set_trace_channel(int t, int channel) @@ -1881,7 +2112,6 @@ void set_trace_channel(int t, int channel) if (trace[t].channel != channel) { trace[t].channel = channel; plot_into_index(measured); - request_to_redraw_grid(); } } @@ -1889,7 +2119,7 @@ void set_trace_scale(int t, float scale) { if (trace[t].scale != scale) { trace[t].scale = scale; - request_to_redraw_grid(); + plot_into_index(measured); } } @@ -1897,7 +2127,7 @@ void set_trace_refpos(int t, float refpos) { if (trace[t].refpos != refpos) { trace[t].refpos = refpos; - request_to_redraw_grid(); + plot_into_index(measured); } } @@ -1905,7 +2135,7 @@ void set_electrical_delay(float picoseconds) { if (electrical_delay != picoseconds) { electrical_delay = picoseconds; - request_to_redraw_grid(); + request_to_redraw(REDRAW_AREA); } } @@ -1925,8 +2155,8 @@ VNA_SHELL_FUNCTION(cmd_trace) return; } - if (strcmp(argv[0], "all") == 0 && - argc > 1 && strcmp(argv[1], "off") == 0) { + if (get_str_index(argv[0], "all") == 0 && + argc > 1 && get_str_index(argv[1], "off") == 0) { for (t = 0; t < TRACES_MAX; t++) set_trace_type(t, TRC_OFF); return; @@ -1945,7 +2175,7 @@ VNA_SHELL_FUNCTION(cmd_trace) #error "Trace type enum possibly changed, check cmd_trace function" #endif // enum TRC_LOGMAG, TRC_PHASE, TRC_DELAY, TRC_SMITH, TRC_POLAR, TRC_LINEAR, TRC_SWR, TRC_REAL, TRC_IMAG, TRC_R, TRC_X, TRC_Q, TRC_OFF - static const char cmd_type_list[] = "logmag|phase|delay|smith|polar|linear|swr|real|imag|r|x|q|off"; + static const char cmd_type_list[] = "logmag|phase|delay|smith|polar|linear|swr|real|imag|r|x|z|q|off"; int type = get_str_index(argv[1], cmd_type_list); if (type >= 0) { if (argc > 2) { @@ -1996,7 +2226,7 @@ VNA_SHELL_FUNCTION(cmd_marker) } return; } - redraw_request |= REDRAW_MARKER; + request_to_redraw(REDRAW_MARKER); // Marker on|off command int enable = get_str_index(argv[0], cmd_marker_list); if (enable >= 0) { // string found: 0 - on, 1 - off @@ -2038,6 +2268,20 @@ VNA_SHELL_FUNCTION(cmd_marker) "marker [%s]\r\n", cmd_marker_list, cmd_marker_smith); } +#if 0 +VNA_SHELL_FUNCTION(cmd_grid) +{ + if (argc != 1) { + shell_printf("grid %s\r\n", config._mode&VNA_MODE_SHOW_GRID ? "on" : "off"); + return; + } + if (my_atoi(argv[0])) + config._mode|= VNA_MODE_SHOW_GRID; + else + config._mode&=~VNA_MODE_SHOW_GRID; +} +#endif + VNA_SHELL_FUNCTION(cmd_touchcal) { (void)argc; @@ -2051,7 +2295,7 @@ VNA_SHELL_FUNCTION(cmd_touchcal) shell_printf("touch cal params: "); for (i = 0; i < 4; i++) { - shell_printf("%d ", config.touch_cal[i]); + shell_printf("%d ", config._touch_cal[i]); } shell_printf("\r\n"); } @@ -2069,8 +2313,7 @@ VNA_SHELL_FUNCTION(cmd_frequencies) (void)argc; (void)argv; for (i = 0; i < sweep_points; i++) { - if (frequencies[i] == 0) break; - shell_printf("%u\r\n", frequencies[i]); + shell_printf("%u\r\n", getFrequency(i)); } } @@ -2078,23 +2321,23 @@ VNA_SHELL_FUNCTION(cmd_frequencies) static void set_domain_mode(int mode) // accept DOMAIN_FREQ or DOMAIN_TIME { - if (mode != (domain_mode & DOMAIN_MODE)) { - domain_mode = (domain_mode & ~DOMAIN_MODE) | (mode & DOMAIN_MODE); - redraw_request |= REDRAW_FREQUENCY | REDRAW_MARKER; - uistat.lever_mode = LM_MARKER; + if (mode != (props_mode & DOMAIN_MODE)) { + props_mode = (props_mode & ~DOMAIN_MODE) | (mode & DOMAIN_MODE); + request_to_redraw(REDRAW_FREQUENCY | REDRAW_MARKER); + lever_mode = LM_MARKER; } } -static void -set_timedomain_func(int func) // accept TD_FUNC_LOWPASS_IMPULSE, TD_FUNC_LOWPASS_STEP or TD_FUNC_BANDPASS +static inline void +set_timedomain_func(uint32_t func) // accept TD_FUNC_LOWPASS_IMPULSE, TD_FUNC_LOWPASS_STEP or TD_FUNC_BANDPASS { - domain_mode = (domain_mode & ~TD_FUNC) | (func & TD_FUNC); + props_mode = (props_mode & ~TD_FUNC) | func; } -static void -set_timedomain_window(int func) // accept TD_WINDOW_MINIMUM/TD_WINDOW_NORMAL/TD_WINDOW_MAXIMUM +static inline void +set_timedomain_window(uint32_t func) // accept TD_WINDOW_MINIMUM/TD_WINDOW_NORMAL/TD_WINDOW_MAXIMUM { - domain_mode = (domain_mode & ~TD_WINDOW) | (func & TD_WINDOW); + props_mode = (props_mode & ~TD_WINDOW) | func; } VNA_SHELL_FUNCTION(cmd_transform) @@ -2262,8 +2505,8 @@ VNA_SHELL_FUNCTION(cmd_stat) // if (mins < p[i+1]) mins = p[i+1]; // if (maxs > p[i+1]) maxs = p[i+1]; } - stat.rms[0] = sqrtf(acc0 / count); - stat.rms[1] = sqrtf(acc1 / count); + stat.rms[0] = vna_sqrtf(acc0 / count); + stat.rms[1] = vna_sqrtf(acc1 / count); stat.ave[0] = ave0; stat.ave[1] = ave1; shell_printf("Ch: %d\r\n", ch); @@ -2305,10 +2548,10 @@ VNA_SHELL_FUNCTION(cmd_vbat) VNA_SHELL_FUNCTION(cmd_vbat_offset) { if (argc != 1) { - shell_printf("%d\r\n", config.vbat_offset); + shell_printf("%d\r\n", config._vbat_offset); return; } - config.vbat_offset = (int16_t)my_atoi(argv[0]); + config._vbat_offset = (int16_t)my_atoi(argv[0]); } #endif @@ -2365,7 +2608,7 @@ VNA_SHELL_FUNCTION(cmd_info) VNA_SHELL_FUNCTION(cmd_color) { uint32_t color; - int i; + uint16_t i; if (argc != 2) { shell_printf("usage: color {id} {rgb24}\r\n"); for (i=0; i < MAX_PALETTE; i++) { @@ -2375,13 +2618,13 @@ VNA_SHELL_FUNCTION(cmd_color) } return; } - i = my_atoi(argv[0]); + i = my_atoui(argv[0]); if (i >= MAX_PALETTE) return; color = RGBHEX(my_atoui(argv[1])); - config.lcd_palette[i] = color; + config._lcd_palette[i] = color; // Redraw all - redraw_request|= REDRAW_AREA|REDRAW_CAL_STATUS|REDRAW_FREQUENCY; + request_to_redraw(REDRAW_CLRSCR | REDRAW_AREA | REDRAW_CAL_STATUS | REDRAW_BATTERY | REDRAW_FREQUENCY); } #endif @@ -2470,7 +2713,7 @@ VNA_SHELL_FUNCTION(cmd_usart_cfg) VNA_SHELL_FUNCTION(cmd_usart) { uint32_t time = 2000; // 200ms wait answer by default - if (argc == 0 || argc > 2 || (config._mode & VNA_MODE_SERIAL)) return; + if (argc == 0 || argc > 2 || (VNA_mode & VNA_MODE_SERIAL)) return; if (argc == 2) time = my_atoui(argv[1])*10; sdWriteTimeout(&SD1, (uint8_t *)argv[0], strlen(argv[0]), time); sdWriteTimeout(&SD1, (uint8_t *)VNA_SHELL_NEWLINE_STR, sizeof(VNA_SHELL_NEWLINE_STR)-1, time); @@ -2486,15 +2729,11 @@ VNA_SHELL_FUNCTION(cmd_usart) #ifndef __USE_SD_CARD__ #error "Need enable SD card support __USE_SD_CARD__ in nanovna.h, for use ENABLE_SD_CARD_CMD" #endif -// Fat file system work area (at the end of spi_buffer) -static FATFS *fs_volume = (FATFS *)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS)); -// FatFS file object (at the end of spi_buffer) -static FIL *fs_file = ( FIL*)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS) - sizeof(FIL)); static FRESULT cmd_sd_card_mount(void){ const FRESULT res = f_mount(fs_volume, "", 1); if (res != FR_OK) - shell_printf("error: card not mounted\r\n"); + shell_printf("err: no card\r\n"); return res; } @@ -2506,11 +2745,16 @@ VNA_SHELL_FUNCTION(cmd_sd_list) DIR dj; FILINFO fno; FRESULT res; - shell_printf("sd_list:\r\n"); - res = cmd_sd_card_mount(); - if (res != FR_OK) + if (cmd_sd_card_mount() != FR_OK) return; - res = f_findfirst(&dj, &fno, "", "*.*"); + char *search; + switch (argc){ + case 0: search = "*.*";break; + case 1: search = argv[0];break; + default: shell_printf("usage: sd_list {pattern}\r\n"); return; + } + shell_printf("sd_list:\r\n"); + res = f_findfirst(&dj, &fno, "", search); while (res == FR_OK && fno.fname[0]) { shell_printf("%s %u\r\n", fno.fname, fno.fsize); @@ -2519,42 +2763,48 @@ VNA_SHELL_FUNCTION(cmd_sd_list) f_closedir(&dj); } -VNA_SHELL_FUNCTION(cmd_sd_readfile) +VNA_SHELL_FUNCTION(cmd_sd_read) { - FRESULT res; char *buf = (char *)spi_buffer; - if (argc < 1) + if (argc != 1) { - shell_printf("usage: sd_readfile {filename}\r\n"); + shell_printf("usage: sd_read {filename}\r\n"); return; } const char *filename = argv[0]; - shell_printf("sd_readfile: %s\r\n", filename); - res = cmd_sd_card_mount(); - if (res != FR_OK) + if (cmd_sd_card_mount() != FR_OK) return; - res = f_open(fs_file, filename, FA_OPEN_EXISTING | FA_READ); - if (res != FR_OK) - { - shell_printf("error: %s not opened\r\n", filename); + if (f_open(fs_file, filename, FA_OPEN_EXISTING | FA_READ) != FR_OK){ + shell_printf("err: no file\r\n"); return; } - + // shell_printf("sd_read: %s\r\n", filename); // number of bytes to follow (file size) - const uint32_t filesize = f_size(fs_file); + uint32_t filesize = f_size(fs_file); streamWrite(shell_stream, (void *)&filesize, 4); - + UINT size = 0; // file data (send all data from file) - while (1) - { - UINT size = 0; - res = f_read(fs_file, buf, 512, &size); - if (res != FR_OK || size == 0) - break; + while (f_read(fs_file, buf, 512, &size) == FR_OK && size > 0) streamWrite(shell_stream, (void *)buf, size); + + f_close(fs_file); + return; +} + +VNA_SHELL_FUNCTION(cmd_sd_delete) +{ + FRESULT res; + if (argc != 1) { + shell_printf("usage: sd_delete {filename}\r\n"); + return; } - res = f_close(fs_file); + if (cmd_sd_card_mount() != FR_OK) + return; + const char *filename = argv[0]; + res = f_unlink(filename); + shell_printf("delete: %s %s\r\n", filename, res == FR_OK ? "OK" : "err"); + return; } #endif @@ -2571,7 +2821,12 @@ typedef struct { // Some commands can executed only in sweep thread, not in main cycle #define CMD_WAIT_MUTEX 1 +// Command execution need in sweep thread, and need break sweep for run #define CMD_BREAK_SWEEP 2 +// Command can run in shell thread (if sweep thread process UI, not sweep) +#define CMD_RUN_IN_UI 4 +// Command can run in load script +#define CMD_RUN_IN_LOAD 8 static const VNAShellCommand commands[] = { @@ -2581,30 +2836,31 @@ static const VNAShellCommand commands[] = #endif {"data" , cmd_data , 0}, {"frequencies" , cmd_frequencies , 0}, - {"freq" , cmd_freq , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"sweep" , cmd_sweep , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"power" , cmd_power , 0}, + {"freq" , cmd_freq , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, + {"sweep" , cmd_sweep , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, + {"power" , cmd_power , CMD_RUN_IN_LOAD}, #ifdef USE_VARIABLE_OFFSET - {"offset" , cmd_offset , CMD_WAIT_MUTEX}, + {"offset" , cmd_offset , CMD_WAIT_MUTEX|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, #endif - {"bandwidth" , cmd_bandwidth , 0}, + {"bandwidth" , cmd_bandwidth , CMD_RUN_IN_LOAD}, #ifdef __USE_RTC__ - {"time" , cmd_time , 0}, + {"time" , cmd_time , CMD_RUN_IN_UI}, #endif #ifdef ENABLE_SD_CARD_CMD - {"sd_list" , cmd_sd_list , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"sd_readfile" , cmd_sd_readfile , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, + { "sd_list", cmd_sd_list, CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI}, + { "sd_read", cmd_sd_read, CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI}, + { "sd_delete", cmd_sd_delete, CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI}, #endif #ifdef __VNA_ENABLE_DAC__ - {"dac" , cmd_dac , 0}, + {"dac" , cmd_dac , CMD_RUN_IN_LOAD}, #endif - {"saveconfig" , cmd_saveconfig , 0}, - {"clearconfig" , cmd_clearconfig , 0}, + {"saveconfig" , cmd_saveconfig , CMD_RUN_IN_LOAD}, + {"clearconfig" , cmd_clearconfig , CMD_RUN_IN_LOAD}, #ifdef ENABLED_DUMP_COMMAND - {"dump" , cmd_dump , 0}, + {"dump" , cmd_dump , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, #endif #ifdef ENABLE_PORT_COMMAND - {"port" , cmd_port , 0}, + {"port" , cmd_port , CMD_RUN_IN_LOAD}, #endif #ifdef ENABLE_STAT_COMMAND {"stat" , cmd_stat , CMD_WAIT_MUTEX}, @@ -2620,37 +2876,42 @@ 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}, - {"resume" , cmd_resume , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, + {"pause" , cmd_pause , CMD_WAIT_MUTEX|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}, {"cal" , cmd_cal , CMD_WAIT_MUTEX}, - {"save" , cmd_save , 0}, - {"recall" , cmd_recall , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"trace" , cmd_trace , 0}, - {"marker" , cmd_marker , 0}, - {"edelay" , cmd_edelay , 0}, - {"capture" , cmd_capture , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"vbat" , cmd_vbat , 0}, - {"reset" , cmd_reset , 0}, + {"save" , cmd_save , CMD_RUN_IN_LOAD}, + {"recall" , cmd_recall , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, + {"trace" , cmd_trace , CMD_RUN_IN_LOAD}, + {"marker" , cmd_marker , CMD_RUN_IN_LOAD}, +// {"grid" , cmd_grid , CMD_RUN_IN_LOAD}, + {"edelay" , cmd_edelay , CMD_RUN_IN_LOAD}, + {"capture" , cmd_capture , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI}, + {"vbat" , cmd_vbat , CMD_RUN_IN_LOAD}, + {"tcxo" , cmd_tcxo , CMD_RUN_IN_LOAD}, + {"reset" , cmd_reset , CMD_RUN_IN_LOAD}, +#ifdef __USE_SMOOTH__ + {"smooth" , cmd_smooth , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, +#endif #ifdef __USE_SERIAL_CONSOLE__ #ifdef ENABLE_USART_COMMAND - {"usart_cfg" , cmd_usart_cfg , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, - {"usart" , cmd_usart , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP}, + {"usart_cfg" , cmd_usart_cfg , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, + {"usart" , cmd_usart , CMD_WAIT_MUTEX|CMD_BREAK_SWEEP|CMD_RUN_IN_UI|CMD_RUN_IN_LOAD}, #endif #endif #ifdef ENABLE_VBAT_OFFSET_COMMAND - {"vbat_offset" , cmd_vbat_offset , 0}, + {"vbat_offset" , cmd_vbat_offset , CMD_RUN_IN_LOAD}, #endif #ifdef ENABLE_TRANSFORM_COMMAND - {"transform" , cmd_transform , 0}, + {"transform" , cmd_transform , CMD_RUN_IN_LOAD}, #endif - {"threshold" , cmd_threshold , 0}, + {"threshold" , cmd_threshold , CMD_RUN_IN_LOAD}, {"help" , cmd_help , 0}, #ifdef ENABLE_INFO_COMMAND {"info" , cmd_info , 0}, #endif {"version" , cmd_version , 0}, #ifdef ENABLE_COLOR_COMMAND - {"color" , cmd_color , 0}, + {"color" , cmd_color , CMD_RUN_IN_LOAD}, #endif #ifdef ENABLE_I2C_COMMAND {"i2c" , cmd_i2c , CMD_WAIT_MUTEX}, @@ -2701,7 +2962,7 @@ VNA_SHELL_FUNCTION(cmd_help) #endif // Before start process command from shell, need select input stream -#define PREPARE_STREAM shell_stream = (config._mode&VNA_MODE_SERIAL) ? (BaseSequentialStream *)&SD1 : (BaseSequentialStream *)&SDU1; +#define PREPARE_STREAM shell_stream = (VNA_mode & VNA_MODE_SERIAL) ? (BaseSequentialStream *)&SD1 : (BaseSequentialStream *)&SDU1; // Update Serial connection speed and settings void shell_update_speed(void){ @@ -2720,7 +2981,7 @@ static bool usb_IsActive(void){ void shell_reset_console(void){ // Reset I/O queue over USB (for USB need also connect/disconnect) if (usb_IsActive()){ - if (config._mode & VNA_MODE_SERIAL) + if (VNA_mode & VNA_MODE_SERIAL) sduDisconnectI(&SDU1); else sduConfigureHookI(&SDU1); @@ -2733,7 +2994,7 @@ void shell_reset_console(void){ // Check active connection for Shell static bool shell_check_connect(void){ // Serial connection always active - if (config._mode & VNA_MODE_SERIAL) + if (VNA_mode & VNA_MODE_SERIAL) return true; // USB connection can be USB_SUSPENDED return usb_IsActive(); @@ -2769,7 +3030,7 @@ static void shell_init_connection(void){ #else // Only USB console, shell_stream always on USB -#define PREPARE_STREAM +#define PREPARE_STREAM shell_stream = (BaseSequentialStream *)&SDU1; // Check connection as Active, if no suspend input static bool shell_check_connect(void){ @@ -2797,47 +3058,69 @@ static void shell_init_connection(void){ /* * Set I/O stream SDU1 for shell */ - shell_stream = (BaseSequentialStream *)&SDU1; + PREPARE_STREAM; } #endif +static const VNAShellCommand *VNAShell_parceLine(char *line){ + // Parse and execute line + char *lp = line, *ep; + shell_nargs = 0; + while (*lp != 0) { + // Skipping white space and tabs at string begin. + while (*lp == ' ' || *lp == '\t') lp++; + // If an argument starts with a double quote then its delimiter is another quote, else + // delimiter is white space. + ep = (*lp == '"') ? strpbrk(++lp, "\"") : strpbrk(lp, " \t"); + // Store in args string + shell_args[shell_nargs++] = lp; + // Stop, end of input string + if ((lp = ep) == NULL) break; + // Argument limits check + if (shell_nargs > VNA_SHELL_MAX_ARGUMENTS) { + shell_printf("too many arguments, max " define_to_STR(VNA_SHELL_MAX_ARGUMENTS) "" VNA_SHELL_NEWLINE_STR); + return NULL; + } + // Set zero at the end of string and continue check + *lp++ = 0; + } + if (shell_nargs){ + const VNAShellCommand *scp; + for (scp = commands; scp->sc_name != NULL; scp++) + if (get_str_index(scp->sc_name, shell_args[0]) == 0) + return scp; + } + return NULL; +} + // // Read command line from shell_stream // static int VNAShell_readLine(char *line, int max_size) { - // Read line from input stream + // send backspace, space for erase, backspace again + char backspace[] = {0x08, 0x20, 0x08, 0x00}; uint8_t c; // Prepare I/O for shell_stream PREPARE_STREAM; - char *ptr = line; - while (1) { - // Return 0 only if stream not active - if (streamRead(shell_stream, &c, 1) == 0) - return 0; + uint16_t j = 0; + // Return 0 only if stream not active + while (streamRead(shell_stream, &c, 1)) { // Backspace or Delete - if (c == 8 || c == 0x7f) { - if (ptr != line) { - static const char backspace[] = {0x08, 0x20, 0x08, 0x00}; - shell_printf(backspace); - ptr--; - } + if (c == 0x08 || c == 0x7f) { + if (j > 0) {shell_printf(backspace); j--;} continue; } // New line (Enter) if (c == '\r') { shell_printf(VNA_SHELL_NEWLINE_STR); - *ptr = 0; + line[j] = 0; return 1; } - // Others (skip) - if (c < 0x20) - continue; - // Store - if (ptr < line + max_size - 1) { - streamPut(shell_stream, c); // Echo - *ptr++ = (char)c; - } + // Others (skip) or too long - skip + if (c < ' ' || j >= max_size - 1) continue; + streamPut(shell_stream, c); // Echo + line[j++] = (char)c; } return 0; } @@ -2847,51 +3130,77 @@ static int VNAShell_readLine(char *line, int max_size) // static void VNAShell_executeLine(char *line) { - // Parse and execute line - char *lp = line, *ep; - shell_nargs = 0; - -// DEBUG_LOG(0, lp); // debug console log - while (*lp != 0) { - // Skipping white space and tabs at string begin. - while (*lp == ' ' || *lp == '\t') lp++; - // If an argument starts with a double quote then its delimiter is another quote, else - // delimiter is white space. - ep = (*lp == '"') ? strpbrk(++lp, "\"") : strpbrk(lp, " \t"); - // Store in args string - shell_args[shell_nargs++] = lp; - // Stop, end of input string - if ((lp = ep) == NULL) break; - // Argument limits check - if (shell_nargs > VNA_SHELL_MAX_ARGUMENTS) { - shell_printf("too many arguments, max " define_to_STR( - VNA_SHELL_MAX_ARGUMENTS) "" VNA_SHELL_NEWLINE_STR); +// DEBUG_LOG(0, line); // debug console log + // Execute line + const VNAShellCommand *scp = VNAShell_parceLine(line); + if (scp) { + 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 + 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 + do { + chThdSleepMilliseconds(10); + } while (shell_function); return; } - // Set zero at the end of string and continue check - *lp++ = 0; + scp->sc_function(shell_nargs - 1, &shell_args[1]); +// DEBUG_LOG(10, "ok"); + return; } - if (shell_nargs == 0) return; - // Execute line - const VNAShellCommand *scp; - for (scp = commands; scp->sc_name != NULL; scp++) { - if (strcmp(scp->sc_name, shell_args[0]) == 0) { - if (scp->flags & CMD_WAIT_MUTEX) { - shell_function = scp->sc_function; - if (scp->flags & CMD_BREAK_SWEEP) operation_requested|=OP_CONSOLE; - // Wait execute command in sweep thread - do { - chThdSleepMilliseconds(100); - } while (shell_function); - } else { - scp->sc_function(shell_nargs - 1, &shell_args[1]); + if (**shell_args) // unknown command (not empty), ignore + shell_printf("%s?" VNA_SHELL_NEWLINE_STR, shell_args[0]); +} + +#ifdef __SD_CARD_LOAD__ +#ifndef __USE_SD_CARD__ +#error "Need enable SD card support __USE_SD_CARD__ in nanovna.h, for use ENABLE_SD_CARD_CMD" +#endif +bool sd_card_load_config(void){ + // Mount card + if (f_mount(fs_volume, "", 1) != FR_OK) + return FALSE; + + if (f_open(fs_file, "config.ini", FA_OPEN_EXISTING | FA_READ) != FR_OK) + return FALSE; + + // Disable shell output + shell_stream = NULL; + char *buf = (char *)spi_buffer; + UINT size = 0; + + uint16_t j = 0, i; + while (f_read(fs_file, buf, 512, &size) == FR_OK && size > 0){ + i = 0; + while (i < size) { + uint8_t c = buf[i++]; + // New line (Enter) + if (c == '\r') { +// shell_line[j ] = '\r'; +// shell_line[j+1] = '\n'; +// shell_line[j+2] = 0; +// shell_printf(shell_line); + shell_line[j] = 0; j = 0; + const VNAShellCommand *scp = VNAShell_parceLine(shell_line); + if (scp && (scp->flags&CMD_RUN_IN_LOAD)) + scp->sc_function(shell_nargs - 1, &shell_args[1]); + continue; } -// DEBUG_LOG(10, "ok"); - return; + // Others (skip) + if (c < 0x20) continue; + // Store + if (j < VNA_SHELL_MAX_LENGTH - 1) + shell_line[j++] = (char)c; } } - shell_printf("%s?" VNA_SHELL_NEWLINE_STR, shell_args[0]); + f_close(fs_file); + PREPARE_STREAM; + return TRUE; } +#endif #ifdef VNA_SHELL_THREAD static THD_WORKING_AREA(waThread2, /* cmd_* max stack size + alpha */442); @@ -2912,9 +3221,6 @@ THD_FUNCTION(myshellThread, p) /* * I2C bus settings */ -// Define i2c bus speed, add predefined for 400k, 600k, 900k -#define STM32_I2C_SPEED 600 - #if STM32_I2C1_CLOCK == 8 // STM32_I2C1SW == STM32_I2C1SW_HSI (HSI=8MHz) #if STM32_I2C_SPEED == 400 // 400kHz @ HSI 8MHz (Use 26.4.10 I2C_TIMINGR register configuration examples from STM32 RM0091 Reference manual) #define STM32_I2C_TIMINGR STM32_TIMINGR_PRESC(0U) |\ @@ -2974,15 +3280,10 @@ int main(void) halInit(); chSysInit(); -#ifdef USE_VARIABLE_OFFSET - generate_DSP_Table(FREQUENCY_OFFSET); -#endif - /* * SPI bus and LCD Initialize */ - ili9341_init(); - show_version(0); + lcd_init(); /* * Restore config @@ -2994,6 +3295,12 @@ int main(void) */ load_properties(0); +/* + * Set frequency offset + */ +#ifdef USE_VARIABLE_OFFSET + si5351_set_frequency_offset(IF_OFFSET); +#endif /* * Init Shell console connection data */ @@ -3031,26 +3338,18 @@ int main(void) i2sStartExchange(&I2SD2); /* - * UI (menu, touch, buttons) and plot initialize + * 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) */ - ui_init(); - //Initialize graph plotting - plot_init(); - redraw_frame(); +#ifdef __USE_SD_CARD__ + disk_initialize(0); +#endif /* * Starting DAC1 driver, setting up the output pin as analog as suggested by the Reference Manual. */ #ifdef __VNA_ENABLE_DAC__ - dacStart(&DACD2, &dac1cfg1); - dacPutChannelX(&DACD2, 0, config.dac_value); // Set config DAC value -#endif - -/* - * Set LCD display brightness - */ -#ifdef __LCD_BRIGHTNESS__ - lcd_setBrightness(config._brightness); + dac_init(); #endif /* @@ -3118,26 +3417,25 @@ void hard_fault_handler_c(uint32_t *sp) uint32_t psr = sp[7]; int y = 0; int x = 20; - char buf[16]; - ili9341_set_background(LCD_BG_COLOR); - ili9341_set_foreground(LCD_FG_COLOR); - plot_printf(buf, sizeof(buf), "SP 0x%08x", (uint32_t)sp);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R0 0x%08x", r0);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R1 0x%08x", r1);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R2 0x%08x", r2);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R3 0x%08x", r3);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R4 0x%08x", r4);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R5 0x%08x", r5);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R6 0x%08x", r6);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R7 0x%08x", r7);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R8 0x%08x", r8);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R9 0x%08x", r9);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R10 0x%08x", r10);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R11 0x%08x", r11);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "R12 0x%08x", r12);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "LR 0x%08x", lr);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "PC 0x%08x", pc);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); - plot_printf(buf, sizeof(buf), "PSR 0x%08x", psr);ili9341_drawstring(buf, x, y+=FONT_STR_HEIGHT); + lcd_set_background(LCD_BG_COLOR); + lcd_set_foreground(LCD_FG_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); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R2 0x%08x", r2); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R3 0x%08x", r3); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R4 0x%08x", r4); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R5 0x%08x", r5); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R6 0x%08x", r6); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R7 0x%08x", r7); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R8 0x%08x", r8); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R9 0x%08x", r9); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R10 0x%08x", r10); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R11 0x%08x", r11); + lcd_printf(x, y+=FONT_STR_HEIGHT, "R12 0x%08x", r12); + lcd_printf(x, y+=FONT_STR_HEIGHT, "LR 0x%08x", lr); + lcd_printf(x, y+=FONT_STR_HEIGHT, "PC 0x%08x", pc); + lcd_printf(x, y+=FONT_STR_HEIGHT, "PSR 0x%08x", psr); shell_printf("===================================\r\n"); #else @@ -3147,6 +3445,7 @@ void hard_fault_handler_c(uint32_t *sp) } } // For new compilers -//void _exit(int){} +//void _exit(int x){(void)x;} //void _kill(void){} +//int _write (int file, char *data, int len) {(void)file; (void)data; return len;} //void _getpid(void){} diff --git a/mcuconf_F303.h b/mcuconf_F303.h new file mode 100644 index 0000000..2d10142 --- /dev/null +++ b/mcuconf_F303.h @@ -0,0 +1,233 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef MCUCONF_H +#define MCUCONF_H + +/* + * STM32F3xx drivers configuration. + * The following settings override the default settings present in + * the various device driver implementation headers. + * Note that the settings for each driver only have effect if the whole + * driver is enabled in halconf.h. + * + * IRQ priorities: + * 15...0 Lowest...Highest. + * + * DMA priorities: + * 0...3 Lowest...Highest. + */ + +#define STM32F3xx_MCUCONF + +/* + * HAL driver system settings. + */ +#define STM32_NO_INIT FALSE +#define STM32_PVD_ENABLE FALSE +#define STM32_PLS STM32_PLS_LEV0 +#define STM32_HSI_ENABLED FALSE +#define STM32_LSI_ENABLED TRUE +#define STM32_HSE_ENABLED TRUE +#define STM32_LSE_ENABLED FALSE +#define STM32_SW STM32_SW_PLL +#define STM32_PLLSRC STM32_PLLSRC_HSE +#define STM32_PREDIV_VALUE 1 +#define STM32_PLLMUL_VALUE 9 +#define STM32_HPRE STM32_HPRE_DIV1 +#define STM32_PPRE1 STM32_PPRE1_DIV2 +//#define STM32_PPRE2 STM32_PPRE2_DIV1 +#define STM32_MCOSEL STM32_MCOSEL_PLLDIV2 +#define STM32_ADC12PRES STM32_ADC12PRES_DIV1 +//#define STM32_ADC34PRES STM32_ADC34PRES_DIV1 +#define STM32_USART1SW STM32_USART1SW_PCLK +//#define STM32_USART2SW STM32_USART2SW_PCLK +//#define STM32_USART3SW STM32_USART3SW_PCLK +#define STM32_I2C1SW STM32_I2C1SW_SYSCLK +//#define STM32_I2C2SW STM32_I2C2SW_SYSCLK +#define STM32_TIM1SW STM32_TIM1SW_PCLK2 +#define STM32_TIM8SW STM32_TIM8SW_PCLK2 +#define STM32_RTCSEL STM32_RTCSEL_LSI +#define STM32_USB_CLOCK_REQUIRED TRUE +#define STM32_USBPRE STM32_USBPRE_DIV1P5 + +/* + * ADC driver system settings. + */ +#define STM32_ADC_USE_ADC1 TRUE +#define STM32_ADC_USE_ADC2 TRUE +#define STM32_ADC_USE_ADC3 FALSE +#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(1, 1) +#define STM32_ADC_ADC2_DMA_STREAM STM32_DMA_STREAM_ID(2, 1) +//#define STM32_ADC_ADC3_DMA_STREAM STM32_DMA_STREAM_ID(2, 5) +//#define STM32_ADC_ADC4_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) +#define STM32_ADC_ADC12_DMA_PRIORITY 2 +//#define STM32_ADC_ADC34_DMA_PRIORITY 2 +#define STM32_ADC_ADC12_IRQ_PRIORITY 2 +//#define STM32_ADC_ADC34_IRQ_PRIORITY 5 +#define STM32_ADC_ADC12_DMA_IRQ_PRIORITY 2 +//#define STM32_ADC_ADC34_DMA_IRQ_PRIORITY 5 +//#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_ADCCK +//#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV2 +#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 +//#define STM32_ADC_ADC34_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 +#define STM32_ADC_DUAL_MODE FALSE + +/* + * CAN driver system settings. + */ +#define STM32_CAN_USE_CAN1 FALSE +#define STM32_CAN_CAN1_IRQ_PRIORITY 11 + +/* + * DAC driver system settings. + */ +#define STM32_DAC_DUAL_MODE FALSE +#define STM32_DAC_USE_DAC1_CH1 TRUE +#define STM32_DAC_USE_DAC1_CH2 TRUE +#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 10 +#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 10 +#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2 +#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2 + +/* + * EXT driver system settings. + */ +#define STM32_EXT_EXTI0_1_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI2_3_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI4_15_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI16_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI17_IRQ_PRIORITY 3 +#define STM32_EXT_EXTI21_22_IRQ_PRIORITY 3 + +#define STM32_DISABLE_EXTI2122_HANDLER TRUE + +/* + * GPT driver system settings. + */ +#define STM32_GPT_USE_TIM1 FALSE +#define STM32_GPT_USE_TIM2 FALSE +#define STM32_GPT_USE_TIM3 TRUE +#define STM32_GPT_USE_TIM4 FALSE +#define STM32_GPT_TIM1_IRQ_PRIORITY 2 +#define STM32_GPT_TIM2_IRQ_PRIORITY 2 +#define STM32_GPT_TIM3_IRQ_PRIORITY 2 +#define STM32_GPT_TIM4_IRQ_PRIORITY 2 + +/* + * I2C driver system settings. + */ +#define STM32_I2C_USE_I2C1 TRUE +#define STM32_I2C_USE_I2C2 FALSE +#define STM32_I2C_BUSY_TIMEOUT 50 +#define STM32_I2C_I2C1_IRQ_PRIORITY 3 +#define STM32_I2C_I2C2_IRQ_PRIORITY 3 +#define STM32_I2C_USE_DMA TRUE +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") + +/* + * I2S driver system settings. + */ +#define STM32_I2S_USE_SPI1 FALSE +#define STM32_I2S_USE_SPI2 TRUE +#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_SLAVE | \ + STM32_I2S_MODE_RX ) +#define STM32_I2S_SPI1_IRQ_PRIORITY 2 +#define STM32_I2S_SPI2_IRQ_PRIORITY 2 +#define STM32_I2S_SPI1_DMA_PRIORITY 1 +#define STM32_I2S_SPI2_DMA_PRIORITY 1 +#define STM32_I2S_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) +#define STM32_I2S_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3) +#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) +#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) +#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure") + +/* + * ICU driver system settings. + */ +#define STM32_ICU_USE_TIM1 FALSE +#define STM32_ICU_USE_TIM2 FALSE +#define STM32_ICU_USE_TIM3 FALSE +#define STM32_ICU_TIM1_IRQ_PRIORITY 3 +#define STM32_ICU_TIM2_IRQ_PRIORITY 3 +#define STM32_ICU_TIM3_IRQ_PRIORITY 3 + +/* + * PWM driver system settings. + */ +#define STM32_PWM_USE_ADVANCED FALSE +#define STM32_PWM_USE_TIM1 FALSE +#define STM32_PWM_USE_TIM2 FALSE +#define STM32_PWM_USE_TIM3 FALSE +#define STM32_PWM_TIM1_IRQ_PRIORITY 3 +#define STM32_PWM_TIM2_IRQ_PRIORITY 3 +#define STM32_PWM_TIM3_IRQ_PRIORITY 3 + +/* + * SERIAL driver system settings. + */ +#define STM32_SERIAL_USE_USART1 TRUE +#define STM32_SERIAL_USE_USART2 FALSE +#define STM32_SERIAL_USART1_PRIORITY 3 +#define STM32_SERIAL_USART2_PRIORITY 3 + +/* + * SPI driver system settings. + */ +#define STM32_SPI_USE_SPI1 TRUE +#define STM32_SPI_USE_SPI2 FALSE +#define STM32_SPI_USE_SPI3 FALSE +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#define STM32_SPI_SPI1_IRQ_PRIORITY 2 +#define STM32_SPI_SPI2_IRQ_PRIORITY 2 +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") + +/* + * ST driver system settings. + */ +#define STM32_ST_IRQ_PRIORITY 2 +#define STM32_ST_USE_TIMER 2 + +/* + * UART driver system settings. + */ +#define STM32_UART_USE_USART1 FALSE +#define STM32_UART_USE_USART2 FALSE +#define STM32_UART_USART1_IRQ_PRIORITY 3 +#define STM32_UART_USART2_IRQ_PRIORITY 3 +#define STM32_UART_USART1_DMA_PRIORITY 0 +#define STM32_UART_USART2_DMA_PRIORITY 0 +#define STM32_UART_USART3_DMA_PRIORITY 0 +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") + +/* + * USB driver system settings. + */ +#define STM32_USB_USE_USB1 TRUE +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#define STM32_USB_USB1_LP_IRQ_PRIORITY 3 + +/* + * WDG driver system settings. + */ +#define STM32_WDG_USE_IWDG FALSE + +#endif /* MCUCONF_H */ diff --git a/nanovna.h b/nanovna.h index 2af333e..c9a81ed 100644 --- a/nanovna.h +++ b/nanovna.h @@ -20,35 +20,86 @@ */ #include "ch.h" -// Need enable HAL_USE_SPI in halconf.h +// Define LCD display driver and size +//#define LCD_DRIVER_ILI9341 +//#define LCD_320x240 + +//#define LCD_DRIVER_ST7796S +//#define LCD_480x320 + +// Enable DMA mode for send data to LCD (Need enable HAL_USE_SPI in halconf.h) #define __USE_DISPLAY_DMA__ // LCD or hardware allow change brightness, add menu item for this //#define __LCD_BRIGHTNESS__ // Use DAC (in H4 used for brightness used DAC, so need enable __LCD_BRIGHTNESS__ for it) //#define __VNA_ENABLE_DAC__ // Allow enter to DFU from menu or command -#define __DFU_SOFTWARE_MODE__ +//#define __DFU_SOFTWARE_MODE__ // Add RTC clock support -//#define __USE_RTC__ +#define __USE_RTC__ // Add SD card support, req enable RTC (additional settings for file system see FatFS lib ffconf.h) -//#define __USE_SD_CARD__ +#define __USE_SD_CARD__ // If enabled serial in halconf.h, possible enable serial console control -//#define __USE_SERIAL_CONSOLE__ +#define __USE_SERIAL_CONSOLE__ +// Add show y grid line values option +#define __USE_GRID_VALUES__ +// Use build in table for sin/cos calculation, allow save a lot of flash space (this table also use for FFT), max sin/cos error = 4e-7 +#define __VNA_USE_MATH_TABLES__ +// Use custom fast/compact approximation for some math functions in calculations (vna_ ...), use it carefully +#define __USE_VNA_MATH__ +// Use cache for window function used by FFT (but need FFT_SIZE*sizeof(float) RAM) +//#define USE_FFT_WINDOW_BUFFER +// Enable data smooth option +#define __USE_SMOOTH__ +// Enable optional change digit separator for locales (dot or comma, need for correct work some external software) +#define __DIGIT_SEPARATOR__ +// Use table for frequency list (if disabled use real time calc) +//#define __USE_FREQ_TABLE__ +// Enable DSP instruction (support only by Cortex M4 and higher) +#ifdef ARM_MATH_CM4 +#define __USE_DSP__ +#endif +// Add measure module option (allow made some measure calculations on data) +#define __VNA_MEASURE_MODULE__ +// Add Z normalization feature +//#define __VNA_Z_RENORMALIZATION__ + +/* + * Submodules defines + */ +// If SD card enabled +#ifdef __USE_SD_CARD__ +// Allow run commands from SD card (config.ini in root) +#define __SD_CARD_LOAD__ +// Allow dump firmware to SD card +#define __SD_CARD_DUMP_FIRMWARE__ +#endif + +// If measure module enabled, add submodules +#ifdef __VNA_MEASURE_MODULE__ // Add LC match function #define __USE_LC_MATCHING__ -// Use buildin table for sin/cos calculation, allow save a lot of flash space (this table also use for FFT), max sin/cos error = 4e-7 -#define __VNA_USE_MATH_TABLES__ +// Enable Series measure option +//#define __S21_MEASURE__ +// Enable S11 cable measure option +//#define __S11_CABLE_MEASURE__ +#endif /* * main.c */ - // Minimum frequency set #define START_MIN 800 // Maximum frequency set #define STOP_MAX 2000000000U // Frequency threshold (max frequency for si5351, harmonic mode after) -#define FREQUENCY_THRESHOLD 290000100U +#define FREQUENCY_THRESHOLD 280000100U +// XTAL frequency on si5351 +#define XTALFREQ 26000000U +// Define i2c bus speed, add predefined for 400k, 600k, 900k +#define STM32_I2C_SPEED 900 +// Define default src impedance for xtal calculations +#define MEASURE_DEFAULT_R 50.0f // Define ADC sample rate in kilobyte (can be 48k, 96k, 192k, 384k) //#define AUDIO_ADC_FREQ_K 768 @@ -66,16 +117,20 @@ // 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_MENU #if AUDIO_ADC_FREQ_K == 768 +#define FREQUENCY_OFFSET_STEP 16000 // For 768k ADC (16k step for 48 samples) -#define FREQUENCY_IF_K 12 +#define FREQUENCY_IF_K 8 // only 96 samples and variable table +//#define FREQUENCY_IF_K 12 // only 192 samples and variable table //#define FREQUENCY_IF_K 16 //#define FREQUENCY_IF_K 32 //#define FREQUENCY_IF_K 48 //#define FREQUENCY_IF_K 64 #elif AUDIO_ADC_FREQ_K == 384 +#define FREQUENCY_OFFSET_STEP 4000 // For 384k ADC (8k step for 48 samples) //#define FREQUENCY_IF_K 8 #define FREQUENCY_IF_K 12 // only 96 samples and variable table @@ -85,6 +140,7 @@ //#define FREQUENCY_IF_K 32 #elif AUDIO_ADC_FREQ_K == 192 +#define FREQUENCY_OFFSET_STEP 4000 // For 192k ADC (sin_cos table in dsp.c generated for 8k, 12k, 16k, 20k, 24k if change need create new table ) //#define FREQUENCY_IF_K 8 #define FREQUENCY_IF_K 12 @@ -94,6 +150,7 @@ //#define FREQUENCY_IF_K 28 #elif AUDIO_ADC_FREQ_K == 96 +#define FREQUENCY_OFFSET_STEP 2000 // For 96k ADC (sin_cos table in dsp.c generated for 6k, 8k, 10k, 12k if change need create new table ) //#define FREQUENCY_IF_K 6 //#define FREQUENCY_IF_K 8 @@ -101,6 +158,7 @@ #define FREQUENCY_IF_K 12 #elif AUDIO_ADC_FREQ_K == 48 +#define FREQUENCY_OFFSET_STEP 1000 // For 48k ADC (sin_cos table in dsp.c generated for 3k, 4k, 5k, 6k, if change need create new table ) //#define FREQUENCY_IF_K 3 //#define FREQUENCY_IF_K 4 @@ -119,11 +177,15 @@ #define SPEED_OF_LIGHT 299792458 // pi const -#define VNA_PI 3.14159265358979323846 +#define VNA_PI 3.14159265358979323846f +#define VNA_TWOPI 6.28318530717958647692f // Maximum sweep point count (limit by flash and RAM size) #define POINTS_COUNT 101 +// Define frequency range (can be unsigned) +typedef uint32_t freq_t; + // Optional sweep point (in UI menu) #if POINTS_COUNT >=401 #define POINTS_SET_COUNT 5 @@ -144,13 +206,13 @@ #endif extern float measured[2][POINTS_COUNT][2]; -extern uint32_t frequencies[POINTS_COUNT]; -#define CAL_LOAD 0 -#define CAL_OPEN 1 -#define CAL_SHORT 2 -#define CAL_THRU 3 -#define CAL_ISOLN 4 +#define CAL_TYPE_COUNT 5 +#define CAL_LOAD 0 +#define CAL_OPEN 1 +#define CAL_SHORT 2 +#define CAL_THRU 3 +#define CAL_ISOLN 4 #define CALSTAT_LOAD (1<<0) #define CALSTAT_OPEN (1<<1) @@ -171,29 +233,12 @@ extern uint32_t frequencies[POINTS_COUNT]; #define ETERM_ET 3 /* error term transmission tracking */ #define ETERM_EX 4 /* error term isolation */ -#define DOMAIN_MODE (1<<0) -#define DOMAIN_FREQ (0<<0) -#define DOMAIN_TIME (1<<0) -#define TD_FUNC (0b11<<1) -#define TD_FUNC_BANDPASS (0b00<<1) -#define TD_FUNC_LOWPASS_IMPULSE (0b01<<1) -#define TD_FUNC_LOWPASS_STEP (0b10<<1) -#define TD_WINDOW (0b11<<3) -#define TD_WINDOW_NORMAL (0b00<<3) -#define TD_WINDOW_MINIMUM (0b01<<3) -#define TD_WINDOW_MAXIMUM (0b10<<3) -// L/C match enable option -#define TD_LC_MATH (1<<5) - #if POINTS_COUNT <= 256 #define FFT_SIZE 256 #elif POINTS_COUNT <= 512 #define FFT_SIZE 512 #endif -// Return sin/cos value, angle have range 0.0 to 1.0 (0 is 0 degree, 1 is 360 degree) -void vna_sin_cos(float angle, float * pSinVal, float * pCosVal); - void cal_collect(uint16_t type); void cal_done(void); @@ -202,29 +247,70 @@ enum stimulus_type { ST_START=0, ST_STOP, ST_CENTER, ST_SPAN, ST_CW }; -void set_marker_index(int m, int idx); -void set_sweep_frequency(int type, uint32_t frequency); -uint32_t get_sweep_frequency(int type); +freq_t getFrequency(uint16_t idx); +freq_t getFrequencyStep(void); + +void set_marker_index(int m, int idx); +freq_t get_marker_frequency(int marker); + +void reset_sweep_frequency(void); +void set_sweep_frequency(int type, freq_t frequency); +freq_t get_sweep_frequency(int type); + void set_bandwidth(uint16_t bw_count); uint32_t get_bandwidth_frequency(uint16_t bw_freq); + void set_power(uint8_t value); +void set_smooth_factor(uint8_t factor); +uint8_t get_smooth_factor(void); + int32_t my_atoi(const char *p); uint32_t my_atoui(const char *p); -double my_atof(const char *p); +float my_atof(const char *p); void toggle_sweep(void); void load_default_properties(void); int load_properties(uint32_t id); void set_sweep_points(uint16_t points); +bool sd_card_load_config(void); + #define SWEEP_ENABLE 0x01 #define SWEEP_ONCE 0x02 #define SWEEP_BINARY 0x08 +#define SWEEP_UI_MODE 0x80 extern uint8_t sweep_mode; extern const char *info_about[]; +/* + * Measure timings for si5351 generator, after ready + */ +// Enable si5351 timing command, used for debug align times +//#define ENABLE_SI5351_TIMINGS +#if defined(NANOVNA_F303) +// Generator ready delays, values in us +#define DELAY_BAND_1_2 US2ST( 90) // Delay for bands 1-2 +#define DELAY_BAND_3_4 US2ST( 160) // 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_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) +#define DELAY_RESET_PLL_AFTER 4000 // 4000 (0 for disabled) +#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( 160) // 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(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_AFTER 2000 // 6 4000 (0 for disabled) +#endif + /* * dsp.c */ @@ -302,69 +388,53 @@ void tlv320aic3204_set_gain(uint8_t lgain, uint8_t rgain); void tlv320aic3204_select(uint8_t channel); void tlv320aic3204_write_reg(uint8_t page, uint8_t reg, uint8_t data); +/* + * vna_math.c + */ +#include "vna_math.h" + /* * plot.c */ // LCD display size settings +#ifdef LCD_320x240 // 320x240 display plot definitions #define LCD_WIDTH 320 #define LCD_HEIGHT 240 // Define maximum distance in pixel for pickup marker (can be bigger for big displays) #define MARKER_PICKUP_DISTANCE 20 // Used marker size settings -#define _USE_BIG_MARKER_ 0 +#define _USE_BIG_MARKER_ 1 // Used font settings -#define _USE_FONT_ 0 - - -extern const uint8_t x5x7_bits[]; -#define FONT_START_CHAR 0x17 -#define FONT_MAX_WIDTH 7 -#define FONT_WIDTH 5 -#define FONT_GET_HEIGHT 7 -#define FONT_STR_HEIGHT 8 -#define FONT_GET_DATA(ch) ( &x5x7_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]) -#define FONT_GET_WIDTH(ch) (8-(x5x7_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]&7)) - -extern const uint8_t x7x11b_bits []; -#define bFONT_START_CHAR 0x17 -#define bFONT_MAX_WIDTH 8 -#define bFONT_WIDTH 7 -#define bFONT_GET_HEIGHT 11 -#define bFONT_STR_HEIGHT 11 -#define bFONT_GET_DATA(ch) ( &x7x11b_bits[(ch-bFONT_START_CHAR)*bFONT_GET_HEIGHT]) -#define bFONT_GET_WIDTH(ch) (8-(x7x11b_bits[(ch-bFONT_START_CHAR)*bFONT_GET_HEIGHT]&7)) - -#if 0 -extern const uint8_t x10x14_bits[]; -#define FONT_START_CHAR 0x17 -#define FONT_MAX_WIDTH 14 -#define FONT_WIDTH 11 -#define FONT_GET_HEIGHT 14 -#define FONT_STR_HEIGHT 16 -#define FONT_GET_DATA(ch) ( &x10x14_bits[(ch-FONT_START_CHAR)*2*FONT_GET_HEIGHT ]) -#define FONT_GET_WIDTH(ch) (14-(x10x14_bits[(ch-FONT_START_CHAR)*2*FONT_GET_HEIGHT+1]&0x7)) -#endif - -extern const uint8_t numfont16x22[]; -#define NUM_FONT_GET_WIDTH 16 -#define NUM_FONT_GET_HEIGHT 22 -#define NUM_FONT_GET_DATA(ch) (&numfont16x22[ch*2*NUM_FONT_GET_HEIGHT]) +#define _USE_FONT_ 1 // Offset of plot area (size of additional info at left side) +#if _USE_FONT_== 1 +#define OFFSETX 15 +// WIDTH better be n*(POINTS_COUNT-1) +#define WIDTH 295 +// HEIGHT = 8*GRIDY +#define HEIGHT 224 +#define FREQUENCIES_XPOS2 176 +#define MENU_BUTTON_WIDTH 84 +#elif #define OFFSETX 10 -#define OFFSETY 0 - // WIDTH better be n*(POINTS_COUNT-1) #define WIDTH 300 // HEIGHT = 8*GRIDY #define HEIGHT 232 +#define FREQUENCIES_XPOS2 206 +#define MENU_BUTTON_WIDTH 66 +#endif +#define OFFSETY 0 + + //#define NGRIDY 10 #define NGRIDY 8 #define FREQUENCIES_XPOS1 OFFSETX -#define FREQUENCIES_XPOS2 206 + #define FREQUENCIES_XPOS3 135 #define FREQUENCIES_YPOS (LCD_HEIGHT-FONT_GET_HEIGHT) @@ -374,25 +444,22 @@ extern const uint8_t numfont16x22[]; // Need for reference marker draw #define CELLOFFSETX 5 -#define bCELLOFFSETX 7 #define AREA_WIDTH_NORMAL (CELLOFFSETX + WIDTH + 1 + 4) #define AREA_HEIGHT_NORMAL ( HEIGHT + 1) -#define GRID_X_TEXT (AREA_WIDTH_NORMAL - 7*5) - // Smith/polar chart #define P_CENTER_X (CELLOFFSETX + WIDTH/2) #define P_CENTER_Y (HEIGHT/2) #define P_RADIUS (HEIGHT/2) -extern int16_t area_width; -extern int16_t area_height; - // Maximum menu buttons count -#define MENU_BUTTON_MAX 8 +#define MENU_BUTTON_MAX 16 +#define MENU_BUTTON_MIN 8 +// Menu buttons y offset +#define MENU_BUTTON_Y_OFFSET 1 // Menu buttons size -#define MENU_BUTTON_WIDTH 80 -#define MENU_BUTTON_HEIGHT (LCD_HEIGHT/8-1) + +#define MENU_BUTTON_HEIGHT(n) (AREA_HEIGHT_NORMAL/(n)) #define MENU_BUTTON_BORDER 1 #define KEYBOARD_BUTTON_BORDER 2 @@ -402,6 +469,14 @@ extern int16_t area_height; // Height of numerical input field (at bottom) #define NUM_INPUT_HEIGHT 32 +// Battery icon position +#define BATTERY_ICON_POSX 1 +#define BATTERY_ICON_POSY 1 + +// Calibration text coordinates +#define CALIBRATION_INFO_POSX 0 +#define CALIBRATION_INFO_POSY 100 + // On screen keyboard button size // Use full screen keyboard #if 1 @@ -419,15 +494,153 @@ extern int16_t area_height; #define KP_GET_Y(posy) ((posy)*KP_HEIGHT + 12 ) #endif -#ifdef __USE_LC_MATCHING__ +#endif // end 320x240 display plot definitions + +#ifdef LCD_480x320 // 480x320 display definitions +#define LCD_WIDTH 480 +#define LCD_HEIGHT 320 + +// Define maximum distance in pixel for pickup marker (can be bigger for big displays) +#define MARKER_PICKUP_DISTANCE 30 +// Used marker size settings +#define _USE_BIG_MARKER_ 1 +// Used font settings +#define _USE_FONT_ 1 + +// Offset of plot area (size of additional info at left side) +#define OFFSETX 15 +#define OFFSETY 0 + +// WIDTH better be n*(POINTS_COUNT-1) +#define WIDTH 455 +// HEIGHT = 8*GRIDY +#define HEIGHT 304 + +//#define NGRIDY 10 +#define NGRIDY 8 + +#define FREQUENCIES_XPOS1 OFFSETX +#define FREQUENCIES_XPOS2 330 +#define FREQUENCIES_XPOS3 200 +#define FREQUENCIES_YPOS (LCD_HEIGHT-FONT_GET_HEIGHT) + +// GRIDX calculated depends from frequency span +//#define GRIDY 29 +#define GRIDY (HEIGHT / NGRIDY) + +// Need for reference marker draw +#define CELLOFFSETX 5 +#define AREA_WIDTH_NORMAL (CELLOFFSETX + WIDTH + 1 + 4) +#define AREA_HEIGHT_NORMAL ( HEIGHT + 1) + +// Smith/polar chart +#define P_CENTER_X (CELLOFFSETX + WIDTH/2) +#define P_CENTER_Y (HEIGHT/2) +#define P_RADIUS (HEIGHT/2) + +// Maximum menu buttons count +#define MENU_BUTTON_MAX 16 +#define MENU_BUTTON_MIN 8 +// Menu buttons y offset +#define MENU_BUTTON_Y_OFFSET 1 +// Menu buttons size +#define MENU_BUTTON_WIDTH 84 +#define MENU_BUTTON_HEIGHT(n) (AREA_HEIGHT_NORMAL/(n)) +#define MENU_BUTTON_BORDER 1 +#define KEYBOARD_BUTTON_BORDER 2 + +// Define message box width +#define MESSAGE_BOX_WIDTH 180 + +// Height of numerical input field (at bottom) +#define NUM_INPUT_HEIGHT 32 + +// Battery icon position +#define BATTERY_ICON_POSX 3 +#define BATTERY_ICON_POSY 2 + +// Calibration text coordinates +#define CALIBRATION_INFO_POSX 0 +#define CALIBRATION_INFO_POSY 100 + +// On screen keyboard button size +// Use full screen keyboard +#if 1 +#define KP_WIDTH ((LCD_WIDTH) / 4) // numeric keypad button width +#define KP_HEIGHT ((LCD_HEIGHT - NUM_INPUT_HEIGHT) / 4) // numeric keypad button height +// Key x, y position (0 - 15) on screen +#define KP_GET_X(posx) ((posx) * KP_WIDTH) // numeric keypad left +#define KP_GET_Y(posy) ((posy) * KP_HEIGHT) // numeric keypad top +#else +// Use less size keyboard +#define KP_WIDTH 64 +#define KP_HEIGHT 64 +// Key x, y position (0 - 15) on screen +#define KP_GET_X(posx) ((posx)*KP_WIDTH + (LCD_WIDTH-128-KP_WIDTH*4)) +#define KP_GET_Y(posy) ((posy)*KP_HEIGHT + 20 ) +#endif + +#endif // end 480x320 display plot definitions + +/* + * Font size defines + */ +#if _USE_FONT_ == 0 +extern const uint8_t x5x7_bits[]; +#define FONT_START_CHAR 0x17 +#define FONT_MAX_WIDTH 7 +#define FONT_WIDTH 5 +#define FONT_GET_HEIGHT 7 +#define FONT_STR_HEIGHT 8 +#define FONT_GET_DATA(ch) ( &x5x7_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]) +#define FONT_GET_WIDTH(ch) (8-(x5x7_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]&7)) + +#elif _USE_FONT_ == 1 +extern const uint8_t x7x11b_bits[]; +#define FONT_START_CHAR 0x17 +#define FONT_MAX_WIDTH 8 +#define FONT_WIDTH 7 +#define FONT_GET_HEIGHT 11 +#define FONT_STR_HEIGHT 11 +#define FONT_GET_DATA(ch) ( &x7x11b_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]) +#define FONT_GET_WIDTH(ch) (8-(x7x11b_bits[(ch-FONT_START_CHAR)*FONT_GET_HEIGHT]&7)) + +#elif _USE_FONT_ == 2 +extern const uint8_t x10x14_bits[]; +#define FONT_START_CHAR 0x17 +#define FONT_MAX_WIDTH 14 +#define FONT_WIDTH 11 +#define FONT_GET_HEIGHT 14 +#define FONT_STR_HEIGHT 16 +#define FONT_GET_DATA(ch) ( &x10x14_bits[(ch-FONT_START_CHAR)*2*FONT_GET_HEIGHT ]) +#define FONT_GET_WIDTH(ch) (14-(x10x14_bits[(ch-FONT_START_CHAR)*2*FONT_GET_HEIGHT+1]&0x7)) +#endif + +extern const uint8_t numfont16x22[]; +#define NUM_FONT_GET_WIDTH 16 +#define NUM_FONT_GET_HEIGHT 22 +#define NUM_FONT_GET_DATA(ch) (&numfont16x22[ch*2*NUM_FONT_GET_HEIGHT]) + +/* + * LC match text output settings + */ +#ifdef __VNA_MEASURE_MODULE__ // X and Y offset to L/C match text - #define STR_LC_MATH_X (OFFSETX + 0) -// Better be aligned by cell - #define STR_LC_MATH_Y (OFFSETY + 64) -// 1/3 Width of text (need 3 column for data) - #define STR_LC_MATH_WIDTH (bFONT_WIDTH * 10) + #define STR_MEASURE_X (OFFSETX + 0) +// Better be aligned by cell (cell height = 32) +#if _USE_FONT_== 0 + #define STR_MEASURE_Y (OFFSETY + 64) +#else +#define STR_MEASURE_Y (OFFSETY + 128) +#endif +// 1/3 Width of text (use 3 column for data) + #define STR_MEASURE_WIDTH (FONT_WIDTH * 10) // String Height (need 2 + 0..4 string) - #define STR_LC_MATH_HEIGHT (bFONT_STR_HEIGHT + 2) + #define STR_MEASURE_HEIGHT (FONT_STR_HEIGHT + 1) +#endif + +#ifdef __USE_GRID_VALUES__ +#define GRID_X_TEXT (WIDTH - 5*FONT_WIDTH) #endif // Additional chars in fonts @@ -437,45 +650,111 @@ extern int16_t area_height; #define S_LARROW "\032" // hex 0x1A #define S_RARROW "\033" // hex 0x1B #define S_PI "\034" // hex 0x1C -#define S_MICRO "\035" // hex 0x1D +#define S_MICRO '\035' // hex 0x1D #define S_OHM "\036" // hex 0x1E #define S_DEGREE "\037" // hex 0x1F // Max palette indexes in config -#define MAX_PALETTE 24 +#define MAX_PALETTE 32 // trace #define MAX_TRACE_TYPE 13 enum trace_type { - TRC_LOGMAG=0, TRC_PHASE, TRC_DELAY, TRC_SMITH, TRC_POLAR, TRC_LINEAR, TRC_SWR, TRC_REAL, TRC_IMAG, TRC_R, TRC_X, TRC_Q, TRC_OFF + TRC_LOGMAG=0, TRC_PHASE, TRC_DELAY, TRC_SMITH, TRC_POLAR, /*TRC_ADMIT,*/ TRC_LINEAR, TRC_SWR, TRC_REAL, TRC_IMAG, TRC_R, TRC_X, TRC_Z, TRC_Q, TRC_OFF }; // Mask for define rectangular plot -#define RECTANGULAR_GRID_MASK ((1<, -// LINMAG: SCALE, REFPOS, REFVAL -// SWR: SCALE, REFPOS, REFVAL -// Electrical Delay -// Phase +#define RECTANGULAR_GRID_MASK ((1<>16)&0xFF), (((d)>>8)&0xFF), ((d)&0xFF) #define _BMP32(d) (((d)>>24)&0xFF), (((d)>>16)&0xFF), (((d)>>8)&0xFF), ((d)&0xFF) -void ili9341_init(void); -void ili9341_test(int mode); -void ili9341_bulk(int x, int y, int w, int h); -void ili9341_bulk_continue(int x, int y, int w, int h); -void ili9341_bulk_finish(void); -void ili9341_fill(int x, int y, int w, int h); -pixel_t *ili9341_get_cell_buffer(void); - -void ili9341_set_foreground(uint16_t fg_idx); -void ili9341_set_background(uint16_t bg_idx); -void ili9341_clear_screen(void); -void ili9341_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *bitmap); -void ili9341_drawchar(uint8_t ch, int x, int y); -void ili9341_drawstring(const char *str, int x, int y); -void ili9341_drawstring_7x13(const char *str, int x, int y); -void ili9341_drawstringV(const char *str, int x, int y); -int ili9341_drawchar_size(uint8_t ch, int x, int y, uint8_t size); -void ili9341_drawstring_size(const char *str, int x, int y, uint8_t size); -void ili9341_drawfont(uint8_t ch, int x, int y); -void ili9341_read_memory(int x, int y, int w, int h, uint16_t* out); -void ili9341_line(int x0, int y0, int x1, int y1); +void lcd_init(void); +void lcd_bulk(int x, int y, int w, int h); +void lcd_fill(int x, int y, int w, int h); + +#if DISPLAY_CELL_BUFFER_COUNT == 1 +#define lcd_get_cell_buffer() spi_buffer +#define lcd_bulk_continue lcd_bulk +#define lcd_bulk_finish() {} +#else +pixel_t *lcd_get_cell_buffer(void); // get buffer for cell render +void lcd_bulk_continue(int x, int y, int w, int h); // send data to display, in DMA mode use it, no wait DMA complete +void lcd_bulk_finish(void); // wait DMA complete (need call at end) +#endif + +void lcd_set_foreground(uint16_t fg_idx); +void lcd_set_background(uint16_t bg_idx); +void lcd_clear_screen(void); +void lcd_blitBitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *bitmap); +void lcd_drawchar(uint8_t ch, int x, int y); +#if 0 +void lcd_drawstring(int16_t x, int16_t y, const char *str); +#else +// use printf for draw string +#define lcd_drawstring lcd_printf +#endif +int lcd_printf(int16_t x, int16_t y, const char *fmt, ...); +void lcd_drawstringV(const char *str, int x, int y); +int lcd_drawchar_size(uint8_t ch, int x, int y, uint8_t size); +void lcd_drawstring_size(const char *str, int x, int y, uint8_t size); +void lcd_drawfont(uint8_t ch, int x, int y); +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); uint32_t lcd_send_command(uint8_t cmd, uint8_t len, const uint8_t *data); void lcd_setBrightness(uint16_t b); @@ -735,6 +1053,17 @@ void lcd_setBrightness(uint16_t b); #ifdef __USE_SD_CARD__ #include "../FatFs/ff.h" #include "../FatFs/diskio.h" + +// Buffers for SD card use spi_buffer +#if SPI_BUFFER_SIZE < 2048 +#error "SPI_BUFFER_SIZE for SD card support need size >= 2048" +#else +// Fat file system work area (at the end of spi_buffer) +#define fs_volume (FATFS *)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS)) +// FatFS file object (at the end of spi_buffer) +#define fs_file ( FIL*)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS) - sizeof(FIL)) +#endif + void testLog(void); // debug log #endif @@ -772,6 +1101,23 @@ void rtc_set_time(uint32_t dr, uint32_t tr); /* * flash.c */ +#if defined(NANOVNA_F303) +// For STM32F303xC CPU setting +#define FLASH_START_ADDRESS 0x08000000 +#define FLASH_TOTAL_SIZE (256*1024) + +#define FLASH_PAGESIZE 0x800 + +#define SAVEAREA_MAX 7 + +// Depend from config_t size, should be aligned by FLASH_PAGESIZE +#define SAVE_CONFIG_SIZE 0x00000800 +// Depend from properties_t size, should be aligned by FLASH_PAGESIZE +#define SAVE_PROP_CONFIG_SIZE 0x00004000 +#else +// For STM32F072xB CPU setting +#define FLASH_START_ADDRESS 0x08000000 +#define FLASH_TOTAL_SIZE (128*1024) #define FLASH_PAGESIZE 0x800 @@ -781,43 +1127,67 @@ void rtc_set_time(uint32_t dr, uint32_t tr); #define SAVE_CONFIG_SIZE 0x00000800 // Depend from properties_t size, should be aligned by FLASH_PAGESIZE #define SAVE_PROP_CONFIG_SIZE 0x00001800 -// Save config_t and properties_t flash area (see flash7 : org = 0x08018000, len = 32k from *.ld settings) +#endif + +// 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 -// len = SAVE_CONFIG_SIZE + SAVEAREA_MAX * SAVE_PROP_CONFIG_SIZE 0x00008000 32k -#define SAVE_CONFIG_ADDR 0x08018000 #define SAVE_PROP_CONFIG_ADDR (SAVE_CONFIG_ADDR + SAVE_CONFIG_SIZE) -#define SAVE_FULL_AREA_SIZE (SAVE_CONFIG_SIZE + SAVEAREA_MAX * SAVE_PROP_CONFIG_SIZE) -#define CONFIG_MAGIC 0x434f4e45 /* 'CONF' */ +#define CONFIG_MAGIC 0x434f4e53 /* 'CONF' */ +#define PROPS_MAGIC 0x434f4e51 /* 'CONF' */ +#define NO_SAVE_SLOT ((uint16_t)(-1)) extern uint16_t lastsaveid; -#define frequency0 current_props._frequency0 -#define frequency1 current_props._frequency1 -#define sweep_points current_props._sweep_points -#define cal_status current_props._cal_status -#define cal_data current_props._cal_data -#define electrical_delay current_props._electrical_delay - -#define trace current_props._trace -#define markers current_props._markers -#define active_marker current_props._active_marker -#define domain_mode current_props._domain_mode -#define velocity_factor current_props._velocity_factor +#define frequency0 current_props._frequency0 +#define frequency1 current_props._frequency1 +#define cal_frequency0 current_props._cal_frequency0 +#define cal_frequency1 current_props._cal_frequency1 +#define var_freq current_props._var_freq +#define sweep_points current_props._sweep_points +#define cal_sweep_points current_props._cal_sweep_points +#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 velocity_factor current_props._velocity_factor #define marker_smith_format current_props._marker_smith_format +#define trace current_props._trace +#define current_trace current_props._current_trace +#define markers current_props._markers +#define active_marker current_props._active_marker +#define previous_marker current_props._previous_marker + +#define props_mode current_props._mode +#define domain_window (props_mode&TD_WINDOW) +#define domain_func (props_mode&TD_FUNC) + +#define FREQ_STARTSTOP() {props_mode&=~TD_CENTER_SPAN;} +#define FREQ_CENTERSPAN() {props_mode|= TD_CENTER_SPAN;} +#define FREQ_IS_STARTSTOP() (!(props_mode&TD_CENTER_SPAN)) +#define FREQ_IS_CENTERSPAN() (props_mode&TD_CENTER_SPAN) +#define FREQ_IS_CW() (frequency0 == frequency1) #define get_trace_scale(t) current_props._trace[t].scale #define get_trace_refpos(t) current_props._trace[t].refpos -#define previous_marker uistat._previous_marker -#define current_trace uistat._current_trace -#define FREQ_IS_STARTSTOP() (!(config._mode&VNA_MODE_CENTER_SPAN)) -#define FREQ_IS_CENTERSPAN() (config._mode&VNA_MODE_CENTER_SPAN) -#define FREQ_IS_CW() (frequency0 == frequency1) +#define VNA_mode config._vna_mode +#define lever_mode config._lever_mode +#define IF_OFFSET config._IF_freq +#ifdef __DIGIT_SEPARATOR__ +#define DIGIT_SEPARATOR config._digit_separator +#else +#define DIGIT_SEPARATOR '.' +#endif int caldata_save(uint32_t id); int caldata_recall(uint32_t id); const properties_t *caldata_reference(void); +const properties_t *get_properties(uint32_t id); int config_save(void); int config_recall(void); @@ -847,35 +1217,21 @@ void enter_dfu(void); #define OP_LEVER 0x01 #define OP_TOUCH 0x02 #define OP_CONSOLE 0x04 -extern volatile uint8_t operation_requested; - -// lever_mode -enum lever_mode { - LM_MARKER, LM_SEARCH, LM_CENTER, LM_SPAN, LM_EDELAY -}; - -#define MARKER_INVALID -1 -#define TRACE_INVALID -1 -typedef struct uistat { -// uint32_t value; // for editing at numeric input area -// int8_t digit; // 0~5 used in numeric input (disabled) -// int8_t digit_mode; // used in numeric input (disabled) - int8_t _current_trace; // 0..(TRACES_MAX -1) (TRACE_INVALID for disabled) - int8_t _previous_marker; // 0..(MARKERS_MAX-1) (MARKER_INVALID for disabled) - uint8_t lever_mode; - uint8_t marker_delta:1; - uint8_t marker_tracking:1; -} uistat_t; - -extern uistat_t uistat; +extern uint8_t operation_requested; #define TOUCH_THRESHOLD 2000 /* * adc.c */ +#if defined(NANOVNA_F303) +#define rccEnableWWDG(lp) rccEnableAPB1(RCC_APB1ENR_WWDGEN, lp) +#define ADC_TOUCH_X ADC_CHANNEL_IN3 +#define ADC_TOUCH_Y ADC_CHANNEL_IN4 +#else #define ADC_TOUCH_X ADC_CHSELR_CHSEL6 #define ADC_TOUCH_Y ADC_CHSELR_CHSEL7 +#endif void adc_init(void); uint16_t adc_single_read(uint32_t chsel); @@ -892,8 +1248,9 @@ int plot_printf(char *str, int, const char *fmt, ...); #define ARRAY_COUNT(a) (sizeof(a)/sizeof(*(a))) // Speed profile definition #define START_PROFILE systime_t time = chVTGetSystemTimeX(); -#define STOP_PROFILE {char string_buf[12];plot_printf(string_buf, sizeof string_buf, "T:%06d", chVTGetSystemTimeX() - time);ili9341_drawstringV(string_buf, 1, 60);} +#define STOP_PROFILE {char string_buf[12];plot_printf(string_buf, sizeof string_buf, "T:%08d", chVTGetSystemTimeX() - time); lcd_set_foreground(LCD_BG_COLOR); lcd_drawstringV(string_buf, 1, 90);} // Macros for convert define value to string #define STR1(x) #x #define define_to_STR(x) STR1(x) +#define SWAP(type, x, y) {type t = x; x=y; y=t;} /*EOF*/ diff --git a/plot.c b/plot.c index 60224b8..26048f7 100644 --- a/plot.c +++ b/plot.c @@ -19,7 +19,6 @@ * Boston, MA 02110-1301, USA. */ -#include #include #include "ch.h" #include "hal.h" @@ -28,22 +27,21 @@ static void cell_draw_marker_info(int x0, int y0); static void draw_battery_status(void); -//static void cell_grid_line_info(int x0, int y0); +static void draw_cal_status(void); +static void draw_frequencies(void); +static int cell_printf(int16_t x, int16_t y, const char *fmt, ...); +static void markmap_all_markers(void); + static int16_t grid_offset; static int16_t grid_width; -int16_t area_width = AREA_WIDTH_NORMAL; -int16_t area_height = AREA_HEIGHT_NORMAL; +static uint8_t redraw_request = 0; // contains REDRAW_XXX flags -// Counter for sweep -uint16_t sweep_count = 0; +static uint16_t area_width = AREA_WIDTH_NORMAL; +static uint16_t area_height = AREA_HEIGHT_NORMAL; // Cell render use spi buffer static pixel_t *cell_buffer; -// Check buffer size -#if CELLWIDTH*CELLHEIGHT > SPI_BUFFER_SIZE -#error "Too small spi_buffer size SPI_BUFFER_SIZE < CELLWIDTH*CELLHEIGH" -#endif // indicate dirty cells (not redraw if cell data not changed) #define MAX_MARKMAP_X ((LCD_WIDTH+CELLWIDTH-1)/CELLWIDTH) @@ -61,16 +59,16 @@ static map_t markmap[2][MAX_MARKMAP_Y]; static uint8_t current_mappage = 0; // Trace data cache, for faster redraw cells -// CELL_X[16:31] x position -// CELL_Y[ 0:15] y position -typedef uint32_t index_t; -static index_t trace_index[TRACES_MAX][POINTS_COUNT]; +typedef struct { + uint16_t y; + uint16_t x; +} index_t; +static index_t trace_index[TRACE_INDEX_COUNT][POINTS_COUNT]; -#define INDEX(x, y) ((((index_t)(x))<<16)|(((index_t)(y)))) -#define CELL_X(i) ((int)(((i)>>16))) -#define CELL_Y(i) ((int)(((i)&0xFFFF))) - -//#define float2int(v) ((int)(v)) +#if 1 +// All used in plot v > 0 +#define float2int(v) ((int)((v)+0.5)) +#else static int float2int(float v) { @@ -78,51 +76,20 @@ float2int(float v) if (v > 0) return v + 0.5; return 0; } - -void update_grid(void) -{ - uint32_t gdigit = 100000000; - uint32_t fstart = get_sweep_frequency(ST_START); - uint32_t fspan = get_sweep_frequency(ST_SPAN); - uint32_t grid; - - while (gdigit > 100) { - grid = 5 * gdigit; - if (fspan / grid >= 4) - break; - grid = 2 * gdigit; - if (fspan / grid >= 4) - break; - grid = gdigit; - if (fspan / grid >= 4) - break; - gdigit /= 10; - } - - grid_offset = (WIDTH) * ((fstart % grid) / 100) / (fspan / 100); - grid_width = (WIDTH) * (grid / 100) / (fspan / 1000); - - redraw_request |= REDRAW_FREQUENCY|REDRAW_AREA; -} +#endif static inline int circle_inout(int x, int y, int r) { - int d = x*x + y*y - r*r; - if (d < -r) - return 1; - if (d > r) - return -1; - return 0; + 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 int +static bool polar_grid(int x, int y) { - // offset to center - x -= P_CENTER_X; - y -= P_CENTER_Y; - uint32_t d = x*x + y*y; if (d > P_RADIUS*P_RADIUS + P_RADIUS) return 0; @@ -149,19 +116,26 @@ polar_grid(int x, int y) return 0; } +static void +cell_polar_grid(int x0, int y0, int w, int h, pixel_t color) +{ + int x, y; + // offset to center + x0 -= P_CENTER_X; + y0 -= P_CENTER_Y; + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + if (polar_grid(x + x0, y + y0)) cell_buffer[y * CELLWIDTH + x] = color; +} + /* - * Constant Resistance circle: (u - r/(r+1))^2 + v^2 = 1/(r+1)^2 - * Constant Reactance circle: (u - 1)^2 + (v-1/x)^2 = 1/x^2 + * Render Smith grid (if mirror by x possible get Admittance grid) */ -static int +static bool smith_grid(int x, int y) { +#if 0 int d; - - // offset to center - x -= P_CENTER_X; - y -= P_CENTER_Y; - // outer circle d = circle_inout(x, y, P_RADIUS); if (d < 0) return 0; @@ -170,279 +144,178 @@ smith_grid(int x, int y) // horizontal axis if (y == 0) return 1; - // shift circle center to right origin - x -= P_RADIUS; - - // Constant Reactance Circle: 2j : R/2 = P_RADIUS/2 - if (circle_inout(x, y + P_RADIUS / 2, P_RADIUS / 2) == 0) return 1; - if (circle_inout(x, y - P_RADIUS / 2, P_RADIUS / 2) == 0) return 1; + if (y < 0) y = -y; // mirror by y axis + if (x >= 0) { // valid only if x >= 0 + if (x >= r/2){ // valid only if x >= P_RADIUS/2 + // Constant Reactance Circle: 2j : R/2 = P_RADIUS/2 (mirror by y) + if (circle_inout(x - P_RADIUS, y - P_RADIUS/2, P_RADIUS/2) == 0) return 1; - // Constant Resistance Circle: 3 : R/4 = P_RADIUS/4 - d = circle_inout(x + P_RADIUS / 4, y, P_RADIUS / 4); - if (d > 0) return 0; - if (d == 0) return 1; - - // Constant Reactance Circle: 1j : R = P_RADIUS - if (circle_inout(x, y + P_RADIUS, P_RADIUS) == 0) return 1; - if (circle_inout(x, y - P_RADIUS, P_RADIUS) == 0) return 1; - - // Constant Resistance Circle: 1 : R/2 - d = circle_inout(x + P_RADIUS / 2, y, P_RADIUS / 2); - if (d > 0) return 0; - if (d == 0) return 1; + // Constant Resistance Circle: 3 : R/4 = P_RADIUS/4 + d = circle_inout(x - 3*P_RADIUS/4, y, P_RADIUS/4); + if (d > 0) return 0; + if (d == 0) return 1; + } + // Constant Reactance Circle: 1j : R = P_RADIUS (mirror by y) + d = circle_inout(x - P_RADIUS, y - P_RADIUS, P_RADIUS); + if (d == 0) return 1; + + // Constant Resistance Circle: 1 : R/2 + d = circle_inout(x - P_RADIUS/2, y, P_RADIUS/2); + if (d > 0) return 0; + if (d == 0) return 1; + } + // Constant Reactance Circle: 1/2j : R*2 (mirror by y) + if (circle_inout(x - P_RADIUS, y - P_RADIUS*2, P_RADIUS*2) == 0) return 1; - // Constant Reactance Circle: 1/2j : R*2 - if (circle_inout(x, y + P_RADIUS * 2, P_RADIUS * 2) == 0) return 1; - if (circle_inout(x, y - P_RADIUS * 2, P_RADIUS * 2) == 0) return 1; + // Constant Resistance Circle: 1/3 : R*3/4 + if (circle_inout(x - P_RADIUS/4, y, P_RADIUS*3/4) == 0) return 1; + return 0; +#else + uint16_t r = P_RADIUS; + // outer circle + int32_t _r = x*x + y*y; + int32_t d = _r; + if (d > r*r + r) return 0; + if (d > r*r - r) return 1; // 1 + // horizontal axis + if (y == 0) return 1; + if (y < 0) y = -y; // mirror by y axis + uint16_t r_y = r*y; // Radius limit ~256 pixels!! or need uint32 + if (x >= 0) { // valid only if x >= 0 + if (x >= r/2){ + // Constant Reactance Circle: 2j : R/2 = P_RADIUS/2 (mirror by y) + d = _r - 2*r*x - r_y + r*r + r/2; + if ((uint32_t)d <= r) return 1; // 2 + + // Constant Resistance Circle: 3 : R/4 = P_RADIUS/4 + d = _r - (3*r/2)*x + r*r/2 + r/4; + if (d < 0) return 0; + if (d <= r/2) return 1; // 3 + } + // Constant Reactance Circle: 1j : R = P_RADIUS (mirror by y) + d = _r - 2*r*x - 2*r_y + r*r + r; + if ((uint32_t)d<=2*r) return 1; // 4 + + // Constant Resistance Circle: 1 : R/2 + d = _r - r*x + r/2; + if (d < 0) return 0; + if (d <= r) return 1; // 5 + } + // Constant Reactance Circle: 1/2j : R*2 (mirror by y) + d = _r - 2*r*x - 4*r_y + r*r + r*2; + if ((uint32_t) d<= r*4) return 1; // 6 // Constant Resistance Circle: 1/3 : R*3/4 - if (circle_inout(x + P_RADIUS * 3 / 4, y, P_RADIUS * 3 / 4) == 0) return 1; + d = _r - x*(r/2) - r*r/2 + r*3/4; + if ((uint32_t)d<=r*3/2) return 1; // 7 return 0; +#endif } -#if 0 -static int -smith_grid2(int x, int y, float scale) +static void +cell_smith_grid(int x0, int y0, int w, int h, pixel_t color) { - int d; - + int x, y; // offset to center - x -= P_CENTER_X; - y -= P_CENTER_Y; - - // outer circle - d = circle_inout(x, y, P_RADIUS); - if (d < 0) - return 0; - if (d == 0) - return 1; - - // shift circle center to right origin - x -= P_RADIUS * scale; - - // Constant Reactance Circle: 2j : R/2 = 58 - if (circle_inout(x, y+58*scale, 58*scale) == 0) - return 1; - if (circle_inout(x, y-58*scale, 58*scale) == 0) - return 1; -#if 0 - // Constant Resistance Circle: 3 : R/4 = 29 - d = circle_inout(x+29*scale, y, 29*scale); - if (d > 0) return 0; - if (d == 0) return 1; - d = circle_inout(x-29*scale, y, 29*scale); - if (d > 0) return 0; - if (d == 0) return 1; -#endif - - // Constant Reactance Circle: 1j : R = 116 - if (circle_inout(x, y+116*scale, 116*scale) == 0) - return 1; - if (circle_inout(x, y-116*scale, 116*scale) == 0) - return 1; - - // Constant Resistance Circle: 1 : R/2 = 58 - d = circle_inout(x+58*scale, y, 58*scale); - if (d > 0) return 0; - if (d == 0) return 1; - d = circle_inout(x-58*scale, y, 58*scale); - if (d > 0) return 0; - if (d == 0) return 1; - - // Constant Reactance Circle: 1/2j : R*2 = 232 - if (circle_inout(x, y+232*scale, 232*scale) == 0) - return 1; - if (circle_inout(x, y-232*scale, 232*scale) == 0) - return 1; - -#if 0 - // Constant Resistance Circle: 1/3 : R*3/4 = 87 - d = circle_inout(x+87*scale, y, 87*scale); - if (d > 0) return 0; - if (d == 0) return 1; - d = circle_inout(x+87*scale, y, 87*scale); - if (d > 0) return 0; - if (d == 0) return 1; -#endif - - // Constant Resistance Circle: 0 : R - d = circle_inout(x+P_RADIUS*scale, y, P_RADIUS*scale); - if (d > 0) return 0; - if (d == 0) return 1; - d = circle_inout(x-P_RADIUS*scale, y, P_RADIUS*scale); - if (d > 0) return 0; - if (d == 0) return 1; - - // Constant Resistance Circle: -1/3 : R*3/2 = 174 - d = circle_inout(x+174*scale, y, 174*scale); - if (d > 0) return 0; - if (d == 0) return 1; - d = circle_inout(x-174*scale, y, 174*scale); - //if (d > 0) return 0; - if (d == 0) return 1; - return 0; + x0 -= P_CENTER_X; + y0 -= P_CENTER_Y; + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + if (smith_grid(x + x0, y + y0)) cell_buffer[y * CELLWIDTH + x] = color; } -#endif #if 0 -const int cirs[][4] = { - { 0, 58/2, 58/2, 0 }, // Constant Reactance Circle: 2j : R/2 = 58 - { 29/2, 0, 29/2, 1 }, // Constant Resistance Circle: 3 : R/4 = 29 - { 0, 115/2, 115/2, 0 }, // Constant Reactance Circle: 1j : R = 115 - { 58/2, 0, 58/2, 1 }, // Constant Resistance Circle: 1 : R/2 = 58 - { 0, 230/2, 230/2, 0 }, // Constant Reactance Circle: 1/2j : R*2 = 230 - { 86/2, 0, 86/2, 1 }, // Constant Resistance Circle: 1/3 : R*3/4 = 86 - { 0, 460/2, 460/2, 0 }, // Constant Reactance Circle: 1/4j : R*4 = 460 - { 115/2, 0, 115/2, 1 }, // Constant Resistance Circle: 0 : R - { 173/2, 0, 173/2, 1 }, // Constant Resistance Circle: -1/3 : R*3/2 = 173 - { 0, 0, 0, 0 } // sentinel -}; - -static int -smith_grid3(int x, int y) +static void +cell_admit_grid(int x0, int y0, int w, int h, pixel_t color) { - int d; - + int x, y; // offset to center - x -= P_CENTER_X; - y -= P_CENTER_Y; - - // outer circle - d = circle_inout(x, y, P_RADIUS); - if (d < 0) - return 0; - if (d == 0) - return 1; - - // shift circle center to right origin - x -= P_RADIUS /2; - - int i; - for (i = 0; cirs[i][2]; i++) { - d = circle_inout(x+cirs[i][0], y+cirs[i][1], cirs[i][2]); - if (d == 0) - return 1; - if (d > 0 && cirs[i][3]) - return 0; - d = circle_inout(x-cirs[i][0], y-cirs[i][1], cirs[i][2]); - if (d == 0) - return 1; - if (d > 0 && cirs[i][3]) - return 0; - } - return 0; + x0 -= P_CENTER_X; + y0 -= P_CENTER_Y; + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) + if (smith_grid(-(x + x0), y + y0)) cell_buffer[y * CELLWIDTH + x] = color; } #endif -#if 0 -static int -rectangular_grid(int x, int y) +void update_grid(void) { - //#define FREQ(x) (((x) * (fspan / 1000) / (WIDTH-1)) * 1000 + fstart) - //int32_t n = FREQ(x-1) / fgrid; - //int32_t m = FREQ(x) / fgrid; - //if ((m - n) > 0) - //if (((x * 6) % (WIDTH-1)) < 6) - //if (((x - grid_offset) % grid_width) == 0) - if (x == 0 || x == WIDTH-1) - return 1; - if ((y % GRIDY) == 0) - return 1; - if ((((x + grid_offset) * 10) % grid_width) < 10) - return 1; - return 0; + freq_t gdigit = 100000000; + freq_t fstart = get_sweep_frequency(ST_START); + freq_t fspan = get_sweep_frequency(ST_SPAN); + freq_t grid; + + while (gdigit > 100) { + grid = 5 * gdigit; + if (fspan / grid >= 4) + break; + grid = 2 * gdigit; + if (fspan / grid >= 4) + break; + grid = gdigit; + if (fspan / grid >= 4) + break; + gdigit /= 10; + } + + grid_offset = (WIDTH) * ((fstart % grid) / 100) / (fspan / 100); + grid_width = (WIDTH) * (grid / 100) / (fspan / 1000); } -#endif -static int +static inline int rectangular_grid_x(int x) { x -= CELLOFFSETX; - if (x < 0) return 0; - if (x == 0 || x == WIDTH) - return 1; - if ((((x + grid_offset) * 10) % grid_width) < 10) + if ((uint32_t)x > WIDTH) return 0; + if ((((x + grid_offset) * 10) % grid_width) < 10 || x == 0 || x == WIDTH) return 1; return 0; } -static int +static inline int rectangular_grid_y(int y) { - if (y < 0) + if (y < 0 || (y % GRIDY)) return 0; - if ((y % GRIDY) == 0) - return 1; - return 0; -} - -#if 0 -int -set_strut_grid(int x) -{ - uint16_t *buf = spi_buffer; - int y; - - for (y = 0; y < HEIGHT; y++) { - int c = rectangular_grid(x, y); - c |= smith_grid(x, y); - *buf++ = c; - } - return y; + return 1; } -void -draw_on_strut(int v0, int d, int color) -{ - int v; - int v1 = v0 + d; - if (v0 < 0) v0 = 0; - if (v1 < 0) v1 = 0; - if (v0 >= HEIGHT) v0 = HEIGHT-1; - if (v1 >= HEIGHT) v1 = HEIGHT-1; - if (v0 == v1) { - v = v0; d = 2; - } else if (v0 < v1) { - v = v0; d = v1 - v0 + 1; - } else { - v = v1; d = v0 - v1 + 1; - } - while (d-- > 0) - spi_buffer[v++] |= color; -} -#endif - /* * calculate log10(abs(gamma)) */ static float -logmag(const float *v) +logmag(int i, const float *v) { - return log10f(v[0]*v[0] + v[1]*v[1]) * 10; + (void) i; + float x = v[0]*v[0] + v[1]*v[1]; +// return log10f(x) * 10.0f; +// return vna_logf(x) * (10.0f / logf(10.0f)); + return vna_log10f_x_10(x); } /* * calculate phase[-2:2] of coefficient */ static float -phase(const float *v) +phase(int i, const float *v) { - return (180.0f / VNA_PI) * atan2f(v[1], v[0]); + (void) i; + return (180.0f / VNA_PI) * vna_atan2f(v[1], v[0]); } /* * calculate groupdelay */ static float -groupdelay(const float *v, const float *w, float deltaf) +groupdelay(const float *v, const float *w, uint32_t deltaf) { #if 1 // atan(w)-atan(v) = atan((w-v)/(1+wv)) - float r = w[0]*v[1] - w[1]*v[0]; - float i = w[0]*v[0] + w[1]*v[1]; - return atan2f(r, i) / (2 * VNA_PI * deltaf); + float r = w[0]*v[0] + w[1]*v[1]; + float i = w[0]*v[1] - w[1]*v[0]; + return vna_atan2f(i, r) / (2 * VNA_PI * deltaf); #else - return (atan2f(w[0], w[1]) - atan2f(v[0], v[1])) / (2 * VNA_PI * deltaf); + return (vna_atan2f(w[0], w[1]) - vna_atan2f(v[0], v[1])) / (2 * VNA_PI * deltaf); #endif } @@ -450,269 +323,277 @@ groupdelay(const float *v, const float *w, float deltaf) * calculate abs(gamma) */ static float -linear(const float *v) +linear(int i, const float *v) { - return sqrtf(v[0]*v[0] + v[1]*v[1]); + (void) i; + return vna_sqrtf(v[0]*v[0] + v[1]*v[1]); } /* * calculate vswr; (1+gamma)/(1-gamma) */ static float -swr(const float *v) +swr(int i, const float *v) { - float x = linear(v); - if (x >= 1) + (void) i; + float x = linear(i, v); + if (x > 0.99) return INFINITY; return (1 + x)/(1 - x); } +#ifdef __VNA_Z_RENORMALIZATION__ +#define PORT_Z current_props._portz +#else +#define PORT_Z 50.0f +#endif + static float -resistance(const float *v) +resistance(int i, const float *v) { - const float z0 = 50; - float d = z0 / ((1-v[0])*(1-v[0])+v[1]*v[1]); - return (1 - v[0]*v[0] - v[1]*v[1]) * d; + (void) i; + const float z0 = PORT_Z; + const float d = (1 - v[0])*(1 - v[0]) + v[1]*v[1]; + return z0 * (1 - v[0]*v[0] - v[1]*v[1]) / d; } static float -reactance(const float *v) +reactance(int i, const float *v) { - const float z0 = 50; - const float d = z0 / ((1-v[0])*(1-v[0])+v[1]*v[1]); - return 2*v[1] * d; + (void) i; + const float z0 = PORT_Z; + const float d = (1 - v[0])*(1 - v[0]) + v[1]*v[1]; + return 2 * z0 * v[1] / d; } -#if 0 static float -mod_z(const float *v){ - float z0 = 50; - float d = z0 / ((1-v[0])*(1-v[0])+v[1]*v[1]); - float zr = (1 - v[0]*v[0] - v[1]*v[1])*d; - float zi = 2*v[1]*d; +mod_z(int i, const float *v) +{ + (void) i; + const float z0 = PORT_Z; + const float d = (1 - v[0])*(1 - v[0]) + v[1]*v[1]; + return z0 * vna_sqrtf(4 * v[0] / d + 1); // always >= 0 +} - return sqrtf(zr*zr + zi*zi); +static float +imag_lc(int i, const float *v) +{ + const float zi = reactance(i, v); + float w = 2 * VNA_PI * getFrequency(i); + if (zi < 0) return 1 / (w * zi); // Capacity + return zi / (w ); // Inductive } -#endif static float -qualityfactor(const float *v) +qualityfactor(int i, const float *v) { - float i = 2*v[1]; - float r = 1 - v[0]*v[0] - v[1]*v[1]; - return fabsf(i / r); + (void) i; + const float im = 2 * v[1]; + const float re = 1 - v[0]*v[0] - v[1]*v[1]; + return vna_fabsf(im / re); } static float -real(const float *v) +real(int i, const float *v) { + (void) i; return v[0]; } static float -imag(const float *v) +imag(int i, const float *v) { + (void) i; return v[1]; } -static void -cartesian_scale(const float *v, int *xp, int *yp, float scale) +float +groupdelay_from_array(int i, const float *v) { - //float scale = 4e-3; - int x = float2int(v[0] * P_RADIUS * scale); - int y = float2int(v[1] * P_RADIUS * scale); - if (x < -P_RADIUS) x = -P_RADIUS; - else if (x > P_RADIUS) x = P_RADIUS; - if (y < -P_RADIUS) y = -P_RADIUS; - else if (y > P_RADIUS) y = P_RADIUS; - *xp = P_CENTER_X + x; - *yp = P_CENTER_Y - y; + int bottom = (i == 0) ? 0 : -1; // get prev point + int top = (i == sweep_points-1) ? 0 : 1; // get next point + freq_t deltaf = get_sweep_frequency(ST_SPAN) / ((sweep_points - 1) / (top - bottom)); + return groupdelay(&v[2*bottom], &v[2*top], deltaf); } -float -groupdelay_from_array(int i, float array[POINTS_COUNT][2]) +static inline void +cartesian_scale(const float *v, int16_t *xp, int16_t *yp, float scale) { - int bottom = (i == 0) ? 0 : i - 1; - int top = (i == sweep_points-1) ? sweep_points-1 : i + 1; - float deltaf = frequencies[top] - frequencies[bottom]; - return groupdelay(array[bottom], array[top], deltaf); + int16_t x = P_CENTER_X + float2int(v[0] * scale); + int16_t y = P_CENTER_Y - float2int(v[1] * scale); + if (x < 0) x = 0; + else if (x > WIDTH) x = WIDTH; + if (y < 0) y = 0; + else if (y > HEIGHT) y = HEIGHT; + *xp = x; + *yp = y; } +#if MAX_TRACE_TYPE != 13 +#error "Redefined trace_type list, need check format_list" +#endif + +const trace_info_t trace_info_list[MAX_TRACE_TYPE] = { +// Type name format delta format ref scale get value +[TRC_LOGMAG] = {"LOGMAG", "%.2fdB", S_DELTA"%.2fdB", NGRIDY-1, 10.0, logmag }, +[TRC_PHASE] = {"PHASE", "%.1f"S_DEGREE, S_DELTA"%.2f"S_DEGREE, NGRIDY/2, 90.0, phase }, +[TRC_DELAY] = {"DELAY", "%.4Fs", "%.4Fs", NGRIDY/2, 1e-9, groupdelay_from_array}, +[TRC_SMITH] = {"SMITH", NULL, NULL, 0, 1.00, NULL }, // Custom +[TRC_POLAR] = {"POLAR", "%.2f%+j.2f", "%.2f%+j.2f", 0, 1.00, NULL }, // Custom +//[TRC_ADMIT]= {"ADMIT", "%.2f%+j.2f", "%.2f%+j.2f", 0, 1.00, NULL }, // Custom +[TRC_LINEAR] = {"LINEAR", "%.4f", S_DELTA"%.3f", 0, 0.125, linear }, +[TRC_SWR] = {"SWR", "%.3f", S_DELTA"%.3f", 0, 0.25, swr }, +[TRC_REAL] = {"REAL", "%.4f", S_DELTA"%.3f", NGRIDY/2, 0.25, real }, +[TRC_IMAG] = {"IMAG", "%.4fj", S_DELTA"%.4fj", NGRIDY/2, 0.25, imag }, +[TRC_R] = {"R", "%.4F"S_OHM, S_DELTA"%.4F"S_OHM, 0, 100.0, resistance }, +[TRC_X] = {"X", "%.4F"S_OHM, S_DELTA"%.4F"S_OHM, NGRIDY/2, 100.0, reactance }, +[TRC_Z] = {"|Z|", "%.3f", S_DELTA"%.3f", 0, 50.0, mod_z }, +[TRC_Q] = { "Q", "%.3f", S_DELTA"%.3f", 0, 10.0, qualityfactor }, +}; + // Calculate and cache point coordinates for trace static void trace_into_index(int t, float array[POINTS_COUNT][2]) { - const float refpos = NGRIDY - get_trace_refpos(t); - const float scale = 1 / get_trace_scale(t); - const uint32_t point_count = sweep_points-1; + uint16_t point_count = sweep_points-1; index_t *index = trace_index[t]; - for (uint32_t i = 0; i <= point_count; i++){ - int y, x; - float v = 0.0f; - float *coeff = array[i]; - switch (trace[t].type) { - case TRC_LOGMAG: - v = logmag(coeff); - break; - case TRC_PHASE: - v = phase(coeff); - break; - case TRC_DELAY: - v = groupdelay_from_array(i, array); - break; - case TRC_LINEAR: - v = linear(coeff); - break; - case TRC_SWR: - v = (swr(coeff) - 1); - break; - case TRC_REAL: - v = real(coeff); - break; - case TRC_IMAG: - v = imag(coeff); - break; - case TRC_R: - v = resistance(coeff); - break; - case TRC_X: - v = reactance(coeff); - break; - case TRC_Q: - v = qualityfactor(coeff); - break; - case TRC_SMITH: - //case TRC_ADMIT: - case TRC_POLAR: - cartesian_scale(coeff, &x, &y, scale); - goto set_index; -// default: -// continue; + uint32_t type = 1<>1), y, i; + for (i = 0; i <= point_count; i++, x+=delta) { + float v = 0; + if (c) v = c(i, array[i]); // Get value + if (v == INFINITY) { + y = 0; + } else { + y = refpos - v * dscale; + if (y < 0) y = 0; + else if (y > HEIGHT) y = HEIGHT; + } + index[i].x = x; + index[i].y = y; + dx+=error; if (dx >=point_count) {x++; dx-= point_count;} + } + return; + } + // Smith/Polar grid + if (type & ((1< NGRIDY) v = NGRIDY; - x = (i * (WIDTH) + (point_count>>1)) / point_count + CELLOFFSETX; - y = float2int(v * GRIDY); -set_index: - index[i] = INDEX(x, y); + return; } +#endif } static void -format_smith_value(char *buf, int len, const float coeff[2], uint32_t frequency) +format_smith_value(int xpos, int ypos, const float *coeff, uint16_t idx) { char *format; float zr, zi; switch (marker_smith_format) { case MS_LIN: - zr = linear(coeff); - zi = phase(coeff); + zr = linear(idx, coeff); + zi = phase(idx, coeff); format = "%.2f %.1f" S_DEGREE; break; case MS_LOG: - zr = logmag(coeff); - zi = phase(coeff); + zr = logmag(idx, coeff); + zi = phase(idx, coeff); format = (zr == -INFINITY) ? "-"S_INFINITY" dB" : "%.1fdB %.1f" S_DEGREE; break; case MS_REIM: - zr = real(coeff); - zi = imag(coeff); - format = "%F%+Fj"; + zr = real(idx, coeff); + zi = imag(idx, coeff); + format = "%F%+jF"; break; case MS_RX: - zr = resistance(coeff); - zi = reactance(coeff); - format = "%F%+Fj"S_OHM; + zr = resistance(idx, coeff); + zi = reactance(idx, coeff); + format = "%F%+jF"S_OHM; break; case MS_RLC: - zr = resistance(coeff); - zi = reactance(coeff); - if (zi < 0) {// Capacity - format = "%F"S_OHM" %FF"; - zi = -1 / (2 * VNA_PI * frequency * zi); - } else { // Inductive - format = "%F"S_OHM" %FH"; - zi = zi / (2 * VNA_PI * frequency); - } + zr = resistance(idx, coeff); + zi = imag_lc(idx, coeff); + if (zi < 0) + format = "%F"S_OHM" %FF"; // Capacity + else + format = "%F"S_OHM" %FH"; // Inductive + zi = vna_fabsf(zi); break; default: return; } - plot_printf(buf, len, format, zr, zi); + cell_printf(xpos, ypos, format, zr, zi); } -#if MAX_TRACE_TYPE != 13 -#error "Redefined trace_type list, need check format_list" -#endif - -typedef float (*get_value_cb_t)(const float *v); -static const struct { - const char *name; - const char *dname; - get_value_cb_t get_value_cb; -} format_list[]={ -// Type format delta format get value -[TRC_LOGMAG] = {"%.2fdB", S_DELTA"%.2fdB", logmag }, -[TRC_PHASE] = {"%.1f"S_DEGREE, S_DELTA"%.2f"S_DEGREE, phase }, -[TRC_DELAY] = {"%.2Fs", "%.2Fs", NULL }, // Custom -[TRC_LINEAR] = {"%.4f", S_DELTA"%.3f", linear }, -[TRC_SWR] = {"%.4f", S_DELTA"%.3f", swr }, -[TRC_REAL] = {"%.4f", S_DELTA"%.3f", real }, -[TRC_IMAG] = {"%.4fj", S_DELTA"%.3fj", imag }, -[TRC_R] = {"%.2F"S_OHM, S_DELTA"%.2F"S_OHM, resistance }, -[TRC_X] = {"%.2F"S_OHM, S_DELTA"%.2F"S_OHM, reactance }, -[TRC_Q] = {"%.3f", S_DELTA"%.3f", qualityfactor}, -[TRC_SMITH] = {NULL, NULL, NULL }, // Custom -[TRC_POLAR] = {"%.2f%+.2fj", "%.2f%+.2fj", NULL } // Custom -}; - static void -trace_get_value_string(int t, char *buf, int len, float array[POINTS_COUNT][2], int index, int index_ref) +trace_print_value_string(int xpos, int ypos, int t, int index, int index_ref) { // Check correct input - if (trace[t].type >= MAX_TRACE_TYPE) return; + int type = trace[t].type; + if (type >= MAX_TRACE_TYPE) return; + float (*array)[2] = measured[trace[t].channel]; float v = 0.0f; float *coeff = array[index]; - float *coeff_ref; + float *coeff_ref = NULL; const char *format; // Get format data if (index_ref >=0){ // Delta value coeff_ref = array[index_ref]; - format = format_list[trace[t].type].dname; + format = trace_info_list[type].dformat; } else{ // No delta - coeff_ref = NULL; - format = format_list[trace[t].type].name; + format = trace_info_list[type].format; } - get_value_cb_t c = format_list[trace[t].type].get_value_cb; - if (c){ // Run standard get value function from table - v = c(coeff); // Get value - if (coeff_ref && v != INFINITY) v-=c(coeff_ref); // Calculate delta value + get_value_cb_t c = trace_info_list[type].get_value_cb; + if (c){ // Run standard get value function from table + v = c(index, coeff); // Get value + if (coeff_ref && v != INFINITY) v-=c(index, coeff_ref); // Calculate delta value } else { // Need custom calculations - switch (trace[t].type) { - case TRC_DELAY: - v = groupdelay_from_array(index, array); - if (coeff_ref) v-= groupdelay_from_array(index_ref, array); - break; + switch (type) { case TRC_SMITH: - format_smith_value(buf, len, coeff, frequencies[index]); + format_smith_value(xpos, ypos, coeff, index); return; //case TRC_ADMIT: case TRC_POLAR: - plot_printf(buf, len, format, coeff[0], coeff[1]); + cell_printf(xpos, ypos, format, coeff[0], coeff[1]); return; default: return; } } - plot_printf(buf, len, format, v); + cell_printf(xpos, ypos, format, v); } static int -trace_get_info(int t, char *buf, int len) +trace_print_info(int xpos, int ypos, int t) { float scale = get_trace_scale(t); char *format; @@ -724,24 +605,18 @@ trace_get_info(int t, char *buf, int len) case TRC_POLAR: format = (scale != 1.0) ? "%s %.1fFS" : "%s "; break; default: format = "%s %F/"; break; } - return plot_printf(buf, len, format, get_trace_typename(t), scale); + return cell_printf(xpos, ypos, format, get_trace_typename(t), scale); } static float time_of_index(int idx) { - return (idx / (float)FFT_SIZE) / (frequencies[1] - frequencies[0]); + freq_t span = get_sweep_frequency(ST_SPAN); + return (idx * (sweep_points-1)) / ((float)FFT_SIZE * span); } static float distance_of_index(int idx) { - return velocity_factor * (SPEED_OF_LIGHT / 2) * time_of_index(idx); -} - -static void -mark_map(int x, int y) -{ - if (y >= 0 && y < MAX_MARKMAP_Y && x >= 0 && x < MAX_MARKMAP_X) - markmap[current_mappage][y] |= 1 << x; + return velocity_factor * (SPEED_OF_LIGHT / 200.0f) * time_of_index(idx); } static inline void @@ -750,32 +625,64 @@ swap_markmap(void) current_mappage^= 1; } -static void +static inline void clear_markmap(void) { memset(markmap[current_mappage], 0, sizeof markmap[current_mappage]); } -void +/* + * Force full screen update + */ +static inline void force_set_markmap(void) { memset(markmap[current_mappage], 0xff, sizeof markmap[current_mappage]); } -void -invalidate_rect(int x0, int y0, int x1, int y1) +/* + * Force region of screen update + */ +static void +invalidate_rect_func(int x0, int y0, int x1, int y1) { - x0 /= CELLWIDTH; - x1 /= CELLWIDTH; - y0 /= CELLHEIGHT; - y1 /= CELLHEIGHT; int x, y; + if (y0 < 0 ) y0 = 0; + if (y1 >=MAX_MARKMAP_Y) y1 = MAX_MARKMAP_Y-1; + map_t *map = markmap[current_mappage]; for (y = y0; y <= y1; y++) - for (x = x0; x <= x1; x++) - mark_map(x, y); + for (x = x0; x <= x1; x++) + map[y]|= 1 << x; +} +#define invalidate_rect(x0, y0, x1, y1) invalidate_rect_func((x0)/CELLWIDTH, (y0)/CELLHEIGHT, (x1)/CELLWIDTH, (y1)/CELLHEIGHT) + +#if STORED_TRACES > 0 +static uint8_t enabled_store_trace = 0; +void storeCurrentTrace(int idx){ + if (current_trace == TRACE_INVALID) return; + memcpy(trace_index[TRACES_MAX + idx], trace_index[current_trace], sizeof(trace_index[0])); + enabled_store_trace|=1<x1) SWAP(x0, x1); m0 = m1; - int y0 = n0; int y1 = n1; if (y0>y1) SWAP(y0, y1); n0 = n1; + int x0 = m0; int x1 = m1; if (x0>x1) SWAP(int, x0, x1); m0 = m1; + int y0 = n0; int y1 = n1; if (y0>y1) SWAP(int, y0, y1); n0 = n1; for (; y0 <= y1; y0++) for (j = x0; j <= x1; j++) map[y0] |= 1 << j; @@ -804,11 +711,20 @@ mark_cells_from_index(void) } } +void set_area_size(uint16_t w, uint16_t h){ + area_width = w; + area_height = h; +} + static inline void markmap_upperarea(void) { // Hardcoded, Text info from upper area - invalidate_rect(0, 0, AREA_WIDTH_NORMAL, 5*bFONT_STR_HEIGHT); +#if LCD_WIDTH == 480 || _USE_FONT_== 0 + invalidate_rect(0, 0, AREA_WIDTH_NORMAL, ((MARKERS_MAX+1)/2 + 1)*FONT_STR_HEIGHT); +#else + invalidate_rect(0, 0, AREA_WIDTH_NORMAL, (MARKERS_MAX+ 1)*FONT_STR_HEIGHT); +#endif } // @@ -823,25 +739,37 @@ cell_drawline(int x0, int y0, int x1, int y1, pixel_t c) if (y0 >= CELLHEIGHT && y1 >= CELLHEIGHT) return; // modifed Bresenham's line algorithm, see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm - if (x1 < x0) { SWAP(x0, x1); SWAP(y0, y1); } - int dx =-(x1 - x0); - int dy = (y1 - y0), sy = 1; if (dy < 0) { dy = -dy; sy = -1; } + // Draw from top to bottom (most graph contain vertical lines) + if (y1 < y0) { SWAP(int, x0, x1); SWAP(int, y0, y1); } + int dx =-(x1 - x0), sx = 1; if (dx > 0) { dx = -dx; sx = -sx; } + int dy = (y1 - y0); int err = ((dy + dx) < 0 ? -dx : -dy) / 2; + // Fast skip points while y0 < 0 + if (y0 < 0){ + while(1){ + int e2 = err; + if (e2 > dx) { err-= dy; x0+=sx;} + if (e2 < dy) { err-= dx; y0++; if (y0 == 0) break;} + } + } + // align y by CELLWIDTH for faster calculations + y0*=CELLWIDTH; + y1*=CELLWIDTH; while (1) { - if (y0 >= 0 && y0 < CELLHEIGHT && x0 >= 0 && x0 < CELLWIDTH) - cell_buffer[y0 * CELLWIDTH + x0] |= c; + if ((uint32_t)x0 < CELLWIDTH) + cell_buffer[y0 + x0]|= c; if (x0 == x1 && y0 == y1) return; int e2 = err; - if (e2 > dx) { err-= dy; x0++; } - if (e2 < dy) { err-= dx; y0+=sy;} + if (e2 > dx) { err-= dy; x0+=sx;} + if (e2 < dy) { err-= dx; y0+=CELLWIDTH; if (y0>=CELLHEIGHT*CELLWIDTH) return;} // stop after cell bottom } } // Give a little speedup then draw rectangular plot (50 systick on all calls, all render req 700 systick) // Write more difficult algorithm for search indexes not give speedup static int -search_index_range_x(int x1, int x2, index_t index[POINTS_COUNT], int *i0, int *i1) +search_index_range_x(int x1, int x2, index_t *index, int *i0, int *i1) { int i, j; int head = 0; @@ -851,7 +779,7 @@ search_index_range_x(int x1, int x2, index_t index[POINTS_COUNT], int *i0, int * // Search index point in cell while (1) { i = (head + tail) / 2; - idx_x = CELL_X(index[i]); + idx_x = index[i].x; if (idx_x >= x2) { // index after cell if (tail == i) return false; @@ -870,76 +798,152 @@ search_index_range_x(int x1, int x2, index_t index[POINTS_COUNT], int *i0, int * do { if (j == 0) break; j--; - } while (x1 <= CELL_X(index[j])); + } while (x1 <= index[j].x); *i0 = j; // Search index right from point do { if (i >=sweep_points-1) break; i++; - } while (CELL_X(index[i]) < x2); + } while (index[i].x < x2); *i1 = i; return TRUE; } static void -cell_blit_bitmap(int x, int y, uint16_t w, uint16_t h, const uint8_t *bmp) +cell_blit_bitmap(int16_t x, int16_t y, uint16_t w, uint16_t h, const uint8_t *bmp) { if (x <= -w) return; uint8_t bits = 0; int c = h+y, r; for (; y < c; y++) { - for (r = 0; r < w; r++) { + for (r = 0; r < w; r++, bits<<=1) { if ((r&7)==0) bits = *bmp++; - if (y >= 0 && x+r >= 0 && y < CELLHEIGHT && x+r < CELLWIDTH && (0x80 & bits)) - cell_buffer[y*CELLWIDTH + x + r] = foreground_color; - bits <<= 1; + if ((0x80 & bits) == 0) continue; // no pixel + if ((uint32_t)(y+0) >= CELLHEIGHT) continue; // y < 0 || y >= CELLHEIGHT + if ((uint32_t)(x+r) >= CELLWIDTH ) continue; // x+r < 0 || x+r >= CELLWIDTH + cell_buffer[y*CELLWIDTH + x + r] = foreground_color; } } } -static void -cell_drawstring(char *str, int x, int y) -{ - if (y <= -FONT_GET_HEIGHT || y >= CELLHEIGHT) - return; - while (*str) { - if (x >= CELLWIDTH) - return; - uint8_t ch = *str++; +typedef struct { + const void *vmt; + int16_t x; + int16_t y; +} cellPrintStream; + +static msg_t cellPut(void *ip, uint8_t ch) { + cellPrintStream *ps = ip; + if (ps->x < CELLWIDTH){ uint16_t w = FONT_GET_WIDTH(ch); - cell_blit_bitmap(x, y, w, FONT_GET_HEIGHT, FONT_GET_DATA(ch)); - x += w; + cell_blit_bitmap(ps->x, ps->y, w, FONT_GET_HEIGHT, FONT_GET_DATA(ch)); + ps->x+= w; } + return MSG_OK; } -static void -cell_drawstring_7x13(char *str, int x, int y) -{ - if (y <= -bFONT_GET_HEIGHT || y >= CELLHEIGHT) - return; - while (*str) { - if (x >= CELLWIDTH) - return; - uint8_t ch = *str++; - uint16_t w = bFONT_GET_WIDTH(ch); - cell_blit_bitmap(x, y, w, bFONT_GET_HEIGHT, bFONT_GET_DATA(ch)); - x += w; - } +// Simple print in buffer function +static int cell_printf(int16_t x, int16_t y, const char *fmt, ...) { + static const struct lcd_printStreamVMT { + _base_sequential_stream_methods + } cell_vmt = {NULL, NULL, cellPut, NULL}; + // Skip print if not on cell (at top/bottom/right) + if ((uint32_t)(y+FONT_GET_HEIGHT) >= CELLHEIGHT + FONT_GET_HEIGHT || x >= CELLWIDTH) + return 0; + va_list ap; + // Init small cell print stream + cellPrintStream ps = {&cell_vmt, x, y}; + // Performing the print operation using the common code. + va_start(ap, fmt); + int retval = chvprintf((BaseSequentialStream *)(void *)&ps, fmt, ap); + va_end(ap); + // Return number of bytes that would have been written. + return retval; } +#ifdef __VNA_MEASURE_MODULE__ + + +typedef void (*measure_cell_cb_t)(int x0, int y0); +typedef void (*measure_prepare_cb_t)(uint8_t mode, uint8_t update_mask); + +static measure_cell_cb_t measure_cell_handler = NULL; +static uint8_t data_update = 0; + +#define MESAURE_NONE 0 +#define MESAURE_S11 1 +#define MESAURE_S21 2 +#define MESAURE_ALL 3 + +#define MEASURE_UPD_SWEEP 1 +#define MEASURE_UPD_FREQ 2 +#define MEASURE_UPD_ALL 3 + // Include L/C match functions #ifdef __USE_LC_MATCHING__ #include "lc_matching.c" #endif +static const struct { + uint8_t option; + uint8_t update; + measure_cell_cb_t measure_cell; + measure_prepare_cb_t measure_prepare; +} measure[]={ + [MEASURE_NONE] = {MESAURE_NONE, 0, NULL, NULL }, +#ifdef __USE_LC_MATCHING__ + [MEASURE_LC_MATH] = {MESAURE_NONE, MEASURE_UPD_ALL, draw_lc_match, prepare_lc_match }, +#endif +#ifdef __S21_MEASURE__ + [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 }, +#endif +#ifdef __S11_CABLE_MEASURE__ + [MEASURE_S11_CABLE] = {MESAURE_S11, MEASURE_UPD_ALL, draw_s11_cable, prepare_s11_cable}, +#endif +}; + +static inline void measure_set_flag(uint8_t flag) { + data_update|= flag; +} + +void plot_set_measure_mode(uint8_t mode) { + if (mode >= MEASURE_END) return; + measure_cell_handler = measure[mode].measure_cell; + current_props._measure = mode; + data_update = 0xFF; + request_to_redraw(REDRAW_AREA); +} + +uint16_t plot_get_measure_channels(void) { + return measure[current_props._measure].option; +} + +static void measure_prepare(void) { + if (current_props._measure == 0) return; + measure_prepare_cb_t measure_cb = measure[current_props._measure].measure_prepare; + // Do measure and cache data only if update flags some + if (measure_cb && (data_update & measure[current_props._measure].update)) + measure_cb(current_props._measure, data_update); + data_update = 0; +} + +static void cell_draw_measure(int x0, int y0){ + if (measure_cell_handler == NULL) return; + lcd_set_background(LCD_BG_COLOR); + lcd_set_foreground(LCD_LC_MATCH_COLOR); + measure_cell_handler(x0, y0); +} +#endif + +// Reference bitmap (size and offset) #define REFERENCE_WIDTH 6 #define REFERENCE_HEIGHT 5 #define REFERENCE_X_OFFSET 5 #define REFERENCE_Y_OFFSET 2 - -// Reference bitmap static const uint8_t reference_bitmap[]={ _BMP8(0b11000000), _BMP8(0b11110000), @@ -948,6 +952,7 @@ static const uint8_t reference_bitmap[]={ _BMP8(0b11000000), }; +// Marker bitmaps (size and offsets) #if _USE_BIG_MARKER_ == 0 #define MARKER_WIDTH 7 #define MARKER_HEIGHT 10 @@ -977,6 +982,7 @@ static const uint8_t marker_bitmap[]={ _BMP8(0b00000000), _BMP8(0b00000000), _BMP8(0b00000000), +#if MARKERS_MAX >=2 // Marker 2 _BMP8(0b00000000), _BMP8(0b00111000), @@ -988,6 +994,8 @@ static const uint8_t marker_bitmap[]={ _BMP8(0b00000000), _BMP8(0b00000000), _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=3 // Marker 3 _BMP8(0b00000000), _BMP8(0b00111000), @@ -999,6 +1007,8 @@ static const uint8_t marker_bitmap[]={ _BMP8(0b00000000), _BMP8(0b00000000), _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=4 // Marker 4 _BMP8(0b00000000), _BMP8(0b00001000), @@ -1010,85 +1020,217 @@ static const uint8_t marker_bitmap[]={ _BMP8(0b00001000), _BMP8(0b00000000), _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=5 + // Marker 5 + _BMP8(0b00000000), + _BMP8(0b01111100), + _BMP8(0b01000000), + _BMP8(0b01111000), + _BMP8(0b00000100), + _BMP8(0b01000100), + _BMP8(0b00111000), + _BMP8(0b00000000), + _BMP8(0b00000000), + _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=6 + // Marker 6 + _BMP8(0b00000000), + _BMP8(0b00111100), + _BMP8(0b01000000), + _BMP8(0b01111000), + _BMP8(0b01000100), + _BMP8(0b01000100), + _BMP8(0b00111000), + _BMP8(0b00000000), + _BMP8(0b00000000), + _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=7 + // Marker 7 + _BMP8(0b00000000), + _BMP8(0b01111100), + _BMP8(0b01000100), + _BMP8(0b00000100), + _BMP8(0b00001000), + _BMP8(0b00010000), + _BMP8(0b00010000), + _BMP8(0b00010000), + _BMP8(0b00000000), + _BMP8(0b00000000), +#endif +#if MARKERS_MAX >=8 + // Marker 8 + _BMP8(0b00000000), + _BMP8(0b00111000), + _BMP8(0b01000100), + _BMP8(0b00111000), + _BMP8(0b01000100), + _BMP8(0b01000100), + _BMP8(0b00111000), + _BMP8(0b00000000), + _BMP8(0b00000000), + _BMP8(0b00000000), +#endif }; #elif _USE_BIG_MARKER_ == 1 -#define MARKER_WIDTH 10 -#define MARKER_HEIGHT 13 -#define X_MARKER_OFFSET 4 -#define Y_MARKER_OFFSET 13 +#define MARKER_WIDTH 11 +#define MARKER_HEIGHT 14 +#define X_MARKER_OFFSET 5 +#define Y_MARKER_OFFSET 14 #define MARKER_BITMAP(i) (&marker_bitmap[(i)*2*MARKER_HEIGHT]) static const uint8_t marker_bitmap[]={ // Marker Back plate - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b1111111110000000), - _BMP16(0b0111111100000000), - _BMP16(0b0011111000000000), - _BMP16(0b0001110000000000), - _BMP16(0b0000100000000000), - // Marker 1 - _BMP16(0b0000000000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0001110000000000), - _BMP16(0b0010110000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0001111000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), _BMP16(0b0000000000000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0111111111000000), + _BMP16(0b0011111110000000), + _BMP16(0b0001111100000000), + _BMP16(0b0000111000000000), + _BMP16(0b0000010000000000), + // Marker 1 + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000111000100000), + _BMP16(0b1001011000100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000011000100000), + _BMP16(0b0100111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#if MARKERS_MAX >=2 // Marker 2 - _BMP16(0b0000000000000000), - _BMP16(0b0001111000000000), - _BMP16(0b0011001100000000), - _BMP16(0b0011001100000000), - _BMP16(0b0000011000000000), - _BMP16(0b0000110000000000), - _BMP16(0b0001100000000000), - _BMP16(0b0011000000000000), - _BMP16(0b0011111100000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1000111100100000), + _BMP16(0b1001100110100000), + _BMP16(0b1001100110100000), + _BMP16(0b1000000110100000), + _BMP16(0b1000001100100000), + _BMP16(0b1000111000100000), + _BMP16(0b1001100000100000), + _BMP16(0b1001100000100000), + _BMP16(0b0101111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=3 // Marker 3 - _BMP16(0b0000000000000000), - _BMP16(0b0011111000000000), - _BMP16(0b0110001100000000), - _BMP16(0b0110001100000000), - _BMP16(0b0000001100000000), - _BMP16(0b0000111000000000), - _BMP16(0b0000001100000000), - _BMP16(0b0110001100000000), - _BMP16(0b0110001100000000), - _BMP16(0b0011111000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1001111100100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1000000110100000), + _BMP16(0b1000011100100000), + _BMP16(0b1000000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b0101111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=4 // Marker 4 - _BMP16(0b0000000000000000), - _BMP16(0b0000011000000000), - _BMP16(0b0000111000000000), - _BMP16(0b0001111000000000), - _BMP16(0b0011011000000000), - _BMP16(0b0110011000000000), - _BMP16(0b0110011000000000), - _BMP16(0b0111111100000000), - _BMP16(0b0000011000000000), - _BMP16(0b0000011000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), - _BMP16(0b0000000000000000), + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1000001100100000), + _BMP16(0b1000011100100000), + _BMP16(0b1000111100100000), + _BMP16(0b1001101100100000), + _BMP16(0b1011001100100000), + _BMP16(0b1011001100100000), + _BMP16(0b1011111110100000), + _BMP16(0b1000001100100000), + _BMP16(0b0100001101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=5 + // Marker 5 + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1011111110100000), + _BMP16(0b1011000000100000), + _BMP16(0b1011000000100000), + _BMP16(0b1011111100100000), + _BMP16(0b1011000110100000), + _BMP16(0b1000000110100000), + _BMP16(0b1000000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b0101111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=6 + // Marker 6 + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1001111100100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000000100000), + _BMP16(0b1011011100100000), + _BMP16(0b1011100110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b0101111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=7 + // Marker 7 + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1011111110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1000000110100000), + _BMP16(0b1000001100100000), + _BMP16(0b1000011000100000), + _BMP16(0b1000110000100000), + _BMP16(0b1000110000100000), + _BMP16(0b1000110000100000), + _BMP16(0b0100110001000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif +#if MARKERS_MAX >=8 + // Marker 8 + _BMP16(0b1111111111100000), + _BMP16(0b1000000000100000), + _BMP16(0b1001111100100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1001111100100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b1011000110100000), + _BMP16(0b0101111101000000), + _BMP16(0b0010000010000000), + _BMP16(0b0001000100000000), + _BMP16(0b0000101000000000), +#endif }; #endif @@ -1098,12 +1240,13 @@ markmap_marker(int marker) int t; if (!markers[marker].enabled) return; + int mk_idx = markers[marker].index; for (t = 0; t < TRACES_MAX; t++) { if (!trace[t].enabled) continue; - index_t index = trace_index[t][markers[marker].index]; - int x = CELL_X(index) - X_MARKER_OFFSET; - int y = CELL_Y(index) - Y_MARKER_OFFSET; + index_t *index = trace_index[t]; + int x = index[mk_idx].x - X_MARKER_OFFSET; + int y = index[mk_idx].y - Y_MARKER_OFFSET; invalidate_rect(x, y, x+MARKER_WIDTH-1, y+MARKER_HEIGHT-1); } } @@ -1120,74 +1263,76 @@ markmap_all_markers(void) markmap_upperarea(); } +#if 0 +static void +markmap_all_refpos(void) +{ + invalidate_rect(OFFSETX, OFFSETY, CELLOFFSETX+1, AREA_HEIGHT_NORMAL); +} +#endif + // // Marker search functions // -static int greater(int x, int y) { return x > y; } -static int lesser(int x, int y) { return x < y; } - -static int (*compare)(int x, int y) = lesser; - +static bool _greater(int x, int y) { return x > y; } +static bool _lesser(int x, int y) { return x < y; } void -set_marker_search(int16_t mode) +marker_search(bool update) { - compare = (mode == MK_SEARCH_MIN) ? greater : lesser; -} - -int -marker_search(void) -{ - int i; + int i, value; int found = 0; - - if (current_trace == TRACE_INVALID) - return -1; - - int value = CELL_Y(trace_index[current_trace][0]); - for (i = 1; i < sweep_points; i++) { - index_t index = trace_index[current_trace][i]; - if ((*compare)(value, CELL_Y(index))) { - value = CELL_Y(index); + if (current_trace == TRACE_INVALID || active_marker == MARKER_INVALID) + return; + // Select search index table + index_t *index = trace_index[current_trace]; + // Select compare function (depend from config settings) + bool (*compare)(int x, int y) = (VNA_mode & VNA_MODE_SEARCH_MIN) ? _lesser : _greater; + for (i = 1, value = index[0].y; i < sweep_points; i++) { + if ((*compare)(value, index[i].y)) { + value = index[i].y; found = i; } } - - return found; + set_marker_index(active_marker, found); + if (update) + redraw_marker(active_marker); } -int +void marker_search_dir(int16_t from, int16_t dir) { - int i; + int i, value; int found = -1; - - if (current_trace == TRACE_INVALID) - return -1; - - int value = CELL_Y(trace_index[current_trace][from]); - for (i = from + dir; i >= 0 && i < sweep_points; i+=dir) { - index_t index = trace_index[current_trace][i]; - if ((*compare)(value, CELL_Y(index))) + if (current_trace == TRACE_INVALID || active_marker == MARKER_INVALID) + return; + // Select search index table + index_t *index = trace_index[current_trace]; + // Select compare function (depend from config settings) + bool (*compare)(int x, int y) = (VNA_mode & VNA_MODE_SEARCH_MIN) ? _lesser : _greater; + // Search next + for (i = from + dir, value = index[from].y; i >= 0 && i < sweep_points; i+=dir) { + if ((*compare)(value, index[i].y)) break; - value = CELL_Y(index); + value = index[i].y; } - + // for (; i >= 0 && i < sweep_points; i+=dir) { - index_t index = trace_index[current_trace][i]; - if ((*compare)(CELL_Y(index), value)) + if ((*compare)(index[i].y, value)) break; + value = index[i].y; found = i; - value = CELL_Y(index); } - return found; + if (found < 0) return; + set_marker_index(active_marker, found); + redraw_marker(active_marker); } int distance_to_index(int8_t t, uint16_t idx, int16_t x, int16_t y) { - index_t index = trace_index[t][idx]; - x-= CELL_X(index); - y-= CELL_Y(index); + index_t *index = trace_index[t]; + x-= index[idx].x; + y-= index[idx].y; return x*x + y*y; } @@ -1214,19 +1359,50 @@ void plot_into_index(float array[2][POINTS_COUNT][2]) { int t; +// START_PROFILE; + // Cache trace data indexes for (t = 0; t < TRACES_MAX; t++) { if (trace[t].enabled) trace_into_index(t, array[trace[t].channel]); } +// STOP_PROFILE; // Marker track on data update - if (uistat.marker_tracking && active_marker != MARKER_INVALID) - set_marker_index(active_marker, marker_search()); - // Current scan count - sweep_count++; - // Build cell list for update - mark_cells_from_index(); // Trace graph update - markmap_all_markers(); // Marker update + if (props_mode & TD_MARKER_TRACK) + marker_search(false); +#ifdef __VNA_MEASURE_MODULE__ + // Current scan update + measure_set_flag(MEASURE_UPD_SWEEP); +#endif + + // Build cell list for update from data indexes + mark_cells_from_index(); + // Mark for update cells, and add markers + request_to_redraw(REDRAW_MARKER | REDRAW_CELLS); +} + +#ifdef __USE_GRID_VALUES__ +static void cell_grid_line_info(int x0, int y0) +{ + // Skip not selected trace + if (current_trace == TRACE_INVALID) return; + // Skip for SMITH/POLAR and off trace + uint32_t trace_type = 1 << trace[current_trace].type; + if (trace_type & (ROUND_GRID_MASK | (1 << TRC_OFF))) return; + // Render at right + int16_t xpos = GRID_X_TEXT - x0; + int16_t ypos = 0 - y0 + 2; + // Get top value + float scale = get_trace_scale(current_trace); + float ref = get_trace_refpos(current_trace); + float v = (NGRIDY - ref) * scale; + if (trace_type&(1 << TRC_SWR)) v+=1.0f; // For SWR trace, value shift by 1.0 + // Render grid values + lcd_set_foreground(LCD_TRACE_1_COLOR + current_trace); + do { + cell_printf(xpos, ypos, "% 6.3F", v); v-=scale; + }while((ypos+=GRIDY) < CELLHEIGHT); } +#endif static void draw_cell(int m, int n) @@ -1247,12 +1423,12 @@ draw_cell(int m, int n) if (w <= 0 || h <= 0) return; // PULSE; - cell_buffer = ili9341_get_cell_buffer(); + cell_buffer = lcd_get_cell_buffer(); // Clear buffer ("0 : height" lines) #if 0 // use memset 350 system ticks for all screen calls // as understand it use 8 bit set, slow down on 32 bit systems - memset(spi_buffer, DEFAULT_BG_COLOR, (h*CELLWIDTH)*sizeof(uint16_t)); + memset(spi_buffer, GET_PALTETTE_COLOR(LCD_BG_COLOR), (h*CELLWIDTH)*sizeof(uint16_t)); #else // use direct set 35 system ticks for all screen calls #if CELLWIDTH%8 != 0 @@ -1294,80 +1470,82 @@ draw_cell(int m, int n) c = GET_PALTETTE_COLOR(LCD_GRID_COLOR); // Generate grid type list uint32_t trace_type = 0; + int t_count = 0; for (t = 0; t < TRACES_MAX; t++) { if (trace[t].enabled) { trace_type |= (1 << trace[t].type); + t_count++; } } + const int step = (VNA_mode & VNA_MODE_DOT_GRID) ? 2 : 1; // Draw rectangular plot (40 system ticks for all screen calls) if (trace_type & RECTANGULAR_GRID_MASK) { for (x = 0; x < w; x++) { if (rectangular_grid_x(x + x0)) { - for (y = 0; y < h; y++) cell_buffer[y * CELLWIDTH + x] = c; + for (y = 0; y < h*CELLWIDTH; y+=step*CELLWIDTH) cell_buffer[y + x] = c; } } for (y = 0; y < h; y++) { if (rectangular_grid_y(y + y0)) { - for (x = 0; x < w; x++) - if (x + x0 >= CELLOFFSETX && x + x0 <= WIDTH + CELLOFFSETX) + for (x = 0; x < w; x+=step) + if ((uint32_t)(x + x0 - CELLOFFSETX) <= WIDTH) cell_buffer[y * CELLWIDTH + x] = c; } } } // Smith greed line (1000 system ticks for all screen calls) - if (trace_type & (1 << TRC_SMITH)) { - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - if (smith_grid(x + x0, y + y0)) cell_buffer[y * CELLWIDTH + x] = c; - } + if (trace_type & (1 << TRC_SMITH)) + cell_smith_grid(x0, y0, w, h, c); // Polar greed line (800 system ticks for all screen calls) - else if (trace_type & (1 << TRC_POLAR)) { - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - if (polar_grid(x + x0, y + y0)) cell_buffer[y * CELLWIDTH + x] = c; - } + else if (trace_type & (1 << TRC_POLAR)) + cell_polar_grid(x0, y0, w, h, c); #if 0 - else if (trace_type & (1 << TRC_ADMIT)) { - for (y = 0; y < h; y++) - for (x = 0; x < w; x++) - if (smith_grid3(x+x0, y+y0) - // smith_grid2(x+x0, y+y0, 0.5)) - cell_buffer[y * CELLWIDTH + x] = c; - } + else if (trace_type & (1 << TRC_ADMIT)) + cell_admit_grid(x0, y0, w, h, c); #endif #endif -#if 0 +#ifdef __USE_GRID_VALUES__ // Only right cells - if (m >= (GRID_X_TEXT)/CELLWIDTH) + if ((VNA_mode & VNA_MODE_SHOW_GRID) && m >= (GRID_X_TEXT)/CELLWIDTH) cell_grid_line_info(x0, y0); #endif + // PULSE; // Draw traces (50-600 system ticks for all screen calls, depend from lines // count and size) #if 1 - for (t = 0; t < TRACES_MAX; t++) { - if (!trace[t].enabled) + for (t = 0; t < TRACE_INDEX_COUNT; t++) { + if (!needProcessTrace(t)) continue; c = GET_PALTETTE_COLOR(LCD_TRACE_1_COLOR + t); - // draw polar plot (check all points) - i0 = 0; - i1 = 0; - uint32_t trace_type = (1 << trace[t].type); - if (trace_type & ((1 << TRC_SMITH) | (1 << TRC_POLAR))) - i1 = sweep_points - 1; - else // draw rectangular plot (search index range in cell, save 50-70 - // system ticks for all screen calls) - search_index_range_x(x0, x0 + w, trace_index[t], &i0, &i1); - if (i1==0) continue; index_t *index = trace_index[t]; + i0 = i1 = 0; + // draw rectangular plot (search index range in cell, save 50-70 system ticks for all screen calls) + if ((1 << trace[t].type) & RECTANGULAR_GRID_MASK && !enabled_store_trace){ + search_index_range_x(x0, x0 + w, index, &i0, &i1); + }else{ + // draw polar plot (check all points) + i1 = sweep_points - 1; + } +#if 1 + // Line mode for (i = i0; i < i1; i++) { - int x1 = CELL_X(index[i]) - x0; - int y1 = CELL_Y(index[i]) - y0; - int x2 = CELL_X(index[i + 1]) - x0; - int y2 = CELL_Y(index[i + 1]) - y0; + int x1 = index[i].x - x0; + int y1 = index[i].y - y0; + int x2 = index[i + 1].x - x0; + int y2 = index[i + 1].y - y0; cell_drawline(x1, y1, x2, y2, c); } +#else + // Dot mode + for (i = i0; i < i1; i++) { + int x = index[i].x - x0; + int y = index[i].y - y0; + if ((uint32_t)x < CELLWIDTH && (uint32_t)y < CELLHEIGHT) + cell_buffer[y * CELLWIDTH + x] = c; + } +#endif } #else for (x = 0; x < area_width; x += 6) @@ -1376,25 +1554,27 @@ draw_cell(int m, int n) #endif // PULSE; // draw marker symbols on each trace (<10 system ticks for all screen calls) + int m_count = 0; #if 1 for (i = 0; i < MARKERS_MAX; i++) { if (!markers[i].enabled) continue; + m_count++; for (t = 0; t < TRACES_MAX; t++) { if (!trace[t].enabled) continue; - index_t index = trace_index[t][markers[i].index]; - int x = CELL_X(index) - x0 - X_MARKER_OFFSET; - int y = CELL_Y(index) - y0 - Y_MARKER_OFFSET; + index_t *index = trace_index[t]; + int16_t mk_idx = markers[i].index; + int16_t x = index[mk_idx].x - x0 - X_MARKER_OFFSET; + int16_t y = index[mk_idx].y - y0 - Y_MARKER_OFFSET; // Check marker icon on cell - if (x + MARKER_WIDTH >= 0 && x - MARKER_WIDTH < CELLWIDTH && - y + MARKER_HEIGHT >= 0 && y - MARKER_HEIGHT < CELLHEIGHT){ -// draw_marker(x, y, LCD_TRACE_1_COLOR + t, i); + if ((uint32_t)(x+MARKER_WIDTH ) < (CELLWIDTH + MARKER_WIDTH ) && + (uint32_t)(y+MARKER_HEIGHT) < (CELLHEIGHT + MARKER_HEIGHT)){ // Draw marker plate - ili9341_set_foreground(LCD_TRACE_1_COLOR + t); + lcd_set_foreground(LCD_TRACE_1_COLOR + t); cell_blit_bitmap(x, y, MARKER_WIDTH, MARKER_HEIGHT, MARKER_BITMAP(0)); // Draw marker number - ili9341_set_foreground(LCD_BG_COLOR); + lcd_set_foreground(LCD_BG_COLOR); cell_blit_bitmap(x, y, MARKER_WIDTH, MARKER_HEIGHT, MARKER_BITMAP(i+1)); } } @@ -1402,28 +1582,31 @@ draw_cell(int m, int n) #endif // Draw trace and marker info on the top (50 system ticks for all screen calls) #if 1 - if (n <= (5*bFONT_STR_HEIGHT)/CELLHEIGHT) + int cnt = t_count > m_count ? t_count : m_count; + // Get max marker/trace string count add one string for edelay/marker freq +#if LCD_WIDTH == 480 || _USE_FONT_== 0 + if (n <= (((cnt+1)/2 + 1)*FONT_STR_HEIGHT)/CELLHEIGHT) +#else + if (n <= ((cnt+ 1)*FONT_STR_HEIGHT)/CELLHEIGHT) +#endif cell_draw_marker_info(x0, y0); #endif -// L/C match data output -#ifdef __USE_LC_MATCHING__ - if (domain_mode & TD_LC_MATH) - cell_draw_lc_match(x0, y0); + +// Measure data output +#ifdef __VNA_MEASURE_MODULE__ + cell_draw_measure(x0, y0); #endif // PULSE; // Draw reference position (<10 system ticks for all screen calls) for (t = 0; t < TRACES_MAX; t++) { - if (!trace[t].enabled) + // Skip draw reference position for disabled/smith/polar traces + if (!trace[t].enabled || ((1 << trace[t].type) & (ROUND_GRID_MASK | (1 << TRC_OFF)))) continue; - uint32_t trace_type = (1 << trace[t].type); - if (trace_type & ((1 << TRC_SMITH) | (1 << TRC_POLAR))) - continue; - int x = 0 - x0 + CELLOFFSETX - REFERENCE_X_OFFSET; - if (x + REFERENCE_WIDTH >= 0 && x - REFERENCE_WIDTH < CELLWIDTH) { + if ((uint32_t)(x + REFERENCE_WIDTH) < CELLWIDTH + REFERENCE_WIDTH) { int y = HEIGHT - float2int(get_trace_refpos(t) * GRIDY) - y0 - REFERENCE_Y_OFFSET; - if (y + REFERENCE_HEIGHT >= 0 && y - REFERENCE_HEIGHT < CELLHEIGHT){ - ili9341_set_foreground(LCD_TRACE_1_COLOR + t); + if ((uint32_t)(y + REFERENCE_HEIGHT) < CELLHEIGHT + REFERENCE_HEIGHT){ + lcd_set_foreground(LCD_TRACE_1_COLOR + t); cell_blit_bitmap(x , y, REFERENCE_WIDTH, REFERENCE_HEIGHT, reference_bitmap); } } @@ -1439,27 +1622,34 @@ draw_cell(int m, int n) } #endif // Draw cell (500 system ticks for all screen calls) - ili9341_bulk_continue(OFFSETX + x0, OFFSETY + y0, w, h); + lcd_bulk_continue(OFFSETX + x0, OFFSETY + y0, w, h); } static void draw_all_cells(bool flush_markmap) { int m, n; +#ifdef __VNA_MEASURE_MODULE__ + measure_prepare(); +#endif // START_PROFILE - for (m = 0; m < (area_width+CELLWIDTH-1) / CELLWIDTH; m++) - for (n = 0; n < (area_height+CELLHEIGHT-1) / CELLHEIGHT; n++) - if ((markmap[0][n] | markmap[1][n]) & (1 << m)) + for (n = 0; n < (area_height+CELLHEIGHT-1) / CELLHEIGHT; n++){ + map_t update_map = markmap[0][n] | markmap[1][n]; + if (update_map == 0) continue; + for (m = 0; update_map; update_map>>=1, m++) + if (update_map & 1) draw_cell(m, n); + } + #if 0 - ili9341_bulk_finish(); + lcd_bulk_finish(); for (m = 0; m < (area_width+CELLWIDTH-1) / CELLWIDTH; m++) for (n = 0; n < (area_height+CELLHEIGHT-1) / CELLHEIGHT; n++) { if ((markmap[0][n] | markmap[1][n]) & (1 << m)) - ili9341_set_background(LCD_LOW_BAT_COLOR); + lcd_set_background(LCD_LOW_BAT_COLOR); else - ili9341_set_background(LCD_NORMAL_BAT_COLOR); - ili9341_fill(m*CELLWIDTH+OFFSETX, n*CELLHEIGHT, 2, 2); + lcd_set_background(LCD_NORMAL_BAT_COLOR); + lcd_fill(m*CELLWIDTH+OFFSETX, n*CELLHEIGHT, 2, 2); } #endif if (flush_markmap) { @@ -1468,18 +1658,24 @@ draw_all_cells(bool flush_markmap) // clear map for next plotting clear_markmap(); } - // Flush LCD buffer, wait completion (need call after end use ili9341_bulk_continue mode) - ili9341_bulk_finish(); + // Flush LCD buffer, wait completion (need call after end use lcd_bulk_continue mode) + lcd_bulk_finish(); // STOP_PROFILE } void draw_all(bool flush) { + if (area_width == 0) {redraw_request = 0; return;} + if (redraw_request & REDRAW_CLRSCR){ + lcd_set_background(LCD_BG_COLOR); + lcd_clear_screen(); + } if (redraw_request & REDRAW_AREA) force_set_markmap(); - if (redraw_request & REDRAW_MARKER) - markmap_upperarea(); + else { + if (redraw_request & REDRAW_MARKER) markmap_all_markers(); + } if (redraw_request & (REDRAW_CELLS | REDRAW_MARKER | REDRAW_AREA)) draw_all_cells(flush); if (redraw_request & REDRAW_FREQUENCY) @@ -1502,265 +1698,280 @@ redraw_marker(int8_t marker) // mark map on new position of marker markmap_marker(marker); +#ifdef __VNA_MEASURE_MODULE__ + if (marker == active_marker) + measure_set_flag(MEASURE_UPD_FREQ); +#endif // mark cells on marker info markmap_upperarea(); draw_all_cells(TRUE); // Force redraw all area after (disable artifacts after fast marker update area) - redraw_request|=REDRAW_AREA; -} - -void -request_to_draw_cells_behind_menu(void) -{ - // Values Hardcoded from ui.c - invalidate_rect(LCD_WIDTH-MENU_BUTTON_WIDTH-OFFSETX, 0, LCD_WIDTH-OFFSETX, LCD_HEIGHT-1); - redraw_request |= REDRAW_CELLS; -} + request_to_redraw(REDRAW_AREA); +} + +// Marker and trace data position +#if LCD_WIDTH == 480 || _USE_FONT_== 0 +static const struct {uint16_t x, y;} marker_pos[]={ + {1 + CELLOFFSETX, 1 }, + {1 + (WIDTH/2) + CELLOFFSETX, 1 }, + {1 + CELLOFFSETX, 1 + FONT_STR_HEIGHT}, + {1 + (WIDTH/2) + CELLOFFSETX, 1 + FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 2*FONT_STR_HEIGHT}, + {1 + (WIDTH/2) + CELLOFFSETX, 1 + 2*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 3*FONT_STR_HEIGHT}, + {1 + (WIDTH/2) + CELLOFFSETX, 1 + 3*FONT_STR_HEIGHT}, +}; -void -request_to_draw_cells_behind_numeric_input(void) -{ - // Values Hardcoded from ui.c - invalidate_rect(0, LCD_HEIGHT-NUM_INPUT_HEIGHT, LCD_WIDTH-1, LCD_HEIGHT-1); - redraw_request |= REDRAW_CELLS; -} -#if 0 -static void cell_grid_line_info(int x0, int y0) -{ - char buf[32]; - int xpos = GRID_X_TEXT - x0; - int ypos = 0 - y0 + 2; - ili9341_set_foreground(LCD_GRID_VALUE_COLOR); - ili9341_set_foreground(LCD_TRACE_1_COLOR + current_trace); - float ref = (trace[current_trace].refpos-NGRIDY) * trace[current_trace].scale; - float scale = trace[current_trace].scale; - for (int i = 0; i < NGRIDY; i++){ - if (ypos >= CELLHEIGHT) break; - if (ypos >= -FONT_GET_HEIGHT){ - plot_printf(buf, sizeof buf, "%.0f", ref); - cell_drawstring(buf, xpos, ypos); - } - ypos+=GRIDY; - ref-=scale; - } -} +#else +static const struct {uint16_t x, y;} marker_pos[]={ + {1 + CELLOFFSETX, 1 }, + {1 + CELLOFFSETX, 1 + FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 2*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 3*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 4*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 5*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 6*FONT_STR_HEIGHT}, + {1 + CELLOFFSETX, 1 + 7*FONT_STR_HEIGHT}, +}; +#endif +#if _USE_FONT_== 0 +#define MARKER_FREQ "%.6qHz" +#define MARKER_FREQ_SIZE 67 +#define PORT_Z_OFFSET 1 +#else +#define MARKER_FREQ "%qHz" +#define MARKER_FREQ_SIZE 112 +#define PORT_Z_OFFSET 0 #endif static void cell_draw_marker_info(int x0, int y0) { - char buf[24]; - int t; + int t, mk, xpos, ypos; if (active_marker == MARKER_INVALID) return; - int idx = markers[active_marker].index; + int active_marker_idx = markers[active_marker].index; int j = 0; - + // Marker (for current selected trace) display mode (selected more then 1 marker, and minimum one trace) if (previous_marker != MARKER_INVALID && current_trace != TRACE_INVALID) { - int t = current_trace; - int mk; + t = current_trace; for (mk = 0; mk < MARKERS_MAX; mk++) { if (!markers[mk].enabled) continue; - int xpos = 1 + bCELLOFFSETX - x0; - int ypos = 1 + j*(bFONT_STR_HEIGHT) - y0; - - ili9341_set_foreground(LCD_TRACE_1_COLOR + t); - if (mk == active_marker) - cell_drawstring_7x13(S_SARROW, xpos, ypos); - xpos += 7; - plot_printf(buf, sizeof buf, "M%d", mk+1); - cell_drawstring_7x13(buf, xpos, ypos); - xpos += 19; - //trace_get_info(t, buf, sizeof buf); - uint32_t freq = frequencies[markers[mk].index]; - if (uistat.marker_delta && mk != active_marker) { - uint32_t freq1 = frequencies[markers[active_marker].index]; - uint32_t delta = freq > freq1 ? freq - freq1 : freq1 - freq; - plot_printf(buf, sizeof buf, S_DELTA"%.9qHz", delta); - } else { - plot_printf(buf, sizeof buf, "%.10qHz", freq); - } - cell_drawstring_7x13(buf, xpos, ypos); - xpos += 94; - int didx = -1; // delta value index - if (uistat.marker_delta && mk != active_marker) - didx = markers[active_marker].index; - trace_get_value_string(t, buf, sizeof buf, measured[trace[t].channel], markers[mk].index, didx); - ili9341_set_foreground(LCD_FG_COLOR); - cell_drawstring_7x13(buf, xpos, ypos); + xpos = marker_pos[j].x - x0; + ypos = marker_pos[j].y - y0; j++; - } - - // draw marker delta - if (!uistat.marker_delta && active_marker != previous_marker) { - int idx0 = markers[previous_marker].index; - int xpos = (WIDTH/2-0) + bCELLOFFSETX - x0; - int ypos = 1 + j*(bFONT_STR_HEIGHT) - y0; - - plot_printf(buf, sizeof buf, S_DELTA"%d-%d:", active_marker+1, previous_marker+1); - ili9341_set_foreground(LCD_FG_COLOR); - cell_drawstring_7x13(buf, xpos, ypos); - xpos += 39; - if ((domain_mode & DOMAIN_MODE) == DOMAIN_FREQ) { - uint32_t freq = frequencies[idx]; - uint32_t freq1 = frequencies[idx0]; - uint32_t delta = freq >= freq1 ? freq - freq1 : freq1 - freq; - plot_printf(buf, sizeof buf, "%c%qHz", freq >= freq1 ? '+' : '-', delta); + lcd_set_foreground(LCD_TRACE_1_COLOR + t); + if (mk == active_marker) + cell_printf(xpos, ypos, S_SARROW); + xpos += FONT_WIDTH; + cell_printf(xpos, ypos, "M%d", mk+1); + xpos += 3*FONT_WIDTH - 2; + int32_t delta_index = -1; + uint32_t mk_index = markers[mk].index; + freq_t freq = get_marker_frequency(mk); + if ((props_mode & TD_MARKER_DELTA) && mk != active_marker) { + freq_t freq1 = get_marker_frequency(active_marker); + freq_t delta = freq > freq1 ? freq - freq1 : freq1 - freq; + delta_index = active_marker_idx; + cell_printf(xpos, ypos, S_DELTA MARKER_FREQ, delta); } else { - plot_printf(buf, sizeof buf, "%Fs (%Fm)", time_of_index(idx) - time_of_index(idx0), distance_of_index(idx) - distance_of_index(idx0)); + cell_printf(xpos, ypos, MARKER_FREQ, freq); } - cell_drawstring_7x13(buf, xpos, ypos); + xpos += MARKER_FREQ_SIZE; + lcd_set_foreground(LCD_FG_COLOR); + trace_print_value_string(xpos, ypos, t, mk_index, delta_index); } - } else { + } else /*if (active_marker != MARKER_INVALID)*/{ // Trace display mode for (t = 0; t < TRACES_MAX; t++) { if (!trace[t].enabled) continue; - int xpos = 1 + bCELLOFFSETX - x0; - int ypos = 1 + j*(bFONT_STR_HEIGHT) - y0; - - ili9341_set_foreground(LCD_TRACE_1_COLOR + t); - if (t == current_trace) - cell_drawstring_7x13(S_SARROW, xpos, ypos); - xpos += bFONT_WIDTH; - plot_printf(buf, sizeof buf, "CH%d", trace[t].channel); - cell_drawstring_7x13(buf, xpos, ypos); - xpos += 3*bFONT_WIDTH + 4; - - int n = trace_get_info(t, buf, sizeof buf); - cell_drawstring_7x13(buf, xpos, ypos); - xpos += n * bFONT_WIDTH + 2; - //xpos += 60; - trace_get_value_string(t, buf, sizeof buf, measured[trace[t].channel], idx, -1); - ili9341_set_foreground(LCD_FG_COLOR); - cell_drawstring_7x13(buf, xpos, ypos); + xpos = marker_pos[j].x - x0; + ypos = marker_pos[j].y - y0; j++; + lcd_set_foreground(LCD_TRACE_1_COLOR + t); + if (t == current_trace) + cell_printf(xpos, ypos, S_SARROW); + xpos += FONT_WIDTH; + cell_printf(xpos, ypos, get_trace_chname(t)); + xpos += 3*FONT_WIDTH + 4; + + int n = trace_print_info(xpos, ypos, t); + xpos += n * FONT_WIDTH + 2; + lcd_set_foreground(LCD_FG_COLOR); + trace_print_value_string(xpos, ypos, t, active_marker_idx, -1); } + } - // draw marker frequency - int xpos = (WIDTH/2 + 15) + bCELLOFFSETX - x0; - int ypos = 1 + j*(bFONT_STR_HEIGHT) - y0; - - ili9341_set_foreground(LCD_FG_COLOR); - if (uistat.lever_mode == LM_MARKER) - cell_drawstring_7x13(S_SARROW, xpos, ypos); - xpos += bFONT_WIDTH; - plot_printf(buf, sizeof buf, "M%d:", active_marker+1); - cell_drawstring_7x13(buf, xpos, ypos); - xpos += 3*bFONT_WIDTH + 4; - - if ((domain_mode & DOMAIN_MODE) == DOMAIN_FREQ) { - plot_printf(buf, sizeof buf, "%qHz", frequencies[idx]); - } else { - plot_printf(buf, sizeof buf, "%Fs (%Fm)", time_of_index(idx), distance_of_index(idx)); + lcd_set_foreground(LCD_FG_COLOR); + // Marker frequency data print +#if LCD_WIDTH == 480 || _USE_FONT_== 0 + xpos = 3 + (WIDTH/2) + CELLOFFSETX - x0; + ypos = 1 + ((j+1)/2)*FONT_STR_HEIGHT - y0; +#else + xpos = 12+(WIDTH/2) + CELLOFFSETX - x0; + ypos = 1 + j*FONT_STR_HEIGHT - y0; +#endif + + if (previous_marker != MARKER_INVALID && current_trace != TRACE_INVALID) { + // draw marker delta + if (!(props_mode & TD_MARKER_DELTA) && active_marker != previous_marker) { + int previous_marker_idx = markers[previous_marker].index; + cell_printf(xpos, ypos, S_DELTA"%d-%d:", active_marker+1, previous_marker+1); + xpos += 5*FONT_WIDTH + 2; + if ((props_mode & DOMAIN_MODE) == DOMAIN_FREQ) { + freq_t freq = get_marker_frequency(active_marker); + freq_t freq1 = get_marker_frequency(previous_marker); + freq_t delta = freq >= freq1 ? freq - freq1 : freq1 - freq; + cell_printf(xpos, ypos, "%c%qHz", freq >= freq1 ? '+' : '-', delta); + } else { + cell_printf(xpos, ypos, "%Fs (%Fm)", time_of_index(active_marker_idx) - time_of_index(previous_marker_idx), + distance_of_index(active_marker_idx) - distance_of_index(previous_marker_idx)); + } } - cell_drawstring_7x13(buf, xpos, ypos); } - ili9341_set_foreground(LCD_FG_COLOR); - if (electrical_delay != 0) { + else /*if (active_marker != MARKER_INVALID)*/{ + // draw marker frequency + if (lever_mode == LM_MARKER) + cell_printf(xpos, ypos, S_SARROW); + xpos += FONT_WIDTH; + cell_printf(xpos, ypos, "M%d:", active_marker+1); + //cell_drawstring(buf, xpos, ypos); + xpos += 3*FONT_WIDTH + 4; + if ((props_mode & DOMAIN_MODE) == DOMAIN_FREQ) + cell_printf(xpos, ypos, "%qHz", get_marker_frequency(active_marker)); + else + cell_printf(xpos, ypos, "%Fs (%Fm)", time_of_index(active_marker_idx), distance_of_index(active_marker_idx)); + } + + if (electrical_delay != 0.0f) { // draw electrical delay - int xpos = 21 + bCELLOFFSETX - x0; - int ypos = 1 + (j+1)*(bFONT_STR_HEIGHT) - y0; +#if LCD_WIDTH == 480 || _USE_FONT_== 0 + xpos = 1 + 18 + CELLOFFSETX - x0; + ypos = 1 + ((j+1)/2)*FONT_STR_HEIGHT - y0; +#else + xpos = 1 - x0; + ypos = 1 + (j)*FONT_STR_HEIGHT - y0; +#endif - if (uistat.lever_mode == LM_EDELAY) - cell_drawstring_7x13(S_SARROW, xpos, ypos); - xpos += 7; + if (lever_mode == LM_EDELAY) + cell_printf(xpos, ypos, S_SARROW); + xpos += 5; float edelay = electrical_delay * 1e-12; // to seconds - plot_printf(buf, sizeof buf, "Edelay %Fs %Fm", edelay, edelay * SPEED_OF_LIGHT * velocity_factor); - cell_drawstring_7x13(buf, xpos, ypos); + cell_printf(xpos, ypos, "Edelay: %Fs (%Fm)", edelay, edelay * (SPEED_OF_LIGHT / 100.0f) * velocity_factor); } +#ifdef __VNA_Z_RENORMALIZATION__ + if (current_props._portz != 50.0f) { + xpos = 1 + 18 + CELLOFFSETX - x0; + ypos = PORT_Z_OFFSET + ((j+1)/2 + 1)*FONT_STR_HEIGHT - y0; + cell_printf(xpos, ypos, "PORT-Z: 50 " S_RARROW " %F" S_OHM, current_props._portz); + } +#endif } -void +static void draw_frequencies(void) { - char buf1[32]; - char buf2[32]; buf2[0] = 0; - if ((domain_mode & DOMAIN_MODE) == DOMAIN_FREQ) { + char lm0 = lever_mode == LM_FREQ_0 ? S_SARROW[0] : ' '; + char lm1 = lever_mode == LM_FREQ_1 ? S_SARROW[0] : ' '; + // Draw frequency string + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); + lcd_fill(0, FREQUENCIES_YPOS, LCD_WIDTH, FONT_GET_HEIGHT); + // Prepare text for frequency string + if ((props_mode & DOMAIN_MODE) == DOMAIN_FREQ) { if (FREQ_IS_CW()) { - plot_printf(buf1, sizeof(buf1), " CW %qHz", get_sweep_frequency(ST_CW)); + lcd_printf(FREQUENCIES_XPOS1, FREQUENCIES_YPOS, "%c%s %15qHz", lm0, "CW", get_sweep_frequency(ST_CW)); } else if (FREQ_IS_STARTSTOP()) { - plot_printf(buf1, sizeof(buf1), " START %qHz", get_sweep_frequency(ST_START)); - plot_printf(buf2, sizeof(buf2), " STOP %qHz", get_sweep_frequency(ST_STOP)); + lcd_printf(FREQUENCIES_XPOS1, FREQUENCIES_YPOS, "%c%s %15qHz", lm0, "START", get_sweep_frequency(ST_START)); + lcd_printf(FREQUENCIES_XPOS2, FREQUENCIES_YPOS, "%c%s %15qHz", lm1, "STOP", get_sweep_frequency(ST_STOP)); } else if (FREQ_IS_CENTERSPAN()) { - plot_printf(buf1, sizeof(buf1), " CENTER %qHz", get_sweep_frequency(ST_CENTER)); - plot_printf(buf2, sizeof(buf2), " SPAN %qHz", get_sweep_frequency(ST_SPAN)); + lcd_printf(FREQUENCIES_XPOS1, FREQUENCIES_YPOS, "%c%s %15qHz", lm0,"CENTER", get_sweep_frequency(ST_CENTER)); + lcd_printf(FREQUENCIES_XPOS2, FREQUENCIES_YPOS, "%c%s %15qHz", lm1, "SPAN", get_sweep_frequency(ST_SPAN)); } } else { - plot_printf(buf1, sizeof(buf1), " START 0s"); - plot_printf(buf2, sizeof(buf2), "STOP %Fs (%Fm)", time_of_index(sweep_points-1), distance_of_index(sweep_points-1)); + lcd_printf(FREQUENCIES_XPOS1, FREQUENCIES_YPOS, "%c%s 0s", lm0, "START"); + lcd_printf(FREQUENCIES_XPOS2, FREQUENCIES_YPOS, "%c%s %Fs (%Fm)", lm1, "STOP", time_of_index(sweep_points-1), distance_of_index(sweep_points-1)); } - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); - ili9341_fill(0, FREQUENCIES_YPOS, LCD_WIDTH, FONT_GET_HEIGHT); - if (uistat.lever_mode == LM_CENTER) - buf1[0] = S_SARROW[0]; - if (uistat.lever_mode == LM_SPAN) - buf2[0] = S_SARROW[0]; - ili9341_drawstring(buf1, FREQUENCIES_XPOS1, FREQUENCIES_YPOS); - ili9341_drawstring(buf2, FREQUENCIES_XPOS2, FREQUENCIES_YPOS); - plot_printf(buf1, sizeof(buf1), "bw:%uHz %up", get_bandwidth_frequency(config.bandwidth), sweep_points); - ili9341_set_foreground(LCD_BW_TEXT_COLOR); - ili9341_drawstring(buf1, FREQUENCIES_XPOS3, FREQUENCIES_YPOS); + // Draw bandwidth and point count +#if LCD_WIDTH == 480 || _USE_FONT_== 0 + lcd_set_foreground(LCD_BW_TEXT_COLOR); + lcd_printf(FREQUENCIES_XPOS3, FREQUENCIES_YPOS,"bw:%uHz %up", get_bandwidth_frequency(config._bandwidth), sweep_points); +#endif } -void +/* + * Draw/update calibration status panel + */ +static void draw_cal_status(void) { uint32_t i; - int x = 0; - int y = 100; - ili9341_set_background(LCD_BG_COLOR); - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_fill(0, y, OFFSETX, 7*(FONT_STR_HEIGHT)); - // Set 'C' string for slot status - char c[4] = {'C', 0, 0, 0}; + int x = CALIBRATION_INFO_POSX; + int y = CALIBRATION_INFO_POSY; + lcd_set_background(LCD_BG_COLOR); + lcd_set_foreground(LCD_DISABLE_CAL_COLOR); + lcd_fill(x, y, OFFSETX - x, 10*(FONT_STR_HEIGHT)); + if (cal_status & CALSTAT_APPLY) { - if (cal_status & CALSTAT_INTERPOLATED){ili9341_set_foreground(LCD_NORMAL_BAT_COLOR); c[0] = 'c';} - c[1] = '0' + lastsaveid; - ili9341_drawstring(c, x, y); + // Set 'C' string for slot status + char c[4] = {'C', '0' + lastsaveid, 0, 0}; + if (lastsaveid == NO_SAVE_SLOT) c[1] = '*'; + if (cal_status & CALSTAT_INTERPOLATED){lcd_set_foreground(LCD_INTERP_CAL_COLOR); c[0] = 'c';} + else lcd_set_foreground(LCD_FG_COLOR); + lcd_drawstring(x, y, c); + lcd_set_foreground(LCD_FG_COLOR); } - ili9341_set_foreground(LCD_FG_COLOR); + static const struct {char text, zero; uint16_t mask;} calibration_text[]={ + {'O', 0, CALSTAT_OPEN}, + {'S', 0, CALSTAT_SHORT}, {'D', 0, CALSTAT_ED}, {'R', 0, CALSTAT_ER}, {'S', 0, CALSTAT_ES}, {'T', 0, CALSTAT_ET}, + {'t', 0, CALSTAT_THRU}, {'X', 0, CALSTAT_EX} }; for (i = 0; i < ARRAY_COUNT(calibration_text); i++) if (cal_status & calibration_text[i].mask) - ili9341_drawstring(&calibration_text[i].text, x, y+=FONT_STR_HEIGHT); - - if (cal_status & CALSTAT_APPLY){ - const properties_t *src = caldata_reference(); - if (src && src->_power != current_props._power) - ili9341_set_foreground(LCD_LOW_BAT_COLOR); + lcd_drawstring(x, y+=FONT_STR_HEIGHT, &calibration_text[i].text); + + if ((cal_status & CALSTAT_APPLY) && cal_power != current_props._power) + lcd_set_foreground(LCD_DISABLE_CAL_COLOR); + + // 2,4,6,8 mA power or auto + lcd_printf(x, y+=FONT_STR_HEIGHT, "P%c", current_props._power > 3 ? ('a') : (current_props._power * 2 + '2')); +#ifdef __USE_SMOOTH__ + y+=FONT_STR_HEIGHT; + uint8_t smooth = get_smooth_factor(); + if (smooth > 0){ + lcd_set_foreground(LCD_FG_COLOR); + lcd_printf(x, y+=FONT_STR_HEIGHT, "s%d", smooth); } - c[0] = 'P'; - c[1] = current_props._power > 3 ? ('a') : (current_props._power * 2 + '2'); // 2,4,6,8 mA power or auto - ili9341_drawstring(c, x, y); +#endif } -// Draw battery level +/* + * Draw battery level + */ #define BATTERY_TOP_LEVEL 4100 #define BATTERY_BOTTOM_LEVEL 3200 #define BATTERY_WARNING_LEVEL 3300 - static void draw_battery_status(void) { int16_t vbat = adc_vbat_read(); if (vbat <= 0) return; - uint8_t string_buf[16]; + uint8_t string_buf[24]; // Set battery color - ili9341_set_foreground(vbat < BATTERY_WARNING_LEVEL ? LCD_LOW_BAT_COLOR : LCD_NORMAL_BAT_COLOR); - ili9341_set_background(LCD_BG_COLOR); + lcd_set_foreground(vbat < BATTERY_WARNING_LEVEL ? LCD_LOW_BAT_COLOR : LCD_NORMAL_BAT_COLOR); + lcd_set_background(LCD_BG_COLOR); // plot_printf(string_buf, sizeof string_buf, "V:%d", vbat); -// ili9341_drawstringV(string_buf, 1, 60); +// lcd_drawstringV(string_buf, 1, 60); // Prepare battery bitmap image // Battery top int x = 0; @@ -1779,26 +1990,43 @@ static void draw_battery_status(void) string_buf[x++] = 0b10000001; string_buf[x++] = 0b11111111; // Draw battery - ili9341_blitBitmap(1, 1, 8, x, string_buf); + lcd_blitBitmap(BATTERY_ICON_POSX, BATTERY_ICON_POSY, 8, x, string_buf); } +/* + * Update cells behind menu + */ void -request_to_redraw_grid(void) +request_to_draw_cells_behind_menu(void) { - redraw_request |= REDRAW_AREA; + // Values Hardcoded from ui.c + invalidate_rect(LCD_WIDTH-MENU_BUTTON_WIDTH-OFFSETX, 0, LCD_WIDTH-OFFSETX, LCD_HEIGHT-1); + request_to_redraw(REDRAW_CELLS); } +/* + * Update cells behind numeric input + */ +void +request_to_draw_cells_behind_numeric_input(void) +{ + // Values Hardcoded from ui.c + invalidate_rect(0, LCD_HEIGHT-NUM_INPUT_HEIGHT, LCD_WIDTH-1, LCD_HEIGHT-1); + request_to_redraw(REDRAW_CELLS); +} + +/* + * Set update mask for next screen update + */ void -redraw_frame(void) +request_to_redraw(uint8_t mask) { - ili9341_set_background(LCD_BG_COLOR); - ili9341_clear_screen(); - draw_frequencies(); - draw_cal_status(); + redraw_request|= mask; } void plot_init(void) { - force_set_markmap(); + request_to_redraw(REDRAW_AREA | REDRAW_BATTERY | REDRAW_CAL_STATUS | REDRAW_FREQUENCY); + draw_all(true); } diff --git a/si5351.c b/si5351.c index cda5ca7..e557864 100644 --- a/si5351.c +++ b/si5351.c @@ -22,8 +22,6 @@ #include "nanovna.h" #include "si5351.h" -// XTAL frequency on si5351 -#define XTALFREQ 26000000U // audio codec frequency clock #define CLK2_FREQUENCY AUDIO_CLOCK_REF @@ -38,7 +36,6 @@ static uint8_t current_band = 0; static uint8_t current_power = 0; static uint32_t current_freq = 0; -static int32_t current_offset = FREQUENCY_OFFSET; // Use cache for this reg, not update if not change static uint8_t clk_cache[3] = {0, 0, 0}; @@ -47,28 +44,33 @@ static void si5351_reset_cache(void){ current_freq = 0; } -// Generator ready delays, values in x100 us -#if 0 - uint16_t timings[16]={ 4, 3, 20, 1000, 5000, 0, 4, 25}; // For H device timings -//uint16_t timings[16]={ 2, 2, 20, 1000, 5000, 0, 3, 25}; // For H4 device timings -void si5351_set_timing(int i, int v) {timings[i]=v;} -#define DELAY_BAND_1_2 timings[0] // Delay for bands -#define DELAY_BAND_3_4 timings[1] // Delay for bands -#define DELAY_BANDCHANGE timings[2] // Band changes need set additional delay after reset PLL -#define DELAY_RESET_PLL_BEFORE timings[3] // Delay before set new PLL values -#define DELAY_RESET_PLL_AFTER timings[4] // Delay after set new PLL values -#define DELAY_GAIN_CHANGE timings[5] // Delay for gain change -//#define DELAY_CHANNEL_CHANGE timings[6] // defined in main.c switch channel delay -//#define DELAY_SWEEP_START timings[7] // defined in main.c delay at sweep start - -#else -#define DELAY_BAND_1_2 4 // Delay for bands 1-2 -#define DELAY_BAND_3_4 3 // Delay for bands 3-4 -#define DELAY_BANDCHANGE 20 // Band changes need set additional delay after reset PLL -// Delay after set new PLL values, and send reset -#define DELAY_RESET_PLL_BEFORE 1000 // 1000 possibly not need it if align freq -#define DELAY_RESET_PLL_AFTER 5000 // 3500 possibly not need it if align freq -#define DELAY_GAIN_CHANGE 0 // Delay for gain change +#ifdef ENABLE_SI5351_TIMINGS +// For debug +uint16_t timings[8]={ + DELAY_BAND_1_2, // 0 + DELAY_BAND_3_4, // 1 + DELAY_BANDCHANGE, // 2 + DELAY_CHANNEL_CHANGE, // 3 + DELAY_SWEEP_START, // 4 + DELAY_RESET_PLL_BEFORE, // 5 + DELAY_RESET_PLL_AFTER, // 6 +}; +inline void si5351_set_timing(int i, int v) {timings[i]=v;} +#undef DELAY_BAND_1_2 +#undef DELAY_BAND_3_4 +#undef DELAY_BANDCHANGE +#undef DELAY_RESET_PLL_BEFORE +#undef DELAY_RESET_PLL_AFTER +#undef DELAY_CHANNEL_CHANGE +#undef DELAY_SWEEP_START + +#define DELAY_BAND_1_2 timings[0] +#define DELAY_BAND_3_4 timings[1] +#define DELAY_BANDCHANGE timings[2] +#define DELAY_CHANNEL_CHANGE timings[3] +#define DELAY_SWEEP_START timings[4] +#define DELAY_RESET_PLL_BEFORE timings[5] +#define DELAY_RESET_PLL_AFTER timings[6] #endif uint32_t si5351_get_frequency(void) @@ -76,11 +78,14 @@ uint32_t si5351_get_frequency(void) return current_freq; } +#ifdef USE_VARIABLE_OFFSET void si5351_set_frequency_offset(int32_t offset) { si5351_reset_cache(); - current_offset = offset; + generate_DSP_Table(offset); + IF_OFFSET = offset; } +#endif void si5351_set_power(uint8_t drive_strength){ if (drive_strength == current_power) return; @@ -152,8 +157,8 @@ si5351_init(void) si5351_bulk_write(p, len); p += len; } - // Set any (let it be 32MHz) frequency for AIC can run - si5351_set_frequency(32000000U, 0); + // Set any (let it be XTALFREQ) frequency for AIC can run + si5351_set_frequency(XTALFREQ, 0); } static const uint8_t disable_output[] = { @@ -191,6 +196,13 @@ void si5351_enable_output(void) si5351_reset_cache(); } +void si5351_set_tcxo(uint32_t xtal){ + if (xtal < XTALFREQ - 2000000 || + xtal > XTALFREQ + 2000000) xtal = XTALFREQ; + config._xtal_freq = xtal; + si5351_reset_cache(); +} + // Set PLL freq = XTALFREQ * (mult + num/denom) static void si5351_setupPLL(uint8_t pllSource, /* SI5351_REG_PLL_A or SI5351_REG_PLL_B */ uint32_t mult, @@ -327,7 +339,7 @@ si5351_set_frequency_fixedpll(uint8_t channel, uint64_t pllfreq, uint32_t freq, static void si5351_setupPLL_freq(uint32_t pllSource, uint32_t freq, uint32_t div, uint32_t mul) { - uint32_t denom = XTALFREQ * mul; + uint32_t denom = config._xtal_freq * mul; uint64_t pllfreq = (uint64_t)freq * div; uint32_t multi = pllfreq / denom; uint32_t num = pllfreq % denom; @@ -389,19 +401,19 @@ const #endif band_strategy_t band_s[] = { { 0U, 0, { 0}, 0, 0, -1, -1, -1, -1, 1}, // 0 - { 10000U, SI5351_FIXED_PLL, { 8}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 1 - { 100000000U, SI5351_FIXED_PLL, {32}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 2 + { 26000U, SI5351_FIXED_PLL, { 8}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_2MA, SI5351_CLK_DRIVE_STRENGTH_2MA, 0, 0, 1}, // 1 + { 100000000U, SI5351_FIXED_PLL, {32}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_2MA, SI5351_CLK_DRIVE_STRENGTH_2MA, 0, 0, 1}, // 2 { 130000000U, SI5351_FIXED_MULT,{ 8}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 3 { 180000000U, SI5351_FIXED_MULT,{ 6}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 4 { 1, SI5351_FIXED_MULT,{ 4}, 1, 1, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 0, 0, 1}, // 5 - { 460000000U, SI5351_FIXED_MULT,{ 6}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 50, 50, 3*5*6}, // 6 - { 600000000U, SI5351_FIXED_MULT,{ 4}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 50, 50, 3*5*4}, // 7 - { 3, SI5351_FIXED_MULT,{ 4}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 50, 50, 3*5*4}, // 8 + { 460000000U, SI5351_FIXED_MULT,{ 6}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 40, 40, 1}, // 6 + { 600000000U, SI5351_FIXED_MULT,{ 4}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 40, 40, 1}, // 7 + { 3, SI5351_FIXED_MULT,{ 4}, 3, 5, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 50, 50, 1}, // 8 - { 1200000000U, SI5351_FIXED_MULT,{ 4}, 5, 7, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 70, 70, 5*7*4}, // 9 - { 5, SI5351_FIXED_MULT,{ 4}, 5, 7, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 70, 70, 5*7*4}, //10 + { 1200000000U, SI5351_FIXED_MULT,{ 4}, 5, 7, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 70, 70, 1}, // 9 + { 5, SI5351_FIXED_MULT,{ 4}, 5, 7, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_6MA, 70, 70, 1}, //10 { 1800000000U, SI5351_FIXED_MULT,{ 4}, 7, 9, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 70, 70, 7*9*4}, //11 { 7, SI5351_FIXED_MULT,{ 4}, 7, 9, SI5351_CLK_DRIVE_STRENGTH_8MA, SI5351_CLK_DRIVE_STRENGTH_8MA, 70, 70, 7*9*4}, //12 @@ -439,7 +451,7 @@ uint32_t si5351_get_harmonic_lvl(uint32_t freq){ uint16_t i; for (i = 0; i < ARRAY_COUNT(band_s); i++){ - uint32_t f = band_s[i].freq; if (f < 20) f*=config.harmonic_freq_threshold; + uint32_t f = band_s[i].freq; if (f < 20) f*=config._harmonic_freq_threshold; if (freq <= f) return i; } @@ -461,34 +473,32 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) if (freq == 0) return 0; uint32_t rdiv = SI5351_R_DIV_1; uint32_t fdiv, pll_n; - uint32_t ofreq = freq + current_offset; + uint32_t ofreq = freq + IF_OFFSET; // Select optimal band for prepared freq - if (freq < 10000U) { - rdiv = SI5351_R_DIV_128; + if (freq < 26000U) { + rdiv = SI5351_R_DIV(7); + drive_strength = SI5351_CLK_DRIVE_STRENGTH_2MA; // Always use 2ma freq<<= 7; ofreq<<= 7; band = 1; - } else if (freq <= 500000U) { - rdiv = SI5351_R_DIV_64; - freq<<= 6; - ofreq<<= 6; - band = 2; - } else if (freq <= 4000000U) { - rdiv = SI5351_R_DIV_8; - freq<<= 3; - ofreq<<= 3; + } else if (freq <= 1000000U) { + rdiv = SI5351_R_DIV(4); + freq<<= 4; + ofreq<<= 4; band = 2; } else band = si5351_get_harmonic_lvl(freq); +#if 0 uint32_t align = band_s[band].freq_align; if (align > 1){ freq/=align; freq*=align; ofreq = freq + current_offset; } +#endif // Check current power settings if (current_power != drive_strength){ si5351_reset_cache(); @@ -499,13 +509,13 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) return 0; if (current_band != band) { - si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN); - si5351_reset_pll(SI5351_PLL_RESET_A | SI5351_PLL_RESET_B); - if (band_s[current_band].l_gain != band_s[band].l_gain || band_s[current_band].r_gain != band_s[band].r_gain){ +// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN); + if (DELAY_RESET_PLL_BEFORE) + si5351_reset_pll(SI5351_PLL_RESET_A | SI5351_PLL_RESET_B); + // Set new gain values + if (band_s[current_band].l_gain != band_s[band].l_gain || band_s[current_band].r_gain != band_s[band].r_gain) tlv320aic3204_set_gain(band_s[band].l_gain, band_s[band].r_gain); -// delay = DELAY_GAIN_CHANGE; - } - // Possibly not need add delay now + // Add delay if (DELAY_RESET_PLL_BEFORE) chThdSleepMicroseconds(DELAY_RESET_PLL_BEFORE); } @@ -522,12 +532,12 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) if (current_band != band) { si5351_setupPLL(SI5351_REG_PLL_A, pll_n, 0, 1); si5351_setupPLL(SI5351_REG_PLL_B, PLL_N_2, 0, 1); - si5351_set_frequency_fixedpll(2, XTALFREQ * PLL_N_2, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_PLL_SELECT_B); + si5351_set_frequency_fixedpll(2, config._xtal_freq * PLL_N_2, CLK2_FREQUENCY, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_PLL_SELECT_B); } delay = DELAY_BAND_1_2; // Calculate and set CH0 and CH1 divider - si5351_set_frequency_fixedpll(0, (uint64_t)omul * XTALFREQ * pll_n, ofreq, rdiv, ods | SI5351_CLK_PLL_SELECT_A); - si5351_set_frequency_fixedpll(1, (uint64_t) mul * XTALFREQ * pll_n, freq, rdiv, ds | SI5351_CLK_PLL_SELECT_A); + si5351_set_frequency_fixedpll(0, (uint64_t)omul * config._xtal_freq * pll_n, ofreq, rdiv, ods | SI5351_CLK_PLL_SELECT_A); + si5351_set_frequency_fixedpll(1, (uint64_t) mul * config._xtal_freq * pll_n, freq, rdiv, ds | SI5351_CLK_PLL_SELECT_A); break; #if 0 case SI5351_MIXED: @@ -544,7 +554,7 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) si5351_setupMultisynth(1, fdiv, 0, 1, SI5351_R_DIV_1, ds | SI5351_CLK_PLL_SELECT_B); // Set CH0 divider - si5351_set_frequency_fixedpll(0, (uint64_t)omul * XTALFREQ * pll_n, ofreq, rdiv, ods | SI5351_CLK_PLL_SELECT_A); + si5351_set_frequency_fixedpll(0, (uint64_t)omul * config._xtal_freq * pll_n, ofreq, rdiv, ods | SI5351_CLK_PLL_SELECT_A); // Calculate CH2 freq = CLK2_FREQUENCY, depend from calculated before CH1 PLLB = (freq/mul)*fdiv si5351_set_frequency_fixedpll(2, (uint64_t)freq * fdiv, CLK2_FREQUENCY * mul, SI5351_R_DIV_1, SI5351_CLK_DRIVE_STRENGTH_2MA | SI5351_CLK_PLL_SELECT_B); delay= DELAY_BAND_3_4; @@ -568,11 +578,12 @@ si5351_set_frequency(uint32_t freq, uint8_t drive_strength) break; } if (current_band != band) { - si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, ~(SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN)); +// si5351_write(SI5351_REG_3_OUTPUT_ENABLE_CONTROL, ~(SI5351_CLK0_EN|SI5351_CLK1_EN|SI5351_CLK2_EN)); // Possibly not need add delay now - if (DELAY_RESET_PLL_AFTER) + if (DELAY_RESET_PLL_AFTER){ chThdSleepMicroseconds(DELAY_RESET_PLL_AFTER); - si5351_reset_pll(SI5351_PLL_RESET_A|SI5351_PLL_RESET_B); + si5351_reset_pll(SI5351_PLL_RESET_A|SI5351_PLL_RESET_B); + } current_band = band; delay = DELAY_BANDCHANGE; } diff --git a/si5351.h b/si5351.h index 30009aa..d20d4b9 100644 --- a/si5351.h +++ b/si5351.h @@ -60,6 +60,7 @@ #define SI5351_R_DIV_32 (5<<4) #define SI5351_R_DIV_64 (6<<4) #define SI5351_R_DIV_128 (7<<4) +#define SI5351_R_DIV(n) ((n)<<4) #define SI5351_REG_177_PLL_RESET 177 #define SI5351_PLL_RESET_B (1<<7) @@ -83,6 +84,7 @@ void si5351_set_power(uint8_t drive_strength); void si5351_bulk_write(const 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); // Get info functions uint32_t si5351_get_frequency(void); diff --git a/tlv320aic3204.c b/tlv320aic3204.c index cdf3f15..83d03a5 100644 --- a/tlv320aic3204.c +++ b/tlv320aic3204.c @@ -285,10 +285,9 @@ static const uint8_t conf_data[] = { 0x00, 0x01, // Select Page 1 0x01, 0x08, // Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO 0x02, 0x01, // Enable Master Analog Power Control - 0x7b, 0x01, // Set the REF charging time to 40ms // 0x14, 0x25, // HP soft stepping settings for optimal pop performance at power up Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound. -// 0x0a, 0x33, // Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to 1.65V - 0x0a, 0x40, // Set the Input Common Mode to 0.75V and Output Common Mode for Headphone to 1.65V + 0x0a, 0x33, // Set the Input Common Mode to 0.9V and Output Common Mode for Headphone to 1.65V +// 0x0a, 0x40, // Set the Input Common Mode to 0.75V and Output Common Mode for Headphone to 1.65V 0x3d, 0x00, // Select ADC PTM_R4 // 0x3d, 0x64, // Select ADC PTM_R3 diff --git a/ui.c b/ui.c index dae5a78..2810180 100644 --- a/ui.c +++ b/ui.c @@ -24,16 +24,6 @@ #include "chprintf.h" #include "nanovna.h" #include "si5351.h" -#include - -uistat_t uistat = { -// digit: 6, - _current_trace: 0, - _previous_marker: MARKER_INVALID, - lever_mode: LM_MARKER, - marker_delta: FALSE, - marker_tracking : FALSE, -}; #define NO_EVENT 0 #define EVT_BUTTON_SINGLE_CLICK 0x01 @@ -43,10 +33,10 @@ uistat_t uistat = { #define EVT_DOWN 0x20 #define EVT_REPEAT 0x40 -#define BUTTON_DOWN_LONG_TICKS 5000 /* 500ms */ -#define BUTTON_DOUBLE_TICKS 2500 /* 250ms */ -#define BUTTON_REPEAT_TICKS 400 /* 10ms */ -#define BUTTON_DEBOUNCE_TICKS 400 /* 40ms */ +#define BUTTON_DOWN_LONG_TICKS MS2ST(500) // 500ms +#define BUTTON_DOUBLE_TICKS MS2ST(250) // 250ms +#define BUTTON_REPEAT_TICKS MS2ST( 40) // 40ms +#define BUTTON_DEBOUNCE_TICKS MS2ST( 20) // 20ms /* lever switch assignment */ #define BIT_UP1 3 @@ -60,20 +50,9 @@ static uint16_t last_button = 0b0000; static systime_t last_button_down_ticks; static systime_t last_button_repeat_ticks; -volatile uint8_t operation_requested = OP_NONE; +uint8_t operation_requested = OP_NONE; -#ifdef __USE_SD_CARD__ -#if SPI_BUFFER_SIZE < 2048 -#error "SPI_BUFFER_SIZE for SD card support need size >= 2048" -#else -// Fat file system work area (at the end of spi_buffer) -static FATFS *fs_volume = (FATFS *)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS)); -// FatFS file object (at the end of spi_buffer) -static FIL *fs_file = ( FIL*)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS) - sizeof(FIL)); -// Filename object (at the end of spi_buffer) -static char *fs_filename = ( char*)(((uint8_t*)(&spi_buffer[SPI_BUFFER_SIZE])) - sizeof(FATFS) - sizeof(FIL) - FF_LFN_BUF - 4); -#endif -#endif +static uint16_t menu_button_height = MENU_BUTTON_HEIGHT(MENU_BUTTON_MIN); enum { UI_NORMAL, UI_MENU, UI_NUMERIC, UI_KEYPAD @@ -82,7 +61,16 @@ enum { // Keypad structures // Enum for keypads_list enum { - KM_START, KM_STOP, KM_CENTER, KM_SPAN, KM_CW, KM_SCALE, KM_REFPOS, KM_EDELAY, KM_VELOCITY_FACTOR, KM_SCALEDELAY, KM_NONE + KM_START = 0, KM_STOP, KM_CENTER, KM_SPAN, KM_CW, KM_VAR, + KM_SCALE, KM_REFPOS, KM_EDELAY, KM_VELOCITY_FACTOR, KM_SCALEDELAY, + KM_XTAL, KM_THRESHOLD, KM_VBAT, +#ifdef __S21_MEASURE__ + KM_MEASURE_R, +#endif +#ifdef __VNA_Z_RENORMALIZATION__ + KM_Z_PORT, +#endif + KM_NONE }; typedef struct { @@ -96,26 +84,25 @@ typedef struct { const char *name; } keypads_list; // Max keyboard input length -#define NUMINPUT_LEN 10 +#define NUMINPUT_LEN 12 static uint8_t ui_mode = UI_NORMAL; static const keypads_t *keypads; static uint8_t keypad_mode; -static uint8_t keypads_last_index; -static char kp_buf[NUMINPUT_LEN+1]; +static char kp_buf[NUMINPUT_LEN+2]; static int8_t kp_index = 0; static uint8_t menu_current_level = 0; -static int8_t selection = 0; +static int8_t selection = -1; // UI menu structure // Type of menu item: #define MT_NONE 0x00 -#define MT_BLANK 0x01 -#define MT_SUBMENU 0x02 -#define MT_CALLBACK 0x03 -#define MT_CANCEL 0x04 -#define MT_CLOSE 0x05 -#define MT_ADV_CALLBACK 0x06 +#define MT_SUBMENU 0x01 +#define MT_CALLBACK 0x02 +#define MT_ADV_CALLBACK 0x03 + +// Set for custom label +#define MT_CUSTOM_LABEL 0 // Button definition (used in MT_ADV_CALLBACK for custom) #define BUTTON_ICON_NONE -1 @@ -123,6 +110,8 @@ static int8_t selection = 0; #define BUTTON_ICON_CHECK 1 #define BUTTON_ICON_GROUP 2 #define BUTTON_ICON_GROUP_CHECKED 3 +#define BUTTON_ICON_CHECK_AUTO 4 +#define BUTTON_ICON_CHECK_MANUAL 5 #define BUTTON_BORDER_NONE 0x00 #define BUTTON_BORDER_WIDTH_MASK 0x0F @@ -146,9 +135,10 @@ typedef struct { union { int32_t i; uint32_t u; + float f; const char *text; - } p1, p2; // void data for label printf - + } p1; // void data for label printf + char label[32]; } button_t; // Call back functions for MT_CALLBACK type @@ -186,21 +176,19 @@ static int16_t last_touch_y; static void ui_mode_normal(void); static void ui_mode_menu(void); +static void draw_menu(uint32_t mask); static void ui_mode_keypad(int _keypad_mode); -static void draw_menu(void); -static void leave_ui_mode(void); -static void erase_menu_buttons(void); -static void ui_process_keypad(void); static void touch_position(int *x, int *y); static void menu_move_back(bool leave_ui); static void menu_push_submenu(const menuitem_t *submenu); + void drawMessageBox(char *header, char *text, uint32_t delay); #ifdef UI_USE_NUMERIC_INPUT static void ui_mode_numeric(int _keypad_mode); #endif -static int btn_check(void) +static uint16_t btn_check(void) { systime_t ticks; // Debounce input @@ -208,9 +196,9 @@ static int btn_check(void) ticks = chVTGetSystemTimeX(); if(ticks - last_button_down_ticks > BUTTON_DEBOUNCE_TICKS) break; - chThdSleepMilliseconds(10); + chThdSleepMilliseconds(2); } - int status = 0; + uint16_t status = 0; uint16_t cur_button = READ_PORT() & BUTTON_MASK; // Detect only changed and pressed buttons uint16_t button_set = (last_button ^ cur_button) & cur_button; @@ -226,7 +214,7 @@ static int btn_check(void) return status; } -static int btn_wait_release(void) +static uint16_t btn_wait_release(void) { while (TRUE) { systime_t ticks = chVTGetSystemTimeX(); @@ -241,7 +229,7 @@ static int btn_wait_release(void) uint16_t changed = last_button ^ cur_button; if (dt >= BUTTON_DOWN_LONG_TICKS && (cur_button & (1< BUTTON_DOWN_LONG_TICKS && ticks > last_button_repeat_ticks) { - int status = 0; + uint16_t status = 0; if (cur_button & (1< i; j--) { // unordered part if (v[j] < v[j - 1]) { - SWAP_16(v[j], v[j - 1]); + SWAP(uint16_t, v[j], v[j - 1]); swapped = true; } } @@ -287,8 +281,8 @@ static void bubbleSort(uint16_t *v, int n) { //******************************************************************************* #ifdef SOFTWARE_TOUCH // ADC read count for measure X and Y (2^N count) -#define TOUCH_X_N 2 -#define TOUCH_Y_N 2 +#define TOUCH_X_N 3 +#define TOUCH_Y_N 3 static int touch_measure_y(void) { @@ -348,7 +342,7 @@ touch_prepare_sense(void) // chThdSleepMilliseconds(10); // Wait 10ms for denounce touch } -void +static void touch_start_watchdog(void) { if (touch_status_flag&TOUCH_INTERRUPT_ENABLED) return; @@ -356,7 +350,7 @@ touch_start_watchdog(void) adc_start_analog_watchdog(); } -void +static void touch_stop_watchdog(void) { if (!(touch_status_flag&TOUCH_INTERRUPT_ENABLED)) return; @@ -415,7 +409,6 @@ touch_check(void) //******************************************************************************* #endif // end SOFTWARE_TOUCH - static inline void touch_wait_release(void) { @@ -435,30 +428,30 @@ touch_cal_exec(void) { int x1, x2, y1, y2; - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); - ili9341_clear_screen(); - ili9341_line(0, 0, 0, 32); - ili9341_line(0, 0, 32, 0); - ili9341_drawstring_7x13("TOUCH UPPER LEFT", 10, 10); + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); + lcd_clear_screen(); + lcd_line(0, 0, 0, 32); + lcd_line(0, 0, 32, 0); + lcd_drawstring(10, 10, "TOUCH UPPER LEFT"); touch_wait_release(); x1 = last_touch_x; y1 = last_touch_y; - ili9341_clear_screen(); - ili9341_line(LCD_WIDTH-1, LCD_HEIGHT-1, LCD_WIDTH-1, LCD_HEIGHT-32); - ili9341_line(LCD_WIDTH-1, LCD_HEIGHT-1, LCD_WIDTH-32, LCD_HEIGHT-1); - ili9341_drawstring_7x13("TOUCH LOWER RIGHT", LCD_WIDTH-17*(bFONT_WIDTH)-10, LCD_HEIGHT-bFONT_GET_HEIGHT-10); + lcd_clear_screen(); + lcd_line(LCD_WIDTH-1, LCD_HEIGHT-1, LCD_WIDTH-1, LCD_HEIGHT-32); + lcd_line(LCD_WIDTH-1, LCD_HEIGHT-1, LCD_WIDTH-32, LCD_HEIGHT-1); + lcd_drawstring(LCD_WIDTH-17*(FONT_WIDTH)-10, LCD_HEIGHT-FONT_GET_HEIGHT-10, "TOUCH LOWER RIGHT"); touch_wait_release(); x2 = last_touch_x; y2 = last_touch_y; - config.touch_cal[0] = x1; - config.touch_cal[1] = y1; - config.touch_cal[2] = (x2 - x1) * 16 / LCD_WIDTH; - config.touch_cal[3] = (y2 - y1) * 16 / LCD_HEIGHT; + config._touch_cal[0] = x1; + config._touch_cal[1] = y1; + config._touch_cal[2] = (x2 - x1) * 16 / LCD_WIDTH; + config._touch_cal[3] = (y2 - y1) * 16 / LCD_HEIGHT; } void @@ -466,55 +459,69 @@ touch_draw_test(void) { int x0, y0; int x1, y1; - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); - ili9341_clear_screen(); - ili9341_drawstring_7x13("TOUCH TEST: DRAG PANEL, PRESS BUTTON TO FINISH", OFFSETX, LCD_HEIGHT - bFONT_GET_HEIGHT); + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); + lcd_clear_screen(); + lcd_drawstring(OFFSETX, LCD_HEIGHT - FONT_GET_HEIGHT, "TOUCH TEST: DRAG PANEL, PRESS BUTTON TO FINISH"); - do { + while (1) { + if (btn_check() & EVT_BUTTON_SINGLE_CLICK) break; if (touch_check() == EVT_TOUCH_PRESSED){ touch_position(&x0, &y0); do { chThdSleepMilliseconds(50); touch_position(&x1, &y1); - ili9341_line(x0, y0, x1, y1); + lcd_line(x0, y0, x1, y1); x0 = x1; y0 = y1; } while (touch_check() != EVT_TOUCH_RELEASED); } - }while (!(btn_check() & EVT_BUTTON_SINGLE_CLICK)); + } } static void touch_position(int *x, int *y) { - int tx = (last_touch_x - config.touch_cal[0]) * 16 / config.touch_cal[2]; + int tx = (last_touch_x - config._touch_cal[0]) * 16 / config._touch_cal[2]; if (tx<0) tx = 0; else if (tx>=LCD_WIDTH ) tx = LCD_WIDTH -1; - int ty = (last_touch_y - config.touch_cal[1]) * 16 / config.touch_cal[3]; + int ty = (last_touch_y - config._touch_cal[1]) * 16 / config._touch_cal[3]; if (ty<0) ty = 0; else if (ty>=LCD_HEIGHT) ty = LCD_HEIGHT-1; *x = tx; *y = ty; } -void -show_version(bool mode) +static void +show_version(void) { int x = 5, y = 5, i = 1; - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); - ili9341_clear_screen(); - uint16_t shift = 0b000100000; - ili9341_drawstring_size(BOARD_NAME, x , y, 3); + lcd_clear_screen(); + uint16_t shift = 0b00010101000; + lcd_drawstring_size(BOARD_NAME, x , y, 3); y+=FONT_GET_HEIGHT*3+3-5; +#if LCD_WIDTH == 480 || _USE_FONT_== 0 while (info_about[i]) { do {shift>>=1; y+=5;} while (shift&1); - ili9341_drawstring(info_about[i++], x, y+=FONT_STR_HEIGHT+3-5); + lcd_drawstring(x, y+=FONT_STR_HEIGHT+3-5, info_about[i++]); } - // Update battery and time + lcd_printf(x, y+= FONT_STR_HEIGHT + 3, "TCXO = %qHz", config._xtal_freq); + y+=3*FONT_STR_HEIGHT; +#else + while (info_about[i]) { + do {shift>>=1; y+=2;} while (shift&1); + lcd_drawstring(x, y+=FONT_STR_HEIGHT -2, info_about[i++]); + } + lcd_printf(x, y+= FONT_STR_HEIGHT , "TCXO = %qHz", config._xtal_freq); + y+=2*FONT_STR_HEIGHT; + +#endif + + // Update battery and time uint16_t cnt = 0; - while (mode) { + while (true) { if (touch_check() == EVT_TOUCH_PRESSED) break; if (btn_check() & EVT_BUTTON_SINGLE_CLICK) @@ -522,11 +529,10 @@ show_version(bool mode) chThdSleepMilliseconds(40); if ((cnt++)&0x07) continue; // Not update time so fast - char buffer[32]; #ifdef __USE_RTC__ uint32_t tr = rtc_get_tr_bin(); // TR read first uint32_t dr = rtc_get_dr_bin(); // DR read second - plot_printf(buffer, sizeof(buffer), "Time: 20%02d/%02d/%02d %02d:%02d:%02d" " (LS%c)", + lcd_printf(x, y, "Time: 20%02d/%02d/%02d %02d:%02d:%02d" " (LS%c)", RTC_DR_YEAR(dr), RTC_DR_MONTH(dr), RTC_DR_DAY(dr), @@ -534,78 +540,67 @@ show_version(bool mode) RTC_TR_MIN(dr), RTC_TR_SEC(dr), (RCC->BDCR & STM32_RTCSEL_MASK) == STM32_RTCSEL_LSE ? 'E' : 'I'); - ili9341_drawstring(buffer, x, y); #endif #if 1 uint32_t vbat=adc_vbat_read(); - plot_printf(buffer, sizeof(buffer), "Batt: %d.%03dV", vbat/1000, vbat%1000); - ili9341_drawstring(buffer, x, y + FONT_STR_HEIGHT + 2); + lcd_printf(x, y + FONT_STR_HEIGHT + 2, "Batt: %d.%03dV", vbat/1000, vbat%1000); #endif } } +#ifdef __DFU_SOFTWARE_MODE__ void enter_dfu(void) { -#ifdef __DFU_SOFTWARE_MODE__ touch_stop_watchdog(); - int x = 5, y = 20; - ili9341_set_foreground(LCD_FG_COLOR); - ili9341_set_background(LCD_BG_COLOR); + lcd_set_foreground(LCD_FG_COLOR); + lcd_set_background(LCD_BG_COLOR); // leave a last message - ili9341_clear_screen(); - ili9341_drawstring_7x13("DFU: Device Firmware Update Mode\n" - "To exit DFU mode, please reset device yourself.", x, y); + lcd_clear_screen(); + lcd_drawstring(x, y, "DFU: Device Firmware Update Mode\n" + "To exit DFU mode, please reset device yourself."); // see __early_init in ./NANOVNA_STM32_F072/board.c *((unsigned long *)BOOT_FROM_SYTEM_MEMORY_MAGIC_ADDRESS) = BOOT_FROM_SYTEM_MEMORY_MAGIC; NVIC_SystemReset(); -#endif } +#endif -static void +static bool select_lever_mode(int mode) { - if (uistat.lever_mode != mode) { - uistat.lever_mode = mode; - redraw_request |= REDRAW_FREQUENCY | REDRAW_MARKER; - } + if (lever_mode == mode) return false; + lever_mode = mode; + request_to_redraw(REDRAW_FREQUENCY | REDRAW_MARKER); + return true; } 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}, + [CAL_ISOLN]= {CALSTAT_ISOLN, 4}, + }; if (b){ - static const uint8_t c_mask_list[5]={ - [CAL_LOAD] = CALSTAT_LOAD, - [CAL_OPEN] = CALSTAT_OPEN, - [CAL_SHORT]= CALSTAT_SHORT, - [CAL_THRU] = CALSTAT_THRU, - [CAL_ISOLN]= CALSTAT_ISOLN, - }; - if (cal_status & c_mask_list[data]) b->icon = BUTTON_ICON_CHECK; + if (cal_status & c_list[data].mask) b->icon = BUTTON_ICON_CHECK; return; } // TODO: Hack! reset button state last_button = 0; cal_collect(data); - static const uint8_t c_next_sel[5]={ - [CAL_LOAD] = 3, - [CAL_OPEN] = 1, - [CAL_SHORT]= 2, - [CAL_THRU] = 5, - [CAL_ISOLN]= 4, - }; - selection = c_next_sel[data]; - draw_menu(); + selection = c_list[data].next; } +extern const menuitem_t menu_save[]; static UI_FUNCTION_CALLBACK(menu_caldone_cb) { - extern const menuitem_t menu_save[]; - (void)data; cal_done(); menu_move_back(false); - menu_push_submenu(menu_save); + if (data == 0) + menu_push_submenu(menu_save); } static UI_FUNCTION_CALLBACK(menu_cal_reset_cb) @@ -613,9 +608,27 @@ static UI_FUNCTION_CALLBACK(menu_cal_reset_cb) (void)data; // RESET cal_status = 0; + lastsaveid = NO_SAVE_SLOT; set_power(SI5351_CLK_DRIVE_STRENGTH_AUTO); - draw_menu(); - draw_cal_status(); +} + +static UI_FUNCTION_ADV_CALLBACK(menu_cal_range_acb){ + (void)data; + bool calibrated = cal_status & (CALSTAT_ES|CALSTAT_ER|CALSTAT_ET|CALSTAT_ED|CALSTAT_EX|CALSTAT_OPEN|CALSTAT_SHORT|CALSTAT_THRU); + if (b){ + if (calibrated){ + b->bg = (cal_status&CALSTAT_INTERPOLATED) ? LCD_INTERP_CAL_COLOR : LCD_MENU_COLOR; + plot_printf(b->label, sizeof(b->label), "CAL: %dp\n %.6FHz\n %.6FHz", cal_sweep_points, (float)cal_frequency0, (float)cal_frequency1); + } + else + plot_printf(b->label, sizeof(b->label), "RESET\nCAL RANGE"); + return; + } + // Reset range to calibration + if (calibrated && (cal_status&CALSTAT_INTERPOLATED)){ + reset_sweep_frequency(); + set_power(cal_power); + } } static UI_FUNCTION_ADV_CALLBACK(menu_cal_apply_acb) @@ -627,25 +640,28 @@ static UI_FUNCTION_ADV_CALLBACK(menu_cal_apply_acb) } // toggle applying correction cal_status ^= CALSTAT_APPLY; - draw_menu(); - draw_cal_status(); + request_to_redraw(REDRAW_CAL_STATUS); } static UI_FUNCTION_ADV_CALLBACK(menu_recall_acb) { if (b){ - b->p1.i = data; + const properties_t *p = get_properties(data); + if (p) + plot_printf(b->label, sizeof(b->label), "%.6FHz\n%.6FHz", (float)p->_frequency0, (float)p->_frequency1, data); + else + plot_printf(b->label, sizeof(b->label), "Empty %d", data); if (lastsaveid == data) b->icon = BUTTON_ICON_CHECK; return; } load_properties(data); - draw_menu(); - draw_cal_status(); } #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) { switch (data) { @@ -656,12 +672,21 @@ static UI_FUNCTION_CALLBACK(menu_config_cb) touch_draw_test(); break; case MENU_CONFIG_VERSION: - show_version(true); + show_version(); + break; + case MENU_CONFIG_RESET: + clear_all_config_prop_data(); + NVIC_SystemReset(); + break; +#ifdef __SD_CARD_LOAD__ + case MENU_CONFIG_LOAD: + if (!sd_card_load_config()) + drawMessageBox("Error", "No config.ini", 2000); break; +#endif } - redraw_frame(); - request_to_redraw_grid(); - draw_menu(); + ui_mode_normal(); + request_to_redraw(REDRAW_CLRSCR | REDRAW_AREA | REDRAW_BATTERY | REDRAW_CAL_STATUS | REDRAW_FREQUENCY); } static UI_FUNCTION_CALLBACK(menu_config_save_cb) @@ -687,24 +712,10 @@ static UI_FUNCTION_ADV_CALLBACK(menu_save_acb) } if (caldata_save(data) == 0) { menu_move_back(true); - draw_cal_status(); + request_to_redraw(REDRAW_CAL_STATUS); } } -static void -choose_active_trace(void) -{ - int i; - if (trace[current_trace].enabled) - // do nothing - return; - for (i = 0; i < TRACES_MAX; i++) - if (trace[i].enabled) { - current_trace = i; - return; - } -} - static UI_FUNCTION_ADV_CALLBACK(menu_trace_acb) { if (b){ @@ -720,9 +731,10 @@ static UI_FUNCTION_ADV_CALLBACK(menu_trace_acb) if (trace[data].enabled) { if (data == current_trace) { - // disable if active trace is selected - trace[data].enabled = FALSE; - choose_active_trace(); + trace[data].enabled = FALSE; // disable if active trace is selected + current_trace = TRACE_INVALID; // invalidate current + for (int i = 0; i < TRACES_MAX; i++) // set first enabled as current trace + if (trace[i].enabled) {current_trace = i; break;} } else { // make active selected trace current_trace = data; @@ -731,8 +743,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_trace_acb) trace[data].enabled = TRUE; current_trace = data; } - request_to_redraw_grid(); - draw_menu(); + request_to_redraw(REDRAW_AREA); } static UI_FUNCTION_ADV_CALLBACK(menu_format_acb) @@ -747,6 +758,7 @@ static UI_FUNCTION_ADV_CALLBACK(menu_format_acb) ui_mode_normal(); } +#if 0 static UI_FUNCTION_ADV_CALLBACK(menu_channel_acb) { if (current_trace == TRACE_INVALID) return; @@ -758,48 +770,108 @@ static UI_FUNCTION_ADV_CALLBACK(menu_channel_acb) set_trace_channel(current_trace, data); menu_move_back(true); } +#endif + +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; + if (b){ + b->p1.text = ch == 0 ? "S11 (REFL)" : "S21 (THRU)"; + return; + } + set_trace_channel(current_trace, ch^1); +} 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; + case TD_WINDOW_NORMAL: text = "NORMAL"; data = TD_WINDOW_MAXIMUM; break; + case TD_WINDOW_MAXIMUM: text = "MAXIMUM"; data = TD_WINDOW_MINIMUM; break; + } if(b){ - b->icon = (domain_mode & TD_WINDOW) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->p1.text = text; return; } - domain_mode = (domain_mode & ~TD_WINDOW) | data; - ui_mode_normal(); + props_mode = (props_mode & ~TD_WINDOW) | data; } static UI_FUNCTION_ADV_CALLBACK(menu_transform_acb) { (void)data; if(b){ - if (domain_mode & DOMAIN_TIME) b->icon = BUTTON_ICON_CHECK; + if (props_mode & DOMAIN_TIME) b->icon = BUTTON_ICON_CHECK; return; } - domain_mode ^= DOMAIN_TIME; + props_mode ^= DOMAIN_TIME; select_lever_mode(LM_MARKER); - ui_mode_normal(); } static UI_FUNCTION_ADV_CALLBACK(menu_transform_filter_acb) { if(b){ - b->icon = (domain_mode & TD_FUNC) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->icon = (props_mode & TD_FUNC) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; } - domain_mode = (domain_mode & ~TD_FUNC) | data; - ui_mode_normal(); + props_mode = (props_mode & ~TD_FUNC) | data; +// ui_mode_normal(); +} + +const menuitem_t menu_bandwidth[]; +static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_sel_acb) +{ + (void)data; + if (b){ + b->p1.u = get_bandwidth_frequency(config._bandwidth); + return; + } + menu_push_submenu(menu_bandwidth); } static UI_FUNCTION_ADV_CALLBACK(menu_bandwidth_acb) { if (b){ - b->icon = config.bandwidth == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->icon = config._bandwidth == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; b->p1.u = get_bandwidth_frequency(data); return; } set_bandwidth(data); - draw_menu(); +} + +#ifdef __USE_SMOOTH__ +static UI_FUNCTION_ADV_CALLBACK(menu_smooth_func_acb) +{ + (void)data; + if (b){ + b->p1.text = (config._vna_mode&VNA_SMOOTH_FUNCTION) ? "Arith" : "Geom"; + return; + } + config._vna_mode^=VNA_SMOOTH_FUNCTION; +} + +static UI_FUNCTION_ADV_CALLBACK(menu_smooth_acb) +{ + if (b){ + b->icon = get_smooth_factor() == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->p1.u = data; + return; + } + set_smooth_factor(data); +} +#endif + +const menuitem_t menu_sweep_points[]; +static UI_FUNCTION_ADV_CALLBACK(menu_points_sel_acb) +{ + (void)data; + if (b){ + b->p1.u = sweep_points; + return; + } + menu_push_submenu(menu_sweep_points); } static const uint16_t point_counts_set[POINTS_SET_COUNT] = POINTS_SET; @@ -812,7 +884,20 @@ static UI_FUNCTION_ADV_CALLBACK(menu_points_acb) return; } set_sweep_points(p_count); - draw_menu(); +} + +const menuitem_t menu_power[]; +static UI_FUNCTION_ADV_CALLBACK(menu_power_sel_acb) +{ + (void)data; + if (b){ + if (current_props._power == SI5351_CLK_DRIVE_STRENGTH_AUTO) + plot_printf(b->label, sizeof(b->label), "POWER AUTO"); + else + plot_printf(b->label, sizeof(b->label), "POWER %umA", 2+current_props._power*2); + return; + } + menu_push_submenu(menu_power); } static UI_FUNCTION_ADV_CALLBACK(menu_power_acb) @@ -823,138 +908,139 @@ static UI_FUNCTION_ADV_CALLBACK(menu_power_acb) return; } set_power(data); - draw_menu(); } -static UI_FUNCTION_CALLBACK(menu_keyboard_cb) +static UI_FUNCTION_ADV_CALLBACK(menu_keyboard_acb) { - if (data == KM_SCALE && trace[current_trace].type == TRC_DELAY) { + if (data == KM_SCALE && current_trace != TRACE_INVALID && trace[current_trace].type == TRC_DELAY) data = KM_SCALEDELAY; + if (b){ + switch(data){ +// case KM_SCALE: b->p1.f = current_trace != TRACE_INVALID ? get_trace_scale(current_trace) : 0; break; + case KM_VELOCITY_FACTOR: b->p1.u = velocity_factor; break; + case KM_VAR: plot_printf(b->label, sizeof(b->label), var_freq ? "JOG STEP\n %.3qHz" : "JOG STEP\n AUTO", var_freq); break; + case KM_XTAL: b->p1.u = config._xtal_freq; break; + case KM_THRESHOLD: b->p1.u = config._harmonic_freq_threshold; break; + case KM_VBAT: b->p1.u = config._vbat_offset; break; + case KM_EDELAY: b->p1.f = electrical_delay * 1E-12; break; +#ifdef __S21_MEASURE__ + case KM_MEASURE_R: b->p1.f = config._measure_r; break; +#endif +#ifdef __VNA_Z_RENORMALIZATION__ + case KM_Z_PORT: b->p1.f = current_props._portz; break; +#endif + } + return; } #ifdef UI_USE_NUMERIC_INPUT if (btn_wait_release() & EVT_BUTTON_DOWN_LONG) { ui_mode_numeric(data); -// ui_process_numeric(); + return; } - else #endif - { - ui_mode_keypad(data); - ui_process_keypad(); + ui_mode_keypad(data); +} + +#ifdef __USE_GRID_VALUES__ +static UI_FUNCTION_ADV_CALLBACK(menu_grid_acb) +{ + if (b){ + b->icon = VNA_mode & data ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; + return; } + VNA_mode^=data; + request_to_redraw(REDRAW_AREA); } +#endif static UI_FUNCTION_ADV_CALLBACK(menu_pause_acb) { (void)data; if (b){ + b->p1.text = sweep_mode&SWEEP_ENABLE ? "PAUSE" : "RESUME"; b->icon = sweep_mode&SWEEP_ENABLE ? BUTTON_ICON_NOCHECK : BUTTON_ICON_CHECK; return; } toggle_sweep(); - //menu_move_back(true); - draw_menu(); -} - -static uint32_t -get_marker_frequency(int marker) -{ - if (marker < 0 || marker >= MARKERS_MAX) - return 0; - if (!markers[marker].enabled) - return 0; - return frequencies[markers[marker].index]; } #define UI_MARKER_EDELAY 4 static UI_FUNCTION_CALLBACK(menu_marker_op_cb) { - uint32_t freq = get_marker_frequency(active_marker); + freq_t freq = get_marker_frequency(active_marker); if (freq == 0) return; // no active marker switch (data) { - case ST_START: /* MARKER->START */ - case ST_STOP: /* MARKER->STOP */ - case ST_CENTER: /* MARKER->CENTER */ + case ST_START: + case ST_STOP: + case ST_CENTER: set_sweep_frequency(data, freq); break; - case ST_SPAN: /* MARKERS->SPAN */ + case ST_SPAN: { if (previous_marker == MARKER_INVALID || active_marker == previous_marker) { // if only 1 marker is active, keep center freq and make span the marker comes to the edge - uint32_t center = get_sweep_frequency(ST_CENTER); - uint32_t span = center > freq ? center - freq : freq - center; + freq_t center = get_sweep_frequency(ST_CENTER); + freq_t span = center > freq ? center - freq : freq - center; set_sweep_frequency(ST_SPAN, span * 2); } else { // if 2 or more marker active, set start and stop freq to each marker - uint32_t freq2 = get_marker_frequency(previous_marker); + freq_t freq2 = get_marker_frequency(previous_marker); if (freq2 == 0) return; - if (freq > freq2) {uint32_t t = freq2; freq2 = freq; freq = t;} + if (freq > freq2) SWAP(freq_t, freq2, freq); set_sweep_frequency(ST_START, freq); set_sweep_frequency(ST_STOP, freq2); } } break; - case UI_MARKER_EDELAY: /* MARKERS->EDELAY */ + case UI_MARKER_EDELAY: { if (current_trace == TRACE_INVALID) break; float (*array)[2] = measured[trace[current_trace].channel]; - float v = groupdelay_from_array(markers[active_marker].index, array); + int index = markers[active_marker].index; + float v = groupdelay_from_array(index, array[index]); set_electrical_delay(electrical_delay + (v / 1e-12)); } break; } ui_mode_normal(); - draw_cal_status(); - //redraw_all(); } #define MENU_MARKER_S_MAX 0 -#define MENU_MARKER_S_MIN 1 -#define MENU_MARKER_S_LEFT 2 -#define MENU_MARKER_S_RIGHT 3 -static UI_FUNCTION_CALLBACK(menu_marker_search_cb) +#define MENU_MARKER_S_MIN VNA_MODE_SEARCH_MIN +static UI_FUNCTION_ADV_CALLBACK(menu_marker_search_mode_acb) { - int i = -1; - if (active_marker == MARKER_INVALID) + if (b){ + b->icon = ((VNA_mode & VNA_MODE_SEARCH_MASK) == data) ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; - - switch (data) { - case MENU_MARKER_S_MAX: /* maximum */ - case MENU_MARKER_S_MIN: /* minimum */ - set_marker_search(data); - i = marker_search(); - break; - case MENU_MARKER_S_LEFT: /* search Left */ - i = marker_search_dir(markers[active_marker].index, MK_SEARCH_LEFT); - break; - case MENU_MARKER_S_RIGHT: /* search right */ - i = marker_search_dir(markers[active_marker].index, MK_SEARCH_RIGHT); - break; - } - if (i >= 0){ - set_marker_index(active_marker, i); - redraw_marker(active_marker); } - uistat.marker_tracking = false; + VNA_mode = (VNA_mode & ~VNA_MODE_SEARCH_MASK) | data; + marker_search(true); +#ifdef UI_USE_LEVELER_SEARCH_MODE + select_lever_mode(LM_SEARCH); +#endif +} + +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 select_lever_mode(LM_SEARCH); #endif - draw_menu(); } static UI_FUNCTION_ADV_CALLBACK(menu_marker_tracking_acb) { (void)data; if (b){ - b->icon = uistat.marker_tracking ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; + b->icon = (props_mode & TD_MARKER_TRACK) ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; return; } - uistat.marker_tracking = !uistat.marker_tracking; - draw_menu(); + props_mode^= TD_MARKER_TRACK; } static UI_FUNCTION_ADV_CALLBACK(menu_marker_smith_acb) @@ -964,20 +1050,18 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_smith_acb) return; } marker_smith_format = data; - redraw_marker(active_marker); - draw_menu(); + request_to_redraw(REDRAW_MARKER); } -#ifdef __USE_LC_MATCHING__ -static UI_FUNCTION_ADV_CALLBACK(menu_marker_lc_match_acb) +#ifdef __VNA_MEASURE_MODULE__ +static UI_FUNCTION_ADV_CALLBACK(menu_measure_acb) { (void)data; if (b){ - b->icon = domain_mode & TD_LC_MATH ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; + b->icon = current_props._measure == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; } - domain_mode^=TD_LC_MATH; - ui_mode_normal(); + plot_set_measure_mode(data); } #endif @@ -997,15 +1081,14 @@ active_marker_check(void) } } -#define UI_MARKER_OFF (MARKERS_MAX ) -#define UI_MARKER_DELTA (MARKERS_MAX+1) static UI_FUNCTION_ADV_CALLBACK(menu_marker_sel_acb) { - int i; if (b){ - if (data < MARKERS_MAX && markers[data].enabled) b->icon = BUTTON_ICON_CHECK; - else if (data == UI_MARKER_DELTA) b->icon = uistat.marker_delta ? BUTTON_ICON_CHECK : BUTTON_ICON_NOCHECK; - b->p1.u = data + 1; + if (data < MARKERS_MAX){ + if (data == active_marker) b->icon = BUTTON_ICON_CHECK_AUTO; + else if (markers[data].enabled) b->icon = BUTTON_ICON_CHECK; + b->p1.u = data + 1; + } return; } // Marker select click @@ -1019,26 +1102,41 @@ static UI_FUNCTION_ADV_CALLBACK(menu_marker_sel_acb) } } else { markers[mk].enabled = TRUE; // Enable marker + } previous_marker = active_marker; // set previous marker as current active active_marker = mk; // set new active marker active_marker_check(); - } else if (data == UI_MARKER_OFF) { // all off - for (i = 0; i < MARKERS_MAX; i++) - markers[i].enabled = FALSE; - previous_marker = MARKER_INVALID; - active_marker = MARKER_INVALID; - } else if (data == UI_MARKER_DELTA) { // marker delta - uistat.marker_delta = !uistat.marker_delta; } - redraw_marker(active_marker); - draw_menu(); + request_to_redraw(REDRAW_MARKER); +} + +static UI_FUNCTION_CALLBACK(menu_marker_disable_all_cb) +{ + (void)data; + int i; + for (i = 0; i < MARKERS_MAX; i++) + markers[i].enabled = FALSE; // all off + previous_marker = MARKER_INVALID; + active_marker = MARKER_INVALID; + request_to_redraw(REDRAW_MARKER); +} + +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; + return; + } + props_mode^= TD_MARKER_DELTA; + request_to_redraw(REDRAW_MARKER); } #ifdef __USE_SERIAL_CONSOLE__ -static const uint32_t usart_speed[] = {19200, 38400, 57600, 115200, 230400, 460800, 921600}; 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){ b->icon = config._serial_speed == speed ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; @@ -1047,58 +1145,101 @@ static UI_FUNCTION_ADV_CALLBACK(menu_serial_speed_acb) } config._serial_speed = speed; shell_update_speed(); - draw_menu(); } static UI_FUNCTION_ADV_CALLBACK(menu_connection_acb) { if (b){ - b->icon = (config._mode&VNA_MODE_CONNECTION_MASK) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; + b->icon = (VNA_mode & VNA_MODE_CONNECTION_MASK) == data ? BUTTON_ICON_GROUP_CHECKED : BUTTON_ICON_GROUP; return; } - config._mode&=~VNA_MODE_CONNECTION_MASK; - config._mode|=data; + VNA_mode = (VNA_mode & ~VNA_MODE_CONNECTION_MASK) | data; shell_reset_console(); - draw_menu(); +} +#endif + +#ifdef USE_VARIABLE_OFFSET_MENU +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; + b->p1.u = offset; + return; + } + si5351_set_frequency_offset(offset); +} + +const menuitem_t menu_offset[]; +static UI_FUNCTION_ADV_CALLBACK(menu_offset_sel_acb) +{ + (void)data; + if (b){ + b->p1.i = IF_OFFSET; + return; + } + menu_push_submenu(menu_offset); } #endif #ifdef __LCD_BRIGHTNESS__ -static UI_FUNCTION_CALLBACK(menu_brightness_cb) +static UI_FUNCTION_ADV_CALLBACK(menu_brightness_acb) { (void)data; + if (b){ + b->p1.u = config._brightness; + return; + } int16_t value = config._brightness; - ili9341_set_foreground(LCD_MENU_TEXT_COLOR); - ili9341_set_background(LCD_MENU_COLOR); - ili9341_fill(LCD_WIDTH/2-80, LCD_HEIGHT/2-20, 160, 40); - ili9341_drawstring("BRIGHTNESS", LCD_WIDTH/2-35, LCD_HEIGHT/2-13); - ili9341_drawstring(S_LARROW" USE LEVELER BUTTON "S_RARROW, LCD_WIDTH/2-72, LCD_HEIGHT/2+2); + lcd_set_foreground(LCD_MENU_TEXT_COLOR); + lcd_set_background(LCD_MENU_COLOR); + lcd_fill(LCD_WIDTH/2-12*FONT_WIDTH, LCD_HEIGHT/2-20, 23*FONT_WIDTH, 40); + lcd_printf(LCD_WIDTH/2-8*FONT_WIDTH, LCD_HEIGHT/2-13, "BRIGHTNESS %3d%% ", value); + lcd_printf(LCD_WIDTH/2-11*FONT_WIDTH, LCD_HEIGHT/2+2, S_LARROW " USE LEVELER BUTTON " S_RARROW); while (TRUE) { - int status = btn_check(); + uint16_t status = btn_check(); if (status & (EVT_UP|EVT_DOWN)) { do { if (status & EVT_UP ) value+=5; if (status & EVT_DOWN) value-=5; if (value < 0) value = 0; if (value > 100) value = 100; + lcd_printf(LCD_WIDTH/2-8*FONT_WIDTH, LCD_HEIGHT/2-13, "BRIGHTNESS %3d%% ", value); lcd_setBrightness(value); - status = btn_wait_release(); - } while (status != 0); + } while ((status = btn_wait_release()) != 0); } if (status == EVT_BUTTON_SINGLE_CLICK) break; } config._brightness = (uint8_t)value; lcd_setBrightness(value); - request_to_redraw_grid(); + request_to_redraw(REDRAW_AREA); ui_mode_normal(); } #endif #ifdef __USE_SD_CARD__ -#define SAVE_S1P_FILE 1 -#define SAVE_S2P_FILE 2 +// Save format enum +enum { + SAVE_S1P_FILE=0, SAVE_S2P_FILE, SAVE_BMP_FILE, +#ifdef __SD_CARD_DUMP_FIRMWARE__ + SAVE_BIN_FILE +#endif +}; +// Save file extension +static const char *file_ext[] = { + [SAVE_S1P_FILE] = "s1p", + [SAVE_S2P_FILE] = "s2p", + [SAVE_BMP_FILE] = "bmp", +#ifdef __SD_CARD_DUMP_FIRMWARE__ + [SAVE_BIN_FILE] = "bin", +#endif +}; + +//******************************************************************************************* +// S1P and S2P file headers, and data structures +//******************************************************************************************* static const char s1_file_header[] = "!File created by NanoVNA\r\n"\ "# Hz S RI R 50\r\n"; @@ -1113,83 +1254,199 @@ static const char s2_file_header[] = static const char s2_file_param[] = "%10u % f % f % f % f 0 0 0 0\r\n"; -static UI_FUNCTION_CALLBACK(menu_sdcard_cb) +//******************************************************************************************* +// Bitmap file header for LCD_WIDTH x LCD_HEIGHT image 16bpp (v4 format allow set RGB mask) +//******************************************************************************************* +#define BMP_UINT32(val) ((val)>>0)&0xFF, ((val)>>8)&0xFF, ((val)>>16)&0xFF, ((val)>>24)&0xFF +#define BMP_H1_SIZE (14) // BMP header 14 bytes +#define BMP_V4_SIZE (56) // v4 header 56 bytes +#define BMP_HEAD_SIZE (BMP_H1_SIZE + BMP_V4_SIZE) // Size of all headers +#define BMP_SIZE (2*LCD_WIDTH*LCD_HEIGHT) // Bitmap size = 2*w*h +#define BMP_FILE_SIZE (BMP_SIZE + BMP_HEAD_SIZE) // File size = headers + bitmap +static const uint8_t bmp_header_v4[14+56] = { +// BITMAPFILEHEADER (14 byte size) + 0x42, 0x4D, // BM signature + BMP_UINT32(BMP_FILE_SIZE), // File size (h + v4 + bitmap) + 0x00, 0x00, // reserved + 0x00, 0x00, // reserved + BMP_UINT32(BMP_HEAD_SIZE), // Size of all headers (h + v4) +// BITMAPINFOv4 (56 byte size) + BMP_UINT32(BMP_V4_SIZE), // Data offset after this point (v4 size) + BMP_UINT32(LCD_WIDTH), // Width + BMP_UINT32(LCD_HEIGHT), // Height + 0x01, 0x00, // Planes + 0x10, 0x00, // 16bpp + 0x03, 0x00, 0x00, 0x00, // Compression (BI_BITFIELDS) + BMP_UINT32(BMP_SIZE), // Bitmap size (w*h*2) + 0xC4, 0x0E, 0x00, 0x00, // x Resolution (96 DPI = 96 * 39.3701 inches per meter = 0x0EC4) + 0xC4, 0x0E, 0x00, 0x00, // y Resolution (96 DPI = 96 * 39.3701 inches per meter = 0x0EC4) + 0x00, 0x00, 0x00, 0x00, // Palette size + 0x00, 0x00, 0x00, 0x00, // Palette used +// Extend v4 header data (color mask for RGB565) + BMP_UINT32(0b1111100000000000),// R mask = 0b11111000 00000000 + BMP_UINT32(0b0000011111100000),// G mask = 0b00000111 11100000 + BMP_UINT32(0b0000000000011111),// B mask = 0b00000000 00011111 + BMP_UINT32(0b0000000000000000) // A mask = 0b00000000 00000000 +}; + +// Create file name from current time +static FRESULT vna_create_file(char *fs_filename, const char *ext) { - char *buf = (char *)spi_buffer; // shell_printf("S file\r\n"); FRESULT res = f_mount(fs_volume, "", 1); // shell_printf("Mount = %d\r\n", res); if (res != FR_OK) - return; - // Prepare filename = .s1p or .s2p and open for write + return res; + // Prepare filename and open for write #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%dp", dr, tr, data); + plot_printf(fs_filename, FF_LFN_BUF, "VNA_%06x_%06x.%s", dr, tr, ext); #else - plot_printf(fs_filename, FF_LFN_BUF, "%08X.s%dp", rtc_get_FAT(), data); + plot_printf(fs_filename, FF_LFN_BUF, "%08x.%s", rtc_get_FAT(), ext); #endif + res = f_open(fs_file, fs_filename, FA_CREATE_ALWAYS | FA_READ | FA_WRITE); + // shell_printf("Open %s, = %d\r\n", fs_filename, res); + return res; +} - int i; +static UI_FUNCTION_CALLBACK(menu_sdcard_cb) +{ + char *buf_8; + uint16_t *buf_16; + char filename[32]; + int i, y; UINT size; + // 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 (data == SAVE_BMP_FILE && ui_mode != UI_NORMAL){ + ui_mode_normal(); + draw_all(true); + } // UINT total_size = 0; // systime_t time = chVTGetSystemTimeX(); - res = f_open(fs_file, fs_filename, FA_CREATE_ALWAYS | FA_READ | FA_WRITE); -// shell_printf("Open %s, = %d\r\n", fs_filename, res); + // Prepare filename = .s1p / .s2p / .bmp and open for write + FRESULT res = vna_create_file(filename, file_ext[data]); if (res == FR_OK){ - // Write S1P file - if (data == SAVE_S1P_FILE){ - // write s1p header (not write NULL terminate at end) - res = f_write(fs_file, s1_file_header, sizeof(s1_file_header)-1, &size); -// total_size+=size; + const char *s_file_format; + switch(data) { + /* + * Save touchstone file for VNA (use rev 1.1 format) + * https://en.wikipedia.org/wiki/Touchstone_file + */ + case SAVE_S1P_FILE: + case SAVE_S2P_FILE: + buf_8 = (char *)spi_buffer; + // Write SxP file + if (data == SAVE_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, 128, s1_file_param, frequencies[i], measured[0][i][0], measured[0][i][1]); + 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, size, &size); + res = f_write(fs_file, buf_8, size, &size); } - } - // Write S2P file - else if (data == SAVE_S2P_FILE){ - // Write s2p header (not write NULL terminate at end) - res = f_write(fs_file, s2_file_header, sizeof(s2_file_header)-1, &size); + break; + /* + * Save bitmap file (use v4 format allow set RGB mask) + */ + case SAVE_BMP_FILE: + buf_16 = spi_buffer; + res = f_write(fs_file, bmp_header_v4, sizeof(bmp_header_v4), &size); // total_size+=size; - // Write all points data - for (i = 0; i < sweep_points && res == FR_OK; i++) { - size = plot_printf(buf, 128, s2_file_param, frequencies[i], measured[0][i][0], measured[0][i][1], measured[1][i][0], measured[1][i][1]); + for (y = LCD_HEIGHT-1; y >= 0 && res == FR_OK; y--) { + lcd_read_memory(0, y, LCD_WIDTH, 1, buf_16); + for (i = 0; i < LCD_WIDTH; i++) + buf_16[i] = __REVSH(buf_16[i]); // swap byte order (example 0x10FF to 0xFF10) + res = f_write(fs_file, buf_16, LCD_WIDTH*sizeof(uint16_t), &size); // total_size+=size; - res = f_write(fs_file, buf, size, &size); } + break; +#ifdef __SD_CARD_DUMP_FIRMWARE__ + case SAVE_BIN_FILE: + { + const char *src = (const char*)FLASH_START_ADDRESS; + for (i = 0; i < FLASH_TOTAL_SIZE && res == FR_OK; i+=512) + res = f_write(fs_file, &src[i], 512, &size); + } + break; +#endif } - res = f_close(fs_file); + 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("SAVE TRACE", res == FR_OK ? fs_filename : " Fail write ", 2000); - request_to_redraw_grid(); + drawMessageBox("SD CARD SAVE", res == FR_OK ? filename : " Fail write ", 2000); + request_to_redraw(REDRAW_AREA); ui_mode_normal(); } +#endif -static const menuitem_t menu_sdcard[] = { - { MT_CALLBACK, SAVE_S1P_FILE, "SAVE S1P", menu_sdcard_cb }, - { MT_CALLBACK, SAVE_S2P_FILE, "SAVE S2P", menu_sdcard_cb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel -}; +#ifdef __DIGIT_SEPARATOR__ +static UI_FUNCTION_ADV_CALLBACK(menu_separator_acb) +{ + (void)data; + if (b){ + b->p1.text = DIGIT_SEPARATOR == '.' ? " DOT '.'" : " COMMA ','"; + return; + } + DIGIT_SEPARATOR = DIGIT_SEPARATOR == '.' ? ',' : '.'; +} #endif -static const menuitem_t menu_calop[] = { +#if STORED_TRACES > 0 +static UI_FUNCTION_CALLBACK(menu_stored_trace_cb) +{ + if (data & 1) + disableStoredTrace(data>>1); + else + storeCurrentTrace(data>>1); +} +#endif + +static UI_FUNCTION_CALLBACK(menu_back_cb) +{ + (void)data; + menu_move_back(false); +} + +// Back button submenu list +static const menuitem_t menu_back[] = { + { MT_CALLBACK, 0, S_LARROW" BACK", menu_back_cb }, + { MT_NONE, 0, NULL, NULL } // sentinel +}; + +#ifdef __USE_SD_CARD__ +static const menuitem_t menu_sdcard[] = { + { MT_CALLBACK, SAVE_S1P_FILE, "SAVE S1P", menu_sdcard_cb }, + { MT_CALLBACK, SAVE_S2P_FILE, "SAVE S2P", menu_sdcard_cb }, + { MT_CALLBACK, SAVE_BMP_FILE, "SCREENSHOT", menu_sdcard_cb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; +#endif + +static const menuitem_t menu_calop[] = { { MT_ADV_CALLBACK, CAL_OPEN, "OPEN", menu_calop_acb }, { MT_ADV_CALLBACK, CAL_SHORT, "SHORT", menu_calop_acb }, { MT_ADV_CALLBACK, CAL_LOAD, "LOAD", menu_calop_acb }, { MT_ADV_CALLBACK, CAL_ISOLN, "ISOLN", menu_calop_acb }, { MT_ADV_CALLBACK, CAL_THRU, "THRU", menu_calop_acb }, - { MT_CALLBACK, 0, "DONE", menu_caldone_cb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_CALLBACK, 0, "DONE", menu_caldone_cb }, + { MT_CALLBACK, 1, "DONE IN RAM", menu_caldone_cb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_save[] = { @@ -1208,28 +1465,26 @@ const menuitem_t menu_save[] = { #if SAVEAREA_MAX > 6 { MT_ADV_CALLBACK, 6, "SAVE %d", menu_save_acb }, #endif - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_recall[] = { - { MT_ADV_CALLBACK, 0, "RECALL %d", menu_recall_acb }, - { MT_ADV_CALLBACK, 1, "RECALL %d", menu_recall_acb }, - { MT_ADV_CALLBACK, 2, "RECALL %d", menu_recall_acb }, + { MT_ADV_CALLBACK, 0, MT_CUSTOM_LABEL, menu_recall_acb }, + { MT_ADV_CALLBACK, 1, MT_CUSTOM_LABEL, menu_recall_acb }, + { MT_ADV_CALLBACK, 2, MT_CUSTOM_LABEL, menu_recall_acb }, #if SAVEAREA_MAX > 3 - { MT_ADV_CALLBACK, 3, "RECALL %d", menu_recall_acb }, + { MT_ADV_CALLBACK, 3, MT_CUSTOM_LABEL, menu_recall_acb }, #endif #if SAVEAREA_MAX > 4 - { MT_ADV_CALLBACK, 4, "RECALL %d", menu_recall_acb }, + { MT_ADV_CALLBACK, 4, MT_CUSTOM_LABEL, menu_recall_acb }, #endif #if SAVEAREA_MAX > 5 - { MT_ADV_CALLBACK, 5, "RECALL %d", menu_recall_acb }, + { MT_ADV_CALLBACK, 5, MT_CUSTOM_LABEL, menu_recall_acb }, #endif #if SAVEAREA_MAX > 6 - { MT_ADV_CALLBACK, 6, "RECALL %d", menu_recall_acb }, + { MT_ADV_CALLBACK, 6, MT_CUSTOM_LABEL, menu_recall_acb }, #endif - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_power[] = { @@ -1238,18 +1493,17 @@ const menuitem_t menu_power[] = { { MT_ADV_CALLBACK, SI5351_CLK_DRIVE_STRENGTH_4MA, "%u mA", menu_power_acb }, { MT_ADV_CALLBACK, SI5351_CLK_DRIVE_STRENGTH_6MA, "%u mA", menu_power_acb }, { MT_ADV_CALLBACK, SI5351_CLK_DRIVE_STRENGTH_8MA, "%u mA", menu_power_acb }, - { MT_CANCEL, 255, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_cal[] = { - { MT_SUBMENU, 0, "CALIBRATE", menu_calop }, - { MT_SUBMENU, 0, "POWER", menu_power }, - { MT_SUBMENU, 0, "SAVE", menu_save }, - { MT_CALLBACK, 0, "RESET", menu_cal_reset_cb }, - { MT_ADV_CALLBACK, 0, "APPLY", menu_cal_apply_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_SUBMENU, 0, "CALIBRATE", menu_calop }, + { MT_ADV_CALLBACK, 0, MT_CUSTOM_LABEL, menu_power_sel_acb }, + { MT_SUBMENU, 0, "SAVE", menu_save }, + { MT_ADV_CALLBACK, 0, MT_CUSTOM_LABEL, menu_cal_range_acb }, + { MT_CALLBACK, 0, "RESET", menu_cal_reset_cb }, + { MT_ADV_CALLBACK, 0, "APPLY", menu_cal_apply_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_trace[] = { @@ -1257,65 +1511,68 @@ const menuitem_t menu_trace[] = { { MT_ADV_CALLBACK, 1, "TRACE %d", menu_trace_acb }, { MT_ADV_CALLBACK, 2, "TRACE %d", menu_trace_acb }, { MT_ADV_CALLBACK, 3, "TRACE %d", menu_trace_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel +#if STORED_TRACES > 0 + { MT_CALLBACK, 0, "STORE TRACE", menu_stored_trace_cb}, + { MT_CALLBACK, 1, "CLEAN STORE", menu_stored_trace_cb}, +#endif +#if STORED_TRACES > 1 + { MT_CALLBACK, 2, "STORE TRACE 1", menu_stored_trace_cb}, + { MT_CALLBACK, 3, "CLEAN STORE 1", menu_stored_trace_cb}, +#endif + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_format2[] = { - { MT_ADV_CALLBACK, TRC_POLAR, "POLAR", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_POLAR, "POLAR", menu_format_acb }, { MT_ADV_CALLBACK, TRC_LINEAR, "LINEAR", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_REAL, "REAL", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_IMAG, "IMAG", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_R, "RESISTANCE", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_X, "REACTANCE", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_Q, "Q FACTOR", menu_format_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_ADV_CALLBACK, TRC_REAL, "REAL", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_IMAG, "IMAG", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_Z, "|Z|", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_Q, "Q FACTOR", menu_format_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_format[] = { { MT_ADV_CALLBACK, TRC_LOGMAG, "LOGMAG", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_PHASE, "PHASE", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_DELAY, "DELAY", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_SMITH, "SMITH", menu_format_acb }, - { MT_ADV_CALLBACK, TRC_SWR, "SWR", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_PHASE, "PHASE", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_DELAY, "DELAY", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_SMITH, "SMITH", menu_format_acb }, +// { MT_ADV_CALLBACK, TRC_ADMIT, "ADMIT", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_SWR, "SWR", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_R, "RESISTANCE", menu_format_acb }, + { MT_ADV_CALLBACK, TRC_X, "REACTANCE", menu_format_acb }, { MT_SUBMENU, 0, S_RARROW" MORE", menu_format2 }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_scale[] = { - { MT_CALLBACK, KM_SCALE, "SCALE/DIV", menu_keyboard_cb }, - { MT_CALLBACK, KM_REFPOS, "REFERENCE\nPOSITION", menu_keyboard_cb }, - { MT_CALLBACK, KM_EDELAY, "ELECTRICAL\nDELAY", menu_keyboard_cb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_ADV_CALLBACK, KM_SCALE, "SCALE/DIV", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_REFPOS, "REFERENCE\nPOSITION", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_EDELAY, "E-DELAY\n %b.7Fs", menu_keyboard_acb }, +#ifdef __USE_GRID_VALUES__ + { MT_ADV_CALLBACK, VNA_MODE_SHOW_GRID, "SHOW GRID\nVALUES", menu_grid_acb }, + { MT_ADV_CALLBACK, VNA_MODE_DOT_GRID , "DOT GRID", menu_grid_acb }, +#endif + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; +#if 0 const menuitem_t menu_channel[] = { - { MT_ADV_CALLBACK, 0, "CH0\nREFLECT", menu_channel_acb }, - { MT_ADV_CALLBACK, 1, "CH1\nTHROUGH", menu_channel_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel -}; - -const menuitem_t menu_transform_window[] = { - { MT_ADV_CALLBACK, TD_WINDOW_MINIMUM, "MINIMUM", menu_transform_window_acb }, - { MT_ADV_CALLBACK, TD_WINDOW_NORMAL, "NORMAL", menu_transform_window_acb }, - { MT_ADV_CALLBACK, TD_WINDOW_MAXIMUM, "MAXIMUM", menu_transform_window_acb }, + { MT_ADV_CALLBACK, 0, "S11\nREFLECT", menu_channel_acb }, + { MT_ADV_CALLBACK, 1, "S21\nTHROUGH", menu_channel_acb }, { MT_CANCEL, 0, S_LARROW" BACK", NULL }, { MT_NONE, 0, NULL, NULL } // sentinel }; +#endif const menuitem_t menu_transform[] = { - { MT_ADV_CALLBACK, 0, "TRANSFORM\nON", menu_transform_acb }, - { MT_ADV_CALLBACK, TD_FUNC_LOWPASS_IMPULSE, "LOW PASS\nIMPULSE", menu_transform_filter_acb }, - { MT_ADV_CALLBACK, TD_FUNC_LOWPASS_STEP, "LOW PASS\nSTEP", menu_transform_filter_acb }, - { MT_ADV_CALLBACK, TD_FUNC_BANDPASS, "BANDPASS", menu_transform_filter_acb }, - { MT_SUBMENU, 0, "WINDOW", menu_transform_window }, - { MT_CALLBACK, KM_VELOCITY_FACTOR, "VELOCITY\nFACTOR", menu_keyboard_cb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_ADV_CALLBACK, 0, "TRANSFORM\n ON", menu_transform_acb }, + { MT_ADV_CALLBACK, TD_FUNC_LOWPASS_IMPULSE, "LOW PASS\nIMPULSE", menu_transform_filter_acb }, + { MT_ADV_CALLBACK, TD_FUNC_LOWPASS_STEP, "LOW PASS\nSTEP", menu_transform_filter_acb }, + { MT_ADV_CALLBACK, TD_FUNC_BANDPASS, "BANDPASS", menu_transform_filter_acb }, + { MT_ADV_CALLBACK, 0, "WINDOW\n %s", menu_transform_window_acb }, + { MT_ADV_CALLBACK, KM_VELOCITY_FACTOR, "VELOCITY\nFACTOR %d%%%%", menu_keyboard_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_bandwidth[] = { @@ -1343,19 +1600,36 @@ const menuitem_t menu_bandwidth[] = { #ifdef BANDWIDTH_10 { MT_ADV_CALLBACK, BANDWIDTH_10, "%u Hz", menu_bandwidth_acb }, #endif - { MT_CANCEL, 255, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; + +#ifdef __USE_SMOOTH__ +const menuitem_t menu_smooth_count[] = { + { MT_ADV_CALLBACK, 0, "SMOOTH\n%s avg",menu_smooth_func_acb }, + { MT_ADV_CALLBACK, 0, "SMOOTH\nOFF",menu_smooth_acb }, + { MT_ADV_CALLBACK, 1, "x%d", menu_smooth_acb }, + { MT_ADV_CALLBACK, 2, "x%d", menu_smooth_acb }, + { MT_ADV_CALLBACK, 4, "x%d", menu_smooth_acb }, + { MT_ADV_CALLBACK, 5, "x%d", menu_smooth_acb }, + { MT_ADV_CALLBACK, 6, "x%d", menu_smooth_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; +#endif const menuitem_t menu_display[] = { - { MT_SUBMENU, 0, "TRACE", menu_trace }, - { MT_SUBMENU, 0, "FORMAT", menu_format }, - { MT_SUBMENU, 0, "SCALE", menu_scale }, - { MT_SUBMENU, 0, "CHANNEL", menu_channel }, - { MT_SUBMENU, 0, "TRANSFORM", menu_transform }, - { MT_SUBMENU, 0, "BANDWIDTH", menu_bandwidth }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_SUBMENU, 0, "TRACE", menu_trace }, + { MT_SUBMENU, 0, "FORMAT", menu_format }, + { MT_SUBMENU, 0, "SCALE", menu_scale }, + { MT_ADV_CALLBACK, 0, "CHANNEL\n %s", menu_channel_acb }, + { MT_SUBMENU, 0, "TRANSFORM", menu_transform }, + { MT_ADV_CALLBACK, 0, "BANDWIDTH\n %uHz", menu_bandwidth_sel_acb }, +#ifdef __USE_SMOOTH__ + { MT_SUBMENU, 0, "DATA\nSMOOTH", menu_smooth_count }, +#endif +#ifdef __VNA_Z_RENORMALIZATION__ + { MT_ADV_CALLBACK, KM_Z_PORT, "PORT-Z\n50 " S_RARROW " %bF" S_OHM, menu_keyboard_acb}, +#endif + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_sweep_points[] = { @@ -1372,30 +1646,47 @@ const menuitem_t menu_sweep_points[] = { #if POINTS_SET_COUNT > 4 { MT_ADV_CALLBACK, 4, "%d point", menu_points_acb }, #endif - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_stimulus[] = { - { MT_CALLBACK, KM_START, "START", menu_keyboard_cb }, - { MT_CALLBACK, KM_STOP, "STOP", menu_keyboard_cb }, - { MT_CALLBACK, KM_CENTER, "CENTER", menu_keyboard_cb }, - { MT_CALLBACK, KM_SPAN, "SPAN", menu_keyboard_cb }, - { MT_CALLBACK, KM_CW, "CW FREQ", menu_keyboard_cb }, - { MT_ADV_CALLBACK, 0, "PAUSE\nSWEEP", menu_pause_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_ADV_CALLBACK, KM_START, "START", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_STOP, "STOP", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_CENTER, "CENTER", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_SPAN, "SPAN", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_CW, "CW FREQ", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_VAR, MT_CUSTOM_LABEL, menu_keyboard_acb }, + { MT_ADV_CALLBACK, 0, "SWEEP\nPOINTS %u", menu_points_sel_acb }, + { MT_ADV_CALLBACK, 0, "%s\nSWEEP", menu_pause_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_marker_sel[] = { { MT_ADV_CALLBACK, 0, "MARKER %d", menu_marker_sel_acb }, +#if MARKERS_MAX >=2 { MT_ADV_CALLBACK, 1, "MARKER %d", menu_marker_sel_acb }, +#endif +#if MARKERS_MAX >=3 { MT_ADV_CALLBACK, 2, "MARKER %d", menu_marker_sel_acb }, +#endif +#if MARKERS_MAX >=4 { MT_ADV_CALLBACK, 3, "MARKER %d", menu_marker_sel_acb }, - { MT_ADV_CALLBACK, UI_MARKER_OFF, "ALL OFF", menu_marker_sel_acb }, - { MT_ADV_CALLBACK, UI_MARKER_DELTA,"DELTA", menu_marker_sel_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel +#endif +#if MARKERS_MAX >=5 + { MT_ADV_CALLBACK, 4, "MARKER %d", menu_marker_sel_acb }, +#endif +#if MARKERS_MAX >=6 + { MT_ADV_CALLBACK, 5, "MARKER %d", menu_marker_sel_acb }, +#endif +#if MARKERS_MAX >=7 + { MT_ADV_CALLBACK, 6, "MARKER %d", menu_marker_sel_acb }, +#endif +#if MARKERS_MAX >=8 + { MT_ADV_CALLBACK, 7, "MARKER %d", menu_marker_sel_acb }, +#endif + { MT_CALLBACK, 0, "ALL OFF", menu_marker_disable_all_cb }, + { MT_ADV_CALLBACK, 0, "DELTA", menu_marker_delta_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_marker_ops[] = { @@ -1404,19 +1695,17 @@ const menuitem_t menu_marker_ops[] = { { MT_CALLBACK, ST_CENTER, S_RARROW"CENTER", menu_marker_op_cb }, { MT_CALLBACK, ST_SPAN, S_RARROW"SPAN", menu_marker_op_cb }, { MT_CALLBACK, UI_MARKER_EDELAY, S_RARROW"EDELAY", menu_marker_op_cb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_marker_search[] = { //{ MT_CALLBACK, "OFF", menu_marker_search_cb }, - { MT_CALLBACK, MENU_MARKER_S_MAX, "MAXIMUM", menu_marker_search_cb }, - { MT_CALLBACK, MENU_MARKER_S_MIN, "MINIMUM", menu_marker_search_cb }, - { MT_CALLBACK, MENU_MARKER_S_LEFT, "SEARCH\n" S_LARROW" LEFT", menu_marker_search_cb }, - { MT_CALLBACK, MENU_MARKER_S_RIGHT, "SEARCH\n" S_RARROW" RIGHT", menu_marker_search_cb }, + { MT_ADV_CALLBACK, MENU_MARKER_S_MAX, "MAXIMUM", menu_marker_search_mode_acb }, + { MT_ADV_CALLBACK, MENU_MARKER_S_MIN, "MINIMUM", menu_marker_search_mode_acb }, + { MT_CALLBACK, MK_SEARCH_LEFT, "SEARCH\n" S_LARROW" LEFT", menu_marker_search_dir_cb }, + { MT_CALLBACK, MK_SEARCH_RIGHT, "SEARCH\n" S_RARROW" RIGHT", menu_marker_search_dir_cb }, { MT_ADV_CALLBACK, 0, "TRACKING", menu_marker_tracking_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_marker_smith[] = { @@ -1425,43 +1714,47 @@ const menuitem_t menu_marker_smith[] = { { MT_ADV_CALLBACK, MS_REIM,"Re+Im", menu_marker_smith_acb }, { MT_ADV_CALLBACK, MS_RX, "R+jX", menu_marker_smith_acb }, { MT_ADV_CALLBACK, MS_RLC, "R+L/C", menu_marker_smith_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; + +#ifdef __VNA_MEASURE_MODULE__ +const menuitem_t menu_marker_measure[] = { + { MT_ADV_CALLBACK, MEASURE_NONE, "OFF", menu_measure_acb }, #ifdef __USE_LC_MATCHING__ - { MT_ADV_CALLBACK, 0, "L/C MATCH", menu_marker_lc_match_acb }, + { MT_ADV_CALLBACK, MEASURE_LC_MATH, "L/C MATCH", menu_measure_acb }, #endif - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel +#ifdef __S11_CABLE_MEASURE__ + { MT_ADV_CALLBACK, MEASURE_S11_CABLE, "CABLE\n (S11)", menu_measure_acb }, +#endif +#ifdef __S21_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, KM_MEASURE_R, "MEASURE\nRl = %b.4F"S_OHM, menu_keyboard_acb}, +#endif + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; +#endif const menuitem_t menu_marker[] = { { MT_SUBMENU, 0, "SELECT\nMARKER", menu_marker_sel }, { MT_SUBMENU, 0, "SEARCH", menu_marker_search }, { MT_SUBMENU, 0, "OPERATIONS", menu_marker_ops }, { MT_SUBMENU, 0, "SMITH\nVALUE", menu_marker_smith }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel +#ifdef __VNA_MEASURE_MODULE__ + { MT_SUBMENU, 0, "MEASURE", menu_marker_measure }, +#endif + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; #ifdef __DFU_SOFTWARE_MODE__ const menuitem_t menu_dfu[] = { { MT_CALLBACK, 0, "RESET AND\nENTER DFU", menu_dfu_cb }, - { MT_CANCEL, 0, S_LARROW"CANCEL", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; #endif #ifdef __USE_SERIAL_CONSOLE__ -//19200, 38400, 57600, 74800, 115200, 230400, 460800, 921600, 1843200, 3686400 -#if 0 -const menuitem_t menu_serial_speed2[] = { - { MT_ADV_CALLBACK, 6, "%u", menu_serial_speed_acb }, - { MT_ADV_CALLBACK, 7, "%u", menu_serial_speed_acb }, - { MT_ADV_CALLBACK, 8, "%u", menu_serial_speed_acb }, - { MT_ADV_CALLBACK, 9, "%u", menu_serial_speed_acb }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel -}; -#endif - const menuitem_t menu_serial_speed[] = { { MT_ADV_CALLBACK, 0, "%u", menu_serial_speed_acb }, { MT_ADV_CALLBACK, 1, "%u", menu_serial_speed_acb }, @@ -1470,37 +1763,75 @@ const menuitem_t menu_serial_speed[] = { { MT_ADV_CALLBACK, 4, "%u", menu_serial_speed_acb }, { MT_ADV_CALLBACK, 5, "%u", menu_serial_speed_acb }, { MT_ADV_CALLBACK, 6, "%u", menu_serial_speed_acb }, -// { MT_SUBMENU, 0, S_RARROW" MORE", menu_serial_speed2 }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_ADV_CALLBACK, 7, "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, 8, "%u", menu_serial_speed_acb }, + { MT_ADV_CALLBACK, 9, "%u", menu_serial_speed_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_connection[] = { { MT_ADV_CALLBACK, VNA_MODE_USB, "USB", menu_connection_acb }, { MT_ADV_CALLBACK, VNA_MODE_SERIAL, "SERIAL", menu_connection_acb }, { MT_SUBMENU, 0, "SERIAL\nSPEED", menu_serial_speed }, - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; +#endif + +const menuitem_t menu_clear[] = { + { MT_CALLBACK, MENU_CONFIG_RESET, "CLEAR ALL\nAND RESET", menu_config_cb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; + +#ifdef USE_VARIABLE_OFFSET_MENU +const menuitem_t menu_offset[] = { + { MT_ADV_CALLBACK, 0, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 1, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 2, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 3, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 4, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 5, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 6, "%dHz", menu_offset_acb }, + { MT_ADV_CALLBACK, 7, "%dHz", menu_offset_acb }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; #endif +const menuitem_t menu_device[] = { + { MT_ADV_CALLBACK, KM_THRESHOLD, "THRESHOLD\n%.6q", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_XTAL, "TCXO\n%.6q", menu_keyboard_acb }, + { MT_ADV_CALLBACK, KM_VBAT, "VBAT OFFSET\n %umV", menu_keyboard_acb }, +#ifdef USE_VARIABLE_OFFSET_MENU + { MT_ADV_CALLBACK, 0, "IF OFFSET\n %dHz", menu_offset_sel_acb }, +#endif +#ifdef __DIGIT_SEPARATOR__ + { MT_ADV_CALLBACK, 0, "SEPARATOR\n%s", menu_separator_acb }, +#endif +#ifdef __SD_CARD_DUMP_FIRMWARE__ + { MT_CALLBACK, SAVE_BIN_FILE, "DUMP\nFIRMWARE", menu_sdcard_cb }, +#endif +#ifdef __SD_CARD_LOAD__ + { MT_CALLBACK, MENU_CONFIG_LOAD, "LOAD\nCONFIG.INI", menu_config_cb }, +#endif + { MT_SUBMENU, 0, "CLEAR\nCONFIG", menu_clear }, + { MT_NONE, 0, NULL, menu_back } // next-> menu_back +}; + 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", menu_config_save_cb }, - { MT_SUBMENU, 0, "SWEEP\nPOINTS", menu_sweep_points }, #ifdef __USE_SERIAL_CONSOLE__ { MT_SUBMENU, 0, "CONNECTION", menu_connection }, #endif { MT_CALLBACK, MENU_CONFIG_VERSION, "VERSION", menu_config_cb }, #ifdef __LCD_BRIGHTNESS__ - { MT_CALLBACK, 0, "BRIGHTNESS", menu_brightness_cb }, + { MT_ADV_CALLBACK, 0, "BRIGHTNESS\n %d%%%%", menu_brightness_acb }, #endif #ifdef __DFU_SOFTWARE_MODE__ { MT_SUBMENU, 0, S_RARROW"DFU", menu_dfu }, #endif - { MT_CANCEL, 0, S_LARROW" BACK", NULL }, - { MT_NONE, 0, NULL, NULL } // sentinel + { MT_NONE, 0, NULL, menu_back } // next-> menu_back }; const menuitem_t menu_top[] = { @@ -1521,17 +1852,36 @@ const menuitem_t *menu_stack[MENU_STACK_DEPTH_MAX] = { menu_top, NULL, NULL, NULL }; +static const menuitem_t *menu_next_item(const menuitem_t *m){ + if (m == NULL) return NULL; + m++; // Next item + return m->type == MT_NONE ? (menuitem_t *)m->reference : m; +} + +static const menuitem_t *current_menu_item(int i){ + const menuitem_t *m = menu_stack[menu_current_level]; + while (i--) m = menu_next_item(m); + return m; +} + +static int current_menu_get_count(void){ + int i = 0; + const menuitem_t *m = menu_stack[menu_current_level]; + while (m){m = menu_next_item(m); i++;} + return i; +} + static void ensure_selection(void) { - const menuitem_t *menu = menu_stack[menu_current_level]; - int i; - for (i = 0; menu[i].type != MT_NONE; i++) - ; + int i = current_menu_get_count(); if (selection < 0) selection = -1; else if (selection >= i) selection = i-1; + if (i < MENU_BUTTON_MIN) i = MENU_BUTTON_MIN; + else if (i >=MENU_BUTTON_MAX) i = MENU_BUTTON_MAX; + menu_button_height = MENU_BUTTON_HEIGHT(i); } static void @@ -1541,11 +1891,8 @@ menu_move_back(bool leave_ui) return; menu_current_level--; ensure_selection(); - erase_menu_buttons(); if (leave_ui) ui_mode_normal(); - else - draw_menu(); } static void @@ -1555,8 +1902,6 @@ menu_push_submenu(const menuitem_t *submenu) menu_current_level++; menu_stack[menu_current_level] = submenu; ensure_selection(); - erase_menu_buttons(); - draw_menu(); } /* @@ -1567,45 +1912,33 @@ menu_move_top(void) return; menu_current_level = 0; ensure_selection(); - erase_menu_buttons(); - draw_menu(); } */ static void menu_invoke(int item) { - const menuitem_t *menu = menu_stack[menu_current_level]; - menu = &menu[item]; - + const menuitem_t *menu = current_menu_item(item); + if (menu == NULL) return; switch (menu->type) { - case MT_NONE: - case MT_BLANK: - case MT_CLOSE: - ui_mode_normal(); + case MT_CALLBACK: + if (menu->reference) ((menuaction_cb_t)menu->reference)(menu->data); break; - case MT_CANCEL: - menu_move_back(false); + case MT_ADV_CALLBACK: + if (menu->reference) ((menuaction_acb_t)menu->reference)(menu->data, NULL); break; - case MT_CALLBACK: { - menuaction_cb_t cb = (menuaction_cb_t)menu->reference; - if (cb) (*cb)(menu->data); - break; - } - case MT_ADV_CALLBACK: { - menuaction_acb_t cb = (menuaction_acb_t)menu->reference; - if (cb) (*cb)(menu->data, NULL); - break; - } case MT_SUBMENU: menu_push_submenu((const menuitem_t*)menu->reference); break; } + // Redraw menu after if UI in menu mode + if (ui_mode == UI_MENU) + draw_menu(-1); } -// Key names +// Key names (use numfont16x22.c glyph) #define KP_0 0 #define KP_1 1 #define KP_2 2 @@ -1669,6 +2002,24 @@ static const keypads_t keypads_scale[] = { { 0, 0, KP_NONE } }; +static const keypads_t keypads_ref[] = { + { 1, 3, KP_PERIOD }, + { 0, 3, KP_0 }, + { 0, 2, KP_1 }, + { 1, 2, KP_2 }, + { 2, 2, KP_3 }, + { 0, 1, KP_4 }, + { 1, 1, KP_5 }, + { 2, 1, KP_6 }, + { 0, 0, KP_7 }, + { 1, 0, KP_8 }, + { 2, 0, KP_9 }, + { 3, 2, KP_MINUS }, + { 3, 3, KP_X1 }, + { 2, 3, KP_BS }, + { 0, 0, KP_NONE } +}; + static const keypads_t keypads_time[] = { { 1, 3, KP_PERIOD }, { 0, 3, KP_0 }, @@ -1689,103 +2040,146 @@ static const keypads_t keypads_time[] = { }; static const keypads_list keypads_mode_tbl[KM_NONE] = { -[KM_START] = {keypads_freq , "START" }, // start -[KM_STOP] = {keypads_freq , "STOP" }, // stop -[KM_CENTER] = {keypads_freq , "CENTER" }, // center -[KM_SPAN] = {keypads_freq , "SPAN" }, // span -[KM_CW] = {keypads_freq , "CW FREQ" }, // cw freq -[KM_SCALE] = {keypads_scale, "SCALE" }, // scale -[KM_REFPOS] = {keypads_scale, "REFPOS" }, // refpos -[KM_EDELAY] = {keypads_time , "EDELAY" }, // electrical delay -[KM_VELOCITY_FACTOR] = {keypads_scale, "VELOCITY%"}, // velocity factor -[KM_SCALEDELAY] = {keypads_time , "DELAY" } // scale of delay +[KM_START] = {keypads_freq , "START" }, // start +[KM_STOP] = {keypads_freq , "STOP" }, // stop +[KM_CENTER] = {keypads_freq , "CENTER" }, // center +[KM_SPAN] = {keypads_freq , "SPAN" }, // span +[KM_CW] = {keypads_freq , "CW FREQ" }, // cw freq +[KM_VAR] = {keypads_freq , "JOG STEP" }, // VAR freq step +[KM_SCALE] = {keypads_scale, "SCALE" }, // scale +[KM_REFPOS] = {keypads_ref, "REFPOS" }, // refpos +[KM_EDELAY] = {keypads_time , "EDELAY" }, // electrical delay +[KM_VELOCITY_FACTOR] = {keypads_scale, "VELOCITY%%" }, // velocity factor +[KM_SCALEDELAY] = {keypads_time , "DELAY" }, // scale of delay +[KM_XTAL] = {keypads_freq , "TCXO 26MHz" }, // XTAL frequency +[KM_THRESHOLD] = {keypads_freq , "THRESHOLD" }, // Harmonic threshold frequency +[KM_VBAT] = {keypads_scale, "BAT OFFSET" }, // Vbat offset input in mV +#ifdef __S21_MEASURE__ +[KM_MEASURE_R] = {keypads_scale, "MEASURE Rl" }, // CH0 port impedance in Om +#endif +#ifdef __VNA_Z_RENORMALIZATION__ +[KM_Z_PORT] = {keypads_scale, "PORT Z 50" S_RARROW }, // Port Z renormalization impedance +#endif }; +static void +set_numeric_value(float f_val, freq_t u_val) +{ + switch (keypad_mode) { + case KM_START: set_sweep_frequency(ST_START, u_val); break; + case KM_STOP: set_sweep_frequency(ST_STOP, u_val); break; + case KM_CENTER: set_sweep_frequency(ST_CENTER, u_val); break; + case KM_SPAN: set_sweep_frequency(ST_SPAN, u_val); break; + case KM_CW: set_sweep_frequency(ST_CW, u_val); break; + case KM_VAR: var_freq = u_val; break; + case KM_SCALE: set_trace_scale(current_trace, f_val); break; + case KM_REFPOS: set_trace_refpos(current_trace, f_val);break; + case KM_EDELAY: set_electrical_delay(f_val); break; // pico seconds + case KM_VELOCITY_FACTOR: velocity_factor = u_val; break; + case KM_SCALEDELAY: set_trace_scale(current_trace, f_val * 1e-12); break;// pico second + case KM_XTAL: si5351_set_tcxo(u_val); break; + case KM_THRESHOLD:config._harmonic_freq_threshold= u_val;break; + case KM_VBAT: config._vbat_offset = u_val; break; +#ifdef __S21_MEASURE__ + case KM_MEASURE_R:config._measure_r = f_val; break; +#endif +#ifdef __VNA_Z_RENORMALIZATION__ + case KM_Z_PORT: current_props._portz = f_val; break; +#endif + } +} + static void draw_button(uint16_t x, uint16_t y, uint16_t w, uint16_t h, button_t *b) { uint16_t bw = b->border&BUTTON_BORDER_WIDTH_MASK; - ili9341_set_background(b->bg);ili9341_fill(x + bw, y + bw, w - (bw * 2), h - (bw * 2)); + lcd_set_foreground(b->fg); + lcd_set_background(b->bg);lcd_fill(x + bw, y + bw, w - (bw * 2), h - (bw * 2)); if (bw==0) return; uint16_t br = LCD_RISE_EDGE_COLOR; uint16_t bd = LCD_FALLEN_EDGE_COLOR; uint16_t type = b->border; - ili9341_set_background(type&BUTTON_BORDER_TOP ? br : bd);ili9341_fill(x, y, w, bw); // top - ili9341_set_background(type&BUTTON_BORDER_RIGHT ? br : bd);ili9341_fill(x + w - bw, y, bw, h); // right - ili9341_set_background(type&BUTTON_BORDER_LEFT ? br : bd);ili9341_fill(x, y, bw, h); // left - ili9341_set_background(type&BUTTON_BORDER_BOTTOM ? br : bd);ili9341_fill(x, y + h - bw, w, bw); // bottom + lcd_set_background(type&BUTTON_BORDER_TOP ? br : bd);lcd_fill(x, y, w, bw); // top + lcd_set_background(type&BUTTON_BORDER_RIGHT ? br : bd);lcd_fill(x + w - bw, y, bw, h); // right + lcd_set_background(type&BUTTON_BORDER_LEFT ? br : bd);lcd_fill(x, y, bw, h); // left + lcd_set_background(type&BUTTON_BORDER_BOTTOM ? br : bd);lcd_fill(x, y + h - bw, w, bw); // bottom + // Set colors for button text after + lcd_set_background(b->bg); } void drawMessageBox(char *header, 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; + // Draw header draw_button((LCD_WIDTH-MESSAGE_BOX_WIDTH)/2, LCD_HEIGHT/2-40, MESSAGE_BOX_WIDTH, 60, &b); - ili9341_set_background(LCD_FG_COLOR); - ili9341_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); - ili9341_set_foreground(b.fg); - ili9341_set_background(b.bg); - ili9341_drawstring(header, (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 10, LCD_HEIGHT/2-40 + 5); - ili9341_set_background(LCD_FG_COLOR); - ili9341_drawstring(text, (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 20, LCD_HEIGHT/2-40 + FONT_STR_HEIGHT + 8 + 14); + x = (LCD_WIDTH-MESSAGE_BOX_WIDTH)/2 + 10; + y = LCD_HEIGHT/2-40 + 5; + lcd_drawstring(x, y, header); + // Draw window + lcd_set_background(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); chThdSleepMilliseconds(delay); } static void -draw_keypad(void) +draw_keypad(uint32_t mask) { - int i = 0; + int i; button_t button; button.fg = LCD_MENU_TEXT_COLOR; - while (keypads[i].c != KP_NONE) { - button.bg = LCD_MENU_COLOR; + for(i = 0; keypads[i].c != KP_NONE; i++) { + if ((mask&(1<>(2-(period_pos()%3)))&(~1); + int c; + while(*buf) { + c = *buf++; + if (c == '.'){c = KP_PERIOD;xsim<<=4;} + else if (c == '-'){c = KP_MINUS; xsim&=~3;} else// if (c >= '0' && c <= '9') c = c - '0'; + uint16_t fg = LCD_INPUT_TEXT_COLOR; + uint16_t bg = LCD_INPUT_BG_COLOR; #ifdef UI_USE_NUMERIC_INPUT if (ui_mode == UI_NUMERIC && uistat.digit == 8-i) { fg = LCD_SPEC_INPUT_COLOR; @@ -1796,19 +2190,20 @@ draw_numeric_input(const char *buf) } } #endif - ili9341_set_foreground(fg); - ili9341_set_background(bg); + lcd_set_foreground(fg); + lcd_set_background(bg); if (c < 0 && focused) c = 0; - if (c >= 0) // c is number - ili9341_drawfont(c, x, LCD_HEIGHT-NUM_INPUT_HEIGHT+4); - else // erase - ili9341_fill(x, LCD_HEIGHT-NUM_INPUT_HEIGHT+4, NUM_FONT_GET_HEIGHT, NUM_FONT_GET_WIDTH+2+8); - - x += xsim&0x8000 ? NUM_FONT_GET_WIDTH+2+8 : NUM_FONT_GET_WIDTH+2; + // Add space before char + uint16_t space = xsim&1 ? 2 + 10 : 2; + xsim>>=1; + lcd_fill(x, y, space, NUM_FONT_GET_HEIGHT); + x+=space; + if (c < 0) continue; // c is number + lcd_drawfont(c, x, y); + x+=NUM_FONT_GET_WIDTH; } - // erase last - ili9341_set_background(LCD_INPUT_BG_COLOR); - ili9341_fill(x, LCD_HEIGHT-NUM_INPUT_HEIGHT+4, NUM_FONT_GET_WIDTH+2+8, NUM_FONT_GET_WIDTH+2+8); + lcd_set_background(LCD_INPUT_BG_COLOR); + lcd_fill(x, y, NUM_FONT_GET_WIDTH+2+10, NUM_FONT_GET_HEIGHT); } static int @@ -1821,9 +2216,13 @@ menu_is_multiline(const char *label) return n; } +/* + * Button icons bitmaps + */ #define ICON_WIDTH 16 #define ICON_HEIGHT 11 -static const uint8_t check_box[] = { +#define ICON_GET_DATA(i) (&button_icons[2*ICON_HEIGHT*(i)]) +static const uint8_t button_icons[] = { _BMP16(0b0011111111110000), _BMP16(0b0010000000010000), _BMP16(0b0010000000010000), @@ -1871,18 +2270,39 @@ static const uint8_t check_box[] = { _BMP16(0b0001001100100000), _BMP16(0b0000100001000000), _BMP16(0b0000011110000000), + + _BMP16(0b0011111111111000), + _BMP16(0b0010000000001000), + _BMP16(0b0010001111101000), + _BMP16(0b0010011001101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010111111101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010000000001000), + _BMP16(0b0011111111111000), + + _BMP16(0b0011111111111000), + _BMP16(0b0010000000001000), + _BMP16(0b0010110001101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010111011101000), + _BMP16(0b0010111111101000), + _BMP16(0b0010110101101000), + _BMP16(0b0010110101101000), + _BMP16(0b0010110001101000), + _BMP16(0b0010000000001000), + _BMP16(0b0011111111111000), }; static void -draw_menu_buttons(const menuitem_t *menu) +draw_menu_buttons(const menuitem_t *m, uint32_t mask) { - int i = 0, y = 1; - for (i = 0; i < MENU_BUTTON_MAX; i++, y+=MENU_BUTTON_HEIGHT) { - if (menu[i].type == MT_NONE) - break; - if (menu[i].type == MT_BLANK) - continue; - + 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){ + if (m->reference) ((menuaction_acb_t)m->reference)(m->data, &button); + // Apply custom text, from button label and + if (m->label != MT_CUSTOM_LABEL) + plot_printf(button.label, sizeof(button.label), m->label, button.p1.u); + text = button.label; } - char button_text[32]; - plot_printf(button_text, sizeof(button_text), menu[i].label, button.p1.u, button.p1.u); - draw_button(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT, &button); - - ili9341_set_foreground(button.fg); - ili9341_set_background(button.bg); - uint16_t text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + 5; - + 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){ - ili9341_blitBitmap(LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + 1, y+(MENU_BUTTON_HEIGHT-ICON_HEIGHT)/2, ICON_WIDTH, ICON_HEIGHT, &check_box[button.icon*2*ICON_HEIGHT]); - text_offs=LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER+1+ICON_WIDTH; + lcd_blitBitmap(LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + 1, 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 + 1 + ICON_WIDTH; } - int lines = menu_is_multiline(button_text); -#define BIG_BUTTON_FONT 1 -#ifdef BIG_BUTTON_FONT - ili9341_drawstring_7x13(button_text, text_offs, y+(MENU_BUTTON_HEIGHT-lines*bFONT_GET_HEIGHT)/2); -#else - ili9341_drawstring(button_text, text_offs, y+(MENU_BUTTON_HEIGHT-lines*FONT_GET_HEIGHT)/2); -#endif + else + text_offs = LCD_WIDTH-MENU_BUTTON_WIDTH+MENU_BUTTON_BORDER + 5; + // Draw button text + int lines = menu_is_multiline(text); + lcd_drawstring(text_offs, y+(menu_button_height-lines*FONT_GET_HEIGHT)/2, text); } - ili9341_set_background(LCD_BG_COLOR); - for (; i < MENU_BUTTON_MAX; i++, y+=MENU_BUTTON_HEIGHT) { - ili9341_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, y, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT); + // 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); } } static void -menu_select_touch(int i) +draw_menu(uint32_t mask) { - selection = i; - draw_menu(); - touch_wait_release(); - selection = -1; - menu_invoke(i); + draw_menu_buttons(menu_stack[menu_current_level], mask); } +#if 0 static void -menu_apply_touch(int touch_x, int touch_y) +erase_menu_buttons(void) { - const menuitem_t *menu = menu_stack[menu_current_level]; - int i; - - for (i = 0; i < MENU_BUTTON_MAX; i++) { - if (menu[i].type == MT_NONE) - break; - if (menu[i].type == MT_BLANK) - continue; - int y = MENU_BUTTON_HEIGHT*i; - if (y < touch_y && touch_y < y+MENU_BUTTON_HEIGHT && LCD_WIDTH-MENU_BUTTON_WIDTH < touch_x) { - menu_select_touch(i); - return; - } - } - - touch_wait_release(); - ui_mode_normal(); + 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 -draw_menu(void) +ui_mode_menu(void) { - draw_menu_buttons(menu_stack[menu_current_level]); + 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 -erase_menu_buttons(void) +ui_process_menu_lever(uint16_t status) { -// ili9341_set_background(LCD_BG_COLOR); -// ili9341_fill(LCD_WIDTH-MENU_BUTTON_WIDTH, 0, MENU_BUTTON_WIDTH, MENU_BUTTON_HEIGHT*MENU_BUTTON_MAX); + if (status & EVT_BUTTON_SINGLE_CLICK) { + menu_invoke(selection); + return; + } + uint16_t count = current_menu_get_count(); + do { + uint32_t mask = 1<= count){ + ui_mode_normal(); + return; + } + draw_menu(mask|(1<= 10 && uistat.digit < 9; uistat.digit++) + x /= 10; +} - { - uint32_t x = uistat.value; - int n = 0; - for (; x >= 10 && n < 9; n++) - x /= 10; - uistat.digit = n; - } -// uistat.previous_value = uistat.value; +static void +draw_numeric_area(void) +{ + char buf[10]; + plot_printf(buf, sizeof buf, "%9d", uistat.value); + draw_numeric_input(buf); } static void -set_numeric_value(void) -{ - switch (keypad_mode) { - case KM_START: - set_sweep_frequency(ST_START, uistat.value); - break; - case KM_STOP: - set_sweep_frequency(ST_STOP, uistat.value); - break; - case KM_CENTER: - set_sweep_frequency(ST_CENTER, uistat.value); - break; - case KM_SPAN: - set_sweep_frequency(ST_SPAN, uistat.value); - break; - case KM_CW: - set_sweep_frequency(ST_CW, uistat.value); - break; - case KM_SCALE: - set_trace_scale(current_trace, uistat.value / 1000.0); - break; - case KM_REFPOS: - set_trace_refpos(current_trace, uistat.value / 1000.0); - break; - case KM_EDELAY: - set_electrical_delay(uistat.value); - break; - case KM_VELOCITY_FACTOR: - velocity_factor = uistat.value/100.0; - break; - } -} - -static void -draw_numeric_area(void) -{ - char buf[10]; - plot_printf(buf, sizeof buf, "%9d", uistat.value); - draw_numeric_input(buf); -} - -static void -ui_mode_numeric(int _keypad_mode) +ui_mode_numeric(int _keypad_mode) { if (ui_mode == UI_NUMERIC) return; - leave_ui_mode(); - // keypads array keypad_mode = _keypad_mode; ui_mode = UI_NUMERIC; @@ -2100,58 +2480,53 @@ ui_mode_numeric(int _keypad_mode) } static void -ui_process_numeric(void) -{ - int status = btn_check(); - - if (status != 0) { - if (status == EVT_BUTTON_SINGLE_CLICK) { - status = btn_wait_release(); - if (uistat.digit_mode) { - if (status & (EVT_BUTTON_SINGLE_CLICK | EVT_BUTTON_DOWN_LONG)) { - uistat.digit_mode = FALSE; - draw_numeric_area(); - } - } else { - if (status & EVT_BUTTON_DOWN_LONG) { - uistat.digit_mode = TRUE; - draw_numeric_area(); - } else if (status & EVT_BUTTON_SINGLE_CLICK) { - set_numeric_value(); - ui_mode_normal(); - } +ui_process_numeric_lever(uint16_t status) +{ + if (status == EVT_BUTTON_SINGLE_CLICK) { + status = btn_wait_release(); + if (uistat.digit_mode) { + if (status & (EVT_BUTTON_SINGLE_CLICK | EVT_BUTTON_DOWN_LONG)) { + uistat.digit_mode = FALSE; + draw_numeric_area(); } } else { - do { - if (uistat.digit_mode) { - if (status & EVT_DOWN) { - if (uistat.digit < 8) - uistat.digit++; - else - goto exit; - } - if (status & EVT_UP) { - if (uistat.digit > 0) - uistat.digit--; - else - goto exit; - } - } else { - int32_t step = 1; - int n; - for (n = uistat.digit; n > 0; n--) - step *= 10; - if (status & EVT_DOWN) - uistat.value += step; - if (status & EVT_UP) - uistat.value -= step; - } + if (status & EVT_BUTTON_DOWN_LONG) { + uistat.digit_mode = TRUE; draw_numeric_area(); - status = btn_wait_release(); - } while (status != 0); + } else if (status & EVT_BUTTON_SINGLE_CLICK) { + set_numeric_value(); + goto exit; + } } + return; } + do { + if (uistat.digit_mode) { + if (status & EVT_DOWN) { + if (uistat.digit < 8) + uistat.digit++; + else + goto exit; + } + if (status & EVT_UP) { + if (uistat.digit > 0) + uistat.digit--; + else + goto exit; + } + } else { + int32_t step = 1; + int n; + for (n = uistat.digit; n > 0; n--) + step *= 10; + if (status & EVT_DOWN) + uistat.value += step; + if (status & EVT_UP) + uistat.value -= step; + } + draw_numeric_area(); + } while ((status = btn_wait_release()) != 0); return; exit: @@ -2198,219 +2573,24 @@ numeric_apply_touch(int touch_x, int touch_y) } #endif +/* + * Keyboard processing + */ static void ui_mode_keypad(int _keypad_mode) { if (ui_mode == UI_KEYPAD) return; - + set_area_size(0, 0); // keypads array keypad_mode = _keypad_mode; keypads = keypads_mode_tbl[keypad_mode].keypad_type; - int i; - for (i = 0; keypads[i+1].c != KP_NONE; i++) - ; - keypads_last_index = i; - + selection = -1; + kp_index = 0; ui_mode = UI_KEYPAD; - area_width = AREA_WIDTH_NORMAL - MENU_BUTTON_WIDTH; - area_height = LCD_HEIGHT-NUM_INPUT_HEIGHT; - draw_menu(); - draw_keypad(); + draw_menu(-1); + draw_keypad(-1); draw_numeric_area_frame(); - draw_numeric_input(""); -} - -static void -ui_mode_normal(void) -{ - if (ui_mode == UI_NORMAL) - return; - - area_width = AREA_WIDTH_NORMAL; - area_height = AREA_HEIGHT_NORMAL; - leave_ui_mode(); - ui_mode = UI_NORMAL; -} - -static void -leave_ui_mode() -{ -#ifdef UI_USE_NUMERIC_INPUT - if (ui_mode == UI_NUMERIC) { - request_to_draw_cells_behind_numeric_input(); - erase_numeric_input(); - } - if (ui_mode == UI_MENU) -#endif - { - request_to_draw_cells_behind_menu(); - erase_menu_buttons(); - } - draw_frequencies(); -} - -#define MARKER_SPEEDUP (808 / POINTS_COUNT) -static void -lever_move_marker(int status) -{ - uint16_t step = 1<>MARKER_SPEEDUP; - if (idx < 0) idx = 0; - } - if (status & EVT_UP) { - idx+= step>>MARKER_SPEEDUP; - if (idx > sweep_points-1) - idx = sweep_points-1 ; - } - set_marker_index(active_marker, idx); - redraw_marker(active_marker); - step++; - } - status = btn_wait_release(); - } while (status != 0); -} - -#ifdef UI_USE_LEVELER_SEARCH_MODE -static void -lever_search_marker(int status) -{ - int i = -1; - if (active_marker >= 0) { - if (status & EVT_DOWN) - i = marker_search_left(markers[active_marker].index); - else if (status & EVT_UP) - i = marker_search_right(markers[active_marker].index); - if (i != -1){ - markers[active_marker].index = i; - redraw_marker(active_marker); - } - } -} -#endif - -// ex. 10942 -> 10000 -// 6791 -> 5000 -// 341 -> 200 -static uint32_t -step_round(uint32_t v) -{ - // decade step - uint32_t x = 1; - for (x = 1; x*10 < v; x*= 10) - ; - - // 1-2-5 step - if (x * 2 > v) - return x; - else if (x * 5 > v) - return x * 2; - else - return x * 5; -} - -static void -lever_zoom_span(int status) -{ - uint32_t span = get_sweep_frequency(ST_SPAN); - if (status & EVT_UP) { - span = step_round(span - 1); - } else if (status & EVT_DOWN) { - span = step_round(span + 1); - span = step_round(span * 3); - } - set_sweep_frequency(ST_SPAN, span); -} - -static void -lever_move(int status, int mode) -{ - uint32_t center = get_sweep_frequency(mode); - uint32_t span = get_sweep_frequency(ST_SPAN); - span = step_round(span / 3); - if (status & EVT_UP) { - set_sweep_frequency(mode, center + span); - } else if (status & EVT_DOWN) { - set_sweep_frequency(mode, center - span); - } -} - -#define STEPRATIO 0.2 - -static void -lever_edelay(int status) -{ - float value = electrical_delay; - float ratio = STEPRATIO; - if (value < 0) - ratio = -ratio; - if (status & EVT_UP) { - value = (1 - ratio) * value; - } else if (status & EVT_DOWN) { - value = (1 + ratio) * value; - } - set_electrical_delay(value); -} - -static void -ui_process_normal(void) -{ - int status = btn_check(); - if (status != 0) { - if (status & EVT_BUTTON_SINGLE_CLICK) { - ui_mode_menu(); - } else { - switch (uistat.lever_mode) { - case LM_MARKER: lever_move_marker(status); break; -#ifdef UI_USE_LEVELER_SEARCH_MODE - case LM_SEARCH: lever_search_marker(status); break; -#endif - case LM_CENTER: - lever_move(status, FREQ_IS_STARTSTOP() ? ST_START : ST_CENTER); - break; - case LM_SPAN: - if (FREQ_IS_STARTSTOP()) - lever_move(status, ST_STOP); - else - lever_zoom_span(status); - break; - case LM_EDELAY: - lever_edelay(status); - break; - } - } - } -} - -static void -ui_process_menu(void) -{ - int status = btn_check(); - if (status != 0) { - if (status & EVT_BUTTON_SINGLE_CLICK) { - menu_invoke(selection); - return; - } - do { - if (status & EVT_UP) - selection++; - if (status & EVT_DOWN) - selection--; - // close menu if next item is sentinel or less - if (selection < 0 || menu_stack[menu_current_level][selection].type == MT_NONE) - goto menuclose; - draw_menu(); - chThdSleepMilliseconds(200); - } while ((status = btn_wait_release()) != 0); - } - return; - -menuclose: - ui_mode_normal(); } static int @@ -2418,68 +2598,38 @@ keypad_click(int key) { int c = keypads[key].c; if ((c >= KP_X1 && c <= KP_G) || c == KP_N || c == KP_P) { - int32_t scale = 1; - if (c >= KP_X1 && c <= KP_G) { - int n = c - KP_X1; - while (n-- > 0) - scale *= 1000; - } else if (c == KP_N) { - scale *= 1000; - } - /* numeric input done */ - double value = my_atof(kp_buf) * scale; - switch (keypad_mode) { - case KM_START: - set_sweep_frequency(ST_START, value); - break; - case KM_STOP: - set_sweep_frequency(ST_STOP, value); - break; - case KM_CENTER: - set_sweep_frequency(ST_CENTER, value); - break; - case KM_SPAN: - set_sweep_frequency(ST_SPAN, value); - break; - case KM_CW: - set_sweep_frequency(ST_CW, value); - break; - case KM_SCALE: - set_trace_scale(current_trace, value); - break; - case KM_REFPOS: - set_trace_refpos(current_trace, value); - break; - case KM_EDELAY: - set_electrical_delay(value); // pico seconds - break; - case KM_VELOCITY_FACTOR: - velocity_factor = value / 100.0; - break; - case KM_SCALEDELAY: - set_trace_scale(current_trace, value * 1e-12); // pico second - break; + if (kp_index == 0) + return KP_CANCEL; + uint16_t scale = 0; + if (c > KP_X1 && c <= KP_G) scale = c - KP_X1; + if (c == KP_N) scale = 1; + if (scale){ + scale+= (scale<<1); + int i = period_pos(); if (i+scale>NUMINPUT_LEN) scale = NUMINPUT_LEN - 1 - i; + while (scale--) { + char v = kp_buf[i+1]; if (v == 0 || kp_buf[i] == 0) {v = '0'; kp_buf[i+2] = 0;} + kp_buf[i+1] = kp_buf[i]; + kp_buf[i ] = v; + i++; + } } + // numeric input done + set_numeric_value(my_atof(kp_buf), my_atoui(kp_buf)); return KP_DONE; } - if (c <= 9 && kp_index < NUMINPUT_LEN) { + if (c <= KP_9 && kp_index < NUMINPUT_LEN) { kp_buf[kp_index++] = '0' + c; } else if (c == KP_PERIOD && kp_index < NUMINPUT_LEN) { - // check period in former input - int j; - for (j = 0; j < kp_index && kp_buf[j] != '.'; j++) - ; // append period if there are no period - if (kp_index == j) + if (kp_index == period_pos()) kp_buf[kp_index++] = '.'; } else if (c == KP_MINUS) { if (kp_index == 0) kp_buf[kp_index++] = '-'; } else if (c == KP_BS) { - if (kp_index == 0) { + if (kp_index == 0) return KP_CANCEL; - } --kp_index; } kp_buf[kp_index] = '\0'; @@ -2487,117 +2637,167 @@ keypad_click(int key) return KP_CONTINUE; } -static int -keypad_apply_touch(void) +static void +keypad_apply_touch(int touch_x, int touch_y) { - int touch_x, touch_y; int i = 0; - - touch_position(&touch_x, &touch_y); - - while (keypads[i].c != KP_NONE) { + do { int x = KP_GET_X(keypads[i].x); int y = KP_GET_Y(keypads[i].y); if (x < touch_x && touch_x < x+KP_WIDTH && y < touch_y && touch_y < y+KP_HEIGHT) { // draw focus + uint32_t mask = (1< keypads_last_index) + selection = 0; + draw_keypad(mask|(1< keypads_last_index) - selection = 0; - draw_keypad(); - status = btn_wait_release(); - } while (status != 0); - } + if (ui_mode == UI_NORMAL) + return; + set_area_size(AREA_WIDTH_NORMAL, AREA_HEIGHT_NORMAL); +#ifdef UI_USE_NUMERIC_INPUT + if (ui_mode == UI_NUMERIC) { + request_to_draw_cells_behind_numeric_input(); + erase_numeric_input(); + } +#endif + if (ui_mode == UI_MENU) + request_to_draw_cells_behind_menu(); + if (ui_mode == UI_KEYPAD) + request_to_redraw(REDRAW_CLRSCR | REDRAW_AREA | REDRAW_BATTERY | REDRAW_CAL_STATUS | REDRAW_FREQUENCY); + request_to_redraw(REDRAW_FREQUENCY); + ui_mode = UI_NORMAL; +} - else if (status == EVT_BUTTON_SINGLE_CLICK) { - if (keypad_click(selection)) - /* exit loop on done or cancel */ - break; +#define MARKER_SPEEDUP (808 / POINTS_COUNT) +static void +lever_move_marker(uint16_t status) +{ + if (active_marker == MARKER_INVALID || !markers[active_marker].enabled) return; + uint16_t step = 1<>MARKER_SPEEDUP; + if (idx < 0) idx = 0; } - - else if (touch_check() == EVT_TOUCH_PRESSED) { - int key = keypad_apply_touch(); - if (key >= 0 && keypad_click(key)) - /* exit loop on done or cancel */ - break; + if (status & EVT_UP) { + idx+= step>>MARKER_SPEEDUP; + if (idx > sweep_points-1) + idx = sweep_points-1 ; } - } - - redraw_frame(); - request_to_redraw_grid(); - ui_mode_normal(); - //redraw_all(); + set_marker_index(active_marker, idx); + redraw_marker(active_marker); + step++; + } while ((status = btn_wait_release()) != 0); } +#ifdef UI_USE_LEVELER_SEARCH_MODE static void -ui_process_lever(void) +lever_search_marker(int status) { - switch (ui_mode) { - case UI_NORMAL: - ui_process_normal(); - break; - case UI_MENU: - ui_process_menu(); - break; -#ifdef UI_USE_NUMERIC_INPUT - case UI_NUMERIC: - ui_process_numeric(); - break; + if (active_marker == active_marker) return; + if (status & EVT_DOWN) + marker_search_dir(markers[active_marker].index, MK_SEARCH_LEFT); + else if (status & EVT_UP) + marker_search_dir(markers[active_marker].index, MK_SEARCH_RIGHT); +} #endif - case UI_KEYPAD: - ui_process_keypad(); - break; + +// ex. 10942 -> 10000 +// 6791 -> 5000 +// 341 -> 200 +static uint32_t +step_round(uint32_t v) +{ + // decade step + uint32_t nx, x = 1; + while((nx = x*10) < v) x = nx; + // 1-2-5 step + if (x * 2 > v) return x; + if (x * 5 > v) return x * 2; + return x * 5; +} + +static void +lever_frequency(uint16_t status, int mode) +{ + freq_t freq = get_sweep_frequency(mode); + if (mode == ST_SPAN){ + if (status & EVT_UP ) freq = var_freq ? (freq + var_freq) : step_round(freq*4 + 1); + if (status & EVT_DOWN) freq = var_freq ? (freq - var_freq) : step_round(freq - 1); + } + else{ + freq_t span = var_freq ? var_freq : step_round(get_sweep_frequency(ST_SPAN) / 4); + if (status & EVT_UP ) freq+= span; + if (status & EVT_DOWN) freq-= span; } + if (freq > STOP_MAX || freq < START_MIN) return; + set_sweep_frequency(mode, freq); } +#define STEPRATIO 0.2 static void -drag_marker(int t, int8_t m) +lever_edelay(uint16_t status) { - /* wait touch release */ - do { - int touch_x, touch_y; - int index; - touch_position(&touch_x, &touch_y); - touch_x -= OFFSETX; - touch_y -= OFFSETY; - index = search_nearest_index(touch_x, touch_y, t); - if (index >= 0) { - set_marker_index(m, index); - redraw_marker(m); - } - } while (touch_check()!= EVT_TOUCH_RELEASED); + float value = electrical_delay; + float ratio = value > 0 ? STEPRATIO : -STEPRATIO; + if (status & EVT_UP) + value*= (1 - ratio); + if (status & EVT_DOWN) + value*= (1 + ratio); + set_electrical_delay(value); + while (btn_wait_release() != 0); } -static int +static bool touch_pickup_marker(int touch_x, int touch_y) { touch_x -= OFFSETX; touch_y -= OFFSETY; - int8_t i = MARKER_INVALID, mt, m, t; + int i = MARKER_INVALID, mt, m, t; int min_dist = MARKER_PICKUP_DISTANCE * MARKER_PICKUP_DISTANCE; // Search closest marker to touch position for (t = 0; t < TRACES_MAX; t++) { @@ -2624,109 +2824,133 @@ touch_pickup_marker(int touch_x, int touch_y) active_marker = i; } // Disable tracking - uistat.marker_tracking = false; + props_mode&= ~TD_MARKER_TRACK; // Leveler mode = marker move select_lever_mode(LM_MARKER); // select trace current_trace = mt; // drag marker until release - drag_marker(mt, i); + do { + touch_position(&touch_x, &touch_y); + int index = search_nearest_index(touch_x - OFFSETX, touch_y - OFFSETY, current_trace); + if (index >= 0) { + set_marker_index(active_marker, index); + redraw_marker(active_marker); + } + } while (touch_check()!= EVT_TOUCH_RELEASED); return TRUE; } -#ifdef __USE_SD_CARD__ -//******************************************************************************************* -// Bitmap file header for LCD_WIDTH x LCD_HEIGHT image 16bpp (v4 format allow set RGB mask) -//******************************************************************************************* -#define BMP_UINT32(val) ((val)>>0)&0xFF, ((val)>>8)&0xFF, ((val)>>16)&0xFF, ((val)>>24)&0xFF -#define BMP_H1_SIZE (14) // BMP header 14 bytes -#define BMP_V4_SIZE (56) // v4 header 56 bytes -#define BMP_HEAD_SIZE (BMP_H1_SIZE + BMP_V4_SIZE) // Size of all headers -#define BMP_SIZE (2*LCD_WIDTH*LCD_HEIGHT) // Bitmap size = 2*w*h -#define BMP_FILE_SIZE (BMP_SIZE + BMP_HEAD_SIZE) // File size = headers + bitmap -static const uint8_t bmp_header_v4[14+56] = { -// BITMAPFILEHEADER (14 byte size) - 0x42, 0x4D, // BM signature - BMP_UINT32(BMP_FILE_SIZE), // File size (h + v4 + bitmap) - 0x00, 0x00, // reserved - 0x00, 0x00, // reserved - BMP_UINT32(BMP_HEAD_SIZE), // Size of all headers (h + v4) -// BITMAPINFOv4 (56 byte size) - BMP_UINT32(BMP_V4_SIZE), // Data offset after this point (v4 size) - BMP_UINT32(LCD_WIDTH), // Width - BMP_UINT32(LCD_HEIGHT), // Height - 0x01, 0x00, // Planes - 0x10, 0x00, // 16bpp - 0x03, 0x00, 0x00, 0x00, // Compression (BI_BITFIELDS) - BMP_UINT32(BMP_SIZE), // Bitmap size (w*h*2) - 0xC4, 0x0E, 0x00, 0x00, // x Resolution (96 DPI = 96 * 39.3701 inches per metre = 0x0EC4) - 0xC4, 0x0E, 0x00, 0x00, // y Resolution (96 DPI = 96 * 39.3701 inches per metre = 0x0EC4) - 0x00, 0x00, 0x00, 0x00, // Palette size - 0x00, 0x00, 0x00, 0x00, // Palette used -// Extend v4 header data (color mask for RGB565) - 0x00, 0xF8, 0x00, 0x00, // R mask = 0b11111000 00000000 - 0xE0, 0x07, 0x00, 0x00, // G mask = 0b00000111 11100000 - 0x1F, 0x00, 0x00, 0x00, // B mask = 0b00000000 00011111 - 0x00, 0x00, 0x00, 0x00 // A mask = 0b00000000 00000000 -}; +static bool +touch_lever_mode_select(int touch_x, int touch_y) +{ + int mode = -1; + if (touch_y > HEIGHT) + mode = touch_x < FREQUENCIES_XPOS2 ? LM_FREQ_0 : LM_FREQ_1; + if (touch_y < 25) + mode = (touch_x < FREQUENCIES_XPOS2 && electrical_delay != 0.0) ? LM_EDELAY : LM_MARKER; + if (mode == -1) return FALSE; -static int + touch_wait_release(); + // Check already selected + if (select_lever_mode(mode)) return TRUE; + // Call keyboard for enter + switch(mode){ + case LM_FREQ_0: ui_mode_keypad(FREQ_IS_CENTERSPAN() ? KM_CENTER : KM_START); break; + case LM_FREQ_1: ui_mode_keypad(FREQ_IS_CENTERSPAN() ? KM_SPAN : KM_STOP ); break; + case LM_EDELAY: ui_mode_keypad(KM_EDELAY); break; + } + return TRUE; +} + +static void +ui_process_normal_lever(uint16_t status) +{ + if (status & EVT_BUTTON_SINGLE_CLICK) { + ui_mode_menu(); + return; + } + switch (lever_mode) { + case LM_MARKER: lever_move_marker(status); break; +#ifdef UI_USE_LEVELER_SEARCH_MODE + case LM_SEARCH: lever_search_marker(status); break; +#endif + case LM_FREQ_0: lever_frequency(status, FREQ_IS_STARTSTOP() ? ST_START : ST_CENTER); break; + case LM_FREQ_1: lever_frequency(status, FREQ_IS_STARTSTOP() ? ST_STOP : ST_SPAN ); break; + case LM_EDELAY: lever_edelay(status); break; + } +} + +static bool +normal_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; + if (touch_x < OFFSETX - 5 || touch_x > OFFSETX + CELLOFFSETX + 10 || + touch_y < OFFSETY || touch_y > AREA_HEIGHT_NORMAL) return FALSE; + float ref = trace[t].refpos; + float scale = trace[t].scale; + + if (touch_y < GRIDY*1*NGRIDY/4) ref+=0.5f; + else if (touch_y < GRIDY*2*NGRIDY/4) {scale*=2.0f;ref=ref/2-NGRIDY/4 + NGRIDY/2;} + else if (touch_y < GRIDY*3*NGRIDY/4) {scale/=2.0f;ref=2*ref-NGRIDY + NGRIDY/2;} + else ref-=0.5f; + + trace[t].scale = scale; + trace[t].refpos = ref; + plot_into_index(measured); + request_to_redraw(REDRAW_AREA); + chThdSleepMilliseconds(100); + return TRUE; +} + +#ifdef __USE_SD_CARD__ +static bool made_screenshot(int touch_x, int touch_y) { - int y, i; - UINT size; if (touch_y < HEIGHT || touch_x < FREQUENCIES_XPOS3 || touch_x > FREQUENCIES_XPOS2) return FALSE; touch_wait_release(); -// uint32_t time = chVTGetSystemTimeX(); -// shell_printf("Screenshot\r\n"); - FRESULT res = f_mount(fs_volume, "", 1); - // fs_volume, fs_file and fs_filename stored at end of spi_buffer!!!!! - uint16_t *buf = (uint16_t *)spi_buffer; -// shell_printf("Mount = %d\r\n", res); - if (res != FR_OK) - return TRUE; -#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.bmp", dr, tr); -#else - plot_printf(fs_filename, FF_LFN_BUF, "%08X.bmp", rtc_get_FAT()); -#endif - res = f_open(fs_file, fs_filename, FA_CREATE_ALWAYS | FA_READ | FA_WRITE); -// shell_printf("Open %s, result = %d\r\n", fs_filename, res); - if (res == FR_OK){ - res = f_write(fs_file, bmp_header_v4, sizeof(bmp_header_v4), &size); - for (y = LCD_HEIGHT-1; y >= 0 && res == FR_OK; y--) { - ili9341_read_memory(0, y, LCD_WIDTH, 1, buf); - for (i = 0; i < LCD_WIDTH; i++) - buf[i] = __REVSH(buf[i]); // swap byte order (example 0x10FF to 0xFF10) - res = f_write(fs_file, buf, LCD_WIDTH*sizeof(uint16_t), &size); - } - res = 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, (LCD_WIDTH*LCD_HEIGHT*sizeof(uint16_t)+sizeof(bmp_header_v4))*10000/time); - drawMessageBox("SCREENSHOT", res == FR_OK ? fs_filename : " Fail write ", 2000); - request_to_redraw_grid(); + menu_sdcard_cb(SAVE_BMP_FILE); return TRUE; } #endif -static int -touch_lever_mode_select(int touch_x, int touch_y) +static void +normal_apply_touch(int touch_x, int touch_y){ + // Try drag marker + if (touch_pickup_marker(touch_x, touch_y)) + return; +#ifdef __USE_SD_CARD__ + // Try made screenshot + if (made_screenshot(touch_x, touch_y)) + return; +#endif + if (normal_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; + // default: switch menu mode after release + touch_wait_release(); + ui_mode_menu(); +} +//========================== end normal plot input ======================= + +static void +ui_process_lever(void) { - if (touch_y > HEIGHT) { - select_lever_mode(touch_x < FREQUENCIES_XPOS2 ? LM_CENTER : LM_SPAN); - return TRUE; - } - if (touch_y < 25) { - select_lever_mode((touch_x < FREQUENCIES_XPOS2 && electrical_delay != 0.0) ? LM_EDELAY : LM_MARKER); - return TRUE; +// last_button = 0; + uint16_t status = btn_check(); + if (status == 0) return; + switch (ui_mode) { + case UI_NORMAL: ui_process_normal_lever(status); break; + case UI_MENU: ui_process_menu_lever(status); break; +#ifdef UI_USE_NUMERIC_INPUT + case UI_NUMERIC:ui_process_numeric_lever(status); break; +#endif + case UI_KEYPAD: ui_process_keypad_lever(status); break; } - return FALSE; } static @@ -2737,32 +2961,12 @@ void ui_process_touch(void) if (status == EVT_TOUCH_PRESSED || status == EVT_TOUCH_DOWN) { touch_position(&touch_x, &touch_y); switch (ui_mode) { - case UI_NORMAL: - // Try drag marker - if (touch_pickup_marker(touch_x, touch_y)) - break; -#ifdef __USE_SD_CARD__ - if (made_screenshot(touch_x, touch_y)) - break; -#endif - // Try select lever mode (top and bottom screen) - if (touch_lever_mode_select(touch_x, touch_y)) { - touch_wait_release(); - break; - } - // switch menu mode after release - touch_wait_release(); - selection = -1; // hide keyboard mode selection - ui_mode_menu(); - break; - case UI_MENU: - menu_apply_touch(touch_x, touch_y); - break; + case UI_NORMAL: normal_apply_touch(touch_x, touch_y); break; + case UI_MENU: menu_apply_touch(touch_x, touch_y); break; #ifdef UI_USE_NUMERIC_INPUT - case UI_NUMERIC: - numeric_apply_touch(touch_x, touch_y); - break; + case UI_NUMERIC:numeric_apply_touch(touch_x, touch_y);break; #endif + case UI_KEYPAD: keypad_apply_touch(touch_x, touch_y); break; } } } diff --git a/usbcfg.c b/usbcfg.c index 11ed22c..65e89b4 100644 --- a/usbcfg.c +++ b/usbcfg.c @@ -70,7 +70,7 @@ static const uint8_t vcom_configuration_descriptor_data[67] = { 4.2). */ 0x02, /* bInterfaceSubClass (Abstract Control Model, CDC section 4.3). */ - 0x01, /* bInterfaceProtocol (AT commands, + 0x00, /* bInterfaceProtocol (No protocol, CDC section 4.4). */ 0), /* iInterface. */ /* Header Functional Descriptor (CDC section 5.2.3).*/ diff --git a/vna_math.c b/vna_math.c new file mode 100644 index 0000000..19c8a4d --- /dev/null +++ b/vna_math.c @@ -0,0 +1,795 @@ +/* + * Copyright (c) 2019-2021, 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 "nanovna.h" +#include + +// Use table increase transform speed, but increase code size +// Use compact table, need 1/4 code size, and not decrease speed +// Used only if not defined __VNA_USE_MATH_TABLES__ (use self table for TTF or direct sin/cos calculations) +#define FFT_USE_SIN_COS_TABLE + +// Use sin table and interpolation for sin/sos calculations +#ifdef __VNA_USE_MATH_TABLES__ +// Use 512 table for calculation sin/cos value, also use this table for FFT +#define FAST_MATH_TABLE_SIZE 512 + +// Not use high part of table +#define GET_SIN_TABLE(idx) (((idx) < 256) ? sin_table_512[(idx)] : -sin_table_512[(idx)-256]) + +static const float sin_table_512[FAST_MATH_TABLE_SIZE/2 + 1] = { + /* + * float has about 7.2 digits of precision + for (int i = 0; i < FAST_MATH_TABLE_SIZE; i++) { + printf("% .8f,%c", sin(2 * M_PI * i / FAST_MATH_TABLE_SIZE), i % 8 == 7 ? '\n' : ' '); + } + */ + 0.00000000f, 0.01227154f, 0.02454123f, 0.03680722f, 0.04906767f, 0.06132074f, 0.07356456f, 0.08579731f, + 0.09801714f, 0.11022221f, 0.12241068f, 0.13458071f, 0.14673047f, 0.15885814f, 0.17096189f, 0.18303989f, + 0.19509032f, 0.20711138f, 0.21910124f, 0.23105811f, 0.24298018f, 0.25486566f, 0.26671276f, 0.27851969f, + 0.29028468f, 0.30200595f, 0.31368174f, 0.32531029f, 0.33688985f, 0.34841868f, 0.35989504f, 0.37131719f, + 0.38268343f, 0.39399204f, 0.40524131f, 0.41642956f, 0.42755509f, 0.43861624f, 0.44961133f, 0.46053871f, + 0.47139674f, 0.48218377f, 0.49289819f, 0.50353838f, 0.51410274f, 0.52458968f, 0.53499762f, 0.54532499f, + 0.55557023f, 0.56573181f, 0.57580819f, 0.58579786f, 0.59569930f, 0.60551104f, 0.61523159f, 0.62485949f, + 0.63439328f, 0.64383154f, 0.65317284f, 0.66241578f, 0.67155895f, 0.68060100f, 0.68954054f, 0.69837625f, + 0.70710678f, 0.71573083f, 0.72424708f, 0.73265427f, 0.74095113f, 0.74913639f, 0.75720885f, 0.76516727f, + 0.77301045f, 0.78073723f, 0.78834643f, 0.79583690f, 0.80320753f, 0.81045720f, 0.81758481f, 0.82458930f, + 0.83146961f, 0.83822471f, 0.84485357f, 0.85135519f, 0.85772861f, 0.86397286f, 0.87008699f, 0.87607009f, + 0.88192126f, 0.88763962f, 0.89322430f, 0.89867447f, 0.90398929f, 0.90916798f, 0.91420976f, 0.91911385f, + 0.92387953f, 0.92850608f, 0.93299280f, 0.93733901f, 0.94154407f, 0.94560733f, 0.94952818f, 0.95330604f, + 0.95694034f, 0.96043052f, 0.96377607f, 0.96697647f, 0.97003125f, 0.97293995f, 0.97570213f, 0.97831737f, + 0.98078528f, 0.98310549f, 0.98527764f, 0.98730142f, 0.98917651f, 0.99090264f, 0.99247953f, 0.99390697f, + 0.99518473f, 0.99631261f, 0.99729046f, 0.99811811f, 0.99879546f, 0.99932238f, 0.99969882f, 0.99992470f, + 1.00000000f, 0.99992470f, 0.99969882f, 0.99932238f, 0.99879546f, 0.99811811f, 0.99729046f, 0.99631261f, + 0.99518473f, 0.99390697f, 0.99247953f, 0.99090264f, 0.98917651f, 0.98730142f, 0.98527764f, 0.98310549f, + 0.98078528f, 0.97831737f, 0.97570213f, 0.97293995f, 0.97003125f, 0.96697647f, 0.96377607f, 0.96043052f, + 0.95694034f, 0.95330604f, 0.94952818f, 0.94560733f, 0.94154407f, 0.93733901f, 0.93299280f, 0.92850608f, + 0.92387953f, 0.91911385f, 0.91420976f, 0.90916798f, 0.90398929f, 0.89867447f, 0.89322430f, 0.88763962f, + 0.88192126f, 0.87607009f, 0.87008699f, 0.86397286f, 0.85772861f, 0.85135519f, 0.84485357f, 0.83822471f, + 0.83146961f, 0.82458930f, 0.81758481f, 0.81045720f, 0.80320753f, 0.79583690f, 0.78834643f, 0.78073723f, + 0.77301045f, 0.76516727f, 0.75720885f, 0.74913639f, 0.74095113f, 0.73265427f, 0.72424708f, 0.71573083f, + 0.70710678f, 0.69837625f, 0.68954054f, 0.68060100f, 0.67155895f, 0.66241578f, 0.65317284f, 0.64383154f, + 0.63439328f, 0.62485949f, 0.61523159f, 0.60551104f, 0.59569930f, 0.58579786f, 0.57580819f, 0.56573181f, + 0.55557023f, 0.54532499f, 0.53499762f, 0.52458968f, 0.51410274f, 0.50353838f, 0.49289819f, 0.48218377f, + 0.47139674f, 0.46053871f, 0.44961133f, 0.43861624f, 0.42755509f, 0.41642956f, 0.40524131f, 0.39399204f, + 0.38268343f, 0.37131719f, 0.35989504f, 0.34841868f, 0.33688985f, 0.32531029f, 0.31368174f, 0.30200595f, + 0.29028468f, 0.27851969f, 0.26671276f, 0.25486566f, 0.24298018f, 0.23105811f, 0.21910124f, 0.20711138f, + 0.19509032f, 0.18303989f, 0.17096189f, 0.15885814f, 0.14673047f, 0.13458071f, 0.12241068f, 0.11022221f, + 0.09801714f, 0.08579731f, 0.07356456f, 0.06132074f, 0.04906767f, 0.03680722f, 0.02454123f, 0.01227154f, + 0.00000000f, + /* + -0.01227154f,-0.02454123f,-0.03680722f,-0.04906767f,-0.06132074f,-0.07356456f,-0.08579731f, + -0.09801714f,-0.11022221f,-0.12241068f,-0.13458071f,-0.14673047f,-0.15885814f,-0.17096189f,-0.18303989f, + -0.19509032f,-0.20711138f,-0.21910124f,-0.23105811f,-0.24298018f,-0.25486566f,-0.26671276f,-0.27851969f, + -0.29028468f,-0.30200595f,-0.31368174f,-0.32531029f,-0.33688985f,-0.34841868f,-0.35989504f,-0.37131719f, + -0.38268343f,-0.39399204f,-0.40524131f,-0.41642956f,-0.42755509f,-0.43861624f,-0.44961133f,-0.46053871f, + -0.47139674f,-0.48218377f,-0.49289819f,-0.50353838f,-0.51410274f,-0.52458968f,-0.53499762f,-0.54532499f, + -0.55557023f,-0.56573181f,-0.57580819f,-0.58579786f,-0.59569930f,-0.60551104f,-0.61523159f,-0.62485949f, + -0.63439328f,-0.64383154f,-0.65317284f,-0.66241578f,-0.67155895f,-0.68060100f,-0.68954054f,-0.69837625f, + -0.70710678f,-0.71573083f,-0.72424708f,-0.73265427f,-0.74095113f,-0.74913639f,-0.75720885f,-0.76516727f, + -0.77301045f,-0.78073723f,-0.78834643f,-0.79583690f,-0.80320753f,-0.81045720f,-0.81758481f,-0.82458930f, + -0.83146961f,-0.83822471f,-0.84485357f,-0.85135519f,-0.85772861f,-0.86397286f,-0.87008699f,-0.87607009f, + -0.88192126f,-0.88763962f,-0.89322430f,-0.89867447f,-0.90398929f,-0.90916798f,-0.91420976f,-0.91911385f, + -0.92387953f,-0.92850608f,-0.93299280f,-0.93733901f,-0.94154407f,-0.94560733f,-0.94952818f,-0.95330604f, + -0.95694034f,-0.96043052f,-0.96377607f,-0.96697647f,-0.97003125f,-0.97293995f,-0.97570213f,-0.97831737f, + -0.98078528f,-0.98310549f,-0.98527764f,-0.98730142f,-0.98917651f,-0.99090264f,-0.99247953f,-0.99390697f, + -0.99518473f,-0.99631261f,-0.99729046f,-0.99811811f,-0.99879546f,-0.99932238f,-0.99969882f,-0.99992470f, + -1.00000000f,-0.99992470f,-0.99969882f,-0.99932238f,-0.99879546f,-0.99811811f,-0.99729046f,-0.99631261f, + -0.99518473f,-0.99390697f,-0.99247953f,-0.99090264f,-0.98917651f,-0.98730142f,-0.98527764f,-0.98310549f, + -0.98078528f,-0.97831737f,-0.97570213f,-0.97293995f,-0.97003125f,-0.96697647f,-0.96377607f,-0.96043052f, + -0.95694034f,-0.95330604f,-0.94952818f,-0.94560733f,-0.94154407f,-0.93733901f,-0.93299280f,-0.92850608f, + -0.92387953f,-0.91911385f,-0.91420976f,-0.90916798f,-0.90398929f,-0.89867447f,-0.89322430f,-0.88763962f, + -0.88192126f,-0.87607009f,-0.87008699f,-0.86397286f,-0.85772861f,-0.85135519f,-0.84485357f,-0.83822471f, + -0.83146961f,-0.82458930f,-0.81758481f,-0.81045720f,-0.80320753f,-0.79583690f,-0.78834643f,-0.78073723f, + -0.77301045f,-0.76516727f,-0.75720885f,-0.74913639f,-0.74095113f,-0.73265427f,-0.72424708f,-0.71573083f, + -0.70710678f,-0.69837625f,-0.68954054f,-0.68060100f,-0.67155895f,-0.66241578f,-0.65317284f,-0.64383154f, + -0.63439328f,-0.62485949f,-0.61523159f,-0.60551104f,-0.59569930f,-0.58579786f,-0.57580819f,-0.56573181f, + -0.55557023f,-0.54532499f,-0.53499762f,-0.52458968f,-0.51410274f,-0.50353838f,-0.49289819f,-0.48218377f, + -0.47139674f,-0.46053871f,-0.44961133f,-0.43861624f,-0.42755509f,-0.41642956f,-0.40524131f,-0.39399204f, + -0.38268343f,-0.37131719f,-0.35989504f,-0.34841868f,-0.33688985f,-0.32531029f,-0.31368174f,-0.30200595f, + -0.29028468f,-0.27851969f,-0.26671276f,-0.25486566f,-0.24298018f,-0.23105811f,-0.21910124f,-0.20711138f, + -0.19509032f,-0.18303989f,-0.17096189f,-0.15885814f,-0.14673047f,-0.13458071f,-0.12241068f,-0.11022221f, + -0.09801714f,-0.08579731f,-0.07356456f,-0.06132074f,-0.04906767f,-0.03680722f,-0.02454123f,-0.01227154f, + -0.00000000f*/ +}; +// +#if FFT_SIZE == 256 +#define FFT_SIN(i) sin_table_512[ 2*(i)] +#define FFT_COS(i) ((i) > 64 ?-sin_table_512[2*(i)-128] : sin_table_512[128-2*(i)]) +#elif FFT_SIZE == 512 +#define FFT_SIN(i) sin_table_512[ (i)] +#define FFT_COS(i) ((i) > 128 ?-sin_table_512[ (i)-128] : sin_table_512[128- (i)]) +#else +#error "Need use bigger sin/cos table for new FFT size" +#endif + +#else +#ifdef FFT_USE_SIN_COS_TABLE +#if FFT_SIZE == 256 +static const float sin_table_256[] = { + /* + * float has about 7.2 digits of precision + for (uint8_t i = 0; i < FFT_SIZE - (FFT_SIZE / 4); i++) { + printf("% .8f,%c", sin(2 * M_PI * i / FFT_SIZE), i % 8 == 7 ? '\n' : ' '); + } + */ + // for FFT_SIZE = 256 + 0.00000000, 0.02454123, 0.04906767, 0.07356456, 0.09801714, 0.12241068, 0.14673047, 0.17096189, + 0.19509032, 0.21910124, 0.24298018, 0.26671276, 0.29028468, 0.31368174, 0.33688985, 0.35989504, + 0.38268343, 0.40524131, 0.42755509, 0.44961133, 0.47139674, 0.49289819, 0.51410274, 0.53499762, + 0.55557023, 0.57580819, 0.59569930, 0.61523159, 0.63439328, 0.65317284, 0.67155895, 0.68954054, + 0.70710678, 0.72424708, 0.74095113, 0.75720885, 0.77301045, 0.78834643, 0.80320753, 0.81758481, + 0.83146961, 0.84485357, 0.85772861, 0.87008699, 0.88192126, 0.89322430, 0.90398929, 0.91420976, + 0.92387953, 0.93299280, 0.94154407, 0.94952818, 0.95694034, 0.96377607, 0.97003125, 0.97570213, + 0.98078528, 0.98527764, 0.98917651, 0.99247953, 0.99518473, 0.99729046, 0.99879546, 0.99969882, + 1.00000000,/* 0.99969882, 0.99879546, 0.99729046, 0.99518473, 0.99247953, 0.98917651, 0.98527764, + 0.98078528, 0.97570213, 0.97003125, 0.96377607, 0.95694034, 0.94952818, 0.94154407, 0.93299280, + 0.92387953, 0.91420976, 0.90398929, 0.89322430, 0.88192126, 0.87008699, 0.85772861, 0.84485357, + 0.83146961, 0.81758481, 0.80320753, 0.78834643, 0.77301045, 0.75720885, 0.74095113, 0.72424708, + 0.70710678, 0.68954054, 0.67155895, 0.65317284, 0.63439328, 0.61523159, 0.59569930, 0.57580819, + 0.55557023, 0.53499762, 0.51410274, 0.49289819, 0.47139674, 0.44961133, 0.42755509, 0.40524131, + 0.38268343, 0.35989504, 0.33688985, 0.31368174, 0.29028468, 0.26671276, 0.24298018, 0.21910124, + 0.19509032, 0.17096189, 0.14673047, 0.12241068, 0.09801714, 0.07356456, 0.04906767, 0.02454123, + 0.00000000, -0.02454123, -0.04906767, -0.07356456, -0.09801714, -0.12241068, -0.14673047, -0.17096189, + -0.19509032, -0.21910124, -0.24298018, -0.26671276, -0.29028468, -0.31368174, -0.33688985, -0.35989504, + -0.38268343, -0.40524131, -0.42755509, -0.44961133, -0.47139674, -0.49289819, -0.51410274, -0.53499762, + -0.55557023, -0.57580819, -0.59569930, -0.61523159, -0.63439328, -0.65317284, -0.67155895, -0.68954054, + -0.70710678, -0.72424708, -0.74095113, -0.75720885, -0.77301045, -0.78834643, -0.80320753, -0.81758481, + -0.83146961, -0.84485357, -0.85772861, -0.87008699, -0.88192126, -0.89322430, -0.90398929, -0.91420976, + -0.92387953, -0.93299280, -0.94154407, -0.94952818, -0.95694034, -0.96377607, -0.97003125, -0.97570213, + -0.98078528, -0.98527764, -0.98917651, -0.99247953, -0.99518473, -0.99729046, -0.99879546, -0.99969882,*/ +}; +// full size table: +// sin = sin_table_256[i ] +// cos = sin_table_256[i+64] +//#define FFT_SIN(i) sin_table_256[(i)] +//#define FFT_COS(i) sin_table_256[(i)+64] + +// for size use only 0-64 indexes +// sin = i > 64 ? sin_table_256[128-i] : sin_table_256[ i]; +// cos = i > 64 ?-sin_table_256[ i-64] : sin_table_256[64-i]; +#define FFT_SIN(i) ((i) > 64 ? sin_table_256[128-(i)] : sin_table_256[ (i)]) +#define FFT_COS(i) ((i) > 64 ?-sin_table_256[ (i)-64] : sin_table_256[64-(i)]) + +#elif FFT_SIZE == 512 +static const float sin_table_512[] = { + /* + * float has about 7.2 digits of precision + for (int i = 0; i < FFT_SIZE - (FFT_SIZE / 4); i++) { + printf("% .8f,%c", sin(2 * M_PI * i / FFT_SIZE), i % 8 == 7 ? '\n' : ' '); + } + */ + // For FFT_SIZE = 512 + 0.00000000, 0.01227154, 0.02454123, 0.03680722, 0.04906767, 0.06132074, 0.07356456, 0.08579731, + 0.09801714, 0.11022221, 0.12241068, 0.13458071, 0.14673047, 0.15885814, 0.17096189, 0.18303989, + 0.19509032, 0.20711138, 0.21910124, 0.23105811, 0.24298018, 0.25486566, 0.26671276, 0.27851969, + 0.29028468, 0.30200595, 0.31368174, 0.32531029, 0.33688985, 0.34841868, 0.35989504, 0.37131719, + 0.38268343, 0.39399204, 0.40524131, 0.41642956, 0.42755509, 0.43861624, 0.44961133, 0.46053871, + 0.47139674, 0.48218377, 0.49289819, 0.50353838, 0.51410274, 0.52458968, 0.53499762, 0.54532499, + 0.55557023, 0.56573181, 0.57580819, 0.58579786, 0.59569930, 0.60551104, 0.61523159, 0.62485949, + 0.63439328, 0.64383154, 0.65317284, 0.66241578, 0.67155895, 0.68060100, 0.68954054, 0.69837625, + 0.70710678, 0.71573083, 0.72424708, 0.73265427, 0.74095113, 0.74913639, 0.75720885, 0.76516727, + 0.77301045, 0.78073723, 0.78834643, 0.79583690, 0.80320753, 0.81045720, 0.81758481, 0.82458930, + 0.83146961, 0.83822471, 0.84485357, 0.85135519, 0.85772861, 0.86397286, 0.87008699, 0.87607009, + 0.88192126, 0.88763962, 0.89322430, 0.89867447, 0.90398929, 0.90916798, 0.91420976, 0.91911385, + 0.92387953, 0.92850608, 0.93299280, 0.93733901, 0.94154407, 0.94560733, 0.94952818, 0.95330604, + 0.95694034, 0.96043052, 0.96377607, 0.96697647, 0.97003125, 0.97293995, 0.97570213, 0.97831737, + 0.98078528, 0.98310549, 0.98527764, 0.98730142, 0.98917651, 0.99090264, 0.99247953, 0.99390697, + 0.99518473, 0.99631261, 0.99729046, 0.99811811, 0.99879546, 0.99932238, 0.99969882, 0.99992470, + 1.00000000,/* 0.99992470, 0.99969882, 0.99932238, 0.99879546, 0.99811811, 0.99729046, 0.99631261, + 0.99518473, 0.99390697, 0.99247953, 0.99090264, 0.98917651, 0.98730142, 0.98527764, 0.98310549, + 0.98078528, 0.97831737, 0.97570213, 0.97293995, 0.97003125, 0.96697647, 0.96377607, 0.96043052, + 0.95694034, 0.95330604, 0.94952818, 0.94560733, 0.94154407, 0.93733901, 0.93299280, 0.92850608, + 0.92387953, 0.91911385, 0.91420976, 0.90916798, 0.90398929, 0.89867447, 0.89322430, 0.88763962, + 0.88192126, 0.87607009, 0.87008699, 0.86397286, 0.85772861, 0.85135519, 0.84485357, 0.83822471, + 0.83146961, 0.82458930, 0.81758481, 0.81045720, 0.80320753, 0.79583690, 0.78834643, 0.78073723, + 0.77301045, 0.76516727, 0.75720885, 0.74913639, 0.74095113, 0.73265427, 0.72424708, 0.71573083, + 0.70710678, 0.69837625, 0.68954054, 0.68060100, 0.67155895, 0.66241578, 0.65317284, 0.64383154, + 0.63439328, 0.62485949, 0.61523159, 0.60551104, 0.59569930, 0.58579786, 0.57580819, 0.56573181, + 0.55557023, 0.54532499, 0.53499762, 0.52458968, 0.51410274, 0.50353838, 0.49289819, 0.48218377, + 0.47139674, 0.46053871, 0.44961133, 0.43861624, 0.42755509, 0.41642956, 0.40524131, 0.39399204, + 0.38268343, 0.37131719, 0.35989504, 0.34841868, 0.33688985, 0.32531029, 0.31368174, 0.30200595, + 0.29028468, 0.27851969, 0.26671276, 0.25486566, 0.24298018, 0.23105811, 0.21910124, 0.20711138, + 0.19509032, 0.18303989, 0.17096189, 0.15885814, 0.14673047, 0.13458071, 0.12241068, 0.11022221, + 0.09801714, 0.08579731, 0.07356456, 0.06132074, 0.04906767, 0.03680722, 0.02454123, 0.01227154, + 0.00000000, -0.01227154, -0.02454123, -0.03680722, -0.04906767, -0.06132074, -0.07356456, -0.08579731, + -0.09801714, -0.11022221, -0.12241068, -0.13458071, -0.14673047, -0.15885814, -0.17096189, -0.18303989, + -0.19509032, -0.20711138, -0.21910124, -0.23105811, -0.24298018, -0.25486566, -0.26671276, -0.27851969, + -0.29028468, -0.30200595, -0.31368174, -0.32531029, -0.33688985, -0.34841868, -0.35989504, -0.37131719, + -0.38268343, -0.39399204, -0.40524131, -0.41642956, -0.42755509, -0.43861624, -0.44961133, -0.46053871, + -0.47139674, -0.48218377, -0.49289819, -0.50353838, -0.51410274, -0.52458968, -0.53499762, -0.54532499, + -0.55557023, -0.56573181, -0.57580819, -0.58579786, -0.59569930, -0.60551104, -0.61523159, -0.62485949, + -0.63439328, -0.64383154, -0.65317284, -0.66241578, -0.67155895, -0.68060100, -0.68954054, -0.69837625, + -0.70710678, -0.71573083, -0.72424708, -0.73265427, -0.74095113, -0.74913639, -0.75720885, -0.76516727, + -0.77301045, -0.78073723, -0.78834643, -0.79583690, -0.80320753, -0.81045720, -0.81758481, -0.82458930, + -0.83146961, -0.83822471, -0.84485357, -0.85135519, -0.85772861, -0.86397286, -0.87008699, -0.87607009, + -0.88192126, -0.88763962, -0.89322430, -0.89867447, -0.90398929, -0.90916798, -0.91420976, -0.91911385, + -0.92387953, -0.92850608, -0.93299280, -0.93733901, -0.94154407, -0.94560733, -0.94952818, -0.95330604, + -0.95694034, -0.96043052, -0.96377607, -0.96697647, -0.97003125, -0.97293995, -0.97570213, -0.97831737, + -0.98078528, -0.98310549, -0.98527764, -0.98730142, -0.98917651, -0.99090264, -0.99247953, -0.99390697, + -0.99518473, -0.99631261, -0.99729046, -0.99811811, -0.99879546, -0.99932238, -0.99969882, -0.99992470*/ +}; +// full size table: +// sin = sin_table_512[i ] +// cos = sin_table_512[i+128] +//#define FFT_SIN(i) sin_table_512[(i) ] +//#define FFT_COS(i) sin_table_512[(i)+128] + +// for size use only 0-128 indexes +// sin = i > 128 ? sin_table_512[256-i] : sin_table_512[ i]; +// cos = i > 128 ?-sin_table_512[i-128] : sin_table_512[128-i]; +#define FFT_SIN(i) ((i) > 128 ? sin_table_512[256-(i)] : sin_table_512[ (i)]) +#define FFT_COS(i) ((i) > 128 ?-sin_table_512[(i)-128] : sin_table_512[128-(i)]) + +#else +#error "Need build table for new FFT size" +#endif + +#else +// Not use FFT_USE_SIN_COS_TABLE, use direct sin/cos calculations +#define FFT_SIN(k) sinf((2 * VNA_PI / FFT_SIZE) * (k)) +#define FFT_COS(k) cosf((2 * VNA_PI / FFT_SIZE) * (k)); + +#endif // FFT_USE_SIN_COS_TABLE + +#endif // __VNA_USE_MATH_TABLES__ + +static uint16_t reverse_bits(uint16_t x, int n) { + uint16_t result = 0; + int i; + for (i = 0; i < n; i++, x >>= 1) + result = (result << 1) | (x & 1U); + return result; +} + +/*** + * dir = forward: 0, inverse: 1 + * https://www.nayuki.io/res/free-small-fft-in-multiple-languages/fft.c + */ +void fft(float array[][2], const uint8_t dir) { +// FFT_SIZE = 2^FFT_N +#if FFT_SIZE == 256 + #define FFT_N 8 +#elif FFT_SIZE == 512 + #define FFT_N 9 +#else + #error "Need define FFT_N for this FFT size" +#endif + const uint16_t n = FFT_SIZE; + const uint8_t levels = FFT_N; // log2(n) + + const uint8_t real = dir & 1; + const uint8_t imag = ~real & 1; + uint16_t i; + + for (i = 0; i < n; i++) { + uint16_t j = reverse_bits(i, levels); + if (j > i) { + SWAP(float, array[i][real], array[j][real]); + SWAP(float, array[i][imag], array[j][imag]); + } + } + const uint16_t size = 2; + uint16_t halfsize = size / 2; + uint16_t tablestep = n / size; + uint16_t j, k; + // Cooley-Tukey decimation-in-time radix-2 FFT + for (;tablestep; tablestep>>=1, halfsize<<=1) { + for (i = 0; i < n; i+=2*halfsize) { + for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) { + uint16_t l = j + halfsize; + float s = FFT_SIN(k); + float c = FFT_COS(k); + float tpre = array[l][real] * c + array[l][imag] * s; + float tpim = -array[l][real] * s + array[l][imag] * c; + array[l][real] = array[j][real] - tpre; + array[l][imag] = array[j][imag] - tpim; + array[j][real] += tpre; + array[j][imag] += tpim; + } + } + } +} + +// Return sin/cos value angle in range 0.0 to 1.0 (0 is 0 degree, 1 is 360 degree) +void vna_sincosf(float angle, float * pSinVal, float * pCosVal) +{ +#ifndef __VNA_USE_MATH_TABLES__ + // Use default sin/cos functions + angle *= 2 * VNA_PI; // Convert to rad + *pSinVal = sinf(angle); + *pCosVal = cosf(angle); +#else + const float Dn = 2 * VNA_PI / FAST_MATH_TABLE_SIZE; // delta between the two points in table (fixed); + uint16_t indexS, indexC; // Index variable + float f1, f2, d1, d2; // Two nearest output values + float Df, fract, temp; + + // Round angle to range 0.0 to 1.0 + temp = vna_fabsf(angle); + temp-= (uint32_t)temp;//floorf(temp); + // Scale input from range 0.0 to 1.0 to table size + temp*= FAST_MATH_TABLE_SIZE; + + indexS = temp; + indexC = indexS + (FAST_MATH_TABLE_SIZE / 4); // cosine add 0.25 (pi/2) to read from sine table + // Calculation of fractional value + fract = temp - indexS; + // Align indexes to table + indexS&= (FAST_MATH_TABLE_SIZE-1); + indexC&= (FAST_MATH_TABLE_SIZE-1); + + // Read two nearest values of input value from the cos & sin tables +#if 0 + f1 = GET_SIN_TABLE(indexC ); + f2 = GET_SIN_TABLE(indexC+1); + d1 = GET_SIN_TABLE(indexS ); + d2 = GET_SIN_TABLE(indexS+1); +#else + if (indexC < 256){f1 = sin_table_512[indexC +0];f2 = sin_table_512[indexC +1];} + else {f1 =-sin_table_512[indexC-256+0];f2 =-sin_table_512[indexC-256+1];} + if (indexS < 256){d1 = sin_table_512[indexS +0];d2 = sin_table_512[indexS +1];} + else {d1 =-sin_table_512[indexS-256+0];d2 =-sin_table_512[indexS-256+1];} +#endif + + // Calculation of cosine value + Df = f2 - f1; // delta between the values of the functions + temp = Dn * (d1 + d2) + 2 * Df; + temp = (3 * Df + (d2 + 2 * d1) * Dn) - fract * temp; + temp = fract * temp - d1 * Dn; + *pCosVal = fract * temp + f1; + + // Calculation of sine value + Df = d2 - d1; // delta between the values of the functions + temp = Dn * (f1 + f2) - 2 * Df; + temp = fract * temp + (3 * Df - (f2 + 2 * f1) * Dn); + temp = fract * temp + f1 * Dn; + *pSinVal = fract * temp + d1; + if (angle < 0) + *pSinVal = -*pSinVal; +#endif +} + +//********************************************************************************** +// VNA math +//********************************************************************************** +// Cleanup declarations if used default math.h functions +#undef vna_sqrtf +#undef vna_cbrtf +#undef vna_logf +#undef vna_atanf +#undef vna_atan2f + +//********************************************************************************** +// square root +//********************************************************************************** +#if (__FPU_PRESENT == 0) && (__FPU_USED == 0) +#if 1 +// __ieee754_sqrtf, remove some check (NAN, inf, normalization), small code optimization to arm +float vna_sqrtf(float x) +{ + int32_t ix,s,q,m,t; + uint32_t r; + union {float f; uint32_t i;} u = {x}; + ix = u.i; +#if 0 + // take care of Inf and NaN + if((ix&0x7f800000)==0x7f800000) return x*x+x; // sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN + // take care if x < 0 + if (ix < 0) return (x-x)/0.0f; +#endif + if (ix == 0) return 0.0f; + m = (ix>>23); +#if 0 // + // normalize x + if(m==0) { // subnormal x + for(int i=0;(ix&0x00800000)==0;i++) ix<<=1; + m -= i-1; + } +#endif + m -= 127; // unbias exponent + ix = (ix&0x007fffff)|0x00800000; + // generate sqrt(x) bit by bit + ix<<= (m&1) ? 2 : 1; // odd m, double x to make it even, and after multiple by 2 + m >>= 1; // m = [m/2] + q = s = 0; // q = sqrt(x) + r = 0x01000000; // r = moving bit from right to left + while(r!=0) { + t = s+r; + if(t<=ix) { + s = t+r; + ix -= t; + q += r; + } + ix += ix; + r>>=1; + } + // use floating add to find out rounding direction + if(ix!=0) { + if ((1.0f - 1e-30f) >= 1.0f) // trigger inexact flag. + q += ((1.0f + 1e-30f) > 1.0f) ? 2 : (q&1); + } + ix = (q>>1)+0x3f000000; + ix += (m <<23); + u.i = ix; + return u.f; +} +#else +// Simple implementation, but slow if no FPU used, and not usable if used hardware FPU sqrtf +float vna_sqrtf(float x) +{ + union {float x; uint32_t i;} u = {x}; + u.i = (1<<29) + (u.i >> 1) - (1<<22); + // Two Babylonian Steps (simplified from:) + // u.x = 0.5f * (u.x + x/u.x); + // u.x = 0.5f * (u.x + x/u.x); + u.x = u.x + x/u.x; + u.x = 0.25f*u.x + x/u.x; + + return u.x; +} +#endif +#endif + +//********************************************************************************** +// Cube root +//********************************************************************************** +float vna_cbrtf(float x) +{ +#if 1 + static const uint32_t + B1 = 709958130, // B1 = (127-127.0/3-0.03306235651)*2**23 + B2 = 642849266; // B2 = (127-127.0/3-24/3-0.03306235651)*2**23 + + float r,T; + union {float f; uint32_t i;} u = {x}; + uint32_t hx = u.i & 0x7fffffff; + +// if (hx >= 0x7f800000) // cbrt(NaN,INF) is itself +// return x + x; + // rough cbrtf to 5 bits + if (hx < 0x00800000) { // zero or subnormal? + if (hx == 0) + return x; // cbrt(+-0) is itself + u.f = x*0x1p24f; + hx = u.i & 0x7fffffff; + hx = hx/3 + B2; + } else + hx = hx/3 + B1; + u.i &= 0x80000000; + u.i |= hx; + + // First step Newton iteration (solving t*t-x/t == 0) to 16 bits. + T = u.f; + r = T*T*T; + T*= (x+x+r)/(x+r+r); +// Second step Newton iteration to 47 bits. + r = T*T*T; + T*= (x+x+r)/(x+r+r); + return T; +#else + if (x == 0) { + // would otherwise return something like 4.257959840008151e-109 + return 0; + } + float b = 1.0f; // use any value except 0 + float last_b_1 = 0; + float last_b_2 = 0; + while (last_b_1 != b && last_b_2 != b) { + last_b_1 = b; +// b = (b + x / (b * b)) / 2; + b = (2 * b + x / b / b) / 3; // for small numbers, as suggested by willywonka_dailyblah + last_b_2 = b; +// b = (b + x / (b * b)) / 2; + b = (2 * b + x / b / b) / 3; //for small numbers, as suggested by willywonka_dailyblah + } + return b; +#endif +} + +//********************************************************************************** +// logf +//********************************************************************************** +float vna_logf(float x) +{ + const float MULTIPLIER = logf(2.0f); +#if 0 + // Give up to 0.006 error (2.5x faster original code) + union {float f; int32_t i;} u = {x}; + const int log_2 = ((u.i >> 23) & 255) - 128; + if (u.i <=0) return (x-x)/0.0f; // if <=0 return NAN + u.i = (u.i&0x007FFFFF) + 0x3F800000; + u.f = ((-1.0f/3) * u.f + 2) * u.f - (2.0f/3); // (1) + return (u.f + log_2) * MULTIPLIER; +#elif 1 + // Give up to 0.0001 error (2x faster original code) + // fast log2f approximation, give 0.0004 error + union { float f; uint32_t i; } vx = { x }; + union { uint32_t i; float f; } mx = { (vx.i & 0x007FFFFF) | 0x3f000000 }; + // if <=0 return NAN + if (vx.i <=0) return (x-x)/0.0f; + return vx.i * (MULTIPLIER / (1 << 23)) - (124.22551499f * MULTIPLIER) - (1.498030302f * MULTIPLIER) * mx.f - (1.72587999f * MULTIPLIER) / (0.3520887068f + mx.f); +#else + // use original code (20% faster default) + static const float + ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ + ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ + two25 = 3.355443200e+07, /* 0x4c000000 */ + /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ + Lg1 = 0xaaaaaa.0p-24, /* 0.66666662693 */ + Lg2 = 0xccce13.0p-25, /* 0.40000972152 */ + Lg3 = 0x91e9ee.0p-25, /* 0.28498786688 */ + Lg4 = 0xf89e26.0p-26; /* 0.24279078841 */ + + union {float f; uint32_t i;} u = {x}; + float hfsq,f,s,z,R,w,t1,t2,dk; + uint32_t ix; + int k; + + ix = u.i; + k = 0; + if (ix < 0x00800000 || ix>>31) { /* x < 2**-126 */ + if (ix<<1 == 0) + return -1/(x*x); /* log(+-0)=-inf */ + if (ix>>31) + return (x-x)/0.0f; /* log(-#) = NaN */ + /* subnormal number, scale up x */ + k -= 25; + x *= two25; + u.f = x; + ix = u.i; + } else if (ix >= 0x7f800000) { + return x; + } else if (ix == 0x3f800000) + return 0; + /* reduce x into [sqrt(2)/2, sqrt(2)] */ + ix += 0x3f800000 - 0x3f3504f3; + k += (int)(ix>>23) - 0x7f; + ix = (ix&0x007fffff) + 0x3f3504f3; + u.i = ix; + x = u.f; + f = x - 1.0f; + s = f/(2.0f + f); + z = s*s; + w = z*z; + t1= w*(Lg2+w*Lg4); + t2= z*(Lg1+w*Lg3); + R = t2 + t1; + hfsq = 0.5f * f * f; + dk = k; + return s*(hfsq+R) + dk*ln2_lo - hfsq + f + dk*ln2_hi; +#endif +} + +float vna_log10f_x_10(float x) +{ + const float MULTIPLIER = (10.0f * logf(2.0f) / logf(10.0f)); +#if 0 + // Give up to 0.006 error (2.5x faster original code) + union {float f; int32_t i;} u = {x}; + const int log_2 = ((u.i >> 23) & 255) - 128; + if (u.i <=0) return (x-x)/0.0f; // if <=0 return NAN + u.i = (u.i&0x007FFFFF) + 0x3F800000; + u.f = ((-1.0f/3) * u.f + 2) * u.f - (2.0f/3); // (1) + return (u.f + log_2) * MULTIPLIER; +#else + // Give up to 0.0001 error (2x faster original code) + // fast log2f approximation, give 0.0004 error + union { float f; uint32_t i; } vx = { x }; + union { uint32_t i; float f; } mx = { (vx.i & 0x007FFFFF) | 0x3f000000 }; + // if <=0 return NAN + if (vx.i <=0) return (x-x)/0.0f; + return vx.i * (MULTIPLIER / (1 << 23)) - (124.22551499f * MULTIPLIER) - (1.498030302f * MULTIPLIER) * mx.f - (1.72587999f * MULTIPLIER) / (0.3520887068f + mx.f); +#endif +} +//********************************************************************************** +// atanf +//********************************************************************************** +// __ieee754_atanf +float vna_atanf(float x) +{ + static const float atanhi[] = { + 4.6364760399e-01, // atan(0.5)hi 0x3eed6338 + 7.8539812565e-01, // atan(1.0)hi 0x3f490fda + 9.8279368877e-01, // atan(1.5)hi 0x3f7b985e + 1.5707962513e+00, // atan(inf)hi 0x3fc90fda + }; + static const float atanlo[] = { + 5.0121582440e-09, // atan(0.5)lo 0x31ac3769 + 3.7748947079e-08, // atan(1.0)lo 0x33222168 + 3.4473217170e-08, // atan(1.5)lo 0x33140fb4 + 7.5497894159e-08, // atan(inf)lo 0x33a22168 + }; + static const float aT[] = { + 3.3333328366e-01, + -1.9999158382e-01, + 1.4253635705e-01, + -1.0648017377e-01, + 6.1687607318e-02, + }; + float w,s1,s2,z; + uint32_t ix,sign; + int id; + union {float f; uint32_t i;} u = {x}; + ix = u.i; + sign = ix>>31; + ix &= 0x7fffffff; + if (ix >= 0x4c800000) { /* if |x| >= 2**26 */ + if (ix > 0x7f800000) + return x; + z = atanhi[3] + 0x1p-120f; + return sign ? -z : z; + } + if (ix < 0x3ee00000) { /* |x| < 0.4375 */ + if (ix < 0x39800000) { /* |x| < 2**-12 */ + return x; + } + id = -1; + } else { + x = vna_fabsf(x); + if (ix < 0x3f980000) { /* |x| < 1.1875 */ + if (ix < 0x3f300000) { /* 7/16 <= |x| < 11/16 */ + id = 0; + x = (2.0f*x - 1.0f)/(2.0f + x); + } else { /* 11/16 <= |x| < 19/16 */ + id = 1; + x = (x - 1.0f)/(x + 1.0f); + } + } else { + if (ix < 0x401c0000) { /* |x| < 2.4375 */ + id = 2; + x = (x - 1.5f)/(1.0f + 1.5f*x); + } else { /* 2.4375 <= |x| < 2**26 */ + id = 3; + x = -1.0f/x; + } + } + } + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*aT[4])); + s2 = w*(aT[1]+w*aT[3]); + if (id < 0) + return x - x*(s1+s2); + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return sign ? -z : z; +} + +//********************************************************************************** +// atan2f +//********************************************************************************** +#if 0 +// __ieee754_atan2f +float vna_atan2f(float y, float x) +{ + static const float pi = 3.1415927410e+00; // 0x40490fdb + static const float pi_lo =-8.7422776573e-08; // 0xb3bbbd2e + float z; + uint32_t m,ix,iy; + union {float f; uint32_t i;} ux = {x}; + union {float f; uint32_t i;} uy = {y}; + ix = ux.i; + iy = uy.i; + + if (ix == 0x3f800000) /* x=1.0 */ + return vna_atanf(y); + m = ((iy>>31)&1) | ((ix>>30)&2); /* 2*sign(x)+sign(y) */ + ix &= 0x7fffffff; + iy &= 0x7fffffff; + + /* when y = 0 */ + if (iy == 0) { + switch (m) { + case 0: + case 1: return y; // atan(+-0,+anything)=+-0 + case 2: return pi; // atan(+0,-anything) = pi + case 3: return -pi; // atan(-0,-anything) =-pi + } + } + /* when x = 0 */ + if (ix == 0) + return m&1 ? -pi/2 : pi/2; + /* when x is INF */ + if (ix == 0x7f800000) { + if (iy == 0x7f800000) { + switch (m) { + case 0: return pi/4; /* atan(+INF,+INF) */ + case 1: return -pi/4; /* atan(-INF,+INF) */ + case 2: return 3*pi/4; /*atan(+INF,-INF)*/ + case 3: return -3*pi/4; /*atan(-INF,-INF)*/ + } + } else { + switch (m) { + case 0: return 0.0f; /* atan(+...,+INF) */ + case 1: return -0.0f; /* atan(-...,+INF) */ + case 2: return pi; /* atan(+...,-INF) */ + case 3: return -pi; /* atan(-...,-INF) */ + } + } + } + /* |y/x| > 0x1p26 */ + if (ix+(26<<23) < iy || iy == 0x7f800000) + return m&1 ? -pi/2 : pi/2; + + /* z = atan(|y/x|) with correct underflow */ + if ((m&2) && iy+(26<<23) < ix) /*|y/x| < 0x1p-26, x < 0 */ + z = 0.0; + else + z = vna_atanf(vna_fabsf(y/x)); + switch (m) { + case 0: return z; /* atan(+,+) */ + case 1: return -z; /* atan(-,+) */ + case 2: return pi - (z-pi_lo); /* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo) - pi; /* atan(-,-) */ + } +} +#else +// Polynomial approximation to atan2f +float vna_atan2f (float y, float x) +{ + union {float f; int32_t i;} ux = {x}; + union {float f; int32_t i;} uy = {y}; + if (ux.i == 0 && uy.i == 0) + return 0.0f; + + float ax, ay, r, a, s; + ax = vna_fabsf(x); + ay = vna_fabsf(y); + a = (ay < ax) ? ay / ax : ax / ay; + s = a * a; + // Polynomial approximation to atan(a) on [0,1] +#if 0 + // give 0.31 degree error + r = 0.970562748477141f - 0.189514164974601f * s; +#elif 1 + // give 0.04 degree error + r = 0.994949366116654f - s * (0.287060635532652f - 0.078037176446441f * s); +#else + // give 0.005 degree error + r = 0.999133448222780f - s * (0.320533292381664f - s * (0.144982490144465f - s * 0.038254464970299f)); +#endif + // Map to full circle + r*=a; + if (ay > ax) r = 1.57079637f - r; + if (ux.i < 0) r = 3.14159274f - r; + if (uy.i < 0) r = -r; + return r; +} +#endif + +//********************************************************************************** +// Fast expf approximation +//********************************************************************************** +float vna_expf(float x) +{ + union { float f; int32_t i; } v; + v.i = (int32_t)(12102203.0f*x) + 0x3F800000; + int32_t m = (v.i >> 7) & 0xFFFF; // copy mantissa +#if 1 + // cubic spline approximation, empirical values for small maximum relative error (8.34e-5): + v.i += ((((((((1277*m) >> 14) + 14825)*m) >> 14) - 79749)*m) >> 11) - 626; +#else + // quartic spline approximation, empirical values for small maximum relative error (1.21e-5): + v.i += (((((((((((3537*m) >> 16) + 13668)*m) >> 18) + 15817)*m) >> 14) - 80470)*m) >> 11); +#endif + return v.f; +} diff --git a/vna_math.h b/vna_math.h new file mode 100644 index 0000000..b5e967f --- /dev/null +++ b/vna_math.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019-2021, 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. + */ +#ifndef __VNA_MATH_H +#define __VNA_MATH_H +// Use math.h functions if need +#include + +#ifndef __FPU_PRESENT +#define __FPU_PRESENT 0 +#endif +#ifndef __FPU_USED +#define __FPU_USED 0 +#endif + +// VNA math used library +#ifdef __USE_VNA_MATH__ +// Some functions implemented in hardware FPU +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) +__attribute__((always_inline)) __STATIC_INLINE float vna_fabsf(float x){__asm__ ("vabs.f32 %0, %1" : "=t"(x) : "t"(x)); return x;} +__attribute__((always_inline)) __STATIC_INLINE float vna_sqrtf(float x){__asm__ ("vsqrt.f32 %0, %1" : "=t"(x) : "t"(x)); return x;} +__attribute__((always_inline)) __STATIC_INLINE float vna_fmaf(float x, float y, float z){__asm__ ("vfma.f32 %0, %1, %2" : "+t"(z) : "t"(x), "t"(y)); return z;} + +#else +// Define inline functions +__attribute__((always_inline)) __STATIC_INLINE float vna_fabsf(float x){union {float f; uint32_t i;} u = {x}; u.i &= 0x7fffffff; return u.f;} +__attribute__((always_inline)) __STATIC_INLINE float vna_fmaf(float x, float y, float z){return z+x*y;} +// square root +float vna_sqrtf(float x); +#endif +//================================ +// log +float vna_logf(float x); +float vna_log10f_x_10(float x); +// atan +float vna_atanf(float x); +float vna_atan2f(float x, float y); +#else +// Use defaults math functions +#define vna_fabsf fabsf +#define vna_sqrtf sqrtf +#define vna_logf logf +#define vna_log10f_x_10 (logf(x) * (10.0f / logf(10.0f))) +#define vna_atanf atanf +#define vna_atan2f atan2f +#endif + +// fft +void fft(float array[][2], const uint8_t dir); +#define fft_forward(array) fft(array, 0) +#define fft_inverse(array) fft(array, 1) + +// cube root +float vna_cbrtf(float x); + +// Return sin/cos value, angle have range 0.0 to 1.0 (0 is 0 degree, 1 is 360 degree) +void vna_sincosf(float angle, float * pSinVal, float * pCosVal); + +#endif