Архитектура AVR в примерах/Простейшая программа

В данной работе мы рассмотрим два основных этапа разработки программ для микроконтроллеров семейства AVRсборку кода и загрузку результата во встроенную Flash-память микроконтроллера — на примере простейшей программы на языке C.

Перед началом

править

Для работы нам потребуются:

  • простейшее устройство на МК ATmega8 (или подобном), или же плата Arduino Uno или Arduino Nano;
  • среда разработки, включающая следующие программные пакеты:
    • GCC и GNU Binutils для целевой архитектуры AVR;
    • универсальный сборщик GNU Make;
    • AVR Libc;
    • загрузчик Avrdude;
    • стандартные системные средства; так, мы будем предполагать использование командного интерпретатора GNU Bash и какого-либо инструмента редактирования кода (GNU nano, Vim, GNU Emacs, или иного);
    отметим, что установить специфичные для AVR инструменты в Debian (и производных системах) можно командой, подобной # apt-get install -- avr-libc binutils-avr gcc-avr avrdude .

Мерцание светодиода

править

/*** blink.c — Blink a LED at PB5, repeatedly  -*- C -*- */
#include <avr/io.h>             /* for DDRB, PORTB, etc. */
#include <util/delay.h>         /* for _delay_ms () */

int
main ()
{
  /* Arduino boards have a LED at PB5 */
  DDRB    |= ((1 << DDB5));
  while (1) {
    PORTB ^= ((1 <<  PB5));
    _delay_ms (2718L);  /* waste cycles for 2.71 s */
  }

  /* not reached */
  /* . */
  return 0;
}
/*** blink.c ends here */

### Makefile  -*- Makefile -*-

MCU     = atmega8
F_CPU   = 7372800

CC      = avr-gcc
CFLAGS  = -O2 -Wall -std=gnu11 -mmcu=$(MCU)
CPPFLAGS  = -DF_CPU=$(F_CPU)L

OBJCOPY = avr-objcopy

.PHONY: default
default: blink.hex blink

blink:  blink.c

%.hex: %
	$(OBJCOPY) -O ihex $< $@

### Makefile ends here

Чтение кода

править

Рассмотрим код программы выше, начиная с его «полезной нагрузки».

  1. Строка PORTB ^= ((1 << PB5)); изменяет состояние вывода PB5 МК (D 13 платы Arduino Uno) на противоположное (на высокий уровень если был низкий и наоборот.)

  2. Следующий за ней вызов _delay_ms (2718L); приведет к выполнению «холостого цикла» в течение 2.718 с (при условии задания соответствующего действительной тактовой частоте процессора значения для макроопределения F_CPU.)

  3. Оба действия выше заключены в «основной цикл» while (1) { }. Условие продолжения цикла всегда выполняется, а значит цикл будет выполняться до момента потери питания или сброса МК.

  4. Строка DDRB |= ((1 << DDB5)); настраивает вывод PB5 МК для использования в качестве выхода.

    В противном случае он будет настроен как вход и изменение его состояния будет приводить к подключению или отключению от вывода «подтягивающего резистора», ток через который может оказаться недостаточным для свечения подключенного к выводу светодиода.

  5. Строки int main () начинают определение «головной» функции main, возвращающей значение типа int (знаковое целое) и получающей неопределенное количество аргументов. Строка return 0;, идущая следом за «бесконечным» циклом, удовлетворяет формальное требование на возврат не-void функцией некоторого значения.

    • Именно функции main передается управление после завершения инициализации служебными подпрограммами, обеспечивающими окружение этапа выполнения (англ. run-time environment) языка C.
    • Такое объявление функции main является одним из допускаемых стандартом.[1]
    • Возвращаемое значение 0 определено стандартом (для функции main) как код «успешного завершения».[2][3]
    • Фактически реализуемое GCC и AVR Libc окружение этапа выполнения не предполагает ни передачу функции main каких-либо аргументов, ни использование возвращаемого ею значения.
  6. Наконец, директивы препроцессора #include в самом начале кода подключают следующие заголовки (англ. header):

    avr/io.h
    включает используемые в коде макроопределения DDRB, DDB5, PORTB, PB5;
    util/delay.h
    включает объявление (и определение) функции _delay_ms (), также используемой в приведенном примере.

Сборка

править
  1. Создадим файлы blink.c и Makefile приведенного выше содержания.

  2. Соберем рассматриваемый пример выполнив команду make:

    $ make 
    avr-gcc -O2 -Wall -std=gnu11 -mmcu=atmega8 -DF_CPU=7372800   ../blink.c   -o blink
    avr-objcopy -O ihex blink blink.hex
    $ 
    
    NB

    При использовании микроконтроллера, отличного от ATmega8, или же кварцевого резонатора на частоту, отличную от 7.3728 MHz, следует явно указать параметры сборки MCU или F_CPU, соответственно

    Так, для платы Arduino Uno R3, поставляемой с МК ATmega328P и кварцевым резонатором на 20 MHz, команда сборки может быть следующей:

    $ make MCU=atmega328p F_CPU=20000000 
    
  3. Удостоверимся в отсутствии ошибок сборки и в появлении файла blink.hex, содержащего результирующий машинный код в формате Intel hex.

Загрузка и проверка работоспособности

править
  1. Подключим устройство к USB-порту основной системы. Удостоверимся в появлении соответствующего файла устройства/dev/ttyUSB1 или подобного.

  2. Проверим возможность связи с загрузчиком Optiboot сохранив образ текущего состояния flash-памяти МК в файл формата Intel hex, подобно:

    $ avrdude -P /dev/ttyUSB1 -c arduino -b 115200 -p atmega8 \
          -U flash:r:"$(mktemp --suffix=.hex -- ./XXXX)":i 
    
    avrdude: AVR device initialized and ready to accept instructions
    
    Reading | ################################################## | 100% 0.00s
    
    avrdude: Device signature = 0x1e9307
    avrdude: reading flash memory:
    
    Reading | ################################################## | 100% 1.02s
    
    avrdude: writing output file "96TF.hex"
    
    avrdude: safemode: Fuses OK
    
    avrdude done.  Thank you.
    
    $ 
    
    NB
    Для запуска Optiboot, Avrdude пытается выполнить сброс МК кратковременно изменяя состояние линий RTS и DTR. Если эти линии не соединены с цепью Reset МК (так, на некоторых адаптерах эти линии не разведены вовсе), то сброс следует выполнить вручную, подобно:
    1. ввести команду выше, не завершая ее нажатием ⏎ Enter;
    2. нажать на кнопку сброса устройства;
    3. отпустить кнопку сброса и немедленно завершить ввод команды avrdude нажатием ⏎ Enter.
  3. Загрузим полученный ранее образ в flash-память МК, подобно:

    $ avrdude -P /dev/ttyUSB1 -c arduino -b 115200 \
          -p atmega8 -U flash:w:blink.hex:i 
    
    avrdude: AVR device initialized and ready to accept instructions
    
    Reading | ################################################## | 100% 0.00s
    
    avrdude: Device signature = 0x1e9307
    avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
             To disable this feature, specify the -D option.
    avrdude: erasing chip
    avrdude: reading input file "blink.hex"
    avrdude: writing flash (90 bytes):
    
    Writing | ################################################## | 100% 0.02s
    
    avrdude: 90 bytes of flash written
    avrdude: verifying flash memory against blink.hex:
    avrdude: load data flash data from input file blink.hex:
    avrdude: input file blink.hex contains 90 bytes
    avrdude: reading on-chip flash data:
    
    Reading | ################################################## | 100% 0.01s
    
    avrdude: verifying ...
    avrdude: 90 bytes of flash verified
    
    avrdude: safemode: Fuses OK
    
    avrdude done.  Thank you.
    
    $ 
    
    NB
    Замечание выше о сбросе устройства и запуске Optiboot остается справедливым и при загрузке образа в МК.
  4. Удостоверимся в правильности работы программы и устройства оценив период вспышек светодиода и сопоставив его с заданным в исходном коде.

Примечания

править
  1. 5.1.2.2.1 Program Startup. WG14 N1570 Committee Draft (2011-04-12). Проверено 19 ноября 2012.
  2. 5.1.2.2.3 Program termination. WG14 N1570 Committee Draft (2011-04-12). Проверено 19 ноября 2012.
  3. 7.22.4.4 The exit function. WG14 N1570 Committee Draft (2011-04-12). Проверено 19 ноября 2012.