⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 单片机.c

📁 一个有关单片机(ATMEL51)的小程序
💻 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 + -