Начинающим программистам микроконтроллеров PIC

Автор:
*****@***ru

Исходя из собственного опыта начала изучения программирования микроконтроллеров постараюсь дать несколько практических советов по составлению программ на ассемблере. Все, приведенные ниже, примеры программирования даны применительно к Pic контроллерам среднего семейства Microchip, как наиболее приемлемых для начала освоения, ввиду относи - тельно простой их архитектуры и несложной системы команд ассемблера.

Предлагаемые программы вполне можно применять в виде готовых макросов (законченных подпрограмм).Они не привязаны к конкретному контроллеру, поэтому при применении следует учитывать данные из datasheet - ов.

1.Применение прерываний от переполнения таймера TMR0 (RTCC)

Примем тактовую частоту - Fтакт. = 4,096 МГц (стандартный кварц). Тогда время цикла составит t c = 1 / Fтакт. * 4 = 0,97656 мкс

INI_TMR ; инициализация режима прерываний от RTCC

bsf STATUS, RP0 ; выбираем банк 1

movlw b''

movwf OPTION ; предделитель для RTCC 1 : 32

bcf STATUS, RP0 ; банк 0

movlw b''

movwf INTCON ; разрешено прерывание от RTCC

movlw .96 ; загружаем в RTCC предварительное число 96

movwf TMR0

Получим время прерываний:
t i = t c * 32 * (= 160)
t i = 0,97656 * 32 * 160 = 5 000 мкс = 5 мс

Теперь, если в Вашу любую программу ввести бесконечный цикл (так называемый цикл ожи - дания прерывания), и окончание программы переводить на этот цикл, получим временную привязку к 5 мс. И после прерывания программа вернётся по адресу, указанном вектором прерываний (чаще это 04h).Для чего это можно использовать - смотри дальше.

НЕ нашли? Не то? Что вы ищете?

Итак:

;

org 0

START ; начало выполнения программы после

; включения питания

org 04h ; а это адрес вектора прерывания, по которому

main ; будет выполняться основная программа

;

START ; здесь обычно происходит обязательная ини-

INI_TMR ; циализация портов, режимов, регистров и т. п.

INI_PORTS

loop

goto loop ; а это и есть бесконечный цикл

;------

main

; далее идёт тело основной программы,

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

; вызываемой командой CALL:

ServTMR

btfsc INTCON, RTIF ; проверяем флаг срабатывания прерываний от RTCC и

call SET_TMR ; если "да",то снова инициализируем TMR0

return ; если "нет" - возврат в место вызова ServTMR в

; основной программе main

;

SET_TMR movlw .96

movwf TMR0 ; снова загружаем число 96

bcf INTCON, RTIF ; сбрасываем флаг срабатывания

retfie ; возврат с разрешением прерываний в ServTMR, а

; затем в основную программу main

Пример использования прерывания от RTCC для получения секундного импульса на одном из выходов, скажем, порта В - RB0 : Используем регистр Rsec, который должен быть ранее объявлен в в адресном поле рабочих регистров.

FORM_1S ; в каждом цикле, а он по прерыванию RTCC длится

incf Rsec, w ; 5 Мс, увеличиваем регистр Rsec на 1 до числа 200

xorlw.200 ; (5 мс * 200 = 1 сек)

btfsc STATUS, z

goto OUT_PORT ; при Rsec = 200 флаг z = '1' и переход на управление

; выводом RB0 порта В

return ; возврат в основную программу main

;

OUT_PORT btfss PORTB,0 ; проверяем состояние вывода RB0

goto OUT_ON ; если RB0 ='0', то устанавливаем в '1'

bcf PORTB,0 ; в противном случае - устанавливаем в '0'

goto main ; возврат в основную программу

;

OUT_ON bsf PORTB,0 ; устанавливаем RB0 = '1'

goto main

Таким образом на выходе RB0 порта В каждую секунду уровень сигнала будет изменяться то '0' то '1'.

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

Рассмотрим примеры преобразований двоичного кода b2 в двоично - десятичный BCD и наоборот.

В 8 - bit регистре можно записать в двоичном коде число от 0 до 255 ( от b'' до b'' ). Преобразуем двоичное число в три разряда двоично - десятичного кода - "сотни", "десятки" и "единицы". Для этого будем использовать следующие регистры, которые должны быть заранее объявлены в адресном поле рабочих регистров :

Rbin - регистр хранения числа в двоичном коде b2
Rhan - регистр "сотни" кода BCD
Rdec - регистр "десятки" кода BCD
Rsim - регистр "единицы" кода BCD

Преобразования проводим используя операции вычитания чисел 100, а затем 10 с подсчётом количества положительных вычитаний.

CON_100 movlw .100 ; вычитаем 100 из Rbin c проверкой, что

subwf Rbin, w ; результат не отрицательный. Флаг 'c' = 1 при

btfss STATUS, c ; результате > или = 0, и 'c' = 0 при < 0

goto CON_10

incf Rhan, f ; подсчёт количества "сотен"

movwf Rbin ; результат вычитания сначала храним в регистре

goto CON_100 ;аккумуляторе и только потом возвращаем в Rbin

; чтобы не потерять остаток при отрицательном

; результате вычитания.

CON_10 movlw .10 ; аналогично определяем "десятки"

subwf Rbin, w

btfss STATUS, c

goto end_con

incf Rdec, f

movwf Rbin

goto CON_10;

end_con

movf Rbin, w

movwf Rsim ; после вычитаний заносим остаток в "единицы"

;продолжение выполнения программы

Обратное преобразование BCD - кода в b2. Используем те же регистры Rhan, Rdec, Rsim где находится число в BCD - коде, регистры RbinH - старший разряд и RbinL - младший разряд для чисел ( > 255) в коде b2 и вспомогательные регистры RM1 - "множимое" , RM2- "множитель".Для преобразования BCD в b2 нужно умножить "сотни" на 100, "десятки" на 10 и сложить всё вместе с "единицами" и с учётом переноса в старший разряд при необ - ходимости. Для умножения используем операцию сложения.

B2X_100 movlw .99 ; преобразование "сотен"

movwf RM2 ; множитель = кол - во сложений (100) минус один

movf Rhan, w

movwf RM1 ; множимое = "сотни"

loopX100 addwf RM1,w

btfsc STASTUS, c ; проверяем перенос в старший разряд

incf RbinH, f ; если есть перенос

decfsz RM2,f ; контролируем количество сложений

goto loopX100

movwf RbinL ; результат сложения заносим в регистр мл. разряда

;

B2X_10 movlw .9 ; преобразование "десятков"

movwf RM2 ; множитель = кол - во сложений (10) минус один

movf Rdec, w

movwf RM1 ; множимое = "десятки"

loopX10 addwf RM1,w ; здесь перенос можно не проверять, т. к. результат

decfsz RM2,f ; всегда < 255

goto loopX10

addwf RbinL, f ; добавляем результат преобразования "десятков"

btfsc STATUS, c ; учитывая возможный перенос в разрядах

incf

RbinH, f

movf Rsim, w

addwf Rbin, f ; добавляем "единицы" с учётом возможного переноса

btfsc STATUS, c

incf RbinH, f

Конец преобразованиям и дальнейшее выполнение программы. В регистрах RbinL и RbinH получили 16 - bit число в коде b2.

Для выполнения арифметической операции деления по аналогии с умножением, рассмот - ренном выше, применяется операция вычитания. Допустим нам нужно произвести деление числа, находящегося в регистрах RHsum (старшие разряды) и RLsum (младшие разряды) - на делитель ( примем делитель не > 255) находящийся в регистре Rdel.

Результат будем заносить в регистры RHrez и RLrez (старшие и младшие разряды соот - ветственно) :

OP_DEL

movf Rdel, w

subwf Rlsum, w

btfss STATUS, c ; проверяем не отрицательный ли результат?

goto DEF_carry ; если "да", то проводим заём из ст. разряда

incf RLrez, f ; подсчитываем кол-во вычитаний с учётом

btfsc STATUS, c ; возможного переноса в старший разряд

incf RHrez, f

movwf RLsum ; восстанавливаем остаток, что бы не потерять

goto OP_DEL ; при отрицательном результате вычитания

;

DEF_carry

movlw 0h

xorwf RHsum, w ; всё ли заняли из старшего разряда в младший?

btfsc STATUS, z ; если "да", т. е. RHdel = 0 и в OP_DEL отри-

goto OUT_ DEL ; цат. результат - конец делению и выход

decf RHsum, f ; если "нет" - заём из старшего разряда и про-

incf RLrez, f ; должаем дальше

btfsc STATUS, c ; проверка необходимости переноса в ст. разряд

incf RHrez, f

goto OP_DEL