Устройства управления роботами, схемотехника и программирование (М. Предко, 2004). Устройства управления роботами, схемотехника и программирование. Устройствауправления роботамисхемотехника и микроконтроллеров picmicro
Скачать 6.79 Mb.
|
ГЛАВА 2 РАЗРАБОТКА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ Во введении я уже объяснял, что удобно выделить три уровня задач, решаемых программой управления роботом. Разбиение на биологический, механический и электронный уровни обычно упрощает процесс разработки и позволяет добить- ся наилучших результатов. Во-первых, такое деление упрощает код программы, которая должна решать только свой круг задач. Во-вторых, при многоуровневом проектировании возможно обеспечить лучшее согласование различных функций, выполняемых роботом, чем в случае, когда все эти функции реализуются в одной общей программе. Ниже мы рассмотрим образцы программ, написанных для ре- шения задач разных уровней, а также обсудим, как они могут быть связаны друг с другом. Как вы увидите, все примеры спроектированы таким чтобы в максимальной степени облегчить стыковку различных программных модулей. В этой главе речь пойдет об использовании различных инструментальных средств, которые необходимы при разработке программного обеспечения (ПО) для микроконтроллеров. Мы начнем с самых простых инструментов программи- ста и постепенно дойдем до наиболее сложных. Будет рассмотрен самый общий инструментарий, применяемый во всех случаях, а не только для программирова- ния роботов. Позже в книге мы обсудим основные шаблоны проектирования про- грамм, которые полезны именно для применения в области робототехники. Надо заметить, что, хотя существуют специализированные средства проекти- рования автоматических устройств, в большинстве случаев достаточно использо- вания универсальных инструментов разработки, описанных в этой главе. Обсуждение начнем с инструментальных средств наиболее высокого уровня, чтобы дать ясное понимание того, на что способны эти средства и зачем они при- меняются, какие у них преимущества и недостатки. Полагаю, что у вас не возник- нет затруднений при инсталляции компилятора Lite и интегрированной среды разработки MPLAB IDE. Обе программы можно найти в свободном досту- пе на сайтах www.microchip.com и www.htsoft.com. Если у вас все-таки возникнут трудности с инсталляцией, желательно обратиться к ресурсам, посвященным эле- ментарным навыкам работы на персональном компьютере, а также потратить не- которое время на изучение основ программирования на языке С. Интегрированная среда разработки MPLAB фирмы Microchip, подробно об- суждаемая в главе 3, предназначена для редактирования текста программы, ее компиляции и осуществления моделирования (simulating) процесса выполнения Разработка программного обеспечения 37 программы (даже в отсутствие самого микроконтроллера), а также для загрузки программы в микроконтроллер. Компилятор Lite фирмы HI-TECH Software - свободно распространяе- мый компилятор языка С для микроконтроллера PIC16F84. Этот компилятор хорошо интегрируется со средой разработки MPLAB. Оба программных продук- та в совокупности представляют мощный инструмент разработки программного обеспечения для микроконтроллеров, обеспечивающий возможности программи- рования и отладки, за которые при использовании других контроллеров пришлось бы заплатить тысячи долларов. К неудовольствию пользователей рабочих компьютеров Apple и при- верженцев Linux, большинство инструментальных средств разработки ПО рабо- тает только на персональных компьютерах PC под управлением операционной системы Windows (так называемые Wintel PC). Инструментальные средства, раз- работанные для рабочих станций, как правило, очень дороги и к тому же обычно входят в еще более дорогие программные пакеты или прилагаются некоторым устройствам. С другой стороны, все еще доступны бесплатные средства разработ- ки, ориентированные на операционные системы Windows 3.x или даже MS DOS. К сожалению, они, как правило, не поддерживают многие полезные возможности и не могут работать с новыми микроконтроллерами. В последнее время все доступнее становится специализированное программное обеспечение для операционной системы Linux. Для многих микроконтроллеров уже существуют версии компиляторов GCC языка С, разработанные в рамках проекта GNU; они поддерживают многие возможности из тех, что описаны в этой книге применительно к компилятору PICC Lite. Кроме того, существуют версии инстру- ментальных средств для системы UNIX, компьютеров Macintosh и др. Чтобы избежать проблем, связанных с поиском средств разработки ПО для опре- деленного микроконтроллера, лучше использовать инструменты, рассчитанные на операционную систему Windows. Они не только дешевле, но и обеспечивают мак- симальную переносимость ваших проектов на другие платформы. 2.1. ОТ ИСХОДНОГО ТЕКСТА ПРОГРАММЫ К НЕХ-ФАЙЛУ Прежде всего обсудим, с какими файлами приходится иметь дело программисту, когда он разрабатывает ПО для микроконтроллеров. Исходный текст программы как известно, хранится в текстовом файле в коде ASCII (American Standard Code for Information Interchange; произ- Этот текст содержит операторы используемого языка програм- мирования, а также комментарии, в которых автор программы может пояснить назначение каждого оператора. Текст программы может быть написан с исполь- зованием любого текстового редактора (Блокнот, WordPad). Простой текстовый редактор также имеется в составе интегрированной среды разработки. Написанный программистом исходный текст обрабатывается специальной про- граммой - интерпретатором или компилятором. В результате работы компилятора 38 Устройства управления роботами получается так называемый объектный файл или файл), который с помощью программатора может быть загружен в память микро- контроллера. В случае использования интерпретатора дело обстоит несколько иначе: программа-интерпретатор встроена в микроконтроллер и может непосред- ственно выполнять команды, записанные в исходном тексте программы. Текст программы может содержать ссылки на другие (обычно тоже текстовые) файлы, в которых описаны используемые в программе функции или определены необходимые данные. Такие включаемые файлы обычно имеют расширение В случае использования языка С файлам, включаемым в исходный текст програм- мы, присвоено расширение .h. Использование отдельных файлов упрощает про- цесс программирования, и после приобретения необходимого опыта вы сможете самостоятельно разработать библиотеку включаемых файлов. В результате компиляции создается файл с расширением .obj или НЕХ- файл уже может загружаться в память микроконтроллера, а OBJ-файл предна- значен для последующего связывания с другими OBJ-файлами для формирования окончательного НЕХ-файла. Эту работу выполняет так редактор свя- зей (компоновщик, linker). Наиболее популярный формат НЕХ-файлов - это 8-разрядный формат Именно в этом формате должен быть записан НЕХ-файл для программатора El Cheapo. Заметим, что для микроконтроллеров фирмы Motorola обычно исполь- зуется другой так называемый S9. Приведем пример НЕХ-файла, подготовленного для микроконтроллера PIC в формате INXM8: :10000000FF308600831686018312A001A101AOOB98 :02400EOOF13F80 Каждая шестнадцатеричная цифра представлена в текстовом файле в коде ASCII, то есть занимает один байт. В каждой строке указаны стартовый адрес и данные, которые должны быть загружены в память начиная с этого адреса. Смысл каждой позиции в текстовом файле поясняется в табл. 2.1. Формат 1NXM8 Порядковый номер позиции Назначение Всегда содержит символ : - начало строки 2-3 Число команд, содержащихся данной строке цифры на каждую 4-7 8-9 Стартовый адрес, начиная с которого будут размещаться команды, в формате Big (начиная со старшего байта, как в процессорах Motorola) Тип строки: 00 - программа или данные, 01 — конец 10-13 Первая команда, которая должна загружаться в память начиная с указанного адреса, в формате Little Endian (начиная с младшего байта, как в процессорах Другие команды, 4 символа на каждую Разработка программного 39 Таблица Формат INXM8 (окончание) номер два байта сумма строки Последние два байта в текстовом редакторе) каретки и перевода строки Для вычисления контрольной суммы необходимо сложить все байты строки и вычесть младший байт полученной суммы из числа Для примера найдем контрольную сумму второй строки приведенного выше фраг- мента НЕХ-файла: 00 10 00 • 07 28 А1 0В 07 - 28 86 03 07 28 ЮС . ' . Вычтем младший байт результата из 0x0100 OxOODC 0x0024 Полученное значение 2 4 записано в конце второй строки рассматриваемого НЕХ-файла. К созданному компилятором объектному файлу компоновщик может добавить нужные библиотечные файлы, содержащие код функций, необходимых для вы- полнения основной программы. Например, в случае использования языка высо- кого уровня в этих библиотечных файлах могут быть описаны команды умноже- ния и деления, операции над комплексными числами или команды ввода-вывода. Подобные библиотеки обычно поставляются вместе с инструментальным сред- ством, но программист может использовать и библиотечные объектные файлы из любых других источников, в том числе свои собственные. Компоновщики обычно имеют многочисленные настройки, определяющие различные режимы работы по созданию конечного НЕХ-файла из одного или нескольких OBJ-файлов. К счастью, значения большинства параметров, установ- ленные по умолчанию, обеспечивают наиболее часто используемый режим компо- новки. При этом программист может быть уверен, что функции, которые хотя 40 Устройства управления роботами и содержатся в подключаемой библиотеке, но ни разу не используются в програм- ме, будут исключены при компоновке из конечного НЕХ-файла. В результате будет создан загрузочный файл минимального размера. Обычно в объектных биб- лиотеках содержатся десятки или сотни различных функций, и глупо было бы зря расходовать память микроконтроллера, загружая в нее те из них, которые никог- да не используются. Компиляция приложения, состоящего всего из одного исходного файла и не обращающегося к каким-либо другим программным модулям, - довольно простое дело. В таких программах очень легко разобраться; притом их не слишком слож- но отлаживать. В противном случае всегда существует некоторая опасность не- правильного обращения к ресурсам других Но иногда затруднительно разработать приложение, состоящее всего из одного файла, например, если над проектом работает не один программист. В этом случае удобнее разбивать боль- шую программу на отдельные модули. Сложные программные проекты, включающие в себя множество исходных файлов и библиотек объектных модулей, хорошо подходят для тех случаев, когда в группе несколько программистов. Но такой подход требует строгой проверки работоспособности каждого программного модуля по отдельности. В некоторых случаях, когда для объектных модулей недоступны исходные тексты, процесс ком- поновки всего приложения может вызвать некоторые затруднения, а отладка ста- новится еще более сложной. 2.2. АССЕМБЛЕР Считается, что язык ассемблера (или просто ассемблер) позволяет разрабатывать наиболее эффективный машинный код. Однако этот язык (как говорят, язык низ- кого уровня) труден для изучения и не слишком легок в использовании, поэтому запрограммированные с его помощью приложения не всегда оказываются ми эффективными. Несмотря на это обстоятельство, ассемблирование является любимым занятием многих разработчиков ПО для управления роботами, пото- му что компиляторы для этого языка, предназначенные для работы с самыми различными микроконтроллерами, обычно бесплатно распространяются через Internet. Кроме программирование на ассемблере - само по себе увлека- тельное занятие. Прежде чем начать разработку программ на языке низкого уровня, вы должны хорошо разобраться в том, как работает микроконтроллер. Причем недостаточно только познакомиться с системой его команд. Программиста подстерегает множе- ство ловушек, которые не видны тем, кто просто смотрит на список команд про- цессора, не пытаясь написать действительно работающую программу. Говоря, что необходимо разобраться в том, как работает микроконтроллер, я не имею в виду, что надо досконально знать принципы его функционирования на аппаратном уровне. Нет, достаточно просто понимать, как он выполняет свои Разработка программного обеспечения 41 команды. Многие команды, которые при первом знакомстве кажутся одинаковыми у разных процессоров, на самом деле работают несколько по-разному, причем это трудно заметить, изучая систему команд процессора. Рассмотрим для примера следующий фрагмент программы на языке С: if (А == 0) // Если значение переменной А равно нулю, // то значение переменной В на Для обычного 8-разрядного процессора ассемблерный код, соответствующий этому фрагменту, будет выглядеть следующим образом: Загрузить в аккумулятор значение переменной А. Прибавить к нему затем сравнить результат с 0. Если результат не нулевой, то перейти на метку Skip, в противном случае выполнить инкремент В. move А add Skip move В add «1 move В, Skip: Для многих микроконтроллеров предусмотрена специальная команда для ин- кремента и декремента (увеличения и уменьшения на в этом случае соответ- ствующие строки программы могут быть изменены. Например, три последние команды в нашем примере заменятся одной-единственной командой инкремента переменной В. У некоторых процессоров (в том числе и у микроконтроллеров PIC) команда move изменяет состояние флажков; это позволяет исключить из текста програм- мы строку add »0 ' • Кроме того, обычно микроконтроллеры имеют специальную инструкцию, ко- торая в зависимости от состояния каких-либо флагов позволяет выполнить или пропустить следующую команду. Например, если использовать инструкцию (SKip if Not Zero - пропустить, если не 0), то наша программа еще более упростится: A, f ; Установить флаг нуля, если = 0. skpnz ; Пропустить следующую команду, если не установлен флаг нуля. В, f ; Выполнить инкремент В. Кроме простоты измененный фрагмент программы имеет много достоинств, которые, возможно, незаметны на первый взгляд: • состояние аккумулятора не изменяется - мы просто устанавливаем флаги в соответствии со значением переменной А; • при использовании команды skpnz отпадает необходимость расставлять мет- ки и применять условные команды перехода; Инструкция skpnz - это которой на самом деле соответствует машинная команда btfsc Z. - Прим. перев. 42 Устройства управления роботами • количество машинных циклов («тиков» тактового генератора), в течение ко- торых будет выполняться программа, не зависит от того, выполнилась коман- да инкремента или нет. Однако в последнем примере есть один недостаток: сравнивая ассемблерный код с исходным текстом программы на языке С, сразу и не поймешь, что оба фраг- мента делают одно и то же. Приведенный пример показывает, как важно понимать назначение и побочный эффект каждой команды процессора, чтобы разрабатывать действительно тивный код. Поверьте, усилия, затраченные на изучение ассемблера, с лихвой окупятся при программировании! Когда вы достаточно хорошо освоите программирование на ассемблере, то сможете представить свою программу как «кинофильм», каждый кадр которого соответствует отдельной команде процессора. Тогда даже без использования от- ладчика вам будет понятно назначение каждой строчки программы, и вы без тру- да найдете ошибки, которые, возможно, в ней содержатся. Но даже опытным программистам на ассемблере стоит учитывать несколь- ко важных моментов. Первое, о чем следует предупредить: программа, разрабо- танная для одного микроконтроллера, не сможет работать на другом. Ее пере- нос на другую аппаратную платформу может потребовать значительных усилий. Это так же трудно, как описать другому программисту во всех подроб- ностях кадры того «кинофильма», который прокручивался у вас в голове. Во- вторых, иногда довольно сложно использовать код, написанный на ассемблере, в программе на языке высокого уровня. Дело в том, что процедуры и функции в высокоуровневых языках имеют входные и выходные параметры. И при раз- работке ассемблерной процедуры, которая будет вызываться из программы, написанной, например, на С, приходится принимать во внимание все тонкости работы с этими параметрами. Корректность передачи данных в ассемблерную процедуру и правильность возвращаемых значений могут потребовать кропот- ливого тестирования. Когда пытаешься разобраться в программе, написанной на языке ассемблера, обычно не так уж просто осознать ее структуру и назначение отдельных частей. И это самый большой недостаток ассемблерных программ. Они обычно плохо структурированы и отвлекают внимание программиста на особенности аппарату- ры, скрывая главную идею, заложенную в или иной фрагмент кода. Языки высокого уровня, напротив, весьма близки к естественному английско- му языку. Например, английское предложение if the contents of variable A are equal to increment the contents of variable В (если значение переменной А нулю, то увеличить на единицу значение переменной В) на языке Basic запишется следующим образом: if (А = 0), then В = В +. 1 а на языке С: if (А == 0) В++; Разработка программного обеспечения 43 Записи на языках Basic или С по структуре близки к вышеуказанному предло- жению, а вот ассемблерная программа для контроллера PIC будет не настолько очевидной. Материальные соображения оказываются не последними, когда разработчику приходится выбирать язык программирования для своего будущего проекта. Ин- струментальные средства разработки приложений на языке ассемблера бесплат- ны для большинства микроконтроллеров и обычно распространяются по сети Internet; порой это оказывается решающим фактором, заставляющим программи- стов писать свои программы на языке низкого уровня. Несмотря на отмеченные недостатки, ассемблер был и остается весьма полез- ным инструментом. Только на этом языке часто оказывается возможным реали- зовать критичные участки программы, работающие с аппаратурой и обеспечива- ющие точно заданные временные задержки между отдельными операциями. Тем не менее в этой книге, как уже отмечалось, я постараюсь не использовать язык низкого уровня - во-первых, чтобы не затруднять понимание программного кода, а во-вторых, чтобы облегчить перенос программ на другие микроконтроллеры. 2.3. ИНТЕРПРЕТАТОРЫ Когда персональные компьютеры только появились, интерпретаторы (такие как Basic) были довольно распространены. Большая часть программного обеспече- ния и даже обучение программированию было ориентировано на их использова- ние. Именно интерпретатор языка Basic загружался в память первых компьюте- ров IBM PC при их включении и брал на себя выполнение тех функций, которые сейчас считаются прерогативой операционной системы. Интерпретатором называется специальная программа, которая запускается на компьютере и выполняет исходный текст программы. Поэтому не требуется какое-либо преобразование исходного файла, как в случае использования компи- лятора. Кроме того, интерпретатор обычно обеспечивает простейший интерфейс пользователя, позволяя ему вводить данные, редактировать программу и сохра- нять ее на диске или магнитной ленте. Приведем пример простой программы на языке Basic: i = 1 to 4 print i next i end Как только пользователь вводит команду, интерпретатор сразу выполняет ее. В нашем примере имеется оператор цикла, который выполнится четыре раза, для значений параметра i от 1 до 4. Поэтому будет распечатано четыре числа: 1 2 3 4 После этого интерпретатор выведет приглашение (prompt) для ввода следую- щей команды. 44 Устройства управления роботами Программа-интерпретатор, встроенная в память компьютера, позволяет фир- мам-изготовителям предоставить пользователям машину, готовую к использова- нию. Вместо того чтобы вводить свою программу в машинных кодах с помощью, например, кнопок, расположенных на лицевой панели компьютера, пользователь получает в свое распоряжение удобный интерфейс для общения с машиной. Интерпретатор, встраиваемый в некоторые микроконтроллеры, чаще всего работает с какого-либо последовательного интерфейса, Например RS-232. Как правило, такой интерпретатор дает пользователю возможность за- гружать свои программы в микроконтроллер с помощью персонального компью- тера. Как и для первых PC, интерпретаторы для микроконтроллеров обычно ос- нованы на языке Basic. Можно привести несколько доводов в пользу использования интерпретатора при разработке автоматического устройства. Во-первых, интерпретатор - это инструмент, дружественный пользователю. Он позволяет быстро проверить ту или иную вашу идею, не прибегая к кропотли- вому процессу перекомпиляции всего приложения. Вы просто подключаете свое- го робота к разъему последовательного порта компьютера, изменяете нужную строку программы и проверяете, что из этого получилось. Конечно, есть и обратная сторона такой легкости изменения кода - часто программист забывает о необходи- мости документировать все изменения, вносимые в свой проект, а иногда по оплош- ности и вовсе не сохраняет на диске компьютера измененную программу. Во-вторых, программа, рассчитанная на работу с интерпретатором, обычно не использует особенности аппаратуры и поэтому легко может быть перенесена на другой микроконтроллер. В-третьих, у программ-интерпретаторов обычно много встроенных функций, к которым можно обратиться, не прибегая к созданию низкоуровневых процедур для работы с различными интерфейсами аппаратных средств (электронный уро- вень) или с электромеханическими устройствами робота (механический уровень). Например, разработчик сможет воспользоваться готовой процедурой для подав- ления дребезга механических контактов или вызвать заложенную в интерпрета- тор функцию широтно-импульсной модуляции (ШИМ) для регулирования ско- рости вращения двигателя. В-четвертых, язык Basic и другие языки, используемые в интерпретаторах, очень просто изучить и применять; к тому же они уже получили распространение в мире программирования, и разработчику программ для роботов не придется осваивать какой-то специальный язык. О языке Basic написано немало книг, и имеется большое количество готовых программ, которые можно будет исполь- зовать в проекте. Наконец, большинство интерпретаторов для микроконтроллеров бесплатно распространяется по сети Internet. (Например, интерпретатор, разработанный мной для микроконтроллера PIC16F87x, можно скачать с сайта www.myke.com.) В большинстве случаев доступна не только программа-интерпретатор, но и ее исходный текст на каком-либо языке, поэтому при необходимости пользователь может изменить заложенные в нее функции. Разработка программного обеспечения 45 Разумеется, используя интерпретатор, программист должен мириться и с не- которыми его недостатками. Самый важный из них - низкая скорость выполне- ния программ. Программа, написанная на ассемблере, обычно выполняется про- цессором со скоростью несколько миллионов машинных команд в секунду, а написанная на компилирующем языке высокого уровня - несколько десятков тысяч операторов в секунду. Интерпретатор же способен выполнить всего сотню, в лучшем случае тысячу операторов в секунду. Для функций биологического уровня такая медлительность еще допустима, но выполнение функций нижних уровней часто требует большего быстродействия. Программист должен знать некоторые особенности использования интерпре- татора при разработке автоматического устройства. Во-первых, многочисленные комментарии к тексту программы, которые мож- но только приветствовать в случае применения компилятора, могут существенно замедлить работу интерпретатора. Ведь последнему приходится читать програм- му строка за строкой и тут же ее выполнять; при этом требуется дополнительное время на то, чтобы распознать и исключить из рассмотрения символы, входящие в комментарий. Во-вторых, при использовании команд перехода (таких как goto) интерпре- татору приходится просматривать всю программу в поисках метки, на которую должен быть сделан переход. Это требует много времени, поэтому во многих ран- них версиях языка Basic все Строки программы нумеровались - ведь найти нуж- ную строку по номеру гораздо проще, чем просматривать весь текст в поисках сим- вольной метки. В-третьих, при использовании интерпретатора сложно реализовать заданные временные промежутки между отдельными командами, потому что затрудни- тельно предсказать, как долго будет выполняться та или иная команда. программа-интерпретатор занимает довольно много памяти, так что на саму программу управления роботом остается меньше места. Немногие микроконтроллеры способны разместить интерпретатор в своем внутреннем ПЗУ, поэтому обычно приходится использовать внешние микросхемы памяти. Здесь опять можно вспомнить о комментариях — в скомпилированной программе они не занимают ни одного дополнительного байта, но интерпретатору приходит- ся держать в памяти весь текст программы, в том числе и ненужные ему коммен- тарии. Следует заметить, что возможности отладки программ, встроенные в большин- ство интерпретаторов, достаточно скромны. Работая с аппаратными средствами микроконтроллера, интерпретатор должен обращаться к его регистрам. Но из-за невысокой скорости работы процесс чтения и записи происходит слишком медленно, и система обработки прерываний может не успеть обработать запрос какого-нибудь внешнего устройства. Хотя интерпретатор позволяет вносить изменения в текст программы без ис- пользования программатора, загрузка интерпретатора в память микроконтроллера требует того же самого программатора. Правда, в некоторых случаях микрокон- троллер может быть приобретен вместе со встроенным в него интерпретатором, 46 Устройства управления роботами но это значительно увеличивает его стоимость. К тому же редко приходится рас- считывать на то, что встроенный интерпретатор будет поддерживать те функции, которые потребуются разработчику для его проекта. Наконец, стоит упомянуть о модулях Parallax Basic Stamp, в которых использу- ется подход, совмещающий преимущества интерпретатора и компилятора. Такие модули программируются на языке PBasic, который каждый оператор исходного текста программы компилирует в специальный код (последовательность Этот код, а не сам текст программы, загружается в память модуля Basic Stamp. Каж- дый токен обычно занимает в памяти один байт и кодирует некоторую встроенную функцию. Но последовательность токенов - не совсем то же самое, что исходный текст программы, поэтому Basic Stamp не может считаться интерпре- татором. Stamp часто используется разработчиками, так как недорог, легко программируется, поддерживает много полезных встроенных функций и в сети Internet для доступно большое число примеров программ. Выбирая подходящий интерпретатор, разработчик должен обратить внимание на наличие следующих встроенных функций: • возможность сохранения программы в ПЗУ; • поддержка простого последовательного интерфейса; • наличие широтно-импульсного модулятора; • поддержка интерфейсов PC, CAN и др.; • наличие функции подавления дребезга контактов; • поддержка аналого-цифрового преобразователя (АЦП). 2.4. КОМПИЛЯТОРЫ Как уже говорилось, компилятор преобразует исходный текст программы в ма- шинный код, предназначенный для исполнения процессором. При этом большин- ство современных компиляторов для языков высокого уровня выполняет еще некоторые действия, пытаясь упростить код программы и/или увеличить ско- рость ее выполнения. Если вы решили освоить язык ассемблера, следует иметь в виду, что машинный код, произведенный компилятором, - прекрасный пример для изучения. Все примеры, приведенные в этой книге, написаны на языке С. Для компиля- ции программ использовался компилятор Lite фирмы HI-TECH Software. Язык С для. микроконтроллеров основан на стандарте ANSI С (он описывается в приложении). Код, производимый PICC Lite, весьма эффективен. Кроме того, этот компилятор прекрасно интегрируется со средой разработки MPLAB. Рассмотрим работу компилятора на примере. Пусть исходный текст програм- мы на С содержит строку А = В + (С * Для перевода этого фрагмента текста в машинный код компилятору требует- ся выполнить ряд действий. Большинство компиляторов при этом использует Разработка программного обеспечения 47 специальную структуру микропроцессора - так называемый стек (stack). Во время синтаксического разбора исходного текста все значения, встречаемые в выражении, помещаются в этот стек в порядке, обратном тому, который имел место в программе. По мере вычислений полученные значения «выталкивают- ся» из стека. 2.2. Основные типы операторов языка высокого уровня Оператор имя = if .) имя . имя [ = константа ] [тип] имя . . Тип Оператор присваивания Условный оператор Вызов подпрограммы переменной подпрограммы В первую очередь компилятору требуется определить тип оператора. Обычно выделяют пять типов, указанные в табл. 2.2. В первых трех случаях часть оператора, обозначенная многоточием, называет- ся выражением. Выражение - это константа или переменная либо несколько кон- стант и/или переменных, соединенных знаками арифметических, логических и других операций. В приложении приведен список операций, допустимых в PICC Lite. Аналогичные операции имеются в стандартном С и во многих других языках программирования. Значения констант и переменных, а также промежуточные результаты во вре- мя вычислений хранятся в стеке. Само выражение обычно переводится компиля- тором в так называемую постфиксную запись, в которой знаки операций распола- гаются после операндов в с D * + или с D * в В памяти компьютера постфиксное представление выражения часто хранится в виде двоичного дерева. Пример постфиксной записи выражения А = В + (С * D); приведен на рис. Приоритет операций в постфиксной записи определяется тем порядком, в ко- тором данные помещались в дерево (снизу вверх, справа налево), поэтому нет необходимости расставлять скобки. Для нашего примера компилятор выполнит такую последова- тельность операций со стеком: А = в + * D) • поместить в стек D; • поместить в стек С; • выполнить умножение двух верхних чисел в стеке, удалить их из стека и поместить в его вершину результат; • поместить в стек В; Рис. Дерево разбора 48 Устройства управления роботами • выполнить сложение двух верхних чисел в стеке, удалить их из стека и поме- стить в его вершину результат; • взять из вершины стека результат и поместить его в переменную А Читатель, возможно, помнит карманные калькуляторы, которые программиро- вались сходным образом. Такая запись вычислений называется обратной нотацией (Reverse Polish Notation - RPN). To, что результаты вычислений сохраняются в стеке, важно для операторов вызова подпрограмм, а также для условных операторов. Первые берут входные данные из стека и результат своей работы помещают туда же, а вторые в зависи- мости от значения, находящегося в вершине стека, либо выполняют, либо пропус- кают указанный в их теле оператор или последовательность операторов. Другой способ вычислений заключается в том, что промежуточные результа- ты хранятся в специальных ячейках памяти, которые задаются непосредственно. Проиллюстрируем это на нашем примере: Tempi = С; = D; - ТетрЗ = В; Tempi = Tempi * Tempi = Tempi + ТетрЗ; A = Tempi Но это более сложный способ, чем использование стека. Рассмотрим теперь пример, когда в вычислениях участвует элемент массива: А = В + ( С[4] * D Дерево выражения представлено на рис. 2.2. Последовательность операций такова: • поместить в стек D; • поместить в стек • поместить в стек элемент массива С, номер которого указан в вершине стека (удалив этот номер из стека); • выполнить умножение двух верхних чисел в стеке, удалить их из стека и по- местить в его вершину результат; • поместить в стек В; А = в + * D) • выполнить сложение двух верхних чисел в стеке, удалить их из стека и поместить в его вершину результат; • взять из вершины стека результат и поме- стить его в переменную А Это можно записать короче: Push D 4 Рис. 2.2. разбора в случав использования элемента массива Разработка программного обеспечения 49 Execute Push В Execute + Pop A Здесь использованы следующие сокращения: push - поместить указанное значение в вершину стека; pop - извлечь значение из вершины стека и записать его в указанную переменную; execute - выполнить операцию; квадратная скоб- ка обозначает элемент указанного массива, номер которого хранится в вершине стека. Теперь рассмотрим, как работают вызовы функций. Допустим, мы описали функцию двух переменных int varA, int varB) и используем ее при вычислении выражения А = в + А = В + D ) Дерево разбора для этого случая приведено на рис. 2.3. Последовательность операций со стеком такова: Push D Push 4 Push C[ Call Рис. 2.3. разбора В в случае функции Execute + Pop А . К тому моменту, когда производится вызов функции, в вершине стека уже находятся оба параметра, необходимых для ее выполнения. Параметр, указанный в тексте программы последним, находится в самой вершине стека, а тот, что ука- зан первым, - в стеке под ним. Это происходит потому, что параметры помещают- ся в стек в том же порядке, в каком они указаны в тексте программы. Теперь рассмотрим следующую функцию на языке С: int varA, varB) { varA = varA + return varA * varB; } Соответствующий машинный код, получившийся после компиляции, будет выглядеть следующим образом: Func: ; Вычислить varA = varA + Push StackTop - 1 ; Поместить varA в вершину стека. Push - 1 ; Поместить 1 в вершину стека. Execute ' + ; Сложить два числа в стеке. Pop ' StackTop - 1 ; Взять из вершины стека и по- 50 Устройства управления роботами его в varA. ; Вычислить и вернуть результат varA varB. Push StackTop - 1 ; Поместить в стека varA. Push StackTop ; Поместить в вершину стека varB. Execute ; Выполнить умножение двух верхних чисел в стеке. return Здесь в переменной StackTop хранится указатель на вершину стека, какой она была в момент вызова функции. Благодаря этому указателю функция получает доступ к своим параметрам. Фрагмент программы, вызывающий нашу функцию, выглядит примерно так: Push varA Push varB Call Pop StackTop - 1 Pop BitBacket Поместить результат в первый параметр функции, то есть в varA. Удалить из стека параметр - varB. ; Результат функции - в вершине стека. Результат работы реального компилятора может несколько отличаться от вы- шеприведенного. Например, многие компиляторы выполняют оптимизацию кода. Тогда фрагмент программы А = В + (С * (4 • будет заменен на А = В (С * 8); В результате получим последовательность стековых операций: Push 8 Push С Execute * Push В Execute + Pop A В случае, когда при разработке приложения используются несколько разных языков программирования (например, если в проекте участвуют несколько про- граммистов), каждый исходный файл, написанный одним из программистов на известном ему языке, переводится компилятором в отдельный объектный файл. Окончательную сборку нескольких объектных файлов выполняет компоновщик (linker). Если исходный текст программы очень велик (более 10000 строк кода), целесообразно также разбить его на несколько программных модулей. В этом слу- чае каждый модуль компилируется и отлаживается отдельно. Программист должен несколько важных моментов, касающихся ис- пользования компиляторов. Результатом работы дешевого компилятора может оказаться машинный код, выполняемый не быстрее, чем при использовании ин- терпретатора, и к тому же занимающий намного больше памяти. Следует заме- тить, что многие компиляторы языков высокого уровня весьма недешевы, в то время как ассемблеры для большинства микроконтроллеров совершенно бесплатны. Разработка программного обеспечения 51 Сообщения об ошибках (errors) и предупреждения (warnings), выдаваемые ком- пиляторами, порой не слишком ясны начинающему программисту (иногда они вовсе не соответствуют действительным ошибкам в программе). Никогда не загружайте программу в микроконтроллер, пока не добьетесь того, чтобы процесс компиляции прошел без всяких предупреждающих сообщений: ведь даже самые на первый взгляд безобидные сообщения во время компиляции могут привести к тому, что программа не будет работать как надо (или даже со- всем не будет работать). 2.5. И ЭМУЛЯТОРЫ Иногда отладка программы оказывается очень трудным делом, особенно если это программа для управления роботом. Позже в этой книге мы обсудим, ка- кими индикаторами следует снабдить автоматическое устройство, чтобы все- гда было ясно, в каком состоянии находится его управляющая программа. Но, так или иначе, прежде чем загрузить программу в память микроконтроллера, следует сначала позаботиться о том, чтобы отладить ее на персональном ком- пьютере. Несколько минут, потраченных на это, с лихвой компенсируют мно- гие часы мучений, которых может потребовать отладка программы на «живом» роботе. Для отладки программы без использования самого микроконтроллера, для которого она разрабатывалась, предназначено специальное инструментальное средство, называемое (simulator). Не имея доступа к реальной аппаратуре, симулятор работает с программной микроконтроллера, имитируя все его действия при выполнении не толь- ко арифметических или логических операций, но и многих команд ввода-вывода. К сожалению, симулятор не способен точно моделировать работу микроконтрол- лера с периферийными устройствами. Например, симулятор, входящий в состав MPLAB IDE, являясь превосходным инструментом разработки программ, все же оказывается не в состоянии поддерживать отладку некоторых сложных операций ввода-вывода. Перечислим основные особенности, которые следует иметь в виду при выборе • возможность отладки программ на уровне исходного текста (source-code level debugging); •• точное моделирование времени выполнения каждой команды (это не слиш- ком важно для высокоуровневых программ, приведенных в данной книге, но может оказаться необходимым при программировании на ассемблере проце- дур, работающих с периферийными устройствами); • возможность задавать набор и последовательность сигналов (stimulus), по- ступающих на входы виртуального микроконтроллера во время отладки про- граммы. Симулятор можно представить в виде набора нескольких блоков (рис. 2.4), соединенных между собой и управляемых с помощью пользовательского 52 Устройства управления роботами интерфейса симулятора. Реальные возможно, использу- ют большее количество блоков, но для нашего обсуждения хватит и тех пяти, что изображены на рис. 2.4. Рис. 2.4. Структура программного симулятора программ моделирует работу реальной памяти микроконтроллера, в которую с помощью программатора загружается управляющая программа. Мо- дель процессора имитирует все действия настоящего процессора, который выби- рает из памяти программ очередную команду и необходимые данные, вычисляет ре- зультат и помещает его в указанное место, после чего выбирает следующую команду и т.д. Регистровый файл моделирует работу регистров микроконтроллера. Модель устройств ввода-вывода (УВВ) предназначена для чтения из заранее подготовленного файла значений сигналов, которые необходимо подать на вход- ные порты виртуального микроконтроллера, и позволяет прочитать состояние его выходных портов на каждом этапе процесса отладки. Обратите внимание на важный момент во время отладки с помощью симуля- тора: при каждом прогоне программы можно обеспечить одну и ту же последова- тельность подаваемых на микроконтроллер входных сигналов. В противном слу- чае было бы сложно выявить ошибки в программе. Модель процессора по праву считается сердцем симулятора. Она позволяет не только имитировать выборку команд из памяти, их выполнение и обращение к регистрам микроконтроллера, но также управляет многими его аппаратными средствами, например системой прерываний. Важно подчеркнуть, что за работой симулятора программист следит в том же окне, где расположен исходный текст программы, то есть моделирование работы микроконтроллера осуществляется на уровне операторов языка высокого уровня, а не на уровне ассемблерных команд (хотя последнее тоже возможно). Другой способ отладки программ, более приближенный к реальности, предпо- лагает использование так называемого внутрисхемного эмулятора. Эмулятор Разработка программного обеспечения 53 (emulator) - это специальное устройство, которое подключается вместо микро- контроллера к той схеме, в которой он должен работать. Работой эмулятора с помощью специальной программы управляет персональный компьютер. В ре- зультате удается максимально точно имитировать поведение микроконтроллера в целевом устройстве - ведь теперь виртуальным оказывается только сам микро- контроллер, а все порты ввода-вывода и аппаратура, работающая с ними, выпол- нены «в железе» (рис. 2.5). При использовании эмулятора можно наблюдать ре- альные сигналы на выводах отлаживаемой схемы. Управляющая работой эмулятора программа Рис. 2.5. Подключение внутрисхемного эмулятора Скажу больше: эмулятор часто выполняется на основе того же самого микро- контроллера, который он должен эмулировать. К обычному микроконтроллеру добавляются специальные устройства для обеспечения связи процессора с ком- пьютером и управления пошаговым режимом работы. Именно так выполнены эмуляторы фирмы Microchip. Можно отметить два недостатка, присущих эмуляторам. Во-первых, настоя- щие эмуляторы весьма дороги. Поэтому выпускаются более простые, такие как Debugger фирмы Microchip. Этот инструмент предназначен для от- ладки устройств на базе микроконтроллеров PIC16F87x и поддерживает боль- шинство функций, присущих дорогим эмуляторам, но его возможности по отлад- ке некоторых команд, работающих с аппаратурой, ограничены. Во-вторых, эмулятор, вставляемый в разъем целевого устройства вместо мик- роконтроллера, должен иметь связь с компьютером. Часто это не столь уж страш- ное ограничение, но в случае отладки управляющей программы для мобильного устройства обеспечить такую связь иногда затруднительно. При выборе эмулятора в первую очередь необходимо выяснить, поддерживает ли он возможность отладки на уровне исходного текста программы. Если это не так, вам придется потратить уйму времени, разбираясь в машинном коде и пута- ясь в абсолютных адресах команд перехода. 2.6. ИНТЕГРИРОВАННЫЕ СРЕДСТВА РАЗРАБОТКИ Многие программисты уже привыкли ко всем удобствам, которые предостав- ляют интегрированные средства разработки (IDE), например Borland Turbo Pascal или Microsoft Visual Basic и Visual C++, входящие в состав Microsoft Visual 54 Устройства управления роботами Development Studio. Такие специалисты смогут почувствовать себя в знакомой среде, работая с программой MPLAB (рис. 2.6). Wrap INS Рис. 2.6. Интерфейс Это средство разработки программ для микроконтроллеров фирмы Microchip включает в себя следующие инструменты: • текстовый редактор (editor); • ассемблер (assembler); • компилятор (compiler); • компоновщик (linker); • симулятор (simulator); • эмулятор (emulator); • программатор (programmer). Назначение любой интегрированной среды заключается в том, чтобы предо- ставить программисту весь спектр услуг, которые требуются на этапах разработ- ки и отладки программы, избавив разработчика от необходимости обращаться к каким-либо другим инструментам и иметь дело с многочисленными файлами, образующимися при работе каждого из используемых инструментальных средств. Разработка программного обеспечения 55 Кроме того, интегрированная среда разработки программ должна поддержи- вать возможности: • добавления новых моделей • подключения других компиляторов языков высокого уровня; • работы с программаторами и эмуляторами. Некоторые (очень дорогие) средства разработки интегрируются с программа- ми автоматизированного проектирования электронных схем, что позволяет раз- работчику в одной среде проектировать и отлаживать не только программную, но и аппаратную часть своего устройства. В заключение отметим, что при выборе интегрированного средства разработ- ки программ для микроконтроллеров следует обращать внимание на то, поддер- живает ли встроенный редактор те же функции, что и обычные текстовые редак- торы, такие как Блокнот или WordPad. Если это не так, то могут возникнуть затруднения при попытке вставить фрагмент исходного текста программы из дру- гого файла (например, из HTML-страницы). Также необходимо убедиться, что формат выходного НЕХ-файла является стандартным и совместим с имеющимся программатором. |