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.

Media de notas

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.

easycode

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 timersdisplays 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                    // 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
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 EASYCODEPrincipal

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 0Interrupcao

Na Figura 5, descendo mais um estágio dentro dessa rotina, podemos ver o detalhamento do bloco referente aos displays de 7 segmentos.

Interrupcao_1

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.

Main

Figura 6: Detalhes do bloco do programa principal

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