基于STM32的蓝牙遥控小车:手机APP控制小车运动

这里写目录标题

  • 一、智能双轮小车+蓝牙APP控制前后左右调速运动
      • 功能描述
      • 原理图设计
      • PCB设计
      • 源码设计
  • 二、更多功能小车网友推荐后开发...

在这里插入图片描述

一、智能双轮小车+蓝牙APP控制前后左右调速运动

功能描述

1、小车可以前后左右运动
2、可以调节小车的速度
3、使用两个电机驱动两个轮子,一个万向轮跟随运动
4、需要自己设计电机驱动电路,达到最小成本 (不超过1.5元)
5、设计一个手机APP可以蓝牙连接小车,进行控制

原理图设计

1、单片机最小系统电路,采用stm32f103c6t6单片机节省成本,包含复位电路、供电电路,下载接口
在这里插入图片描述

2、电源设计,电源可以用USB供电,也可以外接电池供电,4-7V都可以
加粗样式3、蓝牙模块电路,用来和手机进行通信,下发指令控制小车进行运动,当然小车也可以传输消息到手机进行显示,比如电量、速度等信息
在这里插入图片描述

4、电机驱动电路,这里是用了一个非常便宜的驱动芯片,大约1元一片的DRV8833,搭建一下外围电路就可以工作了,接线端子接入电机就可以正常驱动电机运转

在这里插入图片描述

PCB设计

下面就是我设计的PCB了,体积是非常小巧的,焊接好接上电机就可以正常使用啦
在这里插入图片描述
这是搭载到小车上的效果
在这里插入图片描述
小车的视频可以留意哔哩哔哩或者到我的咸鱼查看,账号名都是:化作尘my

源码设计

1、主函数代码设计
首先要初始化延时、串口、GPIO、电机驱动等外设
先让电机停止运转,while循环里面等待蓝牙指令处理

int main(void)
{delay_init();	    	 //延时函数初始化NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级uart_init(9600);	 	//串口初始化为115200usart2_init(usart2_baund);//串口2初始化为usart2_baundprintf("uart_init OK\r\n");GPIO_Config();  //GPIO初始化Drv8833_Init(); //电机驱动初始化  set_drv8833_para(AIN_NUM,DIR_STOP,0);//停止电机转动 set_drv8833_para(BIN_NUM,DIR_STOP,0);//停止电机转动 while(1){Query_IN_Response(); //蓝牙串口指令处理delay_ms(1);}
}

2、电机驱动代码
这里由于电机只有两个,比较少,为了灵活运用普通IO口驱动电机,我使用了GPIO模拟输出PWM的方法,用一个定时器定时10us,并设定参数让IO口在定时中断里面定时翻转,比如100个周期内高电平为50个低电平为50个,那么就得到一个周期为1ms,脉宽为50%的PWM

//驱动接口初始化
void Drv8833_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz#if 0TIM_PWM_Init();#elseGPIO_InitStructure.GPIO_Pin = nSLEEP_PIN;			GPIO_Init(nSLEEP_PORT, &GPIO_InitStructure);					 nSLEEP_DISABLE();#endif
#if (DRV8833_NUM >= 1)GPIO_InitStructure.GPIO_Pin = AIN1_PIN;			GPIO_Init(AIN1_PORT, &GPIO_InitStructure);					 AIN1_L();				GPIO_InitStructure.GPIO_Pin = AIN2_PIN;			GPIO_Init(AIN2_PORT, &GPIO_InitStructure);					 AIN2_L();				GPIO_InitStructure.GPIO_Pin = BIN1_PIN;			GPIO_Init(BIN1_PORT, &GPIO_InitStructure);					 BIN1_L();				GPIO_InitStructure.GPIO_Pin = BIN2_PIN;			GPIO_Init(BIN2_PORT, &GPIO_InitStructure);					 BIN2_L();		#endif#if (DRV8833_NUM >= 2)GPIO_InitStructure.GPIO_Pin = CIN1_PIN;			GPIO_Init(CIN1_PORT, &GPIO_InitStructure);					 CIN1_L();				GPIO_InitStructure.GPIO_Pin = CIN2_PIN;			GPIO_Init(CIN2_PORT, &GPIO_InitStructure);					 CIN2_L();				GPIO_InitStructure.GPIO_Pin = DIN1_PIN;			GPIO_Init(DIN1_PORT, &GPIO_InitStructure);					 DIN1_L();				GPIO_InitStructure.GPIO_Pin = DIN2_PIN;			GPIO_Init(DIN2_PORT, &GPIO_InitStructure);					 DIN2_L();	
#endifTIM2_Int_Init(100 - 1, 1);		//10Us中断
}//定时器2中断服务程序
void TIM2_IRQHandler(void)   //TIM2中断
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 {TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 set_drv8833_ctrl();}
}//设置DRV8833的参数 num  电机编号  dir 前后   speed 速度
void set_drv8833_para(WEL_NUM num,DIR_TypeDef dir,u8 speed)
{drv[num].drv_speed = speed;drv[num].drv_dir = dir;
}

3、蓝牙控制部分代码,这里把接收到手机发过来的摇杆数据,处理成每个电机的速度、前后。

u8 Query_IN_Response(void)
{u8 buff[USART2_MAX_RECV_LEN] = {0}; //接收数据缓冲区u8 recv_flag = 0;                    // 1:接收完成u16 Num = 0;u8 retn = 0;u8 i;u8 *ptemp = buff;if (USART2_RX_STA & 0x8000)          //接收完成{recv_flag = 1;Num = USART2_RX_STA & 0x3fff;memcpy(buff, (const char *)USART2_RX_BUF, Num); //拷贝数据USART2_RX_BUF[Num] = '\0';buff[Num] = '\0';        //加上字符串结束符;USART2_RX_STA = 0;        //清楚接收完成标志{
//            printf("\r\n接收完成: \"");
//            for(i = 0; i < Num; i++)
//                printf("%c", buff[i]);
//            printf("\"\r\n");}}if (recv_flag) //接收完成{if (strncmp((const char *)ptemp, "SPEED:", sizeof("SPEED:") - 1) == 0) //接收到正确的指令{retn = 1;} else if (strncmp((const char *)ptemp, "DIR:", sizeof("DIR:") - 1) == 0) //接收到正确的指令{retn = 2;} else if (strncmp((const char *)ptemp, "ROCKER:", sizeof("ROCKER:") - 1) == 0) //接收到正确的指令{ptemp += sizeof("ROCKER:[") - 1;sysFlag.rocker.x = atoi((const char *)ptemp);    //读取while(ptemp != NULL && *ptemp != '\0'){ptemp++;if(*ptemp == ','){sysFlag.rocker.y = atoi((const char *)(ptemp+1));    //读取break;}}if(sysFlag.rocker.y < 0){if(sysFlag.rocker.x>0){int speed1,speed2;if(sysFlag.rocker.x<PIANYI)sysFlag.rocker.x = 0;else sysFlag.rocker.x -= PIANYI;speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);if(speed1 > 100 )speed1 = 100;speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);if(speed2 < 0 )speed2 = 0;set_drv8833_para(AIN_NUM,DIR_FRONT,speed1);set_drv8833_para(BIN_NUM,DIR_FRONT,speed2);printf("右前 speed %d \t%d\r\n",speed1,speed2);}else{int speed1,speed2;if(sysFlag.rocker.x>-PIANYI)sysFlag.rocker.x = 0;else sysFlag.rocker.x += PIANYI;speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);if(speed1 < 0 )speed1 = 0;speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);if(speed2 > 99 )speed2 = 100;set_drv8833_para(AIN_NUM,DIR_FRONT,speed1);set_drv8833_para(BIN_NUM,DIR_FRONT,speed2);printf("左前 speed %d \t%d\r\n",speed1,speed2);}}else if(sysFlag.rocker.y > 0){if(sysFlag.rocker.x>0){int speed1,speed2;if(sysFlag.rocker.x<PIANYI)sysFlag.rocker.x = 0;else sysFlag.rocker.x -= PIANYI;speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);if(speed1 > 100 )speed1 = 100;speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);if(speed2 < 0 )speed2 = 0;set_drv8833_para(AIN_NUM,DIR_BACK,speed1);set_drv8833_para(BIN_NUM,DIR_BACK,speed2);printf("右后 speed %d \t%d\r\n",speed1,speed2);}else{int speed1,speed2;if(sysFlag.rocker.x>-PIANYI)sysFlag.rocker.x = 0;else sysFlag.rocker.x += PIANYI;speed1 = (abs(sysFlag.rocker.y)+sysFlag.rocker.x/2);if(speed1 < 0 )speed1 = 0;speed2 = (abs(sysFlag.rocker.y)-sysFlag.rocker.x/2);if(speed2 > 99 )speed2 = 100;set_drv8833_para(AIN_NUM,DIR_BACK,speed1);set_drv8833_para(BIN_NUM,DIR_BACK,speed2);printf("左后 speed %d \t%d\r\n",speed1,speed2);}}else if(sysFlag.rocker.y == 0 && sysFlag.rocker.x == 0){set_drv8833_para(AIN_NUM,DIR_STOP,0);set_drv8833_para(BIN_NUM,DIR_STOP,0);}
//			printf("rocker[%d, %d ]",sysFlag.rocker.x,sysFlag.rocker.y);retn = 3;}if(retn){
//            printf("处理OK\r\n");sysFlag.drv_chg_flag = 1;}}return retn;
}

5、手机APP设计
手机APP是使用的QT5设计,大家要是想要做这个项目的话一定要先下载一个APP看看是否可以搜索到蓝牙,因为有少部分网友说他的手机用这个APP是搜索不到蓝牙的,导致用不了这个APP进行连接,解决方法就是要换一个手机

在这里插入图片描述
在这里插入图片描述

二、更多功能小车网友推荐后开发…

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

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

相关文章

Go语言必知必会100问题-06 生产者端接口

生产者端接口 Go语言必知必会100问题-05 接口污染中介绍了程序中使用接口是有价值的。在编码的时候&#xff0c;接口应该放在哪里呢&#xff1f;这是Go开发人员经常有误解的一个问题&#xff0c;本文将深入分析该问题。 在深入探讨问题之前&#xff0c;先对提及的术语做一个定…

MySQL(2/3)

select和别名的使用 主要是用以查询数据 语法&#xff1a;select 字段 from 库名 -- *代表全部字段 select * from student; -- 可以查询多个字段&#xff0c;并使用as起别名&#xff0c;as可以省略 select id as bbb ,name as hhh from student; -- 可以使用函数concat(a,b…

新版Java面试专题视频教程——虚拟机篇②

新版Java面试专题视频教程——虚拟机篇② 3 垃圾收回3.1 简述Java垃圾回收机制&#xff1f;&#xff08;GC是什么&#xff1f;为什么要GC&#xff09;3.2 对象什么时候可以被垃圾器回收3.2.1 引用计数法3.2.2 可达性分析算法 3.3 JVM 垃圾回收算法有哪些&#xff1f;——4种3.3…

Python实现DAS单点登录

❇️ 流程 进入登录页面 &#xff08;DAS验证的登录页面&#xff09; 获取验证码图像&#xff0c;百度OCR识别 登录 &#x1f3de;️ 环境 Windows 11 Python 3.12 PyCharm 2023 &#x1f9f5; 准备工作 安装必要依赖库 bs4 Jupyter 推荐安装 Jupyter&#xff08;Anaco…

数学建模【相关性模型】

一、相关性模型简介 相关性模型并不是指一个具体的模型&#xff0c;而是一类模型&#xff0c;这一类模型用来判断变量之间是否具有相关性。一般来说&#xff0c;分析两个变量之间是否具有相关性&#xff0c;我们根据数据服从的分布和数据所具有的特点选择使用pearson&#xff…

Linux系统——Nginx小总结

目录 一、影响用户体验的因素 二、网络连接——Apache/Nginx服务请求过程 三、I/O模型——Input/Output模型 1.同步/异步 2.阻塞/非阻塞 3.同步异步/阻塞非阻塞组合 四、Nginx用法 一、影响用户体验的因素 客户端硬件配置客户端网络速率客户端与服务端距离服务端网络速…

【JS】事件绑定方法自带一个形参e“function(e)”,what is e?

在学习js的时候 我跳过了一部分章节的内容&#xff0c;导致现在学习react的时候很多内容都不知所措&#xff0c;因为这些教程都是建立在它认为你js所有内容都掌握的前提下&#xff0c;当然这是我自身的原因。需要反省。 下面是正题&#xff1a; 我们知道js有很多事件&#…

【一】【SQL】表的增删查改(部分)

表之“增”操作 建表的操作 mysql> create table students(-> id int unsigned primary key auto_increment,-> sn int unsigned unique key,-> name varchar(20) not null,-> qq varchar(32) unique key-> ); Query OK, 0 rows affected (0.03 sec)mysql&g…

Day01:Web应用架构搭建站库分离路由访问配置受限DNS解析

目录 常规的Web应用搭建 三种常规网站搭建模式 程序源码 中间件配置 数据库类型 文件访问路径 总结 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件…

Pytorch添加自定义算子之(1)-安装配置Eigen库

一、安装对应的ubuntu环境 推荐使用Docker FROM nvcr.io/nvidia/pytorch:23.01-py3 RUN pip install tensorboardX RUN pip install pyyaml RUN pip install yacs RUN pip install termcolor RUN pip install opencv-python RUN pip install timm0.6.12 WORKDIR /app COPY . …

Python入门必学:print函数--从基础语法到高级用法

Python入门必学&#xff1a;print函数–从基础语法到高级用法 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您…

2024 春招市场行情报告:鸿蒙人才遭“爆抢”

前言 2024年可以说是布道鸿蒙开发行业的最佳时机&#xff0c;华为在千帆启航仪式会中发布会中表示&#xff0c;已有200家头部企业加入原生开发当中&#xff0c;并且一直有高薪抢人的局面&#xff0c;这一信息已经引起业界很大关注。 因此有很多公司开始准备要招聘鸿蒙工程师&…

机器学习YOLO操作全流程​​编

YOLO介绍 Ultralytics YOLOv8,是最新的著名实时目标检测和图像分割模型。它基于深度学习和计算机视觉的最新进展,提供了无与伦比的速度和精度性能。由于其精简的设计,适用于各种应用,并且可以轻松适配不同的硬件平台,从边缘设备到云端API。 探索 YOLOv8 文档,这是一个全…

【MySQL】探索表结构、数据类型和基本操作

表、记录、字段 数据库的E-R&#xff08;entity-relationship&#xff0c;实体-关系&#xff09;模型中有三个主要概念&#xff1a; 实体集 、 属性 、 关系集 。 一个实体集对应于数据库中的一个表&#xff0c;一个实体则对应于数据库表 中的一行&#xff0c;也称为一条记录。…

【Linux基础】Linux自动化构建工具make/makefile

背景 会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件需要后…

2/26作业

1.link_stack.c #include "link_stack.h" //申请栈顶指针 top_p creat_top() { top_p top (top_p)malloc(sizeof(top_t)); if(topNULL) { printf("空间申请不成功\n"); return NULL; } top->len 0; top->…

Android基础进阶 - RecyclerView列表加载多类型视图

你是否会经常见到在同一个 RecyclerView 列表中加载多种不同的布局效果&#xff1f;最近写了一篇 ConcatAdapter 相关内容&#xff0c;发现虽然之前一直在使用多类型视图列表&#xff0c;但从未记录过&#xff0c;故重新记录于此 RecyclerView基础 Android进阶之路 - Recycler…

leet hot 100-6 三数之和

三数之和 原题链接思路代码 原题链接 leet hot 100-5 15. 三数之和 思路 从前往后定义第一个数字 first 开始遍历整个数组 然后要求 frist和上一个数字不重复否则就是重复组合 从frist往后遍历第二个数字 同样要求第二个数字不能重复 再定义第三个数字从后往前面数 三个数字…

Stable Diffusion 绘画入门教程(webui)-ControlNet(Shuffle)

Shuffle(随机洗牌)&#xff0c;这个预处理器会把参考图的颜色打乱搅拌到一起&#xff0c;然后重新组合的方式重新生成一张图&#xff0c;可以想象出来这是一个整体风格控制的处理器。 那么问题来了&#xff0c;官方为啥会设计个这样的处理器呢&#xff0c;主要是给懒人用的&am…

内网穿透的应用-如何在群晖配置WebDAV实现云同步Zotero科研文献与笔记【内网穿透】

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…