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!