Изучаем энкодер. Делаем простой RGB контроллер.Можно ли одной «ручкой» регулировать контрастность ЖКИ, изменять уровни срабатывания исполнительных устройств, выбирать язык меню, включать или выключать опции настроек? Оказывается, можно. И элемент, при помощи которого возможно осуществить все эти регулировки, носит название энкодер. Далее, собственно, мы и будем знакомиться с этим элементом.

По выполняемым функциям и внешнему виду энкодеры напоминают аналоговые потенциометры, но на этом их сходство заканчивается. Области применения энкодеров шире и разнообразнее. Как правило, с появлением цифровых систем ранее применявшиеся внешние элементы управления, такие как переключатели, аналоговые потенциометры с ручками на панелях управления, плохо стыкуются с их основным элементом — микроконтроллером.

Многопозиционные переключатели и аналоговые потенциометры которые сильно различающиеся по своим функциям, обладают одним общим свойством — для изменения их состояния нужно вращать имеющиеся у них ручки управления. Следовательно, и то, и другое может быть заменено элементом, подстройка которого осуществляется вращением соответствующей ручки.

Энкодер, в зависимости от направления его вращения, формирует два сигнала. Благодаря ним микроконтроллер может определять направление вращения вала энкодера. Всю остальную работу по реализации аналогового потенциометра или переключателя микроконтроллер берет на себя. Отмечу, что по своему исполнению энкодеры весьма разнообразны. Так, например, приведенный на рисунке сверху энкодер имеет механическую «трещотку», что, несомненно удобно для использования его в качестве переключателя. Кроме того, данный энкодер снабжен независимой нормально разомкнутой кнопкой, что оказывается довольно удобно в целом ряде применений. Существуют энкодеры и без «трещоток», которые плавно вращаются. Их удобно использовать в качестве регуляторов уровня вместо аналоговых потенциометров.

Рассмотрим, как осуществляется взаимодействие микропроцессора (микроконтроллера) с энкодером. На микропроцессорное устройство подаются два сигнала с контактов А и В энкодера. Необходимо, чтобы один из них, например, с контакта А, подавался на вход, который формирует прерывания в микроконтроллере по изменению уровня входного сигнала (по перепаду из 1 в 0). Вызывающая программа должна анализировать при этом состояние сигнала В. Так как устройство является механическим и может выдавать в линию «дребезг», то необходимо это учитывать в алгоритме обработки прерывания. Обработчик прерывания будет таким:

ISR(SIG_INTERRUPT0)
{
_delay_us(50);
if((PIND&(1 << PD2))==0)
{
_delay_us(50);
if((PIND&(1 << PD0))==0)
{ 
// вращение по часовой стрелке
// здесь выполняем операцию
}
else
{
// вращение против часовой стрелки
// здесь выполняем операцию
}
}
GIFR |= (1 << INTF0); // очищаем флаг
return;
}

На основе такого механизма сделаем простой RGB контроллер, где яркость каждого цвета можно будет регулировать отдельно. Переключение между цветами производится нажатием кнопки энкодера. Также предусмотрим 3 дополнительных светодиода HL1 - HL3, которые будут индикаторами регулируемого цвета. Схема для нашего примера будет такой:

Изучаем энкодер. Делаем простой RGB контроллер.

Микроконтроллер Atmega8 работает от внутреннего тактового генератора частотой 8МГц. Резисторы R4 - R6 подтягивающие, на всякий случай, подключим и внутренние. Энкодер можно использовать любой с кнопкой, маркировка используемого в проекте неизвестна. RGB светодиод для поверхностного монтажа размера 5050. Полный текст программы ниже:

// Принцип работы энкодера. Управляем RGB светодиодом с помощью энкодера
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile unsigned char pwm_r, pwm_g, pwm_b, button = 0;

// Обработчик внешнего прерывания INT0
ISR(SIG_INTERRUPT0)
{
_delay_us(50);
if((PIND & (1 << PD2)) == 0)
{
_delay_us(50);
if((PIND & (1 << PD0)) == 0) // Крутим по часовой стрелке
{ 

if(button == 0) // Если включен режим настройки красного цвета 
{ 
if(pwm_r-- <= 0) pwm_r = 0; // Уменьшаем красный цвет
} 

if(button == 1) // Если включен режим настройки зеленого цвета 
{  
if(pwm_g-- <= 0) pwm_g = 0; // Уменьшаем зеленый цвет
}

if(button == 2) // Если включен режим настройки синего цвета 
{
if(pwm_b-- <= 0) pwm_b = 0; // Уменьшаем синий цвет
}
}

else // Крутим против часовой стрелки
{
if(button == 0) // Если включен режим настройки красного цвета 
{
if(pwm_r++ >= 0xFF) pwm_r = 0xFF; // Увеличиваем красный цвет
}

if(button == 1) // Если включен режим настройки зеленого цвета 
{
if(pwm_g++ >= 0xFF) pwm_g = 0xFF; // Увеличиваем зеленый цвет
}

if(button == 2) // Если включен режим настройки синего цвета 
{
if(pwm_b++ >= 0xFF) pwm_b = 0xFF; // Увеличиваем синий цвет
}
}
}
GIFR = (1 << INTF0); // Очищаем флаг внешнего прерывания
return;
}	

// Прерывание по переполнению Т0
ISR(TIMER0_OVF_vect)
{
static unsigned char pwm_counter;
// Если значение яркости больше значения счетчика
// включаем соответствующий вывод
if(pwm_counter < pwm_r) PORTB |= (1 << PB0);
// Иначе вывод выключен
else PORTB &= ~(1 << PB0);
if(pwm_counter < pwm_g) PORTB |= (1 << PB1);
else PORTB &= ~(1 << PB1);
if(pwm_counter < pwm_b) PORTB |= (1 << PB2);
else PORTB &= ~(1 << PB2);	
// Инкрементируем счетчик
pwm_counter++;
}

int main (void) 
{
// Порты ввода/вывода
DDRB = 0xFF; // Выходы
PORTB = 0x00;
DDRD |= (0 << PD2)|(0 << PD1)|(0 << PD0); // Входы
PORTD |= (1 << PD2)|(1 << PD1)|(1 << PD0); // Подключаем подтягивающие резисторы
// Таймер 0
TCCR0 |= (1 << CS00); // Тактирование Т0 без предделителя
TIMSK |= (1 << TOIE0); // Разрешаем прерывание по переполнению Т0
// Внешние прерывания
MCUCR |= (1 << ISC01); // Прерывание по заднему фронту INT0(по спаду импульса)
GIFR |= (1 << INTF0); // Очищаем флаг внешнего прерывания
GICR |= (1 << INT0);  // Разрешаем внешние прерывания INT0
	
sei(); // Глобально разрешаем прерывания

pwm_r = 150; // Начальные установки цветов
pwm_g = 0;
pwm_b = 0; 

while(1)
{	 
if((PIND & (1 << PD1)) == 0) // Если нажата кнопка
{
while((PIND & (1 << PD1)) == 0){} // Ждем отпускания кнопки
_delay_ms(50);
if(button ++>= 2) button = 0; // Переключаем режимы настройки цветов
}  
// Включаем индикаторы настройки цветов		
if(button == 0) PORTB |= (1 << PB3); // Красный
else PORTB &= ~(1 << PB3);
if(button == 1) PORTB |= (1 << PB4); // Зеленый
else PORTB &= ~(1 << PB4);
if(button == 2) PORTB |= (1 << PB5); // Синий
else PORTB &= ~(1 << PB5);
}
}

В данном примере используется программный ШИМ, о реализации которого говорилось на прошлых занятиях. Переменная button хранит состояние кнопки, в зависимости от того сколько раз нажата кнопка регулируется определенный цвет и горит его индикатор. Для удобства практического применения такого контроллера необходимо записывать состояние яркости всех цветов в EEPROM, чтобы при выключении питания не настраивать цвета заново.


Архив для статьи "Изучаем энкодер. Делаем простой RGB контроллер"
Описание: Проект AVRStudio
Размер файла: 38.47 KB Количество загрузок: 2 889 Скачать

Комментарии  

0 #21 Chip 11.09.2016 23:27
Код:

// Главная функция
int main (void)
{
DDRB |= (1 << PB5)|(1 << PB4)|(1 << PB3)|(1 << PB2)|(1 << PB1)|(1 << PB0); // выходы
PORTB = 0x00;
DDRD |= (0 << PD2)|(0 << PD1)|(0 << PD0); // входы
PORTD |= (1 << PD2)|(1 << PD1)|(1 << PD0); // подключаем подтягивающие резисторы

TIMSK |= (1 << TOIE0); // разрешение прерывания по таймеру0
TCCR0 |= (1 << CS00);

MCUCR |= (0 << ISC00)|(1 << ISC01); // прерывание по заднему фронту INT0(по спаду импульса)

GIFR |= (1 << INTF0); // очищаем флаг внешнего прерывания
GICR |= (1 << INT0); // разрешаем внешние прерывания INT0

sei(); //глобально разрешаем прерывания

if (i==2)
{
PORTB=0b00000001;
}

while(1)
{
}


}
Сообщить модератору
0 #22 AntonChip 12.09.2016 21:33
переменную i сделать volatile unsigned int
Сообщить модератору
0 #23 Chip 15.09.2016 16:58
Странно. Добавил volatile unsigned int и все равно не работает.
Сообщить модератору
0 #24 Иоанн 31.01.2017 16:58
Добрый вечер.Благодарю автора за его труд, низкий Вам поклон. Самый информативный курс по AVR что я видел. С данным уроком возникла проблемка. Студия ругается на ISR(SIG_INTERRU PT0) .Если заменить на ISR(INT0_vect), замена будет равносильной?
Сообщить модератору
0 #25 AntonChip 31.01.2017 23:09
Цитирую Иоанн:
Студия ругается на ISR(SIG_INTERRUPT0) .Если заменить на ISR(INT0_vect), замена будет равносильной?
Да, верно
Сообщить модератору
0 #26 Александр713 08.02.2018 21:56
Автору огромное спасибо за труд. В исходнике есть ошибка строка 71(if(pwm_count er++ > 63)), а значения переменных яркости ==64. При таких значениях програмный ШИМ работать не будет.
Сообщить модератору