====== Регистры ====== Зачастую для начинающих «регистр» является наиболее сложно усваиваемой частью у микроконтроллеров. К сожалению, без этого понятия никак не обойтись, если есть желание заниматься микроконтроллерами. Для того, чтобы читатель смог ознакомиться с понятием регистра, данный материал упрощён до уровня начинающего. ===== Суть ===== [{{ :images:logic:tape_buttons.jpg?240|Кнопки кассетного выключателя}}] Регистры можно сравнить с кнопочной панелью какого-либо домашнего устройства, где есть выключатели, которые можно включать и выключать. К примеру, кассетный магнитофон, который имел 6 кнопок: * Запись * Перемотка назад * Воспроизведение * Перемотка вперед * Стоп * Пауза Каждая кнопка имеет свое предназначение, но только при правильном использовании. К примеру, кнопка «Стоп» не делает ничего раньше того, как запустить играть кассету, только после этого она сможет что-либо сделать – останавливает игру кассеты. Кнопки перемотки «Вперед» или «Назад» можно в любой момент нажимать, т.к. лента начнет крутиться как во время игры кассеты, так и во время стопа. Запись магнитофона начнется только в том случае, если нажать кнопку «Запись» вместе с кнопкой «Воспроизведение». У регистров микроконтроллера, также как и у кнопок кассетного магнитофона, есть свои определенные функции. Конечно, в регистрах таких кнопок, как у магнитофона нет, но существует множество транзисторов, которые включают и выключают электричество. У простейших микропроцессоров регистр состоит из электрических выключателей основанных на 8 транзисторах. Регистр можно рассмотреть на примере 8-битной системе счисления, где каждый бит указывает на состояние кнопки. К примеру, значение бита 1 указывает на включенную кнопку, а 0 – на выключенную. [{{ :images:logic:register_buttons_and_bits.png?240|Положения «электрических выключателей» регистра и их битовые значения}}] Поскольку состояние выключателей регистра можно сравнить с числами и наоборот, то регистр можно сравнить с памятью, которая способна хранить данные размером в одно число. Благодаря этим сравнениям, мы пришли к тому, что на самом деле регистры это и есть ячейки памяти. Задача ячейки памяти заключается обычно в том, чтобы хранить информацию, а в регистре эта информация чем-либо управляет. Если, в изображённый на рисунке регистр, вписать битовое значение 01100001, то 3 воображаемых выключателя нажимаются и что-то начинает происходить. Если у кассетного магнитофона каждую кнопку можно включать по отдельности, то в регистрах изменение одного «выключателя» или бита происходит сложнее. В любом случае придётся изменить содержание всего регистра разом. Прежде чем говорить об изменении битов, необходимо знать, что регистров у микроконтроллеров много. Для управления некоторых частей микроконтроллера могут использоваться десятки регистров. Обилие регистров означает то, что их необходимо как-то различать. Для этого используются имена, например, название одного из регистров - PORTB. На самом деле имена регистров придуманы для упрощения работы разработчиков и всего лишь замещают цифровые адреса. ===== Использование ===== В программе языка Си для записывания или чтения значения регистра необходимо к нему обратиться как к переменной. Следующий пример демонстрирует запись бинарного значения в выдуманный регистр REG и чтение этого значения в переменную reg. Перед бинарным значением пишется 0b (впереди ноль), чтобы компилятор понял систему счисления. REG = 0b01100001; unsigned char reg = REG; В записи и чтении значений регистров ничего сложного нет, сложность появляется тогда, когда необходимо изменить значение отдельных битов в регистре. Для изменения битов сначала необходимо изучить бинарные операции и различные системы счисления. Никто не запрещает использовать бинарные числа, но работа с ними неудобна в связи с их длиной и обычно используют шестнадцатеричные числа, которые короче. [{{ :images:logic:logic_hexadecimal.png?209|Шестнадцатеричные числа}}] В отличие от бинарной системы, где числа 0 и 1 или от десятичной системы, где числа от 0 до 9, в шестнадцатеричной системе – от 0 до F. Одно шестнадцатеричное число образуется из четырех битов. На приведённой таблице показаны шестнадцатеричные числа и соответствующие им бинарные числа. Очерёдность их считывания слева направо и их нумерация начинается с нуля. Впереди приведено бинарное значение регистра 01100001, которое в шестнадцатеричной системе счисления соответствует 61, а на языке Си пишется как 0x61 (впереди ноль). Для изменения отдельных битов в числе (регистре, переменной или где-либо ещё), нужно использовать бинарные операции. Бинарная операция - это операция между бинарными числами, где между битами этих чисел происходят отдельные логические операции. В большинстве микроконтроллеров используется четыре бинарных операции, у каждой из которых несколько названий. Далее приведена соответствующая всем четырём бинарным операциям логическая операция с отдельным битом или битами. [{{ :images:logic:logic_all_4.png?550 |Отрицание, логическое умножение, логическое сложение и неравнозначность}}] * **Отрицание / Инверсия** \\ Отрицание изменяет значение бита на противоположное, т.е. 0 меняется на 1 и 1 меняется на 0. На языке Си знак отрицания “~“. * **Логическое умножение / Конъюнкция** \\ При умножении двух битов ответ будет 1, только в случае, если оба бита были 1, во всех других случаях ответ будет 0. В языке Си знак умножения “&“. * **Логическое сложение / Дизъюнкция** \\ При сложении двух битов ответ будет 1, если хотя бы значение одного бита 1, во всех других случаях ответ будет 0. В языке Си знак сложения „|“. * **Неравнозначность / Исключающая дизъюнкция** \\ При операции неравнозначности между двумя битами ответ будет 1, если значения битов отличаются друг от друга, во всех других случаях ответ будет 0. В языке Си знак неравнозначности "^". Всё вышеприведённое необходимо для изменения значений отдельных битов. Но одной лишь теории будет недостаточно, поэтому далее приведены некоторые типичные примеры с регистрами. ~~CL~~ ==== Настройка отдельного бита высоким ==== [{{ :images:logic:op_bit_set.png?230|Операция настройки бита высоким}}] Для того, чтобы один или несколько битов в регистре настроить высокими т.е. единицей, необходимо использовать операцию логического сложения. Один операнд операции сложения должен быть регистром, второй бинарным числом, где высокий только тот бит, который желают настроить высоким и в регистре. Это второе бинарное число называют битовой маской. Рядом изображенная операция на языке Си выглядит следующим образом: ~~CL~~ // Предположим, что REG = 0x0F REG = REG | 0x11; // Первый метод REG |= 0x11; // Второй метод // Здесь REG = 0x1F ==== Настройка отдельного бита низким ==== [{{ :images:logic:op_bit_clear.png?229|Операция настройки бита низким}}] Для того, чтобы один или несколько битов настроить низкими, т.е. нулём, необходимо использовать операцию логического умножения. Один операнд операции должен быть регистром, другой битовой маской, где низким настроен только тот бит, который желают настроить низким и в регистре. Изображённая рядом операция на языке Си выглядит следующим образом: ~~CL~~ // Предположим, что REG = 0x0F REG = REG & 0xFE; // Первый метод REG &= 0xFE; // Второй метод // Здесь REG = 0x0E ==== Операция инвертирования бита ==== [{{ :images:logic:op_bit_invert.png?229|Операция инвертирования бита}}] Для инвертирования одного или нескольких битов в регистре необходимо использовать операцию неравнозначности. Один из операндов операции должен быть регистром, второй битовой маской, где высоким настроен только тот бит, который желают инвертировать и в регистре. Операция, изображенная рядом, на языке Си выглядит следующим образом: ~~CL~~ // Предположим, что REG = 0x0F REG = REG ^ 0x11; // Первый метод REG ^= 0x11; // Второй метод (применить разом можно только один) // Здесь REG = 0x1E ==== Инвертирование всего регистра ==== [{{ :images:logic:op_reg_invert.png?229|Операция инвертирования битов}}] Для инвертирования битов всего регистра, необходимо использовать операцию отрицания. У данной операции только один операнд. Операция, изображенная рядом, на языке Си выглядит следующим образом: ~~CL~~ // Предположим, что REG = 0x0F REG = ~REG; // Здесь REG = 0xF0 ==== Чтение значения отдельного бита ==== [{{ :images:logic:op_bit_get.png?229|Операция чтения бита}}] Для чтения одного или нескольких значений бита из регистра, необходимо использовать ту же операцию, что и при обнулении – логическое умножение. Один из операндов операции должен быть регистром, другой битовой маской, где высоким настроен только тот бит, значение которого желают считать из регистра. Операция, изображённая рядом, выглядит на языке Си следующим образом: ~~CL~~ // Предположим, что REG = 0x0F unsigned char x = REG & 0x01; // Здесь x = 0x01 ==== Смещение бита ==== На самом деле, во многих языках программирования помимо бинарных операций, существуют и некоторые битовые операции, которые облегчают работу программистов. Такими являются операции смещения бита, которые в бинарном числе смещают биты налево или направо. Основная ценность операций по смещению состоит в том, что они способны превращать разряды битов в битовую маску и наоборот. [{{ :images:logic:op_bit_shift_left.png?241|Сдвижение бита влево}}] На указанном рядом изображении, приведён пример операции по сдвигу бита влево. Сдвижение не является логической операцией и у него отсутствует соответствующее обозначение, а на языке Си это “«“. Сдвиг влево используется для превращения битового разряда в битовую маску. Например, если желают маску 6 бита (NB! разряд 5), то требуется число 1 сдвинуть влево 5 раз. Операция, приведённая в примере, выглядеть на языке Си следующим образом: ~~CL~~ REG = 0x01 << 5; // Где REG = 0x20 [{{ :images:logic:op_bit_shift_right.png?241|Сдвижение бита вправо}}] Подобно сдвигу влево, работает и операция сдвижения налево. Обозначение данной операции на языке Си - “»“. Сдвиг вправо используется для нахождения логического значения бита из битовой маски. Впереди был приведён пример операции считывания значения одиночного бита. Предположим, что бит, которые нужно считать не с наименьшим разрядом, а например, с разрядом 5. В этом случае, ответ считывания был бы 0х20 или 0х00, но иногда требуется ответ 1 или 0 и тогда приходит на помощь сдвиг вправо. Операция, приведённая в примере, выглядеть на языке Си следующим образом: ~~CL~~ // Предположим, что значение REG 0x20 unsigned char x = REG >> 5; // Здесь значение x - 0x01 (т.е. просто 1) Если в операции сдвижения бита, бит сдвигается из низкого разряда вправо или из высокого влево, то он исчезает. В некоторых языках программирования существуют вращающиеся операции сдвижения битов, где бит не исчезает, а двигается из одного конца в другой. На языке Си вращающиеся операции сдвижения бита отсутствуют, но при желании их можно написать самим. Все переведённые примеры операций работают помимо регистров и с переменными и константами. Последние могут быть только операндами, а не ответами. ===== Регистры AVR ===== Для того, чтобы сделать что-либо реальное с регистрами микроконтроллера, требуется знать как пользоваться конкретным микроконтроллером. К каждому микроконтроллеру прилагается одна или несколько спецификаций, которая описывает структуру и функциональность микроконтроллера. В спецификации описаны и регистры. Далее изучим, как разобраться в описании регистров в спецификации AVR. [{{ :images:logic:avr_example_register.png?580 |Один регистр AVRиз его спецификации}}] На изображении приведён регистр UCSRnA микроконтроллера ATmega128, полное название которого “USART Control and Status Register A“. Это регистр, с помощью которого конфигурируется модуль USART AVR-а и с которого можно считать состояния этого модуля. Все названия регистров AVR пишутся заглавными буквами, но внимательный читатель заметит, что в этом регистре есть маленькая буква n. Маленькой буквой n обозначается индекс какого-нибудь модуля. Так как в ATmega128 есть 2 очень похожих USART модуля, то в регистре они не записываются вдвойне, а только один раз и вместо буквы n должен читатель вставлять „0“ или „1“. Из чего следует, что в ATmega128 есть регистры UCSR0A и UCSR1A. Содержимое регистра отмечено в ящике с 8 ячейками, выделенным толстой линией. Каждая ячейка обозначает один бит. Над ящиком приведена очерёдность битов, они возрастают справа налево. Так как AVR – это 8-битный микроконтроллер, большинство регистров так же 8-битные. Есть некоторые исключения в виде 16-битных регистров, которые состоят из двух 8-битных регистров. Вдобавок к регистрам название имеет каждый бит регистра, так же как и кнопки магнитофона. В спецификации имеется объяснение к каждому биту. Названия битов – это так же сокращения и букву n в них следует заменить индексом модуля. В некоторых регистрах использованы не все биты, и в этом случае ячейка бита обозначается знаком тире. Под битами регистра приведены две строки, где написано, является ли бит читаемым (R), записываемым (W) или и тем и другим (R/W). Например, бит состояния нельзя переписать, и даже в случае, если это пытаются сделать в программе, не принимает бит желаемого значения. В случае с битом, который можно только записывать, указано одно конкретное значение, которое появляется всегда при его чтении. Во второй стоке под битами указано стандартное значение, которое есть у бита после запуска микроконтроллера (англ. reset). Если названия регистров AVR указывают на действительный адрес в гнезде памяти, то за названиями битов скрываются их порядковые номера. Исходя из этого, при манипуляции с битами, следует названия битов при помощи операции сдвижения преобразовать в битовую маску. Далее приведены некоторые примерные предложения на языке Си, используемые для регистра модуля USART 0. // Установка бита TXC0 высоким UCSR0A |= (1 << TXC0); // Установка бита U2X0 низким UCSR0A &= ~(1 << U2X0); // Считывание значения бита (битовой маски) UDRE0 unsigned char u = (UCSR0A & (1 << UDRE0)); // Здесь значение u – 0 или 32, // что позволяет использовать его в логическом выражении if (u) { // Инвертирование бита MPCM0 UCSR0A ^= (1 << MPCM0); } // Иногда требуется получить конкретное значение 0 или 1, // для этого следует уже считанные биты сдвинуть вправо u >>= UDRE0; // Здесь значение u 0 или 1