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. Должно получиться так, как на видео:
Индивидуальные задания
Попробуйте самостоятельно выполнить следующие задания:
- Используя светодиоды, подключенные к выводам PD0, PD1 и PD2 напишите программу, имитирующую работу светофора. Сначала долго горит красный, затем на короткий промежуток времени одновременно с ним включается желтый, после чего красный и желтый гаснут и загорается зеленый. Зеленый горит достаточно долго, затем мигает три раза, гаснет, загорается желтый, гаснет желтый, загорается красный. Все длинные интервалы – 5с, все короткие – 0,5с.
- Запустите «волну» из последнего примера в обратную сторону. Загораться и гаснуть они должны не справа налево, а слева направо.
Остальные статьи цикла можно найти здесь.
Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.
Метки: AVR, C, выводы, выходы, программирование Просмотров: 10589