Как-то раз я начал работать с одним большим проектом для микроконтроллера. Как многие legacy-проекты в embedded, он привязан к старой версии IDE. В моем случае это оказался IAR 7. Эта среда обладает хорошими отладочными средствами, но работать с кодом в ней не очень комфортно: отсутствует много привычных комбинаций клавиш (Ctrl + точка, например), нет темной темы, скромная подсветка синтаксиса.

Со временем я стал все больше пользоваться VS Code для работы с кодом, а для проверки работоспособности переключался в IAR. Тогда я задумался, можно ли перенести основную работу над проектом в VS Code? Достоинств у редактора много: приятный настраиваемый интерфейс, интегрированный Git, расширения на любой вкус и задачу. Главный нюанс состоял в том, что проект должен собираться компилятором IAR, ибо написан он был строго под него.
Коротко о проекте: он огромен, стар (самое раннее упоминание — 2007 год), в нем много всего привязано к компилятору. Проект написан для серии приборов с небольшим количеством модификаций. Программатор J-Link, микроконтроллеры ARM Cortex-M0 и M4.
План
Какие возможности я хотел получить от новой среды разработки? Для начала:
сборка прошивки в VS Code;
работа с программатором — очистка чипа, запись готового .hex;
отображение ошибок компиляции в проекте.
К чему я стремился из плюсов среды:
удобство работы с кодом (подсветка синтаксиса, форматирование);
автоматизация рутинных задач через tasks;
работа с Git;
проверка кода анализатором.
Задачи в VS Code
Небольшой ликбез. Задачи в VS Code — это механизм, который позволяет запускать внешние скрипты и программы прямо из редактора. С их помощью можно автоматизировать некоторые действия, например прошивку микроконтроллера, удаление/создание папок и т. п.
Описываются задачи в файле .VS Code/tasks.json и выглядят примерно так:
{ "label": "Flash: Erase chip", "type": "shell", "command": "JFlash.exe", "args": [ "-openprj${workspaceFolder}\\_scripts\\J-flash.jflash", "-erasechip", "-exit" ], "group": "build", "presentation": { "reveal": "always", } }
Эта задача запускает SEGGER J-Flash, в нем открывает проект J-flash, стирает чип и закрывает программу. Подробнее про задачи можно почитать в блоге VS Code.
Сборка проекта
Для начала рассмотрим сборку через расширения для VS Code. IAR Build и IAR Debug позволяют перенести работу с прошивкой в VS Code (сборку и отладку), но увы, минимальная поддерживаемая версия IAR — восьмая, так что это не мой вариант.
Чуть более интересный способ — это сборка через IarBuild.exe, поставляемого вместе с IAR. Эта утилита позволяет скомпилировать проект без графической IDE и работает довольно просто. Ее необходимо вызвать, передав ей одну из команд:
-makeпересобирает, что изменилось;-build— уже полная пересборка проекта;-cleanудаляет промежуточные и выходные файлы.
Чтобы запустить сборку проекта, из bat-скрипта вызываем:
"%IAR_DIR%\common\bin\IarBuild.exe" "%REPO_DIR%\name.ewp" -build %CONFIG% -log all
Здесь:
IAR_DIR— адрес папки IAR,REPO_DIR— папка с проектом,name.ewp— файл проекта,CONFIG— конфигурация прибора по умолчанию (Release или Debug), а в моем случае еще и конфигурации под каждый вид прибора,log all— вывод сообщений сборки.
Далее в скрипте можно добавить действия с готовой прошивкой — например, прописать заводские настройки или объединить файл с bootloader.
Но вернемся к VS Code. Для компиляции прошивки надо запустить bat-файл, указав необходимую модификацию. Каждой модификации прибора соответствует отдельная конфигурация сборки в IAR. Я создал одноименный список приборов в tasks.json:
"inputs": [ { "id": "device", "type": "pickString", "description": "Выберите прибор для сборки", "options": [ "Device1", "Device2", "Device3" ] } ]
Вот задача, которая в начале дает выбор модификации, а затем запускает сборку необходимой конфигурации:
{ "label": "IAR: Build Device", "type": "shell", "command": "${workspaceFolder}\\build_device.bat", "args": [ "${input:device}" ], "options": { "cwd": "${workspaceFolder}", "shell": { "executable": "cmd.exe", "args": ["/d", "/c"] } }, "group": "build", "problemMatcher": { "owner": "IAR-build", "fileLocation": "absolute", "source": "IAR", "pattern": { "regexp": "^(.+)\\((\\d+)\\)\\s*:\\s*(Warning|Error)\\[[^\\]]+\\]:\\s*(.*)$", "file": 1, "line": 2, "severity": 3, "message": 4 } } }
Тут интересен момент с problemMatcher: при компиляции все ошибки передаются в VS Code и будут отображаться в окне problems. Очень удобно. В панели Problems сразу появилась сотня предупреждений. Добавляются предупреждения и ошибки сборки с указанием места, так что можно все найти и поправить.

Раз уж зашла речь об ошибках, то поговорим и про IntelliSense. По умолчанию VS Code использует IntelliSense из расширения C/C++ для анализа кода, и он поначалу выдавал огромное количество ошибок, так как не мог найти те или иные элементы. Так как меня интересуют только ошибки из IAR, я отключил отображение ошибок от IntelliSense, создав файл settings.json с таким содержанием:
{ "C_Cpp.errorSquiggles": "disabled", "C_Cpp.intelliSenseEngine": "default" }
При этом сам IntelliSense я оставил включенным, поскольку он обеспечивает автодополнение и навигацию по коду. Для корректной работы этих функций необходимо настроить пути к заголовочным файлам. Чтобы не писать их вручную, попросил нейросеть сгенерировать скрипт на Python, который бы создавал файл c_cpp_properties.json со списком путей, импортированных из проекта IAR:
import json import sys import xml.etree.ElementTree as ET from pathlib import Path IAR_DIR = r"F:\prog\IAR\arm" project = Path(sys.argv[1]).resolve() config_name = sys.argv[2] # Открываем проект и ищем конфигурацию root = ET.parse(project).getroot() config = next( (c for c in root.findall(".//configuration") if c.findtext("name") == config_name), None ) if config is None: sys.exit(f"Configuration not found: {config_name}") # Извлекаем пути заголовочных файлов includes = [] for option in config.findall(".//option"): if option.findtext("name") == "CCIncludePath2": for state in option.findall("state"): if state.text: path = state.text.strip() path = path.replace("$PROJ_DIR$", str(project.parent)) path = path.replace("$TOOLKIT_DIR$", IAR_DIR) includes.append(Path(path).resolve().as_posix()) # Формируем настройки VS Code data = { "configurations": [{ "name": config_name, "includePath": list(dict.fromkeys(includes)), "intelliSenseMode": "gcc-arm", "compilerPath": "", "cStandard": "c99", "cppStandard": "c++98" }], "version": 4 } # Сохраняем файл output = Path(__file__).resolve().parent.parent / ".vscode" / "c_cpp_properties.json" output.parent.mkdir(exist_ok=True) output.write_text(json.dumps(data, indent=4), encoding="utf-8") print(f"Created: {output}") print(f"Include paths: {len(includes)}")
Кнопку сбора прошивки и отображение ошибок разобрали, идем дальше.
Прошивка контроллера
Следующий пункт автоматизации — работа с программатором J-Link через задачи. Для J-Flash я создал проект с моим контроллером, всеми выставленными настройками и написал необходимые мне задачи: одна для прошивки, вторая для стирания чипа.
{ // прошивка "label": "Flash: artifacts", "type": "shell", "command": "JFlash.exe", "args": [ "-openprj${workspaceFolder}\\_scripts\\J-flash.jflash", "-open${workspaceFolder}\\programm.hex", "-auto", "-exit" ], "group": "build", "problemMatcher": [] }, { // стирание "label": "Flash: Erase chip", "type": "shell", "command": "JFlash.exe", "args": [ "-openprj${workspaceFolder}\\_scripts\\J-flash.jflash", "-erasechip", "-exit" ], "group": "build", "presentation": { "reveal": "always", } }
Все задачи пишут результат своего выполнения в терминал. Можно отдельно прописать, что если задача выполнена успешно, то терминал автоматически закрывается, но мне это показалось неудобным. Отвлечешься и неясно, а запускал ли ты задачу вообще.
Отладка
Внимательный читатель наверняка уже сидит с вопросом: а как же отладка проекта? Ведь написание и сборка проекта — это далеко не всё, проект надо еще и дебажить! Тут, увы, результатами похвастаться не получится: последний камень преткновения перенести не удалось. Расширение для отладки не подошло из-за разницы в версиях IAR. Единственное, чем я пользуюсь, это отладка через UART, а вот за полноценной отладкой приходится идти обратно в IAR.
Полезные расширения
План «минимум» закрыл, собрать прошивку и перепрошить контроллер могу, не выходя из VS Code. Теперь рассмотрим расширения, которые мне помогают в работе:
Code Spell Checker — проверяет правильность написания слов. Бывало, что после запушенного на сервер изменения вы находили опечатку в своем комментарии? С этим расширением таких ситуаций больше не будет. Одна беда: постоянно ругается на опечатки, которые внесли другие программисты в проект.
Подсвечивает место ошибки, по нажатию «Ctrl + .» показывает варианты замены, по повторному нажатию исправляет на первое значение.

Также есть отдельное окошко, в котором показаны все ошибки в проверенных файлах. Имеется вариант приложения для русского языка.
Trailing Spaces — показывает лишние пробелы, например, в конце строки. Пишем чище.

VS Code-icons — меняет иконки папок и файлов в Explorer, становится визуально проще ориентироваться в проекте.
Serial monitor — позволяет работать с COM-портом в отдельном окошке, я использую его для отладки по UART.

Doxygen Documentation Generator — расширение для генерации шаблона описания функции. Создает ту самую шапку:
/******************************************************************************** * @brief тестирует исправность АЦП * * @param num_channel - номер канала * @return uint16_t значение на канале ********************************************************************************/ uint16_t ADC_test (uint8_t num_channel) {
Преимущества использования VS Code
Теперь рассмотрю коротко, что я получил, начав работать с кодом в VS Code:
Настраиваемую подсветку синтаксиса и темную тему. Честно говоря, даже одного этого было бы достаточно для того, чтобы начать в этом разбираться.
Форматирование кода.
Удобный поиск по всему проекту.
Структура проекта соответствует структуре папок. В IAR расположение файлов в дереве проекта может не иметь ничего общего с реальным расположением.
Удобно открывать среду: зашел в папку проекта, нажал правой кнопкой мыши — «открыть с помощью code» — и всё, проект открыт.
Можно посмотреть почти любой файл в IDE, разве что с офисными документами не работает.
В любом участке проекта я вижу, кто именно и как давно написал тот или иной код. Особенно полезно в работе с командой.
Подключил анализатор кода — cppcheck. После анализа он выдал тысячу предупреждений. Конечно, это не все места с ошибками, но рассмотреть стоит. Полезно для поиска потенциальных ошибок.
Выводы
Стал ли я все время работать в VS Code, забыв, как выглядит ярлык IAR? Увы, нет. Бо́льшая часть отладки проекта все так же происходит в IAR. Но вся работа по написанию кода и работа с Git теперь перенесена в приятную среду. Сейчас я не перепроверяю орфографию комментариев, стало заметно легче ориентироваться в коде, а часть задач решается через отладку по UART прямо в VS Code. Считаю, что по большей части моя изначальная задача выполнена.
Далее я попытаюсь прикрутить к проекту хоть какое-нибудь автоматическое тестирование, чтобы меньше работать ?
Ссылки
VS Code как IDE для embedded-разработки — про полноценную работу с МК в VS Code. Подглядел для себя парочку полезных расширений там.