Ура, свершилось! Открываем раздел по микроконтроллерам! Я знаю, что все вы, дорогие товарищи, давно держите в своих пытливых умах целый список вопросов про контроллеры. Но не знаете, кому их задать… Теперь жить вам должно стать чуть-чуть проще, ибо мы ответим на все (почти все) ваши вопросы.
Например, в более «навороченных» контроллерах может быть АЦП (аналого-цифровой преобразователь), ЦАП (наоборот) и прочие прибамбасы. Чего только ни придумают! Но мы с вами, для начала, рассмотрим более простой вариант. Для примера возьмем контроллер фирмы Atmel, AT90s2313.
Чтобы понять, как работает контроллер, надо знать, что у него внутри. Ниже я нарисовал как мог сильно упрощенную схему контроллера – без таймеров, генераторов и прочей фигни – все только самое необходимое.
Самый главный элемент любого процессора – арифметико-логический узел (АЛУ). Кстати, по буржуйски он называется так-же – ALU (ariphmetic-logical unit). В нем-то, как вы уже догадались, и происходят все мыслимые и немыслимые арифметические и логические операции над числами. Кстати, насчет чисел: контроллер, как и любая другая цифровая система, работает с двоичными числами. О том, что это такое, я подробно распинался в статье «Что такое digital».
Тот контроллер, который мы сейчас мучаем, работает с 8-разрядными двоичными числами, иными словами – он 8-битный. То есть, АЛУ может захавать два 8-битных слова, произвести над ними какую то арифметическую или логическую операцию, и выплюнуть ответ – опять же, 8-битное слово.
Вопрос: а какие бывают арифметические и логические операции? И че эт ваще такое?
Ну, арифметическим операциям нас всех учили еще в 1 классе – это сложение, вычитание, сравнение 🙂 Логические операции – это операции алгебры логики. Вот некоторые из них: «И», «ИЛИ», «НЕ», «исключающее ИЛИ», сдвиг влево, сдвиг вправо. Также существуют операции, которые ни относятся ни к тем ни к другим: сброс в «0», установка в «1» и т.п.
Чтобы произвести операцию, АЛУ должен взять откуда-то два числа. Выполнив операцию, опять же, должен положить куда-то ответ. Для этих целей служат регистры общего назначения – РОН. Их у нас 16. Каждый регистр – это ячейка памяти емкостью 8 бит. Иначе говоря – 1 Байт. Именно в них и хранятся числа, с которыми работает АЛУ.
Для каждой команды, которую выполняет АЛУ, необходимо назвать те регистры, с которыми он будет в данный момент работать.
Как вы могли заметить, ответ всегда помещается в тот регистр, который назван первым. Естественно, старое содержимое регистра при этом затирается. Если нам нужно сохранить изначальное содержимое регистра, то перед операцией надо скопировать его в какой-нибудь другой регистр. Иначе – никак.
Оператор копирования – mov
Пример: mov R16,R17 add R17,R24
Кроме РОН, на схеме присутствует ОЗУ – оперативная память контроллера. Для чего она? Да для того, чтобы хранить данные при выполнении программы. Регистров ведь – всего 16. Для полноценной работы этого явно недостаточно. В регистрах хранится обычно только те данные, которые будут использоваться непосредственно прямо сейчас. Все остальное удобнее положить в память. Позднее мы поговорим о таких вещах как стек, указатели и пр. Пока что запомним, что память есть.
Ну вот. Откуда АЛУ берет данные для вычислений – разобрались. Теперь разберемся, откуда он берет команды.
А все очень просто! Для этого существует регистр команд. В этот регистр постепенно выводятся команды той программы, которую выполняет контроллер. Эти команды касаются всего контроллера, а не только АЛУ (как мы помним, в контроллере просто тьма всего). Чтобы «выудить» из общей кучи «свою» команду, АЛУ постоянно заглядывает в регистр команд на предмет, нет ли там что-нить для него. И как только видит – стрелой мчится выполнять.
В регистр команд, команды попадают из программной памяти. Программная память – это ПЗУ (постоянное запоминающее устройство), в котором по каждому адресу записана одна команда. Вместе, все эти команды и составляют программу, и записаны в той последовательности, в какой они стоят в программе.
Чтобы «достать» команду из ПЗУ, надо сказать ему адрес ячейки памяти, в которой лежит эта команда. В ответ, ПЗУ выдаст содержимое этой ячейки. Команды записываются в ячейки в той последовательности, в которой стоят в программе. Поэтому, для того чтобы последовательно «перебирать» команды, достаточно просто каждый раз прибавлять к адресу «1».
Именно этим и занимается счетчик команд.
Однако же, программа, выполняющаяся внутри микросхемы и ни коим образом не связанная с внешним миром была бы просто никому не нужна. Для полноценной работы, контроллеру необходимо обмениваться данными с внешним миром.
Для этого существуют порты ввода/вывода (ПВВ).
Порт – это пачка однобитных каналов, каждый из которых может быть независимо настроен либо на ввод, либо на вывод.
В контроллере AT90s2313 два ПВВ – PortB и PortD. PortB состоит из 8-ми каналов, Portd – из 7-ми.
Эта «культяпость» порта D объясняется простой нехваткой ножек микросхемы. Просто разработчики очень хотели запихнуть все это дело в 20-ножный корпус, и поэтому урезали PortD на один канал.
Любая программа для контроллера начинается именно с настройки портов. Мы должны определить, какие каналы будут работать на ввод, какие – на вывод. По умолчанию, все каналы включены на ввод. Но это мы обсудим чуть позже.
Ну в общем-то, вот первоначальные сведения о микроконтроллерах. Дальше мы поговорим о том как написать, скомпилировать и зашить в контроллер его программу.
Регистры общего назначения, регистры ввода/вывода, стек, счетчик команд
Регистры общего назначения, регистры ввода/вывода, стек, счетчик команд
Процессорное ядро микроконтроллеров: — регистры общего назначения – регистры ввода/вывода – стек – счетчик команд
Доброго дня уважаемые радиолюбители! Приветствую вас на сайте “ Радиолюбитель “
Сегодня мы с вами продолжим более подробное изучение процессорного ядра микроконтроллера. В прошлый раз мы рассмотрели арифметико-логическое устройство и организацию памяти микроконтроллера. В этой статье я постараюсь кратко, но и в тоже время как можно подробнее рассмотреть оставшиеся вопросы: — регистры общего назначения – регистры ввода/вывода – стек – счетчик команд и очень подробно, но чуть позже, рассмотрим порты ввода/вывода.
Регистры общего назначения
Надо помнить, что физически РОН (как и рассматриваемые ниже регистры ввода/вывода) не входят в память данных, но для повышения эффективности работы МК и его производительности, РОН (и регистры ввода/вывода) располагаются в адресном пространстве памяти данных, и к ним можно обращаться как по их именам, так и как к ячейкам памяти ОЗУ (SRAM).
Так как все РОН восьмиразрядные (однобайтовые) то и оперировать они могут только с однобайтными данными. В случае, если необходимо проводить операции с двухбайтными данными (шестнадцатиразрядными), то РОН с R26 по R31 могут образовывать сдвоенные регистры, которые в свою очередь могут выступать в роли самостоятельных шестандцатиразрядных регистров и тогда они имеют другие имена (такие сдвоенные регистры иногда называют “словом”, по аналогии с ячейками памяти программ): R26 и R27 – сдвоенный регистр “Х”, R28 и R29 – сдвоенный регистр “Y”, R30 и R31 – сдвоенный регистр “Z”. При этом первый регистр в такой паре (R26, или R28, или R30) играет роль младшего байта и обозначается дополнительной буквой “L”. Например: XL, YL, ZL. Второй регистр в паре играет роль старшего байта и обозначается дополнительной буквой “H”. Например: XH, YH, ZH. Это позволяет нам записать (или считать значение), к примеру, двухбайтовое число непосредственно в двойной регистр, обратившись к его буквенному обозначению X, Y или Z, а также считать любой байт (или записать в любой байт) выбрав буквенное обозначение младшего или старшего байта двойного регистра (к примеру XL или XH).
Кроме того необходимо помнить, что хотя и все регистры общего назначения одинаковы, но не все из них могут участвовать в некоторых операциях. При этом, регистрам с R16 по R31 доступны все операции, а регистрам с R0 по R15 – не все. И еще. В некоторых МК (Tiny) двойной регистр только один – Z (R30,R31).
В описание каждой команды указывается, какие РОН могут участвовать в данной операции, к примеру:
Регистры ввода/вывода
Как мы уже знаем, в МК присутствует много различных периферийных устройств. Кроме них, в МК есть и внутренние устройства. Всеми этими устройствами необходимо управлять, задавать необходимые режимы работы. Для этих целей в МК существуют регистры ввода/вывода.
Еще раз напомню, что хотя РОН и РВВ выделяется место в памяти данных, но доступное пространство памяти данных для нас от этого не уменьшается, ведь физически они там не располагаются. Если написано, что МК имеет память данных 128 байт, то значит мы можем использовать эти 128 байт в своих целях. Просто первый адрес памяти данных будет начинаться не с нуля, как в памяти программ или энергонезависимой памяти.
Кусочек из таблицы регистров ввода/вывода (первое число – номер регистра, и оно же его адрес в адресном пространстве РВВ, в скобках указывается адрес регистра в адресном пространстве памяти данных, т.е. номер +32):
Назначение и работу РВВ мы будем изучать постепенно, по мере изучения работы устройств МК. Следующую статью мы посвятим изучению одного, но самого главного, чаще всего используемого в программах – регистру состояния, под названием SREG.
Стек
Стек, или указатель стека – это специальный регистр, который предназначен для организации стековой памяти. Можно сказать, что стек (точнее стековая память, стек состоит из двух частей: указатель стека и стековая память) – это туннель с тупиком в конце, состоящий из ячеек памяти. По мере заполнения ячеек памяти первые данные уходят в глубь стека, и добраться до них можно, только вытащив сначала последние введенные данные. Допустим, если мы запишем в стек последовательно три числа: 10,20 и 30, то для того, чтобы затем извлечь из стековой памяти число 10, предварительно придется извлечь числа 30 и 20. Т.е., значение записанное последним всегда будет прочитано первым. Стек широко используются не только МК в своих целях, но и программистами. К примеру, при выполнении команды перехода к подпрограмме, МК самостоятельно записывает в стек адрес следующей команды, с которой будет продолжено выполнение основной программы после возвращения из подпрограммы. Возвращаясь из подпрограммы, МК извлекает этот адрес и загружает в счетчик команд, в результате чего выполнение программы продолжается с прерванного места. Для нас тоже очень удобно сохранять в стеке данные при входе в подпрограмму, а затем извлекать их по возвращению из подпрограммы. Также удобно в стеке сохранять промежуточные результаты вычислений, а затем, по мере необходимости, извлекать их.
В микроконтроллерах, в которых отсутствует память данных (ОЗУ), а это часть МК семейства Tiny, стек организуется аппаратно. В таких МК стек располагается в собственной памяти а глубина его равна трем уровням. Аппаратный стек не доступен программисту, его в своих целях использует только сам МК, сохраняя в нем адреса команд при переходе к подпрограммам. Так как возможности аппаратного стека мы использовать в своих целях не можем, то на этом и закончим его изучение. Во всех остальных МК, которые имеют память данных, стек организуется программно. Стек в этом случае не имеет собственной памяти а использует память данных (ОЗУ). Такой стек доступен для наших целей и рассмотрим его подробней. Для организации процесса записи данных в стек и их чтения предназначен указатель стека. В качестве указателя стека используются один или два регистра ввода/вывода: – если память данных небольшая (до 256 байт), используется один восьмиразрядный РВВ – SPL; – если память данных большая (более 256 байт), к первому РВВ – SPL, добавляется второй – SPH, и вместе они составляют один шестнадцатиразрядный указатель стека. В указателе стека содержится адрес ячейки памяти, в которую будут записаны или считаны данные.
Для программиста в системе команд МК есть две специальные команды: — PUSH – команда записи в стек – POP – команда чтения из стека Давайте рассмотри как происходит запись в стек и чтение из него. Запись данных в стек: Для того, чтобы записать данные в стек, их предварительно необходимо загрузить в любой РОН. По команде PUSH МК записывает данные из указанного нами РОН в память данных по адресу, на который указывает указатель стека, а затем уменьшает содержимое стека на 1 (если указатель восьмиразрядный) или на 2 (если указатель шестнадцатиразрядный). Новая команда PUSH запишет данные в следующую ячейку ОЗУ и опять уменьшит содержимое указателя стека. И так, далее.
Чтение данных из стека: По команде POP, МК сначала увеличивает содержимое указателя стека на 1 или 2, а затем считывает данные с ячейки ОЗУ, на которую указывает указатель стека. И так, далее.
После сброса МК содержимое указателя стека равно нулю. Т.е. получается, что после сброса, указатель стека указывает на нулевую ячейку памяти ОЗУ. А теперь смотрите, что получится, если мы попробуем что-то записать в стек: – первая команда PUSH – содержимое РОН будет записано в нулевую ячейку памяти,а затем МК попытается уменьшить содержимое стека на 1 или 2 – и ни чего не получится – адрес ячейки памяти не может быть меньше нуля! Поэтому, прежде чем пользоваться стековой памятью, необходимо записать в указатель стека значение его вершины – адрес конечной ячейки памяти с которой начнется запись в стек. Обычно вершиной стека указывают адрес последней ячейки памяти данных. Если у вашего МК ОЗУ составляет 128 байт, то указывают адрес 128 ячейки памяти. Под стек можно использовать всю память, если только вы не будете хранить в ней свои переменные. Если в качестве вершины стека вы укажите конечную ячейку памяти, то следить за стековой памятью в большинстве случаев не обязательно. А если вершиной стека указать ячейку памяти поближе к началу, и при этом использовать ОЗУ для хранения своих данных, то следить за размерностью стека придется – он может залезть на ячейки в которых вы будете хранить свои данные.
Счетчик команд
Счетчик команд – представляет собой регистр, в котором содержится адрес следующей исполняемой команды.
Размер счетчика команд может быть от нескольких разрядов до шестнадцати (двухбайтовый). Размерность счетчика зависит об объема памяти программ. Счетчик команд не доступен для программиста, в него мы не можем ничего записать и не можем из него ничего считать. Работой счетчика команд руководит единолично МК.
При включении питания устройства или сброса микроконтроллера, счетчик команд устанавливается в ноль, т.е. указывает на нулевой адрес памяти программ. Поэтому адресу начинается наша программа (если мы не используем прерывания) или адрес, по которому начинается наша программа (если мы используем прерывания). При нормальном ходе программы содержимое счетчика команд автоматически увеличивается на 1 или 2 (в зависимости от выполняемой команды) в каждом машинном цикле. Т.е., после выполнения команды, счетчик будет указывать адрес следующей команды. Этот порядок будет нарушен, если на пути МК при выполнении программы встретится команда перехода, команда вызова подпрограммы (или возврата из подпрограммы), а также при возникновении прерывания. В этом случае содержимое счетчика – адрес следующей команды, записывается в стек, а в счетчик записывается адрес по которому надо перейти по команде перехода или по прерыванию. По команде возвращения из подпрограммы, в счетчик записывается адрес команды сохраненный в стеке, программа продолжается дальше. Все это проделывается автоматически, без нашего участия.
В продолжении этой статьи, а точнее – окончании, мы очень подробно рассмотрим порты ввода/вывода микроконтроллера. После этого, вся теория пойдет вперемешку с практикой. Легче понимать работу микроконтроллера разрабатывая практическое устройство. Расскажу немного о том, что будет дальше. После портов ввода/вывода мы начнем собирать цифровой вольтметр, попутно изучая и необходимые для этого теоритические вопросы. Вольтметр я буду собирать на основе МК ATiny26 – самый подходящий (как мне кажется) для этого микроконтроллер. Для вывода информации, в целях изучения вопроса подключения к МК различных типов устройств для отображения информации, мы будем использовать сначала светодиодные сегментные индикаторы, которые потом заменим сегментным LCD дисплеем, который потом заменим буквенно-цифровым ЖК индикатором, а его, в свою очередь, если не погибнем в процессе, графическим ЖК индикатором. Затем добавим к вольтметру амперметр, а сверху этого бутерброда положим частотомер. Что будет дальше, честно говоря, я пока не знаю.
Начинаем изучать STM32: Что такое регистры? Как с ними работать?
Продолжаем рассмотрение базовых вопросов
В предыдущем уроке мы рассмотрели работу с битовыми операциями и двоичными числами, тем самым заложив основу для рассмотрения новой темы. В этом уроке мы с Вами рассмотрим очередной вопрос: что такое регистры и как с ними работать?
Память и регистры
Одним из самых важных навыков необходимых при работе с микроконтроллерами является умение взаимодействовать с регистрами. Давайте для себя разберемся, что же это такое?
В целом, регистр — это особый вид памяти внутри микроконтроллера, который используется для управления процессором и периферийными устройствами. Каждый регистр в архитектуре ARM представляет собой ячейку памяти и имеет длину в 32 бита, где каждый бит можно представить в виде крошечного выключателя с помощью которого осуществляется управление тем или иным параметром микроконтроллера.
Каждый из регистров имеет свой порядковый номер – адрес. Адрес регистра обозначается 32-битным числом представленным в шестнадцатеричной системе счисления. Путём записи по адресу регистра определённой комбинации единиц и нулей, которые обычно представлены в шестнадцатеричном виде, осуществляется настройка и управление тем или иным узлом в МК. Вспомним, что в программе для работы с битовыми операциями, мы могли представить в виде шестнадцатеричного числа произвольный набор единиц и нулей. В целом стоит отметить, что существует два вида регистров: регистры общего назначения и специальные регистры. Первые расположены внутри ядра МК, а вторые являются частью RAM-памяти.
Так же стоит отметить, что Reference Manual, который мы скачивали в первом уроке, это один большой справочник по регистрам, содержащимся в целевом микроконтроллере, а библиотека CMSIS позволяет нам оперировать символьными именами регистров вместо числовых адресов. Например, к регистру 0x40011018 мы можем обратиться просто, используя символьное имя GPIOC_BSSR. Конкретные примеры конфигурирования мы рассмотрим в ходе разбора нашей программы из первого занятия.
Итак, обычно структура регистра описывается в виде небольшой таблицы с указанием:
Разбор кода из первого занятия
Итак, давайте вспомним задачу, которую мы решили на первом уроке используя готовый код примера: нам было необходимо написать программу, которая бы обеспечила попеременное включение двух светодиодов на плате Discovery (возможно и не двух, если у вас другая версия платы Discovery) с временным интервалом.
Давайте еще разок взглянем на код программы, которую мы использовали для того, чтобы заставить наш МК дрыгать двумя ногами на которых расположены наши светодиоды:
Первым делом, при работе с STM32, даже для такой простой задачи как включение и выключение светодиода нам необходимо предварительно ответить на ряд вопросов:
Куда подключены наши светодиоды? К какому выводу микроконтроллера?
Для того, чтобы посмотреть где что находится на плате Discovery, а в частности, нужные нам светодиоды — нужно открыть Schematic-файл, либо тот который мы скачали с сайта ST, либо прямо из Keil:
Открыв Schematic мы увидим схему всего того, что есть на плате — схему ST-Link, обвязку всей периферии и многое другое. На текущий момент нас интересуют два светодиода, ищем их обозначение:
Как мы видим, наши светодиоды подключены к порту GPIOC на 8 и 9 пин.
Как включить тактирование на нужный порт GPIO?
В целом, любая работа с периферией в микроконтроллерах STM32 сводится к стандартной последовательности действий:
Внимание! Вопрос касательно системы тактирования, её настройки и использования мы подробно рассмотрим в отдельной статье.
Найти к какой шине подключен наш порт GPIOC можно найти в Datasheet’е на наш МК в разделе Memory Mapping в Таблице 16. STM32F051xx peripheral register boundary addresses.
Как вы уже успели заметить, необходимая нам шина именуется как AHB2. Для того чтобы подробнее ознакомиться с регистром, в котором включается тактирование на нужный нам порт GPIO на шине AHB, надо перейти в соответствующий раздел в Reference Manual. По названию регистров мы можем определить тот, который нужен нам:
Переходим в этот пункт, и мы видим наш 32-битный регистр, его адрес смещения, значение по умолчанию, способ доступа к регистру и перечисление того, за что отвечает каждый бит в регистре.
Смотрим на таблицу и видим нечто напоминающее опции включения тактирования на портах GPIO. Переходим к описанию и находим нужную нам опцию:
Соответственно если мы установим 19 бит в значение «1» то это обеспечит включение тактирования на порт I/O C – то есть на наш GPIOC. К тому же — нам нужно включить отдельно один бит из группы, не затрагивая остальные т.к. мы не должны мешать и изменять без надобности другие настройки.
Основываясь на материалах прошлого урока, мы знаем что для того чтобы выставить определенный бит нужно используя логическую операцию «ИЛИ» сложить текущее значение регистра с маской которая содержит те биты которые необходимо включить. Например, сложим значение регистра RCC->AHBENR по умолчанию, т.е. 0x14 и число 0x80000 тем самым включим тактирование GPIOC путем установки 19 бита:
Каким образом мы можем это сделать из программы? Всё достаточно просто. В данном случае у нас два варианта:
То есть, мы могли бы обращаться к адресам регистров напрямую по адресу и написать так:
Второй вариант мне кажется наиболее привлекательным, т.к. библиотека CMSIS организована таким способом, что регистру можно обращаться, используя только его название. Препроцессор в ходе обработки текста программы перед компиляцией подставит все цифровые значения адреса регистра автоматически. Давайте разберем этот вопрос чуть подробнее.
Предлагаю открыть наш проект, который мы сделали в первом занятии, или скачайте предварительно подготовленый отсюда и удалите все содержимое программы оставив только подключенный заголовочный файл, функцию main() и инструкцию для включения тактирования (она нам понадобится для подробного разбора кода).
Наш код будет выглядеть следующим образом:
Давайте для ознакомления копнём вглубь библиотеки CMSIS.
Для того, чтобы быстро перейти к месту где объявлена та или иная константа или переменная в Keil реализована удобная функция. Кликаем правой кнопкой по необходимой нам константе, например, на RCC:
И мы переносимся в глубины библиотеки CMSIS, в которой увидим, что все регистры доступные для управления программным способом имеют вид TypeDef-структур, в том числе и наш RCC:
Провалившись подобным образом в RCC_TypeDef мы увидим структуру в которой описаны все поля нашего регистра:
Соответственно, мы можем спокойно обращаться к нужному нам регистру записью вида PERIPH_MODULE->REGISTER и присваивать ему определенное значение.
Помимо мнемонического обозначения регистров есть так же обозначения конкретных битов. Если мы провалимся к объявлению параметра RCC_AHBENR_GPIOCEN из нашей программы, то так же увидим объявление всех параметров:
Таким образом, используя библиотеку CMSIS у нас получается лаконичная читаемая запись нужного нам параметра в регистр, через установку которого мы запускаем тактирование на нужный нам порт:
В качестве задания:определите используя возможности Keil, каким образом получился адрес регистра RCC->AHBENR как 0x40021014.
Как настроить нужные нам пины GPIO для того чтобы можно было включить светодиод?
Итак, мы знаем что нужные нам светодиоды подключены к порту GPIOC к пинам PC8 и PC9. Нам нужно настроить их в такой режим, чтобы загорался светодиод. Хотелось бы сразу же сделать оговорку, что порты GPIO мы рассмотрим подробнее в другой статье и тут мы сконцентрируемся именно на работе с регистрами.
Первым делом нам нужно перевести режим работы пинов PC8 и PC9 в режим Output. Остальные параметры порта можно оставить по умолчанию. Переходим в Reference Manual в раздел 9. General-purpose I/Os (GPIO) и открываем пункт отвечающий за режим работы пинов порта GPIO и видим что за этот параметр отвечает регистр MODER:
Судя по описанию, для установки пинов PC8 и PC9 в режим Output мы должны записать 01 в соответствующие поля регистра GPIOC.
Это можно сделать через прямую установку с помощью числовых значений:
Или через использование определений из библиотеки:
После данной инструкции наши пины PC8 и PC9 перейдут в режим Output.
Как включить светодиод?
Если мы обратим внимание на список доступных регистров для управления портом GPIO то можем увидеть регистр ODR:
Каждый из соответствующих битов отвечает за один из пинов порта. Его структуру вы можете увидеть ниже:
Для того, чтобы обеспечить попеременную смену состояний светодиодов надо с определенным временным интервалом включать/выключать 8 и 9 биты. То есть попеременно присваивать регистру значение 0x100 и 0x200.
Сделать это мы можем через прямое присвоение значений регистру:
Можем через использование определений из библиотеки:
Но так как микроконтроллер работает очень быстро — мы не будем замечать смены состояний светодиодов и визуально будет казаться что они оба горят постоянно. Для того чтобы они действительно моргали попеременно мы внесем искусственную задержку в виде цикла который займет МК бесполезными вычислениями на некоторое время. Получится следующий код:
На этом первоначальное знакомство с регистрами и методами работы с ними мы можем закончить.
Проверка результатов работы нашего кода
Небольшое приятное дополнение в конце статьи: в Keil имеется отличный Debug-инструмент с помощью которого мы можем пошагово выполнить нашу программу и просмотреть текущее состояние любого периферийного блока. Для этого после загрузки прошивки после компиляции мы можем нажать кнопку Start Debug Session:
Рабочая среда Keil переключится в режим отладки. Мы можем управлять ходом программы с помощью данных кнопок:
И есть еще одна удобная функция работы с периферией в режиме отладки, она позволяет просматривать текущее состояние регистров и менять их состояние простым кликом мышкой.
Для того чтобы ей воспользоваться — нужно перейти в соответствующий периферийный блок и справа откроется окно с указанием регистров и их значением.
Если вы кликните по одному из пунктов данного меню, вы увидите адрес регистра и его краткое описание. Так же можно просмотреть описание к каждому отдельному параметру регистра:
Попробуйте самостоятельно пошагово выполнить программу, включить/выключить светодиоды не используя программу, а используя данный режим работы с микроконтроллером. Простор для фантазии тут обширный. Так же попробуйте поиграться с длительностями задержек, сделайте одновременное моргание обоими светодиодами. В общем экспериментируйте! )