RS-485 على ميكروكنترولر المحلية من شركة Milander


قبل بضعة أيام كان لدي الحماقة في الوعد المحجوب بخفض وظيفة عن ميلاندر ... حسنًا ، دعنا نحاول.

كما تعلمون بالفعل ، هناك شركة Milander روسية ، والتي ، من بين أمور أخرى ، تنتج ميكروكنترولر على أساس ARM Cortex-M. بناءً على إرادة القدر ، أُجبرت على التعرف عليهم بإحكام ، وعرفت الألم .

ويرد وصف جزء صغير من هذا الألم الناجم عن العمل مع RS-485 أدناه. أعتذر مقدمًا إذا مضغت كثيرًا على المفاهيم الأساسية ، ولكني أردت جعل هذا المقال متاحًا لفهم جمهور أوسع.
سأقوم أيضًا بالحجز مقدمًا أنني تعاملت فقط مع عامي 1986 و 1991 و1891 ، ولا يمكنني التحدث بثقة عن الآخرين.

TL د
تفتقر Milandrovsk UART إلى مقاطعة "Transmit Complete" ، العكاز هو "وضع اختبار الاسترجاع" ، أي وضع الصدى. ولكن مع الفروق الدقيقة.

الدخول


تعد واجهة RS-485 (المعروفة أيضًا باسم EIA-485 ، على الرغم من أنني لم أسمع بها مطلقًا في الحياة اليومية) واجهة غير متزامنة أحادية الاتجاه مع طوبولوجيا ناقل. ينص هذا المعيار على الفيزياء فقط - أي مستويات الجهد ومخططات التوقيت - ولكنها لا تحدد بروتوكول التبادل والحماية من أخطاء الإرسال والتحكيم وما شابه.

في الواقع ، RS-485 هو مجرد UART أحادي الاتجاه مع مستويات أعلى من الجهد التفاضلي. هذا البساطة هو ما يضمن شعبية RS-485.
لتحويل UART إلى RS-485 ، يتم استخدام الدوائر الدقيقة المحولة الخاصة ، مثل MAX485 أو 5559IN10AU (من نفس Milander). إنهم يعملون "بشفافية" تقريبًا للمبرمج ، حيث يمكنهم فقط اختيار الوضع الصحيح لتشغيل الرقاقة - الاستقبال أو الإرسال. يتم ذلك باستخدام الأرجل nRE (وليس تمكين إخراج المستقبِل) و DE (تمكين خرج التشغيل) ، والتي يتم ، كقاعدة عامة ، دمجها والتحكم فيها بواسطة إحدى ساق جهاز التحكم الدقيق.

رفع هذه الساق يحول الشريحة إلى ناقل الحركة ، ويخفضها إلى الاستقبال.
وفقًا لذلك ، كل ما هو مطلوب من المبرمج هو رفع هذه المحطة RE-DE ونقل العدد المطلوب من البايتات وخفض الساق وانتظار الإجابة. يبدو بسيطا بما فيه الكفاية ، أليس كذلك؟
الكالينجيون

المشكلة


يجب خفض هذه الساق في وقت يتم فيه نقل جميع وحدات البايت المرسلة بالكامل إلى الخط. كيفية التقاط هذه اللحظة؟ للقيام بذلك ، تحتاج إلى التقاط الحدث "Transmit Complete" (اكتمل الإرسال) ، الذي يولد كتلة UART في متحكم دقيق. بالنسبة للجزء الأكبر ، يتم تعيين الأحداث قليلاً في بعض طلب التسجيل أو المقاطعة. للوقوف على الإعداد قليلاً في السجل ، يجب أن يتم استقصاء السجل ، أي استخدام رمز مثل هذا:

while( MDR_UART1->FR & UART_FR_BUSY ) {;} 

هذا إذا استطعنا إيقاف البرنامج تمامًا حتى يتم نقل جميع وحدات البايت. كقاعدة عامة ، لا يمكننا تحمل هذا.

الانقطاع في هذا الصدد أكثر ملاءمة ، لأنه يصل من تلقاء نفسه ، بشكل غير متزامن. في المقاطعة ، يمكننا أن نتجاهل بسرعة RE-DE والعمل بأكمله.

بالطبع ، إذا استطعنا القيام بذلك ، فلن يكون هناك أي ألم ، ولن يكون هذا المنشور كذلك.

والحقيقة هي أنه في كتلة UART ، التي يضعها Milander في جميع ميكروكنترولر الخاصة به على Cortex-M (حسب علمي) ، لا يوجد أي انقطاع لحدث "Transfer Complete". لا يوجد سوى العلم. وهناك مقاطعة "العازلة من الارسال فارغة." والبايت المقاطعة ، بطبيعة الحال.

لا يزال لديك
مجموعة من المقاطعات الأخرى ووضع FIFO ، في رأيي ، عديمة الفائدة تمامًا. إذا كان أي شخص يفهم سبب الحاجة إليه ، فيرجى إخبارنا!

تكمن المشكلة في أن "المخزن المؤقت للإرسال فارغ" - وهذا لا ينطبق على الإطلاق على "إكمال النقل". بقدر ما أفهم جهاز UART الداخلي ، يعني الحدث "Buffer Empty" أن هناك مساحة حرة واحدة على الأقل في المخزن المؤقت للمرسل. حتى لو كان هذا المكان واحدًا فقط (على سبيل المثال ، مخزن مؤقت بحجم بايت واحد) ، فهذا يعني فقط أنه تم نسخ آخر بايت تم نقله إلى سجل الإزاحة الداخلي ، والذي ستخرج منه هذه البايتة إلى الخارج ، فشيئًا فشيئًا.

باختصار ، حدث "المخزن المؤقت للمرسل فارغ" لا يعني أن جميع وحدات البايت قد تم إرسالها بالكامل. إذا أغفلنا RE-DE في هذه اللحظة ، فسوف "نقطع" مجموعتنا.

ماذا تفعل؟

ريبوس



فك التشفير:
"حقول حقول الأعشاب الضارة" هي ميم محلية من موضوع قصير ولكنه مؤلم في منتدى Milandra - forum.milandr.ru/viewtopic.php؟f=33&t=626 .
الحل الأبسط هو "إزالة الأعشاب الضارة" (من "استطلاع" اللغة الإنجليزية - استطلاع مستمر) علامة UART_FR_BUSY.

بالطبع ، هذا الحل ليس لطيفًا جدًا. إذا لم نتمكن من حظر هذه العلامة ، فعلينا التحقق منها بشكل دوري. للتحقق من ذلك بشكل دوري ، يجب عليك سور حديقة كاملة (خاصةً إذا كنت ترغب في كتابة وحدة محمولة ، وليس فقط حل هذه المشكلة لمرة واحدة).

إذا كنت تستخدم نوعًا من RTOS ، فمن أجل هذا التطهير ، يتعين عليك بدء مهمة منفصلة بالكامل ، والاستيقاظ في حالة انقطاع ، وتعيينها ليست أقل أولوية ، أو المتاعب ، باختصار.

ولكن ، على ما يبدو ، حسنا ، تعذب مرة واحدة ، ثم نستخدم ونفرح. لكن لا.
لسوء الحظ ، لا يكفي أن نتجاهل RE-DE بصرامة بعد إرسال جميع البايتات إلى النهاية. نحن بحاجة إلى خفضه ليس بعد فوات الأوان . لأننا لسنا وحدنا في الحافلة. على الأرجح ، يجب أن تصل رسالتنا إلى نوع من الإجابة من مشترك آخر. وإذا تجاهلنا RE-DE بعد فوات الأوان ، فلن نتحول إلى وضع الاستلام ونفقد بعض وحدات بت الاستجابة.

يعتمد الوقت الذي يمكننا فيه تحمل تكلفة "تعريض مفرط" لساق RE-DE بشكل أساسي على سرعة النقل (معدل البث بالباود) وعلى سرعة الجهاز الذي نتواصل معه على الحافلة.
في حالتي ، كانت السرعة منخفضة نسبيًا (57600 باود) ، وكان الجهاز شديد الرقة. وأحيانًا حدث أن الجواب فقد قليلاً أو اثنين.

عموما ، ليس حلا جيدا.

الموقت


الخيار الثاني الذي يتبادر إلى الذهن هو استخدام جهاز توقيت الأجهزة. بعد ذلك ، في المقاطعة "إفراغ المخزن المؤقت للإرسال" ، نبدأ مؤقتًا مع مهلة مساوية لوقت إرسال بايت واحد (يتم حساب هذه المرة بسهولة من baudrate) ، وفي المقاطعة من الموقت ، نخفض الساق.

طريقة جيدة وموثوقة. الموقت فقط هو أمر مؤسف. تقليديا لا يوجد الكثير منهم في ميلاندرا - قطعتين أو ثلاث قطع.

وضع حلقة


إذا كنت تقرأ بعناية تلك. وصف على UART - على سبيل المثال ، لعام 1986 BE91T - يمكنك ملاحظة هذه الفقرة القصيرة جدًا:



( ) 1 LBE UARTCR.


إذا هؤلاء. إذا لم تقرأ الوصف ، فيمكن تحقيق التأثير نفسه تقريبًا عن طريق تقصير أرجل أجهزة RX و TX.

الأفكار بصوت عال
ومن المثير للاهتمام ، أين هذا نوع من حلقة؟ عادة ما يسمى هذا الوضع "صدى" ، ولكن حسنا.

الفكرة كالتالي - قبل إرسال البايت الأخير في الحزمة ، تحتاج إلى تنشيط وضع "الاسترجاع". ثم يمكنك الحصول على مقاطعة لتلقي البايت الأخير الخاص بنا في الوقت الحالي عندما يزحف بالكامل إلى الحافلة! حسنا ، تقريبا.

في الممارسة العملية ، اتضح أن المقاطعة على الاستقبال يتم تشغيلها في وقت أبكر مما ينبغي ، حوالي ثلث الفاصل الزمني للبت. أنا لا أعرف ما يرتبط هذا ؛ من المحتمل أنه في وضع اختبار الحلقة لا تحدث أي عينات حقيقية للخط ، وربما لا يأخذ وضع الحلقة في الاعتبار آخر نقطة توقف. لا اعرف على الرغم من ذلك ، لا يمكننا حذف RE-DE فور دخول هذه المقاطعة ، لأن هذه هي الطريقة التي "نقطع بها" جزء الإيقاف أو جزء منه عن آخر بايت.

بالمعنى الدقيق للكلمة ، لا يمكننا أو لا نعتمد على نسبة سرعة الواجهة (أي مدة الفاصل الزمني بت واحد) وتردد متحكم دقيق ، لكنني لم أستطع الوصول إلى تردد 80 ميغاهرتز بمعدل سرعة البث بالباود 57600.

خيارات أخرى ممكنة.

إذا كنت لا تستطيع الاستطلاع على علامة UART_FR_BUSY عن فاصل زمني واحد - في الواقع ، حتى أقل قليلاً ، لأن الدخول في الفحوصات الأولية والفحصية يستغرق أيضًا وقتًا - فسيتم العثور على الحل. لسرعة 57600 ، سيكون الحد الأقصى لوقت الاقتراع 18 ميكروثانية تقريبًا (فاصل زمني واحد بت) ، في الممارسة العملية - حوالي 5 ميكروثانية.

بالنسبة لأولئك المهتمين ، أقتبس رمز معالج المقاطعة بأكمله.
 void Handle :: irqHandler(void) { UMBA_ASSERT( m_isInited ); m_irqCounter++; // ---------------------------------------------  // do     break do { if ( UART_GetITStatusMasked( m_mdrUart, UART_IT_RX ) != SET ) break; // -,     ,     UART_ClearITPendingBit( m_mdrUart, UART_IT_RX ); uint8_t byte = UART_ReceiveData( m_mdrUart ); //  485   ,        if( m_rs485Port != nullptr && m_echoBytesCounter > 0 ) { //     m_echoBytesCounter--; if( m_echoBytesCounter == 0 ) { //     ____, //        ,  -      // -   . //   ,      -. //     ,   : //  |  , |  , | // |  |  | // | | | // 9600 | 105 | 32 | // 57600 | 18 | 4,5 | // 921600 | 1 | 0 | // | | | //      /  , //      . // ,     ,   . //    while( m_mdrUart->FR & UART_FR_BUSY ) {;} //          rs485TransmitDisable(); // ,    #ifdef UART_USE_FREERTOS osSemaphoreGiveFromISR( m_transmitCompleteSem, NULL ); #endif } break; } //      -      overrun #ifdef UART_USE_FREERTOS BaseType_t result = osQueueSendToBackFromISR( m_rxQueue, &byte, NULL ); if( result == errQUEUE_FULL ) { m_isRxOverrun = true; } #else if( m_rxBuffer.isFull() ) { m_isRxOverrun = true; } else { m_rxBuffer.writeHead(byte); } #endif } while( 0 ); // ---------------------------------------------  //    -   ! //  ,  SPL    m_error = m_mdrUart->RSR_ECR; if( m_error != error_none ) { //     m_mdrUart->RSR_ECR = 0; } // ---------------------------------------------  if( UART_GetITStatusMasked( m_mdrUart, UART_IT_TX ) != SET ) return; //    485 -    if( m_txCount == m_txMsgSize - 1 && m_rs485Port != nullptr ) { setEchoModeState( true ); m_echoBytesCounter = 2; } //   else if( m_txCount == m_txMsgSize ) { //    ( )      UART_ClearITPendingBit( m_mdrUart, UART_IT_TX ); m_pTxBuf = nullptr; return; } //  ,   UMBA_ASSERT( m_pTxBuf != nullptr ); UART_SendData( m_mdrUart, m_pTxBuf[ m_txCount ] ); m_txCount++; } 


إذا كنت تستطيع تحمل العبور (التحكم المثالي) بين أرجل RX و TX ، فكل شيء على ما يرام.

لسوء الحظ ، اليوم لا أستطيع تقديم خيارات أخرى.

هذا كل شيء بالنسبة لي. إذا كان أي شخص يعرف طرقًا أخرى لحل هذه المشكلة ، فيرجى مشاركتها في التعليقات.

أيضًا ، وأغتنم الفرصة وتغيير قواعد Habr ، أريد الترويج لموقع StartMilandr ، والذي هو عبارة عن مجموعة من المقالات حول Microcontrollers. لسبب غير واضح ، يمكنك جوجل جوجل فقط عن طريق الصدفة.

وبطبيعة الحال ، تذكر وجود شوكة في المكتبة الطرفية القياسية ، والتي ، على عكس المكتبة الرسمية ، يتم إصلاح الخلل وهناك دعم gcc.

Source: https://habr.com/ru/post/ar439450/


All Articles