“无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【上】

"无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【上】

前言

  本篇博文介绍的是用51单片机的最终项目《智能语音小车》【上】,包含L9110S电机控制器接线, L9110前后左右控制小车,电机相关代码封装–分文件,串口控制小车,手机通过蓝牙控制小车–自定义按键,蓝牙小车的点动控制,PWM软件调速,左右电机的各自调速管理。看到这篇博文的朋友,可以先赞再看吗?

预备知识

  一、需要我之前写的所有博文的知识,如果还没看我之前的的博文,可以去看看后再看本篇博文
在这里插入图片描述
  二、C变量
  三、基本输入输出
  四、流程控制
  五、函数

  六、指针
  七、字符串

  如果以上知识不清楚,请自行学习后再来浏览。如果我有没例出的,请在评论区写一下。谢谢啦!

1. L9110S电机控制器接线

1.1 L9110S概述

  L9110S是一种双路H桥驱动器芯片,通常用于控制直流电机或步进电机。该芯片具有内置的双H桥驱动器,可实现电机的双向控制。它可以通过控制输入信号来控制电机的转向和速度,并且通常被广泛应用于各种小型电动车辆、机器人以及其他需要电机控制的项目中。L9110S还具有过流保护功能,能够保护电机和驱动器免受损坏。

1.2 L9110S IO口描述

  以下资料来源官方,但是不对

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

1.3 L9110S 实物图

在这里插入图片描述

1.4 L9110S与单片机接线

  • 注意:电源线千万别接错,不然会在0.5秒内烧坏模块,电源正常接通电源指示灯会亮。

在这里插入图片描述

2. L9110前后左右控制小车

2.1 L9110前后左右控制小车

  • 分别用高低电平测试B-1AB-1B ``L9110电机驱动模块引脚,A-1AA-1B ``L9110电机驱动模块引脚。
  • 将测试结果封装函数
  • 封装前后左右行走函数

2.1分别用高低电平测试B-1A和B-1B L9110电机驱动模块引脚,A-1A和A-1B L9110电机驱动模块引脚。

  • B-1A1B-1B0,将A-1A1A-1B0。看一下电机的转向。

  • 代码体现

RightControA = 1;
RightControB = 0;
	
LeftControA  = 1;
LeftControB  = 0;
  • 测试结果为小车向后跑,也就电机反转。

2.2 将测试结果封装函数

  • 测试结果为小车向前跑,封装向前函数。

  • 代码体现。

void goBack()
{
	RightControA = 1;
	RightControB = 0;
	
	LeftControA  = 1;
	LeftControB  = 0;
}

2.3封装前后左右行走函数

  • 向前函数封装思路

  将向后函数的高低电平兑换就实现了向前功能。

  • 代码体现
void goFront()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 1;
}
  • 向左和向右行走函数思路

  如果要实现向左行走就将控制左轮的引脚置零,实现向右行走就将控制右轮的引脚置零。这样就实现了向左和向右行走。

  • 代码体现
void goLeft()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

void goRight()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

2.4主函数内进行间隔2秒调用前后左右函数

  • 代码体现
void main()
{
	while(1)
	{
		goFront();
		Delay2000ms();
		goBack();
		Delay2000ms();
		goLeft();
		Delay2000ms();
		goRight();
		Delay2000ms();
	}
}

2.5完整程序代码

#include "reg52.h"
#include "intrins.h"

sbit RightControA = P3^2;         //右轮控制A
sbit RightControB = P3^3;         //右轮控制B
sbit LeftControA  = P3^4;         //左轮控制A
sbit LeftControB  = P3^5;         //左轮控制B

void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void goLeft()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

void goRight()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void goBack()
{
	RightControA = 1;
	RightControB = 0;
	
	LeftControA  = 1;
	LeftControB  = 0;
}

void goFront()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void main()
{
	while(1)
	{
		goFront();
		Delay2000ms();
		goBack();
		Delay2000ms();
		goLeft();
		Delay2000ms();
		goRight();
		Delay2000ms();
	}
}

3.电机相关代码封装_分文件

3.1电机相关代码封装_分文件核心思路

  • 将控制电机前进,后退,向左,向右的函数封装到电机对应的C文件中。

  • 建立电机头文件,里面声明电机对应C文件中的函数

  • 将延时2秒的函数封装到延时C文件中

  • 建立延时头文件,里面声明延时C文件中的函数。

  • 主函数中包含电机头文件和延时头文件即可完美运行程序

  注:此工程基于L9110前后左右控制小车工程开发

3.2将控制电机前进,后退,向左,向右的函数封装到电机对应的C文件中。

  • 将声明电机控制引脚代码移动电机对应的C文件中(具体如何建立分文件请看温湿度检测系统博文),代码体现如下。
sbit RightControA = P3^2;         //右轮控制A
sbit RightControB = P3^3;         //右轮控制B
sbit LeftControA  = P3^4;         //左轮控制A
sbit LeftControB  = P3^5;         //左轮控制B
  • 将控制电机前进,后退,向左,向右的函数移动到电机对应的C文件中。代码体现如下
void goLeft()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

void goRight()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void goBack()
{
	RightControA = 1;
	RightControB = 0;
	
	LeftControA  = 1;
	LeftControB  = 0;
}

void goFront()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 1;
}
  • 电机对的C文件代码截图

在这里插入图片描述

3.3建立电机头文件,里面声明电机对应C文件中的函数

  • 需要声明的函数代码如下。

void goLeft();

void goRight();

void goBack();

void goFront();
  • 电机头文件代码截图

在这里插入图片描述

3.4将延时2秒的函数封装到延时C文件中

  • 将延时2秒的函数封装到延时C文件中代码体现
void Delay2000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 15;
	j = 2;
	k = 235;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
  • 延时C文件代码截图

在这里插入图片描述

3.5建立延时头文件,里面声明延时C文件中的函数。

  • 需要声明的函数代码如下。
void Delay2000ms();
  • 延时头文件代码截图

在这里插入图片描述

3.6主函数中包含电机头文件和延时头文件即可完美运行程序

  • 主函数中包含电机头文件和延时头文件代码体现
#include "motor.h"
#include "delay.h"
  • 主函数代码截图

在这里插入图片描述

4.串口控制小车

4.1串口控制小车核心思路

  • 找到wifi开灯项目中优化8266,捕获联网失败的状态工程打开主函数
  • 拷贝串口使用相关声明定义串口初始化函数串口中断函数建立串口C文件和头文件
  • 在电机对应C文件中构造小车停止函数
  • 在串口中断函数中通过接收指令操作小车运行

  注:此工程由电机相关代码封装_分文件工程开发

4.2找到wifi开灯项目中优化8266,捕获联网失败的状态工程打开

在这里插入图片描述

4.3拷贝串口使用相关声明定义,串口初始化函数,串口中断函数,建立串口C文件和头文件。

  • 拷贝串口使用相关声明定义
#include "reg52.h"
#include "string.h"
#define SIZE 32

sfr AUXR = 0x8e;   //声明AUXR寄存器地址
char buffer[SIZE]; 
  • 拷贝串口初始化函数
void UartInit(void)		//自己配
{
	//配置串口工作方式为方式1,从只收不发改为能收能发
	SCON =  0x50;
  //配置辅助寄存器,减少电磁辐射,稳定晶振频率  
	AUXR =  0x01;
	//设置定时器工作方式为定时器1的8位自动重装
	TMOD &= 0x0F;
	TMOD |= 0x20;
	//设置串口波特率为9600,0误差
	TH1   = 0xFD;
	TL1   = 0xFD;
	//打开定时器1
	TR1   = 1;
	//打开总中断
	EA = 1;
	//打开串口中断
	ES = 1;
}
  • 拷贝串口中断函数
void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='W' || tmp=='O' || tmp=='L' || tmp=='F')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			
			/*if(buffer[0]=='W' &&  buffer[5]=='G')
			{
				con_Net_Flag = 1;
				memset(buffer,'\0',SIZE);
			}*/
			if(buffer[0]=='O' && buffer[1]=='K')
			{
				at_OK_Flag   = 1;
				memset(buffer,'\0',SIZE);
			}
			if(buffer[0]=='F' && buffer[1] == 'A')
			{
				statusLED1();
				sendString(RESET);
				Delay1000ms();
				sendString(cNetwork);
				memset(buffer,'\0',SIZE);
			}
			//如果用1指令开灯,0指令关灯
			if(buffer[0]=='L' && buffer[2]=='1')  
			{
				LED1 = 0;
				memset(buffer,'\0',SIZE);
			}
			if(buffer[0]=='L' && buffer[2]=='0')
			{
				LED1 = 1;
				memset(buffer,'\0',SIZE);
			}
			
			if(i == SIZE)
			{
				i = 0;
			}
		}
		
	
	if(TI);
		
}
  • 建立串口C文件和头文件。

  一、将拷贝的所有代码整合到一起文件命名为uart.c。

#include "reg52.h"
#include "string.h"
#define SIZE 32

sfr AUXR = 0x8e;   //声明AUXR寄存器地址
char buffer[SIZE]; 

void UartInit(void)		//自己配
{
	//配置串口工作方式为方式1,从只收不发改为能收能发
	SCON =  0x50;
  //配置辅助寄存器,减少电磁辐射,稳定晶振频率  
	AUXR =  0x01;
	//设置定时器工作方式为定时器1的8位自动重装
	TMOD &= 0x0F;
	TMOD |= 0x20;
	//设置串口波特率为9600,0误差
	TH1   = 0xFD;
	TL1   = 0xFD;
	//打开定时器1
	TR1   = 1;
	//打开总中断
	EA = 1;
	//打开串口中断
	ES = 1;
}

void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='W' || tmp=='O' || tmp=='L' || tmp=='F')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			
			/*if(buffer[0]=='W' &&  buffer[5]=='G')
			{
				con_Net_Flag = 1;
				memset(buffer,'\0',SIZE);
			}*/
			if(buffer[0]=='O' && buffer[1]=='K')
			{
				at_OK_Flag   = 1;
				memset(buffer,'\0',SIZE);
			}
			if(buffer[0]=='F' && buffer[1] == 'A')
			{
				statusLED1();
				sendString(RESET);
				Delay1000ms();
				sendString(cNetwork);
				memset(buffer,'\0',SIZE);
			}
			//如果用1指令开灯,0指令关灯
			if(buffer[0]=='L' && buffer[2]=='1')  
			{
				LED1 = 0;
				memset(buffer,'\0',SIZE);
			}
			if(buffer[0]=='L' && buffer[2]=='0')
			{
				LED1 = 1;
				memset(buffer,'\0',SIZE);
			}
			
			if(i == SIZE)
			{
				i = 0;
			}
		}
		
	
	if(TI);
		
}

  二、建立uart.h文件,里面声明串口初始化函数。

void UartInit(void);

4.4在电机对应C文件中构造小车停止函数

  • 函数思路

  将左右轮控制信号的A、B给与低电平信号,实现轮子停止转动。

  • 函数代码
void stop()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

4.5在串口中断函数中通过接收指令操作小车运行

  • 在串口中断函数中通过接收指令操作小车运行思路

  一、将串口中断函数中的13行if(tmp\=='W' || tmp\=='O' || tmp\=='L' || tmp=='F')改为if(tmp\=='M')

  二、删除串口中断函数中19到47行代码

  三、使用if和switch嵌套进行操作小车

  四、重要bug:串口能收到指令却无法操作小车。解决办法:不在if和switch嵌套中使用清理字符串函数,在防止指针越界函数中使用

  • 在串口中断函数中通过接收指令操作小车运行代码
void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='M')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			if(buffer[0]=='M')
			{
				switch(buffer[1])
				{
					case '1':
						goFront();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
				
			}
			
			
			if(i == SIZE)
			{
				i = 0;
				memset(buffer,'\0',SIZE);
			}
		}
		
	
	if(TI);
		
}

4.6串口C文件代码

#include "reg52.h"
#include "motor.h"
#include "string.h"

#define SIZE 32

sfr AUXR = 0x8e;   //声明AUXR寄存器地址
char buffer[SIZE];

void UartInit()		//自己配
{
	//配置串口工作方式为方式1,从只收不发改为能收能发
	SCON =  0x50;
  //配置辅助寄存器,减少电磁辐射,稳定晶振频率  
	AUXR =  0x01;
	//设置定时器工作方式为定时器1的8位自动重装
	TMOD &= 0x0F;
	TMOD |= 0x20;
	//设置串口波特率为9600,0误差
	TH1   = 0xFD;
	TL1   = 0xFD;
	//打开定时器1
	TR1   = 1;
	//打开总中断
	EA = 1;
	//打开串口中断
	ES = 1;
}

void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='M')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			if(buffer[0]=='M')
			{
				switch(buffer[1])
				{
					case '1':
						goFront();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
				
			}
			
			
			if(i == SIZE)
			{
				i = 0;
				memset(buffer,'\0',SIZE);
			}
		}
		
	
	if(TI);
		
}

5.手机通过蓝牙控制小车_自定义按键

5.1手机通过蓝牙控制小车_自定义按键核心思路

  • 将蓝牙模块正确接线连接51单片机串口
  • 给51单片机,蓝牙模块上电,打开手机APP HC蓝牙助手进行连接
  • 通过自定义按钮使HC蓝牙助手发送相应控制小车指令

5.2将蓝牙模块正确接线连接51单片机串口

  相关接线请看我写的串口通信博文

5.3给51单片机,蓝牙模块上电,打开手机APP HC蓝牙助手进行连接

在这里插入图片描述
在这里插入图片描述

5.4通过自定义按钮使HC蓝牙助手发送相应控制小车指令

  1. 点击自定义按钮

在这里插入图片描述

  1. 点击设置方向按钮

在这里插入图片描述

  1. 在设置按钮名称处填写你需要设置的按钮名称,在设置按钮内容处填写你需要发送的指令,最后点击确认

在这里插入图片描述

  1. 依次设置完、五个指令后便可实现手机蓝牙控制小车。

6.蓝牙小车的点动控制

6.1蓝牙小车的点动控制核心思路

  • 主C文件主函数while(1)死循环中调用电机停止函数
  • 在串口中断函数中接收指令并执行调用电机相关函数时延时10毫秒实现点动
  • 通过串口助手自动发送模拟长按一直发送一直运行的状态。

  注:此工程由串口控制小车工程开发

6.2在主C文件主函数while(1)死循环中调用电机停止函数。

  • 函数调用代码
while(1)
{
    stop();
}
  • 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"


void main()
{
	UartInit();
	while(1)
	{
		stop();
	}
}

6.3在串口中断函数中接收指令并执行调用电机相关函数时延时10毫秒实现点动

  • 延时10毫秒函数代码
void Delay10ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 18;
	j = 235;
	do
	{
		while (--j);
	} while (--i);
}
  • 在串口中断函数中调用代码
if(buffer[0]=='M')
{
    switch(buffer[1])
    {
        case '1':
            goFront();
            Delay10ms();
            break;
        case '2':
            goBack();
            Delay10ms();
            break;
        case '3':
            goLeft();
            Delay10ms();
            break;
        case '4':
            goRight();
            Delay10ms();
            break;
        default:
            stop();
            break;
    }

}
  • 串口C文件代码
#include "reg52.h"
#include "motor.h"
#include "string.h"
#include "delay.h"

#define SIZE 32

sfr AUXR = 0x8e;   //声明AUXR寄存器地址
char buffer[SIZE];

void UartInit()		//自己配
{
	//配置串口工作方式为方式1,从只收不发改为能收能发
	SCON =  0x50;
  //配置辅助寄存器,减少电磁辐射,稳定晶振频率  
	AUXR =  0x01;
	//设置定时器工作方式为定时器1的8位自动重装
	TMOD &= 0x0F;
	TMOD |= 0x20;
	//设置串口波特率为9600,0误差
	TH1   = 0xFD;
	TL1   = 0xFD;
	//打开定时器1
	TR1   = 1;
	//打开总中断
	EA = 1;
	//打开串口中断
	ES = 1;
}

void UART_handler() interrupt 4
{
	//定义临时变量tmp用于判断接收的字符是否是需要的首字符。
	char tmp;
	//定义一个静态整型变量,在多次函数调用中只被执行一次初始化
	static int i = 0;
	//在串口中段函数中可以对发送接收中断标志进行处理
	if(RI == 1)
		{
			RI = 0;     //必须软件置零
			
			tmp = SBUF;
			if(tmp=='M')
			{
				i = 0;
			}
			buffer[i++] = tmp;
			if(buffer[0]=='M')
			{
				switch(buffer[1])
				{
					case '1':
						goFront();
						Delay10ms();
						break;
					case '2':
						goBack();
						Delay10ms();
						break;
					case '3':
						goLeft();
						Delay10ms();
						break;
					case '4':
						goRight();
						Delay10ms();
						break;
					default:
						stop();
						break;
				}
				
			}
			
			
			if(i == SIZE)
			{
				i = 0;
				memset(buffer,'\0',SIZE);
			}
		}
		
	
	if(TI);
		
}

6.4通过串口助手自动发送模拟长按一直发送一直运行的状态。

  • 打开单片机下载器点击串口助手

在这里插入图片描述

  • 打开串口,写入指令M1,设置发送周期为1ms,点击自动发送。

在这里插入图片描述

  • 观察小车车轮,小车正在前行,说明模拟成功。

7.PWM软件调速

7.1 PWM软件调速核心思路

  • 打开感应开盖垃圾桶项目_舵机编程实战工程主C文件
  • 在工程内新建time0.c文件
  • 拷贝相关变量定时器0初始化函数定时器零中断函数
  • 修改定时器0中断函数,使之调用电机控制函数,实现调速
  • 建立time.h文件
  • 主C文件内每隔1.5秒改变速度变量,使小车电机变速

  注:此工程由蓝牙小车的点动控制工程开发

7.2打开感应开盖垃圾桶项目_舵机编程实战工程主C文件

在这里插入图片描述

7.3在工程内新建time0.c文件

在这里插入图片描述

7.4拷贝相关变量,定时器0初始化函数,定时器零中断函数

  • 拷贝相关变量代码
int cnt = 0;     //记录爆表次数
int angle;       //记录舵机角度变量

  修改angle为speed,也就是修改记录舵机角度变量为记录小车速度变量。把这两个变量类型修改为char型,节省空间

char cnt = 0;			//记录爆表次数
char speed = 0;  		 //调速变量
  • 拷贝定时器0初始化函数代码
void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}
  • 拷贝定时器零中断函数
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < angle)
	{
     sg90_con = 1;
	}
	else
	{
     sg90_con = 0;
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 20)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	   sg90_con = 1;//定时到20豪秒,重新给舵机控制线一个高电平
	}
}

7.5修改定时器0中断函数,使之调用电机控制函数,实现调速

  • 把定时器零中断函数中第9行if(cnt < angle)修改为if(cnt < speed)
  • 把定时器零中断函数中第11行sg90_con = 1修改为goFront();
  • 把定时器零中断函数中第15行sg90_con = 0修改为stop();
  • 把定时器零中断函数中第21行sg90_con = 1;删除。
  • 添加motor.h头文件。
  • 修改后的函数代码
void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < speed)
	{
    //前进
		goFront();
	}
	else
	{
    //停止
		stop();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	}
}
  • time0.c文件中的代码
#include "reg52.h"
#include "motor.h"
char cnt = 0;			//记录爆表次数
char speed = 0;   //调速变量

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < speed)
	{
    //前进
		goFront();
	}
	else
	{
    //停止
		stop();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	}
}

7.6建立time.h文件

  • 在time0.h中声明定时器0初始化函数
  • 代码体现
void initTime0();

7.7主C文件内每隔1.5秒改变速度变量,使小车电机变速

  • 在主函数中调用定时器0初始化函数
initTime0();
  • 在主C文件中运用extern关键字声明速度变量char speed。
extern char speed;
  • 在主函数while(1)死循环中每隔1.5秒改变速度变量的值。
while(1)
{
    speed = 10;
    Delay1500ms();
    speed = 15;
    Delay1500ms();
    speed = 20;
    Delay1500ms();
    speed = 25;
    Delay1500ms();
    speed = 30;
    Delay1500ms();
    speed = 35;
    Delay1500ms();
    speed = 40;
    Delay1500ms();
}
  • 在delay.c文件中建立延时1.5秒函数。
void Delay1500ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 11;
	j = 130;
	k = 111;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
  • 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"

extern char speed;


void main()
{
	initTime0();
	UartInit();
	while(1)
	{
		speed = 10;     //10为最小速度驱动,再小就无法运行
		Delay1500ms();
		speed = 15;
		Delay1500ms();
		speed = 20;
		Delay1500ms();
		speed = 25;
		Delay1500ms();
		speed = 30;
		Delay1500ms();
		speed = 35;
		Delay1500ms();
		speed = 40;
		Delay1500ms();
	}
}

7.8 PWM软件调速原理图

在这里插入图片描述

8.左右电机的各自调速管理

8.1左右电机的各自调速管理核心思路

  • motor.c文件内建立单独控制左右轮前进和停止函数,并在motor.h文件中声明。

  • 建立time1.ctime1.h文件用于对右轮的控制和初始化。

  • 修改time0.c左轮控制

  • 主C文件内每隔1.5秒改变左右轮速度变量,使小车左右轮电机差速运行

  注:此工程由PWM软件调速开发

8.2在motor.c文件内建立单独控制左右轮前进和停止函数,并在motor.h文件中声明。

  • 复制前进函数,修改名字为goFrontLeft()。删除右轮代码。
修改前
void goFront()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

修改后
 void goFrontLeft()
{
	
	LeftControA  = 0;
	LeftControB  = 1;
}

  按照以上步骤可以建立右轮控制函数

void goFrontRight()
{
	RightControA = 0;
	RightControB = 1;
}
  • 复制停止函数,修改名为stopLeft()。删除右轮代码
修改前
void stop()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

修改后
void stopLeft()
{
	LeftControA  = 0;
	LeftControB  = 0;
}

  按照以上步骤可以建立右轮停止函数函数

void stopRight()
{
	RightControA = 0;
	RightControB = 0;
}
  • 在motor.h中声明这四个函数
void goFrontLeft();

void goFrontRight();

void stopLeft();

void stopRight();
  • motor.c文件中的代码
#include "reg52.h"

sbit RightControA = P3^2;         //右轮控制A
sbit RightControB = P3^3;         //右轮控制B
sbit LeftControA  = P3^4;         //左轮控制A
sbit LeftControB  = P3^5;         //左轮控制B

void goLeft()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

void goRight()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void goBack()
{
	RightControA = 1;
	RightControB = 0;
	
	LeftControA  = 1;
	LeftControB  = 0;
}

void goFront()
{
	RightControA = 0;
	RightControB = 1;
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void stop()
{
	RightControA = 0;
	RightControB = 0;
	
	LeftControA  = 0;
	LeftControB  = 0;
}

void goFrontLeft()
{
	
	LeftControA  = 0;
	LeftControB  = 1;
}

void goFrontRight()
{
	RightControA = 0;
	RightControB = 1;
}

void stopLeft()
{
	LeftControA  = 0;
	LeftControB  = 0;
}

void stopRight()
{
	RightControA = 0;
	RightControB = 0;
}
  • motor.h文件中的代码

void goLeft();

void goRight();

void goBack();

void goFront();

void stop();

void goFrontLeft();

void goFrontRight();

void stopLeft();

void stopRight();

8.3建立time1.c和time1.h文件用于对右轮的控制和初始化。

  1. 拷贝time0.c文件代码到time1.c文件中
#include "reg52.h"
#include "motor.h"
char cnt = 0;			//记录爆表次数
char speed = 0;   //调速变量

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cnt++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cnt < speed)
	{
    //前进
		goFront();
	}
	else
	{
    //停止
		stop();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cnt == 40)
	{
	   cnt = 0;     //定时到20豪秒,cnt从0开始加
	}
}
  1. initTime0()修改为initTime1(),将所有的TH0TL0替换为TH1TL1。将TMOD的两个运算表达式后面的变量替换为0x0F0x01 << 4;实现使用定时器116位模式。将TR0TF0ET0修改为TR1TF1ET1
void initTime1() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0x0F;
	TMOD |= 0x01 << 4;
	
	//2.设置初值,定时0.5毫秒
	TL1 = 0x33;
	TH1 = 0xFE;
	
	//3.启动定时器,开始计时
	TR1 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF1 = 0;
	//4.打开定时器0的中断 赋1打开
	ET1 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}
  1. Time0Handler() 修改为 Time1Handler()interrupt 1 修改为 interrupt 3cnt 修改为 cntRightspeed修改为speedRightgoFront();修改为goFrontRight();stop();修改为stopRight();
void Time1Handler() interrupt 3 //定时器T0中断函数,中断号为interrupt 1
{
	cntRight++;
	//重新给定时器赋初值,定时0.5毫秒
	TL1 = 0x33;
	TH1 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cntRight < speedRight)
	{
    //前进
		goFrontRight();
	}
	else
	{
    //停止
		stopRight();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cntRight == 40)
	{
	   cntRight = 0;     //定时到20豪秒,cnt从0开始加
	}
}
  1. 修改后的代码为
#include "reg52.h"
#include "motor.h"
char cntRight = 0;                            			//记录爆表次数
char speedRight = 0;   //调速变量

void initTime1() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0x0F;
	TMOD |= 0x01 << 4;
	
	//2.设置初值,定时0.5毫秒
	TL1 = 0x33;
	TH1 = 0xFE;
	
	//3.启动定时器,开始计时
	TR1 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF1 = 0;
	//4.打开定时器0的中断 赋1打开
	ET1 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Time1Handler() interrupt 3 //定时器T0中断函数,中断号为interrupt 1
{
	cntRight++;
	//重新给定时器赋初值,定时0.5毫秒
	TL1 = 0x33;
	TH1 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cntRight < speedRight)
	{
    //前进
		goFrontRight();
	}
	else
	{
    //停止
		stopRight();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cntRight == 40)
	{
	   cntRight = 0;     //定时到20豪秒,cnt从0开始加
	}
}

8.4修改time0.c为左轮控制

  1. cnt修改为cntLeftspeed修改为speedLeftgoFront();修改为goFrontLeft();stop();修改为stopLeft();
  2. 修改后的代码为
#include "reg52.h"
#include "motor.h"
char cntLeft = 0;			//记录爆表次数
char speedLeft = 0;   //调速变量

void initTime0() //初始化定时器T0
{
	//1.设置定时器为T0,16位模式
	TMOD &= 0xF0;
	TMOD |= 0x01;
	
	//2.设置初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//3.启动定时器,开始计时
	TR0 = 1; //TR0为启动定时器标志,值为1启动定时器
	TF0 = 0;
	//4.打开定时器0的中断 赋1打开
	ET0 = 1;
	//5.打开总中断EA  赋1打开
	EA  = 1;
}

void Time0Handler() interrupt 1 //定时器T0中断函数,中断号为interrupt 1
{
	cntLeft ++;
	//重新给定时器赋初值,定时0.5毫秒
	TL0 = 0x33;
	TH0 = 0xFE;
	
	//判读记录爆表变量cnt的值是否小于角度变量的值,小于继续给sg90舵机控制线高电平,大于等于则给低电平
	if(cntLeft  < speedLeft)
	{
    //前进
		goFrontLeft();
	}
	else
	{
    //停止
		stopLeft();
	}
	//判断cnt是否加到40,定时是否20毫秒PWM波形周期
	if(cntLeft  == 40)
	{
	   cntLeft  = 0;     //定时到20豪秒,cnt从0开始加
	}
}

8.5主C文件内每隔1.5秒改变左右轮速度变量,使小车左右轮电机差速运行

  1. 调速变量修改代码
while(1)
{
    speedRight = 40;
    speedLeft  = 40;
    Delay1500ms();
    speedRight = 10;     //10为最小速度驱动,再小就无法运行
    speedLeft  = 40;
    Delay1500ms();
    speedRight = 15;
    speedLeft  = 35;
    Delay1500ms();
    speedRight = 20;
    speedLeft  = 30;
    Delay1500ms();
    speedRight = 25;
    speedLeft  = 25;
    Delay1500ms();
    speedRight = 30;
    speedLeft  = 20;
    Delay1500ms();
    speedRight = 35;
    speedLeft  = 15;
    Delay1500ms();
    speedRight = 40;
    speedLeft  = 10;
    Delay1500ms();
}
  1. 主C文件代码
#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time0.h"
#include "time1.h"

extern char speedRight;
extern char speedLeft;


void main()
{
	initTime0();
	initTime1();
	UartInit();
	while(1)
	{
		speedRight = 40;
		speedLeft  = 40;
		Delay1500ms();
		speedRight = 10;     //10为最小速度驱动,再小就无法运行
		speedLeft  = 40;
		Delay1500ms();
		speedRight = 15;
		speedLeft  = 35;
		Delay1500ms();
		speedRight = 20;
		speedLeft  = 30;
		Delay1500ms();
		speedRight = 25;
		speedLeft  = 25;
		Delay1500ms();
		speedRight = 30;
		speedLeft  = 20;
		Delay1500ms();
		speedRight = 35;
		speedLeft  = 15;
		Delay1500ms();
		speedRight = 40;
		speedLeft  = 10;
		Delay1500ms();
	}
}

结束语

  很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值