При использовании семисегментных индикаторов часто возникает необходимость в регулировке их яркости свечения. Существуют несколько таких способов. Например, изменять номиналы токоограничительных резисторов, подключенных к сегментам индикатора, но опять нет возможности оперативно регулировать яркость. Также можно подключить к общему катоду(аноду) транзистор, который будет ограничивать ток протекающий через индикаторы. Мы же применим способ благодаря которому можно программно менять яркость - ШИМ регулирование.
В учебной статье про динамическую индикацию мы использовали таймер/счетчик 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 на индикатор } }
Комментарии
Ув. автор! Просьба о создании статьи(если можно?) с разжеваными функциями работы шины I2C, мульти-мастер,а рбитраж и т.д. Прочитал некоторую лит-ру, сайты, и все равно не пойму как заставить МК работать в режиме ведомого(и правда ли необходимо писать такую сложную программу?).
Спасибо!
Есть вопрос по реализации ШИМ-регулирован ия. У меня собрана схема термометра на 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++;
}
Буду благодарен ,если укажете на мою ошибку.
Спасибо!
digits_PB - массив описания символов, выводимых на индикатор
digit_out - массив индексов выводимых символов
cur_dig - индекс текущего разряда
Записать выражение так:
Код:
PORTD = ~(cursor[cur_dig]);
Спасибо, попробую. А можете объяснить в чем разница между данными выражениями?
Ну тогда надо исходник смотреть полностью
Куда выслать?
Написал в почту...
Добрый день!
Антон, Вы смотрели мой исходник?