Função para Edição de Variável no LCD

Software e Hardware para ATMEL

Moderadores: 51, guest2003, brasilma

Função para Edição de Variável no LCD

Mensagempor Fandango » 06 Fev 2008 13:20

Olá gente,

Estou tentando fazer uma rotina em C para edição de dados num LCD através de 4 teclas (Up, Down, Enter e Cancel), mas não está ficando nada boa :oops: . Na realidade, como a intenção é fazer algo genérico, e estou usando interrupção, percebo que estou pouco a pouco complicando demais a coisa toda, pelo que gostaria de um ponto de partida dos colegas ou alguma orientação de maneira a simplificar as coisas.

Tenho o seguinte:
- Todas as funções de tratamento do LCD
- Main loop que mantém atualizado o buffer de tecla pressionada (com debouncing) = Loop infinito que chama a função "key = ReadKeys();"
- Função "char ReadKeys(void)" que me retorna a tecla pressionada, pelo que posso utilizar estruturas do tipo:

Código: Selecionar todos
if (key == NONE)
  {
  // Código para quando não houver nada pressionado
  }
else if (key == UP)
  {
  // Código para tratar incrementos
  }
else if (key == DOWN)
  {
  // Código para tratar decrementos
  }
else if (key == ENTER)
  {
  // Código para aceitar o dado
  }
else if (key == CANCEL)
  {
  // Código para cancelar a edição
  }


A idéia é permitir a edição de uma variável, cuja representação no LCD possa ser algo como [000000], sendo que inicialmente o cursor estaria posicionado no primeiro caractere (ou seja: [X00000]). A medida que fosse pressionando a tecla UP, o caracter seria incrementado ([100000], [200000],..) mas limitado entre 0 e 9. Assim que pressionasse a tecla ENTER, o cursor passaria ao caracter seguinte ([2X0000]), e assim por diante, até que atingido o último caracter editável, o dado seria aceito e então convertido para utilização pelo programa principal.

Imaginei implementar uma função genérica (EditField), cujos parâmetros seriam:

>Tamanho do campo de edição
- DataLen = Máximo 6 caracteres (0 a 999999)
>Variável a editar
- *Var = teria que ser tipo "double", porque 999999d não cabe em 16 bits
>Coordenadas do dado no LCD (linha e coluna do primeiro caracter)
- Row = Linha do LCD
- Col = Coluna do LCD
>Tecla que foi pressionada
- Key = {NONE, UP, DOWN, ENTER, CANCEL}

Desta forma, poderia editar variáveis de 1 a 6 caracteres, poisicionadas em qualquer coordenada do LCD.

Alguma dica? :shock: Ou melhor, alguém conhece um jeito simples de implementar isto em C, considerando que "EditField" será chamada sempre que uma tecla for pressionada? :?:

Valeu!
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Maurício » 06 Fev 2008 15:32

Fandango... Esse tema dá pano!

O que eu uso é transformar uma variável numérica em string, e manipular a string. Depois de manipulada, converter de novo pra base numérica.

Com string vc cria uma variável "cursor", que indica em qual elemento da string o programa irá alterar. De acordo com o cursor e na base numérica que vc tá trabalhando, é só adicionar ou subtrair, conforme a tecla pressionada.

No seu caso, quando CURSOR estiver em 5 e o cabra apertar a tecla pra direita, por exemplo, então vc faz CURSOR = 0. É circular.

Processamento pra caramba!!

O compilador que eu uso não tem double, só long. Que compilador vc está usando?

É só uma idéia!

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Fandango » 06 Fev 2008 15:47

Beleza Maurício,

Estou usando o WinAVR. Acho que a idéia é por aí mesmo, não?
O lance é usar uma string, manipulá-la, e depois convertê-la.
A questão-chave é tentar fazer isso do jeito mais simples possível. Evidentemente acho que terei que usar variáveis globais, face à reentrância na função, de maneira a preservar a posição atual do cursor. Digamos uma variável "unsigned char" chamada "curpos", onde as primeiras duas letras representariam um sentimento coletivo na implementação de procedimentos deste tipo :lol: .
Mas bem, vou tentar por aí. Se me complicar muito continuarei gritando :!: .
Valeu!
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Maurício » 07 Fev 2008 08:25

É um método, né?!?!

Como a maioria de nós tem mania de repetir um procedimento que deu certo, então....

Mas, desenvolver a tal da IHM é o maior terror da programação!! :lol: Não fosse isso, as coisas seriam bem mais simples!

Mas, cara, é processamento pra caramba! Vai se preparando, ehehehehehe

Boa sorte.

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Fandango » 07 Fev 2008 08:33

Tô quase lá Maurício ! :x
Está dando trabalheira, mas estou a caminho. Sei que deve existir um jeito mais simples, mas assim que fizer funcionar eu posto a implementação. Pelo menos vai servir de início para alguém melhorar.
Por outro lado, se algum C_dófilo tiver algo que resolva, estamos aqui para avaliar :lol:
Abraço.
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor ze » 11 Fev 2008 16:25

será que tem a ver???:

Código: Selecionar todos
uchar entra_valor(curpos)
{
uchar valor;
gotoxy(1,curpos); //aproveita e posiciona cursor (na linha 1 apenas p.ex.).
while((key!=enter) && (key!=cancel)) //pressione enter ou cancel
   {
   if (key == UP)
     {
     ... //delay, debounce, etc
     valor++;// Código para tratar incrementos
     ...
     }
   if (key == DOWN)
     {
     ...
     valor--;// Código para tratar decrementos
     ...
     }
   }
if (valor>9) valor=0; //limite
if (key==cancel) valor=10; //valor fora da faixa 0 a 9
return valor;
}

as rotinas subsequentes avaliariam o retorno: fora da faixa, cursor p. esquerda. p.ex.
tenho 1 rotina pra acertar relógio em lcd 16x2, mas é só pra 2 dígitos de cada vez. A idéia que sugeri pode ser usada para senhas.
Se for para senhas, não vale a pena colocar teclado numérico?
abrç.
Avatar do usuário
ze
Dword
 
Mensagens: 1655
Registrado em: 05 Jun 2007 14:32

Mensagempor Fandango » 11 Fev 2008 19:08

Aé Lellis, acho que tem a ver sim.

O que estou implementando é mais ou menos por aí. Infelizmente tive que suspender um pouquinho a implementação em função de umas urgências, mas já já retomo e te digo.

A idéia que coloquei não é para senhas não, neste caso tens razão, o teclado numérico seria bem mais indicado. A idéia é para setar um contador de até n dígitos, dígito a dígito, utilizando um teclado de 4 teclas (incremento, decremento, cancela e entra).

Logo logo estarei postando o que fiz. É que na realidade ainda falta um ajustezinho prá eu postar sem passar muita vergonha (hehehe), porque meu cursor ficou deslocado do dígito a editar (acho que pisei na bola com algum índice).
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor barboza » 12 Fev 2008 06:59

Oi Amigo!

Se você tem o índice de um numero n dígitos, você pode efetuar somas ou subtrações sem precisar manipular strings.
Considerando numero decimal, você faz assim:


UP:
valor += 10^(indice);

Down:
valor -= 10^(indice);

Left:
if (indice) indice--;

Right:
if (indice < n) indice++;

print_lcd("%i", valor);
set_cursor(indice);

Escrevi de forma genérica, a intenção foi só passar a idéia!
Os homens mentiriam muito menos se as mulheres fizessem menos perguntas.
Avatar do usuário
barboza
Word
 
Mensagens: 948
Registrado em: 17 Out 2006 13:42
Localização: Longe de onde gostaria de estar

Mensagempor Fandango » 12 Fev 2008 07:14

Legal Barboza, agradeço muito o seu post, muito enriquecedor. Vou tentar retomar a empreitada ainda hoje e aplicar o conhecimento que tem sido compartido até agora.
Valeu gente!
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor ze » 12 Fev 2008 07:18

bem, se é pra setar um contador veja se a sugestão é válida:

1 variável;
as setas up e down atuam da seguinte forma: autorepetitivas, a cada 1 (ou x) segundos, se mantidas pressionadas, multiplica por 10 (ou por x) a velocidade de inc/dec. (já é um princípio usado).

Ao meu ver, tende a ser + fácil e eficiente do que editar cada dígito (visto que o valor já está na mente do operador). A não ser que seja uma exigência sua/seu cliente.
Mas na prática a teoria é outra: pode ser que "dedos pesados" tenham realmente + dificuldade.
abrç
Avatar do usuário
ze
Dword
 
Mensagens: 1655
Registrado em: 05 Jun 2007 14:32

Mensagempor Fandango » 12 Fev 2008 07:53

É verdade Lellis, mas sabe o que é? Imagine que o cara tenha que setar uma variável de 6 dígitos, por exemplo com o valor 867200, e que a função na entrada coloque o valor inicial como zero (000000). Mesmo que ocorra um auto-incremento temporizado, na razão de 10 unidades/segundo, até o cara chegar a 867200 vai demorar um bocado. Ainda que a velocidade seja multiplicada por 10 (depois de um tempo de tecla mantida pressionada). Por isso abortei essa idéia e parti para o dígito a dígito, sacou? Mas essa técnica é válida para variáveis menores, claro. Confesso que foi assim que tinha feito, mas quando tentei ajustar um valor prático fiquei doidinho (hehehe).
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor ze » 12 Fev 2008 09:22

pois deixe o valor inicial 5000000. a cada 1 seg mult. por 10. portanto: 1º seg x10; 2º seg x100; 3º x1000; ...

ah, esquece!!!!!!!!!! dígito a dígito fica mais mió mêmo. Ah lembrei: quanto ao cursor deslocado:

LCD_RS=0; //comando
lcd_write(0x10); // desloca p esquerda
lcd_write(0x10); // desloca p esquerda

tive probl. = ; desloquei 2 vezes. ficou 1/2 feio, mas...Se tiver alguma outra solução me avise!!! algo como dígito invertido. difícil, mas não impossível.
abrç
Avatar do usuário
ze
Dword
 
Mensagens: 1655
Registrado em: 05 Jun 2007 14:32

Mensagempor Maurício » 12 Fev 2008 10:49

Isso daqui pode ser um ponto de partida:

Código: Selecionar todos
unsigned long int num = 876200;
unsigned char string_aux [ 10 ];
unsigned char cursor = 0;

unsigned long int incrementar ( unsigned long int dado, unsigned char cursor, unsigned char *string )
{

   ltoa ( dado, string );      // converte LONG em STRING

   if ( ++string [ cursor ] > '9' )      // incrementa o caractere apontado por CURSOR
      string [ cursor ] = '0';
   return ( atol ( string ) );      // devolve um LONG já incrementado na posição correta
}

void main ( void )
{
   num = incrementar ( num, cursor, string_aux );
   while ( 1 );
}


[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Maurício » 12 Fev 2008 11:38

Fandango.

No seu primeiro post vc disse que estava usando interrupção.
Vc usa interrupção para que? Essa interrupção é do timer?

Eu faço varredura de teclado através de uma chave binária.
Programo os temporizadores para me gerarem uma sinalização a cada 100ms, por exemplo, por interrupção do timer, e quando essa variável é sinalizada, então eu varro o teclado.
Eu não uso rotinas de debouncing, por causa desse tempo de varredura.
Se varreu e a tecla foi pressionada... Executa a rotina associada à essa tecla.

Suas chaves estão ligadas direto em pinos, ou vc usa outro método de ler teclas?

Coloque o circuito elétrico, que fica mais fácil visualizar a idéia.

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Fandango » 12 Fev 2008 18:10

Olá Maurício, é verdade, estou usando interrupção em função da abordagem dada ao sistema operacional como um todo.
Gostei do ponto de partida que você publicou. Vou tentar fazer por aí, porque minhas funções ficaram meio que uma bosta :oops: .

Na realidade utilizo um loop infinito que executa sempre um ciclo do tipo:
- Ler entradas (sensores, botões de comando e teclas da IHM)
- Processar variáveis (internas, equações de processo, etc.)
- Atualizar saídas (display, LEDs e buzzer)

As teclas estão ligadas a um buffer 74HC245 (do jeito convencional - chave ao GND e com pullup de 10K), e este ao Port-B do ATmega32, que utilizo como barramento de dados, pois tenho um monte de outras coisas conectadas a ele (essencialmente latches 74HC573, para display, LEDs, relés, etc.). É um circuito bastante grande, não é complexo, mas tem coisa prá chuchu.

Utilizo interrupção do Timer0 para contagem de tempo, do Timer1 para PWM, da Int0 para detecção de zero crossing, e também a UART está por interrupção.

Na realidade essas minhas teclas não estão diretamente numa interrupção do microcontrolador, mas num loop do sistema que volta e meia é interrompido. De início não fui muito específico para ir direto ao assunto, mas creio que devo ter criado uma certa confusão. De ser assim, por favor me disculpem. É que o fato é que não posso ficar preso num loop de tratamento da edição da variável, ou seja, devo verificar a tecla pressionada, atualizar a variável e o display e então cair fora até a próxima verificação.

Bem, de todas maneiras, e como citei lá no começo, uma das atribuições do meu loop infinito é chamar as rotinas de teclado, que fazem o debounce e atualizam os flags, pelo que posso utilizar uma lógica de if-else para saber qual tecla foi pressionada.

Então eu tenho o seguinte:

Código: Selecionar todos
      // -----------------------------------------------------------------------
      // Tecla ENTER
      // -----------------------------------------------------------------------
      if (Ckey == K_ENTER)         // Se ocorreu transição por tecla pressionada
         {
         FieldEdit(fieldbuf, 6, 1, 5, Ckey);
         Ckey = K_NONE;               // Aguarda nova transição por tecla
         }
      // -----------------------------------------------------------------------
      // Tecla UP
      // -----------------------------------------------------------------------
      else if (Ckey == K_UP)      // Se ocorreu transição por tecla pressionada
         {
         FieldEdit(fieldbuf, 6, 1, 5, Ckey);
         Ckey = K_NONE;               // Aguarda nova transição por tecla
         }
      // -----------------------------------------------------------------------
      // Tecla DOWN
      // -----------------------------------------------------------------------
      else if (Ckey == K_DOWN)   // Se ocorreu transição por tecla pressionada
         {
         FieldEdit(fieldbuf, 6, 1, 5, Ckey);
         Ckey = K_NONE;               // Aguarda nova transição por tecla
         }


Por enquanto nem vou me preocupar com a tecla CANCEL, porque ela não irá fazer nada durante a edição.

Repare que a idéia inicial é a de fazer uma função genérica, que nem a que você postou como ponto de partida. A que eu estou tentando implementar (e que está feinha feinha) é essa que chamei de "FieldEdit", cujo protótipo é
Código: Selecionar todos
void FieldEdit(char *fieldbuf, unsigned char fieldsize, unsigned char row, unsigned char col, unsigned char key);

onde "fieldbuf" foi definida como "char fieldbuf[8];" e é o buffer para impressão no LCD, o qual é feito via:
Código: Selecionar todos
   // Exibe o buffer
   LCD_GotoXY(row,col);               // Posiciona o cursor
   LCD_PutStr(fieldbuf);             // Imprime valor atual no LCD
   //

finalmente "fieldsize" é a variável que utilizo para limitar a exibição e a edição (atualmente em 6 caracteres, portanto 6 dígitos).

Então minha função está assim (por favor não riam - hehehe)
Código: Selecionar todos
extern unsigned char ind;         // Índice para edição de dados
//
void FieldEdit(char *fieldbuf, unsigned char fieldsize, unsigned char row, unsigned char col, unsigned char key)
{
   char rasc[3];                        // Buffer de rascunho p/ conversão p/ ASCII
   //
   // Verifica de qual tecla se trata
   //
   if (key == K_UP)                  // Tecla UP
      {
      if (fieldbuf[ind] < '9')   // Maior dígito é o 9
         {
         fieldbuf[ind]++;            // Incrementa o dígito
         }
      }
   else if (key == K_DOWN)         // Tecla DOWN
      {
      if (fieldbuf[ind] > '0')   // Menor dígito é o 0
         {
         fieldbuf[ind]--;            // Decrementa o dígito
         }
      }
   else if (key == K_ENTER)      // Tecla ENTER
      {
      if (ind < fieldsize)         // Se ainda há digitos a editar
         {
         ind++;                           // Avança para o próximo dígito da edição
         }
      else                                 // Se o último dígito foi editado
         {
         // Converter aqui o buffer e guardar o valor na memória
         }
      }
   //
   // Exibe o buffer
   LCD_GotoXY(row,col);               // Posiciona o cursor
   LCD_PutStr(fieldbuf);             // Imprime valor atual no LCD
   //
   col = col + ind;                     // Atualiza para a coluna editada
   //
   LCD_GotoXY(row,col);               // Posiciona o cursor sobre o dígito a editar
}
//


Falei que tava feia!!! :roll:

Vou tentar adaptar a sua Maurício, e se der certo depois eu posto, ok?
De todas maneiras, valeu pela força. Vamos em frente!
... e assim falou Zaratustra !
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Próximo

Voltar para AVR

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

cron

x