mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-07-03 06:40:35 +08:00
STM32 clock driver
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uavcan/driver/system_clock.hpp>
|
||||
|
||||
namespace uavcan_stm32
|
||||
{
|
||||
|
||||
namespace clock
|
||||
{
|
||||
/**
|
||||
* Starts the clock.
|
||||
* Can be called multiple times, only the first call will be effective.
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* For CAN timestamping.
|
||||
*/
|
||||
uavcan::uint64_t getUtcUSecFromInterrupt();
|
||||
|
||||
/**
|
||||
* For general usage.
|
||||
*/
|
||||
uavcan::MonotonicTime getMonotonic();
|
||||
uavcan::UtcTime getUtc();
|
||||
|
||||
/**
|
||||
* Performs UTC time adjustment.
|
||||
* The UTC time will be zero until first adjustment has been performed.
|
||||
*/
|
||||
void adjustUtc(uavcan::UtcDuration adjustment);
|
||||
|
||||
/**
|
||||
* Clock speed error.
|
||||
* Positive if the hardware timer is slower than reference time
|
||||
*/
|
||||
uavcan::int32_t getUtcSpeedCorrectionPPM();
|
||||
|
||||
/**
|
||||
* Number of non-gradual adjustments performed so far.
|
||||
* Ideally should be zero.
|
||||
*/
|
||||
uavcan::uint32_t getUtcAjdustmentJumpCount();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clock interface for CAN driver.
|
||||
*/
|
||||
class ICanTimestampingClock : public uavcan::ISystemClock
|
||||
{
|
||||
public:
|
||||
virtual uavcan::uint64_t getUtcUSecFromInterrupt() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Trivial system clock implementation; can be redefined by the application.
|
||||
* Uses a simple 16-bit hardware timer for both UTC and monotonic clocks.
|
||||
*/
|
||||
class SystemClock : public ICanTimestampingClock, uavcan::Noncopyable
|
||||
{
|
||||
SystemClock() { }
|
||||
|
||||
public:
|
||||
static SystemClock& instance();
|
||||
|
||||
virtual uavcan::uint64_t getUtcUSecFromInterrupt() const { return clock::getUtcUSecFromInterrupt(); }
|
||||
|
||||
virtual uavcan::MonotonicTime getMonotonic() const { return clock::getMonotonic(); }
|
||||
virtual uavcan::UtcTime getUtc() const { return clock::getUtc(); }
|
||||
virtual void adjustUtc(uavcan::UtcDuration adjustment) { clock::adjustUtc(adjustment); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -15,11 +15,11 @@
|
||||
namespace uavcan_stm32
|
||||
{
|
||||
|
||||
#if UAVCAN_STM32_CHIBIOS
|
||||
|
||||
class Event
|
||||
{
|
||||
#if UAVCAN_STM32_CHIBIOS
|
||||
chibios_rt::CounterSemaphore sem_;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Event() : sem_(0) { }
|
||||
@@ -31,6 +31,4 @@ public:
|
||||
void signalFromInterrupt();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <uavcan_stm32/can.hpp>
|
||||
#include <uavcan_stm32/thread.hpp>
|
||||
#include <uavcan_stm32/clock.hpp>
|
||||
#include <uavcan_stm32/can.hpp>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if UAVCAN_STM32_CHIBIOS
|
||||
# include <hal.h>
|
||||
#else
|
||||
# error "Unknown OS"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* IRQ handler macros
|
||||
*/
|
||||
#if UAVCAN_STM32_CHIBIOS
|
||||
# define UAVCAN_STM32_IRQ_HANDLER(id) CH_IRQ_HANDLER(id)
|
||||
# define UAVCAN_STM32_IRQ_PROLOGUE() CH_IRQ_PROLOGUE()
|
||||
# define UAVCAN_STM32_IRQ_EPILOGUE() CH_IRQ_EPILOGUE()
|
||||
#else
|
||||
# define UAVCAN_STM32_IRQ_HANDLER(id) void id(void)
|
||||
# define UAVCAN_STM32_IRQ_PROLOGUE()
|
||||
# define UAVCAN_STM32_IRQ_EPILOGUE()
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Priority mask for timer and CAN interrupts.
|
||||
* Medium priority by default.
|
||||
*/
|
||||
#ifndef UAVCAN_STM32_IRQ_PRIORITY_MASK
|
||||
# define UAVCAN_STM32_IRQ_PRIORITY_MASK CORTEX_PRIORITY_MASK((CORTEX_MAXIMUM_PRIORITY + CORTEX_MINIMUM_PRIORITY) / 2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Any General-Purpose timer (TIM2, TIM3, TIM4, TIM5)
|
||||
* e.g. -DUAVCAN_STM32_TIMER_NUMBER=2
|
||||
*/
|
||||
#ifndef UAVCAN_STM32_TIMER_NUMBER
|
||||
# error UAVCAN_STM32_TIMER_NUMBER
|
||||
#endif
|
||||
|
||||
|
||||
#define UAVCAN_STM32_GLUE2_(A, B) A##B
|
||||
#define UAVCAN_STM32_GLUE2(A, B) UAVCAN_STM32_GLUE2_(A, B)
|
||||
|
||||
#define UAVCAN_STM32_GLUE3_(A, B, C) A##B##C
|
||||
#define UAVCAN_STM32_GLUE3(A, B, C) UAVCAN_STM32_GLUE3_(A, B, C)
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <uavcan_stm32/clock.hpp>
|
||||
#include "internal.hpp"
|
||||
|
||||
/*
|
||||
* Timer instance
|
||||
*/
|
||||
#define TIMX UAVCAN_STM32_GLUE2(TIM, UAVCAN_STM32_TIMER_NUMBER)
|
||||
#define TIMX_IRQn UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQn)
|
||||
#define TIMX_IRQHandler UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQHandler)
|
||||
|
||||
#if UAVCAN_STM32_TIMER_NUMBER >= 2 && UAVCAN_STM32_TIMER_NUMBER <= 5
|
||||
# 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)
|
||||
# define TIMX_INPUT_CLOCK STM32_TIMCLK1
|
||||
#else
|
||||
# error "This UAVCAN_STM32_TIMER_NUMBER is not supported yet"
|
||||
#endif
|
||||
|
||||
#define TIMX_IRQ_ENABLE() TIMX->DIER = TIM_DIER_UIE
|
||||
#define TIMX_IRQ_DISABLE() TIMX->DIER = 0
|
||||
|
||||
namespace uavcan_stm32
|
||||
{
|
||||
namespace clock
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
bool utc_set = false;
|
||||
|
||||
uavcan::uint32_t utc_jump_cnt = 0;
|
||||
uavcan::int32_t utc_correction_usec_per_overflow = 0;
|
||||
|
||||
uavcan::uint64_t time_mono = 0;
|
||||
uavcan::uint64_t time_utc = 0;
|
||||
|
||||
const uavcan::uint32_t USecPerOverflow = 65536;
|
||||
const uavcan::int32_t MaxUtcSpeedCorrection = 500; // x / 65536
|
||||
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
chSysDisable();
|
||||
|
||||
// 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;
|
||||
|
||||
chSysEnable();
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Callable from any context
|
||||
*/
|
||||
static uavcan::uint64_t sampleSpecifiedTime(const volatile uavcan::uint64_t* const value)
|
||||
{
|
||||
assert(initialized);
|
||||
assert(TIMX->DIER & TIM_DIER_UIE);
|
||||
|
||||
TIMX_IRQ_DISABLE();
|
||||
|
||||
volatile uavcan::uint64_t time = *value;
|
||||
volatile uavcan::uint32_t cnt = TIMX->CNT;
|
||||
|
||||
if (TIMX->SR & TIM_SR_UIF)
|
||||
{
|
||||
/*
|
||||
* The timer has overflowed either before or after CNT sample was obtained.
|
||||
* We need to sample it once more to be sure that the obtained
|
||||
* counter value has wrapped over zero.
|
||||
*/
|
||||
cnt = TIMX->CNT;
|
||||
/*
|
||||
* The timer interrupt was set, but not handled yet.
|
||||
* Thus we need to adjust the tick counter manually.
|
||||
*/
|
||||
time += USecPerOverflow;
|
||||
}
|
||||
|
||||
TIMX_IRQ_ENABLE();
|
||||
|
||||
return time + cnt;
|
||||
}
|
||||
|
||||
uavcan::uint64_t getUtcUSecFromInterrupt()
|
||||
{
|
||||
return utc_set ? sampleSpecifiedTime(&time_utc) : 0;
|
||||
}
|
||||
|
||||
uavcan::MonotonicTime getMonotonic()
|
||||
{
|
||||
const uavcan::uint64_t usec = sampleSpecifiedTime(&time_mono);
|
||||
#if !NDEBUG
|
||||
static uavcan::uint64_t prev_usec = 0; // Self-test
|
||||
assert(prev_usec <= usec);
|
||||
prev_usec = usec;
|
||||
#endif
|
||||
return uavcan::MonotonicTime::fromUSec(usec);
|
||||
}
|
||||
|
||||
uavcan::UtcTime getUtc()
|
||||
{
|
||||
return utc_set ? uavcan::UtcTime::fromUSec(sampleSpecifiedTime(&time_utc)) : uavcan::UtcTime();
|
||||
}
|
||||
|
||||
void adjustUtc(uavcan::UtcDuration adjustment)
|
||||
{
|
||||
assert(initialized);
|
||||
if (adjustment.isZero() && utc_set)
|
||||
{
|
||||
return; // Perfect sync
|
||||
}
|
||||
|
||||
/*
|
||||
* Naive speed adjustment
|
||||
* TODO needs better solution
|
||||
*/
|
||||
if (adjustment.isPositive())
|
||||
{
|
||||
if (utc_correction_usec_per_overflow < MaxUtcSpeedCorrection)
|
||||
{
|
||||
utc_correction_usec_per_overflow++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (utc_correction_usec_per_overflow > -MaxUtcSpeedCorrection)
|
||||
{
|
||||
utc_correction_usec_per_overflow--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clock value adjustment
|
||||
* For small adjustments we will rely only on speed change
|
||||
*/
|
||||
if (adjustment.getAbs().toMSec() > 1 || !utc_set)
|
||||
{
|
||||
if (adjustment.isNegative() &&
|
||||
uavcan::uint64_t(adjustment.getAbs().toUSec()) > time_utc)
|
||||
{
|
||||
TIMX_IRQ_DISABLE();
|
||||
time_utc = 1;
|
||||
TIMX_IRQ_ENABLE();
|
||||
}
|
||||
else
|
||||
{
|
||||
TIMX_IRQ_DISABLE();
|
||||
time_utc += adjustment.toUSec();
|
||||
TIMX_IRQ_ENABLE();
|
||||
}
|
||||
|
||||
if (utc_set)
|
||||
{
|
||||
utc_jump_cnt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
utc_set = true;
|
||||
utc_correction_usec_per_overflow = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uavcan::int32_t getUtcSpeedCorrectionPPM()
|
||||
{
|
||||
return uavcan::int64_t(utc_correction_usec_per_overflow * 1000000) / USecPerOverflow;
|
||||
}
|
||||
|
||||
uavcan::uint32_t getUtcAjdustmentJumpCount() { return utc_jump_cnt; }
|
||||
|
||||
} // namespace clock
|
||||
|
||||
SystemClock& SystemClock::instance()
|
||||
{
|
||||
if (!clock::initialized)
|
||||
{
|
||||
clock::init();
|
||||
}
|
||||
static SystemClock inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
} // namespace uavcan_stm32
|
||||
|
||||
|
||||
/**
|
||||
* Timer interrupt handler
|
||||
*/
|
||||
extern "C"
|
||||
UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler)
|
||||
{
|
||||
UAVCAN_STM32_IRQ_PROLOGUE();
|
||||
|
||||
TIMX->SR = ~TIM_SR_UIF;
|
||||
|
||||
using namespace uavcan_stm32::clock;
|
||||
assert(initialized);
|
||||
|
||||
time_mono += USecPerOverflow;
|
||||
if (utc_set)
|
||||
{
|
||||
time_utc += USecPerOverflow + utc_correction_usec_per_overflow;
|
||||
}
|
||||
|
||||
UAVCAN_STM32_IRQ_EPILOGUE();
|
||||
}
|
||||
@@ -16,6 +16,8 @@ UDEFS = -DUAVCAN_STM32_CHIBIOS=1
|
||||
# UAVCAN library
|
||||
#
|
||||
|
||||
UDEFS += -DUAVCAN_STM32_TIMER_NUMBER=2
|
||||
|
||||
include ../../../libuavcan/include.mk
|
||||
CPPSRC += $(LIBUAVCAN_SRC)
|
||||
UINCDIR += $(LIBUAVCAN_INC)
|
||||
|
||||
@@ -75,5 +75,17 @@ int main()
|
||||
app::ledSet(true);
|
||||
sleep(1);
|
||||
app::led_turn_off_event.signal();
|
||||
|
||||
printf("Mono clock: %lu %lu\n",
|
||||
static_cast<unsigned long>(uavcan_stm32::SystemClock::instance().getMonotonic().toUSec()),
|
||||
static_cast<unsigned long>(uavcan_stm32::SystemClock::instance().getMonotonic().toMSec()));
|
||||
|
||||
printf("UTC clock: %lu\n",
|
||||
static_cast<unsigned long>(uavcan_stm32::SystemClock::instance().getUtc().toUSec()));
|
||||
|
||||
if (uavcan_stm32::SystemClock::instance().getMonotonic().toMSec() / 1000 == 10)
|
||||
{
|
||||
uavcan_stm32::SystemClock::instance().adjustUtc(uavcan::UtcDuration::fromMSec(10000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user