Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Committer:
RuslanUrya
Date:
Sat Apr 14 10:25:32 2018 +0000
Revision:
0:dfe6edabb8ec
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
RuslanUrya 0:dfe6edabb8ec 1 /*
RuslanUrya 0:dfe6edabb8ec 2 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
RuslanUrya 0:dfe6edabb8ec 3 */
RuslanUrya 0:dfe6edabb8ec 4
RuslanUrya 0:dfe6edabb8ec 5 #include <uavcan_stm32/clock.hpp>
RuslanUrya 0:dfe6edabb8ec 6 #include <uavcan_stm32/thread.hpp>
RuslanUrya 0:dfe6edabb8ec 7 #include "internal.hpp"
RuslanUrya 0:dfe6edabb8ec 8
RuslanUrya 0:dfe6edabb8ec 9 #if UAVCAN_STM32_TIMER_NUMBER
RuslanUrya 0:dfe6edabb8ec 10
RuslanUrya 0:dfe6edabb8ec 11 #include <cassert>
RuslanUrya 0:dfe6edabb8ec 12 #include <cmath>
RuslanUrya 0:dfe6edabb8ec 13
RuslanUrya 0:dfe6edabb8ec 14 /*
RuslanUrya 0:dfe6edabb8ec 15 * Timer instance
RuslanUrya 0:dfe6edabb8ec 16 */
RuslanUrya 0:dfe6edabb8ec 17 # if (UAVCAN_STM32_CHIBIOS && CH_KERNEL_MAJOR == 2) || UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 18 # define TIMX UAVCAN_STM32_GLUE2(TIM, UAVCAN_STM32_TIMER_NUMBER)
RuslanUrya 0:dfe6edabb8ec 19 # define TIMX_IRQn UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQn)
RuslanUrya 0:dfe6edabb8ec 20 # define TIMX_INPUT_CLOCK STM32_TIMCLK1
RuslanUrya 0:dfe6edabb8ec 21 # endif
RuslanUrya 0:dfe6edabb8ec 22
RuslanUrya 0:dfe6edabb8ec 23 # if (UAVCAN_STM32_CHIBIOS && (CH_KERNEL_MAJOR == 3 || CH_KERNEL_MAJOR == 4 || CH_KERNEL_MAJOR == 5))
RuslanUrya 0:dfe6edabb8ec 24 # define TIMX UAVCAN_STM32_GLUE2(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER)
RuslanUrya 0:dfe6edabb8ec 25 # define TIMX_IRQn UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _NUMBER)
RuslanUrya 0:dfe6edabb8ec 26 # define TIMX_IRQHandler UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _HANDLER)
RuslanUrya 0:dfe6edabb8ec 27 # define TIMX_INPUT_CLOCK STM32_TIMCLK1
RuslanUrya 0:dfe6edabb8ec 28 # else
RuslanUrya 0:dfe6edabb8ec 29 # define TIMX_IRQHandler UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQHandler)
RuslanUrya 0:dfe6edabb8ec 30 # endif
RuslanUrya 0:dfe6edabb8ec 31
RuslanUrya 0:dfe6edabb8ec 32 # if UAVCAN_STM32_NUTTX
RuslanUrya 0:dfe6edabb8ec 33 # define TIMX UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _BASE)
RuslanUrya 0:dfe6edabb8ec 34 # define TMR_REG(o) (TIMX + (o))
RuslanUrya 0:dfe6edabb8ec 35 # define TIMX_INPUT_CLOCK UAVCAN_STM32_GLUE3(STM32_APB1_TIM, UAVCAN_STM32_TIMER_NUMBER, _CLKIN)
RuslanUrya 0:dfe6edabb8ec 36
RuslanUrya 0:dfe6edabb8ec 37 # define TIMX_IRQn UAVCAN_STM32_GLUE2(STM32_IRQ_TIM, UAVCAN_STM32_TIMER_NUMBER)
RuslanUrya 0:dfe6edabb8ec 38 # endif
RuslanUrya 0:dfe6edabb8ec 39
RuslanUrya 0:dfe6edabb8ec 40 # if UAVCAN_STM32_TIMER_NUMBER >= 2 && UAVCAN_STM32_TIMER_NUMBER <= 7
RuslanUrya 0:dfe6edabb8ec 41 # define TIMX_RCC_ENR RCC->APB1ENR
RuslanUrya 0:dfe6edabb8ec 42 # define TIMX_RCC_RSTR RCC->APB1RSTR
RuslanUrya 0:dfe6edabb8ec 43 # define TIMX_RCC_ENR_MASK UAVCAN_STM32_GLUE3(RCC_APB1ENR_TIM, UAVCAN_STM32_TIMER_NUMBER, EN)
RuslanUrya 0:dfe6edabb8ec 44 # define TIMX_RCC_RSTR_MASK UAVCAN_STM32_GLUE3(RCC_APB1RSTR_TIM, UAVCAN_STM32_TIMER_NUMBER, RST)
RuslanUrya 0:dfe6edabb8ec 45 # else
RuslanUrya 0:dfe6edabb8ec 46 # error "This UAVCAN_STM32_TIMER_NUMBER is not supported yet"
RuslanUrya 0:dfe6edabb8ec 47 # endif
RuslanUrya 0:dfe6edabb8ec 48
RuslanUrya 0:dfe6edabb8ec 49 /**
RuslanUrya 0:dfe6edabb8ec 50 * UAVCAN_STM32_TIMX_INPUT_CLOCK can be used to manually override the auto-detected timer clock speed.
RuslanUrya 0:dfe6edabb8ec 51 * This is useful at least with certain versions of ChibiOS which do not support the bit
RuslanUrya 0:dfe6edabb8ec 52 * RCC_DKCFGR.TIMPRE that is available in newer models of STM32. In that case, if TIMPRE is active,
RuslanUrya 0:dfe6edabb8ec 53 * the auto-detected value of TIMX_INPUT_CLOCK will be twice lower than the actual clock speed.
RuslanUrya 0:dfe6edabb8ec 54 * Read this for additional context: http://www.chibios.com/forum/viewtopic.php?f=35&t=3870
RuslanUrya 0:dfe6edabb8ec 55 * A normal way to use the override feature is to provide an alternative macro, e.g.:
RuslanUrya 0:dfe6edabb8ec 56 *
RuslanUrya 0:dfe6edabb8ec 57 * -DUAVCAN_STM32_TIMX_INPUT_CLOCK=STM32_HCLK
RuslanUrya 0:dfe6edabb8ec 58 *
RuslanUrya 0:dfe6edabb8ec 59 * Alternatively, the new clock rate can be specified directly.
RuslanUrya 0:dfe6edabb8ec 60 */
RuslanUrya 0:dfe6edabb8ec 61 # ifdef UAVCAN_STM32_TIMX_INPUT_CLOCK
RuslanUrya 0:dfe6edabb8ec 62 # undef TIMX_INPUT_CLOCK
RuslanUrya 0:dfe6edabb8ec 63 # define TIMX_INPUT_CLOCK UAVCAN_STM32_TIMX_INPUT_CLOCK
RuslanUrya 0:dfe6edabb8ec 64 # endif
RuslanUrya 0:dfe6edabb8ec 65
RuslanUrya 0:dfe6edabb8ec 66 extern "C" UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler);
RuslanUrya 0:dfe6edabb8ec 67
RuslanUrya 0:dfe6edabb8ec 68 namespace uavcan_stm32
RuslanUrya 0:dfe6edabb8ec 69 {
RuslanUrya 0:dfe6edabb8ec 70 namespace clock
RuslanUrya 0:dfe6edabb8ec 71 {
RuslanUrya 0:dfe6edabb8ec 72 namespace
RuslanUrya 0:dfe6edabb8ec 73 {
RuslanUrya 0:dfe6edabb8ec 74
RuslanUrya 0:dfe6edabb8ec 75 const uavcan::uint32_t USecPerOverflow = 65536;
RuslanUrya 0:dfe6edabb8ec 76
RuslanUrya 0:dfe6edabb8ec 77 Mutex mutex;
RuslanUrya 0:dfe6edabb8ec 78
RuslanUrya 0:dfe6edabb8ec 79 bool initialized = false;
RuslanUrya 0:dfe6edabb8ec 80
RuslanUrya 0:dfe6edabb8ec 81 bool utc_set = false;
RuslanUrya 0:dfe6edabb8ec 82 bool utc_locked = false;
RuslanUrya 0:dfe6edabb8ec 83 uavcan::uint32_t utc_jump_cnt = 0;
RuslanUrya 0:dfe6edabb8ec 84 UtcSyncParams utc_sync_params;
RuslanUrya 0:dfe6edabb8ec 85 float utc_prev_adj = 0;
RuslanUrya 0:dfe6edabb8ec 86 float utc_rel_rate_ppm = 0;
RuslanUrya 0:dfe6edabb8ec 87 float utc_rel_rate_error_integral = 0;
RuslanUrya 0:dfe6edabb8ec 88 uavcan::int32_t utc_accumulated_correction_nsec = 0;
RuslanUrya 0:dfe6edabb8ec 89 uavcan::int32_t utc_correction_nsec_per_overflow = 0;
RuslanUrya 0:dfe6edabb8ec 90 uavcan::MonotonicTime prev_utc_adj_at;
RuslanUrya 0:dfe6edabb8ec 91
RuslanUrya 0:dfe6edabb8ec 92 uavcan::uint64_t time_mono = 0;
RuslanUrya 0:dfe6edabb8ec 93 uavcan::uint64_t time_utc = 0;
RuslanUrya 0:dfe6edabb8ec 94
RuslanUrya 0:dfe6edabb8ec 95 }
RuslanUrya 0:dfe6edabb8ec 96
RuslanUrya 0:dfe6edabb8ec 97 #if UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 98
RuslanUrya 0:dfe6edabb8ec 99 static void nvicEnableVector(IRQn_Type irq, uint8_t prio)
RuslanUrya 0:dfe6edabb8ec 100 {
RuslanUrya 0:dfe6edabb8ec 101 #if !defined (USE_HAL_DRIVER)
RuslanUrya 0:dfe6edabb8ec 102 NVIC_InitTypeDef NVIC_InitStructure;
RuslanUrya 0:dfe6edabb8ec 103 NVIC_InitStructure.NVIC_IRQChannel = irq;
RuslanUrya 0:dfe6edabb8ec 104 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = prio;
RuslanUrya 0:dfe6edabb8ec 105 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
RuslanUrya 0:dfe6edabb8ec 106 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
RuslanUrya 0:dfe6edabb8ec 107 NVIC_Init(&NVIC_InitStructure);
RuslanUrya 0:dfe6edabb8ec 108 #else
RuslanUrya 0:dfe6edabb8ec 109 HAL_NVIC_SetPriority(irq, prio, 0);
RuslanUrya 0:dfe6edabb8ec 110 HAL_NVIC_EnableIRQ(irq);
RuslanUrya 0:dfe6edabb8ec 111 #endif
RuslanUrya 0:dfe6edabb8ec 112
RuslanUrya 0:dfe6edabb8ec 113 }
RuslanUrya 0:dfe6edabb8ec 114
RuslanUrya 0:dfe6edabb8ec 115 #endif
RuslanUrya 0:dfe6edabb8ec 116
RuslanUrya 0:dfe6edabb8ec 117 void init()
RuslanUrya 0:dfe6edabb8ec 118 {
RuslanUrya 0:dfe6edabb8ec 119 CriticalSectionLocker lock;
RuslanUrya 0:dfe6edabb8ec 120 if (initialized)
RuslanUrya 0:dfe6edabb8ec 121 {
RuslanUrya 0:dfe6edabb8ec 122 return;
RuslanUrya 0:dfe6edabb8ec 123 }
RuslanUrya 0:dfe6edabb8ec 124 initialized = true;
RuslanUrya 0:dfe6edabb8ec 125
RuslanUrya 0:dfe6edabb8ec 126
RuslanUrya 0:dfe6edabb8ec 127 # if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 128 // Power-on and reset
RuslanUrya 0:dfe6edabb8ec 129 TIMX_RCC_ENR |= TIMX_RCC_ENR_MASK;
RuslanUrya 0:dfe6edabb8ec 130 TIMX_RCC_RSTR |= TIMX_RCC_RSTR_MASK;
RuslanUrya 0:dfe6edabb8ec 131 TIMX_RCC_RSTR &= ~TIMX_RCC_RSTR_MASK;
RuslanUrya 0:dfe6edabb8ec 132
RuslanUrya 0:dfe6edabb8ec 133 // Enable IRQ
RuslanUrya 0:dfe6edabb8ec 134 nvicEnableVector(TIMX_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK);
RuslanUrya 0:dfe6edabb8ec 135
RuslanUrya 0:dfe6edabb8ec 136 # if (TIMX_INPUT_CLOCK % 1000000) != 0
RuslanUrya 0:dfe6edabb8ec 137 # error "No way, timer clock must be divisible by 1e6. FIXME!"
RuslanUrya 0:dfe6edabb8ec 138 # endif
RuslanUrya 0:dfe6edabb8ec 139
RuslanUrya 0:dfe6edabb8ec 140 // Start the timer
RuslanUrya 0:dfe6edabb8ec 141 TIMX->ARR = 0xFFFF;
RuslanUrya 0:dfe6edabb8ec 142 TIMX->PSC = (TIMX_INPUT_CLOCK / 1000000) - 1; // 1 tick == 1 microsecond
RuslanUrya 0:dfe6edabb8ec 143 TIMX->CR1 = TIM_CR1_URS;
RuslanUrya 0:dfe6edabb8ec 144 TIMX->SR = 0;
RuslanUrya 0:dfe6edabb8ec 145 TIMX->EGR = TIM_EGR_UG; // Reload immediately
RuslanUrya 0:dfe6edabb8ec 146 TIMX->DIER = TIM_DIER_UIE;
RuslanUrya 0:dfe6edabb8ec 147 TIMX->CR1 = TIM_CR1_CEN; // Start
RuslanUrya 0:dfe6edabb8ec 148
RuslanUrya 0:dfe6edabb8ec 149 # endif
RuslanUrya 0:dfe6edabb8ec 150
RuslanUrya 0:dfe6edabb8ec 151 # if UAVCAN_STM32_NUTTX
RuslanUrya 0:dfe6edabb8ec 152
RuslanUrya 0:dfe6edabb8ec 153 // Attach IRQ
RuslanUrya 0:dfe6edabb8ec 154 irq_attach(TIMX_IRQn, &TIMX_IRQHandler);
RuslanUrya 0:dfe6edabb8ec 155
RuslanUrya 0:dfe6edabb8ec 156 // Power-on and reset
RuslanUrya 0:dfe6edabb8ec 157 modifyreg32(STM32_RCC_APB1ENR, 0, TIMX_RCC_ENR_MASK);
RuslanUrya 0:dfe6edabb8ec 158 modifyreg32(STM32_RCC_APB1RSTR, 0, TIMX_RCC_RSTR_MASK);
RuslanUrya 0:dfe6edabb8ec 159 modifyreg32(STM32_RCC_APB1RSTR, TIMX_RCC_RSTR_MASK, 0);
RuslanUrya 0:dfe6edabb8ec 160
RuslanUrya 0:dfe6edabb8ec 161
RuslanUrya 0:dfe6edabb8ec 162 // Start the timer
RuslanUrya 0:dfe6edabb8ec 163 putreg32(0xFFFF, TMR_REG(STM32_BTIM_ARR_OFFSET));
RuslanUrya 0:dfe6edabb8ec 164 putreg16(((TIMX_INPUT_CLOCK / 1000000)-1), TMR_REG(STM32_BTIM_PSC_OFFSET));
RuslanUrya 0:dfe6edabb8ec 165 putreg16(BTIM_CR1_URS, TMR_REG(STM32_BTIM_CR1_OFFSET));
RuslanUrya 0:dfe6edabb8ec 166 putreg16(0, TMR_REG(STM32_BTIM_SR_OFFSET));
RuslanUrya 0:dfe6edabb8ec 167 putreg16(BTIM_EGR_UG, TMR_REG(STM32_BTIM_EGR_OFFSET)); // Reload immediately
RuslanUrya 0:dfe6edabb8ec 168 putreg16(BTIM_DIER_UIE, TMR_REG(STM32_BTIM_DIER_OFFSET));
RuslanUrya 0:dfe6edabb8ec 169 putreg16(BTIM_CR1_CEN, TMR_REG(STM32_BTIM_CR1_OFFSET)); // Start
RuslanUrya 0:dfe6edabb8ec 170
RuslanUrya 0:dfe6edabb8ec 171 // Prioritize and Enable IRQ
RuslanUrya 0:dfe6edabb8ec 172 // todo: Currently changing the NVIC_SYSH_HIGH_PRIORITY is HARD faulting
RuslanUrya 0:dfe6edabb8ec 173 // need to investigate
RuslanUrya 0:dfe6edabb8ec 174 // up_prioritize_irq(TIMX_IRQn, NVIC_SYSH_HIGH_PRIORITY);
RuslanUrya 0:dfe6edabb8ec 175 up_enable_irq(TIMX_IRQn);
RuslanUrya 0:dfe6edabb8ec 176
RuslanUrya 0:dfe6edabb8ec 177 # endif
RuslanUrya 0:dfe6edabb8ec 178 }
RuslanUrya 0:dfe6edabb8ec 179
RuslanUrya 0:dfe6edabb8ec 180 void setUtc(uavcan::UtcTime time)
RuslanUrya 0:dfe6edabb8ec 181 {
RuslanUrya 0:dfe6edabb8ec 182 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 183 UAVCAN_ASSERT(initialized);
RuslanUrya 0:dfe6edabb8ec 184
RuslanUrya 0:dfe6edabb8ec 185 {
RuslanUrya 0:dfe6edabb8ec 186 CriticalSectionLocker locker;
RuslanUrya 0:dfe6edabb8ec 187 time_utc = time.toUSec();
RuslanUrya 0:dfe6edabb8ec 188 }
RuslanUrya 0:dfe6edabb8ec 189
RuslanUrya 0:dfe6edabb8ec 190 utc_set = true;
RuslanUrya 0:dfe6edabb8ec 191 utc_locked = false;
RuslanUrya 0:dfe6edabb8ec 192 utc_jump_cnt++;
RuslanUrya 0:dfe6edabb8ec 193 utc_prev_adj = 0;
RuslanUrya 0:dfe6edabb8ec 194 utc_rel_rate_ppm = 0;
RuslanUrya 0:dfe6edabb8ec 195 }
RuslanUrya 0:dfe6edabb8ec 196
RuslanUrya 0:dfe6edabb8ec 197 static uavcan::uint64_t sampleUtcFromCriticalSection()
RuslanUrya 0:dfe6edabb8ec 198 {
RuslanUrya 0:dfe6edabb8ec 199 # if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 200 UAVCAN_ASSERT(initialized);
RuslanUrya 0:dfe6edabb8ec 201 UAVCAN_ASSERT(TIMX->DIER & TIM_DIER_UIE);
RuslanUrya 0:dfe6edabb8ec 202
RuslanUrya 0:dfe6edabb8ec 203 volatile uavcan::uint64_t time = time_utc;
RuslanUrya 0:dfe6edabb8ec 204 volatile uavcan::uint32_t cnt = TIMX->CNT;
RuslanUrya 0:dfe6edabb8ec 205
RuslanUrya 0:dfe6edabb8ec 206 if (TIMX->SR & TIM_SR_UIF)
RuslanUrya 0:dfe6edabb8ec 207 {
RuslanUrya 0:dfe6edabb8ec 208 cnt = TIMX->CNT;
RuslanUrya 0:dfe6edabb8ec 209 const uavcan::int32_t add = uavcan::int32_t(USecPerOverflow) +
RuslanUrya 0:dfe6edabb8ec 210 (utc_accumulated_correction_nsec + utc_correction_nsec_per_overflow) / 1000;
RuslanUrya 0:dfe6edabb8ec 211 time = uavcan::uint64_t(uavcan::int64_t(time) + add);
RuslanUrya 0:dfe6edabb8ec 212 }
RuslanUrya 0:dfe6edabb8ec 213 return time + cnt;
RuslanUrya 0:dfe6edabb8ec 214 # endif
RuslanUrya 0:dfe6edabb8ec 215
RuslanUrya 0:dfe6edabb8ec 216 # if UAVCAN_STM32_NUTTX
RuslanUrya 0:dfe6edabb8ec 217
RuslanUrya 0:dfe6edabb8ec 218 UAVCAN_ASSERT(initialized);
RuslanUrya 0:dfe6edabb8ec 219 UAVCAN_ASSERT(getreg16(TMR_REG(STM32_BTIM_DIER_OFFSET)) & BTIM_DIER_UIE);
RuslanUrya 0:dfe6edabb8ec 220
RuslanUrya 0:dfe6edabb8ec 221 volatile uavcan::uint64_t time = time_utc;
RuslanUrya 0:dfe6edabb8ec 222 volatile uavcan::uint32_t cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
RuslanUrya 0:dfe6edabb8ec 223
RuslanUrya 0:dfe6edabb8ec 224 if (getreg16(TMR_REG(STM32_BTIM_SR_OFFSET)) & BTIM_SR_UIF)
RuslanUrya 0:dfe6edabb8ec 225 {
RuslanUrya 0:dfe6edabb8ec 226 cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
RuslanUrya 0:dfe6edabb8ec 227 const uavcan::int32_t add = uavcan::int32_t(USecPerOverflow) +
RuslanUrya 0:dfe6edabb8ec 228 (utc_accumulated_correction_nsec + utc_correction_nsec_per_overflow) / 1000;
RuslanUrya 0:dfe6edabb8ec 229 time = uavcan::uint64_t(uavcan::int64_t(time) + add);
RuslanUrya 0:dfe6edabb8ec 230 }
RuslanUrya 0:dfe6edabb8ec 231 return time + cnt;
RuslanUrya 0:dfe6edabb8ec 232 # endif
RuslanUrya 0:dfe6edabb8ec 233 }
RuslanUrya 0:dfe6edabb8ec 234
RuslanUrya 0:dfe6edabb8ec 235 uavcan::uint64_t getUtcUSecFromCanInterrupt()
RuslanUrya 0:dfe6edabb8ec 236 {
RuslanUrya 0:dfe6edabb8ec 237 return utc_set ? sampleUtcFromCriticalSection() : 0;
RuslanUrya 0:dfe6edabb8ec 238 }
RuslanUrya 0:dfe6edabb8ec 239
RuslanUrya 0:dfe6edabb8ec 240 uavcan::MonotonicTime getMonotonic()
RuslanUrya 0:dfe6edabb8ec 241 {
RuslanUrya 0:dfe6edabb8ec 242 uavcan::uint64_t usec = 0;
RuslanUrya 0:dfe6edabb8ec 243 // Scope Critical section
RuslanUrya 0:dfe6edabb8ec 244 {
RuslanUrya 0:dfe6edabb8ec 245 CriticalSectionLocker locker;
RuslanUrya 0:dfe6edabb8ec 246
RuslanUrya 0:dfe6edabb8ec 247 volatile uavcan::uint64_t time = time_mono;
RuslanUrya 0:dfe6edabb8ec 248
RuslanUrya 0:dfe6edabb8ec 249 # if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 250
RuslanUrya 0:dfe6edabb8ec 251 volatile uavcan::uint32_t cnt = TIMX->CNT;
RuslanUrya 0:dfe6edabb8ec 252 if (TIMX->SR & TIM_SR_UIF)
RuslanUrya 0:dfe6edabb8ec 253 {
RuslanUrya 0:dfe6edabb8ec 254 cnt = TIMX->CNT;
RuslanUrya 0:dfe6edabb8ec 255 # endif
RuslanUrya 0:dfe6edabb8ec 256
RuslanUrya 0:dfe6edabb8ec 257 # if UAVCAN_STM32_NUTTX
RuslanUrya 0:dfe6edabb8ec 258
RuslanUrya 0:dfe6edabb8ec 259 volatile uavcan::uint32_t cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
RuslanUrya 0:dfe6edabb8ec 260
RuslanUrya 0:dfe6edabb8ec 261 if (getreg16(TMR_REG(STM32_BTIM_SR_OFFSET)) & BTIM_SR_UIF)
RuslanUrya 0:dfe6edabb8ec 262 {
RuslanUrya 0:dfe6edabb8ec 263 cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
RuslanUrya 0:dfe6edabb8ec 264 # endif
RuslanUrya 0:dfe6edabb8ec 265 time += USecPerOverflow;
RuslanUrya 0:dfe6edabb8ec 266 }
RuslanUrya 0:dfe6edabb8ec 267 usec = time + cnt;
RuslanUrya 0:dfe6edabb8ec 268
RuslanUrya 0:dfe6edabb8ec 269 # ifndef NDEBUG
RuslanUrya 0:dfe6edabb8ec 270 static uavcan::uint64_t prev_usec = 0; // Self-test
RuslanUrya 0:dfe6edabb8ec 271 UAVCAN_ASSERT(prev_usec <= usec);
RuslanUrya 0:dfe6edabb8ec 272 (void)prev_usec;
RuslanUrya 0:dfe6edabb8ec 273 prev_usec = usec;
RuslanUrya 0:dfe6edabb8ec 274 # endif
RuslanUrya 0:dfe6edabb8ec 275 } // End Scope Critical section
RuslanUrya 0:dfe6edabb8ec 276
RuslanUrya 0:dfe6edabb8ec 277 return uavcan::MonotonicTime::fromUSec(usec);
RuslanUrya 0:dfe6edabb8ec 278 }
RuslanUrya 0:dfe6edabb8ec 279
RuslanUrya 0:dfe6edabb8ec 280 uavcan::UtcTime getUtc()
RuslanUrya 0:dfe6edabb8ec 281 {
RuslanUrya 0:dfe6edabb8ec 282 if (utc_set)
RuslanUrya 0:dfe6edabb8ec 283 {
RuslanUrya 0:dfe6edabb8ec 284 uavcan::uint64_t usec = 0;
RuslanUrya 0:dfe6edabb8ec 285 {
RuslanUrya 0:dfe6edabb8ec 286 CriticalSectionLocker locker;
RuslanUrya 0:dfe6edabb8ec 287 usec = sampleUtcFromCriticalSection();
RuslanUrya 0:dfe6edabb8ec 288 }
RuslanUrya 0:dfe6edabb8ec 289 return uavcan::UtcTime::fromUSec(usec);
RuslanUrya 0:dfe6edabb8ec 290 }
RuslanUrya 0:dfe6edabb8ec 291 return uavcan::UtcTime();
RuslanUrya 0:dfe6edabb8ec 292 }
RuslanUrya 0:dfe6edabb8ec 293
RuslanUrya 0:dfe6edabb8ec 294 static float lowpass(float xold, float xnew, float corner, float dt)
RuslanUrya 0:dfe6edabb8ec 295 {
RuslanUrya 0:dfe6edabb8ec 296 const float tau = 1.F / corner;
RuslanUrya 0:dfe6edabb8ec 297 return (dt * xnew + tau * xold) / (dt + tau);
RuslanUrya 0:dfe6edabb8ec 298 }
RuslanUrya 0:dfe6edabb8ec 299
RuslanUrya 0:dfe6edabb8ec 300 static void updateRatePID(uavcan::UtcDuration adjustment)
RuslanUrya 0:dfe6edabb8ec 301 {
RuslanUrya 0:dfe6edabb8ec 302 const uavcan::MonotonicTime ts = getMonotonic();
RuslanUrya 0:dfe6edabb8ec 303 const float dt = float((ts - prev_utc_adj_at).toUSec()) / 1e6F;
RuslanUrya 0:dfe6edabb8ec 304 prev_utc_adj_at = ts;
RuslanUrya 0:dfe6edabb8ec 305 const float adj_usec = float(adjustment.toUSec());
RuslanUrya 0:dfe6edabb8ec 306
RuslanUrya 0:dfe6edabb8ec 307 /*
RuslanUrya 0:dfe6edabb8ec 308 * Target relative rate in PPM
RuslanUrya 0:dfe6edabb8ec 309 * Positive to go faster
RuslanUrya 0:dfe6edabb8ec 310 */
RuslanUrya 0:dfe6edabb8ec 311 const float target_rel_rate_ppm = adj_usec * utc_sync_params.offset_p;
RuslanUrya 0:dfe6edabb8ec 312
RuslanUrya 0:dfe6edabb8ec 313 /*
RuslanUrya 0:dfe6edabb8ec 314 * Current relative rate in PPM
RuslanUrya 0:dfe6edabb8ec 315 * Positive if the local clock is faster
RuslanUrya 0:dfe6edabb8ec 316 */
RuslanUrya 0:dfe6edabb8ec 317 const float new_rel_rate_ppm = (utc_prev_adj - adj_usec) / dt; // rate error in [usec/sec], which is PPM
RuslanUrya 0:dfe6edabb8ec 318 utc_prev_adj = adj_usec;
RuslanUrya 0:dfe6edabb8ec 319 utc_rel_rate_ppm = lowpass(utc_rel_rate_ppm, new_rel_rate_ppm, utc_sync_params.rate_error_corner_freq, dt);
RuslanUrya 0:dfe6edabb8ec 320
RuslanUrya 0:dfe6edabb8ec 321 const float rel_rate_error = target_rel_rate_ppm - utc_rel_rate_ppm;
RuslanUrya 0:dfe6edabb8ec 322
RuslanUrya 0:dfe6edabb8ec 323 if (dt > 10)
RuslanUrya 0:dfe6edabb8ec 324 {
RuslanUrya 0:dfe6edabb8ec 325 utc_rel_rate_error_integral = 0;
RuslanUrya 0:dfe6edabb8ec 326 }
RuslanUrya 0:dfe6edabb8ec 327 else
RuslanUrya 0:dfe6edabb8ec 328 {
RuslanUrya 0:dfe6edabb8ec 329 utc_rel_rate_error_integral += rel_rate_error * dt * utc_sync_params.rate_i;
RuslanUrya 0:dfe6edabb8ec 330 utc_rel_rate_error_integral =
RuslanUrya 0:dfe6edabb8ec 331 uavcan::max(utc_rel_rate_error_integral, -utc_sync_params.max_rate_correction_ppm);
RuslanUrya 0:dfe6edabb8ec 332 utc_rel_rate_error_integral =
RuslanUrya 0:dfe6edabb8ec 333 uavcan::min(utc_rel_rate_error_integral, utc_sync_params.max_rate_correction_ppm);
RuslanUrya 0:dfe6edabb8ec 334 }
RuslanUrya 0:dfe6edabb8ec 335
RuslanUrya 0:dfe6edabb8ec 336 /*
RuslanUrya 0:dfe6edabb8ec 337 * Rate controller
RuslanUrya 0:dfe6edabb8ec 338 */
RuslanUrya 0:dfe6edabb8ec 339 float total_rate_correction_ppm = rel_rate_error + utc_rel_rate_error_integral;
RuslanUrya 0:dfe6edabb8ec 340 total_rate_correction_ppm = uavcan::max(total_rate_correction_ppm, -utc_sync_params.max_rate_correction_ppm);
RuslanUrya 0:dfe6edabb8ec 341 total_rate_correction_ppm = uavcan::min(total_rate_correction_ppm, utc_sync_params.max_rate_correction_ppm);
RuslanUrya 0:dfe6edabb8ec 342
RuslanUrya 0:dfe6edabb8ec 343 utc_correction_nsec_per_overflow = uavcan::int32_t((USecPerOverflow * 1000) * (total_rate_correction_ppm / 1e6F));
RuslanUrya 0:dfe6edabb8ec 344
RuslanUrya 0:dfe6edabb8ec 345 // syslog("$ adj=%f rel_rate=%f rel_rate_eint=%f tgt_rel_rate=%f ppm=%f\n",
RuslanUrya 0:dfe6edabb8ec 346 // adj_usec, utc_rel_rate_ppm, utc_rel_rate_error_integral, target_rel_rate_ppm,
RuslanUrya 0:dfe6edabb8ec 347 // total_rate_correction_ppm);
RuslanUrya 0:dfe6edabb8ec 348 }
RuslanUrya 0:dfe6edabb8ec 349
RuslanUrya 0:dfe6edabb8ec 350 void adjustUtc(uavcan::UtcDuration adjustment)
RuslanUrya 0:dfe6edabb8ec 351 {
RuslanUrya 0:dfe6edabb8ec 352 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 353 UAVCAN_ASSERT(initialized);
RuslanUrya 0:dfe6edabb8ec 354
RuslanUrya 0:dfe6edabb8ec 355 if (adjustment.getAbs() > utc_sync_params.min_jump || !utc_set)
RuslanUrya 0:dfe6edabb8ec 356 {
RuslanUrya 0:dfe6edabb8ec 357 const uavcan::int64_t adj_usec = adjustment.toUSec();
RuslanUrya 0:dfe6edabb8ec 358
RuslanUrya 0:dfe6edabb8ec 359 {
RuslanUrya 0:dfe6edabb8ec 360 CriticalSectionLocker locker;
RuslanUrya 0:dfe6edabb8ec 361 if ((adj_usec < 0) && uavcan::uint64_t(-adj_usec) > time_utc)
RuslanUrya 0:dfe6edabb8ec 362 {
RuslanUrya 0:dfe6edabb8ec 363 time_utc = 1;
RuslanUrya 0:dfe6edabb8ec 364 }
RuslanUrya 0:dfe6edabb8ec 365 else
RuslanUrya 0:dfe6edabb8ec 366 {
RuslanUrya 0:dfe6edabb8ec 367 time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + adj_usec);
RuslanUrya 0:dfe6edabb8ec 368 }
RuslanUrya 0:dfe6edabb8ec 369 }
RuslanUrya 0:dfe6edabb8ec 370
RuslanUrya 0:dfe6edabb8ec 371 utc_set = true;
RuslanUrya 0:dfe6edabb8ec 372 utc_locked = false;
RuslanUrya 0:dfe6edabb8ec 373 utc_jump_cnt++;
RuslanUrya 0:dfe6edabb8ec 374 utc_prev_adj = 0;
RuslanUrya 0:dfe6edabb8ec 375 utc_rel_rate_ppm = 0;
RuslanUrya 0:dfe6edabb8ec 376 }
RuslanUrya 0:dfe6edabb8ec 377 else
RuslanUrya 0:dfe6edabb8ec 378 {
RuslanUrya 0:dfe6edabb8ec 379 updateRatePID(adjustment);
RuslanUrya 0:dfe6edabb8ec 380
RuslanUrya 0:dfe6edabb8ec 381 if (!utc_locked)
RuslanUrya 0:dfe6edabb8ec 382 {
RuslanUrya 0:dfe6edabb8ec 383 utc_locked =
RuslanUrya 0:dfe6edabb8ec 384 (std::abs(utc_rel_rate_ppm) < utc_sync_params.lock_thres_rate_ppm) &&
RuslanUrya 0:dfe6edabb8ec 385 (std::abs(utc_prev_adj) < float(utc_sync_params.lock_thres_offset.toUSec()));
RuslanUrya 0:dfe6edabb8ec 386 }
RuslanUrya 0:dfe6edabb8ec 387 }
RuslanUrya 0:dfe6edabb8ec 388 }
RuslanUrya 0:dfe6edabb8ec 389
RuslanUrya 0:dfe6edabb8ec 390 float getUtcRateCorrectionPPM()
RuslanUrya 0:dfe6edabb8ec 391 {
RuslanUrya 0:dfe6edabb8ec 392 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 393 const float rate_correction_mult = float(utc_correction_nsec_per_overflow) / float(USecPerOverflow * 1000);
RuslanUrya 0:dfe6edabb8ec 394 return 1e6F * rate_correction_mult;
RuslanUrya 0:dfe6edabb8ec 395 }
RuslanUrya 0:dfe6edabb8ec 396
RuslanUrya 0:dfe6edabb8ec 397 uavcan::uint32_t getUtcJumpCount()
RuslanUrya 0:dfe6edabb8ec 398 {
RuslanUrya 0:dfe6edabb8ec 399 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 400 return utc_jump_cnt;
RuslanUrya 0:dfe6edabb8ec 401 }
RuslanUrya 0:dfe6edabb8ec 402
RuslanUrya 0:dfe6edabb8ec 403 bool isUtcLocked()
RuslanUrya 0:dfe6edabb8ec 404 {
RuslanUrya 0:dfe6edabb8ec 405 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 406 return utc_locked;
RuslanUrya 0:dfe6edabb8ec 407 }
RuslanUrya 0:dfe6edabb8ec 408
RuslanUrya 0:dfe6edabb8ec 409 UtcSyncParams getUtcSyncParams()
RuslanUrya 0:dfe6edabb8ec 410 {
RuslanUrya 0:dfe6edabb8ec 411 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 412 return utc_sync_params;
RuslanUrya 0:dfe6edabb8ec 413 }
RuslanUrya 0:dfe6edabb8ec 414
RuslanUrya 0:dfe6edabb8ec 415 void setUtcSyncParams(const UtcSyncParams& params)
RuslanUrya 0:dfe6edabb8ec 416 {
RuslanUrya 0:dfe6edabb8ec 417 MutexLocker mlocker(mutex);
RuslanUrya 0:dfe6edabb8ec 418 // Add some sanity check
RuslanUrya 0:dfe6edabb8ec 419 utc_sync_params = params;
RuslanUrya 0:dfe6edabb8ec 420 }
RuslanUrya 0:dfe6edabb8ec 421
RuslanUrya 0:dfe6edabb8ec 422 } // namespace clock
RuslanUrya 0:dfe6edabb8ec 423
RuslanUrya 0:dfe6edabb8ec 424 SystemClock& SystemClock::instance()
RuslanUrya 0:dfe6edabb8ec 425 {
RuslanUrya 0:dfe6edabb8ec 426 static union SystemClockStorage
RuslanUrya 0:dfe6edabb8ec 427 {
RuslanUrya 0:dfe6edabb8ec 428 uavcan::uint8_t buffer[sizeof(SystemClock)];
RuslanUrya 0:dfe6edabb8ec 429 long long _aligner_1;
RuslanUrya 0:dfe6edabb8ec 430 long double _aligner_2;
RuslanUrya 0:dfe6edabb8ec 431 } storage;
RuslanUrya 0:dfe6edabb8ec 432
RuslanUrya 0:dfe6edabb8ec 433 SystemClock* const ptr = reinterpret_cast<SystemClock*>(storage.buffer);
RuslanUrya 0:dfe6edabb8ec 434
RuslanUrya 0:dfe6edabb8ec 435 if (!clock::initialized)
RuslanUrya 0:dfe6edabb8ec 436 {
RuslanUrya 0:dfe6edabb8ec 437 MutexLocker mlocker(clock::mutex);
RuslanUrya 0:dfe6edabb8ec 438 clock::init();
RuslanUrya 0:dfe6edabb8ec 439 new (ptr)SystemClock();
RuslanUrya 0:dfe6edabb8ec 440 }
RuslanUrya 0:dfe6edabb8ec 441 return *ptr;
RuslanUrya 0:dfe6edabb8ec 442 }
RuslanUrya 0:dfe6edabb8ec 443
RuslanUrya 0:dfe6edabb8ec 444 } // namespace uavcan_stm32
RuslanUrya 0:dfe6edabb8ec 445
RuslanUrya 0:dfe6edabb8ec 446
RuslanUrya 0:dfe6edabb8ec 447 /**
RuslanUrya 0:dfe6edabb8ec 448 * Timer interrupt handler
RuslanUrya 0:dfe6edabb8ec 449 */
RuslanUrya 0:dfe6edabb8ec 450
RuslanUrya 0:dfe6edabb8ec 451 extern "C"
RuslanUrya 0:dfe6edabb8ec 452 UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler)
RuslanUrya 0:dfe6edabb8ec 453 {
RuslanUrya 0:dfe6edabb8ec 454 UAVCAN_STM32_IRQ_PROLOGUE();
RuslanUrya 0:dfe6edabb8ec 455
RuslanUrya 0:dfe6edabb8ec 456 # if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL || UAVCAN_STM32_FREERTOS
RuslanUrya 0:dfe6edabb8ec 457 TIMX->SR = 0;
RuslanUrya 0:dfe6edabb8ec 458 # endif
RuslanUrya 0:dfe6edabb8ec 459 # if UAVCAN_STM32_NUTTX
RuslanUrya 0:dfe6edabb8ec 460 putreg16(0, TMR_REG(STM32_BTIM_SR_OFFSET));
RuslanUrya 0:dfe6edabb8ec 461 # endif
RuslanUrya 0:dfe6edabb8ec 462
RuslanUrya 0:dfe6edabb8ec 463 using namespace uavcan_stm32::clock;
RuslanUrya 0:dfe6edabb8ec 464 UAVCAN_ASSERT(initialized);
RuslanUrya 0:dfe6edabb8ec 465
RuslanUrya 0:dfe6edabb8ec 466 time_mono += USecPerOverflow;
RuslanUrya 0:dfe6edabb8ec 467
RuslanUrya 0:dfe6edabb8ec 468 if (utc_set)
RuslanUrya 0:dfe6edabb8ec 469 {
RuslanUrya 0:dfe6edabb8ec 470 time_utc += USecPerOverflow;
RuslanUrya 0:dfe6edabb8ec 471 utc_accumulated_correction_nsec += utc_correction_nsec_per_overflow;
RuslanUrya 0:dfe6edabb8ec 472 if (std::abs(utc_accumulated_correction_nsec) >= 1000)
RuslanUrya 0:dfe6edabb8ec 473 {
RuslanUrya 0:dfe6edabb8ec 474 time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + utc_accumulated_correction_nsec / 1000);
RuslanUrya 0:dfe6edabb8ec 475 utc_accumulated_correction_nsec %= 1000;
RuslanUrya 0:dfe6edabb8ec 476 }
RuslanUrya 0:dfe6edabb8ec 477
RuslanUrya 0:dfe6edabb8ec 478 // Correction decay - 1 nsec per 65536 usec
RuslanUrya 0:dfe6edabb8ec 479 if (utc_correction_nsec_per_overflow > 0)
RuslanUrya 0:dfe6edabb8ec 480 {
RuslanUrya 0:dfe6edabb8ec 481 utc_correction_nsec_per_overflow--;
RuslanUrya 0:dfe6edabb8ec 482 }
RuslanUrya 0:dfe6edabb8ec 483 else if (utc_correction_nsec_per_overflow < 0)
RuslanUrya 0:dfe6edabb8ec 484 {
RuslanUrya 0:dfe6edabb8ec 485 utc_correction_nsec_per_overflow++;
RuslanUrya 0:dfe6edabb8ec 486 }
RuslanUrya 0:dfe6edabb8ec 487 else
RuslanUrya 0:dfe6edabb8ec 488 {
RuslanUrya 0:dfe6edabb8ec 489 ; // Zero
RuslanUrya 0:dfe6edabb8ec 490 }
RuslanUrya 0:dfe6edabb8ec 491 }
RuslanUrya 0:dfe6edabb8ec 492
RuslanUrya 0:dfe6edabb8ec 493 UAVCAN_STM32_IRQ_EPILOGUE();
RuslanUrya 0:dfe6edabb8ec 494 }
RuslanUrya 0:dfe6edabb8ec 495
RuslanUrya 0:dfe6edabb8ec 496 #endif