? ?零知開源(零知IDE)是一個專為電子初學者/電子興趣愛好者設計的開源軟硬件平臺,在硬件上提供超高性價比STM32系列開發板、物聯網控制板。取消了Bootloader程序燒錄,讓開發重心從“配置環境”轉移到“創意實現”,極大降低了技術門檻。零知IDE編程軟件,內置上千個覆蓋多場景的示例代碼,支持項目源碼一鍵下載,項目文章在線瀏覽。零知開源(零知IDE)平臺通過軟硬件協同創新,讓你的創意快速轉化為實物,來動手試試吧!
?訪問零知實驗室,獲取更多實戰項目和教程資源吧!
www.lingzhilab.com
?
?
項目概述
本項目基于零知標準板(主控芯片STM32F103RBT6)為核心控制器,結合先進的PAJ7620U2手勢識別傳感器和WS2812B RGB LED燈帶,實現智能手勢開關控制功能。系統能夠實時檢測手部在三維空間中的位置和運動軌跡,并將這些動作信息轉換為直觀、絢麗的燈光效果
項目難點及解決方案
問題描述:WS2812B時序精度控制,STM32普通IO難以滿足嚴格時序要求
驅動方案:使用SPI+DMA方式,確保時序準確
WS2812B::WS2812Bspi.setClockDivider(SPI_CLOCK_DIV32); // 444ns脈沖 WS2812B::WS2812Bspi.dmaSendAsync(pixels, numBytes); // 異步DMA傳輸
一、系統接線部分
1.1 硬件清單
| 組件名稱 | 型號規格 | 數量 | 說明 |
|---|---|---|---|
| 主控開發板 | 零知標準板(STM32F103RBT6) | 1 | 主控制器,72MHz主頻 |
| 手勢傳感器 | PAJ7620U2 | 1 | 手勢識別,I2C接口,最大檢測距離15cm |
| RGB LED燈帶 | WS2812-8 RGB模塊 | 2 | 16顆燈珠,SPI驅動,單線控制 |
| 連接線 | 杜邦線(母對母) | 若干 | 用于模塊間連接 |
| 電源 | 5V/2A直流電源 | 1 | 為系統供電 |
1.2 接線方案表
根據代碼中的引腳定義,硬件接線方案如下:
PAJ7620U2傳感器接線
| 零知標準板引腳 | PAJ7620U2引腳 | 功能說明 |
|---|---|---|
| A5 | SCL | I2C時鐘線(軟件模擬) |
| A4 | SDA | I2C數據線(軟件模擬) |
| 3.3V | VCC | 傳感器供電(3.3V) |
| GND | GND | 電源地 |
WS2812B燈帶接線
WS2812B需要較大電流,建議使用獨立5V電源供電
| 零知標準板引腳 | WS2812B燈帶 | 功能說明 |
|---|---|---|
| 11 | DIN | SPI數據輸出 |
| 5V | VCC | 燈帶供電(5V) |
| GND | GND | 電源地 |
1.3 具體接線圖
?

1.4 接線實物圖

二、安裝與使用部分
2.1 開源平臺-輸入PAJ7620U2并搜索-代碼下載自動打開

2.2 連接-驗證-上傳

2.3 調試-串口監視器

?
三、代碼講解部分
采用"單次讀取"策略,在主循環開頭一次性讀取isCursorInView/cursorX/cursorY,確保整個循環周期內使用同一組傳感器數據
3.1手勢檢測狀態機
// 手勢檢測邏輯不再讀取傳感器,而是分析傳入的數據 // 參數: isPresent(手是否在), y(當前Y坐標) // 返回: 0(無), 1(向上), -1(向下) int checkGestureLogic(bool isPresent, int y) { static int gestureStartY = 0; static unsigned long gestureStartTime = 0; static bool gestureInProgress = false; // 冷卻時間檢查:避免重復觸發 if (millis() - lastGestureTime < GESTURE_COOLDOWN) { return 0; } // 如果手移開了,重置檢測狀態 if (!isPresent) { gestureInProgress = false; return 0; } // 如果還沒開始檢測,記錄起點 if (!gestureInProgress) { gestureStartY = y; gestureStartTime = millis(); gestureInProgress = true; return 0; } // 計算變化量和速度 unsigned long duration = millis() - gestureStartTime; int yChange = y - gestureStartY; // 只有當持續時間足夠短且速度足夠快時,才認為是手勢 // 增加 duration > 50 是為了避免極其短暫的噪點 if (duration > 50 && abs(yChange) / (duration / 1000.0) > GESTURE_SPEED_THRESHOLD) { // 向上快速移動 (Y值減小) if (yChange < -GESTURE_THRESHOLD) { Serial.println("檢測到: 向上揮手 (開啟)"); lastGestureTime = millis(); gestureInProgress = false; return 1; } // 向下快速移動 (Y值增加) if (yChange > GESTURE_THRESHOLD) { Serial.println("檢測到: 向下揮手 (關閉)"); lastGestureTime = millis(); gestureInProgress = false; return -1; } } // 超時重置 (如果動作太慢,就視為普通移動而非手勢) if (duration > 800) { gestureInProgress = false; } return 0; }
手部進入檢測區域,記錄起始坐標和時間,持續跟蹤Y坐標變化,計算移動速度和幅度
3.2系統狀態管理
// 系統核心狀態變量
bool systemOn = false; // 系統開關狀態,初始為關閉
bool isIdleMode = true; // 是否處于待機模式
int idleEffectMode = 0; // 待機效果模式:0=流水燈, 1=呼吸燈
uint8_t globalBrightness = 30; // 全局亮度控制
void loop()
{
// 獲取傳感器數據
// ...
// 處理系統開關邏輯
if (!systemOn) {
// 關機狀態:只響應開啟手勢
if (detectedDir == 1) {
turnOnSystem();
}
delay(30);
return;
}
// 開機狀態:優先檢查關閉手勢
if (detectedDir == -1) {
turnOffSystem();
return;
}
// 正常的交互邏輯
// ...
}
狀態機流程圖

3.3 視覺交互反饋
開機動畫效果
從中心向兩側漸變紫色光效,過渡到全彩虹色
void showStartupEffect() {
strip.clear();
strip.show();
delay(100);
// 從中心向兩側展開的紫色動畫
int center = NUM_LEDS / 2;
int maxDistance = max(center, NUM_LEDS - center - 1);
for (int step = 0; step <= maxDistance; step++) {
strip.clear();
float brightnessFactor = (float)step / maxDistance;
brightnessFactor = constrain(brightnessFactor, 0.2, 1.0);
// 紫色(RGB: 148,0,211)
uint8_t r = 148 * brightnessFactor;
uint8_t b = 211 * brightnessFactor;
// 兩側對稱點亮
for (int dist = 0; dist <= step; dist++) {
if (center + dist < NUM_LEDS) strip.setPixelColor(center + dist, r, 0, b);
if (center - dist >= 0) strip.setPixelColor(center - dist, r, 0, b);
}
strip.show();
delay(60);
}
// 過渡到彩虹色
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, wheel((i * 256 / NUM_LEDS) % 256));
}
strip.show();
delay(500);
}
關機效果
關機效果為全部燈珠的亮度從當前值漸降至 0 后熄滅
void turnOffAllLEDs() {
// 漸暗效果
for (int b = globalBrightness; b > 0; b -= 10) {
strip.setBrightness(b);
strip.show();
delay(10);
}
strip.clear();
strip.show();
globalBrightness = 30; // 重置亮度
}
狀態指示燈
初始化提示,閃爍第 0 個燈珠 3 次提示就緒
void blinkStatusLED(int times, int delayTime) {
for (int i = 0; i < times; i++) {
strip.setPixelColor(0, 0, 0, 255); // 藍色閃爍
strip.show();
delay(delayTime);
strip.setPixelColor(0, 0, 0, 0);
strip.show();
delay(delayTime);
}
}
3.4 手勢跟蹤效果
void updateHandTrackingEffect(int x, int y) { int ledIndex = map(x, Y_MIN, Y_MAX, 0, NUM_LEDS - 1); ledIndex = constrain(ledIndex, 0, NUM_LEDS - 1); for(int i = 0; i < NUM_LEDS; i++) { float intensity = trailEffect[i]; if(i == ledIndex) { // 當前位置:白色高亮 strip.setPixelColor(i, 255, 255, 255); } else if(intensity > 0.05) { // 尾影位置:彩虹色 uint32_t col = wheel((i * 256 / NUM_LEDS) % 256); uint8_t r = ((col >> 16) & 0xFF) * intensity; uint8_t g = ((col >> 8) & 0xFF) * intensity; uint8_t b = (col & 0xFF) * intensity; strip.setPixelColor(i, r, g, b); } else { // 無尾影:關閉LED strip.setPixelColor(i, 0, 0, 0); } } }
3.5 系統待機
void updateWaterFlowEffect() { // 清屏 for(int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, 0, 0, 0); static int dir = 1; // 移動方向 idlePosition += dir; // 邊界處理和方向反轉 if(idlePosition >= NUM_LEDS || idlePosition < 0) { dir *= -1; idlePosition += dir; idleColorIndex = (idleColorIndex + 1) % 7; // 切換顏色 } // 設置當前光點 uint32_t c = rainbowColors[idleColorIndex]; strip.setPixelColor(idlePosition, c); // 注意:這個版本簡化了尾跡效果,可根據需要恢復 } uint32_t wheel(uint8_t wheelPos) { wheelPos = 255 - wheelPos; // 反轉以獲得更鮮艷的顏色 if(wheelPos < 85) { return strip.Color(255 - wheelPos * 3, 0, wheelPos * 3); // 紅-?>紫 } if(wheelPos < 170) { wheelPos -= 85; return strip.Color(0, wheelPos * 3, 255 - wheelPos * 3); // 紫-?>青 } wheelPos -= 170; return strip.Color(wheelPos * 3, 255 - wheelPos * 3, 0); // 青->綠 }
3.6 完整代碼
/************************************************************************************** * 文件: /PAJ7620U2_Gesture_WS2812/PAJ7620U2_Gesture_WS2812.ino * 作者:零知實驗室(深圳市在芯間科技有限公司) * -^^- 零知實驗室,讓電子制作變得更簡單! -^^- * 時間: 2025-12-26 * 說明: 基于零知標準板(STM32F103RBT6)驅動PAJ7620U2手勢傳感器實現WS2812B燈帶控制, * 支持手部位置跟蹤的彩虹尾影效果,無手部時自動切換為流水燈/呼吸燈待機效果 * 采用"單次讀取"策略,確保在任意狀態下均能準確識別退出手勢。 ***************************************************************************************/ #include "RevEng_PAJ7620.h" #include // 手勢傳感器對象 RevEng_PAJ7620 sensor = RevEng_PAJ7620(); // LED燈帶配置 #define NUM_LEDS 16 WS2812B strip = WS2812B(NUM_LEDS); // 系統狀態 int lastCursorX = 0; bool isIdleMode = true; bool systemOn = false; // 系統開關狀態,初始為關閉 // 手部跟蹤變量 float trailEffect[NUM_LEDS] = {0}; float trailDecay = 0.85; // 待機效果變量 int idleEffectMode = 0; // 0:流水燈, 1:呼吸燈 int idleColorIndex = 0; int idlePosition = 0; float idlePulse = 0; unsigned long idleLastUpdate = 0; // 亮度控制 uint8_t globalBrightness = 30; // 全局亮度,0-255 // 手勢檢測變量 unsigned long lastGestureTime = 0; const unsigned long GESTURE_COOLDOWN = 800; // 稍微縮短冷卻時間提高響應 const int GESTURE_THRESHOLD = 1000; // 調低閾值使其更容易觸發 const int GESTURE_SPEED_THRESHOLD = 600; // 調低速度閾值 // 坐標范圍 const int Y_MIN = 384; const int Y_MAX = 3455; // 預定義顏色(彩虹色) uint32_t rainbowColors[7] = { strip.Color(255, 0, 0), // 紅色 strip.Color(255, 127, 0), // 橙色 strip.Color(255, 255, 0), // 黃色 strip.Color(0, 255, 0), // 綠色 strip.Color(0, 0, 255), // 藍色 strip.Color(75, 0, 130), // 靛藍色 strip.Color(148, 0, 211) // 紫色 }; // *************************************************************************** void setup() { Serial.begin(115200); // 初始化LED燈帶 strip.begin(); strip.clear(); strip.show(); // 初始化手勢傳感器 if(!sensor.begin()) { Serial.println("PAJ7620初始化失敗!"); while(true) {} } Serial.println("PAJ7620U2初始化成功!"); sensor.setCursorMode(); // 設置為光標模式 Serial.println("n手勢控制LED系統已啟動!"); Serial.println("向上快速揮手: 打開系統"); Serial.println("向下快速揮手: 關閉系統"); systemOn = false; turnOffAllLEDs(); blinkStatusLED(3, 300); } // *************************************************************************** void loop() { // 在循環開始時,一次性讀取所有傳感器數據 bool currentInView = sensor.isCursorInView(); int currentX = 0; int currentY = 0; if (currentInView) { currentX = sensor.getCursorX(); currentY = sensor.getCursorY(); } // 將讀取到的數據傳入手勢檢測函數 (不讓函數內部再次讀取) // 返回值: 0=無, 1=向上(開), -1=向下(關) int detectedDir = checkGestureLogic(currentInView, currentY); // 處理系統開關邏輯 if (!systemOn) { // 關機狀態:只響應開啟手勢 if (detectedDir == 1) { turnOnSystem(); } delay(30); // 簡單的防抖延遲 return; } // 開機狀態:優先檢查關閉手勢 if (detectedDir == -1) { turnOffSystem(); return; } // 如果沒有開關機手勢,執行正常的LED顯示邏輯 strip.setBrightness(globalBrightness); if(currentInView) { // 手部跟蹤模式 isIdleMode = false; // 亮度跟隨Y軸變化 int brightness = map(currentY, Y_MIN, Y_MAX, 30, 255); brightness = constrain(brightness, 30, 255); if (abs(brightness - globalBrightness) > 5) { globalBrightness = brightness; strip.setBrightness(globalBrightness); } updateTrail(); // 使用剛才讀取的 currentX 更新效果 updateHandTrackingEffect(currentX, currentY); // 添加高亮 int ledIndex = map(currentX, Y_MIN, Y_MAX, 0, NUM_LEDS - 1); ledIndex = constrain(ledIndex, 0, NUM_LEDS - 1); trailEffect[ledIndex] = 1.0; lastCursorX = currentX; } else { // 待機模式 if(!isIdleMode) { resetIdleEffects(); isIdleMode = true; } updateTrailFast(); updateIdleEffect(); } strip.show(); delay(30); } // *************************************************************************** // 手勢檢測不再讀取傳感器,而是分析傳入的數據 // 參數: isPresent(手是否在), y(當前Y坐標) // 返回: 0(無), 1(向上), -1(向下) int checkGestureLogic(bool isPresent, int y) { static int gestureStartY = 0; static unsigned long gestureStartTime = 0; static bool gestureInProgress = false; unsigned long currentTime = millis(); // 冷卻時間檢查 if (currentTime - lastGestureTime < GESTURE_COOLDOWN) { return 0; } // 如果手移開了,重置檢測狀態 if (!isPresent) { gestureInProgress = false; return 0; } // 如果還沒開始檢測,記錄起點 if (!gestureInProgress) { gestureStartY = y; gestureStartTime = currentTime; gestureInProgress = true; return 0; // 剛開始,還沒結果 } // 計算變化量和速度 unsigned long duration = currentTime - gestureStartTime; int yChange = y - gestureStartY; // 避免除以零 if (duration == 0) return 0; float speed = abs(yChange) / (duration / 1000.0); // 只有當持續時間足夠短且速度足夠快時,才認為是手勢 // 增加 duration > 50 是為了避免極其短暫的噪點 if (duration > 50 && speed > GESTURE_SPEED_THRESHOLD) { // 向上快速移動 (Y值減小) if (yChange < -GESTURE_THRESHOLD) { Serial.println("檢測到: 向上揮手 (開啟)"); lastGestureTime = currentTime; gestureInProgress = false; return 1; } // 向下快速移動 (Y值增加) if (yChange > GESTURE_THRESHOLD) { Serial.println("檢測到: 向下揮手 (關閉)"); lastGestureTime = currentTime; gestureInProgress = false; return -1; } } // 超時重置 (如果動作太慢,就視為普通移動而非手勢) if (duration > 800) { gestureInProgress = false; // 重置,這也允許連續跟蹤而不誤觸發 } return 0; } // *************************************************************************** // 打開系統 void turnOnSystem() { systemOn = true; Serial.println(">>> 系統啟動"); resetIdleEffects(); clearTrailEffects(); globalBrightness = 30; strip.setBrightness(globalBrightness); showStartupEffect(); } // *************************************************************************** // 關閉系統 void turnOffSystem() { systemOn = false; Serial.println(">>> 系統關閉"); turnOffAllLEDs(); } // *************************************************************************** // 啟動效果 void showStartupEffect() { strip.clear(); strip.show(); delay(100); int center = NUM_LEDS / 2; int maxDistance = max(center, NUM_LEDS - center - 1); for (int step = 0; step <= maxDistance; step++) { strip.clear(); float brightnessFactor = (float)step / maxDistance; brightnessFactor = constrain(brightnessFactor, 0.2, 1.0); uint8_t r = 148 * brightnessFactor; uint8_t b = 211 * brightnessFactor; for (int dist = 0; dist <= step; dist++) { if (center + dist < NUM_LEDS) strip.setPixelColor(center + dist, r, 0, b); if (center - dist >= 0) strip.setPixelColor(center - dist, r, 0, b); } strip.show(); delay(60); } // 快速過渡到彩虹 for (int i = 0; i < NUM_LEDS; i++) { strip.setPixelColor(i, wheel((i * 256 / NUM_LEDS) % 256)); } strip.show(); delay(500); } // *************************************************************************** // LED 燈帶平滑漸暗關閉 void turnOffAllLEDs() { for (int b = globalBrightness; b > 0; b -= 10) { strip.setBrightness(b); strip.show(); delay(10); } strip.clear(); strip.show(); globalBrightness = 30; } // *************************************************************************** // 清除所有手勢跟蹤的軌跡變量,重置手勢軌跡效果 void clearTrailEffects() { for(int i = 0; i < NUM_LEDS; i++) trailEffect[i] = 0; } // *************************************************************************** // 控制第1個LED(索引0)按指定次數和間隔閃爍,用于設備狀態初始化成功提示 void blinkStatusLED(int times, int delayTime) { for (int i = 0; i < times; i++) { strip.setPixelColor(0, 0, 0, 255); strip.show(); delay(delayTime); strip.setPixelColor(0, 0, 0, 0); strip.show(); delay(delayTime); } } // *************************************************************************** // 緩慢衰減軌跡強度,用于手勢效果 void updateTrail() { for(int i = 0; i < NUM_LEDS; i++) { trailEffect[i] *= trailDecay; if(trailEffect[i] < 0.01) trailEffect[i] = 0; } } // *************************************************************************** // 快速衰減軌跡強度,用于待機效果 void updateTrailFast() { for(int i = 0; i < NUM_LEDS; i++) { trailEffect[i] *= 0.6; if(trailEffect[i] < 0.01) trailEffect[i] = 0; } } // *************************************************************************** // 根據手勢坐標(x,y)更新LED燈帶的手勢跟蹤顯示效果 void updateHandTrackingEffect(int x, int y) { int ledIndex = map(x, Y_MIN, Y_MAX, 0, NUM_LEDS - 1); ledIndex = constrain(ledIndex, 0, NUM_LEDS - 1); for(int i = 0; i < NUM_LEDS; i++) { float intensity = trailEffect[i]; if(i == ledIndex) { strip.setPixelColor(i, 255, 255, 255); // 手勢當前位置的LED顯示白色 (255,255,255) } else if(intensity > 0.05) { uint32_t col = wheel((i * 256 / NUM_LEDS) % 256); // 彩虹色拖尾 uint8_t r = ((col >> 16) & 0xFF) * intensity; uint8_t g = ((col >> 8) & 0xFF) * intensity; uint8_t b = (col & 0xFF) * intensity; strip.setPixelColor(i, r, g, b); } else { strip.setPixelColor(i, 0, 0, 0); } } } // *************************************************************************** // 重置所有待機狀態特效的參數 void resetIdleEffects() { idlePosition = 0; idlePulse = 0; idleColorIndex = 0; idleLastUpdate = millis(); idleEffectMode = random(0, 2); } // *************************************************************************** // 每5秒自動切換空閑狀態模式 void updateIdleEffect() { unsigned long currentTime = millis(); if(currentTime - idleLastUpdate > 5000) { idleEffectMode = (idleEffectMode + 1) % 2; idleLastUpdate = currentTime; } if(idleEffectMode == 0) updateWaterFlowEffect(); else updateBreathingEffect(); } // *************************************************************************** // 空閑狀態流水燈效果 void updateWaterFlowEffect() { for(int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, 0, 0, 0); static int dir = 1; idlePosition += dir; if(idlePosition >= NUM_LEDS || idlePosition < 0) { dir *= -1; idlePosition += dir; idleColorIndex = (idleColorIndex + 1) % 7; } uint32_t c = rainbowColors[idleColorIndex]; strip.setPixelColor(idlePosition, c); } // *************************************************************************** // 空閑狀態呼吸燈效果 void updateBreathingEffect() { idlePulse += 0.05; if(idlePulse > 6.28) { idlePulse = 0; idleColorIndex = (idleColorIndex + 1) % 7; } float val = (sin(idlePulse) + 1.0) / 2.0 * 0.8 + 0.2; // 周期性生成 0~1 之間的亮度系數val uint32_t c = rainbowColors[idleColorIndex]; uint8_t r = ((c >> 16) & 0xFF) * val; uint8_t g = ((c >> 8) & 0xFF) * val; uint8_t b = (c & 0xFF) * val; for(int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, r, g, b); } // *************************************************************************** // 生成對應的彩虹顏色RGB,實現HSV到RGB的簡易轉換 uint32_t wheel(uint8_t wheelPos) { wheelPos = 255 - wheelPos; if(wheelPos < 85) return strip.Color(255 - wheelPos * 3, 0, wheelPos * 3); if(wheelPos < 170) { wheelPos -= 85; return strip.Color(0, wheelPos * 3, 255 - wheelPos * 3); } wheelPos -= 170; return strip.Color(wheelPos * 3, 255 - wheelPos * 3, 0); } /****************************************************************************** * 深圳市在芯間科技有限公司 * 淘寶店鋪:在芯間科技零知板 * 店鋪網址:https://shop533070398.taobao.com * 版權說明: * 1.本代碼的版權歸【深圳市在芯間科技有限公司】所有,僅限個人非商業性學習使用。 * 2.嚴禁將本代碼或其衍生版本用于任何商業用途(包括但不限于產品開發、付費服務、企業內部使用等)。 * 3.任何商業用途均需事先獲得【深圳市在芯間科技有限公司】的書面授權,未經授權的商業使用行為將被視為侵權。 ******************************************************************************/
系統流程圖

四、操作流程
4.1 系統操作
初始化狀態:燈帶全滅,第 0 個燈珠閃爍 3 次,串口提示 “向上快速揮手:打開系統”;
?

在傳感器上方快速向上揮手開啟系統,在開機狀態下,快速向下揮手關閉系統,LED燈帶會逐漸變暗關閉
手勢控制:將手放在傳感器上方5-10cm處,LED燈帶會對應顯示白色光標;左右平行移動手部,光標跟隨移動并留下彩虹色尾影

手部離開傳感器區域,自動進入待機模式,每5秒在流水燈和呼吸燈之間切換
?
靈敏度調整
// 如果手勢不易觸發,降低閾值 const int GESTURE_THRESHOLD = 800; // 降低幅度要求 const int GESTURE_SPEED_THRESHOLD = 400; // 降低速度要求 // 如果誤觸發過多,增加閾值 const int GESTURE_THRESHOLD = 1200; // 增加幅度要求 const int GESTURE_SPEED_THRESHOLD = 800; // 增加速度要求
視覺效果調整
// 調整尾影持續時間 float trailDecay = 0.9; // 值越大,尾影持續時間越長 // 調整亮度范圍 int brightness = map(currentY, Y_MIN, Y_MAX, 20, 100); // 縮小亮度變化范圍
4.2 視頻演示
?https://www.bilibili.com/video/BV1uivsBBEJo/?vd_source=a31e3d8d8ce008260eee442534c2f63d
系統初始化燈帶閃爍狀態燈提示就緒,手勢跟蹤模式下白色光點跟隨手部 X 軸,亮度隨手部 Y 軸平滑變化,尾影效果自然;移開手部自動切換流水燈 / 呼吸燈
五、PAJ7620U2 手勢傳感器知識點講解
PAJ7620U2是一款內置光源和環境光抑制濾波器集成的 LED,鏡頭和手勢感測器在一個小的立方體模組,能在黑暗或低光環境下工作,其模塊功能框圖如下圖所示

IIC 接口,支持高達 400KHz 通信速率;內置 9 個手勢類型(上、下、左、右、前、后、順時針旋轉、逆時針旋轉、揮動),支持輸出中斷;支持接近檢測功能,檢測物體體積大小和亮度
5.1 軟件I2C通信
MCU 通過 I2C 接口來控制 PAJ7620U2,PAJ7620U2 工作時通過內部 LED 驅動器
I2C 時序參數

| 參數說明 | 符號 | 標準模式 | 快速模式 | 單位 |
|---|---|---|---|---|
| SCL 時鐘頻率 | fscl | 10 ~ 100 | 10 ~ 400 | kHz |
| 起始/重復起始條件保持時間(此后產生第一個時鐘脈沖) | tHD,STA | ≥ 4 | ≥ 0.6 | μs |
| 重復起始條件建立時間 | tSU,STA | ≥ 4.7 | ≥ 0.6 | μs |
| SCL 時鐘低電平時間 | tLOW | ≥ 4.7 | ≥ 1.3 | μs |
| SCL 時鐘高電平時間 | tHIGH | ≥ 4 | ≥ 0.6 | μs |
| 數據保持時間 | tHD,DAT? | ≥ 0 | ≥ 0 | μs |
| 數據建立時間 | tSU,DATt | ≥ 250 | ≥ 100 | ns |
| SDA 與 SCL 信號上升時間 | tr | ≤ 1000 | ≤ 300 | ns |
| SDA 與 SCL 信號下降時間 | tf | ≤ 300 | ≤ 300 | ns |
| 停止條件建立時間 | tSU,STO | ≥ 4 | ≥ 0.6 | μs |
| 停止條件到起始條件的總線空閑時間 | tBUF | ≥ 4.7 | ≥ 1.3 | μs |
當傳感器陣列在有效的距離中探測到物體時,目標信息提取陣列會對探測目標進行特征原始數據的獲取,獲取的數據會存在寄存器中,同時手勢識別陣列會對原始數據進行識別處理,最后將手勢結果存到寄存器中,用戶可根據 I2C 接口對原始數據和手勢識別的結果進行讀取
光標模式數據讀取時序

5.2 寄存器配置
PAJ7620U2 的內部有兩個 BANK 寄存器區域,分別為 BANK0 和 BANK1。不同的區域用于訪問不同的功能寄存器,訪問某一 BANK 區域下的寄存器前需發送控制指令進入該寄存器區域

進入 BANK0 區域需向傳感器 0xEF 地址寫 0x00,而 BANK1 區域需向傳感器 0xEF 地址寫 0x01
使能工作寄存器

該寄存器地址為 0X72,用于使能 PAJ7620 工作,bit0 位設置為 1 則使能 PAJ7620 工作,設置為 0 則失能 PAJ7620 工作
手勢檢測輸出中斷使能寄存器

該寄存器地址為 0X41,用于手勢識別,bit0~bit7 位用于使能不同手勢識別結果的中斷輸出,默認值為 0XFF
六、常見問題解答(FAQ)
Q1:手勢開關機偶爾誤觸發?
A:解決方案:增大GESTURE_THRESHOLD 或GESTURE_SPEED_THRESHOLD 閾值,提高觸發門檻;延長GESTURE_COOLDOWN(如 1000ms),避免短時間重復觸發
Q2:如何增加更多LED?
A:修改步驟:更新NUM_LEDS定義,調整trailEffect數組大小,可能需要增加電源功率
項目資源整合
PAJ7620U2 數據手冊: https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf
PAJ7620U2 庫文件: https://github.com/acrandal/RevEng_PAJ7620
審核編輯 黃宇
-
RGB
+關注
關注
4文章
831瀏覽量
61938 -
IDE
+關注
關注
0文章
365瀏覽量
49056 -
STM32F103RBT6
+關注
關注
0文章
4瀏覽量
7796 -
WS2812
+關注
關注
0文章
35瀏覽量
7143
發布評論請先 登錄
零知IDE——基于零知標準板驅動PAJ7620U2手勢控制L9110風扇模塊和SG90舵機系統
STM32驅動PAJ7620手勢識別傳感器
CW32L012/F030靈眸X1智能小車——板載WS2812驅動示例
零知IDE——基于STM32F103RBT6的PAJ7620U2手勢控制WS2812 RGB燈帶系統
【瑞薩RA6E2地奇星開發板試用】點亮 WS2812 全彩點陣屏
基于STM32F103C8T6驅動WS2812彩燈模塊點亮RGB燈
【瑞薩RA6E2】驅動 WS2812 實現 RGB 跑馬燈效果
【瑞薩RA6E2】點亮 WS2812 全彩點陣屏
零知開源——STM32F103RBT6驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程
零知開源——STM32F103RBT6驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程
零知IDE——基于STM32F103RBT6的PAJ7620U2手勢控制WS2812 RGB燈帶系統
評論