Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
В результате, после восьмого шага она оказывается в ячейке признака переноса. Пока С равно нулю, оператор brcc в строке 29 передает управление по метке m2, и цикл сдвига продолжается. После восьмого шага признак переноса С станет равен единице. Поэтому перехода на начало цикла в строке 29 не произойдет, и управление перейдет к строке 30. В результате очередного девятого цикла сдвига не произойдет. Оператор безусловного перехода в строке 30 передаст управление на начало основного цикла, и программа снова приступит к проверке состояния кнопки.
Процедура сдвига влево занимает строки 31—38. Эта процедура работает точно так же, как и процедура сдвига вправо. Отличия:
начальное значение, записываемое в регистр rab (см. строку 31), равно 0b;
вместо оператора lsr (сдвиг вправо) в строке 36 использован оператор lsl (сдвиг влево).
В качестве подпрограммы задержки применена уже известная нам подпрограмма с тремя вложенными циклами. Текст этой подпрограммы полностью скопирован из предыдущего примера (листинг 1.7) и расположен в строках 39—55.
Программа на языке СИ
Возможный вариант той же программы, но на языке СИ, приведен в листинге
1.10. В этой программе впервые мы будем использовать переменную. До сих пор мы не применяли переменные лишь благодаря предельной простоте предыдущих программ. Теперь же переменная нам понадобится для того, чтобы осуществлять операции сдвига.
Переменная будет хранить текущее значение всех сдвигаемых битов так же, как в программе на Ассемблере их хранил регистр rab. Назовем переменную тем же именем, каким мы называли регистр. Описание переменной в нашей программе происходит в строке 4. Так как сдвигаемых битов должно быть всего восемь, то самый подходящий тип данных для нашей переменной — это unsigned char.
Переменная такого типа имеет длину в один байт. Второй однобайтовый тип (char) в данном случае нам не подходит, так как представляет собой число со знаком. У такого числа старший разряд интерпретируется как знак. И лишь семь младших разрядов используются непосредственно для хранения значений.
Строки 1—9 составляет модуль инициализации программы. В полном соответствии с алгоритмом модуль инициализации новой программы почти полностью повторяет соответствующий модуль из предыдущего примера. Исключение составляет лишь вновь добавленная строка с описанием переменной (строка 4).
Листинг 1.10

Основной цикл программы занимает строки 10—22. Телом цикла является оператор сравнения (конструкция if—else), проверяющий состояние бита, связанного с переключателем. Собственно проверка происходит в строке 11. Здесь младший бит порта PD проверяется на равенство единице. Если он равен единице, то выполняется процедура сдвига вправо (строки 12—16). В противном случае выполняется процедура сдвига влево (строки 18—22).
Каждая из этих процедур выполняет цикл из восьми сдвигов в нужном направлении. Так как вся конструкция if—else находится внутри бесконечного цикла, то она многократно повторяется. То есть после проверки происходит восемь сдвигов в нужном направлении. Затем новая проверка, и так далее.
Обе процедуры сдвига построены одинаково. Рассмотрим подробнее процедуру сдвига вправо. В строке 12 переменной rab присваивается начальное значение. Затем начинается цикл сдвига. Цикл организован при помощи оператора while (строка 13). Его тело составляют строки 14—16, которые реализуют уже знакомый нам алгоритм. Сначала происходит вывод значения всех разрядов переменной rab в порт РВ. Как и в предыдущем случае, выводимое значение нам нужно предварительно проинвертировать.
Для инвертирования числа мы снова используем прием, который мы применили в программе на Ассемблере. То есть, воспользуемся оператором «Исключающее ИЛИ». Обратимся к строке 14 нашей программы. В этой строке регистру PORTB присваивается значение выражения rab^0xFF. Символ « ^ » в языке СИ как раз и означает операцию «Исключающее ИЛИ». При помощи единственного выражения мы сразу и инвертируем и присваиваем.
В строке 15 производится сдвиг разрядов. Для сдвига используется оператор «>>». Результатом выражения rab >> 1 является число, полученное путем сдвига всех разрядов переменной rab на одну позицию вправо. Число 1 справа от оператора сдвига означает количество разрядов, на которое нужно сдвинуть число. Таким образом, выражение
rab = rab >> 1;
означает: присвоить переменной rab значение этой же переменной сдвинутое на один разряд вправо. Язык СИ допускает другую, сокращенную форму записи того же самого выражения:
rab >>= 1;
Новое выражение полностью эквивалентно предыдущему.
Подобные изящные сокращения являются фирменной особенностью языка СИ. Благодаря ним программа на языке СИ получается короче и проще. В строке 16 вызывается функция задержки. Время задержки составляет 200 мс.
Для того, чтобы цикл сдвига повторялся только восемь раз, используется оператор цикла (строка 13). В качестве условия, при котором цикл выполняется, используется выражение rab! = 0. В языке СИ выражение « != » означает «Не равно».
Таким образом, наш цикл сдвига (строкибудет выполняться до тех пор, пока rab не равен нулю. Это и будут наши восемь шагов сдвига. Чтобы убедиться в этом, еще раз посмотрите на табл. 1.2. Значение одного из разрядов b0—b7, а, значит, и всей переменной rab, во время первых восьми шагов не равно нулю. И только на девятом шаге все восемь рабочих разрядов обнулятся. Но так как при этом заложенное нами условие не выполняется, последнего девятого цикла не будет.
Процедура сдвига влево находится в строках 18—22 программы и работает точно так же, как процедура сдвига вправо. Имеются лишь два отличия:
другое начальное значение переменной rab (см. строку 18); применен другой оператор сдвига.
Для сдвига влево применяется оператор «<<» (см. строку 21). При желании выражение в строке 21 тоже можно сократить. Вместо rab = rab << 1; можно
Боремся с дребезгом контактов
http://*****/content/view/629/33/
Обратимся еще раз к схеме на рис. 1.2. В схеме используется кнопка, имеющая одну группу из двух нормально разомкнутых контактов. А если есть контакты, значит, есть и дребезг этих контактов. В [3] рассматривается способ борьбы с антидребезгом аппаратным способом. Теперь рассмотрим способ борьбы с дребезгом контактов программным путем.
Итак, новая задача будет сформулирована следующим образом:
«Разработать схему управления светодиодом при помощи одной кнопки. При нажатии кнопки светодиод должен изменять
свое состояние на противоположное (включен или выключен). При разработке программы принять меры для борьбы с дребезгом контактов».
Схема
Как уже говорилось, принципиальная схема остается прежняя (см. рис. 1.2).
Алгоритм
Алгоритм нам придется доработать. Самый простой способ борьбы с дребезгом — введение в программу специальных задержек. Рассмотрим это подробнее. Начнем с исходного состояния, когда контакты кнопки разомкнуты. Программа ожидает их замыкания. В момент замыкания возникает дребезг контактов.
Дребезг приводит к тому, что на соответствующем разряде порта PD вместо простого перехода с единицы в ноль мы получим серию импульсов. Для того, чтобы избавится от их паразитного влияния, программа должна сработать следующим образом. Обнаружив первый же нулевой уровень на входе, программа должна перейти в режим ожидания. В режиме ожидания программа приостанавливает все свои действия и просто отрабатывает задержку.

Рис. 1.10. Алгоритм управления светодиодом с антидребезгом
Время задержки должно быть выбрано таким образом, чтобы оно превышало время дребезга контактов.
Такую же процедуру задержки нужно ввести в том месте программы, где она ожидает отпускания кнопки. Для разработки нового алгоритма возьмем за основу предыдущий (см. рис. 1.9). Доработанный алгоритм с добавлением операций антидребезговой задержки приведен на рис. 1.10. Как вы можете видеть из рисунка, вся доработка свелась к включению двух процедур задержки. Одной — после обнаружения факта нажатия кнопки, а второй — после обнаружения факта ее отпускания.
Программа на Ассемблере
Для реализации нового алгоритма немного доработаем программу (листинг 1.3). Новый вариант программы приведен ниже (листинг 1.5). В этой программе используются следующие новые для нас операторы:
rcall
Переход к подпрограмме. У этого оператора всего один параметр — относительный адрес начала подпрограммы. Относительный адрес —это просто смещение относительно текущего адреса. Выполняя команду rcall, микроконтроллер запоминает в стеке текущий адрес программы из счетчика команд и переходит по адресу, определяемому смещением. Такой же принцип задания адреса для перехода мы уже встречали в команде rjmp. В строке 20 программы (листинг 1.5) производится вызов подпрограммы задержки по адресу, соответствующему метке wait.
ret
Команда выхода из подпрограммы. По этой команде микроконтроллер извлекает из стека адрес, записанный туда при выполнении команды rcall, и осуществляет передачу управления по этому адресу. В листинге 1.5 команду ret мы можем видеть в конце подпрограммы wait (см. строку37).
push
Запись содержимого регистра общего назначения в стек. У данного оператора всего один операнд — имя регистра, содержимое которого нужно поместить в стек. В строке 32 программы (листинг 1.5) в стек помещается содержимое регистра с именем loop.
pop
Извлечение информации из стека. У этого оператора тоже всего один операнд — имя регистра, в который помещается информация, извлекаемая из стека. В строке 36 программы (листинг 1.5) информация извлекается из стека и помещается в регистр loop.
dec
Уменьшение содержимого РОН. У команды один параметр — имя регистра. Команда dec (декремент) уменьшает на единицу содержимое регистра, имя которого является ее параметром. В строке 34 программы (листинг 1.5) уменьшается на единицу содержимое регистра loop.
brne
Оператор условного перехода (переход по условию). У этого оператора всего один параметр — относительный адрес перехода. Условие перехода звучит как «не равно». Попробуем разобраться, как проверяется это условие. И тут нам придется вспомнить о регистре состояния микроконтроллера (SREG).
Как уже говорилось ранее, каждый бит этого регистра представляет собой флаг. Все флаги регистра предназначены для управления работой микроконтроллера. Кроме уже известного нам флага I (глобальное разрешение прерываний), этот регистр имеет ряд флагов, отражающих результаты работы различных операций.
Полное описание регистра SREG смотрите [4]. В данном случае нас интересует лишь один из таких флагов — флаг нулевого результата (флаг Z). Этот флаг устанавливается в том случае, когда при выполнения очередной команды результат окажется равным нулю. Например при вычитании двух чисел, сдвиге разрядов числа или в результате операции сравнения. В нашем случае на значение флага влияет
команда dec. (строка 34). Если в результате действия этого оператора содержимое регистра окажется равным нулю, то флаг Z устанавливается в единицу. В противном случае он сбрасывается в ноль.
Флаг Z будет хранить записанное в него значение до тех пор, пока какая-нибудь другая команда его не изменит. Какие из команд оказывают влияние на флаг Z, а какие нет, можно узнать из приложения.
Команда brne использует флаг Z в качестве условия. Команда выполняет переход только в том случае, если флаг Z сброшен. То есть когда результат предыдущей команды неравен нулю. В нашей программе (листинг 1.5) подобный оператор применяется в строке 35.
Листинг 1.5


Описание программы (листинг 1.5)
Новый вариант программы является полной копией старой (см. листинг 1.3), в которую добавлены новые элементы, обеспечивающие антидребезговую задержку. Так как задержка нужна в двух разных местах программы, она оформлена в виде подпрограммы. Для формирования задержки используется один дополнительный регистр общего назначения.
Поэтому в начале нашей новой программы (строка 4) добавлена команда описания регистра. Регистру r17 и присваивается имя loop. По-английски слово loop означает цикл. Таким именем принято называть переменные, применяемые для организации циклов. Не удивляйтесь, что я употребил тут термин «переменная». В языке Ассемблер тоже используется понятие «переменные». Так наш регистр loop можно считать переменной с именем loop.
Запись значения в этот регистр эквивалентна присвоению значения переменной. Также можно интерпретировать и другие операции с регистром. Сложение содержимого двух регистров можно считать сложением переменных, вычитание — вычитанием, и так далее.
Подпрограмма задержки расположена в строках 32—37. Первой строке подпрограммы присвоена метка wait. Именно по этой метке и будет вызываться подпрограмма. Опустим пока назначение команд hush и pop (строки 32 и 36). Собственно
процедура задержки расположена в строках 33—35. Формирование задержки производится путем многократного выполнения пустого цикла. Сначала в регистр loop записывается некое начальное значение (строка 33). В нашем случае оно равно 200.
Затем начинается цикл, который постепенно уменьшает значение регистра loop до нуля (строки 34 и 35). Происходит это следующим образом. В строке 34 содержимое регистра уменьшается на единицу, а в строке 35 происходит проверка содержимого на ноль. Если ноль не достигнут, то управление передается по метке wt1, и цикл повторяется. Когда же содержимое loop кажется равным нулю, очередного перехода не произойдет, и цикл задержки закончится.
Очевидно, что в нашем случае цикл задержки выполнится 200 раз. Если обратиться к приложению, то можно узнать, что команда dec выполняется за один такт системного генератора. Команда brne выполняется:
за один такт, если не вызывает перехода; за два такта, если вызывает переход.
Поэтому один цикл задержки будет выполняться за 3 такта. Двести циклов за 3x200=600 тактов. Тактовая частота кварцевого резонатора у нас равна 4 МГц. Длительность одного колебания тактовой частоты равна 1/4 = 0,25 мкс. Поэтому время, за которое будут выполнены все 200 циклов задержки, равно 600x0,25 = 150 мкс. Добавьте сюда время выполнения остальных команд подпрограммы, включая команду вызова подпрограммы и команду возврата из подпрограммы, и вы получите общее время задержки (162 мкс).
Максимальная задержка, которую можно сформировать при помощи данной подпрограммы, равна (255x3x0,25)+ 12=203,25 мкс. Учтите, что в нашем случае не применяется предварительное деление частоты тактового генератора. Если это было бы не так, то длительность выполнения каждой команды нужно было бы умножать на коэффициент деления предварительного делителя.
Теперь вернемся к двум командам работы со стеком, которые мы не стали рассматривать вначале. Они предназначены для сохранения в стеке и последующего восстановления содержимого регистра loop. В начале подпрограммы (строке 32) значение loop сохраняется, а перед выходом их подпрограммы (строка 36) — восстанавливается.
Подобный прием придает программе одно полезное свойство. После
окончания работы подпрограммы значения всех регистров микроконтроллера остается без изменений. В данном конкретном случае такое свойство ничего не дает, кроме, разве что, дополнительной задержки. Однако в сложных программах, имеющих не одну, а несколько подпрограмм, одни и те же регистры удобно использовать в разных подпрограммах.
Те же самые регистры может использовать и основная программа. В этом случае описанное выше полезное свойство просто необходимо для правильной работы всей программы. Зная эту особенность, программисты стараются применять подобный прием в каждой подпрограмме, независимо от того, полезен он в данном конкретном случае или нет.
Не исключена ситуация, когда в процессе доработки программы вам все же придется повторно использовать какие-либо регистры. Заранее обеспечить корректную работу вашей подпрограммы — это хороший стиль программирования.
В соответствии с алгоритмом (рис. 1.5) подпрограмма задержки в нашей программе вызывается два раза. Первый раз — после окончания цикла ожидания нажатия кнопки (строка 20). Второй раз — после окончания цикла ожидания отпускания (строка 30).
Описание программы (листинг 1.5)
Новый вариант программы является полной копией старой (см. листинг 1.3), в которую добавлены новые элементы, обеспечивающие антидребезговую задержку. Так как задержка нужна в двух разных местах программы, она оформлена в виде подпрограммы. Для формирования задержки используется один дополнительный регистр общего назначения.
Поэтому в начале нашей новой программы (строка 4) добавлена команда описания регистра. Регистру r17 и присваивается имя loop. По-английски слово loop означает цикл. Таким именем принято называть переменные, применяемые для организации циклов. Не удивляйтесь, что я употребил тут термин «переменная». В языке Ассемблер тоже используется понятие «переменные». Так наш регистр loop можно считать переменной с именем loop.
Запись значения в этот регистр эквивалентна присвоению значения переменной. Также можно интерпретировать и другие операции с регистром. Сложение содержимого двух регистров можно считать сложением переменных, вычитание — вычитанием, и так далее.
Подпрограмма задержки расположена в строках 32—37. Первой строке подпрограммы присвоена метка wait. Именно по этой метке и будет вызываться подпрограмма. Опустим пока назначение команд hush и pop (строки 32 и 36). Собственно
процедура задержки расположена в строках 33—35. Формирование задержки производится путем многократного выполнения пустого цикла. Сначала в регистр loop записывается некое начальное значение (строка 33). В нашем случае оно равно 200.
Затем начинается цикл, который постепенно уменьшает значение регистра loop до нуля (строки 34 и 35). Происходит это следующим образом. В строке 34 содержимое регистра уменьшается на единицу, а в строке 35 происходит проверка содержимого на ноль. Если ноль не достигнут, то управление передается по метке wt1, и цикл повторяется. Когда же содержимое loop кажется равным нулю, очередного перехода не произойдет, и цикл задержки закончится.
Очевидно, что в нашем случае цикл задержки выполнится 200 раз. Если обратиться к приложению, то можно узнать, что команда dec выполняется за один такт системного генератора. Команда brne выполняется:
за один такт, если не вызывает перехода; за два такта, если вызывает переход.
Поэтому один цикл задержки будет выполняться за 3 такта. Двести циклов за 3x200=600 тактов. Тактовая частота кварцевого резонатора у нас равна 4 МГц. Длительность одного колебания тактовой частоты равна 1/4 = 0,25 мкс. Поэтому время, за которое будут выполнены все 200 циклов задержки, равно 600x0,25 = 150 мкс. Добавьте сюда время выполнения остальных команд подпрограммы, включая команду вызова подпрограммы и команду возврата из подпрограммы, и вы получите общее время задержки (162 мкс).
Максимальная задержка, которую можно сформировать при помощи данной подпрограммы, равна (255x3x0,25)+ 12=203,25 мкс. Учтите, что в нашем случае не применяется предварительное деление частоты тактового генератора. Если это было бы не так, то длительность выполнения каждой команды нужно было бы умножать на коэффициент деления предварительного делителя.
Теперь вернемся к двум командам работы со стеком, которые мы не стали рассматривать вначале. Они предназначены для сохранения в стеке и последующего восстановления содержимого регистра loop. В начале подпрограммы (строке 32) значение loop сохраняется, а перед выходом их подпрограммы (строка 36) — восстанавливается.
Подобный прием придает программе одно полезное свойство. После окончания работы подпрограммы значения всех регистров микроконтроллера остается без изменений. В данном конкретном случае такое свойство ничего не дает, кроме, разве что, дополнительной задержки. Однако в сложных программах, имеющих не одну, а несколько подпрограмм, одни и те же регистры удобно использовать в разных подпрограммах.
Те же самые регистры может использовать и основная программа. В этом случае описанное выше полезное свойство просто необходимо для правильной работы всей программы. Зная эту особенность, программисты стараются применять подобный прием в каждой подпрограмме, независимо от того, полезен он в данном конкретном случае или нет.
Не исключена ситуация, когда в процессе доработки программы вам все же придется повторно использовать какие-либо регистры. Заранее обеспечить корректную работу вашей подпрограммы — это хороший стиль программирования.
В соответствии с алгоритмом (рис. 1.5) подпрограмма задержки в нашей программе вызывается два раза. Первый раз — после окончания цикла ожидания нажатия кнопки (строка 20). Второй раз — после окончания цикла ожидания отпускания (строка 30).
Переключающийся светодиод
http://*****/content/view/628/33/
Постановка задачи
Как уже говорилось, предыдущая задача настолько проста, что решение ее средствами микропроцессорной техники лишено всякого смысла. Усложним немного задачу. Заставим переключаться светодиод при нажатии кнопки.
Новая задача, может звучать так:
«Разработать устройство управления одним светодиодным индикатором при помощи одной кнопки. При каждом нажатии кнопки светодиод должен поочередно включаться и отключаться. При первом нажатии кнопки светодиод должен включиться, при следующем отключиться и т. д.».
Вы можете сказать, что и эта новая задача легко решаема при помощи простейшего D-триггера. Однако все же рассмотрим, как ее можно решить при помощи микроконтроллера.
Принципиальная схема
Так как для новой задачи, как и для предыдущей, нам необходима всего одна кнопка и всего один светодиод, то придумывать новую схему не имеет смысла. Применим для второй задачи уже знакомую нам схему, показанную на рис. 1.2.
Алгоритм
Алгоритм задачи номер два начинается так же, как алгоритм нашей первой задачи. То есть с набора команд, выполняющих инициализацию системы. Так как схема и принцип работы портов ввода—вывода не изменились, то алгоритм инициализации системы будет полностью повторять соответствующий алгоритм из предыдущего примера.
После команд инициализации начинается основной цикл программы. Однако действия, выполняемые основным циклом, будут немного другими. Попробуем, как и в предыдущем случае, описать эти действия словами.
1. Прочитать состояние младшего разряда порта PD (PD.0).
2. Если значение этого разряда равно единице, перейти к началу цикла.
3. Если значение разряда PD.0 равно нулю, изменить состояние выхода РВ.0 на противоположное.
4. Перейти к началу цикла.
Итак, мы описали алгоритм словами. Причем это довольно общее описание. Реальный алгоритм немного сложнее. Словесное описание алгоритма не всегда удобно. Гораздо нагляднее графический способ описания алгоритма. На рис. 1.3 алгоритм нашей работы будущей программы изображен в графическом виде.
Такой способ отображения информации называется графом. Прямоугольниками обозначаются различные действия, выполняемые программой. Суть выполняемого действия вписывается внутрь такого прямоугольника.
Допускается объединять несколько операций в один блок и обозначать одним прямоугольником. Последовательность выполнения действий показывается стрелками. Ромбик реализует разветвление программы. Он представляет собой операцию выбора. Условие выбора записывается внутри ромбика. Если условие истинно, то дальнейшее выполнение программы продолжится по пути, обозначенному словом «Да».
Если условие не выполнено, то программа пойдет по другому пути, обозначенному стрелкой с надписью «Нет». Прямоугольником со скругленными боками принято обозначать начало и конец алгоритма. В нашем случае программа не имеет конца. Основной цикл программы является бесконечным циклом.
Рассмотрим подробнее алгоритм, изображенный на рис. 1.8. Как видно из рисунка, сразу после старта программы выполняется установка вершины стека. Следующее действие — это программирование портов ввода—вывода. Затем начинается главный цикл программы (обведен пунктирной линией). Внутри цикла ход выполнения программы разветвляется.
Первой операцией цикла является проверка
состояния младшего разряда порта PD (PD0). Программа сначала читает состояние этой линии, а затем выполняет операцию сравнения. В процессе сравнения значение разряда PD0 проверяется на равенство единице. Если условие выполняется, программа переходит к началу цикла (по стрелке «Да»).

Рис. 1.8. Алгоритм программы с переключающимся светодиодом
Если условие не выполняется (PD0 не равен единице), выполнение программы продолжается по стрелке «Нет», где выполняется еще одна операции сравнения. Это сравнение является частью процедуры переключения светодиода. Для того, чтобы переключить светодиод, мы должны проверить его текущее состояние и перевести его в противоположное.
Как вы помните, светодиодом управляет младший разряд порта РВ (РВ0). Поэтому именно его мы будем проверять и изменять. Работа алгоритма переключения светодиода предельно проста. Сначала оператор сравнения проверяет разряд РВ0 на равенство единице. Если результат проверки — истина (РВ0=1), то разряд сбрасывается в ноль (0 => РВ0). Если ложно, устанавливается в единицу (1 => РВ0).
Сочетание символов «=>» означает операцию присвоения. Такое обозначение иногда используется в программировании при написании алгоритмов. После переключения светодиода управление передается на начало главного цикла.
Итак, наш алгоритм готов, и можно приступать к составлению программы. Но не торопитесь. Все не так просто. Приведенный выше алгоритм хорош лишь для теоретического изучения приемов программирования. На практике же он работать не будет.
Дело в том, что микроконтроллер работает с такой скоростью, что за время, пока человек будет удерживать кнопку в нажатом состоянии, главный цикл программы успеет выполниться многократно (до сотни раз). Это произойдет даже в том случае, если человек постарается нажать и отпустить кнопку очень быстро. При каждом проходе главного цикла программа обнаружит факт нажатия кнопки и переключит светодиод.
В результате работа нашего устройства будет выглядеть следующим образом. Как только кнопка будет нажата, светодиод начнет быстро переключаться. На столько быстро, что вы даже не увидите, как он мерцает. Это будет выглядеть как свечение в полнакала.
В момент отпускания кнопки процесс переключения остановится, и светодиод окажется в одном из своих состояний (засветится или потухнет). В каком именно состоянии он останется, зависит от момента отпускания кнопки. А это случайная величина. Как видите, описанный выше алгоритм не позволяет создать устройство, соответствующее нашему техническому заданию.
Для того, чтобы решить данную проблему, нам необходимо усовершенствовать наш алгоритм. Для этого в программу достаточно ввести процедуру ожидания. Процедура ожидания приостанавливает основной цикл программы сразу после того, как произойдет переключение светодиода. Теперь программа должна ожидать момента отпускания кнопки. Как только кнопка окажется отпущенной, выполнение главного цикла возобновляется.
Новый, доработанный алгоритм приведен на рис. 1.9. Как видно из рисунка, новый алгоритм дополнен всего двумя новыми операциями, которые и реализуют цикл ожидания. Цикл ожидания добавлен после процедуры переключения светодиода. Выполняя цикл ожидания,
программа сначала читает значение бита PD0, а затем проверяет его на равенство единице. Если PD0 не равно единице (кнопка нажата), то цикл ожидания повторяется. Если PD0 равно единице (кнопка отпущена) то цикл ожидания прерывается, и управление перейдет на начало основного цикла.
Новый алгоритм вполне работоспособен и может стать основой реальной программы. Попробуем составить такую программу.

Рис. 1.9. Усовершенствованный алгоритм программы с переключающимся светодиодом
Программа на Ассемблере
Текст возможного варианта программы для второго примера приведен в листинге 1.3.
Листинг 1.3

В программе применены следующие новые для нас команды:
sbrc
Команда из группы условных переходов. Вызывает пропуск
google_protectAndRun("render_ads. js::google_render_ad", google_handleError, google_render_ad); следующей за ней команды, если соответствующий разряд РОН сброшен. У команды два параметра. Первый параметр — имя регистра общего назначения, второй параметр — номер проверяемого бита. В строке 17 программы (листинг 1.3) подобная команда проверяет нулевой разряд регистра temp. Если этот разряд равен нулю, то команда, записанная в строке 16, пропускается, и выполняется команда из строки 17. Если проверяемый бит равен единице, то пропуска не происходит, и выполняется команда в строке 16.
sbrs
Команда, обратная предыдущей. Пропускает следующую команду, если соответствующий разряд РОН установлен в единицу. Имеет те же два параметра, что и команда sbrc. В строке 26 (листинг 1.3) подобная команда проверяет значение младшего разряда регистра temp. Если проверяемый бит равен единице, то команда в строке 27 пропускается, и выполняется команда в строке 28. Если проверяемый разряд равен нулю, то выполняется строка 27.
sbi
Сброс в ноль одного из разрядов порта ввода—вывода. Команда имеет два параметра: имя порта и номер сбрасываемого разряда. В строке 22 (листинг
google_protectAndRun("ads_core. google_render_ad", google_handleError, google_render_ad); 1.3) подобная команда выполняет сброс младшего разряда порта PORTB.
сbi
Установка в единицу одного из разрядов порта ввода—вывода. Имеет такие же два параметра, как и предыдущая команда. В строке 24 (листинг 1.3) подобная команда устанавливает младший разряд порта PORTB в единицу
Программа на Ассемблере
Текст возможного варианта программы для второго примера приведен в листинге 1.3.
Листинг 1.3

В программе применены следующие новые для нас команды:
sbrc
Команда из группы условных переходов. Вызывает пропуск
следующей за ней команды, если соответствующий разряд РОН сброшен. У команды два параметра. Первый параметр — имя регистра общего назначения, второй параметр — номер проверяемого бита. В строке 17 программы (листинг 1.3) подобная команда проверяет нулевой разряд регистра temp. Если этот разряд равен нулю, то команда, записанная в строке 16, пропускается, и выполняется команда из строки 17. Если проверяемый бит равен единице, то пропуска не происходит, и выполняется команда в строке 16.
sbrs
Команда, обратная предыдущей. Пропускает следующую команду, если соответствующий разряд РОН установлен в единицу. Имеет те же два параметра, что и команда sbrc. В строке 26 (листинг 1.3) подобная команда проверяет значение младшего разряда регистра temp. Если проверяемый бит равен единице, то команда в строке 27 пропускается, и выполняется команда в строке 28. Если проверяемый разряд равен нулю, то выполняется строка 27.
sbi
Сброс в ноль одного из разрядов порта ввода—вывода. Команда имеет два параметра: имя порта и номер сбрасываемого разряда. В строке 22 (листинг 1.3) подобная команда выполняет сброс младшего разряда порта PORTB.
сbi
Установка в единицу одного из разрядов порта ввода—вывода. Имеет такие же два параметра, как и предыдущая команда. В строке 24 (листинг 1.3) подобная команда устанавливает младший разряд порта PORTB в единицу
Описание программы (листинг 1.3)
Первая часть программы (строки 1—15) полностью повторяет аналогичную часть программы из предыдущего примера (листинг 1.1). И это неудивительно, так как алгоритм инициализации не изменился. Зато значительно усложнился основной цикл программы. Теперь он значительно вырос по объему и занимает строки 16—28. В строке 16 производится чтение порта PORTD. Число, прочитанное из порта, записывается в регистр temp.
В строке 17 производится проверка младшего разряда прочитанного числа. Если младший бит равен единице (кнопка не нажата), то управление переходит к строке 18. В строке 18 находится оператор безусловного перехода, который передает управление по метке main, то есть на начало цикла. Таким образом, пока кнопка не нажата, будет выполняться короткий цикл программы (строки 16,17 и 18).
Если кнопка нажата, младший разряд числа в регистре temp окажется равным нулю. В этом случае оператор sbrc в строке 17 передаст управление к строке 19, где начинается модуль переключения светодиода. И начинается он с чтения состояния порта РВ.
В строке 20 производится проверка младшего бита считанного числа. Если этот бит равен нулю, то строка 21 пропускается, и выполняется строка 22. Если младший бит равен единице, то выполняется строка 21. В строке 22 оператор sbi устанавливает младший бит регистра PORTB в единицу.
А в строке 21 находится оператор безусловного перехода, который передает управление по метке ml на строку 24. Там оператор cbi сбрасывает младший бит регистра PORTB в ноль. Таким образом, происходит переключение в младшем разряде порта РВ. Ноль меняется на единицу, а единица на ноль.
После переключения светодиода управление передается на строку 25. Это происходит либо при помощи команды безусловного перехода (строка 23), либо естественным путем после строки 24.
Строки 25—27 содержат цикл ожидания момента отпускания кнопки. Цикл ожидания начинается с чтения содержимого порта PORTD (строка 25). Прочитанное значение записывается в регистр temp. Затем производится проверка младшего разряда прочитанного числа (строка 26). Если этот разряд равен нулю (кнопка еще не отпущена), выполняется строка 27 (безусловный переход на метку m2), и цикл ожидания повторяется снова.
Когда при очередной проверке кнопка окажется отпущенной, повинуясь команде sbrc (в строке 26), микроконтроллер пропустит строку 27 и перейдет к строке 28. Расположенный там безусловный переход передаст управление на начало основного цикла (по метке main).
Программа на языке СИ
Та же задача на языке СИ решается следующим образом. При помощи построителя создаем точно такую же заготовку программы с теми же параметрами, как и в предыдущем случае. Доработка программы также будет сводиться к вписыванию необходимых команд в основной цикл программы. Однако это будут другие команды, реализующие новый алгоритм.
Возможный вариант программы смотри в листинге 1.4. Прежде чем мы перейдем к изучению этой программы, необходимо
остановиться на новом элементе языка СИ, который в ней применяется.
if else
Условный оператор. Этот оператор позволяет выполнять разные операции в зависимости от некоторого условия. В программе на языке СИ оператор записывается следующим образом:
if (условие)
{ Набор операторов 1 }
else
{ Набор операторов 2 }
Условие — это любое логическое выражение. Если результат этого выражения— истина (не равен 0), то выполняется «Набор операторов 1». В противном случае выполняется «Набор операторов 2».
Оба набора операторов — это любые допустимые операторы языка СИ. Каждый из операторов в наборе должен оканчиваться точкой с запятой. Добавочное слово else не обязательно. Его можно исключить вместе с набором операторов 2. Тогда, если условие ложно, оператор не будет выполнять никаких действий.
Листинг 1.4

Описание программы (листинг 1.4)
Начало программы (до строки 30) сформировано автоматически и полностью соответствует соответствующей части предыдущей программы (листинг 1.2). Я лишь немного сократил комментарии для того, чтобы не перегружать текст программы лишней информацией.
Тело основного цикла претерпело значительные изменения. Теперь он занимает строки 31—37. В строке 32 расположена процедура ожидания нажатия кнопки. Она представляет собой пустой цикл while. В теле цикла (две фигурные скобки) нет ни одного оператора.
Цикл не выполняет никаких действий. Он будет выполняться, пока его условие истинно. В качестве условия выбрано равенство младшего разряда регистра PORTD нулю. На языке СИ это записывается следующим образом:
PORTD.0==l.
В языке СИ различают оператор равенства и оператор присвоения:
один символ «=» означает присвоение,
запись типа А=5 означает присвоение переменной А значения 5;
двойной символ «==» означает операцию сравнения,
запись А==5 означает проверку на равенство значений переменной А и константы 5.
Результат такого сравнения равен единице в случае, если А равно пяти, и равен нулю, если это не так. Поэтому, цикл while в строке 32 программы продолжается до тех пор, пока значение разряда PORTD.0 равно единице. То есть до тех пор, пока кнопка, подключенная к этому разряду, остается не нажатой. Как только кнопка окажется нажатой, цикл (строка 32) заканчивается, и программа перейдет к строке 33.
В строках 33—36 находится оператор сравнения. Он выполняет задачу переключения светодиода. Для этого в его условии записана проверка младшего разряда порта РВ, то есть содержимого регистра PINB. Разряд проверяется на равенство единице (строка 33).
Если значение разряда равно единице, то выполняется строка 34, в которой младшему разряду регистра PORTB
присваивается нулевое значение. Если условие не выполняется (значение PINB.0 не равно единице), выполняется строка 36, и младшему разряду PORTB присваивается единица. Значение, записанное в регистр PORTB, непосредственно поступает на выход порта РВ. В результате состояние младшего разряда порта (РВ0) меняется на противоположное.
В строке 37 программы расположен цикл ожидания отпускания кнопки. Он аналогичен циклу в строке 32. Только условие теперь обратное. Цикл выполняется до тех пор, пока значение PORTD.0 равно нулю. То есть пока кнопка нажата.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 |


