BetaFlight模块设计之二十:CMS菜单模块分析

  • CMS菜单模块
  • CMS菜单按键控制
  • CMS菜单Elements
    • CMS_Menu
    • OSD_Etnry
    • Element类型
    • 可调Element类型
  • CMS菜单结构
    • 第一层菜单(Top Menu)
    • 第二层菜单(SubMenu)
      • IMU菜单
      • FEATURES菜单
      • CMS菜单
      • FIRMWARE菜单
      • MISC菜单
  • CMS菜单代码
  • 主要函数分析
    • cmsHandler
    • cmsUpdate
    • cmsScanKeys
    • cmsHandleKeyWithRepeat
    • cmsHandleKey
    • 其他函数
      • 1. display相关函数
      • 2. cmsMenu相关函数
      • 3. cmsDrawMenu相关函数
      • 4. cmsPage相关函数

基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。

CMS菜单模块

描述:将BetaFlight所有配置内容在不同的设备上进行CMS(Custom Menu System)菜单显示。

 ├──> 初始化│   ├──> [x]硬件初始化│   └──> [v]业务初始化cmsInit├──> 任务│   ├──> [x]实时任务│   ├──> [x]事件任务│   └──> [v]时间任务[TASK_CMS] = DEFINE_TASK("CMS", NULL, NULL, cmsHandler, TASK_PERIOD_HZ(20), TASK_PRIORITY_LOW),├──> 驱动│   ├──> [x]查询│   └──> [x]中断└──> 接口├──> bool cmsDisplayPortRegister(displayPort_t *pDisplay);  //注册显示CMS菜单的设备,可以注册多个。├──> void cmsMenuOpen(void); //打开菜单├──> const void *cmsMenuChange(displayPort_t *pPort, const void *ptr); //进入子菜单├──> const void *cmsMenuExit(displayPort_t *pPort, const void *ptr); //退出菜单├──> void cmsAddMenuEntry(OSD_Entry *menuEntry, char *text, uint16_t flags, CMSEntryFuncPtr func, void *data);└──> void cmsSetExternKey(cms_key_e extKey);

注:多菜单显示设备支持,详见支持的设备类型。

typedef enum {DISPLAYPORT_DEVICE_TYPE_MAX7456 = 0,DISPLAYPORT_DEVICE_TYPE_OLED,DISPLAYPORT_DEVICE_TYPE_MSP,DISPLAYPORT_DEVICE_TYPE_FRSKYOSD,DISPLAYPORT_DEVICE_TYPE_CRSF,DISPLAYPORT_DEVICE_TYPE_HOTT,DISPLAYPORT_DEVICE_TYPE_SRXL,
} displayPortDeviceType_e;

CMS菜单按键控制

  • 打开菜单:IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH)
  • 进入菜单:IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) //菜单打开的情况下
  • 向上翻页: IS_HI(PITCH)
  • 向下翻页:IS_LO(PITCH)
  • 向左翻页:IS_LO(ROLL)
  • 向右翻页:IS_HI(ROLL)
  • 退出选项:IS_LO(YAW)
  • 保存菜单:IS_HI(YAW)

CMS菜单Elements

整个菜单从组成结构上由elements构成,分为两大类:CMS_Menu、OSD_Etnry。

CMS_Menu

typedef const void *(*CMSMenuFuncPtr)(displayPort_t *pDisp);
typedef const void *(*CMSMenuOnExitPtr)(displayPort_t *pDisp, const OSD_Entry *self);
typedef const void *(*CMSMenuOnDisplayUpdatePtr)(displayPort_t *pDisp, const OSD_Entry *selected);
typedef struct
{#ifdef CMS_MENU_DEBUG// These two are debug aids for menu content creators.const char *GUARD_text;const OSD_MenuElement GUARD_type;
#endifconst CMSMenuFuncPtr onEnter;const CMSMenuOnExitPtr onExit;const CMSMenuOnDisplayUpdatePtr onDisplayUpdate;const OSD_Entry *entries;
} CMS_Menu;

OSD_Etnry

typedef const void *(*CMSEntryFuncPtr)(displayPort_t *displayPort, const void *ptr);
typedef struct
{const char * text;// Logical OR of OSD_MenuElement and flags belowuint16_t flags;CMSEntryFuncPtr func;void *data;
} __attribute__((packed)) OSD_Entry;

Element类型

各种类型对应的菜单操作详见cmsHandleKey函数。

typedef enum
{OME_Label,    //标记字符串OME_Back,     //返回前级菜单OME_OSD_Exit,OME_Submenu,  //子菜单类型OME_Funcall,  //执行函数OME_Bool,OME_INT8,     //OSD_INT8_tOME_UINT8,    //OSD_UINT8_tOME_UINT16,   //OSD_UINT16_tOME_INT16,    //OSD_INT16_tOME_UINT32,   //OSD_UINT32_tOME_INT32,    //OSD_INT32_tOME_String,   //OSD_String_tOME_FLOAT,    //OSD_FLOAT_t
#ifdef USE_OSDOME_VISIBLE,
#endifOME_TAB,      //OSD_TAB_t, 列表OME_END,// Debug aidOME_MENU,OME_MAX = OME_MENU
} OSD_MenuElement;

可调Element类型

typedef struct
{uint8_t *val;uint8_t min;uint8_t max;uint8_t step;
} OSD_UINT8_t;typedef struct
{int8_t *val;int8_t min;int8_t max;int8_t step;
} OSD_INT8_t;typedef struct
{int16_t *val;int16_t min;int16_t max;int16_t step;
} OSD_INT16_t;typedef struct
{uint16_t *val;uint16_t min;uint16_t max;uint16_t step;
} OSD_UINT16_t;typedef struct
{int32_t *val;int32_t min;int32_t max;int32_t step;
} OSD_INT32_t;typedef struct
{uint32_t *val;uint32_t min;uint32_t max;uint32_t step;
} OSD_UINT32_t;typedef struct
{uint8_t *val;uint8_t min;uint8_t max;uint8_t step;uint16_t multipler;
} OSD_FLOAT_t;typedef struct
{uint8_t *val;uint8_t max;const char * const *names;
} OSD_TAB_t;

CMS菜单结构

第一层菜单(Top Menu)

CMS_Menu cmsx_menuMain = {#ifdef CMS_MENU_DEBUG.GUARD_text = "MENUMAIN",.GUARD_type = OME_MENU,
#endif.onEnter = mainMenuOnEnter,.onExit = NULL,.onDisplayUpdate = NULL,.entries = menuMainEntries,
};
static const OSD_Entry menuMainEntries[] =
{{"-- MAIN --",  OME_Label, NULL, NULL},{"PROFILE",     OME_Submenu,  cmsMenuChange, &cmsx_menuImu},{"FEATURES",    OME_Submenu,  cmsMenuChange, &cmsx_menuFeatures},
#ifdef USE_OSD{"OSD",         OME_Submenu,  cmsMenuChange, &cmsx_menuOsd},
#endif{"FC&FIRMWARE", OME_Submenu,  cmsMenuChange, &cmsx_menuFirmware},{"MISC",        OME_Submenu,  cmsMenuChange, &cmsx_menuMisc},{"SAVE/EXIT",   OME_Funcall,  cmsx_SaveExitMenu, NULL},{NULL, OME_END, NULL, NULL},
};

第二层菜单(SubMenu)

IMU菜单

CMS_Menu cmsx_menuImu = {#ifdef CMS_MENU_DEBUG.GUARD_text = "XIMU",.GUARD_type = OME_MENU,
#endif.onEnter = cmsx_menuImu_onEnter,.onExit = cmsx_menuImu_onExit,.onDisplayUpdate = NULL,.entries = cmsx_menuImuEntries,
};
static const OSD_Entry cmsx_menuImuEntries[] =
{{ "-- PROFILE --", OME_Label, NULL, NULL},{"PID PROF",  OME_UINT8,   cmsx_profileIndexOnChange,     &(OSD_UINT8_t){ &tmpPidProfileIndex, 1, PID_PROFILE_COUNT, 1}},{"PID",       OME_Submenu, cmsMenuChange,                 &cmsx_menuPid},
#ifdef USE_SIMPLIFIED_TUNING{"SIMPLIFIED TUNING",   OME_Submenu, cmsMenuChange,                 &cmsx_menuSimplifiedTuning},
#endif{"MISC PP",   OME_Submenu, cmsMenuChange,                 &cmsx_menuProfileOther},{"FILT PP",   OME_Submenu, cmsMenuChange,                 &cmsx_menuFilterPerProfile},{"RATE PROF", OME_UINT8,   cmsx_rateProfileIndexOnChange, &(OSD_UINT8_t){ &tmpRateProfileIndex, 1, CONTROL_RATE_PROFILE_COUNT, 1}},{"RATE",      OME_Submenu, cmsMenuChange,                 &cmsx_menuRateProfile},{"FILT GLB",  OME_Submenu, cmsMenuChange,                 &cmsx_menuFilterGlobal},
#if  (defined(USE_DYN_NOTCH_FILTER) || defined(USE_DYN_LPF)) && defined(USE_EXTENDED_CMS_MENUS){"DYN FILT",  OME_Submenu, cmsMenuChange,                 &cmsx_menuDynFilt},
#endif#ifdef USE_EXTENDED_CMS_MENUS{"COPY PROF", OME_Submenu, cmsMenuChange,                 &cmsx_menuCopyProfile},
#endif /* USE_EXTENDED_CMS_MENUS */{"BACK", OME_Back, NULL, NULL},{NULL, OME_END, NULL, NULL}
};

FEATURES菜单

static CMS_Menu cmsx_menuFeatures = {#ifdef CMS_MENU_DEBUG.GUARD_text = "MENUFEATURES",.GUARD_type = OME_MENU,
#endif.onEnter = NULL,.onExit = NULL,.onDisplayUpdate = NULL,.entries = menuFeaturesEntries,
};
static const OSD_Entry menuFeaturesEntries[] =
{{"--- FEATURES ---", OME_Label, NULL, NULL},#if defined(USE_BLACKBOX){"BLACKBOX", OME_Submenu, cmsMenuChange, &cmsx_menuBlackbox},
#endif
#if defined(USE_VTX_CONTROL)
#if defined(USE_VTX_RTC6705) || defined(USE_VTX_SMARTAUDIO) || defined(USE_VTX_TRAMP){"VTX", OME_Funcall, cmsSelectVtx, NULL},
#endif
#endif // VTX_CONTROL
#ifdef USE_LED_STRIP{"LED STRIP", OME_Submenu, cmsMenuChange, &cmsx_menuLedstrip},
#endif // LED_STRIP{"POWER", OME_Submenu, cmsMenuChange, &cmsx_menuPower},
#ifdef USE_CMS_FAILSAFE_MENU{"FAILSAFE", OME_Submenu, cmsMenuChange, &cmsx_menuFailsafe},
#endif
#ifdef USE_PERSISTENT_STATS{"PERSISTENT STATS", OME_Submenu, cmsMenuChange, &cmsx_menuPersistentStats},
#endif{"BACK", OME_Back, NULL, NULL},{NULL, OME_END, NULL, NULL}
};

CMS菜单

CMS_Menu cmsx_menuOsd = {#ifdef CMS_MENU_DEBUG.GUARD_text = "MENUOSD",.GUARD_type = OME_MENU,
#endif.onEnter = cmsx_menuOsdOnEnter,.onExit = cmsx_menuOsdOnExit,.onDisplayUpdate = NULL,.entries = cmsx_menuOsdEntries
};
const OSD_Entry cmsx_menuOsdEntries[] =
{{"---OSD---",   OME_Label,   NULL,          NULL},
#ifdef USE_OSD_PROFILES{"OSD PROFILE", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_osdProfileIndex, 1, 3, 1}},
#endif
#ifdef USE_EXTENDED_CMS_MENUS{"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems},{"TIMERS",      OME_Submenu, cmsMenuChange, &menuTimers},{"ALARMS",      OME_Submenu, cmsMenuChange, &menuAlarms},
#endif
#ifdef USE_MAX7456{"INVERT",    OME_Bool,  cmsx_max7456Update, &displayPortProfileMax7456_invert},{"BRT BLACK", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_blackBrightness, 0, 3, 1}},{"BRT WHITE", OME_UINT8, cmsx_max7456Update, &(OSD_UINT8_t){&displayPortProfileMax7456_whiteBrightness, 0, 3, 1}},
#endif{"BACKGROUND",OME_TAB,   cmsx_osdBackgroundUpdate, &(OSD_TAB_t){&osdMenuBackgroundType, DISPLAY_BACKGROUND_COUNT - 1, lookupTableCMSMenuBackgroundType}},{"BACK", OME_Back, NULL, NULL},{NULL,   OME_END,  NULL, NULL}
};

FIRMWARE菜单

CMS_Menu cmsx_menuFirmware = {#ifdef CMS_MENU_DEBUG.GUARD_text = "MENUFIRMWARE",.GUARD_type = OME_MENU,
#endif
#if defined(USE_BOARD_INFO).onEnter = cmsx_FirmwareInit,
#else.onEnter = NULL,
#endif.onExit = NULL,.onDisplayUpdate = NULL,.entries = menuFirmwareEntries
};
static const OSD_Entry menuFirmwareEntries[] = {{ "--- INFO ---", OME_Label, NULL, NULL },{ "FWID", OME_String, NULL, FC_FIRMWARE_IDENTIFIER },{ "FWVER", OME_String, NULL, FC_VERSION_STRING },{ "GITREV", OME_String, NULL, __REVISION__ },{ "TARGET", OME_String, NULL, __TARGET__ },
#if defined(USE_BOARD_INFO){ "MFR", OME_String, NULL, manufacturerId },{ "BOARD", OME_String, NULL, boardName },
#endif{ "--- SETUP ---", OME_Label, NULL, NULL },{ "CALIBRATE",     OME_Submenu, cmsMenuChange, &cmsx_menuCalibration},{ "BACK", OME_Back, NULL, NULL },{ NULL, OME_END, NULL, NULL}
};

MISC菜单

CMS_Menu cmsx_menuMisc = {#ifdef CMS_MENU_DEBUG.GUARD_text = "XMISC",.GUARD_type = OME_MENU,
#endif.onEnter = cmsx_menuMiscOnEnter,.onExit = cmsx_menuMiscOnExit,.onDisplayUpdate = NULL,.entries = menuMiscEntries
};
static const OSD_Entry menuMiscEntries[]=
{{ "-- MISC --", OME_Label, NULL, NULL },{ "MIN THR",       OME_UINT16 | REBOOT_REQUIRED,  NULL,          &(OSD_UINT16_t){ &motorConfig_minthrottle,            1000, 2000, 1 } },{ "DIGITAL IDLE",  OME_UINT8 | REBOOT_REQUIRED,   NULL,          &(OSD_UINT8_t) { &motorConfig_digitalIdleOffsetValue,    0,  200, 1 } },{ "FPV CAM ANGLE", OME_UINT8,   NULL,          &(OSD_UINT8_t) { &rxConfig_fpvCamAngleDegrees,           0,   90, 1 } },{ "RC PREV",       OME_Submenu, cmsMenuChange, &cmsx_menuRcPreview},{ "BACK", OME_Back, NULL, NULL},{ NULL, OME_END, NULL, NULL}
};

注:只要是OME_Submenu类型的OSD_Entry就一直能通过cmsMenuChange进入下级菜单。

CMS菜单代码

所有菜单相关代码在src/main/cms/路径下。

src/main/cms/├── cms_types.h            //CMS菜单相关类型定义├── cms.c                  //CMS菜单控制操作├── cms.h                  //CMS菜单模块对外接口定义├── cms_menu_blackbox.c├── cms_menu_blackbox.h     ├── cms_menu_failsafe.c├── cms_menu_failsafe.h├── cms_menu_firmware.c├── cms_menu_firmware.h├── cms_menu_gps_rescue.c├── cms_menu_gps_rescue.h├── cms_menu_imu.c├── cms_menu_imu.h├── cms_menu_ledstrip.c├── cms_menu_ledstrip.h├── cms_menu_main.c├── cms_menu_main.h├── cms_menu_misc.c├── cms_menu_misc.h├── cms_menu_osd.c├── cms_menu_osd.h├── cms_menu_persistent_stats.c├── cms_menu_persistent_stats.h├── cms_menu_power.c├── cms_menu_power.h├── cms_menu_saveexit.c├── cms_menu_saveexit.h├── cms_menu_vtx_common.c├── cms_menu_vtx_common.h├── cms_menu_vtx_rtc6705.c├── cms_menu_vtx_rtc6705.h├── cms_menu_vtx_smartaudio.c├── cms_menu_vtx_smartaudio.h├── cms_menu_vtx_tramp.c└── cms_menu_vtx_tramp.h0 directories, 35 files

主要函数分析

cmsHandler

排除没有CMS菜单显示设备的情况。

cmsHandler└──> <cmsDeviceCount > 0>└──> cmsUpdate(currentTimeUs);

cmsUpdate

排除FC瘫痪模式、RunCam设置模式、USB连上电脑场景;并根据cmsInMenu是否已经打开菜单进行逻辑判断。

cmsUpdate├──> <IS_RC_MODE_ACTIVE(BOXPARALYZE)>  //FC瘫痪模式│   └──> return├──> <USE_RCDEVICE><rcdeviceInMenu> //已进入RunCam设置菜单│   └──> return├──> <USE_USB_CDC_HID><cdcDeviceIsMayBeActive> //USB连上电脑(配置或者模拟器等使用方式无需CMS菜单)│   └──> return├──> <!cmsInMenu> //Detect menu invocation│   └──> <IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED) && !IS_RC_MODE_ACTIVE(BOXSTICKCOMMANDDISABLE)>│       ├──> cmsMenuOpen│       └──> rcDelayMs = BUTTON_PAUSE;    // Tends to overshoot if BUTTON_TIME(500ms)├──> <cmsInMenu>│   ├──> displayBeginTransaction(pCurrentDisplay, DISPLAY_TRANSACTION_OPT_RESET_DRAWING);│   ├──> rcDelayMs = cmsScanKeys(currentTimeMs, lastCalledMs, rcDelayMs);│   ├──> cmsDrawMenu(pCurrentDisplay, currentTimeUs);│   ├──> <currentTimeMs > lastCmsHeartBeatMs + 500>│   │   ├──> displayHeartbeat(pCurrentDisplay);│   │   └──> lastCmsHeartBeatMs = currentTimeMs;│   └──> displayCommitTransaction(pCurrentDisplay);└──> lastCalledMs = millis();

cmsScanKeys

在已经打开菜单场景下,对按键进行扫描并做简单按键延时、消抖处理。

注:对于Graupner HoTT protocol 特殊处理,采用externKey来做判断。

cmsScanKeys├──> <externKey != CMS_KEY_NONE>│   ├──> rcDelayMs = cmsHandleKey(pCurrentDisplay, externKey);│   └──> externKey = CMS_KEY_NONE;├──> <externKey == CMS_KEY_NONE>│   ├──> <IS_MID(THROTTLE) && IS_LO(YAW) && IS_HI(PITCH) && !ARMING_FLAG(ARMED)>│   │   └──> key = CMS_KEY_MENU;│   ├──> <IS_HI(PITCH)>│   │   └──> key = CMS_KEY_UP;│   ├──> <IS_LO(PITCH)>│   │   └──> key = CMS_KEY_DOWN;│   ├──> <IS_LO(ROLL)>│   │   └──> key = CMS_KEY_LEFT;│   ├──> <IS_HI(ROLL)>│   │   └──> key = CMS_KEY_RIGHT;│   ├──> <IS_LO(YAW)>│   │   └──> key = CMS_KEY_ESC;│   ├──> <IS_HI(YAW)>│   │   └──> key = CMS_KEY_SAVEMENU;│   ├──> <key == CMS_KEY_NONE> // No 'key' pressed, reset repeat control│   │   └──> holdCount = 1;repeatCount = 1;repeatBase = 0;│   ├──> <key != CMS_KEY_NONE> // The 'key' is being pressed; keep counting│   │   └──> ++holdCount;│   ├──> <rcDelayMs > 0>│   │   └──> rcDelayMs -= (currentTimeMs - lastCalledMs);  //处理按键时长│   └──> <rcDelayMs <= 0 && key> //按键时长满足条件,并确实有按键按下(以最后时间检测到的按键为准)│       ├──> rcDelayMs = cmsHandleKeyWithRepeat(pCurrentDisplay, key, repeatCount);│       └──> [repeatCount] ajustments //根据CMS_KEY_LEFT、CMS_KEY_RIGHT键hold时长来提供repeatCount值,最多调整不超过5└──> return rcDelayMs;

cmsHandleKeyWithRepeat

主要处理CMS_KEY_LEFT、CMS_KEY_RIGHT键hold时长来提供多次连续按键事件。

cmsHandleKeyWithRepeat└──> <for (int i = 0 ; i < repeatCount ; i++)>└──> ret = cmsHandleKey(pDisplay, key); // return ret

cmsHandleKey

按键对CMS Elements的实际操作。

cmsHandleKey├──> <!currentCtx.menu>│   └──> return BUTTON_TIME├──> <key == CMS_KEY_MENU>│   ├──> cmsMenuOpen│   └──> return BUTTON_PAUSE;├──> <key == CMS_KEY_ESC>│   ├──> <osdElementEditing>│   │   └──> osdElementEditing = false;│   ├──> <!osdElementEditing>│   │   └──> cmsMenuBack│   └──> return BUTTON_PAUSE;├──> <key == CMS_KEY_SAVEMENU && !saveMenuInhibited>│   ├──> osdElementEditing = false;│   ├──> cmsMenuChange│   └──> return BUTTON_PAUSE;├──> <(key == CMS_KEY_DOWN) && (!osdElementEditing)>│   ├──> <currentCtx.cursorRow < pageMaxRow>│   │   └──> currentCtx.cursorRow++;│   └──> <currentCtx.cursorRow >= pageMaxRow>│       ├──> cmsPageNext│       └──> currentCtx.cursorRow = 0;    // Goto top in any case├──> <(key == CMS_KEY_UP) && (!osdElementEditing)>│   ├──> currentCtx.cursorRow--;│   ├──> <(rowIsSkippable(pageTop + currentCtx.cursorRow)) && currentCtx.cursorRow > 0>  // Skip non-title labels, strings and dynamic read-only entries│   │   └──> currentCtx.cursorRow--;│   └──> <currentCtx.cursorRow == -1 || ((pageTop + currentCtx.cursorRow)->flags & OSD_MENU_ELEMENT_MASK) == OME_Label>│       ├──> cmsPagePrev(pDisplay);│       └──> currentCtx.cursorRow = pageMaxRow;├──> <(key == CMS_KEY_DOWN || key == CMS_KEY_UP) && (!osdElementEditing)>│   └──> return BUTTON_TIME├──> p = pageTop + currentCtx.cursorRow;├──> <p->flags & OSD_MENU_ELEMENT_MASK>│   ├──> <OME_Submenu> │   │   └──> ... //Element类型操作, cmsMenuChange│   ├──> <OME_Funcall> │   │   └──> ... //Element类型操作, cmsMenuBack/setRebootRequired│   ├──> <OME_OSD_Exit> │   │   └──> ... //Element类型操作│   ├──> <OME_Back> │   │   └──> ... //Element类型操作, cmsMenuBack │   ├──> <OME_Bool> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_VISIBLE><USE_OSD> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_UINT8> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_TAB> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_INT8> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_UINT16> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_INT16> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_UINT32> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_INT32> │   │   └──> ... //Element类型操作, setRebootRequired│   ├──> <OME_String> │   │   └──> ... //Element类型操作│   ├──> <OME_Label> │   │   └──> ... //Element类型操作│   ├──> <OME_END> │   │   └──> ... //Element类型操作│   └──> <OME_MENU> // Shouldn't happen└──> return res

其他函数

关于细节函数的分析,有兴趣可以深入挖掘,如以下函数:

1. display相关函数

  • displayBeginTransaction
  • displayHeartbeat
  • displayCommitTransaction

2. cmsMenu相关函数

  • cmsMenuOpen
  • cmsMenuExit

3. cmsDrawMenu相关函数

  • cmsDrawMenuEntry
  • cmsDrawMenu
  • cmsDrawMenuItemValue

4. cmsPage相关函数

  • cmsPageSelect
  • cmsPageNext
  • cmsPagePrev

BetaFlight模块设计之二十:CMS菜单模块分析相关推荐

  1. BetaFlight模块设计之二十六:接收机任务分析

    BetaFlight模块设计之二十六:接收机任务分析 接收机任务 配置情况 硬件配置 软件配置 驱动配置 sbus驱动函数分析 sbusDataReceive函数 sbusFrameStatus函数 ...

  2. BetaFlight模块设计之二十九:滤波模块分析

    BetaFlight模块设计之二十九:滤波模块分析 滤波模块 滤波类型 1. slewFilter 2. simpleLowpassFilter 3. laggedMovingAverage 4. p ...

  3. BetaFlight模块设计之二十四:transponder任务分析

    BetaFlight模块设计之二十四:transponder任务分析 transponder任务 配置情况 硬件配置 驱动配置 业务配置 初始化 MSP协议 三种IR transponder type ...

  4. PX4模块设计之二十六:BatteryStatus模块

    PX4模块设计之二十六:BatteryStatus模块 1. BatteryStatus模块简介 2. 模块入口函数 2.1 主入口battery_status_main 2.2 自定义子命令cust ...

  5. BetaFlight模块设计之二:SERIAL任务分析

    BetaFlight模块设计之二:SERIAL任务分析 SERIAL任务 1.总体情况 2.主要函数分析 2.1 taskHandleSerial任务 2.2 命令行处理函数cliProcess 2. ...

  6. PX4模块设计之二:uORB消息代理

    PX4模块设计之二:uORB消息代理 1. uORB模块接口 1.1. uORB服务接口 1.2. uORB消息注册/去注册接口 1.3. uORB消息发布接口 1.4. uORB消息订阅/去订阅接口 ...

  7. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十:SDRAM模块③ — 页读写 α...

    实验二十:SDRAM模块③ - 页读写 α 完成单字读写与多字读写以后,接下来我们要实验页读写.丑话当前,实验二十的页读写只是实验性质的东西,其中不存在任何实用价值,笔者希望读者可以把它当成页读写的热 ...

  8. AD(altium designer)15原理图与PCB设计教程(十)——信号完整性分析

    目录 信号完整性简介 信号完整性模型 信号完整性分析的环境设定 信号完整性的设计规则 进行信号完整性的分析 信号完整性分析器 串扰分析 反射分析 声明:该文只适用于学习,其内容包含来自书本的摘抄和总结 ...

  9. 智能手环功能模块设计_智能手环、功能模块以及智能手表的制作方法

    智能手环.功能模块以及智能手表的制作方法 [技术领域] [0001]本实用新型涉及通信领域,具体而言,涉及一种智能手环.功能模块以及智能手表. [背景技术] [0002]伴随着无线通信.传感器和处理器 ...

最新文章

  1. 15.3. REST
  2. 计算机程序设计考试题目,计算机程序设计员理论试题(题库)
  3. LVS NAT/DR
  4. access开发精要(11)-对象命名规定
  5. 分组中查询不符合条件的组
  6. UICollectionView 具体解说学习
  7. iptables简单应用
  8. 【WebRTC---进阶篇】(二)libevent实现高性能网络服务器
  9. Javascript基础 对象与方法的识别
  10. Qt4_深入信号和槽
  11. 想成为嵌入式程序员应知道的0x10个基本问题——转
  12. python学习多久可以就业_南昌多长时间能学会python(Python能去从事的工作)
  13. python语法简洁清晰、特色之一是强制用作为语句缩进_问道python之基础篇【一】 认识python...
  14. WordPress站点上传文件插件WordPress File Upload
  15. python 函数注释出现 :expected an indented block
  16. 李居明 饿火命(3)
  17. 白苹果了怎么办_iOS 13如何降级?iOS13降级失败怎么办?
  18. Android 注解处理器使用攻略
  19. 水瓶的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  20. Paper写作查重需要注意哪些问题?

热门文章

  1. windows官方touchpad开发资料链接总结
  2. 十分钟学会使用fileupload插件上传文件
  3. iOS 字体的加粗和其他样式的效果
  4. java项目-第136期ssm超市收银管理系统-java毕业设计
  5. 8行代码实现天数倒计时
  6. 私企做阿米巴经营模式失败的原因——灵魂5问!
  7. 树莓派远程控制(kali)
  8. java中刷新js函数,js常用刷新页面方法汇总
  9. [Windows]截图
  10. 无线打印服务器app,双频无线路由器打印服务器客户端软件_1.14.0613 (Windows)