Файл: Самоучитель по программированию PIC контроллеров для начинающих (Е.А. Корабельников,2008).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 16.06.2025

Просмотров: 3640

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

;ГРУППА ПОДПРОГРАММ ПРЕОБРАЗОВАНИЯ 4-БАЙТНЫХ ДВОИЧНЫХ ЧИСЕЛ В 8-РАЗРЯДНЫЕ

;ДЕСЯТИЧНЫЕ ЧИСЛА.

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;Подготовка к преобразованию.

;================================================================================

Bin2_10

bcf

Status,C

;

Сброс флага переноса-заёма.

movlw

.32

;

Запись в регистр Count числа проходов

movwf

Count

;

преобразования, равного суммарному

;

количеству битов многоразрядного регистра

;

TimerL/TimerM/TimerH/TimerHH (8*4=32).

clrf

LED0

;

Сброс в 0 содержимого регистра LED0.

clrf

LED1

;

-------"------

LED1.

clrf

LED2

;

-------"------

LED2.

clrf

LED3

;

-------"------

LED3.

clrf

LED4

;

-------"------

LED4.

clrf

LED5

;

-------"------

LED5.

clrf

LED6

;

-------"------

LED6.

clrf

LED7

;

-------"------

LED7.

;-------------------------------------------------------------------------------

; Примечание: процесс преобразования

заканчивается при уменьшении числа проходов

; преобразования, которые заложены в регистр Count (.32), до нуля.

;================================================================================

; Циклический сдвиг влево.

;================================================================================

Loop16

rlf

TimerL,F

; Циклический сдвиг влево 4-байтного двоичного

rlf

TimerM,F

; числа, записанного в группе регистров

rlf

TimerH,F

; TimerL/TimerM/TimerH/TimerHH, на одну

rlf

TimerHH,F

; позицию через бит С регистра STATUS.

rlf

LED0,F

; Циклический сдвиг влево 4-байтного двоичного

rlf

LED1,F

; числа, записанного в группе регистров

rlf

LED2,F

; LED0/LED1/LED2/LED3, на одну позицию через

rlf

LED3,F

; бит С регистра STATUS.

decfsz

Count,F

; Декремент (-1) содержимого регистра Count с

; сохранением результата в нем же.

goto

adjDEC

; Если результат не=0, то переход в ПП adjDEC

; Если результат =0, то программа

; исполняется далее.

;================================================================================

;

Поразрядное

распределение содержимого регистров LED0...

3 (обоих

;

полубайтов)

по младшим полубайтам регистров LED0...

7.

;================================================================================

swapf

LED3,W

; Запись старшего полубайта LED3

andlw

0Fh

; в младший полубайт LED7.

movwf

LED7

; --------------------------------

movfw

LED3

; Запись младшего полубайта LED3

andlw

0Fh

; в младший полубайт LED6.

movwf

LED6

; --------------------------------

swapf

LED2,W

; Запись старшего полубайта LED2

andlw

0Fh

; в младший полубайт LED5.

movwf

LED5

; --------------------------------

movfw

LED2

; Запись младшего полубайта LED2

andlw

0Fh

; в младший полубайт LED4.

movwf

LED4

; --------------------------------

swapf

LED1,W

; Запись старшего полубайта LED1

andlw

0Fh

; в младший полубайт LED3.

movwf

LED3

; --------------------------------

movfw

LED1

; Запись младшего полубайта LED1

andlw

0Fh

; в младший полубайт LED2.

movwf

LED2

; --------------------------------

245


swapf

LED0,W

; Запись

старшего полубайта LED0

andlw

0Fh

; в младший полубайт LED1.

movwf

LED1

; --------------------------------

movfw

LED0

; Запись

младшего полубайта LED0

andlw

0Fh

; в младший полубайт LED0.

movwf

LED0

; --------------------------------

;--------------------------------------------------------------------

; Конец распределения. В младших полубайтах

регистров LED0...

7

;установлены двоично-десятичные числа в порядке возрастания разрядности.

;Старшие полубайты = 0.

;--------------------------------------------------------------------

return

;

Переход по стеку в группу подпрограмм

;

8-разрядной динамической индикации.

;================================================================================

; Запись в регистр FSR адресов регистров LED0...3 для дальнейшей косвенной

;адресации к ним в ПП adjBCD.

;Переход к обработке следующего LED - после возврата по стеку.

;================================================================================

adjDEC

movlw

LED0

;

Запись в регистр FSR, через регистр W,

movwf

FSR

;

адреса регистра LED0 с дальнейшим переходом

call

adjBCD

;

в ПП adjBCD (адрес следующей команды

;

закладывается в стек).

;---

> Возврат по стеку из ПП adjBCD.

movlw

LED1

;

-----------------------------

movwf

FSR

;

То же самое для регистра LED1.

call

adjBCD

;

-----------------------------

;---

> Возврат по стеку из ПП adjBCD.

movlw

LED2

;

-----------------------------

movwf

FSR

;

То же самое для регистра LED2.

call

adjBCD

;

-----------------------------

;---

> Возврат по стеку из ПП adjBCD.

movlw

LED3

;

-----------------------------

movwf

FSR

;

То же самое для регистра LED3.

call

adjBCD

;

-----------------------------

;---

> Возврат по стеку из ПП adjBCD.

goto

Loop16

;

Проход всех LED (с LED0 по LED3). Переход в

;

ПП Loop16, то есть на следующее кольцо

;

числовых преобразований.

;================================================================================

; Основные операции преобразования двоичных чисел в двоично-десятичные:

; операции сложения LED0...3 и констант 03h,30h с условиями по 3-му и 7-му битам.

;================================================================================

adjBCD

movlw

3

; Сложить содержимое

текущего LED (LED0...3) с

addwf

0,W

; числом 03h, c записью результата операции,

movwf

Mem

; через регистр W, в

регистр Mem.

btfsc

Mem,3

; Анализ состояния 3-го бита регистра Mem.

movwf

0

; Если бит № 3 =1, то содержимое регистра Mem

; копируется в текущий LED.

movlw

30

; Если бит №3 =0, то

содержимое текущего LED

addwf

0,W

; складывается с константой 30h, с последующей

movwf

Mem

; записью результата

операции, через регистр

; W, в регистр Mem.

btfsc

Mem,7

; Анализ состояния 7-го бита регистра Mem.

movwf

0

; Если бит №7 =1, то

содержимое регистра Mem

; копируется в текущий LED.

retlw

0

; Если бит №7 =0, то

регистр W очищается и

; происходит возврат

по стеку в ПП adjDEC.

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;ГРУППА КОМАНД ПРЕОБРАЗОВАНИЯ ДВОИЧНО-ДЕСЯТИЧНОГО КОДА В КОД 7-СЕГМЕНТНОГО

;ИНДИКАТОРА (относится к группе подпрограмм динамической индикации).

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TABLE .....................................

;.....................................

246


; .....................................

;******************************************************************************** end ; Конец программы.

Сначала разберемся с общей "конструкцией" программы.

Вначале ее исполнения, рабочая точка программы начинает свое движение по "линейному" участку ПП START, начиная с 1-й ее команды и до команды call Bin2_10.

После исполнения команды call Bin2_10, рабочая точка программы "прыгает" на 1-ю команду ПП преобразований чисел Bin2_10, после чего она начнет исполняться.

Врезультате исполнения команды call Bin2_10, адрес команды, следующей за командой call Bin2_10, "закладывается" в стек и находится там до окончания отработки ПП Bin2_10. После окончания отработки ПП Bin2_10 (после исполнения команды return), из вершины стека "выгружается" указанный выше адрес, и после отработки всех оставшихся команд ПП START, начинается исполнение ПП динамической индикации (то, о чем говорилось в предыдущем разделе).

После отработки ПП динамической индикации, рабочая точка программы переходит в ПП формирования 4-байтного, двоичного числа.

Обращаю Ваше внимание на то, что этот переход происходит без использования команды перехода, так как работа происходит на "линейном" участке программы (просто исполняется следующая команда).

"Конструкций" подпрограмм формирования 4-байтного, двоичного числа может быть множество. Это зависит от функциональности устройства, которое обслуживает программа. Главное, чтобы в результате работы этой ПП, тем или иным способом, было сформировано 4-байтное, двоичное число.

После того как это произошло, для обеспечения перехода в ПП преобразований чисел ("закольцовки"), осуществляется безусловный переход на метку NEW, входящую в состав ПП

START.

Таким образом, мы "прошлись" по кольцу полного цикла программы.

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

Это зависит от времени, в течение которого устройство находится во включенном состоянии. Разбираемся с возможными "непонятками".

Вопрос: "Зачем осуществлять безусловный переход на метку NEW? Если этого не делать (команда goto NEW и метка NEW отсутствуют), то из ПП формирования 4-байтного, двоичного числа, в ПП преобразований чисел, рабочая точка программы перейдет самоходом (стык находится на линейном участке программы)".

Ответ: да, таким образом рабочая точка программы войдет в ПП преобразований чисел, но выйти из нее она не сможет, так как стек будет пустой (куда переходить? "Глюк").

А пустой он будет потому, что перед исполнением ПП преобразований чисел, в него не "заложился" адрес возврата (не исполнена команда call).

Для того чтобы обеспечить эту "закладку", а заодно и вернуться "туда, куда положено", необходимо выполнить команду goto NEW (перейти на исполнение команды call Bin2_10). Разбираемся с ПП START.

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

Впротивном случае, ПП START будет исполнена всего один раз (после включения питания устройства, на 1-м "витке" полного цикла программы), и в ходе дальнейшей работы программы (от 2-го "витка" и далее), ПП START будет обходиться.

Вэтом случае, при неконтролируемом изменении содержимого задействованных в программе регистров (например, в результате какого-то сбоя), существует вероятность "зависания" программы.

Если предприняты меры по "борьбе с зависаниями" (например, работает сторожевой таймер), то в принципе, это не страшно, но если этих мер не предпринято или существуют сомнения, или речь идет об обеспечении максимальной надежности работы устройства, то перестраховаться не помешает.

При наличии такой перестраховки, последствия некоторых сбоев будут ликвидированы за счет приведения "поврежденного" содержимого регистров к норме.

Насколько я понимаю, в ответах на этот "скользкий вопрос", единого мнения нет. Можно, начиная со 2-го "витка" полного цикла программы, обойти ПП START. Можно не обходить.

247


Можно включить в полный цикл программы (от 2-го "витка" и далее) наиболее "ответственные", подготовительные операции ПП START (это что-то типа компромиссного варианта).

Реализацию именно такого подхода Вы и видите в тексте программы Bin2_10.asm. "Ответственные" (эта оценка субъективна) команды подготовительных операций ПП START располагаются ниже команды call Bin2_10, а команды подготовительных операций, "рангом пониже", располагаются выше ее.

Это означает то, что команды подготовительных операций, располагающиеся выше команды call Bin2_10, будут исполнены на 1-м "витке" полного цикла программы, и в дальнейшем (от 2-го "витка" и далее), они исполняться не будут.

Команды подготовительных операций, располагающиеся ниже команды call Bin2_10, будут исполняться на каждом "витке" полного цикла программы.

В "перестраховочном" случае, нужно заменить команду goto NEW на команду goto START. Если есть такое желание, то ничто не мешает осуществить такую замену.

А теперь давайте разберемся в сути преобразований двоичных чисел в двоично-десятичные. Предположим, что группа ПП формирования 4-байтного, двоичного числа "выдала на гора" 4-байтное, двоичное число.

По определению, такое число должно быть "заложено" в 4 регистра общего назначения. Пусть они будут называться Timer...

Так как регистров 4 штуки, то нужно определиться с порядком их старшинства:

TimerHH, TimerH, TimerM, TimerL.

Ранее об этом говорилось.

Если нужно что-то подсчитать, то речь идет о многоразрядном, двоичном счетчике, которому можно поставить в соответствие, например, двоичный счетчик на 8-ми микросхемах 555ИЕ5 (каждая 555ИЕ5 работает с полубайтом, а не с байтом, и поэтому таких м/схем нужно 8 штук). Эта аналогия относится к форме представления результатов счета.

По этому показателю, аналогия полная.

То есть, в обоих случаях, 32-битное, двоичное число будет отображаться в стандартном весовом коде.

Что же касается принципов счета, то существуют весьма существенные различия, связанные с тем, что в 555ИЕ5, счет реализуется только аппаратными средствами.

Пошли дальше.

Линейка 7-сегментных индикаторов, состоящая из 8-ми знакомест, может отобразить десятичное число не более 99 999 999.

Двоичный эквивалент этого числа в стандартном весовом коде:

00000101 11110101 11100000 11111111

См. конвертор систем исчисления, который у Вас имеется (можете проверить). Вопрос: "Что такое двоично-десятичное число"?.

Ответ: двоично-десятичное число это одно из чисел числового диапазона 0 … 9 ,

представленное в двоичной форме.

Двоично-десятичное число, по определению, не может быть больше 9-ти.

Из этого следует то, что оно отображается только в младшем полубайте байта, а старший полубайт байта всегда будет равен нулю (0000хххх).

Например, десятичное число 99 999 999, в двоичном виде, может быть отражено в 4-байтном регистре, но в двоично-десятичном виде, такое число не может быть отражено в четырех регистрах.

В последнем случае, для отображения этого числа потребуется 8 регистров. Число 99 999 999, в двоично-десятичном виде, выглядит так:

00001001 00001001 00001001 00001001 00001001 00001001 00001001 00001001

Вы видите, что во всех младших полубайтах всех 8-ми регистров, "лежат" девятки, а во всех старших полубайтах, нули.

Вот зачем и нужны 8 регистров LED0...7, "прописку" которых Вы видите в "шапке" программы. Первые четыре из них (LED0...3), в ПП преобразований чисел, задействуются для хранения промежуточных и конечных результатов преобразований.

Регистры LED4...7 для этого не задействуются.

На конечной стадии работы ПП преобразований чисел, из младших и старших полубайтов

248


регистров LED0...3, в младшие полубайты регистров LED0...7 (это не ошибка, никакой ошибки нет), переписываются конечные результаты преобразований чисел.

Это и есть то, что в дальнейшем, подлежит кодировке.

При этом, в старших полубайтах LED0...7 "выставляются" нули. Это как раз то, что Вы видите в примере (для случая 99999999).

Вконечном итоге, в младших полубайтах регистров LED0...7, на все время полного цикла ПП динамической индикации, фиксируются двоично-десятичные числа, с которыми эта ПП и будет работать.

Вобщем виде, работу ПП преобразований чисел можно описать так:

После "влёта" рабочей точки программы в ПП преобразований, в предварительно назначенный счетчик "проходов" Count, записывается константа, значение которой равно суммарному количеству битов 4-байтного регистра TimerHH/TimerH/TimerM/TimerL (в нем находится 4-байтное, двоичное число, которое нужно преобразовать в двоично-десятичное). То есть, в данном случае, числовое значение этой константы, должно быть равно .32.

На каждом из 32-х "витков" внутреннего цикла ПП преобразований чисел, содержимое счетчика "проходов" Count декрементируется, и если результат декремента не равен 0, то происходит переход рабочей точки программы на следующий "виток" внутреннего цикла ПП преобразований чисел.

Таким образом, войдя в ПП преобразований чисел, рабочая точка программы "наматывает" 32 "витка" внутреннего цикла ПП преобразований чисел, которые, в сумме, составляют полный цикл ПП преобразований чисел.

После этого, она выходит из ПП преобразований чисел по сценарию "программа исполняется далее".

При этом, содержимое счетчика "проходов" Count равно нулю.

Что происходит, в процессе "прохождения" рабочей точкой программы, каждого из 32-х "витков" внутреннего цикла ПП преобразований чисел?

Сначала производится групповой, циклический сдвиг содержимого 8-байтного регистра

TimerL/TimerM/TimerH/TimerHH/LED0/LED1/LED2/LED3 влево (через бит С регистра STATUS).

Перед этим, (в начале исполнения полного цикла ПП преобразований чисел), в регистры LED0...7 записываются нули.

То есть, их "старое" содержимое удаляется, после чего они готовы к работе.

В результате каждого такого группового сдвига (последовательный сдвиг содержимого 8-ми регистров. 8 команд RLF), происходит перенос содержимого старшего бита регистра TimerHH в младший бит регистра LED0.

Таким образом, через 32 таких сдвига, содержимое TimerL/TimerM/TimerH/TimerHH

переместится в LED0/LED1/LED2/LED3.

Если это трудно себе представить, то вспомните про "бегущую строку" или "бегущие огни". Если просто переместить содержимое TimerL/TimerM/TimerH/TimerHH в LED0/LED1/LED2/LED3, то это ничего полезного не даст, так как конечным результатом такого перемещения будет исходное, двоичное число.

Следовательно, после очередного группового сдвига, необходимо преобразовать содержимое 4-байтного регистра LED0/LED1/LED2/LED3 таким образом, чтобы на момент завершения всех 32-х групповых сдвигов (счетчик проходов Count очищен), во всех 8-ми полубайтах 4-байтного регистра LED0/LED1/LED2/LED3, с соблюдением порядка старшинства, "осели" двоично-десятичные числа (любое из них можно отобразить в полубайте), точно отражающие значение исходного, двоичного числа.

То есть, речь идет о процедуре типа "чтение – модификация – запись.

Так как запись результатов числовых преобразований производится в те же регистры, из которых производилось чтение (LED0...3), то при модификации, должен задействоваться регистр оперативной памяти (в нашем случае - Mem), в котором будут храниться как промежуточные, так и конечные результаты преобразований чисел.

По окончании текущего цикла модификации, модифицированное число, "осевшее" в регистре Mem, копируется в текущий LED.

Предположим, что это происходит.

После того как будет "отмотано" 32 "витка" внутреннего цикла ПП преобразований чисел, во всех 8-ми полубайтах 4-байтного регистра LED0/LED1/LED2/LED3 будут "лежать" 8 двоично-десятичных чисел.

249