Introdução
Neste artigo vou apresentar uma ferramenta de software que utilizo há muitos anos, o EASYCODE. Garimpei essa ferramenta para melhorar minha produtividade no desenvolvimento de software embarcado para microcontroladores e DSPs. Eu sempre utilizei o método gráfico para planejamento e projeto de software conhecido como diagramas Nassi-Shneiderman e a abordagem Top-Down para o refinamento dos blocos do software. Essa ferramenta de software utiliza esses mesmos diagramas Nassi-Shneiderman para planejar e gerar o software.
Diagramas Nassi-Shneiderman
Os diagramas N-S, como são chamados na forma curta, permitem representar e desenvolver programas de computador ou algoritmos graficamente. Esses diagramas nos permitem visualizar melhor o sistema como um todo e facilitam o desenvolvimento do projeto e a detecção de eventuais falhas no nosso raciocínio. Na Figura 1 está ilustrado um exemplo de como realizar o cálculo de uma média de notas de um aluno. É muito simples, não? Bem mais fácil de ler ou entender do que o código de programação correspondente.
Os diagramas N-S são associados diretamente aos comandos de programação, tais como IF-THEN-ELSE, FOR, DO-WHILE, etc. A tradução é direta, quando o programa estiver totalmente detalhado. Para complementar a introdução, faltou explicar que a abordagem Top-Down é a maneira de se desenvolver um projeto, sistema, algoritmo, etc, partindo do plano mais geral (Top) e descendo aos poucos para os detalhes, (Down) até que esteja tudo definido nos mínimos detalhes. Para conhecer um pouco mais sobre a técnica Top-Down, sugiro que você leia o artigo técnico Técnicas: Top-Down, Mocks e TDD.
A ferramenta de desenvolvimento de software
Voltando ao tema da ferramenta de software, eu identifiquei uma que além de facilitar a geração dos diagramas N-S também traduz esses diagramas para uma linguagem de programação, no meu caso optei pela linguagem C/C++. Essa ferramenta também tem o recurso de realizar a engenharia reversa de programas. Você consegue montar os diagramas e escrever os comentários em cima do programa sem documentação. Os diagramas podem ser desenvolvidos de forma hierárquica. Essa ferramenta, originalmente desenvolvido pela Siemens da Áustria, chamava-se EASYCASE, depois foi rebatizada de EASYCODE.
Não é uma ferramenta gratuita. Ela custa caro, mas vale muito a pena. Se você possui uma empresa, ou trabalha numa instituição que pode investir nesse tipo de ferramenta, vale muito a pena adquirí-la. Você pode fazer um test drive gratuito, se quiser no link[1] abaixo. Se observarmos a Figura 2, podemos notar que a ferramenta vai muito além de facilitar a programação em diagramas estruturados. Ela permite planejar e desenvolver todo um projeto.
Exemplo
Para exemplificar a utilidade e o quanto essa ferramenta é poderosa, vou usar um programa pequeno, que foi apresentado no artigo técnico Sistemas Operacionais de Tempo Real – Teclados Matriciais, como um programa completo para a simulação da operação de timers, displays de 7 segmentos e um teclado matricial em ambientes de tempo real. Esse programa tem apenas em torno de 700 linhas de código. Se impresso em papel, seriam gastos mais de 12 páginas para essa impressão. Veja a seguir.
/* EasyCODE V6.8 03/03/2015 13:29:47 */
/* EasyCODE O
If=horizontal
LevelNumbers=no
LineNumbers=no
Colors=16777215,0,12582912,12632256,0,0,0,16711680,8388736,0,33023,32768,0,0,0,0,0,32768,12632256,255,65280,255,255,16711935
ScreenFont=System,,100,1,-13,0,700,0,0,0,0,0,0,1,2,1,34,96,96
PrinterFont=Courier New,,100,4,-83,0,400,0,0,0,0,0,0,3,2,1,49,600,600
LastLevelId=23 */
/* EasyCODE ( 1
Exemplo de uso do EASYCODE */
//********************************************************************************************
//** **
//** ROTINA EXEMPLO PARA TESTE DO USO DO TIMER, DISPLAYS E TECLADO **
//** PARA SISTEMAS OPERACIONAIS DE TEMPO REAL **
//** (Resp. Eng. Puhlmann) **
//** **
//** (15/12/2014 - rev. 03/03/2015) **
//** **
//********************************************************************************************
#include <AT89S53.H> // AT89S8253
//----------------------------
/* EasyCODE ( 4
Definicoes e inicializacoes referentes ao Timer */
#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
<strong>Sistemas Operacionais de Tempo Real - Teclados Matriciais</strong>
// 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 definicao dos parametros 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
/* EasyCODE ) */
/* EasyCODE ( 5
Definicoes e inicializacoes referentes ao teclado */
/* EasyCODE < */
//===============================================================================
//
// ROTINA DE LEITURA DE MATRIZ DE CONTATOS II - Linhas de seleção separadas
// (Cada linha de seleção corresponde a um bit da Porta)
//
// Entradas : - bFlagDeTemporizacaoDaMatrizDeContatos - Sinaliza que pode realizar a leitura
// Saídas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que foi realizada nova leitura da matriz
// - ucMatrizDeContatosDeEntrada[] - Leituras atualizadas no vetor
//===== =====
//----------- Definições de parâmetros -------------------------
#define FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS 10.0 // Hz, que corresponde à freqüencia de leitura da matriz/ teclado
// Inicializa o valor de preset do timer de software - Observe que FREQUENCIA_DE_INTERRUPCAO_TIMER_0 está
// definido no módulo do timer
#define PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS (unsigned int)((FREQUENCIA_DE_INTERRUPCAO_TIMER_0/ FREQ_DE_INTERRUP_TIMER_MATRIZ_DE_CONTATOS)+ 0.5)
// Define o número de linhas da matriz (exemplo)
#define NUMERO_DE_LINHAS_DA_MATRIZ 4 // Número de linhas de seleção
// Aqui define-se os ports onde estão conectadas a matriz e as linhas de selação
#define PORTA_DE_ENTRADA_DA_MATRIZ P2 // Define o Port onde está conectada a matriz
#define PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ P2 // Define o port onde estão conectadas as linhas de seleção
// As máscaras a seguir definem máscaras do tipo cheia de "uns", onde existem linhas ativas. Obrigatoriamente essas linhas
// têm que ser consecutivas e no máximo 8, ocupando o port inteiro
// Alguns exemplos ... 3 linhas a partir do bit 0 do port 2
#define MASCARA_DA_MATRIZ 0x07 // Filtra os bits de entrada da matriz (3 nesse exemplo)
// Quatro linhas de selação ... bits 4 a 7 do port 2
#define MASCARA_DA_SELECAO 0xf0 // Filtra os bits dos sinais de seleção
// Bit de seleção a ser shiftado, para gerar o sinal de seleção ... bit 4 (define a primeira linha a ser lida)
#define SELETOR_DOS_SINAIS_DE_SELECAO 0x10 // Define posição do primeiro bit do conjunto de seleção
// Aqui define-se se as saídas são normais ou invertidas, complemento de 1. Se forem invertidas, realiza-se
// um ou exclusivo da máscara de seleção com elas, invertendo a todos os bits..
//#define SAIDAS_INVERTIDAS 1 // Define se as saidas de selecao sao invertidas ou nao
#ifdef SAIDAS_INVERTIDAS
#define MASCARA_DE_INVERSAO MASCARA_DA_SELECAO
#else
#define MASCARA_DE_INVERSAO 0x00
#endif
//*** Aqui definem-se as variáveis globais desse trecho da rotina (fora do bloco principal - main)***
volatile bit bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Sinaliza que pode realizar nova leitura
unsigned int unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS;
unsigned char ucMatrizDeContatosDeEntrada[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
volatile bit bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
//===============================================================================
//
// ROTINA QUE REALIZA O "DEBOUNCE" DA MATRIZ DE CONTATOS
//
// uma vez lidos os contatos, pode-se inserir essa rotina para realizar o debounce, se necessário
//
// Entradas : - bFlagDeNovaLeituraDeMatrizDeContatos - Sinaliza que há leitura nova da matriz de entrada
// - ucMatrizDeContatosDeEntrada[] - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz
// Saídas : - ucMatrizDeContatosDeEntradaAnterior[] - Vetor contendo as leituras anteriores da matriz atualizadas
// - bFlagDeTerminoDeDebounce - Sinalização de término de debounce, permissão para análise
//
//===== =====
#define PRESET_CONTADOR_DEBOUNCE 3 // Número de dados novos com a leitura estável ( 0 - 255)
unsigned char ucMatrizDeContatosDeEntradaAnterior[NUMERO_DE_LINHAS_DA_MATRIZ] = { 0, 0, 0, 0};
unsigned char ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
volatile bit bFlagDeTerminoDeDebounce = DESLIGA;
volatile bit bFlagNovosDados = DESLIGA;
//===============================================================================
//
// ROTINA QUE REALIZA A IDENTIFICAÇ+O DOS CONTATOS FECHADOS
//
// Após a realização da leitura, ou após o debounce, pode-se identificar os
// contatos que foram acionados, se houver. Ela numero os contatos sequencialmente
// a partir do primeiro.
//
// Entradas : - bFlagDeTerminoDeDebounce - Sinaliza que há leitura estável da matriz de entrada
// - ucMatrizDeContatosDeEntrada[] - Vetor contendo as leituras atualizadas da matriz
// - ucMatrizDeContatosDeReferencia[] - Vetor contendo as máscaras de referência para identificar
// os contatos acionados
// Saídas : - bFlagResultadosMatrizDeContatos - Sinaliza que há contatos acionados
// - ucContadorDeContatosFechados - Número de contatos acionados
// - ucVetorDeResultados[] - Vetor contendo a posição relativa do contato acionado
//
//===== =====
// Define o número de colunas da matriz.. (quantos bits tem em cada leitura)
#define NUMERO_DE_COLUNAS_DA_MATRIZ 03
// Identifica a posição do primeiro bit a ser identificado, e utilizado para comparação (com operação AND
// com a máscara) e deslocamento da máscara para leitura dos bits subseqüentes
#define MASCARA_DE_SELECAO_DE_COLUNA_INICIAL 0x01
// Define um número máximo de resultados para o vetor de resultados
#define TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS 5
// define e inicializa um vetor de referência, onde os bits = 1, sinalizam que o contato fechado = 1, e
// bits = 0, quando está em zero... Abrange apenas os bits conectados da matriz... Aqui no exemplo, bits
// de 0 a 2
unsigned char ucMatrizDeContatosDeReferencia[NUMERO_DE_LINHAS_DA_MATRIZ] = {0x07, 0x07, 0x07, 0x07};
unsigned char ucVetorDeResultados[TAMANHO_MAXIMO_DO_VETOR_DE_RESULTADOS] = {0, 0, 0, 0, 0};
// Contador que indica o número de contatos fechados no vetor
unsigned char ucContadorDeContatosFechados;
// bit de sinalização de que encontrou contatos "fechados"
bit bFlagResultadosMatrizDeContatos = DESLIGA;
//*************************************************************************
/* EasyCODE > */
/* EasyCODE ) */
/* EasyCODE ( 8
Definicoes e inicializacoes referntes aos Displays */
/* EasyCODE < */
//===============================================================================
//
// DEFINIÇiES 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 /https://embarcados.com.br/sistemas-operacionais-de-tempo-real-teclados-matriciais// 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};
/* EasyCODE > */
/* EasyCODE ) */
/* EasyCODE ( 2
vInterrupcaoTimer0 */
/* EasyCODE F */
//----------------- Fim dos parâmetros e variáveis dos displays do tipo II ---------
//*******************************************************************************
// TIMER 0
// rotina de interrupção
//
void vInterrupcaoTimer0() interrupt 1
{
/* EasyCODE ( 9
Verifica Temporizador virtual para demosntracao do Timer */
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
}
/* EasyCODE ) */
/* EasyCODE ( 10
Testa temporizador de software referente aos displays */
//********************************************************************************
//****** 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 ************************************
}
/* EasyCODE ) */
/* EasyCODE ( 11
Testa temporizador de siftware referente ao teclado matricial */
//****** Trecho a ser inserido na rotina de interrupção do timer - Matriz de Contatos - Timer de software***
if (--unTimerDaMatrizDeContatos == 0)
{
unTimerDaMatrizDeContatos = PRESET_TEMPORIZADOR_DA_MATRIZ_DE_CONTATOS; // Reinicializa temporizador
bFlagDeTemporizacaoDaMatrizDeContatos = LIGA; // Sinaliza que terminou a contagem
/* EasyCODE - */
//************** Fim da Rotina **************************************************
}
/* EasyCODE ) */
}
/* EasyCODE ) */
/* EasyCODE ( 3
main */
/* EasyCODE F */
//================================================================================
//
// PROGRAMA PRINCIPAL
void main(void)
{
/* EasyCODE ( 15
Inicializacoes referentes aos Displays */
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;
/* EasyCODE : */
{
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 ******************
/* EasyCODE ) */
/* EasyCODE ( 16
Inicializacao do TIMER 0 */
//********************************************************************************
//
// 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
//****
/* EasyCODE ) */
for (;; // Loop principal
)
{
/* EasyCODE ( 17
Trata a sinalizacao de termino de temporizacao do timer virtual */
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
}
//********************************************************************************
//****** Trecho a ser inserido no programa principal - Matriz de Contatos - II ***
//****** ***
/* EasyCODE ) */
/* EasyCODE ( 18
Trata teclado matricial */
/* EasyCODE ( 19
Rotina de nova leitura de teclado */
if (bFlagDeTemporizacaoDaMatrizDeContatos == LIGA // Testa se pode realizar a leitura da matriz
)
{
unsigned char ucIndice;
bFlagDeTemporizacaoDaMatrizDeContatos = DESLIGA; // Desliga o Flag
PORTA_DE_ENTRADA_DA_MATRIZ |= MASCARA_DA_MATRIZ; // Prepara a porta para leitura
//*** Início da rotina para realizar a leitura da matriz de contatos ***
for (ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucAux;
ucAux = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucAux &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
ucAux |= ((SELETOR_DOS_SINAIS_DE_SELECAO << ucIndice) ^ MASCARA_DE_INVERSAO);
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = ucAux;
// Le a linha de contatos, e filtra, deixando só o que interessa
ucMatrizDeContatosDeEntrada[ucIndice] = PORTA_DE_ENTRADA_DA_MATRIZ & MASCARA_DA_MATRIZ;
}
// Desliga as linhas de seleção
ucIndice = ~MASCARA_DA_SELECAO; // Inverte os bits da mascara
ucIndice &= PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ; // Filtra os bits de seleção
PORTA_DE_SAIDA_DA_SELECAO_DA_MATRIZ = (ucIndice ^ MASCARA_DE_INVERSAO);
// Inserir essa instrução apenas se necessitar de "debounce"
bFlagDeNovaLeituraDeMatrizDeContatos = LIGA; // Sinaliza que foi realizada nova leitura
}
//******** Término da rotina Matriz de Contatos - II ********
/* EasyCODE ) */
/* EasyCODE ( 20
Rotina de debounce do teclado */
//******************************************
//******** Rotina de "Debounce " ********
//******* ********
// Testa se há leitura nova
if (bFlagDeNovaLeituraDeMatrizDeContatos == LIGA)
{
bit bFlagOk = LIGA;
unsigned char ucIndice;
// Reseta o flag de sinalização
bFlagDeNovaLeituraDeMatrizDeContatos = DESLIGA; // Sinaliza que foi realizada nova leitura
// Se o contador for diferente de zero, decrementa o contador
if (ucContadorDeDebounce != 0)
{
ucContadorDeDebounce--;
}
for (ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
// Compara a matriz de entrada com a matriz anterior
if (ucMatrizDeContatosDeEntrada[ucIndice] != ucMatrizDeContatosDeEntradaAnterior[ucIndice])
{
// Se diferentes, sinaliza
bFlagNovosDados = LIGA;
bFlagOk = DESLIGA;
}
// Transfere a matriz para a área da matriz anterior
ucMatrizDeContatosDeEntradaAnterior[ucIndice] = ucMatrizDeContatosDeEntrada[ucIndice];
}
// Testa se houve alteração das entradas
if (bFlagOk == DESLIGA)
{
// Se houve alteração, reinicializa o contador
ucContadorDeDebounce = PRESET_CONTADOR_DEBOUNCE;
}
// Testa se estabilizou as leituras
if ((ucContadorDeDebounce == 0) && (bFlagNovosDados == LIGA))
{
// Sinaliza que estabilizou a leitura
bFlagDeTerminoDeDebounce = LIGA;
}
//************* Término da rotina de debounce ***************
}
/* EasyCODE ) */
/* EasyCODE ( 21
Identifica as teclas acionadas */
//******************************************************************
//************* Rotina que identifica os contatos acionados ****
//************* ****
// Verifica se terminou o "debounce"
if (bFlagDeTerminoDeDebounce ==LIGA)
{
unsigned char ucIndice, ucContadorAux;
bFlagDeTerminoDeDebounce = DESLIGA;
bFlagNovosDados = DESLIGA;
// Inicializa o contador de contatos
ucContadorDeContatosFechados = 0; // Inicializa contador de contatos fechados
ucContadorAux = 0; // Inicializa o contador Auxiliar, que indicará o índice do contato
for (ucIndice = 0; ucIndice < NUMERO_DE_LINHAS_DA_MATRIZ ; ucIndice++)
{
unsigned char ucContatoAux;
// Verifica se nesssa linha há contatos fechados
ucContatoAux = (ucMatrizDeContatosDeEntrada[ucIndice] ^ ucMatrizDeContatosDeReferencia[ucIndice]);
if (ucContatoAux != 0)
{
unsigned char ucAux, ucRegistroDeDeslocamento;
ucRegistroDeDeslocamento = MASCARA_DE_SELECAO_DE_COLUNA_INICIAL;
for (ucAux = 0; ucAux < NUMERO_DE_COLUNAS_DA_MATRIZ; ucAux++)
{
if ((ucRegistroDeDeslocamento & ucContatoAux) != 0)
{
ucVetorDeResultados[ucContadorDeContatosFechados++] = ucContadorAux; // Transfere a posição do contato
bFlagResultadosMatrizDeContatos = LIGA; // Sinaliza que encontrou contatos acionados
}
ucRegistroDeDeslocamento = ucRegistroDeDeslocamento << 1; // Desloca a máscara de um
ucContadorAux++; // Incrementa o contador
}
}
}
}
//************** Fim da rotina e identificação dos contatos acionados ***
/* EasyCODE ) */
/* EasyCODE ( 22
Desvia para a rotina de tratamento */
//**********************************************************************************
//************** Inicio da rotina de execução de ações em função dos contatos ******
//************** ******
// Testa se há contatos acionados
if (bFlagResultadosMatrizDeContatos == LIGA)
{
unsigned char ucIndice;
// Verifica todos os contatos localizados
for (ucIndice = 0; ucIndice < ucContadorDeContatosFechados; ucIndice++)
{
// Aqui verifica qual é o contato acionado e realiza alguma ação dependendo de qual for...
switch (ucVetorDeResultados[ucIndice])
{
case 0:
/* EasyCODE : */
{
break;
}
case 1:
/* EasyCODE : */
{
break;
}
default:
/* EasyCODE : */
{
}
}
}
}
/* EasyCODE ) */
/* EasyCODE ) */
}
}
/* EasyCODE ) */
/* EasyCODE ) */
Ficou enorme, não? Já pensou o trabalho que vai dar, se for necessário realizar alterações ou manutenção nesse programa? E se esse programa tivesse mais de 9.000 linhas, como um que desenvolvi para outro projeto? Dá para notar a dificuldade claramente. Agora veja a Figura 3, onde podemos observar o mesmo programa organizado no EASYCODE.
A diferença é gritante!!! Pode-se observar claramente as diversas partes do programa, agora organizado em blocos. Esses diagramas têm estrutura hierárquica, que nos permite avançar para o interior do bloco, apenas “clickando” em cima dele. Veja no Figura 4 os detalhes da rotina de interrupção do Timer 0.
Na Figura 5, descendo mais um estágio dentro dessa rotina, podemos ver o detalhamento do bloco referente aos displays de 7 segmentos.
Para finalizar, veja na Figura 6 o detalhe do programa principal.
O software EASYCODE também permite copiar, arrastar, mover e deletar um bloco inteiro, se você quiser. Pode-se fazer engenharia reversa de algum código, especialmente naqueles mais antigos, em que você não se lembra mais o que foi feito. Recomendo que você baixe o programa de avaliação e dê uma “voltinha” nele. Você vai ficar com água na boca.
Resumo
Neste artigo técnico foi apresentada uma ferramenta comercial para desenvolvimento de software e mostradas algumas características e vantagens de se utilizá-la.









