mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-07-04 17:50:34 +08:00
Merge pull request #71 from UAVCAN/lpc11c24
Missing features for LPC11C24
This commit is contained in:
@@ -29,11 +29,20 @@ public:
|
||||
*/
|
||||
static CanDriver& instance() { return self; }
|
||||
|
||||
/**
|
||||
* Attempts to detect bit rate of the CAN bus.
|
||||
* This function may block for up to X seconds, where X is the number of bit rates to try.
|
||||
* This function is NOT guaranteed to reset the CAN controller upon return.
|
||||
* @return On success: detected bit rate, in bits per second.
|
||||
* On failure: zero.
|
||||
*/
|
||||
static uavcan::uint32_t detectBitRate(void (*idle_callback)() = nullptr);
|
||||
|
||||
/**
|
||||
* Returns negative value if the requested baudrate can't be used.
|
||||
* Returns zero if OK.
|
||||
*/
|
||||
int init(uavcan::uint32_t baudrate);
|
||||
int init(uavcan::uint32_t bitrate);
|
||||
|
||||
bool hasReadyRx() const;
|
||||
bool hasEmptyTx() const;
|
||||
@@ -44,26 +53,41 @@ public:
|
||||
*/
|
||||
bool hadActivity();
|
||||
|
||||
virtual uavcan::int16_t send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline,
|
||||
uavcan::CanIOFlags flags);
|
||||
/**
|
||||
* Returns the number of times the RX queue was overrun.
|
||||
*/
|
||||
uavcan::uint32_t getRxQueueOverflowCount() const;
|
||||
|
||||
virtual uavcan::int16_t receive(uavcan::CanFrame& out_frame, uavcan::MonotonicTime& out_ts_monotonic,
|
||||
uavcan::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags);
|
||||
/**
|
||||
* Whether the controller is currently in bus off state.
|
||||
* Note that the driver recovers the CAN controller from the bus off state automatically!
|
||||
* Therefore, this method serves only monitoring purposes and is not necessary to use.
|
||||
*/
|
||||
bool isInBusOffState() const;
|
||||
|
||||
virtual uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks,
|
||||
const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces],
|
||||
uavcan::MonotonicTime blocking_deadline);
|
||||
uavcan::int16_t send(const uavcan::CanFrame& frame,
|
||||
uavcan::MonotonicTime tx_deadline,
|
||||
uavcan::CanIOFlags flags) override;
|
||||
|
||||
virtual uavcan::int16_t configureFilters(const uavcan::CanFilterConfig* filter_configs,
|
||||
uavcan::uint16_t num_configs);
|
||||
uavcan::int16_t receive(uavcan::CanFrame& out_frame,
|
||||
uavcan::MonotonicTime& out_ts_monotonic,
|
||||
uavcan::UtcTime& out_ts_utc,
|
||||
uavcan::CanIOFlags& out_flags) override;
|
||||
|
||||
virtual uavcan::uint64_t getErrorCount() const;
|
||||
uavcan::int16_t select(uavcan::CanSelectMasks& inout_masks,
|
||||
const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces],
|
||||
uavcan::MonotonicTime blocking_deadline) override;
|
||||
|
||||
virtual uavcan::uint16_t getNumFilters() const;
|
||||
uavcan::int16_t configureFilters(const uavcan::CanFilterConfig* filter_configs,
|
||||
uavcan::uint16_t num_configs) override;
|
||||
|
||||
virtual uavcan::ICanIface* getIface(uavcan::uint8_t iface_index);
|
||||
uavcan::uint64_t getErrorCount() const override;
|
||||
|
||||
virtual uavcan::uint8_t getNumIfaces() const;
|
||||
uavcan::uint16_t getNumFilters() const override;
|
||||
|
||||
uavcan::ICanIface* getIface(uavcan::uint8_t iface_index) override;
|
||||
|
||||
uavcan::uint8_t getNumIfaces() const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ class SystemClock : public uavcan::ISystemClock, uavcan::Noncopyable
|
||||
|
||||
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); }
|
||||
uavcan::MonotonicTime getMonotonic() const override { return clock::getMonotonic(); }
|
||||
uavcan::UtcTime getUtc() const override { return clock::getUtc(); }
|
||||
void adjustUtc(uavcan::UtcDuration adjustment) override { clock::adjustUtc(adjustment); }
|
||||
|
||||
public:
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Bosch C_CAN controller API.
|
||||
*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace uavcan_lpc11c24
|
||||
{
|
||||
namespace c_can
|
||||
{
|
||||
|
||||
struct MsgIfaceType
|
||||
{
|
||||
std::uint32_t CMDREQ;
|
||||
|
||||
union
|
||||
{
|
||||
std::uint32_t W;
|
||||
std::uint32_t R;
|
||||
} CMDMSK;
|
||||
|
||||
std::uint32_t MSK1;
|
||||
std::uint32_t MSK2;
|
||||
|
||||
std::uint32_t ARB1;
|
||||
std::uint32_t ARB2;
|
||||
|
||||
std::uint32_t MCTRL;
|
||||
|
||||
std::uint32_t DA1;
|
||||
std::uint32_t DA2;
|
||||
std::uint32_t DB1;
|
||||
std::uint32_t DB2;
|
||||
|
||||
const std::uint32_t _skip[13];
|
||||
};
|
||||
|
||||
static_assert(offsetof(MsgIfaceType, CMDMSK) == 0x04, "C_CAN offset");
|
||||
static_assert(offsetof(MsgIfaceType, MSK1) == 0x08, "C_CAN offset");
|
||||
static_assert(offsetof(MsgIfaceType, ARB1) == 0x10, "C_CAN offset");
|
||||
static_assert(offsetof(MsgIfaceType, MCTRL) == 0x18, "C_CAN offset");
|
||||
static_assert(offsetof(MsgIfaceType, DA1) == 0x1c, "C_CAN offset");
|
||||
static_assert(offsetof(MsgIfaceType, DB2) == 0x28, "C_CAN offset");
|
||||
|
||||
static_assert(sizeof(MsgIfaceType) == 96, "C_CAN size");
|
||||
|
||||
|
||||
struct Type
|
||||
{
|
||||
std::uint32_t CNTL;
|
||||
std::uint32_t STAT;
|
||||
const std::uint32_t EC;
|
||||
std::uint32_t BT;
|
||||
const std::uint32_t INT;
|
||||
std::uint32_t TEST;
|
||||
std::uint32_t BRPE;
|
||||
|
||||
const std::uint32_t _skip_a[1];
|
||||
|
||||
MsgIfaceType IF[2]; // [0] @ 0x020, [1] @ 0x080
|
||||
|
||||
const std::uint32_t _skip_b[8];
|
||||
|
||||
const std::uint32_t TXREQ[2]; // 0x100
|
||||
|
||||
const std::uint32_t _skip_c[6];
|
||||
|
||||
const std::uint32_t ND[2]; // 0x120
|
||||
|
||||
const std::uint32_t _skip_d[6];
|
||||
|
||||
const std::uint32_t IR[2]; // 0x140
|
||||
|
||||
const std::uint32_t _skip_e[6];
|
||||
|
||||
const std::uint32_t MSGV[2]; // 0x160
|
||||
|
||||
const std::uint32_t _skip_f[6];
|
||||
|
||||
std::uint32_t CLKDIV; // 0x180
|
||||
};
|
||||
|
||||
static_assert(offsetof(Type, CNTL) == 0x000, "C_CAN offset");
|
||||
static_assert(offsetof(Type, STAT) == 0x004, "C_CAN offset");
|
||||
static_assert(offsetof(Type, TEST) == 0x014, "C_CAN offset");
|
||||
static_assert(offsetof(Type, BRPE) == 0x018, "C_CAN offset");
|
||||
static_assert(offsetof(Type, IF[0]) == 0x020, "C_CAN offset");
|
||||
static_assert(offsetof(Type, IF[1]) == 0x080, "C_CAN offset");
|
||||
static_assert(offsetof(Type, TXREQ) == 0x100, "C_CAN offset");
|
||||
static_assert(offsetof(Type, ND) == 0x120, "C_CAN offset");
|
||||
static_assert(offsetof(Type, IR) == 0x140, "C_CAN offset");
|
||||
static_assert(offsetof(Type, MSGV) == 0x160, "C_CAN offset");
|
||||
static_assert(offsetof(Type, CLKDIV) == 0x180, "C_CAN offset");
|
||||
|
||||
static_assert(offsetof(Type, IF[0].DB2) == 0x048, "C_CAN offset");
|
||||
static_assert(offsetof(Type, IF[1].DB2) == 0x0A8, "C_CAN offset");
|
||||
|
||||
|
||||
volatile Type& CAN = *reinterpret_cast<volatile Type*>(0x40050000);
|
||||
|
||||
|
||||
/*
|
||||
* CNTL
|
||||
*/
|
||||
static constexpr std::uint32_t CNTL_TEST = 1 << 7;
|
||||
static constexpr std::uint32_t CNTL_CCE = 1 << 6;
|
||||
static constexpr std::uint32_t CNTL_DAR = 1 << 5;
|
||||
static constexpr std::uint32_t CNTL_EIE = 1 << 3;
|
||||
static constexpr std::uint32_t CNTL_SIE = 1 << 2;
|
||||
static constexpr std::uint32_t CNTL_IE = 1 << 1;
|
||||
static constexpr std::uint32_t CNTL_INIT = 1 << 0;
|
||||
|
||||
static constexpr std::uint32_t CNTL_IRQ_MASK = CNTL_EIE | CNTL_IE | CNTL_SIE;
|
||||
|
||||
/*
|
||||
* TEST
|
||||
*/
|
||||
static constexpr std::uint32_t TEST_RX = 1 << 7;
|
||||
static constexpr std::uint32_t TEST_LBACK = 1 << 4;
|
||||
static constexpr std::uint32_t TEST_SILENT = 1 << 3;
|
||||
static constexpr std::uint32_t TEST_BASIC = 1 << 2;
|
||||
static constexpr std::uint32_t TEST_TX_SHIFT = 5;
|
||||
|
||||
enum class TestTx : std::uint32_t
|
||||
{
|
||||
Controller = 0,
|
||||
SamplePoint = 1,
|
||||
LowDominant = 2,
|
||||
HighRecessive = 3
|
||||
};
|
||||
|
||||
/*
|
||||
* STAT
|
||||
*/
|
||||
static constexpr std::uint32_t STAT_BOFF = 1 << 7;
|
||||
static constexpr std::uint32_t STAT_EWARN = 1 << 6;
|
||||
static constexpr std::uint32_t STAT_EPASS = 1 << 5;
|
||||
static constexpr std::uint32_t STAT_RXOK = 1 << 4;
|
||||
static constexpr std::uint32_t STAT_TXOK = 1 << 3;
|
||||
static constexpr std::uint32_t STAT_LEC_MASK = 7;
|
||||
static constexpr std::uint32_t STAT_LEC_SHIFT = 0;
|
||||
|
||||
enum class StatLec : std::uint32_t
|
||||
{
|
||||
NoError = 0,
|
||||
StuffError = 1,
|
||||
FormError = 2,
|
||||
AckError = 3,
|
||||
Bit1Error = 4,
|
||||
Bit0Error = 5,
|
||||
CRCError = 6,
|
||||
Unused = 7
|
||||
};
|
||||
|
||||
/*
|
||||
* IF.CMDREQ
|
||||
*/
|
||||
static constexpr std::uint32_t IF_CMDREQ_BUSY = 1 << 15;
|
||||
|
||||
/*
|
||||
* IF.CMDMSK
|
||||
*/
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_DATA_A = 1 << 0;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_DATA_B = 1 << 1;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_TXRQST = 1 << 2;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_CTRL = 1 << 4;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_ARB = 1 << 5;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_MASK = 1 << 6;
|
||||
static constexpr std::uint32_t IF_CMDMSK_W_WR_RD = 1 << 7;
|
||||
|
||||
/*
|
||||
* IF.MCTRL
|
||||
*/
|
||||
static constexpr std::uint32_t IF_MCTRL_NEWDAT = 1 << 15;
|
||||
static constexpr std::uint32_t IF_MCTRL_MSGLST = 1 << 14;
|
||||
static constexpr std::uint32_t IF_MCTRL_INTPND = 1 << 13;
|
||||
static constexpr std::uint32_t IF_MCTRL_UMASK = 1 << 12;
|
||||
static constexpr std::uint32_t IF_MCTRL_TXIE = 1 << 11;
|
||||
static constexpr std::uint32_t IF_MCTRL_RXIE = 1 << 10;
|
||||
static constexpr std::uint32_t IF_MCTRL_RMTEN = 1 << 9;
|
||||
static constexpr std::uint32_t IF_MCTRL_TXRQST = 1 << 8;
|
||||
static constexpr std::uint32_t IF_MCTRL_EOB = 1 << 7;
|
||||
static constexpr std::uint32_t IF_MCTRL_DLC_MASK = 15;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <uavcan_lpc11c24/clock.hpp>
|
||||
#include <uavcan/util/templates.hpp>
|
||||
#include <chip.h>
|
||||
#include "c_can.hpp"
|
||||
#include "internal.hpp"
|
||||
|
||||
/**
|
||||
@@ -19,9 +20,9 @@
|
||||
# error UAVCAN_LPC11C24_RX_QUEUE_LEN is too large
|
||||
#endif
|
||||
|
||||
extern "C" void canRxCallback(uint8_t msg_obj_num);
|
||||
extern "C" void canTxCallback(uint8_t msg_obj_num);
|
||||
extern "C" void canErrorCallback(uint32_t error_info);
|
||||
extern "C" void canRxCallback(std::uint8_t msg_obj_num);
|
||||
extern "C" void canTxCallback(std::uint8_t msg_obj_num);
|
||||
extern "C" void canErrorCallback(std::uint32_t error_info);
|
||||
|
||||
namespace uavcan_lpc11c24
|
||||
{
|
||||
@@ -34,25 +35,36 @@ namespace
|
||||
* TX priority is defined by the message object number, not by the CAN ID (chapter 16.7.3.5 of the user manual),
|
||||
* hence we can't use more than one object because that would cause priority inversion on long transfers.
|
||||
*/
|
||||
const unsigned NumMsgObjects = 32;
|
||||
constexpr unsigned NumberOfMessageObjects = 32;
|
||||
constexpr unsigned NumberOfTxMessageObjects = 1;
|
||||
constexpr unsigned NumberOfRxMessageObjects = NumberOfMessageObjects - NumberOfTxMessageObjects;
|
||||
constexpr unsigned TxMessageObjectNumber = 1;
|
||||
|
||||
/**
|
||||
* Total number of CAN errors.
|
||||
* Does not overflow.
|
||||
*/
|
||||
uint32_t error_cnt;
|
||||
volatile std::uint32_t error_cnt = 0;
|
||||
|
||||
/**
|
||||
* True if there's no pending TX frame, i.e. write is possible.
|
||||
* False if there's no pending TX frame, i.e. write is possible.
|
||||
*/
|
||||
bool tx_free = true;
|
||||
volatile bool tx_pending = false;
|
||||
|
||||
/**
|
||||
* Currently pending frame must be aborted on first error.
|
||||
*/
|
||||
volatile bool tx_abort_on_error = false;
|
||||
|
||||
/**
|
||||
* Gets updated every time the CAN IRQ handler is being called.
|
||||
*/
|
||||
uint64_t last_irq_utc_timestamp = 0;
|
||||
volatile std::uint64_t last_irq_utc_timestamp = 0;
|
||||
|
||||
bool had_activity;
|
||||
/**
|
||||
* Set by the driver on every successful TX or RX; reset by the application.
|
||||
*/
|
||||
volatile bool had_activity = false;
|
||||
|
||||
/**
|
||||
* After a received message gets extracted from C_CAN, it will be stored in the RX queue until libuavcan
|
||||
@@ -62,26 +74,18 @@ class RxQueue
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
uint64_t utc_usec;
|
||||
std::uint64_t utc_usec = 0;
|
||||
uavcan::CanFrame frame;
|
||||
Item() : utc_usec(0) { }
|
||||
};
|
||||
|
||||
Item buf_[UAVCAN_LPC11C24_RX_QUEUE_LEN];
|
||||
uint32_t overflow_cnt_;
|
||||
uint8_t in_;
|
||||
uint8_t out_;
|
||||
uint8_t len_;
|
||||
std::uint32_t overflow_cnt_ = 0;
|
||||
std::uint8_t in_ = 0;
|
||||
std::uint8_t out_ = 0;
|
||||
std::uint8_t len_ = 0;
|
||||
|
||||
public:
|
||||
RxQueue()
|
||||
: overflow_cnt_(0)
|
||||
, in_(0)
|
||||
, out_(0)
|
||||
, len_(0)
|
||||
{ }
|
||||
|
||||
void push(const uavcan::CanFrame& frame, const uint64_t& utc_usec)
|
||||
void push(const uavcan::CanFrame& frame, const volatile std::uint64_t& utc_usec)
|
||||
{
|
||||
buf_[in_].frame = frame;
|
||||
buf_[in_].utc_usec = utc_usec;
|
||||
@@ -106,7 +110,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void pop(uavcan::CanFrame& out_frame, uint64_t& out_utc_usec)
|
||||
void pop(uavcan::CanFrame& out_frame, std::uint64_t& out_utc_usec)
|
||||
{
|
||||
if (len_ > 0)
|
||||
{
|
||||
@@ -123,90 +127,200 @@ public:
|
||||
|
||||
unsigned getLength() const { return len_; }
|
||||
|
||||
uint32_t getOverflowCount() const { return overflow_cnt_; }
|
||||
std::uint32_t getOverflowCount() const { return overflow_cnt_; }
|
||||
};
|
||||
|
||||
RxQueue rx_queue;
|
||||
|
||||
|
||||
int computeBaudrate(uint32_t baud_rate, uint32_t can_api_timing_cfg[2])
|
||||
struct BitTimingSettings
|
||||
{
|
||||
const uint32_t pclk = Chip_Clock_GetMainClockRate();
|
||||
const uint32_t clk_per_bit = pclk / baud_rate;
|
||||
for (uint32_t div = 0; div <= 15; div++)
|
||||
std::uint32_t canclkdiv;
|
||||
std::uint32_t canbtr;
|
||||
|
||||
bool isValid() const { return canbtr != 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* http://www.bittiming.can-wiki.info
|
||||
*/
|
||||
BitTimingSettings computeBitTimings(std::uint32_t bitrate)
|
||||
{
|
||||
if (Chip_Clock_GetSystemClockRate() == 48000000) // 48 MHz is optimal for CAN timings
|
||||
{
|
||||
for (uint32_t quanta = 1; quanta <= 32; quanta++)
|
||||
switch (bitrate)
|
||||
{
|
||||
for (uint32_t segs = 3; segs <= 17; segs++)
|
||||
{
|
||||
if (clk_per_bit == (segs * quanta * (div + 1)))
|
||||
{
|
||||
segs -= 3;
|
||||
const uint32_t seg1 = segs / 2;
|
||||
const uint32_t seg2 = segs - seg1;
|
||||
const uint32_t can_sjw = (seg1 > 3) ? 3 : seg1;
|
||||
can_api_timing_cfg[0] = div;
|
||||
can_api_timing_cfg[1] = ((quanta - 1) & 0x3F) |
|
||||
(can_sjw & 0x03) << 6 |
|
||||
(seg1 & 0x0F) << 8 |
|
||||
(seg2 & 0x07) << 12;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case 1000000: return BitTimingSettings{ 0, 0x0505 }; // 8 quanta, 87.5%
|
||||
case 500000: return BitTimingSettings{ 0, 0x1c05 }; // 16 quanta, 87.5%
|
||||
case 250000: return BitTimingSettings{ 0, 0x1c0b }; // 16 quanta, 87.5%
|
||||
case 125000: return BitTimingSettings{ 0, 0x1c17 }; // 16 quanta, 87.5%
|
||||
default: return BitTimingSettings{ 0, 0 };
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
return BitTimingSettings{ 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CanDriver CanDriver::self;
|
||||
|
||||
int CanDriver::init(uavcan::uint32_t baudrate)
|
||||
uavcan::uint32_t CanDriver::detectBitRate(void (*idle_callback)())
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
static constexpr uavcan::uint32_t BitRatesToTry[] =
|
||||
{
|
||||
1000000,
|
||||
500000,
|
||||
250000,
|
||||
125000
|
||||
};
|
||||
|
||||
error_cnt = 0;
|
||||
tx_free = true;
|
||||
last_irq_utc_timestamp = 0;
|
||||
had_activity = false;
|
||||
const auto ListeningDuration = uavcan::MonotonicDuration::fromMSec(1050);
|
||||
|
||||
NVIC_DisableIRQ(CAN_IRQn);
|
||||
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN);
|
||||
|
||||
for (auto bitrate : BitRatesToTry)
|
||||
{
|
||||
// Computing bit timings
|
||||
const auto bit_timings = computeBitTimings(bitrate);
|
||||
if (!bit_timings.isValid())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Configuring the CAN controller
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
|
||||
LPC_SYSCTL->PRESETCTRL |= (1U << RESET_CAN0);
|
||||
|
||||
// Entering initialization mode
|
||||
c_can::CAN.CNTL = c_can::CNTL_INIT | c_can::CNTL_CCE;
|
||||
|
||||
while ((c_can::CAN.CNTL & c_can::CNTL_INIT) == 0)
|
||||
{
|
||||
; // Nothing to do
|
||||
}
|
||||
|
||||
// Configuring bit rate
|
||||
c_can::CAN.CLKDIV = bit_timings.canclkdiv;
|
||||
c_can::CAN.BT = bit_timings.canbtr;
|
||||
c_can::CAN.BRPE = 0;
|
||||
|
||||
// Configuring silent mode
|
||||
c_can::CAN.CNTL |= c_can::CNTL_TEST;
|
||||
c_can::CAN.TEST = c_can::TEST_SILENT;
|
||||
|
||||
// Configuring status monitor
|
||||
c_can::CAN.STAT = (unsigned(c_can::StatLec::Unused) << c_can::STAT_LEC_SHIFT);
|
||||
|
||||
// Leaving initialization mode
|
||||
c_can::CAN.CNTL &= ~(c_can::CNTL_INIT | c_can::CNTL_CCE);
|
||||
|
||||
while ((c_can::CAN.CNTL & c_can::CNTL_INIT) != 0)
|
||||
{
|
||||
; // Nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// Listening
|
||||
const auto deadline = clock::getMonotonic() + ListeningDuration;
|
||||
bool match_detected = false;
|
||||
while (clock::getMonotonic() < deadline)
|
||||
{
|
||||
if (idle_callback != nullptr)
|
||||
{
|
||||
idle_callback();
|
||||
}
|
||||
|
||||
const auto LastErrorCode = (c_can::CAN.STAT >> c_can::STAT_LEC_SHIFT) & c_can::STAT_LEC_MASK;
|
||||
|
||||
if (LastErrorCode == unsigned(c_can::StatLec::NoError))
|
||||
{
|
||||
match_detected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// De-configuring the CAN controller back to reset state
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
|
||||
c_can::CAN.CNTL = c_can::CNTL_INIT;
|
||||
|
||||
while ((c_can::CAN.CNTL & c_can::CNTL_INIT) == 0)
|
||||
{
|
||||
; // Nothing to do
|
||||
}
|
||||
|
||||
LPC_SYSCTL->PRESETCTRL &= ~(1U << RESET_CAN0);
|
||||
}
|
||||
|
||||
// Termination condition
|
||||
if (match_detected)
|
||||
{
|
||||
return bitrate;
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // No match
|
||||
}
|
||||
|
||||
int CanDriver::init(uavcan::uint32_t bitrate)
|
||||
{
|
||||
{
|
||||
auto bit_timings = computeBitTimings(bitrate);
|
||||
if (!bit_timings.isValid())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
CriticalSectionLocker locker;
|
||||
|
||||
error_cnt = 0;
|
||||
tx_abort_on_error = false;
|
||||
tx_pending = false;
|
||||
last_irq_utc_timestamp = 0;
|
||||
had_activity = false;
|
||||
|
||||
/*
|
||||
* C_CAN init
|
||||
*/
|
||||
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN);
|
||||
|
||||
LPC_CCAN_API->init_can(reinterpret_cast<std::uint32_t*>(&bit_timings), true);
|
||||
|
||||
static CCAN_CALLBACKS_T ccan_callbacks =
|
||||
{
|
||||
canRxCallback,
|
||||
canTxCallback,
|
||||
canErrorCallback,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
};
|
||||
LPC_CCAN_API->config_calb(&ccan_callbacks);
|
||||
|
||||
/*
|
||||
* Interrupts
|
||||
*/
|
||||
c_can::CAN.CNTL |= c_can::CNTL_SIE; // This is necessary for transmission aborts on error
|
||||
|
||||
NVIC_EnableIRQ(CAN_IRQn);
|
||||
}
|
||||
|
||||
/*
|
||||
* C_CAN init
|
||||
* Applying default filter configuration (accept all)
|
||||
*/
|
||||
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN);
|
||||
static uint32_t can_api_init_table[2];
|
||||
if (computeBaudrate(baudrate, can_api_init_table) != 0)
|
||||
if (configureFilters(nullptr, 0) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
LPC_CCAN_API->init_can(can_api_init_table, true);
|
||||
static CCAN_CALLBACKS_T ccan_callbacks =
|
||||
{
|
||||
canRxCallback,
|
||||
canTxCallback,
|
||||
canErrorCallback,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
LPC_CCAN_API->config_calb(&ccan_callbacks);
|
||||
NVIC_EnableIRQ(CAN_IRQn);
|
||||
|
||||
/*
|
||||
* Default RX msgobj config:
|
||||
* 31 - all STD
|
||||
* 32 - all EXT
|
||||
* RTR ignored
|
||||
*/
|
||||
CCAN_MSG_OBJ_T msg_obj = CCAN_MSG_OBJ_T();
|
||||
msg_obj.msgobj = 31;
|
||||
LPC_CCAN_API->config_rxmsgobj(&msg_obj);
|
||||
msg_obj.mode_id = CAN_MSGOBJ_EXT;
|
||||
msg_obj.msgobj = 32;
|
||||
LPC_CCAN_API->config_rxmsgobj(&msg_obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -220,7 +334,7 @@ bool CanDriver::hasReadyRx() const
|
||||
bool CanDriver::hasEmptyTx() const
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
return tx_free;
|
||||
return !tx_pending;
|
||||
}
|
||||
|
||||
bool CanDriver::hadActivity()
|
||||
@@ -231,12 +345,23 @@ bool CanDriver::hadActivity()
|
||||
return ret;
|
||||
}
|
||||
|
||||
uavcan::uint32_t CanDriver::getRxQueueOverflowCount() const
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
return rx_queue.getOverflowCount();
|
||||
}
|
||||
|
||||
bool CanDriver::isInBusOffState() const
|
||||
{
|
||||
return (c_can::CAN.STAT & c_can::STAT_BOFF) != 0;
|
||||
}
|
||||
|
||||
uavcan::int16_t CanDriver::send(const uavcan::CanFrame& frame, uavcan::MonotonicTime tx_deadline,
|
||||
uavcan::CanIOFlags flags)
|
||||
{
|
||||
if (frame.isErrorFrame() ||
|
||||
frame.dlc > 8 ||
|
||||
flags != 0) // Only plain IO is allowed. Loopback, TX timestamping are not supported by this driver.
|
||||
(flags & uavcan::CanIOFlagLoopback) != 0) // TX timestamping is not supported by this driver.
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -264,10 +389,11 @@ uavcan::int16_t CanDriver::send(const uavcan::CanFrame& frame, uavcan::Monotonic
|
||||
|
||||
CriticalSectionLocker locker;
|
||||
|
||||
if (tx_free)
|
||||
if (!tx_pending)
|
||||
{
|
||||
tx_free = false; // Mark as pending - will be released in TX callback
|
||||
msgobj.msgobj = 1;
|
||||
tx_pending = true; // Mark as pending - will be released in TX callback
|
||||
tx_abort_on_error = (flags & uavcan::CanIOFlagAbortOnError) != 0;
|
||||
msgobj.msgobj = TxMessageObjectNumber;
|
||||
LPC_CCAN_API->can_transmit(&msgobj);
|
||||
return 1;
|
||||
}
|
||||
@@ -277,7 +403,7 @@ 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::UtcTime& out_ts_utc, uavcan::CanIOFlags& out_flags)
|
||||
{
|
||||
out_ts_monotonic = uavcan_lpc11c24::clock::getMonotonic();
|
||||
out_ts_monotonic = clock::getMonotonic();
|
||||
out_flags = 0; // We don't support any IO flags
|
||||
|
||||
CriticalSectionLocker locker;
|
||||
@@ -285,7 +411,7 @@ uavcan::int16_t CanDriver::receive(uavcan::CanFrame& out_frame, uavcan::Monotoni
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint64_t ts_utc = 0;
|
||||
std::uint64_t ts_utc = 0;
|
||||
rx_queue.pop(out_frame, ts_utc);
|
||||
out_ts_utc = uavcan::UtcTime::fromUSec(ts_utc);
|
||||
return 1;
|
||||
@@ -295,6 +421,16 @@ uavcan::int16_t CanDriver::select(uavcan::CanSelectMasks& inout_masks,
|
||||
const uavcan::CanFrame* (&)[uavcan::MaxCanIfaces],
|
||||
uavcan::MonotonicTime blocking_deadline)
|
||||
{
|
||||
const bool bus_off = isInBusOffState();
|
||||
if (bus_off) // Recover automatically on bus-off
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
if ((c_can::CAN.CNTL & c_can::CNTL_INIT) != 0)
|
||||
{
|
||||
c_can::CAN.CNTL &= ~c_can::CNTL_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
const bool noblock = ((inout_masks.read == 1) && hasReadyRx()) ||
|
||||
((inout_masks.write == 1) && hasEmptyTx());
|
||||
|
||||
@@ -322,32 +458,98 @@ uavcan::int16_t CanDriver::select(uavcan::CanSelectMasks& inout_masks,
|
||||
}
|
||||
|
||||
inout_masks.read = hasReadyRx() ? 1 : 0;
|
||||
inout_masks.write = hasEmptyTx() ? 1 : 0;
|
||||
return 0; // Return value doesn't matter as long as it is non-negative
|
||||
|
||||
inout_masks.write = (hasEmptyTx() && !bus_off) ? 1 : 0; // Disable write while in bus-off
|
||||
|
||||
return 0; // Return value doesn't matter as long as it is non-negative
|
||||
}
|
||||
|
||||
uavcan::int16_t CanDriver::configureFilters(const uavcan::CanFilterConfig* filter_configs,
|
||||
uavcan::uint16_t num_configs)
|
||||
{
|
||||
(void)filter_configs;
|
||||
(void)num_configs;
|
||||
return -1;
|
||||
CriticalSectionLocker locker;
|
||||
|
||||
/*
|
||||
* If C_CAN is active (INIT=0) and the CAN bus has intensive traffic, RX object configuration may fail.
|
||||
* The solution is to disable the controller while configuration is in progress.
|
||||
* The documentation, as always, doesn't bother to mention this detail. Shame on you, NXP.
|
||||
*/
|
||||
struct RAIIDisabler
|
||||
{
|
||||
RAIIDisabler()
|
||||
{
|
||||
c_can::CAN.CNTL |= c_can::CNTL_INIT;
|
||||
}
|
||||
~RAIIDisabler()
|
||||
{
|
||||
c_can::CAN.CNTL &= ~c_can::CNTL_INIT;
|
||||
}
|
||||
} can_disabler; // Must be instantiated AFTER the critical section locker
|
||||
|
||||
if (num_configs == 0)
|
||||
{
|
||||
auto msg_obj = CCAN_MSG_OBJ_T();
|
||||
msg_obj.msgobj = NumberOfTxMessageObjects + 1;
|
||||
LPC_CCAN_API->config_rxmsgobj(&msg_obj); // all STD frames
|
||||
|
||||
msg_obj.mode_id = CAN_MSGOBJ_EXT;
|
||||
msg_obj.msgobj = NumberOfTxMessageObjects + 2;
|
||||
LPC_CCAN_API->config_rxmsgobj(&msg_obj); // all EXT frames
|
||||
}
|
||||
else if (num_configs <= NumberOfRxMessageObjects)
|
||||
{
|
||||
// Making sure the configs use only EXT frames; otherwise we can't accept them
|
||||
for (unsigned i = 0; i < num_configs; i++)
|
||||
{
|
||||
auto& f = filter_configs[i];
|
||||
if ((f.id & f.mask & uavcan::CanFrame::FlagEFF) == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Installing the configuration
|
||||
for (unsigned i = 0; i < NumberOfRxMessageObjects; i++)
|
||||
{
|
||||
auto msg_obj = CCAN_MSG_OBJ_T();
|
||||
msg_obj.msgobj = std::uint8_t(i + 1U + NumberOfTxMessageObjects); // Message objects are numbered from 1
|
||||
|
||||
if (i < num_configs)
|
||||
{
|
||||
msg_obj.mode_id = (filter_configs[i].id & uavcan::CanFrame::MaskExtID) | CAN_MSGOBJ_EXT; // Only EXT
|
||||
msg_obj.mask = filter_configs[i].mask & uavcan::CanFrame::MaskExtID;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg_obj.mode_id = CAN_MSGOBJ_RTR; // Using this configuration to disable the object
|
||||
msg_obj.mask = uavcan::CanFrame::MaskStdID;
|
||||
}
|
||||
|
||||
LPC_CCAN_API->config_rxmsgobj(&msg_obj);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uavcan::uint64_t CanDriver::getErrorCount() const
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
return uint64_t(error_cnt) + uint64_t(rx_queue.getOverflowCount());
|
||||
return std::uint64_t(error_cnt) + std::uint64_t(rx_queue.getOverflowCount());
|
||||
}
|
||||
|
||||
uavcan::uint16_t CanDriver::getNumFilters() const
|
||||
{
|
||||
return NumMsgObjects - 1; // First msgobj is reserved for TX frame
|
||||
return NumberOfRxMessageObjects;
|
||||
}
|
||||
|
||||
uavcan::ICanIface* CanDriver::getIface(uavcan::uint8_t iface_index)
|
||||
{
|
||||
return (iface_index == 0) ? this : NULL;
|
||||
return (iface_index == 0) ? this : nullptr;
|
||||
}
|
||||
|
||||
uavcan::uint8_t CanDriver::getNumIfaces() const
|
||||
@@ -363,9 +565,11 @@ uavcan::uint8_t CanDriver::getNumIfaces() const
|
||||
extern "C"
|
||||
{
|
||||
|
||||
void canRxCallback(uint8_t msg_obj_num)
|
||||
void canRxCallback(std::uint8_t msg_obj_num)
|
||||
{
|
||||
CCAN_MSG_OBJ_T msg_obj = CCAN_MSG_OBJ_T();
|
||||
using namespace uavcan_lpc11c24;
|
||||
|
||||
auto msg_obj = CCAN_MSG_OBJ_T();
|
||||
msg_obj.msgobj = msg_obj_num;
|
||||
LPC_CCAN_API->can_receive(&msg_obj);
|
||||
|
||||
@@ -392,23 +596,40 @@ void canRxCallback(uint8_t msg_obj_num)
|
||||
frame.dlc = msg_obj.dlc;
|
||||
uavcan::copy(msg_obj.data, msg_obj.data + msg_obj.dlc, frame.data);
|
||||
|
||||
uavcan_lpc11c24::rx_queue.push(frame, uavcan_lpc11c24::last_irq_utc_timestamp);
|
||||
uavcan_lpc11c24::had_activity = true;
|
||||
rx_queue.push(frame, last_irq_utc_timestamp);
|
||||
had_activity = true;
|
||||
}
|
||||
|
||||
void canTxCallback(uint8_t msg_obj_num)
|
||||
void canTxCallback(std::uint8_t msg_obj_num)
|
||||
{
|
||||
using namespace uavcan_lpc11c24;
|
||||
|
||||
(void)msg_obj_num;
|
||||
uavcan_lpc11c24::tx_free = true;
|
||||
uavcan_lpc11c24::had_activity = true;
|
||||
|
||||
tx_pending = false;
|
||||
had_activity = true;
|
||||
}
|
||||
|
||||
void canErrorCallback(uint32_t error_info)
|
||||
void canErrorCallback(std::uint32_t error_info)
|
||||
{
|
||||
(void)error_info;
|
||||
if (uavcan_lpc11c24::error_cnt < 0xFFFFFFFF)
|
||||
using namespace uavcan_lpc11c24;
|
||||
|
||||
// Updating the error counter
|
||||
if ((error_info != 0) && (error_cnt < 0xFFFFFFFFUL))
|
||||
{
|
||||
uavcan_lpc11c24::error_cnt++;
|
||||
error_cnt++;
|
||||
}
|
||||
|
||||
// Serving abort requests
|
||||
if (tx_pending && tx_abort_on_error)
|
||||
{
|
||||
tx_pending = false;
|
||||
tx_abort_on_error = false;
|
||||
|
||||
// Using the first interface, because this approach seems to be compliant with the BASIC mode (just in case)
|
||||
c_can::CAN.IF[0].CMDREQ = TxMessageObjectNumber;
|
||||
c_can::CAN.IF[0].CMDMSK.W = c_can::IF_CMDMSK_W_WR_RD; // Clearing IF_CMDMSK_W_TXRQST
|
||||
c_can::CAN.IF[0].MCTRL &= ~c_can::IF_MCTRL_TXRQST; // Clearing IF_MCTRL_TXRQST
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +637,10 @@ void CAN_IRQHandler();
|
||||
|
||||
void CAN_IRQHandler()
|
||||
{
|
||||
uavcan_lpc11c24::last_irq_utc_timestamp = uavcan_lpc11c24::clock::getUtcUSecFromCanInterrupt();
|
||||
using namespace uavcan_lpc11c24;
|
||||
|
||||
last_irq_utc_timestamp = clock::getUtcUSecFromCanInterrupt();
|
||||
|
||||
LPC_CCAN_API->isr();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,18 +17,18 @@ namespace
|
||||
bool initialized = false;
|
||||
bool utc_set = false;
|
||||
|
||||
int32_t utc_correction_usec_per_overflow_x16 = 0;
|
||||
int64_t prev_adjustment = 0;
|
||||
std::int32_t utc_correction_usec_per_overflow_x16 = 0;
|
||||
std::int64_t prev_adjustment = 0;
|
||||
|
||||
uint64_t time_mono = 0;
|
||||
uint64_t time_utc = 0;
|
||||
std::uint64_t time_mono = 0;
|
||||
std::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;
|
||||
constexpr std::uint32_t USecPerOverflow = 65536 * 2;
|
||||
constexpr std::int32_t MaxUtcSpeedCorrectionX16 = 100 * 16;
|
||||
|
||||
}
|
||||
|
||||
@@ -59,30 +59,34 @@ void init()
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t sampleFromCriticalSection(const volatile uint64_t* const value)
|
||||
static std::uint64_t sampleFromCriticalSection(const volatile std::uint64_t* const value)
|
||||
{
|
||||
const uint32_t reload = SysTick->LOAD + 1; // SysTick counts downwards, hence the value subtracted from reload
|
||||
const std::uint32_t reload = SysTick->LOAD + 1; // SysTick counts downwards, hence the value subtracted from reload
|
||||
|
||||
volatile uint64_t time = *value;
|
||||
volatile uint32_t cycles = reload - SysTick->VAL;
|
||||
volatile std::uint64_t time = *value;
|
||||
volatile std::uint32_t cycles = reload - SysTick->VAL;
|
||||
|
||||
if ((SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) == SCB_ICSR_PENDSTSET_Msk)
|
||||
{
|
||||
cycles = reload - SysTick->VAL;
|
||||
time += USecPerOverflow;
|
||||
}
|
||||
const uint32_t cycles_per_usec = SystemCoreClock / 1000000;
|
||||
const std::uint32_t cycles_per_usec = SystemCoreClock / 1000000;
|
||||
return time + (cycles / cycles_per_usec);
|
||||
}
|
||||
|
||||
uint64_t getUtcUSecFromCanInterrupt()
|
||||
std::uint64_t getUtcUSecFromCanInterrupt()
|
||||
{
|
||||
return utc_set ? sampleFromCriticalSection(&time_utc) : 0;
|
||||
}
|
||||
|
||||
uavcan::MonotonicTime getMonotonic()
|
||||
{
|
||||
uint64_t usec = 0;
|
||||
if (!initialized)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
std::uint64_t usec = 0;
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
usec = sampleFromCriticalSection(&time_mono);
|
||||
@@ -92,7 +96,11 @@ uavcan::MonotonicTime getMonotonic()
|
||||
|
||||
uavcan::UtcTime getUtc()
|
||||
{
|
||||
uint64_t usec = 0;
|
||||
if (!initialized)
|
||||
{
|
||||
fail();
|
||||
}
|
||||
std::uint64_t usec = 0;
|
||||
if (utc_set)
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
@@ -108,7 +116,7 @@ uavcan::UtcDuration getPrevUtcAdjustment()
|
||||
|
||||
void adjustUtc(uavcan::UtcDuration adjustment)
|
||||
{
|
||||
const int64_t adj_delta = adjustment.toUSec() - prev_adjustment; // This is the P term
|
||||
const std::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
|
||||
@@ -121,16 +129,16 @@ void adjustUtc(uavcan::UtcDuration adjustment)
|
||||
|
||||
if (adjustment.getAbs().toMSec() > 9 || !utc_set)
|
||||
{
|
||||
const int64_t adj_usec = adjustment.toUSec();
|
||||
const std::int64_t adj_usec = adjustment.toUSec();
|
||||
{
|
||||
CriticalSectionLocker locker;
|
||||
if ((adj_usec < 0) && uint64_t(-adj_usec) > time_utc)
|
||||
if ((adj_usec < 0) && std::uint64_t(-adj_usec) > time_utc)
|
||||
{
|
||||
time_utc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
time_utc = uint64_t(int64_t(time_utc) + adj_usec);
|
||||
time_utc = std::uint64_t(std::int64_t(time_utc) + adj_usec);
|
||||
}
|
||||
}
|
||||
if (!utc_set)
|
||||
@@ -170,7 +178,7 @@ void SysTick_Handler()
|
||||
if (utc_set)
|
||||
{
|
||||
// Values below 16 are ignored
|
||||
time_utc += uint64_t(int32_t(USecPerOverflow) + (utc_correction_usec_per_overflow_x16 / 16));
|
||||
time_utc += std::uint64_t(std::int32_t(USecPerOverflow) + (utc_correction_usec_per_overflow_x16 / 16));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,8 +4,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <chip.h>
|
||||
|
||||
/*
|
||||
* Compiler version check
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49
|
||||
# error "Use GCC 4.9 or newer"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
namespace uavcan_lpc11c24
|
||||
{
|
||||
|
||||
@@ -31,7 +42,7 @@ struct CriticalSectionLocker
|
||||
namespace clock
|
||||
{
|
||||
|
||||
uint64_t getUtcUSecFromCanInterrupt();
|
||||
std::uint64_t getUtcUSecFromCanInterrupt();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,8 @@ DEPDIR = $(BUILDDIR)/dep
|
||||
|
||||
DEF += -DNDEBUG -DCHIP_LPC11CXX -DCORE_M0 -DTHUMB_NO_INTERWORKING -U__STRICT_ANSI__
|
||||
|
||||
# Removing -fconserve-stack reduces code size a little
|
||||
FLAGS = -mthumb -mcpu=cortex-m0 -mno-thumb-interwork -flto -Os -g3 -Wall -Wextra -Werror -Wundef -ffunction-sections \
|
||||
-fdata-sections -fno-common -fno-exceptions -fno-unwind-tables -fno-stack-protector -fomit-frame-pointer \
|
||||
-ftracer -ftree-loop-distribute-patterns -frename-registers -freorder-blocks -fconserve-stack \
|
||||
-Wfloat-equal -Wconversion -Wsign-conversion -Wmissing-declarations
|
||||
|
||||
C_CPP_FLAGS = $(FLAGS) -MD -MP -MF $(DEPDIR)/$(@F).d
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* @brief LPC11xx UART chip driver
|
||||
*
|
||||
* @note
|
||||
* Copyright(C) NXP Semiconductors, 2012
|
||||
* All rights reserved.
|
||||
*
|
||||
* @par
|
||||
* Software that is described herein is for illustrative purposes only
|
||||
* which provides customers with programming information regarding the
|
||||
* LPC products. This software is supplied "AS IS" without any warranties of
|
||||
* any kind, and NXP Semiconductors and its licensor disclaim any and
|
||||
* all warranties, express or implied, including all implied warranties of
|
||||
* merchantability, fitness for a particular purpose and non-infringement of
|
||||
* intellectual property rights. NXP Semiconductors assumes no responsibility
|
||||
* or liability for the use of the software, conveys no license or rights under any
|
||||
* patent, copyright, mask work right, or any other intellectual property rights in
|
||||
* or to any products. NXP Semiconductors reserves the right to make changes
|
||||
* in the software without notification. NXP Semiconductors also makes no
|
||||
* representation or warranty that such application will be suitable for the
|
||||
* specified use without further testing or modification.
|
||||
*
|
||||
* @par
|
||||
* Permission to use, copy, modify, and distribute this software and its
|
||||
* documentation is hereby granted, under NXP Semiconductors' and its
|
||||
* licensor's relevant copyrights in the software, without fee, provided that it
|
||||
* is used in conjunction with NXP Semiconductors microcontrollers. This
|
||||
* copyright, permission, and disclaimer notice must appear in all copies of
|
||||
* this code.
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#if __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
# pragma GCC diagnostic ignored "-Wconversion"
|
||||
# pragma GCC diagnostic ignored "-Wmissing-declarations"
|
||||
# pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* Private types/enumerations/variables
|
||||
****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Public types/enumerations/variables
|
||||
****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Private functions
|
||||
****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* Public functions
|
||||
****************************************************************************/
|
||||
|
||||
/* Initializes the pUART peripheral */
|
||||
void Chip_UART_Init(LPC_USART_T *pUART)
|
||||
{
|
||||
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_UART0);
|
||||
Chip_Clock_SetUARTClockDiv(1);
|
||||
|
||||
/* Enable FIFOs by default, reset them */
|
||||
Chip_UART_SetupFIFOS(pUART, (UART_FCR_FIFO_EN | UART_FCR_RX_RS | UART_FCR_TX_RS));
|
||||
|
||||
/* Default 8N1, with DLAB disabled */
|
||||
Chip_UART_ConfigData(pUART, (UART_LCR_WLEN8 | UART_LCR_SBS_1BIT | UART_LCR_PARITY_DIS));
|
||||
|
||||
/* Disable fractional divider */
|
||||
pUART->FDR = 0x10;
|
||||
}
|
||||
|
||||
/* De-initializes the pUART peripheral */
|
||||
void Chip_UART_DeInit(LPC_USART_T *pUART)
|
||||
{
|
||||
Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_UART0);
|
||||
}
|
||||
|
||||
/* Transmit a byte array through the UART peripheral (non-blocking) */
|
||||
int Chip_UART_Send(LPC_USART_T *pUART, const void *data, int numBytes)
|
||||
{
|
||||
int sent = 0;
|
||||
uint8_t *p8 = (uint8_t *) data;
|
||||
|
||||
/* Send until the transmit FIFO is full or out of bytes */
|
||||
while ((sent < numBytes) &&
|
||||
((Chip_UART_ReadLineStatus(pUART) & UART_LSR_THRE) != 0)) {
|
||||
Chip_UART_SendByte(pUART, *p8);
|
||||
p8++;
|
||||
sent++;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
/* Transmit a byte array through the UART peripheral (blocking) */
|
||||
int Chip_UART_SendBlocking(LPC_USART_T *pUART, const void *data, int numBytes)
|
||||
{
|
||||
int pass, sent = 0;
|
||||
uint8_t *p8 = (uint8_t *) data;
|
||||
|
||||
while (numBytes > 0) {
|
||||
pass = Chip_UART_Send(pUART, p8, numBytes);
|
||||
numBytes -= pass;
|
||||
sent += pass;
|
||||
p8 += pass;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
/* Read data through the UART peripheral (non-blocking) */
|
||||
int Chip_UART_Read(LPC_USART_T *pUART, void *data, int numBytes)
|
||||
{
|
||||
int readBytes = 0;
|
||||
uint8_t *p8 = (uint8_t *) data;
|
||||
|
||||
/* Send until the transmit FIFO is full or out of bytes */
|
||||
while ((readBytes < numBytes) &&
|
||||
((Chip_UART_ReadLineStatus(pUART) & UART_LSR_RDR) != 0)) {
|
||||
*p8 = Chip_UART_ReadByte(pUART);
|
||||
p8++;
|
||||
readBytes++;
|
||||
}
|
||||
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
/* Read data through the UART peripheral (blocking) */
|
||||
int Chip_UART_ReadBlocking(LPC_USART_T *pUART, void *data, int numBytes)
|
||||
{
|
||||
int pass, readBytes = 0;
|
||||
uint8_t *p8 = (uint8_t *) data;
|
||||
|
||||
while (readBytes < numBytes) {
|
||||
pass = Chip_UART_Read(pUART, p8, numBytes);
|
||||
numBytes -= pass;
|
||||
readBytes += pass;
|
||||
p8 += pass;
|
||||
}
|
||||
|
||||
return readBytes;
|
||||
}
|
||||
|
||||
/* Determines and sets best dividers to get a target bit rate */
|
||||
uint32_t Chip_UART_SetBaud(LPC_USART_T *pUART, uint32_t baudrate)
|
||||
{
|
||||
uint32_t div, divh, divl, clkin;
|
||||
|
||||
/* Determine UART clock in rate without FDR */
|
||||
clkin = Chip_Clock_GetMainClockRate();
|
||||
div = clkin / (baudrate * 16);
|
||||
|
||||
/* High and low halves of the divider */
|
||||
divh = div / 256;
|
||||
divl = div - (divh * 256);
|
||||
|
||||
Chip_UART_EnableDivisorAccess(pUART);
|
||||
Chip_UART_SetDivisorLatches(pUART, divl, divh);
|
||||
Chip_UART_DisableDivisorAccess(pUART);
|
||||
|
||||
/* Fractional FDR alreadt setup for 1 in UART init */
|
||||
|
||||
return clkin / div;
|
||||
}
|
||||
|
||||
/* UART receive-only interrupt handler for ring buffers */
|
||||
void Chip_UART_RXIntHandlerRB(LPC_USART_T *pUART, RINGBUFF_T *pRB)
|
||||
{
|
||||
/* New data will be ignored if data not popped in time */
|
||||
while (Chip_UART_ReadLineStatus(pUART) & UART_LSR_RDR) {
|
||||
uint8_t ch = Chip_UART_ReadByte(pUART);
|
||||
RingBuffer_Insert(pRB, &ch);
|
||||
}
|
||||
}
|
||||
|
||||
/* UART transmit-only interrupt handler for ring buffers */
|
||||
void Chip_UART_TXIntHandlerRB(LPC_USART_T *pUART, RINGBUFF_T *pRB)
|
||||
{
|
||||
uint8_t ch;
|
||||
|
||||
/* Fill FIFO until full or until TX ring buffer is empty */
|
||||
while ((Chip_UART_ReadLineStatus(pUART) & UART_LSR_THRE) != 0 &&
|
||||
RingBuffer_Pop(pRB, &ch)) {
|
||||
Chip_UART_SendByte(pUART, ch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate a transmit ring buffer and start UART transmit */
|
||||
uint32_t Chip_UART_SendRB(LPC_USART_T *pUART, RINGBUFF_T *pRB, const void *data, int bytes)
|
||||
{
|
||||
uint32_t ret;
|
||||
uint8_t *p8 = (uint8_t *) data;
|
||||
|
||||
/* Don't let UART transmit ring buffer change in the UART IRQ handler */
|
||||
Chip_UART_IntDisable(pUART, UART_IER_THREINT);
|
||||
|
||||
/* Move as much data as possible into transmit ring buffer */
|
||||
ret = RingBuffer_InsertMult(pRB, p8, bytes);
|
||||
Chip_UART_TXIntHandlerRB(pUART, pRB);
|
||||
|
||||
/* Add additional data to transmit ring buffer if possible */
|
||||
ret += RingBuffer_InsertMult(pRB, (p8 + ret), (bytes - ret));
|
||||
|
||||
/* Enable UART transmit interrupt */
|
||||
Chip_UART_IntEnable(pUART, UART_IER_THREINT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Copy data from a receive ring buffer */
|
||||
int Chip_UART_ReadRB(LPC_USART_T *pUART, RINGBUFF_T *pRB, void *data, int bytes)
|
||||
{
|
||||
(void) pUART;
|
||||
|
||||
return RingBuffer_PopMult(pRB, (uint8_t *) data, bytes);
|
||||
}
|
||||
|
||||
/* UART receive/transmit interrupt handler for ring buffers */
|
||||
void Chip_UART_IRQRBHandler(LPC_USART_T *pUART, RINGBUFF_T *pRXRB, RINGBUFF_T *pTXRB)
|
||||
{
|
||||
/* Handle transmit interrupt if enabled */
|
||||
if (pUART->IER & UART_IER_THREINT) {
|
||||
Chip_UART_TXIntHandlerRB(pUART, pTXRB);
|
||||
|
||||
/* Disable transmit interrupt if the ring buffer is empty */
|
||||
if (RingBuffer_IsEmpty(pTXRB)) {
|
||||
Chip_UART_IntDisable(pUART, UART_IER_THREINT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle receive interrupt */
|
||||
Chip_UART_RXIntHandlerRB(pUART, pRXRB);
|
||||
}
|
||||
|
||||
/* Determines and sets best dividers to get a target baud rate */
|
||||
uint32_t Chip_UART_SetBaudFDR(LPC_USART_T *pUART, uint32_t baudrate)
|
||||
|
||||
{
|
||||
uint32_t uClk = 0;
|
||||
uint32_t dval = 0;
|
||||
uint32_t mval = 0;
|
||||
uint32_t dl = 0;
|
||||
uint32_t rate16 = 16 * baudrate;
|
||||
uint32_t actualRate = 0;
|
||||
|
||||
/* Get Clock rate */
|
||||
uClk = Chip_Clock_GetMainClockRate();
|
||||
|
||||
/* The fractional is calculated as (PCLK % (16 * Baudrate)) / (16 * Baudrate)
|
||||
* Let's make it to be the ratio DivVal / MulVal
|
||||
*/
|
||||
dval = uClk % rate16;
|
||||
|
||||
/* The PCLK / (16 * Baudrate) is fractional
|
||||
* => dval = pclk % rate16
|
||||
* mval = rate16;
|
||||
* now mormalize the ratio
|
||||
* dval / mval = 1 / new_mval
|
||||
* new_mval = mval / dval
|
||||
* new_dval = 1
|
||||
*/
|
||||
if (dval > 0) {
|
||||
mval = rate16 / dval;
|
||||
dval = 1;
|
||||
|
||||
/* In case mval still bigger then 4 bits
|
||||
* no adjustment require
|
||||
*/
|
||||
if (mval > 12) {
|
||||
dval = 0;
|
||||
}
|
||||
}
|
||||
dval &= 0xf;
|
||||
mval &= 0xf;
|
||||
dl = uClk / (rate16 + rate16 *dval / mval);
|
||||
|
||||
/* Update UART registers */
|
||||
Chip_UART_EnableDivisorAccess(pUART);
|
||||
Chip_UART_SetDivisorLatches(pUART, UART_LOAD_DLL(dl), UART_LOAD_DLM(dl));
|
||||
Chip_UART_DisableDivisorAccess(pUART);
|
||||
|
||||
/* Set best fractional divider */
|
||||
pUART->FDR = (UART_FDR_MULVAL(mval) | UART_FDR_DIVADDVAL(dval));
|
||||
|
||||
/* Return actual baud rate */
|
||||
actualRate = uClk / (16 * dl + 16 * dl * dval / mval);
|
||||
return actualRate;
|
||||
}
|
||||
@@ -3,21 +3,74 @@
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <board.hpp>
|
||||
#include <chip.h>
|
||||
#include <uavcan_lpc11c24/uavcan_lpc11c24.hpp>
|
||||
#include <uavcan/protocol/global_time_sync_slave.hpp>
|
||||
#include <uavcan/protocol/dynamic_node_id_client.hpp>
|
||||
#include <uavcan/protocol/logger.hpp>
|
||||
|
||||
/*
|
||||
* GCC 4.9 cannot generate a working binary with higher optimization levels, although
|
||||
* rest of the firmware can be compiled with -Os.
|
||||
* GCC 4.8 and earlier don't work at all on this firmware.
|
||||
*/
|
||||
#if __GNUC__
|
||||
# pragma GCC optimize 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function re-defines the standard ::rand(), which is used by the class uavcan::DynamicNodeIDClient.
|
||||
* Redefinition is normally not needed, but GCC 4.9 tends to generate broken binaries if it is not redefined.
|
||||
*/
|
||||
int rand()
|
||||
{
|
||||
static int x = 1;
|
||||
x = x * 48271 % 2147483647;
|
||||
return x;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef uavcan::Node<2800> Node;
|
||||
static constexpr unsigned NodeMemoryPoolSize = 2800;
|
||||
|
||||
Node& getNode()
|
||||
/**
|
||||
* This is a compact, reentrant and thread-safe replacement to standard llto().
|
||||
* It returns the string by value, no extra storage is needed.
|
||||
*/
|
||||
typename uavcan::MakeString<22>::Type intToString(long long n)
|
||||
{
|
||||
static Node node(uavcan_lpc11c24::CanDriver::instance(), uavcan_lpc11c24::SystemClock::instance());
|
||||
char buf[24] = {};
|
||||
const short sign = (n < 0) ? -1 : 1;
|
||||
if (sign < 0)
|
||||
{
|
||||
n = -n;
|
||||
}
|
||||
unsigned pos = 0;
|
||||
do
|
||||
{
|
||||
buf[pos++] = char(n % 10 + '0');
|
||||
}
|
||||
while ((n /= 10) > 0);
|
||||
if (sign < 0)
|
||||
{
|
||||
buf[pos++] = '-';
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
for (unsigned i = 0, j = pos - 1U; i < j; i++, j--)
|
||||
{
|
||||
std::swap(buf[i], buf[j]);
|
||||
}
|
||||
return static_cast<const char*>(buf);
|
||||
}
|
||||
|
||||
uavcan::Node<NodeMemoryPoolSize>& getNode()
|
||||
{
|
||||
static uavcan::Node<NodeMemoryPoolSize> node(uavcan_lpc11c24::CanDriver::instance(),
|
||||
uavcan_lpc11c24::SystemClock::instance());
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -33,29 +86,63 @@ uavcan::Logger& getLogger()
|
||||
return logger;
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void die()
|
||||
uavcan::NodeID performDynamicNodeIDAllocation()
|
||||
{
|
||||
while (true) { }
|
||||
uavcan::DynamicNodeIDClient client(getNode());
|
||||
|
||||
const int client_start_res = client.start(getNode().getHardwareVersion().unique_id);
|
||||
if (client_start_res < 0)
|
||||
{
|
||||
board::die();
|
||||
}
|
||||
|
||||
while (!client.isAllocationComplete())
|
||||
{
|
||||
board::resetWatchdog();
|
||||
(void)getNode().spin(uavcan::MonotonicDuration::fromMSec(100));
|
||||
}
|
||||
|
||||
return client.getAllocatedNodeID();
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((noinline))
|
||||
#endif
|
||||
void init()
|
||||
{
|
||||
board::resetWatchdog();
|
||||
board::syslog("Boot\r\n");
|
||||
|
||||
if (uavcan_lpc11c24::CanDriver::instance().init(1000000) < 0)
|
||||
board::setErrorLed(false);
|
||||
board::setStatusLed(true);
|
||||
|
||||
/*
|
||||
* Configuring the clock - this must be done before the CAN controller is initialized
|
||||
*/
|
||||
uavcan_lpc11c24::clock::init();
|
||||
|
||||
/*
|
||||
* Configuring the CAN controller
|
||||
*/
|
||||
std::uint32_t bit_rate = 0;
|
||||
while (bit_rate == 0)
|
||||
{
|
||||
die();
|
||||
board::syslog("CAN auto bitrate...\r\n");
|
||||
bit_rate = uavcan_lpc11c24::CanDriver::detectBitRate(&board::resetWatchdog);
|
||||
}
|
||||
board::syslog("Bitrate: ");
|
||||
board::syslog(intToString(bit_rate).c_str());
|
||||
board::syslog("\r\n");
|
||||
|
||||
if (uavcan_lpc11c24::CanDriver::instance().init(bit_rate) < 0)
|
||||
{
|
||||
board::die();
|
||||
}
|
||||
|
||||
board::syslog("CAN init ok\r\n");
|
||||
|
||||
board::resetWatchdog();
|
||||
|
||||
getNode().setNodeID(72);
|
||||
/*
|
||||
* Configuring the node
|
||||
*/
|
||||
getNode().setName("org.uavcan.lpc11c24_test");
|
||||
|
||||
uavcan::protocol::SoftwareVersion swver;
|
||||
@@ -73,55 +160,69 @@ void init()
|
||||
|
||||
board::resetWatchdog();
|
||||
|
||||
while (getNode().start() < 0)
|
||||
/*
|
||||
* Starting the node and performing dynamic node ID allocation
|
||||
*/
|
||||
if (getNode().start() < 0)
|
||||
{
|
||||
board::die();
|
||||
}
|
||||
|
||||
board::syslog("Node ID allocation...\r\n");
|
||||
|
||||
getNode().setNodeID(performDynamicNodeIDAllocation());
|
||||
|
||||
board::syslog("Node ID ");
|
||||
board::syslog(intToString(getNode().getNodeID().get()).c_str());
|
||||
board::syslog("\r\n");
|
||||
|
||||
board::resetWatchdog();
|
||||
|
||||
while (getTimeSyncSlave().start() < 0)
|
||||
/*
|
||||
* Example filter configuration.
|
||||
* Can be removed safely.
|
||||
*/
|
||||
{
|
||||
constexpr unsigned NumFilters = 3;
|
||||
uavcan::CanFilterConfig filters[NumFilters];
|
||||
|
||||
// Acepting all service transfers addressed to us
|
||||
filters[0].id = (unsigned(getNode().getNodeID().get()) << 8) | (1U << 7) | uavcan::CanFrame::FlagEFF;
|
||||
filters[0].mask = 0x7F80 | uavcan::CanFrame::FlagEFF;
|
||||
|
||||
// Accepting time sync messages
|
||||
filters[1].id = (4U << 8) | uavcan::CanFrame::FlagEFF;
|
||||
filters[1].mask = 0xFFFF80 | uavcan::CanFrame::FlagEFF;
|
||||
|
||||
// Accepting zero CAN ID (just for the sake of testing)
|
||||
filters[2].id = 0 | uavcan::CanFrame::FlagEFF;
|
||||
filters[2].mask = uavcan::CanFrame::MaskExtID | uavcan::CanFrame::FlagEFF;
|
||||
|
||||
if (uavcan_lpc11c24::CanDriver::instance().configureFilters(filters, NumFilters) < 0)
|
||||
{
|
||||
board::syslog("Filter init failed\r\n");
|
||||
board::die();
|
||||
}
|
||||
}
|
||||
|
||||
while (getLogger().init() < 0)
|
||||
/*
|
||||
* Initializing other libuavcan-related objects
|
||||
*/
|
||||
if (getTimeSyncSlave().start() < 0)
|
||||
{
|
||||
board::die();
|
||||
}
|
||||
|
||||
if (getLogger().init() < 0)
|
||||
{
|
||||
board::die();
|
||||
}
|
||||
|
||||
getLogger().setLevel(uavcan::protocol::debug::LogLevel::DEBUG);
|
||||
|
||||
board::resetWatchdog();
|
||||
}
|
||||
|
||||
void reverse(char* s)
|
||||
{
|
||||
for (int i = 0, j = int(std::strlen(s)) - 1; i < j; i++, j--)
|
||||
{
|
||||
const char c = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = c;
|
||||
}
|
||||
}
|
||||
|
||||
void lltoa(long long n, char buf[24])
|
||||
{
|
||||
const short sign = (n < 0) ? -1 : 1;
|
||||
if (sign < 0)
|
||||
{
|
||||
n = -n;
|
||||
}
|
||||
unsigned i = 0;
|
||||
do
|
||||
{
|
||||
buf[i++] = char(n % 10 + '0');
|
||||
}
|
||||
while ((n /= 10) > 0);
|
||||
if (sign < 0)
|
||||
{
|
||||
buf[i++] = '-';
|
||||
}
|
||||
buf[i] = '\0';
|
||||
reverse(buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
@@ -143,16 +244,31 @@ int main()
|
||||
{
|
||||
prev_log_at = ts;
|
||||
|
||||
// We don't want to use formatting functions provided by libuavcan because they rely on std::snprintf()
|
||||
char buf[24];
|
||||
lltoa(uavcan_lpc11c24::clock::getPrevUtcAdjustment().toUSec(), buf);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
/*
|
||||
* CAN bus off state monitoring
|
||||
*/
|
||||
if (uavcan_lpc11c24::CanDriver::instance().isInBusOffState())
|
||||
{
|
||||
board::syslog("CAN BUS OFF\r\n");
|
||||
}
|
||||
|
||||
// ...hence we need to construct the message manually:
|
||||
/*
|
||||
* CAN error counter, for debugging purposes
|
||||
*/
|
||||
board::syslog("CAN errors: ");
|
||||
board::syslog(intToString(static_cast<long long>(uavcan_lpc11c24::CanDriver::instance().getErrorCount())).c_str());
|
||||
board::syslog(" ");
|
||||
board::syslog(intToString(uavcan_lpc11c24::CanDriver::instance().getRxQueueOverflowCount()).c_str());
|
||||
board::syslog("\r\n");
|
||||
|
||||
/*
|
||||
* We don't want to use formatting functions provided by libuavcan because they rely on std::snprintf(),
|
||||
* so we need to construct the message manually:
|
||||
*/
|
||||
uavcan::protocol::debug::LogMessage logmsg;
|
||||
logmsg.level.value = uavcan::protocol::debug::LogLevel::INFO;
|
||||
logmsg.source = "app";
|
||||
logmsg.text = buf;
|
||||
logmsg.text = intToString(uavcan_lpc11c24::clock::getPrevUtcAdjustment().toUSec()).c_str();
|
||||
(void)getLogger().log(logmsg);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,24 +9,26 @@
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
|
||||
#define PDRUNCFGUSEMASK 0x0000ED00
|
||||
#define PDRUNCFGMASKTMP 0x000000FF
|
||||
static constexpr unsigned long PDRUNCFGUSEMASK = 0x0000ED00U;
|
||||
static constexpr unsigned long PDRUNCFGMASKTMP = 0x000000FFU;
|
||||
|
||||
const uint32_t OscRateIn = 12000000; ///< External crystal
|
||||
const uint32_t ExtRateIn = 0;
|
||||
const std::uint32_t OscRateIn = 12000000; ///< External crystal
|
||||
const std::uint32_t ExtRateIn = 0;
|
||||
|
||||
uint32_t SystemCoreClock = 12000000; ///< Initialized to default clock value, will be changed on init
|
||||
std::uint32_t SystemCoreClock = 12000000; ///< Initialized to default clock value, will be changed on init
|
||||
|
||||
namespace board
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
const unsigned ErrorLedPort = 1;
|
||||
const unsigned ErrorLedPin = 10;
|
||||
constexpr unsigned TargetSystemCoreClock = 48000000;
|
||||
|
||||
const unsigned StatusLedPort = 1;
|
||||
const unsigned StatusLedPin = 11;
|
||||
constexpr unsigned ErrorLedPort = 1;
|
||||
constexpr unsigned ErrorLedPin = 10;
|
||||
|
||||
constexpr unsigned StatusLedPort = 1;
|
||||
constexpr unsigned StatusLedPin = 11;
|
||||
|
||||
struct PinMuxGroup
|
||||
{
|
||||
@@ -34,10 +36,11 @@ struct PinMuxGroup
|
||||
unsigned modefunc : 24;
|
||||
};
|
||||
|
||||
const PinMuxGroup pinmux[] =
|
||||
constexpr PinMuxGroup pinmux[] =
|
||||
{
|
||||
{ IOCON_PIO1_10, IOCON_FUNC0 | IOCON_MODE_INACT }, // Error LED
|
||||
{ IOCON_PIO1_11, IOCON_FUNC0 | IOCON_MODE_INACT } // Status LED
|
||||
{ IOCON_PIO1_10, IOCON_FUNC0 | IOCON_MODE_INACT }, // Error LED
|
||||
{ IOCON_PIO1_11, IOCON_FUNC0 | IOCON_MODE_INACT }, // Status LED
|
||||
{ IOCON_PIO1_7, IOCON_FUNC1 | IOCON_HYS_EN | IOCON_MODE_PULLUP }, // UART_TXD
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +95,7 @@ void initClock()
|
||||
|
||||
SystemCoreClock = Chip_Clock_GetSystemClockRate();
|
||||
|
||||
while (SystemCoreClock != 48000000) { } // Loop forever if the clock failed to initialize properly
|
||||
while (SystemCoreClock != TargetSystemCoreClock) { } // Loop forever if the clock failed to initialize properly
|
||||
}
|
||||
|
||||
void initGpio()
|
||||
@@ -109,6 +112,13 @@ void initGpio()
|
||||
LPC_GPIO[StatusLedPort].DIR |= 1 << StatusLedPin;
|
||||
}
|
||||
|
||||
void initUart()
|
||||
{
|
||||
Chip_UART_Init(LPC_USART);
|
||||
Chip_UART_SetBaud(LPC_USART, 115200);
|
||||
Chip_UART_TXEnable(LPC_USART);
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
Chip_SYSCTL_SetBODLevels(SYSCTL_BODRSTLVL_2_06V, SYSCTL_BODINTVAL_RESERVED1);
|
||||
@@ -117,16 +127,32 @@ void init()
|
||||
initWatchdog();
|
||||
initClock();
|
||||
initGpio();
|
||||
initUart();
|
||||
|
||||
resetWatchdog();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void die()
|
||||
{
|
||||
static const volatile unsigned& DHCSR = *reinterpret_cast<unsigned*>(0xE000EDF0U);
|
||||
|
||||
syslog("FATAL\r\n");
|
||||
|
||||
while (true)
|
||||
{
|
||||
if ((DHCSR & 1U) != 0)
|
||||
{
|
||||
__asm volatile ("bkpt #0\n"); // Break into the debugger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if __GNUC__
|
||||
__attribute__((optimize(0))) // Optimization must be disabled lest it hardfaults in the IAP call
|
||||
#endif
|
||||
void readUniqueID(uint8_t out_uid[UniqueIDSize])
|
||||
void readUniqueID(std::uint8_t out_uid[UniqueIDSize])
|
||||
{
|
||||
unsigned aligned_array[4] = {}; // out_uid may be unaligned, so we need to use temp array
|
||||
unsigned iap_command = 58;
|
||||
@@ -149,6 +175,11 @@ void resetWatchdog()
|
||||
Chip_WWDT_Feed(LPC_WWDT);
|
||||
}
|
||||
|
||||
void syslog(const char* msg)
|
||||
{
|
||||
Chip_UART_SendBlocking(LPC_USART, msg, static_cast<int>(std::strlen(msg)));
|
||||
}
|
||||
|
||||
} // namespace board
|
||||
|
||||
extern "C"
|
||||
|
||||
@@ -2,18 +2,31 @@
|
||||
* Pavel Kirienko, 2014 <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace board
|
||||
{
|
||||
|
||||
static const unsigned UniqueIDSize = 16;
|
||||
#if __GNUC__
|
||||
__attribute__((noreturn))
|
||||
#endif
|
||||
void die();
|
||||
|
||||
void readUniqueID(uint8_t out_uid[UniqueIDSize]);
|
||||
static constexpr unsigned UniqueIDSize = 16;
|
||||
|
||||
/**
|
||||
* Reads the globally unique 128-bit hardware ID from the MCU.
|
||||
*/
|
||||
void readUniqueID(std::uint8_t out_uid[UniqueIDSize]);
|
||||
|
||||
void setStatusLed(bool state);
|
||||
void setErrorLed(bool state);
|
||||
|
||||
void resetWatchdog();
|
||||
|
||||
/**
|
||||
* Sends the string to UART.
|
||||
*/
|
||||
void syslog(const char* msg);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
|
||||
void* __dso_handle;
|
||||
|
||||
void* operator new(size_t)
|
||||
void* operator new(std::size_t)
|
||||
{
|
||||
std::abort();
|
||||
return reinterpret_cast<void*>(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void* operator new[](size_t)
|
||||
void* operator new[](std::size_t)
|
||||
{
|
||||
std::abort();
|
||||
return reinterpret_cast<void*>(0xFFFFFFFF);
|
||||
|
||||
Reference in New Issue
Block a user