Логотип

Микроконтроллеры AVR. Основы программирования

материалы в категории

Архитектура AVR микроконтроллера

Прежде чем начать изучать самостоятельное создание программ для AVR микроконтроллеров,  хорошо бы знать как эти самые программы исполняются, т.е. узнать побольше о устройстве (архитектуре) микроконтроллера.
В качестве небольшого вступления, хотел бы отметить тот факт что компания Atmel, как и большинство производителей микроконтроллеров, внутреннее строение своего микроконтроллера держат в сектрете, так что придется обходиться материалом предоставляемым разработчиком: даташиты, апп.ноты, блок-схемы и т.д. Таким образом производитель лишает нас возможности покритиковать его схемотехнические решения а также "стырить" какую-нибудь часть или весь контроллер целиком.
В принципе те кому это нужно было давно это сделали...Улыбаюсь
 
Итак приступим. Микроконтроллеры AVR реализованы по принципу гарвардской архитектуры- то есть память программ, память данных и шины доступа к ним разделены.
Сделано это было не просто так: такое разделение позволяет, к примеру, одновременно считывать новую ассемблерную инструкцию из памяти программ по одной шине и в тот же момент записывать результат предыдущей команды в память данных микроконтроллера (ОЗУ/УВВ/РОН) по другой. Это "разделение труда” называется конвейером (pipeline) и позволяет выполнять по одной команде за такт.
архитектура AVR контроллера

Память данных (Data Memory) в AVR микрокотроллере

 
Одна из ключевых составляющих AVR микроконтроллера является 8-битная Шина Данных (8-bit Data Bus), связывающая между собой ОЗУ, счетчик команд, регистр состояния, регистры периферийных устройств а также RALU (Регистры общего назначения+АЛУ) микроконтроллера.
 
Память данных представляет собой совокупность регистров общего назначения (РОН), регистров ввода/вывода а также статического ОЗУ (SRAM). В AVR имеются несколько наборов ассемблерных команд которые предназначены специально для адресации, к регистрам ввода/вывода (со своим адресным пространством), а также набор команд для адресации  через шину данных ко всей области памяти данных (со своим адресным пространством).
архитектура AVR микроконтроллера
При программировании на языке Си, программист может оперировать несколькими типами данных:
 
char – 8-битная переменная.
int, short – 16-битные переменные.
long, float, double – 32-битные переменные.
а также struct, union
 
 
Если мы объявим переменную внутри функции, то есть сделаем ее локальной, то компилятор выделит для нее один или несколько регистров общего назначения. Если в данный момент не хватает РОН, компилятор поместит переменную в оперативную память (точнее в Стек), что в последствии потребует дополнительные такты для считывания переменной из ОЗУ/Стека. Если мы объявим переменную за пределами функции (сделаем ее глабальной) или определим ее как static, то компилятор не раздумывая выделит для нее место в ОЗУ памяти. Так что совет: старайтесь, по возможности, объявлять переменные внутри функций, тем самым предоставив компилятору возможность для выбора наиболее оптимального размещения переменных.
 
Также в языке Си можно рекомендовать (не обязательно для исполнения) компилятору поместить ту или иную переменную в РОН при помощи ключевого слова register.
Поскольку AVR, как и большинство 8-битных микроконтроллеров, не умеет (аппаратно) работать с 16-/32-/64-битными переменными, для таких случаев разработаны библиотечные функции, входящие в состав компиляторов высокоуровневых языков, таких как Си.
Эти функции реализуют большое количество разнообразных математических/логических и пр. операций, таких как умножение/деление 16-/32-/64-битных переменных со знаком и без, работа с числами с плавающей запятой, работа со структурами, битовыми полями и т.д. Но как вы понимаете использование переменных данного типа, а значит и связанные с ними функции, приводит к увеличению числа тактов необходимых для чтения/записи а также регистров общего назначения используемых при адресации к таким переменным, а самое главное, хотя и небольшое, но дополнительное место в памяти программ, для хранения этих функций.
 

Память программ (Flash Program Memory) в AVR микроконтроллерах

 
Программа микроконтроллера по сути представляет собой один большой массив, каждая ячейка которого содержит одну ассемблерную инструкцию, в свою очередь одна ассемблерная инструкция занимает 2 байта памяти программ.  Срок действия памяти программ в AVR микроконтроллерах расчитан на 10000 циклов записи/стирания и может хранить информацию на протяжении 20 лет при температуре в 85°C и до 100 лет при температуре в 25°C.
Flash Program Memory
 

Регистры процессора AVR микроконтроллера

 
Регистры, являются "рабочими лошадками” любого микроконтроллера. При помощи регистров передаются/считываются/хронятся/записываются/вычисляются и т.д. значения переменных, адреса переходов и т.п., через регистры производится настройка и управление периферийными устройствами. Основные регистры учавствующие в вычислениях, контроле, настройке и упралении как периферийными устройствами так и всего микроконтроллера являются: регистровый файл общего назначения, регистр состояния, счетчик команд, указатель стека, регистры ввода/вывода.
 
Регистровый Файл общего назначения (General Purpose Register File) – регистровый файл состоит из 32 регистров общего назначения. Эти регистры используются в качестве операндов в процесе исполнения команд. При программировании на С, программисту предоставляется возможность оперировать  переменными, которые в поледствии будут помещены компилятором в регистровый файл (РОН) или ОЗУ микроконтроллера. То есть некий уровень абстракции. При программировании на ассемблере, программист работает непосредственно с регистрами общего назначения, адресами ОЗУ и т.д.
 
Регистр состояния (Status Register) – данный регистр содержит информацию о результате исполнения предыдущей ассемблерной интсрукции. При помощи данного результата, можно менять ход исполнения (ветвление) программы, то есть принимать решения в зависимости от результата предыдущей операции. Арифметические, логические и операции с битами приводят к изменению регистра состояния. Все остальные ассемблерные команды не изменяют поля регистра состаяния.
 
Счетчик команд (Program counter) – в этом регистре содержится адрес одной ячейки памяти программ (2 байта), то есть адрес ассемблерной инструкции которую на следующем такте надлежит извлечь и передать на исполнение. При подаче напряжения питания (включении микроконтроллера), гарантированно что в счетчике команд, будет находиться значение 0 (нуль), что означает что следующая команда (которая будет передана на исполнение) расположена по адресу 0 (нуль) памяти программ, то есть самая первая команда. После того как команда была извлечена и передана на исполнение, счетчик команд загружает адрес следующей команды.
 

 
В случае условных или безусловных переходов, то в счетчик команд загружается адрес той инструкции которая должна быть выполнена следующей. Ради одной команды бессмысленно делать такие "кульбиты", зачастую такие переходы используются для исполнения некоторой последовательности команд (подпрограммы или функции), что позволяет сэкономить память программ и заодно, сделать программу более читаемой.
 
Указатель стека (Stack pointer) – чтобы знать что такое указатель стека, надо знать что такое стек (Stack).

 
Стек используется в основном для хранения временных данных, таких как локальные переменные (те самые что не помещаются в регистры общего назначения), а также адреса возвратов из подпрограмм/прерываний. Следует отметить, что стек обычно расположен в верхней области ОЗУ, начиная с самой последней ОЗУ ячейки (RAM_END). В нижней ОЗУ, начиная с адреса 0×60 памяти данных, расположены глобальные переменные, массивы данных, константы, область памяти называемая Куча (Heap), используемая для динамического распределения памяти и т.д.
 
Указатель стека представляет собой два 8-битных регистра, которые всегда указывают на вершину стека, то есть в них хранится адрес последней помещенной в стек переменной. Читать/записывать данные можно только по адресу содержащегося в указателе стека. В то время как "нижняя часть” ОЗУ памяти остается неизменной, стек растет навстречу данным хранящимся в ОЗУ и может случится так, что данные стека перепишут данные ОЗУ. Если такая ситуация произойдет то ход исполнения всей программы будет нарушен и в дальнейшем непредсказуем. Таких ситуаций следует избегать и чтобы их не допустить рекомендуется использовать как можно меньше вложенных функций и временных переменных, а также не тратить попусту ОЗУ, впрочем нехватка оперативки извечная проблема.
 
Регистры ввода/вывода (I/O registers) – при помощи регистров ввода/вывода реализуется настройка и упраление периферийными устройствами микроконтроллера. При написании программы, регистрами ввода/вывода можно пользоваться как обычными переменными. У большинства периферийных устройств есть регистр контроля (Control Register), при помощи которого можно настроить режим работы периферийного устройства, а также регистр состояния, при помощи которого можно следить за ходом работы периферийного устройства.
 
В разделе "Instruction Set Summary” каждого даташита, можно найти описание всех ассемблерных инструкций, в том числе какие биты регистра состояния (Flags) они могут изменить, за сколько тактов выполняется каждая инструкция (Clocks).
 

 
На рис.4 приведены несколько ассемблерных инструкций из которых можно заметить что команды ветвления (Branch instructions) не воздействуют на регистр состояния (кроме команды reti), а также эти инструкции выполняются за большее число тактов чем остальные. Также здесь можно посмотреть как работают счетчик команд (PC) и указатель стека (STACK).
 
 
Источникhttp://easymcu.ru

Почта сайта