Libcanard هو تطبيق مدمج لمجموعة بروتوكولات Cyphal/CAN في C99/C11 للأنظمة المدمجة عالية التكامل في الوقت الفعلي.
Cyphal هو معيار ناقل بيانات مفتوح وخفيف الوزن مصمم للاتصالات الموثوقة داخل المركبات في تطبيقات الفضاء والروبوتات عبر ناقل CAN والإيثرنت ووسائل النقل القوية الأخرى.
اقرأ المستندات في libcanard/canard.h
.
ابحث عن الأمثلة والمبتدئين والبرامج التعليمية في منتدى Cyphal.
إذا كنت ترغب في المساهمة، يرجى قراءة CONTRIBUTING.md
.
تم تصميم المكتبة لتكون قابلة للاستخدام خارج الصندوق مع أي نظام أساسي تقليدي 8/16/32/64 بت، بما في ذلك الأنظمة الأساسية المعدنية المضمنة بعمق، طالما يتوفر مترجم متوافق مع المعايير. من المفترض أن يتم توفير طبقة الإدخال والإخراج للوسائط (برنامج التشغيل) الخاصة بالنظام الأساسي بواسطة التطبيق:
+---------------------------------+
| Application |
+-------+-----------------+-------+
| |
+-------+-------+ +-------+-------+
| Libcanard | | Media layer |
+---------------+ +-------+-------+
|
+-------+-------+
| Hardware |
+---------------+
يحتفظ فريق تطوير OpenCyphal بمجموعة من المكونات المختلفة الخاصة بالنظام الأساسي في مستودع منفصل على https://github.com/OpenCyphal/platform_specified_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 زائدة عن الحاجة:
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 بايت. يتم توفير قيم النطاق لكل نوع بيانات بواسطة محولات DSDL مثل Nunavut.
في 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. يتم إنشاء مرشحات القبول في مخطط معرف + قناع ممتد 29 بت ويمكن استخدامها لتقليل عدد عمليات النقل غير ذات الصلة التي تتم معالجتها في البرنامج.
// 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).
إصلاح مشكلات التبعية في سلسلة أدوات عامل الإرساء.
لا توجد تغييرات في واجهة برمجة التطبيقات في هذا الإصدار باستثناء تغيير العلامة التجارية/إعادة التسمية: 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) المرتبطة بأشجار AVL السريعة O(log n) (يتم توزيع مكتبة Cavl باستخدام libcanard). يتطلب اجتياز قائمة اشتراكات RX الآن اجتياز الشجرة بشكل متكرر.
تمت إزالة مساعدات تسلسل DSDL اليدوية؛ استخدم نونافوت بدلاً من ذلك.
استبدل حساب CRC ذو البتات بجدول ثابت أسرع بكثير افتراضيًا (#185). يمكن تعطيل هذا عن طريق الإعداد CANARD_CRC_TABLE=0
، والذي من المتوقع أن يوفر كاليفورنيا. 500 بايت من ذاكرة القراءة فقط.
تم إصلاح مشكلات صحة الثبات في واجهة برمجة التطبيقات (#175).
تمت إعادة تسمية canardRxAccept2()
إلى canardRxAccept()
.
دعم بناء رؤوس التكوين عبر CANARD_CONFIG_HEADER
.
إضافة واجهة برمجة التطبيقات (API) لإنشاء تكوينات مرشح قبول أجهزة CAN (#169).
canardRxAccept2()
وإهمال canardRxAccept()
.CanardRxSubscription
.الإصدار الأولي.