/*
*************************************************************
 *@Copyright (C), 2016-2020 smartlinkMicro. Co., Ltd.  All rights reserved.
 *@Filename:
 *@Complier:    IAR msp430 5.30
 *@Target OS:   NONE
 *@Target HW:   SL1523
 *@Author:      rpliu
 *@Version :    V1.0.0
 *@Date:        2018.12.25
 *@Description: LPCD api code
 *@History:
 *@    <author>     <time>     <version >   <desc>
 *@    rpliu        20181225   V1.0.0       Initial Version
 *@    rpliu        20190110   V1.0.1       add phase auto calibration
 *@    rpliu        20190115   V2.0.0       new lpcd adjust algorithm
 *@    rpliu        20190116   V3.0.0       rewrite lpcd adjust algorithm again
 *@    rpliu        20190117   V3.0.1       little change on lpcd flow
                                            replace SPI operation with macro define
 *@    rpliu        20190117   V3.0.2       replace set_bit_mask and clear_bit_mask
                                            with macro define
 *@    rpliu        20190117   V3.0.3       add timeout for sense adj loop
 *@    rpliu        20190117   V3.0.4       fix a bug in adj_fail assignment
                                            add debug info
 *@    rpliu        20190118   V3.0.5       add a threshold in threshold lut
                                            change the method of expand threshold at the beginning of sense adjust flow
                                            fix a bug for not write to threshold register when succeed
 *@    rpliu        20190311   V3.0.6       adjust threshold calulation
                                            add field strength notification in return value
 *@    rpliu        20190416   V3.0.7       add lpcd_dummy_tr to calib at lpcd_init
                                            remove phase_calib step in lpcd_init
 *@    rpliu        20190416   V3.0.8       use DELAY_1MS macro to wait wakeup
 *@    rpliu        20190507   V3.1.0       solve lpcd_sen_adj fail issue
                                            optimize lpcd_sen_adj time
                                            use status instead of adj_fail in lpcd_sen_adj

*************************************************************
*/

#include <stdio.h>

#include "lpcd.h"
#include "sl2x23.h"
#include "emv_if.h"

#ifdef USE_STDPERIPH_DRIVER
#include "utilities.h"
#include "spi.h"
#endif


lpcd_cfg_t lpcd_cfg;
unsigned char osc_calib() {
    uint8_t val;
    write_reg(0x3f, 0x00);
    write_reg(0x01, 0x00);
    delay_1ms(2);
    write_reg(0x01, 0x06);
    delay_1ms(10);
    write_reg(0x3f, 0x01);
    val = read_reg(0x4e);
    write_reg(0x3f, 0x00);
    if(val & 0x80) {
        return 0;
    } else {
        printf("osc_calib fail\r\n");
        return 1;
    }
}

//unsigned char slm_reg_14, slm_reg_15, slm_reg_28, slm_reg_29, slm_reg_65, slm_reg_66;
void lpcd_init(unsigned char fst_en, unsigned char adc_value) {
    if((fst_en == 0) && (adc_value > AGC_RX_MAX)) {
        fst_en = 1;
//        write_reg(0x3f, 0x1); //
//        write_reg(0x5f,read_reg(0x5f)-1);
//        write_reg(0x3f, 0x0); //
        printf("Warning! adc_value is too large, agc need rejust\r\n");
    }
    if((fst_en == 0) && (adc_value < AGC_RX_MIN)) {
        //   write_reg(0x3f, 0x1); //
        //    write_reg(0x5f,read_reg(0x5f)+1);
        //    write_reg(0x3f, 0x0); //
        fst_en = 1;
        printf("Warning! adc_value is too low, agc need rejust\r\n");
    }
    //if(fst_en) osc_calib();
    write_reg(0x3f, 0x0); // adc_sample time
    write_reg(DivIEnReg, 0xe0); // fst lpcd int en, lpcd_int_en;
    write_reg(ComIrqReg, 0x7f); // clear irq
    write_reg(DivIrqReg, 0x7f); // clear irq
    write_reg(TxASKReg, 0x00)    ;
    write_reg(0x28, 0x3f);
    write_reg(0x29, 0x3f);
    write_reg(0x3f, 0x01);
    write_reg(0x61, 0x10); //set vmid res as min=1.4k, default=3.3K, bqliu 20211124
    write_reg(0x5f, agc_val); // 读卡可以以此值作为参考，并固定该值
    write_reg(0x3f, 0x00);
    if(fst_en) {
        pcd_antenna_on(); //open field
        delay_1ms(5);
        adc_init();
        agc_calib();
    }
    write_reg(0x3f, 0x2); // adc_sample time
    write_reg(LPCD_CTRL0, 0x11 | (fst_en << 5)); //adc_sample_time,0x13->0x11, to turn off vmid(1mA), during spd mode in lpcd, bqliu 20211124
    //write_reg(LPCD_PARAM0, adc_value + lpcd_cfg.sense); // config abs feild strength low threshold
    //write_reg(LPCD_PARAM1, adc_value - lpcd_cfg.sense); // config abs feild strength high threshold
    write_reg(LPCD_PARAM0, adc_value+1);
    write_reg(LPCD_PARAM1, adc_value-1);
    write_reg(LPCD_PARAM2, lpcd_cfg.sense); // config relative feild strength threshold
    write_reg(LPCD_PARAM3, 0x2c); //lpcd_fst_en=1,lpcd_out_inv=1;lpcd_envsel=0;lpcd_en=1;lpcd_work=1;
    // write_reg(LPCD_CTRL2,0x03);//ant_stable_time
    write_reg(LPCD_TIME0, 0x2); //ant_stable_time
    write_reg(LPCD_TIME1, lpcd_cfg.t1); //lpcd_piorid
    write_reg(LPCD_TIME2, 0x2); //start up time
    write_reg(0x3f, 0x0); //
}

// set 0x14/0x15/0x28/0x29/0x65/0x66 and turn off rf field
void lpcd_entry() {
    write_reg(0x01, 0x10);
    #ifndef NOT_IRQ
    #ifdef USE_STDPERIPH_DRIVER
    _ASSERT_SPI_CLK_LOW();
    #endif
    #endif
}

void adc_init() {
    //     write_reg(LPCD_CTRL2,0x80); //adc_sample_time
    write_reg(0x3f, 0x1); //
    clear_bit_mask(0x65, 0x80);
    write_reg(0x66, 0x00); // clkdelay=0s
    write_reg(0x3f, 0x2); //
    write_reg(LPCD_CTRL0, 0x13); //lpcd_out_inv=1,lpcd_envsel=0;lpcd_sample=0;lpcd_en=1;lpcd_work=1
    write_reg(ADC_TIME0, ADC_TIME0_VAL); //adc_sample_time
    write_reg(ADC_TIME1, 0x50); //adc_compare_time
};
unsigned char get_adc_value() {
    write_reg(0x3f, 0x2);
    write_reg(ADC_CTRL, 0x1);
    //delay_10us(8);
    delay_1ms(1);
    return read_reg(ADC_VALUE0);
};

unsigned char get_adc_value_mean(unsigned char n) {
    int i;
    unsigned char adc_value;
    unsigned char mean_adc_value;
    uint16_t adc_value_acc;
    adc_value_acc = 0;
    for(i = 0; i < n; i = i + 1) {
        adc_value = get_adc_value();
        adc_value_acc = adc_value_acc + adc_value;
        #ifdef LPCD_DEBUG
        printf("i=%d: adc_value=%x\r\n", i, adc_value);
        #endif
    };
    mean_adc_value =  adc_value_acc / n;
    return     mean_adc_value;
};


void agc_test(){
    #ifdef LPCD_DEBUG
    unsigned char adc_value;
    unsigned char agc_ctrl = 0xd;
    while(1) {
        write_reg(0x3f, 0x1);
        write_reg(AGC_CTRL, 0x80 + agc_ctrl);
        delay_1ms(1);
        write_reg(0x3f, 0x2);
        adc_value = get_adc_value();
        printf("agc_test agc_ctrl=%x,adc_value=%x\r\n", agc_ctrl, adc_value);
        if(agc_ctrl == 0) break;
        agc_ctrl--;
    }
    write_reg(0x3f, 0x00);
    #endif
}

unsigned char agc_calib() {
    unsigned char adc_value;
    unsigned char agc_ctrl;
    agc_test();
    #if 1
    agc_ctrl = 0xd;
    while(1) {
        write_reg(0x3f, 0x1);
        write_reg(AGC_CTRL, 0x80 + agc_ctrl);
        //delay_10us(50);
        delay_1ms(1);
        write_reg(0x3f, 0x2);
        adc_value = get_adc_value();
        #ifdef LPCD_DEBUG
        printf("agc_ctrl=%x,adc_value=%x\r\n", agc_ctrl, adc_value);
        #endif
        if(adc_value > AGC_TARGET && agc_ctrl > 0x1) {
            agc_ctrl--;
        } else if(adc_value > AGC_RX_MAX && agc_ctrl == 0x0) {
            printf("Warning: The field is overflow! agc can not reduce it!\r\n");
            break;
        } else {
            #ifdef LPCD_DEBUG
            printf("agc ok agc_ctrl=%x,adc_value=%x\r\n", agc_ctrl, adc_value);
            #endif
            break;
        }
    }
    write_reg(0x3f, 0x00);
    printf("[0x28 = %x 0x29 = %x],agc_ctrl=%x,adc_value=%x\r\n", read_reg(0x28),read_reg(0x29),agc_ctrl, adc_value);
    agc_val = 0x80 + agc_ctrl;
    return agc_ctrl;
    #endif
};

void test_agc() {
    unsigned char adc_value, agc_ctrl;
    int i;
    write_reg(0x3f, 0x2);
    adc_init();
    write_reg(0x3f, 0x0);
    write_reg(TxControlReg, 0x83); // open feild
    //delay_10us(500);
    delay_1ms(5);
    write_reg(0x3f, 0x1);
    write_reg(AGC_CTRL, 0x00);
    write_reg(0x3f, 0x2);
    adc_value = get_adc_value();
    printf("agc_ctrl=%x,adc_value=%x\r\n", 0x0, adc_value);
    //delay_10us(100);
    delay_1ms(1);
    #if 1
    for(i = 0; i < 0xe; i = i + 1) {
        agc_ctrl = i;
        write_reg(0x3f, 0x1);
        write_reg(AGC_CTRL, 0x80 + i); //0x00+agc_ctrl);
        //delay_10us(100);
        delay_1ms(1);
        write_reg(0x3f, 0x2);
        adc_value = get_adc_value();
        printf("agc_ctrl=%x,adc_value=%x\r\n", agc_ctrl, adc_value);
    }
    #endif
    write_reg(0x3f, 0x0);
//while(1);
};


void test_lpcd_conf() {
    //unsigned char adc_value;
    int i;
    write_reg(0x3f, 0x0);
    write_reg(TxControlReg, 0x83); // open feild
    //delay_10us(500);
    delay_1ms(5);
    write_reg(0x3f, 0x1);
    write_reg(AGC_CTRL, 0x84);
    write_reg(0x3f, 0x2);
    write_reg(LPCD_CTRL0, 0x37); //adc_sample_time
    for(i = 0; i < 256; i = i + 1) {
        write_reg(LPCD_HYS, i); //adc_compare_time
        //delay_10us(2);
        delay_1ms(1);
    }
    for(i = 256; i > 0; i = i - 1) {
        write_reg(LPCD_HYS, i); //adc_compare_time
        //delay_10us(2);
        delay_1ms(1);
    }
}
// restore 0x14/0x15/0x28/0x29/0x65/0x66
uint8_t lpcd_exit() {
    uint8_t adc_value;
    uint8_t reg_val;
    #ifndef NOT_IRQ
    #ifdef USE_STDPERIPH_DRIVER
    _RELEASE_SPI_CLK_LOW();
    #endif
    #endif
    write_reg(0x01, 0x00);// wait wakeup done
    delay_1ms(1);
    reg_val = read_reg(0x5); //added by xlwang,20220805,for debug
    if(reg_val & 0x60) {
        printf("LPCD_IRQ: irq=%x\r\n", reg_val);
    } else {
        printf("Error: irq=%x, This is not LPCD IRQ\r\n", reg_val);
    }
    write_reg(0x3f, 0x2);
    adc_value = read_reg(ADC_VALUE0);
    printf("after lpcd: read_adc_value=%x\r\n", adc_value);
    write_reg(0x3f, 0x0);
    #ifdef LPCD_DEBUG  //added by xlwang,20220805, for debug
    printf("after_lpcd: 0x2=%x,0x3=%x,0x4=%x,0x5=%x\r\n", read_reg(0x02), read_reg(0x03), read_reg(0x04), read_reg(0x05));
    write_reg(0x3f, 0x1);
    printf("after_lpcd: agc_val=%x\r\n", read_reg(0x5f));
    write_reg(0x3f, 0x0);
    #endif
    return adc_value;
}

void lpcd_print(void) {
    unsigned char reg;
    write_reg(0x3f, 0x00);
    reg = read_reg(0x01);
    printf("read_reg(0x01)= %x\n", reg);
    reg = read_reg(0x15);
    printf("read_reg(0x15)= %x\n", reg);
    reg = read_reg(0x14);
    printf("read_reg(0x14)= %x\n", reg);
    reg = read_reg(0x28);
    printf("read_reg(0x28)= %x\n", reg);
    reg = read_reg(0x29);
    printf("read_reg(0x29)= %x\n", reg);
    write_reg(0x3f, 0x01);
    reg = read_reg(0x65);
    printf("read_reg(0x65)= %x\n", reg);
    reg = read_reg(0x66);
    printf("read_reg(0x66)= %x\n", reg);
    write_reg(0x3f, 0x00);
    reg = read_reg(0x03);
    printf("read_reg(0x03)= %x\n", reg);
}
