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

Генератор кода инициализации периферии AVR

Выберите микроконтроллер и тактовую частоту, включите нужные блоки — генератор посчитает UBRR, предделитель и TOP таймера, делитель АЦП, проверит, что всё достижимо и выводы не заняты дважды, и выдаст файл на C для avr-gcc с именами регистров, битов и векторов, сверенными с заголовками пака Microchip, и заготовками обработчиков прерываний.

Исходные данные

МГц

до 20 МГц

USART
Работа
бод
Таймер
Режим
мс
АЦП
Работа
Внешние прерывания
Выводы GPIO
PORTB
PORTC
PORTD

out — выход, in — вход, pull — вход с подтяжкой. Рамкой отмечены выводы, занятые выбранной периферией.

Результат

USART0: UBRR, U2X
103, U2X = 09615,38 бод, ошибка +0,16 % (допустимо ±2 %)
Таймер 1: N, TOP
1, 15999совпадение каждые 1 мс (1 кГц)
АЦП: делитель
128125 кГц, преобразование 104 мкс
Прерывания
TIMER1_COMPA_vect, INT0_vect

При AVCC и внутреннем опорном на выводе AREF — только конденсатор 100 нФ на землю, никакого внешнего напряжения

Спад на INT0 будит только из Idle; из более глубоких режимов сна, в том числе Power-down, — только низкий уровень (DS40002061B, 10.5 и таблица 10-1)

Выводы

  • PB5 — GPIO: выход
  • PC0 — ADC0
  • PD0 — RXD USART0
  • PD1 — TXD USART0
  • PD2 — INT0

Биты регистров ATmega328P: USART0 · TC1 · ADC · EXINT · порты

Код на C для avr-gcc

Стиль битов
/*
 * ATmega328P, F_CPU = 16 MHz: USART0, timer 1, ADC, INT0.
 * Generated by https://gaw.ru/tools/avr-init/ from the register data of the Microchip pack;
 * check it against the datasheet (DS40002061B). Build: avr-gcc -mmcu=atmega328p -Os
 */
#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

/* Pins: DDRx = 1 output; PORTx = 1 on an input turns the pull-up on */
static void gpio_init(void)
{
    DDRB = (1 << DDB5);  /* outputs: PB5 */
    PORTD = (1 << PD2);  /* pull-ups: PD2 (INT0 pull-up) */
}

/* USART0: 9600 baud (actual 9615.38, +0.16 %), U2X0 = 0, 8N1 */
static void uart_init(void)
{
    UBRR0 = 103;                             /* f_CPU / (16 * 9600) - 1, rounded */
    UCSR0A = 0;                              /* normal speed (a bootloader may leave U2X0 set) */
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);  /* 8 data bits, no parity, 1 stop bit */
    UCSR0B = (1 << RXEN0) | (1 << TXEN0);    /* receiver, transmitter */
}

void uart_putc(char c)
{
    while (!(UCSR0A & (1 << UDRE0))) {}  /* wait for an empty transmit buffer */
    UDR0 = c;
}

void uart_puts(const char *s)
{
    while (*s) uart_putc(*s++);
}

uint8_t uart_getc(void)
{
    while (!(UCSR0A & (1 << RXC0))) {}  /* wait for a received byte */
    return UDR0;
}

/* Timer 1, CTC: compare match every 1 ms (1 kHz) */
static void timer1_init(void)
{
    OCR1A = 15999;                        /* TOP = f_CPU / (N * f) - 1 = 16000000 / (1 * 1000) - 1 */
    TCCR1A = 0;
    TIMSK1 |= (1 << OCIE1A);              /* compare match interrupt */
    TCCR1B = (1 << WGM12) | (1 << CS10);  /* CTC (mode 4), TOP = OCR1A; clk/1: the timer starts */
}

/* ADC: ADC0, AVCC, f_ADC = 125 kHz, 104 us per conversion */
static void adc_init(void)
{
    ADMUX = (1 << REFS0);                                               /* reference AVCC, input ADC0 (PC0) */
    DIDR0 |= (1 << ADC0D);                                              /* digital input buffer off: less current, less noise */
    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);  /* enable; f_ADC = 16 MHz / 128 = 125 kHz */
}

uint16_t adc_read(void)
{
    ADCSRA |= (1 << ADSC);              /* start a conversion: 13 ADC clocks, the first 25 */
    while (ADCSRA & (1 << ADSC)) {}     /* ADSC returns to 0 at the end */
    return ADC;                         /* ADCL, then ADCH */
}

/* External interrupts: INT0 on falling edge */
static void ext_int_init(void)
{
    EICRA |= (1 << ISC01);  /* INT0 on falling edge */
    EIFR = (1 << INTF0);    /* clear flags the sense change may have set */
    EIMSK |= (1 << INT0);   /* enable */
}

ISR(TIMER1_COMPA_vect)
{
    /* every 1 ms */
}

ISR(INT0_vect)
{
    /* INT0: falling edge */
}

int main(void)
{
    gpio_init();
    uart_init();
    timer1_init();
    adc_init();
    ext_int_init();
    sei();  /* global interrupt enable */

    for (;;) {
    }
}

Расчёт выполнен по формулам и данным из источников, приведённых на этой странице, и не заменяет проверку: параметры компонентов у разных производителей и даже партий различаются. Перед применением сверяйте результат с документацией на конкретные детали.

Генератор пишет код настройки периферии классических AVR так, как его пишут вручную по даташиту: регистр за регистром, с именами битов из avr/io.h и комментарием к каждой строке. Числа — делитель скорости USART, предделитель и вершину счёта таймера, делитель АЦП — он считает по формулам даташита и сразу проверяет: достижима ли скорость, укладывается ли период в таймер, попадает ли частота АЦП в окно 50–200 кГц и не занят ли один вывод двумя блоками. Имена регистров, битов и векторов прерываний берутся из файлов описания устройств Microchip, сверенных с даташитом и заголовками — тех же, по которым построены страницы регистров ATmega328P и остальных микроконтроллеров.

Коротко

  • USART: UBRR = f / (16 · BAUD) − 1, с U2X = 1 — f / (8 · BAUD) − 1; 12 бит, 0…4095.
  • Таймер, CTC и быстрый ШИМ: f = f_CPU / (N · (TOP + 1)); у 8-битных таймеров TOP до 255, у 16-битных — до 65 535.
  • АЦП: f_ADC = f_CPU / (2…128), для 10 бит — 50–200 кГц; при 16 МГц делитель 128 и 125 кГц, 104 мкс на преобразование.
  • INT0/INT1: биты ISCn1:0 — 00 низкий уровень, 01 любое изменение, 10 спад, 11 фронт.
  • Векторы — имена из avr-libc: USART_RX_vect у ATmega328P, USART_RXC_vect у ATmega8/16/32, USART0_RX_vect у ATmega2560.

Что генерирует инструмент

На выходе — один файл .c: #define F_CPU, подключение <avr/io.h> и <avr/interrupt.h>, функции gpio_init(), uart_init(), timerN_init(), adc_init(), ext_int_init(), вспомогательные uart_putc(), uart_getc(), adc_read(), обработчики ISR() с именами векторов из заголовков пака и main(), который всё это вызывает и разрешает прерывания sei(). При приёме по прерыванию обработчик кладёт байты в кольцевой буфер на 64 байта, а uart_getc() забирает их без ожидания (−1, если буфер пуст) — по схеме «один писатель, один читатель» с барьерами компилятора из статьи о прерываниях и общих данных. Стиль записи битов выбирается: (1 << CS10), _BV(CS10) или шестнадцатеричное число с именами битов в комментарии.

Порядок записи в регистры — тот, что рекомендует даташит. Делитель USART пишется до включения приёмника и передатчика, у ATmega8/16/32 сначала UBRRH, потом UBRRL (запись младшего байта сразу перезапускает делитель). Регистр с битами выбора тактирования таймера (TCCRnB) записывается последним: таймер запускается уже настроенным. Флаги внешних прерываний сбрасываются после смены режима ISC — смена режима может сама выставить флаг.

Собрать файл можно так:

avr-gcc -mmcu=atmega328p -Os -o main.elf atmega328p_init.c
avr-objcopy -O ihex -R .eeprom main.elf main.hex

Как устроен тулчейн и откуда берутся avr/io.h и -mmcu, рассказано в статье об avr-gcc, прошивку загружает avrdude.

USART: делитель и ошибка скорости

Генератор скорости USART делит частоту процессора на 16 (или на 8 при U2X = 1) и на UBRR + 1. Режим «авто» выбирает тот вариант, где ошибка меньше; при равной ошибке остаётся U2X = 0 — приёмник с 16 выборками на бит терпимее к расхождению частот. Допустимую ошибку генератор берёт из таблиц 20-2 и 20-3 даташита ATmega328P: для кадра с 8 битами данных без чётности ±2 % при U2X = 0 и ±1,5 % при U2X = 1.

Скорость, бод8 МГц, U2X = 08 МГц, U2X = 116 МГц, U2X = 016 МГц, U2X = 1
960051 (0,2 %)103 (0,2 %)103 (0,2 %)207 (0,2 %)
19 20025 (0,2 %)51 (0,2 %)51 (0,2 %)103 (0,2 %)
38 40012 (0,2 %)25 (0,2 %)25 (0,2 %)51 (0,2 %)
57 6008 (−3,5 %)16 (2,1 %)16 (2,1 %)34 (−0,8 %)
115 2003 (8,5 %)8 (−3,5 %)8 (−3,5 %)16 (2,1 %)
250 0001 (0,0 %)3 (0,0 %)3 (0,0 %)7 (0,0 %)

Значения — из таблиц 20-6 и 20-7 даташита ATmega328P (у ATmega8 те же числа: 8 МГц — в таблице 62, 16 МГц — в таблице 63 даташита 2486AA). Таблицу для любой частоты кварца и запас приёмника по кадру считает калькулятор скорости UART.

У ATmega8, ATmega16 и ATmega32 регистр UCSRC делит адрес с UBRRH: запись идёт в UCSRC, только если старший бит URSEL равен 1. Генератор ставит его сам; если забыть, формат кадра уйдёт в старшие биты делителя. В ATmega328P и ATmega2560 регистры раздельные, а UBRR0 записывается как 16-битное число.

Таймер: CTC и быстрый ШИМ

В режиме CTC счётчик считает от нуля до значения в OCRnA (у 16-битных таймеров режим 4) и сбрасывается; на каждом совпадении вызывается прерывание TIMERn_COMPA_vect (у таймеров с одним регистром TCCRn — ATmega8 T2, ATmega16/32 T0 и T2 — TIMERn_COMP_vect и регистр OCRn), а при включённом переключении вывода на OCnA выходит меандр половинной частоты. Генератор перебирает предделители и берёт тот, где ошибка периода наименьшая, а при равной — меньший предделитель: так вершина счёта больше, а шаг точнее. Тот же расчёт, с перебором всех вариантов, есть в калькуляторе таймера.

Таймерf_CPUПериодNTOPРегистры
0 (8 бит)16 МГц1 мс64249TCCR0A = (1 << WGM01), TCCR0B = (1 << CS01) | (1 << CS00)
1 (16 бит)16 МГц1 мс115 999TCCR1B = (1 << WGM12) | (1 << CS10)
2 (8 бит)16 МГц4 мс256249TCCR2B = (1 << CS22) | (1 << CS21)
1 (16 бит)8 МГц1 с25631 249TCCR1B = (1 << WGM12) | (1 << CS12)

Для ШИМ у 16-битных таймеров берётся режим 14: вершина в ICRn, а заполнение задаётся в OCRnA, OCRnB (у ATmega2560 ещё и OCRnC), так что частоту можно выбрать любую. Регистр ICRn записывается только после того, как биты WGMn3:0 выбрали режим с вершиной в ICRn (ATmega328P, раздел 16.6, и так же в остальных даташитах), поэтому генератор сначала задаёт режим при остановленном таймере, потом ICRn и OCRnx и в конце включает тактирование. 8-битные таймеры ATmega328P, ATmega2560 и ATtiny85 могут то же самое в режиме 7 (вершина в OCRnA), но тогда выход только один — OCnB; в режиме 3 вершина фиксирована, 0xFF, и частота — только f_CPU / (N · 256). У таймеров ATmega8/16/32 с одним регистром TCCRn второго варианта нет. Заполнение неинвертирующего быстрого ШИМ — (OCR + 1) / (TOP + 1); ноль так не получить, поэтому для 0 % выход таймера отключают.

Генератор предупредит, если разрешение ШИМ меньше 6 бит: при 1 МГц на 16 МГц у таймера всего 16 шагов. Подробнее о режимах и регистрах — в статье о таймерах AVR, биты конкретного таймера — на странице регистров, например таймера 1 ATmega328P.

АЦП и внешние прерывания

Делитель АЦП в режиме «авто» — наименьший, при котором частота АЦП не выше 200 кГц: 128 при 16 и 20 МГц, 64 при 8 МГц, 8 при 1 МГц. Если задать делитель вручную и частота выйдет за 200 кГц, генератор предупредит, что 10 бит не гарантированы, а за пределами 50 кГц – 1 МГц — что результат будет неверным. Время преобразования, код по напряжению и опорные напряжения подробно считает калькулятор АЦП AVR. Входы ADC6 и ADC7 ATmega328P и ATmega8 — отдельные аналоговые выводы, они есть только в 32-выводных корпусах TQFP и QFN; в DIP-28 их нет. Цифровой входной буфер выбранного канала отключается битом ADCnD в DIDR0 (у ATmega2560 для ADC8–ADC15 — в DIDR2), если такой регистр у микроконтроллера есть.

Опорные напряжения у разных серий кодируются по-разному: у ATmega328P REFS1:0 = 11 — внутренние 1,1 В, у ATmega8/16/32 тот же код — 2,56 В, у ATmega2560 1,1 В — это 10, а у ATtiny85 есть третий бит REFS2, и внешнее опорное подаётся на PB0. Генератор берёт коды из таблиц своего даташита, поэтому ошибка «скопировал ADMUX с другой меги» исключена.

Для INT0 и INT1 режим срабатывания задают биты ISCn1:0: в EICRA у ATmega328P и ATmega2560, в MCUCR у ATmega8/16/32 и ATtiny85. Разрешение — в EIMSK, GICR или GIMSK. Подтяжка на выводе нужна, если к нему подключена кнопка на землю: тогда нажатие — это спад. Для пробуждения из Power-down у ATmega328P, ATmega8/16/32 и ATtiny85 годится только низкий уровень INT0/INT1 — фронт или спад будит лишь из Idle (таблицы режимов сна в даташитах); у ATmega2560 фронты INT0–INT3 обнаруживаются асинхронно и будят из любого режима (2549Q, раздел 15). Подробнее — в статье о режимах сна AVR.

Проверки и типичные ошибки

ПроверкаЧто сообщает генератор
Скорость USARTUBRR вне 0…4095 — ошибка; отклонение больше допустимого по даташиту — предупреждение
Период и частота таймерапериод длиннее, чем 2⁸ или 2¹⁶ тактов при N = 1024, или короче двух тактов — ошибка; заметная неточность и малое разрешение ШИМ — предупреждение
Частота АЦПвыше 200 кГц — предупреждение, вне 50 кГц – 1 МГц — ошибка
Выводыодин вывод у двух блоков (ADC1 и INT0 на PB2 у ATtiny85) — ошибка; GPIO на выводе периферии, RESET, JTAG, выводы кварца — предупреждение или пояснение
Частота процессоравыше предельной для микроконтроллера — предупреждение

Чаще всего код из интернета не работает по одной из этих причин: обработчик прерывания назван не тем вектором (avr-gcc выдаст только предупреждение о неверно названном обработчике — misspelled signal handler, а прерывание уйдёт на сброс), забыт sei(), регистр из чужой серии (TIMSK1 у ATmega8, где он называется TIMSK), фьюз CKDIV8 делит частоту на 8 и все расчёты сдвигаются. Последнее проверьте в калькуляторе фьюзов AVR: с завода ATmega328P работает от внутреннего RC-генератора на 1 МГц.

Код сгенерирован по данным регистров, а не по готовым примерам, но перед прошивкой в устройство сверяйте его с даташитом своего микроконтроллера — особенно если меняли фьюзы тактирования. Карточки микроконтроллеров с выводами и векторами: ATmega328P, ATmega8, ATmega2560, ATtiny85; все AVR — в разделе AVR.

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

Что делает генератор кода AVR?

Пишет функции инициализации на C для выбранных блоков: USART, одного таймера в режиме CTC или быстрого ШИМ, АЦП, внешних прерываний INT0 и INT1 и выводов GPIO. Значения регистров он считает сам — UBRR, предделитель и TOP таймера, делитель АЦП, — а имена регистров и битов берёт из тех же данных, что и страницы регистров сайта. Каждое имя регистра, бита и вектора сверено с заголовками avr/io*.h пака Microchip и avr-libc; код рассчитан на avr-gcc с avr-libc, без Arduino, но перед прошивкой его стоит собрать и проверить на своей плате.

Какие микроконтроллеры поддерживаются?

ATmega328P (Arduino Uno и Nano), ATmega8, ATmega16, ATmega32, ATmega2560 (Arduino Mega) и ATtiny85. Это контроллеры, для которых на сайте есть данные регистров, сверенные с даташитом. ATtiny85 без USART, а его таймер 1 и таймер 0 ATmega8 генератор не настраивает: они устроены иначе.

Почему при 16 МГц скорость 115200 даёт ошибку?

Делитель USART целый: при U2X = 0 ближайшее UBRR = 8 даёт 111 111 бод (−3,5 %), при U2X = 1 UBRR = 16 — 117 647 бод (+2,1 %). Обе ошибки больше рекомендованных даташитом 2 и 1,5 % для 8 бит данных. Для точных скоростей берут кварцы 7,3728, 11,0592, 14,7456 или 18,432 МГц — тогда ошибка 0 %.

Как переделать код под Arduino IDE?

Функции инициализации можно вызывать из setup(), но ядро Arduino само занимает таймер 0 (millis, delay), настраивает таймеры 1 и 2 под analogWrite, а если в скетче используется Serial, подключает свой обработчик приёма USART. Поэтому в Arduino IDE не трогайте таймер 0 (сломаются millis() и delay()), а USART с прерыванием включайте только без Serial — иначе компоновщик сообщит о повторном определении вектора.

Что значит предупреждение о выводе RESET или JTAG?

PC6 у ATmega328P и PB5 у ATtiny85 — вывод сброса: портом он становится только с запрограммированным фьюзом RSTDISBL, после чего прошить микросхему по ISP уже нельзя. У ATmega16 и ATmega32 с завода включён JTAG (фьюз JTAGEN), и выводы PC2–PC5 не работают как порт, пока его не выключить фьюзом или битом JTD.

Источники

  1. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — 13.2.1, таблица 13-2 — ISC01:00; 15.7.2, 16.9.2–16.9.3 — частота CTC и быстрого ШИМ; таблицы 15-8, 15-9, 16-4, 16-5, 18-8, 18-9 — режимы WGM и предделители таймеров 0, 1, 2; таблица 20-1 — UBRR; таблицы 20-2, 20-3 — допустимая ошибка приёмника; таблицы 20-6, 20-7 — UBRR при 8 и 16 МГц (эталон тестов); 24.4 — частота АЦП 50–200 кГц; таблица 24-3 — опорные
  2. Microchip (Atmel). ATmega8 datasheet (2486AA) — таблицы 31 и 32 — ISC в MCUCR; общий адрес UBRRH и UCSRC, бит URSEL; таблица 63 — UBRR при 16 МГц; таблица 74 — опорные 00/01/11
  3. Microchip (Atmel). ATmega16 (2466T) и ATmega32 (2503Q) datasheets — таймер 0 с регистром TCCR0: таблицы 38 и 42 (ATmega16) — WGM01:00 и CS02:00; таблица 83 — опорные; выводы JTAG на PC2–PC5. ATmega32 — doc2503.pdf, те же разделы. Регистры сайта сверены с ATmega16A (8154C) и ATmega32A (DS40002072A)
  4. Microchip (Atmel). ATmega640/1280/1281/2560/2561 datasheet (2549Q) — таблица 26-3 — опорные 1,1 и 2,56 В; таблица 26-4 — входы ADC8–ADC15 через MUX5; четыре USART и таймеры 3, 4, 5
  5. Microchip (Atmel). ATtiny25/45/85 datasheet (2586Q) — таблица 9-2 — ISC01:00 в MCUCR; таблица 10-3 — ADC3 на PB3; таблица 17-3 — REFS2:0
  6. Microchip. ATmega_DFP 3.6.299 и ATtiny_DFP 3.4.278 (packs.download.microchip.com) — файлы ATDF — регистры, биты, векторы прерываний и выводы; заголовки avr/io*.h — с ними сверены все имена регистров, битов и векторов в коде (лицензия Apache-2.0 и BSD)
  7. avr-libc. Reference manual: <avr/interrupt.h>, <avr/sfr_defs.h> — макрос ISR(vector) и имена векторов <имя>_vect; _BV(bit) = (1 << bit)
Встроить калькулятор на свой сайт

Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах

<iframe src="https://gaw.ru/embed/avr-init/" title="Генератор кода инициализации периферии AVR" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/avr-init/">Генератор кода инициализации периферии AVR</a> — GAW.ru</p>
Автоподстройка высоты рамки

Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.

<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>

Обновлено .

Нашли ошибку в расчёте?