Muitas pessoas tem perguntado como multiplexar display de 7 segmentos num PIC qualquer.
Procurando minimizar ao máximo o uso de componentes externos, apresento essa possibilidade, num voltímetro bem elementar:
Os resistores podem ser bem menores, pois efetivamente só um led está aceso por vez. Usei esse valor por atender minha necessidade.
O código fonte:
//////////////////////////////////////////////////////////////////////////////
// Programa exemplo de multiplexação de 3 Displays de 7 segmentos //
// Tensão aplicada ao pino RA0 é apresentado no display //
//////////////////////////////////////////////////////////////////////////////
#include <16f873.h>
#DEVICE ICD=TRUE
#DEVICE ADC=10
#fuses HS,NOWDT,NOLVP,NOBROWNOUT,PUT,NOPROTECT,NOCPD //16F873.
//#zero_ram
#use delay(clock=4000000,restart_wdt)
#include <Minhas_Def.h> //Para poder usar PORTA, PORTB, nop e etc...
#use fast_io(a)
#use fast_io(b)
#use fast_io(c)
//----------------------------------------------------------------------------
//Variáveis
int IndiceDisplay=0x03,IndiceSegmento=0x80,PonteiroSegmento,Display,
Decimos=0,Unidades=0,Centesimos=0,PonteiroDisplay=0,Temp;
int16 _TempoTmr1=0xFC41,LeituraAdc; //Uma inta a cada 1 ms a 4MHZ. (Div_by_1)
byte AdcTemp[5];
//-gfedcba
byte const TabDisplay[]={
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
0b01110111, // A
0b01111100, // b
0b00111001, // C
0b01011110, // D
0b01111001, // E
0b01110001, // F
0b00111000, // L
0b01110110};// H
main(){
*PORTA=0;
*PORTB=0;
*PORTC=0;
set_tris_a(0b11110001);
set_tris_b(0b11111111);
set_tris_c(0b00000000);
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG);
set_adc_channel(0);
setup_counters(RTCC_INTERNAL,WDT_2304MS);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
set_timer1(_TempoTmr1);
enable_interrupts (INT_TIMER1);
enable_interrupts (GLOBAL);
/*#############################################################################
## LOOP PRINCIPAL DO PROGRAMA ##
#############################################################################*/
while(TRUE){
restart_wdt();
delay_ms(50);
LeituraAdc=read_adc(); //Le ADC em 10 bits.
LeituraAdc=LeituraAdc*49; //Aproximação grosseira.
sprintf(AdcTemp,"%05lu",LeituraAdc); //Converte numero para string ASCII.
Unidades=(AdcTemp[0])-0x30; //Converte string em suas subtrings.
Decimos=(AdcTemp[1])-0x30;
Centesimos=(AdcTemp[2])-0x30;
}//while (true).
}//main().
//////////////////////////////////////////////////////////////////////////////
// INTERRUPCOES //
//////////////////////////////////////////////////////////////////////////////
#int_timer1
TrataTimer1(){
set_timer1(_TempoTmr1); //Uma int a cada 1ms (63073 dec pra 1ms).
delay_cycles(1); //Para completar o tempo certo.
//----------------------------------------------------------------------------
// Atualiza Displays.
if(PonteiroSegmento==0){
*PORTC=0; //Evita misturar o primeiro segmento entre displays.
PonteiroSegmento=0x80; //Recomeca a varredura no bit 7.
switch (PonteiroDisplay) //Alterna Display
{
case 0:
output_high(pin_a2);
output_high(pin_a3);
Display=Centesimos;
output_low(pin_a1);
break;
case 1:
output_high(pin_a1);
output_high(pin_a3);
Display=Decimos;
output_low(pin_a2);
break;
case 2:
output_high(pin_a1);
output_high(pin_a2);
Display=Unidades;
output_low(pin_a3);
break;
default:
PonteiroDisplay=0;
}//switch (PonteiroDisplay).
if(++PonteiroDisplay >=3)PonteiroDisplay=0;
}
Temp= TabDisplay[Display]; //Le tabela dos led' correspondentes ao numero.
if(!bit_test(*PORTA,3))bit_set(Temp,7); //Força o led decimal do terceiro segmento a ligar.
Temp=PonteiroSegmento & Temp; //Mascara do segmento atual.
*PORTC=Temp; //Escreve na saída.
PonteiroSegmento=PonteiroSegmento>>1; //Aponta para o proximo segmento.
//----------------------------------------------------------------------------
}//TrataTimer1().
//=============================================
Prof. Francisco:
Trazendo de volta ao topo do forum este post (um dos melhores já publicados no ASM51, na minha opinião - Parabéns, Kraft !
Peço ao Kraft para, por favor, comentar em detalhes estas duas linhas:
if(!bit_test(*PORTA,3))bit_set(Temp,7); Temp=PonteiroSegmento & Temp;
Me parece que elas sao responsáveis por colocar o ponto decimal no lugar correto, mas nao consegui entender direito como funcionam: tem uma operação AND entre o PonteiroSegmento e a variável Temp.
Para que serve ?
Obrigado
//=============================================
KrafT:
Hehehhehe... Nada disso... Todo post é importante... Mas vamos lá:
PonteiroSegmento é um numero que só pode assumir valores 0b00000001,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000.
Como eu faco AND com o valor armazenado na tabela do LCD (retornado em temp), apenas um bit do valor retornado da tabela é transferido pra os displays por vez, fazendo com que só um led esteja aceso por vez.
Note que só passo para outro Display quando tiver rotacioando PonteiroSegmento oito vezes, ou seja varrido todos os leds, ligados ou não.
Mas como quero ligar o Ponto decimal do display (ligado ao pino RA3), eu testo se esse pino está baixo (ou seja é a vez daquele display), e forco o o bit 7, que esta ligado naquale led (RC7) a ficar alto. Na oitava passagem da varredura o PonteiroSegmento vai ser = 0x80 e jogar esse bit para o PORTC, ligando o LED.
Isso por que não há como armazenar o Ponto decimal em tabela, que ia ficar muito esquisito.
Resumindo:
if(!bit_test(*PORTA,3)) ==> É a vez do Display Unidades?
bit_set(Temp,7);==> Então, alem do numero que já está em temp, seta o ponto decimal.
Temp=PonteiroSegmento & Temp; ==> Jogo na másacra que impede ligar mais de um Led por vez.
Ah, entre outras coisas podia em vez de testar RA0, eu podia testar o PonteiroDisplay e ver se é zero. Também poderia usar enum para gerar os modos, emfim dá pra melhorar isso daí...
É isso.
//=============================================
Prof. Francisco:
Ah bom, agora entendi Kraft...
Veja, se cada interrupção do TMR1 demora cerca de 1 ms (no seu projeto ai em cima) e você varre um LED (segmento) do display em cada interrupção, e no total são 21 segmentos (contando os 3 displays juntos), então uma varredura completa de todo o mostrador demora 21 ms, está correto isso ?
*****************************************************************
Interessante: eu uso uma técnica um pouco diferente: mando o byte que vai acender (ou não...) os 7 segmentos de uma vez só !
Daí, eu só faço a varredura do display (no seu caso, eu só teria a variável PonteiroDisplay), eu não usaria o "PonteiroSegmento".
Não varro os segmentos de cada display "um por um".
O programa fica mais rápido e o firmware mais simples.
Quais as vantagens de você varrer um segmento de cada vez ? (desculpe pela minha ignorancia em perguntar !)
*******************************************************
Em tempo: Quando eu disse que achava que este era um dos Posts mais importantes que já apareceram aqui no ASM51, não foi desmerecendo os outros posts: é que esse é na verdade um artigo completo: traz um projeto (até com esquema elétrico) que ensina como fazer multiplexação em displays a leds, como usar interrupções de Timer, como usar o conversor AD interno do PIC, etc !
Uma joia rara ! parabéns Kraft, vamos tocar aquela ideia!!!!
Abraços
Francisco
//=============================================
Aquino:
Kraft essa tua tecnica de varrer segmento por segmento, seria pra economizar os resistores limitadores de cada led e tb não necessitar utilizar os transistores para colocar os catados dos displays ao terra?
//=============================================
KrafT:
Quais as vantagens de você varrer um segmento de cada vez ?
Economizar 5 resistores. Só isso e se for um display só, como esse projeto é originalmente (versão comercial), economiza 7 resistores, pois uso o Ponto decimal para indicar outras coisitas... , se não seriam 6 resistores economizados, placa menor, menos trabalho de montagem enfim, economia. Software é só ctrl+c e ctrl+v.
PS: Tá respondido pro Aquino também.
Ah, Francisco, demora +- 8x3ms, pois o ponto decimal é tratado sempre.
//=============================================
Professor:
OK, Kraft.
Ainda acho que é o tipo da economia que não compensa, pois um resistor na Santa Ifigênia custa 1 centavo cada (em quantidade, chega a custar 0,5 centavo). Quanto ao tamanho da placa, um resistor montado em pé ocupa muito pouco espaço.
Mesmo que você usasse 3 transistores BC 548 no catodo de cada display, isso iria encarecer no maximo uns 30 centavos seu projeto (muito pouco significativo, quando comparado com o preço do próprio PIC, algo em torno de R$ 18,00 estou correto ?)
Além do mais, quem quer fazer placa pequena, usa SMD...
O ganho de velocidade mandando o Byte inteiro é muito grande e acho que vale o investimento: Veja, toda sua rotina de atualização completa do mostrador poderia demorar apenas 8 ms, ao invés dos 24 ms originais.
É claro que neste seu projeto, tem velocidade de sobra e o tempo não é crítico. Mas imagine que, dentro do MAIN( ) você ainda tivesse que chamar mais umas 5 ou 6 funções (para, por exemplo, varrer um teclado, modificar o valor de um potênciometro digital, etc) ?
Imagine que este seu Voltimetro fosse instalado numa Fonte de Alimentação totalmente digital (como aquelas fontes excelentes vendidas pela EXSTO:
http://www.exsto.com.br/inside.php?g=produtos&p=fontes
onde o controle de tensão na saida fosse feito também por um potenciometro digital (como os modelos da Xicor, por exemplo) e controlados pelo mesmo PIC que lê a tensao da fonte e mostra no display.
A velocidade seria algo critico, porque você ia querer usar um unico PIC para realizar todas as funçoes, não é mesmo ?
Veja bem, não estou criticando seu projeto, só estou discordando deste pequeno detalhe de economia de resisotres, que a meu ver traz mais problemas e limitações do que beneficios.
//=============================================
KrafT:
Bom...
Vamos falar do projeto que fiz (originalmente) para um cidadao de S B do Campo, que ampliei para 3 displays nesse exemplo (assunto do tópico):
- O projeto usava um display só, um 16F627 e 16 componentes. Se eu usasse mais sete resistores, iria aumentar em 40% o numero de componentes, 40% o trbalho do montador (manual), 40% as chances de erro de montagem, enfim iria sim onerar o produto final, muito além do custo dos resistores.
Imagina montar 1000 placas, seriam 7000 componentes a mais...
E tem uma frase do (sumido do) Guest2003: "Software só se paga uma vez, hardware; sempre."
Nada mais verdadeiro.
Um abraço, & be happy
//=============================================
Clodoaldo:
Gafar:
Será que vc ainda tem essas informações???Tentei acessar o site,mas não existe mais, nem os fontes!!!!
Se puder disponibilizar abradeço,pois tô apanhando com esse problema!!!
abraço!
[quote]
Muitas pessoas tem perguntado como multiplexar display de 7 segmentos num PIC qualquer.
Procurando minimizar ao máximo o uso de componentes externos, apresento essa possibilidade, num voltímetro bem elementar:
Os resistores podem ser bem menores, pois efetivamente só um led está aceso por vez. Usei esse valor por atender minha necessidade.
O código fonte:
...
[quote]
//=============================================
KrafT:
Atualizando a origem da imagem, já que o kit.net morreu faz tempo:
//=============================================
alexandresrocha:
Seria muito abuso pedir para colocar o "Minhas_Def.h" também?
É muito bom aprender com "OS COBRA!", quem sabe futuramente também poderei ajudar este fórum.
Estou gatinhando ainda, comprei livros, primeiros projetos, etc...[/img]