一、硬件
包括:
STC89C52RC 单片机
sg90舵机(阿克曼转向)
TCRT5000红外模块(黑白线检测)
小车用电机x2(使用L298N电机驱动板驱动)
HC-06蓝牙模块
L2596M降稳压模块
各硬件之间的连接非常简单,无非就是信号线连IO口,正极连正极负极连负极,在此不作描述。(可以参考其他教程的连线,最后修改自己的IO口定义就好了)
二、代码结构
为每个部件都写了单独的头文件,具体如下:
主函数
basic部分的头文件是基础头文件,包括寄存器定义(regx52.h),延时(其实整个程序基本没用到, delay.h),定时器(timer.h)。
functional部分则是每个部件的头文件,包括舵机(servo.h),电机(motor.h),红外循迹(ir.h)以及蓝牙(bluetooth.h)。
主函数内只有每个部件的初始化函数和蓝牙/循迹函数的循环。
//basic
#include <regx52.h>
#include <delay.h>
#include <timer.h>
//functional
#include <servo.h>
#include <motor.h>
#include <ir.h>
#include <bluetooth.h>
void main() {
motorInit();
servoInit();
bluetoothInit();
while (1) {
if (mode == 0) {
ES = 0; //关闭蓝牙中断
detectLine();
ES = 1; //重新打开蓝牙中断
} else if (mode == 1) {
ES = 1; //打开蓝牙中断
}
}
}
头文件
如之前所说,共有7个头文件(虽多,但是能使之后的更新和维护变得简单)。
头文件之间的关系如下图所示:
(等一下。。。。等我拿到平板就传上来)
下面依次附上代码和注释:
(1)寄存器定义(regx52.h)
对原始的REGX52头文件做了少许修改,如把P0^1改为P01,这样更符合我的习惯
#ifndef __AT89X52_H__
#define __AT89X52_H__
/*------------------------------------------------
Byte Registers
------------------------------------------------*/
sfr P0 = 0x80;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr P1 = 0x90;
sfr SCON = 0x98;
sfr SBUF = 0x99;
sfr P2 = 0xA0;
sfr IE = 0xA8;
sfr P3 = 0xB0;
sfr IP = 0xB8;
sfr T2CON = 0xC8;
sfr T2MOD = 0xC9;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
/*------------------------------------------------
P0 Bit Registers
------------------------------------------------*/
sbit P00 = 0x80;
sbit P01 = 0x81;
sbit P02 = 0x82;
sbit P03 = 0x83;
sbit P04 = 0x84;
sbit P05 = 0x85;
sbit P06 = 0x86;
sbit P07 = 0x87;
/*------------------------------------------------
PCON Bit Values
------------------------------------------------*/
#define IDL_ 0x01
#define STOP_ 0x02
#define PD_ 0x02 /* Alternate definition */
#define GF0_ 0x04
#define GF1_ 0x08
#define SMOD_ 0x80
/*------------------------------------------------
TCON Bit Registers
------------------------------------------------*/
sbit IT0 = 0x88;
sbit IE0 = 0x89;
sbit IT1 = 0x8A;
sbit IE1 = 0x8B;
sbit TR0 = 0x8C;
sbit TF0 = 0x8D;
sbit TR1 = 0x8E;
sbit TF1 = 0x8F;
/*------------------------------------------------
TMOD Bit Values
------------------------------------------------*/
#define T0_M0_ 0x01
#define T0_M1_ 0x02
#define T0_CT_ 0x04
#define T0_GATE_ 0x08
#define T1_M0_ 0x10
#define T1_M1_ 0x20
#define T1_CT_ 0x40
#define T1_GATE_ 0x80
#define T1_MASK_ 0xF0
#define T0_MASK_ 0x0F
/*------------------------------------------------
P1 Bit Registers
------------------------------------------------*/
sbit P10 = 0x90;
sbit P11 = 0x91;
sbit P12 = 0x92;
sbit P13 = 0x93;
sbit P14 = 0x94;
sbit P15 = 0x95;
sbit P16 = 0x96;
sbit P17 = 0x97;
sbit T2 = 0x90; /* External input to Timer/Counter 2, clock out */
sbit T2EX = 0x91; /* Timer/Counter 2 capture/reload trigger & dir ctl */
/*------------------------------------------------
SCON Bit Registers
------------------------------------------------*/
sbit RI = 0x98;
sbit TI = 0x99;
sbit RB8 = 0x9A;
sbit TB8 = 0x9B;
sbit REN = 0x9C;
sbit SM2 = 0x9D;
sbit SM1 = 0x9E;
sbit SM0 = 0x9F;
/*------------------------------------------------
P2 Bit Registers
------------------------------------------------*/
sbit P20 = 0xA0;
sbit P21 = 0xA1;
sbit P22 = 0xA2;
sbit P23 = 0xA3;
sbit P24 = 0xA4;
sbit P25 = 0xA5;
sbit P26 = 0xA6;
sbit P27 = 0xA7;
/*------------------------------------------------
IE Bit Registers
------------------------------------------------*/
sbit EX0 = 0xA8; /* 1=Enable External interrupt 0 */
sbit ET0 = 0xA9; /* 1=Enable Timer 0 interrupt */
sbit EX1 = 0xAA; /* 1=Enable External interrupt 1 */
sbit ET1 = 0xAB; /* 1=Enable Timer 1 interrupt */
sbit ES = 0xAC; /* 1=Enable Serial port interrupt */
sbit ET2 = 0xAD; /* 1=Enable Timer 2 interrupt */
sbit EA = 0xAF; /* 0=Disable all interrupts */
/*------------------------------------------------
P3 Bit Registers (Mnemonics & Ports)
------------------------------------------------*/
sbit P30 = 0xB0;
sbit P31 = 0xB1;
sbit P32 = 0xB2;
sbit P33 = 0xB3;
sbit P34 = 0xB4;
sbit P35 = 0xB5;
sbit P36 = 0xB6;
sbit P37 = 0xB7;
sbit RXD = 0xB0; /* Serial data input */
sbit TXD = 0xB1; /* Serial data output */
sbit INT0 = 0xB2; /* External interrupt 0 */
sbit INT1 = 0xB3; /* External interrupt 1 */
sbit T0 = 0xB4; /* Timer 0 external input */
sbit T1 = 0xB5; /* Timer 1 external input */
sbit WR = 0xB6; /* External data memory write strobe */
sbit RD = 0xB7; /* External data memory read strobe */
/*------------------------------------------------
IP Bit Registers
------------------------------------------------*/
sbit PX0 = 0xB8;
sbit PT0 = 0xB9;
sbit PX1 = 0xBA;
sbit PT1 = 0xBB;
sbit PS = 0xBC;
sbit PT2 = 0xBD;
/*------------------------------------------------
T2CON Bit Registers
------------------------------------------------*/
sbit CP_RL2= 0xC8; /* 0=Reload, 1=Capture select */
sbit C_T2 = 0xC9; /* 0=Timer, 1=Counter */
sbit TR2 = 0xCA; /* 0=Stop timer, 1=Start timer */
sbit EXEN2= 0xCB; /* Timer 2 external enable */
sbit TCLK = 0xCC; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */
sbit RCLK = 0xCD; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */
sbit EXF2 = 0xCE; /* Timer 2 external flag */
sbit TF2 = 0xCF; /* Timer 2 overflow flag */
/*------------------------------------------------
T2MOD Bit Values
------------------------------------------------*/
#define DCEN_ 0x01 /* 1=Timer 2 can be configured as up/down counter */
#define T2OE_ 0x02 /* Timer 2 output enable */
/*------------------------------------------------
PSW Bit Registers
------------------------------------------------*/
sbit P = 0xD0;
sbit F1 = 0xD1;
sbit OV = 0xD2;
sbit RS0 = 0xD3;
sbit RS1 = 0xD4;
sbit F0 = 0xD5;
sbit AC = 0xD6;
sbit CY = 0xD7;
/*------------------------------------------------
Interrupt Vectors:
Interrupt Address = (Number * 8) + 3
------------------------------------------------*/
#define IE0_VECTOR 0 /* 0x03 External Interrupt 0 */
#define TF0_VECTOR 1 /* 0x0B Timer 0 */
#define IE1_VECTOR 2 /* 0x13 External Interrupt 1 */
#define TF1_VECTOR 3 /* 0x1B Timer 1 */
#define SIO_VECTOR 4 /* 0x23 Serial port */
#define TF2_VECTOR 5 /* 0x2B Timer 2 */
#define EX2_VECTOR 5 /* 0x2B External Interrupt 2 */
#endif
(2)延时头文件(delay.h)
#ifndef __DELAY_H
#define __DELAY_H
#include <intrins.h>
/**
* @brief 延时
* @param time 延时时间,单位ms
* @retval None
*/
void delay(unsigned int time) {
unsigned char data i, j;
while (time) {
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
time--;
}
}
/**
* @brief 延时
* @param time 延时时间,单位10us
* @retval None
*/
void delay10us(unsigned int time) {
unsigned char data i;
while (time) {
i = 2;
while (--i);
time--;
}
}
#endif
(3)定时器(timer.h)
整个程序共用到三个定时器( 属于榨干了((( ),舵机分配了定时器0,电机PWM是定时器2,蓝牙模块是定时器1(蓝牙模块并未使用下面的 t1Init() 函数,由于是串口的初始化)。
/*
Header Status: OK
*/
#ifndef TIMER_H_
#define TIMER_H_
#include <regx52.h>
void t0Init() { //65微秒@11.0592MHz
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xC4; //设置定时初始值
TH0 = 0xFF; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA = 1; //打开总中断
ET0 = 1; //打开定时器0中断
PT0 = 0; //设置定时器0中断优先级
}
//Not be used( replaced by bluetoothISR() )
void t1Init() { //1毫秒@11.0592MHz
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
EA = 1; //打开总中断
ET1 = 1; //打开定时器1中断
PT0 = 0; //设置定时器0中断优先级
}
void t2Init() { //1毫秒@11.0592MHz
T2MOD = 0; //初始化模式寄存器
T2CON = 0; //初始化控制寄存器
TL2 = 0x18; //设置定时初值
TH2 = 0xFC; //设置定时初值
ET2 = 1;
EA = 1;
PT2 = 0;
TR2 = 1; //定时器2开始计时
}
#endif
(4)舵机(servo.h)
/*
Header Status: OK
*/
#ifndef SERVO_H_
#define SERVO_H_
#include <regx52.h>
#define servoPin P00 //舵机引脚
unsigned int dutyServo = 16; //舵机PWM的占空比变量
/**
* @brief 初始化舵机
* @param None
* @retval None
*/
void servoInit() {
t0Init();
}
void servoISR() interrupt 1 {
static unsigned int counter = 0;
TL0 = 0xC4;
TH0 = 0xFF;
counter++;
counter %= 220; //计数值变化范围限制在0~219
if (counter < dutyServo) {
servoPin = 1;
} else {
servoPin = 0;
}
}
#endif
(5)电机(motor.h)
/*
Header Status: OK
*/
#ifndef MOTOR_H_
#define MOTOR_H_
#include <regx52.h>
#include <timer.h>
#define motorL2 P13
#define motorL1 P12
#define motorR1 P11
#define motorR2 P10
unsigned char Lpwm, Rpwm; //两电机的PWM占空比变量
unsigned char Ldir, Rdir; //两电机的方向设置,0为倒转,1为正转
/**
* @brief 初始化电机
* @param None
* @retval None
*/
void motorInit() {
t2Init();
}
/**
* @brief 设置左电机速度及方向
* @param speed 速度值,范围0-10
* @param direction 方向,0为反转,1为正转
* @retval None
*/
void motorSetL(unsigned char speed, unsigned char direction) {
if (speed > 10) {
speed = 10;
} else if (speed < 0) {
speed = 0;
}
if (direction > 1) {
direction = 1;
} else if (direction < 0) {
direction = 0;
}
Lpwm = speed;
Ldir = direction;
}
/**
* @brief 设置右电机速度及方向
* @param speed 速度值,范围0-10
* @param direction 方向,0为反转,1为正转
* @retval None
*/
void motorSetR(unsigned char speed, unsigned char direction) {
if (speed > 10) {
speed = 10;
} else if (speed < 0) {
speed = 0;
}
if (direction > 1) {
direction = 1;
} else if (direction < 0) {
direction = 0;
}
Rpwm = speed;
Rdir = direction;
}
void motorISR() interrupt 5 {
static unsigned int counter1 = 0;
TF2 = 0; //中断标志复位
RCAP2L = 0x66; //设置低位定时初值
RCAP2H = 0xFC; //设置高位定时初值
counter1++;
counter1 %= 10; //计数值变化范围限制在0~9
//左电机PWM及方向控制
if (counter1 < Lpwm) {
if (Ldir == 1) {
motorL2 = 1;
motorL1 = 0;
} else if (Ldir == 0) {
motorL2 = 0;
motorL1 = 1;
} else {
motorL2 = 0;
motorL1 = 0;
}
} else {
motorL2 = 0;
motorL1 = 0;
}
//右电机PWM及方向控制
if (counter1 < Rpwm) {
if (Rdir == 1) {
motorR2 = 1;
motorR1 = 0;
} else if (Rdir == 0) {
motorR2 = 0;
motorR1 = 1;
} else {
motorR2 = 0;
motorR1 = 0;
}
} else {
motorR2 = 0;
motorR1 = 0;
}
}
#endif
(6)红外循迹(ir.h)
通过设置一个状态量,为不同的状态写了相应的反应程序。注意:此头文件不算完全完成,有些转向功能没达到我的预期,本人正在完善)
/*
Header Status: 80% Done
*/
#ifndef IR_H_
#define IR_H_
#include <regx52.h>
#include <delay.h>
#include <servo.h>
#define l2 P17
#define l1 P16
#define r1 P15
#define r2 P14
static unsigned char state = 1, lastState = 1;
/**
* @brief 循迹函数
* @param None
* @retval None
*/
void detectLine() {
if (l2 == 0 && l1 == 1 && r1 == 1 && r2 == 0) { //线轨迹为直线
dutyServo = 16;
motorSetL(4, 1);
motorSetR(4, 1);
state = 1;
} else if (l2 == 0 && l1 == 0 && r1 == 1 && r2 == 0) { //车稍右偏
dutyServo = 13;
motorSetL(2, 1);
motorSetR(3, 1);
state = 2;
} else if (l2 == 0 && l1 == 1 && r1 == 0 && r2 == 0) { //车稍左偏
dutyServo = 19;
motorSetL(3, 1);
motorSetR(2, 1);
state = 5;
} else if (l2 == 0 && l1 == 0 && r1 == 0 && r2 == 0) {
if (lastState == 2) { //车右偏
dutyServo = 11;
motorSetL(1, 1);
motorSetR(4, 1);
state = 3;
} else if (lastState == 5) { //车左偏
dutyServo = 21;
motorSetL(4, 1);
motorSetR(1, 1);
state = 6;
}
} else if (l2 == 0 && l1 == 0 && r1 == 0 && r2 == 1) { //车急右转(未正常运行)
if (lastState == 3) {
dutyServo = 9;
motorSetL(1, 1);
motorSetR(4, 1);
state = 4;
}
} else if (l2 == 1 && l1 == 0 && r1 == 0 && r2 == 0) { //车急左转(未正常运行)
if (lastState == 6) {
dutyServo = 23;
motorSetL(4, 1);
motorSetR(1, 1);
state = 7;0
}
}
lastState = state;
}
#endif
(7)蓝牙(bluetooth.h)
蓝牙部分使用了一个大switch,对手机蓝牙调试器发来的指令做不同处理。
手机端的软件就叫“蓝牙调试器”,是[email protected]的作品。其中有多个自定义按键用于发送不同指令。当然用其他带有自定义发送功能的软件也行
/*
Header Status: OK
*/
#ifndef __BLUETOOTH_H__
#define __BLUETOOTH_H__
#include <regx52.h>
#include <servo.h>
#include <motor.h>
static unsigned char mode = 1;
unsigned char Data;
/**
* @brief 蓝牙初始化函数
* @param None
* @retval None
*/
void bluetoothInit() { //[email protected]
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器中断
EA = 1; //打开总中断
TR1 = 1; //定时器1开始计时
}
/**
* @brief 蓝牙接收数据处理函数
* @param Data 接收到的数据
* @retval None
*/
void bluetoothRcv(unsigned char Data) {
switch (Data) {
case 'w': //前直行
dutyServo = 16;
motorSetL(8, 1);
motorSetR(8, 1);
break;
case 'q': //前左转弯
dutyServo = 12;
motorSetL(5, 1);
motorSetR(7, 1);
break;
case 'e': //前右转弯
dutyServo = 20;
motorSetL(7, 1);
motorSetR(5, 1);
break;
case 's': //后直行
dutyServo = 16;
motorSetL(5, 0);
motorSetR(5, 0);
break;
case 'a': //后左转弯
dutyServo = 12;
motorSetL(3, 0);
motorSetR(5, 0);
break;
case 'd': //后右转弯
dutyServo = 20;
motorSetL(5, 0);
motorSetR(3, 0);
break;
case 'x': //停止
motorSetL(0, 1);
motorSetR(0, 1);
break;
case '0': //循迹模式
mode = 0;
break;
case '1': //遥控模式
mode = 1;
break;
}
}
void bluetoothISR() interrupt 4 {
RI = 0; //清除接收中断标志位
Data = SBUF; //读取串口数据
bluetoothRcv(Data); //处理串口数据
}
#endif
软件界面截图:
三、效果展示
视频传不上来,反正大概效果就是可循黑线,然后可以蓝牙遥控。这两个功能通过蓝牙发送不同指令来切换。
--END
标签:------------------------------------------------,定时器,循迹,sfr,51,Timer,单片机,sbit,de From: https://blog.csdn.net/m0_59778421/article/details/143652114