【小制作】米家模拟手指点击

代码功能解释

这段代码是一个基于Arduino平台的控制程序,主要功能包括:

  1. 初始化:设置引脚模式、初始化编码器、舵机和EEPROM。
  2. 按键检测:处理按钮的单击、双击和长按事件,并根据事件执行相应操作。
  3. 编码器更新:检测旋转编码器的状态,调整变量值并控制LED闪烁。
  4. 舵机控制:根据设定的角度和速度移动舵机。
  5. LED控制:根据条件交替闪烁两个LED。

详细解释

  1. 初始化

    • 设置引脚模式为输入或输出。
    • 初始化编码器、舵机和EEPROM。
  2. 主循环

    • 持续检测按键状态。
    • 更新编码器状态。
    • 控制舵机运动。
    • 控制LED闪烁。
  3. 按键检测

    • 检查每个按钮的状态。
    • 根据按键事件(长按、双击、单击)执行不同操作。
  4. 编码器更新

    • 检测编码器旋转方向。
    • 根据旋转方向增加或减少变量值。
    • 切换LED状态。
  5. 舵机控制

    • 根据设定的角度和速度移动舵机。
    • 正向和反向移动舵机。
  6. LED控制

    • 控制两个LED交替闪烁。
#include "Arduino.h"
#include <Bounce2.h>
#include <Servo.h>
#include <EEPROM.h>// 定义引脚
#define EC11_A PB_2     // 编码器A
#define EC11_B PA_7     // 编码器B
#define BUTTON_1 PA_4   // 按钮1
#define BUTTON_2 PA_6   // 按钮2
#define PWM_1 PB_0      // PWM控制
#define LED_0 PA_0      // LED0
#define LED_1 PA_1      // LED1// 定义常量
const unsigned long debounceTime = 50;
const unsigned long longPressTime = 1000;
const unsigned long doubleClickTime = 300;
const long led_interval = 300;
const int DEFAULT_START_ANGLE = 90;
const int DEFAULT_STEPS = 100;// 定义变量
uint8_t fast_num = 1;    // 运动速度
uint8_t fast_delta = 90; // 运动距离
uint8_t Count_step = 1;  // 运动速度调节步进
bool Duoji_run_Flag = false;
bool CounterChanged = false;
int pos = 0;
int variableA = 0;
int variableB = 0;
bool isVariableA = true;// 定义LED状态
bool led0State = LOW;
bool led1State = LOW;
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
bool led0Blinked = false;
bool led1Blinked = false;// 定义按键状态结构体
struct ButtonState
{int pin;int lastState;int currentState;unsigned long lastDebounceTime;unsigned long lastClickTime;bool isLongPress;bool isSingleClickHandled;
};// 初始化按键状态
ButtonState button1 = {BUTTON_1, HIGH, HIGH, 0, 0, false, false};
ButtonState button2 = {BUTTON_2, HIGH, HIGH, 0, 0, false, false};// 初始化Bounce对象
Bounce encoderPinAButton = Bounce();
Bounce encoderPinBButton = Bounce();// 初始化Servo对象
Servo myservo;// 宏定义调试输出
#define _DRV_TAG_ " line"
#define serial_dbg(fmt, args...)                                                         \do                                                                                     \{                                                                                      \if (1)                                                                               \{                                                                                    \Serial.printf("" _DRV_TAG_ "%d  [%s] " fmt " \n", __LINE__, __FUNCTION__, ##args); \}                                                                                    \} while (0)// 初始化函数
void setup()
{Serial.begin(115200);serial_dbg("Hello, Air001. \n");// 设置LED引脚为输出模式pinMode(LED_0, OUTPUT);pinMode(LED_1, OUTPUT);// 初始化LED状态为灭digitalWrite(LED_0, LOW);digitalWrite(LED_1, LOW);// 设置按键引脚为输入模式pinMode(BUTTON_1, INPUT);pinMode(BUTTON_2, INPUT);// 初始化编码器sys_RotaryInit();// 初始化PWM频率和分辨率pinMode(PWM_1, OUTPUT);myservo.attach(PWM_1, 500, 2500); // 修正脉冲宽度read_eeprom();//读数据
}// 主循环函数
void loop()
{checkButton();    // 检测按键encoder_update();  // 更新编码器状态duoji();          // 控制舵机led_blink_once(); // 控制LED闪烁
}
void blinkLEDsAlternatingTwice() {// 保存当前LED状态bool savedLed0State = led0State;bool savedLed1State = led1State;// 交替闪烁两次for (int i = 0; i < 2; i++) {digitalWrite(LED_0, HIGH);digitalWrite(LED_1, LOW);delay(led_interval);digitalWrite(LED_0, LOW);digitalWrite(LED_1, HIGH);delay(led_interval);}// 恢复LED状态digitalWrite(LED_0, savedLed0State);digitalWrite(LED_1, savedLed1State);
}
void write_eeprom() {// 写入EEPROMEEPROM.write(0, fast_num);EEPROM.write(1, fast_delta);// 写完EEPROM后两颗LED交替闪烁两次blinkLEDsAlternatingTwice();
}
void read_eeprom() {// 读取EEPROMfast_num = EEPROM.read(0);fast_delta = EEPROM.read(1);if (fast_num == 0 || fast_delta == 0) {fast_num = DEFAULT_START_ANGLE;fast_delta = DEFAULT_STEPS;write_eeprom();}serial_dbg("fast_num: %d, fast_delta: %d", fast_num, fast_delta);
}
// 初始化编码器
void sys_RotaryInit()
{pinMode(EC11_A, INPUT);pinMode(EC11_B, INPUT);pinMode(BUTTON_1, INPUT_PULLUP);encoderPinAButton.attach(EC11_A);encoderPinAButton.interval(5);encoderPinBButton.attach(EC11_B);encoderPinBButton.interval(5);
}// 检测按键状态
void checkButton()
{checkButtonState(button1);checkButtonState(button2);
}// 处理按键状态
void checkButtonState(ButtonState &button)
{int reading = digitalRead(button.pin); // 读取按钮状态// 去抖动处理if (reading != button.lastState){button.lastDebounceTime = millis();}if ((millis() - button.lastDebounceTime) > debounceTime){if (reading != button.currentState){button.currentState = reading;if (button.currentState == LOW){button.lastClickTime = millis();button.isLongPress = false;button.isSingleClickHandled = false; // 新增标志位}else{unsigned long pressDuration = millis() - button.lastClickTime;if (pressDuration > longPressTime){button.isLongPress = true;handleLongPress(button.pin);}else if (!button.isLongPress){// 检查是否为双击if ((millis() - button.lastClickTime) < doubleClickTime && !button.isSingleClickHandled){if (digitalRead(button.pin) == HIGH){handleDoubleClick(button.pin);button.isSingleClickHandled = true; // 标记双击已处理}}else if (!button.isSingleClickHandled){// 如果不是双击,则处理单击事件handleSingleClick(button.pin);button.isSingleClickHandled = true;}}}}}button.lastState = reading;
}// 处理长按事件
void handleLongPress(int pin)
{switch (pin){case BUTTON_1:serial_dbg("Button 1 Long Press");isVariableA = !isVariableA;if (isVariableA){digitalWrite(LED_1, LOW);led1State = LOW;serial_dbg("Switching to Variable A");}else{digitalWrite(LED_1, HIGH);led1State = HIGH;serial_dbg("Switching to Variable B");}break;case BUTTON_2:serial_dbg("Button 2 Long Press");Duoji_run_Flag = true;break;default:break;}
}// 处理双击事件
void handleDoubleClick(int pin)
{switch (pin){case BUTTON_1:serial_dbg("Button 1 Double Click");Duoji_run_Flag = true;break;case BUTTON_2:serial_dbg("Button 2 Double Click");break;default:break;}
}// 处理单击事件
void handleSingleClick(int pin)
{switch (pin){case BUTTON_1:write_eeprom();serial_dbg("Button 1 Single Click");break;case BUTTON_2:serial_dbg("Button 2 Single Click");Duoji_run_Flag = true;break;default:break;}
}// 更新编码器状态
void encoder_update()
{encoderPinAButton.update();encoderPinBButton.update();// 检测编码器旋转if (encoderPinAButton.fell()){if (encoderPinBButton.read() == HIGH){if (isVariableA){variableA=fast_num;variableA += Count_step; // 顺时针旋转fast_num = variableA;}else{variableB = fast_delta;variableB += Count_step; // 顺时针旋转fast_delta = variableB;}}else{if (isVariableA){variableA=fast_num;variableA -= Count_step; // 逆时针旋转fast_num = variableA;}else{variableB = fast_delta;variableB -= Count_step; // 逆时针旋转fast_delta = variableB;}}CounterChanged = true;if (isVariableA){serial_dbg("Variable A= %d ", variableA);}else{serial_dbg("Variable B= %d ", variableB);}led0Blinked = false;led_blink_once();}
}// 控制舵机
void duoji()
{if (Duoji_run_Flag){int DEFAULT_END_ANGLE = fast_delta + DEFAULT_START_ANGLE;Duoji_run_Flag = false;unsigned long startTime = millis();int totalTime = fast_num * 200;// 正向移动moveServo(DEFAULT_START_ANGLE, DEFAULT_END_ANGLE, DEFAULT_STEPS, startTime, totalTime);startTime = millis();// 反向移动moveServo(DEFAULT_END_ANGLE, DEFAULT_START_ANGLE, DEFAULT_STEPS, startTime, totalTime);}
}// 移动舵机的辅助函数
void moveServo(int startAngle, int endAngle, int steps, unsigned long startTime, int totalTime)
{unsigned long stepDuration = totalTime / steps;float deltaAngle = (endAngle - startAngle) * 0.5;for (int i = 0; i <= steps; i++){float t = (float)i / steps;int angle = startAngle + deltaAngle * (1 - cos(t * PI));myservo.write(angle);delay(totalTime / steps);// 非阻塞延时while (millis() - startTime < (i + 1) * stepDuration){checkButton();yield(); // 或者其他适合的多任务处理方法}}
}// 控制LED闪烁
void led_blink_once()
{unsigned long currentMillis = millis();// 控制LED1闪烁一次blinkLEDOnce(LED_0, led0State, previousMillis1, currentMillis, led_interval, led0Blinked);// 控制LED2闪烁一次blinkLEDOnce(LED_1, led1State, previousMillis2, currentMillis, led_interval, led1Blinked);
}// 闪烁LED一次
void blinkLEDOnce(int pin, bool &state, unsigned long &previousMillis, unsigned long currentMillis, long led_interval, bool &blinked)
{if (!blinked){if (currentMillis - previousMillis >= led_interval){previousMillis = currentMillis;state = !state; // 切换LED状态digitalWrite(pin, state);if (state == LOW){blinked = true; // 标记LED已经闪烁}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pentaho Kettle迁移至Oracle的空字符串和NULL的问题处理,大坑!

一、问题说明 在使用 Kettle 将 DB2 数据迁移到 Oracle 的过程中&#xff0c;出现了 DB2 中为空字符串的字段&#xff0c;在插入到 Oracle 过程中实际插入的为 NULL &#xff0c;导致触发了非空校验而迁移失败 空字符串 ‘’ &#xff0c;即长度为0的字符串 搜索该问题后得知…

2025-01-04 Unity插件 YodaSheet1 —— 插件介绍

文章目录 1 介绍2 工作原理2.1 ScriptableObject -> YadeSheetData2.2 YadeDatabase 存储多个 YadeSheetData 3 用途4 缺点5 推荐 1 介绍 ​ Yade 提供类似于 Excel 或者 Google Sheets 的表格编辑器&#xff0c;可以轻松地在 Unity 编辑器中 编辑&#xff0c;搜索&#xf…

VScode 格式化代码空格记录

点击 -> “文件” -> “首选项" -> “设置” -> 按下图操作&#xff1a; 怎么格式化代码空格&#xff0c;先看下&#xff1a; 保存代码后&#xff0c;这代码自动格式化发&#xff0c;如下图&#xff1a; 你可以试试看就即可

苹果系统MacOS下ObjectC建立的App程序访问opencv加载图片程序

前言 苹果系统下使用opencv感觉还是有些不太方便&#xff0c;总是感觉有点受到限制。本博客描述的是在MacOS下建立App程序然后调用opencv显示图片时出现的一些问题并最后解决的一个过程。 一、程序的建立 选择程序的类型&#xff1a; 选择界面模式和编程语言&#xff1a; 其余…

CSS3——3. 书写格式二

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><!--css书写&#xff1a;--><!--1. 属性名:属性值--><!--2.属性值是对属性的相关描述--><!--3.属性名必须是…

windows配置jdk

进入此网址 JDK 11 Releases 找到11版本进行下载 下载好后配置环境变量 按windows加r键输入sysdm.cpl点击确定 在系统变量里新建输入 JAVA_HOME D:\openjdk-1128_windows-x64_bin\jdk-11 //变量值为安装jidk的路径 配置path环境变量 编辑path变量输入%JAVA_HOME%\bin并放置最…

深入刨析数据结构之排序(下)

目录 1.内部排序 1.5选择排序 1.5.1简单选择排序 1.5.2树形选择排序 1.6堆排序 1.7归并排序 1.7.1递归归并 1.7.2非递归归并 1.8计数排序 1.9基数排序 常见内部排序的总结&#xff1a; 1.内部排序 1.5选择排序 选择排序&#xff08;Selection Sort&#xff09;的基…

RocketMQ场景问题

1.消息丢失 有这么一个场景&#xff0c;就是订单支付完成之后&#xff0c;订单系统会进行发送消息给RocketMQ集群&#xff0c;下游会有积分系统进行监听这个消息&#xff0c;进行消费然后给用户发放积分。在下面的这个场景中&#xff0c;通过查询日志发现了订单系统发送订单支付…

cordova项目环境搭建 hello

环境准备&#xff1a; 1.下载nodejs并安装配置。 直接官网下载最新版本。此次我是下载的 Node.js v22.12.0。 1.1安装好后配置镜像。 输入&#xff1a;node -v // 显示node.js版本 npm -v // 显示npm版本 1.2环境配置 &#xff08;1&#xff09;找到安装的目录&am…

LE Audio 初探

LE Audio 架构 一.LE Audio profile框架 profile初识&#xff1a; BAP&#xff1a; BAP通常在手机端实现&#xff0c;它允许配置编解码器&#xff0c;配置QoS(质量服务)&#xff0c;控制流媒体&#xff0c; PACS&#xff1a; Published Audio Capabilities Service&#xff0c;…

Ⅱ.INTRODUCTION TO CUDA C

前言 上一节环境配置好了&#xff0c;我们开始吧&#xff01; 一、A First Program 1. Hello, World! 我们先写一个C语言的 Hello, World! 作为对比 int main(void){printf("Hello, World!\n");return 0; }大家应该知道这个代码运行在CPU上吧&#xff0c;我们CP…

域上的多项式环,整除,相通,互质

例1.已知 (R,,x)为域&#xff0c;请选出正确的说法:(A)(R,,x)也是整区; ABCD (B)R中无零因子; C)R在x运算上满足第一、二、三指数律; (D)R只有平凡理想; (E)R只有平凡子环。 域的特征&#xff1a; 域中&#xff0c;非0元素的加法周期 思考、在模7整数环R,中&#xff0c;…

【0x0014】HCI_Read_Local_Name命令详解

目录 一、命令概述 二、命令格式 三、返回事件及参数说明 3.1. HCI_Command_Complete 事件 3.2. Status 3.3. Local_Name 四、命令执行流程 4.1. 命令发送 4.2. 控制器接收并处理命令 4.3. 控制器返回结果 4.4. 主机接收并解析事件包 4.5. 示例代码 五、应用场景 …

【AI日记】25.01.04 kaggle 比赛 3-3 | 王慧玲与基层女性

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Forecasting Sticker Sales时间&#xff1a;6 小时 读书 书名&#xff1a;基层女性时间&#xff1a;3 小时原因&#xff1a;虽然我之前就知道这个作者&#xff0c;因为我…

【从零开始入门unity游戏开发之——C#篇40】C#特性(Attributes)和自定义特性

文章目录 前言一、特性&#xff08;Attributes&#xff09;基本概念二、自定义特性1、自定义特性代码示例&#xff1a;2、应用自定义特性&#xff1a;3、解释3.1 **AttributeUsage 特性**3.2 特性的命名3.3 **构造函数**&#xff1a;3.4 **属性**&#xff1a; 4、使用反射获取特…

【Python学习(六)——While、for、循环控制、指数爆炸】

Python学习&#xff08;六&#xff09;——While、for、循环控制、指数爆炸 本文介绍了While、for、循环控制、指数爆炸&#xff0c;仅作为本人学习时记录&#xff0c;感兴趣的初学者可以一起看看&#xff0c;欢迎评论区讨论&#xff0c;一起加油鸭~~~ 心中默念&#xff1a;Py…

基于PyQt5的UI界面开发——图像与视频的加载与显示

介绍 这里我们的主要目标是实现一个基于PyQt5和OpenCV的图像浏览和视频播放应用。用户可以选择本地的图像或视频文件夹&#xff0c;进行图像自动播放和图像切换以及视频播放和调用摄像头等操作&#xff0c;并且支持图像保存功能。项目的核心设计包括文件路径选择、图像或视频的…

云手机+Facebook:让科技与娱乐完美结合

移动互联网时代&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;早已成为企业、品牌和组织竞相角逐的营销阵地。而云手机的出现&#xff0c;则为Facebook营销注入了新的活力&#xff0c;其独特的优势让营销活动更加高效、精准且灵活。本文将深入探讨云手机在Fa…

全新免押租赁系统打造便捷安全的租赁体验

内容概要 全新免押租赁系统的推出&#xff0c;标志着租赁行业的一次重大变革。这个系统的最大特点就是“免押金”&#xff0c;大大减轻了用户在租赁过程中的经济负担。从此&#xff0c;不再需要为一部手机或其他商品支付高昂的押金&#xff0c;用户只需通过简单的信用评估&…

postman在软件测试中的应用

postman工具概述 Postman 是一款功能强大的 API 开发和测试工具&#xff0c;在软件开发和测试领域应用广泛。开发阶段&#xff0c;可以通过工具进行mock数据测试&#xff0c;方便开发&#xff0c;联调&#xff1b;测试阶段&#xff0c;可以通过不同环境&#xff0c;不同数据进…