如何将buffer数据置0-memset函数有缺陷

本文探讨了在加密应用程序中,为何不能简单使用memset函数来清除敏感数据,并分析了编译器可能进行的优化。通过示例代码,解释了一种利用volatile函数指针确保内存清除的策略,以防止编译器优化。同时提到了Windows上的securezeroMemory和C11的memset_s作为安全的内存清除选项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近看到mbedtls源码里面有段关于memset的代码,和我们平时使用方式不一样,特意看了下。

mbedtls的关于memset调用的源码如下:

#if !defined(MBEDTLS_PLATFORM_ZEROIZE_ALT)
/*
 * This implementation should never be optimized out by the compiler
 *
 * This implementation for mbedtls_platform_zeroize() was inspired from Colin
 * Percival's blog article at:
 *
 * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html
 *
 * It uses a volatile function pointer to the standard memset(). Because the
 * pointer is volatile the compiler expects it to change at
 * any time and will not optimize out the call that could potentially perform
 * other operations on the input buffer instead of just setting it to 0.
 * Nevertheless, as pointed out by davidtgoldblatt on Hacker News
 * (refer to http://www.daemonology.net/blog/2014-09-05-erratum.html for
 * details), optimizations of the following form are still possible:
 *
 * if( memset_func != memset )
 *     memset_func( buf, 0, len );
 *
 * Note that it is extremely difficult to guarantee that
 * mbedtls_platform_zeroize() will not be optimized out by aggressive compilers
 * in a portable way. For this reason, Mbed TLS also provides the configuration
 * option MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure
 * mbedtls_platform_zeroize() to use a suitable implementation for their
 * platform and needs.
 */
static void * (* const volatile memset_func)( void *<
/* * 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*)&params, PARAMS_SECTOR_ADDR, sizeof(SystemParams)); // 验证数据有效性 uint8_t valid = 1; if(params.magic != PARAMS_MAGIC) { valid = 0; } else { uint32_t calc_checksum = calculate_checksum(&params); 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和时间的设时自动跳转到<--改善代码修改这个问题
07-12
#include "Function.h" #include "HeaderFiles.h" #include "LED.h" #include "MYRTC.h" #include "USART.h" #include "MYflash.h" #include "ff.h" #include "diskio.h" #include "sdcard.h" #include "OLED.h" #include "Key.h" #include "ADC.h" #include "Fmc.h" #include "Timer.h" /* 全局变量定义 */ typedef struct { uint8_t Hours; uint8_t Minutes; uint8_t Seconds; }RTC_TimeTypeDef; typedef struct { uint8_t Year; uint8_t Month; uint8_t Date; uint8_t WeekDay; }RTC_DateTypeDef; uint16_t t = 0; volatile uint8_t systemState = 0; // 系统状态 volatile uint8_t samplingState = 0; // 采样状态 volatile uint32_t samplingCycle = 5; // 采样周期(s) float voltageRatio = 1.0; // 电压变比 float alarmLimit = 100.0; // 报警阈值 RTC_TimeTypeDef currentTime; // 当前时间 RTC_DateTypeDef currentDate; // 当前日期 float currentVoltage = 0.0; // 当前电压值 uint8_t isVoltageOverLimit = 0; // 电压是否超限 /* 函数声明 */ void System_Init(void); void ratio_config(void); void limit_config(void); void data_hide(void); void data_unhide(void); void config_save_to_flash(void); void config_read_from_flash(void); void CommandProcess(void); void DataAcquisition(void); void DataProcessing(void); void DataStorage(void); void DisplayUpdate(void); void SystemSelfTest(void); void ConfigManagement(void); void SamplingControl(void); uint32_t rtc_convert_to_unix(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); /* 系统初始化函数 */ void System_Init(void) { systick_config(); LED_Init(); KEY_Init(); OLED_Init(); ADC_Init(); rtc_config(); norflash_init(); Timer1_Init(1000-1,120-1); USART0_Init(); } void UsrFunction(void) { /* 系统初始化 */ System_Init(); printf("====system init====\r\n"); /* 从Flash读取设备ID */ uint16_t flashId = norflash_read_id(); printf("%04x:2025-CIMC-2025354696\r\n", flashId); printf("====system ready====\r\n"); OLED_ShowString(0, 0, "system running", 16); OLED_Refresh(); while (1) { /* 处理串口命令 */ CommandProcess(); /* 按键检测 */ uint8_t key = KEY_Scan(0); if (key > 0) { switch (key) { case KEY1_PRES:samplingState = !samplingState; if (samplingState) { timer_enable(TIMER1); printf("Periodic Sampling\r\n"); printf("sample cycle: %ds\r\n", samplingCycle); } else { timer_disable(TIMER1); printf("Periodic Sampling STOP\r\n"); OLED_Clear(); LED1(0); OLED_ShowString(0, 0, "system idle", 16); OLED_Refresh(); } break; case KEY2_PRES:samplingCycle = 5; printf("sample cycle adjust: 5s\r\n"); break; case KEY3_PRES:samplingCycle = 10; printf("sample cycle adjust: 10s\r\n"); break; case KEY4_PRES:samplingCycle = 15; printf("sample cycle adjust: 15s\r\n"); break; default:break; } } /* 数据采集 */ if (samplingState == 1) DisplayUpdate(); } } /* 命令处理函数 */ void CommandProcess(void) { static char commandBuffer[32]; static uint8_t cmdIndex = 0; uint8_t ch; /* 读取串口数据 */ if(usart_flag_get(USART0, USART_FLAG_RBNE) != RESET) { ch = usart_data_receive(USART0); /* 处理命令缓冲区 */ if (ch == '\r' || ch == '\n') { commandBuffer[cmdIndex] = '\0'; cmdIndex = 0; /* 解析命令 */ if (strcmp(commandBuffer, "test") == 0)//输入test SystemSelfTest(); else if (strcmp(commandBuffer, "RTC Config") == 0)//输入RTC Config { printf("Input Datetime\r\n"); /*输入时间*/ while(getchar() != '\n');// 清空输入缓冲区 if(fgets(commandBuffer, sizeof(commandBuffer), stdin) != NULL) { /*解析时间格式*/ uint16_t year; uint8_t month, day, hour, minute, second; if(sscanf(commandBuffer, "%hu-%hhu-%hhu %hhu:%hhu:%hhu", &year, &month, &day, &hour, &minute, &second) == 6) { rtc_set_time(hour, minute, second, 0); rtc_set_date(year % 100, month, day, rtc_get_week(year, month, day)); printf("RTC Config success\r\n"); printf("Time:%04hu-%02hhu-%02hhu %02hhu:%02hhu:%02hhu\r\n", year, month, day, hour, minute, second); } } } else if (strcmp(commandBuffer, "RTC now") == 0)//输入RTC now { rtc_get_time(&currentTime.Hours, &currentTime.Minutes, &currentTime.Seconds, NULL); rtc_get_date(&currentDate.Year, &currentDate.Month, &currentDate.Date, &currentDate.WeekDay); printf("Current Time: %04d-%02d-%02d %02d:%02d:%02d\r\n", currentDate.Year+2000, currentDate.Month, currentDate.Date, currentTime.Hours, currentTime.Minutes,currentTime.Seconds); } else if (strcmp(commandBuffer, "ratio") == 0) ratio_config(); else if (strcmp(commandBuffer, "start") == 0) { samplingState = 1; printf("Periodic Sampling\r\n"); printf("sample cycle: %ds\r\n", samplingCycle); timer_enable(TIMER1); memset(commandBuffer, 0, sizeof(commandBuffer)); } else if (strcmp(commandBuffer, "stop") == 0) { samplingState = 0; timer_disable(TIMER1); LED1(0); printf("Periodic Sampling STOP\r\n"); OLED_ShowString(0, 0, "system idle", 16); OLED_Refresh(); memset(commandBuffer, 0, sizeof(commandBuffer)); } } else if (cmdIndex < 31) commandBuffer[cmdIndex++] = ch; } } /* 系统自检函数 */ void SystemSelfTest(void) { printf("======system selftest======\r\n"); /* 检测Flash */ uint16_t flashId = norflash_read_id(); if (flashId != 0) printf("flash.......ok\r\n"); /* 检测SD卡 */ if (sd_init() == SD_OK) printf("TF card............ok\r\n"); else printf("TF card............error\r\n"); /* FlashID */ printf("flash ID:%04x\r\n",flashId); if (sd_init() == SD_OK) printf("TF card memory:%dKB\r\n", sd_card_capacity_get()); else printf("can not find TF card\r\n"); /* 检测RTC */ rtc_get_time(&currentTime.Hours, &currentTime.Minutes, &currentTime.Seconds, NULL); rtc_get_date(&currentDate.Year, &currentDate.Month, &currentDate.Date, &currentDate.WeekDay); printf("RTC:%04d-%02d-%02d%02d:%02d:%02d\r\n", currentDate.Year + 2000, currentDate.Month, currentDate.Date, currentTime.Hours, currentTime.Minutes, currentTime.Seconds); printf("======system selftest======\r\n"); } void ratio_config(void) { float newRatio; printf("Ratio=%.1f\r\nInput value(0-100):\r\n", voltageRatio); /* 等待用户输入 */ static char inputBuffer[16] = {0}; static uint8_t inputIndex = 0; uint8_t ch; if(usart_flag_get(USART0, USART_FLAG_RBNE) != RESET) { ch = usart_data_receive(USART0); if (ch == '\r' || ch == '\n') { inputBuffer[inputIndex] = '\0'; inputIndex = 0; if(fgets(inputBuffer, sizeof(inputBuffer), stdin) != NULL) { if (sscanf(inputBuffer, "%f", &newRatio) == 1) { if (newRatio >= 0 && newRatio <= 100) { voltageRatio = newRatio; printf("ratio modified success\r\nRatio = %.1f\r\n", voltageRatio); } else printf("ratio invalid\r\nRatio=%.1f\r\n", voltageRatio); } } } else if (inputIndex < 15) inputBuffer[inputIndex++] = ch; } } /* 显示更新函数 */ void DisplayUpdate(void) { /* 采集ADC数据 */ uint16_t adcValue = adc_get_result_average(ADC_CHANNEL_10, 10); /* 转换为电压值 */ currentVoltage = (float)adcValue * 3.3 / 4096 * voltageRatio; /* 更新OLED显示 */ OLED_ShowString(0, 0, "Time:", 16); OLED_ShowString(0, 16, "Voltage:", 16); /* 更新时间 */ rtc_get_time(&currentTime.Hours, &currentTime.Minutes, &currentTime.Seconds, NULL); char timeStr[10]; sprintf(timeStr, "%02d:%02d:%02d ", currentTime.Hours, currentTime.Minutes, currentTime.Seconds); OLED_ShowString(40, 0, timeStr, 16); /* 更新电压 */ char voltageStr[8]; sprintf(voltageStr, "%.2f V", currentVoltage); OLED_ShowString(64, 16, voltageStr, 16); OLED_Refresh(); } 为什么我输入start后,再输入stop没有反应
06-16
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值