PX4 Firmware
PX4 Autopilot Software http://px4.io
uc_kinetis_clock.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014, 2018 Pavel Kirienko <pavel.kirienko@gmail.com>
3  * Kinetis Port Author David Sidrane <david_s5@nscdg.com>
4  */
5 
8 #include "internal.hpp"
9 
10 #if UAVCAN_KINETIS_TIMER_NUMBER
11 # include <cassert>
12 # include <math.h>
13 
14 /*
15  * Timer instance
16  * todo:Consider using Lifetime Timer support
17  */
18 # define TIMX_IRQHandler UAVCAN_KINETIS_GLUE3(PIT, UAVCAN_KINETIS_TIMER_NUMBER, _IRQHandler)
19 # define TIMX (KINETIS_PIT_BASE + (UAVCAN_KINETIS_TIMER_NUMBER << 4))
20 # define TMR_REG(o) (TIMX + (o))
21 # define TIMX_INPUT_CLOCK BOARD_BUS_FREQ
22 # define TIMX_INTERRUPT_FREQ 16
23 # define TIMX_IRQn UAVCAN_KINETIS_GLUE2(KINETIS_IRQ_PITCH, UAVCAN_KINETIS_TIMER_NUMBER)
24 
25 # if UAVCAN_KINETIS_TIMER_NUMBER >= 0 && UAVCAN_KINETIS_TIMER_NUMBER <= 3
26 # define KINETIS_PIT_LDVAL_OFFSET KINETIS_PIT_LDVAL0_OFFSET
27 # define KINETIS_PIT_CVAL_OFFSET KINETIS_PIT_CVAL0_OFFSET
28 # define KINETIS_PIT_TCTRL_OFFSET KINETIS_PIT_TCTRL0_OFFSET
29 # define KINETIS_PIT_TFLG_OFFSET KINETIS_PIT_TFLG0_OFFSET
30 # else
31 # error "This UAVCAN_KINETIS_TIMER_NUMBER is not supported yet"
32 # endif
33 
34 extern "C" UAVCAN_KINETIS_IRQ_HANDLER(TIMX_IRQHandler);
35 
36 namespace uavcan_kinetis
37 {
38 namespace clock
39 {
40 namespace
41 {
42 
43 const uavcan::uint32_t CountsPerPeriod = (TIMX_INPUT_CLOCK / TIMX_INTERRUPT_FREQ);
44 const uavcan::uint32_t CountsPerUs = (TIMX_INPUT_CLOCK / 1000000);
45 const uavcan::uint32_t USecPerOverflow = (1000000 / TIMX_INTERRUPT_FREQ);
46 
47 Mutex mutex;
48 
49 bool initialized = false;
50 
51 bool utc_set = false;
52 bool utc_locked = false;
53 uavcan::uint32_t utc_jump_cnt = 0;
54 UtcSyncParams utc_sync_params;
55 float utc_prev_adj = 0;
56 float utc_rel_rate_ppm = 0;
57 float utc_rel_rate_error_integral = 0;
58 uavcan::int32_t utc_accumulated_correction_nsec = 0;
59 uavcan::int32_t utc_correction_nsec_per_overflow = 0;
60 uavcan::MonotonicTime prev_utc_adj_at;
61 
62 uavcan::uint64_t time_mono = 0;
63 uavcan::uint64_t time_utc = 0;
64 
65 }
66 
67 void init()
68 {
69  CriticalSectionLocker lock;
70 
71  if (initialized) {
72  return;
73  }
74 
75  initialized = true;
76 
77  // Attach IRQ
78  irq_attach(TIMX_IRQn, &TIMX_IRQHandler, NULL);
79 
80  // Power-on Clock
81  modifyreg32(KINETIS_SIM_SCGC6, 0, SIM_SCGC6_PIT);
82 
83  // Enable module
84  putreg32(0, KINETIS_PIT_MCR);
85 
86  // Start the timer
87 
88  putreg32(CountsPerPeriod - 1, TMR_REG(KINETIS_PIT_LDVAL_OFFSET));
89  putreg32(PIT_TCTRL_TEN | PIT_TCTRL_TIE, TMR_REG(KINETIS_PIT_TCTRL_OFFSET)); // Start
90 
91  // Prioritize and Enable IRQ
92 
93 #if 0
94  // This has to be off or uses the default priority
95  // Without the ability to point the vector
96  // Directly to this ISR this will reenter the
97  // exception_common and cause the interrupt
98  // Stack pointer to be reset
99  up_prioritize_irq(TIMX_IRQn, NVIC_SYSH_HIGH_PRIORITY);
100 #endif
101  up_enable_irq(TIMX_IRQn);
102 }
103 
104 void setUtc(uavcan::UtcTime time)
105 {
106  MutexLocker mlocker(mutex);
107  UAVCAN_ASSERT(initialized);
108 
109  {
110  CriticalSectionLocker locker;
111  time_utc = time.toUSec();
112  }
113 
114  utc_set = true;
115  utc_locked = false;
116  utc_jump_cnt++;
117  utc_prev_adj = 0;
118  utc_rel_rate_ppm = 0;
119 }
120 
121 static uavcan::uint64_t sampleUtcFromCriticalSection()
122 {
123  UAVCAN_ASSERT(initialized);
124  UAVCAN_ASSERT(getreg32(TMR_REG(KINETIS_PIT_TCTRL_OFFSET)) & PIT_TCTRL_TIE);
125 
126  volatile uavcan::uint64_t time = time_utc;
127  volatile uavcan::uint32_t cnt = CountsPerPeriod - getreg32(TMR_REG(KINETIS_PIT_CVAL_OFFSET));
128 
129  if (getreg32(TMR_REG(KINETIS_PIT_TFLG_OFFSET)) & PIT_TFLG_TIF) {
130  cnt = CountsPerPeriod - getreg32(TMR_REG(KINETIS_PIT_CVAL_OFFSET));
131  const uavcan::int32_t add = uavcan::int32_t(USecPerOverflow) +
132  (utc_accumulated_correction_nsec + utc_correction_nsec_per_overflow) / 1000;
133  time = uavcan::uint64_t(uavcan::int64_t(time) + add);
134  }
135 
136  return time + (cnt / CountsPerUs);
137 }
138 
139 uavcan::uint64_t getUtcUSecFromCanInterrupt()
140 {
141  return utc_set ? sampleUtcFromCriticalSection() : 0;
142 }
143 
144 uavcan::MonotonicTime getMonotonic()
145 {
146  uavcan::uint64_t usec = 0;
147  // Scope Critical section
148  {
149  CriticalSectionLocker locker;
150 
151  volatile uavcan::uint64_t time = time_mono;
152  volatile uavcan::uint32_t cnt = CountsPerPeriod - getreg32(TMR_REG(KINETIS_PIT_CVAL_OFFSET));
153 
154  if (getreg32(TMR_REG(KINETIS_PIT_TFLG_OFFSET)) & PIT_TFLG_TIF) {
155  cnt = CountsPerPeriod - getreg32(TMR_REG(KINETIS_PIT_CVAL_OFFSET));
156  time += USecPerOverflow;
157  }
158 
159  usec = time + (cnt / CountsPerUs);
160 
161  } // End Scope Critical section
162 
163  return uavcan::MonotonicTime::fromUSec(usec);
164 }
165 
166 
167 uavcan::UtcTime getUtc()
168 {
169  if (utc_set) {
170  uavcan::uint64_t usec = 0;
171  {
172  CriticalSectionLocker locker;
173  usec = sampleUtcFromCriticalSection();
174  }
175  return uavcan::UtcTime::fromUSec(usec);
176  }
177 
178  return uavcan::UtcTime();
179 }
180 
181 static float lowpass(float xold, float xnew, float corner, float dt)
182 {
183  const float tau = 1.F / corner;
184  return (dt * xnew + tau * xold) / (dt + tau);
185 }
186 
187 static void updateRatePID(uavcan::UtcDuration adjustment)
188 {
189  const uavcan::MonotonicTime ts = getMonotonic();
190  const float dt = float((ts - prev_utc_adj_at).toUSec()) / 1e6F;
191  prev_utc_adj_at = ts;
192  const float adj_usec = float(adjustment.toUSec());
193 
194  /*
195  * Target relative rate in PPM
196  * Positive to go faster
197  */
198  const float target_rel_rate_ppm = adj_usec * utc_sync_params.offset_p;
199 
200  /*
201  * Current relative rate in PPM
202  * Positive if the local clock is faster
203  */
204  const float new_rel_rate_ppm = (utc_prev_adj - adj_usec) / dt; // rate error in [usec/sec], which is PPM
205  utc_prev_adj = adj_usec;
206  utc_rel_rate_ppm = lowpass(utc_rel_rate_ppm, new_rel_rate_ppm, utc_sync_params.rate_error_corner_freq, dt);
207 
208  const float rel_rate_error = target_rel_rate_ppm - utc_rel_rate_ppm;
209 
210  if (dt > 10) {
211  utc_rel_rate_error_integral = 0;
212 
213  } else {
214  utc_rel_rate_error_integral += rel_rate_error * dt * utc_sync_params.rate_i;
215  utc_rel_rate_error_integral =
216  uavcan::max(utc_rel_rate_error_integral, -utc_sync_params.max_rate_correction_ppm);
217  utc_rel_rate_error_integral =
218  uavcan::min(utc_rel_rate_error_integral, utc_sync_params.max_rate_correction_ppm);
219  }
220 
221  /*
222  * Rate controller
223  */
224  float total_rate_correction_ppm = rel_rate_error + utc_rel_rate_error_integral;
225  total_rate_correction_ppm = uavcan::max(total_rate_correction_ppm, -utc_sync_params.max_rate_correction_ppm);
226  total_rate_correction_ppm = uavcan::min(total_rate_correction_ppm, utc_sync_params.max_rate_correction_ppm);
227 
228  utc_correction_nsec_per_overflow = uavcan::int32_t((USecPerOverflow * 1000) * (total_rate_correction_ppm / 1e6F));
229 
230 // syslog("$ adj=%f rel_rate=%f rel_rate_eint=%f tgt_rel_rate=%f ppm=%f\n",
231 // adj_usec, utc_rel_rate_ppm, utc_rel_rate_error_integral, target_rel_rate_ppm,
232 // total_rate_correction_ppm);
233 }
234 
235 void adjustUtc(uavcan::UtcDuration adjustment)
236 {
237  MutexLocker mlocker(mutex);
238  UAVCAN_ASSERT(initialized);
239 
240  if (adjustment.getAbs() > utc_sync_params.min_jump || !utc_set) {
241  const uavcan::int64_t adj_usec = adjustment.toUSec();
242 
243  {
244  CriticalSectionLocker locker;
245 
246  if ((adj_usec < 0) && uavcan::uint64_t(-adj_usec) > time_utc) {
247  time_utc = 1;
248 
249  } else {
250  time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + adj_usec);
251  }
252  }
253 
254  utc_set = true;
255  utc_locked = false;
256  utc_jump_cnt++;
257  utc_prev_adj = 0;
258  utc_rel_rate_ppm = 0;
259 
260  } else {
261  updateRatePID(adjustment);
262 
263  if (!utc_locked) {
264  utc_locked =
265  (std::abs(utc_rel_rate_ppm) < utc_sync_params.lock_thres_rate_ppm) &&
266  (std::abs(utc_prev_adj) < float(utc_sync_params.lock_thres_offset.toUSec()));
267  }
268  }
269 }
270 
272 {
273  MutexLocker mlocker(mutex);
274  const float rate_correction_mult = float(utc_correction_nsec_per_overflow) / float(USecPerOverflow * 1000);
275  return 1e6F * rate_correction_mult;
276 }
277 
278 uavcan::uint32_t getUtcJumpCount()
279 {
280  MutexLocker mlocker(mutex);
281  return utc_jump_cnt;
282 }
283 
284 bool isUtcLocked()
285 {
286  MutexLocker mlocker(mutex);
287  return utc_locked;
288 }
289 
290 UtcSyncParams getUtcSyncParams()
291 {
292  MutexLocker mlocker(mutex);
293  return utc_sync_params;
294 }
295 
296 void setUtcSyncParams(const UtcSyncParams &params)
297 {
298  MutexLocker mlocker(mutex);
299  // Add some sanity check
300  utc_sync_params = params;
301 }
302 
303 } // namespace clock
304 
305 SystemClock &SystemClock::instance()
306 {
307  static union SystemClockStorage {
308  uavcan::uint8_t buffer[sizeof(SystemClock)];
309  long long _aligner_1;
310  long double _aligner_2;
311  } storage;
312 
313  SystemClock *const ptr = reinterpret_cast<SystemClock *>(storage.buffer);
314 
315  if (!clock::initialized) {
316  MutexLocker mlocker(clock::mutex);
317  clock::init();
318  new (ptr)SystemClock();
319  }
320 
321  return *ptr;
322 }
323 
324 } // namespace uavcan_kinetis
325 
326 
327 /**
328  * Timer interrupt handler
329  */
330 
331 extern "C"
332 UAVCAN_KINETIS_IRQ_HANDLER(TIMX_IRQHandler)
333 {
334  putreg32(PIT_TFLG_TIF, TMR_REG(KINETIS_PIT_TFLG_OFFSET));
335 
336  using namespace uavcan_kinetis::clock;
337  UAVCAN_ASSERT(initialized);
338 
339  time_mono += USecPerOverflow;
340 
341  if (utc_set) {
342  time_utc += USecPerOverflow;
343  utc_accumulated_correction_nsec += utc_correction_nsec_per_overflow;
344 
345  if (std::abs(utc_accumulated_correction_nsec) >= 1000) {
346  time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + utc_accumulated_correction_nsec / 1000);
347  utc_accumulated_correction_nsec %= 1000;
348  }
349 
350  // Correction decay - 1 nsec per 65536 usec
351  if (utc_correction_nsec_per_overflow > 0) {
352  utc_correction_nsec_per_overflow--;
353 
354  } else if (utc_correction_nsec_per_overflow < 0) {
355  utc_correction_nsec_per_overflow++;
356 
357  } else {
358  ; // Zero
359  }
360  }
361 
362  return 0;
363 }
364 
365 #endif
void setUtcSyncParams(const UtcSyncParams &params)
UtcSyncParams getUtcSyncParams()
UTC sync params get/set.
void init()
Starts the clock.
float getUtcRateCorrectionPPM()
Clock rate error.
bool isUtcLocked()
Whether UTC is synchronized and locked.
uavcan::UtcTime getUtc()
Returns UTC time if it has been set, otherwise returns zero time.
uavcan::MonotonicTime getMonotonic()
Returns current monotonic time since the moment when clock::init() was called.
uavcan::uint64_t getUtcUSecFromCanInterrupt()
static SystemClock & instance()
Calls clock::init() as needed.
#define UAVCAN_KINETIS_IRQ_HANDLER(id)
IRQ handler macros.
Definition: internal.hpp:35
constexpr _Tp min(_Tp a, _Tp b)
Definition: Limits.hpp:54
uavcan::uint32_t getUtcJumpCount()
Number of non-gradual adjustments performed so far.
constexpr _Tp max(_Tp a, _Tp b)
Definition: Limits.hpp:60
float dt
Definition: px4io.c:73
void setUtc(uavcan::UtcTime time)
Sets the driver&#39;s notion of the system UTC.
Dual< Scalar, N > abs(const Dual< Scalar, N > &a)
Definition: Dual.hpp:196
void adjustUtc(uavcan::UtcDuration adjustment)
Performs UTC phase and frequency adjustment.