EASYCODE: Uma ferramenta de produtividade - por Henrique Puhlmann
Neste artigo vou apresentar uma ferramenta de software que utilizo há muitos anos, o EASYCODE[1]. 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 desoftware 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. NaFigura 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.
Figura 1: Diagrama N-S para cálculo da nota média de um aluno
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 [2].
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 deEASYCODE.
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.
Figura 2: Módulos do EASYCODE
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 [3], 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
|
/* 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
//----------------------------
/* 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
Sistemas Operacionais de Tempo Real - Teclados Matriciais
// 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://www.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.
Figura 3: Programa exemplo 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.
Figura 4: Detalhes do bloco 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.
Figura 5: Trecho de código ref. aos displays de 7 segmentos inserido na interrupção do Timer 0
Para finalizar, veja na Figura 6 o detalhe do programa principal.
Figura 6: Detalhes do bloco 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
Nesta artigo técnico foi apresentada uma ferramenta comercial para desenvolvimento de software e mostradas algumas características e vantagens de se utilizá-la.
Referências
[1] - https://www.easycode.de/en/home.html
[2] - Técnicas: Top-Down, Mocks e TDD
[3] - Sistemas Operacionais de Tempo Real - Teclados Matriciais
Henrique Puhlmann
Sou paulistano, 56 anos, formado em Engenharia Eletrônica pela Escola Politécnica da Universidade de São Paulo (formado em 1982) e trabalho há pelo menos 31 anos no Instituto de Pesquisas Tecnológicas do Estado de São Paulo S/A - IPT com Pesquisa e Desenvolvimento, principalmente pesquisa aplicada e desenvolvimento tecnológico de protótipos e produtos eletrônicos dedicados.
Fonte - Embarcados