STM32智能小车—蓝牙控制并测速小车、wife控制小车、4g控制小车、语音控制小车

news2025/7/22 15:39:47

目录

1. 蓝牙控制小车

2. 蓝牙控制并测速小车

3. wifi控制测速小车

4. 4g控制小车

5.语音控制小车

5.1语音模块配置:

5.2 语音控制小车开发和调试代码


1. 蓝牙控制小车

  • 使用蓝牙模块,串口透传
  • 蓝牙模块,又叫做蓝牙串口模块

串口透传技术:

  • 透传即透明传送,是指在数据的传输过程中,通过无线的方式这组数据不发生任何形式的改变,仿 佛传输过程是透明的一样,同时保证传输的质量,原封不动地到了最终接收者手里。
  • 以太网,蓝牙,Zigbee, GPRS 等模块玩法一样,对嵌入式程序员来说,不需要关心通讯模块内部数据 及协议栈工作原理,只要通过串口编程获得数据即可

代码实现:

整合前面串口控制小车代码,接入蓝牙模块

if (!strcmp(UART1_RX_Buffer, "M1"))
{
    goForward();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M2"))
{
    goBack();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M3"))
{
    goLeft();
    HAL_Delay(10);
}
else if (!strcmp(UART1_RX_Buffer, "M4"))
{
    goRight();
    HAL_Delay(10);
}
else
    stop();

2. 蓝牙控制并测速小车

原理:

使用蓝牙模块和测速模块,将两者代码整合

代码实现:

usart.c

#include "usart.h"

#include "string.h"
#include "stdio.h"
#include "motor.h"

//串口接收缓存(1字节)
uint8_t buf=0;

//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200

// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];

//  接收状态
//  bit15,      接收完成标志
//  bit14,      接收到0x0d
//  bit13~0,    接收到的有效字节数目
uint16_t UART1_RX_STA=0;

#define SIZE 12

char buffer[SIZE];

// 接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 判断中断是由哪个串口触发的
	if(huart->Instance == USART1)
	{
		// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
		if((UART1_RX_STA & 0x8000) == 0)
		{
			// 如果已经收到了 0x0d (回车),
			if(UART1_RX_STA & 0x4000)
			{
				// 则接着判断是否收到 0x0a (换行)
				if(buf == 0x0a)
				{
					// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
					UART1_RX_STA |= 0x8000;

					// 灯控指令
					if(!strcmp(UART1_RX_Buffer, "M1"))
						goForward();
					else if(!strcmp(UART1_RX_Buffer, "M2"))
						goBack();
					else if(!strcmp(UART1_RX_Buffer, "M3"))
						goLeft();
					else if(!strcmp(UART1_RX_Buffer, "M4"))
						goRight();
					else
						stop();
					
					memset(UART1_RX_Buffer, 0, UART1_REC_LEN);
					UART1_RX_STA = 0;
				}
				else
					// 否则认为接收错误,重新开始
					UART1_RX_STA = 0;
			}
			else	// 如果没有收到了 0x0d (回车)
			{
				//则先判断收到的这个字符是否是 0x0d (回车)
				if(buf == 0x0d)
				{
					// 是的话则将 bit14 位置为1
					UART1_RX_STA |= 0x4000;
				}
				else
				{
					// 否则将接收到的数据保存在缓存数组里
					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
					UART1_RX_STA++;
					
					// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
					if(UART1_RX_STA > UART1_REC_LEN - 1)
						UART1_RX_STA = 0;
				}
			}
		}
		// 重新开启中断
		HAL_UART_Receive_IT(&huart1, &buf, 1);
	}
}

int fputc(int ch, FILE *f)
{      
	unsigned char temp[1]={ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);  
	return ch;
}

oled.c

#include "oled.h"
#include "i2c.h"
#include "oledfont.h"

void Oled_Write_Cmd(uint8_t dataCmd)
{
	
	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,
										&dataCmd, 1, 0xff);
}

void Oled_Write_Data(uint8_t dataData)
{
	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
										&dataData, 1, 0xff);
}

void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address  
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128   
	Oled_Write_Cmd(0xA1);//set segment remap 
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	
	Oled_Write_Cmd(0xAF);//--turn on oled panel		
}

void Oled_Screen_Clear(void){
	char i,n;
	Oled_Write_Cmd (0x20);                    //set memory addressing mode
	Oled_Write_Cmd (0x02);                    //page addressing mode

	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xb0+i);               
		Oled_Write_Cmd(0x00);                 
		Oled_Write_Cmd(0x10);                 
		for(n=0;n<128;n++)Oled_Write_Data(0x00); 			
	}	
}

void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
	unsigned int  i;
	Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high	
	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}

	Oled_Write_Cmd(0xb0+(row*2-1));                           //page 1
	Oled_Write_Cmd(0x00+(col&0x0f));                          //low
	Oled_Write_Cmd(0x10+(col>>4));                            //high
	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
		Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
	}		
}


/******************************************************************************/
// 函数名称:Oled_Show_Char 
// 输入参数:oledChar 
// 输出参数:无 
// 函数功能:OLED显示单个字符
/******************************************************************************/
void Oled_Show_Str(char row,char col,char *str){
	while(*str!=0){
		Oled_Show_Char(row,col,*str);
		str++;
		col += 8;	
	}		
}

main.c

extern uint8_t buf;
unsigned int speedCnt = 0;
char speedMes[24];  //主程序发送速度数据的字符串缓冲区

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (GPIO_Pin == GPIO_PIN_14)
		if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET)
			speedCnt++;
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	printf("speed: %d\r\n", speedCnt);
	sprintf(speedMes,"speed:%2d cm/s",speedCnt);//串口数据的字符串拼装,speed是格子,每个格子1cm
	Oled_Show_Str(2,2,speedMes);
	speedCnt = 0;
}

3. wifi控制测速小车

  • Wifi模块-ESP-01s
  • 蓝牙,ESP-01s,Zigbee, NB-Iot等通信模块都是基于AT指令的设计

AT指令介绍:

  • AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
  • 其对所传输的数据包大小有定义:即对于AT指令的发送,除AT两个字符外,最多可以接收1056个 字符的长度(包括最后的空字符)。
  • 每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response 响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应。AT指令以回车作为结 尾,响应或上报以回车换行为结尾。

硬件接线

  • 把esp8266插进串口1

使用方法:

Wifi模块-ESP-01s_esp01s波特率-CSDN博客

4. 4g控制小车

原理:运用EC03-DNC4G通信模块

模块介绍:

  • 基于串口AT指令的开发方式
  • 有两种工作模式,默认是透传模式,通过其他方式进入AT指令模式
  • 注意插卡不要出错,下图红色位置为SIM卡状态灯,亮才是正常

代码不做修改,直接基于蓝牙小车整合, 4g模块只要做好外网透传就可以了

5.语音控制小车

5.1语音模块配置

使用SU-03T / LD3320

具体介绍看我之前写过的博客:SU-03T语音模块的使用_罗小白的干爹的博客-CSDN博客

  

5.2 语音控制小车开发和调试代码

硬件接线:

 循迹小车: 

  • 循迹模块(左) -- PB3
  • 循迹模块(右) -- PB4

 跟随小车: 

  • 跟随模块(左) -- PA8
  • 跟随模块(右) -- PA9

 避障小车: 

  • sg90:PB9
  • Trig:PA10
  • Echo:PA11

 OLED模块: 

  • SCL -- PB6
  • SDA -- PB7

 语音模块: 

  • A25 -- PA15 (跟随)
  • A26 -- PA13 (避障)
  • A27 -- PA14 (循迹)

cubeMX配置  

代码示例:

#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "gpio.h"

#include "sg90.h"
#include "sr04.h"
#include "motor.h"
#include "oled.h"

#define MIDDLE 0
#define LEFT 1
#define RIGHT 2

#define BZ 1
#define XJ 2
#define GS 3

#define LeftWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value_XJ HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)

#define LeftWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
#define RightWheel_Value_GS HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)

#define A25 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_15)
#define A26 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_13)
#define A27 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_14)

void SystemClock_Config(void);

char dir;

void xunjiMode()
{
	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_RESET)
		goForward();
	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_RESET)
		goLeft();
	if(LeftWheel_Value_XJ == GPIO_PIN_RESET && RightWheel_Value_XJ == GPIO_PIN_SET)
		goRight();
	if(LeftWheel_Value_XJ == GPIO_PIN_SET && RightWheel_Value_XJ == GPIO_PIN_SET)
		stop();
}

void gensuiMode()
{
	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_RESET)
		goForward();
	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_RESET)
		goRight();
	if(LeftWheel_Value_GS == GPIO_PIN_RESET && RightWheel_Value_GS == GPIO_PIN_SET)
		goLeft();
	if(LeftWheel_Value_GS == GPIO_PIN_SET && RightWheel_Value_GS == GPIO_PIN_SET)
		stop();
}

void bizhangMode()
{
	double disMiddle;
	double disLeft;
	double disRight;

	if(dir != MIDDLE){
		sgMiddle();
		dir = MIDDLE;
		HAL_Delay(300);
	}
	disMiddle = get_distance();
	
	if(disMiddle > 35){
		//前进
		goForward();
	}else if(disMiddle < 10){
		goBack();
		
	}else
	{
		//停止
		stop();
		//测左边距离
		sgLeft();
		HAL_Delay(300);
		disLeft = get_distance();
		
		sgMiddle();
		HAL_Delay(300);
		
		sgRight();
		dir = RIGHT;
		HAL_Delay(300);
		disRight = get_distance();
		
		if(disLeft < disRight){
			goRight();
			HAL_Delay(150);
			stop();
		}
		if(disRight < disLeft){
			goLeft();
			HAL_Delay(150);
			stop();
		}
	}
	HAL_Delay(50);
}

int main(void)
{
    int mark = 0;

    HAL_Init();


    SystemClock_Config();


    MX_GPIO_Init();
    MX_TIM4_Init();
    MX_TIM2_Init();
    MX_I2C1_Init();

	initSG90();
	HAL_Delay(1000);
	dir = MIDDLE;
	Oled_Init();
	Oled_Screen_Clear();
	Oled_Show_Str(2,2,"-----Ready----");

  while (1)
  {

		//满足寻迹模式的条件
		if(A25 == 1 && A26 == 1 && A27 == 0){
			if(mark != XJ){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----XunJi----");
			}
			mark = XJ;
			xunjiMode();
		}
		//满足跟随模式的条件
		if(A25 == 0 && A26 == 1 && A27 == 1){
			if(mark != GS){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----GenSui----");
			}
			mark = GS;
			gensuiMode();
		}
		//满足避障模式的条件
		if(A25 == 1 && A26 == 0 && A27 == 1){
			if(mark != BZ){
				Oled_Screen_Clear();
				Oled_Show_Str(2,2,"-----BiZhang----");
			}
			mark = BZ;
			bizhangMode();
		}
  }
}


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

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

相关文章

ts 简易封装 axios,统一 API

文章目录 为什么要封装目标文件结构封装通用请求方法获得类型提示http 方法文件上传使用示例实例化post 请求类型提示文件上传 总结完整代码&#xff1a; 为什么要封装 axios 本身已经很好用了&#xff0c;看似多次一举的封装则是为了让 axios 与项目解耦。比如想要将网络请求…

Apache Doris (五十): Doris表结构变更-动态分区(2)

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录

4.数据库的基本操作

1.创建数据库 系统安装完成后会有部分默认数据库存在: 注意&#xff1a;mysql的语句每一个输入完后要有分号才能写下一个 这初始的四个库不要删除 其中:mysql数据库中存储用户访问权限。 创建自己的数据库命令如下: create database database_name&#xff08;数据库名字&am…

【教3妹学编辑-算法题】参加会议的最多员工数

2哥 :“我要飞的更高&#xff0c; 飞的更高哦……” 3妹&#xff1a;2哥唱的啥呀&#xff0c; 都跑调啦 2哥 :3妹&#xff0c;昨天神州十六返回舱成功着陆&#xff0c;载人飞行任务取得圆满成功。中国真是太厉害了。 3妹&#xff1a;是啊&#xff0c; 此生无悔入华夏~不跟你多说…

案例分析真题-软件架构风格

案例分析真题-软件架构风格 2009年真题 【问题1】 【问题2】 【问题3】 2010年真题 【问题1】 【问题2】 【问题3】 2010年真题 【问题1】 【问题2】 【问题3】 2010年真题 【问题1】 【问题2】 【问题3】 2012年真题 【问题1】 骚戴理解&#xff1a; 首先要知道过滤器-管道的…

阿里云服务器99元一年续费原价注意事项不能变配!

阿里云优惠活动上提供的99元服务器为云服务器ECS经济型e实例&#xff0c;2核2G配置、3M公网带宽和40G ESSD entry 系统盘&#xff0c;续费不涨价&#xff0c;但是一定要注意不能变更云服务器配置&#xff0c;否则续费就涨价&#xff01;阿腾云atengyun.com来详细说下阿里云99元…

酷开科技酷开系统,打造专属家庭云课堂,让学习变得更温馨、有趣

随着科技和时代的不断发展&#xff0c;家庭云课堂已经成为了一种新型的学习方式。酷开科技就通过自主研发的酷开系统凭借其强大的功能和海量的内容&#xff0c;为家庭消费者们打造了一个专属的学习平台。 不用再为孩子享受不到教育资源而感到遗憾&#xff0c;在这里酷开系统为孩…

当女朋友要求你用Python画一个粉粉的Hello Kitty的时候

先看效果图 完整代码 import math import turtle as t# 计算长度、角度 t1:画笔对象 r:半径 angle:扇形&#xff08;圆形&#xff09;的角度 def myarc(t1, r, angle):arc_length 2 * math.pi * r * angle / 360 # angle角度的扇形的弧长n int(arc_length / 3) 1 # 线段…

高性能消息中间件 - Kafka3.x(一)

文章目录 高性能消息中间件 - Kafka3.x&#xff08;一&#xff09;搭建Kafka3.2.1集群⭐Kafka集群机器规划创建3台虚拟机&#xff08;centos7系统&#xff09;必要的环境准备&#xff08;3台虚拟机都要执行如下操作&#xff09;⭐分别修改每个服务器的hosts文件&#xff08;将上…

【zip密码】如何取消zip压缩包的密码?

我们想要对压缩包进行加密&#xff0c;但是当我们不需要加密压缩包的时候&#xff0c;该如何取消zip压缩包密码呢&#xff1f;那么zip压缩包密码取消都有什么方法呢&#xff1f;今天将方法总结分享给大家。 最原始的方法&#xff0c;就是通过解压文件&#xff0c;将解压出的文…

HTTP和HTTPS本质区别——SSL证书

HTTP和HTTPS是两种广泛使用的协议&#xff0c;尽管它们看起来很相似&#xff0c;但是它们在网站数据传输的安全性上有着本质上的区别。 HTTP是明文传输协议&#xff0c;意味着通过HTTP发送的数据是未经加密的&#xff0c;容易受到拦截、窃听和篡改的风险。而HTTPS通过使用SSL或…

使用了Buzz库的HttpClient类来设置代理

php<?php//设置代理服务器//初始化Buzz库$browsernew Browser();//使用代理服务器$browser->setHttpClient(new HttpClient(array(proxy_host>$proxy_host,proxy_port>$proxy_port,)));//创建Crawler对象$crawler$browser->getCrawler();//访问www.jd.com$craw…

全志R128芯片如何解决打包时出现的ERROR: update_mbr failed?

打包出现&#xff1a;ERROR: update_mbr failed 249930 records in 249930 records out 6398208 bytes (6.4 MB) copied, 0.0271082 s, 236 MB/s ERROR: dl file rtos_riscv.fex size too large ERROR: filename rtos_riscv.fex ERROR: dl_file_size 1579 sector ERROR: part…

研发效能城市沙龙【11月12日】深圳站-《敏捷环境下的测试自动化实践指南》—陈晓鹏丨IDCF

IDCF社区研发效能城市沙龙是一个开放、共享的平台&#xff0c;我们欢迎每一位参加者积极分享自己的经验和见解&#xff0c;构建一个互联互通的技术社区。 随着这几年业务的快速变化诉求以及敏捷开发方法的流行&#xff0c;越来越多的组织都采用敏捷模式进行项目开发。而这种时…

Project#2: Extendible Hash Index

文章目录 准备Task #1-Read/Write Page GuardsBasicPageGuard/ReadPageGuard/WritePageGuardUpgradeWrappersTests Task #2-Extendible Hash Table PagesHash Table Header Pages**成员变量&#xff1a;****方法实现&#xff1a;** Hash Table Directory Pages**成员变量&#…

财务数字化转型的切入点是什么?_光点科技

随着科技的不断进步&#xff0c;数字化转型已经成为各个行业追求的目标&#xff0c;财务领域也不例外。那么&#xff0c;财务数字化转型的切入点在哪里呢&#xff1f;如何确保转型的成功进行&#xff1f; 数据整合与管理 财务数据的准确性与及时性是财务管理的基石。数字化转型…

1.认识下Docker

1.Docker为什么会火? Docker能火起来&#xff0c;不仅仅与它开源有关系&#xff0c;一定是解决了我们软件从开发到上线的痛点&#xff0c;要不然几乎不可能。那Docker到底解决了什么问题&#xff1f;下面这几点&#xff0c;相信我们程序员都深有感触: 1. 解决环境不一致的问…

pthread_attr_getstacksize 问题

最近公司里遇到一个线程栈大小的问题&#xff0c;借此机会刚好学习一下这个线程栈大小相关的函数。如果公司里用的还是比较老的代码的话&#xff0c;都是用的 pthread 库支持线程的&#xff0c;而不是 c11 里的线程类。主要有两个相关函数&#xff1a;pthread_attr_setstacksiz…

Android 10-11适配外部存储方案

Android Api 29 对文件和文件夹进行了重大更改。不允许使用外部存储&#xff0c;如下方法&#xff1a; Environment.getExternalStorageDirectory() /mnt/sdcard Environment.getExternalStoragePublicDirectory(“test”) /mnt/sdcard/test 只能使用内部存储 getExterna…

安防监控项目---web网页通过A9控制Zigbee终端节点的风扇

文章目录 前言一、zigbee的CGI接口二、请求线程和硬件控制三、现象展示总结 前言 书接上期&#xff0c;我们可以看一下前面的功能设计的部分&#xff0c;网页端的控制还有一个&#xff0c;那就是通过网页来控制zigbee上的风扇节点&#xff0c;这部分的工作量是相当大的&#x…