AVR, Cute, توصیه شده, مقاله

میکروکنترلر مقصر نیست مقصر برنامه نویسی است

حتما برای شما هم پیش آمده که در پروژه ای به دلایل زیادی از قبیل کم بود حافظه فلش ، کمبود حافظه رم ، سرعت پایین و… مجبور شده اید میکروکنترلر را عوض کنید و زحمت برنامه نویسی مجدد را به جان بخرید در این موارد معمولا تمام کاسه کوزه ها سر میکروکنترلر شکسته می شود و محدودیت های آن مورد سرزنش و شماتت قرار می گیرد! در صورتی که همیشه اینچنین نیست. در اغلب اوقات با تغییرات جزیی در مدار یا سبک برنامه نویسی به راحتی میتوان مشکلات پیش رو حل کرد ؛

متاسفانه خیلی از دوستان و همکاران گرامی به این موضوع اشراف ندارند و به اشتباه میکروکنترلر را مقصر می دانند؛ به عنوان مثال خیلی از دوستان ، میکروکنترلر AVR را یک میکروکنترلر صنعتی نمی دانند ، در عوض میکروکنترلر PIC یا ARM را صنعتی می دانند، برای شخصی که تجربه کافی در خصوص طراحی مدارات دیجیتال و صنعتی داشته باشد ، این استدلال نه تنها بی پایه و اساس است بلکه خنده دار هم خواهد بود؛ محدودیت ها و باید و نباید های هر پردازنده ای در منوال آن ذکر شده است ، با رعایت این نکات و البته طراحی صحیح هر میکروکنترلری را می توان در هر جایی که مورد نیاز باشد استفاد کرد، البته منکر این مهم نیستم که برخی میکروکنترلر ها برای مقاصد خاصی طراحی و تولید می شوند. چه مدارات صنعتی که بر پایه همین میکروکنترلر AVR طراحی شده اند ؛ مهم نکاتی است که باید در طراحی لحاظ کرد. در این مقاله قصد داریم نشان دهیم که چقدر مدل برنامه نویسی می تواند در عملکرد یک میکروکنترلر دخیل باشد و در خیلی از هزینه ها صرفه جویی کنید پس با سیسوگ همراه باشد.

معرفی میکروکنترلر و نحوه آزمایش سبک برنامه نویسی

بورد میکروکنترلر مورد استفاده تست مبتنی بر آردوینو

در این تست از میکروکنترلر ATMEGA328 در فرکانس 16 مگاهرتز (برد آردوینو البته از Gcc برای کامپایل کد استفاده خواهیم کرد نه ابزار آردوینو) استفاده خواهیم کرد و با اتصال یک عدد LCD رنگی به صورت سریال راه اندازی می‌شود قصد داریم سرعت رسم فریم های تصویر را اندازه گیری کنیم. برای این کار مدت زمان رسم ده فریم از تصویر را اندازه گیری می‌کنیم و نمایش می‌دهیم. LCD مذکور از انتقال 9 بیت سریال برای رسم تصویر استفاده می کند و واحد SPI موجود در میکروکنترلر نهایتا در حالت 8 بیتی کار می کند و نمی توانیم از آن استفاده کنیم پس این قسمت از برنامه را  مجبوریم که با کدنویسی پیاده سازی کنیم. در ادامه بررسی خواهیم کرد که مدل های برنامه نویسی چه تاثیری در سرعت اجرای آن خواهند داشت.

برنامه اول ، برنامه‌ای که همه می نویسند

برای نوشتن برنامه ای که 9 بیت را به صورت سریال انتقال دهد یکی از مرسوم ترین راه ها استفاده از حلقه for می باشد. در اولین قدم ما نیز برنامه را به روش رایج می نویسیم:

همانطور که می بینید ابتدا پایه CS را صفر میکنیم ، بعد با استفاده از حلقه For 9 بیت داده را انتقال می دهیم و سپس پایه CS را یک می‌کنیم!

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

سرعت اجرای برنامه اول نوکیا

همانطور که در تصویر مشخص است ، اجرای ده فریم از تصویر (یعنی ده بار پر کردن صفحه) زمانی حدود 20465 میلی ثانیه نیاز داشته است ، به عبارتی هر بار پر کردن صفحه دوثانیه طول می کشد. در این مرحله عده ای نا امید می شوند و تقصیر را به گردن میکروکنترلر می اندازند و سراغ میکروکترلر قوی تری میروند ! اگر دید درستی به سخت افزار و نرم افزار داشته باشیم به سادگی میشه حدس زد که مشکل از کجاست و چطور میشه حلش کرد!

در این مثال بگذارید اول بررسی کنیم ، برای پر کردن صفحه از رنگی خاص چقدر زمان CPU صرف میشه ، صفحه نمایشگر ما 160 در 120 است یعنی 19200 پیکسل باید دیتا دریافت کنند ، برای پر کردن هر پیکسل نیازه که دوبار این تابع قراخوانی بشه ، میشه در واقع 38400 بار باید این تابع رو فراخوانی کنیم ! حالا چطور میتونیم برنامه رو بهینه تر کنیم ، داخل for رو نگاه کنید ! با توجه به این که دستورات درون for با هربار اجرای تابع 9 مرتبه اجرا می شوند ، برای پر کردن صفحه لازمه

345600 بار اجرا شوند. یعنی صرفه جویی در یک سیکل ماشین درون روتین حلقه ، به تعداد 345600  سیکل ما را سریعتر می کند ، با توجه به فرکانس کاری 16 مگ ، هر سیکل صرفه جویی ، معادل 21.6 میلی ثانیه خواهد بود! پس اصلا دست کم نگیریدش ! اما چطور میشه برنامه رو بهینه تر نوشت ؟

برنامه دوم ، برنامه‌ای که بعد از تفکر می نویسید

بعد از کمی فکر کردن در خصوص این که چطور میتونید برنامه رو بهینه کنید ، با مقداری هوش – برنامه رو این چنین خواهید نوشت :

اما چه تغییری کرد برنامه ؟ ، در واقع فرایند مقایسه را بهینه کردیم ، در برنامه قبل عملیات مقایسه به صورت زیر بود

در خط فوق برای هر بیت ، اول 8 را از متغیر i کم می کردیم ، بعد عدد 1 به تعداد حاصل شیفت می دادیم به سمت چپ و بعد با مقدار data اند می کردیم و بر اساس آن خروجی را تنظیم می کردیم ! این فرایند ها را برای پر کردن صفحه 345600  بار تکرار می کردیم ! اگر بتوانید این فرایند را حذف کنیم ، قطعا تعداد سیکل خیلی زیادی صرفه جویی می‌کنیم. اما چطور ؟ خیلی ساده است با تعریف یک جدول مقایسه (lookup table) !

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

برنامه بهینه و سرعت اجرای برنامه دوم

بله ، همین کار ساده سرعت اجرای برنامه را تقریبا 2 برابر افزایش داد ؛ یعنی برای رسم هر فریم نیاز به 1 ثانیه زمان بیشتر نخواهید داشت ! اما آیا باز میشود کد را بهینه تر نوشت ؟

برنامه سوم ، برنامه‌ای که با خلاقیت و شناخت سخت افزار می نویسید

در برنامه قبلی با استفاده از یک تکنیک ساده سرعت اجرای برنامه دو برابر شد ، اما آیا راه دیگری هست که باز هم بشود برنامه را بهینه کرد ؟، اینجاست که دید شما نسبت به سخت افزار و آشنایی با معماری پردازنده به کمک شما میآد ، بیایید نگاهی به کد اسمبلی تولید شده بیندازیم

اسمبلی میکروکنترلر AVR

اگر دقت کنید در خط 624 و 625 دو دستور اسمبلی داریم با نام های LD ، کار این دستور ها لود کردن مقداری است که Z به اون اشاره میکنه (انتخاب اینکدس مورد نظر از آرایه تعریف شده ShiftBit) ، مطابق اونچه داخل دیتاشیت AVR موجوده هر کدام از این دستورات به 2 سیکل ماشین نیاز داره ، دستور brne که هم برای پیاده سازی if استفاده شده هم برای پیاده سازی for ، اجراش نیاز به 1 یا 2(در صورتی که پرش انجام بشه) سیکل ماشین داره ، خود پیاده سازی حلقه هم نیاز به cpc و cpi داره که هر کدوم یک سیکل ماشین رو مصرف می‌کنن !

با توصیفات بالا مشخص میشه که حذف حلقه و انتخاب از ایندکس آرایه خیلی در سیکل های ماشین مصرف شده صرفه جویی خواهد کرد ، پس برنامه را به شکل زیر تغییر می دهیم

بله یا یک روش ساده و حذف حلقه ؛ برنامه به شکل فوق در خواهد آمد !

 

برنامه آخر بهینه سازی آخر

همانطور که میبیتید با این تغییر سرعت اجرا باز دوبرابر شد  یعنی رسم هر فریم تصویر تقریبا 550 میلی ثانیه زمان خواهد برد و این یعنی چهار برابر سریعتر از برنامه اول !

می بینید که به سادگی و فقط یا تغییر در مدل کد نویسی می توان نتیجه خیلی بهتری را از سخت افزار مورد نظر گرفت ، تاجایی که دیگر لازم به تغییر میکروکنترلر نباشد!

مهم نیست از چه میکروکنترلری استفاده می کنید؛ ARM یا AVR مهم این است که چقدر در کار با آن تجربه و تبحر دارید!

مقایسه سرعت بروزرسانی تصویر

 

چالش برنامه نویسی آخر

خوب تا اینجا دیدیم که چقدر نحوه برنامه نویسی می‌تونه توی نتیحه خروجی(حداقل در این مورد خاص سرعت) تاثیر گذار باشه ! به عنوان چالش برای شما ، همین برنامه را با همین ساختار باز بهینه کردم ، ولی این باز شما حدس بزنید به چطور میشه این کار رو انجام داد.

چالش برنامه نویسی

 

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

 


انتشار مطالب با ذکر نام و آدرس وب سایت سیسوگ، بلامانع است.

شما نیز میتوانید یکی از نویسندگان سیسوگ باشید.  همکاری با سیسوگ

 

نوشته های مشابه

30 دیدگاه در “میکروکنترلر مقصر نیست مقصر برنامه نویسی است

  1. Avatar حسین گفت:

    سلام.
    من قبلا با avr کار کرده بودم ولی تحت تاثیر استفاده از تجهیزات جدید و عدم تطابقشون با avr و قیمت گرانش کمی از چشمم افتاد و رفتم سروقت stm32 . البته هنوز هم زیاد خوشم نمیاد ازش . منتها فکر میکردم چون از لحاظ سرعت کلاک از avr قویتره ( از جهت پریفرالها که صد در صد قویتره )
    بهتره وقتم رو بگزارم روی stm32 . تا اینکه همین کتابخانه lcd رو برای stm32 ( با شبیه سازی spi به صورت نرم افزاری ) باز نویسی کردم . نتیجه شوکه کننده بود . سرعت رفرش lcd با avr و کلاک 16 مگاهرتز از stm32 با کلاک 48 مگاهرتز و بهینه سازی روی سرعت اگر نگم بیشتر بود . کمتر هم نبود . به صورت چشمی حتی به نظر میومد avr سریعتر هست .
    خیلی جالب بود این میکروکنترلر فسقلی stm32 رو به چالش کشید . وقتی توی کدها دقیق شدم علتش رو فهمیدم .
    avr تنها با یک دستور اسمبلی که فقط 1 کلاک زمان میبره هر پین رو تغیر وضعیت میداد در حالی که این برای stm32 با 3تا 4 دستور اسمبلی مقدور بود (تازه با بهینه سازی به این نتیجه رسیده بود) که هرکدوم از دستورات هم به زور استفاده از پایپ لاین ، یک کلاک زمان لازم داره و در حالت عادی بیشتر زمان میبره . یعنی زمان تغییر وضعیت 1 پایه با avr با کلاک 16 مگ ازstm32 با 48 مگ کلاک ، حتی مقداری کمتر بود .
    اون موقع واقعا متوجه شدم avr چیست . کلی عزت و احترام پیشم پیدا کرد .

    این چیپ فسقلی یک اژدهای خفته است .

    طراحی هسته ش و دستوراتش خیلی عالی بوده .
    یعنی اگر کلاکش رو به اندازه stm32 برسونن . و با همین باس دیتای 8 بیتی ، در یک تعداد زیادی از موارد میکرو های32 stm همرده خودش( از نظر کلاک) رو از نظر سرعت ، قورت میده .
    واقعا توی میکروهای 8 بیتی از لحاظ سرعت رقیب نداره .فقط حیف یکم قیمتش گران هست.

    1. زئوس Zeus زئوس Zeus گفت:

      ^_^
      خیلی ممنون برای این که تجرتون رو به اشتراک گذاشتید
      حالا اگر شما سری xmega رو ببینی چه خواهی گفت 🙂 هسته avr کلاک آرم خخخخ
      اونم فوق العاده است مخصوصا پریفرال های متنوعی که داره گاهی دست stm32 رو هم می بنده
      یه نگاهی بهش بنداز به نظرم

  2. Avatar حسین گفت:

    سلام جناب زئوس .
    راستش من مدتی هست که روی کتابخانه مربوط به این lcd وقت گزاشتم .
    میخواستم بپرسم حداکثر سرعت رفرشی که شما با avr بهش دست پیدا کردید چقدر بوده ؟
    مثلا زمان اجرای اون 10 فریم حداقل به چقدر رسیده ؟
    مرسی.

    1. زئوس Zeus زئوس Zeus گفت:

      سلام دوست عزیز ؛ والا من زیاد تر وقت نگذاشتم ؛ و نهایتا همون 4 ثانیه برای 10 فریم رو دریافت کردم
      البته با استفاده از esp8266 تا سرعت 15 فریم به ثانیه هم رسیدم چون توی esp ما spi نه بیتی داریم 🙂

      1. Avatar حسین گفت:

        متشکر جناب زئوس

        1. زئوس Zeus زئوس Zeus گفت:

          خواهش میکنم دوست عزیز.

  3. Avatar حسین گفت:

    سلام .
    متشکر از مطلب خوبتون .
    به نظرم اصلا اون آرایه رو حذف کنید ( به درد زمانی میخورد که حلقه for استفاده شده بود ) و مستقیما از مقادیر 0x01 , 0x02 و غیره … استفاده بشه . و دستورات داخل توابع رو به جای توابع نوشت .کدش این میشه :
    CBI(LCD_PORT, LCD_CS);///_cs_clr();

    if(data & 0x001) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x002) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x004) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x008) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x010) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x020) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x040) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    if(data & 0x080) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK );CBI(LCD_PORT,LCD_CLK);

    if(data & 0x100) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

    SBI(LCD_PORT, LCD_CS);
    من یک حساب سر انگشتی کردم . با وضعیت فعلی ، دستورات داخل تابع 326 پالس کلاک احتیاج داره ولی در صورتی که تغیراتی که گفتم انجام بشه فقط به 85 پالس کلاک احتیاج داره 326/85=3.8 یعنی تقریبا سرعت 4 برابر میشه .
    یعنی به جای 4 فریم در 2 ثانیه میشه 16 فریم در 2 ثانیه یا 8 فریم در ثانیه . که تقریبا میشه باهاش انیمیشن نمایش داد.
    متاسفانه من خودم امکانش رو ندارم که این موضوع رو روی سخت افزار تست کنم اگر جناب زئوس این کاررو انجام بدن و زمان رو اندازه گیری کنن
    و نتیجه رو در یک پست به نمایش بگزارن بسیار عالی میشه .
    و یک نکته دیگه اینکه اگر بشه در مد spi …
    8بیتی راه اندازیش کرد سرعت باز هم بیشتر میشه به این دلیل که با تغیراتی که گفتم برای ایجادیک پالس کلاک روی پین میکرو باید 4 پالس هزینه بشه ( 2 پالس برای 1 کردن پین و 2 پالس برای 0 کردنش) که این فرکانس کلاک میکرو رو ربع میکنه ولی اگر از spi میکرو استفاده بشه
    ( به این دلیل که حداکثر فرکانس spi نصف کلاک میکرو هست ) فرکانس پالس کلاک تولیدی نصف فرکانس کلاک خود میکرو میشه که در حالت ایده آل 2 برابر و با درنظر گرفتن بقیه عوامل حدود 1.5 برابرمیشه . توی دیتاشیت درایور خوندم که با وصل کردن یک تعداد پین به gnd و vccمیشه این حالت رو فعال کرد .
    میخواستم بپرسم آیا این پینها در کانکتور ال سی دی در دسترس هستند .؟ یا اصلا به طور کلی چطور میشه این کار رو انجام داد؟
    مرسی

    1. زئوس Zeus زئوس Zeus گفت:

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

      1. Avatar حسین گفت:

        با سلام مجدد.
        راستش به نظرم اومد این تابع مربوط به کتابخانه lcd نوکیا 1661 باشه که شما زحمتش رو کشیدین و این مطلب رو با این فرض نوشتم که این تابع مربوط به اون کتابخانه هست اگر اشتباه هست عذر خواهی میکنم . اونقدری که من از کد فهمیدم ، اینا sda_set(); sda_clr();clk_set();clk_clr(); cs_clr();cs_set(); که داخل تابع LCDsend هستن خودشون تابع هستن ولی دستور داخل این توابع یک کد اسمبلی اینلاین 2 بایتی هست . اما به تابع در حالت اسمبلی توجه کنیم میبینم که جزء ثابتش دوتا دستور call و ret هست . که هرکدوم 4 پالس کلاک توی avr زمان میبره . یعنی ما داریم 8 پالس کلاک هزینه میکنیم که یک دستور 2 پالسی اجرا بشه !!!!!
        من کاری که کردم اون 8 پالس کلاک اضافه رو حذف کردم . گمان نمیکنم کمپایلرخودش ret و call رو حذف بکنه .
        متشکر بابت زحمتی که میکشید و بررسی میکنید.

        1. زئوس Zeus زئوس Zeus گفت:

          سلام خواهش میکنم دوست عزیز
          ببنید اون خط هایی که فرمودید در واقع تابع نیستند و ماکرو هستند ؛ وقتی از ماکرو استفاده میکنیم دقیقا همین دو سیکل برای اجرایشون طول خواهد کشید.

          1. Avatar حسین گفت:

            با سلام .
            lcd pin functions
            */
            #define LCD_PIN_FUNC(a, b) \
            void _ ## a ## _set() { SBI(LCD_PORT,(LCD_ ## b)); } \
            void _ ## a ## _clr() { CBI(LCD_PORT,(LCD_ ## b)); }

            LCD_PIN_FUNC(rst, RST)
            LCD_PIN_FUNC(cs, CS)
            LCD_PIN_FUNC(sda, SDA)
            LCD_PIN_FUNC(clk, CLK)

            راستش من هر چی دقت میکنم میبینم این چهار دستور آخر توابع رو تعریف میکنند( LCD_PIN_FUNC ماکرویی هست که توابع ست و ریست را برای پایه های مربوط به پین های lcd تعریف میکند) فکر میکنم اگر از خود دستورات cbi و sbi استفاده بشه نتیجه متفاوت خواهد شد . اگر اشتباه کرده ام مرا راهنمایی کنید.
            متشکر .

          2. زئوس Zeus زئوس Zeus گفت:

            دقیقا این نوشته های فوق تبدیل به ماکرو میشه 🙂

          3. Avatar حسین گفت:

            سلام .
            فرمایش شما درست بود کمپایلر توابع رو تبدیل به ماکرو کرده .
            مرسی.

      2. Avatar حسین گفت:

        باسلام . ببخشید اون کد اشتباه هست(تریب ارسال بیتها برعکس هست )خیلی ببخشید. این درسته
        CBI(LCD_PORT, LCD_CS);///_cs_clr();

        if(data & 0x100) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x080) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x040) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x020) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x010) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x008) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x004) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        if(data & 0x002) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK );CBI(LCD_PORT,LCD_CLK);

        if(data & 0x001) SBI(LCD_PORT, LCD_SDA ); else CBI(LCD_PORT, LCD_SDA);SBI(LCD_PORT,LCD_CLK ); CBI(LCD_PORT,LCD_CLK);

        SBI(LCD_PORT, LCD_CS);//_cs_set();

  4. Avatar محمد آصف گفت:

    ابتدا تشکر می کنم بابت نوشتن این مجموعه از مقالات مفید.
    من دو تا پیشنهاد دارم.
    1- چون این پروتکل بصورت SPI کار می کنه پس در زمانی که دیتا داره به میکرو منتقل میشه باید پایه CS فعال باشه و در انتهای کار هم غیر فعال بشه. نیازی نیست که در هر سیکل ارسال یک 9 بیتی این پایه فعال و غیر فعال بشه. اینطوری زمان تغییر وضعیت این پایه کاهش پیدا می کنه. من این روش رو در جایی تست کردم و نتیجه گرفتم.
    2- خود عملیات مقایسه چند سیکل کلاک زمان نیاز داره. میتونیم بدون اینکه نتیجه data & ShiftBit[] رو در داخل یک شرط بررسی کنیم مستقیم نتیجه رو به مقدار نیاز شیفت داده و داخل رجیستر خروجی میکروکنترلر بنویسیم. این بخش نیاز به آشنایی جزئی با رجیسترهای میکروکنترلر داره ولی تقریبا با اطمینان میتونم بگم که این کار سرعت رو افزایش میده. چون عملیات مقایسه از دو عملیات کوچکتر تفریق و مقایسه با صفر تشکیل میشه (اکثرا اینطوریه و ممکنه روشهای دیگری هم داشته باشه) در حالی که عملیات شیفت در میکروها سریعتر انجام میشه و در گام بعد نتیجه مستقیم داخل رجیستر خروجی نوشته میشه. این کار مخصوصا در مقایسه با زمانی که بخش else دستور شما اجرا میشه سریعتره.

    1. زئوس Zeus زئوس Zeus گفت:

      خواهش میکنم دوست عزیز ؛ کاملا درسته ؛ دقیقا همین مساله کنترل به موقع پایه CS میتونه تاثیر زیادی داشته باشه
      در مورد پیشنهاد دوم باید بگم که بله حق باشمایت ولی در صورتی که بتونیم تنها یک بیت از یک ریجیتر رو تغییر بدیدم ؛ که خود این کار نیاز به عملیات or داره که زمان بره ؛ در مجموع فرقی نمیکنه ؛ البته توی معماری avr

  5. Avatar امین گفت:

    جالب بود ممنون؛ پیشنهاد من هم استفاده از متغیر نوع رجیستر برای آرایه هستش، و همینطور میشه به جای تابع یک ماکرو تعریف کرد. همینطور باید قسمت slave که یک tft هستش رو هم در نظر گرفت اگر نمایشگر رو پارالل ۱۶ بیتی راه اندازی کنیم سرعت خیلی بالا میره. این کار رو میشه با یه دو تا آی سی شیفت رجیستر انجام داد.

    1. زئوس Zeus زئوس Zeus گفت:

      خواهش میکنم ؛ خوب با توجه به تعداد اجزاء آرایه احتمالا نمیشه از نوع رجیستر استفاده کرد ؛ شایدم بشه خیلی بستگی به CPU داره ولی پیشنهاد خوبیه !
      فرض اینه که میخوایم داده های به شکل سریال ارسال کنیم ؛ اگر نه فرمایش شما کاملا صحیح است ؛ استفاده از مد پارالل نرخ رو خیلی افزایش میده

  6. Avatar نوید گفت:

    من شاید 10 درصدشو متوجه شده باشم
    لطفا بازهم ازین مدل پست ها تهیه کنین طرز فکر ادمو تغییر میده
    بسیار ممنونم

    1. زئوس Zeus زئوس Zeus گفت:

      خواهش میکنم دوست عزیز 🙂
      سعی میکنم ادامه بدم ولی اینقدر مشغله هست که آدم فراموش میکنه :/

  7. Avatar علی گفت:

    با سلام
    اگر در تعریف lookup table از کلمه کلیدی const استفاده میشد بهتر نبود؟ چراکه در اینصورت در حافظه فلش ذخیره میشد. هرچند که مطمئن نیستم که آیا این باعث کندتر خواندنش میشود یا خیر.

    1. زئوس Zeus زئوس Zeus گفت:

      خوب ، البته بسته به مدل طراحی پردازنده میتونه قضیه متفاوت باشه ، ببینید حافظه RAM سریعتر است و پردازنده به صورت مستقیم معمولا بهش دسترسی داره اما حافظه Flash بسته به ساخناری که داره کندتر از RAM هست و توی برخی معماری ها پردازنده دسترسی مستقیم بهش نداره مثل خانواده های ARM پس تعریف const میتونه باعث کاهش سرعت بشه

  8. Avatar محمد گفت:

    با عرض سلام مجدد
    این برنامه رو هم یکم بهینه تر میشه نوشت البته باز هم بدون استفاده از تابع یعنی:
    cs_clr();

    if(data & 0x100)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x80)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x40)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x20)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x10)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x08)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x04)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x02)) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & 0x01)) sda_set(); else sda_clr();clk_set();clk_clr();

    cs_set();

    1. زئوس Zeus زئوس Zeus گفت:

      سلام
      پیشنهاد خوبیه ولی اگر کد اسمبلی رو نگاه کنید موقع کامپایل خود کامپایلر کد قبلی رو به همین حالت تغییر میده ! 🙂
      ببینید به عنوان راهنمایی باید بگم باید دیگه اسمبلی کدها رو بنویسید و در بهترین حالت قرار بدید

    2. Avatar محمد گفت:

      void LcdSend(uint16_t data)
      {
      static uint16_t ShiftBit[] = {0x100,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
      cs_clr();

      if(data & ShiftBit[0])) sda_set();
      else if(data & ShiftBit[1])) sda_set();
      else if(data & ShiftBit[2])) sda_set();
      else if(data & ShiftBit[3])) sda_set();
      else if(data & ShiftBit[4])) sda_set();
      else if(data & ShiftBit[5])) sda_set();
      else if(data & ShiftBit[6])) sda_set();
      else if(data & ShiftBit[7])) sda_set();
      else if(data & ShiftBit[8])) sda_set();
      else sda_clr();
      clk_set();
      clk_clr();

      cs_set();
      }

      1. زئوس Zeus زئوس Zeus گفت:

        امم خوب چی شده الان ؟ چرا یک بار بیشتر کلاک نزدید ؟

  9. Avatar محمد گفت:

    درود
    به نظر من اگه واسه این کار از تابع استفاده نشه و به جای تابع lcd_send از خود دستورات به جای فراخوانی تابع استفاده بشه سرعت بالاتر خواهد رفت چون دستور پرش به یک زیر برنامه و بازگشت به برنامه اصلی چند سیکل رو توی برنامه تلف میکنه و راه حل استفاده نکردن از تابع برای دستیابی به حداکثر سرعت هستش.
    static uint16_t ShiftBit[] = {0x100,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
    cs_clr();

    if(data & ShiftBit[0])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[1])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[2])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[3])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[4])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[5])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[6])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[7])) sda_set(); else sda_clr();clk_set();clk_clr();
    if(data & ShiftBit[8])) sda_set(); else sda_clr();clk_set();clk_clr();

    cs_set();

    1. زئوس Zeus زئوس Zeus گفت:

      درود
      پیشنهاد خوبیه ، این کار بی تاثیر نیست ، شاید در حدود 20 میلی ثانیه نه 200 میلی ثانیه
      ولی همین اقدامات کوچیک باعث این بهبود ها میشه 🙂

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *