Файл: Особенности и рабочая среда HI-Tech PICC (И. Яловой, 2003).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 16.06.2025

Просмотров: 274

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

суффиксы. Так суффикс “l” или “L” показывает, что литерал должен рассматриваться как принадлежащий к типу long. Суффикс “u” или “U” указывает компилятору, что рассматриваемое целое значение не имеет знака (тип целое без знака). Возможно использовать комбинацию суффиксов. Результат, получаемый при этом, вполне предсказуем.

Если константа не может быть соотнесена ни с одним из целых типов, то она рассматривается как double. Чтобы явно указать ее принадлежность к типу float, что кроме прочего экономит память, необходимо воспользоваться суффиксом “f” или “F”.

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

Строковые литералы (константы) записываются в двойных кавычках, например “мама мыла раму”. Строковые константы сохраняются как const char * в ПЗУ. Рассмотрим следующие примеры:

char

* cp

= “one”;

const char * ccp =

“two”;

char

ca[]

=

”two”;

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

2.3 Тип данных – бит

Как уже несколько раз отмечалось, операции с битами имеют важное значение и часто используются при программировании микроконтроллеров. Чтобы обеспечить максимальное удобство программиста при решении задач, связанных с манипуляциями отдельными битами, был введен дополнительный тип данных – бит (bit). Переменные этого типа описываются как обычно:

static bit over_flag;

переменные типа бит не могут иметь модификатор auto и не могут передаваться в функцию в качестве аргумента, но функция может возвращать значение типа бит. По большому счету переменные этого типа ведут себя как обычные переменные типа unsigned char, но при этом могут принимать значение 0 или 1. Поэтому их удобно использовать для определения различных логических переменных и флагов. Такой подход значительно экономит оперативную память. Компилятор не позволяет создавать указатели типа бит или статически инициализировать переменные этого типа. Все операции с переменными типа бит выполняются с помощью бит-ориентированных инструкций ассемблера, насколько это вообще возможно, при этом создается очень эффективный и компактный код.

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

bitvar = char_var != 0;

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


2.4 Использование бит-адресуемых регистров

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

static unsigned char STATUS @ 0x03;

При описании битовых переменных с указанием абсолютного адреса необходимо указывать абсолютный номер бита в непрерывном битовом поле, которое образуется из последовательности регистров. Например 3-тий бит в ячейке памяти с адресом 5 будет иметь абсолютный номер равный 5*8+3. Таким образом, если мы хотим эксклюзивно обращаться к биту PD регистра STATUS, то достаточно совершить следующий реверанс:

static bit PD @ (unsigned)&STATUS*8 + 3;

Справедливости ради, хочу напомнить, что все стандартные регистры и служебные биты уже описаны до нас и за нас в файле заголовка <pic.h>.

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

#define

PORTBIT(adr, bit) ((unsigned)(&adr)*8+(bit))

Используя этот скрипт, вы упрощаете себе жизнь и повышаете читабельность ваших программ:

static bit PD @ PORTBIT(STATUS, 3);

или более жизненный пример:

static unsigned char

ptKeys @ &PORTB;

static bit

btLeft

@

PORTBIT(ptKeys, 0);

static bit

btRight

@

PORTBIT(ptKeys, 1);

static

bit

btDown

@

PORTBIT(ptKeys,

2);

static

bit

btUp

@

PORTBIT(ptKeys,

3);

2.5 8-бит – целые

Компилятор PIC C поддерживает символьный тип как с указанием знака так и без. Для микроконтроллеров среднего класса этот тип является основным. По-умолчанию символьный тип (char) считается без учета знака. Чтобы это изменить можно воспользоваться опцией компилятора SIGNED_CHAR. Если использован указанный параметр, то возможно использование целых с учетом знака в диапазоне от -128 до +127 включительно или без учета знака (unsigned) от 0 до 255 включительно. В ламерской среде существует предрассудок, что тип данных char предназначен исключительно для хранения кодов символов ASCII. Но это глубокое заблуждение, тем более теперь, когда повсеместно применяются 16-и битовые кодировки. Реально же тип char это наименьший из существующих целых типов и ничем не отличается от своих больших собратьев. Исторически сложилось так, что на компьютерах, где С получил наибольшее распространение использовались кодировки, для которых тип char был идеальным хранилищем символов. Использовать же его для хранения чисел было не выгодно, так как адресация памяти была как минимум словами (16 бит), да и диапазон маловат для прикладных ПС программ. А вот для контроллера (конкретно того семейства о котором идет речь в этом труде) char – это родной тип и с ним он работает очень эффективно. Так что, если вы планируете использовать С для разработки встроенных систем на базе PICконтроллеров, то привыкайте использовать тип char как основной.

2.6 16-бит – целые

Для представления 16-и битных данных в PIC C есть 4 типа short, int, unsigned short,


unsigned int. Котрые могут вмещать числа из диапазонов от -32768 до 32767 с учетом знака и от 0 до 65535 без учета знака. Как мы видим, в PIC C два типа int и short практически не имеют отличий. Это связано со стремлением соответствовать стандартам. И это очень серьезный довод, тем более что 8-ми битные целые числа поддержаны в полном объеме посредствам типа char, поэтому последний рекомендуется использовать вместо int, где это только возможно.

2.7 32-бит – целые

Компилятор PIC C поддерживает два вид целых 32 битных чисел – long и unsigned long. Они могут содержать числа от -2,147,483,648 до 2,147,483,647 включительно и от 0 до 4,294,967,295 соответственно. В соответствии со стандартом ANSI на язык С, 32 бита – это наименьший размер для типа long.

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

Числа с плавающей запятой реализованы в соответствии со стандартом IEEE 754 (32 бита) и модифицированный IEEE 754 (24 бита). Для представления чисел с плавающей запятой используется два типа float и double. По умолчанию оба типа реализуют урезанный стандарт. Если требуется повышенная точность в вычислениях, то необходимо воспользоваться опцией компилятора -D32. В этом случае для типа double бедет использован полный IEEE 754.

В соответствии со стандартом число с плавающей запятой представлено тремя полями (см. табл. 4):

знак – это один бит определяющий знак числа);

показатель степени – это 8-и битное дополнение до 127 показателя степени, если реальный показатель степени равен 0 то в это поле будет записано число 127;

мантисса – это мантисса числа (правая часть от десятичной точки), значащая часть числа с левой стороны от десятичной точки всегда равна 1 за исключением случая, когда само

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

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

Число= −1 Знак 2 Показатель степени−127 1. мантисса .

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

Таблица 4 Форматы записи чисел с плавающей запятой

Формат

Знак

Смещенный показатель

мантисса

степени

IEEE 754 (32 бита)

х

хххх хххх

ххх хххх хххх хххх хххх хххх

Уркзанный IEEE 754 (24 бита)

х

хххх хххх

ххх хххх хххх хххх

Таблица 5 Примеры записи чисел с плавающей запятой

Формат

Число

Смещенный

1.мантисса

Десятичное

показатель

число

степени

IEEE 754 (32 бита)

7DA6B69Bh

11111011b

1.01001101011011010011011b

2.77000e+37

(251)

(1.302447676659)

Уркзанный IEEE 754

42123Ah

10000100b

1.001001000111010b

36.557

(24 бита)

(132)

(1.142395019531)


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

2.9 Статические переменные

Глобальные или статические переменные могут располагаться по абсолютному адресу. Для этого в описании переменной используется специальная конструкция “@ <адрес>”. Например:

volatile unsigned char Portvar @ 0x06;

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

_Portvar equ 06h

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

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

3 Структурные типы данных и строки

3.1 Структуры и строки

HI-TECH C поддерживает структурные типы данных struct и union любого размера от одного байта и более, но не стоит забывать и о физических размерах доступной памяти. Переменные структурного типа могут быть переданы в качестве аргумента в функцию и возвращены ей в качестве результата. Позволю себе заметить, что, в соответствии со стандартом, аргументы передаются в функцию по значению, то есть намного гуманнее по отношению к объему кода и расходу памяти передавать не сами структуры а указатели (ссылки) на них. Тем более, что PIC C полностью поддерживает указатели на структурные типы.

3.2 Битовые поля

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

struct {

unsigned

hi

: 1;

unsigned

dummy :

6;

unsigned

lo

:

1;

}foo @ 0x10;

Вданном случае структура будет занимать один байт по адресу 0х10. При этом поле hi будет соответствовать нулевому биту, а lo – 7-му биту регистра по адресу 0х10. Младший бит поля dummy будет располагаться в первом, а старший – в 6-м битах регистра 0х10. Если структура описана как показано выше, то она будет размещена по абсолютному адресу и память под ее размещение выделяться не будет.

Если необходимо определить битовое поле, которое содержит неиспользуемые области, то можно указать безымянные поля:

struct { unsigned hi : 1; unsigned : 6; unsigned lo : 1; } foo @ 0x10;

3.3 Строки в ОЗУ и ПЗУ

Анонимные неизменные строки (строковые литералы) всегда располагаются в ПЗУ и могут быть доступны только через const-указатели. В следующем примере строка “Мама мыла раму” является строковой константой и размещена в ПЗУ. Поэтому к ней можно обращаться только через константный указатель:

#define MATHER “Мама мыла раму” SendBuff(MATHER);

Массивы переменных (в том числе изменяемые строки) должны быть проинициализированы как в следующем примере:

char fred[] = “Мама мыла раму”;

В этом случае мы получаем массив в ОЗУ. Этот массив инициализируется строкой “Мама мыла раму” при запуске программы. Образец строки хранится в ПЗУ. Анонимные неизменные строки в другом контексте будут квалифицированы как константы и доступны