91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

零知IDE——基于STM32F103RBT6的PAJ7620U2手勢(shì)控制WS2812 RGB燈帶系統(tǒng)

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

掃碼添加小助手

加入工程師交流群

? ?零知開源(零知IDE)是一個(gè)專為電子初學(xué)者/電子興趣愛好者設(shè)計(jì)的開源軟硬件平臺(tái),在硬件上提供超高性價(jià)比STM32系列開發(fā)板、物聯(lián)網(wǎng)控制板。取消了Bootloader程序燒錄,讓開發(fā)重心從“配置環(huán)境”轉(zhuǎn)移到“創(chuàng)意實(shí)現(xiàn)”,極大降低了技術(shù)門檻。零知IDE編程軟件,內(nèi)置上千個(gè)覆蓋多場(chǎng)景的示例代碼,支持項(xiàng)目源碼一鍵下載,項(xiàng)目文章在線瀏覽。零知開源(零知IDE)平臺(tái)通過軟硬件協(xié)同創(chuàng)新,讓你的創(chuàng)意快速轉(zhuǎn)化為實(shí)物,來動(dòng)手試試吧!

?訪問零知實(shí)驗(yàn)室,獲取更多實(shí)戰(zhàn)項(xiàng)目和教程資源吧!

www.lingzhilab.com

?

?

項(xiàng)目概述

本項(xiàng)目基于零知標(biāo)準(zhǔn)板(主控芯片STM32F103RBT6)為核心控制器,結(jié)合先進(jìn)的PAJ7620U2手勢(shì)識(shí)別傳感器和WS2812B RGB LED燈帶,實(shí)現(xiàn)智能手勢(shì)開關(guān)控制功能。系統(tǒng)能夠?qū)崟r(shí)檢測(cè)手部在三維空間中的位置和運(yùn)動(dòng)軌跡,并將這些動(dòng)作信息轉(zhuǎn)換為直觀、絢麗的燈光效果

項(xiàng)目難點(diǎn)及解決方案

問題描述:WS2812B時(shí)序精度控制,STM32普通IO難以滿足嚴(yán)格時(shí)序要求

驅(qū)動(dòng)方案:使用SPI+DMA方式,確保時(shí)序準(zhǔn)確

WS2812B::WS2812Bspi.setClockDivider(SPI_CLOCK_DIV32);  // 444ns脈沖
WS2812B::WS2812Bspi.dmaSendAsync(pixels, numBytes);    // 異步DMA傳輸

一、系統(tǒng)接線部分

1.1 硬件清單

組件名稱 型號(hào)規(guī)格 數(shù)量 說明
主控開發(fā)板 零知標(biāo)準(zhǔn)板(STM32F103RBT6) 1 主控制器,72MHz主頻
手勢(shì)傳感器 PAJ7620U2 1 手勢(shì)識(shí)別,I2C接口,最大檢測(cè)距離15cm
RGB LED燈帶 WS2812-8 RGB模塊 2 16顆燈珠,SPI驅(qū)動(dòng),單線控制
連接線 杜邦線(母對(duì)母) 若干 用于模塊間連接
電源 5V/2A直流電源 1 為系統(tǒng)供電

1.2 接線方案表

根據(jù)代碼中的引腳定義,硬件接線方案如下:

PAJ7620U2傳感器接線

零知標(biāo)準(zhǔn)板引腳 PAJ7620U2引腳 功能說明
A5 SCL I2C時(shí)鐘線(軟件模擬)
A4 SDA I2C數(shù)據(jù)線(軟件模擬)
3.3V VCC 傳感器供電(3.3V)
GND GND 電源地

WS2812B燈帶接線

WS2812B需要較大電流,建議使用獨(dú)立5V電源供電

零知標(biāo)準(zhǔn)板引腳 WS2812B燈帶 功能說明
11 DIN SPI數(shù)據(jù)輸出
5V VCC 燈帶供電(5V)
GND GND 電源地

1.3 具體接線圖

?

wKgZPGlSRHSAZ-7uAALe_xnQ9Hw605.png

1.4 接線實(shí)物圖

wKgZPGlSRM2ADTipABDK1CPy2lQ318.png

二、安裝與使用部分

2.1 開源平臺(tái)-輸入PAJ7620U2并搜索-代碼下載自動(dòng)打開

wKgZPGlSRUSAZ946AAHesV__Ntw736.png

2.2 連接-驗(yàn)證-上傳

wKgZPGlSRW-AFAXXAAL1rmvhF3U705.png

2.3 調(diào)試-串口監(jiān)視器

wKgZO2lSRX6ASzi7AALw2nF5hrw130.png

?

三、代碼講解部分

采用"單次讀取"策略,在主循環(huán)開頭一次性讀取isCursorInView/cursorX/cursorY,確保整個(gè)循環(huán)周期內(nèi)使用同一組傳感器數(shù)據(jù)

3.1手勢(shì)檢測(cè)狀態(tài)機(jī)

// 手勢(shì)檢測(cè)邏輯不再讀取傳感器,而是分析傳入的數(shù)據(jù)
// 參數(shù): isPresent(手是否在), y(當(dāng)前Y坐標(biāo))
// 返回: 0(無), 1(向上), -1(向下)
int checkGestureLogic(bool isPresent, int y) {
  static int gestureStartY = 0;
  static unsigned long gestureStartTime = 0;
  static bool gestureInProgress = false;
  
  // 冷卻時(shí)間檢查:避免重復(fù)觸發(fā)
  if (millis() - lastGestureTime < GESTURE_COOLDOWN) {
    return 0;
  }
  
  // 如果手移開了,重置檢測(cè)狀態(tài)
  if (!isPresent) {
    gestureInProgress = false;
    return 0;
  }
  
  // 如果還沒開始檢測(cè),記錄起點(diǎn)
  if (!gestureInProgress) {
    gestureStartY = y;
    gestureStartTime = millis();
    gestureInProgress = true;
    return 0;
  }
  
  // 計(jì)算變化量和速度
  unsigned long duration = millis() - gestureStartTime;
  int yChange = y - gestureStartY;
  
  // 只有當(dāng)持續(xù)時(shí)間足夠短且速度足夠快時(shí),才認(rèn)為是手勢(shì)
  // 增加 duration > 50 是為了避免極其短暫的噪點(diǎn)
  if (duration > 50 && abs(yChange) / (duration / 1000.0) > GESTURE_SPEED_THRESHOLD) {
    
    // 向上快速移動(dòng) (Y值減小)
    if (yChange < -GESTURE_THRESHOLD) {
      Serial.println("檢測(cè)到: 向上揮手 (開啟)");
      lastGestureTime = millis();
      gestureInProgress = false;
      return 1;
    }
    
    // 向下快速移動(dòng) (Y值增加)
    if (yChange > GESTURE_THRESHOLD) {
      Serial.println("檢測(cè)到: 向下?lián)]手 (關(guān)閉)");
      lastGestureTime = millis();
      gestureInProgress = false;
      return -1;
    }
  }
  
  // 超時(shí)重置 (如果動(dòng)作太慢,就視為普通移動(dòng)而非手勢(shì))
  if (duration > 800) {
    gestureInProgress = false;
  }
  
  return 0;
}

手部進(jìn)入檢測(cè)區(qū)域,記錄起始坐標(biāo)和時(shí)間,持續(xù)跟蹤Y坐標(biāo)變化,計(jì)算移動(dòng)速度和幅度

3.2系統(tǒng)狀態(tài)管理

// 系統(tǒng)核心狀態(tài)變量
bool systemOn = false;      // 系統(tǒng)開關(guān)狀態(tài),初始為關(guān)閉
bool isIdleMode = true;     // 是否處于待機(jī)模式
int idleEffectMode = 0;     // 待機(jī)效果模式:0=流水燈, 1=呼吸燈
uint8_t globalBrightness = 30;  // 全局亮度控制

void loop()
{
  // 獲取傳感器數(shù)據(jù)
  // ...
  
  // 處理系統(tǒng)開關(guān)邏輯
  if (!systemOn) {
    // 關(guān)機(jī)狀態(tài):只響應(yīng)開啟手勢(shì)
    if (detectedDir == 1) {
      turnOnSystem();
    }
    delay(30);
    return;
  }
  
  // 開機(jī)狀態(tài):優(yōu)先檢查關(guān)閉手勢(shì)
  if (detectedDir == -1) {
    turnOffSystem();
    return;
  }
  
  // 正常的交互邏輯
  // ...
}

狀態(tài)機(jī)流程圖

wKgZO2lSReaAWtSdAALBna9j8Zg684.png

3.3 視覺交互反饋

開機(jī)動(dòng)畫效果

中心向兩側(cè)漸變紫色光效,過渡到全彩虹色

void showStartupEffect() {
  strip.clear();
  strip.show();
  delay(100);
  
  // 從中心向兩側(cè)展開的紫色動(dòng)畫
  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;
    
    // 兩側(cè)對(duì)稱點(diǎn)亮
    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);
}

關(guān)機(jī)效果

關(guān)機(jī)效果為全部燈珠的亮度從當(dāng)前值漸降至 0 后熄滅

void turnOffAllLEDs() {
  // 漸暗效果
  for (int b = globalBrightness; b > 0; b -= 10) {
    strip.setBrightness(b);
    strip.show();
    delay(10);
  }
  strip.clear();
  strip.show();
  globalBrightness = 30;  // 重置亮度
}

狀態(tài)指示燈

初始化提示,閃爍第 0 個(gè)燈珠 3 次提示就緒

void blinkStatusLED(int times, int delayTime) {
  for (int i = 0; i < times; i++) {
    strip.setPixelColor(0, 0, 0, 255);  // 藍(lán)色閃爍
    strip.show();
    delay(delayTime);
    strip.setPixelColor(0, 0, 0, 0);
    strip.show();
    delay(delayTime);
  }
}

3.4 手勢(shì)跟蹤效果

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) {
      // 當(dāng)前位置:白色高亮
      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 {
      // 無尾影:關(guān)閉LED
      strip.setPixelColor(i, 0, 0, 0);
    }
  }
}

3.5 系統(tǒng)待機(jī)

void updateWaterFlowEffect() {
  // 清屏
  for(int i = 0; i < NUM_LEDS; i++) strip.setPixelColor(i, 0, 0, 0);
  
  static int dir = 1;  // 移動(dòng)方向
  idlePosition += dir;
  
  // 邊界處理和方向反轉(zhuǎn)
  if(idlePosition >= NUM_LEDS || idlePosition < 0) {
    dir *= -1;
    idlePosition += dir;
    idleColorIndex = (idleColorIndex + 1) % 7;  // 切換顏色
  }
  
  // 設(shè)置當(dāng)前光點(diǎn)
  uint32_t c = rainbowColors[idleColorIndex];
  strip.setPixelColor(idlePosition, c);
  
  // 注意:這個(gè)版本簡(jiǎn)化了尾跡效果,可根據(jù)需要恢復(fù)
}

uint32_t wheel(uint8_t wheelPos) {
  wheelPos = 255 - wheelPos;  // 反轉(zhuǎn)以獲得更鮮艷的顏色
  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
 * 作者:零知實(shí)驗(yàn)室(深圳市在芯間科技有限公司)
 * -^^- 零知實(shí)驗(yàn)室,讓電子制作變得更簡(jiǎn)單! -^^-
 * 時(shí)間: 2025-12-26
 * 說明: 基于零知標(biāo)準(zhǔn)板(STM32F103RBT6)驅(qū)動(dòng)PAJ7620U2手勢(shì)傳感器實(shí)現(xiàn)WS2812B燈帶控制,
 *       支持手部位置跟蹤的彩虹尾影效果,無手部時(shí)自動(dòng)切換為流水燈/呼吸燈待機(jī)效果
 *       采用"單次讀取"策略,確保在任意狀態(tài)下均能準(zhǔn)確識(shí)別退出手勢(shì)。
 ***************************************************************************************/

#include "RevEng_PAJ7620.h"
#include 

// 手勢(shì)傳感器對(duì)象
RevEng_PAJ7620 sensor = RevEng_PAJ7620();

// LED燈帶配置
#define NUM_LEDS 16
WS2812B strip = WS2812B(NUM_LEDS);

// 系統(tǒng)狀態(tài)
int lastCursorX = 0;
bool isIdleMode = true;
bool systemOn = false;  // 系統(tǒng)開關(guān)狀態(tài),初始為關(guān)閉

// 手部跟蹤變量
float trailEffect[NUM_LEDS] = {0};
float trailDecay = 0.85;

// 待機(jī)效果變量
int idleEffectMode = 0;  // 0:流水燈, 1:呼吸燈
int idleColorIndex = 0;
int idlePosition = 0;
float idlePulse = 0;
unsigned long idleLastUpdate = 0;

// 亮度控制
uint8_t globalBrightness = 30;  // 全局亮度,0-255

// 手勢(shì)檢測(cè)變量
unsigned long lastGestureTime = 0;
const unsigned long GESTURE_COOLDOWN = 800;   // 稍微縮短冷卻時(shí)間提高響應(yīng)
const int GESTURE_THRESHOLD = 1000;            // 調(diào)低閾值使其更容易觸發(fā)
const int GESTURE_SPEED_THRESHOLD = 600;      // 調(diào)低速度閾值

// 坐標(biāo)范圍
const int Y_MIN = 384;
const int Y_MAX = 3455;

// 預(yù)定義顏色(彩虹色)
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),     // 藍(lán)色
  strip.Color(75, 0, 130),    // 靛藍(lán)色
  strip.Color(148, 0, 211)    // 紫色
};

// ***************************************************************************
void setup()
{
  Serial.begin(115200);
  
  // 初始化LED燈帶
  strip.begin();
  strip.clear();
  strip.show();
  
  // 初始化手勢(shì)傳感器
  if(!sensor.begin())
  {
    Serial.println("PAJ7620初始化失敗!");
    while(true) {}
  }
  
  Serial.println("PAJ7620U2初始化成功!");
  sensor.setCursorMode();  // 設(shè)置為光標(biāo)模式
  
  Serial.println("n手勢(shì)控制LED系統(tǒng)已啟動(dòng)!");
  Serial.println("向上快速揮手: 打開系統(tǒng)");
  Serial.println("向下快速揮手: 關(guān)閉系統(tǒng)");
  
  systemOn = false;
  turnOffAllLEDs();
  blinkStatusLED(3, 300);
}

// ***************************************************************************
void loop()
{
  // 在循環(huán)開始時(shí),一次性讀取所有傳感器數(shù)據(jù)
  bool currentInView = sensor.isCursorInView();
  int currentX = 0;
  int currentY = 0;
  
  if (currentInView) {
    currentX = sensor.getCursorX();
    currentY = sensor.getCursorY();
  }

  // 將讀取到的數(shù)據(jù)傳入手勢(shì)檢測(cè)函數(shù) (不讓函數(shù)內(nèi)部再次讀取)
  // 返回值: 0=無, 1=向上(開), -1=向下(關(guān))
  int detectedDir = checkGestureLogic(currentInView, currentY);

  // 處理系統(tǒng)開關(guān)邏輯
  if (!systemOn) {
    // 關(guān)機(jī)狀態(tài):只響應(yīng)開啟手勢(shì)
    if (detectedDir == 1) {
      turnOnSystem();
    }
    delay(30); // 簡(jiǎn)單的防抖延遲
    return;
  }
  
  // 開機(jī)狀態(tài):優(yōu)先檢查關(guān)閉手勢(shì)
  if (detectedDir == -1) {
    turnOffSystem();
    return;
  }

  // 如果沒有開關(guān)機(jī)手勢(shì),執(zhí)行正常的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
  {
    // 待機(jī)模式
    if(!isIdleMode) {
      resetIdleEffects();
      isIdleMode = true;
    }
    updateTrailFast();
    updateIdleEffect();
  }
  
  strip.show();
  delay(30);
}

// ***************************************************************************
// 手勢(shì)檢測(cè)不再讀取傳感器,而是分析傳入的數(shù)據(jù)
// 參數(shù): isPresent(手是否在), y(當(dāng)前Y坐標(biāo))
// 返回: 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();
  
  // 冷卻時(shí)間檢查
  if (currentTime - lastGestureTime < GESTURE_COOLDOWN) {
    return 0;
  }
  
  // 如果手移開了,重置檢測(cè)狀態(tài)
  if (!isPresent) {
    gestureInProgress = false;
    return 0;
  }
  
  // 如果還沒開始檢測(cè),記錄起點(diǎn)
  if (!gestureInProgress) {
    gestureStartY = y;
    gestureStartTime = currentTime;
    gestureInProgress = true;
    return 0; // 剛開始,還沒結(jié)果
  }
  
  // 計(jì)算變化量和速度
  unsigned long duration = currentTime - gestureStartTime;
  int yChange = y - gestureStartY;
  
  // 避免除以零
  if (duration == 0) return 0;
  
  float speed = abs(yChange) / (duration / 1000.0);
  
  // 只有當(dāng)持續(xù)時(shí)間足夠短且速度足夠快時(shí),才認(rèn)為是手勢(shì)
  // 增加 duration > 50 是為了避免極其短暫的噪點(diǎn)
  if (duration > 50 && speed > GESTURE_SPEED_THRESHOLD) {
    
    // 向上快速移動(dòng) (Y值減小)
    if (yChange < -GESTURE_THRESHOLD) {
      Serial.println("檢測(cè)到: 向上揮手 (開啟)");
      lastGestureTime = currentTime;
      gestureInProgress = false;
      return 1;
    }
    
    // 向下快速移動(dòng) (Y值增加)
    if (yChange > GESTURE_THRESHOLD) {
      Serial.println("檢測(cè)到: 向下?lián)]手 (關(guān)閉)");
      lastGestureTime = currentTime;
      gestureInProgress = false;
      return -1;
    }
  }
  
  // 超時(shí)重置 (如果動(dòng)作太慢,就視為普通移動(dòng)而非手勢(shì))
  if (duration > 800) {
    gestureInProgress = false; // 重置,這也允許連續(xù)跟蹤而不誤觸發(fā)
  }
  
  return 0;
}

// ***************************************************************************
// 打開系統(tǒng)
void turnOnSystem() {
  systemOn = true;
  Serial.println(">>> 系統(tǒng)啟動(dòng)");
  
  resetIdleEffects();
  clearTrailEffects();
  globalBrightness = 30;
  strip.setBrightness(globalBrightness);
  
  showStartupEffect();
}

// ***************************************************************************
// 關(guān)閉系統(tǒng)
void turnOffSystem() {
  systemOn = false;
  Serial.println(">>> 系統(tǒng)關(guān)閉");
  turnOffAllLEDs();
}

// ***************************************************************************
// 啟動(dòng)效果
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 燈帶平滑漸暗關(guān)閉
void turnOffAllLEDs() {
  for (int b = globalBrightness; b > 0; b -= 10) {
    strip.setBrightness(b);
    strip.show();
    delay(10);
  }
  strip.clear();
  strip.show();
  globalBrightness = 30;
}

// ***************************************************************************
// 清除所有手勢(shì)跟蹤的軌跡變量,重置手勢(shì)軌跡效果
void clearTrailEffects() {
  for(int i = 0; i < NUM_LEDS; i++) trailEffect[i] = 0;
}

// ***************************************************************************
// 控制第1個(gè)LED(索引0)按指定次數(shù)和間隔閃爍,用于設(shè)備狀態(tài)初始化成功提示
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);
  }
}

// ***************************************************************************
// 緩慢衰減軌跡強(qiáng)度,用于手勢(shì)效果
void updateTrail() {
  for(int i = 0; i < NUM_LEDS; i++) {
    trailEffect[i] *= trailDecay;
    if(trailEffect[i] < 0.01) trailEffect[i] = 0;
  }
}

// ***************************************************************************
// 快速衰減軌跡強(qiáng)度,用于待機(jī)效果
void updateTrailFast() {
  for(int i = 0; i < NUM_LEDS; i++) {
    trailEffect[i] *= 0.6;
    if(trailEffect[i] < 0.01) trailEffect[i] = 0;
  }
}

// ***************************************************************************
// 根據(jù)手勢(shì)坐標(biāo)(x,y)更新LED燈帶的手勢(shì)跟蹤顯示效果
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);  // 手勢(shì)當(dāng)前位置的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);
    }
  }
}

// ***************************************************************************
// 重置所有待機(jī)狀態(tài)特效的參數(shù)
void resetIdleEffects() {
  idlePosition = 0;
  idlePulse = 0;
  idleColorIndex = 0;
  idleLastUpdate = millis();
  idleEffectMode = random(0, 2);
}

// ***************************************************************************
// 每5秒自動(dòng)切換空閑狀態(tài)模式
void updateIdleEffect() {
  unsigned long currentTime = millis();
  if(currentTime - idleLastUpdate > 5000) {
    idleEffectMode = (idleEffectMode + 1) % 2;
    idleLastUpdate = currentTime;
  }
  if(idleEffectMode == 0) updateWaterFlowEffect();
  else updateBreathingEffect();
}

// ***************************************************************************
// 空閑狀態(tài)流水燈效果
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);
}

// ***************************************************************************
// 空閑狀態(tài)呼吸燈效果
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 之間的亮度系數(shù)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);
}

// ***************************************************************************
// 生成對(duì)應(yīng)的彩虹顏色RGB,實(shí)現(xiàn)HSV到RGB的簡(jiǎn)易轉(zhuǎn)換
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);
}

/******************************************************************************
 * 深圳市在芯間科技有限公司
 * 淘寶店鋪:在芯間科技零知板
 * 店鋪網(wǎng)址:https://shop533070398.taobao.com
 * 版權(quán)說明:
 *  1.本代碼的版權(quán)歸【深圳市在芯間科技有限公司】所有,僅限個(gè)人非商業(yè)性學(xué)習(xí)使用。
 *  2.嚴(yán)禁將本代碼或其衍生版本用于任何商業(yè)用途(包括但不限于產(chǎn)品開發(fā)、付費(fèi)服務(wù)、企業(yè)內(nèi)部使用等)。
 *  3.任何商業(yè)用途均需事先獲得【深圳市在芯間科技有限公司】的書面授權(quán),未經(jīng)授權(quán)的商業(yè)使用行為將被視為侵權(quán)。
******************************************************************************/

系統(tǒng)流程圖

wKgZPGlSRmyAVE5YAAHjzL7wzCU124.png

四、操作流程

4.1 系統(tǒng)操作

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

?

wKgZO2lSRrWAR7oAAAH-XFKWRbY671.png

在傳感器上方快速向上揮手開啟系統(tǒng),在開機(jī)狀態(tài)下,快速向下?lián)]手關(guān)閉系統(tǒng),LED燈帶會(huì)逐漸變暗關(guān)閉

手勢(shì)控制:將手放在傳感器上方5-10cm處,LED燈帶會(huì)對(duì)應(yīng)顯示白色光標(biāo);左右平行移動(dòng)手部,光標(biāo)跟隨移動(dòng)并留下彩虹色尾影

wKgZO2lSRsKAXp1pAAE-0bJrWfs907.png

手部離開傳感器區(qū)域,自動(dòng)進(jìn)入待機(jī)模式,每5秒在流水燈和呼吸燈之間切換

?

靈敏度調(diào)整

// 如果手勢(shì)不易觸發(fā),降低閾值
const int GESTURE_THRESHOLD = 800;      // 降低幅度要求
const int GESTURE_SPEED_THRESHOLD = 400; // 降低速度要求

// 如果誤觸發(fā)過多,增加閾值
const int GESTURE_THRESHOLD = 1200;     // 增加幅度要求
const int GESTURE_SPEED_THRESHOLD = 800; // 增加速度要求

視覺效果調(diào)整

// 調(diào)整尾影持續(xù)時(shí)間
float trailDecay = 0.9;  // 值越大,尾影持續(xù)時(shí)間越長(zhǎng)

// 調(diào)整亮度范圍
int brightness = map(currentY, Y_MIN, Y_MAX, 20, 100);  // 縮小亮度變化范圍

4.2 視頻演示

?https://www.bilibili.com/video/BV1uivsBBEJo/?vd_source=a31e3d8d8ce008260eee442534c2f63d

系統(tǒng)初始化燈帶閃爍狀態(tài)燈提示就緒,手勢(shì)跟蹤模式下白色光點(diǎn)跟隨手部 X 軸,亮度隨手部 Y 軸平滑變化,尾影效果自然;移開手部自動(dòng)切換流水燈 / 呼吸燈

五、PAJ7620U2 手勢(shì)傳感器知識(shí)點(diǎn)講解

PAJ7620U2是一款內(nèi)置光源和環(huán)境光抑制濾波器集成的 LED,鏡頭和手勢(shì)感測(cè)器在一個(gè)小的立方體模組,能在黑暗或低光環(huán)境下工作,其模塊功能框圖如下圖所示

wKgZPGlSTAWAPYu3AACINiJImSc665.png

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

5.1 軟件I2C通信

MCU 通過 I2C 接口來控制 PAJ7620U2,PAJ7620U2 工作時(shí)通過內(nèi)部 LED 驅(qū)動(dòng)器

I2C 時(shí)序參數(shù)

wKgZPGlSTCqAae1SAADW0-A1mwE503.png

參數(shù)說明 符號(hào) 標(biāo)準(zhǔn)模式 快速模式 單位
SCL 時(shí)鐘頻率 fscl 10 ~ 100 10 ~ 400 kHz
起始/重復(fù)起始條件保持時(shí)間(此后產(chǎn)生第一個(gè)時(shí)鐘脈沖) tHD,STA ≥ 4 ≥ 0.6 μs
重復(fù)起始條件建立時(shí)間 tSU,STA ≥ 4.7 ≥ 0.6 μs
SCL 時(shí)鐘低電平時(shí)間 tLOW ≥ 4.7 ≥ 1.3 μs
SCL 時(shí)鐘高電平時(shí)間 tHIGH ≥ 4 ≥ 0.6 μs
數(shù)據(jù)保持時(shí)間 tHD,DAT? ≥ 0 ≥ 0 μs
數(shù)據(jù)建立時(shí)間 tSU,DATt ≥ 250 ≥ 100 ns
SDA 與 SCL 信號(hào)上升時(shí)間 tr ≤ 1000 ≤ 300 ns
SDA 與 SCL 信號(hào)下降時(shí)間 tf ≤ 300 ≤ 300 ns
停止條件建立時(shí)間 tSU,STO ≥ 4 ≥ 0.6 μs
停止條件到起始條件的總線空閑時(shí)間 tBUF ≥ 4.7 ≥ 1.3 μs

當(dāng)傳感器陣列在有效的距離中探測(cè)到物體時(shí),目標(biāo)信息提取陣列會(huì)對(duì)探測(cè)目標(biāo)進(jìn)行特征原始數(shù)據(jù)的獲取,獲取的數(shù)據(jù)會(huì)存在寄存器中,同時(shí)手勢(shì)識(shí)別陣列會(huì)對(duì)原始數(shù)據(jù)進(jìn)行識(shí)別處理,最后將手勢(shì)結(jié)果存到寄存器中,用戶可根據(jù) I2C 接口對(duì)原始數(shù)據(jù)和手勢(shì)識(shí)別的結(jié)果進(jìn)行讀取

光標(biāo)模式數(shù)據(jù)讀取時(shí)序

wKgZO2lSTI6AbsIPAAFkdOAv6Oo596.png

5.2 寄存器配置

PAJ7620U2 的內(nèi)部有兩個(gè) BANK 寄存器區(qū)域,分別為 BANK0 和 BANK1。不同的區(qū)域用于訪問不同的功能寄存器,訪問某一 BANK 區(qū)域下的寄存器前需發(fā)送控制指令進(jìn)入該寄存器區(qū)域

wKgZPGlSTLGAH7C_AADjbYwbEV8102.png

進(jìn)入 BANK0 區(qū)域需向傳感器 0xEF 地址寫 0x00,而 BANK1 區(qū)域需向傳感器 0xEF 地址寫 0x01

使能工作寄存器

wKgZO2lSTOqAe7oJAAD1-Wgfb7c212.png

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

手勢(shì)檢測(cè)輸出中斷使能寄存器

wKgZO2lSTNqAaSriAAE84pQNuJE265.png

該寄存器地址為 0X41,用于手勢(shì)識(shí)別,bit0~bit7 位用于使能不同手勢(shì)識(shí)別結(jié)果的中斷輸出,默認(rèn)值為 0XFF

六、常見問題解答(FAQ)

Q1:手勢(shì)開關(guān)機(jī)偶爾誤觸發(fā)?

A:解決方案:增大GESTURE_THRESHOLD 或GESTURE_SPEED_THRESHOLD 閾值,提高觸發(fā)門檻;延長(zhǎng)GESTURE_COOLDOWN(如 1000ms),避免短時(shí)間重復(fù)觸發(fā)

Q2:如何增加更多LED?

A:修改步驟:更新NUM_LEDS定義,調(diào)整trailEffect數(shù)組大小,可能需要增加電源功率

項(xiàng)目資源整合

PAJ7620U2 數(shù)據(jù)手冊(cè): https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf

PAJ7620U2 庫(kù)文件: https://github.com/acrandal/RevEng_PAJ7620


審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • RGB
    RGB
    +關(guān)注

    關(guān)注

    4

    文章

    831

    瀏覽量

    61924
  • IDE
    IDE
    +關(guān)注

    關(guān)注

    0

    文章

    365

    瀏覽量

    49050
  • STM32F103RBT6
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    7796
  • WS2812
    +關(guān)注

    關(guān)注

    0

    文章

    35

    瀏覽量

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

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    IDE——基于標(biāo)準(zhǔn)板驅(qū)動(dòng)PAJ7620U2手勢(shì)控制L9110風(fēng)扇模塊和SG90舵機(jī)系統(tǒng)

    * 說明:標(biāo)準(zhǔn)板(STM32F103RBT6) + PAJ7620U2 + L9110 手勢(shì)控制
    發(fā)表于 01-06 11:56

    STM32驅(qū)動(dòng)PAJ7620手勢(shì)識(shí)別傳感器

    原文鏈接:[https://www.yourcee.com/newsinfo/2925937.html] 傳感器簡(jiǎn)介 PAJ-7620U2是由原相科技公司開發(fā)的一款手勢(shì)識(shí)別芯片, 內(nèi)集成了光學(xué)數(shù)組式
    的頭像 發(fā)表于 01-06 09:16 ?704次閱讀
    <b class='flag-5'>STM32</b>驅(qū)動(dòng)<b class='flag-5'>PAJ7620</b><b class='flag-5'>手勢(shì)</b>識(shí)別傳感器

    CW32L012/F030靈眸X1智能小車——板載WS2812驅(qū)動(dòng)示例

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

    IDE——基于STM32F103RBT6PAJ7620U2手勢(shì)控制WS2812 RGB系統(tǒng)

    簡(jiǎn)單! -^^- * 時(shí)間: 2025-12-26 * 說明: 基于標(biāo)準(zhǔn)板(STM32F103RBT6)驅(qū)動(dòng)PAJ7620U2手勢(shì)
    發(fā)表于 12-29 19:04

    標(biāo)準(zhǔn)板+PAJ7620U2手勢(shì)控制WS2812 RGB

    RGB
    PCB56242069
    發(fā)布于 :2025年12月29日 17:17:34

    【瑞薩RA6E2地奇星開發(fā)板試用】點(diǎn)亮 WS2812 全彩點(diǎn)陣屏

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

    基于STM32F103C8T6驅(qū)動(dòng)WS2812彩燈模塊點(diǎn)亮RGB

    一、WS2812模塊簡(jiǎn)介 WS2812是一個(gè)集控制電路與發(fā)光電路于一體的智能外控LED光源。其外型與一個(gè)5050LED珠相同,每個(gè)元件即為一個(gè)像素點(diǎn)。像素點(diǎn)內(nèi)部包含了智能數(shù)字接口數(shù)據(jù)
    的頭像 發(fā)表于 12-04 11:29 ?6597次閱讀
    基于<b class='flag-5'>STM32F103C8T6</b>驅(qū)動(dòng)<b class='flag-5'>WS2812</b>彩燈模塊點(diǎn)亮<b class='flag-5'>RGB</b><b class='flag-5'>燈</b>

    【瑞薩RA6E2】驅(qū)動(dòng) WS2812 實(shí)現(xiàn) RGB 跑馬燈效果

    首先說明硬件接線調(diào)整:WS2812 珠串聯(lián)時(shí),第一個(gè)珠的 DI 引腳連接 RA6E2 的 P001 引腳,VCC 接開發(fā)板 5V 電源,GND 與開發(fā)板 GND 共地,后續(xù)
    發(fā)表于 11-25 01:55

    【瑞薩RA6E2】點(diǎn)亮 WS2812 全彩點(diǎn)陣屏

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

    開源——STM32F103RBT6驅(qū)動(dòng) ICM20948 九軸傳感器及 vofa + 上位機(jī)可視化教程

    STM32F1本教程使用標(biāo)準(zhǔn)板(STM32F103RBT6)通過I2C驅(qū)動(dòng)ICM20948九軸傳感器,實(shí)現(xiàn)姿態(tài)解算,并通過串口將數(shù)據(jù)實(shí)時(shí)
    發(fā)表于 06-09 14:53

    開源——STM32F103RBT6驅(qū)動(dòng) ICM20948 九軸傳感器及 vofa + 上位機(jī)可視化教程

    本教程使用標(biāo)準(zhǔn)板(STM32F103RBT6)通過I2C驅(qū)動(dòng)ICM20948九軸傳感器,實(shí)現(xiàn)姿態(tài)解算,并通過串口將數(shù)據(jù)實(shí)時(shí)發(fā)送至VOFA+上位機(jī)進(jìn)行3D可視化。
    的頭像 發(fā)表于 06-09 14:01 ?1860次閱讀
    <b class='flag-5'>零</b><b class='flag-5'>知</b>開源——<b class='flag-5'>STM32F103RBT6</b>驅(qū)動(dòng) ICM20948 九軸傳感器及 vofa + 上位機(jī)可視化教程

    【RA4L1-SENSOR】點(diǎn)亮 WS2812 全彩點(diǎn)陣屏

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

    【RA-Eco-RA4M2開發(fā)板評(píng)測(cè)】點(diǎn)亮WS2812點(diǎn)陣屏

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

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

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

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

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