Merge branch 'motion-sensor' into develop

This commit is contained in:
Jean-François Milants 2021-04-09 21:17:03 +02:00
commit eb769fb60e
33 changed files with 12471 additions and 255 deletions

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(pinetime VERSION 0.15.0 LANGUAGES C CXX ASM) project(pinetime VERSION 0.16.0 LANGUAGES C CXX ASM)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)

View file

@ -34,6 +34,9 @@ As of now, here is the list of achievements of this project:
- Rich user interface via display, touchscreen and pushbutton - Rich user interface via display, touchscreen and pushbutton
- Time synchronization via BLE - Time synchronization via BLE
- Notification via BLE - Notification via BLE
- Heart rate measurements
- Step counting
- Wake-up on wrist rotation
- Multiple 'apps' : - Multiple 'apps' :
* Clock (displays the date, time, battery level, ble connection status, heart rate) * Clock (displays the date, time, battery level, ble connection status, heart rate)
* System info (displays various info : BLE MAC, build date/time, uptime, version,...) * System info (displays various info : BLE MAC, build date/time, uptime, version,...)

View file

@ -83,7 +83,7 @@ set(SDK_SOURCE_FILES
"${NRF5_SDK_PATH}/external/fprintf/nrf_fprintf_format.c" "${NRF5_SDK_PATH}/external/fprintf/nrf_fprintf_format.c"
# TWI # TWI
"${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twi.c" "${NRF5_SDK_PATH}/modules/nrfx/drivers/src/nrfx_twim.c"
# GPIOTE # GPIOTE
"${NRF5_SDK_PATH}/components/libraries/gpiote/app_gpiote.c" "${NRF5_SDK_PATH}/components/libraries/gpiote/app_gpiote.c"
@ -396,11 +396,13 @@ list(APPEND SOURCE_FILES
displayapp/screens/FirmwareUpdate.cpp displayapp/screens/FirmwareUpdate.cpp
displayapp/screens/Music.cpp displayapp/screens/Music.cpp
displayapp/screens/Navigation.cpp displayapp/screens/Navigation.cpp
displayapp/screens/Motion.cpp
displayapp/screens/FirmwareValidation.cpp displayapp/screens/FirmwareValidation.cpp
displayapp/screens/ApplicationList.cpp displayapp/screens/ApplicationList.cpp
displayapp/screens/Notifications.cpp displayapp/screens/Notifications.cpp
displayapp/screens/Twos.cpp displayapp/screens/Twos.cpp
displayapp/screens/HeartRate.cpp displayapp/screens/HeartRate.cpp
displayapp/screens/Motion.cpp
displayapp/screens/FlashLight.cpp displayapp/screens/FlashLight.cpp
displayapp/screens/List.cpp displayapp/screens/List.cpp
displayapp/screens/BatteryInfo.cpp displayapp/screens/BatteryInfo.cpp
@ -429,11 +431,15 @@ list(APPEND SOURCE_FILES
drivers/DebugPins.cpp drivers/DebugPins.cpp
drivers/InternalFlash.cpp drivers/InternalFlash.cpp
drivers/Hrs3300.cpp drivers/Hrs3300.cpp
drivers/Bma421.cpp
drivers/Bma421_C/bma4.c
drivers/Bma421_C/bma423.c
components/battery/BatteryController.cpp components/battery/BatteryController.cpp
components/ble/BleController.cpp components/ble/BleController.cpp
components/ble/NotificationManager.cpp components/ble/NotificationManager.cpp
components/datetime/DateTimeController.cpp components/datetime/DateTimeController.cpp
components/brightness/BrightnessController.cpp components/brightness/BrightnessController.cpp
components/motion/MotionController.cpp
components/ble/NimbleController.cpp components/ble/NimbleController.cpp
components/ble/DeviceInformationService.cpp components/ble/DeviceInformationService.cpp
components/ble/CurrentTimeClient.cpp components/ble/CurrentTimeClient.cpp
@ -487,11 +493,15 @@ list(APPEND RECOVERY_SOURCE_FILES
drivers/DebugPins.cpp drivers/DebugPins.cpp
drivers/InternalFlash.cpp drivers/InternalFlash.cpp
drivers/Hrs3300.cpp drivers/Hrs3300.cpp
drivers/Bma421.cpp
drivers/Bma421_C/bma4.c
drivers/Bma421_C/bma423.c
components/battery/BatteryController.cpp components/battery/BatteryController.cpp
components/ble/BleController.cpp components/ble/BleController.cpp
components/ble/NotificationManager.cpp components/ble/NotificationManager.cpp
components/datetime/DateTimeController.cpp components/datetime/DateTimeController.cpp
components/brightness/BrightnessController.cpp components/brightness/BrightnessController.cpp
components/motion/MotionController.cpp
components/ble/NimbleController.cpp components/ble/NimbleController.cpp
components/ble/DeviceInformationService.cpp components/ble/DeviceInformationService.cpp
components/ble/CurrentTimeClient.cpp components/ble/CurrentTimeClient.cpp
@ -576,6 +586,7 @@ set(INCLUDE_FILES
displayapp/Apps.h displayapp/Apps.h
displayapp/screens/Notifications.h displayapp/screens/Notifications.h
displayapp/screens/HeartRate.h displayapp/screens/HeartRate.h
displayapp/screens/Motion.h
drivers/St7789.h drivers/St7789.h
drivers/SpiNorFlash.h drivers/SpiNorFlash.h
drivers/SpiMaster.h drivers/SpiMaster.h
@ -584,11 +595,15 @@ set(INCLUDE_FILES
drivers/DebugPins.h drivers/DebugPins.h
drivers/InternalFlash.h drivers/InternalFlash.h
drivers/Hrs3300.h drivers/Hrs3300.h
drivers/Bma421.h
drivers/Bma421_C/bma4.c
drivers/Bma421_C/bma423.c
components/battery/BatteryController.h components/battery/BatteryController.h
components/ble/BleController.h components/ble/BleController.h
components/ble/NotificationManager.h components/ble/NotificationManager.h
components/datetime/DateTimeController.h components/datetime/DateTimeController.h
components/brightness/BrightnessController.h components/brightness/BrightnessController.h
components/motion/MotionController.h
components/ble/NimbleController.h components/ble/NimbleController.h
components/ble/DeviceInformationService.h components/ble/DeviceInformationService.h
components/ble/CurrentTimeClient.h components/ble/CurrentTimeClient.h

View file

@ -1,9 +1,14 @@
#include "DateTimeController.h" #include "DateTimeController.h"
#include <date/date.h> #include <date/date.h>
#include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log.h>
#include <systemtask/SystemTask.h>
using namespace Pinetime::Controllers; using namespace Pinetime::Controllers;
DateTime::DateTime(System::SystemTask& systemTask) : systemTask{systemTask} {
}
void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute,
uint8_t second, uint32_t systickCounter) { uint8_t second, uint32_t systickCounter) {
@ -62,6 +67,14 @@ void DateTime::UpdateTime(uint32_t systickCounter) {
hour = time.hours().count(); hour = time.hours().count();
minute = time.minutes().count(); minute = time.minutes().count();
second = time.seconds().count(); second = time.seconds().count();
// Notify new day to SystemTask
if(hour == 0 and not isMidnightAlreadyNotified) {
isMidnightAlreadyNotified = true;
systemTask.PushMessage(System::SystemTask::Messages::OnNewDay);
} else if (hour != 0) {
isMidnightAlreadyNotified = false;
}
} }
const char *DateTime::MonthShortToString() { const char *DateTime::MonthShortToString() {

View file

@ -4,12 +4,17 @@
#include <chrono> #include <chrono>
namespace Pinetime { namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers { namespace Controllers {
class DateTime { class DateTime {
public: public:
enum class Days : uint8_t {Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}; enum class Days : uint8_t {Unknown, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
enum class Months : uint8_t {Unknown, January, February, March, April, May, June, July, August, September, October, November, December}; enum class Months : uint8_t {Unknown, January, February, March, April, May, June, July, August, September, October, November, December};
DateTime(System::SystemTask& systemTask);
void SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter); void SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t dayOfWeek, uint8_t hour, uint8_t minute, uint8_t second, uint32_t systickCounter);
void UpdateTime(uint32_t systickCounter); void UpdateTime(uint32_t systickCounter);
uint16_t Year() const { return year; } uint16_t Year() const { return year; }
@ -31,6 +36,7 @@ namespace Pinetime {
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; } std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> CurrentDateTime() const { return currentDateTime; }
std::chrono::seconds Uptime() const { return uptime; } std::chrono::seconds Uptime() const { return uptime; }
private: private:
System::SystemTask& systemTask;
uint16_t year = 0; uint16_t year = 0;
Months month = Months::Unknown; Months month = Months::Unknown;
uint8_t day = 0; uint8_t day = 0;
@ -43,6 +49,8 @@ namespace Pinetime {
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime; std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> currentDateTime;
std::chrono::seconds uptime {0}; std::chrono::seconds uptime {0};
bool isMidnightAlreadyNotified = false;
static char const *DaysString[]; static char const *DaysString[];
static char const *DaysStringShort[]; static char const *DaysStringShort[];
static char const *DaysStringLow[]; static char const *DaysStringLow[];

View file

@ -0,0 +1,36 @@
#include "MotionController.h"
using namespace Pinetime::Controllers;
void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) {
this->x = x;
this->y = y;
this->z = z;
this->nbSteps = nbSteps;
}
bool MotionController::ShouldWakeUp(bool isSleeping) {
if ((x + 335) <= 670 && z < 0) {
if (not isSleeping) {
if (y <= 0) {
return false;
} else {
lastYForWakeUp = 0;
return false;
}
}
if (y >= 0) {
lastYForWakeUp = 0;
return false;
}
if (y + 230 < lastYForWakeUp) {
lastYForWakeUp = y;
return true;
}
}
return false;
}
void MotionController::IsSensorOk(bool isOk) {
isSensorOk = isOk;
}

View file

@ -0,0 +1,29 @@
#pragma once
#include <cstdint>
namespace Pinetime {
namespace Controllers {
class MotionController {
public:
void Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps);
uint16_t X() const { return x; }
uint16_t Y() const { return y; }
uint16_t Z() const { return z; }
uint32_t NbSteps() const { return nbSteps; }
bool ShouldWakeUp(bool isSleeping);
void IsSensorOk(bool isOk);
bool IsSensorOk() const { return isSensorOk; }
private:
uint32_t nbSteps;
int16_t x;
int16_t y;
int16_t z;
int16_t lastYForWakeUp = 0;
bool isSensorOk = false;
};
}
}

View file

@ -4,7 +4,7 @@ namespace Pinetime {
namespace Applications { namespace Applications {
enum class Apps { enum class Apps {
None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo, None, Launcher, Clock, SysInfo, FirmwareUpdate, FirmwareValidation, NotificationsPreview, Notifications, FlashLight, BatteryInfo,
Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Music, Paint, Paddle, Twos, HeartRate, Navigation, StopWatch, Motion,
QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp QuickSettings, Settings, SettingWatchFace, SettingTimeFormat, SettingDisplay, SettingWakeUp
}; };
} }

View file

@ -1,10 +1,12 @@
#include "DisplayApp.h" #include "DisplayApp.h"
#include <libraries/log/nrf_log.h> #include <libraries/log/nrf_log.h>
#include <displayapp/screens/HeartRate.h> #include <displayapp/screens/HeartRate.h>
#include <displayapp/screens/Motion.h>
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/datetime/DateTimeController.h" #include "components/datetime/DateTimeController.h"
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include "components/motion/MotionController.h"
#include "displayapp/screens/ApplicationList.h" #include "displayapp/screens/ApplicationList.h"
#include "displayapp/screens/Brightness.h" #include "displayapp/screens/Brightness.h"
#include "displayapp/screens/Clock.h" #include "displayapp/screens/Clock.h"
@ -43,7 +45,8 @@ DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Driver
System::SystemTask &systemTask, System::SystemTask &systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings &settingsController) : Controllers::Settings &settingsController,
Pinetime::Controllers::MotionController& motionController) :
lcd{lcd}, lcd{lcd},
lvgl{lvgl}, lvgl{lvgl},
touchPanel{touchPanel}, touchPanel{touchPanel},
@ -54,7 +57,8 @@ DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Driver
systemTask{systemTask}, systemTask{systemTask},
notificationManager{notificationManager}, notificationManager{notificationManager},
heartRateController{heartRateController}, heartRateController{heartRateController},
settingsController{settingsController} { settingsController{settingsController},
motionController{motionController} {
msgQueue = xQueueCreate(queueSize, itemSize); msgQueue = xQueueCreate(queueSize, itemSize);
// Start clock when smartwatch boots // Start clock when smartwatch boots
LoadApp( Apps::Clock, DisplayApp::FullRefreshDirections::None ); LoadApp( Apps::Clock, DisplayApp::FullRefreshDirections::None );
@ -220,7 +224,7 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
break; break;
case Apps::None: case Apps::None:
case Apps::Clock: case Apps::Clock:
currentScreen = std::make_unique<Screens::Clock>(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController); currentScreen = std::make_unique<Screens::Clock>(this, dateTimeController, batteryController, bleController, notificationManager, settingsController, heartRateController, motionController);
break; break;
case Apps::FirmwareValidation: case Apps::FirmwareValidation:
@ -300,6 +304,10 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction)
case Apps::HeartRate: case Apps::HeartRate:
currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, systemTask); currentScreen = std::make_unique<Screens::HeartRate>(this, heartRateController, systemTask);
break; break;
case Apps::Motion:
currentScreen = std::make_unique<Screens::Motion>(this, motionController);
break;
} }
currentApp = app; currentApp = app;
} }

View file

@ -27,6 +27,7 @@ namespace Pinetime {
class DateTime; class DateTime;
class NotificationManager; class NotificationManager;
class HeartRateController; class HeartRateController;
class MotionController;
} }
namespace System { namespace System {
@ -45,7 +46,8 @@ namespace Pinetime {
System::SystemTask &systemTask, System::SystemTask &systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Controllers::Settings &settingsController Controllers::Settings &settingsController,
Pinetime::Controllers::MotionController& motionController
); );
void Start(); void Start();
void PushMessage(Display::Messages msg); void PushMessage(Display::Messages msg);
@ -68,6 +70,7 @@ namespace Pinetime {
Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::HeartRateController& heartRateController; Pinetime::Controllers::HeartRateController& heartRateController;
Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::MotionController& motionController;
Pinetime::Controllers::FirmwareValidator validator; Pinetime::Controllers::FirmwareValidator validator;
Controllers::BrightnessController brightnessController; Controllers::BrightnessController brightnessController;

View file

@ -13,7 +13,8 @@ DisplayApp::DisplayApp(Drivers::St7789 &lcd, Components::LittleVgl &lvgl, Driver
System::SystemTask &systemTask, System::SystemTask &systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController): Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::MotionController& motionController):
lcd{lcd}, bleController{bleController} { lcd{lcd}, bleController{bleController} {
msgQueue = xQueueCreate(queueSize, itemSize); msgQueue = xQueueCreate(queueSize, itemSize);

View file

@ -16,6 +16,7 @@
#include <date/date.h> #include <date/date.h>
#include <drivers/Watchdog.h> #include <drivers/Watchdog.h>
#include <components/heartrate/HeartRateController.h> #include <components/heartrate/HeartRateController.h>
#include <components/motion/MotionController.h>
#include <components/settings/Settings.h> #include <components/settings/Settings.h>
#include "TouchEvents.h" #include "TouchEvents.h"
#include "Apps.h" #include "Apps.h"
@ -35,7 +36,8 @@ namespace Pinetime {
System::SystemTask &systemTask, System::SystemTask &systemTask,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Pinetime::Controllers::HeartRateController& heartRateController, Pinetime::Controllers::HeartRateController& heartRateController,
Pinetime::Controllers::Settings& settingsController); Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::MotionController& motionController);
void Start(); void Start();
void PushMessage(Pinetime::Applications::Display::Messages msg); void PushMessage(Pinetime::Applications::Display::Messages msg);

View file

@ -53,6 +53,7 @@ static lv_style_t style_table_cell;
static lv_style_t style_pad_small; static lv_style_t style_pad_small;
static lv_style_t style_bg_grad; static lv_style_t style_bg_grad;
static lv_style_t style_lmeter; static lv_style_t style_lmeter;
static lv_style_t style_chart_serie;
static lv_style_t style_cb_bg; static lv_style_t style_cb_bg;
static lv_style_t style_cb_bullet; static lv_style_t style_cb_bullet;
@ -277,6 +278,12 @@ static void basic_init(void)
lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10)); lv_style_set_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(10));
lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7)); lv_style_set_scale_end_line_width(&style_lmeter, LV_STATE_DEFAULT, LV_DPX(7));
style_init_reset(&style_chart_serie);
lv_style_set_line_color(&style_chart_serie, LV_STATE_DEFAULT, LV_PINETIME_WHITE);
lv_style_set_line_width(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_size(&style_chart_serie, LV_STATE_DEFAULT, 4);
lv_style_set_bg_opa(&style_chart_serie, LV_STATE_DEFAULT, 0);
lv_style_reset(&style_cb_bg); lv_style_reset(&style_cb_bg);
lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4)); lv_style_set_radius(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(4));
lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(10)); lv_style_set_pad_inner(&style_cb_bg, LV_STATE_DEFAULT, LV_DPX(10));
@ -501,6 +508,13 @@ static void theme_apply(lv_obj_t * obj, lv_theme_style_t name)
_lv_style_list_add_style(list, &style_lmeter); _lv_style_list_add_style(list, &style_lmeter);
break; break;
case LV_THEME_CHART:
lv_obj_clean_style_list(obj, LV_CHART_PART_SERIES);
list = lv_obj_get_style_list(obj, LV_CHART_PART_SERIES);
_lv_style_list_add_style(list, &style_btn);
_lv_style_list_add_style(list, &style_chart_serie);
break;
case LV_THEME_CHECKBOX: case LV_THEME_CHECKBOX:
list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BG); list = lv_obj_get_style_list(obj, LV_CHECKBOX_PART_BG);
_lv_style_list_add_style(list, &style_cb_bg); _lv_style_list_add_style(list, &style_cb_bg);

View file

@ -47,7 +47,7 @@ std::unique_ptr<Screen> ApplicationList::CreateScreen1() {
{Symbols::stopWatch, Apps::StopWatch}, {Symbols::stopWatch, Apps::StopWatch},
{Symbols::music, Apps::Music}, {Symbols::music, Apps::Music},
{Symbols::map, Apps::Navigation}, {Symbols::map, Apps::Navigation},
{Symbols::shoe, Apps::Clock}, {Symbols::shoe, Apps::Motion},
{Symbols::heartBeat, Apps::HeartRate}, {Symbols::heartBeat, Apps::HeartRate},
{"", Apps::None}, {"", Apps::None},
} }

View file

@ -8,6 +8,7 @@
#include "NotificationIcon.h" #include "NotificationIcon.h"
#include "Symbols.h" #include "Symbols.h"
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "components/motion/MotionController.h"
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include "../DisplayApp.h" #include "../DisplayApp.h"
@ -23,11 +24,13 @@ Clock::Clock(DisplayApp* app,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings &settingsController,
Controllers::HeartRateController& heartRateController) : Screen(app), Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController) : Screen(app),
dateTimeController{dateTimeController}, batteryController{batteryController}, dateTimeController{dateTimeController}, batteryController{batteryController},
bleController{bleController}, notificatioManager{notificatioManager}, bleController{bleController}, notificatioManager{notificatioManager},
settingsController{settingsController}, settingsController{settingsController},
heartRateController{heartRateController}, heartRateController{heartRateController},
motionController{motionController},
screens{app, screens{app,
settingsController.GetClockFace(), settingsController.GetClockFace(),
{ {
@ -59,7 +62,7 @@ bool Clock::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
} }
std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() { std::unique_ptr<Screen> Clock::WatchFaceDigitalScreen() {
return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController); return std::make_unique<Screens::WatchFaceDigital>(app, dateTimeController, batteryController, bleController, notificatioManager, settingsController, heartRateController, motionController);
} }
std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() { std::unique_ptr<Screen> Clock::WatchFaceAnalogScreen() {

View file

@ -17,6 +17,7 @@ namespace Pinetime {
class Battery; class Battery;
class Ble; class Ble;
class NotificationManager; class NotificationManager;
class MotionController;
} }
namespace Applications { namespace Applications {
@ -29,7 +30,8 @@ namespace Pinetime {
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings &settingsController,
Controllers::HeartRateController& heartRateController); Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController);
~Clock() override; ~Clock() override;
bool Refresh() override; bool Refresh() override;
@ -44,6 +46,7 @@ namespace Pinetime {
Controllers::NotificationManager& notificatioManager; Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController; Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController; Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;
ScreenList<2> screens; ScreenList<2> screens;

View file

@ -0,0 +1,59 @@
#include <libs/lvgl/lvgl.h>
#include "Motion.h"
#include "../DisplayApp.h"
using namespace Pinetime::Applications::Screens;
extern lv_font_t jetbrains_mono_extrabold_compressed;
extern lv_font_t jetbrains_mono_bold_20;
Motion::Motion(Pinetime::Applications::DisplayApp *app, Controllers::MotionController& motionController) : Screen(app), motionController{motionController} {
chart = lv_chart_create(lv_scr_act(), NULL);
lv_obj_set_size(chart, 240, 240);
lv_obj_align(chart, NULL, LV_ALIGN_IN_TOP_MID, 0, 0);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/
//lv_chart_set_series_opa(chart, LV_OPA_70); /*Opacity of the data series*/
//lv_chart_set_series_width(chart, 4); /*Line width and point radious*/
lv_chart_set_range(chart, -1100, 1100);
lv_chart_set_update_mode(chart, LV_CHART_UPDATE_MODE_SHIFT);
lv_chart_set_point_count(chart, 10);
/*Add 3 data series*/
ser1 = lv_chart_add_series(chart, LV_COLOR_RED);
ser2 = lv_chart_add_series(chart, LV_COLOR_GREEN);
ser3 = lv_chart_add_series(chart, LV_COLOR_YELLOW);
lv_chart_init_points(chart, ser1, 0);
lv_chart_init_points(chart, ser2, 0);
lv_chart_init_points(chart, ser3, 0);
lv_chart_refresh(chart); /*Required after direct set*/
labelStep = lv_label_create(lv_scr_act(), NULL);
lv_obj_align(labelStep, chart, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_label_set_text(labelStep, "Steps: ");
labelStepValue = lv_label_create(lv_scr_act(), NULL);
lv_obj_align(labelStepValue, labelStep, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_label_set_text(labelStepValue, "-");
}
Motion::~Motion() {
lv_obj_clean(lv_scr_act());
}
bool Motion::Refresh() {
lv_chart_set_next(chart, ser1, motionController.X());
lv_chart_set_next(chart, ser2, motionController.Y());
lv_chart_set_next(chart, ser3, motionController.Z());
snprintf(nbStepsBuffer, nbStepsBufferSize, "%lu", motionController.NbSteps());
lv_label_set_text(labelStepValue, nbStepsBuffer);
return running;
}
bool Motion::OnButtonPushed() {
running = false;
return true;
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <cstdint>
#include <chrono>
#include "Screen.h"
#include <bits/unique_ptr.h>
#include <libs/lvgl/src/lv_core/lv_style.h>
#include <libs/lvgl/src/lv_core/lv_obj.h>
#include <components/motion/MotionController.h>
namespace Pinetime {
namespace Applications {
namespace Screens {
class Motion : public Screen{
public:
Motion(DisplayApp* app, Controllers::MotionController& motionController);
~Motion() override;
bool Refresh() override;
bool OnButtonPushed() override;
private:
Controllers::MotionController& motionController;
lv_obj_t * chart;
lv_chart_series_t * ser1;
lv_chart_series_t * ser2;
lv_chart_series_t * ser3;
lv_obj_t* labelStep;
lv_obj_t* labelStepValue;
static constexpr uint8_t nbStepsBufferSize = 9;
char nbStepsBuffer[nbStepsBufferSize+1];
bool running = true;
};
}
}
}

View file

@ -11,6 +11,7 @@
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h" #include "components/ble/NotificationManager.h"
#include "components/heartrate/HeartRateController.h" #include "components/heartrate/HeartRateController.h"
#include "components/motion/MotionController.h"
#include "components/settings/Settings.h" #include "components/settings/Settings.h"
#include "../DisplayApp.h" #include "../DisplayApp.h"
@ -23,11 +24,13 @@ WatchFaceDigital::WatchFaceDigital(DisplayApp* app,
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings &settingsController,
Controllers::HeartRateController& heartRateController): Screen(app), currentDateTime{{}}, Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController) : Screen(app), currentDateTime{{}},
dateTimeController{dateTimeController}, batteryController{batteryController}, dateTimeController{dateTimeController}, batteryController{batteryController},
bleController{bleController}, notificatioManager{notificatioManager}, bleController{bleController}, notificatioManager{notificatioManager},
settingsController{settingsController}, settingsController{settingsController},
heartRateController{heartRateController} { heartRateController{heartRateController},
motionController{motionController} {
settingsController.SetClockFace(0); settingsController.SetClockFace(0);
displayedChar[0] = 0; displayedChar[0] = 0;
@ -236,10 +239,14 @@ bool WatchFaceDigital::Refresh() {
lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0); lv_obj_align(heartbeatBpm, heartbeatValue, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
} }
// TODO stepCount = stepController.GetValue(); stepCount = motionController.NbSteps();
if(stepCount.IsUpdated()) { motionSensorOk = motionController.IsSensorOk();
if(stepCount.IsUpdated() || motionSensorOk.IsUpdated()) {
char stepBuffer[5]; char stepBuffer[5];
if(motionSensorOk.Get())
sprintf(stepBuffer, "%lu", stepCount.Get()); sprintf(stepBuffer, "%lu", stepCount.Get());
else
sprintf(stepBuffer, "---", stepCount.Get());
lv_label_set_text(stepValue, stepBuffer); lv_label_set_text(stepValue, stepBuffer);
lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2); lv_obj_align(stepValue, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, -5, -2);
lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0); lv_obj_align(stepIcon, stepValue, LV_ALIGN_OUT_LEFT_MID, -5, 0);

View file

@ -15,6 +15,7 @@ namespace Pinetime {
class Ble; class Ble;
class NotificationManager; class NotificationManager;
class HeartRateController; class HeartRateController;
class MotionController;
} }
namespace Applications { namespace Applications {
@ -28,7 +29,8 @@ namespace Pinetime {
Controllers::Ble& bleController, Controllers::Ble& bleController,
Controllers::NotificationManager& notificatioManager, Controllers::NotificationManager& notificatioManager,
Controllers::Settings &settingsController, Controllers::Settings &settingsController,
Controllers::HeartRateController& heartRateController); Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController);
~WatchFaceDigital() override; ~WatchFaceDigital() override;
bool Refresh() override; bool Refresh() override;
@ -48,6 +50,7 @@ namespace Pinetime {
DirtyValue<int> batteryPercentRemaining {}; DirtyValue<int> batteryPercentRemaining {};
DirtyValue<bool> bleState {}; DirtyValue<bool> bleState {};
DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime{}; DirtyValue<std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>> currentDateTime{};
DirtyValue<bool> motionSensorOk {};
DirtyValue<uint32_t> stepCount {}; DirtyValue<uint32_t> stepCount {};
DirtyValue<uint8_t> heartbeat {}; DirtyValue<uint8_t> heartbeat {};
DirtyValue<bool> heartbeatRunning {}; DirtyValue<bool> heartbeatRunning {};
@ -73,6 +76,7 @@ namespace Pinetime {
Controllers::NotificationManager& notificatioManager; Controllers::NotificationManager& notificatioManager;
Controllers::Settings& settingsController; Controllers::Settings& settingsController;
Controllers::HeartRateController& heartRateController; Controllers::HeartRateController& heartRateController;
Controllers::MotionController& motionController;

116
src/drivers/Bma421.cpp Normal file
View file

@ -0,0 +1,116 @@
#include <libraries/delay/nrf_delay.h>
#include <libraries/log/nrf_log.h>
#include "Bma421.h"
#include "TwiMaster.h"
#include <drivers/Bma421_C/bma423.h>
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<Bma421*>(intf_ptr);
bma421->Read(reg_addr, reg_data, length);
return 0;
}
int8_t user_i2c_write(uint8_t reg_addr, const uint8_t* reg_data, uint32_t length, void* intf_ptr) {
auto bma421 = static_cast<Bma421*>(intf_ptr);
bma421->Write(reg_addr, reg_data, length);
return 0;
}
void user_delay(uint32_t period_us, void* intf_ptr) {
nrf_delay_us(period_us);
}
}
Bma421::Bma421(TwiMaster& twiMaster, uint8_t twiAddress) : twiMaster{twiMaster}, deviceAddress{twiAddress} {
bma.intf = BMA4_I2C_INTF;
bma.bus_read = user_i2c_read;
bma.bus_write = user_i2c_write;
bma.variant = BMA42X_VARIANT;
bma.intf_ptr = this;
bma.delay_us = user_delay;
bma.read_write_len = 8;
}
void Bma421::Init() {
if(not isResetOk) return; // Call SoftReset (and reset TWI device) first!
auto ret = bma423_init(&bma);
if(ret != BMA4_OK) return;
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;
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;
struct bma4_accel_config accel_conf;
accel_conf.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
accel_conf.range = BMA4_ACCEL_RANGE_2G;
accel_conf.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
accel_conf.perf_mode = BMA4_CIC_AVG_MODE;
ret = bma4_set_accel_config(&accel_conf, &bma);
if(ret != BMA4_OK) return;
isOk = 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) return {};
struct bma4_accel data;
bma4_read_accel_xyz(&data, &bma);
uint32_t steps = 0;
bma423_step_counter_output(&steps, &bma);
int32_t temperature;
bma4_get_temperature(&temperature, BMA4_DEG, &bma);
temperature = temperature / 1000;
uint8_t activity = 0;
bma423_activity_output(&activity, &bma);
NRF_LOG_INFO("MOTION : %d - %d/%d/%d", steps, data.x, data.y, data.z);
// 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;
}
void Bma421::ResetStepCounter() {
bma423_reset_step_counter(&bma);
}
void Bma421::SoftReset() {
auto ret = bma4_soft_reset(&bma);
if(ret == BMA4_OK) {
isResetOk = true;
nrf_delay_ms(1);
}
}

43
src/drivers/Bma421.h Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include <drivers/Bma421_C/bma4_defs.h>
namespace Pinetime {
namespace Drivers {
class TwiMaster;
class Bma421 {
public:
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;
Bma421(Bma421&&) = delete;
Bma421& operator=(Bma421&&) = delete;
/// The chip freezes the TWI bus after the softreset operation. Softreset is separated from the
/// Init() method to allow the caller to uninit and then reinit the TWI device after the softreset.
void SoftReset();
void Init();
Values 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;
private:
void Reset();
TwiMaster& twiMaster;
uint8_t deviceAddress = 0x18;
struct bma4_dev bma;
bool isOk = false;
bool isResetOk = false;
};
}
}

5689
src/drivers/Bma421_C/bma4.c Normal file

File diff suppressed because it is too large Load diff

2281
src/drivers/Bma421_C/bma4.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,196 +2,77 @@
#include <cstring> #include <cstring>
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <nrfx_log.h> #include <nrfx_log.h>
#include <nrfx_twim.h>
#include <nrf_drv_twi.h>
using namespace Pinetime::Drivers; using namespace Pinetime::Drivers;
// TODO use shortcut to automatically send STOP when receive LastTX, for example // TODO use shortcut to automatically send STOP when receive LastTX, for example
// TODO use DMA/IRQ // TODO use DMA/IRQ
TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params} { TwiMaster::TwiMaster(const Modules module, const Parameters& params) : module{module}, params{params}, mutex{xSemaphoreCreateBinary()} {
mutex = xSemaphoreCreateBinary(); ASSERT(mutex != nullptr);
ASSERT(mutex != NULL); switch(module) {
case Modules::TWIM1:
default:
twim = NRFX_TWIM_INSTANCE(1);
break;
}
} }
void TwiMaster::Init() { void TwiMaster::Init() {
NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) nrfx_twim_config_t config;
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) config.frequency = static_cast<nrf_twim_frequency_t>(params.frequency);
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) config.hold_bus_uninit = false;
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) config.interrupt_priority = 0;
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); config.scl = params.pinScl;
config.sda = params.pinSda;
NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) nrfx_twim_init(&twim,
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) &config,
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) nullptr,
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) nullptr);
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos); nrfx_twim_enable(&twim);
switch(module) {
case Modules::TWIM1: twiBaseAddress = NRF_TWIM1; break;
default:
return;
}
switch(static_cast<Frequencies>(params.frequency)) {
case Frequencies::Khz100 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100; break;
case Frequencies::Khz250 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250; break;
case Frequencies::Khz400 : twiBaseAddress->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400; break;
}
twiBaseAddress->PSEL.SCL = params.pinScl;
twiBaseAddress->PSEL.SDA = params.pinSda;
twiBaseAddress->EVENTS_LASTRX = 0;
twiBaseAddress->EVENTS_STOPPED = 0;
twiBaseAddress->EVENTS_LASTTX = 0;
twiBaseAddress->EVENTS_ERROR = 0;
twiBaseAddress->EVENTS_RXSTARTED = 0;
twiBaseAddress->EVENTS_SUSPENDED = 0;
twiBaseAddress->EVENTS_TXSTARTED = 0;
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos);
/* // IRQ
NVIC_ClearPendingIRQ(_IRQn);
NVIC_SetPriority(_IRQn, 2);
NVIC_EnableIRQ(_IRQn);
*/
xSemaphoreGive(mutex); xSemaphoreGive(mutex);
} }
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) { TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
xSemaphoreTake(mutex, portMAX_DELAY); xSemaphoreTake(mutex, portMAX_DELAY);
auto ret = ReadWithRetry(deviceAddress, registerAddress, data, size); TwiMaster::ErrorCodes ret;
auto err = nrfx_twim_tx(&twim, deviceAddress, &registerAddress, 1, false);
if(err != 0) {
return TwiMaster::ErrorCodes::TransactionFailed;
}
err = nrfx_twim_rx(&twim, deviceAddress, data, size);
if(err != 0) {
return TwiMaster::ErrorCodes::TransactionFailed;
}
xSemaphoreGive(mutex); xSemaphoreGive(mutex);
return ret; return TwiMaster::ErrorCodes::NoError;
} }
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) { TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
ASSERT(size <= maxDataSize); ASSERT(size <= maxDataSize);
xSemaphoreTake(mutex, portMAX_DELAY); xSemaphoreTake(mutex, portMAX_DELAY);
auto ret = WriteWithRetry(deviceAddress, registerAddress, data, size);
xSemaphoreGive(mutex);
return ret;
}
/* Execute a read transaction (composed of a write and a read operation). If one of these opeartion fails,
* it's retried once. If it fails again, an error is returned */
TwiMaster::ErrorCodes TwiMaster::ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t *data, size_t size) {
TwiMaster::ErrorCodes ret; TwiMaster::ErrorCodes ret;
ret = Write(deviceAddress, &registerAddress, 1, false);
if(ret != ErrorCodes::NoError)
ret = Write(deviceAddress, &registerAddress, 1, false);
if(ret != ErrorCodes::NoError) return ret;
ret = Read(deviceAddress, data, size, true);
if(ret != ErrorCodes::NoError)
ret = Read(deviceAddress, data, size, true);
return ret;
}
/* Execute a write transaction. If it fails, it is retried once. If it fails again, an error is returned. */
TwiMaster::ErrorCodes TwiMaster::WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t *data, size_t size) {
internalBuffer[0] = registerAddress; internalBuffer[0] = registerAddress;
std::memcpy(internalBuffer+1, data, size); std::memcpy(internalBuffer+1, data, size);
auto ret = Write(deviceAddress, internalBuffer, size+1, true); auto err = nrfx_twim_tx(&twim, deviceAddress, internalBuffer , size+1, false);
if(ret != ErrorCodes::NoError) if(err != 0){
ret = Write(deviceAddress, internalBuffer, size+1, true); return TwiMaster::ErrorCodes::TransactionFailed;
return ret;
} }
xSemaphoreGive(mutex);
TwiMaster::ErrorCodes TwiMaster::Read(uint8_t deviceAddress, uint8_t *buffer, size_t size, bool stop) { return TwiMaster::ErrorCodes::NoError;
twiBaseAddress->ADDRESS = deviceAddress;
twiBaseAddress->TASKS_RESUME = 0x1UL;
twiBaseAddress->RXD.PTR = (uint32_t)buffer;
twiBaseAddress->RXD.MAXCNT = size;
twiBaseAddress->TASKS_STARTRX = 1;
while(!twiBaseAddress->EVENTS_RXSTARTED && !twiBaseAddress->EVENTS_ERROR);
twiBaseAddress->EVENTS_RXSTARTED = 0x0UL;
txStartedCycleCount = DWT->CYCCNT;
uint32_t currentCycleCount;
while(!twiBaseAddress->EVENTS_LASTRX && !twiBaseAddress->EVENTS_ERROR) {
currentCycleCount = DWT->CYCCNT;
if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
FixHwFreezed();
return ErrorCodes::TransactionFailed;
}
}
twiBaseAddress->EVENTS_LASTRX = 0x0UL;
if (stop || twiBaseAddress->EVENTS_ERROR) {
twiBaseAddress->TASKS_STOP = 0x1UL;
while(!twiBaseAddress->EVENTS_STOPPED);
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
}
else {
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
while(!twiBaseAddress->EVENTS_SUSPENDED);
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
}
if (twiBaseAddress->EVENTS_ERROR) {
twiBaseAddress->EVENTS_ERROR = 0x0UL;
}
return ErrorCodes::NoError;
}
TwiMaster::ErrorCodes TwiMaster::Write(uint8_t deviceAddress, const uint8_t *data, size_t size, bool stop) {
twiBaseAddress->ADDRESS = deviceAddress;
twiBaseAddress->TASKS_RESUME = 0x1UL;
twiBaseAddress->TXD.PTR = (uint32_t)data;
twiBaseAddress->TXD.MAXCNT = size;
twiBaseAddress->TASKS_STARTTX = 1;
while(!twiBaseAddress->EVENTS_TXSTARTED && !twiBaseAddress->EVENTS_ERROR);
twiBaseAddress->EVENTS_TXSTARTED = 0x0UL;
txStartedCycleCount = DWT->CYCCNT;
uint32_t currentCycleCount;
while(!twiBaseAddress->EVENTS_LASTTX && !twiBaseAddress->EVENTS_ERROR) {
currentCycleCount = DWT->CYCCNT;
if ((currentCycleCount-txStartedCycleCount) > HwFreezedDelay) {
FixHwFreezed();
return ErrorCodes::TransactionFailed;
}
}
twiBaseAddress->EVENTS_LASTTX = 0x0UL;
if (stop || twiBaseAddress->EVENTS_ERROR) {
twiBaseAddress->TASKS_STOP = 0x1UL;
while(!twiBaseAddress->EVENTS_STOPPED);
twiBaseAddress->EVENTS_STOPPED = 0x0UL;
}
else {
twiBaseAddress->TASKS_SUSPEND = 0x1UL;
while(!twiBaseAddress->EVENTS_SUSPENDED);
twiBaseAddress->EVENTS_SUSPENDED = 0x0UL;
}
if (twiBaseAddress->EVENTS_ERROR) {
twiBaseAddress->EVENTS_ERROR = 0x0UL;
uint32_t error = twiBaseAddress->ERRORSRC;
twiBaseAddress->ERRORSRC = error;
}
return ErrorCodes::NoError;
} }
void TwiMaster::Sleep() { void TwiMaster::Sleep() {
while(twiBaseAddress->ENABLE != 0) { nrfx_twim_disable(&twim);
twiBaseAddress->ENABLE = (TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos); nrfx_twim_uninit(&twim);
}
nrf_gpio_cfg_default(6); nrf_gpio_cfg_default(6);
nrf_gpio_cfg_default(7); nrf_gpio_cfg_default(7);
NRF_LOG_INFO("[TWIMASTER] Sleep"); NRF_LOG_INFO("[TWIMASTER] Sleep");
@ -201,30 +82,3 @@ void TwiMaster::Wakeup() {
Init(); Init();
NRF_LOG_INFO("[TWIMASTER] Wakeup"); NRF_LOG_INFO("[TWIMASTER] Wakeup");
} }
/* Sometimes, the TWIM device just freeze and never set the event EVENTS_LASTTX.
* This method disable and re-enable the peripheral so that it works again.
* This is just a workaround, and it would be better if we could find a way to prevent
* this issue from happening.
* */
void TwiMaster::FixHwFreezed() {
NRF_LOG_INFO("I2C device frozen, reinitializing it!");
// Disable I²C
uint32_t twi_state = NRF_TWI1->ENABLE;
twiBaseAddress->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
NRF_GPIO->PIN_CNF[params.pinScl] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
NRF_GPIO->PIN_CNF[params.pinSda] = ((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos)
| ((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| ((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
| ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
// Re-enable I²C
twiBaseAddress->ENABLE = twi_state;
}

View file

@ -3,13 +3,13 @@
#include <semphr.h> #include <semphr.h>
#include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type #include <drivers/include/nrfx_twi.h> // NRF_TWIM_Type
#include <cstdint> #include <cstdint>
#include <nrfx_twim.h>
namespace Pinetime { namespace Pinetime {
namespace Drivers { namespace Drivers {
class TwiMaster { class TwiMaster {
public: public:
enum class Modules { TWIM1 }; enum class Modules { TWIM1 };
enum class Frequencies {Khz100, Khz250, Khz400};
enum class ErrorCodes {NoError, TransactionFailed}; enum class ErrorCodes {NoError, TransactionFailed};
struct Parameters { struct Parameters {
uint32_t frequency; uint32_t frequency;
@ -27,21 +27,13 @@ namespace Pinetime {
void Wakeup(); void Wakeup();
private: private:
ErrorCodes ReadWithRetry(uint8_t deviceAddress, uint8_t registerAddress, uint8_t* buffer, size_t size); nrfx_twim_t twim;
ErrorCodes WriteWithRetry(uint8_t deviceAddress, uint8_t registerAddress, const uint8_t* data, size_t size);
ErrorCodes Read(uint8_t deviceAddress, uint8_t* buffer, size_t size, bool stop);
ErrorCodes Write(uint8_t deviceAddress, const uint8_t* data, size_t size, bool stop);
void FixHwFreezed();
NRF_TWIM_Type* twiBaseAddress;
SemaphoreHandle_t mutex;
const Modules module; const Modules module;
const Parameters params; const Parameters params;
SemaphoreHandle_t mutex;
static constexpr uint8_t maxDataSize{8}; static constexpr uint8_t maxDataSize{8};
static constexpr uint8_t registerSize{1}; static constexpr uint8_t registerSize{1};
uint8_t internalBuffer[maxDataSize + registerSize]; uint8_t internalBuffer[maxDataSize + registerSize];
uint32_t txStartedCycleCount = 0;
static constexpr uint32_t HwFreezedDelay{161000};
}; };
} }
} }

View file

@ -26,6 +26,7 @@
#include <task.h> #include <task.h>
#include <timers.h> #include <timers.h>
#include <drivers/Hrs3300.h> #include <drivers/Hrs3300.h>
#include <drivers/Bma421.h>
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
#include "components/ble/BleController.h" #include "components/ble/BleController.h"
@ -60,6 +61,7 @@ static constexpr uint8_t pinLcdDataCommand = 18;
static constexpr uint8_t pinTwiScl = 7; static constexpr uint8_t pinTwiScl = 7;
static constexpr uint8_t pinTwiSda = 6; static constexpr uint8_t pinTwiSda = 6;
static constexpr uint8_t touchPanelTwiAddress = 0x15; static constexpr uint8_t touchPanelTwiAddress = 0x15;
static constexpr uint8_t motionSensorTwiAddress = 0x18;
static constexpr uint8_t heartRateSensorTwiAddress = 0x44; static constexpr uint8_t heartRateSensorTwiAddress = 0x44;
Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, { Pinetime::Drivers::SpiMaster spi{Pinetime::Drivers::SpiMaster::SpiModule::SPI0, {
@ -98,14 +100,13 @@ static constexpr bool isFactory = false;
Pinetime::Components::LittleVgl lvgl {lcd, touchPanel}; Pinetime::Components::LittleVgl lvgl {lcd, touchPanel};
#endif #endif
Pinetime::Drivers::Bma421 motionSensor{twiMaster, motionSensorTwiAddress};
Pinetime::Drivers::Hrs3300 heartRateSensor {twiMaster, heartRateSensorTwiAddress}; Pinetime::Drivers::Hrs3300 heartRateSensor {twiMaster, heartRateSensorTwiAddress};
TimerHandle_t debounceTimer; TimerHandle_t debounceTimer;
Pinetime::Controllers::Battery batteryController; Pinetime::Controllers::Battery batteryController;
Pinetime::Controllers::Ble bleController; Pinetime::Controllers::Ble bleController;
Pinetime::Controllers::DateTime dateTimeController;
void ble_manager_set_ble_connection_callback(void (*connection)()); void ble_manager_set_ble_connection_callback(void (*connection)());
void ble_manager_set_ble_disconnection_callback(void (*disconnection)()); void ble_manager_set_ble_disconnection_callback(void (*disconnection)());
static constexpr uint8_t pinTouchIrq = 28; static constexpr uint8_t pinTouchIrq = 28;
@ -257,7 +258,7 @@ int main(void) {
debounceTimer = xTimerCreate ("debounceTimer", 200, pdFALSE, (void *) 0, DebounceTimerCallback); debounceTimer = xTimerCreate ("debounceTimer", 200, pdFALSE, (void *) 0, DebounceTimerCallback);
systemTask = std::make_unique<Pinetime::System::SystemTask>(spi, lcd, spiNorFlash, twiMaster, touchPanel, lvgl, batteryController, bleController, systemTask = std::make_unique<Pinetime::System::SystemTask>(spi, lcd, spiNorFlash, twiMaster, touchPanel, lvgl, batteryController, bleController,
dateTimeController, motorController, heartRateSensor, settingsController); motorController, heartRateSensor, motionSensor, settingsController);
systemTask->Start(); systemTask->Start();
nimble_port_init(); nimble_port_init();

View file

@ -4992,7 +4992,7 @@
// <e> NRFX_TWIM_ENABLED - nrfx_twim - TWIM peripheral driver // <e> NRFX_TWIM_ENABLED - nrfx_twim - TWIM peripheral driver
//========================================================== //==========================================================
#ifndef NRFX_TWIM_ENABLED #ifndef NRFX_TWIM_ENABLED
#define NRFX_TWIM_ENABLED 0 #define NRFX_TWIM_ENABLED 1
#endif #endif
// <q> NRFX_TWIM0_ENABLED - Enable TWIM0 instance // <q> NRFX_TWIM0_ENABLED - Enable TWIM0 instance
@ -5005,7 +5005,7 @@
#ifndef NRFX_TWIM1_ENABLED #ifndef NRFX_TWIM1_ENABLED
#define NRFX_TWIM1_ENABLED 0 #define NRFX_TWIM1_ENABLED 1
#endif #endif
// <o> NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY - Frequency // <o> NRFX_TWIM_DEFAULT_CONFIG_FREQUENCY - Frequency

View file

@ -40,16 +40,16 @@ SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel, Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel,
Components::LittleVgl &lvgl, Components::LittleVgl &lvgl,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Controllers::Battery &batteryController, Controllers::Ble &bleController,
Controllers::DateTime &dateTimeController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Drivers::Hrs3300& heartRateSensor, Pinetime::Drivers::Hrs3300& heartRateSensor,
Pinetime::Drivers::Bma421& motionSensor,
Controllers::Settings &settingsController) : Controllers::Settings &settingsController) :
spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash}, spi{spi}, lcd{lcd}, spiNorFlash{spiNorFlash},
twiMaster{twiMaster}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController}, twiMaster{twiMaster}, touchPanel{touchPanel}, lvgl{lvgl}, batteryController{batteryController},
heartRateController{*this}, heartRateController{*this},
bleController{bleController}, dateTimeController{dateTimeController}, bleController{bleController}, dateTimeController{*this},
watchdog{}, watchdogView{watchdog}, watchdog{}, watchdogView{watchdog},
motorController{motorController}, heartRateSensor{heartRateSensor}, motorController{motorController}, heartRateSensor{heartRateSensor}, motionSensor{motionSensor},
settingsController{settingsController}, settingsController{settingsController},
nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) { nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) {
systemTasksMsgQueue = xQueueCreate(10, 1); systemTasksMsgQueue = xQueueCreate(10, 1);
@ -84,13 +84,18 @@ void SystemTask::Work() {
touchPanel.Init(); touchPanel.Init();
batteryController.Init(); batteryController.Init();
motorController.Init(); motorController.Init();
motionSensor.SoftReset();
// Reset the TWI device because the motion sensor chip most probably crashed it...
twiMaster.Sleep();
twiMaster.Init();
motionSensor.Init();
settingsController.Init(); settingsController.Init();
displayApp = std::make_unique<Pinetime::Applications::DisplayApp>(lcd, lvgl, touchPanel, batteryController, bleController, displayApp = std::make_unique<Pinetime::Applications::DisplayApp>(lcd, lvgl, touchPanel, batteryController, bleController,
dateTimeController, watchdogView, *this, notificationManager, dateTimeController, watchdogView, *this, notificationManager,
heartRateController, settingsController); heartRateController, settingsController, motionController);
displayApp->Start(); displayApp->Start();
batteryController.Update(); batteryController.Update();
@ -132,8 +137,10 @@ void SystemTask::Work() {
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop" #pragma ide diagnostic ignored "EndlessLoop"
while(true) { while(true) {
UpdateMotion();
uint8_t msg; uint8_t msg;
if (xQueueReceive(systemTasksMsgQueue, &msg, isSleeping ? 2500 : 1000)) { if (xQueueReceive(systemTasksMsgQueue, &msg, 100)) {
batteryController.Update(); batteryController.Update();
Messages message = static_cast<Messages >(msg); Messages message = static_cast<Messages >(msg);
switch(message) { switch(message) {
@ -148,10 +155,10 @@ void SystemTask::Work() {
break; break;
case Messages::GoToRunning: case Messages::GoToRunning:
spi.Wakeup(); spi.Wakeup();
twiMaster.Wakeup();
// Double Tap needs the touch screen to be in normal mode // Double Tap needs the touch screen to be in normal mode
if ( settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap ) { if ( settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap ) {
twiMaster.Wakeup();
touchPanel.Wakeup(); touchPanel.Wakeup();
} }
@ -168,7 +175,9 @@ void SystemTask::Work() {
isWakingUp = false; isWakingUp = false;
break; break;
case Messages::TouchWakeUp: { case Messages::TouchWakeUp: {
twiMaster.Wakeup();
auto touchInfo = touchPanel.GetTouchInfo(); auto touchInfo = touchPanel.GetTouchInfo();
twiMaster.Sleep();
if( touchInfo.isTouch and if( touchInfo.isTouch and
( (
( touchInfo.gesture == Pinetime::Drivers::Cst816S::Gestures::DoubleTap and ( touchInfo.gesture == Pinetime::Drivers::Cst816S::Gestures::DoubleTap and
@ -232,12 +241,17 @@ void SystemTask::Work() {
// Double Tap needs the touch screen to be in normal mode // Double Tap needs the touch screen to be in normal mode
if ( settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap ) { if ( settingsController.getWakeUpMode() != Pinetime::Controllers::Settings::WakeUpMode::DoubleTap ) {
touchPanel.Sleep(); touchPanel.Sleep();
twiMaster.Sleep();
} }
twiMaster.Sleep();
isSleeping = true; isSleeping = true;
isGoingToSleep = false; isGoingToSleep = false;
break; break;
case Messages::OnNewDay:
// We might be sleeping (with TWI device disabled.
// Remember we'll have to reset the counter next time we're awake
stepCounterMustBeReset = true;
break;
default: break; default: break;
} }
} }
@ -262,6 +276,30 @@ void SystemTask::Work() {
// Clear diagnostic suppression // Clear diagnostic suppression
#pragma clang diagnostic pop #pragma clang diagnostic pop
} }
void SystemTask::UpdateMotion() {
if(isGoingToSleep or isWakingUp) return;
if(isSleeping)
twiMaster.Wakeup();
if(stepCounterMustBeReset) {
motionSensor.ResetStepCounter();
stepCounterMustBeReset = false;
}
auto motionValues = motionSensor.Process();
if(isSleeping)
twiMaster.Sleep();
motionController.IsSensorOk(motionSensor.IsOk());
motionController.Update(motionValues.x,
motionValues.y,
motionValues.z,
motionValues.steps);
if (motionController.ShouldWakeUp(isSleeping)) {
GoToRunning();
}
}
void SystemTask::OnButtonPushed() { void SystemTask::OnButtonPushed() {
if(isGoingToSleep) return; if(isGoingToSleep) return;
@ -279,6 +317,7 @@ void SystemTask::OnButtonPushed() {
} }
void SystemTask::GoToRunning() { void SystemTask::GoToRunning() {
if(isGoingToSleep or (not isSleeping) or isWakingUp) return;
isWakingUp = true; isWakingUp = true;
PushMessage(Messages::GoToRunning); PushMessage(Messages::GoToRunning);
} }

View file

@ -8,6 +8,8 @@
#include <heartratetask/HeartRateTask.h> #include <heartratetask/HeartRateTask.h>
#include <components/heartrate/HeartRateController.h> #include <components/heartrate/HeartRateController.h>
#include <components/settings/Settings.h> #include <components/settings/Settings.h>
#include <drivers/Bma421.h>
#include <components/motion/MotionController.h>
#include "SystemMonitor.h" #include "SystemMonitor.h"
#include "components/battery/BatteryController.h" #include "components/battery/BatteryController.h"
@ -38,7 +40,8 @@ namespace Pinetime {
class SystemTask { class SystemTask {
public: public:
enum class Messages {GoToSleep, GoToRunning, TouchWakeUp, OnNewTime, OnNewNotification, OnNewCall, BleConnected, UpdateTimeOut, enum class Messages {GoToSleep, GoToRunning, TouchWakeUp, OnNewTime, OnNewNotification, OnNewCall, BleConnected, UpdateTimeOut,
BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, OnButtonEvent, OnDisplayTaskSleeping, EnableSleeping, DisableSleeping BleFirmwareUpdateStarted, BleFirmwareUpdateFinished, OnTouchEvent, OnButtonEvent, OnDisplayTaskSleeping, EnableSleeping, DisableSleeping,
OnNewDay
}; };
SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd, SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
@ -46,9 +49,9 @@ namespace Pinetime {
Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel, Drivers::TwiMaster& twiMaster, Drivers::Cst816S &touchPanel,
Components::LittleVgl &lvgl, Components::LittleVgl &lvgl,
Controllers::Battery &batteryController, Controllers::Ble &bleController, Controllers::Battery &batteryController, Controllers::Ble &bleController,
Controllers::DateTime &dateTimeController,
Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotorController& motorController,
Pinetime::Drivers::Hrs3300& heartRateSensor, Pinetime::Drivers::Hrs3300& heartRateSensor,
Pinetime::Drivers::Bma421& motionSensor,
Controllers::Settings &settingsController); Controllers::Settings &settingsController);
@ -77,7 +80,7 @@ namespace Pinetime {
std::unique_ptr<Pinetime::Applications::HeartRateTask> heartRateApp; std::unique_ptr<Pinetime::Applications::HeartRateTask> heartRateApp;
Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::Ble& bleController;
Pinetime::Controllers::DateTime& dateTimeController; Pinetime::Controllers::DateTime dateTimeController;
QueueHandle_t systemTasksMsgQueue; QueueHandle_t systemTasksMsgQueue;
std::atomic<bool> isSleeping{false}; std::atomic<bool> isSleeping{false};
std::atomic<bool> isGoingToSleep{false}; std::atomic<bool> isGoingToSleep{false};
@ -87,9 +90,11 @@ namespace Pinetime {
Pinetime::Controllers::NotificationManager notificationManager; Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotorController& motorController;
Pinetime::Drivers::Hrs3300& heartRateSensor; Pinetime::Drivers::Hrs3300& heartRateSensor;
Pinetime::Drivers::Bma421& motionSensor;
Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::Settings& settingsController;
Pinetime::Controllers::NimbleController nimbleController; Pinetime::Controllers::NimbleController nimbleController;
Controllers::BrightnessController brightnessController; Controllers::BrightnessController brightnessController;
Pinetime::Controllers::MotionController motionController;
static constexpr uint8_t pinSpiSck = 2; static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3; static constexpr uint8_t pinSpiMosi = 3;
@ -108,6 +113,8 @@ namespace Pinetime {
bool doNotGoToSleep = false; bool doNotGoToSleep = false;
void GoToRunning(); void GoToRunning();
void UpdateMotion();
bool stepCounterMustBeReset = false;
#if configUSE_TRACE_FACILITY == 1 #if configUSE_TRACE_FACILITY == 1
SystemMonitor<FreeRtosMonitor> monitor; SystemMonitor<FreeRtosMonitor> monitor;