Libcanard は、高整合性リアルタイム組み込みシステム向けの C99/C11 の Cyphal/CAN プロトコル スタックのコンパクトな実装です。
CyPhal は、CAN バス、イーサネット、その他の堅牢なトランスポートを介した、航空宇宙およびロボット アプリケーションにおける信頼性の高い車内通信のために設計されたオープン軽量データ バス規格です。
libcanard/canard.h
のドキュメントを読んでください。
CyPhal フォーラムで例、スターター、チュートリアルを見つけてください。
貢献したい場合は、 CONTRIBUTING.md
お読みください。
このライブラリは、標準準拠のコンパイラが利用できる限り、深く組み込まれたベアメタル プラットフォームを含む、従来の 8/16/32/64 ビット プラットフォームですぐに使用できるように設計されています。プラットフォーム固有のメディア IO レイヤー (ドライバー) は、アプリケーションによって提供されることになっています。
+---------------------------------+
| Application |
+-------+-----------------+-------+
| |
+-------+-------+ +-------+-------+
| Libcanard | | Media layer |
+---------------+ +-------+-------+
|
+-------+-------+
| Hardware |
+---------------+
OpenCypha 開発チームは、さまざまなプラットフォーム固有のコンポーネントのコレクションを別のリポジトリ (https://github.com/OpenCypha/platform_specific_components) に維持しています。ユーザーは、アプリケーションのメディア IO 層 (ドライバー) の設計を高速化するために、ターゲット アプリケーションで再利用できるドライバー、サンプル、その他の部分をそのリポジトリで検索することをお勧めします。
この例はドキュメントを拡張しますが、置き換えるものではありません。
ライブラリには、一定の複雑さの決定論的な動的メモリ アロケータが必要です。標準の C ヒープを使用することもできますが、ほとんどの実装は複雑さが一定ではないため、代わりに O1Heap を使用すると仮定します。基本的なラッパーが必要になります。
static void * memAllocate ( CanardInstance * const canard , const size_t amount )
{
( void ) canard ;
return o1heapAllocate ( my_allocator , amount );
}
static void memFree ( CanardInstance * const canard , void * const pointer )
{
( void ) canard ;
o1heapFree ( my_allocator , pointer );
}
ライブラリインスタンスを初期化します。
CanardInstance canard = canardInit ( & memAllocate , & memFree );
canard . node_id = 42 ; // Defaults to anonymous; can be set up later at any point.
ネットワーク経由で転送を送信できるようにするには、冗長 CAN インターフェイスごとに 1 つの送信キューが必要になります。
CanardTxQueue queue = canardTxInit ( 100 , // Limit the size of the queue at 100 frames.
CANARD_MTU_CAN_FD ); // Set MTU = 64 bytes. There is also CANARD_MTU_CAN_CLASSIC.
メッセージをパブリッシュします (メッセージのシリアル化は表示されていません)。
static uint8_t my_message_transfer_id ; // Must be static or heap-allocated to retain state between calls.
const CanardTransferMetadata transfer_metadata = {
. priority = CanardPriorityNominal ,
. transfer_kind = CanardTransferKindMessage ,
. port_id = 1234 , // This is the subject-ID.
. remote_node_id = CANARD_NODE_ID_UNSET , // Messages cannot be unicast, so use UNSET.
. transfer_id = my_message_transfer_id ,
};
++ my_message_transfer_id ; // The transfer-ID shall be incremented after every transmission on this subject.
int32_t result = canardTxPush ( & queue , // Call this once per redundant CAN interface (queue).
& canard ,
tx_deadline_usec , // Zero if transmission deadline is not limited.
& transfer_metadata ,
47 , // Size of the message payload (see Nunavut transpiler).
"x2Dx00" "Sancho, it strikes me thou art in great fear." );
if ( result < 0 )
{
// An error has occurred: either an argument is invalid, the TX queue is full, or we've run out of memory.
// It is possible to statically prove that an out-of-memory will never occur for a given application if the
// heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
}
Nunavut を使用して、DSDL 定義からシリアル化 (逆) コードを自動的に生成します。
メッセージ転送によって生成された CAN フレームはqueue
に保存されます。それを一つ一つ拾い上げて発信していかなければなりません。通常、次のフラグメントを定期的に呼び出して、優先順位付けされた送信キュー (または冗長ネットワーク インターフェイスが使用されている場合は複数) から CAN フレームを CAN ドライバーにアンロードする必要があります。
for ( const CanardTxQueueItem * ti = NULL ; ( ti = canardTxPeek ( & queue )) != NULL ;) // Peek at the top of the queue.
{
if (( 0U == ti -> tx_deadline_usec ) || ( ti -> tx_deadline_usec > getCurrentMicroseconds ())) // Check the deadline.
{
if (! pleaseTransmit ( ti )) // Send the frame over this redundant CAN iface.
{
break ; // If the driver is busy, break and retry later.
}
}
// After the frame is transmitted or if it has timed out while waiting, pop it from the queue and deallocate:
canard . memory_free ( & canard , canardTxPop ( & queue , ti ));
}
転送の受信は、いずれかの冗長インターフェイスから転送再組み立てステート マシンにフレームを供給することによって行われます。ただし、最初にサブスクライブする必要があります。
CanardRxSubscription heartbeat_subscription ;
( void ) canardRxSubscribe ( & canard , // Subscribe to messages uavcan.node.Heartbeat.
CanardTransferKindMessage ,
7509 , // The fixed Subject-ID of the Heartbeat message type (see DSDL definition).
16 , // The extent (the maximum possible payload size) provided by Nunavut.
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC ,
& heartbeat_subscription );
CanardRxSubscription my_service_subscription ;
( void ) canardRxSubscribe ( & canard , // Subscribe to an arbitrary service response.
CanardTransferKindResponse , // Specify that we want service responses, not requests.
123 , // The Service-ID whose responses we will receive.
1024 , // The extent (see above).
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC ,
& my_service_subscription );
「エクステント」とは、互換性のあるバージョンのデータ型のシリアル化された表現を保持するために必要なメモリの最小量を指します。言い換えれば、それは受信したオブジェクトの最大可能サイズです。このパラメーターは、データ型の定義時にデータ型の作成者によって決定されます。通常、データ型の作成者がその型の将来のバージョンでより多くのフィールドを導入できるようにするために、最大オブジェクト サイズよりも大きくなります。たとえば、 MyMessage.1.0
の最大サイズは 100 バイト、範囲は 200 バイトである可能性があります。改訂版MyMessage.1.1
最大サイズは 0 ~ 200 バイトです。エクステント値は、Nunavut などの DSDL トランスコンパイラーによってデータ型ごとに提供されます。
Libcanard では、わかりやすくするために、サブジェクト (メッセージ) だけでなく、サービスに対しても「サブスクリプション」という用語を使用します。
実行時に何度でもサブスクライブしたり、サブスクライブを解除したりできます。ただし、通常、組み込みアプリケーションは一度サブスクライブして、それを使用します。さて、これが転送を受け取る方法です。
CanardRxTransfer transfer ;
const int8_t result = canardRxAccept ( & canard ,
rx_timestamp_usec , // When the frame was received, in microseconds.
& received_frame , // The CAN frame received from the bus.
redundant_interface_index , // If the transport is not redundant, use 0.
& transfer ,
NULL );
if ( result < 0 )
{
// An error has occurred: either an argument is invalid or we've ran out of memory.
// It is possible to statically prove that an out-of-memory will never occur for a given application if
// the heap is sized correctly; for background, refer to the Robson's Proof and the documentation for O1Heap.
// Reception of an invalid frame is NOT an error.
}
else if ( result == 1 )
{
processReceivedTransfer ( redundant_interface_index , & transfer ); // A transfer has been received, process it.
canard . memory_free ( & canard , transfer . payload ); // Deallocate the dynamic memory afterwards.
}
else
{
// Nothing to do.
// The received frame is either invalid or it's a non-last frame of a multi-frame transfer.
// Reception of an invalid frame is NOT reported as an error because it is not an error.
}
CAN ハードウェア アクセプタンス フィルター構成を生成するためのシンプルな API も提供されています。アクセプタンス フィルターは拡張 29 ビット ID + マスク スキームで生成され、ソフトウェアで処理される無関係な転送の数を最小限に抑えるために使用できます。
// Generate an acceptance filter to receive only uavcan.node.Heartbeat.1.0 messages (fixed port-ID 7509):
CanardFilter heartbeat_config = canardMakeFilterForSubject ( 7509 );
// And to receive only uavcan.register.Access.1.0 service transfers (fixed port-ID 384):
CanardFilter register_access_config = canardMakeFilterForService ( 384 , ins . node_id );
// You can also combine the two filter configurations into one (may also accept irrelevant 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 Cyphal specification.
CanardFilter combined_config =
canardConsolidateFilters ( & heartbeat_config , & register_access_config );
configureHardwareFilters ( combined_config . extended_can_id , combined_config . extended_mask );
完全な API 仕様はドキュメントで入手できます。例が不明瞭または間違っていると思われる場合は、チケットを開いてください。
canardRxGetSubscription
を追加しました。UAVCAN v1 の名前が Cyphal に変更されたため、ブランドを更新します。
フレックス アレイの使用を削除して MISRA 準拠を改善します: (#192)。
Docker ツールチェーンの依存関係の問題を修正します。
このリリースでは、ブランド変更/名前変更以外に API の変更はありません: CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR
CANARD_UAVCAN_SPECIFICATION_VERSION_MINOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MINOR
_canard_cavl.h
(#196) に変更することで、ヘッダー ファイル名の衝突のリスクを排除します。深さ制限のある冗長 CAN インターフェイスごとの専用送信キュー。アプリケーションは現在、 CanardTxQueue
(または冗長トランスポートの場合は複数) を手動でインスタンス化することが期待されています。
O(n) 個のリンク リストを高速 O(log n) AVL ツリーに置き換えます (Cavl ライブラリは libcanard で配布されます)。 RX サブスクリプションのリストを走査するには、ツリーの再帰的走査が必要になりました。
手動 DSDL シリアル化ヘルパーが削除されました。代わりにヌナブト準州を使用してください。
ビットごとの CRC 計算をデフォルトでより高速な静的テーブルに置き換えます (#185)。これは、 CANARD_CRC_TABLE=0
設定することで無効にできます。 500バイトのROM。
API の const-correctness に関する問題を修正しました (#175)。
canardRxAccept2()
名前がcanardRxAccept()
に変更されました。
CANARD_CONFIG_HEADER
を介してビルド構成ヘッダーをサポートします。
CAN ハードウェア アクセプタンス フィルター構成を生成するための API を追加しました (#169)。
canardRxAccept2()
を追加し、 canardRxAccept()
を廃止します。CanardRxSubscription
にユーザー参照を提供します。初期リリース。