#include "periph_gpadc.h"


/////////////////////////////////////////////////////////////////////////////
// 全局变量
/////////////////////////////////////////////////////////////////////////////
uint8_t g_gpadc_stat = 0; // 0: no cali, 1: cali done
int32_t g_gpadc_div1_a;
int32_t g_gpadc_div1_b;
int32_t g_gpadc_div2_a;
int32_t g_gpadc_div2_b;
int32_t g_gpadc_div3_a;
int32_t g_gpadc_div3_b;
int32_t g_gpadc_div4_a;
int32_t g_gpadc_div4_b;
int32_t g_gpadc_vbat_a;
int32_t g_gpadc_vbat_b;
int32_t g_gpadc_div1_samp_valid_min;      
int32_t g_gpadc_div1_samp_valid_max;
int32_t g_gpadc_div2_samp_valid_min;     
int32_t g_gpadc_div2_samp_valid_max;
int32_t g_gpadc_div3_samp_valid_min;     
int32_t g_gpadc_div3_samp_valid_max;
int32_t g_gpadc_div4_samp_valid_min;     
int32_t g_gpadc_div4_samp_valid_max;
uint16_t g_gpadc_vbat = 0;  //用于存储电池电压
// vbat
const int16_t gpadc_batvoltoffset[] = {-245, -104, -18, 27, 34, 53, 51, 41, 24, -3, -7, -19, -41, -51, -68, -91};
// div
const int16_t gpadc_div0_offset[][16] = {{-120,-76,-52,-37,-28,-21,-17,-13,-10,-8,-6,-4,-4,-2,-1,0,}};
#ifdef GPADC_HIGH_PRECISION_CALI
    const int16_t gpadc_div1_offset[][16] = 
    {
        {-230,-142,-91,-63,-45,-33,-23,-17,-11,-7,-3,-1,3,5,5,7,},
        {-229,-141,-92,-64,-46,-34,-24,-18,-14,-10,-6,-2,0,2,4,4,},
        {-222,-136,-89,-61,-45,-33,-25,-17,-13,-9,-7,-3,-1,1,3,3,},
        {-211,-129,-84,-58,-42,-30,-22,-16,-12,-8,-6,-4,-2,0,2,2,},
        {-194,-115,-75,-51,-37,-27,-21,-15,-11,-9,-5,-3,-3,-1,1,1,},
        {-159,-94,-60,-40,-30,-22,-16,-12,-10,-8,-6,-4,-2,0,0,0,},
        {-99,-59,-37,-25,-17,-13,-11,-9,-7,-5,-5,-3,-3,-1,-1,-1,},
        {-20,-14,-8,-6,-4,-2,-2,-2,-2,0,0,0,0,0,0,0,},
        {27,39,25,17,11,9,7,5,3,3,3,1,1,1,1,1,},
        {-18,62,66,44,30,20,14,10,8,6,4,2,2,0,0,0,},
        {-71,27,77,77,55,37,25,17,13,9,5,3,3,1,-1,-1,},
        {-104,-8,50,80,80,60,42,28,20,14,8,6,2,0,-2,-2,},
        {-128,-33,23,59,81,83,63,45,31,21,15,9,5,3,1,-1,},
        {-147,-52,4,38,64,80,82,66,46,28,22,16,10,6,2,0,},
        {-162,-69,-13,21,45,63,77,79,65,47,33,21,15,9,3,-1,},
        {-175,-82,-26,8,30,48,62,72,76,64,46,32,22,14,8,2,},
    };
    const int16_t gpadc_div2_offset[][16] = 
    {
        {-215,-73,14,59,68,53,41,32,26,23,20,17,14,14,14,14,},
        {-246,-105,-20,28,61,67,55,40,28,22,16,13,13,10,7,7,},
        {-271,-133,-48,0,33,57,66,51,36,24,18,12,6,3,3,0,},
        {-293,-155,-73,-22,11,35,53,62,50,35,23,14,8,2,-1,-4,},
        {-312,-174,-92,-44,-8,16,34,49,55,46,31,16,7,1,-5,-8,},
        {-331,-193,-111,-63,-30,-6,12,27,39,45,39,24,12,0,-6,-12,},
        {-323,-209,-130,-79,-49,-22,-4,11,23,32,38,32,17,5,-4,-13,},
        {-323,-228,-150,-98,-65,-41,-23,-8,4,13,22,28,25,10,-2,-14,},
        {-323,-244,-163,-114,-81,-57,-39,-24,-12,0,9,15,21,18,6,-9,},
        {-323,-278,-182,-130,-97,-73,-55,-40,-28,-19,-10,-1,5,11,8,-4,},
        {-323,-499,-210,-143,-110,-86,-68,-53,-41,-32,-23,-14,-8,-2,1,1,},
        {-323,-1066,-425,-175,-126,-99,-81,-66,-54,-45,-36,-30,-21,-15,-9,-6,},
        {-323,-1820,-989,-386,-157,-118,-100,-85,-73,-61,-52,-46,-37,-31,-28,-22,},
        {-323,-2659,-1722,-942,-363,-146,-113,-98,-86,-74,-65,-59,-50,-44,-41,-35,},
    };
    const int16_t gpadc_div3_offset[][16] = 
    {
        {-24,-24,-24,-24,-24,-120,-96,-76,-64,-48,-40,-28,-20,-12,-8,-8,},
        {-16,-16,-16,-16,-16,-16,-96,-76,-60,-48,-36,-28,-20,-12,-4,0,},
        {-8,-8,-8,-8,-8,-8,-8,-76,-60,-48,-36,-28,-20,-12,-4,0,},
        {0,0,0,0,0,0,0,0,-60,-48,-36,-28,-20,-12,-4,0,},
        {8,8,8,8,8,8,8,8,8,-48,-36,-28,-20,-12,-4,0,},
        {16,16,16,16,16,16,16,16,16,16,-36,-28,-20,-12,-4,0,},
        {24,24,24,24,24,24,24,24,24,24,24,-28,-20,-12,-4,0,},
        {32,32,32,32,32,32,32,32,32,32,32,32,-16,-8,0,4,},
        {40,40,40,40,40,40,40,40,40,40,40,40,40,-8,-4,4,},
        {48,48,48,48,48,48,48,48,48,48,48,48,48,48,-4,4,},
        {56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,4,}
    };
#else
    const int16_t gpadc_div1_offset[][16] = 
    {
        {-234,-146,-95,-67,-49,-37,-27,-21,-15,-11,-7,-5,-1,1,1,3,},
        {-232,-145,-96,-68,-50,-38,-28,-22,-18,-14,-10,-6,-4,-2,0,0,},
        {-225,-139,-92,-64,-48,-36,-28,-20,-16,-12,-10,-6,-4,-2,0,0,},
        {-213,-132,-87,-61,-45,-33,-25,-19,-15,-11,-9,-7,-5,-3,-1,-1,},
        {-196,-117,-77,-53,-39,-29,-23,-17,-13,-11,-7,-5,-5,-3,-1,-1,},
        {-160,-96,-62,-42,-32,-24,-18,-14,-12,-10,-8,-6,-4,-2,-2,-2,},
        {-100,-60,-38,-26,-18,-14,-12,-10,-8,-6,-6,-4,-4,-2,-2,-2,},
        {-20,-15,-9,-7,-5,-3,-3,-3,-3,-1,-1,-1,-1,-1,-1,-1,},
        {27,39,25,17,11,9,7,5,3,3,3,1,1,1,1,1,},
        {-17,62,66,44,30,20,14,10,8,6,4,2,2,0,0,0,},
        {-70,28,78,78,56,38,26,18,14,10,6,4,4,2,0,0,},
        {-102,-7,51,81,81,61,43,29,21,15,9,7,3,1,-1,-1,},
        {-126,-31,25,61,83,85,65,47,33,23,17,11,7,5,3,1,},
        {-144,-50,6,40,66,82,84,68,48,30,24,18,12,8,4,2,},
        {-159,-66,-10,24,48,66,80,82,68,50,36,24,18,12,6,2,},
        {-171,-79,-23,11,33,51,65,75,79,67,49,35,25,17,11,5,}
    };
    const int16_t gpadc_div2_offset[][16] = 
    {
        {-202,-60,27,72,81,66,54,45,39,36,33,30,27,27,27,27,},
        {-234,-93,-8,40,73,79,67,52,40,34,28,25,25,22,19,19,},
        {-260,-122,-37,11,44,68,77,62,47,35,29,23,17,14,14,11,},
        {-283,-145,-63,-12,21,45,63,72,60,45,33,24,18,12,9,6,},
        {-303,-165,-83,-35,1,25,43,58,64,55,40,25,16,10,4,1,},
        {-323,-185,-103,-55,-22,2,20,35,47,53,47,32,20,8,2,-4,},
        {-323,-202,-123,-72,-42,-15,3,18,30,39,45,39,24,12,3,-6,},
        {-323,-222,-144,-92,-59,-35,-17,-2,10,19,28,34,31,16,4,-8,},
        {-323,-239,-158,-109,-76,-52,-34,-19,-7,5,14,20,26,23,11,-4,},
        {-323,-274,-178,-126,-93,-69,-51,-36,-24,-15,-6,3,9,15,12,0,},
        {-323,-496,-207,-140,-107,-83,-65,-50,-38,-29,-20,-11,-5,1,4,4,},
        {-323,-1064,-423,-173,-124,-97,-79,-64,-52,-43,-34,-28,-19,-13,-7,-4,},
        {-323,-1819,-988,-385,-156,-117,-99,-84,-72,-60,-51,-45,-36,-30,-27,-21,},
        {-323,-2659,-1722,-942,-363,-146,-113,-98,-86,-74,-65,-59,-50,-44,-41,-35,},
    };
    const int16_t gpadc_div3_offset[][16] =
    {
        {-22,-22,-22,-22,-22,-118,-94,-74,-62,-46,-38,-26,-18,-10,-6,-6,},
        {-15,-15,-15,-15,-15,-15,-95,-75,-59,-47,-35,-27,-19,-11,-3,1,},
        {-8,-8,-8,-8,-8,-8,-8,-76,-60,-48,-36,-28,-20,-12,-4,0,},
        {0,0,0,0,0,0,0,0,-60,-48,-36,-28,-20,-12,-4,0,},
        {7,7,7,7,7,7,7,7,7,-49,-37,-29,-21,-13,-5,-1,},
        {14,14,14,14,14,14,14,14,14,14,-38,-30,-22,-14,-6,-2,},
        {22,22,22,22,22,22,22,22,22,22,22,-30,-22,-14,-6,-2,},
        {29,29,29,29,29,29,29,29,29,29,29,29,-19,-11,-3,1,},
        {36,36,36,36,36,36,36,36,36,36,36,36,36,-12,-8,0,},
        {44,44,44,44,44,44,44,44,44,44,44,44,44,44,-8,0,},
        {51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,-1,}
    }; 
#endif
// array
const int16_t (*gpadc_ofst_array[4])[] = {
    gpadc_div0_offset,
    gpadc_div1_offset,
    gpadc_div2_offset,
    gpadc_div3_offset
};

/////////////////////////////////////////////////////////////////////////////
// 内部函数
/////////////////////////////////////////////////////////////////////////////
static uint16_t GPADC_InternalSearchBATVoltageOffset(uint16_t vbat);
static uint16_t GPADC_InternalSearchVoltageOffset(uint16_t volt, uint16_t div_sel);

/////////////////////////////////////////////////////////////////////////////
// 功能函数
/////////////////////////////////////////////////////////////////////////////
static void GPADC_UpdateMin(uint16_t *samp_ary, uint16_t samp){
    int i, j;
    // find insert position
    for(i=GPADC_SAMP_FILT_NUM-1; i>=0; i--)
        if(samp < samp_ary[i])
            break;
    // shift other elements
    for(j=0; j<=i-1; j++)
        samp_ary[j] = samp_ary[j+1];
    // insert samp
    if(i >= 0) samp_ary[i] = samp;
}
static void GPADC_UpdateMax(uint16_t *samp_ary, uint16_t samp){
    int i, j;
    // find insert position
    for(i=GPADC_SAMP_FILT_NUM-1; i>=0; i--)
        if(samp > samp_ary[i])
            break;
    // shift other elements
    for(j=0; j<=i-1; j++)
        samp_ary[j] = samp_ary[j+1];
    // insert samp
    if(i >= 0) samp_ary[i] = samp;
}
// Init
void GPADC_Init(GPADC_InitTypeDef * GPADC_InitStruct){
    int t;
    //----------------------------------------
    // RCC
    //----------------------------------------
    // REG_GPADC_LDO_EN |= 0x303; // ldo_en=1
    // REG_GPADC_LDO_VC = 0x2680; // adda vc 0.95V
    *(volatile int *)(0x40000028) |= (1<<15);
    //----------------------------------------
    // CONFIGURATION
    //----------------------------------------
    // analog
    if (GPADC_InitStruct->GPADC_PositiveInputChannel == GPADC_CHANNEL_P_BAT)
    {
        GPADC_ANA->GPADC_CTRL =     (GPADC_INPUT_DIV4 << 13)         |
                                    (GPADC_InitStruct->GPADC_DiffEN << 12)              |
                                    (8 << 8)                                            |
                                    (GPADC_InitStruct->GPADC_NegativeInputChannel << 4) |
                                    (GPADC_InitStruct->GPADC_PositiveInputChannel << 0) ;
    }
    else
    {
        GPADC_ANA->GPADC_CTRL =     (GPADC_InitStruct->GPADC_InputDivide << 13)         |
                                    (GPADC_InitStruct->GPADC_DiffEN << 12)              |
                                    (8 << 8)                                            |
                                    (GPADC_InitStruct->GPADC_NegativeInputChannel << 4) |
                                    (GPADC_InitStruct->GPADC_PositiveInputChannel << 0) ;
    }
    // digital
    GPADC_DIG->GPADC_ACTION =   (GPADC_InitStruct->GPADC_AverageWindow << 12)       |
                                (GPADC_InitStruct->GPADC_TimerSelect << 4)          |
                                (GPADC_InitStruct->GPADC_TimerTrigEN << 3)          |
                                (GPADC_InitStruct->GPADC_ContinueMode << 2)         ;
    GPADC_DIG->GPADC_CLK_CTRL = (GPADC_InitStruct->GPADC_ClkDivide << 8)            |
                                (3 << 2)                                            |
                                (GPADC_InitStruct->GPADC_ClkAutoGate << 1)          |
                                (GPADC_InitStruct->GPADC_ClkSelect << 0)            ;
    //----------------------------------------
    // GPADC calibration parameters set default
    //----------------------------------------
    if(g_gpadc_stat == 0){
        g_gpadc_div1_a = 3601;
        g_gpadc_div1_b = 0;
        g_gpadc_div2_a = 2 * g_gpadc_div1_a;
        g_gpadc_div2_b = 2 * g_gpadc_div1_b;
        g_gpadc_div3_a = 3 * g_gpadc_div1_a;
        g_gpadc_div3_b = 3 * g_gpadc_div1_b;
        g_gpadc_div4_a = 4 * g_gpadc_div1_a;
        g_gpadc_div4_b = 4 * g_gpadc_div1_b;
        g_gpadc_vbat_a = 4 * g_gpadc_div1_a;
        g_gpadc_vbat_b = 4 * g_gpadc_div1_b;
    }
    //----------------------------------------
    // Power
    //----------------------------------------
    // en
    *(volatile int *)(0x42002004) |= 0x301;
    // vc
    t = *(volatile int*)(0x40080104);
    t &= (~(0x1f<<8));
    t |= (6 << 8);
    *(volatile int*)(0x40080104) = t;
}

void GPADC_StructInit(GPADC_InitTypeDef * GPADC_InitStruct)
{
    GPADC_InitStruct->GPADC_InputDivide = GPADC_INPUT_DIV2;
    GPADC_InitStruct->GPADC_DiffEN = DISABLE;
    GPADC_InitStruct->GPADC_NegativeInputChannel= GPADC_CHANNEL_N_REF0V9;
    GPADC_InitStruct->GPADC_PositiveInputChannel = GPADC_CHANNEL_CIN; // default channel core input
    GPADC_InitStruct->GPADC_AverageWindow = GPADC_AVG_NUM_32; // default 3M/32 ~= 100K
    GPADC_InitStruct->GPADC_TimerSelect = 0;
    GPADC_InitStruct->GPADC_TimerTrigEN = DISABLE;
    GPADC_InitStruct->GPADC_ContinueMode = DISABLE;
    GPADC_InitStruct->GPADC_ClkDivide = 3; // default sample rate: 12m/(ClkDivide+1) = 3M
    GPADC_InitStruct->GPADC_ClkSelect = GPADC_CLK_SRC_RC24M;
    GPADC_InitStruct->GPADC_ClkAutoGate = ENABLE; // default enable
}

void GPADC_DeInit(void){
    // local var
    GPADC_InitTypeDef GPADC_InitStruct;
    // de-init
    GPADC_StructInit(&GPADC_InitStruct);
    GPADC_Init(&GPADC_InitStruct);
    GPADC_DIG->GPADC_CLK_CTRL = 0; //清除时钟配置
    GPADC_DIG->GPADC_CFG = 0;
    GPADC_DIG->GPADC_ACTION =0;
    GPADC_Cmd(DISABLE);
    GPADC_ITConfig(GPADC_ITMODE_DMA | GPADC_ITMODE_SAMP, DISABLE); // 清除中断配置
    // CLK
    *(volatile int *)(0x40000028) &= (~(1<<15));
}

void GPADC_Cmd(FunctionalState NewState){
    if (NewState != DISABLE){
        GPADC_ANA->GPADC_EN |= (uint32_t)(ENABLE << 1);
        GPADC_DIG->GPADC_ACTION |= (uint32_t)(ENABLE << 1);
    } else {
        GPADC_ANA->GPADC_EN &= ~((uint32_t)(ENABLE << 1));
        GPADC_DIG->GPADC_ACTION &= ~((uint32_t)(ENABLE << 1));
    }
}

void ADC_SoftwareStartConvCmd(FunctionalState NewState)
{
  if (NewState != DISABLE)
  {
    GPADC_DIG->GPADC_ACTION |= (uint32_t)(ENABLE << 31);
  }
}

uint16_t GPADC_GetConversionValue(void){
    return GPADC_DIG->GPADC_SAMP;
}

uint16_t GPADC_GetConversionVoltage(void){
    uint32_t div_sel, vip_sel;
    int32_t samp, volt, a, b;
    // init
    samp = GPADC_GetConversionValue();
    div_sel = ((GPADC_ANA->GPADC_CTRL) >> 13) & 0x03;
    vip_sel = ((GPADC_ANA->GPADC_CTRL) >>  0) & 0x0f;
    // 输入通道为BAT通道时单独处理
    if (vip_sel == GPADC_CHANNEL_P_BAT){
        a = g_gpadc_vbat_a;
        b = g_gpadc_vbat_b;
        // voltage calibration
        volt = ((samp*a + b)*10)>>GPADC_CALI_ZOOM_SHIFT;
        volt = GPADC_InternalSearchBATVoltageOffset(volt);
        return volt;
    }
    //GPIO通道
    else
    {
        if(div_sel == 0){
            a = g_gpadc_div1_a;
            b = g_gpadc_div1_b;
        } else if(div_sel == 1){
            a = g_gpadc_div2_a;
            b = g_gpadc_div2_b;
        } else if(div_sel == 2){
            a = g_gpadc_div3_a;
            b = g_gpadc_div3_b;
        } else {
            a = g_gpadc_div4_a;
            b = g_gpadc_div4_b;
        }
        // voltage calibration
        volt = ((samp*a + b)*10)>>GPADC_CALI_ZOOM_SHIFT;
        volt = GPADC_InternalSearchVoltageOffset(volt, div_sel);
        if ((div_sel != 0) && (vip_sel == GPADC_CHANNEL_GPIO15)){
            volt = volt*1000/995;
        }

    }
    // out of range detect
    if(volt < 0) volt = 0;
    if(volt > (GPADC_DIV1_VOLT_MAX*(div_sel + 1))) volt = GPADC_DIV1_VOLT_MAX*(div_sel + 1);
    // return
    return (uint16_t)volt;
}

uint16_t GPADC_SampleVoltagePoll(void){
    uint32_t i, t, cont_mode, div_sel, vip_chl;
    uint16_t volt;
    // init
    vip_chl = ((GPADC_ANA->GPADC_CTRL) >> 0) & 0xf;
    div_sel = ((GPADC_ANA->GPADC_CTRL) >> 13) & 0x3;
    cont_mode = ((GPADC_DIG->GPADC_ACTION) >> 2) & 0x1;
    // from div1 to div4 may change 3 times
    if (vip_chl != GPADC_CHANNEL_P_BAT)
    {
        for(i=0; i<4; i++){
            // trigger sample
            if(cont_mode == 0) ADC_SoftwareStartConvCmd(ENABLE);
            // wait sample
            GPADC_WaitWorkDone();
            // while(GPADC_GetFlagStatus(GPADC_FLAG_SAMP) == 0);
            // get voltage
            volt = GPADC_GetConversionVoltage();
            // check valid
            if(((div_sel == 0) && (volt >= GPADC_DIV1_VALID_MIN) && (volt <= GPADC_DIV1_VALID_MAX)) ||
                ((div_sel == 1) && (volt >= GPADC_DIV2_VALID_MIN) && (volt <= GPADC_DIV2_VALID_MAX)) ||
                ((div_sel == 2) && (volt >= GPADC_DIV3_VALID_MIN) && (volt <= GPADC_DIV3_VALID_MAX)) ||
                ((div_sel == 3) && (volt >= GPADC_DIV4_VALID_MIN) && (volt <= GPADC_DIV4_VALID_MAX)))
                return volt;
            // get div_sel
            if(volt <= GPADC_DIV1_VALID_MAX)        div_sel = 0;
            else if(volt <= GPADC_DIV2_VALID_MAX)   div_sel = 1;
            else if(volt <= GPADC_DIV3_VALID_MAX)   div_sel = 2;
            else                                    div_sel = 3;
            // set div_sel
            t = GPADC_ANA->GPADC_CTRL;
            t &= (~(3 << 13));
            t |= (div_sel << 13);
            GPADC_ANA->GPADC_CTRL = t;
            DELAY_US(2); // wait settle
            if(i == 3) return 0xffff; // error value
        }
    }
    else
    {
        // trigger sample
        if(cont_mode == 0) ADC_SoftwareStartConvCmd(ENABLE);
        // wait sample
        GPADC_WaitWorkDone();
        // while(GPADC_GetFlagStatus(GPADC_FLAG_SAMP) == 0);
        // get voltage
        volt = GPADC_GetConversionVoltage();
        // check valid, for BAT channel, don't check here
        //if(((vip_chl == GPADC_CHANNEL_P_BAT) && (volt >= GPADC_DIV4_VALID_MIN) && (volt <= GPADC_DIV4_VALID_MAX)))
        //    return volt;
    }
    // check value valid ???
    return volt;
}

// VBAT 通道不可用，只用于普通通道
// 返回数据格式
//    [24]    : channel gpio15 flag
//    [17:16] : gpadc div
//    [11:0]   : gpadc sample value
uint32_t GPADC_GetSample(void){
    uint32_t i, t, cont_mode, div_sel, vip_sel, val;
    // init
    vip_sel = ((GPADC_ANA->GPADC_CTRL) >>  0) & 0x0f;
    div_sel = ((GPADC_ANA->GPADC_CTRL) >> 13) & 0x3;
    cont_mode = ((GPADC_DIG->GPADC_ACTION) >> 2) & 0x1;
    // from div1 to div4 may change 4 times
    for(i=0; i<4; i++){
        // trigger sample
        //if(cont_mode == 0) ADC_SoftwareStartConvCmd(ENABLE);
        if(cont_mode == 0) GPADC_DIG->GPADC_ACTION |= (uint32_t)(ENABLE << 31);
        // wait sample
        GPADC_WaitWorkDone();
        // while(GPADC_GetFlagStatus(GPADC_FLAG_SAMP) == 0);
        // while((GPADC_DIG->GPADC_INT_CTRL & GPADC_FLAG_SAMP) == 0); // add timeout here ???
        // get sample value
        //val = GPADC_GetConversionValue();
        val = GPADC_DIG->GPADC_SAMP;
        if(i == 3) break; // last sample
        // check valid
        if(div_sel == 0){ 
            if(val < g_gpadc_div1_samp_valid_min)
                div_sel--;
            else if (val > g_gpadc_div1_samp_valid_max)
                div_sel++;
            else
                break;
        } else if(div_sel == 1) { 
            if(val < g_gpadc_div2_samp_valid_min)
                div_sel--;
            else if (val > g_gpadc_div2_samp_valid_max)
                div_sel++;
            else
                break;
        } else if(div_sel == 2) { 
            if(val < g_gpadc_div3_samp_valid_min)
                div_sel--;
            else if (val > g_gpadc_div3_samp_valid_max)
                div_sel++;
            else
                break;
        } else if(div_sel == 3) { 
            if(val < g_gpadc_div4_samp_valid_min)
                div_sel--;
            else if (val > g_gpadc_div4_samp_valid_max)
                div_sel++;
            else
                break;
        }
        // set div_sel
        t = GPADC_ANA->GPADC_CTRL;
        t &= (~(3 << 13));
        t |= (div_sel << 13);
        GPADC_ANA->GPADC_CTRL = t;
        DELAY_US(2); // wait settle
        //if(i == 3) return 0xffff; // error value
    }
    // [31:16]: add flag
    val <<= GPADC_SAMP_AVG_EXT;
    val |= ((vip_sel == GPADC_CHANNEL_GPIO15) ? (1<<24) : 0);
    val |= (div_sel << 16);
    // return, check value valid ???
    return val;
}
// VBAT 通道不可用，只用于普通通道
// 输入参数
//      exp: 2**(exp)次采样值求平均
// 返回值
//    [24]    : channel gpio15 flag
//    [17:16] : gpadc div
//    [11:0]  : gpadc sample value, 10-bit to 12-bit ???
uint32_t GPADC_GetSampleAvgexp(int exp){
    int i;
    uint32_t samp0, samp_sum;
    uint16_t samp;
#if GPADC_SAMP_FILT_NUM
    uint16_t samp_min[GPADC_SAMP_FILT_NUM];
    uint16_t samp_max[GPADC_SAMP_FILT_NUM];
    for(i=0; i<GPADC_SAMP_FILT_NUM; i++){
        samp_min[i] = 0xffff;
        samp_max[i] = 0x0000;
    }
#endif
    // get first sample use auto scale
    samp0 = GPADC_GetSample();
#if GPADC_SAMP_FILT_NUM
    samp_min[GPADC_SAMP_FILT_NUM-1] = samp0 & 0xffff;
    samp_max[GPADC_SAMP_FILT_NUM-1] = samp0 & 0xffff;
#endif
    samp_sum = samp0 & 0xffff;
    // get avg sample
    //printf("in samp: ");
    for(i=0; i<((1<<exp) + (GPADC_SAMP_FILT_NUM<<1) - 1); i++){
        // trigger sample
        GPADC_DIG->GPADC_ACTION |= (uint32_t)(ENABLE << 31);
        // wait sample
        GPADC_WaitWorkDone();
        //while((GPADC_DIG->GPADC_INT_CTRL & GPADC_FLAG_SAMP) == 0);
        // get sample value
        samp = GPADC_DIG->GPADC_SAMP;
        samp <<= GPADC_SAMP_AVG_EXT;
        samp_sum += samp;
#if GPADC_SAMP_FILT_NUM
        GPADC_UpdateMin(samp_min, samp);
        GPADC_UpdateMax(samp_max, samp);
#endif
        //DELAY_US(GPADC_SDLY);
        //if(i<8) printf("%d, ", samp);
    }
#if GPADC_SAMP_FILT_NUM
    for(i=0; i<GPADC_SAMP_FILT_NUM; i++){
        samp_sum -= samp_min[i];
        samp_sum -= samp_max[i];
    }
#endif
    // bit[11:0]: keep 2-bit lsb
    samp_sum >>= exp;
    //printf("avg: %d", samp_sum);
    // bit[31:16]: add flag
    samp_sum |= (samp0 & 0xffff0000);
    // return
    return samp_sum;
}

// 输入值必须是函数"GPADC_GetSample()"的返回值
uint16_t GPADC_SampleToVoltage(uint32_t d){
    uint32_t div_sel, vip_gp15;
    int32_t samp, volt, a, b;
    // error check
    if(d == 0xffff) return 0xffff; // error value
    // init
    samp = (d >> 0) & 0xffff;
    div_sel = (d >> 16) & 0x03;
    vip_gp15 = (d >> 24) & 0x01;
    // a/b
    if(div_sel == 0){
        a = g_gpadc_div1_a;
        b = g_gpadc_div1_b;
    } else if(div_sel == 1){
        a = g_gpadc_div2_a;
        b = g_gpadc_div2_b;
    } else if(div_sel == 2){
        a = g_gpadc_div3_a;
        b = g_gpadc_div3_b;
    } else {
        a = g_gpadc_div4_a;
        b = g_gpadc_div4_b;
    }
    // voltage calibration, has 2 more lsb, 15+12+4=31
    //volt = (((samp*a) + (b<<GPADC_SAMP_AVG_EXT))*10)>>(GPADC_CALI_ZOOM_SHIFT+GPADC_SAMP_AVG_EXT);
    volt = samp*a;
    volt += (b << GPADC_SAMP_AVG_EXT);
    volt *= 10;
    volt >>= (GPADC_CALI_ZOOM_SHIFT + GPADC_SAMP_AVG_EXT);
    volt = GPADC_InternalSearchVoltageOffset(volt, div_sel);
    if ((div_sel != 0) && (vip_gp15 == 1)){
        volt = volt*1000/995;
    }
    // out of range detect
    if(volt < 0) volt = 0;
    if(volt > (GPADC_DIV1_VOLT_MAX*(div_sel + 1))) volt = GPADC_DIV1_VOLT_MAX*(div_sel + 1);
    // return
    return (uint16_t)volt;
}


/**
 * GPADC采样通道切换
 * param: channel:
 *  #define GPADC_CHANNEL_P_GPIO2       0
    #define GPADC_CHANNEL_P_GPIO6       1
    #define GPADC_CHANNEL_P_GPIO7       2
    #define GPADC_CHANNEL_P_GPIO10      3
    #define GPADC_CHANNEL_P_GPIO15      4
    #define GPADC_CHANNEL_P_GPIO17      5
    #define GPADC_CHANNEL_P_GPIO19      6
    #define GPADC_CHANNEL_P_GPIO21      7
    #define GPADC_CHANNEL_P_TEMP        8
    #define GPADC_CHANNEL_P_BAT         9
    #define GPADC_CHANNEL_P_CIN         10
    #define GPADC_CHANNEL_P_CSUP        11
 */

void GPADC_ChangeChannel(uint8_t channel){
    GPADC_ANA->GPADC_CTRL_b.RG_GPADC_VIP_SEL = channel;
    if(channel == GPADC_CHANNEL_P_BAT)
    {
        GPADC_ANA->GPADC_CTRL_b.RG_GPADC_ATT_SEL = GPADC_INPUT_DIV4;
    }
    DELAY_US(2);
    GPADC_ClearFlag(GPADC_FLAG_SAMP | GPADC_FLAG_DMA); // clear flags
    DELAY_US(2);
}

void GPADC_TempSensorCmd(FunctionalState NewState){
    REG_TEMPERATURE_SENSOR_EN = NewState;
}

/**
 * 检查GPADC标志位状态
 * param: ADC_FLAG 
    #define GPADC_FLAG_SAMP
    #define GPADC_FLAG_DMA 
 * 
 */
FlagStatus GPADC_GetFlagStatus(uint8_t ADC_FLAG)
{
  FlagStatus bitstatus = RESET;
  if ((uint8_t)(GPADC_DIG->GPADC_INT_CTRL & ADC_FLAG) != (uint8_t)RESET)
  {
    /* ADC_FLAG is set */
    bitstatus = SET;
  }
  else
  {
    /* ADC_FLAG is reset */
    bitstatus = RESET;
  }
  /* Return the ADC_FLAG status */
  return  bitstatus;
}

#define CUR_TIME_TICK_US()       (REG_RD(0x42000104))
void GPADC_WaitWorkDone()
{
    volatile uint32_t cur_tick = CUR_TIME_TICK_US();

    while(GPADC_GetFlagStatus(GPADC_FLAG_SAMP) == 0 && CUR_TIME_TICK_US() - cur_tick < 2000);
}


void GPADC_ClearFlag(uint8_t ADC_FLAG)
{
  /* Clear the selected ADC flags */
  GPADC_DIG->GPADC_INT_CTRL_BYTE[0] = ~ADC_FLAG;
}

/**
 * GPADC中断配置
 * param:ADC_ITMODE:
    #define GPADC_ITMODE_SAMP
    #define GPADC_ITMODE_DMA
 */
void GPADC_ITConfig(uint8_t ADC_ITMODE, FunctionalState NewState){
    if (NewState != DISABLE)
        GPADC_DIG->GPADC_INT_CTRL |= (uint32_t)(ADC_ITMODE << 16);
    else
        GPADC_DIG->GPADC_INT_CTRL &= ~((uint32_t)(ADC_ITMODE << 16));
}

ITStatus GPADC_GetITStatus(uint8_t ADC_ITMODE){
    ITStatus bitstatus = RESET;
    if(GPADC_DIG->GPADC_INT_CTRL & (ADC_ITMODE << 8))
        bitstatus = SET;
    else
        bitstatus = RESET;
    return bitstatus;
}

void GPADC_ClearITPendingBit(uint8_t ADC_ITMODE){
    GPADC_DIG->GPADC_INT_CTRL_BYTE[1] = ADC_ITMODE;
}

static int GPADC_InternalCalibrationOneRange(int32_t * a, int32_t * b, int32_t cali_hi, int32_t cali_lo, int32_t delta_hi, int32_t delta_lo)
{
    int32_t volt_hi, volt_lo;
    int32_t samp_hi, samp_lo;
    if(cali_hi != 0xffffffff || cali_lo != 0xffffffff){
        samp_hi = ((cali_hi >> 16) & 0x3ff)*GPADC_FIX_COEF + delta_hi;
        samp_lo = ((cali_lo >> 16) & 0x3ff)*GPADC_FIX_COEF + delta_lo;
        volt_hi = ((cali_hi >>  0) & 0xfff)*GPADC_FIX_COEF;
        volt_lo = ((cali_lo >>  0) & 0xfff)*GPADC_FIX_COEF;
        *a = (volt_hi - volt_lo)*GPADC_CALI_ZOOM/(samp_hi - samp_lo);
        *b = (volt_lo*GPADC_CALI_ZOOM - samp_lo*(*a))/GPADC_FIX_COEF;
        return 1;
    }
    return 0;

}

// 后续优化，改成static函数校准单个档位的
void GPADC_StartCalibrationRaw(void){
    uint8_t stasus = 0;
    stasus = GPADC_InternalCalibrationOneRange(&g_gpadc_div1_a, &g_gpadc_div1_b, GPADC_OTP_CALI_DIV1_HIGH, GPADC_OTP_CALI_DIV1_LOW, GPADC_CALI_DIV1_DELTA_HI, GPADC_CALI_DIV1_DELTA_LO);
    stasus &= GPADC_InternalCalibrationOneRange(&g_gpadc_div2_a, &g_gpadc_div2_b, GPADC_OTP_CALI_DIV2_HIGH, GPADC_OTP_CALI_DIV2_LOW, GPADC_CALI_DIV2_DELTA_HI, GPADC_CALI_DIV2_DELTA_LO);
    stasus &= GPADC_InternalCalibrationOneRange(&g_gpadc_div3_a, &g_gpadc_div3_b, GPADC_OTP_CALI_DIV3_HIGH, GPADC_OTP_CALI_DIV3_LOW, GPADC_CALI_DIV3_DELTA_HI, GPADC_CALI_DIV3_DELTA_LO);
    stasus &= GPADC_InternalCalibrationOneRange(&g_gpadc_div4_a, &g_gpadc_div4_b, GPADC_OTP_CALI_DIV4_HIGH, GPADC_OTP_CALI_DIV4_LOW, GPADC_CALI_DIV4_DELTA_HI, GPADC_CALI_DIV4_DELTA_LO);
    stasus &= GPADC_InternalCalibrationOneRange(&g_gpadc_vbat_a, &g_gpadc_vbat_b, GPADC_OTP_CALI_VBAT_HIGH, GPADC_OTP_CALI_VBAT_LOW, GPADC_CALI_VBAT_DELTA_HI, GPADC_CALI_VBAT_DELTA_LO);
    g_gpadc_stat = stasus;
    // display debug info
#ifdef GPADC_DEBUG_PRINT
    printf("  gpadc calibraiton results:\n");
    printf("    g_gpadc_div1_a: %d, gpadc_div1_b: %d\n", (int)g_gpadc_div1_a, (int)g_gpadc_div1_b);
    printf("    g_gpadc_div2_a: %d, gpadc_div2_b: %d\n", (int)g_gpadc_div2_a, (int)g_gpadc_div2_b);
    printf("    g_gpadc_div3_a: %d, gpadc_div3_b: %d\n", (int)g_gpadc_div3_a, (int)g_gpadc_div3_b);
    printf("    g_gpadc_div4_a: %d, gpadc_div4_b: %d\n", (int)g_gpadc_div4_a, (int)g_gpadc_div4_b);
    printf("    g_gpadc_vbat_a: %d, gpadc_vbat_b: %d\n", (int)g_gpadc_vbat_a, (int)g_gpadc_vbat_b);
    printf("\n");
    printf("    DIV1 a*10000: %d, b*100: %d\n", (int)g_gpadc_div1_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div1_b*100/GPADC_CALI_ZOOM);
    printf("    DIV2 a*10000: %d, b*100: %d\n", (int)g_gpadc_div2_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div2_b*100/GPADC_CALI_ZOOM);
    printf("    DIV3 a*10000: %d, b*100: %d\n", (int)g_gpadc_div3_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div3_b*100/GPADC_CALI_ZOOM);
    printf("    DIV4 a*10000: %d, b*100: %d\n", (int)g_gpadc_div4_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div4_b*100/GPADC_CALI_ZOOM);
    printf("    vbat a*10000: %d, b*100: %d\n", (int)g_gpadc_vbat_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_vbat_b*100/GPADC_CALI_ZOOM);
    printf("\n");
#endif
}

static int GPADC_InternalCalibrationOneRangeEx(int32_t * a, int32_t * b, uint32_t volt_hi, uint32_t samp_hi, uint32_t volt_lo, uint32_t samp_lo)
{
    long long temp;
    //samp_hi *= GPADC_FIX_COEF;
    //samp_lo *= GPADC_FIX_COEF;
    //volt_hi *= GPADC_FIX_COEF;
    //volt_lo *= GPADC_FIX_COEF;
    //samp_hi += delta_hi;
    //samp_lo += delta_lo;
    //*a = (volt_hi - volt_lo)*GPADC_CALI_ZOOM/(samp_hi - samp_lo);
    //*b = (volt_lo*GPADC_CALI_ZOOM - samp_lo*(*a))/GPADC_FIX_COEF;
    // a
    temp = ((long long)volt_hi - (long long)volt_lo)*GPADC_CALI_ZOOM;
    *a = temp/(samp_hi - samp_lo);
    // b
    temp = (long long)volt_lo*GPADC_CALI_ZOOM - (long long)samp_lo*(*a);
    *b = temp/GPADC_FIX_COEF;
    return 0;
}

static void GPADC_InternalParseVoltageSample(uint32_t d, uint32_t sd, uint32_t *v, uint32_t *s)
{
    uint32_t tv, ts;
    // voltage
    tv = (d >> 0) & 0xfff; // 12-bit
    tv *= GPADC_FIX_COEF;
    // sample
    if((((d >> 14) & 1) == 1) && (((d >> 30) & 1) == 0)){ // 14-bit, already extend 4-bit
        ts = (d >> 16) & 0x3fff;
        ts *= (GPADC_FIX_COEF/16);
    } else { // 10-bit
        ts = (d >> 16) & 0x3ff;
        ts *= GPADC_FIX_COEF;
    }
    // return
    *v = tv;
    *s = ts + sd;
}

void GPADC_StartCalibrationEx(void){
    uint32_t hv, hs, lv, ls;
    // div1
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_high_div1.raw, GPADC_CALI_DIV1_DELTA_HI, &hv, &hs);
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_low_div1.raw, GPADC_CALI_DIV1_DELTA_LO, &lv, &ls);
    GPADC_InternalCalibrationOneRangeEx(&g_gpadc_div1_a, &g_gpadc_div1_b, hv, hs, lv, ls);
    // div2
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_high_div2.raw, GPADC_CALI_DIV2_DELTA_HI, &hv, &hs);
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_low_div2.raw, GPADC_CALI_DIV2_DELTA_LO, &lv, &ls);
    GPADC_InternalCalibrationOneRangeEx(&g_gpadc_div2_a, &g_gpadc_div2_b, hv, hs, lv, ls);
    // div3
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_high_div3.raw, GPADC_CALI_DIV3_DELTA_HI, &hv, &hs);
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_low_div3.raw, GPADC_CALI_DIV3_DELTA_LO, &lv, &ls);
    GPADC_InternalCalibrationOneRangeEx(&g_gpadc_div3_a, &g_gpadc_div3_b, hv, hs, lv, ls);
    // div4
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_high_div4.raw, GPADC_CALI_DIV4_DELTA_HI, &hv, &hs);
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_low_div4.raw, GPADC_CALI_DIV4_DELTA_LO, &lv, &ls);
    GPADC_InternalCalibrationOneRangeEx(&g_gpadc_div4_a, &g_gpadc_div4_b, hv, hs, lv, ls);
    // vbat
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_vbat_high.raw, GPADC_CALI_VBAT_DELTA_HI, &hv, &hs);
    GPADC_InternalParseVoltageSample(g_otp_cfg.gpadc_vbat_low.raw, GPADC_CALI_VBAT_DELTA_LO, &lv, &ls);
    GPADC_InternalCalibrationOneRangeEx(&g_gpadc_vbat_a, &g_gpadc_vbat_b, hv, hs, lv, ls);

    g_gpadc_div1_samp_valid_min = 0;
    g_gpadc_div1_samp_valid_max = (GPADC_DIV1_VOLT_VALID_MAX*GPADC_CALI_ZOOM - g_gpadc_div1_b)/g_gpadc_div1_a;
    g_gpadc_div2_samp_valid_min = (GPADC_DIV2_VOLT_VALID_MIN*GPADC_CALI_ZOOM - g_gpadc_div2_b)/g_gpadc_div2_a;
    g_gpadc_div2_samp_valid_max = (GPADC_DIV2_VOLT_VALID_MAX*GPADC_CALI_ZOOM - g_gpadc_div2_b)/g_gpadc_div2_a;
    g_gpadc_div3_samp_valid_min = (GPADC_DIV3_VOLT_VALID_MIN*GPADC_CALI_ZOOM - g_gpadc_div3_b)/g_gpadc_div3_a;
    g_gpadc_div3_samp_valid_max = (GPADC_DIV3_VOLT_VALID_MAX*GPADC_CALI_ZOOM - g_gpadc_div3_b)/g_gpadc_div3_a;
    g_gpadc_div4_samp_valid_min = (GPADC_DIV4_VOLT_VALID_MIN*GPADC_CALI_ZOOM - g_gpadc_div4_b)/g_gpadc_div4_a;
    g_gpadc_div4_samp_valid_max = (GPADC_DIV4_VOLT_VALID_MAX*GPADC_CALI_ZOOM - g_gpadc_div4_b)/g_gpadc_div4_a;
    g_gpadc_stat = 1;
    // display debug info
#ifdef GPADC_DEBUG_PRINT
    printf("  gpadc calibraiton ex results:\n");
    printf("    g_gpadc_div1_a: %d, gpadc_div1_b: %d\n", (int)g_gpadc_div1_a, (int)g_gpadc_div1_b);
    printf("    g_gpadc_div2_a: %d, gpadc_div2_b: %d\n", (int)g_gpadc_div2_a, (int)g_gpadc_div2_b);
    printf("    g_gpadc_div3_a: %d, gpadc_div3_b: %d\n", (int)g_gpadc_div3_a, (int)g_gpadc_div3_b);
    printf("    g_gpadc_div4_a: %d, gpadc_div4_b: %d\n", (int)g_gpadc_div4_a, (int)g_gpadc_div4_b);
    printf("    g_gpadc_vbat_a: %d, gpadc_vbat_b: %d\n", (int)g_gpadc_vbat_a, (int)g_gpadc_vbat_b);
    printf("\n");
    printf("    DIV1 a*10000: %d, b*100: %d\n", (int)g_gpadc_div1_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div1_b*100/GPADC_CALI_ZOOM);
    printf("    DIV2 a*10000: %d, b*100: %d\n", (int)g_gpadc_div2_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div2_b*100/GPADC_CALI_ZOOM);
    printf("    DIV3 a*10000: %d, b*100: %d\n", (int)g_gpadc_div3_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div3_b*100/GPADC_CALI_ZOOM);
    printf("    DIV4 a*10000: %d, b*100: %d\n", (int)g_gpadc_div4_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_div4_b*100/GPADC_CALI_ZOOM);
    printf("    vbat a*10000: %d, b*100: %d\n", (int)g_gpadc_vbat_a*10000/GPADC_CALI_ZOOM, (int)g_gpadc_vbat_b*100/GPADC_CALI_ZOOM);
    printf("\n");
#endif
}

void GPADC_BATUpdate(void)
{
    uint32_t div_sel, vip_sel, gpadc_clk_ctrl, gpadc_ctrl, gpadc_action, gpadc_cmd;
    int32_t samp, volt = 0, a, b;

    //保留现场
    gpadc_ctrl = GPADC_ANA->GPADC_CTRL;
    gpadc_clk_ctrl = GPADC_DIG->GPADC_CLK_CTRL;
    gpadc_action = GPADC_DIG->GPADC_ACTION;
    gpadc_cmd = GPADC_ANA->GPADC_EN;


    //配置为采样电源电压模式
    GPADC_ANA->GPADC_CTRL = 0x6889;
    GPADC_DIG->GPADC_CLK_CTRL = 0x30E;
    GPADC_DIG->GPADC_ACTION = 0x7002;
    GPADC_ANA->GPADC_EN = 0x0002;

    ADC_SoftwareStartConvCmd(ENABLE);
    // wait sample
    GPADC_WaitWorkDone();
    // while(GPADC_GetFlagStatus(GPADC_FLAG_SAMP) == 0);
    //采样电压计算
    samp = GPADC_GetConversionValue();
    div_sel = ((GPADC_ANA->GPADC_CTRL) >> 13) & 0x03;
    vip_sel = ((GPADC_ANA->GPADC_CTRL) >>  0) & 0x0f;
    // get a/b
    if (vip_sel == GPADC_CHANNEL_P_BAT){
        a = g_gpadc_vbat_a;
        b = g_gpadc_vbat_b;

        // voltage calibration
        volt = ((samp*a + b)*10)>>GPADC_CALI_ZOOM_SHIFT;

        // Vbat voltage calibration
        volt = GPADC_InternalSearchBATVoltageOffset(volt);
    }
    //恢复GPADC
    GPADC_ANA->GPADC_CTRL = gpadc_ctrl;
    GPADC_DIG->GPADC_CLK_CTRL = gpadc_clk_ctrl;
    GPADC_DIG->GPADC_ACTION = gpadc_action;
    GPADC_ANA->GPADC_EN = gpadc_cmd;
    // out of range detect
    if(volt < 0) volt = 0;
    if(volt > (GPADC_DIV1_VOLT_MAX*(div_sel + 1))) volt = GPADC_DIV1_VOLT_MAX*(div_sel + 1);
    // return
    g_gpadc_vbat = volt;
}

#define GPADC_STBL_VBAT_LO      17976
#define GPADC_STBL_VBAT_HI      33336
#define GPADC_STBL_COL_NUM      16


static uint16_t GPADC_InternalSearchVoltageOffset(uint16_t volt, uint16_t div_sel)
{
    int16_t col_lo, col_hi, row_lo, row_hi, rem;
    int16_t ofst, ofst_hi, ofst_lo;
    int16_t * stbl;
    //uint16_t volt_max[] = {0, 17408, 26624, 36864};
    uint16_t volt_min[] = {0, 2048,  13312, 23552};
    uint16_t volt_row[] = {0, 15,    13,    10}; // minus 1
    // search table column select
    col_lo = (g_gpadc_vbat - GPADC_STBL_VBAT_LO) >> 10;
    col_hi = col_lo + 1;
    if (g_gpadc_vbat <= GPADC_STBL_VBAT_LO)
    {
        col_lo = 0;
        col_hi = 0;
    }
    else if (g_gpadc_vbat >= GPADC_STBL_VBAT_HI)
    {
        col_lo = 15;
        col_hi = 15;
    }
    // search table row select
    row_lo = (volt - volt_min[div_sel]) >> 10;
    row_hi = row_lo + 1;
    if (row_lo < 0)
    {
        row_lo = 0;
        row_hi = row_lo;
    }
    else if (row_lo >= volt_row[div_sel])
    {
        row_lo = volt_row[div_sel];
        row_hi = row_lo;
    }
    // column search
    stbl = (int16_t *)(gpadc_ofst_array[div_sel]);
    rem = (g_gpadc_vbat - GPADC_STBL_VBAT_LO) & 0x3ff;
    ofst_lo = stbl[row_lo*GPADC_STBL_COL_NUM + col_lo] + (((stbl[row_lo*GPADC_STBL_COL_NUM + col_hi] - stbl[row_lo*GPADC_STBL_COL_NUM + col_lo])*rem)>>10);
    ofst_hi = stbl[row_hi*GPADC_STBL_COL_NUM + col_lo] + (((stbl[row_hi*GPADC_STBL_COL_NUM + col_hi] - stbl[row_hi*GPADC_STBL_COL_NUM + col_lo])*rem)>>10);
    // row search
    rem = volt & 0x3ff;
    ofst = ofst_lo + (((ofst_hi - ofst_lo)*rem)>>10);
#ifdef GPADC_DEBUG_PRINT
    printf("volt:%d, vbat:%d, offset:%d\n", volt, g_gpadc_vbat, ofst);
    printf("col_lo:%d, row_lo:%d \n", col_lo, row_lo);
    printf("col_hi:%d, row_hi:%d \n", col_hi, row_hi);
#endif
    volt += ofst;
    return volt;
}

static uint16_t GPADC_InternalSearchBATVoltageOffset(uint16_t vbat)
{
    int16_t offset;
    int i = (vbat - GPADC_STBL_VBAT_LO) >> 10;
#ifdef GPADC_DEBUG_PRINT
    printf("vbat:%d, i:%d\n", vbat, i);
#endif
    if(vbat < GPADC_STBL_VBAT_LO){
        offset = gpadc_batvoltoffset[0];
    }
    else if(vbat >= GPADC_STBL_VBAT_HI){
        offset = gpadc_batvoltoffset[15];
    }
    else{
        int remainder = (vbat - GPADC_STBL_VBAT_LO) & 0x3ff;
        offset = gpadc_batvoltoffset[i] + (((gpadc_batvoltoffset[i+1] - gpadc_batvoltoffset[i]) * remainder) >> 10);
    }
#ifdef GPADC_DEBUG_PRINT
    printf("vbat:%d, offset:%d\n", vbat, offset);
#endif
    vbat = vbat + offset;
    return vbat;
}

void GPADC_LDOInit(void)
{
    uint32_t reg_val;
    REG_GPADC_LDO_EN |= 0x303; // ldo_en=1
    reg_val = REG_GPADC_LDO_VC;
    reg_val &= ~(0x1f << 8);
    reg_val |= (0x06 << 8);
    REG_GPADC_LDO_VC = reg_val; // adda vc 0.95V
}

uint32_t GPADC_GetRin(void)
{
    return GPADC_OTP_INTERNAL_RESISTANCE;
}
uint32_t GPADC_LionConv(uint32_t rdiv, uint32_t volt)
{
    unsigned long long Rin = GPADC_GetRin();
    uint32_t Vbattery = volt * (Rin + rdiv) / Rin;
    return Vbattery;  
}
