#include "app_config.h"
#include "asm/clock.h"
#include "system/timer.h"
#include "bmp280.h"
#include "asm/cpu.h"
#include "generic/typedef.h"
#include "generic/gpio.h"
#if defined(TCFG_BMP280_ENABLE) && TCFG_BMP280_ENABLE
#if defined(TCFG_BMP280_DEV_ENABLE) && TCFG_BMP280_DEV_ENABLE

#undef LOG_TAG_CONST
#define LOG_TAG     "[bmp280]"
#define LOG_ERROR_ENABLE
#define LOG_INFO_ENABLE
#include "debug.h"

struct _bmp280_dev_platform_data *bmp280_iic_info;

#if TCFG_BMP280_USER_IIC_TYPE
#define iic_init(iic)                       hw_iic_init(iic)
#define iic_uninit(iic)                     hw_iic_uninit(iic)
#define iic_start(iic)                      hw_iic_start(iic)
#define iic_stop(iic)                       hw_iic_stop(iic)
#define iic_tx_byte(iic, byte)              hw_iic_tx_byte(iic, byte)
#define iic_rx_byte(iic, ack)               hw_iic_rx_byte(iic, ack)
#define iic_read_buf(iic, buf, len)         hw_iic_read_buf(iic, buf, len)
#define iic_write_buf(iic, buf, len)        hw_iic_write_buf(iic, buf, len)
#define iic_suspend(iic)                    hw_iic_suspend(iic)
#define iic_resume(iic)                     hw_iic_resume(iic)
#else
#define iic_init(iic)                       soft_iic_init(iic)
#define iic_uninit(iic)                     soft_iic_uninit(iic)
#define iic_start(iic)                      soft_iic_start(iic)
#define iic_stop(iic)                       soft_iic_stop(iic)
#define iic_tx_byte(iic, byte)              soft_iic_tx_byte(iic, byte)
#define iic_rx_byte(iic, ack)               soft_iic_rx_byte(iic, ack)
#define iic_read_buf(iic, buf, len)         soft_iic_read_buf(iic, buf, len)
#define iic_write_buf(iic, buf, len)        soft_iic_write_buf(iic, buf, len)
#define iic_suspend(iic)                    soft_iic_suspend(iic)
#define iic_resume(iic)                     soft_iic_resume(iic)
#endif
/* extern void delay(unsigned int cnt);//eeprom */
extern void delay_2ms(int cnt);//fm
extern spinlock_t sensor_iic;
extern u8 sensor_iic_init_status;
static bool bmp280_read_buf(u8 reg, u8 *buf, u8 len)
{
    spin_lock(&sensor_iic);
    iic_start(bmp280_iic_info->iic_hdl);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, BMP280_I2C_ADDR << 1 | 0)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 read fail1!\n");
        return false;
    }
    delay_2ms(bmp280_iic_info->iic_delay);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, reg)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 read fail2!\n");
        return false;
    }
    delay_2ms(bmp280_iic_info->iic_delay);
    iic_start(bmp280_iic_info->iic_hdl);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, BMP280_I2C_ADDR << 1 | 1)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 read fail3!\n");
        return false;
    }

    for (u8 i = 0; i < len - 1; i++) {
        buf[i] = iic_rx_byte(bmp280_iic_info->iic_hdl, 1);
        delay_2ms(bmp280_iic_info->iic_delay);
    }
    buf[len - 1] = iic_rx_byte(bmp280_iic_info->iic_hdl, 0);
    iic_stop(bmp280_iic_info->iic_hdl);
    delay_2ms(bmp280_iic_info->iic_delay);
    spin_unlock(&sensor_iic);
    return true;
}

static bool bmp280_write_byte(u8 reg, u8 data)
{
    spin_lock(&sensor_iic);
    iic_start(bmp280_iic_info->iic_hdl);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, BMP280_I2C_ADDR << 1)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 write fail1!\n");
        return false;
    }
    delay_2ms(bmp280_iic_info->iic_delay);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, reg)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 write fail2!\n");
        return false;
    }
    delay_2ms(bmp280_iic_info->iic_delay);
    if (0 == iic_tx_byte(bmp280_iic_info->iic_hdl, data)) {
        iic_stop(bmp280_iic_info->iic_hdl);
        spin_unlock(&sensor_iic);
        log_error("bmp280 write fail3!\n");
        return false;
    }
    iic_stop(bmp280_iic_info->iic_hdl);
    delay_2ms(bmp280_iic_info->iic_delay);
    spin_unlock(&sensor_iic);
    return true;
}

//设置BMP过采样因子 MODE
//BMP280_SLEEP_MODE||BMP280_FORCED_MODE||BMP280_NORMAL_MODE
bool bmp280_set_temoversamp(bmp_oversample_mode *oversample_mode)
{
    u8 regtmp;
    regtmp = ((oversample_mode->t_osample) << 5) |
             ((oversample_mode->p_osample) << 2) |
             ((oversample_mode)->workmode);
    /* log_info("init reg:%x",regtmp); */

    return bmp280_write_byte(BMP280_CTRLMEAS_REG, regtmp);
}
//设置保持时间和滤波器分频因子
bool bmp280_set_standby_filter(bmp_config *bmp_config)
{
    u8 regtmp;
    regtmp = ((bmp_config->t_sb) << 5) |
             ((bmp_config->filter_coefficient) << 2) |
             ((bmp_config->spi_en));
    /* log_info("init config reg:%x",regtmp); */

    return bmp280_write_byte(BMP280_CONFIG_REG, regtmp);
}
//设置bmp280工作模式
void bmp280_set_work_mode(BMP280_WORK_MODE workmode)
{
    u8 ctrl_maes_data = 0;
    bmp280_read_buf(BMP280_CTRLMEAS_REG, &ctrl_maes_data, 1);
    /* log_info("old ctrl_maes_data:%x ",ctrl_maes_data); */
    ctrl_maes_data &= ~(BIT(1) | BIT(0));
    ctrl_maes_data += (workmode << 0);
    /* log_info("nem ctrl_maes_data:%x ",ctrl_maes_data); */
    bmp280_write_byte(BMP280_CTRLMEAS_REG, ctrl_maes_data);
}
bool bmp280_check_id(void)
{
    u8 bmp280_id;
    if (bmp280_read_buf(BMP280_CHIPID_REG, &bmp280_id, 1)) {
        log_info("bmp280 id:%x", bmp280_id);
        return true;
    } else {
        log_error("read bmp280 id fail!");
        return false;
    }
}

//bmp280 复位
bool bmp280_reset()  //return 1:ok;   0:fail
{
    return bmp280_write_byte(BMP280_RESET_REG, BMP280_RESET_VALUE);
}

bmp280_params bmp280_par;
bool bmp280_init(void *priv)
{

    if (sensor_iic_init_status == 0) {
        spin_lock_init(&sensor_iic);
        sensor_iic_init_status = 1;
    }
    u8 bmp280_id;
    if (priv == NULL) {
        log_info("bmp280 init fail(no priv)\n");
        return false;
    }
    bmp280_iic_info = (struct _bmp280_dev_platform_data *)priv;

    if (bmp280_read_buf(BMP280_CHIPID_REG, &bmp280_id, 1)) {
        log_info("bmp280 id:%x", bmp280_id);
    } else {
        log_error("read bmp280 id fail!");
        return false;
    }
    /********************读矫正参数*********************/
    u8 temp[24] = {0};
    bmp280_read_buf(BMP280_DIG_T1_LSB_REG, temp, 24);
    //温度传感器的矫正值
    bmp280_par.t1 = (((u16)temp[1]) << 8) + temp[0];
    bmp280_par.t2 = (temp[3] << 8) + temp[2];
    bmp280_par.t3 = (temp[5] << 8) + temp[4];
    //大气压传感器的矫正值
    bmp280_par.p1 = (((u16)temp[7]) << 8) + temp[6];
    bmp280_par.p2 = (temp[9] << 8) + temp[8];
    bmp280_par.p3 = (temp[11] << 8) + temp[10];
    bmp280_par.p4 = (temp[13] << 8) + temp[12];
    bmp280_par.p5 = (temp[15] << 8) + temp[14];
    bmp280_par.p6 = (temp[17] << 8) + temp[16];
    bmp280_par.p7 = (temp[19] << 8) + temp[18];
    bmp280_par.p8 = (temp[21] << 8) + temp[20];
    bmp280_par.p9 = (temp[23] << 8) + temp[22];
    /* log_info("%d,%d,%d;%d,%d,%d,%d,%d,%d,%d,%d,%d",bmp280_par.t1,bmp280_par.t2,bmp280_par.t3,bmp280_par.p1,bmp280_par.p2,bmp280_par.p3,bmp280_par.p4,bmp280_par.p5,bmp280_par.p6,bmp280_par.p7,bmp280_par.p8,bmp280_par.p9); */

    /******************************************************/
    if (!(bmp280_write_byte(BMP280_RESET_REG, BMP280_RESET_VALUE))) {	//往复位寄存器写入给定值
        log_error("bmp280 reset error!\n");
        return false;
    }

    bmp_config					bmp_configstr;
    bmp_configstr.t_sb = BMP280_T_SB1;
    bmp_configstr.filter_coefficient = BMP280_FILTER_MODE_4;
    bmp_configstr.spi_en = DISABLE;
    if (!(bmp280_set_standby_filter(&bmp_configstr))) {
        log_error("bmp280 init mode error!\n");
        return false;
    }

    bmp_oversample_mode			bmp_oversample_modestr;
    bmp_oversample_modestr.t_osample = BMP280_T_MODE_1;
    bmp_oversample_modestr.p_osample = BMP280_P_MODE_3;
    bmp_oversample_modestr.workmode  = BMP280_NORMAL_MODE;
    if (!(bmp280_set_temoversamp(&bmp_oversample_modestr))) {
        log_error("bmp280 init mode error!\n");
        return false;
    }

    return true;
}

/*****************算法*******************/
/************计算补偿值***************/
s32 t_fine;			//用于计算补偿
#if USE_FIXED_POINT_COMPENSATE  //使用定点补偿
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
static s32 bmp280_compensate_t_int32(s32 adc_T)
{
    s32 var1, var2, T;
    var1 = (((adc_T >> 3) - ((s32)bmp280_par.t1 << 1)) * ((s32)bmp280_par.t2)) >> 11;
    var2 = (((((adc_T >> 4) - ((s32)bmp280_par.t1)) * ((adc_T >> 4) - ((s32)bmp280_par.t1))) >> 12) * ((s32)bmp280_par.t3)) >> 14;
    t_fine = var1 + var2;
    T = (t_fine * 5 + 128) >> 8;
    return T;
}

// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
static u32 bmp280_compensate_p_int64(s32 adc_P)
{
    s64 var1, var2, p;
    var1 = ((s64)t_fine) - 128000;
    var2 = var1 * var1 * (s64)bmp280_par.p6;
    var2 = var2 + ((var1 * (s64)bmp280_par.p5) << 17);
    var2 = var2 + (((s64)bmp280_par.p4) << 35);
    var1 = ((var1 * var1 * (s64)bmp280_par.p3) >> 8) + ((var1 * (s64)bmp280_par.p2) << 12);
    var1 = (((((s64)1) << 47) + var1)) * ((s64)bmp280_par.p1) >> 33;
    if (var1 == 0) {
        return 0; // avoid exception caused by division by zero
    }
    p = 1048576 - adc_P;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((s64)bmp280_par.p9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((s64)bmp280_par.p8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((s64)bmp280_par.p7) << 4);
    return (u32)p;
}

#else  //使用浮点补偿
// Returns temperature in DegC, double precision. Output value of “51.23” equals 51.23 DegC.
// t_fine carries fine temperature as global value
static double bmp280_compensate_t_double(s32 adc_T)
{
    double var1, var2, T;
    var1 = (((double)adc_T) / 16384.0 - ((double)bmp280_par.t1) / 1024.0) * ((double)bmp280_par.t2);
    var2 = ((((double)adc_T) / 131072.0 - ((double)bmp280_par.t1) / 8192.0) * (((double)adc_T) / 131072.0 - ((double) bmp280_par.t1) / 8192.0)) * ((double)bmp280_par.t3);
    t_fine = (s32)(var1 + var2);
    T = (var1 + var2) / 5120.0;
    return T;
}

// Returns pressure in Pa as double. Output value of “96386.2” equals 96386.2 Pa = 963.862 hPa
static double bmp280_compensate_p_double(s32 adc_P)
{
    double var1, var2, p;
    var1 = ((double)t_fine / 2.0) - 64000.0;
    var2 = var1 * var1 * ((double)bmp280_par.p6) / 32768.0;
    var2 = var2 + var1 * ((double)bmp280_par.p5) * 2.0;
    var2 = (var2 / 4.0) + (((double)bmp280_par.p4) * 65536.0);
    var1 = (((double)bmp280_par.p3) * var1 * var1 / 524288.0 + ((double)bmp280_par.p2) * var1) / 524288.0;
    var1 = (1.0 + var1 / 32768.0) * ((double)bmp280_par.p1);
    if (var1 == 0.0) {
        return 0; // avoid exception caused by division by zero
    }
    p = 1048576.0 - (double)adc_P;
    p = (p - (var2 / 4096.0)) * 6250.0 / var1;
    var1 = ((double)bmp280_par.p9) * p * p / 2147483648.0;
    var2 = p * ((double)bmp280_par.p8) / 32768.0;
    p = p + (var1 + var2 + ((double)bmp280_par.p7)) / 16.0;
    return p;
}
#endif

//获取BMP当前状态
//status_flag = BMP280_MEASURING
//			 	BMP280_IM_UPDATE
static u8  bmp280_getstatus(u8 status_flag)
{
    u8 flag;
    bmp280_read_buf(BMP280_STATUS_REG, &flag, 1);
    if (flag & status_flag)	{
        return 1;
    } else {
        return 0;
    }
}
//温度值-℃(实际值的100倍(定点法)或实际值(浮点法))
static bmp_temperature_data bmp280_get_temperature(void)
{
    u8 tem_data[3] = {0};
    long signed bit32;
    bmp280_read_buf(BMP280_TEMPERATURE_MSB_REG, tem_data, 3);
    bit32 = ((long)(tem_data[0] << 12)) | ((long)(tem_data[1] << 4)) | (tem_data[2] >> 4);
    /* log_info("tembit32:%ld",bit32); */
#if USE_FIXED_POINT_COMPENSATE  //使用定点补偿
    return bmp280_compensate_t_int32(bit32);
#else      //使用浮点补偿
    return bmp280_compensate_t_double(bit32);
#endif
}
//获取大气压值-Pa(实际值的100倍(定点法)或实际值(浮点法))
static bmp_pressure_data bmp280_get_pressure(void)
{
    u8 tem_data[3] = {0};
    long signed bit32;
    bmp280_read_buf(BMP280_PRESSURE_MSB_REG, tem_data, 3);
    bit32 = ((long)(tem_data[0] << 12)) | ((long)(tem_data[1] << 4)) | (tem_data[2] >> 4);
    /* log_info("prebit32:%ld,t_fine:%d",bit32,t_fine); */
#if USE_FIXED_POINT_COMPENSATE  //使用定点补偿
    u32 pressure = bmp280_compensate_p_int64(bit32);
    pressure = pressure * 100 / 256;
#else      //使用浮点补偿
    double pressure = bmp280_compensate_p_double(bit32);
#endif
    return pressure;
}
/*
 * 仅在BMP280被设置为normal mode时，
 * 可使用该接口直接读取温度和气压。
 * (实际值的100倍(定点法)或实际值(浮点法))
 */
void bmp280_get_temperature_and_pressure(bmp_temperature_data *temperature, bmp_pressure_data *pressure)
{
    if (bmp280_check_id() == false) {
        return ;
    }
    while (bmp280_getstatus(BMP280_MEASURING));
    while (bmp280_getstatus(BMP280_IM_UPDATE));
    *temperature = bmp280_get_temperature();  //必须先读温度再度气压
    *pressure = bmp280_get_pressure();
}

/**
 * 当BMP280被设置为forced mode时，
 * 可使用该接口直接读取温度和气压。
 * (实际值的100倍(定点法)或实际值(浮点法))
 */
void bmp280_forced_mode_get_temperature_and_pressure(bmp_temperature_data *temperature, bmp_pressure_data *pressure)
{
    bmp280_set_work_mode(BMP280_FORCED_MODE);
    bmp280_get_temperature_and_pressure(temperature, pressure);
}

//计算高度, 返回海拔高度: (m)
#include "math.h"
#define SEA_LEVEL_PRESSURE  1013.23f
float bmp280_get_altitude(bmp_temperature_data temperature, bmp_pressure_data pressure)
{
    /* log_info("temperature:%d,pressure:%d",(s32)temperature,(u32)pressure); */
#if USE_FIXED_POINT_COMPENSATE  //使用定点补偿
    /* return 44330 * (1.0 - pow(pressure/10000 / SEA_LEVEL_PRESSURE, 0.1903)); */
    //计算公式温度用实际值，压强用实际值的百分之一
    return ((float)powf(SEA_LEVEL_PRESSURE / ((float)pressure / 10000.0), 0.190223f) - 1.0f) * ((float)temperature / 100.0 + 273.15f) / 0.0065f; // Calculate the altitude in metres
#else      //使用浮点补偿
    /* return 44330 * (1.0 - pow(pressure/100 / SEA_LEVEL_PRESSURE, 0.1903)); */
    //计算公式温度用实际值，压强用实际值的百分之一
    return ((float)powf(SEA_LEVEL_PRESSURE / ((float)pressure / 100.0), 0.190223f) - 1.0f) * ((float)temperature + 273.15f) / 0.0065f; // Calculate the altitude in metres
#endif
}

/*************************test*************************/
#if 0
static struct _bmp280_dev_platform_data bmp280_iic_info_test = {
    .iic_hdl = 1,
    .iic_delay = 0
};
#define _portio 0
void bmp280_init_read_test()
{
    u16 i = 0, iii = 40;
    bmp_pressure_data bmp_pressure;
    bmp_temperature_data bmp_temperature;
    float bmp280_altitude = 0;
    /* JL_PORTA->DIR &= ~(BIT(0)|BIT(9)|BIT(10)); */
    /* JL_PORTA->OUT &= ~(BIT(0)|BIT(9)|BIT(10)); */
    iic_init(0);

    log_info("bmp280 init***************************\n");
    if (bmp280_init(&bmp280_iic_info_test)) {
        log_info("bmp280 init ok!***************************\n");
        while (iii--) {
            bmp280_get_temperature_and_pressure(&bmp_temperature, &bmp_pressure);
            /* bmp280_forced_mode_get_temperature_and_pressure(&bmp_temperature , &bmp_pressure); */
            bmp280_altitude = bmp280_get_altitude(bmp_temperature, bmp_pressure);
#if USE_FIXED_POINT_COMPENSATE  //使用定点补偿

            log_info("bmp280 fixed Pressure %ld.%02dpa,temperature:%d.%02dc,alti:%d.%02dm\n", bmp_pressure / 100, bmp_pressure % 100, bmp_temperature / 100, bmp_temperature % 100, (s32)bmp280_altitude, (u32)((s32)(bmp280_altitude * 100) % 100));
#else          //使用浮点补偿

            log_info("bmp280 float Pressure %d.%02dpa,temperature:%d.%02dc,alti:%d.%02dm\n", (s32)bmp_pressure, (s32)(bmp_pressure * 100) % 100, (s32)bmp_temperature, (s32)(bmp_temperature * 100) % 100, (s32)bmp280_altitude, (u32)((s32)(bmp280_altitude * 100) % 100));
#endif
            os_time_dly(50);
        }
    } else {
        log_error("bmp280 init fail!***************************\n");
    }
    while (1);
}

#endif



#endif
#else
void bmp280_set_work_mode(BMP280_WORK_MODE workmode)
{
    return;
}
void bmp280_get_temperature_and_pressure(bmp_temperature_data *temperature, bmp_pressure_data *pressure)
{
    return;
}
float bmp280_get_altitude(bmp_temperature_data temperature, bmp_pressure_data pressure)
{
    return 0;
}
bool bmp280_init(void *priv)
{
    return 0;
}

#endif
