/**
 * @file ds18b20.c
 * @author zlg (zenglg@tuya.com)
 * @brief 
 * @version 0.1
 * @date 2021-09-02
 * 
 * @copyright Copyright (c) 2021  涂鸦科技 www.tuya.comm
 * 
 */
#include "uni_log.h"
#include "tuya_hal_system.h"

#include "tuya_th_drv_adapt.h"

STATIC UCHAR_T ds18b20_sda_pin = 0;

#define DS18B20_SDA_PIN ds18b20_sda_pin

#define DS18B20_SDA_UP_IN() \
    tuya_gpio_inout_set_select(DS18B20_SDA_PIN, TRUE, FALSE)

#define DS18B20_SDA_DOWN_IN() \
    tuya_gpio_inout_set_select(DS18B20_SDA_PIN, TRUE, TRUE)

#define DS18B20_SDA_UP_OUT() \
    tuya_gpio_inout_set_select(DS18B20_SDA_PIN, FALSE, FALSE)

#define DS18B20_SDA_DOWN_OUT() \
    tuya_gpio_inout_set_select(DS18B20_SDA_PIN, FALSE, TRUE)

#define DS18B20_READ_SDA() \
    tuya_gpio_read(DS18B20_SDA_PIN)

#define DS18B20_SDA_OUT_H() \
    tuya_gpio_write(DS18B20_SDA_PIN, TRUE)

#define DS18B20_SDA_OUT_L() \
    tuya_gpio_write(DS18B20_SDA_PIN, FALSE)


/**
 * @brief 计算ds18b20的crc8校验
 *        ds18b20是逆序crc8，和常规crc8不太一样
 *        x8 + x5 + x4 + 1
 *        多项式：31 crc初始值：0  计算结果异或值：0
 * @param {src} 要计算的buf
 * @param {size} buf大小
 * @return UINT8_T 
 */
UINT8_T __ds18b20_crc8_cal(CONST UINT8_T *buf, UINT8_T len)
{
    CONST UINT8_T *p = NULL;
    UINT8_T crc_val = 0;
    UINT8_T tmp_val = 0;
    UINT8_T i = 0;

    if ((NULL == buf) || (0 == len)) {
        return 0;
    }

    p = buf;
    while(len--) {
        tmp_val = *p++;
        for (i = 0; i < 8; ++i) {
            if ((crc_val ^ tmp_val) & 0x01) {
                crc_val ^= 0x18;
                crc_val >>= 1;
                crc_val |= 0x80;
            } else {
                crc_val >>= 1;
            }
            tmp_val >>= 1;
        }
    }

    return crc_val;
}
/**
 * @brief __18b20_delay_us
 *        最小值1时，约为3us, 10us约15us,200us约280us;倍数1.5左右
 * @return VOID_T
 */
STATIC VOID_T __18b20_delay_us(UINT_T us_cnt)
{
    volatile UINT_T i = 0;

    UINT_T time_cnt = us_cnt * 7;
    while (i < time_cnt) {
        i++;
    };
}

/**
 * @brief 18b20 power init
 * @return
 */
STATIC OPERATE_RET __ds18b20_reset(VOID_T)
{
    UINT8_T data = 0;
    UINT8_T retry_cnt = 0;

    DS18B20_SDA_UP_OUT();
    DS18B20_SDA_OUT_L();
    __18b20_delay_us(600); // 复位至少480us,最大960us
    DS18B20_SDA_UP_IN();
    __18b20_delay_us(10); //15us, 60us~240us,从机响应

RETRY_READ_RESET:
    data = DS18B20_READ_SDA();
    if (data && (++retry_cnt < 10)) {
        __18b20_delay_us(10);
        goto RETRY_READ_RESET;
    }
    DS18B20_SDA_UP_OUT();
    if (data) {
        return OPRT_COM_ERROR;
    } else {
        return OPRT_OK;
    }
}

/**
 * @brief __ds18b20_write_single_byte
 * @return
 */
STATIC VOID_T __ds18b20_write_byte(UINT8_T data)
{
    UINT8_T i = 0;
    UINT8_T tmp = 0;

    for (i = 0; i < 8; i++) {
        tmp = data & 0x01;
        data >>= 1;
        if (tmp) {
            DS18B20_SDA_OUT_L();
            __18b20_delay_us(2); // 拉低约10us，从机15us~45us检测状态
            DS18B20_SDA_OUT_H();
            __18b20_delay_us(55); //等待约60us，写周期至少60us,但不超过120us
        } else {
            DS18B20_SDA_OUT_L();
            __18b20_delay_us(55);
            DS18B20_SDA_OUT_H();
            __18b20_delay_us(2);
        }
    }
    DS18B20_SDA_OUT_H();
}
/**
 * @brief __ds18b20_read_byte
 * @return
 */
STATIC UINT8_T __ds18b20_read_byte(VOID_T)
{
    UINT8_T i = 0;
    UINT8_T data = 0;

    for (i = 0; i < 8; i++) {
        data >>=1;
        DS18B20_SDA_DOWN_OUT();
        DS18B20_SDA_UP_IN();
        if (DS18B20_READ_SDA()) {
            data |= 0x80;
        }
        __18b20_delay_us(55);
    }
    DS18B20_SDA_UP_OUT();

    return data;
}
/**
 * @brief ds18b20_read_measure_value
 * @param temperature
 * @return
 */
OPERATE_RET ds18b20_read_measure_value(FLOAT_T *temperature, FLOAT_T *humidity)
{
    if (NULL == temperature) {
        return OPRT_INVALID_PARM;
    }

    OPERATE_RET rt = OPRT_OK;
    UINT8_T buf[9];
    UINT8_T i = 0;
    UINT8_T cal_crc = 0;
    memset(buf, 0x00, SIZEOF(buf));

    if (humidity) {
        *humidity = INVALID_MEASURE_VALUE;
    }

    rt = __ds18b20_reset();
    if (OPRT_OK != rt) {
        PR_ERR("ds18b20 reset fail");
        return rt;
    }
    tuya_hal_system_sleep(10);// 等待复位结束

    __ds18b20_write_byte(0xcc);// 跳过匹配64地址
    __ds18b20_write_byte(0x44);// 开启转换
    tuya_hal_system_sleep(750); // 等待转换完成,12位采样精度，大概需要750ms

    rt = __ds18b20_reset();
    if (OPRT_OK != rt) {
        PR_ERR("ds18b20 reset fail");
        return rt;
    }
    tuya_hal_system_sleep(10);// 等待复位结束

    __ds18b20_write_byte(0xcc);// 跳过匹配64地址
    __ds18b20_write_byte(0xbe);// 读取转换结果
    for(i = 0; i < 9; ++i) {
        buf[i] = __ds18b20_read_byte();
    }

    cal_crc = __ds18b20_crc8_cal(buf, 8);
    if( cal_crc != buf[8]) {
        return OPRT_COM_ERROR;
    }

    INT16_T tmp = (INT16_T)(((buf[1] & 0x00ff) << 8) | buf[0]);

    *temperature = tmp * 0.0625;

    if ((*temperature < -55.0f) || (*temperature > 125.0f)) {
        return OPRT_COM_ERROR;
    }

    return OPRT_OK;
}
/**
 * @brief ds18b20产测
 * @return
 */
OPERATE_RET ds18b20_fac_test(VOID_T)
{
    UINT8_T pt_cnt = 0;
    OPERATE_RET ret = OPRT_COM_ERROR;
    FLOAT_T pt_temperature = 0.0f;
    FLOAT_T pt_humidity = 0.0f;

    do {
        tuya_hal_system_sleep(500);
        ret = ds18b20_read_measure_value(&pt_temperature, &pt_humidity);
        if (OPRT_OK != ret) {
            continue;
        }
        if ((pt_temperature > T_FAC_TEST_MIN) && (pt_temperature < T_FAC_TEST_MAX)) {
            PR_NOTICE("ds18b20 fac test ok, temperature:%0.1f", pt_temperature);
            return OPRT_OK;
        }
    } while (++pt_cnt < 5);

    PR_NOTICE("ds18b20 fac test fail, temperature:%0.1f", pt_temperature);
    return OPRT_COM_ERROR;
}
/**
 * @brief ds18b20_init
 * @param pin 
 * @return VOID_T 
 */
OPERATE_RET ds18b20_init(UCHAR_T pin)
{
    ds18b20_sda_pin = pin;

    tuya_gpio_inout_set(DS18B20_SDA_PIN, FALSE);
    DS18B20_SDA_OUT_H();
    tuya_hal_system_sleep(200);

    return OPRT_OK;
}
