Печать

Регулировка яркости семисегментного индикатора

Автор: AntonChip Опубликовано . Опубликовано в Программирование на Си

Рейтинг:   / 8
ПлохоОтлично 

При использовании семисегментных индикаторов часто возникает необходимость в регулировке их яркости свечения. Существуют несколько таких способов. Например, изменять номиналы токоограничительных резисторов, подключенных к сегментам индикатора, но опять нет возможности оперативно регулировать яркость. Также можно подключить к общему катоду(аноду) транзистор, который будет ограничивать ток протекающий через индикаторы. Мы же применим способ благодаря которому можно программно менять яркость - ШИМ регулирование.

В учебной статье про динамическую индикацию мы использовали таймер/счетчик 2, который работал в Нормальном режиме. Но обычно для реализации динамической индикации используют режим СТС (сброс при совпадении), это режим, при котором частота возникновения прерываний по совпадению значений счетчика таймера и регистра OCR2 определяется содержимым OCR2 и предделителем тактовой частоты таймера. При таком режиме работы таймера можно легко изменять частоту обновления разрядов, записывая в регистр сравнения OCR2 необходимое значение, предварительно расчитанное.

Сейчас же мы будем использовать режим FASTPWM и будем обрабатывать два прерывания - по совпадению и переполнению таймера. Режим FASTPWM отличается тем, что при равенстве регистра сравнения (OCR2) и счетчика таймера (TCNT2) возникает прерывание по совпадению, но счетчик не обнуляется (как в режиме СТС), а продолжает считать до переполнения, после чего возникает соответствующее прерывание. При этом прерывания по совпадению и переполнению таймера будут следовать с одинаковой частотой. Значение записанное в регистр OCR2 будет определять время прошедшее от переполнения до совпадения. В обработчике по совпадению мы будем обновлять показания индикаторов, а в обработчике прерывания по переполнению гасить все разряды индикатора при этом мы получим ШИМ регулирование яркостью индикатора.

На рисунках ниже показаны два графика, с большим и малым значением OCR2:

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

Напишем программу в которой значение регистра OCR2 будет выводиться на семисегментный индикатор. Меняться же это значение будет с помощью двух кнопок "+"(SB1) и "-"(SB2), которые подключены к выводам PC0 и PC1, также к этим выводам программно подключены внутренние подтягивающие резисторы.  Микроконтроллер Atmega8 работает от внутреннего генератора частотой 8MHz. Семисегментный индикатор трехразрядный с общим анодом. Схема на рисунке ниже:

Регулировка яркости семисегментного индикатора

Как было сказано выше, в программе используется два типа прерывания от таймера 2:

ISR (TIMER2_COMP_vect){} // Прерывание по совпадению

ISR (TIMER2_OVF_vect){} // Прерывание по переполнению

В прерывании по совпадению мы обновляем данные дисплея, поочередно включая каждый его разряд. В прерывании по переполнению мы выключаем сразу все разряды индикатора, таким образом осуществляя ШИМ регулирование.

/********** Использование динамической индикации ***********************/
/********** Регулировка яркости семисегментного индикатора *************/

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

//------------------0-----1-----2-----3-----4-----5-----6-----7-----8-----9---
char SEGMENT[ ] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

volatile unsigned char segcounter = 0;
volatile int display = 0;

// Обработчик прерывания по совпадению таймера2
ISR (TIMER2_COMP_vect)
{	
PORTD = 0xFF; // Гасим все сегменты
PORTB = (1 << segcounter); // Выбираем следующий разряд
	
switch (segcounter)
{	
case 0: // Раскладываем число на разряды
PORTD = ~(SEGMENT[display % 1000 / 100]);
break;	
case 1:
PORTD = ~(SEGMENT[display % 100 / 10]);
break;		
case 2:
PORTD = ~(SEGMENT[display % 10]);
break;
}
if ((segcounter++) > 1) segcounter = 0;	
}

// Обработчик прерывания по переполнению таймера 2
ISR (TIMER2_OVF_vect)
{
PORTB = 0x00; // Гасим все разряды
}

int main (void) 
{
// Настройка портов
DDRB |= (1 << PB2)|(1 << PB1)|(1 << PB0); // Разряды индикатора
PORTB = 0x00;

DDRC &= ~(1 << PC1)|(1 << PC0); // Кнопки
PORTC |= (1 << PC1)|(1 << PC0); // Вкл. внутренние резисторы

DDRD |= 0xFF; // Сегменты индикатора 
PORTD = 0x00;

// Настройка таймера 2
TCCR2 |= (1 << WGM21)|(1 << WGM20)|(1 << CS21); // Режим FASTPWM. Предделитель на 8
TIMSK |= (1 << TOIE2)|(1 << OCIE2); // Разрешение прерываний по таймеру 2
OCR2 = 50; // Начальное значение OCR2 

sei(); // Глобально разрешаем прерывания
 
while(1)
{		
if((PINC&(1 << PC0)) == 0) // Если нажали кнопку "+"
{
if(OCR2++ >= 255) // Увеличиваем значение OCR2
OCR2 = 255;
_delay_ms(100);
}

if((PINC&(1 << PC1)) == 0) // Если нажали кнопку "-"
{
if(OCR2-- <= 0) // Уменьшаем значение OCR2
OCR2 = 0;
_delay_ms(100);
}

display = OCR2; // Выводим значение OCR2 на индикатор

}
}

Комментарии  

+1 #1 Maks_24 04.02.2014 01:02
Спасибо за статью!!!!
Ув. автор! Просьба о создании статьи(если можно?) с разжеваными функциями работы шины I2C, мульти-мастер,а рбитраж и т.д. Прочитал некоторую лит-ру, сайты, и все равно не пойму как заставить МК работать в режиме ведомого(и правда ли необходимо писать такую сложную программу?).
Спасибо!
Сообщить модератору
0 #2 Max08 28.03.2014 12:16
Извините за вопрос не по теме, будет ли статья про подключение sd-card к avr ?
Сообщить модератору
+5 #3 Роман 24.06.2014 09:11
Автор - ты классный!
Сообщить модератору
0 #4 RusLanCk 20.02.2015 17:52
Добрый день!
Есть вопрос по реализации ШИМ-регулирован ия. У меня собрана схема термометра на ATtiny2313, для отображения температуры используется прерывание по переполнению таймера 0. Индикатор с общим катодом. Все работает, но захотелось добавить возможность притухания. Следуя Вашему примеру, приведенному выше, включил режим FastPWM, и задействовал прерывание по совпадению. В результате получил следующее: нужные мне показания выводятся, яркость свечения изменяется, но есть постоянное подсвечивание всех сегментов во всех разрядах индикатора.
Вот кусок кода (PORTB управляет сегментами, а PORTD - разрядами):
Код:

interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
PORTD =0x3F; // потушить все разряды (биты всех знакомест "1" - для Общ.кат.)
}

interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
PORTB = 0x00; // потушить все сегменты (биты всех сегментов "0" - для Общ.кат.)
PORTB=digits_PB[digit_out[cur_dig]]; // выводим сегменты символа на экран
PORTD&=(cursor[cur_dig]); // засветить нужный разряд (бит знакоместа в "0" - для Общ.кат.)

if (cur_dig > 2) cur_dig=0; // сдвигаемся на следующий разряд
else cur_dig++;
}

Буду благодарен ,если укажете на мою ошибку.
Спасибо!
Сообщить модератору
0 #5 RusLanCk 20.02.2015 18:29
Еще немного пояснений:
digits_PB - массив описания символов, выводимых на индикатор
digit_out - массив индексов выводимых символов
cur_dig - индекс текущего разряда
Сообщить модератору
0 #6 AntonChip 20.02.2015 21:19
Цитирую RusLanCk:
Буду благодарен ,если укажете на мою ошибку.
Спасибо!

Записать выражение так:
Код:
PORTD = ~(cursor[cur_dig]);
Сообщить модератору
0 #7 RusLanCk 20.02.2015 21:45
Цитирую AntonChip:

Код:
PORTD = ~(cursor[cur_dig]);


Спасибо, попробую. А можете объяснить в чем разница между данными выражениями?
Сообщить модератору
0 #8 RusLanCk 20.02.2015 21:51
Скомпилировал прошивку со строкой, приведенной выше и запустил на модели в Протеусе. На индикаторе выводится ерунда, при этом из трех работают только два разряда... Завтра попробую на железяке...
Сообщить модератору
0 #9 AntonChip 20.02.2015 22:00
Цитирую RusLanCk:
Скомпилировал прошивку со строкой, приведенной выше и запустил на модели в Протеусе. На индикаторе выводится ерунда, при этом из трех работают только два разряда... Завтра попробую на железяке...

Ну тогда надо исходник смотреть полностью
Сообщить модератору
0 #10 RusLanCk 20.02.2015 22:03
Цитирую AntonChip:

Ну тогда надо исходник смотреть полностью

Куда выслать?
Сообщить модератору
0 #11 AntonChip 20.02.2015 22:18
Можно на емаил что в контактах или создать тему в форуме
Сообщить модератору
0 #12 RusLanCk 21.02.2015 00:31
Цитирую AntonChip:
Можно на емаил что в контактах или создать тему в форуме

Написал в почту...
Сообщить модератору
0 #13 RusLanCk 21.02.2015 12:24
Цитирую RusLanCk:

Написал в почту...

Добрый день!
Антон, Вы смотрели мой исходник?
Сообщить модератору
0 #14 Alex- 25.04.2016 01:31
Идея позитивная, но а если надо не 3 а 4, 5 разрядов например на часы, в этой меге столько шимов нету. Да и как то расточительно. Вам не кажется использовать 2 таймера на 3 разряда? Но выход есть ставим дешифратор типа К555ИД4 или аналог SN74LS155 а лучше SN74HC155 она изготовлена по КМОП технологии. Данная микросхема позволит выводить до 8 разрядов при этом используя только один вывод шим, который подключаем на строб вход микросхемы а остальными 3 выбираем одну из 8 цифр к которую на данный момент засвечиваем, если вам надо только 4 цифры то достаточно будет 2 выводов дешифратора + всего один вывод шим ))) А сигнал шим будет повторятся на выбранной ноге микросхемы. Только есть единственный минус - индикатор должен быть с общим катодом.
Сообщить модератору
0 #15 Alex- 25.04.2016 01:39
Подумал и решил мыло оставить :-)
Сообщить модератору
0 #16 Alex- 25.04.2016 01:51
Извиняюсь за лишний флуд и за то что не рассмотрел код а только схему, таймер действительно один, хотя реализовано всё программно, если же нужно будет увеличить число разрядов то целесообразно будет поставить микросхему дешифратор, при этом не потребуется использовать прерывание :-) мало ли решите что то грандиозное замутить а тут это прерывание мешается ;-)
Сообщить модератору

Рекомендуем посмотреть