Типы переменных
Тип char
Символьный тип (char) является самым экономным типом данных в языке С поскольку использует всего один байт (8 бит) в памяти (микроконтроллера). Может быть знаковым (обозначается как «signed char») и беззнаковым («unsigned char»). Соответственно, в переменной char знакового типа можно хранить значения в диапазоне от -128 до +127, а в переменной беззнакового типа — от 0 до 255.
Фактически, ключевые слова (модификаторы) signed и unsigned в начале объявления типа char интерпретируется как нулевой бит объявляемой переменной. Если unsigned, то все 8 бит интерпретируются как число, поэтому максимальное значение переменной в этом случае составляет 2^8-1=255. Если тип signed, то в этом случае нулевой бит отводится под хранение знака числа (+ (не отображается) или -), соответственно под хранение значения переменной остается всего 7 бит, что соответствует максимальному хранимому значению 127 (128).
Тип int
Переменная целого типа int может быть short (короткой) или long (длинной). При этом модификаторы short и long ставятся после ключевых слов signed или unsigned, поэтому получаем такие типы данных как signed short int, unsigned short int, signed long int, unsigned long int.
Если используется модификатор short, то под хранение переменной целого типа с таким модификатором отводится 2 байта (16 бит). Соответственно, для unsigned short int получаем диапазон хранимых значений от 0 до 65535, а для signed short int — от -32768 до +32767.
При объявлении переменной типа signed short int ключевые слова signed и short можно опускать, поэтому чаще всего при объявлении подобных переменных просто пишут слово int. Вместо int также можно писать просто short, но этим мало кто пользуется, большинство программистов привыкли именно к int.
Переменная типа long int (знаковая (signed) или беззнаковая (unsigned) занимает уже 4 байта (32 бита) в памяти, поэтому диапазоны значений переменных для этих типов будут составлять, соответственно, от -2147483648 до 2147483647 и от 0 до 4294967295.
В языке С существуют еще переменные типа long long int, для хранения которых выделяется 8 байт памяти (64 бита), но вряд ли когда-нибудь в программах для микроконтроллеров у вас возникнет необходимость в использовании столь больших чисел.
Таблица со всеми возможными целыми типами языка С и их параметрами приведена на следующем рисунке.
Исходный код программы на языке С (Си)
Программа для передающей части
C++
#include <io.h>
#include <delay.h>
void main(void)
{
DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=0
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
// инициализация Port C
// Function: Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRC=(1<<DDC6) | (1<<DDC5) | (1<<DDC4) | (1<<DDC3) | (1<<DDC2) | (1<<DDC1) | (1<<DDC0);
// State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
// инициализация Port D
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(1<<DDD7) | (1<<DDD6) | (1<<DDD5) | (1<<DDD4) | (1<<DDD3) | (1<<DDD2) | (1<<DDD1) | (1<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
while (1)
{
if(PINB.0 == 1) {
PORTD = 0x10;
}
if(PINB.0 == 0) {
PORTD = 0x20;
}
}
}
1 |
#include <io.h> voidmain(void) { DDRB=(<<DDB7)|(<<DDB6)|(<<DDB5)|(<<DDB4)|(<<DDB3)|(<<DDB2)|(<<DDB1)|(<<DDB0); // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=0 PORTB=(<<PORTB7)|(<<PORTB6)|(<<PORTB5)|(<<PORTB4)|(<<PORTB3)|(<<PORTB2)|(<<PORTB1)|(<<PORTB0); // инициализация Port C DDRC=(1<<DDC6)|(1<<DDC5)|(1<<DDC4)|(1<<DDC3)|(1<<DDC2)|(1<<DDC1)|(1<<DDC0); // State: Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T PORTC=(<<PORTC6)|(<<PORTC5)|(<<PORTC4)|(<<PORTC3)|(<<PORTC2)|(<<PORTC1)|(<<PORTC0); // инициализация Port D DDRD=(1<<DDD7)|(1<<DDD6)|(1<<DDD5)|(1<<DDD4)|(1<<DDD3)|(1<<DDD2)|(1<<DDD1)|(1<<DDD0); // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T PORTD=(<<PORTD7)|(<<PORTD6)|(<<PORTD5)|(<<PORTD4)|(<<PORTD3)|(<<PORTD2)|(<<PORTD1)|(<<PORTD0); while(1) { if(PINB.==1){ PORTD=0x10; } if(PINB.==){ PORTD=0x20; } } } |
Программа для приемной части
C++
#include <io.h>
#include <delay.h>
// объявляем глобальные переменные
unsigned char byte = 0;
unsigned char lightON = 0;//статус света
int LED_status = 0;
void main(void)
{
// инициализация Port B
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=Out Bit0=Out
DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (1<<DDB0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=0 Bit0=0
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
// инициализация Port D
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
while (1)
{
byte = PIND;
if(PIND.7==0 && PIND.6==0 && PIND.5==0 && PIND.4==1 && LED_status==0)
{
PORTB.0 = ~PORTB.0;
delay_ms(1000);
}
}
}
1 |
#include <io.h> unsignedcharbyte=; unsignedcharlightON=;//статус света intLED_status=; voidmain(void) { DDRB=(<<DDB7)|(<<DDB6)|(<<DDB5)|(<<DDB4)|(<<DDB3)|(<<DDB2)|(<<DDB1)|(1<<DDB0); // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=0 Bit0=0 PORTB=(<<PORTB7)|(<<PORTB6)|(<<PORTB5)|(<<PORTB4)|(<<PORTB3)|(<<PORTB2)|(<<PORTB1)|(<<PORTB0); // инициализация Port D DDRD=(<<DDD7)|(<<DDD6)|(<<DDD5)|(<<DDD4)|(<<DDD3)|(<<DDD2)|(<<DDD1)|(<<DDD0); // State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T PORTD=(<<PORTD7)|(<<PORTD6)|(<<PORTD5)|(<<PORTD4)|(<<PORTD3)|(<<PORTD2)|(<<PORTD1)|(<<PORTD0); while(1) { byte=PIND; if(PIND.7==&&PIND.6==&&PIND.5==&&PIND.4==1&&LED_status==) { PORTB.=~PORTB.; delay_ms(1000); } } } |
________________________________________________________
Для каждого типа микроконтроллера есть свой заголовочный файл. Для ATMega8535 этот файл называется iom8535.h, для ATMega16 – iom16.h. По идее мы должны в начале каждой программы подключать заголовочный файл того микроконтроллера, который мы используем. Умные люди немного облегчили нам жизнь и написали заголовочный файл ioavr.h. Препроцессор обрабатывает этот файл и в зависимости от настроек проекта включает в нашу программу нужный заголовочный файл. Итак, следущая строчка программы #include <ioavr.h> В нашей программе мы будем использовать задержку. Задержку можно реализовать программно и аппаратно. Сейчас нас интересует программная задержка. IAR содержит библиотеку, в которой уже есть готовая функция задержки. Нам нужно подключить к нашей программе эту библиотеку. Как это сделать? Каждая библиотека имеет свой заголовочный файл в котором описано какие фукции она содержит. Этот файл мы и должны включить в программу. Делается это, как вы догадались с помощью директивы #include.#include <intrinsics.h> Основу любой сишной программы составляют функции, и любая программа на Си имеет хотя бы одну функцию – main().Вообще-то на примере main() не хотелось бы объяснять синтаксис функций, потому что main() хоть и является функцией, но вызывается не как обычно, а автоматически. С этой функции микроконтроллер начинает выполнение написанной нами программы. Вызовы всех других функций, наших или библиотечных, должны быть записаны в коде. Как вызывается функция, мы увидим дальше.У функции есть заголовок – int main(void) и тело – оно ограниченно фигурными скобками {}. В тело функции мы и будем добавлять наш код.
3.1 Совет #1 — типы данных и размеры
Используйте наименьший тип данных, способный представить вашу переменную. Для чтения 8-и разрядного регистра, например, не нужно использовать 2-х байтную переменную, достаточно однобайтной. Размеры типов данных для AVR можно посмотреть в заголовочном файле stdint.h и в таблице 3-1.Таблица 3-1. Типы данных для AVR, описанные в stdint.h
Имейте в виду, что некоторые опции компилятора могут повлиять на типы данных (например, AVR-GCC имеет опцию –mint8, которая делает тип int 8-и разрядным).
Два примера кода в таблице 3-2 показывают результат использования разных типов данных. Также в таблице показаны размеры кода, получаемого при построении проекта с опцией оптимизации –Os (оптимизация для размера).Таблица 3-2. Пример использования различных типов данных.
В левом примере мы пользуемся 2-х байтным типом данных для временной переменной и возвращаемого значения. В правом примере вместо этого используется однобайтный тип char. Считывание данных происходит с 8-и разрядного регистра ADCH, а значит такого размера переменной вполне достаточно. Это позволяет нам сэкономить 2 байта памяти.
Заметьте, что до запуска функции main() есть начальный код инициализации (startup code), поэтому эти простые примеры Си кода занимают 90 байт.
3.6 Совет #6 – типы доступа: static
Для глобальных данных использование ключевого слова static не всегда возможно. Если глобальные переменные объявлены с ключевым словом static, они могут быть доступны только в том файле, внутри которого они определены. Это предотвращает случайное использование переменной в других файлов. С другой стороны, объявления локальных переменных внутри функции с ключевым словом static следует избегать. Значение статической переменной сохраняется между вызовами функции, и эта переменная сохраняется в течение всей программы. Таким образом, она требует постоянного места хранения в ОЗУ и дополнительного кода для доступа к ней. Это похоже на глобальную переменную, только область видимости статической переменной ограничена телом функции, в котором она объявлена. Статическими можно объявлять также функции. В этом случае область видимости функции будет тоже ограничена файлом, в котором она объявлена, и вызвать ее можно будет только оттуда. По этим причинам компилятору проще оптимизировать такие функции. Если статическая функция вызывается в файле всего один раз и оптимизация разрешена (-O1, -O2, -O3, -Os), компилятор сделает эту функцию встраиваемой. Таблица 3-7. Пример использования типов доступа – статическая функция.
Заметьте, если функция вызывается несколько раз, она не будет встраиваться, потому что это увеличит код больше, чем прямой вызов функции.
Создание проекта для Atmega8 в программной среде CodeVision
Необходимо выполнить следующую последовательность действий.
Шаг 1. Создайте новый проект в CodeVision, выбрав пункт меню File -> New -> Project. В появившемся диалогом окне нажмите Yes.
Шаг 2. Откроется CodeWizard. Кликните в ней на первой опции, то есть AT90, затем нажмите OK.
Шаг 3. Выберите свой микроконтроллер, в нашем случае им будет Atmega8.
Шаг 4. Кликните на Ports (порты). В нашем проекте на передающей стороне мы будем подсоединять кнопку на вход и 4 линии будем использовать на выход. То есть мы должны сконфигурировать 4 контакта Atmega8 на выход. Кликните на Port D, сконфигурируйте в нем биты 7, 6, 5 и 4 на выход.
Шаг 5. Выберите Program -> Generate, Save and Exit. Теперь более половины вашей работы по программированию микроконтроллера Atmega8 можно считать выполненной.
Шаг 6. Создайте новую папку на рабочем столе чтобы записывать туда наши файлы.
У нас будет 3 диалоговых окна (будут появляться последовательно одно за другим) для сохранения наших файлов.
Сделайте то же самое (что и на представленном рисунке) с двумя другими диалоговыми окнами – то есть сохраните предлагаемые ими файлы.
После этого рабочая область программы будет выглядеть примерно следующим образом:
Теперь большая часть работы по программированию микроконтроллера Atmega8 нами выполнена с использованием такой удобной программной среды как CodeVision. Теперь нам необходимо только дописать несколько строк кода для передающей и приемной части чтобы закончить проект.
Для приемной стороны повторите рассмотренные шаги, отличие будет состоять только в том, чтобы сконфигурировать контакт B0 PortB на выход потому что мы будем подключать к нему светодиод.
Обозначения индексов микроконтроллеров
После обозначения базовой версии и серии микроконтроллера, через дефис идет индекс, указывающий вариант исполнения микроконтроллера.Индекс состоит из 1-2 цифр, которые означают максимальную частоту, на которой микроконтроллер может стабильно работать при нормальном для него напряжении питания, и из 1-3 букв, которые обозначают вариант корпуса, температурный диапазон работы, и особенности изготовления.Первая буква (или две буквы) после частоты обозначает тип корпуса:P — корпус DIP (PDIP)A — корпус TQFPM — корпус MLFTS — корпус SOT-23 (ATtiny4/5/9/10)J — корпус PLCCA — корпус UDFN/USONC — корпус CBGACK — корпус LGAS — корпус EIAJ SOICSS — узкий корпус JEDEC SOICT — корпус TSOPX — корпус TSSOP
Следующая буква означает температурный диапазон и особенности изготовления:C — коммерческий температурный диапазон (0 °C — 70 °C)A — температурный диапазон −20 °C — +85 °C, с использованием бессвинцового припояI — индустриальный температурный диапазон (-40 °C — +85 °C)U — индустриальный температурный диапазон (-40 °C — +85 °C), с использованием бессвинцового припояH — индустриальный температурный диапазон (-40 °C — +85 °C), с использованием NiPdAuN — расширенный температурный диапазон (-40 °C — +105 °C), с использованием бессвинцового припояF — расширенный температурный диапазон (-40 °C — +125 °C)Z — автомобильный температурный диапазон (-40 °C — +125 °C)D — расширенный автомобильный температурный диапазон (-40 °C — +150 °C)
Еще в самом конце может быть буква R, которая означает, что микроконтроллеры упакованы в ленты для автоматизированных систем сборки
К примеру:ATmega8L-8AU — максимальная частота — 8 мегагерц, корпус — TQFP, индустриальный температурный диапазон (-40 °C — +85 °C), с использованием бессвинцового припояATmega8-16PN — максимальная частота — 16 мегагерц, корпус — PDIP, расширенный температурный диапазон (-40 °C — +105 °C), с использованием бессвинцового припоя
Если вы знаете, что обозначают буквы и цифры в маркировке микроконтроллера, значит знаете основные параметры микроконтроллеров, и всегда сможете подобрать для своей конструкции наиболее оптимальный вариант микроконтроллера.
Линейка микроконтроллеров ATmegaЛинейка микроконтроллеров ATtiny
Маркировка микроконтроллеров AVR ATmega и ATtinyМаркировка микроконтроллеров AVR семейства ATmega и ATtiny, базовые версии и версии микроконтроллеров, индекс микроконтроллеров
Published by: Мир микроконтроллеров
Date Published: 04/27/2015
3.7 Совет #7 – низко уровневые ассемблерные инструкции
Ассемблерные команды всегда лучше оптимизированного кода. Единственный недостаток ассемблерного кода – это непереносимый синтаксис, так что это в большинстве случаев его не рекомендуется использовать. Чтобы улучшить читаемость и портируемость кода, можно использовать ассемблерные макросы. Такими макросами можно заменять функции, которые генерируются в 2-3 ассемблерные строки. В таблице 3-8 показан пример использования ассемблерного макроса вместо функции. Таблица 3-8. Пример использования низкоуровневых ассемблерных инструкций.
Для более подробной информации относительно использования ассемблера с языком Си на AVR, ознакомьтесь с разделом “Inline Assembler Cookbook” в руководстве на avr-libc.
Ссылки
Atmel AVR4027: Tips and Tricks to Optimize Your C Code for 8-bit AVR MicrocontrollersAVR4027: Трюки и советы по оптимизации Си кода для 8-и разрядных AVR микроконтроллеров. Ч.2Проект к статье — avr4027.zipВольный перевод — ChipEnable.Ru
Инкремент и декремент
В тех случаях, когда необходимо изменить значение переменной на 1, вместо стандартных арифметических операций сложения и вычитания часто применяют такие операции как инкремент или декремент – для них в языке С существует удобная и интуитивно понятная форма записи..
Инкремент – операция увеличения значения переменной на 1.
Пример использования: z++; // значение переменной z будет увеличено на 1
Декремент — операция уменьшения значения переменной на 1.
Пример использования: z- -; // значение переменной z будет уменьшено на 1
Есть две формы записи этих операций: постфиксная (x++) и префиксная (++x). Если эти операции выполняются совместно с операцией присваивания «=», то первой выполняется префиксная запись.
Примеры: m = n++;
Допустим, что значение переменной n было равно 7. Тогда в m будет записано значение 7, после чего значение переменной n будет увеличено на 1. Таким образом, в m будет 7, а в n — 8.
m =- -n;
Если значение n было равно 3, то сначала будет выполнено уменьшение n до 2, а затем это значение будет присвоено переменной m. Таким образом, n и m будет присвоено значение 2.
Ардуино своими руками
Atmega2560 – хоть и мощный и продвинутый контроллер, но проще и быстрее собрать первую плату на atmega8 или 168.
Левая часть схемы – это модуль связи по USB, иначе говоря, USB-UART/TTL конвертер. Его, вместе с обвязкой, можно выбросить из схемы, для экономии места, собрать на отдельной плате и подключать только для прошивки. Он нужен для преобразования уровней сигнала.
DA1 – это стабилизатор напряжения L7805. В качестве основы можно использовать целый ряд avr микросхем, которые вы найдете, например, серии, arduino atmega32 или собрать arduino atmega16. Для этого нужно использовать разные загрузчики, но для каждого из МК нужно найти свой.
Можно поступить еще проще, и собрать всё на беспаечной макетной плате, как это показано здесь, на примере 328-й атмеги.
Микроконтроллеры – это просто и весело – вы можете сделать кучу приятный и интересных вещей или даже стать выдающимся изобретателем, не имея при этом ни образования, ни знаний о низкоуровневых языках. Ардуино – шаг в электронику с нуля, который позволяет перейти к серьезным проектам и изучению сложных языков, типа C avr и других.
Установка 0 в произвольном бите регистра порта
Если мы хотим сконфигурировать отдельно вывод (контакт) PD2 как вход, то необходимо в соответствующий бит регистра DDRD записать 0. Для этого можно использовать следующую команду.
DDRD &= ~(1<<2);
В этом случае результат сдвига единицы на две позиции влево инвертируется с помощью операции побитного инвертирования, которая в языке С обозначаемой знаком «~».
В результате операции инверсии мы получаем вместо нулей единицы, а вместо единиц — нули. Данная логическая операция также называется операцией НЕ (английское название NOT).
Итак, в результате операции (1<<2) мы получили число 00000100, следовательно после проведения инверсии («~») мы получим число 11111011.
Получившееся число при помощи операции побитного логического умножения & умножается на число, хранящееся в регистре DDRD, и результат затем записывается в регистр DDRD.
Логическое умножение, по другому называемое операцией И (английское название AND), выполняется по следующим правилам:0*0=00*1=01*0=01*1=1
То есть если хотя бы один из операндов операции равен 0, то и результат операции равен 0.
Таким образом, сдвинутая нами влево на две позиции единица (1<<2) превращается при инвертировании («~») в ноль и умножается на соответствующий бит регистра DDRD, поэтому каким бы не было состояние данного бита, то при операции умножения на 0 данный бит становится равным 0. Схематично данный процесс представлен на следующем рисунке.
Конденсаторы по питанию
Перед тем, как подать на микроконтроллер питающее напряжение, выполним правило, которое обязательно для всех цифровых микросхем: в непосредственной близости от выводов питания микросхемы должен быть керамический конденсатор емкостью 0,06 — 0,22 мкф. Обычно устанавливают конденсатор 0,1 мкф. Его часто называют блокировочным конденсатором.
В схему необходимо установить и электролитический конденсатор емкостью 4-10 мкф. Он также является блокировочным фильтром, но на менее высоких частотах. Такой конденсатор можно устанавливать один для нескольких микросхем. Обычно на 2-3 корпуса микросхем.
Дело в том, что микроконтроллер (как и другие цифровые микросхемы) состоит из транзисторных ячеек, которые в процессе работы постоянно переключаются из открытого состояния в закрытое, и наоборот. При этом изменяется потребляемая транзисторными ячейками энергия. В линии питания возникают кратковременные «провалы» напряжения. Этих ячеек в микроконтроллере сотни тысяч (думаю, что сейчас уже миллионы!), поэтому по питающим проводам начинают гулять импульсные помехи с частотами от единиц до десятков тысяч Герц.
Для предотвращения распространения этих помех по цепям схемы, да и самой микросхемы микроконтроллера, параллельно его выводам питания устанавливают такой блокировочный конденсатор. При этом на каждую микросхему необходимо устанавливать индивидуальный конденсатор.
Конденсатор для постоянного тока является изолятором. Но при установке конденсатора в цепи с непостоянным током он делается сопротивлением. Чем выше частота, тем меньшее сопротивление оказывает конденсатор. Следовательно, блокировочный конденсатор с малой емкостью пропускает через себя (шунтирует) высокочастотные сигналы (десятки и сотни Герц), а конденсатор с бОльшей емкостью — низкочастотные. Об этом я писал еще в статье Конденсатор в цепи постоянного и переменного тока
Регистры процессора
Имеется 32 8-битных регистра общего назначения, R0 – R31. Все арифметические и логические операции работают с этими регистрами; только инструкции загрузки и хранения обращаются к ОЗУ.
Ограниченное количество инструкций работает с 16-битными парами регистров. Регистр с младшим номером пары содержит младшие значащие биты и должен быть четным. Последние три пары регистров используются как регистры-указатели для адресации памяти. Они известны как X (R27: R26), Y (R29: R28) и Z (R31: R30). Режимы адресации постинкремент и прединкремент поддерживаются всеми тремя. Y и Z также поддерживают шестибитное положительное смещение.
Команды, которые разрешают немедленное значение, ограничены регистрами R16 – R31 (8-битные операции) или парами регистров R25: R24 – R31: R30 (16-битные операции ADIW и SBIW). Некоторые варианты операции MUL ограничены восемью регистрами, с R16 по R23.
Регистры специального назначения
Помимо этих 32 регистров общего назначения, ЦП имеет несколько регистров специального назначения:
- ПК: 16- или 22-битный счетчик программ
- SP: 8- или 16-битный указатель стека
- SREG: 8-битный регистр состояния
- RAMPX, RAMPY, RAMPZ, RAMPD и EIND: 8-битные сегментные регистры, которые добавляются к 16-битным адресам для формирования 24-битных адресов; доступен только в частях с большими адресными пространствами.
Биты состояния
Биты регистра состояния:
- C Флаг переноски . Это флаг заимствования при вычитании. В и инструкциях не не изменить флаг переноса, так что они могут быть использованы для перебора многобайтных арифметических операций.
- Z нулевой флаг . Устанавливается в 1, когда арифметический результат равен нулю.
- N Отрицательный флаг . Устанавливается в копию самого старшего бита арифметического результата.
- V Флаг переполнения . Устанавливается при переполнении дополнения до двух.
- S Знак флага. Уникальный для AVR, это всегда N⊕V, и это верный признак сравнения.
- H Флаг полупереноса . Это внутренний перенос из дополнений и используется для поддержки арифметики BCD .
- T Битовая копия. Этот бит используется в специальных инструкциях по загрузке и хранению битов.
- I Флаг прерывания . Устанавливается, когда разрешены прерывания.