📄 单片机.c
字号:
//////////相关内容可查阅计算机硬件技术基础、课件、自动控制理论与单片机等书籍/////////////////
///////////////////////////////////程序基础//////////////////////////////////////////////////
#pragma small
#include<reg51.h> ////包括51系列单片机的基本定义
#include<math.h>
#include<absacc.h>////扩展板必备
typedef unsigned char uchar;
typedef unsigned int uint;
///////////////////电路设定////////////////////////
#define BASE1 0x8100////扩展片选信号CS1(触动开关扩展)
#define BASE2 0x8200////扩展片选信号CS2(双头检测板)
#define BASE3 0x8300////扩展片选信号CS3 (单头检测板)
#define BASE5 0x8500////发光二极管阵列
#define BASE6 0x8600////p0输入
#define BASE7 0x8700////p0输出
#define BASE8 0x8800////PWM
#define BASE9 0x8900////码盘计数
sbit P10=P1^0; ////碰撞开关接口
sbit P16=P1^6;
sbit P14=P1^4; ////电桥使能信号
sbit dir1=P1^2;////左轮方向标志,实质上为正负反馈标志。
sbit dir2=P1^3;////右轮方向标志,实质上为正负反馈标志。
//////////////////////基本参数//////////////////////////////
#define PI 3.1415926
#define D 0.12 ////轮子直径
#define DISTANCE 0.37 ////L轮子间距
#define PANE 100 ////码盘线数
#define PRESPEED 9000 ////原始最高速度(单位:转/分)
#define RESPEED 300 ////减速最高速度(单位:转/分)
#define RATE 30 ////减速比(原始最高速度/减速最高速度)
#define PWMBASE 250 ////(8254计数速率*PWM周期的一半(10M*25微秒),可任意)
#define INTOUT 200 ////(决定中断周期(1/20K*200=10毫秒))
#define CONST 3 ////( 减速最高速度与码盘线数之比(300(转/分)/100=3),可任意)
#define RIGHT 1
#define LEFT 0
#define LINE 15915 ////走1米两个轮子的码盘线数(计算公式:2*(1/(PI*D))*RATE*PANE)
#define TURN 51 ////转1度两个轮子的码盘线数(计算公式:((DISTANCE/D)*RATE*PANE)/180)
/////////////////////////控制用变量//////////////////////////////
struct discount
{
int uk1; ////上次输出值
int videal;////理想速度(单位:转/分)
int ek; ////本次误差
int ek1; ////上次误差
int ek2; ////前次误差
};
data uint ukx,uky; ////实际输出计数值
data struct discount sm2,sm1;////左右轮结构体变量
//////////////////////////中间运算变量//////////////////////////////
data uchar countl,counth; ////计数值高低位
data uint temp; ////每次中断时码盘线数(最大值为150:((PRESPEED/60)*中断周期)/1000)*PANE)
data long temp1=0,temp2=0; ////左右轮总的码盘线数
data long due; ////经PID调节后的偏差
data int uk; ////本次输出值(-PWMBASE〈uk〈PWMBASE)
data int kp1,ki1,kd1; ////PID参数
data int kp2,ki2,kd2;
data uchar prestate=0x28,state;////巡线状态
data char dv; ////速度调节量
int flag=0; ////巡线开关
//////////////////////结构体初始化//////////////////////////
void initstruct(struct discount *p)
{
p->ek2=0;
p->ek1=0;
p->ek=0;
p->uk1=0;
}
///////////////////////8254初始化///////////////////////////
void init8254() ////注意:8254为减法计数器
{
XBYTE[BASE8+3]=0x36; ////计数器0,工作方式3,计数初值500,主要功能分频(输出20KHZ的方波)
XBYTE[BASE8]=(PWMBASE*2)%256;
XBYTE[BASE8]=(PWMBASE*2)/256;
XBYTE[BASE8+3]=0x72; ////计数器1,工作方式1,计数初值250,主要功能输出PWM波
XBYTE[BASE8+1]=PWMBASE%256;
XBYTE[BASE8+1]=PWMBASE/256;
XBYTE[BASE8+3]=0xb2; ////计数器2,工作方式1,计数初值250,主要功能输出PWM波
XBYTE[BASE8+2]=PWMBASE%256;
XBYTE[BASE8+2]=PWMBASE/256;
XBYTE[BASE9+3]=0x30; ////计数器0,工作方式0,计数初值65534,计码盘线数
XBYTE[BASE9]=0xfd;
XBYTE[BASE9]=0xff;
XBYTE[BASE9+3]=0x70; ////计数器1,工作方式0,计数初值65534,计码盘线数
XBYTE[BASE9+1]=0xfd;
XBYTE[BASE9+1]=0xff;
XBYTE[BASE9+3]=0xb6; ////计数器2,工作方式3,计数初值200,主要功能分频(10毫秒中断一次)
XBYTE[BASE9+2]=INTOUT%256;
XBYTE[BASE9+2]=INTOUT/256;
}
///////////////////////延时////////////////////////
void delay(uchar i) ////以0.1秒为基本单位
{
uchar j,m;
long l;
for(j=0;j<i;j++)
{
for(l=0;l<100;l++)
{
for(m=0;m<250;m++)
{
;
}
}
}
}
///////////////////////中断反馈控制/////////////////////
void int0(void) interrupt 0 using 1
{
EA=0;
////巡线
if(flag)
{
state=XBYTE[BASE2];
sm2.videal=sm1.videal;
if ((state&0x28)==0x28)
{
dv=0;
XBYTE[BASE5]=state;
}
else
{
switch(state&0xf0) ////左偏,要右转
{case 0x20:dv=4;XBYTE[BASE5]=state;break;
case 0x30:dv=6;XBYTE[BASE5]=state;break;
case 0x10:dv=7;XBYTE[BASE5]=state;break;
case 0x90:dv=8;XBYTE[BASE5]=state;break;
case 0x80:dv=10;XBYTE[BASE5]=state;break;
case 0xc0:dv=12;XBYTE[BASE5]=state;break;
case 0x40:dv=14;XBYTE[BASE5]=state;break;
default:break;
}
switch(state&0x0f) ////右偏,要左转
{case 0x08:dv=-4;XBYTE[BASE5]=state;break;
case 0x0c:dv=-6;XBYTE[BASE5]=state;break;
case 0x04:dv=-7;XBYTE[BASE5]=state;break;
case 0x06:dv=-8;XBYTE[BASE5]=state;break;
case 0x02:dv=-10;XBYTE[BASE5]=state;break;
case 0x03:dv=-12;XBYTE[BASE5]=state;break;
case 0x01:dv=-14;XBYTE[BASE5]=state;break;
default:break;
}
if(!state)
{
if((prestate&0xf0)==0x40)
{
dv=17;
}
else if((prestate&0x0f)==0x01)
{
dv=-17;
}
}
}
prestate=state;
sm2.videal=sm2.videal+dv;
}
////读取电机1的速度
temp=0;
XBYTE[BASE9+3]=0xd2;
countl=XBYTE[BASE9];
counth=XBYTE[BASE9];
temp=65534-countl-counth*256;
if(temp>200) ////若超过最大值,则强制赋为0
{
temp=0;
}
temp1+=temp;
XBYTE[BASE9]=0xfd;
XBYTE[BASE9]=0xff;
////电机1PID控制
sm1.ek2=sm1.ek1;
sm1.ek1=sm1.ek;
if(!dir1) ////目的使其成为负反馈
sm1.ek=sm1.videal-temp*CONST;
else
sm1.ek=sm1.videal+temp*CONST;
due=((kp1+ki1+kd1)*sm1.ek-(kp1+kd1+kd1)*sm1.ek1+kd1*sm1.ek2)/8;////调节比例系数,使PID有合适的范围
uk=due+sm1.uk1;
if(uk-PWMBASE>1)
uk=PWMBASE-1;
else
if(uk+PWMBASE<-1)
uk=PWMBASE-1;
sm1.uk1=uk;
ukx=uk+PWMBASE;
////读取电机2的速度
temp=0;
XBYTE[BASE9+3]=0xd4;
countl=XBYTE[BASE9+1];
counth=XBYTE[BASE9+1];
temp=65534-countl-counth*256;
if(temp>200)
{
temp=0;
}
temp2+=temp;
XBYTE[BASE9+1]=0xfd;
XBYTE[BASE9+1]=0xff;
////电机2PID控制
sm2.ek2=sm2.ek1;
sm2.ek1=sm2.ek;
if(dir2)
sm2.ek=sm2.videal-temp*CONST;
else
sm2.ek=sm2.videal+temp*CONST;
due=((kp2+ki2+kd2)*sm2.ek-(kp2+kd2+kd2)*sm2.ek1+kd2*sm2.ek2)/8;
uk=due+sm2.uk1;
if(uk-PWMBASE>1)
uk=PWMBASE-1;
else
if(uk+PWMBASE<-1)
uk=PWMBASE-1;
sm2.uk1=uk;
uky=uk+PWMBASE-50;
XBYTE[BASE8+1]=ukx%256;
XBYTE[BASE8+1]=ukx/256;
XBYTE[BASE8+2]=uky%256;
XBYTE[BASE8+2]=uky/256;
EA=1;
}
///////////////////////子函数///////////////////////////
////直线程序////
void line(int linespeed,float distance)////以米为单位
{
long linesum=0;
temp1=temp2=0;
linesum=(long)LINE*distance;
sm1.videal=sm2.videal=20;
if((sm1.videal<=linespeed)&&(sm2.videal<=linespeed))
{
sm1.videal+=2;
sm2.videal+=2;
delay(2);
sm2.videal=sm1.videal;
}
sm1.videal=sm2.videal=linespeed;
while((temp1+temp2)<=linesum)
{}
sm1.videal=sm2.videal=0;
temp1=temp2=0;
}
////转弯程序////
void turn(int direction,int turnspeed,int angle)
{
long turnsum=0;
temp1=temp2=0;
turnsum=TURN*angle;
if(direction)
{
sm2.videal=turnspeed;
sm1.videal=0-turnspeed;
}
else
{
sm1.videal=turnspeed;
sm2.videal=0-turnspeed;
}
while((temp1+temp2)<=turnsum)
{}
sm1.videal=sm2.videal=0;
temp1=temp2=0;
}
////扣球程序////
void rise(void)
{
while(P16)
{}
P14=1;
sm1.videal=sm2.videal=0;
XBYTE[BASE7]=0x80;
delay(6);
XBYTE[BASE7]=0x00;
delay(100);
XBYTE[BASE7]=0x80;
}
////出球程序////
void shoot(void)
{
while(P10)
{}
XBYTE[BASE7]=0x00;
XBYTE[BASE7]=0x04;
}
///////////////////////主函数///////////////////////////
main()
{
EA=0;
TCON=0x01; ////单片机控制字
SCON=0x50;
PCON=0x00;
TMOD=0x20;
init8254();
initstruct(&sm2);
initstruct(&sm1);
////PID参数调节
kp1=80;
ki1=10;
kd1=5;
kp2=80;
ki2=10;
kd2=5;
P14=1; ////关电桥
delay(2);
P14=0; ////开电桥
sm1.videal=0;
sm2.videal=0;
EA=1; ////开系统中断
EX0=1; ////开外部0中断
XBYTE[BASE7]=0x00;
XBYTE[BASE5]=0xff;
flag=0;
XBYTE[BASE5]=0x11;
line(30,1.15);
XBYTE[BASE5]=0x22;
turn(RIGHT,20,90);
XBYTE[BASE7]=0x10;
delay(5);
XBYTE[BASE7]=0x00;
flag=1;
sm1.videal=sm2.videal=30;
XBYTE[BASE5]=0x44;
rise();
flag=0;
XBYTE[BASE5]=0x88;
shoot();
while(1)
{}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -