/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see .
*/
#include "stdbool.h"
#include "stdint.h"
#include "string.h"
#include
#include "build/build_config.h"
#include "common/maths.h"
#include "common/utils.h"
#include "common/filter.h"
#include "drivers/adc.h"
#include "drivers/system.h"
#include "config/parameter_group.h"
#include "config/parameter_group_ids.h"
#include "config/config_reset.h"
#include "sensors/current.h"
#include "sensors/esc_sensor.h"
const uint8_t currentMeterIds[] = {
CURRENT_METER_ID_BATTERY_1,
CURRENT_METER_ID_VIRTUAL_1,
#ifdef USE_ESC_SENSOR
CURRENT_METER_ID_ESC_COMBINED_1,
CURRENT_METER_ID_ESC_MOTOR_1,
CURRENT_METER_ID_ESC_MOTOR_2,
CURRENT_METER_ID_ESC_MOTOR_3,
CURRENT_METER_ID_ESC_MOTOR_4,
CURRENT_METER_ID_ESC_MOTOR_5,
CURRENT_METER_ID_ESC_MOTOR_6,
CURRENT_METER_ID_ESC_MOTOR_7,
CURRENT_METER_ID_ESC_MOTOR_8,
CURRENT_METER_ID_ESC_MOTOR_9,
CURRENT_METER_ID_ESC_MOTOR_10,
CURRENT_METER_ID_ESC_MOTOR_11,
CURRENT_METER_ID_ESC_MOTOR_12,
#endif
};
const uint8_t supportedCurrentMeterCount = ARRAYLEN(currentMeterIds);
//
// ADC/Virtual/ESC shared
//
void currentMeterReset(currentMeter_t *meter)
{
meter->amperage = 0;
meter->amperageLatest = 0;
meter->mAhDrawn = 0;
}
//
// ADC/Virtual shared
//
#define ADCVREF 3300 // in mV
#define IBAT_LPF_FREQ 0.4f
static biquadFilter_t adciBatFilter;
#ifndef CURRENT_METER_SCALE_DEFAULT
#define CURRENT_METER_SCALE_DEFAULT 400 // for Allegro ACS758LCB-100U (40mV/A)
#endif
#ifndef CURRENT_METER_OFFSET_DEFAULT
#define CURRENT_METER_OFFSET_DEFAULT 0
#endif
PG_REGISTER_WITH_RESET_TEMPLATE(currentSensorADCConfig_t, currentSensorADCConfig, PG_CURRENT_SENSOR_ADC_CONFIG, 0);
PG_RESET_TEMPLATE(currentSensorADCConfig_t, currentSensorADCConfig,
.scale = CURRENT_METER_SCALE_DEFAULT,
.offset = CURRENT_METER_OFFSET_DEFAULT,
);
PG_REGISTER(currentMeterVirtualConfig_t, currentMeterVirtualConfig, PG_CURRENT_SENSOR_VIRTUAL_CONFIG, 0);
static int32_t currentMeterADCToCentiamps(const uint16_t src)
{
int32_t millivolts;
const currentSensorADCConfig_t *config = currentSensorADCConfig();
millivolts = ((uint32_t)src * ADCVREF) / 4096;
millivolts -= config->offset;
return (millivolts * 1000) / (int32_t)config->scale; // current in 0.01A steps
}
static void updateCurrentmAhDrawnState(currentMeterMAhDrawnState_t *state, int32_t amperageLatest, int32_t lastUpdateAt)
{
state->mAhDrawnF = state->mAhDrawnF + (amperageLatest * lastUpdateAt / (100.0f * 1000 * 3600));
state->mAhDrawn = state->mAhDrawnF;
}
//
// ADC
//
currentMeterADCState_t currentMeterADCState;
void currentMeterADCInit(void)
{
memset(¤tMeterADCState, 0, sizeof(currentMeterADCState_t));
biquadFilterInitLPF(&adciBatFilter, IBAT_LPF_FREQ, 50000); //50HZ Update
}
void currentMeterADCRefresh(int32_t lastUpdateAt)
{
uint16_t iBatSample = adcGetChannel(ADC_CURRENT);
currentMeterADCState.amperageLatest = currentMeterADCToCentiamps(iBatSample);
currentMeterADCState.amperage = currentMeterADCToCentiamps(biquadFilterApply(&adciBatFilter, iBatSample));
updateCurrentmAhDrawnState(¤tMeterADCState.mahDrawnState, currentMeterADCState.amperageLatest, lastUpdateAt);
}
void currentMeterADCRead(currentMeter_t *meter)
{
meter->amperageLatest = currentMeterADCState.amperageLatest;
meter->amperage = currentMeterADCState.amperage;
meter->mAhDrawn = currentMeterADCState.mahDrawnState.mAhDrawn;
}
//
// VIRTUAL
//
currentMeterVirtualState_t currentMeterVirtualState;
void currentMeterVirtualInit(void)
{
memset(¤tMeterVirtualState, 0, sizeof(currentMeterVirtualState_t));
}
void currentMeterVirtualRefresh(int32_t lastUpdateAt, bool armed, bool throttleLowAndMotorStop, int32_t throttleOffset)
{
currentMeterVirtualState.amperage = (int32_t)currentMeterVirtualConfig()->offset;
if (armed) {
if (throttleLowAndMotorStop) {
throttleOffset = 0;
}
int throttleFactor = throttleOffset + (throttleOffset * throttleOffset / 50); // FIXME magic number 50, 50hz?
currentMeterVirtualState.amperage += throttleFactor * (int32_t)currentMeterVirtualConfig()->scale / 1000;
}
updateCurrentmAhDrawnState(¤tMeterVirtualState.mahDrawnState, currentMeterVirtualState.amperage, lastUpdateAt);
}
void currentMeterVirtualRead(currentMeter_t *meter)
{
meter->amperageLatest = currentMeterVirtualState.amperage;
meter->amperage = currentMeterVirtualState.amperage;
meter->mAhDrawn = currentMeterVirtualState.mahDrawnState.mAhDrawn;
}
//
// ESC
//
#ifdef USE_ESC_SENSOR
currentMeterESCState_t currentMeterESCState;
#endif
void currentMeterESCInit(void)
{
#ifdef USE_ESC_SENSOR
memset(¤tMeterESCState, 0, sizeof(currentMeterESCState_t));
#endif
}
void currentMeterESCRefresh(int32_t lastUpdateAt)
{
UNUSED(lastUpdateAt);
#ifdef USE_ESC_SENSOR
escSensorData_t *escData = getEscSensorData(ESC_SENSOR_COMBINED);
if (escData->dataAge <= ESC_BATTERY_AGE_MAX) {
currentMeterESCState.amperage = escData->current;
currentMeterESCState.mAhDrawn = escData->consumption;
} else {
currentMeterESCState.amperage = 0;
currentMeterESCState.mAhDrawn = 0;
}
#endif
}
void currentMeterESCReadCombined(currentMeter_t *meter)
{
#ifdef USE_ESC_SENSOR
meter->amperageLatest = currentMeterESCState.amperage;
meter->amperage = currentMeterESCState.amperage;
meter->mAhDrawn = currentMeterESCState.mAhDrawn;
#else
currentMeterReset(meter);
#endif
}
void currentMeterESCReadMotor(uint8_t motorNumber, currentMeter_t *meter)
{
#ifndef USE_ESC_SENSOR
UNUSED(motorNumber);
currentMeterReset(meter);
#else
escSensorData_t *escData = getEscSensorData(motorNumber);
if (escData->dataAge <= ESC_BATTERY_AGE_MAX) {
meter->amperage = escData->current;
meter->amperageLatest = escData->current;
meter->mAhDrawn = escData->consumption;
return;
}
#endif
}
//
// API for current meters using IDs
//
// This API is used by MSP, for configuration/status.
//
void currentMeterRead(currentMeterId_e id, currentMeter_t *meter)
{
if (id == CURRENT_METER_ID_BATTERY_1) {
currentMeterADCRead(meter);
} else if (id == CURRENT_METER_ID_VIRTUAL_1) {
currentMeterVirtualRead(meter);
}
#ifdef USE_ESC_SENSOR
if (id == CURRENT_METER_ID_ESC_COMBINED_1) {
currentMeterESCReadCombined(meter);
} else
if (id >= CURRENT_METER_ID_ESC_MOTOR_1 && id <= CURRENT_METER_ID_ESC_MOTOR_20 ) {
int motor = id - CURRENT_METER_ID_ESC_MOTOR_1;
currentMeterESCReadMotor(motor, meter);
} else
#endif
{
currentMeterReset(meter);
}
}