forked from achamaikin/CCSModuleSW30Web
213 lines
5.8 KiB
C
213 lines
5.8 KiB
C
#include "cp.h"
|
|
#include "adc.h"
|
|
#include "board.h"
|
|
#include "tim.h"
|
|
#include "debug.h"
|
|
#include <stdint.h>
|
|
|
|
#define MAX_DUTY 450
|
|
#define CP_EMA_ALPHA_Q8 38
|
|
#define CP_DEBOUNCE_MS_DEFAULT 10
|
|
#define CP_DEBOUNCE_MS_F 60
|
|
#define CP_DEBOUNCE_MS_F_LOW_DUTY 100
|
|
#define CP_LOW_DUTY_THRESHOLD_PERCENT 10
|
|
|
|
#define CP_A_ENTER_MV 11000
|
|
#define CP_A_EXIT_MV 10000
|
|
|
|
#define CP_B_ENTER_LOW_MV 8000
|
|
#define CP_B_ENTER_HIGH_MV 10000
|
|
#define CP_B_EXIT_LOW_MV 7500
|
|
#define CP_B_EXIT_HIGH_MV 10500
|
|
|
|
#define CP_C_ENTER_LOW_MV 5000
|
|
#define CP_C_ENTER_HIGH_MV 7000
|
|
#define CP_C_EXIT_LOW_MV 4500
|
|
#define CP_C_EXIT_HIGH_MV 7500
|
|
|
|
#define CP_D_ENTER_LOW_MV 2000
|
|
#define CP_D_ENTER_HIGH_MV 4000
|
|
#define CP_D_EXIT_LOW_MV 1500
|
|
#define CP_D_EXIT_HIGH_MV 4500
|
|
|
|
#define CP_E_ENTER_LOW_MV -1000
|
|
#define CP_E_ENTER_HIGH_MV 2000
|
|
#define CP_E_EXIT_LOW_MV -1500
|
|
#define CP_E_EXIT_HIGH_MV 2500
|
|
|
|
#define CP_F_ENTER_MV -11500
|
|
#define CP_F_EXIT_MV -10500
|
|
|
|
static int32_t cp_voltage_mv = 0;
|
|
static int32_t cp_voltage_filt_mv = 0;
|
|
static uint8_t cp_filter_initialized = 0;
|
|
static uint8_t cp_duty = 0;
|
|
CP_State_t fake_cp_state = EV_STATE_ACQUIRING;
|
|
static CP_State_t cp_stable_state = EV_STATE_ACQUIRING;
|
|
static CP_State_t cp_candidate_state = EV_STATE_ACQUIRING;
|
|
static uint32_t cp_candidate_since_ms = 0;
|
|
|
|
static uint32_t CP_ReadAdcChannel(uint32_t ch) {
|
|
uint32_t adc = 0;
|
|
|
|
ADC_Select_Channel(ch);
|
|
HAL_ADC_Start(&hadc1);
|
|
HAL_ADC_PollForConversion(&hadc1, 10);
|
|
adc = HAL_ADC_GetValue(&hadc1);
|
|
HAL_ADC_Stop(&hadc1);
|
|
|
|
return adc;
|
|
}
|
|
#define VREFINT_CAL_ADDR ((uint16_t*)0x1FFFF7BA) // для STM32F1!
|
|
|
|
static uint8_t CP_IsInRange(int32_t v, int32_t lo, int32_t hi) {
|
|
return (v >= lo && v <= hi) ? 1u : 0u;
|
|
}
|
|
|
|
static int32_t CP_ApplyEma(int32_t raw_mv) {
|
|
if (!cp_filter_initialized) {
|
|
cp_voltage_filt_mv = raw_mv;
|
|
cp_filter_initialized = 1;
|
|
return cp_voltage_filt_mv;
|
|
}
|
|
|
|
cp_voltage_filt_mv += ((raw_mv - cp_voltage_filt_mv) * CP_EMA_ALPHA_Q8) / 256;
|
|
return cp_voltage_filt_mv;
|
|
}
|
|
|
|
static CP_State_t CP_ClassifyWithHysteresis(int32_t v, CP_State_t prev) {
|
|
switch (prev) {
|
|
case EV_STATE_A_IDLE:
|
|
if (v >= CP_A_EXIT_MV) return EV_STATE_A_IDLE;
|
|
break;
|
|
case EV_STATE_B_CONN_PREP:
|
|
if (CP_IsInRange(v, CP_B_EXIT_LOW_MV, CP_B_EXIT_HIGH_MV)) return EV_STATE_B_CONN_PREP;
|
|
break;
|
|
case EV_STATE_C_CONN_ACTIVE:
|
|
if (CP_IsInRange(v, CP_C_EXIT_LOW_MV, CP_C_EXIT_HIGH_MV)) return EV_STATE_C_CONN_ACTIVE;
|
|
break;
|
|
case EV_STATE_D_CONN_ACT_VENT:
|
|
if (CP_IsInRange(v, CP_D_EXIT_LOW_MV, CP_D_EXIT_HIGH_MV)) return EV_STATE_D_CONN_ACT_VENT;
|
|
break;
|
|
case EV_STATE_E_NO_POWER:
|
|
if (CP_IsInRange(v, CP_E_EXIT_LOW_MV, CP_E_EXIT_HIGH_MV)) return EV_STATE_E_NO_POWER;
|
|
break;
|
|
case EV_STATE_F_ERROR:
|
|
if (v <= CP_F_EXIT_MV) return EV_STATE_F_ERROR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (v >= CP_A_ENTER_MV) return EV_STATE_A_IDLE;
|
|
if (CP_IsInRange(v, CP_B_ENTER_LOW_MV, CP_B_ENTER_HIGH_MV)) return EV_STATE_B_CONN_PREP;
|
|
if (CP_IsInRange(v, CP_C_ENTER_LOW_MV, CP_C_ENTER_HIGH_MV)) return EV_STATE_C_CONN_ACTIVE;
|
|
if (CP_IsInRange(v, CP_D_ENTER_LOW_MV, CP_D_ENTER_HIGH_MV)) return EV_STATE_D_CONN_ACT_VENT;
|
|
if (CP_IsInRange(v, CP_E_ENTER_LOW_MV, CP_E_ENTER_HIGH_MV)) return EV_STATE_E_NO_POWER;
|
|
if (v <= CP_F_ENTER_MV) return EV_STATE_F_ERROR;
|
|
return EV_STATE_ACQUIRING;
|
|
}
|
|
|
|
static uint32_t CP_GetDebounceMs(CP_State_t next_state) {
|
|
if (next_state == EV_STATE_F_ERROR) {
|
|
if (cp_duty <= CP_LOW_DUTY_THRESHOLD_PERCENT) {
|
|
return CP_DEBOUNCE_MS_F_LOW_DUTY;
|
|
}
|
|
return CP_DEBOUNCE_MS_F;
|
|
}
|
|
return CP_DEBOUNCE_MS_DEFAULT;
|
|
}
|
|
|
|
static int32_t CP_ReadVoltageMv(void)
|
|
{
|
|
uint32_t adc = 0;
|
|
int32_t v_adc_mv = 0;
|
|
int32_t v_out_mv = 0;
|
|
|
|
adc = CP_ReadAdcChannel((uint32_t)4u);
|
|
v_adc_mv = (int32_t)((adc * 3300u) / 4095u);
|
|
v_out_mv = ((v_adc_mv - 1723) * 1000) / 130;
|
|
|
|
return v_out_mv;
|
|
}
|
|
|
|
void CP_Init(void) {
|
|
/* TIM3_CH2 (PA7): set 1kHz PWM like original CCS logic. */
|
|
htim3.Instance->PSC = 160 - 1;
|
|
htim3.Instance->ARR = MAX_DUTY - 1;
|
|
|
|
#if DUTY_INVERT == 0
|
|
htim3.Instance->CCR2 = MAX_DUTY;
|
|
htim3.Instance->CCR1 = MAX_DUTY + 5;
|
|
#else
|
|
htim3.Instance->CCR2 = 0;
|
|
htim3.Instance->CCR1 = 0;
|
|
#endif
|
|
|
|
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
|
|
HAL_TIM_OC_Start_IT(&htim3, TIM_CHANNEL_1);
|
|
}
|
|
|
|
void CP_SetDuty(uint8_t percentage) {
|
|
uint32_t pwmduty = MAX_DUTY * percentage / 100;
|
|
cp_duty = percentage;
|
|
|
|
#if DUTY_INVERT == 0
|
|
htim3.Instance->CCR2 = pwmduty;
|
|
htim3.Instance->CCR1 = 0 + 1;
|
|
#else
|
|
htim3.Instance->CCR2 = MAX_DUTY - pwmduty;
|
|
htim3.Instance->CCR1 = MAX_DUTY - pwmduty + 5;
|
|
#endif
|
|
}
|
|
|
|
uint8_t CP_GetDuty(void) {
|
|
return cp_duty;
|
|
}
|
|
|
|
int32_t CP_GetVoltage(void) {
|
|
return cp_voltage_mv;
|
|
}
|
|
|
|
CP_State_t CP_GetState(void) {
|
|
int32_t voltage_real = cp_voltage_filt_mv;
|
|
uint32_t now = HAL_GetTick();
|
|
|
|
if(fake_cp_state != EV_STATE_ACQUIRING) {
|
|
return fake_cp_state;
|
|
}
|
|
|
|
CP_State_t instant_state = CP_ClassifyWithHysteresis(voltage_real, cp_stable_state);
|
|
|
|
if (instant_state == cp_stable_state) {
|
|
cp_candidate_state = cp_stable_state;
|
|
cp_candidate_since_ms = now;
|
|
} else {
|
|
if (cp_candidate_state != instant_state) {
|
|
cp_candidate_state = instant_state;
|
|
cp_candidate_since_ms = now;
|
|
} else if ((now - cp_candidate_since_ms) >= CP_GetDebounceMs(cp_candidate_state)) {
|
|
cp_stable_state = cp_candidate_state;
|
|
}
|
|
}
|
|
|
|
return cp_stable_state;
|
|
}
|
|
|
|
void CP_Loop(void) {
|
|
(void)CP_GetState();
|
|
}
|
|
|
|
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
|
|
{
|
|
if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
|
|
if (ADC_TryLock() == 0u) {
|
|
return;
|
|
}
|
|
cp_voltage_mv = CP_ReadVoltageMv();
|
|
(void)CP_ApplyEma(cp_voltage_mv);
|
|
ADC_Unlock();
|
|
}
|
|
}
|
|
|