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.


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?
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
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.
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!