Skip to content

Commit

Permalink
add acceptance filter configuration helpers
Browse files Browse the repository at this point in the history
Adds helper API functions for configuring CAN controller hardware
acceptance filters.
  • Loading branch information
coderkalyan committed Nov 3, 2021
1 parent 5bf4c3c commit 0b2eba7
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 1 deletion.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,28 @@ else
}
```

A simple API for generating CAN hardware acceptance filter configurations is also provided.
Acceptance filters are generated in an extended 29-bit ID + mask scheme and can be used to minimize the number of irrelevant transfers processed in software.

```c
// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port ID 7509):
CanardAcceptanceFilterConfig heartbeat_config = canardMakeAcceptanceFilterConfigForSubject(7509);
CanardAcceptanceFilterConfig register_access_config = canardMakeAcceptanceFilterConfigForService(384, ins.node_id);

// You can also combine the two filter configurations into one (may also accept in other messages).
// This allows consolidating a large set of configurations to fit the number of hardware filters.
// For more information on the optimal subset of configurations to consolidate to minimize wasted CPU,
// see the UAVCAN specification.
CanardAcceptanceFilterConfig combined_config = canardConsolidateAcceptanceFilterConfigs(&heartbeat_config, &register_access_config);
configureHardwareFilters(combined_config.extended_can_id, combined_config.extended_mask);
```
Full API specification is available in the documentation.
If you find the examples to be unclear or incorrect, please, open a ticket.
## Revisions
### v2.0

- Dedicated transmission queues per redundant CAN interface with depth limits.
The application is now expected to instantiate `CanardTxQueue` (or several in case of redundant transport) manually.
Expand All @@ -225,6 +243,8 @@ else
- Support build configuration headers via `CANARD_CONFIG_HEADER`.
- Add API for generating CAN hardware acceptance filter configurations
### v1.1
- Add new API function `canardRxAccept2()`, deprecate `canardRxAccept()`.
Expand Down
44 changes: 44 additions & 0 deletions libcanard/canard.c
Original file line number Diff line number Diff line change
Expand Up @@ -1165,3 +1165,47 @@ int8_t canardRxUnsubscribe(CanardInstance* const ins,
}
return out;
}

CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForSubject(const CanardPortID subject_id)
{
CanardAcceptanceFilterConfig out = {0};

out.extended_can_id = subject_id << OFFSET_SUBJECT_ID;
out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_07 | (CANARD_SUBJECT_ID_MAX << OFFSET_SUBJECT_ID);

return out;
}

CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForService(const CanardPortID service_id,
const CanardNodeID local_node_id)
{
CanardAcceptanceFilterConfig out = {0};

out.extended_can_id =
FLAG_SERVICE_NOT_MESSAGE | (service_id << OFFSET_SERVICE_ID) | (local_node_id << OFFSET_DST_NODE_ID);
out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_23 | (CANARD_SERVICE_ID_MAX << OFFSET_SERVICE_ID) |
(CANARD_NODE_ID_MAX << OFFSET_DST_NODE_ID);

return out;
}

CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForServices(const CanardNodeID local_node_id)
{
CanardAcceptanceFilterConfig out = {0};

out.extended_can_id = FLAG_SERVICE_NOT_MESSAGE | (local_node_id << OFFSET_DST_NODE_ID);
out.extended_mask = FLAG_SERVICE_NOT_MESSAGE | FLAG_RESERVED_23 | (CANARD_NODE_ID_MAX << OFFSET_DST_NODE_ID);

return out;
}

CanardAcceptanceFilterConfig canardConsolidateAcceptanceFilterConfigs(const CanardAcceptanceFilterConfig* a,
const CanardAcceptanceFilterConfig* b)
{
CanardAcceptanceFilterConfig out = {0};

out.extended_mask = a->extended_mask & b->extended_mask & ~(a->extended_can_id ^ b->extended_can_id);
out.extended_can_id = a->extended_can_id & out.extended_mask;

return out;
}
62 changes: 62 additions & 0 deletions libcanard/canard.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,20 @@ struct CanardInstance
CanardTreeNode* rx_subscriptions[CANARD_NUM_TRANSFER_KINDS];
};

/// CAN acceptance filter configuration with an extended 29-bit ID utilizing an ID + mask filter scheme.
/// Filter configuration can be programmed into a CAN controller to filter out irrelevant messages in hardware.
/// This allows the software application to reduce CPU load spent on processing irrelevant messages.
typedef struct CanardAcceptanceFilterConfig
{
/// 29-bit extended ID. Defines the extended CAN ID to filter incoming frames against.
/// The bits above 29-th shall be zero.
uint32_t extended_can_id;
/// 29-bit extended mask. Defines the bitmask used to enable/disable bits used to filter messages.
/// Only bits that are enabled are compared to the extended_can_id for filtering.
/// The bits above 29-th shall be zero.
uint32_t extended_mask;
} CanardAcceptanceFilterConfig;

/// Construct a new library instance.
/// The default values will be assigned as specified in the structure field documentation.
/// If any of the pointers are NULL, the behavior is undefined.
Expand Down Expand Up @@ -635,6 +649,54 @@ int8_t canardRxUnsubscribe(CanardInstance* const ins,
const CanardTransferKind transfer_kind,
const CanardPortID port_id);

/// Generate an acceptance filter configuration to accept a specific subject ID.
///
/// Complex applications will likely subscribe to more subject IDs than there are
/// acceptance filters available in the CAN hardware. In this case, the application
/// should implement filter consolidation. See canardConsolidateAcceptanceFilterConfigs()
/// as well as the UAVCAN specification for details.
CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForSubject(const CanardPortID subject_id);

/// Generate an acceptance filter configuration to accept a specific service.
///
/// Complex applications will likely subscribe to more service IDs than there are
/// acceptance filters available in the CAN hardware. In this case, the application
/// should implement filter consolidation. See canardConsolidateAcceptanceFilterConfigs()
/// as well as the UAVCAN specification for details.
///
/// Users may prefer to instead use a catch-all acceptance filter configuration for accepting
/// all service requests and responses targeted at the specified local node ID.
/// See canardMakeAcceptanceFilterConfigForServices() for this.
CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForService(const CanardPortID service_id,
const CanardNodeID local_node_id);

/// Generate an acceptance filter configuration to accept all service
/// requests or responses targeted to the specified local node ID.
///
/// Due to the relatively low frequency of service transfers expected on a network,
/// and the fact that a service directed at a specific node is not likely to be rejected by that node,
/// a user may prefer to use this over canardMakeAcceptanceFilterConfigForService()
/// in order to simplify the API usage and reduce the number of required hardware CAN acceptance filters.
CanardAcceptanceFilterConfig canardMakeAcceptanceFilterConfigForServices(const CanardNodeID local_node_id);

/// Consolidate two acceptance filter configurations into a single configuration.
///
/// Complex applications will likely subscribe to more subject IDs than there are
/// acceptance filters available in the CAN hardware. In this case, the application
/// should implement filter consolidation. While this may make it impossible to create
/// a 'perfect' filter that only accepts desired subject IDs, the application should apply
/// consolidation in a manner that minimizes the number of undesired messages that pass
/// through the hardware acceptance filters and require software filtering (implemented by canardRxSubscribe).
///
/// While optimal choice of filter consolidation is a function of the number of available hardware filters,
/// the set of transfers needed by the application, and the expected frequency of occurence
/// of all possible distinct transfers on the bus, it is possible to generate a quasi-optimal configuration
/// if information about the frequency of occurence of different transfers is not known.
/// For details, see the "Automatic hardware acceptance filter configuration" note under the UAVCAN/CAN section
/// in the Transport Layer chapter of the UAVCAN specification.
CanardAcceptanceFilterConfig canardConsolidateAcceptanceFilterConfigs(const CanardAcceptanceFilterConfig* a,
const CanardAcceptanceFilterConfig* b);

#ifdef __cplusplus
}
#endif
Expand Down

0 comments on commit 0b2eba7

Please sign in to comment.