Arduino 怎么写多级菜单
展开全部
本帖最后由 qhdtc5 于 2015-3-25 08:00 编辑
说明:
使用了LCD5110作为显示屏,库为LCD5110_Basic
借鉴了其他程序的一些思路,用链表作为菜单结构,并使用Action概念来确定菜单项的动作
虽然动作类型设想有好几种,但我只写了设置逻辑值的菜单动作,其他的可自行完善(我也会继续写完)
代码未优化和未考虑内存占用等问题
请大家指教
先上图片和视频
[media]http://player.youku.com/player.php/sid/XOTE5MjkyOTg4/v.swf[/media]
代码:
[mw_shl_code=bash,true]#include <Wire.h>
#include <DS3231.h>
#include <LCD5110_Basic.h>
#define LIGHT_PIN 3
#define LED_PIN 13
// 菜单最大显示行数
#define MENU_MAX_ROW 5
/**
* 以下定义菜单项类型
*/
// 具有子菜单的菜单项
#define MENU_SUBMENU 0
// 参数项(用于执行参数设置)
#define MENU_PARAM 1
// 无动作的菜单项
#define MENU_ACTION_NONE 101
// 执行逻辑参数设置的菜单项(开/关、真/假等设置)
#define MENU_ACTION_LOGIC 102
// 执行数值调整设置的菜单项(时间、音量等设置)
#define MENU_ACTION_NUMBER 103
// 执行字符串设置的菜单项(欢迎语、LED显示文字等设置)
#define MENU_ACTION_STRING 104
/**
* 以下定义按键引脚
* 设置上拉电阻,低电平有效
*/
#define KEY_UP 7
#define KEY_DOWN 6
#define KEY_ENTER 5
#define KEY_EXIT 4
// 定义按键消抖延时时间
#define KEY_TREMBLE_TIME 20
// DS3231时钟变量
DS3231 clock;
// LCD5110液晶屏变量
LCD5110 myGLCD(8, 9, 10, 11, 12);
// LCD5110使用的字体
extern unsigned char SmallFont[];
/**
* 菜单结构
* 一个数组既代表一级菜单,数组中的一个元素就是一个菜单项,就是一个单独的结构体,
* 数组中的第一个元素中的num值很重要,表示本级菜单具有多少个菜单项。
*
* @var int num 本级菜单数量,必须在第一项中设置正确的菜单项数量
* @var char* label 菜单文本
* @var int type 此项类型,参考宏定义
* @var void (*action)(const char *) 指向动作的函数指针,此项要执行的具体动作函数
* @var mymenu* next 下一级菜单,只需在菜单的第一项设置,其他项置空(NULL)即可
* @var mymenu* prev 上一级菜单,同上
*/
struct mymenu {
int num;
char *label; //display label
int type; //0:item, 1:submenu, 2:param
void (*action)(const char *);
mymenu * next;
mymenu * prev;
};
/**
* 逻辑参数设置菜单
* 逻辑菜单表示菜单项中的action函数要执行逻辑设置动作,即设置某个参数的逻辑值
* 逻辑菜单只需两个菜单项代表true和false
* 需要遵守的规则为:菜单中的文本需要设置为“ON”和“OFF”,
* 在执行动作函数的时候,可以将正确的参数传递过去,
* 动作函数的规则参照函数说明
*/
mymenu logic_menu[2] = {
{2, "ON", MENU_PARAM, NULL, NULL, NULL},
{2, "OFF", MENU_PARAM, NULL, NULL, NULL}
};
/**
* 下面定义了三级菜单说明了菜单应该如何个定义
*/
// 第二级菜单
mymenu light_menu[2] = {
//第一项什么也不做,所以设置了类型为MENU_ACTION_NONE
{2, "light 1", MENU_ACTION_NONE, NULL, NULL, NULL},
//第二项指向了下级菜单,所以设置了类型为MENU_SUBMENU
{2, "light submenu", MENU_SUBMENU, NULL, NULL, NULL}
};
// 第三级菜单
mymenu test_level3_menu[7] = {
{7, "level3menu-1", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-2", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-3", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-4", MENU_ACTION_NONE, NULL, NULL, NULL},
//定义了一个逻辑动作,这里是控制PIN13脚的LED
{7, "LED TEST", MENU_ACTION_LOGIC, NULL, NULL, NULL},
{7, "level3menu-6", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-7", MENU_ACTION_NONE, NULL, NULL, NULL}
};
// 第一级菜单
mymenu main_menu[4] = {
{4, "item 1", MENU_ACTION_NONE, NULL, NULL, NULL},
{4, "item 2", MENU_ACTION_NONE, NULL, NULL, NULL},
//指向下一级菜单
{4, "item 2.1", MENU_SUBMENU, NULL, NULL, NULL},
{4, "item 3", MENU_ACTION_NONE, NULL, NULL, NULL}
};
// 定义菜单操作需要的全局变量
// 分别为当前菜单、上一级菜单、当前菜单项索引和开始显示的菜单项索引
mymenu *cur_item, *prev_item;
int item_index, start_index;
bool stat;
/**
* DS3231需要的全局变量
*/
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;
int second,minute,hour,date,month,year,temperature;
int oldsecond; //此变量用于判断是否更新显示内容
// 定义LCD背光显示计时变量,无按键操作超时就关闭背光
unsigned long starttime;
// 定义菜单的操作按键(上、下、进入和返回)状态变量
bool old_up_stat, old_down_stat, old_enter_stat, old_exit_stat;
void setup() {
//设置LCD背光引脚为输出
pinMode(LIGHT_PIN, OUTPUT);
//设置按键为上拉电阻输入(低电平有效)
for (int i=4; i<=7; i++){
pinMode(i, INPUT_PULLUP);
}
//初始化时打开LCD背光
backlight("ON");
stat = false;
//初始化LCD5110
myGLCD.InitLCD();
myGLCD.setContrast(127);
myGLCD.setFont(SmallFont);
myGLCD.print("Initialize...", 0, 0);
/**
* 菜单的进一步设置
* 在这里将每一个菜单的关联设置好
* 对照每一个初始设置仔细填写它们之间的关系
*/
//第一级(main_menu)的第三项指向了下一级菜单(light_menu)
main_menu[2].next = light_menu;
//第二级(light_menu)的上一级(main_menu)
light_menu[0].prev = main_menu;
//第二级(light_menu)的第二项指向了下一级菜单(test_level3_menu)
light_menu[1].next = test_level3_menu;
//第三级(test_level3_menu)的上一级(light_menu)
test_level3_menu[0].prev = light_menu;
//第三级(test_level3_menu)的第五项定义了个逻辑动作
test_level3_menu[4].action = ledtest;
/**
* 初始化当前菜单为第一级(main_menu)
*/
cur_item = main_menu;
/**
* 上一级菜单为空
*/
prev_item = NULL;
/**
* 当前选择了第一项
*/
item_index = 0;
/**
* 从第一项开始显示菜单
*/
start_index = 0;
/**
* 设置DS3231的日期和时间,只需在需要调整的时候执行一次
*/
//clock.setClockMode(true);
//I2C总线库初始化
Wire.begin();
//一些状态变量的初始化
oldsecond = -1;
old_up_stat = HIGH;
old_down_stat = HIGH;
old_enter_stat = HIGH;
old_exit_stat = HIGH;
//为了视频中能看清楚,延时定义了3秒
delay(3000);
//所有初始化完成,清屏并关闭LCD背光
myGLCD.clrRow(0);
backlight("OFF");
starttime = millis();
}
void loop() {
//获取运行时间
unsigned long endtime = millis();
//获取当前菜单的项目数量
int menu_num = cur_item[0].num;
//定义一个临时变量
int idx;
//绘制菜单
renderMenu(&cur_item[0], menu_num>MENU_MAX_ROW ? MENU_MAX_ROW : menu_num);
//读取按键
int k_down = digitalRead(KEY_DOWN);
int k_up = digitalRead(KEY_UP);
int k_enter = digitalRead(KEY_ENTER);
int k_exit = digitalRead(KEY_EXIT);
//计算无按键时间,决定是否关闭LCD背光
if (endtime-starttime>5000) {
//myGLCD.enableSleep();
backlight("OFF");
}
//有键按下,打开LCD背光,计时清零
if (k_up==LOW || k_down==LOW || k_enter==LOW || k_exit==LOW) {
//myGLCD.disableSleep();
backlight("ON");
starttime = endtime;
}
//检测按键,进入相应动作
if (k_up == LOW && k_up!=old_up_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_up == LOW){
item_index --;
}
} else if (k_down == LOW && k_down!=old_down_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_down == LOW){
item_index ++;
}
} else if (k_enter == LOW && k_enter!=old_enter_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_enter == LOW){
//计算此时的菜单项索引值
idx = start_index+item_index;
if (cur_item[idx].next != NULL && cur_item[idx].type == MENU_SUBMENU){
//条件成立说明此菜单项指向了下一级菜单
//此级菜单变成了上一级菜单
prev_item = cur_item;
//将指向的下一级菜单设置为当前菜单
cur_item = cur_item[idx].next;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
//清屏
myGLCD.clrScr();
} else if (cur_item[idx].action != NULL && cur_item[idx].type != MENU_PARAM){
//条件成立说明此项菜单是动作
//此级菜单变成上一级菜单
prev_item = cur_item;
//根据动作类型调用相应的下一级菜单
switch(cur_item[idx].type){
case MENU_ACTION_LOGIC:
//将动作函数传递给逻辑菜单,使逻辑菜单能够正确执行动作
logic_menu[0].action = cur_item[idx].action;
//设置当前菜单为逻辑菜单
cur_item = logic_menu;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
break;
case MENU_ACTION_NUMBER:
break;
case MENU_ACTION_STRING:
break;
default:
break;
}
//清屏
myGLCD.clrScr();
} else if (cur_item[idx].type == MENU_PARAM){
//条件成立说明正在执行动作
//调用相应的动作函数,并传递参数(这里只举例逻辑设置)
cur_item[0].action((const char *)cur_item[idx].label);
}
}
} else if (k_exit == LOW && k_exit!=old_exit_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_exit == LOW){
//返回上一级菜单的操作
if (prev_item != NULL){
//设置上一级菜单为当前菜单
cur_item = prev_item;
//设置当前菜单的上一级菜单
prev_item = cur_item[0].prev;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
//清屏
myGLCD.clrScr();
}
}
}
/**
* 菜单项上下选择是否越界
*/
if (item_index<0){
item_index = 0;
start_index --;
if (start_index<0) start_index = 0;
}
if (item_index>=MENU_MAX_ROW || item_index>=menu_num){
if (item_index>=menu_num) item_index = menu_num-1;
if (item_index>=MENU_MAX_ROW) item_index = MENU_MAX_ROW-1;
if (start_index+MENU_MAX_ROW<menu_num) start_index ++;
}
//保存按键状态
old_up_stat = digitalRead(KEY_UP);
old_down_stat = digitalRead(KEY_DOWN);
old_enter_stat = digitalRead(KEY_ENTER);
old_exit_stat = digitalRead(KEY_EXIT);
//renderClock(0, 0);
}
/**
* 关闭LCD背光
* 此函数符合菜单动作定义规则,即无返回值、一个字符串参数
*
* @var const char* stat 值为“ON"打开背光,为“OFF”关闭背光
*/
void backlight(const char *stat)
{
//这里定义了一个逻辑动作
//根据参数决定LCD背光的开关
digitalWrite(LIGHT_PIN, strcmp(stat,"ON") ? LOW : HIGH);
}
/**
* 逻辑动作测试函数
*
* @var const char* stat 根据此参数来执行动作,这里测试PIN13的LED
*/
void ledtest(const char *stat)
{
//根据参数决定LED开关
digitalWrite(LED_PIN, strcmp(stat,"ON") ? LOW : HIGH);
}
/**
* 绘制单个菜单项
*
* @var struct mymenu item 要绘制的项目
* @var int row 绘制坐标(LCD5110绘制文字时只需一个行坐标即可,可根据实际显示设备调整)
* @var bool rev 是否反相绘制,默认为不反相绘制(用于当前选中项绘制,可根据实际显示设备调整)
*/
void renderItem(struct mymenu item, int row, bool rev = false)
{
char label[15];
//规范显示项目文字(左对齐,补空格)
sprintf(label, "%-14s", item.label);
if (rev){
//打开5110的反相绘制功能
myGLCD.invertText(true);
}
//绘制菜单项
myGLCD.print(label, 0, row);
if (rev){
//关闭5110的反相绘制功能
myGLCD.invertText(false);
}
}
/**
* 绘制某一级菜单
*
* @var struct mymenu* items 需要绘制的菜单
* @var int menu_num 菜单项目数量
*/
void renderMenu(struct mymenu *items, int menu_num)
{
//绘制数量不能超过每一屏的最大绘制数量
int num = menu_num>MENU_MAX_ROW ? MENU_MAX_ROW : menu_num;
for (int i=0; i<num; i++){
//绘制每个菜单项
renderItem(items[i+start_index], i*8, i==item_index ? true:false);
}
}
void renderClock(int row)
{
char s[12];
second = clock.getSecond();
minute = clock.getMinute();
hour = clock.getHour(h12, PM);
date = clock.getDate();
month = clock.getMonth(Century);
year = clock.getYear();
temperature = clock.getTemperature();
if (oldsecond!=second){
sprintf(s, "20%02d-%02d-%02d", year, month, date);
myGLCD.print(s, 0, row);
sprintf(s, "%02d:%02d:%02d", PM==0 ? hour:hour+12, minute, second);
myGLCD.print(s, 0, row+8);
sprintf(s, "%3d", sizeof(main_menu)/sizeof(mymenu));
myGLCD.print(s, 0, row+16);
oldsecond = second;
}
}
void clockSetting(int year, int month, int day, int hour, int minute, int second)
clock.setSecond(second);//配置秒
clock.setMinute(minute
说明:
使用了LCD5110作为显示屏,库为LCD5110_Basic
借鉴了其他程序的一些思路,用链表作为菜单结构,并使用Action概念来确定菜单项的动作
虽然动作类型设想有好几种,但我只写了设置逻辑值的菜单动作,其他的可自行完善(我也会继续写完)
代码未优化和未考虑内存占用等问题
请大家指教
先上图片和视频
[media]http://player.youku.com/player.php/sid/XOTE5MjkyOTg4/v.swf[/media]
代码:
[mw_shl_code=bash,true]#include <Wire.h>
#include <DS3231.h>
#include <LCD5110_Basic.h>
#define LIGHT_PIN 3
#define LED_PIN 13
// 菜单最大显示行数
#define MENU_MAX_ROW 5
/**
* 以下定义菜单项类型
*/
// 具有子菜单的菜单项
#define MENU_SUBMENU 0
// 参数项(用于执行参数设置)
#define MENU_PARAM 1
// 无动作的菜单项
#define MENU_ACTION_NONE 101
// 执行逻辑参数设置的菜单项(开/关、真/假等设置)
#define MENU_ACTION_LOGIC 102
// 执行数值调整设置的菜单项(时间、音量等设置)
#define MENU_ACTION_NUMBER 103
// 执行字符串设置的菜单项(欢迎语、LED显示文字等设置)
#define MENU_ACTION_STRING 104
/**
* 以下定义按键引脚
* 设置上拉电阻,低电平有效
*/
#define KEY_UP 7
#define KEY_DOWN 6
#define KEY_ENTER 5
#define KEY_EXIT 4
// 定义按键消抖延时时间
#define KEY_TREMBLE_TIME 20
// DS3231时钟变量
DS3231 clock;
// LCD5110液晶屏变量
LCD5110 myGLCD(8, 9, 10, 11, 12);
// LCD5110使用的字体
extern unsigned char SmallFont[];
/**
* 菜单结构
* 一个数组既代表一级菜单,数组中的一个元素就是一个菜单项,就是一个单独的结构体,
* 数组中的第一个元素中的num值很重要,表示本级菜单具有多少个菜单项。
*
* @var int num 本级菜单数量,必须在第一项中设置正确的菜单项数量
* @var char* label 菜单文本
* @var int type 此项类型,参考宏定义
* @var void (*action)(const char *) 指向动作的函数指针,此项要执行的具体动作函数
* @var mymenu* next 下一级菜单,只需在菜单的第一项设置,其他项置空(NULL)即可
* @var mymenu* prev 上一级菜单,同上
*/
struct mymenu {
int num;
char *label; //display label
int type; //0:item, 1:submenu, 2:param
void (*action)(const char *);
mymenu * next;
mymenu * prev;
};
/**
* 逻辑参数设置菜单
* 逻辑菜单表示菜单项中的action函数要执行逻辑设置动作,即设置某个参数的逻辑值
* 逻辑菜单只需两个菜单项代表true和false
* 需要遵守的规则为:菜单中的文本需要设置为“ON”和“OFF”,
* 在执行动作函数的时候,可以将正确的参数传递过去,
* 动作函数的规则参照函数说明
*/
mymenu logic_menu[2] = {
{2, "ON", MENU_PARAM, NULL, NULL, NULL},
{2, "OFF", MENU_PARAM, NULL, NULL, NULL}
};
/**
* 下面定义了三级菜单说明了菜单应该如何个定义
*/
// 第二级菜单
mymenu light_menu[2] = {
//第一项什么也不做,所以设置了类型为MENU_ACTION_NONE
{2, "light 1", MENU_ACTION_NONE, NULL, NULL, NULL},
//第二项指向了下级菜单,所以设置了类型为MENU_SUBMENU
{2, "light submenu", MENU_SUBMENU, NULL, NULL, NULL}
};
// 第三级菜单
mymenu test_level3_menu[7] = {
{7, "level3menu-1", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-2", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-3", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-4", MENU_ACTION_NONE, NULL, NULL, NULL},
//定义了一个逻辑动作,这里是控制PIN13脚的LED
{7, "LED TEST", MENU_ACTION_LOGIC, NULL, NULL, NULL},
{7, "level3menu-6", MENU_ACTION_NONE, NULL, NULL, NULL},
{7, "level3menu-7", MENU_ACTION_NONE, NULL, NULL, NULL}
};
// 第一级菜单
mymenu main_menu[4] = {
{4, "item 1", MENU_ACTION_NONE, NULL, NULL, NULL},
{4, "item 2", MENU_ACTION_NONE, NULL, NULL, NULL},
//指向下一级菜单
{4, "item 2.1", MENU_SUBMENU, NULL, NULL, NULL},
{4, "item 3", MENU_ACTION_NONE, NULL, NULL, NULL}
};
// 定义菜单操作需要的全局变量
// 分别为当前菜单、上一级菜单、当前菜单项索引和开始显示的菜单项索引
mymenu *cur_item, *prev_item;
int item_index, start_index;
bool stat;
/**
* DS3231需要的全局变量
*/
bool Century=false;
bool h12;
bool PM;
byte ADay, AHour, AMinute, ASecond, ABits;
bool ADy, A12h, Apm;
int second,minute,hour,date,month,year,temperature;
int oldsecond; //此变量用于判断是否更新显示内容
// 定义LCD背光显示计时变量,无按键操作超时就关闭背光
unsigned long starttime;
// 定义菜单的操作按键(上、下、进入和返回)状态变量
bool old_up_stat, old_down_stat, old_enter_stat, old_exit_stat;
void setup() {
//设置LCD背光引脚为输出
pinMode(LIGHT_PIN, OUTPUT);
//设置按键为上拉电阻输入(低电平有效)
for (int i=4; i<=7; i++){
pinMode(i, INPUT_PULLUP);
}
//初始化时打开LCD背光
backlight("ON");
stat = false;
//初始化LCD5110
myGLCD.InitLCD();
myGLCD.setContrast(127);
myGLCD.setFont(SmallFont);
myGLCD.print("Initialize...", 0, 0);
/**
* 菜单的进一步设置
* 在这里将每一个菜单的关联设置好
* 对照每一个初始设置仔细填写它们之间的关系
*/
//第一级(main_menu)的第三项指向了下一级菜单(light_menu)
main_menu[2].next = light_menu;
//第二级(light_menu)的上一级(main_menu)
light_menu[0].prev = main_menu;
//第二级(light_menu)的第二项指向了下一级菜单(test_level3_menu)
light_menu[1].next = test_level3_menu;
//第三级(test_level3_menu)的上一级(light_menu)
test_level3_menu[0].prev = light_menu;
//第三级(test_level3_menu)的第五项定义了个逻辑动作
test_level3_menu[4].action = ledtest;
/**
* 初始化当前菜单为第一级(main_menu)
*/
cur_item = main_menu;
/**
* 上一级菜单为空
*/
prev_item = NULL;
/**
* 当前选择了第一项
*/
item_index = 0;
/**
* 从第一项开始显示菜单
*/
start_index = 0;
/**
* 设置DS3231的日期和时间,只需在需要调整的时候执行一次
*/
//clock.setClockMode(true);
//I2C总线库初始化
Wire.begin();
//一些状态变量的初始化
oldsecond = -1;
old_up_stat = HIGH;
old_down_stat = HIGH;
old_enter_stat = HIGH;
old_exit_stat = HIGH;
//为了视频中能看清楚,延时定义了3秒
delay(3000);
//所有初始化完成,清屏并关闭LCD背光
myGLCD.clrRow(0);
backlight("OFF");
starttime = millis();
}
void loop() {
//获取运行时间
unsigned long endtime = millis();
//获取当前菜单的项目数量
int menu_num = cur_item[0].num;
//定义一个临时变量
int idx;
//绘制菜单
renderMenu(&cur_item[0], menu_num>MENU_MAX_ROW ? MENU_MAX_ROW : menu_num);
//读取按键
int k_down = digitalRead(KEY_DOWN);
int k_up = digitalRead(KEY_UP);
int k_enter = digitalRead(KEY_ENTER);
int k_exit = digitalRead(KEY_EXIT);
//计算无按键时间,决定是否关闭LCD背光
if (endtime-starttime>5000) {
//myGLCD.enableSleep();
backlight("OFF");
}
//有键按下,打开LCD背光,计时清零
if (k_up==LOW || k_down==LOW || k_enter==LOW || k_exit==LOW) {
//myGLCD.disableSleep();
backlight("ON");
starttime = endtime;
}
//检测按键,进入相应动作
if (k_up == LOW && k_up!=old_up_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_up == LOW){
item_index --;
}
} else if (k_down == LOW && k_down!=old_down_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_down == LOW){
item_index ++;
}
} else if (k_enter == LOW && k_enter!=old_enter_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_enter == LOW){
//计算此时的菜单项索引值
idx = start_index+item_index;
if (cur_item[idx].next != NULL && cur_item[idx].type == MENU_SUBMENU){
//条件成立说明此菜单项指向了下一级菜单
//此级菜单变成了上一级菜单
prev_item = cur_item;
//将指向的下一级菜单设置为当前菜单
cur_item = cur_item[idx].next;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
//清屏
myGLCD.clrScr();
} else if (cur_item[idx].action != NULL && cur_item[idx].type != MENU_PARAM){
//条件成立说明此项菜单是动作
//此级菜单变成上一级菜单
prev_item = cur_item;
//根据动作类型调用相应的下一级菜单
switch(cur_item[idx].type){
case MENU_ACTION_LOGIC:
//将动作函数传递给逻辑菜单,使逻辑菜单能够正确执行动作
logic_menu[0].action = cur_item[idx].action;
//设置当前菜单为逻辑菜单
cur_item = logic_menu;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
break;
case MENU_ACTION_NUMBER:
break;
case MENU_ACTION_STRING:
break;
default:
break;
}
//清屏
myGLCD.clrScr();
} else if (cur_item[idx].type == MENU_PARAM){
//条件成立说明正在执行动作
//调用相应的动作函数,并传递参数(这里只举例逻辑设置)
cur_item[0].action((const char *)cur_item[idx].label);
}
}
} else if (k_exit == LOW && k_exit!=old_exit_stat){
//消抖
delay(KEY_TREMBLE_TIME);
if (k_exit == LOW){
//返回上一级菜单的操作
if (prev_item != NULL){
//设置上一级菜单为当前菜单
cur_item = prev_item;
//设置当前菜单的上一级菜单
prev_item = cur_item[0].prev;
//重置菜单项索引和绘制索引
item_index = 0;
start_index = 0;
//清屏
myGLCD.clrScr();
}
}
}
/**
* 菜单项上下选择是否越界
*/
if (item_index<0){
item_index = 0;
start_index --;
if (start_index<0) start_index = 0;
}
if (item_index>=MENU_MAX_ROW || item_index>=menu_num){
if (item_index>=menu_num) item_index = menu_num-1;
if (item_index>=MENU_MAX_ROW) item_index = MENU_MAX_ROW-1;
if (start_index+MENU_MAX_ROW<menu_num) start_index ++;
}
//保存按键状态
old_up_stat = digitalRead(KEY_UP);
old_down_stat = digitalRead(KEY_DOWN);
old_enter_stat = digitalRead(KEY_ENTER);
old_exit_stat = digitalRead(KEY_EXIT);
//renderClock(0, 0);
}
/**
* 关闭LCD背光
* 此函数符合菜单动作定义规则,即无返回值、一个字符串参数
*
* @var const char* stat 值为“ON"打开背光,为“OFF”关闭背光
*/
void backlight(const char *stat)
{
//这里定义了一个逻辑动作
//根据参数决定LCD背光的开关
digitalWrite(LIGHT_PIN, strcmp(stat,"ON") ? LOW : HIGH);
}
/**
* 逻辑动作测试函数
*
* @var const char* stat 根据此参数来执行动作,这里测试PIN13的LED
*/
void ledtest(const char *stat)
{
//根据参数决定LED开关
digitalWrite(LED_PIN, strcmp(stat,"ON") ? LOW : HIGH);
}
/**
* 绘制单个菜单项
*
* @var struct mymenu item 要绘制的项目
* @var int row 绘制坐标(LCD5110绘制文字时只需一个行坐标即可,可根据实际显示设备调整)
* @var bool rev 是否反相绘制,默认为不反相绘制(用于当前选中项绘制,可根据实际显示设备调整)
*/
void renderItem(struct mymenu item, int row, bool rev = false)
{
char label[15];
//规范显示项目文字(左对齐,补空格)
sprintf(label, "%-14s", item.label);
if (rev){
//打开5110的反相绘制功能
myGLCD.invertText(true);
}
//绘制菜单项
myGLCD.print(label, 0, row);
if (rev){
//关闭5110的反相绘制功能
myGLCD.invertText(false);
}
}
/**
* 绘制某一级菜单
*
* @var struct mymenu* items 需要绘制的菜单
* @var int menu_num 菜单项目数量
*/
void renderMenu(struct mymenu *items, int menu_num)
{
//绘制数量不能超过每一屏的最大绘制数量
int num = menu_num>MENU_MAX_ROW ? MENU_MAX_ROW : menu_num;
for (int i=0; i<num; i++){
//绘制每个菜单项
renderItem(items[i+start_index], i*8, i==item_index ? true:false);
}
}
void renderClock(int row)
{
char s[12];
second = clock.getSecond();
minute = clock.getMinute();
hour = clock.getHour(h12, PM);
date = clock.getDate();
month = clock.getMonth(Century);
year = clock.getYear();
temperature = clock.getTemperature();
if (oldsecond!=second){
sprintf(s, "20%02d-%02d-%02d", year, month, date);
myGLCD.print(s, 0, row);
sprintf(s, "%02d:%02d:%02d", PM==0 ? hour:hour+12, minute, second);
myGLCD.print(s, 0, row+8);
sprintf(s, "%3d", sizeof(main_menu)/sizeof(mymenu));
myGLCD.print(s, 0, row+16);
oldsecond = second;
}
}
void clockSetting(int year, int month, int day, int hour, int minute, int second)
clock.setSecond(second);//配置秒
clock.setMinute(minute
接插世界网(上海住歧电子科技有限公司)_
2022-05-17 广告
2022-05-17 广告
焊接式的接插件用于电流较小,发热较少的场合,否则过热效导致焊锡熔化,有发生短路的危险。压接的一般用于导线头,也可用于无法焊接或不适于焊接比较高热的场合。 可以订做,不过尽量选择标准的插头,以免增加成本。通用品是量产的,便宜。买卖接插件,就上...
点击进入详情页
本回答由接插世界网(上海住歧电子科技有限公司)_提供
2016-11-18
展开全部
arduino写多级菜单 ,还是看实例吧,我上传到了附件,希望 能帮助到你。
本回答被网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询