Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
2822.pdf
Скачиваний:
7
Добавлен:
15.11.2022
Размер:
2.36 Mб
Скачать

ГОУВПО “Воронежский государственный технический университет”

Ю.С. Слепокуров

ПРОГРАММИРОВАНИЕ РОБОТОВ И РТС

Утверждено Редакционно-издательским советом университета в качестве учебного пособия

Воронеж 2006

УДК 681.142.2

Слепокуров Ю.С. Основы программирования микроконтроллеров: учеб. пособие/ Ю.С. Слепокуров. - Воронеж: ГОУВПО “Воронежский государственный технический университет”, 2006. - 222 с.

В пособии рассматривается принцип организации однокристальных микроконтроллеров MCS-51 и технологических контроллеров SIMATIC S7 фирмы SIEMENS. Дано описание основных операторов языков программирования, приведены примеры текстов программ, описано программное обеспечение, позволяющее произвести отладку программ пользователя.

Материал пособия соответствует рабочей программе дисциплины “Программирование роботов и РТС”, утвержденной методической комиссией факультета автоматизации и роботизации машиностроения.

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

Учебное пособие подготовлено к изданию на магнитном носителе и содержится в файле “Курс_лекций.pdf”.

Табл. 15. Ил. 24. Библиогр.: 6 назв.

Научный редактор д-р техн. наук, профессор А.И. Шиянов

Рецензенты: кафедра информатики и вычислительной техники Международного института компьютерных технологий ( зав. кафедрой д-р техн. наук В.П. Ирхин);

канд. техн. наук О.Ю. Таратынов.

©Слепокуров Ю.С., 2006

©Оформление. ГОУВПО “Воронежский государственный технический университет”, 2006

2

ТЕМА 1

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

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

Как уже было отмечено, по способу управления различают следующие системы управления роботов и соответствующие устройства управления:

программные устройства, в которых управление осуществляется по заранее составленной и остающейся неизменной в процессе реализации управляющей программе;

адаптивные устройства, в которых управление осуществляется в

функции

от информации о текущем состоянии внешней среды и само

го робота,

получаемой в процессе управления от сенсорных устройств;

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

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

Системы управления роботов и их аппаратное обеспечение развиваются не изолированно от систем управления другим технологическим оборудованием. Устройства управления первых роботов и их приводы были заимствованы из станкостроения. Поэтому перед рассмотрением эволюции аппаратуры управления роботов остановимся на этапах развития устройств числового программного управлении (УЧПУ) станков как предшественников этой аппаратуры.

Станки с ЧПУ появились в 1950-е гг. Это был первый и, как показало будущее, решающий шаг в развитии идеи гибкой автоматизации. Первые системы ЧПУ для станков, созданные в Массачусетсом технологическом институте США, программировались обучением с помощью центральной ЭВМ с записью на перфоленте и последующей передачей управляющих программ на станки.

Следующий, второй этап их развития в 1960-е гг. это прямое дистанционное централизованное управление несколькими станками от одной ЭВМ в режиме разделения времени. Управляющие программы при этом записывались уже на магнитных носителях (на магнитной ленте, диске и т. п.). Однако

3

доля станков с ЧПУ была еще очень мала не более0,1%.

Третий этап связан с революционным скачком в развитии и применении этой техники, который произошел в 1970-е гг. с появлением микропроцессоров. На их основе были созданы компьютерные УЧПУ для управления отдельными станками. При этом резко снизилась стоимость УЧПУ: с 50% от общей стоимости станка до 20% и менее. Одновременно была коренным образом повышена надежность систем управления, в том числе благодаря переходу от дистанционного по кабелю централизованного управления к j децентрализованному автономному управлению.

Четвертый этап развития УЧПУ это переход к иерархическим системам комбинированного управления, в которых местные компьютерные УЧПУ отдельных станков объединены централизованной сиcтемой управления от общей ЭВМ. Функции последней групповое управление всем производственным комплексом, содержащим эти станки, включая планирование, хранение библиотек управляющих программ, контроль отказов и другие общесистемные функции.

Современная тенденция развития этих систем это переход к распределенным системам ЧПУ путем постепенного уменьшения функций центрального управления с передачей их местным УЧПУ.

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

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

Первые отечественные унифицированные устройства дискретного позиционного программного управления типа УПМ были построены как синхронный микропрограммный автомат. Основным программным носителем был кассетный накопитель на магнитной ленте. Программирование осуществлялось методом обучения с помощью выносного пульта и с записью в блоке оперативной памяти. Устройство было ориентировано на датчики положения типа синуснокосинусных вращающихся трансформаторов и фотоэлектрические кодовые датчики.

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

4

Опыт создания и применения первых серийных устройств управления промышленных роботов и быстрорастущие требования к ним, по мере расширения областей применения роботов, привели к постепенному формированию следующих тенденций в развитии этих устройств:

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

развитие принципов параллельной обработки информации;

децентрализация управления с переходом к распределенным системам управления.

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

При достаточно сложной кинематике манипулятора для решения этой задачи даже в рамках обратной задачи кинематики может оказаться недостаточно одного микропроцессора. Тем более это относится к быстродействующим манипуляторам с непрерывным управлением, для которых требуется решать обратную задачу не кинематики, а динамики. Отсюда возникает необходимость использования на втором уровне управления многопроцессорных структур с распараллеливанием решения этой задачи. Оптимальное число микропроцессоров при этом, которое, естественно, зависит от их вычислительной мощности, составляет 3 - 4.

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

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

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

5

систем фирмами. Наборы таких модулей предлагают, например, фирмы: "Сименс" (ФРГ), "Фанук" (Япония), "Оливетти" (Италия).

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

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

языки общения человека-оператора с роботом через интеллектуальный интерфейс;

проблемно-ориентированные языки для описания выполняемых тех-

нологических

операций

в

координатах

рабочей

зоны робота

(Autopass, LM-GED, Robex и др.);

 

 

 

роботоориентированные языки для описания движений самого робота

(WAVE, VAL, HELP. TEACH и др.);

языки элементарных движений (SIGLA, RCL, RPL, ANORAD);

языки управления отдельными приводами.

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

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

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

ТЕМА 2

Позиционные системы счисления: десятичная, двоичная и шестнадцатеричная, взаимные переводы. BCD-код числа, его назначение и алгоритм преобразования. Логические команды двоичной математики.

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

6

счисления позиционные и непозиционные. Бывают ещё и смешанные системы счисления. Примером позиционной системы счисления является десятичная, непозиционной римская система счисления.

Система счисления называется позиционной, если значение числа в ней определяется как цифрами, принятыми в системе, так и положением (позицией) этих цифр в числе Закономерность построения позиционных чисел имеет простое математическое представление

Введем следующие обозначения:

q основание системы счисления. Основание системы это целое положительное число, большее 1 и равное максимальному количеству различных символов, употребляемых в данной системе счисления. В частности, для десятичной системы счисления q = 10;

ai любая цифра из множества цифр, принятых в данной системе счисления (в случае десятичной системы ai (0, 1, 2,..., 9);

i индекс, который обозначает номер позиции, занимаемой цифрой.

Позицию для целых чисел будем условно обозначать номерами 0, 1, 2, …n, а позиции в правильных дробях номерами -1, -2, ..., -m. Тогда любое число A в произвольной позиционной системе счисления с основанием q может быть записано следующим образом:

A =an *qn +an1 *qn1 +...+a0 *q0 +a1 *q1 +...+am *qm

где ai - удовлетворяет неравенству

0 ai q 1

и принимает в этом диапазоне только целые значения.

Величину ai будем называть весом i-го разряда. Тогда число А в десятичной системе счисления будет иметь вид:

A =an *10n +an1 *10n1 +...+a0 *100 +a1 *101 +...+am *10m

Для десятичной системы счисления понятие веса разряда соответствует общепринятым названиям позиций - единицы, десятки, сотни, десятые доли, сотые доли и т.д.

ДВОИЧНАЯ СИСТЕМА СЧИСЛЕНИЯ

Большинство современных компьютеров работает в двоичной системе счисления (q=2). Согласно соотношению для записи двоичного числа достаточно использовать только две цифры 0 и 1.

Для того, чтобы перевести целое положительное десятичное число в двоичную систему счисления, необходимо:

1.разделить исходное число на основание системы 2;

2.выделить целую часть частного и остаток. Остаток - младший разряд искомого двоичного числа. Целую часть частного принять за исходное число. Перейти к п.1.

7

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

Пример. Перевести число 37 в двоичную систему счисления.

37

 

2

 

 

 

 

 

(1)

 

18

 

2

 

 

 

 

 

(0)

 

9

2

 

 

 

 

 

 

(1)

4

2

 

 

 

 

 

 

(0)

2

2

 

 

 

 

 

 

(0)

1

 

скобках

записан остаток отделения)

 

 

Ответ:

3710 =1001012 .

 

 

Перевод целого десятичного числа в двоичное всегда является конечным процессом.

Правила перевода правильных десятинных дробей в двоичную систему состоят в следующем:

1.Умножить исходное число на основание системы 2.

2.Выделить целую и дробную часть произведения. Целая часть является старшим после запятой разрядом искомой двоичной дроби. Считать дробную часть произведения исходным числом, перейти к п.1.

3.Процесс перевода заканчивается в двух случаях:

1.дробная часть некоторого произведения равна 0;

2.достигнута заданная точность перевода.

Пример 1. Перевести А = 0.12510 из десятичной системы в двоичную систему счисления.

0,125

0,25

0,5

2

2

2

0,250

0,5

1,0

0

0

1

Ответ: 0.12510 =0.0012 .

Пример 2. Перевести А = 0.210 из десятичной системы в двоичную систему счисления.

0,2

0,4

0,8

0,6

0,2

2

2

2

2

2

0,4

0,8

1,6

1,2

0,4

0

0

1

1

0

Ответ: 0.210 =0.001102 .

Из дальнейшего перевода видно, что процесс вычислений в этом при-

8

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

наибольшая погрешность перевода составляет 25 0.031 Из процедуры перевода правильных десятичных дробей в двоичную

систему счисления и рассмотренного примера следует, что не все правильные десятичные дроби переводятся в двоичную систему счисления точно.

Если требуется перевести в двоичную систему счисления смешанное десятичное число, то для этого следует воспользоваться, сформулированными выше правилами отдельно для целой и дробной частей

Для перевода двоичных чисел в десятичную систему счисления можно было бы воспользоваться ранее сформулированными правилами, заменяя везде основание q = 2 на основание q = 10. Однако в этом случае все арифметические операции деления и умножения пришлось бы выполнять в двоичной системе счисления. Собственно таким путем и осуществляется обратный перевод в машинах.

Пример: Перевести в десятичную систему двоичное число 11100112 . Данное число можно представить как:

1*26 +1*25 +1*24 +0*23 +0*22 +1*21 +1*20 =11510 Ответ: 11100112 =11510 .

ШЕСТНАДЦАТEРИЧНАЯ СИСТЕМА СЧИСЛЕНИЯ

Двоичная система счисления, будучи естественной для ЭВМ, не удобна для восприятия человеком. Большое количество разрядов двоичного числа по сравнению с соответствующим десятичным, однообразное чередование единиц и нулей является источником ошибок и затрудняет чтение двоичного числа. Поэтому для удобства записи двоичных чисел, необходима такая система счисления, которая, с одной стороны, сохраняла бы свойства двоичной, а с другой в написании была бы близка к десятичной. Такими свойствами обладают системы с основанием 2n и, в первую очередь, системы с основанием 23 = 8 и 24 = 16, т.е. восьмеричная и шестнадцатеричная системы счисления.

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

Процедура перевода состоит в следующем:

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

2.Каждой тетраде ставятся в соответствие шестнадцатеричный

9

символ согласно таблице

0000

0

 

1000

8

0001

1

 

1001

9

0010

2

 

1010

A

0011

3

 

1011

B

0100

4

 

1100

C

0101

5

 

1101

D

0110

6

 

1110

E

0111

7

 

1111

F

Пример. Перевести число 1250810

в шестнадцатеричную систему счисления

и обратно.

 

 

 

 

 

120508

16

 

 

 

 

(12)

781

 

16

 

 

 

(13)

 

48

16

 

Ответ: 1250810 =30DC16 .

 

(0)

(3)

 

 

 

 

 

 

Обратный перевод можно осуществить следующим образом:

3*163 +0*162 +13*161 +12*160 =1250810 . BCD-КОД ЧИСЛА

Двоично-десятичный код числа (BCD-код) используется для передачи данных на устройства отображения информации и, в некоторых системах управления, в качестве исходных данных для формирования временных параметров.

Особенностью BCD-кода является то, что каждая тетрада числа, расположенного в какой-либо области памяти системы управления, представляет собой один десятичный разряд. Попадая в аккумулятор, такое число имеет совершенно другое значение, с точки зрения математики, нежели то, которое оно отображает.

Представим число 24610 в двоичном, шестнадцатеричном и BCD-коде:

Двоичное

шестнадцатеричное

BCD-код

1111_0110

F6

246

При этом BCD-код числа отображается в двоичном и шестнадцатеричном отображении следующим образом:

BCD-код

Двоичное

шестнадцатеричное

246

0010_0100_0110

246

Таким образом, если BCD-код числа 24610 попадает в аккумулятор процессора, то с точки зрения математических операций он воспринимается как 24616 или, соот-

ветственно, 2*162 +4*161 +6*160 =58210 .

БУЛЕВА БИТОВАЯ ЛОГИКА Булева битовая логика используется в следующих основных командах:

10

И (A) и ее инверсная форма И-НЕ (AN);

ИЛИ (O) и ее инверсная форма ИЛИ-НЕ (ON);

Исключающее ИЛИ (X) и ее инверсная форма, Исключающее ИЛИ-НЕ

(XN).

Таблица истинности внутри булевой логической цепи

Мнемоника

Команда

Состояние

Результат

 

 

операнда

 

AND

И

0

0

 

 

1

0

 

 

0

0

 

 

1

1

AND-NOT

И-НЕ

0

0

 

 

1

0

 

 

0

1

 

 

1

0

OR

ИЛИ

0

0

 

 

1

1

 

 

0

1

 

 

1

1

OR-NOT

ИЛИ-НЕ

0

1

 

 

1

0

 

 

0

1

 

 

1

1

XOR

Исключающее ИЛИ

0

0

 

 

1

1

 

 

0

1

 

 

1

0

XOR-NOT

Исключающее ИЛИ-НЕ

0

1

 

 

1

0

 

 

0

0

 

 

1

1

ТЕМА 3

Основные устройства внутренней архитектуры микро-ЭВМ, возможности аппаратного расширения систем управления на базе микро-ЭВМ MCS-51, особенности работы системы прерываний микро-ЭВМ, регистры прерываний.

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

11

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

Однако можно выделить некоторые черты архитектуры и системы команд, общие для всех современных микроконтроллеров, это:

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

интеграция в одном корпусе микросхемы (на одном кристалле) практически всех блоков, характерных для полнофункционального компьютера – процессора, ПЗУ, ОЗУ, устройств ввода-вывода, тактового генератора, контроллера прерываний и т.д. Поэтому в русскоязычной литературе подобные устройства часто называются однокристальные ЭВМ (ОЭВМ).

Микроконтроллеры обычно классифицируют по разрядности обрабатываемых чисел

четырехразрядныесамые простые и дешевые,

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

рии MCS-51 (Intel) и совместимые с ними, PIC (MicroChip), HC68 (Motorola), Z8 (Zilog) и др.

шестнадцатиразрядные - MCS-96 (intel)и др. - более высокопроизводительные но более дорогостоящие

тридцатидвухразрядные - обычно являющиеся модификациями универсаль-

ных микропроцессоров, например i80186 или i386EX.

Несмотря на непрерывное развитие и появление все новых и новых 16- и 32-разрядных микроконтроллеров и микропроцессоров, наибольшая доля мирового микропроцессорного рынка и по сей день остается за 8-разрядными устройст-

вами. Согласно данным компании Semico Research Corp., Phoenix, в 1996 году общий мировой объем продаж микроконтроллеров всех типов составил $11,4 миллиарда, при этом $5,56 миллиарда (или 48.6%) пришлось на долю 8-разрядных кристаллов. Это в 2,5 раза больше объема продаж ближайших конкурентов: 16разрядных микроконтроллеров ($2.1 млрд.) и DSP ($2.4). По всем прогнозам аналитических компаний на ближайшие 5 лет лидирующее положение 8-разрядных микроконтроллеров на мировом рынке сохранится.

В настоящее время среди всех 8-разрядных микроконтроллеров - семейство MCS-51 является несомненным чемпионом по количеству разновидностей и количеству компаний, выпускающих его модификации. Оно получило свое название от первого представителя этого семейства - микроконтроллера 8051, выпущенного в 1980 году на базе технологии HMOS. Удачный набор периферийных устройств, возможность гибкого выбора внешней или внутренней программной памяти и приемлемая цена обеспечили этому микроконтроллеру успех на рынке. С

12

точки зрения технологии микроконтроллер 8051 являлся для своего времени очень сложным изделием - в кристалле было использовано 128 тыс. транзисторов, что в 4 раза превышало количество транзисторов в 16-разрядном микропроцессо-

ре 8086.

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

В результате на сегодняшний день существует более 200 модификаций микроконтроллеров семейства 8051, выпускаемых почти 20-ю компаниями. Эти модификации включают в себя кристаллы с широчайшим спектром периферии: от простых 20-выводных устройств с одним таймером и 1К программной памяти до сложнейших 100-выводных кристаллов с 10-разрядными АЦП, массивами тайме- ров-счетчиков, аппаратными 16-разрядными умножителями и 64К программной памяти на кристалле. Каждый год появляются все новые варианты представителей этого семейства. Основными направлениями развития являются: увеличение быстродействия (повышение тактовой частоты и переработка архитектуры), снижение напряжения питания и потребления, увеличение объема ОЗУ и FLASH памяти на кристалле с возможностью внутрисхемного программирования, введение в состав периферии микроконтроллера сложных устройств типа системы управления приводами, CAN и USB интерфейсов и т.п.

Все микроконтроллеры из семейства MCS-51 имеют общую систему команд. Наличие дополнительного оборудования влияет только на количество регистров специального назначения.

Основными производителями клонов 51-го семейства в мире являются фирмы Philips, Siemens, Intel, Atmel, Dallas, Temic, Oki, AMD, MHS, Gold Star, Winbond, Silicon Systems и ряд других.

Микроконтроллер семейства 8051 имеют следующие аппаратные особенно-

сти:

внутреннее ОЗУ объемом 128 байт;

четыре двунаправленных побитно настраиваемых восьмиразрядных порта ввода-вывода;

два 16-разрядных таймера-счетчика;

встроенный тактовый генератор;

адресация 64 КБайт памяти программ и 64 Кбайт памяти данных;

две линии запросов на прерывание от внешних устройств;

интерфейс для последовательного обмена информацией с другими микроконтроллерами или персональными компьютерами.

Микроконтроллер 8751 снабжен УФ ПЗУ объемом 4 Кбайт. Функциональная схема микроконтроллера семейства 8051. Микроконтроллер выполнен на основе высокоуровневой n-МОП техноло-

гии. Через четыре программируемых параллельных порта ввода/вывода и один последовательный порт микроконтроллер взаимодействует с внешними устройст-

13

вами. Основу структурной схемы (рис. 1) образует внутренняя двунаправленная8битная шина, которая связывает между собой основные узлы и устройства микроконтроллера: резидентную память программ (RPM), резидентную память данных (RDM), арифметико-логическое устройство (ALU), блок регистров специальных функций, устройство управления (CU) и порты ввода/вывода (P0-P3).

8-битное арифметико-логическое устройство (ALU) может выполнять арифметические операции сложения, вычитания, умножения и деления; логические операции И, ИЛИ, исключающее ИЛИ, а также операции циклического сдвига, сброса, инвертирования и т.п. К входам подключены программнонедоступные регистры T1 и T2, предназначенные для временного хранения операндов, схема десятичной коррекции (DCU) и схема формирования признаков результата операции (PSW).

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

Простейшие операции автоматически образуют “тандемы” для выполнения таких операций, как, например, инкрементирование 16-битных регистровых пар. В ALU реализуется механизм каскадного выполнения простейших операций для реализации сложных команд. Так, например, при выполнении одной из команд условной передачи управления по результату сравнения в ALU трижды инкрементируется счётчик команд (PC), дважды производится чтение из RDM, выполняется арифметическое сравнение двух переменных, формируется 16-битный адрес перехода и принимается решение о том, делать или не делать переход по программе. Все перечисленные операции выполняются всего лишь за 2 мкс.

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

14

Рис. 3.1. Структурная схема микроконтроллера x8051

Таким образом, ALU может оперировать четырьмя типами информационных объектов: булевыми (1 бит), цифровыми (4 бита), байтными (8 бит) и адресными (16 бит). В ALU выполняется 51 различная операция пересылки или преобразования этих данных. Так как используется 11 режимов адресации (7 для данных и 4 для адресов), то путем комбинирования операции и режима адресации базовое число команд 111 расширяется до 255 из 256 возможных при однобайтном коде операции.

15

Упрощенная схема прерываний микро-ЭВМ 8051 показана на рисунке.

Внешние прерывания INT 0 и INT 1 могут быть вызваны либо уровнем, либо переходом сигнала из 1 в 0 на входах 8051 в зависимости от значений управляющих бит IT0 и IT1 в регистре TCON. От внешних прерываний устанавливаются флаги IE0 и IE1 в регистре TCON, которые инициируют вызов соответствующей программы обслуживания прерывания. Сброс этих флагов выполняется аппаратно только в том случае, если прерывание было вызвано по переходу (срезу) сигнала. Если же прерывание вызвано уровнем входного сигнала, то сбросом флага I должна управлять соответствующая подпрограмма обслуживания прерывания путем воздействия на источник прерывания с целью снятия им запроса.

Флаги запросов прерывания от таймеров TF0 и TF1 сбрасываются автоматически при передаче управления подпрограмме обслуживания. Флаги запросов прерывания RI и TI устанавливаются блоком управления приемопередатчика аппаратно, но сбрасываться должны программным путем.

Прерывания могут быть вызваны или отменены программой, так как все названные флаги программно доступны и могут быть установлены/ сброшены программой с тем же результатом, как если бы они были установлены/сброшены аппаратными средствами.

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

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

16

ваний.

Регистр масок прерывания (IE).

СимволПозиция

EA IE.7

IE.6

lE.5

ES IE.4

ET1 IE.3

EX1 IE.2

ET0 IE.1

EX0 IE.0

Имя и назначение

Снятие блокировки прерывания. Сбрасывается, программно для запрета всех прерываний независимо от состояний IE.4 - IE.0

Не используется Не используется

Бит разрешения прерывания, от приемопередатчика Установка/сброс программой для разрешения/запрета прерываний от флагов TI или RI .

Бит разрешения прерывания от таймера. Установка/сброс программой для разрешения/запрета прерываний от таймера 1 Бит разрешения внешнего прерывания 1. Установка/сброс программой для разрешения/запрета прерывания 1

Бит разрешения прерывания от таймера 0. Установка/сброс программой для разрешения/запрета прерываний от таймера 0 . Бит разрешения внешнего прерывания 0. Установка/сброс программой для разрешения/запрета прерывания 0

Регистр приоритетов прерываний (IP).

Символ

Позиция

Имя и назначение

-

IP.7 -

Не используется

IP.5

 

Бит приоритета приемопередатчика. Установка/сброс програм-

PS

IP.4

мой для присваивания прерыванию от приемопередатчика выс-

 

 

шего/низшего приоритета

РТ1

IP.3

Бит приоритета таймера 1. Установка/сброс программой для

присваивания прерыванию от таймера 1 высшего/низшего при-

 

 

оритета

РХ1

IP.2

Бит приоритета внешнего прерывания 1. Установка/сброс про-

граммой для присваивания высшего/низшего приоритета внеш-

 

 

нему прерыванию INT1

РТ0

IP.1

Бит приоритета таймера 0. Установка/сброс программой для

присваивания прерыванию от таймера 0 высшего/низшего при-

 

 

оритета

РХ0

IP.0

Бит приоритета внешнего прерывания 0. Установка/сброс про-

граммой для присваивания высшего/низшего приоритета внеш-

 

 

нему прерыванию INT0

 

 

Выполнение подпрограммы прерывания.

17

Система прерываний формирует аппаратный вызов (LCALL) соответствующей подпрограммы обслуживания, если она не заблокирована одним из следующих условий:

в данный момент обслуживается запрос прерывания равного или высокого уровня приоритета;

текущий машинный цикл — не последний в цикле выполняемой команды;

выполняется команда RETI или любая команда, связанная с обращением к регистрам IЕ или IP

Отметим, что если флаг прерывания был установлен, но по одному из ука-

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

По аппаратно сформированному коду LCALL система прерывания помещает в стек только содержимое счетчика команд (PC) и загружает в него адрес вектора соответствующей подпрограммы обслуживания. По адресу вектора должна быть расположена команда безусловной передачи управления (JMP) к начальному адресу подпрограммы обслуживания прерывания. В случае необходимости она должна начинаться командами записи в стек (PUSH) слова состояния программы (PSW), аккумулятора, расширителя, указателя данных и т.д. и должна заканчиваться командами восстановления из стека (POP). Подпрограммы обслуживания прерывания должны завершаться командой RETI, по которой в счетчик команд перезагружается из стека сохраненный адрес возврата в основную программу. Команда RET также возвращает управление прерванной основной программе, но при этом не снимут блокировку прерываний, что приводит к необходимости иметь программный механизм анализа окончания процедуры обслуживания данного прерывания.

ТЕМА 4

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

Память программ (ПЗУ).

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

Объем встроенной памяти программ, расположенной на кристалле микроконтроллера 8051 и 8751, равен 4 Кбайт. При обращении к внешней памяти программ все микроконтроллеры семейства 8051 всегда используют 16разрадный адрес, что обеспечивает им доступ к 64 Кбайт ПЗУ. Микроконтроллер обращается к программной памяти при чтении кода операции и операндов (используя счетчик команд PC), а также при выполнении команд переноса байта из памяти программ в аккумулятор. При выполнении команд переноса данных адре-

18

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

Память данных (ОЗУ).

Объем расположенной на кристалле памяти данных—128 байт. Объем

внешней памяти данных может достигать 64 Кбайт. Первые 32 байта организованы в четыре банка регистров общего назначения, обозначаемых соответственно банк 0 — банк 3. Каждый из них состоит из восьми регистров R0 — R7. В любой момент программе доступен только один банк регистров, номер которого содержится в третьем и четвертом битах слова состояния программы PSW (см. ниже).

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

Для обращения к внешней памяти данных используется только косвенная адресация с помощью регистров R0 и R1 или с помощью 16-разрядного регистрауказателя DPTR. Он относится к группе регистров специальных функций, и с его помощью можно адресовать все 64 Кбайта внешней памяти.

Часть памяти данных представляет собой так называемую битовую область, в ней имеется возможность при помощи специальных битовых команд адресовываться к каждому разряду ячеек памяти. Адрес прямо адресуемых битов может быть записан либо в виде (Адрес Байта ).(Разряд), например выражение 21.3 означает третий разряд ячейки памяти с адресом 21H, либо в виде абсолютного битового адреса. Соответствие этих двух способов адресации можно определить по таблице.

Адрес

Адреса битов по разрядам

байта

Adr

D7

D6

D5

D4

D3

D2

D1

D0

2FH

7F

7E

7D

7C

7B

7A

79

78

2EH

77

76

75

74

73

72

71

70

2DH

6F

6E

6D

6C

6B

6A

69

68

2CH

67

66

65

64

63

62

61

60

2BH

5F

5E

5D

5C

5B

5A

59

58

2AH

57

56

55

54

53

52

51

50

29H

4F

4E

4D

4C

4B

4A

49

48

28H

47

46

45

44

43

42

41

40

27H

3F

3E

3D

3C

3B

3A

39

38

26H

37

36

35

34

33

32

31

30

19

25H

2F

2E

2D

2C

2B

2A

29

28

24H

27

26

25

24

23

22

21

20

23H

1F

1E

1D

1C

1B

1A

19

18

22H

17

16

15

14

13

12

11

10

21H

0F

0E

0D

0C

0B

0A

09

08

20H

07

06

05

04

03

02

01

00

ПРИМЕЧАНИЕ. Адрес прямо адресуемых битов может быть записан либо в виде выражения (Адрес Байта ).(Разряд), например выражение 21.3 означает адрес третьего разряда ячейки памяти с адресом 21H, либо в виде абсолютного битового адреса, который для данного бита равен (см. таблицу) 0B.

К адресному пространству памяти данных примыкает адресное пространство регистров специальных функций SFR (Special Function Register).

Адрес

Символ

Наименование

0E0H

*АСС

Аккумулятор (Accumulator)

0F0H

Регистр расширитель аккумулятора (Multiplication Register)

0D0H

*PSW

Слово состояния программы (Program Status Word)

080Н

*P0

Порт 0 (SFR P0)

090H

*Р1

Порт 1 (SFR P1)

0A0H

*P2

Порт 2 (SFR P2)

0B0H

*P3

Порт 3 (SFR РЗ)

081H

SP

Регистр указатель стека (Stack Pointer)

083H

DPH

Старший байт регистра указателя данных DPTR (Data Pointer

 

 

High)

082H

DPL

Младший байт регистра указателя данных DPTR (Data Pointer

 

 

Low)

08CH

TH0

Старший байт таймера 0 ()

08AH

TL0

Младший байт таймера 0 ()

08DH

TH1

Старший байт таймера 1 ()

08BH

TL1

Младший байт таймера 1 ()

089H

TMOD

Регистр режимов таймеров счетчиков (Timer/Counter Mode Control

 

 

Register)

088H

*TCON

Регистр управления статуса таймеров (Timer/Counter Control Reg-

 

 

ister)

0B8H

*IP

Регистр приоритетов (Interrupt Priority Control Register)

0A8H

*IE

Регистр маски прерывания (Interrupt Enable Register)

087H

PCON

Регистр управления мощностью (Power Control Register)

098H

*SCON

Регистр управления приемопередатчиком (Serial Port Control Reg-

 

 

ister)

099H

SBUF

Буфер приемопередатчика (Serial Data Buffer)

20

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

Регистры-защелки SFR параллельных портов P0...P3 - служат для вводавывода информации.

Две регистровые пары с именами TH0, TL0 и TH1, TL1 представляют собой регистры, двух программно-управляемых 16-битных таймеровсчетчиков.

Режимы таймеров-счетчиков задаются с использованием регистра TMOD, а управление ими осуществляется с помощью регистра TCON.

Для управления режимами энергопотребления микро-ЭВМ используется регистр PCON.

Регистры IP и IE управляют работой системы прерываний микро-ЭВМ,

регистры SBUF и SCON — работой приемопередатчика последовательного порта.

Регистр-указатель стека SP в микро-ЭВМ рассматриваемого семейства — восьми битный. Он может адресовать любую область внутренней памяти данных. В отличие от микропроцессора КР580ВМ80, у микро-ЭВМ семейства 8051 стек «растет вверх», т.е. перед выполнением команды PUSH или CALL содержимое SP инкрементируется, после чего производится запись информации в стек. Соответственно при извлечении информации из стека регистр SP декрементируется после извлечения информации. В процессе инициализации микро-ЭВМ после сигнала сброса или при включении питающего напряжения в SP заносится код 07Н. Это означает, что первый элемент стека будет располагаться в ячейке памяти с адресом 08Н.

Регистр-указатель данных DPTR чаще всего используют для фиксации 16битного адреса в операциях обращения к внешней памяти программ и данных. С точки зрения программиста он может выступать как в виде одного 16-битного регистра, так и в виде двух независимых регистров DPL и DPH

Аккумулятор (АСС) является источником операнда и местом фиксации результата при выполнении арифметических, логических операций и ряда операций передачи данных. Кроме того, только с использованием аккумулятора могут быть выполнены операции сдвигов, проверка на нуль, формирование флага паритета и т.п. В распоряжении пользователя имеются 8 регистров общего назначения R0–R7 одного из четырёх возможных банков. При выполнении многих команд в АЛУ формируется ряд признаков операции (флагов), которые фиксируются в регистре PSW.

Регистр В используется как источник и как приемник при операциях умножения и деления, обращение к нему, как к регистру SFR, производится аналогично аккумулятору.

При выполнении ряда команд в арифметико-логическом устройстве (АЛУ) формируются признаки операций — флаги, которые фиксируются в регист-

ре PSW.

21

СимволПозиция

P PSW.0

- PSW.1

OV PSW.2

RS0 - PSW.3 - RS1 PSW.4

F0 PSW.5

АС PSW.6

C PSW.7

Регистр флагов (PSW)

Имя и назначение

Флаг приоритета. Устанавливается и сбрасывается аппаратно в каждом цикле команды и фиксирует нечетное/четное число единичных бит в аккумуляторе

Не используется Флаг переполнения. Устанавливается и сбрасывается аппаратно при

выполнении арифметических операций Биты выбора используемого банка регистров. Могут быть изменены

программным путем

RS0RS1БанкГраницы адресов ОЗУ

0

0

0

00H - 07H

1

0

1

08H - 0FH

0

1

2

10H - 17H

1

1

3

18H - 1FH

Флаг пользователя. Может быть установлен, сброшен или проверен программой пользователя

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

Флаг переноса. Устанавливается и сбрасывается как аппаратно, так и программным путем

Наиболее “активным” флагом PSW является флаг переноса, который принимает участие и модифицируется в процессе выполнения множества операций, включая сложение, вычитание и сдвиги. Кроме того, флаг переноса (C) выполняет функции “булева аккумулятора” в командах, манипулирующих с битами. Флаг переполнения (OV) фиксирует арифметическое переполнение при операциях над целыми числами со знаком и делает возможным использование арифметики в дополнительных кодах. ALU не управляет флагами селекции банка регистров (RS0, RS1), их значение полностью определяется прикладной программой и используется для выбора одного из четырёх регистровых банков.

В микропроцессорах, архитектура которых опирается на аккумулятор, большинство команд работают с ним, используя неявную адресацию. В Intel 8051 дело обстоит иначе. Хотя процессор имеет в своей основе аккумулятор, он может выполнять множество команд и без его участия. Например, данные могут быть переданы из любой ячейки RDM в любой регистр, любой регистр может быть загружен непосредственным операндом и т.д. Многие логические операции могут быть выполнены без участия аккумулятора. Кроме того, переменные могут быть инкрементированы, декрементированы и проверены без использования аккумулятора. Флаги и управляющие биты могут быть проверены и изменены аналогично

22

Работа с внешней памятью микроконтроллера x8051

Обращения к внешней памяти подразделяются на обращения к внешней памяти программ и обращения к внешней памяти данных. В первом случае для формирования сигнала, активирующего ПЗУ с программой, используется сигнал PSEN, во втором — сигналы RD и WR, активизирующие ОЗУ с данными.

Если используется 16-битовый адрес, старшие восемь бит выводятся через порт Р2 , где они сохраняются в течение всего цикла обращения к внешней памяти. Отметим, что выходные каскады порта Р2 имеют внутреннюю нагрузку, несколько отличающуюся от Р1 и РЗ , благодаря чему в SFR Р2 при выводе адресной информации вовсе не обязательно защелкивать все единицы. Добавим также, что при выводе адресной информации информация из SFR Р2, хотя и не присутствует на выводах микроЭВМ, но и не теряется, восстанавливаясь на них после окончания обращений к внешней памяти (если в процессе этих обращений SFR Р2 не был модифицирован).

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

Как уже отмечалось, на выводах порта P0 младший байт адреса мультиплексируется с данными. Сигналы адреса/ данных задействуют оба полевых транзистора выходного каскада порта P0. Таким образом, в этом случае выводы P0 уже не являются выводами с открытым стоком и не требуют внешних нагрузочных элементов.

Сигнал ALE используется для фиксации младшего байта адреса во внешнем регистре-защелке. Адресная информация достоверна в момент окончания сигнала

ALE.

Выводимый в цикле записи байт заносится в P0 непосредственно перед активацией сигнала WR и остается неизменным до окончания этого сигнала. В цикле чтения данные на выводах P0 для достоверного считывания должны быть установившимися к моменту окончания сигнала RD.

Во время обращения к внешней памяти CPU записывает 0FFH в SFR P0, уничтожая, таким образом, хранимую там информацию. Таким образом, использовать для записи порт P0 при работе с внешней памятью надо с известной долей осторожности.

Обращение к внешней памяти программ возможно в двух случаях:

когда сигнал ЕА активен, т.е. имеет нулевой уровень,

когда программный счетчик РС содержит число больше 0FFH. Следовательно, при использовании микро-ЭВМ, не имеющей встроенного

ПЗУ или не использующей его, на входе ЕА должен присутствовать сигнал с нулевым уровнем.

Когда CPU работает с внешней памятью программ, все линии порта Р2 используются для вывода старшего байта адреса и не могут быть использованы для

23

обычного ввода/вывода информации. При этом, как отмечалось выше, в SFR Р2 может быть занесена любая информация — адресная информация, выводимая через Р2, не зависит от состояния его SFR.

ТЕМА 5

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

Команды арифметических операций x8051.

Вданную группу входят 24 команды, краткое описание которых приведено

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

Втаблице также указаны тип команды (Т) в соответствии с таблицей, ее длина в байтах (Б) и время выполнения в машинных циклах (Ц).

Арифметические операции.

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Сложение аккумулятора с регистром

ADD A, Rn

00l01rrr

1

1

1

(A) (A) + (Rn)

(n=0÷7)

 

 

 

 

 

 

Сложение аккумулятора с прямоад-

ADD A, ad

00100101

3

2

1

(A) (A) + (ad)

ресуемым байтом

 

 

 

 

 

 

Сложение аккумулятора с байтом из

ADD A, @Ri

0010011i

1

1

1

(A) (A) + ((Ri))

РПД (i = 0,1)

 

 

 

 

 

 

Сложение аккумулятора с констан-

ADD A, #d

00100100

2

2

1

(A) (A) + #d

той

 

 

 

 

 

 

Сложение аккумулятора с регистром

ADDC A, Rn

00111rrr

1

1

1

(A) (A) + (Rn) +

и переносом

 

 

 

 

 

(C)

Сложение аккумулятора с прямоад-

ADDC A, ad

00110101

3

2

1

(A) (A) + (ad) +

ресуемым байтом и переносом

 

 

 

 

 

(C)

Сложение аккумулятора с байтом из

ADDC A,

0011011i

1

1

1

(A) (A) + ((Ri)) +

РПД и переносом

@Ri

 

 

 

 

(C)

Сложение аккумулятора с констан-

ADDC A, #d

00110100

2

2

1

(A) (A) + # d +

той и переносом

 

 

 

 

 

(C)

Десятичная коррекция аккумулятора

DA A

11010100

1

1

1

Если (А0…3)>9 или

 

 

 

 

 

 

((AC)=1),то(А0…3)

 

 

 

 

 

 

0…З) + 6, затем

 

 

 

 

 

 

если (А4…7) >9 или

 

 

 

 

 

 

((С)=1), то (А4…7)

 

 

 

 

 

 

4…7) + 6

Вычитание из аккумулятора регист-

SUBB A, Rn

10011rrr

1

1

1

(A) (A) - (C) -

ра и заёма

 

 

 

 

 

(Rn)

Вычитание из аккумулятора прямо-

SUBB A, ad

10010101

3

2

1

(A) (A) - (C) -

адресуемого байта и заема

 

 

 

 

 

((ad))

24

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Вычитание из аккумулятора байта

SUBB А, @R

1001011i

1

1

1

(A) (A) - (C) -

РПД и заема

 

 

 

 

 

((Ri))

Вычитание из аккумулятора кон-

SUBB А, d

10010100

2

2

1

(A) (A) - (C) - #d

станты и заема

 

 

 

 

 

 

Инкремент аккумулятора

INC А

00000100

1

1

1

(A) (A) + 1

Инкремент регистра

INC Rn

00001rrr

1

1

1

(Rn) (Rn) + 1

Инкремент прямоадресуемого байта

INC ad

00000101

3

2

1

(ad) (ad) + 1

Инкремент байта в РПД

INC @Ri

0000011i

1

1

1

((Ri)) ((Ri)) + 1

Инкремент указателя данных

INC DPTR

10100011

1

1

2

(DPTR) (DPTR)

 

 

 

 

 

 

+ 1

Декремент аккумулятора

DEC A

00010100

1

1

1

(A) (A) – 1

Декремент регистра

DEC Rn

00011rrr

1

1

1

(Rn) (Rn) – 1

Декремент прямоадресуемого байта

DEC ad

00010101

3

2

1

(ad) (ad) – 1

Декремент байта в РПД

DEC @Ri

0001011i

1

1

1

((Ri)) ((Ri)) - 1

Умножение аккумулятора на ре-

MUL AB

10100100

1

1

4

(B)(A) (A)*(В)

гистр В

 

 

 

 

 

 

Деление аккумулятора на регистр В

DIV AB

10000100

1

1

4

(B).(A) (A)/(В)

По результату выполнения команд ADD, ADDC, SUBB, MUL и DIV устанавливаются флаги PSW, структура которых приведена в таблице.

Флаг С устанавливается при переносе из разряда D7, т. е. в случае, если результат не помещается в восемь разрядов; флаг АС устанавливается при переносе из разряда D3 в командах сложения и вычитания и служит для реализации десятичной арифметики. Этот признак используется командой DAA.

Флаг OV устанавливается при переносе из разряда D6, т. е. в случае, если результат не помещается в семь разрядов и восьмой не может быть интерпретирован как знаковый. Этот признак служит для организации обработки чисел со знаком.

Наконец, флаг Р устанавливается и сбрасывается аппаратно. Если число единичных бит в аккумуляторе нечетно, то Р = 1, в противном случае Р = 0.

Команды логических операций микроконтроллера x8051.

В этой группе 25 команд, их краткое описание приведено в таблице. Нетрудно видеть, что эти команды позволяют выполнять операции над байтами: логическое И (/\ ), логическое ИЛИ ( \/), исключающее ИЛИ ((+)), инверсию (NOT), сброс в нулевое значение и сдвиг. В таблице также указаны тип команды (Т) в соответствии с таблицей, ее длина в байтах (Б) и время выполнения в машинных циклах (Ц).

Логические операции

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Логическое И аккумулятора и регистра

ANL A, Rn

01011rrr

1

1

1

(A) (A) AND

25

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

 

 

 

 

 

 

(Rn)

Логическое И аккумулятора и прямоадре-

ANL A, ad

01010101

3

2

1

(A) (A) AND

суемого байта

 

 

 

 

 

(ad)

Логическое И аккумулятора и байта из

ANL A, @Ri

0101011i

1

1

1

(A) (A) AND

РПД

 

 

 

 

 

((Ri))

Логическое И аккумулятора и константы

ANL A, #d

01010100

2

2

1

(A) (A) AND

 

 

 

 

 

 

#d

Логическое И прямоадресуемого байта и

ANL ad, A

01010010

3

2

1

(ad) (ad) AND

аккумулятора

 

 

 

 

 

(A)

Логическое И прямоадресуемого байта и

ANL ad, #d

01010011

7

3

2

(ad) (ad) AND

константы

 

 

 

 

 

#d

Логическое ИЛИ аккумулятора и регистра

ORL A, Rn

01001rrr

1

1

1

(A) (A) OR

 

 

 

 

 

 

(Rn)

Логическое ИЛИ аккумулятора и прямоад-

ORL A, ad

01000101

3

2

1

(A) (A) OR

ресуемого байта

 

 

 

 

 

(ad)

Логическое ИЛИ аккумулятора и байта из

ORL A, @Ri

0100011i

1

1

1

(A) (A) OR

РПД

 

 

 

 

 

((Ri))

Логическое ИЛИ аккумулятора и констан-

ORL A, #d

01000100

2

2

1

(A) (A) OR

ты

 

 

 

 

 

#d

Логическое ИЛИ прямоадресуемого байта

ORL ad, A

01000010

3

2

1

(ad) (ad) OR

и аккумулятора

 

 

 

 

 

(A)

Логическое ИЛИ прямоадресуемого байта

ORL ad, #d

01000011

7

3

2

(ad) (ad) OR

и константы

 

 

 

 

 

#d

Исключающее ИЛИ аккумулятора и реги-

XRL A, Rn

01101rrr

1

1

1

(A) (A) XOR

стра

 

 

 

 

 

(Rn)

Исключающее ИЛИ аккумулятора и пря-

XRL A, ad

01100101

3

2

1

(A) (A) XOR

моадресуемого байта

 

 

 

 

 

(ad)

Исключающее ИЛИ аккумулятора и байта

XRL A, @Ri

0110011i

1

1

1

(A) (A) XOR

из РПД

 

 

 

 

 

((Ri))

Исключающее ИЛИ аккумулятора и кон-

XRL A, #d

01100100

2

2

1

(A) (A) XOR

станты

 

 

 

 

 

#d

Исключающее ИЛИ прямоадресуемого

XRL ad, A

01100010

3

2

1

(ad) (ad) XOR

байта и аккумулятора

 

 

 

 

 

(A)

Исключающее ИЛИ прямоадресуемого

XRL ad, #d

01100011

7

3

2

(ad) (ad) XOR

байта и константы

 

 

 

 

 

#d

Сброс аккумулятора

CLR A

11100100

1

1

1

(A) 0

Инверсия аккумулятора

CPL A

11110100

1

1

1

(A) NOT(A)

Сдвиг аккумулятора влево циклический

RL A

00100011

1

1

1

(An+1) (An),

 

 

 

 

 

 

n=0÷6, (A0)

 

 

 

 

 

 

(A7)

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

RLC A

00110011

1

1

1

(An+1) (An),

 

 

 

 

 

 

n=0÷6

 

 

 

 

 

 

(A0) (C), (C)

26

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

 

 

 

 

 

 

(A7)

Сдвиг аккумулятора вправо циклический

RR A

00000011

1

1

1

(An) (An+1),

 

 

 

 

 

 

n=0÷6, (A7)

 

 

 

 

 

 

(A0)

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

RRC A

00010011

1

1

1

(An) (An+1),

 

 

 

 

 

 

n=0÷6

 

 

 

 

 

 

(A7) (C), (C)

 

 

 

 

 

 

(A0)

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

SWAP A

11000100

1

1

1

(A0…3) ↔ (A4…7)

Команды операций над битами микроконтроллера 8051.

Группа состоит из 12 команд, краткое описание которых приведено в таблице. Эти команды позволяют выполнять операции над отдельными битами: сброс, установку, инверсию бита, а также логические И (/\) и ИЛИ (\/). В качестве "логического" аккумулятора, участвующего во всех операциях с двумя операндами, выступает признак переноса С (разряд D7 PSW), в качестве операндов могут использоваться 128 бит из резидентной памяти данных и регистры специальных функций, допускающие адресацию отдельных бит.

В таблице также указаны тип команды (Т) в соответствии с таблицей, ее длина в байтах (Б) и время выполнения в машинных циклах (Ц).

Операции с битами

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Сброс переноса

CLR С

11000011

1

1

1

(C) 0

Сброс бита

CLR bit

11000010

4

2

1

(b) 0

Установка переноса

SETB С

11010011

1

1

1

(C) 1

Установка бита

SETB bit

11010010

4

2

1

(b) 1

Инверсия переноса

CPL С

10110011

1

1

1

(C) NOT(C)

Инверсия бита

CPL bit

10110010

4

2

1

(b) NOT(b)

Логическое И бита и переноса

ANL С, bit

10000010

4

2

2

(C) (C) AND

 

 

 

 

 

 

(b)

Логическое И инверсии бита и

ANL С, /bit

10110000

4

2

2

(C) (C) AND

переноса

 

 

 

 

 

(NOT(b))

Логическое ИЛИ бита и переноса

ORL С, bit

01110010

4

2

2

(C) (C) OR

 

 

 

 

 

 

(b)

Логическое ИЛИ инверсии бита и

ORL С, /bit

10100000

4

2

2

(C) (C) OR

переноса

 

 

 

 

 

(NOT(b))

Пересылка бита в перенос

MOV С, bit

10100010

4

2

1

(C) (b)

Пересылка переноса в бит

MOV bit, С

10010010

4

2

2

(b) (C)

27

ТЕМА 6

Группа команд передачи управления, подпрограммы, примеры “блочного” программирования.

Команды передачи управления микроконтроллера 8051.

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

В таблице также указаны тип команды (Т) в соответствии с таблицей, ее длина в байтах (Б) и время выполнения в машинных циклах (Ц).

Команды передачи управления

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Длинный переход в полном объ-

LJMP ad16

00000010

12

3

2

(PC) ad16

еме ПП

 

 

 

 

 

 

 

 

 

 

 

 

 

Абсолютный переход внутри

AJMP ad11

a10a9a80000

6

2

2

(PC) (PC) + 2, (PC0-

страницы в 2 Кб

 

1

 

 

 

10) ad11

Короткий относительный пере-

SJMP rel

10000000

5

2

2

(PC) (PC) + 2, (PC)

ход внутри

 

 

 

 

 

(PC) +rel

страницы в 256 байт

 

 

 

 

 

 

Косвенный относительный пе-

JMP

01110011

1

1

2

(PC) (A) + (DPTR)

реход

@A+DPTR

 

 

 

 

 

Переход, если аккумулятор ра-

JZ rel

01100000

5

2

2

(PC)(PC)+2, если

вен нулю

 

 

 

 

 

(A)=0, то

 

 

 

 

 

 

(PC)(PC)+rel

Переход, если аккумулятор не

JNZ rel

01110000

5

2

2

(PC)(PC)+2, если

равен нулю

 

 

 

 

 

(A)≠0, то

 

 

 

 

 

 

(PC)(PC)+rel

Переход, если перенос равен

JC rel

01000000

5

2

2

(PC)(PC)+2, если

единице

 

 

 

 

 

(С)=1, то

 

 

 

 

 

 

(PC)(PC)+rel

Переход, если перенос равен

JNC rel

01010000

5

2

2

(PC)(PC)+2, если (С)=0,

нулю

 

 

 

 

 

то (PC)(PC)+rel

Переход, если бит равен едини-

JB bit, rel

00100000

11

3

2

(PC)(PC)+3, если (b)=l,

це

 

 

 

 

 

то (PC)(PC)+rel

Переход, если бит равен нулю

JNB bit, rel

00110000

11

3

2

(PC)(PC)+3, если (b)=0,

 

 

 

 

 

 

то (PC)(PC)+rel

Переход, если бит установлен, с

JBC bit, rel

00010000

11

3

2

(PC) (PC) + 3, если

последующим сбросом бита

 

 

 

 

 

(b)=1,

 

 

 

 

 

 

то (b) 0 и (PC)(PC) +

 

 

 

 

 

 

rel

Декремент регистра и переход,

DJNZ Rn, re

11011rrr

5

2

2

(PC) (PC) + 2, (Rn)

если не нуль

 

 

 

 

 

(Rn) - 1,

28

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

 

 

 

 

 

 

если (Rn) ≠ 0, то (PC)

 

 

 

 

 

 

(PC) + rel

Декремент прямоадресуемого

DJNZ ad, rel

11010101

8

3

2

(PC) (PC) + 2, (ad)

байта и переход, если не нуль

 

 

 

 

 

(ad) - 1,

 

 

 

 

 

 

если (ad) ≠ 0, то (PC)

 

 

 

 

 

 

(PC) + rel

Сравнение аккумулятора с пря-

CJNE A, ad,

10110101

8

3

2

(PC) (PC) + 3,если (A)

моадресуемым байтом и пере-

rel

 

 

 

 

≠ (ad), то (PC) (PC) +

ход, если не равно

 

 

 

 

 

rel,если (A) < (ad), то (C)

 

 

 

 

 

 

1, иначе (C) 0

Сравнение аккумулятора с кон-

CJNE A, #d,

10110100

10

3

2

(PC) (PC) + 3,если (A)

стантой и

rel

 

 

 

 

≠ #d, то (PC) (PC) +

переход, если не равно

 

 

 

 

 

rel,если (A) < #d, то (C)

 

 

 

 

 

 

1, иначе (С) 0

 

 

 

 

 

 

 

Сравнение регистра с констан-

CJNE Rn,

10111rrr

10

3

2

(PC) (PC) + 3,если (Rn)

той и переход, если не равно

#d, rel

 

 

 

 

≠ #d, то (PC) (PC) +

 

 

 

 

 

 

rel,если (Rn) < #d, то (C)

 

 

 

 

 

 

1, иначе (С) 0

Сравнение байта в РПД с кон-

CJNE

1011011i

10

3

2

(PC) (PC) + 3,если

стантой и переход, если не рав-

@Ri,#d,rel

 

 

 

 

((Ri)) ≠ #d, то (PC) (PC)

но

 

 

 

 

 

+ rel,если ((Ri)) < #d, то

 

 

 

 

 

 

(C) 1, иначе (C) 0

Длинный вызов подпрограммы

LCALL adl6

00010010

12

3

2

(PC) (PC) + 3, (SP)

 

 

 

 

 

 

(SP) +1,

 

 

 

 

 

 

((SP)) (PC0…7), (SP)

 

 

 

 

 

 

(SP) + 1,

 

 

 

 

 

 

((SP)) (PC8…15), (PC)

 

 

 

 

 

 

ad16

Абсолютный вызов подпро-

ACALL

a10a9a81000

6

2

2

(PC) (PC) + 2, (SP)

граммы в пределах страницы в 2

ad11

1

 

 

 

(SP) + 1,

Кб

 

 

 

 

 

((SP)) (PC0…7), (SP)

 

 

 

 

 

 

(SP) + 1,

 

 

 

 

 

 

((SP)) (PC8…15), (PC0-10)

 

 

 

 

 

 

ad11

Возврат из подпрограммы

RET

00100010

1

1

2

(PC8…15) ((SP)), (SP)

 

 

 

 

 

 

(SP) - 1,

 

 

 

 

 

 

(PC0…7) ((SP)), (SP)

 

 

 

 

 

 

(SP) – 1

Возврат из подпрограммы обра-

RETI

00110010

1

1

2

(PC8…15) ((SP)), (SP)

ботки

 

 

 

 

 

(SP) - 1,

прерывания

 

 

 

 

 

(PC0…7) ((SP)), (SP)

 

 

 

 

 

 

(SP) – 1

29

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Пустая операция

NOP

00000000

1

1

1

(PC) (PC) + 1

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

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

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

SJMP.

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

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

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

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

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

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

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

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

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

при выполнении команды.

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

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

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

30

WAIT: CJNE A, P0, WAIT

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

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

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

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

Команды пересылки данных микроконтроллера 8051.

Эта группа представлена 28 командами, их краткое описание приведено в таблице, где также указаны тип команды (Т) в соответствии с таблицей, ее длина в байтах (Б) и время выполнения в машинных циклах (Ц).

Команды передачи данных

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

Пересылка в аккумулятор из регистра

MOV A, Rn

11101rrr

1

1

1

(A) (Rn)

(n=0÷7)

 

 

 

 

 

 

Пересылка в аккумулятор прямоадресуемого

MOV A, ad

1110010

3

2

1

(A) (ad)

байта

 

1

 

 

 

 

Пересылка в аккумулятор байта из РПД

MOV A, @Ri

1110011

1

1

1

(A) ((Ri))

(i=0,1)

 

 

 

 

 

 

Загрузка в аккумулятор константы

MOV A, #d

0111010

2

2

1

(A) #d

 

 

0

 

 

 

 

Пересылка в регистр из аккумулятора

MOV Rn, A

11111rrr

1

1

1

(Rn) (A)

Пересылка в регистр прямоадресуемого бай-

MOV Rn, ad

10101rrr

3

2

2

(Rn) (ad)

та

 

 

 

 

 

 

Загрузка в регистр константы

MOV Rn, #d

01111rrr

2

2

1

(Rn) #d

Пересылка по прямому адресу аккумулятора

MOV ad, A

1111010

3

2

1

(ad) (A)

 

 

1

 

 

 

 

Пересылка по прямому адресу регистра

MOV ad, Rn

10001rrr

3

2

2

(ad) (Rn)

Пересылка прямоадресуемого байта по пря-

MOV add, ads

1000010

9

3

2

(add) (ads)

мому адресу

 

1

 

 

 

 

Пересылка байта из РПД по прямому адресу

MOV ad, @R

1000011

3

2

2

(ad) ((Ri))

Пересылка по прямому адресу константы

MOV ad, #d

0111010

7

3

2

(ad) #d

31

Название команды

Мнемокод

КОП

Т

Б

Ц

Операция

 

 

1

 

 

 

 

Пересылка в РПД из аккумулятора

MOV @Ri, A

1111011

1

1

1

((Ri)) (A)

Пересылка в РПД прямоадресуемого байта

MOV @Ri, ad

0110011

3

2

2

((Ri)) (ad)

Пересылка в РПД константы

MOV @Ri, #d

0111011

2

2

1

((Ri)) #d

Загрузка указателя данных

MOV DPTR,

1001000

13

3

2

(DPTR)

 

#d16

0

 

 

 

#d16

Пересылка в аккумулятор байта из ПП

MOVC A,

1001001

1

1

2

((A)

 

@A+DPTR

1

 

 

 

+(DPTR))

Пересылка в аккумулятор байта из ПП

MOVC A,

1000001

1

1

2

(PC)

 

@A+PC

1

 

 

 

(PC)+1, (A)

 

 

 

 

 

 

(A)+(PC))

Пересылка в аккумулятор байта из ВПД

MOVX A,

1110001

1

1

2

(A) ((Ri))

 

@Ri

 

 

 

 

 

Пересылка в аккумулятор байта из расши-

MOVX

1110000

1

1

2

(A)

ренной ВПД

A,@DPTR

0

 

 

 

((DPTR))

Пересылка в ВПД из аккумулятора

MOVX @Ri,

1111001

1

1

2

((Ri)) (A)

 

A

 

 

 

 

 

Пересылка в расширенную ВПД из аккуму-

MOVX

1111000

1

1

2

((DPTR))

лятора

@DPTR,A

0

 

 

 

(A)

 

 

 

 

 

 

 

Загрузка в стек

PUSH ad

1100000

3

2

2

(SP) (SP) +

 

 

0

 

 

 

1, ((SP))

 

 

 

 

 

 

(ad)

Извлечение из стека

POP ad

1101000

3

2

2

(ad) (SP),

 

 

0

 

 

 

(SP) (SP) -

 

 

 

 

 

 

1

Обмен аккумулятора с регистром

XCH A, Rn

11001rrr

1

1

1

(A) ↔ (Rn)

Обмен аккумулятора с прямоадресуемым

XCH A, ad

1100010

3

2

1

(A) ↔ (ad)

байтом

 

1

 

 

 

 

Обмен аккумулятора с байтом из РПД

XCH A, @Ri

1100011

1

1

1

(A) ↔ ((Ri))

Обмен младших тетрад аккумулятора и бай-

XCHD A,

1101011

1

1

1

(A0…3)

та РПД

@Ri

 

 

 

 

↔((Ri)0…3)

По команде MOV выполняется пересылка данных из второго операнда в первый. Эта команда не имеет доступа ни к внешней памяти данных, ни к памяти программ. Для этих целей предназначены команды M0VX и MOVC соответственно. Первая из них обеспечивает чтение/запись байт из внешней памяти данных, вторая – чтение байт из памяти программ.

По команде XCH выполняется обмен байтами между аккумулятором и ячейкой РПД, а по команде XCHD – обмен младшими тетрадами (битами 0 – 3).

Команды PUSH и РОР предназначены соответственно для записи данных в стек и их чтения из стека. Размер стека ограничен лишь размером резидентной памяти данных. В процессе инициализации микро-ЭВМ после сигнала сброса или

32

при включении питающего напряжения в SP заносится код 07Н. Это означает, что первый элемент стека будет располагаться в ячейке памяти с адресом 08Н.

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

MOV A, PSW

которая преобразуется Ассемблером к виду

MOV А, 0D0h (E5 D0),

где Е5 – код операции, а D0 – операнд (адрес PSW).

Кроме того, следует отметить, что в микро-ЭВМ аккумулятор имеет два различных имени в зависимости от способа адресации: А – при неявной адресации (например, MOV A, R0) и АСС – при использовании прямого адреса. Первый способ предпочтительнее, однако, не всегда применим.

Пример программы. Условный переход с N-ветвлениями

Обычно в ситуациях условного перехода с N-ветвлениями не все возможные адреса известны во время ассемблирования. Из множества небольших программ выбирается одна в соответствии со значением индекса переменной, который определяется во время выполнения программы. Наиболее эффективным путем решения этой проблемы является использование инструкции MOVC и инструкции перехода с косвенной адресацией, которая использует небольшую таблицу смещений ПЗУ для указателей начальных адресов соответствующих подпрограмм.

Инструкция JMP @A+DPTR выполняет переход с косвенной адресацией по адресу, определяемому в процессе выполнения программы. По этой инструкции выполняется сложение без знака 8-разрядного содержимого аккумулятора с содержимым 16-разрядного указателя данных (аналогично тому, как действует инструкция MOVC A, @A+DPTR). Результат сложения загружается в счетчик команд и используется в качестве адреса для последующих выборок инструкций, 16-разрядное сложение выполняется так, что перенос из младшего байта распространяется на старший байт. При этом содержимое аккумулятора и DPTR сохраняется.

Ниже приводится пример подпрограммы, в которой байт памяти ЗУ считывается в аккумулятор из одной из четырех возможных областей памяти, определяемой содержимым переменной MEMSEL. Адрес считываемого байта определяется содержимым R0. Эта подпрограмма может использоваться для терминалов, когда четыре различных модели принтеров используют один и тот же код памяти ПЗУ, но при этом используются различные типы (и размеры) буферной памяти при различных скоростях и режимах работы.

MEMSEL EQU R3

33

BRANCH: MOV

A, MEMSEL

 

MOV

 

DPTR, #TBL

 

MOVC

A, @A+DPTR

 

JMP

 

@A+DPTR

TBL:

DB

MSP0-TBL

 

DB

MSP1-TBL

 

DB

MSP2-TBL

 

DB

MSP3-TBL

MSP0:

MOV

A, @R0

;Чтение внутренней памяти

 

RET

 

 

 

MSP1:

MOVX

 

A, @R0

;Чтение 256 байт внешней

 

RET

 

;памяти ЗУ

MSP2:

MOV

 

DPL, R0 ;Чтение 64К внешней памяти

 

MOV

 

DPH, R1 ;ЗУ

 

MOVX

A, @DPTR

 

RET

 

 

 

MSP3:

MOV

 

A, R1 ;Чтение 4К байт внешней

 

ANL

 

A, #07H

;памяти ЗУ

ANL PI, #11111000В

ORL PI, A

MOVX A,@R0

RET

Для использования этого подхода необходимо, чтобы размер таблицы адре-

34

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

ТЕМА 7

Прием информации через порты ввода/вывода, прием информации таймером/счетчиком, прием информации по входам внешних прерываний.

ПОРТЫ ВВОДА/ВЫВОДА ИНФОРМАЦИИ

Все четыре порта МК51 предназначены для ввода или вывода информации побайтно. Схемотехника портов ввода/вывода показана на рисунке (порты 1 и 2 имеют такую же структуру, как и порт 3). Каждый порт содержит управляемые регистр-защелку, входной буфер и выходной драйвер.

Выходные драйверы портов 0 и 2, а также входной буфер порта 0 используются

при обращении к внешней памяти (ВП).

При этом через порт 0 в режиме вре-

менного мультиплексирования

сначала

выводится младший байт адреса ВП,

а затем выдается или принимается байт данных. Через порт 2 выводится старший байт адреса в тех случаях, когда разрядность адреса равна 16 бит.

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

ствованы путем записи 1 в

соответствующие биты регистра-защелки (Р3.0-Р3.7)

порта 3.

 

 

 

 

 

 

 

 

 

Символ

Позиция

Имя и назначение

 

 

RD

P3.7

Чтение. Активный сигнал низкого уровня форми-

 

 

 

 

руется аппаратно при обращении к ВПД.

 

 

WR

P3.6

Запись. Активный сигнал низкого уровня форми-

 

 

 

 

руется аппаратно при обращении к ВПД.

 

 

T1

P3.5

Вход таймера/счетчика 1.

 

 

T0

P3.4

Вход таймера/счетчика 0.

 

 

INT1

P3.3

Вход запроса прерывания 1. Воспринимается

 

 

 

 

сигнал низкого уровня или срез.

 

 

INT0

P3.2

Вход запроса прерывания 0. Воспринимается

 

 

 

 

сигнал низкого уровня или срез.

 

 

TxD

P3.1

Выход передатчика последовательного порта

 

 

 

 

в режиме УАПП. Выход синхронизации в режиме

 

 

 

 

сдвигающего регистра.

 

 

RxD

P3.0

Вход приемника последовательного порта в ре-

 

 

 

 

жиме УАПП. Ввод/вывод данных в режиме сдви-

 

 

 

 

гающего регистра.

 

 

 

 

 

 

35

Порт 0 является двунаправленным, а порты 1, 2 и 3 - квазидвунаправленными. Каждая линия портов может быть использована независимо для ввода и вывода информации. Для того чтобы некоторая линия порта использовалась для вода, в D-триггер регистра-защелки порта должна быть записана 1, которая закрывает МОП-транзистор выходной цепи.

Порты ввода/вывода: а) порт 0, б) порты 1-3

Выходные каскады триггеров SFR портов Р1 — РЗ выполнены на полевых транзисторах с внутренней нагрузкой, в то время как аналогичные каскады триггеров SFR P0—на транзисторах с открытым стоком. Каждая линия любого из портов может независимо использоваться как для ввода, так и для вывода информации (для линий портов P0 и Р2 это справедливо тогда, когда они не используются для обращения к внешней памяти).

Для перевода любой линии портов Р1 — РЗ в режим ввода информации необходимо в соответствующий разряд SFR занести 1. При этом выходной полевой транзистор отключается. Внутренний нагрузочный резистор как бы «подтягивает» потенциал вывода к напряжению питания, в то время как внешняя нагрузка может сделать его нулевым. Выходные каскады порта P0 имеют иную структуру. Нагрузочный полевой транзистор линии порта включен только тогда, когда порт выводит 1 при обращении к внешней памяти. В остальных случаях нагрузочный транзистор отключен. Таким образом, при работе в режиме обычного ввода-вывода информации (как, например, порт Р1) выходные каскады порт» P0 представляют собой ступени на транзисторах с открытым стоком. Запись 1 в соответствующий

36

бит SFR отключает и второй транзистор, что приводит к тому, что вывод БИС оказывается под «плавающим» потенциалом. Это позволяет использовать линии порта P0 как выводы с высокоимпедансным состоянием.

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

Пример 1 Ожидание статического сигнала. Датчик подключен к линии 3 порта 1. Программа ожидания замыкания контактов

WAIT0: JNB P1.3,WAIT0 ;Ожидание размыкания контактов датчика

Пример 2. Ожидание размыкания контактов

WAIT1: JB P1.3,WAIT1 ;Ожидание размыкания контактов

Пример 3. Ожидание импульсного сигнала. В этом случае микроконтроллер должен обнаружить не только появление сигнала, но и его окончание.

WAITC:

JB P1.3,WAITC

;Ожидание Р1.3=0

WAIT0:

JNB P1.3,WAIT0

;Ожидание Р1.3=1

Пример 4.

Подсчет числа импульсов между двумя событиями.

Предположим, что необходимо подсчитать количество деталей сошедших с конвейера от момента его пуска до выключения. Факт схода детали с конвейера фиксируется фотоэлементом, на выходе которого формируется импульс. Датчик работы конвейера подключен к линии 3 порта 2.

 

MOV TMOD,#01000010B

;Настройка счетчика 1

 

MOV TH1,#0

;Сброс счетчика деталей

 

MOV TL1,#0

 

WAIT0:

JB P2.3,WAIT0

;Ожидание включения конвейера

 

SETB TCON.6

;Пуск счетчика 1

WAIT1:

JNB P2.3,WAIT1

;Ожидание выключения конвейера

 

CLR TCON.6

;Выключение счетчика 1

ТЕМА 8

Режимы работы таймера/счетчика и их программирование, организация подсчета внешних событий, отсчет временных интервалов, прием и выдача сигналов с квантованием по времени.

Таймеры / счетчики микроконтроллеров семейства 8051.

В базовых моделях семейства имеются два программируемых 16-битных таймера/счетчика (T/C0 и T/C1), которые могут быть использованы как в качестве таймеров, так и в качестве счетчиков внешних событий. В первом случае содержимое соответствующего таймера/счетчика (далее для краткости Т/С) инкремен-

37

тируется в каждом машинном цикле, т.е. через каждые 12 периодов колебаний кварцевого резонатора, во втором оно инкрементируется под воздействием перехода из 1 в 0 внешнего входного сигнала, подаваемого на соответствующий (T0,T1) вывод микро-ЭВМ 8051. Так как на распознавание периода требуются два машинных цикла, максимальная частота подсчета входных сигналов равна 1/24 частоты резонатора. На длительность периода входных сигналов ограничений сверху нет. Для гарантированного прочтения входной сигнал должен удерживать значение 1, как минимум, в течение одного машинного цикла микро-ЭВМ.

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

Регистр режима работы таймера/счетчика TMOD

Символ

Позиция

 

 

Имя и назначение

 

TMOD.7 для

Управление блокировкой. Если бит установлен, то тай-

GATE

Т/С1 и

мер/счетчик "x" разрешен до тех пор, пока на входе "lNTx"

высокий уровень и бит управления "TRx" установлен. Если

 

TMOD.3 для

бит сброшен, то Т/С разрешается, как только бит управле-

 

Т/СО

 

 

ния "TRx" устанавливается

 

TMOD.6 для

Бит выбора режима таймера или счетчика событий. Ес-

С/Т

T/C1 и

ли бит сброшен, то работает таймер от внутреннего источ-

TMOD.2 для

ника сигналов синхронизации. Если; установлен, то работа-

 

Т/СО

ет счетчик от внешних сигналов на входе "Tx"

 

TMOD.5 для

 

 

Режим работы

M1

T/C1 и

M1

M0

 

TMOD.1 для

0

0

Таймер ВЕ48. "TLx" работает как 5-битный предде-

 

 

Т/СО

литель

 

 

0

1

16 битный таймер/счетчик. "THx" и "TLx" включен

 

 

 

 

последовательно

 

 

1

0

8-битный авто перезагружаемый таймер/счетчик.

 

TMOD.4 для

"THx" хранит значение, которое должно быть пере-

M0

Т/С1 и

 

 

загружено в "TLx" каждый раз по переполнению

 

 

Таймер/счетчик 1 останавливается. Таймер/счетчик

TMOD.0 для

 

 

 

 

 

0: TLO работает как 8-битный таймер/счетчик, и его

 

Т/СО

 

 

 

1

1

режим определяется управляющими битами таймера

 

 

 

 

 

 

0. TH0 работает только как 8 битный таймер, и его

 

 

 

 

режим определяется управляющими битами таймера

 

 

 

 

1

Регистр управления/статуса таймера TCON.

Символ

Позиция

Имя и назначение

TF1

TCON.7

Флаг переполнения таймера 1. Устанавливается аппаратно

 

 

при переполнении таймера/счетчика. Сбрасывается при обслу-

38

Символ

Позиция

Имя и назначение

 

 

живании прерывания аппаратно

TR1

TCON.6

Бит управления таймера 1. Устанавливается, / сбрасывается

 

 

программой для пуска/останова

TF0

TCON.5

Флаг переполнения таймера 0. Устанавливается аппаратно.

 

 

Сбрасывается при обслуживании прерывания

TR0

TCON.4

Бит управления таймера 0. Устанавливается / сбрасывается

 

 

программой для пуска/останова таймера/счетчика

IE1

TCON.3

Флаг фронта прерывания 1. Устанавливается аппаратно, когда

детектируется срез внешнего сигнала INT1. Сбрасывается при

 

 

обслуживании прерывания

IT1

TCON.2

Бит управления типом прерывания 1. Устанавливается /

сбрасывается программно для спецификации запроса INT1

 

 

(срез/низкий уровень)

IE0

TCON.1

Флаг фронта прерывания 0. Устанавливается по срезу сигнала

 

 

INT0. Сбрасывается при обслуживании прерывания

IT1

TCON .0

Бит управления типом прерывания 0. Устанавливается /

сбрасывается программно для спецификации запроса INT0

 

 

(срез/низкий уровень)

Режимы работы таймеров-счетчиков.

Как следует из описания управляющих бит TMOD, для обоих Т/С режимы работы 0, 1 и 2 одинаковы. Режимы 3 для Т/СО и Т/С1 различны. Рассмотрим кратко работу Т/С в каждом из режимов.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C /

 

 

 

 

Control

 

 

 

 

OSC

 

 

 

:12

 

 

 

 

 

 

 

 

X =0

 

 

 

 

 

 

 

 

 

T

 

 

 

 

 

 

 

INTR

 

 

 

 

 

 

 

 

 

 

 

TLX

THX

 

 

TFX

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C /

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TX PIN

 

 

 

 

 

 

 

T

X =1

 

Mode 0: TLX (5 bit), THX (8 bit)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TRX

 

 

 

 

 

 

 

 

 

&

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

GATEX

 

 

1

 

 

 

 

 

 

 

 

 

Mode 1: TLX (8 bit), THX (8 bit)

 

 

 

 

 

 

 

 

 

 

 

 

 

X PIN

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

X={0,1}

 

 

 

INT

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

а - логика работы T/C0 и T/C1 в режимах 0 и 1

39

 

 

Control

 

OSC

:12

C / T X =0

INTR

 

 

TLX (8 bit)

TFX

TX PIN

 

C / TX =1

 

TRX

 

&

&

GATEX

1

THX (8 bit)

 

INTX PIN

 

 

 

 

 

X={0,1}

 

 

б - логика работы T/C0 и T/C1 в режиме 2

 

 

 

Control

INTR

 

 

 

 

 

TH0 (8 bit)

TF1

TR1

 

Control

 

 

 

 

OSC

:12

C / T X =0

INTR

 

 

TL0 (8 bit)

TF0

TX PIN

 

C / TX =1

 

TRX

 

&

 

GATEX

1

 

 

INTX PIN

 

 

 

в - логика работы T/C0 в режиме 3

Логика работы T/C0 и Т/C1в режимах 0, 1, 2 и 3

Режим 0.Перевод любого Т/С в режим 0 делает его похожим на таймер КМ1816ВЕ48 (восьми битный счетчик), к входу которого подключен пятибитный предделитель частоты на 32. Работу Т/С в режиме 0 на примере T/C1 иллюстрирует рис а. В этом режиме таймерный регистр имеет разрядность 13 бит. При переходе из состояния "все единицы" в состояние "все нули" устанавливается флаг прерывания от таймера TF 1. Входной синхросигнал таймера 1 разрешен (поступает на вход Т/С1), когда управляющий бит TR1 установлен в 1 либо управляющий бит GATE (блокировка) равен 0, либо на внешний вывод запроса прерывания INT1 поступает уровень 1. Отметим попутно, что установка бита GATE в 1 позволяет использовать таймер для измерения длительности импульсного сигнала подаваемого на вход запроса прерывания.

Режим 1. Работа любого Т/С в этом режиме такая же, как и в режиме 0, за исключением того, что таймерный регистр имеет разрядность 16 бит.

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

40

та (TH 1) таймерного регистра, которое предварительно было задано программным путем. Перегрузка оставляет содержимое TH1 неизменным. В режиме 2 Т/С0 и Т/С1 также работают совершенно одинаково.

Режим 3. В режиме 3 Т/С0 и Т/С1 работают по-разному. Т/С1 сохраняет неизменным свое текущее содержимое. Иными словами, эффект такое же как

ипри сбросе управляющего бита TR1 в 0. Работу Т/С0 иллюстрирует рис. в.

Врежиме 3 TL0 и TH0 функционируют как два независимых восьмибитных счетчика. Работу TL0 определяют управляющие биты Т/С0 (С/Т, GATE TR0), входной сигнал INT0 и флаг переполнения TF0. Работу TH0, который может выполнять только функции таймера (подсчёт машинных циклов микро-ЭВМ), определяет управляющий бит TR1. При этом TH0 использует флаг переполнения TF1. Режим 3 используется в тех случаях, когда требуется наличие дополнительного восьми битного таймера или счетчика событий. Можно считать, что в этом режиме микро-ЭВМ 8051 имеет в своем составе три таймера/счетчика. В случае же, если Т/С0 используется в режиме 3, Т/С1 может быть или выключен, или переведен в режим 0, 1 или 2, или может быть использован последовательным портом в качестве генератора частоты передачи.

Вмодернизированных моделях микроконтроллеров семейства MCS-51 может иметься третий таймер счетчик T/C2 и (или) блок программных счетчиков PCA, которые тоже могут быть использованы для отсчета временных интервалов.

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

конвейера от момента его пуска до выключения. Факт схода детали с конвейера фиксируется фотоэлементом, на выходе которого формируется импульс. Датчик работы конвейера подключен к линии 3 порта 2.

 

MOV TMOD,#01000010B

;Настройка счетчика 1

 

MOV TH1,#0

;Сброс счетчика деталей

 

MOV TL1,#0

 

WAIT0:

JB P2.3,WAIT0

;Ожидание включения конвейера

 

SETB TCON.6

;Пуск счетчика 1

WAIT1:

JNB P2.3,WAIT1

;Ожидание выключения конвейера

 

CLR TCON.6

;Выключение счетчика 1

Пример 2. Организация временной задержки длительностью 50mc.

При использовании таймера/счетчика Т/С0 необходимо организовать вектор прерывания от этого счетчика.

41

ORG 0BH

;Адрес вектора прерывания от Т/С0

CLR TCON.4

;Останов Т/С0

RETI

;Выход из подпрограммы прерывания

ORG 100H

;Начальный адрес программы

MOV TMOD,#01H

;Настройка Т/С0

MOV TL0,#LOW

;Загрузка таймера (NOT(50000-1))

MOV TH0,#HIGH

 

SETB IE.7

;Разрешение обработки прерываний

SETB TCON.4

;Запуск Т/С0

SETB IE.1

;Разрешение прерывания от Т/С0

SETB PCON.0

;Переход в режим холостого хода

ТЕМА 9

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

Последовательный порт микроконтроллера 8051.

Через универсальный асинхронный приемопередатчик UART (Universal Asynchronous Receiver-Transmitter) осуществляются прием и передача информации, представленной последовательным кодом (младшими битами вперед), в полном дуплексном режиме обмена. В состав приемопередатчика, называемого часто последовательным портом входят принимающий и передающий сдвигающие регистры, а также специальный буферный регистр (SBUF) приемопередатчика.

Кроме того, работой последовательного порта управляют два служебных регистра -

Регистр управления/статуса приемопередатчика SCON

Бит SMOD регистра управления мощностью PCON

Запись байта в буфер приводит к автоматической переписи байта в сдви-

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

Последовательный порт 8051 может работать в четырех режимах:

Режим 0. Информация и передается, и принимается через вывод входа приемника (RXi TXi). Принимаются или передается 8 бит данных. Через вывод выхода передатчика (TXD; выдаются импульсы сдвига, которые сопровож-

42

дают каждый бит. Частота передачи бита информации равна 1/12 частоты кварцевого резонатора

Режим 1. В этом режиме передаются через вывод TXD или принимаются через RXD 10 бит информации: старт-бит (0), 8 бит данных и стоп-бит (1) при приеме информации в бит RB8 регистра управления/статуса приемопередатчика SCON заносятся стоп-бит Скорость приема/передачи — величина переменная и задается таймером.

Режим 2. В этом режиме через вывод TXD передаются или через RXD принимаются 11 бит информации: старт-бит, 8 бит данных, программируемый девятый бит и стоп-бит. При передаче девятый бит данных может принимать значение 0 или 1 или, например, для повышения достоверности передачи путем контроля по четности в него может быть помещено значение признака паритета из слова состояния программы (PSW.0). При приеме девятый бит данных помещается в бит RB8 SCON, а стоп-бит, в отличие от режима 1, теряется. Частота приема/передачи выбирается программой и может быть равна либо 1/32, либо 1/64 частоты резонатора в зависимости от управляющего бита SMOD.

Режим 3. совпадает с режимом 2 во всех деталях, за исключением частоты приема/передачи, которая является переменной и задается таймером.

Во всех случаях передача инициализируется инструкцией, в которой дан-

ные перемещаются в SBUF. Прием инициализируется при обнаружении перепада из 1 в 0 на входе приемника. При этом в режиме 0 этот переход должен сопровождаться выполнением условий R1 = 0 и REN= 1 (см. табл. 8), а для остальных ре-

жимов - REN = 1.

Регистр управления/статуса приемопередатчика SCON.

Управление режимом работы приемопередатчика осуществляется через специальный регистр с символическим именем SCON. Этот регистр содержит не только управляющие биты, определяющие режим работы последовательного порта, но и девятый бит принимаемых или передаваемых данных (RB8 и ТВ8) и биты прерывания приемопередатчика (R1 и Т1).

Прикладная программа путем загрузки в старшие биты регистра SCON двухбитного кода определяет режим работы приемопередатчика. Во всех четырех режимах работы передача инициализируется любой командой, в которой буферный регистр SBUF указан как получатель байта. Как уже отмечалось, прием в режиме 0 осуществляется при условии, что R1 = 0 и REN = 1, в остальных режимах - при условии, что REN = 1.

В бите ТВ8 программно устанавливается значение девятого бита данных, который будет передан 8 режиме 2 или 3. В бите RB8 в этих режимах фиксируется девятый принимаемый бит данных. В режиме 1 в бит RB8 заносится стоп-бит. В режиме 0 бит RB8 не используется.

Флаг прерывания передатчика ТI устанавливается аппаратно в конце периода передачи стоп-бита во всех режимах. Соответствующая подпрограмма об-

43

служивания прерывания должна сбрасывать бит TL.

Флаг прерывания приемника RI устанавливается аппаратно в конце периода приема восьмого бита данных в режиме 0 и в середине периода приема стоп-бита в режимах 1, 2 и 3. Подпрограмма обслуживания прерывания должна сбрасывать бит RI.

Функциональное назначение бит регистра управления/статуса приемопередатчика SCON.

СимволПозиция

SM0 SCON.7

SM1 SCON.6

SM2 SCON.5

REN SCON.4

TB8 SCON. 3

RB8 SCON.2

TI SCON. 1

RI SCON.0

Имя и назначение

Биты управления режимом работы приемопередатчика.

SM0

SM1

Режим работы приемопередатчика

0

0

Сдвигающий регистр расширения ввода/вывода

0

1

8 битовый приемопередатчик, изменяемая скорость

передачи

 

 

1

0

9 битовый приемопередатчик. Фиксированная ско-

рость передачи

 

 

1

1

9 битовый приемопередатчик, изменяемая скорость

передачи

 

 

Бит управления режимом приемопередатчика. Устанавливается программно для запрета приема сообщения, в котором девятый бит имеет значение 0

Бит разрешения приема. Устанавливается/сбрасывается программно для разрешения/запрета приема последовательных данных

Передача бита 8. Устанавливается/сбрасывается программно для задания девятого передаваемого бита в режиме 9-битового передатчика

Прием бита 8. Устанавливается/сбрасывается аппаратно для фиксации девятого принимаемого бита в режиме 9-битового приемника

Флаг прерывания передатчика. Устанавливается аппаратно при окончании передачи байта. Сбрасывается программно после обслуживания прерывания

Флаг прерывания приемника. Устанавливается аппаратно при приеме байта. Сбрасывается программно после обслуживания прерывания

Скорость приема/передачи информации через последовательный порт.

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

В режиме 0 частота передачи зависит только от резонансной частоты кварцевого резонатора f РЕЗ:

f=fРЕЗ/12.

44

За машинный цикл последовательный порт передает один бит информации. В режимах 1, 2 и 3 скорость приема/передачи зависит от значения управляющего бита SMOD в регистре специальных функций PCON.

 

 

Регистр управления мощностью PCON.

 

 

Наименование и функция

Символ

Позиция

SMOD

PCON.7

Удвоенная скорость передачи. Если бит установлен в 1, то

скорость передачи вдвое больше, чем при SMOD = 0. По

 

 

сбросу SMOD = 0.

 

PCON.6

Не используется

 

PCON.5

Не используется

 

PCON.4

Hе используется

GF1

PCON.3

Флаги, специфицируемые пользователем (флаги общего на-

GF0

PCON.2

значения)

PD

PCON.1

Бит пониженной мощности. При установке бита в 1 микро-

ЭВМ переходит в режим пониженной потребляемой мощно-

 

 

сти

IDL

PCON.0

Бит холостого хода. Если бит установлен в 1, то микро-ЭВМ

 

 

переходит в режим холостого хода

Примечание. При одновременной записи 1 в PD и IDL бит PD имеет преимущество. Сброс содержимого PCON выполняется путем загрузки в него кода

0XXX0000.

В режиме 2 частота передачи определяется выражением

f =2SMODfРЕЗ/64.

Иными словами, при SMOD = 0 частота передачи равна 1/64 частоты fРЕЗ, а

пои SMOD = 1 — 1/32 частоты fРЕЗ.

В режимах 1 и 3 в формировании частоты передачи, кроме управляющего бита SMOD, принимает участие таймер 1. При этом частота передачи f зависит от частоты переполнения f OVLTи определяется следующим образом:

f=2SMODfOVTL1/32

Прерывание от таймера 1 в этом случае должно быть заблокировано. Сам же таймер может работать как в режиме таймера, так и в режиме счетчика. Номер режима (0, 1, 2) роли не играет. Наиболее типично использование его в режиме таймера с автоперезагрузкой (старшая тетрада TMOD = 0010В). При этом частота передачи определяется выражением:

f = 2SMODfРЕЗ/(32х12х(256 —ТН1)).

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

45

Частота прие-

Частота резо-

 

 

Таймер/счетчик 1

ма/передачи (BAUD

 

 

Режим

Перезагружаемое

натора МГц

SMOD

С/Т

RATE)

 

 

 

(MODE)

число

Режим 0, макс: 1 МГц

12

X

X

X

X

Режим 2, макс: 375 Кгц

12

1

X

X

X

Режим 1, 3: 62,2 Кгц

12

1

0

2

0FFH

19,2 Кгц

11,059

1

0

2

0FDH

9,6 Кгц

11,059

0

0

2

0FDH

4,8 Кгц

11,059

0

0

2

0FAH

2,4 Кгц

11,059

0

0

2

0F4H

1,2 Кгц

11,059

0

0

2

0F4H

137,5 Гц

11,059

0

0

2

1DH

110 Гц

6

0

0

2

72H

110 Гц

12

0

0

1

0FЕЕВН

Отметим, что скорости приема и передачи могут различаться.

Предельно низких частот приемопередачи можно достичь при использовании таймера в режиме 1 (16-битный таймер) и разрешении прерываний от таймера (старший полубайт TMOD = 0001В). Перезагрузка 16-битного таймера должна осуществляться программным путем.

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

Если программа вызывается часто, то наиболее эффективным программным способом передачи параметров является последовательный. Константы в этом случае являются частью программного кода и следуют непосредственно за инструкцией вызова подпрограммы. По адресу возврата в стеке программа определяет место расположения параметров для последующего считывания. Например, подпрограмма LINE выполняет сложение 16-разрядной упакованной двоичнодесятичной константы с двухбайтной двоично-десятичной переменной, хранящейся во внутренней памяти ЗУ, и запоминает сумму в другом двухбайтном буфере. В подпрограмму должны передаваться константа и оба адреса буферов. Вместо использования для этих целей четырех рабочих регистров используются 4 байта в памяти программ, следующих за командой вызова подпрограммы. В приведенном ниже примере подпрограмма осуществляет сложение десятичной константы 1234 с содержимым адреса внутренней памяти 56Н и записывает результат в буфер по адресу 78Н. Подпрограмма LINE определяет, из какой точки был осуществлен ее вызов путем извлечения из стека адреса возврата в старший и младший байты указателя данных. Инструкция MOVC затем считывает параметры из памяти программы по мере необходимости.

CALL LINE

DW 1234H ;BCD-константа

46

DB

56Н

;Адрес строки операнда

DB

78Н

;Адрес строки результата

LINE: POP

DPH

;Извлечение адреса возврата в DPTR

POP

DPL

 

 

MOV

А, #2 ;Индекс строк источника параметров

MOVC

A,@A+DPTR ;Извлечение адреса строки источника

MOV

R0,A

 

 

MOV

A,#3

 

;Индекс строк приемника параметров

MOVC

A,@A+DPTR ;Извлечение адреса строки приемника

MOV

R1, А

 

MOV

А,#1

 

;Индекс мл.байта 16-разр. константы

MOVC

A,@A+DPTR ;Извлечение мл. байта константы

ADD

A,@R0

;Вычисление мл. байта суммы

DA

А

 

;Десятичная коррекция

MOV

@R1,A

;Запись в буфер

INC

R0

 

 

INC

Rl

 

 

 

CLR

A

 

;Индекс ст. байта равен нулю

MOVC

A,@A+DPTR ;Извлечение ст. байта константы

ADDC A,@R0

 

DA

A

 

;Десятичная коррекция

MOV

@R1,A

;Запись в буфер

MOV

А,#4 ;Индекс для продолжения программы

47

JMP @A+DPTR ;Переход к инструкции, следующей за списком па-

раметров.

Следует учесть, что инструкции ACALL и LCALL не используются в программе, поскольку каждая из них продвигает адрес в стек. В то же время инструкция MOVC имеет доступ ко всем 64 К байтам адресного пространства памяти программ микроЭВМ.

ТЕМА 10

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

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

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

Рассмотрим пример программирования такого типа приема, считая, что контролируемые импульсы приходят на восемь каналов порта 0, а строб-сигнал поступает на вход 0 порта 1. Подсчет импульсов по каждому из контролируемых каналов осуществляется в ячейках 30 – 37 ОЗУ микроконтроллера.

MET1:

JNB

P1.0,

MET1

; ожидание поступления строб-импульса

 

MOV R5,

#8

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

 

MOV R0,

#30H

; загрузка счетчика адресов

 

MOV

A, Port0

; загрузка порта 0 в аккумулятор

MET2:

JNB

Acc.0, MET3

; проверка младшего бита аккумулятора

 

 

 

 

; и ветвление, если бит равен 0

 

INC

@RO

 

; увеличение счетчика импульсов

 

 

 

 

; проверяемого канала

 

 

 

 

48

MET3:

RR A

; сдвиг аккумулятора вправо

 

 

 

;теперь в младшем бите импульс ;следующего

 

 

 

канала

 

INC

R0

; подготовка следующего счетчика

 

DJNZ

R5, MET2

; организация цикла опроса каналов

 

 

 

; управления

Рассмотрим теперь вариант, когда управляющие сигналы приходят на все линии порта 0 и являются независимыми друг от друга. Время активного состояния импульсов в каждом канале не определено, но известно, что оно значительно превышает время цикла программы.

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

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

Затем необходимо сформировать такую логику обработки сохраненного значения порта и вновь сформированного, чтобы в полученном образе единичными сигналами были представлены только те каналы, состояние которых изменилось с 0 на 1.

Все возможные комбинации нового и старого состояния одного канала можно представить в виде таблицы

Старое состояние

0

0

1

1

Новое состояние

0

1

0

1

Инверсия старого

1

1

0

0

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

Условия для программы подсчета определим следующим образом:

Импульсные сигналы поступают на восемь линий порта 0;

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

вячейках 30 – 37 ОЗУ микроконтроллера;

Предыдущее значение порта 0 хранится в регистре R4.

MET1:

MOV A, R4

; чтение предыдущего значения порта 0

 

CPL

A

 

; инверсия старого значения порта 0

 

ANL

A,

Port0

; определение вновь поступивших сигналов

 

JNZ

MET2

; если вновь поступившие сигналы есть, то

 

 

 

 

; переход на программу подсчета

 

MOV

R4,

Port0

; если новых сигналов нет, то сохранить

49

 

 

 

 

; значение порта 0 как “старое”

 

SJMP

MET1

; переход на новый цикл сканирования

MET2:

MOV

R4,

Port0

; сохранить значение порта 0 как “старое”

 

MOV R5,

#8

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

 

MOV R0,

#30H

; загрузка счетчика адресов

 

JNB

Acc.0, MET3

; проверка младшего бита аккумулятора

 

 

 

 

; и ветвление, если бит равен 0

 

INC

@R0

 

; увеличение счетчика импульсов

 

 

 

 

; проверяемого канала

MET3:

RR A

 

; сдвиг аккумулятора вправо

 

 

 

 

;теперь в младшем бите импульс ;следующего

 

 

 

 

канала

 

INC

R0

 

; подготовка следующего счетчика

 

DJNZ

R5, MET2

; организация цикла опроса каналов

 

 

 

 

; управления

 

SJMP

MET1

; переход на новый цикл сканирования

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

Пример 1.

Запомнить в ВПД содержимое регистров банка 0. Начальный

адрес ВПД - 5000H:

 

 

 

MOV

PSW,#01000B

;Выбор банка регистров 1

 

MOV

R0,#8

 

;Счетчик загружен числом 8

 

MOV

DPTR,#5000Н

;Определение адреса ВПД

 

MOV

R1,#0

 

;Определение адреса РПД

LOOP:

MOV

A,@R1

 

;Загрузить (A) 7 5 0 (R0)

 

MOVX @DPTR,A

;Передача из аккумулятора в ВПД

 

INC

R1

 

;Переход к R 4i+1

 

INC

DPTR

 

;Приращение указателя адреса

 

DJNZ

R0,LOOP

 

;Организация цикла

Пример 2. Сложить два многобайтных числа. Оба слагаемых располагают-

ся в РПД,

начиная с младшего байта. Начальные адреса слагаемых заданы в R0 и

R1. Формат слагаемых в байтах задан в R2.

LOOP:

CLR

C

;Сброс переноса

MOV

A,@R0

;Загрузка в аккумулятор текущего

 

ADDC A,@R1

; байта первого слагаемого

 

;Сложение байт с учетом переноса

 

MOV

@R0,A

;Размещение байта результата

 

INC

R0

;Продвижение указателей

 

INC

R1

 

 

 

DJNZ

R2,LOOP ;Цикл, если не все байты просуммированы

50

При сложении чисел без знака на переполнение укажет флаг С, а в случае сложения чисел со знаком - флаг OV.

Пример 3. Умножение. Команда MUL вычисляет произведение двух целых беззнаковых чисел, хранящихся в регистрах А и В. Младшая часть произведения размещается в А, а старшая - в регистре-расширителе В. Если содержимое В равно 0, то флаг OV сбрасывается, иначе - устанавливается. Флаг переноса всегда сбрасывается.

Пусть требуется умножить целое двоичное число произвольного формата на константу 173. Исходное число размещается в РПД, адрес младшего байта хранится в R0, формат числа в байтах - в R1.

MOV

A,#0

;Сброс аккумулятора

LOOP: ADD

A,@R0

;Загрузка множимого

MOV

B,#173

;Загрузка множителя

MUL

AB

;Умножение

MOV

@R0,A

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

 

 

;

произведения

INC

R0

;Приращение адреса

MOV

A,B

;Пересылка старшего байта частичного

 

 

;

произведения в аккумулятор

XCH

A,@R0

;Предварительное формирование очередного

 

 

;

байта произведения

DJNZ

R1,LOOP

;Цикл, если не все байты числа умножены

Пример 4.

Подсчет числа импульсов между двумя событиями.

Предположим, что необходимо подсчитать количество деталей сошедших с конвейера от момента его пуска до выключения. Факт схода детали с конвейера фиксируется фотоэлементом, на выходе которого формируется импульс. Датчик работы конвейера подключен к линии 3 порта 2.

 

MOV TMOD,#01000010B

;Настройка счетчика 1

 

MOV TH1,#0

;Сброс счетчика деталей

 

MOV TL1,#0

 

WAIT0:

JB P2.3,WAIT0

;Ожидание включения конвейера

 

SETB TCON.6

;Пуск счетчика 1

WAIT1:

JNB P2.3,WAIT1

;Ожидание выключения конвейера

 

CLR TCON.6

;Выключение счетчика 1

Пример 5. Организация временной задержки длительностью 50mc.

При использовании таймера/счетчика Т/С0 необходимо организовать вектор прерывания от этого счетчика.

ORG 0BH

;Адрес вектора прерывания от Т/С0

CLR TCON.4

;Останов Т/С0

RETI

;Выход из подпрограммы прерывания

51

ORG 100H

;Начальный адрес программы

MOV TMOD,#01H

;Настройка Т/С0

MOV TL0,#LOW

;Загрузка таймера (NOT(50000-1))

MOV TH0,#HIGH

 

SETB IE.7

;Разрешение обработки прерываний

SETB TCON.4

;Запуск Т/С0

SETB IE.1

;Разрешение прерывания от Т/С0

SETB PCON.0

;Переход в режим холостого хода

ТЕМА 11

Директивы языка программирования ASM-51.

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

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

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

Как видно на приведённом примере, использование идентификаторов значительно повышает понятность программы, так как в названии переменной ото-

52

бражается функция, за которую отвечает данная переменная.

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

Один раз назначенный идентификатор уже не может быть изменён в дальнейшем и при повторной попытке назначения точно такого же имени идентификатора будет выдано сообщение об ошибке.

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

Константы, назначаемые директивой equ, могут быть использованы только в одной команде. Достаточно часто требуется работа с таблицей констант, такой как таблица перекодировки, таблицы элементарных функций или синдромы помехоустойчивых кодов. Такие константы используются не на этапе трансляции, а хранятся в памяти программ микроконтроллера. Для занесения констант в память программ микроконтроллера используются директивы db и dw.

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

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

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

Эта же директива позволяет легко записывать надписи, которые в дальней-

53

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

Директива dw позволяет заносить в память программ двухбайтные числа. В этой директиве, как и в директиве db числа можно заносить через запятую. Пример листинга фрагмента программы:

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

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

54

Иногда требуется расположить команду по определённому адресу. Наиболее часто это требуется при использовании прерываний, когда первая команда программы-обработчика прерываний должна быть расположена точно на векторе прерывания. Это можно сделать используя команду nop для заполнения промежутков между векторами прерывания, но лучше воспользоваться директивой

ORG.

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

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

55

Директива using. При использовании прерываний критичным является время, занимаемое программой, обработчиком прерываний. Это время можно значительно сократить, выделив для обработки прерываний отдельный банк регистров. Выделить отдельный банк регистров можно при помощи директивы USING. Номер банка используемых регистров указывается в директиве в качестве операнда. Пример использования директивы USING для подпрограммы обслуживания прерываний от таймера 0:

Директива CALL. В системе команд микроконтроллера MCS-51 используется три команды безусловного перехода. Выбор конкретной команды зависит от расположения ее в памяти программ, однако программист обычно этого не знает. В результате во избежание ошибок приходится использовать самую длинную команду LJMP. Это приводит к более длинным программам и к дополнительной нагрузке на редактор связей. Транслятор сам может подобрать наилучший вариант команды безусловного перехода. Для этого вместо команды микроконтроллера следует использовать директиву call.

ТЕМА 12

Реализация процедур и функций на языке ASM51. Структурное программирование.

Реализация подпрограмм на языке ASM51.

Подпрограммы на языке программирования ASM-51 выносятся отдельно от основного текста программы. Обычно подпрограммы размещают после основного текста программы для того, чтобы случайно не передать управление подпрограмме не оператором вызова подпрограммы. Это может произойти из-за того, что ассемблер назначает адреса операторам в порядке их написания.

Исходный текст подпрограммы начинается с метки, которая одновременно

56

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

Реализация подпрограмм-процедур на языке ASM51.

Подпрограмма процедура вызывается командами процессора LCALL и ACALL. В языке программирования ASM51 допустимо использования директивы CALL эта директива подбирает наиболее подходящую к данному случаю по размеру команду.

Пример подпрограммы управления последовательным портом:

Передача переменных-параметров в подпрограмму.

В приведенном выше примере байт передается в подпрограмму через глобальную переменную G_Per. Однако программа будет эффективнее при использовании подпрограммы с параметрами. Мы знаем, что параметр подпрограммы - это локальная переменная. В этом случае могут значительно снизиться требования к памяти данных. Для размещения локальных переменных лучше всего использовать внутренние регистры процессора. На языке ASM51 для передачи параметра размерностью один байт обычно используется аккумулятор как показано в следующем примере программы.

57

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

Если в подпрограмму нужно передать двухбайтовое значение, то в качестве параметра подпрограммы используется пара регистров (обычно регистры R6старший байт и R7-младший байт). Пример программы, передающей в подпрограмму двухбайтовое число, написанной на языке программирования ASM-51:

Если в подпрограмму нужно передать четырехбайтовое значение (это требуется для переменной, соответствующей типу long или float), то используются регистры R4...R7 (регистр R4 - старший байт):

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

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

58

раметра строки, написанный на языке программирования ASM-51 приведен ниже:

При обращении к массивам или структурам, расположенным во внутренней памяти данных в качестве указателя адреса используется регистр R0 или R1. Пример передачи в подпрограмму массива в качестве параметра, написанный на языке программирования ASM-51:

Реализация подпрограмм-функций на языке ASM51.

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

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

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

Подпрограмма - функция может возвращать и многобайтовые переменные, используя регистр - указатель R0 или R1. Однако для копирования этих переменных потребуется еще одна подпрограмма копирования.

Реализация подпрограмм обработки прерываний на языке ASM51.

Как известно, подпрограммы обработки прерываний вызываются аппаратурой, поэтому эти подпрограммы не могут иметь параметров. Кроме того, эти подпрограммы не могут быть подпрограммами-функциями. При возвращении из подпрограммы обработки прерывания должны быть разрешены дальнейшие прерывания, поэтому возврат из подпрограммы обработки прерывания может быть осуществлен только командой RETI.

59

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

В приведенном примере директива CSEG AT 0BH использована для того, чтобы разместить подпрограмму обработки прерывания на вектор прерывания от таймера 0.

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

Если подпрограмма обработки прерывания использует несколько регистров, то на сохранение регистров в стеке и на восстановление их из стека тратится достаточно много времени. Микроконтроллеры семейства MCS-51 предлагают возможность использовать для подпрограмм прерываний отдельный банк регистров. В языке программирования ASM-51 то, что программа использует не нулевой

60

банк регистров, отображается при помощи директивы USING:

Структурное программирование на языке ASM-51.

Применение структурного программирования позволяет увеличить скорость написания программ и облегчить отладку написанной программы. Языки программирования C, PASCAL, PL/M разрабатывались на основе принципов структурного программирования, поэтому в состав этих языков программирования входят структурные операторы. Тем не менее, структурное программирование возможно и на языках программирования низкого уровня, в том числе и на языке программирования ASM-51, где не предусмотрено структурных операторов,

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

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

61

использование специальных символов-разделителей;

начинать каждое новое слово внутри метки с буквы верхнего регистра

Вкачестве символов-разделителей можно использовать символы подчеркивания '_' и вопроса '?' Примеры назначения говорящих меток:

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

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

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

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

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

62

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

Полная схема условного оператора реализуется на языке ассемблер более сложным образом. Для реализации такого оператора потребуется уже две команды микроконтроллера. Для исключения выполнения второго плеча условного оператора потребуется команда безусловного перехода. Пример реализации условного оператора:

63

Третий структурный оператор - это оператор цикла с проверкой условия после тела цикла. Такой оператор легко реализуется на языке программирования ассемблер при помощи команды условного или безусловного перехода. Отличие от условного оператора заключается в том, что передача управления осуществляется не вперед, а назад. На языках программирования высокого уровня такой оператор входит в состав языка (оператор do..while в языке программирования C или оператор repeat..until в языке программирования PASCAL). На языке программирования ассемблер для реализации этого оператора можно воспользоваться любой условной операцией. Однако для реализации оператора цикла в системе команд микроконтроллера MCS-51 предусмотрена специальная команда, выполняющая сразу два алгоритмических действия - DJNZ. Пример использования этой команды для реализации оператора цикла:

Четвертый структурный оператор - это оператор цикла с проверкой условия до тела цикла. В отличие от предыдущего оператора тело цикла в этом операторе может ни разу не выполниться, если условие цикла сразу же выполнено. Этот оператор, как и условный оператор, невозможно реализовать на одной машинной команде. Для реализации этого оператора тоже потребуется команда безусловного перехода:

64

ТЕМА 13

Программирование микроконтроллеров на языке высокого уровня (С51).Структура программы, символы языка программирования С51

C - это язык программирования общего назначения, предназначенный для написания программ, эффективных по исполняемому коду, с элементами структурного программирования и богатым набором операторов. Язык программирования C практически не имеет ограничений, что позволяет использовать его для эффективного решения широкого круга задач. Однако при написании программ для микроконтроллеров, принадлежащих к семейству MCS-51, необходимо учитывать особенности построения аппаратуры этих микросхем, поэтому был создан диалект этого языка.

В состав языка программирования С-51 введён ряд изменений, отображающих особенности построения памяти микроконтроллеров семейства MCS-51. Кроме того, эти изменения позволяют непосредственно обращаться к встроенным портам, таймерам и другим устройствам микроконтроллеров указанного семейства. Особенности микроконтроллеров этого семейства в основном отображаются через описания переменных.

Язык программирования С-51 удовлетворяет стандарту ANSI-C и предназначен для получения компактных и быстродействующих программ, предназначенных для микроконтроллеров семейства MCS-51. Язык С-51 обеспечивает гибкость программирования на широко известном языке C, при скорости работы и компактности, сравнимой с программами, написанными на языке программирования ассемблер.

Так как язык программирования C не имеет собственных средств ввода и вывода, то он обращается к соответствующим функциям операционных систем. В

65

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

Применение.

Язык программирования C-51 и его библиотеки являются частью интегрированного набора средств разработки программного обеспечения для микроконтроллеров семейства MCS-51. Язык программирования C51 поддерживает модульное написание программ. Процесс разработки программ на языке программирования C-51 показан на рисунке 13.1.

Рис. 13.1. Процесс написания программы на языке программирования С-51.

При разработке программного обеспечения выполняются следующие эта-

пы:

постановка задачи (полное определение решаемой проблемы);

разработка принципиальной схемы и выбор необходимого программного обеспечения;

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

написание текста программы и подготовка к трансляции при помощи любого текстового редактора;

компиляция программы;

исправление синтаксических ошибок, выявленных компилятором, в текстовом редакторе с последующей перетрансляцией;

66

создание и сохранение библиотек часто используемых объектных модулей при помощи программы lib51.ехе;

связывание полученных перемещаемых объектных модулей в абсолютный модуль и размещение переменных в памяти микроконтроллера при помощи редактора связей bl51.exe;

создание программы, записываемой в ПЗУ микроконтроллера (загружаемый модуль) в hex формате, при помощи программы oh.exe;

проверка полученной программы при помощи символьного отладчика или других программных или аппаратных средств.

Файл, в котором хранится программа, написанная на языке C51 (исходный текст программы), называется исходным модулем. Для исходного текста программы принято использовать расширения файла: '*.c'. Исходный текст программы можно написать, используя любой текстовый редактор, однако намного удобнее воспользоваться интегрированной средой программирования, подобной keil-C. В интегрированную среду программирования кроме текстового редактора обычно входят отладчик программ, менеджер проектов и средства запуска программ - трансляторов.

Готовый оттранслированный участок программы обычно хранится на диске в виде файла, записанного в объектном формате. Такой файл называется объект-

ным модулем.

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

Винтегрированной среде программирования процесс получения исполняемого модуля осуществляется командой BILD Target.

Большинство программаторов, предназначенных для записи информации в память программ микроконтроллеров, не может работать с объектным форматом исполняемого модуля программы, поэтому для загрузки машинного кода в процессор необходимо преобразовать объектный формат исполняемого модуля в общепринятый для программаторов HEX-формат. При преобразовании форматов вся отладочная информация теряется. Машинный код процессора в HEX-формате называется загрузочным модулем.

Винтегрированной среде программирования загрузочный файл получается автоматически при выполнении трансляции программного проекта, так как интегрированная среда программирования сама выполняет перечисленные выше действия в соответствии с настройками программного проекта.

Структура программ С-51.

Язык программирования С-51 является структурно-модульным языком. Каждый оператор в программе является частью, по крайней мере, одного модуля. Каждая программа, написанная на языке программирования С-51 состоит из одного или более модулей. Каждый модуль записывается в отдельном файле и ком-

67

пилируется отдельно.

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

Подпрограмма начинается с заголовка подпрограммы, в который входит тип возвращаемой переменной, имя подпрограммы и круглых скобок, внутри которых объявляются переменные-параметры подпрограммы. В подпрограмме main (а также во всех подпрограммах, где не нужно возвращать переменные) вместо типа переменной указывается слово void. Исполняемые операторы заключаются в фигурные скобки.

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

При написании программы для микроконтроллеров всегда необходимо видеть перед глазами принципиальную схему устройства, для которого пишется программа, так как схема и программа тесно связаны между собой и дополняют друг друга. Для иллюстрации простейшей программы, написанной на языке программирования C-51 воспользуемся схемой, приведённой на рисунке 13.2.

Рисунок 13.2. Пример простейшей схемы устройства, построенной с использованием микроконтроллера.

Для примера заставим гореть светодиод VD1. Этот светодиод будет светиться только тогда, когда через него будет протекать ток. Для этого на шестом

68

выводе порта P0 должен присутствовать нулевой потенциал. Запишем его первой же командой нашей программы:

#include<reg51.h>

void main(void)

{P0=0; //Зажигание светодиода while(1); //Бесконечный цикл

}

Эта программа содержит только один исполняемый оператор. Это оператор присваивания 'P0=0;'. Следующий оператор 'while(1);' обеспечивает зацикливание программы. Зацикливание программы сделано для того, чтобы микроконтроллер не выполнял больше никаких действий. В противном случае микроконтроллер перейдёт к следующей ячейке памяти и будет выполнять команды, которые мы не записывали.

Обратите внимание, что язык программирования знает где находится порт P0. Эта информация содержится в строке #include<reg51.h>.

Для того, чтобы получить более полное представление о структуре программ, написанных на языке программирования С-51, приведём пример исходного текста программы с использованием подпрограмм.

#include<reg51.h>

void svGorit(void)

{P0=0; //Зажигание светодиода

}

void main(void)

{svGorit(); //Вызов подпрограммы с именем svGorit while(1); //Бесконечный цикл

}

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

Символы языка программирования С-51.

Как и в любом другом текстовом файле, в исходном тексте программы, написанной на языке программирования С51, используются ASCII или ANSI символы. Множество символов, используемых в языке программирования С-51 можно разделить на пять групп.

1. Символы, используемые для образования ключевых слов и иденти-

фикаторов (табл.13.1). В эту группу входят прописные и строчные буквы английского алфавита, а также символ подчеркивания. Следует отметить, что язык программирования С-51 различает прописные и строчные буквы. Например, идентификаторы start и Start будут считаться различными идентификаторами.

Таблица 13.1

Прописные буквы латинского алфа-A B C D E F G H I J K L M N O P Q R S T U V W X Y Z вита

69

Строчные буквы латинского

алфа-

a b c d e f g h i j k l m n o p q r s t u v w x y z

вита

 

 

Символ подчеркивания

 

_

Арабские цифры

 

0 1 2 3 4 5 6 7 8 9

2. Прописные и строчные буквы русского алфавита (табл. 13.2).

Таблица 13.2

Прописные буквы русского алфа-А Б В Г Д Е Ж З И К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ы Ь

вита

Э Ю Я

Строчные буквы русского алфави-а б в г д е ж з и к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я та

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

 

 

 

Таблица 13.3

Символ

Наименование

Символ

Наименование

 

,

запятая

)

круглая скобка правая

 

.

точка

(

круглая скобка левая

 

;

точка с запятой

}

фигурная скобка правая

 

:

двоеточие

{

фигурная скобка левая

 

?

вопросительный знак

<

меньше

 

'

апостроф

>

больше

 

!

восклицательный знак

[

квадратная скобка

 

|

вертикальная черта

]

квадратная скобка

 

/

дробная черта

#

номер

 

\

обратная черта

%

процент

 

~

тильда

&

амперсанд

 

*

звездочка

^

исключающее ИЛИ

 

+

плюс

=

равно

 

-

минус

"

кавычки

 

4.Управляющие и разделительные символы. К этой группе символов относятся: пробел, символы табуляции, перевода строки, возврата каретки, символы новой страницы и новой строки. Эти символы отделяют друг от друга лексические единицы языка, к которым относятся ключевые слова, константы, идентификаторы и т.д. Последовательность разделительных символов (например, последовательность пробелов) рассматривается компилятором как один символ.

5.Управляющие последовательности, т.е. специальные символьные ком-

бинации, используемые в функциях ввода и вывода информации. Управляющая последовательность начинается с использования обратной косой черты (\), за которой следует комбинация латинских букв и цифр. Список управляющих после-

70

довательностей приведён в таблице 13.4.

 

 

Таблица 13.4

Управляющая по-

Наименование

Шестнадцатеричный

 

следовательность

код

 

 

 

\a

Звонок

007

 

\b

Возврат на шаг

008

 

\t

Горизонтальная табуляция

009

 

\n

Переход на новую строку

00A

 

\v

Вертикальная табуляция

00B

 

\r

Возврат каретки

00D

 

\f

Новая страница

00C

 

\"

Кавычки

022

 

\'

Апостроф

027

 

\0

Ноль-символ

000

 

\\

Обратная дробная черта

05C

 

\OOO

Восьмеричный код ASCII или ANSI символа

 

 

\xHHH

Шестнадцатеричный код ASCII или ANSI символа

HHH

 

Управляющие последовательности \OOO и \xHHH (здесь O обозначает восьмеричную цифру; H обозначает шестнадцатеричную цифру) позволяет представить символ из кодовой таблицы ASCII или ANSI как последовательность восьмеричных или шестнадцатеричных цифр соответственно. Например символ возврата каретки может быть представлен следующими способами:

\r - управляющая последовательность,

\015 - восьмеричный код символа возврата каретки, \x00D - шестнадцатеричный код символа возврата каретки.

Следует отметить, что в строковых константах всегда обязательно задавать все три цифры управляющей последовательности. Например, отдельную управляющую последовательность \n (переход на новую строку) можно представить как \010 или \xA, но в строковых константах необходимо задавать все три цифры, в противном случае символ (или символы) следующие за управляющей последовательностью будут рассматриваться как ее недостающая часть. Например:

"ABCDE\x009FGH" данная строковая команда будет напечатана с использованием определенных функций языка СИ, как два отдельных слова ABCDE и FGH, разделенные табуляцией, в этом случае если указать неполную управляющую строку "ABCDE\x09FGH",то при печати появится строка ABCDEЯGH, так как компилятор воспримет последовательность \x09F как символ "Я".

Отметим тот факт, что, если обратная дробная черта предшествует символу не являющемуся управляющей последовательностью (т.е. не включенному в табл.4) и не являющемуся цифрой, то эта черта игнорируется, а сам символ представляется как литеральный. Например:

символ \h представляется символом h в строковой или символьной констан-

те.

Кроме определения управляющей последовательности, символ обратной

71

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

printf("Это очень длинная \ строка")

Компилятор С-51 выдает сообщение об ошибке, если в тексте исходной программы встречается символ, отличающийся от символов, перечисленных выше.

ТЕМА 14

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

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

В языке программирования C51 любая переменная должна быть объявлена до первого использования этой переменной. Как уже говорилось ранее, этот язык программирования предназначен для написания программ для микроконтроллеров семейства MCS-51, поэтому в составе языка должна отображаться внутренняя структура этого семейства микроконтроллеров. Эти особенности отражены во введении новых типов данных. В остальном язык программирования C-51 не отличается от стандарта ANSI.

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

[спецификатор класса памяти] спецификатор типа [спецификатор типа памяти] описатель [=инициатор] [,описатель [= инициатор] ]...

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

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

Инициатор - задает начальное значение или список начальных значений, которые (которое) присваивается переменной при объявлении.

Спецификатор класса памяти - определяется одним из четырех ключевых слов языка С: auto, bit, extern, register, sbit, sfr, sfr16 static auto, extern, register,

72

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

Спецификатор типа памяти - определяется одним из шести ключевых слов языка С-51: code, data, idata, bdata, xdata, pdata и указывает, в какой области памяти микроконтроллера будет размещена переменная.

Категории типов данных

Ключевые слова для определения основных типов данных Целые типы данных: Типы данных с плавающей запятой:

bit

float

sbit

 

char

 

int

 

short

 

long

 

signed

 

unsigned

 

sfr sfr16

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

Примеры использования ключевого слова const: const float A=2.128E-2;

const B=286; //подразумевается const int B=286

Отметим, что переменные с спецификатором класса памяти размещаются во внутреннем ОЗУ. Неизменяемость контролируется только на этапе трансляции. Для размещения переменной в ПЗУ лучше воспользоваться спецификатором типа памяти code

Целые типы данных

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

73

 

 

 

 

Таблица 14.1

Тип

Размер памяти в

 

Размер памяти в

Диапазон значений

битах

 

байтах

 

 

 

bit

1

 

 

от 0 до 1

char

8

1

 

от -128 до 127

unsigned shar

8

1

 

oт 0 до 255

int, short

16

2

 

от -32768 до 32767

long

32

4

 

от -2 147 483 648 до 2 147 483

 

 

 

 

647

unsigned int, unsigned

16

2

 

от 0 до 65535

short

 

 

 

 

unsigned long

32

4

 

от 0 до 4 294 967 295

sbit

1

 

 

0 или 1

sfr

8

1

 

oт 0 до 255

sfr16

16

2

 

от 0 до 65535

Отметим, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется нулевой бит объявляемой переменной, т.е., если указано ключевое слово unsigned, то нулевой бит интерпретируется как часть числа, в противном случае нулевой бит интерпретируется как знаковый. В случае отсутствия ключевого слова unsigned целая переменная считается знаковой. В том случае, если спецификатор типа состоит из ключевого типа signed или unsigned и далее следует идентификатор переменной, то она будет рассматриваться как переменная типа int. Например:

unsigned int n; //Беззнаковое шестнадцатиразрядное число n unsigned int b;

int c;

//подразумевается signed int c

unsigned d;

//подразумевается unsigned int d

signed f;

//подразумевается signed int f

Отметим, что модификатор типа char используется для представления оди-

ночного символа или для объявления строковых литералов. Значением объекта типа char является код (размером 1 байт), соответствующий представляемому символу.

Отметим также, что восьмеричные и шестнадцатеричные константы также могут иметь модификатор unsigned. Это достигается указанием суффикса u или U после константы, константа без этого префикса считается знаковой.

Например: 0xA8C //int signed 01786l //long signed 0xF7u //int unsigned

Числа с плавающей запятой

Для переменных, представляющих число с плавающей запятой используется модификатор типа float. Модификатор double тоже допустим в языке программирования C51, но он не приводит к увеличению точности результата.

74

Величина с модификатором типа float занимает 4 байта. Из них 1 бит отводится для знака, 8 бит для избыточной экспоненты и 23 бита для мантиссы. Отметим, что старший бит мантиссы всегда равен 1, поэтому он не заполняется, в связи с этим диапазон значений переменной с плавающей точкой равен от ±1.175494E38 до ±3.402823E+38.

Пример объявления переменной с плавающей запятой: float f, a, b;

Переменные перечислимого типа

Переменная, которая может принимать значение из некоторого списка значений, называется переменной перечислимого типа или перечислением.

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

Например, вместо использования чисел 1,2,3,4,5,6,7 можно использовать названия дней недели: Poned, Vtorn, Sreda, Chetv, Pjatn, Subb, Voskr. При этом ка-

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

Переменные enum типа могут использоваться в индексных выражениях и как операнды в арифметических операциях и в операциях отношения. Например: if(rab_ned == SUB) dejstvie = rabota [rab_ned];

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

1.enum [имя типа перечисления] {список констант} имя1 [,имя2 ...];

2.enum имя перечисления описатель [,описатель..];

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

Список констант содержит одну или несколько конструкций вида: идентификатор [= константное выражение] Каждый идентификатор - это имя константы. Все идентификаторы в списке

75

enum должны быть уникальными. В случае если константе явным образом не задается число, то первому идентификатору присваивается значение 0, следующему идентификатору - значение 1 и т.д.

Пример объявления переменной rab_ned и типа переменных, совместимых с этой переменной, week выглядит следующим образом:

enum week {SUB = 0, /* константе SUB присвоено значение 0 */ VOS = 0, /* константе VOS присвоено значение 0 */

POND,

/* константе POND присвоено значение 1 */

VTOR,

/* константе VTOR присвоено значение 2 */

SRED,

/* константе SRED присвоено значение 3 */

HETV,

/* константе HETV присвоено значение 4 */

PJAT

/* константе PJAT присвоено значение 5 */

} rab_ned;

Идентификатор, связанный с константным выражением, принимает значение, задаваемое этим константным выражением. Результат вычисления константного выражения должен иметь тип int и может быть как положительным, так и отрицательным. Следующему идентификатору в списке, если этот идентификатор не имеет своего константного выражения, присваивается значение, равное константному выражению предыдущего идентификатора плюс 1. Использование констант должно подчиняться следующим правилам:

1.Объявляемая переменная может содержать повторяющиеся значения констант.

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

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

4.Значение может следовать за последним элементом списка перечисления.

Во втором формате для объявления переменной перечислимого типа используется уже готовый тип переменной уже объявленный ранее. Например: enum week rab1;

К переменной перечислимого типа можно обращаться при помощи указателей. При этом необходимо заранее определить тип переменной, на которую будет ссылаться указатель. Это может быть сделано, как описывалось выше или при помощи оператора typedef. Например:

Typedef enum {SUB = 0, /* константе SUB присвоено значение 0 */ VOS = 0, /* константе VOS присвоено значение 0 */

POND,

/* константе POND присвоено значение 1 */

VTOR,

/* константе VTOR присвоено значение 2 */

SRED,

/* константе SRED присвоено значение 3 */

76

HETV,

/* константе HETV присвоено значение 4 */

PJAT

/* константе PJAT присвоено значение 5 */

} week;

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

Объявление массивов в языке программирования C-51

При обработке данных достаточно часто приходится работать с рядом переменных одинакового типа (и описывающих одинаковые объекты). В этом случае эти переменные имеет смысл объединить одним идентификатором. Это позволяют сделать массивы.

Массивы - это группа элементов одинакового типа (float, char, int и т.п.). Из объявления массива компилятор должен получить информацию о типе элементов массива и их количестве. Объявление массива имеет два формата:

спецификатор-типа описатель [константное-выражение];

спецификатор-типа описатель [ ];

Описатель - это идентификатор массива.

Спецификатор-типа задает тип элементов объявляемого массива. Элементами массива не могут быть функции и элементы типа void.

Константное-выражение в квадратных скобках задает количество элементов массива. Константноевыражение при объявлении массива может быть опущено

вследующих случаях:

при объявлении массив инициализируется,

массив объявлен как формальный параметр функции,

массив объявлен как ссылка на массив, явно определенный в другом файле.

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

Каждое константное-выражение в квадратных скобках определяет число элементов по данному измерению массива, так что объявление двухмерного массива содержит два константных-выражения, трехмерного - три и т.д. Отметим, что в языке СИ первый элемент массива имеет индекс равный 0.

Примеры:

int a[2][3]; /* представлено в виде матрицы a[0][0] a[0][1] a[0][2]

a[1][0] a[1][1] a[1][2] */

float b[10]; /* вектор из 10 элементов имеющих тип double */ int w[3][3] = { { 2, 3, 4 },

{3, 4, 8 },

{1, 0, 9 } };

77

Впоследнем примере объявлен массив w[3][3]. Списки, выделенные в фигурные скобки, соответствуют строкам массива, в случае отсутствия скобок инициализация будет выполнена неправильно.

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

Примеры: int s[2][3];

Если при обращении к некоторой функции написать s[0], то будет передаваться нулевая строка массива s.

int b[2][3][4];

При обращении к массиву b можно написать, например, b[1][2] и будет передаваться вектор из четырех элементов, а обращение b[1] даст двухмерный массив размером 3 на 4. Нельзя написать b[2][4], подразумевая, что передаваться будет вектор, потому что это не соответствует ограничению наложенному на использование сечений массива.

Для работы с символьными строками в языке программирования С используются массивы символов, например:

char str[] = "объявление массива символов";

Следует учитывать, что в символьной строке находится на один элемент больше, так как последним элементом строки должен быть '\0'. В этом примере использовано неявное задание длины массива символов. Это стало возможным так как массиву сразу присваивается конкретное значение. При программировании микроконтроллеров семейства MCS-51 такое задание массива может привести к неоправданному расходу внутренней памяти данных, поэтому лучше воспользоваться размещением строки в памяти программ:

char code str[] = "объявление массива символов";

Структуры

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

Cтруктура - это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

struct { список описаний}

В структуре обязательно должен быть указан хотя бы один компонент.

78

Компоненты структуры называются полями структуры. Объявление полей производится в следующем виде:

тип-данных описатель; где тип-данных указывает тип структуры для объектов, определяемых в

описателях. В простейшей форме описатели представляют собой идентификаторы переменных или массивов.

Пример объявления структур: struct//Описание типа структуры-------------------------

{char tzvet,//Цвет точки

int x,

//Координата X

y;

//Координата Y

}//------------------------------------------------------

 

 

tochka1, tochka2, //Переменные, обозначающие точки дисплея

simv[7][9];

//Переменная, содержащая рисунок символа

struct

 

 

{int year;

//Поле структуры, в котором хранится год

char moth,

//Поле структуры, в котором хранится месяц

day;

//Поле структуры, в котором хранится день

}//-------------------------------------------------------

date1, date2;//Переменные, обозначающие две различных даты

Переменные tochka1, tochka2 объявляются как структуры, каждая из которых состоит из трех полей tzvet, х и у. Переменная simv объявляется как двумерный массив, состоящий из 63 переменных, описывающих точку дисплея. Во втором объявлении каждая из двух переменных date1, date2 состоит из трех компо-

нентов year, moth, day.

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

struct тип-структуры { список описаний; };

где тип-структуры является идентификатором типа объявляемой структуры и может быть использован для последующего объявления структур данного вида (то есть содержащих точно такие же поля) в форме:

struct тип-структуры список-идентификаторов;

В приведенном ниже примере идентификатор student описывается как тип структуры:

struct student

{char name[25];//Имя и фамилия студента

int id,

//Номер в журнале

age;

//Возраст

char usp;

//успеваемость

};

Пример:

79

struct student st1[23];//объявление массива переменных типа студент Доступ к полям структуры осуществляется с помощью указания имени

структуры и следующего через точку имени поля, например: st[1].name="Иванов";

st[1].id=2;

st[1].age=23;

Поля битов

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

struct { unsigned идентификатор 1 : длина-поля 1; unsigned идентификатор 2 : длина-поля 2; }

длина-поля задается целым выражением или константой. Эта константа определяет число битов, отведенное соответствующему полю. Поле нулевой длинны обозначает выравнивание на границу следующего слова.

Пример: struct

{unsigned R: 1;//Флаг приёма байта unsigned T: 1;//Флаг передачи байта unsigned Cmd:5;//Поле команды unsigned St: 1;//Поле статуса

} Cntr;

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

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

Cntr.Cmd=30;

Объединения (смеси)

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

Объединение применяется для следующих целей:

1.использования одного и той же области памяти для размещения переменных различного типа;

80

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

3.Объединение по описанию подобно структуре. Тип объединения может задаваться в следующем виде:

4.union { описание элемента 1;

i. ...

ii. описание элемента n; };

Доступ к элементам объединения осуществляется тем же способом, что и к структурам.

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

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

union {float Koeff; //Интерпретация объединения как переменной плавающего типа char byte[4];//Интерпретация объединения как массива

} bufer; //Объявление переменной bufer

Объединение bufer позволяет последовательному порту получить отдельный доступ ко всем байтам числа bufer.Koeff начиная от младшего байта bufer.byte[0], и заканчивая старшим байтом bufer.byte[3]. В программе затем можно пользоваться загруженным числом как числом с плавающей запятой.

ТЕМА 15

Операторы языка программирования С51, выражения в операторах. Операторы объявления, исполняемые операторы, оператор присваивания, условный оператор, структурный оператор {}, операторы цикла, оператор выбора switch, опе- ратор-выражение, пустой оператор

В языке программирования C-51 используется два типа операторов: операторы объявления и выполняемые операторы. Все операторы C-51 заканчиваются точкой с запятой.

Операторы объявления.

Объявление является неисполняемым оператором, который объявляет некоторый объект или набор объектов, связывает с ним один или несколько идентификаторов и, если это необходимо, распределяет память микроконтроллера.

Вобъявлении могут быть объявлены три типа объектов: переменные, метки

ифункции. В программе для каждого имени допустимо только одно объявление. Метка полностью объявлена, если она стоит перед выполняемым оператором и

81

заканчивается с двоеточием ':'. Например:

vfg: svGorit(); //Вызов подпрограммы с именем svGorit

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

char Rejim; //Переменная, хранящая номер режима размером в один байт int Schet; //Переменная, использующаяся как счётчик размером в два байта

В языке программирования C-51 нет необходимости знать конкретный адрес переменной, достаточно обратиться к ней по имени Rejim или Schet.

Функция объявляется точно так же как и переменные. То есть перед именем подпрограммы-функции записывается тип переменной, которую возвращает подпрограмма после своего завершения. Так как в подпрограмму в свою очередь тоже могут передаваться переменные, которые называются параметрами подпрограммы, то эти переменные записываются в скобках после имени подпрограммыфункции. Перед каждой переменной обязательно указывается ее тип. Например: char PrinjatByte(void); //Подпрограмма, предназначенная для приёма одного байта.

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

Исполняемые операторы.

Оператор присваивания:

Условный оператор.

Структурный оператор.

Оператор цикла for

Оператор цикла с проверкой условия до тела цикла

Оператор цикла с проверкой условия после тела цикла

Оператор continue

Оператор выбора

Оператор безусловного перехода

Оператор выражение

Оператор возвращения из подпрограммы

Пустой оператор

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

Оператор присваивания.

Оператор присваивания записывается в виде:

Переменная=выражение; Выражение вычисляется, и полученное значение присваивается перемен-

ной. Например:

82

P0=2; //Установить начальные потенциалы на ножках второго порта микроконтроллера

a=cos(b*5); //Этот оператор присваивания осуществляет вызов подпрограммыфункции.

Достаточно часто требуется изменять значение какой-либо переменной. То есть и источником и приёмником данных служит одна и та же переменная. В этом случае можно воспользоваться составным оператором присваивания. Использование составного оператора сокращает исходный текст программы. Например: sum+=3; //Оператор эквивалентен оператору sum=sum+3;

Umensh-=5; //Оператор эквивалентен оператору Umensh=Umensh-5; a*=10; //Оператор эквивалентен оператору a=a*5;

mask&=0x10;//Оператор эквивалентен оператору mask=mask&5; Обычно используется для записи нулей в определённые биты переменной

Условный оператор.

Оператор if обеспечивает условное выполнение операторов. Он записывается в следующей форме:

if(<выражение>) <operator-1>; [else <operator-2>;]

При этом ключевое слово else со следующим за ним исполняемым оператором представляют собой необязательную часть условного оператора. Если результат вычисления выражения равен 1 (истина), то выполняется operator-1. Если результат вычисления выражения равен 0 (ложь), то выполняется operator-2. Если выражение ложно и отсутствует оператор-2, то выполняется оператор, следующий за условным. Пример записи условного оператора:

if(Wes<Min)

/*Условная операция*/

Schetch=Schetch+1; /*Плечо 1*/

else

 

Schetch=0;

/*Плечо 2*/

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

if(P1.5)fnKn5Naj();

//В этом примере предполагается, что к пятой ножке

порта P1 подключена кнопка с надписью "5"

if(Kn5Naj)fnKn5Naj();

//Этот пример эквивалентен предыдущему, но ножке

P1.5 поставлена в соответствие переменная Kn5Naj

83

if(PrinjatByte())DecodCmd(); //Предполагается, что функция PrinjatByte возвраща-

ет значение '1', если байт принят В приведённом примере использована подпрограмма-функция, осуществ-

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

Структурный оператор {} Существует два основных способа использования структурного оператора:

1.Структурный оператор может рассматриваться в качестве отдельного оператора языка С-51 и использоваться в программе везде, где может встречаться отдельный исполняемый оператор. Это используется в операторах for, while, do while, switch of и if;

2.Структурный оператор ограничивает область действия локальных переменных.

Каждый оператор внутри структурного оператора может являться любым оператором языка C-51, в том числе и объявлением, при условии, что все объявления внутри структурного оператора должны быть выполнены до первого исполняемого оператора.

Структурный оператор начинается с открывающей скобки '{' и записывается в следующем виде:

{<operator-1>; //Здесь могут быть объявления переменных

<operator-2>;

...

<operator-n>;

}

Заметим, что в конце составного оператора точка с запятой не ставится. Пример использования структурного оператора:

if(Wes<Min)

/*Условная операция*/

{incr=incr*2;

/*Структурный оператор*/

Schetch=Schetch+1; /*Содержит два оператора*/

}

Структурные операторы могут вкладываться друг в друга: {<operator-1>;

<operator-2>; {<operator-A>; <operator-B> <operator-C>;

}

<operator-3>; <operator-4>;

}

Пример использования объявлений внутри вложенного структурного опе-

84

ратора: int main () {int q,b; float t,d;

...

if(...) {int e,g; float f,q;

...

}

...

return (0);

}

Переменные e,g,f,q будут уничтожены после выполнения составного оператора. Отметим, что переменная q является локальной в составном операторе, т.е. она никоим образом не связана с переменной q объявленной в начале функции main с типом int. Отметим также, что выражение стоящее после return может быть заключено в круглые скобки, хотя наличие последних необязательно.

Оператор цикла for

Оператор for - это наиболее общий способ организации цикла. Оператор цикла for записывается в следующей форме:

for ( выражение 1 ; выражение 2 ; выражение 3 ) тело цикла;

Выражение 1 обычно используется для установления начального значения переменных, управляющих циклом. Выражение 2 - это выражение, определяющее условие, при котором тело цикла будет выполняться. Выражение 3 определяет изменение переменных, управляющих циклом после каждого выполнения тела цикла. В качестве тела цикла может служить любой исполняемый оператор языка C-51, в том числе и составной оператор. Внутри составного оператора может быть заключено любое количество исполняемых операторов.

Схема выполнения оператора for:

1.Вычисляется выражение 1.

2.Вычисляется выражение 2.

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

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

Пример использования оператора for:

85

for(i=1;i<10;i++) //от i равного 1 до 10 с шагом 1 выполнить b=i*i;

В этом примере вычисляются квадраты чисел от 1 до 9.

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

Пример: int main()

{int top,bot;

char string[100],temp; for(top=0,bot=100;top<bot;top++,bot--) {temp=string[top];

string[bot]=temp;

}

return 0;

}

Вэтом примере, реализующем запись строки символов в обратном порядке, для управления циклом используются две переменные top и bot. Отметим, что на месте выражение 1 и выражение 3 здесь используются несколько выражений, записанных через запятую, и выполняемых последовательно.

Так как согласно синтаксису языка С-51 оператор может быть пустым, тело оператора for также может быть пустым. Такая форма оператора может быть использована для организации поиска.

Пример: for(i=0;t[i]<10;i++);

Вданном примере переменная цикла i принимает значение номера первого элемента массива t, значение которого больше 10.

При написании программ для микроконтроллеров достаточно часто требуется реализовать бесконечный цикл. В этом случае оператор цикла for принимает следующий вид:

for(;;) //Постоянно

{Knop=P2; //опрашивать порт P2 ObrabSobyt(); //и обрабатывать нажатие кнопок

}

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

Оператор цикла с проверкой условия до тела цикла while

Оператор цикла while называется циклом с предусловием и имеет следующий формат:

while (выражение) тело цикла;

86

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

1.Вычисляется выражение.

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

3.Процесс повторяется с пункта 1.

Оператор while может быть полезен для ожидания срабатывания какоголибо устройства микроконтроллера, например таймера:

...

 

while(!TF0);

//Программа ожидает переполнения таймера T0

TF0=0;

 

TL0=time;

//Настроить таймер T0

TН0=time>>8; //на очередной интервал времени

...

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

i = 0; while(table(i)<=Level)i++;

В приведённом примере Table - это предварительно объявленный массив. Переменные Level и i тоже должны быть предварительно объявлены.

Переменной i первоначально присваивается значение 0, затем она используется как индекс для массива Table. Так как i увеличивается при каждом проходе цикла while, то каждый раз, когда выполняется оператор внутри блока while, с переменной Level сравнивается следующий элемент массива Table. Когда найден элемент, превышающий значение переменной Level, то условие в операторе while больше неверно, выполнение блока не повторяется и управление передается следующему за циклом while оператору. С этого момента переменная i является индексом первого элемента массива Table, который превышает значение перемен-

ной Level.

Оператор цикла с проверкой условия после тела цикла do while

Оператор цикла while называется циклом с постусловием и имеет следующий формат:

do тело цикла while (выражение);

Оператор do while содержит условную операцию (такую же, как в операто-

87

ре if), и вызывает исполнение операторов в этом блоке до тех пор, пока условие верно. Проверка условия производится после выполнения тела цикла, поэтому оператор тела цикла будет выполнен хотя бы один раз. В качестве выражения допускается использовать любое выражение языка Си, а в качестве тела любой оператор, в том числе пустой или составной. Схема выполнения оператора do while следующая:

1.Выполняется тело цикла (которое может быть составным оператором)

2.Вычисляется выражение.

3.Если выражение ложно, то выполнение оператора do while заканчивается и выполняется следующий по порядку оператор. Если выражение истинно, то выполнение оператора продолжается с пункта 1.

Чтобы прервать выполнение цикла до того, как условие станет ложным, можно использовать оператор break.

Оператор do while в большинстве случаев соответствует одной машинной команде, поэтому может быть использован для написания эффективных по коду и быстродействию программ. Например:

i=10

do тело цикла; while(--i<0);

Однако, конкретно в реализации компилятора C-51, намного эффективнее следующая конструкция:

for(i=10;i>0;i--) тело цикла;

Операторы while и do while могут быть вложенными. Пример использования вложенных операторов цикла: int i,j,k;

...

i=0;j=0;k=0; do{i++;j--;

while (a[k]<i)k++;

}

while(i<30&&j<-30);

Оператор break

Оператор break обеспечивает прекращение выполнения самого внутреннего из охватывающих его операторов switch, do, for, while. После выполнения оператора break управление передается оператору, следующему за прерванным оператором цикла или выбора.

Оператор continue

Оператор continue, как и оператор break, используется только внутри операторов цикла, но в отличие от него выполнение цикла не прерывается, а начинается следующая итерация цикла. Формат записи оператора:

88

continue;

Пример: int main()

{int a,b; for(a=1,b=0;a<100;b+=a,a++) {if(b%2)continue;

... /* обработка четных сумм */

}

return 0;

}

Когда сумма чисел от 1 до а становится нечетной, оператор continue передает управление на очередную итерацию цикла for, не выполняя операторы обработки четных сумм.

Оператор continue, как и оператор break, прерывает самый внутренний из объемлющих его циклов.

Оператор выбора switch

Оператор switch предназначен для организации выбора из множества различных вариантов. Формат оператора следующий:

switch ( выражение ) {[объявление]

...

[ case константное-выражение1]: [ список-операторов1] [ case константное-выражение2]: [ список-операторов2]

...

...

[ default: [ список операторов ]]

}

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

Значение этого выражения является ключевым для выбора из нескольких вариантов. Тело оператора smitch состоит из нескольких операторов, помеченных ключевым словом case с последующим константным выражением.

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

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

Список операторов может быть пустым, либо содержать один или более операторов. Причем в операторе switch не требуется заключать последователь-

89

ность операторов в фигурные скобки.

Отметим также, что в операторе switch можно использовать свои локальные переменные, объявления которых находятся перед первым ключевым словом case, однако в объявлениях не должна использоваться инициализация.

Схема выполнения оператора switch следующая:

вычисляется выражение в круглых скобках;

вычисленные значения последовательно сравниваются с константными выражениями, следующими за ключевыми словами case;

если одно из константных выражений совпадает со значением выражения, то управление передается на оператор, помеченный соответствующим ключевым словом case;

если ни одно из константных выражений не равно выражению, то управление передается на оператор, помеченный ключевым словом default, а в случае его отсутствия управление передается на следующий после switch оператор.

Отметим интересную особенность использования оператора switch: конструкция со словом default может быть не последней в теле оператора switch. Ключевые слова case и default в теле оператора switch существенны только при начальной проверке, когда определяется начальная точка выполнения тела оператора switch. Все операторы, между начальным оператором и концом тела, выполняются вне зависимости от ключевых слов, если только какой-то из операторов не передаст управления из тела оператора switch. Таким образом, программист должен сам позаботится о выходе из case, если это необходимо. Чаще всего для этого используется оператор break.

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

Если значение выражения равно 0, то выполняется только первый оператор присваивания, и 0 будет присвоен переменной red (красный англ). Если значение выражения равно 1, то будет выполнен только второй оператор присваивания и переменной blue (голубой англ) будет присвоен 0. Значения выражения 2 и 3 вызовет присваивание 0 переменным green (зеленый англ) и gray (серый англ) соответственно.

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

Оператор безусловного перехода goto.

90

Оператор goto изменяет порядок выполнения программы при помощи передачи управления на оператор, метка которого указана в операторе goto. Оператор goto записывается в следующем виде:

goto имя-метки;

...

имя-метки: оператор;

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

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

Использование операторов goto является неизбежным при некоторых ситуациях, однако в большинстве случаев там, где должна быть предусмотрена передача управления, более предпочтительным является использование итеративного оператора while, do while, switch, if или вызова функции. Неограниченное использование операторов goto в программе приводит к тому, что программу становится трудно понимать, модифицировать и сопровождать. Реальная практика использования языка C-51 показывает, что в большинстве случаев программные модули могут не содержать оператор goto без ухудшения их эффективности.

Оператор-выражение

Любое выражение, которое заканчивается точкой с запятой, является оператором.

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

Примеры записи операторов-выражений:

++ i;

//Этот оператор представляет выражение, которое увеличивает значение

переменной i на единицу.

a(x,y);

//Этот оператор представляет выражение состоящее из вызова подпро-

граммы-процедуры.

Оператор возвращения из подпрограммы return.

Оператор return завершает выполнение функции, в которой он задан, и возвращает управление в вызывающую функцию, в точку, непосредственно следую-

91

щую за вызовом. Формат оператора: return [выражение] ;

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

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

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

Пример: int sum(int a,int b) {return(a+b);

}

Функция sum имеет два формальных параметра a и b типа int, и возвращает значение типа int, о чем говорит описатель, стоящий перед именем функции. Возвращаемое оператором return значение равно сумме фактических параметров.

Пример:

void prov (int a, double b) {float c;

if(a<3) return; else if(b>10) return; else {c=a+b;

if((2*c-b)==11)return;

}

}

В этом примере оператор return используется для выхода из функции в случае выполнения одного из проверяемых условий.

Пустой оператор.

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

в операторах цикла do while, for, while при использовании их в качестве элементов задержки, в операторе if в строках, когда место оператора не требуется, но по синтаксису требуется хотя бы один оператор;

92

при необходимости пометить структурный оператор.

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

Пример: int main ( )

{ ...

{if (...) goto a; /* переход на скобку */ { ...

}

a:;} return 0;

}

ТЕМА 16

Использование подпрограмм в языке программирования С51.Определение подпрограмм, параметры подпрограмм, предварительное объявление подпрограмм, вызов подпрограмм, рекурсивный вызов подпрограмм, подпрограммы обработки прерываний, многомодульные программы

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

Функция - это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Термин функция в языке программирования С эквивалентен понятию подпрограммы. Действия, выполняемые основной программой в других языках программирования такие как очистка внутреннего ОЗУ и присваивание начального значения переменным, выполняются автоматически при включении питания. После завершения этих действий вызывается подпрограмма с именем main. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова.

Влюбой программе, написанной на языке программирования С-51 должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы. Обратите внимание, что на языке программирования С-51 пишутся программы для микроконтроллеров. Поэтому эти программы не должны завершаться пока включено питание микроконтроллера. Это значит, что в функции main обязательно должен быть бесконечный цикл. В противном случае при выходе из этой функции управление будет передано случайному адресу памяти программ. Это может привести к непредсказуемым результатам, вплоть до выхода микроконтроллерной

93

системы из строя.

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

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

С использованием функций в языке С-51 связаны три понятия - определение функции (описание действий, выполняемых подпрограммой-функцией), объявление функции (задание формы обращения к функции) и вызов функции.

Определение подпрограмм

Определение функции состоит из заголовка и тела. Определение функции записывается в следующем виде:

[спецификатор класса памяти] [спецификатор типа] имя функции ([список формальных параметров]) //Заголовок функции { //тело функции

}

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

Тело функции - это составной оператор, содержащий операторы, определяющие действие функции. Тело функции начинается с фигурной скобки '{' и состоит из объявления переменных и исполняемых операторов. Именно эти операторы, входящие в тело функции, и определяют действие функции. Завершается тело функции закрывающей фигурной скобкой '}'.

Пример определения функции: bit SostKnIzm(void)//Заголовок функции

{//---------------

начало тела функции--------------

bit tmp=0;

 

if(P0!=0xff)tmp=1;

return tmp;

 

}//---------------

конец тела функции-----------------

//================== Вызывающая подпрограмма ==========

...

if(SostKnIzm()) //Вызов подпрограммы SostKnIzm DecodSostKn();

В приведенном примере показано как при помощи функции, возвращающей

94

битовую переменную можно повысить наглядность исходного текста программы. Оператор if(SostKnIzm()) DecodSostKn(); практически не требует комментариев. Имя функции SostKnIzm показывает что контролирует эта функция.

Необязательный спецификатор класса памяти задает класс памяти функции, который может быть static или extern.

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

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

Спецификатор типа функции задает тип возвращаемого значения и может задавать любой тип. Если спецификатор типа не задан, то по умолчанию предполагается, что функция возвращает значение типа int. Функция не может возвращать массив или функцию, но может возвращать указатель на любой тип, в том числе и на массив и на функцию. Тип возвращаемого значения, задаваемый в определении функции, должен соответствовать типу в объявлении этой функции.

Функция возвращает значение если ее выполнение заканчивается оператором return, содержащим некоторое выражение. Указанное выражение вычисляется, преобразуется, если необходимо, к типу возвращаемого значения и возвращается в точку вызова функции в качестве результата. Пример определения подпро- граммы-функции:

#include <reg51.h> //Подключить описания внутренних регистров микроконтроллера

char getkey ()

//Заголовок функции, возвращающей байт, принятый по после-

довательному порту

{while (!RI);

//Если последовательный порт принял байт,

RI = 0;

//то подготовиться к приёму следующего байта

return (SBUF);

//и передать принятый байт в вызывающую подпрограмму.

95

}

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

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

void putchar (char c) //Заголовок функции, передающей один байт через последовательный порт

{while (!TI);

//Если передатчик последовательного порта готов к передаче

байта

 

SBUF = c;

//то занести в буфер передатчика последовательного порта байт

TI = 0;

//и начать передачу

}

 

Все переменные, объявленные в теле функции без указания класса памяти, имеют класс памяти auto, т.е. они являются локальными. Так как глубина стека в процессорах семейства MCS-51 ограничена 256 байтами, то при вызове функций аргументам назначаются конкретные адреса во внутренней памяти микроконтроллера и производится их инициализация. Управление передается первому оператору тела функции и начинается выполнение функции, которое продолжается до тех пор, пока не встретится оператор return или последний оператор тела функции. Управление при этом возвращается в точку, следующую за точкой вызова, а локальные переменные становятся недоступными. При выходе из функции значения этих переменных теряются, так как при вызове других функций эти же ячейки памяти распределяются для их локальных переменных.

Если необходимо, чтобы переменная, объявленная внутри подпрограммы сохраняла своё значение при следующем вызове подпрограммы, то ее необходимо объявить с классом памяти static.

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

Список формальных параметров - это последовательность объявлений формальных параметров, разделенная запятыми. Формальные параметры - это переменные, используемые внутри тела функции и получающие значение при вызове функции путем копирования в них значений соответствующих фактических параметров.

Пример определения функции с одним параметром: int rus (unsigned char r) //Заголовок функции {//---------------начало тела функции--------------

if (r>='А' && c<=' ') return 1;

96

else return 0;

}//---------------конец тела функции-----------------

В данном примере определена функция с именем rus, имеющая один параметр с именем r и типом unsigned char. Функция возвращает целое значение, равное 1, если параметр функции является буквой русского алфавита, или 0 в противном случае.

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

{P0=0;

//Зажигание светодиода

while(1);

//Бесконечный цикл

}

 

Порядок и типы формальных параметров должны быть одинаковыми в определении функции и во всех ее объявлениях. Поэтому желательно объявление функции поместить в отдельный файл, который затем можно включить в исходные тексты программных модулей при помощи директивы #include. Типы фактических параметров при вызове функции должны быть совместимы с типами соответствующих формальных параметров. Тип формального параметра может быть любым основным типом, структурой, объединением, перечислением, указателем или массивом. Если тип формального параметра не указан, то этому параметру присваивается тип int.

Для формального параметра можно задавать класс памяти register, при этом для величин типа int спецификатор типа можно опустить. Однако все известные мне компиляторы языка программирования С-51 игнорируют этот спецификатор, так как расположение параметров в памяти микроконтроллера оптимизируется с точки зрения использования минимального количества необходимой внутренней памяти.

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

 

 

 

 

 

Таблица 16.1

Номер аргу-

char, однобайтовый

int, двухбайтовый ука-

long,float

Нетипизированные ука-

мента

указатель

 

затель

 

затели

1

R7

R6,R7

 

R4 - R7

R1 - R3

2

R5

R4,R5

 

R4 - R7

R1 - R3

3

R3

R2,R3

 

 

R1 - R3

Поскольку при вызове функции значения фактических параметров копируются в локальные переменные, в теле функции нельзя изменить значения переменных в вызывающей функции. Например нужно поменять местами значения переменных x и y:

/* Неправильное использование параметров функции */

97

void change (int x, int y) {int k=x;

x=y;

y=k;

}

В данной функции значения локальных переменных x и y, являющихся формальными параметрами, меняются местами, но поскольку эти переменные существуют только внутри функции change, значения фактических параметров, используемых при вызове функции, останутся неизменными.

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

/* Правильное использование параметров функции */ void change (int *x, int *y)

{int k=*x; *x=*y; *y=k;

}

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

change (&a,&b);

Предварительное объявление подпрограмм.

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

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

Прототип - это явное объявление функции, которое предшествует определению функции. Тип возвращаемого значения при объявлении функции должен

98

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

[спецификатор класса памяти] [спецификатор типа] имя функции ([список формальных параметров]);

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

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

int rus (unsigned char r);

При этом в объявлении функции имена формальных параметров могут быть опущены:

int rus (unsigned char);

Вызов подпрограмм.

Вызов функции имеет следующий формат: имя функции ([список выражений]);

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

DecodSostKn();

VypFunc();

Функция, если она возвращает какое-либо значение (подпрограммафункция), может быть вызвана и в составе выражения, например:

y=sin(x); //sin - это имя подпрограммы-функции if(rus(c))SvDiod=Gorit; //rus - это имя подпрограммы-функции

Выполнение вызова функции происходит следующим образом:

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

2.Происходит присваивание значений фактических параметров соответствующим формальным параметрам.

3.Управление передается на первый оператор функции.

Поскольку имя функции является адресом начала тела функции, то в каче-

99

стве обращения к функции может быть использовано не только имя функции но и значение указателя на функцию. Это значит что функция может быть вызвана через указатель на функцию. Пример объявления указателя на функцию:

int (*fun)(int x, int *y);

Здесь объявлена переменная fun как указатель на функцию с двумя параметрами: типа int и указателем на int. Сама функция должна возвращать значение типа int. Круглые скобки, содержащие имя указателя fun и признак указателя *, обязательны, иначе запись

int *fun (int x,int *y);

будет интерпретироваться как объявление функции fun возвращающей указатель на int.

Вызов функции возможен только после инициализации значения указателя:

float (*funPtr)(int x, int y);

 

float fun2(int k, int l);

 

...

 

 

funPtr=fun2;

/* инициализация указателя на функцию */

(*funPtr)(2,7);

/* обращение к функции

*/

В рассмотренном примере указатель на функцию funPtr описан как указатель на функцию с двумя параметрами, возвращающую значение типа double, и также описана функция fun2. В противном случае, т.е. когда указателю на функцию присваивается функция описанная иначе чем указатель, произойдет ошибка.

Рассмотрим пример использования указателя на функцию в качестве параметра функции вычисляющей производную от функции cos(x).

Пример:

float proiz(float x,float dx,float(*f)(float x)); float fun(float z);

int main()

 

 

 

 

 

{float x;

/*

точка вычисления производной */

 

float dx;

/*

приращение

*/

 

float z;

/*

значение производной

*/

 

scanf("%f,%f",&x,&dx);

/* ввод значений x и dx

*/

z=proiz(x,dx,fun);

/*

вызов функции

*/

 

printf("%f",z);

 

/* печать значения производной */

}

 

 

 

 

 

float proiz(float x,float dx,float (*f)(float z))/* функция вычисляющая производную */

{float xk,xk1; xk=fun(x); xk1=fun(x+dx);

return (xk1/xk-1e0)*xk/dx;

100

}

float fun( float z) /* функция от которой вычисляется производная */ {return (cos(z));

}

Для вычисления производной от какой-либо другой функции можно изменить тело функции fun или использовать при вызове функции proiz имя другой функции. В частности, для вычисления производной от функции cos(x) можно вызвать функцию proiz в форме

z=proiz(x,dx,cos);

а для вычисления производной от функции sin(x) в форме z=proiz(x,dx,sin);

Рекурсивный вызов подпрограмм.

В стандартном языке программирования С все функции могут быть вызваны сами из себя или использоваться различными программными потоками одновременно. Для этого все локальные переменные располагаются в стеке. В микроконтроллерах семейства MCS-51 ресурсы внутренней памяти данных ограничены, поэтому в языке программирования С-51 для функций локальные переменные по умолчанию располагаются не в стеке, а непосредственно во внутренней памяти микроконтроллера. Если же подпрограмма должна вызываться рекурсивно, то ее необходимо объявить как программу с повторным вызовом (reentrant):

return_type funcname ([args]) reentrant

Классический пример рекурсии - это математическое определение факториала n!:

n! = 1 при n=0; n*(n-1)! при n>1 .

Функция, вычисляющая факториал, будет иметь следующий вид: long fakt(int n) reentrant

{return ((n==1)?1:n*fakt(n-1));

}

Подпрограммы обработки прерываний.

Атрибут INTERRUPT позволяет объявить подпрограмму-процедуру обработки сигналов прерываний, поступающих от внешних устройств. Подпрограмма процедура с этим атрибутом вызывается при получении микроконтроллером соответствующего сигнала прерывания. Подпрограмма обработки прерываний не может быть подпрограммой функцией и не может иметь переменные-параметры. Формат использования атрибута:

interrupt N;

где N-любое десятичное число от 0 до 31.

Число N определяет номер обрабатываемого прерывания. При этом номер 0 соответствует внешнему прерыванию от ножки INT0, номер 1 соответствует пре-

101

рыванию от таймера 0, номер 2 соответствует внешнему прерыванию от ножки INT1 и так далее. Пример подпрограммы-обработчика прерывания от таймера 0: void IntTim0(void) interrupt 2

{TH0=25; TL0=32; //Задать новый интервал времени таймера T0

TF=0; //Сбросить флаг таймера T0 для разрешения следующего прерывания от данного таймера

}

При работе с прерываниями определяющим фактором является время реакции на прерывание. Для того, чтобы не сохранять содержимое используемых регистров микроконтроллера в стеке, в микроконтроллерах предусмотрено использование отдельных регистровых банков. В языке программирования С-51 для этого необходимо в объявлении подпрограммы указать используемый ею банк регистров. Для этого служить атрибут using:

void IntTim0(void) interrupt 2 using 1

{TH0=25; TL0=32; //Задать новый интервал времени таймера T0

TF=0; //Сбросить флаг таймера T0 для разрешения следующего прерывания от данного таймера

}

Многомодульные программы.

Язык программирования C-51 позволяет писать многомодульные програм-

мы.

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

Раздельная трансляция программы возможна при использовании двух программ: транслятора исходного текста программы c-51 и редактора связей bl-51.

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

Для того, чтобы транслятор вместо формирования сообщения об ошибке записал в объектный модуль информацию, необходимую для редактора связей, нужно осуществить объявление внешних переменных подпрограмм и меток. Для объявления внешних переменных после обычного объявления добавляется слово extern. При таком объявлении при трансляции исходного текста программы для переменной память данных для переменных не распределяется, а создаются соответствующие записи для редактора связей, позволяющие осуществить соответствующие вызовы подпрограмм и переходы между модулями, а также обращение к переменным другого модуля. Пример объявления внешних переменных на языке программирования C-51:

102

extern char RejRab; extern char UrovGr; extern int NomKan; extern bit Sost; extern bit Soob;

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

Все используемые в модуле подпрограммы должны быть предварительно объявлены или определены. Это же относится и к подпрограммам из других модулей. При объявлении подпрограммы должны быть полностью объявлены все ее параметры. Пример объявления внешних подпрограмм на языке программирования С-51:

char TestOZU(void);

char Rd558(char AdrRAM,char NachAdr,char Nbyte);

char Wr558(char AdrRAM,char NachAdr,char Nbyte);

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

Программа bl-51 объединяет несколько объектных файлов (модулей) в один. Для объединения нескольких модулей в исполняемую программу имена всех модулей передаются в редактор связей в качестве параметров при запуске этой программы. Пример вызова редактора связей из командной строки DOS для объединения трёх модулей:

bl51.exe progr.obj, modul1.obj, modul2.obj

Врезультате работы редактора связей в этом примере будет создан исполняемый модуль с именем progr.

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

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

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

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

103

объектный модуль, но и файл описания своих переменных с атрибутом EXTERNAL. Такие файлы называются файлами заголовками и обычно записываются с расширением *.h. Пример исходного текста модуля приведён ниже:

/*Исходный текст модуля comm.plm*/ $CODE

$NOLIST

Kommand: do;

$INCLUDE (C:\COMP\PLM51\BIN\Rg51FA.Dcl)

$INCLUDE (stdio.h) /*Подключение других файлов заголовков*/ $INCLUDE (dcl.h)

$LIST

declare

NomPult byte external, Bufer(7) byte external, RejRab byte public, NomKan word public,

PrmSoob: procedure using 2; declare

i byte;

for i=0 to KolBytes; do while not RI;end; BufInd(i)=SBuf; RI=0;

end;

Soob=true; end PrmSoob;

PriemKom: procedure interrupt 4 using 2; KodKom=(SBuf and 0f0h)/16; KolBytes=SBuf and 0fh;

RI=0;

If KodKom=KomSoob then call PrmSoob; If KodKom=KomSost then call PrmSost; end PriemKom;

/*------- Конец модуля ---------*/

Исходный текст файла-заголовка этого модуля: /*Начало файла заголовка comm.h*/

declare

104

RejRab byte external,

NomKan word external,

PriemKom:procedure external;end PriemKom; /*------- Конец файла заголовка---------*/

Теперь для того, чтобы воспользоваться приведённым выше модулем достаточно записать строку:

$INCLUDE (comm.h)

Естественно, так как процесс трансляции разбит на два этапа, то ошибки могут возникать не только на этапе трансляции модулей, но и при связывании модулей в исполняемую программу. Наиболее распространённые ошибки - это забыть включить имя объектного модуля в строку вызова редактора связей, забыть объявить переменную или подпрограмму как PUBLIC или использовать одни и те же адреса для переменных или констант в различных модулях. Сообщения об этих ошибках выводятся в файл с расширением *.m51.

ДОПОЛНИТЕЛЬНЫЙ МАТЕРИАЛ.

СРЕДА ПРОГРАММИРОВАНИЯ И ОТЛАДКИ UVISION 2

Отладка программного обеспечения микроконтроллеров. Интегрированная среда программирования Keil μVision2, запуск сеанса отладки, точки прерывания выполнения программы, команды отладчика, программирования отладочных функций, функции моделирования внешней среды микроконтроллера.

После того, как программные модули были успешно оттранслированы, размещены по конкретным адресам и связаны между собой, для отладки программы можно воспользоваться любым из следующих методов:

внутрисхемным эмулятором

встроенным программным отладчиком

внешним программным отладчиком

отлаживаемым устройством с записанным в память программ двоичным кодом программы

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

105

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

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

Встроенный программный отладчик, входящий в состав интегрирован-

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

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

При использовании интегрированной среды программирования предос-

тавляется удобный интерфейс, позволяющий легко отлаживать разрабатываемую программу. В настоящее время стандартом де-факто стал интерфейс, похожий на программную оболочку Visual C. Пример внешнего вида такой программной оболочки имеет программный комплекс Keil μVision2 и его основная компонента -

µVision Debugger.

µVision Debugger фирмы Keil позволяет вести отладку программ, написанных на С и ассемблере или в смешанном формате, сохраняет историю трассировки и позволяет выбирать между симулятором, монитором и внутрисхемным эмулятором.

CPU & Peripheral Simulator - симулятор CPU и периферии

106

Performance Analyzer & Code Coverage - анализаторы производительности и эффективности кода

Target Monitor - отладочный монитор

Breakpoints - точки останова

Debug Function Scripts - C-подобный язык для записи функций

Variables and Memory - просмотр областей памяти и регистров

CPU & Peripheral Simulator

µVision Simulator - чисто программный продукт, который осуществляет отладку в исходных кодах, симуляцию на уровне символов и отладку на рабочей плате - target debugging. Моделируется вся система команд и все периферийные устройства. Симулятор полностью поддерживает периферийные устройства микроконтроллера посредством специальных драйверов xxx.DLL. В распоряжение пользователя предоставлены окна, отображающие состояния таймеров, портов, прерываний, сторожевого таймера, последовательного порта, аналоговоцифрового преобразователя и т.д. Параметры этих устройств могут быть установлены и изменены в соответствии с контекстом приложения.

Performance Analyzer & Code Coverage

В µVision Debugger встроен анализатор производительности Performance Analyzer, который фиксирует время исполнения программных модулей. Задавая список модулей для анализа, пользователь получает диаграмму затрат времени на каждую часть программы. µVision Debugger позволяет также провести анализ эффективности кода Code Coverage, локализуя части программы, к которым редко происходит обращение, что позволяет удалить ненужный код.

Target Monitor

При отладке программ на плате в качестве интерфейса используется специально сконфигурированный отладочный монитор, загружаемый в ОЗУ с помощью встроенного начального загрузчика или прошиваемый в EPROM. Программа монитор обеспечивает прямой интерфейс для отладчика/симулятора и легко настраивается на любой микроконтроллер. При помощи монитора производится комплексная отладка приложения на плате. В остальном же отладка ничем не отличается от режима симуляции. Требования к ресурсам микроконтроллера со стороны монитора минимальны.

Breakpoints

µVision предлагает широкие возможности по заданию простых и условных точек останова. Условием останова может быть или результат выражения или операция обращения к ячейке памяти/ переменной (чтение, запись, доступ). Для редактирования и просмотра параметров контрольных точек служит окно Breakpoint. Точки останова могут остановить исполнение программы или запус-

107

тить команду или сценарий отладчика.

Debug Function Scripts

Для автоматизации типовых операций, выполняемых при отладке, могут быть созданы специальные командные файлы. Для ввода этих команд служит окно Command или окно Toolbox. Кроме того µVision поддерживает C-подобный функциональный язык, позволяющий генерировать:

Встроенные функции типа printf, memset, rand и другие

Сигнальные функции для моделирования аналоговых и цифровых входных/выходных сигналов CPU

Функции пользователя для расширения возможностей команд и повторяющихся операций

Variables and Memory

В распоряжении пользователя находятся окна для просмотра областей памяти Memory и состояний регистров Register. С помощью окна Serial I/O становиться возможной наглядная симуляция последовательного ввода/вывода. µVision предлагает несколько путей для просмотра и изменения переменных и памяти.

Использование симулятора (Use Simulator) – позволяет сконфигуриро-

вать отладчик µVision2 как только программный продукт, который симулирует большинство свойств микроконтроллеров семейства 8051 без необходимости в действительности иметь целевое «железо». Можно тестировать и отлаживать приложение до того как будет готово «железо». Отладчик µVision2 симулирует большой набор периферии, включая последовательный порт, внешний ввод-вывод и таймеры. Набор периферии выбирается, когда выбирается тип целевого процессора из базы данных поддерживаемых устройств.

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

Запуск сеанса отладки

Вы можете запустить режим отладки в среде µVision2 командой меню Debug – Start/Stop Debug Session. В зависимости от конфигурации заданной на закладке Debug диалога Options for Target, отладчик µVision2 загрузит отлаживаемую программу и запустит на исполнение программу инициализации. Среда µVision2 сохраняет расположение окон в режиме редактирования и восстанавливает расположение окон во время последнего сеанса отладки. Когда выполнение отлаживаемой программы останавливается, то отладчик µVision2 открывает окно редактирования с исходным текстом или показывает инструкции процессора в

108

окне дизассемблера. Следующая инструкция, которая будет исполнена, отмечается желтой стрелкой.

В течение сеанса отладки большинство возможностей редактирования остаются доступными. Например, можно использовать команду поиска текста или исправлять ошибки в программе. Исходный текст программы отображается в тех же самых окнах редактирования. Режим отладки среды µVision2 отличается от режима редактирования в таких аспектах:

Доступны меню «Debug» и команды «Debug».

Структура проекта или параметры компилятора/линкера не могут быть изменены. Запрещены все команды компиляции и компоновки программы. Окно дизассемблера показывает программу как смесь исходного текста и

соответствующих инструкций ассемблера или только как ассемблерный код. Историю трассировки ранее исполненных инструкций можно вывести на экран командой меню Debug – View Trace Records. Чтобы разрешить трассировку выбе-

рите меню Debug – Enable/Disable Trace Recording.

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

Вы можете использовать диалоговое окно Debug – Inline Assembly…, чтобы модифицировать инструкции процессора. Эта возможность позволяет Вам корректировать ошибки или производить временные изменения в коде отлажи-

109

ваемой программы.

Точки останова (Breakpoints)

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

Спомощью кнопок на панели File Toolbar. Просто выберите строку кода в окне редактирования или в окне дизассемблера и щелкните по кнопке, относящейся к точкам останова.

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

Диалоговое окно Debug – Breakpoints… позволяет Вам просмотреть, установить или модифицировать настройки точек останова. Это диалоговое окно также позволяет Вам установить точки останова с различными атрибутами по доступу к памяти. См. пример ниже.

На странице Command окна Output Window Вы можете использовать ко-

манды BreakSet, BreakKill, BreakList, BreakEnable и BreakDisable.

Диалоговое окно Debug – Breakpoints… позволяет просмотреть и модифицировать точки останова. Вы можете быстро запрещать или разрешать точки останова щелчком мыши на check box в списке Current Breakpoints. Двойной щелчок на элементе списка Current Breakpoints позволяет Вам модифицировать выбранную точку останова.

110

Можно определить точку останова, введя выражение (Expression) в диалоговом окне Breakpoint. В зависимости от введенного выражения будет определена точка останова одного из следующих типов:

Если выражение представляет собой адрес в исполняемой программе, то будет определена точка останова – прерывание программы (Execution Break (E) ), которая активизируется при достижении указанного адреса программы. Задаваемый программный адрес должен адресовать первый байт [многобайтной] инструкции процессора.

Если в диалоговом окне выбран доступ к памяти (Access: Read, Write или оба сразу), то будет определена точка останова по доступу к памяти (Access Break (A) ), которая активизируется в момент соответствующей попытки обращения к памяти. Вы можете указать размер окна в байтах, внутри которого будет отслеживаться доступ к памяти, или указать размер объекта, заданного в выражении. Выражения для определения точки останова по доступу к памяти (Access Break) должны быть сокращены до вида указания адреса и типа памяти. Операторы (&, &&, <. <=. >, >=, = =, и !=) могут быть использованы для сравнения значения переменной до того как сработает точка останова по доступу к памяти или будет выполнена определенная ко-

манда (Command).

Если выражение не может быть сокращено до вида указания адреса памяти, то будет определена точка останова по условию ( Conditional Break (C) ), которая активизируется когда условное выражение принимает значение «истина». Условное выражение пересчитывается после каждой выполненной инструкции процессора, таким образом, скорость выполнения программы может существенно замедлиться.

111

Если Вы назначаете точке останова некоторую команду (Command), то отладчик µVision2 выполняет эту команду и продолжает выполнение Вашей отлаживаемой программы. В качестве команды Вы можете указать отладочную или сигнальную функцию µVision2. Чтобы остановить выполнение программы, установите системную переменную _break_ в ненулевое значение.

Значение счетчика (Count) определяет сколько раз заданное выражение для точки останова должно принять значение «истина» перед тем, как точка останова будет активизирована.

Примеры точек останова (Breakpoint):

Нижеследующие описания поясняют определения элементов в показанном выше диалоговом окне Breakpoint. Список текущих точек останова (Current Breakpoints) показывает сводку, в которой указаны тип точки останова и ее физический адрес вместе с выражением (Expression), назначенной командой (Command) и счетчиком (Count).

Expression: \Measure\143

Точка останова – прерывание программы (Execution Break (E)) – это выражение приведет к останову отлаживаемой программы, когда она достигнет участка кода, который соответствует строке 143 в модуле MEASURE.

Expression: main

Точка останова – прерывание программы (Execution Break (E)) – это выражение приведет к останову отлаживаемой программы, когда она достигнет функции main.

Expression: timer0 Command: printf ("Timer0 Interrupt occurred\n")

Точка останова – прерывание программы (Execution Break (E)) будет активирована при достижении программой функции timer0, при активизации будет напечатан текст "Timer0 Interrupt occurred" на странице Command окна Output Window. Эта точка останова запрещена в приведенном выше диалоговом ок-

не Breakpoint.

Expression: save_measurements Count: 10

Точка останова – прерывание программы (Execution Break (E)) – это выражение приведет к останову отлаживаемой программы, когда функция save_measurements будет вызвана 10й раз.

112

Expression: mcommand == 1

Точка останова по условию (Contional Break (C)) – это выражение вызо-

вет останов программы, когда mcommand == 1 примет значение «истина». Эта точка останова запрещена в приведенном выше диалоговом окне Breakpoint.

Expression: save_record[10] Access: Read Write Size: 3 Objects

Точка останова по доступу к памяти (Access Break (A)), которая остано-

вит выполнение программы в момент, когда произойдет попытка чтения или записи элемента save_record[10] и 2х следующих за ним элементов. Поскольку save_record представляет собой структуру, размер которой составляет 16 байт, то действие такой точки останова распространяется на область в 48 байт.

Expression: sindex == 10 Access: Write

Точка останова по доступу к памяти (Access Break (A)), которая остано-

вит выполнение программы, когда значение 10 будет записано в переменную sindex.

Expression: measure_display Command: MyStatus ()

Точка останова – прерывание программы (Execution Break (E)), которая вызовет исполнение отладочной функции µVision2 с названием MyStatus, когда отлаживаемая программа достигнет функции measure_display. Выполнение отлаживаемой программы будет продолжено после того, как будет выполнена отладочная функция MyStatus.

Выполнение отлаживаемой программы

Отладчик µVision2 позволяет запускать программу на исполнение несколькими различными способами:

При помощи кнопок на панели инструментов Debug Toolbar или из «Debug Menu и Debug Commands».

При помощи команды Run till Cursor line локального меню. Локальное меню открывается, когда вы щелкаете правой кнопкой мыши на линии кода программы в окне редактора или дизассемблера.

На странице Command окна Output Window можно использовать команды отладчика Go, Ostep, Pstep и Tstep.

Окно Watch

Окно Watch позволяет просматривать и модифицировать программные пе-

113

ременные, и просматривать стек вызовов для текущей функции. Содержимое окна Watch автоматически обновляется при каждом останове программы. Вы можете разрешить опцию View – Periodic Window Update, чтобы во время выполнения отлаживаемой программы значения переменных периодически обновлялись.

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

Выделите текст <enter here> щелчком мыши и подождите секунду. Следующий щелчок мыши поможет войти в режим редактирования, в котором Вы сможете добавить имя переменной. Таким же образом можно изменять значения переменных.

Вокне редактирования откройте контекстное меню щелчком правой кнопки мыши и выберите пункт меню Add to Watch Window. µVision2 автоматически выберет имя переменной под курсором. В альтернативном случае Вы можете выделить выражение перед вызовом контекстного меню.

Вокне Output Window на странице Command можно использовать команду WatchSet, чтобы ввести имя переменной.

Чтобы удалить переменную, щелкните на линии с переменной и нажмите клавишу Delete.

На странице Call Stack отображается стек вызовов для текущей функции. Вы можете сделать двойной щелчок на одной из линий в списке стека вызовов, чтобы увидеть в окне редактирования место, откуда была вызвана функция.

Окно дампа памяти (Memory Window)

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

114

В поле Address окна Memory Вы можете ввести любое выражение, значение которого будет определять начальный адрес и тип области памяти, которую Вы хотите просмотреть. Для изменения содержимого памяти выберите команду Modify memory ... контекстного меню.

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

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

Для обновления окна дампа памяти во время выполнения отлаживаемой программы необходимо разрешить опцию View – Periodic Window Update.

Инструментальная панель (Toolbox)

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

Кнопки на панели Toolbox определяются командой странице Command окна Output Window. Синтаксис

>DEFINE BUTTON "button_label", "command"

где

button_label надпись, которая будет отображаться на кнопке на панели Toolbox;

command Команда отладчика µVision2, которая будет выполнена при нажатии на кнопку.

на

Show R15

Следующий пример показывает, при помощи каких команд были созданы

115

кнопки, показанные на рисунке (см. выше).

>DEFINE BUTTON "Decimal Output", "radix=0x0A"

 

>DEFINE BUTTON "Hex Output", "radix=0x10"

 

>DEFINE BUTTON "My Status Info", "MyStatus ()"

/* вызов отладочной функции */

>DEFINE BUTTON "Analog0..5V", "analog0 ()"

/* вызов сигнальной функции */

>DEFINE BUTTON "Show R15", "printf (\"R15=%04XH\\n\")"

ПРИМЕЧАНИЕ

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

Вы можете удалить кнопку с панели Toolbox с помощью команды KILL BUTTON и указания номера кнопки. Например:

>Kill Button 5 /* Remove Show R15 button */

ПРИМЕЧАНИЕ

Кнопка Update Windows на панели Toolbox создается автоматически и не может быть удалена. Нажатие на кнопку Update Windows обновляет некоторые отладочные окна во время выполнения отлаживаемой программы.

Настройка параметров отладчика

Закладка Debug диалога Options for Target позволяет сконфигурировать отладчик µVision2.

116

Следующая таблица описывает опции доступные на закладке Debug:

Пункт

Описание

Use Simulator

Выбрать симулятор µVision2 в качестве отладочного механизма.

Use Keil Monitor-51

Выбрать Advanced GDI драйвер, с помощью которого отладчик бу-

Driver

дет подключаться к вашему отлаживаемому устройству. Драйвер

 

Keil Monitor-51 Driver позволяет вам подключаться к целевой плате с

 

установленным там Keil Monitor. В разработке находится несколько

 

драйверов под эмулятор для µVision2 и OCDS.

Settings

Открывает конфигурационный диалог для выбранного Advanced

 

GDI драйвера.

Остальные опции доступны отдельно как для секции Simulator, так и для секции Advanced

GDI.

 

 

Load Application

at Разрешите эту опцию для того, чтобы при запуске отладчика

Startup

 

µVision2 автоматически загружалась ваша отлаживаемая программа.

Go till main ()

 

При запуске отладчика запустить выполнение программы до метки

 

 

main.

Initialization File

 

При запуске отладчика обработать указанный файл как командный

 

 

ввод.

Breakpoints

 

Восстанавливать настройки точек останова из предыдущей отладоч-

 

 

ной сессии.

Toolbox

 

Восстанавливать кнопки на панели Toolbox из предыдущей отладоч-

 

 

ной сессии.

Watchpoints & PA

 

Восстанавливать настройки окна Watch и анализатора работы про-

 

 

граммы (Performance Analyzer) из предыдущей отладочной сессии.

Memory Display

 

Восстанавливать настройки отображения дампов памяти из преды-

 

 

дущей отладочной сессии.

CPU DLL

 

Конфигурация внутренних отладочных библиотек µVision2. Эти на-

Driver DLL

 

стройки берутся из базы данных элементов (device database). Пожа-

Parameter

 

луйста, не изменяйте имя библиотеки или параметры.

 

117

Окно терминала последовательного порта (Serial Window)

Отладчик µVision2 имеет два окна терминала последовательного порта для симуляции последовательного ввода и вывода. В этом окне отображаются данные, посылаемые моделируемым процессором из своего последовательного порта. Символы, которые вы вводите в окне терминала, поступают на вход последовательного порта отлаживаемого процессора.

Окно терминала позволяет моделировать работу модуля UART микроконтроллера без дополнительного внешнего оборудования. Кроме того, последовательный порт моделируемого микроконтроллера может быть назначен аппаратному СОМ порту вашего компьютера при помощи команды ASSIGN. Команда ASSIGN вводится в окне Output Window на закладке Command.

Анализатор работы программы (Performance Analyzer)

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

Диапазон адресов <unspecified> генерируется анализатором автоматически.

118

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

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

Метка

Описание

min time

Минимальное время выполнения кода в выбранном диапазоне адресов

или в функции.

 

max time

Максимальное время выполнения кода в выбранном диапазоне адресов

или в функции.

 

avg time

Среднее время выполнения кода в выбранном диапазоне адресов или в

функции.

 

total time

Суммарное время выполнения кода в выбранном диапазоне адресов или

в функции.

 

%

Процент суммарного времени выполнения кода в выбранном диапазоне

адресов или в функции (по отношению ко всему времени выполнения

 

программы).

count

Показывает, сколько раз был выполнен код в указанном диапазоне адре-

сов или в функции.

 

Для настройки параметров анализатора используйте команду Performance Analyzer в меню Debug. Также вы можете ввести команду PA в командном окне для установки диапазонов адресов или для печати результатов.

Покрытие кода

Coverage)

Отладчик µVision2 имеет встроенную функцию определения покрытия кода, которая отмечает участки кода, которые были выполнены. В окне отладки строки программы, которые были выполнены, отмечаются зеленым маркером в левой колонке. Вы можете

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

Диалоговое окно Code Coverage (покрытие кода) предоставляет вам соответствующую информацию и статистику. Вы можете напечатать эту информацию в командном окне (закладка Command в окне Output Window), используя коман-

ду COVERAGE.

Команды отладчика (Debug Commands)

119

Вы можете взаимодействовать с отладчиком µVision2, вводя команды на странице Command окна Output Window. В нижеследующих таблицах приведены все доступные команды (по категориям) отладчика µVision2. Используйте подчеркнутые буквы в именах команд для ввода соответствующих команды. Например, команда WATCHSET должна вводиться как WS.

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

Если вы введете B, то синтаксический генератор уменьшит список доступных команд.

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

Синтаксический генератор — ваш проводник при вводе команды, он помогает избежать ошибок при вводе команд.

Команды работы с памятью (Memory Commands)

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

 

Команда

Описание

 

ASM

Ассемблировать и сохранить код инструкции.

 

DEFINE

Определить типизированный символ, который вы можете использовать в

 

DISPLAY

своих отладочных функциях µVision2.

 

Показать содержимое памяти.

 

ENTER

Ввести значение и сохранить в указанной области памяти.

 

EVALUATE

Вычислить выражение и вывести результаты.

 

MAP

Определить параметры доступа к областям памяти.

 

UNASSEMBLE

Дизассемблировать программную память.

 

WATCHSET

Добавить переменную в окно Watch.

 

 

 

 

120

Команды управления выполнением программы (Program Execution Commands)

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

Команда

Описание

Esc

Остановить выполнение программы.

GO

Запустить выполнение программы.

PSTEP

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

OSTEP

Проделать шаги до выхода из текущей функции.

TSTEP

Шагать по инструкциям и заходить в функции.

 

 

Команды управления точками останова (Breakpoint Commands)

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

Команда

BREAKDISABLE BREAKENABLE BREAKKILL BREAKLIST BREAKSET

Описание

Запретить одну или несколько точек прерывания. Разрешить одну или несколько точек прерывания Удалить одну или несколько точек прерывания из списка. Показать список точек прерывания.

Добавить выражение, определяющее новую точку прерывания.

Области памяти

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

Префикс

Область памяти

Описание

 

 

 

B:

BIT

Бит-адресуемая часть внутреннего ОЗУ.

C:

CODE

Память программы

Bx:

CODE BANK

Банк памяти программ; x указывает номер банка, на-

D:

DATA

пример B1:

Внутреннее прямо адресуемое ОЗУ.

I:

IDATA

Внутреннее косвенно адресуемое ОЗУ.

X:

XDATA

Внешнее ОЗУ.

121

ПРИМЕЧАНИЕ

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

Примеры

C:0x100

/* Адрес 0x100 в памяти программ

*/

I:100

/* Адрес 0x64 во внутреннем ОЗУ 8051 */

X:0FFFFH

/* Адрес 0xFFFF во внешней памяти данных */

B:0x7F

/* Битовый адрес 127 или 2FH.7

*/

B2:0x9000

/* Адрес 0x9000 банке программы №2

*/

Отладочные функции среды μVision2.

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

Примечание:

Не путайте отладочные функции среды μVision2 с функциями в своей программе. Отладочные функции среды μVision2 помогают вам при отладке вашей программы и вводятся при помощи Редактора Функций или в командной строке отладчи-

ка μVision2.

Отладочные функции среды μVision2 используют подмножество языка программирования Си. Это подмножество обладает следующими возможностями и ограничениями:

В отладочных функциях могут использоваться операторы управления ходом выполнения программы: if, else, while, do, switch, case, break, continue и goto. В

отладочных функциях среды μVision2 все эти операторы работают так, как это определено в стандарте ANSI С.

В отладочных функциях можно объявлять локальные скалярные переменные, аналогично тому, как это определено в стандарте ANSI С. Массивы не поддерживаются.

Создание функций

122

Среда μVision2 содержит встроенный редактор отладочных функций, который открывается из меню Debug - Function Editor. Когда вы запускаете редактор функций, редактор запрашивает имя файла или открывает файл, указанный в опциях проекта, которые можно настроить через меню Options for Target - Debug, в поле Initialization File. Редактор отладочных функций работает аналогично редактору текста программы среды μVision2 и дает вам возможность ввести текст отладочной функции и откомпилировать его.

Управляющие элементы

Описание

Кнопка Open

Открывает существующий файл, содержащий отладочные функ-

 

ции или команды среды μVision2

Кнопка New

Создает новый файл

Кнопка Save

Сохраняет текст в окне редактора в файл

Кнопка Save as

Позволяет указать имя файла для сохранения отладочных функ-

 

ций

Кнопка Compile

Посылает текущее содержимое окна редактора в командный ин-

 

терпретатор среды μVision2. При этом компилируются все отла-

 

дочные функции

Поле Compile Error

Показывает список всех ошибок, обнаруженных в процессе ком-

 

пиляции. Выберите ошибку из списка - курсор в окне редактора

 

будет установлен на строку с ошибкой

После создания файла с отладочными функциями среды μVision2 вы можете использовать команду INCLUDE для чтения текстового файла с диска и запуска в интерпретаторе. Например, если вы введете следующую команду в командном окне (Command) содержимое файла MYFUNCS.INI будет загружено и интерпретировано средой μVision2.

>INCLUDE MYFUNCS.INI

Файл MYFUNCS.INI может содержать отладочные команды и определения отладочных функций. Вы также можете указать этот файл в настройках проекта

Options for Target - Debug -Initialization File. Тогда каждый раз при запуске отлад-

чика среды μVision2 будет обрабатываться содержимое файла MYFUNCS.INI.

123

Функции, которые более не нужны в текущем сеансе отладки, могут быть удалены с использованием команды KILL.

Вызов функций

Для вызова или запуска отладочных функций вы должны набрать в командном окне имя функции и необходимые параметры функции. Например, для выполнения встроенной функции printf для вывода строки «Hello World» введите следующий текст в командном окне:

>printf ("Hello World\n")

Отладчик μVision2 в ответ напечатает текст «Hello World» в поле вывода страницы Command, расположенной в окне Output Window отладчика.

Классификация функций

Среда μVision2 поддерживает следующие 3 класса функций: предопределенные функции, пользовательские функции и сигнальные функции.

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

Функции пользователя расширяют возможности отладчика μVision2 и могут обрабатывать те же выражения, которые допускаются на командном уровне. Вы можете использовать предопределенную функцию exec для выполнения отладочных команд внутри пользовательских или сигнальных функций.

Сигнальные функции моделируют поведение сложных генераторов сигналов

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

новременно может быть запущено не более 64 сигнальных функций.

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

Команда DIR BFUNC показывает имена всех встроенных (предопределенных) функций. Команда DIR UFUNC показывает имена всех пользовательских функций. Команда DIR SIGNAL показывает имена всех сигнальных функций. Команда DIR FUNC показывает имена всех функций - пользовательских, сигнальных и встроенных.

Встроенные (предопределенные) функции

μVision2 содержит набор предопределенных функций, которыми вы всегда

124

можете воспользоваться. Эти функции нельзя переопределить или удалить. Предопределенные функции предоставлены для поддержки функциональности в ваших пользовательских и сигнальных функциях. В следующей таблице приведен список всех предопределенных отладочных функций среды μVision2.

Тип возвра-

Имя функции

Параметры

Описание

 

щаемого

 

 

 

 

 

значения

 

 

 

 

 

void

exec

('command_string")

Выполнить команду отладчика

 

double

getdbl

("prompt_string")

Запрашивает у пользователя дробное чис-

 

 

 

ло типа double

 

 

int

getint

("prompt_string")

Запрашивает у пользователя целое число

 

 

 

типа int

 

 

long

getlong

("prompt_string")

Запрашивает у пользователя целое число

 

 

 

типа long

 

 

void

memset

(start_addr, ten,

Заполняет область памяти константой

 

 

value)

 

 

 

void

printf

('string", ...)

Работает аналогично функции printf из

 

 

 

ANSI С

 

 

int

rand

(int seed)

Возвращает случайное число из интервала

 

 

 

от -32768 до +32767

 

 

void

rwatch

(address)

Приостанавливает

выполнение

сигналь-

 

 

 

ной функции до тех пор, пока не будет

 

 

 

произведено чтение памяти по указанному

 

 

 

адресу

 

 

void

wwatch

(address)

Приостанавливает

выполнение

сигналь-

 

 

 

ной функции до тех пор, пока не будет

 

 

 

произведена запись в память по указанно-

 

 

 

му адресу

 

 

void

swatch

(float seconds)

Приостанавливает

выполнение

сигналь-

 

 

 

ной функции на указанное количество се-

 

 

 

кунд

 

 

void

twatch

(ulong states)

Приостанавливает

выполнение

сигналь-

 

 

 

ной функции на указанное количество

 

 

 

тактов процессора

 

 

int

_TaskRunning_

(ulong func_address)

Производит проверку того, что указанная

 

 

 

задача является текущей выполняемой за-

 

 

 

дачей. Функция доступна, если использу-

 

 

 

ется специальная DLL для отладки ядра

 

 

 

RTX.

 

 

uchar

_RBYTE

(address)

Читает значение типа char из памяти по

 

 

 

указанному адресу

 

 

uint

_RWORD

(address)

Читает значение типа int из памяти по

 

 

 

указанному адресу

 

 

ulong

_RDWORD

(address)

Читает значение типа long из памяти по

 

 

 

указанному адресу

 

 

float

_RFLOAT

(address)

Читает значение типа float из памяти по

 

 

 

указанному адресу

 

 

125

double

_RDOUBLE

(address)

Читает значение типа double из памяти по

 

 

 

указанному адресу

void

_WBYTE

(address, uchar val)

Записывает значение типа char в память

 

 

 

по указанному адресу

void

_WWORD

(address, uint val)

Записывает значение типа int в память по

 

 

 

указанному адресу

void

_WDWORD

(address, ulong val)

Записывает значение типа long в память

 

 

 

по указанному адресу

void

_WFLOAT

(address, float val)

Записывает значение типа float в память

 

 

 

по указанному адресу

void

_WDOUBLE

(address, double val)

Записывает значение типа double в память

 

 

 

по указанному адресу

Ниже дано описание предопределенных функций. void exec ("command_string")

Функция exec позволяет вам вызвать команды отладчика μVision2 из ваших пользовательских или сигнальных функций. Командная строка (command_string) может содержать несколько команд, разделенных точкой с запятой.

Командная строка передается командному интерпретатору и потому должна быть корректной командой отладчика.

Пример:

>ехес ("DIR PUBLIC; EVAL R7") >ехес ("BS timer0")

>ехес ("ВК *")

double getdbl ("prompt_string"), int getint ("prompt_string"), long getlong ("prompt_string")

Эти функции выводят пользователю приглашение для ввода числа, и после окончания ввода возвращают введенное значение. Если пользователь ничего не ввел, возвращается 0.

Пример:

>age = getint ("Enter Your Age")

void memset (start address, ulong length, uchar value)

Функция memset заполняет участок памяти, начиная с указанного адреса address, указанным значением value. Длина заполняемого участка памяти задается в length.

Пример:

>MEMSET (0x20000, 0x1000, 'а') /* Заполнит память с 0x20000 до Ox20FFF значе-

нием "а" */

void printf ("format_string",...)

Функция prinf работает аналогично библиотечной функции ANSI С. Первый аргумент - строка форматирования. Следующие аргументы могут быть выра-

126

/* Генератор случайных чисел инициализируется значением 0x1234 */
/* Без инициализации */

жениями или строками. Все определенные стандартом ANSI С спецификации форматирования полностью применимы к printf.

Пример:

>printf ("random number = %04XH\n", rand(0)) random number = 1014H

>printf ("random number = %04XH\n", rand(0)) random number = 64D6H

>printf ("%s for %d\n", "uVision2", 8051) uVision2 for 8051

>printf ("%lu\n", (ulong) -1) 4294967295

int rand (intseed)

Функция rand возвращает случайное число из интервала от -32768 до +32767. Генератор случайных чисел инициализируется каждый раз, когда в функцию передается аргумент seed отличный от нуля. Вы можете использовать функцию rand для формирования задержки на случайное количество тактов процессора или для генерации случайных данных, подаваемых на вход какого-либо алгоритма или процедуры.

Пример: >rand (0x1234) 0хЗВ98

>rand (0) 0x64BD

void twatch (long states)

Функция twatch может использоваться в сигнальных функциях для задержки выполнения функции на указанное количество тактов процессора. Отладчик μVision2 обновляет счетчик тактов во время выполнения вашей программы.

Пример:

Следующая сигнальная функция периодически переключает вход INT0 (P3.2) каждую секунду.

signal void int0_signal (void) { while (1) {

PORT3 |= 0x04; /* перевести INT0(P3.2) в «1» */

PORT3 &= ~0x04; /* перевести INT0(P3.2) в «0» и сгенерировать прерывание */

PORT3 |= 0x04; /* перевести INT0(PS.2) снова в «1» */ twatch (CLOCK); /* подождать 1 секунду */

}

}

127

/* переключение P1.0 */
/* ожидание пока ячейка X:0x1234 не будет прочитана */

Примечание

Функция twatch может вызываться только внутри сигнальных функций. Вызов не из сигнальных функций не допускается и приводит к появлению сообщения об ошибке.

void swatch (float seconds)

Функция swatch может использоваться в сигнальных функциях для задержки выполнения функции на указанное количество секунд.

Пример

Следующая сигнальная функция периодически переключает вход INT0 (P3.2) каждые полсекунды.

signal void int0_signal (void) { while (1) {

PORT3 |= 0x04; /* перевести INT0(PS.2) в «1» */

PORT3 &= ~0x04; /* перевести INT0(PS.2) в «0» и сгенерировать прерывание */

PORT3 |= 0x04; /* перевести INT0(PS.2) снова в «1» */ swatch (0.5); /* подождать 0,5 секунды */

}

}

Примечание

Функция swatch может вызываться только внутри сигнальных функций. Вызов не из сигнальных функций не допускается и приводит к появлению сообщения об ошибке.

void rwatch (address)

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

Пример Следующая сигнальная функция периодически переключает вывод Р1.0 ка-

ждый раз при чтении из памяти XDATA по адресу 0x1234. signal void my_signal (void) {

while (1) {

PORT1 ^= 0x01; rwatch (X:0x1234);

}

}

Примечание

Функция rwatch может вызываться только внутри сигнальных функций. Вызов не из сигнальных функций не допускается и приводит к появлению сообщения об ошибке.

128

/* проверяет - выполняется ли задача 'command' */ /* возвращает 1 если задача выполняется */
/* остановить, когда задача 'init' выполняется */
/* переключение P1.0 */

void wwatch (address)

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

Пример Следующая сигнальная функция периодически переключает вывод Р1.0 ка-

ждый раз при записи в память XDATA по адресу 0x4000. signal void my_signal (void) {

while (1) {

PORT1 ^= 0x01;

wwatch (X:0x4000);/*ожидание пока в Х:0х4000 не будет записано что-либо*/

}

}

Примечание

Функция wwatch может вызываться только внутри сигнальных функций. Вызов не из сигнальных функций не допускается и приводит к появлению сообщения об ошибке.

Int_TaskRunning_ (ulong func_address)

Эта функция производит проверку того, что указанная задача является текущей выполняемой задачей.

Функция _TaskRunning_ доступна только тогда, когда вы выбрали опцию

Operating System на странице настроек проекта Options for Target - Target. Отлад-

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

Результат функции _TaskRunning_ может быть присвоен системной переменной _break_ что позволит остановить выполнение программы когда указанная задача будет активна.

Пример

>_TaskRunning_ (command) 0001

>_break_= _TaskRunning_ (init)

uchar _RBYTE (address), uint _RWORD (address), ulong _RDWORD (address), float _RFLOAT (address), double _RDOUBLE (address)

Эти функции возвращают содержимое памяти по указанному адресу ad-

dress.

Пример

>_RBYTE (0x20000) /* возвращает символ из ячейки 0x20000 */ >_RFLOAT (0xE000) /* возвращает дробное число из ячейки 0хЕ000 */

129

/* записать байт 0x55 в ячейку 0x20000 */ /* записать дробное число 1.5 в ячейку 0хЕ000 */
/* записать длинное целое 12345678 в ячейку

>_RDWORD (0x1000) /* возвращает длинное целое из ячейки 0x1000 */

_WBYTE (address, uchar value), _WWORD (address, uint value), _WDWORD (address, ulong value), _WFLOAT (address, float value, _WDOUBLE (address, double value)

Эти функции записывают значение value в память по указанному адресу. Пример

>_WBYTE (0x20000, 0x55) >_RFLOAT (0xE000, 1.5) >_RDWORD (0x1000, 12345678)

0x1000*/

Пользовательские функции

Пользовательские функции - это функции, которые вы создаете для использования во время отладки в среде μVision2. Эти функции вы можете непосредственно ввести в редакторе функций, или же можно использовать команду INCLUDE для загрузки файла, который содержит одну или несколько пользовательских функций.

Примечание:

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

Пользовательские функции начинаются с ключевого слова FUNC и определяются следующим образом:

FUNC return_type fname (parameter_list) {

 

statements

 

}

return type

Тип значения, возвращаемого функцией. Может быть: bit,

 

char, float, int, long, uchar, uint, ulong, void. Вы можете ис-

 

пользовать тип void если функция не возвращает значения.

 

Если тип возвращаемого значения не указан, то по умолча-

 

нию принимается тип int.

fname

Имя функции.

parameter list

Список параметров, передаваемых в функцию. Каждый ар-

 

гумент должен иметь тип и имя. Если функция не имеет па-

 

раметров, то используйте void в качестве списка парамет-

 

ров. Параметры разделяются запятой.

statements

Инструкции, выполняемые функцией.

130

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

Пример Следующая пользовательская функция выводит на экран содержимое неко-

торых регистров процессора.

 

FUNC void MyRegs (void) {

 

printf ("----------

MyRegsO ----------

\n") ;

printf (" R0

R1 R2 R3 R41

R5\n");

printf (" %04X %04X %04X %04X %04X %04X\n", R0, R1, R2, R3, R4, R5);

printf ("------------------------------

\n") ;

}

 

Для вызова этой функции наберите в командном окне следующее MyRegs() После вызова функция MyRegs выведет содержимое регистров, что будет

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

---------

MyRegsO

---------

 

 

R0

R1

R2

R3

R4

R5

002С 0025 0001 0000 0000 0000

Вы можете определить кнопку на панели инструментов (toolbox), которая позволит вызывать пользовательскую функцию, следующей командой:

DEFINE BUTTON "My Registers", "MyRegs()"

Ограничения

Отладчик μVision2 проверяет, чтобы возвращаемое значение пользовательской функции соответствовало возвращаемому типу функции. Функции, которые имеют void в качестве возвращаемого типа, не должны возвращать никакое значение. Функции, определенные как возвращающее значение, отличное от void, должны возвращать какое-то значение. Заметьте, что отладчик μVision2 не проверяет каждую ветку с return на корректность возвращаемого значения.

Пользовательские функции не могут вызывать сигнальные функции или функцию twatch .

Значение локальных переменных не определено до тех пор, пока им не будет присвоено какое-то значение.

Для удаления пользовательских функций из памяти отладчика используйте команду KILL FUNC.

Сигнальные функции

Сигнальные функции позволяют вам запустить в фоновом режиме повторяющиеся операции, такие как подача сигналов и импульсов на входы процессора, в то время как отладчик μVision2 выполняет вашу программу. Сигнальные

131

функции позволяют вам моделировать и отлаживать последовательный вводвывод, аналоговый ввод-вывод, взаимодействия с портами и другие повторяющиеся внешние события.

Сигнальные функции выполняются в фоновом режиме, в то время как отладчик μVision2 моделирует поведение вашей программы. Поэтому сигнальные функции должны в некоторый момент вызывать функцию twatch, чтобы сделать паузу и позволить отладчику μVision2 выполнять вашу программу. Отладчик μVision2 сообщает об ошибке, если сигнальная функция никогда не вызывает twatch .

Примечание:

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

Сигнальные функции начинаются с ключевого слова SIGNAL и определяются следующим образом:

SIGNAL void fhame (parameter_list) { statements

 

}

fname

Имя функции.

parameter list

Список параметров, передаваемых в функцию. Каждый ар-

 

гумент должен иметь тип и имя. Если функция не имеет па-

 

раметров, то используйте void в качестве списка параметров.

 

Параметры разделяются запятой.

statements

Инструкции, выполняемые функцией.

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

Пример Следующий пример показывает сигнальную функцию, которая помещает

символ 'А' во входной буфер последовательного порта каждые 1000000 (1 млн.) тактов процессора.

SIGNAL void StuffS0in (void) { while (1) {

S0IN = 'A' ; twatch (1000000) ;

}

}

Для вызова этой функции наберите в командном окне следующее: StuffS0in()

После запуска сигнальная функция StuffS0in помещает символ 'А' во вход-

132

ной буфер последовательного порта, ждет 1 000 000 тактов процессора, затем повторяет все сначала.

Ограничения:

Сигнальные функции имеют следующие ограничения:

Типом возвращаемого значения сигнальной функции должен быть void.

Сигнальная функция может иметь максимум 8 параметров.

Сигнальные функции могут вызывать предопределенные и пользовательские функции.

Сигнальная функция не может вызывать другую сигнальную функцию.

Сигнальная функция может быть вызвана из пользовательской функции.

Сигнальная функция должна вызывать функцию twatch хотя бы один раз. Сигнальные функции, которые никогда не вызывают twatch, не предоставляют времени для выполнения моделируемой программы. Поскольку вы не можете использовать Ctrl+C для того, чтобы прервать выполнение сигнальной

функции, отладчик μVision2 может войти в бесконечный цикл.

Управление выполнением сигнальных функций

Отладчик μVision2 имеет очередь активных сигнальных функций. Сигнальная функция может находиться в одном из двух состояний - в режиме простоя или в режиме выполнения. Сигнальная функция, находящаяся в режиме простоя, - это функция, выполнение которой приостановлено пока она ожидает истечение интервала, заданного при вызове функции twatch. Сигнальная функция, находящаяся в режиме выполнения, выполняет операторы, заданные в ее теле.

Когда вы вызываете сигнальную функцию, отладчик μVision2 добавляет ее в очередь и помечает как функцию в режиме выполнения. Сигнальная функция может быть активизирована один раз, если функция уже помещена в очередь, то будет выведено предупреждающее сообщение. Для просмотра состояния активных сигнальных функций используйте команду SIGNAL STATE. Для удаления активной сигнальной функции из очереди используйте команду SIGNAL KILL.

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

зовом twatch.

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

133

Пример аналогового генератора

Следующий пример демонстрирует сигнальную функцию, которая изменяет сигнал на аналоговом входе 0 процессора со встроенным АЦП. Эта функция увеличивает и уменьшает входное напряжение на 0,1В от 0В и до верхнего порога, который задается параметром при вызове сигнальной функции. Сигнальная функция повторяет свою работу в вечном цикле, делая задержку на 200 000 тактов процессора после каждого шага по напряжению.

signal void analog0 (float limit) { float volts;

printf ("Analog0 (%f) entered.\n", limit);

while (1) {

/* вечный цикл */

volts = 0;

 

 

 

while (volts <= limit) {

 

 

 

ain0 = volts;

 

/* аналоговый вход-0 */

 

twatch (200000) ;

/* 200000 тактов пауза */

 

volts += 0.1;

/* увеличение напряжения */

 

}

 

 

volts = limit;

 

 

while (volts >= 0.0) {

 

 

 

ain0 = volts;

 

 

 

twatch (200000);

 

/* 200000 тактов пауза */

volts =0.1;

/* уменьшение напряжения */

}

}

 

}

Сигнальная функция analog0 может быть запущена следующим образом: >ANALOG0 (5.0) /* Запуск функции 'ANALOG0' */

ANALOGO (5.000000) ENTERED

Команда SIGNAL STATE показывает текущее состояние функции analog0: >SIGNAL STATE

1 idle Signal = ANALOG0 (line 8)

μVision2 показывает внутренний номер функции, статус сигнальной функции: простой или выполнение (idle или running), имя функции и номер строки, которая выполняется в данный момент.

Поскольку мы видим, что сигнальная функция находится в режиме простоя, то можно сделать вывод, что analog0 выполняет функцию twatch (строка 8 функции analog0) и ожидает истечения интервала времени, заданного в тактах процессора. Когда 200 000 тактов пройдет, функция analog0 продолжит свое выполнение, пока не встретит следующий вызов twatch в строке 8 или 14.

Следующая команда удаляет сигнальную функцию analog0 из очереди ак-

134

тивных сигнальных функций. >SIGNAL KILL ANALOG0

Различия между отладочными функциями и Си-функциями

Существует ряд различий между стандартом ANSI С и подмножеством языка, который поддерживается в отладчике μVision2 в пользовательских и сигнальных функциях.

В подмножестве μVision2 не различаются прописные и строчные буквы. Имена объектов и управляющие операторы могут быть записаны, как прописными, так и строчными буквами.

Подмножество μVision2 не имеет препроцессора. Такие директивы препроцессора как #define, #include, и #ifdef не поддерживаются.

Подмножество μVision2 не поддерживает объявления глобальных переменных. Скалярные переменные должны объявляться внутри определений функций. Вы можете определить символы командой DEFINE и затем использовать их аналогично глобальным переменным.

В подмножестве μVision2, переменные не могут быть инициализированы при объявлении. Для инициализации переменных должна использоваться инструкция явного присваивания значения.

Функции в подмножестве μVision2 поддерживают только скалярные типы переменных. Структуры, массивы и указатели не допускаются. Это правило распространяется на тип значения возвращаемого функцией, а также на параметры функции.

Функции в подмножестве μVision2 не могут вызываться рекурсивно. Во время выполнения функции отладчик μVision2 распознает рекурсивные вызовы и тотчас прерывает выполнение функции.

В подмножестве μVision2 функции могут вызываться только явным образом по имени функции. Косвенный вызов функции через указатели не поддерживается.

Подмножество μVision2 поддерживает только стиль ANSI для декларации списка параметров функции. Старый формат K&R не поддерживается. На-

пример, в следующей функции использован допустимый стиль ANSI. func test (int pal, int pa2) { /* стиль ANSI, все корректно */ /* ... */

}

В следующем примере используется стиль K&R, что не допускается.

func test (pal, pa2)

/* Старый стиль K&R */

int pal, pa2;

/* не поддерживается */

{

 

/* ... */

 

}

 

135

Системные переменные

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

Переменная

Тип

 

Описание

 

 

 

Представляет собой счетчик команд (program counter).

$

 

 

Вы можете использовать $ для отображения и изменения

unsigned int

 

счетчика команд. Например,

 

 

 

$ = C:0x4000

 

 

 

устанавливает счетчик команд на адрес C:0x4000

 

 

 

Позволяет Вам остановить выполнение отлаживаемой

 

 

 

 

 

программы. Когда Вы присваиваете _break_ ненулевое

_break_

 

 

значение, μVision2 останавливает выполнение отлажи-

unsigned int

 

ваемой программы. Вы можете использовать эту пере-

 

 

 

менную в пользовательских и сигнальных функциях для

 

 

 

останова отлаживаемой программы. Подробнее см. главу

 

 

 

6 «Отладочные функции μVision2» на странице 131.

_iip_

unsigned char

 

Показывает текущую глубину вложенности прерываний.

 

 

 

Отладочные функции могут использовать эту системную

 

 

 

переменную для определения глубины вложенности те-

 

 

 

кущего обрабатываемого прерывания.

states

unsigned long

 

Текущее значение счетчика тактов процессора, началь-

 

 

 

ное значение счетчика равно 0 при старте вашей про-

 

 

 

граммы. Счетчик увеличивается после каждой выпол-

 

 

 

ненной инструкции.

 

 

 

Примечание: переменная states доступна только для

 

 

 

чтения.

itrace

unsigned int

 

Показывает, ведется или нет запись трассировочных дан-

 

 

 

ных во время выполнения отлаживаемой программы. Ко-

 

 

 

гда itrace равно 0, запись трассировки не ведется. Когда

 

 

 

же itrace имеет ненулевое значение, то это значит, что

 

 

 

запись трассировки ведется.

 

 

 

Смотри стр. 95 для получения большей информации

radix

unsigned int

 

Определяет используемую систему счисления для ото-

 

 

 

бражения числовых величин. Переменная radix может

 

 

 

принимать значение 10 или 16. По умолчанию имеет зна-

 

 

 

чение 16, вывод ведется с использованием шестнадцате-

 

 

 

ричной системы счисления.

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

Отладчик µVision2 автоматически определяет большое число символических имен в зависимости от типа процессора, который вы выбрали для вашего

136

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

(VTREG).

Регистры специальных функций (SFR)

Отладчик µVision2 поддерживает все регистры специальных функций для выбранного вами микроконтроллера. Символические имена регистров специальных функций соответствуют адресам этих регистров и могут быть использованы в выражениях.

Виртуальные регистры выводов процессора (VTREG)

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

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

VTREG

AINx

CLOCK

PORTx

SxIN

для однопортовых

SIN

SxOUT

SOUT

Описание

Вывод аналогового входа x процессора. Отлаживаемая программа может читать значение, которое вы запишете в регистр AINx.

Внутренняя тактовая частота процессора, равна количеству инструкций, выполняемых процессором за одну секунду. Системная переменная states и регистр CLOCK используются для вычисления времени выполнения программы в секундах. Регистр CLOCK доступен только для чтения, его значение вычисляется с использованием частоты кварцевого резонатора, заданной в настройках проекта на закладке Target.

Группа выводов одного из портов ввода/вывода процессора. Например, PORT2 относится ко всем 8 выводам порта P2. Эти регистры позволяют вам имитировать ввод/вывод на соответствующем порте.

Входной буфер последовательного интерфейса номер x. Вы можете записывать 8-ми или 9-ти битовое значение в регистр SxIN. Это значение читает ваша программа. Вы можете читать содержимое SxIN для того, чтобы определить, когда входной буфер процессора будет готов принять следующие данные. Прочитанное значение 0xFFFF сигнализирует, что предыдущие данные были обработаны, и можно записывать новые.

Выходной буфер последовательного интерфейса номер x. Отладчик µVision2 копирует 8-ми или 9-ти битовое значение (в зависимости от того, как запрограммирован интерфейс) из выходного буфера в регистр SxOUT.

137

SxTIME STIME

XTAL

Определяет временные характеристики последовательного интерфейса номер x. Когда SxTIME равно 1, отладчик µVision2 имитирует работу последовательного интерфейса на основе запрограммированной скорости обмена. Когда же SxTIME равно 0 (значение по умолчанию), то заданная в программе скорость обмена игнорируется, и имитируется «мгновенная» передача данных.

Частота кварцевого резонатора имитируемого процессора. Соответствует величине, заданной в настройках проекта на закладке Target.

Примечание

Вы можете использовать виртуальные регистры VTREG для имитации ввода/вывода с внешними устройствами, включая имитацию интерфейса с внутренней периферией, такой как прерывания и таймеры. Например, если вы переключите состояние бита 2 в регистре PORT3, то это вызовет генерацию внешнего прерывания 0.

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

Порты ввода/вывода

Отладчик µVision2 определяет регистры VTREG для каждого порта ввода/вывода, например для порта 2 – PORT2. Не путайте эти виртуальные регистры с регистрами специальных функций (SFR) для каждого порта (например, P2). Регистры специальных функций SFR доступны через внутреннюю область памяти самого процессора. Регистры VTREG представляют собой сигналы на соответствующих выводах процессора.

В отладчике µVision2 легко имитировать сигнал, поступающий от внешних цепей. Если вам надо имитировать на каком-то входе последовательность импульсов от внешнего источника, вы можете использовать сигнальную функцию для имитации такого сигнала. Например, ниже приведен исходный текст сигнальной функции, которая формирует меандр с частотой 1000 Гц на входе Р2.0.

signal void one_thou_hz (void) {

 

 

 

while (1) {

 

/* бесконечный цикл

*/

PORT2 |= 1;

/* установить P2.0 в 1 */

 

 

twatch ((CLOCK / 2) / 2000);

/* пауза на 0.0005 сек */

PORT2 &= ~1;

 

/* очистить P2.0

*/

 

twatch ((CLOCK / 2) / 2000);

/* пауза на 0.0005 сек */

}

 

/* повторить сначала

*/

}

 

 

 

 

138

Вот такой командой можно запустить эту сигнальную функцию:

one_thou_hz ()

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

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

signal void check_p20 (void) {

 

if (PORT2 & 1) {

/* Проверить бит P2.0

*/

printf ("LED is ON\n"); }

/* 1? - светодиод горит

*/

else {

/* 0? - светодиод погашен */

printf ("LED is OFF\n"); }

 

 

twatch ((CLOCK / 2) / 1000);

}

Теперь вам надо добавить контрольную точку на отслеживание операции записи в порт. Следующая команда добавляет контрольную точку на отслеживание записи в любой бит порта PORT2.

BS WRITE PORT2, 1, "check_p20 ()"

Теперь в тот момент времени, когда ваша отлаживаемая программа произведет запись в порт PORT2, будет вызвана функция check_P20, которая напечатает в командном окне текущее состояние светодиода.

Последовательные порты

Виртуальные регистры SxTIME, SxIN, и SxOUT позволяют при отладке управлять встроенными последовательными портами процессора. Регистры SxIN и SxOUT представляют собой входной и выходной потоки последовательного порта процессора. Регистр SxTIME позволяет вам задать один из режимов моделирования временных характеристик последовательного порта – либо режим «мгновенной» передачи данных (SxTIME = 0), либо режим, в котором время передачи данных будет соответствовать заданной в программе скорости обмена (SxTIME = 1). Когда значение регистра SxTIME равно 1, данные в окне «Serial»

139

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]