Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 121 additions & 18 deletions src/components/motor/MotorController.cpp
Original file line number Diff line number Diff line change
@@ -1,43 +1,146 @@
#include "components/motor/MotorController.h"
#include <cstdint>
#include <hal/nrf_gpio.h>
#include "systemtask/SystemTask.h"
#include "components/motion/MotionController.h"
#include "drivers/PinMap.h"

using namespace Pinetime::Controllers;

MotorController::MotorController(Pinetime::Controllers::MotionController& motionController) : motionController {motionController} {
}

void MotorController::Init() {
nrf_gpio_cfg_output(PinMap::Motor);
nrf_gpio_pin_set(PinMap::Motor);

shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
nextEvt = xTimerCreate("vib", 1, pdFALSE, this, NextEvent);
}

void MotorController::NextEvent(TimerHandle_t timer) {
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(timer));

TickType_t deviation = xTaskGetTickCount() - motorController->nextEvtTime;

TickType_t duration;

switch (motorController->state) {
case State::Buzz:
SetMotorRunning(false);
motorController->state = State::Idle;
return;
case State::RingOn:
SetMotorRunning(false);
if (motorController->remainingBuzzes == 1) {
motorController->state = State::Idle;
return;
}
if (motorController->remainingBuzzes > 1) {
motorController->remainingBuzzes--;
}
motorController->state = State::RingOff;
duration = motorController->ringPeriod - motorController->ringOnTicks;
break;
case State::RingOff:
SetMotorRunning(true);
motorController->state = State::RingOn;
duration = motorController->ringOnTicks;
break;
case State::Idle:
// should never hit the idle state in the timer callback
assert(false);
__builtin_trap();
}
motorController->nextEvtTime += duration;
if (duration > deviation) {
xTimerChangePeriod(timer, duration - deviation, 0);
} else {
xTimerChangePeriod(timer, 1, 0);
}
}

void MotorController::Ring(TimerHandle_t xTimer) {
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
motorController->RunForDuration(50);
uint16_t MotorController::CalculateLength(Intensity intensity) {
// don't think shake speed really has units
// 200 seems sensible, then clamp the multiplier to range 1-3
// a fancy curve would probably feel better, but this seems to work well
float durationMultiplier = motionController.CurrentShakeSpeed() / 200.f;
durationMultiplier = std::max(std::min(durationMultiplier, 3.f), 1.f);

float length;

switch (intensity) {
case Intensity::Light:
length = 30.f;
break;
case Intensity::Medium:
length = 50.f;
break;
case Intensity::Strong:
length = 90.f;
break;
}
return length * durationMultiplier;
}

void MotorController::RunForDuration(uint8_t motorDuration) {
if (motorDuration > 0 && xTimerChangePeriod(shortVib, pdMS_TO_TICKS(motorDuration), 0) == pdPASS && xTimerStart(shortVib, 0) == pdPASS) {
void MotorController::RunEvent(void* motorControllerPtr, uint32_t requestedStateInt) {
auto* motorController = static_cast<MotorController*>(motorControllerPtr);
auto requestedState = static_cast<RequestEvt>(requestedStateInt);

// these aren't always set, but if they're not set they're never used
// so it's safe to copy unconditionally
motorController->ringOnTicks = motorController->nextOperation.ringOnTicks;
motorController->ringPeriod = motorController->nextOperation.ringPeriod;
motorController->remainingBuzzes = motorController->nextOperation.buzzCount;

if (requestedState == RequestEvt::RequestRingOff) {
SetMotorRunning(false);
motorController->state = State::Idle;
xTimerStop(motorController->nextEvt, 0);
} else {
SetMotorRunning(true);
motorController->nextEvtTime = xTaskGetTickCount() + motorController->ringOnTicks;
if (requestedState == RequestEvt::RequestBuzz) {
motorController->state = State::Buzz;
} else {
motorController->state = State::RingOn;
}
// changing the period starts the timer
xTimerChangePeriod(motorController->nextEvt, motorController->ringOnTicks, 0);
}
}

void MotorController::PushEvent(RequestEvt state) {
// since the timer callback modifies its own state,
// we need to run timer state changes in the timer task to avoid races
xTimerPendFunctionCall(RunEvent, this, static_cast<uint32_t>(state), portMAX_DELAY);
}

void MotorController::Buzz(Intensity intensity) {
nextOperation.ringOnTicks = CalculateLength(intensity);

PushEvent(RequestEvt::RequestBuzz);
}

void MotorController::SetMotorRunning(bool active) {
if (active) {
nrf_gpio_pin_clear(PinMap::Motor);
} else {
nrf_gpio_pin_set(PinMap::Motor);
}
}

void MotorController::StartRinging() {
RunForDuration(50);
xTimerStart(longVib, 0);
// numBuzzes = 0 rings forever
void MotorController::Ring(Intensity intensity, TickType_t ringPeriod, uint16_t numBuzzes) {
nextOperation.ringOnTicks = CalculateLength(intensity);
nextOperation.ringPeriod = ringPeriod;
nextOperation.buzzCount = numBuzzes;

PushEvent(RequestEvt::RequestRingOn);
}

void MotorController::StopRinging() {
xTimerStop(longVib, 0);
nrf_gpio_pin_set(PinMap::Motor);
PushEvent(RequestEvt::RequestRingOff);
}

bool MotorController::IsRinging() {
return (xTimerIsTimerActive(longVib) == pdTRUE);
}

void MotorController::StopMotor(TimerHandle_t /*xTimer*/) {
nrf_gpio_pin_set(PinMap::Motor);
return state == State::RingOn || state == State::RingOff;
}
35 changes: 28 additions & 7 deletions src/components/motor/MotorController.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,43 @@

namespace Pinetime {
namespace Controllers {
class MotionController;

class MotorController {
public:
MotorController() = default;
explicit MotorController(Pinetime::Controllers::MotionController& motionController);

enum class Intensity : uint8_t { Light, Medium, Strong };

void Init();
void RunForDuration(uint8_t motorDuration);
void StartRinging();
void Buzz(Intensity intensity);
void Ring(Intensity intensity, TickType_t ringPeriod, uint16_t numBuzzes = 0);
void StopRinging();
bool IsRinging();

private:
static void Ring(TimerHandle_t xTimer);
static void StopMotor(TimerHandle_t xTimer);
TimerHandle_t shortVib;
TimerHandle_t longVib;
enum class State : uint8_t { Idle, Buzz, RingOn, RingOff };
enum class RequestEvt : uint8_t { RequestBuzz, RequestRingOn, RequestRingOff };

struct Request {
TickType_t ringOnTicks;
TickType_t ringPeriod;
uint16_t buzzCount;
};

Request nextOperation;
State state;
uint16_t ringOnTicks;
TickType_t ringPeriod;
TickType_t nextEvtTime;
TimerHandle_t nextEvt;
uint16_t remainingBuzzes;
MotionController& motionController;
uint16_t CalculateLength(Intensity intensity);
static void NextEvent(TimerHandle_t timer);
static void RunEvent(void* motorControllerPtr, uint32_t requestedStateInt);
static void SetMotorRunning(bool active);
void PushEvent(RequestEvt state);
};
}
}
4 changes: 2 additions & 2 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ void DisplayApp::Refresh() {
break;
case Messages::ShowPairingKey:
LoadNewScreen(Apps::PassKey, DisplayApp::FullRefreshDirections::Up);
motorController.RunForDuration(35);
motorController.Buzz(Controllers::MotorController::Intensity::Light);
break;
case Messages::TouchEvent: {
if (state != States::Running) {
Expand Down Expand Up @@ -483,7 +483,7 @@ void DisplayApp::Refresh() {
break;
case Messages::Chime:
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
motorController.RunForDuration(35);
motorController.Ring(Controllers::MotorController::Intensity::Light, pdMS_TO_TICKS(200), 2);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/Alarm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ void Alarm::SetAlerting() {
minuteCounter.HideControls();
lv_obj_set_hidden(btnStop, false);
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
motorController.StartRinging();
motorController.Ring(Controllers::MotorController::Intensity::Strong, pdMS_TO_TICKS(1000));
wakeLock.Lock();
}

Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/Dice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ void Dice::Roll() {

lv_label_set_text_fmt(resultTotalLabel, "%d", resultTotal);
if (openingRoll == false) {
motorController.RunForDuration(30);
motorController.Buzz(Controllers::MotorController::Intensity::Light);
NextColor();
currentRollHysteresis = rollHysteresis;
}
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/InfiniPaint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
}

std::fill(b, b + bufferSize, selectColor);
motor.RunForDuration(35);
motor.Buzz(Controllers::MotorController::Intensity::Light);
return true;
default:
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/displayapp/screens/Metronome.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ void Metronome::Refresh() {
counter--;
if (counter == 0) {
counter = bpb;
motorController.RunForDuration(90);
motorController.Buzz(Controllers::MotorController::Intensity::Strong);
} else {
motorController.RunForDuration(30);
motorController.Buzz(Controllers::MotorController::Intensity::Light);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/displayapp/screens/Notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ Notifications::Notifications(DisplayApp* app,
if (mode == Modes::Preview) {
wakeLock.Lock();
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
motorController.StartRinging();
motorController.Ring(Controllers::MotorController::Intensity::Medium, pdMS_TO_TICKS(1000));
} else {
motorController.RunForDuration(35);
motorController.Buzz(Controllers::MotorController::Intensity::Light);
}

timeoutLine = lv_line_create(lv_scr_act(), nullptr);
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ void Timer::SetTimerStopped() {
}

void Timer::SetTimerRinging() {
motorController.StartRinging();
motorController.Ring(Controllers::MotorController::Intensity::Medium, pdMS_TO_TICKS(1000));
wakeLock.Lock();
minuteCounter.HideControls();
secondCounter.HideControls();
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/screens/settings/QuickSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ void QuickSettings::OnButtonEvent(lv_obj_t* object) {
settingsController.SetNotificationStatus(Controllers::Settings::Notification::On);
lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn);
lv_obj_set_state(btn3, static_cast<lv_state_t>(ButtonState::NotificationsOn));
motorController.RunForDuration(35);
motorController.Buzz(Controllers::MotorController::Intensity::Light);
}

} else if (object == btn4) {
Expand Down
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ Pinetime::Controllers::Ble bleController;

Pinetime::Controllers::FS fs {spiNorFlash};
Pinetime::Controllers::Settings settingsController {fs};
Pinetime::Controllers::MotorController motorController {};

Pinetime::Controllers::HeartRateController heartRateController;
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController, settingsController);
Expand All @@ -105,6 +104,7 @@ Pinetime::Controllers::DateTime dateTimeController {settingsController};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::MotorController motorController {motionController};
Pinetime::Controllers::StopWatchController stopWatchController;
Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs};
Pinetime::Controllers::TouchHandler touchHandler;
Expand Down
Loading