Главная | |
Материал взят из книги![]() Скачать оригинал КНИГИ в хорошем качествеПосле инициализации системы программа позиционера переходит к основному циклу программы. Назначение и обобщенный алгоритм работы этого цикла был описан ранее В графическом виде цикл показан на рис. 46. Фрагмент №5 программы представляет собой полный текст основного цикла. Это наверно самый короткий из всех представленных в книге фрагментов. Связано это с тем, что все функции основного цикла реализованы в виде подпрограмм. Само же тело основного цикла состоит всего лишь из нескольких команд вызова подпрограмм. В данном случае применение подпрограмм не ведет к сокращению кода. Каждая их этих подпрограмм используется только один раз. Однако подобный способ оформления программы существенно облегчает чтение и модернизацию готовой программы. Программы, написанные в таком стиле, на- ![]() Как мы видели из рис. 46, различается большой и малый циклы. Для переключения между этими двумя режимами используется команда условного перехода jnb. Эта команда производит проверку значения ячейки битовой памяти, адрес которой указан в качестве ее параметра. Передача управления происходит в том случае, когда проверяемый бит равен нулю. В качестве проверяемого бита выступает флаг flonoff. Как уже говорилось ранее, значение флага flonoff определяется режимом работы позиционера. В рабочем режиме значение флага равно единице, а в дежурном режиме флаг равен нулю. Если значение флага равно нулю, управление передается по адресу mal. Это соответствует малому циклу. Если флаг установлен в единичное состояние, выполняется большой цикл. В большом цикле последовательно вызываются следующие процедуры: зываются структурированными. Создание структурированных программ считается хорошим стилем программирования. • процедура ввода команд с клавиатуры; • процедура обслуживания счетчика положения антенны; • процедура автоустановки антенны и • процедура обработки команд с пульта ДУ. В последующих разделах мы подробно рассмотрим каждую их этих процедур. Прежде, чем завершить основной цикл и перейти к его началу, программа выполняет проверку флага ошибки флэш-памяти. Многие из описанных выше процедур используют подпрограммы чтения или записи флэш-памяти. Если при выполнении этих процедур возникает ошибка, то процедура устанавливает значение флага flerf в единицу. В конце основного цикла происходит проверка этого флага. Если ошибок не было, то оператор условного перехода jnb выполнит переход на начало цикла основной программы и весь цикл замкнется. В случае ошибки, перехода не произойдет. Программа выполнит команды индикации Error. Для этого она запишет в ячейки буфера экрана (bufh и bufl) коды, соответствующие буквам «Е» и «г». На экране появится надпись «Ег». Затем флаг flerf сбрасывается, разрешая нормальную работу системы в случае устранения ошибки. Ошибка флэш-памяти может быть связана с отказом микросхемы, плохим контактом, замыканиями в цепи подключения и некоторыми другими. Команда безусловного перехода возвращает управление к началу цикла. В дежурном режиме выполняется малый цикл. Он начинается с адреса, соответствующего метке mal. Первая команда этого цикла записывает в буфер управления мотора (bufmot) код ООН. Это команда остановки мотора. Затем в оба буфера (для первого и второго разрядов) записывается код выключенного состояния. Оба разряда выключаются. Команда clr pl.l переводит вывод 13 микроконтроллера в нулевое состояние. Это приводит к зажиганию индикатора дежурного режима (см. рис. 44). Следующий этап — перевод процессора в режим останова. Это выполняется путем записи в управляющий регистр PCON кода 01Н. Выполнение основной программы в этом месте останавливается. Процессор переходит в режим пониженной мощности. Работают только процедуры прерываний периодически вызываемые системными таймерами. Одна из этих процедур ждет нажатия кнопки «Вкл/Выкл» на передней панели позиционера, а вторая ждет прихода от пульта ДУ команды на включение. Получив одну из этих команд, программа записывает в регистр PCON код ОН. Это выводит систему из «спящего» режима. Подпрограмма обработки команд с клавиатуры Теперь разберем по порядку все подпрограммы, входящие в основной цикл. И начнем мы с подпрограммы обработки команд с клавиатуры. Как уже говорилось, данная подпрограмма предназначена для обработки кода состояния клавиатуры, записанного в буфер клавиатуры подпрограммой, входящей в процедуру динамической индикации. Рассматриваемая же подпрограмма производит распознавание команд и запуск процедур, реализующих эти команды. Рассмотрим подробнее саму задачу логической обработки команд клавиатуры. На первый взгляд никаких трудностей нет. Обнаружил нажатие кнопки — переходи к выполнению соответствующей операции. На самом деле не все так просто. Скорость работы любого микропроцессорного устройства намного больше, чем скорость, с которой человек может нажимать кнопки на клавиатуре. С точки зрения программы нажатие любой из кнопок клавиатуры — эго очень длительный процесс. Пока человек совершает даже кратковременное нажатие кнопки, программа успевает пройти по основному циклу программы несколько тысяч раз. Как следует из блок-схемы на рис. 46, один раз за каждый проход основного цикла выполняется наша подпрограмма анализа команд с клавиатуры. Если каждый раз, когда программа обнаружит код, соответствующий нажатию одной из клавиш, она будет выполнять закрепленную за ней процедуру, то мы получим многократные повторы. Представьте, что вы нажали кнопку увеличения номера выбранного канала, а он вместо того, чтобы увеличиться на единицу, начнет в бешеном темпе увеличивать номер все время, пока вы держите кнопку. Очевидно, что выполнять процедуру, закрепленную за нажатой кнопкой, нужно только один раз при каждом ее нажатии. Существует два способа решения этой задачи. Можно выполнять процедуру либо в момент нажатия кнопки (по переднему фронту импульса), или сразу после отпускания (по заднему фронту). В случае позиционера большинство команд удобнее выполнять по переднему фронту. Но имеется два исключения. Первое исключение — это кнопка записи/стирания канала (Б4). При кратковременном ее нажатии происходит включение режима записи текущей позиции антенны в один из каналов позиционера. При нажатии той же кнопки и удержании ее в течение 2 секунд, включается режим стирания выбранного канала. Для реализации такого двойного назначения кнопки, придется сначала определить длительность удержания кнопки в нажатом состоянии и лишь затем, выполнять одно из вышеописанных действий. В связи с этим обрабатывать команду от кнопки Б4 можно только после ее отпускания. Второе исключение — это кнопки ручной установки антенны (Б5 и Б6). Для поворота антенны нужно нажать одну из этих кнопок и удерживать ее. Пока кнопка удерживается, двигатель поворотного устройства вращается, и антенна поворачивается в выбранном направлении. Кнопка Б5 вызывает поворот на запад, а кнопка Б6 — на восток (реверс). Обрабатывать нажатие этих кнопок придется как по переднему фронту, так и по заднему. По переднему фронту вызывается процедура включения двигателя поворота антенны, а по заднему — процедура остановки двигателя. Есть еще один момент, который нужно учитывать при разработке подпрограммы обработки сигналов с клавиатуры. Порядок работы с клавиатурой предусматривает правило: каждая команда вводится при нажатии только одной соответствующей кнопки. При этом остальные кнопки не должны быть нажаты. Если на клавиатуре нажаты одновременно несколько кнопок, никакие действия не должны выполняться. Исключение составляет команда полного сброса системы. Она запускается путем одновременного нажатия четырех кнопок. Все перечисленное выше — это типичные требования, возникающие при разработке любой клавиатуры. Для решения эгих задач существуют типовые приемы. Ниже приведено описание алгоритма обработки команд с клавиатуры, способного удовлетворить всем вышеперечисленным условиям. ПЕРВЫЙ ЭТАП Любые действия по обработке команд с клавиатуры должны выполняться только в момент изменения ее состояния. Для этого необходимо запоминать предыдущее состояние клавиатуры. При каждом последующем обращении к нашей подпрограмме, компьютер должен сравнивать текущий код состояния клавиатуры с кодом, считанным во время предыдущего обращения. Если код состояния не изменился, то подпрограмма на этом должна завершиться. И только в том случае, когда при очередном обращении будет обнаружена смена состояния клавиатуры, программа должна продолжить анализ и перейти ко второму этапу. ВТОРОЙ ЭТАП На втором этапе программа определяет то, к какому из трех возможных типов относится данный случай изменения состояния клавиатуры. Вариантов может быть множество, но два из них имеют особое значение. Поэтому из всех вариантов мы выделяем три: Вариант А. После того, как все кнопки отпущены, нажимается одна из кнопок. Вариант В. После того, как нажата только одна кнопка, она отпускается, и все кнопки оказываются отпущенными. Вариант С. Все остальные случаи, когда, либо до изменения состояния клавиатуры, либо после него, сразу несколько кнопок нажаты одновременно. ■ оамиучи 1СЛ0 1 1и 1У1И^иМ|2иЦсиии[2Пи1'1 I САПИГЧС Понятно, что последний вариант мы должны исключить. При возникновении такой ситуации подпрограмма должна немедленно завершаться. В дальнейшем нас будут интересовать только варианты А и В. Проверка на этапе номер 2 производится так же, как и на первом этапе, путем оценки значений нового и старого кодов клавиатуры. Критерии оценки просты. В данном случае нам пригодится код состояния клавиатуры в момент, когда все кнопки отпущены (ккпо!). Если код предыдущего состояния клавиатуры оказался равен ккпо!, то, не зависимо от значения нового кода, мы имеем дело с вариантом А по нашей классификации. Если, наоборот, новый код состояния равен ккпо!, то, не зависимо от значения старого кода, мы получим вариант В. Дальнейшая работа происходит по одному из этих вариантов. ТРЕТИЙ ЭТАП На этом этапе программа представляет собой цепочку операторов сравнения. Код состояния клавиатуры должен поочередно сравниваться с кодом каждой кнопки. Причем при работе по варианту А в сравнении участвуют только те кнопки, которые должны срабатывать по переднему фронту. При работе по варианту В должны участвовать кнопки, срабатывающие по заднему фронту. В нашем случае по заднему фронту работает только одна кнопка: это кнопка Б4. Проверка кодов на этом этапе происходит примерно следующим образом: ■ Сравниваем код состояния клавиатуры с кодом кнопки Б К ■ Если эти коды равны — выполняем процедуру переключения режимов «работа/установка» и выходим из подпрограммы оценки кодов. ■ Если коды не равны, то проверка продолжается. Код состояния сравнивается с кодом кнопки 5>2. Если коды равны, выполняем процедуру уменьшения номера канала на единицу. ■ Повторяем вышеуказанные действия для всех кнопок, участвующих в проверке. И так, пока не будут проверены все кнопки клавиатуры. Если в результате всех этих проверок окажется, что код состояния клавиатуры не равен ни одному из возможных кодов кнопок, то это значит, что в данный момент нажато одновременно несколько кнопок. В этом случае подпрограмма также не выполняет никаких действий. Примечание. В реальной программе позиционера порядок проверки кнопок немного другой. Проверка начинается вовсе не с первой кнопки. Но это не имеет принципиального значения. Ниже приведен фрагмент №6 программы позиционера, реализующий вышеописанный алгоритм. ![]() Если вы просмотрите предлагаемый фрагмент до конца, то вы увидите, что он заканчивается командой ret. Как мы уже знаем, ret — это команда выхода из подпрограммы. Это говорит о том, что Фрагмент №6 представляет собой подпрограмму. При обнаружении моментов нажатия клавиш выполняется переход к одной из процедур обработки этого события. Переход к этим процедурам производится при помощи команды jmp, то есть команды безусловного перехода. Поэтому каждая из таких процедур является продолжением приведенной здесь подпрограммы. В конце каждой из этих процедур также стоит оператор ret. В результате подпрограмма обработки команд с клавиатуры превращается в некое подобие спрута. Одна подпрограмма имеет несколько окончаний. Каждое из «щупалец» этого спрута может служить в определенный момент выходом из подпрограммы. Первые три строки Фрагмента №6 предназначены для запуска процедуры стирания текущего канала. Как уже упоминалось, процедура стирания запускается при нажатии и удержании в течение нескольких секунд кнопки S4 (запись/стирание канала). Для отработки этого временного интервала организован программный счетчик. Мы уже упоминали об этом счетчике. Программный счетчик — это просто ячейка памяти, содержимое которой периодически уменьшается (или увеличивается) программным путем. В данном случае используется ячейка памяти coucl. Для того, чтобы счетчик считал, в процедуру динамической индикации включена команда, уменьшающая каждый раз при вызове этой процедуры содержимое ячейки coucl на единицу. Процедура динамической индикации вызывается с постоянным периодом (примерно 50 Гц). Этим достигается синхронизация счетчика во времени. В той же процедуре динамической индикации организован программный запуск и остановка этого счетчика. Запускается счетчик при нажатии кнопки S4 и считает все время, пока эта кнопка нажата. Если кнопка будет отпущена раньше, чем счетчик досчитает до нуля, то счет остановится. При новом нажатии кнопки счетчик перезапустится, и счет начнется сначала. Подробное описание всей программы динамической индикации будет приведено далее. Сейчас же вернемся к фрагменту № 6. Три первые команды этого фрагмента, выполняют последний этап обработки — проверку содержимого счетчика на равенство нулю. Если в ячейке счетчика ноль, то это значит, что клавиша S4 удерживалась в нажатом состоянии уже достаточно долго. Временной лимит истек и пора включать процедуру стирания информации из текущей ячейки памяти. Рассмотрим, как это происходит. Первая команда (mov) копирует содержимое ячейки coucl (значение счетчика) в аккумулятор. Далее идет новая для нас команда cjne. Это команда сравнения двух чисел. Она относится к командам условного перехода. Команда сравнивает содержимое аккумулятора и числовой константы, выступающей в качестве ее второго параметра. В нашем случае константа равна нулю. Если содержимое аккумулятора не равно нулю, команда передает управление по адресу ink. Адрес — это продолжение текущей подпрограммы. Если же содержимое аккумулятора равно нулю (счетчик досчитал до конца), то условного перехода не происходит и выполняется очередная команда, а именно команда безусловного перехода к процедуре стирания информации выбранного канала, начало которой находится по адресу cleank. Мы опустим подробное описание всех подобных процедур. Детальное описание всего текста программы заняло бы слишком много места. Продолжим изучение подпрограммы, приведенной во фрагменте №6. Следующие две команды (начиная с метки ink) служат для обнуления бита номер 6 в коде состояния клавиатуры. Немного ранее я уже писал, зачем это нужно. Шестой бит содержит случайную информацию, поэтому для устранения влияния этого бита на результаты дальнейшего анализа бит нужно обнулить. Рассмотрим и это действие подробно. Сначала код состояния клавиатуры, из буфера bufkl помешается в аккумулятор (команда тог). Затем применяется оператор anl. Этот оператор выполняет поразрядное логическое умножение (операцию «И») с константой. В данном случае значение константы равно 10111111В. Действие команды anl подобно наложению маски. Маской являются разряды нашей константы. Если бит маски равен нулю, то и в аккумуляторе этот бит обнуляется. В тех же разрядах, которые в маске равны единице, содержимое аккумулятора остается без изменений. Если вы еще не поняли как это происходит, посмотрите еще раз на таблицу истинности элемента «И» (рис. 5). Получив однозначный код, определяющий состояние клавиатуры, приступим к следующему этапу — обнаружению момента изменения состояния клавиатуры. Уже знакомый нам оператор cjne выполняет сравнение последнего считанного кода состояния клавиатуры, который после выполнения операции anl находится в аккумуляторе, с содержимым ячейки _klold, где хранится предыдущее значение этого кода. Если коды не равны, то выполняется переход по адресу ini, и процедура обработки команд с клавиатуры продолжается. В противном случае (если коды оказались одинаковы) выполняется команда ret. В том случае, если изменение состояния клавиатуры обнаружено, начинается следующий этап. В начале этого этапа старый и новый коды меняются местами. Обмен байтами выполняется при помощи команды xch. Зачем нужен этот обмен? Это сделано для оптимизации последующих операций. Дело в том, что дальше выполнение программы пойдет по одному из нескольких возможных путей. Это зависит от того, какая из кнопок оказалась нажата. Однако, для правильной работы программы необходимо, чтобы после выхода из подпрограммы в буфере klold оказался тот код состояния клавиатуры, который был считан в текущей сессии. При последующем вызове подпрограммы он будет выступать в роли предыдущего кода. Но и тот код, который до сих пор хранился в буфере klold, нам еще нужен. Ведь мы еще не определили, какой из вариантов изменений наступил (А, В или С). Может показаться, что мы рано делаем этот обмен. Но нет, в самый раз! Далее произойдет разветвление программы. Именно из этого места у нашего «спрута» растут его «щупальца». Так как выходов из этой подпрограммы несколько, нам пришлось бы поместить команду записи нового кода в ячейку klold в конце каждого такого ответвления. С точки зрения оптимальности программного кода разумнее поместить его в начале, перед самым первым ответвлением. Обратите внимание на то, как в разных местах программы происходит обращение к буферу klold. В команде xch используется ссылка «klold», а в команде cjne — «_klold». Если вы обратитесь к модулю описания переменных (фрагмент №I), вы обнаружите, что эти две разные переменные, описывающие одну и ту же ячейку памяти. Только в первом случае она описывается, как регистр, а во втором — как ячейка ОЗУ. Связано это с тем, что оператор cjne не работает с регистрами. Второй параметр этого оператора может быть только адресом ячейки ОЗУ. Переменная _klold была специально создана для этого случая и имена этих двух переменных не случайно похожи друг на друга. Но вернемся к описанию нашей подпрограммы. Следующая команда проверяет, не нажаты ли четыре клавиши для вызова процедуры начального сброса системы. Если вы еще не забыли, код клавиатуры в этом случае должен быть равен константе kkres. При обнаружении такого кода выполняется безусловный переход к процедуре начального сброса fini. В противном случае анализ кода продолжается. Я думаю, подробно разбирать конкретные команды в данном случае не требуется. Далее (начиная с метки in3) программа приступает к определению того, к какому из вариантов (А, В или С) относится наш случай. Сначала новый код состояния клавиатуры, который теперь находится в буфере klold, сравнивается с кодом kknot. Напоминаю, что kknot — это код состояния клавиатуры, когда все кнопки отпущены. Если новый код равен kknot, то мы имеем дело с вариантом В. Вариант b означает, что все кнопки только что оказались отпущены. Поэтому программа выполняет действия, связанные с моментом отпускания кнопок. Сначала программа останавливает мотор. Для этого в буфер команд управления мотором (bufinot) записывается команда остановки. Код этой команды равен нулю. Остановка мотора нужна для того случая, если до этого была нажата одна из кнопок ручного управления (S5 или S6). Если нажаты были не эти кнопки, то команда остановки двигателя тоже не помешает. Затем программа производит проверку флага flks. Значение этого флага зависит от длительности нажатия кнопки S4. Если эта кнопка была нажата, но не настолько долго, чтобы сработала процедура стирания канала, то флаг flks имеет значение Е В противном случае его значение равно нулю. Изменение значения флага происходит в соответствующем месте процедуры динамической индикации. Проверка флага происходит при помощи оператора jbc. Это еще один вид операторов условного перехода — переход по значение бита. Если бит (в данном случае flks) равен единице, то выполняется переход к процедуре записи ка ты а. начало которой расположено по адресу savnk. Кроме того, оператор jbc делает еще одно действие. Он, в отличи е от аналогичного оператора jb, сбрасывает проверяемый бит в ноль. В результате флаг очищается, что предотвращает повторное срабатывание процедуры записи. Если флаг flks окажется сброшенным в ноль, то условный переход не произойдет и команда ret завершит подпрограмму. Следующий раз процедура стирания будет вызвана только после очередного нажатия клавиши S4 (если условия по длительности нажатия будут соблюдены). Теперь рассмотрим ход выполнения программы, если вариант В не подтвердился. В этом случае выполнение программы продолжается с метки in4. В этом месте уже не новый, а старый код состояния клавиатуры сравнивается с константой kknot. Если код равен константе, то это значит, мы имеем дело с вариантом А. Если же коды не равны, то это вариант С. Вариант С ведет к немедленному завершению программы. Для сравнения кодов используется уже известный нам оператор djne. В случае неравенства кодов происходит переход по адресу in9, где расположена команда ret. В противном случае анализ кода продолжается. Этот случай соответствует варианту А. Посмотрим, что же делает программа при обработке варианта А. Дальнейшая программа представляет собой цепочку операторов сравнения. Причем в сравнении участвуют коды тех клавиш, которые должны срабатывать по переднему фронту. И первая операция сравнения, вопреки упрощенному примеру в начале этого раздела, выполняется для кнопки S4. Снова кнопка S4! Что же поделаешь! Слишком много функций нагружено на эту кнопку. Факт нажатия именно этой кнопки определяется путем сравнения кода состояния клавиатуры с константой kkss. На этот раз программа выполняет операции, связанные с моментом нажатия этой кнопки. А именно: сначала флаг flks устанавливается в единичное состояние и индицирует тот факт, что кнопка была нажата. Затем программа перезапускает счетчик coucl. Для этого в ячейку счетчика coucl записывается начальное значение. Начальное значение описано как константа tzcle (см. фрагмент №1). На этом действия, закрепленные за передним фронтом нажатия клавиши S4, заканчиваются. Поэтому команда ret завершает подпрограмму. Если клавиша S4 оказалась не нажата, то прежде, чем выполнить остальные сравнения, сбрасывается флаг flks. Одно из назначений флага flks — запускать и останавливать процесс счета программного таймера coucl. Поэтому, если клавиша S4 оказалась отпущена, таймер нужно остановить. Он должен отсчитывать время, пока нажата клавиша S4. Следующая проверка — это проверка нажатия клавиши S2 (метка in5). Код клавиши S2 описан константой kkmi. В случае совпадения кодов выполняется процедура уменьшения на единицу номера текущего канала (downk). Точно так же происходит проверка нажатия клавиши S3 (метка in6). Код клавиши kkpl. В случае совпадения выполняется процедура — увеличение номера канала (upnk). Перед тем как выполнять последние четыре проверки, программа проверяет значение флага flsav (метка in7). Если значение флага равно единице, то последние четыре проверки не выполняются. Флаг flsav равен единице в режиме записи положения антенны. Позиционер переходит в режим записи при первом нажатии кнопки S4. В этом режиме индикатор мигает и ждет, пока пользователь выберет номер канала, а затем вторично нажмет кнопку S4. Пока идет процесс записи, все кнопки, кроме тех, что участвуют в этом режиме, должны быть отключены во избежание неопределенности результата. При завершении процесса записи флаг flsav будет сброшен, и четыре последние проверки будут разблокированы. Все эти проверки выполняются уже известным нам способом. Предлагаю разобраться со всеми оставшимися команлами фрагмента №6 самостоятельно. Используются технологии uCoz
|