Динамическая индикация

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

Семисегментный индикатор

Семисегментный индикатор

На примере обновлённого вольтметра сделанном на микроконтроллере PIC16F676 в этой статье расскажу о динамической индикации семисегментных индикаторов.

Для динамической индикации понадобиться таймер, в котором будет постоянно крутиться функция вывода значения на дисплей. Обычно микроконтроллеры содержат несколько таймеров, под динамическую индикацию подойдёт самый простой из них. В примере использован первый 8 битный таймер TMR0.

Для начала таймер нужно сконфигурировать:

OPTION=0b00000000; 	//	Настройка TMR0
INTCON=0b10100000; 	//	Прерывания разрешены и только от TMR0

Для работы понадобятся следующие глобальные переменные:

unsigned char v1,v2,v3, vn;

Первые три отвечают за значения соответствующих сегментов, а последняя за номер горящего сегмента в данный момент (динамическая индикация).

Обработчик прерываний занят обработкой таймера, который постоянно по очереди сменяет сегменты.

void interrupt isr(void)
{
	if(T0IF) 		//	при переполнение TMR0
	{
		vn++;		//	переключение сегмента
		switch(vn) 	//	выбор сегмента
		{
			case 1:seg7(v1,1);break;
			case 2:seg7(v2,2);break;
			case 3:{seg7(v3,3);vn=0;}break;
		}
		TMR0=100; 	//	установка таймера не на начало
		T0IF=0; 	//	сбрасываем флаг
	}
}

Для вывода значения символа семисегментного индикатора с общим анодом используется следующая функция:

void seg7(unsigned char c, unsigned char s)
{
	unsigned char t=0; 	// g сегмент
	PORTA=0b00000000; 	// сброс порта A
	switch(s) 		//	выбор анода
	{
		case 1 :{t=1;}break;
		case 2 :{t=2;}break;
		case 3 :{t=32;}break;
	}
	switch(c%10) 	 	//	выбор сегментов (катодов)
	{
//                      			00bfaed	g
		case 0 : {PORTC=0b00000000;PORTA=t+4;}break;
		case 1 : {PORTC=0b00011110;PORTA=t+4;}break;
		case 2 : {PORTC=0b00010001;PORTA=t;}break;
		case 3 : {PORTC=0b00010100;PORTA=t;}break;
		case 4 : {PORTC=0b00001110;PORTA=t;}break;
		case 5 : {PORTC=0b00100100;PORTA=t;}break;
		case 6 : {PORTC=0b00100000;PORTA=t;}break;
		case 7 : {PORTC=0b00010110;PORTA=t+4;}break;
		case 8 : {PORTC=0b00000000;PORTA=t;}break;
		case 9 : {PORTC=0b00000100;PORTA=t;}break;
	}
}

У PIC16F676 неполный порты вывода, поэтому все сегменты на порт C не влезли, сегмент g пришлось разместить на порте A, а точку приходиться подключать схематически к одному из общих анодов через инвертор. Схема такого подключения выглядит следующим образом:

Схема вольтметра на PIC16F676

Схема вольтметра

Если использовать другой микроконтроллер, у которого есть хотя бы один полноценный порт, например PIC16F628A

Схема цифрового термометра

Схема цифрового термометра

функция примет следующий вид:

void seg7(unsigned char c, unsigned char s)
{
	unsigned char t=0; 
	switch(c%10) 	 	//	выбор сегментов (катодов)
	{
//                    	gcPdeafb
		case 0 : {t=0b10100000;}break;
		case 1 : {t=0b10111110;}break;
		case 2 : {t=0b01100010;}break;
		case 3 : {t=0b00101010;}break;
		case 4 : {t=0b00111100;}break;
		case 5 : {t=0b00101001;}break;
		case 6 : {t=0b00100001;}break;
		case 7 : {t=0b10111010;}break;
		case 8 : {t=0b00100000;}break;
		case 9 : {t=0b00101000;}break;
	}
	PORTA=0b00000000; 	// сброс порта A
	switch(s) 		//	выбор анода
	{
		case 1 :{ PORTA =4;}break;
		case 2 :{ PORTA =8;}break;
		case 3 :{ PORTA =64;}break;
	}
	PORTB=t;
	if(c>9)
	{
		PORTB=t&0b11011111;
	}
}

Точка у символа загорается, если с больше 9, значение на индикатор выводиться всё равно будет, т.е. будет выводиться только младший разряд, остальное всё отбрасываться (например: 19, 39 = 9. ).

В основной функции (main) МК спокойно выполняет свою работу, для вывода новой информации на дисплей ему достаточно изменить значения переменных v1,v2,v3, но изменять их следует одновременно, что бы избежать неразберихи на индикаторе.

Скачать исходник вольтметра

10 комментариев на « Динамическая индикация»

  1. tim пишет 01.12.2011 в 16:10 #

    можно включить в схему сдвиговый регистр и тогда существенно сократится количество ножек микроконтроллера. Тогда запустить многосегментные индикаторы будет не проблема даже на мелком мк
    http://avrproject.ru/publ/kak_podkljuchit/podkljuchenie_semisegmentnogo_indikatora_cherez_74hc595_staticheskaja_indikacija/2-1-0-57

  2. Alex_EXE пишет 01.12.2011 в 21:43 #

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

  3. Сергей пишет 19.11.2012 в 14:35 #

    Здравствуйте! Хорошая статья, хотел применить Ваш способ для индикации, но не могу скомпилировать код в MikroC, ругается на обработчик прерываний на строку void interrupt isr(void) с ошибкой: ‘;’ expected but isr found и internal error. Видимо не нравится вызов подпрограммы в обработчике прерываний. Намекните чайнику, что я делаю не так. Контроллер 16F887

  4. Alex_EXE пишет 20.11.2012 в 01:21 #

    у меня код для компилятора HI-TECH приведен.
    Попробуйте:

    void interrupt (void)
    {
       if(INTCON.T0IF)
       {
       }
    }

  5. Сергей пишет 20.11.2012 в 10:28 #

    Пробовал 🙁

    void interrupt (void)
    {
    if(INTCON.T0IF)
    {
    vn++;
    switch(vn)
    {
    case 1 : seg7(v1,1); break;
    case 2 : seg7(v2,2); break;
    case 3 : seg7(v3,3); break;
    case 4 : seg7(v4,4); break;
    case 5 : seg7(v5,5); break;
    case 6 : {seg7(v6,6);vn=0;} break;
    }
    }
    }

    На строке case 1 : seg7(v1,1); break; ошибка
    Undeclared identifier [seg7] in expression

  6. Сергей пишет 20.11.2012 в 12:25 #

    Не знаетели Вы (автор) о применении

    #pragma funcall

    Пытаюсь запихать в разные места кода, результата нет

    unsigned char v1, v2, v3, v4, v5, v6, vn, seg, dig;
    unsigned char CONFIG, rst, data;

    void interrupt (void)
    {

    if(INTCON.T0IF)
    {
    vn++;
    switch(vn)
    {
    case 1 : seg7(v1,1); break;
    case 2 : seg7(v2,2); break;
    case 3 : seg7(v3,3); break;
    case 4 : seg7(v4,4); break;
    case 5 : seg7(v5,5); break;
    case 6 : {seg7(v6,6);vn=0;} break;
    }
    }
    }

    #pragma funcall interrupt seg7
    void seg7(unsigned char c,unsigned char s)
    {
    switch(c)
    {
    case 0 : {PORTD=0b00111111;} break;
    case 1 : {PORTD=0b00000110;} break;
    case 2 : {PORTD=0b01011011;} break;
    case 3 : {PORTD=0b01001111;} break;
    case 4 : {PORTD=0b01100110;} break;
    case 5 : {PORTD=0b01101101;} break;
    case 6 : {PORTD=0b01111101;} break;
    case 7 : {PORTD=0b00000111;} break;
    case 8 : {PORTD=0b01111111;} break;
    case 9 : {PORTD=0b01101111;} break;
    }

    switch(s)
    {
    case 1 : {PORTE=0b00000100; PORTA=0b00000000;} break;
    case 2 : {PORTE=0b00000010; PORTA=0b00000000;} break;
    case 3 : {PORTE=0b00000001; PORTA=0b00000000;} break;
    case 4 : {PORTA=0b00100000; PORTE=0b00000000;} break;
    case 5 : {PORTA=0b01000000; PORTE=0b00000000;} break;
    case 6 : {PORTA=0b10000000; PORTE=0b00000000;} break;
    {
    }

  7. Alex_EXE пишет 21.11.2012 в 01:28 #

    О тонкостях работы с компилятором MikroC не знаю, не работал с ним, по этому больше нечего по нему подсказать не могу.

  8. Василий пишет 06.01.2013 в 20:00 #

    С динамической индикацией есть один нюансик. Ну, он спецефический, конечно. Не все с ним столкнутся. Вот только что запилил динамическую индикацию на плате с 13 индикаторами. Всё классно. Но не получается програмно сделать изменение интенсивности яркости свечения индикаторов, с помощью ШИМ. При динамической индикации, уменьшая ширину импульса, вылазит такая проблема, что начинают светиться неиспользуемые сегменты. Видимо, динамическая индикация и ШИМ регулирования яркости не очень совместимы. Ну, или нужно искать какие-то схемные решения. Просто програмным способом сделать не получается. Микроконтроллер PIC16F1947.

  9. Batir пишет 20.01.2016 в 16:15 #

    DOBROE VREMYA SUTOK . RAZOBRALSYA S SEMISIGMENTNOY INDIKASIEY . ESLI VKLUCHIT DIODI MARISOY NAPRIMER 8X3 .HOTEL VKLUCHIT DIODI PO OCHEREDI . IZMENIL PROGRAMMU ,PROVERIL NA PROTEUSE RABOTAET NE PRAVILNO . MOJETE POSOVETOVAT V CHEM OSHIBKA;

    switch(c) // (c%10) // выбор сегментов (катодов)
    {

    case 1 : {t=0b00000001;}break;
    case 2 : {t=0b00000011;}break;
    case 3 : {t=0b00000111;}break;
    case 4 : {t=0b00001111;}break;
    case 5 : {t=0b00011111;}break;
    case 6 : {t=0b00111111;}break;
    case 7 : {t=0b01111111; }break;
    case 8 : {t=0b11111111; }break;
    //case 9 : {t=0x11111111;}break;
    }

    D=D++;
    v1=D/5;
    if(v1>8){
    v1=8;
    v2=D/5;
    v2=v2-8;
    }
    if(v2>8)
    {
    v2=8;
    v3=D/5;
    v3=D-16;
    }
    if(v3>8)
    {D=0;PORTB=0;v1=0;v2=0;v3=0;}

  10. Arhimed пишет 23.06.2018 в 01:16 #

    tim прав, можно и регистр 595-й применить. Только схему он выбрал неправильную, со СТАТИЧЕСКОЙ индикацией. Я делал на регистре именно ДИНАМИЧЕСКУЮ, вгоняя в регистр сегменты, благодаря чему вместо семи (восьми, если с точкой) ног используются всего три. А если для переключения общих выводов индикатора применить счётчик-дешифратор (или кольцевой счётчик), то и разряды можно переключать по одной ноге или вообще по «защёлке» данных в 595… 🙂

Комментарии RSS

Оставьте отзыв