• C51智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结


    目录

    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. }

  • 相关阅读:
    spring cloud 快速上手系列 -> 04-网关 Gateway -> 041-空的工程
    睿伴科创上线了
    html中如何写一个提示框,css画一个提示框
    .Net添加了引用,仍然提示找不到命名空间
    微查系统,一站式查询,让您的查询更加便捷
    Vue2父传子、子传父和兄弟间互传
    VC++透明图片绘制的三种办法
    马踏棋盘问题
    【微信小程序-初级】一些基础功能实现参考
    【我的OpenGL学习进阶之旅】 C++ 长行字符串多行书写的方法以及如何书写正确的OpenGL Shader着色器代码
  • 原文地址:https://blog.csdn.net/m0_74712453/article/details/132702785