PlayStation Plus (PSN Plus) - 365 Дней
PlayStation Plus (PSN Plus) - 365 Дней


100 VISA VIRTUAL + Быстрая выписка
100 VISA VIRTUAL + Быстрая выписка


iTunes Gift Card (Россия) 700 рублей
iTunes Gift Card (Россия) 700 рублей


В начало

Разработка прикладных программ (Лекция)

 

ПЛАН ЛЕКЦИИ

1. Формализованный подход к разработке прикладных программ

2. Подпрограммы

3. Группа команд передачи управления

 

1. Формализованный подход к разработке прикладных программ

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

1.         подробное описание задачи;

2.         анализ задачи;

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

4.         разработку общей блок-схемы алгоритма (БСА) работы конт­роллера;

5.         разработку детализированных БСА отдельных процедур, выде­ленных на основе модульного принципа составления программ;

6.         детальную проработку интерфейса контроллера и внесение исправ­лений в общую и детализированные БСА;

7.         распределение рабочих регистров и памяти МК;

8.         формирование текста исходной программы.

В результате работы по трем первым пунктам данного перечня полу­чают так называемую функциональную спецификацию прикладной про­граммы МК, в которой основное внимание уделяется детализации спо­собов формирования входной и выходной информации.

На языке схем алгоритмов разработчик описывает метод, выбранный для решения поставленной задачи. Довольно часто бывает, что одна и та же задача может быть решена различными методами. Способ решения задачи, выбранный на этапе ее инженерной интерпретации, на основе ко­торого формируется БСА, определяет не только качество разрабаты­ваемой прикладной программы, но и качественные показатели конечно­го изделия.

Разработка БСА очень похожа на разработку аппаратурных средств . систем автоматики и обработки данных. В основу разработки БСА положена та же самая процедура модульного проектирования, которая традиционно используется разработчиками аппаратурных средств. Отличие состоит в том, что при разработке аппаратурных средств в ка­честве ’’строительного” материала используются логические схемы, триггеры, регистры и другие интегральные элементы, а при создании программного обеспечения разработчик оперирует командами, подпро­граммами, таблицами и другими программными объектами из арсенала средств обработки данных.

Так как алгоритм есть точно определенная процедура, предписываю­щая контроллеру однозначно определенные действия по преобразованию ’’сырых” исходных данных в обработанные выходные данные, то разра­ботка БСА требует предельной точности и однозначности используемой атрибутики: символических имен переменных, констант (уставок), подпрограмм (модулей), символических адресов таблиц, портов ввода/ вывода и т.п. Основное внимание при разработке БСА следует уделить тому разделу функциональной спецификации прикладной программы, в котором приводится описание аппаратуры сопряжения МК с объектом управления. (Это описание для успешной разработки программного обеспечения должно быть детализировано вплоть до электрических и временных характеристик каждого входного и выходного сигнала или устройства.)

Секрет успеха разработки прикладной программы МК заключается в использовании метода декомпозиции, при котором вся задача последо­вательно разделяется на меньшие функциональные модули, каждый из которых можно анализировать, разрабатывать и отлаживать отдельно от других. При выполнении прикладной программы в МК управление без всяких двусмысленностей передается от одного функционального мо­дуля к другому. Схема связности этих функциональных модулей, каж­дый из которых реализует некоторую процедуру, образует общую (или системную) БСА прикладной программы. Это разделение задачи на мо­дули и субмодули выполняется последовательно до такого уровня, ког­да разработка БСА модуля становится простым и понятным делом. Метод последовательной декомпозиции обладает достаточной гибкостью, что позволяет привести степень детализации БСА в соответствие со слож­ностью процедуры. Не следует стесняться при выполнении декомпози­ции дойти до модулей, которые почти тривиальны. Ведь именно эту цель (получение! очень простого и ’’прозрачного” алгоритма модуля) пре­следует разработчик, когда он стремится заставить МК надежно выпол­нять требуемую работу по управлению объектом. Язык графических образов БСА можно использовать на любом уровне детализации описа­ния модулей вплоть до того, что каждому оператору БСА будет соот­ветствовать единственная команда МК.

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

В международном стандарте на программный продукт HIPO (Hie­rarchyInputProcessOutput) (”хай-по”) декларируется аналогичный подход к разработке прикладных программ.

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

1.         Определить, что должен делать модуль (это уже было сделано при разработке системной БСА, но теперь разработчик имеет дело с фрагмен­том прикладной программы, а не с целой программой, и, следовательно, может потребоваться доопределение и уточнение целевого назначения процедуры).

2.         Определить способы получения модулем исходных данных (от дат­чиков через порты ввода, или из таблиц в памяти, или через рабочие регистры). Для реализации ввода исходных данных в модуль в его БСА надо включить соответствующие операторы.

3.         Определить необходимость какой-либо предварительной обработки введенных исходных данных (маскирование, сдвиг, масштабирование, перекодировка). Если до использования ’’сырых” данных требуется их предобработка, то в состав БСА включаются соответствующие опера­торы.

4.         Определить способ преобразования входных данных в требуемые выходные. Используя операторы процедур и условные операторы приня­тия решения, отобразить на языке БСА выбранный метод содержатель­ной обработки исходных данных.

5.         Определить способы выдачи из модуля обработанных данных (пе­редать в память, или в вызывавшую программу, или в порты вывода информации) . Необходимые действия отобразить в БСА.

6.         Определить необходимость какой-либо постобработки выводимых данных (изменение формата, перекодирование, масштабирование, мас­кирование) . Ввести в БСА операторы подготовки данных для вывода из модуля.

7.         Вернуться к п. 1 настоящего перечня работ и проанализировать по­лученный результат. Выполнить итеративную корректировку БСА с целью сделать ее простой, логичной, стройной и обладающей четким графическим образом.

8.         Проверить работоспособность алгоритма на бумаге путем подста­новки в него действительных данных. Убедиться в его сходимости и результативности.

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

10.     Провести мысленный эксперимент по определению работоспособ­ности алгоритма в реальном масштабе времени, когда стохастические события, происходящие в объекте управления, могут оказать влияние на работу алгоритма. При этом самому тщательному анализу следует под­вергнуть реакцию алгоритма на возможные прерывания с целью опреде­ления критических операторов, которые необходимо защитить от преры­ваний. Кроме того, в ходе этого мысленного эксперимента следует про­анализировать логику алгоритма с целью определения таких последова­тельностей операторов, при выполнении которых МК может ”не заме­тить” кратковременных событий в объекте управления. При обнаруже­нии таких ситуаций в логику БСА следует внести коррективы.

Практика разработки программного обеспечения для МК показала, что последовательное использование описанной поэтапной процедуры проектирования алгоритмов, составляющей основу метода структурно­го программирования, позволяет уверенно получать работоспособные прикладные программы. Дисциплинированное следование этой поэтап­ной процедуре проектирования прикладных программ обеспечивает успех проекта! В противном случае вы рискуете заболеть страшным программным заболеванием, которое называется «вползающие осо­бенности». Эта инфекция возникает, когда неадекватная спецификация задачи позволяет вползать в программу организмам, называемым «изящные особенности». Те изменения, которые легко учесть на этапе планирования, могут потребовать огромных усилий на этапе реализации программы. Болезнь эта к моменту обнаружения становится уже серьез­ной и привела к фатальному концу много программных проектов. Чаще всего носителями этой болезни являются профессиональные программисты, которые способны заразить ею программирующих про­фессионалов. Если Вы стали жертвой «вползающих особенностей», то должны или начать заново разрабатывать функциональную специфи­кацию программного обеспечения, или быть готовыми к исключительно высоким трудозатратам на этапе отладки прикладной программы.

 

2. Подпрограммы

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

Назначение подпрограмм

Подпрограммы изначально появились как средство оптимизации программ по объёму занимаемой памяти  –  они позволили не повторять в программе идентичные блоки кода, а описывать их однократно и вызывать по мере необходимости. К настоящему времени данная функция подпрограмм стала вспомогательной, главное их назначение  –  структуризация программы с целью удобства её понимания и сопровождения.

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

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

Механизм подпрограмм, их описание и вызов

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

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

Внимание! Ни в коем случае нельзя попадать в подпрограмму любым способом кроме команды вызова подпрограммы CALL!  В противном случае команда возврата из подпрограммы передаст управление случайному адресу! По этому адресу могут быть расположены данные, которые в этом случае будут интерпретированы как программа.

Очень часто требуется из одной подпрограммы обращаться к другой подпрограмме. Такое обращение к подпрограмме называется вложенным. Количество вложенных подпрограмм называется уровнем вложенности подпрограмм. Максимально допустимый уровень вложенности подпрограмм определяется количеством ячеек памяти, предназначенных для хранения адресов возврата из подпрограмм, т.е. размером сегмента стека.

Параметры подпрограмм

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

Циклы

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

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

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

Виды циклов

Безусловные циклы

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

 

Цикл с предусловием, цикл с постусловием

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

Цикл с постусловием  –  цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз.

На языке ассемблера циклы с предусловием или с постусловием реализуются с помощью команд условных переходов, команд сравнения.

 

Цикл с выходом из середины

Синтаксически такой цикл оформляется с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала маркирует точку программы, в которой начинается тело цикла, конструкция конца  –  точку, где тело заканчивается. Внутри тела должна присутствовать команда выхода из цикла, при выполнении которой цикл заканчивается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнился более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.

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

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

 

Цикл cо счётчиком

Цикл со счётчиком  –  цикл, в котором некоторая переменная изменяет своё значение от заданного начального значения до конечного значения с некоторым шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В языке программирования Ассемблер для микроконтроллеров семейства MCS-51 цикл со счетчиком реализуется с помощью команды DJNZ (Decrement and Jump if Not Zero), в которой указывается счетчик и метка начала цикла, куда переходит микроконтроллер, если счетчик оставшихся оборотов цикла не равен нулю. В качестве счётчика (так называемой «переменной цикла»), используется один из регистров общего назначения или ячейка резидентной памяти данных. Перед циклом в нем задается требуемое количество проходов. Обычно каждое выполнение тела цикла регистр-счетчик декрементируется, и после этого выходит из цикла, если остаточное количество проходов равно нулю, иначе переходит на метку, символизирующую начало цикла.

Листинг 1. Пример организации цикла

MOV Rn,<количество проходов цикла>   ; установка счетчика

<метка>:      ; метка начала тела цикла

     .....    ; тело цикла

DJNZ Rn, <метка>   ; команда зацикливания,

                   ; если счетчик не равен нулю

 

3. Группа команд передачи управления

Группа представлена командами безусловного и условного переходов, командами вызова подпрограмм и командами возврата из подпрограмм.

Команда безусловного перехода LJMP (L - long - длинный) осуществляет переход по абсолютному 16-битному адресу, указанному в теле команды, т. е. команда обеспечивает переход в любую точку памяти программ.

Действие команды AJMP (А - absolute - абсолютный) аналогично команде LJMP, однако в теле команды указаны лишь 11 младших разрядов адреса. Поэтому переход осуществляется в пределах страницы размером 2 Кбайт, при этом надо иметь в виду, что сначала содержимое счетчика команд увеличивается на 2 и только потом заменяются 11 разрядов адреса.

В отличие от предыдущих команд, в команде SJMP (S - short - короткий) указан не абсолютный, а относительный адрес перехода. Величина смещения rel рассматривается как число со знаком, а, следовательно, переход возможен в пределах - 128...-127 байт относительно адреса команды, следующей за командой SJMP.

Команда косвенного перехода JMP @A+DPTR позволяет вычислять адрес перехода в процессе выполнения самой программы.

Командами условного перехода можно проверять следующие условия:

        JZ   аккумулятор содержит нулевое значение;

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

        JC   бит переноса С установлен;

        JNC   бит переноса С не установлен;

        JB   прямо адресуемый бит равен 1

        JNB   прямо адресуемый бит равен 0;

        JBC   прямо адресуемый бит равен 1 и сбрасывается в нулевое значение при выполнении команды.

Все команды условного перехода рассматриваемых микро-ЭВМ содержат короткий относительный адрес, т. е. переход может осуществляться в пределах   -128...127 байт относительно следующей команды.

Команда DJNZ предназначена для организации программных циклов. Регистр Rn или байт по адресу ad, указанные в теле команды, содержат счетчик повторений цикла, а смещение ге1   относительный адрес перехода к началу цикла. При выполнении команды содержимое счетчика уменьшается на 1 и проверяется на 0. Если значение содержимого счетчика не равно 0, то осуществляется переход на начало цикла, в противном случае выполняется следующая команда.

Команда CJNE удобна для реализации процедур ожидания внешних событий. В теле команды указаны "координаты" двух байт и относительный адрес перехода rel. В качестве двух байт могут быть использованы, например, значения содержимого аккумулятора и прямо адресуемого байта или косвенно адресуемого байта и константы. При выполнении команды значения указанных двух байт сравниваются и в случае, если они не одинаковы, осуществляется переход. Например, команда

WAIT: CJNE A, P0, WAIT

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

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

Команда возврата из подпрограммы RET восстанавливает из стека значение содержимого счетчика команд, а команда возврата из процедуры обработки прерывания RETI, кроме того, разрешает прерывание обслуженного уровня. Команды RET и RETI не различают, какой командой - LCALL или ACALL - была вызвана подпрограмма, так как и в том, и в другом случае в стеке сохраняется полный 16-разрядный адрес возврата.

В заключение следует отметить, что большинство Ассемблеров допускают обобщенную мнемонику JMP - для команд безусловного перехода и CALL - для команд вызова подпрограмм. Конкретный тип команды определяется Ассемблером, исходя из "длины" перехода или вызова.