单片机 APDS9960 IIC通讯 手势传感器 4种手势

/* -----------------------------------------头文件-----------------------------------------*/
#include "main.h"
/* -----------------------------------------宏定义-----------------------------------------*/
/* Acceptable parameters for setMode */
#define POWER                   0
#define AMBIENT_LIGHT           1
#define PROXIMITY               2
#define WAIT                    3
#define AMBIENT_LIGHT_INT       4
#define PROXIMITY_INT           5
#define GESTURE                 6
#define ALL                     7
/* Default values */
#define DEFAULT_ATIME           219     // 103ms
#define DEFAULT_WTIME           246     // 27ms
#define DEFAULT_PROX_PPULSE     0x87    // 16us, 8 pulses
#define DEFAULT_GESTURE_PPULSE  0x89    // 16us, 10 pulses
#define DEFAULT_POFFSET_UR      0       // 0 offset
#define DEFAULT_POFFSET_DL      0       // 0 offset      
#define DEFAULT_CONFIG1         0x60    // No 12x wait (WTIME) factor
#define DEFAULT_LDRIVE          LED_DRIVE_100MA
#define DEFAULT_PGAIN           PGAIN_8X
#define DEFAULT_AGAIN           AGAIN_8X
#define DEFAULT_PILT            0       // Low proximity threshold
#define DEFAULT_PIHT            50      // High proximity threshold
#define DEFAULT_AILT            0xFFFF  // Force interrupt for calibration
#define DEFAULT_AIHT            0
#define DEFAULT_PERS            0x11    // 2 consecutive prox or ALS for int.
#define DEFAULT_CONFIG2         0x01    // No saturation interrupts or LED boost  
#define DEFAULT_CONFIG3         0       // Enable all photodiodes, no SAI
#define DEFAULT_GPENTH          40      // Threshold for entering gesture mode
#define DEFAULT_GEXTH           30      // Threshold for exiting gesture mode    
#define DEFAULT_GCONF1          0x40    // 4 gesture events for int., 1 for exit
#define DEFAULT_GGAIN           GGAIN_8X
#define DEFAULT_GLDRIVE         LED_DRIVE_100MA
#define DEFAULT_GWTIME          GWTIME_39_2MS
#define DEFAULT_GOFFSET         0       // No offset scaling for gesture mode
#define DEFAULT_GPULSE          0xC9    // 32us, 10 pulses
#define DEFAULT_GCONF3          0       // All photodiodes active during gesture
#define DEFAULT_GIEN            0       // Disable gesture interrupts


/* -----------------------------------------结构体定义-------------------------------------*/
code IIC_TYPE APDS9960 = {2, 0x39 << 1};
xdata gesture_data_type gesture_data = {0};
xdata uint8_t fifo_data[128] = {0};
/* -----------------------------------------全局变量定义-----------------------------------*/
int gesture_ud_delta;
int gesture_lr_delta;
int gesture_ud_count;
int gesture_lr_count;
int gesture_near_count;
int gesture_far_count;
int gesture_state;
int gesture_motion;
/* -----------------------------------------应用程序---------------------------------------*/
/**
 * @brief Resets all the parameters in the gesture data member
 */
void resetGestureParameters(void)
{
    gesture_data.index = 0;
    gesture_data.total_gestures = 0;

    gesture_ud_delta = 0;
    gesture_lr_delta = 0;

    gesture_ud_count = 0;
    gesture_lr_count = 0;

    gesture_near_count = 0;
    gesture_far_count = 0;

    gesture_state = 0;
    gesture_motion = DIR_NONE;
}
/*********************************************
函数名:APDS9960_Write_DATA
功  能:写 地址 数据
形  参:addr 地址   dat 数据
返回值:
备  注:
作  者:薛建强
时  间:2020/03/19
**********************************************/
void APDS9960_Write_DATA(const uint8_t addr, const uint8_t DATA)
{
    IIC_Start();                 // 启动
    IIC_WriteByte(APDS9960.ADDR); // 设备地址
    IIC_Wait_ACK();              // 等待数据响应
    IIC_WriteByte(addr);         // 发送寄存器地址
    IIC_Wait_ACK();              // 等待数据响应
    IIC_WriteByte(DATA);         // 发送数据
    IIC_Wait_ACK();              // 等待数据响应
    IIC_Stop();                  // 停止
}
/*********************************************
函数名:APDS9960_Read_DATA
功  能:读地址数据
形  参:addr--寄存器地址
返回值:
备  注:
作  者:薛建强
时  间:2020/03/19
**********************************************/
uint8_t APDS9960_Read_DATA(const uint8_t addr)
{
    uint8_t GET_DATA = 0;
    IIC_Start();                   // 启动
    IIC_WriteByte(APDS9960.ADDR);   // 发送地址
    IIC_Wait_ACK();                // 等待数据响应
    IIC_WriteByte(addr);           // 发送地址
    IIC_Wait_ACK();                // 等待数据响应

    IIC_Start();                   // 启动
    IIC_WriteByte(APDS9960.ADDR | 1); // 发送地址
    IIC_Wait_ACK();                // 等待数据响应

    GET_DATA = IIC_ReadByte();     // 获取数据
    IIC_Wait_ACK();                // 等待数据响应

    IIC_Stop();                    // 停止
    return GET_DATA;
}
/*********************************************
函数名:APDS9960_get_data
功  能:读取一次寄存器值
形  参:
返回值:
备  注:
作  者:薛建强
时  间:2020/03/19
**********************************************/
uint16_t APDS9960_get_data(const uint8_t addr, uint8_t *val, uint16_t len)
{
    uint16_t i = 0;
    IIC_Start();                     // 启动
    IIC_WriteByte(APDS9960.ADDR);    // 发送地址
    IIC_Wait_ACK();                  // 等待数据响应
    IIC_WriteByte(addr);             // 发送地址
    IIC_Wait_ACK();                  // 等待数据响应
    IIC_Start();                     // 启动
    IIC_WriteByte(APDS9960.ADDR | 1); // 发送地址
    IIC_Wait_ACK();                  // 等待数据响应
    while (len)
    {
        if (len == 1)
        {
            val[i] =  IIC_ReceiveData(0);
        }
        else
        {
            val[i] =  IIC_ReceiveData(1);
        }
        i++;
        len--;
    }
    IIC_Stop();            // 停止
    return i;
}
/*********************************************
函数名:APDS9960_Get_Mode
功  能:读取并返回启用寄存器的内容
形  参:
返回值:
备  注:启用寄存器的内容。0xFF如果出错。
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
uint8_t APDS9960_Get_Mode()
{
    /* 读取当前使能寄存器 */
    return APDS9960_Read_DATA(APDS9960_ENABLE);
}

/*********************************************
函数名:APDS9960_Set_Mode
功  能:设置器件使能寄存器
形  参:
返回值:0--失败  1--成功
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
uint8_t APDS9960_Set_Mode(uint8_t mode, uint8_t enable)
{
    uint8_t reg_val;
    reg_val = APDS9960_Get_Mode();
    if (reg_val == ERROR)
    {
        return 0;
    }
    /* 更改启用寄存器中的位 */
    enable = enable & 0x01;
    if ((mode >= 0) && (mode <= 6)) //使能或失能某个位
    {
        if (enable) //使能
        {
            reg_val |= (1 << mode);
        }
        else //失能
        {
            reg_val &= ~(1 << mode);
        }
    }
    else if (mode == ALL)  //使能全部
    {
        if (enable)
        {
            reg_val = 0x7F;//0x80=0x7F   全部使能
        }
        else //全部使能
        {
            reg_val = 0x00;//0x80=0x00
        }
    }

    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_ENABLE, reg_val);
    return 1;
}
/*********************************************
函数名:APDS9960_Set_Proximity_Gain
功  能:设置接近检测的接收器增益
形  参:
 * Value    Gain
 *   0       1x
 *   1       2x
 *   2       4x
 *   3       8x
返回值:如果操作成功,则为True。否则就错了。
备  注:drive值(0-3)
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_Proximity_Gain(uint8_t drive)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_CONTROL);
    /* 将寄存器中的位设置为给定值 */
    drive &= 00000011;
    drive = drive << 2;
    val &= 11110011;
    val |= drive;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_CONTROL, val);
}
/*********************************************
函数名:APDS9960_Set_GestureGain
功  能:设置手势增益
形  参:
返回值:
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_GestureGain(uint8_t gain)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GCONF2);
    /* 将寄存器中的位设置为给定值 */
    gain &= 00000011;//取gain最低两位
    gain = gain << 5;//移动到6:5
    val &= 10011111;//将6:5位清零
    val |= gain;//将gain的6:5位赋值给val
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_GCONF2, val);
}

/*********************************************
函数名:APDS9960_Set_Gesture_LEDDrive
功  能:在手势模式下设置LED驱动电流
形  参:
 * Value    LED Current
 *   0        100 mA
 *   1         50 mA
 *   2         25 mA
 *   3         12.5 mA
返回值:
备  注:驱动LED驱动电流的值

作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_Gesture_LEDDrive(uint8_t drive)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GCONF2);
    /* 将寄存器中的位设置为给定值 */
    drive &= 00000011;
    drive = drive << 3;//bit 4:3
    val &= 11100111;
    val |= drive;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_GCONF2, val);
}

/*********************************************
函数名:APDS9960_Set_Gesture_WaitTime
功  能:设置手势检测之间的低功耗模式时间
形  参:
 * Value    Wait time
 *   0          0 ms
 *   1          2.8 ms
 *   2          5.6 ms
 *   3          8.4 ms
 *   4         14.0 ms
 *   5         22.4 ms
 *   6         30.8 ms
 *   7         39.2 ms
返回值:如果操作成功,则为True。否则就错了。
备  注:等待时间的值
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_Gesture_WaitTime(uint8_t time)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GCONF2);
    /* 将寄存器中的位设置为给定值 */
    time &= 00000111;
    val &= 11111000;
    val |= time;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_GCONF2, val);
}

/*********************************************
函数名:APDS9960_Set_Gesture_IntEnable
功  能:打开或关闭与手势相关的中断
形  参:1--启用中断,0--禁用中断

返回值:如果操作成功,则为True。否则就错了。
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_Gesture_IntEnable(uint8_t enable)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GCONF4);
    /* 将寄存器中的位设置为给定值 */
    enable &= 00000001;
    enable = enable << 1;
    val &= 11111101;
    val |= enable;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_GCONF4, val);
}

/*********************************************
函数名:APDS9960_Init
功  能:手势传感器初始化
形  参:
返回值:0--初始化失败  1--初始化成功
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
uint8_t APDS9960_Init(void)
{
    uint8_t status;
    IIC_Init();          // I2C初始化
    status = APDS9960_Read_DATA(APDS9960_ID);//读取器件ID
    if (!(status == APDS9960_ID_1 || status == APDS9960_ID_2))
    {
        return 0;
    }

    /* 失能使能寄存器0x80 = 0x00 */
    if (!APDS9960_Set_Mode(ALL, OFF))  //(7,0)
    {
        return 0;
    }
    //设置手势接近进入(手势状态机)阀值为0xA0 = 40
    /* 设置手势传感器寄存器默认值 */
    APDS9960_Write_DATA(APDS9960_GPENTH, DEFAULT_GPENTH);

    //设置手势接近退出(手势状态机)阀值为0xA1 = 30
    APDS9960_Write_DATA(APDS9960_GEXTH, DEFAULT_GEXTH);

    //设置配置寄存器1 0xA2 = 0x40
    //1.在4个数据集被添加到FIFO里后产生中断
    //2.All UDLR 探测数据被包含到集合中
    //3.手势退出持久性.当连续的手势结束发生称为比GEXPERS大于或等于的值时,
    //  手势状态机退出(第1个手势结束发生导致手势状态机退出)
    APDS9960_Write_DATA(APDS9960_GCONF1, DEFAULT_GCONF1);

    //设置手势增益 设置配置寄存器2 0xA3 的 bit 6:5 = 10  8x增益
    APDS9960_Set_GestureGain(DEFAULT_GGAIN);

    //设置配置寄存器2 0xA3 的 bit 4:3 = 00  100ma
    APDS9960_Set_Gesture_LEDDrive(DEFAULT_GLDRIVE);

    //设定配置寄存器2 0xA3 的 bit 2:0=001   39.2ms
    APDS9960_Set_Gesture_WaitTime(DEFAULT_GWTIME);

    //设置手势UP偏移寄存器 0xA4 = 0 没有偏移
    APDS9960_Write_DATA(APDS9960_GOFFSET_U, DEFAULT_GOFFSET);

    //设置手势DOWN偏移寄存器 0xA5 = 0 没有偏移
    APDS9960_Write_DATA(APDS9960_GOFFSET_D, DEFAULT_GOFFSET);

    //设置手势LEFT偏移寄存器 0xA7 = 0 没有偏移
    APDS9960_Write_DATA(APDS9960_GOFFSET_L, DEFAULT_GOFFSET);

    //设置手势RIGHT偏移寄存器 0xA9 = 0 没有偏移
    APDS9960_Write_DATA(APDS9960_GOFFSET_R, DEFAULT_GOFFSET);

    //设置收势脉冲数和脉宽寄存器0xA6 = 0xC9   32us, 10 pulses
    APDS9960_Write_DATA(APDS9960_GPULSE, DEFAULT_GPULSE);

    //设置配置寄存器3  0xAA 的bit 1:0 = 00  所有光电二极管在手势期间均有效
    APDS9960_Write_DATA(APDS9960_GCONF3, DEFAULT_GCONF3);


    //设置配置寄存器4 0xAB 的bit1 = 0 关闭手势中断 GIEN=0
    APDS9960_Set_Gesture_IntEnable(DEFAULT_GIEN);
    return 1;
}


/*********************************************
函数名:APDS9960_Set_LED_Boost
功  能:设置LED当前的升压值
形  参:
 * Value  Boost Current
 *   0        100%
 *   1        150%
 *   2        200%
 *   3        300%
返回值:如果操作成功,则为True。否则就错了。
备  注:驱动电流提升值(0-3)(100-300%)

作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_LED_Boost(uint8_t boost)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_CONFIG2);
    /* 将寄存器中的位设置为给定值 */
    boost &= 00000011;
    boost = boost << 4;
    val &= 11001111;
    val |= boost;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_CONFIG2, val);
}
/*********************************************
函数名:APDS9960_Set_Gesture_mode
功  能:告诉状态机进入或退出手势状态机
形  参:0--关闭  1--开启
返回值:如果操作成功,则为True。否则就错了。
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
void APDS9960_Set_Gesture_mode(uint8_t mode)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GCONF4);
    /* 将寄存器中的位设置为给定值 */
    mode &= 00000001;
    val &= 11111110;
    val |= mode;
    /* 将值回写入使能寄存器中 */
    APDS9960_Write_DATA(APDS9960_GCONF4, val);
}

void APDS9960_Gesture_EN(uint8_t interrupts)
{
    /* 启用手势模式
       将“启用”设置为0(关闭电源)
       将WTIME设置为0xFF
       将AUX设置为LED增强
       启用PON、WEN、PEN、GEN-in启用
    */
    resetGestureParameters();//复位手势变量=0

    //设置等待时间寄存器0x83 = 0xFF (WLONG=1  0.03s)   (WLONG=0   2.78ms)
    APDS9960_Write_DATA(APDS9960_WTIME, 0xFF);

    //设置接近脉冲计数寄存器 0x8E = 0x89 16us, 10 pulses
    APDS9960_Write_DATA(APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE);

    //设置接近脉冲计数寄存器 0x8E = 0x89 16us, 10 pulses
    APDS9960_Write_DATA(APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE);

    //设置配置寄存器2 0x90的bit5:4=11  %300   LED驱动电流
    APDS9960_Set_LED_Boost(LED_BOOST_300);

    //是否开启手势中断配置寄存器4  0xAB
    if (interrupts)
    {
        APDS9960_Set_Gesture_IntEnable(1);
    }
    else
    {
        APDS9960_Set_Gesture_IntEnable(0);
    }

    //设置手势模式GMODE = 1
    APDS9960_Set_Gesture_mode(1);

    //打开APDS-9960  PON = 1  0x80 的 bit0 = 1
    APDS9960_Set_Mode(POWER, 1);

    //WEN = 1   0x80 的 bit3 = 1
    APDS9960_Set_Mode(WAIT, 1);

    //PEN=1   0x80 的 bit2 = 1
    APDS9960_Set_Mode(PROXIMITY, 1);

    //PIEN=1   0x80 的 bit6 = 1
    APDS9960_Set_Mode(GESTURE, 1);

    //设置ADC 积分时间寄存器  0x81 有效距离  [0,255]   0-->15cm   255-->5cm
    APDS9960_Write_DATA(APDS9960_ATIME, 0x0);
    //等待时间寄存器  0x83 
    APDS9960_Write_DATA(APDS9960_WTIME, 0xF0);


APDS9960_Write_DATA(APDS9960_CONTROL, 0x0F);
}

/*********************************************
函数名:APDS9960_Check_Gesture_State
功  能:检测手势数据是否有效
形  参:
返回值:1--有效  0--无效
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
uint8_t APDS9960_Check_Gesture_State(void)
{
    uint8_t val;
    val = APDS9960_Read_DATA(APDS9960_GSTATUS);
    val &= APDS9960_GVALID; //判断0xAF最低位GVALID是否为1
    if (val == 1)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**
 * 处理原始手势数据确定滑动方向
 *
 * @return True if near or far state seen. False otherwise.
 */
uint8_t processGestureData(void)
{
    uint8_t u_first = 0;
    uint8_t d_first = 0;
    uint8_t l_first = 0;
    uint8_t r_first = 0;
    uint8_t u_last = 0;
    uint8_t d_last = 0;
    uint8_t l_last = 0;
    uint8_t r_last = 0;
    int ud_ratio_first;
    int lr_ratio_first;
    int ud_ratio_last;
    int lr_ratio_last;
    int ud_delta;
    int lr_delta;
    int i;

    /* If we have less than 4 total gestures, that's not enough */
    if (gesture_data.total_gestures <= 4)
    {
        return 0;
    }

    /* Check to make sure our data isn't out of bounds */
    if ((gesture_data.total_gestures <= 32) && \
        (gesture_data.total_gestures > 0))
    {

        /* Find the first value in U/D/L/R above the threshold */
        for (i = 0; i < gesture_data.total_gestures; i++)
        {
            if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT))
            {
                u_first = gesture_data.u_data[i];
                d_first = gesture_data.d_data[i];
                l_first = gesture_data.l_data[i];
                r_first = gesture_data.r_data[i];
                break;
            }
        }
        /* If one of the _first values is 0, then there is no good data */
        if ((u_first == 0) || (d_first == 0) || \
            (l_first == 0) || (r_first == 0))
        {
            return 0;
        }
        /* Find the last value in U/D/L/R above the threshold */
        for (i = gesture_data.total_gestures - 1; i >= 0; i--)
        {
            if ((gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) &&
                (gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT))
            {

                u_last = gesture_data.u_data[i];
                d_last = gesture_data.d_data[i];
                l_last = gesture_data.l_data[i];
                r_last = gesture_data.r_data[i];

                break;
            }
        }
    }

    /* Calculate the first vs. last ratio of up/down and left/right */
    ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first);
    lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first);
    ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last);
    lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last);


    /* Determine the difference between the first and last ratios */
    ud_delta = ud_ratio_last - ud_ratio_first;
    lr_delta = lr_ratio_last - lr_ratio_first;


    /* Accumulate the UD and LR delta values */
    gesture_ud_delta += ud_delta;
    gesture_lr_delta += lr_delta;

    /* Determine U/D gesture */
    if (gesture_ud_delta >= GESTURE_SENSITIVITY_1)  //50
    {
        gesture_ud_count = 1;//U-->D
    }
    else if (gesture_ud_delta <= -GESTURE_SENSITIVITY_1)
    {
        gesture_ud_count = -1;//D--->U
    }
    else
    {
        gesture_ud_count = 0;
    }

    /* Determine L/R gesture */
    if (gesture_lr_delta >= GESTURE_SENSITIVITY_1)
    {
        gesture_lr_count = 1;//L--->R
    }
    else if (gesture_lr_delta <= -GESTURE_SENSITIVITY_1)
    {
        gesture_lr_count = -1;//R--->L
    }
    else
    {
        gesture_lr_count = 0;
    }
    /* Determine Near/Far gesture */
    if ((gesture_ud_count == 0) && (gesture_lr_count == 0))
    {
        if ((abs(ud_delta) < GESTURE_SENSITIVITY_2) && (abs(lr_delta) < GESTURE_SENSITIVITY_2))  //20
        {
            if ((ud_delta == 0) && (lr_delta == 0))
            {
                gesture_near_count++;
            }
            else if ((ud_delta != 0) || (lr_delta != 0))
            {
                gesture_far_count++;
            }

            if ((gesture_near_count >= 10) && (gesture_far_count >= 2))
            {
                if ((ud_delta == 0) && (lr_delta == 0))
                {
                    gesture_state = NEAR_STATE;
                }
                else if ((ud_delta != 0) && (lr_delta != 0))
                {
                    gesture_state = FAR_STATE;
                }
                return 1;
            }
        }
    }
    else
    {
        if ((abs((int)ud_delta) < GESTURE_SENSITIVITY_2) && (abs((int)lr_delta) < GESTURE_SENSITIVITY_2))
        {

            if ((ud_delta == 0) && (lr_delta == 0))
            {
                gesture_near_count++;
            }

            if (gesture_near_count >= 10)
            {
                gesture_ud_count = 0;
                gesture_lr_count = 0;
                gesture_ud_delta = 0;
                gesture_lr_delta = 0;
            }
        }
    }
    return 0;
}
/**
 * 确定滑动方向、远近状态
 *
 * @return True if near/far event. False otherwise.
 */
uint8_t decodeGesture(void)
{
    /* Return if near or far event is detected */
    if (gesture_state == NEAR_STATE)  //手势状态 = 进距离
    {
        gesture_motion = DIR_NEAR;
        return 1;
    }
    else if (gesture_state == FAR_STATE)   //手势状态 = 远距离
    {
        gesture_motion = DIR_FAR;
        return 1;
    }

    /* Determine swipe direction 确定滑动方向 */
    if ((gesture_ud_count == -1) && (gesture_lr_count == 0))
    {
        gesture_motion = DIR_UP;
    }
    else if ((gesture_ud_count == 1) && (gesture_lr_count == 0))
    {
        gesture_motion = DIR_DOWN;
    }
    else if ((gesture_ud_count == 0) && (gesture_lr_count == 1))
    {
        gesture_motion = DIR_RIGHT;
    }
    else if ((gesture_ud_count == 0) && (gesture_lr_count == -1))
    {
        gesture_motion = DIR_LEFT;
    }
    else if ((gesture_ud_count == -1) && (gesture_lr_count == 1))
    {
        if (abs(gesture_ud_delta) > abs(gesture_lr_delta))
        {
            gesture_motion = DIR_UP;
        }
        else
        {
            gesture_motion = DIR_RIGHT;
        }
    }
    else if ((gesture_ud_count == 1) && (gesture_lr_count == -1))
    {
        if (abs(gesture_ud_delta) > abs(gesture_lr_delta))
        {
            gesture_motion = DIR_DOWN;
        }
        else
        {
            gesture_motion = DIR_LEFT;
        }
    }
    else if ((gesture_ud_count == -1) && (gesture_lr_count == -1))
    {
        if (abs(gesture_ud_delta) > abs(gesture_lr_delta))
        {
            gesture_motion = DIR_UP;
        }
        else
        {
            gesture_motion = DIR_LEFT;
        }
    }
    else if ((gesture_ud_count == 1) && (gesture_lr_count == 1))
    {
        if (abs(gesture_ud_delta) > abs(gesture_lr_delta))
        {
            gesture_motion = DIR_DOWN;
        }
        else
        {
            gesture_motion = DIR_RIGHT;
        }
    }
    else
    {
        return 0;
    }
    return 1;
}


/*********************************************
函数名:APDS9960_Gesture_Get_State
功  能:得到手势状态
形  参:
返回值:
备  注:
作  者:薛建强
时  间:2020/03/19
使  用:
**********************************************/
int APDS9960_Gesture_Get_State(void)
{
    uint8_t fifo_level = 0;
    char bytes_read = 0;

    uint8_t gstatus;
    int i;


    /* 确保电源和手势打开且数据有效 */
    if (!APDS9960_Check_Gesture_State() || !(APDS9960_Get_Mode() & 01000001))
    {
        return DIR_NONE;
    }

    gstatus = APDS9960_Read_DATA(APDS9960_GSTATUS);//手势状态寄存器 3--溢出  1--有效
    if ((gstatus & APDS9960_GVALID) == APDS9960_GVALID)//手势数据有效
    {
        fifo_level = APDS9960_Read_DATA(APDS9960_GFLVL);//fifo中数据的数量
        if (fifo_level > 0) //如果FIFO里有东西,把它读到我们的数据块里
        {
            bytes_read = APDS9960_get_data(APDS9960_GFIFO_U, (uint8_t *)fifo_data, (fifo_level * 4));
        }
        /* 如果至少有一组数据,则将数据分类为U/D/L/R */
        if (bytes_read >= 4)
        {
            for (i = 0; i < bytes_read; i += 4)
            {
                gesture_data.u_data[gesture_data.index] = fifo_data[i + 0];
                gesture_data.d_data[gesture_data.index] = fifo_data[i + 1];
                gesture_data.l_data[gesture_data.index] = fifo_data[i + 2];
                gesture_data.r_data[gesture_data.index] = fifo_data[i + 3];
                gesture_data.index++;
                gesture_data.total_gestures++;
            }
            /* 过滤和处理手势数据。解码近/远状态 */
            if (processGestureData())
            {
                if (decodeGesture())
                {
                    //***TODO: U-Turn Gestures
                }
            }
            /* Reset data */
            gesture_data.index = 0;
            gesture_data.total_gestures = 0;
        }
    }
    return 0;
}

#ifndef _APDS9960_H
#define _APDS9960_H
/* -----------------------------------------宏定义-----------------------------------------*/
/* APDS-9960 I2C address */
#define APDS9960_I2C_ADDR       0x39	//I2C地址

/* Gesture parameters */
#define GESTURE_THRESHOLD_OUT   10	 //输出阀值
#define GESTURE_SENSITIVITY_1   50   //灵敏度1
#define GESTURE_SENSITIVITY_2   20	 //灵敏度2   远近敏感度  越大越敏感

/* Error code for returned values */
#define ERROR                   0xFF 

/* Acceptable device IDs */
#define APDS9960_ID_1           0xAB
#define APDS9960_ID_2           0x9C 

/* Misc parameters */
#define FIFO_PAUSE_TIME         30      // Wait period (ms) between FIFO reads

/* APDS-9960 register addresses */
/*0x80	使能寄存器
7 保留
6 GEN:手势使能
5 PIEN:接近中断使能
4 AIEN:环境光感中断使能
3 WEN:等待使能
2 PEN:接近检测使能
1 AEN:环境光检测使能
0 PON:上电使能
*/
#define APDS9960_ENABLE         0x80	





/*0x81  ADC积分时间寄存器
7:0	  ATIME 
字段值			  周期		       时间		      最大值	
0                 256              712ms           65535
182                72              200ms           65535
256-TIME/2.78      ...             ...             ...
219                37              103ms           37889
246                10              27.8ms          10241
255                1               2.78ms           1025
*/
#define APDS9960_ATIME          0x81




/*0x83  等待时间寄存器
7:0  WTIME
字段值			  等待时间		   时间(WLONG=0)	   时间(WLONG=1)	  	
0                 256              712ms               8.54s             
256-TIME/2.78      ...             ...                 ...
171                85              236ms               2.84s
255                1               2.78ms              0.03s
*/
#define APDS9960_WTIME          0x83




/*0x84 -- 0x87  环境光中断阀值寄存器
0x84  AILTL     low byte low interrupt threshold
0x85  AILTH	  	high byte low interrupt threshold
0x86  AIHTL		low byte high  interrupt threshold  
0x87  AIHTH		high byte high  interrupt threshold
*/
#define APDS9960_AILTL          0x84
#define APDS9960_AILTH          0x85
#define APDS9960_AIHTL          0x86
#define APDS9960_AIHTH          0x87




/*0x89、0x8B	接近中断阀值寄存器
0x89	PILT	低中断阀值
0x8B	PIHT	高中断阀值
*/
#define APDS9960_PILT           0x89
#define APDS9960_PIHT           0x8B



/*0x8C	持续寄存器
PPERS   7:4   	 接近中断持续,控制主进程接近中断的速率
		字段值   当下列情况发生时接近中断产生
		0        每个接近周期
		1		 任何在阀值范围外的接近值
		2		 2个连续的在阀值范围外的接近值
		...		 ...
APERS   3:0		 环境光感应中断持续,控制主进程环境光感应中断的速率
		字段值	 当下列情况发生时环境光中断产生
		0        每个接近周期
		1		 任何在阀值范围外的环境光感应值
		2		 2个连续的在阀值范围外的环境光感应值
		3        3...
		4        5...
		5        10...
		6        15...
		7        20...
		8        25...
		9        30...
		10       35...
		11       40...
		...      ...
		15       60
*/
#define APDS9960_PERS           0x8C




/*0x8D  配置寄存器1  上电时被设为0x40
7    保留   写0
6    保留   写1
5	 保留   写1
4-2	 保留	写0
1    WLONG	 WaitLong 长时间等待   12x 设置值
0	 保留
*/
#define APDS9960_CONFIG1        0x8D




/*0x8E  接近脉冲计数寄存器    
PPLEN  7:6   接近脉宽
		0      	4us
		1		8us(default)
		2		16us
		3		32us
PPULSE  5:0   接近脉冲计数
		0		1
		1		2
		2		3
		...		...
		63		64
*/
#define APDS9960_PPULSE         0x8E





/*0x8F	控制寄存器1
LDRIVE	7:6	  LED驱动强度
		0		100ma
		1		50ma
		2		25ma
		3		12.5ma
保留	5:4		写0
PGAIN	3:2		接近增益控制
		0		1x
		1		2x
		2		4x
		3		8x
AGAIN	1:0		环境光感应及颜色增益
		0		1x
		1		4x
		2		16x
		3		64x
*/
#define APDS9960_CONTROL        0x8F




/*0x90   配置寄存器2
PSIEN		7	  接近饱和中断使能
				  0 接近饱和中断失能
				  1	接近饱和中断使能
CPSIEN		6	  清除光电二极管饱和中断(ALS饱和中断)使能
				  0 环境光感饱和中断失能
				  1 环境光感饱和中断使能
LED_BOOST	5:4   增加LDR当前接近和手势LED脉冲,当前值由LDRIVE设置,增长由LDE_BOOST
				  0  100%
				  1	 150%
				  2  200%
				  3  300%
保留		3:1   写0
保留		0     写1,在POR时默认被设为高
*/			
#define APDS9960_CONFIG2        0x90





/*0x92   ID寄存器(只读)
ID			7:0		唯一标识的器件ID
					0xAB = APDS-9960
*/
#define APDS9960_ID             0x92




/*0x93    状态寄存器(只读)在上电时被设为0x04
CPSAT		7	    清除光电二极管饱和,这个位会被还原当发送清除通道中断命令(0xE6 CICLEAR)
					或者使能ADC位(AEN=0),这个位会出发中断如果CPSIEN被设置
PGSAT		6		显示模拟饱和事件在前面的手势或接近周期,一旦被设置,这个位将一直被保持
                    直到被接近中断特殊的功能命令(0xE5 PICLEAR)清除或者使能接近(PEN=0)这个位
					引发一次中断如果PSIEN被设置
PINT		5		接近中断,这个位触发一次中断如果PIEN在使能中被设置
AINT		4		环境光感中断,这个位触发一次中断如果AIEN在使能中被设置
保留		3		不作操作
GINT		2		手势中断,GFVLV变成比GFIFOTH大或GVALID被置位(GMODE发送0时)被设置,这个位
                    被复位当FIFO完全为空时(read)
PVALID		1		有效接近值.完整显示接近周期自从PEN被设置或PDATA被最后读,读一次PDATA将自动清除PVALID
AVALID		0		有效环境光感.完整显示环境光感周期当AEN被设置或在ALS/Color数据寄存器其中任一被读出
*/
#define APDS9960_STATUS         0x93




/*0x94--0x9B   RGBC数据寄存器
CDATAL	0x94	7:0		clear通道低字节
CDATAH	0x95	7:0		clear通道高字节
RDATAL	0x96	7:0		red通道低字节
RDATAH	0x97	7:0		red通道高字节
GDATAL	0x98	7:0		green通道低字节
GDATAH	0x99	7:0		green通道高字节
BDATAL 	0x9A	7:0		blue通道低字节
BDATAH	0x9B	7:0		blue通道高字节
*/
#define APDS9960_CDATAL         0x94
#define APDS9960_CDATAH         0x95
#define APDS9960_RDATAL         0x96
#define APDS9960_RDATAH         0x97
#define APDS9960_GDATAL         0x98
#define APDS9960_GDATAH         0x99
#define APDS9960_BDATAL         0x9A
#define APDS9960_BDATAH         0x9B




/*0x9C	接近数据寄存器
PDATA	 7:0	接近数据
*/
#define APDS9960_PDATA          0x9C




/*0x9D	接近偏移 UP和RIGHT
POFFSET_UR	7:0		字段值			偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_POFFSET_UR     0x9D





/*0x9E	接近偏移 DOWN和LEFT
POFFSET_DL	7:0		字段值			偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_POFFSET_DL     0x9E




/*0x9F		配置寄存器3 
保留		7:6		写0
PCMP		5		接近增益补偿使能.这个位提供增益,当接近光电二极管信号由于遮挡二减少时.如果
					只有一个二极管(在二极管对中)有效,那么仅仅只有一般的信号在ADC中是有效的,这个
					结果ADC最大值是127.使PCMP额外的增益2X,将会使最大的ADC值为255
					PLMASK_X(U.D.L.R)		PCMP
					0,1,1,1					1
					1,0,1,1					1
					1,1,0,1					1
					1,1,1,0					1
					0,1,0,1					1
					1,0,1,0					1
					All Other				0
SAI			4		中断后进入睡眠.被使能后当发生中断时自动进入低功耗模式,并且器件状态会转换到SAI决策块
					正常的操作是当中断引脚被I2C清零时被恢复
PMASK_U		3		接近遮挡UP使能	写1失能此光电二极管
PMASK_D		2		接近遮挡DOWN使能	写1失能此光电二极管
PMASK_L		1		接近遮挡LEFT使能	写1失能此光电二极管
PMASK_R		0		接近遮挡RIGHT使能 写1失能此光电二极管
*/
#define APDS9960_CONFIG3        0x9F




/*0xA0	手势接近进入阀值寄存器 
GPENTH		7:0		接近手势输入阀值.这个寄存器设定接近阀值用作决定手势开始,并且同时进入
					手势状态机(bit4必须设为0)
*/
#define APDS9960_GPENTH         0xA0




/*0xA1   手势退出阀值寄存器
GEXTH		7:0		手势退出阀值.此寄存器设置阀值决定手势结束,同时退出手势状态机.
					设置GTHR_OUT为0x00会防止手势退出直到GMODE被设为0
*/
#define APDS9960_GEXTH          0xA1




/*0xA2	手势配置寄存器1
GFIFOTH		7:6		手势FIFO阀值.这个值与FIFO  level比较产生一个中断
					0		在1个数据集被添加到FIFO里后产生中断
					1		4
					2		8
					3		16
GEXMSK		5:2	    手势退出遮挡.控制哪个手势探测光电二极管被包含到决定手势结束并且同时退出手势状态机
			0000	All UDLR 探测数据被包含到集合中
			0001	R不被包含到集合中
			0010	L
			0100	D
			1000	U
			0101	...
			0110	L D
			1111	UDLR
GEXPERS		1:0		手势退出持久性.当连续的手势结束发生称为比GEXPERS大于或等于的值时,手势状态机退出
			0		第1个手势结束发生导致手势状态机退出
			1		2
			2		4
			3		7
*/
#define APDS9960_GCONF1         0xA2




/*0xA3   手势配置寄存器2
保留		7		写0
GGAIN		6:5		手势增益控制,设定一个增益手势接收在手势模式下
			0		1x
			1		2x
			2		4x
			3		8x
GLDRIVE		4:3		手势LED驱动强度
			0		100ma
			1		50ma
			2		25ma
			3		12.5ma
GWTIME		2:0		手势等待时间	
			0		0ms
			1		2.8ms
			2		5.6ms	
			3		8.4ms
			4		14.0ms
			5		22.4ms
			6		30.8ms
			7		39.2ms
*/
#define APDS9960_GCONF2         0xA3




/*0xA4    手势UP偏移寄存器
GOFFSET_U	7:0		字段值				偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_GOFFSET_U      0xA4





/*0xA5    手势DOWN偏移寄存器
GOFFSET_D	7:0		字段值				偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_GOFFSET_D      0xA5





/*0xA7    手势LEFT偏移寄存器
GOFFSET_L	7:0		字段值				偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_GOFFSET_L      0xA7






/*0xA9    手势RIGHT偏移寄存器
GOFFSET_R	7:0		字段值				偏移校正因子
					01111111			127
					...					...
					00000001			1
					00000000			0
					10000001			-1
					...					...
					11111111			-127
*/
#define APDS9960_GOFFSET_R      0xA9




/*0xA6		手势脉冲数和脉宽寄存器
GPLEN		7:6		手势脉宽.设定LED_ON脉宽在手势LDR脉冲期间
			0		4us
			1		8us(default)
			2		16us
			3		32us
GPULSE		5:0		手势脉冲数.特定的脉冲数由LDR产生
			0		1
			1		2
			2		3
			...		...
			63		64
*/
#define APDS9960_GPULSE         0xA6




/*0xAA	手势配置寄存器3
保留		7:2		写0
GDIMS		1:0		选择哪种手势光电二极管在对手势时能够手机结果
			0		两对都有效.UP-DOWN  LEFT-RIGHT FIFO数据有效
			1		UP-DOWN有效		LEFT-RIGHT不用管
			2		LEFT-RIGHT		UP-DOWN
			3		同0
*/
#define APDS9960_GCONF3         0xAA





/*0xAB	手势配置寄存器4
保留		7:3		写0
GFIFO_CLR	2		设1清除GFIFO,GINT,GVALID,GFIFO_OV,GFIFO_LVL
GIEN		1		手势中断使能	
GMODE		0		手势模式	
*/
#define APDS9960_GCONF4         0xAB





/*0xAE	手势FIFO Level寄存器
GFLVL		7:0		一个四字节数据等同于一个GFLVL数
*/
#define APDS9960_GFLVL          0xAE




/*0xAF	手势状态寄存器
保留		7:2		不作操作
GFOV		1		手势FIFO溢出
GVALID		0		手势FIFO数据
*/
#define APDS9960_GSTATUS        0xAF





/*0xE4--0xE7	清除中断寄存器
IFORCE		0xE4	7:0		强制中断
PICLEAR		0xE5	7:0		接近中断清除
CICLEAR		0xE6	7:0		环境光感中断清除
AICLEAR		0xE7	7:0		清除非手势中断
*/
#define APDS9960_IFORCE         0xE4
#define APDS9960_PICLEAR        0xE5
#define APDS9960_CICLEAR        0xE6
#define APDS9960_AICLEAR        0xE7




/*0xFC--0xFF	手势FIFO寄存器
GFIFO_U		0xFC	7:0		手势FIFO-UP 值
GFIFO_D		0xFD	7:0		手势FIFO-DOWN值
GFIFO_L		0xFE	7:0	手势FIFO-LEFT值
GFIFO_R		0xFF	7:0		手势FIFO-RIGHT值
*/
#define APDS9960_GFIFO_U        0xFC
#define APDS9960_GFIFO_D        0xFD
#define APDS9960_GFIFO_L        0xFE
#define APDS9960_GFIFO_R        0xFF



/* Bit fields */
#define APDS9960_PON            00000001//Power on enable
#define APDS9960_AEN            00000010//ALS enable
#define APDS9960_PEN            00000100//Prox enable
#define APDS9960_WEN            00001000//Wait enable
#define APSD9960_AIEN           00010000//ALS interrupt enable
#define APDS9960_PIEN           00100000//Peox interrupt enable
#define APDS9960_GEN            01000000//Gesture enable
#define APDS9960_GVALID         00000001//Gesture FIFO data

/* On/Off definitions */
#define OFF                     0
#define ON                      1



/* LED Drive values */
#define LED_DRIVE_100MA         0
#define LED_DRIVE_50MA          1
#define LED_DRIVE_25MA          2
#define LED_DRIVE_12_5MA        3

/* Proximity Gain (PGAIN) values */
#define PGAIN_1X                0
#define PGAIN_2X                1
#define PGAIN_4X                2
#define PGAIN_8X                3

/* ALS Gain (AGAIN) values */
#define AGAIN_1X                0
#define AGAIN_4X                1
#define AGAIN_16X               2
#define AGAIN_64X               3

/* Gesture Gain (GGAIN) values */
#define GGAIN_1X                0
#define GGAIN_2X                1
#define GGAIN_4X                2
#define GGAIN_8X                3

/* LED Boost values */
#define LED_BOOST_100           0
#define LED_BOOST_150           1
#define LED_BOOST_200           2
#define LED_BOOST_300           3    

/* Gesture wait time values */
#define GWTIME_0MS              0
#define GWTIME_2_8MS            1
#define GWTIME_5_6MS            2
#define GWTIME_8_4MS            3
#define GWTIME_14_0MS           4
#define GWTIME_22_4MS           5
#define GWTIME_30_8MS           6
#define GWTIME_39_2MS           7

/* Direction definitions */
enum {
  DIR_NONE,
  DIR_LEFT,
  DIR_RIGHT,
  DIR_UP,
  DIR_DOWN,
  DIR_NEAR,
  DIR_FAR,
  DIR_ALL
};

/* State definitions */
enum {
  NA_STATE,
  NEAR_STATE,
  FAR_STATE,
  ALL_STATE
};
/* -----------------------------------------头文件-----------------------------------------*/
#include "main.h"

/* -----------------------------------------结构体定义-------------------------------------*/
/* Container for gesture data */
typedef struct
{
    uint8_t u_data[32];
    uint8_t d_data[32];
    uint8_t l_data[32];
    uint8_t r_data[32];
    uint8_t index;
    uint8_t total_gestures;
    uint8_t in_threshold;
    uint8_t out_threshold;
} gesture_data_type;

extern xdata gesture_data_type gesture_data;
/* -----------------------------------------全局变量定义-----------------------------------*/
extern int gesture_motion;
/* -----------------------------------------应用程序---------------------------------------*/
uint8_t APDS9960_Init(void);
void APDS9960_Gesture_EN(uint8_t interrupts);
uint8_t APDS9960_Check_Gesture_State(void);
int APDS9960_Gesture_Get_State(void);
void resetGestureParameters(void);
uint8_t decodeGesture(void);
#endif

使用例子:

        if (SystemReg.RunningState == 0 && SystemInit.APDS9960 == 0)
        {
            SystemInit.APDS9960 = APDS9960_Init();
            if (SystemInit.APDS9960 == 1)
            {
                APDS9960_Gesture_EN(1);
            }
        }
        if (SystemReg.RunningState == 0 && SystemInit.APDS9960 == 1)
        {
            LowPower_Tmr = 0;
            if (APDS9960_Check_Gesture_State())//手势数据是否有效
            {
                State = APDS9960_Gesture_Get_State();
            }
            else
            {
                /* 确定最佳猜测姿势并清理 */
                decodeGesture();
                if (gesture_motion > 0)
                {
                    switch (gesture_motion)
                    {
                        case DIR_UP:
                            printf("UP\n");
                            break;
                        case DIR_DOWN:
                            printf("DOWN\n");
                            break;
                        case DIR_LEFT:
                            printf("LEFT\n");
                            break;
                        case DIR_RIGHT:
                            printf("RIGHT\n");
                            break;
                        case DIR_NEAR:
                            printf("NEAR\n");
                            break;
                        case DIR_FAR:
                            printf("FAR\n");
                            break;
                        default:
                            printf("NONE:%d\n", (uint16_t)State);
                    }
                }
                resetGestureParameters();
                os_wait(K_IVL, 100, 0);//100ms
            }
        }
        os_wait(K_IVL, 250, 0);//100ms

IIC协议链接

猜你喜欢

转载自blog.csdn.net/qq_29246181/article/details/105487183