mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-26 02:33:34 +08:00
LPC11C24 clock driver. Not well tested yet, but generally seems to be OK
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <uavcan/driver/system_clock.hpp>
|
||||||
|
|
||||||
|
namespace uavcan_lpc11c24
|
||||||
|
{
|
||||||
|
namespace clock
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Starts the clock.
|
||||||
|
* Can be called multiple times, only the first call will be effective.
|
||||||
|
*/
|
||||||
|
void init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns current monotonic time passed since the moment when clock::init() was called.
|
||||||
|
* Note that both monotonic and UTC clocks are implemented using SysTick timer.
|
||||||
|
*/
|
||||||
|
uavcan::MonotonicTime getMonotonic();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns UTC time if it has been set, otherwise returns zero time.
|
||||||
|
* Note that both monotonic and UTC clocks are implemented using SysTick timer.
|
||||||
|
*/
|
||||||
|
uavcan::UtcTime getUtc();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs UTC time adjustment.
|
||||||
|
* The UTC time will be zero until first adjustment has been performed.
|
||||||
|
*/
|
||||||
|
void adjustUtc(uavcan::UtcDuration adjustment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns clock error sampled at previous UTC adjustment.
|
||||||
|
* Positive if the hardware timer is slower than reference time.
|
||||||
|
*/
|
||||||
|
uavcan::UtcDuration getPrevUtcAdjustment();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for uavcan::ISystemClock.
|
||||||
|
*/
|
||||||
|
class SystemClock : public uavcan::ISystemClock, uavcan::Noncopyable
|
||||||
|
{
|
||||||
|
static SystemClock self;
|
||||||
|
|
||||||
|
SystemClock() { }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Calls clock::init() as needed.
|
||||||
|
* This function is thread safe.
|
||||||
|
*/
|
||||||
|
static SystemClock& instance();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <uavcan_lpc11c24/can.hpp>
|
#include <uavcan_lpc11c24/can.hpp>
|
||||||
|
#include <uavcan_lpc11c24/clock.hpp>
|
||||||
#include <chip.h>
|
#include <chip.h>
|
||||||
#include "internal.hpp"
|
#include "internal.hpp"
|
||||||
|
|
||||||
@@ -264,8 +265,8 @@ uavcan::int16_t CanDriver::send(const uavcan::CanFrame& frame, uavcan::Monotonic
|
|||||||
uavcan::int16_t CanDriver::receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic,
|
uavcan::int16_t CanDriver::receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic,
|
||||||
uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags)
|
uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags)
|
||||||
{
|
{
|
||||||
out_ts_monotonic = uavcan::MonotonicTime(); // TODO: read monotonic
|
out_ts_monotonic = uavcan_lpc11c24::clock::getMonotonic();
|
||||||
out_flags = 0; // We don't support any flags
|
out_flags = 0; // We don't support any IO flags
|
||||||
|
|
||||||
CriticalSectionLocker locker;
|
CriticalSectionLocker locker;
|
||||||
if (rx_queue.getLength() == 0)
|
if (rx_queue.getLength() == 0)
|
||||||
@@ -368,9 +369,9 @@ void canErrorCallback(uint32_t error_info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAN_IRQHandler(void)
|
void CAN_IRQHandler()
|
||||||
{
|
{
|
||||||
uavcan_lpc11c24::last_irq_utc_timestamp = 0; // TODO: Read UTC timestamp
|
uavcan_lpc11c24::last_irq_utc_timestamp = uavcan_lpc11c24::clock::getUtcUSecFromCanInterrupt();
|
||||||
LPC_CCAN_API->isr();
|
LPC_CCAN_API->isr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <uavcan_lpc11c24/clock.hpp>
|
||||||
|
#include <chip.h>
|
||||||
|
#include "internal.hpp"
|
||||||
|
|
||||||
|
namespace uavcan_lpc11c24
|
||||||
|
{
|
||||||
|
namespace clock
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
bool utc_set = false;
|
||||||
|
|
||||||
|
int32_t utc_correction_usec_per_overflow_x16 = 0;
|
||||||
|
int64_t prev_adjustment = 0;
|
||||||
|
|
||||||
|
uint64_t time_mono = 0;
|
||||||
|
uint64_t time_utc = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this value is too large for the given core clock, reload value will be out of the 24-bit integer range.
|
||||||
|
* This will be detected at run time during timer initialization - refer to SysTick_Config().
|
||||||
|
*/
|
||||||
|
const uint32_t USecPerOverflow = 65536 * 2;
|
||||||
|
const int32_t MaxUtcSpeedCorrectionX16 = 100 * 16;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute((noreturn))
|
||||||
|
void fail()
|
||||||
|
{
|
||||||
|
while (true) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
CriticalSectionLocker lock;
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
if ((SystemCoreClock % 1000000) != 0) // Core clock frequency validation
|
||||||
|
{
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SysTick_Config((SystemCoreClock / 1000000) * USecPerOverflow) != 0)
|
||||||
|
{
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimizer breaks this function.
|
||||||
|
__attribute__((optimize("0")))
|
||||||
|
static uint64_t sampleFromCriticalSection(const volatile uint64_t* const value)
|
||||||
|
{
|
||||||
|
const uint32_t reload = SysTick->LOAD; // SysTick counts downwards, hence the value subtracted from reload
|
||||||
|
volatile uint64_t time = *value;
|
||||||
|
volatile uint32_t cycles = reload - SysTick->VAL;
|
||||||
|
|
||||||
|
if (NVIC_GetPendingIRQ(SysTick_IRQn))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
cycles = reload - SysTick->VAL;
|
||||||
|
/*
|
||||||
|
* The timer interrupt was set, but not handled yet.
|
||||||
|
* Thus we need to adjust the tick counter manually.
|
||||||
|
*/
|
||||||
|
time += USecPerOverflow;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Raw counter value must be converted from core cycles to microseconds
|
||||||
|
*/
|
||||||
|
return time + (cycles / (SystemCoreClock / 1000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t getUtcUSecFromCanInterrupt()
|
||||||
|
{
|
||||||
|
return utc_set ? sampleFromCriticalSection(&time_utc) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uavcan::MonotonicTime getMonotonic()
|
||||||
|
{
|
||||||
|
uint64_t usec = 0;
|
||||||
|
{
|
||||||
|
CriticalSectionLocker locker;
|
||||||
|
usec = sampleFromCriticalSection(&time_mono);
|
||||||
|
}
|
||||||
|
return uavcan::MonotonicTime::fromUSec(usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
uavcan::UtcTime getUtc()
|
||||||
|
{
|
||||||
|
if (utc_set)
|
||||||
|
{
|
||||||
|
uint64_t usec = 0;
|
||||||
|
{
|
||||||
|
CriticalSectionLocker locker;
|
||||||
|
usec = sampleFromCriticalSection(&time_utc);
|
||||||
|
}
|
||||||
|
return uavcan::UtcTime::fromUSec(usec);
|
||||||
|
}
|
||||||
|
return uavcan::UtcTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
uavcan::UtcDuration getPrevUtcAdjustment()
|
||||||
|
{
|
||||||
|
return uavcan::UtcDuration::fromUSec(prev_adjustment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void adjustUtc(uavcan::UtcDuration adjustment)
|
||||||
|
{
|
||||||
|
const int64_t adj_delta = adjustment.toUSec() - prev_adjustment; // This is the P term
|
||||||
|
prev_adjustment = adjustment.toUSec();
|
||||||
|
|
||||||
|
utc_correction_usec_per_overflow_x16 += adjustment.isPositive() ? 1 : -1; // I
|
||||||
|
utc_correction_usec_per_overflow_x16 += (adj_delta > 0) ? 1 : -1; // P
|
||||||
|
|
||||||
|
utc_correction_usec_per_overflow_x16 = std::max(utc_correction_usec_per_overflow_x16, -MaxUtcSpeedCorrectionX16);
|
||||||
|
utc_correction_usec_per_overflow_x16 = std::min(utc_correction_usec_per_overflow_x16, MaxUtcSpeedCorrectionX16);
|
||||||
|
|
||||||
|
if (adjustment.getAbs().toMSec() > 2 || !utc_set)
|
||||||
|
{
|
||||||
|
const int64_t adj_usec = adjustment.toUSec();
|
||||||
|
{
|
||||||
|
CriticalSectionLocker locker;
|
||||||
|
if ((adj_usec < 0) && uint64_t(-adj_usec) > time_utc)
|
||||||
|
{
|
||||||
|
time_utc = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
time_utc += adj_usec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!utc_set)
|
||||||
|
{
|
||||||
|
utc_set = true;
|
||||||
|
utc_correction_usec_per_overflow_x16 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace clock
|
||||||
|
|
||||||
|
SystemClock SystemClock::self;
|
||||||
|
|
||||||
|
SystemClock& SystemClock::instance()
|
||||||
|
{
|
||||||
|
clock::init();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timer interrupt handler
|
||||||
|
*/
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
|
||||||
|
__attribute__((optimize("0")))
|
||||||
|
void SysTick_Handler()
|
||||||
|
{
|
||||||
|
using namespace uavcan_lpc11c24::clock;
|
||||||
|
if (initialized)
|
||||||
|
{
|
||||||
|
time_mono += USecPerOverflow;
|
||||||
|
if (utc_set)
|
||||||
|
{
|
||||||
|
// Values below 16 are ignored
|
||||||
|
time_utc += USecPerOverflow + (utc_correction_usec_per_overflow_x16 / 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <chip.h>
|
||||||
|
|
||||||
namespace uavcan_lpc11c24
|
namespace uavcan_lpc11c24
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -23,4 +25,14 @@ struct CriticalSectionLocker
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal for the driver
|
||||||
|
*/
|
||||||
|
namespace clock
|
||||||
|
{
|
||||||
|
|
||||||
|
uint64_t getUtcUSecFromCanInterrupt();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <board.hpp>
|
#include <board.hpp>
|
||||||
#include <chip.h>
|
#include <chip.h>
|
||||||
#include <uavcan_lpc11c24/can.hpp>
|
#include <uavcan_lpc11c24/can.hpp>
|
||||||
|
#include <uavcan_lpc11c24/clock.hpp>
|
||||||
|
|
||||||
#define ENFORCE(x) \
|
#define ENFORCE(x) \
|
||||||
if ((x) == 0) { \
|
if ((x) == 0) { \
|
||||||
@@ -18,9 +19,18 @@ int main()
|
|||||||
while (true) { }
|
while (true) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uavcan_lpc11c24::clock::init();
|
||||||
|
|
||||||
|
uavcan::MonotonicTime prev_mono;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
//for (volatile int i = 0; i < 2000000; i++) { __asm volatile ("nop"); }
|
const uavcan::MonotonicTime ts_mono = uavcan_lpc11c24::clock::getMonotonic();
|
||||||
|
|
||||||
|
while (prev_mono >= ts_mono)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
prev_mono = ts_mono;
|
||||||
|
|
||||||
board::setErrorLed(uavcan_lpc11c24::CanDriver::instance().getErrorCount() > 0);
|
board::setErrorLed(uavcan_lpc11c24::CanDriver::instance().getErrorCount() > 0);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user