闲时偷学51一周 完成蓝牙小车定时器中断PWM调速控制
关键问题: 51蓝牙串口通信、定时器、中断、L298N、PWM调速
- 先把我学习过程中的问题摆出来:89C52仅有的两个定时器在使用串口时的干扰问题,PWM产生
- 一开始找资料看别人51控制L298N的例子,PWM调速的比较少,有的自己试了结果不太理想;
- 还有就是 初来乍到,哪里有不对的地方 欢迎指正!
操作说明:
- 打开蓝牙调试器,对应设置好控制字符按键名
- 打开小车电源并将手机连接到蓝牙模块进行控制
一、前期硬件准备
-
TT电机小车底板套件,随便买一个两个电机的
-
蓝牙模块(我是在淘宝找便宜便买的,啊?买HC-06毕竟咱穷呀~)默认波特率9600一定要对应单片机串口通信波特率
单片机-----蓝牙模块连线:
- RXD/P3.0----TXD *********** GND----GND
- TXD/P3.1----RXD ***** VCC/5V/3V- -VCC/5V/3V
- 用到的蓝牙软件:蓝牙调试器 一般手机应用商店可下载,我的按键设置
-
STC89C52或者STC89C52RC最小系统板
-
L298N电机驱动、杜邦线等。本次只用到298N的四个逻辑引脚,其与单片机具体连接看程序定义
-
5-12V电池,注意与298N正确连接,通过L298N的板载5V使能输出(插上跳线帽时5V输入端有5V输出)给单片机和蓝牙模块供电。
-
其他,还需要用到USB转TTL给单片机下载程序;TTL如图:
最后就是编程环境 arm Keil了 。
二、51C语言程序编写
- 废话不多说,看看怎么用当今官方都不建议使用的STC89C52这块单片机来实现蓝牙通信,定时器中断PWM调速小车的主体程序吧。
这里掏出我第一版的部分代码://就是为了举个例子,写的什么玩意? 后来的自己都看不下去了
/* * * 反面教材,大家笑笑就行了 ~ ~ * * *
**************************************************
*用延时函数模拟PWM输出,但后面实验发现这玩意太LOW了~
*运行下去都是个问题
**************************************************/
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sbit ENA=P1^0; //使能端口A和B
sbit ENB=P1^5;
sbit IN1=P1^1; //以下四个逻辑端口
sbit IN2=P1^2;
sbit IN3=P1^3;
sbit IN4=P1^4;
void delay(uint n) //延时函数
{
uint x,y;
for(x=n;x>0;x--)
for(y=114;y>0;y--);
}
void Speed1(uint b) // 定义一个速度函数模拟PWM输出 形参b
{
ENB=1; // ENB使能或给高电平
delay(b); // 延时
ENB=0; // ENB失能或给低电平
delay(100-b);
}
void Speed(uint a) // 定义一个速度函数模拟PWM输出 形参
{
ENA=1; // ENA使能或给高电平
delay(a); // 延时
ENA=0; // ENA失能或给低电平
delay(100-a);
}
//L298N逻辑函数,控制电机正反转以及速度大小
void stop() //小车停止函数
{
IN1=0;
IN2=0;
IN3=0;
IN4=0;
}
void forward() //小车前进函数
{
IN1=1;
IN2=0;
IN3=1;
IN4=0;
Speed(30);
Speed1(30);
}
void back() //小车后退函数
{
IN1=0;
IN2=1;
IN3=0;
IN4=1;
Speed(30);
Speed1(30);
}
void left() //小车左转函数
{
IN1=0;
IN2=1;
IN3=1;
IN4=0;
Speed(30);
Speed1(30);
}
void right() //小车左转函数
{
IN1=1;
IN2=0;
IN3=0;
IN4=1;
Speed(30);
Speed1(30);
}
void UsartConfiguration() //串口初始化
{
SCON=0X50; //SCON 是一个特殊功能寄存器,用以设定串行口的工作方式
TMOD=0X20; //和接收/发送控制以及设置状态标志
PCON=0X00;
TH1=0Xfd;
TL1=0Xfd; //波特率为9600,计算所得
TR1= 1;
ES = 1; //开启串口中断
EA = 1; //开启总中断
}
void Com_Int() interrupt 4
{
uchar receive_data;
//EA = 0;
if(RI == 1) //一次数据接收完成后RI由硬件自动置1,作接收完成的标志
{ //初始设置电机停止
ENA=0; //PWM使能端口失能,电机停转
ENB=0;
receive_data = SBUF; //将收/发数据缓存器SBUF中的数据存入变量
/*****电机逻辑判断函数*****/
switch(receive_data)
{
case ('F'): forward();
break;
case ('B'): back();
break;
case ('L'): left();
break;
case ('R'): right();
break;
case ('S'): stop();
break;
}
}
RI=0;
EA = 1; //开启总中断
}
void main() //主函数
{
UsartConfiguration();
while(1);
}
- 由于我头一回的草率,后面又专门在学习网站~哔哩哔哩找了51定时器、中断系统的课看了几遍,哎枯燥难耐的我竟然还认真地做了笔记—但后面自己试验的多了,又觉得那些笔记记得都是废话,因为你永远不会在了解它之前觉得某个问题产生的几率有多大。
- 关于51 89系列的芯片仅有的两个定时器不能同时工作的问题 :也许不知道是我看的教程有问题还是,为了考验学习者,如果某些教程书里有,当我没说。
解决办法来源:当两个定时器只有一个工作另一个罢工时、当我实在没辙的时候,低头之际突然想到了来CSDN瞧瞧。在浏览了众多关于定时器的文章里,哎~~ 我TM终于找到了原因—两个定时器同时使用,在中断时会产生干扰,关键就在定时器中断控制寄存器这,程序里会提到。哎~~说多了都是泪啊,硬用时间换来的呢!?!!
以下 直接掏出我呕心沥血的最终代码:
/**********************************************************
* 用定时器中断实现PWM输出控制L298N的蓝牙小车/51两个定时器同时使用冲突问题
* 单 片 机:STC89C52/STC89C52RC
* 作 者 :GUard_Byte
* * * * * * * * * * * * * * * *
* 单片机与蓝牙模块连线:
* RXD----TXD GND----GND
* TXD----RXD VCC/5V/3V--VCC/5V/3V
*
* 关键问题:中断系统优先级TMOD|与串口中断的设置,或运算使得后面中断时前面赋值不被改变
* 保证两个定时器不干扰,同时工作
***********************************************************/
#include <reg52.h>
typedef unsigned int uint;
typedef unsigned char uchar;
// PWM输出I/O口定义与298N连接,注意我使用的最小系统P0有上拉电阻可置高,若无上拉电阻请换至其他
sbit PWM1 = P0^1;
sbit PWM2 = P0^2;
sbit PWM3 = P0^3;
sbit PWM4 = P0^4;
uchar receive_data; //储存串口接收的数据
uint count0 = 0; // 定时器T0中的计时
uint count1 = 0; // 定时器T1中的计时
uint flag1 = 0; // 电机1正反转的标志位
uint flag2 = 0; // 电机2正反转的标志位
uint rate1; // 电机速度等级, 共10级
uint rate2; // 电机速度等级, 共10级
/*******************************************************
* 函数名:void Time0_Int()
* 功 能:定时器0初始化,设定定时器0工作方式和初值
*******************************************************/
void Time0_Int()
{
EA = 1; // 开启总中断
EX0 = 1; // 允许外部中断0中断
IT0 = 1; // 下降沿触发中断
ET0 = 1; // 定时器0中断
TH0 = (65536 - 10) / 256; // 赋初值, 10us
TL0 = (65536 - 10) % 256;
TMOD|= 0x01; // 选择方式一
TR0 = 1; // 打开定时器0中断
}
/*******************************************************
* 函数名:void T0_inter() ;中断号 1
* 功 能:定时器0中断服务,及计数 PWM产生
* +++++ PWM产生的部分思路借鉴网络 本来想贴链接,啊~后来找不到原文了
* 求原谅!++++++
*******************************************************/
void T0_inter() interrupt 1
{
TR0 = 0;
TH0 = (65536 - 10)/256;
TL0 = (65536 - 10)%256;
TR0 = 1;
count0 ++;
if (count0 >= 100)
{
count0 = 0;
}
if (count0 < ( rate1 * 10))
{
if ( flag1 == 0)
{
PWM1 = 1;
PWM2 = 0;
}
else
{
PWM1 = 0;
PWM2 = 1;
}
}
else
{
PWM1 = 1;
PWM2 = 1;
}
if (count0 < ( rate2 * 10))
{
if (flag2 == 0)
{
PWM3 = 1;
PWM4 = 0;
}
else
{
PWM3 = 0;
PWM4 = 1;
}
}
else
{
PWM3 = 1;
PWM4 = 1;
}
}
// 小车前进函数, 速率为3
void forward()
{
rate1 = rate2 = 2;
flag1 = flag2 = 1;
}
// 小车后退函数, 速率为3
void back()
{
rate1 = rate2 = 2;
flag1 = flag2 = 0;
}
//小车右急转弯函数, 左边电机正转, 右边电机反转,行进过程有效
void turn_left()
{
flag1 = 1;
flag2 = 0;
}
// 小车左急转弯函数, 右边电机正转, 左边电机反转,行进过程有效
void turn_right()
{
flag1 = 0;
flag2 = 1;
}
// 右边电机加速,实现左转
void right_add()
{
if (rate1 == 5)
{
rate1 = 3;
}
rate1++;
}
// 左边电机加速,实现右转
void left_add()
{
if (rate2 == 5)
{
rate2 = 3;
}
rate2++;
}
// 电机加速,最大10,此处限制到5级
void moter_add()
{
if ((rate1==5)&&(rate2 == 5))
{
rate1 = 2;
rate2 = 2;
}
rate1++;
rate2++;
}
// 电机减速,最小为2
void moter_less()
{
if ((rate1==1)&&(rate2 == 1))
{
rate1 = 5;
rate2 = 5;
}
rate1--;
rate2--;
}
// 小车停止函数
void stop()
{
rate1 = rate2 = 0;
}
/*******************************************************
* 函数名:void Com_init()
* 功 能:串口初始化,设置串口和定时器1工作模式
*******************************************************/
void Com_init()
{
ES=0; // 关串口中断
SCON = 0x50; // REN=1允许串行接受状态,串口工作模式1,波特率可变
TMOD|=0x20; // 定时器1工作于方式2,8位自动重载模式, 用于产生波特率
TH1=TL1=0xFD; // 波特率9600 (晶振为11.0592)
PCON &= 0x7f; // 波特率不倍增
TR1 = 1; // 定时器1开始工作,产生波特率
RI = 0; // 接收标志位置0
EA=0; //关总中断
ES=1; // 开启串口中断
}
/*******************************************************
* 函数名:void Datectr()
* 功 能:用以处理串口将接收到的数据
*******************************************************/
void DateCtrl()
{
switch(receive_data)
{
case ('F'): forward();
break;
case ('B'): back();
break;
case ('L'): left_add(); //left
break;
case ('l'): turn_left();
break;
case ('R'): right_add(); //right
break;
case ('r'): turn_right();
break;
case ('u'): moter_add();
break;
case ('d'): moter_less();
break;
case ('S'): stop();
break;
}
}
void main() //主函数
{
Com_init(); //串口初始化和定时器初始化
Time0_Int();
while(1){
if(RI==1){ // 判断是否有数据到来
receive_data = SBUF; //将收/发数据缓存器SBUF中的数据存入变量
DateCtrl(); //调用数据处理函数
RI = 0;
}
}
}
//欧克,收工;
- 现在就可以愉快的玩耍自己亲手制作的蓝牙遥控小车了!最后别忘了给自己比个耶 ✌!哈哈~
- 最后来张成品图圆满结束: