基于BetaFlight开源代码框架简介的框架设计,逐步分析内部模块功能设计。这里主要针对四轴飞控DIY集成FPV功能第七章节调试遇到的问题,进一步深入分析和理解。
航模主要通过遥控器或者数传两个通道进行控制,而BetaFlight这种穿越机最主要的方式还是通过遥控器。
遥控器控制飞控这个通道的可靠性、稳定性需要通过这个通信链路的一些指标量化体现,从BetaFlight代码上看,主要有以下几个指标:
定时更新RSSI信号强度,并将强度值范围线性映射到0-100之间。值越大表示信号强度越大,值为0表示信号丢失(飞控进入failsafe模式)。
RSSI Value设置的几种方式:
void setRssiDirect(uint16_t newRssi, rssiSource_e source)
{
if (source != rssiSource) {
return;
}
rssi = newRssi;
}
void setRssi(uint16_t rssiValue, rssiSource_e source)
{
if (source != rssiSource) {
return;
}
// Filter RSSI value
if (source == RSSI_SOURCE_FRAME_ERRORS) {
rssi = pt1FilterApply(&frameErrFilter, rssiValue);
} else {
// calculate new sample mean
rssi = updateRssiSamples(rssiValue);
}
}
void setRssiMsp(uint8_t newMspRssi)
{
if (rssiSource == RSSI_SOURCE_NONE) {
rssiSource = RSSI_SOURCE_MSP;
}
if (rssiSource == RSSI_SOURCE_MSP) {
rssi = ((uint16_t)newMspRssi) << 2;
lastMspRssiUpdateUs = micros();
}
}
RSSI dBm Value设置的几种方式:
void setRssiDbmDirect(int16_t newRssiDbm, rssiSource_e source)
{
if (source != rssiSource) {
return;
}
rssiDbm = newRssiDbm;
}
void setRssiDbm(int16_t rssiDbmValue, rssiSource_e source)
{
if (source != rssiSource) {
return;
}
rssiDbm = updateRssiDbmSamples(rssiDbmValue);
}
void setLinkQualityDirect(uint16_t linkqualityValue)
{
#ifdef USE_RX_LINK_QUALITY_INFO
linkQuality = linkqualityValue;
#else
UNUSED(linkqualityValue);
#endif
}
static void setLinkQuality(bool validFrame, timeDelta_t currentDeltaTimeUs)
{
static uint16_t rssiSum = 0;
static uint16_t rssiCount = 0;
static timeDelta_t resampleTimeUs = 0;
#ifdef USE_RX_LINK_QUALITY_INFO
if (linkQualitySource == LQ_SOURCE_NONE) {
// calculate new sample mean
linkQuality = updateLinkQualitySamples(validFrame ? LINK_QUALITY_MAX_VALUE : 0);
}
#endif
if (rssiSource == RSSI_SOURCE_FRAME_ERRORS) {
resampleTimeUs += currentDeltaTimeUs;
rssiSum += validFrame ? RSSI_MAX_VALUE : 0;
rssiCount++;
if (resampleTimeUs >= FRAME_ERR_RESAMPLE_US) {
setRssi(rssiSum / rssiCount, rssiSource);
rssiSum = 0;
rssiCount = 0;
resampleTimeUs -= FRAME_ERR_RESAMPLE_US;
}
}
}
STATIC_UNIT_TESTED uint16_t updateLinkQualitySamples(uint16_t value)
{
static uint16_t samples[LINK_QUALITY_SAMPLE_COUNT];
static uint8_t sampleIndex = 0;
static uint16_t sum = 0;
sum += value - samples[sampleIndex];
samples[sampleIndex] = value;
sampleIndex = (sampleIndex + 1) % LINK_QUALITY_SAMPLE_COUNT;
return sum / LINK_QUALITY_SAMPLE_COUNT;
}
static void updateRSSIADC(timeUs_t currentTimeUs)
{
#ifndef USE_ADC
UNUSED(currentTimeUs);
#else
static uint32_t rssiUpdateAt = 0;
if ((int32_t)(currentTimeUs - rssiUpdateAt) < 0) {
return;
}
rssiUpdateAt = currentTimeUs + DELAY_20_MS;
const uint16_t adcRssiSample = adcGetChannel(ADC_RSSI);
uint16_t rssiValue = adcRssiSample / RSSI_ADC_DIVISOR;
setRssi(rssiValue, RSSI_SOURCE_ADC);
#endif
}
static void updateRSSIPWM(void)
{
// Read value of AUX channel as rssi
int16_t pwmRssi = rcData[rxConfig()->rssi_channel - 1];
// Range of rawPwmRssi is [1000;2000]. rssi should be in [0;1023];
setRssiDirect(scaleRange(constrain(pwmRssi, PWM_RANGE_MIN, PWM_RANGE_MAX), PWM_RANGE_MIN, PWM_RANGE_MAX, 0, RSSI_MAX_VALUE), RSSI_SOURCE_RX_CHANNEL);
}
\src\main\msp\msp.c
case MSP_SET_TX_INFO:
setRssiMsp(sbufReadU8(src));
break;
\src\main\rx\rx.c
case RSSI_SOURCE_MSP:
if (cmpTimeUs(micros(), lastMspRssiUpdateUs) > DELAY_1500_MS) { // 1.5s
rssi = 0;
}
注:CRSF具有双向通信功能以及协商速率的功能,BetaFlight模块设计之二十三:CRSF V3串口速率协商任务分析。
该报文使用链路统计数据:
static void handleCrsfLinkStatisticsFrame(const crsfLinkStatistics_t* statsPtr, timeUs_t currentTimeUs)
{
const crsfLinkStatistics_t stats = *statsPtr;
lastLinkStatisticsFrameUs = currentTimeUs;
int16_t rssiDbm = -1 * (stats.active_antenna ? stats.uplink_RSSI_2 : stats.uplink_RSSI_1);
if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
if (rxConfig()->crsf_use_rx_snr) {
// -10dB of SNR mapped to 0 RSSI (fail safe is likely to happen at this measure)
// 0dB of SNR mapped to 20 RSSI (default alarm)
// 41dB of SNR mapped to 99 RSSI (SNR can climb to around 60, but showing that is not very meaningful)
const uint16_t rsnrPercentScaled = constrain((stats.uplink_SNR + 10) * 20, 0, RSSI_MAX_VALUE);
setRssi(rsnrPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
#ifdef USE_RX_RSSI_DBM
rssiDbm = stats.uplink_SNR;
#endif
} else {
const uint16_t rssiPercentScaled = scaleRange(rssiDbm, CRSF_RSSI_MIN, 0, 0, RSSI_MAX_VALUE);
setRssi(rssiPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
}
}
#ifdef USE_RX_RSSI_DBM
setRssiDbm(rssiDbm, RSSI_SOURCE_RX_PROTOCOL_CRSF);
#endif
#ifdef USE_RX_LINK_QUALITY_INFO
if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
setLinkQualityDirect(stats.uplink_Link_quality);
rxSetRfMode(stats.rf_Mode);
}
#endif
#ifdef USE_RX_LINK_UPLINK_POWER
const uint8_t crsfUplinkPowerStatesItemIndex = (stats.uplink_TX_Power < CRSF_UPLINK_POWER_LEVEL_MW_ITEMS_COUNT) ? stats.uplink_TX_Power : 0;
rxSetUplinkTxPwrMw(uplinkTXPowerStatesMw[crsfUplinkPowerStatesItemIndex]);
#endif
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 0, stats.uplink_RSSI_1);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 1, stats.uplink_RSSI_2);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 2, stats.uplink_Link_quality);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 3, stats.rf_Mode);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 0, stats.active_antenna);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 1, stats.uplink_SNR);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_PWR, 2, stats.uplink_TX_Power);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 0, stats.downlink_RSSI);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 1, stats.downlink_Link_quality);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_DOWN, 2, stats.downlink_SNR);
}
该报文CRSF_V3使用,链路统计数据:
static void handleCrsfLinkStatisticsTxFrame(const crsfLinkStatisticsTx_t* statsPtr, timeUs_t currentTimeUs)
{
const crsfLinkStatisticsTx_t stats = *statsPtr;
lastLinkStatisticsFrameUs = currentTimeUs;
if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
const uint16_t rssiPercentScaled = scaleRange(stats.uplink_RSSI_percentage, 0, 100, 0, RSSI_MAX_VALUE);
setRssi(rssiPercentScaled, RSSI_SOURCE_RX_PROTOCOL_CRSF);
}
#ifdef USE_RX_RSSI_DBM
int16_t rssiDbm = -1 * stats.uplink_RSSI;
if (rxConfig()->crsf_use_rx_snr) {
rssiDbm = stats.uplink_SNR;
}
setRssiDbm(rssiDbm, RSSI_SOURCE_RX_PROTOCOL_CRSF);
#endif
#ifdef USE_RX_LINK_QUALITY_INFO
if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
setLinkQualityDirect(stats.uplink_Link_quality);
}
#endif
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 0, stats.uplink_RSSI);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 1, stats.uplink_SNR);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 2, stats.uplink_Link_quality);
DEBUG_SET(DEBUG_CRSF_LINK_STATISTICS_UPLINK, 3, stats.uplink_RSSI_percentage);
}
超时场景下直接将RSSI Value & RSSI dBm以及链路质量清零。
static void crsfCheckRssi(uint32_t currentTimeUs) {
if (cmpTimeUs(currentTimeUs, lastLinkStatisticsFrameUs) > CRSF_LINK_STATUS_UPDATE_TIMEOUT_US) {
if (rssiSource == RSSI_SOURCE_RX_PROTOCOL_CRSF) {
setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL_CRSF);
#ifdef USE_RX_RSSI_DBM
if (rxConfig()->crsf_use_rx_snr) {
setRssiDbmDirect(CRSF_SNR_MIN, RSSI_SOURCE_RX_PROTOCOL_CRSF);
} else {
setRssiDbmDirect(CRSF_RSSI_MIN, RSSI_SOURCE_RX_PROTOCOL_CRSF);
}
#endif
}
#ifdef USE_RX_LINK_QUALITY_INFO
if (linkQualitySource == LQ_SOURCE_RX_PROTOCOL_CRSF) {
setLinkQualityDirect(0);
}
#endif
}
}
注:后续再做更新,分析方法类似。