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

Тулчейн и первая прошивка

Прежде чем мигать светодиодом, нужно собрать программу на C в машинный код ATmega328P и записать его во флеш-память. Для этого хватает трёх программ: компилятора avr-gcc, библиотеки avr-libc и прошивальщика avrdude.

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

AVR с нуляурок 1 из 825 мин

Цель урока: Собрать программу на C для ATmega328P командами avr-gcc и записать её в Arduino Uno через загрузчик с помощью avrdude.

Программа для микроконтроллера пишется на обычном C, но собирается не для компьютера, а для ядра AVR. Для этого нужен кросс-тулчейн: компилятор avr-gcc с утилитами binutils, стандартная библиотека avr-libc с заголовками регистров и программа avrdude, которая записывает результат в микроконтроллер. Как поставить их в Windows, Linux и macOS, подробно разобрано в статье «Тулчейн AVR GCC»; в Debian и Ubuntu это одна команда sudo apt install gcc-avr binutils-avr avr-libc avrdude.

Плата урока

Во всех уроках трека — Arduino Uno. На ней стоит ATmega328P, тактируемый частотой 16 МГц (в файле boards.txt ядра Arduino для Uno указано f_cpu=16000000L), а в конце флеш-памяти записан загрузчик Optiboot. Загрузчик занимает 256 слов, то есть 512 байт: поэтому для программы остаётся 32 256 байт из 32 КБ. Размеры загрузочной секции от 256 до 2048 слов перечислены в разделе 27.3 даташита (табл. 27-13).

К цифровому выводу 13 платы подключён светодиод L. В обозначениях микроконтроллера это пятый бит порта B — PB5, и в первой программе мы зажжём его.

Первая программа

#include <avr/io.h>

int main(void)
{
    DDRB |= _BV(DDB5);     /* PB5 (Arduino pin 13) is an output */
    PORTB |= _BV(PORTB5);  /* drive PB5 high: the LED turns on */

    for (;;) {
        /* nothing else to do */
    }
}

Заголовок avr/io.h по ключу -mmcu подключает файл регистров нужного кристалла — для ATmega328P это iom328p.h. В нём определены имена DDRB, PORTB и номера битов DDB5, PORTB5. Макрос _BV(bit) из avr-libc — это просто (1 << (bit)). Что делают регистры DDRB и PORTB, разберём в уроке 2; пока достаточно знать, что первый делает вывод выходом, а второй выставляет на нём единицу.

Функция main никогда не возвращается: у микроконтроллера нет операционной системы, куда можно было бы вернуться, поэтому программа заканчивается бесконечным циклом.

Сборка

avr-gcc -mmcu=atmega328p -DF_CPU=16000000UL -Os -Wall -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
avr-size main.elf
КлючЗачем
-mmcu=atmega328pнабор команд, регистры, код запуска и векторы именно этого кристалла; нужен и при компиляции, и при компоновке
-DF_CPU=16000000ULтактовая частота для util/delay.h, понадобится с урока 2
-Osоптимизация по размеру; без -O avr-gcc не оптимизирует код вовсе
-Wallпредупреждения компилятора

Компоновщик выдаёт файл ELF с отладочной информацией, а avr-objcopy превращает его в Intel HEX — текстовый формат, который понимает avrdude. Ключ -R .eeprom исключает данные EEPROM: их пишут отдельно. Подробно о форматах — в статье «Файлы прошивки».

Прошивка через загрузчик

Загрузчик Optiboot принимает программу по последовательному порту, а мост USB-UART платы превращает его в виртуальный COM-порт. avrdude работает с ним как с программатором arduino:

avrdude -p m328p -c arduino -P COM3 -b 115200 -U flash:w:main.hex:i

Вместо COM3 подставьте порт платы: в Linux это обычно /dev/ttyACM0. Скорость 115 200 бит/с и протокол arduino взяты из boards.txt ядра Arduino. Перед записью avrdude читает сигнатуру кристалла и сравнивает её с ожидаемой: у ATmega328P это 0x1E 0x95 0x0F (раздел 28.3 даташита). Если сигнатура не совпала, прошивка останавливается — разбор этой ошибки есть в ответе «Invalid device signature». После записи загорится светодиод L.

Прошивка программатором

ISP-программатор (USBasp, AVRISP mkII, Arduino в роли ArduinoISP) подключают к шестиконтактному разъёму ICSP. Он держит вывод RESET на земле и программирует кристалл по SPI — выводами SCK, MOSI и MISO (раздел 28.8 даташита):

avrdude -p m328p -c usbasp -U flash:w:main.hex:i

Учтите две вещи. Во-первых, перед записью флеш-памяти avrdude стирает весь кристалл, и загрузчик Optiboot пропадёт — прошивать по USB снова можно будет только после записи загрузчика. Во-вторых, только программатор умеет менять фьюзы, а неверные фьюзы могут заблокировать кристалл; об этом подробно в уроке 4. Все способы прошивки AVR собраны в статье «Программирование AVR».

Безопасность

Плата питается от USB, но выводы микроконтроллера не защищены. Абсолютный максимум постоянного тока одного вывода — 40 мА, а через выводы питания VCC и GND — 200 мА (раздел 29.1 даташита). Превышение может необратимо повредить кристалл, поэтому светодиоды подключают только через резистор, а вывод никогда не замыкают напрямую на землю или питание, пока он настроен выходом.

Задание

Измените программу так, чтобы светодиод L не горел, но вывод PB5 оставался выходом. Затем посчитайте: сколько процентов флеш-памяти, доступной для программы на Uno, займёт программа размером 176 байт (такой размер, например, может показать avr-size для .text и .data вместе)?

Ответ и решение

Достаточно не выставлять единицу в PORTB5: после сброса регистр PORTB равен нулю, поэтому вывод-выход сразу выдаёт ноль. Явно это записывается как PORTB &= ~_BV(PORTB5);.

Под программу на Uno остаётся 32 256 байт: 176 / 32 256 ≈ 0,55 %. Расчёт наш; 176 байт — условный пример, точный размер зависит от версии компилятора.

Что дальше

В уроке 2 разберём регистры DDRx, PORTx и PINx и помигаем светодиодом без библиотеки Arduino. Ключи компилятора, Makefile и версии тулчейна — в статье «Тулчейн AVR GCC», все ключи avrdude — в статье «AVRDUDE». Что такое загрузчик и ISP, коротко объясняют термины «Загрузчик» и «ISP», а распиновка и характеристики кристалла — на странице ATmega328P.

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

Нужен ли программатор, чтобы прошивать Arduino Uno из avr-gcc?

Нет. В Uno записан загрузчик Optiboot, и avrdude с программатором arduino прошивает плату по тому же кабелю USB, что и Arduino IDE. Программатор ISP понадобится, если нужно менять фьюзы, восстановить загрузчик или прошить голый ATmega328P без загрузчика.

Почему без ключа -mmcu программа не работает?

По ключу -mmcu=atmega328p компилятор выбирает набор команд, заголовок с адресами регистров, код запуска и таблицу векторов прерываний. Без него avr-gcc собирает код для самого простого ядра без кода запуска, и avr/io.h не знает, какие регистры есть у кристалла.

Сотрёт ли прошивка через USBasp загрузчик Arduino?

Да. Перед записью флеш-памяти avrdude по умолчанию стирает весь кристалл, и Optiboot пропадает. Плата продолжит работать с вашей программой, но прошить её по USB уже не получится, пока загрузчик не записан снова — пунктом «Записать загрузчик» в Arduino IDE или командой avrdude.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — разд. 14.2.1: биты DDxn и PORTxn; разд. 27.3 и табл. 27-13: размер загрузочной секции 256–2048 слов; разд. 28.3: сигнатура ATmega328P 0x1E 0x95 0x0F; разд. 28.8: последовательное программирование по SPI при RESET на земле; разд. 29.1: ток вывода не больше 40 мА
  2. AVR-LibC 2.3.2: Using the GNU tools, avr/io.h (github.com/avrdudes/avr-libc) — ключ -mmcu на этапах компиляции и компоновки, -Os, avr-objcopy -O ihex -R .eeprom; avr/io.h подключает заголовок регистров выбранного кристалла; макрос _BV(bit) = (1 << (bit))
  3. AVRDUDE 8.3 manual page (src/avrdude.1) — программатор arduino — для Arduino Uno Rev3 и любого AVR с загрузчиком Optiboot; автоматическое стирание кристалла перед записью флеш-памяти и ключ -D
  4. ArduinoCore-avr, boards.txt и variants/standard/pins_arduino.h (github.com/arduino/ArduinoCore-avr) — Uno: atmega328p, f_cpu 16000000L, протокол arduino, 115200 бит/с, наибольший размер программы 32 256 байт, фьюзы 0xFF/0xDE/0xFD, загрузчик optiboot_atmega328.hex; LED_BUILTIN — цифровой вывод 13, порт B

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