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
 └──> 接口
     ├──> 支持SRXL2SPEKTRUMSBUSSUMDSUMHXBUSIBUSJETIEXBUSCRSFGHSTFPORT协议
     └──> 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接口:

  1. static uint8_t nullFrameStatus(rxRuntimeState_t *rxRuntimeState)
  2. static float nullReadRawRC(const rxRuntimeState_t *rxRuntimeState, uint8_t channel)
  3. typedef void (*serialReceiveCallbackPtr)(uint16_t data, void *rxCallbackData);
  4. static bool nullProcessFrame(const rxRuntimeState_t *rxRuntimeState)

Kakute F7 AIO使用的是sbus协议,所以这里驱动相关主要函数如下:

  1. sbusFrameStatus
  2. sbusChannelsReadRawRC
  3. 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模块设计之二十七:姿态更新任务分析

BetaFlight模块设计之二十二:地面测距任务分析

BetaFlight模块设计之二十一:dashboard任务分析

BetaFlight模块设计之二十五:dispatch任务分析

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

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