diff --git a/Makefile b/Makefile index f194b9204..8357e4e5b 100644 --- a/Makefile +++ b/Makefile @@ -7,11 +7,11 @@ ENABLE_SWD := 0 ENABLE_OVERLAY := 1 ENABLE_UART := 1 ENABLE_AIRCOPY := 0 -ENABLE_FMRADIO := 1 +ENABLE_FMRADIO := 0 ENABLE_NOAA := 0 ENABLE_VOICE := 0 -ENABLE_ALARM := 0 -ENABLE_BIG_FREQ := 0 +ENABLE_ALARM := 1 +ENABLE_BIG_FREQ := 1 ENABLE_SMALL_BOLD := 1 ENABLE_KEEP_MEM_NAME := 1 ENABLE_WIDE_RX := 1 @@ -28,6 +28,7 @@ ENABLE_AM_FIX_SHOW_DATA := 1 ENABLE_SQUELCH1_LOWER := 0 ENABLE_RSSI_BAR := 1 ENABLE_AUDIO_BAR := 1 +ENABLE_SPECTRUM := 1 #ENABLE_COPY_CHAN_TO_VFO := 1 #ENABLE_SINGLE_VFO_CHAN := 1 #ENABLE_BAND_SCOPE := 1 @@ -88,6 +89,9 @@ endif OBJS += app/generic.o OBJS += app/main.o OBJS += app/menu.o +ifeq ($(ENABLE_SPECTRUM), 1) +OBJS += app/spectrum.o +endif OBJS += app/scanner.o ifeq ($(ENABLE_UART),1) OBJS += app/uart.o @@ -156,6 +160,10 @@ CFLAGS = -Os -Wall -Werror -mcpu=cortex-m0 -fno-builtin -fshort-enums -fno-delet CFLAGS += -DPRINTF_INCLUDE_CONFIG_H CFLAGS += -DGIT_HASH=\"$(GIT_HASH)\" + +ifeq ($(ENABLE_SPECTRUM),1) +CFLAGS += -DENABLE_SPECTRUM +endif ifeq ($(ENABLE_SWD),1) CFLAGS += -DENABLE_SWD endif diff --git a/app/main.c b/app/main.c index e2e5f5ac3..715efcc26 100644 --- a/app/main.c +++ b/app/main.c @@ -24,6 +24,9 @@ #include "app/generic.h" #include "app/main.h" #include "app/scanner.h" +#ifdef ENABLE_SPECTRUM +#include "app/spectrum.h" +#endif #include "audio.h" #include "driver/bk4819.h" #include "dtmf.h" @@ -176,7 +179,10 @@ static void processFKeyFunction(const KEY_Code_t Key, const bool beep) } gRequestSaveVFO = true; gVfoConfigureMode = VFO_CONFIGURE_RELOAD; - #else + #elif defined(ENABLE_SPECTRUM) + APP_RunSpectrum(); + gRequestDisplayScreen = DISPLAY_MAIN; + #else // toggle scanlist-1 and scanlist 2 if (gScreenToDisplay != DISPLAY_SCANNER) { diff --git a/app/spectrum.c b/app/spectrum.c new file mode 100644 index 000000000..f9f02e56c --- /dev/null +++ b/app/spectrum.c @@ -0,0 +1,1185 @@ +/* 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 = {STEPS_64, + S_STEP_25_0kHz, + 80000, + 3200, + 0, + true, + BK4819_FILTER_BW_WIDE, + BK4819_FILTER_BW_WIDE, + false, + -130, + -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); + BK4819_WriteRegister(BK4819_REG_30, 0); + BK4819_WriteRegister(BK4819_REG_30, 0xbff1); +} + +// 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[0]; } + +uint16_t GetRssi() { + SYSTICK_DelayUs(settings.scanDelay); + /* while ((BK4819_ReadRegister(0x63) & 0b11111111) >= 255) + ; */ + 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); + settings.rssiTriggerLevel = RSSI_MAX_VALUE; + 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; +} + +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; + } + // RelaunchScan(); + redrawStatus = 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(); + redrawStatus = 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(); + redrawStatus = 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 + +uint8_t Rssi2PX(uint16_t rssi, uint8_t pxMin, uint8_t pxMax) { + const int DB_MIN = settings.dbMin; + const int DB_MAX = settings.dbMax; + const int DB_RANGE = DB_MAX - DB_MIN; + + const uint8_t PX_RANGE = pxMax - pxMin; + + int dbm = clamp(Rssi2DBm(rssi), 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() { + sprintf(String, "%d/%d", settings.dbMin, settings.dbMax); + GUI_DisplaySmallest(String, 0, 2, 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, 0, 127, 0); + + sprintf(String, "%s", modulationTypeOptions[settings.modulationType]); + GUI_DisplaySmallest(String, 115, 1, false, true); + sprintf(String, "%s", bwOptions[settings.listenBw]); + GUI_DisplaySmallest(String, 107, 7, false, true); +} + +static void DrawNums() { + sprintf(String, "P:%d", Rssi2DBm(peak.rssi)); + GUI_DisplaySmallest(String, 32, 8, false, true); + sprintf(String, "T:%d", Rssi2DBm(settings.rssiTriggerLevel)); + GUI_DisplaySmallest(String, 64, 8, false, true); + + 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); + sprintf(String, "V: %d", scanInfo.rssi); + GUI_DisplaySmallest(String, 64, 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) { + SetF(fMeasure); + 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_CHANNEL].pRX->Frequency; + + BackupRegisters(); + + isListening = true; // to turn off RX later + redrawStatus = true; + redrawScreen = false; // we will wait until scan done + newScanStart = true; + + ToggleRX(false); + 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 000000000..d812dd0f8 --- /dev/null +++ b/app/spectrum.h @@ -0,0 +1,172 @@ +/* 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 { + StepsCount stepsCount; + ScanStep scanStepIndex; + uint32_t frequencyChangeStep; + uint16_t scanDelay; + uint16_t rssiTriggerLevel; + + bool backlightState; + BK4819_FilterBandwidth_t bw; + BK4819_FilterBandwidth_t listenBw; + ModulationType modulationType; + int dbMin; + int dbMax; +} 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; + uint8_t i; + uint32_t f; +} PeakInfo; + +void APP_RunSpectrum(void); + +#endif /* ifndef SPECTRUM_H */ + +// vim: ft=c diff --git a/board.h b/board.h index 74e60d243..a65d1c9ac 100644 --- a/board.h +++ b/board.h @@ -18,6 +18,7 @@ #define BOARD_H #include +#include void BOARD_FLASH_Init(void); void BOARD_GPIO_Init(void); diff --git a/font.c b/font.c index b820807b9..f5d563f29 100644 --- a/font.c +++ b/font.c @@ -165,6 +165,174 @@ const uint8_t gFontSmallDigits[11][7] = {0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00} // '-' }; */ + +#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 + const uint8_t gFontSmall[95][6] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // ' ' diff --git a/font.h b/font.h index 1c07fb8fa..8e42afe21 100644 --- a/font.h +++ b/font.h @@ -23,6 +23,7 @@ extern const uint8_t gFontBig[95][15]; extern const uint8_t gFontBigDigits[11][26]; //extern const uint8_t gFontSmallDigits[11][7]; +extern const uint8_t gFont3x5[160][3]; extern const uint8_t gFontSmall[95][6]; #ifdef ENABLE_SMALL_BOLD extern const uint8_t gFontSmallBold[95][6]; diff --git a/version.c b/version.c index b49633373..4f407bff2 100644 --- a/version.c +++ b/version.c @@ -11,6 +11,6 @@ const char Version[] = "OEFW-"VER; const char UART_Version[] = "UV-K5 Firmware, Open Edition, OEFW-"VER"\r\n"; #else - const char Version[] = "1o11-"VER; - const char UART_Version[] = "UV-K5 Firmware, Open Edition, 1o11-"VER"\r\n"; + const char Version[] = "1o11+fagci-"VER; + const char UART_Version[] = "UV-K5 Firmware, Open Edition, 1o11+fagci-"VER"\r\n"; #endif