در قسمت هفتم از آموزش LVGL به نحوه کار با Simulator و دانلود فایلهای موردنیاز برای آن پرداختیم. در این قسمت قصد داریم که ورودیهای سیستم گرافیکی LVGL یعنی inputها، انواع interfaceهای مختلف، بخش تیک سیستم گرافیکی (tick interface)، بخش task handler و بخش LOG را آموزش دهیم. پس تا پایان این قسمت همراه سیسوگ باشید.
Input device interface
ابتدا میخواهیم بررسی کنیم که inputها چی هستند و برای سیستم گرافیکی چه فایدهای دارند. بهطورکلی، Inputها دیوایسهای جانبی هستند که به سیستم گرافیکی متصل میشوند و ما توسط این Inputها، میتوانیم به سیستم گرافیکی فرمان دهیم، یک گزینه را جابهجا کنیم و یا میتوانیم یک سری اطلاعات به سیستم خود بدهیم؛ به عبارت بهتر میتوان گفت: inputها واسطه بین کاربر و سیستم گرافیکی هستند.
در سیستم گرافیکی LVGL ،inputها در چهار دسته تقسیم میشوند:
- موس و صفحه تاچ
- کیبورد و کی پد
- انکودر: برای انتخاب وجابهجایی بین منوها
- کلیدها: همان buttonهای تنها در بردها هستند که به نحوههای مختلف یا بهصورت up و down یا left و right یا enter هستند.
lv_indev_drv_t
inputها با استراکچر lv_indev_drv_t مشخص میشوند. به عبارتی، این استراکچر برای معرفی یک دیوایس ورودی به سیستم گرافیکی میباشد. این استراکچر حاوی نوع دیوایس، توابع خواندن دیوایس ورودی و اطلاعات تایمر خواندن دادهها و درایور صفحهنمایش میباشد. کلاً استراکچر بزرگی هست؛ چون شامل چهارتا مورد موس و صفحه تاچ، کیبورد و کی پد، انکودر و کلیدها میباشد.
Touchpad و mouse
بهعنوانمثال، برای معرفی این ورودی باید به صورت کد زیر عمل کنیم:
1 2 3 4 5 6 7 8 9 | static lv_indev_drv_t indev_drv_1; lv_indev_drv_init(&indev_drv_1); indev_drv_1.type = LV_INDEV_TYPE_POINTER; indev_drv_1.read_cb = my_input_read; lv_indev_t *touch_indev = lv_indev_drv_register(&indev_drv_1); |
نکته✅
ما میتوانیم همزمان، چند تا دیوایس ورودی برای سیستم خود تعریف کنیم و سیستم گرافیکی هیچ محدودیتی در این زمینه ندارد.
استراکچر lv_indev_data_t
اگر شما بخواهید یک سری دیتا (اطلاعات) از طریق تابع call back به سیستم گرافیکی خود بدهید، به یک استراکچر به نام lv_indev_data_t احتیاج دارید که این استراکچر عمومی هست؛ یعنی هم مربوط به همهی ورودیها میباشد.
بهطورکلی، این استراکچر نگهدارنده اطلاعات مربوط به دیواس ورودی میباشد. به مثال زیر توجه کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | typedef struct { lv_point_t point; uint32_t key; uint32_t btn_id; int16_t enc_diff; lv_indev_state_t state; bool continue_reading; } lv_indev_data_t; |
lv_point_t نیز خودش یک استراکچر است که در ادامه درباره آن توضیح می دهیم.
استراکچر lv_point_t
این استراکچر حاوی مختصات X و Y است؛ یعنی اگر شما بخواهید به سسیتم گرافیکیتان، یک مختصات را معرفی کنید، باید از استراکچر lv_point_t استفاده کنید.
بهطورکلی، این استراکچر حاوی اطلاعات مربوط به مکان لمس شده است. به مثال زیر توجه کنید:
1 2 3 4 5 6 7 | typedef struct { lv_coord_t x; lv_coord_t y; } lv_point_t; |
نکته✅
نحوه درایور کردن همه موسها و کیبوردها به این شکل هست که یا Polling هستند یا بهصورت اینتراپتی (Interrupt). عیبی که Polling دارد، این است که برای سیستم، سربار دارد، درصورتیکه در حالت اینتراپتی، این سربار وجود ندارد و دلیل آن هم خواندن پشت سر هم آن ورودی هستش.
نمونه کد تابع خواندن تاچ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void my_input_read(lv_indev_drv_t * drv, lv_indev_data_t * data) { if(touchpad_pressed) { data->point.x = touchpad_x; data->point.y = touchpad_y; data->state = LV_INDEV_STATE_PRESSED; } else { data->state = LV_INDEV_STATE_RELEASED; } } |
Keypad و keyboard
در Keypad و keyboard شما باید آبجکتهایی که قرار است با Keypad یا keyboard تغییر کنند یا جابه جا شوند را داخل group قرار دهید.
استراکچر lv_group_t
دراینخصوص، شما باید ابتدا یک group از جنس lv_group_t تعریف کنید که lv_group_t خودش یک استراکچر هست. مانند کد زیر شما باید ابتدا یک group تشکیل دهید و آن را بزارید رو حالت default. (وقتی رو حالت default بزارید، هر چی که تو صفحتون هست جز این گروه قرار میگیرد.) سپس دیوایس ورودی خود را تعریف کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | lv_group_t * g = lv_group_create(); lv_group_set_default(g); static lv_indev_drv_t indev_drv_2; lv_indev_drv_init(&indev_drv_2); indev_drv_2.type = LV_INDEV_TYPE_KEYPAD; indev_drv_2.read_cb = keyboard_read; lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv_2); lv_indev_set_group(kb_indev, g); |
توجه: تابع خواندن KEYPAD و keyboard یکسان هستند.
1 2 3 4 5 6 7 8 9 | void keyboard_read(lv_indev_drv_t * drv, lv_indev_data_t * data){ data->key = last_key(); /*Get the last pressed or released key*/ if(key_pressed()) data->state = LV_INDEV_STATE_PRESSED; else data->state = LV_INDEV_STATE_RELEASED; } |
انکودر (Encoder)
انکودر نیز مانند KEYPAD و keyboard به این صورت است که شما نیاز دارید که یک group ایجاد کنید و آن را روی group خود، ست کنید.
تنها تفاوتی که انکودر با KEYPAD و keyboard دارد این است که ورودی از نوع انکودر میشود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | lv_group_t * g = lv_group_create(); lv_group_set_default(g); static lv_indev_drv_t indev_drv_3; lv_indev_drv_init(&indev_drv_3); indev_drv_3.type = LV_INDEV_TYPE_ENCODER; indev_drv_3.read_cb = encoder_read; lv_indev_t * enc_indev = lv_indev_drv_register(&indev_drv_3); lv_indev_set_group(enc_indev, g); |
تابع خواندن انکودر
تابع خواندن انکودر مانند تابعهای قبلی است. فقط در اینجا شما باید خروجی تفاضل کانتر انکودرتون به ورودی دیتا بدهید.
1 2 3 4 5 6 7 8 9 | void encoder_read(lv_indev_drv_t * drv, lv_indev_data_t*data){ data->enc_diff = enc_get_new_moves(); if(enc_pressed()) data->state = LV_INDEV_STATE_PRESSED; else data->state = LV_INDEV_STATE_RELEASED; } |
نکته✅
تایمر کانترها قابلیت اتصال انکودر را دارند؛ یعنی وقتی که شما تایمر کانتر خود را داخل stm فعال میکنید، هر step ای که داخل انکودرتان بدید مقدار این تایمر کانتر افزایش یا کاهش مییابد و در صورت فعال سازی وقفه، با هر تغییر یک وقفه به وجود می آید.
Tick interface
Tick interface یک بخش عمومی در تمام سختافزارها و سیستمهای گرافیکی است. کار Tick interface در سیستم گرافیکی LVGL، این است که زمانسنجی کند و این فرایند را توسط وقفههای یک میلی ثانیهای انجام میدهد.
lv_tick_inc(tick_period)
ویژگیهای این تابع عبارتاند از:
- این تابع باید بهصورت مداوم با زمانبندی میلیثانیه فراخوانی شود.
- اصولاً این تابع باید بهصورت یک میلیثانیه یکبار اجرا شود.
- وجود آن برای عملکرد سیستم اجباری است.
- محل قرارگیری پیشنهادی در system_tick یاtaskها میباشد.
- سطح اولویت بالایی باید داشته باشد.
1: super Loop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | void system_Tick(void) { lv_tick_inc(1); } 2: Free Rtos void * tick_thread (void *args) { while(1) { usleep(5*1000); lv_tick_inc(5); } } |
lv_task_handler()
ویژگیهای این تابع عبارتاند از:
- این تابع باید بهصورت مداوم فراخوانی شود.
- زمان اجرای آن از 5 میلی ثانیه باید بالاتر باشد.
- مکان پیشنهادی در حلقة اصلی برنامه است.
LOG
این تابع بررسی و نمایش وضعیت و اخطارها و خطاهای سیستم میباشد.
LV_LOG_LEVEL_TRACE
LV_LOG_LEVEL_WARN
LV_LOG_LEVEL_ERROR
LV_LOG_LEVEL_USER
LV_LOG_LEVEL_NONE
نکته✅
- برای فعل سازی LOG باید LV_USE_LOG درh فعال شود.
- اگر بخواهید از دستور printf برای LOG کردن استفاده کنید باید LV_LOG_PRINTF در h فعال شود.
به عنوان مثال توسط در کد وقفهی زیر وقتی کلید شما لمس میشود سیستم عبارت “Clicked” را LOG می کند و در صورت تغییر وضعیت کلید عبارت “Toggled” LOG میشود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void event_handler(lv_event_t * e) { lv_event_code_t code = lv_event_get_code(e); if(code == LV_EVENT_CLICKED) { LV_LOG_USER("Clicked"); } else if(code == LV_EVENT_VALUE_CHANGED) { LV_LOG_USER("Toggled"); } } |
نکته✅
از ویژگی LOG برای بررسی و مانیتوریگ و اشکال یابی سیستم استفاده میشود.