mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-12-01 19:52:13 +00:00
Compare commits
31 Commits
nightly-ta
...
nightly-ta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86c326ef12 | ||
|
|
5ae97d47f1 | ||
|
|
762512b7dc | ||
|
|
9c848a6215 | ||
|
|
9c86e15ddb | ||
|
|
57ccdb0044 | ||
|
|
1172175cc8 | ||
|
|
9ed8ad5903 | ||
|
|
e4da8b979e | ||
|
|
4b83fd8a40 | ||
|
|
cd80da58d8 | ||
|
|
a1f7010e08 | ||
|
|
3499d2fa75 | ||
|
|
948e039574 | ||
|
|
27dc37713b | ||
|
|
ed834e3553 | ||
|
|
ce08d93ae2 | ||
|
|
13fd1b1f3b | ||
|
|
d04c781ada | ||
|
|
24605777a6 | ||
|
|
b5ac792853 | ||
|
|
44c319dcc6 | ||
|
|
a442971b81 | ||
|
|
8b5adb6bc1 | ||
|
|
c3add0ce84 | ||
|
|
8725b01995 | ||
|
|
936e8279f9 | ||
|
|
6193023c8f | ||
|
|
1800939833 | ||
|
|
2f48fc2ef9 | ||
|
|
918ec0574f |
48
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
48
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Bug report
|
||||
description: File a bug reports regarding the firmware.
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Mayhem firmware.
|
||||
Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug.
|
||||
description: "A clear and concise description of what the bug is."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: "How can this bug be reproduced?"
|
||||
placeholder: |
|
||||
1. Switch on...
|
||||
2. Press button '....'
|
||||
3. Wait for the end of the universe
|
||||
4. It burns
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: "A clear and concise description of what you expected to happen"
|
||||
placeholder: |
|
||||
1. Generates file on...
|
||||
2. I get a cheeseburger...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Environment/versions
|
||||
description: Specify extra details about versions and environments affected
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
||||
24
.github/ISSUE_TEMPLATE/02_feature_request.yml
vendored
Normal file
24
.github/ISSUE_TEMPLATE/02_feature_request.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Feature Request
|
||||
description: For feature requests regarding the firmware.
|
||||
labels: ['feature request']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.
|
||||
Please try the latest nightly release before submitting this, make sure it has not been implemented already. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: "Description of the feature you're suggesting."
|
||||
description: |
|
||||
Please describe your feature request in as many details as possible.
|
||||
- Describe what it should do.
|
||||
- Note whether it is to extend existing functionality or introduce new functionality.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
||||
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve the software
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
|
||||
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases)
|
||||
|
||||
----
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Tap on '....'
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Affected versions**
|
||||
Please write any difference related with the Expected behavior, on the following versions:
|
||||
* Latest Stable release:
|
||||
* Latest Nightly release:
|
||||
* Previous working release:
|
||||
|
||||
**Additional**
|
||||
If the bug is difficult to explain, additionally to the text please include images and videos.
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Need more help?
|
||||
url: https://discord.com/invite/tuwVMv3
|
||||
about: For any question that does not fit as an issue, check our discord.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. Remember that adding stuff is always possible, but time is a limited resource for everyone. Check the wiki for more information how to compile the firmware and try to explore modifying the code yourself.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional**
|
||||
If the suggestion is difficult to explain, additionally to the text please include images and videos.
|
||||
2
.github/workflows/past_version.txt
vendored
2
.github/workflows/past_version.txt
vendored
@@ -1 +1 @@
|
||||
v1.9.0
|
||||
v1.9.1
|
||||
|
||||
2
.github/workflows/version.txt
vendored
2
.github/workflows/version.txt
vendored
@@ -1 +1 @@
|
||||
v1.9.1
|
||||
v2.0.0
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -68,9 +68,11 @@ CMakeFiles/
|
||||
.DS_Store
|
||||
/firmware/CMakeCache.txt
|
||||
|
||||
# Python env/ venv
|
||||
# Python env/ venv and cache
|
||||
env/
|
||||
venv/
|
||||
**/__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Other
|
||||
*.bak
|
||||
|
||||
15
README.md
15
README.md
@@ -1,5 +1,10 @@
|
||||
> [!IMPORTANT]
|
||||
> this repository **has just been moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please keep this in mind to **update your links** accordingly.
|
||||
> [!WARNING]
|
||||
> Recently, we have become aware of certain individuals distributing prepackaged packages of Mayhem for a fee, and we want to bring this to your attention to ensure the integrity and security of our project.
|
||||
>
|
||||
> We would like to clarify that none of the contributors to Mayhem receive any monetary compensation for their efforts. All the work put into this project is driven by passion and hobby, and we are committed to providing the community with an open-source solution that remains free of charge.
|
||||
>
|
||||
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED PACKAGES, YOU'RE BEING SCAMMED.__ Seek a refund from your card company and report the issue immediately.
|
||||
> The only legitimate link leading to our repositories is the organization [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please ensure that any download or access to our software is done exclusively through these trusted sources.
|
||||
|
||||
# PortaPack Mayhem
|
||||
|
||||
@@ -13,11 +18,11 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
# What is this?
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check these:
|
||||
|
||||
[](https://www.youtube.com/watch?v=H-bqdWfbhpg)
|
||||
[<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="512">](https://grabify.link/C0J6ZR)
|
||||
|
||||
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="254">](https://grabify.link/9UZGEW)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ if(cpp20_supported)
|
||||
else()
|
||||
set(USE_CPPOPT "-std=c++17")
|
||||
endif()
|
||||
set(USE_CPPOPT "${USE_CPPOPT} -flto -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized -Wno-volatile")
|
||||
set(USE_CPPOPT "${USE_CPPOPT} -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized -Wno-volatile")
|
||||
|
||||
# Enable this if you want the linker to remove unused code and data
|
||||
set(USE_LINK_GC yes)
|
||||
@@ -52,7 +52,7 @@ set(USE_LINK_GC yes)
|
||||
# Linker extra options here.
|
||||
set(USE_LDOPT)
|
||||
|
||||
# Enable this if you want link time optimizations (LTO)
|
||||
# Enable this if you want link time optimizations (LTO) - this flag affects chibios only
|
||||
set(USE_LTO no)
|
||||
|
||||
# If enabled, this option allows to compile the application in THUMB mode.
|
||||
@@ -293,7 +293,7 @@ set(CPPSRC
|
||||
# apps/ui_jammer.cpp
|
||||
# apps/ui_keyfob.cpp
|
||||
# apps/ui_lcr.cpp
|
||||
apps/ui_level.cpp
|
||||
apps/ui_level.cpp
|
||||
apps/ui_looking_glass_app.cpp
|
||||
apps/ui_mictx.cpp
|
||||
apps/ui_modemsetup.cpp
|
||||
@@ -304,7 +304,7 @@ set(CPPSRC
|
||||
apps/ui_pocsag_tx.cpp
|
||||
apps/ui_rds.cpp
|
||||
apps/ui_recon_settings.cpp
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_remote.cpp
|
||||
apps/ui_scanner.cpp
|
||||
apps/ui_sd_over_usb.cpp
|
||||
@@ -347,10 +347,12 @@ set(CPPSRC
|
||||
${HACKRF_CPLD_DATA_CPP}
|
||||
ui_external_items_menu_loader.cpp
|
||||
view_factory_base.cpp
|
||||
|
||||
${EXTCPPSRC}
|
||||
)
|
||||
|
||||
set_source_files_properties(${CPPSRC} PROPERTIES COMPILE_FLAGS -flto) # Add lto flag to the non-external sources only
|
||||
|
||||
list (APPEND CPPSRC ${EXTCPPSRC}) # Append external sources after setting lto flag to internal ones
|
||||
|
||||
# C sources to be compiled in ARM mode regardless of the global setting.
|
||||
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
|
||||
# option that results in lower performance and larger code size.
|
||||
|
||||
@@ -323,6 +323,11 @@ bool ControlsSwitchesWidget::on_key(const KeyEvent key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ControlsSwitchesWidget::on_encoder(const EncoderEvent delta) {
|
||||
last_delta = delta;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ControlsSwitchesWidget::paint(Painter& painter) {
|
||||
const auto pos = screen_pos();
|
||||
|
||||
@@ -405,6 +410,8 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
|
||||
|
||||
switches_event >>= 1;
|
||||
}
|
||||
|
||||
painter.draw_string({5 * 8, 12 * 16}, Styles::light_grey, to_string_dec_int(last_delta, 3));
|
||||
}
|
||||
|
||||
void ControlsSwitchesWidget::on_frame_sync() {
|
||||
@@ -450,6 +457,17 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
/* DebugReboot **********************************************/
|
||||
|
||||
DebugReboot::DebugReboot(NavigationView& nav) {
|
||||
(void)nav;
|
||||
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 0);
|
||||
|
||||
while (1)
|
||||
__WFE();
|
||||
}
|
||||
|
||||
/* DebugMenuView *********************************************************/
|
||||
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
@@ -465,6 +483,7 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Pers. Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }},
|
||||
//{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{"Reboot", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<DebugReboot>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SDCardDebugView>(); }},
|
||||
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<DebugScreenTest>(); }},
|
||||
|
||||
@@ -252,18 +252,21 @@ class ControlsSwitchesWidget : public Widget {
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect},
|
||||
key_event_mask(0),
|
||||
long_press_key_event_mask{0} {
|
||||
long_press_key_event_mask{0},
|
||||
last_delta{0} {
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
void on_show() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
uint8_t key_event_mask;
|
||||
uint8_t long_press_key_event_mask;
|
||||
EncoderEvent last_delta;
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
@@ -285,6 +288,7 @@ class DebugControlsView : public View {
|
||||
private:
|
||||
Labels labels{
|
||||
{{8 * 8, 1 * 16}, "Controls State", Color::white()},
|
||||
{{0 * 8, 11 * 16}, "Dial:", Color::grey()},
|
||||
{{0 * 8, 14 * 16}, "Long-Press Mode:", Color::grey()}};
|
||||
|
||||
ControlsSwitchesWidget switches_widget{
|
||||
@@ -420,6 +424,11 @@ class DebugPeripheralsMenuView : public BtnGridView {
|
||||
std::string title() const override { return "Peripherals"; };
|
||||
};
|
||||
|
||||
class DebugReboot : public BtnGridView {
|
||||
public:
|
||||
DebugReboot(NavigationView& nav);
|
||||
};
|
||||
|
||||
class DebugMenuView : public BtnGridView {
|
||||
public:
|
||||
DebugMenuView(NavigationView& nav);
|
||||
|
||||
@@ -28,27 +28,49 @@ namespace ui {
|
||||
|
||||
static const char16_t* firmware_folder = u"/FIRMWARE";
|
||||
|
||||
// Firmware image validation
|
||||
static const char* hackrf_magic = "HACKRFFW";
|
||||
#define FIRMWARE_INFO_AREA_OFFSET 0x400
|
||||
#define FIRST_CHECKSUM_NIGHTLY 240125
|
||||
|
||||
Thread* FlashUtilityView::thread{nullptr};
|
||||
static constexpr size_t max_filename_length = 26;
|
||||
|
||||
bool valid_firmware_file(std::filesystem::path::string_type path) {
|
||||
File firmware_file;
|
||||
bool require_checksum{false};
|
||||
uint32_t read_buffer[128];
|
||||
uint32_t checksum{(uint32_t)~FLASH_EXPECTED_CHECKSUM}; // initializing to invalid checksum in case file can't be read
|
||||
uint32_t checksum{FLASH_CHECKSUM_ERROR}; // initializing to invalid checksum in case file can't be read
|
||||
|
||||
static_assert((FIRMWARE_INFO_AREA_OFFSET % sizeof(read_buffer)) == 0, "Read buffer size must divide evenly into FIRMWARE_INFO_AREA_OFFSET");
|
||||
|
||||
// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
|
||||
auto result = firmware_file.open(path.c_str());
|
||||
if (!result.is_valid()) {
|
||||
checksum = 0;
|
||||
for (uint32_t i = 0; i < FLASH_ROM_SIZE / sizeof(read_buffer); i++) {
|
||||
for (uint32_t offset = 0; offset < FLASH_ROM_SIZE; offset += sizeof(read_buffer)) {
|
||||
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
|
||||
|
||||
// if file is smaller than 1MB, assume it's a downgrade to an old FW version and ignore the checksum
|
||||
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
|
||||
checksum = FLASH_EXPECTED_CHECKSUM;
|
||||
// File was smaller than 1MB:
|
||||
// If version is such that the file SHOULD have been 1MB, call it a checksum error (otherwise say it's OK).
|
||||
checksum = (require_checksum) ? FLASH_CHECKSUM_ERROR : FLASH_EXPECTED_CHECKSUM;
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we just read the firmware info area?
|
||||
if (offset == FIRMWARE_INFO_AREA_OFFSET) {
|
||||
// If there's no info area (missing HACKRFFW signature), it could be an ancient FW version (so skipping check)
|
||||
if (memcmp(read_buffer, hackrf_magic, 8) == 0) {
|
||||
char* version_string = (char*)&read_buffer[4];
|
||||
|
||||
// Require a 1MB firmware image with a valid checksum if release version >=v2.x or nightly >n_240125
|
||||
if (((version_string[0] == 'v') && (std::atoi(&version_string[1]) >= 2)) ||
|
||||
((version_string[0] == 'n') && (version_string[1] == '_') && (std::atoi(&version_string[2]) >= FIRST_CHECKSUM_NIGHTLY)))
|
||||
require_checksum = true;
|
||||
}
|
||||
}
|
||||
|
||||
checksum += simple_checksum((uint32_t)read_buffer, sizeof(read_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#define FLASH_ROM_SIZE 1048576
|
||||
#define FLASH_STARTING_ADDRESS 0x00000000
|
||||
#define FLASH_EXPECTED_CHECKSUM 0x00000000
|
||||
#define FLASH_CHECKSUM_ERROR 0xFFFFFFFF
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace fs = std::filesystem;
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "config_mode.hpp"
|
||||
|
||||
@@ -612,6 +613,8 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
[this](bool choice) {
|
||||
if (choice) {
|
||||
pmem::cache::defaults();
|
||||
// Refresh status bar
|
||||
send_system_refresh();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -755,7 +758,7 @@ void SetConfigModeView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* FakeBrightnessView ************************************/
|
||||
/* SetFakeBrightnessView ************************************/
|
||||
|
||||
SetFakeBrightnessView::SetFakeBrightnessView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
@@ -783,6 +786,53 @@ void SetFakeBrightnessView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* SetMenuColorView ************************************/
|
||||
|
||||
void SetMenuColorView::paint_sample() {
|
||||
Color c = Color(field_red_level.value(), field_green_level.value(), field_blue_level.value());
|
||||
button_sample.set_bg_color(c);
|
||||
}
|
||||
|
||||
SetMenuColorView::SetMenuColorView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
&button_sample,
|
||||
&field_red_level,
|
||||
&field_green_level,
|
||||
&field_blue_level,
|
||||
&button_save,
|
||||
&button_cancel});
|
||||
|
||||
button_sample.set_focusable(false);
|
||||
|
||||
Color c = pmem::menu_color();
|
||||
field_red_level.set_value(c.r());
|
||||
field_green_level.set_value(c.g());
|
||||
field_blue_level.set_value(c.b());
|
||||
paint_sample();
|
||||
|
||||
const auto color_changed_fn = [this](int32_t) {
|
||||
paint_sample();
|
||||
};
|
||||
field_red_level.on_change = color_changed_fn;
|
||||
field_green_level.on_change = color_changed_fn;
|
||||
field_blue_level.on_change = color_changed_fn;
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
Color c = Color(field_red_level.value(), field_green_level.value(), field_blue_level.value());
|
||||
pmem::set_menu_color(c);
|
||||
send_system_refresh();
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void SetMenuColorView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* SettingsMenuView **************************************/
|
||||
|
||||
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
@@ -804,6 +854,7 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push<SetUIView>(); }},
|
||||
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push<SetQRCodeView>(); }},
|
||||
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetFakeBrightnessView>(); }},
|
||||
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetMenuColorView>(); }},
|
||||
});
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -588,7 +588,7 @@ class SetEncoderDialView : public View {
|
||||
NumberField field_encoder_rate_multiplier{
|
||||
{20 * 8, 12 * 16},
|
||||
2,
|
||||
{1, 10},
|
||||
{1, 15},
|
||||
1,
|
||||
' '};
|
||||
|
||||
@@ -731,6 +731,64 @@ class SetFakeBrightnessView : public View {
|
||||
};
|
||||
};
|
||||
|
||||
class SetMenuColorView : public View {
|
||||
public:
|
||||
SetMenuColorView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Menu Color"; };
|
||||
|
||||
private:
|
||||
void paint_sample();
|
||||
|
||||
Labels labels{
|
||||
{{3 * 8, 1 * 16}, "Menu Button Color Scheme", Color::light_grey()},
|
||||
{{2 * 8, 8 * 16}, "Red Level:", Color::light_grey()},
|
||||
{{2 * 8, 9 * 16}, "Green Level:", Color::light_grey()},
|
||||
{{2 * 8, 10 * 16}, "Blue Level:", Color::light_grey()},
|
||||
};
|
||||
|
||||
NewButton button_sample{
|
||||
{8 * 8, 4 * 16, 14 * 8, 3 * 16},
|
||||
"New Color",
|
||||
&bitmap_icon_brightness,
|
||||
};
|
||||
|
||||
NumberField field_red_level{
|
||||
{15 * 8, 8 * 16},
|
||||
3,
|
||||
{8, 248},
|
||||
8,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_green_level{
|
||||
{15 * 8, 9 * 16},
|
||||
3,
|
||||
{8, 248},
|
||||
8,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_blue_level{
|
||||
{15 * 8, 10 * 16},
|
||||
3,
|
||||
{8, 248},
|
||||
8,
|
||||
' ',
|
||||
};
|
||||
|
||||
Button button_save{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Save"};
|
||||
|
||||
Button button_cancel{
|
||||
{16 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Cancel",
|
||||
};
|
||||
};
|
||||
|
||||
class SettingsMenuView : public BtnGridView {
|
||||
public:
|
||||
SettingsMenuView(NavigationView& nav);
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "log_file.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -427,6 +425,9 @@ TextEditorView::TextEditorView(NavigationView& nav)
|
||||
&text_size,
|
||||
});
|
||||
|
||||
text_position.set_style(&Styles::bg_dark_blue);
|
||||
text_size.set_style(&Styles::bg_dark_blue);
|
||||
|
||||
viewer.set_font_zoom(enable_zoom);
|
||||
|
||||
viewer.on_select = [this]() {
|
||||
@@ -553,8 +554,6 @@ void TextEditorView::open_file(const fs::path& path) {
|
||||
viewer.set_file(*file_);
|
||||
}
|
||||
|
||||
portapack::persistent_memory::set_apply_fake_brightness(false); // work around to resolve the display issue in notepad app. not elegant i know, so TODO.
|
||||
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,8 @@ using namespace lpc43xx;
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
static int delayed_error = 0;
|
||||
|
||||
extern "C" {
|
||||
|
||||
CH_IRQ_HANDLER(M4Core_IRQHandler) {
|
||||
@@ -161,6 +163,10 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
}
|
||||
|
||||
if (events & EVT_MASK_RTC_TICK) {
|
||||
// delay error message by 2 seconds to wait for LCD being ready
|
||||
if (portapack::init_error != nullptr && ++delayed_error > 1)
|
||||
draw_guru_meditation(CORTEX_M4, portapack::init_error);
|
||||
|
||||
handle_rtc_tick();
|
||||
}
|
||||
|
||||
|
||||
12
firmware/application/external/external.cmake
vendored
12
firmware/application/external/external.cmake
vendored
@@ -1,12 +1,12 @@
|
||||
set(EXTCPPSRC
|
||||
|
||||
#pacman
|
||||
# external/pacman/main.cpp
|
||||
# external/pacman/ui_pacman.cpp
|
||||
external/pacman/main.cpp
|
||||
external/pacman/ui_pacman.cpp
|
||||
|
||||
#tetris
|
||||
# external/tetris/main.cpp
|
||||
# external/tetris/ui_tetris.cpp
|
||||
external/tetris/main.cpp
|
||||
external/tetris/ui_tetris.cpp
|
||||
|
||||
#afsk_rx
|
||||
external/afsk_rx/main.cpp
|
||||
@@ -65,7 +65,7 @@ set(EXTCPPSRC
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
# pacman
|
||||
pacman
|
||||
afsk_rx
|
||||
calculator
|
||||
font_viewer
|
||||
@@ -79,5 +79,5 @@ set(EXTAPPLIST
|
||||
gpssim
|
||||
spainter
|
||||
keyfob
|
||||
# tetris
|
||||
tetris
|
||||
)
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace ui::external_app::pacman {
|
||||
#include "playfield.hpp"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
Playfield _game;
|
||||
|
||||
PacmanView::PacmanView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
add_children({&dummy});
|
||||
@@ -26,19 +24,36 @@ void PacmanView::focus() {
|
||||
|
||||
void PacmanView::paint(Painter& painter) {
|
||||
(void)painter;
|
||||
static Playfield _game;
|
||||
static bool wait_for_button_release{false};
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
_game.Init();
|
||||
}
|
||||
|
||||
auto switches_raw = swizzled_switches() & ((1 << (int)Switch::Right) | (1 << (int)Switch::Left) | (1 << (int)Switch::Down) | (1 << (int)Switch::Up) | (1 << (int)Switch::Sel) | (1 << (int)Switch::Dfu));
|
||||
auto switches_debounced = get_switches_state().to_ulong();
|
||||
|
||||
but_RIGHT = (switches_debounced & 0x01) == 0x01;
|
||||
but_LEFT = (switches_debounced & 0x02) == 0x02;
|
||||
but_DOWN = (switches_debounced & 0x04) == 0x04;
|
||||
but_UP = (switches_debounced & 0x08) == 0x08;
|
||||
but_A = (switches_debounced & 0x10) == 0x10;
|
||||
// For the Select (Start/Pause) button, wait for release to avoid repeat
|
||||
uint8_t buttons_to_wait_for = (1 << (int)Switch::Sel);
|
||||
if (wait_for_button_release) {
|
||||
if ((switches_debounced & buttons_to_wait_for) == 0)
|
||||
wait_for_button_release = false;
|
||||
switches_debounced &= ~buttons_to_wait_for;
|
||||
} else {
|
||||
if (switches_debounced & buttons_to_wait_for)
|
||||
wait_for_button_release = true;
|
||||
}
|
||||
|
||||
// For the directional buttons, use the raw inputs for fastest response time
|
||||
but_RIGHT = (switches_raw & (1 << (int)Switch::Right)) != 0;
|
||||
but_LEFT = (switches_raw & (1 << (int)Switch::Left)) != 0;
|
||||
but_DOWN = (switches_raw & (1 << (int)Switch::Down)) != 0;
|
||||
but_UP = (switches_raw & (1 << (int)Switch::Up)) != 0;
|
||||
|
||||
// For the pause button, use the debounced input to avoid glitches, and OR in the value to make sure that we don't clear it before it's seen
|
||||
but_A |= (switches_debounced & (1 << (int)Switch::Sel)) != 0;
|
||||
|
||||
_game.Step();
|
||||
}
|
||||
|
||||
@@ -56,35 +56,43 @@ static const Color pp_colors[] = {
|
||||
Color::black(),
|
||||
};
|
||||
|
||||
class SPI_TFT_ILI9341 {
|
||||
public:
|
||||
SPI_TFT_ILI9341(int, int, int, int, int, int, std::string) { (void)0; };
|
||||
// NB: ELIMINATED SPI_TFT_ILI9341 DISPLAY CLASS DUE TO GLOBAL OBJECT INITIALIZATION ISSUE WITH EXTERNAL APPS
|
||||
|
||||
void claim(__FILE* x) { (void)x; };
|
||||
static void claim(__FILE* x) {
|
||||
(void)x;
|
||||
};
|
||||
|
||||
void cls() {
|
||||
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
|
||||
};
|
||||
static void cls() {
|
||||
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
|
||||
};
|
||||
|
||||
void background(int color) { bg_color = color; };
|
||||
void foreground(int color) { fg_color = color; };
|
||||
static void background(int color) {
|
||||
bg_color = color;
|
||||
};
|
||||
|
||||
void locate(int x, int y) {
|
||||
x_pos = x;
|
||||
y_pos = y;
|
||||
};
|
||||
void set_orientation(int x) { (void)x; };
|
||||
void set_font(unsigned char* x) { (void)x; };
|
||||
static void foreground(int color) {
|
||||
fg_color = color;
|
||||
};
|
||||
|
||||
void fillrect(int x1, int y1, int x2, int y2, int color) {
|
||||
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||
};
|
||||
static void locate(int x, int y) {
|
||||
x_pos = x;
|
||||
y_pos = y;
|
||||
};
|
||||
|
||||
void rect(int x1, int y1, int x2, int y2, int color) {
|
||||
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||
};
|
||||
static void set_orientation(int x) {
|
||||
(void)x;
|
||||
};
|
||||
|
||||
private:
|
||||
static void set_font(unsigned char* x) {
|
||||
(void)x;
|
||||
};
|
||||
|
||||
static void fillrect(int x1, int y1, int x2, int y2, int color) {
|
||||
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||
};
|
||||
|
||||
static void rect(int x1, int y1, int x2, int y2, int color) {
|
||||
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
|
||||
};
|
||||
|
||||
static void printf(std::string str) {
|
||||
|
||||
2
firmware/application/external/tetris/mbed.h
vendored
2
firmware/application/external/tetris/mbed.h
vendored
@@ -67,7 +67,7 @@ static bool but_DOWN;
|
||||
static bool but_SELECT;
|
||||
|
||||
//
|
||||
// AnalogIn Class -- DID NOT WORK BECAUSE INITIALIZER CODE WON'T EXECUTE -- hacked original code module instead
|
||||
// AnalogIn Class -- DID NOT WORK DUE TO GLOBAL OBJECT INITIALIZER ISSUE WITH EXTERNAL APPS -- hacked original code module instead
|
||||
//
|
||||
// dp9 = joystick rotate button --> select button
|
||||
// dp10 = joystick y --> up & down buttons
|
||||
|
||||
188
firmware/application/external/tetris/tetris.cpp
vendored
188
firmware/application/external/tetris/tetris.cpp
vendored
@@ -4,9 +4,15 @@
|
||||
|
||||
// clang-format off
|
||||
|
||||
//////// PORTAPACK CHANGES HIGHLIGHTED
|
||||
//////// HACKED FOR PORTAPACK -- CHANGES HIGHLIGHTED
|
||||
int main();
|
||||
void pause_game();
|
||||
void Initialize(unsigned char c);
|
||||
void DeleteFigure();
|
||||
void DrawFigure();
|
||||
bool InCollisionDown(char delta);
|
||||
bool InCollisionLeft();
|
||||
bool InCollisionRight();
|
||||
//////// PORTAPACK
|
||||
|
||||
#include "mbed.h"
|
||||
@@ -15,32 +21,29 @@ void pause_game();
|
||||
|
||||
#define dp23 P0_0
|
||||
|
||||
//////// PORTAPACK - DISABLED MOST CLASSES DUE TO GLOBAL OBJECT INITIALIZER ISSUE WITH EXTERNAL APPS:
|
||||
//deklaracija display-a
|
||||
SPI_TFT_ILI9341 display(dp2, dp1, dp6, dp24, dp23, dp25, "TFT");
|
||||
|
||||
//////// PORTAPACK - DISABLED ANALOGIN CLASS DUE TO OBJECT INITIALIZER CODE NOT RUNNING:
|
||||
//SPI_TFT_ILI9341 display(dp2, dp1, dp6, dp24, dp23, dp25, "TFT");
|
||||
//
|
||||
//analogni ulazi za joystick
|
||||
// AnalogIn VRx(dp11);
|
||||
// AnalogIn VRy(dp10);
|
||||
//
|
||||
// AnalogIn random(dp13); //analogni ulaz za generisanje random vrijednosti
|
||||
//////// PORTAPACK
|
||||
|
||||
//taster na joysticku za rotaciju
|
||||
InterruptIn taster(dp9);
|
||||
|
||||
//////// PORTAPACK - DISABLED ANALOGIN CLASS DUE TO OBJECT INITIALIZER CODE NOT RUNNING:
|
||||
// AnalogIn random(dp13); //analogni ulaz za generisanje random vrijednosti
|
||||
//////// PORTAPACK
|
||||
|
||||
//ticker za spustanje figure
|
||||
//timer za debouncing tastera na joysticku
|
||||
Ticker game, joystick;
|
||||
Timer debounceTaster;
|
||||
|
||||
|
||||
unsigned char level = 0; //mora biti tipa usigned char jer inače se može desiti da level bude manji od 0, a i da ne trošimo memoriju
|
||||
const float delays[4] = {1.2, 0.7, 0.4, 0.25}; //svakih koliko se spusti jedan red, ovo provjeriti da li je presporo ili prebrzo, ovisi o levelu
|
||||
|
||||
//////// PORTAPACK - UNNEEDED JOYSTICK HYSTERESIS VARIABLES
|
||||
//////// PORTAPACK - DELETED UNNEEDED JOYSTICK HYSTERESIS VARIABLES
|
||||
//char leftBoundary = 1, rightBoundary = 5, downBoundary = 1, upBoundary = 5;// sada je ovo tipa char
|
||||
//////// PORTAPACK
|
||||
|
||||
@@ -49,7 +52,6 @@ bool firstTime = true; //ako je prvi put, figura se crta u Tickeru
|
||||
bool gameStarted = false;
|
||||
unsigned char nextFigure = 1; //ovo je sad globalna varijabla, da bi mogli unaprijed generisati sljedeću figuru radi prikaza
|
||||
|
||||
|
||||
//white - no figure
|
||||
//I - BLUE
|
||||
//O - YELLOW
|
||||
@@ -69,81 +71,70 @@ short figuresY[7][4] = {{0,1,2,3}, {1,0,0,1}, {1,1,2,0}, {0,1,1,2}, {0,1,1,2}, {
|
||||
unsigned int GenerateRandomSeed() {
|
||||
//////// PORTAPACK - USE RTC FOR SEED
|
||||
return LPC_RTC->CTIME0;
|
||||
// unsigned int randomNumber = 0;
|
||||
// for(int i = 0; i <= 32; i += 2) {
|
||||
// randomNumber += ((random.read_u16() % 3) << i);
|
||||
// wait_us(10);
|
||||
// }
|
||||
// return randomNumber;
|
||||
//////// PORTAPACK
|
||||
}
|
||||
|
||||
void Init() {
|
||||
//ovo su zajedničke osobine za sve prikaze na display-u
|
||||
//nikad se ne mijenjaju i pozvat ćemo je jednom prije petlje
|
||||
display.claim(stdout);
|
||||
display.set_orientation(2); // 2 ili 0, zavisi kako okrenemo display, provjerit ćemo na labu kako nam je najlakše povezat
|
||||
display.set_font((unsigned char*) Arial12x12);
|
||||
claim(stdout);
|
||||
set_orientation(2); // 2 ili 0, zavisi kako okrenemo display, provjerit ćemo na labu kako nam je najlakše povezat
|
||||
set_font((unsigned char*) Arial12x12);
|
||||
}
|
||||
|
||||
|
||||
void ShowScore() {
|
||||
//pomocna funkcija za prikazivanje score-a
|
||||
display.fillrect(165, 20, 235, 50, White); //popunimo pravugaonik da obrišemo stari score
|
||||
display.locate(200, 35); //valjda je na sredini pravougaonika
|
||||
fillrect(165, 20, 235, 50, White); //popunimo pravugaonik da obrišemo stari score
|
||||
locate(200, 35); //valjda je na sredini pravougaonika
|
||||
printf("%d", score);
|
||||
}
|
||||
|
||||
void ShowNextFigure() {
|
||||
//prikaz sljedeće figure koristeći pomoćnu varijablu nextFigure
|
||||
display.fillrect(165, 70, 235, 120, White);
|
||||
fillrect(165, 70, 235, 120, White);
|
||||
int upperLeftX = 176, upperLeftY = 83;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
int x = upperLeftX + DIMENSION_NEXT * figuresY[nextFigure - 1][i], y = upperLeftY + DIMENSION_NEXT * figuresX[nextFigure - 1][i];
|
||||
display.fillrect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, colors[nextFigure]);
|
||||
display.rect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, Black);
|
||||
fillrect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, colors[nextFigure]);
|
||||
rect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, Black);
|
||||
}
|
||||
}
|
||||
|
||||
//funkcija za crtanje cursora za odabir levela
|
||||
void DrawCursor(int color, unsigned char lev) {
|
||||
display.fillrect(60, lev * 70 + 50, 72, lev * 70 + 50 + 12, color);
|
||||
fillrect(60, lev * 70 + 50, 72, lev * 70 + 50 + 12, color);
|
||||
}
|
||||
|
||||
// PORTAPACK - ADDED EXTRA LEVEL:
|
||||
void ShowLevelMenu() {
|
||||
//ovdje inicijalizujemo display
|
||||
display.cls(); // brišemo prethodno
|
||||
display.background(Black);
|
||||
display.foreground(White);
|
||||
display.locate(80, 50);
|
||||
cls(); // brišemo prethodno
|
||||
background(Black);
|
||||
foreground(White);
|
||||
locate(80, 50);
|
||||
printf("LEVEL 1");
|
||||
display.locate(80, 120);
|
||||
locate(80, 120);
|
||||
printf("LEVEL 2");
|
||||
display.locate(80, 190);
|
||||
locate(80, 190);
|
||||
printf("LEVEL 3");
|
||||
display.locate(80, 260);
|
||||
locate(80, 260);
|
||||
printf("LEVEL 4");
|
||||
DrawCursor(White, level);
|
||||
}
|
||||
//////// PORTAPACK
|
||||
|
||||
//////// PORTAPACK - KLUDGED FOR BUTTONS VS JOYSTICK:
|
||||
//////// PORTAPACK - USE BUTTONS VS JOYSTICK:
|
||||
void ReadJoystickForLevel(){
|
||||
unsigned char old = level;
|
||||
if(but_UP){
|
||||
// upBoundary = 4;
|
||||
(level == 0) ? level = 3 : level--;
|
||||
}
|
||||
else if(but_DOWN){
|
||||
//ne radi ona prethodna varijanta jer % vraća i negastivni rezultat
|
||||
//to što ne koristimo unsigned tip ne pomaže jer će doći do overflow-a
|
||||
// downBoundary = 2;
|
||||
level = (level + 1) % 4;
|
||||
}
|
||||
else {
|
||||
// downBoundary = 1;
|
||||
// upBoundary = 5;
|
||||
}
|
||||
DrawCursor(Black, old); //na prethodni level popunimo bojom pozadine
|
||||
DrawCursor(White, level); //na novi level popunimo bijelom bojom - pozadina je crna
|
||||
//koristio sam fillrect, jer njega svakako moramo koristiti, jer možda budemo morali da brišemo fillcircle iz biblioteke
|
||||
@@ -169,11 +160,11 @@ main();
|
||||
|
||||
void StartGame()
|
||||
{
|
||||
display.cls(); // brišemo ShowLevelMenu
|
||||
display.background(White);
|
||||
display.foreground(Black);
|
||||
display.fillrect(0, 0, 160, 320, White);
|
||||
display.fillrect(160, 0, 240, 320, Black); //dio za prikazivanje rezultata će biti crni pravougaonik, a tabla je bijeli
|
||||
cls(); // brišemo ShowLevelMenu
|
||||
background(White);
|
||||
foreground(Black);
|
||||
fillrect(0, 0, 160, 320, White);
|
||||
fillrect(160, 0, 240, 320, Black); //dio za prikazivanje rezultata će biti crni pravougaonik, a tabla je bijeli
|
||||
ShowScore();
|
||||
}
|
||||
|
||||
@@ -206,34 +197,34 @@ void PutBorders(short x, short y) {
|
||||
for(int i = x - 1; i <= x + 1; i++) {
|
||||
for(int j = y - 1; j <= y + 1; j++) {
|
||||
if(i < 0 || i > 9 || j < 0 || j > 19 || board[j][i] == 0) continue;
|
||||
display.rect(i * DIMENSION, j * DIMENSION, (i + 1) * DIMENSION, (j + 1) * DIMENSION, Black);
|
||||
rect(i * DIMENSION, j * DIMENSION, (i + 1) * DIMENSION, (j + 1) * DIMENSION, Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Tetromino{
|
||||
private:
|
||||
//////// PORTAPACK - ELIMINATED CLASSES DUE TO GLOBAL OBJECT INITIALIZATION ISSUE WITH EXTERNAL APPS:
|
||||
//class Tetromino{
|
||||
//private:
|
||||
short X[4];
|
||||
short Y[4];
|
||||
short boardX, boardY;
|
||||
unsigned char colorIndex;//dodao sam colorIndex zasad, jer nema drugog načina da popunimo matricu sa indeksima boja
|
||||
//ovo je najbezbolnija varijanta što se memorije tiče
|
||||
public:
|
||||
Tetromino(){
|
||||
//////// PORTAPACK - NOTE - DEFAULT INITIALIZER CODE DOESN'T GET EXECUTED FOR SOME REASON:
|
||||
unsigned char r = rand() % 7 + 1;
|
||||
Initialize(r);
|
||||
//public:
|
||||
// Tetromino(){
|
||||
// unsigned char r = rand() % 7 + 1;
|
||||
// Initialize(r);
|
||||
// }
|
||||
|
||||
void Tetromino(unsigned char c) {
|
||||
Initialize(c);
|
||||
}
|
||||
|
||||
Tetromino(unsigned char colorIndex) {
|
||||
Initialize(colorIndex);
|
||||
}
|
||||
|
||||
void Initialize(unsigned char colorIndex) {
|
||||
Tetromino::colorIndex = colorIndex;
|
||||
void Initialize(unsigned char c) {
|
||||
colorIndex = c;
|
||||
boardX = 0;
|
||||
boardY = 4; //3,4 ili 5 najbolje da vidimo kad imamo display
|
||||
copyCoordinates(X, Y, colorIndex - 1);
|
||||
copyCoordinates(X, Y, c - 1);
|
||||
}
|
||||
|
||||
void Rotate(){
|
||||
@@ -266,13 +257,9 @@ public:
|
||||
//stavio sam 16 za početak, možemo se opet skontati na labu
|
||||
//ovo pretpostavlja da nema margina, mogu se lagano dodati uz neku konstantu kao offset
|
||||
int upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
|
||||
display.fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, colors[colorIndex]);
|
||||
fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, colors[colorIndex]);
|
||||
//ovo boji granice blokova u crno, možemo skloniti ako ti se ne sviđa
|
||||
|
||||
//////// PORTAPACK - HIDE DEFAULT WHITE BLOCK (ALTERNATE KLUDGE FOR TETRONIMO INITIALIZATION CODE NOT RUNNING AT CONSTRUCTION)
|
||||
if (colorIndex != White)
|
||||
//////// PORTAPACK
|
||||
display.rect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, Black);
|
||||
rect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, Black);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +267,7 @@ if (colorIndex != White)
|
||||
for(int i = 0; i < 4; i++) {
|
||||
//ista logika kao u DrawFigure, samo popunjavamo sve blokove sa bijelim pravougaonicima
|
||||
short upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
|
||||
display.fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, White);
|
||||
fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, White);
|
||||
PutBorders(upperLeftY, upperLeftX);
|
||||
}
|
||||
}
|
||||
@@ -295,9 +282,6 @@ if (colorIndex != White)
|
||||
}
|
||||
|
||||
bool MoveDown(char delta = 1){
|
||||
//////// PORTAPACK - MOVE DEFAULT WHITE BLOCK TO BOTTOM IMMEDIATELY (ALTERNATE KLUDGE FOR TETRONIMO INITIALIZATION CODE NOT RUNNING AT CONSTRUCTION)
|
||||
if (colorIndex == White) delta = 19;
|
||||
//////// PORTAPACK
|
||||
if(!InCollisionDown(delta)){
|
||||
DeleteFigure();
|
||||
boardX+=delta;
|
||||
@@ -384,34 +368,23 @@ if (colorIndex == White) delta = 19;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
// };
|
||||
|
||||
// Tetromino currentTetromino;
|
||||
|
||||
Tetromino currentTetromino;
|
||||
|
||||
//////// PORTAPACK - KLUDGED FOR BUTTONS VS JOYSTICK:
|
||||
//////// PORTAPACK - USE BUTTONS VS JOYSTICK, AND ADDED PAUSE FEATURE:
|
||||
void ReadJoystickForFigure() {
|
||||
if(but_LEFT) {
|
||||
// leftBoundary = 2;
|
||||
currentTetromino.MoveLeft();
|
||||
MoveLeft();
|
||||
}
|
||||
else if(but_RIGHT) {
|
||||
// rightBoundary = 4;
|
||||
currentTetromino.MoveRight();
|
||||
MoveRight();
|
||||
}
|
||||
else if(but_UP) {
|
||||
// downBoundary = 2;
|
||||
pause_game();
|
||||
pause_game();
|
||||
}
|
||||
else if(but_DOWN) {
|
||||
// upBoundary = 4;
|
||||
currentTetromino.SoftDrop();
|
||||
}
|
||||
else {
|
||||
// leftBoundary = 1;
|
||||
// rightBoundary = 5;
|
||||
// downBoundary = 1;
|
||||
// upBoundary = 5;
|
||||
SoftDrop();
|
||||
}
|
||||
}
|
||||
//////// PORTAPACK
|
||||
@@ -460,10 +433,10 @@ void UpdateBoard()
|
||||
board[i][j] = board[i - numberOfLines][j];
|
||||
board[i - numberOfLines][j] = 0;
|
||||
short tmp = i - numberOfLines;
|
||||
display.fillrect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , colors[board[i][j]]); // bojimo novi blok
|
||||
display.fillrect( j * DIMENSION, tmp * DIMENSION,(j + 1) * DIMENSION, (tmp + 1) * DIMENSION , White);
|
||||
fillrect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , colors[board[i][j]]); // bojimo novi blok
|
||||
fillrect( j * DIMENSION, tmp * DIMENSION,(j + 1) * DIMENSION, (tmp + 1) * DIMENSION , White);
|
||||
if(board[i][j] != 0)
|
||||
display.rect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , Black);
|
||||
rect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , Black);
|
||||
}
|
||||
}
|
||||
score += UpdateScore(numberOfLines);
|
||||
@@ -480,26 +453,23 @@ bool IsOver() {
|
||||
|
||||
void ShowGameOverScreen() {
|
||||
//////// PORTAPACK - SKIP CLS
|
||||
// display.cls();
|
||||
// display.background(Black);
|
||||
// display.foreground(White);
|
||||
display.background(White);
|
||||
display.foreground(Black);
|
||||
// cls();
|
||||
// background(Black);
|
||||
// foreground(White);
|
||||
background(White);
|
||||
foreground(Black);
|
||||
//////// PORTAPACK
|
||||
display.locate(60, 120);
|
||||
locate(60, 120);
|
||||
printf("GAME OVER");
|
||||
display.locate(40, 150);
|
||||
locate(40, 150);
|
||||
printf("YOUR SCORE IS %d", score);
|
||||
wait(5); //ovaj prikaz traje 3s (možemo mijenjati) a nakon toga se ponovo prikazuje meni sa levelima
|
||||
}
|
||||
|
||||
void InitGame() {
|
||||
if(firstTime) {
|
||||
//////// PORTAPACK - NOTE - ATTEMPTED WORKAROUND FOR SKIPPED INITIALIZER CODE - BUT ANY OF THESE CRASHES FIRMWARE AT POWER-UP EVEN IF THERE'S NO TETRIS APP INSTALLED:
|
||||
// currentTetromino = Tetromino(rand() % 7 + 1); // TEST #1
|
||||
// currentTetromino.Initialize(rand() % 7 + 1); // TEST #2
|
||||
//////// PORTAPACK
|
||||
currentTetromino.DrawFigure();
|
||||
Tetromino(rand() % 7 + 1);
|
||||
DrawFigure();
|
||||
nextFigure = rand() % 7 + 1;
|
||||
ShowNextFigure();
|
||||
firstTime = false;
|
||||
@@ -508,13 +478,13 @@ void InitGame() {
|
||||
|
||||
void PlayGame(){
|
||||
InitGame();
|
||||
if(!currentTetromino.MoveDown()){
|
||||
currentTetromino.OnAttached();
|
||||
if(!MoveDown()){
|
||||
OnAttached();
|
||||
UpdateBoard();
|
||||
ShowScore();
|
||||
|
||||
currentTetromino = Tetromino(nextFigure);
|
||||
currentTetromino.DrawFigure();
|
||||
Tetromino(nextFigure);
|
||||
DrawFigure();
|
||||
nextFigure = rand() % 7 + 1;
|
||||
ShowNextFigure();
|
||||
if(IsOver()) {
|
||||
@@ -531,7 +501,7 @@ void PlayGame(){
|
||||
void OnTasterPressed(){
|
||||
if(debounceTaster.read_ms() > 200) {
|
||||
if(gameStarted){
|
||||
currentTetromino.Rotate();
|
||||
Rotate();
|
||||
}
|
||||
else{
|
||||
joystick.detach();
|
||||
@@ -568,7 +538,7 @@ return 0;
|
||||
void pause_game() {
|
||||
game.detach();
|
||||
joystick.detach();
|
||||
display.locate(180, 200);
|
||||
locate(180, 200);
|
||||
printf("PAUSED");
|
||||
while ((get_switches_state().to_ulong() & 0x10) == 0); // wait for SELECT button to resume
|
||||
printf(" ");
|
||||
|
||||
@@ -162,6 +162,8 @@ static void event_loop() {
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */
|
||||
|
||||
if (config_mode_should_enter()) {
|
||||
config_mode_clear();
|
||||
config_mode_run();
|
||||
@@ -169,25 +171,30 @@ int main(void) {
|
||||
|
||||
config_mode_set();
|
||||
|
||||
first_if.init(); /* To avoid initial short Ant_DC_Bias pulse ,we need quick set up GP01_RFF507X =1 */
|
||||
if (portapack::init()) {
|
||||
portapack::display.init();
|
||||
config_mode_clear();
|
||||
switch (portapack::init()) {
|
||||
case portapack::init_status_t::INIT_HACKRF_CPLD_FAILED:
|
||||
portapack::init_error = "HACKRF CPLD FAILED";
|
||||
[[fallthrough]];
|
||||
|
||||
// sdcStart(&SDCD1, nullptr); // Commented out as now happens in portapack.cpp
|
||||
case portapack::init_status_t::INIT_SUCCESS:
|
||||
|
||||
// controls_init(); // Commented out as now happens in portapack.cpp
|
||||
lcd_frame_sync_configure();
|
||||
rtc_interrupt_enable();
|
||||
config_mode_clear();
|
||||
|
||||
event_loop();
|
||||
lcd_frame_sync_configure();
|
||||
rtc_interrupt_enable();
|
||||
|
||||
sdcDisconnect(&SDCD1);
|
||||
sdcStop(&SDCD1);
|
||||
event_loop();
|
||||
|
||||
portapack::shutdown();
|
||||
} else {
|
||||
config_mode_clear();
|
||||
sdcDisconnect(&SDCD1);
|
||||
sdcStop(&SDCD1);
|
||||
|
||||
portapack::shutdown();
|
||||
break;
|
||||
|
||||
case portapack::init_status_t::INIT_NO_PORTAPACK:
|
||||
case portapack::init_status_t::INIT_PORTAPACK_CPLD_FAILED:
|
||||
config_mode_clear();
|
||||
break;
|
||||
}
|
||||
|
||||
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
#include "jtag_target_gpio.hpp"
|
||||
|
||||
#include "clock_manager.hpp"
|
||||
#include "event_m0.hpp"
|
||||
@@ -50,9 +51,12 @@ using asahi_kasei::ak4951::AK4951;
|
||||
#include "file.hpp"
|
||||
#include "sd_card.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "bitmap.hpp"
|
||||
|
||||
namespace portapack {
|
||||
|
||||
const char* init_error = nullptr;
|
||||
|
||||
portapack::IO io{
|
||||
portapack::gpio_dir,
|
||||
portapack::gpio_lcd_rdx,
|
||||
@@ -172,50 +176,21 @@ constexpr I2CConfig i2c_config_fast_clock{
|
||||
enum class PortaPackModel {
|
||||
R1_20150901,
|
||||
R2_20170522,
|
||||
AGM,
|
||||
AUTODETECT,
|
||||
};
|
||||
|
||||
static bool save_config(int8_t value) {
|
||||
persistent_memory::set_config_cpld(value);
|
||||
if (sd_card::status() == sd_card::Status::Mounted) {
|
||||
ensure_directory("/hardware");
|
||||
File file;
|
||||
auto sucess = file.create("/hardware/settings.txt");
|
||||
if (!sucess.is_valid()) {
|
||||
file.write_line(to_string_dec_uint(value));
|
||||
}
|
||||
}
|
||||
portapack::persistent_memory::cache::persist();
|
||||
return true;
|
||||
}
|
||||
|
||||
int read_file(std::string name) {
|
||||
std::string return_string = "";
|
||||
File file;
|
||||
auto success = file.open(name);
|
||||
|
||||
if (!success.is_valid()) {
|
||||
char one_char[1];
|
||||
for (size_t pointer = 0; pointer < file.size(); pointer++) {
|
||||
file.seek(pointer);
|
||||
file.read(one_char, 1);
|
||||
return_string += one_char[0];
|
||||
}
|
||||
return std::stoi(return_string);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int load_config() {
|
||||
static Optional<int> config_value;
|
||||
if (!config_value.is_valid()) {
|
||||
int8_t value = portapack::persistent_memory::config_cpld();
|
||||
if ((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted) {
|
||||
int data = read_file("/hardware/settings.txt");
|
||||
if (data != -1) {
|
||||
config_value = data;
|
||||
}
|
||||
} else {
|
||||
config_value = value;
|
||||
}
|
||||
config_value = value;
|
||||
}
|
||||
return config_value.value();
|
||||
}
|
||||
@@ -224,39 +199,49 @@ static PortaPackModel portapack_model() {
|
||||
static Optional<PortaPackModel> model;
|
||||
|
||||
if (!model.is_valid()) {
|
||||
const auto switches_state = get_switches_state();
|
||||
// Only save config if no other multi key boot action is triggered (like pmem reset)
|
||||
if (switches_state.count() == 1) {
|
||||
if (switches_state[(size_t)ui::KeyEvent::Up]) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
portapack::cpld::CPLD cpld{jtag};
|
||||
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
uint32_t idcode = cpld.get_idcode();
|
||||
if (idcode == 0x25610) {
|
||||
model = PortaPackModel::AGM;
|
||||
} else {
|
||||
const auto switches_state = swizzled_switches();
|
||||
// chDbgPanic(to_string_hex((uint32_t)switches_state.count(), 8).c_str());
|
||||
// Only save config if no other multi key boot action is triggered (like pmem reset)
|
||||
if (((switches_state >> (size_t)ui::KeyEvent::Up) & 1) == 1) {
|
||||
save_config(1);
|
||||
// model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway
|
||||
} else if (switches_state[(size_t)ui::KeyEvent::Down]) {
|
||||
} else if (((switches_state >> (size_t)ui::KeyEvent::Down) & 1) == 1) {
|
||||
save_config(2);
|
||||
// model = PortaPackModel::R1_20150901;
|
||||
} else if (switches_state[(size_t)ui::KeyEvent::Left]) {
|
||||
} else if (((switches_state >> (size_t)ui::KeyEvent::Left) & 1) == 1) {
|
||||
save_config(3);
|
||||
// model = PortaPackModel::R1_20150901;
|
||||
} else if (switches_state[(size_t)ui::KeyEvent::Right]) {
|
||||
} else if (((switches_state >> (size_t)ui::KeyEvent::Right) & 1) == 1) {
|
||||
save_config(4);
|
||||
// model = PortaPackModel::R2_20170522;
|
||||
} else if (switches_state[(size_t)ui::KeyEvent::Select]) {
|
||||
} else if (((switches_state >> (size_t)ui::KeyEvent::Select) & 1) == 1) {
|
||||
save_config(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (load_config() == 1) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
} else if (load_config() == 2) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 3) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 4) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
} else {
|
||||
if (audio_codec_wm8731.detected()) {
|
||||
model = PortaPackModel::R1_20150901; // H1R1
|
||||
if (load_config() == 1) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
} else if (load_config() == 2) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 3) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 4) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
} else {
|
||||
model = PortaPackModel::R2_20170522; // H1R2, H2, H2+
|
||||
model = PortaPackModel::AUTODETECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -343,6 +328,129 @@ static void shutdown_base() {
|
||||
clock_manager.shutdown();
|
||||
}
|
||||
|
||||
static void set_cpu_clock_speed() {
|
||||
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
|
||||
* core clock speed to the 110 - 204MHz range.
|
||||
*/
|
||||
|
||||
/* Step into the 90-110MHz M4 clock range */
|
||||
/* OG:
|
||||
* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
* r9:
|
||||
* Fclkin = 10M
|
||||
* /N=1 = 10M = PFDin
|
||||
* Fcco = PFDin * (M=20) = 200M
|
||||
* Fclk = Fcco / (2*(P=1)) = 100M
|
||||
*/
|
||||
cgu::pll1::ctrl({
|
||||
.pd = 1,
|
||||
.bypass = 0,
|
||||
.fbsel = 0,
|
||||
.direct = 0,
|
||||
.psel = 0,
|
||||
.autoblock = 1,
|
||||
.nsel = hackrf_r9 ? 0UL : 1UL,
|
||||
.msel = hackrf_r9 ? 19UL : 9UL,
|
||||
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
|
||||
});
|
||||
|
||||
cgu::pll1::enable();
|
||||
while (!cgu::pll1::is_locked())
|
||||
;
|
||||
|
||||
set_clock_config(clock_config_pll1_step);
|
||||
|
||||
/* Delay >50us at 90-110MHz clock speed */
|
||||
volatile uint32_t delay = 1400;
|
||||
while (delay--)
|
||||
;
|
||||
|
||||
set_clock_config(clock_config_pll1);
|
||||
|
||||
/* Remove /2P divider from PLL1 output to achieve full speed */
|
||||
cgu::pll1::direct();
|
||||
}
|
||||
|
||||
static void draw_splash_screen_icon(int16_t n, const ui::Bitmap& bitmap) {
|
||||
ui::Painter painter;
|
||||
|
||||
painter.draw_bitmap(
|
||||
{portapack::display.width() / 2 - 8 - 40 + (n * 20), portapack::display.height() / 2 - 8 + 40},
|
||||
bitmap,
|
||||
ui::Color::white(),
|
||||
ui::Color::black());
|
||||
}
|
||||
|
||||
static bool is_portapack_present() {
|
||||
systime_t timeout = 50;
|
||||
uint8_t wm8731_reset_command[] = {0x0f, 0x00};
|
||||
if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) {
|
||||
audio_codec_ak4951.reset();
|
||||
uint8_t ak4951_init_command[] = {0x00, 0x00};
|
||||
i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout);
|
||||
chThdSleepMilliseconds(10);
|
||||
if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool check_portapack_cpld() {
|
||||
switch (portapack_model()) {
|
||||
case PortaPackModel::AUTODETECT: {
|
||||
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_autodetect(
|
||||
portapack::cpld::rev_20150901::config, portapack::cpld::rev_20170522::config);
|
||||
if (result != portapack::cpld::CpldUpdateStatus::Success) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case PortaPackModel::R1_20150901:
|
||||
case PortaPackModel::R2_20170522: {
|
||||
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config());
|
||||
if (result == portapack::cpld::CpldUpdateStatus::Program_failed) {
|
||||
chThdSleepMilliseconds(10);
|
||||
// Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update
|
||||
// Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update
|
||||
if (load_config() != 3 /* left */ && load_config() != 4 /* right */) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case PortaPackModel::AGM:
|
||||
// the AGM devices are always factory flashed. so do nothing
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void initialize_boot_splash_screen() {
|
||||
ui::Painter painter;
|
||||
portapack::display.init();
|
||||
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
ui::Color::black());
|
||||
|
||||
chThdSleepMilliseconds(17);
|
||||
portapack::backlight()->on();
|
||||
|
||||
painter.draw_bitmap(
|
||||
{portapack::display.width() / 2 - 40, portapack::display.height() / 2 - 8},
|
||||
ui::bitmap_titlebar_image,
|
||||
ui::Color::white(),
|
||||
ui::Color::black());
|
||||
}
|
||||
|
||||
/* Clock scheme after exiting bootloader in SPIFI mode:
|
||||
*
|
||||
* XTAL_OSC = powered down
|
||||
@@ -389,28 +497,29 @@ static void shutdown_base() {
|
||||
* everything else = IRC
|
||||
*/
|
||||
|
||||
bool init() {
|
||||
init_status_t init() {
|
||||
set_idivc_base_clocks(cgu::CLK_SEL::IDIVC);
|
||||
|
||||
i2c0.start(i2c_config_boot_clock);
|
||||
|
||||
// Keeping this here for now incase we need to revert
|
||||
// if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
|
||||
// shutdown_base();
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// if( !hackrf::cpld::load_sram() ) {
|
||||
// chSysHalt();
|
||||
// }
|
||||
chThdSleepMilliseconds(100);
|
||||
|
||||
configure_pins_portapack();
|
||||
|
||||
portapack::io.init();
|
||||
persistent_memory::cache::init();
|
||||
|
||||
const auto switches_state = swizzled_switches() & (~(0xC0 | 0x80));
|
||||
bool lcd_fast_setup = switches_state == 0 && portapack::display.read_display_status();
|
||||
|
||||
if (lcd_fast_setup) {
|
||||
initialize_boot_splash_screen();
|
||||
} else {
|
||||
if (check_portapack_cpld() == false)
|
||||
return init_status_t::INIT_PORTAPACK_CPLD_FAILED;
|
||||
}
|
||||
|
||||
/* Cache some configuration data from persistent memory. */
|
||||
persistent_memory::cache::init();
|
||||
rtc_time::dst_init();
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
@@ -423,48 +532,10 @@ bool init() {
|
||||
set_clock_config(clock_config_irc);
|
||||
cgu::pll1::disable();
|
||||
|
||||
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
|
||||
* core clock speed to the 110 - 204MHz range.
|
||||
*/
|
||||
set_cpu_clock_speed();
|
||||
|
||||
/* Step into the 90-110MHz M4 clock range */
|
||||
/* OG:
|
||||
* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
* r9:
|
||||
* Fclkin = 10M
|
||||
* /N=1 = 10M = PFDin
|
||||
* Fcco = PFDin * (M=20) = 200M
|
||||
* Fclk = Fcco / (2*(P=1)) = 100M
|
||||
*/
|
||||
cgu::pll1::ctrl({
|
||||
.pd = 1,
|
||||
.bypass = 0,
|
||||
.fbsel = 0,
|
||||
.direct = 0,
|
||||
.psel = 0,
|
||||
.autoblock = 1,
|
||||
.nsel = hackrf_r9 ? 0UL : 1UL,
|
||||
.msel = hackrf_r9 ? 19UL : 9UL,
|
||||
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
|
||||
});
|
||||
|
||||
cgu::pll1::enable();
|
||||
while (!cgu::pll1::is_locked())
|
||||
;
|
||||
|
||||
set_clock_config(clock_config_pll1_step);
|
||||
|
||||
/* Delay >50us at 90-110MHz clock speed */
|
||||
volatile uint32_t delay = 1400;
|
||||
while (delay--)
|
||||
;
|
||||
|
||||
set_clock_config(clock_config_pll1);
|
||||
|
||||
/* Remove /2P divider from PLL1 output to achieve full speed */
|
||||
cgu::pll1::direct();
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(0, ui::bitmap_icon_memory);
|
||||
|
||||
usb_serial.initialize();
|
||||
|
||||
@@ -472,18 +543,11 @@ bool init() {
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
/* Check if portapack is attached by checking if any of the two audio chips is present. */
|
||||
systime_t timeout = 50;
|
||||
uint8_t wm8731_reset_command[] = {0x0f, 0x00};
|
||||
if (i2c0.transmit(0x1a /* wm8731 */, wm8731_reset_command, 2, timeout) == false) {
|
||||
audio_codec_ak4951.reset();
|
||||
uint8_t ak4951_init_command[] = {0x00, 0x00};
|
||||
i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout);
|
||||
chThdSleepMilliseconds(10);
|
||||
if (i2c0.transmit(0x12 /* ak4951 */, ak4951_init_command, 2, timeout) == false) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lcd_fast_setup == false && is_portapack_present() == false)
|
||||
return init_status_t::INIT_NO_PORTAPACK;
|
||||
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(1, ui::bitmap_icon_remote);
|
||||
|
||||
touch::adc::init();
|
||||
controls_init();
|
||||
@@ -499,20 +563,19 @@ bool init() {
|
||||
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
portapack::cpld::CpldUpdateStatus result = portapack::cpld::update_if_necessary(portapack_cpld_config());
|
||||
if (result == portapack::cpld::CpldUpdateStatus::Program_failed) {
|
||||
chThdSleepMilliseconds(10);
|
||||
// Mode left (R1) and right (R2,H2,H2+) bypass going into hackrf mode after failing CPLD update
|
||||
// Mode center (autodetect), up (R1) and down (R2,H2,H2+) will go into hackrf mode after failing CPLD update
|
||||
if (load_config() != 3 /* left */ && load_config() != 4 /* right */) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(2, ui::bitmap_icon_sd);
|
||||
|
||||
init_status_t return_code = init_status_t::INIT_SUCCESS;
|
||||
if (!hackrf::cpld::load_sram()) {
|
||||
if (lcd_fast_setup)
|
||||
chDbgPanic("HACKRF CPLD FAILED");
|
||||
|
||||
return_code = init_status_t::INIT_HACKRF_CPLD_FAILED;
|
||||
}
|
||||
|
||||
if (!hackrf::cpld::load_sram()) {
|
||||
chSysHalt();
|
||||
}
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(3, ui::bitmap_icon_hackrf);
|
||||
|
||||
chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues
|
||||
|
||||
@@ -523,7 +586,14 @@ bool init() {
|
||||
|
||||
audio::init(portapack_audio_codec());
|
||||
|
||||
return true;
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
|
||||
else {
|
||||
portapack::display.init();
|
||||
portapack::backlight()->on();
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
void shutdown(const bool leave_screen_on) {
|
||||
|
||||
@@ -41,6 +41,15 @@
|
||||
* guardrails on setting properties. */
|
||||
namespace portapack {
|
||||
|
||||
enum class init_status_t {
|
||||
INIT_SUCCESS,
|
||||
INIT_NO_PORTAPACK,
|
||||
INIT_PORTAPACK_CPLD_FAILED,
|
||||
INIT_HACKRF_CPLD_FAILED,
|
||||
};
|
||||
|
||||
extern const char* init_error;
|
||||
|
||||
extern portapack::IO io;
|
||||
|
||||
extern lcd::ILI9341 display;
|
||||
@@ -65,7 +74,7 @@ extern TemperatureLogger temperature_logger;
|
||||
void set_antenna_bias(const bool v);
|
||||
bool get_antenna_bias();
|
||||
|
||||
bool init();
|
||||
init_status_t init();
|
||||
void shutdown(const bool leave_screen_on = false);
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt);
|
||||
|
||||
@@ -127,6 +127,7 @@ void BtnGridView::add_item(GridItem new_item) {
|
||||
|
||||
void BtnGridView::update_items() {
|
||||
size_t i = 0;
|
||||
Color bg_color = portapack::persistent_memory::menu_color();
|
||||
|
||||
if ((menu_items.size()) > (displayed_max + offset)) {
|
||||
more = true;
|
||||
@@ -147,6 +148,7 @@ void BtnGridView::update_items() {
|
||||
item->set_text(menu_items[i + offset].text);
|
||||
item->set_bitmap(menu_items[i + offset].bitmap);
|
||||
item->set_color(menu_items[i + offset].color);
|
||||
item->set_bg_color(bg_color);
|
||||
item->on_select = menu_items[i + offset].on_select;
|
||||
item->set_dirty();
|
||||
}
|
||||
@@ -171,15 +173,19 @@ bool BtnGridView::set_highlighted(int32_t new_value) {
|
||||
if (((uint32_t)new_value > offset) && ((new_value - offset) >= displayed_max)) {
|
||||
// Shift BtnGridView up
|
||||
highlighted_item = new_value;
|
||||
offset += rows_;
|
||||
// rounding up new offset to next multiple of rows
|
||||
offset = new_value - displayed_max + rows_;
|
||||
offset -= (offset % rows_);
|
||||
update_items();
|
||||
set_dirty();
|
||||
// refresh whole screen (display flickers) only if scrolling last row up and a blank button is needed at the bottom
|
||||
if ((new_value + rows_ >= item_count) && (item_count % rows_) != 0)
|
||||
set_dirty();
|
||||
} else if ((uint32_t)new_value < offset) {
|
||||
// Shift BtnGridView down
|
||||
highlighted_item = new_value;
|
||||
offset = (new_value / rows_) * rows_;
|
||||
update_items();
|
||||
set_dirty();
|
||||
// no need to set_dirty() here since all buttons have been repainted
|
||||
} else {
|
||||
// Just update highlight
|
||||
highlighted_item = new_value;
|
||||
|
||||
@@ -86,6 +86,12 @@ const Style Styles::bg_blue{
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
const Style Styles::bg_dark_blue{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::dark_blue(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
const Style Styles::light_grey{
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
|
||||
@@ -58,6 +58,9 @@ class Styles {
|
||||
/* Blue background. */
|
||||
static const Style bg_blue;
|
||||
|
||||
/* Dark blue background. */
|
||||
static const Style bg_dark_blue;
|
||||
|
||||
/* Light grey foreground. */
|
||||
static const Style light_grey;
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "string_format.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
static File* shell_file = nullptr;
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, File::Error& error) {
|
||||
@@ -97,7 +99,7 @@ void cmd_sd_mkdir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
if (std::filesystem::is_directory(path)) {
|
||||
chprintf(chp, "directory already exists.\r\n");
|
||||
return;
|
||||
}
|
||||
@@ -371,3 +373,34 @@ void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_crc32(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: crc32 <path>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
File* crc_file = new File();
|
||||
auto error = crc_file->open(path, true, false);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
uint8_t buffer[64];
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
while (true) {
|
||||
auto bytes_read = crc_file->read(buffer, 64);
|
||||
if (report_on_error(chp, bytes_read)) return;
|
||||
|
||||
if (bytes_read.value() > 0) {
|
||||
crc.process_bytes((void*)buffer, bytes_read.value());
|
||||
}
|
||||
|
||||
if (64 != bytes_read.value()) {
|
||||
chprintf(chp, "CRC32: 0x%08X\r\n", crc.checksum());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delete crc_file;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_read_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_crc32(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
static std::filesystem::path path_from_string8(char* path) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
|
||||
@@ -62,6 +63,7 @@ static std::filesystem::path path_from_string8(char* path) {
|
||||
{"ftell", cmd_sd_tell}, \
|
||||
{"fread", cmd_sd_read}, \
|
||||
{"frb", cmd_sd_read_binary}, \
|
||||
{"fwrite", cmd_sd_write}, \
|
||||
{"fwb", cmd_sd_write_binary}
|
||||
{"fwrite", cmd_sd_write}, \
|
||||
{"fwb", cmd_sd_write_binary}, \
|
||||
{"crc32", cmd_sd_crc32}
|
||||
// clang-format on
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include "jtag_target_gpio.hpp"
|
||||
#include "cpld_max5.hpp"
|
||||
@@ -30,11 +31,15 @@
|
||||
#include "portapack_cpld_data.hpp"
|
||||
#include "hackrf_cpld_data.hpp"
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
#define REV_20150901_CHECKSUM 0xE0EF80FB
|
||||
#define REV_20170522_CHECKSUM 0xD1BEB722
|
||||
|
||||
namespace portapack {
|
||||
namespace cpld {
|
||||
|
||||
CpldUpdateStatus update_if_necessary(
|
||||
const Config config) {
|
||||
CpldUpdateStatus update_if_necessary(const Config config) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
@@ -87,6 +92,121 @@ CpldUpdateStatus update_if_necessary(
|
||||
return ok ? CpldUpdateStatus::Success : CpldUpdateStatus::Program_failed;
|
||||
}
|
||||
|
||||
static CpldUpdateStatus enter_maintenance_mode(CPLD& cpld) {
|
||||
/* Unknown state */
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
|
||||
/* Run-Test/Idle */
|
||||
if (!cpld.idcode_ok()) {
|
||||
return CpldUpdateStatus::Idcode_check_failed;
|
||||
}
|
||||
|
||||
cpld.sample();
|
||||
cpld.bypass();
|
||||
cpld.enable();
|
||||
|
||||
/* If silicon ID doesn't match, there's a serious problem. Leave CPLD
|
||||
* in passive state.
|
||||
*/
|
||||
if (!cpld.silicon_id_ok()) {
|
||||
return CpldUpdateStatus::Silicon_id_check_failed;
|
||||
}
|
||||
|
||||
return CpldUpdateStatus::Success;
|
||||
}
|
||||
|
||||
static void exit_maintenance_mode(CPLD& cpld) {
|
||||
cpld.disable();
|
||||
cpld.bypass();
|
||||
|
||||
/* Initiate SRAM reload from flash we just programmed. */
|
||||
cpld.sample();
|
||||
cpld.clamp();
|
||||
cpld.disable();
|
||||
}
|
||||
|
||||
static uint32_t get_firmware_crc(CPLD& cpld) {
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
cpld.prepare_read(0x0000);
|
||||
|
||||
for (size_t i = 0; i < 3328; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
cpld.prepare_read(0x0001);
|
||||
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
return crc.checksum();
|
||||
}
|
||||
|
||||
CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
CPLD cpld{jtag};
|
||||
|
||||
if (portapack::display.read_display_status())
|
||||
return CpldUpdateStatus::Success; // LCD is ready
|
||||
|
||||
CpldUpdateStatus result = enter_maintenance_mode(cpld);
|
||||
if (result != CpldUpdateStatus::Success)
|
||||
return result;
|
||||
|
||||
uint32_t checksum = get_firmware_crc(cpld);
|
||||
|
||||
if (checksum == REV_20170522_CHECKSUM) {
|
||||
// H2 firmware present
|
||||
if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1))
|
||||
return CpldUpdateStatus::Program_failed;
|
||||
} else if (checksum == REV_20150901_CHECKSUM) {
|
||||
// H1 firmware present
|
||||
if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1))
|
||||
return CpldUpdateStatus::Program_failed;
|
||||
} else {
|
||||
// no firmware present
|
||||
if (!cpld.program(config_rev_20150901.block_0, config_rev_20150901.block_1))
|
||||
return CpldUpdateStatus::Program_failed;
|
||||
}
|
||||
|
||||
exit_maintenance_mode(cpld);
|
||||
|
||||
if (portapack::display.read_display_status())
|
||||
return CpldUpdateStatus::Success; // LCD is ready
|
||||
|
||||
if (checksum != REV_20150901_CHECKSUM && checksum != REV_20170522_CHECKSUM) {
|
||||
// try the other one
|
||||
CpldUpdateStatus result = enter_maintenance_mode(cpld);
|
||||
|
||||
if (result != CpldUpdateStatus::Success)
|
||||
return result;
|
||||
|
||||
if (!cpld.program(config_rev_20170522.block_0, config_rev_20170522.block_1))
|
||||
return CpldUpdateStatus::Program_failed;
|
||||
|
||||
exit_maintenance_mode(cpld);
|
||||
|
||||
if (portapack::display.read_display_status())
|
||||
return CpldUpdateStatus::Success; // LCD is ready
|
||||
}
|
||||
|
||||
return CpldUpdateStatus::Program_failed;
|
||||
}
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace portapack */
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ enum class CpldUpdateStatus {
|
||||
Program_failed = 3
|
||||
};
|
||||
|
||||
CpldUpdateStatus update_if_necessary(
|
||||
const Config config);
|
||||
CpldUpdateStatus update_if_necessary(const Config config);
|
||||
CpldUpdateStatus update_autodetect(const Config config_rev_20150901, const Config config_rev_20170522);
|
||||
|
||||
} /* namespace cpld */
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -84,6 +84,17 @@ void lcd_wake() {
|
||||
lcd_display_on();
|
||||
}
|
||||
|
||||
uint32_t lcd_read_display_status() {
|
||||
io.lcd_data_write_command_and_data(0x09, {});
|
||||
io.lcd_read_word();
|
||||
|
||||
uint32_t value2 = io.lcd_read_word();
|
||||
uint32_t value3 = io.lcd_read_word();
|
||||
uint32_t value4 = io.lcd_read_word();
|
||||
uint32_t value5 = io.lcd_read_word();
|
||||
return value5 + (value4 << 8) + (value3 << 16) + (value2 << 24);
|
||||
}
|
||||
|
||||
void lcd_init() {
|
||||
// LCDs are configured for IM[2:0] = 001
|
||||
// 8080-I system, 16-bit parallel bus
|
||||
@@ -260,6 +271,25 @@ void lcd_vertical_scrolling_start_address(
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ILI9341::read_display_status() {
|
||||
lcd_reset();
|
||||
uint32_t display_status = lcd_read_display_status();
|
||||
|
||||
/* This tries to validate the display_status.
|
||||
* The value could vary from device to device, so we are less specific here.
|
||||
* 0xFFFFFEFF was seen when the display was not reachable
|
||||
* 0x00610000 was seen when the display was reachable
|
||||
*/
|
||||
|
||||
if (display_status > 0x0E000000ULL)
|
||||
return false;
|
||||
|
||||
if (display_status < 0x00000100ULL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ILI9341::init() {
|
||||
lcd_reset();
|
||||
lcd_init();
|
||||
|
||||
@@ -42,6 +42,8 @@ class ILI9341 {
|
||||
ILI9341(ILI9341&&) = delete;
|
||||
void operator=(const ILI9341&) = delete;
|
||||
|
||||
bool read_display_status();
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -34,6 +35,21 @@
|
||||
|
||||
// #include "portapack_persistent_memory.hpp"
|
||||
|
||||
// Darkened pixel bit mask for each possible shift value.
|
||||
static const uint16_t darken_mask[4] = {
|
||||
0b1111111111111111, // RrrrrGgggggBbbbb
|
||||
0b0111101111101111, // 0Rrrr0Ggggg0Bbbb
|
||||
0b0011100111100111, // 00Rrr00Gggg00Bbb
|
||||
0b0001100011100011 // 000Rr000Ggg000Bb
|
||||
};
|
||||
|
||||
// To darken, dividing each color level R/G/B by 2^shift.
|
||||
#define DARKENED_PIXEL(pixel, shift) ((pixel >> shift) & darken_mask[shift])
|
||||
|
||||
// To un-darken, multiply each color level by 2^shift (might still be darker that before since some bits may have been lost above).
|
||||
// This function will only be called when the pixel has previously been darkened, so no masking is needed.
|
||||
#define UNDARKENED_PIXEL(pixel, shift) (pixel << shift)
|
||||
|
||||
namespace portapack {
|
||||
|
||||
class IO {
|
||||
@@ -147,7 +163,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixel(ui::Color pixel) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
lcd_write_data(pixel.v);
|
||||
}
|
||||
@@ -158,7 +174,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixels(ui::Color pixel, size_t n) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
while (n--) {
|
||||
lcd_write_data(pixel.v);
|
||||
@@ -167,7 +183,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
auto v = pixel.v;
|
||||
n >>= 3;
|
||||
@@ -331,25 +347,6 @@ class IO {
|
||||
addr(1); /* Set up for data phase (most likely after a command) */
|
||||
}
|
||||
|
||||
void darken_color(ui::Color& pixel, uint8_t darken_level_shift) {
|
||||
// TODO: 1. do we need edge control?
|
||||
// currently didn't see and issue without edge control
|
||||
// but maybe hurts screen hardware without one?
|
||||
|
||||
// TODO: 2. de-color mode for accessibility
|
||||
// TODO: 3. high contrast mode for accessibility
|
||||
|
||||
uint16_t r = (pixel.v >> 11) & 0x1F; // Extract red
|
||||
uint16_t g = (pixel.v >> 5) & 0x3F; // Extract green
|
||||
uint16_t b = pixel.v & 0x1F; // Extract blue
|
||||
|
||||
r = r >> darken_level_shift; // Darken red
|
||||
g = g >> darken_level_shift; // Darken green
|
||||
b = b >> darken_level_shift; // Darken blue
|
||||
|
||||
pixel.v = (r << 11) | (g << 5) | b; // Combine back to color, check UI::color for the color layout
|
||||
}
|
||||
|
||||
// void high_contrast(ui::Color& pixel, size_t contrast_level_shift) { // TODO
|
||||
// uint16_t r = (pixel.v >> 11) & 0x1F;
|
||||
// uint16_t g = (pixel.v >> 5) & 0x3F;
|
||||
@@ -417,7 +414,12 @@ class IO {
|
||||
halPolledDelay(18); // 90ns
|
||||
|
||||
const auto value_low = data_read();
|
||||
return (value_high << 8) | value_low;
|
||||
uint32_t original_value = (value_high << 8) | value_low;
|
||||
|
||||
if (get_dark_cover()) {
|
||||
original_value = UNDARKENED_PIXEL(original_value, get_brightness());
|
||||
}
|
||||
return original_value;
|
||||
}
|
||||
|
||||
void io_write(const bool address, const uint_fast16_t value) {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "memory_map.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_flash_utility.hpp"
|
||||
@@ -46,6 +47,7 @@
|
||||
#include <hal.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace ui;
|
||||
|
||||
namespace portapack {
|
||||
namespace persistent_memory {
|
||||
@@ -229,7 +231,9 @@ struct data_t {
|
||||
uint16_t fake_brightness_level : 4;
|
||||
|
||||
// Encoder rotation rate multiplier for larger increments when rotated rapidly
|
||||
uint16_t encoder_rate_multiplier : 8;
|
||||
uint16_t encoder_rate_multiplier : 4;
|
||||
|
||||
uint16_t UNUSED : 4;
|
||||
|
||||
// Headphone volume in centibels.
|
||||
int16_t headphone_volume_cb;
|
||||
@@ -246,6 +250,11 @@ struct data_t {
|
||||
// Daylight savings time
|
||||
dst_config_t dst_config;
|
||||
|
||||
// Menu Color Scheme
|
||||
Color menu_color;
|
||||
|
||||
uint16_t UNUSED_16;
|
||||
|
||||
constexpr data_t()
|
||||
: structure_version(data_structure_version_enum::VERSION_CURRENT),
|
||||
target_frequency(target_frequency_reset_value),
|
||||
@@ -296,11 +305,15 @@ struct data_t {
|
||||
encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL),
|
||||
fake_brightness_level(BRIGHTNESS_50),
|
||||
encoder_rate_multiplier(1),
|
||||
UNUSED(0),
|
||||
|
||||
headphone_volume_cb(-600),
|
||||
misc_config(),
|
||||
ui_config2(),
|
||||
config_mode_storage(CONFIG_MODE_NORMAL_VALUE),
|
||||
dst_config() {
|
||||
dst_config(),
|
||||
menu_color(Color::grey()),
|
||||
UNUSED_16() {
|
||||
}
|
||||
};
|
||||
|
||||
@@ -408,6 +421,7 @@ void defaults() {
|
||||
set_config_disable_external_tcxo(false);
|
||||
set_encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL);
|
||||
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
|
||||
set_menu_color(Color::grey());
|
||||
|
||||
// Default values for recon app.
|
||||
set_recon_autosave_freqs(false);
|
||||
@@ -430,13 +444,15 @@ void defaults() {
|
||||
}
|
||||
|
||||
void init() {
|
||||
const auto switches_state = get_switches_state();
|
||||
const auto switches_state = swizzled_switches();
|
||||
|
||||
// ignore for valid check
|
||||
auto config_mode_backup = config_mode_storage_direct();
|
||||
set_config_mode_storage_direct(CONFIG_MODE_NORMAL_VALUE);
|
||||
|
||||
if (!(switches_state[(size_t)ui::KeyEvent::Left] && switches_state[(size_t)ui::KeyEvent::Right]) && backup_ram->is_valid()) {
|
||||
if (!(((switches_state >> (size_t)ui::KeyEvent::Left & 1) == 1) &&
|
||||
((switches_state >> (size_t)ui::KeyEvent::Right & 1) == 1)) &&
|
||||
backup_ram->is_valid()) {
|
||||
// Copy valid persistent data into cache.
|
||||
cached_backup_ram = *backup_ram;
|
||||
|
||||
@@ -455,7 +471,7 @@ void init() {
|
||||
|
||||
// Firmware upgrade handling - adjust newly defined fields where 0 is an invalid default
|
||||
if (fake_brightness_level() == 0) set_fake_brightness_level(BRIGHTNESS_50);
|
||||
if (encoder_rate_multiplier() == 0) set_encoder_rate_multiplier(1);
|
||||
if (menu_color().v == 0) set_menu_color(Color::grey());
|
||||
}
|
||||
|
||||
void persist() {
|
||||
@@ -987,7 +1003,9 @@ void set_encoder_dial_sensitivity(uint8_t v) {
|
||||
data->encoder_dial_sensitivity = v;
|
||||
}
|
||||
uint8_t encoder_rate_multiplier() {
|
||||
return data->encoder_rate_multiplier;
|
||||
uint8_t v = data->encoder_rate_multiplier;
|
||||
if (v == 0) v = 1; // minimum value is 1; treat 0 the same as 1
|
||||
return v;
|
||||
}
|
||||
void set_encoder_rate_multiplier(uint8_t v) {
|
||||
data->encoder_rate_multiplier = v;
|
||||
@@ -1045,6 +1063,14 @@ void toggle_fake_brightness_level() {
|
||||
}
|
||||
}
|
||||
|
||||
// Menu Color Scheme
|
||||
Color menu_color() {
|
||||
return data->menu_color;
|
||||
}
|
||||
void set_menu_color(Color v) {
|
||||
data->menu_color = v;
|
||||
}
|
||||
|
||||
// PMem to sdcard settings
|
||||
|
||||
bool should_use_sdcard_for_pmem() {
|
||||
@@ -1151,6 +1177,7 @@ bool debug_dump() {
|
||||
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(data->config_mode_storage, 8));
|
||||
pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)data->dst_config.v, 8));
|
||||
pmem_dump_file.write_line("fake_brightness_level: " + to_string_dec_uint(data->fake_brightness_level));
|
||||
pmem_dump_file.write_line("menu_color: 0x" + to_string_hex(data->menu_color.v, 4));
|
||||
|
||||
// ui_config bits
|
||||
const auto backlight_timer = portapack::persistent_memory::config_backlight_timer();
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "serializer.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "config_mode.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
// persistent memory from/to sdcard flag file
|
||||
#define PMEM_FILEFLAG u"/SETTINGS/PMEM_FILEFLAG"
|
||||
@@ -46,6 +47,7 @@
|
||||
|
||||
using namespace modems;
|
||||
using namespace serializer;
|
||||
using namespace ui;
|
||||
|
||||
namespace portapack {
|
||||
|
||||
@@ -282,6 +284,9 @@ uint8_t fake_brightness_level();
|
||||
void set_fake_brightness_level(uint8_t v);
|
||||
void toggle_fake_brightness_level();
|
||||
|
||||
Color menu_color();
|
||||
void set_menu_color(Color v);
|
||||
|
||||
/* Recon app */
|
||||
bool recon_autosave_freqs();
|
||||
bool recon_autostart_recon();
|
||||
|
||||
@@ -78,6 +78,18 @@ struct Color {
|
||||
((r & 0xf8) << 8) | ((g & 0xfc) << 3) | ((b & 0xf8) >> 3))} {
|
||||
}
|
||||
|
||||
uint8_t r() {
|
||||
return (uint8_t)((v >> 8) & 0xf8);
|
||||
}
|
||||
|
||||
uint8_t g() {
|
||||
return (uint8_t)((v >> 3) & 0xfc);
|
||||
}
|
||||
|
||||
uint8_t b() {
|
||||
return (uint8_t)((v << 3) & 0xf8);
|
||||
}
|
||||
|
||||
uint8_t to_greyscale() {
|
||||
uint32_t r = (v >> 8) & 0xf8;
|
||||
uint32_t g = (v >> 3) & 0xfc;
|
||||
|
||||
@@ -1290,6 +1290,11 @@ void NewButton::set_color(Color color) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void NewButton::set_bg_color(Color color) {
|
||||
bg_color_ = color;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void NewButton::set_vertical_center(bool value) {
|
||||
vertical_center_ = value;
|
||||
set_dirty();
|
||||
@@ -1343,7 +1348,7 @@ Style NewButton::paint_style() {
|
||||
s.background = style().foreground;
|
||||
s.foreground = Color::black();
|
||||
} else {
|
||||
s.background = Color::grey();
|
||||
s.background = bg_color_;
|
||||
s.foreground = style().foreground;
|
||||
}
|
||||
|
||||
|
||||
@@ -504,6 +504,7 @@ class NewButton : public Widget {
|
||||
void set_bitmap(const Bitmap* bitmap);
|
||||
void set_text(const std::string value);
|
||||
void set_color(Color value);
|
||||
void set_bg_color(Color value);
|
||||
void set_vertical_center(bool value);
|
||||
std::string text() const;
|
||||
const Bitmap* bitmap();
|
||||
@@ -522,6 +523,7 @@ class NewButton : public Widget {
|
||||
protected:
|
||||
virtual Style paint_style();
|
||||
Color color_;
|
||||
Color bg_color_{Color::light_grey()};
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
|
||||
@@ -49,5 +49,5 @@ for i in range(0, len(image), 4):
|
||||
snippet = image[i:i+4]
|
||||
val = int.from_bytes(snippet, byteorder='little')
|
||||
offset = val & 0xFFFF
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0):
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size):
|
||||
print ("External code address", hex(val),"at offset", hex(i),"in", sys.argv[1])
|
||||
|
||||
@@ -76,7 +76,7 @@ def patch_image(path, image_data, search_address, replace_address):
|
||||
external_application_image += new_snippet
|
||||
else:
|
||||
external_application_image += snippet
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0):
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size):
|
||||
print ("WARNING: External code address", hex(val), "at offset", hex(x*4), "in", path)
|
||||
|
||||
return external_application_image
|
||||
|
||||
@@ -91,7 +91,7 @@ for i in range(0, len(spi_image), 4):
|
||||
snippet = spi_image[i:i+4]
|
||||
val = int.from_bytes(snippet, byteorder='little')
|
||||
checksum += val
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0):
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size):
|
||||
print ("WARNING: External code address", hex(val), "at offset", hex(i), "in", sys.argv[3])
|
||||
|
||||
final_checksum = 0
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
a=150000,b=285000,m=AM,bw=DSB,s=5kHz,d=Longwave broadcast band|BCB (EU)
|
||||
a=525000,b=1605000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (EU & J)
|
||||
a=530000,b=1710000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (US)
|
||||
a=1800000,b=29700000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=26900000,b=27400000,m=WFM,bw=16k,d=Citizens_band_radio|Citizens band
|
||||
a=28000000,b=30000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=29000000,b=54000000,m=WFM,bw=16k,d=Land mobile
|
||||
a=50000000,b=54000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=65000000,b=85000000,m=WFM,bw=16k,d=Land mobile (EU)
|
||||
a=108000000,b=136000000,m=WFM,bw=16k,d=Aircraft
|
||||
a=120000000,b=160000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (EU)
|
||||
a=132000000,b=174000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=142000000,b=170000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=144000000,b=148000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=216000000,b=222000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=222000000,b=225000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=335000000,b=384000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=406000000,b=512000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=450000000,b=470000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=430000000,b=450000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=806000000,b=947000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=1200000000,b=1600000000,m=WFM,bw=16k,s=50kHz,d=Amateur|Land mobile|GPS
|
||||
a=150000,b=285000,m=AM,bw=DSB 9k,s=5kHz,d=Longwave broadcast band|BCB (EU)
|
||||
a=525000,b=1605000,m=AM,bw=DSB 9k,s=5kHz,d=AM broadcast band|BCB (EU & J)
|
||||
a=530000,b=1710000,m=AM,bw=DSB 9k,s=5kHz,d=AM broadcast band|BCB (US)
|
||||
a=1800000,b=29700000,m=AM,bw=LSB-3k,d=Amateur_radio|Amateur
|
||||
a=26900000,b=27400000,m=AM,bw=DSB 9k,d=Citizens_band_radio|Citizens band
|
||||
a=28000000,b=30000000,m=AM,bw=USB+3k,d=Amateur_radio|Amateur
|
||||
a=29000000,b=54000000,m=AM,bw=DSB 9k,d=Land mobile
|
||||
a=50000000,b=54000000,m=NFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=65000000,b=85000000,m=NFM,bw=16k,d=Land mobile (EU)
|
||||
a=108000000,b=136000000,m=NFM,bw=16k,d=Aircraft
|
||||
a=120000000,b=160000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (EU)
|
||||
a=132000000,b=174000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=142000000,b=170000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=144000000,b=148000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=216000000,b=222000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=222000000,b=225000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=335000000,b=384000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=406000000,b=512000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=450000000,b=470000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=430000000,b=450000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=806000000,b=947000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=1200000000,b=1600000000,m=NFM,bw=16k,s=50kHz,d=Amateur|Land mobile|GPS
|
||||
|
||||
Reference in New Issue
Block a user