当前位置:   article > 正文

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

智能小车可以用51单片机吗

前言

有需要帮忙代做51和32小车或者其他单片机项目,课程设计,报告,PCB原理图的小伙伴,可以在文章最下方加我V交流咨询!!!

目录

1.电机模块开发

1.1 让小车动起来

1.2 串口控制小车方向

1.3 如何进行小车PWM调速

1.4 PWM方式实现小车转向

2.循迹小车 

2.1 循迹模块使用

2.2 循迹小车原理

2.3 循迹小车核心代码

3.跟随/避障小车

3.1 红外壁障模块分析​编辑

3.2 跟随小车的原理

3.3 跟随小车开发和调试代码

3.4 超声波模块介绍

3.5 摇头测距小车开发和调试代码

4.测速小车

4.1 测速模块

4.2 测试原理和单位换算

4.3 定时器和中断实现测速开发和调试代码

4.4 小车速度显示在OLED屏

5.远程控制小车

5.1 蓝牙控制小车

5.2 蓝牙控制并测速小车

5.3 wifi控制测速小车

5.4 4g控制小车

6.语音控制小车

6.1语音模块配置:

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


1.电机模块开发

L9110s概述

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

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

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

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

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

1.1 让小车动起来

核心代码:

  1. #include "reg52.h"
  2. #include "intrins.h"
  3. sbit RightCon1A = P3^2;
  4. sbit RightCon1B = P3^3;
  5. sbit LeftCon1A = P3^4;
  6. sbit LeftCon1B = P3^5;
  7. void Delay1000ms() //@11.0592MHz
  8. {
  9. unsigned char i, j, k;
  10. _nop_();
  11. i = 8;
  12. j = 1;
  13. k = 243;
  14. do
  15. {
  16. do
  17. {
  18. while (--k);
  19. } while (--j);
  20. } while (--i);
  21. }
  22. void goForward()
  23. {
  24. LeftCon1A = 0;
  25. LeftCon1B = 1;
  26. RightCon1A = 0;
  27. RightCon1B = 1;
  28. }
  29. void goLeft()
  30. {
  31. LeftCon1A = 0;
  32. LeftCon1B = 0;
  33. RightCon1A = 0;
  34. RightCon1B = 1;
  35. }
  36. void goRight()
  37. {
  38. LeftCon1A = 0;
  39. LeftCon1B = 1;
  40. RightCon1A = 0;
  41. RightCon1B = 0;
  42. }
  43. void goBack()
  44. {
  45. LeftCon1A = 1;
  46. LeftCon1B = 0;
  47. RightCon1A = 1;
  48. RightCon1B = 0;
  49. }
  50. void main()
  51. {
  52. while(1){
  53. goForward();
  54. Delay1000ms();
  55. Delay1000ms();
  56. goBack();
  57. Delay1000ms();
  58. Delay1000ms();
  59. goLeft();
  60. Delay1000ms();
  61. Delay1000ms();
  62. goRight();
  63. Delay1000ms();
  64. Delay1000ms();
  65. }
  66. }
1.2 串口控制小车方向
  • 串口分文件编程进行代码整合——通过现象来改代码
  • 接入蓝牙模块,通过蓝牙控制小车
  • 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不 能实现),就能实现前进按键按下后小车一直往前走的功能
1.3 如何进行小车PWM调速

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

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

核心代码:

  1. #include "motor.h"
  2. #include "delay.h"
  3. #include "uart.h"
  4. #include "time.h"
  5. extern char speed;
  6. void main()
  7. {
  8. Time0Init();
  9. //UartInit();
  10. while(1){
  11. speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
  12. Delay1000ms();
  13. Delay1000ms();
  14. speed = 20;
  15. Delay1000ms();
  16. Delay1000ms();
  17. speed = 40;
  18. Delay1000ms();
  19. Delay1000ms();
  20. }
  21. }
  22. //time.c
  23. #include "motor.h"
  24. #include "reg52.h"
  25. char speed;
  26. char cnt = 0;
  27. void Time0Init()
  28. {
  29. //1. 配置定时器0工作模式位16位计时
  30. TMOD = 0x01;
  31. //2. 给初值,定一个0.5出来
  32. TL0=0x33;
  33. TH0=0xFE;
  34. //3. 开始计时
  35. TR0 = 1;
  36. TF0 = 0;
  37. //4. 打开定时器0中断
  38. ET0 = 1;
  39. //5. 打开总中断EA
  40. EA = 1;
  41. }
  42. void Time0Handler() interrupt 1
  43. {
  44. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  45. //重新给初值
  46. TL0=0x33;
  47. TH0=0xFE;
  48. //控制PWM波
  49. if(cnt < speed){
  50. //前进
  51. goForward();
  52. }else{
  53. //停止
  54. stop();
  55. }
  56. if(cnt == 40){//爆表40次,经过了20ms
  57. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  58. }
  59. }
1.4 PWM方式实现小车转向

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

核心代码:

  1. #include "motor.h"
  2. #include "reg52.h"
  3. char speedLeft;
  4. char cntLeft = 0;
  5. char speedRight;
  6. char cntRight = 0;
  7. void Time1Init()
  8. {
  9. //1. 配置定时器1工作模式位16位计时
  10. TMOD &= 0x0F;
  11. TMOD |= 0x1 << 4;
  12. //2. 给初值,定一个0.5出来
  13. TL1=0x33;
  14. TH1=0xFE;
  15. //3. 开始计时
  16. TR1 = 1;
  17. TF1 = 0;
  18. //4. 打开定时器1中断
  19. ET1 = 1;
  20. //5. 打开总中断EA
  21. EA = 1;
  22. }
  23. void Time0Init()
  24. {
  25. //1. 配置定时器0工作模式位16位计时
  26. TMOD = 0x01;
  27. //2. 给初值,定一个0.5出来
  28. TL0=0x33;
  29. TH0=0xFE;
  30. //3. 开始计时
  31. TR0 = 1;
  32. TF0 = 0;
  33. //4. 打开定时器0中断
  34. ET0 = 1;
  35. //5. 打开总中断EA
  36. EA = 1;
  37. }
  38. void Time1Handler() interrupt 3
  39. {
  40. cntRight++; //统计爆表的次数. cnt=1的时候,报表了1
  41. //重新给初值
  42. TL1=0x33;
  43. TH1=0xFE;
  44. //控制PWM波
  45. if(cntRight < speedRight){
  46. //右前进
  47. goForwardRight();
  48. }else{
  49. //停止
  50. stopRight();
  51. }
  52. if(cntRight == 40){//爆表40次,经过了20ms
  53. cntRight = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  54. }
  55. }
  56. void Time0Handler() interrupt 1
  57. {
  58. cntLeft++; //统计爆表的次数. cnt=1的时候,报表了1
  59. //重新给初值
  60. TL0=0x33;
  61. TH0=0xFE;
  62. //控制PWM波
  63. if(cntLeft < speedLeft){
  64. //左前进
  65. goForwardLeft();
  66. }else{
  67. //停止
  68. stopLeft();
  69. }
  70. if(cntLeft == 40){//爆表40次,经过了20ms
  71. cntLeft = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  72. }
  73. }

2.循迹小车 

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

接线方式

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

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

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

2.3 循迹小车核心代码
  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. #include "time.h"
  6. #include "reg52.h"
  7. extern char speedLeft;
  8. extern char speedRight;
  9. sbit leftSensor = P2^7;
  10. sbit rightSensor = P2^6;
  11. void main()
  12. {
  13. Time0Init();
  14. Time1Init();
  15. //UartInit();
  16. while(1){
  17. if(leftSensor == 0 && rightSensor == 0){
  18. speedLeft = 32;
  19. speedRight = 40;
  20. }
  21. if(leftSensor == 1 && rightSensor == 0){
  22. speedLeft = 12;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
  23. speedRight = 40;
  24. }
  25. if(leftSensor == 0 && rightSensor == 1){
  26. speedLeft = 32;
  27. speedRight = 20;
  28. }
  29. if(leftSensor == 1 && rightSensor == 1){
  30. //停
  31. speedLeft = 0;
  32. speedRight = 0;
  33. }
  34. }
  35. }
  36. //motor.c
  37. #include "reg52.h"
  38. sbit RightCon1A = P3^2;
  39. sbit RightCon1B = P3^3;
  40. sbit LeftCon1A = P3^4;
  41. sbit LeftCon1B = P3^5;
  42. void goForwardLeft()
  43. {
  44. LeftCon1A = 0;
  45. LeftCon1B = 1;
  46. }
  47. void stopLeft()
  48. {
  49. LeftCon1A = 0;
  50. LeftCon1B = 0;
  51. }
  52. void goForwardRight()
  53. {
  54. RightCon1A = 0;
  55. RightCon1B = 1;
  56. }
  57. void stopRight()
  58. {
  59. RightCon1A = 0;
  60. RightCon1B = 0;
  61. }
  62. void goForward()
  63. {
  64. LeftCon1A = 0;
  65. LeftCon1B = 1;
  66. RightCon1A = 0;
  67. RightCon1B = 1;
  68. }
  69. void goRight()
  70. {
  71. LeftCon1A = 0;
  72. LeftCon1B = 1;
  73. RightCon1A = 0;
  74. RightCon1B = 0;
  75. }
  76. void goLeft()
  77. {
  78. LeftCon1A = 0;
  79. LeftCon1B = 0;
  80. RightCon1A = 0;
  81. RightCon1B = 1;
  82. }
  83. void goBack()
  84. {
  85. LeftCon1A = 1;
  86. LeftCon1B = 0;
  87. RightCon1A = 1;
  88. RightCon1B = 0;
  89. }
  90. void stop()
  91. {
  92. LeftCon1A = 0;
  93. LeftCon1B = 0;
  94. RightCon1A = 0;
  95. RightCon1B = 0;
  96. }
  97. //delay.c
  98. #include "intrins.h"
  99. void Delay1000ms() //@11.0592MHz
  100. {
  101. unsigned char i, j, k;
  102. _nop_();
  103. i = 8;
  104. j = 1;
  105. k = 243;
  106. do
  107. {
  108. do
  109. {
  110. while (--k);
  111. } while (--j);
  112. } while (--i);
  113. }
  114. //time.c
  115. #include "motor.h"
  116. #include "reg52.h"
  117. char speedLeft;
  118. char cntLeft = 0;
  119. char speedRight;
  120. char cntRight = 0;
  121. void Time1Init()
  122. {
  123. //1. 配置定时器1工作模式位16位计时
  124. TMOD &= 0x0F;
  125. TMOD |= 0x1 << 4;
  126. //2. 给初值,定一个0.5出来
  127. TL1=0x33;
  128. TH1=0xFE;
  129. //3. 开始计时
  130. TR1 = 1;
  131. TF1 = 0;
  132. //4. 打开定时器1中断
  133. ET1 = 1;
  134. //5. 打开总中断EA
  135. EA = 1;
  136. }
  137. void Time0Init()
  138. {
  139. //1. 配置定时器0工作模式位16位计时
  140. TMOD = 0x01;
  141. //2. 给初值,定一个0.5出来
  142. TL0=0x33;
  143. TH0=0xFE;
  144. //3. 开始计时
  145. TR0 = 1;
  146. TF0 = 0;
  147. //4. 打开定时器0中断
  148. ET0 = 1;
  149. //5. 打开总中断EA
  150. EA = 1;
  151. }
  152. void Time1Handler() interrupt 3
  153. {
  154. cntRight++; //统计爆表的次数. cnt=1的时候,报表了1
  155. //重新给初值
  156. TL1=0x33;
  157. TH1=0xFE;
  158. //控制PWM波
  159. if(cntRight < speedRight){
  160. //右前进
  161. goForwardRight();
  162. }else{
  163. //停止
  164. stopRight();
  165. }
  166. if(cntRight == 40){//爆表40次,经过了20ms
  167. cntRight = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  168. }
  169. }
  170. void Time0Handler() interrupt 1
  171. {
  172. cntLeft++; //统计爆表的次数. cnt=1的时候,报表了1
  173. //重新给初值
  174. TL0=0x33;
  175. TH0=0xFE;
  176. //控制PWM波
  177. if(cntLeft < speedLeft){
  178. //左前进
  179. goForwardLeft();
  180. }else{
  181. //停止
  182. stopLeft();
  183. }
  184. if(cntLeft == 40){//爆表40次,经过了20ms
  185. cntLeft = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  186. }
  187. }

3.跟随/避障小车

3.1 红外壁障模块分析

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

3.2 跟随小车的原理
  • 左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
  • 右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
3.3 跟随小车开发和调试代码
  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "reg52.h"
  5. //sbit leftSensor = P2^7;
  6. //sbit rightSensor = P2^6;
  7. sbit leftSensor = P2^5;
  8. sbit rightSensor = P2^4;
  9. void main()
  10. {
  11. while(1){
  12. if(leftSensor == 0 && rightSensor == 0){
  13. goForward();
  14. }
  15. if(leftSensor == 1 && rightSensor == 0){
  16. goRight();
  17. }
  18. if(leftSensor == 0 && rightSensor == 1){
  19. goLeft();
  20. }
  21. if(leftSensor == 1 && rightSensor == 1){
  22. //停
  23. stop();
  24. }
  25. }
  26. }
  27. //motor.c
  28. #include "reg52.h"
  29. sbit RightCon1A = P3^2;
  30. sbit RightCon1B = P3^3;
  31. sbit LeftCon1A = P3^4;
  32. sbit LeftCon1B = P3^5;
  33. void goForward()
  34. {
  35. LeftCon1A = 0;
  36. LeftCon1B = 1;
  37. RightCon1A = 0;
  38. RightCon1B = 1;
  39. }
  40. void goRight()
  41. {
  42. LeftCon1A = 0;
  43. LeftCon1B = 1;
  44. RightCon1A = 0;
  45. RightCon1B = 0;
  46. }
  47. void goLeft()
  48. {
  49. LeftCon1A = 0;
  50. LeftCon1B = 0;
  51. RightCon1A = 0;
  52. RightCon1B = 1;
  53. }
  54. void goBack()
  55. {
  56. LeftCon1A = 1;
  57. LeftCon1B = 0;
  58. RightCon1A = 1;
  59. RightCon1B = 0;
  60. }
  61. void stop()
  62. {
  63. LeftCon1A = 0;
  64. LeftCon1B = 0;
  65. RightCon1A = 0;
  66. RightCon1B = 0;
  67. }
  68. //delay.c
  69. #include "intrins.h"
  70. void Delay1000ms() //@11.0592MHz
  71. {
  72. unsigned char i, j, k;
  73. _nop_();
  74. i = 8;
  75. j = 1;
  76. k = 243;
  77. do
  78. {
  79. do
  80. {
  81. while (--k);
  82. } while (--j);
  83. } while (--i);
  84. }
3.4 超声波模块介绍

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

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

时序图:

3.5 摇头测距小车开发和调试代码
  1. //main.c
  2. #include "reg52.h"
  3. #include "hc04.h"
  4. #include "delay.h"
  5. #include "sg90.h"
  6. #include "motor.h"
  7. #define MIDDLE 0
  8. #define LEFT 1
  9. #define RIGHT 2
  10. void main()
  11. {
  12. char dir;
  13. double disMiddle;
  14. double disLeft;
  15. double disRight;
  16. Time0Init();
  17. Time1Init();
  18. //舵机的初始位置
  19. sgMiddle();
  20. Delay300ms();
  21. Delay300ms();
  22. dir = MIDDLE;
  23. while(1){
  24. if(dir != MIDDLE){
  25. sgMiddle();
  26. dir = MIDDLE;
  27. Delay300ms();
  28. }
  29. disMiddle = get_distance();
  30. if(disMiddle > 35){
  31. //前进
  32. goForward();
  33. }else if(disMiddle < 10){
  34. goBack();
  35. }else
  36. {
  37. //停止
  38. stop();
  39. //测左边距离
  40. sgLeft();
  41. Delay300ms();
  42. disLeft = get_distance();
  43. sgMiddle();
  44. Delay300ms();
  45. sgRight();
  46. dir = RIGHT;
  47. Delay300ms();
  48. disRight = get_distance();
  49. if(disLeft < disRight){
  50. goRight();
  51. Delay150ms();
  52. stop();
  53. }
  54. if(disRight < disLeft){
  55. goLeft();
  56. Delay150ms();
  57. stop();
  58. }
  59. }
  60. }
  61. }
  62. //hc04.c
  63. #include "reg52.h"
  64. #include "delay.h"
  65. sbit Trig = P2^3;
  66. sbit Echo = P2^2;
  67. void Time1Init()
  68. {
  69. TMOD &= 0x0F; //设置定时器模式
  70. TMOD |= 0x10;
  71. TH1 = 0;
  72. TL1 = 0;
  73. //设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
  74. }
  75. void startHC()
  76. {
  77. Trig = 0;
  78. Trig = 1;
  79. Delay10us();
  80. Trig = 0;
  81. }
  82. double get_distance()
  83. {
  84. double time;
  85. //定时器数据清零,以便下一次测距
  86. TH1 = 0;
  87. TL1 = 0;
  88. //1. Trig ,给Trig端口至少10us的高电平
  89. startHC();
  90. //2. echo由低电平跳转到高电平,表示开始发送波
  91. while(Echo == 0);
  92. //波发出去的那一下,开始启动定时器
  93. TR1 = 1;
  94. //3. 由高电平跳转回低电平,表示波回来了
  95. while(Echo == 1);
  96. //波回来的那一下,我们开始停止定时器
  97. TR1 = 0;
  98. //4. 计算出中间经过多少时间
  99. time = (TH1 * 256 + TL1)*1.085;//us为单位
  100. //5. 距离 = 速度 (340m/s)* 时间/2
  101. return (time * 0.017);
  102. }
  103. //delay.c
  104. #include "intrins.h"
  105. void Delay2000ms() //@11.0592MHz
  106. {
  107. unsigned char i, j, k;
  108. i = 15;
  109. j = 2;
  110. k = 235;
  111. do
  112. {
  113. do
  114. {
  115. while (--k);
  116. } while (--j);
  117. } while (--i);
  118. }
  119. void Delay10us() //@11.0592MHz
  120. {
  121. unsigned char i;
  122. i = 2;
  123. while (--i);
  124. }
  125. void Delay300ms() //@11.0592MHz
  126. {
  127. unsigned char i, j, k;
  128. _nop_();
  129. i = 3;
  130. j = 26;
  131. k = 223;
  132. do
  133. {
  134. do
  135. {
  136. while (--k);
  137. } while (--j);
  138. } while (--i);
  139. }
  140. void Delay150ms() //@11.0592MHz
  141. {
  142. unsigned char i, j, k;
  143. i = 2;
  144. j = 13;
  145. k = 237;
  146. do
  147. {
  148. do
  149. {
  150. while (--k);
  151. } while (--j);
  152. } while (--i);
  153. }
  154. void Delay450ms() //@11.0592MHz
  155. {
  156. unsigned char i, j, k;
  157. _nop_();
  158. i = 4;
  159. j = 39;
  160. k = 209;
  161. do
  162. {
  163. do
  164. {
  165. while (--k);
  166. } while (--j);
  167. } while (--i);
  168. }
  169. //sg90.c
  170. #include "reg52.h"
  171. #include "delay.h"
  172. sbit sg90_con = P1^1;
  173. int jd;
  174. int cnt = 0;
  175. void Time0Init()
  176. {
  177. //1. 配置定时器0工作模式位16位计时
  178. TMOD &= 0xF0; //设置定时器模式
  179. TMOD |= 0x01;
  180. //2. 给初值,定一个0.5出来
  181. TL0=0x33;
  182. TH0=0xFE;
  183. //3. 开始计时
  184. TR0 = 1;
  185. TF0 = 0;
  186. //4. 打开定时器0中断
  187. ET0 = 1;
  188. //5. 打开总中断EA
  189. EA = 1;
  190. }
  191. void sgMiddle()
  192. {
  193. //中间位置
  194. jd = 3; //90度 1.5ms高电平
  195. cnt = 0;
  196. }
  197. void sgLeft()
  198. {
  199. //左边位置
  200. jd = 5; //135度 1.5ms高电平
  201. cnt = 0;
  202. }
  203. void sgRight()
  204. {
  205. //右边位置
  206. jd = 1; //0度
  207. cnt = 0;
  208. }
  209. void Time0Handler() interrupt 1
  210. {
  211. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  212. //重新给初值
  213. TL0=0x33;
  214. TH0=0xFE;
  215. //控制PWM波
  216. if(cnt < jd){
  217. sg90_con = 1;
  218. }else{
  219. sg90_con = 0;
  220. }
  221. if(cnt == 40){//爆表40次,经过了20ms
  222. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  223. sg90_con = 1;
  224. }
  225. }
  226. //motor.c
  227. #include "reg52.h"
  228. sbit RightCon1A = P3^2;
  229. sbit RightCon1B = P3^3;
  230. sbit LeftCon1A = P3^4;
  231. sbit LeftCon1B = P3^5;
  232. void goForward()
  233. {
  234. LeftCon1A = 0;
  235. LeftCon1B = 1;
  236. RightCon1A = 0;
  237. RightCon1B = 1;
  238. }
  239. void goRight()
  240. {
  241. LeftCon1A = 0;
  242. LeftCon1B = 1;
  243. RightCon1A = 0;
  244. RightCon1B = 0;
  245. }
  246. void goLeft()
  247. {
  248. LeftCon1A = 0;
  249. LeftCon1B = 0;
  250. RightCon1A = 0;
  251. RightCon1B = 1;
  252. }
  253. void goBack()
  254. {
  255. LeftCon1A = 1;
  256. LeftCon1B = 0;
  257. RightCon1A = 1;
  258. RightCon1B = 0;
  259. }
  260. void stop()
  261. {
  262. LeftCon1A = 0;
  263. LeftCon1B = 0;
  264. RightCon1A = 0;
  265. RightCon1B = 0;
  266. }

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 定时器和中断实现测速开发和调试代码

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

  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. #include "reg52.h"
  6. #include "time.h"
  7. #include "stdio.h"
  8. sbit speedIO = P3^2;//外部中断0
  9. unsigned int speedCnt = 0; //统计格子,脉冲次数
  10. extern unsigned int speed;//速度
  11. extern char signal; //主程序发速度数据的通知
  12. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
  13. void Ex0Init()
  14. {
  15. EX0 = 1;//允许中断
  16. //EA = 1;在串口初始化函数中已经打开了总中断
  17. IT0 = 1;//外部中断的下降沿触发
  18. }
  19. void main()
  20. {
  21. Time0Init();//定时器0初始化
  22. UartInit();//串口相关初始化
  23. //外部中断初始化
  24. Ex0Init();
  25. while(1){
  26. if(signal){//定时器1s到点,把signal置一,主程序发送速度
  27. sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
  28. SendString(speedMes);//速度发出去
  29. signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
  30. }
  31. }
  32. }
  33. void speedHandler() interrupt 0 //外部中断处理函数
  34. {
  35. speedCnt++;//码盘转动了一个格子
  36. }
  37. //uart.c
  38. #include "reg52.h"
  39. #include "motor.h"
  40. #include "string.h"
  41. sbit D5 = P3^7;
  42. #define SIZE 12
  43. sfr AUXR = 0x8E;
  44. char buffer[SIZE];
  45. void UartInit(void) //9600bps@11.0592MHz
  46. {
  47. AUXR = 0x01;
  48. SCON = 0x50; //配置串口工作方式1,REN使能接收
  49. TMOD &= 0x0F;
  50. TMOD |= 0x20;//定时器1工作方式位8位自动重装
  51. TH1 = 0xFD;
  52. TL1 = 0xFD;//9600波特率的初值
  53. TR1 = 1;//启动定时器
  54. EA = 1;//开启总中断
  55. ES = 1;//开启串口中断
  56. }
  57. void SendByte(char mydata)
  58. {
  59. SBUF = mydata;
  60. while(!TI);
  61. TI = 0;
  62. }
  63. void SendString(char *str)
  64. {
  65. while(*str != '\0'){
  66. SendByte(*str);
  67. str++;
  68. }
  69. }
  70. //M1qian M2 hou M3 zuo M4 you
  71. void Uart_Handler() interrupt 4
  72. {
  73. static int i = 0;//静态变量,被初始化一次
  74. char tmp;
  75. if(RI)//中断处理函数中,对于接收中断的响应
  76. {
  77. RI = 0;//清除接收中断标志位
  78. tmp = SBUF;
  79. if(tmp == 'M'){
  80. i = 0;
  81. }
  82. buffer[i++] = tmp;
  83. //灯控指令
  84. if(buffer[0] == 'M'){
  85. switch(buffer[1]){
  86. case '1':
  87. goForward();
  88. break;
  89. case '2':
  90. goBack();
  91. break;
  92. case '3':
  93. goLeft();
  94. break;
  95. case '4':
  96. goRight();
  97. break;
  98. default:
  99. stop();
  100. break;
  101. }
  102. }
  103. if(i == 12) {
  104. memset(buffer, '\0', SIZE);
  105. i = 0;
  106. }
  107. }
  108. }
  109. //motor.c
  110. #include "reg52.h"
  111. sbit RightCon1A = P3^7;
  112. sbit RightCon1B = P3^3;
  113. sbit LeftCon1A = P3^4;
  114. sbit LeftCon1B = P3^5;
  115. void goForward()
  116. {
  117. LeftCon1A = 0;
  118. LeftCon1B = 1;
  119. RightCon1A = 0;
  120. RightCon1B = 1;
  121. }
  122. void goRight()
  123. {
  124. LeftCon1A = 0;
  125. LeftCon1B = 1;
  126. RightCon1A = 0;
  127. RightCon1B = 0;
  128. }
  129. void goLeft()
  130. {
  131. LeftCon1A = 0;
  132. LeftCon1B = 0;
  133. RightCon1A = 0;
  134. RightCon1B = 1;
  135. }
  136. void goBack()
  137. {
  138. LeftCon1A = 1;
  139. LeftCon1B = 0;
  140. RightCon1A = 1;
  141. RightCon1B = 0;
  142. }
  143. void stop()
  144. {
  145. LeftCon1A = 0;
  146. LeftCon1B = 0;
  147. RightCon1A = 0;
  148. RightCon1B = 0;
  149. }
  150. //time.c
  151. #include "motor.h"
  152. #include "reg52.h"
  153. extern unsigned int speedCnt;
  154. unsigned int speed;
  155. char signal = 0;
  156. unsigned int cnt = 0;
  157. void Time0Init()
  158. {
  159. //1. 配置定时器0工作模式位16位计时
  160. TMOD = 0x01;
  161. //2. 给初值,定一个0.5出来
  162. TL0=0x33;
  163. TH0=0xFE;
  164. //3. 开始计时
  165. TR0 = 1;
  166. TF0 = 0;
  167. //4. 打开定时器0中断
  168. ET0 = 1;
  169. //5. 打开总中断EA
  170. EA = 1;
  171. }
  172. void Time0Handler() interrupt 1
  173. {
  174. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  175. //重新给初值
  176. TL0=0x33;
  177. TH0=0xFE;
  178. if(cnt == 2000){//爆表2000次,经过了1s
  179. signal = 1;
  180. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  181. //计算小车的速度,也就是拿到speedCnt的值
  182. speed = speedCnt;
  183. speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
  184. }
  185. }
4.4 小车速度显示在OLED屏

使用oled模块

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

5.远程控制小车

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

串口透传技术:

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

代码实现:

  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. void main()
  6. {
  7. UartInit();
  8. while(1){
  9. stop();
  10. }
  11. }
  12. //uart.c
  13. #include "reg52.h"
  14. #include "motor.h"
  15. #include "string.h"
  16. #include "delay.h"
  17. sbit D5 = P3^7;
  18. #define SIZE 12
  19. sfr AUXR = 0x8E;
  20. char buffer[SIZE];
  21. void UartInit(void) //9600bps@11.0592MHz
  22. {
  23. AUXR = 0x01;
  24. SCON = 0x50; //配置串口工作方式1,REN使能接收
  25. TMOD &= 0x0F;
  26. TMOD |= 0x20;//定时器1工作方式位8位自动重装
  27. TH1 = 0xFD;
  28. TL1 = 0xFD;//9600波特率的初值
  29. TR1 = 1;//启动定时器
  30. EA = 1;//开启总中断
  31. ES = 1;//开启串口中断
  32. }
  33. //M1qian M2 hou M3 zuo M4 you
  34. void Uart_Handler() interrupt 4
  35. {
  36. static int i = 0;//静态变量,被初始化一次
  37. char tmp;
  38. if(RI)//中断处理函数中,对于接收中断的响应
  39. {
  40. RI = 0;//清除接收中断标志位
  41. tmp = SBUF;
  42. if(tmp == 'M'){
  43. i = 0;
  44. }
  45. buffer[i++] = tmp;
  46. //灯控指令
  47. if(buffer[0] == 'M'){
  48. switch(buffer[1]){
  49. case '1':
  50. goForward();
  51. Delay10ms();
  52. break;
  53. case '2':
  54. goBack();
  55. Delay10ms();
  56. break;
  57. case '3':
  58. goLeft();
  59. Delay10ms();
  60. break;
  61. case '4':
  62. goRight();
  63. Delay10ms();
  64. break;
  65. default:
  66. stop();
  67. break;
  68. }
  69. }
  70. if(i == 12) {
  71. memset(buffer, '\0', SIZE);
  72. i = 0;
  73. }
  74. }
  75. }
  76. //motor.c
  77. #include "reg52.h"
  78. sbit RightCon1A = P3^2;
  79. sbit RightCon1B = P3^3;
  80. sbit LeftCon1A = P3^4;
  81. sbit LeftCon1B = P3^5;
  82. void goForward()
  83. {
  84. LeftCon1A = 0;
  85. LeftCon1B = 1;
  86. RightCon1A = 0;
  87. RightCon1B = 1;
  88. }
  89. void goRight()
  90. {
  91. LeftCon1A = 0;
  92. LeftCon1B = 1;
  93. RightCon1A = 0;
  94. RightCon1B = 0;
  95. }
  96. void goLeft()
  97. {
  98. LeftCon1A = 0;
  99. LeftCon1B = 0;
  100. RightCon1A = 0;
  101. RightCon1B = 1;
  102. }
  103. void goBack()
  104. {
  105. LeftCon1A = 1;
  106. LeftCon1B = 0;
  107. RightCon1A = 1;
  108. RightCon1B = 0;
  109. }
  110. void stop()
  111. {
  112. LeftCon1A = 0;
  113. LeftCon1B = 0;
  114. RightCon1A = 0;
  115. RightCon1B = 0;
  116. }
  117. //delay.c
  118. #include "intrins.h"
  119. void Delay10ms() //@11.0592MHz
  120. {
  121. unsigned char i, j;
  122. i = 18;
  123. j = 235;
  124. do
  125. {
  126. while (--j);
  127. } while (--i);
  128. }
  129. void Delay1000ms() //@11.0592MHz
  130. {
  131. unsigned char i, j, k;
  132. _nop_();
  133. i = 8;
  134. j = 1;
  135. k = 243;
  136. do
  137. {
  138. do
  139. {
  140. while (--k);
  141. } while (--j);
  142. } while (--i);
  143. }
5.2 蓝牙控制并测速小车

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

代码实现:

  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. #include "reg52.h"
  6. #include "time.h"
  7. #include "stdio.h"
  8. #include "Oled.h"
  9. sbit speedIO = P3^2;//外部中断0
  10. unsigned int speedCnt = 0; //统计格子,脉冲次数
  11. extern unsigned int speed;//速度
  12. extern char signal; //主程序发速度数据的通知
  13. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
  14. void Ex0Init()
  15. {
  16. EX0 = 1;//允许中断
  17. //EA = 1;在串口初始化函数中已经打开了总中断
  18. IT0 = 1;//外部中断的下降沿触发
  19. }
  20. void main()
  21. {
  22. Time0Init();//定时器0初始化
  23. UartInit();//串口相关初始化
  24. //外部中断初始化
  25. Ex0Init();
  26. Oled_Init();
  27. Oled_Clear();
  28. while(1){
  29. if(signal){//定时器1s到点,把signal置一,主程序发送速度
  30. sprintf(speedMes,"speed:%d cm/s",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
  31. SendString(speedMes);//速度发出去
  32. signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
  33. }
  34. Oled_Show_Str(2,2,speedMes);
  35. }
  36. }
  37. void speedHandler() interrupt 0 //外部中断处理函数
  38. {
  39. speedCnt++;//码盘转动了一个格子
  40. }
  41. //uart.c
  42. #include "reg52.h"
  43. #include "motor.h"
  44. #include "string.h"
  45. sbit D5 = P3^7;
  46. #define SIZE 12
  47. sfr AUXR = 0x8E;
  48. char buffer[SIZE];
  49. void UartInit(void) //9600bps@11.0592MHz
  50. {
  51. AUXR = 0x01;
  52. SCON = 0x50; //配置串口工作方式1,REN使能接收
  53. TMOD &= 0x0F;
  54. TMOD |= 0x20;//定时器1工作方式位8位自动重装
  55. TH1 = 0xFD;
  56. TL1 = 0xFD;//9600波特率的初值
  57. TR1 = 1;//启动定时器
  58. EA = 1;//开启总中断
  59. ES = 1;//开启串口中断
  60. }
  61. void SendByte(char mydata)
  62. {
  63. SBUF = mydata;
  64. while(!TI);
  65. TI = 0;
  66. }
  67. void SendString(char *str)
  68. {
  69. while(*str != '\0'){
  70. SendByte(*str);
  71. str++;
  72. }
  73. }
  74. //M1qian M2 hou M3 zuo M4 you
  75. void Uart_Handler() interrupt 4
  76. {
  77. static int i = 0;//静态变量,被初始化一次
  78. char tmp;
  79. if(RI)//中断处理函数中,对于接收中断的响应
  80. {
  81. RI = 0;//清除接收中断标志位
  82. tmp = SBUF;
  83. if(tmp == 'M'){
  84. i = 0;
  85. }
  86. buffer[i++] = tmp;
  87. //灯控指令
  88. if(buffer[0] == 'M'){
  89. switch(buffer[1]){
  90. case '1':
  91. goForward();
  92. break;
  93. case '2':
  94. goBack();
  95. break;
  96. case '3':
  97. goLeft();
  98. break;
  99. case '4':
  100. goRight();
  101. break;
  102. default:
  103. stop();
  104. break;
  105. }
  106. }
  107. if(i == 12) {
  108. memset(buffer, '\0', SIZE);
  109. i = 0;
  110. }
  111. }
  112. }
  113. //motor.c
  114. #include "reg52.h"
  115. sbit RightCon1A = P3^7;
  116. sbit RightCon1B = P3^3;
  117. sbit LeftCon1A = P3^4;
  118. sbit LeftCon1B = P3^5;
  119. void goForward()
  120. {
  121. LeftCon1A = 0;
  122. LeftCon1B = 1;
  123. RightCon1A = 0;
  124. RightCon1B = 1;
  125. }
  126. void goRight()
  127. {
  128. LeftCon1A = 0;
  129. LeftCon1B = 1;
  130. RightCon1A = 0;
  131. RightCon1B = 0;
  132. }
  133. void goLeft()
  134. {
  135. LeftCon1A = 0;
  136. LeftCon1B = 0;
  137. RightCon1A = 0;
  138. RightCon1B = 1;
  139. }
  140. void goBack()
  141. {
  142. LeftCon1A = 1;
  143. LeftCon1B = 0;
  144. RightCon1A = 1;
  145. RightCon1B = 0;
  146. }
  147. void stop()
  148. {
  149. LeftCon1A = 0;
  150. LeftCon1B = 0;
  151. RightCon1A = 0;
  152. RightCon1B = 0;
  153. }
  154. //time.c
  155. #include "motor.h"
  156. #include "reg52.h"
  157. extern unsigned int speedCnt;
  158. unsigned int speed;
  159. char signal = 0;
  160. unsigned int cnt = 0;
  161. void Time0Init()
  162. {
  163. //1. 配置定时器0工作模式位16位计时
  164. TMOD = 0x01;
  165. //2. 给初值,定一个0.5出来
  166. TL0=0x33;
  167. TH0=0xFE;
  168. //3. 开始计时
  169. TR0 = 1;
  170. TF0 = 0;
  171. //4. 打开定时器0中断
  172. ET0 = 1;
  173. //5. 打开总中断EA
  174. EA = 1;
  175. }
  176. void Time0Handler() interrupt 1
  177. {
  178. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  179. //重新给初值
  180. TL0=0x33;
  181. TH0=0xFE;
  182. if(cnt == 2000){//爆表2000次,经过了1s
  183. signal = 1;
  184. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  185. //计算小车的速度,也就是拿到speedCnt的值
  186. speed = speedCnt;
  187. speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
  188. }
  189. }
  190. //oled.c
  191. #include "reg52.h"
  192. #include "intrins.h"
  193. #include "Oledfont.h"
  194. sbit scl = P1^2;
  195. sbit sda = P1^3;
  196. void IIC_Start()
  197. {
  198. scl = 0;
  199. sda = 1;
  200. scl = 1;
  201. _nop_();
  202. sda = 0;
  203. _nop_();
  204. }
  205. void IIC_Stop()
  206. {
  207. scl = 0;
  208. sda = 0;
  209. scl = 1;
  210. _nop_();
  211. sda = 1;
  212. _nop_();
  213. }
  214. char IIC_ACK()
  215. {
  216. char flag;
  217. sda = 1;//就在时钟脉冲9期间释放数据线
  218. _nop_();
  219. scl = 1;
  220. _nop_();
  221. flag = sda;
  222. _nop_();
  223. scl = 0;
  224. _nop_();
  225. return flag;
  226. }
  227. void IIC_Send_Byte(char dataSend)
  228. {
  229. int i;
  230. for(i = 0;i<8;i++){
  231. scl = 0;//scl拉低,让sda做好数据准备
  232. sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
  233. _nop_();//发送数据建立时间
  234. scl = 1;//scl拉高开始发送
  235. _nop_();//数据发送时间
  236. scl = 0;//发送完毕拉低
  237. _nop_();//
  238. dataSend = dataSend << 1;
  239. }
  240. }
  241. void Oled_Write_Cmd(char dataCmd)
  242. {
  243. // 1. start()
  244. IIC_Start();
  245. //
  246. // 2. 写入从机地址 b0111 1000 0x78
  247. IIC_Send_Byte(0x78);
  248. // 3. ACK
  249. IIC_ACK();
  250. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  251. IIC_Send_Byte(0x00);
  252. // 5. ACK
  253. IIC_ACK();
  254. //6. 写入指令/数据
  255. IIC_Send_Byte(dataCmd);
  256. //7. ACK
  257. IIC_ACK();
  258. //8. STOP
  259. IIC_Stop();
  260. }
  261. void Oled_Write_Data(char dataData)
  262. {
  263. // 1. start()
  264. IIC_Start();
  265. //
  266. // 2. 写入从机地址 b0111 1000 0x78
  267. IIC_Send_Byte(0x78);
  268. // 3. ACK
  269. IIC_ACK();
  270. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  271. IIC_Send_Byte(0x40);
  272. // 5. ACK
  273. IIC_ACK();
  274. ///6. 写入指令/数据
  275. IIC_Send_Byte(dataData);
  276. //7. ACK
  277. IIC_ACK();
  278. //8. STOP
  279. IIC_Stop();
  280. }
  281. void Oled_Init(void){
  282. Oled_Write_Cmd(0xAE);//--display off
  283. Oled_Write_Cmd(0x00);//---set low column address
  284. Oled_Write_Cmd(0x10);//---set high column address
  285. Oled_Write_Cmd(0x40);//--set start line address
  286. Oled_Write_Cmd(0xB0);//--set page address
  287. Oled_Write_Cmd(0x81); // contract control
  288. Oled_Write_Cmd(0xFF);//--128
  289. Oled_Write_Cmd(0xA1);//set segment remap
  290. Oled_Write_Cmd(0xA6);//--normal / reverse
  291. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  292. Oled_Write_Cmd(0x3F);//--1/32 duty
  293. Oled_Write_Cmd(0xC8);//Com scan direction
  294. Oled_Write_Cmd(0xD3);//-set display offset
  295. Oled_Write_Cmd(0x00);//
  296. Oled_Write_Cmd(0xD5);//set osc division
  297. Oled_Write_Cmd(0x80);//
  298. Oled_Write_Cmd(0xD8);//set area color mode off
  299. Oled_Write_Cmd(0x05);//
  300. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  301. Oled_Write_Cmd(0xF1);//
  302. Oled_Write_Cmd(0xDA);//set com pin configuartion
  303. Oled_Write_Cmd(0x12);//
  304. Oled_Write_Cmd(0xDB);//set Vcomh
  305. Oled_Write_Cmd(0x30);//
  306. Oled_Write_Cmd(0x8D);//set charge pump enable
  307. Oled_Write_Cmd(0x14);//
  308. Oled_Write_Cmd(0xAF);//--turn on oled panel
  309. }
  310. void Oled_Clear()
  311. {
  312. unsigned char i,j; //-128 --- 127
  313. for(i=0;i<8;i++){
  314. Oled_Write_Cmd(0xB0 + i);//page0--page7
  315. //每个page从0列
  316. Oled_Write_Cmd(0x00);
  317. Oled_Write_Cmd(0x10);
  318. //0到127列,依次写入0,每写入数据,列地址自动偏移
  319. for(j = 0;j<128;j++){
  320. Oled_Write_Data(0);
  321. }
  322. }
  323. }
  324. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
  325. unsigned int i;
  326. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
  327. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  328. Oled_Write_Cmd(0x10+(col>>4)); //high
  329. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
  330. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  331. }
  332. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
  333. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  334. Oled_Write_Cmd(0x10+(col>>4)); //high
  335. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
  336. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  337. }
  338. }
  339. /******************************************************************************/
  340. // 函数名称:Oled_Show_Char
  341. // 输入参数:oledChar
  342. // 输出参数:无
  343. // 函数功能:OLED显示单个字符
  344. /******************************************************************************/
  345. void Oled_Show_Str(char row,char col,char *str){
  346. while(*str!=0){
  347. Oled_Show_Char(row,col,*str);
  348. str++;
  349. col += 8;
  350. }
  351. }

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指令以回车作为结 尾,响应或上报以回车换行为结尾。

代码实现:

  1. //main.c
  2. #include "motor.h"
  3. #include "delay.h"
  4. #include "uart.h"
  5. #include "reg52.h"
  6. #include "time.h"
  7. #include "stdio.h"
  8. #include "Oled.h"
  9. #include "esp8266.h"
  10. sbit speedIO = P3^2;//外部中断0
  11. unsigned int speedCnt = 0; //统计格子,脉冲次数
  12. extern unsigned int speed;//速度
  13. extern char signal; //主程序发速度数据的通知
  14. char speedMes[24]; //主程序发送速度数据的字符串缓冲区
  15. //发送数据
  16. char FSSJ[] = "AT+CIPSEND=0,5\r\n";
  17. void Ex0Init()
  18. {
  19. EX0 = 1;//允许中断
  20. //EA = 1;在串口初始化函数中已经打开了总中断
  21. IT0 = 1;//外部中断的下降沿触发
  22. }
  23. void main()
  24. {
  25. Time0Init();//定时器0初始化
  26. UartInit();//串口相关初始化
  27. Delay1000ms();//给espwifi模块上电时间
  28. initWifi_AP(); //初始化wifi工作在ap模式
  29. waitConnect(); //等待客户端的连接
  30. //外部中断初始化
  31. Ex0Init();
  32. Oled_Init();
  33. Oled_Clear();
  34. while(1){
  35. if(signal){//定时器1s到点,把signal置一,主程序发送速度
  36. SendString(FSSJ);
  37. Delay1000ms();
  38. sprintf(speedMes,"%dcms",speed);//串口数据的字符串拼装,speed是格子,每个格子1cm
  39. SendString(speedMes);//速度发出去
  40. signal = 0;//清0speed,下次由定时器1s后的中断处理中再置一
  41. }
  42. Oled_Show_Str(2,2,speedMes);
  43. }
  44. }
  45. void speedHandler() interrupt 0 //外部中断处理函数
  46. {
  47. speedCnt++;//码盘转动了一个格子
  48. }
  49. //uart.c
  50. #include "reg52.h"
  51. #include "motor.h"
  52. #include "string.h"
  53. sbit D5 = P3^7;
  54. #define SIZE 12
  55. sfr AUXR = 0x8E;
  56. char buffer[SIZE];
  57. extern char AT_OK_Flag; //OK返回值的标志位
  58. extern char Client_Connect_Flag;
  59. void UartInit(void) //9600bps@11.0592MHz
  60. {
  61. AUXR = 0x01;
  62. SCON = 0x50; //配置串口工作方式1,REN使能接收
  63. TMOD &= 0x0F;
  64. TMOD |= 0x20;//定时器1工作方式位8位自动重装
  65. TH1 = 0xFD;
  66. TL1 = 0xFD;//9600波特率的初值
  67. TR1 = 1;//启动定时器
  68. EA = 1;//开启总中断
  69. ES = 1;//开启串口中断
  70. }
  71. void SendByte(char mydata)
  72. {
  73. SBUF = mydata;
  74. while(!TI);
  75. TI = 0;
  76. }
  77. void SendString(char *str)
  78. {
  79. while(*str != '\0'){
  80. SendByte(*str);
  81. str++;
  82. }
  83. }
  84. //M1qian M2 hou M3 zuo M4 you
  85. void Uart_Handler() interrupt 4
  86. {
  87. static int i = 0;//静态变量,被初始化一次
  88. char tmp;
  89. if(RI)//中断处理函数中,对于接收中断的响应
  90. {
  91. RI = 0;//清除接收中断标志位
  92. tmp = SBUF;
  93. if(tmp == 'M' || tmp == 'O' || tmp == '0'){
  94. i = 0;
  95. }
  96. buffer[i++] = tmp;
  97. //连接服务器等OK返回值指令的判断
  98. if(buffer[0] == 'O' && buffer[1] == 'K'){
  99. AT_OK_Flag = 1;
  100. memset(buffer, '\0', SIZE);
  101. }
  102. if(buffer[0] == '0' && buffer[2] == 'C'){
  103. Client_Connect_Flag = 1;
  104. memset(buffer, '\0', SIZE);
  105. }
  106. //灯控指令
  107. if(buffer[0] == 'M'){
  108. switch(buffer[1]){
  109. case '1':
  110. goForward();
  111. break;
  112. case '2':
  113. goBack();
  114. break;
  115. case '3':
  116. goLeft();
  117. break;
  118. case '4':
  119. goRight();
  120. break;
  121. default:
  122. stop();
  123. break;
  124. }
  125. }
  126. if(i == 12) {
  127. memset(buffer, '\0', SIZE);
  128. i = 0;
  129. }
  130. }
  131. }
  132. //motor.c
  133. #include "reg52.h"
  134. sbit RightCon1A = P3^7;
  135. sbit RightCon1B = P3^3;
  136. sbit LeftCon1A = P3^4;
  137. sbit LeftCon1B = P3^5;
  138. void goForward()
  139. {
  140. LeftCon1A = 0;
  141. LeftCon1B = 1;
  142. RightCon1A = 0;
  143. RightCon1B = 1;
  144. }
  145. void goRight()
  146. {
  147. LeftCon1A = 0;
  148. LeftCon1B = 1;
  149. RightCon1A = 0;
  150. RightCon1B = 0;
  151. }
  152. void goLeft()
  153. {
  154. LeftCon1A = 0;
  155. LeftCon1B = 0;
  156. RightCon1A = 0;
  157. RightCon1B = 1;
  158. }
  159. void goBack()
  160. {
  161. LeftCon1A = 1;
  162. LeftCon1B = 0;
  163. RightCon1A = 1;
  164. RightCon1B = 0;
  165. }
  166. void stop()
  167. {
  168. LeftCon1A = 0;
  169. LeftCon1B = 0;
  170. RightCon1A = 0;
  171. RightCon1B = 0;
  172. }
  173. //time.c
  174. #include "motor.h"
  175. #include "reg52.h"
  176. extern unsigned int speedCnt;
  177. unsigned int speed;
  178. char signal = 0;
  179. unsigned int cnt = 0;
  180. void Time0Init()
  181. {
  182. //1. 配置定时器0工作模式位16位计时
  183. TMOD = 0x01;
  184. //2. 给初值,定一个0.5出来
  185. TL0=0x33;
  186. TH0=0xFE;
  187. //3. 开始计时
  188. TR0 = 1;
  189. TF0 = 0;
  190. //4. 打开定时器0中断
  191. ET0 = 1;
  192. //5. 打开总中断EA
  193. EA = 1;
  194. }
  195. void Time0Handler() interrupt 1
  196. {
  197. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  198. //重新给初值
  199. TL0=0x33;
  200. TH0=0xFE;
  201. if(cnt == 2000){//爆表2000次,经过了1s
  202. signal = 1;
  203. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  204. //计算小车的速度,也就是拿到speedCnt的值
  205. speed = speedCnt;
  206. speedCnt = 0;//1秒后拿到speedCnt个格子,就能算出这1s的速度,格子清零
  207. }
  208. }
  209. //oled.c
  210. #include "reg52.h"
  211. #include "intrins.h"
  212. #include "Oledfont.h"
  213. sbit scl = P1^2;
  214. sbit sda = P1^3;
  215. void IIC_Start()
  216. {
  217. scl = 0;
  218. sda = 1;
  219. scl = 1;
  220. _nop_();
  221. sda = 0;
  222. _nop_();
  223. }
  224. void IIC_Stop()
  225. {
  226. scl = 0;
  227. sda = 0;
  228. scl = 1;
  229. _nop_();
  230. sda = 1;
  231. _nop_();
  232. }
  233. char IIC_ACK()
  234. {
  235. char flag;
  236. sda = 1;//就在时钟脉冲9期间释放数据线
  237. _nop_();
  238. scl = 1;
  239. _nop_();
  240. flag = sda;
  241. _nop_();
  242. scl = 0;
  243. _nop_();
  244. return flag;
  245. }
  246. void IIC_Send_Byte(char dataSend)
  247. {
  248. int i;
  249. for(i = 0;i<8;i++){
  250. scl = 0;//scl拉低,让sda做好数据准备
  251. sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
  252. _nop_();//发送数据建立时间
  253. scl = 1;//scl拉高开始发送
  254. _nop_();//数据发送时间
  255. scl = 0;//发送完毕拉低
  256. _nop_();//
  257. dataSend = dataSend << 1;
  258. }
  259. }
  260. void Oled_Write_Cmd(char dataCmd)
  261. {
  262. // 1. start()
  263. IIC_Start();
  264. //
  265. // 2. 写入从机地址 b0111 1000 0x78
  266. IIC_Send_Byte(0x78);
  267. // 3. ACK
  268. IIC_ACK();
  269. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  270. IIC_Send_Byte(0x00);
  271. // 5. ACK
  272. IIC_ACK();
  273. //6. 写入指令/数据
  274. IIC_Send_Byte(dataCmd);
  275. //7. ACK
  276. IIC_ACK();
  277. //8. STOP
  278. IIC_Stop();
  279. }
  280. void Oled_Write_Data(char dataData)
  281. {
  282. // 1. start()
  283. IIC_Start();
  284. //
  285. // 2. 写入从机地址 b0111 1000 0x78
  286. IIC_Send_Byte(0x78);
  287. // 3. ACK
  288. IIC_ACK();
  289. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  290. IIC_Send_Byte(0x40);
  291. // 5. ACK
  292. IIC_ACK();
  293. ///6. 写入指令/数据
  294. IIC_Send_Byte(dataData);
  295. //7. ACK
  296. IIC_ACK();
  297. //8. STOP
  298. IIC_Stop();
  299. }
  300. void Oled_Init(void){
  301. Oled_Write_Cmd(0xAE);//--display off
  302. Oled_Write_Cmd(0x00);//---set low column address
  303. Oled_Write_Cmd(0x10);//---set high column address
  304. Oled_Write_Cmd(0x40);//--set start line address
  305. Oled_Write_Cmd(0xB0);//--set page address
  306. Oled_Write_Cmd(0x81); // contract control
  307. Oled_Write_Cmd(0xFF);//--128
  308. Oled_Write_Cmd(0xA1);//set segment remap
  309. Oled_Write_Cmd(0xA6);//--normal / reverse
  310. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  311. Oled_Write_Cmd(0x3F);//--1/32 duty
  312. Oled_Write_Cmd(0xC8);//Com scan direction
  313. Oled_Write_Cmd(0xD3);//-set display offset
  314. Oled_Write_Cmd(0x00);//
  315. Oled_Write_Cmd(0xD5);//set osc division
  316. Oled_Write_Cmd(0x80);//
  317. Oled_Write_Cmd(0xD8);//set area color mode off
  318. Oled_Write_Cmd(0x05);//
  319. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  320. Oled_Write_Cmd(0xF1);//
  321. Oled_Write_Cmd(0xDA);//set com pin configuartion
  322. Oled_Write_Cmd(0x12);//
  323. Oled_Write_Cmd(0xDB);//set Vcomh
  324. Oled_Write_Cmd(0x30);//
  325. Oled_Write_Cmd(0x8D);//set charge pump enable
  326. Oled_Write_Cmd(0x14);//
  327. Oled_Write_Cmd(0xAF);//--turn on oled panel
  328. }
  329. void Oled_Clear()
  330. {
  331. unsigned char i,j; //-128 --- 127
  332. for(i=0;i<8;i++){
  333. Oled_Write_Cmd(0xB0 + i);//page0--page7
  334. //每个page从0列
  335. Oled_Write_Cmd(0x00);
  336. Oled_Write_Cmd(0x10);
  337. //0到127列,依次写入0,每写入数据,列地址自动偏移
  338. for(j = 0;j<128;j++){
  339. Oled_Write_Data(0);
  340. }
  341. }
  342. }
  343. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
  344. unsigned int i;
  345. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
  346. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  347. Oled_Write_Cmd(0x10+(col>>4)); //high
  348. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
  349. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  350. }
  351. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
  352. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  353. Oled_Write_Cmd(0x10+(col>>4)); //high
  354. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
  355. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  356. }
  357. }
  358. /******************************************************************************/
  359. // 函数名称:Oled_Show_Char
  360. // 输入参数:oledChar
  361. // 输出参数:无
  362. // 函数功能:OLED显示单个字符
  363. /******************************************************************************/
  364. void Oled_Show_Str(char row,char col,char *str){
  365. while(*str!=0){
  366. Oled_Show_Char(row,col,*str);
  367. str++;
  368. col += 8;
  369. }
  370. }
  371. //esp8266.c
  372. #include "uart.h"
  373. //1 工作在路由模式
  374. code char LYMO[] = "AT+CWMODE=2\r\n";
  375. //2 使能多链接
  376. code char DLJ[] = "AT+CIPMUX=1\r\n";
  377. //3 建立TCPServer
  378. code char JLFW[] = "AT+CIPSERVER=1\r\n"; // default port = 333
  379. char AT_OK_Flag = 0; //OK返回值的标志位
  380. char Client_Connect_Flag = 0;
  381. void initWifi_AP()
  382. {
  383. SendString(LYMO);
  384. while(!AT_OK_Flag);
  385. AT_OK_Flag = 0;
  386. SendString(DLJ);
  387. while(!AT_OK_Flag);
  388. AT_OK_Flag = 0;
  389. }
  390. void waitConnect()
  391. {
  392. SendString(JLFW);
  393. while(!Client_Connect_Flag);
  394. AT_OK_Flag = 0;
  395. }
  396. //delay.c
  397. #include "intrins.h"
  398. void Delay1000ms() //@11.0592MHz
  399. {
  400. unsigned char i, j, k;
  401. _nop_();
  402. i = 8;
  403. j = 1;
  404. k = 243;
  405. do
  406. {
  407. do
  408. {
  409. while (--k);
  410. } while (--j);
  411. } while (--i);
  412. }
5.4 4g控制小车

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

模块介绍:

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

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

6.语音控制小车

6.1语音模块配置

使用SU-03T / LD3320

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

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

代码示例:

  1. //main.c
  2. #include "reg52.h"
  3. #include "hc04.h"
  4. #include "delay.h"
  5. #include "sg90.h"
  6. #include "Oled.h"
  7. #include "motor.h"
  8. #define MIDDLE 0
  9. #define LEFT 1
  10. #define RIGHT 2
  11. #define BZ 1
  12. #define XJ 2
  13. #define GS 3
  14. sbit A25 = P1^5;
  15. sbit A26 = P1^6;
  16. sbit A27 = P1^7;
  17. sbit leftSensorX = P2^7;
  18. sbit rightSensorX = P2^6;
  19. sbit leftSensorG = P2^5;
  20. sbit rightSensorG = P2^4;
  21. char dir;
  22. double disMiddle;
  23. double disLeft;
  24. double disRight;
  25. void xunjiMode()
  26. {
  27. if(leftSensorX == 0 && rightSensorX == 0){
  28. goForward();
  29. }
  30. if(leftSensorX == 1 && rightSensorX == 0){
  31. goLeft();
  32. }
  33. if(leftSensorX == 0 && rightSensorX == 1){
  34. goRight();
  35. }
  36. if(leftSensorX == 1 && rightSensorX == 1){
  37. //停
  38. stop();
  39. }
  40. }
  41. void gensuiMode()
  42. {
  43. if(leftSensorG == 0 && rightSensorG == 0){
  44. goForward();
  45. }
  46. if(leftSensorG == 1 && rightSensorG == 0){
  47. goRight();
  48. }
  49. if(leftSensorG == 0 && rightSensorG == 1){
  50. goLeft();
  51. }
  52. if(leftSensorG == 1 && rightSensorG == 1){
  53. //停
  54. stop();
  55. }
  56. }
  57. void bizhangMode()
  58. {
  59. if(dir != MIDDLE){
  60. sgMiddle();
  61. dir = MIDDLE;
  62. Delay300ms();
  63. }
  64. disMiddle = get_distance();
  65. if(disMiddle > 35){
  66. //前进
  67. goForward();
  68. }else if(disMiddle < 10){
  69. goBack();
  70. }else
  71. {
  72. //停止
  73. stop();
  74. //测左边距离
  75. sgLeft();
  76. Delay300ms();
  77. disLeft = get_distance();
  78. sgMiddle();
  79. Delay300ms();
  80. sgRight();
  81. dir = RIGHT;
  82. Delay300ms();
  83. disRight = get_distance();
  84. if(disLeft < disRight){
  85. goRight();
  86. Delay150ms();
  87. stop();
  88. }
  89. if(disRight < disLeft){
  90. goLeft();
  91. Delay150ms();
  92. stop();
  93. }
  94. }
  95. }
  96. void main()
  97. {
  98. int mark = 0;
  99. Time0Init();
  100. Time1Init();
  101. //舵机的初始位置
  102. sgMiddle();
  103. Delay300ms();
  104. Delay300ms();
  105. dir = MIDDLE;
  106. Oled_Init();
  107. Oled_Clear();
  108. Oled_Show_Str(2,2,"-----Ready----");
  109. while(1){
  110. //满足寻迹模式的条件
  111. if(A25 == 0 && A26 == 1 && A27 == 1){
  112. if(mark != XJ){
  113. Oled_Clear();
  114. Oled_Show_Str(2,2,"-----XunJi----");
  115. }
  116. mark = XJ;
  117. xunjiMode();
  118. }
  119. //满足跟随模式的条件
  120. if(A25 == 1 && A26 == 0 && A27 == 1){
  121. if(mark != GS){
  122. Oled_Clear();
  123. Oled_Show_Str(2,2,"-----GenSui----");
  124. }
  125. mark = GS;
  126. gensuiMode();
  127. }
  128. //满足避障模式的条件
  129. if(A25 == 1 && A26 == 1 && A27 == 0){
  130. if(mark != BZ){
  131. Oled_Clear();
  132. Oled_Show_Str(2,2,"-----BiZhang----");
  133. }
  134. mark = BZ;
  135. bizhangMode();
  136. }
  137. }
  138. }
  139. //hc04.c
  140. #include "reg52.h"
  141. #include "delay.h"
  142. sbit Trig = P2^3;
  143. sbit Echo = P2^2;
  144. void Time1Init()
  145. {
  146. TMOD &= 0x0F; //设置定时器模式
  147. TMOD |= 0x10;
  148. TH1 = 0;
  149. TL1 = 0;
  150. //设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
  151. }
  152. void startHC()
  153. {
  154. Trig = 0;
  155. Trig = 1;
  156. Delay10us();
  157. Trig = 0;
  158. }
  159. double get_distance()
  160. {
  161. double time;
  162. //定时器数据清零,以便下一次测距
  163. TH1 = 0;
  164. TL1 = 0;
  165. //1. Trig ,给Trig端口至少10us的高电平
  166. startHC();
  167. //2. echo由低电平跳转到高电平,表示开始发送波
  168. while(Echo == 0);
  169. //波发出去的那一下,开始启动定时器
  170. TR1 = 1;
  171. //3. 由高电平跳转回低电平,表示波回来了
  172. while(Echo == 1);
  173. //波回来的那一下,我们开始停止定时器
  174. TR1 = 0;
  175. //4. 计算出中间经过多少时间
  176. time = (TH1 * 256 + TL1)*1.085;//us为单位
  177. //5. 距离 = 速度 (340m/s)* 时间/2
  178. return (time * 0.017);
  179. }
  180. //delay.c
  181. #include "intrins.h"
  182. void Delay2000ms() //@11.0592MHz
  183. {
  184. unsigned char i, j, k;
  185. i = 15;
  186. j = 2;
  187. k = 235;
  188. do
  189. {
  190. do
  191. {
  192. while (--k);
  193. } while (--j);
  194. } while (--i);
  195. }
  196. void Delay10us() //@11.0592MHz
  197. {
  198. unsigned char i;
  199. i = 2;
  200. while (--i);
  201. }
  202. void Delay300ms() //@11.0592MHz
  203. {
  204. unsigned char i, j, k;
  205. _nop_();
  206. i = 3;
  207. j = 26;
  208. k = 223;
  209. do
  210. {
  211. do
  212. {
  213. while (--k);
  214. } while (--j);
  215. } while (--i);
  216. }
  217. void Delay150ms() //@11.0592MHz
  218. {
  219. unsigned char i, j, k;
  220. i = 2;
  221. j = 13;
  222. k = 237;
  223. do
  224. {
  225. do
  226. {
  227. while (--k);
  228. } while (--j);
  229. } while (--i);
  230. }
  231. void Delay450ms() //@11.0592MHz
  232. {
  233. unsigned char i, j, k;
  234. _nop_();
  235. i = 4;
  236. j = 39;
  237. k = 209;
  238. do
  239. {
  240. do
  241. {
  242. while (--k);
  243. } while (--j);
  244. } while (--i);
  245. }
  246. //sg90.c
  247. #include "reg52.h"
  248. #include "delay.h"
  249. sbit sg90_con = P1^1;
  250. int jd;
  251. int cnt = 0;
  252. void Time0Init()
  253. {
  254. //1. 配置定时器0工作模式位16位计时
  255. TMOD &= 0xF0; //设置定时器模式
  256. TMOD |= 0x01;
  257. //2. 给初值,定一个0.5出来
  258. TL0=0x33;
  259. TH0=0xFE;
  260. //3. 开始计时
  261. TR0 = 1;
  262. TF0 = 0;
  263. //4. 打开定时器0中断
  264. ET0 = 1;
  265. //5. 打开总中断EA
  266. EA = 1;
  267. }
  268. void sgMiddle()
  269. {
  270. //中间位置
  271. jd = 3; //90度 1.5ms高电平
  272. cnt = 0;
  273. }
  274. void sgLeft()
  275. {
  276. //左边位置
  277. jd = 5; //135度 1.5ms高电平
  278. cnt = 0;
  279. }
  280. void sgRight()
  281. {
  282. //右边位置
  283. jd = 1; //0度
  284. cnt = 0;
  285. }
  286. void Time0Handler() interrupt 1
  287. {
  288. cnt++; //统计爆表的次数. cnt=1的时候,报表了1
  289. //重新给初值
  290. TL0=0x33;
  291. TH0=0xFE;
  292. //控制PWM波
  293. if(cnt < jd){
  294. sg90_con = 1;
  295. }else{
  296. sg90_con = 0;
  297. }
  298. if(cnt == 40){//爆表40次,经过了20ms
  299. cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
  300. sg90_con = 1;
  301. }
  302. }
  303. //motor.c
  304. #include "reg52.h"
  305. sbit RightCon1A = P3^7;
  306. sbit RightCon1B = P3^3;
  307. sbit LeftCon1A = P3^4;
  308. sbit LeftCon1B = P3^5;
  309. void goForward()
  310. {
  311. LeftCon1A = 0;
  312. LeftCon1B = 1;
  313. RightCon1A = 0;
  314. RightCon1B = 1;
  315. }
  316. void goRight()
  317. {
  318. LeftCon1A = 0;
  319. LeftCon1B = 1;
  320. RightCon1A = 0;
  321. RightCon1B = 0;
  322. }
  323. void goLeft()
  324. {
  325. LeftCon1A = 0;
  326. LeftCon1B = 0;
  327. RightCon1A = 0;
  328. RightCon1B = 1;
  329. }
  330. void goBack()
  331. {
  332. LeftCon1A = 1;
  333. LeftCon1B = 0;
  334. RightCon1A = 1;
  335. RightCon1B = 0;
  336. }
  337. void stop()
  338. {
  339. LeftCon1A = 0;
  340. LeftCon1B = 0;
  341. RightCon1A = 0;
  342. RightCon1B = 0;
  343. }
  344. //oled.c
  345. #include "reg52.h"
  346. #include "intrins.h"
  347. #include "Oledfont.h"
  348. sbit scl = P1^2;
  349. sbit sda = P1^3;
  350. void IIC_Start()
  351. {
  352. scl = 0;
  353. sda = 1;
  354. scl = 1;
  355. _nop_();
  356. sda = 0;
  357. _nop_();
  358. }
  359. void IIC_Stop()
  360. {
  361. scl = 0;
  362. sda = 0;
  363. scl = 1;
  364. _nop_();
  365. sda = 1;
  366. _nop_();
  367. }
  368. char IIC_ACK()
  369. {
  370. char flag;
  371. sda = 1;//就在时钟脉冲9期间释放数据线
  372. _nop_();
  373. scl = 1;
  374. _nop_();
  375. flag = sda;
  376. _nop_();
  377. scl = 0;
  378. _nop_();
  379. return flag;
  380. }
  381. void IIC_Send_Byte(char dataSend)
  382. {
  383. int i;
  384. for(i = 0;i<8;i++){
  385. scl = 0;//scl拉低,让sda做好数据准备
  386. sda = dataSend & 0x80;//1000 0000获得dataSend的最高位,给sda
  387. _nop_();//发送数据建立时间
  388. scl = 1;//scl拉高开始发送
  389. _nop_();//数据发送时间
  390. scl = 0;//发送完毕拉低
  391. _nop_();//
  392. dataSend = dataSend << 1;
  393. }
  394. }
  395. void Oled_Write_Cmd(char dataCmd)
  396. {
  397. // 1. start()
  398. IIC_Start();
  399. //
  400. // 2. 写入从机地址 b0111 1000 0x78
  401. IIC_Send_Byte(0x78);
  402. // 3. ACK
  403. IIC_ACK();
  404. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  405. IIC_Send_Byte(0x00);
  406. // 5. ACK
  407. IIC_ACK();
  408. //6. 写入指令/数据
  409. IIC_Send_Byte(dataCmd);
  410. //7. ACK
  411. IIC_ACK();
  412. //8. STOP
  413. IIC_Stop();
  414. }
  415. void Oled_Write_Data(char dataData)
  416. {
  417. // 1. start()
  418. IIC_Start();
  419. //
  420. // 2. 写入从机地址 b0111 1000 0x78
  421. IIC_Send_Byte(0x78);
  422. // 3. ACK
  423. IIC_ACK();
  424. // 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
  425. IIC_Send_Byte(0x40);
  426. // 5. ACK
  427. IIC_ACK();
  428. ///6. 写入指令/数据
  429. IIC_Send_Byte(dataData);
  430. //7. ACK
  431. IIC_ACK();
  432. //8. STOP
  433. IIC_Stop();
  434. }
  435. void Oled_Init(void){
  436. Oled_Write_Cmd(0xAE);//--display off
  437. Oled_Write_Cmd(0x00);//---set low column address
  438. Oled_Write_Cmd(0x10);//---set high column address
  439. Oled_Write_Cmd(0x40);//--set start line address
  440. Oled_Write_Cmd(0xB0);//--set page address
  441. Oled_Write_Cmd(0x81); // contract control
  442. Oled_Write_Cmd(0xFF);//--128
  443. Oled_Write_Cmd(0xA1);//set segment remap
  444. Oled_Write_Cmd(0xA6);//--normal / reverse
  445. Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
  446. Oled_Write_Cmd(0x3F);//--1/32 duty
  447. Oled_Write_Cmd(0xC8);//Com scan direction
  448. Oled_Write_Cmd(0xD3);//-set display offset
  449. Oled_Write_Cmd(0x00);//
  450. Oled_Write_Cmd(0xD5);//set osc division
  451. Oled_Write_Cmd(0x80);//
  452. Oled_Write_Cmd(0xD8);//set area color mode off
  453. Oled_Write_Cmd(0x05);//
  454. Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
  455. Oled_Write_Cmd(0xF1);//
  456. Oled_Write_Cmd(0xDA);//set com pin configuartion
  457. Oled_Write_Cmd(0x12);//
  458. Oled_Write_Cmd(0xDB);//set Vcomh
  459. Oled_Write_Cmd(0x30);//
  460. Oled_Write_Cmd(0x8D);//set charge pump enable
  461. Oled_Write_Cmd(0x14);//
  462. Oled_Write_Cmd(0xAF);//--turn on oled panel
  463. }
  464. void Oled_Clear()
  465. {
  466. unsigned char i,j; //-128 --- 127
  467. for(i=0;i<8;i++){
  468. Oled_Write_Cmd(0xB0 + i);//page0--page7
  469. //每个page从0列
  470. Oled_Write_Cmd(0x00);
  471. Oled_Write_Cmd(0x10);
  472. //0到127列,依次写入0,每写入数据,列地址自动偏移
  473. for(j = 0;j<128;j++){
  474. Oled_Write_Data(0);
  475. }
  476. }
  477. }
  478. void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
  479. unsigned int i;
  480. Oled_Write_Cmd(0xb0+(row*2-2)); //page 0
  481. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  482. Oled_Write_Cmd(0x10+(col>>4)); //high
  483. for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
  484. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  485. }
  486. Oled_Write_Cmd(0xb0+(row*2-1)); //page 1
  487. Oled_Write_Cmd(0x00+(col&0x0f)); //low
  488. Oled_Write_Cmd(0x10+(col>>4)); //high
  489. for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
  490. Oled_Write_Data(F8X16[i]); //写数据oledTable1
  491. }
  492. }
  493. /******************************************************************************/
  494. // 函数名称:Oled_Show_Char
  495. // 输入参数:oledChar
  496. // 输出参数:无
  497. // 函数功能:OLED显示单个字符
  498. /******************************************************************************/
  499. void Oled_Show_Str(char row,char col,char *str){
  500. while(*str!=0){
  501. Oled_Show_Char(row,col,*str);
  502. str++;
  503. col += 8;
  504. }
  505. }

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

闽ICP备14008679号