第3-9讲:手势识别(基于PAJ7620U2)
-
- 学习目的
- 了解IK-PAJ7620U2手势识别传感器模块的功能。
- 掌握IK-PAJ7620U2的I2C协议、操作流程,并编程实现配置IK-PAJ7620U2工作于接近检测和手势识别模式以及读取检测结果。
- PAJ7620手势识别模块
- 产品型号
- PAJ7620手势识别模块
IK-PAJ7620U2手势识别模块型号命名如下,其中IK是艾克姆科技的公司缩写,PAJ7620U2是该模块使用的手势识别芯片的型号。
图1:产品型号
-
-
- 产品描述
-
艾克姆科技IK-PAJ7620手势识别模块是一款采用原相科技(Pixart)公司的PAJ7620U2芯片的高性能手势识别模块。PAJ7620U2支持上移、下移、左移、右移、前进、后退,顺时针转,逆时针转、摇摆9种手势识别和接近检测。PAJ7620U2广泛应用于智能家居、机器人交互、手势玩具、体感游戏装备等。
图2:IK-PAJ7620手势识别模块
IK-PAJ7620手势识别模块通过I2C接口访问,最大通信速率为400kbps,模块通过5芯单排针将信号引出,同时,考虑到模块对不同电源系统的兼容性,模块硬件上设计了电平转换电路,供电和IO电平均兼容3.3V和5V,可直接用于3.3V电源系统和5V电源系统的单片机,这极大地方便了用户在不同的电源系统中使用该模块,而无需自己做信号转换。
- 规格参数
表1:IK-PAJ7620U2手势识别模块规格参数
参数 | 规格 |
工作电压 | 3.3V/5V。 |
通信接口 | I2C,最大通信速率:400 kbps。 |
硬件接口 | 5芯单排针,针间距2.54mm。 |
识别距离 | 5cm~15cm。 |
环境光免疫力 | <100k Lux。 |
尺寸 | 20.32mm×19.81mm。 |
重量 | 约16.4g。 |
工作温度 | -20°C至+ 70°C。 |
- 模块尺寸
IK-PAJ7620U2手势识别模块尺寸如下图所示,模块对外接口为针间距为2.54mm的5芯的单排针,模块上设计了2个孔径为2mm的固定孔,以便于用户安装固定。
图3:IK-PAJ7620U2手势识别模块尺寸
- 模块引脚定义
IK-PAJ7620U2手势识别模块通过排针接入电源和信号,针脚信号定义如下表所示。
图4:IK-PAJ7620U2手势识别模块引脚描述
- 注:I2C的SCL和SDA信号上已经在模块上加了上拉电阻,用户无需自己再加上拉电阻。
- 典型应用电路
IK-PAJ7620手势识别模块和单片机之间的典型应用电路如下图所示。
图5:IK-PAJ7620U2手势识别模块典型应用电路
-
- 软件设计
- 手势识别实验
- 软件设计
- 注:本节的实验是在“实验2-6-1:串口1数据收发实验”的基础上修改,本节对应的实验源码是:“实验3-9-1:手势识别实验(基于PAJ7620U2)”。
-
-
- 实验内容
-
-
通过开发板上的按键控制IK-PAJ7620手势识别模块工作于接近检测或手势识别模式,并通过串口输出检测结果。
-
-
-
- 代码编写
-
-
- 定义引脚
本例中,我们用P5.0和P5.1分别用于I2C的SDA和SCA信号引脚,定义如下。
代码清单:定义用于I2C通信的引脚
- sbit SDA = P5^0; //定义I2C数据信号引脚SDA
- sbit SCL = P5^1; //定义I2C时钟信号引脚SCL
- 确定PAJ7620的I2C地址
PAJ7620的7位I2C地址为0x73,对应的8位I2C读写地址分别是:
- I2C写地址:0xE6。
- I2C读地址:0xE7。
程序中定义PAJ7620的I2C地址如下。
代码清单:I2C地址
- #define PAJ7620_ADDR_W 0x46 //I2C从机写地址
- #define PAJ7620_ADDR_R 0x47 //I2C从机读地址
- 编写PAJ7620U2 I2C协议实现代码
PAJ7620U2 I2C协议包含“Wake-up”、“Single Write”、“Single Read”和“Burst Read”4种。
- 唤醒(Wake-up)
图6:Wake-up协议
Wake-up协议的代码清单如下:
代码清单:PAJ7620U2唤醒
- /**************************************************************************************
- * 功 能 : 唤醒PAJ7620U2:命令格式:S(起始条件) + (I2C ADDRESS + W) + P(停止条件)
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- void gs_wakeup(void)
- {
- I2C_Start(); //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_W); //发送器件地址(写)
- I2C_Stop(); //发送停止命令,产生停止条件
- }
- Single Write:写单个寄存器
图7:Single Write协议
Single Write协议的代码清单如下:
代码清单:写单个寄存器
- /**************************************************************************************
- * 功 能 : 向指定的寄存器写入一个字节数据
- * 参 数 : regAddr[in]:寄存器地址
- * : regDat[in]:写入的数据
- * 返回值 : GS_SUCCESS:写寄存器成功,GS_ERR:写寄存器出错
- **************************************************************************************/
- u8 gs_write_byte(u8 regAddr,u8 regDat)
- {
- if(!I2C_Start()) return GS_ERR; //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_W); //发送器件地址(写)
- if(!I2C_RecvACK()){I2C_Stop(); return GS_ERR;} //接收ACK
- I2C_SendData(regAddr); //发送寄存器地址
- I2C_RecvACK(); //接收ACK
- I2C_SendData(regDat); //发送写入的数据
- I2C_RecvACK(); //接收ACK
- I2C_Stop(); //发送停止命令,产生停止条件
- return GS_SUCCESS;
- }
- Single Read:读取单个寄存器的数据。
图8:Single Read协议
Single Read协议的代码清单如下:
代码清单:读单个寄存器
- /**************************************************************************************
- * 功 能 : 从指定寄存器读取一个字节数据
- * 参 数 : regAddr[in]:寄存器地址
- * 返回值 : 读取的数据,如果出错会进入错误处理程序
- **************************************************************************************/
- u8 gs_read_byte(u8 regAddr)
- {
- u8 dat;
- if(!I2C_Start()) return GS_ERR; //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_W); //发送器件地址(写)
- if(!I2C_RecvACK()){I2C_Stop(); return GS_ERR;} //接收ACK
- I2C_SendData(regAddr); //发送寄存器地址
- I2C_RecvACK(); //接收ACK
- I2C_Start(); //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_R); //发送器件地址(读)
- I2C_RecvACK(); //接收ACK
- dat = I2C_RecvData(); //读取一个字节数据
- I2C_SendNAK(); //发送NAK
- I2C_Stop(); //发送停止命令,产生停止条件
- return dat;
- }
- Burst Read:从给定的地址开始,连续读取数据,读取过程中,地址自动递增。
图9:Burst Read协议
Burst Read协议的代码清单如下:
代码清单:连续读取
- /**************************************************************************************
- * 功 能 : 从给定的起始地址开始连续读取给定长度的数据
- * 参 数 : regAddr[in]:寄存器地址
- * : *buf[out]:指向保存读出数据的缓存
- * : len[in]:读出的数据长度(字节数)
- * 返回值 : GS_SUCCESS:操作成功,如果出错会进入错误处理程序
- **************************************************************************************/
- u8 gs_read_bytes(u8 regAddr,u8 *buf,u16 len)
- {
- if(!I2C_Start()) return GS_ERR; //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_W); //发送器件地址(写)
- if(!I2C_RecvACK()){I2C_Stop(); return GS_ERR;} //接收ACK
- I2C_SendData(regAddr); //发送寄存器地址
- I2C_RecvACK(); //接收ACK
- if(!I2C_Start()) return GS_ERR; //发送起始命令,产生起始条件
- I2C_SendData(PAJ7620_ADDR_R); //发送器件地址(读)
- I2C_RecvACK(); //接收ACK
- while(len) //读取数据
- {
- if(len==1)
- {
- *(buf+1) = I2C_RecvData();
- I2C_SendNAK();
- }
- else
- {
- *buf = I2C_RecvData();
- I2C_SendACK();
- }
- buf++;
- len--;
- }
- I2C_Stop(); //发送停止命令,产生停止条件
- return GS_SUCCESS; //返回操作成功
- }
- 初始化PAJ7620
PAJ7620使用之前需要先进行初始化配置,首次上电启动后步骤如下:
- 写入从机ID或I2C读取命令唤醒PAJ7620。建议读取Reg_0x00,当唤醒完成时,它将返回“0x20”,以此确认唤醒成功。
- 写入手势识别初始配置:将数据手册提供的配置数组中的数据写入到配置寄存器。
对应的代码清单如下:
代码清单:初始化PAJ7620U2
- /**************************************************************************************
- * 功 能 : 初始化PAJ7620U2
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- u8 paj7620u2_init(void)
- {
- u8 i;
- gs_wakeup(); //唤醒PAJ7620U2
- delay_ms(5); //唤醒时间>700us
- gs_wakeup(); //唤醒PAJ7620U2
- delay_ms(5); //唤醒时间>700us
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);//进入BANK0寄存器区域
- if(gs_read_byte(0x00) != 0x20) //唤醒PAJ7620U2
- {
- return GS_WAKEUP_ERR; //唤醒失败
- }
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);//进入BANK0寄存器区域
- for(i=0;i<GS_INIT_ARRAY_SIZE;i++)
- {
- //写入上电初始化配置
- gs_write_byte(initial_register_array[i][0],initial_register_array[i][1]);
- }
- //写入初始化配置数据时会进入BANK1,这里再切换回BANK0寄存器区域
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);
- return GS_SUCCESS;
- }
- 更改为PS(接近检测)模式、读取物体亮度和物体大小
PAJ7620更改为PS模式、进行接近检测的步骤如下:
- 写入接近检测配置:将数据手册提供的接近检测配置数组中的数据写入到配置寄存器。
- 读取Bank_0_Reg_0xB0获取物体亮度,读取Bank_0_Rg_0xB1和Bank_0_Rg_0xB2获取物体大小。
对应的代码清单如下:
代码清单:接近检测初始化
- /**************************************************************************************
- * 功 能 : 初始化接近检测
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- void ps_init(void)
- {
- u8 i;
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);//进入BANK0寄存器区域
- for(i=0;i<GS_PROXIM_ARRAY_SIZE;i++)
- {
- gs_write_byte(proximity_arry[i][0],proximity_arry[i][1]);//写入接近检测初始配置
- }
- //写入初始化配置数据时会进入BANK1,这里再切换回BANK0寄存器区域
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);
- }
读取接近检测结果的代码清单如下:
代码清单:读取接近检测结果
- /**************************************************************************************
- * 功 能 : 读取接近检测结果
- * 参 数 : p_brightness[out]:物体亮度;p_size[out]:物体大小
- * 返回值 : GS_SUCCESS:读取成功
- **************************************************************************************/
- u8 get_ps_result(u16 *p_brightness, u16 *p_size)
- {
- u8 read_buf[2];
- u8 temp;
- temp = gs_read_byte(PAJ_OBJECT_BRIGHTNESS); //读取物体亮度
- *p_brightness = (u16)temp;
- read_buf[0] = gs_read_byte(PAJ_OBJECT_SIZE_1);//读取物体大小
- read_buf[1] = gs_read_byte(PAJ_OBJECT_SIZE_2);
- *p_size = ((u16)read_buf[1] & 0x0f)<<8 | read_buf[0];
- return GS_SUCCESS;
- }
- 更改为GS(手势识别)模式、读取手势识别结果
PAJ7620更改为GS模式、进行手势识别的步骤如下:
- 写入接近检测配置:将数据手册提供的手势识别配置数组中的数据写入到配置寄存器。
- 根据中断引脚状态或者定时读取Bank_0_Reg_0x43/0x44以获取手势结果,I2C读取完成时手势结果寄存器将清空。
对应的代码清单如下:
代码清单:手势识别初始化
- /**************************************************************************************
- * 功 能 : 写入手势识别初始化配置
- * 参 数 : 无
- * 返回值 : 无
- **************************************************************************************/
- void gesture_init(void)
- {
- u8 i;
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);//进入BANK0寄存器区域
- for(i=0; i<GS_GESTURE_ARRAY_SIZE; i++)
- {
- gs_write_byte(gesture_arry[i][0],gesture_arry[i][1]);//写入手势识别初始配置
- }
- //写入初始化配置数据时会进入BANK1,这里再切换回BANK0寄存器区域
- gs_write_byte(PAJ_REGITER_BANK_SEL,PAJ_BANK0);
- }
读取手势识别结果的代码清单如下:
代码清单:读取手势识别结果
- /***************************************************************************************
- * 功 能 : 读取手势识别结果
- * 参 数 : 无
- * 返回值 : 手势识别结果
- ***************************************************************************************/
- u8 get_gesture_result(u16 *p_gesture)
- {
- u8 status;
- u8 read_buf[2];
- status = gs_read_bytes(PAJ_INT_FLAG1,read_buf,2);//读取手势状态
- if(!status)
- {
- *p_gesture = (u16)(read_buf[1]<<8) | read_buf[0];
- return GS_SUCCESS;
- }
- else return GS_GESTURE_ERR;
- }
- 主函数
主函数中完成相关的初始化,之后,在主循环中扫描按键状态,根据按键动作执行相应动作。并通过串口输出测量结果。
- 按下KEY1按键:系统进入接近检测模式,并将检测结果通过串口输出。
- 按下KEY2按键:如果当前正在执行接近检测,再次按动KEY2按键进入手势识别,否则,直接进入手势识别模式,当识别到有效的手势后,通过串口输出识别结果。
- 按下KEY3按键:停止手势识别/接近检测。
代码清单:主函数
- /**************************************************************************
- 功能描述:主函数
- 参 数:无
- 返 回 值:int类型
- **************************************************************************/
- int main(void)
- {
- u8 button_num;
- u16 gesture_result;
- u16 obj_brightness;
- u16 obj_size;
- P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口(指示灯D1和D2)
- P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD)
- P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD)
- P3M1 &= 0x3F; P3M0 &= 0x3F; //设置P3.6~P3.7为准双向口(按键KEY2和KEY1)
- P0M1 &= 0x5F; P0M0 &= 0x5F; //设置P0.5,P0.7为准双向口(按键KEY4和KEY3)
- I2C_init();
- uart1_init(); //串口1初始化
- EA = 1; //使能总中断
- delay_ms(10); //初始化后延时
- //初始化PAJ7620U2
- if(paj7620u2_init() != GS_SUCCESS)
- {
- while(1)
- {
- printf("PAJ7620Y2 wakeup err!\r\n"); //串口输出错误信息
- delay_ms(1000);
- }
- }
- while(1)
- {
- button_num = buttons_scan(0); //获取开发板用户按键检测值,不支持连按
- if(button_num == BUTTON1_PRESSED) //按键KEY1按下
- {
- ps_init(); //初始化接近检测
- leds_off(); //熄灭指示灯
- printf("PS test started\r\n"); //串口打印提示信息
- while(1)
- {
- if(get_ps_result(&obj_brightness,&obj_size) == GS_SUCCESS)
- {
- printf("obj_brightness: %d ",obj_brightness);
- printf("obj_size: %d\r\n",obj_size);
- }
- delay_ms(80); //软件延时80ms
- led_toggle(LED_1); //翻转指示灯D1状态
- button_num = buttons_scan(0);
- //如果案件S3按下,停止检测
- if((button_num == BUTTON2_PRESSED) || (button_num == BUTTON3_PRESSED))break;
- }
- }
- else if(button_num == BUTTON2_PRESSED) //按键KEY2按下
- {
- gesture_init(); //手势识别初始化
- leds_off(); //熄灭指示灯
- printf("Gesture test started\r\n");
- while(1)
- {
- if(get_gesture_result(&gesture_result) == GS_SUCCESS)//获取手势识别结果
- {
- switch(gesture_result)
- {
- case GESTURE_UP:
- printf("Up\r\n"); break; //向上
- case GESTURE_DOWM:
- printf("Dowm\r\n"); break; //向下
- case GESTURE_LEFT:
- printf("Left\r\n"); break; //向左
- case GESTURE_RIGHT:
- printf("Right\r\n"); break; //向右
- case GESTURE_FORWARD:
- printf("Forward\r\n"); break; //向前
- case GESTURE_BACKWARD:
- printf("Backward\r\n"); break; //向后
- case GESTURE_CLOCKWISE:
- printf("Clockwise\r\n"); break; //顺时针
- case GESTURE_COUNT_CLOCKWISE:
- printf("AntiClockwise\r\n"); break; //逆时针
- case GESTURE_WAVE:
- printf("Wave\r\n"); break; //挥动
- default:
- break;
- }
- }
- delay_ms(80); //软件延时80ms
- led_toggle(LED_2); //翻转指示灯D2状态
- button_num = buttons_scan(0);
- if((button_num == BUTTON1_PRESSED) || (button_num == BUTTON3_PRESSED))
- {
- break; //如果按键S3按下,停止检测
- }
- }
- }
- }
- }
-
-
- 硬件连接
-
-
如下图所示,将IK-PAJ7620手势识别传感器安装J22插座上。
图10:硬件连接
-
-
-
- 实验步骤
-
-
- 解压“…\第3部分:配套例程源码”目录下的压缩文件“实验3-9-1:手势识别实验(基于PAJ7620)”,将解压后得到的文件夹拷贝到合适的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。
- 双击“…\GS_PAJ7620\project”目录下的工程文件“GS_PAJ7620.uvproj”。
- 点击编译按钮编译工程,编译成功后生成的HEX文件“GS_PAJ7620.hex”位于工程的“…\GS_PAJ7620\Project\Object”目录下。
- 打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。
- 电脑上打开串口调试助手,选择开发板对应的串口号,将波特率设置为9600bps。
- 程序运行后,分别按下KEY1~ KEY3:
- 按下KEY1按键:系统进入接近检测模式,用手掌靠近传感器开窗部分,串口调试助手中可以实时观察到目标物体亮度和大小数值,如下图所示。
图11:接近检测测量结果
- 按下KEY2按键:如果当前正在执行接近检测,再次按动KEY2按键进入手势识别,否则,直接进入手势识别模式,识别到正确的手势后,串口调试助手中会观察到手势信息,如下图所示。
图12:手势识别测试结果
- 按下KEY3按键:停止手势识别/接近检测。