Печать

Подключение семисегментных индикаторов к AVR. Динамическая индикация

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

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

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

На рисунке 1 изображен внешний вид индикатора. Принято каждый сегмент индикатора обозначать латинской буквой: a, b, c, d, e, f, g. Точка обозначается буквой h.

По схеме включения семисегментные индикаторы подразделяются на индикаторы с общим катодом и с общим анодом. Схемы включения приведены на рисунке 2.

Подключить один семисегментный индикатор и управлять им с помощью микроконтроллера процедура несложная. Для этого достаточно сегменты индикатора подключить к порту микроконтроллера через токоограничительные резисторы по 150 Ом. Общий вывод подключить к линии другого порта микроконтроллера. В зависимости от того какую цифру надо вывести, в порт выводим двоичный код этой цифры, ссылаясь на тип подключенного индикатора (с общим анодом или катодом) на общий провод подаем плюс или минус. Для удобства можно сделать таблицу кодов для индикатора. Если подключение такое: PD7-h, PD6-g, PD5-f, PD4-e, PD3-d, PD2-c, PD1-b, PD0-a, то для отображения цифры 1 в порт D нужно вывести такой двоичный код: 0b00000110.

Для отображения цифровых данных одного семисегментного индикатора обычно недостаточно. В таких случаях к микроконтроллеру подключают сразу несколько индикаторов. Однако, из-за отсутствия достаточного количества выводов у микроконтроллера применяют специальные методы. Один из таких методов это динамическая индикация. Режим динамической индикации применяют для построения многоразрядных индикаторов. При таком режиме разряды индикатора работают не одновременно, а по очереди. Переключение разрядов происходит с большой скоростью (50 Гц), из-за этого человеческий глаз не замечает , что индикаторы работают по очереди. Так как у светодиодов очень малая инерционность, сменяющиеся разряды сливаютя в одно изображение. В этом режиме в каждый момент времени работает только один разряд, включаются по очереди начиная с первого заканчивая последним, затем все начинается сначала.

Принцип действия динамической индикации

Сделаем простой секундомер. Отсчет секунд будет производится на четырехразрядном индикаторе (с общим анодом) от 0 до 9999. В нашей программе используем процедуру прерывания по таймеру, т.е. смена разряда индикатора будет происходить каждый раз когда таймер досчитает до конца(до 255). Используем восьмиразрядный таймер/счетчик Т2, он будет работать  в нормальном режиме. Но обычно для реализации динамической индикации используют режим СТС (сброс при совпадении), это режим, при котором частота возникновения прерываний по совпадению значений счетчика таймера и регистра OCR2 определяется содержимым OCR2 и предделителем тактовой частоты таймера. При таком режиме работы таймера можно легко изменять частоту обновления разрядов, записывая в регистр сравнения OCR2 необходимое значение, предварительно расчитанное. Частоту обновления разрядов делают обычно 50Hz или больше, так как у нас 4 разряда, частота обновления будет равна 200Hz. Подсчитаем частоту обновления для нашего примера: тактовая частота равна 8MHz, предделитель сделаем на 8. На вход таймера будут поступать импульсы частотой 1MHz. Тогда таймер будет увеличивать значение каждые 1 микросекунду, переполняться он будет каждые 255*0,000001 = 255 мкс. Частота обновления будет равна 1/255мкс = 3921Hz.

Принцип действия динамической индикации - схема

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

Цифра

PD7 (H)

PD6 (G)

PD5 (F)

PD4 (E)

PD3 (D)

PD2 (C)

PD1 (B)

PD0 (A)

HEX  
0 0 0 1 1 1 1 1 1 0x3F
1 0 0 0 0 0 1 1 0 0x06
2 0 1 0 1 1 0 1 1 0x5B
3 0 1 0 0 1 1 1 1 0x4F
4 0 1 1 0 0 1 1 0 0x66
5 0 1 1 0 1 1 0 1 0x6D
6 0 1 1 1 1 1 0 1 0x7D
7 0 0 0 0 0 1 1 1 0x07
8 0 1 1 1 1 1 1 1 0x7F
9 0 1 1 0 1 1 1 1 0x6F
h 1 0 0 0 0 0 0 0 0x80

Массив с кодами цифр получится такой:

unsigned char SEGMENTE[ ] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

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

switch ( выражение )
{
case значение1:
......
break;
case значение2:
......
break;
......
default:
......
} 

Данный оператор производит выбор по выражению, обычно это число. Если выражение присутствует в значении case, то выполняются команды после case до break, иначе выполняется код после default.

Ниже приведен полный текст программы:

// Подключение семисегментных индикаторов к AVR. Динамическая индикация
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

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

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

// Обработчик прерывания по переполнению таймера 2
ISR(TIMER2_OVF_vect)
{	
PORTD = 0xFF; // Гасим все разряды
PORTB = (1 << segcounter); // Выбираем следующий разряд
	
switch(segcounter)
{	
case 0:
PORTD = ~(SEGMENTE[display % 10000 / 1000]); // Раскладываем число на разряды
break;
case 1:
PORTD = ~(SEGMENTE[display % 1000 / 100]);
break;	
case 2:
PORTD = ~(SEGMENTE[display % 100 / 10]);
break;		
case 3:
PORTD = ~(SEGMENTE[display % 10]);
break;
}
if(segcounter++ > 2) segcounter = 0;	
}
// Главная функция	
int main(void) 
{
DDRB = 0xFF; // Порт B - выход
PORTB = 0x00;
DDRD = 0xFF; // Порт D - выход
PORTD = 0x00;

TCCR2 |= (1 << CS21); // Предделитель на 8 
TIMSK |= (1 << TOIE2); // Разрешение прерывания по таймеру 2

sei(); // Глобально разрешаем прерывания
 
while(1)
{		
display++; // Увеличиваем счет от 0000 до 9999
_delay_ms(100); // Задержка
}
}

Комментарии  

0 #41 Capitan_Black 17.11.2015 15:41
Здравствуйте!
Поясните, пожалуйста, зачем используется ключевое слово volatile?
Сообщить модератору
0 #42 AntonChip 17.11.2015 21:19
Цитирую Capitan_Black:
Здравствуйте!
Поясните, пожалуйста, зачем используется ключевое слово volatile?

Потому что переменная используется как в обработчике прерывания так и в основном цикле
Сообщить модератору
0 #43 Eldar 24.11.2015 12:59
Добрый день.Можно ли при помощи данного алгоритма выводить данные из двух переменных? Например переменная hour в первые два разряда, а min во вторые (допустим часы).
Сообщить модератору
+1 #44 AntonChip 24.11.2015 20:43
Можно, например так:
Код:
switch(segcounter)
{
case 0:
PORTD = ~(SEGMENTE[hour % 100 / 10]);
break;
case 1:
PORTD = ~(SEGMENTE[hour % 10]);
break;
case 2:
PORTD = ~(SEGMENTE[minute % 100 / 10]);
break;
case 3:
PORTD = ~(SEGMENTE[minute % 10]);
break;
}
Сообщить модератору
0 #45 Eldar 25.11.2015 11:43
Спасибо большое. Ваш ответ натолкнул на мысль о том, что в принципе можно создать четыре переменные для каждого разряда, а в основном коде рулить разрядами как угодно. Как вы думаете?
Код:
switch(segcounter)
{
case 0:
PORTD = ~(SEGMENTE[raz1]);
break;
case 1:
PORTD = ~(SEGMENTE[raz2]);
break;
case 2:
PORTD = ~(SEGMENTE[raz3]);
break;
case 3:
PORTD = ~(SEGMENTE[raz4]);
break;
}
Сообщить модератору
+1 #46 Eldar 26.11.2015 06:10
Проверил в протеусе, работает. То, что нужно было. ;-)
Сообщить модератору
0 #47 Никита 05.12.2015 07:05
Здравствуйте, а можно ли как то реализовать управление 2-разрядным индикатором управляя всего одной ножкой разрядами? то есть подключаю как в схеме сегменты , но разряды подключаю один к ножке МК, второй через логику (НЕ) к этой же ножке МК. И соответсвенно при 0 светиться один разряд, при 1 другой разряд. можно ли это программно как то реализовать?
Сообщить модератору
0 #48 AntonChip 05.12.2015 23:10
Цитирую Никита:
Здравствуйте, а можно ли как то реализовать управление 2-разрядным индикатором управляя всего одной ножкой разрядами?

Да, можно, например так:
Код:
ISR(TIMER2_OVF_vect)
{
PORTD = 0xFF;
switch (segcounter)
{
case 0:
PORTD = ~(SEGMENTE[display % 10]);
PORTB |= (1 << PB0);
break;
case 1:
PORTD = ~(SEGMENTE[display % 100 / 10]);
PORTB &= ~(1 << PB0);
break;
}
if ((segcounter++) > 1) segcounter = 0;
}
Сообщить модератору

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