11.12.2014, 19:35
|
|
Гуру портала
Регистрация: 27.10.2008
Адрес: ЕС
Сообщений: 10,835
Сказал спасибо: 918
Сказали Спасибо 4,308 раз(а) в 2,573 сообщении(ях)
|
Пишем свое API
В данной статье мы разберемся с базовыми принципами построения пользовательского интерфейса и познакомимся с некоторыми хитростями программирования на языке С.
Итак, любой интерфейс начинается с самого элементарного – кнопки. Как нарисовать кнопку на экране? Как заставить кнопку реагировать на нажатие сенсорной панели? Как вызывать процедуру - обработчик действия? Но, обо всем по порядку:
Чтобы нарисовать кнопку, нам нужно определиться, какими свойствами она должна обладать. Их множество, если кто помнит Windows Form или знает WPF. Но аппаратных мощностей x86 у нас нет, и мы поскромничаем – обойдемся лишь необходимыми. Опишем их некоей структурой:
Код:
|
typedef struct
{
unsigned int StartX, StartY; //location
unsigned char SizeX, SizeY; //size
char * Caption; //text caption
int FontColor, BackColor; //color palete
void (*Handle)(unsigned char BNum); //handler void
FlagStatus Exist; //attribute exist
FlagStatus Drawn; //attribute drawn
FunctionalState Active; //attribute active
FlagStatus isPressed; //attribute isPressed
FlagStatus isSelected;
}Button_TypeDef; |
Давайте разберемся в них. StartX и StartY, как ясно из названия, определяют расположения верхней левой вершины прямоугольника, коему суждено стать изображением кнопки на дисплее.
Далее идет описание размеров кнопки. Я посчитал, что размер кнопки вряд ли превысят 255 пикселей и оставил по байту на размер по Х и по Y. Указатель Caption представляет собой ссылку на массив элементов типа char, в которых будет храниться надпись на кнопке в виде текста.
Переменные FontColor и BackColor описывают цвет шрифта и цвет фона соответственно в системе кодировки цвета 5-6-5.
Указатель на процедуру Handle() есть обработчик нажатия на кнопку. С ним мы разберемся позже.
Энумерованные типы FlagStatus и FunctionalState я позаимствовал из файла stm32f10x.h, но для партирования на другую платформу их придется определить отдельно. Впрочем, можно обойтись и простой переменной типа byte т.к. кроме сравнения на ноль и не ноль эти свойства не используются. Для них можно было бы применить тип bool, если бы я верил в то, что это чем-то поможет общему делу.
Определившись со структурой, мы создаем отдельно файлы .с и .h, где сразу декларируем локальный массив структур Button_TypeDef:
Код:
|
Button_TypeDef Buttons[max_buttons]; |
Где константа max_buttons определена дефайной:
#define max_buttons 5 – количество кнопок, которое вы хотите использовать.
Итак, мы готовы нарисовать нашу первую кнопку:
Код:
|
FlagStatus ButtonDraw (unsigned char BNum)
{
char TextLen;
if (Buttons[BNum].Drawn == SET) return RESET;
TextLen = StringLen(Buttons[BNum].Caption);
if (Buttons[BNum].SizeX == 0) Buttons[BNum].SizeX = TextLen * font_width + standart_sizeX;
if (Buttons[BNum].SizeY == 0) Buttons[BNum].SizeY = standart_sizeY;
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, Buttons[BNum].SizeX, Buttons[BNum].SizeY, Buttons[BNum].BackColor);
GUI_Text(Buttons[BNum].StartX + Buttons[BNum].SizeX/2 - (font_width)*TextLen/2,
Buttons[BNum].StartY + Buttons[BNum].SizeY/2 - font_height/2,
(uint8_t*)(Buttons[BNum].Caption), Buttons[BNum].FontColor,
Buttons[BNum].BackColor);
/* Paint Shadow Frame */
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY + Buttons[BNum].SizeY - frame_width, Buttons[BNum].SizeX, frame_width, Black);
LCD_FillWindow(Buttons[BNum].StartX + Buttons[BNum].SizeX - frame_width, Buttons[BNum].StartY, frame_width, Buttons[BNum].SizeY, Black);
if (Buttons[BNum].isSelected == RESET)
{
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, Buttons[BNum].SizeX, frame_width, White);
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, frame_width, Buttons[BNum].SizeY, White);
}
else
{
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, Buttons[BNum].SizeX, frame_width, Black);
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, frame_width, Buttons[BNum].SizeY, Black);
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY + Buttons[BNum].SizeY - frame_width*2, Buttons[BNum].SizeX, frame_width*2, Black);
LCD_FillWindow(Buttons[BNum].StartX + Buttons[BNum].SizeX - frame_width*2, Buttons[BNum].StartY, frame_width*2, Buttons[BNum].SizeY, Black);
}
Buttons[BNum].Drawn = SET;
return SET;
} |
Функция получилась не маленькая, но давайте в ней разберемся. Для начала мы убедимся, что кнопка не нарисована. В противном случае рисовать ее никакой необходимости нет и можно вернуть статус SET, сообщающий об успешности процедуры. Получив в ответ SET, мы можем быть уверены, что кнопка нарисована. Далее идет две строки, в которых мы убеждаемся, что размеры кнопки не равны нулю. В противном случае процедура сама рассчитывает длину и ширину кнопки, зная количество символов в строке Caption и ширину каждого символа. Т.к. мы используем шрифт 8x16, то соответствующие значения определены дефайном:
Код:
|
#define font_width 8
#define font_height 16
#define frame_width 1
#define standart_sizeX 20
#define standart_sizeY 30 |
Где frame_width – ширина рамки с тенью, которая рисуется в конце функции отрисовки.
Как хорошо заметно, я из всех графических методов я использую только два – рисование шрифта GUI_Text и заполнение окна LCD_FillWindow. Последний я считаю наиболее важным и достойным внимания. Он состоит из двух частей. Первое – это определение координат окна. Мы передаем значения координат в регистры контроллера дисплея. И после этого просто заливаем память сплошным потоком, что позволяет не отвлекаться на ногодрыг в случае применения SPI интерфейса или, как в нашем случае, используя модуль управления внешней памятью FSMC, значительно ускоряющего процесс заливки. Еще одним интересным вариантом было бы выделить участок оперативной памяти, построить изображение кнопки в нем и после «вылить» содержимое буфера в GRAM по средствам DMA. Но т.к. я использую простейшую операционную систему FreeRTOS, особой необходимости в этом нет. Все и так прекрасно успевает отрисовываться и обрабатываться.
Теперь у нас есть кнопка, которую мы нарисуем, если определим структуру и вызовем функцию DrawButton(номер кнопки). Но как определить структуру, которая находится в отличном от main.c файле? Разумного было бы воспользоваться методом extern и описать структуру в файле инитиализации. Но это показалось мне не спортивно. Для доступа к процедуре я написал такую функцию:
Код:
|
Button_TypeDef * ButtonStructPointer (unsigned char BNum)
{
return &Buttons[BNum];
} |
Она возвращает адрес нашего массива структур в памяти. Для определения структуры в другом файле мы декларируем указатель на эту структуру:
Button_TypeDef * Button;
Определяем, что собираемся работать с нулевым элементом нашего массива, т.е. структурой кнопки номер 0:
Код:
|
Button = AddButton(0); |
И далее просто определяем необходимые свойства кнопки, например:
Код:
|
Button-›Active = ENABLE;
Button-›BackColor = Grey;
Button-›Caption = "OK";
Button-›Exist = SET;
Button-›FontColor = Black;
Button-›SizeX = 80;
Button-›SizeY = 30;
Button-›StartX = 80;
Button-›StartY = 100;
Button-›Handle = Button0_Handle;
DrawButton(0); |
Хорошо видно, что вызвав функцию DrawButton(0) мы нарисовали серую кнопку с черной надписью «ОК» размером 80х30 где-то в первой половине экрана.
Важный момент здесь – обработчик Button0_Handle. Процедура обработки нажатия на кнопку 0 «ОК». К примеру, поморгать светодиодом:
Код:
|
void Button0_Handle (void)
{
GPIOB-›ODR = GPIOB-›IDR ^ GPIO_Pin_1;
} |
Так что пришло время поговорить об обработчике. Он вызывается некоторой процедурой ButtonsHandler(int X, int Y), которой мы передаем координаты нажатия на тачпанель. Процедура пробегаем по всем существующим кнопкам и ищет, не попадает ли данная координата в область какой-либо кнопки. В случае попадания, вызывает обработчик Button-›Handle.
Обработчик может быть один на все кнопки сразу т.к. получит в качестве параметра BNum номер нажатой кнопки, либо индивидуальный на каждую кнопку в зависимости от поставленной задачи.
Код:
|
FlagStatus ButtonsHandler (int X, int Y)
{
char cnt;
FlagStatus res = RESET;
for (cnt = 0; cnt != max_buttons; cnt++)
if (ButtonCheck(cnt, X, Y) == SET) res = SET;
return res;
} |
И, собственно, сама процедура поиска:
Код:
|
FlagStatus ButtonCheck (unsigned char BNum, int X, int Y)
{
if (Buttons[BNum].Exist == RESET) return RESET;
if (Buttons[BNum].Active == DISABLE) return RESET;
if (Buttons[BNum].Drawn == RESET) return RESET;
if ((X › Buttons[BNum].StartX) && (X ‹ (Buttons[BNum].StartX + Buttons[BNum].SizeX))
&& (Y › Buttons[BNum].StartY) && (Y ‹ (Buttons[BNum].StartY + Buttons[BNum].SizeY)))
{
if ((Buttons[BNum].Handle != (void*)0) && (Buttons[BNum].isPressed == RESET))
{
Buttons[BNum].Handle(BNum);
Buttons[BNum].isPressed = SET;
return SET;
}
return RESET;
}
else
Buttons[BNum].isPressed = RESET;
return RESET;
} |
Триггер isPressed нужен для того, чтобы обработка события происходила единожды до выхода координат за поле объекта. Иначе говоря, пока палец давит на кнопку, процедура выполняется только один раз.
Так же нам понадобится еще три процедуры:
Код:
|
void ButtonDelete (unsigned char BNum)
{
Buttons[BNum].Handle = (void*)0;
Buttons[BNum].Exist = RESET;
Buttons[BNum].Drawn = RESET;
Buttons[BNum].Active = DISABLE;
Buttons[BNum].isPressed = RESET;
Buttons[BNum].isSelected = RESET;
}
void ButtonHide (unsigned char BNum, int BackColor)
{
LCD_FillWindow(Buttons[BNum].StartX, Buttons[BNum].StartY, Buttons[BNum].SizeX, Buttons[BNum].SizeY, BackColor);
Buttons[BNum].Active = DISABLE;
Buttons[BNum].Drawn = RESET;
}
void ButtonSelect (unsigned char BNum, FlagStatus Select)
{
Buttons[BNum].isSelected = Select;
Buttons[BNum].Drawn = RESET;
ButtonDraw(BNum);
} |
Первая удаляет кнопку и освобождает структуру. Вторая прячет кнопку с экрана. Третья включает прямоугольник выделения, если параметр Select отличен от RESET. Полезно для визуализации нажатия на кнопку.
Ну и наконец просто полезная процедура быстрой инитиализации кнопки с параметрами по умолчанию:
Код:
|
void ButtonAddStandart (unsigned int StartX, unsigned int StartY, unsigned int SizeX, unsigned int SizeY, unsigned char BNum, char * txt, void (*Handle)(unsigned char BNum))
{
Buttons[BNum].StartX = StartX;
Buttons[BNum].StartY = StartY;
Buttons[BNum].SizeX = SizeX;
Buttons[BNum].SizeY = SizeY;
Buttons[BNum].FontColor = Black;
Buttons[BNum].BackColor = Grey;
Buttons[BNum].Handle = Handle;
Buttons[BNum].Exist = SET;
Buttons[BNum].Drawn = RESET;
Buttons[BNum].Active = ENABLE;
Buttons[BNum].Caption = txt;
Buttons[BNum].isPressed = RESET;
Buttons[BNum].isSelected = RESET;
ButtonDraw(BNum);
} |
С помощью последней процедуры инитиализация кнопки выглядит так:
Код:
|
ButtonAddStandart(80,100,0,0,0,"OK",Button0_Handle); |
Где 80 и 100 – координаты кнопки, 0, 0 говорят о том, что размер кнопки будет выбран автоматически, «ОК» - текст и Button0_Handle – обработчик.
Дальше я опишу, как нам выделить процедуры и функции, связанные с кнопками API в отдельный класс средствами языка С.
Нам понадобится структура:
Код:
|
typedef struct
{
FlagStatus (*Handler) (int X, int Y);
FlagStatus (*Draw) (unsigned char BNum);
void (*AddStandart) (unsigned int StartX, unsigned int StartY, unsigned int SizeX, unsigned int SizeY, unsigned char BNum, char * txt, void (*Handle)(unsigned char BNum));
void (*Delete) (unsigned char BNum);
void (*Hide) (unsigned char BNum, int BackColor);
void (*Select) (unsigned char BNum, FlagStatus Select);
}ButtonClass_TypeDef; |
Состоящая из указателей на функции, связанные с элементом Кнопка. Далее мы вводим эту структуру в файл проекта:
Код:
|
const ButtonClass_TypeDef ButtonClass = {ButtonsHandler, ButtonDraw, ButtonAddStandart, ButtonDelete, ButtonHide, ButtonSelect}; |
Теперь для обращения к функциям класса, надо в файле .с задекларировать класс так:
Код:
|
extern const ButtonClass_TypeDef ButtonClass; |
И теперь при вызове класса ButtonClass после точки будет выпадать список связанных с ним процедур и функций:
Код:
|
for (i = 0; i ‹ 4; i++)
for (j = 0; j ‹ 3; j++)
ButtonClass.AddStandart(30+j*65,70+i*60,50,50,k++, (char*)(CaptionMatrix[i][j]),Button_Handle); |
Рисует на дисплее матрицу элементов CaptionMatrix:
Код:
|
const char * CaptionMatrix[4][3] =
{{"1", "2", "3"},
{"4", "5", "6"},
{"7", "8", "9"},
{"‹", "0", "OK"}}; |
Проект FreeRTOS под STM32F103VC + SSD1289 на HY32Mini во вложении.
Продолжение следует...
Последний раз редактировалось Easyrider83; 11.12.2014 в 20:18.
|
|
|
Эти 23 пользователя(ей) сказали Спасибо Easyrider83 за это сообщение:
|
-shiva- (22.06.2015), akegor (11.12.2014), andronio (31.08.2015), Ara41 (11.12.2014), baiderin (11.12.2014), BYZON80 (26.08.2015), dosikus (11.12.2014), Ironium (12.12.2014), Kabron (11.12.2014), lisergin (12.12.2014), makakus (24.12.2015), MisterDi (12.12.2014), pinco (12.12.2014), serg_42 (22.06.2015), shyub (12.12.2014), swat24 (12.12.2014), switch0 (14.12.2014), v4575820 (19.05.2016), Viktor2004 (23.08.2015), Vlad German (12.12.2014), V_Hvost (12.12.2014), Zoosman (17.12.2014), С.М.С (11.12.2014) |
|
11.12.2014, 19:51
|
|
Гражданин KAZUS.RU
Регистрация: 03.07.2010
Сообщений: 843
Сказал спасибо: 156
Сказали Спасибо 378 раз(а) в 190 сообщении(ях)
|
Re: Пишем свое API
Как то я хотел предложить сообществу написать открытую и понятную GUI, но потом забыл про цветные дисплеи.
|
|
|
|
11.12.2014, 21:00
|
|
Гуру портала
Регистрация: 20.11.2004
Сообщений: 10,018
Сказал спасибо: 936
Сказали Спасибо 2,270 раз(а) в 1,565 сообщении(ях)
|
Re: Пишем свое API
Может все таки "свой" и "понятный" ?
__________________
Осторожно , злой кот
|
|
|
|
11.12.2014, 21:07
|
|
Гуру портала
Регистрация: 27.10.2008
Адрес: ЕС
Сообщений: 10,835
Сказал спасибо: 918
Сказали Спасибо 4,308 раз(а) в 2,573 сообщении(ях)
|
Re: Пишем свое API
Сообщение от dosikus
|
Может все таки "свой" и "понятный" ?
|
Сначала одно кофе и два булка!
Ладно, Леш, давай тут по делу писать. А то зафлудим тему, а я хотел остальные элементы добавлять потихоньку.
|
|
|
|
12.12.2014, 15:21
|
|
Прописка
Регистрация: 20.08.2008
Сообщений: 263
Сказал спасибо: 305
Сказали Спасибо 85 раз(а) в 45 сообщении(ях)
|
Re: Пишем свое API
За стаатью спасибо.
Не могли бы вы уточнить, на чём это написано, привести принципиальную схему "железа" и "перезалить" ссылку (не открывается).
|
|
|
|
12.12.2014, 15:34
|
|
Гуру портала
Регистрация: 27.10.2008
Адрес: ЕС
Сообщений: 10,835
Сказал спасибо: 918
Сказали Спасибо 4,308 раз(а) в 2,573 сообщении(ях)
|
Re: Пишем свое API
shyub, написано на С, схема очень простая SSD1289 -› STM32 FSMC (NORSRAM1), хотя в данном случае это не имеет никакого отношения к теме. Процедура заливки может быть любой в зависимости от типа контроллера дисплея и интерфейса, а отрисовка шрифта так же должна проводится через заливку.
Ссылка открывается, я проверил.
Сейчас работаю с отрисовкой шрифтов. Тут работы побольше т.к. хочется сделать универсально и быстро. Поэтому потребуется время на это.
|
|
|
|
12.12.2014, 21:54
|
|
Почётный гражданин KAZUS.RU
Регистрация: 08.06.2008
Сообщений: 1,394
Сказал спасибо: 4
Сказали Спасибо 183 раз(а) в 167 сообщении(ях)
|
Re: Пишем свое API
Создать GUI - хорошая мысль, но с такой реализацией, боюсь что могут быть проблемы. Ибо контролы имеют координату Z, которой у Вас нет.
У контрола всегда должен быть родитель ! Контрол никогда не "лезет" раньше "батьки" и знает свое место за "столом". По этому "дереву" производиться отрисовка, обрабока событий и т.д.
Как бы задачку надо решать с другого конца - написать соэдание , изменение этого дерева.Затем процессы отображения, обработки событий по этому дереву.А уж потом создавать контролы.
|
|
|
|
12.12.2014, 22:04
|
|
Гуру портала
Регистрация: 20.11.2004
Сообщений: 10,018
Сказал спасибо: 936
Сказали Спасибо 2,270 раз(а) в 1,565 сообщении(ях)
|
Re: Пишем свое API
Сообщение от Easyrider83
|
Ладно, Леш, давай тут по делу писать.
|
Пока никак, как разгребу предновогодний завал, присоединюсь.
__________________
Осторожно , злой кот
|
|
|
|
12.12.2014, 22:35
|
|
Гуру портала
Регистрация: 27.10.2008
Адрес: ЕС
Сообщений: 10,835
Сказал спасибо: 918
Сказали Спасибо 4,308 раз(а) в 2,573 сообщении(ях)
|
Re: Пишем свое API
Написал отрисовку текста.
Нам понадобится некоторый дефайн:
Код:
|
#define MaxFontSizeXY 21 * 18 |
Память приходится распределять заранее, поэтому жестко фиксируем его за буфером.
Шрифт у нас будет описан такой структурой:
Код:
|
typedef struct
{
const unsigned char StartFrom;
const unsigned char LastChar;
const unsigned char SizeX;
const unsigned char SizeY;
const unsigned char * Code;
}Font_StructTypeDef; |
Ниже добавим 4 шрифта, импортированных при помощи GLCD Font Creator от MikroElektronika
Код:
|
const Font_StructTypeDef Times_New_Roman13x15_FontStruct = {32, 127, 13, 15, Times_New_Roman13x15};
const Font_StructTypeDef Tahoma10x11_FontStruct = {32, 127, 10, 11, Tahoma10x11};
const Font_StructTypeDef Britannic_Bold10x12_FontStruct = {32, 127, 10, 12, Britannic_Bold10x12};
const Font_StructTypeDef Broadway21x18_FontStruct = {32, 127, 21, 18, Broadway21x18}; |
Ну и для упрощения вызова понадобится еще одна залипуха:
Код:
|
const Font_StructTypeDef * Font[4] = {&Times_New_Roman13x15_FontStruct,
&Tahoma10x11_FontStruct,
&Britannic_Bold10x12_FontStruct,
&Broadway21x18_FontStruct}; |
Теперь процедура отрисовки символа:
Код:
|
unsigned char DrawChar (unsigned int StartX, unsigned int StartY, char Symbol, char SelectFont, int Color, int BackColor)
{
extern const Font_StructTypeDef * Font[3];
char x,y,s, k;
static int FontPic[MaxFontSizeXY];
if (Font[SelectFont]-›SizeY ‹ 8) {s = Font[SelectFont]-›SizeX + 1; k = 1;}
if ((Font[SelectFont]-›SizeY ›=8) && (Font[SelectFont]-›SizeY ‹ 16)) {s = Font[SelectFont]-›SizeX * 2 + 1; k = 2;}
if (Font[SelectFont]-›SizeY › 16) {s = Font[SelectFont]-›SizeX * 3 + 1; k = 3;}
Symbol -= Font[SelectFont]-›StartFrom;
if (Symbol › Font[SelectFont]-›LastChar) return 0;
for (y = 0; y ‹ Font[SelectFont]-›SizeY; y++)
for (x = 0; x ‹ Font[SelectFont]-›SizeX; x++)
{
if (y ‹ 8)
{
if (Font[SelectFont]-›Code[Symbol * s + k*x + 1] & (0x01 ‹‹ y)) FontPic[Font[SelectFont]-›SizeX*y+x] = Color;
else FontPic[Font[SelectFont]-›SizeX*y+x] = BackColor;
}
if ((y ›= 8) && (y ‹ 16))
{
if (Font[SelectFont]-›Code[Symbol * s + k*x + 2] & (0x01 ‹‹ (y-8))) FontPic[Font[SelectFont]-›SizeX*y+x] = Color;
else FontPic[Font[SelectFont]-›SizeX*y+x] = BackColor;
}
if (y ›= 16)
{
if (Font[SelectFont]-›Code[Symbol * s + k*x + 3] & (0x01 ‹‹ (y-16))) FontPic[Font[SelectFont]-›SizeX*y+x] = Color;
else FontPic[Font[SelectFont]-›SizeX*y+x] = BackColor;
}
}
LCD_DrawPic(StartX, StartY, Font[SelectFont]-›SizeX, Font[SelectFont]-›SizeY, FontPic);
return Font[0]-›Code[Symbol * s];
} |
Она сложная т.к. кодировка у GLCD Font Creator'a не простая. Первым идет байт длинны символа в пикселях. Но в отличии от нормального шрифта, здесь этот параметр одинаковый на все символы шрифта. Ну так вот мыслят программисты MikroE. Бог им судья. А мы пользуем бесплатную утилиту. Как видно из кода, я вывожу символ на дисплей в виде картинки. Никаких пикселей. Только потоковая заливка.
И, собственно, функция вывода строки:
Код:
|
unsigned int LCD_PrintText (unsigned int StartX, unsigned int StartY, char * String, char Len, char SelectFont, int Color, int BackColor)
{
extern const Font_StructTypeDef * Font[3];
char cnt = 0;
unsigned int TextLen = 0;
while (Len--)
TextLen += DrawChar(StartX + (cnt++) * Font[SelectFont]-›SizeX, StartY, * String++, SelectFont, Color, BackColor);
return TextLen;
} |
Пример:
Код:
|
LCD_PrintText(0,0, "TEXT 1 example", 14, 0, Red, Grey);
LCD_PrintText(0,20, "TEXT 2 example", 14, 1, Blue, Grey);
LCD_PrintText(0,40, "TEXT 3 example", 14, 2, Green, Grey);
LCD_PrintText(0,60, "TEXT 4 example", 14, 3, Black, Grey); |
Или если использовать энумератор:
Код:
|
typedef enum {Times_New_Roman1315 = 0, Tahoma1011, Britannic_Bold1012, Broadway2118} FontEnum; |
Код:
|
LCD_PrintText(0,0, "TEXT 1 example", 14, Times_New_Roman1315, Red, Grey);
LCD_PrintText(0,20, "TEXT 2 example", 14, Tahoma1011, Blue, Grey);
LCD_PrintText(0,40, "TEXT 3 example", 14, Britannic_Bold1012, Green, Grey);
LCD_PrintText(0,60, "TEXT 4 example", 14, Broadway2118, Black, Grey); |
Вот так все просто. Проект с шрифтами прилагаю.
Последний раз редактировалось Easyrider83; 12.12.2014 в 23:13.
|
|
|
Эти 4 пользователя(ей) сказали Спасибо Easyrider83 за это сообщение:
|
|
|
12.12.2014, 22:36
|
|
Почётный гражданин KAZUS.RU
Регистрация: 13.02.2008
Адрес: Днепр. Украина
Сообщений: 3,294
Сказал спасибо: 442
Сказали Спасибо 1,048 раз(а) в 706 сообщении(ях)
|
Re: Пишем свое API
Цитата:
|
У контрола всегда должен быть родитель !
|
Это верно для случая многозадачных систем, где и один и тот же элемент может работать в разных задачах. На МК с ограниченным экраном и фиксированным набором элементов интерфейса, введение дополнительной иерархии ИМХО избыточно.
__________________
misterdi<@>i.ua
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Тема |
Автор |
Раздел |
Ответов |
Последнее сообщение |
свое USB устройство
|
ESWANT |
Микроконтроллеры, АЦП, память и т.д |
2 |
08.09.2009 14:58 |
Часовой пояс GMT +4, время: 18:54.
|
|