2018-11-16 05:11:26 -04:00
|
|
|
/*
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2014 Pavel Kirienko
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
|
|
* the Software without restriction, including without limitation the rights to
|
|
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
* subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
* copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This file is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Modified for Ardupilot by Siddharth Bharat Purohit
|
|
|
|
*/
|
|
|
|
|
2019-01-19 01:27:52 -04:00
|
|
|
#include "AP_HAL_ChibiOS.h"
|
|
|
|
|
|
|
|
#if HAL_WITH_UAVCAN
|
2018-11-16 05:11:26 -04:00
|
|
|
#include "CANClock.h"
|
|
|
|
#include "CANThread.h"
|
|
|
|
#include "CANInternal.h"
|
|
|
|
|
|
|
|
#ifndef UAVCAN_STM32_TIMER_NUMBER
|
|
|
|
#define UAVCAN_STM32_TIMER_NUMBER 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if UAVCAN_STM32_TIMER_NUMBER
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Timer instance
|
|
|
|
*/
|
|
|
|
# if (CH_KERNEL_MAJOR == 2)
|
|
|
|
# define TIMX UAVCAN_STM32_GLUE2(TIM, UAVCAN_STM32_TIMER_NUMBER)
|
|
|
|
# define TIMX_IRQn UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQn)
|
|
|
|
# define TIMX_INPUT_CLOCK STM32_TIMCLK1
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# if (CH_KERNEL_MAJOR == 3 || CH_KERNEL_MAJOR == 4)
|
|
|
|
# define TIMX UAVCAN_STM32_GLUE2(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER)
|
|
|
|
# define TIMX_IRQn UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _NUMBER)
|
|
|
|
# define TIMX_IRQHandler UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _HANDLER)
|
|
|
|
# define TIMX_INPUT_CLOCK STM32_TIMCLK1
|
|
|
|
# else
|
|
|
|
# define TIMX_IRQHandler UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQHandler)
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# if UAVCAN_STM32_TIMER_NUMBER >= 2 && UAVCAN_STM32_TIMER_NUMBER <= 7
|
|
|
|
# define TIMX_RCC_ENR RCC->APB1ENR
|
|
|
|
# define TIMX_RCC_RSTR RCC->APB1RSTR
|
|
|
|
# define TIMX_RCC_ENR_MASK UAVCAN_STM32_GLUE3(RCC_APB1ENR_TIM, UAVCAN_STM32_TIMER_NUMBER, EN)
|
|
|
|
# define TIMX_RCC_RSTR_MASK UAVCAN_STM32_GLUE3(RCC_APB1RSTR_TIM, UAVCAN_STM32_TIMER_NUMBER, RST)
|
|
|
|
# else
|
|
|
|
# error "This UAVCAN_STM32_TIMER_NUMBER is not supported yet"
|
|
|
|
# endif
|
|
|
|
|
|
|
|
# if (TIMX_INPUT_CLOCK % 1000000) != 0
|
|
|
|
# error "No way, timer clock must be divisible to 1e6. FIXME!"
|
|
|
|
# endif
|
|
|
|
|
|
|
|
extern "C" UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler);
|
|
|
|
|
|
|
|
namespace ChibiOS_CAN {
|
|
|
|
namespace clock {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
const uavcan::uint32_t USecPerOverflow = 65536;
|
|
|
|
|
|
|
|
Mutex mutex;
|
|
|
|
|
|
|
|
bool initialized = false;
|
|
|
|
|
|
|
|
bool utc_set = false;
|
|
|
|
bool utc_locked = false;
|
|
|
|
uavcan::uint32_t utc_jump_cnt = 0;
|
|
|
|
UtcSyncParams utc_sync_params;
|
|
|
|
float utc_prev_adj = 0;
|
|
|
|
float utc_rel_rate_ppm = 0;
|
|
|
|
float utc_rel_rate_error_integral = 0;
|
|
|
|
uavcan::int32_t utc_accumulated_correction_nsec = 0;
|
|
|
|
uavcan::int32_t utc_correction_nsec_per_overflow = 0;
|
|
|
|
uavcan::MonotonicTime prev_utc_adj_at;
|
|
|
|
|
|
|
|
uavcan::uint64_t time_mono = 0;
|
|
|
|
uavcan::uint64_t time_utc = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void init()
|
|
|
|
{
|
|
|
|
CriticalSectionLocker lock;
|
|
|
|
if (initialized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Power-on and reset
|
|
|
|
TIMX_RCC_ENR |= TIMX_RCC_ENR_MASK;
|
|
|
|
TIMX_RCC_RSTR |= TIMX_RCC_RSTR_MASK;
|
|
|
|
TIMX_RCC_RSTR &= ~TIMX_RCC_RSTR_MASK;
|
|
|
|
|
|
|
|
// Enable IRQ
|
|
|
|
nvicEnableVector(TIMX_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK);
|
|
|
|
|
|
|
|
# if (TIMX_INPUT_CLOCK % 1000000) != 0
|
|
|
|
# error "No way, timer clock must be divisible to 1e6. FIXME!"
|
|
|
|
# endif
|
|
|
|
|
|
|
|
// Start the timer
|
|
|
|
TIMX->ARR = 0xFFFF;
|
|
|
|
TIMX->PSC = (TIMX_INPUT_CLOCK / 1000000) - 1; // 1 tick == 1 microsecond
|
|
|
|
TIMX->CR1 = TIM_CR1_URS;
|
|
|
|
TIMX->SR = 0;
|
|
|
|
TIMX->EGR = TIM_EGR_UG; // Reload immediately
|
|
|
|
TIMX->DIER = TIM_DIER_UIE;
|
|
|
|
TIMX->CR1 = TIM_CR1_CEN; // Start
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUtc(uavcan::UtcTime time)
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
UAVCAN_ASSERT(initialized);
|
|
|
|
|
|
|
|
{
|
|
|
|
CriticalSectionLocker locker;
|
|
|
|
time_utc = time.toUSec();
|
|
|
|
}
|
|
|
|
|
|
|
|
utc_set = true;
|
|
|
|
utc_locked = false;
|
|
|
|
utc_jump_cnt++;
|
|
|
|
utc_prev_adj = 0;
|
|
|
|
utc_rel_rate_ppm = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uavcan::uint64_t sampleUtcFromCriticalSection()
|
|
|
|
{
|
|
|
|
UAVCAN_ASSERT(initialized);
|
|
|
|
UAVCAN_ASSERT(TIMX->DIER & TIM_DIER_UIE);
|
|
|
|
|
|
|
|
volatile uavcan::uint64_t time = time_utc;
|
|
|
|
volatile uavcan::uint32_t cnt = TIMX->CNT;
|
|
|
|
|
|
|
|
if (TIMX->SR & TIM_SR_UIF) {
|
|
|
|
cnt = TIMX->CNT;
|
|
|
|
const uavcan::int32_t add = uavcan::int32_t(USecPerOverflow) +
|
|
|
|
(utc_accumulated_correction_nsec + utc_correction_nsec_per_overflow) / 1000;
|
|
|
|
time = uavcan::uint64_t(uavcan::int64_t(time) + add);
|
|
|
|
}
|
|
|
|
return time + cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
uavcan::uint64_t getUtcUSecFromCanInterrupt()
|
|
|
|
{
|
|
|
|
return utc_set ? sampleUtcFromCriticalSection() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uavcan::MonotonicTime getMonotonic()
|
|
|
|
{
|
|
|
|
uavcan::uint64_t usec = 0;
|
|
|
|
// Scope Critical section
|
|
|
|
{
|
|
|
|
CriticalSectionLocker locker;
|
|
|
|
|
|
|
|
volatile uavcan::uint64_t time = time_mono;
|
|
|
|
|
|
|
|
|
|
|
|
volatile uavcan::uint32_t cnt = TIMX->CNT;
|
|
|
|
if (TIMX->SR & TIM_SR_UIF) {
|
|
|
|
cnt = TIMX->CNT;
|
|
|
|
time += USecPerOverflow;
|
|
|
|
}
|
|
|
|
usec = time + cnt;
|
|
|
|
|
|
|
|
# ifndef NDEBUG
|
|
|
|
static uavcan::uint64_t prev_usec = 0; // Self-test
|
|
|
|
UAVCAN_ASSERT(prev_usec <= usec);
|
|
|
|
(void)prev_usec;
|
|
|
|
prev_usec = usec;
|
|
|
|
# endif
|
|
|
|
} // End Scope Critical section
|
|
|
|
|
|
|
|
return uavcan::MonotonicTime::fromUSec(usec);
|
|
|
|
}
|
|
|
|
|
|
|
|
uavcan::UtcTime getUtc()
|
|
|
|
{
|
|
|
|
if (utc_set) {
|
|
|
|
uavcan::uint64_t usec = 0;
|
|
|
|
{
|
|
|
|
CriticalSectionLocker locker;
|
|
|
|
usec = sampleUtcFromCriticalSection();
|
|
|
|
}
|
|
|
|
return uavcan::UtcTime::fromUSec(usec);
|
|
|
|
}
|
|
|
|
return uavcan::UtcTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
static float lowpass(float xold, float xnew, float corner, float dt)
|
|
|
|
{
|
|
|
|
const float tau = 1.F / corner;
|
|
|
|
return (dt * xnew + tau * xold) / (dt + tau);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void updateRatePID(uavcan::UtcDuration adjustment)
|
|
|
|
{
|
|
|
|
const uavcan::MonotonicTime ts = getMonotonic();
|
|
|
|
const float dt = float((ts - prev_utc_adj_at).toUSec()) / 1e6F;
|
|
|
|
prev_utc_adj_at = ts;
|
|
|
|
const float adj_usec = float(adjustment.toUSec());
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Target relative rate in PPM
|
|
|
|
* Positive to go faster
|
|
|
|
*/
|
|
|
|
const float target_rel_rate_ppm = adj_usec * utc_sync_params.offset_p;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Current relative rate in PPM
|
|
|
|
* Positive if the local clock is faster
|
|
|
|
*/
|
|
|
|
const float new_rel_rate_ppm = (utc_prev_adj - adj_usec) / dt; // rate error in [usec/sec], which is PPM
|
|
|
|
utc_prev_adj = adj_usec;
|
|
|
|
utc_rel_rate_ppm = lowpass(utc_rel_rate_ppm, new_rel_rate_ppm, utc_sync_params.rate_error_corner_freq, dt);
|
|
|
|
|
|
|
|
const float rel_rate_error = target_rel_rate_ppm - utc_rel_rate_ppm;
|
|
|
|
|
|
|
|
if (dt > 10) {
|
|
|
|
utc_rel_rate_error_integral = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
utc_rel_rate_error_integral += rel_rate_error * dt * utc_sync_params.rate_i;
|
|
|
|
utc_rel_rate_error_integral =
|
|
|
|
uavcan::max(utc_rel_rate_error_integral, -utc_sync_params.max_rate_correction_ppm);
|
|
|
|
utc_rel_rate_error_integral =
|
|
|
|
uavcan::min(utc_rel_rate_error_integral, utc_sync_params.max_rate_correction_ppm);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Rate controller
|
|
|
|
*/
|
|
|
|
float total_rate_correction_ppm = rel_rate_error + utc_rel_rate_error_integral;
|
|
|
|
total_rate_correction_ppm = uavcan::max(total_rate_correction_ppm, -utc_sync_params.max_rate_correction_ppm);
|
|
|
|
total_rate_correction_ppm = uavcan::min(total_rate_correction_ppm, utc_sync_params.max_rate_correction_ppm);
|
|
|
|
|
|
|
|
utc_correction_nsec_per_overflow = uavcan::int32_t((USecPerOverflow * 1000) * (total_rate_correction_ppm / 1e6F));
|
|
|
|
|
|
|
|
// syslog("$ adj=%f rel_rate=%f rel_rate_eint=%f tgt_rel_rate=%f ppm=%f\n",
|
|
|
|
// adj_usec, utc_rel_rate_ppm, utc_rel_rate_error_integral, target_rel_rate_ppm,
|
|
|
|
// total_rate_correction_ppm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void adjustUtc(uavcan::UtcDuration adjustment)
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
UAVCAN_ASSERT(initialized);
|
|
|
|
|
|
|
|
if (adjustment.getAbs() > utc_sync_params.min_jump || !utc_set) {
|
|
|
|
const uavcan::int64_t adj_usec = adjustment.toUSec();
|
|
|
|
|
|
|
|
{
|
|
|
|
CriticalSectionLocker locker;
|
|
|
|
if ((adj_usec < 0) && uavcan::uint64_t(-adj_usec) > time_utc) {
|
|
|
|
time_utc = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + adj_usec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
utc_set = true;
|
|
|
|
utc_locked = false;
|
|
|
|
utc_jump_cnt++;
|
|
|
|
utc_prev_adj = 0;
|
|
|
|
utc_rel_rate_ppm = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
updateRatePID(adjustment);
|
|
|
|
|
|
|
|
if (!utc_locked) {
|
|
|
|
utc_locked =
|
|
|
|
(std::abs(utc_rel_rate_ppm) < utc_sync_params.lock_thres_rate_ppm) &&
|
|
|
|
(std::abs(utc_prev_adj) < utc_sync_params.lock_thres_offset.toUSec());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float getUtcRateCorrectionPPM()
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
const float rate_correction_mult = float(utc_correction_nsec_per_overflow) / float(USecPerOverflow * 1000);
|
|
|
|
return 1e6F * rate_correction_mult;
|
|
|
|
}
|
|
|
|
|
|
|
|
uavcan::uint32_t getUtcJumpCount()
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
return utc_jump_cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isUtcLocked()
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
return utc_locked;
|
|
|
|
}
|
|
|
|
|
|
|
|
UtcSyncParams getUtcSyncParams()
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
return utc_sync_params;
|
|
|
|
}
|
|
|
|
|
|
|
|
void setUtcSyncParams(const UtcSyncParams& params)
|
|
|
|
{
|
|
|
|
MutexLocker mlocker(mutex);
|
|
|
|
// Add some sanity check
|
|
|
|
utc_sync_params = params;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace clock
|
|
|
|
|
|
|
|
SystemClock& SystemClock::instance()
|
|
|
|
{
|
|
|
|
static union SystemClockStorage {
|
|
|
|
uavcan::uint8_t buffer[sizeof(SystemClock)];
|
|
|
|
long long _aligner_1;
|
|
|
|
long double _aligner_2;
|
|
|
|
} storage;
|
|
|
|
|
|
|
|
SystemClock* const ptr = reinterpret_cast<SystemClock*>(storage.buffer);
|
|
|
|
|
|
|
|
if (!clock::initialized) {
|
|
|
|
MutexLocker mlocker(clock::mutex);
|
|
|
|
clock::init();
|
|
|
|
new (ptr)SystemClock();
|
|
|
|
}
|
|
|
|
return *ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace uavcan_stm32
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timer interrupt handler
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler)
|
|
|
|
{
|
|
|
|
UAVCAN_STM32_IRQ_PROLOGUE();
|
|
|
|
|
|
|
|
TIMX->SR = 0;
|
|
|
|
|
|
|
|
using namespace uavcan_stm32::clock;
|
|
|
|
UAVCAN_ASSERT(initialized);
|
|
|
|
|
|
|
|
time_mono += USecPerOverflow;
|
|
|
|
|
|
|
|
if (utc_set) {
|
|
|
|
time_utc += USecPerOverflow;
|
|
|
|
utc_accumulated_correction_nsec += utc_correction_nsec_per_overflow;
|
|
|
|
if (std::abs(utc_accumulated_correction_nsec) >= 1000) {
|
|
|
|
time_utc = uavcan::uint64_t(uavcan::int64_t(time_utc) + utc_accumulated_correction_nsec / 1000);
|
|
|
|
utc_accumulated_correction_nsec %= 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Correction decay - 1 nsec per 65536 usec
|
|
|
|
if (utc_correction_nsec_per_overflow > 0) {
|
|
|
|
utc_correction_nsec_per_overflow--;
|
|
|
|
}
|
|
|
|
else if (utc_correction_nsec_per_overflow < 0) {
|
|
|
|
utc_correction_nsec_per_overflow++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
; // Zero
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UAVCAN_STM32_IRQ_EPILOGUE();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2019-01-19 01:27:52 -04:00
|
|
|
#endif //HAL_WITH_UAVCAN
|