LCD12864 菜单部分编写

需要创建一个菜单的结构体,以便建立链表

typedef struct menu {  //定义一个菜单
    uint8_t range_from,range_to; //当前显示的项开始及结束序号
    uint8_t itemCount;//项目总数
    uint8_t selected;//当前选择项
    char menuItems[SUBMENUS][17];//菜单项目
    struct menu *subMenus[SUBMENUS];//子菜单
    struct menu *parent;//上级菜单 ,如果是顶级则为null
    int (*func[SUBMENUS])(void);//选择相应项按确定键后执行的函数
    float funcValue[SUBMENUS];
    uint32_t menuId[SUBMENUS];
}Menu;

创建一些基本的函数

/*
 * 添加下级单项菜单函数
 * menu:        菜单指针
 * submenus:    下级单项菜单
 * index:       索引值
 * return:索引值 error:-1  
 */
int Menu_addItem(Menu *menu, Menu * subMenu, uint8_t index) {
    if (menu == NULL) {
        DEBUGOUT("menu is null");
        return -1;
    }

    if (index > menu->itemCount) {
        DEBUGOUT("index too big");
        return -2;
    }

    //menu的下级菜单索引值[i]为subMenu
    menu->subMenus[index] = subMenu; 
    menu->menuId[index+1] = (menu->menuId[0] << 4) | (index + 1);
    subMenu->menuId[0] = menu->menuId[index+1];
    //subMenu的上级菜单为menu
    subMenu->parent = menu;
    //printf("menu->id=%02x, submenu->id=%02x\r\n", menu->menuId[0], subMenu->menuId[0]);
    return index;
}

这个函数是进行下级菜单的插入,进行菜单的深度插入。
同时,将下级菜单的父级菜单绑定到当前菜单上,这样使用返回的时候,可以跳转到本机菜单。



char Menu_addFunc(Menu * menu, int(*fun)(void), uint8_t index) {
    if (menu == NULL) {
        DEBUGOUT("menu is null");
        return 0;
    }

    if (index > menu->itemCount) {
        DEBUGOUT("index is too big");
        return 0;
    }

    if (fun == NULL) {
        DEBUGOUT("function is null");
        return 0;
    }
    //添加调用函数
    menu->func[index] = fun;
    menu->menuId[index+1] = (menu->menuId[0] << 4) | (index + 1);    
    return 1;
}

进行参数判断
把函数绑定到菜单的函数列表中

/*
 * 返回上级菜单
 * 
 * menu:        菜单指针
 */
void Menu_return(Menu * menu) {
    if (menu == 0){
        DEBUGOUT("menu is null");
        return ;
    }
    // LCD清屏操作
    f_LCD_clear = SET;
    if (LCD_TaskFlag & TASK_SET) {
        LCD_TaskFlag &= ~TASK_SET;
    } else {
        //如果有上级菜单的话
        if (menu->parent) {
            currentMenu = menu->parent;
        }
    }
}

会去判断menu->parent是否存在,如果存在,则跳转到上级菜单。
返回上级菜单,从最低级菜单,一层层返回到第高级菜单上。
如果没有上级菜单,则不进行刷新界面。


/*
 * 进入子菜单
 * menu:        菜单指针 
 * index:       子菜单索引
 * 如果存在函数,首先运行函数。
 */
void Menu_enter(Menu * menu, uint16_t index) {
    // LCD清屏操作
    f_LCD_clear = SET;

    //索引值大于项目总数 且 不具有下级子菜单
    if (index >= menu->itemCount 
     || (menu->subMenus[index] == NULL 
     || menu->subMenus == NULL)) {
        //如果存在函数  执行函数
        if (menu->func != NULL && menu->func[index] != NULL) {
            LCD_TaskFlag |= TASK_SET;
            // 执行函数
            (*menu->func[index])();
        }
    } else {
        // 进入子菜单
        currentMenu = menu->subMenus[index];
    }
}

判断当前选中菜单是否大于总数 && 下级菜单列表是否为空 && 选中菜单项是否有下级菜单
如果不满足,则进入函数选择分支
判断当前选中函数列表是否为空 当前选中函数指针是否为空
如果有可执行函数,置函数标志位LCD_TaskFlag |= TASK_SET;
接下来直接执行函数(*menu->func[index])();
如果不满足上述条件,则说明有下级菜单。

进行显示部分说明

//LCD显示函数重定义
#define LCD_displayStr       my_LCD_Display_String    
#define LCD_displayStrRev    my_LCD_Display_String_Reverse
#define LCD_displayChar      my_LCD_Display_Char
#define LCD_displayCharRev   my_LCD_Display_Char_Reverse
#define LCD_Clear            my_LCD_Display_Clear

这些函数在display.c中直接定义,如果需要在LCD屏幕上实现,则只需要实现这几个函数就可以


void my_LCD_Display_String(unsigned char x, unsigned char y, char *displayStr) {
    uint8_t col, len;

    for (col = 0;col < strlen(displayStr);col++) {
        LCD_Screen[x][y+col] = displayStr[col];
    }
}

void my_LCD_Display_String_Reverse(unsigned char x, unsigned char y, char *displayStr) {
    uint8_t col, len;

    for (col = 0;col < strlen(displayStr);col++) {
        LCD_Screen[x][y+col] = displayStr[col];
    }
}
void my_LCD_Display_Clear(void) {
    uint8_t row;
    if (!f_LCD_clear) {
        return ;
    }
    for (row = 0;row < 4;row++) {
        my_LCD_Display_String(row, 0, MSG_LCD_NULL);
    }
}

声明一个数组LCD_Screen,用来存放需要显示的字符,共分为4行16列
显示时,显示LCD_Screen的数据即可。


#define FOREGROUND_WHITE    (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED)
#define BACKGROUND_WHITE    (BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_RED)

#define normal_display(x, y) \
    do { \
        pos.X = 50+y;\
        pos.Y = 10+x;\
        SetConsoleCursorPosition(hOut, pos); \
        SetConsoleTextAttribute(hOut, BACKGROUND_WHITE);\
    } while (0)

#define rev_display(x, y) \
    do { \
        pos.X = 50+y;\
        pos.Y = 10+x;\
        SetConsoleCursorPosition(hOut, pos); \
        SetConsoleTextAttribute(hOut, BACKGROUND_GREEN);\
    } while (0)

void LCD_Dispaly(void) {
    uint8_t row = 0, col = 0;

    for (row = 0;row < 4;row++) {
        if (row != currentMenu->selected) {
            for (col = 0;col < 16;col++) {
                normal_display(row, col);
                dchar(LCD_Screen[row][col]);
            }
        } else {
            for (col = 0;col < 16;col++) {
                rev_display(row, col);
                dchar(LCD_Screen[row][col]);
            }
        }
    }
}

normal_display() 是进行正常显示
rev_display() 是进行反向显示
因为我是在上位机C语言环境下进行调试,所以这个是可以在控制台上输出正向和反向打印。

进行一些初始化

/*
 * 以下程序,可以放置在本文件外部
 * 属于本菜单程序实例 
 */


//一级菜单
Menu fMenu = {
    // form, to, count, selected
    0, 3, 4, 0, 
    // items string
    {
        "1",
        "2",
        "3",
        "4",
    },
};

// 二级菜单
Menu Menu_Level = {
    // form, to, count, selected
    0, 2, 3, 0, 
    // items string
    {
        "11",
        "12",
        "13",
    },
};

Menu Menu_Temp = {
    // form, to, count, selected
    0, 0, 1, 0, 
    // items string
    {
        "21",
    },
};
Menu Menu_Alarm = {
    // form, to, count, selected
    0, 1, 2, 0, 
    // items string
    {
        "211",
        "212",
    },
};

//int LCD_displayLevelHigh(void) {
//  char s[20];
//  sprintf(s, "   %.0f", LCD_DisplayTempValue);
//  LCD_displayStr(1, 0, s);
//} 
#define GET_LCD_FUNC_NAME(name)    (LCD_display##name)

#define LCD_CREATE_DISPLAY_FUNC(name, title, unit) \
static int LCD_display##name(void) { \
    char s[20]; \
    LCD_displayStr(0, 0, #title); \
    sprintf(s, "%.2f %s", LCD_DisplayTempValue, #unit); \
    LCD_displayStr(1, 3, s); \
}
LCD_CREATE_DISPLAY_FUNC(LevelHigh, level, mm);

void menu_init(void) {
    Menu_addFunc(&Menu_Level, GET_LCD_FUNC_NAME(LevelHigh), 0);
    Menu_addItem(&fMenu, &Menu_Level, 0);
    Menu_addItem(&fMenu, &Menu_Temp, 1);
    Menu_addItem(&fMenu, &Menu_Alarm, 2);   
    currentMenu = &fMenu;
}

此部分主要是进行菜单的初始化和对第二级菜单的绑定
利用宏,进行方便的创建显示函数,和获取显示函数的名字
将显示函数绑定到指定的菜单项上
currentMenu = &fMenu; 将第一级菜单作为当前菜单进行显示。
currentMenu是当前可以操作的菜单


#define TASK_SET        0x0001  // 设置界面
#define TASK_ROOT       0x0002  // 登陆用户状态 0:user 1:root
#define TASK_DFP        0x0004  // 显示界面状态(DFP:displayFirstPage) 0:首页 1:菜单项
#define TASK_GPRS       0x0008  // GPRS发送状态 0:不可以发送 1:可以发送
#define TASK_SW         0x0010  // 电磁阀开启状态:0:没有开启 1:开启
#define TASK_LOGIN      0x0020  // 登陆状态 0:未登录 1:已登录

需要有一些显示控制位,来表达当前显示任务处于什么样的显示阶段。

进行实验

本代码是在win10上使用c-free5进行调试,可以正常执行,后续还会对这个代码进行修改和优化,如果看到有问题的地方,希望指出来,帮助改正

资源放在CSDN上了,由于资源分最低2分,所以就放了两分,如果没有分的同学,可以去百度云上下载。

CSDN链接
百度云链接
上:上翻页
下:下翻页
左:返回上级菜单或者退出函数显示
右:进入下级菜单或者进入函数显示

开始执行
第二级菜单
函数显示

猜你喜欢

转载自blog.csdn.net/jeek_we/article/details/79633842