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

@@ -0,0 +1,83 @@
/*
* 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 "i2cdev_ppmod.hpp"
#include "portapack.hpp"
#include <optional>
namespace i2cdev {
bool I2cDev_PPmod::init(uint8_t addr_) {
if (addr_ != I2CDEV_PPMOD_ADDR_1) return false;
addr = addr_;
model = I2CDECMDL_PPMOD;
return true;
}
void I2cDev_PPmod::update() {
}
std::optional<I2cDev_PPmod::device_info> I2cDev_PPmod::readDeviceInfo() {
Command cmd = Command::COMMAND_INFO;
I2cDev_PPmod::device_info info;
bool success = i2c_read((uint8_t*)&cmd, 2, (uint8_t*)&info, sizeof(I2cDev_PPmod::device_info));
if (success == false) {
return std::nullopt;
}
return info;
}
std::optional<I2cDev_PPmod::standalone_app_info> I2cDev_PPmod::getStandaloneAppInfo(uint32_t index) {
Command cmd = Command::COMMAND_APP_INFO;
uint32_t data = (uint32_t)cmd + (index << 16);
I2cDev_PPmod::standalone_app_info info;
bool success = i2c_read((uint8_t*)&data, 4, (uint8_t*)&info, sizeof(I2cDev_PPmod::standalone_app_info));
if (success == false) {
return std::nullopt;
}
return info;
}
constexpr size_t transfer_block_size = 128;
std::vector<uint8_t> I2cDev_PPmod::downloadStandaloneApp(uint32_t index, size_t offset) {
if (offset % transfer_block_size != 0) {
return {};
}
uint16_t data[3] = {(uint16_t)Command::COMMAND_APP_TRANSFER, index, offset / transfer_block_size};
std::vector<uint8_t> ret(transfer_block_size);
bool success = i2c_read((uint8_t*)&data, sizeof(data), (uint8_t*)ret.data(), transfer_block_size);
if (success == false) {
return {};
}
return ret;
}
} // namespace i2cdev

View File

@@ -0,0 +1,73 @@
/*
* 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 <cstdint>
#include <array>
#include <string>
#include <optional>
#include "standalone_app.hpp"
#include "i2cdevmanager.hpp"
namespace i2cdev {
class I2cDev_PPmod : public I2cDev {
public:
enum class Command : uint16_t {
COMMAND_NONE = 0,
// will respond with device_info
COMMAND_INFO = 0x18F0,
// will respond with info of application
COMMAND_APP_INFO = 0xA90B,
// will respond with application data
COMMAND_APP_TRANSFER = 0x4183,
};
typedef struct {
uint32_t api_version;
uint32_t module_version;
char module_name[20];
uint32_t application_count;
} device_info;
typedef struct {
uint32_t header_version;
uint8_t app_name[16];
uint8_t bitmap_data[32];
uint32_t icon_color;
app_location_t menu_location;
uint32_t binary_size;
} standalone_app_info;
bool init(uint8_t addr_) override;
void update() override;
std::optional<device_info> readDeviceInfo();
std::optional<standalone_app_info> getStandaloneAppInfo(uint32_t index);
std::vector<uint8_t> downloadStandaloneApp(uint32_t index, size_t offset);
};
} /* namespace i2cdev */

View File

@@ -35,6 +35,7 @@ enum I2C_DEVMDL {
I2CDEVMDL_BMP280,
I2CDEVMDL_BME280,
I2CDECMDL_BH1750,
I2CDECMDL_PPMOD,
};
#define I2CDEV_BMX280_ADDR_1 0x76
@@ -47,6 +48,8 @@ enum I2C_DEVMDL {
#define I2CDEV_BH1750_ADDR_1 0x23
#define I2CDEV_PPMOD_ADDR_1 0x51
// this will be the update interval for battery management ic's:
#define BATTERY_WIDGET_REFRESH_INTERVAL 10

View File

@@ -31,6 +31,7 @@
#include "i2cdev_max17055.hpp"
#include "i2cdev_ads1110.hpp"
#include "i2cdev_bh1750.hpp"
#include "i2cdev_ppmod.hpp"
namespace i2cdev {
@@ -86,6 +87,11 @@ bool I2CDevManager::found(uint8_t addr) {
if (!item.dev->init(addr)) item.dev = nullptr;
}
if (!item.dev && (addr == I2CDEV_PPMOD_ADDR_1)) {
item.dev = std::make_unique<I2cDev_PPmod>();
if (!item.dev->init(addr)) item.dev = nullptr;
}
// if can't find any driver, add it too with empty, so we won't try to init it again and again
devlist.push_back(std::move(item));
return true;

View File

@@ -26,9 +26,12 @@
#include <cstdint>
#include <stddef.h>
#define CURRENT_STANDALONE_APPLICATION_API_VERSION 1
#include "ui.hpp"
#define CURRENT_STANDALONE_APPLICATION_API_VERSION 2
struct standalone_application_api_t {
// Version 1
void* (*malloc)(size_t size);
void* (*calloc)(size_t num, size_t size);
void* (*realloc)(void* p, size_t size);
@@ -38,6 +41,26 @@ struct standalone_application_api_t {
uint8_t (*swizzled_switches)();
uint64_t (*get_switches_state)();
// Version 2
const uint8_t* fixed_5x8_glyph_data;
const uint8_t* fixed_8x16_glyph_data;
void (*fill_rectangle_unrolled8)(int x, int y, int width, int height, uint16_t color);
void (*draw_bitmap)(int x, int y, int width, int height, const uint8_t* pixels, uint16_t foreground, uint16_t background);
ui::Coord (*scroll_area_y)(const ui::Coord y);
void (*scroll_set_area)(const ui::Coord top_y, const ui::Coord bottom_y);
void (*scroll_disable)();
ui::Coord (*scroll_set_position)(const ui::Coord position);
ui::Coord (*scroll)(const int32_t delta);
bool (*i2c_read)(uint8_t* cmd, size_t cmd_len, uint8_t* data, size_t data_len);
void (*panic)(const char* msg);
void (*set_dirty)();
// TODO: add filesystem access functions
// TODO: add baseband access functions
// HOW TO extend this interface:
// to keep everything backward compatible: add new fields at the end
// and increment CURRENT_STANDALONE_APPLICATION_API_VERSION
@@ -69,6 +92,13 @@ struct standalone_application_information_t {
/// @brief gets called once at application shutdown
void (*shutdown)();
void (*PaintViewMirror)();
void (*OnTouchEvent)(int x, int y, uint32_t type);
void (*OnFocus)();
bool (*OnKeyEvent)(uint8_t key);
bool (*OnEncoder)(int32_t delta);
bool (*OnKeyboad)(uint8_t key);
};
#endif /*__UI_STANDALONE_APP_H__*/

View File

@@ -38,6 +38,14 @@ Widget* FocusManager::focus_widget() const {
return focus_widget_;
}
void FocusManager::setMirror(Widget* const mirror_widget) {
mirror_widget_ = mirror_widget;
}
void FocusManager::clearMirror() {
mirror_widget_ = nullptr;
}
void FocusManager::set_focus_widget(Widget* const new_focus_widget) {
// Widget already has focus.
if (new_focus_widget == focus_widget()) {
@@ -153,9 +161,12 @@ static int32_t rect_distances(
}
}
void FocusManager::update(
Widget* const top_widget,
const KeyEvent event) {
void FocusManager::update(Widget* const top_widget, const KeyEvent event) {
if (mirror_widget_) {
if (mirror_widget_->on_key(event))
return;
}
if (focus_widget()) {
const auto focus_screen_rect = focus_widget()->screen_rect();

View File

@@ -36,8 +36,12 @@ class FocusManager {
void update(Widget* const top_widget, const KeyEvent event);
// void update(Widget* const top_widget, const TouchEvent event);
void setMirror(Widget* const mirror_widget);
void clearMirror();
private:
Widget* focus_widget_{nullptr};
Widget* mirror_widget_{nullptr};
};
} /* namespace ui */

View File

@@ -94,6 +94,10 @@ class Font {
Size size_of(const std::string s) const;
const uint8_t* get_data() const {
return data;
}
private:
const Dim w;
const Dim h;

View File

@@ -94,8 +94,14 @@ void Widget::set_parent(Widget* const widget) {
visible(false);
}
if (widget == nullptr)
on_before_detach();
parent_ = widget;
if (widget != nullptr)
on_after_attach();
set_dirty();
}

View File

@@ -84,6 +84,9 @@ class Widget {
Widget* parent() const;
void set_parent(Widget* const widget);
virtual void on_after_attach() { return; };
virtual void on_before_detach() { return; };
bool hidden() const { return flags.hidden; }
void hidden(bool hide);
@@ -97,8 +100,8 @@ class Widget {
virtual void paint(Painter& painter) = 0;
virtual void on_show(){};
virtual void on_hide(){};
virtual void on_show() { return; };
virtual void on_hide() { return; };
virtual bool on_key(const KeyEvent event);
virtual bool on_encoder(const EncoderEvent event);