44953

Устройство формирования сигнала тонального вызова

Реферат

Информатика, кибернетика и программирование

Полупериоды формируем используя €œзакольцовку рабочей точки программы в подпрограммах задержки по аналогии с программой Multi. К моменту начала составления текста программы желательно определиться с как можно большим количеством исходных данных. Так как программа должна исполняться непрерывно то в случае нахождения устройства в режиме ожидания включения на передачу рабочая точка программы должна €œзакольцеваться€ до последующего нажатия на кнопку в какой-нибудь подпрограмме. Часто такого рода закольцовки осуществляют в...

Русский

2013-11-14

87.52 KB

2 чел.

Устройство формирования сигнала тонального вызова

Необходимо создать устройство формирования сигнала тонального вызова с высокостабильной частотой 1450 Гц в течение определенного интервала времени и передающего сформированный сигнал при включении устройства на передачу.

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

1. Частота сигнала тонального вызова должна быть высокостабильной и равной 1450 Гц.

2. Формирование сигнала тонального вызова должно инициализироваться с помощью соответственного управляющего переключателя (или кнопки с двумя состояниями).

3. После инициализации передачи, сигнал тонального вызова должен формироваться (и “выдаваться” в эфир) в течение калиброванного интервала времени, оптимально подобранного по длительности.

4. Режим тонального вызова должен включаться и отключаться вручную.

Теперь начинаем “раскрутку” алгоритма работы устройства по предварительно определенным требованиям. Так как речь идет о высокостабильной частоте сигнала, то тактовый генератор микроконтроллера должен быть кварцевым (если не требуется высокой стабильности частоты, то его можно перевести в режим RC-генератора. В микроконтроллерах PIC такое предусмотрено). Итак, используем стандартный кварцевый генератор (называется XT), с частотой формируемых тактовых сигналов 4 мГц. В этом случае, один машинный цикл равен одной микросекунде, что очень удобно при предварительных (т.е. “прикидочных”) расчетах. При использовании кварцевого генератора, время “выдачи” в эфир сигнала тонального вызова также окажется высокостабильным, хотя к этому интервалу времени, высоких требований по стабильности не предъявляются.

В принципе, можно сформировать импульсную последовательность любой скважности. Исходя из того, что анализатор приемника тонального вызова корреспондента (приемника тонального сигнала) представляет собой цифровое устройство, некритичное к скважности импульсной последовательности, вырабатываемой на передающей стороне, то каких-то жестких условий по скважности нет, за исключением того, что импульсы должны быть “не слишком короткими и не слишком длинными” (учет инерционности тракта передачи). С целью упрощения устройства, задаем отношение длительности периода к длительности импульса  равным 2 (так называемый “меандр”). Полупериоды формируем, используя “закольцовку” рабочей точки программы в подпрограммах задержки (по аналогии с программой Multi.asm).

В части касающейся 1-го пункта, всё, что возможно определить на предварительной стадии разработки устройства (“грубая прикидка”), определено. К моменту начала составления текста программы, желательно определелиться с как можно большим количеством исходных данных.

Так как требования 2-го и 3-го пунктов между собой связаны, то для их реализации, определение исходных данных лучше всего производить в комплексе. Совершенно очевидно, что при нажатии на кнопку (включении на передачу) должен быть запущен какой-то механизм, формирующий сам сигнал тонального вызова и “дозирующий”, по времени, его “выдачу” в эфир.

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

А теперь прикинем, а что же должно быть далее? Естественно, должен начаться процесс формирования одного периода тонального сигнала, соответствующего частоте 1450 Гц. Предположим, мы его сформировали, а дальше? Совершенно очевидно, что необходима “закольцовка” рабочей точки программы, с конца на начало процедуры формирования одного периода, а иначе сформируется один период и на этом все закончится. Если поставить время нахождения, в этой “закольцовке” в зависимость только от времени нажатия кнопки включения передачи, то тональный сигнал будет “выдаваться” в эфир в течение всего времени нажатия кнопки. Получаемая в этом случае неоднозначная продолжительность сигнала может оказаться неприемлемым по каким-либо производственным (или по каким-то другим) причинам.

Желательно, чтобы после нажатия кнопки тональный сигнал “выдавался” в эфир в течение вполне определенного времени. В качестве оптимального значения выберем 3 сек. и будем ориентироваться на него. Итак, процесс формирования сигнала тонального вызова должен быть поставлен в зависимость не только от сигнала включения на передачу, но и от таймера (счетчика времени), отмеряющего с момента нажатия на кнопку 3 сек.

Таймер должен что-то считать. Решение напрашивается само собой: считать нужно количество периодов сигнала тонального вызова. Записывая в таймер различные значения констант, можно получить те или иные значения времени “выхода” в эфир сигнала тонального вызова, а следовательно и “отрегулировать” это время.

Таким образом, можно сформировать следующий принцип работы программы: В режиме ожидания включения на передачу, в подпрограмме Start, рабочая точка программы должна “уйти в вечное кольцо” и оставаться в ней до момента переключения устройства на передачу. После того как это случится, рабочая точка программы должна выйти из этого “вечного кольца” по сценарию “программа исполняется далее” и еще раз “закольцеваться” в процедуре формирования периода тонального сигнала. Одновременно должен начать работать таймер, время срабатывания которого зависит от количества периодов тонального сигнала, сформированных программой с момента нажатия на кнопку. После отсчитывания заданного количества периодов (через 3 сек.), таймер должен “сработать”, и рабочая точка программы не зависимо от того, находится ли устройство на передаче или нет, до следующего рабочего перепада управляющего сигнала (до следующего перехода к передаче) должна снова “уйти в вечное кольцо” подпрограммы Start.

Для того чтобы еще более конкретно оформить “конструкцию” программы, необходимо ответить на вопрос: “В каком месте программы должны записываться константы (числа, определяющие величину интервала времени “выхода” тонального сигнала в эфир)”? Дело в том (а это очень важно и сейчас, и при работе с другими программами), что константы, используемые в работе устройств, подобных упомянутому выше таймеру (устройств, которые что-то подсчитывают), должны “закладываться” (записываться) в эти устройства предварительно, то есть, до начала их работы. Эта “закладка” должна происходить вне цикла работы этого таймера (счетчика), а иначе, значения констант будут обновляться в каждом цикле и таймер никогда не сработает (никогда не досчитает до нуля). В нашем случае ясно, что вне 3-х секундного “кольца” находится только подпрограмма Start.

Таким образом, задача сводится к определению “места”, внутри подпрограммы Start, в которое можно “врезать” команды записи констант. В частности, их можно “врезать” между командами подготовительных операций, но это затрудняет комфортное восприятие текста программы. Во избежание этого, лучше всего поместить группу команд записи констант в конце подпрограммы Start.  

Для реализации четвертого пункта, нужен какой-то орган управления типа переключателя или кнопки.

Переходим к составлению (точнее, к описанию) принципиальной схемы включения микроконтроллера. Так как применяется стандартный кварцевый генератор (XT), то можно использовать стандартную схему подключения кварца. В отношении используемых выводов портов микроконтроллера назначим следующую привязку:

  1. вывод RB0 порта В будет входом управления режимами работы устройства (ожидание/передача). Этот вывод нужно подключить к тому контакту кнопки управления, который не соединен с корпусом. В этом случае, режиму ожидание будет соответствовать 1, а режиму передачи 0;
  2. вывод RB2 назначаем выходом устройства тонального вызова;
  3. вывод RB6 назначаем входом управления работы передатчика (вкл/выкл режима тонального вызова).

Таким образом, уровни управляющих сигналов “четко привязались” к исполняемым операциям, и к ним, в дальнейшем, будет “привязана” программа.

Алгоритм работы программы (стратегический замысел) сформирован ранее. Принципиальная схема описана. Переходим к тексту программы.

Текст программы выглядит так:

 

;Device of the call 1.45 KHz

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

; Microcontroller PIC16F84A

; Clock Frequency 4.0 MHz

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

Sec  equ  0Ch

SecH  equ  0Dh

SecL  equ  0Eh

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

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

 org  0

 goto  START

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

START clrf  IntCon

 clrwdt

 bsf  Status,RP0

 movlw .65

 movwf TrisB

 movlw .143

 movwf Option_REG

 bcf  Status,RP0

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

  movlw .15

 movwf SecH

 movlw .255

 movwf SecL

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

CYCLE btfsc  PortB,0

 goto  START

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

 btfss  PortB,6

 goto  PRD

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

  bcf  PortB,2

 nop

 nop

 movlw .85

 movwf Sec

PAUSE_1 clrwdt

  decfsz Sec,F

 goto  PAUSE_1

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

  bsf  PortB,2

 nop

 nop

 movlw .83

 movwf Sec

PAUSE_2 clrwdt

  decfsz Sec,F

 goto  PAUSE_2

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

  decfsz SecL,F

 goto  CYCLE

 decfsz SecH,F

 goto  CYCLE

 bcf  PortB,2

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

PRD  clrwdt

 btfss  PortB,0

 goto  PRD

 btfss  PortB,0

 goto  PRD

 goto  START

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

 end

Можно проассемблировать программу и убедиться в отсутствии ошибок. Давайте разбираться с программой.

Напоминаю, что движение рабочей точки программы по тексту программы, никогда не останавливается (за исключением “уходов” в спящий режим SLEEP). Это означает то, что если необходимо задержать исполнение последующих команд, то рабочую точку программы обязательно нужно “закольцевать” (задержать) в какой-то подпрограмме, образно выражаясь, “поставленной на счетчик”. Выход рабочей точки программы из этой подпрограммы возможен либо после завершения, программно организованным счетчиком, своего счета, либо после поступления внешнего управляющего сигнала. Любая программа это постоянная “вереница” такого рода колец, и необходимо самым тщательным образом разобраться с “технологией” этих “закольцовок”.

Вверху “заголовка” программы, обычно, помещается общая пояснительная информация о программе, об устройстве, которое она обслуживает и т.п. (“правило хорошего тона”. Не обязательно, но желательно). Эта информация может быть изложена в произвольной форме. То же самое относится и к комментариям.

В “активной” части “заголовка” программы для реального микроконтроллера размещается дополнительная информация директивного характера (включаемые файлы, конфигурационные биты, “прописываемые” регистры специального назначения и их биты и др.), а при работе с программным симулятором (с помощью которого мы учимся работать с микроконтроллерами) в них нет необходимости.

Далее необходимо определиться с регистрами общего назначения, т.е. их “прописать”. При этом нужно “прописывать” только те регистры, которые задействованы при выполнении программы (к содержимому которых происходят обращения). “Прописка” происходит при помощи директивы equ с указанием адресов регистров в области оперативной памяти. Регистры общего назначения прописываются по адресам области оперативной памяти, не относящимся к регистрам специального назначения, т.е. можно выбрать любые из этих адресов. Регистрам общего назначения нужно присвоить какие-нибудь названия.  Ну и соответственно, “прописать” эти названия в “заголовке” программы. В программе они названы как Sec, SecH и SecL.

Здесь предоставлена готовая программа, и по этой причине, не вполне понятно, какие именно регистры общего назначения нужно прописывать. Анализируя текст программы, станет понятным и то, зачем они нужны и какие функции выполняют. В тексте рассматриваемой программы, таких регистров три. В других программах, их может быть другое количество. Зависит от замысла.

И последнее, что нужно указать в “заголовке: “точку входа’ в программу и “точку входа” в подпрограмму прерывания (о последнем будет отдельный разговор). Так как в нашей программе прерывания не используются, то и “точку входа” в подпрограмму прерывания указывать не нужно.

Для того чтобы задать “точку входа” в программу, необходимо, при помощи директивы org, указать, с какого адреса (в счетчике команд PC) нужно начать исполнение программы  (org    0 – начать с нулевого адреса). В подавляющем большинстве случаев, указывается нулевой адрес, но можно указать и другой. В последнем случае, в памяти программ, все команды программы смещаются вниз на количество ячеек PC, указанное в рабочей части директивы org (применяется редко и в специфических случаях).

Если рабочая часть программы начинается с подпрограммы START, то никаких дополнительных “смещений” не нужно: программа начнет исполняться с подпрограммы START, как говорится, “своим ходом”. А если подпрограмма START находится где-то в “дебрях” текста программы (например, в середине текста), то нужно “смещение”, в качестве которого используется команда goto START. В соответствии со сказанным, в нашей программе после директивы org 0, команда goto START не нужна, но в обучающих целях (для того, чтобы привыкали к нему), убирать ее не стали, так как во многих случаях, эта команда нужна.

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

Рабочая часть программы начинается с подпрограммы START, которая, в свою очередь, начинается с подготовительных операций и заканчивается записью в регистры общего назначения констант, задающих время работы таймера. Так как запись этих констант не требует никаких предварительных установок, то эту группу команд можно разместить, например, в начале подпрограммы START или “врезать” ее между командами подготовительных операций. В данном случае, эти группы команд размещены в конце подпрограммы START.

Подпрограмма START начинается с команды clrf IntCon. Смысл этой команды заключается в следующем: вывод RB0 порта В задействован как вход. Одновременно, вывод PB0 является входом внешнего прерывания (INT). К тому же, вывод RB6, по которому организуется прерывание по изменению уровня сигнала на выводах RB4...RB7, также задействован как вход.

В случаях, когда задействованы выводы микроконтроллера, участвующие в организации прерываний, и когда хотя бы один из них, работает на вход, рекомендуется глобально запретить прерывания. Для этого достаточно установить в 0 седьмой бит регистра IntCon (глобальный запрет прерываний), но чаще всего, сбрасывается в 0 весь байт регистра IntCon, что Вы и видите в тексте программы (хотя в нашей программе и не предусмотрен уход в прерывания,). На первых порах, команду clrf IntCon можно всегда вставлять в начало программы, в которой не предусмотрен уход в прерывания (или предусмотрен, но позднее), особо не задумываясь, нужна она или нет (в целях “перестраховки”). О прерываниях разговор будет позже, а пока просто примите это к сведению.

Следующая команда: clrwdt (сброс сторожевого таймера WDT). Так как сторожевой таймер включен (в реальном микроконтроллере это устанавливается соответствующим битом  конфигурации), то его по ходу исполнения программы нужно периодически сбрасывать. Напоминаю, что сторожевой таймер представляет собой RC-одновибратор (ждущий мультивибратор) с перезапуском. Если его периодически не перезапускать (не сбрасывать), то он закончит формирование импульса, и по заднему его фронту произойдет сброс программы на начало, то есть, переход на первую команду подпрограммы START. Таким образом, если программа, по каким-то причинам, “зависнет”, то WDT выведет ее из этого состояния. В этом и заключается основной смысл его применения. Так вот, рассматриваемая сейчас команда является первой из нескольких таких же команд, “разбросанных” по тексту программы, что вполне объяснимо, так как WDT нужно периодически сбрасывать, чтобы он не закончил формирование импульса. Если программа работает нормально (не “зависает”), то WDT никогда не закончит формирование своего импульса, так как он будет периодически перезапускаться.

Ниже по тексту программы, располагается группа из шести команд. Это и есть основные команды подготовительных операций.

1. Выводы RB0 и RB6 должны работать на вход, а вывод RB2 должен работать на выход. Следовательно, речь идет о регистре TrisB.

2. Необходимо определиться с подтягивающими резисторами, предделителем, активными фронтами и тактовыми сигналами, то есть c содержимым регистра Option_REG.

Оба этих регистра (TrisB и Option_REG) расположены в 1-м банке (см. рисунок 1.1). По умолчанию, программа (начиная с первой команды подпрограммы START) начинает исполняться в  0-м банке, следовательно, прежде чем работать с содержимым регистров TrisB и Option_REG, необходимо перейти в 1-й банк, что и делается при помощи первой команды из этой группы команд: bsf Status,RP0 (установка 1-го банка). Напоминаю, что 5-му биту этого регистра, в “заголовке” программы, присвоено название RP0 (см. рисунок 1.2), и поэтому в команде можно указать не номер бита, а более удобное для восприятия текста программы, его название.

Итак, переход в 1-й банк осуществлен. Далее все очень просто: устанавливаем 0-й и 6-й биты этого регистра в 1 (работа “на вход”), а остальные в 0 (“работа на выход”). Собственно говоря, в ноль нужно установить только 2-й бит, но так как выводы RB1,3,4,5,7 не задействованы, и по этой причине, не имеет значения, как именно они будут работать, то установим их всех в 0, и дело с концом. Бинарное число, которое соответствует такой установке (0100 0001) переводим в более компактную форму. В данном случае, число переведено из бинарной системы исчисления в десятичную. Получилось .65 (вспомните, что точка перед числом является признаком десятичного числа). А можно оставить его в бинарной форме или перевести в 16-ричную. Данное число определяется (назначается) программистом, следовательно оно является константой.

Запись в регистр TrisB числа .65 происходит в 2 приема, так как за один прием записать в него константу нельзя. Сначала константа .65 записывается в аккумулятор (movlw .65), а затем копируется из аккумулятора в регистр TrisB (movwf TrisB). Итак, задействованные, в принципиальной схеме, выводы порта В, работают в соответствии с ранее сформулированными требованиями.

Переходим к содержимому регистра Option_REG (см таблицу 2.1). Сразу “выбраковываем” то, что не нужно. Так как внешние тактовые сигналы в работе устройства не используются (внешние сигналы управления не являются тактовыми) и уходов в прерывания нет, то значения 4-го, 5-го и 6-го битов регистра Option_REG могут быть любыми. То есть, они не влияют на работу устройства и поэтому установим их, например, в 0 (а можно и в 1 или в любой комбинации).

Теперь работаем с битами, которые на что-то влияют. Сначала нужно определиться: задействовать или нет предварительный делитель? Предделитель может быть включен либо перед таймером TMR0, либо после сторожевого таймера WDT. Внешних тактовых сигналов у нас нет, значит и незачем включать предделитель перед TMR0. Остается WDT.

Таблица 2.1 Регистр Option_REG

Биты

Название

Описание

7

RBPU’

Подтягивающие резисторы:

1 – отключены

0 – включены

6

INTEDG

Активный фронт сигнала на входе INT:

1 – передний фронт (0/1)

0 – задний фронт (1/0)

5

T0CS

Такт для TMR0:

1 – внешний такт с вывода RA4/T0CKI

0 – внутренний такт CLKOUT

4

T0SE

Фронт приращения TMR0 при внешнем такте:

1 – при перепаде 1/0 на выводе RA4/T0CKI

0 – при перепаде 0/1 на выводе RA4/T0CKI 

3

PSA

Предделитель:

1 – включен после сторожевого таймера WDT

0 – включен перед TMR0

2-0

PS2

PS1

PS0

Коэффициент деления предделителя:

Значение

TMR0

WDT

000

1:2

1:1

001

1:4

1:2

010

1:8

1:4

011

1:16

1:8

100

1:32

1:16

101

1:64

1:32

110

1:128

1:64

111

1:256

1:128

Давайте поподробнее с ним разберемся. WDT (сторожевой таймер) сбрасывается командой clrwdt. Строго говоря, вместо слова “сбрасывается”, нужно применять слово “перезапускается”, но так как по отношению к WDT, слово “сбрасывается” является стандартным, то его и будем применять.  

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

Если WDT работает без предделителя, то сбрасывать (перезапускать) его необходимо через промежутки времени менее чем 18 мс. (рекомендованный ориентир разработчиков микроконтроллера), т.е. достаточно часто. Это не всегда бывает удобным из-за необходимости применения относительно большого количество команд clrwdt. Если после WDT включить предделитель, то максимальный интервал времени перезапуска увеличится. Он будет кратен коэффициенту деления предделителя. Например, если предделитель с максимальным коэффициентом деления (128) подключен к WDT, то команды clrwdt нужно располагать в тексте программы через промежутки времени меньшие чем 18х128=2304 мс. (2,3 сек.). То есть, потребуется меньшее количество команд clrwdt, нежели в случае, когда предделитель не подключен к WDT. Если программа “зависнет” (перезапуски WDT прекращаются), то в данном случае, примерно через 2,3 сек. после начала “зависания”, произойдет аппаратный сброс программы на ее начало (исполнение программы начнется с подпрограммы START). “Не раздумывая долго”, применяем это на практике (можно задать и меньший Кдел. предделителя). Итак, определились: подключаем предделитель, с коэффициентом деления 128, к WDT. Для этого необходимо установить в 1 первые 4 бита (начиная с младшего) регистра  Option_REG.

Остался один 7-й бит: включение/выключение подтягивающих резисторов порта В. Если подтягивающие резисторы порта В программно включены, то они автоматически (аппаратно) отключаются от тех выводов порта В, которые работают “на выход” и остаются подключенными к тем выводам порта В, которые работают “на вход”. Таким образом, если подтягивающие резисторы программно подключены к выводам порта В, то влияние этих резисторов на внешние устройства, подключенные к выводам порта В, работающим “на выход”, можно не учитывать. А вот для внешних устройств, подключенных к выводам порта В, работающим “на вход”, это важно. Если оконечные каскады этих внешних устройств имеют “свою” нагрузку (например, в цепи коллектора или в цепи стока), то подключение подтягивающих резисторов может изменить их режим работы по постоянному току. В большинстве случаев, такие изменения незначительны (номинал резистора внутренней “подтяжки” – несколько килоом), но в некоторых случаях, могут быть “проблемы”. Если такие “проблемы” возможны, то подтягивающие резисторы порта В включать не нужно. А вот если оконечные каскады внешних устройств, подключенных к выводам порта В, работающим “на вход”, имеют открытый выход (каскады с открытым коллектором или открытым стоком, т.е. “своя” нагрузка отсутствует), то “подтяжку” можно/нужно включить. В этом случае, внутренние, подтягивающие резисторы порта В можно/нужно использовать в качестве нагрузок оконечных каскадов внешних устройств, что очень удобно, так как в этом случае, внешняя “подтяжка” не нужна (принципиальная схема устройства упрощается). То же самое относится и к внешним, механическим, коммутационным устройствам типа кнопок/тумблеров.

В нашем случае подтягивающие резисторы порта В включать не нужно, так как предполагаем, что выходы внешних устройств, подключенных к выводам RB0 и RB6, имеют “свои” нагрузки. Следовательно, 7-й бит регистра Option_REG нужно установить в 1. То есть, в регистр Option_REG нужно записать константу .143 (1000 1111). Можно использовать также и число 8Fh. Так как это число является константой, то запись производится через аккумулятор (W). Итак, подтягивающие резисторы порта В выключены, и предделитель, с коэффициентом деления 128, включен после WDT.

В 1-м банке “все дела сделаны” и делать в нем больше нечего. Теперь нужно перейти в основной (нулевой) банк: bcf Status,RP0 (выбор нулевого банка).

Продолжаем “разборку” программы. Теперь необходимо определиться с числовыми значениями тех констант, которые определяют величину интервала времени “выхода” сигнала тонального вызова в эфир. Реально, при составлении программы “с чистого листа”, подпрограмма START так и заканчивается командой bcf Status,RP0. И это естественно, так как пока неизвестно, что из себя будет представлять таймер. Только после того, как “конструкция” и принцип работы таймера будут определены и задействованные в нем регистры общего назначения, будут “прописаны”, можно рассуждать о записи в эти регистры каких-то констант.

Забегая вперед, скажу, что таймер “собран” на регистрах общего назначения SecH и SecL. После их “прописки”, а также “конструирования” на их основе таймера, группа команд записи констант (4 команды) просто-напросто “врезается” в концовку подпрограммы START (см. текст программы).

Предположим, что программа пишется “с чистого листа” и принцип работы таймера пока не известен. В этом случае, сразу же после команды bcf Status,RP0, минуя команды операций с константами таймера и расcчитывая на то, что впоследствии, эти команды будут “вставлены” в концовку подпрограммы START (так и происходит при реальном составлении текста программы), переходим к составлению подпрограммы CYCLE.

Итак, в левом столбце текста программы, введем название подпрограммы (CYCLE). Смотрим в алгоритм программы. Сначала необходимо проверить, управляющий сигнал включения на передачу есть, или его нет? Ранее мы определились, что сигналу включения на передачу соответствует 0, а выключения – 1. Этот сигнал присутствует на выводе RB0, который настроен на работу “на вход”. Следовательно, нужно опросить состояние этого вывода, и в зависимости от результата того опроса, “уйти” в один из двух возможных сценариев работы программы. Вот и типичный пример необходимости применения команды ветвления.

Теперь нужно выбрать, какую из команд ветвления применить? Для этого нужно предельно четко и ясно сформулировать алгоритм производимой логической операции. Он формулируется следующим образом:  

  1. Если нулевой бит регистра PortB (он соответствует выводу RB0) равен 1 (передача выключена), то тональный сигнал формироваться не должен, и рабочая точка программы должна где-то “закольцеваться” (напоминаю, что выполнение программы не должно останавливаться) до момента появления на выводе RB0 нулевого уровня;
  2. Если нулевой бит регистра PortB равен 0 (передача включена), то должно начаться формирование тонального сигнала (программа должна исполняться далее).

Эта формулировка хотя и достаточно конкретна, но остается некоторая неопределенность, которая заключается в том, что пока, не понятно, каким образом и куда именно необходимо перейти рабочей точке программы, для того чтобы там “закольцеваться”? Если речь идет о том, что нужно куда-то перейти, то сама собой возникает мысль о том, что этот переход можно осуществить, используя команду перехода. Вопрос: какую именно команду перехода использовать?

Анализируем. “Закольцовывать” рабочую точку программы в командах, расположенных ниже (по тексту программы) нельзя, так как ниже будут располагаться команды, формирующие сигнал тонального вызова, а в соответствии со сформулированным выше алгоритмом, при наличии на выводе RB0 единицы, сигнал тонального вызова формироваться не должен. Остается только одно “место”, это – подпрограмма START. Следовательно, нужно перейти в нее. В этом случае, рабочая точка программы будет “крутиться по кольцу”, “границы” которого: от первой команды подпрограммы START и до команды перехода в подпрограмме START.

Теперь остается только выяснить, какой из двух видов переходов применить (безусловный или по стеку)? Переход с использованием стека, применять нет смысла хотя бы потому, что для возврата по стеку требуется лишняя команда (команда возврата), да и вообще, смысла задействовать для выполнения элементарной “закольцовки” стек, нет. Вполне достаточно команды безусловного перехода goto. В данном случае (на выводе RB0 1), команда goto, на каждом “витке”, просто будет отправлять рабочую точку программы на первую команду подпрограммы START. До тех пор, пока 1 на выводе RB0 не сменится на 0. Итак, устраняем последнюю неопределенность: при наличии, на выводе RB0, единицы, должен быть осуществлен безусловный переход в ПП START.

В конечном виде, алгоритм проверки формулируется так:  

  1. если нулевой бит регистра PortB (вывод RB0) равен 1 (передача выключена), то должен быть осуществлен безусловный переход в ПП START;
  2. а если нулевой бит регистра PortB равен 0 (передача включена), то программа должна исполняться далее.

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

Смотрим в список команд. Таких команд две: btfsc и btfss. Команда btfss не подходит, так как сразу же после команды ветвления, необходимо будет устанавливать две команды переходов, устанавливать метку и вообще, неэффективно “ломать голову по поводу выхода из вилки”. А вот btfsc – в самый раз. В тексте программы, посмотрите на первые две команды подпрограммы CYCLE. Все достаточно ясно. Единственное, что можно добавить: при нулевом значении на вывод RB0 регистра PortB “программа исполняется далее”, что означает то, что следующей будет исполняться команда btfss PortB,6. Если мыслить “глобально”, т.е. не “привязываясь” к данной программе, а имея ввиду программы вообще, то на месте этой команды может оказаться любая другая команда.

Прошу обратить внимание на одну особенность команд ветвления, связанную с переходами. Суть: любая команда ветвления предполагает наличие двух сценариев дальнейшей работы программы. Если одним из этих сценариев является сценарий типа “программа исполняется далее”, то вторым сценарием, в большинстве случаев (но не всегда), является сценарий типа “переход куда-то” (в подпрограмму, на метку), причем, команда, на которую переходит рабочая точка программы по сценарию “программа исполняется далее”, должна быть второй снизу (по тексту программы), от команды ветвления, так как только в этом случае программа может “исполняться далее”. В случае наличия и на первом, и на втором “месте” (снизу от команды ветвления) команд переходов, осуществляется либо первый, либо второй переход.

Продолжаем “разборку” программы. Следующая операция – проверка включения или выключения режима тонального вызова. Ход рассуждений точно такой же, как и выше. Разница заключается в следующем. Работа происходит с битом RB6 регистра PortB. Уровни управляющего сигнала “привязаны” к другому органу управления. Переход, с целью “закольцовки” рабочей точки программы в “вечном кольце”, осуществляется в подпрограмму PRD. Для реализации алгоритма проверки, использована команда btfss.

Указанные выше 2 проверки, идущие подряд одна за другой, выполняют роль своеобразного “сита”. Результат их совместной работы таков, что для того, чтобы началось формирование сигнала тонального вызова, необходимо одновременное выполнение 2-х условий:  

  1. режим тонального вызова должен быть включен;
  2. и устройство должно быть включено на передачу.

Подобного рода “сита”, при составлении программ, применяются сплошь и рядом. Если нужно обеспечить одновременное выполнение, например, 3-х или 4-х условий, то применяются 3 или 4 проверки. В большинстве случаев, такие проверки “идут друг за другом”, но они могут быть и разнесены.

Рассмотрим поподробней подпрограмму PRD. Подпрограмма PRD нужна для того, чтобы, после 3-хсекундной “выдачи” в эфир сигнала тонального вызова, рабочая точка программы либо “закольцевалась” в этой подпрограмме (если передача включена), либо “ушла” на новый “виток” полного цикла программы (если передача выключена).

Если текст программы составляется “с чистого листа” и программист находится на теперешней стадии ее составления, то при проверке включения или выключения режима тонального вызова, в команде безусловного перехода goto, просто указывается название подпрограммы (PRD), а саму эту подпрограмму можно составить позднее. То есть, тогда, когда “до нее дойдет дело (что и будет сделано в дальнейшем).

Таким образом, работу по составлению рабочей части программы целесообразно начать с подпрограммы START, а в дальнейшем, отрабатывать рабочую часть программы последовательно. То есть, по ходу исполнения программы. При этом, можно пропускать команды, групп команд и даже целые подпрограммы, работающие с содержимым регистров, которые еще не “введены в бой” (но будут “введены” в будущем), в том числе и с переходами в подпрограммы, которые еще не созданы (но будут созданы в будущем). Можно пропускать даже несколько подпрограмм, с расчетом на их создание/проработку в будущем (приходит с опытом). В любом случае, нужно быть уверенным в том, что “стратегических ошибок” не допущено (алгоритм работы устройства капитально продуман).

Итак, мы остановились на том, что, в ходе описанных выше проверок, состояния выводов RB0 и RB6 анализируются, и в зависимости от соотношения уровней сигналов на этих выводах, рабочая точка программы либо “закольцовывается” в подпрограмме START, либо “закольцовывается” в подпрограмме PRD (в обеих случаях  “уход в вечное кольцо”, с выходом из него по внешнему воздействию), либо она "движется далее" по тексту программы (сценарий типа “программа исполняется далее”).

Первый случай был описан выше. Это самая простая "закольцовка". Во втором случае, при условии, что режим тонального вызова выключен, а передача включена, рабочая точка программы “уходит в вечное кольцо” подпрограмму PRD. После выключения с передачи, рабочая точка программы должна “прыгнуть” в подпрограмму START, в которой должна “уйти в еще одно вечное кольцо”, выход из которого происходит в момент переключения с ожидания на передачу. В третьем случае, рабочая точка программы должна продолжить свое “движение” вниз по тексту программы.

Теперь необходимо сформировать один период сигнала тонального вызова, соответствующий частоте 1450 Гц. То есть, сначала нужно сформировать первую половину периода (полупериод), а затем, вторую (форма сигнала – “меандр”). В конечном итоге, задача сводится к изменениям уровней сигнала на выводе RB2 и фиксации (задержке) этих уровней на время полупериода. Вывод RB2 работает “на выход” (см. подготовительные операции), а это означает то, что к этому выводу подключен выход защелки, которая управляется вторым битом регистра PortB. Таким образом, устанавливая бит RB2 регистра PortB в 0 или 1 и задерживая момент следующей смены состояний этого бита, можно сформировать нужный период. Зависимость простая: если бит RB2 регистра PortB установить в 0, то на выходе соответствующей защелки, а следовательно и на выводе RB2, установится 0, а если 1, то установится 1.

Напоминаю, что защелка это триггер, и если он установлен управляющим импульсом (командой, которая его формирует) в какое-то одно из двух своих состояний, то он будет находиться в этом состоянии вплоть до поступления следующего управляющего импульса (формируется другой командой). Таким образом, единожды сформировав, на выходе защелки, например, нулевой уровень, в дальнейшем (в интервале времени его фиксации), подтверждать, в каждом машинном цикле, этот уровень, не нужно. Вплоть до смены нулевого уровня на единичный. Из этого следует то, что после установки, на выводе RB2, например, того же нулевого уровня, рабочая точка программы может “скакать” по каким-то другим командам, производя при этом действия, не связанные с управлением защелками порта В. Следовательно, после смены уровней на выводе RB2, можно, на определенное время, “закольцевать” рабочую точку программы в подпрограммах задержки, и за счет этого, сформировать интервалы времени полупериодов. А значит и период.

Этим сейчас и займемся. Сначала нужно определиться, с какого уровня начинать формирование полупериода: с нулевого или с единичного? В данном случае, ничего синхронизировать не нужно. Значит, формирование периода можно начать с любого уровня. Начнем, например, с нулевого. Для того чтобы начать формирование нулевого уровня на выводе RB2, необходимо установить в ноль бит RB2 регистра PortB. Делаем это при помощи бит-ориентированной команды bcf (bcf PortB,2). Далее Вы видите три NOPа. При составлении программы с “чистото листа”, изначально, их нет. Это “элементы” точной установки (“подгонки”) интервала времени отрицательного полупериода под заданное значение. Они “врезаются” в текст программы после “прогонки” подпрограммы задержки через секундомер (об этом, позднее).

Теперь нужно “закольцевать” рабочую точку программы в подпрограмме задержки, которая ранее названа PAUSE_1. Эта подпрограмма задержки построена “по образу и подобию”, рассмотренной ранее, подпрограммы задержки программы Multi.asm. Следовательно, необходимо, в качестве счетчика, назначить регистр (регистры) общего назначения. Количество этих регистров зависит от величины времени задержки. Чем большее время задержки нужно обеспечить, тем большее количество регистров требуется. Расчет их количества и “грубое” определение величины константы/констант: Период сигнала с частотой 1450 Гц. = 689,6 мкс., а полупериод 344,8 мкс. Так как в данном случае, время отработки одного машинного цикла равно 1 мкс., то округляем величину интервала времени полупериода до 345 мкс. Именно такую величину задержки и нужно сформировать.

Полный цикл рассматриваемой подпрограммы задержки составляет 3 м.ц. (3 мкс.), плюс 2 м.ц. (2 мкс.) на последнем “витке”. Если применить конструкцию подпрограммы задержки программы Multi.asm, то одним регистром можно обеспечить задержку до 256х3-1=767 мкс. Полупериод равен 345 мкс., следовательно, достаточно одного регистра общего назначения.

Если речь идет о задержках, то уместно вспомнить о том, что не мешало бы сбросить WDT (вспомните, по ходу выполнения программы, его нужно периодически сбрасывать). Команду clrwdt можно поставить следующей, после команды bcf PortB,2 c сохранением  3-хмикросекундного, полного цикла подпрограммы задержки, а можно и “врезать” команду clrwdt в полный цикл ПП задержки, увеличив тем самым полный цикл ПП задержки c 3 мкс. до 4 мкс. (команда clrwdt выполняется за 1 м.ц.).

В обоих случаях, подпрограммы задержки будет работать, но естественно, что при формировании ими одинакового времени задержки, величины их констант не будут равны. По большому счету, выгоднее “врезать” команду clrwdt в цикл подпрограммы задержки, чем размещать ее после команды bcf PortB,2. В этом случае, сброс WDT будет происходить чаще (1 раз за 4 мкс., вместо 1 раза за 690 мкс.). В тексте программы, Вы видите именно такой вариант построения подпрограммы задержки (с “врезкой”), который я применил не столько по причине необходимости столь частого сброса WDT (такой необходимости нет), сколько в обучающих целях. Дело в том, что “врезая” в нее другие команды аналогично указанной выше “врезки”), время отработки цикла подпрограммы задержки можно сделать гораздо большим, чем 3 м.ц. В этом случае, и производится полезное действие, и увеличивается время отработки цикла подпрограммы задержки.

А ведь вместо одной команды можно “врезать” и несколько. С прибавлением каждой такой команды, время отработки цикла подпрограммы задержки будет увеличиваться. И чем “массивнее врезка”, тем большим будет это увеличение. Если в интервале времени отработки подпрограммы задержки, не нужно производить никаких действий (нужно просто увеличить время отработки подпрограммы задержки), то “врезается” NOP (NOPы). Таким же образом, в подпрограмму задержки, можно “врезать” группу команд, производящих действия, подпрограмму (в том числе и циклическую), группу подпрограмм, а также и все это в комплексе и в любой комбинации.

Для того чтобы “закрыть” эту тему, приведу практический пример: Предположим, что используется классическая конструкция подпрограммы задержки (как в программе Multi.asm), обеспечивающая максимальное время задержки 767 мкс., а нужно сформировать задержку в 1000 мкс. Можно задействовать второй регистр общего назначения, а можно обойтись и одним, “встроив” в подпрограмму задержки всего один NOP. В этом случае, полный цикл подпрограммы задержки увеличивается с 3-х до 4-х м.ц., и при помощи такой подпрограммы задержки можно сформировать задержки до 256х4-1=1023 м.ц. (1023 мкс.) Задержка в 1000 мкс. попадает в этот интервал, следовательно, можно обойтись одним регистром общего назначения.

Если в подпрограмму задержки “врезается” группа команд, то с целью обеспечения “правильной закольцовки”, название подпрограммы задержки должно приходиться на первую команду этой группы команд, а после последней команды этой группы, должна следовать команда ветвления.

Двигаемся дальше. Ранее было выяснено, что для того чтобы обеспечить задержку в 345 мкс., достаточно одного регистра общего назначения (даже без “врезки”). Если, при 4-х машинных циклах полного цикла подпрограммы PAUSE_1 (с “врезкой” clrwdt), обеспечивается максимальная задержка в 1023 мкс. (что соответствует максимальному значению константы .255), то значению времени задержки 345 мкс. будет соответствовать значение константы 345х255:1023=85,99... Округляем до 86-ти. При написании программы “с чистого листа”, значение этой константы (.86) и нужно “прописать” в тексте программы.

В тексте программы, Вы видите другое значение константы (.85) и три NOPа. Такого рода числовая коррекция произойдет позднее, при отладке временных характеристик программы в симуляторе, а пока используем результат “грубой прикидки” (.86).

Теперь остается только придумать название этого регистра (назовем его Sec) и “прописать” его в “заголовке” программы (“прописываем” его по адресу 0Ch, но можно назначить и другой). Вот вам и ответ на вопрос: “Откуда, в “заголовке” программы, взялся регистр Sec и каков механизм его возникновения”.

После этого, константа .86, обычным образом (за 2 приема, через регистр W), записывается в “новорожденный” регистр Sec. Эту запись нужно произвести до первой команды подпрограммы (PAUSE_1 или PAUSE_2), в которой используется этот регистр. Далее располагаются 3 команды подпрограммы PAUSE_1.

Идем дальше. Теперь необходимо сформировать положительный полупериод. По своей конструкции, подпрограмма PAUSE_2 такая же, как и подпрограмма PAUSE_1, только, перед ее началом, в бит RB2 регистра PortB, записывается 1, и безусловный переход осуществляется на начало подпрограммы PAUSE_2.

Регистр Sec уже имеется в наличии, так что ничего “назначать и прописывать” не нужно. На момент перехода рабочей точки программы на команду bsf PortB,2 , в регистре Sec будет “лежать” ноль (конечный результат декремента содержимого регистра Sec в ПП PAUSE_1). Поэтому, перед “влётом” в подпрограмму PAUSE_2, в регистр Sec, нужно записать “новую” константу. Она будет определять продолжительность положительного полупериода. При написании программы “с чистого листа”, в качестве этой константы, с расчетом на осуществление дальнейшей коррекции числового значения константы, можно использовать всё то же число .86. Изначально, NOPов также нет. Все это оставляется “на потом”. Почему, в тексте программы, Вы видите число .83, узнаете позже.

Примечание: классическая подпрограмма задержки (такая, как в программе Multi.asm), и она же, но с “врезкой” (такая, как в нашей программе), по своей сути, есть вычитающий счетчик импульсов, то есть, абсолютно необходимое, в цифровой технике, устройство. Восприятию этого факта мешает то, что импульсов, которые нужно считать, как-будто бы и нет, а есть команды, что на первый взгляд, не одно и то же. Но на самом деле, импульсы есть, и считаются именно они, так как результатом исполнения команды (например, такой как decfsz) является активный перепад (строб), аппаратно формируемый внутри микроконтроллера (его прохождение нельзя проконтролировать с помощью приборов), который и уменьшает (или увеличивает, если применяется команда incfsz) на единицу содержимое регистра-счетчика (в данном случае, Sec).

Также следует иметь ввиду, что в части касающейся м/контроллеров, счетчик реализуется не одними только аппаратными средствами (например, как в 555ИЕ2), но и программными средствами (в комплексе). И в самом деле, для того чтобы создать счетчик, необходимо не только задействовать (назначить), в качестве счетчика, регистр/регистры общего назначения (все регистры области оперативной памяти реализованы аппаратно), но и “встроить” его/их в циклическую подпрограмму задержки, в состав которой должны входить байт-ориентированные команды ветвления incfsz и/или decfsz (они управляют регистром/регистрами). В первом случае, получается суммирующий, а во втором случае, вычитающий счетчик.

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

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

Подпрограмма задержки не должна отрабатываться все время. Если такое произойдет, то рабочая точка программы просто “зависнет” в этой подпрограмме. Счетчик должен считать, от предварительно записанного в него числа (так называемая предустановка), до момента установки в нем нуля (“очищение”), после чего рабочая точка программы должна выйти из подпрограммы задержки по сценарию “программа исполняется далее”. Даже в случае “ухода” рабочей точки программы в “вечное кольцо” (специфическая разновидность счетчика без предустановки. “Ловушка рабочей точки”), она все-равно рано или поздно из него выходит. По внешнему воздействию (например, после нажатия кнопки).

Понятия “счетчик” и “задержка” являются как бы синонимами, которые нельзя отделить друг от друга. И в самом деле, счетчик, при условии, что он когда-то “остановится”, всегда обеспечивает какую-то задержку, а задержка какого-то процесса на время, кратное машинному циклу, предполагает их подсчет, то есть, применение счетчика.

Таким образом, подпрограммы PAUSE_1 и PAUSE_2 можно описать так: циклическая подпрограмма задержки с “врезкой” из одной команды, на основе однобайтного, вычитающего счетчика, с предустановкой и выходом из полного цикла подпрограммы после его очистки (после окончания счета).

Если “привязаться к понятию “закольцовка”, то в части касающейся стандартных подпрограмм задержек, можно сказать так: “закольцовка” рабочей точки программы в подпрограммы задержки, создает задержку выполнения следующей (после подпрограммы) команды. Суть “закольцовки” – многократная отработка цикла подпрограммы задержки, вплоть до очищения регистра  общего назначения, выполняющего функцию счетчика. В этом случае, речь идет о калиброванном времени задержки. Пример задержек: подпрограммы PAUSE_1 и PAUSE_2. Если речь идет о “вечном кольце”, то такие “закольцовки” есть в подпрограммах START и PRD.

Обращаю Ваше внимание на следующее: такого рода общие рассуждения следует рассматривать не как мешающее отвлечение от "разборок" с текстом программы, а как насущную необходимость, связанную с капитальным осознанием смысла того, о чем идет речь. Прошу отнестись к этим общим рассуждениям/выводам со всей серьезностью. Если Вы прислушаетесь к этому доброму совету, то в дальнейшем, у Вас не будет проблем с пониманием текстов следующих программ, так как при описании их работы, я буду исходить из того, что Вы усвоили предыдущую информацию.

А теперь возвращаемся к дальнейшим “разборкам” с нашей программой. Мы остановились на том, что сформировали оба полупериода (то есть, один период) тонального сигнала вызова. Естественно, что этого маловато. Нужно последовательно сформировать несколько тысяч таких периодов, и причем так, чтобы обеспечить заданную скважность (“меандр”. Полупериоды равны друг другу).

Так как интервал времени “выдачи” сигнала тонального вызова в эфир вовсе не обязательно в точности делать равным 3 секундам, то задачу можно сформулировать так: сигнал тонального вызова должен “выдаваться” в эфир в течение приблизительно 3-х секунд. Не трудно догадаться, что после окончания формирования одного периода, нужно сразу же начать формирование следующего периода, и т.д. До тех пор, пока не сформируется трехсекундный интервал времени “выдачи” сигнала тонального вызова в эфир.

Теперь переходим к устранению всех неопределенностей. Предположим, что мы работаем “с чистого листа”. На данный момент составления текста программы, известно, что при переходе на передачу (на выводе RB0 1 меняется на 0), должен быть запущен некий счетчик времени (таймер), который отмеряет приблизительно 3 секунды. По окончании формирования этого интервала времени, должно быть выполнено следующее:

  1. если устройство включено на передачу, то рабочая точка программы должна “уйти в    вечное кольцо” подпрограммы PRD (ее еще нет, мы просто дали ей название) и выйти из него при переключении с передачи на ожидание (переход на “новый”, полный цикл программы);
  2. если устройство включено на ожидание, то рабочая точка программы сразу же должна уйти в    “вечное кольцо” подпрограммы START и выйти из него при переключении на передачу.

Детализируем. Так как сначала нужно сформировать трехсекундный интервал времени “выдачи” сигнала тонального вызова в эфир, а только после этого производить “уход в вечное кольцо”, то группа команд, производящих операции с таймером, должна быть расположена в тексте программы сразу же после последней команды подпрограммы PAUSE_2, а группа команд подпрограммы PRD должна следовать сразу же после последней команды группы команд, производящих операции с таймером.

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

Так как необходимо сформировать фиксированный интервал времени и после этого “уйти” в сценарий “программа исполняется далее” (перейти в подпрограмму PRD), то очевидно, что счетчик можно построить по подобию рассмотренного выше счетчика. Такой счетчик, построенный на основе двух регистров SecH и SecL, представлен группой команд, следующих за командой goto PAUSE_2. Назначение и содержание регистров SecH и SecL будет рассмотрено немного позже, а сейчас рассмотрим некоторые проблемы, связанные с осуществлением безусловный перехода (goto) в этой группе команд.

Считать необходимо количество периодов, следовательно, для организации “закольцовки” счетчика, формально, можно было бы перейти на ту команду, с которой начинается формирование периода, т.е. на команду bcf PortB,2, размещенный немного ранее подпрограммы PAUSE_1 (“пометив” ее определенной меткой). В этом случае трехсекундный интервал сформируется полностью, и только после этого рабочая точка программы “уйдет в вечное кольцо” подпрограммы START, но будет исключена возможность проверки состояния второй кнопки, определяющей включение устройства на передачу.

В программе реализован более выгодный вариант организации “закольцовки” счетчика с помощью осуществления безусловного перехода в подпрограмму CYCLE. В этом случае, задействуются команды обеих проверок, которые рассматривались выше. Этот вариант выгоден тем, что если выключение с передачи происходит во время формирования сигнала тонального вызова (во время 3-секундного интервала времени), то в момент перехода с передачи на ожидание, формирование сигнала тонального вызова тут же прекращается.

Теперь можно сформировать трехсекундный интервал времени. Период тонального сигнала равен 690 мкс. (см. выше). Таким образом, необходим счетчик, считающий, как минимум, до 3000000:690=4347,826. Округляем до 4348.

“Грубая прикидка”: при применении стандартной подпрограммы задержки (полный цикл подпрограммы – 3 м.ц.), значение константы, записываемой в назначенный, в качестве счетчика, регистр общего назначения, должно быть 4348:3= приблизительно 1450. Максимальное значение константы, которую можно “заложить” в один регистр, равно .255. На одном регистре общего назначения, при использовании хитроумной “врезки” типа дополнительной подпрограммы задержки, собрать такой счетчик, конечно же, можно, но это не самый лучший выход из положения (потребуется много команд). Гораздо удобнее и проще применить двухбайтный счетчик (не путать с двумя однобайтными счетчиками!). Такой счетчик считает до 256х256=65536 и, с его помощью, можно сформировать (применительно к нашему случаю) не только трехсекундный интервал времени, но и гораздо больший.

Если речь идет о нескольких байтах, то нужно определить порядок их старшинства. Старший байт пометим буквой H, а младший, буквой L. Такого рода пометки – стандарт, хотя можно и придумать что-нибудь свое. Если счетчик трехбайтный, то добавляется буква M – средний байт (порядок старшинства: H, M, L), а если четырехбайтный, то добавляются буквы HH – “старший старшего” (порядок старшинства: HH, H, M, L).

Под это дело, в “заголовке” программы, “прописываем” регистры общего назначения с названиями SecH (в нем “лежит” старший байт) и SecL (в нем “лежит” младший байт) и назначаем им адреса в области оперативной памяти (0Dh и 0Eh соответственно). Вот Вам и ответ на вопрос: откуда в “заголовке” программы взялись регистры SecH и SecL?

Давайте разберемся с 2-байтным счетчиком (что это такое и откуда что взялось?). Стандартный принцип организации работы 2-байтного вычитающего счетчика:  

  1. сначала декрементируется содержимое регистра младшего байта;
  2. а после его очищения (в счетчике - ноль), декрементируется содержимое регистра старшего байта.

Декремент счетчика младшего разряда будет происходить каждый раз после формирования периода тонального сигнала, а декремент счетчика старшего разряда будет происходить через каждые 256 периодов тонального сигнала, в момент смены, в младшем разряде счетчика, числа .255 на число .0

Сигналом окончания формирования трехсекундного интервала времени является очищение счетчика старшего разряда, после чего рабочая точка программы должна “уйти” в сценарий “программа исполняется далее”. По своей сути, этот “механизм” ничем не отличается от “механизма” работы счетчика, собранного, например, на микросхемах счетчиков (...ИЕ...) и тот, кто с ними работал, без особого труда поймет, о чем идет речь.

В регистры SecH и SecL, нужно записать какие-то константы. Нужно определиться с их числовыми значениями. Для того чтобы “отмерить” 3 сек., 2-хразрядный счетчик должен посчитать 4348 периодов (см. выше). Счетчик младшего разряда декрементируется каждый период. Если установить в нем константу, например, .255, то в процессе последовательного декрементирования, после установки числа .0, произойдет один декремент содержимого счетчика старшего разряда. Затем, в счетчике младшего разряда, происходит переход от .0 к .255 и все повторяется снова. И так происходит много раз (счет “по кольцу”). Пока не обнулится счетчик старшего разряда. Таким образом, при прохождении 4348 периодов, должно произойти примерно 17 декрементов содержимого счетчика старшего разряда. Следовательно, для того чтобы очистить счетчик старшего разряда примерно за 3 сек., необходимо записать, в регистр SecH, константу .17. В данном случае, в счетчик младшего разряда записывается максимально возможное значение константы (.255), которое, по этой причине, условно можно приравнять единице значения константы старшего разряда. Таким образом, в счетчик старшего разряда необходимо заложить “прикидочную” (“грубую”) константу .17 - .1 = .16

Обращаю Ваше внимание на следующее. Между первой командой ПП CYCLE и командой decfsz SecL,F располагаются не только отдельные команды, но и целых две циклические подпрограммы. Подпрограмма CYCLE классифицируется как циклическая подпрограмма задержки, с “массивной врезкой”, включающей в себя две циклические подпрограммы задержки PAUSE_1 и PAUSE_2. Вот Вам и наглядная иллюстрация того, о чем я говорил ранее: в состав “врезки” могут входить не только отдельные команды, но и целые циклические подпрограммы. В циклическую подпрограмму задержки, образно выражаясь, можно “врезать все что угодно”. Главное при этом то, чтобы после этого, подпрограмма задержки не “потеряла свою жизнеспособность” и обеспечивала нужное время задержки.

Если говорить конкретно о циклической подпрограмме CYCLE, то можно сделать следующий вывод: “врезка” подпрограммы CYCLE работает по заданному разработчиком алгоритму и обеспечивает полный цикл этой подпрограммы, равный периоду сигнала с частотой 1450 Гц. (690 мкс.).

2-байтный счетчик отсчитывает число периодов, “помещающихся” в трехсекундном интервале времени, и по его окончании, осуществляется переход в сценарий “программа исполняется далее”. Остается только детально разобраться с константами трехсекундного счетчика (таймера).

“Привяжемся" к тем значениям констант, которые были указаны выше, т.е. .255 – в младшем байте счетчика и .16 – в старшем. С учетом того, что “прикидка” производилась “грубо”, и в результате этого, реальное время “выхода” в эфир сигнала тонального вызова несколько больше, чем расчетное, на единицу уменьшим числовое значение константы счетчика старшего разряда и сделаем ее равной .15. В этом случае сигнал тонального вызова будет “выдаваться” в эфир в течение времени немного меньшего, чем 3 сек. На фоне слова приблизительно, это приемлемо.

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

Итак, “окончательно и бесповоротно” назначаем: константа старшего разряда счетчика – .15, константа младшего разряда счетчика – .255. Теперь возникает вопрос: “В какое место текста программы нужно врезать группу команд записи констант”? Ответ на этот вопрос был дан ранее, и поэтому “врезаю” эту группу команд в концовку подпрограммы START. Вот Вам и ответ на вопрос: “Откуда взялась, в концовке подпрограммы START, группа команд записи констант и почему она выглядит именно так”?

Декремент содержимого 2-байтного счетчика производится при наличии на выходе устройства единичного уровня, так как рабочая точка программы “заходит в этот счетчик” во время формирования положительного полупериода. Если результат декремента не равен нулю, то происходит переход в подпрограмму CYCLE  (goto CYCLE) и все повторяется снова и снова, до тех пор, пока, с момента включения на передачу, не пройдет примерно 3 сек.

Посмотрите в текст программы. После 4-х команд 2-хразрядного счетчика, Вы видите команду bcf PortB,2. Именно на эту команду переходит рабочая точка программы после того, как счетчик старшего разряда очистится, то есть, завершится формирование 3-секундного интервала времени “выхода” тонального сигнала в эфир. Этой командой осуществляется смена единичного уровня на выходе устройства на нулевой. Это сделано для того, чтобы после очищения счетчика старшего разряда, закончить формирование положительного полупериода.

Далее, рабочая точка программы “влетает” в подпрограмму PRD, которую сейчас предстоит создать. Напоминаю, что она должна делать. В этой подпрограмме рабочая точка программы должна уйти в “вечное кольцо” и выйти из него в момент переключения с передачи на ожидание, с последующим переходом на новый цикл программы.

“Механизм” функционирования “вечного кольца” рассмотрен ранее. И в подпрограмме PRD он точно такой же. Разница только в деталях: так как “закольцовка” происходит внутри подпрограммы PRD, то команда безусловного перехода должна обращаться к подпрограмме PRD, и при опросе состояния вывода RB0, вместо команды btfsc, используется команда btfss (если применить btfsc, то будет "вилка"). Если речь идет о длительной “закольцовке” (что и имеет место быть), то обязательно нужно периодически сбрасывать WDT.

В “вечном кольце” подпрограммы START, это делалось с помощью команды clrwdt, расположенной в группе команд подготовительных операций. Примерно так же нужно поступить и по отношению к подпрограммы PRD. То есть, нужно “врезать” команду clrwdt в цикл “вечного кольца” этой подпрограммы. Необходимость применения команды clrwdt обусловлена тем, что время включения на передачу может быть большим, чем 2,3 сек. Первые 3 команды подпрограммы PRD можно классифицировать так: циклическая подпрограмма задержки, с “врезкой” из одной команды, с “уходом в вечное кольцо” и с выходом из него по внешнему управляющему сигналу. Для сценария типа “закольцовка”, цикл такой подпрограммы равен 3 м.ц.+1 м.ц. команды clrwdt = 4 м.ц. и поэтому срабатывания WDT опасаться не нужно.

Итак, мы дошли до конца полного цикла программы. Остается только вставить в текст программы команду безусловного перехода на “новый”, полный цикл программы (goto START), чем и обеспечивается соблюдение “глобального” принципа: рабочая точка программы всегда должна находиться в движении.

Текст программы всегда заканчивается директивой end для того, чтобы Ассемблер “знал”, что ниже этой директивы, текст программы “искать” не нужно.


 

А также другие работы, которые могут Вас заинтересовать

68078. Schulleben. Der erste Schultag 29.5 KB
  Обговорення прислів’я «Wie der Anfang so das Ende» та прислів’я «Guter Anfang ist halbe Arbeit». Was bedeutet dieses Sprichwort? Könnt ihr ein ukrainisches Äquivalent geben? Seid ihr mit dieses Sprichwort einverstanden?
68079. В гості до товариша 48.5 KB
  Любиш у гостях бувати люби й гостей приймати. Теми для обговорення: а У Оксани день народження Діти накривають святковий стіл запрошують гостей б У гості з подарунком Що дарувати і як приймати подарунки в У передпокої Як господар вітає і зустрічає гостей г Скупий і непривітний хазяїн Господар сідає...
68080. Kleidung und Mode (Одяг і мода) 47.5 KB
  Wir begrüßen Sie hier in diesem Saal. Heute haben wir einen ungewöhnlichen Abend zum Thema «Kleidung und Mode». In den Deutschstunden haben wir viel über die Kleidung gelesen, gesprochen, und verschiedene Übungen gemacht. Mode ist ein Teil unseres Lebens. Es gibt Mode auf alles: auf Kleidung, auf Handys, auf Musik usw.
68081. Методична розробка «З Перемогою!» 89 KB
  Цілі: Знайомство з героїчними сторінками історії нашої країни. Формування уявлень про військовий обов,язок і вірність Батьківщині, формування досвіду моральної поведінки особистості, спонукання інтересу до історії своєї країни. Підвищення інформаційної культури учнів
68083. Планування дій. Алгоритм 473.5 KB
  Навчальна: Розкрити зміст поняття алгоритм. Формувати в учнів уміння складати алгоритм здійснення того чи іншого процесу. Формувати в учнів уміння передбачати певний результат. Розвивальна: Розвивати вміння узагальнювати. Розвивати вміння працювати колективно.
68085. Прогулянка до лісу. Диференціація звуків л-р-л’-р’ 35.5 KB
  Мета: вчити дітей розрізняти звуки (л-р-л′-р′), автоматизувати ці звуки у зв’язному мовленні; розвивати у дітей слухову увагу, пам'ять; розвивати дрібну моторику; розвивати міміку дітей; удосконалювати мовну моторику, фонематичний слух, фонематичне сприймання, фонематичний аналіз та синтез.