Подключение vga к stm32
Достался мне нерабочий телевизор PE-1180. Это ни чем не примечательный китайский переносной телевизор с разрешением экрана 800x480 пикселей и диагональю 11.5 дюйма. Телевизор не показывал аналоговое видео (из эфира и с композитного входа) и у него было что-то с питанием — при подаче напряжения на него с лабораторного блока питания, срабатывала защита по току — 3А, и телевизор не включался. При питании от штатного блока питания телевизор работал нормально — и блок питания даже особо и не грелся, видимо, такой бросок тока давали какие-то переходные процессы. Что это было — особенности китайской схемотехники или проблема телевизора — непонятно. К телевизору можно было подключить источник VGA сигнала, правда необычное разрешение не очень-то хорошо сказывалось на картинке.
Поскольку телевизор был уже разобран, и пользы от него не было не было ни какой, я решил попробовать подключить к нему контроллер.
Как подключить модуль LCD с большим разрешением и без видеопамяти к контроллеру — под катом.
Ниже приведена временная диаграмма сигналов для LZ9JG17: clk — тактовый, HSY — горизонтальной синхронизации, DATA — данных.
По какой-то причине в реальности 216 тактовых импульсов отсчитывались от 2 фронта импульса HSY. Для правильной работы индикатора нужно выводить данные о всех 800 видимых пикселях — иначе нормального изображения не добиться.
Для хранения одного кадка потребуется (800*480)/8 = 48000 байт. К сожалению, у контроллера STM32-DISCOVERY всего 8 килобайт ОЗУ. Поэтому на DISCOVERY возможно формировать только текст. При размере знакоместа 8x16 на экране можно расположить 100x30 знакомест — то потребует 3000 байт ОЗУ.
По поводу подключения в модулю. Модуль LCD соединялся с основной платой телевизора через 40-пиновй шлейф, к выводам которого не подпаятся. Однако рядом с разъемом шлейфа на плате модуля имеются круглые тестовые площадки достаточно большого размера, чтобы к ним можно было припаять провод. Поскольку даташит на контроллер LCD у меня был, то прозвонив мультиметром выводы LZ9JG17 и тестовые площадки, я определил назначение тестовых площадок модуля. С цепями питания еще проще — при подключенной плате телевизора измерил напряжения на наиболее толстых дорожках рядом с разъемом, и определил, что где. Модуль требует 5В 0.1А для питания основных цепей и 12В 0.8А для инвертора. Напряжение сигналов, подаваемых на модуль — 3.3В, так как LZ9JG17 питается именно от 3.3 В. Источник 3.3 вольт есть на самом модуле. Сигнал управления на инвертор подается с еще одного вывода разъема, для того включить подсветку, нужно подать на него 3.3В.
Фотография участка модуля LCD с распиновкой.
Подписи соответствуют подписям выводов LZ9JG17 в даташите. Площадки HENAB, HRVC, HRVC остаются не подключенными.
Что интересно, диод на плате установили китайцы — когда я в первый раз разбирал телевизор, отверстия под винты были закрыты заводской наклейкой.
Вывод VSY модуля у меня соединяется с выводом PC5 платы DISCOVERY, HSY — с PC4, clk — с PA5(SPI_SCK), запараллеленные входы данных — с PA7(SPI_MOSI).
Опыт показал, что вывод clk нужно соединить с землей через конденсатор 18пФ, иначе возникают артефакты изображения.
Фотография конструкции в сборе:
Фотография готового устройства:
Работает LCD модуль стабильно, мерцание экрана не заметно. В принципе, подобным образом к DISCOVERY можно подключить любой экран с RGB интерфейсом, лишь бы был даташит на контроллер LCD и индикатор мог работать на соответствующей частоте развертки(у меня 17 Гц).
Позже хочу подключить модуль к более мощному контроллеру с 64 Кбайт памяти, что позволит организовать настоящую видеопамять и выводить на экран любую графику, и сделать что-то вроде настенного календаря-будильника с wifi, что позволит показывать прогноз погоды из интернета, синхронизировать данные будильника с компьютером, показывать заметки, сделанные на компьютере. Эдакий шаг к умному дому.
Thinking about old video game consoles and arcade machines (very old, like those in the 70’s/80’s) it came to our minds what can be done today using very low-cost microprocessors. Generally, these microprocessors weren’t even created to do this task, so the challenge began, and we started to think the way to output video to a screen with few or no external components at all.
We have picked a 36-pin, 72 MHz STM32 (STM32F103T8U6), fast enough to generate monochrome video synchronism and dot signals. We use a couple of timers and the SPI (this way the refresh of the frame buffer is done automatically). And the final result is a pretty decent monochrome VGA output with 400 x 200 dots resolution.
The list of materials:
- A board with a STM32F103T8U6 or similar. We use the AK-STM32-LKIT.
- A female VGA connector (DB15).
Even if the frame buffer is 400×200 pixels length, the output resolution is 800×600 at 56Hz. We will be painting every horizontal dot twice, and every line will be repeated 3 times. This way we fill the entire screen.
Another reason we have chosen 800×600 @ 56Hz is because of the pixel clock: this resolution uses a 36 MHz pixel clock, that is a multiple of 72Mhz, the frequency of the STM32. Since we will be generating the pixels signal with the SPI, we can divide the STM32 clock with the SPI preescaler to get a 18MHz pixel clock, and paint every pixel twice. The SPI MOSI line will stay high or low twice the time needed to output a single pixel for a 800 pixels horizontal resolution.
The frame buffer is composed of an array of 52×200 bytes. 50 x 8 = 400 pixels (every bit is a pixel). The two remaining bytes will simulate the blanking interval for every line.
Everything we write in this piece of RAM will be output directly to the screen without intervention of the application: the DMA is set to automatically read from the frame buffer and output the values to the SPI MOSI pin.
The horizontal synchronism
The horizontal synchronism signal and the back porch time are generated using the TIM1 timer, channels 1 and 2 (respectively). The TIM1 channel 1 is connected to the pin PA8.
The H-SYNC timer 1 channel 1 (pin PA8) will actually generate the horizontal synchronism that the monitor will receive.
The H-BACKPORCH timer 1 channel 2 signal is calculated from the sum of the horizontal synchronism time and the back porch time. This timer will generate an interrupt that will be used to fire the DMA request to start sending pixels through the SPI.
This is repeated for every line in the frame buffer.
The vertical synchronism
The vertical synchronism is generated using the TIM2 timer, but in slave mode. The TIM2 timer counts the H-SYNC pulses generated by its master, the TIM1 timer.
The TIM2 timer channel 2 outputs the V-SYNC pulse through pin PA1.
The TIM2 timer channel 3 will trigger an interrupt when the timer counter reaches the sum of the V-SYNC and vertical back porch time. This interrupt will set a variable indicating that the scanning is within a valid frame and the DMA can start sending pixels to the screen.
Pixel generation
Pixels are generated using the SPI MOSI pin (PA7). The timer TIM1 channel 2 generates an interrupt that will enable DMA TX requests to the SPI. The DMA will read a line from the frame buffer and will put the values in the SPI DR register.
The DMA is set to generate an interrupt after a single line is sent, where the line number is incremented. Since we are sending each line three times, we will increment a counter in this interrupt. When the three lines have been sent, we set the DMA pointer to the next line in the frame buffer.
When all the lines have been sent, the DMA cycle is disabled until the next valid frame interrupt (TIM2 channel 3).
Connections
To use this example you will only need some wires and a female VGA connector.
The VGA standard says that the output signals should be 0,7V to 1V, so you may want to put a voltage divider in the pixel line (serial 68 ohm resistor and a 33 ohm resistor to ground, better with a 47pF in parallel with the 68 ohm resistor). We have tested a couple of LCD monitors without the divider and it went just fine.
Note that the pin layout is referred to the AK-STM32-LKIT expansion connector, but the pin names are valid for any STM32. Check your chosen STM32 datasheet to see if the timers and SPI pins matches the design.
Note: we use the green color (pin 2 of the VGA connector) to emulate the old style green phosphor monitors, but you can use another color combination using the RED/GREEN/BLUE DB15 pins. It is possible to create up to 8 color combination.
AK-STM32-LKIT pin | VGA connector pin | Description |
PA1 | Pin 14 | Vertical sync |
PA7 | Pin 2 | Green |
PA8 | Pin 13 | Horizontal sync |
GND | Pin 5 | Ground |
Conclusion
We have created a VGA controller using a very low cost microprocessor/development board. The method used it’s certainly not the only way to do it, but this one uses no external components besides a VGA connector.
If you are using this example with a bigger STM32, you can try to use double buffering and to write to the frame buffer while the DMA is disabled, to avoid tearing.
You may download the source code for this project. There you will also find a utility library to draw lines, points, circles, bitmaps, character generation, bit blit and more.
In the next blog entry we will be implementing a video game using this VGA example.
В статье Текстовый VGA-модуль для микроконтроллеров я рассказывал о доработанном варианте терминала 64х30 символов. 15 цветов. Всё хорошо, сделал плату, допилил софт под себя, но текст это текст. Только символы и только 8х16. Можно конечно рисовать псевдографикой. Таблички выходят на ура. Я даже написал колхозную функцию увеличения цифр. Работает достаточно шустро:
И что-то можно изобразить похожее на интерфейс
Но, черт возьми, как рисовать диагональные линии ? В принципе, если хорошенько дунуть пораскинуть мозгами, то можно из этой таблицы символов
выкинуть всё не нужное и заменить на своё. Как собственно это сделано с логотипом Microchip. (Символы с кодами с 0x80 по 0x98, выстроенные в 5 рядов по 5 символов, образуют графическое изображение логотипа Microchip.) Ну то есть взять и выкинуть например английский алфавит. Или русский. Или всякие стрелочки и параграфы с тильдами и стрелочками. Получаем кучу символов, которые можно перерисовать и сделать части окружностей, дуг, диагональные линии и.т.д. Потом наколбасить библиотеку, которая будет рисовать кривые этой псевдографикой. Но что-то это сильно пахнет адским геморроем и я начал искать варианты с графикой.
Конечно, выводить на VGA-монитор информацию средствами микроконтроллера — это жесть. Те, кто знает что для этого нужно делать и с какой скоростью, меня поймут. А тем, кто не знает, 2 варианта: 1) читать принцип формирования vga-сигналов 2) поверить на слово, что борьба идет за каждый такт.
В этом плане RaspberryPi Zero и дешевле и функциональнее. Как же так ? Неужели невозможно по-простому сделать выход 640*480 с разрешением 640*480 ? Просто сделать дисплей 12864 так, чтобы он был 640480 🙂 Для ч/б варианта это 38,4кБ памяти. (видимая область). Вообще ни о чем по нынешним меркам. То есть diy-мейкеры довольствуются дисплейчиками 1602, 2004, 12864 и всякими 0.96″ OLED. Нет, ну я тоже их использую, когда это необходимо. Но только когда это необходимо и достаточно.
Но это всё «синяя изолента». О проекте контроллера для сауны напишу позднее.
Вот в нем планировалось использование этих дисплеев. А как же развитие ? А как же стремление вперед ? То есть крайности какие-то. Или быстро, дешево и убого 2″, или медленно и дорого, но в полном разрешении и цветности. 21-ый век на дворе, а с ардуины мы можем максимум куда вывести изображение — это на модуль 320*240, который 5″ LCD, задействуя кучу ног. О быстродействии не буду судить, ибо даже не пытался. Причем это как минимум должна быть arduino mega на Atmega2560. Для нее шилд и делался. Младшие довольствуются вышеупомянутыми сине-белыми штуками. Есть Nextion HMI, но опять же цена. Проще купить самый дешевый планшет и забыть про микроконтроллеры, погрузившись в программирование под Андройд.
Отвлекся я сильно. Вижу что утомил 🙂 Ну так вот… Тот же самый Игорь прислал мне «в подарок» на ДР схему графического терминала 640*480 одноцветный. И код к ней!. Как знал что я мучаюсь…
Уж не знаю, его это разработка или нет, не суть, все равно большое человеческое спасибо за идею. На тот момент для меня vga конечно был темный лес, но код-то на СИ! Ух я сейчас развернусь!… и развернулся. Как обычно, прога была не доделана, но, как известно, дареному коню в зубы не смотрят 🙂 Такое чувство, что в порыве стремления к прекрасному люди просто в какой-то момент перегорают идеей и бросают. Резко и навсегда. Тут надо помнить про Леонардо да Винчи, который 12 лет губы Моны Лизы рисовал. Потому-что часто отвлекали: то сделай им водокачку, то кран подъёмный, то летать хотим… 🙂 Это наверное про теорию «80/20». Когда 80% работы делается за 20% времени. Остальные 20% работы делается за 80% времени. Но уже лень столько времени тратить ради мизерного результата по сравнению с уже сделанным. За собой тоже такое замечал, но нашел по-моему гениальный выход из этого адского круга: всеми своими проектами я занимаюсь по спирали. Перегорел одним — пошел развивать другой. Потом вернулся. Ну, например, свежая мысль пришла.
Итак, опять схема без платы… Геморрой… (то же мне, любитель всего готовенького) Ну ничего 🙂 По-быстрому наваял рисунок, сделал плату, как обычно, в духе адского минимализма.
Как-то вот так оно и подключалось: с одной стороны USB, с другой, блин… Монитор! 🙂
Сам удивляюсь как смог всё разместить на плате таких размеров. И тут понеслось… Во-первых код написан как псевдомногозадачная система. Отдельная задача на UART, отдельная задача на изменение буфера, и.т.д. А сам вывод в монитор через DMA. 2 таймера для синхронизаций и весь массив буфера строк непрерывно валится в SPI. Любое фоновое изменение массива моментально отображается на экране. Это гениально. Но мог бы и сам догадаться… наверное… сильно позже. Во-вторых в какой-то момент произошел взрыв срыв. Я удалил всё что было в коде, оставив только инициализацию переменных и DMA. А всё остальное написал заново. Ну не могу я в чужом коде копаться, особенно когда он не дописан. Точнее заточен именно на вывод текста а не графики. Текст меня как-то мало интересовал уже.
// отображение пикселя
case WORK_PUT_PIXEL:
break;
Ну как так-то ? 🙂 Это же самое главное 🙂 А если учесть что 640 это больше чем 1 байт, то для передачи координат одного пикселя нужно использовать 2 байта на X и 2 байта на Y… И писал я долго и упорно…
В итоге функция рисования линии по двум точкам стала одной функцией а не тремя. (Хотя я понимаю, это было для быстродействия). Мой код в этом нисколько не проиграл. Функцию рисования окружностей сделал по алгоритму Брезенхэма. У Этого дядьки окружности более округлые. Переписал все команды по-своему, а их там 31. Сделал рисование не только белым, но и черным. Задаваемую толщину линий. Закраску прямоугольников и окружностей. Встроил второй громадный шрифт. Запилил пропорциональное увеличение шрифта 8*16 на заданную величину множителя. Убил весь обратный вывод в UART. Не нужно ничего отвечать на команды. Просто рисуй быстрее. Одно подтверждение обработки команды тормозит вывод графики в десятки раз. Естественно добавил кольцевой буфер на 1024 байта. Конечно UART в этом смысле достаточно убогий по скорости интерфейс, но зато более универсальный. Во-первых, в микроконтроллере их может быть несколько, а во-вторых, можно и в ПК воткнуть. I2C интерфейс убил по-этой же причине. Да и в скорости он не сильно выигрывает, особенно софтварный.
Далее пошли первые тесты. (фотографировал на тапок)
Округлые окружности
Прямоугольнички
А дальше пошел писать библиотеку для ардуино. В этом плане всё проще. Берем стандартный класс SoftwareSerial и наследуемся от него, добавляя свои функции.
Можно конечно юзать и HardwareSerial, но их в ардуине гораздо меньше чем SoftwareSerial 🙂 Cофтовый UART спокойно работает на 115200 и в arduino nano (Atmega328) и в arduino pro micro (Atmega32u).
class VgaSoftwareSerial : public SoftwareSerial <
public:
VgaSoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
void cls(void);
void pixel(int x1, int y1, uint8_t color);
void setcolor(uint8_t color);
void setcursor(uint8_t x1, uint8_t y1);
void line(int x1, int y1, int x2, int y2, uint8_t color);
void rectangle(int x1, int y1, int x2, int y2, uint8_t color, uint8_t filling);
void circle(int x1, int y1, uint8_t color, uint8_t radius, uint8_t filling);
int version(void);
Собственно всё рисование сводится к последовательной подаче байт в UART.
void VgaSoftwareSerial::pixel(int x1, int y1, uint8_t color)
<
uint8_t buf[] = x1, // значение X1 Lo
0x10, // команда установки X1 Hi
x1 >> 8, // значение X1 Hi
0x13, // команда установки Y1 Lo
y1, // значение Y1 Lo
0x12, // команда установки Y1 Hi
y1 >> 8, // значение Y1 Hi
0x03, // команда установки цвета пикселя
color, // значение цвета пикселя
0x0A // команда рисования пикселя
>;
VgaSoftwareSerial::write(buf, 11);
>
А так рисуется линия:
void VgaSoftwareSerial::line(int x1, int y1, int x2, int y2, uint8_t color)
<
uint8_t buf[] = > 8, 0x13, y1, 0x12, y1 >> 8, 0x15, x2, 0x14, x2 >> 8, 0x17, y2, 0x16, y2 >> 8, 0x03, color, 0x0B>;
VgaSoftwareSerial::write(buf, 19);
>
В самом скетче всё еще проще:
И на обоих мониторах рисуется точка по центру.
Обкатанные платы были заново отрисованы в DipTrace и отправлены на завод.
Изменилась схема питания. Применен прекраснейший DC-DC преобразователь LM3671MF. Платы стали еще меньше.
И это, на минуточку, PIC32. Расту над собой 🙂
Такими же нехитрыми командами был наколбашен тест всего что есть в модуле.
В дальнейшем использовал сей девайс для отрисовки приборной панели для второй версии автомобильчика.
Ну и 3D до кучи:
Данный VGA-модуль доступен в 4х вариантах исполнения и вы можете его приобрести в соответствующем разделе.
Буду рад вопросам и комментариям. Еще больше буду рад рационализаторским предложениям.
Вот, случайно наткнулся на статью по формированию монохромного VGA видеосигнала (разрешение 400х200). Ничего революционного в этом нет, но данная реализация мне понравилась своей простотой и лаконичностью. Нет никакой дополнительной внешней обвязки, 2 таймера для формирования синхронизации и SPI в режиме DMA для вывода изображения из буфера. Все построено на прерываниях/DMA, вычислительная нагрузка на МК минимальна. Теперь осталось придумать где это можно применить :)
Комментарии ( 14 )
О, на основании Вашей статьи я когда-то формировал аналоговый ч/б видеосигнал на AT91SAM7. Приятно познакомиться с автором.
У меня сейчас чем-то похожая задача – наложить монохромное изображение поверх готового аналогового видео. В принципе, ничего особо сложного, но хочется сделать все максимально лаконично и просто.
e_mc2 , очень интересует тема наложения доп. информации на аналоговый VGA сигнал! Пару лет назад пытался что-то подобное сделать, но не удалось добиться нормальной вертикальной синхронизации по кадру. Что, правда логично, МК тогда использовал слабый — ATmega8, разогнанный до 24МГц.
Не поделитесь наработками? Статью бы с удовольствием почитал!
Моделисты давно используют наложение телеметрии о полете на видеосигнал камеры. У меня в архиве вроде есть несколько сохраненных страничек с такими вещами. Довольно просто.
Правда, там сигнал — обычно комплексный, телевизионного формата, но VGA — еще проще, в нем RGB и синхроимпульсы отдельными линиями идут.
Коллега ARMag говорит о «наложении доп. информации на аналоговый VGA сигнал». В VGA вопрс выделения синхронизации не стоит, там и так отдельные сигналы для синхронизации и LM1881 ему не нужна.
А вот у меня стоит как раз задача наложения каринки на аналоговый сигнал, и использование LM1881 – один из вариантов.
Как правильно заметил SWG , есть множество готовых решений для наложения картинки на аналоговый видеосигнал, в моделизме это называется OSD (on-screen display). Например вот реализация на LM1881, а вот без LM1881.
Для наложения графики на VGA решение нужно незначительно переделать (общий принцип тот же, но в VGA все немного по другому, я бы сказал, что там все проще).
Ну, как проще… Проще в том плане, что синхроимпульсы и цвета по отдельным линиям идут. На это «проще» заканчиваются. Взять, например, самый примитивный режим 800х600 (ниже не рассматриваю, так как даже этот на 15 дюймах смотрится уже не очень) с кадровой частотой 56Гц (минимум). Получаем пиксельную частоту 36МГц. В моем случае надо было нарисовать контрастную сетку на экране. Вот и считайте, как проще.
Для справки, стандарт VESA.
Ну, я это и имел ввиду.
Начет высоких разрешений и, соответственно, высоких частот – согласен с Вами, там начинаются проблемы. Теоретически, через ногодрыг на МК что-то и можно сделать, но это уже на пределе. ИМХО в таком случае есть смысл либо искать специализированный чип (первое что нагуглилось — STV9427, но там только знакогенерация), либо смотреть в сторону ПЛИС.
Увы, я Вам мало чем смогу помочь, у меня стоит задача наложения изображения именно на аналоговый видеосигнал с камеры.
Через тупой ногодрыг на пределе (даже немного за пределом :) я, в конце-концов, и сделал.
И в сторону ПЛИС смотрел… Но тогда пришлось бы навешивать ещё три внешних АЦП для связи с пользователем (потенциометры) или использовать энкодеры. Короче выходила стрельба с пушки по воробьям.
Понадобилось мне выводить изображение с платы ПЛИС через VGA. А из доступных мониторов оказался только монитор компьютера ))). Естественно подключать туда сюда монитор меня сразу напрягло, а потому решил поискать в интернете что нибудь подходящее. Вот сегодня приехал этот монитор, далее под катом не много подробностей.
И так, решив что надо отдельный монитор для экспериментов, я в первую очередь подумал приобрести монитор «за пиво», но так как минимальный размер обычных офисных мониторов 14", держать такую бандуру мне не хотелось. На ибей же по тегам vga+tft гуглиться замечательный дисплей c платой управления для разбери ПИ типа такого 7 inch TFT LCD Display Monitor Screen For Raspberry pi . Его огромный плюс в том, что за + $10 — 15 продавец может добавить тачскрин и юсб контроллер для него. Таким образом можно сделать из малинки планшет.
Но есть и минус, из-за которого я решил отказаться от такого варианта — вот придет дисплей, а где его держать, корпуса то нет.
Порадовал али, на котором по тегам car+monitor+VGA удалось найти практически тот же набор, но уже в корпусе:
Плюс подставочка и пультик.
Краткие характеристики: разрешение 800*480, 16:9, питание 12 вольт (на 50% яркости потребляет около 250мА, в режиме ожидания 0,02А).
Экран умеет принимать изображение по vga, или по одному из 2 AV входов. Также есть провод выбора av входа (коль вещь для авто видимо активный уровень +12В). Также кнопками спереди можно выполнять простейшие настройки яркости, контрастности, языка, поворот изображения.
А также регулировать звук, вот только никакого вывода для управления звуком я не нашел, хотя корпус и имеет место под динамики. Я даже открыл его, что бы проверить… И только потом, посмотрел, что продавец аудио и не обещал.
Внутренности выглядят так:
Собственно ничего интересного процессор MST703, флеш, да преобразователи напряжения.
Вообщем удобная штука, для работы с разными девайсами требующими экрана. Единственный минус -vga разъем мама, так что придется искать кабель или переходник.
Комментарии ( 10 )
На DX немало подобных дисплейчиков и плат для них. Правда, там обычно в основе лежат реалтековские скейлеры.
Та вроде нормально, просто не знаю с чем сравнить. Цвета так точно правильно передает, яркость — на 50%, мне даже ярковато показалось. Понятно конечно, что это не ips, но имхо заявленным у продавца характеристикам соответствует.
Ну и при прямом солнце скорее всего видно будет плохо (еще не проверял).
Ну вот например 7 Inch TFT LCD Monitor For Raspberry Pi Touch Screen + Driver Board HDMI.
Первое что выдало, но без корпуса. Можно еще поискать на али может подешевле будет.
Я себе присматриваю чего-нибудь подобное чтобы с STM32F4 + какой-нибудь Embedded Linux управлять. VGA-интерфейс поддерживают контроллеры?
М-да, глупый вопрос. Переформулирую — как с контроллера управлять через VGA разъем?
Не совсем вас понял. Вы хотите формировать изображения с помощью STM32F4, а потом через VGA выводить его на монитор. Так это вроде не так сложно, можно обойтись даже r2r цапом. Или вот пример:Формирование монохромного VGA видеосигнала на STM32F103.
А так чем управлять через VGA не ясно (нет там конечно есть i2c для общения с монитором, но в эмбедд проектах ее не используют.
Читайте также: