Libcanard는 무결성이 뛰어난 실시간 임베디드 시스템을 위한 C99/C11의 Cyphal/CAN 프로토콜 스택을 컴팩트하게 구현한 것입니다.
Cyphal은 CAN 버스, 이더넷 및 기타 강력한 전송을 통해 항공우주 및 로봇 응용 분야에서 안정적인 차량 내 통신을 위해 설계된 개방형 경량 데이터 버스 표준입니다.
libcanard/canard.h
에서 문서를 읽어보세요.
Cyphal 포럼에서 예제, 시작 도구, 튜토리얼을 찾아보세요.
기여하고 싶다면 CONTRIBUTING.md
읽어보세요.
이 라이브러리는 사용 가능한 표준 호환 컴파일러가 있는 한 깊이 내장된 베어메탈 플랫폼을 포함하여 모든 기존 8/16/32/64비트 플랫폼에서 즉시 사용할 수 있도록 설계되었습니다. 플랫폼별 미디어 IO 계층(드라이버)은 애플리케이션에서 제공되어야 합니다.
+---------------------------------+
| Application |
+-------+-----------------+-------+
| |
+-------+-------+ +-------+-------+
| Libcanard | | Media layer |
+---------------+ +-------+-------+
|
+-------+-------+
| Hardware |
+---------------+
OpenCyphal 개발팀은 https://github.com/OpenCyphal/platform_special_comComponents의 별도 저장소에 다양한 플랫폼별 구성 요소 모음을 유지 관리합니다. 사용자는 애플리케이션용 미디어 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 인터페이스당 하나의 전송 큐가 필요합니다.
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 직렬화 도우미가 제거되었습니다. 대신 누나부트(Nunavut)를 사용하세요.
기본적으로 비트별 CRC 계산을 훨씬 빠른 정적 테이블로 대체합니다(#185). 이는 CANARD_CRC_TABLE=0
설정하여 비활성화할 수 있으며, 이는 ca를 절약할 것으로 예상됩니다. 500바이트의 ROM.
API의 const 정확성 문제를 수정했습니다(#175).
canardRxAccept2()
canardRxAccept()
로 이름이 변경되었습니다.
CANARD_CONFIG_HEADER
를 통해 빌드 구성 헤더를 지원합니다.
CAN 하드웨어 수용 필터 구성을 생성하기 위한 API를 추가합니다(#169).
canardRxAccept2()
를 추가하고 canardRxAccept()
지원 중단합니다.CanardRxSubscription
에 사용자 참조를 제공하세요.초기 릴리스입니다.