51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2

简介: 51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2

51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2

https://developer.aliyun.com/article/1506698


5.远程控制小车

5.1 蓝牙控制小车
  • 使用蓝牙模块,串口透传
  • 蓝牙模块,又叫做蓝牙串口模块


串口透传技术:


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


代码实现:

//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
 
void main()
{
  UartInit();
  
  while(1){
    stop();
  }
}
 
//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
#include "delay.h"
sbit D5 = P3^7;
#define SIZE 12
 
sfr AUXR = 0x8E;
char buffer[SIZE];
 
void UartInit(void)   //9600bps@11.0592MHz
{
  AUXR = 0x01;
  SCON = 0x50; //配置串口工作方式1,REN使能接收
  TMOD &= 0x0F;
  TMOD |= 0x20;//定时器1工作方式位8位自动重装
  
  TH1 = 0xFD;
  TL1 = 0xFD;//9600波特率的初值
  TR1 = 1;//启动定时器
  
  EA = 1;//开启总中断
  ES = 1;//开启串口中断
}
 
//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
  static int i = 0;//静态变量,被初始化一次
  char tmp;
 
  if(RI)//中断处理函数中,对于接收中断的响应
  {
      RI = 0;//清除接收中断标志位
      tmp = SBUF;
      if(tmp == 'M'){
        i = 0;
      }
      buffer[i++] = tmp;
    
      //灯控指令
      if(buffer[0] == 'M'){
        switch(buffer[1]){
          case '1':
            goForward();
            Delay10ms();
            break;
          case '2':
            goBack();
            Delay10ms();
            break;
          case '3':
            goLeft();
            Delay10ms();
            break;
          case '4':
            goRight();
            Delay10ms();
            break;
          default:
            stop();
            break;
        }
      }
    
      if(i == 12) {
        memset(buffer, '\0', SIZE);
        i = 0;
      }
  }
    
}
 
//motor.c
#include "reg52.h"
 
sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
void goForward()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goRight()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
void goLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goBack()
{
  LeftCon1A = 1;
  LeftCon1B = 0;
  
  RightCon1A = 1;
  RightCon1B = 0;
}
 
void stop()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
//delay.c
 
#include "intrins.h"
 
void Delay10ms()    //@11.0592MHz
{
  unsigned char i, j;
 
  i = 18;
  j = 235;
  do
  {
    while (--j);
  } while (--i);
}
 
void Delay1000ms()    //@11.0592MHz
{
  unsigned char i, j, k;
 
  _nop_();
  i = 8;
  j = 1;
  k = 243;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
 
 
5.2 蓝牙控制并测速小车

原理:运用上面讲到的蓝牙模块和测速模块

代码实现:

//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"
#include "Oled.h"
 
sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24];  //主程序发送速度数据的字符串缓冲区
 
void Ex0Init()
{
  EX0 = 1;//允许中断
  //EA = 1;在串口初始化函数中已经打开了总中断
  IT0 = 1;//外部中断的下降沿触发
}
 
void main()
{
  Time0Init();//定时器0初始化
  UartInit();//串口相关初始化
  //外部中断初始化
  Ex0Init();
  Oled_Init();
  Oled_Clear();
  while(1){
    if(signal){//定时器1s到点,把signal置一,主程序发送速度
      sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
      SendString(speedMes);//速度发出去
    
      signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
    }
    Oled_Show_Str(2,2,speedMes);
  }
}
 
void speedHandler() interrupt 0 //外部中断处理函数
{
  speedCnt++;//码盘转动了一个格子
}
 
//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12
 
sfr AUXR = 0x8E;
char buffer[SIZE];
 
void UartInit(void)   //9600bps@11.0592MHz
{
  AUXR = 0x01;
  SCON = 0x50; //配置串口工作方式1,REN使能接收
  TMOD &= 0x0F;
  TMOD |= 0x20;//定时器1工作方式位8位自动重装
  
  TH1 = 0xFD;
  TL1 = 0xFD;//9600波特率的初值
  TR1 = 1;//启动定时器
  
  EA = 1;//开启总中断
  ES = 1;//开启串口中断
}
 
 
void SendByte(char mydata)
{
  SBUF = mydata;
  while(!TI);
  TI = 0;
}
 
void SendString(char *str)
{
  while(*str != '\0'){
    SendByte(*str);
    str++;
  }
}
 
 
//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
  static int i = 0;//静态变量,被初始化一次
  char tmp;
 
  if(RI)//中断处理函数中,对于接收中断的响应
  {
      RI = 0;//清除接收中断标志位
      tmp = SBUF;
      if(tmp == 'M'){
        i = 0;
      }
      buffer[i++] = tmp;
    
      //灯控指令
      if(buffer[0] == 'M'){
        switch(buffer[1]){
          case '1':
            goForward();
            break;
          case '2':
            goBack();
            break;
          case '3':
            goLeft();
            break;
          case '4':
            goRight();
            break;
          default:
            stop();
            break;
        }
      }
    
      if(i == 12) {
        memset(buffer, '\0', SIZE);
        i = 0;
      }
  }
    
}
 
//motor.c
#include "reg52.h"
 
sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
void goForward()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goRight()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
void goLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goBack()
{
  LeftCon1A = 1;
  LeftCon1B = 0;
  
  RightCon1A = 1;
  RightCon1B = 0;
}
 
void stop()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
//time.c
#include "motor.h"
#include "reg52.h"
 
extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;
 
void Time0Init()
{
  //1. 配置定时器0工作模式位16位计时
  TMOD = 0x01;
  //2. 给初值,定一个0.5出来
  TL0=0x33;
  TH0=0xFE;
  //3. 开始计时
  TR0 = 1;
  TF0 = 0;
  //4. 打开定时器0中断
  ET0 = 1;
  //5. 打开总中断EA
  EA = 1;
}
 
void Time0Handler() interrupt 1
{
  cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL0=0x33;
  TH0=0xFE;
  
  if(cnt == 2000){//爆表2000次,经过了1s
    signal = 1;
    cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
    //计算小车的速度,也就是拿到speedCnt的值
    speed = speedCnt;
    speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
  }
    
}
 
//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"
 
 
sbit scl = P1^2;
sbit sda = P1^3;
 
void IIC_Start()
{
  scl = 0;
  sda = 1;
  scl = 1;
  _nop_();
  sda = 0;
  _nop_();
}
 
void IIC_Stop()
{
  scl = 0;
  sda = 0;
  scl = 1;
  _nop_();
  sda = 1;
  _nop_();
}
 
char IIC_ACK()
{
  char flag;
  sda = 1;//就在时钟脉冲9期间释放数据线
  _nop_();
  scl = 1;
  _nop_();
  flag = sda;
  _nop_();
  scl = 0;
  _nop_();
  
  return flag;
}
 
void IIC_Send_Byte(char dataSend)
{
  int i;
  
  for(i = 0;i<8;i++){
    scl = 0;//scl拉低,让sda做好数据准备
    sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    _nop_();//发送数据建立时间
    scl = 1;//scl拉高开始发送
    _nop_();//数据发送时间
    scl = 0;//发送完毕拉低
    _nop_();//
    dataSend = dataSend << 1;
  }
}
 
void Oled_Write_Cmd(char dataCmd)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //  5. ACK
  IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
void Oled_Write_Data(char dataData)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //  5. ACK
  IIC_ACK();
  ///6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
 
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_Clear()
{
  unsigned char i,j; //-128 --- 127
  
  for(i=0;i<8;i++){
    Oled_Write_Cmd(0xB0 + i);//page0--page7
    //每个page从0列
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
    //0到127列,依次写入0,每写入数据,列地址自动偏移
    for(j = 0;j<128;j++){
      Oled_Write_Data(0);
    }
  }
}
 
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; 
  }   
}
 
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指令以回车作为结 尾,响应或上报以回车换行为结尾。

代码实现:

//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.h"
#include "Oled.h"
#include "esp8266.h"
 
sbit speedIO = P3^2;//外部中断0
unsigned int speedCnt = 0; //统计格子,脉冲次数
extern unsigned int speed;//速度
extern char signal; //主程序发速度数据的通知
char speedMes[24];  //主程序发送速度数据的字符串缓冲区
//发送数据
char FSSJ[] = "AT+CIPSEND=0,5\r\n";
 
void Ex0Init()
{
  EX0 = 1;//允许中断
  //EA = 1;在串口初始化函数中已经打开了总中断
  IT0 = 1;//外部中断的下降沿触发
}
 
void main()
{
  Time0Init();//定时器0初始化
  UartInit();//串口相关初始化
  Delay1000ms();//给espwifi模块上电时间
  
  initWifi_AP(); //初始化wifi工作在ap模式
  waitConnect(); //等待客户端的连接
  
  //外部中断初始化
  Ex0Init();
  Oled_Init();
  Oled_Clear();
  while(1){
    if(signal){//定时器1s到点,把signal置一,主程序发送速度
      SendString(FSSJ);
      Delay1000ms();
      sprintf(speedMes,"%dcms",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
      SendString(speedMes);//速度发出去
    
      signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
    }
    Oled_Show_Str(2,2,speedMes);
  }
}
 
void speedHandler() interrupt 0 //外部中断处理函数
{
  speedCnt++;//码盘转动了一个格子
}
 
//uart.c
#include "reg52.h"
#include "motor.h"
#include "string.h"
sbit D5 = P3^7;
#define SIZE 12
 
sfr AUXR = 0x8E;
char buffer[SIZE];
 
extern char AT_OK_Flag;       //OK返回值的标志位
extern char Client_Connect_Flag;
 
void UartInit(void)   //9600bps@11.0592MHz
{
  AUXR = 0x01;
  SCON = 0x50; //配置串口工作方式1,REN使能接收
  TMOD &= 0x0F;
  TMOD |= 0x20;//定时器1工作方式位8位自动重装
  
  TH1 = 0xFD;
  TL1 = 0xFD;//9600波特率的初值
  TR1 = 1;//启动定时器
  
  EA = 1;//开启总中断
  ES = 1;//开启串口中断
}
 
 
void SendByte(char mydata)
{
  SBUF = mydata;
  while(!TI);
  TI = 0;
}
 
void SendString(char *str)
{
  while(*str != '\0'){
    SendByte(*str);
    str++;
  }
}
 
 
//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
  static int i = 0;//静态变量,被初始化一次
  char tmp;
 
  if(RI)//中断处理函数中,对于接收中断的响应
  {
      RI = 0;//清除接收中断标志位
      tmp = SBUF;
      if(tmp == 'M' || tmp == 'O' || tmp == '0'){
        i = 0;
      }
      buffer[i++] = tmp;
    
      //连接服务器等OK返回值指令的判断
      if(buffer[0] == 'O' && buffer[1] == 'K'){
        AT_OK_Flag  = 1;
        memset(buffer, '\0', SIZE);
      }
      
      if(buffer[0] == '0' && buffer[2] == 'C'){
        Client_Connect_Flag = 1;
        memset(buffer, '\0', SIZE);
      }
      //灯控指令
      if(buffer[0] == 'M'){
        switch(buffer[1]){
          case '1':
            goForward();
            break;
          case '2':
            goBack();
            break;
          case '3':
            goLeft();
            break;
          case '4':
            goRight();
            break;
          default:
            stop();
            break;
        }
      }
    
      if(i == 12) {
        memset(buffer, '\0', SIZE);
        i = 0;
      }
  }
    
}
 
//motor.c
#include "reg52.h"
 
sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
void goForward()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goRight()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
void goLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goBack()
{
  LeftCon1A = 1;
  LeftCon1B = 0;
  
  RightCon1A = 1;
  RightCon1B = 0;
}
 
void stop()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
//time.c
#include "motor.h"
#include "reg52.h"
 
extern unsigned int speedCnt;
unsigned int speed;
char signal = 0;
unsigned int cnt = 0;
 
void Time0Init()
{
  //1. 配置定时器0工作模式位16位计时
  TMOD = 0x01;
  //2. 给初值,定一个0.5出来
  TL0=0x33;
  TH0=0xFE;
  //3. 开始计时
  TR0 = 1;
  TF0 = 0;
  //4. 打开定时器0中断
  ET0 = 1;
  //5. 打开总中断EA
  EA = 1;
}
 
void Time0Handler() interrupt 1
{
  cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL0=0x33;
  TH0=0xFE;
  
  if(cnt == 2000){//爆表2000次,经过了1s
    signal = 1;
    cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
    //计算小车的速度,也就是拿到speedCnt的值
    speed = speedCnt;
    speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
  }
    
}
 
//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"
 
 
sbit scl = P1^2;
sbit sda = P1^3;
 
void IIC_Start()
{
  scl = 0;
  sda = 1;
  scl = 1;
  _nop_();
  sda = 0;
  _nop_();
}
 
void IIC_Stop()
{
  scl = 0;
  sda = 0;
  scl = 1;
  _nop_();
  sda = 1;
  _nop_();
}
 
char IIC_ACK()
{
  char flag;
  sda = 1;//就在时钟脉冲9期间释放数据线
  _nop_();
  scl = 1;
  _nop_();
  flag = sda;
  _nop_();
  scl = 0;
  _nop_();
  
  return flag;
}
 
void IIC_Send_Byte(char dataSend)
{
  int i;
  
  for(i = 0;i<8;i++){
    scl = 0;//scl拉低,让sda做好数据准备
    sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    _nop_();//发送数据建立时间
    scl = 1;//scl拉高开始发送
    _nop_();//数据发送时间
    scl = 0;//发送完毕拉低
    _nop_();//
    dataSend = dataSend << 1;
  }
}
 
void Oled_Write_Cmd(char dataCmd)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //  5. ACK
  IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
void Oled_Write_Data(char dataData)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //  5. ACK
  IIC_ACK();
  ///6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
 
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_Clear()
{
  unsigned char i,j; //-128 --- 127
  
  for(i=0;i<8;i++){
    Oled_Write_Cmd(0xB0 + i);//page0--page7
    //每个page从0列
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
    //0到127列,依次写入0,每写入数据,列地址自动偏移
    for(j = 0;j<128;j++){
      Oled_Write_Data(0);
    }
  }
}
 
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; 
  }   
}
 
 
//esp8266.c
#include "uart.h"
 
//1 工作在路由模式
code char LYMO[] = "AT+CWMODE=2\r\n";
//2 使能多链接
code char DLJ[] = "AT+CIPMUX=1\r\n"; 
//3 建立TCPServer
code char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333 
 
 
 
char AT_OK_Flag = 0;        //OK返回值的标志位
char Client_Connect_Flag = 0;
 
void initWifi_AP()
{
  SendString(LYMO);
  while(!AT_OK_Flag);
  AT_OK_Flag = 0;
  SendString(DLJ);
  while(!AT_OK_Flag);
  AT_OK_Flag = 0;
}
 
void waitConnect()
{
  SendString(JLFW);
  while(!Client_Connect_Flag);
  AT_OK_Flag = 0; 
}
 
//delay.c
#include "intrins.h"
 
void Delay1000ms()    //@11.0592MHz
{
  unsigned char i, j, k;
 
  _nop_();
  i = 8;
  j = 1;
  k = 243;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
5.4 4g控制小车

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

模块介绍:

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

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

6.语音控制小车

6.1语音模块配置

使用SU-03T / LD3320

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


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

代码示例:

//main.c
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "Oled.h"
#include "motor.h"
 
#define MIDDLE 0
#define LEFT 1
#define RIGHT 2
 
#define BZ 1
#define XJ 2
#define GS 3
 
 
sbit A25 = P1^5;
sbit A26 = P1^6;
sbit A27 = P1^7;
 
sbit leftSensorX = P2^7;
sbit rightSensorX = P2^6;
 
sbit leftSensorG = P2^5;
sbit rightSensorG = P2^4;
 
char dir;
  
double disMiddle;
double disLeft;
double disRight;
 
void xunjiMode()
{
    if(leftSensorX == 0 && rightSensorX == 0){
      goForward();
    }
    if(leftSensorX == 1 && rightSensorX == 0){
      goLeft();
    }
    
    if(leftSensorX == 0 && rightSensorX == 1){
      goRight();
    }
    
    if(leftSensorX == 1 && rightSensorX == 1){
      //停
      stop();
    }
}
 
void gensuiMode()
{
    if(leftSensorG == 0 && rightSensorG == 0){
      goForward();
    }
    if(leftSensorG == 1 && rightSensorG == 0){
      goRight();
    }
    
    if(leftSensorG == 0 && rightSensorG == 1){
      
      goLeft();
    }
    
    if(leftSensorG == 1 && rightSensorG == 1){
      //停
      stop();
    }
}
 
void bizhangMode()
{
  if(dir != MIDDLE){
      sgMiddle();
      dir = MIDDLE;
      Delay300ms();
    }
    disMiddle = get_distance();
    
    if(disMiddle > 35){
      //前进
      goForward();
    }else if(disMiddle < 10){
      goBack();
      
    }else
    {
      //停止
      stop();
      //测左边距离
      sgLeft();
      Delay300ms();
      disLeft = get_distance();
      
      sgMiddle();
      Delay300ms();
      
      sgRight();
      dir = RIGHT;
      Delay300ms();
      disRight = get_distance();
      
      if(disLeft < disRight){
        goRight();
        Delay150ms();
        stop();
      }
      if(disRight < disLeft){
        goLeft();
        Delay150ms();
        stop();
      }
    }
 
}
 
void main()
{
 
  int mark = 0;
  
  Time0Init();
  Time1Init();
  //舵机的初始位置
 
  sgMiddle();
  Delay300ms();
  Delay300ms();
  dir = MIDDLE;
  
  Oled_Init();
  Oled_Clear();
  Oled_Show_Str(2,2,"-----Ready----");
  
  while(1){
    //满足寻迹模式的条件
    if(A25 == 0 && A26 == 1 && A27 == 1){
      if(mark != XJ){
        Oled_Clear();
        Oled_Show_Str(2,2,"-----XunJi----");
      }
      mark = XJ;
      xunjiMode();
    }
    //满足跟随模式的条件
    if(A25 == 1 && A26 == 0 && A27 == 1){
      if(mark != GS){
        Oled_Clear();
        Oled_Show_Str(2,2,"-----GenSui----");
      }
      mark = GS;
      gensuiMode();
    }
    //满足避障模式的条件
    if(A25 == 1 && A26 == 1 && A27 == 0){
      if(mark != BZ){
        Oled_Clear();
        Oled_Show_Str(2,2,"-----BiZhang----");
      }
      mark = BZ;
      bizhangMode();
    }
  
  }
}
 
 
//hc04.c
#include "reg52.h"
#include "delay.h"
 
sbit Trig     = P2^3;
sbit Echo     = P2^2;
 
void Time1Init()
{ 
  TMOD &= 0x0F;   //设置定时器模式
  TMOD |= 0x10;
  TH1 = 0;
  TL1 = 0;
  //设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}
 
void startHC()
{
  Trig = 0;
  Trig = 1;
  Delay10us();
  Trig = 0;
}
 
double get_distance()
{
    double time;
    //定时器数据清零,以便下一次测距
    TH1 = 0;
    TL1 = 0;
  //1. Trig ,给Trig端口至少10us的高电平
    startHC();
    //2. echo由低电平跳转到高电平,表示开始发送波
    while(Echo == 0);
    //波发出去的那一下,开始启动定时器
    TR1 = 1;
    //3. 由高电平跳转回低电平,表示波回来了
    while(Echo == 1);
    //波回来的那一下,我们开始停止定时器
    TR1 = 0;
    //4. 计算出中间经过多少时间
    time = (TH1 * 256 + TL1)*1.085;//us为单位
    //5. 距离 = 速度 (340m/s)* 时间/2
    return  (time * 0.017);
}
 
 
//delay.c
#include "intrins.h"
 
void Delay2000ms()    //@11.0592MHz
{
  unsigned char i, j, k;
 
  i = 15;
  j = 2;
  k = 235;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
 
void Delay10us()    //@11.0592MHz
{
  unsigned char i;
 
  i = 2;
  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);
}
 
 
void Delay150ms()   //@11.0592MHz
{
  unsigned char i, j, k;
 
  i = 2;
  j = 13;
  k = 237;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
void Delay450ms()   //@11.0592MHz
{
  unsigned char i, j, k;
 
  _nop_();
  i = 4;
  j = 39;
  k = 209;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
 
//sg90.c
#include "reg52.h"
#include "delay.h"
 
sbit sg90_con = P1^1;
 
int jd;
int cnt = 0;
 
void Time0Init()
{
  //1. 配置定时器0工作模式位16位计时
  TMOD &= 0xF0;   //设置定时器模式
  TMOD |= 0x01;
  //2. 给初值,定一个0.5出来
  TL0=0x33;
  TH0=0xFE;
  //3. 开始计时
  TR0 = 1;
  TF0 = 0;
  //4. 打开定时器0中断
  ET0 = 1;
  //5. 打开总中断EA
  EA = 1;
}
 
void sgMiddle()
{
  //中间位置
  jd = 3; //90度 1.5ms高电平
  cnt = 0;
}
 
void sgLeft()
{
  //左边位置
  jd = 5; //135度 1.5ms高电平
  cnt = 0;
}
 
void sgRight()
{
  //右边位置
  jd = 1; //0度
  cnt = 0;
}
 
 
void Time0Handler() interrupt 1
{
  cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL0=0x33;
  TH0=0xFE;
  
  //控制PWM波
  if(cnt < jd){
    sg90_con = 1;
  }else{
    sg90_con = 0;
  }
  
  if(cnt == 40){//爆表40次,经过了20ms
    cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
    sg90_con = 1;
  }
    
}
 
//motor.c
#include "reg52.h"
 
sbit RightCon1A = P3^7;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
void goForward()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goRight()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
void goLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goBack()
{
  LeftCon1A = 1;
  LeftCon1B = 0;
  
  RightCon1A = 1;
  RightCon1B = 0;
}
 
void stop()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
//oled.c
#include "reg52.h"
#include "intrins.h"
#include "Oledfont.h"
 
 
sbit scl = P1^2;
sbit sda = P1^3;
 
void IIC_Start()
{
  scl = 0;
  sda = 1;
  scl = 1;
  _nop_();
  sda = 0;
  _nop_();
}
 
void IIC_Stop()
{
  scl = 0;
  sda = 0;
  scl = 1;
  _nop_();
  sda = 1;
  _nop_();
}
 
char IIC_ACK()
{
  char flag;
  sda = 1;//就在时钟脉冲9期间释放数据线
  _nop_();
  scl = 1;
  _nop_();
  flag = sda;
  _nop_();
  scl = 0;
  _nop_();
  
  return flag;
}
 
void IIC_Send_Byte(char dataSend)
{
  int i;
  
  for(i = 0;i<8;i++){
    scl = 0;//scl拉低,让sda做好数据准备
    sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
    _nop_();//发送数据建立时间
    scl = 1;//scl拉高开始发送
    _nop_();//数据发送时间
    scl = 0;//发送完毕拉低
    _nop_();//
    dataSend = dataSend << 1;
  }
}
 
void Oled_Write_Cmd(char dataCmd)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x00);
  //  5. ACK
  IIC_ACK();
  //6. 写入指令/数据
  IIC_Send_Byte(dataCmd);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
void Oled_Write_Data(char dataData)
{
  //  1. start()
  IIC_Start();
  //    
  //  2. 写入从机地址  b0111 1000 0x78
  IIC_Send_Byte(0x78);
  //  3. ACK
  IIC_ACK();
  //  4. cotrol byte: (0)(0)000000 写入命令   (0)(1)000000写入数据
  IIC_Send_Byte(0x40);
  //  5. ACK
  IIC_ACK();
  ///6. 写入指令/数据
  IIC_Send_Byte(dataData);
  //7. ACK
  IIC_ACK();
  //8. STOP
  IIC_Stop();
}
 
 
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_Clear()
{
  unsigned char i,j; //-128 --- 127
  
  for(i=0;i<8;i++){
    Oled_Write_Cmd(0xB0 + i);//page0--page7
    //每个page从0列
    Oled_Write_Cmd(0x00);
    Oled_Write_Cmd(0x10);
    //0到127列,依次写入0,每写入数据,列地址自动偏移
    for(j = 0;j<128;j++){
      Oled_Write_Data(0);
    }
  }
}
 
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; 
  }   
}
 
 
 
 
相关实践学习
一键创建和部署高分电影推荐语音技能
本场景使用天猫精灵技能应用平台提供的技能模板,在2-5分钟内,创建一个好玩的高分电影推荐技能,使用模板后无须代码开发,系统自动配置意图、实体等,新手0基础也可体验创建技能的乐趣。
达摩院智能语音交互 - 声纹识别技术
声纹识别是基于每个发音人的发音器官构造不同,识别当前发音人的身份。按照任务具体分为两种: 声纹辨认:从说话人集合中判别出测试语音所属的说话人,为多选一的问题 声纹确认:判断测试语音是否由目标说话人所说,是二选一的问题(是或者不是) 按照应用具体分为两种: 文本相关:要求使用者重复指定的话语,通常包含与训练信息相同的文本(精度较高,适合当前应用模式) 文本无关:对使用者发音内容和语言没有要求,受信道环境影响比较大,精度不高 本课程主要介绍声纹识别的原型技术、系统架构及应用案例等。 讲师介绍: 郑斯奇,达摩院算法专家,毕业于美国哈佛大学,研究方向包括声纹识别、性别、年龄、语种识别等。致力于推动端侧声纹与个性化技术的研究和大规模应用。
相关文章
|
1月前
|
物联网 程序员 语音技术
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3
|
1月前
|
传感器 语音技术
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-1
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-1
|
1月前
|
传感器
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
|
1月前
|
编译器 C语言 开发者
单片机原理与应用:探索微型计算机世界
单片机原理与应用:探索微型计算机世界
25 1
|
1月前
|
数据采集 数据处理 C语言
单片机:探索其原理、应用与编程实践
单片机:探索其原理、应用与编程实践
27 1
|
1月前
|
物联网
STC51单片机-实验开发装置仿真-物联网应用系统设计
STC51单片机-实验开发装置仿真-物联网应用系统设计
71 0
|
1月前
|
物联网
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
49 0
|
1月前
|
芯片
AT89S52单片机的最小应用系统
AT89S52单片机的最小应用系统
19 0