Привіт! Я Sam4uk, і ви потрапили на мою персональну сторінку, де я ділюся
своїми захопленнями у світі електроніки та програмування. Ця сторінка, перш за
все, створена для моїх особистих потреб – це мій цифровий блокнот з нотатками та
шпаргалками, які допомагають мені в роботі, особливо у сфері Linux та C\C++.
Я завжди радий, якщо мої матеріали виявляться корисними й для когось іншого. Тут
ви знайдете мої думки, технічні рішення та корисні знахідки, що накопичувалися
під час роботи над різними проектами.
Мої захоплення
Електроніка
Світ електроніки для мене – це нескінченне джерело натхнення та можливостей для
експериментів. Зараз на сторінці ви знайдете мою шпаргалку щодо самостійного
вивчення мікроконтролерів STM8. У майбутньому я планую активно заглиблюватися
у вивчення та освоєння платформи STM32, і, звісно, ділитимуся своїми успіхами
та труднощами тут.
Програмування на C++ та Linux
Мови програмування C/C++ та операційна система Linux – це мої основні
інструменти. Мій досвід програмування охоплює різні платформи, зокрема
Raspberry Pi, Windows, Arduino, AVR, ESP32 та, звичайно, Linux.
Я працював як над приватними проєктами, так і над opensource, що дозволило мені
набути різнобічного досвіду.
Зосереджуюсь переважно на програмуванні для мікроконтролерів, а також створюю
додатки для Linux. Інколи також займаюся розробкою кросплатформних рішень, що
розширює мої горизонти у сфері розробки програмного забезпечення.
Є активним користувачем Linux Debian-базованих дистрибутивів. Як вдома, так і на
роботі, я віддаю перевагу цій операційній системі та принципово відмовляюся від
використання WinDOS. На відміну від WinDOS, Linux забезпечує набагато більший
комфорт та безпеку для розробників будь-якого напрямку, що є для мене ключовим
фактором. Це дозволяє мені повністю зануритися у світ відкритого вихідного коду
та використовувати всі переваги Linux для моїх проектів.
Мої розробки для Arduino
Окремий розділ на цій сторінці присвячений документації до бібліотек для Arduino,
які я розробив сам або адаптував для цієї платформи. Це результат моєї практичної
роботи, який, сподіваюся, буде корисним іншим розробникам та ентузіастам.
Суброзділ
Про мене
ХТО я такий?
Хто я такий? Для вас я ще один чувак з інтернету, який любить писати, програмувати та робити різні речі. Я не ставлю перед собою мету комусь і щось донести через мережу. Кому
потрібно сам все знайде. Інтернет відкриває широкі можливості не тільки, щоб гаяти час у соціальних мережах, а й самоудосконалюватись.
Не так давно я більш інтенсивно почав цікавитись програмуванням та операційними системами Linux, і мікроконтролерами. Все що люблю я буду розміщувати тут. Все що хоч раз мені допомогло буду тут записувати на цих сторнінках.
Це мої нотатки на полях.
Це все, що я можу зараз придумати. Сподіваюся, я додам до цього ще щось пізніше. :sweat_smile:
Вам цікаво про мене? Використовуйте лінки нижче, щоб відвідати мої профілі.
GNU/Linux
РОЗДІЛ З НОТАТКАМИ ПРО ЛІНУКС
Суброзділ GNU/Linux
csignal
У операційних системах типу Unix (Linux, macOS) сигнали — це форма міжпроцесної взаємодії (IPC). Хоча більшість сигналів мають конкретне призначення (як-от SIGKILL для зупинки процесу), існують спеціальні сигнали, зарезервовані саме для користувацьких потреб.
#
NAME
C/C++
htop
1
HUP
SIGHUP
SIGHUP
“Hangup” (відбій). Раніше означав розрив зв’язку з терміналом. Зараз часто використовується для того, щоб змусити фонові програми (демони) перечитати конфігураційні файли.
2
INT
SIGINT
SIGINT
“Interrupt” (переривання). Виникає, коли ви натискаєте Ctrl+C.
3
QUIT
SIGQUIT
SIGQUIT
Схожий на INT (натискання Ctrl+\), але змушує програму створити “core dump” (знімок пам’яті) для налагодження.
4
ILL
SIGILL
SIGILL
“Illegal Instruction”. Програма намагається виконати команду, яку процесор не розуміє.
5
TRAP
SIGTRAP
SIGTRAP
Використовується налагоджувачами (debugger) для встановлення точок зупинки (breakpoints).
6
ABRT
SIG
SIG
“Abort”. Посилається самою програмою через функцію abort(), зазвичай при критичній помилці в коді.
7
BUS
-
SIGBUS
Помилка шини. Програма намагається отримати доступ за неправильною адресою пам’яті.
8
FPE
SIGFPE
SIGFPE
“Floating Point Exception”. Виникає при помилковій математичній операції, наприклад, при діленні на нуль.
9
KILL
SIGKILL
SIGKILL
Найпотужніший сигнал. Він миттєво вбиває процес. Його неможливо проігнорувати або перехопити через код.
10
USR1
-
SIGUSR1
Ці сигнали не мають визначеного системою значення.
11
SEGV
SIGSEGV
SIGSEGV
“Segmentation Fault”. Найвідоміша помилка програміста — спроба звернутися до пам’яті, яка програмі не належить.
12
USR2
-
SIGUSR2
Ці сигнали не мають визначеного системою значення.
13
PIPE
SIGPIPE
SIGPIPE
Виникає, коли програма намагається писати в “трубу” (pipe), інший кінець якої вже закритий.
14
ALRM
SIGALRM
SIGALRM
Сигнал-будильник. Використовується для таймерів.
15
TERM
SIGTERM
SIGTERM
“Terminate”. Стандартний сигнал для ввічливого прохання до програми завершити роботу. На відміну від KILL, програма може його “спіймати” і встигнути зберегти дані.
16
STKFLT
-
SIGSTKFLT
Помилка стека на співпроцесорі (зараз майже не використовується).
17
CHLD
-
SIGCHLD
“Child”. Надсилається батьківському процесу, коли його дочірній процес завершується або зупиняється.
18
CONT
-
SIGCONT
“Continue”. Наказує зупиненому процесу продовжити роботу.
19
STOP
-
SIGSTOP
Зупиняє (ставить на паузу) процес. Як і KILL, його не можна перехопити.
20
TSTP
-
SIGTSTP
“Terminal Stop”. Виникає при натисканні Ctrl+Z. Зупиняє процес, але його можна перехопити.
21
SIGTTIN
-
SIGTTIN
Фоновий процес хоче read() з термінала.
22
SIGTTOU
-
SIGTTOU
Фоновий процес хоче write() в термінал (якщо увімкнено tostop)
23
URG
-
SIGURG
Термінові дані в мережевому сокеті.
24
XCPU
-
SIGXCPU
Програма перевищила ліміт часу роботи процесора.
25
XFSZ
-
SIGXFSZ
Програма намагається створити файл, більший за дозволений ліміт.
26
VTALRM
-
SIGVTALRM
Віртуальний таймер (враховує тільки час, коли процес працював).
27
PROF
-
SIGPROF
Використовується для профайлінгу (вимірювання швидкості роботи коду).
28
WINCH
-
SIGWINCH
Зміна розміру вікна термінала.
29
POLL
-
SIGPOLL
Подія введення-виведення.
30
PWR
-
SIGPWR
Помилка живлення (наприклад, коли ДБЖ повідомляє про розряд батареї).
31
SYS
-
SIGSYS
Неправильний системний виклик.
33
reserved
-
SIGCANCEL
Використовуються NPTL (Native POSIX Thread Library). Використовується бібліотекою для скасування потоків.
33
reserved
-
SIGSETXID
Використовуються NPTL (Native POSIX Thread Library).Використовується для синхронізації змін ідентифікаторів користувача/групи (UID/GID) між усіма потоками одного процесу.
#include<csignal>#include<fstream>#include<iostream>#include<unistd.h>// Обробник сигналів з параметром
voidhandle_signal(intsig,siginfo_t*info,void*context){std::cout<<"\n[Альфа] Отримано сигнал з параметром: "<<info->si_value.sival_int<<std::endl;}intmain(){pid_tmy_pid=getpid();std::cout<<"Альфа запущена. PID: "<<my_pid<<std::endl;// 1. Записуємо свій PID
std::ofstreamout("alpha.pid");out<<my_pid;out.close();// 2. Налаштовуємо обробник
structsigactionsa;sa.sa_sigaction=handle_signal;sigemptyset(&sa.sa_mask);sa.sa_flags=SA_SIGINFO;sigaction(SIGRTMIN,&sa,NULL);std::cout<<"Чекаю на файл beta.pid та сигнали..."<<std::endl;// 3. Цикл взаємодії
while(true){std::ifstreamin("beta.pid");pid_ttarget_pid;if(in>>target_pid){intval;std::cout<<"Введіть число для надсилання Беті (або 0 для виходу): ";std::cin>>val;if(val==0)break;sigvalsv;sv.sival_int=val;sigqueue(target_pid,SIGRTMIN,sv);}in.close();sleep(1);}unlink("alpha.pid");// Видаляємо файл при виході
return0;}
#include<csignal>#include<fstream>#include<iostream>#include<unistd.h>voidhandle_signal(intsig,siginfo_t*info,void*context){std::cout<<"\n[Бета] Отримано сигнал з параметром: "<<info->si_value.sival_int<<std::endl;}intmain(){pid_tmy_pid=getpid();std::cout<<"Бета запущена. PID: "<<my_pid<<std::endl;// 1. Записуємо свій PID
std::ofstreamout("beta.pid");out<<my_pid;out.close();// 2. Налаштовуємо обробник
structsigactionsa;sa.sa_sigaction=handle_signal;sigemptyset(&sa.sa_mask);sa.sa_flags=SA_SIGINFO;sigaction(SIGRTMIN,&sa,NULL);// 3. Читаємо PID Альфи та надсилаємо вітання
std::cout<<"Шукаю Альфу..."<<std::endl;while(true){std::ifstreamin("alpha.pid");pid_ttarget_pid;if(in>>target_pid){std::cout<<"Альфу знайдено (PID: "<<target_pid<<"). Насилаю '777'..."<<std::endl;sigvalsv;sv.sival_int=777;sigqueue(target_pid,SIGRTMIN,sv);break;}sleep(1);}// Залишаємося працювати для прийому сигналів
while(true)pause();unlink("beta.pid");return0;}
systemctl
systemctl є головним інструментом для керування системою ініціалізації systemd.
Коли ви віддаєте команду systemctl, вона не просто “вбиває” процес, а намагається керувати ним цивілізовано.
systemctl stop \[service\]
Це найчастіша операція. Systemd діє за певним алгоритмом:
SIGTERM (15). Це “ввічливе” прохання завершити роботу. Програма отримує сигнал і має час,
щоб зберегти файли, закрити з’єднання з базою даних і вийти самостійно.
Очікування (Timeout). Systemd чекає (за замовчуванням 90 секунд).
SIGKILL (9). Якщо програма не закрилася за відведений час, systemd надсилає
“контрольний постріл”, який завершує процес миттєво без збереження даних.
systemctl restart \[service\]
Працює точно так само, як stop, а після повного завершення процесу запускає його знову.
systemctl reload \[service\]
Ця команда не зупиняє програму. Вона зазвичай надсилає сигнал SIGHUP (1). Більшість
серверів (як-от Nginx або Apache) налаштовані так, що при отриманні SIGHUP
вони перечитують свої конфігураційні файли, не перериваючи обслуговування користувачів.
Команда systemctl
Сигнал за замовчуванням
Мета
stop
SIGTERM
Безпечне завершення.
kill
SIGTERM(або інший вказаний)
Негайне надсилання сигналу всій групі процесів.
reload
SIGHUP
Оновлення конфігурації без перезапуску.
(якщо завис)
SIGKILL
Примусове завершення після таймауту.
Налаштування сигналів у .service файлі
KillSignal= Вказує, який сигнал надіслати першим при зупинці (systemctl stop). За замовчуванням це SIGTERM. Ви можете змінити його, наприклад, на SIGQUIT або SIGUSR1.
RestartKillSignal= Який сигнал використовувати при перезавантаженні.
FinalKillSignal= Який сигнал надіслати, якщо програма не закрилася вчасно (за замовчуванням SIGKILL). Його неможливо змінити на щось, що можна ігнорувати.
ExecReload= Тут ви можете вказати команду, яка виконується при systemctl reload. Часто туди пишуть /bin/kill -HUP $MAINPID.
[Unit]Description=Мій кастомний C++ сервіс[Service]ExecStart=/usr/local/bin/my_app# Змінюємо сигнал зупинки на SIGINT (як Ctrl+C)KillSignal=SIGINT# Даємо програмі 20 секунд на завершення перед примусовим вбивствомTimeoutStopSec=20# Якщо натиснули reload, надіслати сигнал реального часу 34ExecReload=/bin/kill -34 $MAINPID[Install]WantedBy=multi-user.target
Як перевірити, що програма отримала сигнал від systemd?
Коли ви зміните файл сервісу, не забудьте оновити конфігурацію systemd:
# Рекомендується використовувати опцію '-R' raw для отримання вмісту без колірних кодівmy_YAML=$(./yb -f tests/user.yaml -k "new")my_YAML=$(./yb -ao "${my_YAML}" -k "in_memory" -v "true")
Друкує відформатований вивід для представлення деревоподібності в рядку.
yb -f "file.yaml" -F -k "key"
Забезпечить інше форматування, якщо використовувати з -A або -d.
-K
keys ключі
format
Друкує лише ключі.
yb -Kf "file.yaml" -k "key"
-l
line
format
Друкує { {line}} у кожному рядку.
yb -f "file.yaml" -l -k "key"
-L
level
format
Друкує { {<номер рівня>} } у кожному рядку.
yb -f "file.yaml" -L -k "key"
-R
raw
format
Друкує вивід без доданих кольорів, коментарів та порожніх рядків.
yb -f "file.yaml" -R -k "key"
-n
number
format
Друкує { {<номер рядка>} } на кожному рядку.
yb -f "file.yaml" -n -k "key"
-T
type
format
Друкує тип значення.
yb -f "file.yaml" -T -k "key"
Підтримує null, boolean, integer, floating number, string.
-s
spaces
Deprecated
Вибір кількості пробілів.
Підтримка YAML
yb забезпечує базову підтримку YAML для редагування та читання YAML-файлу. Починаючи з версії 0.9, yb не підтримує розширені функції, такі як пошук на основі груп, прив’язки, псевдоніми та перевизначення.
Розробка
Повні вихідні коди доступні в папці /src/. Версія, що знаходиться на кореневому рівні, зібрана за допомогою скрипта /src/tools/dist.sh.
Тести
Простий набір тестів доступний у папці tests/. Він представляє різні варіанти використання yb.
Щоб запустити його, виконайте команду нижче з кореневого рівня репозиторію:
./tests/tests.sh
Ви можете надати додатковий параметр для вибору парсера (наприклад, під час розробки):
./tests/tests.sh "./src/yb.dev"
Дякую
Всім вам, YAML-івцям!
Зроблено t0pd4wn на Bash.
Створення власного debian пакету
Огляд
Пакет Debian є найпростішим і найефективнішим способом розповсюдження програмного забезпечення в дистрибутивах на основі Debian. Він піклується про керування залежностями та забезпечує хороший інтерфейс для операцій встановлення/оновлення/видалення.
Офіційний спосіб створення пакету включає багато кроків і процесів. У цій шпаргалці ми розглянемо простіший спосіб створення цих пакетів. Однак офіційний спосіб створення упаковки є ідеальним і рекомендованим для виробничих цілей.
Підготовка файлів
Структура пакета .deb
У дистрибутивах на основі Debian одним із способів встановлення програм є завантаження файлу пакета .deb і використання команди dpkg для його встановлення . Цей «пакет deb» — це архів двійкових і конфігураційних файлів, пов’язаних із програмним додатком. Усі файли всередині архіву зберігаються в певній структурі папок.
Під час інсталяції на цільовій машині двійкові файли та файли конфігурації переходять у подібну структуру папок із кореневої папки .
Ця інструкція про створення та інсталяцію власного debian пакету
Мене засміяли коли я поцікавився які форми декларації головної функції main допустимі.
Ніхто мені не дав відповіді, яку я хотів отримати. Тож це мої нотатки про головну функцію
My name is main
main
return
В пошуках інформації про функцію main мені на форумах та в групах понакидали лінки на різні статті в яких писалося про головну функцію програми. Так от в одній з цих статей автор писав, що писати у функціях які повертають void писати return не обов’язково. Дозволю собі не погодитися з автором. Писати це ключове слово потрібно і бажано.
return;
Писати чи не писати return у void функціях? Візьмемо для прикладу дві реалізації однієї і тієї ж функції. Одні з ключовим словом
voidF(void){// ...
// багато коду
// ...
return;}
, а інша без
voidF(void){// ...
// багато коду
// ...
}
Якщо потікфункції лінійний то між функціями нема ніякої різниці. І компілятор навідь ворнінга не викине. Так як у void функціях компілятор самостійно робить вихід з return перед закриваючою скобкою }, яка позначає кінець тіла функції.
Але якщо логіка функції не є лінійною то за допомогою ключового слова return ми можемо достроково завершити роботу функції, якщо буде виконана чи не виконана якась умова.
voidF(void){// підготовка
// код
if(!ready)return;// не треба продовжувати роботи
// ...
// багато коду
// ...
return;// успішне завершення
}
Я, наприклад, прихильник писати return у void функціях і можу не використати це ключове слово лише коли конкретно профілонив (не захтів писати).
Але як дізнатись чи успішно завершена функція чи аварійно? Потрібно якийсь результат отримати від її роботи
return 0;
Який результат має повертати int main(void)?
Згідно стандарту нуль вказує на успішене виконання функції. У бібліотеці є визначений макрос який позначає EXIT_SUCCESS успішну роботу. Цей макрос рівний 0.
#include<stdlib.h>intmain(void){// ...
// багато коду
// ...
returnEXIT_SUCCESS;// успішне завершення
}
що тотожно
#include<stdlib.h>intmain(void){// ...
// багато коду
// ...
return0;// успішне завершення
}
А яке чило повертати, якщо було аварійне завершення програми? Стандартно повертають 1.
#include<stdlib.h>intmain(void){// ...
// багато коду
// ...
if(error)returnEXIT_FAILURE;// ...
// багато коду
// ...
returnEXIT_SUCCESS;// успішне завершення
}
це ж те саме
#include<stdlib.h>intmain(void){// ...
// багато коду
// ...
if(error)return1;// ...
// багато коду
// ...
return0;// успішне завершення
}
Але ніде не написано, що обов’язково то має бути одиничка. Нам доступний широкий діапазон
min
max
bit
-32768
32767
16
-2,147,483,648
2,147,483,647
32
-9,223,372,036,854,775,808
9,223,372,036,854,775,807
64
Все окрім 0
Таким чином ми можемо передавати повідомлення для скрипта, що викликатиме нашу програму, для прикладу, і сповіщатимемо, як ми виконали роботу.
Код
Опис
0
Немає помилок
1
Попередження (не фатальна помилка (и)).Наприклад, один або кілька файлів були заблоковані якоюсь іншою програмою, тому вони не стиснулися.
2
Критична помилка
7
Помилка командного рядка
8
Недостатьньо пам’яті для роботи
255
Користувач припинив процес
Які можна ловити errorlevel. Тому не обов’язково return 1; можна писати будь яке число, але задокументувати чому таке, а не 1 чи -1.
Ніби розібралися voidint.
(void)
Ніби вже й розібралися який результат має повертати main функція. Спробуємо розібратися, які параметри може отримувати головні функція.
Почнемо з найпростішого і будемо поступово підвищувати ставку.
Почнемо з int main(void)
Такий формат виклику не дозволяє налаштувати початкові дані роботи функції. Дані для обробки програма може отримувати в інтерактивному форматі під час робочого потоку. Влинути на режим роботи програми до її початку не можливо
intmain(void){returnEXIT_SUCCESS;// успішне завершення
}
(int)
Увага
Цей варіант виклику допустимий, але компілятор викидає попередження, що потрібно перевірити кількість аргументів у функції main.
intmain(intcount){switch(count){0:// цей код ніколи не виконається так як не можливо запустити програму щоб count був рівним нулю!
break;1:// що робити при запуску без параметрів
break;2:// це робити коли параметр один
break;3:// ця дія якщо параметрів два
break;// ...
break;default:// цей код запуститься в інших випадках
break;}returnEXIT_SUCCESS;}
Програма в такому вигляді працездатна, але дещо “кастрована”.
Ми може дізнатися кількість переданих у програму параметрів. Змінювати її поведінку у залежності від кількості аргументів, але всі ті аргументи… З програми дізнатися, що їй сказали нема можливості. Їй абсолютно “до лампочки” що написано у комнадному рядку.
На такий
myprog -h
, і такий
myprog --help
, і такий
myprog --version
програма реагуватиме однаково.
myprog foo bar
Програма може реагувати тільки на різну кількість параметрів. Це не найзручніший спосіб керування програмою.
Тож переходимо до наступного варіанту
(int, char**)
Наступна форма, яка є стандартизованою це
#include<stdlib.h>intmain(intint_value,char**char_pointer){// ...
// багато коду
// ...
return0;// успішне завершення
}
Перший параметр типу int передає кількість переданих параметрів нашій програмі.
Цей параметр НІКОЛИ не може бути рівним нулю (0). Змінна int_value може мати значення в проміжку [1…INT_MAX].
Гадаю вам відомо, що в мовах C та C++, і в інших відомих мені мовах програмувння починають рахувати з нуля. Тобто під нульовим параметром ми маємо шлях до виконуваного вайлу.
А як ми можемо отримати значення передані програмі з командного рядка? Оскільки розглядаємо main з двома параметрами то мабуть у другому параметрі. Другий параметр є вказівником на масив нультермінальних рядків з якого ми можемо прочитати всі параметри передані нашій програмі, першим елементом якого є шлях до виконуваного файлу.
#include<stdlib.h>#include<stdio.h>intmain(int,char**pointer){printf("My path: %s\n",*pointer);returnEXIT_SUCCESS;// успішне завершення
}
Цей коди виведе в консоль повідомлення з шляхом переданим у приграму при запиуску
#include<stdlib.h>#include<cstdio>intmain(int,char**pointer){for(autoit{pointer};*it!=nullptr;it++)if(it==argv)printf("Programm path or param[0]: %s\n",*it);elseprintf("\tparam [%d]: %s\n",it-pointer,*it);returnEXIT_SUCCESS;// успішне завершення
}
Якщо запустити програму
./a.out one two foo bar end
в результаті її роботи буде виведено приблизно такий текст
Programm path or param[0]: ./a.out
param [1]: one
param [2]: two
param [3]: foo
param [4]: bar
param [5]: end
Тобто не обов’язково звертатись до першого пареметру в якому передано їх кількість аргументів командного рядка.
#include<stdlib.h>#include<cstdio>intmain(intcount,char**pointer){for(autoit{0};it!=count;it++)if(0==it)printf("Programm path or param[0]: %s\n",*pointer);elseprintf("\tparam [%d]: %s\n",it,pointer[it]);returnEXIT_SUCCESS;// успішне завершення
}
Цей код тотожний попередньому (результат виконання однаковий).
Тепер програма може отримувати та реагувати на різні параметри, та різну їх кількість. Лишається лише їх розпарсити але це вже інша тема
(int, char**, char**)
Йдемо далі? Додамо ще один “документований” параметр?
Мені траплялися фрагменти кодів, де main має три параметри. Саме це мене підштовхуло шукати всі можливі та умовно можливі варіанти функції.
Якщо попередній варіант int main(int, char**) ми могли прочитати кількість c-string з першого параметру, то наступний третій параметр… Щоб отримати кількість елементів нам потрібно “вручну” рахувати
#include<сstdlib>#include<cstdio>intmain(int,char**,char**e){inti{0};for(autoc{e};*c!=nullptr;c++,i++)printf("\t%s \n",*c);printf("total: %d",i);returnEXIT_SUCCESS;// успішне завершення
}
На цьому ноуті, який я використовую цей код нарахував 81 рядок змінних
Деякі рядки я видалив (Не потрібно всім знати, яке сміття в моїй системі. Це моє сміття).
З вихлопу зрозуміло, що це змінні оточення. Цікаво а в динаміці можна їх читати?
Хочу підглянути адреси де знаходяться параметри.
#include<сstdlib>#include<cstdio>intmain(int,char**,char**e){printf("int @: %ld\nchar ** @:%ld\nchar ** @:%ld",&p1,p2,p3);returnEXIT_SUCCESS;// успішне завершення
}
І кілька разів поспіль запустимо виконуваний файл. Ми помітимо, що кожного разу адреси різні.
Тож ми отримуємо копію змінних середовища на момент пуску програми. Від моменту пуску до моменту закінчення роботи програми ці значення можуть змінитися. Актуальні значення змінних середовища потрібно отримувти іншими методами.
(…)
intmain(...){}
Я не знаю як з таким кодом працювати, але оце компілюється.
Компілюється з ворнінгом, але ще й запускається.
Мені ще є що вчити. Одже продовження буде.
Ставлю трикрапик тут, а не в кінці
Замість резюме
Я довідався про стандартні mainи.
int main()int main(void)
int main(int, char**)
int main(int, char**, char**)
І про (хочеться написати слово нестандартні, але за це можна й отрмати гнилими помідорами по пиці) ось такі варіанти. На ці варіанти компілятор матюкається, але все ж збирає життєздатні бінарні файли
int main(int)
int main(int, ...)
int main(int, char**, ...)
int main(int, char**, char**, ...)
Те що компілятор дозволяє “скріпя зубами” зібрати згадані мейни не значить що їх треба використовувати. Синтаксис мови дозволяє так писати, а бездушний компілятор попереджає, що за свої діяння він не несе відповідальності - його попросили так зробити.
Про імена
Історично склалось, що параметри функції main мають наступні імена
#
name
type
1.
argc
int
Кількість параметрів передених з командного рядка
2.
argv
char**
Параметри передані через командний рядок
3.
env
char**
Змінні оточнення
Також дозволено використовувати фіктивну декларацію параметів (оголосити що функція отримуватиме параметр, але не оголосити ім’я змінної).
Про приклади
Не варто кидати помідорами та камінням за те як я написав приклади кодів. Десь я навмисне знущався зі стилю. Десь навмисне відходив від стандарту. Десь через неуважність. Десь через не знання. Я не намагаюсь когось навчити. Я сам вчусь.
Але якщо маєте слушні зауваження, я з радістю їх вислухаю.
На цій сторінці я покажу, як з нуля визначити та створити графічний додаток SDL2,
використовуючи лише улюблений текстовий редактор. Документ написаний для у Linux, оскільки це найкраща
платформа для вивчення програмування.
Створення зразка програми
Тепер нам потрібно створити файл вихідного коду та файл визначення збірки cmake.
Ми взагалі не будемо використовувати SDL, а почнемо з простої програми, яка лише
друкує певний текст. Як тільки вона запрацює, ми зможемо розширити її для
відображення графіки. Вихідний код зберігається у файлі main.cpp та має такий вміст:
#include<iostream>intmain(intargc,char**argv){std::cout<<"App is running.\n";return0;}
Визначення збірки зберігається у файлі з назвою CMakeLists.txt та виглядає так:
Буду старатися використовувати розумний менеджмент пам’яті. Тому підключаємо необхідні
для цього заголовки.
#include<memory>
Створимо структуру яка при вихоід з області видимоті автоматично викличе SDL_Quit().
Це обов’язково. А для початку треба ініціалізувати все, що нам потрібно функцією
SDL_Init(Uint32), яка проініціалізує потрібні нам системи.
SDL_INIT_TIMER
timer subsystem
SDL_INIT_AUDIO
audio subsystem
SDL_INIT_VIDEO
video subsystem; automatically initializes the events subsystem
SDL_INIT_JOYSTICK
joystick subsystem; automatically initializes the events subsystem
SDL_INIT_HAPTIC
haptic (force feedback) subsystem
SDL_INIT_GAMECONTROLLER
controller subsystem; automatically initializes the joystick subsystem
SDL_INIT_EVENTS
events subsystem
SDL_INIT_EVERYTHING
all of the above subsystems
Можна ініціалізувати всі потрібні системи одразу, або згодом при необхідності SDL_InitSubSystem(Uint32) ініціалізувати конкретну систему, або зупинити SDL_QuitSubSystem(Uint32) вже не потрібну систему. При виклику SDL_Quit() зупиняються всі системи.
structSDL_Guard{SDL_Guard(Uint32flags){if(0>SDL_Init(flags))throwstd::runtime_error("SDL could not initialize! SDL_Error:"+std::string(SDL_GetError()));}~SDL_Guard(){SDL_Quit();}};
Для вікна ми використаємо розумний вказівник який розглянемо трохи пізніше.
Створюємо вікно функцією SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags). Перший параметр це заголовок (назва) вікна і є собою c-string параметром. Інші параметри вказують на позицію та розміри вікна.
-
-
-
-
-
-
-
-
-
-
SDL_WINDOW_FULLSCREEN
fullscreen window
SDL_WINDOW_OPENGL
window usable with OpenGL context
SDL_WINDOW_SHOWN
window is visible
SDL_WINDOW_HIDDEN
window is not visible
SDL_WINDOW_BORDERLESS
no window decoration
SDL_WINDOW_RESIZABLE
window can be resized
SDL_WINDOW_MINIMIZED
window is minimized
SDL_WINDOW_MAXIMIZED
window is maximized
SDL_WINDOW_MOUSE_GRABBED
window has grabbed mouse input
SDL_WINDOW_INPUT_FOCUS
window has input focus
SDL_WINDOW_MOUSE_FOCUS
window has mouse focus
SDL_WINDOW_FULLSCREEN_DESKTOP
SDL_WINDOW_FOREIGN
window not created by SDL
SDL_WINDOW_ALLOW_HIGHDPI
window should be created in high-DPI mode if supported. On macOS NSHighResolutionCapable must be set true in the application’s Info.plist for this to have any effect.
SDL_WINDOW_MOUSE_CAPTURE
window has mouse captured (unrelated to MOUSE_GRABBED)
SDL_WINDOW_ALWAYS_ON_TOP
window should always be above others
SDL_WINDOW_SKIP_TASKBAR
window should not be added to the taskbar
SDL_WINDOW_UTILITY
window should be treated as a utility window
SDL_WINDOW_TOOLTIP
window should be treated as a tooltip
SDL_WINDOW_POPUP_MENU
window should be treated as a popup menu
SDL_WINDOW_KEYBOARD_GRABBED
window has grabbed keyboard input
SDL_WINDOW_VULKAN
window usable for Vulkan surface
SDL_WINDOW_METAL
window usable for Metal view
SDL_WINDOW_INPUT_GRABBED
equivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility
smpte (0)
snow (1)
black (2)
white (3)
red (4)
green (5)
blue (6)
checkers-1 (7)
checkers-2 (8)
checkers-4 (9)
checkers-8 (10)
circular (11)
blink (12)
smpte75 (13)
zone-plate (14)
gamut (15)
chroma-zone-plate (16)
solid-color (17)
ball (18)
smpte100 (19)
bar (20)
pinwheel (21)
spokes (22)
gradient (23)
colors (24)
smpte-rp-219 (25)
Libraries
Суброзділ Libraries
WD.Easy
WD.Easy
Ця бібліотека призначена для керування режимами роботи сторожового таймера, поки тільки мікроконтролерів серії AVR.
Особливості
Я не писав її в стилі класу синглтон (сторожовий таймер то один). Все рівно скільки об’єктів не було б створено всі вони працюватимуть з одним регістом. Перед собою поставив завдання, щоб бібліотека була максимально легкою та можна використовувати для будь якої ардуїни та не тільки.
Основна вимога щоб ТАЙМЕР МАЄ ПОЧАТИ ВІДЛІК ЩЕ ДО СТАРТУ ФУНКЦІЇ setup, а ще краще до початку main.
Здається вийшло
Методи доступні у таймеру
Метод
ОПИС
результат
setTimeOut(uint8_t)
Задає інтервал спрацьовувань таймеру
void
setMode(uint8_t)
Задає дію при спрацьовуванні таймеру
void
setTask(ptr*)
Задати функцію обробник переривання
void
getTimeOut()
Отримати періодичність спрацьовуань
uint8_t
getMode()
Отримати режим роботи таймеру
uint8_t
isEnable()
Чи активний таймер
bool
reset()
Почати новий відлік
void
Інтервали
У таблиці наведені параметри які приймає метод setTimeOut(uint8_t) та повертає getTimeOut().
define
час c
WD_15MS
0.015
WD_30MS
0.030
WD_60MS
0.060
WD_120MS
0.120
WD_250MS
0.250
WD_500MS
0.500
WD_1S
1
WD_2S
2
WD_4S
4
WD_8S
8
Примітка Час приблизний і не підходить для вимірювання точних інтервалів, оскільки сторожевий таймер тактується від внутрішнього низькочатоного генератора, який “пливе” при зміні напруги живлення та температури
Дії
define
Означення
DISABLED
Таймер зупинено
INTERRUPT
При спрацьовуванні буде викликано обробник переривання
SYSTEMRESET
При спрацьовуванні буде виконано перезавантаження мікроконтроллера
INTERUPTANDRESET
При спрацьовуванні буде викликано обробник переривання через такий же інтервали виконається перезавантаження контролеру
Обробник переривань
За замовчуванням обробникпереривань не вказаний (nullptr).
Тут все те саме, але таймер починає відлік після останнього сеттера.
Всі сетери скидають таймер на новий відлік. Геттери цього не роблять (Будьте уважні). Під час виконання геттеру може збігти час і виконатися дія запрограмованна у таймері.
Тут таймер ініціалізується ще до входження у main(). На мій погляд це супер зручно
“Системні вимоги”
Таймер працюватиме на будь якому мікроконтролери серії Ардуїно та AVR
Місця в флеш займе додаткових 198 байт та 2 байти в оперативній пам’яті
Найголовніше
Встановлення
Можна завантажити zip архів WD.Easy v1.0 І розпакувати вміст у потрібний каталог
Бібліотека доступна для встановлення через менеджер бібліотек Arduino IDE. Шукайте в категрії
Можна завантажити zip архів WD.Easy prerelise. Це гілка з тестовим не відлагодженим та найсвіжішім кодом. Встанволюється як перший пункт даного списку
Потрібно підключити
#include<WD.Easy.hpp>
та не забувати скадати таймер, щоб він не скинув програму
CRSF
/**
* Для того щоб почативикористовувати мінімальний функціонал бібліотеки
* Потрібно її заінклюдити
*/#include<CrsfSerial.h>/**
* В даному прикладі розглядається підключення приймача CRSF до Serial3
* Arduino Mega 2560 або до Serial якщо у вас інша "доска".
*
* Будь ласка не використовуйте програмні реалізації серійного порту.
* Використовуйте тільки апаратні серійні інтерфейси оскільки протокол
* CRSF використовує нестандартну швидкість передачі даних яка визначена
* в константі CRSF_BAUDRATE. Якщо користуєтесь редактором Arduino IDE
* то ви не знайдете у моніторі серійного порту значення швидкості 420000
* boud.
*
* По друге. Переконайтеся що ваша плата може працювати на такій швидкості
* послідовного інтерфейсу. Ваш мікроеонтролер можливо не може працювати на
* такій високій частоті через апаратні обмеження (не достатня частота тактового
* генератора), софтверні обмеження (Не можливість вказати нестандартну швидкість)
*/#if defined(__AVR_ATmega2560__)
/**
* Тут ми створюємо об'єкт crsf класу CrsfSerial, який слухатиме Serial3.
*
* В конструкторі можна задати іншу швидкість роботи, але для цього потрібно
* переналаштовувати приймач, якщо він підтримує таку кастомізацію. Перевірте
* ваш модуль на наявність такої опції
*/CrsfSerialcrsf(Serial3,CRSF_BAUDRATE);#else
/**
* Для плат які зібрані не на ATmega2560
*/// CrsfSerial crsf(Serial, CRSF_BAUDRATE); ///< Цей
// CrsfSerial crsf(Serial); ///< і цей записи тотожні
/// за замовчуванням crsf працює
/// на швидкості 420000 бод
#error NOT MEGA2560
#endif
voidpacketChannels();///< Тут ми оголошуємо функцію яку реалізуємо пізніше
/// Ця функція буде виконуватися коли на порт "прилетить"
/// пакет з командами
/**
* Цей приклад написано для Ардуїно Мега і призначений для тестування пульта та
* роботи скетчу.
*
* Скетч очікує що модуль приймача вісить на Serial3 а вся інформація для відладки
* вилітає у стандартний серійний порт у монітор порта
*/voidsetup(){Serial.begin(115200);///< Ініціюємо черійний порт для відладкт
crsf.begin();///< Починаємо слухати приймач
crsf.onPacketChannels=&packetChannels;///< Вказуємо яка функція "витягає" команди
}voidloop(){crsf.loop();}///< Тут живе програма
voidpacketChannels(){///< Функція яка обробляє пакет
for(autoch{1};ch<=CRSF_NUM_CHANNELS;++ch){///< Проходимо по всіх каналах
Serial.print(crsf.getChannel(ch));///< Друкуємо
if(ch!=CRSF_NUM_CHANNELS)Serial.print(", ");///< Ствимо розділові знаки
}///< Повторюємо
Serial.println();///< Починаємо з нового рядка
}
Деталі
При виявленні неточностей чи помилок буду вдячний за фітбек