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!!!!
Perfeito, Muito bom ja testei funcionou file, so um detalhe quando estou na funçao2 e vou para
ResponderExcluirsubmenu desta porque na hora de voltar ela volta para funçao1 e nao a funçao2?
dê uma olhada na linha 195. quanto volta para o menu anterior, "selecao" será igual à 0.
ResponderExcluirVoce pode criar um vetor para salvar os valores de "selecao" quando trocar de menu.
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?
Excluirtem como postar a solução para esse caso meu amigo tiago não consegui resolver
ExcluirTiago, muito bom esse post!
ResponderExcluirAchei 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,
Sim. Ela é uma função que retorna um char ( no caso, retorna 0 ou 1 ).
ExcluirEla é 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.
Certo... Mas, essa forma de verificação não é C-ANSI?
Excluirexato, 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.
ExcluirImaginei... 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...
ExcluirAbs.
Olá Ronaldo. Otima ideia...quando terminar de fazer o video, é só entrar em contato comigo.
ExcluirAbraços.
Olá
ResponderExcluirEstou 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
Olá!! o arduino não segue totalmente o padrão da linguagem C, por isso não tem como declarar "const Item* menu[]".
ExcluirAlguém sabe como ficaria exatamente o mesmo programa no Arduino?
ResponderExcluirOlá!. 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
Excluirblz 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?
ResponderExcluirSe puder me dar uma ajudinha agradeço.
Forte abraço, vlw
Tem que criar um novo projeto, copiar e colar o código e compilá-lo...
ExcluirSim, eu fiz isso mas quando compilo da erro na linha 167, pois o programa não reconhece o "Func"
ExcluirQual a versão do compilador que você está utilizando?
Excluir5.6.1
Excluirmuito estranho...tente executar com a versão mais recente, 6.4.0
ExcluirOla Tiago, ficou muito bom seu projeto, como ficaria num Lcd grafico k0108, teria que mudar o projeto td.
ResponderExcluirVoce so tem que substituir as rotinas do LCD com as rotinas do seu display grafico...
Excluirna LcdWriteFromROM tentei mudar para GlcdwriteFromRom mas nao deu certo.tem outra maneira.
ExcluirTiago 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 ?
ResponderExcluirolá 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!
ResponderExcluircomo faço para fazer um delay nas saidas portd1 a 4 quando for acionada.
ResponderExcluirpra nao atrapalhar a execução do código tem que utilizar o timer como base de tempo, assim voce consegue o delay desejado.
ExcluirTiago 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 .....
ResponderExcluirThiago!
ResponderExcluirTeria como explicar melhor o uso de mais de um asterisco nos ponteiros?
Desde já; Grato.
Tiago, bom dia!
ResponderExcluirEstou 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
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.
ResponderExcluirqueria 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.
amigo não consegui com que esse código funcionasse da erro de compilação
ResponderExcluirBom dia, estou começando agora com microcontroladores, nesse arquivo esta disponível também o esquema no Protheus ?
ResponderExcluirnao consegui achar
Muito Obrigdo.
Bom dia, estou começando agora com microcontroladores, nesse arquivo esta disponível também o esquema no Protheus ?
ResponderExcluirnao consegui achar
Muito Obrigdo.
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.
ResponderExcluirAchei 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
Estou utilizando o MikroC versão 6.6.2 e esta 100% ok!!
ExcluirEstou com os mesmos problemas do Renan carvalho!!
ResponderExcluirErros nas mesmas linhas.
Tentei incluir todas as bibliotecas e nada!!!
Há alguém que responda este problema????
ResponderExcluirOk rodou redondo, instalei a versão 6.4.0!!!
ExcluirPor favor estou com dificuldade de baixar a versão 6.4.0!! da mikro c
ExcluirPoderiam ajudar-me
Por favor estou com dificuldade de baixar a versão 6.4.0!! da mikro c
ExcluirPoderiam ajudar-me
Conseguiu baixar?
ExcluirBom 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.
ResponderExcluirPretendo 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
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!!
ExcluirEste comentário foi removido pelo autor.
ResponderExcluirFiquei 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
ResponderExcluirPor favor alguém pode ajudar!!!?
Sim, vamos por parte.
ExcluirParece 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?
Pra isso precisa mudar tudo na função MostrarItens, pois ela incrementa o row acima de 4 que é a capacidade do display (mesmo com 2 linhas ele achei ser comandado com até 4 linhas).
ExcluirCorrigi 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!!
ResponderExcluirhttps://drive.google.com/file/d/0B-YFPrv2IzWXSjYwZEoyNjZBNVE/view?usp=sharing
contato https://www.facebook.com/eericardosjb
O programa funciona perfeito, mas eu queria que ao "CANCELAR" ele não voltasse ao primeiro item do menu. Algum pode dar um exemplo de código pra resolver isso?
ResponderExcluirBoa noite Tiago,
ResponderExcluirFicou muito bom e prático o funcionamento deste menu.
Obrigado!!