Libcanard adalah implementasi kompak dari tumpukan protokol Cyphal/CAN di C99/C11 untuk sistem tertanam real-time berintegritas tinggi.
Cyphal adalah standar bus data ringan terbuka yang dirancang untuk komunikasi intrakendaraan yang andal dalam aplikasi luar angkasa dan robot melalui bus CAN, Ethernet, dan transportasi tangguh lainnya.
Baca dokumen di libcanard/canard.h
.
Temukan contoh, permulaan, tutorial di forum Cyphal.
Jika Anda ingin berkontribusi, silakan baca CONTRIBUTING.md
.
Pustaka ini dirancang agar dapat langsung digunakan dengan platform 8/16/32/64-bit konvensional apa pun, termasuk platform baremetal yang tertanam dalam, selama tersedia kompiler yang memenuhi standar. Lapisan IO media khusus platform (driver) seharusnya disediakan oleh aplikasi:
+---------------------------------+
| Application |
+-------+-----------------+-------+
| |
+-------+-------+ +-------+-------+
| Libcanard | | Media layer |
+---------------+ +-------+-------+
|
+-------+-------+
| Hardware |
+---------------+
Tim Pengembangan OpenCyphal menyimpan koleksi berbagai komponen khusus platform dalam repositori terpisah di https://github.com/OpenCyphal/platform_special_components. Pengguna didorong untuk mencari melalui repositori tersebut untuk driver, contoh, dan bagian lain yang dapat digunakan kembali dalam aplikasi target untuk mempercepat desain lapisan IO media (driver) untuk aplikasi tersebut.
Contoh ini menambah dokumentasi tetapi tidak menggantikannya.
Perpustakaan memerlukan pengalokasi memori dinamis deterministik kompleksitas konstan. Kita dapat menggunakan heap C standar, namun sebagian besar implementasinya tidak memiliki kompleksitas yang konstan, jadi anggaplah kita menggunakan O1Heap sebagai gantinya. Kita membutuhkan pembungkus dasar:
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 );
}
Init contoh perpustakaan:
CanardInstance canard = canardInit ( & memAllocate , & memFree );
canard . node_id = 42 ; // Defaults to anonymous; can be set up later at any point.
Agar dapat mengirim transfer melalui jaringan, kita memerlukan satu antrian transmisi per antarmuka CAN redundan:
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.
Publikasikan pesan (serialisasi pesan tidak ditampilkan):
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.
}
Gunakan Nunavut untuk secara otomatis menghasilkan (de)kode serialisasi dari definisi DSDL.
Frame CAN yang dihasilkan dari transfer pesan sekarang disimpan dalam queue
. Kita perlu memilihnya satu per satu dan mengirimkannya. Biasanya, fragmen berikut harus dipanggil secara berkala untuk membongkar frame CAN dari antrian transmisi yang diprioritaskan (atau beberapa, jika antarmuka jaringan redundan digunakan) ke dalam driver 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 ));
}
Penerimaan transfer dilakukan dengan memasukkan frame ke dalam mesin status perakitan ulang transfer dari salah satu antarmuka redundan. Tapi pertama-tama, kita perlu berlangganan:
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 );
"Tingkat" mengacu pada jumlah minimum memori yang diperlukan untuk menampung representasi serial dari versi tipe data apa pun yang kompatibel; atau, dengan kata lain, ini adalah ukuran maksimum objek yang diterima. Parameter ini ditentukan oleh pembuat tipe data pada waktu definisi tipe data. Biasanya lebih besar dari ukuran objek maksimum untuk memungkinkan penulis tipe data memperkenalkan lebih banyak bidang di versi tipe data yang akan datang; misalnya, MyMessage.1.0
mungkin memiliki ukuran maksimum 100 byte dan luasnya 200 byte; versi revisi MyMessage.1.1
mungkin memiliki ukuran maksimum antara 0 dan 200 byte. Nilai tingkat disediakan per tipe data oleh transkompiler DSDL seperti Nunavut.
Di Libcanard kami menggunakan istilah "langganan" tidak hanya untuk subjek (pesan), tetapi juga untuk layanan, untuk kesederhanaan.
Kita dapat berlangganan dan berhenti berlangganan pada saat runtime sebanyak yang kita inginkan. Biasanya, bagaimanapun, aplikasi yang tertanam akan berlangganan satu kali dan terus menggunakannya. Oke, begini cara kami menerima transfer:
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.
}
API sederhana untuk menghasilkan konfigurasi filter penerimaan perangkat keras CAN juga disediakan. Filter penerimaan dihasilkan dalam skema ID + mask 29-bit yang diperluas dan dapat digunakan untuk meminimalkan jumlah transfer tidak relevan yang diproses dalam perangkat lunak.
// 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 );
Spesifikasi API lengkap tersedia di dokumentasi. Jika Anda menemukan contohnya tidak jelas atau salah, silakan buka tiket.
canardRxGetSubscription
baru.Perbarui pencitraan merek saat UAVCAN v1 diganti namanya menjadi Cyphal.
Tingkatkan kepatuhan MISRA dengan menghapus penggunaan array fleksibel: (#192).
Perbaiki masalah ketergantungan pada rantai alat buruh pelabuhan.
Tidak ada perubahan API dalam rilis ini selain rebranding/penggantian nama: CANARD_UAVCAN_SPECIFICATION_VERSION_MAJOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MAJOR
CANARD_UAVCAN_SPECIFICATION_VERSION_MINOR
-> CANARD_CYPHAL_SPECIFICATION_VERSION_MINOR
_canard_cavl.h
(#196). Antrean transmisi khusus per antarmuka CAN redundan dengan batas kedalaman. Aplikasi ini sekarang diharapkan untuk membuat instance CanardTxQueue
(atau beberapa jika terjadi transportasi berlebihan) secara manual.
Ganti daftar tertaut O(n) dengan pohon AVL O(log n) cepat (perpustakaan Cavl didistribusikan dengan libcanard). Melintasi daftar langganan RX sekarang memerlukan traversal pohon secara rekursif.
Pembantu serialisasi DSDL manual dihapus; gunakan Nunavut sebagai gantinya.
Ganti perhitungan CRC bitwise dengan tabel statis yang jauh lebih cepat secara default (#185). Ini dapat dinonaktifkan dengan mengatur CANARD_CRC_TABLE=0
, yang diharapkan dapat menghemat ca. ROM 500 byte.
Memperbaiki masalah dengan kebenaran-konstanta di API (#175).
canardRxAccept2()
berganti nama menjadi canardRxAccept()
.
Mendukung header konfigurasi build melalui CANARD_CONFIG_HEADER
.
Tambahkan API untuk menghasilkan konfigurasi filter penerimaan perangkat keras CAN (#169).
canardRxAccept2()
, hentikan canardRxAccept()
.CanardRxSubscription
.Rilis awal.