اختراع مكتبة vusb

مقدمة


بعد قراءة العنوان ، قد ينشأ سؤال منطقي: لماذا تدرس في الوقت الحاضر تنفيذ برنامج USB منخفض السرعة عندما يكون هناك مجموعة من وحدات التحكم الرخيصة باستخدام وحدة الأجهزة؟ الحقيقة هي أن وحدة الأجهزة ، التي تخفي مستوى تبادل المستويات المنطقية ، تحول بروتوكول USB إلى نوع من السحر. لكي تشعر كيف يعمل هذا "السحر" ، لا يوجد شيء أفضل من إعادة إنتاجه من نقطة الصفر ، بدءًا من المستوى الأدنى.


تحقيقًا لهذه الغاية ، سنحاول جعل جهازًا يتظاهر بأنه USB-HID استنادًا إلى جهاز التحكم ATmega8. على عكس الأدب الواسع الانتشار ، لن ننتقل من النظرية إلى الممارسة ، من المستوى الأدنى إلى الأعلى ، من الفولتية المنطقية إلى الاستنتاجات ، وننتهي بـ "الاختراع" لنفس المصطلح ، بعد كل خطوة نتحقق مما إذا كانت الشفرة تعمل كما هو متوقع. بشكل منفصل ، لاحظت أنني لا أخترع بديلاً لهذه المكتبة ، لكنني دائمًا ما أعيد إنتاج التعليمات البرمجية المصدر الخاصة بها ، مع الحفاظ على الهيكل الأصلي والأسماء قدر الإمكان ، مع توضيح سبب عمل هذا القسم أو هذا القسم. ومع ذلك ، فإن أسلوب كتابة الكود المألوف بالنسبة لي يختلف عن أسلوب مؤلفي vusb. على الفور ، أعترف بصدق أنه بالإضافة إلى الاهتمام بالإيثار (لإخبار الآخرين عن موضوع صعب) ، لدي أيضًا مصلحة أنانية - دراسة الموضوع بمفردي والتقاط أكبر قدر من النقاط الدقيقة بنفسي. ويترتب على ذلك أيضًا فقدان بعض النقاط المهمة ، أو عدم الكشف عن بعض الموضوعات بشكل كامل.


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


وألاحظ أيضًا أن USB منخفض السرعة فقط يعتبر ، حتى من دون ذكر ، ما يميز أكثر الأصناف عالية السرعة.


الخطوة 0. الحديد وغيرها من التحضير


كاختبار ، دعنا نأخذ لوحة تصحيح محلية الصنع تعتمد على ATmega8 مع 12 ميغاهيرتز كوارتز. لن أعطي هذا المخطط ، فهو قياسي تمامًا (راجع موقع vusb الرسمي) ، الشيء الوحيد الذي يستحق الذكر هو الاستنتاجات المستخدمة. في حالتي ، يتوافق الإخراج D + مع PD2 ، والإخراج D- PD3 ، ويعلق المعلق على PD4. من حيث المبدأ ، يمكن توصيل المقاوم للسحب إلى السلطة ، ولكن التحكم اليدوي يبدو أكثر اتساقا قليلا مع المعيار.


يتم توفير طاقة 5 فولت من موصل USB ، ولكن لا يُتوقع أكثر من 3.6 فولت على خطوط الإشارة (لماذا كان هذا لغزا بالنسبة لي). لذلك تحتاج إما إلى خفض قوة وحدة التحكم ، أو وضع ثنائيات زينر على خطوط الإشارة. اخترت الخيار الثاني ، ولكن إلى حد كبير لا يهم.


نظرًا لأننا "نخترع" التنفيذ ، سيكون من الجيد أن نرى ما يحدث في أدمغة وحدة التحكم ، أي أنه على الأقل هناك حاجة إلى نوع من معلومات تصحيح الأخطاء. في حالتي ، فهناك مؤشران LED على PD6 ، PD7 ، والأهم من ذلك ، UART on PD0 ، PD1 ، تم تكوينهما على 115200 ، بحيث يمكنك الاستماع إلى ثرثرة وحدة التحكم من خلال شاشة عادية أو برنامج آخر للعمل مع منفذ COM:


$ screen /dev/ttyUSB0 115200 

وأيضًا ، سوف يتحول استخدام أداة wharhar مع الوحدة النمطية المناسبة إلى أداة مفيدة لتصحيح أخطاء USB (لا تبدأ دائمًا من المربع ، ولكن حل مثل هذه المشكلات يقع بنجاح كبير على الإنترنت وليس مهمة هذه المقالة).


هنا سيكون من الممكن إنفاق كيلوبايت آخر من النص على وصف المبرمج ، والتركيبات ، وأشياء أخرى ، ولكن هذا بالكاد منطقي. وبالمثل ، لن أركز على الإعدادات الطرفية غير المرتبطة بـ USB. إذا لم يستطع أحد معرفة ذلك ، فهل من السابق لأوانه الدخول في أحاسيس برنامج USB؟


شفرة المصدر لجميع الخطوات متاحة على جيثب.


الخطوة 1. قبول ما لا يقل عن شيء


وفقًا للوثائق ، يدعم USB عدة سرعات ثابتة ، منها AVR ستسحب فقط أدنى: 1.5 ميغابت في الثانية. يتم تحديده بواسطة المقاوم سحب والاتصالات اللاحقة. بالنسبة للتردد الذي نختاره ، يجب على المقاوم توصيل D- مع مصدر طاقة 3.3 فولت وأن يكون له قيمة اسمية تبلغ 1.5 كيلو أوم ، لكن في الممارسة العملية يمكن توصيله بـ +5 فولت ويمكن أن تتغير القيمة الاسمية قليلاً. مع تردد تحكم من 12 ميغاهيرتز ، فقط 8 دورات على مدار الساعة لكل بت. من الواضح أن هذه الدقة والسرعة لا يمكن تحقيقهما إلا في أداة التجميع ، لذلك سنفتح ملف drvasm.S. هذا يعني أيضًا الحاجة إلى استخدام مقاطعة للقبض على بداية بايت. أنا سعيد لأن البايت الأول الذي يتم إرساله عبر USB هو نفسه دائمًا ، SYNC ، لذلك إذا وصلت إلى البداية ، فلا بأس بذلك. نتيجة لذلك ، من بداية البايت إلى نهايته ، يتم إجراء 64 دورة فقط من وحدة التحكم (في الواقع ، يكون الهامش أصغر) ، لذلك يجب عدم استخدام المقاطعات الأخرى غير التابعة لـ USB.


مباشرة وضع التكوين في ملف منفصل usbconfig.h. هناك سيتم تعيين المسامير المسؤولة عن USB ، وكذلك البتات والثوابت والسجلات المستخدمة.


إدراج النظري
يتم النقل عبر USB في حزم من عدة بايت في كل منها. البايت الأول هو دائمًا بايت تزامن SYNC ، يساوي 0b10000000 ، والثاني هو معرف البايت لحزمة PID. ينتقل نقل كل بايت من البتة الأقل دلالة إلى البتة الأكثر أهمية (هذا ليس صحيحًا تمامًا ، ولكن في vusb يتم تجاهل هذه الدقة في مكان آخر) باستخدام تشفير NRZI. تتكون هذه الطريقة من حقيقة أن الصفر المنطقي يتم إرساله عن طريق تغيير المستوى المنطقي إلى الاتجاه المعاكس ، ويتم إرسال الوحدة المنطقية عن طريق عدم التغيير. بالإضافة إلى ذلك ، يتم توفير الحماية من desync (التي لن نستخدمها ، ولكن يجب أخذها في الاعتبار) لمصدر الإشارة والمستقبل: إذا كانت هناك ست وحدات متتالية في التسلسل المرسَل ، أي أن حالة المطاريف لا تتغير لمدة ست فترات متتالية ، تتم إضافة انقلاب قسري إلى الإرسال ، كما لو ينتقل الصفر. وبالتالي ، يمكن أن يكون حجم البايت 8 أو 9 بت.
تجدر الإشارة أيضًا إلى أن خطوط البيانات في USB تفاضلية ، أي عندما تكون + D مرتفعة ، تكون D- منخفضة (تسمى هذه الحالة K) والعكس (J-state). يتم ذلك لتحسين مناعة الضوضاء عند التردد العالي. صحيح ، هناك استثناء: يتم إرسال الإشارة في نهاية الحزمة (يطلق عليها SE0) عن طريق سحب كلا خطوط الإشارة إلى الأرض (D + = D- = 0). هناك نوعان من الإشارات المرسلة عن طريق الضغط المنخفض على الخط D + والجهد العالي على الخط D + لأوقات مختلفة. إذا كان الوقت صغيراً (طول بايت واحد أو أطول قليلاً) فهذا هو الخمول ، وقفة مؤقتة بين الحزم ، وإذا كانت كبيرة ، فإعادة تعيين إشارة.

لذلك ، يكون ناقل الحركة على زوج تفاضلي ، دون حساب الحالة الغريبة لـ SE0 ، لكننا لن نفكر فيها بعد. لذلك لتحديد حالة ناقل USB ، نحتاج إلى سطر واحد فقط ، D + أو D-. إلى حد كبير ، لا يوجد فرق بين ما يجب اختياره ، ولكن من أجل التحديد ، دع D- يكون.


يمكن تحديد بداية الحزمة من خلال تلقي بايت SYNC بعد خمول طويل. تتوافق حالة الخمول مع السجل 1 على الخط D (وهو أيضًا الحالة J) ، وبايتة SYNC هي 0b100000 ، لكنها تنتقل من البتة الأقل أهمية إلى الأكثر أهمية ، علاوة على ذلك ، يتم ترميزها في NRZI ، أي يعني كل صفر انقلاب الإشارة ، ووسيلة واحدة الحفاظ على نفس المستوى. وبالتالي فإن تسلسل الحالات D- سيكون على النحو التالي:


بايتخاملSYNCPID
USB1..100000001؟؟؟؟؟؟؟؟
D-1..101010100؟؟؟؟؟؟؟؟

من السهل اكتشاف بداية الحزمة على حافة السقوط ، وسنقوم بتكوين مقاطعة عليها. ولكن ماذا لو كانت وحدة التحكم مشغولة أثناء بدء الاستقبال ولم تتمكن من الدخول إلى المقاطعة على الفور؟ لتجنب فقدان عدد مرات التتبع في مثل هذا الموقف ، نستخدم بايت SYNC للغرض المقصود منه. وهي تتألف بالكامل من جبهات على حدود البتات ، بحيث يمكننا أن ننتظر أحدها ، ثم نصفًا آخر ، وننتقل مباشرة إلى منتصف التالي. ومع ذلك ، فإن انتظار "بعض" الجبهة ليس فكرة جيدة ، لأننا لا نحتاج فقط إلى الدخول في منتصف الصورة ، ولكن أيضًا لمعرفة الشيء الذي حصلنا عليه في النتيجة. وللحصول على SYNC هذا مناسب أيضًا: يحتوي على قطعتين صفريتين على التوالي في النهاية (هما K-state). هنا سنقبض عليهم. لذلك ، في ملف drvasm.S ، تظهر قطعة من التعليمات البرمجية من إدخال المقاطعة إلى foundK. علاوة على ذلك ، نظرًا لوقت التحقق من حالة المنفذ ، والانتقال غير المشروط وما إلى ذلك ، فإننا لن نصل إلى العلامة في بداية البت ، ولكن فقط في المنتصف. ولكن من غير المجدي التحقق من الشيء نفسه ، لأننا نعرف بالفعل معناها. لذلك ، ننتظر 8 دورات على مدار الساعة (حتى الآن nop'ami فارغة حتى الآن) ونفحص البت التالي. إذا كان الرقم صفرًا أيضًا ، فقد وجدنا نهاية SYNC ويمكننا الانتقال إلى استقبال أجزاء كبيرة.


في الواقع ، تم تصميم جميع التعليمات البرمجية الإضافية لقراءة وحدتي بايت أخريين مع الإخراج التالي إلى UART. حسنًا ، في انتظار حالة SE0 حتى لا تدخل بطريق الخطأ في الحزمة التالية.


يمكنك الآن ترجمة الشفرة الناتجة ومعرفة ما يقبله جهازنا. شخصيا ، لدي التسلسل التالي:


 4E 55 00 00 4E 55 00 00 4E 55 00 00 4E 55 00 00 4E 55 00 00 

تذكر أننا نخرج بيانات أولية ، باستثناء الأصفار الإضافية وفك تشفير NRZI. دعنا نحاول فك شفرة يدويًا ، بدءًا بالبت القليل:


4E
NRZI010011100 (بت السابقة)
بايت00101101
2D

55
NRZI010101010 (بت السابقة)
بايت00000000
00

لا معنى لفك تشفير الأصفار ، حيث لا يمكن تضمين 16 قيمة متطابقة في صف واحد في الرزمة.


وبالتالي ، تمكنا من كتابة البرامج الثابتة التي تقبل أول وحدتي بايت من الحزمة ، على الرغم من ذلك حتى الآن دون فك التشفير.


الخطوة 2. الإصدار التجريبي من NRZI


حتى لا تتم إعادة الترميز يدويًا ، يمكنك تكليف هذا بوحدة التحكم نفسها: تعمل عملية XOR على ما تحتاجه بالضبط ، على الرغم من أن النتيجة معكوسة ، لذلك أضف انقلابًا آخر بعد ذلك:


 mov temp, shift lsl shift eor temp, shift com temp rcall uart_hex 

النتيجة متوقعة تمامًا:


 2D 00 FF FF 2D 00 FF FF 2D 00 FF FF 2D 00 FF FF 2D 00 FF FF 

الخطوة 3. تخلص من بايت تلقي دورة


لنأخذ خطوة صغيرة أخرى ونوسع دائرة استلام البايتة الأولى في رمز خطي. وبالتالي اتضح الكثير من الغثيان ، وهناك حاجة فقط لانتظار بداية البت المقبل. بدلاً من بعضها ، يمكنك استخدام وحدة فك ترميز NRZI ، وسوف يكون الآخرون في متناول اليد لاحقًا.


النتيجة من الخيار السابق لا تختلف.


الخطوة 4. قراءة إلى المخزن المؤقت


تعتبر القراءة في سجلات منفصلة ، بطبيعة الحال ، سريعة وجميلة ، ولكن عندما يكون هناك الكثير من البيانات ، فمن الأفضل استخدام إدخال مخزن مؤقت موجود في مكان ما في ذاكرة الوصول العشوائي. للقيام بذلك ، سنعلن مجموعة من الحجم الكافي في الرئيسي ، وفي المقاطعة سنكتب هناك.
إدراج النظري


هيكل الحزمة في USB موحد ويتكون من الأجزاء التالية: بايت SYNC ، بايت PID + CHECK (حقلان من 4 بت لكل منهما) ، حقل بيانات (أحيانًا 11 بت ، لكن في كثير من الأحيان رقم تعسفي للبايت 8 بت) ومجموع تحقق CRC إما 5 ( لحقل البيانات 11 بت) ، أو 16 (للباقي) بت. أخيرًا ، نهاية مؤشر الرزم (EOP) عبارة عن بتتين مؤقتتين ، لكن هذا لم يعد بيانات.


قبل العمل مع المصفوفة ، لا تزال بحاجة إلى تكوين السجلات ، و nop المجاني قبل البتة الأولى لا يكفي لذلك. لذلك ، سيتعين عليك وضع قراءة البتات الأولين في القسم الخطي من الشفرة ، بين الأمرين اللذين سنقوم بإدخال رمز التهيئة لهما ، ثم الانتقال إلى منتصف دورة القراءة ، إلى تسمية rxbit2. الحديث عن حجم العازلة. وفقًا للوثائق ، فمن المستحيل في حزمة واحدة نقل أكثر من 8 بايتات من البيانات. نضيف خدمة بايت PID و CRC16 ، نحصل على حجم المخزن المؤقت 11 بايت. لن تتم كتابة بايت SYNC وحالة EOP. لن نتمكن من التحكم في الفاصل الزمني للطلبات المقدمة من المضيف ، لكننا لا نريد أن نفقدها أيضًا ، لذلك سنأخذ هامشًا مزدوجًا للقراءة. في الوقت الحالي ، لن نستخدم المخزن المؤقت بالكامل ، ولكن حتى لا نعود في المستقبل ، من الأفضل تخصيص الحجم المطلوب على الفور.


الخطوة 5. العمل مع المخزن المؤقت إنسانيا


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


 >03 2D 00 10 >01 FF >03 2D 00 10 >01 FF >03 2D 00 10 >01 FF >03 2D 00 10 >01 FF >03 2D 00 10 >01 FF 

الخطوة 6. إضافة مضافة صفر المضافة


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


 unstuff0: ;1 (  breq) andi x3, ~(1<<0) ;1 [15]  0-  .     mov x1, x2 ;1 [16]      () in x2, USBIN ;1 [17] <-- 1-   .     ori shift, (1<<0) ;1 [18]  0-   .1      rjmp didUnstuff0 ;2 [20] ;<---//---> rxLoop: eor shift, x3 ;1 [0] in x1, USBIN ;1 [1] st y+, shift ;2 [3] ldi x3, 0xFF ;1 [4] nop ;1 [5] eor x2, x1 ;1 [6] bst x2, USBMINUS ;1 [7]     0-   shift bld shift, 0 ;1 [8] in x2, USBIN ;1 [9] <--  1- (, ) andi x2, USBMASK ;1 [10] breq se0 ;1 [11] andi shift, 0xF9 ;1 [12] didUnstuff0: breq unstuff0 ;1 [13] eor x1, x2 ;1 [14]; bst x1, USBMINUS ;1 [15]     1-   shift bld shift, 1 ;1 [16] rxbit2: in x1, USBIN ;1 [17] <--  2-  (, ) andi shift, 0xF3 ;1 [18] breq unstuff1 ;1 [19] didUnstuff1: 

لتوفير الراحة في التنقل ، سيتم حساب عناوين الأوامر الموضحة بواسطة التصنيفات الموجودة على اليمين. يرجى ملاحظة أنه تم تقديمها لحساب دورات الساعة من وحدة تحكم ، لذلك ليست في النظام. تتم قراءة البايت التالي على ملصق rxLoop ، يتم عكس البايتة السابقة وكتابتها إلى المخزن المؤقت [0 ، 3]. بعد ذلك ، على الملصق [1] ، تتم قراءة حالة الخط D- وفقًا لـ XOR بالحالة المقبولة السابقة ، نقوم بفك تشفير NRZI (أذكر أن XOR العادي يضيف انعكاسه ، لإصلاح الذي ندخله في سجل القناع x3 ، الذي تمت تهيئته بوحدات 0xFF) والكتابة إلى 0- إيث قليلا من سجل التحول [7،8]. ثم تبدأ المتعة - نتحقق مما إذا كانت البتة المستلمة هي السادسة دون تغيير. البتة المستمرة المستلمة مع D- تقابل الكتابة صفر (لا واحد! سنتغير إلى واحدة في النهاية ، XOR) في السجل. لذلك ، تحتاج إلى التحقق مما إذا كانت البتات 0 و 7 و 6 و 5 و 4 و 3 عبارة عن أصفار. لا يهم البتات اثنين المتبقية ، فإنها بقيت من البايت السابق وتم فحصها في وقت سابق. للتخلص منهم ، قمنا بقطع التسجيل عن طريق القناع [12] ، حيث تم تعيين كل البتات التي تهمنا على 1: 0b11111001 = 0xF9. إذا تحولت بعد تطبيق القناع إلى كل الأصفار ، فإن موقف إضافة بعض الشيء يكون ثابتًا وهناك انتقال إلى التسمية unstuff0. تتم قراءة جزء واحد آخر [17] بدلاً من ما تمت قراءته مسبقًا ، في الفترة الفاصلة بين العمليات الأخرى ، من فائض [9]. نقوم أيضًا بتبادل سجلات القيم الحالية والسابقة x1 ، x2. والحقيقة هي أنه في كل بت ، يتم قراءة القيمة في سجل واحد ، ثم XOR مع سجل آخر ، وبعد ذلك يتم تبادل السجلات. وفقًا لذلك ، عند قراءة السجل التدريجي ، يجب أيضًا إجراء هذه العملية. لكن الشيء الأكثر إثارة للاهتمام هو أننا في سجل بيانات التحول نكتب ليس الصفر ، الذي تلقيناه بأمانة ، ولكن الوحدة التي حاول المضيف نقلها [18]. ويرجع ذلك إلى حقيقة أنه عند استلام البتات التالية ، يجب أيضًا أخذ قيمة الصفر في الاعتبار ، وإذا سجلنا الصفر ، فلن يتمكن فحص القناع من اكتشاف أن البتة الزائدة قد تم أخذها بالفعل في الاعتبار. وبالتالي ، في سجل الإزاحة ، يتم عكس كل البتات (نسبة إلى المرسلة من قبل المضيف) ، والصفر ليس كذلك. لمنع حدوث مثل هذه الفوضى في المخزن المؤقت ، سنقوم بإجراء انعكاس عكسي وفقًا لـ XOR ليس مع 0xFF [0] ، ولكن مع 0xFE ، أي السجل الذي ستتم إعادة تعيين البت المطابق فيه إلى 0 وبالتالي ، لن يؤدي ذلك إلى انعكاس. للقيام بذلك ، في العينة [15] وإعادة تعيين بت صفر.


يحدث موقف مماثل مع البتات 1-5. لنفترض أن البتة الأولى تقابل المدقق 1 و 0 و 7 و 6 و 5 و 4 ، بينما يتم تجاهل البتات 2 و 3. هذا يتوافق مع القناع 0xF3.
لكن معالجة 6 و 7 بت مختلفة:


 didUnstuff5: andi shift, 0x3F ;1 [45]   5-0 breq unstuff5 ;1 [46] ;<---//---> bld shift, 6 ;1 [52] didUnstuff6: cpi shift, 0x02 ;1 [53]   6-1 brlo unstuff6 ;1 [54] ;<---//---> bld shift, 7 ;1 [60] didUnstuff7: cpi shift, 0x04 ;1 [61]   7-2 brsh rxLoop ;3 [63] unstuff7: 

قناع الجزء السادس هو الرقم 0b01111110 (0x7E) ، لكن لا يمكنك تثبيته على سجل الإزاحة ، حيث إنه سيعيد تعيين البت 0 ، الذي يجب كتابته على المصفوفة. بالإضافة إلى ذلك ، في العد التنازلي [45] ، تم بالفعل تثبيت القناع ، وإعادة ضبط 7 بت. لذلك ، من الضروري معالجة البتة الإضافية إذا كانت البتات 1-6 مساوية للصفر ، ولا يهم البتة 0. أي أن قيمة السجل يجب أن تكون 0 أو 1 ، والتي يتم التحقق منها تمامًا عن طريق مقارنة "أقل من 2" [53 ، 54].


تم استخدام نفس المبدأ للبت السابع: بدلاً من تطبيق قناع 0xFC ، يتم إجراء فحص لـ "أقل من 4" [61 ، 63].


الخطوة 7. فرز الحزم


نظرًا لأنه يمكننا تلقي حزمة ذات البايت الأول (PID) تساوي 0x2D (SETUP) ، سنحاول فرز الحزمة المستلمة. بالمناسبة ، لماذا اتصلت الحزمة 0x2D SETUP عندما يبدو أنه ACK؟ والحقيقة هي أن نقل USB من بت الأقل أهمية إلى الأكثر أهمية يتم داخل كل حقل ، وليس البايت ، بينما نحن نقبل البايت. يشتمل الحقل الأول الهام ، PID ، على 4 بتات فقط ، متبوعًا بـ 4 بتات CHECK أخرى ، مما يمثل انعكاسًا حيويًا لحقل PID. وبالتالي ، لن تكون البايتة الأولى المستلمة هي PID + CHECK ، ولكن CHECK + PID. ومع ذلك ، لا يوجد فرق كبير ، حيث أن جميع القيم معروفة مسبقًا ، ومن السهل إعادة ترتيب القضم في الأماكن. على الفور ، سنكتب الرموز الرئيسية التي قد تكون مفيدة لنا في ملف usbconfig.h.


حتى نبدأ في إضافة رمز معالجة PID ، يجب أن نلاحظ أنه يجب أن يكون سريعًا (أي ، في المجمع) ، لكن المحاذاة حسب الساعات غير مطلوبة ، لأننا قبلنا بالفعل الحزمة. وبالتالي ، سيتم نقل هذا القسم لاحقًا إلى ملف asmcommon.inc ، والذي سيحتوي على رمز المجمع غير المرتبط بالتردد. في غضون ذلك ، سلط الضوء على التعليق.
الآن دعنا ننتقل إلى فرز الحزم المستلمة.


إدراج النظري
يتم دمج حزم البيانات على الناقل USB في المعاملات. تبدأ كل معاملة بالإرسال بواسطة المضيف لحزمة علامة خاصة تحمل معلومات حول ما يريد المضيف القيام به مع الجهاز: تكوين (SETUP) أو نقل البيانات (OUT) أو استلامها (IN). بعد إرسال رزمة العلامة ، يتبع توقف مؤقت من جزئين. يتبع ذلك حزمة بيانات (DATA0 أو DATA1) ، يمكن إرسالها بواسطة المضيف والجهاز ، وفقًا لحزمة العلامة. بعد ذلك ، هناك وقفة أخرى من طول البتات اثنين والإجابة هي HANDSHAKE ، حزمة تأكيد (ACK ، NAK ، STALL ، سننظر فيها مرة أخرى).
SETUPDATA0مصافحة
المضيف> الجهازوقفةالمضيف> الجهازوقفةالجهاز> المضيف

OUTDATA0 / DATA1مصافحة
المضيف> الجهازوقفةالمضيف> الجهازوقفةالجهاز> المضيف

INDATA0 / DATA1مصافحة
المضيف> الجهازوقفةالجهاز> المضيفوقفةالمضيف> الجهاز


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

لذلك ، نحن نعرف جميع أنواع الحزم اللازمة للتبادل. نضيف التحقق من بايت PID المستلم للامتثال لكل منها. في الوقت الحالي ، لم يكن الجهاز قادرًا حتى الآن على كتابة حتى الحزم البدائية مثل ACK إلى الحافلة ، مما يعني أنه غير قادر على إخبار المضيف بما هو عليه. لذلك ، لا يمكن توقع أوامر مثل IN. لذلك ، سوف نتحقق فقط من استقبال أوامر SETUP و OUT ، والتي سنشير إلى إدراج مصابيح LED المقابلة لها في الفروع المقابلة.


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


نومض الجهاز بما حدث بعد إجراء هذه التغييرات ونلاحظ التسلسل التالي للبايتات المستلمة:


 2D|80|06|00|01|00|00|40|00 C3|80|06|00|01|00|00|40|00 2D|80|06|00|01|00|00|40|00 C3|80|06|00|01|00|00|40|00 

وإلى جانب ذلك - كلا حرق المصابيح. لذلك ، وقعنا SETUP و OUT.


الخطوة 8. قراءة العنوان على المغلف


إدراج النظري
لا تخدم حزم العلامات (SETUP ، IN ، OUT) فقط في إظهار الجهاز ما يريدون منه ، ولكن أيضًا لمعالجة جهاز معين على الناقل وإلى نقطة نهاية محددة بداخله. هناك حاجة إلى نقاط النهاية لتسليط الضوء وظيفيًا على وظيفة فرعية معينة للجهاز. يمكن أن تختلف في وتيرة الاقتراع ، وسعر الصرف ، وغيرها من المعالم. لنقل أنه إذا بدا أن الجهاز عبارة عن محول USB-COM ، فإن مهمته الرئيسية هي تلقي البيانات من الحافلة ونقلها إلى المنفذ (نقطة النهاية الأولى) وتلقي البيانات من المنفذ وإرسالها إلى الناقل (الثانية). من حيث المعنى ، هذه النقاط مخصصة لتدفق كبير من البيانات غير المهيكلة. ولكن إلى جانب ذلك ، من وقت لآخر ، يجب أن يتبادل الجهاز مع المضيف حالة خطوط التحكم (جميع أنواع RTS و DTR وغيرها) وإعدادات التبادل (السرعة ، التماثل). وهنا ، لا يتوقع وجود كميات كبيرة من البيانات. بالإضافة إلى ذلك ، تكون مريحة عندما لا يتم خلط معلومات الخدمة مع البيانات. لذلك اتضح أنه مناسب لاستخدام 3 نقاط نهاية على الأقل لمحول USB-COM. في الممارسة العملية ، بالطبع ، يحدث بطرق مختلفة ...
سؤال مثير للاهتمام بنفس القدر هو سبب إرسال الجهاز لعنوانه ، لأنه بصرف النظر عن ذلك ، لا يزال يتعذر عليك إدخال أي شيء في هذا المنفذ المحدد. يتم ذلك لتبسيط تطوير لوحات وصل USB. يمكن أن تكون "غبية" وببساطة إشارات من المضيف إلى جميع الأجهزة دون الحاجة إلى القلق بشأن الفرز. وسيقوم الجهاز نفسه باكتشافها أو معالجة الحزمة أو تجاهلها.
لذلك ، يتم تضمين كل من عنوان الجهاز وعنوان نقطة النهاية في حزم العلامة. ويرد هيكل هذه الحزم أدناه:
المجال
المجالSYNCADDRنقطة النهايةCRCEOP
بت USB0-7012345601230123401
بت تلقى0123456701234567


, - ( - PID = SETUP OUT) (IN) , .

, (-) (Handshake) :


  • : , , NAK
  • -: SETUP OUT, , IN — ,
  • . , , ,

« — » . PID', , . «PID» . usbCurrentTok. PID' (DATA0, DATA1) , . , ? : , ( 0 usbCurrentTok ), , . ( SE0) , - , D+, D- . , SYNC, . , , . «» , . .


, . x3, (, , , ).


, USB , , . , , , CRC ( ). , [21]. 0- . , [26]. , CRC, .


9.


, , « », ACK. NAK', ( cnt — ). USB , , SYNC PID. Y, cnt ( ). , — ACK. x3 — 1 , . x3 ( r20) 20.


( SETUP, ), ACK' , , , . , .


, D+, D- ( ), — . XOR , , , , - .


, , , , . , , , . . vusb : txBitloop 2 ([00], [08]). 3 , 6 . , . 1 3 : 171. ( 171, 11 , ), — , . cnt=4:


4 — 171 = -167 = ( ) 89 (+ )
89 — 171 = -82 = ( ) 174 (+ )
174 — 171 = 3. ,
, .


, 3 , 1. 6 , , x4. D+, D- , . .
:


 2D|80|06|00|01|00|00|40|00 69|00|10|00|01|00|00|40|00 

C3 . , , UART . , , IN , . , .


10. NAK


NAK , . , . , - .


, . , , - , . usbRxBuf, . , — , USB_BUFSIZE. usbInputBufOffset, . .


NAK handleData , [22]. (usbRxLen), - . ( — ), usbRxLen, , — usbRxToken, SETUP OUT - . : , , ACK .


. , , - , -, . ? , , , , - .


,


 2D|80|06|00|01|00|00|40|00 

, NAK`, , .


11.


, , . — . , , , , , . . . , USB, usbPoll. — , . — . SETUP , PID CRC, SETUP 5- , 16-. 3 «» . «» PID usbRxToken, CRC , , . usbProcessRx, , .


, , — , SE0. , USB .


. SETUP, . . SETUP usbRequest_t 8 . : ( USB-) , - . , . .
, , , .


12. SETUP'


, , . . usbDriverSetup, . , . , ( , , ) . , : ACK NAK, .


13.


, SETUP + DATAx, DATAx 8 . IN DATAx, . , . , ACK NAK. , . — usbTxBuf, , usbTxLen . low-speed USB 8 ( PID, CRC), usbTxLen 11. PID, , . , 16, , 0x0F, . PID , . IN, , (handshake , ).


:
SETUP + DATAx, ACK NAK . , , usbPoll, , ( PID=DATA1 ( DATA0 DATA1 , , DATA1). CRC . , , - . — 4 . , 3 , 4. , SYNC . « IN NAK?» NAK. , , DATA1 .


, — USBRQ_SET_ADDRESS ( , ). . (drvsdm.S, make SE0). , , , DATA1 , , . , , , , , . , , .


14.


, . , USBRQ_GET_DESCRIPTOR USBRQ_SET_ADDRESS, , . usbDriverDescriptor, . , USBRQ_GET_DESCRIPTOR. , , :


USBDESCR_DEVICE — : USB (1.1 ), , , . .
USBDESCR_CONFIG — , , . .
USBDESCR_STRING — , .
, , USBDESCR_DEVICE, , .


15.


. -, . , - - , , HID, , . Vendor ID Product ID, USB, . , vusb .


, , - . , , , (, ) usbMsgPtr, — len, usbMsgLen. ( ) 18 , 8. , , 3 . - , STALL.


usbDeviceRead. , memcpy_P, , , .


, , , . , , .


, , .



PID' DATA0 DATA1 . PID' , , - .

, DATA0 / DATA1 ( ), , , 3 , . XOR PID', . , , XOR' . PID DATA1, XOR PID , XOR DATA0 .


, , USBDESCR_CONFIG.


16. - !


USBDESCR_CONFIG USBDESCR_DEVICE. ( , ) . , - USB-, , D+, D-.


, : , , . , ( , ). , UTF-16, . USB UTF-8 .


vusb , lsusb . VID, PID , . , VID, PID, — .


, , ( ). SETUP: , , . 0, , — . , , , .
.


17. (HID)



HID — human interface device, , , . HID , . , , , , , . «» . HID ( low-speed 800 ), .

HID , USBDESCR_HID_REPORT. vusb, . , usbDriverSetup ( ) usbFunctionSetup ( ). , SETUP, OUT. , , , usbFunctionWrite.


, usbDeviceRead usbFunctionRead, . , , usbFunctionSetup ( , ) USB_FLG_USE_USER_RW, usbDriverSetup .


— — usbFunctionWrite usbFunctionRead. . — , .


usbDriverSetup.


18.


, , . HID, , , ( udev - ). , , . , , , .
UPD: ramzes2 , HIDAPI


.


19. vusb


vusb , .


drvasm.S - usbdrvasm.S asmcommon.inc, -, , usbdrvasm12.inc — usbdrvasm20.inc.


main.c main.c ( ) usbdrv.c ( vusb)
usbconfig.h ( ), , , usbconfig.h.


استنتاج


vusb, , , . , , . . , , , USB-HID. , , , vusb, , , , .



https://www.obdev.at/products/vusb/index.html ( vusb)
http://microsin.net/programming/arm-working-with-usb/usb-in-a-nutshell-part1.html
.. USB:
https://radiohlam.ru/tag/usb/
http://we.easyelectronics.ru/electro-and-pc/usb-dlya-avr-chast-1-vvodnaya.html
http://usb.fober.net/cat/teoriya/


PS - (, ) ,

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


All Articles