مرحبا يا هابر!في هذه المقالة ، أود أن أتحدث عن تجربتي في توصيل شاشات LCD إلى متحكم STM32 باستخدام مكتبة HAL على ناقل I2C.
سأقوم بتوصيل الشاشة 1602 و 2004. كلاهما يحتوي على محول I2C ملحوم على أساس رقاقة PCF8574T. ستكون لوحة التصحيح Nucleo767ZI ، وستكون بيئة التطوير STM32CubeIDE 1.3.0.لن أتحدث بالتفصيل عن مبدأ تشغيل الحافلات I2C، أنصحك أن ننظر هنا و هنا .نقوم بإنشاء المشروع ، حدد لوحة التصحيح: نشير
إلى أننا سنستخدم I2C1. سأقوم أيضًا بتوصيل UART5 للتواصل مع اللوحة ، وهذا ضروري لتلقي معلومات من اللوحة حول عنوان العرض.
في نفس النافذة ، يمكنك رؤية أرقام الأرجل التي تتصل بها الشاشة ، في حالتي ، تحولت إلى ما يلي:
للبدء ، قم بتوصيل شاشة واحدة فقط ، سأبدأ بـ 1602. أيضًا ، سأقوم بتوصيل محول USB-UART CH340 ، المعروف لسائقي arduin ذوي الخبرة ، لتلقي البيانات من اللوحة.
يرجى ملاحظة أن المحول يربط RX بـ TX و TX إلى RX ، فإن وصلة العبور على المحول هي 3.3V
دعونا نلقي نظرة فاحصة على العمل مع رقاقة PCF8574T والشاشة. فيما يلي رسم تخطيطي لوحدة نمطية مع شاشة عرض:
تشبه رقاقة PCF8574T في وظيفتها سجل التحول 74hc595 - حيث تستقبل وحدات البايت عبر واجهة I2C وتعين قيم البت المطابق لمخرجاتها (P0-P7).ضع في اعتبارك دبابيس الدائرة المصغرة المتصلة بالشاشة وما هي مسؤوليتها:- يتم توصيل الطرف P0 للدائرة الدقيقة بطرف RS للشاشة ، المسؤولة عن استقبال بيانات العرض (1) أو تعليمات تشغيل العرض (0) ؛
- يتم توصيل Pin P1 بـ R \ W ، إذا كان 1 - كتابة البيانات على الشاشة ، 0 - قراءة ؛
- يتم توصيل المحطة الطرفية P2 بمحطة CS - المحطة ، عند قراءة الحالة التي تتم قراءتها ؛
- الاستنتاج P3 - التحكم في الإضاءة الخلفية ؛
- يتم استخدام الاستنتاجات P4 - P7 لإرسال البيانات إلى الشاشة.
يمكن توصيل العديد من الأجهزة بناقل I2C واحد في نفس الوقت. لكي نتمكن من الوصول إلى جهاز معين ، لكل منهم عنوانه الخاص ، سنكتشف أولاً. إذا لم يتم إغلاق جهات الاتصال A1 و A2 و A3 على لوحة المحول ، فمن المرجح أن يكون العنوان 0x27 ، ولكن من الأفضل التحقق منه. للقيام بذلك ، سنكتب وظيفة صغيرة تعرض عناوين جميع الأجهزة المتصلة بناقل I2C:void I2C_Scan ()
{
HAL_StatusTypeDef res;
char info[] = "Scanning I2C bus...\r\n";
HAL_UART_Transmit(&huart5, (uint8_t*)info, strlen(info), HAL_MAX_DELAY);
for(uint16_t i = 0; i < 128; i++)
{
res = HAL_I2C_IsDeviceReady(&hi2c1, i << 1, 1, HAL_MAX_DELAY);
if(res == HAL_OK)
{
char msg[64];
snprintf(msg, sizeof(msg), "0x%02X", i);
HAL_UART_Transmit(&huart5, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);
HAL_UART_Transmit(&huart5, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
}
else HAL_UART_Transmit(&huart5, (uint8_t*)".", 1, HAL_MAX_DELAY);
}
HAL_UART_Transmit(&huart5, (uint8_t*)"\r\n", 2, HAL_MAX_DELAY);
}
تستقصي هذه الوظيفة جميع العناوين من 0 إلى 127 ، وإذا تم تلقي استجابة من هذا العنوان ، فإنها ترسل رقم العنوان في شكل سداسي عشري إلى UART.للتواصل مع المجلس ، أستخدم برنامج Termite. بشكل افتراضي ، يتم تعيين سرعة UART للميكروكونترولر إلى 115200 ، يجب عليك تعيين نفس السرعة في النمل الأبيض. نحن نطلق على الوظيفة في الجزء الرئيسي من البرنامج ، ونومض اللوح ونقوم بالاتصال باللمسي الأبيض بوحدة التحكم الدقيقة لدينا:
تعرض النقاط جميع العناوين التي لم يتم استلام الإجابة منها. العنوان على شاشتي هو 0x26 ، منذ أن قمت بلحام الطائر A0. الآن قم بتوصيل الشاشة الثانية الموازية للشاشة الأولى ، وانظر ما سيعطيه البرنامج:
لدينا عنوانان: 0x26 (عرض 1602) و 0x27 (عرض 2004). الآن حول كيفية العمل مع الشاشة. يرسل وحدة التحكم الدقيقة بايت العنوان ، وتقوم جميع الأجهزة المتصلة بالناقل بفحصها بنفسها. إذا تطابق ، تبدأ الوحدة في الاتصال بوحدة التحكم الدقيقة. بادئ ذي بدء ، تحتاج إلى تكوين العرض: من أين سيذهب عدد الأحرف ومن أي اتجاه ، وكيف سيتصرف المؤشر ، إلخ. بعد ذلك ، سيكون من الممكن بالفعل نقل المعلومات للعرض على الشاشة. تكمن الخصوصية في أنه لا يمكننا استخدام سوى 4 بتات لإرسال المعلومات ، أي يجب تقسيم البيانات إلى قسمين. يتم تخزين البيانات في البتات العالية (4-7) ، ويتم استخدام البتات المنخفضة للإشارة إلى ما إذا كان سيتم تشغيل الإضاءة الخلفية (3 بتات) ، وما إذا كانت البيانات قادمة للإخراج ، أو إعدادات العرض (خرج RS ، 0 بت) ، و 2 بت ، من خلال التغيير الذي تتم القراءة ، أيe لإرسال 1 بايت من البيانات تحتاج إلى إرسال 4 بايت - البايت الأول سيحتوي على 4 بت من المعلومات ، البتة الثانية للحالة 1 ، البايت الثاني هو تكرار للبايت الأول ، فقط البت الثاني للحالة 0. البايتان الثالث والرابع متشابهان ، فقط يحتويان النصف الثاني من البيانات. يبدو غير مفهوم قليلاً ، سأريكم مثالاً:void I2C_send(uint8_t data, uint8_t flags)
{
HAL_StatusTypeDef res;
for(;;) {
res = HAL_I2C_IsDeviceReady(&hi2c1, LCD_ADDR, 1, HAL_MAX_DELAY);
if(res == HAL_OK) break;
}
uint8_t up = data & 0xF0;
uint8_t lo = (data << 4) & 0xF0;
uint8_t data_arr[4];
data_arr[0] = up|flags|BACKLIGHT|PIN_EN;
data_arr[1] = up|flags|BACKLIGHT;
data_arr[2] = lo|flags|BACKLIGHT|PIN_EN;
data_arr[3] = lo|flags|BACKLIGHT;
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, data_arr, sizeof(data_arr), HAL_MAX_DELAY);
HAL_Delay(LCD_DELAY_MS);
}
لنأخذ الأمر بالترتيب. في البداية ، هناك متغيرات تخزن عنوان العرض ، وبتات من الإعدادات التي يجب إرسالها في كل مرة مع البيانات. في وظيفة الإرسال ، نتحقق أولاً من وجود وحدة نمطية في العنوان المسجل. في حالة تلقي رسالة HAL_OK ، نبدأ في تشكيل وحدات بايت للإرسال. في بداية البايت الذي سنرسله ، من الضروري التقسيم إلى قسمين ، وكتابة كلاهما إلى البتات العالية. لنفترض أننا نريد أن تعرض الشاشة الأحرف ، في النظام الثنائي يكون 1110011 ( الآلة الحاسبة) باستخدام العملية المنطقية ونكتب = 01110000 في المتغير ، أي اكتب أهم القطع فقط. يتم نقل البتات ذات الترتيب المنخفض إلى اليسار بأربعة أحرف في البداية ، ثم تتم كتابتها في المتغير lo = 00110000. بعد ذلك ، نقوم بتشكيل صفيف من 4 بايت يحتوي على معلومات حول الحرف الذي سيتم عرضه. الآن نقوم بتعيين بتات التكوين (0-3 بت) للبايتات الحالية. بعد ذلك ، نرسل بايت العنوان و 4 بايت من المعلومات إلى الشاشة باستخدام وظيفة HAL_I2C_Master_Transmit () ؛ولكن لا تتسرع في تنزيل البرنامج ، لأنه في البداية تحتاج إلى ضبط إعدادات العرض. يحتوي الموقع على جدول مترجم ممتاز مع أوامر لتخصيص العرض. بعد التحقق من ذلك مع الوثائق ، جئت إلى الإعدادات التالية المثلى لنفسي: I2C_send(0b00110000,0);
I2C_send(0b00000010,0);
I2C_send(0b00001100,0);
I2C_send(0b00000001,0);
نضع هذه الأوامر قبل بداية حلقة لا نهائية ، بحيث يتم إرسال الإعدادات مرة واحدة قبل بدء العمل (مثل إعداد الفراغ في arduinki). تتطلب وظيفة I2C_send ، بالإضافة إلى البايت ، تحديد ما إذا كان سيتم إرسال إعدادات العرض أو البيانات. إذا كانت الوسيطة الثانية للدالة هي 0 ، فإن الإعدادات ، وإذا كانت 1 ، فإن البيانات.واللمسة الأخيرة - أنت بحاجة إلى وظيفة ترسل موعدًا نهائيًا حرفًا تلو الآخر. كل شيء بسيط جدًا هنا:void LCD_SendString(char *str)
{
while(*str)
{
I2C_send((uint8_t)(*str), 1);
str++;
}
}
بعد جمع كل هذه الوظائف معًا ، يمكنك كتابة: LCD_SendString(" Hello");
I2C_send(0b11000000,0);
LCD_SendString(" Habr");
حسنًا ، اكتشفنا شاشة 1602 ، الآن 2004. الفرق بينهما ضئيل ، حتى هذا الرمز سيعمل بشكل جيد. كل الفرق يأتي لتنظيم عناوين الخلايا على الشاشة. في كلا الشاشتين ، تحتوي الذاكرة على 80 خلية ، في شاشة 1602 ، تكون أول 16 خلية مسؤولة عن السطر الأول ، والخلايا 40 إلى 56 مسؤولة عن السطر الثاني. لا يتم عرض خلايا الذاكرة المتبقية ، لذلك إذا أرسلت 17 حرفًا إلى الشاشة ، فلن يتم نقل آخر خلية على السطر الثاني ، وسيتم تسجيله في خلية ذاكرة لا تحتوي على إخراج للعرض. بشكل أوضح قليلاً ، يتم تنظيم الذاكرة على النحو التالي:
لاستخدام تغذية الخط ، استخدمت الأمر I2C_send (0b11000000،0) ؛ ، يذهب فقط إلى 40 خلية. عرض 2004 أكثر إثارة للاهتمام.الصف الأول هو الخلايا من 1 إلى 20والصف الثاني هو الخلايا من 40 إلى 60الصف الثالث - الخلايا من 21 إلى 40الصف الرابع - الخلايا من 60 إلى 80 ،أي إذا قمت بإرسال أمرLCD_SendString("___________________1___________________2___________________3___________________4");
نحصل على ما يلي:
لتنظيم الانتقالات بين الخطوط ، يجب عليك تحريك المؤشر يدويًا إلى خلية الذاكرة المطلوبة ، أو يمكنك استكمال الوظيفة برمجيًا. لقد استقرت حتى الآن على النسخة اليدوية: I2C_send(0b10000000,0);
LCD_SendString(" Hello Habr");
I2C_send(0b11000000,0);
LCD_SendString(" STM32 + LCD 1602");
I2C_send(0b10010100,0);
LCD_SendString(" +LCD 2004A");
I2C_send(0b11010100,0);
LCD_SendString(" library HAL");
النتيجة:
من المحتمل أن يكون كل هذا مع هذه الشاشات ، روابط مفيدة تمكنت بفضلها من اكتشافها بالكامل:- بدا الرمز كثيرًا هنا
- تبدو جداول تكوين العرض هنا
- تم النظر في الإجراء هنا
البرنامج وورقة البياناتPS: لا تنس ضبط سطوع الشاشة مسبقًا.