Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

691_Mikushin_A.V._Programmirovanie_mikroprotsessorov_

.pdf
Скачиваний:
48
Добавлен:
12.11.2022
Размер:
1.96 Mб
Скачать

Отметим также, что восьмеричные и шестнадцатеричные константы также могут иметь модификатор unsigned. Это достигается указанием суффикса u или U после константы, константа без этого префикса считается знаковой.

Например:

0xA8C //int signed 01786l //long signed 0xF7u //int unsigned

Числа с плавающей запятой.

Для переменных, представляющих число с плавающей запятой используется модификатор типа float. Модификатор double тоже допустим в языке программирования C-51, но он не приводит к увеличению точности результата.

Величина с модификатором типа float занимает 4 байта. Из них 1 бит отводится для знака, 8 бит для смещенного порядка и 23 бита для мантиссы. Отметим, что старший бит мантиссы всегда равен 1, поэтому он не запоминается в памяти микроконтроллера, в связи с этим диапазон значений переменной с пла-

вающей точкой равен от ±1.175494E–38 до ±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];

41

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

enum [имя типа перечисления] {список констант} имя1 [,имя2 ...]; enum имя перечисления описатель [,описатель..];

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

Список констант содержит одну или несколько конструкций вида:

идентификатор [= константное выражение]

Каждый идентификатор – это имя константы. Все идентификаторы в списке 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. Использование констант должно подчиняться следующим правилам:

Тип

Размер памяти в

Размер памяти в

Диапазон значений

 

битах

байтах

 

bit

1

 

от 0 до 1

char

8

1

от –128 до 127

 

 

 

 

unsigned shar

8

1

oт 0 до 255

42

int, short

16

2

от –32768 до

32767

long

32

4

от –2 147 483

648 до

 

 

 

2 147 483 647

unsigned int,

16

2

от 0 до 65535

unsigned 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

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

*/

HETV,

/* константе HETV присвоено значение 4

*/

PJAT

/* константе PJAT присвоено значение 5

*/

} week;

 

 

 

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

43

1.7. Объявление массивов в языке программирования C-51

При обработке данных достаточно часто приходится работать с рядом переменных одинакового типа (и описывающих одинаковые объекты). В этом случае эти переменные имеет смысл объединить одним идентификатором. Это позволяют сделать массивы.

Массивы – это группа элементов одинакового типа (float, char, int и т.п.). Из объявления массива компилятор должен получить информацию о типе элементов массива и их количестве. Объявление массива имеет два формата:

спецификатор-типа описатель [константное-выражение]; спецификатор-типа описатель [ ]; Описатель - это идентификатор массива.

Спецификатор-типа задает тип элементов объявляемого массива. Элементами массива не могут быть функции и элементы типа void.

Константное-выражение в квадратных скобках задает количество элементов массива. Константное-выражение при объявлении массива может быть опущено в следующих случаях:

1.при объявлении массив инициализируется,

2.массив объявлен как формальный параметр функции,

3.массив объявлен как ссылка на массив, явно определенный в другом файле.

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

Каждое константное-выражение в квадратных скобках определяет число элементов по данному измерению массива, так что объявление двухмерного массива содержит два константных-выражения, трехмерного – три и т.д. Отметим, что в языке С-51 первый элемент массива имеет индекс равный 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 } };

Впоследнем примере объявлен массив w[3][3]. Списки, выделенные в фигурные скобки, соответствуют строкам массива, в случае отсутствия скобок инициализация будет выполнена неправильно.

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

44

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

Примеры использования сечений массивов:

Пусть объявлен массив S:

int s[2][3];

Если при обращении к некоторой функции написать s[0], то в эту функцию будет передаваться нулевая строка массива s.

int b[2][3][4];

При обращении к массиву b можно написать, например, b[1][2] и будет передаваться вектор из четырех элементов, а обращение b[1] даст двухмерный массив размером 3 на 4. Нельзя написать b[2][4], подразумевая, что передаваться будет вектор, потому что это не соответствует ограничению, наложенному на использование сечений массива.

Для работы с символьными строками в языке программирования C используются массивы символов, например:

char str[] = "объявление массива символов";

Следует учитывать, что в символьной строке находится на один элемент больше, так как последним элементом строки должен быть ‘\0’. В этом примере использовано неявное задание длины массива символов. Это стало возможным, так как массиву сразу присваивается конкретное значение.

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

char code str[] = "объявление массива символов";

1.8. Структуры

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

Структура – это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является одно-

45

родным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

struct { список описаний}

В структуре обязательно должен быть указан хотя бы один компонент. Компоненты структуры называются полями структуры. Объявление полей производится в следующем виде:

тип-данных описатель;

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

Пример объявления структур:

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 тип-структуры список-идентификаторов;

46

В приведенном ниже примере идентификатор student описывается как тип структуры:

struct student

{char name[25];//Имя и фамилия студента int id, //Номер в журнале

age; //Возраст char usp; //успеваемость

};

Пример:

struct student st1[23];//объявление массива переменных типа студент

Доступ к полям структуры осуществляется с помощью указания имени структуры и следующего через точку имени поля, например:

st[1].name="Иванов"; st[1].id=2; st[1].age=23;

1.9. Поля битов

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

struct { unsigned

идентификатор

1

:

длина-поля

1;

 

unsigned

идентификатор

2

:

длина-поля

2;

}

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

Пример:

struct

{unsigned R: 1;//Флаг приёма байта unsigned T: 1;//Флаг передачи байта unsigned Cmd:5;//Поле команды unsigned St: 1;//Поле статуса

} Cntr;

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

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

Cntr.Cmd=30;

47

Следует отметить, что, несмотря на то, что язык программирования C-51, как наследник стандартного языка программирования C, позволяет использовать поля битов, лучше для битовых переменных использовать специализированный тип переменных bit, размещаемых в пространстве битовой адресации.

1.10. Объединения (смеси)

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

Объединение применяется для следующих целей:

1.использования одного и той же области памяти для размещения переменных различного типа;

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

Объединение по описанию подобно структуре. Тип объединения может задаваться в следующем виде:

union { описание элемента 1;

...

описание элемента n; };

Доступ к элементам объединения осуществляется тем же способом, что и к структурам.

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

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

union {float Koeff; //Интерпретация объединения как переменной плавающего типа

char byte[4];//Интерпретация объединения как массива }buffer; //Объявление переменной buffer

Объединение buffer позволяет последовательному порту получить отдельный доступ ко всем байтам числа buffer.Koeff начиная от младшего байта buffer.byte[0], и заканчивая старшим байтом buffer.byte[3]. В программе затем можно пользоваться загруженным числом как числом с плавающей запятой.

48

1.11. Объявление указателей в языке программирования C-51

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

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

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

спецификатор-типа [ модификатор ] *описатель.

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

Примеры объявления указателей на различные типы переменных:

unsigned int * ptr; /* переменная ptr представляет собой указатель на целую беззнаковую) переменную*/

float * x; /* переменная х указывает на переменную с плавающей точкой*/ char *buffer ; /*объявляется указатель с именем buffer который указывает на символьную переменную*/

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

Например:

ptr=&A; //Присвоить адрес переменной A *ptr=2+2;//Работаем с переменной A a=&B;

*ptr=3*4;//А теперь работаем с переменной B

В качестве модификаторов при объявлении указателя могут выступать ключевые слова const, data, idata, xdata, code. Ключевое слово const указывает,

49

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

Для изменения размера указателя можно использовать ключевые слова

data, idata, xdata, code.

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

float nomer; void *addres; addres = &nomer;

(float *)addres ++;

/* Переменная addres объявлена как указатель на объект любого типа. Поэтому ей можно присвоить адрес любого объекта (& - операция вычисления адреса). Однако, как было отмечено выше, ни одна арифметическая операция не может быть выполнена над указателем, пока не будет явно определен тип данных, на которые он указывает. Это можно сделать, используя операцию приведения типа (float *) для преобразования типа указателя addres к типу float. Затем оператор ++ отдаёт приказ перейти к следующему адресу.*/

В качестве модификаторов при объявлении указателя могут выступать ключевые слова const, data, idata, xdata, code. Ключевое слово const указывает, что указатель не может быть изменен в программе.

Вследствие уникальности архитектуры микроконтроллера 8051 и его последующих модификаций, компилятор С-51 поддерживает 2 вида указателей: память-зависимые и нетипизированные.

Нетипизированные указатели.

Нетипизированные указатели объявляются точно так же, как указатели в стандартном языке программирования C. Для того чтобы не зависеть от типа памяти, в которой может быть размещена переменная, для нетипизированных указателей выделяется 3 байта. В первом байте указывается вид памяти переменной, во втором байте – старший байт адреса, в третьем – младший байт адреса переменной. Нетипизированные указатели могут быть использованы для обращения к любым переменным независимо от типа памяти микроконтроллера. Именно поэтому многие библиотечные функции языка программирования C-51 используют указатели этого типа, при этом им совершенно неважно, в какой именно области памяти размещаются переменные. Приведем листинг, в котором отображаются особенности трансляции нетипизированных указателей:

50