А надо ли?
Для начала стоит задуматься о целесообразности затеи. Обьясню почему.
В наше время, тема интерфейсов уже далеко не нова. Существуют уже устоявшиеся методики разработки UI's, которые обеспечивают хорошую гибкость кода, удобство использования, а так же упрощают индивидуализацию интерфейса, его отрисовку. Написание UI сейчас - много возможностей, но в случае с AVR микроконтроллерами, эти возможности не совпадают с возможностями МК.
Все верно. возможности AVR микроконтроллеров крайне скудны и сложные интерфейсы идут в ущерб основным задачам программы. К счастью, на рынке есть и другие микроконтроллеры. Например уже полюбившийся всем STM32, который работает с интерфейсами чудесно. Но все же, заходя в крайности стоит отметить. Да, на самых продвинутых версиях Stm32, можно делать сложные оконные системы, похожие на UI современных OS, но для таких задач используются и более мощные процессоры, другой подход к построению устройства.
Применимость:
1. Устройства, где необходимо иметь несколько окн с информацией.
2. Изменять параметры на лету в отдельном окне.
3. Устройство, которые выполняют разные действия где нужно наблюдать за ходом действий, используя отдельное окно.
Либа для STM32 с многопоточностью и другими классными плюшками будет, но это уже совсем другая история.
#include
#include // Core graphics library
#include // Hardware-specific library
#include // Touch library
#define ROTATION 2
#define DEBUG 0
#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0
#define LCD_RESET A4
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define YP A2
#define XM A3
#define YM 8
#define XP 9
#define WINDOW 0
#define TEXT 1
#define BUTTON 2
Adafruit_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
// Определяем окно
typedef void (* DigitFuncPtr) (); // Для хранения событий
struct Screen // Контейнер для данных окна.
{
byte type; // Тип: Окно, кнопка или текст
byte x; // Позиция на экране по X
byte y; // Позиция на экране по Y
byte width; // Ширина
byte height; // Высота
String value; //Значение (текст)
int param; // вспомогательные параметр
int color; // Цвет текста
int background; // Цвет фона
DigitFuncPtr onclick; //Событие при нажатии если кнопка и если окно - событие при отрабатывании
int activetextcolor; // цвет HOVER текста
};
Screen const *current; // Указатель на активное окно
size_t current_size=0; // Размер активного окна
void Main(){ // Действия, происходящие во время активности окна Main
/* Здесь какой-то выполняемый в цикле код */
}
void Window1_Work(){ // Действия, происходящие во время активности окна WINDOW1
/* Здесь какой-то выполняемый в цикле код */
}
void Window2_Work(){ // Действия, происходящие во время активности окна WINDOW2
/* Здесь какой-то выполняемый в цикле код */
}
Screen const Window1[2]={ // параметры окна сканирования
{WINDOW,50 /*text x*/,5 /*text y*/,240,320,"WINDOW 1",1,WHITE,BLACK,Window1_Work /*Здесь функция которая крутится во время показа окна */,NULL},
{BUTTON,20,20,70,30,"Back",1,WHITE,GREEN,toMain,BLACK}
};
Screen const Window2[2]={ // параметры окна сканирования
{WINDOW,50 /*text x*/,5 /*text y*/,240,320,"WINDOW 2",1,WHITE,BLACK,Window2_Work /*Здесь функция которая крутится во время показа окна */,NULL},
{BUTTON,20,20,70,30,"Back",1,WHITE,GREEN,toMain,BLACK}
};
/* События */
void OpenWindow1(){ // событие для включение окна сканирования
DrawScreen(window1,2);
}
void OpenWindow2(){ // событие для включение окна сканирования
DrawScreen(window2,2);
}
Screen const main[7]={
{WINDOW,50 /*text x*/,5 /*text y*/,240,320,"Zagolovok okna",1,WHITE,BLACK, Main /*Здесь функция которая крутится во время показа окна */,NULL},
{BUTTON,20,20,200,30,"Open Window 1",1,WHITE,GREEN,OpenWindow1,BLACK},
{BUTTON,20,60,200,30,"Open Window 2",1,WHITE,GREEN,OpenWindow2,BLACK},
};
void toMain(void){ // Событие "Домой". можно повесить на отдельную кнопку, как на смартфонах, а можно указывать в кнопке назад, в окнах.
DrawScreen(main,7);
}
void DrawScreen(const struct Screen *screens,size_t size){ //Рисуем экран
for (int sn;sn
if(screens[sn].type==BUTTON){ //Рисуем кнопку
tft.setCursor(screens[sn].x + (screens[sn].width/4), screens[sn].y + (screens[sn].height/3+screens[sn].param));
tft.setTextColor(screens[sn].color); tft.setTextSize(screens[sn].param);
tft.print(screens[sn].value);
tft.drawRect(screens[sn].x, screens[sn].y, screens[sn].width, screens[sn].height, screens[sn].background);
}
if(screens[sn].type==WINDOW){ // Рисуем окно
tft.fillScreen(screens[sn].background);
tft.setCursor(screens[sn].x, screens[sn].y);
tft.setTextColor(screens[sn].color); tft.setTextSize(screens[sn].param);
tft.print(screens[sn].value);
}
if(screens[sn].type==TEXT){ // Рисуем текст
tft.setCursor(screens[sn].x, screens[sn].y);
tft.setTextColor(screens[sn].color); tft.setTextSize(screens[sn].param);
tft.print(screens[sn].value);
}
}
current=screens; //Указываем активное окно
current_size=size; // И его размер
}
void TouchEvents(void){
TSPoint p;
byte x,y;
p = ts.getPoint(); //Получаем координаты
pinMode(XM, OUTPUT); //возвращаем скрину контрол
pinMode(YP, OUTPUT);
p.x = map(940-p.x, 210, 940, 0, 319)+80; //Это самая сложная часть, подгонка координат сенсора по горизонтали.
p.y = map(p.y, 134,805, 0, 239);-20; // И по вертикали
x=p.y;
y=p.x;
if((p.z > 100) && y<280){
/* Если есть событие.. Где 100 - сила нажатия.
У меня глючил сенсор, по этому указал Y принудительно, чтобы небыло случайных срабатываний. На другом экране такой проблемы небыло*/
for (int sn;sn
if(current[sn].type==2){ //Если это кнопка
if(x>current[sn].x && x
current[sn].y && y /* Здесь определяем попадание в одну из отмечанных областей. Учитываем, что На модуле с SDFP5408, стороны перепутаны. */
tft.setCursor(current[sn].x+(current[sn].width/4), current[sn].y+(current[sn].height/3+current[sn].param)); // Указываем где печатать текст.
tft.fillRect(current[sn].x, current[sn].y, current[sn].width, current[sn].height, current[sn].background); //Рисуем квадратик
tft.setTextColor(current[sn].activetextcolor); tft.setTextSize(current[sn].param); //Указываем параметры текста.
tft.print(current[sn].value); //Печатаем текст
//Рисуем кнопку в нажатом состоянии.
current[sn].onclick(); //Запускаем событие, которое указано в выбранном обьекте.
delay(200); // Необходимая задержка, на срабатывание экрана
}
}
}
}
}
void setup(void) {
tft.reset(); // Начинаем работу с экраном
tft.begin(0x9341); // Указываем, что работаем именно с SDFP5408
tft.setRotation(ROTATION); //Поворачиваем экран
DrawScreen(main,7); // Инициируем первое окно
}
void loop(void) {
TouchEvents(); //Обрабатываем события текущего окна
current[0].onclick(); // Выполняем код для цикла активного окна.
}