Fork me on GitHub

12/07/14

PIC: LCD com Menus

PIC: LCD com Menus




Como foi me pedido, neste post irei explicar como criar um menu, bem sofisticado, para um display de LCD.

Nesse menu, cada item pode conter um submenu, cada submenu pode conter outro submenu, e assim por diante, infinitamente( até acabar a memória ). Cada item pode também executar uma rotina ao ser selecionado.

Primeiramente cria-se um registro que irá armazenar as informações de cada item:
struct MenuItem
{
        const Item** mItem;     //SubMenu
        char Texto[16];         //texto
        void(*Tarefa)(char);    //Funcao a ser executada
        char Parms;             //valor do parametro da funcao
};

Depois criamos os items...
const  Item Menu01 = { Screen1, "FUNCAO 1", &SuaFuncao, 0 };
const  Item Menu02 = { Screen2, "FUNCAO 2", NULL, 0 };

Screen1 - é o vetor de itens do submenu. Se não possui um submenu colocar 0.
"FUNCAO 1" - É o texto do item a ser exibido.
&SuaFuncao - É o endereço da função a ser executada pelo item ao ser selecionado. Se não tiver, colocar 0.
0 - é o valor do parâmetro a ser passado para a função dita anteriormente.

e um vetor que armazena os itens da primeira tela a ser exibida.

//Terminar sempre com 0(Zero).
const Item* TelaInicial[] = { &Menu01, &Menu02, 0 }; 

Criamos tambem os itens de uma segunda, terceira, ... , tela:
const Item Menu10 = { Screen3, "Outra Funcao", 0, 0 };
const Item Menu11 = { NULL, "Ativar Saidas", &DefinirSaidas, 255 };
const Item Menu12 = { NULL, "Alternar Saidas", &AlternarSaidas, 0 };
const Item Menu13 = { NULL, "Desligar Saidas", &DefinirSaidas, 0 };
const Item* Screen1[] ={ &Menu10, &Menu11, &Menu12, &Menu13, 0 }; 

Esse código é mais fácil no sentido de poder acrescentar quantos itens e subitens quiser, sem ter que adicionar mais instruções "if", "else", "switch", etc, deixando o código mais limpo. Por outro lado, é necessário um conhecimento sobre o uso de estruturas( struct ) e ponteiros. Vocês podem ficar espantados ao ver tanto asterisco, como nesse trecho do código "static const Item*** MenuAnterior". Para quem quiser entender, irei explicar:
Com Apenas um asterisco: Item* - É um simples ponteiro para um struct Item.
Com dois asterisco: Item** - É um vetor de *Item.
Com três asterisco: Item*** - É um vetor de **Item. ( ou matriz de *Item ).

CÓDIGO:
/*
  PROJETO: LCD com MENUS
  MCU: PIC18F4550
  CLOCK: 48MHz ( 20MHz + PLL )
  COMPILADOR: MIKROC PRO PIC
  AUTOR: Tiago Henrique
*/

#define NULL 0

//PINOS DO LCD
sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RB2_bit;
sbit LCD_D5 at RB3_bit;
sbit LCD_D6 at RB4_bit;
sbit LCD_D7 at RB5_bit;

sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISB2_bit;
sbit LCD_D5_Direction at TRISB3_bit;
sbit LCD_D6_Direction at TRISB4_bit;
sbit LCD_D7_Direction at TRISB5_bit;


/*
       PROTOTIPOS
*/
typedef struct MenuItem Item;
static void LcdWriteFromROM( const char* texto );
//Suas Funcoes
void DefinirSaidas( char );
void AlternarSaidas( char );



struct MenuItem
{
        const Item** mItem;     //submenu
        char Texto[16];         //texto
        void(*Tarefa)(char);    //Funcao a ser executada
        char Parms;             //valor do parametro da funcao
};

/*
       VARIAVEIS GLOBAIS
*/
static char selecao = 0;        //posicao do item selecionado
static const Item** MenuAtual;  //Menu Atual
static const Item*** MenuAnterior; //Pilha de Menus
static unsigned Stack[16];      //Aloca memoria para a pilha



/*
       MENU
*/

//Terceiro Nivel
const Item Menu30 = { NULL, "PORTD = 32", &DefinirSaidas, 32 };
const Item* Screen3[] = { &Menu30, 0 }; //Terminar sempre com o valor 0(NULO)

//Segundo Nivel
const  Item Menu10 = { Screen3, "Outra Funcao", 0, 0 };
const  Item Menu11 = { NULL, "Ativar Saidas", &DefinirSaidas, 255 };
const  Item Menu12 = { NULL, "Alternar Saidas", &AlternarSaidas, 0 };
const  Item Menu13 = { NULL, "Desligar Saidas", &DefinirSaidas, 0 };
const Item* Screen1[] = { &Menu10, &Menu11, &Menu12, &Menu13, 0 };  //Terminar sempre com o valor 0(NULO)

const  Item Menu20 = { NULL, "PORTD = 1", &DefinirSaidas, 1 };
const  Item Menu21 = { NULL, "PORTD = 2", &DefinirSaidas, 2 };
const  Item Menu22 = { NULL, "PORTD = 4", &DefinirSaidas, 4 };
const  Item Menu23 = { NULL, "PORTD = 8", &DefinirSaidas, 8 };
const Item* Screen2[] = { &Menu20, &Menu21, &Menu22, &Menu23, 0 }; //Terminar sempre com o valor 0(NULO)

//Primeiro Nivel
const  Item Menu01 = { Screen1, "FUNCAO 1", NULL, 0 };
const  Item Menu02 = { Screen2, "FUNCAO 2", NULL, 0 };
const Item* TelaInicial[] = { &Menu01, &Menu02, 0 }; //Terminar sempre com o valor 0(NULO)




/*
      Exibe os items do menu atual no display LCD
*/
static char MostrarItens( const Item* menu[], char sel )
{
char index = 0, row = 1;

     Lcd_Cmd( _LCD_CLEAR );

     if( menu == 0 )
         return 0;

     while( menu[index] )
     {
             if( index < sel ) { index++; continue; }
             if( index == sel ) Lcd_Chr( row, 1, '>' );
             else Lcd_Chr( row, 1, ' ' );
             row++;
             LcdWriteFromROM( menu[index++]->Texto );
     }

     if( index <= sel )
         return 1;
     return 0;
}

static void LcdWriteFromROM( const char* texto )
{
    while( *texto )
    {
           Lcd_Chr_CP( *texto++ );
    }
}



void main()
{
char flags = 0; //flags dos botoes

    CMCON = 7;
    ADCON1 = 15;
    //Botoes
    TRISA.B0 = 1;
    TRISA.B1 = 1;
    TRISA.B2 = 1;
    TRISD = 0;
    PORTD = 0;
    
    Lcd_Init();
    Lcd_Cmd( _LCD_CURSOR_OFF );

    MenuAtual = TelaInicial; //A tela atual = tela inicial
    MenuAnterior = &Stack;   //Aponta para a pilha
    
    MostrarItens( MenuAtual, selecao ); //Exibe os itens

    while( 1 )
    {
         if( PORTA.B0 ) flags.B0 = 1;
         else if( PORTA.B1 ) flags.B1 = 1;
         else if( PORTA.B2 ) flags.B2 = 1;

         //Botao Proximo foi pressionado
         if( !PORTA.B0 && flags.B0 )
         {
             ++selecao; //seleciona o proximo item
             //Exibe os itens da tela selecionada e
             //se o indice do item estourar
             //retorna para o primeiro item
             if( MostrarItens( MenuAtual, selecao ).B0 )
             {
                  selecao = 0;
                  MostrarItens( MenuAtual, selecao );
             }
             flags.B0 = 0;
         }
         
         //Botao Enter foi pressionado
         if( !PORTA.B1 && flags.B1 )
         {
             //Recebe o ponteiro da funcao a ser executada
             void(*Func)(char) = MenuAtual[selecao]->Tarefa;
             

             //Caso exista uma funcao a ser executada...
             if( Func != NULL )
             {
                 //Executa a função
                 Func( MenuAtual[selecao]->Parms );
             }

             //Se existir um submenu
             if( MenuAtual[selecao]->mItem != NULL )
             {
                 //Salva o menu atual no MenuAnteror
                 *MenuAnterior++ = MenuAtual;
                 //atualiza o menu atual com o proximo menu
                 MenuAtual = MenuAtual[selecao]->mItem;
                 //Exibe os itens deste proximo menu
                 selecao = 0;
                 MostrarItens( MenuAtual, selecao );
             }
             
             flags.B1 = 0;
         }
         
         //Botao Voltar foi pressionado
         if( !PORTA.B2 && flags.B2 )
         {
              selecao = 0;
              //Se não for o primeiro menu( Tela Inicial )
              if( ((unsigned*)&MenuAnterior)[0] > (unsigned)&Stack )
              {
                 //MenuAtual recebe o menu salvo no MenuAnterior
                 MenuAtual = *--MenuAnterior;
                 MostrarItens( MenuAtual, selecao );
              }
              flags.B2 = 0;
         }
    }

}

//Suas Funcoes

void DefinirSaidas( char valor )
{
    PORTD = valor;
}

void AlternarSaidas( char valor )
{
     PORTD = ~PORTD;
}

DOWNLOAD:
Projeto: LCD_com_menus.rar

O código pode ser um pouco difícil de entender, então, qualquer dúvida é só perguntar. Até mais!!!!

47 comentários:

  1. Perfeito, Muito bom ja testei funcionou file, so um detalhe quando estou na funçao2 e vou para
    submenu desta porque na hora de voltar ela volta para funçao1 e nao a funçao2?

    ResponderExcluir
  2. dê uma olhada na linha 195. quanto volta para o menu anterior, "selecao" será igual à 0.
    Voce pode criar um vetor para salvar os valores de "selecao" quando trocar de menu.

    ResponderExcluir
    Respostas
    1. Tiago, criei o vetor para guardar a selecao , no simulador do mikroc ele funciona file incrementa a decrementa o valor da selecao , so que no proteus que estou simulando nao funciona porque?

      Excluir
    2. tem como postar a solução para esse caso meu amigo tiago não consegui resolver

      Excluir
  3. Tiago, muito bom esse post!
    Achei que vc deu uma boa elevada no nível de programação em C e isso pode ter se tornado u pouco difícil para quem não tem tanta familiaridade com ponteiros, structs, etc.. mas, em contra-partida para quem for interessado vai correr atras dessas informações e vai aprender cada vez mais.
    Só tira uma dúvida o que acontece na linha 155? onde se tem if( MostrarItens( MenuAtual, selecao ).B0 )... não entendi e tbm nunca ví, MostrarItens é uma função que retorna um char, mas, nessa linha vc trata como se fosse uma struct que contém o membro B0...

    Abs,

    ResponderExcluir
    Respostas
    1. Sim. Ela é uma função que retorna um char ( no caso, retorna 0 ou 1 ).
      Ela é então é chamada e a instrução if verifica o bit 0 ( .B0 ) do valor retornado. Esse é o meu jeito de otimizar um pouco o código já que a função só ira retornar 0 ou 1.

      Excluir
    2. Certo... Mas, essa forma de verificação não é C-ANSI?

      Excluir
    3. exato, não é ANSI C...isso é valido somente para o compilador MikroC, já que os microcontroladores PIC possuem instruções próprias para definir ou ler um determinado bit.

      Excluir
    4. Imaginei... Valeu pela atenção, Tiago. Precisando de algo para colaborar com o seu blog estou a disposição. Eu estou com uma ideia de fazer um vídeo-tutorial para ensinar como instalar GNU Arm Cross-toolchain+Eclipse+Windows, quero fazer isso pq apanhei muito para conseguir fazer as devidas configurações e sei como é a dificuldade. Qualquer coisa posso lançar isso pelo seu blog...

      Abs.

      Excluir
    5. Olá Ronaldo. Otima ideia...quando terminar de fazer o video, é só entrar em contato comigo.
      Abraços.

      Excluir
  4. Olá

    Estou tendo problemas para portar o código para a plataforma arduino.
    Recebo o erro

    error: ISO C++ forbids declaration of 'Item"' with no type

    a principio algo com a declaração da função

    char MostrarItens( const Item* menu[], char sel )

    Agradeço sua atenção
    Pedro A. Peres

    ResponderExcluir
    Respostas
    1. Olá!! o arduino não segue totalmente o padrão da linguagem C, por isso não tem como declarar "const Item* menu[]".

      Excluir
  5. Alguém sabe como ficaria exatamente o mesmo programa no Arduino?

    ResponderExcluir
    Respostas
    1. Olá!. Até tentei adaptar esse código que fiz para o Arduino, mas não teve como...ele não segue totalmente o padrão da linguagem C. O correto seria criar classes. De uma olhada neste projeto AQUI

      Excluir
  6. blz Tiago, então cara seu post esta muito bom, apresentando essa função que para mim tbm é novidade, mas eu estou tentando usar no mikroc pro e quando eu compilo da erro nas linhas 167,171 e 174 no "Func" e eu estou em duvida se eu tenho que criar um novo ou se era para funcionar assim msm?
    Se puder me dar uma ajudinha agradeço.
    Forte abraço, vlw

    ResponderExcluir
    Respostas
    1. Tem que criar um novo projeto, copiar e colar o código e compilá-lo...

      Excluir
    2. Sim, eu fiz isso mas quando compilo da erro na linha 167, pois o programa não reconhece o "Func"

      Excluir
    3. Qual a versão do compilador que você está utilizando?

      Excluir
    4. muito estranho...tente executar com a versão mais recente, 6.4.0

      Excluir
  7. Ola Tiago, ficou muito bom seu projeto, como ficaria num Lcd grafico k0108, teria que mudar o projeto td.

    ResponderExcluir
    Respostas
    1. Voce so tem que substituir as rotinas do LCD com as rotinas do seu display grafico...

      Excluir
    2. na LcdWriteFromROM tentei mudar para GlcdwriteFromRom mas nao deu certo.tem outra maneira.

      Excluir
  8. Tiago estou tentando implementar seu projeto com 5 menus mais com 5 não roda perfeitamente parece que o programa coloca o quinto em cima do primeiro você pode me ajudar ?

    ResponderExcluir
  9. olá amigo, quero fazer um menu como o seu para meu arduino uno mas to apanhando pra caramba....teria como você fazer um tutorial como esse para arduino?valeu amigo!

    ResponderExcluir
  10. como faço para fazer um delay nas saidas portd1 a 4 quando for acionada.

    ResponderExcluir
    Respostas
    1. pra nao atrapalhar a execução do código tem que utilizar o timer como base de tempo, assim voce consegue o delay desejado.

      Excluir
  11. Tiago vc pode me ajudar queria passar por uma tomada de decisão e escrever no lcd se estive se uma PORT ligado escrever ligado ou desligado tem como não estou achando um método .....

    ResponderExcluir
  12. Thiago!

    Teria como explicar melhor o uso de mais de um asterisco nos ponteiros?

    Desde já; Grato.

    ResponderExcluir
  13. Tiago, bom dia!

    Estou usando o compilador PIC C e ao inserir na struct o ponteiro: void(*Tarefa)(char); me retorna o erro que é um tipo desconhecido (unknow type).

    Pesquisei diversos sites e não encontrei nenhuma solução para isto. Pode me ajudar!

    Grato.
    Sidney

    ResponderExcluir
  14. ola galera, mestou iniciando em programação pic e gostaria da ajudar de vcs para a criação de um menu capaz da calibrar, ou seja definir valores.
    queria criar um menu que eu pudesse manipular valores em um programa rodando no pic, tipo, um sistema de proteção com voltimetro, onde eu pudesse indicar qual a maxima voltagem que o circuioto aceitaria, sendo assim, o sistema ficaria monitorando e caso a voltagem exceda o limita que foi inserido através do menu, o mesmo desarme o sistema.
    bom a proteção é facil, o problema é a criação do menu para que possa manipular os valores limites.
    espero que alguem possa me ajudar
    abraços e muito obrigado pelo excelnte trabalho que vem prestando.

    ResponderExcluir
  15. amigo não consegui com que esse código funcionasse da erro de compilação

    ResponderExcluir
  16. Bom dia, estou começando agora com microcontroladores, nesse arquivo esta disponível também o esquema no Protheus ?

    nao consegui achar

    Muito Obrigdo.

    ResponderExcluir
  17. Bom dia, estou começando agora com microcontroladores, nesse arquivo esta disponível também o esquema no Protheus ?

    nao consegui achar

    Muito Obrigdo.

    ResponderExcluir
  18. Gostaria de agradecer pelas informações do seu blog, aprecio o seu conteúdo pelo alto nível. Tem me ajudado muito no meu aprendizado na programação de microcontroladores com mikroC.
    Achei fantástica esta rotina de Menus, e vi que funcionou bem para alguns programadores, porem não consegui fazer funcionar!
    Utilizo o mikroC PRO for PIC versão 6.5.0 e quando mando compilar
    Ocorre o seguinte erro:
    "166 374 Const expression expected lcd_com_menus.c"
    Junstamente na linha que tem o comando
    "void(*Func)(char) = MenuAtual[selecao]->Tarefa;"
    Sera que pode me ajudar?
    Obrigado
    Maurício

    ResponderExcluir
  19. Estou com os mesmos problemas do Renan carvalho!!
    Erros nas mesmas linhas.
    Tentei incluir todas as bibliotecas e nada!!!

    ResponderExcluir
  20. Respostas
    1. Por favor estou com dificuldade de baixar a versão 6.4.0!! da mikro c

      Poderiam ajudar-me

      Excluir
    2. Por favor estou com dificuldade de baixar a versão 6.4.0!! da mikro c

      Poderiam ajudar-me

      Excluir
  21. Bom dia ilustres, estou com uma dificuldade no projecto acima "Pic Lcd Menu". Ou seja, desejo mudar as informações que são apresentada na tela LCD.

    Pretendo que a tela lcd apresenta um cardápio de restaurante

    por exemplo:

    = MENU PRINCIPAL =
    --------------
    1 - PRATO_PRINCIPAL
    2 - SOBRE_MESA
    3 - BEBIDAS

    -------//---------//------

    = SUB_MENUS =
    -------------

    = PRATO PRINCIPAL =
    -------------------
    #Funge com galinha de moamba...3000kz
    #Fejoada.......................1500kz
    #Bacalhão a gomes de sá........2500kz
    #Cosido a portuguêsa...........1500kz
    #Bitoque.......................1000kz
    #Mufete........................4000kz

    = SOBRE MESAS =
    ------------------------
    #Arroz doce....................2000kz
    #Pudim.........................1500kz
    #Mouse de mucua................1000kz
    #Salada de frutas..............5000kz
    #Frutas em cauda...............4000kz
    #Bolos.........................1400kz

    = BEBIDAS =
    -------------

    REFRIGERANTES
    #Cocacola......................400kz
    #Fanta.........................400kz
    #Água..........................200kz
    #Sprit.........................400kz
    #Sumol.........................500kz
    #Blue..........................300kz

    BEBIDA COM ALCOOL
    #Cuca..........................150kz
    #Nocal.........................100kz
    #SuperBock.....................200kz
    #Eka...........................200kz
    #Savana........................150kz
    #Tigra.........................400kz


    ResponderExcluir
    Respostas
    1. Posso corrigir esse code ai !! Mas fazer o que falas não tem como pois o Display é de 16x2 e a quantidade de caracteres e mais como podes reparar!!

      Excluir
  22. Fiquei dois dias a tentar alterar, mas não consegui. Continua apresentar na tela LCD as mesma funções como "Função 1 e 2, Alternar saída, etc

    Por favor alguém pode ajudar!!!?

    ResponderExcluir
    Respostas
    1. Sim, vamos por parte.
      Parece que código não faz mais de 4 itens por menu.
      Estou tentando achar uma solução, fora isto , agora esta funcionando bem.
      QUe ajuda precisa?

      Excluir
  23. Corrigi o erro do menu (Que ia até 4) fiz uns comentarios pra ficar melhor entendido !! Qualquer coisa estamos ai pra melhorar mais em conjuto com vcs ok!!

    https://drive.google.com/file/d/0B-YFPrv2IzWXSjYwZEoyNjZBNVE/view?usp=sharing


    contato https://www.facebook.com/eericardosjb

    ResponderExcluir

Postagens Relacionadas!!