توجهت
الأنظار مؤخرا إلى شبكات Ethernet بشكل كبير من قبل مصممي أنظمة التحكم و
الأتمتة ، وذلك بسبب انتشارها على نطاق واسع ، فلا يكاد يخلو بناء منها
(بالطبع في الدول المتقدمة) ، و استخدامها في هذه التطبيقات سيغني عن إنشاء
شبكات كهربائية لأنظمة التحكم ..
سنتعلم في هذه الجولة طريقة ربط المتحكم PIC18F252 مع شبكة محلية LAN ،
كما سنتعرف على كيفية استخدام أساليب التخاطب المختلفة UDP و TCP و غيرها
لاستخدامها في تصميم الأنظمة الخاصة بنا ، أو تطوير الأنظمة الحالية ...
المسألة ليس فيها سوى سر واحد هو اختيار متحكم Ethernet مناسب ، لأن
هذا المتحكم هو المسؤول عن استلام رزم المعطيات من الشبكة وتسليمها بشكلها
الرقمي المجرد للمتحكم الصغري و بالعكس من المتحكم إلى الشبكة ، لذا فهو
المسؤول عن سرعة الاتصال وبالتالي على تصميم المشروع بشكل عام .
في الحقيقة توجد الكثير من هذه المتحكمات ( أرجو التفريق بين متحكم
Ethernet و المتحكم الذي يدير النظام PIC18F252 ) سأتحدث عن اثنين منهما
جربتهما ثم فضلت أحدهم على الآخر وهما : المتحكم RTL8019AS من شركة Realtek
و المتحكم ENC28J60 من شركة
Microchip .
لن أخوض في المقارنة الفنية بين المتحكمين ، فقراءة الصفحات الأولى من
النشرة الفنية لكل منهما ستعطي صورة واضحة عن ميزاتهما ، و بما أن
المتحكمين يعملان بسرعة 10Mbps ، فالمقارنة ليست مجدية كثيرا ، وما سأفعله
هو إجراء مقارنة تجريبية لما واجهته أثناء العمل على كل منهما من صعوبات و
ما دعاني لتفضيل أحدهما على الآخر ، فلنبدأ ..
بدأت مشوار التخاطب مع الشبكات مع المتحكم RTL8019AS لأنه كان الوحيد
الذي استطعت الحصول عليه ، فصممت دارة مطبوعة ونفذتها و أنجزت الاتصال مع
المتحكم PIC16F877A ، لكنني واجهت صعوبات في الحصول على مكتبة للتخاطب مع
هذا المتحكم ، فالمكتبة الموجودة في المترجم mikroC لم تعمل إطلاقا ،
وتأكدت من ذلك من خلال منتدى الدعم الفني للشركة فانتقلت للمترجم C18 و
استخدمت TCP/IP Stack الذي توفره
Microchip
بشكل مجاني ، فنجح العمل لكن ذاكرة البرنامج ذهب نصفها لإنجاز روتين اتصال
عبر UDP فقط ، والقشة التي قصمت ظهر البعير بعد ذلك هي عدم توفر أقطاب
كافية لإنجاز أي مشروع حقيقي لأن المتحكم RTL8019AS يحتاج إلى 16 قطب على
الأقل ليعمل (8 للبيانات و 5 للعنونة و 3 للتحكم) فأوقفت العمل بعد هذه
الصعوبات بالرغم من نجاح التجربة ، و هذه بعض الصور من بقايا هذه التجربة
الدامية :
تمكنت بعد ذلك من الحصول على المتحكم ENC28J60 بعد عناء طويل ، فأعدت
الكرة و رسمت دارة مطبوعة وأنجزت العمل مرة أخرى لكن مع المتحكم PIC18F252
الذي يتمتع بذاكرة كبيرة نسبيا ، وجربت جميع البروتوكولات المتاحة UDP و
TCP و ARP و ICMP ، وجربت الـ Web Server ، و تم العمل بشكل جيد هذه المرة
دون صعوبات تذكر ، و إليكم بعض الصور :
قبل أن أشرح عملية البرمجة ، أرجو ممن يقرأ هذه المقالة أن يشاهد المخطط
النظري الموجود بالأعلى ، و هذه بعض الملاحظات التي قد توضح عمل الدارة
بشكل أكبر :
- الدارة المتكاملة ENC28J60 : هي محور هذه المقالة ، و هي عبارة عن
متحكم Ethernet ، و هي بالمناسبة أصغر متحكم Ethernet في العالم حتى تاريخ
كتابة هذه السطور ، يتم التخاطب مع هذه المتكاملة تسلسليا عبر مسرى SPI ، وهي تحتاج جهد تغذية مقداره 3.3 فولت و هزاز كريستالي تردده 25MHz ،و مداخلها متوافقة تماما مع سوية TTL المنطقية ( 5 فولت ) .
- J0026D21E : ليست مجرد منفذ RJ-45 كما يلاحظ البعض ، فهي تحتوي إلى
جانب ذلك على مرشحات خاصة للتعامل مع شبكات Ethernet ، و ثمنها يصل لعشرة
أضعاف منفذ RJ-45 العادي !
- الدارة المتكاملة 74HCT244 تعمل على تحويل مستوى الجهد لمخارج المتحكم ENC28J60 من 3.3 فولت إلى 5 فولت المناسب للمتحكم PIC18F252 .
- منظم الجهد LM1117 لتأمين جهد التغذية 3.3 فولت للمتحكم ENC28J60 .
لندخل الآن في صلب الموضوع بعد هذه المقدمة المملة ، قد يتفاجأ البعض
بمدى سهولة كتابة برنامج المتحكم PIC18F252 للتخاطب مع المتحكم ENC28J60 في
mikroC ، و الهدف هذه المقالة هو شرح هذه العملية .
الجزء الأول من البرنامج هو تعريف بعض الثوابت :
- // duplex config flags
- #define Spi_Ethernet_HALFDUPLEX 0x00 // half duplex
- #define Spi_Ethernet_FULLDUPLEX 0x01 // full duplex
- const char httpHeader[] = "HTTP/1.1 200 OK\nContent-type: "; // HTTP header
- const char httpMimeTypeHTML[] = "text/html\n\n"; // HTML MIME type
- const char httpMimeTypeScript[] = "text/plain\n\n"; // TEXT MIME type
هذه الثوابت قد لا يعرفها سوى المختصون بالشبكات ، لذلك أرجو ألا تكون
عقبة لمن ليس مختصا ، و باختصار ، أول تعريفين (Spi_Ethernet_HALFDUPLEX و
Spi_Ethernet_FULLDUPLEX ) يخصان المكتبة ، و الثابت []httpHeader يحتوي
على ترويسة HTTP اللازمة لعملية توليد الصفحات ، والثابتان التاليان
يستخدمان لإخبار المتصفح عن نوع الملف الذي يستقبله ( html أو text ) .
الجزء التالي هو تعريف الصفحة التي سيتم توليدها من قبل المتحكم ، و هي
مكتوبة بالطبع بلغة HTML ، و من لا يعرف هذه اللغة نهائيا أرجو منه تعلم
أساسياتها ( قد لا تستغرق أكثر من يومين ) قبل إنجاز المشروع لأن العمل على
هذه المشاريع بدون تعلم هذه اللغة كمن يخوض البحر بلا سفينة .
- // default html page
- char indexPage[] =
- "NoorTronics.net\
NoorTronics Webserver
\
- \
- ";
هنا يتم تعريف بارامترات الشبكة : عنوان MAC و عنوان IP للمتحكم وللعبارة و لمزود DNS ، و قناع الشبكة الفرعية .
- // network parameters
- char myMacAddr[6] = {0x00, 0x14, 0xA5, 0x76, 0x19, 0x3f}; // my MAC address
- char myIpAddr[4] = {192, 168, 20, 60}; // my IP address
- char gwIpAddr[4] = {192, 168, 20, 6}; // gateway IP address
- char dnsIpAddr[4] = {192, 168, 20, 1}; // dns IP address
- char ipMask[4] = {255, 255, 255, 0}; // subnet mask
- // end network parameters
- unsigned char getRequest[20]; // HTTP request buffer
صار كل شيء جاهزا الآن ، سنستخدم التابع SPI_Ethernet_UserTCP لمعالجة
رزم TCP ، و هذا التابع سيتم استدعاؤه بشكل آلي من قبل المكتبة عند اللزوم .
الجزء الأول من التابع يتم فيه تجاهل جميع الطلبات الموجهة إلى غير
المنفذ 80 ، ثم تتم قراءة أول 15 محرف من الطلب ، ثم يتم اختبار نوع الطلب
ورفض الجميع ما عدا طلبات GET ، يتم بعد ذلك إجراء عملية التحكم إن كانت
موجودة ( في حالتنا هذه يتم التحكم بالطرف RB0 ) ، و من ثم التعديل على
محتويات الصفحة و ألوانها و يعيد التابع القيمة len التي تعبر عن طول الرد (
عدد محارف الرزمة ) و الذي يحتوي على الصفحة التي ستظهر للمستخدم .
- unsigned int SPI_Ethernet_UserTCP( char *remoteHost, unsigned int remotePort,
- unsigned int localPort, unsigned int reqLength)
- {
- unsigned int len; // my reply length
- if(localPort != 80) return(0); // I listen only to web request on port 80
- // get 10 first bytes only of the request, the rest does not matter here
- for(len = 0 ; len < 15 ; len++) getRequest[len] = SPI_Ethernet_getByte();
- getRequest[len] = 0;
- if(memcmp(getRequest, "GET /", 5)) return(0); // only GET method
- if(!memcmp(getRequest+11, "ON", 2)) // do we have ON command
- PORTB.F0 = 1; // set PORTB bit0
- else
- if(!memcmp(getRequest+11, "OFF", 3)) // do we have OFF command
- PORTB.F0 = 0; // clear PORTB bit0
- if (PORTB.F0)
- {
- memcpy(indexPage+340, "#FFFF00", 6); // highlight (yellow) ON
- memcpy(indexPage+431, "#4974E2", 6); // clear OFF
- }
- else
- {
- memcpy(indexPage+340, "#4974E2", 6); // clear ON
- memcpy(indexPage+431, "#FFFF00", 6); // highlight (yellow) OFF
- }
- len = SPI_Ethernet_putConstString(httpHeader); // HTTP header
- len += SPI_Ethernet_putConstString(httpMimeTypeHTML); // with HTML MIME type
- len += SPI_Ethernet_putString(indexPage); // HTML page first part
- return len; // return to the library with the number of bytes to transmit
- }
هذا التابع مماثل للتابع السابق ، و يتم استدعاؤه عند استقبال رسائل عبر
UDP من قبل المكتبة , لكننا لن نستخدمه الآن ، و كما تلاحظون فإنه يجيب
على جميع رسائل UDP بالقيمة 0 ، أي أنه لا يجيب عمليا .
- unsigned int SPI_Ethernet_UserUDP( char *remoteHost, unsigned int remotePort,
- unsigned int destPort, unsigned int reqLength)
- {
- return 0; // back to the library with the length of the UDP reply
- }
هذا هو جسم البرنامج ، يتم فيه تهيئة المتحكم للعمل المناسب ، و هنا يتم
إطفاء المبدل ADC و المقارنات Comparators و تهيئة الطرف RB0 كمخرج ، ثم
تتم تهيئة منفذ
SPI
و تهيئة الاتصال بالمتحكم ENC28J60 ، ومن ثم إدخال إعدادات الشبكة للمتحكم
ENC28J60 . كل هذ القسم سينفذ مرة واحدة فقط ، ثم سيدخل المتحكم في حلقة
لا نهائية يتم خلالها استدعاء التابع SPI_Ethernet_doPacket الذي تولى
المهمة برمتها .
- void main()
- {
- ADCON1 |= 0x0F ; // no analog inputs
- CMCON |= 0x07 ; // turn off comparators
- PORTB.F0 = 0;
- TRISB.F0 = 0; // set PORTB.B0 as output (rele control pin)
- // starts ENC28J60 with: reset bit on PORTC.F0, CS bit on PORTC.F1,
- // my MAC & IP address, full duplex
- Spi_Init();
- // full duplex, CRC + MAC Unicast + MAC Broadcast filtering
- Spi_Ethernet_Init (&PORTC, 0, &PORTC, 1,
- myMacAddr, myIpAddr, Spi_Ethernet_FULLDUPLEX) ;
- // dhcp will not be used here, so use preconfigured addresses
- SPI_Ethernet_confNetwork(ipMask, gwIpAddr, dnsIpAddr);
- while(1) { // do forever
- SPI_Ethernet_doPacket(); // process incoming Ethernet packets
- }
- }
صار بإمكاننا الآن التحكم بالطرف RB0 عن طريق المتصفح :
أرجو أن تكون هذه المقالة واضحة بما يكفي لفتح الباب أمام من يريد
التعلم ، و أرجو ممن لديه أسئلة أن يطرحها و لايبخل بها علينا ، وتقبل الله
منا ومنكم ..
المصدر
/www.noortronics.net