Операцию поразрядного логического И также следует использовать, если необходимо проверить, установлены или сброшены биты порта с определенными номерами. Например, приведенный ниже фрагмент программы производит чтение регистра данных порта Port A, логически умножает его содержимое на константу 0x81 и сравнивает полученный результат с нулем. Если условие равенства нулю выполняется, то это означает, что биты 7 и 0 порта Port A одновременно равны нулю, и следует выполнить действия, которые описаны операторами в фигурных скобках. Если хотя бы один бит PTA7 или PTA0 не равен нулю, то условие ((PORTA & 0х81) == 0) не выполняется, и операторы в фигурных скобках будут пропущены при исполнении.
if ((PORTA & 0x81) == 0) {
:
}
В качестве примера использования оператора ИСКЛЮЧАЮЩЕГО ИЛИ приведем выражение для инвертирования значения бита 7 порта Port A:
PORTA ~= 0х80; //инвертировать бит PTA7
Операторы группы унарных операций. Поскольку операторы инкремента и декремента были рассмотрены выше, основное внимание уделим операторам указателя и косвенной адресации (см. табл. 3.2). Для иллюстрации действия этих операторов рассмотрим следующий пример. Определим три целочисленных переменных с именами num, address, и new_num:
int num, address, new_num;
Также предположим, что переменная num расположена в памяти по адресу 0x2000. Запишем следующее выражение:
address = #
Результатом исполнения выражения будет присвоение переменной address значения адреса переменной num, т.е. новое значение переменной address будет равно 0x2000.
Запишем новое выражение:
new_num = *address;
Результатом выполнения этого выражения будет присвоение переменной new_num значения, которое содержится в ячейке памяти, адрес которой равен текущему значению переменной address. Поскольку содержимое address равно 0x2000, т.е. адресу переменной num, то рассматриваемое выражение в нашем случае эквивалентно выражению:
new_num = num;
Несмотря на то, что в рассмотренных примерах используется корректный синтаксис, в стандарте ANSI C переменную, в которой будут храниться адреса, используемые в качестве указателей на ячейки памяти других переменных, следует определять следующим выражением:
int *address
Отличие от предыдущего способа определения состоит в том, что теперь компилятор самостоятельно определяет формат представления данных для переменной address, чтобы в этой переменной было бы возможно разместить численное значение адреса. Если бы в предыдущем случае программист ошибся и определил тип переменной address как char, то в процессе исполнения выражения address = &num возникла бы потеря информации. В последнем случае ошибка формата исключается.
Обсудим действие следующего выражения:
address = (int *) 0x1000;
Это выражение назначает ячейку памяти с адресом 0x1000 как указатель с именем address. Для того, чтобы извлечь содержимое ячеек памяти следует поместить оператор * перед именем address.
3.4. Функции
В этом параграфе мы познакомимся с Вами с понятием «функция». Мы покажем Вам, как в языке Си определить функцию, как передать в функцию численные значения параметров и как получить после выполнения функции рассчитанные ею значения переменных.
3.4.1. Что такое функция?
Функция — это независимый фрагмент исходного текста программы, предназначенный для решения некоторой задачи. Функции состоят из операторов языка Си и представляют собой обычные подпрограммы.
Представьте себе, что Вы работаете членом большой бригады инженеров, которой предстоит разработать программное обеспечение для встраиваемых систем самолета. Очевидно, что на начальной стадии проекта вся команда разработчиков должна пройти через те этапы структурного проектирования, которые были рассмотрены нами в главе 2. На завершающем этапе структурного проектирования, когда одна большая задача будет поделена на множество мелких, но функционально законченных фрагментов, каждому из членов команды будет поручено выполнение какого либо фрагмента общей программы. Эти фрагменты оформляются как функции, из которых впоследствии будет состоять большая программа.
В соответствии с приведенной стратегией составления большой программы, каждая функция должна обладать тремя свойствами: независимостью, гибкостью и переносимостью. Функция должна быть относительно независима от другого программного кода, поскольку эта функция в дальнейшем может быть использована различными программистами в данном проекте или даже в другом проекте. Возвращаясь к примеру, предположим, что Вам предложили написать функцию, которая устраняет шумовую составляющую входного аналогового сигнала (цифровой фильтр). Ваша программа цифрового фильтра будет использоваться многими соисполнителями проекта для устранения шума различных входных сигналов. Следовательно, Ваша программа, оформленная как функция, должна обеспечивать возможность ее вызова из любого места большой программы (свойство независимости) и должна легко настраиваться на прием сигнала с различных портов МК (свойство гибкости). Термин «относительно независима» в начале этого параграфа мы применили потому, что функция может получать от ранее исполненного программного кода некоторые численные значения, которые будет использовать при своей работе. Например, в Вашу функцию могут передаваться имя порта ввода и номер линии, на которой присутствует сигнал, который подлежит цифровой фильтрации. Вы значительно увеличите гибкость своего решения, если предусмотрите возможность изменения частотного диапазона шумовой составляющей сигнала, которая будет устранена после исполнения программного кода Вашей функции.