Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)

Декілька років тому я познайомився з російськими мікроконтролерами фірми Міландр. Це був 2013 рік, коли інженери бурхливо обговорювали перші результати ФЦП «Розвиток електронної компонентної бази та радіоелектроніки» на 2008-2015 роки. На той момент вже був випущений контролер К1986ВЕ9x (ядро Cortex-M3) і тільки-но з'явився контролер 1986ВЕ1Т (ядро Cortex-M1). Він же в пластиковому корпусі LQFP-144 мав у документації позначення К1986ВЕ1QI (авіа), а на мікросхемі позначення MDR32F1QI. На сайті виробника у нього стоїть суфікс «авіа», оскільки він має специфічні інтерфейси для авіабудування (ARINC 429, MIL_STD_1553).

Дивно, але на момент поширення цих контролерів фірмою «Міландр» були підготовлені налагоджувальні набори та бібліотека підпрограм для роботи з периферією, «але без будь-яких додаткових гарантій та зобов'язань щодо коректності бібліотеки». Бібліотека схожа на Standard Peripheral Library від компанії STMicroelectronics. Загалом, всі ARM-контролери, побудовані на ядрі Cortex-M, мають багато спільного. Тому ознайомлення з новими російськими контролерами йшло швидко. А для тих хто купував фірмові налагоджувальні комплекти надавалася технічна підтримка в процесі використання.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Налагоджувальний комплект для мікроконтролера 1986ВЕ1Т, © Міландр

Однак згодом почали виявлятися «дитячі хвороби» нових мікросхем та бібліотек. Тестові приклади прошивок працювали без видимих ​​проблем, але за суттєвої модифікації сипалися збої та помилки. Першою «ластівкою» у моїй практиці були непояснені збої в роботі CAN-контролера. Через рік на контролері 1986ВЕ1Т (авіа) ранньої ревізії виявилася проблема з модулем МКІО (мультиплексний канал інформаційного обміну). Загалом всі ревізії цих мікроконтролерів до 2016 року були обмежено придатними. Багато часу і нервів пішло на виявлення цих проблем, підтвердження яким зараз можна знайти списках помилок (Errata).

Неприємною особливістю було те, що працювати і розбиратися з помилками доводилося не на платах налагодження, а на платах прототипів приладів, які планувалися для серійного заводського виготовлення. Крім роз'єму JTAG, там зазвичай нічого не було. Підключитися логічним аналізатором було складно та незручно, а світлодіодів та екранів зазвичай не було. З цієї причини у мене в голові з'явилася думка про створення своєї налагоджувальної плати.

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

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Налагоджувальна плата MILANDR LDM-HELPER-K1986BE1QI-FULL, © LDM Systems

У фірми «Міландр» цінова політика та маркетинг своєрідні. Так, є можливість отримати безкоштовно зразки деяких мікросхем, але це доступно лише юридичним особам і пов'язано з бюрократичним квестом. У цілому нині мікросхеми в металлокерамическом корпусі є золотими у прямому і переносному сенсах. Наприклад, контролер 1986ВЕ1Т коштує у Москві від 14 до 24 тисяч рублів. Мікросхема статичної пам'яті 1645РУ6У коштує від 15000 рублів. І такий порядок ціни всю продукцію. У результаті, навіть профільні НДІ з держзамовленням економлять і сахаються від таких цін. Мікросхеми в пластиковому корпусі для цивільного застосування суттєво дешевші, але їх немає в наявності у популярних постачальників. Крім того, якість мікросхем у пластиковому корпусі, як мені здається, гірша за «золоті». Наприклад, я не зміг запустити контролер K1986BE1QI на частоті 128МГц без збільшення параметра flash latency. Одночасно температура цього контролера піднялася до 40-50С. А ось контролер 1986BE1T ("золотий") запустився на 128МГц без додаткових налаштувань і залишався холодним. Він справді гарний.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
"Золотий" мікроконтролер 1986BE1T, (с) Міландр

Мені пощастило, що мікроконтролер у пластиковому корпусі таки можна купити в роздріб у компанії LDM Systems, а всі схеми плат є у вільному доступі. Погано те, що на сайті на фотографії контролера видно маркування, яке говорить, що це 4 ревізії 2014 року, тобто. із дефектами. Я довго думав – купувати чи не купувати. Так минуло кілька років.

Думка про створення налагоджувальної плати нікуди не зникла. Поступово я сформував усі вимоги і думав, як все це розмістити на одній платі, щоби вийшло компактно і не дорого. Паралельно я замовляв у китайців комплектуючі. Я нікуди не поспішав – все робив собі. Китайські постачальники славляться розгильдяйством - мені довелося замовляти те саме в різних місцях, щоб отримати все, що потрібно. Більш того, частина мікросхем пам'яті виявилися вживаними - явно випаяними зі зламаних приладів. Це мені пізніше відгукнулося.

Купити мікроконтролер Міландр К1986ВЕ1QI (авіа) – не просте завдання. У тому ж магазині «Чіп і Діп» у розділі «Позиції на замовлення» я знайшов лише K1986BE92QI за 740 рублів, але він мені не пасував. Єдиний варіант - купити в компанії LDM-Systems за 2000 рублів не свіжу ревізію. Оскільки знайти заміну я ніде більше не зміг, то вирішив купити те, що було. На мій приємний подив мені продали новенький контролер випуску грудень 2018 року, ревізія 6+ (1820). А на сайті досі стара фотографія, і на момент написання статті контролера немає…

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Мікроконтролер К1986ВЕ1QI (авіа) у технологічній упаковці, (с) Фото автора

Основні технічні характеристики моєї налагоджувальної плати MDB1986 наступні:

  • вбудований налагоджувач-програматор, сумісний з J-Link та CMSIS-DAP;
  • статична пам'ять 4Мбіт (256k x 16, 10 ns);
  • мікросхема флеш пам'яті 64Мбіт, Winbond 25Q64FVSIG;
  • приймач інтерфейсу RS-232 з лініями RTS і CTS;
  • інтерфейси та роз'єми для Ethernet, USB, CAN;
  • контролер 7-сегментного дисплея MAX7221;
  • штирьовий роз'єм для роботи з МКИО (MIL_STD_1553) та ARINC429;
  • фототранзистор Everlight PT17-21C;
  • п'ять кольорових світлодіодів, кнопка скидання і дві кнопки;
  • живлення він порту USB 5 вольт;
  • розміри друкованої плати 100 х 80 мм

Мені подобалися плати серії STM-Discovery тим, що є вбудований програматор-відладчик - ST-Link. Фірмовий ST-Link працює тільки з контролерами фірми STMicroelectronics, але кілька років тому з'явилася можливість оновити в ST-Link прошивку та отримати SEGGER J-Link OB (on-board) Debugger. Юридично є обмеження на використання такого налагоджувача тільки з платами STMicroelectronics, але фактично потенціал не обмежений. Таким чином, маючи J-Link OB можна на налагоджувальній платі мати вбудований програматор-відладчик. Зазначу, що в продукції LDM-Systems використовується перетворювач CP2102 (Usb2Uart), який може тільки прошивати.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Мікроконтролери STM32F103C8T6, справжні та не дуже, (с) Фото автора

Отже, необхідно було купити оригінальний STM32F103C8T6, оскільки фірмові прошивки не коректно працюватимуть з клоном. Я сумнівався в цій тезі і вирішив спробувати у роботі контролер CS32F103C8T6 китайської фірми CKS. До самого контролера у мене претензій немає, але фірмова прошивка ST-Link у ньому не запрацювала. J-Link працював частково - USB-пристрій визначався, але свої функції програматор не виконував і постійно нагадував, що він "defective".

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Помилка при роботі відладчика на оригінальному контролері

Я на цьому не заспокоївся і спочатку написав прошивку для миготіння світлодіодом, а потім реалізував запит IDCODE за протоколом JTAG. Програматор ST-Link, який був у мене на платі Discovery, і програма ST-Link Utility прошивали без проблем CS32F103C8T6. У результаті я переконався, що моя плата працює. На мою радість цільовий контролер К1986ВЕ1QI (авіа) бадьоро видавав свій IDCODE по лінії TDO.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Осцилограма сигнальної лінії TDO із закодованою відповіддю IDCODE, (с) Фото автора

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Ось і знадобився порт SWD для налагодження самого відладчика та перевірки IDCODE

Залишався варіант із відладчиком CMSIS-DAP (Debug Access Port). Зібрати проект із вихідників від ARM – справа не проста, я взяв проект у X893, А потім ще спробував DAP42. На жаль, Keil uVision зависав і не хотів із ними працювати. У результаті я замінив мікросхему відладчика фірмової STM32F103C8T6 і більше до цього питання не повертався.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Успішна робота вбудованого відладчика J-Link STLink V2

Коли всі ключові компоненти майбутньої налагоджувальної плати були в наявності, я поліз у Eagle CAD і виявив, що їх немає в бібліотеці елементів. Подітися нікуди – їх довелося креслити самому. Заодно я зробив посадкові місця для пам'яті, роз'єм HanRun для Ethernet, а для резисторів і конденсаторів додав рамочки. Файл проекту та бібліотеку компонентів можна знайти у мене на GitHub.

Принципова схема налагоджувальної плати MDB1986Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)

Живиться плата джерела постійного струму напругою 5 вольт, одержуваних з порту USB. Усього на платі два порти USB Type-B. Один – для програматора, другий – для контролера K1986BE1QI. Плата може працювати від будь-якого з цих джерел або з обома одночасно. Найпростіше регулювання навантаження та захист ліній живлення реалізовані на діодах Шоттки, на схемі D2 та D3 (SS24). Також на схемі можна побачити запобіжники F1 і F2, що самовідновлюються, на 500мА. Сигнальні лінії порту USB захищені діодним складанням USBLC6-2SC6.

Схема відладчика-програматора ST-Link багатьом відома, її можна знайти у документації на плати STM32-Discovery та інших джерелах. Для первинної прошивки клону ST-Link/J-Link-OB/DAP (на вибір) я вивів лінії SWDIO (PA13), SWCLK (PA14), GND. Багато хто використовують для прошивки UART і змушені смикати перемички BOOT. Але мені зручніше SWD, до того ж, цей протокол дозволяє вести налагодження.

Багато компонентів плати живляться від 3.3 вольт, які надходять від регулятора напруги AMS1117-3.3. Для придушення електромагнітних перешкод та кидків струму використовуються LC-фільтри із конденсаторів та дроселів серії BLM31PG.

Окремо варто згадати про драйвер 7-сегментного дисплея MAX7221. Відповідно до специфікації рекомендоване харчування від 4 до 5.5 вольт, а рівень високого сигналу (логічна одиниця) не менше 3.5 (0.7 x VCC), при живленні 5В. У контролера К1986ВЕ1QI (авіа) вихід логічної одиниці відповідає напрузі від 2.8 до 3.3В. Вочевидь не відповідність рівнів сигналів, які можуть порушити нормальну роботу. Я вирішив запитати MAX7221 від 4В та знизити рівні сигналів до 2.8В (0.7 x 4 = 2.8). Для цього послідовно ланцюг живлення драйвера встановлений діод D4 (RS1A або FR103). Отже падіння напруги становить 0.9В (діод Шоттки 0.3В і діод 0.6В), і все працює.

Більшість портів мікроконтролера К1986ВЕ1QI (авіа) сумісні із сигналами до 5В. Тому не викликає проблем використання CAN-приймача MCP2551, який також працює від 5В. Як RS-232 приймач на схемі вказана мікросхема MAX3232, але насправді я використовував SN65C3232D від компанії Texas Instruments, т.к. вона працює від 3.3В та забезпечує швидкість до 1Mbit/s.

На платі розміщено 4 кварцові резонатори – один для відладчика (8МГц) і три для цільового мікроконтролера К1986ВЕ1QI (авіа) номіналами 32.768кГц, 16МГц, 25Мгц. Це компоненти, т.к. параметри вбудованого RC-генератора знаходяться у більших межах від 6 до 10 МГц. Частота 25МГц потрібна для роботи вбудованого Ethernet-контролера. На сайті Міландра (можливо, помилково) чомусь зазначено, що у пластиковому корпусі Ethernet відсутня. Але ми спиратимемося на специфікацію та факти.

Важливим стимулом для створення своєї налагоджувальної плати була можливість попрацювати із зовнішньою системною шиною EBC (external bus controller), яка є паралельним портом. Мікроконтролер К1986ВЕ1QI (авіа) дозволяє підключати та працювати із зовнішніми мікросхемами пам'яті та периферійними пристроями, наприклад, АЦП, ПЛІС тощо. Можливості зовнішньої системної шини досить великі – можна працювати з 8-бітними, 16-бітними та 32-бітними статичними ОЗУ, ПЗУ та NAND Flash. Для зчитування/запису 32-бітових даних контролер вміє автоматично виконувати 2 відповідні операції для 16-бітних мікросхем, а для 8-бітних - 4 операції. Очевидно, що 32-бітова операція введення-виведення виконається найшвидше з 32-бітною шиною даних. До недоліків можна віднести необхідність у програмі оперувати 32-бітовими даними, а на платі доведеться прокласти 32 доріжки.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Мікросхеми статичного ОЗП, б/в (вгадай яка з дефектом)

Збалансованим рішенням є використання 16-бітових мікросхем пам'яті. У мене опинилися мікросхеми Integrated Silicon Solutions Inc. (ISSI IS61LV25616AL, 16 x 256k, 10 ns, 3.3V). Звичайно, фірма «Міландр» має свої мікросхеми статичної пам'яті. серії 1645РУале вони занадто дорогі і недоступні. Як альтернатива є сумісні за висновками Samsung K6R4016V1D. Раніше я згадував, що мікросхеми виявилися вживаними і екземпляр, який я встановив спочатку давав збої та хаотичні значення в 15 лінії даних. Пішло кілька днів на пошук апаратних помилок, і тим більше було почуття задоволення, коли замінив пошкоджену мікросхему на справну. Як би там не було, швидкість роботи із зовнішньою пам'яттю залишає бажати кращого.

Зовнішня шина та режим StandAloneМікроконтролер К1986ВЕ1QI (авіа) має унікальний режим StandAlone, який призначений для прямого доступу ззовні до контролерів Ethernet та МКИО (MIL_STD_1553) по зовнішній шині, причому ядро ​​перебуває у стані скидання, тобто. не використовується. Цей режим зручний для процесорів та ПЛІС, в яких немає Ethernet та/або МКІО.
Схема підключення така:

  • шина даних MCU(D0-D15) => SRAM(I/O0-I/O15),
  • шина адреси MCU(A1-A18) => SRAM(A0-A17),
  • управління MCU (nWR, nRD, PortC2) => SRAM (WE, OE, CE),
  • SRAM(UB,LB) підключені чи підтягнуті до землі через резистор.

Лінія CE підтягнута до живлення через резистор, висновки для вибірки байта MCU(BE0-BE3) не використовуються. Під спойлером наводжу код ініціалізації портів та контролера зовнішньої шини.

Ініціалізація портів та контролера EBC (external bus controller)

void SRAM_Init (void)
{
	EBC_InitTypeDef          EBC_InitStruct = { 0 };
	EBC_MemRegionInitTypeDef EBC_MemRegionInitStruct = { 0 };
	PORT_InitTypeDef         initStruct = { 0 };

	RST_CLK_PCLKcmd (RST_CLK_PCLK_EBC, ENABLE);

	PORT_StructInit (&initStruct);
	//--------------------------------------------//
	// DATA PA0..PA15 (D0..D15)                   //
	//--------------------------------------------//
	initStruct.PORT_MODE      = PORT_MODE_DIGITAL;
	initStruct.PORT_PD_SHM    = PORT_PD_SHM_ON;
	initStruct.PORT_SPEED     = PORT_SPEED_FAST;
	initStruct.PORT_FUNC      = PORT_FUNC_MAIN;
	initStruct.PORT_Pin       = PORT_Pin_All;
	PORT_Init (MDR_PORTA, &initStruct);	
	//--------------------------------------------//
	// Address PF3-PF15 (A0..A12), A0 - not used. //
	//--------------------------------------------//
	initStruct.PORT_FUNC      = PORT_FUNC_ALTER;
	initStruct.PORT_Pin       = PORT_Pin_4  | PORT_Pin_5  |
	                            PORT_Pin_6  | PORT_Pin_7  |
	                            PORT_Pin_8  | PORT_Pin_9  |
								PORT_Pin_10 | PORT_Pin_11 |
	                            PORT_Pin_12 | PORT_Pin_13 |
								PORT_Pin_14 | PORT_Pin_15;
	PORT_Init (MDR_PORTF, &initStruct);	
	//--------------------------------------------//
	// Address PD3..PD0 (A13..A16)                //
	//--------------------------------------------//
	initStruct.PORT_FUNC      = PORT_FUNC_OVERRID;
	initStruct.PORT_Pin       = PORT_Pin_0 | PORT_Pin_1 |
	                            PORT_Pin_2 | PORT_Pin_3;
	PORT_Init (MDR_PORTD, &initStruct);	
	//--------------------------------------------//
	// Address PE3, PE4 (A17, A18)                //
	//--------------------------------------------//
	initStruct.PORT_FUNC      = PORT_FUNC_ALTER;
	initStruct.PORT_Pin       = PORT_Pin_3 | PORT_Pin_4;
	PORT_Init (MDR_PORTE, &initStruct);	
	//--------------------------------------------//
	// Control PC0,PC1 (nWE,nOE)                  //
	//--------------------------------------------//
	initStruct.PORT_FUNC      = PORT_FUNC_MAIN;
	initStruct.PORT_Pin       = PORT_Pin_0 | PORT_Pin_1;
	PORT_Init (MDR_PORTC, &initStruct);	
	//--------------------------------------------//
	// Control PC2 (nCE)                          //
	//--------------------------------------------//
	initStruct.PORT_PD        = PORT_PD_DRIVER;
	initStruct.PORT_OE        = PORT_OE_OUT;
	initStruct.PORT_FUNC      = PORT_FUNC_PORT;
	initStruct.PORT_Pin       = MDB_SRAM_CE;
	PORT_Init (MDR_PORTC, &initStruct);	

	//--------------------------------------------//
	// Initialize EBC controler                   //
	//--------------------------------------------//
	EBC_DeInit();
	EBC_StructInit(&EBC_InitStruct);
	EBC_InitStruct.EBC_Mode             = EBC_MODE_RAM;
	EBC_InitStruct.EBC_WaitState        = EBC_WAIT_STATE_3HCLK;
	EBC_InitStruct.EBC_DataAlignment    = EBC_EBC_DATA_ALIGNMENT_16;
	EBC_Init(&EBC_InitStruct);
	
	EBC_MemRegionStructInit(&EBC_MemRegionInitStruct);
	EBC_MemRegionInitStruct.WS_Active   = 2;
	EBC_MemRegionInitStruct.WS_Setup    = EBC_WS_SETUP_CYCLE_1HCLK;
	EBC_MemRegionInitStruct.WS_Hold     = EBC_WS_HOLD_CYCLE_1HCLK;
	EBC_MemRegionInitStruct.Enable_Tune = ENABLE;
	EBC_MemRegionInit (&EBC_MemRegionInitStruct, EBC_MEM_REGION_60000000);
	EBC_MemRegionCMD(EBC_MEM_REGION_60000000, ENABLE);

	// Turn ON RAM (nCE)
	PORT_ResetBits (MDR_PORTC, MDB_SRAM_CE);
}

Мікроконтролер у корпусі LQFP-144 та пам'ять у корпусі TSOP-44 мають багато зв'язаних висновків та займають багато місця на друкованій платі. Маючи за плечима досвід вирішення оптимізаційних завдань у галузі економіки, мені було очевидно, що розміщувати на платі ці мікросхеми необхідно насамперед. У різних джерелах мені зустрічалися хвалебні відгуки про САПР TopoR (Topological Router). Я скачав пробну версію (trial) і зміг експортувати туди свій проект із Eagle CAD лише тоді, коли вилучив майже всі компоненти. На жаль, навіть 10 елементів програма TopoR не допомогла мені розмістити на платі. Спочатку всі компоненти були вміщені в кут, а потім по краю розставлені. Мене такий варіант не задовольнив, і я довго виконував трасування плати у ручному режимі у звичному середовищі Eagle CAD.

Важливим елементом друкованої плати є шовкографія. На налагоджувальній платі мають бути не лише підписи до електронних компонентів, але й усі роз'єми мають бути підписані. На звороті плати я розмістив таблиці-пам'ятки з функціями портів контролера (основна, альтернативна, перевизначена, фактична). Виготовлення друкованих плат я замовив у Китаї у всій відомій конторі PCBWay. Хвалити не буду, бо якість хороша. Вони можуть зробити краще, з меншими допусками, але за окрему платню.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Виготовлені друковані плати MDB1986, (с) Фото автора

Розпаювати компоненти довелося «на коліні» паяльником 40-ват і припоєм ПОС-61, тому що паяю я рідко, 1-2 рази на рік, і паяльна паста засохла. Довелося міняти китайський контролер CS32F103 на оригінальний STM32F103, а потім ще й пам'ять замінити. Загалом, зараз мене результат повністю влаштовує, хоча ще не перевірив роботу RS-232 і CAN.

Розробка налагоджувальної плати для К1986ВЕ1QI (авіа)
Налагоджувальна плата MDB1986 у роботі — світить і гріє, (с) Фото автора

На сайті «Міландра» можна знайти достатньо навчальних матеріалів для вивчення контролерів серії 1986BE9 (ядро Cortex-M3), але для мікроконтролера К1986ВЕ1QI (авіа) я не бачу там нічого. Переглянувши опубліковані там матеріали, методички та лабораторні роботи для ВНЗ, я тішуся, що готуються кадри по всій країні для роботи з російськими контролерами. Більшість навчальних матеріалів готують до роботи з портами вводу-виводу, таймерами, АЦП, ЦАП, SPI, UART. Використовуються різні середовища розробки IDE (Keil, IAR, CodeMaster). Десь програмують за допомогою регістрів CMSIS, а десь використовують MDR Library. Необхідно згадати ресурс Start Milandr, який містить багато статей від програмістів-практиків. І, звичайно ж, слід не забувати про форум Міландра.

Дума про МіландруМікроелектроніка у Росії розвивається, й у процесі компанія «Міландр» грає помітну роль. З'являються нові цікаві мікроконтролери, наприклад, 1986ВЕ81Т і «Електросила» з інтерфейсами SpaceWire та МКІО (такий як у 1986BE1 і, можливо, з тими ж проблемами), і т.д. Але простим студентам, викладачам та цивільним інженерам купити такі мікросхеми не реально. А отже співтовариство інженерів не зможе швидко виявити помилки та проблеми цієї мікросхеми. Мені здається, що спочатку необхідно випускати мікросхеми в пластиковому корпусі, поширювати серед усіх зацікавлених осіб, а після апробації (лат. approbatio — схвалення, визнання) фахівцями можна готувати ревізію в металокерамічному корпусі із захистами від усіх страшних факторів. Сподіваюся у найближчому майбутньому нас ВСІХ порадують заявленими на виставках новими проектами.
Розроблену мною налагоджувальну плату будь-хто може повторити, модифікувати та використовувати у навчальному процесі. Насамперед я робив плату для себе, але вийшло настільки добре, що я вирішив з усіма поділитися.

К1986ВЕ1QI (авіа) – це дуже цікавий контролер з унікальними інтерфейсами, який може використовуватись у ВНЗ для навчання студентів. Думаю, що після виправлення виявлених у контролері помилок та проходження сертифікаційних випробувань, контролер полетить у прямому значенні цього слова!

Джерело: habr.com

Додати коментар або відгук