роботы
робототехника
микроконтроллеры
Главная
Новости
Спорт
Шаг за шагом
Статьи
Wiki
Форум
Downloads
Ссылки
Контакты
 

Простейший робот
Схема и описание простого робота на одной микросхеме. Робот может двигаться на свет или следовать за рукой.  

Схема и описание первого проекта на микроконтроллере AVR
"Hello, world!" для микроконтроллера.  

Робот на микроконтроллере AVR
Схема робота на микроконтроллере. Примеры программ.  

AVR GCC :: УПРАВЛЕНИЕ ПОРТАМИ МИКРОКОНТРОЛЛЕРА


Управление портами в AVR GCC. Регистры DDRx и PORTx.
Представление чисел. Побитные операции.
Функция задержки. Безусловный переход в программе.


Порты микроконтроллера - это устройства ввода/вывода, позволяющие микроконтроллеру передавать или принимать данные. Стандартный порт микроконтроллера AVR имеет восемь разрядов данных, которые могут передаваться или приниматься параллельно. Каждому разряду (или биту) соответствует вывод (ножка) микроконтроллера. Ножки микроконтроллера также называют пинами. Для обозначения портов используются латинские буквы А, В, С и т.д. Количество портов ввода/вывода варьируется в зависимости от модели микроконтроллера.

Любой порт микроконтроллера можно сконфигурировать как вход или как выход. Для того чтобы это сделать, следует записать в соответствующий порту регистр DDRx необходимое значение. Кроме того, как вход или выход можно сконфигурировать отдельно любой вывод (пин) порта. В любом случае, хотите вы сконфигурировать весь порт или отдельный вывод, вам необходимо будет работать с ригистрами DDRx.

DDRx - регистр направления передачи данных. Этот регистр определяет, является тот или иной вывод порта входом или выходом. Если некоторый разряд регистра DDRx содержит логическую единицу, то соответствующий вывод порта сконфигурирован как выход, в противном случае - как вход. Буква x в данном случае должна обозначать имя порта, с которым вы работаете. Таким образом, для порта A это будет регистр DDRA, для порта B - регистр DDRB и т. д.

Используя AVR GCC, записать в необходимый регистр то или иное значение можно одним из следующих способов.

Для всего порта сразу.

DDRD = 0xff;

Все выводы порта D будут сконфигурированы как выходы.

0xff - шестнадцатиричное представление числа ff, где 0x является префиксом, используемым для записи шестнадцатиричных чисел. В десятичном представлении это будет число 255, а в двоичном виде оно будет выглядеть как 11111111. То есть во всех битах регистра DDRD будут записаны логические единицы.

В AVR GCC для представления двоичных чисел используется префикс 0b. Таким образом, число 11111111 должно представляться в программе как 0b11111111. Мы можем записать предыдущую команду в более читабельном виде.

DDRD = 0b11111111;

Хотя такая запись и выглядит более наглядной, при конфигурировании портов принято использовать шестнадцатиричное представление чисел.

Для того чтобы сконфигурировать все выводы порта D как входы, следует записать во все биты регистра DDRD логические нули.

DDRD = 0x00;

В регистр DDRD можно записать и другие числа. Например:

DDRD = 0xb3;

0xb3 - шестнадцатиричное представление числа 179. В двоичном виде оно будет выглядеть как 10110011. То есть часть выводов порта D будет сконфигурирована как выходы, а часть - как входы.

PD0 - 1 (выход)
PD1 - 1 (выход)
PD2 - 0 (вход)
PD3 - 0 (вход)
PD4 - 1 (выход)
PD5 - 1 (выход)
PD6 - 0 (вход)
PD7 - 1 (выход)




Каждый бит регистров DDRx может быть установлен отдельно. Например, чтобы сконфигурировать отдельно вывод PD2 как выход, нам необходимо в соответствующий бит регистра DDRD записать 1. Для этого применяют следующую конструкцию.

DDRD |= 1<<2;

1<<2 - осуществляет сдвиг единички влево на 2 бита, то есть справа добавляются два нулевых бита и получается 100, а знак "|", стоящий перед знаком присваивания "=", осуществляет операцию побитного логического сложения.

При логическом сложении 0+0=0, 0+1=1, 1+1=1. Операцию логического сложения по-другому называют операцией ИЛИ (английское название OR).

Таким образом, к битам, хранящимся в регистре DDRD, прибавляется двоичное 100, представленное в 8-битном регистре микроконтроллера как 00000100, и результат записывается обратно в регистр DDRD.




Чтобы сконфигурировать отдельно вывод PD2 как вход, нам необходимо в соответствующий бит регистра DDRD записать 0. Для этого применяют следующую конструкцию.

DDRD &= ~(1<<2);

В данном случае результат сдвига единицы на две позиции влево инвертируется с помощью операции побитного инвертирования, обозначаемой значком "~".

При инверсии мы получаем вместо нулей единички, а вместо единичек - нули. Эта логическая операция иначе называется операцией НЕ (английское название NOT).

Таким образом, при побитном инвертировании 00000100 мы получаем 11111011. (Подробнее о работе с числами в микроконтроллере см. во врезке ниже.)

Получившееся число с помощью операции побитного логического умножения & умножается на число, хранящееся в регистре DDRD, и результат записывается в регистр DDRD.

При логическом умножении 0*0=0, 0*1=0, 1*1=1. Операцию логического умножения иначе называют операцией И (английское название AND).

То есть сдвинутая нами влево на две позиции единичка превращается при инвертировании в ноль и умножается на соответствующий бит, хранящийся в регистре DDRD. При умножении на ноль мы получаем ноль. Таким образом, бит PD2 становится равным нулю.



Кроме логических операций И, ИЛИ, НЕ существует также операция "исключающее ИЛИ" (английское название XOR). Она обозначается значком ^.

При исключающем ИЛИ значение бита, к которому "прибавляется" единичка, изменяется на противоположное.

Например, 110011 ^ 11010 = 101001.

Следует добавить, что работа с числами в 8-битном микроконтроллере проходит с использованием 8-битных регистров. Перед вычислениями аргумент помещается в один из специальных регистров, с которыми напрямую может работать арифметико-логическое устройство (АЛУ). Например, перед выполнением команды DDRD &= ~(1<<2) аргумент помещается во вспомогательный регистр микроконтроллера. Содержимое такого регистра будет выглядеть как 11111011.
После этого осуществляется операция побитного умножения, что дает во втором бите регистра DDRD значение 0.
Спасибо участникам форума Олегу (oleg) и VCOM за появление этого комментария и корректирование статьи.




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

Если вывод сконфигурирован как выход, то единичка в соответствующем бите регистра PORTx формирует на выводе сигнал высокого уровня, а ноль - сигнал низкого уровня.

Если же вывод сконфигурирован как вход, то единичка в соответствующем бите регистра PORTx подключает к выводу внутренний подтягивающий pull-up резистор, который обеспечивает высокий уровень на входе при отсутствии внешнего сигнала.

Установить "1" на всех выводах порта D можно следующим образом.

PORTD = 0xff;

А установить "0" на всех выводах порта D можно так.

PORTD = 0x00;

К каждому биту регистров PORTx можно обращаться и по отдельности так же, как в случае с регистрами DDRx.

Например, команда

PORTD |= 1<<3;

установит "1" (сигнал высокого уровня) на выводе PD3.

Команда

PORTD &= ~(1<<4);

установит "0" (сигнал низкого уровня) на выводе PD4.

В AVR GCC сдвиг можно осуществлять и с помощью функции _BV(), которая выполняет поразрядный сдвиг и вставляет результат в компилируемый код.

В случае использования функции _BV() две предыдущие команды будут выглядеть следующим образом.

PORTD |= _BV(PD3);      // установить "1" на линии 3 порта D

PORTD &= ~_BV(PD4);      // установить "0" на линии 4 порта D


В микроконтроллерах AVR каждому параллельному порту ввода/вывода поставлен в соответствие также регистр PINx.
PINx является регистром выводов порта и в отличие от регистров DDRx и PORTx доступен только для чтения. PINx позволяет считывать входные данные порта на внутреннюю шину микроконтроллера. Об этом регистре мы поговорим чуть позже.




Теперь попробуем написать несколько простых программ для лучшего понимания принципа работы с портами микроконтроллера.

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

Подключить светодиод к микроконтроллеру можно различными способами.

Рисунок 1 Рисунок 2


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



/*************************************************************************
ПРИМЕР ВКЛЮЧЕНИЯ СВЕТОДИОДА СИГНАЛОМ ВЫСОКОГО УРОВНЯ
Пример подключения на рисунке 1
**************************************************************************/

   #include <avr/io.h>   
   int main(void) {	// начало основной программы
   DDRD = 0xff;		// все выводы порта D сконфигурировать как выходы
   PORTD |= _BV(PD1);	// установить "1" (высокий уровень) на выводе PD1
   }			// закрывающая скобка основной программы





/***********************************************************************
ПРИМЕР ВКЛЮЧЕНИЯ СВЕТОДИОДА СИГНАЛОМ НИЗКОГО УРОВНЯ
Пример подключения на рисунке 2
************************************************************************/ 

   #include <avr/io.h>
   
   int main(void) {	// начало основной программы
   DDRD = 0xff;		// все выводы порта D сконфигурировать как выходы
   PORTD &= ~_BV(PD1);	// установить "0" (низкий уровень) на выводе PD1
   }			// закрывающая скобка основной программы





Теперь попробуем мигнуть светодиодом, подключенным так, как это изображено на левом рисунке. Для этого используем функцию задержки _delay_ms().

Функция _delay_ms() формирует задержку в зависимости от передаваемого ей аргумента, выраженного в миллисекундах (в одной секунде 1000 миллисекунд). Максимальная задержка может достигать 262.14 миллисекунд. Если пользователь передаст функции значение более 262.14, то произойдет автоматическое уменьшение разрешения до 1/10 миллисекунды, что обеспечивает задержки до 6.5535 секунд. (О формировании более длительных задержек можно прочитать в статье "Циклы в языке Си. Формирование задержки".)

Функция _delay_ms() содержится в файле delay.h, поэтому нам будет необходимо подключить этот файл к программе. Кроме того, для нормальной работы этой функции необходимо указать частоту, на которой работает микроконтроллер, в герцах.



/*************************************
ПРИМЕР МИГАНИЯ СВЕТОДИОДОМ
Пример подключения на рисунке 1
**************************************/

   #define F_CPU 1000000UL  // указываем частоту в герцах

   #include <avr/io.h>
   #include <util/delay.h>

   int main(void) {	// начало основной программы
   
   DDRD = 0xff;		// все выводы порта D сконфигурировать как выходы

   PORTD |= _BV(PD1);	// установить "1" (высокий уровень) на выводе PD1, 
			//зажечь светодиод

   _delay_ms(500);	// ждем 0.5 сек.

   PORTD &= ~_BV(PD1);	// установить "0" (низкий уровень) на выводе PD1,
			//погасить светодиод

   _delay_ms(500);	// ждем 0.5 сек.

   PORTD |= _BV(PD1);	// установить "1" (высокий уровень) на выводе PD1,
			//зажечь светодиод

   _delay_ms(500);	// ждем 0.5 сек.

   PORTD &= ~_BV(PD1);	// установить "0" (низкий уровень) на выводе PD1,
			//погасить светодиод

   }			// закрывающая скобка основной программы



Серия миганий светодиодом будет очень короткой. Для того чтобы сделать мигание непрерывным, можно организовать бесконечный цикл с помощью оператора безусловного перехода "goto". Оператор goto осуществляет переход к месту программы, обозначенному меткой. Имя метки не должно содержать пробелов. После имени метки ставится знак двоеточия. Между именем метки и двоеточием не должно быть пробелов.

/*******************************************************
ПРИМЕР БЕСКОНЕЧНОГО МИГАНИЯ СВЕТОДИОДОМ
Пример подключения на рисунке 1
********************************************************/

   #define F_CPU 1000000UL  // указываем частоту в герцах

   #include <avr/io.h>
   #include <util/delay.h>

   int main(void) {	// начало основной программы
   
   DDRD = 0xff;		// все выводы порта D сконфигурировать как выходы

start:			// метка для команды goto start

   PORTD |= _BV(PD1);	// установить "1" (высокий уровень) на выводе PD1,
			//зажечь светодиод

   _delay_ms(250);	// ждем 0.25 сек.

   PORTD &= ~_BV(PD1);	// установить "0" (низкий уровень) на выводе PD1,
			//погасить светодиод

   _delay_ms(250);	// ждем 0.25 сек.

goto start;		// перейти к метке start

   }			// закрывающая скобка основной программы




myROBOT.ru Это оригинальная статья myROBOT.ru
Постоянный адрес статьи: http://myrobot.ru/stepbystep/pr_mcports.php








Статьи раздела
ПРОГРАММИРОВАНИЕ

Все статьи курса
Язык программирования C (Си): введение.

Переменные и константы. Присваивание.

Управление портами микроконтроллера AVR.

Циклы в языке Си. Формирование задержки.






Copyright © myrobot.ru, 2005-2013


OpenHosting