mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Add special recovery mode for wiped calibration (no more reboot loop)
This commit is contained in:
1 parent
fe9c4e9432
commit
6e75d13cbe
6 files changed
+188
No files matched your search
@@ -197,6 +197,9 @@ enable_feature(ENABLE_FEAT_F4HWN_SCAN_FASTER)
|
|||||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
enable_feature(ENABLE_FEAT_F4HWN_SCAN_RSSI)
|
||||||
enable_feature(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE)
|
enable_feature(ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE)
|
||||||
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
|
enable_feature(ENABLE_FEAT_F4HWN_RESCUE_OPS)
|
||||||
|
enable_feature(ENABLE_FEAT_F4HWN_RECOVER
|
||||||
|
helper/recovery.c
|
||||||
|
)
|
||||||
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
enable_feature(ENABLE_FEAT_F4HWN_VOL)
|
||||||
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
enable_feature(ENABLE_FEAT_F4HWN_AUDIO)
|
||||||
enable_feature(ENABLE_FEAT_F4HWN_RESET_VFO)
|
enable_feature(ENABLE_FEAT_F4HWN_RESET_VFO)
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/* Copyright 2025 Armel FAUVEAU (F4HWN)
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "py32f0xx.h" // NVIC_SystemReset
|
||||||
|
|
||||||
|
#include "helper/recovery.h"
|
||||||
|
#include "driver/py25q16.h"
|
||||||
|
#include "driver/st7565.h"
|
||||||
|
#include "driver/system.h" // SYSTEM_DelayMs
|
||||||
|
#include "driver/backlight.h"
|
||||||
|
#include "app/uart.h"
|
||||||
|
#include "ui/helper.h" // UI_DisplayClear, UI_PrintString*
|
||||||
|
|
||||||
|
// External-flash calibration zone (see the mapping in App/driver/eeprom_compat.c:
|
||||||
|
// EEPROM logical 0xB000..0xB200 <-> PY25Q16 physical 0x010000..0x010200).
|
||||||
|
#define CALIB_ADDR 0x010000
|
||||||
|
#define CALIB_SIZE 512
|
||||||
|
|
||||||
|
bool RECOVERY_CalibrationIsWiped(void)
|
||||||
|
{
|
||||||
|
uint8_t buf[64];
|
||||||
|
bool all_ff = true;
|
||||||
|
bool all_00 = true;
|
||||||
|
|
||||||
|
for (uint32_t off = 0; off < CALIB_SIZE; off += sizeof(buf))
|
||||||
|
{
|
||||||
|
PY25Q16_ReadBuffer(CALIB_ADDR + off, buf, sizeof(buf));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < sizeof(buf); i++)
|
||||||
|
{
|
||||||
|
if (buf[i] != 0xFF) all_ff = false;
|
||||||
|
if (buf[i] != 0x00) all_00 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As soon as a byte breaks both patterns, a real calibration is
|
||||||
|
// present: nothing to recover.
|
||||||
|
if (!all_ff && !all_00)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_ff || all_00;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RECOVERY_DrawWaitScreen(void)
|
||||||
|
{
|
||||||
|
UI_StatusClear();
|
||||||
|
UI_DisplayClear();
|
||||||
|
|
||||||
|
UI_PrintString("RESTORE", 0, 127, 1, 10);
|
||||||
|
UI_PrintStringSmallNormal("CALIBRATION", 0, 127, 3);
|
||||||
|
UI_PrintStringSmallNormal("Connect UV Studio", 0, 127, 5);
|
||||||
|
UI_PrintStringSmallNormal("and restore calib", 0, 127, 6);
|
||||||
|
|
||||||
|
ST7565_BlitStatusLine(); // blank status line
|
||||||
|
ST7565_BlitFullScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RECOVERY_Loop(void)
|
||||||
|
{
|
||||||
|
BACKLIGHT_TurnOn();
|
||||||
|
RECOVERY_DrawWaitScreen();
|
||||||
|
|
||||||
|
// Both counters are expressed in loop ticks (~10 ms each).
|
||||||
|
uint16_t idle = 0; // time since the last serviced command
|
||||||
|
uint16_t recheck = 0; // time since the last calibration re-read
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
bool activity = false;
|
||||||
|
|
||||||
|
#ifdef ENABLE_UART
|
||||||
|
if (UART_IsCommandAvailable(UART_PORT_UART))
|
||||||
|
{
|
||||||
|
UART_HandleCommand(UART_PORT_UART);
|
||||||
|
activity = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_USB
|
||||||
|
if (UART_IsCommandAvailable(UART_PORT_VCP))
|
||||||
|
{
|
||||||
|
UART_HandleCommand(UART_PORT_VCP);
|
||||||
|
activity = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (activity)
|
||||||
|
idle = 0;
|
||||||
|
else if (idle < 60000)
|
||||||
|
idle++;
|
||||||
|
|
||||||
|
// Re-read the calibration about once per second, but only reboot once
|
||||||
|
// it is valid AND the link has been quiet for ~3 s. This guarantees we
|
||||||
|
// never reset in the middle of an UV Studio transfer (which briefly
|
||||||
|
// makes the zone look non-wiped after the first sector is written).
|
||||||
|
if (++recheck >= 100)
|
||||||
|
{
|
||||||
|
recheck = 0;
|
||||||
|
|
||||||
|
if (idle >= 300 && !RECOVERY_CalibrationIsWiped())
|
||||||
|
{
|
||||||
|
UI_DisplayClear();
|
||||||
|
UI_PrintString("CALIB OK", 0, 127, 2, 10);
|
||||||
|
UI_PrintString("REBOOT", 0, 127, 4, 10);
|
||||||
|
ST7565_BlitStatusLine();
|
||||||
|
ST7565_BlitFullScreen();
|
||||||
|
|
||||||
|
SYSTEM_DelayMs(1500);
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSTEM_DelayMs(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/* Copyright 2025 Armel FAUVEAU (F4HWN)
|
||||||
|
*
|
||||||
|
* 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 HELPER_RECOVERY_H
|
||||||
|
#define HELPER_RECOVERY_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// True when the external-flash calibration zone (0x010000, 512 bytes) is
|
||||||
|
// entirely erased (all 0xFF) or entirely zeroed (all 0x00) -- i.e. a radio
|
||||||
|
// whose calibration has been wiped by a faulty firmware. The check is
|
||||||
|
// deliberately strict: a real calibration always contains mixed bytes, so it
|
||||||
|
// can never be mistaken for a wiped one.
|
||||||
|
bool RECOVERY_CalibrationIsWiped(void);
|
||||||
|
|
||||||
|
// Recovery waiting screen. Displays "RESTORE CALIBRATION" and keeps the
|
||||||
|
// UART/USB link alive so an external tool (UV Studio) can rewrite the
|
||||||
|
// calibration zone. Never returns: once a valid calibration has been written
|
||||||
|
// and the link has gone quiet, the radio is reset to boot normally.
|
||||||
|
//
|
||||||
|
// This path deliberately bypasses the battery / reduced-service logic that
|
||||||
|
// would otherwise trap a wiped radio in a reboot loop.
|
||||||
|
void RECOVERY_Loop(void) __attribute__((noreturn));
|
||||||
|
|
||||||
|
#endif
|
||||||
+12
@@ -60,6 +60,9 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "helper/battery.h"
|
#include "helper/battery.h"
|
||||||
#include "helper/boot.h"
|
#include "helper/boot.h"
|
||||||
|
#ifdef ENABLE_FEAT_F4HWN_RECOVER
|
||||||
|
#include "helper/recovery.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ui/lock.h"
|
#include "ui/lock.h"
|
||||||
#include "ui/welcome.h"
|
#include "ui/welcome.h"
|
||||||
@@ -114,6 +117,15 @@ void Main(void)
|
|||||||
SETTINGS_WriteBuildOptions();
|
SETTINGS_WriteBuildOptions();
|
||||||
SETTINGS_LoadCalibration();
|
SETTINGS_LoadCalibration();
|
||||||
|
|
||||||
|
#ifdef ENABLE_FEAT_F4HWN_RECOVER
|
||||||
|
// A radio whose calibration zone has been wiped by a faulty firmware would
|
||||||
|
// otherwise read a bogus battery voltage, be forced into reduced service
|
||||||
|
// and reset in a loop. Instead, park it on a safe recovery screen and wait
|
||||||
|
// for the calibration to be restored over USB (UV Studio).
|
||||||
|
if (RECOVERY_CalibrationIsWiped())
|
||||||
|
RECOVERY_Loop(); // never returns until a valid calibration is restored
|
||||||
|
#endif
|
||||||
|
|
||||||
RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
|
RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
|
||||||
RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);
|
RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);
|
||||||
|
|
||||||
|
|||||||
@@ -534,6 +534,12 @@ void SETTINGS_LoadCalibration(void)
|
|||||||
gBatteryCalibration[0] = 1900;
|
gBatteryCalibration[0] = 1900;
|
||||||
gBatteryCalibration[1] = 2000;
|
gBatteryCalibration[1] = 2000;
|
||||||
}
|
}
|
||||||
|
// A wiped calibration zone (0x0000 / 0xFFFF) leaves gBatteryCalibration[3]
|
||||||
|
// invalid. As it is the divisor of the battery-voltage computation, that
|
||||||
|
// collapses the reading to "critical" and can trap the radio in reduced
|
||||||
|
// service -> reset (reboot loop). Fall back to a nominal value (RAM only).
|
||||||
|
if (gBatteryCalibration[3] < 1000 || gBatteryCalibration[3] > 3000)
|
||||||
|
gBatteryCalibration[3] = 2000;
|
||||||
gBatteryCalibration[5] = 2300;
|
gBatteryCalibration[5] = 2300;
|
||||||
|
|
||||||
#ifdef ENABLE_VOX
|
#ifdef ENABLE_VOX
|
||||||
|
|||||||
@@ -200,6 +200,7 @@
|
|||||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||||
|
"ENABLE_FEAT_F4HWN_RECOVER": true,
|
||||||
"ENABLE_FEAT_F4HWN_VOL": true,
|
"ENABLE_FEAT_F4HWN_VOL": true,
|
||||||
"ENABLE_FEAT_F4HWN_AUDIO": true,
|
"ENABLE_FEAT_F4HWN_AUDIO": true,
|
||||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
|
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
|
||||||
|
|||||||
Reference in new issue
Block a user