STM32، واجهة I2C التسلسلية. STM32، تابع وصف الواجهة التسلسلية I2C واجهة Stm32 i2c

بعض الناس يحبون الفطائر والبعض الآخر لا.

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

العمل مع الوحدة، بشكل عام، هو نفسه كما هو الحال في وحدات التحكم الأخرى: أنت تعطيها الأوامر، وتنفذها وتبلغ عن النتيجة:
أنا> لقد ذهبنا إلى البداية.
S> حسنًا، لقد أرسلته.
أنا> رائع، أرسل العنوان الآن. مثل هذا: 0xXX.
س> حسنًا، لقد أرسلته. قيل لي أن ACK. هيا لنذهب.
أنا> ما زلت على قيد الحياة، جيد. هذا هو رقم التسجيل: 0xYY، - هيا بنا.
س> أرسلت، تلقى ACK.
أنا> الآن أرسل له البيانات، وإليك البايت: 0xZZ.
س> أرسل، يوافق على المزيد: ACK.
أنا> اللعنة عليه، ليس بعد. لقد توقفوا.
س> حسنًا.

وكل شيء تقريبًا بهذه الروح.

في وحدة التحكم هذهتنتشر دبابيس i2c عبر المنافذ على النحو التالي:
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
بشكل عام، من المناسب إلقاء نظرة على موصلات الأجهزة الطرفية في الصفحة 59.

من المثير للدهشة أنك تحتاج إلى جميع سجلاتها للعمل مع i2c، ولحسن الحظ يوجد عدد قليل منها:
I2C_CR1- أوامر إلى الوحدة لإرسال الأوامر/الحالات واختيار أوضاع التشغيل؛
I2C_CR2- إعداد DMA والإشارة إلى تردد تشغيل الوحدة (2-42 ميجاهرتز)؛
I2C_OAR1- ضبط عنوان الجهاز (للتابع)، وحجم العنوان (7 أو 10 بت)؛
I2C_OAR2- تحديد عنوان الجهاز (إذا كان هناك عنوانين)؛
I2C_DR- سجل البيانات.
I2C_SR1- سجل حالة الوحدة؛
I2C_SR2- سجل الحالة (التابع، يجب قراءته إذا تم تعيين إشارات ADDR أو STOPF في SR1)؛
I2C_CCR- ضبط سرعة الواجهة؛
I2C_TRISE- تحديد توقيتات الحواف.

ومع ذلك، فإن نصفهم من نوع "اكتب وانسى الأمر".

تحتوي لوحة STM32F4-Discovery بالفعل على جهاز I2C يمكنك من خلاله التدرب على: CS43L22، DAC الصوتي. وهو متصل بالدبابيس PB6/PB9. الشيء الرئيسي هو عدم نسيان تطبيق مستوى عالٍ لتثبيت PD4 (~RESET موجود هناك)، وإلا فلن تستجيب DAC.

إجراء الإعداد تقريبًا كما يلي:
1 . السماح بتسجيل الوقت للمنافذ والوحدة نفسها.
نحتاج إلى الأطراف PB6/PB9، لذا نحتاج إلى تعيين البت 1 (GPIOBEN) في سجل RCC_AHB1ENR لتمكين المنفذ.
وقم بتعيين البت 21 (I2C1EN) في سجل RCC_APB1ENR لتمكين وحدة I2C. بالنسبة للوحدة الثانية والثالثة، أرقام البت هي 22 و23 على التوالي.
2 . بعد ذلك، يتم تكوين الدبابيس: مخرج الصرف المفتوح (GPIO->OTYPER)، ووضع الوظيفة البديلة (GPIO->MODER)، ورقم الوظيفة البديلة (GPIO->AFR).
إذا رغبت في ذلك، يمكنك تكوين السحب (GPIO->PUPDR)، إذا لم يكن موجودًا على اللوحة (ويلزم السحب إلى مصدر الطاقة لكلا الخطين بأي شكل من الأشكال). رقم I2C هو نفسه دائمًا: 4. من الجيد أن يكون هناك رقم منفصل لكل نوع من الأجهزة الطرفية.
3 . تتم الإشارة إلى تردد الساعة الحالي للجهاز الطرفي Fpclk1 (معبرًا عنه بالميغاهرتز) في سجل CR2. كما أفهمها، هذا ضروري لحساب توقيتات البروتوكول المختلفة.
بالمناسبة، يجب أن يكون هناك اثنان على الأقل للوضع العادي وأربعة على الأقل للوضع السريع. وإذا كنت بحاجة إلى سرعة كاملة تبلغ 400 كيلو هرتز، فيجب أيضًا تقسيمها على 10 (10، 20، 30، 40 ميجا هرتز).
الحد الأقصى لتردد الساعة المسموح به: 42 ميجا هرتز.
4 . يتم تكوين سرعة الواجهة في سجل CCR، ويتم تحديد الوضع (عادي/سريع).
والمعنى هو: Tsck = CCR * 2 * Tpckl1، أي. تتناسب فترة SCK مع CCR (بالنسبة للوضع السريع، كل شيء أكثر صعوبة بعض الشيء، ولكن تم وصفه في RM).
5 . يتم ضبط الحد الأقصى لوقت الحافة الصاعدة في سجل TRISE. للوضع القياسي هذه المرة هو 1 ميكروثانية. في السجل، تحتاج إلى كتابة عدد دورات الحافلة التي تتناسب مع هذا الوقت، بالإضافة إلى واحدة:
إذا استمرت دورة Tpclk1 125 ns، فاكتب (1000 ns / 125 ns) + 1 = 8 + 1 = 9.
6 . يتم تمكين إنشاء إشارات المقاطعة (الخطأ والحالة والبيانات) بشكل اختياري؛
7 . يتم تشغيل الوحدة: تم ضبط علامة PE في سجل CR1 على 1.

ثم تعمل الوحدة كما ينبغي. كل ما عليك فعله هو تنفيذ الترتيب الصحيح للأوامر والتحقق من النتائج. على سبيل المثال، إدخال السجل:
1 . تحتاج أولاً إلى إرسال START عن طريق تعيين علامة بنفس الاسم في سجل CR1. إذا كان كل شيء على ما يرام، فبعد مرور بعض الوقت سيتم تعيين علامة SB في سجل SR1.
أود أن أشير إلى نقطة واحدة - إذا لم يكن هناك سحب على الخط (وهم عند 0)، فقد لا تنتظر هذه العلامة على الإطلاق.
2 . إذا تم استلام العلم، فإننا نرسل العنوان. بالنسبة لعنوان مكون من سبعة بتات، فإننا ببساطة نكتبه في DR تمامًا كما سيكون على السطر (7 بتات عنوان + بتات اتجاه). لعشرة بت، خوارزمية أكثر تعقيدا.
إذا استجاب الجهاز للعنوان بـ ACK، فستظهر علامة ADDR في سجل SR1. وإذا لم يكن الأمر كذلك، فستظهر علامة AF (فشل الإقرار).
إذا ظهر ADDR، فأنت بحاجة إلى قراءة سجل SR2. ليس عليك أن تنظر إلى أي شيء هناك، فقط القراءة المتسلسلة لـ SR1 وSR2 تعيد تعيين هذه العلامة. وأثناء تعيين العلامة، يظل SCL منخفضًا بواسطة الجهاز الرئيسي، وهو أمر مفيد إذا كنت بحاجة إلى مطالبة الجهاز البعيد بالانتظار قبل إرسال البيانات.
إذا كان كل شيء على ما يرام، فستتحول الوحدة بعد ذلك إلى وضع استقبال البيانات أو إرسالها، اعتمادًا على الجزء الأقل أهمية من العنوان المرسل. للكتابة يجب أن يكون صفرًا، وللقراءة يجب أن يكون واحدًا.
لكننا ننظر إلى السجل، لذلك سنفترض أنه كان هناك صفر.
3 . بعد ذلك نرسل عنوان السجل الذي يهمنا. بنفس الطريقة، كتابتها في DR. بعد الإرسال، يتم تعيين علامتي TXE (مخزن الإرسال المؤقت فارغ) وBTF (اكتمل النقل).
4 . بعد ذلك تأتي البيانات التي يمكن إرسالها بينما يستجيب الجهاز بـ ACK. إذا كانت الاستجابة NACK، فلن يتم تعيين هذه العلامات.
5 . عند الانتهاء من النقل (أو في حالة وجود حالة غير متوقعة)، نرسل STOP: يتم تعيين العلم الذي يحمل نفس الاسم في سجل CR1.

عند القراءة، كل شيء هو نفسه. التغييرات فقط بعد كتابة عنوان التسجيل.
بدلاً من كتابة البيانات، يتم إرسال START مرة أخرى (إعادة التشغيل) ويتم إرسال العنوان مع مجموعة البت الأقل أهمية (علامة القراءة).
ستنتظر الوحدة البيانات من الجهاز. لتشجيعها على إرسال البايتات التالية، تحتاج إلى تعيين علامة ACK في CR1 قبل الاستلام (بحيث يتم إرسال نفس ACK بعد استلام الوحدة).
عندما تتعب من ذلك، قم بإزالة العلامة، وسيرى الجهاز NACK وسيصبح صامتًا. وبعد ذلك نرسل STOP بالطريقة المعتادة ونبتهج بالبيانات المستلمة.

إليك نفس الشيء في نموذج التعليمات البرمجية:
// تهيئة الوحدة void i2c_Init(void) ( uint32_t Clock = 16000000UL؛ // تردد ساعة الوحدة (لم يتم استخدام system_stm32f4xx.c) uint32_t Speed ​​​​= 100000UL؛ // 100 كيلو هرتز // تمكين تسجيل الوقت لمنفذ GPIOB RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // إعداد الدبابيس PB6, PB9 // التصريف المفتوح! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // السحب خارجي، لذلك لا يمكن تهيئته هنا! // إذا لزم الأمر، راجع تسجيل GPIOB->PUPDR // رقم وظيفة GPIOB البديلة ->AFR &= ~(0x0FUL)<< (6 * 4)); // 6 очистим GPIOB->أفر |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->أفر |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->مودر &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->مودر |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // عند هذه النقطة يجب إيقاف تشغيل I2C // إعادة تعيين كل شيء (SWRST == 1، إعادة التعيين) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0، هذا هو الشيء الرئيسي I2C1->CR1 = 0; // نفترض أننا نعمل من RC (16 ميجاهرتز) // لا يوجد مقياس مسبق في نظام الساعة (الكل 1) // بطريقة ودية، يجب علينا حساب كل هذا من // تردد الساعة الفعلي للوحدة I2C1->CR2 = الساعة / 1000000UL؛ // 16 ميجا هرتز // ضبط التردد ( // Tclk = (1 / Fperiph)؛ // Thigh = Tclk * CCR؛ // Tlow = Thigh؛ // Fi2c = 1 / CCR * 2؛ // CCR = Fperiph / ( Fi2c * 2)؛ uint16_t Value = (uint16_t)(الساعة / (السرعة * 2))؛ // الحد الأدنى للقيمة: 4 if(Value< 4) Value = 4; I2C1->CCR = القيمة؛ ) // اضبط وقت الارتفاع المحدد // في الوضع القياسي، هذه المرة هي 1000 ns // نقوم ببساطة بإضافة واحد إلى التردد المعبر عنه بالميجاهرتز (انظر RM ص 604). I2C1->TRISE = (الساعة / 1000000UL) + 1; // تمكين الوحدة I2C1->CR1 |= (I2C_CR1_PE); // الآن يمكنك فعل شيء ما) // إرسال بايت bool i2c_SendByte(uint8_t Address, uint8_t Register, uint8_t Data) ( if(!i2c_SendStart()) return false; // عنوان الشريحة if(!i2c_SendAddress(Address)) return i2c_SendStop ()؛ // سجل العنوان if(!i2c_SendData(Register)) return i2c_SendStop(); // البيانات if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) // تلقي بايت bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) return false; // عنوان الشريحة if(!i2c_SendAddress(Address)) return i2c_SendStop(); // تسجيل العنوان if(! i2c_SendData(تسجيل)) إرجاع i2c_SendStop(); // إعادة التشغيل إذا (!i2c_SendStart()) إرجاع خطأ؛ // ​​عنوان الشريحة (اقرأ) if(!i2c_SendAddress(Address | 1)) إرجاع i2c_SendStop(); // تلقي بايت if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) الاستخدام: ( uint8_t ID = 0; i2c_Init(); // نفترض أنه تم تعيين PD4 على مستوى عالٍ و تعمل DAC (يجب أن يتم ذلك بطريقة ما) // أرسل بايت إلى الجهاز بعنوان 0x94، لتسجيل 0x00 بالقيمة 0x00. i2c_SendByte(0x94, 0x00, 0x00); // تلقي بايت من الجهاز بعنوان 0x94 من السجل 0x01 (ID) إلى المخزن المؤقت المتغير i2c_ReceiveByte(0x94, 0x01, &ID); )
وبطبيعة الحال، لا يمكنك القيام بذلك إلا في مثال تدريبي. إن انتظار اكتمال الإجراء طويل جدًا بالنسبة لوحدة التحكم السريعة هذه.

(دليل المطور لوحدات التحكم الدقيقة من عائلة HCS08)

للتحكم في وحدة I2C، يتم استخدام 6 سجلات وظائف خاصة:

  • IICC - سجل التحكم الأول لوحدة I2C؛
  • IICC2 - سجل التحكم الثاني لوحدة I2C؛
  • IICS - سجل حالة وحدة I2C؛
  • IICF - تسجيل معدل الباود لوحدة I2C؛
  • IICA - تسجيل عنوان وحدة I2C؛
  • IICD هو سجل بيانات وحدة I2C.

تحتوي سلسلة QE MCU على وحدتين I2C، وبالتالي، سجلين تحكم من كل نوع. على سبيل المثال، سجل الحالة الأول هو IIC1S وسجل الحالة الثاني هو IIC2S.

11.2.8.1. سجل التحكم IICC

لسلسلة MK AC. AW، Dx، EL، GB، GT، JM، LC، QE. س.ج. سان جرمان، ش، SL
يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
IICC قراءة آي سي إن IICIE مست تكساس تساك 0 0 0
سِجِلّ RSTA
إعادة ضبط 0 0 0 0 0 0 0 0
وصف البتات:
اسم البت وصف الرمز بلغة C
آي سي إن وحدة I2C تمكين بت:
0 — تم تعطيل وحدة التحكم I2C؛
1 - تم تمكين وحدة تحكم I2C.
بايسين
IICIE تمكين مقاطعة الوحدة النمطية من I2C:
0 - تم تعطيل مقاطعات طلب I2C؛
1 - تم تمكين مقاطعات طلب I2C.
bHCIE
مست بت اختيار وضع تشغيل وحدة التحكم I2C:
0 - تعمل وحدة التحكم I2C في الوضع التابع؛
1 - تعمل وحدة التحكم I2C في الوضع الرئيسي.
عندما يتغير هذا البت من 0 إلى 1، يتم إنشاء حالة البدء. وعلى العكس من ذلك، عندما يتغير البت من 1 إلى 0، يتم إنشاء شرط الإيقاف.
bMST
تكساس بت اختيار اتجاه الإرسال على خط بيانات SDA:
0 — الخط يعمل للإدخال؛
1- خط يعمل للإخراج .
bTX
تساك بت الإقرار في وضع الاستلام:
0 - يتم إنشاء بتة الإقرار بعد استلام البايت؛
1 - يتم إنشاء بتة غير معترف بها.
يتحكم هذا البت في إنشاء بت الإقرار بعد تلقي بايت بيانات، بغض النظر عما إذا كان تابعًا أو رئيسيًا.
bTXAK
RSTA إذا كانت وحدة I2C تعمل في الوضع الرئيسي، فإن كتابة 1 إلى هذا البت يؤدي إلى إعادة إنشاء حالة البدء - "إعادة التشغيل". برستا

11.2.8.2. سجل حالة IICS

لسلسلة MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
IICS قراءة تريليون قدم مكعب IAS مشغول أربل 0 سرو IICIF ركساك
سِجِلّ
إعادة ضبط 0 0 0 0 0 0 0 0
وصف البتات:
اسم البت وصف الرمز بلغة C
تريليون قدم مكعب بت إكمال التبادل. يتم تعيينه بعد اكتمال تبادل بايت واحد:
0 — لم يكتمل التبادل؛
1 - تم الصرف .
يتم مسح علامة TCF إلى 0 عند قراءة سجل بيانات IICD (في وضع الاستلام) أو عند كتابة سجل بيانات IICD (في وضع الإرسال).
bTCF
IAS علامة عنوان الرقيق. قم بتعيين ما إذا كان الجهاز يعمل في الوضع التابع وكان العنوان المرسل في الرسالة الرئيسية يساوي العنوان التابع، الذي تم تخزينه في سجل عناوين IICA.
يتم مسح العلم عند الكتابة إلى سجل IICC.
التحيز
مشغول علامة خط مشغول. يتم تعيين هذه العلامة إذا تعرفت وحدة I2C على بت البداية على السطر. يتم مسح العلامة عندما تكتشف الوحدة بت التوقف على الخط.
0 — حافلة I2C مجانية؛
1 - حافلة I2C مشغولة.
bBUSY
أربل علامة خسارة التحكيم:
0 - عدم وجود مخالفات في تشغيل حافلة I2C؛
1 – سقوط التحكيم . يجب أن تنتظر وحدة I2C لبعض الوقت ثم تبدأ عملية النقل مرة أخرى.
باربل
سرو الرقيق ينقل بت الاتجاه. تشير هذه البتة إلى حالة بت R/W في حقل العنوان:
0 - يقبل العبد. ينقل القائد إلى العبد؛
1 - ينقل العبد . القائد يتلقى من العبد.
bSRW
IICIF علامة طلبات المقاطعة غير المخدومة لوحدة I2C. اضبط على 1 إذا تم تعيين إحدى العلامات: TCF أو IAAS أو ARBL.
0 - لا توجد مقاطعات غير مخدومة؛
1- وجود انقطاعات غير مخدومة.
يتم إعادة تعيين العلم عن طريق كتابة 1 عليه.
BIIICIF
ركساك سيد الاعتراف قليلا:
0 — أكد العبد تلقي البيانات؛
1- لم يقم العبد باستقبال البيانات.
تعكس هذه البتة حالة حقل ASK في مخطط التوقيت الخاص بالتبادل.
بركساك

11.2.8.3. تسجيل عنوان IICA

يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
IICA قراءة ADDR
سِجِلّ
إعادة ضبط 0 0 0 0 0 0 0 0

يقوم هذا السجل بتخزين العنوان التابع ذو الـ 7 بت الذي قام المطور بتعيينه هذا الجهازعند تطوير النظام. تتم مقارنة هذا العنوان تلقائيًا برمز العنوان الذي تلقاه العبد في حقل العنوان على ناقل I2C. إذا تطابقت العناوين، يتم تعيين بت IAAS في سجل حالة IICS.

11.2.8.4. IICF سجل معدل الباود

لسلسلة MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
آي آي سي إف قراءة متعدد إي سي آر
سِجِلّ
إعادة ضبط 0 0 0 0 0 0 0 0
وصف البتات:

قيم المعاملات SCL_DIV وSDA_HV

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0أ 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0د 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3أ 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

يقوم هذا السجل بتخزين حقلين من البتات يحددان معلمات السرعة والتوقيت لتبادل I2C. يتم تحديد تردد إشارات التزامن بواسطة الصيغة:

وقت تسوية البيانات SDA_hold_time على ناقل I2C هو الفاصل الزمني بين لحظة ضبط إشارة SCL على 0 وتغيير البيانات الموجودة على خط SDA. تم تعيينه بواسطة المعلمة SDA_HV (SDA_Hold_Value) من الجدول لعامل ICR لسجل معدل الباود:

.

11.2.8.5. سجل بيانات IICD

لسلسلة MK AC، AW، Dx، EL، GB، GT، JM، LC، QE، QG، SG، SH، SL
يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
IICD قراءة بيانات I2C
سِجِلّ
إعادة ضبط 0 0 0 0 0 0 0 0

إذا كانت وحدة I2C تعمل في الوضع الرئيسي، فإن عملية الكتابة إلى هذا السجل تبدأ اتصال I2C (ولكن فقط إذا تم ضبط بت اتجاه الاتصال في سجل التحكم IICC بشكل صحيح، أي TX = 1). يتم تفسير البايت الأول بعد حالة البدء، والذي يكتبه البرنامج إلى سجل البيانات، بواسطة العبيد على أنه عنوان الجهاز. لذلك، يجب أن يقوم البرنامج بتكوين محتويات البايت الأول بشكل صحيح. تقوم عملية قراءة السجل بإرجاع البايت الأخير الذي تم استلامه عبر I2C. تبدأ عملية قراءة السجل أيضًا بداية استقبال البايتة التالية، ولكن فقط إذا تم ضبط بت اتجاه الاتصال في سجل التحكم IICC بشكل صحيح، أي. في تكساس = 0! مع TX = 1، لن تتسبب عملية قراءة السجل في استلام بايت جديد عبر I2C من التابع.

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

11.2.8.6. سجل التحكم IICC2

لسلسلة MK AC، Dx، EL، JM، QE، SG، SH، SL
يسجل وضع د7 د6 د5 د4 د3 د2 د1 د0
IICC قراءة GCAEN ADEXT 0 0 0 م10 م9 م8
سِجِلّ
إعادة ضبط 0 0 0 0 0 0 0 0

الخطوات الأولى مع مترجم STM32 وmikroC لبنية ARM - الجزء 4 - اتصال LCD المستند إلى I2C وpcf8574 وHD4478

أود أن أخصص المقالة التالية للعمل مع واجهة i2c الشائعة، والتي تستخدم غالبًا في مختلف الدوائر الدقيقة المتصلة بوحدة التحكم الدقيقة.

I2C عبارة عن حافلة تعمل عبر وصلتين ماديتين (بالإضافة إلى السلك المشترك). لقد تم كتابة الكثير عنها على الإنترنت، وهناك مقالات جيدة على ويكيبيديا. بالإضافة إلى ذلك، تم وصف خوارزمية تشغيل الحافلة بوضوح شديد. باختصار، الحافلة عبارة عن حافلة متزامنة ذات سلكين. يمكن أن يكون هناك ما يصل إلى 127 جهازًا في الناقل في نفس الوقت (عنوان الجهاز هو 7 بت، وسنعود إلى هذا لاحقًا). يوجد أدناه رسم تخطيطي نموذجي لتوصيل الأجهزة بحافلة i2c، مع MK باعتباره الجهاز الرئيسي.


بالنسبة لـ i2c، تستخدم جميع الأجهزة (سواء الرئيسية أو التابعة) مخرجات مفتوحة. ببساطة، يمكنهم فقط جذب الإطار إلى الأرض. يتم ضمان مستوى الناقل العالي بواسطة مقاومات السحب. عادة ما يتم تحديد قيمة هذه المقاومات في حدود 4.7 إلى 10 كيلو أوم. إن i2c حساس للغاية للخطوط المادية التي تربط الأجهزة، لذلك إذا تم استخدام اتصال بسعة كبيرة (على سبيل المثال، كابل طويل رفيع أو محمي)، فإن تأثير هذه السعة يمكن أن "يطمس" حواف الإشارة ويتداخل مع عملية عاديةالإطارات. كلما كانت مقاومة السحب أصغر، قل تأثير هذه السعة على خصائص حواف الإشارة، ولكن زاد الحمل على ترانزستورات الخرج على واجهات i2c. يتم تحديد قيمة هذه المقاومات لكل تطبيق محدد، ولكن لا ينبغي أن تكون أقل من 2.2 كيلو أوم، وإلا يمكنك ببساطة حرق ترانزستورات الإخراج في الأجهزة التي تعمل مع الناقل.

تتكون الحافلة من خطين: SDA (خط البيانات) وSCL (إشارة الساعة). ساعات جهاز الحافلة الرئيسية، عادة عضو الكنيست لدينا. عندما يكون مستوى SCL مرتفعًا، تتم قراءة المعلومات من ناقل البيانات. لا يمكن تغيير حالة SDA إلا عندما تكون إشارة الساعة منخفضة.. عندما يكون SCL مرتفعًا، تتغير الإشارة الموجودة على SDA عند توليد الإشارات يبدأ (عندما يكون SCL مرتفعًا، تتغير الإشارة الموجودة على SDA من الأعلى إلى الأدنى) و قف - عندما يكون مستوى SCL مرتفعًا، تتغير الإشارة الموجودة على SDA من منخفض إلى مرتفع).

بشكل منفصل، ينبغي القول أنه في i2c يتم تحديد العنوان كرقم 7 بت. 8 - البتة الأقل أهمية تشير إلى اتجاه نقل البيانات 0 - يعني أن العبد سوف ينقل البيانات، 1 - يستقبل.. باختصار، خوارزمية العمل مع i2c هي كما يلي:

  • مستوى عال على SDA وSCL- الحافلة مجانية، يمكنك البدء بالعمل
  • مصاعد رئيسية SCLإلى 1، وتغيير الحالة S.D.A.من 1 إلى 0 - يجذبه إلى الأرض - تتشكل إشارة يبدأ
  • يرسل السيد عنوانًا تابعًا مكونًا من 7 بتات مع بتة اتجاه (يتم تشغيل البيانات S.D.A.يتم عرضها عندما SCLيُسحب إلى الأرض، ويقرأه العبد عند تحريره). إذا لم يكن لدى العبد الوقت الكافي "للإمساك" بالجزء السابق، فإنه ينجذب SCLعلى الأرض، موضحًا للسيد أن حالة ناقل البيانات لا تحتاج إلى تغيير: "ما زلت أقرأ الحالة السابقة". بعد أن يطلق السيد الإطار، يقوم بالتحقق هل تركها العبد تذهب؟.
  • بعد إرسال 8 بتات من العنوان، يقوم السيد بإنشاء دورة الساعة التاسعة وتحرير ناقل البيانات. فإذا سمع العبد خطابه وقبله، فهو سوف اضغط S.D.A.على الأرض. هذه هي الطريقة التي يتم بها تشكيل الإشارة بسأل- مقبولة، كل شيء على ما يرام. إذا كان العبد لا يفهم أي شيء، أو أنه ببساطة ليس هناك، فلن يكون هناك من يضغط على الإطار. سينتظر السيد مهلة ويفهم أنه لم يتم فهمه.
  • بعد إرسال العنوان، إذا قمنا بتعيين الاتجاه من السيد إلى العبد(8 بتات من العنوان تساوي 1)، ثم يقوم السيد بنقل البيانات إلى العبد، دون أن ينسى التحقق من وجود بسألمن العبد، في انتظار الجهاز التابع لمعالجة المعلومات الواردة.
  • عندما يتلقى السيد البيانات من العبد، يقوم السيد نفسه بإنشاء إشارة بسألبعد استقبال كل بايت، ويتحكم العبد في وجوده. قد لا يرسل السيد على وجه التحديد بسألقبل إرسال الأمر قف، عادةً ما يوضح للعبد أنه ليست هناك حاجة لتقديم المزيد من البيانات.
  • إذا كان من الضروري، بعد إرسال البيانات بواسطة السيد (وضع الكتابة)، قراءة البيانات من العبد، ثم يقوم السيد بإنشاء الإشارة مرة أخرى يبدأ ، وإرسال عنوان الرقيق مع علامة القراءة. (إذا كان قبل الأمر يبدألم يتم نقله قفثم يتم تشكيل فريق إعادة تشغيل). يستخدم هذا لتغيير اتجاه الاتصال بين السيد والعبد. على سبيل المثال، نقوم بتمرير عنوان التسجيل إلى العبد، ثم نقرأ البيانات منه.)
  • عند الانتهاء من العمل مع العبد، يقوم السيد بإنشاء إشارة قف- عند مستوى عالٍ من إشارة الساعة، فإنها تشكل انتقالًا لحافلة البيانات من 0 إلى 1.
يحتوي STM 32 على أجهزة إرسال واستقبال ناقلة i2c مُنفذة بالأجهزة. من الممكن أن يكون هناك وحدتين أو ثلاث من هذه الوحدات في MK، لتكوينها، يتم استخدام سجلات خاصة، موصوفة في مرجع MK المستخدم.

في MicroC، قبل استخدام i2c (وكذلك أي جهاز طرفي)، يجب تهيئته بشكل صحيح. للقيام بذلك، نستخدم الوظيفة التالية (التهيئة كرئيسية):

I2Cn_Init_Advanced(unsigned long: I2C_ClockSpeed, const Module_Struct *module);

  • ن- رقم الوحدة المستخدمة، على سبيل المثال I2C1أو I2C2.
  • I2C_ClockSpeed- سرعة الحافلة 100000 (100 كيلو بايت، الوضع القياسي) أو 400000 (400 كيلو بايت، الوضع السريع). والثاني أسرع 4 مرات، ولكن ليس كل الأجهزة تدعمه
  • *وحدة- مؤشر إلى وحدة طرفية، على سبيل المثال &_GPIO_MODULE_I2C1_PB67، دعونا لا ننسى هنا ذلك مساعد الكود (مفتاح التحكم ) يساعد كثيرا.
أولا، دعونا نتحقق مما إذا كانت الحافلة مجانية؛ هناك وظيفة لهذا I2Cn_Is_Idle();يُعاد 1 إذا كانت الحافلة مجانية، و0 إذا كان هناك تبديل عليها.

I2Cn_Start();
أين ن- رقم وحدة i2c المستخدمة في وحدة التحكم الدقيقة الخاصة بنا. سترجع الدالة 0 إذا كان هناك خطأ في الناقل و1 إذا كان كل شيء على ما يرام.

من أجل نقل البيانات إلى العبد نستخدم الوظيفة:

I2Cn_Write(unsigned charslave_address, unsigned char *buf, unsigned long count,unsigned long END_mode);

  • ن- رقم الوحدة المستخدمة
  • Slave_address- عنوان الرقيق 7 بت.
  • * بوف- مؤشر لبياناتنا - مصفوفة بايت أو بايت.
  • عدد- عدد بايتات البيانات المنقولة.
  • END_mode- ماذا تفعل بعد نقل البيانات إلى العبد، END_MODE_STOP - إرسال إشارة قف، أو END_MODE_RESTART أعد الإرسال يبدأ، توليد إشارة إعادة تشغيلوإعلام القسم بأن الجلسة معه لم تنته وسيتم الآن قراءة البيانات منه.
لقراءة البيانات من العبد، استخدم الدالة:

I2Cn_Read(charslave_address, char *ptrdata, unsigned long count,unsigned long END_mode);

  • ن- رقم الوحدة المستخدمة
  • Slave_address- عنوان الرقيق 7 بت.
  • * بوف- مؤشر لمتغير أو مصفوفة نستقبل فيها البيانات، اكتب char أو short int
  • عدد- عدد بايتات البيانات المستلمة.
  • END_mode- ماذا تفعل بعد تلقي البيانات من العبد - END_MODE_STOP - إرسال إشارة قف، أو END_MODE_RESTART إرسال إشارة إعادة تشغيل.
دعونا نحاول توصيل شيء ما بـ MK. لنبدأ بـ: الدائرة الدقيقة PCF8574(A) الواسعة الانتشار، وهي عبارة عن موسع لمنافذ الإدخال/الإخراج يتم التحكم فيها عبر ناقل i2c. تحتوي هذه الشريحة على سجل داخلي واحد فقط، وهو منفذ الإدخال/الإخراج الفعلي. أي أنك إذا قمت بتمرير بايت إليها، فسوف تتعرض على الفور لاستنتاجاتها. إذا قمت بحساب بايت منه (Transmit يبدأعنوان مع قراءة العلم والإشارة إعادة تعيين،قراءة البيانات وأخيرا توليد إشارة قف) فسوف يعكس الحالات المنطقية على مخرجاته. لنقم بتوصيل دائرتنا الدقيقة وفقًا لورقة البيانات:


يتم تشكيل عنوان الدائرة المصغرة من حالة المسامير أ0، أ1، أ2. للدوائر الدقيقة PCF8574العنوان سيكون: 0100A0A1A2. (على سبيل المثال، لدينا A0 وA1 وA2 على مستوى عالٍ، وبالتالي فإن عنوان دائرتنا الصغيرة سيكون 0b0100 111 = 0x27). ل PCF8574A - 0111A0A1A2، والذي من خلال مخطط الاتصال الخاص بنا سيعطي العنوان 0b0111 111 = 0x3F. إذا، على سبيل المثال، A2 متصل بالأرض، فإن عنوان PCF8574Aسوف 0x3B. في المجموع، يمكن تركيب 16 دائرة دقيقة في وقت واحد على ناقل i2c واحد، و8 PCF8574A وPCF8574 لكل منهما.

دعنا نحاول نقل شيء ما، وتهيئة ناقل i2c ونقل شيء ما إلى PCF8574.

#define PCF8574A_ADDR 0x3F // عنوان PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // إنشاء إشارة البداية I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); // نقل بايت واحد من البيانات وإنشاء STOP إشارة) شار PCF8574A_reg ؛ // المتغير الذي نكتبه في PCF8574 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // ابدأ I2C Delay_ms(25); // انتظر قليلاً PCF8574A_reg.b0 = 0; // أشعل أول LED PCF8574A_reg.b1 = 1؛ // أطفئ مؤشر LED الثاني while (1) (lay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // عكس حالة مصابيح LED I2C_PCF8574_WriteReg (PCF8574A_reg) ; // نقل البيانات إلى PCF8574 الخاص بنا ))
نقوم بتجميع برنامجنا وتشغيله ونرى أن مصابيح LED الخاصة بنا تومض بالتناوب.
لقد قمت بتوصيل كاثود LEDs بجهاز PCF8574 لسبب ما. الشيء هو أنه عندما يتم توفير 0 منطقي للإخراج، فإن الدائرة الدقيقة تسحب مخرجاتها بأمانة إلى الأرض، ولكن عندما يتم تطبيق 1 منطقي، فإنها تقوم بتوصيلها بالطاقة + من خلال مصدر حالي يبلغ 100 ميكرو أمبير. أي أنه لا يمكنك الحصول على الرقم المنطقي "الصادق" عند الإخراج. ولا يمكنك إضاءة مصباح LED بقوة 100 ميكرو أمبير. تم ذلك من أجل تكوين مخرج PCF8574 إلى الإدخال بدون سجلات إضافية. نحن نكتب ببساطة لتسجيل الإخراج 1 (بشكل أساسي ضبط حالات الدبوس على Vdd) ويمكننا ببساطة اختصاره إلى الأرض. لن يسمح المصدر الحالي لمرحلة الإخراج الخاصة بموسع الإدخال / الإخراج الخاص بنا "بالاحتراق". إذا تم سحب الساق إلى الأرض، فإن إمكانات الأرض عليها، ويتم قراءة المنطق 0. إذا تم سحب الساق إلى +، تتم قراءة المنطقي 1. من ناحية، الأمر بسيط، ولكن من ناحية أخرى، يجب أن تتذكر هذا دائمًا عند العمل مع هذه الدوائر الدقيقة.


دعونا نحاول قراءة حالة دبابيس شريحة الموسع لدينا.

#define PCF8574A_ADDR 0x3F // عنوان PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // إنشاء إشارة البداية I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); // نقل بايت واحد من البيانات وإنشاء STOP إشارة) void I2C_PCF8574_ReadReg (unsigned char rData) ( I2C1_Start(); // إنشاء إشارة البداية I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // قراءة بايت واحد من البيانات وإنشاء إشارة STOP) char PCF8574A_reg; // المتغير الذي نكتبه إلى PCF8574 char PCF8574A_out; // المتغير الذي قرأنا فيه وPCF8574 char lad_state; // مؤشر LED الخاص بنا قيد التشغيل أو الإيقاف void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // بدء تشغيل I2C Delay_ms(25); // انتظر قليلاً PCF8574A_reg.b0 = 0; // قم بإضاءة أول مؤشر LED PCF8574A_reg.b1 = 1; // أطفئ مؤشر LED الثاني PCF8574A_reg.b6 = 1; // اسحب المنفذين 6 و7 إلى الطاقة. PCF8574A_reg.b7 = 1; while (1) (lay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // كتابة البيانات إلى PCF8574 I2C_PCF8574_ReadReg (PCF8574 A_out )؛ // القراءة من PCF8574 if (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0؛ // إذا تم الضغط على زر واحد (البت السادس من بايت القراءة من PCF8574 هو 0، إذن قم بتشغيل/إيقاف تشغيل مؤشر LED الخاص بنا) إذا (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // مماثل لزرين و2 مصابيح LED))
الآن بالضغط على الأزرار نقوم بتشغيل أو إيقاف تشغيل مؤشر LED الخاص بنا. الدائرة الدقيقة لها مخرج آخر إنت. يتم إنشاء نبض عليه في كل مرة تتغير فيها حالة أطراف موسع الإدخال / الإخراج الخاص بنا. من خلال توصيله بإدخال المقاطعة الخارجية لـ MK الخاص بنا (سأخبرك بكيفية تكوين المقاطعات الخارجية وكيفية العمل معها في إحدى المقالات التالية).

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


يمكن العثور بسهولة على ورقة البيانات الخاصة به ووحدة التحكم HD44780 على الإنترنت. لنقم بتوصيل شاشتنا بـ PCF8574، وشاشتها، على التوالي، بـ STM32.

HD44780يستخدم واجهة بوابات متوازية. يتم إرسال البيانات بواسطة 8 (في دورة ساعة واحدة) أو 4 (في دورتين على مدار الساعة) عند الإخراج ه. (تتم قراءته بواسطة وحدة تحكم العرض على الحافة التنازلية، والانتقال من 1 إلى 0). خاتمة ر.س.يشير إلى ما إذا كنا نرسل البيانات إلى شاشتنا ( رس = 1) (الأحرف التي يجب عرضها هي في الواقع رموز ASCII) أو الأمر ( رس = 0). رويشير إلى اتجاه نقل البيانات أو الكتابة أو القراءة. عادة نكتب البيانات على الشاشة، لذلك ( رو = 0). يتحكم المقاوم R6 في تباين الشاشة. لا يمكنك ببساطة توصيل مدخل ضبط التباين بالأرض أو الطاقة، وإلا فلن ترى أي شيء.. يتم استخدام VT1 لتشغيل وإيقاف الإضاءة الخلفية للشاشة وفقًا لأوامر MK. لدى MicroC مكتبة للعمل مع مثل هذه الشاشات عبر واجهة متوازية، ولكن عادةً ما يكون إنفاق 8 أرجل على الشاشة أمرًا مكلفًا، لذلك أستخدم دائمًا PCF8574 للعمل مع مثل هذه الشاشات. (إذا كان أي شخص مهتمًا، سأكتب مقالًا حول العمل مع شاشات العرض المستندة إلى HD44780 المضمنة في MicroC عبر واجهة متوازية.) بروتوكول التبادل ليس معقدًا بشكل خاص (سنستخدم 4 خطوط بيانات وننقل المعلومات في دورتين على مدار الساعة)، ويظهر ذلك بوضوح من خلال مخطط التوقيت التالي:


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

  • 0x28- التواصل مع المؤشر عبر 4 خطوط
  • 0x0C- تمكين إخراج الصورة، وتعطيل عرض المؤشر
  • 0x0E- تمكين إخراج الصورة، وتمكين عرض المؤشر
  • 0x01- مسح المؤشر
  • 0x08- تعطيل إخراج الصورة
  • 0x06- بعد عرض الرمز، يتحرك المؤشر بمقدار مكان واحد مألوف
وبما أننا سنحتاج إلى العمل مع هذا المؤشر في كثير من الأحيان، فسوف نقوم بإنشاء مكتبة المكونات الإضافية "i2c_lcd.h" . لهذا الغرض في مدير المشروع ملفات الرأس و اختار إضافة ملف جديد . لنقم بإنشاء ملف الرأس الخاص بنا.

#define PCF8574A_ADDR 0x3F // عنوان PCF8574 #define DB4 b4 // المراسلات بين منافذ PCF8574 والمؤشر #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // التحكم في الإضاءة الخلفية #define displenth 20 // عدد الأحرف في خط العرض لدينا static unsigned char BL_status; // متغير يخزن حالة الإضاءة الخلفية (تشغيل / إيقاف) void lcd_I2C_Init(void); // العرض ووظيفة التهيئة PCF8574 void lcd_I2C_txt(char *pnt); // يعرض سطرًا من النص، والمعلمة هي مؤشر لهذا السطر void lcd_I2C_int(int pnt); // يعرض قيمة متغير عدد صحيح، المعلمة هي قيمة الإخراج void lcd_I2C_Goto(unsigned shortrow, unsigned short col); // يحرك المؤشر إلى الموضع المحدد، صف المعلمات - السطر (من 1 إلى 2 أو 4 حسب الشاشة) والعمود - (من 1 إلى displenth)) void lcd_I2C_cls(); // يمسح شاشة LCD_I2C_backlight الفارغة (حالة int قصيرة غير موقعة)؛ // تمكين (عند الإرسال 1 وتعطيل - عند الإرسال 0 الإضاءة الخلفية للشاشة)
الآن دعونا نصف وظائفنا، مرة أخرى نذهب إلى مدير المشروعانقر بزر الماوس الأيمن على المجلد مصادر و اختار إضافة ملف جديد . قم بإنشاء ملف "i2c_lcd.с" .

#include "i2c_lcd.h" // تضمين ملف الرأس الخاص بنا char LCD_reg; // سجل تخزين مؤقت للبيانات المرسلة إلى PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) // وظيفة لإرسال البيانات عبر i2c إلى شريحة PCF8574 ( I2C1_Start(); I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); ) void LCD_COMMAND (char com) // وظيفة إرسال أمر إلى شاشتنا (lcd_reg = 0؛ // اكتب 0 إلى السجل المؤقت LCD_reg.BL = BL_status.b0؛ // اضبط دبوس الإضاءة الخلفية وفقًا لقيمة المتغير الذي يخزن الإضاءة الخلفية الحالة LCD_reg.DB4 = com.b4؛ // قم بتعيين البتات الأربعة الأكثر أهمية في أمرنا إلى ناقل بيانات المؤشر LCD_reg.DB5 = com.b5؛ LCD_reg.DB6 = com.b6؛ LCD_reg.DB7 = com.b7؛ LCD_reg .EN = 1؛ // اضبط خرج الوميض على 1 I2C_PCF8574_WriteReg (lcd_reg)؛ // اكتب إلى سجل PCF8574، وأرسل البيانات فعليًا إلى المؤشر Delay_us (300)؛ // انتظر المهلة LCD_reg.EN = 0؛ // أعد ضبط النبضة القوية على 0، وسيقرأ المؤشر البيانات I2C_PCF8574_WriteReg (lcd_reg)؛ Delay_us (300) ; LCD_reg = 0؛ LCD_reg.BL = BL_status.b0؛ LCD_reg.DB4 = com.b0؛ // نفس الشيء بالنسبة للأربعة الأقل أهمية البتات LCD_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; LCD_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); تأخير_us(300); LCD_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); تأخير_us(300); ) void LCD_CHAR (unsigned char com) //إرسال البيانات (رمز حرف ASCII) إلى المؤشر (lcd_reg = 0؛lcd_reg.BL = BL_status.b0؛lcd_reg.EN = 1؛lcd_reg.RS = 1؛ //إرسال حرف يختلف عن إرسال الأوامر عن طريق ضبط بت RS على 1 LCD_reg.DB4 = com.b4؛ // قم بتعيين البتات الأربع الأكثر أهمية عند المدخلات LCD_reg.DB5 = com.b5؛ LCD_reg.DB6 = com.b6؛ LCD_reg.DB7 = com.b7؛ I2C_PCF8574_WriteReg (lcd_reg) ؛ تأخير_us (300)؛ LCD_reg.EN = 0؛ // إعادة ضبط النبض الوامض إلى 0، يقرأ المؤشر البيانات I2C_PCF8574_WriteReg (lcd_reg)؛ تأخير_us (300)؛ LCD_reg = 0؛ LCD_reg .BL = BL_status.b0؛lcd_reg.EN = 1؛lcd_reg.RS = 1؛lcd_reg.DB4 = com.b0؛ // تعيين البتات الأربعة الأقل أهمية عند المدخلات LCD_reg.DB5 = com.b1؛ lcd_reg.DB6 = com.b2؛ lcd_reg.DB7 = com.b3؛ I2C_PCF8574_WriteReg (lcd_reg)؛ Delay_us (300)؛ LCD_reg.EN = 0؛ I2C_PCF8574_WriteReg (lcd_reg)؛ Delay_us (300)؛ ) void lcd_I2C_Init(void) ( I2C1_Init_Advanced(400000, &_GPIO_MODU LE_I2C1_PB67 ); // تهيئة وحدة I2c الخاصة بنا في M K Delay_ms(200) ;lcd_Command(0x28); // العرض في وضع 4 بتات لكل ساعة تأخير_ms (5); شاشات الكريستال السائل_الأوامر(0x08); // تعطيل إخراج البيانات إلى شاشة العرض Delay_ms (5); شاشات الكريستال السائل_Command(0x01); // امسح تأخير العرض (5)؛ شاشات الكريستال السائل_الأوامر(0x06); // تمكين التحول التلقائي للمؤشر بعد عرض الرمز تأخير_ms (5)؛ LCD_Command(0x0C); // قم بتشغيل عرض المعلومات دون عرض المؤشر Delay_ms (25); ) void lcd_I2C_txt(char *pnt) // إخراج سلسلة من الأحرف إلى الشاشة (غير موقعة قصيرة int i؛ // متغير فهرس مجموعة الأحرف المؤقتة char tmp_str؛ // مجموعة مؤقتة من الأحرف، الطول 1 أكبر من طول الشاشة سطر، نظرًا لأن السطر يحتاج إلى إنهاء сiv بحرف NULL ASCII 0x00 strncpy(tmp_str, pnt, displenth); // انسخ ما لا يزيد عن أحرف displenth من السلسلة الأصلية إلى السلسلة المؤقتة الخاصة بنا for (i=0; i لنقم الآن بتوصيل المكتبة التي تم إنشاؤها حديثًا بالملف من خلال وظيفتنا الرئيسية:

#include "i2c_lcd.h" // تضمين ملف الرأس الخاص بنا غير الموقع int i; // عداد متغير مؤقت void main() (lcd_I2C_Init(); // تهيئة العرض lcd_I2C_backlight (1)؛ // تشغيل الإضاءة الخلفية lcd_I2C_txt ("Hello habrahabr")؛ // عرض الخط بينما (1) (تأخر_ms( 1000)؛ LCD_I2C_Goto (2,1)؛ //انتقل إلى الحرف 1 من السطر 2 LCD_i2c_int (i)؛ //عرض القيمة i++؛ //زيادة العداد))

إذا تم تجميع كل شيء بشكل صحيح، فيجب أن نرى نصًا على المؤشر وعدادًا يتزايد كل ثانية. بشكل عام، لا شيء معقد :)

وفي المقالة القادمة سنستمر في فهم بروتوكول i2c والأجهزة التي تعمل معه. لنفكر في العمل مع ذاكرة EEPROM 24XX ومقياس التسارع/الجيروسكوب MPU6050.

يوجد اليوم ضيف جديد على طاولة العمليات لدينا، وهو منتج Microchip، وهو موسع المنافذ MCP23008-E. يهدف هذا الشيء (كما يوحي الاسم) إلى زيادة عدد أرجل الإدخال/الإخراج الخاصة بوحدة التحكم الدقيقة إذا أصبحت فجأة غير كافية. بالطبع، إذا كنا بحاجة إلى الأرجل والمخارج، فيمكننا أن نأخذها ولا نقلق بشأنها. إذا كنت بحاجة إلى أرجل الإدخال، فهناك حل يعتمد على المنطق الصارم. إذا كنا بحاجة إلى كل من المدخلات والمخرجات وكذلك سحبًا متحكمًا للمدخلات، فربما يكون موسع المنفذ هو الحل الأكثر شيوعًا. أما بالنسبة لسعر الجهاز فهو متواضع جدًا – حوالي دولار واحد. سأحاول في هذه المقالة أن أصف بالتفصيل كيفية التحكم في هذه الدائرة المصغرة باستخدام متحكم AVR.

أولا، قليلا عن الخصائص:

  • 8 دبابيس منفذ مستقلة
  • واجهة للتواصل مع العالم الخارجي - I2C (تردد يصل إلى 1.7 ميجاهرتز)
  • سحب قابل للتخصيص للمداخل
  • قد يهز ساقه عندما تتغير حالة بعض المدخلات
  • ثلاثة مدخلات لتعيين عنوان الدائرة المصغرة (يمكنك تعليق 8 أجهزة على ناقل واحد)
  • جهد التشغيل من 1.8 إلى 5.5 فولت
  • انخفاض الاستهلاك الحالي
  • مجموعة كبيرة من العبوات (PDIP/SOIC/SSOP/QFN)

أفضل استخدام واجهة I2C، فهي تجذبني بعدد صغير من الأسلاك :-) ومع ذلك، إذا كنت بحاجة إلى سرعة عالية جدًا (تصل إلى 10 ميجاهرتز)، فأنت بحاجة إلى استخدام واجهة SPI الموجودة في MCP23S08. الفرق بين MCP23S08 وMCP23008، كما أفهمه، هو فقط في الواجهة وفي عدد الأرجل لتعيين عنوان الشريحة. من يحب شيئا أقصر؟ دبوس mikruhi موجود في ورقة البيانات، وهو ليس مثيرًا للاهتمام بأي شكل من الأشكال ولن يتم أخذه في الاعتبار هنا. لذلك، دعونا ننتقل على الفور إلى كيفية البدء في العمل مع هذا الجهاز. يتلخص كل العمل في كتابة وقراءة بيانات معينة من سجلات الدائرة الدقيقة. من دواعي سروري أنه لم يكن هناك الكثير من السجلات على الإطلاق - أحد عشر فقط. إن كتابة البيانات وقراءتها من السجلات أمر بسيط للغاية، قم بالنشر عنها. صحيح أننا لا نتحدث عن السجلات، ولكن المبدأ هو نفسه. الآن كل ما تبقى هو معرفة السجلات التي يجب قراءة ماذا منها وأي السجلات تكتب ماذا. وبطبيعة الحال، سوف تساعدنا ورقة البيانات في هذا. نتعلم من ورقة البيانات أن الدائرة الدقيقة تحتوي على السجلات التالية:

سجل IODIR
يحدد الاتجاه الذي تتدفق فيه البيانات. إذا تم ضبط البتة المقابلة لساق معينة على واحد، فإن الساق تعتبر مدخلاً. في حالة إعادة التعيين إلى الصفر، قم بالخروج. باختصار، هذا السجل مشابه لـ DDRx في AVR (فقط في AVR 1 هو مخرج و0 هو مدخل).

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

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

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

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

سجل إيكون
هذا هو سجل الإعدادات يتكون من أربع بتات:
SEQOP— تعالج عناصر التحكم في البت الزيادة التلقائية. إذا تم تثبيته، فسيتم تعطيل الزيادة التلقائية، وإلا فسيتم تمكينه. إذا قمنا بإيقاف تشغيله، يمكننا قراءة قيمة نفس السجل بسرعة كبيرة نظرًا لأننا لا نحتاج إلى إرسال عنوانه في كل مرة. إذا كنت بحاجة إلى قراءة جميع السجلات الـ 11 بسرعة، فيجب تمكين الزيادة التلقائية. في كل مرة تتم فيها قراءة بايت من السجل، سيزداد العنوان نفسه بمقدار واحد ولن يلزم إرساله.
ديسلو- من يعرف ما هو هذا الشيء. بغض النظر عن كيفية تحريفه، كل شيء لا يزال يعمل. سأكون سعيدا إذا شرح شخص ما.
هاين- بت إعداد إخراج INT. إذا تم ضبطه، فسيتم تكوين الدبوس كمصرف مفتوح، وإذا تمت إعادة تعيين البت، فإن المستوى النشط في ساق INT يحدد بت INTPOL
الانتربول— يحدد المستوى النشط في الساق INT. إذا تم ضبطه، فإن المستوى النشط هو واحد، وإلا صفر.

لا يزال هناك القليل هاينولكن لا يتم استخدامه في هذه الشريحة (يعمل على تشغيل/إيقاف دبابيس عنونة الأجهزة في MCP23S08)

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

سجل INTF
تسجيل إشارة المقاطعة. إذا تم ضبط البت، فهذا يعني أن ساق المنفذ المقابل تسبب في حدوث مقاطعة. بالطبع، يجب تمكين المقاطعات للأرجل المطلوبة في سجل GPINTEN

سجل INTCAP
عند حدوث مقاطعة، تتم قراءة المنفذ بأكمله في هذا السجل. إذا كان هناك المزيد من المقاطعات بعد ذلك، فلن يتم استبدال محتويات هذا السجل بالقيمة الجديدة حتى نقرأها أو GPIO.

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

سجل OLAT
من خلال كتابة البيانات إلى هذا السجل، نقوم بإخراج البيانات إلى المنفذ. إذا قرأت البيانات من هناك، فسوف تقرأ ما تم كتابته، وليس ما هو موجود بالفعل عند مدخلات المنفذ. لقراءة المدخلات نستخدم GPIO فقط.

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

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

برنامج راشيريتيل; const AdrR=%01000001; // عنوان الشريحة ذات بت القراءة const AdrW=%01000000; // عنوان الشريحة التي تحتوي على سجل البت var r:byte; /// يكتب الإجراء البيانات من متغير Dat إلى السجل على العنوان Adr الإجراء WriteReg(Dat,Adr:byte); ابدأ TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); نهاية؛ /// تقوم الدالة بإرجاع قيمة التسجيل على العنوان Adr Function ReadReg(Adr:byte):byte; فار أ: بايت؛ ابدأ TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Start(); TWI_Write(AdrR); أ:=TWI_Read(0); TWI_Stop(); النتيجة:=أ؛ نهاية؛ ابدأ TWI_INIT(200000); ///تهيئة i2c WriteReg(%00001111,0x00); // أقل 4 بتات هي مدخلات والبتات 4 المتبقية هي مخرجات WriteReg(%00001111,0x06); //على سحب لأعلى لـ 4 مدخلات بينما يبدأ TRUE r:=ReadReg(0x09); // اقرأ حالة المدخلات r:= NOT r; // من الضروري قلب البتات، وإلا ستضيء مصابيح LED عند تحرير الزر r:= r shl 4; // إزاحة 4 بتات إلى اليسار... WriteReg(r,0x0A); // عرض حالة الأزرار end؛ نهاية.

تم النشر بتاريخ 26/10/2016

في المقالة السابقة، نظرنا إلى تشغيل STM32 مع الحافلة I 2 C باعتبارها Master. أي أنه كان القائد واستجوب المستشعر. الآن دعونا نجعل STM32 تابعًا ونستجيب للطلبات، أي أنه في حد ذاته يعمل كجهاز استشعار. سنخصص 255 بايت من الذاكرة للسجلات ذات العناوين من 0 إلى 0xFF، ونسمح للسيد بكتابتها/قراءتها. ولجعل المثال ليس بهذه البساطة، دعونا نجعل جهاز STM32 الخاص بنا عبارة عن محول تناظري إلى رقمي بواجهة I 2 C. سيقوم ADC بمعالجة 8 قنوات. ستعطي وحدة التحكم نتائج التحولات إلى السيد عند القراءة من السجلات. وبما أن نتيجة تحويل ADC هي 12 بت، فإننا نحتاج إلى مسجلين (2 بايت) لكل قناة ADC.

i2c_slave.hيحتوي على الإعدادات:

I2CSLAVE_ADDR- عنوان أجهزتنا؛

ADC_ADDR_START- عنوان البداية للسجلات المسؤولة عن نتائج تحويلات ADC.

في الملف i2c_slave.cنحن مهتمون أكثر بالوظائف get_i2c1_ramو set_i2c1_ram. وظيفة get_i2c1_ramهو المسؤول عن قراءة البيانات من السجلات. تقوم بإرجاع البيانات من العنوان المحدد، والتي تعطى للسيد. في حالتنا، تتم قراءة البيانات من المصفوفة i2c1_ramولكن إذا طلب السيد الحصول على عناوين التسجيل من النطاق المخصص لنتائج ADC، فسيتم إرسال بيانات تحويل ADC.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( // بيانات ADC إذا ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

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

set_i2c1_ram:

مجموعة باطلة_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

التهيئة بسيطة للغاية:

Int main(void) (SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ( ) )

أولاً قمنا بتعيين الحد الأقصى لتردد التشغيل لوحدة التحكم. السرعة القصوى مطلوبة عند الحاجة إلى تجنب أي تأخير في الحافلة I 2 C. ثم نبدأ عملية ADC باستخدام DMA. عن . عن . وأخيرًا، نقوم بتهيئة الناقل I 2 C كـ عبد. كما ترون، لا شيء معقد.

لنقم الآن بتوصيل وحدة STM32 الخاصة بنا بـ Raspberry Pi. لنقم بتوصيل مقاييس الجهد بقنوات ADC. وسوف نقرأ مؤشرات ADC من وحدة التحكم الخاصة بنا. لا تنس أنه لكي تعمل الحافلة I 2 C، تحتاج إلى تثبيت مقاومات السحب على كل سطر من الحافلة.

في وحدة التحكم Raspberry، دعونا نتحقق مما إذا كان جهازنا مرئيًا على الناقل I 2 C (حول ذلك):

I2cdetect -y 1

كما ترون، عنوان الجهاز 0x27، على الرغم من أننا حددنا 0x4E. عندما يكون لديك الوقت، فكر في سبب حدوث ذلك.

للقراءة من سجلات جهاز I 2 C-Slave، قم بتنفيذ الأمر:

I2cget -y 1 0x27 0x00

أين:
0x27- عنوان الجهاز،
0x00- عنوان التسجيل (0x00…0xFF).

للكتابة إلى سجلات جهاز I 2 C-Slave، قم بتنفيذ الأمر:

I2cset -y 1 0x27 0xA0 0xDD

دي:
0x27- عنوان الجهاز،
0xA0- عنوان التسجيل
0xDD-بيانات 8 بت (0x00…0xFF)

كتب الأمر السابق الرقم 0xDD في السجل 0xA0(يمكنك الكتابة إلى أول 16 سجلا، ولكن لا فائدة من ذلك، ولكنها محجوزة لـ ADC). الآن دعونا نقرأ:

I2cget -y 1 0x27 0xA0

لتبسيط عملية قراءة بيانات قناة ADC، كتبت نصًا:

#!/usr/bin/env python import smbus import time bus = smbus.SMBus(1) عنوان = 0x27 while (1): ADC = (); بالنسبة إلى i في النطاق (0، 8): LBS = bus.read_byte_data(address, 0x00+i*2) MBS = bus.read_byte_data(address, 0x00+i*2+1) ADC[i] = MBS*256 + LBS طباعة ADC time.sleep(0.2)

يقوم باستقصاء وعرض نتائج جميع قنوات ADC الثمانية على وحدة التحكم.

بطريقة مماثلة، يمكنك الجمع بين العديد من وحدات التحكم الدقيقة. يجب أن يكون أحدهما السيد () والآخر تابعًا.

أتمنى لك النجاح!




قمة