Мінімум, який має знати junior embedded-розробник, і про який реально питають на співбесіді. Не енциклопедія — а саме той зріз, що покриває bare-metal STM32 і Linux kernel modules з нашої серії. Колонка «💬 на співбесіді» — типовий ракурс питання.
◆ Загальні поняття
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| Bare-metal | Код, що працює прямо на залізі без ОС. Сам керуєш регістрами, перериваннями, пам’яттю | «Чим bare-metal відрізняється від програмування під Linux?» |
| Firmware | Прошивка — програма, зашита в пам’ять пристрою | — |
| RTOS | Операційна система реального часу (FreeRTOS, Zephyr). Гарантує час реакції на подію | «Навіщо RTOS, якщо є super-loop у while(1)?» |
| HAL | Hardware Abstraction Layer — шар, що ховає роботу з регістрами за зручними функціями (gpio_write) |
«Що дає свій HAL і чим він гірший за прямі регістри?» |
| Toolchain | Набір інструментів збірки: компілятор, лінкер, асемблер (arm-none-eabi-gcc) |
— |
| Cross-compilation | Збірка на одній архітектурі (x86) під іншу (ARM) | «Чому не можна просто gcc на ПК для STM32?» |
| SoC | System on Chip — процесор + периферія на одному кристалі (RV1106, BCM2837) | — |
◆ Пам’ять і C
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
volatile |
Каже компілятору «не оптимізуй це, значення може змінитись ззовні» | Класика. «Навіщо volatile для регістрів і змінних з ISR?» |
| MMIO | Memory-Mapped I/O — регістри периферії доступні як звичайні адреси пам’яті | «Як CPU спілкується з GPIO?» |
.text |
Секція з кодом програми (read-only, у Flash) | «Де живе код, а де змінні?» |
.data |
Ініціалізовані глобальні змінні (копіюються з Flash у RAM при старті) | — |
.bss |
Неініціалізовані глобальні (обнуляються в Reset_Handler) |
«Що відбувається з .bss до main()?» |
| Stack | Стек — локальні змінні, адреси повернення. Росте вниз | «Stack vs heap, де що?» |
| Heap | Купа — malloc. У bare-metal зазвичай уникають |
«Чому malloc погано в embedded?» |
| Linker script | .ld-файл: де у пам’яті лежать секції, де стек, де вектори |
«Навіщо лінкер-скрипт?» |
| Endianness | Порядок байтів: little-endian (ARM) vs big-endian | «У якому порядку зберігається uint32_t?» |
Чому volatile критичний (готова відповідь): Компілятор бачить while (!(SR & RXNE)); і думає: «SR я вже прочитав, він не міняється — зациклю на старому значенні». Без volatile цикл зависне назавжди. volatile змушує читати з пам’яті щоразу. Те саме з прапором, який ставить ISR: без volatile основний код його «не помітить».
◆ Периферія
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| GPIO | General Purpose I/O — універсальний цифровий пін (вхід/вихід) | — |
| UART | Послідовний інтерфейс, 2 дроти (TX/RX), без тактового сигналу | «Скільки дротів у UART? Що таке baud rate?» |
| I²C | Двопровідна шина (SDA/SCL), багато пристроїв на адресах | «Чим I²C відрізняється від SPI?» |
| SPI | Швидка шина (MOSI/MISO/SCK/CS), full-duplex | «Коли SPI, коли I²C?» |
| PWM | Широтно-імпульсна модуляція — «середня» напруга через шпаруватість | «Як керувати яскравістю LED / швидкістю мотора?» |
| ADC / DAC | Аналого-цифровий / цифро-аналоговий перетворювач | — |
| Push-pull | Вихід активно тягне і в 1, і в 0 | «push-pull vs open-drain?» |
| Open-drain | Вихід тягне тільки в 0; для 1 потрібен pull-up (так працює I²C) | — |
| Pull-up / pull-down | Підтяжний резистор, що задає стан «висячого» входу | «Навіщо pull-up на кнопці?» |
| Baud rate | Швидкість UART у бітах/с (9600, 115200) | «Що буде, якщо baud не збіжиться?» |
| Debounce | Усунення «дребезгу» механічної кнопки | «Кнопка дає кілька спрацювань — чому?» |
◆ Переривання і час
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| Interrupt / IRQ | Сигнал, що змушує CPU кинути все й виконати обробник | Класика. «Interrupt vs polling?» |
| ISR | Interrupt Service Routine — функція-обробник переривання | «Що НЕ можна робити в ISR?» |
| Polling | Активне опитування в циклі замість переривання | — |
| Vector table | Таблиця адрес обробників на початку Flash (у нас — startup.c) |
«Що лежить за адресою 0x08000000?» |
| NVIC | Контролер переривань у Cortex-M (пріоритети, вмикання) | — |
| SysTick | Системний таймер ядра Cortex-M (у нас — delay_ms) |
«Як зробити затримку без бібліотек?» |
| DMA | Прямий доступ до пам’яті без участі CPU | «Навіщо DMA при роботі з UART/ADC?» |
| Watchdog | Сторожовий таймер: перезавантажує систему, якщо її «повісило» | «Як захиститись від зависання?» |
| Race condition | Гонитва: результат залежить від порядку доступу до спільних даних | — |
Що не можна в ISR (готова відповідь): ISR має бути коротким. Не можна: довгі затримки, printf/malloc, блокуючі операції. Спільні з основним кодом дані — тільки через volatile (а в Linux — через блокування). Ідея: ISR ставить прапор / кладе байт у буфер, а важку роботу робить основний цикл.
◆ Завантаження
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| Bootloader | Перша програма після ввімкнення; готує систему й запускає основну | «Що відбувається від подачі живлення до main()?» |
| Reset_Handler | Перша функція bare-metal: копіює .data, чистить .bss, кличе main() |
«Хто викликає main() на STM32?» |
| U-Boot | Універсальний bootloader для embedded Linux | «Роль U-Boot у boot-ланцюгу?» |
| SPL | Secondary Program Loader — мінімальний перший етап (вліз у малий SRAM) | — |
| Boot sequence | ROM → SPL → U-Boot → kernel → init → userspace | «Намалюй ланцюг завантаження Linux» |
| RCC / clock gating | Тактування периферії: вимкнений блок не працює, поки не ввімкнеш такт | «Чому GPIO мовчить, хоч код правильний?» |
◆ Linux kernel
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| udev | User-space демон, що автоматично створює/видаляє /dev/* вузли, коли ядро повідомляє про новий пристрій |
«Звідки береться /dev/sdb1, коли встромляєш флешку?» |
mknod |
Ручне створення device-вузла (mknod /dev/x c major minor). Потрібен тільки якщо драйвер НЕ використовує class_create/device_create |
«Чим відрізняється ручний mknod від автоматичного udev?» |
class_create / device_create |
API ядра: драйвер сам реєструє клас і просить udev створити /dev/... без ручного mknod |
«Як /dev/mydev з’являється сам після insmod?» |
| Device permissions (udev rules) | За замовчуванням device_create дає 0600 (тільки root). Права налаштовуються через /etc/udev/rules.d/*.rules, наприклад KERNEL=="mydev", MODE="0666" |
«Чому echo > /dev/mydev дає Permission denied навіть коли драйвер запущений?» |
| User space / kernel space | Розділення: програми (обмежені права) vs ядро (повний доступ) | Класика. «Чим відрізняються?» |
| System call | Місток із user space у kernel (read, write, open) |
«Як userspace просить ядро щось зробити?» |
| Kernel module | Код, що додається в ядро на льоту (insmod/rmmod) |
«Навіщо модуль, а не вкомпілювати в ядро?» |
| Character device | Пристрій, з яким працюють побайтово як з потоком (/dev/ttyS0) |
«char vs block device?» |
| Block device | Пристрій блоками з довільним доступом (диск, SD) | — |
| major / minor | Старший номер = драйвер, молодший = конкретний пристрій | «Що значать цифри в ls -l /dev/...?» |
file_operations |
Структура з вказівниками на open/read/write/release драйвера |
«Як cat читає твій /dev/mydev?» |
| sysfs / procfs | Віртуальні ФС, що показують стан ядра (/sys, /proc) |
— |
| Device Tree | Опис заліза для ядра у вигляді дерева (.dts → .dtb) |
«Як ядро дізнається, яка периферія є на платі?» |
printk |
printf ядра; вивід читаємо через dmesg |
— |
Platform driver / probe |
Драйвер, що «знаходить» своє залізо через DT; probe() викликається при збігу |
«Що таке probe і коли він спрацьовує?» |
copy_to_user / copy_from_user |
Безпечне копіювання між kernel і user space | «Чому не можна просто memcpy з userspace-вказівника?» |
| Kernel oops / panic | oops — локальна помилка (модуль), panic — фатальна (система стає) | «Різниця між oops і panic?» |
| Tainted kernel | «Забруднене» ядро (сторонній модуль, oops) — впливає на підтримку | — |
user vs kernel space (готова відповідь): User space — де живуть програми, з обмеженими правами й власною віртуальною пам’яттю; впала програма — впала тільки вона. Kernel space — ядро з повним доступом до заліза; помилка тут валить усю систему. Перехід — лише через системні виклики. Тому copy_from_user: вказівник з userspace не можна довіряти напряму (може бути невалідним або чужим).
◆ Конкурентність
| Термін | Що це | 💬 на співбесіді |
|---|---|---|
| Mutex | Блокування, що може спати; для довгих секцій, тільки в контексті процесу | «mutex vs spinlock — коли що?» |
| Spinlock | Блокування, що крутиться в циклі (busy-wait); для коротких секцій, можна в ISR | — |
| Semaphore | Лічильник доступів; дозволяє N одночасних власників | — |
| Atomic | Операція, яку не можна перервати посередині (atomic_inc) |
«Як безпечно інкрементувати лічильник?» |
mutex vs spinlock (готова відповідь): Mutex може заснути, поки чекає — тому не можна в ISR (там спати не можна). Spinlock крутиться в циклі й не спить — підходить для ISR, але марнує CPU, тож тільки для дуже коротких секцій. Правило: довга критична секція в контексті процесу — mutex; коротка або в перериванні — spinlock.
◆ «Підступні» питання, на яких сиплються
| Питання | Коротка правильна відповідь |
|---|---|
Навіщо volatile? |
Заборонити оптимізацію змінних, що міняються ззовні (регістри, прапори з ISR) |
Чому не malloc в ISR? |
Може блокувати/спати, фрагментує купу, недетермінований час — ISR має бути швидким |
sudo echo 1 > /sys/... не працює, чому? |
> виконує шел від користувача, не від root. Треба echo 1 | sudo tee |
echo > /dev/mydev дає Permission denied, хоч модуль завантажений? |
device_create за замовчуванням ставить 0600 (тільки root). Треба sudo або udev rule з MODE="0666" |
| Чому GPIO мовчить, хоч код вірний? | Не ввімкнене тактування порту (RCC / clock gating) |
| Stack чи heap росте вгору? | Stack зазвичай вниз, heap вгору — назустріч одне одному |
| Що буде при колізії stack і heap? | Stack overflow / пошкодження даних — у bare-metal без MMU тихо і боляче |
| Чому модуль під інше ядро не вставляється? | Version magic: .ko прив’язаний до версії й конфігу ядра |
| open-drain навіщо? | Кілька пристроїв на одній лінії (I²C), wired-AND, рівні через pull-up |
insmod дає -517, а піни вільні за gpioinfo? |
Це EPROBE_DEFER, не «зайнято». Legacy gpio_request + module_init приречені — треба platform_driver + Device Tree |
Перебираю піни в коді, а все одно failed — чому? |
Бо проблема не в пінах, а в моделі драйвера. Жоден номер не виправить EPROBE_DEFER на голому module_init |
probe викликався, але -16 на GPIO? |
EBUSY: лінію тримає інший consumer. Частий винуватець — залишений у фоні gpiomon/gpioset. pkill його |
Overlay зібрався, але &gpio не резолвиться? |
Забув -@ у dtc. Без symbols посилання в overlay не працюють |
Після reboot /dev/hcsr04 зник? |
Overlay лише оголошує пристрій. Сам модуль треба insmod вручну або через /etc/modules-load.d/ |
throttled=0x50000 — живлення вмерло? |
Ні. Біти 16+18 = «траплялось у минулому». Активна проблема — тільки біти 0–3 |