Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bb1260ea0 | |||
| fa54eaa58a | |||
| f9e1f737fd | |||
| 686f7cce83 | |||
| c2edbad0fb | |||
| d86dc40472 | |||
| 3be251d8fb | |||
| bb7e79886f | |||
| 9fb8d45b2e | |||
| b13502170e | |||
| aa85f7d19d | |||
| dc20ed4874 | |||
| 12a9839420 | |||
| 97b8007aca | |||
| c3bcf93698 | |||
| 2bbd44a8b5 | |||
| 72230129bb | |||
| 3ea02d912f | |||
| 3ca0f8cf97 |
@@ -5,6 +5,7 @@ AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: BlockIndent
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignOperands: DontAlign
|
||||
AlignTrailingComments: false
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
@@ -37,6 +38,8 @@ BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 0
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 4
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
EmptyLineAfterAccessModifier: Always
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [ByteWelder]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: 'Bug: '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Details (please complete the following information):**
|
||||
- ESP type: [e.g. ESP32-S3]
|
||||
- Device: [e.g. LilyGo T-Deck]
|
||||
- Tactility version: [e.g. 0.2.0 or main branch]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
Add your oops.tactility.one URL if you had a crash (scanned from QR).
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
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.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
### Checklist
|
||||
|
||||
- [ ] I read [contribution guidelines](https://github.com/ByteWelder/Tactility/blob/main/CONTRIBUTING.md)
|
||||
- [ ] Code adheres to the [coding style](https://github.com/ByteWelder/Tactility/blob/main/CODING_STYLE.md)
|
||||
|
||||
### Description
|
||||
|
||||
(describe what your changes are)
|
||||
@@ -20,7 +20,7 @@ runs:
|
||||
- name: 'Build'
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: v5.3.2
|
||||
esp_idf_version: v5.4
|
||||
target: ${{ inputs.arch }}
|
||||
path: './'
|
||||
- name: 'Release'
|
||||
@@ -29,12 +29,12 @@ runs:
|
||||
- name: 'Upload Artifact: Release'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tactility-${{ inputs.board_id }}
|
||||
name: Tactility-${{ inputs.board_id }}
|
||||
path: release/Tactility-${{ inputs.board_id }}
|
||||
retention-days: 30
|
||||
- name: 'Upload Artifact: Release symbols'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tactility-${{ inputs.board_id }}-symbols
|
||||
name: Tactility-${{ inputs.board_id }}-symbols
|
||||
path: release/Tactility-${{ inputs.board_id }}-symbols
|
||||
retention-days: 30
|
||||
|
||||
@@ -20,7 +20,7 @@ runs:
|
||||
- name: 'Build'
|
||||
uses: espressif/esp-idf-ci-action@main
|
||||
with:
|
||||
esp_idf_version: v5.3.2
|
||||
esp_idf_version: v5.4
|
||||
target: ${{ inputs.arch }}
|
||||
path: './'
|
||||
- name: 'Release'
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
name: Build Firmware
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
yellow-board:
|
||||
cyd-2432S024c:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Build"
|
||||
uses: ./.github/actions/build-firmware
|
||||
with:
|
||||
board_id: yellow-board
|
||||
board_id: cyd-2432S024c
|
||||
arch: esp32
|
||||
lilygo-tdeck:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -40,3 +45,12 @@ jobs:
|
||||
with:
|
||||
board_id: m5stack-cores3
|
||||
arch: esp32s3
|
||||
unphone:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Build"
|
||||
uses: ./.github/actions/build-firmware
|
||||
with:
|
||||
board_id: unphone
|
||||
arch: esp32s3
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
name: Build SDK
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
@@ -11,7 +16,7 @@ jobs:
|
||||
- name: "Build"
|
||||
uses: ./.github/actions/build-sdk
|
||||
with:
|
||||
board_id: yellow-board
|
||||
board_id: cyd-2432S024c
|
||||
arch: esp32
|
||||
esp32s3:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Disabled because of issue: https://github.com/libsdl-org/setup-sdl/issues/23
|
||||
name: Build Simulator
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
Build-Simulator-Linux:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Build"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
name: Tests
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
jobs:
|
||||
Run:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+2
-1
@@ -5,7 +5,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
if("${IDF_TARGET}" STREQUAL "esp32")
|
||||
list(APPEND BOARD_COMPONENTS
|
||||
YellowBoard
|
||||
CYD-2432S024C
|
||||
M5stackCore2
|
||||
)
|
||||
endif()
|
||||
@@ -14,6 +14,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
list(APPEND BOARD_COMPONENTS
|
||||
LilygoTdeck
|
||||
M5stackCoreS3
|
||||
UnPhone
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+10
-3
@@ -1,19 +1,26 @@
|
||||
# Kconfig file for Tactility example app
|
||||
menu "Tactility App"
|
||||
config TT_BOARD_NAME
|
||||
string "Board Name"
|
||||
default ""
|
||||
config TT_BOARD_ID
|
||||
string "Board ID"
|
||||
default ""
|
||||
choice
|
||||
prompt "Board"
|
||||
default TT_BOARD_CUSTOM
|
||||
|
||||
config TT_BOARD_CUSTOM
|
||||
bool "Custom"
|
||||
config TT_BOARD_YELLOW_BOARD_24_CAP
|
||||
bool "Yellow Board (2.4\" capacitive)"
|
||||
config TT_BOARD_CYD_2432S024C
|
||||
bool "CYD 2432S024C"
|
||||
config TT_BOARD_LILYGO_TDECK
|
||||
bool "LilyGo T-Deck"
|
||||
config TT_BOARD_M5STACK_CORE2
|
||||
bool "M5Stack Core2"
|
||||
config TT_BOARD_M5STACK_CORES3
|
||||
bool "M5Stack CoreS3"
|
||||
config TT_BOARD_UNPHONE
|
||||
bool "unPhone"
|
||||
help
|
||||
Select a board/hardware configuration.
|
||||
Use TT_BOARD_CUSTOM if you will manually configure the board in your project.
|
||||
|
||||
+6
-3
@@ -7,15 +7,18 @@
|
||||
#if defined(CONFIG_TT_BOARD_LILYGO_TDECK)
|
||||
#include "LilygoTdeck.h"
|
||||
#define TT_BOARD_HARDWARE &lilygo_tdeck
|
||||
#elif defined(CONFIG_TT_BOARD_YELLOW_BOARD_24_CAP)
|
||||
#include "YellowBoard.h"
|
||||
#define TT_BOARD_HARDWARE &yellow_board_24inch_cap
|
||||
#elif defined(CONFIG_TT_BOARD_CYD_2432S024C)
|
||||
#include "CYD2432S024C.h"
|
||||
#define TT_BOARD_HARDWARE &cyd_2432S024c_config
|
||||
#elif defined(CONFIG_TT_BOARD_M5STACK_CORE2)
|
||||
#include "M5stackCore2.h"
|
||||
#define TT_BOARD_HARDWARE &m5stack_core2
|
||||
#elif defined(CONFIG_TT_BOARD_M5STACK_CORES3)
|
||||
#include "M5stackCoreS3.h"
|
||||
#define TT_BOARD_HARDWARE &m5stack_cores3
|
||||
#elif defined(CONFIG_TT_BOARD_UNPHONE)
|
||||
#include "UnPhone.h"
|
||||
#define TT_BOARD_HARDWARE &unPhone
|
||||
#else
|
||||
#define TT_BOARD_HARDWARE NULL
|
||||
#error Replace TT_BOARD_HARDWARE in main.c with your own. Or copy one of the ./sdkconfig.board.* files into ./sdkconfig.
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
#include "app/AppManifest.h"
|
||||
#include "lvgl.h"
|
||||
#include "lvgl/Toolbar.h"
|
||||
|
||||
static void onShow(tt::app::AppContext& context, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, context);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
using namespace tt::app;
|
||||
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
lv_label_set_text(label, "Hello, world!");
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
class HelloWorldApp : public App {
|
||||
|
||||
extern const tt::app::AppManifest hello_world_app = {
|
||||
void onShow(AppContext& context, lv_obj_t* parent) override {
|
||||
lv_obj_t* toolbar = tt::lvgl::toolbar_create(parent, context);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
lv_label_set_text(label, "Hello, world!");
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
extern const AppManifest hello_world_app = {
|
||||
.id = "HelloWorld",
|
||||
.name = "Hello World",
|
||||
.onShow = onShow,
|
||||
.createApp = create<HelloWorldApp>
|
||||
};
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
dependencies:
|
||||
espressif/esp_lcd_ili9341: "2.0.0"
|
||||
espressif/esp_lcd_touch: "1.1.2"
|
||||
atanisoft/esp_lcd_touch_xpt2046: "1.0.5"
|
||||
espressif/esp_lcd_touch_cst816s: "1.0.3"
|
||||
espressif/esp_lcd_touch_gt911: "1.1.1~2"
|
||||
espressif/esp_lcd_touch_ft5x06: "1.0.6~1"
|
||||
espressif/esp_lcd_touch: "1.1.2"
|
||||
espressif/esp_io_expander: "1.0.1"
|
||||
espressif/esp_io_expander_tca95xx_16bit: "1.0.1"
|
||||
espressif/esp_tinyusb:
|
||||
version: "1.5.0"
|
||||
rules:
|
||||
- if: "target == esp32s3"
|
||||
idf: '5.3.2'
|
||||
idf: '5.4'
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
#include "YellowBoard.h"
|
||||
#include "CYD2432S024C.h"
|
||||
#include "hal/YellowDisplay.h"
|
||||
#include "hal/YellowSdCard.h"
|
||||
|
||||
bool twodotfour_lvgl_init();
|
||||
bool twodotfour_boot();
|
||||
|
||||
const tt::hal::Configuration yellow_board_24inch_cap = {
|
||||
const tt::hal::Configuration cyd_2432S024c_config = {
|
||||
.initBoot = &twodotfour_boot,
|
||||
.initLvgl = &twodotfour_lvgl_init,
|
||||
.createDisplay = createDisplay,
|
||||
@@ -15,7 +15,7 @@ const tt::hal::Configuration yellow_board_24inch_cap = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "First",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitDisabled,
|
||||
.initMode = tt::hal::i2c::InitMode::Disabled,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
@@ -33,7 +33,7 @@ const tt::hal::Configuration yellow_board_24inch_cap = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Second",
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitDisabled,
|
||||
.initMode = tt::hal::i2c::InitMode::Disabled,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
#include "hal/Configuration.h"
|
||||
|
||||
// Capacitive touch version of the 2.4" yellow board
|
||||
extern const tt::hal::Configuration yellow_board_24inch_cap;
|
||||
extern const tt::hal::Configuration cyd_2432S024c_config;
|
||||
+2
-1
@@ -44,6 +44,7 @@ static bool setBacklight(uint8_t duty) {
|
||||
.timer_sel = TWODOTFOUR_LCD_BACKLIGHT_LEDC_TIMER,
|
||||
.duty = duty,
|
||||
.hpoint = 0,
|
||||
.sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD,
|
||||
.flags = {
|
||||
.output_invert = false
|
||||
}
|
||||
@@ -140,7 +141,7 @@ bool YellowDisplay::start() {
|
||||
}
|
||||
|
||||
bool YellowDisplay::stop() {
|
||||
tt_assert(displayHandle != nullptr);
|
||||
assert(displayHandle != nullptr);
|
||||
|
||||
lvgl_port_remove_disp(displayHandle);
|
||||
|
||||
+1
-3
@@ -7,16 +7,14 @@
|
||||
|
||||
#define SDCARD_SPI_HOST SPI3_HOST
|
||||
#define SDCARD_PIN_CS GPIO_NUM_5
|
||||
#define SDCARD_SPI_FREQUENCY 800000U
|
||||
|
||||
std::shared_ptr<SdCard> createYellowSdCard() {
|
||||
auto* configuration = new tt::hal::SpiSdCard::Config(
|
||||
SDCARD_SPI_FREQUENCY,
|
||||
SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCard::MountBehaviourAtBoot,
|
||||
SdCard::MountBehaviour::AtBoot,
|
||||
nullptr,
|
||||
std::vector<gpio_num_t>(),
|
||||
SDCARD_SPI_HOST
|
||||
@@ -31,6 +31,7 @@ static bool init_spi() {
|
||||
.data5_io_num = 0,
|
||||
.data6_io_num = 0,
|
||||
.data7_io_num = 0,
|
||||
.data_io_default_level = false,
|
||||
.max_transfer_sz = TDECK_SPI_TRANSFER_SIZE_LIMIT,
|
||||
.flags = 0,
|
||||
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO,
|
||||
|
||||
@@ -20,7 +20,7 @@ extern const tt::hal::Configuration lilygo_tdeck = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = false,
|
||||
.hasMutableConfiguration = false,
|
||||
.config = (i2c_config_t) {
|
||||
@@ -38,7 +38,7 @@ extern const tt::hal::Configuration lilygo_tdeck = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "External",
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
|
||||
@@ -45,6 +45,7 @@ static bool setBacklight(uint8_t duty) {
|
||||
.timer_sel = TDECK_LCD_BACKLIGHT_LEDC_TIMER,
|
||||
.duty = duty,
|
||||
.hpoint = 0,
|
||||
.sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD,
|
||||
.flags = {
|
||||
.output_invert = 0
|
||||
}
|
||||
@@ -68,6 +69,8 @@ bool TdeckDisplay::start() {
|
||||
.user_ctx = nullptr,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans = 0,
|
||||
.flags = {
|
||||
.dc_high_on_cmd = 0,
|
||||
.dc_low_on_data = 0,
|
||||
@@ -134,6 +137,7 @@ bool TdeckDisplay::start() {
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = ioHandle,
|
||||
.panel_handle = panelHandle,
|
||||
.control_handle = nullptr,
|
||||
.buffer_size = TDECK_LCD_HORIZONTAL_RESOLUTION * TDECK_LCD_DRAW_BUFFER_HEIGHT * (TDECK_LCD_BITS_PER_PIXEL / 8),
|
||||
.double_buffer = true, // Disable to free up SPIRAM
|
||||
.trans_size = 0,
|
||||
@@ -145,12 +149,15 @@ bool TdeckDisplay::start() {
|
||||
.mirror_x = true,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
.flags = {
|
||||
.buff_dma = false,
|
||||
.buff_spiram = true,
|
||||
.sw_rotate = false,
|
||||
.swap_bytes = false
|
||||
},
|
||||
.swap_bytes = false,
|
||||
.full_refresh = false,
|
||||
.direct_mode = false
|
||||
}
|
||||
};
|
||||
|
||||
displayHandle = lvgl_port_add_disp(&disp_cfg);
|
||||
@@ -159,7 +166,7 @@ bool TdeckDisplay::start() {
|
||||
}
|
||||
|
||||
bool TdeckDisplay::stop() {
|
||||
tt_assert(displayHandle != nullptr);
|
||||
assert(displayHandle != nullptr);
|
||||
|
||||
lvgl_port_remove_disp(displayHandle);
|
||||
|
||||
|
||||
@@ -56,11 +56,11 @@ TdeckPower::~TdeckPower() {
|
||||
|
||||
bool TdeckPower::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE:
|
||||
case CHARGE_LEVEL:
|
||||
case MetricType::BatteryVoltage:
|
||||
case MetricType::ChargeLevel:
|
||||
return true;
|
||||
case IS_CHARGING:
|
||||
case CURRENT:
|
||||
case MetricType::IsCharging:
|
||||
case MetricType::Current:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -69,17 +69,17 @@ bool TdeckPower::supportsMetric(MetricType type) const {
|
||||
|
||||
bool TdeckPower::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE:
|
||||
case MetricType::BatteryVoltage:
|
||||
return readBatteryVoltageSampled(data.valueAsUint32);
|
||||
case CHARGE_LEVEL:
|
||||
case MetricType::ChargeLevel:
|
||||
if (readBatteryVoltageSampled(data.valueAsUint32)) {
|
||||
data.valueAsUint32 = estimateChargeLevelFromVoltage(data.valueAsUint32);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
case IS_CHARGING:
|
||||
case CURRENT:
|
||||
case MetricType::IsCharging:
|
||||
case MetricType::Current:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,17 @@
|
||||
#include <esp_vfs_fat.h>
|
||||
#include <sdmmc_cmd.h>
|
||||
|
||||
#define TDECK_SDCARD_SPI_FREQUENCY 800000U
|
||||
#define TDECK_SDCARD_PIN_CS GPIO_NUM_39
|
||||
#define TDECK_LCD_PIN_CS GPIO_NUM_12
|
||||
#define TDECK_RADIO_PIN_CS GPIO_NUM_9
|
||||
|
||||
std::shared_ptr<SdCard> createTdeckSdCard() {
|
||||
auto* configuration = new tt::hal::SpiSdCard::Config(
|
||||
TDECK_SDCARD_SPI_FREQUENCY,
|
||||
TDECK_SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCard::MountBehaviourAtBoot,
|
||||
SdCard::MountBehaviour::AtBoot,
|
||||
tt::lvgl::getLvglSyncLockable(),
|
||||
{
|
||||
TDECK_RADIO_PIN_CS,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <intr_types.h>
|
||||
#include <esp_intr_types.h>
|
||||
#include "Log.h"
|
||||
#include "hal/Core2DisplayConstants.h"
|
||||
#include "axp192/axp192.h"
|
||||
@@ -16,11 +16,11 @@
|
||||
axp192_t axpDevice;
|
||||
|
||||
static int32_t axpI2cRead(TT_UNUSED void* handle, uint8_t address, uint8_t reg, uint8_t* buffer, uint16_t size) {
|
||||
return tt::hal::i2c::masterRead(I2C_NUM_0, address, reg, buffer, size, 50 / portTICK_PERIOD_MS);
|
||||
return tt::hal::i2c::masterReadRegister(I2C_NUM_0, address, reg, buffer, size, 50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static int32_t axpI2cWrite(TT_UNUSED void* handle, uint8_t address, uint8_t reg, const uint8_t* buffer, uint16_t size) {
|
||||
return tt::hal::i2c::masterWrite(I2C_NUM_0, address, reg, buffer, size, 50 / portTICK_PERIOD_MS);
|
||||
return tt::hal::i2c::masterWriteRegister(I2C_NUM_0, address, reg, buffer, size, 50 / portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
static bool initSpi2() {
|
||||
@@ -37,7 +37,7 @@ static bool initSpi2() {
|
||||
.data7_io_num = GPIO_NUM_NC,
|
||||
.max_transfer_sz = CORE2_LCD_DRAW_BUFFER_SIZE,
|
||||
.flags = 0,
|
||||
.isr_cpu_id = INTR_CPU_ID_AUTO,
|
||||
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO,
|
||||
.intr_flags = 0
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ extern const tt::hal::Configuration m5stack_core2 = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = false, // Might be set to try after trying out what it does AXP and screen
|
||||
.hasMutableConfiguration = false,
|
||||
.config = (i2c_config_t) {
|
||||
@@ -33,7 +33,7 @@ extern const tt::hal::Configuration m5stack_core2 = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "External", // (Grove)
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
|
||||
@@ -102,7 +102,7 @@ bool Core2Display::start() {
|
||||
}
|
||||
|
||||
bool Core2Display::stop() {
|
||||
tt_assert(displayHandle != nullptr);
|
||||
assert(displayHandle != nullptr);
|
||||
|
||||
lvgl_port_remove_disp(displayHandle);
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ extern axp192_t axpDevice;
|
||||
|
||||
bool Core2Power::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE:
|
||||
case CHARGE_LEVEL:
|
||||
case IS_CHARGING:
|
||||
case MetricType::BatteryVoltage:
|
||||
case MetricType::ChargeLevel:
|
||||
case MetricType::IsCharging:
|
||||
return true;
|
||||
case CURRENT:
|
||||
case MetricType::Current:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ bool Core2Power::supportsMetric(MetricType type) const {
|
||||
|
||||
bool Core2Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE: {
|
||||
case MetricType::BatteryVoltage: {
|
||||
float voltage;
|
||||
if (axp192_read(&axpDevice, AXP192_BATTERY_VOLTAGE, &voltage) == ESP_OK) {
|
||||
data.valueAsUint32 = (uint32_t)TT_MAX((voltage * 1000.f), 0.0f);
|
||||
@@ -30,7 +30,7 @@ bool Core2Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case CHARGE_LEVEL: {
|
||||
case MetricType::ChargeLevel: {
|
||||
float vbat, charge_current;
|
||||
if (
|
||||
axp192_read(&axpDevice, AXP192_BATTERY_VOLTAGE, &vbat) == ESP_OK &&
|
||||
@@ -51,7 +51,7 @@ bool Core2Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case IS_CHARGING: {
|
||||
case MetricType::IsCharging: {
|
||||
float charge_current;
|
||||
if (axp192_read(&axpDevice, AXP192_CHARGE_CURRENT, &charge_current) == ESP_OK) {
|
||||
data.valueAsBool = charge_current > 0.001f;
|
||||
@@ -60,7 +60,7 @@ bool Core2Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case CURRENT: {
|
||||
case MetricType::Current: {
|
||||
float charge_current, discharge_current;
|
||||
if (
|
||||
axp192_read(&axpDevice, AXP192_CHARGE_CURRENT, &charge_current) == ESP_OK &&
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
|
||||
#include <esp_vfs_fat.h>
|
||||
|
||||
#define CORE2_SDCARD_SPI_FREQUENCY 800000U
|
||||
#define CORE2_SDCARD_PIN_CS GPIO_NUM_4
|
||||
#define CORE2_LCD_PIN_CS GPIO_NUM_5
|
||||
|
||||
std::shared_ptr<SdCard> createSdCard() {
|
||||
auto* configuration = new tt::hal::SpiSdCard::Config(
|
||||
CORE2_SDCARD_SPI_FREQUENCY,
|
||||
CORE2_SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCard::MountBehaviourAtBoot,
|
||||
SdCard::MountBehaviour::AtBoot,
|
||||
tt::lvgl::getLvglSyncLockable(),
|
||||
{
|
||||
CORE2_LCD_PIN_CS
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "Source" "Source/hal" "Source/Axp2101"
|
||||
SRC_DIRS "Source" "Source/hal" "Source/Axp2101" "Source/Aw9523"
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port esp_lcd esp_lcd_ili9341 esp_lcd_touch_ft5x06 driver vfs fatfs
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "Aw9523.h"
|
||||
|
||||
#define AW9523_REGISTER_P0 0x02
|
||||
#define AW9523_REGISTER_P1 0x03
|
||||
|
||||
bool Aw9523::readP0(uint8_t& output) const {
|
||||
return readRegister8(AW9523_REGISTER_P0, output);
|
||||
}
|
||||
|
||||
bool Aw9523::readP1(uint8_t& output) const {
|
||||
return readRegister8(AW9523_REGISTER_P1, output);
|
||||
}
|
||||
|
||||
bool Aw9523::writeP0(uint8_t value) const {
|
||||
return writeRegister8(AW9523_REGISTER_P0, value);
|
||||
}
|
||||
|
||||
bool Aw9523::writeP1(uint8_t value) const {
|
||||
return writeRegister8(AW9523_REGISTER_P1, value);
|
||||
}
|
||||
|
||||
bool Aw9523::bitOnP1(uint8_t bitmask) const {
|
||||
return bitOn(AW9523_REGISTER_P1, bitmask);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/i2c/I2cDevice.h"
|
||||
|
||||
#define AW9523_ADDRESS 0x58
|
||||
|
||||
class Aw9523 : I2cDevice {
|
||||
|
||||
public:
|
||||
|
||||
explicit Aw9523(i2c_port_t port) : I2cDevice(port, AW9523_ADDRESS) {}
|
||||
|
||||
bool readP0(uint8_t& output) const;
|
||||
bool readP1(uint8_t& output) const;
|
||||
|
||||
bool writeP0(uint8_t value) const;
|
||||
bool writeP1(uint8_t value) const;
|
||||
|
||||
bool bitOnP1(uint8_t bitmask) const;
|
||||
};
|
||||
@@ -1,44 +1,6 @@
|
||||
#include "Axp2101.h"
|
||||
#include "Log.h"
|
||||
|
||||
bool Axp2101::readRegister12(uint8_t reg, float& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterRead(port, DEFAULT_ADDRESS, reg, data, 2, DEFAULT_TIMEOUT) == ESP_OK) {
|
||||
out = (data[0] & 0x0F) << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Axp2101::readRegister14(uint8_t reg, float& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterRead(port, DEFAULT_ADDRESS, reg, data, 2, DEFAULT_TIMEOUT) == ESP_OK) {
|
||||
out = (data[0] & 0x3F) << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Axp2101::readRegister16(uint8_t reg, uint16_t& out) const {
|
||||
std::uint8_t data[2] = {0};
|
||||
if (tt::hal::i2c::masterRead(port, DEFAULT_ADDRESS, reg, data, 2, DEFAULT_TIMEOUT) == ESP_OK) {
|
||||
out = data[0] << 8 | data[1];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Axp2101::readRegister8(uint8_t reg, uint8_t& result) const {
|
||||
return tt::hal::i2c::masterWriteRead(port, DEFAULT_ADDRESS, ®, 1, &result, 1, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
bool Axp2101::writeRegister8(uint8_t reg, uint8_t value) const {
|
||||
return tt::hal::i2c::masterWrite(port, DEFAULT_ADDRESS, reg, &value, 1, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
bool Axp2101::getBatteryVoltage(float& vbatMillis) const {
|
||||
return readRegister14(0x34, vbatMillis);
|
||||
}
|
||||
@@ -72,3 +34,26 @@ bool Axp2101::setChargingEnabled(bool enabled) const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Axp2101::isVBus() const {
|
||||
uint8_t value;
|
||||
return readRegister8(0x00, value) && (value & 0x20);
|
||||
}
|
||||
|
||||
bool Axp2101::getVBusVoltage(float& out) const {
|
||||
if (!isVBus()) {
|
||||
return false;
|
||||
} else {
|
||||
float vbus;
|
||||
if (readRegister14(0x38, vbus) && vbus < 16375) {
|
||||
out = vbus / 1000.0f;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Axp2101::setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const {
|
||||
return tt::hal::i2c::masterWriteRegisterArray(port, address, bytePairs, bytePairsSize, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/i2c/I2c.h"
|
||||
#include "hal/i2c/I2cDevice.h"
|
||||
|
||||
#define AXP2101_ADDRESS 0x34
|
||||
|
||||
/**
|
||||
* References:
|
||||
* - https://github.com/m5stack/M5Unified/blob/master/src/utility/AXP2101_Class.cpp
|
||||
* - http://file.whycan.com/files/members/6736/AXP2101_Datasheet_V1.0_en_3832.pdf
|
||||
*/
|
||||
class Axp2101 {
|
||||
|
||||
i2c_port_t port;
|
||||
|
||||
static constexpr uint8_t DEFAULT_ADDRESS = 0x34;
|
||||
static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS;
|
||||
|
||||
bool readRegister8(uint8_t reg, uint8_t& result) const;
|
||||
bool writeRegister8(uint8_t reg, uint8_t value) const;
|
||||
bool readRegister12(uint8_t reg, float& out) const;
|
||||
bool readRegister14(uint8_t reg, float& out) const;
|
||||
bool readRegister16(uint8_t reg, uint16_t& out) const;
|
||||
class Axp2101 : I2cDevice {
|
||||
|
||||
public:
|
||||
|
||||
@@ -28,10 +19,15 @@ public:
|
||||
CHARGE_STATUS_STANDBY = 0b00
|
||||
};
|
||||
|
||||
Axp2101(i2c_port_t port) : port(port) {}
|
||||
explicit Axp2101(i2c_port_t port) : I2cDevice(port, AXP2101_ADDRESS) {}
|
||||
|
||||
bool setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const;
|
||||
|
||||
bool getBatteryVoltage(float& vbatMillis) const;
|
||||
bool getChargeStatus(ChargeStatus& status) const;
|
||||
bool isChargingEnabled(bool& enabled) const;
|
||||
bool setChargingEnabled(bool enabled) const;
|
||||
|
||||
bool isVBus() const;
|
||||
bool getVBusVoltage(float& out) const;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/spi_master.h>
|
||||
#include <intr_types.h>
|
||||
#include <esp_intr_types.h>
|
||||
#include "Log.h"
|
||||
#include "hal/CoreS3DisplayConstants.h"
|
||||
#include "hal/i2c/I2c.h"
|
||||
#include "CoreS3Constants.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "Axp2101/Axp2101.h"
|
||||
#include "Aw9523/Aw9523.h"
|
||||
|
||||
#define TAG "core2"
|
||||
|
||||
@@ -27,9 +28,10 @@ static bool initSpi3() {
|
||||
.data5_io_num = GPIO_NUM_NC,
|
||||
.data6_io_num = GPIO_NUM_NC,
|
||||
.data7_io_num = GPIO_NUM_NC,
|
||||
.data_io_default_level = false,
|
||||
.max_transfer_sz = CORES3_LCD_DRAW_BUFFER_SIZE,
|
||||
.flags = 0,
|
||||
.isr_cpu_id = INTR_CPU_ID_AUTO,
|
||||
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO,
|
||||
.intr_flags = 0
|
||||
};
|
||||
|
||||
@@ -43,62 +45,141 @@ static bool initSpi3() {
|
||||
|
||||
/**
|
||||
* For details see https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_core_s3/m5stack_core_s3.c
|
||||
* and schematic: https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/K128%20CoreS3/Sch_M5_CoreS3_v1.0.pdf
|
||||
*/
|
||||
bool initGpioExpander() {
|
||||
TT_LOG_I(TAG, "Init AW9523 GPIO expander");
|
||||
uint8_t aw9523_P0 = 0b10;
|
||||
uint8_t aw9523_P1 = 0b10100000;
|
||||
TT_LOG_I(TAG, "AW9523 init");
|
||||
|
||||
// Enable LCD
|
||||
aw9523_P1 |= (1 << 1);
|
||||
/**
|
||||
* P0 pins:
|
||||
* 0: Touch reset
|
||||
* 1: Bus out enable
|
||||
* 2: AW88298 reset (I2S)
|
||||
* 3: ES7210 interrupt (Audio ADC)
|
||||
* 4: SD Card SW(itch on?)
|
||||
* 5: USB OTG enable
|
||||
* 6: /
|
||||
* 7: /
|
||||
*/
|
||||
|
||||
/**
|
||||
* P1 pins:
|
||||
* 0: Cam reset
|
||||
* 1: LCD reset
|
||||
* 2: Touch interrupt
|
||||
* 3: AW88298 interrupt (I2S)
|
||||
* 4: /
|
||||
* 5: /
|
||||
* 6: /
|
||||
* 7: Boost enable
|
||||
*/
|
||||
|
||||
uint8_t p0_state = 0U;
|
||||
uint8_t p1_state = 0U;
|
||||
|
||||
// Enable touch
|
||||
aw9523_P0 |= (1);
|
||||
|
||||
p0_state |= (1U);
|
||||
// Bus out enable
|
||||
p0_state |= (1U << 1U);
|
||||
// I2S
|
||||
p0_state |= (1U << 2U);
|
||||
// SD card
|
||||
aw9523_P0 |= (1 << 4);
|
||||
p0_state |= (1U << 4U);
|
||||
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AW9523_ADDRESS, 0x02, &aw9523_P0, 1, 1000)) {
|
||||
TT_LOG_E(TAG, "Failed to enable LCD");
|
||||
// Enable LCD
|
||||
p1_state |= (1U << 1U);
|
||||
// Boost enable
|
||||
p1_state |= (1U << 7U);
|
||||
|
||||
Aw9523 aw(I2C_NUM_0);
|
||||
|
||||
if (!aw.writeP0(p0_state)) {
|
||||
TT_LOG_E(TAG, "AW9523: Failed to set P0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AW9523_ADDRESS, 0x03, &aw9523_P1, 1, 1000)) {
|
||||
TT_LOG_E(TAG, "Failed to enable touch");
|
||||
if (!aw.writeP1(p1_state)) {
|
||||
TT_LOG_E(TAG, "AW9523: Failed to set P1");
|
||||
return false;
|
||||
}
|
||||
|
||||
Axp2101 axp(I2C_NUM_0);
|
||||
if (axp.isVBus()) {
|
||||
float voltage = 0.0f;
|
||||
axp.getVBusVoltage(voltage);
|
||||
TT_LOG_I(TAG, "AXP2101: VBus at %.2f", voltage);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "AXP2101: VBus disabled");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initPowerControl() {
|
||||
TT_LOG_I(TAG, "Init power control");
|
||||
TT_LOG_I(TAG, "Init power control (AXP2101)");
|
||||
|
||||
uint8_t sdcard_3v3 = 0b00011100;
|
||||
// TODO: Refactor to use Axp2101 class with https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/AXP2101_Class.cpp#L62
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AXP2101_ADDRESS, 0x95, &sdcard_3v3, 1, 1000)) {
|
||||
TT_LOG_E(TAG, "Failed to enable SD card");
|
||||
Aw9523 aw(I2C_NUM_0);
|
||||
// Source: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/Power_Class.cpp#L61
|
||||
aw.bitOnP1(0b10000000); // SY7088 boost enable
|
||||
|
||||
/** AXP2101 usage
|
||||
Source: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/README.md?plain=1#L223
|
||||
| |M5Stack<BR>CoreS3<BR>CoreS3SE| |
|
||||
|:---------:|:-----------------:|:---------:|
|
||||
| ALDO1 |VDD 1v8 | ALDO1 |
|
||||
| ALDO2 |VDDA 3v3 | ALDO2 |
|
||||
| ALDO3 |CAM 3v3 | ALDO3 |
|
||||
| ALDO4 |TF 3v3 | ALDO4 |
|
||||
| BLDO1 |AVDD | BLDO1 |
|
||||
| BLDO2 |DVDD | BLDO2 |
|
||||
| DLDO1/DC1 |LCD BL | DLDO1/DC1 |
|
||||
| DLDO2/DC2 | --- | DLDO2/DC2 |
|
||||
| BACKUP |RTC BAT | BACKUP |
|
||||
*/
|
||||
|
||||
/**
|
||||
* 0x92 = ALD01
|
||||
* 0x93 = ALD02
|
||||
* 0x94 = ALD03
|
||||
* 0x95 = ALD04
|
||||
* 0x96 = BLD01
|
||||
* 0x97 = BLD02
|
||||
*
|
||||
* DCDC1 : 0.7-3.5V, 25mV/step 1200mA
|
||||
* DCDC2 : 0.7-2.275V,25mV/step 1600mA
|
||||
* DCDC3 : 0.7-3.5V, 25mV/step 700mA
|
||||
|
||||
* LDOio0: 1.8-3.3V, 100mV/step 50mA
|
||||
* LDO1 : 30mA always on
|
||||
* LDO2 : 1.8-3.3V, 100mV/step 200mA
|
||||
* LDO3 : 1.8-3.3V, 100mV/step 200mA
|
||||
*/
|
||||
// Source: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/Power_Class.cpp#L64
|
||||
static constexpr uint8_t reg_data_array[] = {
|
||||
0x90U, 0xBFU, // LDOS ON/OFF control 0 (backlight)
|
||||
0x92U, 18U -5U, // ALDO1 set to 1.8v // for AW88298
|
||||
0x93U, 33U -5U, // ALDO2 set to 3.3v // for ES7210
|
||||
0x94U, 33U -5U, // ALDO3 set to 3.3v // for camera
|
||||
0x95U, 33U -5U, // ALDO3 set to 3.3v // for TF card slot
|
||||
0x27, 0x00, // PowerKey Hold=1sec / PowerOff=4sec
|
||||
0x69, 0x11, // CHGLED setting
|
||||
0x10, 0x30, // PMU common config
|
||||
0x30, 0x0F // ADC enabled (for voltage measurement)
|
||||
};
|
||||
|
||||
Axp2101 axp(I2C_NUM_0);
|
||||
if (axp.setRegisters((uint8_t*)reg_data_array, sizeof(reg_data_array))) {
|
||||
TT_LOG_I(TAG, "AXP2101 initialized with %d registers", sizeof(reg_data_array) / 2);
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_E(TAG, "AXP2101: Failed to set registers");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Refactor to use Axp2102 class with https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/AXP2101_Class.cpp#L42
|
||||
uint8_t axp_dld01_enable = 0xBF; // For backlight
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AXP2101_ADDRESS, 0x90, &axp_dld01_enable, 1, 1000)) {
|
||||
TT_LOG_E(TAG, "Failed to enable display backlight");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Refactor to use Axp2101 class with https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/AXP2101_Class.cpp#L62
|
||||
uint8_t axp_dld01_voltage = 0b00011000; // For backlight
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AXP2101_ADDRESS, 0x99, &axp_dld01_voltage, 1, 1000)) {
|
||||
TT_LOG_E(TAG, "Failed to set display backlight voltage");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initBoot() {
|
||||
TT_LOG_I(TAG, "initBoot");
|
||||
return initPowerControl() && initGpioExpander() && initSpi3();
|
||||
TT_LOG_I(TAG, "initBoot()");
|
||||
return initPowerControl() &&
|
||||
initGpioExpander() &&
|
||||
initSpi3();
|
||||
}
|
||||
@@ -15,7 +15,7 @@ const tt::hal::Configuration m5stack_cores3 = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = false,
|
||||
.hasMutableConfiguration = false,
|
||||
.config = (i2c_config_t) {
|
||||
@@ -33,7 +33,7 @@ const tt::hal::Configuration m5stack_cores3 = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "External", // Grove
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
|
||||
@@ -29,6 +29,8 @@ bool CoreS3Display::start() {
|
||||
.user_ctx = nullptr,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans = 0,
|
||||
.flags = {
|
||||
.dc_high_on_cmd = 0,
|
||||
.dc_low_on_data = 0,
|
||||
@@ -119,7 +121,7 @@ bool CoreS3Display::start() {
|
||||
}
|
||||
|
||||
bool CoreS3Display::stop() {
|
||||
tt_assert(displayHandle != nullptr);
|
||||
assert(displayHandle != nullptr);
|
||||
|
||||
lvgl_port_remove_disp(displayHandle);
|
||||
|
||||
@@ -175,7 +177,7 @@ void CoreS3Display::setGammaCurve(uint8_t index) {
|
||||
void CoreS3Display::setBacklightDuty(uint8_t backlightDuty) {
|
||||
const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100] - under 20 is too dark
|
||||
// TODO: Refactor to use Axp2102 class with https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/AXP2101_Class.cpp#L42
|
||||
if (!tt::hal::i2c::masterWrite(I2C_NUM_0, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000)) {
|
||||
if (!tt::hal::i2c::masterWriteRegister(I2C_NUM_0, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000)) { // Sets DLD01
|
||||
TT_LOG_E(TAG, "Failed to set display backlight voltage");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
bool CoreS3Power::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE:
|
||||
case IS_CHARGING:
|
||||
case CHARGE_LEVEL:
|
||||
case MetricType::BatteryVoltage:
|
||||
case MetricType::IsCharging:
|
||||
case MetricType::ChargeLevel:
|
||||
return true;
|
||||
case CURRENT:
|
||||
case MetricType::Current:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ bool CoreS3Power::supportsMetric(MetricType type) const {
|
||||
|
||||
bool CoreS3Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case BATTERY_VOLTAGE: {
|
||||
case MetricType::BatteryVoltage: {
|
||||
float milliVolt;
|
||||
if (axpDevice.getBatteryVoltage(milliVolt)) {
|
||||
data.valueAsUint32 = (uint32_t)milliVolt;
|
||||
@@ -27,7 +27,7 @@ bool CoreS3Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case CHARGE_LEVEL: {
|
||||
case MetricType::ChargeLevel: {
|
||||
float vbatMillis;
|
||||
if (axpDevice.getBatteryVoltage(vbatMillis)) {
|
||||
float vbat = vbatMillis / 1000.f;
|
||||
@@ -44,7 +44,7 @@ bool CoreS3Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case IS_CHARGING: {
|
||||
case MetricType::IsCharging: {
|
||||
Axp2101::ChargeStatus status;
|
||||
if (axpDevice.getChargeStatus(status)) {
|
||||
data.valueAsBool = (status == Axp2101::CHARGE_STATUS_CHARGING);
|
||||
@@ -53,7 +53,7 @@ bool CoreS3Power::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case CURRENT:
|
||||
case MetricType::Current:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,16 @@
|
||||
|
||||
#include <esp_vfs_fat.h>
|
||||
|
||||
#define CORES3_SDCARD_SPI_FREQUENCY 800000U
|
||||
#define CORES3_SDCARD_PIN_CS GPIO_NUM_4
|
||||
#define CORES3_LCD_PIN_CS GPIO_NUM_3
|
||||
|
||||
std::shared_ptr<SdCard> createSdCard() {
|
||||
auto* configuration = new tt::hal::SpiSdCard::Config(
|
||||
CORES3_SDCARD_SPI_FREQUENCY,
|
||||
CORES3_SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCard::MountBehaviourAtBoot,
|
||||
SdCard::MountBehaviour::AtBoot,
|
||||
tt::lvgl::getLvglSyncLockable(),
|
||||
{
|
||||
CORES3_LCD_PIN_CS
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
#define TAG "lvgl_task"
|
||||
|
||||
// Mutex for LVGL drawing
|
||||
static tt::Mutex lvgl_mutex(tt::Mutex::TypeRecursive);
|
||||
static tt::Mutex task_mutex(tt::Mutex::TypeRecursive);
|
||||
static tt::Mutex lvgl_mutex(tt::Mutex::Type::Recursive);
|
||||
static tt::Mutex task_mutex(tt::Mutex::Type::Recursive);
|
||||
|
||||
static uint32_t task_max_sleep_ms = 10;
|
||||
// Mutex for LVGL task state (to modify task_running state)
|
||||
@@ -19,12 +19,12 @@ static bool task_running = false;
|
||||
|
||||
static void lvgl_task(void* arg);
|
||||
|
||||
static bool task_lock(int timeout_ticks) {
|
||||
return task_mutex.acquire(timeout_ticks) == tt::TtStatusOk;
|
||||
static bool task_lock(TickType_t timeout) {
|
||||
return task_mutex.lock(timeout);
|
||||
}
|
||||
|
||||
static void task_unlock() {
|
||||
task_mutex.release();
|
||||
task_mutex.unlock();
|
||||
}
|
||||
|
||||
static void task_set_running(bool running) {
|
||||
@@ -41,15 +41,15 @@ bool lvgl_task_is_running() {
|
||||
}
|
||||
|
||||
static bool lvgl_lock(uint32_t timeoutMillis) {
|
||||
return lvgl_mutex.acquire(pdMS_TO_TICKS(timeoutMillis)) == tt::TtStatusOk;
|
||||
return lvgl_mutex.lock(pdMS_TO_TICKS(timeoutMillis));
|
||||
}
|
||||
|
||||
static void lvgl_unlock() {
|
||||
lvgl_mutex.release();
|
||||
lvgl_mutex.unlock();
|
||||
}
|
||||
|
||||
void lvgl_task_interrupt() {
|
||||
tt_check(task_lock(tt::TtWaitForever));
|
||||
tt_check(task_lock(portMAX_DELAY));
|
||||
task_set_running(false); // interrupt task with boolean as flag
|
||||
task_unlock();
|
||||
}
|
||||
@@ -69,7 +69,7 @@ void lvgl_task_start() {
|
||||
nullptr
|
||||
);
|
||||
|
||||
tt_assert(task_result == pdTRUE);
|
||||
assert(task_result == pdTRUE);
|
||||
}
|
||||
|
||||
lv_disp_t* displayHandle = nullptr;
|
||||
|
||||
@@ -33,7 +33,7 @@ void freertosMain() {
|
||||
nullptr
|
||||
);
|
||||
|
||||
tt_assert(task_result == pdTRUE);
|
||||
assert(task_result == pdTRUE);
|
||||
|
||||
// Blocks forever
|
||||
vTaskStartScheduler();
|
||||
|
||||
@@ -36,7 +36,7 @@ extern const tt::hal::Configuration hardware = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = false,
|
||||
.hasMutableConfiguration = false,
|
||||
.config = (i2c_config_t) {
|
||||
@@ -54,7 +54,7 @@ extern const tt::hal::Configuration hardware = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "External",
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitByTactility,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
|
||||
bool SimulatorPower::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case IS_CHARGING:
|
||||
case CURRENT:
|
||||
case BATTERY_VOLTAGE:
|
||||
case CHARGE_LEVEL:
|
||||
case MetricType::IsCharging:
|
||||
case MetricType::Current:
|
||||
case MetricType::BatteryVoltage:
|
||||
case MetricType::ChargeLevel:
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,16 +16,16 @@ bool SimulatorPower::supportsMetric(MetricType type) const {
|
||||
|
||||
bool SimulatorPower::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case IS_CHARGING:
|
||||
case MetricType::IsCharging:
|
||||
data.valueAsBool = true;
|
||||
return true;
|
||||
case CURRENT:
|
||||
case MetricType::Current:
|
||||
data.valueAsInt32 = 42;
|
||||
return true;
|
||||
case BATTERY_VOLTAGE:
|
||||
case MetricType::BatteryVoltage:
|
||||
data.valueAsUint32 = 4032;
|
||||
return true;
|
||||
case CHARGE_LEVEL:
|
||||
case MetricType::ChargeLevel:
|
||||
data.valueAsUint8 = 100;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ class SimulatorSdCard : public SdCard {
|
||||
private:
|
||||
State state;
|
||||
public:
|
||||
SimulatorSdCard() : SdCard(MountBehaviourAtBoot), state(StateUnmounted) {}
|
||||
SimulatorSdCard() : SdCard(MountBehaviour::AtBoot), state(State::Unmounted) {}
|
||||
|
||||
bool mount(const char* mountPath) override {
|
||||
state = StateMounted;
|
||||
state = State::Mounted;
|
||||
return true;
|
||||
}
|
||||
bool unmount() override {
|
||||
state = StateUnmounted;
|
||||
state = State::Unmounted;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(
|
||||
SRC_DIRS "Source" "Source/hal" "Source/hx8357" "Source/bq24295"
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port esp_io_expander esp_io_expander_tca95xx_16bit esp_lcd_touch esp_lcd_touch_xpt2046
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "TactilityCore.h"
|
||||
#include "hal/UnPhoneDisplayConstants.h"
|
||||
#include "hx8357/disp_spi.h"
|
||||
#include <driver/spi_common.h>
|
||||
#include <soc/gpio_num.h>
|
||||
#include <lvgl.h>
|
||||
|
||||
#define TAG "unphone"
|
||||
|
||||
// SPI
|
||||
#define UNPHONE_SPI_HOST SPI2_HOST
|
||||
#define UNPHONE_SPI_PIN_SCLK GPIO_NUM_39
|
||||
#define UNPHONE_SPI_PIN_MOSI GPIO_NUM_40
|
||||
#define UNPHONE_SPI_PIN_MISO GPIO_NUM_41
|
||||
#define UNPHONE_SPI_TRANSFER_SIZE_LIMIT (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_SPI_TRANSFER_HEIGHT * LV_COLOR_DEPTH / 8)
|
||||
|
||||
static bool initSpi() {
|
||||
TT_LOG_I(TAG, LOG_MESSAGE_SPI_INIT_START_FMT, UNPHONE_SPI_HOST);
|
||||
|
||||
spi_bus_config_t bus_config = {
|
||||
.mosi_io_num = UNPHONE_SPI_PIN_MOSI,
|
||||
.miso_io_num = UNPHONE_SPI_PIN_MISO,
|
||||
.sclk_io_num = UNPHONE_SPI_PIN_SCLK,
|
||||
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported
|
||||
.data4_io_num = 0,
|
||||
.data5_io_num = 0,
|
||||
.data6_io_num = 0,
|
||||
.data7_io_num = 0,
|
||||
.data_io_default_level = false,
|
||||
.max_transfer_sz = UNPHONE_SPI_TRANSFER_SIZE_LIMIT,
|
||||
.flags = 0,
|
||||
.isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO,
|
||||
.intr_flags = 0
|
||||
};
|
||||
|
||||
if (spi_bus_initialize(UNPHONE_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO) != ESP_OK) {
|
||||
TT_LOG_E(TAG, LOG_MESSAGE_SPI_INIT_FAILED_FMT, UNPHONE_SPI_HOST);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unPhoneInitHardware() {
|
||||
return initSpi();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "lvgl/LvglSync.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "hal/UnPhoneDisplay.h"
|
||||
|
||||
#define TAG "unphone_lvgl"
|
||||
|
||||
// LVGL
|
||||
// The minimum task stack seems to be about 3500, but that crashes the wifi app in some scenarios
|
||||
// At 8192, it sometimes crashes when wifi-auto enables and is busy connecting and then you open WifiManage
|
||||
#define UNPHONE_LVGL_TASK_STACK_DEPTH 9216
|
||||
|
||||
bool unPhoneInitLvgl() {
|
||||
static lv_disp_t* display = nullptr;
|
||||
const lvgl_port_cfg_t lvgl_cfg = {
|
||||
.task_priority = static_cast<UBaseType_t>(tt::THREAD_PRIORITY_RENDER),
|
||||
.task_stack = UNPHONE_LVGL_TASK_STACK_DEPTH,
|
||||
.task_affinity = -1, // core pinning
|
||||
.task_max_sleep_ms = 500,
|
||||
.timer_period_ms = 5
|
||||
};
|
||||
|
||||
TT_LOG_D(TAG, "LVGL port init");
|
||||
if (lvgl_port_init(&lvgl_cfg) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "LVGL port init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
tt::lvgl::syncSet(&lvgl_port_lock, &lvgl_port_unlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "Preferences.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "UnPhoneFeatures.h"
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#define TAG "unphone"
|
||||
|
||||
extern UnPhoneFeatures unPhoneFeatures;
|
||||
|
||||
static std::unique_ptr<tt::Thread> powerThread;
|
||||
|
||||
static const char* bootCountKey = "boot_count";
|
||||
static const char* powerOffCountKey = "power_off_count";
|
||||
static const char* powerSleepKey = "power_sleep_key";
|
||||
|
||||
class DeviceStats {
|
||||
|
||||
private:
|
||||
|
||||
tt::Preferences preferences = tt::Preferences("unphone");
|
||||
|
||||
int32_t getValue(const char* key) {
|
||||
int32_t value = 0;
|
||||
preferences.optInt32(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void setValue(const char* key, int32_t value) {
|
||||
preferences.putInt32(key, value);
|
||||
}
|
||||
|
||||
void increaseValue(const char* key) {
|
||||
int32_t new_value = getValue(key) + 1;
|
||||
setValue(key, new_value);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void notifyBootStart() {
|
||||
increaseValue(bootCountKey);
|
||||
}
|
||||
|
||||
void notifyPowerOff() {
|
||||
increaseValue(powerOffCountKey);
|
||||
}
|
||||
|
||||
void notifyPowerSleep() {
|
||||
increaseValue(powerSleepKey);
|
||||
}
|
||||
|
||||
void printInfo() {
|
||||
TT_LOG_I("TAG", "Device stats:");
|
||||
TT_LOG_I("TAG", " boot: %ld", getValue(bootCountKey));
|
||||
TT_LOG_I("TAG", " power off: %ld", getValue(powerOffCountKey));
|
||||
TT_LOG_I("TAG", " power sleep: %ld", getValue(powerSleepKey));
|
||||
}
|
||||
};
|
||||
|
||||
DeviceStats bootStats;
|
||||
|
||||
enum class PowerState {
|
||||
Initial,
|
||||
On,
|
||||
Off
|
||||
};
|
||||
|
||||
#define DEBUG_POWER_STATES false
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
/** Helper method to use the buzzer to signal the different power stages */
|
||||
static void powerInfoBuzz(uint8_t count) {
|
||||
if (DEBUG_POWER_STATES) {
|
||||
uint8_t index = 0;
|
||||
while (index < count) {
|
||||
unPhoneFeatures.setVibePower(true);
|
||||
tt::kernel::delayMillis(50);
|
||||
unPhoneFeatures.setVibePower(false);
|
||||
|
||||
index++;
|
||||
|
||||
if (index < count) {
|
||||
tt::kernel::delayMillis(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void updatePowerSwitch() {
|
||||
static PowerState last_state = PowerState::Initial;
|
||||
|
||||
if (!unPhoneFeatures.isPowerSwitchOn()) {
|
||||
if (last_state != PowerState::Off) {
|
||||
last_state = PowerState::Off;
|
||||
TT_LOG_W(TAG, "Power off");
|
||||
}
|
||||
|
||||
if (!unPhoneFeatures.isUsbPowerConnected()) { // and usb unplugged we go into shipping mode
|
||||
TT_LOG_W(TAG, "Shipping mode until USB connects");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
unPhoneFeatures.setExpanderPower(true);
|
||||
powerInfoBuzz(3);
|
||||
unPhoneFeatures.setExpanderPower(false);
|
||||
#endif
|
||||
|
||||
unPhoneFeatures.turnPeripheralsOff();
|
||||
|
||||
bootStats.notifyPowerOff();
|
||||
|
||||
unPhoneFeatures.setShipping(true); // tell BM to stop supplying power until USB connects
|
||||
} else { // When power switch is off, but USB is plugged in, we wait (deep sleep) until USB is unplugged.
|
||||
TT_LOG_W(TAG, "Waiting for USB disconnect to power off");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
powerInfoBuzz(2);
|
||||
#endif
|
||||
|
||||
unPhoneFeatures.turnPeripheralsOff();
|
||||
|
||||
bootStats.notifyPowerSleep();
|
||||
|
||||
// Deep sleep for 1 minute, then awaken to check power state again
|
||||
// GPIO trigger from power switch also awakens the device
|
||||
unPhoneFeatures.wakeOnPowerSwitch();
|
||||
esp_sleep_enable_timer_wakeup(60000000);
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
} else {
|
||||
if (last_state != PowerState::On) {
|
||||
last_state = PowerState::On;
|
||||
TT_LOG_W(TAG, "Power on");
|
||||
|
||||
#if DEBUG_POWER_STATES
|
||||
powerInfoBuzz(1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t powerSwitchMain(void*) { // check power switch every 10th of sec
|
||||
while (true) {
|
||||
updatePowerSwitch();
|
||||
tt::kernel::delayMillis(200);
|
||||
}
|
||||
}
|
||||
|
||||
static void startPowerSwitchThread() {
|
||||
powerThread = std::make_unique<tt::Thread>(
|
||||
"unphone_power_switch",
|
||||
4096,
|
||||
powerSwitchMain,
|
||||
nullptr
|
||||
);
|
||||
powerThread->start();
|
||||
}
|
||||
|
||||
static bool unPhonePowerOn() {
|
||||
// Print early, in case of early crash (info will be from previous boot)
|
||||
bootStats.printInfo();
|
||||
|
||||
bootStats.notifyBootStart();
|
||||
|
||||
if (!unPhoneFeatures.init()) {
|
||||
TT_LOG_E(TAG, "UnPhoneFeatures init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
unPhoneFeatures.printInfo();
|
||||
|
||||
unPhoneFeatures.setBacklightPower(false);
|
||||
unPhoneFeatures.setVibePower(false);
|
||||
unPhoneFeatures.setIrPower(false);
|
||||
unPhoneFeatures.setExpanderPower(false);
|
||||
|
||||
// Turn off the device if power switch is on off state,
|
||||
// instead of waiting for the Thread to start and continue booting
|
||||
updatePowerSwitch();
|
||||
startPowerSwitchThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unPhoneInitPower() {
|
||||
ESP_LOGI(TAG, LOG_MESSAGE_POWER_ON_START);
|
||||
|
||||
if (!unPhonePowerOn()) {
|
||||
TT_LOG_E(TAG, LOG_MESSAGE_POWER_ON_FAILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "UnPhoneFeatures.h"
|
||||
#include "hal/Configuration.h"
|
||||
#include "hal/UnPhoneDisplay.h"
|
||||
#include "hal/UnPhonePower.h"
|
||||
#include "hal/UnPhoneSdCard.h"
|
||||
|
||||
bool unPhoneInitPower();
|
||||
bool unPhoneInitHardware();
|
||||
bool unPhoneInitLvgl();
|
||||
|
||||
// Shared object, used in PowerOn and UnPhoneDisplay
|
||||
UnPhoneFeatures unPhoneFeatures;
|
||||
|
||||
extern const tt::hal::Configuration unPhone = {
|
||||
.initBoot = unPhoneInitPower,
|
||||
.initHardware = unPhoneInitHardware,
|
||||
.initLvgl = unPhoneInitLvgl,
|
||||
.createDisplay = createDisplay,
|
||||
.sdcard = createUnPhoneSdCard(),
|
||||
.power = unPhoneGetPower,
|
||||
.i2c = {
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Internal",
|
||||
.port = I2C_NUM_0,
|
||||
.initMode = tt::hal::i2c::InitMode::ByTactility,
|
||||
.canReinit = false,
|
||||
.hasMutableConfiguration = false,
|
||||
.config = (i2c_config_t) {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = GPIO_NUM_3,
|
||||
.scl_io_num = GPIO_NUM_4,
|
||||
.sda_pullup_en = true,
|
||||
.scl_pullup_en = true,
|
||||
.master = {
|
||||
.clk_speed = 400000
|
||||
},
|
||||
.clk_flags = 0
|
||||
}
|
||||
},
|
||||
tt::hal::i2c::Configuration {
|
||||
.name = "Unused",
|
||||
.port = I2C_NUM_1,
|
||||
.initMode = tt::hal::i2c::InitMode::Disabled,
|
||||
.canReinit = true,
|
||||
.hasMutableConfiguration = true,
|
||||
.config = (i2c_config_t) {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = GPIO_NUM_NC,
|
||||
.scl_io_num = GPIO_NUM_NC,
|
||||
.sda_pullup_en = false,
|
||||
.scl_pullup_en = false,
|
||||
.master = {
|
||||
.clk_speed = 400000
|
||||
},
|
||||
.clk_flags = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <hal/Configuration.h>
|
||||
|
||||
extern const tt::hal::Configuration unPhone;
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "UnPhoneFeatures.h"
|
||||
#include "FreeRTOS-Kernel/include/FreeRTOS.h"
|
||||
#include "Log.h"
|
||||
#include "kernel/Kernel.h"
|
||||
#include "service/loader/Loader.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
namespace pin {
|
||||
static const gpio_num_t BUTTON1 = GPIO_NUM_45; // left button
|
||||
static const gpio_num_t BUTTON2 = GPIO_NUM_0; // middle button
|
||||
static const gpio_num_t BUTTON3 = GPIO_NUM_21; // right button
|
||||
static const gpio_num_t IR_LEDS = GPIO_NUM_12;
|
||||
static const gpio_num_t LED_RED = GPIO_NUM_13;
|
||||
static const gpio_num_t POWER_SWITCH = GPIO_NUM_18;
|
||||
} // namespace pin
|
||||
|
||||
namespace expanderpin {
|
||||
static const esp_io_expander_pin_num_t BACKLIGHT = IO_EXPANDER_PIN_NUM_2;
|
||||
static const esp_io_expander_pin_num_t EXPANDER_POWER = IO_EXPANDER_PIN_NUM_0; // enable exp brd if high
|
||||
static const esp_io_expander_pin_num_t LED_GREEN = IO_EXPANDER_PIN_NUM_9;
|
||||
static const esp_io_expander_pin_num_t LED_BLUE = IO_EXPANDER_PIN_NUM_13;
|
||||
static const esp_io_expander_pin_num_t USB_VSENSE = IO_EXPANDER_PIN_NUM_14;
|
||||
static const esp_io_expander_pin_num_t VIBE = IO_EXPANDER_PIN_NUM_7;
|
||||
} // namespace expanderpin
|
||||
|
||||
#define TAG "unhpone_features"
|
||||
|
||||
// TODO: Make part of a new type of UnPhoneFeatures data struct that holds all the thread-related data
|
||||
QueueHandle_t interruptQueue;
|
||||
|
||||
static void IRAM_ATTR navButtonInterruptHandler(void* args) {
|
||||
int pinNumber = (int)args;
|
||||
xQueueSendFromISR(interruptQueue, &pinNumber, NULL);
|
||||
}
|
||||
|
||||
static int32_t buttonHandlingThreadMain(void* context) {
|
||||
auto* interrupted = (bool*)context;
|
||||
int pinNumber;
|
||||
while (!*interrupted) {
|
||||
if (xQueueReceive(interruptQueue, &pinNumber, portMAX_DELAY)) {
|
||||
// The buttons might generate more than 1 click because of how they are built
|
||||
TT_LOG_I(TAG, "Pressed button %d", pinNumber);
|
||||
if (pinNumber == pin::BUTTON1) {
|
||||
tt::service::loader::stopApp();
|
||||
}
|
||||
|
||||
// Debounce all events for a short period of time
|
||||
// This is easier than keeping track when each button was last pressed
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(interruptQueue);
|
||||
tt::kernel::delayMillis(50);
|
||||
xQueueReset(interruptQueue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
UnPhoneFeatures::~UnPhoneFeatures() {
|
||||
if (buttonHandlingThread.getState() != tt::Thread::State::Stopped) {
|
||||
buttonHandlingThreadInterruptRequest = true;
|
||||
buttonHandlingThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initPowerSwitch() {
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = BIT64(pin::POWER_SWITCH),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_POSEDGE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Power pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rtc_gpio_pullup_en(pin::POWER_SWITCH) == ESP_OK &&
|
||||
rtc_gpio_pulldown_en(pin::POWER_SWITCH) == ESP_OK) {
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Failed to set RTC for power switch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initNavButtons() {
|
||||
if (!initGpioExpander()) {
|
||||
TT_LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
interruptQueue = xQueueCreate(4, sizeof(int));
|
||||
|
||||
buttonHandlingThread.setName("unphone_buttons");
|
||||
buttonHandlingThread.setPriority(tt::Thread::Priority::High);
|
||||
buttonHandlingThread.setStackSize(3072);
|
||||
buttonHandlingThread.setCallback(buttonHandlingThreadMain, &buttonHandlingThreadInterruptRequest);
|
||||
buttonHandlingThread.start();
|
||||
|
||||
uint64_t pin_mask =
|
||||
BIT64(pin::BUTTON1) |
|
||||
BIT64(pin::BUTTON2) |
|
||||
BIT64(pin::BUTTON3);
|
||||
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = pin_mask,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
/**
|
||||
* We have to listen to the button release (= positive signal).
|
||||
* If we listen to button press, the buttons might create more than 1 signal
|
||||
* when they are continuously pressed.
|
||||
*/
|
||||
.intr_type = GPIO_INTR_POSEDGE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Nav button pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
gpio_install_isr_service(0) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON1, navButtonInterruptHandler, (void*)pin::BUTTON1) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON2, navButtonInterruptHandler, (void*)pin::BUTTON2) != ESP_OK ||
|
||||
gpio_isr_handler_add(pin::BUTTON3, navButtonInterruptHandler, (void*)pin::BUTTON3) != ESP_OK
|
||||
) {
|
||||
TT_LOG_E(TAG, "Nav buttons ISR init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initOutputPins() {
|
||||
uint64_t output_pin_mask =
|
||||
BIT64(pin::IR_LEDS) |
|
||||
BIT64(pin::LED_RED);
|
||||
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = output_pin_mask,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
if (gpio_config(&config) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Output pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::initGpioExpander() {
|
||||
// ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110 corresponds with 0x26 from the docs at
|
||||
// https://gitlab.com/hamishcunningham/unphonelibrary/-/blob/main/unPhone.h?ref_type=heads#L206
|
||||
if (esp_io_expander_new_i2c_tca95xx_16bit(I2C_NUM_0, ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_110, &ioExpander) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "IO expander init failed");
|
||||
return false;
|
||||
}
|
||||
assert(ioExpander != nullptr);
|
||||
|
||||
// Output pins
|
||||
|
||||
/**
|
||||
* Important:
|
||||
* If you clear the pins too late, the display or vibration motor might briefly turn on.
|
||||
*/
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::BACKLIGHT, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::BACKLIGHT, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::EXPANDER_POWER, IO_EXPANDER_OUTPUT);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::LED_GREEN, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_GREEN, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::LED_BLUE, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_BLUE, 0);
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::VIBE, IO_EXPANDER_OUTPUT);
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::VIBE, 0);
|
||||
|
||||
// Input pins
|
||||
|
||||
esp_io_expander_set_dir(ioExpander, expanderpin::USB_VSENSE, IO_EXPANDER_INPUT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::init() {
|
||||
TT_LOG_I(TAG, "init");
|
||||
|
||||
if (!initGpioExpander()) {
|
||||
TT_LOG_E(TAG, "GPIO expander init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initNavButtons()) {
|
||||
TT_LOG_E(TAG, "Input pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initOutputPins()) {
|
||||
TT_LOG_E(TAG, "Output pin init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initPowerSwitch()) {
|
||||
TT_LOG_E(TAG, "Power button init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::printInfo() const {
|
||||
esp_io_expander_print_state(ioExpander);
|
||||
batteryManagement.printInfo();
|
||||
bool backlight_power;
|
||||
const char* backlight_power_state = getBacklightPower(backlight_power) && backlight_power ? "on" : "off";
|
||||
TT_LOG_I(TAG, "Backlight: %s", backlight_power_state);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setRgbLed(bool red, bool green, bool blue) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return gpio_set_level(pin::LED_RED, red ? 1U : 0U) == ESP_OK &&
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_GREEN, green ? 1U : 0U) == ESP_OK &&
|
||||
esp_io_expander_set_level(ioExpander, expanderpin::LED_BLUE, blue ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setBacklightPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::BACKLIGHT, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::getBacklightPower(bool& on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
uint32_t level_mask;
|
||||
if (esp_io_expander_get_level(ioExpander, expanderpin::BACKLIGHT, &level_mask) == ESP_OK) {
|
||||
on = level_mask != 0U;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setIrPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return gpio_set_level(pin::IR_LEDS, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setVibePower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::VIBE, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setExpanderPower(bool on) const {
|
||||
assert(ioExpander != nullptr);
|
||||
return esp_io_expander_set_level(ioExpander, expanderpin::EXPANDER_POWER, on ? 1U : 0U) == ESP_OK;
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::isPowerSwitchOn() const {
|
||||
return gpio_get_level(pin::POWER_SWITCH) > 0;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::turnPeripheralsOff() const {
|
||||
setExpanderPower(false);
|
||||
setBacklightPower(false);
|
||||
setIrPower(false);
|
||||
setRgbLed(false, false, false);
|
||||
setVibePower(false);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::setShipping(bool on) const {
|
||||
if (on) {
|
||||
TT_LOG_W(TAG, "setShipping: on");
|
||||
batteryManagement.setWatchDogTimer(Bq24295::WatchDogTimer::Disabled);
|
||||
batteryManagement.setBatFetOn(false);
|
||||
} else {
|
||||
TT_LOG_W(TAG, "setShipping: off");
|
||||
batteryManagement.setWatchDogTimer(Bq24295::WatchDogTimer::Enabled40s);
|
||||
batteryManagement.setBatFetOn(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPhoneFeatures::wakeOnPowerSwitch() const {
|
||||
esp_sleep_enable_ext0_wakeup(pin::POWER_SWITCH, 1);
|
||||
}
|
||||
|
||||
bool UnPhoneFeatures::isUsbPowerConnected() const {
|
||||
return batteryManagement.isUsbPowerConnected();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "Thread.h"
|
||||
#include "bq24295/Bq24295.h"
|
||||
#include <esp_io_expander_tca95xx_16bit.h>
|
||||
|
||||
/**
|
||||
* Easy access to GPIO pins
|
||||
*/
|
||||
class UnPhoneFeatures {
|
||||
|
||||
private:
|
||||
|
||||
esp_io_expander_handle_t ioExpander = nullptr;
|
||||
Bq24295 batteryManagement = Bq24295(I2C_NUM_0);
|
||||
tt::Thread buttonHandlingThread;
|
||||
bool buttonHandlingThreadInterruptRequest = false;
|
||||
|
||||
bool initNavButtons();
|
||||
static bool initOutputPins();
|
||||
static bool initPowerSwitch();
|
||||
bool initGpioExpander();
|
||||
|
||||
public:
|
||||
|
||||
UnPhoneFeatures() = default;
|
||||
~UnPhoneFeatures();
|
||||
|
||||
bool init();
|
||||
|
||||
bool setBacklightPower(bool on) const;
|
||||
bool getBacklightPower(bool& on) const;
|
||||
bool setIrPower(bool on) const;
|
||||
bool setVibePower(bool on) const;
|
||||
bool setExpanderPower(bool on) const;
|
||||
|
||||
bool isPowerSwitchOn() const;
|
||||
|
||||
void turnPeripheralsOff() const;
|
||||
|
||||
/** Battery management (BQ24295) will stop supplying power until USB connects */
|
||||
bool setShipping(bool on) const;
|
||||
|
||||
void wakeOnPowerSwitch() const;
|
||||
|
||||
bool isUsbPowerConnected() const;
|
||||
|
||||
bool setRgbLed(bool red, bool green, bool blue) const;
|
||||
|
||||
void printInfo() const;
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "Bq24295.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define TAG "bq24295"
|
||||
|
||||
/** Reference:
|
||||
* https://www.ti.com/lit/ds/symlink/bq24295.pdf
|
||||
* https://gitlab.com/hamishcunningham/unphonelibrary/-/blob/main/unPhone.h?ref_type=heads
|
||||
*/
|
||||
namespace registers {
|
||||
static const uint8_t CHARGE_TERMINATION = 0x05U; // Datasheet page 35: Charge end/timer cntrl
|
||||
static const uint8_t OPERATION_CONTROL = 0x07U; // Datasheet page 37: Misc operation control
|
||||
static const uint8_t STATUS = 0x08U; // Datasheet page 38: System status
|
||||
static const uint8_t VERSION = 0x0AU; // Datasheet page 38: Vendor/part/revision status
|
||||
} // namespace registers
|
||||
|
||||
bool Bq24295::readChargeTermination(uint8_t& out) const {
|
||||
return readRegister8(registers::CHARGE_TERMINATION, out);
|
||||
}
|
||||
|
||||
// region Watchdog
|
||||
bool Bq24295::getWatchDogTimer(WatchDogTimer& out) const {
|
||||
uint8_t value;
|
||||
if (readChargeTermination(value)) {
|
||||
uint8_t relevant_bits = value & (BIT(4) | BIT(5));
|
||||
switch (relevant_bits) {
|
||||
case 0b000000:
|
||||
out = WatchDogTimer::Disabled;
|
||||
return true;
|
||||
case 0b010000:
|
||||
out = WatchDogTimer::Enabled40s;
|
||||
return true;
|
||||
case 0b100000:
|
||||
out = WatchDogTimer::Enabled80s;
|
||||
return true;
|
||||
case 0b110000:
|
||||
out = WatchDogTimer::Enabled160s;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bq24295::setWatchDogTimer(WatchDogTimer in) const {
|
||||
uint8_t value;
|
||||
if (readChargeTermination(value)) {
|
||||
uint8_t bits_to_set = 0b00110000 & static_cast<uint8_t>(in);
|
||||
uint8_t value_cleared = value & 0b11001111;
|
||||
uint8_t to_set = bits_to_set & value_cleared;
|
||||
TT_LOG_I(TAG, "WatchDogTimer: %02x -> %02x", value, to_set);
|
||||
return writeRegister8(registers::CHARGE_TERMINATION, to_set);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// endregoin
|
||||
|
||||
// region Operation Control (REG07)
|
||||
|
||||
bool Bq24295::setBatFetOn(bool on) const {
|
||||
if (on) {
|
||||
// bit 5 low means bat fet is on
|
||||
return bitOff(registers::OPERATION_CONTROL, BIT(5));
|
||||
} else {
|
||||
// bit 5 high means bat fet is off
|
||||
return bitOn(registers::OPERATION_CONTROL, BIT(5));
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Other
|
||||
|
||||
bool Bq24295::getStatus(uint8_t& value) const {
|
||||
return readRegister8(registers::STATUS, value);
|
||||
}
|
||||
|
||||
bool Bq24295::isUsbPowerConnected() const {
|
||||
uint8_t status;
|
||||
if (getStatus(status)) {
|
||||
return (status & BIT(2)) != 0U;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bq24295::getVersion(uint8_t& value) const {
|
||||
return readRegister8(registers::VERSION, value);
|
||||
}
|
||||
|
||||
void Bq24295::printInfo() const {
|
||||
uint8_t version, status, charge_termination;
|
||||
if (getStatus(status) && getVersion(version) && readChargeTermination(charge_termination)) {
|
||||
TT_LOG_I(TAG, "Version %d, status %02x, charge termination %02x", version, status, charge_termination);
|
||||
} else {
|
||||
TT_LOG_E(TAG, "Failed to retrieve version and/or status");
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/i2c/I2cDevice.h"
|
||||
|
||||
#define BQ24295_ADDRESS 0x6BU
|
||||
|
||||
class Bq24295 : I2cDevice {
|
||||
|
||||
private:
|
||||
|
||||
bool readChargeTermination(uint8_t& out) const;
|
||||
|
||||
public:
|
||||
|
||||
enum class WatchDogTimer {
|
||||
Disabled = 0b000000,
|
||||
Enabled40s = 0b010000,
|
||||
Enabled80s = 0b100000,
|
||||
Enabled160s = 0b110000
|
||||
};
|
||||
|
||||
explicit Bq24295(i2c_port_t port) : I2cDevice(port, BQ24295_ADDRESS) {}
|
||||
|
||||
bool getWatchDogTimer(WatchDogTimer& out) const;
|
||||
bool setWatchDogTimer(WatchDogTimer in) const;
|
||||
|
||||
bool isUsbPowerConnected() const;
|
||||
|
||||
bool setBatFetOn(bool on) const;
|
||||
|
||||
bool getStatus(uint8_t& value) const;
|
||||
bool getVersion(uint8_t& value) const;
|
||||
|
||||
void printInfo() const;
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "UnPhoneDisplay.h"
|
||||
#include "UnPhoneDisplayConstants.h"
|
||||
#include "UnPhoneTouch.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <TactilityCore.h>
|
||||
|
||||
#include "UnPhoneFeatures.h"
|
||||
#include "esp_err.h"
|
||||
#include "hx8357/disp_spi.h"
|
||||
#include "hx8357/hx8357.h"
|
||||
|
||||
#define TAG "unphone_display"
|
||||
#define BUFFER_SIZE (UNPHONE_LCD_HORIZONTAL_RESOLUTION * UNPHONE_LCD_DRAW_BUFFER_HEIGHT * LV_COLOR_DEPTH / 8)
|
||||
|
||||
extern UnPhoneFeatures unPhoneFeatures;
|
||||
|
||||
bool UnPhoneDisplay::start() {
|
||||
TT_LOG_I(TAG, "Starting");
|
||||
|
||||
disp_spi_add_device(SPI2_HOST);
|
||||
|
||||
hx8357_reset(GPIO_NUM_46);
|
||||
hx8357_init(UNPHONE_LCD_PIN_DC);
|
||||
uint8_t madctl = (1U << MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER);
|
||||
hx8357_set_madctl(madctl);
|
||||
|
||||
displayHandle = lv_display_create(UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION);
|
||||
lv_display_set_physical_resolution(displayHandle, UNPHONE_LCD_HORIZONTAL_RESOLUTION, UNPHONE_LCD_VERTICAL_RESOLUTION);
|
||||
lv_display_set_color_format(displayHandle, LV_COLOR_FORMAT_NATIVE);
|
||||
|
||||
// TODO malloc to use SPIRAM
|
||||
static auto* buffer1 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
||||
static auto* buffer2 = (uint8_t*)heap_caps_malloc(BUFFER_SIZE, MALLOC_CAP_SPIRAM);
|
||||
assert(buffer1 != nullptr);
|
||||
assert(buffer2 != nullptr);
|
||||
|
||||
lv_display_set_buffers(
|
||||
displayHandle,
|
||||
buffer1,
|
||||
buffer2,
|
||||
BUFFER_SIZE,
|
||||
LV_DISPLAY_RENDER_MODE_PARTIAL
|
||||
);
|
||||
|
||||
lv_display_set_flush_cb(displayHandle, hx8357_flush);
|
||||
|
||||
if (displayHandle != nullptr) {
|
||||
TT_LOG_I(TAG, "Finished");
|
||||
unPhoneFeatures.setBacklightPower(true);
|
||||
return true;
|
||||
} else {
|
||||
TT_LOG_I(TAG, "Failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneDisplay::stop() {
|
||||
assert(displayHandle != nullptr);
|
||||
|
||||
lv_display_delete(displayHandle);
|
||||
displayHandle = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
tt::hal::Touch* _Nullable UnPhoneDisplay::createTouch() {
|
||||
return static_cast<tt::hal::Touch*>(new UnPhoneTouch());
|
||||
}
|
||||
|
||||
tt::hal::Display* createDisplay() {
|
||||
return static_cast<tt::hal::Display*>(new UnPhoneDisplay());
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_lcd_types.h>
|
||||
#include "lvgl.h"
|
||||
#include "hal/Display.h"
|
||||
|
||||
extern lv_disp_t* displayHandle;
|
||||
|
||||
class UnPhoneDisplay : public tt::hal::Display {
|
||||
|
||||
private:
|
||||
|
||||
lv_display_t* displayHandle = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
bool start() override;
|
||||
|
||||
bool stop() override;
|
||||
|
||||
tt::hal::Touch* _Nullable createTouch() override;
|
||||
|
||||
lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; }
|
||||
};
|
||||
|
||||
tt::hal::Display* createDisplay();
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#define UNPHONE_LCD_SPI_HOST SPI2_HOST
|
||||
#define UNPHONE_LCD_PIN_CS GPIO_NUM_48
|
||||
#define UNPHONE_LCD_PIN_DC GPIO_NUM_47
|
||||
#define UNPHONE_LCD_PIN_RESET GPIO_NUM_46
|
||||
#define UNPHONE_LCD_SPI_FREQUENCY 27000000
|
||||
#define UNPHONE_LCD_HORIZONTAL_RESOLUTION 320
|
||||
#define UNPHONE_LCD_VERTICAL_RESOLUTION 480
|
||||
#define UNPHONE_LCD_DRAW_BUFFER_HEIGHT (UNPHONE_LCD_VERTICAL_RESOLUTION / 15)
|
||||
#define UNPHONE_LCD_SPI_TRANSFER_HEIGHT (UNPHONE_LCD_VERTICAL_RESOLUTION / 15)
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "UnPhonePower.h"
|
||||
#include "UnPhoneTouch.h"
|
||||
#include "Log.h"
|
||||
|
||||
#define TAG "unphone_power"
|
||||
|
||||
#define BATTERY_VOLTAGE_MIN 3.2f
|
||||
#define BATTERY_VOLTAGE_MAX 4.2f
|
||||
|
||||
static uint8_t estimateChargeLevelFromVoltage(uint32_t milliVolt) {
|
||||
float volts = TT_MIN((float)milliVolt / 1000.f, BATTERY_VOLTAGE_MAX);
|
||||
float voltage_percentage = (volts - BATTERY_VOLTAGE_MIN) / (BATTERY_VOLTAGE_MAX - BATTERY_VOLTAGE_MIN);
|
||||
float voltage_factor = TT_MIN(1.0f, voltage_percentage);
|
||||
auto charge_level = (uint8_t) (voltage_factor * 100.f);
|
||||
TT_LOG_V(TAG, "mV = %lu, scaled = %.2f, factor = %.2f, result = %d", milliVolt, volts, voltage_factor, charge_level);
|
||||
return charge_level;
|
||||
}
|
||||
|
||||
bool UnPhonePower::supportsMetric(MetricType type) const {
|
||||
switch (type) {
|
||||
case MetricType::BatteryVoltage:
|
||||
case MetricType::ChargeLevel:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhonePower::getMetric(Power::MetricType type, Power::MetricData& data) {
|
||||
switch (type) {
|
||||
case MetricType::BatteryVoltage:
|
||||
return readBatteryVoltageSampled(data.valueAsUint32);
|
||||
case MetricType::ChargeLevel: {
|
||||
uint32_t milli_volt;
|
||||
if (readBatteryVoltageSampled(milli_volt)) {
|
||||
data.valueAsUint8 = estimateChargeLevelFromVoltage(milli_volt);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhonePower::readBatteryVoltageOnce(uint32_t& output) const {
|
||||
auto* touch = UnPhoneTouch::getInstance();
|
||||
if (touch != nullptr) {
|
||||
float vbat;
|
||||
if (touch->getVBat(vbat)) {
|
||||
// Convert to mV
|
||||
output = (uint32_t)(vbat * 1000.f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_VOLTAGE_SAMPLES 15
|
||||
|
||||
bool UnPhonePower::readBatteryVoltageSampled(uint32_t& output) const {
|
||||
size_t samples_read = 0;
|
||||
uint32_t sample_accumulator = 0;
|
||||
uint32_t sample_read_buffer;
|
||||
|
||||
for (size_t i = 0; i < MAX_VOLTAGE_SAMPLES; ++i) {
|
||||
if (readBatteryVoltageOnce(sample_read_buffer)) {
|
||||
sample_accumulator += sample_read_buffer;
|
||||
samples_read++;
|
||||
}
|
||||
}
|
||||
|
||||
if (samples_read > 0) {
|
||||
output = sample_accumulator / samples_read;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static std::shared_ptr<Power> power;
|
||||
|
||||
std::shared_ptr<Power> unPhoneGetPower() {
|
||||
if (power == nullptr) {
|
||||
power = std::make_shared<UnPhonePower>();
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/Power.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
class UnPhonePower : public Power {
|
||||
|
||||
public:
|
||||
|
||||
UnPhonePower() = default;
|
||||
~UnPhonePower() = default;
|
||||
|
||||
bool supportsMetric(MetricType type) const override;
|
||||
bool getMetric(Power::MetricType type, Power::MetricData& data) override;
|
||||
|
||||
private:
|
||||
|
||||
bool readBatteryVoltageOnce(uint32_t& output) const;
|
||||
bool readBatteryVoltageSampled(uint32_t& output) const;
|
||||
};
|
||||
|
||||
std::shared_ptr<Power> unPhoneGetPower();
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "UnPhoneSdCard.h"
|
||||
|
||||
#include "lvgl/LvglSync.h"
|
||||
#include "hal/SpiSdCard.h"
|
||||
|
||||
#include <esp_vfs_fat.h>
|
||||
|
||||
#define UNPHONE_SDCARD_PIN_CS GPIO_NUM_43
|
||||
#define UNPHONE_LCD_PIN_CS GPIO_NUM_48
|
||||
#define UNPHONE_LORA_PIN_CS GPIO_NUM_44
|
||||
#define UNPHONE_TOUCH_PIN_CS GPIO_NUM_38
|
||||
|
||||
std::shared_ptr<SdCard> createUnPhoneSdCard() {
|
||||
auto* configuration = new tt::hal::SpiSdCard::Config(
|
||||
UNPHONE_SDCARD_PIN_CS,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
GPIO_NUM_NC,
|
||||
SdCard::MountBehaviour::AtBoot,
|
||||
tt::lvgl::getLvglSyncLockable(),
|
||||
{
|
||||
UNPHONE_LORA_PIN_CS,
|
||||
UNPHONE_LCD_PIN_CS,
|
||||
UNPHONE_TOUCH_PIN_CS
|
||||
}
|
||||
);
|
||||
|
||||
auto* sdcard = (SdCard*) new SpiSdCard(
|
||||
std::unique_ptr<SpiSdCard::Config>(configuration)
|
||||
);
|
||||
|
||||
return std::shared_ptr<SdCard>(sdcard);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/SdCard.h"
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
std::shared_ptr<SdCard> createUnPhoneSdCard();
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "UnPhoneTouch.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_touch_xpt2046.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "lvgl/LvglSync.h"
|
||||
|
||||
#define TAG "unphone_touch"
|
||||
|
||||
#define UNPHONE_TOUCH_X_MAX 320
|
||||
#define UNPHONE_TOUCH_Y_MAX 480
|
||||
|
||||
UnPhoneTouch* UnPhoneTouch::instance = nullptr;
|
||||
|
||||
bool UnPhoneTouch::start(lv_display_t* display) {
|
||||
const esp_lcd_panel_io_spi_config_t io_config = ESP_LCD_TOUCH_IO_SPI_XPT2046_CONFIG(GPIO_NUM_38);
|
||||
|
||||
if (esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &ioHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Touch IO SPI creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_lcd_touch_config_t config = {
|
||||
.x_max = UNPHONE_TOUCH_X_MAX,
|
||||
.y_max = UNPHONE_TOUCH_Y_MAX,
|
||||
.rst_gpio_num = GPIO_NUM_NC,
|
||||
.int_gpio_num = GPIO_NUM_NC,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = 0,
|
||||
.mirror_x = 0,
|
||||
.mirror_y = 0,
|
||||
},
|
||||
.process_coordinates = nullptr,
|
||||
.interrupt_callback = nullptr,
|
||||
.user_data = nullptr,
|
||||
.driver_data = nullptr
|
||||
};
|
||||
|
||||
if (esp_lcd_touch_new_spi_xpt2046(ioHandle, &config, &touchHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "XPT2046 driver init failed");
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||
.disp = display,
|
||||
.handle = touchHandle,
|
||||
};
|
||||
|
||||
TT_LOG_I(TAG, "Adding touch to LVGL");
|
||||
deviceHandle = lvgl_port_add_touch(&touch_cfg);
|
||||
if (deviceHandle == nullptr) {
|
||||
TT_LOG_E(TAG, "Adding touch failed");
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
instance = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnPhoneTouch::stop() {
|
||||
instance = nullptr;
|
||||
cleanup();
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPhoneTouch::cleanup() {
|
||||
if (deviceHandle != nullptr) {
|
||||
lv_indev_delete(deviceHandle);
|
||||
deviceHandle = nullptr;
|
||||
}
|
||||
|
||||
if (touchHandle != nullptr) {
|
||||
esp_lcd_touch_del(touchHandle);
|
||||
touchHandle = nullptr;
|
||||
}
|
||||
|
||||
if (ioHandle != nullptr) {
|
||||
esp_lcd_panel_io_del(ioHandle);
|
||||
ioHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnPhoneTouch::getVBat(float& outputVbat) {
|
||||
if (touchHandle != nullptr) {
|
||||
// Shares the SPI bus with the display, so we have to sync/lock as this method might be called from anywhere
|
||||
if (tt::lvgl::lock(50 / portTICK_PERIOD_MS)) {
|
||||
esp_lcd_touch_xpt2046_read_battery_level(touchHandle, &outputVbat);
|
||||
tt::lvgl::unlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "hal/Touch.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "esp_lcd_panel_io_interface.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
|
||||
class UnPhoneTouch : public tt::hal::Touch {
|
||||
|
||||
private:
|
||||
|
||||
static UnPhoneTouch* instance;
|
||||
|
||||
esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr;
|
||||
esp_lcd_touch_handle_t _Nullable touchHandle = nullptr;
|
||||
lv_indev_t* _Nullable deviceHandle = nullptr;
|
||||
void cleanup();
|
||||
|
||||
public:
|
||||
|
||||
bool start(lv_display_t* display) override;
|
||||
bool stop() override;
|
||||
lv_indev_t* _Nullable getLvglIndev() override { return deviceHandle; }
|
||||
|
||||
bool getVBat(float& outputVbat);
|
||||
|
||||
/** Used for accessing getVBat() in Power driver */
|
||||
static UnPhoneTouch* getInstance() { return instance; }
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
The files in this folder are from https://github.com/lvgl/lvgl_esp32_drivers
|
||||
The original license is an MIT license: https://github.com/lvgl/lvgl_esp32_drivers/blob/master/LICENSE
|
||||
|
||||
You may use the files in this folder under the original license, or under GPL v3 from the main Tactility project.
|
||||
@@ -0,0 +1,315 @@
|
||||
#define LV_USE_PRIVATE_API 1 // For actual lv_obj_t declaration
|
||||
|
||||
/**
|
||||
* @file disp_spi.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "esp_system.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "disp_spi"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
#include "disp_spi.h"
|
||||
//#include "disp_driver.h"
|
||||
|
||||
//#include "../lvgl_helpers.h"
|
||||
#include "../lvgl_spi_conf.h"
|
||||
|
||||
/******************************************************************************
|
||||
* Notes about DMA spi_transaction_ext_t structure pooling
|
||||
*
|
||||
* An xQueue is used to hold a pool of reusable SPI spi_transaction_ext_t
|
||||
* structures that get used for all DMA SPI transactions. While an xQueue may
|
||||
* seem like overkill it is an already built-in RTOS feature that comes at
|
||||
* little cost. xQueues are also ISR safe if it ever becomes necessary to
|
||||
* access the pool in the ISR callback.
|
||||
*
|
||||
* When a DMA request is sent, a transaction structure is removed from the
|
||||
* pool, filled out, and passed off to the esp32 SPI driver. Later, when
|
||||
* servicing pending SPI transaction results, the transaction structure is
|
||||
* recycled back into the pool for later reuse. This matches the DMA SPI
|
||||
* transaction life cycle requirements of the esp32 SPI driver.
|
||||
*
|
||||
* When polling or synchronously sending SPI requests, and as required by the
|
||||
* esp32 SPI driver, all pending DMA transactions are first serviced. Then the
|
||||
* polling SPI request takes place.
|
||||
*
|
||||
* When sending an asynchronous DMA SPI request, if the pool is empty, some
|
||||
* small percentage of pending transactions are first serviced before sending
|
||||
* any new DMA SPI transactions. Not too many and not too few as this balance
|
||||
* controls DMA transaction latency.
|
||||
*
|
||||
* It is therefore not the design that all pending transactions must be
|
||||
* serviced and placed back into the pool with DMA SPI requests - that
|
||||
* will happen eventually. The pool just needs to contain enough to float some
|
||||
* number of in-flight SPI requests to speed up the overall DMA SPI data rate
|
||||
* and reduce transaction latency. If however a display driver uses some
|
||||
* polling SPI requests or calls disp_wait_for_pending_transactions() directly,
|
||||
* the pool will reach the full state more often and speed up DMA queuing.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define SPI_TRANSACTION_POOL_SIZE 50 /* maximum number of DMA transactions simultaneously in-flight */
|
||||
|
||||
/* DMA Transactions to reserve before queueing additional DMA transactions. A 1/10th seems to be a good balance. Too many (or all) and it will increase latency. */
|
||||
#define SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE 10
|
||||
#if SPI_TRANSACTION_POOL_SIZE >= SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE
|
||||
#define SPI_TRANSACTION_POOL_RESERVE (SPI_TRANSACTION_POOL_SIZE / SPI_TRANSACTION_POOL_RESERVE_PERCENTAGE)
|
||||
#else
|
||||
#define SPI_TRANSACTION_POOL_RESERVE 1 /* defines minimum size */
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void spi_ready(spi_transaction_t*trans);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
static spi_host_device_t spi_host;
|
||||
static spi_device_handle_t spi;
|
||||
static QueueHandle_t TransactionPool = NULL;
|
||||
static transaction_cb_t chained_post_cb;
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg)
|
||||
{
|
||||
spi_host=host;
|
||||
chained_post_cb=devcfg->post_cb;
|
||||
devcfg->post_cb=spi_ready;
|
||||
esp_err_t ret=spi_bus_add_device(host, devcfg, &spi);
|
||||
assert(ret==ESP_OK);
|
||||
}
|
||||
|
||||
void disp_spi_add_device(spi_host_device_t host)
|
||||
{
|
||||
disp_spi_add_device_with_speed(host, SPI_TFT_CLOCK_SPEED_HZ);
|
||||
}
|
||||
|
||||
void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz)
|
||||
{
|
||||
ESP_LOGI(TAG, "Adding SPI device");
|
||||
ESP_LOGI(TAG, "Clock speed: %dHz, mode: %d, CS pin: %d",
|
||||
clock_speed_hz, SPI_TFT_SPI_MODE, DISP_SPI_CS);
|
||||
|
||||
spi_device_interface_config_t devcfg={
|
||||
.clock_speed_hz = clock_speed_hz,
|
||||
.mode = SPI_TFT_SPI_MODE,
|
||||
.spics_io_num=DISP_SPI_CS, // CS pin
|
||||
.input_delay_ns=DISP_SPI_INPUT_DELAY_NS,
|
||||
.queue_size=SPI_TRANSACTION_POOL_SIZE,
|
||||
.pre_cb=NULL,
|
||||
.post_cb=NULL,
|
||||
#if defined(DISP_SPI_HALF_DUPLEX)
|
||||
.flags = SPI_DEVICE_NO_DUMMY | SPI_DEVICE_HALFDUPLEX, /* dummy bits should be explicitly handled via DISP_SPI_VARIABLE_DUMMY as needed */
|
||||
#else
|
||||
#if defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_FT81X)
|
||||
.flags = 0,
|
||||
#elif defined (CONFIG_LV_TFT_DISPLAY_CONTROLLER_RA8875)
|
||||
.flags = SPI_DEVICE_NO_DUMMY,
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
disp_spi_add_device_config(host, &devcfg);
|
||||
|
||||
/* create the transaction pool and fill it with ptrs to spi_transaction_ext_t to reuse */
|
||||
if(TransactionPool == NULL) {
|
||||
TransactionPool = xQueueCreate(SPI_TRANSACTION_POOL_SIZE, sizeof(spi_transaction_ext_t*));
|
||||
assert(TransactionPool != NULL);
|
||||
for (size_t i = 0; i < SPI_TRANSACTION_POOL_SIZE; i++)
|
||||
{
|
||||
spi_transaction_ext_t* pTransaction = (spi_transaction_ext_t*)heap_caps_malloc(sizeof(spi_transaction_ext_t), MALLOC_CAP_DMA);
|
||||
assert(pTransaction != NULL);
|
||||
memset(pTransaction, 0, sizeof(spi_transaction_ext_t));
|
||||
xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disp_spi_change_device_speed(int clock_speed_hz)
|
||||
{
|
||||
if (clock_speed_hz <= 0) {
|
||||
clock_speed_hz = SPI_TFT_CLOCK_SPEED_HZ;
|
||||
}
|
||||
ESP_LOGI(TAG, "Changing SPI device clock speed: %d", clock_speed_hz);
|
||||
disp_spi_remove_device();
|
||||
disp_spi_add_device_with_speed(spi_host, clock_speed_hz);
|
||||
}
|
||||
|
||||
void disp_spi_remove_device()
|
||||
{
|
||||
/* Wait for previous pending transaction results */
|
||||
disp_wait_for_pending_transactions();
|
||||
|
||||
esp_err_t ret=spi_bus_remove_device(spi);
|
||||
assert(ret==ESP_OK);
|
||||
}
|
||||
|
||||
void disp_spi_transaction(const uint8_t *data, size_t length,
|
||||
int flags, uint8_t *out,
|
||||
uint64_t addr, uint8_t dummy_bits)
|
||||
{
|
||||
if (0 == length) {
|
||||
return;
|
||||
}
|
||||
|
||||
spi_transaction_ext_t t = {0};
|
||||
|
||||
/* transaction length is in bits */
|
||||
t.base.length = length * 8;
|
||||
|
||||
if (length <= 4 && data != NULL) {
|
||||
t.base.flags = SPI_TRANS_USE_TXDATA;
|
||||
memcpy(t.base.tx_data, data, length);
|
||||
} else {
|
||||
t.base.tx_buffer = data;
|
||||
}
|
||||
|
||||
if (flags & DISP_SPI_RECEIVE) {
|
||||
assert(out != NULL && (flags & (DISP_SPI_SEND_POLLING | DISP_SPI_SEND_SYNCHRONOUS)));
|
||||
t.base.rx_buffer = out;
|
||||
|
||||
#if defined(DISP_SPI_HALF_DUPLEX)
|
||||
t.base.rxlength = t.base.length;
|
||||
t.base.length = 0; /* no MOSI phase in half-duplex reads */
|
||||
#else
|
||||
t.base.rxlength = 0; /* in full-duplex mode, zero means same as tx length */
|
||||
#endif
|
||||
}
|
||||
|
||||
if (flags & DISP_SPI_ADDRESS_8) {
|
||||
t.address_bits = 8;
|
||||
} else if (flags & DISP_SPI_ADDRESS_16) {
|
||||
t.address_bits = 16;
|
||||
} else if (flags & DISP_SPI_ADDRESS_24) {
|
||||
t.address_bits = 24;
|
||||
} else if (flags & DISP_SPI_ADDRESS_32) {
|
||||
t.address_bits = 32;
|
||||
}
|
||||
if (t.address_bits) {
|
||||
t.base.addr = addr;
|
||||
t.base.flags |= SPI_TRANS_VARIABLE_ADDR;
|
||||
}
|
||||
|
||||
#if defined(DISP_SPI_HALF_DUPLEX)
|
||||
if (flags & DISP_SPI_MODE_DIO) {
|
||||
t.base.flags |= SPI_TRANS_MODE_DIO;
|
||||
} else if (flags & DISP_SPI_MODE_QIO) {
|
||||
t.base.flags |= SPI_TRANS_MODE_QIO;
|
||||
}
|
||||
|
||||
if (flags & DISP_SPI_MODE_DIOQIO_ADDR) {
|
||||
t.base.flags |= SPI_TRANS_MODE_DIOQIO_ADDR;
|
||||
}
|
||||
|
||||
if ((flags & DISP_SPI_VARIABLE_DUMMY) && dummy_bits) {
|
||||
t.dummy_bits = dummy_bits;
|
||||
t.base.flags |= SPI_TRANS_VARIABLE_DUMMY;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Save flags for pre/post transaction processing */
|
||||
t.base.user = (void *) flags;
|
||||
|
||||
/* Poll/Complete/Queue transaction */
|
||||
if (flags & DISP_SPI_SEND_POLLING) {
|
||||
disp_wait_for_pending_transactions(); /* before polling, all previous pending transactions need to be serviced */
|
||||
spi_device_polling_transmit(spi, (spi_transaction_t *) &t);
|
||||
} else if (flags & DISP_SPI_SEND_SYNCHRONOUS) {
|
||||
disp_wait_for_pending_transactions(); /* before synchronous queueing, all previous pending transactions need to be serviced */
|
||||
spi_device_transmit(spi, (spi_transaction_t *) &t);
|
||||
} else {
|
||||
|
||||
/* if necessary, ensure we can queue new transactions by servicing some previous transactions */
|
||||
if(uxQueueMessagesWaiting(TransactionPool) == 0) {
|
||||
spi_transaction_t *presult;
|
||||
while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_RESERVE) {
|
||||
if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) {
|
||||
xQueueSend(TransactionPool, &presult, portMAX_DELAY); /* back to the pool to be reused */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spi_transaction_ext_t *pTransaction = NULL;
|
||||
xQueueReceive(TransactionPool, &pTransaction, portMAX_DELAY);
|
||||
memcpy(pTransaction, &t, sizeof(t));
|
||||
if (spi_device_queue_trans(spi, (spi_transaction_t *) pTransaction, portMAX_DELAY) != ESP_OK) {
|
||||
xQueueSend(TransactionPool, &pTransaction, portMAX_DELAY); /* send failed transaction back to the pool to be reused */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void disp_wait_for_pending_transactions(void)
|
||||
{
|
||||
spi_transaction_t *presult;
|
||||
|
||||
while(uxQueueMessagesWaiting(TransactionPool) < SPI_TRANSACTION_POOL_SIZE) { /* service until the transaction reuse pool is full again */
|
||||
if (spi_device_get_trans_result(spi, &presult, 1) == ESP_OK) {
|
||||
xQueueSend(TransactionPool, &presult, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disp_spi_acquire(void)
|
||||
{
|
||||
esp_err_t ret = spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
|
||||
void disp_spi_release(void)
|
||||
{
|
||||
spi_device_release_bus(spi);
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void IRAM_ATTR spi_ready(spi_transaction_t *trans)
|
||||
{
|
||||
disp_spi_send_flag_t flags = (disp_spi_send_flag_t) trans->user;
|
||||
|
||||
if (flags & DISP_SPI_SIGNAL_FLUSH) {
|
||||
lv_disp_t* disp = lv_refr_get_disp_refreshing();
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
if (chained_post_cb) {
|
||||
chained_post_cb(trans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @file disp_spi.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DISP_SPI_H
|
||||
#define DISP_SPI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <driver/spi_master.h>
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
typedef enum _disp_spi_send_flag_t {
|
||||
DISP_SPI_SEND_QUEUED = 0x00000000,
|
||||
DISP_SPI_SEND_POLLING = 0x00000001,
|
||||
DISP_SPI_SEND_SYNCHRONOUS = 0x00000002,
|
||||
DISP_SPI_SIGNAL_FLUSH = 0x00000004,
|
||||
DISP_SPI_RECEIVE = 0x00000008,
|
||||
DISP_SPI_CMD_8 = 0x00000010, /* Reserved */
|
||||
DISP_SPI_CMD_16 = 0x00000020, /* Reserved */
|
||||
DISP_SPI_ADDRESS_8 = 0x00000040,
|
||||
DISP_SPI_ADDRESS_16 = 0x00000080,
|
||||
DISP_SPI_ADDRESS_24 = 0x00000100,
|
||||
DISP_SPI_ADDRESS_32 = 0x00000200,
|
||||
DISP_SPI_MODE_DIO = 0x00000400,
|
||||
DISP_SPI_MODE_QIO = 0x00000800,
|
||||
DISP_SPI_MODE_DIOQIO_ADDR = 0x00001000,
|
||||
DISP_SPI_VARIABLE_DUMMY = 0x00002000,
|
||||
} disp_spi_send_flag_t;
|
||||
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
void disp_spi_add_device(spi_host_device_t host);
|
||||
void disp_spi_add_device_config(spi_host_device_t host, spi_device_interface_config_t *devcfg);
|
||||
void disp_spi_add_device_with_speed(spi_host_device_t host, int clock_speed_hz);
|
||||
void disp_spi_change_device_speed(int clock_speed_hz);
|
||||
void disp_spi_remove_device();
|
||||
|
||||
/* Important!
|
||||
All buffers should also be 32-bit aligned and DMA capable to prevent extra allocations and copying.
|
||||
When DMA reading (even in polling mode) the ESP32 always read in 4-byte chunks even if less is requested.
|
||||
Extra space will be zero filled. Always ensure the out buffer is large enough to hold at least 4 bytes!
|
||||
*/
|
||||
void disp_spi_transaction(const uint8_t *data, size_t length,
|
||||
int flags, uint8_t *out, uint64_t addr, uint8_t dummy_bits);
|
||||
|
||||
void disp_wait_for_pending_transactions(void);
|
||||
void disp_spi_acquire(void);
|
||||
void disp_spi_release(void);
|
||||
|
||||
static inline void disp_spi_send_data(uint8_t *data, size_t length) {
|
||||
disp_spi_transaction(data, length, DISP_SPI_SEND_POLLING, NULL, 0, 0);
|
||||
}
|
||||
|
||||
static inline void disp_spi_send_colors(uint8_t *data, size_t length) {
|
||||
disp_spi_transaction(data, length,
|
||||
DISP_SPI_SEND_QUEUED | DISP_SPI_SIGNAL_FLUSH,
|
||||
NULL, 0, 0);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*DISP_SPI_H*/
|
||||
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* @file HX8357.c
|
||||
*
|
||||
* Roughly based on the Adafruit_HX8357_Library
|
||||
*
|
||||
* This library should work with:
|
||||
* Adafruit 3.5" TFT 320x480 + Touchscreen Breakout
|
||||
* http://www.adafruit.com/products/2050
|
||||
*
|
||||
* Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers
|
||||
* https://www.adafruit.com/product/3651
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "hx8357.h"
|
||||
#include "disp_spi.h"
|
||||
#include "driver/gpio.h"
|
||||
#include <esp_log.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
#define TAG "HX8357"
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
static gpio_num_t dcPin = GPIO_NUM_NC;
|
||||
|
||||
/*The LCD needs a bunch of command/argument values to be initialized. They are stored in this struct. */
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t data[16];
|
||||
uint8_t databytes; //No of data in data; bit 7 = delay after set; 0xFF = end of cmds.
|
||||
} lcd_init_cmd_t;
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void hx8357_send_cmd(uint8_t cmd);
|
||||
static void hx8357_send_data(void * data, uint16_t length);
|
||||
static void hx8357_send_color(void * data, uint16_t length);
|
||||
|
||||
|
||||
/**********************
|
||||
* INITIALIZATION ARRAYS
|
||||
**********************/
|
||||
// Taken from the Adafruit driver
|
||||
static const uint8_t
|
||||
initb[] = {
|
||||
HX8357B_SETPOWER, 3,
|
||||
0x44, 0x41, 0x06,
|
||||
HX8357B_SETVCOM, 2,
|
||||
0x40, 0x10,
|
||||
HX8357B_SETPWRNORMAL, 2,
|
||||
0x05, 0x12,
|
||||
HX8357B_SET_PANEL_DRIVING, 5,
|
||||
0x14, 0x3b, 0x00, 0x02, 0x11,
|
||||
HX8357B_SETDISPLAYFRAME, 1,
|
||||
0x0c, // 6.8mhz
|
||||
HX8357B_SETPANELRELATED, 1,
|
||||
0x01, // BGR
|
||||
0xEA, 3, // seq_undefined1, 3 args
|
||||
0x03, 0x00, 0x00,
|
||||
0xEB, 4, // undef2, 4 args
|
||||
0x40, 0x54, 0x26, 0xdb,
|
||||
HX8357B_SETGAMMA, 12,
|
||||
0x00, 0x15, 0x00, 0x22, 0x00, 0x08, 0x77, 0x26, 0x66, 0x22, 0x04, 0x00,
|
||||
HX8357_MADCTL, 1,
|
||||
0xC0,
|
||||
HX8357_COLMOD, 1,
|
||||
0x55,
|
||||
HX8357_PASET, 4,
|
||||
0x00, 0x00, 0x01, 0xDF,
|
||||
HX8357_CASET, 4,
|
||||
0x00, 0x00, 0x01, 0x3F,
|
||||
HX8357B_SETDISPMODE, 1,
|
||||
0x00, // CPU (DBI) and internal oscillation ??
|
||||
HX8357_SLPOUT, 0x80 + 120/5, // Exit sleep, then delay 120 ms
|
||||
HX8357_DISPON, 0x80 + 10/5, // Main screen turn on, delay 10 ms
|
||||
0 // END OF COMMAND LIST
|
||||
}, initd[] = {
|
||||
HX8357_SWRESET, 0x80 + 100/5, // Soft reset, then delay 10 ms
|
||||
HX8357D_SETC, 3,
|
||||
0xFF, 0x83, 0x57,
|
||||
0xFF, 0x80 + 500/5, // No command, just delay 300 ms
|
||||
HX8357_SETRGB, 4,
|
||||
0x80, 0x00, 0x06, 0x06, // 0x80 enables SDO pin (0x00 disables)
|
||||
HX8357D_SETCOM, 1,
|
||||
0x25, // -1.52V
|
||||
HX8357_SETOSC, 1,
|
||||
0x68, // Normal mode 70Hz, Idle mode 55 Hz
|
||||
HX8357_SETPANEL, 1,
|
||||
0x05, // BGR, Gate direction swapped
|
||||
HX8357_SETPWR1, 6,
|
||||
0x00, // Not deep standby
|
||||
0x15, // BT
|
||||
0x1C, // VSPR
|
||||
0x1C, // VSNR
|
||||
0x83, // AP
|
||||
0xAA, // FS
|
||||
HX8357D_SETSTBA, 6,
|
||||
0x50, // OPON normal
|
||||
0x50, // OPON idle
|
||||
0x01, // STBA
|
||||
0x3C, // STBA
|
||||
0x1E, // STBA
|
||||
0x08, // GEN
|
||||
HX8357D_SETCYC, 7,
|
||||
0x02, // NW 0x02
|
||||
0x40, // RTN
|
||||
0x00, // DIV
|
||||
0x2A, // DUM
|
||||
0x2A, // DUM
|
||||
0x0D, // GDON
|
||||
0x78, // GDOFF
|
||||
HX8357D_SETGAMMA, 34,
|
||||
0x02, 0x0A, 0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b,
|
||||
0x42, 0x3A, 0x27, 0x1B, 0x08, 0x09, 0x03, 0x02, 0x0A,
|
||||
0x11, 0x1d, 0x23, 0x35, 0x41, 0x4b, 0x4b, 0x42, 0x3A,
|
||||
0x27, 0x1B, 0x08, 0x09, 0x03, 0x00, 0x01,
|
||||
HX8357_COLMOD, 1,
|
||||
0x57, // 0x55 = 16 bit, 0x57 = 24bit
|
||||
HX8357_MADCTL, 1,
|
||||
0xC0,
|
||||
HX8357_TEON, 1,
|
||||
0x00, // TW off
|
||||
HX8357_TEARLINE, 2,
|
||||
0x00, 0x02,
|
||||
HX8357_SLPOUT, 0x80 + 150/5, // Exit Sleep, then delay 150 ms
|
||||
HX8357_DISPON, 0x80 + 50/5, // Main screen turn on, delay 50 ms
|
||||
0, // END OF COMMAND LIST
|
||||
};
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
static uint8_t displayType = HX8357D;
|
||||
|
||||
void hx8357_reset(gpio_num_t resetPin) {
|
||||
if (resetPin != GPIO_NUM_NC) {
|
||||
esp_rom_gpio_pad_select_gpio(resetPin);
|
||||
gpio_set_direction(resetPin, GPIO_MODE_OUTPUT);
|
||||
|
||||
//Reset the display
|
||||
gpio_set_level(resetPin, 0);
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
gpio_set_level(resetPin, 1);
|
||||
vTaskDelay(120 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void hx8357_init(gpio_num_t newDcPin) {
|
||||
ESP_LOGI(TAG, "Initialization.");
|
||||
|
||||
dcPin = newDcPin;
|
||||
|
||||
//Initialize non-SPI GPIOs
|
||||
esp_rom_gpio_pad_select_gpio(dcPin);
|
||||
gpio_set_direction(dcPin, GPIO_MODE_OUTPUT);
|
||||
|
||||
//Send all the commands
|
||||
const uint8_t *addr = (displayType == HX8357B) ? initb : initd;
|
||||
uint8_t cmd, x, numArgs;
|
||||
while((cmd = *addr++) > 0) { // '0' command ends list
|
||||
x = *addr++;
|
||||
numArgs = x & 0x7F;
|
||||
if (cmd != 0xFF) { // '255' is ignored
|
||||
if (x & 0x80) { // If high bit set, numArgs is a delay time
|
||||
hx8357_send_cmd(cmd);
|
||||
} else {
|
||||
hx8357_send_cmd(cmd);
|
||||
hx8357_send_data((void *) addr, numArgs);
|
||||
addr += numArgs;
|
||||
}
|
||||
}
|
||||
if (x & 0x80) { // If high bit set...
|
||||
vTaskDelay(numArgs * 5 / portTICK_PERIOD_MS); // numArgs is actually a delay time (5ms units)
|
||||
}
|
||||
}
|
||||
|
||||
#if HX8357_INVERT_COLORS
|
||||
hx8357_send_cmd(HX8357_INVON);
|
||||
#else
|
||||
hx8357_send_cmd(HX8357_INVOFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
//(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
|
||||
void hx8357_flush(lv_disp_t* drv, const lv_area_t * area, uint8_t * color_map)
|
||||
{
|
||||
uint32_t size = lv_area_get_width(area) * lv_area_get_height(area);
|
||||
|
||||
/* Column addresses */
|
||||
uint8_t xb[] = {
|
||||
(uint8_t) (area->x1 >> 8) & 0xFF,
|
||||
(uint8_t) (area->x1) & 0xFF,
|
||||
(uint8_t) (area->x2 >> 8) & 0xFF,
|
||||
(uint8_t) (area->x2) & 0xFF,
|
||||
};
|
||||
|
||||
/* Page addresses */
|
||||
uint8_t yb[] = {
|
||||
(uint8_t) (area->y1 >> 8) & 0xFF,
|
||||
(uint8_t) (area->y1) & 0xFF,
|
||||
(uint8_t) (area->y2 >> 8) & 0xFF,
|
||||
(uint8_t) (area->y2) & 0xFF,
|
||||
};
|
||||
|
||||
/*Column addresses*/
|
||||
hx8357_send_cmd(HX8357_CASET);
|
||||
hx8357_send_data(xb, 4);
|
||||
|
||||
/*Page addresses*/
|
||||
hx8357_send_cmd(HX8357_PASET);
|
||||
hx8357_send_data(yb, 4);
|
||||
|
||||
/*Memory write*/
|
||||
hx8357_send_cmd(HX8357_RAMWR);
|
||||
hx8357_send_color((void*)color_map, size * (LV_COLOR_DEPTH / 8));
|
||||
}
|
||||
|
||||
void hx8357_set_madctl(uint8_t value) {
|
||||
hx8357_send_cmd(HX8357_MADCTL);
|
||||
hx8357_send_data(&value, 1);
|
||||
}
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
|
||||
static void hx8357_send_cmd(uint8_t cmd)
|
||||
{
|
||||
disp_wait_for_pending_transactions();
|
||||
gpio_set_level(dcPin, 0); /*Command mode*/
|
||||
disp_spi_send_data(&cmd, 1);
|
||||
}
|
||||
|
||||
|
||||
static void hx8357_send_data(void * data, uint16_t length)
|
||||
{
|
||||
disp_wait_for_pending_transactions();
|
||||
gpio_set_level(dcPin, 1); /*Data mode*/
|
||||
disp_spi_send_data(data, length);
|
||||
}
|
||||
|
||||
|
||||
static void hx8357_send_color(void * data, uint16_t length)
|
||||
{
|
||||
disp_wait_for_pending_transactions();
|
||||
gpio_set_level(dcPin, 1); /*Data mode*/
|
||||
disp_spi_send_colors(data, length);
|
||||
}
|
||||
|
||||
uint8_t hx8357d_get_gamma_curve_count() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
void hx8357d_set_gamme_curve(uint8_t index) {
|
||||
uint8_t curve = 1;
|
||||
switch (index) {
|
||||
case 0:
|
||||
curve = 0x01;
|
||||
break;
|
||||
case 1:
|
||||
curve = 0x02;
|
||||
break;
|
||||
case 2:
|
||||
curve = 0x04;
|
||||
break;
|
||||
case 3:
|
||||
curve = 0x08;
|
||||
break;
|
||||
}
|
||||
hx8357_send_cmd(HX8357D_SETGAMMA_BY_ID);
|
||||
hx8357_send_data(&curve, 1);
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @file hx8357.h
|
||||
*
|
||||
* Roughly based on the Adafruit_HX8357_Library
|
||||
*
|
||||
* This library should work with:
|
||||
* Adafruit 3.5" TFT 320x480 + Touchscreen Breakout
|
||||
* http://www.adafruit.com/products/2050
|
||||
*
|
||||
* Adafruit TFT FeatherWing - 3.5" 480x320 Touchscreen for Feathers
|
||||
* https://www.adafruit.com/product/3651
|
||||
*
|
||||
* Datasheet:
|
||||
* https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
|
||||
*/
|
||||
|
||||
#ifndef HX8357_H
|
||||
#define HX8357_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <soc/gpio_num.h>
|
||||
|
||||
#define HX8357D 0xD ///< Our internal const for D type
|
||||
#define HX8357B 0xB ///< Our internal const for B type
|
||||
|
||||
#define HX8357_TFTWIDTH 320 ///< 320 pixels wide
|
||||
#define HX8357_TFTHEIGHT 480 ///< 480 pixels tall
|
||||
|
||||
#define HX8357_NOP 0x00 ///< No op
|
||||
#define HX8357_SWRESET 0x01 ///< software reset
|
||||
#define HX8357_RDDID 0x04 ///< Read ID
|
||||
#define HX8357_RDDST 0x09 ///< (unknown)
|
||||
|
||||
#define HX8357_RDPOWMODE 0x0A ///< Read power mode Read power mode
|
||||
#define HX8357_RDMADCTL 0x0B ///< Read MADCTL
|
||||
#define HX8357_RDCOLMOD 0x0C ///< Column entry mode
|
||||
#define HX8357_RDDIM 0x0D ///< Read display image mode
|
||||
#define HX8357_RDDSDR 0x0F ///< Read dosplay signal mode
|
||||
|
||||
#define HX8357_SLPIN 0x10 ///< Enter sleep mode
|
||||
#define HX8357_SLPOUT 0x11 ///< Exit sleep mode
|
||||
#define HX8357B_PTLON 0x12 ///< Partial mode on
|
||||
#define HX8357B_NORON 0x13 ///< Normal mode
|
||||
|
||||
#define HX8357_INVOFF 0x20 ///< Turn off invert
|
||||
#define HX8357_INVON 0x21 ///< Turn on invert
|
||||
#define HX8357_DISPOFF 0x28 ///< Display on
|
||||
#define HX8357_DISPON 0x29 ///< Display off
|
||||
|
||||
#define HX8357_CASET 0x2A ///< Column addr set
|
||||
#define HX8357_PASET 0x2B ///< Page addr set
|
||||
#define HX8357_RAMWR 0x2C ///< Write VRAM
|
||||
#define HX8357_RAMRD 0x2E ///< Read VRAm
|
||||
|
||||
#define HX8357B_PTLAR 0x30 ///< (unknown)
|
||||
#define HX8357_TEON 0x35 ///< Tear enable on
|
||||
#define HX8357_TEARLINE 0x44 ///< (unknown)
|
||||
#define HX8357_MADCTL 0x36 ///< Memory access control
|
||||
#define HX8357_COLMOD 0x3A ///< Color mode
|
||||
|
||||
#define HX8357_SETOSC 0xB0 ///< Set oscillator
|
||||
#define HX8357_SETPWR1 0xB1 ///< Set power control
|
||||
#define HX8357B_SETDISPLAY 0xB2 ///< Set display mode
|
||||
#define HX8357_SETRGB 0xB3 ///< Set RGB interface
|
||||
#define HX8357D_SETCOM 0xB6 ///< Set VCOM voltage
|
||||
|
||||
#define HX8357B_SETDISPMODE 0xB4 ///< Set display mode
|
||||
#define HX8357D_SETCYC 0xB4 ///< Set display cycle reg
|
||||
#define HX8357B_SETOTP 0xB7 ///< Set OTP memory
|
||||
#define HX8357D_SETC 0xB9 ///< Enable extension command
|
||||
|
||||
#define HX8357B_SET_PANEL_DRIVING 0xC0 ///< Set panel drive mode
|
||||
#define HX8357D_SETSTBA 0xC0 ///< Set source option
|
||||
#define HX8357B_SETDGC 0xC1 ///< Set DGC settings
|
||||
#define HX8357B_SETID 0xC3 ///< Set ID
|
||||
#define HX8357B_SETDDB 0xC4 ///< Set DDB
|
||||
#define HX8357B_SETDISPLAYFRAME 0xC5 ///< Set display frame
|
||||
#define HX8357B_GAMMASET 0xC8 ///< Set Gamma correction
|
||||
#define HX8357B_SETCABC 0xC9 ///< Set CABC
|
||||
#define HX8357_SETPANEL 0xCC ///< Set Panel
|
||||
|
||||
#define HX8357B_SETPOWER 0xD0 ///< Set power control
|
||||
#define HX8357B_SETVCOM 0xD1 ///< Set VCOM
|
||||
#define HX8357B_SETPWRNORMAL 0xD2 ///< Set power normal
|
||||
|
||||
#define HX8357B_RDID1 0xDA ///< Read ID #1
|
||||
#define HX8357B_RDID2 0xDB ///< Read ID #2
|
||||
#define HX8357B_RDID3 0xDC ///< Read ID #3
|
||||
#define HX8357B_RDID4 0xDD ///< Read ID #4
|
||||
|
||||
#define HX8357D_SETGAMMA 0xE0 ///< Set Gamma curve data
|
||||
#define HX8357D_SETGAMMA_BY_ID 0x26 ///< Set Gamma curve by curve identifier (0x01, 0x02, 0x04, 0x08)
|
||||
#define HX8357B_SETGAMMA 0xC8 ///< Set Gamma
|
||||
#define HX8357B_SETPANELRELATED 0xE9 ///< Set panel related
|
||||
|
||||
// MADCTL
|
||||
// See datasheet page 123: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
|
||||
#define MADCTL_BIT_INDEX_COMMON_OUTPUTS_RAM 0 // N/A - set to 0
|
||||
#define MADCTL_BIT_INDEX_SEGMENT_OUTPUTS_RAM 1 // N/A - set to 0
|
||||
#define MADCTL_BIT_INDEX_DATA_LATCH_ORDER 2 // 0 = left-to-right refresh, 1 = right-to-left
|
||||
#define MADCTL_BIT_INDEX_RGB_BGR_ORDER 3 // 0 = RGB, 1 = BGR
|
||||
#define MADCTL_BIT_INDEX_LINE_ADDRESS_ORDER 4 // 0 = top-to-bottom refresh, 1 = bottom-to-top
|
||||
#define MADCTL_BIT_INDEX_PAGE_COLUMN_ORDER 5 // 0 = normal, 1 = reverse
|
||||
#define MADCTL_BIT_INDEX_COLUMN_ADDRESS_ORDER 6 // 0 = left-to-right, 1 = right-to-left
|
||||
#define MADCTL_BIT_INDEX_PAGE_ADDRESS_ORDER 7 // 0 = top-to-bottom, 1 = bottom-to-top
|
||||
|
||||
void hx8357_reset(gpio_num_t resetPin);
|
||||
void hx8357_init(gpio_num_t dcPin);
|
||||
void hx8357_set_madctl(uint8_t value);
|
||||
void hx8357_flush(lv_disp_t* drv, const lv_area_t* area, uint8_t* color_map);
|
||||
|
||||
uint8_t hx8357d_get_gamma_curve_count();
|
||||
/**
|
||||
* Note: this doesn't work, even though the manual says it should
|
||||
* Page 141: https://cdn-shop.adafruit.com/datasheets/HX8357-D_DS_April2012.pdf
|
||||
*/
|
||||
void hx8357d_set_gamme_curve(uint8_t index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*HX8357_H*/
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define SPI_TFT_CLOCK_SPEED_HZ (26*1000*1000)
|
||||
#define SPI_TFT_SPI_MODE (0)
|
||||
#define DISP_SPI_CS GPIO_NUM_48
|
||||
#define DISP_SPI_INPUT_DELAY_NS 0
|
||||
|
||||
@@ -6,8 +6,6 @@ endfunction()
|
||||
|
||||
macro(tactility_project project_name)
|
||||
set(TACTILITY_SKIP_SPIFFS 1)
|
||||
add_definitions(-DESP_TARGET)
|
||||
add_compile_definitions(ESP_TARGET)
|
||||
|
||||
include("${TACTILITY_SDK_PATH}/Libraries/elf_loader/elf_loader.cmake")
|
||||
project_elf($project_name)
|
||||
|
||||
+7
-7
@@ -8,13 +8,13 @@ set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_CXX_COMPILER_TARGET}")
|
||||
|
||||
include("Buildscripts/logo.cmake")
|
||||
|
||||
file(READ version.txt TACTILITY_VERSION)
|
||||
add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}")
|
||||
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
message("Building with ESP-IDF v$ENV{ESP_IDF_VERSION}")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
add_definitions(-DESP_TARGET)
|
||||
add_compile_definitions(ESP_TARGET)
|
||||
|
||||
set(COMPONENTS App)
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"Boards"
|
||||
@@ -34,7 +34,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
|
||||
# Non-ESP32 boards
|
||||
if(NOT "${IDF_TARGET}" STREQUAL "esp32")
|
||||
set(EXCLUDE_COMPONENTS "YellowBoard")
|
||||
set(EXCLUDE_COMPONENTS "CYD-2432S024C")
|
||||
set(EXCLUDE_COMPONENTS "M5stackCore2")
|
||||
endif()
|
||||
|
||||
@@ -42,6 +42,7 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
if(NOT "${IDF_TARGET}" STREQUAL "esp32s3")
|
||||
set(EXCLUDE_COMPONENTS "LilygoTdeck")
|
||||
set(EXCLUDE_COMPONENTS "M5stackCoreS3")
|
||||
set(EXCLUDE_COMPONENTS "UnPhone")
|
||||
endif()
|
||||
|
||||
# LVGL
|
||||
@@ -52,11 +53,10 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}/lv_conf_kconfig.h")
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
|
||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_log_write" APPEND)
|
||||
|
||||
file(READ version.txt TACTILITY_VERSION)
|
||||
add_compile_definitions(TT_VERSION="${TACTILITY_VERSION}")
|
||||
else()
|
||||
message("Building for sim target")
|
||||
add_compile_definitions(CONFIG_TT_BOARD_ID="simulator")
|
||||
add_compile_definitions(CONFIG_TT_BOARD_NAME="Simulator")
|
||||
endif()
|
||||
|
||||
project(Tactility)
|
||||
|
||||
+8
-2
@@ -2,7 +2,9 @@
|
||||
|
||||
The basic formatting rules are set in `.clang-format`. Use auto-formatting in your editor.
|
||||
|
||||
All code should target C++ language revision 17.
|
||||
All code should target C++ language revision 20.
|
||||
|
||||
If you use CLion, please enable the setting called "Enable ClangFormat" under Settings > Editor > Code Style.
|
||||
|
||||
## Naming
|
||||
|
||||
@@ -49,7 +51,7 @@ void getLimit() {
|
||||
Preprocessor functions are written in snake-case and prefixed with `tt_`, for example:
|
||||
|
||||
```c++
|
||||
#define tt_assert(x) __tt_assert_internal(x)
|
||||
#define tt_check(x) if (!(x)) { /* .. */ }
|
||||
```
|
||||
|
||||
### Type names
|
||||
@@ -81,3 +83,7 @@ enum class {
|
||||
Error
|
||||
};
|
||||
```
|
||||
|
||||
### Exceptions
|
||||
|
||||
Don't ever throw them. Make a return type that wraps all the error and success scenarios that are relevant.
|
||||
|
||||
+48
-7
@@ -1,11 +1,52 @@
|
||||
# Note
|
||||
# Contributing
|
||||
|
||||
During the pre-alpha stage, the APIs are changing rapidly.
|
||||
I don't want to disappoint people with huge merge conflicts, so new features are not accepted yet.
|
||||
## Accepted changes
|
||||
|
||||
I welcome fixes and possibly also board implementations:
|
||||
Please open an [Issue](https://github.com/ByteWelder/Tactility/issues/new) on GitHub to discuss first new boards!
|
||||
Before releasing version 1.0.0, the APIs are changing rapidly.
|
||||
I want to minimize the amount of changes that I have to maintain, because it limits the amount of breaking changes that I have to deal with when the APIs change.
|
||||
|
||||
# Code Style
|
||||
### New features
|
||||
|
||||
See [this document](CODING_STYLE.md).
|
||||
These are currently not accepted.
|
||||
|
||||
### Visual / interaction design changes
|
||||
|
||||
These are currently not accepted. I'm aware that there is a lot of room for improvement, but I want to mainly focusing on strong technical foundations before
|
||||
I start building further on top of that.
|
||||
Feel free to open an [issue](https://github.com/ByteWelder/Tactility/issues/new) to discuss ideas you have, if you feel like they will have a considerable impact.
|
||||
|
||||
### Fixing things
|
||||
|
||||
The general take here is that minor changes are not accepted at this stage. I don't want to spend my time reviewing and discussing these during the current stage of the project.
|
||||
|
||||
Only fixes for serious issues are accepted, like:
|
||||
- Bugs
|
||||
- Crashes
|
||||
- Security issues
|
||||
|
||||
Some examples of non-serious issues include:
|
||||
- Documentation changes
|
||||
- Renaming code
|
||||
- Fixes for compiler warnings that have low impact on the actual application logic (e.g. only happens on simulator when calling a logging function)
|
||||
|
||||
### New board implementations
|
||||
|
||||
I only support boards that I also own. If I don't own a board, I can't properly test the reliability of the firmware.
|
||||
If you wish to send me a board, please [contact me](https://tactility.one/#/support).
|
||||
|
||||
Please open an [issue](https://github.com/ByteWelder/Tactility/issues/new) on GitHub to discuss new boards.
|
||||
|
||||
If you implemented a board yourself, I'm willing to refer to your implementation on the main website. Please [contact me](https://tactility.one/#/support).
|
||||
|
||||
### Anything that doesn't fall in the above categories?
|
||||
|
||||
Please [contact me](https://tactility.one/#/support) me first!
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests should only contain a single set of changes that are related to eachother.
|
||||
That way, an approved set of changes will not be blocked by an unapproved set of changes.
|
||||
|
||||
## Code Style
|
||||
|
||||
See [this document](CODING_STYLE.md) and [.clang-format](.clang-format).
|
||||
|
||||
+16
-27
@@ -1,36 +1,24 @@
|
||||
# Issues
|
||||
- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes active again.
|
||||
- ESP32 (CYD) memory issues (or any device without PSRAM):
|
||||
- Boot app doesn't show logo
|
||||
- WiFi is on and navigating back to Desktop makes desktop icons disappear
|
||||
- WiFi might fail quietly when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB)
|
||||
Possible mitigations:
|
||||
- When no PSRAM is available, use simplified desktop buttons
|
||||
- Add statusbar icon for memory pressure.
|
||||
- Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount)
|
||||
- Clean up static_cast when casting to base class.
|
||||
- M5Stack CoreS3 SD card mounts, but cannot be read. There is currently a notice about it [here](https://github.com/espressif/esp-bsp/blob/master/bsp/m5stack_core_s3/README.md).
|
||||
- EventFlag: Fix return value of set/get/wait (the errors are weirdly mixed in)
|
||||
- Consistently use either ESP_TARGET or ESP_PLATFORM
|
||||
- tt_check() failure during app argument bundle nullptr check seems to trigger SIGSEGV
|
||||
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
||||
- M5Stack Core only shows 4MB of SPIRAM in use
|
||||
|
||||
# TODOs
|
||||
- Fix bug in T-Deck/etc: esp_lvgl_port settings has a large stack size (~9kB) to fix an issue where the T-Deck would get a stackoverflow. This sometimes happens when WiFi is auto-enabled and you open the app while it is still connecting.
|
||||
- Clean up static_cast when casting to base class.
|
||||
- Mutex: Implement give/take from ISR support (works only for non-recursive ones)
|
||||
- Extend unPhone power driver: add charging status, usb connection status, etc.
|
||||
- Expose app::Paths to TactilityC
|
||||
- Experiment with what happens when using C++ code in an external app (without using standard library!)
|
||||
- Boards' CMakeLists.txt manually declare each source folder. Update them all to do a recursive search of all folders.
|
||||
- We currently build all boards for a given platform (e.g. ESP32S3), but it's better to filter all irrelevant ones based on the Kconfig board settings:
|
||||
Projects will load and compile faster as it won't compile all the dependencies of all these other boards
|
||||
- Make a ledger for setting CPU affinity of various services and tasks
|
||||
- Make "blocking" argument the last one, and put it default to false (or remove it entirely?): void startApp(const std::string& id, bool blocking, std::shared_ptr<const Bundle> parameters) {
|
||||
- Boot hooks instead of a single boot method in config. Define different boot phases/levels in enum.
|
||||
- Add toggle to Display app for sysmon overlay: https://docs.lvgl.io/master/API/others/sysmon/index.html
|
||||
- CrashHandler: use "corrupted" flag
|
||||
- CrashHandler: process other types of crashes (WDT?)
|
||||
- Call tt::lvgl::isSyncSet after HAL init and show error (and crash?) when it is not set.
|
||||
- Create different partitions files for different ESP flash size targets (N4, N8, N16, N32)
|
||||
- Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time.
|
||||
We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks.
|
||||
- T-Deck: Clear screen before turning on blacklight
|
||||
- T-Deck: Use knob for UI selection
|
||||
- T-Deck: Use knob for UI selection?
|
||||
- Crash monitoring: Keep track of which system phase the app crashed in (e.g. which app in which state)
|
||||
- AppContext's onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
|
||||
- App::onResult should pass the app id (or launch request id!) that was started, so we can differentiate between multiple types of apps being launched
|
||||
- Create more unit tests for `tactility-core` and `tactility` (PC-only for now)
|
||||
- Show a warning screen if firmware encryption or secure boot are off when saving WiFi credentials.
|
||||
- Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot.
|
||||
@@ -38,19 +26,18 @@
|
||||
- Explore LVGL9's FreeRTOS functionality
|
||||
- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility.
|
||||
- Scanning SD card for external apps and auto-register them (in a temporary register?)
|
||||
- tt::app::start() and similar functions as proxies for Loader app start/stop/etc.
|
||||
- Support hot-plugging SD card
|
||||
|
||||
# Nice-to-haves
|
||||
- CoreS3 has a hardware issue that prevents mounting SD cards while using the display too: allow USB Mass Storage to use `/data` instead? Perhaps give the USB settings app a drop down to select the root filesystem to attach.
|
||||
- Give external app a different icon. Allow an external app update their id, icon, type and name once they are running(, and persist that info?). Loader will need to be able to find app by (external) location.
|
||||
- Audio player app
|
||||
- Audio recording app
|
||||
- OTA updates
|
||||
- Web flasher
|
||||
- T-Deck Plus: Create separate board config?
|
||||
- Support for displays with different DPI. Consider the layer-based system like on Android.
|
||||
- Make firmwares available via web serial website
|
||||
- If present, use LED to show boot/wifi status
|
||||
- T-Deck Power: capacity estimation uses linear voltage curve, but it should use some sort of battery discharge curve.
|
||||
- Capacity based on voltage: estimation for various devices uses linear voltage curve, but it should use some sort of battery discharge curve.
|
||||
- Statusbar widget to show how much memory is in use?
|
||||
- Wrapper for Slider that shows "+" and "-" buttons, and also the value in a label.
|
||||
- Display app: Add toggle to display performance measurement overlay (consider showing FPS in statusbar!)
|
||||
@@ -58,6 +45,8 @@
|
||||
- On crash, try to save current log to flash or SD card? (this is risky, though, so ask in Discord first)
|
||||
|
||||
# App Ideas
|
||||
- Weather app: https://lab.flipper.net/apps/flip_weather
|
||||
- wget app: https://lab.flipper.net/apps/web_crawler (add profiles for known public APIs?)
|
||||
- USB implementation to make device act as mass storage device.
|
||||
- System logger
|
||||
- BlueTooth keyboard app
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* Note: LVGL and Tactility methods need to be exposed manually from TactilityC/Source/tt_init.cpp
|
||||
* Only C is supported for now (C++ symbols fail to link)
|
||||
*/
|
||||
static void onShow(AppContextHandle context, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt_lvgl_toolbar_create(parent, context);
|
||||
static void onShow(AppHandle app, void* data, lv_obj_t* parent) {
|
||||
lv_obj_t* toolbar = tt_lvgl_toolbar_create(parent, app);
|
||||
lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0);
|
||||
|
||||
lv_obj_t* label = lv_label_create(parent);
|
||||
@@ -14,15 +14,12 @@ static void onShow(AppContextHandle context, lv_obj_t* parent) {
|
||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
ExternalAppManifest manifest = {
|
||||
.name = "Hello World",
|
||||
.onShow = onShow
|
||||
};
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
tt_set_app_manifest(
|
||||
"Hello World",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
onShow,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
tt_app_register(&manifest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -848,20 +848,10 @@ int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_
|
||||
}
|
||||
|
||||
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
|
||||
if (x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) {
|
||||
if (x >= qrcode->size || y >= qrcode->size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t offset = y * qrcode->size + x;
|
||||
return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
uint8_t qrcode_getHexLength(QRCode *qrcode) {
|
||||
return ((qrcode->size * qrcode->size) + 7) / 4;
|
||||
}
|
||||
|
||||
void qrcode_getHex(QRCode *qrcode, char *result) {
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -4,8 +4,6 @@ Tactility is an operating system that focuses on the ESP32 microcontroller famil
|
||||
|
||||
See [https://tactility.one](https://tactility.one) for more information.
|
||||
|
||||
Status: pre-release
|
||||
|
||||
 
|
||||
|
||||
You can run built-in apps or start them from an SD card:
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| main branch | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We appreciate your efforts to responsibly disclose your findings, and we will make every effort to acknowledge your contributions.
|
||||
|
||||
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/bytewelder/tactility/security/advisories/new) tab.
|
||||
|
||||
You can expect a response indicating the next steps in handling your report. After the initial reply to your report, you'll be kept informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
||||
|
||||
Report security bugs in third-party dependencies to the person or team maintaining the module.
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/AppContext.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "Bundle.h"
|
||||
#include "Mutex.h"
|
||||
#include "app/AppContext.h"
|
||||
#include "app/AppManifest.h"
|
||||
#include "app/ElfApp.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -17,19 +18,6 @@ typedef enum {
|
||||
StateStopped // App is not in memory
|
||||
} State;
|
||||
|
||||
struct ResultHolder {
|
||||
Result result;
|
||||
std::shared_ptr<const Bundle> resultData;
|
||||
|
||||
explicit ResultHolder(Result result) : result(result), resultData(nullptr) {}
|
||||
|
||||
ResultHolder(Result result, std::shared_ptr<const Bundle> resultData) :
|
||||
result(result),
|
||||
resultData(std::move(resultData))
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread-safe app instance.
|
||||
*/
|
||||
@@ -37,8 +25,8 @@ class AppInstance : public AppContext {
|
||||
|
||||
private:
|
||||
|
||||
Mutex mutex = Mutex(Mutex::TypeNormal);
|
||||
const AppManifest& manifest;
|
||||
Mutex mutex = Mutex(Mutex::Type::Normal);
|
||||
const std::shared_ptr<AppManifest> manifest;
|
||||
State state = StateInitial;
|
||||
Flags flags = { .showStatusbar = true };
|
||||
/** @brief Optional parameters to start the app with
|
||||
@@ -46,22 +34,40 @@ private:
|
||||
* Do not mutate after app creation.
|
||||
*/
|
||||
std::shared_ptr<const tt::Bundle> _Nullable parameters;
|
||||
/** @brief @brief Contextual data related to the running app's instance
|
||||
* The app can attach its data to this.
|
||||
* The lifecycle is determined by the on_start and on_stop methods in the AppManifest.
|
||||
* These manifest methods can optionally allocate/free data that is attached here.
|
||||
*/
|
||||
std::shared_ptr<void> _Nullable data;
|
||||
std::unique_ptr<ResultHolder> _Nullable resultHolder;
|
||||
|
||||
std::shared_ptr<App> app;
|
||||
|
||||
static std::shared_ptr<app::App> createApp(
|
||||
const std::shared_ptr<app::AppManifest>& manifest
|
||||
) {
|
||||
if (manifest->location.isInternal()) {
|
||||
assert(manifest->createApp != nullptr);
|
||||
return manifest->createApp();
|
||||
} else if (manifest->location.isExternal()) {
|
||||
if (manifest->createApp != nullptr) {
|
||||
TT_LOG_W("", "Manifest specifies createApp, but this is not used with external apps");
|
||||
}
|
||||
#ifdef ESP_PLATFORM
|
||||
return app::createElfApp(manifest);
|
||||
#else
|
||||
tt_crash("not supported");
|
||||
#endif
|
||||
} else {
|
||||
tt_crash("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit AppInstance(const AppManifest& manifest) :
|
||||
manifest(manifest) {}
|
||||
|
||||
AppInstance(const AppManifest& manifest, std::shared_ptr<const Bundle> parameters) :
|
||||
explicit AppInstance(const std::shared_ptr<AppManifest>& manifest) :
|
||||
manifest(manifest),
|
||||
parameters(std::move(parameters)) {}
|
||||
app(createApp(manifest))
|
||||
{}
|
||||
|
||||
AppInstance(const std::shared_ptr<AppManifest>& manifest, std::shared_ptr<const Bundle> parameters) :
|
||||
manifest(manifest),
|
||||
parameters(std::move(parameters)),
|
||||
app(createApp(manifest)) {}
|
||||
|
||||
~AppInstance() override = default;
|
||||
|
||||
@@ -70,22 +76,15 @@ public:
|
||||
|
||||
const AppManifest& getManifest() const override;
|
||||
|
||||
Flags getFlags() const override;
|
||||
Flags getFlags() const;
|
||||
void setFlags(Flags flags);
|
||||
Flags& mutableFlags() { return flags; } // TODO: locking mechanism
|
||||
|
||||
std::shared_ptr<void> _Nullable getData() const override;
|
||||
void setData(std::shared_ptr<void> data) override;
|
||||
|
||||
std::shared_ptr<const Bundle> getParameters() const override;
|
||||
|
||||
void setResult(Result result) override;
|
||||
void setResult(Result result, std::shared_ptr<const Bundle> bundle) override;
|
||||
bool hasResult() const override;
|
||||
|
||||
std::unique_ptr<Paths> getPaths() const override;
|
||||
|
||||
std::unique_ptr<ResultHolder>& getResult() { return resultHolder; }
|
||||
std::shared_ptr<App> getApp() const override { return app; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
Mutex mutex = Mutex(Mutex::TypeRecursive);
|
||||
Mutex mutex = Mutex(Mutex::Type::Recursive);
|
||||
std::vector<dirent> dir_entries;
|
||||
std::string current_path;
|
||||
std::string selected_child_entry;
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
bool setEntriesForPath(const std::string& path);
|
||||
|
||||
const std::vector<dirent>& lockEntries() const {
|
||||
mutex.lock(TtWaitForever);
|
||||
mutex.lock();
|
||||
return dir_entries;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "State.h"
|
||||
#include "./State.h"
|
||||
|
||||
#include "app/AppManifest.h"
|
||||
#include <lvgl.h>
|
||||
#include <memory>
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
void onRenamePressed();
|
||||
void onDeletePressed();
|
||||
void onDirEntryListScrollBegin();
|
||||
void onResult(Result result, const Bundle& bundle);
|
||||
void onResult(Result result, std::unique_ptr<Bundle> bundle);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <TactilityCore.h>
|
||||
#include <Mutex.h>
|
||||
#include <Thread.h>
|
||||
#include "lvgl.h"
|
||||
#include "hal/i2c/I2c.h"
|
||||
#include "Timer.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
|
||||
#define TAG "i2cscanner"
|
||||
|
||||
enum ScanState {
|
||||
ScanStateInitial,
|
||||
ScanStateScanning,
|
||||
ScanStateStopped
|
||||
};
|
||||
|
||||
struct Data {
|
||||
|
||||
};
|
||||
|
||||
void onScanTimerFinished(std::shared_ptr<Data> data);
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "I2cScanner.h"
|
||||
#include "./I2cScannerPrivate.h"
|
||||
#include <memory>
|
||||
|
||||
namespace tt::app::i2cscanner {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user