BetaFlight模块设计之十六:OSD更新任务分析
Posted lida2003
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BetaFlight模块设计之十六:OSD更新任务分析相关的知识,希望对你有一定的参考价值。
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,详见下面状态机分析。
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分析回顾
根据上面的分析,貌似代码历史比较悠久,有几个抽象的概念:
- 机型硬件配置USE_MAX7456(\\src\\main\\target\\KAKUTEF7\\target.h)
- 框架代码Init初始化,使用了OSD_DISPLAYPORT_DEVICE概念的初始化函数max7456DisplayPortInit(\\src\\main\\io\\displayport_max7456.c)
- OSD_DISPLAYPORT_DEVICE抽象了OSD接口static const displayPortVTable_t max7456VTable(\\src\\main\\io\\displayport_max7456.c)
- OSD_DISPLAYPORT_DEVICE根据OSD接口封装硬件驱动(\\src\\main\\io\\displayport_max7456.c)
- 驱动代码(\\src\\main\\drivers\\max7456.c)
如果有机会,可以尝试一下以下尝试(目的:解耦和简化业务)
- 【重构】OSD_DISPLAYPORT状态机业务抽象
- 【优化】OSD_DISPLAYPORT标准API抽象
- 【重构】OSD_DISPLAYPORT_DEVICE可以考虑适配层,机型硬件配置在板级启动过程进行适配层注册
- 【优化】硬件驱动代码
- 【优化】框架代码调整
以上是关于BetaFlight模块设计之十六:OSD更新任务分析的主要内容,如果未能解决你的问题,请参考以下文章