一、小车底盘组装
根据视频的安装步骤安装
二、 电机模块开发
2.1 L9110s概述
接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,但是不对,根据下节课实际调试
IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转; 倒退
IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转; 前进
IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转; 倒退
IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转; 前进
2.1.1 与C51单片机解法
2.2 让小车动起来
代码看网盘资料,因为分文件编写,不好贴近来,只放核心部分代码
#include "reg52.h"
#include "intrins.h"// 左轮
sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;// 右轮
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;void Delay1000ms() //@11.0592MHz
{unsigned char i, j, k;_nop_();i = 8;j = 1;k = 243;do{do{while (--k);} while (--j);} while (--i);
}void goForward() //前进
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 1;
}void goRight() // 右转弯
{LeftCon1A = 0;LeftCon1B = 1;RightCon1A = 0;RightCon1B = 0;
}void goLeft() // 左转弯
{LeftCon1A = 0;LeftCon1B = 0;RightCon1A = 0;RightCon1B = 1;
}void goBack() // 倒退
{LeftCon1A = 1;LeftCon1B = 0;RightCon1A = 1;RightCon1B = 0;
}void main()
{while(1){goForward(); // 前进Delay1000ms();Delay1000ms();goBack(); // 倒退Delay1000ms();Delay1000ms();goLeft(); // 左转弯Delay1000ms();Delay1000ms();goRight(); // 右转弯Delay1000ms();Delay1000ms();}
}
2.2.1 串口控制小车方向
1. 串口分文件编程进行代码整合——具体过程看课程,主要考验C语言功底和代码调试能力,通过现象来改代码
2. 接入蓝牙模块,通过蓝牙控制小车,实现6.6.1的课程需求,蓝牙透传太容易了。
3. 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不能实现),就能实现前进按键按下后小车一直往前走的功能
#include "reg52.h"
#include "motor.h"
#include "string.h"
#include "delay.h"
sbit D5 = P3^7;
#define SIZE 12sfr AUXR = 0x8E;
char buffer[SIZE];void UartInit(void) //9600bps@11.0592MHz
{AUXR = 0x01;SCON = 0x50; //配置串口工作方式1,REN使能接收TMOD &= 0x0F;TMOD |= 0x20;//定时器1工作方式位8位自动重装TH1 = 0xFD;TL1 = 0xFD;//9600波特率的初值TR1 = 1;//启动定时器EA = 1;//开启总中断ES = 1;//开启串口中断
}//M1qian M2 hou M3 zuo M4 you
void Uart_Handler() interrupt 4
{static int i = 0;//静态变量,被初始化一次char tmp;if(RI)//中断处理函数中,对于接收中断的响应{RI = 0;//清除接收中断标志位tmp = SBUF;if(tmp == 'M'){i = 0;}buffer[i++] = tmp;//灯控指令if(buffer[0] == 'M'){switch(buffer[1]){case '1':goForward();Delay10ms();break;case '2':goBack();Delay10ms();break;case '3':goLeft();Delay10ms();break;case '4':goRight();Delay10ms();break;default:stop();break;}}if(i == 12) {memset(buffer, '\0', SIZE);i = 0;}}}
2.3 如何进行小车PWM调速
原理: 全速前进是LeftCon1A = 0; LeftCon1B = 1;完全停止是LeftCon1A = 0;LeftCon1B = 0;那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!
开发:借用PWM的舵机控制代码
#include "motor.h"
#include "reg52.h"char speed;
char cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < speed){//前进goForward();}else{//停止stop();}if(cnt == 40){//爆表40次,经过了20mscnt = 0; //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms}}extern char speed;void main()
{Time0Init();//UartInit();while(1){speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500usDelay1000ms();Delay1000ms();speed = 20;Delay1000ms();Delay1000ms();speed = 40;Delay1000ms();Delay1000ms();}
}
2.3.1 PWM方式实现小车转向
原理: 左轮定时器0调速,右轮定时器1调速,那么左转就是右轮速度大于左轮!
开发:有手就行
#include "motor.h"
#include "reg52.h"char speedLeft;
char cntLeft = 0;char speedRight;
char cntRight = 0;void Time1Init()
{//1. 配置定时器1工作模式位16位计时TMOD &= 0x0F;TMOD |= 0x1 << 4;//2. 给初值,定一个0.5出来TL1=0x33;TH1=0xFE;//3. 开始计时TR1 = 1;TF1 = 0;//4. 打开定时器1中断ET1 = 1;//5. 打开总中断EAEA = 1;
}void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD = 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void Time1Handler() interrupt 3
{cntRight++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL1=0x33;TH1=0xFE;//控制PWM波if(cntRight < speedRight){//右前进goForwardRight();}else{//停止stopRight();}if(cntRight == 40){//爆表40次,经过了20mscntRight = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}void Time0Handler() interrupt 1
{cntLeft++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cntLeft < speedLeft){//左前进goForwardLeft();}else{//停止stopLeft();}if(cntLeft == 40){//爆表40次,经过了20mscntLeft = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s}}extern char speedLeft;
extern char speedRight;void main()
{Time0Init();Time1Init();//UartInit();while(1){speedLeft = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500usspeedRight = 40;Delay1000ms();Delay1000ms();speedLeft = 40;speedRight = 10;Delay1000ms();Delay1000ms();}
}
2.4 循迹小车
2.4.1 循迹模块使用
TCRT5000传感器的红外发射二极管不断发射红外线
当发射出的红外线没有被反射回来或被反射回来但强度不够大时,
红外接收管一直处于关断状态,此时模块的输出端为高电平,指示二极管一直处于熄灭状态
被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,
此时模块的输出端为低电平,指示二极管被点亮
总结就是一句话,没反射回来,D0输出高电平,灭灯
- 接线方式
-
- VCC:接电源正极(3-5V)
- GND:接电源负极
- DO:TTL开关信号输出0、1
- AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)
6.3.2 循迹小车原理
由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致
循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED
常亮
总结就是一句话,有感应到黑线,D0输出高电平 ,灭灯
循迹模块安装在小车车头两侧下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走
上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
- 代码实现
#include "motor.h"
#include "delay.h"
#include "reg52.h"sbit leftSensor = P2^7;
sbit rightSensor = P2^6;void main()
{//下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走//上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转while(1){if(leftSensor == 0 && rightSensor == 0){goForward(); // q前进}if(leftSensor == 1 && rightSensor == 0){goLeft(); // 左转}if(leftSensor == 0 && rightSensor == 1){goRight(); // 右转}if(leftSensor == 1 && rightSensor == 1){//停stop(); }}
}
修改BUG
防止小车跑车赛道
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"
#include "reg52.h"
extern char speedLeft;
extern char speedRight;sbit leftSensor = P2^7;
sbit rightSensor = P2^6;void main()
{Time0Init(); // 中断器Time1Init();//UartInit();while(1){if(leftSensor == 0 && rightSensor == 0){speedLeft = 32;speedRight = 40;}if(leftSensor == 1 && rightSensor == 0){speedLeft = 12;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500usspeedRight = 40;}if(leftSensor == 0 && rightSensor == 1){speedLeft = 32;speedRight = 20;}if(leftSensor == 1 && rightSensor == 1){//停speedLeft = 0;speedRight = 0;}}
}
2.5 跟随小车/壁障小车
2.5.1 红外壁障模块分析
原理和寻线是一样的,寻线红外观朝下,跟随朝前
6.4.2 跟随小车的原理
左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转
右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转
6.4.3 跟随小车开发和调试
有手就行,哈哈
- 代码实现
#include "motor.h"
#include "delay.h"
#include "reg52.h"//sbit leftSensor = P2^7;
//sbit rightSensor = P2^6;sbit leftSensor = P2^5;
sbit rightSensor = P2^4;void main()
{while(1){if(leftSensor == 0 && rightSensor == 0){goForward();}if(leftSensor == 1 && rightSensor == 0){goRight();}if(leftSensor == 0 && rightSensor == 1){goLeft();}if(leftSensor == 1 && rightSensor == 1){//停stop();}}
}
6.4.4 超声波避障小车
1. 超声波代码模块
超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。
-
- 怎么让它发送波
Trig ,给Trig端口至少10us的高电平
-
- 怎么知道它开始发了
Echo信号,由低电平跳转到高电平,表示开始发送波
-
- 怎么知道接收了返回波
Echo,由高电平跳转回低电平,表示波回来了
-
- 怎么算时间
Echo引脚维持高电平的时间!
波发出去的那一下,开始启动定时器
波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间
-
- 怎么算距离
距离 = 速度 (340m/s)* 时间/2
- 超声波的时序图
- 代码实现
#include "reg52.h"
#include "delay.h"sbit Trig = P1^3;
sbit Echo = P1^2;void Time1Init()
{ TMOD &= 0x0F; //设置定时器模式TMOD |= 0x10;TH1 = 0;TL1 = 0;//设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}void startHC()
{Trig = 0;Trig = 1;Delay10us();Trig = 0;
}double get_distance()
{double time;//定时器数据清零,以便下一次测距TH1 = 0;TL1 = 0;//1. Trig ,给Trig端口至少10us的高电平startHC();//2. echo由低电平跳转到高电平,表示开始发送波while(Echo == 0);//波发出去的那一下,开始启动定时器TR1 = 1;//3. 由高电平跳转回低电平,表示波回来了while(Echo == 1);//波回来的那一下,我们开始停止定时器TR1 = 0;//4. 计算出中间经过多少时间time = (TH1 * 256 + TL1)*1.085;//us为单位//5. 距离 = 速度 (340m/s)* 时间/2return (time * 0.017);
}
2. sg90舵机代码模块
- 怎么控制舵机
向黄色信号线“灌入”PWM信号。
PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右
数据:
0.5ms-------------0度; 2.5% 对应函数中占空比为250
1.0ms------------45度; 5.0% 对应函数中占空比为500
1.5ms------------90度; 7.5% 对应函数中占空比为750
2.0ms-----------135度; 10.0% 对应函数中占空比为1000
2.5ms-----------180度; 12.5% 对应函数中占空比为1250
定时器需要定时20ms, 关心的单位0.5ms, 40个的0.5ms,初值0.5m cnt++
1s = 10ms * 100
20ms = 0.5ms * 40
- 编程实现
#include "reg52.h"
#include "delay.h"sbit sg90_con = P1^1;int jd;
int cnt = 0;void Time0Init()
{//1. 配置定时器0工作模式位16位计时TMOD &= 0xF0; //设置定时器模式TMOD |= 0x01;//2. 给初值,定一个0.5出来TL0=0x33;TH0=0xFE;//3. 开始计时TR0 = 1;TF0 = 0;//4. 打开定时器0中断ET0 = 1;//5. 打开总中断EAEA = 1;
}void sgMiddle()
{//中间位置jd = 3; //90度 1.5ms高电平cnt = 0;
}void sgLeft()
{//左边位置jd = 5; //135度 1.5ms高电平cnt = 0;
}void sgRight()
{//右边位置jd = 1; //0度cnt = 0;
}void Time0Handler() interrupt 1
{cnt++; //统计爆表的次数. cnt=1的时候,报表了1//重新给初值TL0=0x33;TH0=0xFE;//控制PWM波if(cnt < jd){sg90_con = 1;}else{sg90_con = 0;}if(cnt == 40){//爆表40次,经过了20mscnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1ssg90_con = 1;}}
3. mian函数组合调用
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"void main()
{Time0Init();Time1Init();//舵机的初始位置sgMiddle();Delay300ms();Delay300ms();while(1){sgLeft(); // 左摇头Delay440ms();sgMiddle(); // 回正Delay430ms();sgRight(); // 右摇头Delay420ms();sgMiddle(); // 回正Delay450ms();}
}
4. 测距摇头实现
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"#define MIDDLE 0
#define LEFT 1
#define RIGHT 2void main()
{char dir; // 方向变量double disMiddle; // 中间距离double disLeft; // 左间距离double disRight; // 右间距离Time0Init();Time1Init();//舵机的初始位置sgMiddle();Delay300ms();Delay300ms();dir = MIDDLE;while(1){if(dir != MIDDLE){ // 保持测完左右间距之后,回到中间值sgMiddle();dir = MIDDLE;Delay300ms();}disMiddle = get_distance(); // 超声波测距if(disMiddle > 35){ // 如果中间的距离大于了35cm;就前进//前进}else // 如果前方的距离小于了35cm;就停止{ //停止//测左边距离sgLeft();Delay300ms();disLeft = get_distance();sgMiddle(); // 测中间距离Delay300ms();sgRight(); // 测右间距离dir = RIGHT;Delay300ms();disRight = get_distance();}}
}
main函数先调用两个定时器(超声波与sg90舵机定时器);22行至25行代码。将超声波模块保持正前方向之后,进入while循环语句,先进入第一个if语句判断超声波方向,如果超声波方向不在正中间,将其回正之后,将超声波测量的距离,赋值给第二个if语句,进去判断,如何距离大于35cm,小车前进,如果距离不大于35,超声波通过sg90舵机,进行左右测距,测出35cm内无障碍之后,进行前进。
- 下面是加上电机驱动的代码,让小车跑起来
#include "reg52.h" // 包含寄存器定义头文件
#include "hc04.h" // 包含超声波传感器(HC-SR04)头文件
#include "delay.h" // 包含延时函数头文件
#include "sg90.h" // 包含舵机(SG90)头文件
#include "motor.h" // 包含电机控制头文件#define MIDDLE 0 // 定义舵机的中间位置为 0
#define LEFT 1 // 定义舵机的左边位置为 1
#define RIGHT 2 // 定义舵机的右边位置为 2void main()
{char dir; // 定义舵机的当前方向变量double disMiddle; // 定义前方的距离变量double disLeft; // 定义左方的距离变量double disRight; // 定义右方的距离变量Time0Init(); // 初始化定时器0Time1Init(); // 初始化定时器1// 舵机的初始位置设为中间位置sgMiddle();Delay300ms(); // 延时300毫秒Delay300ms(); // 再延时300毫秒dir = MIDDLE; // 将舵机方向变量设置为中间位置while(1){ // 无限循环,控制主逻辑if(dir != MIDDLE){ // 如果当前方向不是中间sgMiddle(); // 将舵机调整到中间位置dir = MIDDLE; // 更新方向变量为中间Delay300ms(); // 延时300毫秒}disMiddle = get_distance(); // 获取前方距离if(disMiddle > 35){ // 如果前方距离大于35goForward(); // 向前移动}else if(disMiddle < 10){ // 如果前方距离小于10goBack(); // 向后移动Delay150ms(); // 延时150毫秒stop(); // 停止移动}else{// 停止移动stop();// 测量左边的距离sgLeft();Delay300ms(); // 延时300毫秒disLeft = get_distance(); // 获取左边距离sgMiddle(); // 将舵机回到中间位置Delay300ms(); // 延时300毫秒sgRight(); // 将舵机转向右边dir = RIGHT; // 更新方向变量为右边Delay300ms(); // 延时300毫秒disRight = get_distance(); // 获取右边距离if(disLeft < disRight){ // 如果左边距离小于右边距离goRight(); // 向右移动Delay150ms(); // 延时150毫秒stop(); // 停止移动}if(disRight < disLeft){ // 如果右边距离小于左边距离goLeft(); // 向左移动Delay150ms(); // 延时150毫秒stop(); // 停止移动}}}
}
大家在实际操作中,可根据现场实际情况进行对代码测距的修改。
关于相关代码,si信我即可。