实现功能:
- 能够实现自动识别黑线,完成5路循迹。
- 能够通过蓝牙于89C52系列芯片串口通信控制小车
- 能够自动感应障碍物实现自动刹停。
准备材料:
- 89C52芯片及最小系统板 ,
- L298n模块,
- 模拟量5路循迹模块,
- 稳压模块,
- HC-06模块,
- 超声波模块,
- 亚巧克力板,
- 4个电机,
- 12vDC电源,
- DC转接母头
普通小车使能实现原理:
利用51单片机IO口控制L298n模块引脚供电实现对电机触发。再通过EN使能端进行PWM调速,实现转弯,漂移,转圈等特殊转速。
IN为L298N模块控制引脚
EN为L298N模块使能引脚
L298N模块单边IN为0,0时,电机停止转动,为1,0 、0,1时正转、反转
1#include <REGX52.H>
2unsigned char Duty_left,Duty_right,j=0,i=0;
3//l298n
4sbit IN11=P0^2;//左前轮
5sbit IN12=P0^1;
6
7sbit IN13=P0^3;//右前轮
8sbit IN14=P0^4;
9
10sbit IN21=P1^1;//左后轮
11sbit IN22=P1^2;
12
13sbit IN23=P1^4;//右后轮
14sbit IN24=P1^3;
15
16//使能端
17sbit EN1A=P0^0;//左前
18sbit EN1B=P0^5;//右前1
19sbit EN2A=P1^6;//左后
20sbit EN2B=P1^7;//右后1
21
22 //定时器+中断
23void Timer0_Init() //100微秒@11.0592MHz
24{
25 TMOD|=0x01;//定时器T0,,工作方式都为方式1
26 TH0=0xff;//定时ff9c0.1ms fe0c0.5ms
27 TL0=0x9c;
28
29 EA=1; //开启总中断
30 ET0=1;//开启中断允许
31 TR0=1;//开启定时器中断
32}
33//利用中断,PWM调速
34void Timer0_Routine() interrupt 1
35{
36 TH0=0xff;//定时ff9c0.1ms fe0c0.5ms
37 TL0=0x9c;
38
39 i++;
40 j++;
41
42if(i<=Duty_left)
43 {
44 EN1A=1;
45 EN2A=1;
46 }
47else
48 {
49 EN1A=0;
50 EN2A=0;
51 }
52if(j<=Duty_right)
53 {
54 EN1B=1;
55 EN2B=1;
56 }
57else
58 {
59 EN1B=0;
60 EN2B=0;
61 }
62
63 if(i==100) //设置pwm周期=0.1ms*100=10ms,这样开头定义的变量正好表示占空比数值
64 {
65 i=0;//加到100后变为0,重新计数
66 }
67 if(j==100) //设置pwm周期=0.1ms*100=10ms,这样开头定义的变量正好表示占空比数值
68 {
69 j=0;
70 }
71
72}
蓝牙小车原理:
通过将蓝牙模块的{% label TX %}与{% label RX %}与单片机交叉相接进行串口通信,实现收发数据。
1#include <REGX52.H>
2//l298n
3sbit IN11=P0^2;//左前轮
4sbit IN12=P0^1;
5
6sbit IN13=P0^3;//右前轮
7sbit IN14=P0^4;
8
9sbit IN21=P1^1;//左后轮
10sbit IN22=P1^2;
11
12sbit IN23=P1^4;//右后轮
13sbit IN24=P1^3;
14
15char i;
16unsigned char ReceiveData; //接收到的数据存放变量
17//向左转
18void correct_left()
19{
20 IN11=0;
21 IN12=0;
22 IN21=0;
23 IN22=0;
24
25 IN13=1;
26 IN14=0;
27 IN23=1;
28 IN24=0;
29
30
31}
32//向右转
33void correct_right()
34{
35 IN11=1;
36 IN12=0;
37 IN21=1;
38 IN22=0;
39
40 IN13=0;
41 IN14=0;
42 IN23=0;
43 IN24=0;
44
45
46}
47
48//左转
49void Left_turning()
50{
51
52 IN11=0;
53 IN12=1;
54 IN21=0;
55 IN22=1;
56
57 IN13=1;
58 IN14=0;
59 IN23=1;
60 IN24=0;
61}
62//右转
63void Right_turning()
64{
65
66 IN11=1;
67 IN12=0;
68 IN21=1;
69 IN22=0;
70
71 IN13=0;
72 IN14=1;
73 IN23=0;
74 IN24=1;
75}
76//小车直走前行,不拐弯速度赋值
77void forward_move()
78{
79
80 IN11=1;
81 IN12=0;
82 IN21=1;
83 IN22=0;
84
85 IN13=1;
86 IN14=0;
87 IN23=1;
88 IN24=0;
89}
90
91void back()
92{
93
94 IN11=0;
95 IN12=1;
96 IN21=0;
97 IN22=1;
98
99 IN13=0;
100 IN14=1;
101 IN23=0;
102 IN24=1;
103}
104
105void stop()
106{
107
108 IN11=0;
109 IN12=0;
110 IN21=0;
111 IN22=0;
112
113 IN13=0;
114 IN14=0;
115 IN23=0;
116 IN24=0;
117}
118
119
120
121void receive(unsigned char m)//执行函数
122{
123 switch(m)
124 {
125 case '8':forward_move();break;
126 case '5':stop();break;//停止
127 }
128}
129
130void Uart_Init() //9600bps@11.0592MHz
131{
132 PCON = 0x00; //波特率不倍速s
133 SCON = 0x50; //8位数据,可变波特率
134 TMOD = 0x20; //设定定时器1为8位自动重装方式
135 TL1 = 0xFD; //设定定时初值
136 TH1 = 0xFD; //设定定时器重装值
137 ET1 = 0; //禁止定时器1中断
138 TR1 = 1; //启动定时器1
139 EA=1; //总中断
140 ES=1; //打开串口中断
141}
142
143void Uart_Routine() interrupt 4 //中断
144{
145 stop();
146 RI=0; //清除接收中断标志位
147 ReceiveData=SBUF; //除去接受的数据
148 receive(ReceiveData);
149}
150
151void main()
152{
153 Uart_Init();
154 while(1);
155}
超声波急停避障小车原理:
1.采用 IO 口 {% label TRIG %} 触发测距,给一个 10us 的高电平信号;
2.模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
3.有信号返回,通过 IO 口 {% label ECHO %} 输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。
4.测试距离=(高电平时间*声速(340M/S))/2
{% image https://img2.imgtp.com/2024/05/29/C0lSwYoH.png, alt= %}
1#include <REGX52.H>
2#include "delay.h"
3sbit Trig=P3^3;//定义超声波模块引脚
4sbit Echo=P3^2;
5unsigned int a,distance;
6
7int csb()
8{
9
10
11 TMOD &= 0x0F; //设置定时器模式
12
13 TL1 = 0; //设置定时初始值
14 TH1 = 0; //设置定时初始值
15
16
17 Trig=1;//发射20US的脉冲
18 Delay10us(2);
19 Trig=0;//关闭
20
21
22 while(!Echo);//等待返回脉冲
23 TR1=1;
24 while(!Echo);//返回脉冲结束
25 TR1=0;
26
27 a=TH1*256+TL1;
28 TH1=0;
29 TL1=0;
30
31 distance= a*1.7/100; //计算公式,按照数据手册为准,不同环境不同计算方法。
32
33 Trig=0;
34 Echo=0;
35 return distance;
36}
37
5路PID循迹小车原理:
{% image https://img2.imgtp.com/2024/05/29/jT3cO80Q.png, alt= %}
简单的循迹就是通过循迹模块反馈给单片机的高低电平信号进行不断判断和迭代,列出全部情况,这里就不做赘述。在此分享和学习关于PID算法循迹。
1#include <REGX52.H>
2
3// 定义PID控制的参数
4float Kp = 0.5; // 比例系数
5float Ki = 0.2; // 积分系数
6float Kd = 0.1; // 微分系数
7
8int error = 0; // 当前误差
9int lastError = 0; // 上一次的误差
10int integral = 0; // 误差的积分
11int derivative = 0; // 误差的微分
12
13// 红外循迹传感器定义...
14// L298N控制引脚定义...
15// 使能端引脚定义...
16// 延时函数定义...
17// Timer0 初始化函数定义...
18// 电机控制函数定义...
19
20void CalculatePID() {
21 // 根据传感器的读数计算误差,这里假设按照传感器是5个连续的数值
22 int sensorValues = (left1 << 4) | (left2 << 3) | (mid3 << 2) | (right4 << 1) | right5;
23
24 switch(sensorValues) {
25 case 0b00000:
26 case 0b11111:
27 // 特殊情况处理,比如急转弯或者丢线
28 break;
29 case 0b00100:
30 error = 0; // 小车在中心线上
31 break;
32 //...其它情况根据传感器值来设置误差值
33 }
34
35 // PID计算
36 integral = integral + error;
37 derivative = error - lastError;
38 int turn = (int)(Kp * error + Ki * integral + Kd * derivative);
39 lastError = error;
40
41 // 根据turn计算左右电机的占空比
42 Duty_left = 26 + turn;
43 Duty_right = 26 - turn;
44
45 // 确保占空比在0-100之间
46 Duty_left = Duty_left < 0 ? 0 : (Duty_left > 100 ? 100 : Duty_left);
47 Duty_right = Duty_right < 0 ? 0 : (Duty_right > 100 ? 100 : Duty_right);
48}
49
50// 中断服务函数,用于更新PWM信号
51void Timer0_Routine() interrupt 1 {
52 // 更新定时器...
53 // PWM控制代码...
54}
55
56void main() {
57 Timer0_Init(); // 定时器初始化
58 while(1) {
59 CalculatePID(); // 计算PID
60 // 控制电机...
61 }
62}