mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-18 09:25:06 +00:00
Compare commits
6 Commits
1a6cc814a5
...
48f9ed4d73
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48f9ed4d73 | ||
|
|
4d245d42cf | ||
|
|
aa954da8a9 | ||
|
|
8400d37c07 | ||
|
|
e7e8c50406 | ||
|
|
e64f4ff16b |
@ -2,18 +2,15 @@ idf_component_register(
|
||||
INCLUDE_DIRS
|
||||
"Libraries/TactilityC/include"
|
||||
"Libraries/TactilityKernel/include"
|
||||
"Libraries/TactilityFreeRtos/include"
|
||||
"Libraries/lvgl/include"
|
||||
"Libraries/lvgl-module/include"
|
||||
REQUIRES esp_timer
|
||||
)
|
||||
|
||||
# Regular and core features
|
||||
add_prebuilt_library(TactilityC Libraries/TactilityC/binary/libTactilityC.a)
|
||||
add_prebuilt_library(TactilityKernel Libraries/TactilityKernel/binary/libTactilityKernel.a)
|
||||
add_prebuilt_library(lvgl Libraries/lvgl/binary/liblvgl.a)
|
||||
add_prebuilt_library(lvgl-module Libraries/lvgl-module/binary/liblvgl-module.a)
|
||||
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityC)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE TactilityKernel)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE lvgl)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE lvgl-module)
|
||||
|
||||
@ -14,4 +14,22 @@ macro(tactility_project project_name)
|
||||
if (NOT "$ENV{ESP_IDF_VERSION}" STREQUAL "${TACTILITY_SDK_IDF_VERSION}")
|
||||
message(FATAL_ERROR "ESP-IDF version of Tactility SDK (${TACTILITY_SDK_IDF_VERSION}) does not match current ESP-IDF version ($ENV{ESP_IDF_VERSION})")
|
||||
endif()
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"Libraries/TactilityFreeRtos"
|
||||
"Modules"
|
||||
"Drivers"
|
||||
)
|
||||
|
||||
set(COMPONENTS
|
||||
TactilityFreeRtos
|
||||
bm8563-module
|
||||
bm8563-module
|
||||
bmi270-module
|
||||
mpu6886-module
|
||||
pi4ioe5v6408-module
|
||||
qmi8658-module
|
||||
rx8130ce-module
|
||||
)
|
||||
|
||||
endmacro()
|
||||
|
||||
@ -5,6 +5,7 @@ import shutil
|
||||
import glob
|
||||
import subprocess
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
def map_copy(mappings, target_base):
|
||||
"""
|
||||
@ -60,6 +61,45 @@ def map_copy(mappings, target_base):
|
||||
os.makedirs(os.path.dirname(final_dst), exist_ok=True)
|
||||
shutil.copy2(src, final_dst)
|
||||
|
||||
def get_driver_mappings(driver_name):
|
||||
return [
|
||||
{'src': f'Drivers/{driver_name}/include/**', 'dst': f'Drivers/{driver_name}/include/'},
|
||||
{'src': f'Drivers/{driver_name}/*.md', 'dst': f'Drivers/{driver_name}/'},
|
||||
{'src': f'build/esp-idf/{driver_name}/lib{driver_name}.a', 'dst': f'Drivers/{driver_name}/binary/lib{driver_name}.a'},
|
||||
]
|
||||
|
||||
def get_module_mappings(module_name):
|
||||
return [
|
||||
{'src': f'Modules/{module_name}/include/**', 'dst': f'Modules/{module_name}/include/'},
|
||||
{'src': f'Modules/{module_name}/*.md', 'dst': f'Modules/{module_name}/'},
|
||||
{'src': f'build/esp-idf/{module_name}/lib{module_name}.a', 'dst': f'Modules/{module_name}/binary/lib{module_name}.a'},
|
||||
]
|
||||
|
||||
def create_module_cmakelists(module_name):
|
||||
return dedent(f'''
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
idf_component_register(
|
||||
INCLUDE_DIRS "include"
|
||||
)
|
||||
add_prebuilt_library({module_name} "binary/lib{module_name}.a")
|
||||
'''.format(module_name=module_name))
|
||||
|
||||
def write_module_cmakelists(path, content):
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
def add_driver(target_path, driver_name):
|
||||
mappings = get_driver_mappings(driver_name)
|
||||
map_copy(mappings, target_path)
|
||||
cmakelists_content = create_module_cmakelists(driver_name)
|
||||
write_module_cmakelists(os.path.join(target_path, f"Drivers/{driver_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def add_module(target_path, module_name):
|
||||
mappings = get_module_mappings(module_name)
|
||||
map_copy(mappings, target_path)
|
||||
cmakelists_content = create_module_cmakelists(module_name)
|
||||
write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: release-sdk.py [target_path]")
|
||||
@ -85,12 +125,7 @@ def main():
|
||||
{'src': 'build/esp-idf/TactilityKernel/libTactilityKernel.a', 'dst': 'Libraries/TactilityKernel/binary/'},
|
||||
{'src': 'TactilityKernel/include/**', 'dst': 'Libraries/TactilityKernel/include/'},
|
||||
{'src': 'TactilityKernel/CMakeLists.txt', 'dst': 'Libraries/TactilityKernel/'},
|
||||
{'src': 'TactilityKernel/LICENSE*.*', 'dst': 'Libraries/TactilityKernel/'},
|
||||
# lvgl-module
|
||||
{'src': 'build/esp-idf/lvgl-module/liblvgl-module.a', 'dst': 'Libraries/lvgl-module/binary/'},
|
||||
{'src': 'Modules/lvgl-module/include/**', 'dst': 'Libraries/lvgl-module/include/'},
|
||||
{'src': 'Modules/lvgl-module/CMakeLists.txt', 'dst': 'Libraries/lvgl-module/'},
|
||||
{'src': 'Modules/lvgl-module/LICENSE*.*', 'dst': 'Libraries/lvgl-module/'},
|
||||
{'src': 'TactilityKernel/*.md', 'dst': 'Libraries/TactilityKernel/'},
|
||||
# lvgl (basics)
|
||||
{'src': 'build/esp-idf/lvgl__lvgl/liblvgl__lvgl.a', 'dst': 'Libraries/lvgl/binary/liblvgl.a'},
|
||||
{'src': 'Libraries/lvgl/lvgl.h', 'dst': 'Libraries/lvgl/include/'},
|
||||
@ -108,6 +143,18 @@ def main():
|
||||
|
||||
map_copy(mappings, target_path)
|
||||
|
||||
# Modules
|
||||
add_module(target_path, "lvgl-module")
|
||||
|
||||
# Drivers
|
||||
add_driver(target_path, "bm8563-module")
|
||||
add_driver(target_path, "bm8563-module")
|
||||
add_driver(target_path, "bmi270-module")
|
||||
add_driver(target_path, "mpu6886-module")
|
||||
add_driver(target_path, "pi4ioe5v6408-module")
|
||||
add_driver(target_path, "qmi8658-module")
|
||||
add_driver(target_path, "rx8130ce-module")
|
||||
|
||||
# Output ESP-IDF SDK version to file
|
||||
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
||||
with open(os.path.join(target_path, "idf-version.txt"), "a") as f:
|
||||
|
||||
@ -613,10 +613,12 @@ function renderDashboard(data) {
|
||||
const psramUsed = data.psram.total - data.psram.free;
|
||||
const psramPercent = data.psram.total > 0 ? (psramUsed / data.psram.total) * 100 : 0;
|
||||
const hasPsram = data.psram.total > 0;
|
||||
const dataUsed = data.storage.data.mounted ? (data.storage.data.total - data.storage.data.free) : 0;
|
||||
const dataPercent = data.storage.data.mounted && data.storage.data.total > 0 ? (dataUsed / data.storage.data.total) * 100 : 0;
|
||||
const sdUsed = data.storage.sdcard.mounted ? (data.storage.sdcard.total - data.storage.sdcard.free) : 0;
|
||||
const sdPercent = data.storage.sdcard.mounted && data.storage.sdcard.total > 0 ? (sdUsed / data.storage.sdcard.total) * 100 : 0;
|
||||
const dataPartition = data.storage?.data ?? {};
|
||||
const sdcard = data.storage?.sdcard ?? {};
|
||||
const dataUsed = dataPartition.mounted ? (dataPartition.total - dataPartition.free) : 0;
|
||||
const dataPercent = dataPartition.mounted && dataPartition.total > 0 ? (dataUsed / dataPartition.total) * 100 : 0;
|
||||
const sdUsed = sdcard.mounted ? (sdcard.total - sdcard.free) : 0;
|
||||
const sdPercent = sdcard.mounted && sdcard.total > 0 ? (sdUsed / sdcard.total) * 100 : 0;
|
||||
|
||||
versionEl.textContent = `Tactility v${data.firmware.version} | ESP-IDF v${data.firmware.idf_version}`;
|
||||
|
||||
@ -711,20 +713,20 @@ function renderDashboard(data) {
|
||||
<h2>Storage</h2>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Data Partition</span>
|
||||
<span class="stat-value ${data.storage.data.mounted ? getUsageClass(dataPercent) : ''}">${
|
||||
data.storage.data.mounted ? formatBytes(data.storage.data.free) + ' free' : 'Not mounted'
|
||||
<span class="stat-value ${dataPartition.mounted ? getUsageClass(dataPercent) : ''}">${
|
||||
dataPartition.mounted ? formatBytes(dataPartition.free) + ' free' : 'Not mounted'
|
||||
}</span>
|
||||
</div>
|
||||
${data.storage.data.mounted ? `<div class="progress-bar">
|
||||
${dataPartition.mounted ? `<div class="progress-bar">
|
||||
<div class="progress-fill ${getUsageClass(dataPercent)}" style="width: ${dataPercent}%"></div>
|
||||
</div>` : ''}
|
||||
<div class="stat" style="margin-top: 15px">
|
||||
<span class="stat-label">SD Card</span>
|
||||
<span class="stat-value ${data.storage.sdcard.mounted ? getUsageClass(sdPercent) : ''}">${
|
||||
data.storage.sdcard.mounted ? formatBytes(data.storage.sdcard.free) + ' free' : 'Not mounted'
|
||||
<span class="stat-value ${sdcard.mounted ? getUsageClass(sdPercent) : ''}">${
|
||||
sdcard.mounted ? formatBytes(sdcard.free) + ' free' : 'Not mounted'
|
||||
}</span>
|
||||
</div>
|
||||
${data.storage.sdcard.mounted ? `<div class="progress-bar">
|
||||
${sdcard.mounted ? `<div class="progress-bar">
|
||||
<div class="progress-fill ${getUsageClass(sdPercent)}" style="width: ${sdPercent}%"></div>
|
||||
</div>` : ''}
|
||||
</div>
|
||||
|
||||
@ -20,3 +20,4 @@ dpi=187
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
|
||||
@ -26,3 +26,4 @@ warningMessage=
|
||||
[lvgl]
|
||||
theme=DefaultDark
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
|
||||
@ -21,3 +21,4 @@ dpi=187
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
|
||||
@ -21,3 +21,4 @@ dpi=187
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
# Add all driver modules because the generic devices are used to build the SDK
|
||||
- Drivers/bm8563-module
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/qmi8658-module
|
||||
- Drivers/rx8130ce-module
|
||||
dts: generic,esp32.dts
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
# Add all driver modules because the generic devices are used to build the SDK
|
||||
- Drivers/bm8563-module
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/qmi8658-module
|
||||
- Drivers/rx8130ce-module
|
||||
dts: generic,esp32c6.dts
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
# Add all driver modules because the generic devices are used to build the SDK
|
||||
- Drivers/bm8563-module
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/qmi8658-module
|
||||
- Drivers/rx8130ce-module
|
||||
dts: generic,esp32p4.dts
|
||||
|
||||
@ -1,3 +1,10 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
# Add all driver modules because the generic devices are used to build the SDK
|
||||
- Drivers/bm8563-module
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/qmi8658-module
|
||||
- Drivers/rx8130ce-module
|
||||
dts: generic,esp32s3.dts
|
||||
|
||||
@ -20,3 +20,4 @@ dpi=187
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
fontSize=18
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
pin-clk = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||
pin-cmd = <&gpio0 11 GPIO_FLAG_NONE>;
|
||||
pin-d0 = <&gpio0 13 GPIO_FLAG_NONE>;
|
||||
pin-d3 = <&gpio0 10 GPIO_FLAG_NONE>;
|
||||
bus-width = <1>;
|
||||
pullups;
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/bmi270-module
|
||||
dts: m5stack,cardputer-adv.dts
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/bmi270.h>
|
||||
|
||||
// Reference: https://docs.m5stack.com/en/core/Cardputer-Adv
|
||||
/ {
|
||||
@ -23,6 +24,11 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 8 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 9 GPIO_FLAG_NONE>;
|
||||
|
||||
bmi270 {
|
||||
compatible = "bosch,bmi270";
|
||||
reg = <0x68>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_port_a {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/bm8563-module
|
||||
dts: m5stack,core2.dts
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/mpu6886.h>
|
||||
#include <bindings/bm8563.h>
|
||||
|
||||
// Reference: https://docs.m5stack.com/en/core/Core2
|
||||
/ {
|
||||
@ -23,6 +25,16 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||
|
||||
mpu6886 {
|
||||
compatible = "invensense,mpu6886";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
bm8563 {
|
||||
compatible = "belling,bm8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_port_a {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/bm8563-module
|
||||
dts: m5stack,cores3.dts
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/bmi270.h>
|
||||
#include <bindings/bm8563.h>
|
||||
|
||||
// Reference: https://docs.m5stack.com/en/core/CoreS3
|
||||
/ {
|
||||
@ -23,6 +25,16 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 12 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 11 GPIO_FLAG_NONE>;
|
||||
|
||||
bmi270 {
|
||||
compatible = "bosch,bmi270";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
bm8563 {
|
||||
compatible = "belling,bm8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_port_a {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/bm8563-module
|
||||
dts: m5stack,papers3.dts
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <bindings/bmi270.h>
|
||||
#include <bindings/bm8563.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
@ -20,6 +22,16 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 41 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 42 GPIO_FLAG_NONE>;
|
||||
|
||||
bmi270 {
|
||||
compatible = "bosch,bmi270";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
bm8563 {
|
||||
compatible = "belling,bm8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/bm8563-module
|
||||
dts: m5stack,stickc-plus.dts
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/mpu6886.h>
|
||||
#include <bindings/bm8563.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
@ -21,6 +23,16 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||
|
||||
mpu6886 {
|
||||
compatible = "invensense,mpu6886";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
bm8563 {
|
||||
compatible = "belling,bm8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_grove {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/mpu6886-module
|
||||
- Drivers/bm8563-module
|
||||
dts: m5stack,stickc-plus2.dts
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/mpu6886.h>
|
||||
#include <bindings/bm8563.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
@ -20,6 +22,16 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 21 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 22 GPIO_FLAG_NONE>;
|
||||
|
||||
mpu6886 {
|
||||
compatible = "invensense,mpu6886";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
bm8563 {
|
||||
compatible = "belling,bm8563";
|
||||
reg = <0x51>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_grove {
|
||||
|
||||
7
Devices/m5stack-sticks3/CMakeLists.txt
Normal file
7
Devices/m5stack-sticks3/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port esp_lcd ST7789 PwmBacklight ButtonControl m5pm1-module
|
||||
)
|
||||
26
Devices/m5stack-sticks3/Source/Configuration.cpp
Normal file
26
Devices/m5stack-sticks3/Source/Configuration.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "devices/Display.h"
|
||||
#include "devices/Power.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#include <Tactility/hal/Configuration.h>
|
||||
#include <ButtonControl.h>
|
||||
#include <PwmBacklight.h>
|
||||
|
||||
using namespace tt::hal;
|
||||
|
||||
bool initBoot() {
|
||||
return driver::pwmbacklight::init(GPIO_NUM_38, 512);
|
||||
}
|
||||
|
||||
static DeviceVector createDevices() {
|
||||
return {
|
||||
createPower(),
|
||||
ButtonControl::createTwoButtonControl(11, 12), // top button, side button
|
||||
createDisplay()
|
||||
};
|
||||
}
|
||||
|
||||
extern const Configuration hardwareConfiguration = {
|
||||
.initBoot = initBoot,
|
||||
.createDevices = createDevices
|
||||
};
|
||||
32
Devices/m5stack-sticks3/Source/devices/Display.cpp
Normal file
32
Devices/m5stack-sticks3/Source/devices/Display.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "Display.h"
|
||||
|
||||
#include <PwmBacklight.h>
|
||||
#include <St7789Display.h>
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
St7789Display::Configuration panel_configuration = {
|
||||
.horizontalResolution = LCD_HORIZONTAL_RESOLUTION,
|
||||
.verticalResolution = LCD_VERTICAL_RESOLUTION,
|
||||
.gapX = 52,
|
||||
.gapY = 40,
|
||||
.swapXY = false,
|
||||
.mirrorX = false,
|
||||
.mirrorY = false,
|
||||
.invertColor = true,
|
||||
.bufferSize = LCD_BUFFER_SIZE,
|
||||
.touch = nullptr,
|
||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
||||
.resetPin = LCD_PIN_RESET,
|
||||
.lvglSwapBytes = false
|
||||
};
|
||||
|
||||
auto spi_configuration = std::make_shared<St7789Display::SpiConfiguration>(St7789Display::SpiConfiguration {
|
||||
.spiHostDevice = LCD_SPI_HOST,
|
||||
.csPin = LCD_PIN_CS,
|
||||
.dcPin = LCD_PIN_DC,
|
||||
.pixelClockFrequency = 40'000'000,
|
||||
.transactionQueueDepth = 10
|
||||
});
|
||||
|
||||
return std::make_shared<St7789Display>(panel_configuration, spi_configuration);
|
||||
}
|
||||
17
Devices/m5stack-sticks3/Source/devices/Display.h
Normal file
17
Devices/m5stack-sticks3/Source/devices/Display.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Tactility/hal/display/DisplayDevice.h"
|
||||
#include <memory>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
constexpr auto LCD_SPI_HOST = SPI2_HOST;
|
||||
constexpr auto LCD_PIN_CS = GPIO_NUM_41;
|
||||
constexpr auto LCD_PIN_DC = GPIO_NUM_45;
|
||||
constexpr auto LCD_PIN_RESET = GPIO_NUM_21;
|
||||
constexpr auto LCD_HORIZONTAL_RESOLUTION = 135;
|
||||
constexpr auto LCD_VERTICAL_RESOLUTION = 240;
|
||||
constexpr auto LCD_BUFFER_HEIGHT = LCD_VERTICAL_RESOLUTION / 3;
|
||||
constexpr auto LCD_BUFFER_SIZE = LCD_HORIZONTAL_RESOLUTION * LCD_BUFFER_HEIGHT;
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
|
||||
94
Devices/m5stack-sticks3/Source/devices/Power.cpp
Normal file
94
Devices/m5stack-sticks3/Source/devices/Power.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "Power.h"
|
||||
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
#include <drivers/m5pm1.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
using namespace tt::hal::power;
|
||||
|
||||
static constexpr auto* TAG = "StickS3Power";
|
||||
|
||||
static constexpr float MIN_BATTERY_VOLTAGE_MV = 3300.0f;
|
||||
static constexpr float MAX_BATTERY_VOLTAGE_MV = 4200.0f;
|
||||
|
||||
class StickS3Power final : public PowerDevice {
|
||||
public:
|
||||
explicit StickS3Power(::Device* m5pm1Device) : m5pm1(m5pm1Device) {}
|
||||
|
||||
std::string getName() const override { return "M5Stack StickS3 Power"; }
|
||||
std::string getDescription() const override { return "Battery monitoring via M5PM1 over I2C"; }
|
||||
|
||||
bool supportsMetric(MetricType type) const override {
|
||||
switch (type) {
|
||||
using enum MetricType;
|
||||
case BatteryVoltage:
|
||||
case ChargeLevel:
|
||||
case IsCharging:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool getMetric(MetricType type, MetricData& data) override {
|
||||
switch (type) {
|
||||
using enum MetricType;
|
||||
|
||||
case BatteryVoltage: {
|
||||
uint16_t mv = 0;
|
||||
if (m5pm1_get_battery_voltage(m5pm1, &mv) != ERROR_NONE) return false;
|
||||
data.valueAsUint32 = mv;
|
||||
return true;
|
||||
}
|
||||
|
||||
case ChargeLevel: {
|
||||
uint16_t mv = 0;
|
||||
if (m5pm1_get_battery_voltage(m5pm1, &mv) != ERROR_NONE) return false;
|
||||
float voltage = static_cast<float>(mv);
|
||||
if (voltage >= MAX_BATTERY_VOLTAGE_MV) {
|
||||
data.valueAsUint8 = 100;
|
||||
} else if (voltage <= MIN_BATTERY_VOLTAGE_MV) {
|
||||
data.valueAsUint8 = 0;
|
||||
} else {
|
||||
float factor = (voltage - MIN_BATTERY_VOLTAGE_MV) / (MAX_BATTERY_VOLTAGE_MV - MIN_BATTERY_VOLTAGE_MV);
|
||||
data.valueAsUint8 = static_cast<uint8_t>(factor * 100.0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case IsCharging: {
|
||||
bool charging = false;
|
||||
if (m5pm1_is_charging(m5pm1, &charging) != ERROR_NONE) {
|
||||
LOG_W(TAG, "Failed to read charging status");
|
||||
return false;
|
||||
}
|
||||
data.valueAsBool = charging;
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool supportsPowerOff() const override { return true; }
|
||||
|
||||
void powerOff() override {
|
||||
LOG_W(TAG, "Powering off via M5PM1");
|
||||
if (m5pm1_shutdown(m5pm1) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to send power-off command");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
::Device* m5pm1;
|
||||
};
|
||||
|
||||
std::shared_ptr<PowerDevice> createPower() {
|
||||
auto* m5pm1 = device_find_by_name("m5pm1");
|
||||
if (m5pm1 == nullptr) {
|
||||
LOG_E(TAG, "m5pm1 device not found");
|
||||
}
|
||||
return std::make_shared<StickS3Power>(m5pm1);
|
||||
}
|
||||
6
Devices/m5stack-sticks3/Source/devices/Power.h
Normal file
6
Devices/m5stack-sticks3/Source/devices/Power.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Tactility/hal/power/PowerDevice.h>
|
||||
|
||||
std::shared_ptr<tt::hal::power::PowerDevice> createPower();
|
||||
23
Devices/m5stack-sticks3/Source/module.cpp
Normal file
23
Devices/m5stack-sticks3/Source/module.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
static error_t start() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
// Empty for now
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
struct Module m5stack_sticks3_module = {
|
||||
.name = "m5stack-sticks3",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = nullptr,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
25
Devices/m5stack-sticks3/device.properties
Normal file
25
Devices/m5stack-sticks3/device.properties
Normal file
@ -0,0 +1,25 @@
|
||||
[general]
|
||||
vendor=M5Stack
|
||||
name=StickS3
|
||||
|
||||
[apps]
|
||||
launcherAppId=Launcher
|
||||
autoStartAppId=ApWebServer
|
||||
|
||||
[hardware]
|
||||
target=ESP32S3
|
||||
flashSize=8MB
|
||||
spiRam=true
|
||||
spiRamMode=OCT
|
||||
spiRamSpeed=80M
|
||||
esptoolFlashFreq=80M
|
||||
tinyUsb=true
|
||||
|
||||
[display]
|
||||
size=1.14"
|
||||
shape=rectangle
|
||||
dpi=242
|
||||
|
||||
[lvgl]
|
||||
colorDepth=16
|
||||
uiDensity=compact
|
||||
5
Devices/m5stack-sticks3/devicetree.yaml
Normal file
5
Devices/m5stack-sticks3/devicetree.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/m5pm1-module
|
||||
dts: m5stack,sticks3.dts
|
||||
71
Devices/m5stack-sticks3/m5stack,sticks3.dts
Normal file
71
Devices/m5stack-sticks3/m5stack,sticks3.dts
Normal file
@ -0,0 +1,71 @@
|
||||
/dts-v1/;
|
||||
#include <tactility/bindings/root.h>
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_i2s.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <tactility/bindings/esp32_uart.h>
|
||||
#include <bindings/bmi270.h>
|
||||
#include <bindings/m5pm1.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
model = "M5Stack StickS3";
|
||||
|
||||
gpio0 {
|
||||
compatible = "espressif,esp32-gpio";
|
||||
gpio-count = <49>;
|
||||
};
|
||||
|
||||
i2c_internal {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_0>;
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 47 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 48 GPIO_FLAG_NONE>;
|
||||
|
||||
m5pm1 {
|
||||
compatible = "m5stack,m5pm1";
|
||||
reg = <0x6E>;
|
||||
};
|
||||
|
||||
bmi270 {
|
||||
compatible = "bosch,bmi270";
|
||||
reg = <0x68>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_grove {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_1>;
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 9 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 10 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
spi0 {
|
||||
compatible = "espressif,esp32-spi";
|
||||
host = <SPI2_HOST>;
|
||||
pin-mosi = <&gpio0 39 GPIO_FLAG_NONE>;
|
||||
pin-sclk = <&gpio0 40 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
// Speaker and microphone (ES8311)
|
||||
i2s0 {
|
||||
compatible = "espressif,esp32-i2s";
|
||||
port = <I2S_NUM_0>;
|
||||
pin-bclk = <&gpio0 17 GPIO_FLAG_NONE>;
|
||||
pin-ws = <&gpio0 15 GPIO_FLAG_NONE>;
|
||||
pin-data-out = <&gpio0 14 GPIO_FLAG_NONE>;
|
||||
pin-data-in = <&gpio0 16 GPIO_FLAG_NONE>;
|
||||
pin-mclk = <&gpio0 18 GPIO_FLAG_NONE>;
|
||||
};
|
||||
|
||||
uart_grove: uart1 {
|
||||
compatible = "espressif,esp32-uart";
|
||||
status = "disabled";
|
||||
port = <UART_NUM_1>;
|
||||
pin-tx = <&gpio0 9 GPIO_FLAG_NONE>;
|
||||
pin-rx = <&gpio0 10 GPIO_FLAG_NONE>;
|
||||
};
|
||||
};
|
||||
@ -3,5 +3,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS "Source"
|
||||
REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c GT911 PwmBacklight driver vfs fatfs
|
||||
REQUIRES Tactility esp_lvgl_port esp_lcd EspLcdCompat esp_lcd_ili9881c esp_lcd_st7123 esp_lcd_touch_st7123 GT911 PwmBacklight driver vfs fatfs
|
||||
)
|
||||
|
||||
@ -102,11 +102,17 @@ static void initExpander0(::Device* io_expander0) {
|
||||
static void initExpander1(::Device* io_expander1) {
|
||||
|
||||
auto* c6_wlan_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_C6_WLAN_ENABLE, GPIO_OWNER_GPIO);
|
||||
check(c6_wlan_enable_pin);
|
||||
auto* usb_a_5v_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_USB_A_5V_ENABLE, GPIO_OWNER_GPIO);
|
||||
check(usb_a_5v_enable_pin);
|
||||
auto* device_power_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_DEVICE_POWER, GPIO_OWNER_GPIO);
|
||||
check(device_power_pin);
|
||||
auto* ip2326_ncharge_qc_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_NCHG_QC_EN, GPIO_OWNER_GPIO);
|
||||
check(ip2326_ncharge_qc_enable_pin);
|
||||
auto* ip2326_charge_state_led_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_STAT_LED, GPIO_OWNER_GPIO);
|
||||
check(ip2326_charge_state_led_pin);
|
||||
auto* ip2326_charge_enable_pin = gpio_descriptor_acquire(io_expander1, GPIO_EXP1_PIN_IP2326_CHG_EN, GPIO_OWNER_GPIO);
|
||||
check(ip2326_charge_enable_pin);
|
||||
|
||||
gpio_descriptor_set_flags(c6_wlan_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
|
||||
gpio_descriptor_set_flags(usb_a_5v_enable_pin, GPIO_FLAG_DIRECTION_OUTPUT);
|
||||
|
||||
45
Devices/m5stack-tab5/Source/devices/Detect.cpp
Normal file
45
Devices/m5stack-tab5/Source/devices/Detect.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "Detect.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <esp_lcd_touch_gt911.h>
|
||||
#include <esp_lcd_touch_st7123.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("Tab5Detect");
|
||||
|
||||
Tab5Variant detectVariant() {
|
||||
// Allow time for touch IC to fully boot after expander reset in initBoot().
|
||||
// 100ms is enough for I2C ACK (probe) but cold power-on needs ~300ms before
|
||||
// register reads (read_fw_info) succeed reliably.
|
||||
vTaskDelay(pdMS_TO_TICKS(300));
|
||||
|
||||
auto* i2c0 = device_find_by_name("i2c0");
|
||||
check(i2c0);
|
||||
|
||||
constexpr auto PROBE_TIMEOUT = pdMS_TO_TICKS(50);
|
||||
|
||||
for (int attempt = 0; attempt < 3; ++attempt) {
|
||||
// GT911 address depends on INT pin state during reset:
|
||||
// GPIO 23 has a pull-up resistor to 3V3, so INT is high at reset → GT911 uses 0x5D (primary)
|
||||
// It may also appear at 0x14 (backup) if the pin happened to be driven low
|
||||
if (i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, PROBE_TIMEOUT) == ERROR_NONE ||
|
||||
i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, PROBE_TIMEOUT) == ERROR_NONE) {
|
||||
LOGGER.info("Detected GT911 touch — using ILI9881C display");
|
||||
return Tab5Variant::Ili9881c_Gt911;
|
||||
}
|
||||
|
||||
// Probe for ST7123 touch (new variant)
|
||||
if (i2c_controller_has_device_at_address(i2c0, ESP_LCD_TOUCH_IO_I2C_ST7123_ADDRESS, PROBE_TIMEOUT) == ERROR_NONE) {
|
||||
LOGGER.info("Detected ST7123 touch — using ST7123 display");
|
||||
return Tab5Variant::St7123;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
LOGGER.warn("No known touch controller detected, defaulting to ST7123");
|
||||
return Tab5Variant::St7123;
|
||||
}
|
||||
8
Devices/m5stack-tab5/Source/devices/Detect.h
Normal file
8
Devices/m5stack-tab5/Source/devices/Detect.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
enum class Tab5Variant {
|
||||
Ili9881c_Gt911, // Older variant
|
||||
St7123, // Newer variant (default)
|
||||
};
|
||||
|
||||
[[nodiscard]] Tab5Variant detectVariant();
|
||||
@ -1,16 +1,22 @@
|
||||
#include "Detect.h"
|
||||
#include "Display.h"
|
||||
#include "Ili9881cDisplay.h"
|
||||
#include "St7123Display.h"
|
||||
#include "St7123Touch.h"
|
||||
|
||||
#include <Gt911Touch.h>
|
||||
#include <PwmBacklight.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/Mutex.h>
|
||||
#include <Tactility/hal/gpio/Gpio.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("Tab5Display");
|
||||
|
||||
constexpr auto LCD_PIN_RESET = GPIO_NUM_0; // Match P4 EV board reset line
|
||||
constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_22;
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createGt911Touch() {
|
||||
auto configuration = std::make_unique<Gt911Touch::Configuration>(
|
||||
I2C_NUM_0,
|
||||
720,
|
||||
@ -19,25 +25,46 @@ static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
|
||||
false, // mirrorX
|
||||
false, // mirrorY
|
||||
GPIO_NUM_NC, // reset pin
|
||||
GPIO_NUM_NC // "GPIO_NUM_23 cannot be used due to resistor to 3V3" https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L76234
|
||||
GPIO_NUM_NC // "GPIO_NUM_23 cannot be used due to resistor to 3V3"
|
||||
// https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L76234
|
||||
);
|
||||
|
||||
return std::make_shared<Gt911Touch>(std::move(configuration));
|
||||
}
|
||||
|
||||
static std::shared_ptr<tt::hal::touch::TouchDevice> createSt7123Touch() {
|
||||
auto configuration = std::make_unique<St7123Touch::Configuration>(
|
||||
I2C_NUM_0,
|
||||
720,
|
||||
1280,
|
||||
false, // swapXY
|
||||
false, // mirrorX
|
||||
false, // mirrorY
|
||||
GPIO_NUM_23 // interrupt pin
|
||||
);
|
||||
return std::make_shared<St7123Touch>(std::move(configuration));
|
||||
}
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
// Initialize PWM backlight
|
||||
if (!driver::pwmbacklight::init(LCD_PIN_BACKLIGHT, 5000, LEDC_TIMER_1, LEDC_CHANNEL_0)) {
|
||||
tt::Logger("Tab5").warn("Failed to initialize backlight");
|
||||
LOGGER.warn("Failed to initialize backlight");
|
||||
}
|
||||
|
||||
auto touch = createTouch();
|
||||
Tab5Variant variant = detectVariant();
|
||||
|
||||
// Work-around to init touch : interrupt pin must be set to low
|
||||
// Note: There is a resistor to 3V3 on interrupt pin which is blocking GT911 touch
|
||||
// See https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L777
|
||||
tt::hal::gpio::configure(23, tt::hal::gpio::Mode::Output, true, false);
|
||||
tt::hal::gpio::setLevel(23, false);
|
||||
std::shared_ptr<tt::hal::touch::TouchDevice> touch;
|
||||
|
||||
if (variant == Tab5Variant::St7123) {
|
||||
touch = createSt7123Touch();
|
||||
} else {
|
||||
touch = createGt911Touch();
|
||||
|
||||
// Work-around to init GT911 touch: interrupt pin must be set to low
|
||||
// Note: There is a resistor to 3V3 on interrupt pin which is blocking GT911 touch
|
||||
// See https://github.com/espressif/esp-bsp/blob/ad668c765cbad177495a122181df0a70ff9f8f61/bsp/m5stack_tab5/src/m5stack_tab5.c#L777
|
||||
tt::hal::gpio::configure(23, tt::hal::gpio::Mode::Output, true, false);
|
||||
tt::hal::gpio::setLevel(23, false);
|
||||
}
|
||||
|
||||
auto configuration = std::make_shared<EspLcdConfiguration>(EspLcdConfiguration {
|
||||
.horizontalResolution = 720,
|
||||
@ -50,6 +77,8 @@ std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
.mirrorY = false,
|
||||
.invertColor = false,
|
||||
.bufferSize = 0, // 0 = default (1/10 of screen)
|
||||
.swRotate = (variant == Tab5Variant::St7123), // ST7123 MIPI-DSI panel does not support hardware swap_xy
|
||||
.buffSpiram = (variant == Tab5Variant::St7123), // sw_rotate needs a 3rd buffer; use PSRAM to avoid OOM in internal SRAM
|
||||
.touch = touch,
|
||||
.backlightDutyFunction = driver::pwmbacklight::setBacklightDuty,
|
||||
.resetPin = LCD_PIN_RESET,
|
||||
@ -59,6 +88,13 @@ std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay() {
|
||||
.bitsPerPixel = 16
|
||||
});
|
||||
|
||||
const auto display = std::make_shared<Ili9881cDisplay>(configuration);
|
||||
return std::static_pointer_cast<tt::hal::display::DisplayDevice>(display);
|
||||
if (variant == Tab5Variant::St7123) {
|
||||
return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
|
||||
std::make_shared<St7123Display>(configuration)
|
||||
);
|
||||
} else {
|
||||
return std::static_pointer_cast<tt::hal::display::DisplayDevice>(
|
||||
std::make_shared<Ili9881cDisplay>(configuration)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include "Ili9881cDisplay.h"
|
||||
#include "disp_init_data.h"
|
||||
#include "ili9881_init_data.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <esp_lcd_ili9881c.h>
|
||||
@ -47,6 +47,8 @@ bool Ili9881cDisplay::createMipiDsiBus() {
|
||||
|
||||
if (esp_lcd_new_dsi_bus(&bus_config, &mipiDsiBus) != ESP_OK) {
|
||||
LOGGER.error("Failed to create bus");
|
||||
esp_ldo_release_channel(ldoChannel);
|
||||
ldoChannel = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -67,6 +69,10 @@ bool Ili9881cDisplay::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) {
|
||||
|
||||
if (esp_lcd_new_panel_io_dbi(mipiDsiBus, &dbi_config, &ioHandle) != ESP_OK) {
|
||||
LOGGER.error("Failed to create panel IO");
|
||||
esp_lcd_del_dsi_bus(mipiDsiBus);
|
||||
mipiDsiBus = nullptr;
|
||||
esp_ldo_release_channel(ldoChannel);
|
||||
ldoChannel = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -108,15 +114,15 @@ bool Ili9881cDisplay::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, cons
|
||||
.vsync_back_porch = 20,
|
||||
.vsync_front_porch = 20,
|
||||
},
|
||||
.flags {
|
||||
.flags = {
|
||||
.use_dma2d = 1, // TODO: true?
|
||||
.disable_lp = 0,
|
||||
}
|
||||
};
|
||||
|
||||
ili9881c_vendor_config_t vendor_config = {
|
||||
.init_cmds = disp_init_data,
|
||||
.init_cmds_size = std::size(disp_init_data),
|
||||
.init_cmds = ili9881_init_data,
|
||||
.init_cmds_size = std::size(ili9881_init_data),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipiDsiBus,
|
||||
.dpi_config = &dpi_config,
|
||||
|
||||
148
Devices/m5stack-tab5/Source/devices/St7123Display.cpp
Normal file
148
Devices/m5stack-tab5/Source/devices/St7123Display.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#include "St7123Display.h"
|
||||
#include "st7123_init_data.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <esp_lcd_st7123.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("St7123");
|
||||
|
||||
St7123Display::~St7123Display() {
|
||||
// TODO: This should happen during ::stop(), but this isn't currently exposed
|
||||
if (mipiDsiBus != nullptr) {
|
||||
esp_lcd_del_dsi_bus(mipiDsiBus);
|
||||
mipiDsiBus = nullptr;
|
||||
}
|
||||
if (ldoChannel != nullptr) {
|
||||
esp_ldo_release_channel(ldoChannel);
|
||||
ldoChannel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool St7123Display::createMipiDsiBus() {
|
||||
esp_ldo_channel_config_t ldo_mipi_phy_config = {
|
||||
.chan_id = 3,
|
||||
.voltage_mv = 2500,
|
||||
.flags = {
|
||||
.adjustable = 0,
|
||||
.owned_by_hw = 0,
|
||||
.bypass = 0
|
||||
}
|
||||
};
|
||||
|
||||
if (esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldoChannel) != ESP_OK) {
|
||||
LOGGER.error("Failed to acquire LDO channel for MIPI DSI PHY");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGGER.info("Powered on");
|
||||
|
||||
const esp_lcd_dsi_bus_config_t bus_config = {
|
||||
.bus_id = 0,
|
||||
.num_data_lanes = 2,
|
||||
.phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT,
|
||||
.lane_bit_rate_mbps = 965 // ST7123 lane bitrate per M5Stack BSP
|
||||
};
|
||||
|
||||
if (esp_lcd_new_dsi_bus(&bus_config, &mipiDsiBus) != ESP_OK) {
|
||||
LOGGER.error("Failed to create bus");
|
||||
esp_ldo_release_channel(ldoChannel);
|
||||
ldoChannel = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGGER.info("Bus created");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool St7123Display::createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) {
|
||||
if (mipiDsiBus == nullptr) {
|
||||
if (!createMipiDsiBus()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// DBI interface for LCD commands/parameters (8-bit cmd/param per ST7123 spec)
|
||||
esp_lcd_dbi_io_config_t dbi_config = {
|
||||
.virtual_channel = 0,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
};
|
||||
|
||||
if (esp_lcd_new_panel_io_dbi(mipiDsiBus, &dbi_config, &ioHandle) != ESP_OK) {
|
||||
LOGGER.error("Failed to create panel IO");
|
||||
esp_lcd_del_dsi_bus(mipiDsiBus);
|
||||
mipiDsiBus = nullptr;
|
||||
esp_ldo_release_channel(ldoChannel);
|
||||
ldoChannel = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_lcd_panel_dev_config_t St7123Display::createPanelConfig(std::shared_ptr<EspLcdConfiguration> espLcdConfiguration, gpio_num_t resetPin) {
|
||||
return {
|
||||
.reset_gpio_num = resetPin,
|
||||
.rgb_ele_order = espLcdConfiguration->rgbElementOrder,
|
||||
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
|
||||
.bits_per_pixel = 16,
|
||||
.flags = {
|
||||
.reset_active_high = 0
|
||||
},
|
||||
.vendor_config = nullptr
|
||||
};
|
||||
}
|
||||
|
||||
bool St7123Display::createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) {
|
||||
esp_lcd_dpi_panel_config_t dpi_config = {
|
||||
.virtual_channel = 0,
|
||||
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
|
||||
.dpi_clock_freq_mhz = 70,
|
||||
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
|
||||
.num_fbs = 1,
|
||||
.video_timing = {
|
||||
.h_size = 720,
|
||||
.v_size = 1280,
|
||||
.hsync_pulse_width = 2,
|
||||
.hsync_back_porch = 40,
|
||||
.hsync_front_porch = 40,
|
||||
.vsync_pulse_width = 2,
|
||||
.vsync_back_porch = 8,
|
||||
.vsync_front_porch = 220,
|
||||
},
|
||||
.flags = {
|
||||
.use_dma2d = 1,
|
||||
.disable_lp = 0,
|
||||
}
|
||||
};
|
||||
|
||||
st7123_vendor_config_t vendor_config = {
|
||||
.init_cmds = st7123_init_data,
|
||||
.init_cmds_size = std::size(st7123_init_data),
|
||||
.mipi_config = {
|
||||
.dsi_bus = mipiDsiBus,
|
||||
.dpi_config = &dpi_config,
|
||||
},
|
||||
};
|
||||
|
||||
// Create a mutable copy of panelConfig to set vendor_config
|
||||
esp_lcd_panel_dev_config_t mutable_panel_config = panelConfig;
|
||||
mutable_panel_config.vendor_config = &vendor_config;
|
||||
|
||||
if (esp_lcd_new_panel_st7123(ioHandle, &mutable_panel_config, &panelHandle) != ESP_OK) {
|
||||
LOGGER.error("Failed to create panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGGER.info("Panel created successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
lvgl_port_display_dsi_cfg_t St7123Display::getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) {
|
||||
// Disable avoid_tearing to prevent stalls/blank flashes when other tasks (e.g. flash writes) block timing
|
||||
return lvgl_port_display_dsi_cfg_t{
|
||||
.flags = {
|
||||
.avoid_tearing = 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
38
Devices/m5stack-tab5/Source/devices/St7123Display.h
Normal file
38
Devices/m5stack-tab5/Source/devices/St7123Display.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <EspLcdDisplayV2.h>
|
||||
|
||||
#include <esp_lcd_mipi_dsi.h>
|
||||
#include <esp_ldo_regulator.h>
|
||||
|
||||
class St7123Display final : public EspLcdDisplayV2 {
|
||||
|
||||
esp_lcd_dsi_bus_handle_t mipiDsiBus = nullptr;
|
||||
esp_ldo_channel_handle_t ldoChannel = nullptr;
|
||||
|
||||
bool createMipiDsiBus();
|
||||
|
||||
protected:
|
||||
|
||||
bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override;
|
||||
|
||||
esp_lcd_panel_dev_config_t createPanelConfig(std::shared_ptr<EspLcdConfiguration> espLcdConfiguration, gpio_num_t resetPin) override;
|
||||
|
||||
bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) override;
|
||||
|
||||
bool useDsiPanel() const override { return true; }
|
||||
|
||||
lvgl_port_display_dsi_cfg_t getLvglPortDisplayDsiConfig(esp_lcd_panel_io_handle_t /*ioHandle*/, esp_lcd_panel_handle_t /*panelHandle*/) override;
|
||||
|
||||
public:
|
||||
|
||||
St7123Display(
|
||||
const std::shared_ptr<EspLcdConfiguration>& configuration
|
||||
) : EspLcdDisplayV2(configuration) {}
|
||||
|
||||
~St7123Display() override;
|
||||
|
||||
std::string getName() const override { return "St7123"; }
|
||||
|
||||
std::string getDescription() const override { return "St7123 MIPI-DSI display"; }
|
||||
};
|
||||
42
Devices/m5stack-tab5/Source/devices/St7123Touch.cpp
Normal file
42
Devices/m5stack-tab5/Source/devices/St7123Touch.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "St7123Touch.h"
|
||||
|
||||
#include <Tactility/Logger.h>
|
||||
#include <esp_lcd_touch_st7123.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("ST7123Touch");
|
||||
|
||||
bool St7123Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
|
||||
esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_ST7123_CONFIG();
|
||||
return esp_lcd_new_panel_io_i2c(
|
||||
static_cast<esp_lcd_i2c_bus_handle_t>(configuration->port),
|
||||
&io_config,
|
||||
&outHandle
|
||||
) == ESP_OK;
|
||||
}
|
||||
|
||||
bool St7123Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& touchHandle) {
|
||||
return esp_lcd_touch_new_i2c_st7123(ioHandle, &config, &touchHandle) == ESP_OK;
|
||||
}
|
||||
|
||||
esp_lcd_touch_config_t St7123Touch::createEspLcdTouchConfig() {
|
||||
return {
|
||||
.x_max = configuration->xMax,
|
||||
.y_max = configuration->yMax,
|
||||
.rst_gpio_num = GPIO_NUM_NC,
|
||||
.int_gpio_num = configuration->pinInterrupt,
|
||||
.levels = {
|
||||
.reset = 0,
|
||||
.interrupt = 0,
|
||||
},
|
||||
.flags = {
|
||||
.swap_xy = configuration->swapXy,
|
||||
.mirror_x = configuration->mirrorX,
|
||||
.mirror_y = configuration->mirrorY,
|
||||
},
|
||||
.process_coordinates = nullptr,
|
||||
.interrupt_callback = nullptr,
|
||||
.user_data = nullptr,
|
||||
.driver_data = nullptr
|
||||
};
|
||||
}
|
||||
59
Devices/m5stack-tab5/Source/devices/St7123Touch.h
Normal file
59
Devices/m5stack-tab5/Source/devices/St7123Touch.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <EspLcdTouch.h>
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <driver/i2c.h>
|
||||
|
||||
class St7123Touch final : public EspLcdTouch {
|
||||
|
||||
public:
|
||||
|
||||
class Configuration {
|
||||
public:
|
||||
|
||||
Configuration(
|
||||
i2c_port_t port,
|
||||
uint16_t xMax,
|
||||
uint16_t yMax,
|
||||
bool swapXy = false,
|
||||
bool mirrorX = false,
|
||||
bool mirrorY = false,
|
||||
gpio_num_t pinInterrupt = GPIO_NUM_NC
|
||||
) : port(port),
|
||||
xMax(xMax),
|
||||
yMax(yMax),
|
||||
swapXy(swapXy),
|
||||
mirrorX(mirrorX),
|
||||
mirrorY(mirrorY),
|
||||
pinInterrupt(pinInterrupt)
|
||||
{}
|
||||
|
||||
i2c_port_t port;
|
||||
uint16_t xMax;
|
||||
uint16_t yMax;
|
||||
bool swapXy;
|
||||
bool mirrorX;
|
||||
bool mirrorY;
|
||||
gpio_num_t pinInterrupt;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<Configuration> configuration;
|
||||
|
||||
bool createIoHandle(esp_lcd_panel_io_handle_t& outHandle) override;
|
||||
|
||||
bool createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& config, esp_lcd_touch_handle_t& touchHandle) override;
|
||||
|
||||
esp_lcd_touch_config_t createEspLcdTouchConfig() override;
|
||||
|
||||
public:
|
||||
|
||||
explicit St7123Touch(std::unique_ptr<Configuration> inConfiguration) : configuration(std::move(inConfiguration)) {
|
||||
assert(configuration != nullptr);
|
||||
}
|
||||
|
||||
std::string getName() const override { return "ST7123Touch"; }
|
||||
|
||||
std::string getDescription() const override { return "ST7123 I2C touch driver"; }
|
||||
};
|
||||
@ -6,7 +6,7 @@
|
||||
#pragma once
|
||||
#include <esp_lcd_ili9881c.h>
|
||||
|
||||
static const ili9881c_lcd_init_cmd_t disp_init_data[] = {
|
||||
static const ili9881c_lcd_init_cmd_t ili9881_init_data[] = {
|
||||
// {cmd, { data }, data_size, delay}
|
||||
|
||||
/**** CMD_Page 1 ****/
|
||||
39
Devices/m5stack-tab5/Source/devices/st7123_init_data.h
Normal file
39
Devices/m5stack-tab5/Source/devices/st7123_init_data.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include <esp_lcd_st7123.h>
|
||||
|
||||
//Refer to https://github.com/m5stack/M5Tab5-UserDemo
|
||||
//https://github.com/m5stack/M5Tab5-UserDemo/blob/main/LICENSE
|
||||
static const st7123_lcd_init_cmd_t st7123_init_data[] = {
|
||||
{0x60, (uint8_t[]){0x71, 0x23, 0xa2}, 3, 0},
|
||||
{0x60, (uint8_t[]){0x71, 0x23, 0xa3}, 3, 0},
|
||||
{0x60, (uint8_t[]){0x71, 0x23, 0xa4}, 3, 0},
|
||||
{0xA4, (uint8_t[]){0x31}, 1, 0},
|
||||
{0xD7, (uint8_t[]){0x10, 0x0A, 0x10, 0x2A, 0x80, 0x80}, 6, 0},
|
||||
{0x90, (uint8_t[]){0x71, 0x23, 0x5A, 0x20, 0x24, 0x09, 0x09}, 7, 0},
|
||||
{0xA3, (uint8_t[]){0x80, 0x01, 0x88, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x1E, 0x5C, 0x1E, 0x80, 0x00, 0x4F, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x1E, 0x5C, 0x1E, 0x80, 0x00, 0x6F, 0x58, 0x00, 0x00, 0x00, 0xFF}, 40, 0},
|
||||
{0xA6, (uint8_t[]){0x03, 0x00, 0x24, 0x55, 0x36, 0x00, 0x39, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0x24, 0x55, 0x38, 0x00, 0x37, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0x24, 0x11, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x00, 0xEC, 0x11, 0x00, 0x03, 0x00, 0x03, 0x6E, 0x6E, 0xFF, 0xFF, 0x00, 0x08, 0x80, 0x08, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00}, 55, 0},
|
||||
{0xA7, (uint8_t[]){0x19, 0x19, 0x80, 0x64, 0x40, 0x07, 0x16, 0x40, 0x00, 0x44, 0x03, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x25, 0x34, 0x40, 0x00, 0x02, 0x01, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x6E, 0x6E, 0x91, 0xFF, 0x08, 0x80, 0x64, 0x40, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x6E, 0x6E, 0x84, 0xFF, 0x08, 0x80, 0x44}, 60, 0},
|
||||
{0xAC, (uint8_t[]){0x03, 0x19, 0x19, 0x18, 0x18, 0x06, 0x13, 0x13, 0x11, 0x11, 0x08, 0x08, 0x0A, 0x0A, 0x1C, 0x1C, 0x07, 0x07, 0x00, 0x00, 0x02, 0x02, 0x01, 0x19, 0x19, 0x18, 0x18, 0x06, 0x12, 0x12, 0x10, 0x10, 0x09, 0x09, 0x0B, 0x0B, 0x1C, 0x1C, 0x07, 0x07, 0x03, 0x03, 0x01, 0x01}, 44, 0},
|
||||
{0xAD, (uint8_t[]){0xF0, 0x00, 0x46, 0x00, 0x03, 0x50, 0x50, 0xFF, 0xFF, 0xF0, 0x40, 0x06, 0x01, 0x07, 0x42, 0x42, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, 25, 0},
|
||||
{0xAE, (uint8_t[]){0xFE, 0x3F, 0x3F, 0xFE, 0x3F, 0x3F, 0x00}, 7, 0},
|
||||
{0xB2, (uint8_t[]){0x15, 0x19, 0x05, 0x23, 0x49, 0xAF, 0x03, 0x2E, 0x5C, 0xD2, 0xFF, 0x10, 0x20, 0xFD, 0x20, 0xC0, 0x00}, 17, 0},
|
||||
{0xE8, (uint8_t[]){0x20, 0x6F, 0x04, 0x97, 0x97, 0x3E, 0x04, 0xDC, 0xDC, 0x3E, 0x06, 0xFA, 0x26, 0x3E}, 15, 0},
|
||||
{0x75, (uint8_t[]){0x03, 0x04}, 2, 0},
|
||||
{0xE7, (uint8_t[]){0x3B, 0x00, 0x00, 0x7C, 0xA1, 0x8C, 0x20, 0x1A, 0xF0, 0xB1, 0x50, 0x00, 0x50, 0xB1, 0x50, 0xB1, 0x50, 0xD8, 0x00, 0x55, 0x00, 0xB1, 0x00, 0x45, 0xC9, 0x6A, 0xFF, 0x5A, 0xD8, 0x18, 0x88, 0x15, 0xB1, 0x01, 0x01, 0x77}, 36, 0},
|
||||
{0xEA, (uint8_t[]){0x13, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2C}, 8, 0},
|
||||
{0xB0, (uint8_t[]){0x22, 0x43, 0x11, 0x61, 0x25, 0x43, 0x43}, 7, 0},
|
||||
{0xB7, (uint8_t[]){0x00, 0x00, 0x73, 0x73}, 0x04, 0},
|
||||
{0xBF, (uint8_t[]){0xA6, 0xAA}, 2, 0},
|
||||
{0xA9, (uint8_t[]){0x00, 0x00, 0x73, 0xFF, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03}, 10, 0},
|
||||
{0xC8, (uint8_t[]){0x00, 0x00, 0x10, 0x1F, 0x36, 0x00, 0x5D, 0x04, 0x9D, 0x05, 0x10, 0xF2, 0x06, 0x60, 0x03, 0x11, 0xAD, 0x00, 0xEF, 0x01, 0x22, 0x2E, 0x0E, 0x74, 0x08, 0x32, 0xDC, 0x09, 0x33, 0x0F, 0xF3, 0x77, 0x0D, 0xB0, 0xDC, 0x03, 0xFF}, 37, 0},
|
||||
{0xC9, (uint8_t[]){0x00, 0x00, 0x10, 0x1F, 0x36, 0x00, 0x5D, 0x04, 0x9D, 0x05, 0x10, 0xF2, 0x06, 0x60, 0x03, 0x11, 0xAD, 0x00, 0xEF, 0x01, 0x22, 0x2E, 0x0E, 0x74, 0x08, 0x32, 0xDC, 0x09, 0x33, 0x0F, 0xF3, 0x77, 0x0D, 0xB0, 0xDC, 0x03, 0xFF}, 37, 0},
|
||||
{0x36, (uint8_t[]){0x00}, 1, 0},
|
||||
{0x11, (uint8_t[]){0x00}, 1, 100},
|
||||
{0x29, (uint8_t[]){0x00}, 1, 0},
|
||||
{0x35, (uint8_t[]){0x00}, 1, 100},
|
||||
};
|
||||
@ -1,5 +1,6 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/bmi270-module
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/pi4ioe5v6408-module
|
||||
- Drivers/bmi270-module
|
||||
- Drivers/rx8130ce-module
|
||||
dts: m5stack,tab5.dts
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <bindings/bmi270.h>
|
||||
#include <bindings/pi4ioe5v6408.h>
|
||||
#include <bindings/rx8130ce.h>
|
||||
|
||||
/ {
|
||||
compatible = "root";
|
||||
@ -20,7 +21,7 @@
|
||||
i2c_internal: i2c0 {
|
||||
compatible = "espressif,esp32-i2c";
|
||||
port = <I2C_NUM_0>;
|
||||
clock-frequency = <400000>;
|
||||
clock-frequency = <100000>;
|
||||
pin-sda = <&gpio0 31 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 32 GPIO_FLAG_NONE>;
|
||||
|
||||
@ -38,6 +39,11 @@
|
||||
compatible = "bosch,bmi270";
|
||||
reg = <0x68>;
|
||||
};
|
||||
|
||||
rx8130ce {
|
||||
compatible = "epson,rx8130ce";
|
||||
reg = <0x32>;
|
||||
};
|
||||
};
|
||||
|
||||
i2c_port_a: i2c1 {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/qmi8658-module
|
||||
dts: waveshare,s3-lcd-13.dts
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <bindings/qmi8658.h>
|
||||
|
||||
// Reference: https://www.waveshare.com/wiki/ESP32-S3-LCD-1.3
|
||||
/ {
|
||||
@ -21,6 +22,11 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 47 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 48 GPIO_FLAG_NONE>;
|
||||
|
||||
qmi8658 {
|
||||
compatible = "qst,qmi8658";
|
||||
reg = <0x6B>;
|
||||
};
|
||||
};
|
||||
|
||||
spi0 {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
dependencies:
|
||||
- Platforms/platform-esp32
|
||||
- Platforms/platform-esp32
|
||||
- Drivers/qmi8658-module
|
||||
dts: waveshare,s3-touch-lcd-128.dts
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <tactility/bindings/esp32_gpio.h>
|
||||
#include <tactility/bindings/esp32_i2c.h>
|
||||
#include <tactility/bindings/esp32_spi.h>
|
||||
#include <bindings/qmi8658.h>
|
||||
|
||||
// Reference: https://www.waveshare.com/wiki/ESP32-S3-Touch-LCD-1.28
|
||||
/ {
|
||||
@ -21,6 +22,11 @@
|
||||
clock-frequency = <400000>;
|
||||
pin-sda = <&gpio0 6 GPIO_FLAG_NONE>;
|
||||
pin-scl = <&gpio0 7 GPIO_FLAG_NONE>;
|
||||
|
||||
qmi8658 {
|
||||
compatible = "qst,qmi8658";
|
||||
reg = <0x6B>;
|
||||
};
|
||||
};
|
||||
|
||||
display_spi: spi0 {
|
||||
|
||||
@ -29,7 +29,6 @@
|
||||
- Fix glitches when installing app via App Hub with 4.3" Waveshare
|
||||
- TCA9534 keyboards should use interrupts
|
||||
- GT911 drivers should use interrupts if it's stable
|
||||
- Change ButtonControl to work with interrupts and xQueue
|
||||
- Fix Cardputer (original): use LV_KEY_NEXT and _PREV in keyboard mapping instead of encoder driver hack (and check GPIO app if it then hangs too)
|
||||
- Logging with a function that uses std::format
|
||||
- Expose http::download() and main dispatcher to TactiltyC.
|
||||
|
||||
@ -1,22 +1,52 @@
|
||||
#include "ButtonControl.h"
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/Logger.h>
|
||||
|
||||
#include <esp_lvgl_port.h>
|
||||
|
||||
static const auto LOGGER = tt::Logger("ButtonControl");
|
||||
|
||||
ButtonControl::ButtonControl(const std::vector<PinConfiguration>& pinConfigurations) : pinConfigurations(pinConfigurations) {
|
||||
ButtonControl::ButtonControl(const std::vector<PinConfiguration>& pinConfigurations)
|
||||
: buttonQueue(20, sizeof(ButtonEvent)),
|
||||
pinConfigurations(pinConfigurations) {
|
||||
|
||||
pinStates.resize(pinConfigurations.size());
|
||||
for (const auto& pinConfiguration : pinConfigurations) {
|
||||
tt::hal::gpio::configure(pinConfiguration.pin, tt::hal::gpio::Mode::Input, false, false);
|
||||
|
||||
// Build isrArgs with one entry per unique physical pin, then configure GPIO.
|
||||
isrArgs.reserve(pinConfigurations.size());
|
||||
for (size_t i = 0; i < pinConfigurations.size(); i++) {
|
||||
const auto pin = static_cast<gpio_num_t>(pinConfigurations[i].pin);
|
||||
|
||||
// Skip if this physical pin was already seen.
|
||||
bool seen = false;
|
||||
for (const auto& arg : isrArgs) {
|
||||
if (arg.pin == pin) { seen = true; break; }
|
||||
}
|
||||
if (seen) continue;
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = 1ULL << pin,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_ANYEDGE,
|
||||
};
|
||||
esp_err_t err = gpio_config(&io_conf);
|
||||
if (err != ESP_OK) {
|
||||
LOGGER.error("Failed to configure GPIO {}: {}", static_cast<int>(pin), esp_err_to_name(err));
|
||||
continue;
|
||||
}
|
||||
|
||||
// isrArgs is reserved upfront; push_back will not reallocate, keeping addresses stable
|
||||
// for gpio_isr_handler_add() called later in startThread().
|
||||
isrArgs.push_back({ .self = this, .pin = pin });
|
||||
}
|
||||
}
|
||||
|
||||
ButtonControl::~ButtonControl() {
|
||||
if (driverThread != nullptr && driverThread->getState() != tt::Thread::State::Stopped) {
|
||||
interruptDriverThread = true;
|
||||
driverThread->join();
|
||||
stopThread();
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +78,7 @@ void ButtonControl::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
break;
|
||||
case Action::AppClose:
|
||||
// TODO: implement
|
||||
tt::app::stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -57,57 +87,86 @@ void ButtonControl::readCallback(lv_indev_t* indev, lv_indev_data_t* data) {
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonControl::updatePin(std::vector<PinConfiguration>::const_reference configuration, std::vector<PinState>::reference state) {
|
||||
if (tt::hal::gpio::getLevel(configuration.pin)) { // if pressed
|
||||
if (state.pressState) {
|
||||
// check time for long press trigger
|
||||
auto time_passed = tt::kernel::getMillis() - state.pressStartTime;
|
||||
if (time_passed > 500) {
|
||||
// state.triggerLongPress = true;
|
||||
}
|
||||
} else {
|
||||
state.pressStartTime = tt::kernel::getMillis();
|
||||
state.pressState = true;
|
||||
}
|
||||
void ButtonControl::updatePin(std::vector<PinConfiguration>::const_reference configuration, std::vector<PinState>::reference state, bool pressed) {
|
||||
auto now = tt::kernel::getMillis();
|
||||
|
||||
// Software debounce: ignore edges within 20ms of the last state change.
|
||||
if ((now - state.lastChangeTime) < 20) {
|
||||
return;
|
||||
}
|
||||
state.lastChangeTime = now;
|
||||
|
||||
if (pressed) {
|
||||
state.pressStartTime = now;
|
||||
state.pressState = true;
|
||||
} else { // released
|
||||
if (state.pressState) {
|
||||
auto time_passed = tt::kernel::getMillis() - state.pressStartTime;
|
||||
auto time_passed = now - state.pressStartTime;
|
||||
if (time_passed < 500) {
|
||||
LOGGER.debug("Trigger short press");
|
||||
LOGGER.info("Short press ({}ms)", time_passed);
|
||||
state.triggerShortPress = true;
|
||||
} else {
|
||||
LOGGER.info("Long press ({}ms)", time_passed);
|
||||
state.triggerLongPress = true;
|
||||
}
|
||||
state.pressState = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR ButtonControl::gpioIsrHandler(void* arg) {
|
||||
auto* isrArg = static_cast<IsrArg*>(arg);
|
||||
ButtonEvent event {
|
||||
.pin = isrArg->pin,
|
||||
.pressed = gpio_get_level(isrArg->pin) == 0, // active-low: LOW = pressed
|
||||
};
|
||||
// tt::MessageQueue::put() is ISR-safe with timeout=0: it detects ISR context via
|
||||
// xPortInIsrContext() and uses xQueueSendFromISR() + portYIELD_FROM_ISR() internally.
|
||||
isrArg->self->buttonQueue.put(&event, 0);
|
||||
}
|
||||
|
||||
void ButtonControl::driverThreadMain() {
|
||||
while (!shouldInterruptDriverThread()) {
|
||||
if (mutex.lock(100)) {
|
||||
for (int i = 0; i < pinConfigurations.size(); i++) {
|
||||
updatePin(pinConfigurations[i], pinStates[i]);
|
||||
ButtonEvent event;
|
||||
while (buttonQueue.get(&event, portMAX_DELAY)) {
|
||||
if (event.pin == GPIO_NUM_NC) {
|
||||
break; // shutdown sentinel
|
||||
}
|
||||
LOGGER.info("Pin {} {}", static_cast<int>(event.pin), event.pressed ? "down" : "up");
|
||||
if (mutex.lock(portMAX_DELAY)) {
|
||||
// Update ALL PinConfiguration entries that share this physical pin.
|
||||
for (size_t i = 0; i < pinConfigurations.size(); i++) {
|
||||
if (static_cast<gpio_num_t>(pinConfigurations[i].pin) == event.pin) {
|
||||
updatePin(pinConfigurations[i], pinStates[i], event.pressed);
|
||||
}
|
||||
}
|
||||
mutex.unlock();
|
||||
}
|
||||
tt::kernel::delayMillis(5);
|
||||
}
|
||||
}
|
||||
|
||||
bool ButtonControl::shouldInterruptDriverThread() const {
|
||||
bool interrupt = false;
|
||||
if (mutex.lock(50 / portTICK_PERIOD_MS)) {
|
||||
interrupt = interruptDriverThread;
|
||||
mutex.unlock();
|
||||
}
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
void ButtonControl::startThread() {
|
||||
bool ButtonControl::startThread() {
|
||||
LOGGER.info("Start");
|
||||
|
||||
mutex.lock();
|
||||
esp_err_t err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
LOGGER.error("Failed to install GPIO ISR service: {}", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
interruptDriverThread = false;
|
||||
// isrArgs has one entry per unique physical pin — no duplicate registrations.
|
||||
// Addresses are stable: vector was reserved in constructor and is not modified after that.
|
||||
int handlersAdded = 0;
|
||||
for (auto& arg : isrArgs) {
|
||||
err = gpio_isr_handler_add(arg.pin, gpioIsrHandler, &arg);
|
||||
if (err != ESP_OK) {
|
||||
LOGGER.error("Failed to add ISR for GPIO {}: {}", static_cast<int>(arg.pin), esp_err_to_name(err));
|
||||
for (int i = 0; i < handlersAdded; i++) {
|
||||
gpio_isr_handler_remove(isrArgs[i].pin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
handlersAdded++;
|
||||
}
|
||||
|
||||
driverThread = std::make_shared<tt::Thread>("ButtonControl", 4096, [this] {
|
||||
driverThreadMain();
|
||||
@ -115,22 +174,21 @@ void ButtonControl::startThread() {
|
||||
});
|
||||
|
||||
driverThread->start();
|
||||
|
||||
mutex.unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ButtonControl::stopThread() {
|
||||
LOGGER.info("Stop");
|
||||
|
||||
mutex.lock();
|
||||
interruptDriverThread = true;
|
||||
mutex.unlock();
|
||||
for (const auto& arg : isrArgs) {
|
||||
gpio_isr_handler_remove(arg.pin);
|
||||
}
|
||||
|
||||
ButtonEvent sentinel { .pin = GPIO_NUM_NC, .pressed = false };
|
||||
buttonQueue.put(&sentinel, portMAX_DELAY);
|
||||
|
||||
driverThread->join();
|
||||
|
||||
mutex.lock();
|
||||
driverThread = nullptr;
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
bool ButtonControl::startLvgl(lv_display_t* display) {
|
||||
@ -138,7 +196,9 @@ bool ButtonControl::startLvgl(lv_display_t* display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
startThread();
|
||||
if (!startThread()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
deviceHandle = lv_indev_create();
|
||||
lv_indev_set_type(deviceHandle, LV_INDEV_TYPE_ENCODER);
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
|
||||
#include <Tactility/hal/encoder/EncoderDevice.h>
|
||||
#include <Tactility/hal/gpio/Gpio.h>
|
||||
#include <Tactility/MessageQueue.h>
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <Tactility/Thread.h>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
class ButtonControl final : public tt::hal::encoder::EncoderDevice {
|
||||
|
||||
public:
|
||||
@ -31,28 +34,41 @@ private:
|
||||
|
||||
struct PinState {
|
||||
long pressStartTime = 0;
|
||||
long pressReleaseTime = 0;
|
||||
long lastChangeTime = 0;
|
||||
bool pressState = false;
|
||||
bool triggerShortPress = false;
|
||||
bool triggerLongPress = false;
|
||||
};
|
||||
|
||||
/** Queued from ISR to worker thread. pin == GPIO_NUM_NC is a shutdown sentinel. */
|
||||
struct ButtonEvent {
|
||||
gpio_num_t pin;
|
||||
bool pressed;
|
||||
};
|
||||
|
||||
/** One entry per unique physical pin; addresses must remain stable after construction. */
|
||||
struct IsrArg {
|
||||
ButtonControl* self;
|
||||
gpio_num_t pin;
|
||||
};
|
||||
|
||||
lv_indev_t* deviceHandle = nullptr;
|
||||
std::shared_ptr<tt::Thread> driverThread;
|
||||
bool interruptDriverThread = false;
|
||||
tt::Mutex mutex;
|
||||
tt::MessageQueue buttonQueue;
|
||||
std::vector<PinConfiguration> pinConfigurations;
|
||||
std::vector<PinState> pinStates;
|
||||
std::vector<IsrArg> isrArgs; // one entry per unique physical pin
|
||||
|
||||
bool shouldInterruptDriverThread() const;
|
||||
|
||||
static void updatePin(std::vector<PinConfiguration>::const_reference value, std::vector<PinState>::reference pin_state);
|
||||
static void updatePin(std::vector<PinConfiguration>::const_reference config, std::vector<PinState>::reference state, bool pressed);
|
||||
|
||||
void driverThreadMain();
|
||||
|
||||
static void readCallback(lv_indev_t* indev, lv_indev_data_t* data);
|
||||
|
||||
void startThread();
|
||||
static void IRAM_ATTR gpioIsrHandler(void* arg);
|
||||
|
||||
bool startThread();
|
||||
void stopThread();
|
||||
|
||||
public:
|
||||
|
||||
@ -180,9 +180,9 @@ lvgl_port_display_cfg_t EspLcdDisplayV2::getLvglPortDisplayConfig(std::shared_pt
|
||||
},
|
||||
.color_format = configuration->lvglColorFormat,
|
||||
.flags = {
|
||||
.buff_dma = 1,
|
||||
.buff_spiram = 0,
|
||||
.sw_rotate = 0,
|
||||
.buff_dma = configuration->buffSpiram ? 0u : 1u,
|
||||
.buff_spiram = configuration->buffSpiram ? 1u : 0u,
|
||||
.sw_rotate = configuration->swRotate ? 1u : 0u,
|
||||
.swap_bytes = configuration->lvglSwapBytes,
|
||||
.full_refresh = 0,
|
||||
.direct_mode = 0
|
||||
|
||||
@ -20,6 +20,8 @@ struct EspLcdConfiguration {
|
||||
bool mirrorY;
|
||||
bool invertColor;
|
||||
uint32_t bufferSize; // Size in pixel count. 0 means default, which is 1/10 of the screen size
|
||||
bool swRotate = false; // Use LVGL software rotation instead of hardware swap_xy (required for MIPI-DSI panels that don't support swap_xy)
|
||||
bool buffSpiram = false; // Allocate LVGL draw buffers from PSRAM instead of DMA-capable internal SRAM (required when sw_rotate needs a 3rd buffer that won't fit in internal SRAM)
|
||||
std::shared_ptr<tt::hal::touch::TouchDevice> touch;
|
||||
std::function<void(uint8_t)> _Nullable backlightDutyFunction;
|
||||
gpio_num_t resetPin;
|
||||
|
||||
11
Drivers/bm8563-module/CMakeLists.txt
Normal file
11
Drivers/bm8563-module/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||
|
||||
tactility_add_module(bm8563-module
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS include/
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
195
Drivers/bm8563-module/LICENSE-Apache-2.0.md
Normal file
195
Drivers/bm8563-module/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
9
Drivers/bm8563-module/README.md
Normal file
9
Drivers/bm8563-module/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
# BM8563 I2C Driver
|
||||
|
||||
A driver for the `BM8563` Realtime Clock from BELLING [SHANGHAI BELLING CO., LTD.]
|
||||
Drop-in functional clone — same init, same registers, same I2C protocol as the NXP PCF8563.
|
||||
|
||||
See https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf
|
||||
And: https://www.alldatasheet.com/datasheet-pdf/pdf/1768247/BELLING/BM8563.html
|
||||
|
||||
License: [Apache v2.0](LICENSE-Apache-2.0.md)
|
||||
5
Drivers/bm8563-module/bindings/belling,bm8563.yaml
Normal file
5
Drivers/bm8563-module/bindings/belling,bm8563.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
description: BM8563 RTC (PCF8563-compatible)
|
||||
|
||||
include: [ "i2c-device.yaml" ]
|
||||
|
||||
compatible: "belling,bm8563"
|
||||
3
Drivers/bm8563-module/devicetree.yaml
Normal file
3
Drivers/bm8563-module/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- TactilityKernel
|
||||
bindings: bindings
|
||||
15
Drivers/bm8563-module/include/bindings/bm8563.h
Normal file
15
Drivers/bm8563-module/include/bindings/bm8563.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/bm8563.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(bm8563, struct Bm8563Config)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
Drivers/bm8563-module/include/bm8563_module.h
Normal file
14
Drivers/bm8563-module/include/bm8563_module.h
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/module.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct Module bm8563_module;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
45
Drivers/bm8563-module/include/drivers/bm8563.h
Normal file
45
Drivers/bm8563-module/include/drivers/bm8563.h
Normal file
@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Bm8563Config {
|
||||
/** Address on bus */
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
struct Bm8563DateTime {
|
||||
uint16_t year; // 2000–2199
|
||||
uint8_t month; // 1–12
|
||||
uint8_t day; // 1–31
|
||||
uint8_t hour; // 0–23
|
||||
uint8_t minute; // 0–59
|
||||
uint8_t second; // 0–59
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the current date and time from the RTC.
|
||||
* @param[in] device bm8563 device
|
||||
* @param[out] dt Pointer to Bm8563DateTime to populate
|
||||
* @return ERROR_NONE on success
|
||||
*/
|
||||
error_t bm8563_get_datetime(struct Device* device, struct Bm8563DateTime* dt);
|
||||
|
||||
/**
|
||||
* Write the date and time to the RTC.
|
||||
* @param[in] device bm8563 device
|
||||
* @param[in] dt Pointer to Bm8563DateTime to write (year must be 2000–2199)
|
||||
* @return ERROR_NONE on success, ERROR_INVALID_ARGUMENT if any field is out of range
|
||||
*/
|
||||
error_t bm8563_set_datetime(struct Device* device, const struct Bm8563DateTime* dt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
118
Drivers/bm8563-module/source/bm8563.cpp
Normal file
118
Drivers/bm8563-module/source/bm8563.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/bm8563.h>
|
||||
#include <bm8563_module.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#define TAG "BM8563"
|
||||
|
||||
static constexpr uint8_t REG_CTRL1 = 0x00; // Control status 1
|
||||
static constexpr uint8_t REG_SECONDS = 0x02; // Seconds BCD, bit 7 = VL (clock integrity)
|
||||
// Registers 0x02–0x08: seconds, minutes, hours, days, weekdays, months, years
|
||||
|
||||
static constexpr TickType_t I2C_TIMEOUT_TICKS = pdMS_TO_TICKS(50);
|
||||
|
||||
#define GET_CONFIG(device) (static_cast<const Bm8563Config*>((device)->config))
|
||||
|
||||
// region Helpers
|
||||
|
||||
static uint8_t bcd_to_dec(uint8_t bcd) { return static_cast<uint8_t>((bcd >> 4) * 10 + (bcd & 0x0F)); }
|
||||
static uint8_t dec_to_bcd(uint8_t dec) { return static_cast<uint8_t>(((dec / 10) << 4) | (dec % 10)); }
|
||||
|
||||
// endregion
|
||||
|
||||
// region Driver lifecycle
|
||||
|
||||
static error_t start(Device* device) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||
LOG_E(TAG, "Parent is not an I2C controller");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// Clear STOP bit — chip may have been stopped after a power cycle
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_CTRL1, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to clear STOP bit at 0x%02X", address);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
// RTC oscillator should continue running on battery backup
|
||||
// No action needed on driver stop
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t bm8563_get_datetime(Device* device, Bm8563DateTime* dt) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// Burst-read 7 registers starting at 0x02:
|
||||
// [0]=seconds [1]=minutes [2]=hours [3]=days [4]=weekdays [5]=months [6]=years
|
||||
uint8_t buf[7] = {};
|
||||
error_t error = i2c_controller_read_register(i2c_controller, address, REG_SECONDS, buf, sizeof(buf), I2C_TIMEOUT_TICKS);
|
||||
if (error != ERROR_NONE) return error;
|
||||
|
||||
if (buf[0] & 0x80u) {
|
||||
LOG_E(TAG, "Clock integrity compromised (VL flag set) — data unreliable");
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
dt->second = bcd_to_dec(buf[0] & 0x7Fu); // mask VL flag
|
||||
dt->minute = bcd_to_dec(buf[1] & 0x7Fu);
|
||||
dt->hour = bcd_to_dec(buf[2] & 0x3Fu);
|
||||
dt->day = bcd_to_dec(buf[3] & 0x3Fu);
|
||||
// buf[4] = weekday — ignored
|
||||
dt->month = bcd_to_dec(buf[5] & 0x1Fu);
|
||||
bool century = (buf[5] & 0x80u) != 0;
|
||||
dt->year = static_cast<uint16_t>(2000 + bcd_to_dec(buf[6]) + (century ? 100 : 0));
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t bm8563_set_datetime(Device* device, const Bm8563DateTime* dt) {
|
||||
if (dt->year < 2000 || dt->year > 2199 ||
|
||||
dt->month < 1 || dt->month > 12 ||
|
||||
dt->day < 1 || dt->day > 31 ||
|
||||
dt->hour > 23 || dt->minute > 59 || dt->second > 59) {
|
||||
return ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
bool century = (dt->year >= 2100);
|
||||
uint8_t y = static_cast<uint8_t>(century ? dt->year - 2100 : dt->year - 2000);
|
||||
|
||||
uint8_t buf[7] = {};
|
||||
buf[0] = dec_to_bcd(dt->second);
|
||||
buf[1] = dec_to_bcd(dt->minute);
|
||||
buf[2] = dec_to_bcd(dt->hour);
|
||||
buf[3] = dec_to_bcd(dt->day);
|
||||
buf[4] = 0; // weekday — leave as Sunday (unused)
|
||||
buf[5] = static_cast<uint8_t>(dec_to_bcd(dt->month) | (century ? 0x80u : 0x00u));
|
||||
buf[6] = dec_to_bcd(y);
|
||||
|
||||
return i2c_controller_write_register(i2c_controller, address, REG_SECONDS, buf, sizeof(buf), I2C_TIMEOUT_TICKS);
|
||||
}
|
||||
|
||||
Driver bm8563_driver = {
|
||||
.name = "bm8563",
|
||||
.compatible = (const char*[]) { "belling,bm8563", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &bm8563_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
34
Drivers/bm8563-module/source/module.cpp
Normal file
34
Drivers/bm8563-module/source/module.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver bm8563_driver;
|
||||
|
||||
static error_t start() {
|
||||
/* We crash when construct fails, because if a single driver fails to construct,
|
||||
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||
check(driver_construct_add(&bm8563_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||
check(driver_remove_destruct(&bm8563_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
extern const ModuleSymbol bm8563_module_symbols[];
|
||||
|
||||
Module bm8563_module = {
|
||||
.name = "bm8563",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = bm8563_module_symbols,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
9
Drivers/bm8563-module/source/symbols.c
Normal file
9
Drivers/bm8563-module/source/symbols.c
Normal file
@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/bm8563.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
const struct ModuleSymbol bm8563_module_symbols[] = {
|
||||
DEFINE_MODULE_SYMBOL(bm8563_get_datetime),
|
||||
DEFINE_MODULE_SYMBOL(bm8563_set_datetime),
|
||||
MODULE_SYMBOL_TERMINATOR
|
||||
};
|
||||
@ -131,6 +131,20 @@ static error_t start(Device* device) {
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||
LOG_E(TAG, "Parent is not an I2C controller");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// Disable accelerometer and gyroscope (clear bit1=gyr_en, bit2=acc_en)
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_PWR_CTRL, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to put BMI270 to sleep");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
11
Drivers/m5pm1-module/CMakeLists.txt
Normal file
11
Drivers/m5pm1-module/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||
|
||||
tactility_add_module(m5pm1-module
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS include/
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
195
Drivers/m5pm1-module/LICENSE-Apache-2.0.md
Normal file
195
Drivers/m5pm1-module/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
8
Drivers/m5pm1-module/README.md
Normal file
8
Drivers/m5pm1-module/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# M5PM1 I2C Driver
|
||||
|
||||
A driver for the `M5PM1` power management chip.
|
||||
|
||||
See https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1207/M5PM1_Datasheet_EN.pdf
|
||||
And https://github.com/m5stack/M5PM1
|
||||
|
||||
License: [Apache v2.0](LICENSE-Apache-2.0.md)
|
||||
5
Drivers/m5pm1-module/bindings/m5stack,m5pm1.yaml
Normal file
5
Drivers/m5pm1-module/bindings/m5stack,m5pm1.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
description: M5Stack M5PM1 Power Management IC
|
||||
|
||||
include: ["i2c-device.yaml"]
|
||||
|
||||
compatible: "m5stack,m5pm1"
|
||||
3
Drivers/m5pm1-module/devicetree.yaml
Normal file
3
Drivers/m5pm1-module/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- TactilityKernel
|
||||
bindings: bindings
|
||||
15
Drivers/m5pm1-module/include/bindings/m5pm1.h
Normal file
15
Drivers/m5pm1-module/include/bindings/m5pm1.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/m5pm1.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(m5pm1, struct M5pm1Config)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
88
Drivers/m5pm1-module/include/drivers/m5pm1.h
Normal file
88
Drivers/m5pm1-module/include/drivers/m5pm1.h
Normal file
@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct M5pm1Config {
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Power source (REG_PWR_SRC 0x04)
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef enum {
|
||||
M5PM1_PWR_SRC_5VIN = 0,
|
||||
M5PM1_PWR_SRC_5VINOUT = 1,
|
||||
M5PM1_PWR_SRC_BAT = 2,
|
||||
M5PM1_PWR_SRC_UNKNOWN = 3,
|
||||
} M5pm1PowerSource;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Voltage readings
|
||||
// ---------------------------------------------------------------------------
|
||||
error_t m5pm1_get_battery_voltage(struct Device* device, uint16_t* mv);
|
||||
error_t m5pm1_get_vin_voltage(struct Device* device, uint16_t* mv);
|
||||
error_t m5pm1_get_5vout_voltage(struct Device* device, uint16_t* mv);
|
||||
error_t m5pm1_get_power_source(struct Device* device, M5pm1PowerSource* source);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Charging & power rails
|
||||
// ---------------------------------------------------------------------------
|
||||
/** PM1_G0 low = charging (connected to charge-status pin of the charge IC) */
|
||||
error_t m5pm1_is_charging(struct Device* device, bool* charging);
|
||||
error_t m5pm1_set_charge_enable(struct Device* device, bool enable);
|
||||
error_t m5pm1_set_boost_enable(struct Device* device, bool enable); ///< 5V BOOST / Grove power
|
||||
error_t m5pm1_set_ldo_enable(struct Device* device, bool enable); ///< 3.3V LDO
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Temperature (internal chip sensor)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Returns temperature in units of 0.1 °C */
|
||||
error_t m5pm1_get_temperature(struct Device* device, uint16_t* decidegc);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// System commands
|
||||
// ---------------------------------------------------------------------------
|
||||
error_t m5pm1_shutdown(struct Device* device);
|
||||
error_t m5pm1_reboot(struct Device* device);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Power button (M5PM1 internal button, not ESP32 GPIO)
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Current instantaneous state of the power button */
|
||||
error_t m5pm1_btn_get_state(struct Device* device, bool* pressed);
|
||||
/** Edge-triggered flag — auto-clears on read */
|
||||
error_t m5pm1_btn_get_flag(struct Device* device, bool* was_pressed);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Watchdog timer
|
||||
// ---------------------------------------------------------------------------
|
||||
/** timeout_sec: 0 = disabled, 1–255 = timeout in seconds */
|
||||
error_t m5pm1_wdt_set(struct Device* device, uint8_t timeout_sec);
|
||||
error_t m5pm1_wdt_feed(struct Device* device);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RTC RAM (32 bytes, retained across sleep / power-off)
|
||||
// ---------------------------------------------------------------------------
|
||||
error_t m5pm1_read_rtc_ram(struct Device* device, uint8_t offset, uint8_t* data, uint8_t len);
|
||||
error_t m5pm1_write_rtc_ram(struct Device* device, uint8_t offset, const uint8_t* data, uint8_t len);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NeoPixel LED (via M5PM1 LED controller, max 32 LEDs)
|
||||
// ---------------------------------------------------------------------------
|
||||
error_t m5pm1_set_led_count(struct Device* device, uint8_t count);
|
||||
error_t m5pm1_set_led_color(struct Device* device, uint8_t index, uint8_t r, uint8_t g, uint8_t b);
|
||||
error_t m5pm1_refresh_leds(struct Device* device);
|
||||
error_t m5pm1_disable_leds(struct Device* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
Drivers/m5pm1-module/include/m5pm1_module.h
Normal file
14
Drivers/m5pm1-module/include/m5pm1_module.h
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/module.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct Module m5pm1_module;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
288
Drivers/m5pm1-module/source/m5pm1.cpp
Normal file
288
Drivers/m5pm1-module/source/m5pm1.cpp
Normal file
@ -0,0 +1,288 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/m5pm1.h>
|
||||
#include <m5pm1_module.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <tactility/log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#define TAG "M5PM1"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Register map
|
||||
// ---------------------------------------------------------------------------
|
||||
static constexpr uint8_t REG_DEVICE_ID = 0x00; ///< R - Device ID (0x50)
|
||||
static constexpr uint8_t REG_PWR_SRC = 0x04; ///< R - Power source (0=5VIN, 1=5VINOUT, 2=BAT)
|
||||
static constexpr uint8_t REG_PWR_CFG = 0x06; ///< RW - [3]=BOOST_EN [2]=LDO_EN [1]=DCDC_EN [0]=CHG_EN
|
||||
static constexpr uint8_t REG_I2C_CFG = 0x09; ///< RW - [4]=SPD(400kHz) [3:0]=SLP_TO(0=off)
|
||||
static constexpr uint8_t REG_WDT_CNT = 0x0A; ///< RW - Watchdog countdown (0=disabled, 1–255=seconds)
|
||||
static constexpr uint8_t REG_WDT_KEY = 0x0B; ///< W - Write 0xA5 to feed watchdog
|
||||
static constexpr uint8_t REG_SYS_CMD = 0x0C; ///< W - High nibble=0xA; low: 1=shutdown 2=reboot 3=download
|
||||
static constexpr uint8_t REG_GPIO_MODE = 0x10; ///< RW - GPIO direction [4:0] (1=output, 0=input)
|
||||
static constexpr uint8_t REG_GPIO_OUT = 0x11; ///< RW - GPIO output level [4:0]
|
||||
static constexpr uint8_t REG_GPIO_IN = 0x12; ///< R - GPIO input state [4:0]
|
||||
static constexpr uint8_t REG_GPIO_DRV = 0x13; ///< RW - Drive mode [4:0] (0=push-pull, 1=open-drain)
|
||||
static constexpr uint8_t REG_GPIO_FUNC0 = 0x16; ///< RW - GPIO0–3 function (2 bits each: 00=GPIO)
|
||||
static constexpr uint8_t REG_VBAT_L = 0x22; ///< R - Battery voltage low byte (mV, 16-bit LE)
|
||||
static constexpr uint8_t REG_VIN_L = 0x24; ///< R - VIN voltage low byte (mV, 16-bit LE)
|
||||
static constexpr uint8_t REG_5VOUT_L = 0x26; ///< R - 5V output voltage low byte (mV, 16-bit LE)
|
||||
static constexpr uint8_t REG_ADC_RES_L = 0x28; ///< R - ADC result low byte (mV, 16-bit LE)
|
||||
static constexpr uint8_t REG_ADC_CTRL = 0x2A; ///< RW - [3:1]=channel [0]=START
|
||||
static constexpr uint8_t REG_BTN_STATUS = 0x48; ///< R - [7]=BTN_FLAG(auto-clear) [0]=BTN_STATE
|
||||
static constexpr uint8_t REG_NEO_CFG = 0x50; ///< RW - [6]=REFRESH [5:0]=LED_CNT
|
||||
static constexpr uint8_t REG_NEO_DATA = 0x60; ///< RW - NeoPixel RGB565 data, 2 bytes per LED (max 32)
|
||||
static constexpr uint8_t REG_RTC_RAM = 0xA0; ///< RW - 32 bytes of RTC RAM
|
||||
|
||||
// PWR_CFG bit masks
|
||||
static constexpr uint8_t PWR_CFG_CHG_EN = (1U << 0U);
|
||||
static constexpr uint8_t PWR_CFG_DCDC_EN = (1U << 1U);
|
||||
static constexpr uint8_t PWR_CFG_LDO_EN = (1U << 2U);
|
||||
static constexpr uint8_t PWR_CFG_BOOST_EN = (1U << 3U);
|
||||
|
||||
// System command values (high nibble must be 0xA)
|
||||
static constexpr uint8_t SYS_CMD_SHUTDOWN = 0xA1;
|
||||
static constexpr uint8_t SYS_CMD_REBOOT = 0xA2;
|
||||
|
||||
// ADC channel for temperature
|
||||
static constexpr uint8_t ADC_CH_TEMP = 6;
|
||||
|
||||
// PM1_G2: LCD power enable on M5Stack StickS3
|
||||
static constexpr uint8_t LCD_POWER_BIT = (1U << 2U);
|
||||
|
||||
static constexpr TickType_t TIMEOUT = pdMS_TO_TICKS(50);
|
||||
|
||||
#define GET_CONFIG(device) (static_cast<const M5pm1Config*>((device)->config))
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Driver lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static error_t start(Device* device) {
|
||||
Device* i2c = device_get_parent(device);
|
||||
if (device_get_type(i2c) != &I2C_CONTROLLER_TYPE) {
|
||||
LOG_E(TAG, "Parent is not an I2C controller");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
const uint8_t addr = GET_CONFIG(device)->address;
|
||||
|
||||
// M5PM1 enters I2C sleep after inactivity. The first transaction after sleep
|
||||
// is ignored as the chip wakes up. Retry with increasing delays until ACK.
|
||||
bool awake = false;
|
||||
for (int attempt = 0; attempt < 5; attempt++) {
|
||||
uint8_t chip_id = 0;
|
||||
if (i2c_controller_register8_get(i2c, addr, REG_DEVICE_ID, &chip_id, TIMEOUT) == ERROR_NONE) {
|
||||
LOG_I(TAG, "M5PM1 online (chip_id=0x%02X)", chip_id);
|
||||
awake = true;
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(20 * (attempt + 1)));
|
||||
}
|
||||
|
||||
if (!awake) {
|
||||
LOG_E(TAG, "M5PM1 not responding — LCD power will not be enabled");
|
||||
return ERROR_NONE; // non-fatal: don't crash the kernel
|
||||
}
|
||||
|
||||
// Disable I2C idle sleep so the PMIC stays reachable on battery power
|
||||
if (i2c_controller_register8_set(i2c, addr, REG_I2C_CFG, 0x00, TIMEOUT) != ERROR_NONE) {
|
||||
LOG_W(TAG, "Failed to disable I2C sleep (non-fatal)");
|
||||
}
|
||||
|
||||
// PM1_G2 → LCD power enable (L3B rail on StickS3)
|
||||
// Sequence matches M5GFX: clear FUNC0 bit2, set MODE bit2 output, clear DRV bit2 push-pull, set OUT bit2 high
|
||||
bool lcd_ok =
|
||||
i2c_controller_register8_reset_bits(i2c, addr, REG_GPIO_FUNC0, LCD_POWER_BIT, TIMEOUT) == ERROR_NONE &&
|
||||
i2c_controller_register8_set_bits (i2c, addr, REG_GPIO_MODE, LCD_POWER_BIT, TIMEOUT) == ERROR_NONE &&
|
||||
i2c_controller_register8_reset_bits(i2c, addr, REG_GPIO_DRV, LCD_POWER_BIT, TIMEOUT) == ERROR_NONE &&
|
||||
i2c_controller_register8_set_bits (i2c, addr, REG_GPIO_OUT, LCD_POWER_BIT, TIMEOUT) == ERROR_NONE;
|
||||
|
||||
if (lcd_ok) {
|
||||
LOG_I(TAG, "LCD power enabled via PM1_G2");
|
||||
} else {
|
||||
LOG_E(TAG, "Failed to enable LCD power via PM1_G2");
|
||||
}
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t m5pm1_get_battery_voltage(Device* device, uint16_t* mv) {
|
||||
return i2c_controller_register16le_get(device_get_parent(device), GET_CONFIG(device)->address, REG_VBAT_L, mv, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_get_vin_voltage(Device* device, uint16_t* mv) {
|
||||
return i2c_controller_register16le_get(device_get_parent(device), GET_CONFIG(device)->address, REG_VIN_L, mv, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_get_5vout_voltage(Device* device, uint16_t* mv) {
|
||||
return i2c_controller_register16le_get(device_get_parent(device), GET_CONFIG(device)->address, REG_5VOUT_L, mv, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_get_power_source(Device* device, M5pm1PowerSource* source) {
|
||||
uint8_t val = 0;
|
||||
error_t err = i2c_controller_register8_get(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_SRC, &val, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
*source = static_cast<M5pm1PowerSource>(val & 0x03U);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t m5pm1_is_charging(Device* device, bool* charging) {
|
||||
// PM1_G0 is wired to the charge IC's charge-status output: LOW = charging
|
||||
uint8_t gpio_in = 0;
|
||||
error_t err = i2c_controller_register8_get(device_get_parent(device), GET_CONFIG(device)->address, REG_GPIO_IN, &gpio_in, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
*charging = (gpio_in & 0x01U) == 0;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t m5pm1_set_charge_enable(Device* device, bool enable) {
|
||||
if (enable) {
|
||||
return i2c_controller_register8_set_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_CHG_EN, TIMEOUT);
|
||||
} else {
|
||||
return i2c_controller_register8_reset_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_CHG_EN, TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
error_t m5pm1_set_boost_enable(Device* device, bool enable) {
|
||||
if (enable) {
|
||||
return i2c_controller_register8_set_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_BOOST_EN, TIMEOUT);
|
||||
} else {
|
||||
return i2c_controller_register8_reset_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_BOOST_EN, TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
error_t m5pm1_set_ldo_enable(Device* device, bool enable) {
|
||||
if (enable) {
|
||||
return i2c_controller_register8_set_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_LDO_EN, TIMEOUT);
|
||||
} else {
|
||||
return i2c_controller_register8_reset_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_PWR_CFG, PWR_CFG_LDO_EN, TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
error_t m5pm1_get_temperature(Device* device, uint16_t* decidegc) {
|
||||
Device* i2c = device_get_parent(device);
|
||||
uint8_t addr = GET_CONFIG(device)->address;
|
||||
|
||||
// Select temperature channel and start conversion
|
||||
uint8_t ctrl = static_cast<uint8_t>((ADC_CH_TEMP << 1U) | 0x01U);
|
||||
error_t err = i2c_controller_register8_set(i2c, addr, REG_ADC_CTRL, ctrl, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
|
||||
// Poll until conversion complete (START bit clears)
|
||||
bool conversion_done = false;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
uint8_t status = 0;
|
||||
if (i2c_controller_register8_get(i2c, addr, REG_ADC_CTRL, &status, TIMEOUT) == ERROR_NONE) {
|
||||
if ((status & 0x01U) == 0) {
|
||||
conversion_done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!conversion_done) {
|
||||
return ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
return i2c_controller_register16le_get(i2c, addr, REG_ADC_RES_L, decidegc, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_shutdown(Device* device) {
|
||||
uint8_t cmd = SYS_CMD_SHUTDOWN;
|
||||
return i2c_controller_write_register(device_get_parent(device), GET_CONFIG(device)->address, REG_SYS_CMD, &cmd, 1, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_reboot(Device* device) {
|
||||
uint8_t cmd = SYS_CMD_REBOOT;
|
||||
return i2c_controller_write_register(device_get_parent(device), GET_CONFIG(device)->address, REG_SYS_CMD, &cmd, 1, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_btn_get_state(Device* device, bool* pressed) {
|
||||
uint8_t val = 0;
|
||||
error_t err = i2c_controller_register8_get(device_get_parent(device), GET_CONFIG(device)->address, REG_BTN_STATUS, &val, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
*pressed = (val & 0x01U) != 0;
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t m5pm1_btn_get_flag(Device* device, bool* was_pressed) {
|
||||
uint8_t val = 0;
|
||||
error_t err = i2c_controller_register8_get(device_get_parent(device), GET_CONFIG(device)->address, REG_BTN_STATUS, &val, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
*was_pressed = (val & 0x80U) != 0; // BTN_FLAG auto-clears on read
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t m5pm1_wdt_set(Device* device, uint8_t timeout_sec) {
|
||||
return i2c_controller_register8_set(device_get_parent(device), GET_CONFIG(device)->address, REG_WDT_CNT, timeout_sec, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_wdt_feed(Device* device) {
|
||||
return i2c_controller_register8_set(device_get_parent(device), GET_CONFIG(device)->address, REG_WDT_KEY, 0xA5, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_read_rtc_ram(Device* device, uint8_t offset, uint8_t* data, uint8_t len) {
|
||||
if (offset + len > 32) return ERROR_INVALID_ARGUMENT;
|
||||
return i2c_controller_read_register(device_get_parent(device), GET_CONFIG(device)->address, static_cast<uint8_t>(REG_RTC_RAM + offset), data, len, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_write_rtc_ram(Device* device, uint8_t offset, const uint8_t* data, uint8_t len) {
|
||||
if (offset + len > 32) return ERROR_INVALID_ARGUMENT;
|
||||
return i2c_controller_write_register(device_get_parent(device), GET_CONFIG(device)->address, static_cast<uint8_t>(REG_RTC_RAM + offset), data, len, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_set_led_count(Device* device, uint8_t count) {
|
||||
if (count == 0 || count > 32) return ERROR_INVALID_ARGUMENT;
|
||||
uint8_t val = count & 0x3FU;
|
||||
return i2c_controller_register8_set(device_get_parent(device), GET_CONFIG(device)->address, REG_NEO_CFG, val, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_set_led_color(Device* device, uint8_t index, uint8_t r, uint8_t g, uint8_t b) {
|
||||
if (index >= 32) return ERROR_INVALID_ARGUMENT;
|
||||
Device* i2c = device_get_parent(device);
|
||||
uint8_t addr = GET_CONFIG(device)->address;
|
||||
// Store as RGB565: [15:11]=R5, [10:5]=G6, [4:0]=B5
|
||||
uint16_t rgb565 = static_cast<uint16_t>(((r >> 3U) << 11U) | ((g >> 2U) << 5U) | (b >> 3U));
|
||||
uint8_t buf[2] = { static_cast<uint8_t>(rgb565 & 0xFFU), static_cast<uint8_t>(rgb565 >> 8U) };
|
||||
return i2c_controller_write_register(i2c, addr, static_cast<uint8_t>(REG_NEO_DATA + index * 2U), buf, 2, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_refresh_leds(Device* device) {
|
||||
return i2c_controller_register8_set_bits(device_get_parent(device), GET_CONFIG(device)->address, REG_NEO_CFG, 0x40U, TIMEOUT);
|
||||
}
|
||||
|
||||
error_t m5pm1_disable_leds(Device* device) {
|
||||
// Set count to 1 and write black, then refresh
|
||||
Device* i2c = device_get_parent(device);
|
||||
uint8_t addr = GET_CONFIG(device)->address;
|
||||
uint8_t black[2] = { 0, 0 };
|
||||
error_t err = i2c_controller_write_register(i2c, addr, REG_NEO_DATA, black, 2, TIMEOUT);
|
||||
if (err != ERROR_NONE) return err;
|
||||
uint8_t cfg = 0x41U; // REFRESH | count=1
|
||||
return i2c_controller_register8_set(i2c, addr, REG_NEO_CFG, cfg, TIMEOUT);
|
||||
}
|
||||
|
||||
Driver m5pm1_driver = {
|
||||
.name = "m5pm1",
|
||||
.compatible = (const char*[]) { "m5stack,m5pm1", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &m5pm1_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
34
Drivers/m5pm1-module/source/module.cpp
Normal file
34
Drivers/m5pm1-module/source/module.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver m5pm1_driver;
|
||||
|
||||
static error_t start() {
|
||||
/* We crash when construct fails, because if a single driver fails to construct,
|
||||
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||
check(driver_construct_add(&m5pm1_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||
check(driver_remove_destruct(&m5pm1_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
extern const ModuleSymbol m5pm1_module_symbols[];
|
||||
|
||||
Module m5pm1_module = {
|
||||
.name = "m5pm1",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = m5pm1_module_symbols,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
28
Drivers/m5pm1-module/source/symbols.c
Normal file
28
Drivers/m5pm1-module/source/symbols.c
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/m5pm1.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
const struct ModuleSymbol m5pm1_module_symbols[] = {
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_get_battery_voltage),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_get_vin_voltage),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_get_5vout_voltage),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_get_power_source),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_is_charging),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_set_charge_enable),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_set_boost_enable),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_set_ldo_enable),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_get_temperature),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_shutdown),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_reboot),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_btn_get_state),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_btn_get_flag),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_wdt_set),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_wdt_feed),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_read_rtc_ram),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_write_rtc_ram),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_set_led_count),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_set_led_color),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_refresh_leds),
|
||||
DEFINE_MODULE_SYMBOL(m5pm1_disable_leds),
|
||||
MODULE_SYMBOL_TERMINATOR
|
||||
};
|
||||
11
Drivers/mpu6886-module/CMakeLists.txt
Normal file
11
Drivers/mpu6886-module/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||
|
||||
tactility_add_module(mpu6886-module
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS include/
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
195
Drivers/mpu6886-module/LICENSE-Apache-2.0.md
Normal file
195
Drivers/mpu6886-module/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
7
Drivers/mpu6886-module/README.md
Normal file
7
Drivers/mpu6886-module/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# MPU6886 I2C Driver
|
||||
|
||||
A driver for the `MPU6886` 6-axis IMU.
|
||||
|
||||
See https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/datasheet/core/MPU-6886-000193%2Bv1.1_GHIC_en.pdf
|
||||
|
||||
License: [Apache v2.0](LICENSE-Apache-2.0.md)
|
||||
5
Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml
Normal file
5
Drivers/mpu6886-module/bindings/invensense,mpu6886.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
description: InvenSense (TDK) MPU-6886 6-axis IMU
|
||||
|
||||
include: ["i2c-device.yaml"]
|
||||
|
||||
compatible: "invensense,mpu6886"
|
||||
3
Drivers/mpu6886-module/devicetree.yaml
Normal file
3
Drivers/mpu6886-module/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- TactilityKernel
|
||||
bindings: bindings
|
||||
15
Drivers/mpu6886-module/include/bindings/mpu6886.h
Normal file
15
Drivers/mpu6886-module/include/bindings/mpu6886.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/mpu6886.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(mpu6886, struct Mpu6886Config)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
33
Drivers/mpu6886-module/include/drivers/mpu6886.h
Normal file
33
Drivers/mpu6886-module/include/drivers/mpu6886.h
Normal file
@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Mpu6886Config {
|
||||
/** Address on bus */
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
struct Mpu6886Data {
|
||||
float ax, ay, az; // acceleration in g (±8g range)
|
||||
float gx, gy, gz; // angular rate in °/s (±2000°/s range)
|
||||
};
|
||||
|
||||
/**
|
||||
* Read accelerometer and gyroscope data.
|
||||
* @param[in] device mpu6886 device
|
||||
* @param[out] data Pointer to Mpu6886Data to populate
|
||||
* @return ERROR_NONE on success
|
||||
*/
|
||||
error_t mpu6886_read(struct Device* device, struct Mpu6886Data* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
Drivers/mpu6886-module/include/mpu6886_module.h
Normal file
14
Drivers/mpu6886-module/include/mpu6886_module.h
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/module.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern struct Module mpu6886_module;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
34
Drivers/mpu6886-module/source/module.cpp
Normal file
34
Drivers/mpu6886-module/source/module.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/check.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern Driver mpu6886_driver;
|
||||
|
||||
static error_t start() {
|
||||
/* We crash when construct fails, because if a single driver fails to construct,
|
||||
* there is no guarantee that the previously constructed drivers can be destroyed */
|
||||
check(driver_construct_add(&mpu6886_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop() {
|
||||
/* We crash when destruct fails, because if a single driver fails to destruct,
|
||||
* there is no guarantee that the previously destroyed drivers can be recovered */
|
||||
check(driver_remove_destruct(&mpu6886_driver) == ERROR_NONE);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
extern const ModuleSymbol mpu6886_module_symbols[];
|
||||
|
||||
Module mpu6886_module = {
|
||||
.name = "mpu6886",
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.symbols = mpu6886_module_symbols,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
158
Drivers/mpu6886-module/source/mpu6886.cpp
Normal file
158
Drivers/mpu6886-module/source/mpu6886.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/mpu6886.h>
|
||||
#include <mpu6886_module.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/drivers/i2c_controller.h>
|
||||
#include <tactility/log.h>
|
||||
|
||||
#define TAG "MPU6886"
|
||||
|
||||
// Register map
|
||||
static constexpr uint8_t REG_SMPLRT_DIV = 0x19; // sample rate divider
|
||||
static constexpr uint8_t REG_CONFIG = 0x1A; // DLPF config
|
||||
static constexpr uint8_t REG_GYRO_CONFIG = 0x1B; // gyro full-scale select
|
||||
static constexpr uint8_t REG_ACCEL_CONFIG = 0x1C; // accel full-scale select
|
||||
static constexpr uint8_t REG_ACCEL_CONFIG2 = 0x1D; // accel low-pass filter
|
||||
static constexpr uint8_t REG_INT_PIN_CFG = 0x37; // interrupt pin config
|
||||
static constexpr uint8_t REG_INT_ENABLE = 0x38; // interrupt enable
|
||||
static constexpr uint8_t REG_ACCEL_XOUT_H = 0x3B; // first accel output register
|
||||
static constexpr uint8_t REG_GYRO_XOUT_H = 0x43; // first gyro output register
|
||||
static constexpr uint8_t REG_USER_CTRL = 0x6A; // user control (DMP, FIFO, I2C)
|
||||
static constexpr uint8_t REG_PWR_MGMT_1 = 0x6B; // power management 1
|
||||
static constexpr uint8_t REG_PWR_MGMT_2 = 0x6C; // power management 2
|
||||
static constexpr uint8_t REG_FIFO_EN = 0x23; // FIFO enable
|
||||
static constexpr uint8_t REG_WHO_AM_I = 0x75; // chip ID — expect 0x19
|
||||
|
||||
static constexpr uint8_t WHO_AM_I_VALUE = 0x19;
|
||||
|
||||
// Configuration values
|
||||
// GYRO_CONFIG: FS_SEL=3 (±2000°/s), FCHOICE_B=00 → 0x18
|
||||
static constexpr uint8_t GYRO_CONFIG_VAL = 0x18;
|
||||
// ACCEL_CONFIG: AFS_SEL=2 (±8g) → 0x10
|
||||
static constexpr uint8_t ACCEL_CONFIG_VAL = 0x10;
|
||||
// CONFIG: DLPF_CFG=1 → gyro BW=176Hz, temp BW=188Hz → 0x01
|
||||
static constexpr uint8_t CONFIG_VAL = 0x01;
|
||||
// SMPLRT_DIV: sample rate = 1kHz / (1 + 5) = 166Hz → 0x05
|
||||
static constexpr uint8_t SMPLRT_DIV_VAL = 0x05;
|
||||
|
||||
// Scaling: full-scale / 2^15
|
||||
static constexpr float ACCEL_SCALE = 8.0f / 32768.0f; // g per LSB (±8g)
|
||||
static constexpr float GYRO_SCALE = 2000.0f / 32768.0f; // °/s per LSB (±2000°/s)
|
||||
|
||||
static constexpr TickType_t I2C_TIMEOUT_TICKS = pdMS_TO_TICKS(10);
|
||||
|
||||
#define GET_CONFIG(device) (static_cast<const Mpu6886Config*>((device)->config))
|
||||
|
||||
// region Driver lifecycle
|
||||
|
||||
static error_t start(Device* device) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||
LOG_E(TAG, "Parent is not an I2C controller");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// Verify chip ID
|
||||
uint8_t who_am_i = 0;
|
||||
if (i2c_controller_register8_get(i2c_controller, address, REG_WHO_AM_I, &who_am_i, I2C_TIMEOUT_TICKS) != ERROR_NONE
|
||||
|| who_am_i != WHO_AM_I_VALUE) {
|
||||
LOG_E(TAG, "WHO_AM_I mismatch: got 0x%02X, expected 0x%02X", who_am_i, WHO_AM_I_VALUE);
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
// Wake from sleep (clear all PWR_MGMT_1 bits)
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_PWR_MGMT_1, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// Device reset (bit 7)
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_PWR_MGMT_1, 0x80, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// Select auto clock (CLKSEL=1: PLL with gyro reference when available)
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_PWR_MGMT_1, 0x01, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
// Configure accel: ±8g
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_ACCEL_CONFIG, ACCEL_CONFIG_VAL, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Configure gyro: ±2000°/s
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_GYRO_CONFIG, GYRO_CONFIG_VAL, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// DLPF: gyro BW=176Hz
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_CONFIG, CONFIG_VAL, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Sample rate: 1kHz / (1+5) = 166Hz
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_SMPLRT_DIV, SMPLRT_DIV_VAL, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Clear interrupt enables before reconfiguring
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_INT_ENABLE, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Accel low-pass filter: ACCEL_FCHOICE_B=0, A_DLPF_CFG=0
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_ACCEL_CONFIG2, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Disable DMP and FIFO
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_USER_CTRL, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_FIFO_EN, 0x00, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Interrupt: active-high (ACTL=0), push-pull (OPEN=0), latched (LATCH_INT_EN=1), cleared on any read (INT_ANYRD_2CLEAR=1) → 0x30
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_INT_PIN_CFG, 0x30, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
// Enable DATA_RDY interrupt
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_INT_ENABLE, 0x01, I2C_TIMEOUT_TICKS) != ERROR_NONE) return ERROR_RESOURCE;
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
static error_t stop(Device* device) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
if (device_get_type(i2c_controller) != &I2C_CONTROLLER_TYPE) {
|
||||
LOG_E(TAG, "Parent is not an I2C controller");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// Put device to sleep (set SLEEP bit in PWR_MGMT_1)
|
||||
if (i2c_controller_register8_set(i2c_controller, address, REG_PWR_MGMT_1, 0x40, I2C_TIMEOUT_TICKS) != ERROR_NONE) {
|
||||
LOG_E(TAG, "Failed to put MPU6886 to sleep");
|
||||
return ERROR_RESOURCE;
|
||||
}
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t mpu6886_read(Device* device, Mpu6886Data* data) {
|
||||
auto* i2c_controller = device_get_parent(device);
|
||||
auto address = GET_CONFIG(device)->address;
|
||||
|
||||
// MPU6886 is big-endian (MSB first), unlike BMI270
|
||||
auto toI16 = [](uint8_t hi, uint8_t lo) -> int16_t {
|
||||
return static_cast<int16_t>(static_cast<uint16_t>(hi) << 8 | lo);
|
||||
};
|
||||
|
||||
// Burst read: accel (6) + temp (2) + gyro (6) = 14 bytes at 0x3B
|
||||
uint8_t buf[14] = {};
|
||||
error_t error = i2c_controller_read_register(i2c_controller, address, REG_ACCEL_XOUT_H, buf, sizeof(buf), I2C_TIMEOUT_TICKS);
|
||||
if (error != ERROR_NONE) return error;
|
||||
|
||||
data->ax = toI16(buf[0], buf[1]) * ACCEL_SCALE;
|
||||
data->ay = toI16(buf[2], buf[3]) * ACCEL_SCALE;
|
||||
data->az = toI16(buf[4], buf[5]) * ACCEL_SCALE;
|
||||
// buf[6..7] = temperature (skipped)
|
||||
data->gx = toI16(buf[8], buf[9]) * GYRO_SCALE;
|
||||
data->gy = toI16(buf[10], buf[11]) * GYRO_SCALE;
|
||||
data->gz = toI16(buf[12], buf[13]) * GYRO_SCALE;
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
Driver mpu6886_driver = {
|
||||
.name = "mpu6886",
|
||||
.compatible = (const char*[]) { "invensense,mpu6886", nullptr },
|
||||
.start_device = start,
|
||||
.stop_device = stop,
|
||||
.api = nullptr,
|
||||
.device_type = nullptr,
|
||||
.owner = &mpu6886_module,
|
||||
.internal = nullptr
|
||||
};
|
||||
|
||||
} // extern "C"
|
||||
8
Drivers/mpu6886-module/source/symbols.c
Normal file
8
Drivers/mpu6886-module/source/symbols.c
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <drivers/mpu6886.h>
|
||||
#include <tactility/module.h>
|
||||
|
||||
const struct ModuleSymbol mpu6886_module_symbols[] = {
|
||||
DEFINE_MODULE_SYMBOL(mpu6886_read),
|
||||
MODULE_SYMBOL_TERMINATOR
|
||||
};
|
||||
11
Drivers/qmi8658-module/CMakeLists.txt
Normal file
11
Drivers/qmi8658-module/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../Buildscripts/module.cmake")
|
||||
|
||||
file(GLOB_RECURSE SOURCE_FILES "source/*.c*")
|
||||
|
||||
tactility_add_module(qmi8658-module
|
||||
SRCS ${SOURCE_FILES}
|
||||
INCLUDE_DIRS include/
|
||||
REQUIRES TactilityKernel
|
||||
)
|
||||
195
Drivers/qmi8658-module/LICENSE-Apache-2.0.md
Normal file
195
Drivers/qmi8658-module/LICENSE-Apache-2.0.md
Normal file
@ -0,0 +1,195 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
7
Drivers/qmi8658-module/README.md
Normal file
7
Drivers/qmi8658-module/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# QMI8658 I2C Driver
|
||||
|
||||
A driver for the `QMI8658` 6-axis IMU.
|
||||
|
||||
See https://www.waveshare.net/w/upload/5/5f/QMI8658C.pdf
|
||||
|
||||
License: [Apache v2.0](LICENSE-Apache-2.0.md)
|
||||
5
Drivers/qmi8658-module/bindings/qst,qmi8658.yaml
Normal file
5
Drivers/qmi8658-module/bindings/qst,qmi8658.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
description: QST QMI8658 6-axis IMU
|
||||
|
||||
include: [ "i2c-device.yaml" ]
|
||||
|
||||
compatible: "qst,qmi8658"
|
||||
3
Drivers/qmi8658-module/devicetree.yaml
Normal file
3
Drivers/qmi8658-module/devicetree.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- TactilityKernel
|
||||
bindings: bindings
|
||||
15
Drivers/qmi8658-module/include/bindings/qmi8658.h
Normal file
15
Drivers/qmi8658-module/include/bindings/qmi8658.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/bindings/bindings.h>
|
||||
#include <drivers/qmi8658.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DEFINE_DEVICETREE(qmi8658, struct Qmi8658Config)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
33
Drivers/qmi8658-module/include/drivers/qmi8658.h
Normal file
33
Drivers/qmi8658-module/include/drivers/qmi8658.h
Normal file
@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <tactility/error.h>
|
||||
|
||||
struct Device;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct Qmi8658Config {
|
||||
/** I2C address (0x6A when SA0=low, 0x6B when SA0=high) */
|
||||
uint8_t address;
|
||||
};
|
||||
|
||||
struct Qmi8658Data {
|
||||
float ax, ay, az; // acceleration in g (±8g range)
|
||||
float gx, gy, gz; // angular rate in °/s (±2048°/s range)
|
||||
};
|
||||
|
||||
/**
|
||||
* Read accelerometer and gyroscope data.
|
||||
* @param[in] device qmi8658 device
|
||||
* @param[out] data Pointer to Qmi8658Data to populate
|
||||
* @return ERROR_NONE on success
|
||||
*/
|
||||
error_t qmi8658_read(struct Device* device, struct Qmi8658Data* data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user