国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

零知IDE——基于STM32F103RBT6的PAJ7620U2手勢控制WS2812 RGB燈帶系統

零知實驗室 ? 來源:PCB56242069 ? 作者:PCB56242069 ? 2025-12-29 17:48 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

? ?零知開源(零知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 具體接線圖

?

wKgZPGlSRHSAZ-7uAALe_xnQ9Hw605.png

1.4 接線實物圖

wKgZPGlSRM2ADTipABDK1CPy2lQ318.png

二、安裝與使用部分

2.1 開源平臺-輸入PAJ7620U2并搜索-代碼下載自動打開

wKgZPGlSRUSAZ946AAHesV__Ntw736.png

2.2 連接-驗證-上傳

wKgZPGlSRW-AFAXXAAL1rmvhF3U705.png

2.3 調試-串口監視器

wKgZO2lSRX6ASzi7AALw2nF5hrw130.png

?

三、代碼講解部分

采用"單次讀取"策略,在主循環開頭一次性讀取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;
  }
  
  // 正常的交互邏輯
  // ...
}

狀態機流程圖

wKgZO2lSReaAWtSdAALBna9j8Zg684.png

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.任何商業用途均需事先獲得【深圳市在芯間科技有限公司】的書面授權,未經授權的商業使用行為將被視為侵權。
******************************************************************************/

系統流程圖

wKgZPGlSRmyAVE5YAAHjzL7wzCU124.png

四、操作流程

4.1 系統操作

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

?

wKgZO2lSRrWAR7oAAAH-XFKWRbY671.png

在傳感器上方快速向上揮手開啟系統,在開機狀態下,快速向下揮手關閉系統,LED燈帶會逐漸變暗關閉

手勢控制:將手放在傳感器上方5-10cm處,LED燈帶會對應顯示白色光標;左右平行移動手部,光標跟隨移動并留下彩虹色尾影

wKgZO2lSRsKAXp1pAAE-0bJrWfs907.png

手部離開傳感器區域,自動進入待機模式,每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,鏡頭和手勢感測器在一個小的立方體模組,能在黑暗或低光環境下工作,其模塊功能框圖如下圖所示

wKgZPGlSTAWAPYu3AACINiJImSc665.png

IIC 接口,支持高達 400KHz 通信速率;內置 9 個手勢類型(上、下、左、右、前、后、順時針旋轉、逆時針旋轉、揮動),支持輸出中斷;支持接近檢測功能,檢測物體體積大小和亮度

5.1 軟件I2C通信

MCU 通過 I2C 接口來控制 PAJ7620U2,PAJ7620U2 工作時通過內部 LED 驅動器

I2C 時序參數

wKgZPGlSTCqAae1SAADW0-A1mwE503.png

參數說明 符號 標準模式 快速模式 單位
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 接口對原始數據和手勢識別的結果進行讀取

光標模式數據讀取時序

wKgZO2lSTI6AbsIPAAFkdOAv6Oo596.png

5.2 寄存器配置

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

wKgZPGlSTLGAH7C_AADjbYwbEV8102.png

進入 BANK0 區域需向傳感器 0xEF 地址寫 0x00,而 BANK1 區域需向傳感器 0xEF 地址寫 0x01

使能工作寄存器

wKgZO2lSTOqAe7oJAAD1-Wgfb7c212.png

該寄存器地址為 0X72,用于使能 PAJ7620 工作,bit0 位設置為 1 則使能 PAJ7620 工作,設置為 0 則失能 PAJ7620 工作

手勢檢測輸出中斷使能寄存器

wKgZO2lSTNqAaSriAAE84pQNuJE265.png

該寄存器地址為 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
    RGB
    +關注

    關注

    4

    文章

    831

    瀏覽量

    61938
  • IDE
    IDE
    +關注

    關注

    0

    文章

    365

    瀏覽量

    49056
  • STM32F103RBT6
    +關注

    關注

    0

    文章

    4

    瀏覽量

    7796
  • WS2812
    +關注

    關注

    0

    文章

    35

    瀏覽量

    7143
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    IDE——基于標準板驅動PAJ7620U2手勢控制L9110風扇模塊和SG90舵機系統

    * 說明:標準板(STM32F103RBT6) + PAJ7620U2 + L9110 手勢控制
    發表于 01-06 11:56

    STM32驅動PAJ7620手勢識別傳感器

    原文鏈接:[https://www.yourcee.com/newsinfo/2925937.html] 傳感器簡介 PAJ-7620U2是由原相科技公司開發的一款手勢識別芯片, 內集成了光學數組式
    的頭像 發表于 01-06 09:16 ?763次閱讀
    <b class='flag-5'>STM32</b>驅動<b class='flag-5'>PAJ7620</b><b class='flag-5'>手勢</b>識別傳感器

    CW32L012/F030靈眸X1智能小車——板載WS2812驅動示例

    WS2812是一種廣泛使用的地址可控制RGB LED模塊,其內置驅動電路和控制芯片,允許用戶通過單線信號控制多個LED的顏色和亮度。
    的頭像 發表于 01-05 16:35 ?2865次閱讀
    CW32L012/<b class='flag-5'>F</b>030靈眸X1智能小車——板載<b class='flag-5'>WS2812</b>驅動示例

    IDE——基于STM32F103RBT6PAJ7620U2手勢控制WS2812 RGB系統

    簡單! -^^- * 時間: 2025-12-26 * 說明: 基于標準板(STM32F103RBT6)驅動PAJ7620U2手勢
    發表于 12-29 19:04

    標準板+PAJ7620U2手勢控制WS2812 RGB

    RGB
    PCB56242069
    發布于 :2025年12月29日 17:17:34

    【瑞薩RA6E2地奇星開發板試用】點亮 WS2812 全彩點陣屏

    通過單總線方式控制,只需一個 IO 口,就可以點亮數千個珠。 這里使用 RA6E2 點亮 WS2812 珠,效果如下:
    發表于 12-27 03:28

    基于STM32F103C8T6驅動WS2812彩燈模塊點亮RGB

    一、WS2812模塊簡介 WS2812是一個集控制電路與發光電路于一體的智能外控LED光源。其外型與一個5050LED珠相同,每個元件即為一個像素點。像素點內部包含了智能數字接口數據
    的頭像 發表于 12-04 11:29 ?6598次閱讀
    基于<b class='flag-5'>STM32F103C8T6</b>驅動<b class='flag-5'>WS2812</b>彩燈模塊點亮<b class='flag-5'>RGB</b><b class='flag-5'>燈</b>

    【瑞薩RA6E2】驅動 WS2812 實現 RGB 跑馬燈效果

    首先說明硬件接線調整:WS2812 珠串聯時,第一個珠的 DI 引腳連接 RA6E2 的 P001 引腳,VCC 接開發板 5V 電源,GND 與開發板 GND 共地,后續
    發表于 11-25 01:55

    【瑞薩RA6E2】點亮 WS2812 全彩點陣屏

    通過單總線方式控制,只需一個 IO 口,就可以點亮數千個珠。 這里使用 RA6E2 點亮 WS2812 珠,效果如下:
    發表于 11-05 08:36

    開源——STM32F103RBT6驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程

    STM32F1本教程使用標準板(STM32F103RBT6)通過I2C驅動ICM20948九軸傳感器,實現姿態解算,并通過串口將數據實時
    發表于 06-09 14:53

    開源——STM32F103RBT6驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程

    本教程使用標準板(STM32F103RBT6)通過I2C驅動ICM20948九軸傳感器,實現姿態解算,并通過串口將數據實時發送至VOFA+上位機進行3D可視化。
    的頭像 發表于 06-09 14:01 ?1862次閱讀
    <b class='flag-5'>零</b><b class='flag-5'>知</b>開源——<b class='flag-5'>STM32F103RBT6</b>驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程

    【RA4L1-SENSOR】點亮 WS2812 全彩點陣屏

    WS2812 是彩色燈珠控制芯片,可以控制 RGB 三色珠混合呈現出 16M 種顏色,WS2812
    發表于 06-09 12:48

    【RA-Eco-RA4M2開發板評測】點亮WS2812點陣屏

    WS2812 是彩色燈珠控制芯片,可以控制 RGB 三色珠混合呈現出 16M 種顏色,WS2812
    發表于 05-07 15:28

    WS2812B on S32K144始終閃爍白色,即使沒有數據也是如此,為什么?

    我正在嘗試控制WS2812B RGB LED使用型號 S32K144EVB-Q100.我的目標是發送GRB 數據使用 bit-banging onPTD0.但是,LED 始終閃爍白色 (R=255
    發表于 04-08 07:15

    用NANO STM32F103RBT6的開發板燒錄不了是哪里出了問題?

    今天用久別的NANO STM32F103RBT6的開發板,在公司的電腦上編譯基本工程,怎么就燒錄不了了?望好友伸出援助之手,不勝感激。
    發表于 03-10 07:48