? ?零知開源(零知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í)物,來(lái)動(dòng)手試試吧!
?訪問零知實(shí)驗(yàn)室,獲取更多實(shí)戰(zhàn)項(xiàng)目和教程資源吧!
www.lingzhilab.com
?
項(xiàng)目概述
本項(xiàng)目使用零知標(biāo)準(zhǔn)板(主控芯片:STM32F103RBT6)作為核心控制器,結(jié)合PAJ7620U2手勢(shì)傳感器實(shí)現(xiàn)對(duì)L9110風(fēng)扇模塊和SG90舵機(jī)的智能控制。系統(tǒng)通過識(shí)別9種不同的手勢(shì)動(dòng)作(上下、左右、順時(shí)針/逆時(shí)針、揮手、前推、后拉)分別控制風(fēng)扇的啟停、正反轉(zhuǎn)、調(diào)速以及舵機(jī)的精確角度定位,實(shí)現(xiàn)了無(wú)接觸式智能交互體驗(yàn)
項(xiàng)目難點(diǎn)及解決方案
問題描述:零知標(biāo)準(zhǔn)板的analogWrite()函數(shù)導(dǎo)致系統(tǒng)卡死
解決方案:放棄analogWrite()函數(shù),手動(dòng)配置STM32硬件定時(shí)器,直接操作定時(shí)器寄存器
一、系統(tǒng)接線部分
1.1 硬件清單
| 名稱 | 型號(hào)/參數(shù) | 數(shù)量 | 說明 |
|---|---|---|---|
| 主控板 | 零知標(biāo)準(zhǔn)板 (STM32F103RBT6) | 1 | 核心控制器 |
| 擴(kuò)展板 | 零知標(biāo)準(zhǔn)板-擴(kuò)展板 | 1 | 傳感器擴(kuò)展板 |
| 手勢(shì)傳感器 | PAJ7620U2 | 1 | I2C接口,識(shí)別9種手勢(shì) |
| 風(fēng)扇驅(qū)動(dòng)模塊 | L9110 / L9110S | 1 | 雙路H橋,控制電機(jī)正反轉(zhuǎn) |
| 舵機(jī) | SG90 (180度) | 1 | 控制風(fēng)向擺動(dòng) |
| 杜邦線 | 公對(duì)母/公對(duì)公 | 若干 | 連接線 |
1.2 接線方案表
注意:請(qǐng)嚴(yán)格按照以下代碼定義的引腳進(jìn)行連接,否則程序無(wú)法正常工作。
| 模塊 | 引腳名稱 | 連接到零知標(biāo)準(zhǔn)板 (STM32) | 功能說明 |
|---|---|---|---|
| PAJ7620U2 | VIN | 3.3V | 通常是3.3V邏輯電平 |
| GND | GND | 地線 | |
| SCL | SCL (或?qū)?yīng)I2C SCL) | I2C 時(shí)鐘線 | |
| SDA | SDA (或?qū)?yīng)I2C SDA) | I2C 數(shù)據(jù)線 | |
| SG90 舵機(jī) | 信號(hào)線 (橙) | 12 | PWM控制信號(hào) |
| VCC (紅) | 3.3V(直插拓展板) | 電源正 | |
| GND (棕) | GND | 電源地 | |
| L9110 風(fēng)扇 | INA | 9(PB7) | 電機(jī)控制腳A |
| INB | 5(PB6) | 電機(jī)控制腳B | |
| VCC | 5V (建議外接) | 電源正 | |
| GND | GND | 電源地 |
PS:本項(xiàng)目采用擴(kuò)展板直插零知標(biāo)準(zhǔn)板,請(qǐng)注意I2C接口線序,與開發(fā)板定義的內(nèi)容不一致,需要將外接的帶鎖扣端子轉(zhuǎn)杜邦線調(diào)整為VIN、GND、SCL和SDA;舵機(jī)直插D12 PWM接口,舵機(jī)黃色信號(hào)線靠近''D12''絲印一端
?
1.3 具體接線圖

請(qǐng)注意:如果風(fēng)扇使用外部電源,務(wù)必將外部電源的負(fù)極(-)連接到零知標(biāo)準(zhǔn)板的 GND,否則控制信號(hào)無(wú)法形成回路
?
1.4 接線實(shí)物圖

二、安裝與使用部分
2.1 開源平臺(tái)-輸入"PAJ7620U2"并搜索-代碼下載自動(dòng)打開

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

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

三、代碼講解部分
本項(xiàng)目的代碼結(jié)構(gòu)清晰,采用了模塊化設(shè)計(jì),代碼從初始化、手勢(shì)處理邏輯和硬件控制部分展開
3.1 軟件I2C配置
//1. I2C寫寄存器
uint8_t RevEng_PAJ7620::writeRegister(uint8_t i2cAddress, uint8_t dataByte) {
wireHandle->beginTransmission(PAJ7620_I2C_BUS_ADDR); // 0x73
wireHandle->write(i2cAddress); // 寄存器地址
wireHandle->write(dataByte); // 數(shù)據(jù)
return wireHandle->endTransmission();
}
//2. I2C讀寄存器
uint8_t RevEng_PAJ7620::readRegister(uint8_t i2cAddress, uint8_t byteCount, uint8_t data[]) {
wireHandle->beginTransmission(PAJ7620_I2C_BUS_ADDR);
wireHandle->write(i2cAddress);
uint8_t result = wireHandle->endTransmission();
if (result) return result; // 通信錯(cuò)誤
wireHandle->requestFrom((int)PAJ7620_I2C_BUS_ADDR, (int)byteCount);
while (wireHandle->available()) {
*data = wireHandle->read();
data++;
}
return 0;
}
軟件I2C通過GPIO模擬I2C時(shí)序,雖然速度略慢,但穩(wěn)定性更高
3.2平滑移動(dòng)算法
// 平滑移動(dòng)舵機(jī)(防止舵機(jī)快速轉(zhuǎn)動(dòng)時(shí)抖動(dòng)或損壞)
void moveServoSmoothly() {
int step = 2; // 默認(rèn)每次移動(dòng)2度
// 根據(jù)目標(biāo)位置調(diào)整移動(dòng)步長(zhǎng)
// 如果是移動(dòng)到0度或180度(左右手勢(shì)),使用較大步長(zhǎng)實(shí)現(xiàn)快速響應(yīng)
if (targetServoPos == 0 || targetServoPos == 180) {
step = 5; // 大步長(zhǎng),快速移動(dòng)
}
// 根據(jù)當(dāng)前位置和目標(biāo)位置的關(guān)系,逐步移動(dòng)
if (currentServoPos < targetServoPos) {
// 當(dāng)前位置小于目標(biāo)位置,向右轉(zhuǎn)
currentServoPos = min(currentServoPos + step, targetServoPos);
}
else if (currentServoPos > targetServoPos) {
// 當(dāng)前位置大于目標(biāo)位置,向左轉(zhuǎn)
currentServoPos = max(currentServoPos - step, targetServoPos);
}
myServo.write(currentServoPos); // 寫入新位置
delay(15); // 給舵機(jī)一點(diǎn)時(shí)間響應(yīng)
}
一種非阻塞式的控制思路,利用 loop() 的快速刷新特性實(shí)現(xiàn)了類似PID控制的緩啟緩?fù)PЧ?/p>
3.3風(fēng)扇控制算法
void controlFan(int speed, int direction) { // 限制速度在有效范圍 [0, 255] speed = constrain(speed, 0, FAN_MAX_SPEED); if (direction == 1) { // 反轉(zhuǎn): IA=0, IB=PWM setPWM(FAN_IA_PIN, 0); setPWM(FAN_IB_PIN, speed); fanDirection = 1; } else if (direction == -1) { // 正轉(zhuǎn): IA=PWM, IB=0 setPWM(FAN_IA_PIN, speed); setPWM(FAN_IB_PIN, 0); fanDirection = -1; } else { // 停止: IA=0, IB=0 setPWM(FAN_IA_PIN, 0); setPWM(FAN_IB_PIN, 0); fanDirection = 0; } fanSpeed = speed; }
H橋驅(qū)動(dòng)原理
L9110內(nèi)部包含一個(gè)H橋電路,通過控制4個(gè)開關(guān)管實(shí)現(xiàn)電機(jī)正反轉(zhuǎn)
IA=HIGH, IB=LOW → 正轉(zhuǎn)IA=LOW, IB=HIGH → 反轉(zhuǎn)
IA=LOW, IB=LOW → 停止IA=HIGH, IB=HIGH → 剎車(不常用)
3.4 PWM定時(shí)器手動(dòng)配置
定時(shí)器工作原理
PWM頻率 = 時(shí)鐘頻率 / (預(yù)分頻系數(shù) × 重裝載值)
= 72MHz / (1 × 65535) ≈ 1098Hz
占空比 = 比較值 / 重裝載值 × 100%
// ============ PWM初始化 ============
void initPWMTimer() {
Serial.println("[PWM] 初始化定時(shí)器...");
// 配置引腳為PWM模式
pinMode(FAN_IA_PIN, PWM); // 引腳9 (PB7)
pinMode(FAN_IB_PIN, PWM); // 引腳5 (PB6)
// 暫停定時(shí)器進(jìn)行配置
Timer4.pause();
// 設(shè)置PWM參數(shù)
Timer4.setPrescaleFactor(1); // 預(yù)分頻=1(不分頻)
Timer4.setOverflow(65535); // ARR=65535(16位最大)
// 初始化比較值(占空比0%)
Timer4.setCompare(TIMER_CH1, 0); // CCR1=0 (引腳5)
Timer4.setCompare(TIMER_CH2, 0); // CCR2=0 (引腳9)
// 刷新寄存器并啟動(dòng)定時(shí)器
Timer4.refresh();
Timer4.resume();
Serial.println("[PWM] 定時(shí)器初始化完成");
}
// ============ PWM占空比設(shè)置 ============
void setPWM(int pin, uint8_t dutyCycle) {
// 將0-255映射到0-65535
uint16_t compareValue = (uint32_t)dutyCycle * 65535 / 255;
if (pin == FAN_IA_PIN) {
Timer4.setCompare(TIMER_CH2, compareValue); // 引腳9
} else if (pin == FAN_IB_PIN) {
Timer4.setCompare(TIMER_CH1, compareValue); // 引腳5
}
}
參數(shù)說明
| 參數(shù) | 含義 | 取值范圍 | 本項(xiàng)目設(shè)置 |
|---|---|---|---|
| 預(yù)分頻系數(shù) | 時(shí)鐘分頻倍數(shù) | 1-65536 | 1(不分頻) |
| 重裝載值(ARR) | 計(jì)數(shù)器最大值 | 1-65535 | 65535(最大分辨率) |
| 比較值(CCR) | 高電平持續(xù)計(jì)數(shù) | 0-ARR | 0-65535 |
| 占空比 | CCR/ARR | 0%-100% | 用戶輸入0-255映射 |
3.5 完整代碼
/************************************************************************************** * 文件: /Gesture_Control_Servo_Fan/Gesture_Control_Servo_Fan.ino * 作者:零知實(shí)驗(yàn)室(深圳市在芯間科技有限公司) * -^^- 零知實(shí)驗(yàn)室,讓電子制作變得更簡(jiǎn)單! -^^- * 時(shí)間: 2025-12-30 * 說明:零知標(biāo)準(zhǔn)板(STM32F103RBT6) + PAJ7620U2 + L9110 手勢(shì)控制系統(tǒng) * 功能:手勢(shì)控制舵機(jī)(12號(hào)引腳)和風(fēng)扇(5,9號(hào)引腳) * 向上-風(fēng)扇正轉(zhuǎn),向下-舵機(jī)90°,向左-舵機(jī)0°,向右-舵機(jī)180° * 順時(shí)針-風(fēng)扇正轉(zhuǎn),逆時(shí)針-風(fēng)扇反轉(zhuǎn),揮手-風(fēng)扇停止 * 向前-風(fēng)扇加速,向后-風(fēng)扇減速 ***************************************************************************************/ #include #include #include "RevEng_PAJ7620.h" // 對(duì)象創(chuàng)建 RevEng_PAJ7620 sensor; Servo myServo; // 引腳定義 const int SERVO_PIN = 12; // 風(fēng)扇引腳 - PB6(引腳5)和PB7(引腳9)對(duì)應(yīng)Timer4 const int FAN_IB_PIN = 5; // PB6 - TIM4_CH1 const int FAN_IA_PIN = 9; // PB7 - TIM4_CH2 // 系統(tǒng)參數(shù) #define FAN_MIN_SPEED 80 // 風(fēng)扇最低啟動(dòng)速度 #define FAN_MAX_SPEED 255 // 風(fēng)扇最大速度 #define SPEED_STEP 175 // 每次調(diào)速的步長(zhǎng) // 狀態(tài)變量 int currentServoPos = 90; // 舵機(jī)當(dāng)前位置(角度) int targetServoPos = 90; // 舵機(jī)目標(biāo)位置(角度) int fanSpeed = 0; // 當(dāng)前風(fēng)扇速度(0-255) int fanDirection = 0; // 風(fēng)扇方向:0=停止,1=正轉(zhuǎn),-1=反轉(zhuǎn) // 手勢(shì)檢測(cè)冷卻時(shí)間,防止重復(fù)觸發(fā) unsigned long lastGestureTime = 0; const unsigned long GESTURE_COOLDOWN = 500; // 毫秒 bool systemReady = false; // 系統(tǒng)是否就緒 // 系統(tǒng)狀態(tài)標(biāo)志 // ==================== 初始化函數(shù) ==================== void setup() { // 第1步:初始化串口通信 initSerial(); // 第2步:初始化I2C總線(PAJ7620傳感器需要) initI2C(); // 第3步:初始化舵機(jī) initServo(); // 第4步:初始化風(fēng)扇控制引腳 initFan(); // 第5步:初始化手勢(shì)傳感器 initGestureSensor(); // 第6步:顯示功能說明 printFunctionMenu(); // 第7步:系統(tǒng)就緒提示 systemStartupComplete(); systemReady = true; // 標(biāo)記系統(tǒng)已就緒 } // ==================== 主循環(huán)函數(shù) ==================== void loop() { unsigned long currentTime = millis(); // 檢測(cè)手勢(shì)(帶冷卻時(shí)間,避免同一個(gè)手勢(shì)重復(fù)觸發(fā)) if (currentTime - lastGestureTime > GESTURE_COOLDOWN) { Gesture gesture = sensor.readGesture(); // 讀取當(dāng)前手勢(shì) // 如果檢測(cè)到有效手勢(shì),則處理 if (gesture != GES_NONE) { lastGestureTime = currentTime; // 更新最后手勢(shì)時(shí)間 handleGesture(gesture); // 調(diào)用手勢(shì)處理函數(shù) } } // 平滑移動(dòng)舵機(jī)到目標(biāo)位置(每次循環(huán)移動(dòng)一小步) if (currentServoPos != targetServoPos) { moveServoSmoothly(); } delay(50); // 主循環(huán)延遲,不要太短以免CPU負(fù)擔(dān)過重 } // ==================== PWM相關(guān)函數(shù) ==================== // 設(shè)置PWM占空比 void setPWM(int pin, uint8_t dutyCycle) { // 計(jì)算比較值:dutyCycle / 255 * overflow uint16_t compareValue = (uint32_t)dutyCycle * 65535 / 255; if (pin == FAN_IA_PIN) { // 引腳9 (PB7) 使用 Timer4 Channel2 Timer4.setCompare(TIMER_CH2, compareValue); } else if (pin == FAN_IB_PIN) { // 引腳5 (PB6) 使用 Timer4 Channel1 Timer4.setCompare(TIMER_CH1, compareValue); } } // 初始化PWM定時(shí)器 void initPWMTimer() { Serial.println("[PWM] 初始化定時(shí)器..."); // 配置引腳為PWM模式 pinMode(FAN_IA_PIN, PWM); // 引腳9 (PB7) pinMode(FAN_IB_PIN, PWM); // 引腳5 (PB6) // 暫停定時(shí)器4進(jìn)行配置 Timer4.pause(); // 設(shè)置PWM參數(shù) // 72MHz / 1 / 65535 ≈ 1098Hz Timer4.setPrescaleFactor(1); // 不分頻 Timer4.setOverflow(65535); // 16位最大分辨率 // 初始化占空比為0(風(fēng)扇停止) Timer4.setCompare(TIMER_CH1, 0); // 引腳5 (FAN_IB_PIN) Timer4.setCompare(TIMER_CH2, 0); // 引腳9 (FAN_IA_PIN) // 刷新并啟動(dòng)定時(shí)器 Timer4.refresh(); Timer4.resume(); Serial.println("[PWM] 定時(shí)器初始化完成 (引腳5=PB6/CH1, 引腳9=PB7/CH2)"); } // ==================== 初始化函數(shù)詳細(xì)實(shí)現(xiàn) ==================== // 初始化串口通信 void initSerial() { Serial.begin(115200); delay(300); // 等待串口穩(wěn)定 Serial.println("n╔═══════════════════╗"); Serial.println("║ 零知實(shí)驗(yàn)室 - 手勢(shì)控制系統(tǒng) V2.0 ║"); Serial.println("╚═══════════════════╝n"); Serial.println("【系統(tǒng)初始化開始】n"); } // 初始化I2C總線 void initI2C() { Serial.print("[1/5] I2C總線初始化..."); Wire.begin(); delay(100); Serial.println(" ?"); } // 初始化舵機(jī) void initServo() { Serial.print("[2/5] 舵機(jī)初始化..."); myServo.attach(SERVO_PIN); myServo.write(currentServoPos); // 設(shè)置初始位置90度 delay(500); // 等待舵機(jī)轉(zhuǎn)到初始位置 Serial.print(" ? (初始位置: "); Serial.print(currentServoPos); Serial.println("°)"); } // 初始化風(fēng)扇控制引腳 void initFan() { Serial.print("[3/5] 風(fēng)扇模塊初始化..."); // 先初始化PWM定時(shí)器 initPWMTimer(); pinMode(LED_BUILTIN, OUTPUT); // 確保風(fēng)扇初始狀態(tài)為停止 controlFan(0, 0); delay(200); Serial.println(" ? (狀態(tài): 停止)"); } // 初始化PAJ7620手勢(shì)傳感器 void initGestureSensor() { Serial.println("[4/5] PAJ7620手勢(shì)傳感器初始化..."); bool sensorInitialized = false; // 嘗試5次初始化 for (int attempt = 1; attempt <= 5; attempt++) { Serial.print(" 嘗試 "); Serial.print(attempt); Serial.print("/5..."); if (sensor.begin()) { sensorInitialized = true; Serial.println(" ? 成功!"); break; } Serial.println(" ? 失敗"); delay(500); } // 如果初始化失敗,進(jìn)入錯(cuò)誤處理 if (!sensorInitialized) { handleSensorInitError(); } } // 傳感器初始化失敗處理 void handleSensorInitError() { Serial.println("n╔════════════════════╗"); Serial.println("║ ? PAJ7620初始化失敗! ║"); Serial.println("╚════════════════════╝"); Serial.println("n【故障排查清單】"); Serial.println(" □ 1. 傳感器VCC是否接3.3V(不能接5V!)"); Serial.println(" □ 2. GND是否正確接地"); Serial.println(" □ 3. SDA和SCL引腳是否正確連接"); Serial.println(" □ 4. 杜邦線接觸是否良好"); Serial.println(" □ 5. 傳感器與開發(fā)板距離不要太遠(yuǎn)"); Serial.println(" □ 6. 檢查傳感器是否損壞(聞是否有燒焦味)"); Serial.println("n系統(tǒng)已停止運(yùn)行,請(qǐng)修復(fù)后重新上電。n"); // LED快速閃爍表示錯(cuò)誤狀態(tài) while (1) { digitalWrite(LED_BUILTIN, HIGH); delay(50); digitalWrite(LED_BUILTIN, LOW); delay(50); } } // 顯示功能菜單 void printFunctionMenu() { Serial.println("[5/5] 系統(tǒng)配置加載... ?n"); Serial.println("n========================================"); Serial.println(" 手勢(shì)功能說明"); Serial.println("========================================"); Serial.println(" 向上 ↑ : 風(fēng)扇正轉(zhuǎn)啟動(dòng)"); Serial.println(" 向下 ↓ : 系統(tǒng)復(fù)位(舵機(jī)90°)"); Serial.println(" 向左 ← : 舵機(jī)轉(zhuǎn)到0°"); Serial.println(" 向右 → : 舵機(jī)轉(zhuǎn)到180°"); Serial.println(" 順時(shí)針 ? : 風(fēng)扇正轉(zhuǎn)"); Serial.println(" 逆時(shí)針 ? : 風(fēng)扇反轉(zhuǎn)"); Serial.println(" 揮手 ? : 風(fēng)扇停止"); Serial.println(" 向前 ? : 風(fēng)扇加速"); Serial.println(" 向后 ? : 風(fēng)扇減速"); Serial.println("========================================n"); } // 系統(tǒng)啟動(dòng)完成提示 void systemStartupComplete() { Serial.println("【系統(tǒng)初始化完成】n"); // LED閃爍3次表示系統(tǒng)就緒 for (int i = 0; i < 3; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(150); digitalWrite(LED_BUILTIN, LOW); delay(150); } Serial.println("? 系統(tǒng)就緒,等待手勢(shì)輸入...n"); Serial.println("═══════════════════════════════════════════n"); } // ==================== 風(fēng)扇控制函數(shù) ==================== // 控制風(fēng)扇的轉(zhuǎn)速和方向 // speed: 速度值(0-255) // direction: 方向(1=反轉(zhuǎn),-1=正轉(zhuǎn),0=停止) void controlFan(int speed, int direction) { // 限制速度在有效范圍內(nèi) speed = constrain(speed, 0, FAN_MAX_SPEED); if (direction == 1) { // 反轉(zhuǎn):IA(引腳9)=0, IB(引腳5)=PWM setPWM(FAN_IA_PIN, 0); setPWM(FAN_IB_PIN, speed); fanDirection = 1; } else if (direction == -1) { // 正轉(zhuǎn):IA(引腳9)=PWM, IB(引腳5)=0 setPWM(FAN_IA_PIN, speed); setPWM(FAN_IB_PIN, 0); fanDirection = -1; } else { // 停止:兩個(gè)引腳都輸出0 setPWM(FAN_IA_PIN, 0); setPWM(FAN_IB_PIN, 0); fanDirection = 0; } fanSpeed = speed; } // 停止風(fēng)扇 void stopFan() { controlFan(0, 0); Serial.println("→ 風(fēng)扇: 已停止"); } // 風(fēng)扇正轉(zhuǎn) void fanForward() { // 如果當(dāng)前是停止?fàn)顟B(tài),使用最小速度啟動(dòng) if (fanDirection == 0) { fanSpeed = FAN_MIN_SPEED; } controlFan(fanSpeed, -1); Serial.print("→ 風(fēng)扇: 正轉(zhuǎn) | 速度: "); Serial.println(fanSpeed); } // 風(fēng)扇反轉(zhuǎn) void fanReverse() { // 如果當(dāng)前是停止?fàn)顟B(tài),使用最小速度啟動(dòng) if (fanDirection == 0) { fanSpeed = FAN_MIN_SPEED; } controlFan(fanSpeed, 1); Serial.print("→ 風(fēng)扇: 反轉(zhuǎn) | 速度: "); Serial.println(fanSpeed); } // 風(fēng)扇加速 void fanSpeedUp() { // 只有在風(fēng)扇運(yùn)行時(shí)才能加速 if (fanDirection != 0) { // 增加速度,但不超過最大值 fanSpeed = constrain(fanSpeed + SPEED_STEP, FAN_MIN_SPEED, FAN_MAX_SPEED); controlFan(fanSpeed, fanDirection); // 應(yīng)用新速度 Serial.print("→ 風(fēng)扇: 加速至 "); Serial.println(fanSpeed); } else { Serial.println("? 提示: 請(qǐng)先啟動(dòng)風(fēng)扇(向上或順時(shí)針手勢(shì))"); } } // 風(fēng)扇減速 void fanSpeedDown() { // 只有在風(fēng)扇運(yùn)行時(shí)才能減速 if (fanDirection != 0) { // 降低速度,但不低于最小值 fanSpeed = constrain(fanSpeed - SPEED_STEP, FAN_MIN_SPEED, FAN_MAX_SPEED); controlFan(fanSpeed, fanDirection); // 應(yīng)用新速度 Serial.print("→ 風(fēng)扇: 減速至 "); Serial.println(fanSpeed); } else { Serial.println("? 提示: 請(qǐng)先啟動(dòng)風(fēng)扇(向上或順時(shí)針手勢(shì))"); } } // ==================== 舵機(jī)控制函數(shù) ==================== // 平滑移動(dòng)舵機(jī)(防止舵機(jī)快速轉(zhuǎn)動(dòng)時(shí)抖動(dòng)或損壞) void moveServoSmoothly() { int step = 2; // 默認(rèn)每次移動(dòng)2度 // 根據(jù)目標(biāo)位置調(diào)整移動(dòng)步長(zhǎng) // 如果是移動(dòng)到0度或180度(左右手勢(shì)),使用較大步長(zhǎng)實(shí)現(xiàn)快速響應(yīng) if (targetServoPos == 0 || targetServoPos == 180) { step = 5; // 大步長(zhǎng),快速移動(dòng) } // 根據(jù)當(dāng)前位置和目標(biāo)位置的關(guān)系,逐步移動(dòng) if (currentServoPos < targetServoPos) { // 當(dāng)前位置小于目標(biāo)位置,向右轉(zhuǎn) currentServoPos = min(currentServoPos + step, targetServoPos); } else if (currentServoPos > targetServoPos) { // 當(dāng)前位置大于目標(biāo)位置,向左轉(zhuǎn) currentServoPos = max(currentServoPos - step, targetServoPos); } myServo.write(currentServoPos); // 寫入新位置 delay(15); // 給舵機(jī)一點(diǎn)時(shí)間響應(yīng) } // ==================== 手勢(shì)處理函數(shù) ==================== void handleGesture(Gesture gesture) { Serial.print("? 檢測(cè)到手勢(shì): "); // 根據(jù)不同的手勢(shì)類型執(zhí)行相應(yīng)動(dòng)作 switch (gesture) { case GES_UP: // 向上手勢(shì):?jiǎn)?dòng)風(fēng)扇正轉(zhuǎn) Serial.println("向上 ↑"); fanForward(); break; case GES_DOWN: // 向下手勢(shì):系統(tǒng)復(fù)位(舵機(jī)回中間,風(fēng)扇停止) Serial.println("向下 ↓"); Serial.println("→ 執(zhí)行系統(tǒng)復(fù)位"); targetServoPos = 90; // 舵機(jī)回到90度中間位置 // stopFan(); // 風(fēng)扇停止 break; case GES_LEFT: // 向左手勢(shì):舵機(jī)轉(zhuǎn)到0度(最左側(cè)) Serial.println("向左 ←"); targetServoPos = 0; Serial.println("→ 舵機(jī): 轉(zhuǎn)向0°"); break; case GES_RIGHT: // 向右手勢(shì):舵機(jī)轉(zhuǎn)到180度(最右側(cè)) Serial.println("向右 →"); targetServoPos = 180; Serial.println("→ 舵機(jī): 轉(zhuǎn)向180°"); break; case GES_CLOCKWISE: // 順時(shí)針旋轉(zhuǎn)手勢(shì):風(fēng)扇正轉(zhuǎn) Serial.println("順時(shí)針 ?"); fanForward(); break; case GES_ANTICLOCKWISE: // 逆時(shí)針旋轉(zhuǎn)手勢(shì):風(fēng)扇反轉(zhuǎn) Serial.println("逆時(shí)針 ?"); fanReverse(); break; case GES_WAVE: // 揮手手勢(shì):停止風(fēng)扇 Serial.println("揮手 ?"); stopFan(); break; case GES_FORWARD: // 向前推手勢(shì):風(fēng)扇加速 Serial.println("向前 ?"); fanSpeedUp(); break; case GES_BACKWARD: // 向后拉手勢(shì):風(fēng)扇減速 Serial.println("向后 ?"); fanSpeedDown(); break; default: // 未識(shí)別的手勢(shì) Serial.println("未識(shí)別的手勢(shì)"); return; } // 顯示當(dāng)前系統(tǒng)狀態(tài) displayStatus(); Serial.println("---"); } // ==================== 狀態(tài)顯示函數(shù) ==================== void displayStatus() { Serial.print(" 審核編輯 黃宇
-
STM32
+關(guān)注
關(guān)注
2309文章
11162瀏覽量
373435 -
SG90
+關(guān)注
關(guān)注
0文章
10瀏覽量
11744 -
L9110
+關(guān)注
關(guān)注
3文章
13瀏覽量
17367
發(fā)布評(píng)論請(qǐng)先 登錄
零知IDE——基于STM32F103RBT6的PAJ7620U2手勢(shì)控制WS2812 RGB燈帶系統(tǒng)
零知IDE——基于零知標(biāo)準(zhǔn)板驅(qū)動(dòng)PAJ7620U2手勢(shì)控制L9110風(fēng)扇模塊和SG90舵機(jī)系統(tǒng)
利用PAJ7620U2 Gesture Sensor手勢(shì)識(shí)別控制小車
手勢(shì)傳感PAJ7620U2的智能家居應(yīng)用分享
【RA4M2設(shè)計(jì)挑戰(zhàn)賽】基于PAJ7620U2手勢(shì)識(shí)別網(wǎng)關(guān)設(shè)計(jì)
使用Arduino和PAJ7620手勢(shì)傳感器制作手勢(shì)控制機(jī)器人
ATK-PAJ7620手勢(shì)識(shí)別模塊的用戶手冊(cè)免費(fèi)下載
PAJ7620U2集成I2C接口手勢(shì)識(shí)別傳感器的數(shù)據(jù)手冊(cè)免費(fèi)下載
微雪電子PAJ7620U2手勢(shì)傳感器簡(jiǎn)介
PAJ7620U2帶I2C接口的集成手勢(shì)識(shí)別傳感器數(shù)據(jù)手冊(cè)免費(fèi)下載
零知IDE——基于STM32F103RBT6的PAJ7620U2手勢(shì)控制WS2812 RGB燈帶系統(tǒng)
零知IDE——基于零知標(biāo)準(zhǔn)板驅(qū)動(dòng)PAJ7620U2手勢(shì)控制L9110風(fēng)扇模塊和SG90舵機(jī)系統(tǒng)
評(píng)論