"无限交互,全新驾驶体验!智能语音小车,与您共同开创未来出行。”#51单片机最终项目《智能语音小车》【上】
- 前言
- 预备知识
- 1. L9110S电机控制器接线
- 1.1 L9110S概述
- 1.2 L9110S IO口描述
- 1.3 L9110S 实物图
- 1.4 L9110S与单片机接线
- 2. L9110前后左右控制小车
- 2.1 L9110前后左右控制小车
- 2.1分别用高低电平测试B-1A和B-1B L9110电机驱动模块引脚,A-1A和A-1B L9110电机驱动模块引脚。
- 2.2 将测试结果封装函数
- 2.3封装前后左右行走函数
- 2.4主函数内进行间隔2秒调用前后左右函数
- 2.5完整程序代码
- 3.电机相关代码封装_分文件
- 3.1电机相关代码封装_分文件核心思路
- 3.2将控制电机前进,后退,向左,向右的函数封装到电机对应的C文件中。
- 3.3建立电机头文件,里面声明电机对应C文件中的函数
- 3.4将延时2秒的函数封装到延时C文件中
- 3.5建立延时头文件,里面声明延时C文件中的函数。
- 3.6主函数中包含电机头文件和延时头文件即可完美运行程序
- 4.串口控制小车
- 4.1串口控制小车核心思路
- 4.2找到wifi开灯项目中优化8266,捕获联网失败的状态工程打开
- 4.3拷贝串口使用相关声明定义,串口初始化函数,串口中断函数,建立串口C文件和头文件。
- 4.4在电机对应C文件中构造小车停止函数
- 4.5在串口中断函数中通过接收指令操作小车运行
- 4.6串口C文件代码
- 5.手机通过蓝牙控制小车_自定义按键
- 5.1手机通过蓝牙控制小车_自定义按键核心思路
- 5.2将蓝牙模块正确接线连接51单片机串口
- 5.3给51单片机,蓝牙模块上电,打开手机APP HC蓝牙助手进行连接
- 5.4通过自定义按钮使HC蓝牙助手发送相应控制小车指令
- 6.蓝牙小车的点动控制
- 6.1蓝牙小车的点动控制核心思路
- 6.2在主C文件主函数while(1)死循环中调用电机停止函数。
- 6.3在串口中断函数中接收指令并执行调用电机相关函数时延时10毫秒实现点动
- 6.4通过串口助手自动发送模拟长按一直发送一直运行的状态。
- 7.PWM软件调速
- 7.1 PWM软件调速核心思路
- 7.2打开感应开盖垃圾桶项目_舵机编程实战工程主C文件
- 7.3在工程内新建time0.c文件
- 7.4拷贝相关变量,定时器0初始化函数,定时器零中断函数
- 7.5修改定时器0中断函数,使之调用电机控制函数,实现调速
- 7.6建立time.h文件
- 7.7主C文件内每隔1.5秒改变速度变量,使小车电机变速
- 7.8 PWM软件调速原理图
- 8.左右电机的各自调速管理
- 8.1左右电机的各自调速管理核心思路
- 8.2在motor.c文件内建立单独控制左右轮前进和停止函数,并在motor.h文件中声明。
- 8.3建立time1.c和time1.h文件用于对右轮的控制和初始化。
- 8.4修改time0.c为左轮控制
- 8.5主C文件内每隔1.5秒改变左右轮速度变量,使小车左右轮电机差速运行
- 结束语
前言
本篇博文介绍的是用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-1A
和B-1B ``L9110电机驱动模块
引脚,A-1A
和A-1B ``L9110电机驱动模块
引脚。 - 将测试结果封装函数
- 封装前后左右行走函数
2.1分别用高低电平测试B-1A和B-1B L9110电机驱动模块引脚,A-1A和A-1B L9110电机驱动模块引脚。
-
将
B-1A
置1
,B-1B
置0
,将A-1A
置1
,A-1B
置0
。看一下电机的转向。 -
代码体现
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; //左轮控制Bvoid 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 32sfr 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;//打开定时器1TR1 = 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 32sfr 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;//打开定时器1TR1 = 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 32sfr 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;//打开定时器1TR1 = 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单片机,蓝牙模块
上电
,打开手机APPHC蓝牙助手
进行连接 - 通过自定义按钮使HC蓝牙助手发送相应控制小车指令
5.2将蓝牙模块正确接线连接51单片机串口
相关接线请看我写的串口通信博文
5.3给51单片机,蓝牙模块上电,打开手机APP HC蓝牙助手进行连接
5.4通过自定义按钮使HC蓝牙助手发送相应控制小车指令
- 点击自定义按钮
- 点击设置方向按钮
- 在设置
按钮名称处
填写你需要设置的按钮名称,在设置按钮内容处
填写你需要发送的指令,最后点击确认
- 依次设置完
前
、后
、左
、右
、停
、五个指令后便可实现手机蓝牙控制小车。
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 32sfr 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;//打开定时器1TR1 = 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.c
和time1.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; //左轮控制Bvoid 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文件用于对右轮的控制和初始化。
- 拷贝
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开始加}
}
- 将
initTime0()
修改为initTime1()
,将所有的TH0
,TL0
替换为TH1
和TL1
。将TMOD
的两个运算表达式后面的变量替换为0x0F
和0x01 << 4
;实现使用定时器1
,16
位模式。将TR0
,TF0
,ET0
修改为TR1
,TF1
,ET1
。
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;
}
- 将
Time0Handler()
修改为Time1Handler()
,interrupt 1
修改为interrupt 3
,cnt
修改为cntRight
,speed
修改为speedRight
,goFront()
;修改为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开始加}
}
- 修改后的代码为
#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为左轮控制
- 将
cnt
修改为cntLeft
,speed
修改为speedLeft
,goFront();
修改为goFrontLeft();
,stop();
修改为stopLeft();
。 - 修改后的代码为
#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秒改变左右轮速度变量,使小车左右轮电机差速运行
- 调速变量修改代码
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();
}
- 主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();}
}
结束语
很高兴您能看到这里,点个赞再走呗。谢谢您啦!!!