ногу свело!

 

Теперь разберем подробно программу, которая только что была залита в чип.  Написана на языке ассемблера (язык ассемблера схож с языком для программируемых микрокалькуляторов). В отличие от языков высокого уровня, в ассемблере нет конструкций типа присвоение, условные операторы, циклы и тому подобное. Вся программа состоит из последовательности машинных команд и блоков данных. А уже сами машинные команды могут быть командами пересылки, перехода по условию и т.д. Программисту на языке ассемблера самому приходится заботиться о распределении памяти и использовании регистров.

Выглядит оно вот так


;=======================================================================
;Autor:     MadMayDay 2008    
;Project:   StepByStep
;Name:      LED
;=======================================================================
.device     AT90S2313
.include    "2313def.inc"
.def        SlopReg=R16
;=======================================================================
Start:      rjmp    Init;
        
            ;----------------------------------------------------------
            ;устанавливаем часть ног микросхемы в состояние "выход",
            ;и часть в состояние "вход"
            ;входы   02,03,06,07,08,09,11
            ;выходы  12,13,14,15,16,17,18,19

Init:       ldi     SlopReg,0b11111111   ; грузим признак выхода
            out     DDRB,SlopReg         ; в регистр управления
                                         ; портом "B"
            ldi     SlopReg,0b00000000   ; грузим признак входа
            out     DDRD,SlopReg         ; в регистр упр. портом "D"

            ldi     SlopReg,0b11111111   ; грузим признак "активности"
            out     PortD,SlopReg        ; в регистр  входа
                                    
            ;----------------------------------------------------------
            ; просто тупо перекладываем все из D в B.
Begin:      in      SlopReg,PinD
            out     PortB,SlopReg;
            rjmp    Begin;
End:
;======================================================================

 

Начнем разбирать пример по строчкам.

Строчки, начинающиеся с точки с запятой, являются комментариями и компилятором не учитываются. Строки, начинающиеся с точки, являются директивами компилятора, и заставляют компилятор выполнять определенные действия. Остальные рассматриваются компилятором как мнемоники (имена) машинных команд микроконтроллера. Команды состоят из имени и операндов. Операндами могут являться регистры, или константы. Кроме того существуют «метки» оканчивающиеся на двоеточие.

 

Разберем приведенный выше пример. Сперва идут директивы компилятора.

.device    AT90S2313  - указывает компилятору, для какого типа процессора компилировать программу.

.include  "2313def.inc" – указывает компилятору включить в тело программы дополнительный файл.

.def         SlopReg=R16 – указывает компилятору, что шестнадцатый рабочий регистр мы будем называть SlopReg.

 

После идет блок команд настраивающих «переферию» ядра микроконтроллера. Затем идет основной цикл

 

Ldi -  команда загрузки константы в рабочий регистр.

Out – команда загрузки значения из рабочего регистра в порт.

In – команда загрузки значения из порта в рабочий регистр.

Rjmp – команда перехода по указанному адресу.

 

Разберем работу микропроцессора под управлением данной программы:

 

Если образно представить чип как некую фирму, то корпус – это здание фирмы. Процессор – это директор. Еще в этом здании существуют сотрудники – это периферия процессора. (ну а мы скромненько так – учредители всего этого безобразия)

 

Микропроцессор имеет несколько рабочих регистров (регистров общего назначения). Под рабочими регистрами мы подразумеваем  определенные области памяти находящиеся внутри микропроцессора. Более просто можно представить тридцать два (или больше) пустых папки поставленных одна за другой и пронумерованных (R1, R2…R16).  Кроме процессора (директора) никакие устройства не имеют к ним доступ.

 

Кроме регистров общего назначения чип имеет «на борту» регистры специального назначения или «порты».  С их помощью процессор управляет всей «периферией», которая находится в чипе. Они не являются собственностью процессора. Если снова образно представить чип как фирму, то директор раздает служащим задания через окошки в конторках. Эти «окошки» и есть регистры управления. Каждый служащий – это периферийное устройство, которое умеет делать только определенные действия, понимает определенные команды и закреплено за своим «окошком». Задача «директора» организовать совместную работу служащих. Он не знает какой «человек» сидит за окошком, и каким образом он выполняет порученное задание. Задача директора лишь дать нужные данные в нужное окошко.

 

То есть, уясним главное – у процессора НЕТ отдельных команд для доступа к определенным «периферийным» устройствам. Он не знает их «фамилии». Вся периферия управляется с помощью записываемых в соответствующие регистры управления данных. Регистры управления, как и рабочие регистры, пронумерованы. Однако,  для удобства использования в программе у них есть еще и имена. Компилятор, обрабатывая программу, заменяет эти имена на соответствующие им номера.

 

«Ноги» (выводы) чипа – это тоже «периферия», в простейшем случае они разделены на несколько групп. Каждая группа управляется портами, с помощью которых можно установить режим работы – прием или передачу данных. Возвращаясь к нашей ассоциации со зданием – ноги чипа это входы и выходы. В нашем здании есть несколько вестибюлей (групп выводов). Каждый вестибюль имеет 8 таких дверей. Они могут быть либо закрыты, либо работать вход, либо на выход.

 

В нашем случае мы используем «вестибюль» (физический порт) «B» и «D». В данном случае речь идет об устройстве, управляющем выводами  2,3,6,7,8,9,11 (порт B)  и  12,13,14,15,16,17,18,19 (порт D).  У каждого из этих устройств есть несколько регистров управления. Один регистр отвечает за направление DDRX (DataDirectionRegistr). Другой регистр отвечает за состояние выводов PinX. Этот регистр предназначен для чтения и служит лишь индикатором (частая ошибка новичков – писать в этот регистр. Но это тоже самое, что пытаться поднять напряжение в сети передвигая стрелку вольтметра). Третий регистр PortX используется в зависимости от направления. Когда ножка чипа сконфигурирована на ввод регистр PortX управляет «подтяжкой» (образно выражаясь доводчиком, пружиной двери) если не указать применение «подтяжки» нога чипа, оставленная в «воздухе» (то есть не присоединенный ни к земле, ни к питанию) будет «плавать» между нулем и единицей (как дверь болтается на ветру). Когда нога сконфигурирована на вывод – регистр PortX управляет её состоянием – ноль или единица.

 

Первыми двумя командами мы конфигурируем порт "B" так все его ножки становятся выходами. Каждому биту соответствует своя ножка. Когда бит устанавливается в единицу - ножка конфигурируется как выход. Когда в ноль -  соответственно как вход.

 

            ldi     SlopReg,0b11111111    ; грузим признак выхода

            out   DDRB,SlopReg                ; в регистр управления портом "B"

 

следующие две команды конфигурируют порт "D" как вход.

 

            ldi    SlopReg,0b00000000     ; грузим признак входа

            out   DDRD,SlopReg                ; в регистр упр. портом "D"

 

еще две команды устанавливают «подтяжку» к единице (высокий уровень) ножек порта "D"

 

            ldi     SlopReg,0b11111111    ; грузим признак "активности"

            out   PortD,SlopReg                ; в регистр  входа

 

Обратите внимание, что мы не можем напрямую «залить» константу в регистр управления. Это можно сделать только через рабочий регистр. Объясняется это тем, микросхемы AVR имеют так называемую RISC архитектуру ядра.

 

Что такое RISC архитектура и чем она отличается от того процессора, что стоит в наших компьютерах (CISC)?. Образно можно сравнить их как жонглера(RISC) и фокусника(CISC). Фокусник может переместить любой мячик в любое место, да и вообще превратить его во что угодно, однако он может оперировать всего несколькими мячиками и не очень быстро. Жонглер за один момент может переместить мячик только недалеко от себя, и то, только тот, который держит в руках, однако делает это очень быстро и может удержать в руках гораздо больше шариков, чем фокусник. Вот, в общем, и все концептуальные различия.

 

Теперь все что нужно сделать нашей программе – это переложить значения, полученные из порта “D” в порт “B”. Опять же через рабочий регистр..

 

Begin:  in    SlopReg,PinD

             out  PortB,SlopReg;

 

и повторить это пока не наступит маленький конец света в масштабе одной микросхемы.

 

             rjmp   Begin;

 

вот в общем то и все. Программа предельно проста – берет уровни с  одних ножек и выставляет такие-же на соответствующих.

 

Дальше --->