آموزش آردوینو مدارهای الکترونیک

آموزش ارتباط سریال UART در بردهای آردوینو – بخش چهارم

آموزش ارتباط سریال Arduino
نوشته شده توسط علیرضا قربانی

در جلسه چهارم از آموزش ارتباط سریال در آردوینو قصد داریم به توضیح توابع find  , peek  ,flush  بپردازیم. در ادامه با مرجع تخصصی آردوینو به زبان فارسی، دیجی اسپارک همراه باشید.

 


ساختار find در ارتباط سریال UART


ساختار Serial.find در ارتباط سریال UART برای جستجوی الگوی خاصی در داده‌های دریافتی از پورت سریال استفاده می‌شود. این ساختار در برنامه‌ها و کتابخانه‌های برنامه نویسی مختلف برای کار با ارتباط سریال موجود است.در بسیاری از زبان‌ها و کتابخانه‌ها، مانند Arduino، ساختار Serial.find به عنوان یک روش جستجوی الگو در داده‌های دریافتی مورد استفاده قرار می‌گیرد. الگوی مورد جستجو می‌تواند یک رشته یا یک سری بایت‌ها باشد.ساختار Serial.find به صورت زیر تعریف می‌شود:

  • int Serial.find(const char *target);

در این حالت، target پارامتر الگویی است که می‌خواهید در داده‌های دریافتی جستجو کنید. برای مثال، اگر می‌خواهید بررسی کنید که آیا عبارت “Hello” در داده‌های دریافتی وجود دارد یا خیر، می‌توانید از متد Serial.find(“Hello”) استفاده کنید. این متد مقدار صفر را برمی‌گرداند اگر الگوی مورد جستجو یافت نشود، در غیر این صورت موقعیت اولین رخداد الگو را برمی‌گرداند.علاوه بر این، ممکن است برخی از زبان‌ها و کتابخانه‌ها دارای پارامترهای دیگری برای تنظیمات مربوط به جستجو باشند. برای مثال، برخی از نمونه‌های ساختار Serial.find در Arduino به صورت زیر استفاده می‌شوند:

  • int Serial.find(const char *target, size_t length);
  • int Serial.find(const uint8_t *target, size_t length);

در این حالت‌ها، target پارامتر الگو است و length طول الگو را مشخص می‌کند. این امکان وجود دارد که الگوی مورد جستجو بین مجموعه‌ای از بایت‌ها قرار گیرد.

 

find()

ساختار تابع به صورت زیر می‌باشد:

Serial.find(target);

 

عمل کرد تابع این گونه می‌باشد که به جای target یک رشته به تابع می‌دهیم ، برنامه شروع به خواندن بافر سریال میکند و در بافر عبارت مورد نظر را جست و جو می‌کند و در صورت پیدا کردن عبارت مورد نظر ، مقدار true را برمی‌گرداند. مثال:

bool find = false;
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  // در صورت وجود دیتا در بافر سریال  وارد شرط زیر می شود
  if ( Serial.available () > 0)
  {
    //عبارت digispark را در بافر جست و جو می کند
    find = Serial.find( "digispark" ) ;
    //در صورت یافتن عبارت ، بر روی سریاالل مانیتور پیامی را نمایش می دهد
    if (find)
    {
      Serial.println( "digispark find" );
    }
  }
}

 


ساختار ()peek در ارتباط سریال UART


ساختار Serial.peek() در ارتباط سریال UART برای خواندن بایت بعدی در صف دریافتی بدون حذف آن از صف استفاده می‌شود. این ساختار به صورت واحد در برنامه‌ها و کتابخانه‌های برنامه نویسی مختلف برای کار با ارتباط سریال موجود است.در بسیاری از زبان‌ها و کتابخانه‌ها، مانند Arduino، ساختار Serial.peek() به عنوان یک روش برای نگاه کردن به بایت بعدی در صف دریافتی مورد استفاده قرار می‌گیرد. این ساختار به صورت زیر تعریف می‌شود:

  • int Serial.peek();

این ساختار، بایت بعدی در صف دریافتی را برمی‌گرداند، اما بایت مورد نظر را از صف حذف نمی‌کند. با استفاده از این ساختار، می‌توانید بدون تغییر در صف دریافتی، بایت بعدی را بخوانید و بررسی‌های لازم را انجام دهید.مقدار برگشتی ساختار Serial.peek() عدد صحیحی است که نشان دهنده بایت بعدی در صف است. اگر صف خالی باشد، مقدار -۱ برگشت داده می‌شود. در غیر این صورت، مقدار بایت بعدی را بین ۰ و ۲۵۵ برمی‌گرداند.نکته مهمی که باید در نظر داشته باشید این است که استفاده از Serial.peek() تنها برای نگاه کردن به بایت بعدی در صف دریافتی است و هنوز آن را از صف حذف نمی‌کند. بنابراین، برای خواندن و حذف بایت از صف دریافتی، باید از ساختار Serial.read() استفاده کنید.

 

peek()

این تابع عمل کردی تقریبا مشابه تابع read  دارد با این تفاوت که تابع read پس از خواندن داده از بافر سریال ، آن داده را از بافر حذف می‌کرد . ولی تابع peek پس از خواندن دیتا از بافر سریال ، دیتا را دست نخورده در بافر باقی می‌گذارد. به مثال زیر توجه کنید.

void setup ()
  {
  Serial.begin (9600);
  }

void loop ()
  {
  if ( Serial.available () > 0 )
    {
    Serial.println (Serial.read ());
    Serial.println (Serial.read ());
    }
  }

 

برای انجام مثال فوق ابتدا یک کاراکتر توسط سریال مانیتور نرم افزار بر روی پورت سریال ارسال میکنیم . در خط شماره ۱۰ ، دیتا توسط تابع read خوانده و سپس از بافر سریال حذف می‌شود . بعد از آن نوبت به عملیات خط شماره ۱۱ می‌رسد  ،در بافر دیتایی وجود ندارد پس تابع read که در خط شماره ۱۱ به کار برده شده است  مقدار -۱ را برمی‌گرداند. همانطور که در آموزش های قبلی ارتباط سریال آردوینو گفته شد ، در صورتی که تابع read ، دیتایی دریافت نکند ، مقدار منفی ۱ را برمی‌گرداند. حال مثال را با استفاده از تابع peek بیان می‌کنیم :

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop()
{
  // put your main code here, to run repeatedly:

  if (Serial.available() > 0)
  {

    Serial.println(Serial.peek());
    Serial.println(Serial.read());
  }
}

 

در این مثال نیز یک  کاراکتر توسط سریال مانیتور نرم افزار به پورت سریال ارسال می‌کنیم. در خط شماره ۱۰ دیتا توسط تابع peek خوانده می‌شود ولی برخلاف مثال قبل این بار دیتا از بافر سریال حذف نمی‌شود . پس از آن نوبت به خط شماره ۱۱ می‌رسد . این بار به این علت که قبل از آن از تابع peek استفاده شده است و دیتا از بافر پاک نشده ، دیتا را خوانده و بر روی سریال مانیتور نرم افزار نمایش می‌دهد.

 


ساختار ()flush در ارتباط سریال UART


ساختار Serial.flush() در ارتباط سریال UART برای پاک کردن صف ارسال و دریافت استفاده می‌شود. این ساختار به صورت واحد در برنامه‌ها و کتابخانه‌های برنامه نویسی مختلف برای کار با ارتباط سریال موجود است.در بسیاری از زبان‌ها و کتابخانه‌ها، مانند Arduino، ساختار Serial.flush() به عنوان یک روش برای پاک کردن صف ارسال و دریافت استفاده می‌شود. این ساختار به صورت زیر تعریف می‌شود:

  • void Serial.flush();

وظیفه ساختار Serial.flush() این است که صف ارسال و دریافت را پاک کند و اطمینان حاصل کند که تمام داده‌های در صف ارسال شده‌اند و هیچ داده‌ای در صف دریافت باقی نمانده است. عمل flush می‌تواند مفید باشد زمانی که می‌خواهید اطمینان حاصل کنید که تمام داده‌های دریافت شده خوانده شده‌اند و صف آماده برای دریافت داده جدید است.استفاده از Serial.flush() معمولاً پس از ارسال دستورات یا داده‌های مهم از طریق ارتباط سریال توصیه می‌شود. این کار مطمئن می‌شود که داده‌های ارسالی به درستی به مقصد رسیده‌اند و سیستم آماده دریافت داده جدید است.

 

flush()

در کتابخانه سریال نرم افزار آردینو  تابعی به نام flush قرار دارد که کمی کاربران را در استفاده از آن گمراه می‌کند و در تمام آموزش ها هم (حداقل آموزش های زبان فارسی) این تابع را به صورت کاملا اشتباه توضیح داده اند. و این آموزش تنها آموزشی است که به تشریح درست این تابع میپردازد. در ورژن‌های ۱٫۰ به قبل نرم افزار آردوینو ، این تابع وظیقه‌ی پاک کردن بافرگیرنده سریال را داشت. یعنی هرگاه از این تابع استفاده میشد تمام اطلاعات بافر گیرنده به طور کامل حذف میشد. ولی بعد از ورژن ۱٫۰ نرم افزار آردوینو، ماهیت این تابع کاملا تغییر یافت. در حال حاضر هنگامی که از این تابع استفاده می‌شود ، برنامه متوقف شده تا تمام دیتا را از بافر فرستنده ارسال کند. توجه کنید گفته شد اطلاعات را ارسال کند نه دریافت. برای درک بهتر این تابع دو مثال بیان میکنیم   و زمان ارسال کامل اطلاعات در هنگام استفاده از این تابع و عدم استفاده از این تابع را به کمک تابع millis  محاسبه می‌کنیم. مثال اول قطعه کدی است که در آن از تابع flush استفاده نشده است  و قصد داریم یک سری اطلاعات را به سریال مانیتور ارسال کنیم و زمان ارسال آن را اندازه گیری نماییم :

void setup() {
  // Give some time to open serial monitor after programming
  delay(2500);
  Serial.begin(9600);

  // Store the current time (start the clock)
  unsigned long millisNoFlushStart = millis();
  // Transmit some psuedo-random text
  Serial.println(F("How long will it take to transmit the following:"));
  Serial.println(F("abcdefghijklmnopqrstuvwxyz"));
  Serial.println(F("done"));
  // Store the current time (stop the clock)
  unsigned long millisNoFlushStop = millis();
  Serial.print(F("Without flush, Serial.println()s return control in: "));
  Serial.print( millisNoFlushStop - millisNoFlushStart);
  Serial.println(F(" milliseconds."));
}
void loop() {}

 

پس از کامپایل این کد ، مشاهده می‌کنید که اطلاعات در زمان حدود ۲۰ میلی ثانیه ارسال شده‌اند. مثال دوم  قطعه کدی است که در آن از تابع flush استفاده شده است و قصد داریم یک سری اطلاعات را به سریال مانیتور ارسال کنیم و زمان ارسال آن را اندازه گیری نماییم:

void setup() {
  // Give some time to open serial monitor after programming
  delay(2500);
  Serial.begin(9600);

  // Store the current time (start the clock)
  unsigned long millisWithFlushStart = millis();
  // Transmit the same psuedo-random text as before
  Serial.println(F("How long will it take to transmit the following:"));
  Serial.println(F("abcdefghijklmnopqrstuvwxyz"));
  Serial.println(F("done"));
  // This time, let TX buffer flush before "stopping" the clock
  Serial.flush();
  // Store the current time (stop the clock)
  unsigned long millisWithFlushStop = millis();

  // Print results for flushed calls
  Serial.print(F("WITH flush, Serial.println()s return control in: "));
  Serial.print( millisWithFlushStop - millisWithFlushStart);
  Serial.println(F(" milliseconds."));
}

void loop() {
}

 

پس از کامپایل کد مشاهده می‌کنید که زمان ارسال اطلاعات به حدود ۸۰ میلی ثانیه رسیده است.یعنی حدود ۴ برابر بیشتر از زمانی شده است که از تابع flush استفاده نشده بود. پس همانطور که از مثال ها پیداست تابع flush باعث مکث در برنامه می‌شود و زمانی که قصد داریم که ابتدا اطلاعات به طور کامل ارسال شود و بعد از آن برنامه را ادامه دهیم باید از این تابع استفاده کرد. این قسمت از آموزش را به پایان می‌رسانیم  و در قسمت بعد مبحث ارتباط سریال آردوینو را تکمیل می‌کنیم.

 

چنانچه در مراحل راه اندازی و انجام این پروژه با مشکل مواجه شدید، بدون هیچ نگرانی در انتهای همین پست، به صورت ثبت نظر سوالتان را مطرح کنید. من در سریع‌ترین زمان ممکن پاسخ رفع مشکل شما را خواهم داد. همچنین اگر ایرادی در کدها و یا مراحل اجرایی وجود دارند می‌توانید از همین طریق اطلاع رسانی کنید.

 

درباره نویسنده

علیرضا قربانی

علیرضا قربانی هستم دانشجوی مهندسی پزشکی. چیزهای خیلی زیادی رو در دنیا دوست دارم. یک گیک هستم و یشه گفت یک هکر. نه به معنی دزدی پسورد و ایمیل مردم. به معنی عشق به دونستن و عشق به تحقیق درباره خیلی از چیزهایی که می بینم و می‌شنوم. اگر بخوام علاقه‌مندی‌ درجه یکم رو نام ببرم میتونم بگم برنامه نویسی و بعد از اون امبدد سیستم ها.

تبادل نظر و رفع عیب با ثبت دیدگاه

۱۸ دیدگاه

  • انصافا تابع flush رو عالی توضیح دادین

    من خیلی وقت بود میخواسم بدونم کارش چیه ولی نه خود سایت آردوینو نه سایتای دیگه هیچکدوم این چیزا را دربارش نگفتن

    خیلی مرسی

  • سلام بر شما
    مقدار millis را در یک متغییر ذخیره کرده و روی سریال مانیتور نمایش میدم.
    هر بار سریال مانیتور را باز میکنم، از صفر شروع میشه. بدون ریست کردن یا دانلود کد روی برد!!!
    void setup() {
    Serial.begin(9600);

    }

    void loop() {
    unsigned long Time=millis();
    Serial.println(Time);
    }
    اگه ممکنه توضیح دهید
    ممنون

    • درود
      توسط تابع millis زمان شرو برنامه بر حسب میلی ثانیه نمایش داده می‌شود. با اتصال برد آردوینو و اجرای این دستور شروع به شمارش خواهد کرد.

  • با عرض سلام
    خیلی ممنونم بابت اموزش های عالیتون
    برای بنده یک سوالی بوجود اومده و هیچ جا نتونستم پیداش کنم جوابشو ممنون میشم بفرمایین
    در دستور if ( Serial.available () > 0 ) این کد > 0 برای چی هست کلا!؟

    • با سلام
      از تابع available جهت خواندن دیتا از سریال پورت استفاده می‌شود که یک عدد بایت است. به عنوان مثال با فراخوانی این تابع با ارسال عدد در سریال مانیتور یک عدد نمایش داده می‌شود.

  • سلام من serial.read bytes(s,10);
    میکنم
    بعدش
    If (strstr(s,”password”)
    Serial.println(“yes”);
    اما کار نمیکنه
    موقعی هم که کاراکتر کاراکتر چاپ میکنم یه کاراکتر با کد اسکی ۱۰ اخر اون جملم هستش
    .
    نمیدونم مشکل چیه
    .
    در ضمن while(serial.available())
    میکنم برای خواندن اعداد صحیح در اخر یه عدد ۰ هم بعد از عدد خودم چاپ میشه
    باید all bailable()>1 میکنم درست میشه
    .
    مشکل چیه

  • سلام
    ممنوناز توضیحاتتون
    الان اگر بخواهیم اطلاعات بافر رو پاک کنیم باید از چه روشی استفاده کنیم ؟