blob: 8b0a5d7770b6df8611e25a94e6e7692c94f198eb (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
/*
* SoftTimer.cpp
*
* Created on: 21 Jul 2017
* Author: David
*/
#include "SoftTimer.h"
#include "Platform.h"
#include "Movement/DDA.h"
SoftTimer * volatile SoftTimer::pendingList = nullptr;
SoftTimer::SoftTimer() : next(nullptr), callback(nullptr)
{
}
// Schedule a callback at a particular tick count, returning true if it was not scheduled because it is already due or imminent.
// There must be no callback already scheduled for this timer, else the linked list will get messed up. If in doubt, call CancelCallback before calling this.
bool SoftTimer::ScheduleCallback(Ticks when, Callback cb, void *param)
{
whenDue = when;
callback = cb;
cbParam = param;
const irqflags_t flags = cpu_irq_save();
const Ticks now = GetTimerTicksNow();
const int32_t howSoon = (int32_t)(when - now);
SoftTimer** ppst = const_cast<SoftTimer**>(&pendingList);
if (*ppst == nullptr || howSoon < (int32_t)((*ppst)->whenDue - now))
{
// No other callbacks are scheduled, or this one is due earlier than the first existing one
if (Platform::ScheduleSoftTimerInterrupt(when))
{
cpu_irq_restore(flags);
return true;
}
}
else
{
while (*ppst != nullptr && (int32_t)((*ppst)->whenDue - now) < howSoon)
{
ppst = &((*ppst)->next);
}
}
next = *ppst;
*ppst = this;
cpu_irq_restore(flags);
return false;
}
// Cancel any scheduled callback for this timer. Harmless if there is no callback scheduled.
void SoftTimer::CancelCallback()
{
const irqflags_t flags = cpu_irq_save();
for (SoftTimer** ppst = const_cast<SoftTimer**>(&pendingList); *ppst != nullptr; ppst = &((*ppst)->next))
{
if (*ppst == this)
{
*ppst = this->next; // unlink this from the pending list
break;
}
}
cpu_irq_restore(flags);
}
// Get the current tick count
/*static*/ SoftTimer::Ticks SoftTimer::GetTimerTicksNow()
{
return Platform::GetInterruptClocks();
}
// Get the tick rate
/*static*/ SoftTimer::Ticks SoftTimer::GetTickRate()
{
return DDA::stepClockRate; // the software timer uses the same counter as the step timer
}
// ISR called from Platform. May sometimes get called prematurely.
/*static*/ void SoftTimer::Interrupt()
{
for (;;)
{
SoftTimer * const tmr = pendingList;
if (tmr == nullptr)
{
break;
}
// On the first iteration, the timer at the head of the list is probably expired.
// Try to schedule another interrupt for it, if we get a true return then it has indeed expired and we need to execute the callback.
// On subsequent iterations this just sets up the interrupt for the next timer that is due to expire.
if (Platform::ScheduleSoftTimerInterrupt(tmr->whenDue))
{
pendingList = tmr->next; // remove it from the pending list
if (tmr->callback != nullptr && tmr->callback(tmr->cbParam, tmr->whenDue)) // execute its callback
{
// Schedule another callback for this timer
SoftTimer** ppst = const_cast<SoftTimer**>(&pendingList);
while (*ppst != nullptr && (int32_t)(tmr->whenDue - (*ppst)->whenDue) > 0)
{
ppst = &((*ppst)->next);
}
tmr->next = *ppst;
*ppst = tmr;
}
}
else
{
break;
}
}
}
// End
|