Тулчейн и первая прошивка
Прежде чем мигать светодиодом, нужно собрать программу на 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.
Источники
- 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 мА
- 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))
- AVRDUDE 8.3 manual page (src/avrdude.1) — программатор arduino — для Arduino Uno Rev3 и любого AVR с загрузчиком Optiboot; автоматическое стирание кристалла перед записью флеш-памяти и ключ -D
- 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
Нашли ошибку?
Смотрите также
- avr-gcc и avr-libcКомпилятор, binutils, avr-libc, avrdude: установка, команды сборки, Makefile, заголовки, ошибки
- AVRDUDEПрошивка AVR из командной строки: ключи, Arduino, USBasp, фьюзы, ошибки
- Прошивка AVRISP, HVPP, debugWIRE, JTAG, PDI, TPI, UPDI, загрузчики, avrdude
- ATmega328PРаспиновка, фьюзы, Optiboot, периферия, питание, схема и код на C
- Как прошить ATmega328P через другую Arduino?Скетч ArduinoISP, D10 → RESET, D11–D13 → SPI, 10 мкФ, stk500v1 на 19 200