语音模块 SU-03T

这是一个非特定语音识别,也就是不识别特定人类的声音的模块。这个模块的操作相较于其他语音识别模块更简单,不需要编程或二次开发,只需要通过厂家给的网站配置后即可使用。

配置语音模块

1. 首先,进入生产该模块的厂家所提供的配置网址 智能公元/AI产品零代码平台 然后点击右上角注册登录

 2. 点击左侧的“所有产品”,然后点击右上侧的“创建产品”,然后选择“其他产品” -> “纯离线方案” -> "SU-03T" -> “下一步”

3.  配置"GPIO_A25" "GPIO_A26" "GPIO_A27",使其默认输出高电平,目的是通过语音识别模块来改变小车的三种工作模式:

 4. 配置唤醒的文字:

5. 配置命令

5.1 先点击基础信息配置命令的内容

 5.2 再点击控制详情,分别在follow_path; avoid_obs; follow_thing后点击“添加控制”:

👆,这里只展示了一张图,实际上,比如设置 follow_path 的控制,就要使得只有GPIO_A25为低电平,且另外两个保持高电平,所以一共要设置9次,设置完的界面如下图:

 因此,配置的结果就是,唤醒语句分别是“循迹海豹”,“避障海豹” 和 “跟随海豹”,分别对应只有GPIO_A25,GPIO_A26 和 GPIO_A27 为低电平。

 6. 配置一些其他的有的没的

 7. 点击右上角的发布版本进行保存并发布:

 8. 等待SDK生成(大概10-30分钟),生成之后下载SDK下来,然后解压到一个只有英文的目录下:

 

 9. 打开文件夹,点击“image_demo” -> 点击“Hummingbird-M-Update-Tool” -> 点击“UniOneUpdateTool.exe”

10. 将SU-03T通过CH340通过串口连接到电脑,根据手册B6接TXD B7接RXD

11. 再次打开第9步的软件,此时已经发现了这个设备,可以进行烧录:

 11.1 选择镜像文件,选择刚刚下载的SDK中的bin文件:

11.2  将模块的电源拨到OFF状态,然后点击烧录

11.3 再把电源从OFF 拨到 ON(其实和51单片机的烧录很像,就是软件不一样):

等待烧录完成 

此时语音模块就配置好了!如果接入单片机,就可以通过代码实现对模式的控制了!

语音模块实现效果

1. 开机播报“海豹世界第一"

1. 当说出“你好海豹战车”或“海豹战车在否"可以唤醒模块,模块回复“海豹海豹”或“海豹战车随时准备着

2. 当超过10s没有指令或说出“再见海豹”时,模块会进入休眠模式,并回复“海豹先去休息拉

3. 当说出“循迹海豹”时,模块回复“我将沿着黑线找到每一只企鹅”,并只将GPIO_A25拉低

4. 当说出“避障海豹”时,模块回复“雌性海豹我一只也不会碰”,并只将GPIO_A26拉低

5. 当说出“跟随海豹”时,模块回复“我将追随你,我的海豹王”,并只将GPIO_A27拉低

最终整合

将语音模块接入小车,并移植整合循迹;跟随;避障和显示的代码,实现最终的效果!

(但是由于避障小车的舵机和测距传感器各用了一个定时器,而C52一共就两个定时器,所以两轮分别调速速度的测量蓝牙控制就无法同时实现了,但如果使用更高级的板子,有5个及以上的定时器的话,其实把调速和速度测量和蓝牙串口控制的代码也一起移植过来就可以,也很简单。)

语音模块接线后小车的终极形态:

PS:注意!由于最终形态的外设过多且都需要供电,所以对于前面4个传感器的供电GND线给单片机供电的VCC线是两根烧写时必须拔掉,烧写完才能插上的线!!!

代码整合:

motor.c:

#include "reg52.h"

sbit B_1A_le = P3^6;//LEFT WHEEL
sbit B_1B_le = P3^3;//LEFT WHEEL
sbit A_1A_ri = P3^4;//RIGHR WHEEL
sbit A_1B_ri = P3^5;//RIGHR WHEEL


void move_backward() //后退
{
	B_1A_le = 1;
	B_1B_le = 0;
	
	A_1A_ri = 1;
	A_1B_ri = 0;
}

void move_forward() //前进
{
	B_1A_le = 0;
	B_1B_le = 1;
	
	A_1A_ri = 0;
	A_1B_ri = 1;
}

void move_leftturn() //左转
{
	B_1A_le = 0; //左轮不动
	B_1B_le = 0;
	
	A_1A_ri = 0; //右轮往前
	A_1B_ri = 1;
}

void move_rightturn() //右转
{
	B_1A_le = 0; //左轮往前
	B_1B_le = 1;
	
	A_1A_ri = 0; //右轮不动
	A_1B_ri = 0;
}

void move_stop() //停止
{
	B_1A_le = 0;
	B_1B_le = 0;
	
	A_1A_ri = 0;
	A_1B_ri = 0;
}

delay.c:

#include "reg52.h"
#include "intrins.h" //这个库加了,delay函数里面的nop()才不会报错

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

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

void Delay500ms()		//@11.0592MHz //0.5s
{
	unsigned char i, j, k;
 
	_nop_();
	i = 4;
	j = 129;
	k = 119;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;
 
	i = 9;
	j = 244;
	do
	{
		while (--j);
	} while (--i);
}

void Delay300ms()		//@11.0592MHz
{
	unsigned char i, j, k;
 
	_nop_();
	i = 3;
	j = 26;
	k = 223;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

route_sensor.c:

#include "reg52.h"
#include "motor.h"


sbit sensor_left = P2^7;
sbit sensor_right = P2^6;


void sensor_react_route()
{
	if(sensor_left == 0 && sensor_right == 0){ //此时左右都没有检测到黑线,说明在轨迹上,直走, 两个灯都亮
		move_forward();
	}
	if(sensor_left == 1 && sensor_right == 0){ //此时左模块检测到黑线,说明车身偏右即将离开轨道,此时需要左转修正,只有右灯亮
		move_leftturn();
	}
	if(sensor_left == 0 && sensor_right == 1){ //此时右模块检测到黑线,说明车身偏左即将离开轨道,此时需要右转修正,只有左灯亮
		move_rightturn();
	}
	if(sensor_left == 1 && sensor_right == 1){ //此时左右都检测到黑线,说明不在轨迹上了,停止,两个灯都灭
		move_stop();
	}
	
}

follow_sensor.c:

#include "reg52.h"
#include "motor.h"


sbit sensor_left = P2^5;
sbit sensor_right = P2^4;


void sensor_react_follow()
{
	if(sensor_left == 0 && sensor_right == 0){ //左右模块都能反射红外,物体在正前方,直行
		move_forward();
	}
	if(sensor_left == 1 && sensor_right == 0){ //右模块能反射红外而左模块不能,物体在右边,右转
		move_rightturn();
	}
	if(sensor_left == 0 && sensor_right == 1){ //左模块能反射红外而右模块不能,物体在左边,左转
		move_leftturn();
	}
	if(sensor_left == 1 && sensor_right == 1){ //左右模块都不能反射红外,前方无物体,停止
		move_stop();
	}
	
}

HC-SR04.c:

#include "reg52.h"
#include "delay.h"
#include "motor.h"
#include "SG90.h"

sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册
sbit Trig = P2^3; //超声波测距模块Trig口
sbit Echo = P2^2; //超声波测距模块Echo口
double time;
double dist_middle;
double dist_left;
double dist_right;


void Timer1Init(void)		//timer1控制超声波测距模块
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0;		//设置定时初值
	TH1 = 0;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时

}

void startHC() //先要给Trig一个至少10ms的高电平方波
{
	Trig = 0;
	Delay5ms();
	Trig = 1;
	Delay5ms();
	Delay5ms();
	Trig = 0;
}

double get_dist()
{
	double dist;
	
	startHC();
	while(Echo == 0);//程序会卡在这里直到Echo变高的一瞬间
		
	TR1 = 1; //定时器1开始计时
		
	while(Echo == 1);//程序会卡在这里直到Echo变低的一瞬间
	
	TR1 = 0; //定时器1停止计时
		
		//十进制2左移1位,变成20,相当于乘以10
		//二进制1左移1位,变成10,相当于乘以2
		//所以16进制左移1位需要乘以16,通过左移2位(16*16=256)之后将TH0和TL0进行拼接,就可以得到定时器的计数值
	time = (TH1 * 256 + TL1)*1.085; //us为单位
		
	dist = 0.034 * time * 0.5; //340m/s换算到cm为34000cm/s;换算到us为34000cm/1000000us = 0.034cm/us
	
	TH1 = 0;
	TL1 = 0; //重新给初值!!!!!  

	return dist;
}

void deal_dist()
{
	dist_middle = get_dist();
	if(dist_middle > 35){ //如果距离大于35cm就可以前进
		move_forward();//前进
	}else if((dist_middle > 10) && (dist_middle < 35)){ //距离小于35cm但大于10cm时
		move_stop();//先停下
		head_left();//将测距传感器通过SG90转到左边
		dist_left = get_dist();
		Delay300ms();//根据实际情况
		
		head_right();//将测距传感器通过SG90转到右边
		dist_right = get_dist();
		Delay300ms();//根据实际情况
		
		if(dist_right > dist_left){ //哪边距离大往哪边转
			move_rightturn();
			Delay300ms();//根据实际情况
			move_stop();
		}else{
			move_leftturn();
			Delay300ms();//根据实际情况
			move_stop();
		}
	}else{ //当距离已经小于10cm时
		move_backward();//此时距离已经危险了,需要后退一下
		Delay300ms();//根据实际情况
		move_stop();
	}
}	

SG90.c:

#include "reg52.h"

sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册
sbit SG90_CON = P1^6;
int cnt = 0;
int angle;

void Timer0Init(void)		//timer0控制舵机
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x33;		//设置定时初值
	TH0 = 0xFE;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0 = 1;
	EA = 1;   //打开中断!
}

//timer0的中断处理程序 //中断程序一般写在main函数的后面 //定时器0溢出时将触发这个中断函数
void SG90_inter() interrupt 1 
{
	cnt++;
	TL0 = 0x33;		//重新给初值!!
	TH0 = 0xFE;
	
	if(cnt < angle){ //cnt =1 时,爆表了一次,过了0.5ms
		SG90_CON = 1;
	}else{
		SG90_CON = 0;
	}
 
	if(cnt == 39){//每经过(40*0.5毫秒 = )20毫秒,PWM波经过一个周期
		cnt = 0;
		SG90_CON = 1;
	}
	
}

void head_right()
{
	angle = 1; //右边位置
	cnt = 0; //角度改变,cnt清零,防止出现cnt数到一半变角度了
}

void head_middle()
{
	angle = 3; //中间位置
	cnt = 0; //角度改变,cnt清零,防止出现cnt数到一半变角度了
}
	
void head_left()
{
	angle = 5; //左边位置
	cnt = 0; //角度改变,cnt清零,防止出现cnt数到一半变角度了
}

OLED.c:

#include "reg52.h"
#include "intrins.h" //这个库加了,delay函数里面的nop()才不会报错
#include <string.h>
#include "OLEDfont.h"
 
 
sfr AUXR = 0x8E; //配置了这句话,才可以在UART的初始化里写AUXR寄存器,原因见STC89系列的手册

sbit SCL = P1^3;
sbit SDA = P1^2;
 
 
 
void IIC_start()
{
	SDA = 0;	
	SCL = 1;
	SDA = 1;
	_nop_(); //约5微妙
	SDA = 0;
	_nop_();
	SCL = 0;
	
}
 
void IIC_stop()
{
	SCL = 0;	
	SCL = 1;
	SDA = 0;
	_nop_();
	SDA = 1;
	_nop_();
	SDA = 0;
	
}
 
char IIC_ACK()
{
	char flag;
	
	SDA = 1;//在脉冲9释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	
	flag = SDA;
	_nop_();
	SCL = 0;
	//_nop_();
	
	return flag;
}
 
void IIC_sendByte(char sdata) //data直接定义为char的好处是char就是1个byte(8个bit)
{	
	int i;
	
	for(i = 0; i < 8; i++){
		SCL = 0; //拉低,让SDA准备数据
		SDA = sdata & 0x80; //与上“1000 0000”,即只有最高位保留,其他全部清0
		_nop_();//给数据建立一个时间
		SCL = 1; //拉高,形成脉冲开始传输,此时SDA不能变
		_nop_();//发送中
		SCL = 0;//重新拉低
		_nop_();
		sdata = sdata << 1; //发完1个bit左移一位
	}
 
}
 
void OLED_writecmd(char cmd)
{
	IIC_start();
	IIC_sendByte(0x78);
	IIC_ACK();
	IIC_sendByte(0x00);
	IIC_ACK();
	IIC_sendByte(cmd);
	IIC_ACK();
	IIC_stop();
		
}
 
void OLED_writedata(char wdata)
{
	IIC_start();
	IIC_sendByte(0x78);
	IIC_ACK();
	IIC_sendByte(0x40);
	IIC_ACK();
	IIC_sendByte(wdata);
	IIC_ACK();
	IIC_stop();
	
}
 
void Oled_Init()
{
	OLED_writecmd(0xAE);//--display off
	OLED_writecmd(0x00);//---set low column address
	OLED_writecmd(0x10);//---set high column address
	OLED_writecmd(0x40);//--set start line address  
	OLED_writecmd(0xB0);//--set page address
	OLED_writecmd(0x81); // contract control
	OLED_writecmd(0xFF);//--128   
	OLED_writecmd(0xA1);//set segment remap 
	OLED_writecmd(0xA6);//--normal / reverse
	OLED_writecmd(0xA8);//--set multiplex ratio(1 to 64)
	OLED_writecmd(0x3F);//--1/32 duty
	OLED_writecmd(0xC8);//Com scan direction
	OLED_writecmd(0xD3);//-set display offset
	OLED_writecmd(0x00);//
	
	OLED_writecmd(0xD5);//set osc division
	OLED_writecmd(0x80);//
	
	OLED_writecmd(0xD8);//set area color mode off
	OLED_writecmd(0x05);//
	
	OLED_writecmd(0xD9);//Set Pre-Charge Period
	OLED_writecmd(0xF1);//
	
	OLED_writecmd(0xDA);//set com pin configuartion
	OLED_writecmd(0x12);//
	
	OLED_writecmd(0xDB);//set Vcomh
	OLED_writecmd(0x30);//
	
	OLED_writecmd(0x8D);//set charge pump enable
	OLED_writecmd(0x14);//
	
	OLED_writecmd(0xAF);//--turn on oled panel		
}
 
void Oled_Clear()
{
	int i;
	int j;
	
	for(i = 0; i<8; i++){
		OLED_writecmd(0xB0 + i);//依次选择每一页
		OLED_writecmd(0x00);//选择0列
	  OLED_writecmd(0x10);//选择0列
		for(j = 0; j<128; j++){
			OLED_writedata(0x00);//由于地址会自动偏移,所以只要重复写128次全0,就可以清一个PAGE
		}
	}
}
 
void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
	unsigned int  i;
	OLED_writecmd(0xb0+(row*2-2));                           //page 0
	OLED_writecmd(0x00+(col&0x0f));                          //low
	OLED_writecmd(0x10+(col>>4));                            //high	
	for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
		OLED_writedata(F8X16[i]);                            //写数据oledTable1
	}

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

void Oled_Show_Str(char row,char col,char *str){
	while(*str!=0){
		Oled_Show_Char(row,col,*str);
		str++;
		col += 8;	
	}		
}

main.c:

#include "reg52.h"
#include "intrins.h" //这个库加了,delay函数里面的nop()才不会报错
#include "motor.h"
#include "delay.h"
#include "route_sensor.h"
#include "follow_sensor.h"
#include "SG90.h"
#include "HC-SR04.h"
#include "OLED.h"

extern int angle; //angle 1(0度);2(45度);3(90度);4(135度);5(180度)

sbit A25 = P3^1;
sbit A26 = P3^0;
sbit A27 = P1^7;

void xunji()
{
	sensor_react_route();
}

void gensui()
{
	sensor_react_follow();
}

void bizhang()
{
	if(angle != 3){ //如果不在中间的话,回到中间
		head_middle();
		Delay300ms();
	}
	deal_dist();	
}

void main()
{
	int mark = 0;
	
	Delay300ms(); //由于软件实现PWM本来就不太精确且稳定,所以先让系统delay300ms 稳定一下
	
	Timer0Init();
	Timer1Init();
	
	head_middle();
	
	Oled_Init();
	Oled_Clear();
	OLED_writecmd(0x20);//页寻址模式
	OLED_writecmd(0x02);//页寻址模式
	Oled_Show_Str(2,2,"-----Ready----");
	
	
	while(1){
		//满足寻迹模式的条件
		if(A25 == 0 && A26 == 1 && A27 == 1){
			if(mark != 1){
				Oled_Clear();
				Oled_Show_Str(2,2,"follow route");
			}
			mark = 1;
			xunji();
		}
		//满足跟随模式的条件
		if(A25 == 1 && A26 == 1 && A27 == 0){
			if(mark != 2){
				Oled_Clear();
				Oled_Show_Str(2,2,"following");
			}
			mark = 2;
			gensui();
		}
		//满足避障模式的条件
		if(A25 == 1 && A26 == 0 && A27 == 1){
			if(mark != 3){
				Oled_Clear();
				Oled_Show_Str(2,2,"avoiding obs");
			}
			mark = 3;
			bizhang();
		}
	
	}
}
	

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐