diff --git a/Makefile b/Makefile index 937701d..911000e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ # compile options (see Readme.md) -# '0' = disable, 1 = enable +# '0' = disable +# '1' = enable +# ENABLE_SWD := 0 ENABLE_OVERLAY := 1 ENABLE_UART := 1 @@ -19,6 +21,7 @@ ENABLE_BOOT_BEEPS := 0 ENABLE_COMPANDER := 1 ENABLE_SHOW_CHARGE_LEVEL := 0 ENABLE_REVERSE_BAT_SYMBOL := 1 +ENABLE_AUDIO_BAR := 0 #ENABLE_SINGLE_VFO_CHAN := 1 #ENABLE_BAND_SCOPE := 1 @@ -127,7 +130,9 @@ LD = arm-none-eabi-gcc OBJCOPY = arm-none-eabi-objcopy SIZE = arm-none-eabi-size +# the user might not have/want git installed GIT_HASH := $(shell git rev-parse --short HEAD) +$(info GIT_HASH = $(GIT_HASH)) ASFLAGS = -c -mcpu=cortex-m0 ifeq ($(ENABLE_OVERLAY),1) @@ -135,7 +140,6 @@ ifeq ($(ENABLE_OVERLAY),1) endif CFLAGS = -Os -Wall -Werror -mcpu=cortex-m0 -fno-builtin -fshort-enums -fno-delete-null-pointer-checks -std=c11 -MMD -#CFLAGS = -Os -Wall -Werror -mcpu=cortex-m0 -fno-builtin -fshort-enums -fno-delete-null-pointer-checks -std=c11 -MMD -fdata-sections -ffunction-sections CFLAGS += -DPRINTF_INCLUDE_CONFIG_H CFLAGS += -DGIT_HASH=\"$(GIT_HASH)\" ifeq ($(ENABLE_SWD),1) @@ -192,6 +196,9 @@ endif ifeq ($(ENABLE_REVERSE_BAT_SYMBOL),1) CFLAGS += -DENABLE_REVERSE_BAT_SYMBOL endif +ifeq ($(ENABLE_AUDIO_BAR),1) + CFLAGS += -DENABLE_AUDIO_BAR +endif ifeq ($(ENABLE_SINGLE_VFO_CHAN),1) CFLAGS += -DENABLE_SINGLE_VFO_CHAN endif @@ -200,7 +207,6 @@ ifeq ($(ENABLE_BAND_SCOPE),1) endif LDFLAGS = -mcpu=cortex-m0 -nostartfiles -Wl,-T,firmware.ld -#LDFLAGS = -mcpu=cortex-m0 -nostartfiles -Wl,-T,firmware.ld,--gc-sections ifeq ($(DEBUG),1) ASFLAGS += -g @@ -219,8 +225,8 @@ DEPS = $(OBJS:.o=.d) all: $(TARGET) $(OBJCOPY) -O binary $< $<.bin -# -python fw-pack.py $<.bin $(GIT_HASH) $<.packed.bin -# -python3 fw-pack.py $<.bin $(GIT_HASH) $<.packed.bin + -python fw-pack.py $<.bin $(GIT_HASH) $<.packed.bin + -python3 fw-pack.py $<.bin $(GIT_HASH) $<.packed.bin $(SIZE) $< debug: diff --git a/README.md b/README.md index fbf0bf3..8bcbe29 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ ENABLE_BOOT_BEEPS := 0 give user audio feedback on volume knob ENABLE_COMPANDER := 1 compander option (per channel) ENABLE_SHOW_CHARGE_LEVEL := 1 show the charge level when the radio is on charge ENABLE_REVERSE_BAT_SYMBOL := 1 mirror the battery symbol on the status bar (+ pole on the right) +ENABLE_AUDIO_BAR := 0 experimentle, display an audo bar level when TX'ing #ENABLE_SINGLE_VFO_CHAN := 1 not yet implemented - single VFO on display when possible #ENABLE_BAND_SCOPE := 1 not yet implemented - spectrum/pan-adapter ``` @@ -67,7 +68,8 @@ To compile directly in windows without the need of a linux virtual machine: ``` 1. Download and install "gcc-arm-none-eabi-10.3-2021.10-win32.exe" from https://developer.arm.com/downloads/-/gnu-rm 2. Download and install "gnu_make-3.81.exe" from https://gnuwin32.sourceforge.net/packages/make.htm -3. You may (or may not) need to reboot your PC after installing the above +3. You may (or not) need to manualy add gcc path to you OS environment PATH, ie C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2021.10\bin +4. You may (or not) need to reboot your PC after installing the above ``` Then you can run 'win_make.bat' from the directory you saved this source code too. diff --git a/app/app.c b/app/app.c index 86fce6b..9337268 100644 --- a/app/app.c +++ b/app/app.c @@ -56,6 +56,7 @@ #endif #include "ui/battery.h" #include "ui/inputbox.h" +#include "ui/main.h" #include "ui/menu.h" #include "ui/rssi.h" #include "ui/status.h" @@ -389,6 +390,9 @@ static void APP_HandleFunction(void) if (!gRxIdleMode) APP_CheckForIncoming(); break; + + case FUNCTION_BAND_SCOPE: + break; } } @@ -1515,16 +1519,33 @@ void APP_TimeSlice10ms(void) APP_CheckRadioInterrupts(); if (gCurrentFunction != FUNCTION_TRANSMIT) - { + { // receiving if (gUpdateStatus) UI_DisplayStatus(false); if (gUpdateDisplay) { - GUI_DisplayScreen(); gUpdateDisplay = false; + GUI_DisplayScreen(); } } + else + { // transmitting + #ifdef ENABLE_AUDIO_BAR + if (gSetting_mic_bar && (gFlashLightBlinkCounter % (100 / 10)) == 0) // once every 100ms + UI_DisplayAudioBar(); + //gUpdateDisplay = true; + #endif + + if (gUpdateDisplay) + { + gUpdateDisplay = false; + GUI_DisplayScreen(); + } + + if (gUpdateStatus) + UI_DisplayStatus(false); + } // Skipping authentic device checks diff --git a/app/menu.c b/app/menu.c index 2cc837c..6b406e1 100644 --- a/app/menu.c +++ b/app/menu.c @@ -82,6 +82,7 @@ VOICE_ID_INVALID, // STE VOICE_ID_INVALID, // RP-STE VOICE_ID_INVALID, // MIC + VOICE_ID_INVALID, // MICBAR #ifdef ENABLE_COMPANDER VOICE_ID_INVALID, // COMPND #endif @@ -254,6 +255,9 @@ int MENU_GetLimits(uint8_t Cursor, int32_t *pMin, int32_t *pMax) break; #endif + #ifdef ENABLE_AUDIO_BAR + case MENU_MIC_BAR: + #endif case MENU_BCL: case MENU_BEEP: case MENU_AUTOLK: @@ -596,6 +600,12 @@ void MENU_AcceptSetting(void) gFlagReconfigureVfos = true; break; + #ifdef ENABLE_AUDIO_BAR + case MENU_MIC_BAR: + gSetting_mic_bar = gSubMenuSelection; + break; + #endif + #ifdef ENABLE_COMPANDER case MENU_COMPAND: gTxVfo->Compander = gSubMenuSelection; @@ -966,6 +976,12 @@ void MENU_ShowCurrentSetting(void) gSubMenuSelection = gEeprom.MIC_SENSITIVITY; break; + #ifdef ENABLE_AUDIO_BAR + case MENU_MIC_BAR: + gSubMenuSelection = gSetting_mic_bar; + break; + #endif + #ifdef ENABLE_COMPANDER case MENU_COMPAND: gSubMenuSelection = gTxVfo->Compander; diff --git a/board.c b/board.c index 46d6650..c10fb67 100644 --- a/board.c +++ b/board.c @@ -709,7 +709,10 @@ void BOARD_EEPROM_Init(void) gSetting_TX_EN = (Data[7] & (1u << 0)) ? true : false; gSetting_live_DTMF_decoder = (Data[7] & (1u << 1)) ? true : false; gSetting_battery_text = (((Data[7] >> 2) & 3u) <= 2) ? (Data[7] >> 2) & 3: 2; - + #ifdef ENABLE_AUDIO_BAR + gSetting_mic_bar = (Data[7] & (1u << 4)) ? true : false; + #endif + if (!gEeprom.VFO_OPEN) { gEeprom.ScreenChannel[0] = gEeprom.MrChannel[0]; diff --git a/driver/bk4819.c b/driver/bk4819.c index dd7861c..3d784e3 100644 --- a/driver/bk4819.c +++ b/driver/bk4819.c @@ -821,23 +821,27 @@ void BK4819_DisableDTMF(void) void BK4819_EnableDTMF(void) { // no idea what this register does - BK4819_WriteRegister(BK4819_REG_21, 0x06D8); + BK4819_WriteRegister(BK4819_REG_21, 0x06D8); // 0000 0110 1101 1000 - // REG_24 <5> 0 DTMF/SelCall Enable - // 1 = Enable - // 0 = Disable - // REG_24 <4> 1 DTMF or SelCall Detection Mode - // 1 = for DTMF - // 0 = for SelCall - // REG_24 <3:0> 14 Max Symbol Number for SelCall Detection + // REG_24 <15> 1 ??? + // REG_24 <14:7> 24 Threshold + // REG_24 <6> 1 ??? + // REG_24 <5> 0 DTMF/SelCall enable + // 1 = Enable + // 0 = Disable + // REG_24 <4> 1 DTMF or SelCall detection mode + // 1 = for DTMF + // 0 = for SelCall + // REG_24 <3:0> 14 Max symbol number for SelCall detection // - BK4819_WriteRegister(BK4819_REG_24, - (1u << BK4819_REG_24_SHIFT_UNKNOWN_15) - | (24u << BK4819_REG_24_SHIFT_THRESHOLD) - | (1u << BK4819_REG_24_SHIFT_UNKNOWN_6) - | BK4819_REG_24_ENABLE - | BK4819_REG_24_SELECT_DTMF - | (14u << BK4819_REG_24_SHIFT_MAX_SYMBOLS)); + const uint16_t threshold = 24; + BK4819_WriteRegister(BK4819_REG_24, // 1 00011000 1 1 1 1110 + (1u << BK4819_REG_24_SHIFT_UNKNOWN_15) + | (threshold << BK4819_REG_24_SHIFT_THRESHOLD) + | (1u << BK4819_REG_24_SHIFT_UNKNOWN_6) + | BK4819_REG_24_ENABLE + | BK4819_REG_24_SELECT_DTMF + | (14u << BK4819_REG_24_SHIFT_MAX_SYMBOLS)); } void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch) @@ -1185,6 +1189,26 @@ uint16_t BK4819_GetRSSI(void) return BK4819_ReadRegister(BK4819_REG_67) & 0x01FF; } +uint8_t BK4819_GetGlitchIndicator(void) +{ + return BK4819_ReadRegister(BK4819_REG_63) & 0x00FF; +} + +uint8_t BK4819_GetExNoiceIndicator(void) +{ + return BK4819_ReadRegister(BK4819_REG_65) & 0x007F; +} + +uint16_t BK4819_GetVoiceAmplitudeOut(void) +{ + return BK4819_ReadRegister(BK4819_REG_64); +} + +uint8_t BK4819_GetAfTxRx(void) +{ + return BK4819_ReadRegister(BK4819_REG_6F) & 0x003F; +} + bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency) { const uint16_t High = BK4819_ReadRegister(BK4819_REG_0D); diff --git a/driver/bk4819.h b/driver/bk4819.h index 8990781..3c1033a 100644 --- a/driver/bk4819.h +++ b/driver/bk4819.h @@ -132,6 +132,10 @@ void BK4819_EnableCDCSS(void); void BK4819_EnableCTCSS(void); uint16_t BK4819_GetRSSI(void); +uint8_t BK4819_GetGlitchIndicator(void); +uint8_t BK4819_GetExNoiceIndicator(void); +uint16_t BK4819_GetVoiceAmplitudeOut(void); +uint8_t BK4819_GetAfTxRx(void); bool BK4819_GetFrequencyScanResult(uint32_t *pFrequency); BK4819_CssScanResult_t BK4819_GetCxCSSScanResult(uint32_t *pCdcssFreq, uint16_t *pCtcssFreq); diff --git a/firmware b/firmware index 7b86b5a..5965714 100644 Binary files a/firmware and b/firmware differ diff --git a/firmware.bin b/firmware.bin index dc063f2..9a7846c 100644 Binary files a/firmware.bin and b/firmware.bin differ diff --git a/firmware.packed.bin b/firmware.packed.bin index 886eaaf..be37674 100644 Binary files a/firmware.packed.bin and b/firmware.packed.bin differ diff --git a/functions.c b/functions.c index a3d9e76..250a2fc 100644 --- a/functions.c +++ b/functions.c @@ -193,6 +193,9 @@ void FUNCTION_Select(FUNCTION_Type_t Function) BK4819_DisableScramble(); break; + + case FUNCTION_BAND_SCOPE: + break; } gBatterySaveCountdown_10ms = battery_save_count_10ms; diff --git a/functions.h b/functions.h index 991d921..cc63dd8 100644 --- a/functions.h +++ b/functions.h @@ -26,7 +26,8 @@ enum FUNCTION_Type_t FUNCTION_MONITOR, FUNCTION_INCOMING, FUNCTION_RECEIVE, - FUNCTION_POWER_SAVE + FUNCTION_POWER_SAVE, + FUNCTION_BAND_SCOPE }; typedef enum FUNCTION_Type_t FUNCTION_Type_t; diff --git a/helper/boot.c b/helper/boot.c index c6f6c43..d0d45b1 100644 --- a/helper/boot.c +++ b/helper/boot.c @@ -68,7 +68,8 @@ void BOOT_ProcessMode(BOOT_Mode_t Mode) { // enable all the menu items gMenuListCount = 0; - while (MenuList[gMenuListCount][0] != 0) + //while (MenuList[gMenuListCount][0] != 0) + while (MenuList[gMenuListCount] != NULL) gMenuListCount++; gMenuCursor = MENU_350TX; diff --git a/main.c b/main.c index 255f369..2c9071f 100644 --- a/main.c +++ b/main.c @@ -15,6 +15,7 @@ */ #include +#include // NULL #include "app/app.h" #include "app/dtmf.h" @@ -95,7 +96,12 @@ void Main(void) BATTERY_GetReadings(false); + // count the number of menu list items gMenuListCount = 0; +// while (MenuList[gMenuListCount][0] != 0) + while (MenuList[gMenuListCount] != NULL) + gMenuListCount++; + gMenuListCount -= 8; // disable the last few menu items .. they are the normally 'hidden' menu items boot_counter_10ms = 250; // 2.5 sec @@ -114,13 +120,6 @@ void Main(void) { BOOT_Mode_t BootMode; - // count the number of menu list items - while (MenuList[gMenuListCount][0] != 0) - gMenuListCount++; - // disable the N menu items - //gMenuListCount -= 6; - gMenuListCount -= 8; - UI_DisplayWelcome(); BACKLIGHT_TurnOn(); diff --git a/misc.c b/misc.c index 080a2f1..b2b2572 100644 --- a/misc.c +++ b/misc.c @@ -50,8 +50,8 @@ const uint16_t dual_watch_count_toggle_10ms = 100 / 10; // 100ms betw const uint16_t battery_save_count_10ms = 10000 / 10; // 10 seconds const uint16_t gMax_bat_v = 843; // 8.43V -//const uint16_t gMin_bat_v = 660; // 6.6V -const uint16_t gMin_bat_v = 710; // 7.1V +const uint16_t gMin_bat_v = 660; // 6.6V +//const uint16_t gMin_bat_v = 690; // 6.9V const uint32_t gDefaultAesKey[4] = {0x4AA5CC60, 0x0312CC5F, 0xFFD2DABB, 0x6BBA7F92}; @@ -66,6 +66,9 @@ bool gSetting_TX_EN; uint8_t gSetting_F_LOCK; bool gSetting_ScrambleEnable; +#ifdef ENABLE_AUDIO_BAR + bool gSetting_mic_bar; +#endif bool gSetting_live_DTMF_decoder; uint8_t gSetting_battery_text; diff --git a/misc.h b/misc.h index ace6e35..1161f58 100644 --- a/misc.h +++ b/misc.h @@ -127,6 +127,9 @@ extern bool gSetting_TX_EN; extern uint8_t gSetting_F_LOCK; extern bool gSetting_ScrambleEnable; +#ifdef ENABLE_AUDIO_BAR + extern bool gSetting_mic_bar; +#endif extern bool gSetting_live_DTMF_decoder; extern uint8_t gSetting_battery_text; diff --git a/settings.c b/settings.c index 5ee317d..9866f60 100644 --- a/settings.c +++ b/settings.c @@ -168,6 +168,9 @@ void SETTINGS_SaveSettings(void) if (!gSetting_TX_EN) State[7] &= ~(1u << 0); if (!gSetting_live_DTMF_decoder) State[7] &= ~(1u << 1); State[7] = (State[7] & ~(3u << 2)) | ((gSetting_battery_text & 3u) << 2); + #ifdef ENABLE_AUDIO_BAR + if (!gSetting_mic_bar) State[7] &= ~(1u << 4); + #endif EEPROM_WriteBuffer(0x0F40, State); } diff --git a/ui/main.c b/ui/main.c index 70ee4e0..4f2fe47 100644 --- a/ui/main.c +++ b/ui/main.c @@ -19,6 +19,7 @@ #include "app/dtmf.h" #include "bitmaps.h" #include "board.h" +#include "driver/bk4819.h" #include "driver/st7565.h" #include "external/printf/printf.h" #include "functions.h" @@ -34,6 +35,43 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif +#ifdef ENABLE_AUDIO_BAR + void UI_DisplayAudioBar(void) + { +// if (gCurrentFunction == FUNCTION_TRANSMIT && gSetting_mic_bar) + if (gSetting_mic_bar) + { + const unsigned int line = 3; + const unsigned int lcd_width = sizeof(gFrameBuffer[line]) - 2; + + #if 1 + // TX audio level + const uint16_t voice_amp = BK4819_GetVoiceAmplitudeOut(); // 15:0 + const unsigned int max = 32767; + const unsigned int level = (((uint32_t)voice_amp * lcd_width) + (max / 2)) / max; // with rounding + #else + // TX/RX AF input level (dB) + const uint8_t af_tx_rx = BK4819_GetAfTxRx(); // 6:0 + const unsigned int max = 63; + const unsigned int level = (((uint16_t)af_tx_rx * lcd_width) + (max / 2)) / max; // with rounding + #endif + + const unsigned int len = (level <= lcd_width) ? level : lcd_width; + uint8_t *pLine = gFrameBuffer[line]; + memset(pLine, 0, lcd_width); + #if 0 + // solid bar + memset(pLine, 0x3e, len); + #else + for (unsigned int i = 0; i < len; i += 2) + pLine[i] = 0x3e; + #endif + + ST7565_BlitFullScreen(); + } + } +#endif + void UI_DisplayMain(void) { char String[16]; @@ -256,7 +294,7 @@ void UI_DisplayMain(void) memmove(pLine0 + 120 + LCD_WIDTH, BITMAP_compand, sizeof(BITMAP_compand)); #endif #endif - + switch (gEeprom.CHANNEL_DISPLAY_MODE) { case MDF_FREQUENCY: // show the channel frequency @@ -397,6 +435,7 @@ void UI_DisplayMain(void) } UI_PrintStringSmall(String, LCD_WIDTH + 24, 0, Line + 1); + if (State != VFO_STATE_TX_DISABLE) { // show the TX power const char pwr_list[] = "LMH"; const unsigned int i = gEeprom.VfoInfo[vfo_num].OUTPUT_POWER; @@ -427,8 +466,8 @@ void UI_DisplayMain(void) } UI_PrintStringSmall(String, LCD_WIDTH + 70, 0, Line + 1); } - - // show the DTMF decoding symbol + + // show the DTMF decoding symbol( if (gEeprom.VfoInfo[vfo_num].DTMF_DECODING_ENABLE || gSetting_KILLED) UI_PrintStringSmall("DTMF", LCD_WIDTH + 78, 0, Line + 1); @@ -439,6 +478,10 @@ void UI_DisplayMain(void) if (center_line_is_free) { + #ifdef ENABLE_AUDIO_BAR + UI_DisplayAudioBar(); + #endif + if (gSetting_live_DTMF_decoder && gDTMF_ReceivedSaved[0] >= 32) { // show live DTMF decode UI_PrintStringSmall(gDTMF_ReceivedSaved, 8, 0, 3); diff --git a/ui/main.h b/ui/main.h index e839af5..347b9f4 100644 --- a/ui/main.h +++ b/ui/main.h @@ -17,6 +17,7 @@ #ifndef UI_MAIN_H #define UI_MAIN_H +void UI_DisplayAudioBar(void); void UI_DisplayMain(void); #endif diff --git a/ui/menu.c b/ui/menu.c index cde01e2..26ee6f8 100644 --- a/ui/menu.c +++ b/ui/menu.c @@ -39,7 +39,7 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) #endif -const char MenuList[][7] = +const char *MenuList[] = { "SQL", "STEP", @@ -74,6 +74,7 @@ const char MenuList[][7] = "STE", "RP-STE", "MIC", + "MICBAR", #ifdef ENABLE_COMPANDER "COMPND", #endif @@ -118,7 +119,7 @@ const char MenuList[][7] = "TX-EN", // enable TX "F-CALI", // reference xtal calibration - "" // end of list - DO NOT DELETE THIS ! .. I use this to compute this list size + NULL // end of list - DO NOT DELETE THIS }; const char gSubMenu_TXP[3][5] = @@ -325,6 +326,12 @@ void UI_DisplayMenu(void) } break; + #ifdef ENABLE_AUDIO_BAR + case MENU_MIC_BAR: + strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]); + break; + #endif + case MENU_STEP: sprintf(String, "%d.%02uKHz", StepFrequencyTable[gSubMenuSelection] / 100, abs(StepFrequencyTable[gSubMenuSelection]) % 100); break; diff --git a/ui/menu.h b/ui/menu.h index 61b2f34..39eea78 100644 --- a/ui/menu.h +++ b/ui/menu.h @@ -55,6 +55,9 @@ enum MENU_STE, MENU_RP_STE, MENU_MIC, + #ifdef ENABLE_AUDIO_BAR + MENU_MIC_BAR, + #endif #ifdef ENABLE_COMPANDER MENU_COMPAND, #endif @@ -99,7 +102,7 @@ enum MENU_F_CALI // reference xtal calibration }; -extern const char MenuList[][7]; +extern const char *MenuList[]; extern const char gSubMenu_TXP[3][5]; extern const char gSubMenu_SFT_D[3][4]; diff --git a/version.c b/version.c index 2cbe055..05455b6 100644 --- a/version.c +++ b/version.c @@ -2,7 +2,7 @@ #ifdef GIT_HASH #define VER GIT_HASH #else - #define VER "230920" + #define VER "230921" #endif const char Version[] = "OEFW-"VER; diff --git a/win_make.bat b/win_make.bat index 1dc179a..370a5ba 100644 --- a/win_make.bat +++ b/win_make.bat @@ -1,9 +1,9 @@ :: download "gcc-arm-none-eabi-10.3-2021.10-win32.exe" for windows .. -:: https://developer.arm.com/downloads/-/gnu-rm +:: https://developer.arm.com/downloads/-/gnu-rm :: :: download "gnu_make-3.81.exe" for windows .. -:: https://gnuwin32.sourceforge.net/packages/make.htm +:: https://gnuwin32.sourceforge.net/packages/make.htm del /S /Q *.o >nul 2>nul del /S /Q *.d >nul 2>nul @@ -15,13 +15,17 @@ del /S /Q *.d >nul 2>nul :: If you have python installed, you can create a 'packed' .bin from the compiled firmware.bin file. :: The Quangsheng windows upload-to-radio program requires a 'packed' .bin file. :: -:: if you don't have python installed, then comment out the python line below, in which case you'll need -:: to upload the standard firmware.bin file another way. +:: if you don't have python installed, then comment out the python line(s) below, in which case you'll need +:: to upload the standard unpacked firmware.bin file another way. +:: :: I wrote a windows version of k5prog to do this easily in windows .. :: https://github.com/OneOfEleven/k5prog-win -:: -::python -m pip install --upgrade pip crcmod -fw-pack.py firmware.bin 230920 firmware.packed.bin + +::python -m pip install --upgrade pip crcmod +::python3 -m pip install --upgrade pip crcmod + +::python fw-pack.py firmware.bin 230921 firmware.packed.bin +::python3 fw-pack.py firmware.bin 230921 firmware.packed.bin ::arm-none-eabi-size firmware