一、小车底盘组装
根据视频的安装步骤安装
二、 电机模块开发
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 串口控制小车方向
-
串口分文件编程进行代码整合------具体过程看课程,主要考验C语言功底和代码调试能力,通过现象来改代码
-
接入蓝牙模块,通过蓝牙控制小车,实现6.6.1的课程需求,蓝牙透传太容易了。
-
添加点动控制,如果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. 打开总中断EA
EA = 1;
}
void Time0Handler() interrupt 1
{
cnt++; //统计爆表的次数. cnt=1的时候,报表了1
//重新给初值
TL0=0x33;
TH0=0xFE;
//控制PWM波
if(cnt < speed){
//前进
goForward();
}else{
//停止
stop();
}
if(cnt == 40){//爆表40次,经过了20ms
cnt = 0; //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms
}
}
extern char speed;
void main()
{
Time0Init();
//UartInit();
while(1){
speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
Delay1000ms();
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. 打开总中断EA
EA = 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. 打开总中断EA
EA = 1;
}
void Time1Handler() interrupt 3
{
cntRight++; //统计爆表的次数. cnt=1的时候,报表了1
//重新给初值
TL1=0x33;
TH1=0xFE;
//控制PWM波
if(cntRight < speedRight){
//右前进
goForwardRight();
}else{
//停止
stopRight();
}
if(cntRight == 40){//爆表40次,经过了20ms
cntRight = 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次,经过了20ms
cntLeft = 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份的500us
speedRight = 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份的500us
speedRight = 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) 时间/2
return (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. 打开总中断EA
EA = 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次,经过了20ms
cnt = 0; //当100次表示1s,重新让cnt从0开始,计算下一次的1s
sg90_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 2
void 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(); // 初始化定时器0 Time1Init(); // 初始化定时器1 // 舵机的初始位置设为中间位置 sgMiddle(); Delay300ms(); // 延时300毫秒 Delay300ms(); // 再延时300毫秒 dir = MIDDLE; // 将舵机方向变量设置为中间位置 while(1){ // 无限循环,控制主逻辑 if(dir != MIDDLE){ // 如果当前方向不是中间 sgMiddle(); // 将舵机调整到中间位置 dir = MIDDLE; // 更新方向变量为中间 Delay300ms(); // 延时300毫秒 } disMiddle = get_distance(); // 获取前方距离 if(disMiddle > 35){ // 如果前方距离大于35 goForward(); // 向前移动 }else if(disMiddle < 10){ // 如果前方距离小于10 goBack(); // 向后移动 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信我即可。