در قسمت قبل به اصول نرمافزاری نمایشگر و انواع کتابخانه های آن پرداختیم، در این قسمت به صورت پروژه محور کتابخانه LVGL و نکات مربوط به آن را شرح داده و جمع بندی نهایی را به همراه نمونهی اجرای Demo انجام خواهیم داد.
کتابخانه LVGL، مناسبترین کتابخانه برای نیازهای گرافیکی پروژه
Light and Versatile Graphics Library کتابخانهای است مناسب برای راهاندازی هر مدل نمایشگر گرافیکی رنگی و تکرنگ که در زبان C به شکل منبع باز و بر اساس استاندارد C99 نوشتهشده است (در محیط C++ هم مطابقت دارد). تحت مجوز MIT در دسترس عموم قرارگرفته و کاملاً رایگان است. کد کتابخانه در دسترس است. پشتیبانی فعال توسط حدود 110 نفر از سراسر دنیا دارد که در هرماه بر اساس برنامهای از پیش تعیینشده نسخهی جدید آن به هدایت آقای Gabor Kiss-Vamosi که نویسندهی اصلی آن است، بیرون دادهمیشود. راهنمای جامعی دارد که میتوان برای شروع قدمهای محکم و سریعی با آن برداشت. همچنین شایانذکر است که شرکت معتبر TI(Texas Instruments) برای راهاندازی بخش گرافیکی مرتبط با میکروکنترلرهایش استفاده از این کتابخانه را توصیه کردهاست. برای خواندن این بخش در وبسایت TI از لینک زیر استفاده کنید.
این کتابخانه حجم نسبتاً کمی دارد و برای استفاده در سیستمهای Embedded که منابع سختافزاری و حافظه محدود است، مناسب است. در صورت محدودیت زیاد، با پیکربندی بافر حافظه میتوان میزان استفاده از حافظه را کنترل کرد. همچنین کتابخانهی آمادهی آن در زبان MicroPython هم منتشر میشود که برای استفاده در چیپهایی چون ESP32 کار را بسیار آسانمیکند. البته برای پیادهسازی در میکروکنترلرهای 8 بیتی مناسب نیست.
امکان ایجاد بیش از 30 نوع Widget آماده مثل دکمه، اسلایدر، نمودار و گراف، کیبورد و … را در style های دلخواه بهسادگی برای طراح مهیا میکند. تنظیمات مربوط به Event برای هر Widget و نوشتن تابع Callback برای انجام کاری دلخواه در زمان وقوع Event بهراحتی انجام میگیرد. ایجاد انیمیشن در تبدیل حالت Widget ها جذابیت حرفهای به طراحی گرافیکی میدهد که کتابخانهی LVGL این امکان را در اختیار طراح گذاشته است.
خواندن انواع ورودیهای اشارهگر از کلید تا موس و Touchpad در این کتابخانه در نظر گرفتهشده است و بهسادگی با پیکربندی ابزار ورودی در هنگام Port کردن، نیاز طراح برای داشتن یک رابط کاربری گرافیکی رنگی اعم از ورودی و خروجی را مرتفع میسازد. حتی امکان پیکربندی خروجی صوتی برای ایجاد صداهای مختلف در شرایط وقوع Event ها را دارد. بنابراین میتوان گفت که کتابخانهی LVGL نه تنها برای راهاندازی نمایشگر، که به شکل کلی برای ایجاد رابط کاربری گرافیکی کامل مناسب و کارا است.
برای بخش نوشتن متن ابزارهای جامع و کاملی شبیه یک ویرایشگر متن با توابع آماده در اختیار طراح است تا بتواند بهسرعت امکانات ویرایش متن حرفهای را در رابط کاربری بگنجاند. همچنین از زبان فارسی بهطور کامل پشتیبانی میکند و ابزارهای آمادهای برای Port کردن قلم (Font Type) دلخواه به پروژه دارد. علاوه بر آن بهسادگی میتوان هر عکسی را با هر استاندارد رنگی به کد مناسب C تبدیل کرد و به حافظه انتقال و یا از آن فراخوانی کرد.
داشتن قابلیت style های از پیش طراحیشده، حفظ یکپارچگی را در طراحی رابط کاربری برای پروژه آسان میکند. همچنین این پروژهی منبع باز، بخش شبیهسازی دارد که با فعالسازی آن امکان طراحی صفحات کاربری در شبیهساز و تولید کد آماده و متناظر با آن مهیا میشود. این ابزار سرعت طراحی و اشکالیابی را به طرز چشمگیری افزایش میدهد. از سوی دیگر میتوان با فعالسازی حالت اشکالیابی (debug) در تنظیمات کتابخانه، به ردیابی برخطِ اشکالات احتمالی در زمان اجرای برنامه و مشاهدهی گزارش آن در فایل Log پرداخت.
یکی دیگر از قابلیتهای مهم این کتابخانه، امکان استفاده از آن در مدل برنامهنویسی super loop و نیز سازگاری با اجرا در سیستمعاملهای کوچکی چون FreeRTOS تا لینوکس میباشد که محدودیتی برای طراح در پروژههای مختلف ایجاد نمیکند.
اصول کار و Port کردن کتابخانهی LVGL
اصول کار LVGL را میتوان در خلاصهای چندخطی زیر بیان کرد:
در ابتدا توسط تابع مقداردهی اولیه که طراح برای کتابخانه مینویسد، پیکربندی بافر حافظهی موردنیاز کتابخانه انجام میگیرد. بعدازآن، پیکربندی نمایشگر و درایور چیپ کنترلر صورت میگیرد. سپس در حلقهی اصلی برنامه، با یک دورهی زمانی ثابت و دلخواه (مثلاً هر 20 میلیثانیه که میتواند توسط تایمر میکروکنترلر ایجاد شود)، تابع handler کتابخانه اجرا میشود. وظیفهی تابع handler آن است که اگر Event ی اتفاق افتاده بود یا نیازی به Refresh کردن صفحهی نمایشگر بود، آن را انجام داده و بافر حافظه را آماده و به نمایشگر ارسال کند.
جدای از روند بالا، تایمری دیگر از میکروکنترلر به شکل Tick Timer برای Synchronize کردن باید در نظر گرفته شود تا از طریق فراخوانی تابع lv_tick_inc(x)، زمان سپریشده را به اطلاع کتابخانه برساند.
واردکردن(Porting) کتابخانه به پروژهای در زبان C به سادگی 9 مرحلهی زیر است.
- کد کتابخانهی بهروز، مثالها و درایورها از صفحهی Github دریافت شود. اینجا کلیک کنید. پوشهی lvgl به پروژه اضافه شود.
- کتابخانه یا فایل درایور نرمافزاری چیپِ کنترلر (مثلاً SSD1963) باید نوشته یا از منبعی معتبر دریافت شود.
- در فایل h تنظیمات مربوط به فعالسازی بخشهای مختلف کتابخانه انجام شود. اگر بخشی موردنیاز نیست، برای کاهش حافظهی اشغالی حتماً غیرفعال شود.
- حداقل 3 مورد Define زیر در فایل h، بر اساس نیاز پروژه مقداردهی شود:
-
- LV_HOR_RES_MAX: تعداد Pixel افقی نمایشگر
- LV_VER_RES_MAX: تعداد Pixel عمودی نمایشگر
- LV_COLOR_DEPTH: یکی از مقادیر 8،16 یا 32
- مقداردهی اولیه(Initialize) کردن کتابخانهی گرافیکی LVGL شامل مراحل زیر است:
- فراخوانی تابع lv_init
- مقداردهی اولیهی درایور چیپ کنترلر
- تخصیص بافر حافظه و register کردن درایورِ Display و Input Device (در مرحله 6 و 7 توضیح داده شدهاست)
- در هر x میلیثانیه تابع lv_tick_inc(x) فراخوانی شود.
- تابع lv_task_handler() در هر y میلیثانیه برای انجام کارهای LVGL فراخوانی شود. مقدار y باید از x کمتر باشد.
- در فایل main، متغیر lv_disp_buf_t به شکل زیر مقداردهی شود.
/*A static or global variable to store the buffers*/ static lv_disp_buf_t disp_buf; /*Static or global buffer(s). The second buffer is optional*/ static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; static lv_color_t buf_2[MY_DISP_HOR_RES * 10]; /*Initialize `disp_buf` with the buffer(s) */ lv_disp_buf_init(&disp_buf, buf_1, buf_2, MY_DISP_HOR_RES*10);
در فایل main، متغیر lv_disp_drv_t به شکل زیر مقداردهی شود. اعضای buffer و flush_cb حتما باید مقداردهی شوند. مقداردهی بقیهی اعضا اختیاری است. سپس توابع lv_disp_drv_init(&disp_drv) و lv_disp_drv_register(&disp_drv) فراخوانی شوند.
lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Can be local variable*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/ disp_drv.buffer = &disp_buf; /*Set an initialized buffer*/ disp_drv.flush_cb = my_flush_cb; /*Set a flush callback to draw to the display*/ lv_disp_t * disp; disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/ void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/ int32_t x, y; for(y = area->y1; y <= area->y2; y++) { for(x = area->x1; x <= area->x2; x++) { put_px(x, y, *color_p) color_p++; } } /* IMPORTANT!!! * Inform the graphics library that you are ready with the flushing*/
- کتابخانه LVGL برای اطلاع از زمان سپریشده نیاز دارد تا تابع lv_tick_inc(tick_period) به شکل دورهای در هر tick_Period فراخوانی شود. با راهاندازی یک زمانسنجی (timer) میتوان به این مقصود رسید. یا بهعنوانمثال در FreeRTOS در تابع vApplicationTickHook میتوان آن را فراخوانی کرد.
- کتابخانه LVGL برای انجام وظایف و تغییرات لازم در بافر حافظه نیاز دارد تا تابع lv_task_handler() با دورهای ثابت فراخوانی شود. زمان فراخوان دورهای آن از فراخوانی تابع در مرحله قبل باید بیشتر باشد و همچنین اگر در Interrupt استفاده میشود، اولویت آن از مرحله 8 باید کمتر باشد. با فراخوانی این تابع در حلقهی while اصلی برنامه یا از طریق callback زمانسنجی دیگر به این مقصود میتوان دستیافت.
ذکر مراحل بالا برای آشنایی کلی با کارکرد کتابخانهی LVGL آورده شده است. مسلماً در نگاه اول ممکن است گویا نباشد. لذا توصیه میشود برای دسترسی به راهنمای جامع این کتابخانه از آدرس زیر استفاده کنید. بعد از کمی مطالعهی با دقت و راهاندازی اولین پروژه با LVGL، شما هم در مورد سادگی Port کردن آن با ما همنظر خواهیدشد.
صفحهی کتابخانهی LVGL در Github
جمعبندی
آغاز مبحث، با معرفی رابط کاربری بود. توضیح دادهشد که رابط کاربری چیست و استفاده از رابط کاربری گرافیکی چه مزایایی در بر دارد. نشان دادهشد که برای رسیدن به این مزایا، با پیچیدگیِ راهاندازی نمایشگرهای گرافیکی مواجه هستیم. برای برآمدن بر این پیچیدگیها، استفاده از بستهی نرمافزاری جامعی که وابستگی به منابع و واسطهای سختافزاری نداشتهباشد، پیشنهاد شد. در ادامه اصول ارتباطی نمایشگرها بررسی شد و برای راهاندازی نرمافزاری آن، راهکارهای موجود مورد بررسی اجمالی قرارگرفت.
در آخر به طور خلاصه میتوان گفت که برای راهاندازی همهکاره و جامع یک رابط کاربری کامل گرافیکی اعم از پیکربندی ابزار ورودی و خروجی، مناسبترین کتابخانهای که در حال حاضر میتوان استفادهکرد، کتابخانهی LVGL است. البته در مورد راهاندازی نمایشگرهای تکرنگ، استفاده از کتابخانهی U8G2 از آنجا که مصرف حافظهی کمتری دارد بهینهتر است.
نمونهی اجرای Demo
به عنوان یک نمونه، Demoی کتابخانهی LVGL به پروژهای با مشخصات زیر برای راهاندازی یک واحد کامل رابط کاربری گرافیکی، Port شدهاست.
از compile پروژه در دو حالت مقداردهی بافر lv_disp_buf_t، نتایج زیر بدست آمدهاست
باید توجهداشت که بخشی از مصرف حافظه در این پروژه به راهاندازی USB Mouse اختصاص یافتهاست و همهی آن مربوط به کتابخانه LVGL نیست. همچنین این پروژه بدون بهینهسازی Compile شده و متغییرهای Dummy از آن حذف نشدهاند.
برای مشاهدهی ویدئوی اجرای برنامه از لینک زیر استفاده کنید.
ویدئوی اجرای Demo با بافر حافظهی 36000 تایی
ویدئوی اجرای Demo با بافر حافظهی 8000 تایی
منبع: سیسوگ