UA1ZBE Custom Firmware v1.0
Features: - VFO mode with direct frequency input - POCSAG decoder (512/1200 baud) with BCH(31,21) correction - Full-screen Spectrum analyzer - FM Radio receiver - RSSI signal indicator overlay - Custom boot splash (UA1ZBE / POCSAG pager / build date) Architecture: - app/mode.c — mode dispatcher (VFO/POCSAG/Spectrum/FM) - app/boot_splash.c — 2-second boot splash - app/pocsag/ — POCSAG decoder + BCH correction - app/display_rssi.c — RSSI indicator - main.c — entry point with custom init - syscalls.c — bare-metal _sbrk stub Build: arm-none-eabi-gcc -Os -flto -Wall -Werror -Wextra Size: 57.9KB Flash / 3.6KB RAM Controls: - 0-9: Direct frequency input (VFO) - SK2: POCSAG mode - SK1: Spectrum analyzer - 0: FM Radio - EXIT: Return to VFO - F/*: Toggle 512/1200 baud (in POCSAG)
This commit is contained in:
72
ui/aircopy.c
Normal file
72
ui/aircopy.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/aircopy.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "ui/aircopy.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
|
||||
void UI_DisplayAircopy(void)
|
||||
{
|
||||
char String[16] = { 0 };
|
||||
char *pPrintStr = { 0 };
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gAircopyState == AIRCOPY_READY) {
|
||||
pPrintStr = "AIR COPY(RDY)";
|
||||
} else if (gAircopyState == AIRCOPY_TRANSFER) {
|
||||
pPrintStr = "AIR COPY";
|
||||
} else {
|
||||
pPrintStr = "AIR COPY(CMP)";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 127, 0, 8);
|
||||
|
||||
if (gInputBoxIndex == 0) {
|
||||
uint32_t frequency = gRxVfo->freq_config_RX.Frequency;
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 97, 0, 3);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
} else {
|
||||
const char *ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s", ascii, ascii + 3);
|
||||
UI_DisplayFrequency(String, 16, 2, false);
|
||||
}
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAirCopyIsSendMode == 0) {
|
||||
sprintf(String, "RCV:%u E:%u", gAirCopyBlockNumber, gErrorsDuringAirCopy);
|
||||
} else if (gAirCopyIsSendMode == 1) {
|
||||
sprintf(String, "SND:%u", gAirCopyBlockNumber);
|
||||
}
|
||||
UI_PrintString(String, 2, 127, 4, 8);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
#endif
|
||||
25
ui/aircopy.h
Normal file
25
ui/aircopy.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_AIRCOPY_H
|
||||
#define UI_AIRCOPY_H
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
void UI_DisplayAircopy(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
55
ui/battery.c
Normal file
55
ui/battery.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bitmaps.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "functions.h"
|
||||
#include "ui/battery.h"
|
||||
#include "../misc.h"
|
||||
|
||||
void UI_DrawBattery(uint8_t* bitmap, uint8_t level, uint8_t blink)
|
||||
{
|
||||
if (level < 2 && blink == 1) {
|
||||
memset(bitmap, 0, sizeof(BITMAP_BatteryLevel1));
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(bitmap, BITMAP_BatteryLevel1, sizeof(BITMAP_BatteryLevel1));
|
||||
|
||||
if (level <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t bars = MIN(4, level - 2);
|
||||
|
||||
for (int i = 0; i < bars; i++) {
|
||||
#ifndef ENABLE_REVERSE_BAT_SYMBOL
|
||||
memcpy(bitmap + sizeof(BITMAP_BatteryLevel1) - 4 - (i * 3), BITMAP_BatteryLevel, 2);
|
||||
#else
|
||||
memcpy(bitmap + 3 + (i * 3) + 0, BITMAP_BatteryLevel, 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DisplayBattery(uint8_t level, uint8_t blink)
|
||||
{
|
||||
uint8_t bitmap[sizeof(BITMAP_BatteryLevel1)];
|
||||
UI_DrawBattery(bitmap, level, blink);
|
||||
ST7565_DrawLine(LCD_WIDTH - sizeof(bitmap), 0, bitmap, sizeof(bitmap));
|
||||
}
|
||||
25
ui/battery.h
Normal file
25
ui/battery.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_BATTERY_H
|
||||
#define UI_BATTERY_H
|
||||
|
||||
#include <stdint.h>
|
||||
void UI_DrawBattery(uint8_t* bitmap, uint8_t level, uint8_t blink);
|
||||
void UI_DisplayBattery(uint8_t Level, uint8_t blink);
|
||||
|
||||
#endif
|
||||
|
||||
102
ui/fmradio.c
Normal file
102
ui/fmradio.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "app/fm.h"
|
||||
#include "driver/bk1080.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/fmradio.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void UI_DisplayFM(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char *pPrintStr = String;
|
||||
UI_DisplayClear();
|
||||
|
||||
UI_PrintString("FM", 2, 0, 0, 8);
|
||||
|
||||
sprintf(String, "%d%s-%dM",
|
||||
BK1080_GetFreqLoLimit(gEeprom.FM_Band)/10,
|
||||
gEeprom.FM_Band == 0 ? ".5" : "",
|
||||
BK1080_GetFreqHiLimit(gEeprom.FM_Band)/10
|
||||
);
|
||||
|
||||
UI_PrintStringSmallNormal(String, 1, 0, 6);
|
||||
|
||||
//uint8_t spacings[] = {20,10,5};
|
||||
//sprintf(String, "%d0k", spacings[gEeprom.FM_Space % 3]);
|
||||
//UI_PrintStringSmallNormal(String, 127 - 4*7, 0, 6);
|
||||
|
||||
if (gAskToSave) {
|
||||
pPrintStr = "SAVE?";
|
||||
} else if (gAskToDelete) {
|
||||
pPrintStr = "DEL?";
|
||||
} else if (gFM_ScanState == FM_SCAN_OFF) {
|
||||
if (gEeprom.FM_IsMrMode) {
|
||||
sprintf(String, "MR(CH%02u)", gEeprom.FM_SelectedChannel + 1);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "VFO";
|
||||
for (unsigned int i = 0; i < 20; i++) {
|
||||
if (gEeprom.FM_FrequencyPlaying == gFM_Channels[i]) {
|
||||
sprintf(String, "VFO(CH%02u)", i + 1);
|
||||
pPrintStr = String;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (gFM_AutoScan) {
|
||||
sprintf(String, "A-SCAN(%u)", gFM_ChannelPosition + 1);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "M-SCAN";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 0, 127, 3, 10); // memory, vfo, scan
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gAskToSave || (gEeprom.FM_IsMrMode && gInputBoxIndex > 0)) {
|
||||
UI_GenerateChannelString(String, gFM_ChannelPosition);
|
||||
} else if (gAskToDelete) {
|
||||
sprintf(String, "CH-%02u", gEeprom.FM_SelectedChannel + 1);
|
||||
} else {
|
||||
if (gInputBoxIndex == 0) {
|
||||
sprintf(String, "%3d.%d", gEeprom.FM_FrequencyPlaying / 10, gEeprom.FM_FrequencyPlaying % 10);
|
||||
} else {
|
||||
const char * ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.1s",ascii, ascii + 3);
|
||||
}
|
||||
|
||||
UI_DisplayFrequency(String, 36, 1, gInputBoxIndex == 0); // frequency
|
||||
ST7565_BlitFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
UI_PrintString(String, 0, 127, 1, 10);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
#endif
|
||||
25
ui/fmradio.h
Normal file
25
ui/fmradio.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_FM_H
|
||||
#define UI_FM_H
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
void UI_DisplayFM(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
255
ui/helper.c
Normal file
255
ui/helper.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "font.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
#endif
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (gInputBoxIndex == 0)
|
||||
{
|
||||
sprintf(pString, "CH-%02u", Channel + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
pString[0] = 'C';
|
||||
pString[1] = 'H';
|
||||
pString[2] = '-';
|
||||
for (i = 0; i < 2; i++)
|
||||
pString[i + 3] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
}
|
||||
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint8_t ChannelNumber)
|
||||
{
|
||||
if (gInputBoxIndex > 0) {
|
||||
for (unsigned int i = 0; i < 3; i++) {
|
||||
pString[i] = (gInputBox[i] == 10) ? '-' : gInputBox[i] + '0';
|
||||
}
|
||||
|
||||
pString[3] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bShowPrefix) {
|
||||
// BUG here? Prefixed NULLs are allowed
|
||||
sprintf(pString, "CH-%03u", ChannelNumber + 1);
|
||||
} else if (ChannelNumber == 0xFF) {
|
||||
strcpy(pString, "NULL");
|
||||
} else {
|
||||
sprintf(pString, "%03u", ChannelNumber + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void UI_PrintStringBuffer(const char *pString, uint8_t * buffer, uint32_t char_width, const uint8_t *font)
|
||||
{
|
||||
const size_t Length = strlen(pString);
|
||||
const unsigned int char_spacing = char_width + 1;
|
||||
for (size_t i = 0; i < Length; i++) {
|
||||
const unsigned int index = pString[i] - ' ' - 1;
|
||||
if (pString[i] > ' ' && pString[i] < 127) {
|
||||
const uint32_t offset = i * char_spacing + 1;
|
||||
memcpy(buffer + offset, font + index * char_width, char_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Line, uint8_t Width)
|
||||
{
|
||||
size_t i;
|
||||
size_t Length = strlen(pString);
|
||||
|
||||
if (End > Start)
|
||||
Start += (((End - Start) - (Length * Width)) + 1) / 2;
|
||||
|
||||
for (i = 0; i < Length; i++)
|
||||
{
|
||||
const unsigned int ofs = (unsigned int)Start + (i * Width);
|
||||
if (pString[i] > ' ' && pString[i] < 127)
|
||||
{
|
||||
const unsigned int index = pString[i] - ' ' - 1;
|
||||
memcpy(gFrameBuffer[Line + 0] + ofs, &gFontBig[index][0], 7);
|
||||
memcpy(gFrameBuffer[Line + 1] + ofs, &gFontBig[index][7], 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI_PrintStringSmall(const char *pString, uint8_t Start, uint8_t End, uint8_t Line, uint8_t char_width, const uint8_t *font)
|
||||
{
|
||||
const size_t Length = strlen(pString);
|
||||
const unsigned int char_spacing = char_width + 1;
|
||||
|
||||
if (End > Start) {
|
||||
Start += (((End - Start) - Length * char_spacing) + 1) / 2;
|
||||
}
|
||||
|
||||
UI_PrintStringBuffer(pString, gFrameBuffer[Line] + Start, char_width, font);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
UI_PrintStringSmall(pString, Start, End, Line, ARRAY_SIZE(gFontSmall[0]), (const uint8_t *)gFontSmall);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line)
|
||||
{
|
||||
#ifdef ENABLE_SMALL_BOLD
|
||||
const uint8_t *font = (uint8_t *)gFontSmallBold;
|
||||
const uint8_t char_width = ARRAY_SIZE(gFontSmallBold[0]);
|
||||
#else
|
||||
const uint8_t *font = (uint8_t *)gFontSmall;
|
||||
const uint8_t char_width = ARRAY_SIZE(gFontSmall[0]);
|
||||
#endif
|
||||
|
||||
UI_PrintStringSmall(pString, Start, End, Line, char_width, font);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t * buffer)
|
||||
{
|
||||
UI_PrintStringBuffer(pString, buffer, ARRAY_SIZE(gFontSmall[0]), (uint8_t *)gFontSmall);
|
||||
}
|
||||
|
||||
void UI_PrintStringSmallBufferBold(const char *pString, uint8_t * buffer)
|
||||
{
|
||||
#ifdef ENABLE_SMALL_BOLD
|
||||
const uint8_t *font = (uint8_t *)gFontSmallBold;
|
||||
const uint8_t char_width = ARRAY_SIZE(gFontSmallBold[0]);
|
||||
#else
|
||||
const uint8_t *font = (uint8_t *)gFontSmall;
|
||||
const uint8_t char_width = ARRAY_SIZE(gFontSmall[0]);
|
||||
#endif
|
||||
UI_PrintStringBuffer(pString, buffer, char_width, font);
|
||||
}
|
||||
|
||||
void UI_DisplayFrequency(const char *string, uint8_t X, uint8_t Y, bool center)
|
||||
{
|
||||
const unsigned int char_width = 13;
|
||||
uint8_t *pFb0 = gFrameBuffer[Y] + X;
|
||||
uint8_t *pFb1 = pFb0 + 128;
|
||||
bool bCanDisplay = false;
|
||||
|
||||
uint8_t len = strlen(string);
|
||||
for(int i = 0; i < len; i++) {
|
||||
char c = string[i];
|
||||
if(c=='-') c = '9' + 1;
|
||||
if (bCanDisplay || c != ' ')
|
||||
{
|
||||
bCanDisplay = true;
|
||||
if(c>='0' && c<='9' + 1) {
|
||||
memcpy(pFb0 + 2, gFontBigDigits[c-'0'], char_width - 3);
|
||||
memcpy(pFb1 + 2, gFontBigDigits[c-'0'] + char_width - 3, char_width - 3);
|
||||
}
|
||||
else if(c=='.') {
|
||||
*pFb1 = 0x60; pFb0++; pFb1++;
|
||||
*pFb1 = 0x60; pFb0++; pFb1++;
|
||||
*pFb1 = 0x60; pFb0++; pFb1++;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if (center) {
|
||||
pFb0 -= 6;
|
||||
pFb1 -= 6;
|
||||
}
|
||||
pFb0 += char_width;
|
||||
pFb1 += char_width;
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black)
|
||||
{
|
||||
const uint8_t pattern = 1 << (y % 8);
|
||||
if(black)
|
||||
buffer[y/8][x] |= pattern;
|
||||
else
|
||||
buffer[y/8][x] &= ~pattern;
|
||||
}
|
||||
|
||||
static void sort(int16_t *a, int16_t *b)
|
||||
{
|
||||
if(*a > *b) {
|
||||
int16_t t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black)
|
||||
{
|
||||
if(x2==x1) {
|
||||
sort(&y1, &y2);
|
||||
for(int16_t i = y1; i <= y2; i++) {
|
||||
UI_DrawPixelBuffer(buffer, x1, i, black);
|
||||
}
|
||||
} else {
|
||||
const int multipl = 1000;
|
||||
int a = (y2-y1)*multipl / (x2-x1);
|
||||
int b = y1 - a * x1 / multipl;
|
||||
|
||||
sort(&x1, &x2);
|
||||
for(int i = x1; i<= x2; i++)
|
||||
{
|
||||
UI_DrawPixelBuffer(buffer, i, i*a/multipl +b, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UI_DrawRectangleBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black)
|
||||
{
|
||||
UI_DrawLineBuffer(buffer, x1,y1, x1,y2, black);
|
||||
UI_DrawLineBuffer(buffer, x1,y1, x2,y1, black);
|
||||
UI_DrawLineBuffer(buffer, x2,y1, x2,y2, black);
|
||||
UI_DrawLineBuffer(buffer, x1,y2, x2,y2, black);
|
||||
}
|
||||
|
||||
|
||||
void UI_DisplayPopup(const char *string)
|
||||
{
|
||||
UI_DisplayClear();
|
||||
|
||||
// for(uint8_t i = 1; i < 5; i++) {
|
||||
// memset(gFrameBuffer[i]+8, 0x00, 111);
|
||||
// }
|
||||
|
||||
// for(uint8_t x = 10; x < 118; x++) {
|
||||
// UI_DrawPixelBuffer(x, 10, true);
|
||||
// UI_DrawPixelBuffer(x, 46-9, true);
|
||||
// }
|
||||
|
||||
// for(uint8_t y = 11; y < 37; y++) {
|
||||
// UI_DrawPixelBuffer(10, y, true);
|
||||
// UI_DrawPixelBuffer(117, y, true);
|
||||
// }
|
||||
// DrawRectangle(9,9, 118,38, true);
|
||||
UI_PrintString(string, 9, 118, 2, 8);
|
||||
UI_PrintStringSmallNormal("Press EXIT", 9, 118, 6);
|
||||
}
|
||||
|
||||
void UI_DisplayClear()
|
||||
{
|
||||
memset(gFrameBuffer, 0, sizeof(gFrameBuffer));
|
||||
}
|
||||
40
ui/helper.h
Normal file
40
ui/helper.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_UI_H
|
||||
#define UI_UI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void UI_GenerateChannelString(char *pString, const uint8_t Channel);
|
||||
void UI_GenerateChannelStringEx(char *pString, const bool bShowPrefix, const uint8_t ChannelNumber);
|
||||
void UI_PrintString(const char *pString, uint8_t Start, uint8_t End, uint8_t Line, uint8_t Width);
|
||||
void UI_PrintStringSmallNormal(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBold(const char *pString, uint8_t Start, uint8_t End, uint8_t Line);
|
||||
void UI_PrintStringSmallBufferNormal(const char *pString, uint8_t *buffer);
|
||||
void UI_PrintStringSmallBufferBold(const char *pString, uint8_t * buffer);
|
||||
void UI_DisplayFrequency(const char *string, uint8_t X, uint8_t Y, bool center);
|
||||
|
||||
void UI_DisplayPopup(const char *string);
|
||||
|
||||
void UI_DrawPixelBuffer(uint8_t (*buffer)[128], uint8_t x, uint8_t y, bool black);
|
||||
void UI_DrawLineBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
void UI_DrawRectangleBuffer(uint8_t (*buffer)[128], int16_t x1, int16_t y1, int16_t x2, int16_t y2, bool black);
|
||||
|
||||
void UI_DisplayClear();
|
||||
|
||||
#endif
|
||||
44
ui/inputbox.c
Normal file
44
ui/inputbox.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "ui/inputbox.h"
|
||||
|
||||
char gInputBox[8];
|
||||
char inputBoxAscii[9];
|
||||
uint8_t gInputBoxIndex;
|
||||
|
||||
void INPUTBOX_Append(const KEY_Code_t Digit)
|
||||
{
|
||||
if (gInputBoxIndex >= sizeof(gInputBox))
|
||||
return;
|
||||
|
||||
if (gInputBoxIndex == 0)
|
||||
memset(gInputBox, 10, sizeof(gInputBox));
|
||||
|
||||
if (Digit != KEY_INVALID)
|
||||
gInputBox[gInputBoxIndex++] = (char)(Digit - KEY_0);
|
||||
}
|
||||
|
||||
const char* INPUTBOX_GetAscii()
|
||||
{
|
||||
for(int i = 0; i < 8; i++) {
|
||||
char c = gInputBox[i];
|
||||
inputBoxAscii[i] = (c==10)? '-' : '0' + c;
|
||||
}
|
||||
return inputBoxAscii;
|
||||
}
|
||||
31
ui/inputbox.h
Normal file
31
ui/inputbox.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_INPUTBOX_H
|
||||
#define UI_INPUTBOX_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/keyboard.h"
|
||||
|
||||
extern char gInputBox[8];
|
||||
extern uint8_t gInputBoxIndex;
|
||||
|
||||
void INPUTBOX_Append(const KEY_Code_t Digit);
|
||||
const char* INPUTBOX_GetAscii();
|
||||
|
||||
#endif
|
||||
|
||||
161
ui/lock.c
Normal file
161
ui/lock.c
Normal file
@@ -0,0 +1,161 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_PWRON_PASSWORD
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ARMCM0.h"
|
||||
#include "app/uart.h"
|
||||
#include "audio.h"
|
||||
#include "driver/keyboard.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/lock.h"
|
||||
|
||||
static void Render(void)
|
||||
{
|
||||
unsigned int i;
|
||||
char String[7];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_DisplayClear();
|
||||
|
||||
UI_PrintString("LOCK", 0, 127, 1, 10);
|
||||
for (i = 0; i < 6; i++)
|
||||
String[i] = (gInputBox[i] == 10) ? '-' : '*';
|
||||
String[6] = 0;
|
||||
UI_PrintString(String, 0, 127, 3, 12);
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
void UI_DisplayLock(void)
|
||||
{
|
||||
KEY_Code_t Key;
|
||||
BEEP_Type_t Beep;
|
||||
|
||||
gUpdateDisplay = true;
|
||||
|
||||
memset(gInputBox, 10, sizeof(gInputBox));
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (!gNextTimeslice) {}
|
||||
|
||||
// TODO: Original code doesn't do the below, but is needed for proper key debounce
|
||||
|
||||
gNextTimeslice = false;
|
||||
|
||||
Key = KEYBOARD_Poll();
|
||||
|
||||
if (gKeyReading0 == Key)
|
||||
{
|
||||
if (++gDebounceCounter == key_debounce_10ms)
|
||||
{
|
||||
if (Key == KEY_INVALID)
|
||||
{
|
||||
gKeyReading1 = KEY_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
gKeyReading1 = 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:
|
||||
INPUTBOX_Append(Key - KEY_0);
|
||||
|
||||
if (gInputBoxIndex < 6) // 6 frequency digits
|
||||
{
|
||||
Beep = BEEP_1KHZ_60MS_OPTIONAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t Password;
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
Password = StrToUL(INPUTBOX_GetAscii());
|
||||
|
||||
if ((gEeprom.POWER_ON_PASSWORD) == Password)
|
||||
{
|
||||
AUDIO_PlayBeep(BEEP_1KHZ_60MS_OPTIONAL);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(gInputBox, 10, sizeof(gInputBox));
|
||||
|
||||
Beep = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
||||
}
|
||||
|
||||
AUDIO_PlayBeep(Beep);
|
||||
|
||||
gUpdateDisplay = true;
|
||||
break;
|
||||
|
||||
case KEY_EXIT:
|
||||
if (gInputBoxIndex > 0)
|
||||
{
|
||||
gInputBox[--gInputBoxIndex] = 10;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
|
||||
AUDIO_PlayBeep(BEEP_1KHZ_60MS_OPTIONAL);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gKeyBeingHeld = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gDebounceCounter = 0;
|
||||
gKeyReading0 = Key;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UART
|
||||
if (UART_IsCommandAvailable())
|
||||
{
|
||||
__disable_irq();
|
||||
UART_HandleCommand();
|
||||
__enable_irq();
|
||||
}
|
||||
#endif
|
||||
if (gUpdateDisplay)
|
||||
{
|
||||
Render();
|
||||
gUpdateDisplay = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
25
ui/lock.h
Normal file
25
ui/lock.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_LOCK_H
|
||||
#define UI_LOCK_H
|
||||
|
||||
#ifdef ENABLE_PWRON_PASSWORD
|
||||
void UI_DisplayLock(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
797
ui/main.c
Normal file
797
ui/main.c
Normal file
@@ -0,0 +1,797 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h> // abs()
|
||||
|
||||
#include "app/chFrScanner.h"
|
||||
#include "app/dtmf.h"
|
||||
#ifdef ENABLE_AM_FIX
|
||||
#include "am_fix.h"
|
||||
#endif
|
||||
#include "bitmaps.h"
|
||||
#include "board.h"
|
||||
#include "driver/bk4819.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "functions.h"
|
||||
#include "helper/battery.h"
|
||||
#include "misc.h"
|
||||
#include "radio.h"
|
||||
#include "settings.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
center_line_t center_line = CENTER_LINE_NONE;
|
||||
|
||||
const int8_t dBmCorrTable[7] = {
|
||||
-15, // band 1
|
||||
-25, // band 2
|
||||
-20, // band 3
|
||||
-4, // band 4
|
||||
-7, // band 5
|
||||
-6, // band 6
|
||||
-1 // band 7
|
||||
};
|
||||
|
||||
const char *VfoStateStr[] = {
|
||||
[VFO_STATE_NORMAL]="",
|
||||
[VFO_STATE_BUSY]="BUSY",
|
||||
[VFO_STATE_BAT_LOW]="BAT LOW",
|
||||
[VFO_STATE_TX_DISABLE]="TX DISABLE",
|
||||
[VFO_STATE_TIMEOUT]="TIMEOUT",
|
||||
[VFO_STATE_ALARM]="ALARM",
|
||||
[VFO_STATE_VOLTAGE_HIGH]="VOLT HIGH"
|
||||
};
|
||||
|
||||
// ***************************************************************************
|
||||
|
||||
static void DrawSmallAntennaAndBars(uint8_t *p, unsigned int level)
|
||||
{
|
||||
if(level>6)
|
||||
level = 6;
|
||||
|
||||
memcpy(p, BITMAP_Antenna, ARRAY_SIZE(BITMAP_Antenna));
|
||||
|
||||
for(uint8_t i = 1; i <= level; i++) {
|
||||
char bar = (0xff << (6-i)) & 0x7F;
|
||||
memset(p + 2 + i*3, bar, 2);
|
||||
}
|
||||
}
|
||||
#if defined ENABLE_AUDIO_BAR || defined ENABLE_RSSI_BAR
|
||||
|
||||
static void DrawLevelBar(uint8_t xpos, uint8_t line, uint8_t level)
|
||||
{
|
||||
const char hollowBar[] = {
|
||||
0b01111111,
|
||||
0b01000001,
|
||||
0b01000001,
|
||||
0b01111111
|
||||
};
|
||||
|
||||
uint8_t *p_line = gFrameBuffer[line];
|
||||
level = MIN(level, 13);
|
||||
|
||||
for(uint8_t i = 0; i < level; i++) {
|
||||
if(i < 9) {
|
||||
for(uint8_t j = 0; j < 4; j++)
|
||||
p_line[xpos + i * 5 + j] = (~(0x7F >> (i+1))) & 0x7F;
|
||||
}
|
||||
else {
|
||||
memcpy(p_line + (xpos + i * 5), &hollowBar, ARRAY_SIZE(hollowBar));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
|
||||
unsigned int sqrt16(unsigned int value)
|
||||
{ // return square root of 'value'
|
||||
unsigned int shift = 16; // number of bits supplied in 'value' .. 2 ~ 32
|
||||
unsigned int bit = 1u << --shift;
|
||||
unsigned int sqrti = 0;
|
||||
while (bit)
|
||||
{
|
||||
const unsigned int temp = ((sqrti << 1) | bit) << shift--;
|
||||
if (value >= temp) {
|
||||
value -= temp;
|
||||
sqrti |= bit;
|
||||
}
|
||||
bit >>= 1;
|
||||
}
|
||||
return sqrti;
|
||||
}
|
||||
|
||||
void UI_DisplayAudioBar(void)
|
||||
{
|
||||
if (gSetting_mic_bar)
|
||||
{
|
||||
if(gLowBattery && !gLowBatteryConfirmed)
|
||||
return;
|
||||
|
||||
const unsigned int line = 3;
|
||||
|
||||
if (gCurrentFunction != FUNCTION_TRANSMIT ||
|
||||
gScreenToDisplay != DISPLAY_MAIN
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return; // screen is in use
|
||||
}
|
||||
|
||||
#if defined(ENABLE_ALARM) || defined(ENABLE_TX1750)
|
||||
if (gAlarmState != ALARM_STATE_OFF)
|
||||
return;
|
||||
#endif
|
||||
const unsigned int voice_amp = BK4819_GetVoiceAmplitudeOut(); // 15:0
|
||||
|
||||
// make non-linear to make more sensitive at low values
|
||||
const unsigned int level = MIN(voice_amp * 8, 65535u);
|
||||
const unsigned int sqrt_level = MIN(sqrt16(level), 124u);
|
||||
uint8_t bars = 13 * sqrt_level / 124;
|
||||
|
||||
uint8_t *p_line = gFrameBuffer[line];
|
||||
memset(p_line, 0, LCD_WIDTH);
|
||||
|
||||
DrawLevelBar(62, line, bars);
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void DisplayRSSIBar(const bool now)
|
||||
{
|
||||
#if defined(ENABLE_RSSI_BAR)
|
||||
|
||||
const unsigned int txt_width = 7 * 8; // 8 text chars
|
||||
const unsigned int bar_x = 2 + txt_width + 4; // X coord of bar graph
|
||||
|
||||
const unsigned int line = 3;
|
||||
uint8_t *p_line = gFrameBuffer[line];
|
||||
char str[16];
|
||||
|
||||
const char plus[] = {
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
0b01111110,
|
||||
0b01111110,
|
||||
0b01111110,
|
||||
0b00011000,
|
||||
0b00011000,
|
||||
};
|
||||
|
||||
if ((gEeprom.KEY_LOCK && gKeypadLocked > 0) || center_line != CENTER_LINE_RSSI)
|
||||
return; // display is in use
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT ||
|
||||
gScreenToDisplay != DISPLAY_MAIN
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
|
||||
#endif
|
||||
)
|
||||
return; // display is in use
|
||||
|
||||
if (now)
|
||||
memset(p_line, 0, LCD_WIDTH);
|
||||
|
||||
|
||||
const int16_t s0_dBm = -gEeprom.S0_LEVEL; // S0 .. base level
|
||||
const int16_t rssi_dBm =
|
||||
BK4819_GetRSSI_dBm()
|
||||
#ifdef ENABLE_AM_FIX
|
||||
+ ((gSetting_AM_fix && gRxVfo->Modulation == MODULATION_AM) ? AM_fix_get_gain_diff() : 0)
|
||||
#endif
|
||||
+ dBmCorrTable[gRxVfo->Band];
|
||||
|
||||
int s0_9 = gEeprom.S0_LEVEL - gEeprom.S9_LEVEL;
|
||||
const uint8_t s_level = MIN(MAX((int32_t)(rssi_dBm - s0_dBm)*100 / (s0_9*100/9), 0), 9); // S0 - S9
|
||||
uint8_t overS9dBm = MIN(MAX(rssi_dBm + gEeprom.S9_LEVEL, 0), 99);
|
||||
uint8_t overS9Bars = MIN(overS9dBm/10, 4);
|
||||
|
||||
if(overS9Bars == 0) {
|
||||
sprintf(str, "% 4d S%d", rssi_dBm, s_level);
|
||||
}
|
||||
else {
|
||||
sprintf(str, "% 4d %2d", rssi_dBm, overS9dBm);
|
||||
memcpy(p_line + 2 + 7*5, &plus, ARRAY_SIZE(plus));
|
||||
}
|
||||
|
||||
UI_PrintStringSmallNormal(str, 2, 0, line);
|
||||
DrawLevelBar(bar_x, line, s_level + overS9Bars);
|
||||
if (now)
|
||||
ST7565_BlitLine(line);
|
||||
#else
|
||||
int16_t rssi = BK4819_GetRSSI();
|
||||
uint8_t Level;
|
||||
|
||||
if (rssi >= gEEPROM_RSSI_CALIB[gRxVfo->Band][3]) {
|
||||
Level = 6;
|
||||
} else if (rssi >= gEEPROM_RSSI_CALIB[gRxVfo->Band][2]) {
|
||||
Level = 4;
|
||||
} else if (rssi >= gEEPROM_RSSI_CALIB[gRxVfo->Band][1]) {
|
||||
Level = 2;
|
||||
} else if (rssi >= gEEPROM_RSSI_CALIB[gRxVfo->Band][0]) {
|
||||
Level = 1;
|
||||
} else {
|
||||
Level = 0;
|
||||
}
|
||||
|
||||
uint8_t *pLine = (gEeprom.RX_VFO == 0)? gFrameBuffer[2] : gFrameBuffer[6];
|
||||
if (now)
|
||||
memset(pLine, 0, 23);
|
||||
DrawSmallAntennaAndBars(pLine, Level);
|
||||
if (now)
|
||||
ST7565_BlitFullScreen();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool now)
|
||||
{
|
||||
char buf[20];
|
||||
memset(gFrameBuffer[3], 0, 128);
|
||||
union {
|
||||
struct {
|
||||
uint16_t _ : 5;
|
||||
uint16_t agcSigStrength : 7;
|
||||
int16_t gainIdx : 3;
|
||||
uint16_t agcEnab : 1;
|
||||
};
|
||||
uint16_t __raw;
|
||||
} reg7e;
|
||||
reg7e.__raw = BK4819_ReadRegister(0x7E);
|
||||
uint8_t gainAddr = reg7e.gainIdx < 0 ? 0x14 : 0x10 + reg7e.gainIdx;
|
||||
union {
|
||||
struct {
|
||||
uint16_t pga:3;
|
||||
uint16_t mixer:2;
|
||||
uint16_t lna:3;
|
||||
uint16_t lnaS:2;
|
||||
};
|
||||
uint16_t __raw;
|
||||
} agcGainReg;
|
||||
agcGainReg.__raw = BK4819_ReadRegister(gainAddr);
|
||||
int8_t lnaShortTab[] = {-28, -24, -19, 0};
|
||||
int8_t lnaTab[] = {-24, -19, -14, -9, -6, -4, -2, 0};
|
||||
int8_t mixerTab[] = {-8, -6, -3, 0};
|
||||
int8_t pgaTab[] = {-33, -27, -21, -15, -9, -6, -3, 0};
|
||||
int16_t agcGain = lnaShortTab[agcGainReg.lnaS] + lnaTab[agcGainReg.lna] + mixerTab[agcGainReg.mixer] + pgaTab[agcGainReg.pga];
|
||||
|
||||
sprintf(buf, "%d%2d %2d %2d %3d", reg7e.agcEnab, reg7e.gainIdx, -agcGain, reg7e.agcSigStrength, BK4819_GetRSSI());
|
||||
UI_PrintStringSmallNormal(buf, 2, 0, 3);
|
||||
if(now)
|
||||
ST7565_BlitLine(3);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UI_MAIN_TimeSlice500ms(void)
|
||||
{
|
||||
if(gScreenToDisplay==DISPLAY_MAIN) {
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
UI_MAIN_PrintAGC(true);
|
||||
return;
|
||||
#endif
|
||||
|
||||
if(FUNCTION_IsRx()) {
|
||||
DisplayRSSIBar(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
|
||||
void UI_DisplayMain(void)
|
||||
{
|
||||
char String[22];
|
||||
|
||||
center_line = CENTER_LINE_NONE;
|
||||
|
||||
// clear the screen
|
||||
UI_DisplayClear();
|
||||
|
||||
if(gLowBattery && !gLowBatteryConfirmed) {
|
||||
UI_DisplayPopup("LOW BATTERY");
|
||||
ST7565_BlitFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
if (gEeprom.KEY_LOCK && gKeypadLocked > 0)
|
||||
{ // tell user how to unlock the keyboard
|
||||
UI_PrintString("Long press #", 0, LCD_WIDTH, 1, 8);
|
||||
UI_PrintString("to unlock", 0, LCD_WIDTH, 3, 8);
|
||||
ST7565_BlitFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int activeTxVFO = gRxVfoIsActive ? gEeprom.RX_VFO : gEeprom.TX_VFO;
|
||||
|
||||
for (unsigned int vfo_num = 0; vfo_num < 2; vfo_num++)
|
||||
{
|
||||
const unsigned int line0 = 0; // text screen line
|
||||
const unsigned int line1 = 4;
|
||||
const unsigned int line = (vfo_num == 0) ? line0 : line1;
|
||||
const bool isMainVFO = (vfo_num == gEeprom.TX_VFO);
|
||||
uint8_t *p_line0 = gFrameBuffer[line + 0];
|
||||
uint8_t *p_line1 = gFrameBuffer[line + 1];
|
||||
enum Vfo_txtr_mode mode = VFO_MODE_NONE;
|
||||
|
||||
if (activeTxVFO != vfo_num) // this is not active TX VFO
|
||||
{
|
||||
#ifdef ENABLE_SCAN_RANGES
|
||||
if(gScanRangeStart) {
|
||||
UI_PrintString("ScnRng", 5, 0, line, 8);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStart / 100000, gScanRangeStart % 100000);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line);
|
||||
sprintf(String, "%3u.%05u", gScanRangeStop / 100000, gScanRangeStop % 100000);
|
||||
UI_PrintStringSmallNormal(String, 56, 0, line + 1);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (gDTMF_InputMode
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE || gDTMF_IsTx
|
||||
#endif
|
||||
) {
|
||||
char *pPrintStr = "";
|
||||
// show DTMF stuff
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char Contact[16];
|
||||
if (!gDTMF_InputMode) {
|
||||
if (gDTMF_CallState == DTMF_CALL_STATE_CALL_OUT) {
|
||||
pPrintStr = DTMF_FindContact(gDTMF_String, Contact) ? Contact : gDTMF_String;
|
||||
} else if (gDTMF_CallState == DTMF_CALL_STATE_RECEIVED || gDTMF_CallState == DTMF_CALL_STATE_RECEIVED_STAY){
|
||||
pPrintStr = DTMF_FindContact(gDTMF_Callee, Contact) ? Contact : gDTMF_Callee;
|
||||
}else if (gDTMF_IsTx) {
|
||||
pPrintStr = gDTMF_String;
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 2 + (vfo_num * 3), 8);
|
||||
|
||||
pPrintStr = "";
|
||||
if (!gDTMF_InputMode) {
|
||||
if (gDTMF_CallState == DTMF_CALL_STATE_CALL_OUT) {
|
||||
pPrintStr = (gDTMF_State == DTMF_STATE_CALL_OUT_RSP) ? "CALL OUT(RSP)" : "CALL OUT";
|
||||
} else if (gDTMF_CallState == DTMF_CALL_STATE_RECEIVED || gDTMF_CallState == DTMF_CALL_STATE_RECEIVED_STAY) {
|
||||
sprintf(String, "CALL FRM:%s", (DTMF_FindContact(gDTMF_Caller, Contact)) ? Contact : gDTMF_Caller);
|
||||
pPrintStr = String;
|
||||
} else if (gDTMF_IsTx) {
|
||||
pPrintStr = (gDTMF_State == DTMF_STATE_TX_SUCC) ? "DTMF TX(SUCC)" : "DTMF TX";
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
sprintf(String, ">%s", gDTMF_InputBox);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 0 + (vfo_num * 3), 8);
|
||||
|
||||
center_line = CENTER_LINE_IN_USE;
|
||||
continue;
|
||||
}
|
||||
|
||||
// highlight the selected/used VFO with a marker
|
||||
if (isMainVFO)
|
||||
memcpy(p_line0 + 0, BITMAP_VFO_Default, sizeof(BITMAP_VFO_Default));
|
||||
}
|
||||
else // active TX VFO
|
||||
{ // highlight the selected/used VFO with a marker
|
||||
if (isMainVFO)
|
||||
memcpy(p_line0 + 0, BITMAP_VFO_Default, sizeof(BITMAP_VFO_Default));
|
||||
else
|
||||
memcpy(p_line0 + 0, BITMAP_VFO_NotDefault, sizeof(BITMAP_VFO_NotDefault));
|
||||
}
|
||||
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
{ // transmitting
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
if (gAlarmState == ALARM_STATE_SITE_ALARM)
|
||||
mode = VFO_MODE_RX;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (activeTxVFO == vfo_num)
|
||||
{ // show the TX symbol
|
||||
mode = VFO_MODE_TX;
|
||||
UI_PrintStringSmallBold("TX", 14, 0, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // receiving .. show the RX symbol
|
||||
mode = VFO_MODE_RX;
|
||||
if (FUNCTION_IsRx() && gEeprom.RX_VFO == vfo_num) {
|
||||
UI_PrintStringSmallBold("RX", 14, 0, line);
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
|
||||
{ // channel mode
|
||||
const unsigned int x = 2;
|
||||
const bool inputting = gInputBoxIndex != 0 && gEeprom.TX_VFO == vfo_num;
|
||||
if (!inputting)
|
||||
sprintf(String, "M%u", gEeprom.ScreenChannel[vfo_num] + 1);
|
||||
else
|
||||
sprintf(String, "M%.3s", INPUTBOX_GetAscii()); // show the input text
|
||||
UI_PrintStringSmallNormal(String, x, 0, line + 1);
|
||||
}
|
||||
else if (IS_FREQ_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
|
||||
{ // frequency mode
|
||||
// show the frequency band number
|
||||
const unsigned int x = 2;
|
||||
char * buf = gEeprom.VfoInfo[vfo_num].pRX->Frequency < _1GHz_in_KHz ? "" : "+";
|
||||
sprintf(String, "F%u%s", 1 + gEeprom.ScreenChannel[vfo_num] - FREQ_CHANNEL_FIRST, buf);
|
||||
UI_PrintStringSmallNormal(String, x, 0, line + 1);
|
||||
}
|
||||
#ifdef ENABLE_NOAA
|
||||
else
|
||||
{
|
||||
if (gInputBoxIndex == 0 || gEeprom.TX_VFO != vfo_num)
|
||||
{ // channel number
|
||||
sprintf(String, "N%u", 1 + gEeprom.ScreenChannel[vfo_num] - NOAA_CHANNEL_FIRST);
|
||||
}
|
||||
else
|
||||
{ // user entering channel number
|
||||
sprintf(String, "N%u%u", '0' + gInputBox[0], '0' + gInputBox[1]);
|
||||
}
|
||||
UI_PrintStringSmallNormal(String, 7, 0, line + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ************
|
||||
|
||||
enum VfoState_t state = VfoState[vfo_num];
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT && gAlarmState == ALARM_STATE_SITE_ALARM) {
|
||||
if (activeTxVFO == vfo_num)
|
||||
state = VFO_STATE_ALARM;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t frequency = gEeprom.VfoInfo[vfo_num].pRX->Frequency;
|
||||
|
||||
if (state != VFO_STATE_NORMAL)
|
||||
{
|
||||
if (state < ARRAY_SIZE(VfoStateStr))
|
||||
UI_PrintString(VfoStateStr[state], 31, 0, line, 8);
|
||||
}
|
||||
else if (gInputBoxIndex > 0 && IS_FREQ_CHANNEL(gEeprom.ScreenChannel[vfo_num]) && gEeprom.TX_VFO == vfo_num)
|
||||
{ // user entering a frequency
|
||||
const char * ascii = INPUTBOX_GetAscii();
|
||||
bool isGigaF = frequency>=_1GHz_in_KHz;
|
||||
sprintf(String, "%.*s.%.3s", 3 + isGigaF, ascii, ascii + 3 + isGigaF);
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
if(!isGigaF) {
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 113, 0, line + 1);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 32, line, false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// show the frequency in the main font
|
||||
UI_PrintString(String, 32, 0, line, 8);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT)
|
||||
{ // transmitting
|
||||
if (activeTxVFO == vfo_num)
|
||||
frequency = gEeprom.VfoInfo[vfo_num].pTX->Frequency;
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gEeprom.ScreenChannel[vfo_num]))
|
||||
{ // it's a channel
|
||||
|
||||
// show the scan list assigment symbols
|
||||
const ChannelAttributes_t att = gMR_ChannelAttributes[gEeprom.ScreenChannel[vfo_num]];
|
||||
if (att.scanlist1)
|
||||
memcpy(p_line0 + 113, BITMAP_ScanList1, sizeof(BITMAP_ScanList1));
|
||||
if (att.scanlist2)
|
||||
memcpy(p_line0 + 120, BITMAP_ScanList2, sizeof(BITMAP_ScanList2));
|
||||
|
||||
// compander symbol
|
||||
#ifndef ENABLE_BIG_FREQ
|
||||
if (att.compander)
|
||||
memcpy(p_line0 + 120 + LCD_WIDTH, BITMAP_compand, sizeof(BITMAP_compand));
|
||||
#else
|
||||
// TODO: // find somewhere else to put the symbol
|
||||
#endif
|
||||
|
||||
switch (gEeprom.CHANNEL_DISPLAY_MODE)
|
||||
{
|
||||
case MDF_FREQUENCY: // show the channel frequency
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
if(frequency < _1GHz_in_KHz) {
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 113, 0, line + 1);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 32, line, false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// show the frequency in the main font
|
||||
UI_PrintString(String, 32, 0, line, 8);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MDF_CHANNEL: // show the channel number
|
||||
sprintf(String, "CH-%03u", gEeprom.ScreenChannel[vfo_num] + 1);
|
||||
UI_PrintString(String, 32, 0, line, 8);
|
||||
break;
|
||||
|
||||
case MDF_NAME: // show the channel name
|
||||
case MDF_NAME_FREQ: // show the channel name and frequency
|
||||
|
||||
SETTINGS_FetchChannelName(String, gEeprom.ScreenChannel[vfo_num]);
|
||||
if (String[0] == 0)
|
||||
{ // no channel name, show the channel number instead
|
||||
sprintf(String, "CH-%03u", gEeprom.ScreenChannel[vfo_num] + 1);
|
||||
}
|
||||
|
||||
if (gEeprom.CHANNEL_DISPLAY_MODE == MDF_NAME) {
|
||||
UI_PrintString(String, 32, 0, line, 8);
|
||||
}
|
||||
else {
|
||||
UI_PrintStringSmallBold(String, 32 + 4, 0, line);
|
||||
// show the channel frequency below the channel number/name
|
||||
sprintf(String, "%03u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintStringSmallNormal(String, 32 + 4, 0, line + 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // frequency mode
|
||||
sprintf(String, "%3u.%05u", frequency / 100000, frequency % 100000);
|
||||
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
if(frequency < _1GHz_in_KHz) {
|
||||
// show the remaining 2 small frequency digits
|
||||
UI_PrintStringSmallNormal(String + 7, 113, 0, line + 1);
|
||||
String[7] = 0;
|
||||
// show the main large frequency digits
|
||||
UI_DisplayFrequency(String, 32, line, false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// show the frequency in the main font
|
||||
UI_PrintString(String, 32, 0, line, 8);
|
||||
}
|
||||
|
||||
// show the channel symbols
|
||||
const ChannelAttributes_t att = gMR_ChannelAttributes[gEeprom.ScreenChannel[vfo_num]];
|
||||
if (att.compander)
|
||||
#ifdef ENABLE_BIG_FREQ
|
||||
memcpy(p_line0 + 120, BITMAP_compand, sizeof(BITMAP_compand));
|
||||
#else
|
||||
memcpy(p_line0 + 120 + LCD_WIDTH, BITMAP_compand, sizeof(BITMAP_compand));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ************
|
||||
|
||||
{ // show the TX/RX level
|
||||
uint8_t Level = 0;
|
||||
|
||||
if (mode == VFO_MODE_TX)
|
||||
{ // TX power level
|
||||
switch (gRxVfo->OUTPUT_POWER)
|
||||
{
|
||||
case OUTPUT_POWER_LOW: Level = 2; break;
|
||||
case OUTPUT_POWER_MID: Level = 4; break;
|
||||
case OUTPUT_POWER_HIGH: Level = 6; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (mode == VFO_MODE_RX)
|
||||
{ // RX signal level
|
||||
#ifndef ENABLE_RSSI_BAR
|
||||
// bar graph
|
||||
if (gVFO_RSSI_bar_level[vfo_num] > 0)
|
||||
Level = gVFO_RSSI_bar_level[vfo_num];
|
||||
#endif
|
||||
}
|
||||
if(Level)
|
||||
DrawSmallAntennaAndBars(p_line1 + LCD_WIDTH, Level);
|
||||
}
|
||||
|
||||
// ************
|
||||
|
||||
String[0] = '\0';
|
||||
const VFO_Info_t *vfoInfo = &gEeprom.VfoInfo[vfo_num];
|
||||
|
||||
// show the modulation symbol
|
||||
const char * s = "";
|
||||
const ModulationMode_t mod = vfoInfo->Modulation;
|
||||
switch (mod){
|
||||
case MODULATION_FM: {
|
||||
const FREQ_Config_t *pConfig = (mode == VFO_MODE_TX) ? vfoInfo->pTX : vfoInfo->pRX;
|
||||
const unsigned int code_type = pConfig->CodeType;
|
||||
const char *code_list[] = {"", "CT", "DCS", "DCR"};
|
||||
if (code_type < ARRAY_SIZE(code_list))
|
||||
s = code_list[code_type];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s = gModulationStr[mod];
|
||||
break;
|
||||
}
|
||||
UI_PrintStringSmallNormal(s, LCD_WIDTH + 24, 0, line + 1);
|
||||
|
||||
if (state == VFO_STATE_NORMAL || state == VFO_STATE_ALARM)
|
||||
{ // show the TX power
|
||||
const char pwr_list[][2] = {"L","M","H"};
|
||||
int i = vfoInfo->OUTPUT_POWER % 3;
|
||||
UI_PrintStringSmallNormal(pwr_list[i], LCD_WIDTH + 46, 0, line + 1);
|
||||
}
|
||||
|
||||
if (vfoInfo->freq_config_RX.Frequency != vfoInfo->freq_config_TX.Frequency)
|
||||
{ // show the TX offset symbol
|
||||
const char dir_list[][2] = {"", "+", "-"};
|
||||
int i = vfoInfo->TX_OFFSET_FREQUENCY_DIRECTION % 3;
|
||||
UI_PrintStringSmallNormal(dir_list[i], LCD_WIDTH + 54, 0, line + 1);
|
||||
}
|
||||
|
||||
// show the TX/RX reverse symbol
|
||||
if (vfoInfo->FrequencyReverse)
|
||||
UI_PrintStringSmallNormal("R", LCD_WIDTH + 62, 0, line + 1);
|
||||
|
||||
if (vfoInfo->CHANNEL_BANDWIDTH == BANDWIDTH_NARROW)
|
||||
UI_PrintStringSmallNormal("N", LCD_WIDTH + 70, 0, line + 1);
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
// show the DTMF decoding symbol
|
||||
if (vfoInfo->DTMF_DECODING_ENABLE || gSetting_KILLED)
|
||||
UI_PrintStringSmallNormal("DTMF", LCD_WIDTH + 78, 0, line + 1);
|
||||
#endif
|
||||
|
||||
// show the audio scramble symbol
|
||||
if (vfoInfo->SCRAMBLING_TYPE > 0 && gSetting_ScrambleEnable)
|
||||
UI_PrintStringSmallNormal("SCR", LCD_WIDTH + 106, 0, line + 1);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
center_line = CENTER_LINE_IN_USE;
|
||||
UI_MAIN_PrintAGC(false);
|
||||
#endif
|
||||
|
||||
if (center_line == CENTER_LINE_NONE)
|
||||
{ // we're free to use the middle line
|
||||
|
||||
const bool rx = FUNCTION_IsRx();
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
if (gSetting_mic_bar && gCurrentFunction == FUNCTION_TRANSMIT) {
|
||||
center_line = CENTER_LINE_AUDIO_BAR;
|
||||
UI_DisplayAudioBar();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AM_FIX) && defined(ENABLE_AM_FIX_SHOW_DATA)
|
||||
if (rx && gEeprom.VfoInfo[gEeprom.RX_VFO].Modulation == MODULATION_AM && gSetting_AM_fix)
|
||||
{
|
||||
if (gScreenToDisplay != DISPLAY_MAIN
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
|
||||
center_line = CENTER_LINE_AM_FIX_DATA;
|
||||
AM_fix_print_data(gEeprom.RX_VFO, String);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 3);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_RSSI_BAR
|
||||
if (rx) {
|
||||
center_line = CENTER_LINE_RSSI;
|
||||
DisplayRSSIBar(false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (rx || gCurrentFunction == FUNCTION_FOREGROUND || gCurrentFunction == FUNCTION_POWER_SAVE)
|
||||
{
|
||||
#if 1
|
||||
if (gSetting_live_DTMF_decoder && gDTMF_RX_live[0] != 0)
|
||||
{ // show live DTMF decode
|
||||
const unsigned int len = strlen(gDTMF_RX_live);
|
||||
const unsigned int idx = (len > (17 - 5)) ? len - (17 - 5) : 0; // limit to last 'n' chars
|
||||
|
||||
if (gScreenToDisplay != DISPLAY_MAIN
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
|
||||
center_line = CENTER_LINE_DTMF_DEC;
|
||||
|
||||
sprintf(String, "DTMF %s", gDTMF_RX_live + idx);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 3);
|
||||
}
|
||||
#else
|
||||
if (gSetting_live_DTMF_decoder && gDTMF_RX_index > 0)
|
||||
{ // show live DTMF decode
|
||||
const unsigned int len = gDTMF_RX_index;
|
||||
const unsigned int idx = (len > (17 - 5)) ? len - (17 - 5) : 0; // limit to last 'n' chars
|
||||
|
||||
if (gScreenToDisplay != DISPLAY_MAIN ||
|
||||
gDTMF_CallState != DTMF_CALL_STATE_NONE)
|
||||
return;
|
||||
|
||||
center_line = CENTER_LINE_DTMF_DEC;
|
||||
|
||||
sprintf(String, "DTMF %s", gDTMF_RX_live + idx);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 3);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SHOW_CHARGE_LEVEL
|
||||
else if (gChargingWithTypeC)
|
||||
{ // charging .. show the battery state
|
||||
if (gScreenToDisplay != DISPLAY_MAIN
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| gDTMF_CallState != DTMF_CALL_STATE_NONE
|
||||
#endif
|
||||
)
|
||||
return;
|
||||
|
||||
center_line = CENTER_LINE_CHARGE_DATA;
|
||||
|
||||
sprintf(String, "Charge %u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 3);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
// ***************************************************************************
|
||||
49
ui/main.h
Normal file
49
ui/main.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_MAIN_H
|
||||
#define UI_MAIN_H
|
||||
|
||||
enum center_line_t {
|
||||
CENTER_LINE_NONE = 0,
|
||||
CENTER_LINE_IN_USE,
|
||||
CENTER_LINE_AUDIO_BAR,
|
||||
CENTER_LINE_RSSI,
|
||||
CENTER_LINE_AM_FIX_DATA,
|
||||
CENTER_LINE_DTMF_DEC,
|
||||
CENTER_LINE_CHARGE_DATA
|
||||
};
|
||||
|
||||
enum Vfo_txtr_mode{
|
||||
VFO_MODE_NONE = 0,
|
||||
VFO_MODE_TX = 1,
|
||||
VFO_MODE_RX = 2,
|
||||
};
|
||||
|
||||
typedef enum center_line_t center_line_t;
|
||||
|
||||
extern center_line_t center_line;
|
||||
extern const int8_t dBmCorrTable[7];
|
||||
|
||||
void UI_DisplayAudioBar(void);
|
||||
void UI_MAIN_TimeSlice500ms(void);
|
||||
void UI_DisplayMain(void);
|
||||
|
||||
#ifdef ENABLE_AGC_SHOW_DATA
|
||||
void UI_MAIN_PrintAGC(bool force);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
971
ui/menu.c
Normal file
971
ui/menu.c
Normal file
@@ -0,0 +1,971 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../app/dtmf.h"
|
||||
#include "../app/menu.h"
|
||||
#include "../bitmaps.h"
|
||||
#include "../board.h"
|
||||
#include "../dcs.h"
|
||||
#include "../driver/backlight.h"
|
||||
#include "../driver/bk4819.h"
|
||||
#include "../driver/eeprom.h"
|
||||
#include "../driver/st7565.h"
|
||||
#include "../external/printf/printf.h"
|
||||
#include "../frequencies.h"
|
||||
#include "../helper/battery.h"
|
||||
#include "../misc.h"
|
||||
#include "../settings.h"
|
||||
#include "helper.h"
|
||||
#include "inputbox.h"
|
||||
#include "menu.h"
|
||||
#include "ui.h"
|
||||
|
||||
|
||||
const t_menu_item MenuList[] =
|
||||
{
|
||||
// text, voice ID, menu ID
|
||||
{"Step1", VOICE_ID_FREQUENCY_STEP, MENU_STEP },
|
||||
{"TxPwr", VOICE_ID_POWER, MENU_TXP }, // was "TXP"
|
||||
{"RxDCS", VOICE_ID_DCS, MENU_R_DCS }, // was "R_DCS"
|
||||
{"RxCTCS", VOICE_ID_CTCSS, MENU_R_CTCS }, // was "R_CTCS"
|
||||
{"TxDCS", VOICE_ID_DCS, MENU_T_DCS }, // was "T_DCS"
|
||||
{"TxCTCS", VOICE_ID_CTCSS, MENU_T_CTCS }, // was "T_CTCS"
|
||||
{"TxODir", VOICE_ID_TX_OFFSET_FREQUENCY_DIRECTION, MENU_SFT_D }, // was "SFT_D"
|
||||
{"TxOffs", VOICE_ID_TX_OFFSET_FREQUENCY, MENU_OFFSET }, // was "OFFSET"
|
||||
{"W/N", VOICE_ID_CHANNEL_BANDWIDTH, MENU_W_N },
|
||||
{"Scramb", VOICE_ID_SCRAMBLER_ON, MENU_SCR }, // was "SCR"
|
||||
{"BusyCL", VOICE_ID_BUSY_LOCKOUT, MENU_BCL }, // was "BCL"
|
||||
{"Compnd", VOICE_ID_INVALID, MENU_COMPAND },
|
||||
{"Demodu", VOICE_ID_INVALID, MENU_AM }, // was "AM"
|
||||
{"ScAdd1", VOICE_ID_INVALID, MENU_S_ADD1 },
|
||||
{"ScAdd2", VOICE_ID_INVALID, MENU_S_ADD2 },
|
||||
{"ChSave", VOICE_ID_MEMORY_CHANNEL, MENU_MEM_CH }, // was "MEM-CH"
|
||||
{"ChDele", VOICE_ID_DELETE_CHANNEL, MENU_DEL_CH }, // was "DEL-CH"
|
||||
{"ChName", VOICE_ID_INVALID, MENU_MEM_NAME },
|
||||
|
||||
{"SList", VOICE_ID_INVALID, MENU_S_LIST },
|
||||
{"SList1", VOICE_ID_INVALID, MENU_SLIST1 },
|
||||
{"SList2", VOICE_ID_INVALID, MENU_SLIST2 },
|
||||
{"ScnRev", VOICE_ID_INVALID, MENU_SC_REV },
|
||||
#ifdef ENABLE_NOAA
|
||||
{"NOAA-S", VOICE_ID_INVALID, MENU_NOAA_S },
|
||||
#endif
|
||||
{"F1Shrt", VOICE_ID_INVALID, MENU_F1SHRT },
|
||||
{"F1Long", VOICE_ID_INVALID, MENU_F1LONG },
|
||||
{"F2Shrt", VOICE_ID_INVALID, MENU_F2SHRT },
|
||||
{"F2Long", VOICE_ID_INVALID, MENU_F2LONG },
|
||||
{"M Long", VOICE_ID_INVALID, MENU_MLONG },
|
||||
|
||||
{"KeyLck", VOICE_ID_INVALID, MENU_AUTOLK }, // was "AUTOLk"
|
||||
{"TxTOut", VOICE_ID_TRANSMIT_OVER_TIME, MENU_TOT }, // was "TOT"
|
||||
{"BatSav", VOICE_ID_SAVE_MODE, MENU_SAVE }, // was "SAVE"
|
||||
{"Mic", VOICE_ID_INVALID, MENU_MIC },
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
{"MicBar", VOICE_ID_INVALID, MENU_MIC_BAR },
|
||||
#endif
|
||||
{"ChDisp", VOICE_ID_INVALID, MENU_MDF }, // was "MDF"
|
||||
{"POnMsg", VOICE_ID_INVALID, MENU_PONMSG },
|
||||
{"BatTxt", VOICE_ID_INVALID, MENU_BAT_TXT },
|
||||
{"BackLt", VOICE_ID_INVALID, MENU_ABR }, // was "ABR"
|
||||
{"BLMin", VOICE_ID_INVALID, MENU_ABR_MIN },
|
||||
{"BLMax", VOICE_ID_INVALID, MENU_ABR_MAX },
|
||||
{"BltTRX", VOICE_ID_INVALID, MENU_ABR_ON_TX_RX },
|
||||
{"Beep", VOICE_ID_BEEP_PROMPT, MENU_BEEP },
|
||||
#ifdef ENABLE_VOICE
|
||||
{"Voice", VOICE_ID_VOICE_PROMPT, MENU_VOICE },
|
||||
#endif
|
||||
{"Roger", VOICE_ID_INVALID, MENU_ROGER },
|
||||
{"STE", VOICE_ID_INVALID, MENU_STE },
|
||||
{"RP STE", VOICE_ID_INVALID, MENU_RP_STE },
|
||||
{"1 Call", VOICE_ID_INVALID, MENU_1_CALL },
|
||||
#ifdef ENABLE_ALARM
|
||||
{"AlarmT", VOICE_ID_INVALID, MENU_AL_MOD },
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
{"ANI ID", VOICE_ID_ANI_CODE, MENU_ANI_ID },
|
||||
#endif
|
||||
{"UPCode", VOICE_ID_INVALID, MENU_UPCODE },
|
||||
{"DWCode", VOICE_ID_INVALID, MENU_DWCODE },
|
||||
{"PTT ID", VOICE_ID_INVALID, MENU_PTT_ID },
|
||||
{"D ST", VOICE_ID_INVALID, MENU_D_ST },
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
{"D Resp", VOICE_ID_INVALID, MENU_D_RSP },
|
||||
{"D Hold", VOICE_ID_INVALID, MENU_D_HOLD },
|
||||
#endif
|
||||
{"D Prel", VOICE_ID_INVALID, MENU_D_PRE },
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
{"D Decd", VOICE_ID_INVALID, MENU_D_DCD },
|
||||
{"D List", VOICE_ID_INVALID, MENU_D_LIST },
|
||||
#endif
|
||||
{"D Live", VOICE_ID_INVALID, MENU_D_LIVE_DEC }, // live DTMF decoder
|
||||
#ifdef ENABLE_AM_FIX
|
||||
{"AM Fix", VOICE_ID_INVALID, MENU_AM_FIX },
|
||||
#endif
|
||||
#ifdef ENABLE_VOX
|
||||
{"VOX", VOICE_ID_VOX, MENU_VOX },
|
||||
#endif
|
||||
{"BatVol", VOICE_ID_INVALID, MENU_VOL }, // was "VOL"
|
||||
{"RxMode", VOICE_ID_DUAL_STANDBY, MENU_TDR },
|
||||
{"Sql", VOICE_ID_SQUELCH, MENU_SQL },
|
||||
|
||||
// hidden menu items from here on
|
||||
// enabled if pressing both the PTT and upper side button at power-on
|
||||
{"F Lock", VOICE_ID_INVALID, MENU_F_LOCK },
|
||||
{"Tx 200", VOICE_ID_INVALID, MENU_200TX }, // was "200TX"
|
||||
{"Tx 350", VOICE_ID_INVALID, MENU_350TX }, // was "350TX"
|
||||
{"Tx 500", VOICE_ID_INVALID, MENU_500TX }, // was "500TX"
|
||||
{"350 En", VOICE_ID_INVALID, MENU_350EN }, // was "350EN"
|
||||
{"ScraEn", VOICE_ID_INVALID, MENU_SCREN }, // was "SCREN"
|
||||
#ifdef ENABLE_F_CAL_MENU
|
||||
{"FrCali", VOICE_ID_INVALID, MENU_F_CALI }, // reference xtal calibration
|
||||
#endif
|
||||
{"BatCal", VOICE_ID_INVALID, MENU_BATCAL }, // battery voltage calibration
|
||||
{"BatTyp", VOICE_ID_INVALID, MENU_BATTYP }, // battery type 1600/2200mAh
|
||||
{"Reset", VOICE_ID_INITIALISATION, MENU_RESET }, // might be better to move this to the hidden menu items ?
|
||||
|
||||
{"", VOICE_ID_INVALID, 0xff } // end of list - DO NOT delete or move this this
|
||||
};
|
||||
|
||||
const uint8_t FIRST_HIDDEN_MENU_ITEM = MENU_F_LOCK;
|
||||
|
||||
const char gSubMenu_TXP[][5] =
|
||||
{
|
||||
"LOW",
|
||||
"MID",
|
||||
"HIGH"
|
||||
};
|
||||
|
||||
const char gSubMenu_SFT_D[][4] =
|
||||
{
|
||||
"OFF",
|
||||
"+",
|
||||
"-"
|
||||
};
|
||||
|
||||
const char gSubMenu_W_N[][7] =
|
||||
{
|
||||
"WIDE",
|
||||
"NARROW"
|
||||
};
|
||||
|
||||
const char gSubMenu_OFF_ON[][4] =
|
||||
{
|
||||
"OFF",
|
||||
"ON"
|
||||
};
|
||||
|
||||
const char gSubMenu_SAVE[][4] =
|
||||
{
|
||||
"OFF",
|
||||
"1:1",
|
||||
"1:2",
|
||||
"1:3",
|
||||
"1:4"
|
||||
};
|
||||
|
||||
const char gSubMenu_TOT[][7] =
|
||||
{
|
||||
"30 sec",
|
||||
"1 min",
|
||||
"2 min",
|
||||
"3 min",
|
||||
"4 min",
|
||||
"5 min",
|
||||
"6 min",
|
||||
"7 min",
|
||||
"8 min",
|
||||
"9 min",
|
||||
"15 min"
|
||||
};
|
||||
|
||||
const char* const gSubMenu_RXMode[] =
|
||||
{
|
||||
"MAIN\nONLY", // TX and RX on main only
|
||||
"DUAL RX\nRESPOND", // Watch both and respond
|
||||
"CROSS\nBAND", // TX on main, RX on secondary
|
||||
"MAIN TX\nDUAL RX" // always TX on main, but RX on both
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
const char gSubMenu_VOICE[][4] =
|
||||
{
|
||||
"OFF",
|
||||
"CHI",
|
||||
"ENG"
|
||||
};
|
||||
#endif
|
||||
|
||||
const char gSubMenu_SC_REV[][8] =
|
||||
{
|
||||
"TIMEOUT",
|
||||
"CARRIER",
|
||||
"STOP"
|
||||
};
|
||||
|
||||
const char* const gSubMenu_MDF[] =
|
||||
{
|
||||
"FREQ",
|
||||
"CHANNEL\nNUMBER",
|
||||
"NAME",
|
||||
"NAME\n+\nFREQ"
|
||||
};
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
const char gSubMenu_AL_MOD[][5] =
|
||||
{
|
||||
"SITE",
|
||||
"TONE"
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
const char gSubMenu_D_RSP[][11] =
|
||||
{
|
||||
"DO\nNOTHING",
|
||||
"RING",
|
||||
"REPLY",
|
||||
"BOTH"
|
||||
};
|
||||
#endif
|
||||
|
||||
const char* const gSubMenu_PTT_ID[] =
|
||||
{
|
||||
"OFF",
|
||||
"UP CODE",
|
||||
"DOWN CODE",
|
||||
"UP+DOWN\nCODE",
|
||||
"APOLLO\nQUINDAR"
|
||||
};
|
||||
|
||||
const char gSubMenu_PONMSG[][8] =
|
||||
{
|
||||
"FULL",
|
||||
"MESSAGE",
|
||||
"VOLTAGE",
|
||||
"NONE"
|
||||
};
|
||||
|
||||
const char gSubMenu_ROGER[][6] =
|
||||
{
|
||||
"OFF",
|
||||
"ROGER",
|
||||
"MDC"
|
||||
};
|
||||
|
||||
const char gSubMenu_RESET[][4] =
|
||||
{
|
||||
"VFO",
|
||||
"ALL"
|
||||
};
|
||||
|
||||
const char * const gSubMenu_F_LOCK[] =
|
||||
{
|
||||
"DEFAULT+\n137-174\n400-470",
|
||||
"FCC HAM\n144-148\n420-450",
|
||||
"CE HAM\n144-146\n430-440",
|
||||
"GB HAM\n144-148\n430-440",
|
||||
"137-174\n400-430",
|
||||
"137-174\n400-438",
|
||||
"DISABLE\nALL",
|
||||
"UNLOCK\nALL",
|
||||
};
|
||||
|
||||
const char gSubMenu_BACKLIGHT[][7] =
|
||||
{
|
||||
"OFF",
|
||||
"5 sec",
|
||||
"10 sec",
|
||||
"20 sec",
|
||||
"1 min",
|
||||
"2 min",
|
||||
"4 min",
|
||||
"ON"
|
||||
};
|
||||
|
||||
const char gSubMenu_RX_TX[][6] =
|
||||
{
|
||||
"OFF",
|
||||
"TX",
|
||||
"RX",
|
||||
"TX/RX"
|
||||
};
|
||||
|
||||
const char gSubMenu_BAT_TXT[][8] =
|
||||
{
|
||||
"NONE",
|
||||
"VOLTAGE",
|
||||
"PERCENT"
|
||||
};
|
||||
|
||||
const char gSubMenu_BATTYP[][9] =
|
||||
{
|
||||
"1600mAh",
|
||||
"2200mAh"
|
||||
};
|
||||
|
||||
const char gSubMenu_SCRAMBLER[][7] =
|
||||
{
|
||||
"OFF",
|
||||
"2600Hz",
|
||||
"2700Hz",
|
||||
"2800Hz",
|
||||
"2900Hz",
|
||||
"3000Hz",
|
||||
"3100Hz",
|
||||
"3200Hz",
|
||||
"3300Hz",
|
||||
"3400Hz",
|
||||
"3500Hz"
|
||||
};
|
||||
|
||||
const t_sidefunction gSubMenu_SIDEFUNCTIONS[] =
|
||||
{
|
||||
{"NONE", ACTION_OPT_NONE},
|
||||
#ifdef ENABLE_FLASHLIGHT
|
||||
{"FLASH\nLIGHT", ACTION_OPT_FLASHLIGHT},
|
||||
#endif
|
||||
{"POWER", ACTION_OPT_POWER},
|
||||
{"MONITOR", ACTION_OPT_MONITOR},
|
||||
{"SCAN", ACTION_OPT_SCAN},
|
||||
#ifdef ENABLE_VOX
|
||||
{"VOX", ACTION_OPT_VOX},
|
||||
#endif
|
||||
#ifdef ENABLE_ALARM
|
||||
{"ALARM", ACTION_OPT_ALARM},
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
{"FM RADIO", ACTION_OPT_FM},
|
||||
#endif
|
||||
#ifdef ENABLE_TX1750
|
||||
{"1750HZ", ACTION_OPT_1750},
|
||||
#endif
|
||||
{"LOCK\nKEYPAD", ACTION_OPT_KEYLOCK},
|
||||
{"SWITCH\nVFO", ACTION_OPT_A_B},
|
||||
{"VFO/MR", ACTION_OPT_VFO_MR},
|
||||
{"SWITCH\nDEMODUL", ACTION_OPT_SWITCH_DEMODUL},
|
||||
#ifdef ENABLE_BLMIN_TMP_OFF
|
||||
{"BLMIN\nTMP OFF", ACTION_OPT_BLMIN_TMP_OFF}, //BackLight Minimum Temporay OFF
|
||||
#endif
|
||||
#ifdef ENABLE_SPECTRUM
|
||||
{"SPECTRUM", ACTION_OPT_SPECTRUM}
|
||||
#endif
|
||||
};
|
||||
|
||||
const uint8_t gSubMenu_SIDEFUNCTIONS_size = ARRAY_SIZE(gSubMenu_SIDEFUNCTIONS);
|
||||
|
||||
bool gIsInSubMenu;
|
||||
uint8_t gMenuCursor;
|
||||
int UI_MENU_GetCurrentMenuId() {
|
||||
if(gMenuCursor < ARRAY_SIZE(MenuList))
|
||||
return MenuList[gMenuCursor].menu_id;
|
||||
|
||||
return MenuList[ARRAY_SIZE(MenuList)-1].menu_id;
|
||||
}
|
||||
|
||||
uint8_t UI_MENU_GetMenuIdx(uint8_t id)
|
||||
{
|
||||
for(uint8_t i = 0; i < ARRAY_SIZE(MenuList); i++)
|
||||
if(MenuList[i].menu_id == id)
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t gSubMenuSelection;
|
||||
|
||||
// edit box
|
||||
char edit_original[17]; // a copy of the text before editing so that we can easily test for changes/difference
|
||||
char edit[17];
|
||||
int edit_index;
|
||||
|
||||
void UI_DisplayMenu(void)
|
||||
{
|
||||
const unsigned int menu_list_width = 6; // max no. of characters on the menu list (left side)
|
||||
const unsigned int menu_item_x1 = (8 * menu_list_width) + 2;
|
||||
const unsigned int menu_item_x2 = LCD_WIDTH - 1;
|
||||
unsigned int i;
|
||||
char String[64]; // bigger cuz we can now do multi-line in one string (use '\n' char)
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
char Contact[16];
|
||||
#endif
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
#ifndef ENABLE_CUSTOM_MENU_LAYOUT
|
||||
// original menu layout
|
||||
for (i = 0; i < 3; i++)
|
||||
if (gMenuCursor > 0 || i > 0)
|
||||
if ((gMenuListCount - 1) != gMenuCursor || i != 2)
|
||||
UI_PrintString(MenuList[gMenuCursor + i - 1].name, 0, 0, i * 2, 8);
|
||||
|
||||
// invert the current menu list item pixels
|
||||
for (i = 0; i < (8 * menu_list_width); i++)
|
||||
{
|
||||
gFrameBuffer[2][i] ^= 0xFF;
|
||||
gFrameBuffer[3][i] ^= 0xFF;
|
||||
}
|
||||
|
||||
// draw vertical separating dotted line
|
||||
for (i = 0; i < 7; i++)
|
||||
gFrameBuffer[i][(8 * menu_list_width) + 1] = 0xAA;
|
||||
|
||||
// draw the little sub-menu triangle marker
|
||||
if (gIsInSubMenu)
|
||||
memcpy(gFrameBuffer[0] + (8 * menu_list_width) + 1, BITMAP_CurrentIndicator, sizeof(BITMAP_CurrentIndicator));
|
||||
|
||||
// draw the menu index number/count
|
||||
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
|
||||
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
|
||||
#else
|
||||
{ // new menu layout .. experimental & unfinished
|
||||
const int menu_index = gMenuCursor; // current selected menu item
|
||||
i = 1;
|
||||
|
||||
if (!gIsInSubMenu) {
|
||||
while (i < 2)
|
||||
{ // leading menu items - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k < 0)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount + k].name, 0, 0, i); // wrap-a-round
|
||||
else if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
// current menu item - keep big n fat
|
||||
if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 2, 8);
|
||||
i++;
|
||||
|
||||
while (i < 4)
|
||||
{ // trailing menu item - small text
|
||||
const int k = menu_index + i - 2;
|
||||
if (k >= 0 && k < (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[k].name, 0, 0, 1 + i);
|
||||
else if (k >= (int)gMenuListCount)
|
||||
UI_PrintStringSmallNormal(MenuList[gMenuListCount - k].name, 0, 0, 1 + i); // wrap-a-round
|
||||
i++;
|
||||
}
|
||||
|
||||
// draw the menu index number/count
|
||||
sprintf(String, "%2u.%u", 1 + gMenuCursor, gMenuListCount);
|
||||
UI_PrintStringSmallNormal(String, 2, 0, 6);
|
||||
}
|
||||
else if (menu_index >= 0 && menu_index < (int)gMenuListCount)
|
||||
{ // current menu item
|
||||
// strcat(String, ":");
|
||||
UI_PrintString(MenuList[menu_index].name, 0, 0, 0, 8);
|
||||
// UI_PrintStringSmallNormal(String, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// **************
|
||||
|
||||
memset(String, 0, sizeof(String));
|
||||
|
||||
bool already_printed = false;
|
||||
|
||||
/* Brightness is set to max in some entries of this menu. Return it to the configured brightness
|
||||
level the "next" time we enter here.I.e., when we move from one menu to another.
|
||||
It also has to be set back to max when pressing the Exit key. */
|
||||
|
||||
BACKLIGHT_TurnOn();
|
||||
|
||||
switch (UI_MENU_GetCurrentMenuId())
|
||||
{
|
||||
case MENU_SQL:
|
||||
sprintf(String, "%d", gSubMenuSelection);
|
||||
break;
|
||||
|
||||
case MENU_MIC:
|
||||
{ // display the mic gain in actual dB rather than just an index number
|
||||
const uint8_t mic = gMicGain_dB2[gSubMenuSelection];
|
||||
sprintf(String, "+%u.%01udB", mic / 2, mic % 2);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
case MENU_MIC_BAR:
|
||||
strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_STEP: {
|
||||
uint16_t step = gStepFrequencyTable[FREQUENCY_GetStepIdxFromSortedIdx(gSubMenuSelection)];
|
||||
sprintf(String, "%d.%02ukHz", step / 100, step % 100);
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_TXP:
|
||||
strcpy(String, gSubMenu_TXP[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_R_DCS:
|
||||
case MENU_T_DCS:
|
||||
if (gSubMenuSelection == 0)
|
||||
strcpy(String, "OFF");
|
||||
else if (gSubMenuSelection < 105)
|
||||
sprintf(String, "D%03oN", DCS_Options[gSubMenuSelection - 1]);
|
||||
else
|
||||
sprintf(String, "D%03oI", DCS_Options[gSubMenuSelection - 105]);
|
||||
break;
|
||||
|
||||
case MENU_R_CTCS:
|
||||
case MENU_T_CTCS:
|
||||
{
|
||||
if (gSubMenuSelection == 0)
|
||||
strcpy(String, "OFF");
|
||||
else
|
||||
sprintf(String, "%u.%uHz", CTCSS_Options[gSubMenuSelection - 1] / 10, CTCSS_Options[gSubMenuSelection - 1] % 10);
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_SFT_D:
|
||||
strcpy(String, gSubMenu_SFT_D[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_OFFSET:
|
||||
if (!gIsInSubMenu || gInputBoxIndex == 0)
|
||||
{
|
||||
sprintf(String, "%3d.%05u", gSubMenuSelection / 100000, abs(gSubMenuSelection) % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * ascii = INPUTBOX_GetAscii();
|
||||
sprintf(String, "%.3s.%.3s ",ascii, ascii + 3);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 1, 8);
|
||||
}
|
||||
|
||||
UI_PrintString("MHz", menu_item_x1, menu_item_x2, 3, 8);
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
|
||||
case MENU_W_N:
|
||||
strcpy(String, gSubMenu_W_N[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_SCR:
|
||||
strcpy(String, gSubMenu_SCRAMBLER[gSubMenuSelection]);
|
||||
#if 1
|
||||
if (gSubMenuSelection > 0 && gSetting_ScrambleEnable)
|
||||
BK4819_EnableScramble(gSubMenuSelection - 1);
|
||||
else
|
||||
BK4819_DisableScramble();
|
||||
#endif
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
case MENU_VOX:
|
||||
if (gSubMenuSelection == 0)
|
||||
strcpy(String, "OFF");
|
||||
else
|
||||
sprintf(String, "%d", gSubMenuSelection);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_ABR:
|
||||
strcpy(String, gSubMenu_BACKLIGHT[gSubMenuSelection]);
|
||||
if(BACKLIGHT_GetBrightness() < 4)
|
||||
BACKLIGHT_SetBrightness(4);
|
||||
break;
|
||||
|
||||
case MENU_ABR_MIN:
|
||||
case MENU_ABR_MAX:
|
||||
sprintf(String, "%d", gSubMenuSelection);
|
||||
if(gIsInSubMenu)
|
||||
BACKLIGHT_SetBrightness(gSubMenuSelection);
|
||||
else if(BACKLIGHT_GetBrightness() < 4)
|
||||
BACKLIGHT_SetBrightness(4);
|
||||
break;
|
||||
|
||||
case MENU_AM:
|
||||
strcpy(String, gModulationStr[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_AUTOLK:
|
||||
strcpy(String, (gSubMenuSelection == 0) ? "OFF" : "AUTO");
|
||||
break;
|
||||
|
||||
case MENU_COMPAND:
|
||||
case MENU_ABR_ON_TX_RX:
|
||||
strcpy(String, gSubMenu_RX_TX[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_AM_FIX
|
||||
case MENU_AM_FIX:
|
||||
#endif
|
||||
case MENU_BCL:
|
||||
case MENU_BEEP:
|
||||
case MENU_S_ADD1:
|
||||
case MENU_S_ADD2:
|
||||
case MENU_STE:
|
||||
case MENU_D_ST:
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
case MENU_D_DCD:
|
||||
#endif
|
||||
case MENU_D_LIVE_DEC:
|
||||
#ifdef ENABLE_NOAA
|
||||
case MENU_NOAA_S:
|
||||
#endif
|
||||
case MENU_350TX:
|
||||
case MENU_200TX:
|
||||
case MENU_500TX:
|
||||
case MENU_350EN:
|
||||
case MENU_SCREN:
|
||||
strcpy(String, gSubMenu_OFF_ON[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_MEM_CH:
|
||||
case MENU_1_CALL:
|
||||
case MENU_DEL_CH:
|
||||
{
|
||||
const bool valid = RADIO_CheckValidChannel(gSubMenuSelection, false, 0);
|
||||
|
||||
UI_GenerateChannelStringEx(String, valid, gSubMenuSelection);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 0, 8);
|
||||
|
||||
if (valid && !gAskForConfirmation)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4, 8);
|
||||
}
|
||||
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
UI_PrintString(String[0] ? String : "--", menu_item_x1, menu_item_x2, 2, 8);
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_MEM_NAME:
|
||||
{
|
||||
const bool valid = RADIO_CheckValidChannel(gSubMenuSelection, false, 0);
|
||||
|
||||
UI_GenerateChannelStringEx(String, valid, gSubMenuSelection);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 0, 8);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
const uint32_t frequency = SETTINGS_FetchChannelFrequency(gSubMenuSelection);
|
||||
|
||||
if (!gIsInSubMenu || edit_index < 0)
|
||||
{ // show the channel name
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
char *pPrintStr = String[0] ? String : "--";
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 2, 8);
|
||||
}
|
||||
else
|
||||
{ // show the channel name being edited
|
||||
UI_PrintString(edit, menu_item_x1, 0, 2, 8);
|
||||
if (edit_index < 10)
|
||||
UI_PrintString("^", menu_item_x1 + (8 * edit_index), 0, 4, 8); // show the cursor
|
||||
}
|
||||
|
||||
if (!gAskForConfirmation)
|
||||
{ // show the frequency so that the user knows the channels frequency
|
||||
sprintf(String, "%u.%05u", frequency / 100000, frequency % 100000);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4 + (gIsInSubMenu && edit_index >= 0), 8);
|
||||
}
|
||||
}
|
||||
|
||||
already_printed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_SAVE:
|
||||
strcpy(String, gSubMenu_SAVE[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_TDR:
|
||||
strcpy(String, gSubMenu_RXMode[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_TOT:
|
||||
strcpy(String, gSubMenu_TOT[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
case MENU_VOICE:
|
||||
strcpy(String, gSubMenu_VOICE[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_SC_REV:
|
||||
strcpy(String, gSubMenu_SC_REV[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_MDF:
|
||||
strcpy(String, gSubMenu_MDF[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_RP_STE:
|
||||
if (gSubMenuSelection == 0)
|
||||
strcpy(String, "OFF");
|
||||
else
|
||||
sprintf(String, "%d*100ms", gSubMenuSelection);
|
||||
break;
|
||||
|
||||
case MENU_S_LIST:
|
||||
if (gSubMenuSelection < 2)
|
||||
sprintf(String, "LIST%u", 1 + gSubMenuSelection);
|
||||
else
|
||||
strcpy(String, "ALL");
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_ALARM
|
||||
case MENU_AL_MOD:
|
||||
sprintf(String, gSubMenu_AL_MOD[gSubMenuSelection]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
case MENU_ANI_ID:
|
||||
strcpy(String, gEeprom.ANI_DTMF_ID);
|
||||
break;
|
||||
#endif
|
||||
case MENU_UPCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_UP_CODE, gEeprom.DTMF_UP_CODE + 8);
|
||||
break;
|
||||
|
||||
case MENU_DWCODE:
|
||||
sprintf(String, "%.8s\n%.8s", gEeprom.DTMF_DOWN_CODE, gEeprom.DTMF_DOWN_CODE + 8);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
case MENU_D_RSP:
|
||||
strcpy(String, gSubMenu_D_RSP[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_D_HOLD:
|
||||
sprintf(String, "%ds", gSubMenuSelection);
|
||||
break;
|
||||
#endif
|
||||
case MENU_D_PRE:
|
||||
sprintf(String, "%d*10ms", gSubMenuSelection);
|
||||
break;
|
||||
|
||||
case MENU_PTT_ID:
|
||||
strcpy(String, gSubMenu_PTT_ID[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_BAT_TXT:
|
||||
strcpy(String, gSubMenu_BAT_TXT[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
case MENU_D_LIST:
|
||||
gIsDtmfContactValid = DTMF_GetContact((int)gSubMenuSelection - 1, Contact);
|
||||
if (!gIsDtmfContactValid)
|
||||
strcpy(String, "NULL");
|
||||
else
|
||||
memcpy(String, Contact, 8);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_PONMSG:
|
||||
strcpy(String, gSubMenu_PONMSG[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_ROGER:
|
||||
strcpy(String, gSubMenu_ROGER[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_VOL:
|
||||
sprintf(String, "%u.%02uV\n%u%%",
|
||||
gBatteryVoltageAverage / 100, gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
break;
|
||||
|
||||
case MENU_RESET:
|
||||
strcpy(String, gSubMenu_RESET[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_F_LOCK:
|
||||
if(!gIsInSubMenu && gUnlockAllTxConfCnt>0 && gUnlockAllTxConfCnt<10)
|
||||
strcpy(String, "READ\nMANUAL");
|
||||
else
|
||||
strcpy(String, gSubMenu_F_LOCK[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
#ifdef ENABLE_F_CAL_MENU
|
||||
case MENU_F_CALI:
|
||||
{
|
||||
const uint32_t value = 22656 + gSubMenuSelection;
|
||||
const uint32_t xtal_Hz = (0x4f0000u + value) * 5;
|
||||
|
||||
writeXtalFreqCal(gSubMenuSelection, false);
|
||||
|
||||
sprintf(String, "%d\n%u.%06u\nMHz",
|
||||
gSubMenuSelection,
|
||||
xtal_Hz / 1000000, xtal_Hz % 1000000);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MENU_BATCAL:
|
||||
{
|
||||
const uint16_t vol = (uint32_t)gBatteryVoltageAverage * gBatteryCalibration[3] / gSubMenuSelection;
|
||||
sprintf(String, "%u.%02uV\n%u", vol / 100, vol % 100, gSubMenuSelection);
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BATTYP:
|
||||
strcpy(String, gSubMenu_BATTYP[gSubMenuSelection]);
|
||||
break;
|
||||
|
||||
case MENU_F1SHRT:
|
||||
case MENU_F1LONG:
|
||||
case MENU_F2SHRT:
|
||||
case MENU_F2LONG:
|
||||
case MENU_MLONG:
|
||||
strcpy(String, gSubMenu_SIDEFUNCTIONS[gSubMenuSelection].name);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (!already_printed)
|
||||
{ // we now do multi-line text in a single string
|
||||
|
||||
unsigned int y;
|
||||
unsigned int lines = 1;
|
||||
unsigned int len = strlen(String);
|
||||
bool small = false;
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
// count number of lines
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (String[i] == '\n' && i < (len - 1))
|
||||
{ // found new line char
|
||||
lines++;
|
||||
String[i] = 0; // null terminate the line
|
||||
}
|
||||
}
|
||||
|
||||
if (lines > 3)
|
||||
{ // use small text
|
||||
small = true;
|
||||
if (lines > 7)
|
||||
lines = 7;
|
||||
}
|
||||
|
||||
// center vertically'ish
|
||||
if (small)
|
||||
y = 3 - ((lines + 0) / 2); // untested
|
||||
else
|
||||
y = 2 - ((lines + 0) / 2);
|
||||
|
||||
// draw the text lines
|
||||
for (i = 0; i < len && lines > 0; lines--)
|
||||
{
|
||||
if (small)
|
||||
UI_PrintStringSmallNormal(String + i, menu_item_x1, menu_item_x2, y);
|
||||
else
|
||||
UI_PrintString(String + i, menu_item_x1, menu_item_x2, y, 8);
|
||||
|
||||
// look for start of next line
|
||||
while (i < len && String[i] >= 32)
|
||||
i++;
|
||||
|
||||
// hop over the null term char(s)
|
||||
while (i < len && String[i] < 32)
|
||||
i++;
|
||||
|
||||
y += small ? 1 : 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_SLIST1 || UI_MENU_GetCurrentMenuId() == MENU_SLIST2)
|
||||
{
|
||||
i = (UI_MENU_GetCurrentMenuId() == MENU_SLIST1) ? 0 : 1;
|
||||
char *pPrintStr = String;
|
||||
|
||||
if (gSubMenuSelection < 0) {
|
||||
pPrintStr = "NULL";
|
||||
} else {
|
||||
UI_GenerateChannelStringEx(String, true, gSubMenuSelection);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
// channel number
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 0, 8);
|
||||
|
||||
SETTINGS_FetchChannelName(String, gSubMenuSelection);
|
||||
pPrintStr = String[0] ? String : "--";
|
||||
|
||||
// channel name and scan-list
|
||||
if (gSubMenuSelection < 0 || !gEeprom.SCAN_LIST_ENABLED[i]) {
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 2, 8);
|
||||
} else {
|
||||
UI_PrintStringSmallNormal(pPrintStr, menu_item_x1, menu_item_x2, 2);
|
||||
|
||||
if (IS_MR_CHANNEL(gEeprom.SCANLIST_PRIORITY_CH1[i])) {
|
||||
sprintf(String, "PRI%d:%u", 1, gEeprom.SCANLIST_PRIORITY_CH1[i] + 1);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 3, 8);
|
||||
}
|
||||
|
||||
if (IS_MR_CHANNEL(gEeprom.SCANLIST_PRIORITY_CH2[i])) {
|
||||
sprintf(String, "PRI%d:%u", 2, gEeprom.SCANLIST_PRIORITY_CH2[i] + 1);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_R_CTCS || UI_MENU_GetCurrentMenuId() == MENU_R_DCS) && gCssBackgroundScan)
|
||||
UI_PrintString("SCAN", menu_item_x1, menu_item_x2, 4, 8);
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_D_LIST && gIsDtmfContactValid) {
|
||||
Contact[11] = 0;
|
||||
memcpy(&gDTMF_ID, Contact + 8, 4);
|
||||
sprintf(String, "ID:%4s", gDTMF_ID);
|
||||
UI_PrintString(String, menu_item_x1, menu_item_x2, 4, 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (UI_MENU_GetCurrentMenuId() == MENU_R_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_CTCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_R_DCS ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_T_DCS
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
|| UI_MENU_GetCurrentMenuId() == MENU_D_LIST
|
||||
#endif
|
||||
) {
|
||||
sprintf(String, "%2d", gSubMenuSelection);
|
||||
UI_PrintStringSmallNormal(String, 105, 0, 0);
|
||||
}
|
||||
|
||||
if ((UI_MENU_GetCurrentMenuId() == MENU_RESET ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_CH ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_MEM_NAME ||
|
||||
UI_MENU_GetCurrentMenuId() == MENU_DEL_CH) && gAskForConfirmation)
|
||||
{ // display confirmation
|
||||
char *pPrintStr = (gAskForConfirmation == 1) ? "SURE?" : "WAIT!";
|
||||
UI_PrintString(pPrintStr, menu_item_x1, menu_item_x2, 5, 8);
|
||||
}
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
180
ui/menu.h
Normal file
180
ui/menu.h
Normal file
@@ -0,0 +1,180 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_MENU_H
|
||||
#define UI_MENU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "audio.h" // VOICE_ID_t
|
||||
#include "settings.h"
|
||||
|
||||
typedef struct {
|
||||
const char name[7]; // menu display area only has room for 6 characters
|
||||
VOICE_ID_t voice_id;
|
||||
uint8_t menu_id;
|
||||
} t_menu_item;
|
||||
|
||||
enum
|
||||
{
|
||||
MENU_SQL = 0,
|
||||
MENU_STEP,
|
||||
MENU_TXP,
|
||||
MENU_R_DCS,
|
||||
MENU_R_CTCS,
|
||||
MENU_T_DCS,
|
||||
MENU_T_CTCS,
|
||||
MENU_SFT_D,
|
||||
MENU_OFFSET,
|
||||
MENU_TOT,
|
||||
MENU_W_N,
|
||||
MENU_SCR,
|
||||
MENU_BCL,
|
||||
MENU_MEM_CH,
|
||||
MENU_DEL_CH,
|
||||
MENU_MEM_NAME,
|
||||
MENU_MDF,
|
||||
MENU_SAVE,
|
||||
#ifdef ENABLE_VOX
|
||||
MENU_VOX,
|
||||
#endif
|
||||
MENU_ABR,
|
||||
MENU_ABR_ON_TX_RX,
|
||||
MENU_ABR_MIN,
|
||||
MENU_ABR_MAX,
|
||||
MENU_TDR,
|
||||
MENU_BEEP,
|
||||
#ifdef ENABLE_VOICE
|
||||
MENU_VOICE,
|
||||
#endif
|
||||
MENU_SC_REV,
|
||||
MENU_AUTOLK,
|
||||
MENU_S_ADD1,
|
||||
MENU_S_ADD2,
|
||||
MENU_STE,
|
||||
MENU_RP_STE,
|
||||
MENU_MIC,
|
||||
#ifdef ENABLE_AUDIO_BAR
|
||||
MENU_MIC_BAR,
|
||||
#endif
|
||||
MENU_COMPAND,
|
||||
MENU_1_CALL,
|
||||
MENU_S_LIST,
|
||||
MENU_SLIST1,
|
||||
MENU_SLIST2,
|
||||
#ifdef ENABLE_ALARM
|
||||
MENU_AL_MOD,
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
MENU_ANI_ID,
|
||||
#endif
|
||||
MENU_UPCODE,
|
||||
MENU_DWCODE,
|
||||
MENU_PTT_ID,
|
||||
MENU_D_ST,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
MENU_D_RSP,
|
||||
MENU_D_HOLD,
|
||||
#endif
|
||||
MENU_D_PRE,
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
MENU_D_DCD,
|
||||
MENU_D_LIST,
|
||||
#endif
|
||||
MENU_D_LIVE_DEC,
|
||||
MENU_PONMSG,
|
||||
MENU_ROGER,
|
||||
MENU_VOL,
|
||||
MENU_BAT_TXT,
|
||||
MENU_AM,
|
||||
#ifdef ENABLE_AM_FIX
|
||||
MENU_AM_FIX,
|
||||
#endif
|
||||
#ifdef ENABLE_NOAA
|
||||
MENU_NOAA_S,
|
||||
#endif
|
||||
MENU_RESET,
|
||||
MENU_F_LOCK,
|
||||
MENU_200TX,
|
||||
MENU_350TX,
|
||||
MENU_500TX,
|
||||
MENU_350EN,
|
||||
MENU_SCREN,
|
||||
#ifdef ENABLE_F_CAL_MENU
|
||||
MENU_F_CALI, // reference xtal calibration
|
||||
#endif
|
||||
MENU_BATCAL, // battery voltage calibration
|
||||
MENU_F1SHRT,
|
||||
MENU_F1LONG,
|
||||
MENU_F2SHRT,
|
||||
MENU_F2LONG,
|
||||
MENU_MLONG,
|
||||
MENU_BATTYP
|
||||
};
|
||||
|
||||
extern const uint8_t FIRST_HIDDEN_MENU_ITEM;
|
||||
extern const t_menu_item MenuList[];
|
||||
|
||||
extern const char gSubMenu_TXP[3][5];
|
||||
extern const char gSubMenu_SFT_D[3][4];
|
||||
extern const char gSubMenu_W_N[2][7];
|
||||
extern const char gSubMenu_OFF_ON[2][4];
|
||||
extern const char gSubMenu_SAVE[5][4];
|
||||
extern const char gSubMenu_TOT[11][7];
|
||||
extern const char* const gSubMenu_RXMode[4];
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
extern const char gSubMenu_VOICE[3][4];
|
||||
#endif
|
||||
extern const char gSubMenu_SC_REV[3][8];
|
||||
extern const char* const gSubMenu_MDF[4];
|
||||
#ifdef ENABLE_ALARM
|
||||
extern const char gSubMenu_AL_MOD[2][5];
|
||||
#endif
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
extern const char gSubMenu_D_RSP[4][11];
|
||||
#endif
|
||||
extern const char* const gSubMenu_PTT_ID[5];
|
||||
extern const char gSubMenu_PONMSG[4][8];
|
||||
extern const char gSubMenu_ROGER[3][6];
|
||||
extern const char gSubMenu_RESET[2][4];
|
||||
extern const char* const gSubMenu_F_LOCK[F_LOCK_LEN];
|
||||
extern const char gSubMenu_BACKLIGHT[8][7];
|
||||
extern const char gSubMenu_RX_TX[4][6];
|
||||
extern const char gSubMenu_BAT_TXT[3][8];
|
||||
extern const char gSubMenu_BATTYP[2][9];
|
||||
extern const char gSubMenu_SCRAMBLER[11][7];
|
||||
|
||||
typedef struct {char* name; uint8_t id;} t_sidefunction;
|
||||
extern const uint8_t gSubMenu_SIDEFUNCTIONS_size;
|
||||
extern const t_sidefunction gSubMenu_SIDEFUNCTIONS[];
|
||||
|
||||
extern bool gIsInSubMenu;
|
||||
|
||||
extern uint8_t gMenuCursor;
|
||||
|
||||
extern int32_t gSubMenuSelection;
|
||||
|
||||
extern char edit_original[17];
|
||||
extern char edit[17];
|
||||
extern int edit_index;
|
||||
|
||||
void UI_DisplayMenu(void);
|
||||
int UI_MENU_GetCurrentMenuId();
|
||||
uint8_t UI_MENU_GetMenuIdx(uint8_t id);
|
||||
|
||||
#endif
|
||||
83
ui/scanner.c
Normal file
83
ui/scanner.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "app/scanner.h"
|
||||
#include "dcs.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "misc.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/scanner.h"
|
||||
|
||||
void UI_DisplayScanner(void)
|
||||
{
|
||||
char String[16] = {0};
|
||||
char *pPrintStr = String;
|
||||
bool bCentered;
|
||||
uint8_t Start;
|
||||
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gScanSingleFrequency || (gScanCssState != SCAN_CSS_STATE_OFF && gScanCssState != SCAN_CSS_STATE_FAILED)) {
|
||||
sprintf(String, "FREQ:%u.%05u", gScanFrequency / 100000, gScanFrequency % 100000);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
pPrintStr = "FREQ:**.*****";
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 1, 8);
|
||||
|
||||
if (gScanCssState < SCAN_CSS_STATE_FOUND || !gScanUseCssResult) {
|
||||
pPrintStr = "CTC:******";
|
||||
} else if (gScanCssResultType == CODE_TYPE_CONTINUOUS_TONE) {
|
||||
sprintf(String, "CTC:%u.%uHz", CTCSS_Options[gScanCssResultCode] / 10, CTCSS_Options[gScanCssResultCode] % 10);
|
||||
pPrintStr = String;
|
||||
} else {
|
||||
sprintf(String, "DCS:D%03oN", DCS_Options[gScanCssResultCode]);
|
||||
pPrintStr = String;
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, 2, 0, 3, 8);
|
||||
memset(String, 0, sizeof(String));
|
||||
if (gScannerSaveState == SCAN_SAVE_CHANNEL) {
|
||||
pPrintStr = "SAVE?";
|
||||
Start = 0;
|
||||
bCentered = 1;
|
||||
} else {
|
||||
Start = 2;
|
||||
bCentered = 0;
|
||||
|
||||
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
||||
strcpy(String, "SAVE:");
|
||||
UI_GenerateChannelStringEx(String + 5, gShowChPrefix, gScanChannel);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState < SCAN_CSS_STATE_FOUND) {
|
||||
strcpy(String, "SCAN");
|
||||
memset(String + 4, '.', (gScanProgressIndicator & 7) + 1);
|
||||
pPrintStr = String;
|
||||
} else if (gScanCssState == SCAN_CSS_STATE_FOUND) {
|
||||
pPrintStr = "SCAN CMP.";
|
||||
} else {
|
||||
pPrintStr = "SCAN FAIL.";
|
||||
}
|
||||
}
|
||||
|
||||
UI_PrintString(pPrintStr, Start, bCentered ? 127 : 0, 5, 8);
|
||||
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
23
ui/scanner.h
Normal file
23
ui/scanner.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_SCANNER_H
|
||||
#define UI_SCANNER_H
|
||||
|
||||
void UI_DisplayScanner(void);
|
||||
|
||||
#endif
|
||||
|
||||
188
ui/status.c
Normal file
188
ui/status.c
Normal file
@@ -0,0 +1,188 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "app/chFrScanner.h"
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
#endif
|
||||
#include "app/scanner.h"
|
||||
#include "bitmaps.h"
|
||||
#include "driver/keyboard.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "functions.h"
|
||||
#include "helper/battery.h"
|
||||
#include "misc.h"
|
||||
#include "settings.h"
|
||||
#include "ui/battery.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/status.h"
|
||||
|
||||
void UI_DisplayStatus()
|
||||
{
|
||||
gUpdateStatus = false;
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
|
||||
uint8_t *line = gStatusLine;
|
||||
unsigned int x = 0;
|
||||
// **************
|
||||
|
||||
// POWER-SAVE indicator
|
||||
if (gCurrentFunction == FUNCTION_TRANSMIT) {
|
||||
memcpy(line + x, BITMAP_TX, sizeof(BITMAP_TX));
|
||||
}
|
||||
else if (FUNCTION_IsRx()) {
|
||||
memcpy(line + x, BITMAP_RX, sizeof(BITMAP_RX));
|
||||
}
|
||||
else if (gCurrentFunction == FUNCTION_POWER_SAVE) {
|
||||
memcpy(line + x, BITMAP_POWERSAVE, sizeof(BITMAP_POWERSAVE));
|
||||
}
|
||||
x += 8;
|
||||
unsigned int x1 = x;
|
||||
|
||||
#ifdef ENABLE_NOAA
|
||||
if (gIsNoaaMode) { // NOASS SCAN indicator
|
||||
memcpy(line + x, BITMAP_NOAA, sizeof(BITMAP_NOAA));
|
||||
x1 = x + sizeof(BITMAP_NOAA);
|
||||
}
|
||||
x += sizeof(BITMAP_NOAA);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_DTMF_CALLING
|
||||
if (gSetting_KILLED) {
|
||||
memset(line + x, 0xFF, 10);
|
||||
x1 = x + 10;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
if (gFmRadioMode) { // FM indicator
|
||||
memcpy(line + x, BITMAP_FM, sizeof(BITMAP_FM));
|
||||
x1 = x + sizeof(BITMAP_FM);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{ // SCAN indicator
|
||||
if (gScanStateDir != SCAN_OFF || SCANNER_IsScanning()) {
|
||||
char * s = "";
|
||||
if (IS_MR_CHANNEL(gNextMrChannel) && !SCANNER_IsScanning()) { // channel mode
|
||||
switch(gEeprom.SCAN_LIST_DEFAULT) {
|
||||
case 0: s = "1"; break;
|
||||
case 1: s = "2"; break;
|
||||
case 2: s = "*"; break;
|
||||
}
|
||||
}
|
||||
else { // frequency mode
|
||||
s = "S";
|
||||
}
|
||||
UI_PrintStringSmallBufferNormal(s, line + x + 1);
|
||||
x1 = x + 10;
|
||||
}
|
||||
}
|
||||
x += 10; // font character width
|
||||
|
||||
#ifdef ENABLE_VOICE
|
||||
// VOICE indicator
|
||||
if (gEeprom.VOICE_PROMPT != VOICE_PROMPT_OFF){
|
||||
memcpy(line + x, BITMAP_VoicePrompt, sizeof(BITMAP_VoicePrompt));
|
||||
x1 = x + sizeof(BITMAP_VoicePrompt);
|
||||
}
|
||||
x += sizeof(BITMAP_VoicePrompt);
|
||||
#endif
|
||||
|
||||
if(!SCANNER_IsScanning()) {
|
||||
uint8_t dw = (gEeprom.DUAL_WATCH != DUAL_WATCH_OFF) + (gEeprom.CROSS_BAND_RX_TX != CROSS_BAND_OFF) * 2;
|
||||
if(dw == 1 || dw == 3) { // DWR - dual watch + respond
|
||||
if(gDualWatchActive)
|
||||
memcpy(line + x + (dw==1?0:2), BITMAP_TDR1, sizeof(BITMAP_TDR1) - (dw==1?0:5));
|
||||
else
|
||||
memcpy(line + x + 3, BITMAP_TDR2, sizeof(BITMAP_TDR2));
|
||||
}
|
||||
else if(dw == 2) { // XB - crossband
|
||||
memcpy(line + x + 2, BITMAP_XB, sizeof(BITMAP_XB));
|
||||
}
|
||||
}
|
||||
x += sizeof(BITMAP_TDR1) + 1;
|
||||
|
||||
#ifdef ENABLE_VOX
|
||||
// VOX indicator
|
||||
if (gEeprom.VOX_SWITCH) {
|
||||
memcpy(line + x, BITMAP_VOX, sizeof(BITMAP_VOX));
|
||||
x1 = x + sizeof(BITMAP_VOX) + 1;
|
||||
}
|
||||
x += sizeof(BITMAP_VOX) + 1;
|
||||
#endif
|
||||
|
||||
x = MAX(x1, 61u);
|
||||
|
||||
// KEY-LOCK indicator
|
||||
if (gEeprom.KEY_LOCK) {
|
||||
memcpy(line + x, BITMAP_KeyLock, sizeof(BITMAP_KeyLock));
|
||||
x += sizeof(BITMAP_KeyLock);
|
||||
x1 = x;
|
||||
}
|
||||
else if (gWasFKeyPressed) {
|
||||
memcpy(line + x, BITMAP_F_Key, sizeof(BITMAP_F_Key));
|
||||
x += sizeof(BITMAP_F_Key);
|
||||
x1 = x;
|
||||
}
|
||||
|
||||
{ // battery voltage or percentage
|
||||
char s[8] = "";
|
||||
unsigned int x2 = LCD_WIDTH - sizeof(BITMAP_BatteryLevel1) - 0;
|
||||
|
||||
if (gChargingWithTypeC)
|
||||
x2 -= sizeof(BITMAP_USB_C); // the radio is on charge
|
||||
|
||||
switch (gSetting_battery_text) {
|
||||
default:
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1: { // voltage
|
||||
const uint16_t voltage = (gBatteryVoltageAverage <= 999) ? gBatteryVoltageAverage : 999; // limit to 9.99V
|
||||
sprintf(s, "%u.%02uV", voltage / 100, voltage % 100);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: // percentage
|
||||
sprintf(s, "%u%%", BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned int space_needed = (7 * strlen(s));
|
||||
if (x2 >= (x1 + space_needed))
|
||||
UI_PrintStringSmallBufferNormal(s, line + x2 - space_needed);
|
||||
}
|
||||
|
||||
// move to right side of the screen
|
||||
x = LCD_WIDTH - sizeof(BITMAP_BatteryLevel1) - sizeof(BITMAP_USB_C);
|
||||
|
||||
// USB-C charge indicator
|
||||
if (gChargingWithTypeC)
|
||||
memcpy(line + x, BITMAP_USB_C, sizeof(BITMAP_USB_C));
|
||||
x += sizeof(BITMAP_USB_C);
|
||||
|
||||
// BATTERY LEVEL indicator
|
||||
UI_DrawBattery(line + x, gBatteryDisplayLevel, gLowBatteryBlink);
|
||||
|
||||
// **************
|
||||
|
||||
ST7565_BlitStatusLine();
|
||||
}
|
||||
23
ui/status.h
Normal file
23
ui/status.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_STATUS_H
|
||||
#define UI_STATUS_H
|
||||
|
||||
void UI_DisplayStatus();
|
||||
|
||||
#endif
|
||||
|
||||
97
ui/ui.c
Normal file
97
ui/ui.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app/chFrScanner.h"
|
||||
#include "app/dtmf.h"
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "app/fm.h"
|
||||
#endif
|
||||
#include "driver/keyboard.h"
|
||||
#include "misc.h"
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
#include "ui/aircopy.h"
|
||||
#endif
|
||||
#ifdef ENABLE_FMRADIO
|
||||
#include "ui/fmradio.h"
|
||||
#endif
|
||||
#include "ui/inputbox.h"
|
||||
#include "ui/main.h"
|
||||
#include "ui/menu.h"
|
||||
#include "ui/scanner.h"
|
||||
#include "ui/ui.h"
|
||||
#include "../misc.h"
|
||||
|
||||
GUI_DisplayType_t gScreenToDisplay;
|
||||
GUI_DisplayType_t gRequestDisplayScreen = DISPLAY_INVALID;
|
||||
|
||||
uint8_t gAskForConfirmation;
|
||||
bool gAskToSave;
|
||||
bool gAskToDelete;
|
||||
|
||||
|
||||
void (*UI_DisplayFunctions[])(void) = {
|
||||
[DISPLAY_MAIN] = &UI_DisplayMain,
|
||||
[DISPLAY_MENU] = &UI_DisplayMenu,
|
||||
[DISPLAY_SCANNER] = &UI_DisplayScanner,
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
[DISPLAY_FM] = &UI_DisplayFM,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
[DISPLAY_AIRCOPY] = &UI_DisplayAircopy,
|
||||
#endif
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(UI_DisplayFunctions) == DISPLAY_N_ELEM);
|
||||
|
||||
void GUI_DisplayScreen(void)
|
||||
{
|
||||
if (gScreenToDisplay != DISPLAY_INVALID) {
|
||||
UI_DisplayFunctions[gScreenToDisplay]();
|
||||
}
|
||||
}
|
||||
|
||||
void GUI_SelectNextDisplay(GUI_DisplayType_t Display)
|
||||
{
|
||||
if (Display == DISPLAY_INVALID)
|
||||
return;
|
||||
|
||||
if (gScreenToDisplay != Display)
|
||||
{
|
||||
DTMF_clear_input_box();
|
||||
|
||||
gInputBoxIndex = 0;
|
||||
gIsInSubMenu = false;
|
||||
gCssBackgroundScan = false;
|
||||
gScanStateDir = SCAN_OFF;
|
||||
#ifdef ENABLE_FMRADIO
|
||||
gFM_ScanState = FM_SCAN_OFF;
|
||||
#endif
|
||||
gAskForConfirmation = 0;
|
||||
gAskToSave = false;
|
||||
gAskToDelete = false;
|
||||
gWasFKeyPressed = false;
|
||||
|
||||
gUpdateStatus = true;
|
||||
}
|
||||
|
||||
gScreenToDisplay = Display;
|
||||
gUpdateDisplay = true;
|
||||
}
|
||||
53
ui/ui.h
Normal file
53
ui/ui.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum GUI_DisplayType_t
|
||||
{
|
||||
DISPLAY_MAIN = 0,
|
||||
DISPLAY_MENU,
|
||||
DISPLAY_SCANNER,
|
||||
|
||||
#ifdef ENABLE_FMRADIO
|
||||
DISPLAY_FM,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_AIRCOPY
|
||||
DISPLAY_AIRCOPY,
|
||||
#endif
|
||||
|
||||
DISPLAY_N_ELEM,
|
||||
DISPLAY_INVALID = 0xFFu
|
||||
};
|
||||
|
||||
typedef enum GUI_DisplayType_t GUI_DisplayType_t;
|
||||
|
||||
extern GUI_DisplayType_t gScreenToDisplay;
|
||||
extern GUI_DisplayType_t gRequestDisplayScreen;
|
||||
|
||||
extern uint8_t gAskForConfirmation;
|
||||
extern bool gAskToSave;
|
||||
extern bool gAskToDelete;
|
||||
|
||||
void GUI_DisplayScreen(void);
|
||||
void GUI_SelectNextDisplay(GUI_DisplayType_t Display);
|
||||
|
||||
#endif
|
||||
77
ui/welcome.c
Normal file
77
ui/welcome.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "driver/eeprom.h"
|
||||
#include "driver/st7565.h"
|
||||
#include "external/printf/printf.h"
|
||||
#include "helper/battery.h"
|
||||
#include "settings.h"
|
||||
#include "misc.h"
|
||||
#include "ui/helper.h"
|
||||
#include "ui/welcome.h"
|
||||
#include "ui/status.h"
|
||||
#include "version.h"
|
||||
|
||||
void UI_DisplayReleaseKeys(void)
|
||||
{
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_DisplayClear();
|
||||
|
||||
UI_PrintString("RELEASE", 0, 127, 1, 10);
|
||||
UI_PrintString("ALL KEYS", 0, 127, 3, 10);
|
||||
|
||||
ST7565_BlitStatusLine(); // blank status line
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
|
||||
void UI_DisplayWelcome(void)
|
||||
{
|
||||
char WelcomeString0[16];
|
||||
char WelcomeString1[16];
|
||||
|
||||
memset(gStatusLine, 0, sizeof(gStatusLine));
|
||||
UI_DisplayClear();
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_NONE || gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_FULL_SCREEN) {
|
||||
ST7565_FillScreen(0xFF);
|
||||
} else {
|
||||
memset(WelcomeString0, 0, sizeof(WelcomeString0));
|
||||
memset(WelcomeString1, 0, sizeof(WelcomeString1));
|
||||
|
||||
if (gEeprom.POWER_ON_DISPLAY_MODE == POWER_ON_DISPLAY_MODE_VOLTAGE)
|
||||
{
|
||||
strcpy(WelcomeString0, "VOLTAGE");
|
||||
sprintf(WelcomeString1, "%u.%02uV %u%%",
|
||||
gBatteryVoltageAverage / 100,
|
||||
gBatteryVoltageAverage % 100,
|
||||
BATTERY_VoltsToPercent(gBatteryVoltageAverage));
|
||||
}
|
||||
else
|
||||
{
|
||||
EEPROM_ReadBuffer(0x0EB0, WelcomeString0, 16);
|
||||
EEPROM_ReadBuffer(0x0EC0, WelcomeString1, 16);
|
||||
}
|
||||
|
||||
UI_PrintString(WelcomeString0, 0, 127, 0, 10);
|
||||
UI_PrintString(WelcomeString1, 0, 127, 2, 10);
|
||||
UI_PrintStringSmallNormal(Version, 0, 128, 6);
|
||||
|
||||
ST7565_BlitStatusLine(); // blank status line
|
||||
ST7565_BlitFullScreen();
|
||||
}
|
||||
}
|
||||
24
ui/welcome.h
Normal file
24
ui/welcome.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* Copyright 2023 Dual Tachyon
|
||||
* https://github.com/DualTachyon
|
||||
*
|
||||
* 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 UI_WELCOME_H
|
||||
#define UI_WELCOME_H
|
||||
|
||||
void UI_DisplayReleaseKeys(void);
|
||||
void UI_DisplayWelcome(void);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user