Микроконтроллер ATmega8 и ATmega8A
ATmega8 — 8-битный микроконтроллер AVR с 8 КБ флеш-памяти, 1 КБ ОЗУ и частотой до 16 МГц, предшественник семейства ATmega88/168/328P. Ниже — характеристики, распиновка и фьюзы, чем ATmega8 отличается от ATmega8A и ATmega328P и как перенести проект на ATmega88 по указанию по применению Atmel AVR094.
Обновлено Редакция GAW
ATmega8 — микроконтроллер серии megaAVR с ядром AVR, который выпускала Atmel, а теперь Microchip. Это предшественник семейства ATmega48/88/168/328: та же разводка 28-выводного корпуса, похожий набор периферии, но другие регистры, фьюзы и меньше возможностей. С ATmega8 выпускались первые платы Arduino — в файле boards.txt ядра Arduino AVR до сих пор есть плата «Arduino NG or older» с вариантом ATmega8, — а программатор USBasp работает на ATmega8 или ATmega88. Актуальная версия кристалла — ATmega8A; данные ниже взяты из его даташита DS40001974B и указаний по применению Atmel.
Коротко
- Память: 8 КБ флеш-памяти (4K слов), 1 КБ ОЗУ, 512 байт EEPROM; ресурс — 10 000 циклов записи флеш-памяти и 100 000 — EEPROM.
- Частота и питание: ATmega8A — от 2,7 до 5,5 В, до 8 МГц при 2,7 В и до 16 МГц при 4,5–5,5 В. Старые ATmega8 рассчитаны на 4,5–5,5 В и 16 МГц, ATmega8L — на 2,7–5,5 В и 8 МГц.
- Периферия: Timer0 (8 бит, только счёт), Timer1 (16 бит, два канала ШИМ), Timer2 (8 бит, один канал ШИМ, часовой кварц) — всего три канала ШИМ; 10-битный АЦП на 6 или 8 входов, USART, SPI, TWI (I²C), компаратор, сторожевой таймер.
- Тактирование: внутренний RC-генератор 1, 2, 4 или 8 МГц, кварц, внешняя RC-цепочка или внешний генератор. С завода — RC 1 МГц (Low 0xE1, High 0xD9); для кварца выше 8 МГц нужен фьюз CKOPT.
- Совместимость: выводы совпадают с ATmega88/168/328P, а регистры, векторы прерываний и фьюзы — нет. Сигнатура 0x1E 0x93 0x07 у ATmega8 и ATmega8A.
- Чего нет: прерываний по изменению уровня PCINT, отладки debugWIRE, делителя системной частоты и датчика температуры.
Характеристики ATmega8
| Параметр | Значение |
|---|---|
| Ядро | AVR, 130 команд, 32 регистра общего назначения, умножение за 2 такта |
| Производительность | до 16 MIPS на 16 МГц |
| Флеш-память | 8 КБ (4K слов), страницы по 32 слова, загрузочная секция от 256 байт до 2 КБ |
| ОЗУ (SRAM) | 1 КБ, адреса 0x0060–0x045F |
| EEPROM | 512 байт, запись байта — 8,5 мс |
| Тактовая частота | 0–16 МГц, предел зависит от напряжения питания |
| Питание | ATmega8A — 2,7–5,5 В |
| Линии ввода-вывода | 23: порт B — 8, порт C — 7, порт D — 8 |
| Таймеры | Timer0 и Timer2 — 8 бит, Timer1 — 16 бит |
| ШИМ | 3 канала: OC1A, OC1B, OC2 |
| АЦП | 10 бит, 6 входов в DIP-28, 8 — в TQFP-32 и VQFN-32; внутренняя опора 2,56 В |
| Интерфейсы | USART, SPI, TWI — совместимый с I²C |
| Прерывания | 19 векторов по одному слову, внешние — только INT0 и INT1 |
| Режимы сна | Idle, ADC Noise Reduction, Power-down, Power-save, Standby |
| Прошивка | ISP по SPI, высоковольтное параллельное программирование, самопрограммирование из загрузочной секции |
| Корпуса | PDIP-28, TQFP-32, VQFN-32 |
Ядро у ATmega8 то же, что у остальных классических ATmega: команды, флаги и такты — в статье «Система команд AVR». Набор команд у него чуть меньше, чем у ATmega328P: в даташите заявлено 130 команд против 131, нет, например, команды BREAK.
ATmega8, ATmega8L и ATmega8A
ATmega8 выпускался в двух исполнениях: ATmega8 на 4,5–5,5 В и до 16 МГц и низковольтный ATmega8L на 2,7–5,5 В и до 8 МГц. ATmega8A объединил их: указание по применению AVR523 называет его функционально идентичной прямой заменой ATmega8, у них одна сигнатура и одна прошивка. Отличаются электрические параметры — ATmega8A делают по другой технологии.
| ATmega8 | ATmega8L | ATmega8A | |
|---|---|---|---|
| Питание | 4,5–5,5 В | 2,7–5,5 В | 2,7–5,5 В |
| Частота | 0–16 МГц | 0–8 МГц | 0–8 МГц при 2,7 В, 0–16 МГц при 4,5–5,5 В |
| Активный режим, 4 МГц, 3 В | — | 3 мА | 2 мА |
| Активный режим, 8 МГц, 5 В | 11 мА | — | 6 мА |
| Idle, 4 МГц, 3 В | — | 1 мА | 0,5 мА |
| Idle, 8 МГц, 5 В | 4,5 мА | — | 2,2 мА |
Токи типовые, при 25 °C. У ATmega8 и ATmega8L общий даташит, и ток при 3 В нормирован в нём только для ATmega8L, а при 5 В и 8 МГц — для ATmega8. AVR523 называет снижение потребления главным отличием ATmega8A: от 33 до 51 % в зависимости от режима. Менять при переходе на ATmega8A ничего не нужно — ни схему, ни прошивку, ни фьюзы.
ATmega8 и семейство ATmega88/168/328P
Следующее поколение — ATmega48/88/168, позже ATmega328 — Atmel сделала совместимым с ATmega8 по выводам. Но в AVR094 прямо сказано: ATmega88 не задуман как замена ATmega8, это кристалл со сходным набором возможностей. Что в нём добавилось по сравнению с ATmega8, по тому же документу:
- расширенный диапазон: 4 МГц при 1,8 В, 10 МГц при 2,7 В, 20 МГц при 4,5 В;
- меньшее потребление и более быстрая запись EEPROM — 3,4 мс вместо 8,5 мс;
- делитель системной частоты и вывод тактового сигнала на порт;
- отладка по debugWIRE;
- прерывания по изменению уровня на любом выводе;
- сторожевой таймер с режимом прерывания;
- ШИМ и блоки сравнения у Timer0 и Timer2;
- режим ведущего SPI у USART и регистры общего назначения GPIOR.
Что это значит на практике, разобрано в разделе о замене ATmega8 на ATmega88, а сам ATmega328P — в отдельной статье об ATmega328P.
Распиновка ATmega8
Разводка корпусов у ATmega8 и ATmega8A одинакова и совпадает с ATmega88/168/328P — отличаются только альтернативные функции некоторых выводов. Номера в рамках — выводы Arduino, как их нумерует ядро Arduino AVR для платы NG с ATmega8.
Чем разметка отличается от ATmega328P:
- PD5 и PD6 — только вход счёта T1 и вход компаратора AIN0: выходов OC0B и OC0A нет, потому что у Timer0 нет блока сравнения.
- PD3 — только INT1, выхода OC2B нет: у Timer2 один канал сравнения, его выход OC2 — на PB3 вместе с MOSI.
- PB0 — только вход захвата ICP1, вывода тактового сигнала CLKO нет.
- PCINT нет ни на одном выводе: внешние прерывания — только INT0 (PD2) и INT1 (PD3).
AVCC питает АЦП, линии PC0–PC3 и входы ADC6, ADC7; PC4 и PC5 питаются от VCC. Подключать AVCC нужно всегда, даже без АЦП, с АЦП — через фильтр; разница с VCC — не больше 0,3 В. Абсолютный максимум — 40 мА на вывод и 300 мА через выводы VCC и GND; выходные уровни нормированы при 20 мА и 5 В: низкий не выше 0,9 В, высокий не ниже 4,2 В. Внутренние подтягивающие резисторы — 20–50 кОм, на RESET — 30–80 кОм.
Тактирование ATmega8
Источники тактирования и фьюз CKOPT
| CKSEL3:0 | Источник | Частота |
|---|---|---|
| 1111–1010 | кварц или керамический резонатор | 0,4–16 МГц; выше 8 МГц — только с запрограммированным CKOPT |
| 1001 | часовой кварц 32,768 кГц | 32,768 кГц |
| 1000–0101 | внешняя RC-цепочка, f ≈ 1/(3RC) | 0,1–12 МГц |
| 0100–0001 | внутренний RC-генератор | 8, 4, 2 или 1 МГц |
| 0000 | внешний тактовый сигнал на XTAL1 | 0–16 МГц |
Фьюз CKOPT у ATmega8 выбирает режим кварцевого генератора. Незапрограммированный (1) — пониженный размах на XTAL2, меньший ток и ограниченный диапазон: кварцы до 8 МГц, резонаторы тоже до 8 МГц. Запрограммированный (0) — размах от нуля до питания, работа в шумной обстановке и возможность тактировать другие микросхемы с XTAL2; с ним генератор работает с кварцами от 1 МГц и выше, а с резонаторами — до 16 МГц.
| CKOPT | CKSEL3:1 | Частота кварца | Конденсаторы C1, C2 |
|---|---|---|---|
| 1 | 101 | 0,4–0,9 МГц, только керамические резонаторы | — |
| 1 | 110 | 0,9–3,0 МГц | 12–22 пФ |
| 1 | 111 | 3,0–8,0 МГц | 12–22 пФ |
| 0 | 101, 110, 111 | от 1,0 МГц | 12–22 пФ |
В других режимах CKOPT значит другое. С внешним тактовым сигналом и внешней RC-цепочкой он подключает внутренний конденсатор 36 пФ, с часовым кварцем — внутренние конденсаторы 36 пФ на XTAL1 и XTAL2, и тогда внешние не нужны. С внутренним RC-генератором CKOPT по даташиту всегда должен оставаться незапрограммированным.
Внутренний RC-генератор и калибровка
Внутренний генератор даёт 1, 2, 4 или 8 МГц (CKSEL3:0 = 0001, 0010, 0011, 0100). Калибровочных байтов четыре — по одному на частоту, они лежат в сигнатурной строке, но при сбросе в регистр OSCCAL автоматически загружается только байт для 1 МГц. С ним частота при 5 В и 25 °C отличается от номинала не больше чем на ±3 %, а подстройкой во время работы можно добиться ±1 %. Если выбран генератор 2, 4 или 8 МГц, калибровку для этой частоты программа должна записать в OSCCAL сама: прочитать байт программатором (в avrdude — память calibration), сохранить во флеш-памяти или EEPROM и при старте переписать в OSCCAL. Без этого точность частоты не гарантирована: заводская калибровка, загруженная при сбросе, относится только к 1 МГц.
Если программа пишет флеш-память или EEPROM, OSCCAL нельзя поднимать больше чем на 10 % выше номинала: этот генератор задаёт время записи, и запись может не пройти.
У ATmega8 нет делителя системной частоты вроде CLKPR у ATmega328P: частоту выбирают только фьюзами, а внутренний генератор — одним из четырёх значений.
Фьюзы ATmega8
У ATmega8 два байта фьюзов, расширенного байта нет. Бит, равный 0, — запрограммирован.
| Байт | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| High | RSTDISBL | WDTON | SPIEN | CKOPT | EESAVE | BOOTSZ1 | BOOTSZ0 | BOOTRST |
| Low | BODLEVEL | BODEN | SUT1 | SUT0 | CKSEL3 | CKSEL2 | CKSEL1 | CKSEL0 |
- BODEN = 0 включает детектор пониженного питания, BODLEVEL выбирает порог: 1 — 2,6 В (2,4–2,9 В), 0 — 4,0 В (3,7–4,5 В).
- SUT1:0 вместе с CKSEL0 задают время запуска: для кварца — 16K тактов и дополнительно 0, 4,1 или 65 мс после сброса.
- BOOTSZ1:0 — размер загрузочной секции: 11 — 128 слов (256 байт), 10 — 256 слов, 01 — 512 слов (1 КБ), 00 — 1024 слова (2 КБ) с началом по адресам слов 0xF80, 0xF00, 0xE00 и 0xC00. BOOTRST = 0 — старт с загрузчика.
- RSTDISBL, WDTON, SPIEN, EESAVE — как у ATmega328P: вывод RESET как порт PC6, сторожевой таймер всегда включён, программирование по SPI, сохранение EEPROM при стирании кристалла.
| Настройка | Low | High | Что получится |
|---|---|---|---|
| Заводская | 0xE1 | 0xD9 | RC-генератор 1 МГц, BOD выключен, старт с адреса 0 |
| RC-генератор 8 МГц | 0xE4 | 0xD9 | 8 МГц без кварца, калибровку записать в OSCCAL программой |
| Кварц 8 МГц, BOD 2,6 В | 0x9F | 0xD9 | режим для ATmega8L и для ATmega8A с питанием от 2,7 В |
| Кварц 16 МГц, BOD 4,0 В | 0x1F | 0xC9 | CKOPT запрограммирован, вся флеш-память под программу |
| Кварц 16 МГц без BOD | 0xFF | 0xC9 | то же, детектор выключен |
| Arduino NG (ATmega8) | 0xDF | 0xCA | кварц 16 МГц, CKOPT, загрузчик 1 КБ, BOD выключен |
Значения для Arduino NG взяты из boards.txt ядра Arduino AVR: загрузчик занимает 1 КБ, под скетч остаётся 7168 байт, а загружается он на 19 200 бит/с. Записывают фьюзы avrdude, ключ -p m8:
avrdude -c usbasp -p m8 -B 10 -U lfuse:w:0x1F:m -U hfuse:w:0xC9:m
Частота SCK программатора должна быть ниже четверти тактовой частоты кристалла: новый ATmega8 работает на 1 МГц, и ключ -B 10 (около 100 кГц) здесь обязателен. Опасные ошибки те же, что у ATmega328P: выбор несуществующего источника тактирования лечится подачей внешнего такта на XTAL1, а RSTDISBL — только высоковольтным параллельным программированием. Подробнее — в разделе о фьюзах ATmega328P. У ATmega8 добавляется своя ловушка — кварц 16 МГц с незапрограммированным CKOPT. Это режим вне даташита: генератор с пониженным размахом рассчитан на кварцы только до 8 МГц, и ни запуск, ни устойчивая работа не гарантированы.
Периферия ATmega8 и отличия от ATmega328P
| ATmega8 | ATmega328P | |
|---|---|---|
| Флеш / ОЗУ / EEPROM | 8 КБ / 1 КБ / 512 байт | 32 КБ / 2 КБ / 1 КБ |
| Частота и питание | до 16 МГц, 2,7–5,5 В (ATmega8A) | до 20 МГц, 1,8–5,5 В |
| Timer0 | только счёт и прерывание по переполнению | два канала сравнения, ШИМ на PD5 и PD6 |
| Timer2 | один канал сравнения OC2 (PB3) | два канала: OC2A (PB3) и OC2B (PD3) |
| Каналы ШИМ | 3 | 6 |
| Прерывания PCINT | нет | на всех 23 линиях |
| Векторы прерываний | 19, по одному слову | 26, по два слова |
| USART | UBRRH и UCSRC по одному адресу, бит URSEL | отдельные UBRR0H и UCSR0C, режим ведущего SPI |
| Внутренняя опора АЦП | 2,56 В | 1,1 В |
| Автозапуск АЦП | только непрерывный режим, бит ADFR | 8 источников запуска: ADATE и ADTS |
| Датчик температуры | нет | вход ADC8 |
| Сторожевой таймер | 16 мс — 2,1 с, только сброс | 16 мс — 8 с, сброс и (или) прерывание |
| Внутренний RC-генератор | 1, 2, 4 или 8 МГц, автоматическая калибровка — только для 1 МГц | 8 МГц, делитель CLKPR от 1 до 256 |
| Внешняя RC-цепочка | есть | нет |
| Режимы сна | 5 | 6 и отключение BOD во сне |
| Отладка | нет | debugWIRE |
| Запись байта EEPROM | 8,5 мс | 3,3 мс |
| SBI и CBI на регистрах с флагами | сбрасывают все установленные флаги регистра | меняют только указанный бит |
| Запись 1 в PINx | не действует, регистр только для чтения | переключает бит PORTx |
| Сигнатура | 1E 93 07 | 1E 95 0F |
Таймеры: ШИМ только на трёх выводах
- Timer0 — 8-битный счётчик с делителем 1, 8, 64, 256 или 1024 и внешним входом T0 (PD4). Блоков сравнения у него нет, а значит, нет ни ШИМ, ни режима CTC: только прерывание по переполнению. Ядро Arduino для ATmega8 считает на нём
millis()с тем же делителем 64, что и на ATmega328P. - Timer1 — 16 бит, как у ATmega328P: два канала сравнения OC1A (PB1) и OC1B (PB2), захват по ICP1 (PB0) или от компаратора, все 15 режимов, включая fast PWM с верхним значением в ICR1. Код для Timer1 переносится между ATmega8 и ATmega328P почти без изменений: меняются регистры прерываний TIMSK и TIFR, а биты принудительного сравнения FOC1A и FOC1B у ATmega328P переехали из TCCR1A в TCCR1C.
- Timer2 — 8 бит, один канал сравнения OC2 (PB3), режимы normal, CTC, fast PWM и phase correct, делитель 1, 8, 32, 64, 128, 256 или 1024. Все настройки — в одном регистре TCCR2, а не в двух, как у ATmega328P. Timer2 может считать от часового кварца на TOSC1 и TOSC2, но, как и у ATmega328P, только когда ядро работает от внутреннего RC-генератора.
USART: общий адрес UBRRH и UCSRC
Главная ловушка ATmega8. Регистры UBRRH (старшие 4 бита делителя скорости) и UCSRC (формат кадра) делят один адрес ввода-вывода, и запись в него направляет старший бит байта — URSEL: 0 — запись в UBRRH, 1 — в UCSRC.
UBRRH = 0; /* URSEL = 0: this goes to UBRRH */
UBRRL = 103; /* 9600 baud at 16 MHz, +0.2 % */
UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0); /* URSEL = 1: UCSRC, 8N1 */
Если забыть URSEL и написать UCSRC = (1 << UCSZ1) | (1 << UCSZ0), байт 0x06 попадёт в UBRRH: делитель станет 0x667 = 1639, и при 16 МГц скорость упадёт с 9600 примерно до 610 бит/с. С чтением сложнее: первое чтение адреса возвращает UBRRH, а UCSRC — только второе чтение подряд, в следующем такте, поэтому на время чтения запрещают прерывания. После сброса UCSRC уже равен 0x86 — формат 8N1, — так что для обычного кадра его можно вообще не трогать.
У ATmega328P этой особенности нет: UBRR0H и UCSR0C — отдельные регистры, бита URSEL нет, а на его месте в UCSR0C — бит выбора режима UMSEL01. Формулы и таблицы скоростей у обоих одинаковые: UBRR = 103 на 16 МГц для 9600 бит/с с ошибкой 0,2 %, проверить другие сочетания поможет калькулятор скорости UART. Как устроен кадр UART и почему важна точность скорости, разобрано в статье про UART.
АЦП, компаратор, прерывания и сторожевой таймер
- АЦП — 10 бит, тактовая частота 50–200 кГц для полной точности, 13 тактов на преобразование (первое — 25), делитель 2–128 как у ATmega328P. Опорное напряжение — AREF, AVCC или внутренний источник 2,56 В. Автозапуск только один: непрерывный режим битом ADFR. Входы ADC4 и ADC5 совмещены с линиями TWI: работа TWI мешает преобразованиям только на этих двух каналах.
- Аналоговый компаратор — AIN0 (PD6) и AIN1 (PD7); бит ACME, который подключает вместо AIN1 мультиплексор АЦП, лежит в регистре SFIOR.
- Внешние прерывания — только INT0 (PD2) и INT1 (PD3), биты условий — в MCUCR, разрешения — в GICR.
- Сторожевой таймер тактируется собственным генератором; таймаут при 5 В — от 16,3 мс до 2,1 с. Прерывания по таймауту нет: только сброс.
- SPI и TWI устроены так же, как у ATmega328P: выводы PB2–PB5 и PC4, PC5. Подробно — в статьях про SPI и I²C.
Питание и режимы сна
Граница допустимой частоты у ATmega8A идёт от 8 МГц при 2,7 В до 16 МГц при 4,5 В. При питании 3,3 В это около 10,7 МГц, поэтому 3,3-вольтовые схемы на ATmega8A обычно работают на 8 МГц — от кварца или внутреннего RC-генератора. Детектор пониженного питания подстраивают под частоту: для 16 МГц — порог 4,0 В (более высокого у ATmega8 нет), для 8 МГц — 2,6 В.
| Режим | Что продолжает работать | Чем разбудить | Ток ATmega8A при 25 °C |
|---|---|---|---|
| Активный | всё | — | 2 мА (4 МГц, 3 В), 6 мА (8 МГц, 5 В) |
| Idle | всё, кроме ядра и флеш-памяти | любое прерывание | 0,5 мА (4 МГц, 3 В), 2,2 мА (8 МГц, 5 В) |
| ADC Noise Reduction | АЦП, внешние прерывания, TWI, Timer2, сторожевой таймер | окончание преобразования, сброс — в том числе от сторожевого таймера и BOD, низкий уровень на INT0 или INT1, совпадение адреса TWI, Timer2, готовность EEPROM | — |
| Power-down | сторожевой таймер, внешние прерывания, адрес TWI | сброс, сторожевой таймер, BOD, совпадение адреса TWI, низкий уровень на INT0 или INT1 | меньше 1 мкА без сторожевого таймера, меньше 10 мкА с ним (3 В) |
| Power-save | то же и Timer2 от часового кварца | то же и Timer2 | — |
| Standby | как Power-down, но генератор кварца работает | то же | — |
Токи в таблице — типовые; максимальные в Power-down при 3 В — 3 мкА без сторожевого таймера и 28 мкА с ним. Режим сна выбирают биты SM2:0 и SE в регистре MCUCR — у ATmega328P они переехали в отдельный регистр SMCR. Режима Extended Standby у ATmega8 нет: по даташиту его убрали, потому что выводы TOSC и XTAL совпадают.
Замена ATmega8 на ATmega88, ATmega168 или ATmega328P
Новый кристалл встанет в ту же панельку, но прошивка для ATmega8 на нём не заработает: её нужно пересобрать с другим -mmcu и поправить. Указание по применению AVR094 перечисляет все отличия ATmega8 от ATmega88; для ATmega168 и ATmega328P они те же, только вектор прерывания у них занимает два слова.
Векторы прерываний
У ATmega88 добавились прерывания PCINT, сторожевого таймера и вторых каналов сравнения таймеров 0 и 2, поэтому все векторы, кроме сброса, INT0 и INT1, сдвинулись. Таблица векторов, собранная avr-gcc для ATmega8, на ATmega88 вызовет не те обработчики.
| Прерывание | ATmega8: номер, адрес | ATmega88: номер, адрес | ATmega168 и ATmega328P: адрес | Имя в avr-libc: ATmega8 → ATmega328P |
|---|---|---|---|---|
| Сброс | 1, 0x000 | 1, 0x000 | 0x0000 | — |
| INT0, INT1 | 2, 3: 0x001, 0x002 | 2, 3: 0x001, 0x002 | 0x0002, 0x0004 | без изменений |
| PCINT0–PCINT2 | — | 4–6: 0x003–0x005 | 0x0006–0x000A | новые |
| Сторожевой таймер | — | 7: 0x006 | 0x000C | WDT_vect, новый |
| Timer2: совпадение | 4: 0x003 | 8: 0x007 (канал A), 9: 0x008 (канал B) | 0x000E, 0x0010 | TIMER2_COMP_vect → TIMER2_COMPA_vect |
| Timer2: переполнение | 5: 0x004 | 10: 0x009 | 0x0012 | без изменений |
| Timer1: захват, совпадения A и B, переполнение | 6–9: 0x005–0x008 | 11–14: 0x00A–0x00D | 0x0014–0x001A | без изменений |
| Timer0: совпадения A и B | — | 15, 16: 0x00E, 0x00F | 0x001C, 0x001E | новые |
| Timer0: переполнение | 10: 0x009 | 17: 0x010 | 0x0020 | без изменений |
| SPI | 11: 0x00A | 18: 0x011 | 0x0022 | без изменений |
| USART: приём, буфер пуст, передача | 12–14: 0x00B–0x00D | 19–21: 0x012–0x014 | 0x0024–0x0028 | USART_RXC_vect → USART_RX_vect, USART_TXC_vect → USART_TX_vect |
| АЦП | 15: 0x00E | 22: 0x015 | 0x002A | без изменений |
| EEPROM готово | 16: 0x00F | 23: 0x016 | 0x002C | EE_RDY_vect → EE_READY_vect |
| Компаратор | 17: 0x010 | 24: 0x017 | 0x002E | ANA_COMP_vect → ANALOG_COMP_vect |
| TWI | 18: 0x011 | 25: 0x018 | 0x0030 | без изменений |
| SPM готово | 19: 0x012 | 26: 0x019 | 0x0032 | SPM_RDY_vect → SPM_READY_vect |
В C достаточно поменять имена векторов в ISR(): avr-gcc сам построит таблицу. В ассемблере таблицу переписывают целиком, а у ATmega168 и ATmega328P вместо RJMP в векторах ставят JMP.
Регистры и биты
Почти все регистры ATmega8 у ATmega88 переехали на другие адреса, многие — в расширенное пространство ввода-вывода 0x60–0xFF. В программе на C с символическими именами это незаметно, а в ассемблере команды IN, OUT, SBI и CBI для таких регистров заменяют на LDS и STS: например, регистры USART, TWI, Timer1, Timer2 и АЦП доступны только через память данных. Переименования из AVR094:
| ATmega8 | ATmega88/168/328P | Что изменилось |
|---|---|---|
| UDR, UCSRA, UCSRB, UBRRL | UDR0, UCSR0A, UCSR0B, UBRR0L | добавлен номер 0; так же биты: RXC → RXC0, U2X → U2X0, PE → UPE0, UCSZ0 → UCSZ00 |
| UBRRH и UCSRC по одному адресу | UBRR0H и UCSR0C — разные регистры | бита URSEL нет; UMSEL → UMSEL00 |
| TCCR0 | TCCR0B (биты делителя) и новый TCCR0A | Timer0 получил режимы ШИМ и CTC |
| TCCR2, OCR2 | TCCR2A, TCCR2B, OCR2A | COM2x и WGM20, WGM21 — в TCCR2A; делитель, WGM22 и FOC2A — в TCCR2B |
| TIMSK, TIFR | TIMSK0–2, TIFR0–2 | по регистру на каждый таймер |
| GICR | EIMSK, MCUCR | INT0, INT1 — в EIMSK; IVSEL, IVCE — в MCUCR |
| GIFR | EIFR | — |
| MCUCR | EICRA, SMCR | ISC — в EICRA; SE и SM2:0 — в SMCR |
| MCUCSR | MCUSR | — |
| SFIOR | ADCSRB, MCUCR, GTCCR | ACME — в ADCSRB, PUD — в MCUCR, PSR10 → PSRSYNC и PSR2 → PSRASY — в GTCCR |
| WDTCR | WDTCSR | другие значения делителя, появился режим прерывания |
| SPMCR | SPMCSR | — |
| ADCSRA: ADFR | ADCSRA: ADATE и ADCSRB: ADTS2:0 | непрерывный режим — один из источников автозапуска |
| EECR: EEMWE, EEWE | EECR: EEMPE, EEPE | — |
| ASSR: OCR2UB, TCR2UB | ASSR: OCR2AUB, TCR2AUB | добавлены флаги для канала B |
Фьюзы, тактирование и аналоговая часть
- Фьюзы придётся считать заново. У ATmega88 три байта: BOOTSZ и BOOTRST переехали в расширенный байт (у ATmega328P они остались в старшем), детектор пониженного питания задают три бита BODLEVEL2:0, CKOPT исчез, появились CKDIV8, CKOUT и DWEN. Пороги BOD другие: 1,8, 2,7 и 4,3 В у ATmega88 против 2,6 и 4,0 В у ATmega8 — сопоставимые пороги у нового кристалла выше.
- Тактирование. Источники почти те же, кроме внешней RC-цепочки — у ATmega88 её нет. Ко всем задержкам запуска добавлено 14 тактов, а значения CKSEL и SUT нужно подобрать заново по даташиту. Калиброванный внутренний генератор у ATmega88 один — 8 МГц (есть ещё неточный на 128 кГц); с завода он работает через делитель на 8, калибровочный байт загружается автоматически, а другую частоту задают делителем CLKPR, а не фьюзами.
- Опорные напряжения. Внутренний источник для АЦП — 1,1 В вместо 2,56 В, для компаратора — 1,1 В вместо 1,23 В. Коэффициенты пересчёта кодов АЦП в напряжение придётся поменять.
- Сторожевой таймер настраивается другими значениями делителя и отдельной последовательностью: бит WDE нельзя сбросить, пока установлен флаг WDRF в MCUSR.
- Сигнатура ATmega88 — 0x1E 0x93 0x0A, у ATmega88PA — 0x1E 0x93 0x0F: в avrdude меняется ключ
-p.
Порядок переноса
- Пересобрать проект с
-mmcu=atmega88(или atmega168, atmega328p) и исправить ошибки компиляции по таблице регистров выше — это быстрее всего находит устаревшие имена. - Переписать инициализацию USART без URSEL, таймеров 0 и 2 — с учётом TCCRxA и TCCRxB, прерываний — с EIMSK, EICRA и TIMSKx.
- Поменять имена векторов в
ISR(); в ассемблере переписать таблицу векторов и обращения к регистрам, переехавшим в расширенное пространство ввода-вывода. - Пересчитать фьюзы под новый кристалл и записать их до первой прошивки: не забыть, что с завода включён делитель на 8.
- Проверить АЦП с внутренней опорой и сторожевой таймер — у них изменились напряжение и логика.
Минимальная схема ATmega8
Схема включения ATmega8 такая же, как у ATmega328P: разводка выводов совпадает, а развязку питания, сброс и кварц AVR042 рекомендует одинаково для всех AVR.
- Кварц 16 МГц с конденсаторами 22 пФ и фьюзы Low 0x1F, High 0xC9 — CKOPT запрограммирован. Для кварца до 8 МГц CKOPT можно не программировать: генератор с пониженным размахом потребляет меньше.
- Без кварца XTAL1 и XTAL2 становятся линиями PB6 и PB7, а кристалл работает от внутреннего генератора 1–8 МГц — для UART без калибровки его точности обычно не хватает.
- Развязка: по 100 нФ у выводов 7–8 и 20–22, AVCC — к питанию даже без АЦП, AREF — конденсатор 100 нФ на общий провод при опоре AVCC или 2,56 В.
- Сброс: подтяжка 10 кОм. debugWIRE у ATmega8 нет, поэтому конденсатор от RESET на общий провод, который AVR042 советует для защиты от помех, ставить можно — но вместе с C6 он образует делитель и ослабляет импульс автосброса.
- ISP — те же выводы MOSI, MISO, SCK и RESET (17, 18, 19 и 1), что и у ATmega328P. USB–UART с цепочкой DTR — C6 — RESET нужен, только если в кристалле есть загрузчик.
Пример на C: один исходник для ATmega8 и ATmega328P
Программа принимает символы через USART на 9600 бит/с, отправляет их обратно и цифрами 0–9 задаёт яркость светодиода на выводе PB3 (OC2 у ATmega8, OC2A у ATmega328P, D11 на Arduino). Кварц — 16 МГц. Различия регистров собраны в двух ветках #if, так что файл собирается для обоих кристаллов — это заготовка для переноса старого проекта.
/* USART echo at 9600 baud, 8N1, and an LED dimmer on PB3; 16 MHz crystal.
* Builds for ATmega8 (-mmcu=atmega8) and ATmega88/168/328P (-mmcu=atmega328p). */
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#define BAUD 9600
#include <avr/io.h>
#include <stdint.h>
#include <util/setbaud.h>
#if defined(__AVR_ATmega8__)
/* ATmega8: UBRRH and UCSRC share one address, URSEL selects UCSRC */
static void uart_init(void)
{
UBRRH = UBRRH_VALUE; /* URSEL = 0: this is UBRRH */
UBRRL = UBRRL_VALUE; /* 103 at 16 MHz */
UCSRA = USE_2X ? _BV(U2X) : 0;
UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); /* URSEL = 1: this is UCSRC, 8N1 */
UCSRB = _BV(RXEN) | _BV(TXEN);
}
#define UART_RX_READY() (UCSRA & _BV(RXC))
#define UART_TX_READY() (UCSRA & _BV(UDRE))
#define UART_DATA UDR
static void pwm_init(void)
{
DDRB |= _BV(PB3); /* OC2 */
TCCR2 = _BV(WGM21) | _BV(WGM20) | _BV(COM21) | _BV(CS22); /* fast PWM, clk/64 */
}
#define PWM_DUTY OCR2
#else
/* ATmega88/168/328P: separate UBRR0H and UCSR0C, timer 2 has TCCR2A and TCCR2B */
static void uart_init(void)
{
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0A = USE_2X ? _BV(U2X0) : 0;
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8N1, no URSEL bit */
UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}
#define UART_RX_READY() (UCSR0A & _BV(RXC0))
#define UART_TX_READY() (UCSR0A & _BV(UDRE0))
#define UART_DATA UDR0
static void pwm_init(void)
{
DDRB |= _BV(PB3); /* OC2A */
TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); /* fast PWM on OC2A */
TCCR2B = _BV(CS22); /* clk/64 */
}
#define PWM_DUTY OCR2A
#endif
int main(void)
{
uart_init();
pwm_init();
for (;;) {
while (!UART_RX_READY()) {}
uint8_t c = UART_DATA;
if (c >= '0' && c <= '9') {
PWM_DUTY = (uint8_t)((c - '0') * 28); /* '0'...'9' -> OCR 0...252, duty (OCR + 1) / 256 */
}
while (!UART_TX_READY()) {}
UART_DATA = c; /* echo */
}
}
Частота ШИМ в обоих случаях — 16 МГц / (64 · 256) ≈ 976,6 Гц, а скважность в режиме fast PWM — (OCR + 1) / 256: даже при нуле на выходе остаётся узкий импульс в один такт таймера. У ATmega8 делитель 64 (бит CS22) и режим fast PWM (биты WGM21 и WGM20) задаются в одном регистре TCCR2, у ATmega328P CS22 лежит в TCCR2B, а WGM21 и WGM20 — в TCCR2A. Сборка и прошивка через USBasp:
avr-gcc -mmcu=atmega8 -Os -o echo.elf echo.c
avr-objcopy -O ihex -R .eeprom echo.elf echo.hex
avrdude -c usbasp -p m8 -U flash:w:echo.hex:i
Для ATmega328P меняются -mmcu=atmega328p и -p m328p. Светодиод на PB3 подключают через резистор: его номинал для нужного тока посчитает калькулятор резистора для светодиода. На время прошивки по ISP помните, что PB3 — это ещё и MOSI: нагрузка на нём не должна мешать программатору.
Типичные ошибки
| Симптом | Вероятная причина | Что сделать |
|---|---|---|
| Новый кристалл не отвечает программатору | частота 1 МГц, SCK программатора выше 250 кГц | -B 10 в avrdude или перемычка медленного режима |
| USART работает на неверной скорости или формат кадра не тот | UCSRC записан без URSEL, и байт попал в UBRRH | UCSRC = (1 << URSEL) | ... |
| Кварц 16 МГц не запускается или работает со сбоями | CKOPT не запрограммирован | High с CKOPT = 0, например 0xC9 |
| После перехода на 8 МГц от RC-генератора UART и задержки неточны | в OSCCAL осталась калибровка для 1 МГц | записать в OSCCAL калибровочный байт для 8 МГц |
analogWrite() на D3, D5, D6 даёт только 0 или 1 | у ATmega8 нет ШИМ на этих выводах | использовать D9, D10, D11 |
Флаг прерывания пропал после sbi в соседний бит | SBI и CBI у ATmega8 перезаписывают весь регистр и сбрасывают установленные флаги | менять биты регистров с флагами записью целого значения |
| Код ATmega328P не компилируется: нет TIMSK1, UCSR0A, PCICR | другие имена регистров или их нет вовсе | таблица регистров из раздела о замене, PCINT заменить на INT0 и INT1 |
| Прошивка для ATmega8 на ATmega88 зависает в прерываниях | таблица векторов собрана для ATmega8 | пересобрать с нужным -mmcu, поправить имена векторов |
| Программатор пишет «Device signature = 0x1e930a» | вместо ATmega8 стоит ATmega88 | -p m88 и проект, перенесённый на ATmega88 |
Если нужен 28-выводный AVR для нового устройства, проще сразу взять ATmega328P: та же разводка, вчетверо больше флеш-памяти и вдвое больше каналов ШИМ. Старший кристалл с 128 КБ флеш-памяти и JTAG разобран в статье об ATmega128, сравнение всех семейств и программаторов — в разделе об AVR, а общий обзор микроконтроллеров — в разделе «Микроконтроллеры».
Частые вопросы
Чем ATmega8 отличается от ATmega8A?
По функциям ничем: в указании по применению AVR523 Atmel называет ATmega8A функционально идентичной прямой заменой ATmega8, сигнатура у обоих 0x1E 0x93 0x07, прошивка и фьюзы переносятся без изменений. ATmega8A выпускается по другой технологии: он работает от 2,7 до 5,5 В на частотах до 16 МГц, тогда как ATmega8 рассчитан на 4,5–5,5 В, а для 2,7–5,5 В выпускался ATmega8L до 8 МГц. Типовой ток у ATmega8A меньше на 33–51 %: например, 6 мА вместо 11 мА в активном режиме на 8 МГц при 5 В.
Можно ли заменить ATmega8 на ATmega88, ATmega168 или ATmega328P?
По выводам — да, разводка корпусов DIP-28 и TQFP-32 одинаковая. Но прошивку придётся пересобрать и поправить: по AVR094 у ATmega88 26 векторов прерываний вместо 19, большая часть регистров переименована или переехала в расширенное пространство ввода-вывода, у USART больше нет общего адреса UBRRH и UCSRC, а фьюзы тактирования и детектора пониженного питания устроены иначе и CKOPT исчез. Кристалл с завода работает на 1 МГц, как и ATmega8, но уже от RC-генератора 8 МГц с делителем на 8.
Какие фьюзы поставить ATmega8 для кварца 16 МГц?
Low = 0x1F и High = 0xC9: CKSEL3:0 = 1111 и SUT1:0 = 01 выбирают кварц с запуском за 16K тактов, BOD включён с порогом 4,0 В, а запрограммированный CKOPT включает генератор с полным размахом — без него ATmega8 рассчитан на кварцы только до 8 МГц. Если детектор пониженного питания не нужен, Low = 0xFF. Плата Arduino NG с загрузчиком использует Low = 0xDF и High = 0xCA.
Почему ATmega8 работает на 1 МГц и как включить 8 МГц без кварца?
С завода фьюзы выбирают внутренний RC-генератор 1 МГц: Low = 0xE1. Для 8 МГц записывают Low = 0xE4 (CKSEL3:0 = 0100). Но автоматически при сбросе загружается только калибровочный байт для 1 МГц; калибровки для 2, 4 и 8 МГц хранятся в сигнатурной строке, и программа должна сама записать нужную в регистр OSCCAL — иначе частота может заметно отличаться от 8 МГц.
Почему на ATmega8 не работает analogWrite на выводах 3, 5 и 6?
У ATmega8 всего три аппаратных канала ШИМ: OC1A и OC1B таймера 1 на выводах PB1 и PB2 (Arduino D9 и D10) и OC2 таймера 2 на PB3 (D11). Таймер 0 умеет только считать и вызывать прерывание по переполнению, а у таймера 2 один канал сравнения вместо двух. Поэтому ядро Arduino для ATmega8 выдаёт ШИМ только на D9, D10 и D11.
Почему USART на ATmega8 работает не на той скорости?
Скорее всего, при записи UCSRC не установлен бит URSEL. У ATmega8 регистры UBRRH и UCSRC делят один адрес, и бит 7 записываемого байта выбирает регистр: 0 — UBRRH, 1 — UCSRC. Строка UCSRC = (1 << UCSZ1) | (1 << UCSZ0) без URSEL запишет 0x06 в старший байт делителя скорости, и при 16 МГц вместо 9600 бит/с получится около 610 бит/с. Правильно: UCSRC = (1 << URSEL) | (1 << UCSZ1) | (1 << UCSZ0).
Источники
- Microchip. ATmega8A Data Sheet (DS40001974B), 2020 — распиновка, тактирование и CKOPT, калибровка RC-генератора, режимы сна, сторожевой таймер, векторы прерываний, таймеры, доступ к UBRRH и UCSRC, АЦП, загрузчик, фьюзы и сигнатура, электрические характеристики и рис. 26-1
- Atmel. ATmega8/ATmega8L Datasheet (2486AA–AVR–02/2013) — напряжения, частоты и токи ATmega8 и ATmega8L
- Atmel. AVR094: Replacing ATmega8 by ATmega88 (2553C–AVR–04/05) — таблицы векторов и регистров, переименованные биты, тактирование, BOD, доступ к UCSRC, опорные напряжения, фьюзы, сигнатуры, время записи EEPROM, напряжения и частоты
- Atmel. AVR523: Migration from ATmega8 to ATmega8A (8164A–AVR–05/09) — ATmega8A — функционально идентичная прямая замена; сравнение токов потребления
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B), 2020 — векторы прерываний ATmega328P, поведение SBI и CBI, переключение выводов записью в PINx
- Atmel. AVR042: AVR Hardware Design Considerations (Atmel-2521R), 09/2016 — развязка питания, AVCC и AREF, цепь сброса, конденсаторы кварца
- Arduino. ArduinoCore-avr: boards.txt, variants/standard/pins_arduino.h, cores/arduino/wiring.c — плата Arduino NG or older с ATmega8: фьюзы, загрузчик, 7168 байт под скетч, 19 200 бит/с; выводы ШИМ D9–D11; Timer0 без ШИМ
- AVR-LibC: заголовки avr/iom8.h и avr/iom328p.h — имена регистров, битов и векторов прерываний
- AVRDUDE: руководство (avrdude.1) — USBasp работает на ATmega8 или ATmega88; ключ -B для частоты SCK
Смотрите также
- AVRATmega, ATtiny, AVR Dx/Ex: семейства, прошивка, инструменты
- Система команд AVRВсе инструкции с флагами и тактами, адресация, примеры кода
- ATmega128Память, распиновка, фьюзы, M103C и JTAG, ISP через PE0/PE1
- UARTКадр 8N1, скорость и допуск, TX и RX, уровни, USB–UART, AVR и STM32
- SPISCLK, MOSI, MISO, CS, режимы 0–3, daisy chain, частота, SPI в AVR
- I²CSDA и SCL, START/STOP, ACK, адреса, скорости, расчёт подтяжек
- МикроконтроллерыAVR, ARM Cortex-M, ESP32, PIC, MSP430: архитектура и выбор