Найти в Дзене
Финальная упаковка: как получить .bin и зачем это знать
От ELF до прошивки: последний шаг перед загрузкой в микроконтроллер Мы получили firmware.elf - файл, который содержит всё: код, данные, адреса, отладочную информацию. Но программатору (ST-Link, J-Link) нужен простой бинарый дамп: байты, которые нужно записать во флеш-память по порядку. Именно здесь вступает в игру утилита objcopy (в LLVM - llvm-objcopy). Команда: llvm-objcopy -O binary firmware.elf firmware.bin Флаг -O binary означает: "извлеки из ELF-файла только секци, которые должны быть во флеше (обычно .text, .data и т.д.), и запиши их подряд, как они расположены в памяти". Получается файл firmware...
3 месяца назад
Ассемблирование и линковка: собираем пазл из объектных файлов
Объектные файлы и линкер-скрипты: как микроконтроллер узнаёт, где что лежит После того как у нас есть ассемблерный код (main.s), его нужно превратить в настоящий машинный код. Это делает ассемблер (в составе LLVM). Он просто транслируеь каждую инсрукцию в байты и упаковывает их в объектный файл (main.o). Объектный файл - это контейнер с несколькими секциями: 🔴 .text - код программы. 🔴 .data - инициализированные глобальные переменные. 🔴 .bss - описание неинициализированных переменных (занимает место в памяти, но не в файле). 🔴 Таблица символов - имена функций и переменных, определённых в этом файле...
3 месяца назад
Препроцессинг и компиляция: от текста до промежуточного представления
Clang и LLVM IR: на каком языке думают компиляторы. Первый этап - препроцессинг. Это самая простая часть: компилятор берёт ваш main.c и выполняет текстовые директивы. Он выкидывает коментарии, вставляет заголовочные файлы (#include), раскрывает макросы (#define) и обрабатываеи условную компиляцию (#ifdef). В Clang остановиться после препроцессора можно флагом -E: clang -E main.c -o main.i Теперь у нас есть один большой .i-файл с чистым кодом на C. На этом этапе часто кроются проблемы с макросами или неверными путями к заголовочным файлам. Следующий шаг - собственно компиляция. Здесь Clang делает синтаксический и семантический анализ...
3 месяца назад
Компиляция для embedded: почему это не просто «нажать F5
» Как рождается прошивка: обзор пути от C-кода до микроконтроллера Вы пишете код на C, жмете "скомпилировать" и получате .hex файл, котрый заливаете в микроконтроллер. Кажется, что всё магически превращается в работающее устройство. Но за этой магией стоит строгий инженерный процесс, и для embedded-систем у него есть свои особенности. Главное отличие от разработки под ПК - кросс-компиляция. Вы работаете на мощном x86-процессоре, а код выполняется на ARM, AVR или RISC-V. Компилятор должен не только перевести C в машинные команды, но и учесть, что память микроконтроллера разделена на флеш (ROM) и RAM, а ресурсы жёстко ограничены...
3 месяца назад
Что куда ставить
? Итак, у нас есть 5 основных архитектур. Как выбрать? Давайте сравним их по ключевым критериям. По простоте и порогу входа 🟢Суперлуп - начать может любой новичок 🟢Прерывания - требуется понимание работы МК 🟢Планировщик - нужны знания таймеров и прерываний 🟢RTOS - серьезный скачок в сложности 🟢Событийно-ориентированная - самый сложный для проектирования По отзывчивости и времени отклика 🟡Прерывания - абсолютный лидер (почти мгновенно) 🟡RTOS - отличная отзывчивость для приоритетных задач 🟡Событийно-ориентированная - зависит от реализации 🟡Планировщик - задержка до следующего "тика"...
4 месяца назад
Событийно-ориентированная архитектура - общение сообщениями
Самый высокоуровневый подход - Событийно-ориентированная архитектура (Event-Driven Architecture, EDA). Здесь нет центрального цикла. Компоненты системы общаются через сообщения (события): одни модули их "издают" (publish), другие - "обрабатывают" (subscribe). Часто строится поверх RTOS или как большая state-машина. // Издатель события (например, драйвер кнопки) void button_isr(void) { event_t btn_event = { .type = EVENT_BUTTON_PRESSED, .data = 1 }; event_bus_publish(&btn_event); // Отправка в шину событий } // Подписчик события (обработчик интерфейса) void ui_handler(const event_t *event) {...
4 месяца назад
Планировщик на таймере - порядок во времени
Следующий шаг к структурированию - Планировщик на таймере (Timer-Based Scheduler). Это эволюция суперлупа, где системный таймер периодически генерирует прерывание (тик), которое проверяет, каким задачам пора выполняться, и выставляет флаги. Основной цикл проверяет эти флаги и запускает соответствующие задачи. volatile uint32_t tick_count = 0; #define TASK1_PERIOD 100 // Выполнять каждые 100 мс #define TASK2_PERIOD 500 // Выполнять каждые 500 мс void systick_isr(void) { tick_count++; } int main(void) { uint32_t last_task1 = 0, last_task2 = 0; setup_timer(); enable_interrupts(); while(1) { uint32_t...
4 месяца назад
Прерывания: как заставить систему реагировать мгновенно
Чтобы система реагировала на события мгновенно, переходим к архитектуре на основе Прерываний (Interrupt-Driven). Основная логика переносится в обработчики прерываний (ISR), которые асинхронно вызываются аппаратурой микроконтроллера. Суперлуп часто остается для фоновых, не срочных задач. volatile bool button_pressed = false; void button_isr(void) { button_pressed = true; } int main(void) { setup_hardware(); enable_interrupts(); while(1) { update_clock(); if(button_pressed) { button_pressed = false; handle_button_action(); } sleep_mode(); } } Как это работает на практике? Аппаратура МК генерирует сигнал прерывания при событии...
4 месяца назад
Суперлуп: первый и самый простой каркас для прошивки
Представьте, что вы учитесь готовить. Вы не начинаете со сложного торта Прага. Вы начинаете с бутерброда: взял хлеб, намазал масло, положил колбасу. Суперлуп (Super Loop) - это тот самый бутерброд в мире встраиваемого ПО. Это фундамент, на котором всё начинается. Технически, это бесконечный цикл while(1), в котором ваши функции (задачи) вызываются строго одна за другой, по кругу. Никакой магии, только четкая последовательность. while(1) // Вечный двигатель вашей прошивки { check_buttons(); // 1. Опрос кнопок read_sensors(); // 2. Чтение датчиков process_logic(); // 3. Вычисление логики update_display();// 4...
4 месяца назад
Каркас для прошивки: почему архитектура решает все
Запустить светодиод на микроконтроллере можно и простым скетчем. Но когда ваш проект превращается в сложное устройство с дисплеем, кнопками, датчиками, связью и логикой, код без продуманной структуры становится кошмаром. Он превращается в хрупкое полотно из тысяч строк, где изменение в одном месте ломает три других. Решение - выбрать архитектурный подход, то есть фундаментальный принцип организации кода. Это не про библиотеки или конкретные функции. Это решение о том, как разные части программы будут общаться, в каком порядке выполняться и кто будет принимать решения. Почему это критически важно? Выбор архитектуры определяет: 🟡Скорость реакции на события...
4 месяца назад
👩‍💻👩‍💻👩‍💻 Современная среда разработки на языке C
👩‍💻👩‍💻👩‍💻 Современная среда разработки на языке C В этой статье рассмотривается, как настроить контейнерную среду разработки для проектов на языке C. Также настройку системы сборки с помощью CMake, среды тестирования с помощью Unity и даже то, как использовать контейнерную среду в конвейере непрерывной интеграции! Статья старая, но не бесполезная...
9 месяцев назад
https://youtu.be/F0Eh3XYMq7M?si=BTFAKFK0CYZqgtWz
9 месяцев назад