مدتها بود میخواستم یک پشتهٔآ TCP/IP خوب برای AVR پیدا کنم و کار کردن با شبکه رو راحت!
قبلا چند تجربه یا کتابخونهٔ Tuxgraphic داشتم.. خوب بود، ولی راستش خیلی بهم ریخته کدزنی شده بود و براحتی نمیشد باهاش کنار اومد. بخاطر همین رفتم سراغ یک پشتهٔ دیگه، بنام uIP (خونده میشه Micro IP)
توابعی که در اختیار برنامهآنویس قرار میده خیلی شسته رفته و قابل درک هست و براحتی و بدون درد میشه باهاش برنامهآهای تمیز و بزرگی نوشت (:
میکروکنترلر انتخابیم ATmega128 و کنترلر شبکه ENC28J60 بود. نکته جالب قضیه این هست که uIP برای معماری پردازندهآها و همچنین کنترلرهای شبکه مختلفی قابل استفاده هست و یک پورت عالی از ورژن 0.9 این کتابخونه برای AVR و ENC28J60 وجود داره. (لینکهای دانلود در انتهای پست آورده شده)
پس برد رو درست کردم و افتادم به ور رفتن با این کتابخونه.
تست اول این بود که برد ساخته شده به Ping جواب میده؟ که براحتی یا چند خط کد ساده که در مثالآهای کتابخونه بود این قابلیت بدست اومد. کد رو در زیر میارم و شروع میکنم به توضیح قسمتهای اصلیش :
کد PHP:
#include "global.h
#include "uip.h"
#include "nic.h"
#include "uip_arp.h"
#include "compiler.h"
#define BUF ((struct uip_eth_hdr *)&uip_buf[0])
/*****************************************************************************
* Periodic Timout Functions and variables
*
* The periodic timeout rate can be changed depeding on your application
* Modify these functions and variables based on your AVR device and clock
* rate
* The current setup will interrupt every 256 timer ticks when the timer
* counter overflows. timerCounter must count until 0.5 seconds have
* alapsed
*****************************************************************************/
#define TIMER_PRESCALE 1024
#define TIMERCOUNTER_PERIODIC_TIMEOUT 15
static unsigned char timerCounter;
void initTimer(void){
TCCR2=0x05;
TCNT2=0x00;
OCR2=0x00;
TIMSK |= _BV(TOIE2);
timerCounter = 0;
}
ISR(TIMER2_OVF_vect){
timerCounter++;
}
/*****************************************************************************
* Main Control Loop
*****************************************************************************/
int main(void){
unsigned char i;
unsigned char arptimer=0;
// init NIC device driver
nic_init();
// init uIP
uip_init();
// init ARP cache
uip_arp_init();
// init periodic timer
initTimer();
sei();
while(1){
// look for a packet
uip_len = nic_poll();
if(uip_len == 0){
// if timed out, call periodic function for each connection
if(timerCounter > TIMERCOUNTER_PERIODIC_TIMEOUT){
timerCounter = 0;
for(i = 0; i < UIP_CONNS; i++){
uip_periodic(i);
// transmit a packet, if one is ready
if(uip_len > 0){
uip_arp_out();
nic_send();
}
}
/* Call the ARP timer function every 10 seconds. */
if(++arptimer == 20)
{
uip_arp_timer();
arptimer = 0;
}
}
}else{ // packet received
// process an IP packet
if(BUF->type == htons(UIP_ETHTYPE_IP)){
// add the source to the ARP cache
// also correctly set the ethernet packet length before processing
uip_arp_ipin();
uip_input();
// transmit a packet, if one is ready
if(uip_len > 0){
uip_arp_out();
nic_send();
}
}else if(BUF->type == htons(UIP_ETHTYPE_ARP)){ // process an ARP packet
uip_arp_arpin();
// transmit a packet, if one is ready
if(uip_len > 0)
nic_send();
}
}
}
return 1;
}
در خطوط اول برنامه که یکسری فایل هیدر مربوط به کتابخونهٔ uip الحاق شده.
نکته مهمی که در این کتابخونه وجود داره این هست که باید تابعی بنام uip_periodic هر نیم ثانیه و تابع uip_arp_timer هر ۱۰ ثانیه اجرا بشن. بخاطر همین نیاز هست که در پردازنده یک سیستم زمانآبندی راهآاندازی بشه. در AVR میشه اینکار رو خیلی تمیز و راحت با یک تایمر و وقفه سرریز ایجاد کرد. در این مثال من از تایمر شماره ۲ در Mega128 که ۸ بیت هست استفاده کردم. Prescale روی ۱۰۲۴ در فرکانس 8MHz تنظیم هست،پس هر ۱۲۸ میکروثانیه یک تیک داریم و هر ۲۵۶ تیک تایمر معادل تقریبا ۳۳ میلیآثانیه میشه. پس تقریبا هر ۱۵ باری که تایمر ۲ سرریز داشته باشد، زمانی معادل ۴۹۰ میلیآثانیه طی شده است.
عدد ۱۵ را در یک Define بنام TIMERCOUNTER_PERIODIC_TIMEOUT در برنامه تعریف کردهآایم.
پس از نوشتن سیستم زمانآبندی، سراغ تابع main میرویم. در ابتدای تابع راهآاندازیآهای اولیه کتابخانه انجام میشه.این راهآاندازیآها از nic_init برای پایینآترین سطح کتابخانه (راهآاندازی کنترلر ENC28J60) شروع میشود و بعد از آن راهآاندازی اصلی کتابخانه بنام uip_init را داریم. سپس سراغ uip_arp_init میرویم که راهآاندازی مربوط به جداول ARP است. در ادامه سیستم زمانآبندی توسط تابع initTimer که خودمان با استفاده از تایمر نوشتیم راهآاندازی میآشود و وقفهآهای کلی توسط sei فعال خواهد شد.
بعد از تمام این مقدمات، سراغ حلقه اصلی برنامه میریم که کل هیچ وقت نباید پایان داشته باشه.
خب، در این حلقه ابتدا توسط تابع nic_poll بررسی میشه که آیا فعالیت جدیدی در کنترلر شبکه داریم یا خیر. اگر خروجی این تایع 0 باشد، یعنی فعلا خبری از فعالیت شبکه نیست و میتونیم با خیال راحت کارهای دیگری انجام بدیم. این کارهای دیگه، میتونه بررسی تعداد سرریزهای تایمر باشه که اگر به عدد ۱۵ رسید که معادل با ۵۰۰ میلیآثانیه هست، تابع uip_periodic فراخونده بشه. این تابع در اصل وضعیت اتصالات فعلی رو چک میکنه و مثلا اگر در جایی از برنامه یک کانکشن جدید ایجاد شده باشه، به محض فراخوانی این تابع، برنامه از این اتصال جدید گاه میشه. یا مثلا اگر به یک اتصال قدیمی دادهآهای فرستاده شده باشه (که در حافظه موقت ذخیره میشن)، این اطلاعات توسط تابع nic_send به کنترلر ارسال و از اون روی شبکه فرستاده بشن.
همچنین تعداد سرریزهای اگر به ۲۰ رسید، به معنی طی شدن زمان ۱۰ ثانیه هست. همونطور که گفته شد، تابع uip_arp_timer باید هر ۱۰ ثانیه فراخوانی بشه تا جدول ARP مجددا مرتب بشه. (لطفا نپرسید ARP چیه، در اینترنت به اندازه کافی در موردش اطلاعات هست)
حالا اگر خروجی nic_poll برابر با صفر نبود چی ؟
خب این به معنی این هست که فعالیت جدیدی در کنترلر شبکه بوجود اومده و یکسری داده از شبکه دریافت شده. قرار نیست ما بدونیم این دادهآها چطوری هستن و بایت به بایت چه معنیآای دارن. این کارها رو خود uIP انجام میده. فقط مهمه که بدونیم دادهآها از چه نوعی هستن تا بر طبق اون توابع درستی از uIP رو فراخوانی کنیم تا خودش کارهارو درست کنه (:
ما میتونیم توسط المنت type در یک ساختار که برای راحتی بیشتر اسم این ساختار BUF گزاشته شده، متوجه بشیم که اطلاعات جدیدی که در از کنترلر شبکه دریافت شده، از چه جنس هستن. یه نوع بسته مختلف داریم که در فایلآهای هیدر کتابخونه برای هر کدومش یک Define وجود داره :
FILE: uip_arp.h
کد PHP:
#define UIP_ETHTYPE_ARP 0x0806
#define UIP_ETHTYPE_IP 0x0800
#define UIP_ETHTYPE_IP6 0x86dd
هر کدوم اینها در این قسمت کد بررسی میشن و بر طبق نوع بسته، توابع خاصی بترتیب فراخوانی میشن.
خب، با توشتن این کدها در اصل برنامه شما قادر هست یک ارتباط پایهآای با شبکه رو برقرار کنه و در اصل بستر برای ارتباطات پیچیدهآتر بوجود اومده. شما حالا میتونید بردتون رو از شبکه Ping کنید و ازش جواب بگیرید.
ولی یک سوال مهم: IP دستگاه چیست ؟!
به این سوال در پست بعدی جواب میدم ... پس به سبک این برنامهآهای تلویزیونی یک تبلیغ واسه پست بعدی برم (:
در پست بعدی یاد خواهیم گرفت :
* چطور تنظیمات IP دستگاه خود را انجام دهیم (IP دستگاه، netmask و gateway)
* چطور یک سرور ساده و با ادب بسازیم که به هر اتصال جدید متن Welcome رو بفرسته (:
لینکآها :
## دانلود کتابخونه از اینجا
## دانلود مرجع کامل uIP از اینجا
دیدگاه