В качестве продолжения экспериментов, описанных в статье
"Простой робот на микроконтроллере" мы попробуем сделать робота, объезжающего препятствия. Особенностью робота будет то, что у него не будет датчиков в традиционном смысле, а определять препятствия робот будет при помощи измерения значений напряжения на электромоторах.
Когда робот упирается в препятствие, его колеса встречают сопротивление и начинают тормозиться. Моторы при этом пытаются вращать колеса и испытывают увеличение нагрузки. Нагрузка вызывает увеличение потребления тока электромоторами. В электрической цепи робота происходит просадка напряжения, связанная с увеличением потребления тока электромоторами.
Встретив на своем пути препятствие, робот будет "чувствовать" увеличение нагрузки на моторы. Алгоритм его движения в этом случае будет достаточно простым: робот будет отъезжать немного назад, поворачиваться и снова двигаться вперед, пытаясь таким образом объехать встретившееся препятствие.
Для того, чтобы измерить напряжение на моторах нам будет необходимо использовать аналого-цифровой преобразователь (АЦП, англ. Analog-to-digital converter, ADC), который производит преобразование входного сигнала в численное представление. АЦП в Atmega8 десятибитный, то есть значение измеренного напряжения будет лежать в пределах от 0 до 1023 в численном выражении.
Для работы АЦП требуется источник опорного напряжения, относительно которого АЦП производит измерения. Напряжение, которое преобразуется АЦП, должно быть меньше опорного. Опорное напряжение можно подать на специальную ножку AREF микроконтроллера. Можно также использовать внутренний источник опорного напряжения на 2,56 вольта или использовать напряжение питания. Кроме того, у Atmega8 есть отдельные выводы для питания АЦП: AVCC (аналоговое питание) и AGND (аналоговая "земля"). Подключим AVCC и AREF к положительному полюсу источника питания, а AGND к общей "земле". Следует отметить, что на точность работы АЦП могут влиять наводки и помехи. В нашем учебном примере ими можно пренебречь, но в законченных устройствах следует предпринять меры, описанные в статье
"СТАБИЛИЗАЦИЯ РАБОТЫ МИКРОКОНТРОЛЛЕРА".
Выводы PC0 (канал АЦП ADC0) и PC1 (канал АЦП ADC1) будем использовать для измерения напряжения. Подключим их через ограничительные
резисторы с номиналом 150-220 Ом к тем выводам электромоторов, которые обеспечивают движение вперед при подаче на них положительного напряжения. Для избежания слишком больших просадок напряжения, приводящих к случайной перезагрузке микроконтроллера, подключим к выводам питания микросхем электролитические конденсаторы с номиналом около 470 мкф.
Для индикации используем два светодиода, которые подключим через ограничительные резисторы, например, к выводам PD6 и PD7. Выводы PC2 и PC3 используем для управления мотором M1, а выводы PB1 и PB2 для управления мотором M2.
Схема робота, объезжающего препятствия
Комментарии к схеме робота |
|
|
Выбор выводов для управления моторами продиктован в данном примере по большей части тем, что они находятся на одной стороне микроконтроллера и их подключение к микросхеме драйвера моторов L293D достаточно удобно нарисовать на схеме. В своей конструкции Вы можете использовать другой набор выводов как для светодиодов, так и для электромоторов. Выводы PC4 и PC5 оставлены незадействованными для того, чтобы при совершенствовании конструкции в дальнейшем у нас были в запасе как минимум два свободных канала АЦП (ADC4 и ADC5).
|
|
|
В общем виде программа для робота будет выглядеть следующим образом. Включаем моторы для движения вперед. Немного ждем, пока утихнет скачок напряжения, вызванный стартом моторов. Пока робот еще не столкнулся ни с одним препятствием, делаем серию опросов АЦП, чтобы найти минимальное и максимальное значение напряжения. В каждой серии мы будем определять напряжения на моторах M1 и M2 многократно, чтобы найти их средние значения. Поиск средних значений необходим, чтобы нивелировать помехи.
Вычислив минимальное и максимальное значение напряжения на каждом моторе, мы можем определить значение порога (threshold) для напряжения, при котором мы будем считать, что нагрузка на моторы возросла и перед роботом, скорее всего, возникло препятствие. Этот порог должен быть немного больше, чем среднеминимальное значение напряжения. Вычислим его по следующей формуле:
threshold = min + (max - min) / 20
Следует отметить, что величина, на которую делится дельта между max и min, может быть не 20, а, например, 10 или 15. Эта величина зависит от конкретных моторов, которые будут использоваться в конструкции робота, и подбирается опытным путем. Именно от этой величины будет зависеть чувствительность робота к препятствиям.
После того как все предварительные замеры и вычисления сделаны, мы можем начать бесконечный цикл основной части программы, в котором мы будем опрашивать АЦП, измеряющий напряжение на моторах, и если оно упадет ниже порога (threshold), то будем давать команды моторам для отъезда назад и небольшого поворота. Сторону для поворота будем определять сравнив напряжение на моторах M1 и M2: на моторе со стороны препятствия, падение напряжения обычно больше.
При написании программы воспользуемся набором макроопределений (подстановок) для более удобной работы с портами микроконтроллера. Макроопределение начинается с директивы
#define, после которой написан идентификатор (буквосочетание) для того, чтобы препроцессор при компиляции заменил в тексте программы все вхождения этого буквосочетания на макроподстановку, которая написана после идентификатора. Если в макроподстановке несколько строк, то для их объединения в один блок используют знак обратного слэша \.
Для удобства работы с АЦП напишем функцию read_ADC, которая будет принимать номер канала АЦП и возвращать значение, полученное от преобразователя. Работа этой функции достаточно проста. Сначала нам необходимо в регистре ADMUX (ADC Multiplexer Select Register) указать номер канала (вывода микроконтроллера), для которого будет производиться преобразование (выбор канала АЦП осуществляется установкой битов MUX4..MUX0). Структура регистра ADMUX показана на рисунке.
В старших битах REFS1, REFS0 и ADLAR оставим нули, что будет соответствовать выбору источника опорного напряжения на выводе AREF и выравниванию результата в регистрах ADCH и ADCL по правому краю.
Как уже говорилось выше, АЦП в ATmega8 десятибитный, результат преобразования записывается в два восьмибитных регистра ADCH (для старших разрядов) и ADCL (для младших). При этом первым следует читать значение из регистра ADCL, а затем из регистра ADCH.
Если достаточно восьмибитной точности оцифровки аналогового сигнала, например в тех случаях, когда сигнал зашумлен, то можно отбросить два младших бита в получаемом результате. Сделать это можно, если в бите ADLAR регистра ADMUX установить 1 — результат будет выровнен влево и достаточно будет считывать только регистр ADCH, отбрасывая два младших бита, остающихся в регистре ADCL.
Теперь перейдем к регистру ADCSR (ADC Control and Status Register). Для того, чтобы начать преобразование необходимо установить единицу в бите ADSC. Структура регистра ADCSR показана на следующем рисунке.
После окончания преобразования бит ADSC будет сброшен к нулю и нам можно будет прочитать получившийся результат. Стоит добавить, что помимо регистров ADCH и ADCL, о которых речь шла выше, WinAVR предоставляет виртуальный шестнадцатибитный регистр ADCW, в который склеиваются данные из регистров ADCH и ADCL. Именно значение из регистра ADCW и будет возвращать наша функция.
В заключение описания работы с АЦП остановимся еще на двух важных установках, которые нам будет необходимо сделать перед началом преобразований в регистре ADCSR. По сути, эти установки являются инициализацией АЦП. Во-первых, выберем делитель частоты. Оптимальное значение частоты для АЦП лежит в пределах 50-200 КГц. Если наш микроконтроллер будет работать на частоте 1000000 Гц, то при делителе 8, частота получится равной 125000 Гц или 125 КГц, что будет хорошим выбором. Установим биты ADPS: ADPS2 = 0, ADPS1 = 1, ADPS0 = 1. Во-вторых, чтобы включить АЦП установим бит ADEN = 1. Все эти установки можно сделать одной командой
ADCSR = 0b10000011, которую мы напишем в начале программы в разделе инициализаций.