mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-11-01 06:11:35 +08:00
Very basic stuff: CAN driver iface, linked list, system clock interface, tests
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Default build type is RelWithDebInfo" FORCE)
|
||||
endif()
|
||||
|
||||
project(libuavcan)
|
||||
|
||||
#
|
||||
# -DUAVCAN_DEBUG=1 enables the tracing feature that writes debug info into stdout.
|
||||
# Normally this feature should be used only for library development.
|
||||
#
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -g")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O1 -DNDEBUG")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g3 -DUAVCAN_DEBUG=1")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++03 -Wall -Wextra -Werror -pedantic")
|
||||
|
||||
include_directories(include)
|
||||
|
||||
#
|
||||
# libuavcan
|
||||
#
|
||||
# TODO
|
||||
|
||||
#
|
||||
# Test
|
||||
#
|
||||
find_package(GTest QUIET)
|
||||
if (GTEST_FOUND)
|
||||
find_package(Threads REQUIRED)
|
||||
include_directories(${GTEST_INCLUDE_DIRS})
|
||||
|
||||
file(GLOB_RECURSE TEST_CXX_FILES RELATIVE ${CMAKE_SOURCE_DIR} "test/*.cpp")
|
||||
add_executable(libuavcan_test ${TEST_CXX_FILES})
|
||||
#add_dependencies(libuavcan_test libuavcan)
|
||||
|
||||
set_target_properties(libuavcan_test PROPERTIES
|
||||
COMPILE_FLAGS "-Wno-unused-parameter -Wno-unused-function"
|
||||
)
|
||||
|
||||
target_link_libraries(libuavcan_test ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
|
||||
#target_link_libraries(libuavcan_test ${CMAKE_BINARY_DIR}/libuavcan.so)
|
||||
|
||||
add_custom_command(TARGET libuavcan_test POST_BUILD
|
||||
COMMAND "./libuavcan_test"
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
else (GTEST_FOUND)
|
||||
message(">> Google test is not found, you will not be able to run libuavcan_test")
|
||||
endif (GTEST_FOUND)
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/linked_list.hpp>
|
||||
|
||||
struct ListItem : uavcan::LinkedListNode<ListItem>
|
||||
{
|
||||
int value;
|
||||
ListItem() : value(0) { }
|
||||
};
|
||||
|
||||
TEST(LinkedList, Basic)
|
||||
{
|
||||
uavcan::LinkedListRoot<ListItem> root;
|
||||
|
||||
/*
|
||||
* Insert/remove
|
||||
*/
|
||||
EXPECT_EQ(0, root.length());
|
||||
|
||||
ListItem item1;
|
||||
root.insert(&item1);
|
||||
root.insert(&item1); // Insert twice - second will be ignored
|
||||
EXPECT_EQ(1, root.length());
|
||||
|
||||
EXPECT_TRUE(root.remove(&item1));
|
||||
EXPECT_FALSE(root.remove(&item1));
|
||||
EXPECT_EQ(0, root.length());
|
||||
|
||||
ListItem items[3];
|
||||
root.insert(&item1);
|
||||
root.insert(items + 0);
|
||||
root.insert(items + 1);
|
||||
root.insert(items + 2);
|
||||
EXPECT_EQ(4, root.length());
|
||||
|
||||
/*
|
||||
* Order persistence
|
||||
*/
|
||||
items[0].value = 10;
|
||||
items[1].value = 11;
|
||||
items[2].value = 12;
|
||||
const int expected_values[] = {12, 11, 10, 0};
|
||||
ListItem* node = root.get();
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
EXPECT_EQ(expected_values[i], node->value);
|
||||
node = node->getNextListNode();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(root.remove(items + 0));
|
||||
EXPECT_TRUE(root.remove(items + 2));
|
||||
EXPECT_FALSE(root.remove(items + 2));
|
||||
EXPECT_EQ(2, root.length());
|
||||
|
||||
const int expected_values2[] = {11, 0};
|
||||
node = root.get();
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
EXPECT_EQ(expected_values2[i], node->value);
|
||||
node = node->getNextListNode();
|
||||
}
|
||||
|
||||
root.insert(items + 2);
|
||||
EXPECT_EQ(3, root.length());
|
||||
EXPECT_EQ(12, root.get()->value);
|
||||
|
||||
/*
|
||||
* Emptying
|
||||
*/
|
||||
EXPECT_TRUE(root.remove(&item1));
|
||||
EXPECT_FALSE(root.remove(items + 0));
|
||||
EXPECT_TRUE(root.remove(items + 1));
|
||||
EXPECT_TRUE(root.remove(items + 2));
|
||||
EXPECT_EQ(0, root.length());
|
||||
}
|
||||
|
||||
struct Summator
|
||||
{
|
||||
long sum;
|
||||
Summator() : sum(0) { }
|
||||
void operator()(const ListItem* item)
|
||||
{
|
||||
sum += item->value;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(LinkedList, Predicate)
|
||||
{
|
||||
uavcan::LinkedListRoot<ListItem> root;
|
||||
ListItem items[5];
|
||||
for (int i = 0 ; i < 5; i++)
|
||||
{
|
||||
EXPECT_FALSE(root.remove(items + i)); // Just to make sure that there's no such item
|
||||
root.insert(items + i);
|
||||
items[i].value = i;
|
||||
}
|
||||
EXPECT_EQ(5, root.length());
|
||||
|
||||
Summator sum;
|
||||
root.map(sum);
|
||||
EXPECT_EQ(10, sum.sum);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user