Перейти к содержанию

Тулчейн AVR GCC: avr-gcc, avr-libc и avrdude из командной строки

Как собрать программу для AVR без среды разработки: из чего состоит тулчейн AVR GCC, где взять свежую сборку для Windows, Linux и macOS, какими командами превратить файл на C в HEX и записать его в ATmega328P, как выглядит минимальный Makefile, что дают основные заголовки avr-libc и что означают частые предупреждения и ошибки.

Обновлено Редакция GAW

Тулчейн AVR GCC (AVR GNU toolchain) — набор свободных программ для сборки программ под 8-битные микроконтроллеры AVR: компилятор avr-gcc из проекта GCC, ассемблер, компоновщик и утилиты GNU Binutils (avr-as, avr-ld, avr-objcopy, avr-objdump, avr-size и другие), стандартная библиотека avr-libc с заголовками регистров и кодом запуска и программа прошивки avrdude. Компилятор, binutils и avr-libc работают и внутри сред разработки: Arduino IDE ставит их вместе с avrdude, Microchip Studio — свою сборку компилятора без avrdude. Здесь разобрано, как пользоваться им напрямую — из командной строки и Makefile.

Коротко

  • Состав — avr-gcc (компилятор C и C++), binutils (avr-as, avr-ld, avr-objcopy, avr-objdump, avr-size), avr-libc (заголовки, код запуска, libc и libm), avrdude (запись в микроконтроллер).
  • Свежие версии — Microchip AVR 8-Bit Toolchain 4.0.0; на его странице указаны GCC 15.1.0, Binutils 2.44, AVR-LibC 2.2.1. Последний выпуск AVR-LibC — 2.3.2 от 6 июня 2026 года, avrdude — 8.3 от 11 сентября 2026 года.
  • Главные ключи — -mmcu=atmega328p (на этапах компиляции и компоновки), -DF_CPU=16000000UL, -Os. Без -O avr-gcc не оптимизирует код вовсе.
  • HEX для прошивки — avr-objcopy -O ihex -R .eeprom firmware.elf firmware.hex.
  • Arduino IDE использует свою сборку: avr-gcc 7.3.0 с binutils 2.26 и avr-libc 2.0.0.

Из чего состоит тулчейн

ПрограммаПроектЧто делает
avr-gcc, avr-g++GCCдрайвер компилятора: вызывает препроцессор, компилятор C или C++, ассемблер и компоновщик
avr-asGNU Binutilsассемблер
avr-ldGNU Binutilsкомпоновщик; обычно его вызывает сам avr-gcc
avr-objcopyGNU Binutilsпереводит ELF в Intel HEX, Motorola S-record или двоичный образ, выделяет и удаляет секции
avr-objdumpGNU Binutilsзаголовки секций, дизассемблер с исходным текстом, отчёт о занятой памяти
avr-sizeGNU Binutilsразмеры секций
avr-nm, avr-readelf, avr-addr2lineGNU Binutilsтаблица символов, содержимое ELF, адрес → файл и строка
avr-libcAVR-LibCзаголовки <avr/io.h> и другие, код запуска crt<mcu>.o, библиотеки libc и libm, библиотека устройства lib<mcu>.a
avrdudeAVRDUDEзаписывает и читает флеш-память, EEPROM и фьюзы
avr-gdbGDBотладчик; к микроконтроллеру подключается через отдельную программу-посредник, например AVaRICE для JTAG

avr-gcc — только «драйвер»: он сам вызывает нужные программы в зависимости от расширения файла. Полная поддержка конкретного микроконтроллера складывается из четырёх частей: specs-файла устройства в компиляторе, заголовка <avr/io*.h>, кода запуска и библиотеки устройства — последние три даёт avr-libc. C++ поддерживается с ограничениями: по примечаниям Microchip к версии 3.7.0, стандартная библиотека C++ (libstdc++) в их тулчейне не поддерживается.

Где взять тулчейн

ИсточникGCCBinutilsAVR-LibCavrdudeСистемы
Microchip AVR 8-Bit Toolchain 4.0.0 (24 сентября 2025 года)15.1.02.442.2.1не входитWindows, Linux, macOS
Debian 13 (trixie): пакеты gcc-avr, binutils-avr, avr-libc, avrdude14.2.02.43.50.20250108 — снимок разработки, не выпуск2.2.17.1Linux
Arduino AVR Boards 1.8.8: avr-gcc 7.3.0-atmel3.6.1-arduino7 и avrdude 8.0.0-arduino17.3.02.262.0.08.0Windows, Linux (x86, ARM), macOS
Сборка из исходников по руководству avr-libcлюбаялюбаялюбая—Linux, BSD, Windows

Microchip. На странице GCC Compilers for AVR лежат архивы версии 4.0.0: avr8-gnu-toolchain-4.0.0.52-win32.any.x86_64.zip для Windows, …-linux.any.x86_64.tar.gz для Linux и …-darwin.any.universal.tar.gz для macOS. Версии компонентов (GCC 15.1.0, Binutils 2.44, AVR-LibC 2.2.1) указаны там же, в описании тулчейна. Установщика нет: архив распаковывают в любую папку и добавляют её подпапку bin в переменную PATH. В avr/include лежат заголовки avr-libc, в avr/lib — библиотеки, код запуска и скрипты компоновщика. В примечаниях к версии 3.7.0 Microchip пишет, что обновлять поверх старой версии нельзя — новую ставят рядом. Microchip Studio при установке ставит собственную сборку этого тулчейна, какую именно — зависит от выпуска Studio. avrdude не входит ни в архив, ни в Studio, её скачивают отдельно со страницы релизов — подробно в статье «AVRDUDE».

Debian. Всё ставится одной командой:

sudo apt install gcc-avr binutils-avr avr-libc avrdude

Arduino IDE. Если IDE уже стоит, компилятор есть на диске: ядро Arduino AVR Boards кладёт его в папку Arduino15, в packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/, а avrdude — в packages/arduino/tools/avrdude/8.0.0-arduino1/. Где лежит Arduino15 в каждой системе, показано в статье «Arduino IDE 2». Учтите, что это GCC 7.3.0 с binutils 2.26 и avr-libc 2.0.0 — заметно старше текущих выпусков.

Версии. Узнать, что установлено, помогают команды из FAQ avr-libc. Если тулчейнов несколько, вызывайте программы по полному пути — иначе легко собрать одной версией, а проверить другой.

avr-gcc --version
echo "#include <avr/version.h>" | avr-gcc -xc - -E -dM | grep AVR_LIBC_VERSION_STRING

Новый микроконтроллер, которого нет в компиляторе. Старые сборки знают не все кристаллы: например, в списке устройств Microchip AVR 8-bit Toolchain 3.7.0 (GCC 7.3.0) нет ATmega4809 и ATtiny1624, а в текущем GCC оба есть (архитектура avrxmega3). Поддержку можно добавить пакетом устройств Microchip (DFP, файл .atpack — это zip-архив): в нём есть specs-файл, заголовки, код запуска и библиотека устройства. Пакет распаковывают и передают компилятору два пути — к specs-файлу ключом -B и к заголовкам ключом -I:

avr-gcc -mmcu=atmega4809 -B <dfp>/gcc/dev/atmega4809/ -I <dfp>/include/ -Os -c main.c

Здесь <dfp> — папка распакованного пакета ATmega_DFP; так же, со своим кристаллом в путях, подключают и другие пакеты.

Минимальная сборка: от main.c до записи в Arduino Uno

Возьмём программу, которая мигает светодиодом платы Arduino Uno: он подключён к выводу 13 платы, то есть к линии PB5 микроконтроллера ATmega328P, тактовая частота — 16 МГц.

/* blink.c - toggle the LED on Arduino Uno pin 13 (PB5) using avr-libc only */
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB |= _BV(DDB5);              /* PB5 as output */

    for (;;) {
        PORTB ^= _BV(PORTB5);       /* toggle the LED */
        _delay_ms(500);
    }
}

Сборка и прошивка — пять команд:

# 1. Compile: device, clock frequency, optimize for size
avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -Os -g -Wall -c blink.c -o blink.o

# 2. Link: -mmcu again, it selects the startup code and device libraries
avr-gcc -mmcu=atmega328p -g blink.o -o blink.elf

# 3. Convert the program to Intel HEX, without the EEPROM section
avr-objcopy -O ihex -R .eeprom blink.elf blink.hex

# 4. Check the size
avr-size blink.elf

# 5. Flash through the Uno bootloader (COM3 on Windows)
avrdude -p m328p -c arduino -P /dev/ttyACM0 -b 115200 -U flash:w:blink.hex:i

Что происходит на каждом шаге:

  1. Компиляция. -mmcu=atmega328p задаёт набор команд (ATmega328P относится к архитектуре avr5) и через встроенные макросы выбирает заголовок с регистрами этого кристалла. -DF_CPU=16000000UL определяет частоту для <util/delay.h>. -Os включает оптимизацию по размеру — для AVR это обычный выбор, а без ключа -O компилятор не оптимизирует вовсе. -g добавляет отладочную информацию: она остаётся в ELF и в HEX не попадает.
  2. Компоновка. -mmcu нужен и здесь: по нему avr-gcc подключает код запуска crtatmega328p.o и библиотеки устройства. Результат — ELF с кодом, данными, таблицей символов и отладочной информацией. Маленькую программу можно собрать одной командой, без -c, но в проекте из нескольких файлов каждый компилируют отдельно и компонуют вместе.
  3. HEX. -R .eeprom убирает секцию с данными для EEPROM: в ELF она лежит по адресам от 0x810000, которые к флеш-памяти не относятся. Так делает и ядро Arduino. Руководство avr-libc предлагает обратный способ — взять только нужные секции: avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex. Секцию .data пропускать нельзя: в ней начальные значения переменных, которые код запуска копирует в ОЗУ. У кристаллов с отдельной секцией .rodata — ATtiny4…40 с урезанным ядром, tinyAVR 0/1/2, megaAVR 0, AVR Dx и Ex — к этому списку добавляют -j .rodata. Данные для EEPROM, если они есть, выносят в отдельный файл: avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex blink.elf blink.eep.
  4. Размер. avr-size по умолчанию печатает столбцы text, data и bss. У AVR флеш-память занимают text и data (начальные значения переменных хранятся во флеш-памяти), статическое ОЗУ — data и bss. Отчёт о занятой памяти, а если в ELF записаны сведения о кристалле, то и с процентами заполнения, даёт avr-objdump -P mem-usage blink.elf (binutils 2.26 и новее). Сведения о кристалле записывает в ELF код запуска avr-libc. В binutils 2.45 строка Program — это .text + .data + .rodata + .bootloader, в 2.26 (сборка Arduino) — .text + .data + .bootloader; строка Data — .data + .bss + .noinit. Ключи avr-size --mcu=atmega328p -C, которые часто встречаются в инструкциях, в документации GNU Binutils не описаны: проверьте avr-size --help, прежде чем на них полагаться. Как читать вывод size, подробно разобрано в статье «Файлы прошивки».
  5. Прошивка. Через загрузчик Uno — протоколом arduino на 115 200 бит/с; программатором USBasp — avrdude -c usbasp -p m328p -U flash:w:blink.hex:i. Ключи avrdude и готовые команды для других плат — в статье «AVRDUDE». avrdude умеет принимать и сам ELF (-U flash:w:blink.elf:e), если собрана с поддержкой ELF.

Содержимое получившегося HEX можно посмотреть и проверить контрольные суммы записей в просмотрщике HEX-файлов, а дизассемблированный листинг с исходным текстом выводит avr-objdump -h -S blink.elf > blink.lst — команды в нём описаны в статье «Система команд AVR».

Ключи, которые стоит знать

КлючЧто делает
-mmcu=<кристалл>выбирает микроконтроллер или архитектуру; без него действует архитектура avr2 — классические AVR до 8 КБ
-DF_CPU=16000000ULопределяет макрос F_CPU — тактовую частоту в герцах для <util/delay.h> и <util/setbaud.h>. Он только сообщает частоту заголовкам; настоящую задают фьюзы, и новый ATmega328P работает на 1 МГц (внутренний RC-генератор 8 МГц и делитель CKDIV8)
-Osоптимизация по размеру: всё из -O2, кроме того, что обычно увеличивает код
-Ogоптимизация, удобная для отладки
-O3для AVR обычно проигрыш: встраивание функций и развёртка циклов раздувают код
-gотладочная информация для avr-gdb и листингов
-c, -o файлтолько компилировать, без компоновки; имя выходного файла
-ffunction-sections -fdata-sections и -Wl,--gc-sectionsкаждая функция и переменная — в своей секции, компоновщик выбрасывает неиспользуемые
-mrelaxкомпоновщик заменяет CALL и JMP на более короткие RCALL и RJMP, где хватает дальности
-Wl,-Map,firmware.mapфайл карты памяти: где лежит каждая секция и модуль; создаётся даже при переполнении памяти
-save-tempsсохранить промежуточные файлы .i и .s, чтобы посмотреть, во что превратился код
-MMD -MPзаписать зависимости от заголовков в файлы .d для make

Минимальный Makefile

Набирать команды каждый раз незачем — их собирает Makefile для GNU make. Этот вариант компилирует все файлы из списка SRC, следит за изменениями заголовков, печатает размер и прошивает плату командой make flash:

# Minimal Makefile for an ATmega328P project (GNU make)
MCU     = atmega328p
F_CPU   = 16000000UL
TARGET  = blink
SRC     = blink.c

# Override on the command line, e.g. make flash AVRDUDE_FLAGS="-c usbasp"
AVRDUDE_FLAGS ?= -c arduino -P /dev/ttyACM0 -b 115200

CC      = avr-gcc
OBJCOPY = avr-objcopy
SIZE    = avr-size

CFLAGS  = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Os -g -std=gnu11 -Wall -Wextra \
          -ffunction-sections -fdata-sections -MMD -MP
LDFLAGS = -mmcu=$(MCU) -g -Wl,--gc-sections -Wl,-Map,$(TARGET).map

OBJ = $(SRC:.c=.o)

all: $(TARGET).hex size

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

$(TARGET).elf: $(OBJ)
	$(CC) $(LDFLAGS) $^ -o $@

$(TARGET).hex: $(TARGET).elf
	$(OBJCOPY) -O ihex -R .eeprom $< $@

size: $(TARGET).elf
	$(SIZE) $<

flash: $(TARGET).hex
	avrdude -p $(MCU) $(AVRDUDE_FLAGS) -U flash:w:$<:i

clean:
	rm -f $(OBJ) $(OBJ:.o=.d) $(TARGET).elf $(TARGET).hex $(TARGET).map

-include $(OBJ:.o=.d)

.PHONY: all size flash clean

Строки команд под целями должны начинаться с символа табуляции, а не с пробелов: так требует синтаксис правил GNU make. В Windows порт задают при вызове: make flash AVRDUDE_FLAGS="-c arduino -P COM3 -b 115200", для программатора USBasp — make flash AVRDUDE_FLAGS="-c usbasp". Цель clean вызывает rm, поэтому в Windows её запускают из оболочки, где эта команда есть. Пример Makefile посложнее — с листингом и файлами S-record и BIN — есть в руководстве avr-libc, в разделе о демо-проекте.

Заголовки avr-libc

ЗаголовокЧто даётПример
<avr/io.h>имена регистров и битов выбранного -mmcu кристалла (PORTB, DDRB, TCCR1B, DDB5), имена векторов прерываний, константы RAMEND, FLASHEND, E2END, SPM_PAGESIZE; заодно подключает <avr/sfr_defs.h> с макросом _BV(bit) = 1 << bitDDRB |= _BV(DDB5);
<util/delay.h>задержки _delay_ms() и _delay_us() по частоте F_CPU_delay_ms(500);
<avr/interrupt.h>sei() и cli() — разрешить и запретить прерывания; макрос ISR(вектор) для обработчикаISR(TIMER1_COMPA_vect) { … }
<avr/pgmspace.h>PROGMEM, PSTR(), pgm_read_byte() и функции с суффиксом _P — константы во флеш-памяти вместо ОЗУpgm_read_byte(&table[i])

<avr/io.h>. Файл сам выбирает заголовок кристалла (<avr/iom328p.h> для ATmega328P) по макросам, которые задаёт -mmcu; подключать iom328p.h напрямую не нужно. Имена регистров и битов совпадают с даташитом, но у разных кристаллов одинаковые по смыслу биты иногда называются по-разному — при переносе программы сверяйтесь с заголовком.

<util/delay.h>. Перед подключением должен быть определён F_CPU — частота в герцах: строкой #define F_CPU 16000000UL выше #include или ключом -D. Функции работают как задумано, только если включена оптимизация и задержка — константа, известная при компиляции; тогда расчёт тактов выполняет компилятор, а в программу вставляется точный цикл. Прерывания функции задержки не запрещают, поэтому каждое сработавшее прерывание удлиняет задержку. Пределы и фактическое число тактов покажет калькулятор задержек AVR.

<avr/interrupt.h>. Обработчик объявляют макросом ISR() с именем вектора из заголовка кристалла. Переменная, которую меняет обработчик, а читает основная программа, должна быть volatile: иначе оптимизатор прочитает её один раз и больше не заглянет. Если прерывание разрешено, а обработчика нет, код запуска avr-libc отправляет его на __bad_interrupt, а оттуда — на __vector_default, который по умолчанию указывает на адрес 0: программа начинается заново. Это не аппаратный сброс: регистры периферии сохраняют свои значения, а в регистре MCUSR не появляется признак причины сброса. Поймать такие прерывания можно своим обработчиком ISR(BADISR_vect) — это и есть __vector_default. Пример — светодиод мигает по прерыванию таймера 1 в режиме CTC:

/* timer-blink.c - toggle PB5 every 0.5 s from a Timer1 compare interrupt */
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>

static volatile uint8_t tick;           /* set in the ISR, read in main() */

ISR(TIMER1_COMPA_vect)
{
    tick = 1;
}

int main(void)
{
    DDRB |= _BV(DDB5);
    OCR1A  = 31249;                     /* 16 MHz / 256 / (31249 + 1) = 2 Hz */
    TCCR1A = 0;                         /* WGM11:10 = 0, outputs disconnected */
    TCCR1B = _BV(WGM12) | _BV(CS12);    /* CTC with TOP = OCR1A, clk/256 */
    TIMSK1 = _BV(OCIE1A);               /* enable compare match A interrupt */
    sei();

    for (;;) {
        if (tick) {
            tick = 0;
            PORTB ^= _BV(PORTB5);
        }
    }
}

Период прерываний: T = N · (OCR1A + 1)/fCPU = 256 · 31 250/16 000 000 = 0,5 с. Бит WGM12 вместе с нулевыми WGM13, WGM11 и WGM10 включает режим 4 — CTC с вершиной OCR1A, CS12 выбирает делитель 256, OCIE1A разрешает прерывание по совпадению. Строка TCCR1A = 0 нужна, если код окажется в скетче Arduino: функция init() ядра заранее ставит бит WGM10, и вместе с WGM12 получился бы режим 5 — 8-битный Fast PWM с вершиной 0xFF, в котором совпадения с OCR1A = 31249 не будет. Как подобрать делитель и OCR для другой частоты, считает калькулятор таймеров МК, а режимы таймеров разобраны в статье «Таймеры AVR». Почему без volatile такой код ломается и что ещё важно в C для микроконтроллеров, рассказано в статье «Язык C для микроконтроллеров».

<avr/pgmspace.h>. У ATmega328P и других классических AVR константы и строковые литералы по умолчанию попадают в секцию .data: они занимают место и во флеш-памяти, и в ОЗУ, куда их копирует код запуска. Таблицы и строки, которые не меняются, объявляют с PROGMEM и читают специальными функциями:

#include <stdint.h>
#include <avr/pgmspace.h>

static const uint8_t table[] PROGMEM = { 1, 2, 4, 8, 16, 32, 64, 128 };

uint8_t bit_mask(uint8_t i)
{
    return pgm_read_byte(&table[i & 7]);   /* read from flash, not RAM */
}

Функции _P и pgm_read_byte() работают с первыми 64 КБ флеш-памяти. Для данных выше этой границы есть макросы pgm_read_*_far() и функции с суффиксом _PF (например, strlen_PF()): они принимают 32-битный адрес типа uint_farptr_t, который даёт pgm_get_far_address(). Другие заголовки avr-libc — <avr/sleep.h> для режимов сна (см. режимы сна AVR), <avr/eeprom.h>, <avr/wdt.h>, <util/setbaud.h> — устроены так же: каждый описан в руководстве avr-libc.

Частые ошибки и предупреждения

Сообщение или симптомПричинаЧто сделать
#warning "F_CPU not defined for <util/delay.h>"F_CPU не определён, заголовок подставил 1 МГцзадать -DF_CPU=16000000UL или #define до #include; иначе на 16 МГц задержки в 16 раз короче
#warning "Compiler optimizations disabled; functions from <util/delay.h> won't work as designed"сборка без оптимизации: по умолчанию действует -O0добавить -Os; без неё задержки намного длиннее и непредсказуемы, а в программу попадает библиотека плавающей точки
#warning "device type not defined", затем DDRB, PORTB «не объявлены»не указан -mmcuдобавить -mmcu= при компиляции и компоновке
программа собралась, но не работает-mmcu не указан при компоновке или отличается от компиляцииодинаковый -mmcu на всех этапах. Начиная с GCC 5 без -mmcu драйвер берёт specs ядра avr2, а код запуска и библиотеку устройства подключает только для конкретного кристалла, — программа собирается без кода запуска и таблицы векторов
компилятор не знает кристалл, указанный в -mmcuкристалл новее тулчейнасвежий тулчейн или пакет устройств (DFP) с ключами -B и -I
переменная из обработчика прерывания «не меняется»нет volatileобъявить переменную volatile
программа время от времени начинается заново, хотя сброса не былоразрешено прерывание без обработчика: переход на адрес 0 через __bad_interruptнаписать обработчик или запретить прерывание; для отладки — ISR(BADISR_vect)
ОЗУ быстро кончается, в .data много строкстроковые литералы копируются в ОЗУPROGMEM, PSTR() и функции _P
_delay_ms(t) с переменной t: ошибка компиляции __builtin_avr_delay_cycles expects a compile time integer constantс оптимизацией <util/delay.h> пересчитывает время в такты встроенной функцией компилятора, а ей нужна константаконстанта в аргументе; переменную задержку набирают циклом из _delay_ms(1) или делают таймером
_delay_ms(t) с переменной t ждёт слишком долго и раздувает кодтак бывает только с -O0 или с макросом __DELAY_BACKWARD_COMPATIBLE__: пересчёт в такты идёт во время работы в числах с плавающей точкойто же: константа, цикл из _delay_ms(1) или таймер
компоновщик сообщает о переполнении области text или dataпрограмма или данные не помещаются в память кристалла-Os, --gc-sections, PROGMEM, причину ищут в файле карты (-Wl,-Map)
в HEX для флеш-памяти попали чужие данные с адресами от 0x810000в ELF есть секции .eeprom, .fuse, .lock или .signature — компоновщик кладёт их с адресов 0x810000, 0x820000, 0x830000 и 0x840000, — а avr-objcopy вызван без -R или -jнадёжнее всего явный список -j .text -j .data (и -j .rodata для кристаллов с этой секцией); вариант с -R требует перечислить все лишние секции

Последний пункт касается программ, в которых служебные данные объявлены в коде: макрос FUSES из <avr/fuse.h> создаёт секцию .fuse, LOCKBITS из <avr/lock.h> — секцию .lock, а <avr/signature.h> — секцию .signature с сигнатурой кристалла. Эти секции нужны программе прошивки, которая берёт их из ELF, но в HEX для флеш-памяти им не место. Значения фьюзов для своей платы проверьте в калькуляторе фьюзов AVR, а обзор семейств AVR, интерфейсов прошивки и программаторов — в разделе «Микроконтроллеры AVR».

Частые вопросы

Как скомпилировать программу для ATmega328P через avr-gcc?

Тремя командами: avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -Os -c main.c -o main.o компилирует, avr-gcc -mmcu=atmega328p main.o -o main.elf компонует, avr-objcopy -O ihex -R .eeprom main.elf main.hex делает HEX для прошивки. Ключ -mmcu нужен на обоих этапах: по нему выбираются набор команд, заголовок с регистрами, код запуска и библиотеки.

Что значит «F_CPU not defined for <util/delay.h>»?

Заголовок util/delay.h не нашёл макрос F_CPU с тактовой частотой в герцах и подставил 1 МГц. Если микроконтроллер работает на 16 МГц, все задержки станут в 16 раз короче. Задайте частоту ключом -DF_CPU=16000000UL или строкой #define F_CPU 16000000UL до подключения заголовка.

Почему _delay_ms работает неправильно?

Проверьте, совпадает ли F_CPU с настоящей тактовой частотой. F_CPU только сообщает частоту заголовку, а задают её фьюзы: новый ATmega328P с завода работает от внутреннего RC-генератора 8 МГц с делителем CKDIV8, то есть на 1 МГц, и при F_CPU=16000000UL задержки выйдут в 16 раз длиннее. Вторая причина — сборка без оптимизации (по умолчанию -O0): задержки становятся намного длиннее, в программу попадает библиотека плавающей точки. Нужен -Os и константа в аргументе. Прерывания во время задержки тоже её удлиняют.

Где взять avr-gcc для Windows?

Готовый архив AVR 8-Bit Toolchain выкладывает Microchip: последняя версия — 4.0.0, на странице тулчейна указаны GCC 15.1.0, Binutils 2.44 и AVR-LibC 2.2.1, есть сборки для Windows, Linux и macOS. Архив распаковывают и добавляют его папку bin в PATH. Ещё один вариант — компилятор из Arduino IDE: он лежит в папке Arduino15, но это старая версия GCC 7.3.0.

Как узнать размер прошивки после avr-gcc?

Командой avr-size firmware.elf: флеш-память занимают столбцы text и data, ОЗУ — data и bss. Отчёт о занятой памяти выдаёт avr-objdump -P mem-usage firmware.elf (binutils 2.26 и новее); проценты заполнения он показывает, только если в ELF записаны сведения о кристалле — их добавляет код запуска avr-libc. Ключей --mcu и -C у avr-size в документации GNU Binutils нет — они есть не во всех сборках.

Можно ли пользоваться avr-gcc из Arduino IDE?

Да: ядро Arduino AVR Boards ставит avr-gcc 7.3.0 и avrdude в папку Arduino15, в подпапки packages/arduino/tools/avr-gcc и packages/arduino/tools/avrdude. Их можно вызывать по полному пути из командной строки. В сборке Arduino — binutils 2.26 и avr-libc 2.0.0, это заметно старше текущих выпусков.

Источники

  1. AVR-LibC 2.3.2. User Manual: Toolchain Overview, Using the GNU tools, A simple project (demo), FAQ, модули <avr/io.h>, <util/delay.h>, <avr/interrupt.h>, <avr/pgmspace.h>, <avr/fuse.h> (исходники документации, тег avr-libc-2_3_2-release) — состав тулчейна, ключи -mmcu, -O, -g, -ffunction-sections, -mrelax, -save-temps, -O0 по умолчанию и -Os как обычный выбор, команды сборки демо-проекта и его Makefile, avr-objdump -P mem-usage, avr-objcopy для HEX и EEPROM, F_CPU и требования к оптимизации в util/delay.h, sei, cli, ISR и BADISR_vect, volatile, PROGMEM и строки в ОЗУ, версии инструментов; выпуск 2.3.2 от 6 июня 2026 года
  2. GCC Manual: AVR Options, Optimize Options, Preprocessor Options, Overall Options, AVR Built-in Functions (текущая версия на 20 октября 2026 года) — -mmcu и архитектуры avr2…avrxmega7, значение по умолчанию avr2, ATmega328P — avr5; полная поддержка устройства — crt, io-заголовок, библиотека устройства и specs-файл; ключи -D, -MMD, -MP, -c, -o; __builtin_avr_delay_cycles
  3. GNU Binutils documentation: objcopy, objdump, size; ld: MEMORY; исходники binutils 2.26 и 2.45 (binutils/od-elf32_avr.c, ld/scripttempl/avr.sc) — -O ihex, -j, -R, форматы вывода size, ключи size без --mcu и -C, ошибка компоновщика при переполнении области памяти, AVR-отчёт mem-usage в avr-objdump с версии 2.26, адреса областей eeprom, fuse, lock в скрипте компоновщика AVR
  4. Microchip. GCC Compilers for AVR and Arm-Based MCUs and MPUs (копия Web Archive от 11 июля 2026 года); AVR 8-bit GNU Toolchain 3.7.0 Release Notes (42372A) — AVR 8-Bit Toolchain 4.0.0 от 24 сентября 2025 года: GCC 15.1.0, Binutils 2.44, AVR-LibC 2.2.1, архивы для Windows, Linux и macOS; в примечаниях к 3.7.0 — состав пакета, папка bin в PATH, обновление установкой рядом, ограничения C++, пакеты поддержки устройств (DFP) с ключами -B и -I
  5. Arduino. Индекс пакетов package_index.json, репозиторий arduino/toolchain-avr (коммит 2c677fd) — Arduino AVR Boards 1.8.8 использует avr-gcc 7.3.0-atmel3.6.1-arduino7 и avrdude 8.0.0-arduino1; состав сборки Arduino: binutils 2.26, gcc 7.3.0, avr-libc 2.0.0, gdb 7.8; рецепты platform.txt (ArduinoCore-avr, коммит 11b9130)
  6. GCC 15.1.0: исходники gcc/config/avr (avr.cc, driver-avr.cc, gen-avr-mmcu-specs.cc) — ошибка «expects a compile time integer constant» у __builtin_avr_delay_cycles; без -mmcu — specs ядра avr2 без кода запуска и библиотеки устройства
  7. AVR-LibC 2.3.2: crt1/gcrt1.S; ArduinoCore-avr: cores/arduino/wiring.c (коммит 11b9130) — незанятые векторы → __bad_interrupt → __vector_default = __vectors (адрес 0); init() ядра Arduino ставит WGM10 в TCCR1A
  8. GNU Make Manual: Rule Syntax — строки рецепта начинаются с символа табуляции
  9. Debian 13 (trixie): пакеты gcc-avr, binutils-avr, avr-libc, avrdude — версии 14.2.0, 2.43.50.20250108, 2.2.1 и 7.1, страницы прочитаны 20 октября 2026 года
  10. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 16.9.2 — режим CTC таймера 1 (WGM13:0 = 4, вершина OCR1A), табл. 16-5 — делитель CS12:0, 16.11.8 — бит OCIE1A регистра TIMSK1

Нашли ошибку?