Начинающим программистам микроконтроллеров PIC
Автор: Владимир Д.
[email protected]
Исходя из собственного опыта начала изучения программирования микроконтроллеров постараюсь дать несколько практических советов по составлению программ на ассемблере. Все, приведенные ниже, примеры программирования даны применительно к 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'00000100' movwf OPTION ; предделитель для RTCC 1 : 32 bcf STATUS,RP0 ; банк 0 movlw b'10100000' movwf INTCON ; разрешено прерывание от RTCC movlw .96 ; загружаем в RTCC предварительное число 96 movwf TMR0
Получим время прерываний:
t i = t c * 32 * (256 - 96
= 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'00000000' до b'11111111' ). Преобразуем двоичное число в три разряда двоично - десятичного кода - "сотни", "десятки" и "единицы". Для этого будем использовать следующие регистры, которые должны быть заранее объявлены в адресном поле рабочих регистров :
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
Источник: www.radiokot.ru