Skip to content

Commit 4693d69

Browse files
committed
Improve MotorController flexibility
1 parent 8250565 commit 4693d69

11 files changed

Lines changed: 161 additions & 37 deletions

File tree

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,146 @@
11
#include "components/motor/MotorController.h"
2+
#include <cstdint>
23
#include <hal/nrf_gpio.h>
3-
#include "systemtask/SystemTask.h"
4+
#include "components/motion/MotionController.h"
45
#include "drivers/PinMap.h"
56

67
using namespace Pinetime::Controllers;
78

9+
MotorController::MotorController(Pinetime::Controllers::MotionController& motionController) : motionController {motionController} {
10+
}
11+
812
void MotorController::Init() {
913
nrf_gpio_cfg_output(PinMap::Motor);
1014
nrf_gpio_pin_set(PinMap::Motor);
1115

12-
shortVib = xTimerCreate("shortVib", 1, pdFALSE, nullptr, StopMotor);
13-
longVib = xTimerCreate("longVib", pdMS_TO_TICKS(1000), pdTRUE, this, Ring);
16+
nextEvt = xTimerCreate("vib", 1, pdFALSE, this, NextEvent);
17+
}
18+
19+
void MotorController::NextEvent(TimerHandle_t timer) {
20+
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(timer));
21+
22+
TickType_t deviation = xTaskGetTickCount() - motorController->nextEvtTime;
23+
24+
TickType_t duration;
25+
26+
switch (motorController->state) {
27+
case State::Buzz:
28+
SetMotorRunning(false);
29+
motorController->state = State::Idle;
30+
return;
31+
case State::RingOn:
32+
SetMotorRunning(false);
33+
if (motorController->remainingBuzzes == 1) {
34+
motorController->state = State::Idle;
35+
return;
36+
}
37+
if (motorController->remainingBuzzes > 1) {
38+
motorController->remainingBuzzes--;
39+
}
40+
motorController->state = State::RingOff;
41+
duration = motorController->ringPeriod - motorController->ringOnTicks;
42+
break;
43+
case State::RingOff:
44+
SetMotorRunning(true);
45+
motorController->state = State::RingOn;
46+
duration = motorController->ringOnTicks;
47+
break;
48+
case State::Idle:
49+
// should never hit the idle state in the timer callback
50+
assert(false);
51+
__builtin_trap();
52+
}
53+
motorController->nextEvtTime += duration;
54+
if (duration > deviation) {
55+
xTimerChangePeriod(timer, duration - deviation, 0);
56+
} else {
57+
xTimerChangePeriod(timer, 1, 0);
58+
}
1459
}
1560

16-
void MotorController::Ring(TimerHandle_t xTimer) {
17-
auto* motorController = static_cast<MotorController*>(pvTimerGetTimerID(xTimer));
18-
motorController->RunForDuration(50);
61+
uint16_t MotorController::CalculateLength(Intensity intensity) {
62+
// don't think shake speed really has units
63+
// 200 seems sensible, then clamp the multiplier to range 1-3
64+
// a fancy curve would probably feel better, but this seems to work well
65+
float durationMultiplier = motionController.CurrentShakeSpeed() / 200.f;
66+
durationMultiplier = std::max(std::min(durationMultiplier, 3.f), 1.f);
67+
68+
float length;
69+
70+
switch (intensity) {
71+
case Intensity::Light:
72+
length = 30.f;
73+
break;
74+
case Intensity::Medium:
75+
length = 50.f;
76+
break;
77+
case Intensity::Strong:
78+
length = 90.f;
79+
break;
80+
}
81+
return length * durationMultiplier;
1982
}
2083

21-
void MotorController::RunForDuration(uint8_t motorDuration) {
22-
if (motorDuration > 0 && xTimerChangePeriod(shortVib, pdMS_TO_TICKS(motorDuration), 0) == pdPASS && xTimerStart(shortVib, 0) == pdPASS) {
84+
void MotorController::RunEvent(void* motorControllerPtr, uint32_t requestedStateInt) {
85+
auto* motorController = static_cast<MotorController*>(motorControllerPtr);
86+
auto requestedState = static_cast<RequestEvt>(requestedStateInt);
87+
88+
// these aren't always set, but if they're not set they're never used
89+
// so it's safe to copy unconditionally
90+
motorController->ringOnTicks = motorController->nextOperation.ringOnTicks;
91+
motorController->ringPeriod = motorController->nextOperation.ringPeriod;
92+
motorController->remainingBuzzes = motorController->nextOperation.buzzCount;
93+
94+
if (requestedState == RequestEvt::RequestRingOff) {
95+
SetMotorRunning(false);
96+
motorController->state = State::Idle;
97+
xTimerStop(motorController->nextEvt, 0);
98+
} else {
99+
SetMotorRunning(true);
100+
motorController->nextEvtTime = xTaskGetTickCount() + motorController->ringOnTicks;
101+
if (requestedState == RequestEvt::RequestBuzz) {
102+
motorController->state = State::Buzz;
103+
} else {
104+
motorController->state = State::RingOn;
105+
}
106+
// changing the period starts the timer
107+
xTimerChangePeriod(motorController->nextEvt, motorController->ringOnTicks, 0);
108+
}
109+
}
110+
111+
void MotorController::PushEvent(RequestEvt state) {
112+
// since the timer callback modifies its own state,
113+
// we need to run timer state changes in the timer task to avoid races
114+
xTimerPendFunctionCall(RunEvent, this, static_cast<uint32_t>(state), portMAX_DELAY);
115+
}
116+
117+
void MotorController::Buzz(Intensity intensity) {
118+
nextOperation.ringOnTicks = CalculateLength(intensity);
119+
120+
PushEvent(RequestEvt::RequestBuzz);
121+
}
122+
123+
void MotorController::SetMotorRunning(bool active) {
124+
if (active) {
23125
nrf_gpio_pin_clear(PinMap::Motor);
126+
} else {
127+
nrf_gpio_pin_set(PinMap::Motor);
24128
}
25129
}
26130

27-
void MotorController::StartRinging() {
28-
RunForDuration(50);
29-
xTimerStart(longVib, 0);
131+
// numBuzzes = 0 rings forever
132+
void MotorController::Ring(Intensity intensity, TickType_t ringPeriod, uint16_t numBuzzes) {
133+
nextOperation.ringOnTicks = CalculateLength(intensity);
134+
nextOperation.ringPeriod = ringPeriod;
135+
nextOperation.buzzCount = numBuzzes;
136+
137+
PushEvent(RequestEvt::RequestRingOn);
30138
}
31139

32140
void MotorController::StopRinging() {
33-
xTimerStop(longVib, 0);
34-
nrf_gpio_pin_set(PinMap::Motor);
141+
PushEvent(RequestEvt::RequestRingOff);
35142
}
36143

37144
bool MotorController::IsRinging() {
38-
return (xTimerIsTimerActive(longVib) == pdTRUE);
39-
}
40-
41-
void MotorController::StopMotor(TimerHandle_t /*xTimer*/) {
42-
nrf_gpio_pin_set(PinMap::Motor);
145+
return state == State::RingOn || state == State::RingOff;
43146
}

src/components/motor/MotorController.h

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,43 @@
66

77
namespace Pinetime {
88
namespace Controllers {
9+
class MotionController;
910

1011
class MotorController {
1112
public:
12-
MotorController() = default;
13+
explicit MotorController(Pinetime::Controllers::MotionController& motionController);
14+
15+
enum class Intensity : uint8_t { Light, Medium, Strong };
1316

1417
void Init();
15-
void RunForDuration(uint8_t motorDuration);
16-
void StartRinging();
18+
void Buzz(Intensity intensity);
19+
void Ring(Intensity intensity, TickType_t ringPeriod, uint16_t numBuzzes = 0);
1720
void StopRinging();
1821
bool IsRinging();
1922

2023
private:
21-
static void Ring(TimerHandle_t xTimer);
22-
static void StopMotor(TimerHandle_t xTimer);
23-
TimerHandle_t shortVib;
24-
TimerHandle_t longVib;
24+
enum class State : uint8_t { Idle, Buzz, RingOn, RingOff };
25+
enum class RequestEvt : uint8_t { RequestBuzz, RequestRingOn, RequestRingOff };
26+
27+
struct Request {
28+
TickType_t ringOnTicks;
29+
TickType_t ringPeriod;
30+
uint16_t buzzCount;
31+
};
32+
33+
Request nextOperation;
34+
State state;
35+
uint16_t ringOnTicks;
36+
TickType_t ringPeriod;
37+
TickType_t nextEvtTime;
38+
TimerHandle_t nextEvt;
39+
uint16_t remainingBuzzes;
40+
MotionController& motionController;
41+
uint16_t CalculateLength(Intensity intensity);
42+
static void NextEvent(TimerHandle_t timer);
43+
static void RunEvent(void* motorControllerPtr, uint32_t requestedStateInt);
44+
static void SetMotorRunning(bool active);
45+
void PushEvent(RequestEvt state);
2546
};
2647
}
2748
}

src/displayapp/DisplayApp.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ void DisplayApp::Refresh() {
394394
break;
395395
case Messages::ShowPairingKey:
396396
LoadNewScreen(Apps::PassKey, DisplayApp::FullRefreshDirections::Up);
397-
motorController.RunForDuration(35);
397+
motorController.Buzz(Controllers::MotorController::Intensity::Light);
398398
break;
399399
case Messages::TouchEvent: {
400400
if (state != States::Running) {
@@ -483,7 +483,7 @@ void DisplayApp::Refresh() {
483483
break;
484484
case Messages::Chime:
485485
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
486-
motorController.RunForDuration(35);
486+
motorController.Ring(Controllers::MotorController::Intensity::Light, pdMS_TO_TICKS(200), 2);
487487
break;
488488
}
489489
}

src/displayapp/screens/Alarm.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ void Alarm::SetAlerting() {
209209
minuteCounter.HideControls();
210210
lv_obj_set_hidden(btnStop, false);
211211
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
212-
motorController.StartRinging();
212+
motorController.Ring(Controllers::MotorController::Intensity::Strong, pdMS_TO_TICKS(1000));
213213
wakeLock.Lock();
214214
}
215215

src/displayapp/screens/Dice.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ void Dice::Roll() {
186186

187187
lv_label_set_text_fmt(resultTotalLabel, "%d", resultTotal);
188188
if (openingRoll == false) {
189-
motorController.RunForDuration(30);
189+
motorController.Buzz(Controllers::MotorController::Intensity::Light);
190190
NextColor();
191191
currentRollHysteresis = rollHysteresis;
192192
}

src/displayapp/screens/InfiniPaint.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ bool InfiniPaint::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
5252
}
5353

5454
std::fill(b, b + bufferSize, selectColor);
55-
motor.RunForDuration(35);
55+
motor.Buzz(Controllers::MotorController::Intensity::Light);
5656
return true;
5757
default:
5858
return true;

src/displayapp/screens/Metronome.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ void Metronome::Refresh() {
8282
counter--;
8383
if (counter == 0) {
8484
counter = bpb;
85-
motorController.RunForDuration(90);
85+
motorController.Buzz(Controllers::MotorController::Intensity::Strong);
8686
} else {
87-
motorController.RunForDuration(30);
87+
motorController.Buzz(Controllers::MotorController::Intensity::Light);
8888
}
8989
}
9090
}

src/displayapp/screens/Notifications.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ Notifications::Notifications(DisplayApp* app,
4242
if (mode == Modes::Preview) {
4343
wakeLock.Lock();
4444
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
45-
motorController.StartRinging();
45+
motorController.Ring(Controllers::MotorController::Intensity::Medium, pdMS_TO_TICKS(1000));
4646
} else {
47-
motorController.RunForDuration(35);
47+
motorController.Buzz(Controllers::MotorController::Intensity::Light);
4848
}
4949

5050
timeoutLine = lv_line_create(lv_scr_act(), nullptr);

src/displayapp/screens/Timer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void Timer::SetTimerStopped() {
170170
}
171171

172172
void Timer::SetTimerRinging() {
173-
motorController.StartRinging();
173+
motorController.Ring(Controllers::MotorController::Intensity::Medium, pdMS_TO_TICKS(1000));
174174
wakeLock.Lock();
175175
minuteCounter.HideControls();
176176
secondCounter.HideControls();

src/displayapp/screens/settings/QuickSettings.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ void QuickSettings::OnButtonEvent(lv_obj_t* object) {
159159
settingsController.SetNotificationStatus(Controllers::Settings::Notification::On);
160160
lv_label_set_text_static(btn3_lvl, Symbols::notificationsOn);
161161
lv_obj_set_state(btn3, static_cast<lv_state_t>(ButtonState::NotificationsOn));
162-
motorController.RunForDuration(35);
162+
motorController.Buzz(Controllers::MotorController::Intensity::Light);
163163
}
164164

165165
} else if (object == btn4) {

0 commit comments

Comments
 (0)