Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Микроконтроллеры PIC фирмы Microchip для начинающих выпуск №7


РАССЫЛКА Микроконтроллеры PIC фирмы Microchip для начинающих
Ведущий: Алексей (pont_a@mail.ru)


Доброго времени суток, дамы и господа!!

Как я и обещал, весь сегодняшний выпуск будет посвящен примерам программирования для микроконтроллеров серии PIC18! Мы узнаем, что такое макросы, и как их писать. Научимся писать подпрограммы и вызывать их!

Что такое макрос? Это определенный код, который компилятор вставляет в программу, на место, где этот макрос вызывается. Макрос может содержать локальные метки. Метки в теле макроса, обязательно, надо определять, как локальные. Иначе, если макрос будет вызываться больше одного раза в программе, то получиться наложение глобальных меток и компилятор выдаст ошибку!  Также, макросу могут передаваться различные параметры. Обычно, макросы используют, если в программе, часто встречаются одинаковые участки кода.
Тело макроса описывается с использованием директив ассемблера: macro – начало макроса, endm – окончание макроса. Для объявления локальных меток используется директива local.

Повторяем для вновь подписавшихся объявление некоторых констант, которые фигурировали уже в предыдущих выпусках рассылки:
ab        equ      0          ; признак, что переменная находиться в access bank                  
bb        equ      1          ; признак, что адрес необходимо считать с использованием регистра BSR
rw        equ      0          ; rw=0. Результат размещается в регистр-аккумулятор WREG
rf         equ      1          ; rf=1.  Результат размещается в регистр FREG

Например: макрос сложения константы и двухбайтовой переменной (a=a+k), где передаваемые параметры: a – двухбайтовая переменная, k – константа, b – номер банка, в котором расположена переменная a.
Это тело макроса. Оно объявляется, обычно, в начале программы.
ADD16K  MACRO  a_low, a_high, k, b
            MOVLB         b                                  ; устанавливаем банк данных переменной а
            MOVLW        LOW(k)                     ; WREG= младший байт константы     
            ADDWF         a_low, rf, bb               ; сложили младшие байты
            MOVLW        HIGH(k)                    ; WREG= старший байт константы
            ADDWFC      a_high, rf, bb ; сложили старшие байты и флаг переноса
            ENDM

Пусть у нас в программе микроконтроллера:
tempa            equ      0x00                ; смещение переменной tempa = 0
CONST          equ      0x55AA          ; константа  

Это вызов макроса. Вызов происходит уже из программы микроконтроллера.

ADD16K tempa, tempa+1, CONST, 1

Реально, компилятор, вместо вызова вставит тело макроса. В дизассемблерном  листинге  вызов макроса будет уже выглядеть так:

MOVLB         0x1                     
MOVLW        0xaa
ADDWF         0, F, BANKED
MOVLW        0x55
ADDWFC      0x1, F, BANKED

Пример макроса с локальной меткой.
Если константа s=0, то выполняется действие: a=a+c. Иначе, выполняется действие: a=a-c.  Где: b – номер банка памяти данных, в котором расположены переменные а и с.
AS8REG        MACRO  a, с, s, b
            LOCAL          Met1, Met2
            MOVLB         b
            MOVLW        s
            IORLW          0                                 ; проверяем значение константы s
            BZ                   Met1                         
            MOVF            c, rw, bb                      ; s не равно 0, а=a-с 
            SUBWF          a, rf, bb
            BRA               Met2  
Met1:
            MOVF            c, rw, bb                      ; s=0, a=a+с
            ADDWF         a, rf, bb
Met2:                        
            ENDM
Сколько раз был вызов макроса в программе, столько раз компилятор и вставит его тело в программу. Такой механизм позволяет увеличить скорость работы программы, но при этом, сильно расходуется программная память. Если быстродействие разрабатываемого устройства, не является критичным фактором, или, алгоритм программы достаточно сложен,  то используют подпрограммы (функции).

Подпрограммы позволяют экономить память программ, но расходуется время на вызов функции и возврат из нее.  Функции позволяют структурировать всю программу и облегчить ее написание. Например, обычно, программы для микроконтроллеров состоят из: основного цикла, обработчика прерываний и набора подпрограмм для работы с разными периферийными внешними и внутренними устройствами.  Код функции располагается в программной памяти только один раз. И дальше, при каждом вызове функции, счетчик программ (PC) переходит на этот код. Возврат осуществляется командами: RETURN, RETLW и т.п. Обязательно, код подпрограммы должен заканчиваться командами возврата!!!  Под возвратом подразумевается, что счетчик программ начинает, дальше, исполнять код основной программы – следующая команда после вызова подпрограммы: RCALL, CALL.      
Например: подпрограмма выполняет тоже действие, что и предыдущий макрос. 
Это тело функции:
AS8REGF:
            MOVF            c, rw, bb          ; WREG=c    
            MOVF            s, rf, bb           ; s=s (MOVF формирует Z-флаг
            BZ                   Met1                         
            SUBWF          a, rf, bb           ; s не равно 0, а=a-WREG
            RETURN                              ; возврат из подпрограммы         
Met1:
            ADDWF         a, rf, bb           ; s равно 0, a=a+WREG
            RETURN                               ; возврат из подпрограммы

Вызов функции:
            …
            MOVLB         1                      ; все переменные расположены в bank1 памяти данных
            MOVLW        3                      ; заносим значения в передаваемые функции переменные
            MOVWF        a, bb                ; a=3
            MOVLW        2                      ; c=2
            MOVWF        c, bb
            CLRF             s, bb                ; s=0, функция выполнит операцию a=a+c
            CALL             AS8REGF      ; вызов функции, дальше будет выполняться тело функции
            ; после возврата из функции a=a+c=2+3=5      
            INCF              c, rf, bb           ;с=с+1 сюда счетчик команд попадет, после возврата из функции
           
Если в передаваемых параметрах переменная s не будет равна 0, то функция выполнит операцию: a=a-c=3-2=1
Но самый распространенный случай – это, когда используются и макросы и функции! Причем, как из функции можно вызвать макрос, так и из макроса вызвать функцию!

Приведу пример, где используется и макрос и функция. Конечно, с таймером получиться намного проще код, но пока, для уяснения темы, мы будем делать так. А когда начнем изучать периферийные устройства, я покажу, как делается задержка с помощью таймера.
Программная организация задержек.
Пусть у нас микроконтроллер работает на частоте 20МГц, т.е. его частота командных циклов равна 20/4 = 5 МГц. Получается, что микроконтроллер выполняет одну команду за 200нсек (1/5000000), при условии, что на выполнение команды требуется один командный цикл (см. предыдущие выпуски рассылки).

;Переменные для формирования задержки:
count               equ      0x00
count2            equ      0x01
ms                   equ      0x02

;Макрос делает задержку на 100мкс:
DELAY_100MKS     MACRO
            LOCAL          loop_m
            MOVLW        0x64                ; count=100   
            MOVWF        count, ab         ; погрешность задержки 400нс (эти 2 команды)
loop_m:                                             ; каждый цикл выполняется за 1 мксек
            NOP                                       ; 200нс
            NOP                                       ; 200нс
            DECFSZ         count, rf, ab    ; 200нс, если count=0, то 400нс
            BRA               loop_m            ; 400нс (команда выполняется за 2 цикла)
            ENDM
; макрос формирует задержку на time_val мили секунд. Где, time_val – константа.
DELAY_MSEK        MACRO        time_val
            LOCAL          m1
            MOVLW        time_val
            IORLW          0                      ; если задержка 0, то выходим
            BZ                   m1
            MOVWF        ms, ab             ; загружаем количество мсек
            CALL             Delay_ms       ; вызов функции, реализующей задержку       
m1:
            ENDM          
; Функция реализует задержку на время, задаваемое переменной ms(количество мсек)
; на 1мсек функция дает погрешность в 10мксек
Delay_ms:                                          ; максимальная задержка 255 мсек
            MOVLW        0x0A              
            MOVWF        count2, ab       ; count2=10, 1мсек=10*100мксек
Delay_ms_m1:
            DELAY_100MKS                 ; вызов макроса, задержка на 100мксек
            DECFSZ         count2, rf, ab  ; макрос вызвали 10 раз?
            BRA               Delay_ms_m1            ; нет, повторяем цикл
            DECFSZ         ms, rf, ab        ; да, 1мсек прошла,  весь таймаут прошел?
            BRA               Delay_ms       ; нет, повторяем цикл для задержки в 1 мсек
            RETURN                               ; выходим из функции     

В основной программе, формирование задержки будет выглядеть так:

DELAY_MSEK        0x64    ; формирование задержки на 101мс (1мс – погрешность 10мкс*100)

На этот год закончим!

Следующий выпуск выйдет после праздников! В нем я расскажу, как создавать проект в среде разработки Mplab. После этого, будет несколько выпусков по аппаратной части! Потом, начнем изучать периферию микроконтроллеров серии PIC18 на примере микроконтроллера PIC18F452.

Полный архив рассылки Вы можете прочитать на нашем сайте! Также, на сайте, я буду, периодически, выкладывать статьи по микроконтроллерам и примеры реализации готовых устройств. Коды работы, с какой либо периферией. Например, сейчас выложен пример  устройства, организующего управление 20-ю светодиодами. Вполне подойдет для организации новогодней гирлянды!

Компания 2AplusA поздравляет Вас с наступающим Новым 2007-м годом! Желаем Вам здоровья и удачи в Новом году!


С уважением, Алексей pont_a@mail.ru
Cайт разработчиков 2AplusA http://2aplusa.ru


В избранное