STM32实践项目(激光炮台)

刚开始设想做一个上半部分可以上下180°移动,下半部分底座360°移动的激光炮台。于是便开始了实践。

所需材料清单:

序号 名称 数量 备注说明
1 面包板(Breadboard) 2 用于电路搭建和模块连接
2 杜邦线(公对公、公对母等) 若干 建议准备 30~50 根,方便连接
3 MB-102 电源模块 2 插在面包板上,提供 3.3V / 5V 电源
4 电池(适配 MB-102) 2 建议 9V 方块电池或 7.4V 锂电池
5 SG90 舵机(180° 限位舵机) 1 控制角度在 0°~180°
6 SG90 舵机(360° 连续舵机) 1 可连续旋转,用作角度模拟+PID控制
7 STM32F103C8T6 开发板 2 最小系统板(Blue Pill)
8 KY-008 激光模块 1 激光发射模块(带限流电阻)
9 HC-05 蓝牙模块 2 一发一收,用于无线通信
10 旋转编码器(KY-040 或同类) 2 用于输入角度,连接 STM32 编码器接口

主要过程

起初设想用简单的按钮控制,而单凭if语句只能实现按钮按一下舵机角度变化一下,无法实现舵机角度连续性变化。

//uint8_t KeyNum;
//float Angle;//uint8_t i;//int main(void)
//{
//	OLED_Init();
//	Servo_Init();
//	Key_Init();
//	
//	OLED_ShowString(1,1,"Angle:");
//    
//    while (1)
//    {
//       KeyNum=Key_GetNum();
//		if(KeyNum==1)
//		{
//			Angle+=30;
//			if(Angle>180)
//			{
//				Angle=0;
//			}
//		}
//		Servo_SetAngle(Angle);
//		OLED_ShowNum(1,7,Angle,3);
//    }
//}

因此,完善key.c代码,在原代码中添加按住一直返回低电平来实现。

// 新增的实时检测(按住时一直返回)
uint8_t Key_IsPressed(uint8_t keyID)
{if (keyID == 1){return (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);}else if (keyID == 2){return (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);}return 0;
}

而此时又发现新的问题,如何很好的控制转速。一开始采用的是改变延时速率来提升转速。但如此一来舵机的稳定性便出现的问题。会发生抖动。

//int main(void)
//{
//    OLED_Init();
//    Servo_Init();
//    Key_Init();
//    
//    OLED_ShowString(1, 1, "Angle:");
//	OLED_ShowString(2, 1, "Angle:");
//    while (1)
//{
//    if (Key_IsPressed(1)) // 如果按键1被按住
//    {
//        Angle += 1;
//        if (Angle > 180) Angle = 180;//        Servo_SetAngle(Angle);
//        OLED_ShowNum(1, 7, Angle, 3);
//        Delay_ms(1); // 匀速控制
//    }
//	if (Key_IsPressed(2)) // 如果按键1被按住
//    {
//        Angle -= 1;
//        if (Angle < 0) Angle = 0;//        Servo_SetAngle(Angle);
//        OLED_ShowNum(2, 7, Angle, 3);
//        Delay_ms(1); // 匀速控制
//    }
//}

于是加大每一次增加的角度,而延迟秒数不变。

float Angle = 90;     // 初始角度
float lastAngle = -1; // 用于减少 OLED 刷新频率int main(void)
{OLED_Init();Servo_Init();Key_Init();OLED_ShowString(1, 1, "Angle+ :");Servo_SetAngle(Angle); // 舵机先转到中位while (1){// 按键 1:增加角度(快速)if (Key_IsPressed(1)){Angle += 5;               // 每次增加 5°if (Angle > 180) Angle = 180;Servo_SetAngle(Angle);Delay_ms(15);             // 匀速快速控制}// 按键 2:减少角度(快速)if (Key_IsPressed(2)){Angle -= 5;               // 每次减少 5°if (Angle < 0) Angle = 0;Servo_SetAngle(Angle);Delay_ms(15);}// 角度变化才刷新显示if (Angle != lastAngle){OLED_ShowNum(1, 9, (uint16_t)Angle, 3);lastAngle = Angle;}}
}

的确这样能让舵机较好的连续变化,但是按键寿命有限,频繁操作容易损坏。于是换成更加方便顺手的旋转编码器。其优点也是比较突出的。

优点

操作更直观:想让舵机转多少,就拧多少;比按键舒服很多。

分辨率可调:可以设置每格 1° / 5° / 10°,灵活性高。

响应更快:旋钮快速转几格,舵机就能快速到位。

支持连续调节:不像按钮那样要一直按着,旋钮转动一圈就能从 0° 到 180°。

耐用性更好:旋转编码器机械寿命通常比按键长。

//main函数中
#include "Encoder.h"
float Angle = 90;     // 初始角度
float lastAngle = -1; // 上一次显示的角度
int main(void)
{OLED_Init();PWM_Init();Encoder_Init();Servo_SetAngle(Angle); // 初始角度OLED_ShowString(1,1,"Angle:");while(1){int16_t val = Encoder_Get();   // 获取旋转增量if(val != 0){Angle += val*5;              // 编码器每跳一下 → 改变 5°if(Angle < 0) Angle = 0;if(Angle > 180) Angle = 180;Servo_SetAngle(Angle);}if(Angle != lastAngle){OLED_ShowNum(1, 8, (uint16_t)Angle, 3);lastAngle = Angle;}}
}
//编码器函数
#include "stm32f10x.h"                  // Device header
/*================= 编码器初始化 =================*/int16_t Encoder_Count; 
void Encoder_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1;EXTI_InitStructure.EXTI_LineCmd=ENABLE;EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;NVIC_Init(&NVIC_InitStructure);
}int16_t Encoder_Get(void)
{int16_t Temp;Temp=Encoder_Count;Encoder_Count=0;return Temp;
}/*================= 编码器中断服务函数 =================*/
void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line0)==SET){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){Encoder_Count--;}EXTI_ClearITPendingBit(EXTI_Line0);}
}
void EXTI1_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line1)==SET){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){Encoder_Count++;}EXTI_ClearITPendingBit(EXTI_Line1);}
}

但如此一来旋转编码器需要旋转720°舵机才能旋转180°,如果没有显示屏不太好控制,并且旋转两圈也比较难操作,就想着能否对应编码器旋转360°舵机旋转180。

1.确定编码器的分辨率

绝大多数常见机械旋转编码器是 20 格/圈(detents),有的高分辨率型号可能是 24、30、32 格。

如果是 20 格/圈:旋转 360° → 20 次脉冲。

中断服务函数里 Encoder_Count++ / -- 正好就是在数这些脉冲。

2. 计算换算关系

目标是:

20 格(1 圈) → 180°

那么 每格对应 = 180 ÷ 20 = 9°

如果是 24 格:

24 格(1 圈) → 180°

每格 = 180 ÷ 24 = 7.5°

于是在原有主函数上改遍(我这里的旋转编码器是20格的)

#define ENCODER_STEPS_PER_REV 20   // 编码器分辨率(根据实际修改)
#define SERVO_RANGE_DEG       180  // 舵机可动角度范围float Angle = 90;     // 初始角度
float lastAngle = -1;while (1)
{int16_t val = Encoder_Get();   // 获取旋转的脉冲数if (val != 0){Angle += val * (SERVO_RANGE_DEG / ENCODER_STEPS_PER_REV);if (Angle < 0) Angle = 0;if (Angle > 180) Angle = 180;Servo_SetAngle(Angle);}if (Angle != lastAngle){OLED_ShowNum(1, 8, (uint16_t)Angle, 3);lastAngle = Angle;}
}

3.同理,再加上一个舵机

//#define ENCODER_STEPS_PER_REV 20   // 编码器一圈脉冲数
//#define SERVO_RANGE_DEG       180  // 舵机行程角度//#include "Encoder.h"
//#include "OLED.h"
//#include "PWM.h"
//#include "Servo.h"//float Angle1 = 90;  // 舵机1初始角度
//float Angle2 = 90;  // 舵机2初始角度//float lastAngle1 = -1;
//float lastAngle2 = -1;//int main(void)
//{
//    OLED_Init();
//    PWM_Init();
//    Encoder_Init();//    Servo1_SetAngle(Angle1); // 初始角度
//    Servo2_SetAngle(Angle2);//    OLED_ShowString(1,1,"Servo1:");
//    OL

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

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

相关文章

不止是夹住,更是“感知”:Contactile GAL2触觉型夹爪实现自适应抓取

近日&#xff0c;专注于触觉传感与智能抓取技术的Contactile推出全新Contactile 触觉型夹爪 GAL2&#xff0c;这款集成先进传感技术的双指夹爪&#xff0c;凭借实时触觉反馈能力&#xff0c;为多行业智能抓取场景带来突破性解决方案。 Contactile 触觉型夹爪GAL2是一款多功能即…

Grafana - 监控磁盘使用率Variables使用

1 查询prometheus2 编辑grafana dashboard 2.1 配置变量2.2 配置多选2.3 配置legend2.4 优化显示 1 查询prometheus 指标名称描述node_filesystem_size_bytes文件系统总容量node_filesystem_avail_bytes用户可用空间node_filesystem_files_free剩余inode数量比如我们想看/目…

WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析10

上一篇&#xff1a;WindowsAPI|每天了解几个winAPI接口之网络配置相关文档Iphlpapi.h详细分析9 如果有错误欢迎指正批评&#xff0c;在此只作为科普和参考。 C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\um\iphlpapi.h 文章目录GetNetworkParams&#xff1a…

算法 --- 分治(归并)

分治&#xff08;归并&#xff09; 分治&#xff08;特别是归并&#xff09;算法适用于解决“整体求解依赖于子问题合并”且子问题相互独立的题目&#xff0c;其典型特征是能将大规模数据分解、递归求解&#xff0c;然后通过合并操作&#xff08;这正是归并排序中‘归并’的精…

【程序人生】有梦想就能了不起,就怕你没梦想

梦想不是遥不可及的星辰&#xff0c;而是需要我们用脚步丈量的路途两年前的一个夏日&#xff0c;我在日记本上郑重地写下&#xff1a;"我要掌握Web开发&#xff0c;能够独立构建一个完整的Web应用。"那天是2023年6月8日&#xff0c;当时的我连Java和JavaScript都分不…

前端基础(四十二):非固定高度的容器实现折叠面板效果

效果展示源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head>…

发票、收据合并 PDF 小程序,报销上传 3 秒搞定

每到报销、报税、财务整理时&#xff0c;手里是不是总有一堆格式不一的票据&#xff1a; 聊天记录里的电子发票邮件附件中的 PDF 发票手机相册里的报销收据甚至还有零散的纸质票据扫描件 要上传或交给财务前&#xff0c;还得一个个整理、转换、排版&#xff0c;既耗时又容易出…

GitHub每日最火火火项目(9.4)

1. bytebot-ai / bytebot 项目名称&#xff1a;bytebot项目介绍&#xff1a;基于 TypeScript 开发&#xff0c;是一款自托管的 AI 桌面智能体&#xff0c;能通过自然语言命令自动化执行计算机任务&#xff0c;运行在容器化的 Linux 桌面环境中。它借助自然语言处理和 AI 技术&a…

MMORPG 游戏战斗系统架构

&#x1f30c; MMORPG 游戏战斗系统架构 引用&#xff1a; 游戏服务器同步技术解析&#xff08;C&#xff09;MMORPG移动同步与反外挂 虽然我已离开游戏行业&#xff0c;转而与几位成功的商人共同创业&#xff0c;投身于商用机器人领域&#xff0c;但坦诚地说&#xff0c;游戏…

【数学建模学习笔记】启发式算法:蒙特卡洛算法

蒙特卡洛模拟入门笔记&#xff1a;从原理到代码实践一、什么是蒙特卡洛模拟&#xff1f;蒙特卡洛模拟是一种通过大量随机实验来解决复杂问题的方法。简单说&#xff0c;就是用电脑模拟成千上万次随机事件&#xff0c;然后统计结果&#xff0c;以此估算一个问题的答案。举个生活…

20250904的学习笔记

一、封包与拆包1. 封包&#xff08;Packet Encapsulation&#xff09;封包 是指在发送数据时&#xff0c;将数据从高层协议封装到低层协议的过程。每经过一层协议&#xff0c;数据都会被加上相应的协议头&#xff08;有时也会加上协议尾&#xff09;&#xff0c;形成一个新的数…

STM32F4 + RT-Thread 实战指南:TIM10 硬件定时器驱动开发与 1 秒定时功能实现

目录前言一、STM32定时器10是个什么定时器&#xff1f;二、工程创建、环境配置三、程序代码四、运行前言 在rtthread中&#xff0c;STM32F4的定时器10有些驱动并不完整&#xff0c;对比与其它定时器在使用时需要手动的添加一些代码&#xff0c;我在使用上拆踩了一些坑&#xf…

echarts图库

环形图// 指定图表的配置项和数据this.option {// tooltip: {// trigger: item// },color: [#FFB32F, #FF5757, #57D5FF, #2FA8FF, #95FFF1], // 扇形区域以及列表颜色legend: {orient:vertical,//文字横向排itemGap:20,left: left,textStyle:{color: #F3F9FF,// fontSi…

进程(Process)全面概述

进程&#xff08;Process&#xff09;全面概述 本文档扩展了进程的定义、属性、生命周期、管理机制及示例&#xff0c;涵盖 task_struct 结构、进程链表、状态与优先级、fork 函数及其写时复制示例。 一、进程基本概念 进程&#xff1a;系统进行资源分配和调度的基本单位&#…

Java并发编程:sleep()与wait()核心区别详解

今天完成了实验室纳新网站的工作&#xff0c;大体功能都已经完善&#xff0c;也和前端测试过了&#xff0c;费了点时间&#xff0c;而且今天大部分时间在看langchain4j的东西&#xff0c;就简单复习一下八股&#xff0c;等会再复习一下算法题吧在Java并发编程中&#xff0c;sle…

AR眼镜在智能制造的应用方向和场景用例|阿法龙XR云平台

AR巡检在制造业的应用已形成覆盖设备维护、质量检测、安全监控和远程协作四大类别的成熟场景&#xff0c;不同制造领域的实践各具特色&#xff0c;为行业提供了宝贵参考。在汽车制造领域&#xff0c;AR 巡检主要应用于生产线设备维护和焊接质量检测。在汽车厂总装车间部署 AR 系…

【Linux系统】线程同步

在上一章节中&#xff0c;我们使用互斥量之后&#xff0c;确实解决了数据竞争问题&#xff0c;但出现了新的问题&#xff1a;只有一个线程&#xff08;thread 1&#xff09;在处理所有售票任务。这展示了互斥量的一个局限性&#xff1a;它确保了线程安全&#xff0c;但不保证公…

代码随想录训练营第三十一天|LeetCode56.合并区间、LeetCode738.单调递增的数字

56.合并区间 思路&#xff1a;先让二维数组进行排序&#xff1b; 遍历数组&#xff0c;定义一个min表示重合区间的左边界&#xff0c;max表示重合区间的右边界&#xff1b; 如果当前区间左边大于max&#xff0c;就证明重合区间断了&#xff0c;就要对它进行加入ArrayList&am…

【Unity项目经验分享】实现左右分屏裸眼3D程序

1、实现原理左右分屏原理&#xff0c;左右屏内容左右方向存在些许偏差。通过左右相机&#xff0c;然后左侧相机向左侧偏移一点3cm&#xff0c;右侧相机向右侧屏偏移一定3cm&#xff0c;然后将左右相机渲染内容通过RenderTexture渲染到Canvas上面的左右RawImage上面。2、实现具体…

设计软件启动失败?“找不到vcruntime140.dll,无法继续执行代码” 场景化解决方案来了

打游戏时&#xff0c;刚加载到登录界面就因 “找不到 vcruntime140.dll, 无法继续执行代码” 闪退&#xff1b;写代码时&#xff0c;编译工具突然报错中断工作&#xff1b;做设计时&#xff0c;PS、AE 启动失败弹出相同提示 —— 不同场景下的 vcruntime140.dll 错误&#xff0…