Двохпозиційний графічний перемикач для сенсорного екрану: програмна реалізація

Сенсорний дисплей

Програмна реалізація двохпозиційного графічного перемикача для сенсорного інтерфейсу

Основні елементи та структури для створення графічного елемента зображення

 

На базі тестової відлагоджувальної плати, що розглядалася в попередній статті «Розробка графічного сенсорного інтерфейсу», в поточній роботі буде продемонстровано програмну реалізацію одного графічного елемента на прикладі створення двохпозиційного перемикача. Вся програмна частина реалізована на мові Сі, середовище програмування – IAR.

Поточний опис демонструє лише саму ідею програмування графічного інтерфейсу і націлений на читача, який не є початківцем в області програмування контролерів. В даному матеріалі не будуть розглядатися низькорівневі функції ініціалізації чи робота з самим дисплеєм або контролером. Всі нижче описані функції представляють собою драйвер середнього рівня. По бажанню читачів опис програми низького рівня, наприклад роботи з дисплеєм, буде розглянутий в наступних статтях.

Для початку нам потрібно зробити основні об’явлення структур-шаблонів на яких буде базуватися вся програмна реалізація. Краще за все такі об’явлення робити в окремому (header) файлі.

Перша конструкція, яка нам потрібна – це перелічуваний тип даних для зберігання ідентифікаторів окремого графічного елементу. Представляє собою інкрементований список констант з потрібною назвою. Тут індекси починаючи з 1-ці і далі вказувати не обов’язково, так як даний тип автоматично може здійснювати задання значення з інкрементом.

 

 

Наступна конструкція – це структура-шаблон, що містить дві змінні для зберігання координат положення графічного елементу на дисплеї.

 

 

 

Далі – структура-шаблон, що містить дані про новостворюваний графічний елемент.

 

 

 

Розглянемо змінні, що входять в останню структуру.

Змінна ControlPTR – вказівник невизначеного типу на іншу структуру-шаблон графічного компоненту, що створюється. Остання для поточного прикладу буде розглядатися нижче.

Змінна ControlType – це змінна, що містить ідентифікатор графічного елементу, який створюється. Ідентифікатор об’являється в попередньо розглянутому перелічуваному типі даних типу ControlType_TypeDef.

Змінна ControlCoord – це вказівник на попередньо розглянуту структуру-шаблон ControlCoord_TypeDef.

Остання змінна (*SetControlVisible)(PageControl_TypeDef*, ControlCoord_TypeDef) – це вказівник на функцію, яка викликається при візуалізації поточного графічного елементу. В якості параметрів, що передаються останній функції, використовуються вказівники на структуру-шаблон сторінки, що буде розглянута нижче та структуру-шаблон ControlCoord_TypeDef.

Остання структура-шаблон, яка використовується в нашій програмі – це структура, що описує параметри створюваної графічної сторінки.

 

 

 

В останню сходять наступні змінні.

PageName[CONTROL_MAX_NAME_LEN] – вказівник на текстову строку, що описує назву сторінки. Дана текстова змінна по кількості символів не має бути більшою за максимальну довжину, що задається константою CONTROL_MAX_NAME_LEN.

PageActive – змінна, що вказую чи є створювана сторінка активною, використовується при відображенні останньої. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

PageVisible – змінна, що вказує поточний стан сторінки (відображається / не відображається). В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

ControlCount – змінна, що вказує кількість графічних елементів на поточній сторінці.

PageColor – змінна, що вказує код кольору фону поточної сторінки.

PageTochActive – змінна, що вказує чи активна сторінка для обробки подій дотику до сенсорного екрану. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

*PageControls[CONTROL_MAX_COUNT]– масив вказівників на структуру типу PageControl_TypeDef, що розглядалася вище.

(*ShowPage)(Page_TypeDef*, uint8_t) – вказівник на функцію відображення заданої сторінки. В якості параметрів остання функція передає вказівник на щойно розглянуту структуру типу Page_TypeDef та значення, що вмикає або вимикає події дотику до сенсорного екрану. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

Остання структура яку будемо використовувати в поточному прикладі – це структура власне потрібного нам графічного двохпозиційного перемикача ControlTwoStateSwich_Struc.

 

 

 

Далі розглянемо змінні та вказівники, що входять в останню структуру.

ID – це змінна-ідентифікатор створюваного графічного елемента.

IsTwoStateSwich_Touched – змінна,що вказує чи був поточний графічний елемент активований подією дотику. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

TwoStateSwich_Enable – змінна, що вказує чи поточний графічний елемент активний до подій дотику. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

TwoStateSwich_Visible – змінна, що вказує чи поточний графічний елемент буде відображатися по замовчуванню на заданій сторінці. В якості значень, які приймає остання змінна використовуються бітові значення ‘0’ і ‘1’.

TwoStateSwich_CheckState – змінна, що вказує в якому стані знаходиться поточний графічний елемент (ввімкнутий / вимкнутий).

TwoStateSwich_Width – змінна, що задає розмір по ширині поточного графічного елементу.

TwoStateSwich_Heght – змінна, що задає розмір по висоті поточного графічного елементу.

*TwoStateSwich_ImageOn – вказівник, що вказує на масив, який зберігає дані зображення поточного графічного елементу в стані ввімкнутий.

*TwoStateSwich_ImageOff – вказівник, що вказує на масив, який зберігає дані зображення поточного графічного елементу в стані вимкнутий.

(*EventHandler)(uint8_t) – вказівник на функцію обробки події дотику до поточного графічного елементу. В якості параметрів, що передає остання функціє – це поточний стан графічного елементу (ввімкнутий / вимкнутий).

 

Опис функцій потрібних для створення та відображення графічного елемента

 

 

Для початку потрібно ініціалізувати графічний елемент, який ми хочемо створити, в пам’яті та задати для нього всі потрібні параметри. Для цього використовується наступна функція CreateTwoStateSwich_Func.

 

 

В якості параметрів, що передаються для останньої функції, використовуються більшість змінних і вказівників описаних для структури ControlTwoStateSwich_Struc:

  • змінна-ідентифікатор створюваного графічного елемента ID;
  • змінна, що вказує в якому стані знаходиться поточний графічний елемент (ввімкнутий / вимкнутий) PressState;
  • змінна, що задає розмір по ширині поточного графічного елементу Width;
  • змінна, що задає розмір по висоті поточного графічного елементу Height;
  • вказівник, що вказує на масив, який зберігає дані зображення поточного графічного елементу в стані ввімкнутий *ImageOn;
  • вказівник, що вказує на масив, який зберігає дані зображення поточного графічного елементу в стані вимкнутий *ImageOff;
  • вказівник на функцію обробки події дотику до поточного графічного елементу (*pEventHandler)(uint8_t);
  • змінна, що вказує чи поточний графічний елемент активний до подій дотику Enable;
  • змінна, що вказує чи поточний графічний елемент буде відображатися по замовчуванню на заданій сторінці Visible.

В якості параметру, що повертає поточна функція використовується вказівник на новостворену структуру типу PageControl_TypeDef.

Коротко розглянемо дії, які виконуються тут. Для початку об’являємо змінні-вказівники *pPageControl типу PageControl_TypeDef та *pControl типу ControlTwoStateSwich_TypeDef.

 

 

 

Далі для змінної-вказівника pControl виділяємо оперативну пам’ять розміром, який потрібен для зберігання структури типу ControlTwoStateSwich_TypeDef.

 

 

 

Якщо виконується перевірка умови if (pControl){}, це означає, що пам’ять виділена без помилок. Далі можна ініціалізувати змінні, що входять в щойно створену структуру. Для операції ініціалізації використовується наступне звернення до структури через вказівник.

 

 

 

Далі аналогічно попереднім операціям для змінної-вказівника pPageControl виділяємо оперативну пам’ять розміром, який потрібен для зберігання структури типу PageControl_TypeDef.

 

 

 

Після вдалого виділення пам’яті для останньої динамічної структури, переходимо до її ініціалізації.

 

 

 

В даному випадку задаємо наступні параметри, – це вказівник на структуру вище створеного графічного елемента (void*)pControl, константу-тип CONTROL_TYPE_TWO_STATE_SWITCH з списку типу ControlType_TypeDef та вказівник на функцію, що використовується для візуалізації поточного графічного елемента TwoStateSwichVisible_Func.

Далі розглянемо функцію для створення окремої графічної сторінки CreatePage_Func.

 

 

 

В якості параметрів, що передаються для останньої функції використовуються наступні, – це вказівник на попередньо об’явлену структуру типу Page_TypeDef та код кольору фону поточної сторінки. Параметр, що повертає функція – це ідентифікатор вдалого виконання операцій (‘0’ або ‘1’).

Розглянемо опис самої функції. Для початку програма перевіряє чи створений в пам’яті вказівник типу Page_TypeDef умовою if (!pPage){}. Якщо перевірка виконана вдало, далі перевіряється чи не перевищена задана максимальна кількість створюваних сторінок умовою if (PageCounter++ < PAGES_MAX_COUNT){}. Після чого проводимо ініціалізацію структури типу Page_TypeDef. В якості вказівника на функцію, що буде відображати поточну сторінку задаємо ShowPage_Func, вона буде спільною для усіх сторінок. Опис останньої розглянемо пізніше. Для масиву вказівників PageControls типу PageControl_TypeDef виділяємо оперативну пам’ять розміром рівним розміру структури типу PageControl_TypeDef в заданій максимальній кількості графічних елементів на сторінку.

 

 

 

Наступна потрібна нам функція – це функція відображення на дисплеї нашого графічного елемента (перемикача).

 

 

 

Розглянемо що тут відбувається. В якості параметрів, що отримує дана функція використовуються вказівник *pControl на структуру типу PageControl_TypeDef та вказівник pCoords на структуру типу ControlCoord_TypeDef.

Далі створюємо змінну-вказівник типу ControlTwoStateSwich_TypeDef і записуємо сюди значення вказівника на структуру графічного елементу з відповідної отриманої структури типу PageControl_TypeDef.

 

 

 

Наступна умова перевіряє чи є поточний графічний елементи видимим або не видимим.

 

 

 

Якщо елемент є не видимим і в програмі об’явлена директива FILL_UNVISIBLE_CONTROL_ATTAY, область розміщення поточного елементу замальовується в колір фону сторінки за допомогою функції DrawFullRect_Func. Для цього останній в якості параметрів передаються координати розміщення графічного елементу, його розміри та код кольору фону даної сторінки.

Функції, що стосуються системної роботи з самим дисплеєм, наприклад вище розглянута DrawFullRect_Func, описуватися в поточній статі не будуть. Їхня реалізація в певній мірі також залежить від конкретної моделі дисплею, що використовується.

В противному випадку, якщо елемент є видимим, перевіряється наступна умова.

 

 

 

Остання перевіряє чи є поточний елемент активним або пасивним. В даному випадку при умові пасивного елемента не передбачено ніяких дій. В деяких інших типах графічних елементів в такому разі останній може відображатися сірим неактивним, або напівпрозорим кольором.

Якщо графічний елемент активний далі програма робить наступну перевірку

 

 

 

Тут TWO_STATE_SWITCH_OFF – це директива-константа, що об’являється вище в програмі або в файлі додаткових типів і констант. В даному випадку ця констант приймає значення логічний ‘0’.

Якщо вище описана умова виконується, далі програма виконує функцію DrawControl_Func.

 

 

 

Остання виконує візуалізацію графічного елемента в заданих координатах (uint8_t)pCoords.StartX, (uint16_t)pCoords.StartY та розміром (uint8_t)pTemp->TwoStateSwich_Width, (uint16_t)pTemp->TwoStateSwich_Heght. Зображення елемента задається вказівником на масив даних (uint16_t*)pTemp->TwoStateSwich_ImageOff), в даному випадку це зображення елемента в стані вимкнутий. Функція DrawControl_Func є системною функцією виводу зображення і в поточній статі розглядатися не буде.

В противному випадку, якщо вище розглянута умова не виконується, відбувається візуалізація графічного елементу в стані ввімкнутий.

 

 

 

Наступна функція – це функція відображення заданої сторінки і всіх графічних елементів, що для неї задані ShowPage_Func.

 

 

 

Розглянемо опис останньої функції. В якості параметрів, що приймає функція ShowPage_Func використовуються вказівник *pPage на попередньо створену структуру типу Page_TypeDef, та змінна TochActive, яка вмикає або вимикає функцію обробки дотику до графічних елементів. Параметр, що повертає функція – це ідентифікатор вдалого виконання операцій (‘0’ або ‘1’).

В першу чергу перевіряється наступна умова

 

 

 

Якщо остання виконується, то це значить, що структура на яку вказує вказівник pPage типу Page_TypeDef була створена вдало і поточна сторінка до цього не відображалася.

Наступна умова перевіряє чи була попередня сторінка активна. Поточна активна сторінка задається змінною-вказівником ActivePage типу Page_TypeDef.

 

 

 

Якщо вище описана умова виконується, далі для попередньої активної сторінки змінну PageVisible встановлюємо в значення FALSE або ‘0’.

Далі для структури сторінки, яку відображаємо, задаємо значення, що відповідають за відображення поточної активної сторінки PageVisible та активну або неактивну функцію обробки подій дотику до графічних елементів PageTochActive. А також перезаписуємо вказівник сторінки, що відображаємо, в змінну-вказівник поточної сторінки ActivePage.

 

 

 

Наступна системна функція ClearScreen_Func робить заливку екрану в колір, що був заданий як фоновий для даної сторінки PageColor.

 

 

 

І по завершенню виконання поточної функції, в циклі, кількість ітерацій якого рівна кількості графічних елементів, що були задані для сторінки, робимо виклик функцій, які відображають кожен заданий елемент.

В нашому випадку буде викликана вище розглянута функція TwoStateSwichVisible_Func.

 

 

 

Наступна потрібна функція – функція додання створеного графічного лементу до заданої сторінки AddPageControl_Func.

 

 

 

В останню функцію в якості параметрів передаються координати положення графічного елементу, який потрібно додати, вказівник pControl на структуру графічного елементу PageControl_TypeDef та вказівник pPage на структуру сторінки до якої добавляємо графічний елемент.

На початку тіла функції об’являємо вказівник невизначеного типу pTemp. Далі робимо перевірку вказівника pControl.

 

 

 

Наступна умова перевіряє чи не перевищена максимальна кількість графічних елементів для заданої сторінки.

 

 

 

Якщо остання умова виконується, далі об’являємо динамічну структуру ControlCoord типу ControlCoord_TypeDef і виділяємо для неї потрібний розмір оперативної пам’яті.

 

 

 

Далі оператором switch робимо перевірку на тип елементу на структуру якого вказує вказівник pControl.

 

 

 

В нашому випадку вище розглянутий вибір зупиниться на нашому двопозиційному перемикачі.

 

 

 

Нижче робимо ініціалізацію вказівника pTemp привівши його до типу ControlTwoStateSwich_TypeDef, і записуємо в нього адрес, що вказує на структуру поточного графічного елементу ControlPTR.

 

 

 

Наступна умова виконує перевірку на входження елементу, який ми добавляємо в допустиму область графічного дисплею. Для цього використовуються початкові координати елементу то його розміри. Якщо умова не виконується поточна функція завершує своє виконання.

 

 

 

При вдалій останній перевірці ініціалізуємо поля, що входять в вище створену структуру ControlCoord, записавши туди координати графічного елементу, які були отримані в вхідних параметрах.

 

 

 

Останні дії поточної функції – це ініціалізація поля ControlCoord структури на яку вказує вказівник pControl типу PageControl_TypeDef, та ініціалізація полів PageControls та ControlCount структури на яку вказує вказівник pPage типу Page_TypeDef записуючи туди вказівник на структуру pControl та інкрементоване значення поля ControlCount відповідно.

 

 

Остання функція яку розглянемо в поточному параграфі – це функція обробки дотику до графічних елементів активної сторінки ProcessInputData_Func. Остання може бути викликана при перериванні по приході активного сигналу з драйвера сенсорної панелі або періодично викликатися в безкінечному циклі або окремій задачі операційної системи.

 

 

 

Розглянемо опис вище вказаної функції. Остання не приймає і не повертає ніяких додаткових параметрів. На початку тіла функції об’являються змінні, що використовуються функцією, призначення останніх буде розглядатися далі.

Перша дія – це часова циклічна затримка, яка була введена для завдання періодичності між кожною наступною перевіркою подій. Змінна DelayIndex – лічильник затримки, який затримує виконання функції на кількість ітерацій, що задається константою CONTROL_TOUCH_DELAY.

 

 

Далі виконується наступна перевірка.

 

 

 

Функція ConvertPos_Func є системною функцією зчитування даних з драйвера сенсорної панелі, розгляду в поточній статті не підлягає. Остання виконує задачі зчитування координат з драйвера в визначеній послідовності з подальшим фільтруванням отриманих чисел і перевірку на помилки.

Після вдалого виконання функції ConvertPos_Func програма переходить до циклу, кількість ітерацій якого рівна кількості добавлених графічних елементів до поточної сторінки, на яку вказує вказівник ActivePage.

 

 

 

При кожній наступній ітерації циклу програма за допомогою оператора switch робить перевірку на тип графічного елементу, якому відповідає індекс Index ітерації.

 

 

 

В нашому випадку програма виконає наступну умову при перевірці на тим графічного елементу USE_TWO_STATE_SWITCH.

 

 

 

Наступні дії програми – це ініціалізація змінної-вказівника pTemp на структуру поточного графічного елементу, що відповідає індексу ітерації Index, та розмірів елементу для чого використовуються вище об’явлені змінні ControlWidth та ControlHeight.

 

 

 

Далі програма перевіряє чи поточний елемент активний і видимий, для чого використовуються поля структури елементу TwoStateSwich_Enable та TwoStateSwich_Visible.

 

 

 

Наступна перевірка виконує виклик функції CheckCoordBeyong_Func, для якої передаються наступні параметри, – це координати поточного графічного елементу, що відповідає індексу ітерації Index, та розміри цього елементу.

 

 

 

Остання функція здійснює перевірку на приналежність отриманих координат дотику до активних меж графічного елемента, розглядатися в поточній статті не буде. У випадку приналежності точки дотику до меж даного елементу програма в циклі while починає безперервно виконувати функцію IsTochPress_Func.

 

 

 

Системна функція IsTochPress_Func виконує перевірку на присутність активного сигналу дотику до сенсорної панелі з драйвера останньої.

В вище описаному циклі здійснюються ті самі дії, що були описані на початку розгляду функції ProcessInputData_Func – це зчитування координат дотику і перевірка на приналежність отриманих координат до поточного графічного елементу, що відповідає індексу ітерації Index. Ці дії виконуються для того щоб відслідковувати подію відтискання від сенсорної панелі або виходу точки дотику з активних меж графічного елементу без відтискання від останньої. Тобто якщо говорити на мові програмування – ми відслідковуємо подію «Key Up» та «Move Out». При цьому якщо точка дотику вийде з меж поточного графічного елементу, активна подія для нього закінчується і функція ProcessInputData_Func завершує свою роботу. У випадку відтискання точки дотику на активній площі графічного елементу відбувається обробка події дотику, що розглянемо далі.

Спочатку програма робить перевірку в якому стані до цього знаходився поточний графічний елемент, для чого використовується поле TwoStateSwich_CheckState структури останнього.

 

 

 

Далі, якщо остання умова виконується, спочатку програма змінює значення поля структури елементу TwoStateSwich_CheckState на активний ввімкнутий, для чого використовується константа TWO_STATE_SWITCH_ON, далі відбувається виклик вже відомої нам функції TwoStateSwichVisible_Func, і завершує роботу виклик попередньо заданої функції обробника події дотику, для якої в якості параметра передається активний стан нашого перемикача.

 

 

 

У випадку, якщо вище описана умова не виконується, програма здійснює всі ті самі операції тільки вже з використанням константи TWO_STATE_SWITCH_OFF.

 

 

Застосування розробленої бібліотеки в основній програм 

Тепер розглянемо як застосувати всі вище розглянуті функції в основній програмі для створення одної сторінки і одного двохпозиційного перемикача.

Для початку в нашій програмі потрібно глобально об’явити структуру нової сторінки типу Page_TypeDef.

 

 

 

Далі в будь-якій функції динамічно створюємо структуру, яка описує, в даному випадку, наш двохпозиційний перемикач.

 

 

 

Всі параметри які приймає остання функція були розглянути вище при її описі. Всі змінні, константи та вказівники на масиви даних об’явлені глобально в основній програмі або в допоміжному файлі, і в поточній статті розгляду не будуть підлягати.

Наступна дія – ініціалізація вище об’явленої сторінки Page1 і, за допомогою функції AddPageControl_Func добавлення до неї створеного вище перемикача.

 

 

 

Останнє, що залишається – це відобразити нашу сторінку з добавленим до неї перемикачем викликом функції ShowPage з структури створеної сторінки.

 

 

 

По завершенню в основній програмі прописуємо задану нами функцію обробки дотику до графічного елементу Swich_1_Click. В якості параметру, що передається останній є поточний стан нашого перемикача (ввімкнутий / вимкнутий).

 

 

 

Автор: Сергій Корсун, ХНУ (2010 р. вип.), місто Хмельницький.