Merge branch 'master' into lpc11c24

This commit is contained in:
Pavel Kirienko
2015-10-11 18:36:34 +03:00
37 changed files with 854 additions and 312 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
lib*.so
lib*.so.*
*.a
build
build*/
.dep
__pycache__
*.pyc
+68 -12
View File
@@ -4,24 +4,80 @@
cmake_minimum_required(VERSION 2.8)
if(DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "Debug Release RelWithDebInfo MinSizeRel")
else()
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Debug Release RelWithDebInfo MinSizeRel")
project(uavcan C CXX)
#
# Build options
#
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(DEFAULT_UAVCAN_PLATFORM "linux")
endif()
project(uavcan)
# options are listed in a table format below
set(opts
# name: type: default value: string options list : description
"CMAKE_BUILD_TYPE:STRING:RelWithDebInfo:Debug Release RelWithDebInfo MinSizeRel:Build type."
"CMAKE_CXX_FLAGS:STRING:::C++ flags."
"CMAKE_C_FLAGS:STRING:::C flags."
"UAVCAN_USE_CPP03:BOOL:OFF::Use C++03 standard."
"UAVCAN_PLATFORM:STRING:generic:generic linux stm32:Platform."
)
foreach(_opt ${opts})
# arguments are : delimited
string(REPLACE ":" ";" _opt ${_opt})
list(GET _opt 0 _name)
list(GET _opt 1 _type)
list(GET _opt 2 _default)
list(GET _opt 3 _options)
list(GET _opt 4 _descr)
# options are space delimited
string(REPLACE " " ";" _options "${_options}")
# if a default has not already been defined, use default from table
if(NOT DEFINED DEFAULT_${_name})
set(DEFAULT_${_name} ${_default})
endif()
# option has not been set already or it is empty, set it with the default
if(NOT DEFINED ${_name} OR ${_name} STREQUAL "")
set(${_name} ${DEFAULT_${_name}})
endif()
# create a cache from the variable and force it to set
message(STATUS "${_name}\t: ${${_name}} : ${_descr}")
set("${_name}" "${${_name}}" CACHE "${_type}" "${_descr}" FORCE)
# if an options list is provided for the cache, set it
if("${_type}" STREQUAL "STRING" AND NOT "${_options}" STREQUAL "")
set_property(CACHE ${_name} PROPERTY STRINGS ${_options})
endif()
endforeach()
#
# libuavcan
# Set flags
#
include_directories(
./libuavcan/include/
./libuavcan/include/dsdlc_generated
)
#
# Install
#
# DSDL definitions
install(DIRECTORY dsdl DESTINATION share/uavcan)
#
# Subdirectories
#
# library
add_subdirectory(libuavcan)
#
# libuavcan drivers
#
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
message(STATUS "Adding Linux support library")
# drivers
if (${UAVCAN_PLATFORM} STREQUAL "linux")
message(STATUS "Adding Linux platform driver")
add_subdirectory(libuavcan_drivers/posix)
add_subdirectory(libuavcan_drivers/linux)
endif ()
elseif(${UAVCAN_PLATFORM} STREQUAL "stm32")
message(STATUS "Adding STM32 platform driver")
add_subdirectory(libuavcan_drivers/posix)
add_subdirectory(libuavcan_drivers/stm32/driver)
endif()
# vim: set et ft=cmake fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
+11
View File
@@ -47,6 +47,15 @@ make -j8
sudo make install
```
For cross-compiling the procedure is similar.
```bash
mkdir build
cd build
cmake .. -D CMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-stm32-cortex-m4.cmake
make -j8
```
The following components will be installed into the system:
* Libuavcan headers and the static library
@@ -78,6 +87,8 @@ make
```
Test outputs can be found in the build directory under `libuavcan`.
Note that unit tests must be executed in real time, otherwise they may produce false warnings;
this implies that they will likely fail if ran on a virtual machine or on a highly loaded system.
Contributors, please follow the [Zubax Style Guide](https://github.com/Zubax/zubax_style_guide).
+1 -1
Submodule dsdl updated: 1abd370556...9804a3e697
+4 -2
View File
@@ -43,11 +43,11 @@ include_directories(${DSDLC_OUTPUT})
#
if (COMPILER_IS_GCC_COMPATIBLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef")
if (USE_CPP03)
if (UAVCAN_USE_CPP03)
message(STATUS "Using C++03")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++03 -Wno-variadic-macros -Wno-long-long")
else ()
message(STATUS "Using C++11 (pass USE_CPP03=1 to override)")
message(STATUS "Using C++11 (pass UAVCAN_USE_CPP03=1 to override)")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif ()
endif ()
@@ -128,3 +128,5 @@ if (DEBUG_BUILD)
else ()
message(STATUS "Release build type: " ${CMAKE_BUILD_TYPE})
endif ()
# vim: set et ft=cmake fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :
@@ -521,6 +521,7 @@ class UAVCAN_EXPORT Array : public ArrayImpl<T, ArrayMode, MaxSize_>
{
return res_sz;
}
// coverity[result_independent_of_operands]
if (static_cast<unsigned>(sz) > MaxSize_) // False 'type-limits' warning occurs here
{
return -ErrInvalidMarshalData;
+8 -4
View File
@@ -150,10 +150,11 @@ public:
int start(const TransferPriority node_status_transfer_priority = TransferPriority::Default);
/**
* Sets the node name, e.g. "com.example.product_name". The node name can be set only once.
* Must be executed before the node is started, otherwise the node will refuse to start up.
* Gets/sets the node name, e.g. "com.example.product_name". The node name can be set only once.
* The name must be set before the node is started, otherwise the node will refuse to start up.
*/
void setName(const char* name) { proto_nsp_.setName(name); }
const NodeStatusProvider::NodeName& getName() const { return proto_nsp_.getName(); }
void setName(const NodeStatusProvider::NodeName& name) { proto_nsp_.setName(name); }
/**
* Node health code helpers.
@@ -187,11 +188,14 @@ public:
}
/**
* Sets the node version information.
* Gets/sets the node version information.
*/
void setSoftwareVersion(const protocol::SoftwareVersion& version) { proto_nsp_.setSoftwareVersion(version); }
void setHardwareVersion(const protocol::HardwareVersion& version) { proto_nsp_.setHardwareVersion(version); }
const protocol::SoftwareVersion& getSoftwareVersion() const { return proto_nsp_.getSoftwareVersion(); }
const protocol::HardwareVersion& getHardwareVersion() const { return proto_nsp_.getHardwareVersion(); }
NodeStatusProvider& getNodeStatusProvider() { return proto_nsp_; }
#if !UAVCAN_TINY
@@ -63,6 +63,8 @@ class UAVCAN_EXPORT DynamicNodeIDClient : private TimerBase
void handleAllocation(const ReceivedDataStructure<protocol::dynamic_node_id::Allocation>& msg);
public:
typedef protocol::HardwareVersion::FieldTypes::unique_id UniqueID;
DynamicNodeIDClient(INode& node)
: TimerBase(node)
, dnida_pub_(node)
@@ -71,7 +73,7 @@ public:
{ }
/**
* @param hardware_version Hardware version information, where unique_id must be set correctly.
* @param unique_id Unique ID of the local node. Must be the same as in the hardware version struct.
* @param preferred_node_id Node ID that the application would like to take; set to broadcast (zero) if
* the application doesn't have any preference (this is default).
* @param transfer_priority Transfer priority, Normal by default.
@@ -79,7 +81,7 @@ public:
* Negative error code on failure
* -ErrLogic if 1. the node is not in passive mode or 2. the client is already started
*/
int start(const protocol::HardwareVersion& hardware_version,
int start(const UniqueID& unique_id,
const NodeID preferred_node_id = NodeID::Broadcast,
const TransferPriority transfer_priority = TransferPriority::OneHigherThanLowest);
@@ -154,7 +154,7 @@ class AllocationRequestManager
}
const uint8_t expected_stage = getExpectedStage();
if (request_stage == InvalidStage)
if (expected_stage == InvalidStage)
{
UAVCAN_ASSERT(0);
return;
@@ -88,7 +88,7 @@ public:
*
* Events from this class can be routed to many listeners, @ref INodeInfoListener.
*/
class UAVCAN_EXPORT NodeInfoRetriever : NodeStatusMonitor
class UAVCAN_EXPORT NodeInfoRetriever : public NodeStatusMonitor
, TimerBase
{
public:
@@ -51,6 +51,8 @@ public:
typedef typename StorageType<typename protocol::NodeStatus::FieldTypes::vendor_specific_status_code>::Type
VendorSpecificStatusCode;
typedef typename StorageType<typename protocol::GetNodeInfo::Response::FieldTypes::name>::Type NodeName;
explicit NodeStatusProvider(INode& node)
: TimerBase(node)
, creation_timestamp_(node.getMonotonicTime())
@@ -119,8 +121,8 @@ public:
* Can be set only once before the provider is started.
* The provider will refuse to start if the node name is not set.
*/
const typename protocol::GetNodeInfo::Response::FieldTypes::name& getName() const { return node_info_.name; }
void setName(const char* name);
const NodeName& getName() const { return node_info_.name; }
void setName(const NodeName& name);
/**
* Node version information.
@@ -144,7 +144,7 @@ void DynamicNodeIDClient::handleAllocation(const ReceivedDataStructure<protocol:
}
}
int DynamicNodeIDClient::start(const protocol::HardwareVersion& hardware_version,
int DynamicNodeIDClient::start(const UniqueID& unique_id,
const NodeID preferred_node_id,
const TransferPriority transfer_priority)
{
@@ -157,7 +157,7 @@ int DynamicNodeIDClient::start(const protocol::HardwareVersion& hardware_version
}
// Unique ID initialization & validation
copy(hardware_version.unique_id.begin(), hardware_version.unique_id.end(), unique_id_);
copy(unique_id.begin(), unique_id.end(), unique_id_);
bool unique_id_is_zero = true;
for (uint8_t i = 0; i < sizeof(unique_id_); i++)
{
@@ -124,11 +124,11 @@ void NodeStatusProvider::setVendorSpecificStatusCode(VendorSpecificStatusCode co
node_info_.status.vendor_specific_status_code = code;
}
void NodeStatusProvider::setName(const char* name)
void NodeStatusProvider::setName(const NodeName& name)
{
if ((name != NULL) && (*name != '\0') && (node_info_.name.empty()))
if (node_info_.name.empty())
{
node_info_.name = name; // The string contents will be copied, not just pointer.
node_info_.name = name;
}
}
@@ -21,19 +21,19 @@ TEST(DynamicNodeIDClient, Basic)
/*
* Client initialization
*/
uavcan::protocol::HardwareVersion hwver;
uavcan::protocol::HardwareVersion::FieldTypes::unique_id unique_id;
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(hwver)); // Empty hardware version is not allowed
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(unique_id)); // Empty hardware version is not allowed
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
for (uavcan::uint8_t i = 0; i < unique_id.size(); i++)
{
hwver.unique_id[i] = i;
unique_id[i] = i;
}
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(hwver, uavcan::NodeID()));
ASSERT_LE(-uavcan::ErrInvalidParam, dnidac.start(unique_id, uavcan::NodeID()));
const uavcan::NodeID PreferredNodeID = 42;
ASSERT_LE(0, dnidac.start(hwver, PreferredNodeID));
ASSERT_LE(0, dnidac.start(unique_id, PreferredNodeID));
ASSERT_FALSE(dnidac.getAllocatedNodeID().isValid());
ASSERT_FALSE(dnidac.getAllocatorNodeID().isValid());
@@ -56,7 +56,7 @@ TEST(DynamicNodeIDClient, Basic)
ASSERT_TRUE(dynid_sub.collector.msg->first_part_of_unique_id);
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
dynid_sub.collector.msg->unique_id.end(),
hwver.unique_id.begin()));
unique_id.begin()));
dynid_sub.collector.msg.reset();
// Second - rate is no lower than 0.5 Hz
@@ -92,7 +92,7 @@ TEST(DynamicNodeIDClient, Basic)
{
uavcan::protocol::dynamic_node_id::Allocation msg;
msg.unique_id.resize(BytesPerRequest);
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.begin() + BytesPerRequest, msg.unique_id.begin());
uavcan::copy(unique_id.begin(), unique_id.begin() + BytesPerRequest, msg.unique_id.begin());
std::cout << "First-stage offer:\n" << msg << std::endl;
@@ -106,7 +106,7 @@ TEST(DynamicNodeIDClient, Basic)
ASSERT_FALSE(dynid_sub.collector.msg->first_part_of_unique_id);
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
dynid_sub.collector.msg->unique_id.end(),
hwver.unique_id.begin() + BytesPerRequest));
unique_id.begin() + BytesPerRequest));
dynid_sub.collector.msg.reset();
}
@@ -116,7 +116,7 @@ TEST(DynamicNodeIDClient, Basic)
{
uavcan::protocol::dynamic_node_id::Allocation msg;
msg.unique_id.resize(BytesPerRequest * 2);
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.begin() + BytesPerRequest * 2, msg.unique_id.begin());
uavcan::copy(unique_id.begin(), unique_id.begin() + BytesPerRequest * 2, msg.unique_id.begin());
std::cout << "Second-stage offer:\n" << msg << std::endl;
@@ -130,7 +130,7 @@ TEST(DynamicNodeIDClient, Basic)
ASSERT_FALSE(dynid_sub.collector.msg->first_part_of_unique_id);
ASSERT_TRUE(uavcan::equal(dynid_sub.collector.msg->unique_id.begin(),
dynid_sub.collector.msg->unique_id.end(),
hwver.unique_id.begin() + BytesPerRequest * 2));
unique_id.begin() + BytesPerRequest * 2));
dynid_sub.collector.msg.reset();
}
@@ -145,7 +145,7 @@ TEST(DynamicNodeIDClient, Basic)
uavcan::protocol::dynamic_node_id::Allocation msg;
msg.unique_id.resize(16);
msg.node_id = 72;
uavcan::copy(hwver.unique_id.begin(), hwver.unique_id.end(), msg.unique_id.begin());
uavcan::copy(unique_id.begin(), unique_id.end(), msg.unique_id.begin());
ASSERT_FALSE(dynid_sub.collector.msg.get());
ASSERT_LE(0, dynid_pub.broadcast(msg));
@@ -169,11 +169,11 @@ TEST(DynamicNodeIDClient, NonPassiveMode)
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::Allocation> _reg1;
(void)_reg1;
uavcan::protocol::HardwareVersion hwver;
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
uavcan::protocol::HardwareVersion::FieldTypes::unique_id unique_id;
for (uavcan::uint8_t i = 0; i < unique_id.size(); i++)
{
hwver.unique_id[i] = i;
unique_id[i] = i;
}
ASSERT_LE(-uavcan::ErrLogic, dnidac.start(hwver));
ASSERT_LE(-uavcan::ErrLogic, dnidac.start(unique_id));
}
@@ -78,13 +78,13 @@ TEST(dynamic_node_id_server_AllocationRequestManager, Basic)
/*
* Client initialization
*/
uavcan::protocol::HardwareVersion hwver;
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
uavcan::protocol::HardwareVersion::FieldTypes::unique_id unique_id;
for (uavcan::uint8_t i = 0; i < unique_id.size(); i++)
{
hwver.unique_id[i] = i;
unique_id[i] = i;
}
const uavcan::NodeID PreferredNodeID = 42;
ASSERT_LE(0, client.start(hwver, PreferredNodeID));
ASSERT_LE(0, client.start(unique_id, PreferredNodeID));
/*
* Request manager initialization
@@ -102,9 +102,9 @@ TEST(dynamic_node_id_server_AllocationRequestManager, Basic)
*/
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000));
ASSERT_TRUE(handler.matchAndPopLastRequest(hwver.unique_id, PreferredNodeID));
ASSERT_TRUE(handler.matchAndPopLastRequest(unique_id, PreferredNodeID));
ASSERT_LE(0, manager.broadcastAllocationResponse(hwver.unique_id, PreferredNodeID));
ASSERT_LE(0, manager.broadcastAllocationResponse(unique_id, PreferredNodeID));
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(100));
@@ -49,13 +49,13 @@ TEST(dynamic_node_id_server_centralized_Server, Basic)
* Client
*/
uavcan::DynamicNodeIDClient client(nodes.b);
uavcan::protocol::HardwareVersion hwver;
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
uavcan::protocol::HardwareVersion::FieldTypes::unique_id unique_id;
for (uavcan::uint8_t i = 0; i < unique_id.size(); i++)
{
hwver.unique_id[i] = i;
unique_id[i] = i;
}
const uavcan::NodeID PreferredNodeID = 42;
ASSERT_LE(0, client.start(hwver, PreferredNodeID));
ASSERT_LE(0, client.start(unique_id, PreferredNodeID));
/*
* Fire
@@ -154,13 +154,13 @@ TEST(dynamic_node_id_server_Server, Basic)
* Client
*/
uavcan::DynamicNodeIDClient client(nodes.b);
uavcan::protocol::HardwareVersion hwver;
for (uavcan::uint8_t i = 0; i < hwver.unique_id.size(); i++)
uavcan::protocol::HardwareVersion::FieldTypes::unique_id unique_id;
for (uavcan::uint8_t i = 0; i < unique_id.size(); i++)
{
hwver.unique_id[i] = i;
unique_id[i] = i;
}
const uavcan::NodeID PreferredNodeID = 42;
ASSERT_LE(0, client.start(hwver, PreferredNodeID));
ASSERT_LE(0, client.start(unique_id, PreferredNodeID));
/*
* Fire
@@ -44,7 +44,7 @@ uavcan_linux::NodePtr initNodeWithDynamicID(const std::vector<std::string>& ifac
*/
uavcan::DynamicNodeIDClient client(*node);
ENFORCE(0 <= client.start(node->getNodeStatusProvider().getHardwareVersion(), preferred_node_id));
ENFORCE(0 <= client.start(node->getNodeStatusProvider().getHardwareVersion().unique_id, preferred_node_id));
std::cout << "Waiting for dynamic node ID allocation..." << std::endl;
@@ -48,7 +48,7 @@ uavcan_linux::NodePtr initNode(const std::vector<std::string>& ifaces, uavcan::N
void runForever(const uavcan_linux::NodePtr& node)
{
uavcan_posix::BasicFileSeverBackend backend(*node);
uavcan_posix::BasicFileServerBackend backend(*node);
uavcan::FileServer server(*node, backend);
@@ -354,10 +354,6 @@ class VirtualCanDriver : public uavcan::ICanDriver,
ifaces_[frame.iface_index]->addRxFrame(frame, flags);
event_.signal();
}
else
{
assert(false);
}
}
/**
@@ -138,11 +138,11 @@ public:
* It takes the following inputs:
* - Unique machine ID
* - Node name string (e.g. "org.uavcan.linux_app.dynamic_node_id_server")
* - Instance ID byte, e.g. node ID
* - Instance ID byte, e.g. node ID (optional)
*/
std::array<std::uint8_t, 16> makeApplicationID(const MachineIDReader::MachineID& machine_id,
const std::string& node_name,
const std::uint8_t instance_id)
inline std::array<std::uint8_t, 16> makeApplicationID(const MachineIDReader::MachineID& machine_id,
const std::string& node_name,
const std::uint8_t instance_id = 0)
{
union HalfID
{
@@ -15,6 +15,7 @@
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <ctime>
#include <unistd.h>
#include <fcntl.h>
@@ -31,7 +32,7 @@ namespace uavcan_posix
/**
* This interface implements a POSIX compliant IFileServerBackend interface
*/
class BasicFileSeverBackend : public uavcan::IFileServerBackend
class BasicFileServerBackend : public uavcan::IFileServerBackend
{
enum { FilePermissions = 438 }; ///< 0o666
@@ -433,12 +434,12 @@ protected:
}
public:
BasicFileSeverBackend(uavcan::INode& node) :
BasicFileServerBackend(uavcan::INode& node) :
fdcache_(NULL),
node_(node)
{ }
~BasicFileSeverBackend()
~BasicFileServerBackend()
{
if (fdcache_ != &fallback_)
{
@@ -447,6 +448,14 @@ public:
}
}
};
#if __GNUC__
/// Typo fix in a backwards-compatible way (only for GCC projects). Will be removed someday.
typedef BasicFileServerBackend
BasicFileSeverBackend // Missing 'r'
__attribute__((deprecated));
#endif
}
#endif // Include guard
@@ -0,0 +1,17 @@
include_directories(
./include
)
add_library(uavcan_stm32_driver STATIC
./src/uc_stm32_can.cpp
./src/uc_stm32_clock.cpp
./src/uc_stm32_thread.cpp
)
add_dependencies(uavcan_stm32_driver uavcan)
install(DIRECTORY include/uavcan_stm32 DESTINATION include)
install(TARGETS uavcan_stm32_driver DESTINATION lib)
# vim: set et ft=cmake fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :)
@@ -15,6 +15,10 @@
# define UAVCAN_STM32_NUTTX 0
#endif
#ifndef UAVCAN_STM32_BAREMETAL
# define UAVCAN_STM32_BAREMETAL 0
#endif
/**
* Number of interfaces must be enabled explicitly
*/
@@ -223,6 +223,8 @@ class CanDriver : public uavcan::ICanDriver, uavcan::Noncopyable
const uavcan::CanFrame* (& pending_tx)[uavcan::MaxCanIfaces],
uavcan::MonotonicTime blocking_deadline);
static void initOnce();
public:
template <unsigned RxQueueCapacity>
CanDriver(CanRxItem (&rx_queue_storage)[UAVCAN_STM32_NUM_IFACES][RxQueueCapacity])
@@ -24,6 +24,14 @@ void init();
*/
uavcan::MonotonicTime getMonotonic();
/**
* Sets the driver's notion of the system UTC. It should be called
* at startup and any time the system clock is updated from an
* external source that is not the UAVCAN Timesync master.
* This function is thread safe.
*/
void setUtc(uavcan::UtcTime time);
/**
* Returns UTC time if it has been set, otherwise returns zero time.
* This function is thread safe.
@@ -42,13 +50,23 @@ void adjustUtc(uavcan::UtcDuration adjustment);
*/
struct UtcSyncParams
{
float offset_p = 0.01F; ///< PPM per one usec error
float rate_i = 0.02F; ///< PPM per one PPM error for second
float rate_error_corner_freq = 0.01F;
float max_rate_correction_ppm = 300.0F;
float lock_thres_rate_ppm = 2.0F;
uavcan::UtcDuration lock_thres_offset = uavcan::UtcDuration::fromMSec(4);
uavcan::UtcDuration min_jump = uavcan::UtcDuration::fromMSec(10); ///< Min error to jump rather than change rate
float offset_p; ///< PPM per one usec error
float rate_i; ///< PPM per one PPM error for second
float rate_error_corner_freq;
float max_rate_correction_ppm;
float lock_thres_rate_ppm;
uavcan::UtcDuration lock_thres_offset;
uavcan::UtcDuration min_jump; ///< Min error to jump rather than change rate
UtcSyncParams()
: offset_p(0.01F)
, rate_i(0.02F)
, rate_error_corner_freq(0.01F)
, max_rate_correction_ppm(300.0F)
, lock_thres_rate_ppm(2.0F)
, lock_thres_offset(uavcan::UtcDuration::fromMSec(4))
, min_jump(uavcan::UtcDuration::fromMSec(10))
{ }
};
/**
@@ -16,6 +16,7 @@
# include <cstdio>
# include <ctime>
# include <cstring>
#elif UAVCAN_STM32_BAREMETAL
#else
# error "Unknown OS"
#endif
@@ -92,11 +93,82 @@ public:
void signalFromInterrupt();
};
class Mutex
{
pthread_mutex_t mutex_;
public:
Mutex()
{
init();
}
int init()
{
return pthread_mutex_init(&mutex_, NULL);
}
int deinit()
{
return pthread_mutex_destroy(&mutex_);
}
void lock()
{
(void)pthread_mutex_lock(&mutex_);
}
void unlock()
{
(void)pthread_mutex_unlock(&mutex_);
}
};
#elif UAVCAN_STM32_BAREMETAL
class BusEvent
{
volatile bool ready;
public:
BusEvent(CanDriver& can_driver)
: ready(false)
{
(void)can_driver;
}
bool wait(uavcan::MonotonicDuration duration)
{
bool lready = ready;
return __atomic_exchange_n (&lready, false, __ATOMIC_SEQ_CST);
}
void signal()
{
__atomic_store_n (&ready, true, __ATOMIC_SEQ_CST);
}
void signalFromInterrupt()
{
__atomic_store_n (&ready, true, __ATOMIC_SEQ_CST);
}
};
class Mutex
{
public:
void lock()
{
};
void unlock()
{
};
};
#endif
#if UAVCAN_STM32_CHIBIOS
class MutexLocker
{
Mutex& mutex_;
@@ -113,6 +185,4 @@ public:
}
};
#endif
}
+39 -10
View File
@@ -10,7 +10,11 @@
# include <hal.h>
#elif UAVCAN_STM32_NUTTX
# include <nuttx/arch.h>
# include <arch/board/board.h>
# include <chip/stm32_tim.h>
# include <syslog.h>
#elif UAVCAN_STM32_BAREMETAL
# include <chip.h>
#else
# error "Unknown OS"
#endif
@@ -20,7 +24,7 @@
*/
#ifndef UAVCAN_STM32_LOG
// lowsyslog() crashes the system in this context
//# if UAVCAN_STM32_NUTTX && CONFIG_ARCH_LOWPUTC
// # if UAVCAN_STM32_NUTTX && CONFIG_ARCH_LOWPUTC
# if 0
# define UAVCAN_STM32_LOG(fmt, ...) lowsyslog("uavcan_stm32: " fmt "\n", ##__VA_ARGS__)
# else
@@ -32,17 +36,17 @@
* 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()
#elif UAVCAN_STM32_NUTTX
# define UAVCAN_STM32_IRQ_HANDLER(id) int id(int irq, FAR void* context)
# define UAVCAN_STM32_IRQ_PROLOGUE()
# define UAVCAN_STM32_IRQ_EPILOGUE() return 0;
#else
# define UAVCAN_STM32_IRQ_HANDLER(id) void id(void)
# define UAVCAN_STM32_IRQ_PROLOGUE()
# define UAVCAN_STM32_IRQ_EPILOGUE()
#endif
#if UAVCAN_STM32_CHIBIOS
@@ -50,7 +54,20 @@
* Priority mask for timer and CAN interrupts.
*/
# ifndef UAVCAN_STM32_IRQ_PRIORITY_MASK
# define UAVCAN_STM32_IRQ_PRIORITY_MASK CORTEX_PRIORITY_MASK(CORTEX_MAX_KERNEL_PRIORITY)
# if (CH_KERNEL_MAJOR == 2)
# define UAVCAN_STM32_IRQ_PRIORITY_MASK CORTEX_PRIORITY_MASK(CORTEX_MAX_KERNEL_PRIORITY)
# else // ChibiOS 3
# define UAVCAN_STM32_IRQ_PRIORITY_MASK CORTEX_MAX_KERNEL_PRIORITY
# endif
# endif
#endif
#if UAVCAN_STM32_BAREMETAL
/**
* Priority mask for timer and CAN interrupts.
*/
# ifndef UAVCAN_STM32_IRQ_PRIORITY_MASK
# define UAVCAN_STM32_IRQ_PRIORITY_MASK 0
# endif
#endif
@@ -65,7 +82,6 @@
namespace uavcan_stm32
{
#if UAVCAN_STM32_CHIBIOS
struct CriticalSectionLocker
@@ -90,13 +106,26 @@ struct CriticalSectionLocker
}
};
#elif UAVCAN_STM32_BAREMETAL
struct CriticalSectionLocker
{
CriticalSectionLocker()
{
__disable_irq();
}
~CriticalSectionLocker()
{
__enable_irq();
}
};
#endif
namespace clock
{
uavcan::uint64_t getUtcUSecFromCanInterrupt();
}
}
@@ -14,11 +14,13 @@
# include <nuttx/arch.h>
# include <nuttx/irq.h>
# include <arch/board/board.h>
#elif UAVCAN_STM32_BAREMETAL
#include <chip.h>
#else
# error "Unknown OS"
#endif
#if !UAVCAN_STM32_NUTTX
#if (UAVCAN_STM32_CHIBIOS && CH_KERNEL_MAJOR == 2) || UAVCAN_STM32_BAREMETAL
# if !(defined(STM32F10X_CL) || defined(STM32F2XX) || defined(STM32F4XX))
// IRQ numbers
# define CAN1_RX0_IRQn USB_LP_CAN1_RX0_IRQn
@@ -31,6 +33,17 @@
# endif
#endif
#if (UAVCAN_STM32_CHIBIOS && CH_KERNEL_MAJOR == 3)
#define CAN1_TX_IRQHandler STM32_CAN1_TX_HANDLER
#define CAN1_RX0_IRQHandler STM32_CAN1_RX0_HANDLER
#define CAN1_RX1_IRQHandler STM32_CAN1_RX1_HANDLER
#define CAN1_SCE_IRQHandler STM32_CAN1_SCE_HANDLER
#define CAN2_TX_IRQHandler STM32_CAN2_TX_HANDLER
#define CAN2_RX0_IRQHandler STM32_CAN2_RX0_HANDLER
#define CAN2_RX1_IRQHandler STM32_CAN2_RX1_HANDLER
#define CAN2_SCE_IRQHandler STM32_CAN2_SCE_HANDLER
#endif
#if UAVCAN_STM32_NUTTX
# if !defined(STM32_IRQ_CAN1TX) && !defined(STM32_IRQ_CAN1RX0)
# define STM32_IRQ_CAN1TX STM32_IRQ_USBHPCANTX
@@ -45,6 +58,17 @@ static int can2_irq(const int irq, void*);
}
#endif
/* STM32F3's only CAN inteface does not have a number. */
#if defined(STM32F3XX)
#define RCC_APB1ENR_CAN1EN RCC_APB1ENR_CANEN
#define RCC_APB1RSTR_CAN1RST RCC_APB1RSTR_CANRST
#define CAN1_TX_IRQn CAN_TX_IRQn
#define CAN1_RX0_IRQn CAN_RX0_IRQn
#define CAN1_RX1_IRQn CAN_RX1_IRQn
#define CAN1_SCE_IRQn CAN_SCE_IRQn
#endif
namespace uavcan_stm32
{
namespace
@@ -188,7 +212,9 @@ int CanIface::computeTimings(const uavcan::uint32_t target_bitrate, Timings& out
/*
* Hardware configuration
*/
#if UAVCAN_STM32_CHIBIOS
#if UAVCAN_STM32_BAREMETAL
const uavcan::uint32_t pclk = STM32_PCLK1_FREQUENCY;
#elif UAVCAN_STM32_CHIBIOS
const uavcan::uint32_t pclk = STM32_PCLK1;
#elif UAVCAN_STM32_NUTTX
const uavcan::uint32_t pclk = STM32_PCLK1_FREQUENCY;
@@ -462,10 +488,26 @@ bool CanIface::waitMsrINakBitStateChange(bool target_state)
int CanIface::init(const uavcan::uint32_t bitrate, const OperatingMode mode)
{
int res = 0;
/*
* We need to silence the controller in the first order, otherwise it may interfere with the following operations.
*/
{
CriticalSectionLocker lock;
can_->MCR &= ~bxcan::MCR_SLEEP; // Exit sleep mode
can_->MCR |= bxcan::MCR_INRQ; // Request init
can_->IER = 0; // Disable interrupts while initialization is in progress
}
if (!waitMsrINakBitStateChange(true))
{
UAVCAN_STM32_LOG("MSR INAK not set");
return -1;
}
/*
* Object state
* Object state - interrupts are disabled, so it's safe to modify it now
*/
rx_queue_.reset();
error_cnt_ = 0;
@@ -479,27 +521,17 @@ int CanIface::init(const uavcan::uint32_t bitrate, const OperatingMode mode)
* CAN timings for this bitrate
*/
Timings timings;
res = computeTimings(bitrate, timings);
if (res < 0)
const int timings_res = computeTimings(bitrate, timings);
if (timings_res < 0)
{
goto leave;
return timings_res;
}
UAVCAN_STM32_LOG("Timings: presc=%u sjw=%u bs1=%u bs2=%u",
unsigned(timings.prescaler), unsigned(timings.sjw), unsigned(timings.bs1), unsigned(timings.bs2));
/*
* Hardware initialization
* Hardware initialization (the hardware has already confirmed initialization mode, see above)
*/
can_->MCR &= ~bxcan::MCR_SLEEP; // Exit sleep mode
can_->MCR |= bxcan::MCR_INRQ; // Request init
if (!waitMsrINakBitStateChange(true))
{
UAVCAN_STM32_LOG("MSR INAK not set");
res = -1;
goto leave;
}
can_->MCR = bxcan::MCR_ABOM | bxcan::MCR_AWUM | bxcan::MCR_INRQ; // RM page 648
can_->BTR = ((timings.sjw & 3U) << 24) |
@@ -519,8 +551,7 @@ int CanIface::init(const uavcan::uint32_t bitrate, const OperatingMode mode)
if (!waitMsrINakBitStateChange(false))
{
UAVCAN_STM32_LOG("MSR INAK not cleared");
res = -1;
goto leave;
return -1;
}
/*
@@ -553,8 +584,7 @@ int CanIface::init(const uavcan::uint32_t bitrate, const OperatingMode mode)
can_->FMR &= ~bxcan::FMR_FINIT;
}
leave:
return res;
return 0;
}
void CanIface::handleTxMailboxInterrupt(uavcan::uint8_t mailbox_index, bool txok, const uavcan::uint64_t utc_usec)
@@ -838,63 +868,48 @@ uavcan::int16_t CanDriver::select(uavcan::CanSelectMasks& inout_masks,
return 1; // Return value doesn't matter as long as it is non-negative
}
int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode)
#if UAVCAN_STM32_BAREMETAL
static void nvicEnableVector(int irq, uint8_t prio)
{
int res = 0;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = irq;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = prio;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
UAVCAN_STM32_LOG("Bitrate %lu", static_cast<unsigned long>(bitrate));
#endif
void CanDriver::initOnce()
{
/*
* CAN1
* CAN1, CAN2
*/
{
CriticalSectionLocker lock;
#if UAVCAN_STM32_NUTTX
modifyreg32(STM32_RCC_APB1ENR, 0, RCC_APB1ENR_CAN1EN);
modifyreg32(STM32_RCC_APB1ENR, 0, RCC_APB1ENR_CAN1EN);
modifyreg32(STM32_RCC_APB1RSTR, 0, RCC_APB1RSTR_CAN1RST);
modifyreg32(STM32_RCC_APB1RSTR, RCC_APB1RSTR_CAN1RST, 0);
# if UAVCAN_STM32_NUM_IFACES > 1
modifyreg32(STM32_RCC_APB1ENR, 0, RCC_APB1ENR_CAN2EN);
modifyreg32(STM32_RCC_APB1RSTR, 0, RCC_APB1RSTR_CAN2RST);
modifyreg32(STM32_RCC_APB1RSTR, RCC_APB1RSTR_CAN2RST, 0);
# endif
#else
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST;
#endif
}
UAVCAN_STM32_LOG("Initing iface 0...");
res = if0_.init(bitrate, mode);
if (res < 0)
{
UAVCAN_STM32_LOG("Iface 0 init failed %i", res);
goto fail;
}
ifaces[0] = &if0_;
/*
* CAN2
*/
#if UAVCAN_STM32_NUM_IFACES > 1
{
CriticalSectionLocker lock;
# if UAVCAN_STM32_NUTTX
modifyreg32(STM32_RCC_APB1ENR, 0, RCC_APB1ENR_CAN2EN);
modifyreg32(STM32_RCC_APB1RSTR, 0, RCC_APB1RSTR_CAN2RST);
modifyreg32(STM32_RCC_APB1RSTR, RCC_APB1RSTR_CAN2RST, 0);
# else
# if UAVCAN_STM32_NUM_IFACES > 1
RCC->APB1ENR |= RCC_APB1ENR_CAN2EN;
RCC->APB1RSTR |= RCC_APB1RSTR_CAN2RST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN2RST;
# endif
}
UAVCAN_STM32_LOG("Initing iface 1...");
res = if1_.init(bitrate, mode);
if (res < 0)
{
UAVCAN_STM32_LOG("Iface 1 init failed %i", res);
goto fail;
}
ifaces[1] = &if1_;
#endif
}
/*
* IRQ
@@ -902,12 +917,9 @@ int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMod
#if UAVCAN_STM32_NUTTX
# define IRQ_ATTACH(irq, handler) \
{ \
res = irq_attach(irq, handler); \
if (res < 0) \
{ \
UAVCAN_STM32_LOG("IRQ attach failed %i", irq); \
goto fail; \
} \
const int res = irq_attach(irq, handler); \
(void)res; \
assert(res >= 0); \
up_enable_irq(irq); \
}
IRQ_ATTACH(STM32_IRQ_CAN1TX, can1_irq);
@@ -921,7 +933,7 @@ int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMod
IRQ_ATTACH(STM32_IRQ_CAN2SCE, can2_irq);
# endif
# undef IRQ_ATTACH
#else
#elif UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL
{
CriticalSectionLocker lock;
nvicEnableVector(CAN1_TX_IRQn, UAVCAN_STM32_IRQ_PRIORITY_MASK);
@@ -936,6 +948,47 @@ int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMod
# endif
}
#endif
}
int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMode mode)
{
int res = 0;
UAVCAN_STM32_LOG("Bitrate %lu mode %d", static_cast<unsigned long>(bitrate), static_cast<int>(mode));
static bool initialized_once = false;
if (!initialized_once)
{
initialized_once = true;
UAVCAN_STM32_LOG("First initialization");
initOnce();
}
/*
* CAN1
*/
UAVCAN_STM32_LOG("Initing iface 0...");
res = if0_.init(bitrate, mode);
if (res < 0)
{
UAVCAN_STM32_LOG("Iface 0 init failed %i", res);
goto fail;
}
ifaces[0] = &if0_;
/*
* CAN2
*/
#if UAVCAN_STM32_NUM_IFACES > 1
UAVCAN_STM32_LOG("Initing iface 1...");
res = if1_.init(bitrate, mode);
if (res < 0)
{
UAVCAN_STM32_LOG("Iface 1 init failed %i", res);
goto fail;
}
ifaces[1] = &if1_;
#endif
UAVCAN_STM32_LOG("CAN drv init OK");
UAVCAN_ASSERT(res >= 0);
@@ -944,21 +997,6 @@ int CanDriver::init(const uavcan::uint32_t bitrate, const CanIface::OperatingMod
fail:
UAVCAN_STM32_LOG("CAN drv init failed %i", res);
UAVCAN_ASSERT(res < 0);
CriticalSectionLocker lock;
#if UAVCAN_STM32_NUTTX
// TODO: Unattach and disable all IRQs
modifyreg32(STM32_RCC_APB1ENR, RCC_APB1ENR_CAN1EN, 0);
# if UAVCAN_STM32_NUM_IFACES > 1
modifyreg32(STM32_RCC_APB1ENR, RCC_APB1ENR_CAN2EN, 0);
# endif
#else
RCC->APB1ENR &= ~RCC_APB1ENR_CAN1EN;
# if UAVCAN_STM32_NUM_IFACES > 1
RCC->APB1ENR &= ~RCC_APB1ENR_CAN2EN;
# endif
#endif
return res;
}
@@ -1044,7 +1082,7 @@ static int can2_irq(const int irq, void*)
# endif
#else // UAVCAN_STM32_NUTTX
UAVCAN_STM32_IRQ_HANDLER(CAN1_TX_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN1_TX_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1052,6 +1090,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN1_TX_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN1_RX0_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN1_RX0_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1059,6 +1098,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN1_RX0_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN1_RX1_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN1_RX1_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1066,6 +1106,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN1_RX1_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN1_SCE_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN1_SCE_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1075,6 +1116,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN1_SCE_IRQHandler)
# if UAVCAN_STM32_NUM_IFACES > 1
UAVCAN_STM32_IRQ_HANDLER(CAN2_TX_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN2_TX_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1082,6 +1124,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN2_TX_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN2_RX0_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN2_RX0_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1089,6 +1132,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN2_RX0_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN2_RX1_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN2_RX1_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -1096,6 +1140,7 @@ UAVCAN_STM32_IRQ_HANDLER(CAN2_RX1_IRQHandler)
UAVCAN_STM32_IRQ_EPILOGUE();
}
UAVCAN_STM32_IRQ_HANDLER(CAN2_SCE_IRQHandler);
UAVCAN_STM32_IRQ_HANDLER(CAN2_SCE_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
@@ -14,19 +14,43 @@
/*
* 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_CHIBIOS && CH_KERNEL_MAJOR == 2) || UAVCAN_STM32_BAREMETAL
# define TIMX UAVCAN_STM32_GLUE2(TIM, UAVCAN_STM32_TIMER_NUMBER)
# define TIMX_IRQn UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQn)
# define TIMX_INPUT_CLOCK STM32_TIMCLK1
# endif
#if UAVCAN_STM32_TIMER_NUMBER >= 2 && UAVCAN_STM32_TIMER_NUMBER <= 7
# 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
# if (UAVCAN_STM32_CHIBIOS && CH_KERNEL_MAJOR == 3)
# define TIMX UAVCAN_STM32_GLUE2(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER)
# define TIMX_IRQn UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _NUMBER)
# define TIMX_IRQHandler UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _HANDLER)
# define TIMX_INPUT_CLOCK STM32_TIMCLK1
# else
# define TIMX_IRQHandler UAVCAN_STM32_GLUE3(TIM, UAVCAN_STM32_TIMER_NUMBER, _IRQHandler)
# endif
# if UAVCAN_STM32_NUTTX
# define TIMX UAVCAN_STM32_GLUE3(STM32_TIM, UAVCAN_STM32_TIMER_NUMBER, _BASE)
# define TMR_REG(o) (TIMX + (o))
# define TIMX_INPUT_CLOCK STM32_TIM27_FREQUENCY
# define TIMX_IRQn UAVCAN_STM32_GLUE2(STM32_IRQ_TIM, UAVCAN_STM32_TIMER_NUMBER)
# endif
# if UAVCAN_STM32_TIMER_NUMBER >= 2 && UAVCAN_STM32_TIMER_NUMBER <= 7
# 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)
# else
# error "This UAVCAN_STM32_TIMER_NUMBER is not supported yet"
# endif
# if (TIMX_INPUT_CLOCK % 1000000) != 0
# error "No way, timer clock must be divisible to 1e6. FIXME!"
# endif
extern "C" UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler);
namespace uavcan_stm32
{
@@ -57,6 +81,21 @@ uavcan::uint64_t time_utc = 0;
}
#if UAVCAN_STM32_BAREMETAL
static void nvicEnableVector(int irq, uint8_t prio)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = irq;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = prio;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
#endif
void init()
{
CriticalSectionLocker lock;
@@ -66,6 +105,8 @@ void init()
}
initialized = true;
# if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL
// Power-on and reset
TIMX_RCC_ENR |= TIMX_RCC_ENR_MASK;
TIMX_RCC_RSTR |= TIMX_RCC_RSTR_MASK;
@@ -74,9 +115,9 @@ void init()
// 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
# if (TIMX_INPUT_CLOCK % 1000000) != 0
# error "No way, timer clock must be divisible to 1e6. FIXME!"
# endif
// Start the timer
TIMX->ARR = 0xFFFF;
@@ -86,10 +127,58 @@ void init()
TIMX->EGR = TIM_EGR_UG; // Reload immediately
TIMX->DIER = TIM_DIER_UIE;
TIMX->CR1 = TIM_CR1_CEN; // Start
# endif
# if UAVCAN_STM32_NUTTX
// Attach IRQ
irq_attach(TIMX_IRQn, &TIMX_IRQHandler);
// Power-on and reset
modifyreg32(STM32_RCC_APB1ENR, 0, TIMX_RCC_ENR_MASK);
modifyreg32(STM32_RCC_APB1RSTR, 0, TIMX_RCC_RSTR_MASK);
modifyreg32(STM32_RCC_APB1RSTR, TIMX_RCC_RSTR_MASK, 0);
// Start the timer
putreg32(0xFFFF, TMR_REG(STM32_BTIM_ARR_OFFSET));
putreg16(((TIMX_INPUT_CLOCK / 1000000)-1), TMR_REG(STM32_BTIM_PSC_OFFSET));
putreg16(BTIM_CR1_URS, TMR_REG(STM32_BTIM_CR1_OFFSET));
putreg16(0, TMR_REG(STM32_BTIM_SR_OFFSET));
putreg16(BTIM_EGR_UG, TMR_REG(STM32_BTIM_EGR_OFFSET)); // Reload immediately
putreg16(BTIM_DIER_UIE, TMR_REG(STM32_BTIM_DIER_OFFSET));
putreg16(BTIM_CR1_CEN, TMR_REG(STM32_BTIM_CR1_OFFSET)); // Start
// Prioritize and Enable IRQ
// todo: Currently changing the NVIC_SYSH_HIGH_PRIORITY is HARD faulting
// need to investigate
// up_prioritize_irq(TIMX_IRQn, NVIC_SYSH_HIGH_PRIORITY);
up_enable_irq(TIMX_IRQn);
# endif
}
void setUtc(uavcan::UtcTime time)
{
MutexLocker mlocker(mutex);
UAVCAN_ASSERT(initialized);
{
CriticalSectionLocker locker;
time_utc = time.toUSec();
}
utc_set = true;
utc_locked = false;
utc_jump_cnt++;
utc_prev_adj = 0;
utc_rel_rate_ppm = 0;
}
static uavcan::uint64_t sampleUtcFromCriticalSection()
{
# if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL
UAVCAN_ASSERT(initialized);
UAVCAN_ASSERT(TIMX->DIER & TIM_DIER_UIE);
@@ -104,6 +193,25 @@ static uavcan::uint64_t sampleUtcFromCriticalSection()
time = uavcan::uint64_t(uavcan::int64_t(time) + add);
}
return time + cnt;
# endif
# if UAVCAN_STM32_NUTTX
UAVCAN_ASSERT(initialized);
UAVCAN_ASSERT(getreg16(TMR_REG(STM32_BTIM_DIER_OFFSET)) & BTIM_DIER_UIE);
volatile uavcan::uint64_t time = time_utc;
volatile uavcan::uint32_t cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
if (getreg16(TMR_REG(STM32_BTIM_SR_OFFSET)) & BTIM_SR_UIF)
{
cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
const uavcan::int32_t add = uavcan::int32_t(USecPerOverflow) +
(utc_accumulated_correction_nsec + utc_correction_nsec_per_overflow) / 1000;
time = uavcan::uint64_t(uavcan::int64_t(time) + add);
}
return time + cnt;
# endif
}
uavcan::uint64_t getUtcUSecFromCanInterrupt()
@@ -114,25 +222,41 @@ uavcan::uint64_t getUtcUSecFromCanInterrupt()
uavcan::MonotonicTime getMonotonic()
{
uavcan::uint64_t usec = 0;
// Scope Critical section
{
CriticalSectionLocker locker;
volatile uavcan::uint64_t time = time_mono;
# if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL
volatile uavcan::uint32_t cnt = TIMX->CNT;
if (TIMX->SR & TIM_SR_UIF)
{
cnt = TIMX->CNT;
# endif
# if UAVCAN_STM32_NUTTX
volatile uavcan::uint32_t cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
if (getreg16(TMR_REG(STM32_BTIM_SR_OFFSET)) & BTIM_SR_UIF)
{
cnt = getreg16(TMR_REG(STM32_BTIM_CNT_OFFSET));
# endif
time += USecPerOverflow;
}
usec = time + cnt;
#ifndef NDEBUG
static uavcan::uint64_t prev_usec = 0; // Self-test
UAVCAN_ASSERT(prev_usec <= usec);
prev_usec = usec;
#endif
}
return uavcan::MonotonicTime::fromUSec(usec);
# ifndef NDEBUG
static uavcan::uint64_t prev_usec = 0; // Self-test
UAVCAN_ASSERT(prev_usec <= usec);
(void)prev_usec;
prev_usec = usec;
# endif
} // End Scope Critical section
return uavcan::MonotonicTime::fromUSec(usec);
}
uavcan::UtcTime getUtc()
@@ -201,7 +325,8 @@ static void updateRatePID(uavcan::UtcDuration adjustment)
utc_correction_nsec_per_overflow = uavcan::int32_t((USecPerOverflow * 1000) * (total_rate_correction_ppm / 1e6F));
// lowsyslog("$ adj=%f rel_rate=%f rel_rate_eint=%f tgt_rel_rate=%f ppm=%f\n",
// adj_usec, utc_rel_rate_ppm, utc_rel_rate_error_integral, target_rel_rate_ppm, total_rate_correction_ppm);
// adj_usec, utc_rel_rate_ppm, utc_rel_rate_error_integral, target_rel_rate_ppm,
// total_rate_correction_ppm);
}
void adjustUtc(uavcan::UtcDuration adjustment)
@@ -288,12 +413,13 @@ SystemClock& SystemClock::instance()
long long _aligner_1;
long double _aligner_2;
} storage;
SystemClock* const ptr = reinterpret_cast<SystemClock*>(storage.buffer);
if (!clock::initialized)
{
clock::init();
new (ptr) SystemClock();
new (ptr)SystemClock();
}
return *ptr;
}
@@ -304,12 +430,18 @@ SystemClock& SystemClock::instance()
/**
* Timer interrupt handler
*/
extern "C"
UAVCAN_STM32_IRQ_HANDLER(TIMX_IRQHandler)
{
UAVCAN_STM32_IRQ_PROLOGUE();
# if UAVCAN_STM32_CHIBIOS || UAVCAN_STM32_BAREMETAL
TIMX->SR = 0;
# endif
# if UAVCAN_STM32_NUTTX
putreg16(0, TMR_REG(STM32_BTIM_SR_OFFSET));
# endif
using namespace uavcan_stm32::clock;
UAVCAN_ASSERT(initialized);
@@ -23,13 +23,25 @@ bool BusEvent::wait(uavcan::MonotonicDuration duration)
if (msec <= 0)
{
# if (CH_KERNEL_MAJOR == 2)
ret = sem_.waitTimeout(TIME_IMMEDIATE);
# else // ChibiOS 3
ret = sem_.wait(TIME_IMMEDIATE);
# endif
}
else
{
# if (CH_KERNEL_MAJOR == 2)
ret = sem_.waitTimeout((msec > MaxDelayMSec) ? MS2ST(MaxDelayMSec) : MS2ST(msec));
# else // ChibiOS 3
ret = sem_.wait((msec > MaxDelayMSec) ? MS2ST(MaxDelayMSec) : MS2ST(msec));
# endif
}
# if (CH_KERNEL_MAJOR == 2)
return ret == RDY_OK;
# else // ChibiOS 3
return ret == MSG_OK;
# endif
}
void BusEvent::signal()
@@ -39,9 +51,15 @@ void BusEvent::signal()
void BusEvent::signalFromInterrupt()
{
# if (CH_KERNEL_MAJOR == 2)
chSysLockFromIsr();
sem_.signalI();
chSysUnlockFromIsr();
# else // ChibiOS 3
chSysLockFromISR();
sem_.signalI();
chSysUnlockFromISR();
# endif
}
/*
@@ -54,7 +72,11 @@ void Mutex::lock()
void Mutex::unlock()
{
# if (CH_KERNEL_MAJOR == 2)
chibios_rt::BaseThread::unlockMutex();
# else // ChibiOS 3
mtx_.unlock();
# endif
}
#elif UAVCAN_STM32_NUTTX
@@ -10,8 +10,9 @@ PROJECT = uavcan_test_stm32f107
MAIN ?= main.cpp
CPPSRC = src/$(MAIN) \
src/dummy.cpp
CPPSRC = src/$(MAIN) \
src/dummy.cpp \
src/board/board.cpp
#
# UAVCAN library
@@ -37,10 +38,15 @@ $(info $(shell $(LIBUAVCAN_DSDLC) $(UAVCAN_DSDL_DIR)))
UINCDIR += dsdlc_generated
#
# Platform
# Git commit hash
#
CSRC += src/sys/board.c
GIT_HASH := $(shell git rev-parse --short HEAD)
UDEFS += -DGIT_HASH=0x$(GIT_HASH)
#
# Platform
#
UINCDIR += src/sys
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include "board.hpp"
#include <cstring>
#include <unistd.h>
#include <ch.h>
#include <ch.hpp>
#include <hal.h>
#include <zubax_chibios/sys/sys.h>
/**
* GPIO config for ChibiOS PAL driver
*/
const PALConfig pal_default_config =
{
{ VAL_GPIOAODR, VAL_GPIOACRL, VAL_GPIOACRH },
{ VAL_GPIOBODR, VAL_GPIOBCRL, VAL_GPIOBCRH },
{ VAL_GPIOCODR, VAL_GPIOCCRL, VAL_GPIOCCRH },
{ VAL_GPIODODR, VAL_GPIODCRL, VAL_GPIODCRH },
{ VAL_GPIOEODR, VAL_GPIOECRL, VAL_GPIOECRH }
};
namespace board
{
void init()
{
halInit();
chibios_rt::System::init();
sdStart(&STDOUT_SD, NULL);
}
__attribute__((noreturn))
void die(int error)
{
lowsyslog("Fatal error %i\n", error);
while (1)
{
setLed(false);
::sleep(1);
setLed(true);
::sleep(1);
}
}
void setLed(bool state)
{
palWritePad(GPIO_PORT_LED, GPIO_PIN_LED, state);
}
void restart()
{
NVIC_SystemReset();
}
void readUniqueID(std::uint8_t bytes[UniqueIDSize])
{
std::memcpy(bytes, reinterpret_cast<const void*>(0x1FFFF7E8), UniqueIDSize);
}
}
/*
* Early init from ChibiOS
*/
extern "C"
{
void __early_init(void)
{
stm32_clock_init();
}
void boardInit(void)
{
AFIO->MAPR |=
AFIO_MAPR_CAN_REMAP_REMAP3 |
AFIO_MAPR_CAN2_REMAP |
AFIO_MAPR_USART2_REMAP;
/*
* Enabling the CAN controllers, then configuring GPIO functions for CAN_TX.
* Order matters, otherwise the CAN_TX pins will twitch, disturbing the CAN bus.
* This is why we can't perform this initialization using ChibiOS GPIO configuration.
*/
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
palSetPadMode(GPIOD, 1, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
#if UAVCAN_STM32_NUM_IFACES > 1
RCC->APB1ENR |= RCC_APB1ENR_CAN2EN;
palSetPadMode(GPIOB, 6, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
#endif
}
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#pragma once
#include <cstdint>
namespace board
{
void init();
__attribute__((noreturn))
void die(int error);
void setLed(bool state);
void restart();
constexpr unsigned UniqueIDSize = 12;
void readUniqueID(std::uint8_t bytes[UniqueIDSize]);
}
@@ -6,6 +6,8 @@
#include <zubax_chibios/sys/sys.h>
#include <uavcan_stm32/uavcan_stm32.hpp>
#include <uavcan/protocol/global_time_sync_slave.hpp>
#include <uavcan/protocol/dynamic_node_id_client.hpp>
#include "board/board.hpp"
namespace app
{
@@ -14,29 +16,17 @@ namespace
uavcan_stm32::CanInitHelper<128> can;
typedef uavcan::Node<16384> Node;
constexpr unsigned NodePoolSize = 16384;
uavcan::LazyConstructor<Node> node_;
Node& getNode()
uavcan::Node<NodePoolSize>& getNode()
{
if (!node_.isConstructed())
{
node_.construct<uavcan::ICanDriver&, uavcan::ISystemClock&>(can.driver, uavcan_stm32::SystemClock::instance());
}
return *node_;
}
void ledSet(bool state)
{
palWritePad(GPIO_PORT_LED, GPIO_PIN_LED, state);
static uavcan::Node<NodePoolSize> node(can.driver, uavcan_stm32::SystemClock::instance());
return node;
}
void init()
{
halInit();
chibios_rt::System::init();
sdStart(&STDOUT_SD, NULL);
board::init();
int res = 0;
do
@@ -54,80 +44,103 @@ void init()
while (res < 0);
}
#if __GNUC__
__attribute__((noreturn))
#endif
void die(int status)
{
lowsyslog("Initialization failure %i\n", status);
while (1)
{
ledSet(false);
sleep(1);
ledSet(true);
sleep(1);
}
}
class : public chibios_rt::BaseStaticThread<8192>
{
void configureNodeInfo()
{
getNode().setName("org.uavcan.stm32_test_stm32f107");
/*
* Software version
* TODO: Fill other fields too
*/
uavcan::protocol::SoftwareVersion swver;
swver.vcs_commit = GIT_HASH;
swver.optional_field_flags = swver.OPTIONAL_FIELD_FLAG_VCS_COMMIT;
getNode().setSoftwareVersion(swver);
lowsyslog("Git commit hash: 0x%08x\n", GIT_HASH);
/*
* Hardware version
* TODO: Fill other fields too
*/
uavcan::protocol::HardwareVersion hwver;
std::uint8_t uid[board::UniqueIDSize] = {};
board::readUniqueID(uid);
std::copy(std::begin(uid), std::end(uid), std::begin(hwver.unique_id));
getNode().setHardwareVersion(hwver);
lowsyslog("UDID:");
for (auto b : hwver.unique_id)
{
lowsyslog(" %02x", unsigned(b));
}
lowsyslog("\n");
}
void performDynamicNodeIDAllocation()
{
uavcan::DynamicNodeIDClient client(getNode());
const int client_start_res = client.start(getNode().getHardwareVersion().unique_id);
if (client_start_res < 0)
{
board::die(client_start_res);
}
lowsyslog("Waiting for dynamic node ID allocation...\n");
while (!client.isAllocationComplete())
{
const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(100));
if (spin_res < 0)
{
lowsyslog("Spin failure: %i\n", spin_res);
}
}
lowsyslog("Dynamic node ID %d allocated by %d\n",
int(client.getAllocatedNodeID().get()),
int(client.getAllocatorNodeID().get()));
getNode().setNodeID(client.getAllocatedNodeID());
}
public:
msg_t main()
{
/*
* Setting up the node parameters
*/
Node& node = app::getNode();
node.setNodeID(64);
node.setName("org.uavcan.stm32_test_stm32f107");
// TODO: fill software version info (version number, VCS commit hash, ...)
// TODO: fill hardware version info (version number, unique ID)
configureNodeInfo();
/*
* Initializing the UAVCAN node - this may take a while
* Initializing the UAVCAN node
*/
while (true)
const int node_init_res = getNode().start();
if (node_init_res < 0)
{
// Calling start() multiple times is OK - only the first successfull call will be effective
int res = node.start();
#if !UAVCAN_TINY
uavcan::NetworkCompatibilityCheckResult ncc_result;
if (res >= 0)
{
lowsyslog("Checking network compatibility...\n");
res = node.checkNetworkCompatibility(ncc_result);
}
#endif
if (res < 0)
{
lowsyslog("Node initialization failure: %i, will try agin soon\n", res);
}
#if !UAVCAN_TINY
else if (!ncc_result.isOk())
{
lowsyslog("Network conflict with %u, will try again soon\n", ncc_result.conflicting_node.get());
}
#endif
else
{
break;
}
::sleep(3);
board::die(node_init_res);
}
/*
* Waiting for a dynamic node ID allocation
*/
performDynamicNodeIDAllocation();
/*
* Time synchronizer
*/
static uavcan::GlobalTimeSyncSlave time_sync_slave(node);
static uavcan::GlobalTimeSyncSlave time_sync_slave(getNode());
{
const int res = time_sync_slave.start();
if (res < 0)
{
die(res);
board::die(res);
}
}
@@ -135,10 +148,10 @@ public:
* Main loop
*/
lowsyslog("UAVCAN node started\n");
node.setModeOperational();
getNode().setModeOperational();
while (true)
{
const int spin_res = node.spin(uavcan::MonotonicDuration::fromMSec(5000));
const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(5000));
if (spin_res < 0)
{
lowsyslog("Spin failure: %i\n", spin_res);
@@ -146,8 +159,10 @@ public:
lowsyslog("Time sync master: %u\n", unsigned(time_sync_slave.getMasterNodeID().get()));
lowsyslog("Memory usage: used=%u free=%u\n",
node.getAllocator().getNumUsedBlocks(), node.getAllocator().getNumFreeBlocks());
lowsyslog("Memory usage: free=%u used=%u worst=%u\n",
getNode().getAllocator().getNumFreeBlocks(),
getNode().getAllocator().getNumUsedBlocks(),
getNode().getAllocator().getPeakNumUsedBlocks());
lowsyslog("CAN errors: %lu %lu\n",
static_cast<unsigned long>(can.driver.getIface(0)->getErrorCount()),
@@ -179,7 +194,7 @@ int main()
{
for (int i = 0; i < 200; i++)
{
app::ledSet(app::can.driver.hadActivity());
board::setLed(app::can.driver.hadActivity());
::usleep(25000);
}
@@ -1,27 +0,0 @@
/*
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
*/
#include <ch.h>
#include <hal.h>
const PALConfig pal_default_config = {
{ VAL_GPIOAODR, VAL_GPIOACRL, VAL_GPIOACRH },
{ VAL_GPIOBODR, VAL_GPIOBCRL, VAL_GPIOBCRH },
{ VAL_GPIOCODR, VAL_GPIOCCRL, VAL_GPIOCCRH },
{ VAL_GPIODODR, VAL_GPIODCRL, VAL_GPIODCRH },
{ VAL_GPIOEODR, VAL_GPIOECRL, VAL_GPIOECRH }
};
void __early_init(void)
{
stm32_clock_init();
}
void boardInit(void)
{
AFIO->MAPR |=
AFIO_MAPR_CAN_REMAP_REMAP3 |
AFIO_MAPR_CAN2_REMAP |
AFIO_MAPR_USART2_REMAP;
}
@@ -20,6 +20,8 @@
#define GPIO_PORT_LED GPIOB
#define GPIO_PIN_LED 9
// GPIOD 10 is configured as OUTPUT, it is used as board reboot monitor.
/*
* I/O ports initial setup, this configuration is established soon after reset
* in the initialization code.
@@ -48,7 +50,7 @@
#define VAL_GPIOACRH 0x88888888 // 15..8
#define VAL_GPIOAODR 0x00000000
#define VAL_GPIOBCRL 0x8B488888
#define VAL_GPIOBCRL 0x84488888 // CAN2 TX initialized as INPUT, it must be configured later!
#define VAL_GPIOBCRH 0x88888828
#define VAL_GPIOBODR 0x00000000
@@ -56,9 +58,9 @@
#define VAL_GPIOCCRH 0x88888888
#define VAL_GPIOCODR 0x00000000
#define VAL_GPIODCRL 0x88b888B4
#define VAL_GPIODCRH 0x88888888
#define VAL_GPIODODR 0x00000000
#define VAL_GPIODCRL 0x88b88844 // CAN1 TX initialized as INPUT, it must be configured later!
#define VAL_GPIODCRH 0x88888288
#define VAL_GPIODODR ((1 << 10))
#define VAL_GPIOECRL 0x88888888
#define VAL_GPIOECRH 0x88888888