Multiplexação de displays Led de 7 segmentos com o PIC18F4520

Neste post vamos demonstrar como implementar um hardware e firmware para multiplexar displays led de 7 segmentos utilizando um PIC18F4520.

Primeiramente é importante entender a motivação de se utilizar a multiplexação de displays e ela é simples e óbvia: economia de pinos!

Um display led possui 7 segmentos (8 se incluirmos o ponto decimal), assim, sem a multiplexação, seriam necessários 4×7=28 pinos para comandar os 4 displays (ou 32 se utilizarmos os pontos decimais)! Cada display adicional irá necessitar de outros 7 ou 8 pinos!

Como pinos de microcontrolador não dão em árvores (desculpem a analogia ridícula) utilizamos a multiplexação como forma de economizar os valiosos pinos do MCU.

Esta técnica interliga os segmentos de todos os displays, ativando separadamente o pino de catodo comum (CC) ou anodo comum (CA), de acordo com o tipo de display utilizado. Um software rodando no microcontrolador encarrega-se de varrer o display (calma, não estamos falando de limpá-los, mas sim de ativar cada display separadamente ao longo do tempo).

Graças a uma característica no olho humano, chamada de persistência retiniana, a nossa retina armazena momentaneamente uma imagem por uma fração de segundo. Acendendo e apagando cada display rapidamente, aproveitamos esta característica para criar a ilusão de que todos os displays estão acesos, quando de fato, num determinado momento, apenas um deles está!

Desta forma, graças a multiplexação, os mesmos quatro displays led irão necessitar de apenas 7 ou 8 pinos para os segmentos, mais 1 pino por display para o catodo (ou anodo) comum!

Obviamente que a redução na utilização de pinos do MCU tem um preço (na verdade mais de um):

  • Redução do brilho dos displays (já que um cada display permanece aceso por uma fração de tempo);
  • Aumento da complexidade do software rodando no microcontrolador;
  • Ocupação adicional da CPU do microcontrolador (já que o mesmo deve realizar toda a sequência de varredura dos displays, repetindo-a no mínimo 24 vezes por segundo).

A aplicação descrita neste post demonstra a utilização de uma máquina de estados finitos (ou FSM) para o controle da multiplexação dos displays. O esquemático mostra um display de 4 dígitos conectado diretamente ao PIC, numa aplicação real, os pinos CC (catodo comum) deveriam ser conectados a transistores acionados pelo MCU (já que os pinos CC drenam mais corrente que os pinos do MCU real podem fornecer).

Basicamente temos quatro estados: DISP1, DISP2, DISP3 e DISP4, encarregados de apresentar no display a informação, além de dois estados: WAIT1 e WAIT2 encarregados de controlar a temporização do display ativo. Maiores explicações nos comentários dentro do código.

A principal vantagem de se utilizar uma FSM para a multiplexação dos displays é a redução no tempo de processamento da mesma: mesmo com a temporização dos displays, o tempo de execução da função display() é extremamente rápido, liberando a CPU para executar o restante do programa principal (que no nosso caso não faz absolutamente nada!).

#include <p18f4520.h>
#include <stdio.h>

#pragma config OSC = HS, WDT = OFF, MCLRE = OFF
#pragma config DEBUG = OFF, LVP = OFF, PWRT = ON

#define DISP_SEG	LATB
#define DISP_CC1	LATAbits.LATA0
#define DISP_CC2	LATAbits.LATA1
#define DISP_CC3	LATAbits.LATA2
#define DISP_CC4	LATAbits.LATA3

// disp_data[0] contém o valor a ser apresentado no primeiro display (unidades)
// disp_data[1] contém o valor a ser apresentado no segundo display (dezenas)
// disp_data[2] contém o valor a ser apresentado no terceiro display (centenas)
// disp_data[3] contém o valor a ser apresentado no terceiro display (milhares)
unsigned char disp_data[4];

enum efsm_display {DISP1, DISP2, DISP3, DISP4, WAIT1, WAIT2};

#pragma romdata
const rom char disp7seg[16]=
{	// 0   1     2     3     4     5     6     7
	0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07,
	// 8   9     A     b     c     d     E     F
	0x7F, 0x6F, 0x77, 0x7C, 0x58, 0x5E, 0x79, 0x71
};

// Esta função é a máquina de estados do display
// O estado atual é indicado pela variável pos
// Os estados DISP1 a DISP4 controlam os displays
// Os estados WAIT1 e WAIT2 fazem a temporização
// A variável next_pos indica o novo estado após a temporização
void display(void)
{
	// Variáveis estáticas são preservadas após a saída da função!
	static unsigned char contador;
	static enum efsm_display pos, next_pos;
	switch (pos)
	{
		case DISP1:	// primeiro display (unidades)
			DISP_SEG = disp7seg[disp_data[0]];
			DISP_CC1 = 1;
			DISP_CC2 = 1;
			DISP_CC3 = 1;
			DISP_CC4 = 0;
			pos = WAIT1;	// muda para o estado de temporização
			next_pos = DISP2;
			break;
		case DISP2:	// segundo display (dezenas)
			DISP_SEG = disp7seg[disp_data[1]];
			DISP_CC1 = 1;
			DISP_CC2 = 1;
			DISP_CC3 = 0;
			DISP_CC4 = 1;
			pos = WAIT1;	// muda para o estado de temporização
			next_pos = DISP3;
			break;
		case DISP3:	// terceiro display (centenas)
			DISP_SEG = disp7seg[disp_data[2]];
			DISP_CC1 = 1;
			DISP_CC2 = 0;
			DISP_CC3 = 1;
			DISP_CC4 = 1;
			pos = WAIT1;	// muda para o estado de temporização
			next_pos = DISP4;
			break;
		case DISP4:	// terceiro display (milhares)
			DISP_SEG = disp7seg[disp_data[3]];
			DISP_CC1 = 0;
			DISP_CC2 = 1;
			DISP_CC3 = 1;
			DISP_CC4 = 1;
			pos = WAIT1;	// muda para o estado de temporização
			next_pos = DISP1;
			break;
		case WAIT1: // inicialização do temporizador
			contador = 40;	// carrega o contador de temporização
			pos = WAIT2;
			break;
		case WAIT2: // temporizador
			// o contador é decrementado a cada chamada, quanto ele atinge zero
			// a maquina de estados avança para o próximo estado indicado por
			// next_pos
			if (contador) contador--; else
			{
				pos = next_pos;
				DISP_SEG = 0;	// desliga os segmentos
				DISP_CC1 = 1;
				DISP_CC2 = 1;
				DISP_CC3 = 1;
				DISP_CC4 = 1;
			}
			break;
		default:
			pos = DISP1;
	}
}

void init(void)
{
	ADCON1 = 0x0F; 			// desliga entradas analógicas
	// todos os pinos das portas C, D e E como saídas (exceto RD0)
	TRISB = 0;
	TRISC = 0;
	TRISD = 0;
	TRISE = 0;
	TRISA = 0;
}

void main(void)
{
	init();
	disp_data[0] = 4;
	disp_data[1] = 3;
	disp_data[2] = 2;
	disp_data[3] = 1;
	while(1)
	{
		display();
	}
}

O arquivo acima está disponível para download aqui.

4 thoughts on “Multiplexação de displays Led de 7 segmentos com o PIC18F4520

  1. Bom dia! Estou tentando utilizar este código para um trabalho que vou fazer e ao compilar o código no MPLAB (XC8), dá erro na linha 19.

     

    debug/default/newmain.c:294: warning: (983) storage class redeclared for “WAIT1”
    debug/default/newmain.c:19: error: (984) type redeclared
    debug/default/newmain.c:19: error: (1098) conflicting declarations for variable “WAIT1” (debug/default/newmain.c:19)

     

    você consegue me ajudar?

  2. Mudei de lugar a linha e parou de retornar este erro. Mas agora é outro erro. 🙁

    Na linha 21…

     

    debug/default/newmain.c:21: warning: (335) unknown pragma “rom”
    debug/default/newmain.c:21: warning: (374) missing basic type; int assumed
    debug/default/newmain.c:21: error: (314) “;” expected

    1. Bom dia,

      Olha, o código foi escrito para o compilador C18, não sei se este é o compilador que você está utilizando. Provavelmente o erro sobre o símbolo WAIT1 é decorrente do compilador que você está utilizando já possuir um símbolo interno com este nome. A solução seria renomear todas as ocorrências deste símbolo para outro (por exemplo, ESPERA1).

      O outro erro que você relatou é também decorrente de incompatibilidade entre o seu compilador e o C18 utilizado no projeto. Neste caso você terá de verificar como funciona a declaração de símbolos para ser armazenados em memória de programa. Eventualmente você pode simplesmente remover o modificador ROM e declarar aquele array simplesmente como const char (ou const int se isso não funcionar). Sugiro que você verifique detalhadamente a documentação do compilador que você está utilizando. Infelizmente não utilizo o MPLAB XC8.

  3. Muito bom! Me ajudou MUITO! Era isso mesmo. Mudei para const char somente e rodou 😉
    Estou engatinhando ainda nessa área. Sua ajuda foi de grande valia! Obrigada!

Leave a Reply