当前位置:   article > 正文

51单片机智能小车——语音切换循迹避障跟随功能并跟随OLED显示_基于51单片机的智能语音循迹避障小车

基于51单片机的智能语音循迹避障小车

  学完51单片机之后,智能小车是一个很好的练手项目,本篇文章介绍的小车具有3个功能,循迹,摇头避障,跟随这三个功能,使用SU-03T语音模块切换这三个功能。

小车电机驱动

先把小车组装完成后,我们需要驱动电机,使用L9110s。L9110S是一款直流电机驱动电路,该产品为电池供电的玩具、低压或电池供电的控制应用提供了一种集成直流马达驱动的解决方案。电路内部集成了采用MOS管设计的H桥驱动电路,主要应用于驱动通用直流电机。

 

 

motor.c代码

  1. #include "reg52.h"
  2. sbit RightCon1A = P3^7;
  3. sbit RightCon1B = P3^3;
  4. sbit LeftCon1A = P3^4;
  5. sbit LeftCon1B = P3^5;
  6. void goForward()//前进
  7. {
  8. LeftCon1A = 0;
  9. LeftCon1B = 1;
  10. RightCon1A = 0;
  11. RightCon1B = 1;
  12. }
  13. void goRight()//右转
  14. {
  15. LeftCon1A = 0;
  16. LeftCon1B = 1;
  17. RightCon1A = 0;
  18. RightCon1B = 0;
  19. }
  20. void goLeft()//左转
  21. {
  22. LeftCon1A = 0;
  23. LeftCon1B = 0;
  24. RightCon1A = 0;
  25. RightCon1B = 1;
  26. }
  27. void goBack()//后退
  28. {
  29. LeftCon1A = 1;
  30. LeftCon1B = 0;
  31. RightCon1A = 1;
  32. RightCon1B = 0;
  33. }
  34. void stop()//停止
  35. {
  36. LeftCon1A = 0;
  37. LeftCon1B = 0;
  38. RightCon1A = 0;
  39. RightCon1B = 0;
  40. }

循迹模块

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

 接线方式 VCC:接电源正极(3-5V) GND:接电源负极 DO:TTL开关信号输出0、1 AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接) 6.3.2 循迹小车原理 由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致 循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED 常亮。循迹模块安装在小车车头两侧 下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走 上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转。

跟随模块

原理和寻线是一样的,寻线红外观朝下,跟随朝前 6.4.2 跟随小车的原理 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转。

超声波避障模块

超声波避障我们需要两个原件,舵机sg90,HC-SR04。

sg90舵机

接线方式 

红色VCC;灰色GND,黄色PWM信号。

控制舵机

向黄色信号线“灌入”PWM信号。 PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右 数据:

0.5ms-------------0度; 2.5% 对应函数中占空比为250

1.0ms------------45度; 5.0% 对应函数中占空比为500

1.5ms------------90度; 7.5% 对应函数中占空比为750

2.0ms-----------135度; 10.0% 对应函数中占空比为1000

2.5ms-----------180度; 12.5% 对应函数中占空比为1250

定时器需要定时20ms, 关心的单位0.5ms, 40个的0.5ms,初值0.5m cnt++ 1s = 10ms * 100 20ms = 0.5ms * 40。

sg90.c

  1. #include "reg52.h"
  2. #include "delay.h"
  3. sbit sg90_con = P1^1;
  4. int jd;
  5. int cnt = 0;
  6. void Time0Init()
  7. {
  8. //1. 配置定时器0工作模式位16位计时
  9. TMOD &= 0xF0; //设置定时器模式
  10. TMOD |= 0x01;
  11. //2. 给初值,定一个0.5出来
  12. TL0=0x33;
  13. TH0=0xFE;
  14. //3. 开始计时
  15. TR0 = 1;
  16. TF0 = 0;
  17. //4. 打开定时器0中断
  18. ET0 = 1;
  19. //5. 打开总中断EA
  20. EA = 1;
  21. }
  22. void sgMiddle()
  23. {
  24. //中间位置
  25. jd = 3; //901.5ms高电平
  26. cnt = 0;
  27. }
  28. void sgLeft()
  29. {
  30. //左边位置
  31. jd = 5; //1351.5ms高电平
  32. cnt = 0;
  33. }
  34. void sgRight()
  35. {
  36. //右边位置
  37. jd = 1; //0
  38. cnt = 0;
  39. }
  40. void Time0Handler() interrupt 1
  41. {
  42. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  43. //重新给初值
  44. TL0=0x33;
  45. TH0=0xFE;
  46. //控制PWM波
  47. if(cnt < jd){
  48. sg90_con = 1;
  49. }else{
  50. sg90_con = 0;
  51. }
  52. if(cnt == 40){//爆表40次,经过了20ms
  53. cnt = 0; //100次表示1s,重新让cnt从0开始,计算下一次的1s
  54. sg90_con = 1;
  55. }
  56. }

3. 超声波测距

3.1 简介 型号:HC-SR04

超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。

 

怎么让它发送波?

给Trig端口至少10us的高电平.

怎么知道它开始发了 Echo信号?

由低电平跳转到高电平,表示开始发送波

怎么知道接收了返回波 Echo?

由高电平跳转回低电平,表示波回来了

怎么算时间 Echo引脚维持高电平的时间!

波发出去的那一下,开始启动定时器 波回来的那一下,我们开始停止定时器,计算出中间经过多少时间

怎么算距离 距离 = 速度 (340m/s)* 时间/2

hc04.c

  1. #include "reg52.h"
  2. #include "delay.h"
  3. sbit Trig = P2^3;
  4. sbit Echo = P2^2;
  5. void Time1Init()
  6. {
  7. TMOD &= 0x0F; //设置定时器模式
  8. TMOD |= 0x10;
  9. TH1 = 0;
  10. TL1 = 0;
  11. //设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
  12. }
  13. void startHC()
  14. {
  15. Trig = 0;
  16. Trig = 1;
  17. Delay10us();
  18. Trig = 0;
  19. }
  20. double get_distance()
  21. {
  22. double time;
  23. //定时器数据清零,以便下一次测距
  24. TH1 = 0;
  25. TL1 = 0;
  26. //1. Trig ,给Trig端口至少10us的高电平
  27. startHC();
  28. //2. echo由低电平跳转到高电平,表示开始发送波
  29. while(Echo == 0);
  30. //波发出去的那一下,开始启动定时器
  31. TR1 = 1;
  32. //3. 由高电平跳转回低电平,表示波回来了
  33. while(Echo == 1);
  34. //波回来的那一下,我们开始停止定时器
  35. TR1 = 0;
  36. //4. 计算出中间经过多少时间
  37. time = (TH1 * 256 + TL1)*1.085;//us为单位
  38. //5. 距离 = 速度 (340m/s)* 时间/2
  39. return (time * 0.017);
  40. }

OLED模块

oled用来显示当前模式,比如现在我们处于避障模式,我们就在OLED 屏幕上显示---bizhang--

oled.c

  1. #include "reg52.h"
  2. #include "intrins.h"
  3. #include "Oledfont.h"
  4. sbit scl = P1^2;
  5. sbit sda = P1^3;
  6. void IIC_Start()
  7. {
  8. scl = 0;
  9. sda = 1;
  10. scl = 1;
  11. _nop_();
  12. sda = 0;
  13. _nop_();
  14. }
  15. void IIC_Stop()
  16. {
  17. scl = 0;
  18. sda = 0;
  19. scl = 1;
  20. _nop_();
  21. sda = 1;
  22. _nop_();
  23. }
  24. char IIC_ACK()
  25. {
  26. char flag;
  27. sda = 1;//就在时钟脉冲9期间释放数据线
  28. _nop_();
  29. scl = 1;
  30. _nop_();
  31. flag = sda;
  32. _nop_();
  33. scl = 0;
  34. _nop_();
  35. return flag;
  36. }
  37. void IIC_Send_Byte(char dataSend)
  38. {
  39. int i;
  40. for(i = 0;i<8;i++){
  41. scl = 0;//scl拉低,让sda做好数据准备
  42. sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
  43. _nop_();//发送数据建立时间
  44. scl = 1;//scl拉高开始发送
  45. _nop_();//数据发送时间
  46. scl = 0;//发送完毕拉低
  47. _nop_();//
  48. dataSend = dataSend << 1;
  49. }
  50. }
  51. void Oled_Write_Cmd(char dataCmd)
  52. {
  53. // 1. start()
  54. IIC_Start();
  55. //
  56. // 2. 写入从机地址 b0111 1000 0x78
  57. IIC_Send_Byte(0x78);
  58. // 3. ACK
  59. IIC_ACK();
  60. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  61. IIC_Send_Byte(0x00);
  62. // 5. ACK
  63. IIC_ACK();
  64. //6. 写入指令/数据
  65. IIC_Send_Byte(dataCmd);
  66. //7. ACK
  67. IIC_ACK();
  68. //8. STOP
  69. IIC_Stop();
  70. }
  71. void Oled_Write_Data(char dataData)
  72. {
  73. // 1. start()
  74. IIC_Start();
  75. //
  76. // 2. 写入从机地址 b0111 1000 0x78
  77. IIC_Send_Byte(0x78);
  78. // 3. ACK
  79. IIC_ACK();
  80. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  81. IIC_Send_Byte(0x40);
  82. // 5. ACK
  83. IIC_ACK();
  84. ///6. 写入指令/数据
  85. IIC_Send_Byte(dataData);
  86. //7. ACK
  87. IIC_ACK();
  88. //8. STOP
  89. IIC_Stop();
  90. }
  91. void Oled_Init(void){
  92. Oled_Write_Cmd(0xAE);//--display off
  93. Oled_Write_Cmd(0x00);//---set low column address
  94. Oled_Write_Cmd(0x10);//---set high column address
  95. Oled_Write_Cmd(0x40);//--set start line address
  96. Oled_Write_Cmd(0xB0);//--set page address
  97. Oled_Write_Cmd(0x81); // contract control
  98. Oled_Write_Cmd(0xFF);//--128
  99. Oled_Write_Cmd(0xA1);//set segment remap
  100. Oled_Write_Cmd(0xA6);//--normal / reverse
  101. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  102. Oled_Write_Cmd(0x3F);//--1/32 duty
  103. Oled_Write_Cmd(0xC8);//Com scan direction
  104. Oled_Write_Cmd(0xD3);//-set display offset
  105. Oled_Write_Cmd(0x00);//
  106. Oled_Write_Cmd(0xD5);//set osc division
  107. Oled_Write_Cmd(0x80);//
  108. Oled_Write_Cmd(0xD8);//set area color mode off
  109. Oled_Write_Cmd(0x05);//
  110. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  111. Oled_Write_Cmd(0xF1);//
  112. Oled_Write_Cmd(0xDA);//set com pin configuartion
  113. Oled_Write_Cmd(0x12);//
  114. Oled_Write_Cmd(0xDB);//set Vcomh
  115. Oled_Write_Cmd(0x30);//
  116. Oled_Write_Cmd(0x8D);//set charge pump enable
  117. Oled_Write_Cmd(0x14);//
  118. Oled_Write_Cmd(0xAF);//--turn on oled panel
  119. }
  120. void Oled_Clear()
  121. {
  122. unsigned char i,j; //-128 --- 127
  123. for(i=0;i<8;i++){
  124. Oled_Write_Cmd(0xB0 + i);//page0--page7
  125. //每个page0
  126. Oled_Write_Cmd(0x00);
  127. Oled_Write_Cmd(0x10);
  128. //0127列,依次写入0,每写入数据,列地址自动偏移
  129. for(j = 0;j<128;j++){
  130. Oled_Write_Data(0);
  131. }
  132. }
  133. }
  134. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
  135. unsigned int i;
  136. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
  137. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  138. Oled_Write_Cmd(0x10+(col>>4)); //high
  139. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
  140. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  141. }
  142. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
  143. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  144. Oled_Write_Cmd(0x10+(col>>4)); //high
  145. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
  146. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  147. }
  148. }
  149. /******************************************************************************/
  150. // 函数名称:Oled_Show_Char
  151. // 输入参数:oledChar
  152. // 输出参数:无
  153. // 函数功能:OLED显示单个字符
  154. /******************************************************************************/
  155. void Oled_Show_Str(char row,char col,char *str){
  156. while(*str!=0){
  157. Oled_Show_Char(row,col,*str);
  158. str++;
  159. col += 8;
  160. }
  161. }

语音模块

语音模块使用的SU-03T

 SU-03T接5根线,VCC,GND接电源即可。另外三根线选择A25,A26,A27。 SU-03T通过厂家给的网站配置后即可使用,通过配置后,开发思路是我们说出“循迹”这两个字之后,电平的情况是:A25 == 0  A26 == 1 A27 == 1,说出跟随两个字之后,电平的情况是A25 == 1  A26 == 0  A27 == 1

说出避障两个字之后,A25 == 1  A26 == 1  A27 == 0。

下面将所有代码整合

delay.c

  1. #include "intrins.h"
  2. void Delay2000ms() //@11.0592MHz
  3. {
  4. unsigned char i, j, k;
  5. i = 15;
  6. j = 2;
  7. k = 235;
  8. do
  9. {
  10. do
  11. {
  12. while (--k);
  13. } while (--j);
  14. } while (--i);
  15. }
  16. void Delay10us() //@11.0592MHz
  17. {
  18. unsigned char i;
  19. i = 2;
  20. while (--i);
  21. }
  22. void Delay300ms() //@11.0592MHz
  23. {
  24. unsigned char i, j, k;
  25. _nop_();
  26. i = 3;
  27. j = 26;
  28. k = 223;
  29. do
  30. {
  31. do
  32. {
  33. while (--k);
  34. } while (--j);
  35. } while (--i);
  36. }
  37. void Delay150ms() //@11.0592MHz
  38. {
  39. unsigned char i, j, k;
  40. i = 2;
  41. j = 13;
  42. k = 237;
  43. do
  44. {
  45. do
  46. {
  47. while (--k);
  48. } while (--j);
  49. } while (--i);
  50. }
  51. void Delay450ms() //@11.0592MHz
  52. {
  53. unsigned char i, j, k;
  54. _nop_();
  55. i = 4;
  56. j = 39;
  57. k = 209;
  58. do
  59. {
  60. do
  61. {
  62. while (--k);
  63. } while (--j);
  64. } while (--i);
  65. }

main.c

  1. #include "reg52.h"
  2. #include "hc04.h"
  3. #include "delay.h"
  4. #include "sg90.h"
  5. #include "Oled.h"
  6. #include "motor.h"
  7. #define MIDDLE 0
  8. #define LEFT 1
  9. #define RIGHT 2
  10. #define BZ 1
  11. #define XJ 2
  12. #define GS 3
  13. sbit A25 = P1^5;
  14. sbit A26 = P1^6;
  15. sbit A27 = P1^7;
  16. sbit leftSensorX = P2^7;
  17. sbit rightSensorX = P2^6;
  18. sbit leftSensorG = P2^5;
  19. sbit rightSensorG = P2^4;
  20. char dir;
  21. double disMiddle;
  22. double disLeft;
  23. double disRight;
  24. void xunjiMode()
  25. {
  26. if(leftSensorX == 0 && rightSensorX == 0){
  27. goForward();
  28. }
  29. if(leftSensorX == 1 && rightSensorX == 0){
  30. goLeft();
  31. }
  32. if(leftSensorX == 0 && rightSensorX == 1){
  33. goRight();
  34. }
  35. if(leftSensorX == 1 && rightSensorX == 1){
  36. //
  37. stop();
  38. }
  39. }
  40. void gensuiMode()
  41. {
  42. if(leftSensorG == 0 && rightSensorG == 0){
  43. goForward();
  44. }
  45. if(leftSensorG == 1 && rightSensorG == 0){
  46. goRight();
  47. }
  48. if(leftSensorG == 0 && rightSensorG == 1){
  49. goLeft();
  50. }
  51. if(leftSensorG == 1 && rightSensorG == 1){
  52. //
  53. stop();
  54. }
  55. }
  56. void bizhangMode()
  57. {
  58. if(dir != MIDDLE){
  59. sgMiddle();
  60. dir = MIDDLE;
  61. Delay300ms();
  62. }
  63. disMiddle = get_distance();
  64. if(disMiddle > 35){
  65. //前进
  66. goForward();
  67. }else if(disMiddle < 10){
  68. goBack();
  69. }else
  70. {
  71. //停止
  72. stop();
  73. //测左边距离
  74. sgLeft();
  75. Delay300ms();
  76. disLeft = get_distance();
  77. sgMiddle();
  78. Delay300ms();
  79. sgRight();
  80. dir = RIGHT;
  81. Delay300ms();
  82. disRight = get_distance();
  83. if(disLeft < disRight){
  84. goRight();
  85. Delay150ms();
  86. stop();
  87. }
  88. if(disRight < disLeft){
  89. goLeft();
  90. Delay150ms();
  91. stop();
  92. }
  93. }
  94. }
  95. void main()
  96. {
  97. int mark = 0;
  98. Time0Init();
  99. Time1Init();
  100. //舵机的初始位置
  101. sgMiddle();
  102. Delay300ms();
  103. Delay300ms();
  104. dir = MIDDLE;
  105. Oled_Init();
  106. Oled_Clear();
  107. Oled_Show_Str(2,2,"-----Ready----");
  108. while(1){
  109. //满足寻迹模式的条件
  110. if(A25 == 0 && A26 == 1 && A27 == 1){
  111. if(mark != XJ){
  112. Oled_Clear();
  113. Oled_Show_Str(2,2,"-----XunJi----");
  114. }
  115. mark = XJ;
  116. xunjiMode();
  117. }
  118. //满足跟随模式的条件
  119. if(A25 == 1 && A26 == 0 && A27 == 1){
  120. if(mark != GS){
  121. Oled_Clear();
  122. Oled_Show_Str(2,2,"-----GenSui----");
  123. }
  124. mark = GS;
  125. gensuiMode();
  126. }
  127. //满足避障模式的条件
  128. if(A25 == 1 && A26 == 1 && A27 == 0){
  129. if(mark != BZ){
  130. Oled_Clear();
  131. Oled_Show_Str(2,2,"-----BiZhang----");
  132. }
  133. mark = BZ;
  134. bizhangMode();
  135. }
  136. }
  137. }

最后,语音切换循迹避障跟随功能并跟随OLED显示小车就完成了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/539922
推荐阅读
相关标签
  

闽ICP备14008679号