diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..4b6c3f9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM archlinux:latest
+RUN pacman -Syyu base-devel --noconfirm
+RUN pacman -Syyu arm-none-eabi-gcc --noconfirm
+RUN pacman -Syyu arm-none-eabi-newlib --noconfirm
+RUN pacman -Syyu git --noconfirm
+RUN pacman -Syyu python-pip --noconfirm
+RUN pacman -Syyu python-crcmod --noconfirm
+WORKDIR /app
+COPY . .
+
+RUN git submodule update --init --recursive
+#RUN make && cp firmware* compiled-firmware/
diff --git a/Makefile b/Makefile
index c7e0521..9e878a2 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ ENABLE_VOICE := 0
ENABLE_VOX := 0
ENABLE_ALARM := 0
ENABLE_TX1750 := 0
-ENABLE_PWRON_PASSWORD := 1
+ENABLE_PWRON_PASSWORD := 0
ENABLE_BIG_FREQ := 0
ENABLE_SMALL_BOLD := 1
ENABLE_KEEP_MEM_NAME := 1
diff --git a/README.md b/README.md
index c1051ae..36212d0 100644
--- a/README.md
+++ b/README.md
@@ -159,13 +159,13 @@ You may obtain a copy of the License at
# Example changes/updates
-
-
-
+
+
+
Video showing the AM fix working ..
-
+
diff --git a/app/action.c b/app/action.c
index 3d0ec31..fe07b1d 100644
--- a/app/action.c
+++ b/app/action.c
@@ -333,6 +333,7 @@ void ACTION_Handle(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
if (Key == KEY_SIDE1 && !bKeyHeld && bKeyPressed)
{
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
+
if (gDTMF_InputIndex > 0)
{
gDTMF_InputBox[--gDTMF_InputIndex] = '-';
diff --git a/app/app.c b/app/app.c
index c0a284b..e1006ab 100644
--- a/app/app.c
+++ b/app/app.c
@@ -2256,14 +2256,16 @@ static void APP_ProcessKey(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
char Code;
if (Key == KEY_SIDE2)
- {
+ { // transmit 1750Hz tone
Code = 0xFE;
}
else
{
- Code = DTMF_GetCharacter(Key);
+ Code = DTMF_GetCharacter(Key - KEY_0);
if (Code == 0xFF)
goto Skip;
+
+ // transmit DTMF keys
}
if (!bKeyPressed || bKeyHeld)
diff --git a/app/dtmf.c b/app/dtmf.c
index 83d71d3..b5135ff 100644
--- a/app/dtmf.c
+++ b/app/dtmf.c
@@ -390,8 +390,9 @@ void DTMF_Reply(void)
default:
case DTMF_REPLY_NONE:
- if (gDTMF_CallState != DTMF_CALL_STATE_NONE ||
- gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_OFF ||
+ if (gDTMF_CallState != DTMF_CALL_STATE_NONE ||
+ gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_APOLLO ||
+ gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_OFF ||
gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_TX_DOWN)
{
gDTMF_ReplyState = DTMF_REPLY_NONE;
diff --git a/app/main.c b/app/main.c
index c7b3659..b650338 100644
--- a/app/main.c
+++ b/app/main.c
@@ -34,6 +34,9 @@
#include "settings.h"
#include "ui/inputbox.h"
#include "ui/ui.h"
+#ifdef ENABLE_SPECTRUM
+// #include "app/spectrum.h"
+#endif
void toggle_chan_scanlist(void)
{ // toggle the selected channels scanlist setting
diff --git a/app/spectrum.c b/app/spectrum.c
new file mode 100644
index 0000000..9d00b86
--- /dev/null
+++ b/app/spectrum.c
@@ -0,0 +1,1193 @@
+/* Copyright 2023 fagci
+ * https://github.com/fagci
+ *
+ * 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 "../app/spectrum.h"
+
+struct FrequencyBandInfo {
+ uint32_t lower;
+ uint32_t upper;
+ uint32_t middle;
+};
+
+const struct FrequencyBandInfo FrequencyBandTable[7] = {
+ [BAND1_50MHz ] = {.lower = 1600000, .middle = 6500000, .upper = 7600000},
+ [BAND2_108MHz] = {.lower = 10800000, .middle = 12200000, .upper = 13599990},
+ [BAND3_136MHz] = {.lower = 13600000, .middle = 15000000, .upper = 17399990},
+ [BAND4_174MHz] = {.lower = 17400000, .middle = 26000000, .upper = 34999990},
+ [BAND5_350MHz] = {.lower = 35000000, .middle = 37000000, .upper = 39999990},
+ [BAND6_400MHz] = {.lower = 40000000, .middle = 43500000, .upper = 46999990},
+ [BAND7_470MHz] = {.lower = 47000000, .middle = 55000000, .upper = 130000000},
+};
+
+
+#define F_MIN FrequencyBandTable[0].lower
+#define F_MAX FrequencyBandTable[ARRAY_SIZE(FrequencyBandTable) - 1].upper
+
+const uint16_t RSSI_MAX_VALUE = 65535;
+
+static uint16_t R30, R37, R3D, R43, R47, R48, R7E;
+static uint32_t initialFreq;
+static char String[32];
+
+bool isInitialized = false;
+bool isListening = true;
+bool monitorMode = false;
+bool redrawStatus = true;
+bool redrawScreen = false;
+bool newScanStart = true;
+bool preventKeypress = true;
+bool audioState = true;
+
+State currentState = SPECTRUM, previousState = SPECTRUM;
+
+PeakInfo peak;
+ScanInfo scanInfo;
+KeyboardState kbd = {KEY_INVALID, KEY_INVALID, 0};
+
+const char *bwOptions[] = {" 25k", "12.5k", "6.25k"};
+const char *modulationTypeOptions[] = {" FM", " AM", "USB"};
+const uint8_t modulationTypeTuneSteps[] = {100, 50, 10};
+const uint8_t modTypeReg47Values[] = {1, 7, 5};
+
+SpectrumSettings settings = {stepsCount: STEPS_64,
+ scanStepIndex: S_STEP_25_0kHz,
+ frequencyChangeStep: 80000,
+ scanDelay: 3200,
+ rssiTriggerLevel: 150,
+ backlightState: true,
+ bw: BK4819_FILTER_BW_WIDE,
+ listenBw: BK4819_FILTER_BW_WIDE,
+ modulationType: false,
+ dbMin: -130,
+ dbMax: -50};
+
+uint32_t fMeasure = 0;
+uint32_t currentFreq, tempFreq;
+uint16_t rssiHistory[128] = {};
+
+uint8_t freqInputIndex = 0;
+uint8_t freqInputDotIndex = 0;
+KEY_Code_t freqInputArr[10];
+char freqInputString[11] = "----------\0"; // XXXX.XXXXX\0
+
+uint8_t menuState = 0;
+uint16_t listenT = 0;
+
+RegisterSpec registerSpecs[] = {
+ {},
+ {"LNAs", 0x13, 8, 0b11, 1},
+ {"LNA", 0x13, 5, 0b111, 1},
+ {"PGA", 0x13, 0, 0b111, 1},
+ {"IF", 0x3D, 0, 0xFFFF, 0x2aaa},
+ // {"MIX", 0x13, 3, 0b11, 1}, // TODO: hidden
+};
+
+uint16_t statuslineUpdateTimer = 0;
+
+static uint8_t DBm2S(int dbm) {
+ uint8_t i = 0;
+ dbm *= -1;
+ for (i = 0; i < ARRAY_SIZE(U8RssiMap); i++) {
+ if (dbm >= U8RssiMap[i]) {
+ return i;
+ }
+ }
+ return i;
+}
+
+static int Rssi2DBm(uint16_t rssi) { return (rssi >> 1) - 160; }
+
+static uint16_t GetRegMenuValue(uint8_t st) {
+ RegisterSpec s = registerSpecs[st];
+ return (BK4819_ReadRegister(s.num) >> s.offset) & s.maxValue;
+}
+
+static void SetRegMenuValue(uint8_t st, bool add) {
+ uint16_t v = GetRegMenuValue(st);
+ RegisterSpec s = registerSpecs[st];
+
+ uint16_t reg = BK4819_ReadRegister(s.num);
+ if (add && v <= s.maxValue - s.inc) {
+ v += s.inc;
+ } else if (!add && v >= 0 + s.inc) {
+ v -= s.inc;
+ }
+ // TODO: use max value for bits count in max value, or reset by additional
+ // mask in spec
+ reg &= ~(s.maxValue << s.offset);
+ BK4819_WriteRegister(s.num, reg | (v << s.offset));
+ redrawScreen = true;
+}
+
+// GUI functions
+
+static void PutPixel(uint8_t x, uint8_t y, bool fill) {
+ if (fill) {
+ gFrameBuffer[y >> 3][x] |= 1 << (y & 7);
+ } else {
+ gFrameBuffer[y >> 3][x] &= ~(1 << (y & 7));
+ }
+}
+static void PutPixelStatus(uint8_t x, uint8_t y, bool fill) {
+ if (fill) {
+ gStatusLine[x] |= 1 << y;
+ } else {
+ gStatusLine[x] &= ~(1 << y);
+ }
+}
+
+static void DrawHLine(int sy, int ey, int nx, bool fill) {
+ for (int i = sy; i <= ey; i++) {
+ if (i < 56 && nx < 128) {
+ PutPixel(nx, i, fill);
+ }
+ }
+}
+
+static void GUI_DisplaySmallest(const char *pString, uint8_t x, uint8_t y,
+ bool statusbar, bool fill) {
+ uint8_t c;
+ uint8_t pixels;
+ const uint8_t *p = (const uint8_t *)pString;
+
+ while ((c = *p++) && c != '\0') {
+ c -= 0x20;
+ for (int i = 0; i < 3; ++i) {
+ pixels = gFont3x5[c][i];
+ for (int j = 0; j < 6; ++j) {
+ if (pixels & 1) {
+ if (statusbar)
+ PutPixelStatus(x + i, y + j, fill);
+ else
+ PutPixel(x + i, y + j, fill);
+ }
+ pixels >>= 1;
+ }
+ }
+ x += 4;
+ }
+}
+
+// Utility functions
+
+KEY_Code_t GetKey() {
+ KEY_Code_t btn = KEYBOARD_Poll();
+ if (btn == KEY_INVALID && !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT)) {
+ btn = KEY_PTT;
+ }
+ return btn;
+}
+
+static int clamp(int v, int min, int max) {
+ return v <= min ? min : (v >= max ? max : v);
+}
+
+static uint8_t my_abs(signed v) { return v > 0 ? v : -v; }
+
+void SetState(State state) {
+ previousState = currentState;
+ currentState = state;
+ redrawScreen = true;
+ redrawStatus = true;
+}
+
+// Radio functions
+
+static void ToggleAFBit(bool on) {
+ uint16_t reg = BK4819_ReadRegister(BK4819_REG_47);
+ reg &= ~(1 << 8);
+ if (on)
+ reg |= on << 8;
+ BK4819_WriteRegister(BK4819_REG_47, reg);
+}
+
+static void BackupRegisters() {
+ R30 = BK4819_ReadRegister(0x30);
+ R37 = BK4819_ReadRegister(0x37);
+ R3D = BK4819_ReadRegister(0x3D);
+ R43 = BK4819_ReadRegister(0x43);
+ R47 = BK4819_ReadRegister(0x47);
+ R48 = BK4819_ReadRegister(0x48);
+ R7E = BK4819_ReadRegister(0x7E);
+}
+
+static void RestoreRegisters() {
+ BK4819_WriteRegister(0x30, R30);
+ BK4819_WriteRegister(0x37, R37);
+ BK4819_WriteRegister(0x3D, R3D);
+ BK4819_WriteRegister(0x43, R43);
+ BK4819_WriteRegister(0x47, R47);
+ BK4819_WriteRegister(0x48, R48);
+ BK4819_WriteRegister(0x7E, R7E);
+}
+
+static void SetModulation(ModulationType type) {
+ RestoreRegisters();
+ uint16_t reg = BK4819_ReadRegister(BK4819_REG_47);
+ reg &= ~(0b111 << 8);
+ BK4819_WriteRegister(BK4819_REG_47, reg | (modTypeReg47Values[type] << 8));
+ if (type == MOD_USB) {
+ BK4819_WriteRegister(0x3D, 0b0010101101000101);
+ BK4819_WriteRegister(BK4819_REG_37, 0x160F);
+ BK4819_WriteRegister(0x48, 0b0000001110101000);
+ }
+}
+
+static void ToggleAFDAC(bool on) {
+ uint32_t Reg = BK4819_ReadRegister(BK4819_REG_30);
+ Reg &= ~(1 << 9);
+ if (on)
+ Reg |= (1 << 9);
+ BK4819_WriteRegister(BK4819_REG_30, Reg);
+}
+
+static void SetF(uint32_t f) {
+ fMeasure = f;
+
+ BK4819_SetFrequency(fMeasure);
+ BK4819_PickRXFilterPathBasedOnFrequency(fMeasure);
+ uint16_t reg = BK4819_ReadRegister(BK4819_REG_30);
+ BK4819_WriteRegister(BK4819_REG_30, 0);
+ BK4819_WriteRegister(BK4819_REG_30, reg);
+}
+
+// Spectrum related
+
+bool IsPeakOverLevel() { return peak.rssi >= settings.rssiTriggerLevel; }
+
+static void ResetPeak() {
+ peak.t = 0;
+ peak.rssi = 0;
+}
+
+bool IsCenterMode() { return settings.scanStepIndex < S_STEP_2_5kHz; }
+uint8_t GetStepsCount() { return 128 >> settings.stepsCount; }
+uint16_t GetScanStep() { return scanStepValues[settings.scanStepIndex]; }
+uint32_t GetBW() { return GetStepsCount() * GetScanStep(); }
+uint32_t GetFStart() {
+ return IsCenterMode() ? currentFreq - (GetBW() >> 1) : currentFreq;
+}
+uint32_t GetFEnd() { return currentFreq + GetBW(); }
+
+static void TuneToPeak() {
+ scanInfo.f = peak.f;
+ scanInfo.rssi = peak.rssi;
+ scanInfo.i = peak.i;
+ SetF(scanInfo.f);
+}
+
+static void DeInitSpectrum() {
+ SetF(initialFreq);
+ RestoreRegisters();
+ isInitialized = false;
+}
+
+uint8_t GetBWRegValueForScan() {
+ return scanStepBWRegValues[settings.scanStepIndex];
+}
+
+uint16_t GetRssi() {
+ // SYSTICK_DelayUs(800);
+ // testing autodelay based on Glitch value
+ while ((BK4819_ReadRegister(0x63) & 0b11111111) >= 255) {
+ SYSTICK_DelayUs(100);
+ }
+ return BK4819_GetRSSI();
+}
+
+static void ToggleAudio(bool on) {
+ if (on == audioState) {
+ return;
+ }
+ audioState = on;
+ if (on) {
+ GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
+ } else {
+ GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
+ }
+}
+
+static void ToggleRX(bool on) {
+ isListening = on;
+
+ BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_GREEN, on);
+
+ ToggleAudio(on);
+ ToggleAFDAC(on);
+ ToggleAFBit(on);
+
+ if (on) {
+ listenT = 1000;
+ BK4819_WriteRegister(0x43, listenBWRegValues[settings.listenBw]);
+ } else {
+ BK4819_WriteRegister(0x43, GetBWRegValueForScan());
+ }
+}
+
+// Scan info
+
+static void ResetScanStats() {
+ scanInfo.rssi = 0;
+ scanInfo.rssiMax = 0;
+ scanInfo.iPeak = 0;
+ scanInfo.fPeak = 0;
+}
+
+static void InitScan() {
+ ResetScanStats();
+ scanInfo.i = 0;
+ scanInfo.f = GetFStart();
+
+ scanInfo.scanStep = GetScanStep();
+ scanInfo.measurementsCount = GetStepsCount();
+}
+
+static void ResetBlacklist() {
+ for (int i = 0; i < 128; ++i) {
+ if (rssiHistory[i] == RSSI_MAX_VALUE)
+ rssiHistory[i] = 0;
+ }
+}
+
+static void RelaunchScan() {
+ InitScan();
+ ResetPeak();
+ ToggleRX(false);
+#ifdef SPECTRUM_AUTOMATIC_SQUELCH
+ settings.rssiTriggerLevel = RSSI_MAX_VALUE;
+#endif
+ preventKeypress = true;
+ scanInfo.rssiMin = RSSI_MAX_VALUE;
+}
+
+static void UpdateScanInfo() {
+ if (scanInfo.rssi > scanInfo.rssiMax) {
+ scanInfo.rssiMax = scanInfo.rssi;
+ scanInfo.fPeak = scanInfo.f;
+ scanInfo.iPeak = scanInfo.i;
+ }
+
+ if (scanInfo.rssi < scanInfo.rssiMin) {
+ scanInfo.rssiMin = scanInfo.rssi;
+ settings.dbMin = Rssi2DBm(scanInfo.rssiMin);
+ redrawStatus = true;
+ }
+}
+
+static void AutoTriggerLevel() {
+ if (settings.rssiTriggerLevel == RSSI_MAX_VALUE) {
+ settings.rssiTriggerLevel = clamp(scanInfo.rssiMax + 8, 0, RSSI_MAX_VALUE);
+ }
+}
+
+static void UpdatePeakInfoForce() {
+ peak.t = 0;
+ peak.rssi = scanInfo.rssiMax;
+ peak.f = scanInfo.fPeak;
+ peak.i = scanInfo.iPeak;
+ AutoTriggerLevel();
+}
+
+static void UpdatePeakInfo() {
+ if (peak.f == 0 || peak.t >= 1024 || peak.rssi < scanInfo.rssiMax)
+ UpdatePeakInfoForce();
+}
+
+static void Measure() { rssiHistory[scanInfo.i] = scanInfo.rssi = GetRssi(); }
+
+// Update things by keypress
+
+static void UpdateRssiTriggerLevel(bool inc) {
+ if (inc)
+ settings.rssiTriggerLevel += 2;
+ else
+ settings.rssiTriggerLevel -= 2;
+ redrawScreen = true;
+ redrawStatus = true;
+}
+
+static void UpdateDBMax(bool inc) {
+ if (inc && settings.dbMax < 10) {
+ settings.dbMax += 1;
+ } else if (!inc && settings.dbMax > settings.dbMin) {
+ settings.dbMax -= 1;
+ } else {
+ return;
+ }
+ redrawStatus = true;
+ redrawScreen = true;
+ SYSTEM_DelayMs(20);
+}
+
+static void UpdateScanStep(bool inc) {
+ if (inc && settings.scanStepIndex < S_STEP_100_0kHz) {
+ settings.scanStepIndex++;
+ } else if (!inc && settings.scanStepIndex > 0) {
+ settings.scanStepIndex--;
+ } else {
+ return;
+ }
+ settings.frequencyChangeStep = GetBW() >> 1;
+ RelaunchScan();
+ ResetBlacklist();
+ redrawScreen = true;
+}
+
+static void UpdateCurrentFreq(bool inc) {
+ if (inc && currentFreq < F_MAX) {
+ currentFreq += settings.frequencyChangeStep;
+ } else if (!inc && currentFreq > F_MIN) {
+ currentFreq -= settings.frequencyChangeStep;
+ } else {
+ return;
+ }
+ RelaunchScan();
+ ResetBlacklist();
+ redrawScreen = true;
+}
+
+static void UpdateCurrentFreqStill(bool inc) {
+ uint8_t offset = modulationTypeTuneSteps[settings.modulationType];
+ uint32_t f = fMeasure;
+ if (inc && f < F_MAX) {
+ f += offset;
+ } else if (!inc && f > F_MIN) {
+ f -= offset;
+ }
+ SetF(f);
+ redrawScreen = true;
+}
+
+static void UpdateFreqChangeStep(bool inc) {
+ uint16_t diff = GetScanStep() * 4;
+ if (inc && settings.frequencyChangeStep < 200000) {
+ settings.frequencyChangeStep += diff;
+ } else if (!inc && settings.frequencyChangeStep > 10000) {
+ settings.frequencyChangeStep -= diff;
+ }
+ SYSTEM_DelayMs(100);
+ redrawScreen = true;
+}
+
+static void ToggleModulation() {
+ if (settings.modulationType < MOD_USB) {
+ settings.modulationType++;
+ } else {
+ settings.modulationType = MOD_FM;
+ }
+ SetModulation(settings.modulationType);
+ redrawScreen = true;
+}
+
+static void ToggleListeningBW() {
+ if (settings.listenBw == BK4819_FILTER_BW_NARROWER) {
+ settings.listenBw = BK4819_FILTER_BW_WIDE;
+ } else {
+ settings.listenBw++;
+ }
+ redrawScreen = true;
+}
+
+static void ToggleBacklight() {
+ settings.backlightState = !settings.backlightState;
+ if (settings.backlightState) {
+ GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
+ } else {
+ GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
+ }
+}
+
+static void ToggleStepsCount() {
+ if (settings.stepsCount == STEPS_128) {
+ settings.stepsCount = STEPS_16;
+ } else {
+ settings.stepsCount--;
+ }
+ settings.frequencyChangeStep = GetBW() >> 1;
+ RelaunchScan();
+ ResetBlacklist();
+ redrawScreen = true;
+}
+
+static void ResetFreqInput() {
+ tempFreq = 0;
+ for (int i = 0; i < 10; ++i) {
+ freqInputString[i] = '-';
+ }
+}
+
+static void FreqInput() {
+ freqInputIndex = 0;
+ freqInputDotIndex = 0;
+ ResetFreqInput();
+ SetState(FREQ_INPUT);
+}
+
+static void UpdateFreqInput(KEY_Code_t key) {
+ if (key != KEY_EXIT && freqInputIndex >= 10) {
+ return;
+ }
+ if (key == KEY_STAR) {
+ if (freqInputIndex == 0 || freqInputDotIndex) {
+ return;
+ }
+ freqInputDotIndex = freqInputIndex;
+ }
+ if (key == KEY_EXIT) {
+ freqInputIndex--;
+ } else {
+ freqInputArr[freqInputIndex++] = key;
+ }
+
+ ResetFreqInput();
+
+ uint8_t dotIndex =
+ freqInputDotIndex == 0 ? freqInputIndex : freqInputDotIndex;
+
+ KEY_Code_t digitKey;
+ for (int i = 0; i < 10; ++i) {
+ if (i < freqInputIndex) {
+ digitKey = freqInputArr[i];
+ freqInputString[i] = digitKey <= KEY_9 ? '0' + digitKey : '.';
+ } else {
+ freqInputString[i] = '-';
+ }
+ }
+
+ uint32_t base = 100000; // 1MHz in BK units
+ for (int i = dotIndex - 1; i >= 0; --i) {
+ tempFreq += freqInputArr[i] * base;
+ base *= 10;
+ }
+
+ base = 10000; // 0.1MHz in BK units
+ if (dotIndex < freqInputIndex) {
+ for (int i = dotIndex + 1; i < freqInputIndex; ++i) {
+ tempFreq += freqInputArr[i] * base;
+ base /= 10;
+ }
+ }
+ redrawScreen = true;
+}
+
+static void Blacklist() {
+ rssiHistory[peak.i] = RSSI_MAX_VALUE;
+ ResetPeak();
+ ToggleRX(false);
+ newScanStart = true;
+}
+
+// Draw things
+
+// applied x2 to prevent initial rounding
+uint8_t Rssi2PX(uint16_t rssi, uint8_t pxMin, uint8_t pxMax) {
+ const int DB_MIN = settings.dbMin << 1;
+ const int DB_MAX = settings.dbMax << 1;
+ const int DB_RANGE = DB_MAX - DB_MIN;
+
+ const uint8_t PX_RANGE = pxMax - pxMin;
+
+ int dbm = clamp(rssi - (160 << 1), DB_MIN, DB_MAX);
+
+ return ((dbm - DB_MIN) * PX_RANGE + DB_RANGE / 2) / DB_RANGE + pxMin;
+}
+
+uint8_t Rssi2Y(uint16_t rssi) {
+ return DrawingEndY - Rssi2PX(rssi, 0, DrawingEndY);
+}
+
+static void DrawSpectrum() {
+ for (uint8_t x = 0; x < 128; ++x) {
+ uint16_t rssi = rssiHistory[x >> settings.stepsCount];
+ if (rssi != RSSI_MAX_VALUE) {
+ DrawHLine(Rssi2Y(rssi), DrawingEndY, x, true);
+ }
+ }
+}
+
+static void DrawStatus() {
+#ifdef SPECTRUM_EXTRA_VALUES
+ sprintf(String, "%d/%d P:%d T:%d", settings.dbMin, settings.dbMax,
+ Rssi2DBm(peak.rssi), Rssi2DBm(settings.rssiTriggerLevel));
+#else
+ sprintf(String, "%d/%d", settings.dbMin, settings.dbMax);
+#endif
+ GUI_DisplaySmallest(String, 0, 1, true, true);
+
+ for (int i = 0; i < 4; i++) {
+ BOARD_ADC_GetBatteryInfo(&gBatteryVoltages[i], &gBatteryCurrent);
+ }
+
+ uint16_t Voltage;
+ uint8_t v = 0;
+
+ Voltage = (gBatteryVoltages[0] + gBatteryVoltages[1] + gBatteryVoltages[2] +
+ gBatteryVoltages[3]) /
+ 4;
+
+ if (gBatteryCalibration[5] < Voltage) {
+ v = 5;
+ } else if (gBatteryCalibration[4] < Voltage) {
+ v = 5;
+ } else if (gBatteryCalibration[3] < Voltage) {
+ v = 4;
+ } else if (gBatteryCalibration[2] < Voltage) {
+ v = 3;
+ } else if (gBatteryCalibration[1] < Voltage) {
+ v = 2;
+ } else if (gBatteryCalibration[0] < Voltage) {
+ v = 1;
+ }
+
+ gStatusLine[127] = 0b01111110;
+ for (int i = 126; i >= 116; i--) {
+ gStatusLine[i] = 0b01000010;
+ }
+ v <<= 1;
+ for (int i = 125; i >= 116; i--) {
+ if (126 - i <= v) {
+ gStatusLine[i + 2] = 0b01111110;
+ }
+ }
+ gStatusLine[117] = 0b01111110;
+ gStatusLine[116] = 0b00011000;
+}
+
+static void DrawF(uint32_t f) {
+ sprintf(String, "%u.%05u", f / 100000, f % 100000);
+ UI_PrintStringSmall(String, 8, 127, 0);
+
+ sprintf(String, "%s", modulationTypeOptions[settings.modulationType]);
+ GUI_DisplaySmallest(String, 116, 1, false, true);
+ sprintf(String, "%s", bwOptions[settings.listenBw]);
+ GUI_DisplaySmallest(String, 108, 7, false, true);
+}
+
+static void DrawNums() {
+
+ if (currentState == SPECTRUM) {
+ sprintf(String, "%ux", GetStepsCount());
+ GUI_DisplaySmallest(String, 0, 1, false, true);
+ sprintf(String, "%u.%02uk", GetScanStep() / 100, GetScanStep() % 100);
+ GUI_DisplaySmallest(String, 0, 7, false, true);
+ }
+
+ if (IsCenterMode()) {
+ sprintf(String, "%u.%05u \xB1%u.%02uk", currentFreq / 100000,
+ currentFreq % 100000, settings.frequencyChangeStep / 100,
+ settings.frequencyChangeStep % 100);
+ GUI_DisplaySmallest(String, 36, 49, false, true);
+ } else {
+ sprintf(String, "%u.%05u", GetFStart() / 100000, GetFStart() % 100000);
+ GUI_DisplaySmallest(String, 0, 49, false, true);
+
+ sprintf(String, "\xB1%u.%02uk", settings.frequencyChangeStep / 100,
+ settings.frequencyChangeStep % 100);
+ GUI_DisplaySmallest(String, 48, 49, false, true);
+
+ sprintf(String, "%u.%05u", GetFEnd() / 100000, GetFEnd() % 100000);
+ GUI_DisplaySmallest(String, 93, 49, false, true);
+ }
+}
+
+static void DrawRssiTriggerLevel() {
+ if (settings.rssiTriggerLevel == RSSI_MAX_VALUE || monitorMode)
+ return;
+ uint8_t y = Rssi2Y(settings.rssiTriggerLevel);
+ for (uint8_t x = 0; x < 128; x += 2) {
+ PutPixel(x, y, true);
+ }
+}
+
+static void DrawTicks() {
+ uint32_t f = GetFStart() % 100000;
+ uint32_t step = GetScanStep();
+ for (uint8_t i = 0; i < 128; i += (1 << settings.stepsCount), f += step) {
+ uint8_t barValue = 0b00000001;
+ (f % 10000) < step && (barValue |= 0b00000010);
+ (f % 50000) < step && (barValue |= 0b00000100);
+ (f % 100000) < step && (barValue |= 0b00011000);
+
+ gFrameBuffer[5][i] |= barValue;
+ }
+
+ // center
+ if (IsCenterMode()) {
+ gFrameBuffer[5][62] = 0x80;
+ gFrameBuffer[5][63] = 0x80;
+ gFrameBuffer[5][64] = 0xff;
+ gFrameBuffer[5][65] = 0x80;
+ gFrameBuffer[5][66] = 0x80;
+ } else {
+ gFrameBuffer[5][0] = 0xff;
+ gFrameBuffer[5][1] = 0x80;
+ gFrameBuffer[5][2] = 0x80;
+ gFrameBuffer[5][3] = 0x80;
+ gFrameBuffer[5][124] = 0x80;
+ gFrameBuffer[5][125] = 0x80;
+ gFrameBuffer[5][126] = 0x80;
+ gFrameBuffer[5][127] = 0xff;
+ }
+}
+
+static void DrawArrow(uint8_t x) {
+ for (signed i = -2; i <= 2; ++i) {
+ signed v = x + i;
+ if (!(v & 128)) {
+ gFrameBuffer[5][v] |= (0b01111000 << my_abs(i)) & 0b01111000;
+ }
+ }
+}
+
+static void OnKeyDown(uint8_t key) {
+ switch (key) {
+ case KEY_3:
+ UpdateDBMax(true);
+ break;
+ case KEY_9:
+ UpdateDBMax(false);
+ break;
+ case KEY_1:
+ UpdateScanStep(true);
+ break;
+ case KEY_7:
+ UpdateScanStep(false);
+ break;
+ case KEY_2:
+ UpdateFreqChangeStep(true);
+ break;
+ case KEY_8:
+ UpdateFreqChangeStep(false);
+ break;
+ case KEY_UP:
+ UpdateCurrentFreq(true);
+ break;
+ case KEY_DOWN:
+ UpdateCurrentFreq(false);
+ break;
+ case KEY_SIDE1:
+ Blacklist();
+ break;
+ case KEY_STAR:
+ UpdateRssiTriggerLevel(true);
+ break;
+ case KEY_F:
+ UpdateRssiTriggerLevel(false);
+ break;
+ case KEY_5:
+ FreqInput();
+ break;
+ case KEY_0:
+ ToggleModulation();
+ break;
+ case KEY_6:
+ ToggleListeningBW();
+ break;
+ case KEY_4:
+ ToggleStepsCount();
+ break;
+ case KEY_SIDE2:
+ ToggleBacklight();
+ break;
+ case KEY_PTT:
+ SetState(STILL);
+ TuneToPeak();
+ break;
+ case KEY_MENU:
+ break;
+ case KEY_EXIT:
+ if (menuState) {
+ menuState = 0;
+ break;
+ }
+ DeInitSpectrum();
+ break;
+ default:
+ break;
+ }
+}
+
+static void OnKeyDownFreqInput(uint8_t key) {
+ switch (key) {
+ case KEY_0:
+ case KEY_1:
+ case KEY_2:
+ case KEY_3:
+ case KEY_4:
+ case KEY_5:
+ case KEY_6:
+ case KEY_7:
+ case KEY_8:
+ case KEY_9:
+ case KEY_STAR:
+ UpdateFreqInput(key);
+ break;
+ case KEY_EXIT:
+ if (freqInputIndex == 0) {
+ SetState(previousState);
+ break;
+ }
+ UpdateFreqInput(key);
+ break;
+ case KEY_MENU:
+ if (tempFreq < F_MIN || tempFreq > F_MAX) {
+ break;
+ }
+ SetState(previousState);
+ currentFreq = tempFreq;
+ if (currentState == SPECTRUM) {
+ ResetBlacklist();
+ RelaunchScan();
+ } else {
+ SetF(currentFreq);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void OnKeyDownStill(KEY_Code_t key) {
+ switch (key) {
+ case KEY_3:
+ UpdateDBMax(true);
+ break;
+ case KEY_9:
+ UpdateDBMax(false);
+ break;
+ case KEY_UP:
+ if (menuState) {
+ SetRegMenuValue(menuState, true);
+ break;
+ }
+ UpdateCurrentFreqStill(true);
+ break;
+ case KEY_DOWN:
+ if (menuState) {
+ SetRegMenuValue(menuState, false);
+ break;
+ }
+ UpdateCurrentFreqStill(false);
+ break;
+ case KEY_STAR:
+ UpdateRssiTriggerLevel(true);
+ break;
+ case KEY_F:
+ UpdateRssiTriggerLevel(false);
+ break;
+ case KEY_5:
+ FreqInput();
+ break;
+ case KEY_0:
+ ToggleModulation();
+ break;
+ case KEY_6:
+ ToggleListeningBW();
+ break;
+ case KEY_SIDE1:
+ monitorMode = !monitorMode;
+ break;
+ case KEY_SIDE2:
+ ToggleBacklight();
+ break;
+ case KEY_PTT:
+ // TODO: start transmit
+ /* BK4819_ToggleGpioOut(BK4819_GPIO0_PIN28_GREEN, false);
+ BK4819_ToggleGpioOut(BK4819_GPIO1_PIN29_RED, true); */
+ break;
+ case KEY_MENU:
+ if (menuState == ARRAY_SIZE(registerSpecs) - 1) {
+ menuState = 1;
+ } else {
+ menuState++;
+ }
+ redrawScreen = true;
+ break;
+ case KEY_EXIT:
+ if (!menuState) {
+ SetState(SPECTRUM);
+ monitorMode = false;
+ RelaunchScan();
+ break;
+ }
+ menuState = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+static void RenderFreqInput() {
+ UI_PrintString(freqInputString, 2, 127, 0, 8);
+}
+
+static void RenderStatus() {
+ memset(gStatusLine, 0, sizeof(gStatusLine));
+ DrawStatus();
+ ST7565_BlitStatusLine();
+}
+
+static void RenderSpectrum() {
+ DrawTicks();
+ DrawArrow(peak.i << settings.stepsCount);
+ DrawSpectrum();
+ DrawRssiTriggerLevel();
+ DrawF(peak.f);
+ DrawNums();
+}
+
+static void RenderStill() {
+ DrawF(fMeasure);
+
+ const uint8_t METER_PAD_LEFT = 3;
+
+ for (int i = 0; i < 121; i++) {
+ if (i % 10 == 0) {
+ gFrameBuffer[2][i + METER_PAD_LEFT] = 0b01110000;
+ } else if (i % 5 == 0) {
+ gFrameBuffer[2][i + METER_PAD_LEFT] = 0b00110000;
+ } else {
+ gFrameBuffer[2][i + METER_PAD_LEFT] = 0b00010000;
+ }
+ }
+
+ uint8_t x = Rssi2PX(scanInfo.rssi, 0, 121);
+ for (int i = 0; i < x; ++i) {
+ if (i % 5) {
+ gFrameBuffer[2][i + METER_PAD_LEFT] |= 0b00000111;
+ }
+ }
+
+ int dbm = Rssi2DBm(scanInfo.rssi);
+ uint8_t s = DBm2S(dbm);
+ sprintf(String, "S: %u", s);
+ GUI_DisplaySmallest(String, 4, 25, false, true);
+ sprintf(String, "%d dBm", dbm);
+ GUI_DisplaySmallest(String, 28, 25, false, true);
+
+ if (!monitorMode) {
+ uint8_t x = Rssi2PX(settings.rssiTriggerLevel, 0, 121);
+ gFrameBuffer[2][METER_PAD_LEFT + x] = 0b11111111;
+ }
+
+ const uint8_t PAD_LEFT = 4;
+ const uint8_t CELL_WIDTH = 30;
+ uint8_t offset = PAD_LEFT;
+ uint8_t row = 4;
+
+ for (int i = 0, idx = 1; idx <= 4; ++i, ++idx) {
+ if (idx == 5) {
+ row += 2;
+ i = 0;
+ }
+ offset = PAD_LEFT + i * CELL_WIDTH;
+ if (menuState == idx) {
+ for (int j = 0; j < CELL_WIDTH; ++j) {
+ gFrameBuffer[row][j + offset] = 0xFF;
+ gFrameBuffer[row + 1][j + offset] = 0xFF;
+ }
+ }
+ sprintf(String, "%s", registerSpecs[idx].name);
+ GUI_DisplaySmallest(String, offset + 2, row * 8 + 2, false,
+ menuState != idx);
+ sprintf(String, "%u", GetRegMenuValue(idx));
+ GUI_DisplaySmallest(String, offset + 2, (row + 1) * 8 + 1, false,
+ menuState != idx);
+ }
+}
+
+static void Render() {
+ memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
+
+ switch (currentState) {
+ case SPECTRUM:
+ RenderSpectrum();
+ break;
+ case FREQ_INPUT:
+ RenderFreqInput();
+ break;
+ case STILL:
+ RenderStill();
+ break;
+ }
+
+ ST7565_BlitFullScreen();
+}
+
+bool HandleUserInput() {
+ kbd.prev = kbd.current;
+ kbd.current = GetKey();
+
+ if (kbd.current == KEY_INVALID) {
+ kbd.counter = 0;
+ return true;
+ }
+
+ if (kbd.current == kbd.prev && kbd.counter <= 16) {
+ kbd.counter++;
+ SYSTEM_DelayMs(20);
+ }
+
+ if (kbd.counter == 3 || kbd.counter > 16) {
+ switch (currentState) {
+ case SPECTRUM:
+ OnKeyDown(kbd.current);
+ break;
+ case FREQ_INPUT:
+ OnKeyDownFreqInput(kbd.current);
+ break;
+ case STILL:
+ OnKeyDownStill(kbd.current);
+ break;
+ }
+ }
+
+ return true;
+}
+
+static void Scan() {
+ if (rssiHistory[scanInfo.i] != RSSI_MAX_VALUE) {
+ SetF(scanInfo.f);
+ Measure();
+ UpdateScanInfo();
+ }
+}
+
+static void NextScanStep() {
+ ++peak.t;
+ ++scanInfo.i;
+ scanInfo.f += scanInfo.scanStep;
+}
+
+static void UpdateScan() {
+ Scan();
+
+ if (scanInfo.i < scanInfo.measurementsCount) {
+ NextScanStep();
+ return;
+ }
+
+ redrawScreen = true;
+ preventKeypress = false;
+
+ UpdatePeakInfo();
+ if (IsPeakOverLevel()) {
+ ToggleRX(true);
+ TuneToPeak();
+ return;
+ }
+
+ newScanStart = true;
+}
+
+static void UpdateStill() {
+ Measure();
+ redrawScreen = true;
+ preventKeypress = false;
+
+ peak.rssi = scanInfo.rssi;
+ AutoTriggerLevel();
+
+ ToggleRX(IsPeakOverLevel() || monitorMode);
+}
+
+static void UpdateListening() {
+ preventKeypress = false;
+ if (currentState == STILL) {
+ listenT = 0;
+ }
+ if (listenT) {
+ listenT--;
+ SYSTEM_DelayMs(1);
+ return;
+ }
+
+ if (currentState == SPECTRUM) {
+ BK4819_WriteRegister(0x43, GetBWRegValueForScan());
+ Measure();
+ BK4819_WriteRegister(0x43, listenBWRegValues[settings.listenBw]);
+ } else {
+ Measure();
+ }
+
+ peak.rssi = scanInfo.rssi;
+ redrawScreen = true;
+
+ if (IsPeakOverLevel() || monitorMode) {
+ listenT = 1000;
+ return;
+ }
+
+ ToggleRX(false);
+ newScanStart = true;
+}
+
+static void Tick() {
+ if (!preventKeypress) {
+ HandleUserInput();
+ }
+ if (newScanStart) {
+ InitScan();
+ newScanStart = false;
+ }
+ if (isListening && currentState != FREQ_INPUT) {
+ UpdateListening();
+ } else {
+ if (currentState == SPECTRUM) {
+ UpdateScan();
+ } else if (currentState == STILL) {
+ UpdateStill();
+ }
+ }
+ if (redrawStatus || ++statuslineUpdateTimer > 4096) {
+ RenderStatus();
+ redrawStatus = false;
+ statuslineUpdateTimer = 0;
+ }
+ if (redrawScreen) {
+ Render();
+ redrawScreen = false;
+ }
+}
+
+void APP_RunSpectrum() {
+ // TX here coz it always? set to active VFO
+ currentFreq = initialFreq =
+ gEeprom.VfoInfo[gEeprom.TX_VFO].pRX->Frequency;
+
+ BackupRegisters();
+
+ isListening = true; // to turn off RX later
+ redrawStatus = true;
+ redrawScreen = false; // we will wait until scan done
+ newScanStart = true;
+
+ ToggleRX(true), ToggleRX(false); // hack to prevent noise when squelch off
+ SetModulation(settings.modulationType = MOD_FM);
+ BK4819_SetFilterBandwidth(settings.listenBw = BK4819_FILTER_BW_WIDE, false);
+
+ RelaunchScan();
+
+ for (int i = 0; i < 128; ++i) {
+ rssiHistory[i] = 0;
+ }
+
+ isInitialized = true;
+
+ while (isInitialized) {
+ Tick();
+ }
+}
diff --git a/app/spectrum.h b/app/spectrum.h
new file mode 100644
index 0000000..61ac358
--- /dev/null
+++ b/app/spectrum.h
@@ -0,0 +1,171 @@
+/* Copyright 2023 fagci
+ * https://github.com/fagci
+ *
+ * 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 SPECTRUM_H
+#define SPECTRUM_H
+
+#include "../bitmaps.h"
+#include "../board.h"
+#include "../bsp/dp32g030/gpio.h"
+#include "../driver/bk4819-regs.h"
+#include "../driver/bk4819.h"
+#include "../driver/gpio.h"
+#include "../driver/keyboard.h"
+#include "../driver/st7565.h"
+#include "../driver/system.h"
+#include "../driver/systick.h"
+#include "../external/printf/printf.h"
+#include "../font.h"
+#include "../frequencies.h"
+#include "../helper/battery.h"
+#include "../misc.h"
+#include "../radio.h"
+#include "../settings.h"
+#include "../ui/helper.h"
+#include
+#include
+#include
+
+static const uint8_t DrawingEndY = 40;
+
+static const uint8_t U8RssiMap[] = {
+ 121, 115, 109, 103, 97, 91, 85, 79, 73, 63,
+};
+
+static const uint16_t scanStepValues[] = {
+ 1, 10, 50, 100,
+
+ 250, 500, 625, 833, 1000, 1250, 2500, 10000,
+};
+
+static const uint16_t scanStepBWRegValues[] = {
+ // RX RXw TX BW
+ // 0b0 000 000 001 01 1000
+ // 1
+ 0b0000000001011000, // 6.25
+ // 10
+ 0b0000000001011000, // 6.25
+ // 50
+ 0b0000000001011000, // 6.25
+ // 100
+ 0b0000000001011000, // 6.25
+ // 250
+ 0b0000000001011000, // 6.25
+ // 500
+ 0b0010010001011000, // 6.25
+ // 625
+ 0b0100100001011000, // 6.25
+ // 833
+ 0b0110110001001000, // 6.25
+ // 1000
+ 0b0110110001001000, // 6.25
+ // 1250
+ 0b0111111100001000, // 6.25
+ // 2500
+ 0b0011011000101000, // 25
+ // 10000
+ 0b0011011000101000, // 25
+};
+
+static const uint16_t listenBWRegValues[] = {
+ 0b0011011000101000, // 25
+ 0b0111111100001000, // 12.5
+ 0b0100100001011000, // 6.25
+};
+
+typedef enum State {
+ SPECTRUM,
+ FREQ_INPUT,
+ STILL,
+} State;
+
+typedef enum StepsCount {
+ STEPS_128,
+ STEPS_64,
+ STEPS_32,
+ STEPS_16,
+} StepsCount;
+
+typedef enum ModulationType {
+ MOD_FM,
+ MOD_AM,
+ MOD_USB,
+} ModulationType;
+
+typedef enum ScanStep {
+ S_STEP_0_01kHz,
+ S_STEP_0_1kHz,
+ S_STEP_0_5kHz,
+ S_STEP_1_0kHz,
+
+ S_STEP_2_5kHz,
+ S_STEP_5_0kHz,
+ S_STEP_6_25kHz,
+ S_STEP_8_33kHz,
+ S_STEP_10_0kHz,
+ S_STEP_12_5kHz,
+ S_STEP_25_0kHz,
+ S_STEP_100_0kHz,
+} ScanStep;
+
+typedef struct SpectrumSettings {
+ uint32_t frequencyChangeStep;
+ StepsCount stepsCount;
+ ScanStep scanStepIndex;
+ uint16_t scanDelay;
+ uint16_t rssiTriggerLevel;
+ BK4819_FilterBandwidth_t bw;
+ BK4819_FilterBandwidth_t listenBw;
+ int dbMin;
+ int dbMax;
+ ModulationType modulationType;
+ bool backlightState;
+} SpectrumSettings;
+
+typedef struct KeyboardState {
+ KEY_Code_t current;
+ KEY_Code_t prev;
+ uint8_t counter;
+} KeyboardState;
+
+typedef struct ScanInfo {
+ uint16_t rssi, rssiMin, rssiMax;
+ uint8_t i, iPeak;
+ uint32_t f, fPeak;
+ uint16_t scanStep;
+ uint8_t measurementsCount;
+} ScanInfo;
+
+typedef struct RegisterSpec {
+ char *name;
+ uint8_t num;
+ uint8_t offset;
+ uint16_t maxValue;
+ uint16_t inc;
+} RegisterSpec;
+
+typedef struct PeakInfo {
+ uint16_t t;
+ uint16_t rssi;
+ uint32_t f;
+ uint8_t i;
+} PeakInfo;
+
+void APP_RunSpectrum(void);
+
+#endif /* ifndef SPECTRUM_H */
+
+// vim: ft=c
diff --git a/compile-with-docker.bat b/compile-with-docker.bat
new file mode 100644
index 0000000..42adad1
--- /dev/null
+++ b/compile-with-docker.bat
@@ -0,0 +1,4 @@
+@echo off
+docker build -t uvk5 .
+docker run -v %CD%\compiled-firmware:/app/compiled-firmware uvk5 /bin/bash -c "cd /app && make clean && make && cp firmware* compiled-firmware/"
+pause
\ No newline at end of file
diff --git a/compile-with-docker.sh b/compile-with-docker.sh
new file mode 100644
index 0000000..fb5dd01
--- /dev/null
+++ b/compile-with-docker.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+docker build -t uvk5 .
+docker run -v $(PWD)/compiled-firmware:/app/compiled-firmware uvk5 /bin/bash -c "cd /app && make && cp firmware* compiled-firmware/"
\ No newline at end of file
diff --git a/driver/bk4819.c b/driver/bk4819.c
index d485c96..0d921d7 100644
--- a/driver/bk4819.c
+++ b/driver/bk4819.c
@@ -1004,6 +1004,42 @@ void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch)
BK4819_WriteRegister(BK4819_REG_71, scale_freq(Frequency));
}
+void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay, const unsigned int level, const bool play_speaker)
+{
+ BK4819_EnterTxMute();
+
+ if (play_speaker)
+ {
+ GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
+ BK4819_SetAF(BK4819_AF_BEEP);
+ }
+ else
+ BK4819_SetAF(BK4819_AF_MUTE);
+
+ // level 0 ~ 127
+// BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (96u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
+// BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (28u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
+ BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | ((level & 0x7f) << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
+
+ BK4819_EnableTXLink();
+ SYSTEM_DelayMs(50);
+
+ BK4819_WriteRegister(BK4819_REG_71, scale_freq(tone_Hz));
+
+ BK4819_ExitTxMute();
+ SYSTEM_DelayMs(delay);
+ BK4819_EnterTxMute();
+
+ if (play_speaker)
+ {
+ GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
+ BK4819_SetAF(BK4819_AF_MUTE);
+ }
+
+ BK4819_WriteRegister(BK4819_REG_70, 0x0000);
+ BK4819_WriteRegister(BK4819_REG_30, 0xC1FE);
+}
+
void BK4819_EnterTxMute(void)
{
BK4819_WriteRegister(BK4819_REG_50, 0xBB20);
@@ -1287,7 +1323,7 @@ void BK4819_TransmitTone(bool bLocalLoopback, uint32_t Frequency)
// set the tone amplitude
//
// BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_MASK_ENABLE_TONE1 | (96u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
- BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_MASK_ENABLE_TONE1 | (50u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
+ BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_MASK_ENABLE_TONE1 | (28u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
BK4819_WriteRegister(BK4819_REG_71, scale_freq(Frequency));
@@ -1631,7 +1667,9 @@ void BK4819_PlayRoger(void)
BK4819_EnterTxMute();
BK4819_SetAF(BK4819_AF_MUTE);
- BK4819_WriteRegister(BK4819_REG_70, 0xE000); // 1110 0000 0000 0000
+
+// BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (96u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
+ BK4819_WriteRegister(BK4819_REG_70, BK4819_REG_70_ENABLE_TONE1 | (28u << BK4819_REG_70_SHIFT_TONE1_TUNING_GAIN));
BK4819_EnableTXLink();
SYSTEM_DelayMs(50);
diff --git a/driver/bk4819.h b/driver/bk4819.h
index 352bbf5..736b171 100644
--- a/driver/bk4819.h
+++ b/driver/bk4819.h
@@ -102,6 +102,7 @@ void BK4819_DisableVox(void);
void BK4819_DisableDTMF(void);
void BK4819_EnableDTMF(void);
void BK4819_PlayTone(uint16_t Frequency, bool bTuningGainSwitch);
+void BK4819_PlaySingleTone(const unsigned int tone_Hz, const unsigned int delay, const unsigned int level, const bool play_speaker);
void BK4819_EnterTxMute(void);
void BK4819_ExitTxMute(void);
void BK4819_Sleep(void);
diff --git a/firmware.bin b/firmware.bin
index 9858669..01983d5 100644
Binary files a/firmware.bin and b/firmware.bin differ
diff --git a/firmware.packed.bin b/firmware.packed.bin
index 3b838c8..be0723a 100644
Binary files a/firmware.packed.bin and b/firmware.packed.bin differ
diff --git a/font.c b/font.c
index 1aba955..3d0e3b2 100644
--- a/font.c
+++ b/font.c
@@ -19,6 +19,7 @@
//const uint8_t gFontBig[95][16] =
const uint8_t gFontBig[95][15] =
{
+#if 0
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // , 0x00}, // ' '
{0x00, 0x00, 0x70, 0xF8, 0xF8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x1B, 0x00, 0x00}, // , 0x00}, // '!'
{0x00, 0x1E, 0x3E, 0x00, 0x00, 0x3E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // , 0x00}, // '"'
@@ -114,7 +115,104 @@ const uint8_t gFontBig[95][15] =
{0x00, 0x00, 0x00, 0x78, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00}, // , 0x00}, // '|'
{0x00, 0x08, 0x08, 0x78, 0xF0, 0x80, 0x80, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x0F, 0x00, 0x00}, // , 0x00}, // '}'
{0x10, 0x18, 0x08, 0x18, 0x10, 0x18, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // , 0x00} // '->'
-};
+#else
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00},
+ {0x00, 0x0F, 0x1F, 0x00, 0x00, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x20, 0xF8, 0xF8, 0x20, 0xF8, 0xF8, 0x20, 0x00, 0x02, 0x0F, 0x0F, 0x02, 0x0F, 0x0F, 0x02},
+ {0x70, 0xF8, 0x88, 0x8E, 0x8E, 0x98, 0x10, 0x00, 0x04, 0x0C, 0x08, 0x38, 0x38, 0x0F, 0x07},
+ {0x30, 0x30, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x00, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x0C, 0x0C},
+ {0x80, 0xD8, 0x7C, 0xE4, 0xBC, 0xD8, 0x40, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x07, 0x0F, 0x08},
+ {0x00, 0x10, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0xF0, 0xF8, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x0C, 0x08, 0x00},
+ {0x00, 0x00, 0x04, 0x0C, 0xF8, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0C, 0x07, 0x03, 0x00},
+ {0x00, 0x80, 0xA0, 0xE0, 0xC0, 0xE0, 0xA0, 0x80, 0x00, 0x00, 0x02, 0x03, 0x01, 0x03, 0x02},
+ {0x00, 0x80, 0x80, 0xE0, 0xE0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1E, 0x0E, 0x00, 0x00},
+ {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x00, 0x0C, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00},
+ {0xF8, 0xFC, 0x84, 0xC4, 0x64, 0xFC, 0xF8, 0x00, 0x07, 0x0F, 0x09, 0x08, 0x08, 0x0F, 0x07},
+ {0x00, 0x10, 0x18, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0F, 0x0F, 0x08, 0x08},
+ {0x18, 0x1C, 0x04, 0x84, 0xC4, 0x7C, 0x38, 0x00, 0x0C, 0x0E, 0x0B, 0x09, 0x08, 0x08, 0x08},
+ {0x18, 0x1C, 0x44, 0x44, 0x44, 0xFC, 0xB8, 0x00, 0x06, 0x0E, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0x80, 0xC0, 0x60, 0x30, 0x18, 0xFC, 0xFC, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0F, 0x0F},
+ {0x7C, 0x7C, 0x44, 0x44, 0x44, 0xC4, 0x84, 0x00, 0x04, 0x0C, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xF0, 0xF8, 0x4C, 0x44, 0x44, 0xC4, 0x80, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0x04, 0x04, 0x04, 0x84, 0xE4, 0x7C, 0x1C, 0x00, 0x00, 0x00, 0x0E, 0x0F, 0x01, 0x00, 0x00},
+ {0xB8, 0xFC, 0x44, 0x44, 0x44, 0xFC, 0xB8, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0x78, 0xFC, 0x84, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x00, 0x08, 0x08, 0x08, 0x0C, 0x07, 0x03},
+ {0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0E, 0x06, 0x00, 0x00},
+ {0x00, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x08, 0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x0C, 0x08},
+ {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02},
+ {0x00, 0x08, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x08, 0x0C, 0x06, 0x03, 0x01, 0x00},
+ {0x38, 0x3C, 0x04, 0x84, 0xC4, 0x7C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00},
+ {0xF0, 0xF8, 0x08, 0xC8, 0xC8, 0xF8, 0xF0, 0x00, 0x07, 0x0F, 0x08, 0x0B, 0x0B, 0x0B, 0x01},
+ {0xF8, 0xFC, 0x84, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F},
+ {0xFC, 0xFC, 0x44, 0x44, 0x44, 0xFC, 0xB8, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xF8, 0xFC, 0x04, 0x04, 0x04, 0x1C, 0x18, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0E, 0x06},
+ {0xFC, 0xFC, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x0C, 0x07, 0x03},
+ {0xFC, 0xFC, 0x44, 0x44, 0x44, 0x04, 0x04, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x08},
+ {0xFC, 0xFC, 0x44, 0x44, 0x44, 0x04, 0x04, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF8, 0xFC, 0x04, 0x84, 0x84, 0x9C, 0x98, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xFC, 0xFC, 0x40, 0x40, 0x40, 0xFC, 0xFC, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F},
+ {0x00, 0x00, 0x04, 0xFC, 0xFC, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0F, 0x0F, 0x08, 0x00},
+ {0x00, 0x00, 0x00, 0x04, 0xFC, 0xFC, 0x04, 0x00, 0x06, 0x0E, 0x08, 0x08, 0x0F, 0x07, 0x00},
+ {0xFC, 0xFC, 0xE0, 0x30, 0x18, 0x0C, 0x04, 0x00, 0x0F, 0x0F, 0x01, 0x03, 0x06, 0x0C, 0x08},
+ {0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x08, 0x08, 0x08},
+ {0xFC, 0xFC, 0x18, 0x70, 0x18, 0xFC, 0xFC, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F},
+ {0xFC, 0xFC, 0x60, 0xC0, 0x80, 0xFC, 0xFC, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0F},
+ {0xF8, 0xFC, 0x04, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xFC, 0xFC, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xF8, 0xFC, 0x04, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x07, 0x0F, 0x08, 0x0C, 0x0C, 0x1F, 0x17},
+ {0xFC, 0xFC, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x0F, 0x0F, 0x01, 0x03, 0x06, 0x0C, 0x08},
+ {0x38, 0x7C, 0x44, 0x44, 0x44, 0xCC, 0x88, 0x00, 0x06, 0x0E, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0x00, 0x04, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00},
+ {0xFC, 0xFC, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0x7C, 0xFC, 0x80, 0x00, 0x80, 0xFC, 0x7C, 0x00, 0x00, 0x03, 0x0F, 0x0C, 0x0F, 0x03, 0x00},
+ {0xFC, 0xFC, 0x00, 0x80, 0x00, 0xFC, 0xFC, 0x00, 0x0F, 0x0F, 0x06, 0x03, 0x06, 0x0F, 0x0F},
+ {0x0C, 0x3C, 0xF0, 0xC0, 0xF0, 0x3C, 0x0C, 0x00, 0x0C, 0x0F, 0x03, 0x00, 0x03, 0x0F, 0x0C},
+ {0x00, 0x3C, 0x7C, 0xC0, 0xC0, 0x7C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00},
+ {0x04, 0x04, 0x84, 0xC4, 0x64, 0x3C, 0x1C, 0x00, 0x0E, 0x0F, 0x09, 0x08, 0x08, 0x08, 0x08},
+ {0x00, 0x00, 0xFC, 0xFC, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x00},
+ {0x38, 0x70, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0E},
+ {0x00, 0x00, 0x04, 0x04, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0F, 0x0F, 0x00},
+ {0x08, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20},
+ {0x00, 0x00, 0x03, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x00, 0xA0, 0xA0, 0xA0, 0xA0, 0xE0, 0xC0, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x0F},
+ {0xFC, 0xFC, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x0F, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0x60, 0x40, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0C, 0x04},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0xFC, 0xFC, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x0F},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x07, 0x0F, 0x09, 0x09, 0x09, 0x09, 0x01},
+ {0x20, 0x20, 0xF8, 0xFC, 0x24, 0x24, 0x04, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x00, 0x07, 0x4F, 0x48, 0x48, 0x48, 0x7F, 0x3F},
+ {0xFC, 0xFC, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F},
+ {0x00, 0x00, 0x20, 0xEC, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0F, 0x0F, 0x08, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x20, 0xEC, 0xEC, 0x00, 0x00, 0x30, 0x70, 0x40, 0x40, 0x7F, 0x3F},
+ {0xFC, 0xFC, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x00, 0x0F, 0x0F, 0x01, 0x03, 0x06, 0x0C, 0x08},
+ {0x00, 0x00, 0x04, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0F, 0x0F, 0x08, 0x00},
+ {0xE0, 0xE0, 0x20, 0xE0, 0x20, 0xE0, 0xC0, 0x00, 0x0F, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x0F},
+ {0xE0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xE0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x7F, 0x7F, 0x08, 0x08, 0x08, 0x0F, 0x07},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x7F, 0x7F},
+ {0xE0, 0xE0, 0x60, 0x20, 0x20, 0x20, 0x20, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xC0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x08, 0x09, 0x09, 0x09, 0x09, 0x0F, 0x06},
+ {0x20, 0x20, 0xFC, 0xFC, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08},
+ {0xE0, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x07, 0x0F, 0x08, 0x08, 0x08, 0x0F, 0x0F},
+ {0xE0, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0x03, 0x0F, 0x0C, 0x0F, 0x03, 0x00},
+ {0xE0, 0xE0, 0x00, 0x80, 0x00, 0xE0, 0xE0, 0x00, 0x07, 0x0F, 0x08, 0x0F, 0x08, 0x0F, 0x07},
+ {0x60, 0xE0, 0x80, 0x00, 0x80, 0xE0, 0x60, 0x00, 0x0C, 0x0E, 0x03, 0x01, 0x03, 0x0E, 0x0C},
+ {0xE0, 0xE0, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x07, 0x4F, 0x48, 0x48, 0x48, 0x7F, 0x3F},
+ {0x20, 0x20, 0x20, 0xA0, 0xE0, 0x60, 0x20, 0x00, 0x0C, 0x0E, 0x0B, 0x09, 0x08, 0x08, 0x08},
+ {0x00, 0x00, 0x40, 0xF8, 0xBC, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x08, 0x08},
+ {0x00, 0x00, 0x00, 0xBC, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00},
+ {0x00, 0x04, 0x04, 0xBC, 0xF8, 0x40, 0x00, 0x00, 0x00, 0x08, 0x08, 0x0F, 0x07, 0x00, 0x00},
+ {0x08, 0x0C, 0x04, 0x0C, 0x08, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+#endif
+};
#if 0
// original font
@@ -132,7 +230,7 @@ const uint8_t gFontBig[95][15] =
{0x00, 0xF0, 0xF8, 0xB8, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1C, 0xB8, 0xF0, 0xE0, 0x00, 0x11, 0x33, 0x77, 0x67, 0x66, 0x66, 0x66, 0x76, 0x33, 0x3F, 0x1F, 0x07},
{0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00}
};
-#else
+#elif 0
// VCR font
const uint8_t gFontBigDigits[11][26] =
{
@@ -148,6 +246,22 @@ const uint8_t gFontBig[95][15] =
{0x00, 0x00, 0x78, 0xFC, 0xC6, 0x86, 0x86, 0x86, 0x86, 0x86, 0xFC, 0xF8, 0x00, 0x00, 0x00, 0x18, 0x38, 0x71, 0x61, 0x61, 0x61, 0x61, 0x71, 0x3F, 0x1F, 0x00},
{0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}
};
+#else
+ // Terminus font
+ const uint8_t gFontBigDigits[11][26] =
+ {
+ {0x00, 0x00, 0xFC, 0xFE, 0xFE, 0x06, 0x86, 0xC6, 0xE6, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x7F, 0x67, 0x63, 0x61, 0x60, 0x7F, 0x7F, 0x3F, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x7F, 0x7F, 0x7F, 0x60, 0x60, 0x00, 0x00},
+ {0x00, 0x00, 0x1C, 0x1E, 0x1E, 0x06, 0x06, 0x06, 0x86, 0xFE, 0xFE, 0x7C, 0x00, 0x00, 0x00, 0x60, 0x70, 0x78, 0x7C, 0x6E, 0x67, 0x63, 0x61, 0x60, 0x60, 0x00},
+ {0x00, 0x00, 0x0C, 0x0E, 0x0E, 0x86, 0x86, 0x86, 0x86, 0xFE, 0xFE, 0x7C, 0x00, 0x00, 0x00, 0x30, 0x70, 0x70, 0x61, 0x61, 0x61, 0x61, 0x7F, 0x7F, 0x3E, 0x00},
+ {0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0C, 0x0C, 0x0C, 0x0C, 0x7F, 0x7F, 0x7F, 0x00},
+ {0x00, 0x00, 0xFE, 0xFE, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x30, 0x70, 0x70, 0x60, 0x60, 0x60, 0x60, 0x7F, 0x7F, 0x3F, 0x00},
+ {0x00, 0x00, 0xF8, 0xFC, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x80, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x7F, 0x60, 0x60, 0x60, 0x60, 0x7F, 0x7F, 0x3F, 0x00},
+ {0x00, 0x00, 0x0E, 0x0E, 0x0E, 0x06, 0x06, 0x86, 0xE6, 0xFE, 0x7E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7F, 0x7F, 0x03, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x7C, 0xFE, 0xFE, 0x86, 0x86, 0x86, 0x86, 0xFE, 0xFE, 0x7C, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0x7F, 0x61, 0x61, 0x61, 0x61, 0x7F, 0x7F, 0x3F, 0x00},
+ {0x00, 0x00, 0xFC, 0xFE, 0xFE, 0x06, 0x06, 0x06, 0x06, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7F, 0x3F, 0x1F, 0x00},
+ {0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}
+ };
#endif
/*
const uint8_t gFontSmallDigits[11][7] =
@@ -363,4 +477,170 @@ const uint8_t gFontSmall[95][6] =
{0x00, 0x41, 0x77, 0x3E, 0x08, 0x00},
{0x0C, 0x06, 0x0C, 0x18, 0x0C, 0x00}
};
-#endif
\ No newline at end of file
+#endif
+
+#ifdef ENABLE_SPECTRUM
+ const uint8_t gFont3x5[160][3] =
+ {
+ {0x00, 0x00, 0x00}, // 32 - space
+ {0x00, 0x17, 0x00}, // 33 - exclam
+ {0x03, 0x00, 0x03}, // 34 - quotedbl
+ {0x1f, 0x0a, 0x1f}, // 35 - numbersign
+ {0x0a, 0x1f, 0x05}, // 36 - dollar
+ {0x09, 0x04, 0x12}, // 37 - percent
+ {0x0f, 0x17, 0x1c}, // 38 - ampersand
+ {0x00, 0x03, 0x00}, // 39 - quotesingle
+ {0x00, 0x0e, 0x11}, // 40 - parenleft
+ {0x11, 0x0e, 0x00}, // 41 - parenright
+ {0x05, 0x02, 0x05}, // 42 - asterisk
+ {0x04, 0x0e, 0x04}, // 43 - plus
+ {0x10, 0x08, 0x00}, // 44 - comma
+ {0x04, 0x04, 0x04}, // 45 - hyphen
+ {0x00, 0x10, 0x00}, // 46 - period
+ {0x18, 0x04, 0x03}, // 47 - slash
+ {0x1e, 0x11, 0x0f}, // 48 - zero
+ {0x02, 0x1f, 0x00}, // 49 - one
+ {0x19, 0x15, 0x12}, // 50 - two
+ {0x11, 0x15, 0x0a}, // 51 - three
+ {0x07, 0x04, 0x1f}, // 52 - four
+ {0x17, 0x15, 0x09}, // 53 - five
+ {0x1e, 0x15, 0x1d}, // 54 - six
+ {0x19, 0x05, 0x03}, // 55 - seven
+ {0x1f, 0x15, 0x1f}, // 56 - eight
+ {0x17, 0x15, 0x0f}, // 57 - nine
+ {0x00, 0x0a, 0x00}, // 58 - colon
+ {0x10, 0x0a, 0x00}, // 59 - semicolon
+ {0x04, 0x0a, 0x11}, // 60 - less
+ {0x0a, 0x0a, 0x0a}, // 61 - equal
+ {0x11, 0x0a, 0x04}, // 62 - greater
+ {0x01, 0x15, 0x03}, // 63 - question
+ {0x0e, 0x15, 0x16}, // 64 - at
+ {0x1e, 0x05, 0x1e}, // 65 - A
+ {0x1f, 0x15, 0x0a}, // 66 - B
+ {0x0e, 0x11, 0x11}, // 67 - C
+ {0x1f, 0x11, 0x0e}, // 68 - D
+ {0x1f, 0x15, 0x15}, // 69 - E
+ {0x1f, 0x05, 0x05}, // 70 - F
+ {0x0e, 0x15, 0x1d}, // 71 - G
+ {0x1f, 0x04, 0x1f}, // 72 - H
+ {0x11, 0x1f, 0x11}, // 73 - I
+ {0x08, 0x10, 0x0f}, // 74 - J
+ {0x1f, 0x04, 0x1b}, // 75 - K
+ {0x1f, 0x10, 0x10}, // 76 - L
+ {0x1f, 0x06, 0x1f}, // 77 - M
+ {0x1f, 0x0e, 0x1f}, // 78 - N
+ {0x0e, 0x11, 0x0e}, // 79 - O
+ {0x1f, 0x05, 0x02}, // 80 - P
+ {0x0e, 0x19, 0x1e}, // 81 - Q
+ {0x1f, 0x0d, 0x16}, // 82 - R
+ {0x12, 0x15, 0x09}, // 83 - S
+ {0x01, 0x1f, 0x01}, // 84 - T
+ {0x0f, 0x10, 0x1f}, // 85 - U
+ {0x07, 0x18, 0x07}, // 86 - V
+ {0x1f, 0x0c, 0x1f}, // 87 - W
+ {0x1b, 0x04, 0x1b}, // 88 - X
+ {0x03, 0x1c, 0x03}, // 89 - Y
+ {0x19, 0x15, 0x13}, // 90 - Z
+ {0x1f, 0x11, 0x11}, // 91 - bracketleft
+ {0x02, 0x04, 0x08}, // 92 - backslash
+ {0x11, 0x11, 0x1f}, // 93 - bracketright
+ {0x02, 0x01, 0x02}, // 94 - asciicircum
+ {0x10, 0x10, 0x10}, // 95 - underscore
+ {0x01, 0x02, 0x00}, // 96 - grave
+ {0x1a, 0x16, 0x1c}, // 97 - a
+ {0x1f, 0x12, 0x0c}, // 98 - b
+ {0x0c, 0x12, 0x12}, // 99 - c
+ {0x0c, 0x12, 0x1f}, // 100 - d
+ {0x0c, 0x1a, 0x16}, // 101 - e
+ {0x04, 0x1e, 0x05}, // 102 - f
+ {0x0c, 0x2a, 0x1e}, // 103 - g
+ {0x1f, 0x02, 0x1c}, // 104 - h
+ {0x00, 0x1d, 0x00}, // 105 - i
+ {0x10, 0x20, 0x1d}, // 106 - j
+ {0x1f, 0x0c, 0x12}, // 107 - k
+ {0x11, 0x1f, 0x10}, // 108 - l
+ {0x1e, 0x0e, 0x1e}, // 109 - m
+ {0x1e, 0x02, 0x1c}, // 110 - n
+ {0x0c, 0x12, 0x0c}, // 111 - o
+ {0x3e, 0x12, 0x0c}, // 112 - p
+ {0x0c, 0x12, 0x3e}, // 113 - q
+ {0x1c, 0x02, 0x02}, // 114 - r
+ {0x14, 0x1e, 0x0a}, // 115 - s
+ {0x02, 0x1f, 0x12}, // 116 - t
+ {0x0e, 0x10, 0x1e}, // 117 - u
+ {0x0e, 0x18, 0x0e}, // 118 - v
+ {0x1e, 0x1c, 0x1e}, // 119 - w
+ {0x12, 0x0c, 0x12}, // 120 - x
+ {0x06, 0x28, 0x1e}, // 121 - y
+ {0x1a, 0x1e, 0x16}, // 122 - z
+ {0x04, 0x1b, 0x11}, // 123 - braceleft
+ {0x00, 0x1b, 0x00}, // 124 - bar
+ {0x11, 0x1b, 0x04}, // 125 - braceright
+ {0x02, 0x03, 0x01}, // 126 - asciitilde
+ {0x00, 0x00, 0x00}, // 127 - empty
+ {0x00, 0x00, 0x00}, // 128 - empty
+ {0x00, 0x00, 0x00}, // 129 - empty
+ {0x00, 0x00, 0x00}, // 130 - empty
+ {0x00, 0x00, 0x00}, // 131 - empty
+ {0x00, 0x00, 0x00}, // 132 - empty
+ {0x00, 0x00, 0x00}, // 133 - empty
+ {0x00, 0x00, 0x00}, // 134 - empty
+ {0x00, 0x00, 0x00}, // 135 - empty
+ {0x00, 0x00, 0x00}, // 136 - empty
+ {0x00, 0x00, 0x00}, // 137 - empty
+ {0x00, 0x00, 0x00}, // 138 - empty
+ {0x00, 0x00, 0x00}, // 139 - empty
+ {0x00, 0x00, 0x00}, // 140 - empty
+ {0x00, 0x00, 0x00}, // 141 - empty
+ {0x00, 0x00, 0x00}, // 142 - empty
+ {0x00, 0x00, 0x00}, // 143 - empty
+ {0x00, 0x00, 0x00}, // 144 - empty
+ {0x00, 0x00, 0x00}, // 145 - empty
+ {0x00, 0x00, 0x00}, // 146 - empty
+ {0x00, 0x00, 0x00}, // 147 - empty
+ {0x00, 0x00, 0x00}, // 148 - empty
+ {0x00, 0x00, 0x00}, // 149 - empty
+ {0x00, 0x00, 0x00}, // 150 - empty
+ {0x00, 0x00, 0x00}, // 151 - empty
+ {0x00, 0x00, 0x00}, // 152 - empty
+ {0x00, 0x00, 0x00}, // 153 - empty
+ {0x00, 0x00, 0x00}, // 154 - empty
+ {0x00, 0x00, 0x00}, // 155 - empty
+ {0x00, 0x00, 0x00}, // 156 - empty
+ {0x00, 0x00, 0x00}, // 157 - empty
+ {0x00, 0x00, 0x00}, // 158 - empty
+ {0x00, 0x00, 0x00}, // 159 - empty
+ {0x00, 0x00, 0x00}, // 160 - empty
+ {0x00, 0x1d, 0x00}, // 161 - exclamdown
+ {0x0e, 0x1b, 0x0a}, // 162 - cent
+ {0x14, 0x1f, 0x15}, // 163 - sterling
+ {0x15, 0x0e, 0x15}, // 164 - currency
+ {0x0b, 0x1c, 0x0b}, // 165 - yen
+ {0x00, 0x1b, 0x00}, // 166 - brokenbar
+ {0x14, 0x1b, 0x05}, // 167 - section
+ {0x01, 0x00, 0x01}, // 168 - dieresis
+ {0x02, 0x05, 0x05}, // 169 - copyright
+ {0x16, 0x15, 0x17}, // 170 - ordfeminine
+ {0x02, 0x05, 0x00}, // 171 - guillemotleft
+ {0x02, 0x02, 0x06}, // 172 - logicalnot
+ {0x04, 0x04, 0x00}, // 173 - softhyphen
+ {0x07, 0x03, 0x04}, // 174 - registered
+ {0x01, 0x01, 0x01}, // 175 - macron
+ {0x02, 0x05, 0x02}, // 176 - degree
+ {0x12, 0x17, 0x12}, // 177 - plusminus
+ {0x01, 0x07, 0x04}, // 178 - twosuperior
+ {0x05, 0x07, 0x07}, // 179 - threesuperior
+ {0x00, 0x02, 0x01}, // 180 - acute
+ {0x1f, 0x08, 0x07}, // 181 - mu
+ {0x02, 0x1d, 0x1f}, // 182 - paragraph
+ {0x0e, 0x0e, 0x0e}, // 183 - periodcentered
+ {0x10, 0x14, 0x08}, // 184 - cedilla
+ {0x00, 0x07, 0x00}, // 185 - onesuperior
+ {0x12, 0x15, 0x12}, // 186 - ordmasculine
+ {0x00, 0x05, 0x02}, // 187 - guillemotright
+ {0x03, 0x08, 0x18}, // 188 - onequarter
+ {0x0b, 0x18, 0x10}, // 189 - onehalf
+ {0x03, 0x0b, 0x18}, // 190 - threequarters
+ {0x18, 0x15, 0x10}, // 191 - questiondown
+ };
+#endif
diff --git a/functions.c b/functions.c
index a302208..cd02ab1 100644
--- a/functions.c
+++ b/functions.c
@@ -204,6 +204,9 @@ void FUNCTION_Select(FUNCTION_Type_t Function)
DTMF_Reply();
+ if (gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_APOLLO)
+ BK4819_PlaySingleTone(2525, 250, 0, gEeprom.DTMF_SIDE_TONE);
+
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
if (gAlarmState != ALARM_STATE_OFF)
{
diff --git a/AM_fix.mp4 b/images/AM_fix.mp4
similarity index 100%
rename from AM_fix.mp4
rename to images/AM_fix.mp4
diff --git a/image1.png b/images/image1.png
similarity index 100%
rename from image1.png
rename to images/image1.png
diff --git a/image2.png b/images/image2.png
similarity index 100%
rename from image2.png
rename to images/image2.png
diff --git a/image3.png b/images/image3.png
similarity index 100%
rename from image3.png
rename to images/image3.png
diff --git a/radio.c b/radio.c
index 98b7907..38dd1b5 100644
--- a/radio.c
+++ b/radio.c
@@ -332,12 +332,12 @@ void RADIO_ConfigureChannel(const unsigned int VFO, const unsigned int configure
if (Data[5] == 0xFF)
{
gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = false;
- gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE = 0;
+ gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE = PTT_ID_OFF;
}
else
{
- gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = !!((Data[5] >> 0) & 1u);
- gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE = ((Data[5] >> 1) & 3u);
+ gEeprom.VfoInfo[VFO].DTMF_DECODING_ENABLE = ((Data[5] >> 0) & 1u) ? true : false;
+ gEeprom.VfoInfo[VFO].DTMF_PTT_ID_TX_MODE = ((Data[5] >> 1) & 7u);
}
// ***************
@@ -1080,8 +1080,12 @@ void RADIO_SendEndOfTransmission(void)
if (gEeprom.ROGER == ROGER_MODE_MDC)
BK4819_PlayRogerMDC();
+ if (gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_APOLLO)
+ BK4819_PlaySingleTone(2475, 250, 28, gEeprom.DTMF_SIDE_TONE);
+
if (gDTMF_CallState == DTMF_CALL_STATE_NONE &&
- (gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_TX_DOWN || gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_BOTH))
+ (gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_TX_DOWN ||
+ gCurrentVfo->DTMF_PTT_ID_TX_MODE == PTT_ID_BOTH))
{ // end-of-tx
if (gEeprom.DTMF_SIDE_TONE)
{
@@ -1093,15 +1097,14 @@ void RADIO_SendEndOfTransmission(void)
BK4819_EnterDTMF_TX(gEeprom.DTMF_SIDE_TONE);
BK4819_PlayDTMFString(
- gEeprom.DTMF_DOWN_CODE,
- 0,
- gEeprom.DTMF_FIRST_CODE_PERSIST_TIME,
- gEeprom.DTMF_HASH_CODE_PERSIST_TIME,
- gEeprom.DTMF_CODE_PERSIST_TIME,
- gEeprom.DTMF_CODE_INTERVAL_TIME);
-
+ gEeprom.DTMF_DOWN_CODE,
+ 0,
+ gEeprom.DTMF_FIRST_CODE_PERSIST_TIME,
+ gEeprom.DTMF_HASH_CODE_PERSIST_TIME,
+ gEeprom.DTMF_CODE_PERSIST_TIME,
+ gEeprom.DTMF_CODE_INTERVAL_TIME);
+
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_AUDIO_PATH);
-
gEnableSpeaker = false;
}
diff --git a/radio.h b/radio.h
index 66f1536..e1d03da 100644
--- a/radio.h
+++ b/radio.h
@@ -44,7 +44,8 @@ enum PTT_ID_t {
PTT_ID_OFF = 0, // OFF
PTT_ID_TX_UP, // BEGIN OF TX
PTT_ID_TX_DOWN, // END OF TX
- PTT_ID_BOTH // BOTH
+ PTT_ID_BOTH, // BOTH
+ PTT_ID_APOLLO // Apolo quindar tones
};
typedef enum PTT_ID_t PTT_ID_t;
diff --git a/settings.c b/settings.c
index b7821f8..1910c55 100644
--- a/settings.c
+++ b/settings.c
@@ -219,7 +219,7 @@ void SETTINGS_SaveChannel(uint8_t Channel, uint8_t VFO, const VFO_Info_t *pVFO,
| (pVFO->OUTPUT_POWER << 2)
| (pVFO->CHANNEL_BANDWIDTH << 1)
| (pVFO->FrequencyReverse << 0);
- State[5] = (pVFO->DTMF_PTT_ID_TX_MODE << 1) | (pVFO->DTMF_DECODING_ENABLE << 0);
+ State[5] = ((pVFO->DTMF_PTT_ID_TX_MODE & 7u) << 1) | ((pVFO->DTMF_DECODING_ENABLE & 1u) << 0);
State[6] = pVFO->STEP_SETTING;
State[7] = pVFO->SCRAMBLING_TYPE;
EEPROM_WriteBuffer(OffsetVFO + 8, State);
diff --git a/ui/menu.c b/ui/menu.c
index 26b3cc0..03984b7 100644
--- a/ui/menu.c
+++ b/ui/menu.c
@@ -240,12 +240,13 @@ const char gSubMenu_D_RSP[4][11] =
"BOTH"
};
-const char gSubMenu_PTT_ID[4][7] =
+const char gSubMenu_PTT_ID[5][15] =
{
"OFF",
- "KEY UP",
- "KEY DN",
- "BOTH"
+ "KEY\nUP",
+ "KEY\nDOWN",
+ "KEY\nUP+DOWN",
+ "APOLLO\nQUINDAR"
};
const char gSubMenu_PONMSG[4][8] =
diff --git a/ui/menu.h b/ui/menu.h
index d4d96fa..157b3d2 100644
--- a/ui/menu.h
+++ b/ui/menu.h
@@ -139,7 +139,7 @@ extern const char gSubMenu_MDF[4][15];
extern const char gSubMenu_AL_MOD[2][5];
#endif
extern const char gSubMenu_D_RSP[4][11];
-extern const char gSubMenu_PTT_ID[4][7];
+extern const char gSubMenu_PTT_ID[5][15];
extern const char gSubMenu_PONMSG[4][8];
extern const char gSubMenu_ROGER[3][9];
extern const char gSubMenu_RESET[2][4];