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

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

1.电机模块开发

L9110s概述

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

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

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

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

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

1.1 让小车动起来

核心代码:

#include "reg52.h"
#include "intrins.h"
 
sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
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 goForward()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
  
  RightCon1A = 0;
  RightCon1B = 1;
}
 
void goRight()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
  
  RightCon1A = 0;
  RightCon1B = 0;
}
 
void goBack()
{
  LeftCon1A = 1;
  LeftCon1B = 0;
  
  RightCon1A = 1;
  RightCon1B = 0;
}
 
void main()
{
  while(1){
    goForward();
    Delay1000ms();
    Delay1000ms();
    goBack();
    Delay1000ms();
    Delay1000ms();
    goLeft();
    Delay1000ms();
    Delay1000ms();
    goRight();
    Delay1000ms();
    Delay1000ms();
  }
}
 
 
1.2 串口控制小车方向
  • 串口分文件编程进行代码整合——通过现象来改代码
  • 接入蓝牙模块,通过蓝牙控制小车
  • 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能
1.3 如何进行小车PWM调速

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


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


核心代码:

#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"
 
extern char speed;
 
void main()
{
  Time0Init();
  //UartInit();
  
  while(1){
    speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
    Delay1000ms();
    Delay1000ms();
    speed = 20;
    Delay1000ms();
    Delay1000ms();
    speed = 40;
    Delay1000ms();
    Delay1000ms();
  }
}
 
 
//time.c
#include "motor.h"
#include "reg52.h"
 
char speed;
char 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;
  
  //控制PWM波
  if(cnt < speed){
    //前进
    goForward();
  }else{
    //停止
    stop();
  }
  
  if(cnt == 40){//爆表40次,经过了20ms
    cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  }
    
}
1.4 PWM方式实现小车转向

原理: 左轮定时器0调速,右轮定时器1调速,那么左转就是右轮速度大于左轮!

核心代码:

#include "motor.h"
#include "reg52.h"
 
char speedLeft;
char cntLeft = 0;
 
char speedRight;
char cntRight = 0;
 
void Time1Init()
{
  //1. 配置定时器1工作模式位16位计时
  TMOD &= 0x0F;
  TMOD |= 0x1 << 4;
  //2. 给初值,定一个0.5出来
  TL1=0x33;
  TH1=0xFE;
  //3. 开始计时
  TR1 = 1;
  TF1 = 0;
  //4. 打开定时器1中断
  ET1 = 1;
  //5. 打开总中断EA
  EA = 1;
}
 
 
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 Time1Handler() interrupt 3
{
  cntRight++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL1=0x33;
  TH1=0xFE;
  
  //控制PWM波
  if(cntRight < speedRight){
    //右前进
    goForwardRight();
  }else{
    //停止
    stopRight();
  }
  
  if(cntRight == 40){//爆表40次,经过了20ms
    cntRight = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  }
    
}
 
void Time0Handler() interrupt 1
{
  cntLeft++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL0=0x33;
  TH0=0xFE;
  
  //控制PWM波
  if(cntLeft < speedLeft){
    //左前进
    goForwardLeft();
  }else{
    //停止
    stopLeft();
  }
  
  if(cntLeft == 40){//爆表40次,经过了20ms
    cntLeft = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  }
    
}

2.循迹小车

2.1 循迹模块使用

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

接线方式

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

2.2 循迹小车原理

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

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

2.3 循迹小车核心代码
//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"
#include "reg52.h"
extern char speedLeft;
extern char speedRight;
 
 
sbit leftSensor = P2^7;
sbit rightSensor = P2^6;
 
void main()
{
  Time0Init();
  Time1Init();
  //UartInit();
  
  while(1){
    
    if(leftSensor == 0 && rightSensor == 0){
        speedLeft = 32;
        speedRight = 40;
    }
    if(leftSensor == 1 && rightSensor == 0){
        speedLeft = 12;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
        speedRight = 40;
    }
    
    if(leftSensor == 0 && rightSensor == 1){
        speedLeft = 32;
        speedRight = 20;
    }
    
    if(leftSensor == 1 && rightSensor == 1){
      //停
        speedLeft = 0;
        speedRight = 0;
    }
  }
}
 
//motor.c
#include "reg52.h"
 
sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;
 
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;
 
void goForwardLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 1;
}
 
void stopLeft()
{
  LeftCon1A = 0;
  LeftCon1B = 0;
}
 
void goForwardRight()
{
  RightCon1A = 0;
  RightCon1B = 1;
}
void stopRight()
{
  RightCon1A = 0;
  RightCon1B = 0;
}
 
 
 
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 Delay1000ms()    //@11.0592MHz
{
  unsigned char i, j, k;
 
  _nop_();
  i = 8;
  j = 1;
  k = 243;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
 
 
//time.c
#include "motor.h"
#include "reg52.h"
 
char speedLeft;
char cntLeft = 0;
 
char speedRight;
char cntRight = 0;
 
void Time1Init()
{
  //1. 配置定时器1工作模式位16位计时
  TMOD &= 0x0F;
  TMOD |= 0x1 << 4;
  //2. 给初值,定一个0.5出来
  TL1=0x33;
  TH1=0xFE;
  //3. 开始计时
  TR1 = 1;
  TF1 = 0;
  //4. 打开定时器1中断
  ET1 = 1;
  //5. 打开总中断EA
  EA = 1;
}
 
 
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 Time1Handler() interrupt 3
{
  cntRight++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL1=0x33;
  TH1=0xFE;
  
  //控制PWM波
  if(cntRight < speedRight){
    //右前进
    goForwardRight();
  }else{
    //停止
    stopRight();
  }
  
  if(cntRight == 40){//爆表40次,经过了20ms
    cntRight = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  }
    
}
 
void Time0Handler() interrupt 1
{
  cntLeft++;  //统计爆表的次数. cnt=1的时候,报表了1
  //重新给初值
  TL0=0x33;
  TH0=0xFE;
  
  //控制PWM波
  if(cntLeft < speedLeft){
    //左前进
    goForwardLeft();
  }else{
    //停止
    stopLeft();
  }
  
  if(cntLeft == 40){//爆表40次,经过了20ms
    cntLeft = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  }
    
}
 

3.跟随/避障小车

3.1 红外壁障模块分析

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

3.2 跟随小车的原理
  • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
  • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
3.3 跟随小车开发和调试代码
//main.c
#include "motor.h"
#include "delay.h"
#include "reg52.h"
 
//sbit leftSensor = P2^7;
//sbit rightSensor = P2^6;
 
sbit leftSensor = P2^5;
sbit rightSensor = P2^4;
 
void main()
{
 
  while(1){
    if(leftSensor == 0 && rightSensor == 0){
      goForward();
    }
    if(leftSensor == 1 && rightSensor == 0){
      goRight();
    }
    
    if(leftSensor == 0 && rightSensor == 1){
      
      goLeft();
    }
    
    if(leftSensor == 1 && rightSensor == 1){
      //停
      stop();
    }
  }
}
 
 
//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 Delay1000ms()    //@11.0592MHz
{
  unsigned char i, j, k;
 
  _nop_();
  i = 8;
  j = 1;
  k = 243;
  do
  {
    do
    {
      while (--k);
    } while (--j);
  } while (--i);
}
 
 
 
3.4 超声波模块介绍

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

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

时序图:

3.5 摇头测距小车开发和调试代码
//main.c
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"
#include "motor.h"
 
#define MIDDLE 0
#define LEFT 1
#define RIGHT 2
 
void main()
{
  char dir;
  
  double disMiddle;
  double disLeft;
  double disRight;
  
  Time0Init();
  Time1Init();
  //舵机的初始位置
 
  sgMiddle();
  Delay300ms();
  Delay300ms();
  dir = MIDDLE;
  
  while(1){
    
    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();
      }
    }
    
  }
}
 
//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^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;
}
 
 
 
 
 
 

4.测速小车

4.1 测速模块

  • 用途:广泛用于电机转速检测,脉冲计数,位置限位等。
  • 有遮挡,输出高电平;无遮挡,输出低电平
  • 接线 :VCC 接电源正极3.3-5V
  • GND 接电源负极 DO TTL开关信号输出
  • AO 此模块不起作用


4.2 测试原理和单位换算

  • 轮子走一圈,经过一个周长,C = 2x3.14x半径= 3.14 x 直径(6.5cm)
  • 对应的码盘也转了一圈,码盘有20个格子,每经过一个格子,会遮挡(高电平)和不遮挡(低电平), 那么一个脉冲就是走了 3.14 * 6.5 cm /20 = 1.0205CM
  • 定时器可以设计成一秒,统计脉冲数,一个脉冲就是1cm
  • 假设一秒有80脉冲,那么就是80cm/s
4.3 定时器和中断实现测速开发和调试代码

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

//main.c
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "reg52.h"
#include "time.h"
#include "stdio.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();
  
  while(1){
    if(signal){//定时器1s到点,把signal置一,主程序发送速度
      sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
      SendString(speedMes);//速度发出去
      signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
    }
  }
}
 
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的速度,格子清零
  }
    
}
4.4 小车速度显示在OLED屏

使用oled模块

//main.c
#include "reg52.h"
#include "intrins.h"
#include "Oled.h"
 
void main()
{
    //1. OLED初始化
    Oled_Init();
    Oled_Clear();
    Oled_Show_Str(2,2,"speed:35cm/s");
  
    while(1);
}
 
//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; 
  }   
}


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

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

相关实践学习
一键创建和部署高分电影推荐语音技能
本场景使用天猫精灵技能应用平台提供的技能模板,在2-5分钟内,创建一个好玩的高分电影推荐技能,使用模板后无须代码开发,系统自动配置意图、实体等,新手0基础也可体验创建技能的乐趣。
达摩院智能语音交互 - 声纹识别技术
声纹识别是基于每个发音人的发音器官构造不同,识别当前发音人的身份。按照任务具体分为两种: 声纹辨认:从说话人集合中判别出测试语音所属的说话人,为多选一的问题 声纹确认:判断测试语音是否由目标说话人所说,是二选一的问题(是或者不是) 按照应用具体分为两种: 文本相关:要求使用者重复指定的话语,通常包含与训练信息相同的文本(精度较高,适合当前应用模式) 文本无关:对使用者发音内容和语言没有要求,受信道环境影响比较大,精度不高 本课程主要介绍声纹识别的原型技术、系统架构及应用案例等。 讲师介绍: 郑斯奇,达摩院算法专家,毕业于美国哈佛大学,研究方向包括声纹识别、性别、年龄、语种识别等。致力于推动端侧声纹与个性化技术的研究和大规模应用。
相关文章
|
1月前
|
传感器
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
|
1月前
|
语音技术 物联网 程序员
51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2
51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2
51单片机智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结-2
|
1月前
|
编译器 C语言 开发者
单片机原理与应用:探索微型计算机世界
单片机原理与应用:探索微型计算机世界
28 1
|
1月前
|
数据采集 数据处理 C语言
单片机:探索其原理、应用与编程实践
单片机:探索其原理、应用与编程实践
29 1
|
1月前
|
物联网
STC51单片机-实验开发装置仿真-物联网应用系统设计
STC51单片机-实验开发装置仿真-物联网应用系统设计
75 0
|
1月前
|
物联网
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
51 0
|
1月前
|
传感器 人工智能 物联网
单片机:编程实践与应用探索
单片机:编程实践与应用探索
28 4
|
1月前
|
存储 芯片
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P11】(第十一章:15题速过串行口的工作原理和应用)经典例题盘点(带图解析)

热门文章

最新文章