/*
* menu.c
*
* Created on: Jul 10, 2025
* Author: 徐思恬
*/
#include "stm32f1xx_hal.h"
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h> // Device header
#include "../../icode/oled/oled.h"
#include "../../icode/key/key.h"
#include "../../icode/flash/flash.h"
#include "../../icode/menu/menu.h"
#define EH1_CURSOR 0xFFCFFC
#define EH2_CURSOR 0xFFCFF8
#define SH_CURSOR 0xFFDFEC
#define MFH1_CURSOR 0xFFDFF0
#define MFH2_CURSOR 0xFFDFF8
// 定义全局变量
int EH1 = 0;
int EH2 = 0;
int FUSE1_ON_TIME = 0;
int FUSE2_ON_TIME = 0;
SystemParams sysParams;
// 编辑相关全局变量
char editBuffer[12] = {0}; // 编辑缓冲区,大小12
char editTitle[20] = {0}; // 编辑标题
int *editTarget = NULL; // 指向要编辑的变量
/**
* @brief 计算参数校验和
* @param params: 参数结构体指针
* @retval 校验和
*/
uint32_t calculate_checksum(SystemParams *params) {
uint32_t sum = 0;
uint8_t *data = (uint8_t*)params;
size_t len = sizeof(SystemParams) - sizeof(params->checksum);
for(size_t i = 0; i < len; i++) {
sum = (sum << 3) + data[i]; // 简单但有效的校验算法
}
return sum;
}
/**
* @brief 保存参数到Flash
* @retval 0:成功, 1:失败
*/
uint8_t save_params_to_flash(void) {
// 更新系统参数
sysParams.EH1 = EH1;
sysParams.EH2 = EH2;
sysParams.FUSE1_ON_TIME = FUSE1_ON_TIME;
sysParams.FUSE2_ON_TIME = FUSE2_ON_TIME;
// 设置魔数和校验和
sysParams.magic = PARAMS_MAGIC;
sysParams.checksum = calculate_checksum(&sysParams);
// 擦除扇区
W25QXX_Erase_Sector(PARAMS_SECTOR_ADDR / 4096);
W25QXX_Wait_Busy();
// 写入数据
W25QXX_Write((uint8_t*)&sysParams, PARAMS_SECTOR_ADDR, sizeof(SystemParams));
// 验证写入
SystemParams verify;
W25QXX_Read((uint8_t*)&verify, PARAMS_SECTOR_ADDR, sizeof(SystemParams));
if(memcmp(&sysParams, &verify, sizeof(SystemParams)) != 0) {
return 1; // 写入失败
}
return 0; // 成功
}
/**
* @brief 从Flash加载参数
* @retval 0:成功, 1:失败
*/
uint8_t load_params_from_flash(void) {
// 读取参数
W25QXX_Read((uint8_t*)&sysParams, PARAMS_SECTOR_ADDR, sizeof(SystemParams));
// 检查魔数
if(sysParams.magic != PARAMS_MAGIC) {
return 1; // 魔数错误
}
// 检查校验和
uint32_t calc_checksum = calculate_checksum(&sysParams);
if(sysParams.checksum != calc_checksum) {
return 2; // 校验和错误
}
// 加载到全局变量
EH1 = sysParams.EH1;
EH2 = sysParams.EH2;
FUSE1_ON_TIME = sysParams.FUSE1_ON_TIME;
FUSE2_ON_TIME = sysParams.FUSE2_ON_TIME;
return 0; // 成功
}
/**
* @brief 初始化参数系统
*/
void init_params_system(void) {
// 尝试加载参数
if(load_params_from_flash() != 0) {
// 加载失败,使用默认值
EH1 = 0;
EH2 = 0;
FUSE1_ON_TIME = 0;
FUSE2_ON_TIME = 0;
// 保存默认值
save_params_to_flash();
}
}
// 定义菜单状态枚举
enum MenuState { MAIN_MENU, SUB_MENU, EDIT_MENU, DISPLAY_MENU };
enum MenuState currentMenu = MAIN_MENU;
// 定义菜单选项
int selectedMainMenu = 0; // 一级菜单选项
int selectedSubMenu = 0; // 二级菜单选项
// 全局变量
uint8_t KeyNum; // 用于存储键码值
//int EH1 = 0; // 默认高度(cm)
//int EH2 = 0;
//int FUSE1_ON_TIME = 0; // 默认时间(s)
//int FUSE2_ON_TIME = 0;
//char editBuffer[5]; // 编辑缓冲区
//// 编辑相关全局变量
//int* editTarget = NULL; // 指向要修改的变量
//// 全局变量修改
//char editBuffer[12] = {0}; // 初始化缓冲区
/**
* @brief 数值编辑函数 - 修复版本
* @param value: 要编辑的变量的指针
* @param title: 编辑界面的标题
* @retval 无
*/
void edit_value(int *value, const char *title) {
editTarget = value;
strncpy(editTitle, title, sizeof(editTitle)-1);
editTitle[sizeof(editTitle)-1] = '\0';
// 安全初始化编辑缓冲区
memset(editBuffer, 0, sizeof(editBuffer)); // 清空缓冲区
snprintf(editBuffer, sizeof(editBuffer), "%d", *value); // 安全格式化
}
/**
* @brief 数据展示函数 - 三级菜单
* @param title: 显示标题
* @param value1: 第一个值
* @param value2: 第二个值
* @retval 无
*/
void display_data(const char* title, int value1, int value2) {
OLED_Clear();
OLED_ShowString(5, 5,(char*)title, OLED_8X16);
char displayStr[20];
snprintf(displayStr, sizeof(displayStr), "EH1: %d cm", value1);
OLED_ShowString(5, 16, displayStr, OLED_8X16);
snprintf(displayStr, sizeof(displayStr), "EH2: %d cm", value2);
OLED_ShowString(5, 32, displayStr, OLED_8X16);
OLED_ShowString(5, 48, "<- Back", OLED_8X16);
OLED_Update();
currentMenu = DISPLAY_MENU;
}
/**
* @brief 处理编辑菜单
* @param 无
* @retval 无
*/
void handle_edit_menu(void) {
while(1) {
// 显示编辑界面
OLED_Clear();
OLED_ShowString(5, 0, editTitle, OLED_8X16);
// 显示当前编辑值
char valueStr[20];
snprintf(valueStr, sizeof(valueStr), "Value: %s", editBuffer);
OLED_ShowString(5, 16, valueStr, OLED_8X16);
// 显示操作提示
OLED_ShowString(5, 32, "4:Del 8:Confirm", OLED_8X16);
OLED_ShowString(5, 48, "0-9:Input 12:Cancel", OLED_8X16);
OLED_Update();
KeyNum = Key_GetNum();
// 处理数字输入
if(KeyNum >= 0 && KeyNum <= 9) {
if(strlen(editBuffer) < 10) {
char numChar = '0' + KeyNum;
strncat(editBuffer, &numChar, 1);
}
}
// 删除字符
else if(KeyNum == 20) {
size_t len = strlen(editBuffer);
if(len > 0)
editBuffer[len-1] = '\0';
}
// 确认保存
else if(KeyNum == 21) {
*editTarget = atoi(editBuffer);
// save_params_to_flash();
currentMenu = SUB_MENU;
return;
}
// 取消编辑
else if(KeyNum == 12) {
currentMenu = SUB_MENU;
return;
}
}
}
/**
* @brief 处理编辑菜单 - 完整实现
* @param 无
* @retval 无
*/
//void handle_edit_menu(void) {
// // 记录原始值用于取消操作
// int original_value = *editTarget;
// uint8_t cursor_pos = strlen(editBuffer);
// uint8_t max_length = sizeof(editBuffer) - 1;
// uint32_t last_key_time = HAL_GetTick();
// uint8_t blink_state = 1; // 光标闪烁状态
//
// while(1) {
// // 获取当前时间
// uint32_t current_time = HAL_GetTick();
//
// // 每500ms更新光标闪烁状态
// if(current_time - last_key_time > 500) {
// blink_state = !blink_state;
// last_key_time = current_time;
// }
//
// // 显示编辑界面
// OLED_Clear();
//
// // 显示标题
// OLED_ShowString(5, 0, editTitle, OLED_8X16);
//
// // 显示当前编辑值
// char valueStr[20];
// snprintf(valueStr, sizeof(valueStr), "Value: %s", editBuffer);
// OLED_ShowString(5, 16, valueStr, OLED_8X16);
//
// // 显示光标
// if(blink_state) {
// // 计算光标位置
// uint8_t cursor_x = 5 + (strlen("Value: ") + cursor_pos) * 6; // 6px per char (8x16 font)
// OLED_DrawRectangle(cursor_x, 32, cursor_x + 6, 32 + 2,1);
// }
//
// // 显示操作提示
// OLED_ShowString(5, 32, "4:Del 8:Confirm", OLED_8X16);
// OLED_ShowString(5, 48, "0-9:Input C:Cancel", OLED_8X16);
// OLED_Update();
//
// // 非阻塞方式获取按键
// KeyNum = Key_GetNum_NonBlocking();
//
// // 处理数字输入
// if(KeyNum >= 0 && KeyNum <= 9) {
// size_t len = strlen(editBuffer);
// if(len < max_length) {
// editBuffer[len] = '0' + KeyNum;
// editBuffer[len + 1] = '\0';
// cursor_pos = len + 1;
// blink_state = 1; // 重置光标状态
// last_key_time = current_time;
// }
// }
// // 删除字符
// else if(KeyNum == 20) {
// size_t len = strlen(editBuffer);
// if(len > 0) {
// editBuffer[len - 1] = '\0';
// cursor_pos = len - 1;
// blink_state = 1; // 重置光标状态
// last_key_time = current_time;
// }
// }
// // 确认保存
// else if(KeyNum == 21) {
// // 转换缓冲区内容为整数
// int new_value = atoi(editBuffer);
//
// // 输入验证
// if(new_value < 0 || new_value > 9999) {
// show_message("Invalid value!");
// HAL_Delay(1000);
// continue;
// }
//
// // 更新目标值
// *editTarget = new_value;
//
// // 保存到Flash
// FlashStatus status = save_params_to_flash();
// if(status == FLASH_OK) {
// show_message("Saved!");
// } else {
// show_error_message(status);
// }
//
// // 返回二级菜单
// currentMenu = SUB_MENU;
// return;
// }
// // 取消编辑
// else if(KeyNum == 12) {
// // 恢复原始值
// *editTarget = original_value;
// currentMenu = SUB_MENU;
// return;
// }
// // 左右移动光标
// else if(KeyNum == 13) { // 左键
// if(cursor_pos > 0) {
// cursor_pos--;
// blink_state = 1;
// last_key_time = current_time;
// }
// }
// else if(KeyNum == 15) { // 右键
// size_t len = strlen(editBuffer);
// if(cursor_pos < len) {
// cursor_pos++;
// blink_state = 1;
// last_key_time = current_time;
// }
// }
//
// // 防止CPU占用过高
// HAL_Delay(50);
// }
//}
//
//// 辅助函数实现
//void show_message(const char *msg) {
// OLED_Clear();
// OLED_ShowString(5, 16, (char*)msg, OLED_8X16);
// OLED_ShowString(5, 48, "Press any key", OLED_8X16);
// OLED_Update();
//
// while(Key_GetNum() == 0); // 等待按键
//}
//
//void show_error_message(FlashStatus status) {
// OLED_Clear();
// switch(status) {
// case FLASH_ERASE_FAILED:
// OLED_ShowString(5, 16, "Erase Failed!", OLED_8X16);
// break;
// case FLASH_WRITE_FAILED:
// OLED_ShowString(5, 16, "Write Failed!", OLED_8X16);
// break;
// case FLASH_VERIFY_FAILED:
// OLED_ShowString(5, 16, "Verify Failed!", OLED_8X16);
// break;
// case FLASH_CHECKSUM_MISMATCH:
// OLED_ShowString(5, 16, "Checksum Error!", OLED_8X16);
// break;
// case FLASH_MAGIC_INVALID:
// OLED_ShowString(5, 16, "Invalid Data!", OLED_8X16);
// break;
// default:
// OLED_ShowString(5, 16, "Unknown Error!", OLED_8X16);
// }
// OLED_ShowString(5, 48, "Press BACK to return", OLED_8X16);
// OLED_Update();
//
// while(Key_GetNum() != 12); // 等待返回键
//}
//
//// 非阻塞按键检测函数
//uint8_t Key_GetNum_NonBlocking(void) {
// static uint8_t last_key = 0;
// static uint32_t last_time = 0;
//
// uint8_t current_key = Key_GetNum();
//
// // 按键消抖处理
// if(current_key != 0) {
// if(current_key == last_key && HAL_GetTick() - last_time < 200) {
// return 0; // 消抖期内返回0
// }
// last_key = current_key;
// last_time = HAL_GetTick();
// return current_key;
// }
//
// last_key = 0;
// return 0;
//}
/**
* @brief 一级菜单函数,用于显示一级菜单八行选项
* @param 无
* @retval 返回当前行是第几行
*/
int menu1(void) {
uint8_t flag=1;
currentMenu = MAIN_MENU;
/*初始显示*/
OLED_ShowString(5,0,"Servo ",OLED_8X16);
OLED_ShowString(5,16,"High ",OLED_8X16);
OLED_ShowString(5,32,"Flash ",OLED_8X16);
OLED_ShowString(5,48,"Time ",OLED_8X16);
OLED_Update();
while(1) {
KeyNum = Key_GetNum();
if(KeyNum == 20) { //上一项
flag--;
if(flag == 0) { flag = 8; }
}
if(KeyNum == 21) { //下一项
flag++;
if(flag == 9) { flag = 1; }
}
if(KeyNum == 12) { //确认
OLED_Clear();
OLED_Update();
selectedMainMenu = flag; // 记录选择的一级菜单项
currentMenu = SUB_MENU; // 进入二级菜单
return flag;
}
// 根据当前选项高亮显示
OLED_Clear();
// 显示前4项
if (flag <= 4) {
OLED_ShowString(5,0,"Servo ",OLED_8X16);
OLED_ShowString(5,16,"High ",OLED_8X16);
OLED_ShowString(5,32,"Flash ",OLED_8X16);
OLED_ShowString(5,48,"Time ",OLED_8X16);
// 高亮当前选项
switch(flag) {
case 1: OLED_ReverseArea(5,0,128,16); break;
case 2: OLED_ReverseArea(5,16,128,16); break;
case 3: OLED_ReverseArea(5,32,128,16); break;
case 4: OLED_ReverseArea(5,48,128,16); break;
}
} else { // 显示后4项
OLED_ShowString(5,0,"fuse ",OLED_8X16);
OLED_ShowString(5,16,"buzzer ",OLED_8X16);
OLED_ShowString(5,32,"music ",OLED_8X16);
OLED_ShowString(5,48,"Settings ",OLED_8X16);
// 高亮当前选项
switch(flag) {
case 5: OLED_ReverseArea(5,0,128,16); break;
case 6: OLED_ReverseArea(5,16,128,16); break;
case 7: OLED_ReverseArea(5,32,128,16); break;
case 8: OLED_ReverseArea(5,48,128,16); break;
}
}
OLED_Update();
}
}
/*-----------------------------------二级舵机菜单------------------------------------------------*/
/**
* @brief 舵机二级菜单函数
* @param 无
* @retval 0:返回主菜单,1:进入三级菜单
*/
int menu2_Servo(void) {
uint8_t moflag = 1;//默认在第一行
currentMenu = SUB_MENU;
while(1) {
OLED_Clear();
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"Angle setting ",OLED_8X16);
OLED_ShowString(5,32,"Time setting ",OLED_8X16);
OLED_ShowString(5,48,"High setting ",OLED_8X16);
// 高亮当前选项
switch(moflag) {
case 1: OLED_ReverseArea(0,0,128,16); break;
case 2: OLED_ReverseArea(0,16,128,16); break;
case 3: OLED_ReverseArea(0,32,128,16); break;
case 4: OLED_ReverseArea(0,48,128,16); break;
}
OLED_Update();
KeyNum = Key_GetNum();
if(KeyNum == 20) moflag = (moflag == 1) ? 4 : moflag - 1; // 上一项
if(KeyNum == 21) moflag = (moflag % 4) + 1; // 下一项
if(KeyNum == 12) { // 确认
switch(moflag) {
case 1: // 返回
currentMenu = MAIN_MENU;
return 0;
case 2: // 进入角度设置
// 这里可以添加角度设置的三级菜单
// 例如: edit_value(&angle, "Set Angle");
return 1;
case 3: // 时间设置
// 例如: edit_value(&time, "Set Time");
return 1;
case 4: // 高度设置
// 例如: edit_value(&height, "Set Height");
return 1;
}
}
}
}
///*-----------------------------------二级高度菜单-----------------------------------------*/
///**
// * @brief 高度二级菜单函数
// * @param 无
// * @retval 0:返回主菜单,1:进入三级菜单
// */
int menu2_high(void) {
uint8_t adflag = 1;//默认在第一行
currentMenu = SUB_MENU;
while(1) {
OLED_Clear();
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"high1 setting ",OLED_8X16);
OLED_ShowString(5,32,"high2 setting ",OLED_8X16);
OLED_ShowString(5,48,"Data display ",OLED_8X16);
// 高亮当前选项
switch(adflag) {
case 1: OLED_ReverseArea(0,0,128,16); break;
case 2: OLED_ReverseArea(0,16,128,16); break;
case 3: OLED_ReverseArea(0,32,128,16); break;
case 4: OLED_ReverseArea(0,48,128,16); break;
}
OLED_Update();
KeyNum = Key_GetNum();
if(KeyNum == 20) adflag = (adflag == 1) ? 4 : adflag - 1; // 上一项
if(KeyNum == 21) adflag = (adflag % 4) + 1; // 下一项
if(KeyNum == 12) { // 确认
switch(adflag) {
case 1: // 返回
currentMenu = MAIN_MENU;
return 0;
case 2: // 编辑EH1
edit_value(&EH1, "Set Height 1");
return 1; // 进入编辑状态
case 3: // 编辑EH2
edit_value(&EH2, "Set Height 2");
return 1;
case 4: // 数据显示
display_data("Height Data", EH1, EH2);
return 1;
}
}
}
}
/*-----------------------------二级闪存芯片数据菜单-----------------------------------------*/
/**
* @brief 闪存芯片数据二级菜单函数
* @param 无
* @retval 0:返回主菜单
*/
int menu2_flash(void)
{
uint8_t ygflag = 1; // 默认在第一行
currentMenu = SUB_MENU;
while(1)
{
OLED_Clear();
OLED_ShowString(5,0,"<- Back ",OLED_8X16);
OLED_ShowString(5,16,"Read Flash Info ",OLED_8X16);
OLED_ShowString(5,32,"Read Parameters ",OLED_8X16);
OLED_ShowString(5,48,"Write Parameters ",OLED_8X16);
// 高亮当前选项
switch(ygflag) {
case 1: OLED_ReverseArea(0,0,128,16); break;
case 2: OLED_ReverseArea(0,16,128,16); break;
case 3: OLED_ReverseArea(0,32,128,16); break;
case 4: OLED_ReverseArea(0,48,128,16); break;
}
OLED_Update();
KeyNum = Key_GetNum();
if(KeyNum == 20) // 上一项
ygflag = (ygflag == 1) ? 4 : ygflag - 1;
if(KeyNum == 21) // 下一项
ygflag = (ygflag % 4) + 1;
if(KeyNum == 12) // 确认
{
switch(ygflag) {
case 1: // 返回
currentMenu = MAIN_MENU;
return 0;
case 2: // 读取Flash信息
// display_flash_info();
break;
case 3: // 读取参数数据
// display_flash_data();
break;
case 4: // 写入参数
// 这里可以添加写入参数的函数,或者直接调用保存函数
// save_params_to_flash();
// 可以显示保存成功或失败的提示
OLED_Clear();
// if(save_params_to_flash() == 0) {
// OLED_ShowString(5, 0, "Save Success!", OLED_8X16);
// } else {
// OLED_ShowString(5, 0, "Save Failed!", OLED_8X16);
// }
// OLED_ShowString(5, 48, "Press BACK to return", OLED_8X16);
// OLED_Update();
while(Key_GetNum() != 12); // 等待返回
break;
}
}
}
}
/**
* @brief 显示Flash存储的参数数据
*/
void display_flash_data(void) {
// 从Flash中读取参数
SystemParams params;
W25QXX_Read((uint8_t*)¶ms, PARAMS_SECTOR_ADDR, sizeof(SystemParams));
// 验证数据有效性
uint8_t valid = 1;
if(params.magic != PARAMS_MAGIC) {
valid = 0;
} else {
uint32_t calc_checksum = calculate_checksum(¶ms);
if(params.checksum != calc_checksum) {
valid = 0;
}
}
// 显示参数
OLED_Clear();
if(valid) {
OLED_ShowString(0, 0, "Flash Parameters:", OLED_8X16);
char buffer[30];
snprintf(buffer, sizeof(buffer), "EH1: %d cm", params.EH1);
OLED_ShowString(0, 16, buffer, OLED_8X16);
snprintf(buffer, sizeof(buffer), "EH2: %d cm", params.EH2);
OLED_ShowString(0, 32, buffer, OLED_8X16);
snprintf(buffer, sizeof(buffer), "Time1: %d s", params.FUSE1_ON_TIME);
OLED_ShowString(0, 48, buffer, OLED_8X16);
} else {
OLED_ShowString(0, 16, "Invalid Data!", OLED_8X16);
OLED_ShowString(0, 32, "Check Flash or Reset", OLED_8X16);
}
OLED_ShowString(0, 48, "Press BACK to return", OLED_8X16);
OLED_Update();
// 等待返回键
while(Key_GetNum() != 12) {
HAL_Delay(100);
}
}
//
//// 显示Flash芯片信息
//void display_flash_info(void)
//{
// OLED_Clear();
//
// // 显示芯片信息
// uint16_t flash_id = W25QXX_ReadID();
// char id_str[20];
// snprintf(id_str, sizeof(id_str), "ID: 0x%04X", flash_id);
// OLED_ShowString(0, 0, id_str, OLED_8X16);
//
// // 显示容量信息
// const char *size_str = "Unknown";
// switch(W25QXX_TYPE) {
// case W25Q80: size_str = "1MB (8Mb)"; break;
// case W25Q16: size_str = "2MB (16Mb)"; break;
// case W25Q32: size_str = "4MB (32Mb)"; break;
// case W25Q64: size_str = "8MB (64Mb)"; break;
// case W25Q128: size_str = "16MB (128Mb)"; break;
// case W25Q256: size_str = "32MB (256Mb)"; break;
// }
// char size_info[30];
// snprintf(size_info, sizeof(size_info), "Size: %s", size_str);
// OLED_ShowString(0, 16, size_info, OLED_8X16);
//
// // 显示状态寄存器
// uint8_t sr1 = W25QXX_ReadSR(1);
// uint8_t sr2 = W25QXX_ReadSR(2);
// uint8_t sr3 = W25QXX_ReadSR(3);
// char sr_str[40];
// snprintf(sr_str, sizeof(sr_str), "SR: %02X %02X %02X", sr1, sr2, sr3);
// OLED_ShowString(0, 32, sr_str, OLED_8X16);
//
// // 显示使用情况统计
// char usage_str[30];
// uint32_t total_sectors = get_total_sectors();
// uint32_t used_sectors = get_used_sectors();
// snprintf(usage_str, sizeof(usage_str), "Used: %lu/%lu sectors",
// used_sectors, total_sectors);
// OLED_ShowString(0, 48, usage_str, OLED_8X16);
//
// OLED_Update();
//
// // 等待返回键
// while(Key_GetNum() != 12) {
// HAL_Delay(100);
// }
//}
/*---------------------------------二级时间菜单------------------------------------------*/
/**
* @brief 时间二级菜单函数
* @param 无
* @retval 0:返回主菜单,1:进入三级菜单
*/
// 显示时间参数的详细视图
void display_time_detail(void) {
OLED_Clear();
// 标题
OLED_ShowString(5, 0, "FUSE Timing Details", OLED_8X16);
// 分割线
// OLED_DrawLine(0, 16, 128, 16, WHITE);
// FUSE1信息
char fuse1Str[40];
snprintf(fuse1Str, sizeof(fuse1Str), "FUSE1 Time: %d s", FUSE1_ON_TIME);
OLED_ShowString(5, 20, fuse1Str, OLED_8X16);
// FUSE2信息
char fuse2Str[40];
snprintf(fuse2Str, sizeof(fuse2Str), "FUSE2 Time: %d s", FUSE2_ON_TIME);
OLED_ShowString(5, 36, fuse2Str, OLED_8X16);
// 总时间
int totalTime = FUSE1_ON_TIME + FUSE2_ON_TIME;
char totalStr[40];
snprintf(totalStr, sizeof(totalStr), "Total Time: %d s", totalTime);
OLED_ShowString(5, 52, totalStr, OLED_8X16);
OLED_Update();
// 等待返回键
while(Key_GetNum() != 12) {
HAL_Delay(100);
}
}
// 显示时间数据
void display_time_data(void) {
OLED_Clear();
OLED_ShowString(5, 0, "Time Settings:", OLED_8X16);
char timeStr[30];
snprintf(timeStr, sizeof(timeStr), "FUSE1: %d seconds", FUSE1_ON_TIME);
OLED_ShowString(5, 16, timeStr, OLED_8X16);
snprintf(timeStr, sizeof(timeStr), "FUSE2: %d seconds", FUSE2_ON_TIME);
OLED_ShowString(5, 32, timeStr, OLED_8X16);
// 显示操作提示
OLED_ShowString(5, 48, "8:Detail 12:Back", OLED_8X16);
OLED_Update();
// 等待用户操作
while(1) {
KeyNum = Key_GetNum();
if(KeyNum == 12) { // 返回键
break;
}
else if(KeyNum == 21) { // 查看详情
display_time_detail();
// 返回后重新显示基本数据
OLED_Clear();
OLED_ShowString(5, 0, "Time Settings:", OLED_8X16);
snprintf(timeStr, sizeof(timeStr), "FUSE1: %d seconds", FUSE1_ON_TIME);
OLED_ShowString(5, 16, timeStr, OLED_8X16);
snprintf(timeStr, sizeof(timeStr), "FUSE2: %d seconds", FUSE2_ON_TIME);
OLED_ShowString(5, 32, timeStr, OLED_8X16);
OLED_ShowString(5, 48, "8:Detail 12:Back", OLED_8X16);
OLED_Update();
}
HAL_Delay(50);
}
}
int menu2_time(void) {
uint8_t mpuflag = 1;//默认在第一行
currentMenu = SUB_MENU;
while(1) {
OLED_Clear();
OLED_ShowString(5,0,"<- Back ",OLED_8X16);
OLED_ShowString(5,16,"Set FUSE1 Time ",OLED_8X16);
OLED_ShowString(5,32,"Set FUSE2 Time ",OLED_8X16);
OLED_ShowString(5,48,"Time Data Display",OLED_8X16);
// 高亮当前选项
switch(mpuflag) {
case 1: OLED_ReverseArea(0,0,128,16); break;
case 2: OLED_ReverseArea(0,16,128,16); break;
case 3: OLED_ReverseArea(0,32,128,16); break;
case 4: OLED_ReverseArea(0,48,128,16); break;
}
OLED_Update();
KeyNum = Key_GetNum();
if(KeyNum == 20) mpuflag = (mpuflag == 1) ? 4 : mpuflag - 1; // 上一项
if(KeyNum == 21) mpuflag = (mpuflag % 4) + 1; // 下一项
if(KeyNum == 12) { // 确认
switch(mpuflag) {
case 1: // 返回
currentMenu = MAIN_MENU;
return 0;
case 2: // 编辑时间1
edit_value(&FUSE1_ON_TIME, "Set FUSE1 Time");
return 1;
case 3: // 编辑时间2
edit_value(&FUSE2_ON_TIME, "Set FUSE2 Time");
return 1;
case 4: // 数据显示
display_time_data();
return 1;
}
}
}
}
/*--------------------------------------------------------------------------------------*/
/*---------------------------------二级fuse菜单------------------------------------------*/
/**
* @brief fuse二级菜单函数
* @param 无
* @retval
*/
int menu2_fuse(void)
{
int menu3_PPID;
uint8_t pidflag = 1;//默认在第一行
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_Update();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 20)//上一项
{
pidflag--;
if(pidflag == 0){pidflag = 4;}//共四行
}
if(KeyNum == 21)//下一项
{
pidflag++;
if(pidflag == 5){pidflag = 1;}
}
if(KeyNum == 12)//确认
{
OLED_Clear();
OLED_Update();
menu3_PPID = pidflag;
}
if(menu3_PPID == 1){return 0;}//返回0,退到第一级菜单
if(menu3_PPID == 2){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_PPID == 3){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_PPID == 4){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
switch(pidflag)
{
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 1:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 2:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,16,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 3:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,32,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 4:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
}
}
}
/*--------------------------------------------------------------------------------------*/
/*---------------------------------二级蜂鸣器菜单-----------------------------------------*/
/**
* @brief 蜂鸣器二级菜单函数
* @param 无
* @retval
*/
int menu2_buzzer(void)
{
int menu3_PPID;
uint8_t pidflag = 1;//默认在第一行
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_Update();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 20)//上一项
{
pidflag--;
if(pidflag == 0){pidflag = 4;}//共四行
}
if(KeyNum == 21)//下一项
{
pidflag++;
if(pidflag == 5){pidflag = 1;}
}
if(KeyNum == 12)//确认
{
OLED_Clear();
OLED_Update();
menu3_PPID = pidflag;
}
if(menu3_PPID == 1){return 0;}//返回0,退到第一级菜单
if(menu3_PPID == 2){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_PPID == 3){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_PPID == 4){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
switch(pidflag)
{
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 1:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 2:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,16,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 3:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,32,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 4:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"RESET ",OLED_8X16);
OLED_ShowString(5,32,"SET ",OLED_8X16);
OLED_ShowString(5,48,"CLOSE ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
}
}
}
/*--------------------------------------------------------------------------------------*/
/*---------------------------------二级音乐菜单-----------------------------------------*/
/**
* @brief 音乐二级菜单函数
* @param 无
* @retval
*/
int menu2_Music(void)
{
int menu3_MMusic;
uint8_t musicflag = 1;//默认在第一行
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"老男孩 ",OLED_8X16);
OLED_ShowString(0,32,"七里香 ",OLED_8X16);
OLED_ShowString(0,48,"我记得 ",OLED_8X16);
OLED_Update();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 20)//上一项
{
musicflag--;
if(musicflag == 0){musicflag = 6;}//共四行
}
if(KeyNum == 21)//下一项
{
musicflag++;
if(musicflag == 7){musicflag = 1;}
}
if(KeyNum == 12)//确认
{
OLED_Clear();
OLED_Update();
menu3_MMusic = musicflag;
}
if(menu3_MMusic == 1){return 0;}//返回0,退到第一级菜单
if(menu3_MMusic == 2){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_MMusic == 3){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_MMusic == 4){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_MMusic == 5){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_MMusic == 6){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
switch(musicflag)
{
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 1:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"老男孩 ",OLED_8X16);
OLED_ShowString(0,32,"七里香 ",OLED_8X16);
OLED_ShowString(0,48,"我记得 ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 2:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"老男孩 ",OLED_8X16);
OLED_ShowString(0,32,"七里香 ",OLED_8X16);
OLED_ShowString(0,48,"我记得 ",OLED_8X16);
OLED_ReverseArea(0,16,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 3:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"老男孩 ",OLED_8X16);
OLED_ShowString(0,32,"七里香 ",OLED_8X16);
OLED_ShowString(0,48,"我记得 ",OLED_8X16);
OLED_ReverseArea(0,32,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 4:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"老男孩 ",OLED_8X16);
OLED_ShowString(0,32,"七里香 ",OLED_8X16);
OLED_ShowString(0,48,"我记得 ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
case 5:
{
OLED_ShowString(0,0,"老男孩 ",OLED_8X16);
OLED_ShowString(0,16,"七里香 ",OLED_8X16);
OLED_ShowString(0,32,"我记得 ",OLED_8X16);
OLED_ShowString(0,48,"晴天 ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
case 6:
{
OLED_ShowString(0,0,"七里香 ",OLED_8X16);
OLED_ShowString(0,16,"我记得 ",OLED_8X16);
OLED_ShowString(0,32,"晴天 ",OLED_8X16);
OLED_ShowString(0,48,"卡农 ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
}
}
}
/*--------------------------------------------------------------------------------------*/
/*---------------------------------二级设置菜单-----------------------------------------*/
/**
* @brief 设置控制二级菜单函数
* @param 无
* @retval
*/
int menu2_Set(void)
{
int menu3_SSet;
uint8_t setflag = 1;//默认在第一行
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"mode setting ",OLED_8X16);
OLED_ShowString(5,32,"animation ",OLED_8X16);
OLED_ShowString(5,48,"language ",OLED_8X16);
OLED_Update();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 20)//上一项
{
setflag--;
if(setflag == 0){setflag = 5;}
}
if(KeyNum == 21)//下一项
{
setflag++;
if(setflag == 6){setflag = 1;}
}
if(KeyNum == 12)//确认
{
OLED_Clear();
OLED_Update();
menu3_SSet = setflag;
}
if(menu3_SSet == 1){return 0;}//返回0,退到第一级菜单
if(menu3_SSet == 2){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_SSet == 3){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_SSet == 4){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
if(menu3_SSet == 5){return 0;}//返回0,退到第一级菜单(因为还没写三级菜单函数)
switch(setflag)
{
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 1:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"mode setting ",OLED_8X16);
OLED_ShowString(5,32,"animation ",OLED_8X16);
OLED_ShowString(5,48,"language ",OLED_8X16);
OLED_ReverseArea(0,0,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 2:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"mode setting ",OLED_8X16);
OLED_ShowString(5,32,"animation ",OLED_8X16);
OLED_ShowString(5,48,"language ",OLED_8X16);
OLED_ReverseArea(0,16,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 3:
{
OLED_ShowString(0,0,"<- ",OLED_8X16);
OLED_ShowString(0,16,"mode setting ",OLED_8X16);
OLED_ShowString(0,32,"animation ",OLED_8X16);
OLED_ShowString(0,48,"language ",OLED_8X16);
OLED_ReverseArea(0,32,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 4:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"mode setting ",OLED_8X16);
OLED_ShowString(5,32,"animation ",OLED_8X16);
OLED_ShowString(5,48,"language ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
//再次显示,解决乱码和闪烁问题(确保反相前是亮的状态)
case 5:
{
OLED_ShowString(5,0,"<- ",OLED_8X16);
OLED_ShowString(5,16,"mode setting ",OLED_8X16);
OLED_ShowString(5,32,"animation ",OLED_8X16);
OLED_ShowString(5,48,"language ",OLED_8X16);
OLED_ReverseArea(0,48,128,16);
OLED_Update();
break;
}
}
}
}
/**
* @brief 主菜单循环
* @param 无
* @retval 无
*/
void menu_main_loop(void) {
// load_params_from_flash();
while(1) {
switch(currentMenu) {
case MAIN_MENU:
menu1(); // 调用一级菜单,返回后selectedMainMenu被设置
break;
case SUB_MENU:
// 根据一级菜单的选择调用对应的二级菜单
switch(selectedMainMenu) {
case 1:
if (menu2_Servo() == 0)
currentMenu = MAIN_MENU;
break;
case 2:
if (menu2_high() == 0)
currentMenu = MAIN_MENU;
break;
case 3:
if (menu2_flash() == 0)
currentMenu = MAIN_MENU;
break;
case 4:
if (menu2_time() == 0)
currentMenu = MAIN_MENU;
break;
case 5:
if (menu2_fuse() == 0)
currentMenu = MAIN_MENU;
break;
case 6:
if (menu2_buzzer() == 0)
currentMenu = MAIN_MENU;
break;
case 7:
if (menu2_Music() == 0)
currentMenu = MAIN_MENU;
break;
case 8:
if (menu2_Set() == 0)
currentMenu = MAIN_MENU;
break;
}
break;
case EDIT_MENU:
handle_edit_menu();
break;
case DISPLAY_MENU:
// 等待返回键
KeyNum = Key_GetNum();
if(KeyNum == 12) { // 按下返回键
currentMenu = SUB_MENU;
}
break;
}
}
}
点击high1或者2 setting和时间的设置时自动跳转到<--改善代码修改这个问题