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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
/*
* StepTimer.h
*
* Created on: 9 Sep 2018
* Author: David
*/
#ifndef SRC_MOVEMENT_STEPTIMER_H_
#define SRC_MOVEMENT_STEPTIMER_H_
#include "RepRapFirmware.h"
#define STEP_TIMER_DEBUG 1 // currently this only works for the SAME5x
class CanMessageTimeSync;
// Class to implement a software timer with a few microseconds resolution
// Important! In systems that use 16-bit timers, callbacks may take place at multiples of 65536 ticks before they are actually due.
// In order to achieve the maximum step rate possible, the timer code doesn't check for this, because the step generation code checks which drivers are due steps anyway.
// Any other client that uses the timer MUST do a similar check. The simple way to do this is to use a callback function of the following form:
// if (timer.ScheduleCallbackFromIsr()) { /* code to execute it the callback really was due */ }
class StepTimer
{
public:
typedef uint32_t Ticks;
typedef void (*TimerCallbackFunction)(CallbackParameter) noexcept;
StepTimer() noexcept;
// Set up the callback function and parameter
void SetCallback(TimerCallbackFunction cb, CallbackParameter param) noexcept;
// Schedule a callback at a particular tick count, returning true if it was not scheduled because it is already due or imminent
bool ScheduleCallback(Ticks when) noexcept SPEED_CRITICAL;
// As ScheduleCallback but base priority >= NvicPriorityStep when called. Can be called from within a callback.
bool ScheduleCallbackFromIsr(Ticks when) noexcept SPEED_CRITICAL;
// Check whether a callback really is due, schedule it if not. Returns true if it really is due. Can be called from within a callback.
bool ScheduleCallbackFromIsr() noexcept SPEED_CRITICAL;
// Cancel any scheduled callbacks
void CancelCallback() noexcept;
// As CancelCallback but base priority >= NvicPriorityStep when called
void CancelCallbackFromIsr() noexcept SPEED_CRITICAL;
// Initialise the timer system
static void Init() noexcept;
// Disable the timer interrupt. Called when we shut down the system.
static void DisableTimerInterrupt() noexcept;
// Get the current tick count
static Ticks GetTimerTicks() noexcept SPEED_CRITICAL;
// Get the current tick count when we only need a 16-bit value. Faster than GetTimerTicks() on the SAM4S and SAME70.
static uint16_t GetTimerTicks16() noexcept;
// Get the tick rate (can also access it directly as StepClockRate)
static constexpr uint32_t GetTickRate() noexcept { return StepClockRate; }
// ISR called from StepTimer
static void Interrupt() noexcept;
static constexpr uint32_t MinInterruptInterval = 6; // Minimum interval between step timer interrupts, in step clocks; about 6us
#if SUPPORT_REMOTE_COMMANDS
static uint32_t GetLocalTimeOffset() noexcept { return localTimeOffset; }
static void ProcessTimeSyncMessage(const CanMessageTimeSync& msg, size_t msgLen, uint16_t timeStamp) noexcept;
static uint32_t ConvertToLocalTime(uint32_t masterTime) noexcept { return masterTime + localTimeOffset; }
static uint32_t ConvertToMasterTime(uint32_t localTime) noexcept { return localTime - localTimeOffset; }
static uint32_t GetMasterTime() noexcept { return ConvertToMasterTime(GetTimerTicks()); }
static bool IsSynced() noexcept;
static void Diagnostics(const StringRef& reply) noexcept;
static constexpr uint32_t MinSyncInterval = 2000; // maximum interval in milliseconds between sync messages for us to remain synced
// increased from 1000 because of workaround we added for bad Tx time stamps on SAME70
#endif
#if STEP_TIMER_DEBUG
static uint32_t maxInterval;
#endif
private:
static bool ScheduleTimerInterrupt(uint32_t tim) noexcept; // Schedule an interrupt at the specified clock count, or return true if it has passed already
StepTimer *next;
Ticks whenDue;
TimerCallbackFunction callback;
CallbackParameter cbParam;
volatile bool active;
static StepTimer * volatile pendingList; // list of pending callbacks, soonest first
#if STEP_TIMER_DEBUG
static uint32_t lastTimerResult;
#endif
#if SUPPORT_REMOTE_COMMANDS
static volatile uint32_t localTimeOffset; // local time minus master time
static volatile uint32_t whenLastSynced; // the millis tick count when we last synced
static uint32_t prevMasterTime; // the previous master time received
static uint32_t prevLocalTime; // the previous local time when the master time was received, corrected for receive processing delay
static int32_t peakPosJitter, peakNegJitter; // the max and min corrections we made to local time offset while synced
static bool gotJitter; // true if we have recorded the jitter
static uint32_t peakReceiveDelay; // the maximum receive delay we measured by using the receive time stamp
static volatile unsigned int syncCount; // the number of messages we have received since starting sync
static unsigned int numJitterResyncs, numTimeoutResyncs;
static constexpr uint32_t MaxSyncJitter = StepClockRate/100; // 10ms
static constexpr unsigned int MaxSyncCount = 10;
#endif
};
// Function GetTimerTicks() is quite long for SAM4S and SAME70 processors, so it is moved to StepTimer.cpp and no longer inlined
#if !(SAM4S || SAME70 || SAME5x)
inline __attribute__((always_inline)) StepTimer::Ticks StepTimer::GetTimerTicks() noexcept
{
# if defined(__LPC17xx__)
return STEP_TC->TC;
# else
return STEP_TC->TC_CHANNEL[STEP_TC_CHAN].TC_CV;
# endif
}
#endif
inline __attribute__((always_inline)) uint16_t StepTimer::GetTimerTicks16() noexcept
{
#if SAME5x
return (uint16_t)GetTimerTicks();
#elif defined(__LPC17xx__)
return (uint16_t)STEP_TC->TC;
#else
return (uint16_t)STEP_TC->TC_CHANNEL[STEP_TC_CHAN].TC_CV;
#endif
}
#endif /* SRC_MOVEMENT_STEPTIMER_H_ */
|