BetaFlight模块设计之二十六:接收机任务分析
Posted lida2003
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BetaFlight模块设计之二十六:接收机任务分析相关的知识,希望对你有一定的参考价值。
BetaFlight模块设计之二十六:接收机任务分析
基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。
接收机任务
描述:主要用于处理接受遥控指令的任务。
├──> 初始化
│ ├──> [v]硬件初始化openSerialPort
│ └──> [v]业务初始化rxInit(serialRxInit/sbusInit/sbusFrameStatus/sbusDataReceive/sbusChannelsReadRawRC)
├──> 任务
│ ├──> [x]实时任务
│ ├──> [v]事件任务[TASK_RX] = DEFINE_TASK("RX", NULL, rxUpdateCheck, taskUpdateRxMain, TASK_PERIOD_HZ(33), TASK_PRIORITY_HIGH),
│ └──> [x]时间任务
├──> 驱动
│ ├──> [x]查询
│ └──> [v]中断sbusDataReceive
└──> 接口
├──> 支持SRXL2、SPEKTRUM、SBUS、SUMD、SUMH、XBUS、IBUS、JETIEXBUS、CRSF、GHST、FPORT协议
└──> bool getRxRateValid(void)
配置情况
硬件配置
在配置文件\\src\\main\\target\\KAKUTEF7\\target.h定义。
#define SERIALRX_UART SERIAL_PORT_USART6
#define SERIALRX_PROVIDER SERIALRX_SBUS
软件配置
\\src\\main\\pg\\rx.c
PG_REGISTER_WITH_RESET_FN(rxConfig_t, rxConfig, PG_RX_CONFIG, 3);
void pgResetFn_rxConfig(rxConfig_t *rxConfig)
RESET_CONFIG_2(rxConfig_t, rxConfig,
.halfDuplex = 0,
.serialrx_provider = SERIALRX_PROVIDER,
.serialrx_inverted = 0,
.spektrum_bind_pin_override_ioTag = IO_TAG(SPEKTRUM_BIND_PIN),
.spektrum_bind_plug_ioTag = IO_TAG(BINDPLUG_PIN),
.spektrum_sat_bind = 0,
.spektrum_sat_bind_autoreset = 1,
.midrc = RX_MID_USEC,
.mincheck = 1050,
.maxcheck = 1900,
.rx_min_usec = RX_MIN_USEC, // any of first 4 channels below this value will trigger rx loss detection
.rx_max_usec = RX_MAX_USEC, // any of first 4 channels above this value will trigger rx loss detection
.rssi_src_frame_errors = false,
.rssi_channel = 0,
.rssi_scale = RSSI_SCALE_DEFAULT,
.rssi_offset = 0,
.rssi_invert = 0,
.rssi_src_frame_lpf_period = 30,
.fpvCamAngleDegrees = 0,
.airModeActivateThreshold = 25,
.max_aux_channel = DEFAULT_AUX_CHANNEL_COUNT,
.rc_smoothing_mode = 1,
.rc_smoothing_setpoint_cutoff = 0,
.rc_smoothing_feedforward_cutoff = 0,
.rc_smoothing_throttle_cutoff = 0,
.rc_smoothing_debug_axis = ROLL,
.rc_smoothing_auto_factor_rpy = 30,
.rc_smoothing_auto_factor_throttle = 30,
.srxl2_unit_id = 1,
.srxl2_baud_fast = true,
.sbus_baud_fast = false,
.crsf_use_rx_snr = false,
.msp_override_channels_mask = 0,
.crsf_use_negotiated_baud = false,
);
#ifdef RX_CHANNELS_TAER
parseRcChannels("TAER1234", rxConfig);
#else
parseRcChannels("AETR1234", rxConfig);
#endif
驱动配置
从驱动框架设计的角度,需要实现以下几个API接口:
- static uint8_t nullFrameStatus(rxRuntimeState_t *rxRuntimeState)
- static float nullReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
- typedef void (*serialReceiveCallbackPtr)(uint16_t data, void *rxCallbackData);
- static bool nullProcessFrame(const rxRuntimeState_t *rxRuntimeState)
Kakute F7 AIO使用的是sbus协议,所以这里驱动相关主要函数如下:
- sbusFrameStatus
- sbusChannelsReadRawRC
- sbusDataReceive
注:这里sbus不需要auxiliaryProcessing,所以nullProcessFrame接口不需要实现。
sbus驱动函数分析
sbusDataReceive函数
这里Kakute F7 AIO采用的是sbus协议,所以这里以这个为例,其他协议请根据配置查找对应的协议代码
sbusDataReceive
├──> nowUs = microsISR();
├──> sbusFrameTime = cmpTimeUs(nowUs, sbusFrameData->startAtUs);
├──> <sbusFrameTime > (long)(SBUS_TIME_NEEDED_PER_FRAME + 500)> //一帧数据接收超时
│ └──> sbusFrameData->position = 0; //重置起始位
├──> <sbusFrameData->position == 0>
│ ├──> <c != SBUS_FRAME_BEGIN_BYTE>
│ │ └──> return //不是有效SOF,直接跳过
│ └──> sbusFrameData->startAtUs = nowUs; //有效SOF,记录起始接收时间,以便后续判断帧接收超时
└──> <sbusFrameData->position < SBUS_FRAME_SIZE>
├──> sbusFrameData->frame.bytes[sbusFrameData->position++] = (uint8_t)c; //记录数据
├──> <sbusFrameData->position < SBUS_FRAME_SIZE>
│ └──> sbusFrameData->done = false; //一帧数据尚未传输完
└──> <!(sbusFrameData->position < SBUS_FRAME_SIZE)>
└──> sbusFrameData->done = true; //一帧数据传输完成
sbusFrameStatus函数
sbusFrameStatus
├──> <!sbusFrameData->done>
│ └──> return RX_FRAME_PENDING; //一帧数据报文尚未传输完成
├──> sbusFrameData->done = false;
├──> frameStatus = sbusChannelsDecode(rxRuntimeState, &sbusFrameData->frame.frame.channels); //报文解码
├──> <!(frameStatus & (RX_FRAME_FAILSAFE | RX_FRAME_DROPPED))>
│ └──> rxRuntimeState->lastRcFrameTimeUs = sbusFrameData->startAtUs; //正确报文,记录最近一次收到报文的时间,以便后续判断接收机失联
└──> return frameStatus;
sbusChannelsDecode函数
对一帧数据进行译码,判断帧数据是否存在问题。
sbusChannelsDecode
├──> 赋值rxRuntimeState->channelData[chan], chan=0..15
├──> 数据最大最小值赋值rxRuntimeState->channelData[chan], chan=16..17
├──> <channels->flags & SBUS_FLAG_FAILSAFE_ACTIVE>
│ └──> return RX_FRAME_COMPLETE | RX_FRAME_FAILSAFE;
├──> <channels->flags & SBUS_FLAG_SIGNAL_LOSS>
│ └──> return RX_FRAME_COMPLETE | RX_FRAME_DROPPED;
└──> return RX_FRAME_COMPLETE;
注:一帧SBUS总长度是24字节。
typedef struct sbusChannels_s
// 176 bits of data (11 bits per channel * 16 channels) = 22 bytes.
unsigned int chan0 : 11;
unsigned int chan1 : 11;
unsigned int chan2 : 11;
unsigned int chan3 : 11;
unsigned int chan4 : 11;
unsigned int chan5 : 11;
unsigned int chan6 : 11;
unsigned int chan7 : 11;
unsigned int chan8 : 11;
unsigned int chan9 : 11;
unsigned int chan10 : 11;
unsigned int chan11 : 11;
unsigned int chan12 : 11;
unsigned int chan13 : 11;
unsigned int chan14 : 11;
unsigned int chan15 : 11;
uint8_t flags;
__attribute__((__packed__)) sbusChannels_t;
#define SBUS_CHANNEL_DATA_LENGTH sizeof(sbusChannels_t)
#define SBUS_FRAME_SIZE (SBUS_CHANNEL_DATA_LENGTH + 2)
sbusChannelsReadRawRC函数
函数注释上有关于这个公式的来源,详见链接。
sbusChannelsReadRawRC // Linear fitting values read from OpenTX-ppmus and comparing with values received by X4R
└──> return (5 * (float)rxRuntimeState->channelData[chan] / 8) + 880;
主要任务相关函数分析
rxUpdateCheck函数
以下3种直接场景+1钟隐含场景,需要提升该任务优先级:
1.【直接场景】taskUpdateRxMainInProgress //函数正处在数据处理过程中,需要尽快得到调度以处理后续报文中的数据
2.【直接场景】rxDataProcessingRequired //rxFrameCheck检查过程中发现有接收数据或者有新报文需要处理,提升优先级应对后续数据或者报文
3.【直接场景】auxiliaryProcessingRequired //根据报文状态标志,还有帧数据需要处理
4.【隐含场景】上述三种场景已经满足情况下,时间轮询时发现任然未得到调度,继续提升优先级(以便尽快得到处理)
rxUpdateCheck //主要用于增加动态调度优先级,详见[Schedule调度框架](https://blog.csdn.net/lida2003/article/details/124876862)
└──> taskUpdateRxMainInProgress() || rxDataProcessingRequired || auxiliaryProcessingRequired
taskUpdateRxMain函数
遥控器通过RC接收机发送给飞控,如果定义USE_USB_CDC_HID,正好截断发给PC,作为一个模拟器使用。
taskUpdateRxMain
├──> <rxState != RX_STATE_UPDATE> //非update状态,无需更新任务的anticipatedExecutionTime(内部更新)
│ └──> schedulerIgnoreTaskExecRate
├──> <rxState> //正常情况三个状态机依次时间片轮询
│ ├──> <RX_STATE_CHECK>
│ │ ├──> <!processRx(currentTimeUs)>
│ │ │ ├──> rxState = RX_STATE_CHECK;
│ │ │ └──> break
│ │ └──> rxState = RX_STATE_MODES;
│ ├──> <RX_STATE_MODES>
│ │ ├──> processRxModes(currentTimeUs);
│ │ └──> rxState = RX_STATE_UPDATE;
│ └──> <RX_STATE_UPDATE>
│ ├──> updateRcCommands
│ ├──> updateArmingStatus
│ ├──> <USE_USB_CDC_HID><!ARMING_FLAG(ARMED)> //PC模拟器,非常好的idea,可以作为模拟stick玩游戏了。
│ │ └──> sendRcDataToHid
│ └──> rxState = RX_STATE_CHECK;
├──> <!schedulerGetIgnoreTaskExecTime()>
│ ├──> <anticipatedExecutionTime != (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT> //期望时间与上次更新执行时间不一致
│ │ └──> rxStateDurationFractionUs[oldRxState] = anticipatedExecutionTime << RX_TASK_DECAY_SHIFT; //更新为实际执行时间
│ ├──> <executeTimeUs > (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT> //实际执行时间大于上次更新执行时间
│ │ └──> rxStateDurationFractionUs[oldRxState] = executeTimeUs << RX_TASK_DECAY_SHIFT; //更新上次执行时间
│ └──> <executeTimeUs <= (rxStateDurationFractionUs[oldRxState] >> RX_TASK_DECAY_SHIFT> //实际执行时间小于等于上次更新执行时间
│ └──> rxStateDurationFractionUs[oldRxState]--; //慢慢减少上次执行时间
└──> schedulerSetNextStateTime //提示schedulerExecuteTask更新期望任务执行时间
注:这里作为穿越机手动模拟器有一个非常好的FPV FreeRider,链接详见。
以上是关于BetaFlight模块设计之二十六:接收机任务分析的主要内容,如果未能解决你的问题,请参考以下文章
BetaFlight模块设计之二十一:dashboard任务分析
BetaFlight模块设计之二十五:dispatch任务分析