External module api (#2290)

* added external module debug view

* integrated module driver into i2c subsystem

* implemented i2c app loader

* added view mirror api

* fixed build

* added i2c to module api

* implemented standalone app touch event

* implemented focus management

* reverted formating change

* refactoring

* refactoring

* implemented events

* fixed memory usage
This commit is contained in:
Bernd Herzog
2024-10-14 13:07:41 +02:00
committed by GitHub
parent 5a0066963e
commit dcaa02c1e1
18 changed files with 667 additions and 24 deletions

View File

@@ -37,6 +37,7 @@
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_max17055.hpp"
#include "ui_external_module_view.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -514,6 +515,7 @@ void DebugMenuView::on_populate() {
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
{"Ext Module", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<ExternalModuleView>(); }},
});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) {

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_external_module_view.hpp"
#include "portapack.hpp"
#include "ui_standalone_view.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
#include <optional>
namespace ui {
void ExternalModuleView::focus() {
dummy.focus();
}
void ExternalModuleView::on_tick_second() {
i2cdev::I2CDevManager::manual_scan();
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (!dev) {
text_header.set("No module connected");
text_name.set("");
text_version.set("");
text_number_apps.set("");
text_app1_name.set("");
text_app2_name.set("");
text_app3_name.set("");
text_app4_name.set("");
text_app5_name.set("");
return;
}
auto device_info = dev->readDeviceInfo();
if (device_info.has_value() == false) {
text_header.set("No module connected");
text_name.set("");
text_version.set("");
text_number_apps.set("");
text_app1_name.set("");
text_app2_name.set("");
text_app3_name.set("");
text_app4_name.set("");
text_app5_name.set("");
return;
}
text_header.set("Module found");
std::string btnText = (std::string) "Module: " + device_info->module_name;
text_name.set(btnText);
text_version.set("Version: " + std::to_string(device_info->module_version));
text_number_apps.set("No# Apps: " + std::to_string(device_info->application_count));
for (uint32_t i = 0; i < device_info->application_count && i < 5; i++) {
auto appInfo = dev->getStandaloneAppInfo(i);
if (appInfo.has_value() == false) {
continue;
}
std::string btnText = (std::string) "App " + std::to_string(device_info->application_count) + ": " + (const char*)appInfo->app_name;
switch (appInfo->menu_location) {
case app_location_t::UTILITIES:
btnText += " (Utilities)";
break;
case app_location_t::RX:
btnText += " (RX)";
break;
case app_location_t::TX:
btnText += " (TX)";
break;
case app_location_t::DEBUG:
btnText += " (Debug)";
break;
case app_location_t::HOME:
btnText += " (Home)";
break;
}
switch (i) {
case 0:
text_app1_name.set(btnText);
break;
case 1:
text_app2_name.set(btnText);
break;
case 2:
text_app3_name.set(btnText);
break;
case 3:
text_app4_name.set(btnText);
break;
case 4:
text_app5_name.set(btnText);
break;
}
}
}
} // namespace ui

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
#include "portapack.hpp"
#include "memory_map.hpp"
#include "irq_controls.hpp"
#include <functional>
#include <utility>
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
namespace ui {
class ExternalModuleView : public View {
public:
ExternalModuleView(NavigationView& nav)
: nav_(nav) {
add_children({&text_header,
&text_name,
&text_version,
&text_number_apps,
&text_app1_name,
&text_app2_name,
&text_app3_name,
&text_app4_name,
&text_app5_name,
&dummy});
text_header.set("No module connected");
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
~ExternalModuleView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
}
std::string title() const override { return "Ext Module"; };
void focus() override;
private:
NavigationView& nav_;
Text text_header{{16, 16, 208, 16}};
Text text_name{{24, 32, 200, 16}};
Text text_version{{24, 48, 200, 16}};
Text text_number_apps{{24, 64, 200, 16}};
Text text_app1_name{{24, 96, 200, 16}};
Text text_app2_name{{24, 112, 200, 16}};
Text text_app3_name{{24, 128, 200, 16}};
Text text_app4_name{{24, 144, 200, 16}};
Text text_app5_name{{24, 160, 200, 16}};
Button dummy{
{240, 0, 0, 0},
""};
SignalToken signal_token_tick_second{};
void on_tick_second();
};
} // namespace ui

View File

@@ -22,6 +22,12 @@
#include "ui_standalone_view.hpp"
#include "irq_controls.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_ppmod.hpp"
#include "ui_font_fixed_5x8.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {
void create_thread(int32_t (*fn)(void*), void* arg, size_t stack_size, int priority) {
@@ -34,6 +40,16 @@ void fill_rectangle(int x, int y, int width, int height, uint16_t color) {
painter.fill_rectangle({x, y, width, height}, ui::Color(color));
}
void fill_rectangle_unrolled8(int x, int y, int width, int height, uint16_t color) {
ui::Painter painter;
painter.fill_rectangle_unrolled8({x, y, width, height}, ui::Color(color));
}
void draw_bitmap(int x, int y, int width, int height, const uint8_t* pixels, uint16_t foreground, uint16_t background) {
ui::Painter painter;
painter.draw_bitmap({x, y}, {{width, height}, pixels}, ui::Color(foreground), ui::Color(background));
}
void* alloc(size_t size) {
void* p = chHeapAlloc(0x0, size);
if (p == nullptr)
@@ -45,6 +61,45 @@ uint64_t get_switches_state_ulong() {
return get_switches_state().to_ulong();
}
ui::Coord scroll_area_y(const ui::Coord y) {
return portapack::display.scroll_area_y(y);
}
void scroll_set_area(const ui::Coord top_y, const ui::Coord bottom_y) {
portapack::display.scroll_set_area(top_y, bottom_y);
}
void scroll_disable() {
portapack::display.scroll_disable();
}
ui::Coord scroll_set_position(const ui::Coord position) {
return portapack::display.scroll_set_position(position);
}
ui::Coord scroll(const int32_t delta) {
return portapack::display.scroll(delta);
}
bool i2c_read(uint8_t* cmd, size_t cmd_len, uint8_t* data, size_t data_len) {
auto dev = (i2cdev::I2cDev_PPmod*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDECMDL_PPMOD);
if (!dev) {
return false;
}
if (data_len == 0 || data == nullptr)
return dev->i2c_write(nullptr, 0, cmd, cmd_len);
return dev->i2c_read(cmd, cmd_len, data, data_len);
}
StandaloneView* standaloneView = nullptr;
void set_dirty() {
if (standaloneView != nullptr)
standaloneView->set_dirty();
}
standalone_application_api_t api = {
/* .malloc = */ &alloc,
/* .calloc = */ &calloc,
@@ -54,20 +109,77 @@ standalone_application_api_t api = {
/* .fill_rectangle = */ &fill_rectangle,
/* .swizzled_switches = */ &swizzled_switches,
/* .get_switches_state = */ &get_switches_state_ulong,
/* .fixed_5x8_glyph_data = */ ui::font::fixed_5x8.get_data(),
/* .fixed_8x16_glyph_data = */ ui::font::fixed_8x16.get_data(),
/* .fill_rectangle_unrolled8 = */ &fill_rectangle_unrolled8,
/* .draw_bitmap = */ &draw_bitmap,
/* .scroll_area_y = */ &scroll_area_y,
/* .scroll_set_area = */ &scroll_set_area,
/* .scroll_disable = */ &scroll_disable,
/* .scroll_set_position = */ &scroll_set_position,
/* .scroll = */ &scroll,
/* .i2c_read = */ &i2c_read,
/* .panic = */ &chDbgPanic,
/* .set_dirty = */ &set_dirty,
};
StandaloneView::StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image)
: nav_(nav), _app_image(std::move(app_image)) {
get_application_information()->initialize(api);
add_children({&dummy});
StandaloneView::StandaloneView(NavigationView& nav, uint8_t* app_image)
: nav_(nav), _app_image(app_image) {
if (_app_image == nullptr) {
chDbgPanic("Invalid application image");
}
set_focusable(true);
standaloneView = this;
}
void StandaloneView::focus() {
dummy.focus();
View::focus();
}
void StandaloneView::paint(Painter& painter) {
(void)painter;
if (initialized &&
get_application_information()->header_version > 1) {
get_application_information()->PaintViewMirror();
}
}
void StandaloneView::on_focus() {
if (get_application_information()->header_version > 1) {
get_application_information()->OnFocus();
}
}
bool StandaloneView::on_key(const KeyEvent key) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnKeyEvent((uint8_t)key);
}
return false;
}
bool StandaloneView::on_encoder(const EncoderEvent event) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnEncoder((int32_t)event);
}
return false;
}
bool StandaloneView::on_touch(const TouchEvent event) {
if (get_application_information()->header_version > 1) {
get_application_information()->OnTouchEvent(event.point.x(), event.point.y(), (uint32_t)event.type);
}
return true;
}
bool StandaloneView::on_keyboard(const KeyboardEvent event) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnKeyboad((uint8_t)event);
}
return false;
}
void StandaloneView::frame_sync() {
@@ -79,4 +191,14 @@ void StandaloneView::frame_sync() {
}
}
void StandaloneView::on_after_attach() {
context().focus_manager().setMirror(this);
get_application_information()->initialize(api);
}
void StandaloneView::on_before_detach() {
get_application_information()->shutdown();
context().focus_manager().clearMirror();
}
} // namespace ui

View File

@@ -31,28 +31,34 @@ namespace ui {
class StandaloneView : public View {
public:
StandaloneView(NavigationView& nav, std::unique_ptr<uint8_t[]> app_image);
virtual ~StandaloneView() override { get_application_information()->shutdown(); }
void focus() override;
StandaloneView(NavigationView& nav, uint8_t* app_image);
virtual ~StandaloneView() override {
}
std::string title() const override { return (const char*)get_application_information()->app_name; };
void focus() override;
void paint(Painter& painter) override;
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_encoder(const EncoderEvent event) override;
bool on_touch(const TouchEvent event) override;
bool on_keyboard(const KeyboardEvent event) override;
void on_after_attach() override;
void on_before_detach() override;
void frame_sync();
private:
bool initialized = false;
NavigationView& nav_;
std::unique_ptr<uint8_t[]> _app_image;
uint8_t* _app_image;
standalone_application_information_t* get_application_information() const {
return reinterpret_cast<standalone_application_information_t*>(_app_image.get());
return reinterpret_cast<standalone_application_information_t*>(_app_image);
}
Button dummy{
{240, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_sample{
Message::ID::DisplayFrameSync,
[this](const Message* const) {