基于单片机的LCD菜单设计

 

在小型的单片机设备中,需要使用LCD菜单进行人机交互。对于这种交互的实现,一来无需像在手机上做APP一样,做出花哨的界面效果;二来这种小设备也不支持那样太消耗资源的界面系统。所以这种场合下,需要我们亲自使用C语言去编写一套精简的界面框架代码。

 

  • 定义菜单项的数据类型

一个菜单项,有自身的唯一标志码ID,和显示的字符串ItemDisplayStr,和选中它时执行的函数ItemFun。但仅仅这样还不够,因为很多菜单项之间是有逻辑关联的。在一个菜单项下点击跟进、回退,都会转到不同的页面。所以还需要有表示菜单项逻辑关系的父菜单项ItemFather、子菜单项ItemChildren、下一个菜单项ItemNext、上一个菜单项ItemLast。下文给出了示意图。

 

 

 

  • 实现菜单项机制

要实现菜单项机制,需要实现四个基础功能:

1,菜单项跟进

2,菜单项回退

3,菜单项上选

4,菜单项下选

 

为了设计这些基本功能,先定义一个起示范作用的菜单组织。理论上,每个菜单项的ID可以随意制定。但实际上,为了便于管理,可规定,菜单项编号为12位数字。前4位为菜单级数,中4位为父菜单在列表中的次序(无父菜单则为0),后四位为本菜单在列表中的次序。这种规定限制了一个父菜单项在同一级下最多挂9999个子菜单项,整个菜单体最多不超过9999级深度。不过一般情况下这完全足够了。下图给出了一个示范性菜单,方框内注明了菜单的显示字符和唯一ID。

 

 

根据这种菜单的组织图,可以知道:

  1. 菜单跟进,其实就是从所有的菜单栏目中找到和目前菜单项的ItemChildren一致的菜单项ID,就得到了子菜单项。菜单项上选、下选、回退同理。

 

 

  • 定义菜单数据类型

一个菜单,是由若干个菜单项构成的。我们需要定义一个菜单类型,来管理菜单项列表等内容。注意菜单和菜单项的区别:菜单项只是单个单个的条目,而菜单是管理若干个菜单项的类型。下图解释了这一区别。菜单类型由两个成员组成,一个是菜单项列表的起始地址st_lcd_item_list,一个是当前菜单项的地址st_lcd_item_current。

 

其实这样已经可以完成界面的功能了。但这样还不够完美,因为某些美观上的需求,比如:

1,菜单回退的时候要能回到先前的箭头位置,“恢复现场”。

2,菜单在某一级时,要显示出这一级的菜单列表。如果LCD行数不够,需要翻页显示。

 

对于需求1,推荐的做法是设计一个栈,每跟进就压栈,每回退就出栈,即把现场使用栈的形式保存起来。现场的主要内容是箭头位置。

对于需求2,循环搜索菜单项的ItemLast和ItemNext,直到到达LCD顶部和底部即可停止搜索。

 

为了在电脑上模拟,假设有一个函数LCD_OnxyPrint可以在指定坐标显示字符串。把下图红色方框当做一个LCD 128*64的液晶,可见实现了菜单功能。

 

 

 

 

 

 

 

 

 

 

源代码分两个文件,LCD_Demo.h和LCD_Demo.c,如下:

 

/*---------------------------------LCD_Demo.h----------------------------------*/

#ifndef __LCD_DEMO_H__
#define    __LCD_DEMO_H__


//液晶屏尺寸参数
#define LCD_LINE_START        0
#define LCD_LINE_END        3
#define LCD_COLUMN_START    0
#define LCD_COLUMN_END        15 


#define STACK_ARROW_SIZE    256
 

//-------------菜单项-----------------------
typedef void *(*ItemFun_Typedef )(void *);

typedef struct ST_LCD_ITEM
{
    int ItemID;

    int ItemLast;
    int ItemNext;
    int ItemFather;
    int ItemChildren;


    
    char *ItemDisplayStr;
    ItemFun_Typedef ItemFun;

}ST_LCD_ITEM;


//-------------菜单 -----------------------
typedef struct ST_LCD_MENU
{
    ST_LCD_ITEM* st_lcd_item_list;
    ST_LCD_ITEM* st_lcd_item_current;

 

}ST_LCD_MENU;


//-------------记忆栈--------------------

typedef struct ST_STACK
{
    
    int* ArrowStackBufferStart;
    int* ArrowStackBufferEnd;

    int* ArrowStackTop;


}ST_STACK;

 
extern void    MENU_ItemInitSet(ST_LCD_MENU* st_lcd_menu,ST_LCD_ITEM st_lcd_item[ ]);
 


extern ST_LCD_ITEM* MENU_Enter(ST_LCD_MENU* st_lcd_menu);
extern ST_LCD_ITEM* MENU_Exit(ST_LCD_MENU* st_lcd_menu);
extern ST_LCD_ITEM* MENU_Next(ST_LCD_MENU* st_lcd_menu);
extern ST_LCD_ITEM* MENU_Last(ST_LCD_MENU* st_lcd_menu);

#endif

 

 

/*-----------------------------------LCD_Demo.c---------------------------------*/

#include <stdio.h>
#include <windows.h>
#include <conio.h>

#include "LCD_Demo.h"


void LCD_OnxyPrint(char *buf, int startX, int startY)
{
    HANDLE hd;
    COORD pos;
    
    pos.X = startX;
    pos.Y = startY;
    hd = GetStdHandle(STD_OUTPUT_HANDLE);    /*获取标准输出的句柄*/ 
    SetConsoleCursorPosition(hd, pos);        /*设置控制台光标输出的位置*/
    printf("%s",buf );
}
void LCD_Clear()
{
    int i,j;
    for(i=LCD_COLUMN_START;i<=LCD_COLUMN_END;i++)
        for(j=LCD_LINE_START;j<=LCD_LINE_END;j++)
        {
            LCD_OnxyPrint(" ", i, j);
            
        }


}

void PrintScore(ST_LCD_MENU* st_lcd_menu)
{
    LCD_OnxyPrint("PrintScore:您的分数:100 ", 0, 5);

}

 

ST_LCD_ITEM st_lcd_item[ ] = 
{
        {000100000001,000000000000,000000000000,000000000000,000200010001,"学生成绩",    MENU_Enter},
        {000200010001,000000000000,000200010002,000100000001,000300010001,"英语",        MENU_Enter},

        {000300010001,000000000000,000300010002,000200010001,000000000000,"分数",        PrintScore},
        {000300010002,000300010001,000300010003,000200010001,000000000000,"排名",        NULL},
        {000300010003,000300010002,000300010004,000200010001,000000000000,"档位",        NULL},
        {000300010004,000300010003,000300010005,000200010001,000000000000,"是否及格",    NULL},
        {000300010005,000300010004,000000000000,000200010001,000000000000,"是否优秀",    NULL},


        {000200010002,000200010001,000200010003,000100000001,000300020001,"数学",        MENU_Enter},
        {000300020001,000000000000,000300020002,000200010002,000000000000,"分数",        NULL},
        {000300020002,000300020001,000000000000,000200010002,000000000000,"排名",        NULL},

        {000200010003,000200010002,000000000000,000100000001,000000000000,"语文",        NULL},
        {000000000000,000000000000,000000000000,000000000000,000000000000,"NULL",        NULL},


};

void MENU_ItemInitSet(ST_LCD_MENU* st_lcd_menu,ST_LCD_ITEM st_lcd_item[ ] )
{
    st_lcd_menu->st_lcd_item_list = &st_lcd_item[ 0];
    st_lcd_menu->st_lcd_item_current = &st_lcd_item[ 0];
 

}

ST_LCD_ITEM* MENU_ItemSearch(ST_LCD_MENU* st_lcd_menu,int ItemID)
{    
    
    int i = 0;
    ST_LCD_ITEM* pItem = NULL;

    if(ItemID == 0)
    {
        return pItem;
    }

    while(st_lcd_menu->st_lcd_item_list[ i ].ItemID != 0)
    {
        if(st_lcd_menu->st_lcd_item_list[ i ].ItemID == ItemID)
        {
            pItem = &st_lcd_menu->st_lcd_item_list[ i ];
            return pItem;
         
            
        }
        i++;
    
    }
    
 
    return pItem;

}

ST_LCD_ITEM* MENU_Trace(ST_LCD_MENU* st_lcd_menu,int item_id)
{
    ST_LCD_ITEM* pItem = NULL;
    
    pItem= MENU_ItemSearch( st_lcd_menu,item_id);

    if(pItem!=NULL )
    {
        st_lcd_menu->st_lcd_item_current = pItem;
    }
    return pItem;

}

ST_LCD_ITEM* MENU_Enter(ST_LCD_MENU* st_lcd_menu)
{
    return MENU_Trace(st_lcd_menu,st_lcd_menu->st_lcd_item_current->ItemChildren);
}
ST_LCD_ITEM* MENU_Exit(ST_LCD_MENU* st_lcd_menu)
{
    return MENU_Trace(st_lcd_menu,st_lcd_menu->st_lcd_item_current->ItemFather);
}
ST_LCD_ITEM* MENU_Next(ST_LCD_MENU* st_lcd_menu)
{
    return MENU_Trace(st_lcd_menu,st_lcd_menu->st_lcd_item_current->ItemNext);
}
ST_LCD_ITEM* MENU_Last(ST_LCD_MENU* st_lcd_menu)
{
    return MENU_Trace(st_lcd_menu,st_lcd_menu->st_lcd_item_current->ItemLast);
}


//记住上一次箭头位置用的栈

void ST_STACK_Init(ST_STACK* stack,int* buffer_start,int* buffer_end)
{

    stack->ArrowStackBufferStart = buffer_start;
    stack->ArrowStackBufferEnd = buffer_end;
    stack->ArrowStackTop = buffer_start;

}

void ST_STACK_DataIn(ST_STACK* stack,int Data)
{

    if(stack->ArrowStackTop<= stack->ArrowStackBufferEnd)
    {
        *(stack->ArrowStackTop) = Data;
        (stack->ArrowStackTop)++;

    }
    

}
int ST_STACK_DataOut(ST_STACK* stack )
{
    if(stack->ArrowStackTop > stack->ArrowStackBufferStart)
    {
        (stack->ArrowStackTop)--;
        return    *(stack->ArrowStackTop) ;
    }
    else
    {
        return  LCD_LINE_START;
    }
 

}


void main()
{
    char key=0;
    int i=0;
    int ArrowLine = LCD_LINE_START;
    ST_LCD_ITEM* p;
    int item_stack_buffer[STACK_ARROW_SIZE];
    ST_STACK item_stack;


    ST_LCD_MENU menu;
    ST_LCD_MENU menu_Dis;
    MENU_ItemInitSet(&menu,st_lcd_item );
    MENU_ItemInitSet(&menu_Dis,st_lcd_item );
    ST_STACK_Init(&item_stack,&item_stack_buffer[0],&item_stack_buffer[STACK_ARROW_SIZE-1]);
    
    

    while(1)
    {

        if(kbhit() != 0)
        {
            key = getch();

        //    printf("\nKey = %c \n",key);

            if(key == 'w')
            {
                if(NULL != MENU_Last(&menu))
                {
                    if(ArrowLine>LCD_LINE_START) ArrowLine--;
                    
                }

                
            
            }
            if(key == 's')
            {
                if(NULL!=MENU_Next(&menu))
                {
                    if(ArrowLine<LCD_LINE_END) ArrowLine++;
                }
                

            
            }
            if(key == 'a')
            {
                MENU_Exit(&menu);
                ArrowLine = ST_STACK_DataOut(&item_stack );
            
            }
            if(key == 'd')
            {
            //    MENU_Enter(&menu);
                if(menu.st_lcd_item_current->ItemFun == MENU_Enter)
                {
                    if(NULL!=menu.st_lcd_item_current->ItemFun(&menu))
                    {

                        ST_STACK_DataIn(&item_stack,ArrowLine);

                        ArrowLine = LCD_LINE_START;
                    }
                    
                
                }

                else if(menu.st_lcd_item_current->ItemFun!=NULL)
                {
                    menu.st_lcd_item_current->ItemFun(&menu);
                
                }
                
                
            
            }

 
            //把菜单页面显示在屏幕上
            LCD_Clear();
            
            menu_Dis = menu;
            LCD_OnxyPrint("◆",LCD_COLUMN_START, ArrowLine);
            LCD_OnxyPrint(menu_Dis.st_lcd_item_current->ItemDisplayStr,LCD_COLUMN_START+2, ArrowLine);

            
            for(i=ArrowLine-1;i>=LCD_LINE_START;i--)
            {            
                    
                    p=MENU_Last(&menu_Dis);
                    if(p!=NULL)
                    {    LCD_OnxyPrint("  ",LCD_COLUMN_START, i);
                        LCD_OnxyPrint(p->ItemDisplayStr,LCD_COLUMN_START+2, i);
                    }
                    
            }
            menu_Dis = menu;
            for(i=ArrowLine+1;i<=LCD_LINE_END;i++)
            {
                    
                    p = MENU_Next(&menu_Dis);
                    if(p!=NULL)
                    {    LCD_OnxyPrint("  ",LCD_COLUMN_START, i);
                        LCD_OnxyPrint(p->ItemDisplayStr,LCD_COLUMN_START+2, i);
                    }
                     
            }
        
        }

    
    }

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值