Пример 1. Мигающие светодиодыВ предыдущем разделе мы получили неоценимый опыт работы с ножками порта МК. Однако у многих это вызовет ироничную улыбку. Действительно, в чем польза монотонного включения группы ножек порта.
В этом примере мы оживим работу нашего МК, попеременно устанавливая высокий и низкий уровни сигналов на одних и тех же ножках.
Рассмотрим следующую программу.
LIST P=PIC16F84A
__CONFIG H3FF1
STATUS EQU H0003
PORTB EQU H0006
TRISB EQU H0006
Reg_1 EQU H000C
Reg_2 EQU H000D
Reg_3 EQU H000E
org 0 ; начало программы
; подготовительные моменты
bsf STATUS,5 ; переход в Банк 1
clrf TRISB
bcf STATUS,5 ; переход назад в Банк 0
; установка сигналов на порту В
m1 movlw b11111111 ; запись в аккумулятор
movwf PORTB ; перенос из аккумулятора в порт
call Pause ; переход на метку (с возвратом)
clrf PORTB ; "очистка" порта
call Pause ; переход на метку (с возвратом)
goto m1 ; переход на метку (зацикливание)
;delay = 500000 machine cycles
Pause movlw .85
movwf Reg_1
movlw .138
movwf Reg_2
movlw .3
movwf Reg_3
wr decfsz Reg_1, F
goto wr
decfsz Reg_2, F
goto wr
decfsz Reg_3, F
goto wr
return
end ; конец программы
И собственно результат компиляции – текст прошивки:
:020000040000FA
:10000000831686018312FF308600092086010920AD
:10001000032855308C008A308D0003308E008C0B05
:0C0020000F288D0B0F288E0B0F280800F6
:02400E00F13F80
:00000001FF
Разберем эту программу. В шапке программы появилось обозначение трех регистров общего назначения как, например, Reg_1 EQU H000C . Закономерен вопрос – почему именно такие имена и такие адреса? Адреса регистров общего назначения согласно документации лежат в диапазоне от h0C до h4F , т.е. всего 68 регистров; первые три регистра из этого диапазона мы именовали для наших дальнейших целей. Имена регистров выбраны по причине рациональности использования как в плане перечисления, так и в плане восприятия. Действительно, проще регистрам дать "бытовые" номера, чем придумывать какие-то имена и путаться в них.
Подготовительные моменты рассмотрены в предыдущей программе.
Далее следует сегмент установки сигналов на порту. Этот сегмент является основным в работе программы. Суть проста. Сначала устанавливается на всех ножках сигнал высокого уровня (2 команды). Затем выдерживается пауза 0,5 сек (светодиоды включены). Затем порт очищается, что приводит к установке сигналов низкого уровня. Снова пауза (светодиоды выключены). И, наконец, переход к началу, что зацикливает нашу программу.
Переход к сегменту паузы производится командой call Pause . Эта команда, как мы упоминали ранее в главе 2, используется в паре с одной из двух команд (RETLW и RETURN). Собственно команду RETURN мы видим в конце сегмента паузы.
Сегмент паузы осуществляет задержку в программе длительностью 0,5 сек. Понятие о времени и расчет задержек рассмотрен в главе 3. Мы использовали программу Pause ver1.2 для создания сегмента паузы. После этого несложно догадаться, почему регистрам общего назначения даны такие. С другой стороны, нет никаких препятствий переименовать регистры в созданном сегменте паузы. Нами был выбран путь меньшего сопротивления.
В этом примере важно понять, что к одному и тому же сегменту паузы (сегменту задержки) мы дважды обращались посредством переходов. Однако, возможен частный нерациональный случай, когда такие переходы не используются и после включения и выключения стоит индивидуальный сегмент задержки. Такой решение должно возникнуть лишь в том случае, если длительность свечения и длительность в потушенном состоянии отличаются по времени. Ниже фрагмент программы такого частного случая
; установка сигналов на порту В
m1 movlw b11111111 ; запись в аккумулятор
movwf PORTB ; перенос из аккумулятора в порт
call Pause1 ; переход на метку (с возвратом)
clrf PORTB ; "очистка" порта
call Pause2 ; переход на метку (с возвратом)
goto m1 ; переход на метку (зацикливание)
;delay = 500000 machine cycles
Pause1 movlw .85
movwf Reg_1
movlw .138
movwf Reg_2
movlw .3
movwf Reg_3
wr1 decfsz Reg_1, F
goto wr1
decfsz Reg_2, F
goto wr1
decfsz Reg_3, F
goto wr1
return
;delay = 1000000 machine cycles
Pause2 movlw .173
movwf Reg_1
movlw .19
movwf Reg_2
movlw .6
movwf Reg_3
wr2 decfsz Reg_1, F
goto wr2
decfsz Reg_2, F
goto wr2
decfsz Reg_3, F
goto wr2
return
Здесь два сегмента паузы помеченные метками Pause1 и Pause2. Для правильности работы сегментов метки wr в соответствующих сегментах паузы переименованы в wr1 и wr2. Обратите внимание, в разных сегментах паузы используются одинаковые регистры общего назначения, т.к. в начале каждой паузы эти регистры заполняются своими значениями.
И даже в этом случае, программу можно попытаться упростить.
; установка сигналов на порту В
m1 movlw b11111111 ; запись в аккумулятор
movwf PORTB ; перенос из аккумулятора в порт
call Pause1 ; переход на метку (с возвратом)
clrf PORTB ; "очистка" порта
call Pause1 ; переход на метку (с возвратом)
call Pause1 ; переход на метку (с возвратом)
goto m1 ; переход на метку (зацикливание)
Действительно, нам ни что не мешает два раза подряд обратиться к одному и тому же сегменту паузы. Экономия места строчек в программном коде и эстетичность очевидна. По этому поводу можно высказать два противоречивых подхода, каждый из которых имеет право на существование. Первый подход заключается в быстром создании неоптимизированного и объемного кода; лишь бы работал. А второй подход – это создание кода, где нет ничего лишнего, где одно вытекает из другого, а другое является частью еще какого-либо сегмента. И на всё это требуется время для создания программы. Уверяю вас, второй подход гораздо интереснее. Вы по-новому будете смотреть на машинную математику и логику её выполнения. Через некоторое время вы этому научитесь на наших примерах и иначе не сможете работать.
В заключении этого примера предлагаю вам самостоятельно модифицировать программу и исключить команду очистки порта (clrf PORTB). Вместо этого предлагаю "затирать" число в порту B другим числом, отличающимся от нуля. Даю подсказку – вам потребуется пара команд (movlw и movwf). Поэкспериментируйте, вам понравится.
|