Very basic stuff: CAN driver iface, linked list, system clock interface, tests

This commit is contained in:
Pavel Kirienko
2014-01-25 20:06:43 +04:00
parent 0d2f1da2a2
commit b685173185
6 changed files with 439 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
/*
* CAN bus driver interface.
* Copyright (C) 2013 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cassert>
#include <cstdint>
#include <cstring>
namespace uavcan
{
/**
* Raw CAN frame, as passed to/from the CAN driver.
*/
struct CanFrame
{
static const uint32_t MASK_STDID = 0x000007FF;
static const uint32_t MASK_EXTID = 0x1FFFFFFF;
static const uint32_t FLAG_EFF = 1 << 31; ///< Extended frame format
static const uint32_t FLAG_RTR = 1 << 30; ///< Remote transmission request
uint32_t id; ///< CAN ID with flags (above)
uint8_t data[8];
uint8_t dlc; ///< Data Length Code
Frame()
: id(0)
, dlc(0)
{ }
Frame(uint32_t id, const uint8_t* data, uint8_t dlc)
: id(id)
, dlc(dlc)
{
assert(data && dlc <= 8);
std::memmove(this->data, data, dlc);
}
bool isExtended() const { return id & FLAG_EFF; }
bool isRemoteTransmissionRequest() const { return id & FLAG_RTR; }
};
/**
* CAN hardware filter config struct. @ref ICanDriver::filter().
*/
struct CanFilterConfig
{
uint32_t id;
uint32_t mask;
};
/**
* Single non-blocking CAN interface.
*/
class ICanIface
{
public:
virtual ~ICanIface() { }
/**
* Non-blocking transmission.
* If the frame wasn't transmitted upon TX timeout expiration, the driver should discard it.
* @return 1 = one frame transmitted, 0 = TX buffer full, negative for error.
*/
virtual int send(const Frame& frame, uint64_t tx_timeout_usec) = 0;
/**
* Non-blocking reception.
* out_utc_timestamp_usec must be provided by the driver, ideally by the hardware CAN controller; 0 if unknown.
* @return 1 = one frame received, 0 = RX buffer empty, negative for error.
*/
virtual int receive(Frame& out_frame, uint64_t& out_utc_timestamp_usec) = 0;
/**
* Configure the hardware CAN filters. @ref CanFilterConfig.
* @return 0 = success, negative for error.
*/
virtual int filter(const CanFilterConfig* filter_configs, int num_configs) = 0;
/**
* Number of available hardware filters.
*/
virtual int getNumFilters() const = 0;
/**
* Continuously incrementing counter of detected hardware errors.
*/
virtual uint64_t getNumErrors() const = 0;
};
/**
* Generic CAN driver.
*/
class ICanDriver
{
public:
virtual ~ICanDriver() { }
/**
* Returns the interface by index, or null pointer if the index is out of range.
*/
virtual ICanIface* getIface(int iface_index) = 0;
/**
* Total number of available CAN interfaces.
*/
virtual int getNumIfaces() const = 0;
/**
* Block until the blocking timeout expires, or one of the specified interfaces becomes available for read or write.
* Iface masks will be modified by the driver to indicate which exactly interfaces are available for IO.
* Bit position in the masks defines interface index.
* @param [in,out] inout_write_iface_mask Mask indicating which interfaces are needed/available to write.
* @param [in,out] inout_read_iface_mask Same as above for reading.
* @param [in] timeout_usec Zero means non-blocking operation.
* @return Positive number of ready interfaces or negative error code.
*/
virtual int select(int& inout_write_iface_mask, int& inout_read_iface_mask, uint64_t timeout_usec) = 0;
};
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Singly-linked list.
* Copyright (C) 2013 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cstdlib>
#include <cassert>
namespace uavcan
{
/**
* Classes that are supposed to be linked-listed should derive this.
*/
template <typename T>
class LinkedListNode
{
T* next_;
protected:
LinkedListNode()
: next_(NULL)
{ }
public:
T* getNextListNode() const { return next_; }
void setNextListNode(T* node)
{
next_ = node;
}
};
/**
* Linked list root.
*/
template <typename T>
class LinkedListRoot
{
T* root_;
public:
LinkedListRoot()
: root_(NULL)
{ }
T* get() const { return root_; }
unsigned int length() const
{
T* node = root_;
unsigned int cnt = 0;
while (node)
{
cnt++;
node = node->getNextListNode();
}
return cnt;
}
void insert(T* node)
{
remove(node); // Making sure there will be no loops
node->setNextListNode(root_);
root_ = node;
}
bool remove(const T* node)
{
if (root_ == NULL || node == NULL)
return false;
if (root_ == node)
{
root_ = root_->getNextListNode();
return true;
}
else
{
T* p = root_;
while (p->getNextListNode())
{
if (p->getNextListNode() == node)
{
p->setNextListNode(p->getNextListNode()->getNextListNode());
return true;
}
p = p->getNextListNode();
}
return false;
}
}
template <typename Fun>
void map(Fun& fun)
{
T* node = root_;
while (node)
{
fun(node);
node = node->getNextListNode();
}
}
};
}
+44
View File
@@ -0,0 +1,44 @@
/*
* System clock driver interface.
* Copyright (C) 2013 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cstdint>
namespace uavcan
{
/**
* System clock interface - monotonic and UTC.
* Note that this library uses microseconds for all time related values (timestamps, deadlines, delays),
* if not explicitly specified otherwise.
* All time values are represented as 64-bit integers.
*/
class ISystemClock
{
/**
* Monototic system clock in microseconds.
* This shall never jump during UTC timestamp adjustments; the base time is irrelevant.
* On POSIX systems use clock_gettime() with CLOCK_MONOTONIC.
*/
virtual uint64_t getMonotonicMicroseconds() const = 0;
/**
* UTC clock in microseconds.
* This can jump when the UTC timestamp is being adjusted.
* Return 0 if the UTC time is not available yet (e.g. the device just started up with no battery clock).
* On POSIX systems use gettimeofday().
*/
virtual uint64_t getUtcMicroseconds() const = 0;
/**
* Set the UTC system clock.
* @param [in] timestamp New UTC timestamp. Avoid when possible.
* @param [in] offset Current UTC time error. More precise than just timestamp, use it if possible.
* For POSIX refer to adjtime(), settimeofday().
*/
virtual void adjustUtcMicroseconds(uint64_t new_timestamp_usec, int64_t offset_usec) = 0;
};
}