BetaFlight模块设计之十六:OSD更新任务分析

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

OSD更新任务

描述:主要用于在摄像头视频上增加一层飞控数据展示层,也通常叫做OSD Layer上显示一些FC参数,比如:高度,速度等。

 ├──> 初始化
 │   ├──> [v]硬件初始化max7456DisplayPortInit/max7456PreInit/osdInit
 │   └──> [v]业务初始化pgResetFn_osdConfig
 ├──> 任务
 │   ├──> [x]实时任务
 │   ├──> [v]事件任务[TASK_OSD] = DEFINE_TASK("OSD", NULL, osdUpdateCheck, osdUpdate, TASK_PERIOD_HZ(OSD_FRAMERATE_DEFAULT_HZ), TASK_PRIORITY_LOW),
 │   └──> [x]时间任务
 ├──> 驱动
 │   ├──> [x]查询
 │   └──> [x]中断
 └──> 接口
     └──> 支持[FRSKYOSD](https://www.frsky-rc.com/wp-content/uploads/Downloads/Manual/OSD/OSD%20OSD%20Mini-Manual.pdf)[MAX7456](https://datasheets.maximintegrated.com/en/ds/MAX7456.pdf)MSP三种OSD显示方式

机型配置

在机型上宏定义使用MAX7456芯片,作为在视频信号上叠加OSD的处理芯片。

\src\main\target\KAKUTEF7\target.h
#define USE_MAX7456
#define MAX7456_SPI_INSTANCE    SPI2
#define MAX7456_SPI_CS_PIN      SPI2_NSS_PIN

驱动上对默认的SPI做了默认配置。

\src\main\pg\max7456.c
PG_REGISTER_WITH_RESET_FN(max7456Config_t, max7456Config, PG_MAX7456_CONFIG, 0);

void pgResetFn_max7456Config(max7456Config_t *config)
{
    config->clockConfig = MAX7456_CLOCK_CONFIG_NOMINAL;
    config->csTag = IO_TAG(MAX7456_SPI_CS_PIN);
    config->spiDevice = SPI_DEV_TO_CFG(spiDeviceByInstance(MAX7456_SPI_INSTANCE));
    config->preInitOPU = false;
}

在驱动和业务之间增加了类似OSD适配层,对驱动进行了封装。

\src\main\io\displayport_max7456.c
static const displayPortVTable_t max7456VTable = {
    .grab = grab,
    .release = release,
    .clearScreen = clearScreen,
    .drawScreen = drawScreen,
    .screenSize = screenSize,
    .writeString = writeString,
    .writeChar = writeChar,
    .isTransferInProgress = isTransferInProgress,
    .heartbeat = heartbeat,
    .redraw = redraw,
    .isSynced = isSynced,
    .txBytesFree = txBytesFree,
    .layerSupported = layerSupported,
    .layerSelect = layerSelect,
    .layerCopy = layerCopy,
    .writeFontCharacter = writeFontCharacter,
    .checkReady = checkReady,
    .setBackgroundType = setBackgroundType,
};

osdUpdateCheck函数分析

这里是稳态OSD_STATE_IDLE,周期进入OSD_STATE_CHECK状态的触发地。

osdUpdateCheck
 ├──> static timeUs_t osdUpdateDueUs = 0;
 ├──> <osdState == OSD_STATE_IDLE>
 │   └──> <cmpTimeUs(currentTimeUs, osdUpdateDueUs)>
 │       ├──> osdState = OSD_STATE_CHECK;   //超过OSD_UPDATE_INTERVAL_US时间后,触发状态机进入check模式
 │       ├──> <osdUpdateDueUs>
 │       │   └──> osdUpdateDueUs += OSD_UPDATE_INTERVAL_US;
 │       └──> <!osdUpdateDueUs>
 │           └──>  osdUpdateDueUs = currentTimeUs + OSD_UPDATE_INTERVAL_US;
 └──> return (osdState != OSD_STATE_IDLE);

osdUpdate函数分析

不管是从OSD_STATE_INIT触发,还是从OSD_STATE_CHECK触发的业务,最终将归于稳态OSD_STATE_IDLE,详见下面状态机分析。

OSD Update状态机

osdUpdate
 ├──> <osdState != OSD_STATE_UPDATE_CANVAS>
 │   └──> schedulerIgnoreTaskExecRate
 ├──> switch (osdState)
 │   ├──> <OSD_STATE_INIT>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_CHECK>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_UPDATE_HEARTBEAT>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_PROCESS_STATS1>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_REFRESH_STATS>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_PROCESS_STATS2>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_PROCESS_STATS3>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_UPDATE_ALARMS>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_UPDATE_CANVAS>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_GROUP_ELEMENTS>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_UPDATE_ELEMENTS>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_COMMIT>
 │   │   └──> 状态机业务,略。。。
 │   ├──> <OSD_STATE_TRANSFER>
 │   │   └──> 状态机业务,略。。。
 │   └──> <OSD_STATE_IDLE>
 │       └──> 状态机业务,略。。。
 ├──> <!schedulerGetIgnoreTaskExecTime()>
 │   ├──> executeTimeUs = micros() - currentTimeUs;
 │   └──> <!firstPass>
 │       ├──> <osdCurrentState == OSD_STATE_UPDATE_ELEMENTS>
 │       │   ├──> <executeTimeUs > (osdElementGroupDurationFractionUs[osdCurrentElementGroup] >> OSD_EXEC_TIME_SHIFT)>
 │       │   │   └──> osdElementGroupDurationFractionUs[osdCurrentElementGroup] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
 │       │   └──> <osdElementGroupDurationFractionUs[osdCurrentElementGroup] > 0>
 │       │       └──> osdElementGroupDurationFractionUs[osdCurrentElementGroup]--;
 │       ├──> <executeTimeUs > (osdStateDurationFractionUs[osdCurrentState] >> OSD_EXEC_TIME_SHIFT)>
 │       │   └──> osdStateDurationFractionUs[osdCurrentState] = executeTimeUs << OSD_EXEC_TIME_SHIFT;
 │       └──> <osdStateDurationFractionUs[osdCurrentState] > 0>
 │           └──> osdStateDurationFractionUs[osdCurrentState]--;
 ├──> <osdState == OSD_STATE_UPDATE_ELEMENTS>
 │   └──> schedulerSetNextStateTime((osdElementGroupDurationFractionUs[osdElementGroup] >> OSD_EXEC_TIME_SHIFT) + OSD_ELEMENT_RENDER_GROUP_MARGIN);
 └──> <!(osdState == OSD_STATE_UPDATE_ELEMENTS)>
     ├──> <osdState == OSD_STATE_IDLE>
     │   └──> schedulerSetNextStateTime((osdStateDurationFractionUs[OSD_STATE_CHECK] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);
     └──> <!osdState == OSD_STATE_IDLE>
         └──> schedulerSetNextStateTime((osdStateDurationFractionUs[osdState] >> OSD_EXEC_TIME_SHIFT) + OSD_TASK_MARGIN);

OSD Display分析回顾

根据上面的分析,貌似代码历史比较悠久,有几个抽象的概念:

  1. 机型硬件配置USE_MAX7456(\src\main\target\KAKUTEF7\target.h)
  2. 框架代码Init初始化,使用了OSD_DISPLAYPORT_DEVICE概念的初始化函数max7456DisplayPortInit(\src\main\io\displayport_max7456.c)
  3. OSD_DISPLAYPORT_DEVICE抽象了OSD接口static const displayPortVTable_t max7456VTable(\src\main\io\displayport_max7456.c)
  4. OSD_DISPLAYPORT_DEVICE根据OSD接口封装硬件驱动(\src\main\io\displayport_max7456.c)
  5. 驱动代码(\src\main\drivers\max7456.c)

如果有机会,可以尝试一下以下尝试(目的:解耦和简化业务)

  1. 【重构】OSD_DISPLAYPORT状态机业务抽象
  2. 【优化】OSD_DISPLAYPORT标准API抽象
  3. 【重构】OSD_DISPLAYPORT_DEVICE可以考虑适配层,机型硬件配置在板级启动过程进行适配层注册
  4. 【优化】硬件驱动代码
  5. 【优化】框架代码调整
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值