“无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【下】

“无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【下】

前言

  本篇博文介绍的是用51单片机的最终项目《智能语音小车》【下】,包含小车测速原理,小车测速代码实现,OLED 二次开发为了显示速度,蓝牙控制且OLED和蓝牙显示速度,wifi控制小车并发送速度,非特点语音识别方案介绍,SU-03T语音模块的配置使用,SU-03T固件烧录并语音识别测试,语音识别切换小车寻迹避障跟随且Oled显示状态。看到这篇博文的朋友,可以先赞再看吗?

预备知识

  一、需要我之前写的所有博文的知识,如果还没看我之前的的博文,可以去看看后再看本篇博文
在这里插入图片描述
  二、C变量
  三、基本输入输出
  四、流程控制
  五、函数

  六、指针
  七、字符串

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

1.小车测速原理

1.1测速模块简介

在这里插入图片描述

  1. 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
  2. 输出信号原理:有遮挡,输出高电平;无遮挡,输出低电平
  3. 接线:

  VCC 接电源正极3.3-5V
  GND 接电源负极
  DO TTL开关信号输出
  AO 此模块不起作用

1.2测速原理和单位换算

  轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)

  对应的码盘也转了一圈,码盘有20个格子,每经过一个格子会遮挡(高电平)不遮挡(低电平),那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM

  定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm

  假设一秒有80脉冲,那么就是80cm/s

2.小车测速代码实现

2.1小车测速代码实现核心思路

  • 使用外部中断0实现速度记录
  • 在定时器0中断函数中实现每秒发送一下速度数据
  • 在主C文件主函数中通过串口发送速度数据

  注:基于串口控制小车工程开发

2.2使用外部中断0实现速度记录

  1. 打开外部中断零,使用下降沿触发。
void Ex0init()
{
	EX0 = 1;    //打开外部中断0
	IT0 = 1;    //下降沿触发
}
  1. 建立速度记录变量speedCnt,建立外部中断0函数,在函数内自增speedCnt
unsigned int speedCnt = 0;

void Int0Handler() interrupt 0   //外部中断执行函数
{
	speedCnt++;
}

2.3在定时器0中断函数中实现每秒发送一下速度数据

  1. cnt每增加1,过0.5mscnt = 2000时过了1秒。对于cnt的判断条件为等于2000。定义一个发送数据请求标志flag,当cnt到2000时等于1。再定义一个真实速度变量speed,用于向主C文件传送速度数据。数据记录变量speedCnt当cnt = 2000时先把数据传给speed,再清零。
  2. 代码体现
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期,加到2000就是1秒
	if(cnt == 2000)
	{
	  flag = 1;    
		speed = speedCnt;
		speedCnt = 0;
		cnt = 0;     //定时到20豪秒,cnt从0开始加
	}
}
  1. 定时器0的C文件代码
#include "reg52.h"
#include "motor.h"
unsigned int cnt = 0;			     //记录爆表次数
char flag = 0;                 //发送数据请求标志
unsigned int speed = 0;        //真实速度
extern unsigned int speedCnt;  //使用外部全局变量

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期,加到2000就是1秒
	if(cnt == 2000)
	{
	  flag = 1;    
		speed = speedCnt;
		speedCnt = 0;
		cnt = 0;     //定时到20豪秒,cnt从0开始加
	}
}

2.4在主C文件主函数中通过串口发送速度数据

  1. 在串口C文件中添加发送字符和字符串函数,此处参照串口通信博文;并在串口.h文件中加以声明
void sendByte(char speedData)
{
	SBUF = speedData;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}
void sendByte(char speedData);

void sendString(char *str);
  1. 使用字符串与整型数拼接函数sprintf在主C文件主函数while(1)函数每秒生成一个速度数据并通过串口发送。由于测速传感器模块有问题,导致速度数据需要除以10。
while(1)
{
    if(flag)
    {
        speed = speed /10;
        sprintf(charSpeed,"轮速:%d cm/s",speed);
        sendString(charSpeed);
        sendString("\r\n");
        flag = 0;
    }
}
  1. 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "reg52.h"
#include "stdio.h"

unsigned int speedCnt = 0;
extern char flag;
extern unsigned int speed;

char charSpeed[24];

void Ex0init()
{
	EX0 = 1;    //打开外部中断0
	IT0 = 1;    //下降沿触发
}

void main()
{
	initTime0();
	UartInit();
	Ex0init();  //外部中断初始化
	while(1)
	{
		if(flag)
		{
			speed = speed /10;
			sprintf(charSpeed,"轮速:%d cm/s",speed);
			sendString(charSpeed);
			sendString("\r\n");
			flag = 0;
		}
	}
}

void Int0Handler() interrupt 0   //外部中断执行函数
{
	speedCnt++;
}

3. OLED 二次开发为了显示速度

3.1 OLED 二次开发为了显示速度核心思路

  • OLED有关的寄存器声明函数单独封装成oled.c文件和oled.h文件
  • 根据厂家提供的Oledfont.hOled.c文件内的字符库发送字符串函数完善代码,使代码能直接掉用API就能使OLED显示字符串。
  • 测试OLED显示speed:35cm/s

  注:基于OLED显示字符A工程开发

3.2将OLED有关的寄存器声明和函数单独封装成oled.c文件和oled.h文件

  1. OLED有关寄存器声明
sbit SCL = P0^1;                   //把SCL接单片机P0.1口
sbit SDA = P0^3;                   //把SDA接单片机P0.3口
  1. OLED有关函数
void IICStart()                  //IIC起始信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA先是高电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //_nop_()寒素执行时间为5微秒,并不是1微秒,原因是调用函数时有进出栈操作
	                               //此时在这里,根据起始信号时序图可知需要延时》4.7微秒
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA此时为低电平
	_nop_();                       //延时5微秒
}

void IICStop()                   //IIC终止信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA先是低电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //延时5微秒
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA此时为高电平
	_nop_();                       //延时5微秒
}

char IIC_ACK()
{
	char flag;
	
	SDA = 1;                         //释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}

void IICSendByte(char cdata)
{
	char i;
	for(i=0; i<8; i++ )
  {   	
	  SCL = 0;                       //scl拉低,让sda做好数据准备
		SDA = cdata & 0x80;            //0x80 = 1000 0000获得cdata的最高位,给sda
		_nop_();                       //发送建立数据时间
		SCL = 1;                       //SCL被拉高开始发送数据
		_nop_();											 //数据发送时间
		SCL = 0;                       //发送完数据被拉低
		_nop_();
		cdata = cdata << 1;
	}
}

void OledWriteCmd(char Cmd)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x00);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Cmd);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void OledWriteData(char Data)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x40);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Data);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void initOLED()
{
//( 01) display off (0xae)
	OledWriteCmd(0xae);
//( 02) set low column address (0x00)
	OledWriteCmd(0x00);
//( 03) set high column address (0x10)
	OledWriteCmd(0x10);
//( 04) set start line address (0x40)
	OledWriteCmd(0x40);
//( 05) set page address (0xb0)
	OledWriteCmd(0xb0);
//( 06) contract control (0x81)
	OledWriteCmd(0x81);
//( 07) send 0xff (多字节指令)
	OledWriteCmd(0xff);
//( 08) set segment remap (0xa1)
	OledWriteCmd(0xa1);
//( 09) set normal/reverse (0xa6)
	OledWriteCmd(0xa6);
//( 10) set multiplex ratio (1 to 64) (0xa8 )
	OledWriteCmd(0xa8);
//( 11) set duty 1/32 (0x3f)
	OledWriteCmd(0x3f);
//( 12) com scan direction (0xc8)
  OledWriteCmd(0xc8);
//( 13) set display offset (0xd3)
	OledWriteCmd(0xd3);
//( 14) send 0x00
  OledWriteCmd(0x00);
//( 15) set osc division (0xd5)
	OledWriteCmd(0xd5);
//( 16) send 0x80
	OledWriteCmd(0x80);
//( 17) set area color mode off (0xd8)
	OledWriteCmd(0xd8);
//( 18) send 0x05
	OledWriteCmd(0x05);
//( 19) set pre-charge period (0xd9)
	OledWriteCmd(0xd9);
//( 20) send 0xf1
	OledWriteCmd(0xf1);
//( 21) set com pin configuration (0xda)
	OledWriteCmd(0xda);
//( 22) send 0x12
	OledWriteCmd(0x12);
//( 23) set Vcomh (0xdb)
	OledWriteCmd(0xdb);
//( 24) send 0x30
	OledWriteCmd(0x30);
//( 25) set charge pump enable (0x8d)
	OledWriteCmd(0x8d);
//( 26) send 0x14
	OledWriteCmd(0x14);
//( 27) turn on oled panel(0xaf)
	OledWriteCmd(0xaf);
}

void OLEDClearScreen()
{
	unsigned char i;   //使用无符号的字符型变量i,j的作用是防止内存越界。因为
	unsigned char j;   //有符号的字符型数据只能表示 -128~127,会在内层for中越界
	
	for(i=0; i<8; i++)
	{
		OledWriteCmd(0xB0 + i); //偏移Page
		OledWriteCmd(0x00);     //从第一列开始清屏
		OledWriteCmd(0x10); 
		for(j=0; j<128; j++)    //列地址自动偏移
		{
			OledWriteData(0);     //写入清屏数据0
		}
	}
}
  1. oled.c文件代码
#include "reg52.h"
#include "intrins.h"
#include "oledfont.h"

sbit SCL = P0^1;                   //把SCL接单片机P0.1口
sbit SDA = P0^3;                   //把SDA接单片机P0.3口

void IICStart()                  //IIC起始信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA先是高电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //_nop_()寒素执行时间为5微秒,并不是1微秒,原因是调用函数时有进出栈操作
	                               //此时在这里,根据起始信号时序图可知需要延时》4.7微秒
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA此时为低电平
	_nop_();                       //延时5微秒
}

void IICStop()                   //IIC终止信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA先是低电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //延时5微秒
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA此时为高电平
	_nop_();                       //延时5微秒
}

char IIC_ACK()
{
	char flag;
	
	SDA = 1;                         //释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}

void IICSendByte(char cdata)
{
	char i;
	for(i=0; i<8; i++ )
  {   	
	  SCL = 0;                       //scl拉低,让sda做好数据准备
		SDA = cdata & 0x80;            //0x80 = 1000 0000获得cdata的最高位,给sda
		_nop_();                       //发送建立数据时间
		SCL = 1;                       //SCL被拉高开始发送数据
		_nop_();											 //数据发送时间
		SCL = 0;                       //发送完数据被拉低
		_nop_();
		cdata = cdata << 1;
	}
}

void OledWriteCmd(char Cmd)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x00);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Cmd);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void OledWriteData(char Data)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x40);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Data);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void initOLED()
{
//( 01) display off (0xae)
	OledWriteCmd(0xae);
//( 02) set low column address (0x00)
	OledWriteCmd(0x00);
//( 03) set high column address (0x10)
	OledWriteCmd(0x10);
//( 04) set start line address (0x40)
	OledWriteCmd(0x40);
//( 05) set page address (0xb0)
	OledWriteCmd(0xb0);
//( 06) contract control (0x81)
	OledWriteCmd(0x81);
//( 07) send 0xff (多字节指令)
	OledWriteCmd(0xff);
//( 08) set segment remap (0xa1)
	OledWriteCmd(0xa1);
//( 09) set normal/reverse (0xa6)
	OledWriteCmd(0xa6);
//( 10) set multiplex ratio (1 to 64) (0xa8 )
	OledWriteCmd(0xa8);
//( 11) set duty 1/32 (0x3f)
	OledWriteCmd(0x3f);
//( 12) com scan direction (0xc8)
  OledWriteCmd(0xc8);
//( 13) set display offset (0xd3)
	OledWriteCmd(0xd3);
//( 14) send 0x00
  OledWriteCmd(0x00);
//( 15) set osc division (0xd5)
	OledWriteCmd(0xd5);
//( 16) send 0x80
	OledWriteCmd(0x80);
//( 17) set area color mode off (0xd8)
	OledWriteCmd(0xd8);
//( 18) send 0x05
	OledWriteCmd(0x05);
//( 19) set pre-charge period (0xd9)
	OledWriteCmd(0xd9);
//( 20) send 0xf1
	OledWriteCmd(0xf1);
//( 21) set com pin configuration (0xda)
	OledWriteCmd(0xda);
//( 22) send 0x12
	OledWriteCmd(0x12);
//( 23) set Vcomh (0xdb)
	OledWriteCmd(0xdb);
//( 24) send 0x30
	OledWriteCmd(0x30);
//( 25) set charge pump enable (0x8d)
	OledWriteCmd(0x8d);
//( 26) send 0x14
	OledWriteCmd(0x14);
//( 27) turn on oled panel(0xaf)
	OledWriteCmd(0xaf);
}

void OLEDClearScreen()
{
	unsigned char i;   //使用无符号的字符型变量i,j的作用是防止内存越界。因为
	unsigned char j;   //有符号的字符型数据只能表示 -128~127,会在内层for中越界
	
	for(i=0; i<8; i++)
	{
		OledWriteCmd(0xB0 + i); //偏移Page
		OledWriteCmd(0x00);     //从第一列开始清屏
		OledWriteCmd(0x10); 
		for(j=0; j<128; j++)    //列地址自动偏移
		{
			OledWriteData(0);     //写入清屏数据0
		}
	}
}
  1. 建立oled.h文件,在文件中声明需要使用到的函数


void OledWriteCmd(char Cmd);


void OledWriteData(char Data);


void initOLED();


void OLEDClearScreen();

3.3根据厂家提供的Oledfont.h和Oled.c文件内的字符库和发送字符串函数完善代码,使代码能直接掉用API就能使OLED显示字符串

  1. 在工程内添加oledfont.h文件,将F8X16字符数组添加到库文件中
const unsigned char code F8X16[]=	  
{
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0
  0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
  0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
  0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
  0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
  0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
  0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
  0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
  0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
  0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
  0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
  0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
  0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
  0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
  0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
  0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
  0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
  0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
  0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
  0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
  0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
  0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
  0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
  0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
  0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
  0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
  0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
  0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
  0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
  0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
  0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
  0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
  0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
  0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
  0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
  0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
  0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
  0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
  0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
  0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
  0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
  0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
  0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
  0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
  0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
  0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
  0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
  0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
  0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
  0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
  0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
  0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
  0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
  0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
  0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
  0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
  0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
  0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
  0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
  0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
  0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
  0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
  0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
  0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
  0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
  0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
  0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
  0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
  0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
  0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
  0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
  0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
  0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
  0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
  0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
  0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
  0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
  0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
  0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
  0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
  0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
  0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
  0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
  0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
  0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
  0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
  0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
  0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
  0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
  0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
  0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};
  1. 在oled.c文件中添加发送显示字符显示字符串函数并修改调用的cmd函数data函数
  • 添加显示字符和现实字符串函数
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;	
	}		
}
  • 将Oled_Write_Cmd修改为OledWriteCmd,将Oled_Write_Data修改为OledWriteData。
void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
	unsigned int  i;
	OledWriteCmd(0xb0+(row*2-2));                           //page 0
	OledWriteCmd(0x00+(col&0x0f));                          //low
	OledWriteCmd(0x10+(col>>4));                            //high	
	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
		OledWriteData(F8X16[i]);                            //写数据oledTable1
	}

	OledWriteCmd(0xb0+(row*2-1));                           //page 1
	OledWriteCmd(0x00+(col&0x0f));                          //low
	OledWriteCmd(0x10+(col>>4));                            //high
	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
		OledWriteData(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;	
	}		
}
  • oled.h文件中声明void Oled_Show_Str(char row,char col,char *str)函数。
void Oled_Show_Str(char row,char col,char *str);
  • 此时只需要在主C文件中调用Oled_Show_Str函数即可显示对应的数据。
  1. oled.c和oled.h文件代码
  • oled.c
#include "reg52.h"
#include "intrins.h"
#include "oledfont.h"

sbit SCL = P0^1;                   //把SCL接单片机P0.1口
sbit SDA = P0^3;                   //把SDA接单片机P0.3口

void IICStart()                  //IIC起始信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA先是高电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //_nop_()寒素执行时间为5微秒,并不是1微秒,原因是调用函数时有进出栈操作
	                               //此时在这里,根据起始信号时序图可知需要延时》4.7微秒
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA此时为低电平
	_nop_();                       //延时5微秒
}

void IICStop()                   //IIC终止信号函数
{
	SCL = 0;                       //防止雪花
	SDA = 0;                       //根据起始信号时序图阴影部分,SDA先是低电平
	SCL = 1;                       //根据起始信号时序图,SCL一直为高电平
	_nop_();                       //延时5微秒
	SDA = 1;                       //根据起始信号时序图阴影部分,SDA此时为高电平
	_nop_();                       //延时5微秒
}

char IIC_ACK()
{
	char flag;
	
	SDA = 1;                         //释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}

void IICSendByte(char cdata)
{
	char i;
	for(i=0; i<8; i++ )
  {   	
	  SCL = 0;                       //scl拉低,让sda做好数据准备
		SDA = cdata & 0x80;            //0x80 = 1000 0000获得cdata的最高位,给sda
		_nop_();                       //发送建立数据时间
		SCL = 1;                       //SCL被拉高开始发送数据
		_nop_();											 //数据发送时间
		SCL = 0;                       //发送完数据被拉低
		_nop_();
		cdata = cdata << 1;
	}
}

void OledWriteCmd(char Cmd)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x00);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Cmd);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void OledWriteData(char Data)
{
	//1.start
	IICStart();
	//2.写入从机地址 b0111 1000 0x78
	IICSendByte(0x78);
	//3.ACK
	IIC_ACK();
	//4.cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
	IICSendByte(0x40);
	//5.ACK
	IIC_ACK();
	//6. 写入指令/数据
	IICSendByte(Data);
	//7.ACK
	IIC_ACK();
	//8.STOP
	IICStop();
}

void initOLED()
{
//( 01) display off (0xae)
	OledWriteCmd(0xae);
//( 02) set low column address (0x00)
	OledWriteCmd(0x00);
//( 03) set high column address (0x10)
	OledWriteCmd(0x10);
//( 04) set start line address (0x40)
	OledWriteCmd(0x40);
//( 05) set page address (0xb0)
	OledWriteCmd(0xb0);
//( 06) contract control (0x81)
	OledWriteCmd(0x81);
//( 07) send 0xff (多字节指令)
	OledWriteCmd(0xff);
//( 08) set segment remap (0xa1)
	OledWriteCmd(0xa1);
//( 09) set normal/reverse (0xa6)
	OledWriteCmd(0xa6);
//( 10) set multiplex ratio (1 to 64) (0xa8 )
	OledWriteCmd(0xa8);
//( 11) set duty 1/32 (0x3f)
	OledWriteCmd(0x3f);
//( 12) com scan direction (0xc8)
  OledWriteCmd(0xc8);
//( 13) set display offset (0xd3)
	OledWriteCmd(0xd3);
//( 14) send 0x00
  OledWriteCmd(0x00);
//( 15) set osc division (0xd5)
	OledWriteCmd(0xd5);
//( 16) send 0x80
	OledWriteCmd(0x80);
//( 17) set area color mode off (0xd8)
	OledWriteCmd(0xd8);
//( 18) send 0x05
	OledWriteCmd(0x05);
//( 19) set pre-charge period (0xd9)
	OledWriteCmd(0xd9);
//( 20) send 0xf1
	OledWriteCmd(0xf1);
//( 21) set com pin configuration (0xda)
	OledWriteCmd(0xda);
//( 22) send 0x12
	OledWriteCmd(0x12);
//( 23) set Vcomh (0xdb)
	OledWriteCmd(0xdb);
//( 24) send 0x30
	OledWriteCmd(0x30);
//( 25) set charge pump enable (0x8d)
	OledWriteCmd(0x8d);
//( 26) send 0x14
	OledWriteCmd(0x14);
//( 27) turn on oled panel(0xaf)
	OledWriteCmd(0xaf);
}

void OLEDClearScreen()
{
	unsigned char i;   //使用无符号的字符型变量i,j的作用是防止内存越界。因为
	unsigned char j;   //有符号的字符型数据只能表示 -128~127,会在内层for中越界
	
	for(i=0; i<8; i++)
	{
		OledWriteCmd(0xB0 + i); //偏移Page
		OledWriteCmd(0x00);     //从第一列开始清屏
		OledWriteCmd(0x10); 
		for(j=0; j<128; j++)    //列地址自动偏移
		{
			OledWriteData(0);     //写入清屏数据0
		}
	}
}

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

	OledWriteCmd(0xb0+(row*2-1));                           //page 1
	OledWriteCmd(0x00+(col&0x0f));                          //low
	OledWriteCmd(0x10+(col>>4));                            //high
	for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
		OledWriteData(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;	
	}		
}
  • oled.h


void OledWriteCmd(char Cmd);


void OledWriteData(char Data);


void initOLED();


void OLEDClearScreen();

void Oled_Show_Str(char row,char col,char *str);

3.4测试OLED显示speed:35cm/s

  1. 主C文件主函数中首先初始化OLED,再清屏,最后调用显示字符串函数
void main()
{	
	//1.初始化OLED
	initOLED();
	//2.清理屏幕残留数据
	OLEDClearScreen();
	//选择2行2列显示
	Oled_Show_Str(2,2,"speed:35cm/s");
	//防止主函数退出
	while(1);
}
  1. 主C文件代码
#include "reg52.h"
#include "intrins.h"
#include "oled.h"


void main()
{	
	//1.初始化OLED
	initOLED();
	//2.清理屏幕残留数据
	OLEDClearScreen();
	//选择2行2列显示
	Oled_Show_Str(2,2,"speed:35cm/s");
	//防止主函数退出
	while(1);
}
  1. 测试结果:正常显示

4.蓝牙控制且OLED和蓝牙显示速度

4.1蓝牙控制且OLED和蓝牙显示速度核心思路

  • OLED 二次开发为了显示速度工程中拷贝oled.coled.holedfont.h文件到现在的工程文件夹下
  • 在工程内添加oled.c文件;在主C文件中添加oled.h,然后在主函数内进行OLED初始化和清屏,最后在while(1)死循环内添加oled在2页2列位置开始显示速度数据。

  注:基于小车测速代码实现工程开发

4.2从 OLED 二次开发为了显示速度工程中拷贝oled.c,oled.h和oledfont.h文件到现在的工程文件夹下

  1. 找到oled.c,oled.h和oledfont.h文件

在这里插入图片描述

  1. 将这些文件拷贝到当前工程文件夹中

在这里插入图片描述

4.3在工程内添加oled.c文件;在主C文件中添加oled.h,然后在主函数内进行OLED初始化和清屏,最后在while(1)死循环内添加oled在2页2列位置开始显示速度数据

  1. 在工程内添加oled.c文件

在这里插入图片描述

  1. 在主C文件中添加oled.h文件
#include "oled.h"
  1. 在主函数内进行OLED初始化和清屏
initOLED();
OLEDClearScreen();
  1. 在while(1)死循环内添加oled在2页2列位置开始显示速度数据
while(1)
{
    if(flag)
    {
        speed = speed /10;                        //模块数据上传有误,需要除以10
        sprintf(charSpeed,"speed: %d cm/s",speed); //将轮速: cm/s 与speed的数据拼接成“轮速:speed cm/s”送进charSpeed中
        sendString(charSpeed);                    //发送速度数据
        sendString("\r\n");                       //发送换行符
        flag = 0;                                 //发送数据标志位清零
    }

    Oled_Show_Str(2,2,charSpeed);
}
  1. 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "reg52.h"
#include "stdio.h"
#include "oled.h"

unsigned int speedCnt = 0;
extern char flag;
extern unsigned int speed;

char charSpeed[24];

void Ex0init()
{
	EX0 = 1;    //打开外部中断0
	IT0 = 1;    //下降沿触发
}

void main()
{
	initTime0();
	UartInit();
	Ex0init();  //外部中断初始化
	initOLED();
	OLEDClearScreen();
	while(1)
	{
		if(flag)
		{
			speed = speed /10;                        //模块数据上传有误,需要除以10
			sprintf(charSpeed,"speed: %d cm/s",speed); //将轮速: cm/s 与speed的数据拼接成“轮速:speed cm/s”送进charSpeed中
			sendString(charSpeed);                    //发送速度数据
			sendString("\r\n");                       //发送换行符
			flag = 0;                                 //发送数据标志位清零
		}
		
		Oled_Show_Str(2,2,charSpeed);
	}
}

void Int0Handler() interrupt 0   //外部中断执行函数
{
	speedCnt++;
}

5.wifi控制小车并发送速度

5.1wifi控制小车并发送速度核心思路

  • 在工程内建立esp8266.c文件和esp8266.h文件,并把wifi开灯项目中的上官一号当路由器和服务器模式控制工程中有关wifi模块操作的代码整合进esp8266.c文件并在esp8266.h文件中声明
  • urat.c文件中添加对wifi模块返回值的判断
  • delay.c文件中添加延时500ms1000ms函数并在delay.h文件中声明
  • 在主C文件主函数中设计合理的逻辑使ESP8266可以发送测速数据。

  注:基于蓝牙控制且OLED和蓝牙显示速度工程开发

5.2在工程内建立esp8266.c文件和esp8266.h文件,并把wifi开灯项目中的上官一号当路由器和服务器模式控制工程中有关wifi模块操作的代码整合进esp8266.c文件并在esp8266.h文件中声明

  1. 拷贝使用esp8266路由模式的相关声明
code char routeMode[]         = "AT+CWMODE=2\r\n";                                   //打开路由模式
code char moreJoin[]          = "AT+CIPMUX=1\r\n";                                   //打开使能多连接
code char setUpServer[]       = "AT+CIPSERVER=1\r\n";                                //建立服务器
char at_OK_Flag          = 0;                                                   //定义指令执行成功等待标志
char at_Cli_Con_Flag     = 0;                                                   //终端连接服务器标志

sbit LED1 = P3^7;  //使用位定义声明LED1
sbit LED2 = P3^6;  //使用位定义声明LED2
  1. 将打开路由模式和使能多连接代码封装为initWifi()函数
void initWifi()
{
	//发送打开路由模式
	sendString(routeMode);
	//等待指令执行成功返回OK
	while(!at_OK_Flag);
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	
	//发送打开使能多链接
	sendString(moreJoin);
	//等待指令执行成功返回OK
	while(!at_OK_Flag);
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	
}
  1. wifi模块建立服务器代码封装为wifiServer()函数
void wifiServer()
{
	sendString(setUpServer);
	while(!at_OK_Flag);
	if(at_OK_Flag)
	{
		statusLED1(); //建立服务器完成后让LED1闪烁5次,方便用PC网络助手连接
	}
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	//等待PC网络助手连接
	while(!at_Cli_Con_Flag);
	
	//PC网络助手连接成功让两颗LED亮
	if(at_Cli_Con_Flag) 
	{
		LED1 = 0;
		LED2 = 0;
	}
}
  1. 拷贝LED灯状态代码
void statusLED1()
{
	char i;
	
	for(i=0; i<5; i++)
	{
		LED1 = 0;
		Delay500ms();
		LED1 = 1;
		Delay500ms();
	}
}
  1. esp8266.c文件完整代码
#include "reg52.h"
#include "delay.h"
#include "uart.h"

code char routeMode[]         = "AT+CWMODE=2\r\n";                                   //打开路由模式
code char moreJoin[]          = "AT+CIPMUX=1\r\n";                                   //打开使能多连接
code char setUpServer[]       = "AT+CIPSERVER=1\r\n";                                //建立服务器
char at_OK_Flag          = 0;                                                   //定义指令执行成功等待标志
char at_Cli_Con_Flag     = 0;                                                   //终端连接服务器标志

sbit LED1 = P3^7;  //使用位定义声明LED1
sbit LED2 = P3^6;  //使用位定义声明LED2

void statusLED1()
{
	char i;
	
	for(i=0; i<5; i++)
	{
		LED1 = 0;
		Delay500ms();
		LED1 = 1;
		Delay500ms();
	}
}

void initWifi()
{
	//发送打开路由模式
	sendString(routeMode);
	//等待指令执行成功返回OK
	while(!at_OK_Flag);
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	
	//发送打开使能多链接
	sendString(moreJoin);
	//等待指令执行成功返回OK
	while(!at_OK_Flag);
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	
}

void wifiServer()
{
	sendString(setUpServer);
	while(!at_OK_Flag);
	if(at_OK_Flag)
	{
		statusLED1(); //建立服务器完成后让LED1闪烁5次,方便用PC网络助手连接
	}
	//让OK标志的值返回零,方便下次使用
	at_OK_Flag = 0;
	//等待PC网络助手连接
	while(!at_Cli_Con_Flag);
	
	//PC网络助手连接成功让两颗LED亮
	if(at_Cli_Con_Flag) 
	{
		LED1 = 0;
		LED2 = 0;
	}
}
  1. esp8266.h文件中的声明


void initWifi();

void wifiServer();

5.3在urat.c文件中添加对wifi模块返回值的判断

  1. 引入外部标志变量
extern char at_OK_Flag;                                                 //定义指令执行成功等待标志
extern char at_Cli_Con_Flag;                                                   //终端连接服务器标志
  1. 在串口中断函数中进行wifi模块返回数据关键字OK,0:C的判断。
if(RI == 1)
{
    RI = 0;     //必须软件置零

    tmp = SBUF;
    if(tmp=='M' || tmp=='0' || tmp=='O')
    {
        i = 0;
    }
    buffer[i++] = tmp;
    if(buffer[0]=='M')
    {
        switch(buffer[1])
        {
            case '1':
                goFront();
                break;
            case '2':
                goBack();
                break;
            case '3':
                goLeft();
                break;
            case '4':
                goRight();
                break;
            default:
                stop();
                break;
        }

    }
    //检测返回的字符串是否为OK,是就将OK标志置1,退出等待循环
    if(buffer[0]=='O' && buffer[1]=='K')
    {
        at_OK_Flag        = 1;
        memset(buffer,'\0',SIZE);
    }

    //检测返回的字符串是否为0,C开始,是就将连接终端标志置1,退出等待循环
    if(buffer[0]=='0' && buffer[2]=='C')
    {
        at_Cli_Con_Flag   = 1;
        memset(buffer,'\0',SIZE);
    }

    if(i == SIZE)
    {
        i = 0;
        memset(buffer,'\0',SIZE);
    }
}
  1. uart.c文件代码
#include "reg52.h"
#include "motor.h"
#include "string.h"

#define SIZE 32

sfr AUXR = 0x8e;   //声明AUXR寄存器地址
char buffer[SIZE];
extern char at_OK_Flag;                                                 //定义指令执行成功等待标志
extern char at_Cli_Con_Flag;                                                   //终端连接服务器标志

void UartInit()		//自己配
{
	//配置串口工作方式为方式1,从只收不发改为能收能发
	SCON =  0x50;
  //配置辅助寄存器,减少电磁辐射,稳定晶振频率  
	AUXR =  0x01;
	//设置定时器工作方式为定时器1的8位自动重装
	TMOD &= 0x0F;
	TMOD |= 0x20;
	//设置串口波特率为9600,0误差
	TH1   = 0xFD;
	TL1   = 0xFD;
	//打开定时器1
	TR1   = 1;
	//打开总中断
	EA = 1;
	//打开串口中断
	ES = 1;
}

void sendByte(char speedData)
{
	SBUF = speedData;
	while(!TI);
	TI = 0;
}

void sendString(char *str)
{
	while(*str != '\0')
	{
		sendByte(*str);
		str++;
	}
}

void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='M' || tmp=='0' || tmp=='O')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			if(buffer[0]=='M')
			{
				switch(buffer[1])
				{
					case '1':
						goFront();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
				
			}
			//检测返回的字符串是否为OK,是就将OK标志置1,退出等待循环
			if(buffer[0]=='O' && buffer[1]=='K')
			{
				at_OK_Flag        = 1;
				memset(buffer,'\0',SIZE);
			}
			
			//检测返回的字符串是否为0,C开始,是就将连接终端标志置1,退出等待循环
			if(buffer[0]=='0' && buffer[2]=='C')
			{
				at_Cli_Con_Flag   = 1;
				memset(buffer,'\0',SIZE);
			}
			
			if(i == SIZE)
			{
				i = 0;
				memset(buffer,'\0',SIZE);
			}
		}
		
	
	if(TI);
		
}

5.4在delay.c文件中添加延时500ms和1000ms函数并在delay.h文件中声明

  1. 延时500ms函数和1000ms函数代码
void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
  1. delay.h文件中的函数声明
void Delay2000ms();

void Delay100ms();

void Delay1000ms();

void Delay500ms();
  1. delay.c文件代码
#include "intrins.h"

void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay100ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	_nop_();
	i = 5;
	j = 52;
	k = 195;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

5.5在主C文件主函数中设计合理的逻辑使ESP8266可以发送测速数据

  1. 通过延时1000ms使单片机等待wifi模块开机,然后让wifi模块配置成路由模式并连接终端
Delay1000ms();
initWifi();
wifiServer();
  1. while(1)死循环if中每隔500毫秒发送一次发送字符命令,接着发送测速数据。
while(1)
{
    if(flag)
    {
        sendString(sendData);
        Delay500ms();
        speed = speed /10;                        //模块数据上传有误,需要除以10
        sprintf(charSpeed,"speed: %d cm/s",speed); //将轮速: cm/s 与speed的数据拼接成“轮速:speed cm/s”送进charSpeed中
        sendString(charSpeed);


        //发送速度数据
        sendString("\r\n");                       //发送换行符
        flag = 0;                                 //发送数据标志位清零
    }

    Oled_Show_Str(2,2,charSpeed);
}
  1. 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "reg52.h"
#include "stdio.h"
#include "oled.h"
#include "esp8266.h"

unsigned int speedCnt = 0;
extern char flag;
extern unsigned int speed;
char sendData[]       = "AT+CIPSEND=0,15\r\n";                               //发送15个字节数据给终端刚好可以显示玩测速数据

char charSpeed[24];


void Ex0init()
{
	EX0 = 1;    //打开外部中断0
	IT0 = 1;    //下降沿触发
}

void main()
{
	initTime0();
	UartInit();
	Ex0init();  //外部中断初始化
	initOLED();
	OLEDClearScreen();
	Delay1000ms();
	initWifi();
	wifiServer();
	while(1)
	{
		if(flag)
		{
			sendString(sendData);
			Delay500ms();
			speed = speed /10;                        //模块数据上传有误,需要除以10
			sprintf(charSpeed,"speed: %d cm/s",speed); //将轮速: cm/s 与speed的数据拼接成“轮速:speed cm/s”送进charSpeed中
			sendString(charSpeed);


			//发送速度数据
			sendString("\r\n");                       //发送换行符
			flag = 0;                                 //发送数据标志位清零
		}
	
		Oled_Show_Str(2,2,charSpeed);
	}
}

void Int0Handler() interrupt 0   //外部中断执行函数
{
	speedCnt++;
}

6.非特点语音识别方案介绍

6.1什么是非特定语音方案

  非特定语音方案是指语音识别技术中针对大多数用户的识别方案,而非仅针对特定人的识别。这种方案一般需要采集多个人的语音进行录音和训练,通过学习,使语音识别系统能够达到较高的识别率。这种方案的应用范围广泛,适用于不同的场景和用户群体。

  "非特定语音方案"通常指的是一种通信协议或标准,它不依赖于特定的语音编解码(codec)技术。这种方案的设计目的是为了在不同的通信设备和网络环境中实现语音通信的互操作性。
  在这种方案中,语音数据通常以数字形式进行传输,而不是模拟形式。这样可以确保在不同的设备和网络中,语音数据的传输和处理都是一致的。

  总之,非特定语音方案是一种更加通用、灵活的语音识别方案,能够满足不同用户的需求,提高语音识别的准确率和易用性;实现语音通信的互操作性和可扩展性,而不依赖于特定的技术或设备。

6.2语音模块使用介绍

  1. 型号为:SU-03TLD3320
  2. 使用介绍:这个语音模块不需要编程,不需要二次开发通过厂家给的网站配置后即可使用,傻瓜式操作。

6.3语音模块手册介绍

  1. 产品概述

  SU-03T 是一款低成本、 低功耗、 小体积的离线语音识别模组, 能快速应用于智能家居, 各类智能小家电, 86 盒, 玩具, 灯具等需要语音操控的产品。

  功能框图:

在这里插入图片描述

  1. 特性
  • 32bit RISC 内核,运行频率 240M
  • 支持 DSP 指令集以及 FPU 浮点运算单元
  • FFT 加速器:最大支持 1024 点复数 FFT/IFFT 运算,或者是 2048 点
    的实 数 FFT/IFFT 运算
  • 内置高速 SRAM,内置 2MB FLASH
  • 内置 3W、单声道 AB 类功放
  • 支持 1 路驻极体麦
  • 支持 I2S input/output
  • 支持 5V 电源输入
  • 内置 5V 转 3.3V,3.3V 外部负 载不超过 150mA
  • RC 12MHz 时钟源和 PLL 锁相环时钟源
  • 内置 POR(Power on Reset),低电压检测和看门狗
  • 所有 GPIO 均可配置为外部中断输入和唤醒源
  • 1 个标准 SPI Master 接口,最高速率 30MHz
  • 1 个 SPI Slave 接口最高速率 30MHz
  • 1 个全双工 UART 最高速率 3Mbps。
  • 1 个 I2C 主/从控制器最高速率 400kHz
  • 2 个 PWM 输出
  • 1 个 12-bit SAR-ADC 最大 450Khz 采样率
  1. 主要参数
模块型号SU-03T
封装SMD18/DIP18
尺寸21153(±0.2)mm
支持接口UART/GPIO/ADC/PWM/SPI /I2S/I2C
IO 口8
支持喇叭规格VCC=5V,4Ω 负载下,提供高达 2.9W 的输出功率 ; VCC=5V,8Ω 负载下,提供高达 1.8W 的输出功率。
IO 口8
功耗平均工作电流: 60mA
供电范围供电电压 3.6V ~ 5.5V,一般 5V 供电,供电电流 >200mA
工作温度-20 ℃ ~ 85℃
  1. 管脚定义

  注意:UART0 串口 B0,B1 引脚是调试器的烧录口,串口烧录使用 UART1B6,B7 脚)。

序号Pin 脚 名称功能说明
1VCC5V 供电
2GND数字地
33V3芯片内部 LDO 输出 3.3V,外部负载不能超过 150mA
4B8打印信息引脚,不用可悬空
5B7ADC13/UART1_TXD/I2C_SCL
6B6ADC12/UART1_RXD/I2C_SDA
7B2UART1_TXD/I2C_SCL/TIM3_PWM
8MIC-驻极体麦负极
9MIC+驻极体麦正极
10B3UART1_RXD/I2C_SDA/TIM4_PWM
11A27ADC6/SPIS_MOSI/SPIM_MOSI/I2S0_DO/DMIC1_CLK/TIM3_PWM
12A26ADC5/SPIS_CLK/SPIM_CLK/I2S0_BCLK/I2S1_BCLK/DMIC0_CLK
13A25ADC4/SPIS_MISO/SPIM_MISO/I2S0_LRCLK/I2S1_LRCLK/DMIC_DAT
14B0UART0_TXD/I2C_SCL/TIM3_PWM
15B1UART0_RXD/I2C_SDA/TIM4_PWM
16GND数字地
17SPK-喇叭负极
18SPK+喇叭正极

7.SU-03T语音模块的配置使用

7.1进入官方给的网站http://www.smartpi.cn/#/注册会员后进行配置

  1. 注册流程

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

  1. 注册并登录后的样子

在这里插入图片描述

7.2开始配置

  1. 点击产品管理下的所以产品

在这里插入图片描述

  1. 点击创建产品

在这里插入图片描述

  1. 产品类别选择其他产品,场景选择离线方案,模组选择SU-03T,产品信息填写产品名称,语言选择中文

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

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

  1. 在Pin脚处将A25A26A27脚默认输出改为高电平。

在这里插入图片描述

  1. 唤醒词自定义处自定义你的唤醒词,唤醒灵敏度选择高

在这里插入图片描述

  1. 唤醒回复可以默认也可以自己设置。这里我选择默认

在这里插入图片描述

  1. 命令词自定义处:行为设置为你需要设置的行为,触发方式不用管,命令词设置自己所使用的命令词,回复语设置自己想要的回复。控制详情处只需要在自己添加的行为后面添加相应控制即可。

在这里插入图片描述

在这里插入图片描述

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

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

  1. 发音人配置设置你喜欢的声音,设置音量大小,语速快慢,亮度大小。我这里用默认。

在这里插入图片描述

  1. 其他配置处开机播报看需求设置,我选默认。超时退出也就是唤醒后多少秒内没下命令自动退出,我选默认。退出回复可以自己设置,我选默认。主动退出命令可以自定义,我选默认。退出回复也可以自定义,我选默认。

在这里插入图片描述

  1. 点击保存后点击发布版本。发布描述用英文随便设置

在这里插入图片描述

在这里插入图片描述

  1. 等产品详情内的SDK状态生成完毕后点击更多下载SDK。即可完成配置

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

在这里插入图片描述

8. SU-03T固件烧录并语音识别测试

8.1对下载好的SDK文件进行解压

在这里插入图片描述

在这里插入图片描述

8.2在uni_hb_m_solution-140731-20240218.tar\uni_hb_m_solution-140731-20240218\uni_hb_m_solution\image_demo\Hummingbird-M-Update-Tool路径下找到UniOneUpdateTool.exe右键以管理员身份打开。

在这里插入图片描述

8.3在镜像文中选择uni_app_release_update.bin文件然后点击烧录即可。

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

8.4串口接线参照6.3即可。

9.语音识别切换小车寻迹避障跟随且Oled显示状态

9.1语音识别切换小车寻迹避障跟随且Oled显示状态核心思路

  • 封装避障模式函数
  • 封装跟随模式函数
  • 封装循迹模式函数
  • 添加OLED显示相关代码
  • 主C文件主函数中通过语音模块调用三个模式API

  注:基于摇头避障小车摇头测距小车04_摇头测距和行驶工程开发

9.2封装避障模式函数

  1. 将避障相关变量从主C文件主函数中剪切到主C文件上放变为全局变量。
float disMiddle;                 //中间距离变量
float disLeft;                   //左边距离变量
float disRight;                  //右边距离变量
char dir;                         //转头标志位变量
  1. 将避障相关执行代码封装为函数。
void avoidingMode()
{
	  if(dir != MIDDLE)               //方向不在中间会中
		{
			sg90Middle();                 //回中
			Delay200ms();
			Delay300ms();
			dir = MIDDLE;                 //软件初始化
		}
		disMiddle = getDistance();      //先测中间
		
		if(disMiddle > 30)  						//如果中间距离大于30厘米就前进
		{
			//前进
			goFront();
		}
		else if(disMiddle < 10)
		{
			goBack();
		}
		else                						//否则就摇头
		{
			stop();                       //停止
		  sg90Left();       						//转左边
			Delay300ms();                //等待500毫秒
			Delay200ms();
			disLeft = getDistance();      //测左边
			
			sg90Middle();                 //转中间
			Delay300ms();                 //等待500毫秒
			Delay200ms();
			
			sg90Right();                  //转右边			
			Delay300ms();                 //等待500毫秒
			Delay200ms();
			disRight = getDistance();
			dir      = RIGHT;             //记录末位置
			
			if(disLeft < disRight)        //左边小于右边,向右转
			{
				D1 = 0;
				goRight();
				Delay300ms();                 
				stop();
				D1 = 1;
				
			}
			
			if(disRight < disLeft )        //左边大于右边,向左转
			{
				D2 = 0;
				goLeft();
				Delay300ms();                 
				stop();
				D2 = 1;
			}
		}
}

9.3封装跟随模式函数

  1. 从跟随小车工程中的主C文件中拷贝跟随相关寄存器声明
sbit leftFollow = P2^5;       //左边轮子的跟随模块接单片机P2.5口
sbit rightFollow = P2^4;      //右边轮子的跟随模块接单片机P2.4口
  1. 从跟随小车工程中的主C文件中拷贝跟随相关执行代码封装为跟随模式函数
void  followMode()
{
	  //下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
	  if(leftTracking==0 && rightTracking==0)
		{
			goFront();
		}
		//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
		if(leftTracking==1 && rightTracking==0)
		{
			goLeft();
		}
		
		if(leftTracking==0 && rightTracking==1)
		{
			goRight();
		}
		
		if(leftTracking==1 && rightTracking==1)
		{
			stop();
		}
}

9.4封装循迹模式函数

  1. 从根据循迹原理实现循迹功能代码编写工程的主C文件中拷贝循迹相关寄存器代码。
sbit leftTracking = P2^7;     //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6;    //右边轮子的循迹模块接单片机P2.6口
  1. 从根据循迹原理实现循迹功能代码编写工程的主C文件中拷贝循迹相关执行代码封装成循迹模式函数
void trackingMode()
{
	//小车两个模块都能反射回来红外,输出低电平,灯亮,直走
     
		if(leftFollow==0 && rightFollow==0)
		{
			goFront();
		}
		
		//右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
		if(leftFollow==1 && rightFollow==0)
		{
			goRight();
		}
		
		//左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
		if(leftFollow==0 && rightFollow==1)
		{
			goLeft();
		}
		
		if(leftFollow==1 && rightFollow==1)
		{
			stop();
		}
}

9.5添加OLED显示相关代码

  1. 从蓝牙控制且OLED和蓝牙显示速度工程文件夹下拷贝oled.c,oled.h,oledfont.h文件到当前工程文件夹下

在这里插入图片描述

在这里插入图片描述

  1. 根据蓝牙控制且OLED和蓝牙显示速度工程主C文件中对OLED初始化的操作在本工程中的主C文件主函数中同样操作
initOLED();
OLEDClearScreen();
  1. 添加oled.c文件和oled.h文件进工程
#include "oled.h"

在这里插入图片描述

9.6主C文件主函数中通过语音模块调用三个模式API

  1. 在主C文件上方声明预约模块所使用的寄存器
sbit A25 = P1^3;              //语音控制端口A25接单片机P1.3口
sbit A26 = P1^4;              //语音控制端口A26接单片机P1.4口
sbit A27 = P1^5;              //语音控制端口A27接单片机P1.5口
  1. 定义四个模式OLED显示标志宏
#define TRACK  1    //定义循迹模式OLED显示
#define FOLLOW 2    //定义跟随模式OLED显示
#define AVOID  3    //定义避障模式OLED显示
#define STOP   4    //定义停下模式OLED显示
  1. 根据在语音模块配置官网配置的A25,A26, A27引脚语音指令操作建立判断语句。

在这里插入图片描述

//满足循迹模式,调用循迹模式
if(A25==1 && A26==1 && A27==0)
{

}

//满足避障模式,调用避障模式
if(A25==1 && A26==0 && A27==1)
{
    
}

//满足跟随模式,调用跟随模式
if(A25==0 && A26==1 && A27==1)
{

}

//满足停止模式,调用停止模式
if(A25==0 && A26==0 && A27==0)
{

}
  1. 利用对OLED显示标志位的判断进行显示工作模式
循迹模式显示判断
if(mark != FOLLOW)
{
    OLEDClearScreen();
    Oled_Show_Str(2,2,"---Follow---");
}

跟随模式显示判断
if(mark != TRACK)
{
    OLEDClearScreen();
    Oled_Show_Str(2,2,"---Tracking---");
}

避障模式显示判断
if(mark != AVOID)
{
    OLEDClearScreen();
    Oled_Show_Str(2,2,"---Avoiding---");
}

停止模式显示判断
if(mark != STOP)
{
    OLEDClearScreen();
    Oled_Show_Str(2,2,"---Stop---");
}
  1. 四种模式具体判断代码
while(1)
{
    //满足循迹模式,调用循迹模式
    if(A25==1 && A26==1 && A27==0)
    {
        if(mark != FOLLOW)
        {
            OLEDClearScreen();
            Oled_Show_Str(2,2,"---Follow---");
        }
        mark = FOLLOW;
        followMode();
    }

    //满足避障模式,调用避障模式
    if(A25==1 && A26==0 && A27==1)
    {
        if(mark != AVOID)
        {
            OLEDClearScreen();
            Oled_Show_Str(2,2,"---Avoiding---");
        }
        mark = AVOID;
        avoidingMode();
    }

    //满足跟随模式,调用跟随模式
    if(A25==0 && A26==1 && A27==1)
    {
        if(mark != TRACK)
        {
            OLEDClearScreen();
            Oled_Show_Str(2,2,"---Tracking---");
        }
        mark = TRACK;
        trackingMode();
    }
    //满足停止模式,调用停止模式
    if(A25==0 && A26==0 && A27==0)
    {
        if(mark != STOP)
        {
            OLEDClearScreen();
            Oled_Show_Str(2,2,"---Stop---");
        }
        mark = STOP;
        stop();
    }
}
  1. 主C文件代码
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "motor.h"
#include "oled.h"

#define MIDDLE 0    //定义中间位置宏
#define RIGHT  1    //定义右边位置宏
#define LEFT   2    //定义左边位置宏
#define TRACK  1    //定义循迹模式OLED显示
#define FOLLOW 2    //定义跟随模式OLED显示
#define AVOID  3    //定义避障模式OLED显示
#define STOP   4    //定义停下模式OLED显示

sbit D1 = P3^7;     //右转指示灯
sbit D2 = P3^6;     //左转指示灯
sbit leftTracking = P2^7;     //左边轮子的循迹模块接单片机P2.7口
sbit rightTracking = P2^6;    //右边轮子的循迹模块接单片机P2.6口
sbit leftFollow = P2^5;       //左边轮子的跟随模块接单片机P2.5口
sbit rightFollow = P2^4;      //右边轮子的跟随模块接单片机P2.4口
sbit A25 = P1^3;              //语音控制端口A25接单片机P1.3口
sbit A26 = P1^4;              //语音控制端口A26接单片机P1.4口
sbit A27 = P1^5;              //语音控制端口A27接单片机P1.5口

/*
		时间:    2023年10月4日14:00:24
		程序功能:垃圾桶03实现距离感应开盖
		注意:一定要在魔术手中Output内勾选输出Hex文件,不然代码无效,执行其他代码。
*/

float disMiddle;                 //中间距离变量
float disLeft;                   //左边距离变量
float disRight;                  //右边距离变量
char dir;                         //转头标志位变量

void avoidingMode()
{
	  if(dir != MIDDLE)               //方向不在中间会中
		{
			sg90Middle();                 //回中
			Delay200ms();
			Delay300ms();
			dir = MIDDLE;                 //软件初始化
		}
		disMiddle = getDistance();      //先测中间
		
		if(disMiddle > 30)  						//如果中间距离大于30厘米就前进
		{
			//前进
			goFront();
		}
		else if(disMiddle < 10)
		{
			goBack();
		}
		else                						//否则就摇头
		{
			stop();                       //停止
		  sg90Left();       						//转左边
			Delay300ms();                //等待500毫秒
			Delay200ms();
			disLeft = getDistance();      //测左边
			
			sg90Middle();                 //转中间
			Delay300ms();                 //等待500毫秒
			Delay200ms();
			
			sg90Right();                  //转右边			
			Delay300ms();                 //等待500毫秒
			Delay200ms();
			disRight = getDistance();
			dir      = RIGHT;             //记录末位置
			
			if(disLeft < disRight)        //左边小于右边,向右转
			{
				D1 = 0;
				goRight();
				Delay300ms();                 
				stop();
				D1 = 1;
				
			}
			
			if(disRight < disLeft )        //左边大于右边,向左转
			{
				D2 = 0;
				goLeft();
				Delay300ms();                 
				stop();
				D2 = 1;
			}
		}
}

void  followMode()
{
	  //下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
	  if(leftTracking==0 && rightTracking==0)
		{
			goFront();
		}
		//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
		if(leftTracking==1 && rightTracking==0)
		{
			goLeft();
		}
		
		if(leftTracking==0 && rightTracking==1)
		{
			goRight();
		}
		
		if(leftTracking==1 && rightTracking==1)
		{
			stop();
		}
}

void trackingMode()
{
	//小车两个模块都能反射回来红外,输出低电平,灯亮,直走
     
		if(leftFollow==0 && rightFollow==0)
		{
			goFront();
		}
		
		//右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
		if(leftFollow==1 && rightFollow==0)
		{
			goRight();
		}
		
		//左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
		if(leftFollow==0 && rightFollow==1)
		{
			goLeft();
		}
		
		if(leftFollow==1 && rightFollow==1)
		{
			stop();
		}
}

void main()
{
	char mark = 0;
	D1 = 1;
	D2 = 1;
	initTime0();
	initTime1();
	initOLED();
	OLEDClearScreen();
	Oled_Show_Str(2,2,"---Ready---");
	sg90Middle();
	Delay300ms();
	Delay300ms();
	dir = MIDDLE;                     //记录中间位置
	while(1)
	{
		//满足循迹模式,调用循迹模式
		if(A25==1 && A26==1 && A27==0)
		{
			if(mark != FOLLOW)
			{
				OLEDClearScreen();
				Oled_Show_Str(2,2,"---Follow---");
			}
			mark = FOLLOW;
			followMode();
		}
		
		//满足避障模式,调用避障模式
		if(A25==1 && A26==0 && A27==1)
		{
			if(mark != AVOID)
			{
				OLEDClearScreen();
				Oled_Show_Str(2,2,"---Avoiding---");
			}
			mark = AVOID;
			avoidingMode();
		}
		
		//满足跟随模式,调用跟随模式
		if(A25==0 && A26==1 && A27==1)
		{
			if(mark != TRACK)
			{
				OLEDClearScreen();
				Oled_Show_Str(2,2,"---Tracking---");
			}
			mark = TRACK;
			trackingMode();
		}
		//满足停止模式,调用停止模式
		if(A25==0 && A26==0 && A27==0)
		{
			if(mark != STOP)
			{
				OLEDClearScreen();
				Oled_Show_Str(2,2,"---Stop---");
			}
			mark = STOP;
			stop();
		}
	}
}

结束语

  很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值