初始化demo版本

This commit is contained in:
冯佳
2025-12-19 10:22:28 +08:00
parent 1cd79af915
commit 294a49f207
22 changed files with 2101 additions and 0 deletions

666
menu.c Normal file
View File

@ -0,0 +1,666 @@
/**
**********************************************************************************************************************
* @file cot_menu.c
* @brief 该文件提供菜单框架功能
* @author const_zpc any question please send mail to const_zpc@163.com
* @version V1.3.0
* @date 2024-06-09
*
* @details 功能详细说明:
* + 菜单初始化函数
* + 返回主菜单函数
* + 菜单控制函数
* + 菜单轮询任务函数
*
**********************************************************************************************************************
* 源码路径https://gitee.com/cot_package/cot_menu.git 具体问题及建议可在该网址填写 Issue
*
* 使用方式:
* 1、使用前初始化函数 cotMenu_Init, 设置主菜单内容
* 2、周期调用函数 cotMenu_Task, 用来处理菜单显示和执行相关回调函数
* 3、使用其他函数对菜单界面控制
*
**********************************************************************************************************************
*/
/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "menu.h"
#ifdef _MENU_USE_MALLOC_
#include <malloc.h>
#endif
#ifdef _MENU_USE_SHORTCUT_
#include <stdarg.h>
#endif
/* Private typedef ---------------------------------------------------------------------------------------------------*/
typedef struct MenuCtrl
{
struct MenuCtrl *pParentMenuCtrl; /*!< 父菜单控制处理 */
MenuDsecStr_u uMenuDesc; /*!< 当前菜单的字符串描述 */
void *pExtendData; /*!< 当前选项注册时的扩展数据 */
ShowMenuCallFun_f pfnShowMenuFun; /*!< 当前菜单显示效果函数 */
MenuList_t *pMenuList; /*!< 当前菜单列表 */
MenuCallFun_f pfnLoadCallFun; /*!< 当前菜单加载函数 */
MenuCallFun_f pfnRunCallFun; /*!< 当前选项的非菜单功能函数 */
menusize_t itemsNum; /*!< 当前菜单选项总数目 */
menusize_t showBaseItem; /*!< 当前菜单首个显示的选项 */
menusize_t selectItem; /*!< 当前菜单选中的选项 */
bool isSelected; /*!< 菜单选项是否已经被选择 */
}MenuCtrl_t;
typedef struct
{
MenuCtrl_t *pMenuCtrl; /*!< 当前菜单控制处理 */
MenuCallFun_f pfnMainEnterCallFun; /*!< 主菜单进入时(进入菜单)需要执行一次的函数 */
MenuCallFun_f pfnMainExitCallFun; /*!< 主菜单进入后退出时(退出菜单)需要执行一次的函数 */
MenuCallFun_f pfnLoadCallFun; /*!< 重加载函数 */
uint8_t isEnterMainMenu : 1; /*!< 是否进入了主菜单 */
}MenuManage_t;
/* Private define ----------------------------------------------------------------------------------------------------*/
/* Private macro -----------------------------------------------------------------------------------------------------*/
/* Private variables -------------------------------------------------------------------------------------------------*/
static MenuManage_t sg_tMenuManage;
#ifndef _MENU_USE_MALLOC_
static MenuCtrl_t sg_arrMenuCtrl[COT_MENU_MAX_DEPTH];
#endif
static uint8_t sg_currMenuDepth = 0;
/* Private function prototypes ---------------------------------------------------------------------------------------*/
static MenuCtrl_t *NewMenu(void);
static void DeleteMenu(MenuCtrl_t *pMenu);
/* Private function --------------------------------------------------------------------------------------------------*/
/**
* @brief 新建菜单层级
*
* @return MenuCtrl_t*
*/
static MenuCtrl_t *NewMenu(void)
{
MenuCtrl_t *pMenuCtrl = NULL;
if (sg_currMenuDepth < COT_MENU_MAX_DEPTH)
{
#ifdef _COT_MENU_USE_MALLOC_
pMenuCtrl = (MenuCtrl_t *)malloc(sizeof(MenuCtrl_t));
#else
pMenuCtrl = &sg_arrMenuCtrl[sg_currMenuDepth];
#endif
sg_currMenuDepth++;
}
return pMenuCtrl;
}
/**
* @brief 销毁菜单层级
*
* @param pMenu
*/
static void DeleteMenu(MenuCtrl_t *pMenu)
{
#ifdef _COT_MENU_USE_MALLOC_
free(pMenu);
#endif
if (sg_currMenuDepth > 0)
{
sg_currMenuDepth--;
}
}
/**
* @brief 菜单初始化
*
* @param[in] pMainMenu 主菜单注册信息
* @return 0,成功; -1,失败
*/
int Menu_Init(MainMenuCfg_t *pMainMenu)
{
MenuCtrl_t *pNewMenuCtrl = NULL;
if (sg_tMenuManage.pMenuCtrl != NULL)
{
return -1;
}
#if COT_MENU_MAX_DEPTH != 0
sg_currMenuDepth = 0;
#endif
if ((pNewMenuCtrl = NewMenu()) == NULL)
{
return -1;
}
pNewMenuCtrl->uMenuDesc = pMainMenu->uMenuDesc;
pNewMenuCtrl->pExtendData = pMainMenu->pExtendData;
pNewMenuCtrl->pParentMenuCtrl = NULL;
pNewMenuCtrl->pfnLoadCallFun = pMainMenu->pfnLoadCallFun;
pNewMenuCtrl->pfnShowMenuFun = NULL;
pNewMenuCtrl->pfnRunCallFun = pMainMenu->pfnRunCallFun;
pNewMenuCtrl->pMenuList = NULL;
pNewMenuCtrl->itemsNum = 0;
pNewMenuCtrl->selectItem = 0;
pNewMenuCtrl->showBaseItem = 0;
sg_tMenuManage.pMenuCtrl = pNewMenuCtrl;
sg_tMenuManage.isEnterMainMenu = 0;
sg_tMenuManage.pfnMainEnterCallFun = pMainMenu->pfnEnterCallFun;
sg_tMenuManage.pfnMainExitCallFun = pMainMenu->pfnExitCallFun;
sg_tMenuManage.pfnLoadCallFun = pNewMenuCtrl->pfnLoadCallFun;
return 0;
}
/**
* @brief 菜单反初始化
*
* @attention 不管处于任何界面都会逐级退出到主菜单后(会调用退出函数),再退出主菜单,最后反初始化
* @return 0,成功; -1,失败
*/
int Menu_DeInit(void)
{
if (sg_tMenuManage.pMenuCtrl == NULL)
{
return -1;
}
Menu_MainExit();
DeleteMenu(sg_tMenuManage.pMenuCtrl);
sg_tMenuManage.pMenuCtrl = NULL;
sg_tMenuManage.isEnterMainMenu = 0;
sg_tMenuManage.pfnMainEnterCallFun = NULL;
sg_tMenuManage.pfnMainExitCallFun = NULL;
sg_tMenuManage.pfnLoadCallFun = NULL;
return 0;
}
/**
* @brief 子菜单绑定当前菜单选项
*
* @param pMenuList 新的菜单列表
* @param menuNum 新的菜单列表数目
* @param pfnShowMenuFun 新的菜单列表显示效果回调函数, 为NULL则延续上级菜单显示效果
* @return 0,成功; -1,失败
*/
int Menu_Bind(MenuList_t *pMenuList, menusize_t menuNum, ShowMenuCallFun_f pfnShowMenuFun)
{
if (sg_tMenuManage.pMenuCtrl == NULL)
{
return -1;
}
if (sg_tMenuManage.pMenuCtrl->pMenuList != NULL)
{
return 0;
}
sg_tMenuManage.pMenuCtrl->pMenuList = pMenuList;
sg_tMenuManage.pMenuCtrl->itemsNum = menuNum;
if (pfnShowMenuFun != NULL)
{
sg_tMenuManage.pMenuCtrl->pfnShowMenuFun = pfnShowMenuFun;
}
return 0;
}
/**
* @brief 复位菜单, 回到主菜单界面
*
* @note 该复位回到主菜单不会执行退出所需要执行的回调函数
* @return 0,成功; -1,失败
*/
int Menu_Reset(void)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
while (sg_tMenuManage.pMenuCtrl->pParentMenuCtrl != NULL)
{
MenuCtrl_t *pMenuCtrl = sg_tMenuManage.pMenuCtrl;
sg_tMenuManage.pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;
DeleteMenu(pMenuCtrl);
}
sg_tMenuManage.pMenuCtrl->selectItem = 0;
return 0;
}
/**
* @brief 主菜单进入
*
* @return 0,成功; -1,失败
*/
int Menu_MainEnter(void)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 1)
{
return -1;
}
if (sg_tMenuManage.pfnMainEnterCallFun != NULL)
{
MenuItemInfo_t tItemInfo;
tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;
sg_tMenuManage.pfnMainEnterCallFun(&tItemInfo);
}
sg_tMenuManage.isEnterMainMenu = 1;
sg_tMenuManage.pfnLoadCallFun = sg_tMenuManage.pMenuCtrl->pfnLoadCallFun;
return 0;
}
/**
* @brief 主菜单退出
*
* @attention 不管处于任何界面都会逐级退出到主菜单后(会调用退出函数),再退出主菜单
* @return 0,成功; -1,失败
*/
int Menu_MainExit(void)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
while (Menu_Exit(1) == 0){}
if (sg_tMenuManage.pfnMainExitCallFun != NULL)
{
sg_tMenuManage.pfnMainExitCallFun(NULL);
}
sg_tMenuManage.isEnterMainMenu = 0;
return 0;
}
/**
* @brief 进入当前菜单选项
*
* @return 0,成功; -1,失败
*/
int Menu_Enter(void)
{
MenuCtrl_t *pNewMenuCtrl = NULL;
MenuCtrl_t *pCurrMenuCtrl = sg_tMenuManage.pMenuCtrl;
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if ((pNewMenuCtrl = NewMenu()) == NULL)
{
return -1;
}
pNewMenuCtrl->uMenuDesc = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].uMenuDesc;
pNewMenuCtrl->pExtendData = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pExtendData;
pNewMenuCtrl->pMenuList = NULL;
pNewMenuCtrl->itemsNum = 0;
pNewMenuCtrl->pfnShowMenuFun = pCurrMenuCtrl->pfnShowMenuFun;
pNewMenuCtrl->pfnLoadCallFun = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnLoadCallFun;
pNewMenuCtrl->pfnRunCallFun = pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnRunCallFun;
pNewMenuCtrl->selectItem = 0;
pNewMenuCtrl->isSelected = true;
pNewMenuCtrl->pParentMenuCtrl = pCurrMenuCtrl;
sg_tMenuManage.pMenuCtrl = pNewMenuCtrl;
sg_tMenuManage.pfnLoadCallFun = pNewMenuCtrl->pfnLoadCallFun;
if (pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallFun != NULL)
{
MenuItemInfo_t tItemInfo;
tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;
pCurrMenuCtrl->pMenuList[pCurrMenuCtrl->selectItem].pfnEnterCallFun(&tItemInfo);
}
return 0;
}
/**
* @brief 退出当前选项并返回上一层菜单
*
* @param[in] isReset 菜单选项是否从头选择
* @return 0,成功; -1,失败, 即目前处于主菜单, 无法返回
*/
int Menu_Exit(bool isReset)
{
MenuCtrl_t *pMenuCtrl = sg_tMenuManage.pMenuCtrl;
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (sg_tMenuManage.pMenuCtrl->pParentMenuCtrl == NULL)
{
return -1;
}
sg_tMenuManage.pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;
sg_tMenuManage.pfnLoadCallFun = sg_tMenuManage.pMenuCtrl->pfnLoadCallFun;
DeleteMenu(pMenuCtrl);
pMenuCtrl = NULL;
if (sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pfnExitCallFun != NULL)
{
MenuItemInfo_t tItemInfo;
tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].uMenuDesc;
tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pExtendData;
sg_tMenuManage.pMenuCtrl->isSelected = false;
sg_tMenuManage.pMenuCtrl->pMenuList[sg_tMenuManage.pMenuCtrl->selectItem].pfnExitCallFun(&tItemInfo);
}
if (isReset)
{
sg_tMenuManage.pMenuCtrl->selectItem = 0;
}
return 0;
}
/**
* @brief 选择上一个菜单选项
*
* @param[in] isAllowRoll 第一个选项时是否从跳转到最后一个选项
* @return 0,成功; -1,失败
*/
int Menu_SelectPrevious(bool isAllowRoll)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (sg_tMenuManage.pMenuCtrl->selectItem > 0)
{
sg_tMenuManage.pMenuCtrl->selectItem--;
}
else
{
if (isAllowRoll)
{
sg_tMenuManage.pMenuCtrl->selectItem = sg_tMenuManage.pMenuCtrl->itemsNum - 1;
}
else
{
sg_tMenuManage.pMenuCtrl->selectItem = 0;
return -1;
}
}
return 0;
}
/**
* @brief 选择下一个菜单选项
*
* @param[in] isAllowRoll 最后一个选项时是否跳转到第一个选项
* @return 0,成功; -1,失败
*/
int Menu_SelectNext(bool isAllowRoll)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (sg_tMenuManage.pMenuCtrl->selectItem < (sg_tMenuManage.pMenuCtrl->itemsNum - 1))
{
sg_tMenuManage.pMenuCtrl->selectItem++;
}
else
{
if (isAllowRoll)
{
sg_tMenuManage.pMenuCtrl->selectItem = 0;
}
else
{
sg_tMenuManage.pMenuCtrl->selectItem = sg_tMenuManage.pMenuCtrl->itemsNum - 1;
return -1;
}
}
return 0;
}
/**
* @brief 选择指定的菜单选项
*
* @param selectItem 指定的菜单选项
* @return 0,成功; -1,失败
*/
int Menu_Select(menusize_t selectItem)
{
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (selectItem >= sg_tMenuManage.pMenuCtrl->itemsNum)
{
return -1;
}
sg_tMenuManage.pMenuCtrl->selectItem = selectItem;
return 0;
}
#ifdef _MENU_USE_SHORTCUT_
/**
* @brief 相对主菜单或当前菜单通过下级各菜单索引快速进入指定选项
*
* @param[in] isAbsolute 是否采用绝对菜单索引(从主菜单开始)
* @param[in] deep 菜单深度,大于 0
* @param[in] ... 各级菜单索引值(从0开始), 入参个数由 deep 的值决定
* @return 0,成功; -1,失败
*/
int Menu_ShortcutEnter(bool isAbsolute, uint8_t deep, ...)
{
uint8_t selectDeep = 0;
va_list pItemList;
menusize_t selectItem;
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.pMenuCtrl->pMenuList == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (isAbsolute)
{
Menu_Reset();
}
va_start(pItemList, deep);
while (selectDeep < deep)
{
selectItem = va_arg(pItemList, int);
if (selectItem >= sg_tMenuManage.pMenuCtrl->itemsNum)
{
va_end(pItemList);
return -1;
}
sg_tMenuManage.pMenuCtrl->selectItem = selectItem;
Menu_Enter();
selectDeep++;
}
va_end(pItemList);
return 0;
}
#endif
/**
* @brief 限制当前菜单界面最多显示的菜单数目
*
* @note 在菜单显示效果回调函数中使用, 使用成员变量 showBaseItem 得到显示界面的第一个选项索引
* @param[in,out] tMenuShow 当前菜单显示信息
* @param[in,out] showNum 当前菜单中需要显示的选项数目, 根据当前菜单选项的总数得到最终的显示的选项数目
* @return 0,成功; -1,失败
*/
int Menu_LimitShowListNum(MenuShow_t *ptMenuShow, menusize_t *pShowNum)
{
if (ptMenuShow == NULL || pShowNum == NULL)
{
return -1;
}
if (*pShowNum > ptMenuShow->itemsNum)
{
*pShowNum = ptMenuShow->itemsNum;
}
if (ptMenuShow->selectItem < ptMenuShow->showBaseItem)
{
ptMenuShow->showBaseItem = ptMenuShow->selectItem;
}
else if ((ptMenuShow->selectItem - ptMenuShow->showBaseItem) >= *pShowNum)
{
ptMenuShow->showBaseItem = ptMenuShow->selectItem - *pShowNum + 1;
}
else
{
// 保持
}
return 0;
}
/**
* @brief 获取当前父菜单显示信息
* 如获取当前菜单的二级父菜单信息level 为2
*
* @param[out] ptMenuShow 父 n 级菜单显示信息
* @param[in] level n 级, 大于 0
* @return 0,成功; -1,失败
*/
int Menu_QueryParentMenu(MenuShow_t *ptMenuShow, uint8_t level)
{
int i;
MenuList_t *pMenu;
MenuCtrl_t *pMenuCtrl = NULL;
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
pMenuCtrl = sg_tMenuManage.pMenuCtrl->pParentMenuCtrl;
while (level && pMenuCtrl != NULL)
{
pMenu = pMenuCtrl->pMenuList;
ptMenuShow->itemsNum = pMenuCtrl->itemsNum;
ptMenuShow->selectItem = pMenuCtrl->selectItem;
ptMenuShow->showBaseItem = pMenuCtrl->showBaseItem;
ptMenuShow->uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
for (i = 0; i < ptMenuShow->itemsNum && i < COT_MENU_MAX_NUM; i++)
{
ptMenuShow->uItemsListDesc[i] = pMenu[i].uMenuDesc;
ptMenuShow->pItemsListExtendData[i] = pMenu[i].pExtendData;
}
pMenuCtrl = pMenuCtrl->pParentMenuCtrl;
level--;
}
if (level != 0 && pMenuCtrl == NULL)
{
return -1;
}
return 0;
}
/**
* @brief 菜单任务
*
* @return 0,成功, 处于菜单模式下; -1,失败, 未处于菜单模式下
*/
int Menu_Task(void)
{
int i;
MenuList_t *pMenuList;
MenuShow_t tMenuShow;
if (sg_tMenuManage.pMenuCtrl == NULL || sg_tMenuManage.isEnterMainMenu == 0)
{
return -1;
}
if (sg_tMenuManage.pfnLoadCallFun != NULL)
{
MenuItemInfo_t tItemInfo;
tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;
sg_tMenuManage.pfnLoadCallFun(&tItemInfo);
sg_tMenuManage.pfnLoadCallFun = NULL;
}
if (sg_tMenuManage.pMenuCtrl->pMenuList != NULL)
{
pMenuList = sg_tMenuManage.pMenuCtrl->pMenuList;
tMenuShow.itemsNum = sg_tMenuManage.pMenuCtrl->itemsNum;
tMenuShow.selectItem = sg_tMenuManage.pMenuCtrl->selectItem;
tMenuShow.showBaseItem = sg_tMenuManage.pMenuCtrl->showBaseItem;
tMenuShow.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
tMenuShow.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;
for (i = 0; i < tMenuShow.itemsNum && i < COT_MENU_MAX_NUM; i++)
{
tMenuShow.uItemsListDesc[i] = pMenuList[i].uMenuDesc;
tMenuShow.pItemsListExtendData[i] = pMenuList[i].pExtendData;
}
if (sg_tMenuManage.pMenuCtrl->pfnShowMenuFun != NULL)
{
sg_tMenuManage.pMenuCtrl->pfnShowMenuFun(&tMenuShow);
}
sg_tMenuManage.pMenuCtrl->showBaseItem = tMenuShow.showBaseItem;
}
if (sg_tMenuManage.pMenuCtrl->pfnRunCallFun != NULL)
{
MenuItemInfo_t tItemInfo;
tItemInfo.uMenuDesc = sg_tMenuManage.pMenuCtrl->uMenuDesc;
tItemInfo.pExtendData = sg_tMenuManage.pMenuCtrl->pExtendData;
sg_tMenuManage.pMenuCtrl->pfnRunCallFun(&tItemInfo);
}
return 0;
}