توصيل لوحة ألعاب مصغرة من Nintendo Classic ب Raspberry pi

العطلات تقترب من نهايتها ، مما يعني أن الوقت قد حان لندم الكبد وتحريك الرأس. لذلك جاءت في ذهني فكرة أخرى. بعد أن قمت بتوصيل لوحة الألعاب من Dendy (إنها عصا التحكم ، إنها وحدة تحكم ، إنها عصا تحكم ، إنها وحدة تحكم في الألعاب ، وما إلى ذلك) geektimes.ru/post/281520 ، فكرت في توصيل الثانية ب Raspberry pi. لم أكن أرغب في شراء الزبالة الثانية بأزرار التشويش ، وبالمناسبة ، قاموا برميها على رفوف Nintendo Classic Mini ، حسنًا ، أثناء رميها للخارج ، ستشتري الفجل. لم يكن الهدف هو شراء محاكي لـ 4K ، لكنني قررت شراء لوحة ألعاب. لحسن الحظ ، تمكنت من شرائه ، كان الأخير في المتجر. أولئك الذين يهتمون بما جاء منه يمكنهم النقر على الزر أدناه.

هنا رابط مباشر للدليل ، إذا لم يتم تفعيل الرابط العادي.

الصورة

لا يخفى على أحد أنه يمكن توصيل لوحة الألعاب هذه بكل من Wii و Wii U ، مما يعني أنه يمكن استجوابها عبر واجهة i2c. ما يبعث على التشجيع هو أن لوحة الألعاب تعمل بقوة 3.3 فولت ، مما يعني أنك لست مضطرًا لأن تهتم بمستويات جهد مطابقة. لربط لوحة الألعاب بـ Raspberry pi ، قررت شراء موصلات wiimote على aliexpress ، والتي يتم لحامها على اللوحة منه. على الرغم من وجود رابطين في الحزمة ، وكانا في فقاعة مشتركة ، إلا أن أحد الموصلات كان في حالة سيئة. لا يمكن أن تفعل القصدير هنا. بعد أن تم لحام كل شيء ، كان من الضروري التحقق مما إذا كان هذا يعمل. لهذا ، قررت استخدام الأدوات المساعدة من حزمة أدوات i2c. كانت الفكرة هي تحديد الجهاز على العنوان - 52. بعد إطلاق i2cdetect ، رأيت الكنز:

i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          03 04 05 06 07 -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- 52 -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

أصبح قلبي أكثر دفئًا على الفور (كما لو ابتسمت فتاة غير مألوفة لك في أحد الشوارع في الربيع) ، لذلك يعمل. بعد ذلك ، كان عليك google حول توصيل الأجهزة الطرفية بـ Wii. لقد وجدت مثالاً لكيفية اتصال Wii nunchak ب Raspberry pi. من هذا المثال ، اكتشفت أنه يجب تهيئة لوحة الألعاب بكتابة صفر إلى تسجيل 0x40 ، ثم قبل القراءة مباشرةً في كل مرة تحتاج فقط إلى كتابة صفر وقراءة أول 6 بايت.

في هذه الحالة ، واجهت أيضًا اختيار مكتبة لـ i2c. نظرًا لأنني لم أنجح في مكتبة bcm2835 ، فقد قررت استخدام المكتبة التي تم استخدامها في المثال - هذه مكتبة WirinPi. كل شيء يعمل معها. تجريبيا ، اكتشفت أن معلومات الزر موجودة في 4 و 5 بايت (إذا قمت بحساب بايت من الصفر). توجد معلومات حول الأزرار المحددة والبدء والأسفل واليمين في البايت الرابع ، والمعلومات حول الأزرار A و B والأعلى واليسار في البايت الخامس. علاوة على ذلك ، فإن المعلومات حول الأزرار المحددة والأسفل واليمنى توجد في البتات الأربعة العالية للبايت ، والمعلومات حول زر البدء في الأسفل. وينطبق الشيء نفسه على البايت الخامس ، حيث تكون المعلومات حول الأزرار A ، B في البتات الأربعة العالية للبايت ، والمعلومات حول الأزرار لأعلى ، في اليسار في الأجزاء السفلية. فيما يلي رموز الأزرار: A - 0xcf، B - 0xbf، up - 0xf0، left - 0xf1، select - 0xcf، start - 0xf3، down - 0xbf، right - 0x7f.إن نتيجة الضغط المشترك على الأزرار ، والمعلومات التي يوجد حولها بايت واحد وفي نفس البتات ، هي حرف "AND" منطقي لرموزهم. على سبيل المثال ، الضغط على الزرين A و B معًا يعطي القيمة 0x8f.

في ما يلي رابط إلى الجدول:

الصورة

بعد ذلك ، كتبت برنامج اختبار صغيرًا لأزرار القراءة ، وسأدرجه أدناه بالكامل وأشرح ما يحدث به:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <errno.h>
char i, a, b, c, d, state;
char bytes[6];
int main(void) {
wiringPiSetup();
int fd = wiringPiI2CSetup(0x52);
wiringPiI2CWriteReg8(fd, 0x40, 0x00);
delayMicroseconds(20);
while(1) 
{
	state = 0;
	wiringPiI2CWrite(fd, 0x00);
        delayMicroseconds(100);
        for (i=0; i<6; i++)
	{
           bytes[i] = wiringPiI2CRead(fd);
        }
	a = bytes[5] >> 4;
	b = bytes[5] << 4;
	c = bytes[4] >> 4;
	d = bytes[4] << 4;
	if (a == 0xc)
	state ^= (1 << 0);
	if (a == 0xb)
	state ^= (1 << 1);
	if (c == 0xc)
	state ^= (1 << 2);
	if (d == 0x30)
	state ^= (1 << 3);
	if (b == 0x00)
	state ^= (1 << 4);
	if (c == 0xb)
	state ^= (1 << 5);
	if (b == 0x10)
	state ^= (1 << 6);
	if (c == 0x7)
	state ^= (1 << 7);
	printf("%x \n", state);
	}
    return 0;
}

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

بعد ذلك ، في النص الرئيسي للبرنامج ، تتم تهيئة مكتبة WiringPi - wiringPiSetup () ؛ ثم يتم تعيين عنوان i2c للجهاز على 0x52. بعد ذلك ، تتم تهيئة وحدة التحكم:


wiringPiI2CWriteReg8(fd, 0x40, 0x00);
delayMicroseconds(20);

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

آخر مرة ، يتم تسجيل تهيئة المكتبة في ملف fceu.c:


int FCEUI_Initialize(void)
{
    if(!FCEU_InitVirtualVideo())
    return 0;
    memset(&FSettings,0,sizeof(FSettings));
    FSettings.UsrFirstSLine[0]=8;
    FSettings.UsrFirstSLine[1]=0;
    FSettings.UsrLastSLine[0]=231;
    FSettings.UsrLastSLine[1]=239;
    FSettings.SoundVolume=100;
    FCEUPPU_Init();
    X6502_Init();
    wiringPiSetup();
    return 1;
}

حسنًا ، وتتعلق جميع التغييرات أيضًا بملف input.c. في البداية ، في دالة int FCEUI_Initialize (باطلة) ، يتم تعيين أوضاع تشغيل منفذ gpio للعمل مع لوحة الألعاب القديمة (من Simba's) ، وتتم تهيئة لوحة الألعاب.


pinMode (0, OUTPUT);
pinMode (2, OUTPUT);
pinMode (3, INPUT);
digitalWrite (0, HIGH);
digitalWrite (2, LOW);
fd = wiringPiI2CSetup(0x52);
wiringPiI2CWriteReg8(fd, 0x40, 0x00);
usleep(20);

في البداية ، تحتاج أيضًا إلى الإعلان عن المتغيرات التي ستحدد الضغط على الزر.

في وظيفة DECLFW (B4016) الثابتة ، يتم توفير ستروب للوحة الألعاب القديمة ، ولا حاجة إلى ستروب جديد:


if (LastStrobe==0)
{
	digitalWrite (0, LOW);
}


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


void FCEU_UpdateInput(void)
{
joy[0] = 0;
joy[1] = 0xff;
wiringPiI2CWrite(fd, 0x00);
usleep(100);
for (i = 0; i <= 7; i++)
	{
	bytes[i] = wiringPiI2CRead(fd);
	joy[1] ^= digitalRead(3) << i;
	digitalWrite (2, HIGH);
	delayMicroseconds (20);
	digitalWrite (2, LOW);
	delayMicroseconds (20);
	}
	a = bytes[5] >> 4;
	b = bytes[5] << 4;
	c = bytes[4] >> 4;
	d = bytes[4] << 4;
	switch (a){
	case 0xc:
	joy[0] ^= (1 << 0);
	break;
	case 0xb:
	joy[0] ^= (1 << 1);
	break;
	case 0x8:
	joy[0] ^= (1 << 0);
	joy[0] ^= (1 << 1);
	break;
	}
	switch (b){
	case 0x00:
	joy[0] ^= (1 << 4);
	break;
	case 0x10:
	joy[0] ^= (1 << 6);
	break;
	case 0x20:
	joy[0] ^= (1 << 4);
	joy[0] ^= (1 << 6);
	break;
	}
	switch (c){
	case 0xc:
	joy[0] ^= (1 << 2);
	break;
	case 0xb:
	joy[0] ^= (1 << 5);
	break;
	case 0x7:
	joy[0] ^= (1 << 7);
	break;
	case 0x8:
	joy[0] ^= (1 << 2);
	joy[0] ^= (1 << 5);
	break;
	case 0x4:
	joy[0] ^= (1 << 2);
	joy[0] ^= (1 << 7);
	break;
	case 0x3:
	joy[0] ^= (1 << 5);
	joy[0] ^= (1 << 7);
	break;
	}
	switch (d){
	case 0x30:
	joy[0] ^= (1 << 3);
	break;
	}
	digitalWrite (0, HIGH);
	}
	

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

بشكل عام ، كل شيء على ما يرام.

PS: على ما أذكر ، غدا في العمل ، مشوهة بالفعل.

PPS لقد نسيت أن أضيف أنه من أجل التجميع الناجح ، في Makefile (الذي تم تشكيله بعد تنفيذ الأمر Configure) ، الموجود في دليل src ، تحتاج إلى إضافة -lwiringPi -lm -lrt إلى المكان حيث تتم كتابة تبعيات المكتبة. الخط:

LIBS =

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


All Articles