CUSTOMELECTRONICS.RU
Информационно-учебный блог о разработке электроники
Эл. почта: info@customelectronics.ru

AVR. Управление цифровыми выходами. Продолжение

Это продолжение статьи по работе с цифровыми выходами. Здесь мы разберем то, как происходит управление большим количеством выводов.
Напоминаем, что все наши примеры проверены на нашем аппаратном обеспечении — EduBoard и TutorShield.
Перед началом работы установите перемычке на плате TutorShield, подключите его к EduBoard (или другой Arduino-совместимой плате) и подключите к USB вашего компьютера. О том, как подготовить компьютер, мы писали ранее.

Установка перемычек

Установка перемычек

Управление двумя выводами

Запустите Atmel Studio и создайте новый проект. Назовите его «DoubleBlink» и сохраните в папке C:\AVRProject.
Для начала мы напишем программу, которая будет управлять двумя светодиодами, подключенными к 12му и 13му выводам Arduino. Для обращения к ним на C, потребуется работать с регистрами. Подсказка соответствия выводов Arduino есть на обратной стороне шилда:

Соответствие выводов

Соответствие выводов

Судя по описанию, нужные нам светодиоды подключены к выводам PB4 и PB5. Код программы напишем путем добавления еще одного вывода в пример, рассмотренный ранее.

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	DDRB  |= (1<<4)|(1<<5);
	PORTB &= ~((1<<4)|(1<<5));
	while(1) {
		PORTB |= 1<<5;
		PORTB &= ~(1<<4);
		_delay_ms(1000);
		PORTB &= ~(1<<5);
		PORTB |= 1<<4;
		_delay_ms(1000);
	}
}

Отличие этого примера в том, что мы при конфигурации выводов записываем сразу два бита. То есть строка «DDRB |=(1< <4)|(1<<5);» означает, что нужно побитно сложить содержимое регистра DDRB с числом «(1< <4)|(1<<5);». Эта запись означает, что нужно единицу сдвинуть четыре раза и сложить с единицей, сдвинутой пять раз. В результате получается число 0b00110000, которое записывается в регистр DDRB, настраивая выводы PB4 и PB5 на выход.
Скомпилируете код и загрузите его Arduino Uploader’ом на плату.
Обратите внимание, что два крайних светодиода в линейке горят постоянно. Это связано с особенностями работы Arduino-совместимых плат.
Для загрузки программы в память используется bootloder. Он, в свою очередь, использует интерфейс UART микроконтроллера. Запуск работы контроллера начинается с работы bootloader’а, который инициализирует UART. После завершения его работы управляющие регистры UART остаются без изменения и нужно сбросить управляющие биты. Для этого достаточно добавить следующую строку:

UCSRB &= ~((1<<RXEN)|(1<<TXEN));

UART будет отключен и выводы PD0, PD1 станут доступны для работы. Весь код будет иметь следующий вид:

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	UCSRB &= ~((1<<RXEN)|(1<<TXEN));
	DDRB  |= (1<<4)|(1<<5);
	PORTB &= ~((1<<4)|(1<<5));
	while(1) {
		PORTB |= 1<<5;
		PORTB &= ~(1<<4);
		_delay_ms(1000);
		PORTB &= ~(1<<5);
		PORTB |= 1<<4;
		_delay_ms(1000);
	}
}


И опять PD0 остался в высоком состоянии и светодиод горит! В этом нет ничего страшного, потому, что к выводам микроконтроллера осталась подключена интерфейсная микросхема FT232RL, которая подтягивает вывод PD0 к плюсу питания. Если хотите сделать все окончательно хорошо, то можете настроить PD0 на выход и выставить его в «0″.

Использование циклов

Для воспроизведения каких-либо светодиодных эффектов на линейки удобней всего использовать циклы. Самый простой из них — цикл for. Рассмотрим работу с ним примере. Для начала работы создайте новый проект с именем «Cycle» и введите следующий код:

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	UCSRB &= ~((1<<RXEN)|(1<<TXEN));
	DDRD = 0xFF;
	PORTD = 0x00;
	while(1) {
		for (int i=0; i<256; i++) {
			PORTD = i;
			_delay_ms(100);
		}
	}
}

Работать будем с портом D. Во все биты регистра DDRD записываем единицу (0xFF в шестнадцатеричном виде), а во все биты PORTD — 0.
В цикле for сначала происходит объявление переменной i и присваивается ей значение «0″. Далее задается условия входа в цикл — i<256. Нам нужно, чтобы текущее значение i выводилось на светодиодах. Их восемь штук, поэтому максимальное число, которое получится показать — «0b11111111″, или «255″ в десятичной системе. То есть, пока i меняется от 0 до 255, программа будет заходить в цикл и выполнять инструкции, содержащиеся в теле цикла. И последнее условие цикла — шаг инкремента. В нашем случае, содержимое цикла после каждой итерации увеличивается на единицу.
В самом цикле текущее значение i записывается в регистр PORTD и делается задержка 100мс. На светодиодной линейке вы увидите, как переменная циклически инкрементируется в двоичном виде.

Волна из светодиодов

Для того, чтобы последовательно переключать светодиоды можно записывать в биты регистра единицу, сдвинутую на значение i. После цикла нужно обязательно сбросить состояние этих битов в «0″.

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	UCSRB &= ~((1<<RXEN)|(1<<TXEN));
	DDRD = 0xFF;
	PORTD = 0x00;
	while(1) {
	_delay_ms(1000);
		for (int i=0; i<8; i++) {
			PORTD |= (1<<i);
			_delay_ms(1000);
		}
	PORTD = 0x00;
	}
}

Чтобы это выглядело интереснее можно добавить последовательное отключение светодиодов. Для этого можно добавить такой же точно цикл, в котором нужно записывать не единицу, а ноль. Все очень просто:

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	UCSRB &= ~((1<<RXEN)|(1<<TXEN));
	DDRD = 0xFF;
	PORTD = 0x00;
	while(1) {
		for (int i=0; i<8; i++) {
			PORTD |= (1<<i);
			_delay_ms(1000);
		}
		for (int i=0; i<8; i++) {
			PORTD &= ~(1<<i);
			_delay_ms(1000);
		}
	}
}

Чтобы подключить больше светодиодов потребуется инициализировать PORTB. В нем у микроконтроллера Atmega8 только шесть выводов, то есть мы сможем управлять светодиодами от нулевого до 13-го.

#define F_CPU 16000000UL //16MHz
#include <avr/io.h>
#include <util/delay.h>

int main(void) {
	UCSRB &= ~((1<<RXEN)|(1<<TXEN));
	DDRD = 0xFF;
	PORTD = 0x00;
	DDRB = 0x3F;
	PORTB = 0x00;
	while(1) {
		for (int i=0; i<8; i++) {
			PORTD |= (1<<i);
			_delay_ms(100);
		}
		for (int i=0; i<6; i++) {
			PORTB |= (1<<i);
			_delay_ms(100);
		}
		for (int i=0; i<8; i++) {
			PORTD &= ~(1<<i);
			_delay_ms(100);
		}
		for (int i=0; i<6; i++) {
			PORTB &= ~(1<<i);
			_delay_ms(100);
		}
	}
}

Для ускорения установлена задержка 100мс:

Волна по всем светодиодам

Теперь подключите самостоятельно остальные шесть светодиодов. Они управляются шестью битами регистра PORTC. Должно получиться так, как на видео:

Индивидуальные задания

Попробуйте самостоятельно выполнить следующие задания:

  1. Используя светодиоды, подключенные к выводам PD0, PD1 и PD2 напишите программу, имитирующую работу светофора. Сначала долго горит красный, затем на короткий промежуток времени одновременно с ним включается желтый, после чего красный и желтый гаснут и загорается зеленый. Зеленый горит достаточно долго, затем мигает три раза, гаснет, загорается желтый, гаснет желтый, загорается красный. Все длинные интервалы – 5с, все короткие – 0,5с.
  2. Запустите «волну» из последнего примера в обратную сторону. Загораться и гаснуть они должны не справа налево, а слева направо.

Остальные статьи цикла можно найти здесь.

Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.

Метки: , , , , Просмотров: 7965