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)
167 lines
4.1 KiB
C
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();
|
|
}
|
|
}
|
|
}
|
|
}
|