CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Creating Menus and Submenus

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
mvaraujo



Joined: 20 Feb 2004
Posts: 59
Location: Brazil

View user's profile Send private message

Creating Menus and Submenus
PostPosted: Tue May 18, 2004 10:01 am     Reply with quote

Hi everyone,

I would like to put in discussion an issue that always makes me confused: which is the best way to implement menus and submenus for display in a PIC application?

I usually use a structure of state machines based in some variables like state and substate, whatever, I use the keyboard to create actions of incrementing or decrementing these state variables and then jump from one state to another. These states can be screens with information, a menus with a selection cursor that points to the options, etc.

What do you guys think about this? Does anyone point to other direction that could bring better results? When I talk about better results, I always think about faster code and smaller code.

Thanks in advance!

Marcus
Haplo



Joined: 06 Sep 2003
Posts: 659
Location: Sydney, Australia

View user's profile Send private message

PostPosted: Tue May 18, 2004 5:31 pm     Reply with quote

Once we had to do a big menu system for a dot-matrix display, under 68HC16. After careful delibration we decided the state machine was the best way to go. It is fast but the code tends to get big.
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

PostPosted: Tue May 18, 2004 7:45 pm     Reply with quote

I would suggest using a state machine with a state for each menu item. Prior to running the current state read and debounce the inputs and set a flag for each input that is active. After running the current state clear all input flags. This keeps the code required for reading inputs at a minimum and keeps the code easy to read. The bigger problem though is going to be displaying text. If you setup a couple buffers that are pushed to your display after running the state you only need those functions once. That also gets you some versitility. Maybe have a flag that when set will cause a temp integer printed to one of the buffers. Then copy your working variable to the temp integer within the state and set the flag. I don't think I have every seen a good example of a menu system posted.
mvaraujo



Joined: 20 Feb 2004
Posts: 59
Location: Brazil

View user's profile Send private message

PostPosted: Wed May 19, 2004 7:28 am     Reply with quote

I understand what you guys say...

That's the point, the state machine is simple to understand into the code but the code itself gets big.

One issue that is always complicated to handle is that you need substates to each screen if you want to setup variables of your system. For example, I have a menu chain like this in one of the options:

Main screen >> Config >> Clock/Calendar >> Clock Setup Screen

In this clock setup screen (my state #4), the screen should start with hour blinking, so you can set it up (substate #0), then when you accept, it should blink minutes (substate #1), and so on for day of month, month, year. For each substate the keys don't have similiar actions. My keyboard is only four keys (arrows: up, down, left and right). So I have to write code for each substate and it gets amazingly big. It's easy to follow up and understand but it's large.

I'll post the code to show how it's going until now:

Code:
void maq_estados_disp(void)
// maquina de estados provisoria
{
   int temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8;         // registradores temporarios reservados
   switch (estado_display)
   {
      case 0:      // tela padrao
         subestado_display = 0;                              // subestado em 0
         temp1 = temperat / 10;
         temp2 = temperat - (temp1*10);
         printf(disp_store,"SIPIG-A-v2 %03d.%01u",temp1,temp2);      // monta o display padrao
         if (v_rms < 85 || v_rms > 260 || freq < 45 || freq > 65)
            printf(disp_store,"CA fora de faixa");
         else
            printf(disp_store,"%03lu.%01uV    %02u.%01uHz",v_rms,v_rms_dec,freq,freq_dec);
         rep_tcl = 0x00;                                    // tcl em modo nao repetitivo para todas teclas
         if (buf_tcl.RT == TRUE)                              // se RT pressionado, avanca estado
            estado_display++;
         break;            
      case 1:      // selecao de opcao geral                                    
         switch (subestado_display)
         {
            case 0:
            case 1:
               printf(disp_store,CONFIG_DISP);               // constroi o display de selecao
               if (subestado_display == 0)
                  printf(disp_store,CURSOR);
               else
                  printf(disp_store," ");
               printf(disp_store,TESTE_DISP);
               if (subestado_display == 0)
                  printf(disp_store," ");
               else
                  printf(disp_store,CURSOR);
               if (buf_tcl.LT == TRUE)
               {
                  estado_display--;                        // se LT pressionado, volta estado
                  return;
               }
               if (buf_tcl.DN == TRUE)
               {
                  if (subestado_display < 1)                  // se subestado dentro dos limites
                     subestado_display++;                  // se DN pressionado, avanca subestado
                  return;
               }
               if (buf_tcl.UP == TRUE)
               {
                  if (subestado_display > 0)                  // se subestado dentro dos limites
                     subestado_display--;                  // se UP pressionado, avanca subestado
                  return;
               }
               if (buf_tcl.RT == TRUE)                        // se RT pressionado, avanca estado
               {
                  if (subestado_display == 0)                  // vai para config
                  {
                     estado_display=2;
                     subestado_display=0;
                  }
                  else
                  {
                     //estado_display=3;                     // vai para auto-teste
                     subestado_display=0;
                  }
                  return;
               }
               break;
         }
         break;
      case 2:      // selecao de config
         switch (subestado_display)
         {
            case 0:
            case 1:
               printf(disp_store,LIMIAR);               // constroi o display de selecao
               if (subestado_display == 0)
                  printf(disp_store,CURSOR);
               else
                  printf(disp_store," ");
               printf(disp_store,RELOGIO);
               if (subestado_display == 0)
                  printf(disp_store," ");
               else
                  printf(disp_store,CURSOR);
               
               break;
            case 2:
            case 3:
               printf(disp_store,COMUNIC);               // constroi o display de selecao
               if (subestado_display == 2)
                  printf(disp_store,CURSOR);
               else
                  printf(disp_store," ");
               printf(disp_store,MEMORIA);
               if (subestado_display == 2)
                  printf(disp_store," ");
               else
                  printf(disp_store,CURSOR);
               break;
         }
         if (buf_tcl.LT == TRUE)
         {
            estado_display=1;                        // se LT pressionado, volta estado
            subestado_display=0;
            return;
         }
         if (buf_tcl.DN == TRUE)
         {
            if (subestado_display < 3)                  // se subestado dentro dos limites
               subestado_display++;                  // se DN pressionado, avanca subestado
            return;
         }
         if (buf_tcl.UP == TRUE)
         {
            if (subestado_display > 0)                  // se subestado dentro dos limites
               subestado_display--;                  // se UP pressionado, avanca subestado
            return;
         }
         if (buf_tcl.RT == TRUE)                        // se RT pressionado, avanca estado
         {
            estado_display = subestado + 3;               // aponta para o estado correto
            subestado_display=0;
            return;
         }         
         break;
      case 3:   
         estado_display=0;
         break;
      case 4:      // config do relogio
         break;
      case default:
         estado_display=0;
         break;
   }   
}


The display messages are these by now:

Code:
// Display principal (estado 0)

// 1o Display de escolha (config / autoteste)
#DEFINE CONFIG_DISP      "Config         "               // 15 caracteres
#DEFINE   TESTE_DISP      "Auto-teste     "               // 15 caracteres
#DEFINE CURSOR         "<"                           // 1 caracter

// Selecao no menu de config
#DEFINE   LIMIAR         "Limiares       "               // 15 caracteres
#DEFINE   RELOGIO         "Relogio        "               // 15 caracteres
#DEFINE COMUNIC         "Comunicacao    "               // 15 caracteres
#DEFINE MEMORIA         "Memoria        "               // 15 caracteres
//#DEFINE CURSOR         "<"                           // 1 caracter

// Selecao no menu de limiares
#DEFINE DESCONE         "Desconexao     "               // 15 caracteres
#DEFINE ALARME         "Alarme         "               // 15 caracteres
//#DEFINE CURSOR         "<"                           // 1 caracter

// Selecao no menu de comunicacao
#DEFINE   BAUDRATE      "Baud Rate      "               // 15 caracteres
#DEFINE   ENDERECO      "Endereco       "               // 15 caracteres
//#DEFINE CURSOR         "<"                           // 1 caracter


As you see, for a pretty simple structure of menus, submenus and key actions, long code. And consider that I didn't include all the setup screen for my variables, only the menus and submenus. At least the posted code can be a simple reference of how implementing a menu, I didn't seen anything like these posted.

Give me your comments of the code, perhaps I can optimize stuff in there from your ideas.

Thanks!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group