Roteiro 9 EA870 2S2009 Experimental

De DCA-Wiki

Contents

Roteiro 9 Experimental - Uso do teclado

O objetivo desta experiência será desenvolver subrotinas de uso do teclado feito por uma matriz de chaves.

Em algumas experiências anteriores, utilizamos a função getchar() para fazermos a leitura do teclado do Hyperterminal, através da porta serial (UART0). Existem 3 funções para acesso à UART: uart_init(c,s,b) e uart_getchar(canal). Seria possível termos também a função usart_checkchar(canal) onde ela retornaria verdadeiro ou false, caso exista ou não um caractere disponível no teclado.

A idéia deste laboratório, é substituir estas tres funções, init, checkchar e getchar pelas funções correspondentes acessando o teclado: tecl_init(), tecl_checkchar(), tecl_getchar(). A substituição deve ser de tal forma que as experiências anteriores feitas com a leitura do teclado do Hyperterminal possam ser feitas agora com a leitura do teclado da matriz de botões.

Vamos implementar estas 3 funções, em 2 versões: a versão onde não se utiliza interrupção e a versão onde a varredura do teclado é feita por interrupção. A idéia de utilizar aqui interrupção é programar um temporizador que gere interrupções periódicas, de 1 ms, de modo que a cada interrupção, uma pequena trecho para tratar a máquina de estado da subrotina do teclado seja executado.

Este exercício é importante e traz vários conceitos de sistema operacional, quando se utiliza E/S. Este laboratório ocupará duas aulas, sendo que a segunda aula será utilizado para correção das soluções de modo que possamos aprender com a solução de cada um e chegarmos a uma conclusão de como melhor fazer a solução e então iremos utilizar a aula adicional para fazermos a segunda versão, melhor implementada.

Algumas melhorias no programa apresentado como exemplo: varre_teclado(int situacao[]):

  • portas de E/S estão na sua maioria utilizando 32 bits. Verificar se o correto não seria utilizar acesso às portas através de variáveis uint8 (8 bits)
  • verificar se há realmente necessidade de reprogramar a Porta PCTC como saída a cada laço. Acredito que ela poderia ser colocada sempre como saída. RESPOSTA: a vantagem de se reprogramar a PCTC como apenas um bit como saída e os restantes como entrada é para evitar curto-circuito quando duas teclas da mesma coluna estão pressionadas.
  • Outra questão que pode ser melhorada é na leitura da coluna, fazer isto apenas uma vez e não a cada novo teste da coluna.

Na solução por interrupção, projetar a máquina de estado da rotina de interrupção de modo que ela faça o tratamento das teclas e informe à subrotina tecl_checkchar e tecl_getchar quando uma nova tecla foi apertada.

#include "support_common.h" /* include peripheral declarations and more */
#include <stdio.h>          /* see stdio in /6/  */

#define PORTTC  0x4010000F
#define DDRTC   0x40100027
#define PORTUBP 0x40100042
#define DDRUB   0x4010002A
#define PUBPAR  0x40100072

char varre_le_teclado()
{
	int i,j;
	uint8 mask, key_coluna;
	uint8 aterra[4] = {0x0E,0x0D,0x0B,0x07}; /*Sequencia de saidas para as portas*/
	uint8 io[4] = {0x010,0x02,0x040,0x08}; /*Sequencia de configuracao de porta*/
	char key_ascii[] = "123456789#0*"; /* tabela ASCII da matriz do teclado na ordem de varredura */
	int tecla_i; /* indice da tecla sendo varrida */

	uint8 *porttc  = (uint8 *)PORTTC; /* aciona as linhas do teclado */
	uint8 *ddrtc   = (uint8 *)DDRTC;
	uint8 *portubp = (uint8 *)PORTUBP; /* leitura das colunas do teclado */
	uint8 *ddrub   = (uint8 *)DDRUB;
	uint8 *pubpar  = (uint8 *)PUBPAR;

    *pubpar = 0; /* configura Porta B como GPIO, entrada e saída de propósito geral */
    *ddrub  = 0; /* configura Porta B como entrada */

	/*Rotina para varrer teclado*/
	tecla_i = 0;
	for (i=0; i<4; i++){  
	  /*Faz a leitura da linha i*/
	  *ddrtc = io[i]; /*Configura TCi como saida e o restante como entrada, previnindo contra curto
	                    se duas teclas de linhas diferente e mesma coluna são apertas simultaneamente */
	  *porttc = aterra[i]; /*Seta ZERO no bit TCi para configura aquele bit como saída*/
      mask = 0x01; /*Configura mascara para detectar botao apertado ou nao*/
	  key_coluna = *portubp; /* Lê a porta B, coluna da matriz de teclas */
	  for (j=0;j<3;j++){
		/*Caso o botao esteja apertado, o bit i da leitura da portubp (key_coluna) estará em ZERO*/
		if(!(mask & key_coluna)) {
		    return key_ascii[tecla_i]; /* retorna o código ASCII da tecla correspondente */
		}
        mask = mask <<1; /* Vai testar próxima coluna */
    	tecla_i++; /*Passa para proximo índice de varredura do teclado*/
	  }
	}
    return (char)0; /* indica que nenhuma tecla foi apertada */
}
/* para teste inicial */
main() {
  char c;
  while (1) {
    c = varre_le_teclado();
    if (c) printf("tecla %d\n", c); /* imprime codigo ASCII em decimal */
	else   printf("tecle\n");
  }
}

/* Para teste imprimindo no hyperterminal */
main() {
	char c;

	uart_init(0, 80000 ,9600); /* configuração da UART, 9600 bps */ 
	while (1)
	{
	   c = varre_le_teclado();
	   uart_putchar(0, c);
	}
}

Entrega dos relatórios

Os alunos das turmas E e F deverão entregar seus relatórios aqui na wiki.

Atenção, oss relatórios devem conter não apenas as funções de inicialização e as tecl_getchar nas versões com e sem interrupção, mas também a experiência 7 que utilizava o hyperterminal, porém em vez de entrar dados em hexadecimal, trocar para entrar dados em decimal, já que nosso teclado tem apenas 12 teclas.

Assim, a experiência 7 modificada seria:

  • Leia dois números em (hexadecimal - trocar por decimal)

recebidos pela porta serial (pelo Hyper Terminal).

  • Os números serão separados por ENTER (como não temos ENTER, usar * ou #) : o primeiro ENTER significa que ali acaba o primeiro número e o segundo ENTER significa que ali acaba o segundo número.
  • Some-os e envie o resultado pela serial para o Hyper Terminal em HEXADECIMAL, BINARIO e DECIMAL (separados por espaço).


Cada turma deve criar um link para o seu relatório. Para aprender a editar a wiki e criar links, veja no menu à esquerda na página de Ajuda.

Turma E

Turma F

  • grupo 1 Marcus Vinicius Laganá e FabianoEmmanuel Montoro: EA870_R9
  • grupo 2 Matheus Souza e Gustavo Azevedo de Oliveira: ra071855_EA870_R09
  • grupo 3 Vinicius Tasso e Vinicius Dessoti R9
  • grupo 5 Henri Maranghetti Lourenço e William Robert Miyazaki Ra043985_ra083097_rel9

Comentários do Professor sobre os relatórios

Comentários gerais

Observe que o enunciado deste laboratório pede que os programas principais e subrotinas, tanto na versão do programa sem interrupção e com interrupção sejam os mesmos. As únicas mudanças são as novas codificações das rotinas de E/S com o teclado.

Outra sugestão é que a rotina de interrupção deve varrer apenas uma linha do teclado. Para isto é necessário desenhar uma máquina de estado que saiba qual é a linha que será varrida na próxima interrupção.

É importante projetar as variáveis globais que se comunicarão entre a subrotina de interrupção e as subrotinas de leitura do teclado tecl_getchar() e tecl_checkchar().

Lembrar que uma subrotina de interrupção deve ser a mais curta possível e deve fazer o mínimo necessário. A entrada e saída em sí deve ser tratada nas subrotinas que não são de interrupção. Adicionalmente, uma subrotina de interrupção não deve tratar de nenhuma outra entrada ou saída que não esteja diretamente associada à entrada e saída associada a subrotina de interrupção.

Um outro cuidado que praticamente ninguém está tomando é que é preciso sinalizar quando a tecla é lida para que possamos esperar pela leitura da nova tecla. Recomendo o uso de uma variável com o mesmo significado de RXRDY onde ela fica verdadeira quando uma tecla é apertada e vai ficar falsa quando a tecla é lida pelo getchar(). Adicionalmente é necessário uma variável informando que a tecla continua apertada para não sinalizar duas ou mais leituras num único aperto da tecla.

Atenção: Uma eventual crítica sendo feita numa solução entregue não quer dizer que a nota do aluno/equipe seja baixa, pelo contrário, os primeiros alunos que postarem seus resultados aqui tem o mérito de serem os primeiros a mostrarem suas soluções. Os demais alunos podem aproveitar tanto estes comentários como a solução do colega para aperfeiçoar a sua.

Comentários de cada grupo

André e Diego (turma E): a subrotina de interrupção faz tudo e o programa principal não faz nada. O correto é que o mesmo programa principal do exemplo sem interrupção deve ser usado.

Danilo e Thiago (turma E):

  • Evitar o uso de constantes binárias (*UCSR0 = 0%11011101; ou *epfr = 0b00010000; prefira usar *UCSR0 = 0xDD; e *epfr = 0x10;).
Resolvido.
  • Evitar o uso de goto ( goto charanalisys) pois não é uma prática recomendada. Utilize break e variáveis sinalizando a detecção da tecla apertada.
Resolvido.
  • A implementação do tecl_getchar() precisa ser melhorada pois se não for testado se existe caractere apertado a subrotina vai devolver lixo ou a última tecla apertada.
Resolvido.
  • A solução ainda não está compatível com o significado da função getchar() pois foi usado uma espera explícita para que a tecla seja desapertada. O correto é sinalizar um flag informando que a tecla ainda está apertada e somente após ela ser solta é que uma nova tecla poderá ser lida. Veja meu comentário genérico onde é necessário ter uma variável que sinaliza quando existe uma nova tecla, que o seu programa não tem.
Roteiro com Interrupção modificado de acordo com combinado. Caso um estado do teclado se repita MINTIME vezes, a rotina de interrupção irá ativar RXRDYteclado e modificar teclado para o valor correspondente, e caso um estado se repita MAXTIME vezes esta irá reativar RXRDYteclado para indicar que a tecla foi apertada por um tempo muito grande o que foi interpretado como duas apertadas na mesma tecla. RXRDY é checada por tecl_checkchar(). Tecl_getchar() espera RXRDY ser ativada e então a desativa e lê o valor adequado.

Marcus Vinicius Laganá e FabianoEmmanuel Montoro: (Turma F):

  • Programa principal na versão com interrupção deve ser o mesmo, está usando uma variável de flag.
Resolvido com comentários dia 02/11.


Matheus Souza e Gustavo Azevedo de Oliveira (Turma F):

  • Primeira solução entregue onde a varredura por interrupção é feita por linha, como recomendado.
  • Erro conceitual grave: uso de wait() dentro de subrotina de interrupção. O conceito de interrupção é justamente para otimizar o uso da cpu em tarefas concorrentes. Colocar um wait() dentro de uma subrotina de interrupção viola este conceito.
Resolvido. A função wait foi movida para o programa principal, havendo uma pequena modificação com o uso da flag.
  • Programa principal na versão com interrupção deve ser o mesmo, está usando uma variável de flag.
Estamos usando uma variável de flag para controlar o acesso do teclado pela interrupção e para comunicá-la com o programa principal. Não encontramos um modo de retirar essa flag e usar o mesmo programa principal da outra solução.
Ferramentas pessoais