diff --git a/CMakeLists.txt b/CMakeLists.txt index adb1754b28..f254b1fa75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ if(BUILD_RESOURCES) endif() set(TARGET_DEVICE "PINETIME" CACHE STRING "Target device") -set_property(CACHE TARGET_DEVICE PROPERTY STRINGS PINETIME MOY-TFK5 MOY-TIN5 MOY-TON5 MOY-UNK) +set_property(CACHE TARGET_DEVICE PROPERTY STRINGS PINETIME MOY_TFK5 MOY_TIN5 MOY_TON5 MOY_UNK MOY_UNK2) set(PROJECT_GIT_COMMIT_HASH "") diff --git a/doc/buildAndProgram.md b/doc/buildAndProgram.md index 78bea1b458..29f7a91275 100644 --- a/doc/buildAndProgram.md +++ b/doc/buildAndProgram.md @@ -43,7 +43,7 @@ CMake configures the project according to variables you specify the command line **CMAKE_BUILD_TYPE (\*)**| Build type (Release or Debug). Release is applied by default if this variable is not specified.|`-DCMAKE_BUILD_TYPE=Debug` **BUILD_DFU (\*\*)**|Build DFU files while building (needs [adafruit-nrfutil](https://github.com/adafruit/Adafruit_nRF52_nrfutil)).|`-DBUILD_DFU=1` **BUILD_RESOURCES (\*\*)**| Generate external resource while building (needs [lv_font_conv](https://github.com/lvgl/lv_font_conv) and [lv_img_conv](https://github.com/lvgl/lv_img_conv). |`-DBUILD_RESOURCES=1` -**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY-TFK5, MOY-TIN5, MOY-TON5, MOY-UNK`|`-DTARGET_DEVICE=PINETIME` (Default) +**TARGET_DEVICE**|Target device, used for hardware configuration. Allowed: `PINETIME, MOY_TFK5, MOY_TIN5, MOY_TON5, MOY_UNK, MOY_UNK2`|`-DTARGET_DEVICE=PINETIME` (Default) #### (\*) Note about **CMAKE_BUILD_TYPE** By default, this variable is set to *Release*. It compiles the code with size and speed optimizations. We use this value for all the binaries we publish when we [release](https://github.com/InfiniTimeOrg/InfiniTime/releases) new versions of InfiniTime. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e59c0d814b..0e8763e0f4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -443,9 +443,11 @@ list(APPEND SOURCE_FILES drivers/DebugPins.cpp drivers/InternalFlash.cpp drivers/Hrs3300.cpp + drivers/AccelerationSensor.cpp drivers/Bma421.cpp drivers/Bma421_C/bma4.c drivers/Bma421_C/bma423.c + drivers/SC7A20.cpp components/battery/BatteryController.cpp components/ble/BleController.cpp components/ble/NotificationManager.cpp @@ -510,9 +512,11 @@ list(APPEND RECOVERY_SOURCE_FILES drivers/DebugPins.cpp drivers/InternalFlash.cpp drivers/Hrs3300.cpp + drivers/AccelerationSensor.cpp drivers/Bma421.cpp drivers/Bma421_C/bma4.c drivers/Bma421_C/bma423.c + drivers/SC7A20.cpp components/battery/BatteryController.cpp components/ble/BleController.cpp components/ble/NotificationManager.cpp @@ -626,9 +630,12 @@ set(INCLUDE_FILES drivers/InternalFlash.h drivers/Hrs3300.h drivers/PinMap.h + drivers/AccelerationSensor.h drivers/Bma421.h drivers/Bma421_C/bma4.c drivers/Bma421_C/bma423.c + drivers/SC7A20.h + drivers/SC7A20_registers.h components/battery/BatteryController.h components/ble/BleController.h components/ble/NotificationManager.h @@ -656,6 +663,7 @@ set(INCLUDE_FILES components/timer/TimerController.h components/alarm/AlarmController.h drivers/Cst816s.h + drivers/Cst816s_registers.h FreeRTOS/portmacro.h FreeRTOS/portmacro_cmsis.h libs/date/include/date/tz.h @@ -798,23 +806,40 @@ add_definitions(-DTARGET_DEVICE_NAME="${TARGET_DEVICE}") if(TARGET_DEVICE STREQUAL "PINETIME") add_definitions(-DDRIVER_PINMAP_PINETIME) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL -elseif(TARGET_DEVICE STREQUAL "MOY-TFK5") # P8a + add_definitions(-DDRIVER_ACC_BMA421) + add_definitions(-DDRIVER_TOUCH_DYNAMIC) +elseif(TARGET_DEVICE STREQUAL "MOY_TFK5") # P8a add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL -elseif(TARGET_DEVICE STREQUAL "MOY-TIN5") # P8a variant 2 + add_definitions(-DDRIVER_ACC_BMA421) + add_definitions(-DDRIVER_TOUCH_GESTURE) +elseif(TARGET_DEVICE STREQUAL "MOY_TIN5") # P8a variant 2 add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=1) # XTAL -elseif(TARGET_DEVICE STREQUAL "MOY-TON5") # P8b + add_definitions(-DDRIVER_ACC_SC7A20) + add_definitions(-DDRIVER_TOUCH_GESTURE) +elseif(TARGET_DEVICE STREQUAL "MOY_TON5") # P8b add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) -elseif(TARGET_DEVICE STREQUAL "MOY-UNK") # P8b mirrored + add_definitions(-DDRIVER_ACC_SC7A20) + add_definitions(-DDRIVER_TOUCH_REPORT) +elseif(TARGET_DEVICE STREQUAL "MOY_UNK") # P8b mirrored add_definitions(-DDRIVER_PINMAP_P8) add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) add_definitions(-DDRIVER_DISPLAY_MIRROR) + add_definitions(-DDRIVER_ACC_SC7A20) + add_definitions(-DDRIVER_TOUCH_REPORT) +elseif(TARGET_DEVICE STREQUAL "MOY_UNK2") # P8b variant + add_definitions(-DDRIVER_PINMAP_P8) + add_definitions(-DCLOCK_CONFIG_LF_SRC=0) # RC + add_definitions(-DMYNEWT_VAL_BLE_LL_SCA=500) + add_definitions(-DCLOCK_CONFIG_LF_CAL_ENABLED=1) + add_definitions(-DDRIVER_ACC_SC7A20) + add_definitions(-DDRIVER_TOUCH_GESTURE) else() message(FATAL_ERROR "Invalid TARGET_DEVICE") endif() diff --git a/src/components/ble/MotionService.cpp b/src/components/ble/MotionService.cpp index 121ad3b08a..8750f96672 100644 --- a/src/components/ble/MotionService.cpp +++ b/src/components/ble/MotionService.cpp @@ -90,20 +90,22 @@ void MotionService::OnNewStepCountValue(uint32_t stepCount) { ble_gattc_notify_custom(connectionHandle, stepCountHandle, om); } -void MotionService::OnNewMotionValues(int16_t x, int16_t y, int16_t z) { + +void MotionService::OnNewMotionValues(int16_t* samples, uint16_t samples_length) { if (!motionValuesNoficationEnabled) return; - int16_t buffer[3] = {motionController.X(), motionController.Y(), motionController.Z()}; - auto* om = ble_hs_mbuf_from_flat(buffer, 3 * sizeof(int16_t)); + if (samples_length > 0 && samples != nullptr) { + auto* om = ble_hs_mbuf_from_flat(samples, samples_length * 3 * sizeof(int16_t)); - uint16_t connectionHandle = system.nimble().connHandle(); + uint16_t connectionHandle = system.nimble().connHandle(); - if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { - return; - } + if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) { + return; + } - ble_gattc_notify_custom(connectionHandle, motionValuesHandle, om); + ble_gattc_notify_custom(connectionHandle, motionValuesHandle, om); + } } void MotionService::SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle) { diff --git a/src/components/ble/MotionService.h b/src/components/ble/MotionService.h index 1b4ac0a37e..172ed270fc 100644 --- a/src/components/ble/MotionService.h +++ b/src/components/ble/MotionService.h @@ -18,7 +18,7 @@ namespace Pinetime { void Init(); int OnStepCountRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt* context); void OnNewStepCountValue(uint32_t stepCount); - void OnNewMotionValues(int16_t x, int16_t y, int16_t z); + void OnNewMotionValues(int16_t* samples, uint16_t samples_length); void SubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle); void UnsubscribeNotification(uint16_t connectionHandle, uint16_t attributeHandle); diff --git a/src/components/motion/MotionController.cpp b/src/components/motion/MotionController.cpp index 7dd321271c..518ad08c55 100644 --- a/src/components/motion/MotionController.cpp +++ b/src/components/motion/MotionController.cpp @@ -2,13 +2,13 @@ #include "os/os_cputime.h" using namespace Pinetime::Controllers; -void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) { +void MotionController::Update(uint32_t nbSteps, int16_t x, int16_t y, int16_t z, int16_t* samples, uint16_t samples_length) { if (this->nbSteps != nbSteps && service != nullptr) { service->OnNewStepCountValue(nbSteps); } if (service != nullptr && (this->x != x || this->y != y || this->z != z)) { - service->OnNewMotionValues(x, y, z); + service->OnNewMotionValues(samples, samples_length); } this->x = x; @@ -62,6 +62,7 @@ bool MotionController::Should_ShakeWake(uint16_t thresh) { lastZForShake = z; return wake; } + int32_t MotionController::currentShakeSpeed() { return accumulatedspeed; } @@ -69,19 +70,11 @@ int32_t MotionController::currentShakeSpeed() { void MotionController::IsSensorOk(bool isOk) { isSensorOk = isOk; } -void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) { - switch (types) { - case Drivers::Bma421::DeviceTypes::BMA421: - this->deviceType = DeviceTypes::BMA421; - break; - case Drivers::Bma421::DeviceTypes::BMA425: - this->deviceType = DeviceTypes::BMA425; - break; - default: - this->deviceType = DeviceTypes::Unknown; - break; - } + +void MotionController::Init(Pinetime::Drivers::AccelerationDeviceTypes types) { + this->deviceType = types; } + void MotionController::SetService(Pinetime::Controllers::MotionService* service) { this->service = service; } diff --git a/src/components/motion/MotionController.h b/src/components/motion/MotionController.h index f80b11b994..da9dd16050 100644 --- a/src/components/motion/MotionController.h +++ b/src/components/motion/MotionController.h @@ -8,13 +8,7 @@ namespace Pinetime { namespace Controllers { class MotionController { public: - enum class DeviceTypes { - Unknown, - BMA421, - BMA425, - }; - - void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps); + void Update(uint32_t nbSteps, int16_t x, int16_t y, int16_t z, int16_t* samples, uint16_t samples_length); int16_t X() const { return x; @@ -44,11 +38,11 @@ namespace Pinetime { return isSensorOk; } - DeviceTypes DeviceType() const { + Pinetime::Drivers::AccelerationDeviceTypes DeviceType() const { return deviceType; } - void Init(Pinetime::Drivers::Bma421::DeviceTypes types); + void Init(Pinetime::Drivers::AccelerationDeviceTypes types); void SetService(Pinetime::Controllers::MotionService* service); private: @@ -59,7 +53,7 @@ namespace Pinetime { int16_t z; int16_t lastYForWakeUp = 0; bool isSensorOk = false; - DeviceTypes deviceType = DeviceTypes::Unknown; + Pinetime::Drivers::AccelerationDeviceTypes deviceType = Pinetime::Drivers::AccelerationDeviceTypes::Unknown; Pinetime::Controllers::MotionService* service = nullptr; int16_t lastXForShake = 0; diff --git a/src/displayapp/DummyLittleVgl.h b/src/displayapp/DummyLittleVgl.h index 05355a9730..cf105cc504 100644 --- a/src/displayapp/DummyLittleVgl.h +++ b/src/displayapp/DummyLittleVgl.h @@ -33,6 +33,8 @@ namespace Pinetime { } void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) { } + void SetNewTap(uint16_t x, uint16_t y) { + } }; } } diff --git a/src/displayapp/LittleVgl.cpp b/src/displayapp/LittleVgl.cpp index d5f31848c4..75e069c524 100644 --- a/src/displayapp/LittleVgl.cpp +++ b/src/displayapp/LittleVgl.cpp @@ -180,6 +180,14 @@ void LittleVgl::SetNewTouchPoint(uint16_t x, uint16_t y, bool contact) { tap_x = x; tap_y = y; tapped = contact; + simulate_tap_release = false; +} + +void LittleVgl::SetNewTap(uint16_t x, uint16_t y) { + tap_x = x; + tap_y = y; + tapped = true; + simulate_tap_release = true; } bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) { @@ -187,6 +195,12 @@ bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) { ptr->point.y = tap_y; if (tapped) { ptr->state = LV_INDEV_STATE_PR; + if (simulate_tap_release) { + // If a tap consists of only a single event, enqueue a synthetic release state update + tapped = false; + simulate_tap_release = false; + return true; + } } else { ptr->state = LV_INDEV_STATE_REL; } diff --git a/src/displayapp/LittleVgl.h b/src/displayapp/LittleVgl.h index 4582616533..e35da7fbbb 100644 --- a/src/displayapp/LittleVgl.h +++ b/src/displayapp/LittleVgl.h @@ -25,6 +25,7 @@ namespace Pinetime { bool GetTouchPadInfo(lv_indev_data_t* ptr); void SetFullRefresh(FullRefreshDirections direction); void SetNewTouchPoint(uint16_t x, uint16_t y, bool contact); + void SetNewTap(uint16_t x, uint16_t y); bool GetFullRefresh() { bool returnValue = fullRefresh; @@ -62,6 +63,7 @@ namespace Pinetime { uint16_t tap_x = 0; uint16_t tap_y = 0; bool tapped = false; + bool simulate_tap_release = false; }; } } diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index 01c351955e..eaf33c688d 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -17,13 +17,15 @@ using namespace Pinetime::Applications::Screens; namespace { - const char* ToString(const Pinetime::Controllers::MotionController::DeviceTypes deviceType) { + const char* ToString(const Pinetime::Drivers::AccelerationDeviceTypes deviceType) { switch (deviceType) { - case Pinetime::Controllers::MotionController::DeviceTypes::BMA421: + case Pinetime::Drivers::AccelerationDeviceTypes::BMA421: return "BMA421"; - case Pinetime::Controllers::MotionController::DeviceTypes::BMA425: + case Pinetime::Drivers::AccelerationDeviceTypes::BMA425: return "BMA425"; - case Pinetime::Controllers::MotionController::DeviceTypes::Unknown: + case Pinetime::Drivers::AccelerationDeviceTypes::SC7A20: + return "SC7A20"; + case Pinetime::Drivers::AccelerationDeviceTypes::Unknown: return "???"; } return "???"; diff --git a/src/drivers/AccelerationSensor.cpp b/src/drivers/AccelerationSensor.cpp new file mode 100644 index 0000000000..c0d2bda168 --- /dev/null +++ b/src/drivers/AccelerationSensor.cpp @@ -0,0 +1,39 @@ +#include "AccelerationSensor.h" + +namespace Pinetime { + namespace Drivers { + + AccelerationSensor::AccelerationSensor(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster(twiMaster), deviceAddress(twiAddress) { + } + + void AccelerationSensor::SoftReset() { + } + + void AccelerationSensor::Init() { + } + + AccelerationValues AccelerationSensor::Process() { + return {0}; + } + + void AccelerationSensor::ResetStepCounter() { + } + + void AccelerationSensor::Read(uint8_t registerAddress, uint8_t* buffer, size_t size) { + twiMaster.Read(deviceAddress, registerAddress, buffer, size); + } + + void AccelerationSensor::Write(uint8_t registerAddress, const uint8_t* data, size_t size) { + twiMaster.Write(deviceAddress, registerAddress, data, size); + } + + AccelerationDeviceTypes AccelerationSensor::DeviceType() const { + return deviceType; + } + + bool AccelerationSensor::IsInitialized() const { + return isInitialized; + } + + } +} diff --git a/src/drivers/AccelerationSensor.h b/src/drivers/AccelerationSensor.h new file mode 100644 index 0000000000..428948c20f --- /dev/null +++ b/src/drivers/AccelerationSensor.h @@ -0,0 +1,47 @@ +#pragma once + +#include "drivers/TwiMaster.h" + +namespace Pinetime { + namespace Drivers { + + enum class AccelerationDeviceTypes : uint8_t { Unknown, BMA421, BMA425, SC7A20 }; + + struct AccelerationValues { + uint32_t steps; + int16_t x; + int16_t y; + int16_t z; + int16_t* samples = nullptr; + uint16_t samples_length = 0; + }; + + class AccelerationSensor { + public: + AccelerationSensor(TwiMaster& twiMaster, uint8_t twiAddress); + AccelerationSensor(const AccelerationSensor&) = delete; + AccelerationSensor& operator=(const AccelerationSensor&) = delete; + AccelerationSensor(AccelerationSensor&&) = delete; + AccelerationSensor& operator=(AccelerationSensor&&) = delete; + + virtual void SoftReset(); + virtual void Init(); + virtual AccelerationValues Process(); + virtual void ResetStepCounter(); + + void Read(uint8_t registerAddress, uint8_t* buffer, size_t size); + void Write(uint8_t registerAddress, const uint8_t* data, size_t size); + + bool IsInitialized() const; + AccelerationDeviceTypes DeviceType() const; + + protected: + TwiMaster& twiMaster; + uint8_t deviceAddress; + bool isInitialized = false; + AccelerationDeviceTypes deviceType = AccelerationDeviceTypes::Unknown; + int16_t fifo[32][3] = {0}; + }; + + } +} diff --git a/src/drivers/Bma421.cpp b/src/drivers/Bma421.cpp index 539cc8d1d3..313ba7c56f 100644 --- a/src/drivers/Bma421.cpp +++ b/src/drivers/Bma421.cpp @@ -1,11 +1,9 @@ #include "drivers/Bma421.h" -#include -#include #include "drivers/TwiMaster.h" #include +#include using namespace Pinetime::Drivers; - namespace { int8_t user_i2c_read(uint8_t reg_addr, uint8_t* reg_data, uint32_t length, void* intf_ptr) { auto bma421 = static_cast(intf_ptr); @@ -24,7 +22,7 @@ namespace { } } -Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, deviceAddress {twiAddress} { +Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : AccelerationSensor(twiMaster, twiAddress) { bma.intf = BMA4_I2C_INTF; bma.bus_read = user_i2c_read; bma.bus_write = user_i2c_write; @@ -32,94 +30,106 @@ Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster} bma.intf_ptr = this; bma.delay_us = user_delay; bma.read_write_len = 16; + bma.resolution = 12; } void Bma421::Init() { - if (not isResetOk) + if (!isResetOk) return; // Call SoftReset (and reset TWI device) first! + // Initialize interface auto ret = bma423_init(&bma); if (ret != BMA4_OK) return; + // Identify chip by ID. The driver code has been modified to handle BMA421 as BMA423 switch (bma.chip_id) { case BMA423_CHIP_ID: - deviceType = DeviceTypes::BMA421; + deviceType = AccelerationDeviceTypes::BMA421; break; case BMA425_CHIP_ID: - deviceType = DeviceTypes::BMA425; + deviceType = AccelerationDeviceTypes::BMA425; break; default: - deviceType = DeviceTypes::Unknown; + deviceType = AccelerationDeviceTypes::Unknown; break; } + // Load proprietary firmware blob required for step counting engine ret = bma423_write_config_file(&bma); if (ret != BMA4_OK) return; - ret = bma4_set_interrupt_mode(BMA4_LATCH_MODE, &bma); - if (ret != BMA4_OK) - return; - + // Enable step counter and accelerometer, disable step detector ret = bma423_feature_enable(BMA423_STEP_CNTR, 1, &bma); if (ret != BMA4_OK) return; - ret = bma423_step_detector_enable(0, &bma); if (ret != BMA4_OK) return; - ret = bma4_set_accel_enable(1, &bma); if (ret != BMA4_OK) return; + // Configure FIFO + ret = bma4_set_fifo_config(BMA4_FIFO_ACCEL, 1, &bma); + if (ret != BMA4_OK) + return; + fifo_frame.data = (uint8_t*) &fifo; + fifo_frame.length = sizeof(fifo); + fifo_frame.fifo_data_enable = BMA4_FIFO_A_ENABLE; + fifo_frame.fifo_header_enable = 0; + + // Configure accelerometer struct bma4_accel_config accel_conf; - accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ; + accel_conf.odr = BMA4_OUTPUT_DATA_RATE_200HZ; accel_conf.range = BMA4_ACCEL_RANGE_2G; accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4; - accel_conf.perf_mode = BMA4_CIC_AVG_MODE; + accel_conf.perf_mode = BMA4_CONTINUOUS_MODE; ret = bma4_set_accel_config(&accel_conf, &bma); if (ret != BMA4_OK) return; - isOk = true; + isInitialized = true; } -void Bma421::Reset() { - uint8_t data = 0xb6; - twiMaster.Write(deviceAddress, 0x7E, &data, 1); -} - -void Bma421::Read(uint8_t registerAddress, uint8_t* buffer, size_t size) { - twiMaster.Read(deviceAddress, registerAddress, buffer, size); -} - -void Bma421::Write(uint8_t registerAddress, const uint8_t* data, size_t size) { - twiMaster.Write(deviceAddress, registerAddress, data, size); -} - -Bma421::Values Bma421::Process() { - if (not isOk) +AccelerationValues Bma421::Process() { + if (!isInitialized) return {}; - struct bma4_accel data; - bma4_read_accel_xyz(&data, &bma); + // Dump entire FIFO into buffer + bma4_read_fifo_data(&fifo_frame, &bma); + // Decode FIFO frames in-place sequentially + uint16_t length = 32; // Should be about 20 (200 Hz ODR / 10 Hz main loop) + bma4_extract_accel((bma4_accel*) fifo, &length, &fifo_frame, &bma); + + // Read step counter engine uint32_t steps = 0; bma423_step_counter_output(&steps, &bma); - int32_t temperature; + // Read temperature sensor + int32_t temperature = 0; bma4_get_temperature(&temperature, BMA4_DEG, &bma); temperature = temperature / 1000; + // Read sport activity mode uint8_t activity = 0; bma423_activity_output(&activity, &bma); - // X and Y axis are swapped because of the way the sensor is mounted in the PineTime - return {steps, data.y, data.x, data.z}; -} -bool Bma421::IsOk() const { - return isOk; + // Compute averages of FIFO + int16_t avgs[3] = {0}; + for (uint8_t i = 0; i < length; i++) { + // X and Y axis are swapped because of the way the sensor is mounted in the PineTime + int16_t swap = fifo[i][0]; + fifo[i][0] = fifo[i][1]; + fifo[i][1] = swap; + for (uint8_t j = 0; j < 3; j++) + avgs[j] += fifo[i][j]; + } + for (uint8_t j = 0; j < 3; j++) + avgs[j] /= length; + + return {steps, avgs[0], avgs[1], avgs[2], (int16_t*) fifo, length}; } void Bma421::ResetStepCounter() { @@ -133,6 +143,3 @@ void Bma421::SoftReset() { nrf_delay_ms(1); } } -Bma421::DeviceTypes Bma421::DeviceType() const { - return deviceType; -} diff --git a/src/drivers/Bma421.h b/src/drivers/Bma421.h index ac5c707ffc..f2a6a1b9d6 100644 --- a/src/drivers/Bma421.h +++ b/src/drivers/Bma421.h @@ -1,18 +1,12 @@ #pragma once + +#include "drivers/AccelerationSensor.h" #include namespace Pinetime { namespace Drivers { - class TwiMaster; - class Bma421 { + class Bma421 : public AccelerationSensor { public: - enum class DeviceTypes : uint8_t { Unknown, BMA421, BMA425 }; - struct Values { - uint32_t steps; - int16_t x; - int16_t y; - int16_t z; - }; Bma421(TwiMaster& twiMaster, uint8_t twiAddress); Bma421(const Bma421&) = delete; Bma421& operator=(const Bma421&) = delete; @@ -23,24 +17,13 @@ namespace Pinetime { /// Init() method to allow the caller to uninit and then reinit the TWI device after the softreset. void SoftReset(); void Init(); - Values Process(); + AccelerationValues Process(); void ResetStepCounter(); - void Read(uint8_t registerAddress, uint8_t* buffer, size_t size); - void Write(uint8_t registerAddress, const uint8_t* data, size_t size); - - bool IsOk() const; - DeviceTypes DeviceType() const; - private: - void Reset(); - - TwiMaster& twiMaster; - uint8_t deviceAddress = 0x18; - struct bma4_dev bma; - bool isOk = false; bool isResetOk = false; - DeviceTypes deviceType = DeviceTypes::Unknown; + struct bma4_dev bma; + struct bma4_fifo_frame fifo_frame; }; } -} \ No newline at end of file +} diff --git a/src/drivers/Cst816s.cpp b/src/drivers/Cst816s.cpp index cf10c895f5..4fba71d135 100644 --- a/src/drivers/Cst816s.cpp +++ b/src/drivers/Cst816s.cpp @@ -1,108 +1,103 @@ #include "drivers/Cst816s.h" +#include "drivers/PinMap.h" #include #include #include #include -#include "drivers/PinMap.h" using namespace Pinetime::Drivers; -/* References : +/* + * References : * This implementation is based on this article : * https://medium.com/@ly.lee/building-a-rust-driver-for-pinetimes-touch-controller-cbc1a5d5d3e9 Touch panel datasheet (weird chinese * translation) : https://wiki.pine64.org/images/5/51/CST816S%E6%95%B0%E6%8D%AE%E6%89%8B%E5%86%8CV1.1.en.pdf * - * TODO : we need a complete datasheet and protocol reference! + * TODO: We need a complete datasheet and protocol reference! + * For register desciptions, see Cst816s_registers.h. Information was collected from various chinese datasheets and documents. * */ Cst816S::Cst816S(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster {twiMaster}, twiAddress {twiAddress} { } bool Cst816S::Init() { + // Reset the touch driver nrf_gpio_cfg_output(PinMap::Cst816sReset); nrf_gpio_pin_clear(PinMap::Cst816sReset); - vTaskDelay(5); + vTaskDelay(10); nrf_gpio_pin_set(PinMap::Cst816sReset); vTaskDelay(50); - // Wake the touchpanel up - uint8_t dummy; - twiMaster.Read(twiAddress, 0x15, &dummy, 1); - vTaskDelay(5); - twiMaster.Read(twiAddress, 0xa7, &dummy, 1); - vTaskDelay(5); - - // TODO This function check that the device IDs from the controller are equal to the ones - // we expect. However, it seems to return false positive (probably in case of communication issue). - // Also, it seems that some users have pinetimes that works correctly but that report different device IDs - // Until we know more about this, we'll just read the IDs but not take any action in case they are not 'valid' - CheckDeviceIds(); - - /* - [2] EnConLR - Continuous operation can slide around - [1] EnConUD - Slide up and down to enable continuous operation - [0] EnDClick - Enable Double-click action - */ - static constexpr uint8_t motionMask = 0b00000101; - twiMaster.Write(twiAddress, 0xEC, &motionMask, 1); - - /* - [7] EnTest - Interrupt pin to test, enable automatic periodic issued after a low pulse. - [6] EnTouch - When a touch is detected, a periodic pulsed Low. - [5] EnChange - Upon detecting a touch state changes, pulsed Low. - [4] EnMotion - When the detected gesture is pulsed Low. - [0] OnceWLP - Press gesture only issue a pulse signal is low. - */ - static constexpr uint8_t irqCtl = 0b01110000; - twiMaster.Write(twiAddress, 0xFA, &irqCtl, 1); + // Chip ID is suspected to be dependent on embedded firmware and factory configuration, + // and not (just) on hardware type and revision. + if (twiMaster.Read(twiAddress, CHIP_ID, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + chipId = 0xFF; + } + // Vendor / project ID and firmware version can vary between devices. + if (twiMaster.Read(twiAddress, PROJ_ID, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + vendorId = 0xFF; + } + if (twiMaster.Read(twiAddress, FW_VERSION, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) { + fwVersion = 0xFF; + } + + // These configuration settings will be ignored by chips which were + // fused / pre-configured in the factory (GESTURE and REPORT settings). + // This mainly applies to CST716, however there may be CST816S with static configurations as well. + // The other, freely configureable ones (DYNAMIC), are configured in reporting mode here. + + // Configure motion behaviour + static constexpr uint8_t motionMask = MOTION_MASK_EN_DCLICK | MOTION_MASK_EN_CON_UD | MOTION_MASK_EN_CON_LR; + twiMaster.Write(twiAddress, MOTION_MASK, &motionMask, 1); + + // Configure interrupt generating events + static constexpr uint8_t irqCtl = IRQ_CTL_EN_MOTION | IRQ_CTL_EN_CHANGE | IRQ_CTL_EN_TOUCH; + twiMaster.Write(twiAddress, IRQ_CTL, &irqCtl, 1); return true; } Cst816S::TouchInfos Cst816S::GetTouchInfo() { - Cst816S::TouchInfos info; - uint8_t touchData[7]; - - auto ret = twiMaster.Read(twiAddress, 0, touchData, sizeof(touchData)); - if (ret != TwiMaster::ErrorCodes::NoError) { - info.isValid = false; - return info; + // Some chips fail to wake up even though the reset pin has been toggled. + // They only provide an I2C communication window after a touch interrupt, + // so the first touch interrupt is used to force initialisation. + if (firstEvent) { + Init(); + firstEvent = false; + // The data registers should now be reset, so this touch event will be detected as invalid. } - // This can only be 0 or 1 - uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f; - uint8_t xHigh = touchData[touchXHighIndex] & 0x0f; - uint8_t xLow = touchData[touchXLowIndex]; - uint16_t x = (xHigh << 8) | xLow; - uint8_t yHigh = touchData[touchYHighIndex] & 0x0f; - uint8_t yLow = touchData[touchYLowIndex]; - uint16_t y = (yHigh << 8) | yLow; - Gestures gesture = static_cast(touchData[gestureIndex]); - - // Validity check - if (x >= maxX || y >= maxY || - (gesture != Gestures::None && gesture != Gestures::SlideDown && gesture != Gestures::SlideUp && gesture != Gestures::SlideLeft && - gesture != Gestures::SlideRight && gesture != Gestures::SingleTap && gesture != Gestures::DoubleTap && - gesture != Gestures::LongPress)) { - info.isValid = false; + // Read gesture metadata and first touch point coordinate block + Cst816S::TouchInfos info; + uint8_t touchData[P1_Y_POS_L + 1]; + auto ret = twiMaster.Read(twiAddress, 0x00, touchData, sizeof(touchData)); + if (ret != TwiMaster::ErrorCodes::NoError) return info; - } - info.x = x; - info.y = y; - info.touching = (nbTouchPoints > 0); - info.gesture = gesture; - info.isValid = true; + // Assemble 12 bit point coordinates from lower 8 bits and higher 4 bits + info.x = ((touchData[P1_X_POS_H] & POS_H_POS_MASK) << 8) | touchData[P1_X_POS_L]; + info.y = ((touchData[P1_Y_POS_H] & POS_H_POS_MASK) << 8) | touchData[P1_Y_POS_L]; + // Evaluate number of touch points + info.touching = (touchData[TD_STATUS] & TD_STATUS_MASK) > 0; + // Decode gesture ID + info.gesture = static_cast(touchData[GESTURE_ID]); + + // Validity check, verify value ranges + info.isValid = (info.x < maxX && info.y < maxY && + (info.gesture == Gestures::None || info.gesture == Gestures::SlideDown || info.gesture == Gestures::SlideUp || + info.gesture == Gestures::SlideLeft || info.gesture == Gestures::SlideRight || info.gesture == Gestures::SingleTap || + info.gesture == Gestures::DoubleTap || info.gesture == Gestures::LongPress)); + return info; } void Cst816S::Sleep() { - nrf_gpio_pin_clear(PinMap::Cst816sReset); - vTaskDelay(5); - nrf_gpio_pin_set(PinMap::Cst816sReset); - vTaskDelay(50); - static constexpr uint8_t sleepValue = 0x03; - twiMaster.Write(twiAddress, 0xA5, &sleepValue, 1); + // This only controls the CST716, the CST816S will ignore this register. + // The CST816S power state is managed using auto-sleep. + + static constexpr uint8_t sleepValue = PWR_MODE_DEEP_SLEEP; + twiMaster.Write(twiAddress, PWR_MODE_CST716, &sleepValue, 1); + NRF_LOG_INFO("[TOUCHPANEL] Sleep"); } @@ -110,21 +105,3 @@ void Cst816S::Wakeup() { Init(); NRF_LOG_INFO("[TOUCHPANEL] Wakeup"); } - -bool Cst816S::CheckDeviceIds() { - // There's mixed information about which register contains which information - if (twiMaster.Read(twiAddress, 0xA7, &chipId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - chipId = 0xFF; - return false; - } - if (twiMaster.Read(twiAddress, 0xA8, &vendorId, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - vendorId = 0xFF; - return false; - } - if (twiMaster.Read(twiAddress, 0xA9, &fwVersion, 1) == TwiMaster::ErrorCodes::TransactionFailed) { - fwVersion = 0xFF; - return false; - } - - return chipId == 0xb4 && vendorId == 0 && fwVersion == 1; -} diff --git a/src/drivers/Cst816s.h b/src/drivers/Cst816s.h index 9d426c9db9..fde7149f04 100644 --- a/src/drivers/Cst816s.h +++ b/src/drivers/Cst816s.h @@ -1,5 +1,6 @@ #pragma once +#include "drivers/Cst816s_registers.h" #include "drivers/TwiMaster.h" namespace Pinetime { @@ -7,14 +8,15 @@ namespace Pinetime { class Cst816S { public: enum class Gestures : uint8_t { - None = 0x00, - SlideDown = 0x01, - SlideUp = 0x02, - SlideLeft = 0x03, - SlideRight = 0x04, - SingleTap = 0x05, - DoubleTap = 0x0B, - LongPress = 0x0C + None = GESTURE_ID_NONE, + SlideDown = GESTURE_ID_SLIDE_DOWN, + SlideUp = GESTURE_ID_SLIDE_UP, + SlideLeft = GESTURE_ID_SLIDE_LEFT, + SlideRight = GESTURE_ID_SLIDE_RIGHT, + SingleTap = GESTURE_ID_SINGLE_TAP, + DoubleTap = GESTURE_ID_DOUBLE_TAP, + LongPress = GESTURE_ID_LONG_PRESS, + Invalid = 0xFF }; struct TouchInfos { uint16_t x = 0; @@ -46,21 +48,6 @@ namespace Pinetime { } private: - bool CheckDeviceIds(); - - // Unused/Unavailable commented out - static constexpr uint8_t gestureIndex = 1; - static constexpr uint8_t touchPointNumIndex = 2; - // static constexpr uint8_t touchEventIndex = 3; - static constexpr uint8_t touchXHighIndex = 3; - static constexpr uint8_t touchXLowIndex = 4; - // static constexpr uint8_t touchIdIndex = 5; - static constexpr uint8_t touchYHighIndex = 5; - static constexpr uint8_t touchYLowIndex = 6; - // static constexpr uint8_t touchStep = 6; - // static constexpr uint8_t touchXYIndex = 7; - // static constexpr uint8_t touchMiscIndex = 8; - static constexpr uint8_t maxX = 240; static constexpr uint8_t maxY = 240; @@ -70,6 +57,8 @@ namespace Pinetime { uint8_t chipId; uint8_t vendorId; uint8_t fwVersion; + + bool firstEvent = true; }; } diff --git a/src/drivers/Cst816s_registers.h b/src/drivers/Cst816s_registers.h new file mode 100644 index 0000000000..edec944fde --- /dev/null +++ b/src/drivers/Cst816s_registers.h @@ -0,0 +1,162 @@ +#pragma once + +// CST816(S) / CST716 High-performance self-capacitive touch chip +// Hynitron, www.hynitron.com + +// Manually assembled from auto-translated incomplete datasheets and documents. + +// The device contains an embedded firmware, behaviour may differ. +// Code fragments on how to update the internal firmware exist, but not much is know about +// the internal architecture. No verified compatible binary firmware has been found yet. + +// The "S" suffix stands for speed, i.e. double the sample rate (>100 Hz instead of ~50 Hz (?)). + +// The CST816S has an auto-sleep low-power mode, from which it can wake via a touch event. +// In low-power sleep, the I2C interface is inactive. +// It sends a gesture event while the gesture is still being performed. + +// The CST716 has no auto-sleep functionality, and sends a gesture event after the gesture has been completed. +// Its I2C interface is always active, as it stays in normal mode. + +// Both chips are able to enter a deep-sleep mode, from which they can only wake via a signal on the reset pin. + +// Registers + +// * = Works only on chips which have not been preconfigured (fused) in the factory. +// CST716 are probably always fused, default: reporting mode. +// CST816S can be reconfigureable, but can be fused as well. Some fused ones report with a CST716 chip ID. + +// ** = Auto-sleep related registers. Only on the CST816S. + +#define DEV_MODE 0x00 // readonly. Always 0. +#define GESTURE_ID 0x01 // readonly. Which gesture was detected. +#define TD_STATUS 0x02 // readonly. Number of touch points. (0 - 2). + +#define P1_X_POS_H 0x03 // readonly. Point 1: X touch event flag + X touch coordinate MSB. +#define P1_X_POS_L 0x04 // readonly. Point 1: X touch coordinate LSB. +#define P1_Y_POS_H 0x05 // readonly. Point 1: Y touch event flag + Y touch coordinate MSB. +#define P1_Y_POS_L 0x06 // readonly. Point 1: Y touch coordinate LSB. +#define P1_WEIGHT 0x07 // readonly. Point 1: Touch weight. +#define P1_MISC 0x08 // readonly. Point 1: Touch area. + +#define P2_X_POS_H 0x09 // readonly. Point 2: X touch event flag + X touch coordinate MSB. +#define P2_X_POS_L 0x0A // readonly. Point 2: X touch coordinate LSB. +#define P2_Y_POS_H 0x0B // readonly. Point 2: Y touch event flag + Y touch coordinate MSB. +#define P2_Y_POS_L 0x0C // readonly. Point 2: Y touch coordinate LSB. +#define P2_WEIGHT 0x0D // readonly. Point 2: Touch weight. +#define P2_MISC 0x0E // readonly. Point 2: Touch area. + +#define BPC0_H 0xB0 // unknown. BPC0 value MSB. +#define BPC0_L 0xB1 // unknown. BPC0 value LSB. +#define BPC1_H 0xB2 // unknown. BPC1 value MSB. +#define BPC1_L 0xB3 // unknown. BPC1 value LSB. + +#define CHIP_ID 0xA7 // readonly. Chip / firmware type ID. +#define PROJ_ID 0xA8 // readonly. Vendor / project ID. +#define FW_VERSION 0xA9 // readonly. Firmware version. + +#define MOTION_MASK 0xEC // read-write*. Motion configuration. +#define IRQ_PULSE_WIDTH 0xED // read-write. Interrupt low pulse output width. Unit 0.1ms, possible value: 1~200. The default value is 10. +// read-write. Normal fast detection cycle. This value affects LpAutoWakeTime and AutoSleepTime. Unit 10ms, possible value: 1~30. The +// default value is 1. +#define NOR_SCAN_PER 0xEE +// read-write. Gesture detection sliding partition angle control. Angle=tan(c)*10. c is the angle based on the positive direction of +// the x-axis. +#define MOTION_S1_ANGLE 0xEF + +#define LP_SCAN_RAW1_H 0xF0 // readonly**. Low power scan, MSB of the reference value of channel 1. +#define LP_SCAN_RAW1_L 0xF1 // readonly**. Low power scan, LSB of the reference value of channel 1. +#define LP_SCAN_RAW2_H 0xF2 // readonly**. Low power scan, MSB of the reference value of channel 2. +#define LP_SCAN_RAW2_L 0xF3 // readonly**. Low power scan, LSB of the reference value of channel 2. +// read-write**. Automatic recalibration cycle in low power mode. The unit is 1 minute, and the possible value is 1 to 5. The default +// value is 5. +#define LP_AUTO_WAKE_TIME 0xF4 +// read-write**. Low-power scan wake-up threshold. The smaller the more sensitive. Possible values: 1 to 255. The default value +// is 48. +#define LP_SCAN_TH 0xF5 +// read-write**. Low power scan range. The larger the more sensitive, the higher the power consumption. Possible values: 0, 1, 2, 3. +// The default value is 3. +#define LP_SCAN_WIN 0xF6 +// read-write**. Low power scan frequency. The smaller the more sensitive. Possible values: 1 to 255. The default value is 7. +#define LP_SCAN_FREQ 0xF7 +#define LP_SCAN_I_DAC 0xF8 // read-write**. Low power scan current. The smaller the more sensitive. Possible values: 1 to 255. +// read-write**. Automatically enter low power mode when there is no touch for x seconds. The unit is 1s, the default value is 2. +#define AUTO_SLEEP_TIME 0xF9 +// read-write*. Interrupt configuration. 0x60 = report mode, 0x11 = gesture mode, 0x71 = both. +#define IRQ_CTL 0xFA +// read-write. Automatic reset (cancel) when there is a touch but no valid gesture within x seconds. The unit is 1s. When it is 0, +// this function is not enabled. Default is 5. +#define AUTO_RESET 0xFB +// read-write. Automatic reset (cancel) after long press for x seconds. The unit is 1s. When it is 0, this function is not enabled. +// Default is 10. +#define LONG_PRESS_TIME 0xFC +// read-write. Pin IO configuration. +#define IO_CTL 0xFD +// read-write**. The default is 0, enabling automatic entry into low power mode. When it is a non-zero value, automatic entry into +// low power mode is disabled. +#define DIS_AUTO_SLEEP 0xFE + +#define PWR_MODE_CST816S 0xE5 // read-write. Power state for the CST816S. +#define PWR_MODE_CST716 0xA5 // read-write. Power state for the CST716. + +// Data fields + +// GESTURE_ID + +#define GESTURE_ID_NONE 0x00 +#define GESTURE_ID_SLIDE_DOWN 0x01 +#define GESTURE_ID_SLIDE_UP 0x02 +#define GESTURE_ID_SLIDE_LEFT 0x03 +#define GESTURE_ID_SLIDE_RIGHT 0x04 +#define GESTURE_ID_SINGLE_TAP 0x05 +#define GESTURE_ID_DOUBLE_TAP 0x0B +#define GESTURE_ID_LONG_PRESS 0x0C + +// TD_STATUS + +#define TD_STATUS_MASK 0x0F + +// *_POS_H + +#define POS_H_POS_MASK 0x0F + +#define POS_H_EVENT_MASK 0xC0 +#define POS_H_EVENT0 (1 << 6) +#define POS_H_EVENT1 (1 << 7) + +#define POS_H_EVENT_DOWN (0 << 6) +#define POS_H_EVENT_UP (1 << 6) +#define POS_H_EVENT_CONTACT (2 << 6) +#define POS_H_EVENT_NONE (3 << 6) + +// CHIP_ID + +#define CHIP_ID_CST816S 0xB4 +#define CHIP_ID_CST716 0x20 + +// Control register configurations + +// MOTION_MASK + +#define MOTION_MASK_EN_DCLICK (1 << 0) // Enable double click action +#define MOTION_MASK_EN_CON_UD (1 << 1) // Enable continuous up-down sliding action +#define MOTION_MASK_EN_CON_LR (1 << 2) // Enable continuous left-right sliding action + +// IRQ_CTL + +#define IRQ_CTL_ONCE_WLP (1 << 0) // The long press gesture only emits a low pulse signal. +#define IRQ_CTL_EN_MOTION (1 << 4) // When a gesture is detected, a low pulse is emitted. +#define IRQ_CTL_EN_CHANGE (1 << 5) // A low pulse is emitted when a touch state change is detected. +#define IRQ_CTL_EN_TOUCH (1 << 6) // Periodically emit low pulses when a touch is detected. +#define IRQ_CTL_EN_TEST (1 << 7) // Interrupt pin test, automatically and periodically send low pulses after enabling. + +// IO_CTL + +#define IO_CTL_EN_1V8 (1 << 0) // I2C and IRQ pin voltage level selection, the default is VDD level. 0: VDD, 1: 1.8V. +#define IO_CTL_IIC_OD (1 << 1) // I2C pin drive mode, the default is resistor pull-up. 0: Resistor pull-up 1: OD. +#define IO_CTL_SOFT_RST (1 << 2) // Enable soft reset by pulling down the IRQ pin. 0: Soft reset is disabled. 1: Enable soft reset. + +// PWR_MODE_* + +#define PWR_MODE_ACTIVE 0x00 // Normal state of execution +#define PWR_MODE_DEEP_SLEEP 0x03 // Deep-sleep mode. Touch to wakeup and I2C are disabled. diff --git a/src/drivers/SC7A20.cpp b/src/drivers/SC7A20.cpp new file mode 100644 index 0000000000..4e75d24465 --- /dev/null +++ b/src/drivers/SC7A20.cpp @@ -0,0 +1,89 @@ +#include "drivers/SC7A20.h" +#include "drivers/SC7A20_registers.h" +#include +#include + +using namespace Pinetime::Drivers; + +SC7A20::SC7A20(TwiMaster& twiMaster, uint8_t twiAddress) : AccelerationSensor(twiMaster, twiAddress) { +} + +void SC7A20::Init() { + // Reset internal memory + uint8_t data = CTRL_REG5_BOOT; + Write(CTRL_REG5, &data, 1); + vTaskDelay(5); + data = 0; + Write(CTRL_REG5, &data, 1); + + // Read Chip ID + Read(WHO_AM_I, &data, 1); + if (data == 17) { + deviceType = AccelerationDeviceTypes::SC7A20; + } else { + deviceType = AccelerationDeviceTypes::Unknown; + return; + } + + // Configure resolution to be +-2g + data = CTRL_REG4_FS_2G; + Write(CTRL_REG4, &data, 1); + + // Enable block update, configure 12 bit resolution mode + data = CTRL_REG4_BDU | CTRL_REG4_HR; + Write(CTRL_REG4, &data, 1); + + // Use FIFO for batch data + data = CTRL_REG5_FIFO_EN; + Write(CTRL_REG5, &data, 1); + data = FIFO_CTRL_REG_FIFO; + Write(FIFO_CTRL_REG, &data, 1); + + // Set 200 Hz sample rate, enable all axes + data = CTRL_REG1_ODR_200HZ | CTRL_REG1_X_EN | CTRL_REG1_Y_EN | CTRL_REG1_Z_EN; + Write(CTRL_REG1, &data, 1); + + isInitialized = true; +} + +AccelerationValues SC7A20::Process() { + if (!isInitialized) + return {}; + + // Read FIFO size, should be about 20 (200 Hz ODR / 10 Hz main loop) + uint8_t length = 0; + Read(FIFO_SRC_REG, &length, 1); + length &= FIFO_SRC_REG_FSS_MASK; + + // Read FIFO samples one by one (full read does not work) + for (uint8_t i = 0; i < length; i++) { + // Set the most significant bit of the sub-address field for block read + Read(0x80 | OUT_X_L, (uint8_t*) &fifo[i], sizeof(int16_t) * 3); + // Shift because value is left-justified + for (uint8_t j = 0; j < 3; j++) + fifo[i][j] >>= (16 - 12); + // X and Y axis are swapped because of the way the sensor is mounted in the P8 + int16_t swap = fifo[i][0]; + fifo[i][0] = fifo[i][1]; + fifo[i][1] = swap; + } + + // Restart FIFO + uint8_t data = FIFO_CTRL_REG_BYPASS; + Write(FIFO_CTRL_REG, &data, 1); + data = FIFO_CTRL_REG_FIFO; + Write(FIFO_CTRL_REG, &data, 1); + + // Compute averages of FIFO + int16_t avgs[3] = {0}; + // 2g range in n bits + for (uint8_t i = 0; i < length; i++) + for (uint8_t j = 0; j < 3; j++) { + avgs[j] += ((fifo[i][j] * 2000) / (1 << (12 - 1))); + } + for (uint8_t j = 0; j < 3; j++) + avgs[j] /= length; + + // Step counting is not implemented + return {0, avgs[0], avgs[1], avgs[2], (int16_t*) fifo, length}; +} diff --git a/src/drivers/SC7A20.h b/src/drivers/SC7A20.h new file mode 100644 index 0000000000..9716a57098 --- /dev/null +++ b/src/drivers/SC7A20.h @@ -0,0 +1,19 @@ +#pragma once + +#include "drivers/AccelerationSensor.h" + +namespace Pinetime { + namespace Drivers { + class SC7A20 : public AccelerationSensor { + public: + SC7A20(TwiMaster& twiMaster, uint8_t twiAddress); + SC7A20(const SC7A20&) = delete; + SC7A20& operator=(const SC7A20&) = delete; + SC7A20(SC7A20&&) = delete; + SC7A20& operator=(SC7A20&&) = delete; + + void Init(); + AccelerationValues Process(); + }; + } +} diff --git a/src/drivers/SC7A20_registers.h b/src/drivers/SC7A20_registers.h new file mode 100644 index 0000000000..9b3464a1ef --- /dev/null +++ b/src/drivers/SC7A20_registers.h @@ -0,0 +1,423 @@ +#pragma once + +// SC7A20 ±2G/±4G/±8G/±16G Three-axis micromachined digital accelerometer +// Hangzhou Silan Microelectronics Co., Ltd. www.silan.com.cn + +// Manually assembled from an auto-translated incomplete datasheet +// This chips behaves nearly the same as a ST LIS2DH, the rest was added from the ST datasheet + +// Registers + +#define OUT_TEMP_L 0x0C // readonly +#define OUT_TEMP_H 0x0D // readonly +#define WHO_AM_I 0x0F // readonly +#define USER_CAL 0x13 // read-write +#define NVM_WR 0x1E // read-write +#define TEMP_CFG 0x1F // read-write +#define CTRL_REG1 0x20 // read-write. Control register 1. +#define CTRL_REG2 0x21 // read-write. Control register 2. +#define CTRL_REG3 0x22 // read-write. Control register 3. +#define CTRL_REG4 0x23 // read-write. Control register 4. +#define CTRL_REG5 0x24 // read-write. Control register 5. +#define CTRL_REG6 0x25 // read-write. Control register 6. +#define REFERENCE 0x26 // read-write +#define STATUS_REG 0x27 // read-write. Status register. +#define OUT_X_L 0x28 // readonly. X Axis accelerometer value. This value starts with 2 output in two's complement form. +#define OUT_X_H 0x29 // readonly +#define OUT_Y_L 0x2A // readonly. Y Axis accelerometer value. This value starts with 2 output in two's complement form. +#define OUT_Y_H 0x2B // readonly +#define OUT_Z_L 0x2C // readonly. Z Axis accelerometer value. This value starts with 2 output in two's complement form. +#define OUT_Z_H 0x2D // readonly +#define FIFO_CTRL_REG 0x2E // read-write +#define FIFO_SRC_REG 0x2F // readonly +#define INT1_CFG 0x30 // read-write. Interrupt 1 configuration. +#define INT1_SOURCE 0x31 // readonly. Interrupt 1 source / status. +#define INT1_THS 0x32 // read-write. Interrupt 1 treshold. +#define INT1_DURATION 0x33 // read-write. Interrupt 1 duration. +#define INT2_CFG 0x34 // read-write. Interrupt 2 configuration. +#define INT2_SOURCE 0x35 // readonly. Interrupt 2 source / status. +#define INT2_THS 0x36 // read-write. Interrupt 2 treshold. +#define INT2_DURATION 0x37 // read-write. Interrupt 1 duration. +#define CLICK_CFG 0x38 // read-write +#define CLICK_SRC 0x39 // readonly +#define CLICK_THS 0x3A // read-write +#define TIME_LIMIT 0x3B // read-write +#define TIME_LATENCY 0x3C // read-write +#define TIME_WINDOW 0x3D // read-write +#define ACT_THS 0x3E // read-write +#define ACT_DURATION 0x3F // read-write + +// Control register configurations + +// CTRL_REG1 + +#define CTRL_REG1_X_EN (1 << 0) // XAxis enable, default is 1. (0: Xaxis disabled, 1: Xaxis enabled) +#define CTRL_REG1_Y_EN (1 << 1) // YAxis enable, default is 1. (0: Yaxis disabled, 1: Yaxis enabled) +#define CTRL_REG1_Z_EN (1 << 2) // ZAxis enable, default is 1. (0: Zaxis disabled, 1: Zaxis enabled) +#define CTRL_REG1_LP_EN (1 << 3) // Low power enable, the default value is 0. (0: normal working mode, 1: low power mode) +#define CTRL_REG1_ODR0 (1 << 4) // Data rate selection, default: 0000 +#define CTRL_REG1_ODR1 (1 << 5) +#define CTRL_REG1_ODR2 (1 << 6) +#define CTRL_REG1_ODR3 (1 << 7) + +#define CTRL_REG1_ODR_POWERDOWN (0 << 4) // Power down mode +#define CTRL_REG1_ODR_1HZ (1 << 4) // Normal / Low power mode (1 Hz) +#define CTRL_REG1_ODR_10HZ (2 << 4) // Normal / Low power mode (10 Hz) +#define CTRL_REG1_ODR_25HZ (3 << 4) // Normal / Low power mode (25 Hz) +#define CTRL_REG1_ODR_50HZ (4 << 4) // Normal / Low power mode (50 Hz) +#define CTRL_REG1_ODR_100HZ (5 << 4) // Normal / Low power mode (100 Hz) +#define CTRL_REG1_ODR_200HZ (6 << 4) // Normal / Low power mode (200 Hz) +#define CTRL_REG1_ODR_400HZ (7 << 4) // Normal / Low power mode (400 Hz) +#define CTRL_REG1_ODR_1_6KHZ (8 << 4) // Low power mode (1.6 KHz) +#define CTRL_REG1_ODR_1_25KHZ (9 << 4) // normal working mode (1.25 kHz) / Low power mode (5KHz) + +// CTRL_REG2 + +#define CTRL_REG2_HPIS1 (1 << 0) // Interrupt 1 AOIFunction high pass filter enable. (0: Filter disabled; 1: filter enable) +#define CTRL_REG2_HPIS2 (1 << 1) // Interrupt 2 AOIFunction high pass filter enable. (0: Filter disabled; 1: filter enable) +#define CTRL_REG2_HPCLICK (1 << 2) // CLICK Function high pass filter enable. (0: Filter disabled; 1: filter enable) +// Data filtering options. Defaults: 0. (0: skip internal filtering; 1: The data after internal filtering is output to the data register or +// FIFO) +#define CTRL_REG2_FDS (1 << 3) +#define CTRL_REG2_HPCF1 (1 << 4) // High pass cutoff frequency selection +#define CTRL_REG2_HPCF2 (1 << 5) +#define CTRL_REG2_HPM0 (1 << 6) // High pass mode selection, Default 00 +#define CTRL_REG2_HPM1 (1 << 7) + +#define CTRL_REG2_HPM_NORMAL_HPFILTER_AUTORESET (0 << 6) // Normal mode (read high-pass filter resets automatically) +#define CTRL_REG2_HPM_FILTER_REF (1 << 6) // Filter reference signal +#define CTRL_REG2_HPM_NORMAL (2 << 6) // Normal mode +#define CTRL_REG2_HPM_INTERRUPT_AUTORESET (3 << 6) // Interrupt event auto reset + +// CTRL_REG3 + +#define CTRL_REG3_I1_OVERRUN (1 << 1) // FIFO overflow interrupt at INT1 superior. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG3_I1_WTM (1 << 2) // FIFO watermark breaks at INT1 superior. Defaults:0 (0: prohibit; 1:Enable) +#define CTRL_REG3_I1_DRDY2 (1 << 3) // DRDY2 interrupted at INT1 superior. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG3_I1_DRDY1 (1 << 4) // DRDY1 interrupted at INT1 superior. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG3_I1_AOI2 (1 << 5) // Enable interrupt function 2 on interrupt pin 1. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG3_I1_AOI1 (1 << 6) // Enable interrupt function 1 on interrupt pin 1. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG3_I1_CLICK (1 << 7) // CLICK interrupted at INT1 superior. Defaults:0 (0: prohibit; 1: Enable) + +// CTRL_REG4 + +#define CTRL_REG4_SIM (1 << 0) // SPI Serial interface mode configuration. Defaults: 0 (0: 4line interface; 1: 3line interface) +#define CTRL_REG4_ST0 (1 << 1) // Self-test enabled. Defaults: 00. +#define CTRL_REG4_ST1 (1 << 2) +#define CTRL_REG4_HR (1 << 3) // High precision output mode selection. Defaults: (0: high precision prohibited; 1: high precision enable) +#define CTRL_REG4_FS0 (1 << 4) // Full range selection. Defaults: 00. +#define CTRL_REG4_FS1 (1 << 5) +// Big endian/little endian data selection. Defaults:0 (0: The low byte data is at the low address; 1: high byte data at low address) +#define CTRL_REG4_BLE (1 << 6) +// Block data update. Defaults: 0. (0: continuous update; 1: The output data register is not updated until MSB and LSB is read) +#define CTRL_REG4_BDU (1 << 7) + +#define CTRL_REG4_ST_NORMAL (0 << 1) +#define CTRL_REG4_ST_TEST0 (1 << 1) +#define CTRL_REG4_ST_TEST1 (2 << 1) + +#define CTRL_REG4_FS_2G (0 << 4) +#define CTRL_REG4_FS_4G (1 << 4) +#define CTRL_REG4_FS_8G (2 << 4) +#define CTRL_REG4_FS_16G (3 << 4) + +// CTRL_REG5 + +// 4D enable: in INT2 enable on pin 4D detection, while taking the interrupt2 in the configuration register 6D set 1. +#define CTRL_REG5_D4D_INT2 (1 << 0) +// Latch Interrupt2 The interrupt response specified on the configuration register. Interrupt by read 2 The configuration +// register can clear the corresponding interrupt latch signal. Defaults: 0 (0: Do not latch the interrupt signal; 1: Latch interrupt +// signal) +#define CTRL_REG5_LIR_INT2 (1 << 1) +// 4D enable: in INT1 enable on pin 4D detection, while taking the interrupt1 in the configuration register 6D set 1. +#define CTRL_REG5_D4D_INT1 (1 << 2) +// Latch Interrupt1 The interrupt response specified on the configuration register. Interrupt by read 1 The configuration +// register can clear the corresponding interrupt latch signal. Defaults: 0 (0: Do not latch the interrupt signal; 1: Latch interrupt +// signal) +#define CTRL_REG5_LIR_INT1 (1 << 3) +// FIFO Enable. Defaults: 0. (0: FIFO prohibit; 1: FIFO Enable) +#define CTRL_REG5_FIFO_EN (1 << 6) +// Override trim value. Defaults: 0. (0: normal mode; 1: Overload trim value) +#define CTRL_REG5_BOOT (1 << 7) + +// CTRL_REG6 + +#define CTRL_REG6_H_LACTIVE (1 << 1) // 0: High level trigger interrupt; 1: Low level trigger interrupt +#define CTRL_REG6_BOOT_I2 (1 << 4) // BOOT status is INT2 superior. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG6_I2_AOI2 (1 << 5) // Enable interrupt function 2 on interrupt pin 2. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG6_I2_AOI1 (1 << 6) // Enable interrupt function 1 on interrupt pin 2. Defaults: 0 (0: prohibit; 1: Enable) +#define CTRL_REG6_I2_CLICK (1 << 7) // CLICK interrupted at INT2 superior. Defaults: 0. (0: prohibit; 1: Enable) + +// Status register + +// STATUS_REG + +// X New data for the axis arrives. Defaults: 0 (0: X The new data of the axis has not been converted; 1: X Axis new data conversion +// completed) +#define STATUS_REG_XDA (1 << 0) +// Y New data for the axis arrives. Defaults: 0 (0: Y The new data of the axis has not been converted; 1: Y Axis new data conversion +// completed) +#define STATUS_REG_YDA (1 << 1) +// Z New data for the axis arrives. Defaults: 0 (0: Z The new data of the axis has not been converted; 1: Z Axis new data conversion +// completed) +#define STATUS_REG_ZDA (1 << 2) +// X, Y and Z The new data of the three axes are all converted. Defaults: 0 (0: The data of at least one of the three axes has +// not been converted; 1: The new data of the three axes are all converted) +#define STATUS_REG_ZYXDA (1 << 3) +// X The new data of the axis has overwritten the old data. Defaults: 0. (0: X The new data of the axis has not overwritten the +// old data; 1: X The new data of the axis overwrites the old data) +#define STATUS_REG_XOR (1 << 4) +// Y The new data of the axis has overwritten the old data. Defaults: 0. (0: Y The new data of the axis has not overwritten the +// old data; 1: Y The new data of the axis overwrites the old data) +#define STATUS_REG_YOR (1 << 5) +// Z The new data of the axis has overwritten the old data. Defaults: 0. (0: Z The new data of the axis has not overwritten the +// old data; 1: Z The new data of the axis overwrites the old data) +#define STATUS_REG_ZOR (1 << 6) +// X, Y and Z At least one of the new data on the three axes has overwritten the old data. Defaults: 0 .(0: The new data of none +// of the three axes overwrites the old data; 1: The new data of at least one of the three axes has overwritten the old data) +#define STATUS_REG_ZYXOR (1 << 7) + +// FIFO configuration + +// FIFO_CTRL_REG + +#define FIFO_CTRL_REG_BYPASS (0 << 6) // Bypass mode +#define FIFO_CTRL_REG_FIFO (1 << 6) // FIFO mode +#define FIFO_CTRL_REG_STREAM (2 << 6) // Stream mode +#define FIFO_CTRL_REG_STREAM_TO_FIFO (3 << 6) // Stream to FIFO mode + +// FIFO_SRC_REG + +#define FIFO_SRC_REG_FSS_MASK 0x1F + +#define FIFO_SRC_REG_FSS0 (1 << 0) // Contains the current number of unread samples stored in the FIFO buffer +#define FIFO_SRC_REG_FSS1 (1 << 1) +#define FIFO_SRC_REG_FSS2 (1 << 2) +#define FIFO_SRC_REG_FSS3 (1 << 3) +#define FIFO_SRC_REG_FSS4 (1 << 4) +// Set high when all FIFO samples have been read and the FIFO is empty +#define FIFO_SRC_REG_EMPTY (1 << 5) +// Set high when the FIFO buffer is full, which means that the FIFO buffer contains 32 unread samples +#define FIFO_SRC_REG_OVRN_FIFO (1 << 6) +#define FIFO_SRC_REG_WTM (1 << 7) // Set high when FIFO content exceeds watermark level + +// Interrupt configuration + +// INT1_CFG + +// X Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_XLIE_XDOWNE (1 << 0) +// X Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_XHIE_XUPE (1 << 1) +// Y Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_YLIE_YDOWNE (1 << 2) +// Y Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_YHIE_YUPE (1 << 3) +// Z Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_ZLIE_XDOWNE (1 << 4) +// Z Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT1_CFG_ZHIE_ZUPE (1 << 5) +// 6 The direction detection function is enabled. Defaults: 0. Refer to "Interrupt Mode" +#define INT1_CFG_6D (1 << 6) +// and/or interrupt events. Defaults: 0. Refer to "Interrupt Mode" +#define INT1_CFG_AOI (1 << 7) + +#define INT1_CFG_6D_AOI_OR_INT_EVENT (0 << 6) // or interrupt event +#define INT1_CFG_6D_AOI_6D_MOTION (1 << 6) // 6 direction motion recognition +#define INT1_CFG_6D_AOI_AND_INT_EVENT (2 << 6) // and interrupt event +#define INT1_CFG_6D_AOI_6D_POSITION (3 << 6) // 6 direction position detection + +// INT1_SOURCE + +#define INT1_SOURCE_XL (1 << 0) // X axis low. Defaults: 0. (0: no interruption, 1: X Axis low event has been generated) +#define INT1_SOURCE_XH (1 << 1) // X axis high. Defaults: 0. (0: no interruption, 1: X Axis high event has been generated) +#define INT1_SOURCE_YL (1 << 2) // Y axis low. Defaults: 0. (0: no interruption, 1: Y Axis low event has been generated) +#define INT1_SOURCE_YH (1 << 3) // Y axis high. Defaults: 0. (0: no interruption, 1: Y Axis high event has been generated) +#define INT1_SOURCE_ZL (1 << 4) // Z axis low. Defaults: 0. (0: no interruption, 1: Z Axis low event has been generated) +#define INT1_SOURCE_ZH (1 << 5) // Z axis high. Defaults: 0. (0: no interruption, 1: Z Axis high event has been generated) +// Interrupt activation. Defaults: 0. (0: Interrupt is not generated; 1: one or more interrupts have been generated) +#define INT1_SOURCE_IA (1 << 6) + +// INT1_THS + +#define INT1_THS_TH0 (1 << 0) // Interrupt 1 threshold. Defaults: 000 0000. Units: 16mg @ FS=2g, 32mg @ FS=4g, 64mg @ FS=8g, 128mg @ FS=16g +#define INT1_THS_TH1 (1 << 1) +#define INT1_THS_TH2 (1 << 2) +#define INT1_THS_TH3 (1 << 3) +#define INT1_THS_TH4 (1 << 4) +#define INT1_THS_TH5 (1 << 5) +#define INT1_THS_TH6 (1 << 6) + +// INT2_DURATION + +// Duration count value. Defaults: 000 0000. set recognized interrupt The minimum duration of the event. The maximum time and +// time step of the duration register is ODR for the clock. +#define INT1_DURATION_D0 (1 << 0) +#define INT1_DURATION_D1 (1 << 1) +#define INT1_DURATION_D2 (1 << 2) +#define INT1_DURATION_D3 (1 << 3) +#define INT1_DURATION_D4 (1 << 4) +#define INT1_DURATION_D5 (1 << 5) +#define INT1_DURATION_D6 (1 << 6) + +// INT2_CFG +// X Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_XLIE_XDOWNE (1 << 0) +// X Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_XHIE_XUPE (1 << 1) +// Y Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_YLIE_YDOWNE (1 << 2) +// Y Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_YHIE_YUPE (1 << 3) +// Z Axis low event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_ZLIE_XDOWNE (1 << 4) +// Z Axis high event interrupt or Z Axis Orientation Detection Interrupt Enable. Defaults: 0 (0: disable interrupt; 1: enable interrupt) +#define INT2_CFG_ZHIE_ZUPE (1 << 5) +// 6 The direction detection function is enabled. Defaults: 0. Refer to "Interrupt Mode" +#define INT2_CFG_6D (1 << 6) +// and/or interrupt events. Defaults: 0. Refer to "Interrupt Mode" +#define INT2_CFG_AOI (1 << 7) + +#define INT2_CFG_6D_AOI_OR_INT_EVENT (0 << 6) // or interrupt event +#define INT2_CFG_6D_AOI_6D_MOTION (1 << 6) // 6 direction motion recognition +#define INT2_CFG_6D_AOI_AND_INT_EVENT (2 << 6) // and interrupt event +#define INT2_CFG_6D_AOI_6D_POSITION (3 << 6) // 6 direction position detection + +// INT2_SOURCE + +#define INT2_SOURCE_XL (1 << 0) // X axis low. Defaults: 0. (0: no interruption, 1: X Axis low event has been generated) +#define INT2_SOURCE_XH (1 << 1) // X axis high. Defaults: 0. (0: no interruption, 1: X Axis high event has been generated) +#define INT2_SOURCE_YL (1 << 2) // Y axis low. Defaults: 0. (0: no interruption, 1: Y Axis low event has been generated) +#define INT2_SOURCE_YH (1 << 3) // Y axis high. Defaults: 0. (0: no interruption, 1: Y Axis high event has been generated) +#define INT2_SOURCE_ZL (1 << 4) // Z axis low. Defaults: 0. (0: no interruption, 1: Z Axis low event has been generated) +#define INT2_SOURCE_ZH (1 << 5) // Z axis high. Defaults: 0. (0: no interruption, 1: Z Axis high event has been generated) +// Interrupt activation. Defaults: 0. (0: Interrupt is not generated; 1: one or more interrupts have been generated) +#define INT2_SOURCE_IA (1 << 6) + +// INT2_THS + +#define INT2_THS_TH0 (1 << 0) // Interrupt 1 threshold. Defaults: 000 0000. Units: 16mg @ FS=2g, 32mg @ FS=4g, 64mg @ FS=8g, 128mg @ FS=16g +#define INT2_THS_TH1 (1 << 1) +#define INT2_THS_TH2 (1 << 2) +#define INT2_THS_TH3 (1 << 3) +#define INT2_THS_TH4 (1 << 4) +#define INT2_THS_TH5 (1 << 5) +#define INT2_THS_TH6 (1 << 6) + +// INT2_DURATION + +// Duration count value. Defaults: 000 0000. set recognized interrupt The minimum duration of the event. The maximum time and +// time step of the duration register is ODR for the clock. +#define INT2_DURATION_D0 (1 << 0) +#define INT2_DURATION_D1 (1 << 1) +#define INT2_DURATION_D2 (1 << 2) +#define INT2_DURATION_D3 (1 << 3) +#define INT2_DURATION_D4 (1 << 4) +#define INT2_DURATION_D5 (1 << 5) +#define INT2_DURATION_D6 (1 << 6) + +// Click configuration + +// CLICK_CFG + +// Enable interrupt single tap on X axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_XS (1 << 0) +// Enable interrupt double tap on X axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_XD (1 << 1) +// Enable interrupt single tap on Y axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_YS (1 << 2) +// Enable interrupt double tap on Y axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_YD (1 << 3) +// Enable interrupt single tap on Z axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_ZS (1 << 4) +// Enable interrupt double tap on Z axis. Default value: 0. (0: disable interrupt request; 1: enable interrupt request on +// measured accel. value higher than preset threshold) +#define CLICK_CFG_ZD (1 << 5) + +// CLICK_SRC + +#define CLICK_SRC_X (1 << 0) // X Click-Click detection. Default value: 0. (0: no interrupt, 1: X High event has occurred) +#define CLICK_SRC_Y (1 << 1) // Y Click-Click detection. Default value: 0. (0: no interrupt, 1: Y High event has occurred) +#define CLICK_SRC_Z (1 << 2) // Z Click-Click detection. Default value: 0. (0: no interrupt, 1: Z High event has occurred) +#define CLICK_SRC_SIGN (1 << 3) // Click-Click Sign. 0: positive detection, 1: negative detection +// Single Click-Click enable. Default value: 0. (0:Single Click-Click detection disable, 1: single Click-Click detection enable) +#define CLICK_SRC_SCLICK (1 << 4) +// Single Click-Click enable. Default value: 0. (0:Single Click-Click detection disable, 1: single Click-Click detection enable) +#define CLICK_SRC_DCLICK (1 << 5) +// Interrupt active. Default value: 0. (0: no interrupt has been generated; 1: one or more interrupts have been generated) +#define CLICK_SRC_IA (1 << 6) + +// CLICK_THS + +#define CLICK_THS_TH0 (1 << 0) // Click-Click threshold. Default value: 000 0000 +#define CLICK_THS_TH1 (1 << 1) +#define CLICK_THS_TH2 (1 << 2) +#define CLICK_THS_TH3 (1 << 3) +#define CLICK_THS_TH4 (1 << 4) +#define CLICK_THS_TH5 (1 << 5) +#define CLICK_THS_TH6 (1 << 6) +// If the LIR_Click bit is not set, the interrupt is kept high for the duration of the latency window. If the LIR_Click bit is +// set, the interrupt is kept high until CLICK_SRC (39h) is read +#define CLICK_THS_LIR_CLICK (1 << 7) + +// TIME_LIMIT + +#define CLICK_TIME_LIMIT_TLI0 (1 << 0) // Click-Click Time Limit. Default value: 000 0000 +#define CLICK_TIME_LIMIT_TLI1 (1 << 1) +#define CLICK_TIME_LIMIT_TLI2 (1 << 2) +#define CLICK_TIME_LIMIT_TLI3 (1 << 3) +#define CLICK_TIME_LIMIT_TLI4 (1 << 4) +#define CLICK_TIME_LIMIT_TLI5 (1 << 5) +#define CLICK_TIME_LIMIT_TLI6 (1 << 6) + +// TIME_LATENCY + +#define CLICK_TIME_LATENCY_TLA0 (1 << 0) // Click-Click Time Latency. Default value: 000 0000 +#define CLICK_TIME_LATENCY_TLA1 (1 << 1) +#define CLICK_TIME_LATENCY_TLA2 (1 << 2) +#define CLICK_TIME_LATENCY_TLA3 (1 << 3) +#define CLICK_TIME_LATENCY_TLA4 (1 << 4) +#define CLICK_TIME_LATENCY_TLA5 (1 << 5) +#define CLICK_TIME_LATENCY_TLA6 (1 << 6) +#define CLICK_TIME_LATENCY_TLA7 (1 << 7) + +// TIME_WINDOW + +#define CLICK_TIME_WINDOW_TW0 (1 << 0) // Click-Click Time Window +#define CLICK_TIME_WINDOW_TW1 (1 << 1) +#define CLICK_TIME_WINDOW_TW2 (1 << 2) +#define CLICK_TIME_WINDOW_TW3 (1 << 3) +#define CLICK_TIME_WINDOW_TW4 (1 << 4) +#define CLICK_TIME_WINDOW_TW5 (1 << 5) +#define CLICK_TIME_WINDOW_TW6 (1 << 6) +#define CLICK_TIME_WINDOW_TW7 (1 << 7) + +// Active configuration + +// ACT_THS + +// Sleep to wake, return to Sleep activation threshold in Low power mode (1LSb = 16mg @FS=2g, 1LSb = 32 mg @FS=4g, 1LSb = 62 mg @FS=8g, 1LSb +// = 186 mg @FS=16g) +#define ACT_THS_ACTH0 (1 << 0) +#define ACT_THS_ACTH1 (1 << 1) +#define ACT_THS_ACTH2 (1 << 2) +#define ACT_THS_ACTH3 (1 << 3) +#define ACT_THS_ACTH4 (1 << 4) +#define ACT_THS_ACTH5 (1 << 5) +#define ACT_THS_ACTH6 (1 << 6) + +// ACT_DUR + +#define ACT_DUR_ACTD0 (1 << 0) // Sleep to Wake, Return to Sleep duration (1LSb = (8*1[LSb]+1)/ODR) +#define ACT_DUR_ACTD1 (1 << 1) +#define ACT_DUR_ACTD2 (1 << 2) +#define ACT_DUR_ACTD3 (1 << 3) +#define ACT_DUR_ACTD4 (1 << 4) +#define ACT_DUR_ACTD5 (1 << 5) +#define ACT_DUR_ACTD6 (1 << 6) +#define ACT_DUR_ACTD7 (1 << 7) diff --git a/src/main.cpp b/src/main.cpp index ad7a07dc98..cc26f4b4fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include "BootloaderVersion.h" #include "components/battery/BatteryController.h" @@ -58,7 +57,6 @@ Pinetime::Logging::DummyLogger logger; #endif static constexpr uint8_t touchPanelTwiAddress = 0x15; -static constexpr uint8_t motionSensorTwiAddress = 0x18; static constexpr uint8_t heartRateSensorTwiAddress = 0x44; Pinetime::Drivers::SpiMaster spi {Pinetime::Drivers::SpiMaster::SpiModule::SPI0, @@ -90,7 +88,17 @@ Pinetime::Drivers::Cst816S touchPanel {twiMaster, touchPanelTwiAddress}; #endif Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; +#if (defined DRIVER_ACC_SC7A20) + #include +static constexpr uint8_t motionSensorTwiAddress = 0x18; +Pinetime::Drivers::SC7A20 motionSensor {twiMaster, motionSensorTwiAddress}; +#else +// Assume PineTime (DRIVER_ACC_BMA421) + #include +static constexpr uint8_t motionSensorTwiAddress = 0x18; Pinetime::Drivers::Bma421 motionSensor {twiMaster, motionSensorTwiAddress}; +#endif + Pinetime::Drivers::Hrs3300 heartRateSensor {twiMaster, heartRateSensorTwiAddress}; TimerHandle_t debounceTimer; diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index ef631af74e..09e92abcc7 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -62,7 +62,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Pinetime::Controllers::MotorController& motorController, Pinetime::Drivers::Hrs3300& heartRateSensor, Pinetime::Controllers::MotionController& motionController, - Pinetime::Drivers::Bma421& motionSensor, + Pinetime::Drivers::AccelerationSensor& motionSensor, Controllers::Settings& settingsController, Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Applications::DisplayApp& displayApp, @@ -377,7 +377,13 @@ void SystemTask::Work() { // Double Tap needs the touch screen to be in normal mode if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) { - touchPanel.Sleep(); +// REPORT and GESTURE mode sensors must be normal mode for single tap as well +#if !defined(DRIVER_TOUCH_DYNAMIC) + if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap)) +#endif + { + touchPanel.Sleep(); + } } state = SystemTaskState::Sleeping; @@ -471,11 +477,6 @@ void SystemTask::UpdateMotion() { return; } - if (state == SystemTaskState::Sleeping && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) || - settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake))) { - return; - } - if (stepCounterMustBeReset) { motionSensor.ResetStepCounter(); stepCounterMustBeReset = false; @@ -483,8 +484,9 @@ void SystemTask::UpdateMotion() { auto motionValues = motionSensor.Process(); - motionController.IsSensorOk(motionSensor.IsOk()); - motionController.Update(motionValues.x, motionValues.y, motionValues.z, motionValues.steps); + motionController.IsSensorOk(motionSensor.IsInitialized()); + motionController + .Update(motionValues.steps, motionValues.x, motionValues.y, motionValues.z, motionValues.samples, motionValues.samples_length); if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep) { if ((settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) && diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index d1e4a004f9..1439e3a80a 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include @@ -70,7 +70,7 @@ namespace Pinetime { Pinetime::Controllers::MotorController& motorController, Pinetime::Drivers::Hrs3300& heartRateSensor, Pinetime::Controllers::MotionController& motionController, - Pinetime::Drivers::Bma421& motionSensor, + Pinetime::Drivers::AccelerationSensor& motionSensor, Controllers::Settings& settingsController, Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Applications::DisplayApp& displayApp, @@ -115,7 +115,7 @@ namespace Pinetime { Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::MotorController& motorController; Pinetime::Drivers::Hrs3300& heartRateSensor; - Pinetime::Drivers::Bma421& motionSensor; + Pinetime::Drivers::AccelerationSensor& motionSensor; Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::HeartRateController& heartRateController; Pinetime::Controllers::MotionController& motionController; diff --git a/src/touchhandler/TouchHandler.cpp b/src/touchhandler/TouchHandler.cpp index 0e4fb5414a..16ce246c41 100644 --- a/src/touchhandler/TouchHandler.cpp +++ b/src/touchhandler/TouchHandler.cpp @@ -50,11 +50,42 @@ Pinetime::Applications::TouchEvents TouchHandler::GestureGet() { bool TouchHandler::GetNewTouchInfo() { info = touchPanel.GetTouchInfo(); - - if (!info.isValid) { + if (!info.isValid) return false; - } + // REPORT configurations (P8b variants) of the fused (usually) Cst716 + // generate multiple "none" gesture events with info.touching == true during the physical gesture. + // The last event is a e.g. "slide" event with info.touching == true. + // gestureReleased state does not have to be computed manually, instead it occurs when event != "none". + + // GESTURE configurations (P8a variants) of the fused (usually) Cst716 generate no events during the physical gesture. + // The only event is a e.g. "slide" event with info.touching == true. + // gestureReleased state does not have to be computed manually, instead it occurs everytime. + + // DYNAMIC configurations (PineTime) are configured in reporting mode during initialisation. + // Usually based on the Cst816s, they generate multiple e.g. "slide" gesture events with info.touching == true during the physical + // gesture. The last of these e.g. "slide" events has info.touching == false. gestureReleased state is computed manually by checking for + // the transition to info.touching == false. + + // Unfortunately, there is no way to reliably obtain which configuration is used at runtime. + // In all cases, the event is bubbled up once the gesture is released. + +#if defined(DRIVER_TOUCH_REPORT) + if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { + gesture = ConvertGesture(info.gesture); + info.touching = false; + } +#elif defined(DRIVER_TOUCH_GESTURE) + if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { + gesture = ConvertGesture(info.gesture); + // A new variant configuration behaves in a way such that it generates a gesture event at the start of a physical gesture, + // but does not set the info.touching flag at all. Since gestures are handled separately, special behaviour is only needed + // for the tap event. For the original P8b, which always sets info.touching = true, this operation is idempotent. + if (gesture == TouchEvents::Tap) { + info.touching = true; + } + } +#elif defined(DRIVER_TOUCH_DYNAMIC) if (info.gesture != Pinetime::Drivers::Cst816S::Gestures::None) { if (gestureReleased) { if (info.gesture == Pinetime::Drivers::Cst816S::Gestures::SlideDown || @@ -75,15 +106,22 @@ bool TouchHandler::GetNewTouchInfo() { if (!info.touching) { gestureReleased = true; } +#endif return true; } void TouchHandler::UpdateLvglTouchPoint() { if (info.touching) { +#if defined(DRIVER_TOUCH_GESTURE) + // GESTURE config only generates a single event / state change + // so the LVGL wrapper is used to generate a successive release state update + lvgl.SetNewTap(info.x, info.y); +#else if (!isCancelled) { lvgl.SetNewTouchPoint(info.x, info.y, true); } +#endif } else { if (isCancelled) { lvgl.SetNewTouchPoint(-1, -1, false);