STM32 STC15系列单片机的APDS-9960手势识别模块代码实现

一、硬件连接配置

APDS-9960引脚 STC15引脚 功能说明
VCC 3.3V 电源供电
GND GND 地线
SDA P1.0 I2C数据线
SCL P1.1 I2C时钟线
INT INT0 手势中断

二、核心代码实现

1. I2C底层驱动

#include <STC15F2K60S2.h>

#define APDS9960_ADDR 0x39 << 1  // 7位地址左移1位

sbit SDA = P1^0;
sbit SCL = P1^1;

// I2C延时函数
void I2C_Delay() {
    unsigned char i;
    for(i=0; i<100; i++);
}

// 产生起始信号
void I2C_Start() {
    SDA = 1;
    SCL = 1;
    I2C_Delay();
    SDA = 0;
    I2C_Delay();
    SCL = 0;
}

// 产生停止信号
void I2C_Stop() {
    SDA = 0;
    SCL = 1;
    I2C_Delay();
    SDA = 1;
    I2C_Delay();
}

// 发送一个字节
unsigned char I2C_WriteByte(unsigned char dat) {
    unsigned char i;
    for(i=0; i<8; i++) {
        SDA = (dat & 0x80) ? 1 : 0;
        SCL = 1;
        I2C_Delay();
        SCL = 0;
        dat <<= 1;
    }
    SDA = 1;  // 释放SDA线
    SCL = 1;
    i = SDA;  // 读取ACK
    SCL = 0;
    return ~i;  // 返回ACK状态
}

// 读取一个字节
unsigned char I2C_ReadByte(unsigned char ack) {
    unsigned char i, dat=0;
    SDA = 1;
    for(i=0; i<8; i++) {
        SCL = 1;
        I2C_Delay();
        dat <<= 1;
        dat |= SDA;
        SCL = 0;
    }
    if(ack) SDA = 0;  // 发送ACK
    else    SDA = 1;  // 发送NACK
    SCL = 1;
    I2C_Delay();
    SCL = 0;
    return dat;
}

2. APDS-9960驱动

#define GESTURE_NONE 0
#define GESTURE_UP   1
#define GESTURE_DOWN 2
#define GESTURE_LEFT 3
#define GESTURE_RIGHT 4

// 寄存器地址定义
#define APDS9960_ENABLE      0x80
#define APDS9960_ATIME       0x81
#define APDS9960_WTIME       0x83
#define APDS9960_GCONF4      0xAB
#define APDS9960_GPENTH      0xA0
#define APDS9960_GEXTH       0xA1
#define APDS9960_GFIFO_U     0xFC

// 初始化APDS-9960
bit APDS9960_Init() {
    I2C_Start();
    if(I2C_WriteByte(APDS9960_ADDR) != 0) return 0;  // 检测ACK
    I2C_WriteByte(0x00);  // 复位所有寄存器
    I2C_Stop();

    // 基础配置
    APDS9960_WriteReg(APDS9960_ENABLE, 0x00);  // 关闭所有功能
    APDS9960_WriteReg(APDS9960_ATIME, 0xC0);   // 103ms积分时间
    APDS9960_WriteReg(APDS9960_WTIME, 0xF9);   // 27ms等待时间
    
    // 手势配置
    APDS9960_WriteReg(APDS9960_GCONF4, 0x01);  // 4方向手势检测
    APDS9960_WriteReg(APDS9960_GPENTH, 0x28);  // 进入阈值40
    APDS9960_WriteReg(APDS9960_GEXTH, 0x1E);   // 退出阈值30
    APDS9960_WriteReg(APDS9960_GCONF1, 0x40);  // 手势数据FIFO深度
    
    // 使能手势检测
    APDS9960_WriteReg(APDS9960_ENABLE, 0x41);  // 使能手势+电源
    return 1;
}

// 读取手势数据
unsigned char APDS9960_ReadGesture() {
    unsigned char fifo_data[128];
    unsigned char gstatus;
    
    APDS9960_ReadReg(APDS9960_GSTATUS, &gstatus, 1);
    if(!(gstatus & 0x01)) return GESTURE_NONE;  // 无手势
    
    APDS9960_ReadReg(APDS9960_GFLVL, &fifo_len, 1);
    APDS9960_ReadReg(APDS9960_GFIFO_U, fifo_data, fifo_len*4);
    
    // 简单手势判断算法
    int16_t ud_diff = 0, lr_diff = 0;
    for(int i=0; i<fifo_len; i++) {
        ud_diff += fifo_data[i*4] - fifo_data[i*4+1];
        lr_diff += fifo_data[i*4+2] - fifo_data[i*4+3];
    }
    
    if(abs(ud_diff) > abs(lr_diff)) {
        return (ud_diff > 0) ? GESTURE_UP : GESTURE_DOWN;
    } else {
        return (lr_diff > 0) ? GESTURE_RIGHT : GESTURE_LEFT;
    }
}

// 寄存器操作函数
bit APDS9960_WriteReg(unsigned char reg, unsigned char dat) {
    I2C_Start();
    if(I2C_WriteByte(APDS9960_ADDR) != 0) return 0;
    if(I2C_WriteByte(reg) != 0) return 0;
    if(I2C_WriteByte(dat) != 0) return 0;
    I2C_Stop();
    return 1;
}

bit APDS9960_ReadReg(unsigned char reg, unsigned char *dat, unsigned char len) {
    I2C_Start();
    if(I2C_WriteByte(APDS9960_ADDR) != 0) return 0;
    if(I2C_WriteByte(reg) != 0) return 0;
    I2C_Start();
    if(I2C_WriteByte(APDS9960_ADDR|0x01) != 0) return 0;
    while(len--) *dat++ = I2C_ReadByte(0);
    I2C_Stop();
    return 1;
}

三、主程序设计

#include <STC15F2K60S2.h>

// 全局变量
unsigned char gesture;

void main() {
    // 硬件初始化
    P1M1 = 0x00;  // 设置P1口为准双向
    P1M0 = 0x00;
    
    // 初始化APDS-9960
    if(!APDS9960_Init()) {
        while(1);  // 初始化失败
    }
    
    // 配置外部中断
    IT0 = 1;      // 下降沿触发
    EX0 = 1;      // 使能外部中断0
    EA = 1;       // 总中断使能
    
    while(1) {
        // 主循环处理其他任务
        gesture = APDS9960_ReadGesture();
        if(gesture != GESTURE_NONE) {
            // 执行手势对应操作
            switch(gesture) {
                case GESTURE_UP:    // 向上手势处理
                    break;
                case GESTURE_DOWN:  // 向下手势处理
                    break;
                case GESTURE_LEFT:  // 向左手势处理
                    break;
                case GESTURE_RIGHT:// 向右手势处理
                    break;
            }
        }
    }
}

// 外部中断0服务函数
void EX0_ISR() interrupt 0 {
    gesture = APDS9960_ReadGesture();  // 中断读取手势
}

四、关键配置说明

  1. I2C配置: 时钟频率:100kHz(通过I2C_Delay函数控制) 地址模式:7位地址(左移1位) 数据格式:8位数据+1位ACK
  2. 手势检测参数: 积分时间:103ms(ATIME=0xC0) 等待时间:27ms(WTIME=0xF9) 进入阈值:40(GPENTH=0x28) 退出阈值:30(GEXTH=0x1E)
  3. 中断配置: 触发方式:下降沿触发(INT0) 中断优先级:最高优先级

参考代码 APDS9960模块基于STM32 STC15的代码 www.youwenfan.com/contentcnn/73283.html

五、调试与优化建议

  1. 硬件调试要点: 检查3.3V供电稳定性 确认I2C上拉电阻(建议4.7kΩ) 使用逻辑分析仪验证I2C通信波形

  2. 软件优化方向

    // 提高手势识别精度
    #define GESTURE_FILTER_SIZE 5
    static int gesture_buffer[GESTURE_FILTER_SIZE] = {0};
    
    // 滑动平均滤波
    void Gesture_Filter() {
        static int index = 0;
        gesture_buffer[index++] = gesture;
        if(index >= GESTURE_FILTER_SIZE) index = 0;
    
        int sum = 0;
        for(int i=0; i<GESTURE_FILTER_SIZE; i++) {
            sum += gesture_buffer[i];
        }
        gesture = (sum >= GESTURE_FILTER_SIZE/2) ? gesture : GESTURE_NONE;
    }
    
  3. 功耗优化

    // 低功耗模式配置
    void Enter_LowPower() {
        APDS9960_WriteReg(APDS9960_ENABLE, 0x00);  // 关闭所有功能
        PCON |= 0x01;  // 进入空闲模式
    }
    
posted @ 2025-12-09 16:45  yes_go  阅读(1)  评论(0)    收藏  举报