В процедуре, приведенной выше, осуществляется логическое умножение (&) содержимого PORTB и константы Ь’10000000’, чтобы определить, установлен 7-й бит регистра или нет; если это так, результат выражения будет отличным от нуля (см. стр. 143). При этом будет выполнен очередной проход цикла while. В теле цикла для установки бита регистра PORTA используется операция ИЛИ «|» (см. стр. 144), а для сброса бита — операция И. Как можно увидеть из приведенного ниже ассемблерного кода, сгенерированного компилятором CSS версии 3, эти выражения были совершенно верно интерпретированы как операции установки и сброса единственного бита. В результате были корректно использованы команды btfss, bcf и bsf.
Если же в программе осуществляется сброс или установка нескольких битов, то используются соответствующие команды ior и and[125].
btfss 6,7; Проверяем 7-й бит регистра PORTB
goto NEXT; ЕСЛИ 0, ТО выходим из цикла
bsf 5,0; Выставляем на RA0 ВЫСОКИЙ уровень
bcf 5,0; Выставляем на RA0 НИЗКИЙ уровень
NEXT ... ...
Этот исполнимый код в точности соответствует тому, который мы написали бы при программировании на ассемблере.
В конкретном случае компилятора CCS для именования содержимого ячейки памяти данных можно было бы использовать нестандартную директиву #byte. Например, строка
#byte INTCON = 0x0В
присваивает регистру с адресом h’06’ имя INTCON. Аналогичным образом в компиляторе CCS можно именовать отдельные биты, используя директиву #bit. Так, строка
#bit INTF = 0х0В.1
присваивает имя 1-му биту регистра h’0B’. Причем если имя INTCON было уже определено, как показано выше, то эту же строку можно было бы записать как
#bit INTF = INTCON.1
Определенные таким образом объекты могут принимать значения только 0 и 1[126]. Таким образом, оператор INTF = 0; сбросит 1-й бит регистра INTCON.
Используя эти директивы компилятора, перепишем наш тестовый фрагмент:
#byte PORTA =5 /* Порт A — регистр h’05’ */
#byte PORTB = 6 /* Порт В — регистр h’06’ */
#bit RA0 = PORTA.0 /* 0-й бит регистра h’05’ — RA0 */
#bit RB7 = PORTB.7 /* 7-й бит регистра h’06’ — RB7 */
while(RB7)
{
RA0 =1; /* На выводе RA0 — ВЫСОКИЙ уровень */
RA0 =0; /* На выводе RA1 — НИЗКИЙ уровень */
}
При компиляции данного фрагмента будет сгенерирован точно такой же исполнимый код, как и раньше. Наличие в компиляторе подобных специальных средств гарантирует, что при их использовании будут сгенерированы эффективные команды
Для удобства все стандартные операторы языка Си приведены в Приложении В.
Напишите на базе алгоритма, показанного на Рис. 6.11 (стр. 198), функцию, возвращающую квадратный корень из положительного 16-битного целого числа.
Решение
Приведя алгоритм из Примера 6.5 к структуре цикла while, получим следующее:
1. Обнулить счетчик цикла.
2. Присвоить 1 переменной i (магическое число).
3. Пока i меньше или равно заданному числу:
а) Вычесть i из числа.
б) Добавить 2 к i.
в) Инкрементировать счетчик цикла.
4. Вернуть счетчик цикла в качестве значения квадратного корня из числа.
В заголовке функции указывается ее имя (sqr_root) и задаются параметры, передаваемые в функцию, а также возвращаемое ею значение. Строка
unsigned int sqr_root(unsigned long number)
означает, что функция возвращает значение типа unsigned int и ожидает передачи одного объекта типа unsigned long, который внутри функции будет известен под именем number. Собственно код функции приведен в Программе 9.2. Поскольку квадратный корень из 16-битного числа поместится в одном байте, счетчик цикла был объявлен как переменная типа unsigned int. А магическое число i будет в 2 раза больше числа count, поэтому оно объявлено как unsigned long. Одновременно с объявлением локальных переменных можно также задавать их начальные значения.
unsigned int sqr_root(unsigned long number)
{
unsigned int count = 0;
unsigned long i = 1;
while(number >= i)
{
number = number — i;
i = i + 2;
count++;
}
return count;
}