Files
ua1zbe a1206d6fd4 UA1ZBE Custom Firmware v1.0
Features:
- VFO mode with direct frequency input
- POCSAG decoder (512/1200 baud) with BCH(31,21) correction
- Full-screen Spectrum analyzer
- FM Radio receiver
- RSSI signal indicator overlay
- Custom boot splash (UA1ZBE / POCSAG pager / build date)

Architecture:
- app/mode.c — mode dispatcher (VFO/POCSAG/Spectrum/FM)
- app/boot_splash.c — 2-second boot splash
- app/pocsag/ — POCSAG decoder + BCH correction
- app/display_rssi.c — RSSI indicator
- main.c — entry point with custom init
- syscalls.c — bare-metal _sbrk stub

Build: arm-none-eabi-gcc -Os -flto -Wall -Werror -Wextra
Size: 57.9KB Flash / 3.6KB RAM

Controls:
- 0-9: Direct frequency input (VFO)
- SK2: POCSAG mode
- SK1: Spectrum analyzer
- 0: FM Radio
- EXIT: Return to VFO
- F/*: Toggle 512/1200 baud (in POCSAG)
2026-04-12 00:44:05 +03:00

167 lines
4.1 KiB
C

/* UA1ZBE Custom Firmware for Quansheng UV-K5/K6
* Based on egzumer/uv-k5-firmware-custom
*
* Three modes: VFO, POCSAG decoder, Spectrum analyzer
* Custom boot splash, RSSI indicator, direct frequency input
*
* Entry point: Main()
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "audio.h"
#include "board.h"
#include "misc.h"
#include "radio.h"
#include "settings.h"
#include "app/app.h"
#include "app/boot_splash.h"
#include "app/mode.h"
#include "app/dtmf.h"
#include "bsp/dp32g030/gpio.h"
#include "bsp/dp32g030/syscon.h"
#include "driver/backlight.h"
#include "driver/bk4819.h"
#include "driver/gpio.h"
#include "driver/system.h"
#include "driver/systick.h"
#include "driver/keyboard.h"
#include "app/display_rssi.h"
#include "helper/battery.h"
#include "helper/boot.h"
#include "ui/lock.h"
#include "ui/welcome.h"
#include "ui/menu.h"
void _putchar(__attribute__((unused)) char c)
{
#ifdef ENABLE_UART
extern void UART_Send(const uint8_t *p, uint16_t len);
UART_Send((uint8_t *)&c, 1);
#endif
}
void Main(void)
{
/* Enable clock gating of blocks we need */
SYSCON_DEV_CLK_GATE = 0
| SYSCON_DEV_CLK_GATE_GPIOA_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_GPIOB_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_GPIOC_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_UART1_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_SPI0_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_SARADC_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_CRC_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_AES_BITS_ENABLE
| SYSCON_DEV_CLK_GATE_PWM_PLUS0_BITS_ENABLE;
SYSTICK_Init();
BOARD_Init();
boot_counter_10ms = 250;
/* Clear DTMF string */
memset(gDTMF_String, '-', sizeof(gDTMF_String));
gDTMF_String[sizeof(gDTMF_String) - 1] = 0;
BK4819_Init();
BOARD_ADC_GetBatteryInfo(&gBatteryCurrentVoltage, &gBatteryCurrent);
SETTINGS_InitEEPROM();
SETTINGS_WriteBuildOptions();
SETTINGS_LoadCalibration();
RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);
RADIO_SelectVfos();
RADIO_SetupRegisters(true);
for (unsigned int i = 0; i < ARRAY_SIZE(gBatteryVoltages); i++)
BOARD_ADC_GetBatteryInfo(&gBatteryVoltages[i], &gBatteryCurrent);
BATTERY_GetReadings(false);
/* Wait for user to release keys */
const BOOT_Mode_t BootMode = BOOT_GetMode();
if (BootMode == BOOT_MODE_F_LOCK) {
extern bool gF_LOCK;
gF_LOCK = true;
}
/* Count menu items */
gMenuListCount = 0;
while (MenuList[gMenuListCount].name[0] != '\0') {
if (!gF_LOCK && MenuList[gMenuListCount].menu_id == FIRST_HIDDEN_MENU_ITEM)
break;
gMenuListCount++;
}
if (!GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) ||
KEYBOARD_Poll() != KEY_INVALID ||
BootMode != BOOT_MODE_NORMAL)
{
UI_DisplayReleaseKeys();
BACKLIGHT_TurnOn();
for (int i = 0; i < 50;) {
i = (GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT) &&
KEYBOARD_Poll() == KEY_INVALID) ? i + 1 : 0;
SYSTEM_DelayMs(10);
}
gKeyReading0 = KEY_INVALID;
gKeyReading1 = KEY_INVALID;
gDebounceCounter = 0;
}
if (!gChargingWithTypeC && gBatteryDisplayLevel == 0)
{
FUNCTION_Select(FUNCTION_POWER_SAVE);
if (gEeprom.BACKLIGHT_TIME < (ARRAY_SIZE(gSubMenu_BACKLIGHT) - 1))
BACKLIGHT_TurnOff();
else
BACKLIGHT_TurnOn();
gReducedService = true;
}
else
{
/* === CUSTOM BOOT SPLASH (2 seconds) === */
BACKLIGHT_TurnOn();
BOOT_SplashShow();
/* Initialize RSSI display */
RSSI_Init();
/* Initialize mode dispatcher — start in VFO mode */
MODE_Init();
gUpdateStatus = true;
}
/* === MAIN LOOP === */
while (true) {
APP_Update();
if (gNextTimeslice) {
/* Update RSSI periodically */
RSSI_Update();
/* Dispatch to current mode */
MODE_TimeSlice10ms();
if (gNextTimeslice_500ms) {
MODE_TimeSlice500ms();
}
}
}
}