一:模块资料介绍:PAJ7620U2 Gesture Sensor是一款基于PAJ7620U2的手势识别模块,可直接识别9种基本手势,支持I2C接口,兼容3.3V/5V电平。这里为了方式调试,我使用的是STM32硬件的IIC驱动代码,也加深了自己对IIC通讯的理解;
模块资料:
电源电压 : 3.3V/5V
工作原理:光学数组式环境亮度传感检测
通讯接口:I²C 接口,400Khz(Max)
识别速度:240Hz
工作环境光:<100K Lux(抗灯光干扰)
有效探测距离:5~15cm
检测角度范围:60°~180°
手势识别种类:内置 9 个手势类型:(上、下、左、右、前、后、顺时针旋转、逆时针旋转、挥动)
功耗:3~10mA(待机功耗电流 15u)
PAJ7620U2传感器的工作原理
我们可以看到PAJ7620U2传感器内部自带 LED 驱动器(可发射红外线信号),内置有传感器感应阵列、目标信息提取阵列和手势识别阵列。另外,PAJ7620U2作为一种光学数组式传感器,其内置LED驱动器集成了环境光和光源抑制滤波器,模块基本不受环境光干扰。
红外LED手势识别原理:传感器工作时通过内部 LED 驱动器,驱动红外 LED 向外发射红外线信号,当传感器阵列在有效的距离中探测到物体时,目标信息提取阵列会对探测目标进行特征原始数据的获取,采集到的数据被保存在寄存器中,同时手势识别阵列会对原始数据进行识别处理,最后将手势结果存到寄存器中。
根据 PAJ7620U2传感器数据手册,用户可通过I²C接口总线采集信号并迅速识别出UP、Down、Right、Left等9种常用手势。另外PAJ7620U2还提供内置的接近检测功能,用于检测物体的接近或离开。
二:学习,了解IIC通讯
2.1:硬件连接方式:I²C的两根线:SCL时钟线和SDA数据线。
SCL时钟线:是为整个通信过程提供了时钟信号
SDA数据线:在每一个周期里发送0或者1,用这些0和1传输数据
软件传输:
数据线SDA以时钟线SCL作为参照。
开始信号:当SCL为高电平时,SDA由高电平向低电平跳变时,开始传输数据。
结束信号:当SCL为高电平时,SDA由低电平向高电平跳变时,停止传输数据。
应答信号:接收数据的IC 在接收完8个bit的数据之后,返回应答ack指令,表示数据已被签收。
三:实际硬件连接
1脚 GND 供电负极
2脚 VCC 供电正极 这里我设计的是5V
3脚 SDA IIC的数据线 PB6
4脚 SCL IIC的时钟线 PB7
电路图:
STM32cube的配置图:
然后配置好之后,点击生成软件代码即可。
查看生成的IIC底层驱动是否正确:
/* USER CODE BEGIN 0 */ /* USER CODE END 0 */ I2C_HandleTypeDef hi2c1; /* I2C1 init function */ void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.Timing = 0x60808CD3; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /** Configure Analogue filter */ if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK) { Error_Handler(); } /** Configure Digital filter */ if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; if(i2cHandle->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER CODE END I2C1_MspInit 0 */ /** Initializes the peripherals clock */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2C1; PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(); } __HAL_RCC_GPIOB_CLK_ENABLE(); /**I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* I2C1 clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); /* USER CODE BEGIN I2C1_MspInit 1 */ /* USER CODE END I2C1_MspInit 1 */ } } void HAL_I2C_MspDeInit(I2C_HandleTypeDef* i2cHandle) { if(i2cHandle->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspDeInit 0 */ /* USER CODE END I2C1_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_I2C1_CLK_DISABLE(); /**I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6); HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7); /* USER CODE BEGIN I2C1_MspDeInit 1 */ /* USER CODE END I2C1_MspDeInit 1 */ } }
确保配置引脚与实际接线保持一致i,这里我设置的IIC的通讯速率是100KHZ;
手势模块驱动函数如下:这里我封装到了一个c文件里面,方便再次移植;
/* GESTURE.c 手势模块的底层驱动部分 功能:硬件IIC读取PAJ7602的当前状态 建立时间:2024.06.26 赵瑞聪 适用CPU:STM32H503RB 编译器:KEIL5.38a 参考资料: 1 https://www.digikey.cn/zh/products/detail/seeed-technology-co-ltd/101020083/5775185 2 https://www.seeedstudio.com/Grove-Gesture-PAJ7620U2.html (文末附件中有主芯片手册) 3 PAJ7620U2手册: https://img.dfrobot.com.cn/wiki/none/b1d7ac88927f5ffedea5a0ecf107188c.pdf 4 PAJ7620U2中文翻译: https://blog.csdn.net/weixin_36590806/article/details/124805530 5 参考驱动代码: https://gitee.com/zhagyuji/stm32f4_paj7620/tree/master/OLED/PAJ7620 6 官方驱动代码库: https://gitee.com/RT-Thread-Mirror/Gesture_PAJ7620 */ #include "Gesture.h" #include "paj7620.h" #include <stdio.h> #include <string.h> #include "stdlib.h" #include <stdarg.h> /*定义蜂鸣器、双路继电器的输出状态控制部分,直接操作对应IO口*/ #define BEEPON HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET); #define BEEPOFF HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); #define JDQ1ON HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_SET); #define JDQ1OFF HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET); #define JDQ2ON HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); #define JDQ2OFF HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); extern I2C_HandleTypeDef hi2c1; GestureData gesture; ProximityData proximity; GestureData gesture_key; //初始化 const unsigned char initRegisterArray[][2] = { // BANK 0 {0xEF, 0x00}, {0x37, 0x07}, {0x38, 0x17}, {0x39, 0x06}, {0x42, 0x01}, {0x46, 0x2D}, {0x47, 0x0F}, {0x48, 0x3C}, {0x49, 0x00}, {0x4A, 0x1E}, {0x4C, 0x20}, {0x51, 0x10}, {0x5E, 0x10}, {0x60, 0x27}, {0x80, 0x42}, {0x81, 0x44}, {0x82, 0x04}, {0x8B, 0x01}, {0x90, 0x06}, {0x95, 0x0A}, {0x96, 0x0C}, {0x97, 0x05}, {0x9A, 0x14}, {0x9C, 0x3F}, {0xA5, 0x19}, {0xCC, 0x19}, {0xCD, 0x0B}, {0xCE, 0x13}, {0xCF, 0x64}, {0xD0, 0x21}, // BANK 1 {0xEF, 0x01}, {0x02, 0x0F}, {0x03, 0x10}, {0x04, 0x02}, {0x25, 0x01}, {0x27, 0x39}, {0x28, 0x7F}, {0x29, 0x08}, {0x3E, 0xFF}, {0x5E, 0x3D}, {0x65, 0x96}, {0x67, 0x97}, {0x69, 0xCD}, {0x6A, 0x01}, {0x6D, 0x2C}, {0x6E, 0x01}, {0x72, 0x01}, {0x73, 0x35}, {0x77, 0x01}, {0xEF, 0x00}, }; #define INIT_REG_ARRAY_SIZE (sizeof(initRegisterArray) / sizeof(initRegisterArray[0])) #define GESTURE_SIZE sizeof(gesture_arry) / 2 //手势识别初始化数组 const unsigned char gesture_arry[][2] = { {0xEF, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0xEF, 0x00}, {0x48, 0x3C}, {0x49, 0x00}, {0x51, 0x10}, {0x83, 0x20}, {0x9F, 0xF9}, {0xEF, 0x01}, {0x01, 0x1E}, {0x02, 0x0F}, {0x03, 0x10}, {0x04, 0x02}, {0x41, 0x40}, {0x43, 0x30}, {0x65, 0x96}, {0x66, 0x00}, {0x67, 0x97}, {0x68, 0x01}, {0x69, 0xCD}, {0x6A, 0x01}, {0x6B, 0xB0}, {0x6C, 0x04}, {0x6D, 0x2C}, {0x6E, 0x01}, {0x74, 0x00}, {0xEF, 0x00}, {0x41, 0xFF}, {0x42, 0x01}, }; #define PROXIM_SIZE sizeof(proximity_arry) / 2 //接近检测初始化数组 const unsigned char proximity_arry[][2] = { {0xEF, 0x00}, {0x41, 0x00}, {0x42, 0x00}, {0x48, 0x3C}, {0x49, 0x00}, {0x51, 0x13}, {0x83, 0x20}, {0x84, 0x20}, {0x85, 0x00}, {0x86, 0x10}, {0x87, 0x00}, {0x88, 0x05}, {0x89, 0x18}, {0x8A, 0x10}, {0x9f, 0xf8}, {0x69, 0x96}, {0x6A, 0x02}, {0xEF, 0x01}, {0x01, 0x1E}, {0x02, 0x0F}, {0x03, 0x10}, {0x04, 0x02}, {0x41, 0x50}, {0x43, 0x34}, {0x65, 0xCE}, {0x66, 0x0B}, {0x67, 0xCE}, {0x68, 0x0B}, {0x69, 0xE9}, {0x6A, 0x05}, {0x6B, 0x50}, {0x6C, 0xC3}, {0x6D, 0x50}, {0x6E, 0xC3}, {0x74, 0x05}, }; /************************************************************************************************** * 函数功能: static unsigned char PAJ7620_send_byte(unsigned char dta) * 输入参数: data * 返 回 值: 1:正常 0:异常 * 说 明: 通过IIC向模块发送一个字节 * ***************************************************************************************************/ static unsigned char PAJ7620_send_byte(unsigned char data) { if (HAL_I2C_Master_Transmit(&hi2c1, PAJ7620_I2C_ADDR, &data, 1, 100) != HAL_OK) { return 0; } return 1; } /************************************************************************************************** * 函数功能: static unsigned char PAJ7620_send_byteS(unsigned char *data, unsigned char length) * 输入参数: data length * 返 回 值: 1:正常 0:异常 * 说 明: 通过IIC向模块发送 长度为length 的数据 * ***************************************************************************************************/ static unsigned char PAJ7620_send_byteS(unsigned char *data, unsigned char length) { if (HAL_I2C_Master_Transmit(&hi2c1, PAJ7620_I2C_ADDR, data, length, 100) != HAL_OK) { return 0; } return 1; } /************************************************************************************************** * 函数功能: static unsigned char PAJ7620_read_byteS(unsigned char *data, unsigned char length) * 输入参数: data length * 返 回 值: 1:正常 0:异常 * 说 明: 通过IIC向模块读取 长度为length 的数据 * ***************************************************************************************************/ static unsigned char PAJ7620_read_byteS(unsigned char *data, unsigned char length) { /* Do the read */ if (HAL_I2C_Master_Receive(&hi2c1, PAJ7620_I2C_ADDR, data, length, 100) != HAL_OK) { return 0; } return 1; } /************************************************************************************************** * 函数功能: static void writeReg(uint8_t addr, uint8_t value) * 输入参数: addr value * 返 回 值: 无 * 说 明: 通过 IIC 向模块的指定寄存器写入数据 * ***************************************************************************************************/ static void writeReg(uint8_t addr, uint8_t value) { unsigned char dta[2] = {0}; dta[0] = addr; dta[1] = value; PAJ7620_send_byteS(dta, 2); } /************************************************************************************************** * 函数功能: static unsigned char readRegs(uint8_t addr, uint8_t *values, int size) * 输入参数: addr values size * 返 回 值: 无 * 说 明: 通过 IIC 向模块的读取 指定寄存器及其该寄存器的之后的数据长度 * ***************************************************************************************************/ static unsigned char readRegs(uint8_t addr, uint8_t *values, int size) { if (PAJ7620_send_byte(addr) != 1) return 0; if (PAJ7620_read_byteS(values, size) != 1) return 0; return 1; } /************************************************************************************************** * 函数功能: static unsigned char readReg(uint8_t addr) * 输入参数: addr * 返 回 值: 无 * 说 明: 通过 IIC 向模块的读取 指定寄存器的数据 * ***************************************************************************************************/ static unsigned char readReg(uint8_t addr) { uint8_t values; readRegs(addr, &values, 1); return values; } /************************************************************************************************** * 函数功能: void paj7620u2_selectBank(bank_e bank) * 输入参数: bank * 返 回 值: 无 * 说 明: 通过 IIC 向模块的读取 指定寄存器的数据 * ***************************************************************************************************/ void paj7620u2_selectBank(bank_e bank) { switch (bank) { case BANK0: writeReg(PAJ_REGITER_BANK_SEL, PAJ_BANK0); break; // BANK0 case BANK1: writeReg(PAJ_REGITER_BANK_SEL, PAJ_BANK1); break; // BANK1 } } void GS_WakeUp() { PAJ7620_send_byte(0); } /************************************************************************************************** * 函数功能: unsigned char paj7620u2_wakeup() * 输入参数: bank * 返 回 值: 无 * 说 明: 唤醒手势模块 * ***************************************************************************************************/ unsigned char paj7620u2_wakeup() { unsigned char data1 = 0x0a; unsigned char data2 = 0x0a; GS_WakeUp(); // wake PAJ7620U2 HAL_Delay(5); // wake time >400us GS_WakeUp(); // wake PAJ7620U2 HAL_Delay(5); // wake time>400us paj7620u2_selectBank(BANK0); //进入BANK0 /* check ID */ data1 = readReg(0x00); //读取id data2 = readReg(0x01); //读取id if ((data2 != 0x76) || (data1 != 0x20)) return 0; return 1; } /************************************************************************************************** * 函数功能: unsigned char PAJ7620u2_init(void) * 输入参数: * 返 回 值: 无 * 说 明: 模块初始化 * ***************************************************************************************************/ unsigned char PAJ7620u2_init(void) { unsigned char status; HAL_Delay(10); status = paj7620u2_wakeup(); //唤醒PAJ7620U2 if (!status) return 0; paj7620u2_selectBank(BANK0); // enter BANK0 HAL_Delay(50); /* Load the registers data */ for (uint8_t i = 0; i < INIT_REG_ARRAY_SIZE; i++) writeReg(initRegisterArray[i][0], initRegisterArray[i][1]); // init PAJ7620U2 paj7620u2_selectBank(BANK0); // enter BANK0 return 1; } /************************************************************************************************** * 函数功能: void Gesture_Init(void) * 输入参数: * 返 回 值: 无 * 说 明: 模块初始化 * ***************************************************************************************************/ void Gesture_Init(void) { unsigned char i; paj7620u2_selectBank(BANK0); // for (i = 0; i < GESTURE_SIZE; i++) { writeReg(gesture_arry[i][0], gesture_arry[i][1]); } paj7620u2_selectBank(BANK0); // memset(&gesture, 0, sizeof(GestureData)); } /************************************************************************************************** * 函数功能: void Gesture_Init(void) * 输入参数: * 返 回 值: 无 * 说 明: 模块初始化 * ***************************************************************************************************/ void PAJ7620_Init(void) { while (!PAJ7620u2_init()) // PAJ7620U2初始化 { printf("PAJ7620U2_B Error!!!\r\n"); HAL_Delay(500); } HAL_Delay(1000); printf("PAJ7620U2 OK\r\n"); // } unsigned char PAJ7620_Read_nByte(unsigned char addr, unsigned short len, unsigned char *buf) { return readRegs(addr, &buf[0], len); } /************************************************************************************************** * 函数功能: unsigned char GetCurrentGesture(void) * 输入参数: * 返 回 值: 无 * 说 明: 读取模块的手势地址 * ***************************************************************************************************/ unsigned char GetCurrentGesture(void) { uint8_t Data[2]={0,0}; uint16_t Gesture_Data; uint8_t state ; uint8_t i ; // Gesture_Init(); // 此处判断读取来当前手势不为0时,进行处理 state = PAJ7620_Read_nByte(PAJ_GET_INT_FLAG1, 2, &gesture.data[0]); // if(state != 0) { Gesture_Data = (gesture.data[1]<<8) | gesture.data[0]; switch(Gesture_Data) { case PAJ_UP: BEEPON ; printf("Up\r\n"); break; case PAJ_DOWN: BEEPOFF ; printf("Down\r\n"); break; case PAJ_LEFT: JDQ1ON ; printf("Left\r\n"); break; case PAJ_RIGHT: JDQ1OFF ; printf("Right\r\n"); break; case PAJ_FORWARD: JDQ2ON ; printf("Forward\r\n"); break; case PAJ_BACKWARD: JDQ2OFF ; printf("Backward\r\n"); break; case PAJ_CLOCKWISE: printf("Clockwise\r\n"); break; case PAJ_COUNT_CLOCKWISE: printf("AntiClockwise\r\n");break; case PAJ_WAVE: printf("Wave\r\n"); break; default: break; } Gesture_Data = 0; } return 0; }
主函数程序如下:
int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART3_UART_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ printf("autor by congconggege\r\n"); HAL_Delay(1000); printf("PAJ7620U2 Gesture Sensor Test Program ...\r\n"); PAJ7620_Init(); printf("PAJ7620U2 Gesture Sensor OK \r\n"); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ GetCurrentGesture();// HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5); HAL_Delay(500); } /* USER CODE END 3 */ }
实物图片如下:
试验效果:向上(Up)、向下(Dowm)、向左(Left)、向右(Right)、向前(Forward)、向后(Backward)、顺时针(Clockwise)、逆时针(Counterclockwise)、和挥动(Wave)。当识别到正确的手势,手势类型会打印在串口3上。