Introdução
Este artigo faz parte da série de artigos que apresenta Bibliotecas de funções e rotinas padronizadas em linguagem C para MCS-51, que podem facilmente ser adaptadas para outros microcontroladores. Para a melhor compreensão do que será desenvolvido aqui, é recomendável que você leia os artigos anteriores dessa série e esse sobre displays de LED de 7 seguimentos.
A filosofia utilizada para o desenvolvimento das rotinas para os displays de 7 segmentos é a mesma utilizada nas rotinas apresentadas no artigo Sistemas Operacionais de Tempo Real – Timers. Cria-se um temporizador virtual por software, que é baseado na interrupção de um timer de hardware definindo assim a frequência com a qual os displays são atualizados. Deve-se escolher uma frequência de atualização alta o suficiente para que não se tenha a desagradável sensação de que o display está “piscando”. Em geral essa frequência é da ordem de 20 a 30 vezes por segundo para cada display. A transferência de dados entre o programa principal e a rotina de interrupção é feita através de um vetor dedicado cujo conteúdo é o que deve ser apresentado no display e de eventuais mecanismos para controlar o acesso a esse vetor.
A biblioteca permite que se opere com algumas soluções de hardware distintas, tais como linhas de seleção multiplexadas, ou então separadas, dependendo do tipo de solução e compromissos desenvolvidos no projeto. Também é ilustrado como se pode de maneira simples codificar as letras e símbolos a serem mostrados nos displays.
Arquitetura de Hardware
Para que se possa desenvolver rotinas de programação padronizadas para os displays de 7 segmentos e cuja adaptação ao hardware seja simples é necessário definir arbitrariamente e fixar alguns modelos físicos para as conexões. A mais óbvia é a alocação de um port inteiro de 8 bits para gerar os segmentos e o ponto decimal dos displays. Para recordar como que é uma interface com um display de 7 segmentos, veja um exemplo na Figura 1.
Acionamento dos segmentos de LED
A atribuição dos segmentos de LED aos pinos do Port foi arbitrária. Por questão de simplicidade eles foram conectados em sequência numa mesma porta: bits 0123456 nos segmentos ABCDEFG e o ponto decimal foi colocado no bit 7. Pode-se observar na Figura 2 uma ilustração simplificada disso. Essa solução de hardware para conexão dos segmentos do display ao microcontrolador é pressuposta nas rotinas em linguagem C da biblioteca, que serão apresentadas mais adiante, agrupadas como ROTINA DOS DISPLAYS DE 7 SEGMENTOS I e ROTINA DOS DISPLAYS DE 7 SEGMENTOS II. O que diferencia os dois grupos, são as formas de seleção dos displays.
Nessa atribuição arbitrária, pode-se formar os dígitos e alguns caracteres alfabéticos conforme ilustrado na Tabela 1.
Tabela 1: Códigos para acender os segmentos de acordo com o dígito ou caractere desejado.
// SEGMENTOS: g f e | d c b a Código Hexadecimal //----------------------------------------- // Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00 // 0 0 1 1 | 1 1 1 1 = 0x3f // 1 0 0 0 | 0 1 1 0 = 0x06 // 2 1 0 1 | 1 0 1 1 = 0x5b // 3 1 0 0 | 1 1 1 1 = 0x4f // 4 1 1 0 | 0 1 1 0 = 0x66 // 5 1 1 0 | 1 1 0 1 = 0x6d // 6 1 1 1 | 1 1 0 0 = 0x7c // 7 0 0 0 | 0 1 1 1 = 0x07 // 8 1 1 1 | 1 1 1 1 = 0x7f // 9 1 1 0 | 0 1 1 1 = 0x67 // // P 1 1 1 | 0 0 1 1 = 0x73 // r 1 0 1 | 0 0 0 0 = 0x50 // E 1 1 1 | 1 0 0 1 = 0x79 // F 1 1 1 | 0 0 0 1 = 0x71 // i 0 0 0 | 0 1 0 0 = 0x04
Outra forma de conectar o display de LED de 7 segmentos a um microprocessador é por meio de um decodificador BCD para 7 segmentos, tais como por exemplo os circuitos integrados 74LS49, ou então o 74HC4511. Na Figura 3 é ilustrada uma solução de hardware onde é utilizado um decodificador desse tipo, com a atribuição arbitrária dos pinos e ports utilizada nas rotinas padronizadas em linguagem C agrupadas como ROTINA DOS DISPLAYS DE 7 SEGMENTOS III.
Formas de seleção dos displays
Nas rotinas padronizadas foram previstas duas formas de seleção dos displays: linhas de seleção separadas, utilizadas nos grupos de rotinas ROTINA DOS DISPLAYS DE 7 SEGMENTOS II e III, e linhas de seleção codificadas, utilizadas na ROTINA DOS DISPLAYS DE 7 SEGMENTOS I.
Linhas Separadas
As linhas de seleção separadas são alocadas num segundo Port. Uma definição cuidadosa das variáveis do programa padronizado, permite que se selecione em que bit do Port começam essas linhas. Essas linhas de seleção devem estar necessariamente alocadas em ordem crescente e na sequência dos pinos do Port. Veja por exemplo o código em C mostrado na Figura 4.
No exemplo da Figura 4 foram definidos que são utilizados 3 displays, os segmentos conectados ao Port 3, as linhas de seleção ao Port 1 e será utilizado o ponto decimal. Na parte específica relacionado com as linhas de seleção, é definido que as linhas estão alocadas nos pinos 5, 6 e 7 do Port 1 (0xE0).
#define NUMERO_DE_LINHAS_DO_DISPLAY 3 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
//#define TEM_PONTO_DECIMAL 1
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xE0 // Filtra os bits dos sinais de seleção dos displays
.
.
.
.
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY 0x20 // Define posição do primeiro bit do conjunto de seleção
Figura 4: Definições relacionadas com as linhas de seleção
Linhas codificadas
Nessa forma de seleção dos displays são utilizados poucos pinos do port para codificar o número do display a ser ativado em código binário. Ou seja, 1 bit seleciona até dois displays, 2 bits quatro etc. A intenção foi a de economizar pinos nos Ports do microcontrolador, especialmente se o número de displays for grande. É necessária a utilização de um decodificador / multiplexador na saída do Port, conforme ilustrado na Figura 5. Um exemplo desse tipo de componente é o 74LS139.
Rotinas padronizadas em C para os displays de 7 segmentos
O código em linguagem C para os displays segue o mesmo padrão do desenvolvido para os timers em Sistemas Operacionais de Tempo Real – Timers. Tem um conjunto de definições e variáveis, uma parte a ser inserida na rotina de interrupção do timer, de forma que quando o timer virtual do display terminar a contagem, é executado um trecho curto de código para atualizar os displays. No programa principal são inseridas algumas inicializações.
Definições e variáveis
A seguir os códigos exemplo referentes às definições e configurações correspondentes às arquiteturas descritas anteriormente. Esse trecho de código é inserido tipicamente num arquivo do tipo .h do programa.
ROTINA DOS DISPLAYS DE 7 SEGMENTOS I (Sinais de seleção codificados)
//*******************************************************************************
//* *
//* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES *
//* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) *
//* (Resp. Engº Puhlmann) * *
//* *
//* (31/05/2000 - Rev. 16/05/2005) *
//* *
//*******************************************************************************
//===============================================================================
//
// DEFINIÇÕES DOS DÍGITOS UTILIZADOS NA ROTINA DOS DISPLAYS DE 7 SEGMENTOS I
// Linhas de seleção codificadas
// (Necessita de decodificador na saída para acinar as linhas de seleção)
//
// Entradas : -
// Saídas : - ucFilaDeSaidaDosDisplays[] - Vetor de saídas para os displays
// - ucIndiceDisplayAtivo - ìndica o display que está ativo
//
// Deve-se definir um port dedicado a isso, configurado da seguinte maneira:
//
// PORT.Bit - Segmento do Display
//
// Px.0 - a
// Px.1 - b
// Px.2 - c
// Px.3 - d
// Px.4 - e
// Px.5 - f
// Px.6 - g
// Px.7 - DP
//
// Composição dos números decimais utilizando a especificação acima:
//
// a
// --------
// | |
// f | | b
// | g |
// ---------
// | |
// e | | c
// | d |
// -------
//
// SEGMENTOS: g f e | d c b a Código Hexadecimal
//-----------------------------------------
// Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00
// 0 0 1 1 | 1 1 1 1 = 0x3f
// 1 0 0 0 | 0 1 1 0 = 0x06
// 2 1 0 1 | 1 0 1 1 = 0x5b
// 3 1 0 0 | 1 1 1 1 = 0x4f
// 4 1 1 0 | 0 1 1 0 = 0x66
// 5 1 1 0 | 1 1 0 1 = 0x6d
// 6 1 1 1 | 1 1 0 0 = 0x7c
// 7 0 0 0 | 0 1 1 1 = 0x07
// 8 1 1 1 | 1 1 1 1 = 0x7f
// 9 1 1 0 | 0 1 1 1 = 0x67
//
// P 1 1 1 | 0 0 1 1 = 0x73
// r 1 0 1 | 0 0 0 0 = 0x50
// E 1 1 1 | 1 0 0 1 = 0x79
// F 1 1 1 | 0 0 0 1 = 0x71
// i 0 0 0 | 0 1 0 0 = 0x04
//
//===== =====
//#define DISPLAY_INVERTIDO 1
#define TEM_PONTO_DECIMAL 1
#ifdef DISPLAY_INVERTIDO
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_INV_DISPLAY 0xff
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#else
#define MASCARA_INV_DISPLAY 0x7f
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#endif
#else
#define MASCARA_INV_DISPLAY 0x00
#ifdef TEM_PONTO_DECIMAL
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0xff
#else
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x7f
#endif
#endif
#define DISPLAY_7_SEG_APAGADO 0x00 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ZERO 0x3f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_UM 0x06 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_DOIS 0x5b ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_TRES 0x4f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_QUATRO 0x66 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_CINCO 0x6d ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SEIS 0x7c ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SETE 0x07 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_OITO 0x7f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_NOVE 0x67 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_PE 0x73 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ERRE 0x50 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_E 0x79 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_EFE 0x71 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_I 0x04 ^ MASCARA_INV_DISPLAY
#define NUMERO_DE_DISPLAYS 0x03
#define FREQ_DE_INTERRUP_TIMER_DISPLAYS 90.0 // Hz
// ************* Precisa revisar essas linhas !!!!! *****
//#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 * NUMERO_DE_DISPLAYS)/ FREQ_DE_INTERRUP_TIMER_DISPLAYS)+ 0.5) // Original, mas acho que está errado
#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 )/ (FREQ_DE_INTERRUP_TIMER_DISPLAYS * NUMERO_DE_DISPLAYS))+ 0.5)
//********************************************************
#define NUMERO_DE_LINHAS_DO_DISPLAY 2 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xe0 // Filtra os bits dos sinais de seleção dos displays
#define INCREMENTO_PARA_O_CONTADOR_DE_SELECAO_DISP 0x40 // Define o "mais 1" no contador
//#define SAIDAS_INVERTIDAS_SEL_DISP 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS_SEL_DISP
#define MASCARA_DE_INVERSAO_SEL_DISP MASCARA_DA_SELECAO_DE_DISPLAY
#else
#define MASCARA_DE_INVERSAO_SEL_DISP 0x00
#endif
volatile int unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS;
unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS] = { DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD};
unsigned char ucIndiceDisplayAtivo = 0;
unsigned char ucContadorDeSelecaoDisplay = 0;
//-------- Definição das variáveis globais ---------------
code unsigned char ucTabelaDeDisplayDeSeteSegmentos[10] = { DISPLAY_7_SEG_ZERO,
DISPLAY_7_SEG_UM,
DISPLAY_7_SEG_DOIS,
DISPLAY_7_SEG_TRES,
DISPLAY_7_SEG_QUATRO,
DISPLAY_7_SEG_CINCO,
DISPLAY_7_SEG_SEIS,
DISPLAY_7_SEG_SETE,
DISPLAY_7_SEG_OITO,
DISPLAY_7_SEG_NOVE};
//----------------- Fim dos parâmetros e variáveis dos displays do tipo I ---------
ROTINA DOS DISPLAYS DE 7 SEGMENTOS II (Sinais de seleção separados)
//*******************************************************************************
//* *
//* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES *
//* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) *
//* (Resp. Engº Puhlmann) * *
//* *
//* (31/05/2000 - Rev. 16/05/2005) *
//* *
//*******************************************************************************
//===============================================================================
//
// DEFINIÇÕES DOS DÍGITOS UTILIZADOS NA ROTINA DOS DISPLAYS DE 7 SEGMENTOS II
// Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : -
// Saídas : - ucFilaDeSaidaDosDisplays[] - Vetor de saídas para os displays
// - ucIndiceDisplayAtivo - ìndica o display que está ativo
//
// Deve-se definir um port dedicado a isso, configurado da seguinte maneira:
//
// PORT.Bit - Segmento do Display
//
// Px.0 - a
// Px.1 - b
// Px.2 - c
// Px.3 - d
// Px.4 - e
// Px.5 - f
// Px.6 - g
// Px.7 - DP
//
// Composição dos números decimais utilizando a especificação acima:
//
// a
// --------
// | |
// f | | b
// | g |
// ---------
// | |
// e | | c
// | d |
// -------
//
// SEGMENTOS: g f e | d c b a Código Hexadecimal
//-----------------------------------------
// Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00
// 0 0 1 1 | 1 1 1 1 = 0x3f
// 1 0 0 0 | 0 1 1 0 = 0x06
// 2 1 0 1 | 1 0 1 1 = 0x5b
// 3 1 0 0 | 1 1 1 1 = 0x4f
// 4 1 1 0 | 0 1 1 0 = 0x66
// 5 1 1 0 | 1 1 0 1 = 0x6d
// 6 1 1 1 | 1 1 0 0 = 0x7c
// 7 0 0 0 | 0 1 1 1 = 0x07
// 8 1 1 1 | 1 1 1 1 = 0x7f
// 9 1 1 0 | 0 1 1 1 = 0x67
//
// P 1 1 1 | 0 0 1 1 = 0x73
// r 1 0 1 | 0 0 0 0 = 0x50
// E 1 1 1 | 1 0 0 1 = 0x79
// F 1 1 1 | 0 0 0 1 = 0x71
// i 0 0 0 | 0 1 0 0 = 0x04
//
//===== =====
//#define DISPLAY_INVERTIDO 1
#define TEM_PONTO_DECIMAL 1
#ifdef DISPLAY_INVERTIDO
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_INV_DISPLAY 0xff
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#else
#define MASCARA_INV_DISPLAY 0x7f
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#endif
#else
#define MASCARA_INV_DISPLAY 0x00
#ifdef TEM_PONTO_DECIMAL
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0xff
#else
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x7f
#endif
#endif
#define DISPLAY_7_SEG_APAGADO 0x00 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ZERO 0x3f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_UM 0x06 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_DOIS 0x5b ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_TRES 0x4f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_QUATRO 0x66 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_CINCO 0x6d ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SEIS 0x7c ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SETE 0x07 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_OITO 0x7f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_NOVE 0x67 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_PE 0x73 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ERRE 0x50 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_E 0x79 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_EFE 0x71 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_I 0x04 ^ MASCARA_INV_DISPLAY
#define NUMERO_DE_DISPLAYS 0x04
#define FREQ_DE_INTERRUP_TIMER_DISPLAYS 120.0 // Hz
// ************* Precisa revisar essas linhas !!!!! *****
//#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 * NUMERO_DE_DISPLAYS)/ FREQ_DE_INTERRUP_TIMER_DISPLAYS)+ 0.5) // Original, mas acho que está errado
#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 )/ (FREQ_DE_INTERRUP_TIMER_DISPLAYS * NUMERO_DE_DISPLAYS))+ 0.5)
//********************************************************
#define NUMERO_DE_LINHAS_DO_DISPLAY 4 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xf0 // Filtra os bits dos sinais de seleção dos displays
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 1 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY 0x10 // Define posição do primeiro bit do conjunto de seleção
//#define SAIDAS_INVERTIDAS_SEL_DISP 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS_SEL_DISP
#define MASCARA_DE_INVERSAO_SEL_DISP MASCARA_DA_SELECAO_DE_DISPLAY
#else
#define MASCARA_DE_INVERSAO_SEL_DISP 0x00
#endif
volatile int unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS;
unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS] = { DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD};
unsigned char ucIndiceDisplayAtivo = 0;
unsigned char ucContadorDeSelecaoDisplay = 0;
//-------- Definição das variáveis globais ---------------
code unsigned char ucTabelaDeDisplayDeSeteSegmentos[10] = { DISPLAY_7_SEG_ZERO,
DISPLAY_7_SEG_UM,
DISPLAY_7_SEG_DOIS,
DISPLAY_7_SEG_TRES,
DISPLAY_7_SEG_QUATRO,
DISPLAY_7_SEG_CINCO,
DISPLAY_7_SEG_SEIS,
DISPLAY_7_SEG_SETE,
DISPLAY_7_SEG_OITO,
DISPLAY_7_SEG_NOVE};
//----------------- Fim dos parâmetros e variáveis dos displays do tipo II ---------
ROTINA DOS DISPLAYS DE 7 SEGMENTOS III (Utiliza decodificador BCD para 7 segmentos e sinais de seleção separados)
//*******************************************************************************
//* *
//* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES *
//* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) *
//* (Resp. Engº Puhlmann) * *
//* *
//* (31/05/2000 - Rev. 16/05/2005) *
//* *
//*******************************************************************************
//===============================================================================
//
// DEFINIÇÕES DOS DÍGITOS UTILIZADOS NA ROTINA DOS DISPLAYS DE 7 SEGMENTOS III
// Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : -
// Saídas : - ucFilaDeSaidaDosDisplays[] - Vetor de saídas para os displays
// - ucIndiceDisplayAtivo - ìndica o display que está ativo
//
// Deve-se definir um port dedicado a isso, configurado da seguinte maneira:
//
// PORT.Bit - Segmento do Display
//
// Px.0 - a
// Px.1 - b
// Px.2 - c
// Px.3 - d
// Px.4 - e
// Px.5 - f
// Px.6 - g
// Px.7 - DP
//
// Composição dos números decimais utilizando a especificação acima:
//
// a
// --------
// | |
// f | | b
// | g |
// ---------
// | |
// e | | c
// | d |
// -------
//
// SEGMENTOS: g f e | d c b a Código Hexadecimal
//-----------------------------------------
// Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00
// 0 0 1 1 | 1 1 1 1 = 0x3f
// 1 0 0 0 | 0 1 1 0 = 0x06
// 2 1 0 1 | 1 0 1 1 = 0x5b
// 3 1 0 0 | 1 1 1 1 = 0x4f
// 4 1 1 0 | 0 1 1 0 = 0x66
// 5 1 1 0 | 1 1 0 1 = 0x6d
// 6 1 1 1 | 1 1 0 0 = 0x7c
// 7 0 0 0 | 0 1 1 1 = 0x07
// 8 1 1 1 | 1 1 1 1 = 0x7f
// 9 1 1 0 | 0 1 1 1 = 0x67
//
// P 1 1 1 | 0 0 1 1 = 0x73
// r 1 0 1 | 0 0 0 0 = 0x50
// E 1 1 1 | 1 0 0 1 = 0x79
// F 1 1 1 | 0 0 0 1 = 0x71
// i 0 0 0 | 0 1 0 0 = 0x04
//
//===== =====
//#define DISPLAY_INVERTIDO 1
#define TEM_PONTO_DECIMAL 1
#ifdef DISPLAY_INVERTIDO
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_INV_DISPLAY 0xff
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#else
#define MASCARA_INV_DISPLAY 0x7f
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#endif
#else
#define MASCARA_INV_DISPLAY 0x00
#ifdef TEM_PONTO_DECIMAL
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0xff
#else
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x7f
#endif
#endif
#define DISPLAY_7_SEG_APAGADO 0x00 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ZERO 0x3f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_UM 0x06 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_DOIS 0x5b ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_TRES 0x4f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_QUATRO 0x66 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_CINCO 0x6d ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SEIS 0x7c ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SETE 0x07 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_OITO 0x7f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_NOVE 0x67 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_PE 0x73 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ERRE 0x50 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_E 0x79 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_EFE 0x71 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_I 0x04 ^ MASCARA_INV_DISPLAY
#define NUMERO_DE_DISPLAYS 0x04
#define FREQ_DE_INTERRUP_TIMER_DISPLAYS 90.0 // Hz
// ************* Precisa revisar essas linhas !!!!! *****
//#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 * NUMERO_DE_DISPLAYS)/ FREQ_DE_INTERRUP_TIMER_DISPLAYS)+ 0.5) // Original, mas acho que está errado
#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 )/ (FREQ_DE_INTERRUP_TIMER_DISPLAYS * NUMERO_DE_DISPLAYS))+ 0.5)
//********************************************************
#define NUMERO_DE_LINHAS_DO_DISPLAY 4 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xf0 // Filtra os bits dos sinais de seleção dos displays
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 1 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY 0x10 // Define posição do primeiro bit do conjunto de seleção
//#define SAIDAS_INVERTIDAS_SEL_DISP 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS_SEL_DISP
#define MASCARA_DE_INVERSAO_SEL_DISP MASCARA_DA_SELECAO_DE_DISPLAY
#else
#define MASCARA_DE_INVERSAO_SEL_DISP 0x00
#endif
volatile int unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS;
unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS] = { DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD};
unsigned char ucIndiceDisplayAtivo = 0;
unsigned char ucContadorDeSelecaoDisplay = 0;
//-------- Definição das variáveis globais ---------------
code unsigned char ucTabelaDeDisplayDeSeteSegmentos[10] = { DISPLAY_7_SEG_ZERO,
DISPLAY_7_SEG_UM,
DISPLAY_7_SEG_DOIS,
DISPLAY_7_SEG_TRES,
DISPLAY_7_SEG_QUATRO,
DISPLAY_7_SEG_CINCO,
DISPLAY_7_SEG_SEIS,
DISPLAY_7_SEG_SETE,
DISPLAY_7_SEG_OITO,
DISPLAY_7_SEG_NOVE};
//----------------- Fim dos parâmetros e variáveis dos displays do tipo III ---------
São utilizadas algumas convenções com relação às definições e uso das constantes. Em todo o programa, 1 quer dizer ligado ou ativo e 0 desligado ou inativo. Este trecho de código é na sua essência o configurador dos displays, amarrando as particularidades do hardware com o software. O código é muito parecido com o da primeira rotina. Existem apenas algumas pequenas diferenças O que deve ser configurado:
- #define DISPLAY_INVERTIDO: Essa linha deve ser colocada como comentário, se acaso as saídas para os segmentos não necessitam de inversão lógica;
- #define TEM_PONTO_DECIMAL: Essa linha deve ser colocada como comentário, se acaso não for utilizado o ponto decimal;
- #define NUMERO_DE_DISPLAYS: Nessa linha deverá ser colocado o número de displays multiplexados que existem no hardware;
- #define FREQ_DE_INTERRUP_TIMER_DISPLAYS: é a frequência de atualização de um display multiplicada pelo número de displays;
- #define NUMERO_DE_LINHAS_DO_DISPLAY: Aqui é definido o número de linhas de seleção que serão utilizadas;
-
#define PORTA_DE_SAIDA_DOS_DISPLAYS: Aqui deve ser definida em que Port estão conectados os segmentos dos displays;
-
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS: Aqui é definido em que Port estão conectadas as linhas de seleção;
-
#define MASCARA_DA_SELECAO_DE_DISPLAY: Aqui é definida a posição e abrangência das linhas de seleção;
-
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY: Define a posição do bit de seleção que será deslocado em anel para selecionar os diversos displays;
-
#define SAIDAS_INVERTIDAS_SEL_DISP : Colocar essa linha como comentário, se acaso as linhas de seleção não precisarem ser invertidas;
- unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS]: É necessário dimensionar e inicializar corretamente esse vetor.
As demais variáveis e constantes são calculadas automaticamente pelo programa.
Código a ser inserido na rotina de interrupção do Timer 0
Os trechos de códigos a seguir implementam um temporizador virtual para o acionamento dos displays. Sempre que o contador termina a contagem, é desligado o display atual e ativado o próximo, tomando as providências de hardware para isso.
ROTINA DOS DISPLAYS DE 7 SEGMENTOS I (Sinais de seleção codificados)
//**********************************************************************************************
//************* Trecho a ser inserido na rotina de interrupção do Timer (exemplo) **************
//************* **************
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
.
.
.
//*******************************************************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - DISPLAYS I ***
//****** ***
if(--unTimerDosDisplays == 0)
{
unsigned char ucAux;
unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS; // Reinicializa temporizador
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= DISPLAY_7_SEG_APAGADO; // Apaga o display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
if(ucIndiceDisplayAtivo == NUMERO_DE_DISPLAYS) // Testa se atualizou o último display
{
ucIndiceDisplayAtivo = 0; // Reinicializa o Indice do display
ucContadorDeSelecaoDisplay = 0; // Reinicializa o contador de seleção
}
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
ucAux |= (ucContadorDeSelecaoDisplay ^ MASCARA_DE_INVERSAO_SEL_DISP);
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux;
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= ucFilaDeSaidaDosDisplays[ucIndiceDisplayAtivo++]; // Lê o valor a ser apresentado no display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
ucContadorDeSelecaoDisplay += INCREMENTO_PARA_O_CONTADOR_DE_SELECAO_DISP; // Incrementa o contador
}
//************** Fim da Rotina Displays I ***************************************
.
.
.
}
ROTINA DOS DISPLAYS DE 7 SEGMENTOS II (Sinais de seleção separados)
Para examinar o código, “clique” na barra a seguir.
//**********************************************************************************************
//************* Trecho a ser inserido na rotina de interrupção do Timer (exemplo) **************
//************* **************
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
.
.
.
//********************************************************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - DISPLAYS II ***
//****** ***
if(--unTimerDosDisplays == 0)
{
unsigned char ucAux;
unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS; // Reinicializa temporizador
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= DISPLAY_7_SEG_APAGADO; // Apaga o display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
if(ucIndiceDisplayAtivo == NUMERO_DE_DISPLAYS) // Testa se atualizou o último display
{
ucIndiceDisplayAtivo = 0; // Reinicializa o Indice do display
ucContadorDeSelecaoDisplay = 0; // Reinicializa o contador de seleção
}
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY << ucIndiceDisplayAtivo) ^ MASCARA_DE_INVERSAO_SEL_DISP);
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux;
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= ucFilaDeSaidaDosDisplays[ucIndiceDisplayAtivo++]; // Lê o valor a ser apresentado no display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
}
//************** Fim da Rotina Displays II ***************************************
.
.
.
}
ROTINA DOS DISPLAYS DE 7 SEGMENTOS III (Utiliza decodificador BDC para 7 segmmentos e sinais de seleção separados)
Para examinar o código, “clique” na barra a seguir.
//**********************************************************************************************
//************* Trecho a ser inserido na rotina de interrupção do Timer (exemplo) **************
//************* **************
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
.
.
.
//*********************************************************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - DISPLAYS III ***
//****** ***
if(--unTimerDosDisplays == 0)
{
unsigned char ucAux;
unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS; // Reinicializa temporizador
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= DISPLAY_BCD_APAGADO; // Apaga o display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
ucAux = PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS & ~MASCARA_DO_STROBE; // Le e apaga o strobe
PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS = ucAux;
PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS = ucAux | MASCARA_DO_STROBE; // Gera pulso e carrega o dado
if(ucIndiceDisplayAtivo == NUMERO_DE_DISPLAYS) // Testa se atualizou o último display
{
ucIndiceDisplayAtivo = 0; // Reinicializa o Indice do display
ucContadorDeSelecaoDisplay = 0; // Reinicializa o contador de seleção
}
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY << ucIndiceDisplayAtivo) ^ MASCARA_DE_INVERSAO_SEL_DISP);
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux;
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= ucFilaDeSaidaDosDisplays[ucIndiceDisplayAtivo++]; // Lê o valor a ser apresentado no display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
ucAux = PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS & ~MASCARA_DO_STROBE; // Le e apaga o strobe
PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS = ucAux;
PORTA_DA_LINHA_DE_STROBE_DOS_DISPLAYS = ucAux | MASCARA_DO_STROBE; // Gera pulso e carrega o dado
}
//************** Fim da Rotina Displays III *************************************** .
.
.
}
Código a ser inserido nas inicializações do programa principal
O trecho de código a seguir inicializa uma variável auxiliar e desativa as linhas de seleção dos displays.
.
.
.
// Trecho a ser inserido na rotina principal - nas inicializações
// Inicializa Displays
{
unsigned char ucAux;
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux; // Inicializa bits de selecao
}
.
.
.
Como transferir dígitos para os displays?
Da forma como foi projetado esse programa, o gerenciamento do display de 7 segmentos propriamente dito é totalmente automático e transparente para o programa principal. Você só precisa converter o dígito para o código, usando-o como índice na tabela ucTabelaDeDisplayDeSeteSegmentos[dígito] e transferí-lo para a fila de saída dos displays (ucFilaDeSaidaDosDisplays[]) na posição correspondente. Veja no exemplo a seguir.
EXEMPLO
Nesse programa foi aproveitado o mesmo programa desenvolvido para a demonstração de operação dos timers de tempo real, acrescidos os trechos relativos aos displays. Aqui será programado o Timer 0 no modo 2 de operação, com o microcontrolador operando com um clock de 4 MHz e uma frequência de interrupção de 1,5 kHz. Foram definidos 3 displays de 7 segmentos do tipo II com a seleção separada, e atualizados a cada 30 contagens cada um. Confira o código no quadro abaixo.
//***************************************************************************
//** **
//** ROTINA EXEMPLO PARA TESTE DO USO DO TIMER E DISPLAYS **
//** PARA SISTEMAS OPERACIONAIS DE TEMPO REAL **
//** (Resp. Eng. Puhlmann) **
//** **
//** (22/11/2014 - rev. 22/11/2014) **
//** **
//***************************************************************************
#include <AT89S53.H> // AT89S8253
//----------------------------
#define DESLIGA 0
#define LIGA 1
// Define-se aqui a freqüência de clock da CPU
#define FREQUENCIA_DO_CLOCK_DA_CPU 4.0e+6 // Hz ----> 4 MHz por exemplo
// Define o pre-scaler
#define FATOR_DE_ESCALA_CLOCK_TIMER 12.0f // Divide o clock por 12
// Define a freqüência de interrupção do timer
#define FREQUENCIA_DE_INTERRUPCAO_TIMER_0 1.5e+3 // Hz ---- > 1,5 kHz
//--------------
// Definição de variáveis referentes aos timers
//------- Timer 0 ----------
#define MASCARA_CT_0 0xfb
// Definição utilizada apenas quando o timer estiver no modo 2 de operação
// se acaso nao for usado no modo 2, comentar a linha abaixo
// Define o Timer 0 como operando no modo 2
// A conta desse preset tem que dar um número de 0 a 255, senão não funciona...
#define PRESET_TIMER_0_MODO_2 (255 - (unsigned char)((FREQUENCIA_DO_CLOCK_DA_CPU/(FATOR_DE_ESCALA_CLOCK_TIMER * FREQUENCIA_DE_INTERRUPCAO_TIMER_0))+ 0.5))
#define MASCARA_DO_MODO_DO_TIMER_0 0xfc
#define MODO_0_TIMER_0 0x00
#define MODO_1_TIMER_0 0x01
#define MODO_2_TIMER_0 0x02
#define MODO_3_TIMER_0 0x03
//=============== Fim da definição dos parâmetros do Timer 0 ===========
#define PRESET_TIMER_DE_SOFTWARE 10 // Define o valor inicial do temporizador de software
int nTimerDeSoftware = PRESET_TIMER_DE_SOFTWARE; // Inicializa o temporizador de software com 10
bit bFlagTimerDeSoftware = DESLIGA; // Inicializa a sinalização de término de contagem
//===============================================================================
//
// DEFINIÇÕES DOS DÍGITOS UTILIZADOS NA ROTINA DOS DISPLAYS DE 7 SEGMENTOS II
// Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : -
// Saídas : - ucFilaDeSaidaDosDisplays[] - Vetor de saídas para os displays
// - ucIndiceDisplayAtivo - ìndica o display que está ativo
//
// Deve-se definir um port dedicado a isso, configurado da seguinte maneira:
//
// PORT.Bit - Segmento do Display
//
// Px.0 - a
// Px.1 - b
// Px.2 - c
// Px.3 - d
// Px.4 - e
// Px.5 - f
// Px.6 - g
// Px.7 - DP
//
// Composição dos números decimais utilizando a especificação acima:
//
// a
// --------
// | |
// f | | b
// | g |
// ---------
// | |
// e | | c
// | d |
// -------
//
// SEGMENTOS: g f e | d c b a Código Hexadecimal
//-----------------------------------------
// Dígito: apagado 0 0 0 | 0 0 0 0 = 0x00
// 0 0 1 1 | 1 1 1 1 = 0x3f
// 1 0 0 0 | 0 1 1 0 = 0x06
// 2 1 0 1 | 1 0 1 1 = 0x5b
// 3 1 0 0 | 1 1 1 1 = 0x4f
// 4 1 1 0 | 0 1 1 0 = 0x66
// 5 1 1 0 | 1 1 0 1 = 0x6d
// 6 1 1 1 | 1 1 0 0 = 0x7c
// 7 0 0 0 | 0 1 1 1 = 0x07
// 8 1 1 1 | 1 1 1 1 = 0x7f
// 9 1 1 0 | 0 1 1 1 = 0x67
//
// P 1 1 1 | 0 0 1 1 = 0x73
// r 1 0 1 | 0 0 0 0 = 0x50
// E 1 1 1 | 1 0 0 1 = 0x79
// F 1 1 1 | 0 0 0 1 = 0x71
// i 0 0 0 | 0 1 0 0 = 0x04
//
//===== =====
//#define DISPLAY_INVERTIDO 1
#define TEM_PONTO_DECIMAL 1
#ifdef DISPLAY_INVERTIDO
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_INV_DISPLAY 0xff
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#else
#define MASCARA_INV_DISPLAY 0x7f
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x00
#endif
#else
#define MASCARA_INV_DISPLAY 0x00
#ifdef TEM_PONTO_DECIMAL
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0xff
#else
#define DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD MASCARA_INV_DISPLAY
#define LIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD 0x7f
#endif
#endif
#define DISPLAY_7_SEG_APAGADO 0x00 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ZERO 0x3f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_UM 0x06 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_DOIS 0x5b ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_TRES 0x4f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_QUATRO 0x66 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_CINCO 0x6d ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SEIS 0x7c ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_SETE 0x07 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_OITO 0x7f ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_NOVE 0x67 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_PE 0x73 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_ERRE 0x50 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_E 0x79 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_EFE 0x71 ^ MASCARA_INV_DISPLAY
#define DISPLAY_7_SEG_I 0x04 ^ MASCARA_INV_DISPLAY
#define NUMERO_DE_DISPLAYS 0x03
#define FREQ_DE_INTERRUP_TIMER_DISPLAYS 90.0 // Hz
// ************* Precisa revisar essas linhas !!!!! *****
//#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 * NUMERO_DE_DISPLAYS)/ FREQ_DE_INTERRUP_TIMER_DISPLAYS)+ 0.5) // Original, mas acho que está errado
#define PRESET_TEMPORIZADOR_DOS_DISPLAYS (unsigned int)(((FREQUENCIA_DE_INTERRUPCAO_TIMER_0 )/ (FREQ_DE_INTERRUP_TIMER_DISPLAYS * NUMERO_DE_DISPLAYS))+ 0.5)
//********************************************************
#define NUMERO_DE_LINHAS_DO_DISPLAY 3 // Número de linhas de seleção
#define PORTA_DE_SAIDA_DOS_DISPLAYS P3 // Define o Port onde estão conectados os displays
#define PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS P1 // Define o port onde estão conectadas as linhas de seleção
#ifdef TEM_PONTO_DECIMAL
#define MASCARA_DOS_DISPLAYS 0xff // Filtra os bits de saída do port dos Displays incluindo o do Ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x08
#else
#define MASCARA_DOS_DISPLAYS 0x7f // Filtra os bits de saída do port dos Displays, sem o ponto decimal
#define FIM_DO_TESTE_DISPLAY 0x07
#endif
#define LIGA_PONTO_DECIMAL 0x80
#define MASCARA_DA_SELECAO_DE_DISPLAY 0xE0 // Filtra os bits dos sinais de seleção dos displays
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 1 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY 0x20 // Define posição do primeiro bit do conjunto de seleção
//#define SAIDAS_INVERTIDAS_SEL_DISP 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS_SEL_DISP
#define MASCARA_DE_INVERSAO_SEL_DISP MASCARA_DA_SELECAO_DE_DISPLAY
#else
#define MASCARA_DE_INVERSAO_SEL_DISP 0x00
#endif
volatile int unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS;
unsigned char ucFilaDeSaidaDosDisplays[NUMERO_DE_DISPLAYS] = { DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD,
DESLIGA_TODOS_SEGMENTOS_DISP_7_SEG_E_PD};
unsigned char ucIndiceDisplayAtivo = 0;
unsigned char ucContadorDeSelecaoDisplay = 0;
//-------- Definição das variáveis globais ---------------
code unsigned char ucTabelaDeDisplayDeSeteSegmentos[10] = { DISPLAY_7_SEG_ZERO,
DISPLAY_7_SEG_UM,
DISPLAY_7_SEG_DOIS,
DISPLAY_7_SEG_TRES,
DISPLAY_7_SEG_QUATRO,
DISPLAY_7_SEG_CINCO,
DISPLAY_7_SEG_SEIS,
DISPLAY_7_SEG_SETE,
DISPLAY_7_SEG_OITO,
DISPLAY_7_SEG_NOVE};
//----------------- Fim dos parâmetros e variáveis dos displays do tipo II ---------
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
nTimerDeSoftware--; // Decrementa o temporizador de software
if(nTimerDeSoftware == 0)
{
bFlagTimerDeSoftware = LIGA; // Sinaliza que terminou a temporização de software
nTimerDeSoftware = PRESET_TIMER_DE_SOFTWARE; // Reinicializa o temporizador
}
//********************************************************************************
//****** Trecho a ser inserido na rotina de interrupção do timer - DISPLAYS II ***
//****** ***
if(--unTimerDosDisplays == 0)
{
unsigned char ucAux;
unTimerDosDisplays = PRESET_TEMPORIZADOR_DOS_DISPLAYS; // Reinicializa temporizador
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= DISPLAY_7_SEG_APAGADO; // Apaga o display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
if(ucIndiceDisplayAtivo == NUMERO_DE_DISPLAYS) // Testa se atualizou o último display
{
ucIndiceDisplayAtivo = 0; // Reinicializa o Indice do display
ucContadorDeSelecaoDisplay = 0; // Reinicializa o contador de seleção
}
ucAux = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO_DISPLAY << ucIndiceDisplayAtivo) ^ MASCARA_DE_INVERSAO_SEL_DISP);
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux;
ucAux = PORTA_DE_SAIDA_DOS_DISPLAYS & ~MASCARA_DOS_DISPLAYS; // Lê o conteúdo do port, para salvar o bit do Ponto decinal, se for o caso
ucAux |= ucFilaDeSaidaDosDisplays[ucIndiceDisplayAtivo++]; // Lê o valor a ser apresentado no display
PORTA_DE_SAIDA_DOS_DISPLAYS = ucAux; // Atualiza a saída
}
//************** Fim da Rotina Displays II ************************************
}
//================================================================================
//
// PROGRAMA PRINCIPAL
void main(void)
{
int nContadorSemFuncao = 0;
//********************* Inicializações *************
//************************ TIMER 0 ********************************************
// Inicializa os bits do timer 0
TR0 = DESLIGA;
TMOD &= MASCARA_CT_0; // Seleciona função de "timer"
T2CON = 0x00;
TR2 = DESLIGA;
T2MOD &= 0x0f; // Seleciona função de "timer"
TH0 = PRESET_TIMER_0_MODO_2; // Inicializa o preset do contador
TL0 = PRESET_TIMER_0_MODO_2;
TMOD &= MASCARA_DO_MODO_DO_TIMER_0; // Seta M1 e M0 para timer 0 - modo 2
TMOD |= MODO_2_TIMER_0;
//******
//********************* Inicializa Displays para demostração ******
ucFilaDeSaidaDosDisplays[0] = DISPLAY_7_SEG_UM;
ucFilaDeSaidaDosDisplays[1] = DISPLAY_7_SEG_DOIS;
ucFilaDeSaidaDosDisplays[2] = DISPLAY_7_SEG_TRES;
{
unsigned char ucAux1;
ucAux1 = ~MASCARA_DA_SELECAO_DE_DISPLAY; // Inverte os bits da mascara
ucAux1 &= PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DOS_DISPLAYS = ucAux1; // Inicializa bits de selecao
}
//******
//********** Fim das Inicializações ******************
//********************************************************************************
//
// TIMER 0
//
// Este bloco deve ser colocado no programa principal, para iniciar a operação do timer
TR0 = LIGA; // Liga o timer
ET0 = LIGA; // Libera a interrupção do timer
EA = LIGA; // Habilita as interrupções
//****
for(;;) // Loop principal
{
nContadorSemFuncao++; // Soma um no contador. Essa instrução foi inserida aqui apenas para não dar a impressão que a simulação está parada
if(bFlagTimerDeSoftware == LIGA) // Verifica se terminou a temporização por software (10 vezes a interrupção do Timer 0)
{
P1_1 = ~P1_1; // Inverte o bit 1 da porta 1 - ;
bFlagTimerDeSoftware = DESLIGA; // Desliga a sinalização de término
}
}
}
Se você quiser simular o programa exemplo acima e explorá-lo um pouco mais, siga os seguintes passos:
- Instale o KEIL C51 μVision;
- Baixe o arquivo Testa_Display.zip;
- Instale os arquivos de Testa_Display.zip num diretório para testes;
- Navegue até o diretório de testes e acione o arquivo Testa_Display.uvproj (2 “clickes”) (Figura 6).
Nesse instante deverá abrir o programa Keil e a tela deverá ser a retratada na Figura 7. Esse painel permite navegar no código em C, editá-lo e compilá-lo. Não é necessário fazer isso para realizar a simulação, mas você poderá posteriormente alterar alguns parâmetros do código e testar o programa com as novas alterações.
A seguir coloque o programa no modo de Debug (Figura 8, Figura 9 e Figura10) para que se possa iniciar a simulação.
Antes de entrar no modo de depuração, aparece na tela a mensagem da Figura 9. Clique no “OK” para continuar.
Observe que já estão destacados os painéis de monitoramento do Timer 0, do Port 1, cujo bit 1 deverá ser alternado a cada término de contagem do temporizador de software do teste do timer e cujos bits 5, 6 e 7 são usados como linhas de seleção dos 3 displays, e o Port 3, onde aparecem os códigos de cada dígito de 7 segmentos. Nessa simulação, aparecem os números 1, 2 e 3 respectivamente nos displays 0, 1 e 2.
É possível utilizar as teclas de função F11 para executar o programa passo a passo ou F5 entrar em execução. Recomendo que você inicie a simulação utilizando o passo a passo para poder observar detalhadamente como que funciona o mecanismo de operação desse programa.
Resumo
Neste artigo técnico foram apresentadas algumas possíveis soluções padronizadas para acionamento de displays de 7 segmentos com auxílio de um timer de hardware. Essas soluções constituem uma biblioteca padronizada para a utilização dos displays de 7 segmentos em microcontroladores da família MCS-51, codificada em C, para ser compilada no programa da Keil. Depois foi desenvolvido um programa exemplo para que você possa simular o que foi apresentado e observar os detalhes dessa implementação.
Este artigo é o terceiro da série de artigos que abordam algumas funções comuns em projetos de sistemas embarcados de tempo real. Confira os demais artigos (em breve).
Bibliotecas de funções e rotinas padronizadas em linguagem C para MCS-51
- Timers – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso dos Timers do MCS-51;
- Displays de 7 segmentos (este artigo) – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso em displays de 7 segmentos;
- Teclados Matriciais – É apresentada uma biblioteca desenvolvida em linguagem C para varredura, leitura, debounce, identificação da tecla acionada e desvio para a rotina de tratamento. A forma como as rotinas foram escritas, permitem sua fácil reutilização em outros projetos;
- Periféricos – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso de alguns periféricos, tais como conversores A/D, sensor de temperatura e memórias seriais.









Muito legal esta biblioteca.
Um ponto interessante que não poderia passar batido é a importância de se apagar o display antes de escrever um novo dígito. Esta prática evita o surgimento de fantasmas no display.
Caro Rafael,
muito bem observado destacar a necessidade de se apagar o display antes de acionar o próximo.De fato neste artigo não abordei explicitamente esse assunto, mas no artigo anterior, “Displays de LED de 7 segmentos”, foi comentado isso no tópico “Como acionar um arranjo com vários displays de 7 segmentos?”
De qualquer maneira, no código em C acima é realizada essa operação.
Agradeço o seu comentário.
Abraço