STM32智能小车(循迹、跟随、避障、测速、蓝牙、wifi、4g、语音识别)总结

前言

有需要帮忙代做51和32小车或者其他单片机项目,课程设计,报告,PCB原理图的小伙伴,可以在文章最下方加我V交流咨询,本篇文章的小车所有功能实现的代码还有硬件清单放在资源包里,有需要的自行下载即可!

目录

1.电机模块开发

1.1 让小车动起来

1.2 串口控制小车方向

1.3 如何进行小车PWM调速

1.4 PWM方式实现小车转向

2.循迹小车 

2.1 循迹模块使用

2.2 循迹小车原理

2.3 循迹小车核心代码

2.4 循迹小车解决转弯平滑问题

3.跟随/避障小车

3.1 红外壁障模块分析​编辑

3.2 跟随小车的原理

3.3 跟随小车开发和调试代码

3.4 超声波模块介绍

3.5 舵机模块介绍

3.6 摇头避障小车开发和调试代码

4.测速小车

4.1 测速模块

4.2 测试原理和单位换算

4.3 定时器和中断实现测速开发和调试代码

4.4 小车速度显示在OLED屏

5.远程控制小车

5.1 蓝牙控制小车

5.2 蓝牙控制并测速小车

5.3 wifi控制测速小车

5.4 4g控制小车

6.语音控制小车

6.1语音模块配置:

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


1.电机模块开发

L9110s概述

接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,具体根据实际调试

IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;

IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;

IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;

IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;

接线参考:

B-1A -- PA0

B-1B -- PB1

A-1A -- PA1

A-1B -- PB10 

1.1 让小车动起来

代码实现:

motor.c

#include "motor.h"
void goForward(void)
{// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);// 右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goBack(void)
{// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);// 右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
}
void goLeft(void)
{// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);// 右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void goRight(void)
{// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);// 右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}
void stop(void)
{// 左轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);// 右轮HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
}

motor.h

#ifndef __MOTOR_H__
#define __MOTOR_H__
#include "main.h"
void goForward(void);
void goBack(void);
void goLeft(void);
void goRight(void);
void stop(void);
#endif

main.c

#include "motor.h"//main函数的while循环部分:
while (1)
{goForward();HAL_Delay(1000);goBack();HAL_Delay(1000);goLeft();HAL_Delay(1000);goRight();HAL_Delay(1000);stop();HAL_Delay(1000);
}
1.2 串口控制小车方向
  • 串口分文件编程进行代码整合——通过现象来改代码
  • 接入蓝牙模块,通过蓝牙控制小车
  • 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能

代码实现:

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 12char 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 位置为1UART1_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();elsestop();memset(UART1_RX_Buffer, 0, UART1_REC_LEN);UART1_RX_STA = 0;}else// 否则认为接收错误,重新开始UART1_RX_STA = 0;}else	// 如果没有收到了 0x0d (回车){//则先判断收到的这个字符是否是 0x0d (回车)if(buf == 0x0d){// 是的话则将 bit14 位置为1UART1_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;
}
1.3 如何进行小车PWM调速

原理

全速前进是LeftCon1A = 0; LeftCon1B = 1;

完全停止是LeftCon1A = 0;LeftCon1B = 0;

那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!

开发:借用PWM的舵机控制代码

将控制车轮的4个 GPIO 口配置修改如下,否则小车动不起来。

原因:L9110每个控制口需要一高一低才可以动起来,如果PWM有效电平为高电平,则另一个 GPIO口则需要输出低电平才可以驱动轮子。

代码实现:

main.c

// main函数里
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
while (1)
{__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 8);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 8);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 10);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 10);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, 15);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2, 15);HAL_Delay(1000);
}
1.4 PWM方式实现小车转向

右转原理: 左轮速度大于右轮

左转原理: 右轮速度大于左轮

左右轮各自调速代码实现:

// main函数里
while (1)
{__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);HAL_Delay(1000);
}

2.循迹小车 

2.1 循迹模块介绍
  • TCRT5000传感器的红外发射二极管不断发射红外线
  • 当发射出的红外线没有被反射回来或被反射回来但强度不够大时
  • 红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
  • 被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和
  • 此时模块的输出端为低电平,指示二极管被点亮
  • 总结就是一句话,没反射回来,D0输出高电平,灭灯

接线方式

  • VCC:接电源正极(3-5V)
  • GND:接电源负极 DO:TTL开关信号输出0、1
  • AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
2.2 循迹小车原理

由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致 循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED常亮

总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯

2.3 循迹小车核心代码

硬件接线

  • B-1A -- PA0
  • B-1B -- PB1
  • A-1A -- PA1
  • A-1B -- PB10
  • 循迹模块(左)--  PB3
  • 循迹模块(右) -- PB4

代码示例:

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)// main函数里
while (1)
{if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)goForward();if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)goLeft();if (LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)goRight();if (LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)stop();
}
2.4 循迹小车解决转弯平滑问题

原理:两轮都有速度且一轮速度大于另一轮

代码实现:

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4)
// main函数里
while (1)
{if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET){__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,19);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,19);}if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET){__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,15);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,8);}if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET){__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,8);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,15);}if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET){__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1,0);__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_2,0);}
}

3.跟随/避障小车

3.1 红外壁障模块分析

原理和循迹是一样的,循迹红外观朝下,跟随朝前

3.2 跟随小车的原理
  • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
  • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
3.3 跟随小车开发和调试代码

硬件接线

  • B-1A -- PB0
  • B-1B -- PB1
  • A-1A -- PB2
  • A-1B -- PB10
  • 跟随模块(左) -- PB5
  • 跟随模块(右) -- PB6

代码示例:

#define LeftWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5)
#define RightWheel_Value HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6)
// main函数里
while (1)
{if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_RESET)goForward();if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_RESET)goRight();if(LeftWheel_Value == GPIO_PIN_RESET && RightWheel_Value == GPIO_PIN_SET)goLeft();if(LeftWheel_Value == GPIO_PIN_SET && RightWheel_Value == GPIO_PIN_SET)stop();
}
3.4 超声波模块介绍

使用超声波模块,型号:HC-SR04

  • 怎么让它发送波 Trig ,给Trig端口至少10us的高电平
  • 怎么知道它开始发了 Echo信号,由低电平跳转到高电平,表示开始发送波
  • 怎么知道接收了返回波 Echo,由高电平跳转回低电平,表示波回来了
  • 怎么算时间 Echo引脚维持高电平的时间! 波发出去的那一下,开始启动定时器 波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
  • 怎么算距离 距离 = 速度 (340m/s)* 时间/2

时序图:

3.5 舵机模块介绍

 1. 什么是舵机

如下图所示,最便宜的舵机sg90,常用三根或者四根接线,黄色为PWM信号控制 用处:垃圾桶项目开盖用、智能小车的全比例转向、摄像头云台、机械臂等 常见的有0-90°、0-180°、0-360°

2. 怎么控制舵机

向黄色信号线“灌入”PWM信号

PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右

确定周期/频率:

如果周期为20ms,则 PSC=7199,ARR=199

角度控制

0.5ms-------------0度; 2.5% 对应函数中CCRx为5

1.0ms------------45度; 5.0% 对应函数中CCRx为10

1.5ms------------90度; 7.5% 对应函数中CCRx为15

2.0ms-----------135度; 10.0% 对应函数中CCRx为20

2.5ms-----------180度; 12.5% 对应函数中CCRx为25

3.6 摇头避障小车开发和调试代码

硬件接线

  • sg90 -- PB9

cubeMX配置

代码实现

sg90.c

#include "sg90.h"
#include "gpio.h"
#include "tim.h"
void initSG90(void)
{HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgMiddle(void)
{__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 17); //将舵机置为90度
}
void sgRight(void)
{__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
}
void sgLeft(void)
{__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 25); //将舵机置为180度
}

sg90.h

#ifndef __SG90_H__
#define __SG90_H__void initSG90(void);
void sgMiddle(void);
void sgRight(void);
void sgLeft(void);#endif

main.c

initSG90();
HAL_Delay(1000);while (1)
{sgLeft();HAL_Delay(1000);sgMiddle();HAL_Delay(1000);sgRight();HAL_Delay(1000);sgMiddle();HAL_Delay(1000);
}

封装超声波传感器

超声波模块接线:

  • Trig       --    PB7
  • Echo     -- PB8

cubeMX配置

代码实现

sr04.c

#include "sr04.h"
#include "gpio.h"
#include "tim.h"//使用TIM2来做us级延时函数
void TIM2_Delay_us(uint16_t n_us)
{/* 使能定时器2计数 */__HAL_TIM_ENABLE(&htim2);__HAL_TIM_SetCounter(&htim2, 0);while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );/* 关闭定时器2计数 */__HAL_TIM_DISABLE(&htim2);
}double get_distance(void)
{int cnt=0;//1. Trig ,给Trig端口至少10us的高电平HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);//拉高TIM2_Delay_us(20);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);//拉低//2. echo由低电平跳转到高电平,表示开始发送波//波发出去的那一下,开始启动定时器while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET);//等待输入电平拉高HAL_TIM_Base_Start(&htim2);__HAL_TIM_SetCounter(&htim2,0);//3. 由高电平跳转回低电平,表示波回来了while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET);//等待输入电平变低//波回来的那一下,我们开始停止定时器HAL_TIM_Base_Stop(&htim2);//4. 计算出中间经过多少时间cnt = __HAL_TIM_GetCounter(&htim2);//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)return (cnt*340/2*0.000001*100); //单位:cm
}

sr04.h

#ifndef __SR04_H__
#define __SR04_H__double get_distance(void);#endif

main.c

while (1)
{if(dir != MIDDLE){sgMiddle();dir = MIDDLE;HAL_Delay(300);}disMiddle = get_distance();if(disMiddle > 35){//前进}else{//停止//测左边距离sgLeft();HAL_Delay(300);disLeft = get_distance();sgMiddle();HAL_Delay(300);sgRight();dir = RIGHT;HAL_Delay(300);disRight = get_distance();}
}

封装电机驱动

代码实现:

while (1)
{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);
}

4.测速小车

4.1 测速模块

  • 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
  • 有遮挡,输出高电平;无遮挡,输出低电平
  • 接线 :VCC 接电源正极3.3-5V
  • GND 接电源负极 DO TTL开关信号输出
  • AO 此模块不起作用
4.2 测试原理和单位换算
  • 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
  • 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
  • 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
  • 假设一秒有80脉冲,那么就是80cm/s
4.3 定时器和中断实现测速开发和调试代码

测试数据通过串口发送到上位机

硬件接线

测速模块:

  • VCC -- 3.3V 不能接5V,否则遮挡一次会触发3次中断
  • OUT -- PB14

cubeMX配置

代码实现:

unsigned int speedCnt;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);speedCnt = 0;
}main函数里:
HAL_TIM_Base_Start_IT(&htim2);
4.4 小车速度显示在OLED屏

OLED模块介绍:STM32 OLED屏幕显示详解

硬件接线

  • SCL -- PB6
  • SDA -- PB7

代码示例:

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 offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address  Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128   Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_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 modeOled_Write_Cmd (0x02);                    //page addressing modefor(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-2unsigned int  i;Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0Oled_Write_Cmd(0x00+(col&0x0f));                          //lowOled_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 1Oled_Write_Cmd(0x00+(col&0x0f));                          //lowOled_Write_Cmd(0x10+(col>>4));                            //highfor(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是格子,每个格子1cmOled_Show_Str(2,2,speedMes);speedCnt = 0;
}

5.远程控制小车

5.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);
}
elsestop();
5.2 蓝牙控制并测速小车

原理:运用上面讲到的蓝牙模块和测速模块,将两者代码整合

5.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博客

5.4 4g控制小车

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

模块介绍:

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

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

6.语音控制小车

6.1语音模块配置

使用SU-03T / LD3320

具体介绍看我之前写过的博客:SU-03T语音模块的使用_su-03t开发板语音指令-CSDN博客

  

6.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.mzph.cn/diannao/66340.shtml

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

相关文章

微服务篇-深入了解 Elasticsearch DSL 查询和 RestClient 查询、数据聚合(Bucket 聚合、带条件聚合、Metric 聚合)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 DSL 查询 1.1 叶子查询 1.1.1 全文检索查询 1.1.2 精确查询 1.2 复合查询 1.2.1 bool 查询 1.3 排序 1.4 分页 1.4.1 深度分页 1.5 高亮 1.5.1 实现高亮 2.0 Rest…

使用Apache Mahout制作 推荐引擎

目录 创建工程 基本概念 关键概念 基于用户与基于项目的分析 计算相似度的方法 协同过滤 基于内容的过滤 混合方法 创建一个推荐引擎 图书评分数据集 加载数据 从文件加载数据 从数据库加载数据 内存数据库 协同过滤 基于用户的过滤 基于项目的过滤 添加自定…

javaEE-网络编程4.TCP回显服务器

目录 TCP流套接字编程 一.API介绍 ServerSocket类 构造方法&#xff1a; ​编辑方法&#xff1a; Socket类 构造方法&#xff1a; 方法&#xff1a; 二、TCP连接 三、通过TCP实现回显服务器 TCP服务端&#xff1a; 1.创建Socket对象 2.构造方法 3.start方法 TCP客…

数据库1-4讲

各种名词区分 内模式也叫物理模式、存储模式。 概念模式也叫全局模式、逻辑模式。 外模式也叫用户模式。 笛卡尔积&#xff1a;D1、D2、D3集合中任取一个的所有可能情况。 因此上述笛卡尔积的基数22312 关系模型的三个完整性&#xff1a; 实体完整性&#x…

UnityWebGl:打包成webgl后UGUI不显示文字(中文)问题

是由于unity默认使用的是Arial,导致打包成webgl时中文不显示 解决方案&#xff1a; 可在电脑C盘下&#xff0c;路径为C:\Windows\Fonts 找个中文简体的字体文件放到unity里面&#xff0c;格式必须为. ttf

朴素贝叶斯方法

一般来说训练时的一个实例有很多属性用一个<a1,a2,....,an>来表示一个数据&#xff0c;那么此时根据最大后验概率的计算公式可以得出&#xff1a; 其中&#xff0c; H 是目标值集合。 估计每个 P&#xff08;hi&#xff09;很容易&#xff0c; 只要计算每个目标值 hi出现…

Launcher3主页面加载显示流程分析

布局结构 抓取布局后&#xff0c;可以看到每个图标是一个DoubleShadowBubbleTextView&#xff0c;父布局是CellLayout、workspace。 我们可以在CellLayout添加子view打印出调用堆栈信息&#xff0c;可以整体上看页面加载显示流程。 主要类 Launcher.java&#xff1a;主界面&…

C++编程进阶:标准库中的算法库解析

文章目录 概述1. 非修改性序列操作2. 修改性序列操作3. 排序相关算法4. 二分查找算法5. 合并与集合操作6. 堆操作7. 最小/最大操作8. 数值算法(`<numeric>`头文件)概述 算法库总览:介绍了C++ 标准库提供的海量算法,这些算法作用于各类容器(如vector、list、set等)和…

Express 加 sqlite3 写一个简单博客

例图&#xff1a; 搭建 命令&#xff1a; 前提已装好node.js 开始创建项目结构 npm init -y package.json:{"name": "ex01","version": "1.0.0","main": "index.js","scripts": {"test": &q…

Linux双端口服务器:端口1的文件系统目录挂载到端口2

目录 一、服务器安装NFS服务并配置二、文件挂载三、持久化挂载总结为什么服务器配置多个端口 目前有一台服务器&#xff0c;不过他设置了两个SSH的端口&#xff0c;通过下面方法可以让这两个端口连接的主机能够共享同一个文件系统&#xff0c;原本这两个端口的文件系统是隔离的…

nginx-灰度发布策略(split_clients)

一. 简述&#xff1a; 基于客户端的灰度发布&#xff08;也称为蓝绿部署或金丝雀发布&#xff09;是一种逐步将新版本的服务或应用暴露给部分用户&#xff0c;以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx&#xff0c;可以通过配置和使用不同的模块来实现基于…

【NLP自然语言处理】Transformer模型的几大核心优势与应用前景

目录 &#x1f354; Transformer的并行计算 &#x1f354; Transformer架构的并行化过程 2.1 Transformer架构中Encoder的并行化 2.2 Transformer架构中Decoder的并行化 &#x1f354; Transformer的特征抽取能力 &#x1f354; 为什么说Transformer可以代替seq2seq? 4…

数据结构与算法之排序

9.1 排序的概念 1. 排序的定义 定义&#xff1a;排序是将表中的记录按关键字递增&#xff08;或递减&#xff09;有序排列的过程。说明&#xff1a;数据中可以存在相同关键字的记录。本章主要考虑递增排序。扩展&#xff1a;排序是数据处理中的基本操作之一&#xff0c;广泛应用…

《C++11》各种初始化方式的详细列举与对比

在 C 中&#xff0c;初始化对象的方式多种多样。随着 C 标准的演进&#xff0c;特别是 C11 的引入&#xff0c;初始化方式得到了显著的扩展和改进。本文将详细列举 C 中的各种初始化方式&#xff0c;并对它们进行对比&#xff0c;帮助开发者更好地理解和应用这些特性。 1. C98…

基于 Python Django 的西西家居全屋定制系统(源码+部署+文档)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

25考研|重邮软件工程复试攻略!

与计算机一样&#xff0c;重邮复试不合格也很有可能被淘汰&#xff01;快快认真准备起来&#xff01; 一、复试内容 1、笔试&#xff1a;分值100 2、综合面试&#xff1a;满分100 主要考核考生的综合素质和业务能力&#xff0c;由各招生学院具体组织实施&#xff0c;综合面试…

如何制作重识别数据集及如何解决all query identities do not appear in gallery的问题

如何制作重识别数据集 数据集制作链接 注意点&#xff1a; 按照上述方式制作完成数据集之后&#xff0c;分别建立3个文件夹&#xff0c;分别为train&#xff0c;test&#xff0c;query&#xff0c; 值得注意的是&#xff0c;query文件里的相机编号要进行修改&#xff0c;修改…

链地址法(哈希桶)

链地址法&#xff08;哈希桶&#xff09; 解决冲突的思路 开放定址法中所有的元素都放到哈希表⾥&#xff0c;链地址法中所有的数据不再直接存储在哈希表中&#xff0c;哈希表 中存储⼀个指针&#xff0c;没有数据映射这个位置时&#xff0c;这个指针为空&#xff0c;有多个数…

【C语言】可移植性陷阱与缺陷(七): 除法运算时发生的截断

在C语言编程中&#xff0c;除法运算可能会引发一些与可移植性相关的问题&#xff0c;特别是当涉及到整数除法时发生的截断&#xff08;truncation&#xff09;。不同平台对于整数除法的行为和处理方式可能会有所不同&#xff0c;这可能导致代码在不同编译器或硬件平台上的行为不…

了解RabbitMQ的工作原理

RabbitMQ是一个开源的消息代理系统&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。在现代分布式系统中&#xff0c;特别是在微服务架构中&#xff0c;RabbitMQ有广泛的应用。本文将详细介绍RabbitMQ的工作原理&#xff0c;并通过实践案例帮助读者理解和应用…