基于51单片机机械臂控制系统

1控制系统所运用到的模块

所用到的模块有:pca9685控制多路舵机模块;矩阵按键模块;LCD1702显示模块;DS18B20温度检测模块;独立按键模块;步进电机;ULN2003步进电机控制模,DS1302时钟模块。

2控制系统工作模式的功能

上电将LCD1602显示初始化,温度测量初始化,定时器初始化,测量当前温度,显示当前所需要显示的字符;

工作模式一:显示当前年月日时分秒,显示步进电机开关状态,显示处于哪种工作模式,在此界面可通过矩阵按键模块调整时间,控制步进电机的开关。

工作模式二:显示密码锁界面,显示按键所输入的数值,在此界面可通过矩阵按键控制所输入的数值,如果输入密码正确跳转到工作模式三,否者返回工作模式一。

工作模式三:显示每个舵机的工作状态,在此界面可以通过独立按键和矩阵按键配合控制舵机工作;按下指定按键返回到工作模式一。

工作模式四:在此工作模式下机械臂由红外遥控器控制,LCD1602显示舵机状态

3控制系统所运用的协议以及基础知识

DS18B20控制所运用到的单线协议,实时时钟DS1302所运用到SPI协议;控制舵机所运用的IIC协议,矩阵键盘状态机写法;步进电机运动控制LCD1602显示,定时器中断配置,NEC协议,舵机控制中pwm控制等等。

4控制系统流程

 

5控制系统主要思路

LCD1602负责显示,按键负责控制。

最重要的是:pca9685模块控制舵机  舵机具体控制细节在 【SG90模拟舵机控制及PCA9685模块的使用】这篇博客有具体说明。

通过pca9685可以控制多个舵机,非常节约IO口。

6控制系统代码

主函数

#include <REGX52.H>
#include <ONEWIRE.H>
#include <LCD1602.H>
#include <INTERRUPT.H>
#include <DS18B20.H>
#include <DISPLAY.H>
#include <DS1302.H>
#include <KEY.H>
#include <MOTOR.H>
#include <SERVO.H>
#include <INIT_SYSTEM.H>
unsigned int display_count;//lcd1602中断时间控制
unsigned int key_count;//按键中断时间控制
unsigned char mode=1;//工作模式
unsigned char count0=90;//底部旋转舵机初始角度
unsigned char count1=125;//高度舵机初始角度
unsigned char count2=0;//前后舵机初始角度
unsigned char count3=90;//夹爪舵机初始角度
extern unsigned int hw_count;
extern bit work_mode;
extern bit motor_control;
extern unsigned int hw_count;
extern bit displaymode3_flag;

void main()
{
		Init_process();//系统初始化
		while(1)
		{
			display_process();//lcd1602显示画面
			key_process();//按键操作函数
		}
}
 
void timer0service() interrupt 1
{
		TL0 = 0x66;		//设置定时初值
	  TH0 = 0xFC;		//设置定时初值
		key_count++;
}

void timer1service() interrupt 3
{
		TL1 = 0xCD;		//设置定时初值
		TH1 = 0xD4;		//设置定时初值
		if((mode==1)||(mode==2)||(mode==3))
		{
			display_count++;
		}
	 if(mode==2)//当处于定时器模式一
	 {
		 if(work_mode==0)
		 {
			 if(motor_control)
				{
					 motor_process();//启动步进电机
				}
		 }
		 else
		 {
			flag();
		 }
		 
	 }
	 
	 
}

LCD1602显示函数

#include <REGX52.H>
#include <ONEWIRE.H>
#include <LCD1602.H>
#include <DS18B20.H>
#include <DS1302.H>

extern unsigned char mode;//模式一用来显示温度 模式二用来显示舵机开关和时间
extern unsigned int display_count;
extern unsigned char key_press;

extern unsigned char count0;//底部旋转舵机初始角度
extern unsigned char count1;//高度舵机初始角度
extern unsigned char count2;//前后舵机初始角度
extern unsigned char count3;//夹爪舵机初始角度
extern bit run;//控制舵机正转反转
extern bit key_flag;//控制是否跳转到舵机控制界面
unsigned char xq;
unsigned char nian=0;
unsigned char yue=0;
unsigned char ri=0;
unsigned char xq=3;
unsigned char shi;
unsigned char fen;
unsigned char miao;
unsigned char xingqi;
unsigned int keynumber;//密码输入
unsigned int  real_number=123;//真实密码

bit work_mode=0;
bit ds1302_flag=0;
bit motor_control=0;//舵机开关状态0为关1为开
bit flag_show;
 
/*
此段代码用来显示模式一LCD1602所要显示的内容
*/
void display_mode1()
{
		float temp;
	  DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	  EA=0;
		temp=DS18B20_ReadT();
	  EA=1;
		LCD_ShowString(1,1,"Origin");
	  LCD_ShowString(1,7,"  Menu    ");
	
		LCD_ShowString(2,1,"Temp:");
		LCD_ShowNum(2,6,temp,2);		//显示温度整数部分
		LCD_ShowChar(2,8,'.');		//显示小数点
		LCD_ShowNum(2,9,(unsigned long)(temp*10000)%10000,4);//显示温度小数部分
	  write_du();
		LCD_ShowChar(2,14,'C');
}

/*
以下函数在设置函数时0.5秒进行闪烁
*/
void flag()//设置时间每隔一秒闪烁
{
	unsigned int flag_count;
	flag_count++;
	if(flag_count>50)
	{
		flag_count=0;
		flag_show=~flag_show;
	}

}
/*
此段代码用来显示模式二LCD1602所要显示的内容
*/
void display_mode2()
{
	if(ds1302_flag==0)
	{
		  DS1302_Init();//DS1302初始化
	    DS1302_SetTime();//设置时间
			ds1302_flag=1;
	}
	if(work_mode==0)//处于时间显示模式
	 {
		 if(motor_control)
		 {
			LCD_ShowString(2,10,"Open ");
		 }
		 else
		 {
			LCD_ShowString(2,10,"Close  ");
		 }
		 LCD_ShowString(1,15,"M1");//工作模式1
		 LCD_ShowChar(2,9,' '); 
		 DS1302_ReadTime();//读取时间
		LCD_ShowChar(1,3,'-');
		LCD_ShowChar(1,6,'-');
		LCD_ShowChar(1,9,' ');
		LCD_ShowChar(2,3,':');
		LCD_ShowChar(2,6,':');
		LCD_ShowChar(2,9,' ');
		
		LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
		LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
		LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
		LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
		LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
		LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
	
		xingqi=DS1302_Time[6];
		switch(xingqi)//用来选择星期
		{
			case 1:LCD_ShowString(1,10,"Mon  ");break;
			case 2:LCD_ShowString(1,10,"Tue  ");break;
			case 3:LCD_ShowString(1,10,"Wed  ");break;
			case 4:LCD_ShowString(1,10,"Thu  ");break;
			case 5:LCD_ShowString(1,10,"Fri  ");break;
			case 6:LCD_ShowString(1,10,"Sat  ");break;
			case 7:LCD_ShowString(1,10,"Sun  ");break;	
		}
		
	}
	if(work_mode==1)// 处于时间设置界面
	{
		       if(motor_control)
					 {
						LCD_ShowString(2,10,"Open ");
					 }
					 else
					 {
						LCD_ShowString(2,10,"Close  ");
					 }
			      LCD_ShowChar(1,3,'-');
						LCD_ShowChar(1,6,'-');
						LCD_ShowChar(1,9,' ');
						LCD_ShowChar(2,3,':');
						LCD_ShowChar(2,6,':'); 
						LCD_ShowChar(2,9,' ');
						 
						if(key_press==1)//按第一下
						{
							if(flag_show)
							{
								LCD_ShowNum(1,1,nian,2);//显示年
							}
							else
							{
								LCD_ShowChar(1,1,' '); 
								LCD_ShowChar(1,2,' ');
							}
							
							
							LCD_ShowNum(1,4,yue,2);//显示月
							LCD_ShowNum(1,7,ri,2);//显示日
							LCD_ShowNum(2,1,shi,2);//显示时
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,7,miao,2);//显示秒
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
						
						else if(key_press==2)//按第二下
						{
							if(flag_show)
							{
								LCD_ShowNum(1,4,yue,2);//显示月
							}
							else
							{
								LCD_ShowChar(1,4,' '); 
								LCD_ShowChar(1,5,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							
							LCD_ShowNum(1,7,ri,2);//显示日
							LCD_ShowNum(2,1,shi,2);//显示时
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,7,miao,2);//显示秒
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
						
						else if(key_press==3)//按第三下
						{
							if(flag_show)
							{
								LCD_ShowNum(1,7,ri,2);//显示日
							}
							else
							{
								LCD_ShowChar(1,7,' '); 
								LCD_ShowChar(1,8,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							LCD_ShowNum(1,4,yue,2);//显示月
							
							LCD_ShowNum(2,1,shi,2);//显示时
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,7,miao,2);//显示秒
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
						
						else if(key_press==4)//按第四下
						{
							if(flag_show)
							{
								switch(xq)
								 {
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
								 }
							}
							else
							{
								LCD_ShowChar(1,10,' '); 
								LCD_ShowChar(1,11,' ');
								LCD_ShowChar(1,12,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							LCD_ShowNum(1,4,yue,2);//显示月
							LCD_ShowNum(1,7,ri,2);//显示日
							LCD_ShowNum(2,1,shi,2);//显示时
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,7,miao,2);//显示秒
						}
						
						else if(key_press==5)//按第五下
						{
							if(flag_show)
							{
								LCD_ShowNum(2,1,shi,2);//显示时
							}
							else
							{
								LCD_ShowChar(2,1,' '); 
								LCD_ShowChar(2,2,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							LCD_ShowNum(1,4,yue,2);//显示月
							LCD_ShowNum(1,7,ri,2);//显示日
							
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,7,miao,2);//显示秒
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
						
						else if(key_press==6)//按第六下
						{
							if(flag_show)
							{
								LCD_ShowNum(2,4,fen,2);//显示分
							}
							else
							{
								LCD_ShowChar(2,4,' '); 
								LCD_ShowChar(2,5,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							LCD_ShowNum(1,4,yue,2);//显示月
							LCD_ShowNum(1,7,ri,2);//显示日
							
							LCD_ShowNum(2,1,shi,2);//显示时
							LCD_ShowNum(2,7,miao,2);//显示秒
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
						
						else if(key_press==7)//按第七下
						{
							if(flag_show)
							{
								LCD_ShowNum(2,7,miao,2);//显示秒
							}
							else
							{
								LCD_ShowChar(2,7,' '); 
								LCD_ShowChar(2,8,' ');
							}
							
							LCD_ShowNum(1,1,nian,2);//显示年
							LCD_ShowNum(1,4,yue,2);//显示月
							LCD_ShowNum(1,7,ri,2);//显示日
							LCD_ShowNum(2,4,fen,2);//显示分
							LCD_ShowNum(2,1,shi,2);//显示时
							
								
							switch(xq)
							{
								case 1:LCD_ShowString(1,10,"Mon  ");break;
								case 2:LCD_ShowString(1,10,"Tue  ");break;
								case 3:LCD_ShowString(1,10,"Wed  ");break;
								case 4:LCD_ShowString(1,10,"Thu  ");break;
								case 5:LCD_ShowString(1,10,"Fri  ");break;
								case 6:LCD_ShowString(1,10,"Sat  ");break;
								case 7:LCD_ShowString(1,10,"Sun  ");break;	
							}
						}
	}
				
}
/*
	密码输入显示界面
*/
void display_mode3()
{
		LCD_ShowString(1,1,"Trick  Lock");
	  LCD_ShowString(1,12,"   M2");
	  LCD_ShowString(2,1,"Password:");
	  LCD_ShowNum(2,10,keynumber,3);
	  LCD_ShowString(2,13,"    ");
	  
}
void display_process()
{
		if(display_count>10)
		{
				display_count=0;
			  switch(mode)
				{
					case 1:display_mode1();break;
					case 2:display_mode2();break;
					case 3:display_mode3();break;
				}
		
		}
}

LCD1602显示函数头文件

#ifndef __DSIPLAY_H
#define __DISPLAY_H

void flag();
void display_mode1();
void display_mode2();
void display_mode3();
void display_process();

#endif

LCD1602驱动函数

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

void write_du()
{
	LCD_SetCursor(2,13);
	LCD_WriteData(0xdf);
}
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
/*
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}
*/


/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
/*
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

*/

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
	
/*
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}
*/


LCD1602驱动函数头文件

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void write_du();
#endif

矩阵按键控制函数

#include <REGX52.H>
#include <LCD1602.H>
#include <DS1302.H>
#include <SERVO.H>
#include <DISPLAY.H>

sbit buzzer=P2^5;
#define NO_KEY 0XFF
#define KEY_STATE0 0
#define KEY_STATE1 1
#define KEY_STATE2 2

extern unsigned int key_count;
extern unsigned char mode;
extern unsigned char nian;
extern unsigned char yue;
extern unsigned char ri;
extern unsigned char xq;
extern unsigned char shi;
extern unsigned char fen;
extern unsigned char miao;

extern	 unsigned char count0;
extern   unsigned char count1;
extern   unsigned char count2;
extern   unsigned char count3;
extern   unsigned int keynumber;
extern   unsigned int  real_number;
extern bit motor_control;
extern bit work_mode;
bit run=1;//当run为1时++为0时--
bit displaymode3flag;
bit key_flag;

unsigned char key_press;//此函数用来统计按键按下的次数,按下选中数值进行闪烁

unsigned char num[6];
unsigned char press;
unsigned char number; 
unsigned char keyscan()//按键扫描函数
{
	  static unsigned char key_state=KEY_STATE0;
	  unsigned char key_value=0,key_temp;
	  unsigned char key1,key2;
	  
	  P1=0Xf0;
	  if(P1_7==0){key1=0x70;}
		if(P1_6==0){key1=0xb0;}
	  if(P1_5==0){key1=0xd0;}
	  if(P1_4==0){key1=0xe0;}
		if((P1_7==1)&&(P1_6==1)&&(P1_5==1)&&(P1_4==1)){key1=0xf0;}
	
		P1=0X0f;
		if(P1_3==0){key2=0x07;}
		if(P1_2==0){key2=0x0b;}
	  if(P1_1==0){key2=0x0d;}
	  if(P1_0==0){key2=0x0e;}
		if((P1_3==1)&&(P1_2==1)&&(P1_1==1)&&(P1_0==1)){key2=0x0f;}
	  
		key_temp=key1|key2;
		
		switch(key_state)
		{
			case KEY_STATE0:
				            if(key_temp!=NO_KEY)
							{
								key_state=KEY_STATE1;
							}
							break;
			case KEY_STATE1: 
							if(key_temp==NO_KEY)
							{
								key_state=KEY_STATE0;
							}
							else
						    {
									switch(key_temp)
									{
										case 0x77:key_value=1;break;
										case 0x7b:key_value=2;break;
										case 0x7d:key_value=3;break;
										case 0x7e:key_value=4;break;
										
										case 0xb7:key_value=5;break;
										case 0xbb:key_value=6;break;
										case 0xbd:key_value=7;break;
										case 0xbe:key_value=8;break;
										
										case 0xd7:key_value=9;break;
										case 0xdb:key_value=10;break;
										case 0xdd:key_value=11;break;
										case 0xde:key_value=12;break;
										
										case 0xe7:key_value=13;break;
										case 0xeb:key_value=14;break;
										case 0xed:key_value=15;break;
										case 0xee:key_value=16;break;
										
										case 0xff:key_value=0;break;
									}
								key_state=KEY_STATE2;
							}
							break;
			case KEY_STATE2:
							if(key_temp==NO_KEY)
							{
								key_state=KEY_STATE0;
							}
							break;
		}					
									
		
		
		
		
		
		return key_value;
}
/*
	以下函数用按键控制舵机
*/
void engine_key()
{
	  
	  if(displaymode3flag==0)
		{
			LCD_ShowString(1,1,"L2:");
			LCD_ShowString(1,8,"L2:");
			LCD_ShowString(1,15,"M3");
			LCD_ShowString(2,1,"L3:");
			LCD_ShowString(2,8,"L4:");
			LCD_ShowChar(1,7,' ');
			LCD_ShowChar(1,14,' ');
			LCD_ShowChar(2,7,' ');
			LCD_ShowChar(2,14,' ');

			LCD_ShowNum(1,4,count0,3);
			LCD_ShowNum(1,11,count1,3);
			LCD_ShowNum(2,4,count2,3);
			LCD_ShowNum(2,11,count3,3);
			if(run)
			{
				LCD_ShowString(2,15,"++");
			}
			else
			{
				LCD_ShowString(2,15,"--");
			}
			displaymode3flag=1;
     }

		if(P3_0==0)//按键二
		{
			if(run==1)
			{
				if(count0<180)
				{
					count0++;
					servo_control(4,count0);
					LCD_ShowNum(1,4,count0,3);
				}	
			}
			else
			{
				if(count0>0)
				{
					count0--;
					servo_control(4,count0);
					LCD_ShowNum(1,4,count0,3);
				}
				
			}
			
			if((count0==90)||(count0==180))
		  {
						buzzer=~buzzer;
			}
			
		}
		
		if(P3_2==0)//按键三
		{
			if(run==1)
				{
					if(count1<180)
					{
						count1++;
						servo_control(1,count1);
						LCD_ShowNum(1,11,count1,3);
					}	
				}
				else
				{
					if(count1>0)
					{
						count1--;
						servo_control(1,count1);
						LCD_ShowNum(1,11,count1,3);
					}
					
				}
				if((count1==90)||(count1==180))
				{
						buzzer=~buzzer;
				}
		}
		
		if((P3_3==0))//按键四
		{
			if(run==1)
			{
				if(count2<180)
				{
					count2++;
					servo_control(2,count2);
				  LCD_ShowNum(2,4,count2,3);
				}	
			}
			else
			{
				if(count2>0)
				{
					count2--;
					servo_control(2,count2);
					LCD_ShowNum(2,4,count2,3);
				}
				
			}
			if((count2==0)||(count2==180))
				{
						buzzer=~buzzer;
				}
		}
		
		if((P1_4==0)&&(P1_3==0))//S13
		{
			if(run==1)
			{
				if(count3<180)
				{
					count3++;
					servo_control(3,count3);
				  LCD_ShowNum(2,11,count3,3);
				}	
			}
			else
			{
				if(count3>90)
				{
					count3--;
					servo_control(3,count3);
				  LCD_ShowNum(2,11,count3,3);
				}
			
			}
			if((count3==90)||(count3==180))
				{
						buzzer=~buzzer;
				}
			
		}
		
		
		
		
}
/*
	以下函数为按键处理函数
*/
void key_process()
{
	    unsigned char key_number;
			if(key_count>=5)
			{
					key_count=0;
				  key_number=keyscan();
				  switch(key_number)
					{
						case 1:P2_0=0;
						       if(key_flag==0)
									 {
									 mode++;
									 }
						       if(mode==4)//当切换到工作模式2是关闭步进电机
										{
											  key_flag=0;
											  keynumber=0;
											  displaymode3flag=0;
												mode=2;
											  
											  
										 }
									 
									 
									 break;
						case 2:
									
							     if(mode==2)//在时间设置界面下每按以后进行下一个变量设置
									 {
											if(work_mode==1)
											{
												key_press++;
											 if(key_press==8)
											 {
												key_press=0;
												work_mode=0;
												DS1302_Time[0]=nian;DS1302_Time[1]=yue;
											  DS1302_Time[2]=ri;DS1302_Time[3]=shi;
											  DS1302_Time[4]=fen;DS1302_Time[5]=miao;
												DS1302_Time[6]=xq;DS1302_SetTime();//设置时间
											 }
											}
									 }
									 if(mode==3)
									 {
											 number=1;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
									 break;
						case 3:
							    /*
									以下代码在时间设置界面时进行时间累加
						      */
									if(mode==2)//处于工作模式1
									 {
										 if(work_mode==1)//处于时间设置界面,每次按下加一
											{
												if(key_press==1)//按第一次
												{
														if(nian<99)
														{
																nian++;
														}
												}
												if(key_press==2)//按第二次
												{
														if(yue<12)
														{
																yue++;
														}
												}
												if(key_press==3)//按第三次
												{
														if(ri<31)
														{
																ri++;
														}
												}
												if(key_press==4)//按第四次
												{
														if(xq<7)
														{
																xq++;
														}
												}
												if(key_press==5)//按第五次
												{
														if(shi<23)
														{
																shi++;
														}
												}
												if(key_press==6)//按第六次
												{
														if(fen<59)
														{
																fen++;
														}
												}
												if(key_press==7)//按第七次
												{
														if(miao<59)
														{
																miao++;
														}
												}
											}
									 }
									 if(mode==3)
									 {
											 number=2;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
						       break;
						case 4:
									/*
									以下代码在时间设置界面时进行时间累减
						      */
									if(mode==2)//处于工作模式1
									 {
										 if(work_mode==1)//处于时间设置界面,每次按下加一
											{
												if(key_press==1)//按第一次
												{
														if(nian>0)
														{
																nian--;
														}
												}
												if(key_press==2)//按第二次
												{
														if(yue>1)
														{
																yue--;
														}
												}
												if(key_press==3)//按第三次
												{
														if(ri>1)
														{
																ri--;
														}
												}
												if(key_press==4)//按第四次
												{
														if(xq>1)
														{
																xq--;
														}
												}
												if(key_press==5)//按第五次
												{
														if(shi>0)
														{
																shi--;
														}
												}
												if(key_press==6)//按第六次
												{
														if(fen>0)
														{
																fen--;
														}
												}
												if(key_press==7)//按第七次
												{
														if(miao>0)
														{
																miao--;
														}
												}
											}
									 }
									 if(mode==3)
									 {
											 number=3;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
									 
						       break;
						
						case 5:
									if(mode==2)
									{
										work_mode=1;
										key_press=1;
									}
									if(mode==3)
									{
										keynumber=0;
										press=0;
									}
									
						       break;
						case 6:
									if(mode==3)
									 {
											 number=4;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
						       break;
						case 7:
										if(mode==3)
									 {
											 number=5;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
							     break;
						case 8:
							     if(mode==3)
									 {
											 number=6;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
									 break;
						
						case 9:
							     if(mode==2)
									 {
											if(work_mode==0)
											{
												motor_control=~motor_control;//用来控制步进电机开关		
											}
									 }
										if(mode==3)
										{
											if(real_number==keynumber)
											{
													mode=4;
												  key_flag=1;
												  keynumber=0;
												  press=0;
												  P3_0=1;P3_1=1;P3_2=1;P3_3=1;
												  motor_control=0;
											}
											else
											{
												  mode=2;
												  keynumber=0;
												  press=0;
											}
                    }
							    
      						break;
						case 10:
							   if(mode==3)
									 {
											 number=7;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
						       break;
						case 11:
							    if(mode==3)
									 {
											 number=8;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
						        break;
						case 12:
										if(mode==3)
									 {
											 number=9;
										   press++;
										   if(press<=3)
											 {
												 if(press==1)
												 {
														keynumber=number*100+keynumber;
												 }
												 else if(press==2)
												 {
														keynumber=number*10+keynumber;
												 }
												 else
												 {
														keynumber=number*1+keynumber;
												 }
											 }
										   
										   
									 }
						        break;
						
						case 13:
							     
						        break;
						case 14:
							      if(mode==4)
										{
											run=!run;
											if(run)
											{
												LCD_ShowString(2,15,"++");
											}
											else
											{
												LCD_ShowString(2,15,"--");
											}
										}
						        break;
						case 15:break;
						case 16:break;
					}
					if(mode==4)
					{
						engine_key();
					}
					
			}
}

矩阵按键控制函数头文件

#ifndef __KEY_H
#define __KEY_H


unsigned char keyscan();
void key_process();
void engine_key();

#endif

系统初始化函数

#include <REGX52.H>
#include <ONEWIRE.H>
#include <LCD1602.H>
#include <INTERRUPT.H>
#include <DS18B20.H>
#include <SERVO.H>

/*
	以下函数用于系统初始化
*/
void Init_process()
{
		DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	  Timer1Init();//定时器1初始化
		Timer0Init();//定时器0初始化
	  LCD_Init();//LCD	初始化
	  init();
		begin();
		setPWMFreq(50);
	  /*
		各舵机状态初始化
	  */
	  servo_control(4,90);//底部舵机初始化0度
	  delayms(300);
	  servo_control(1,125);//高度调节舵机初始化
		delayms(300);
	  servo_control(2,0);//前后舵机初始化
		delayms(300);
	  servo_control(3,90);//抓取舵机初始化
		delayms(300);
}

系统初始化函数头文件

#ifndef __INIT_SYSTEM_H
#define __INIT_SYSTEM_H


void Init_process();

#endif

DS18B20底层驱动函数

#include <REGX52.H>

//引脚定义
sbit OneWire_DQ=P3^7;

/**
  * @brief  单总线初始化
  * @param  无
  * @retval 从机响应位,0为响应,1为未响应
  */
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i = 247;while (--i);		//Delay 500us
	OneWire_DQ=1;
	i = 32;while (--i);			//Delay 70us
	AckBit=OneWire_DQ;
	i = 247;while (--i);		//Delay 500us
	return AckBit;
}

/**
  * @brief  单总线发送一位
  * @param  Bit 要发送的位
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	OneWire_DQ=0;
	i = 4;while (--i);			//Delay 10us
	OneWire_DQ=Bit;
	i = 24;while (--i);			//Delay 50us
	OneWire_DQ=1;
}

/**
  * @brief  单总线接收一位
  * @param  无
  * @retval 读取的位
  */
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char i;
	unsigned char Bit;
	OneWire_DQ=0;
	i = 2;while (--i);			//Delay 5us
	OneWire_DQ=1;
	i = 2;while (--i);			//Delay 5us
	Bit=OneWire_DQ;
	i = 24;while (--i);			//Delay 50us
	return Bit;
}

/**
  * @brief  单总线发送一个字节
  * @param  Byte 要发送的字节
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		OneWire_SendBit(Byte&(0x01<<i));
	}
}

/**
  * @brief  单总线接收一个字节
  * @param  无
  * @retval 接收的一个字节
  */
unsigned char OneWire_ReceiveByte(void)
{
	unsigned char i;
	unsigned char Byte=0x00;
	for(i=0;i<8;i++)
	{
		if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
	}
	return Byte;
}

DS18B20驱动函数头文件

#ifndef __DS18B20_H__
#define __DS18B20_H__

void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);

#endif

DS1302驱动函数

#include <REGX52.H>

extern unsigned char nian;
extern unsigned char yue;
extern unsigned char ri;
extern unsigned char xq;
extern unsigned char shi;
extern unsigned char fen;
extern unsigned char miao;

//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

//寄存器写入地址/指令定义
#define DS1302_SECOND		0x80
#define DS1302_MINUTE		0x82
#define DS1302_HOUR			0x84
#define DS1302_DATE			0x86
#define DS1302_MONTH		0x88
#define DS1302_DAY			0x8A
#define DS1302_YEAR			0x8C
#define DS1302_WP			0x8E

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
char DS1302_Time[]={22,8,6,15,57,0,6};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init(void)
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	for(i=0;i<8;i++)
	{
		DS1302_IO=Data&(0x01<<i);
		DS1302_SCLK=1;
		DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

/**
  * @brief  DS1302读一个字节
  * @param  Command 命令字/地址
  * @retval 读出的数据
  */
unsigned char DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command|=0x01;	//将指令转换为读指令
	DS1302_CE=1;
	for(i=0;i<8;i++)
	{
		DS1302_IO=Command&(0x01<<i);
		DS1302_SCLK=0;
		DS1302_SCLK=1;
	}
	for(i=0;i<8;i++)
	{
		DS1302_SCLK=1;
		DS1302_SCLK=0;
		if(DS1302_IO){Data|=(0x01<<i);}
	}
	DS1302_CE=0;
	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
	DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
	DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
	DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
	DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
	DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
	DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
	DS1302_WriteByte(DS1302_WP,0x80);
	nian=DS1302_Time[0];yue=DS1302_Time[1];
	ri=DS1302_Time[2];  shi=DS1302_Time[3];
	fen= DS1302_Time[4];miao=DS1302_Time[5];
	xq= DS1302_Time[6];
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
	Temp=DS1302_ReadByte(DS1302_MONTH);
	DS1302_Time[1]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DATE);
	DS1302_Time[2]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_HOUR);
	DS1302_Time[3]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MINUTE);
	DS1302_Time[4]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_SECOND);
	DS1302_Time[5]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_DAY);
	DS1302_Time[6]=Temp/16*10+Temp%16;
}

DS1302驱动头文件

#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
extern char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif

定时器配置函数

#include <REGX52.H>

/*定时器0一毫秒中断函数*/
void Timer0Init()		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
}

void Timer1Init()		//1毫秒@11.0592MHz
{
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0xCD;		//设置定时初值
	TH1 = 0xD4;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
	EA=1;
}

定时器配置头文件

#ifndef __INTERRUPT_H
#define __INTERRUPT_H

void Timer0Init();		//1毫秒@11.0592MHz
void Timer1Init();

#endif

步进电机驱动文件

#include "reg52.h"

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;
extern bit motor_control;
//定义ULN2003控制步进电机管脚
sbit IN1_A=P3^0;
sbit IN2_B=P3^1;
sbit IN3_C=P3^2;
sbit IN4_D=P3^3;




/*******************************************************************************
* 函 数 名       : delay_ms
* 函数功能		 : ms延时函数,ms=1时,大约延时1ms
* 输    入       : ten_us
* 输    出    	 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
	u16 i,j;
	for(i=ms;i>0;i--)
		for(j=110;j>0;j--);
}

/*******************************************************************************
* 函 数 名       : step_motor_28BYJ48_send_pulse
* 函数功能		 : 输出一个数据给ULN2003从而实现向步进电机发送一个脉冲
* 输    入       : step:指定步进序号,可选值0~7
				   dir:方向选择,1:顺时针,0:逆时针
* 输    出    	 : 无
*******************************************************************************/
void step_motor_28BYJ48_send_pulse(u8 step,u8 dir)
{
	u8 temp=step;
	
	if(dir==0)	//如果为逆时针旋转
		temp=7-step;//调换节拍信号
	switch(temp)//8个节拍控制:A->AB->B->BC->C->CD->D->DA
	{
		case 0: IN1_A=1;IN2_B=0;IN3_C=0;IN4_D=0;break;
		case 1: IN1_A=1;IN2_B=1;IN3_C=0;IN4_D=0;break;
		case 2: IN1_A=0;IN2_B=1;IN3_C=0;IN4_D=0;break;
		case 3: IN1_A=0;IN2_B=1;IN3_C=1;IN4_D=0;break;
		case 4: IN1_A=0;IN2_B=0;IN3_C=1;IN4_D=0;break;
		case 5: IN1_A=0;IN2_B=0;IN3_C=1;IN4_D=1;break;
		case 6: IN1_A=0;IN2_B=0;IN3_C=0;IN4_D=1;break;
		case 7: IN1_A=1;IN2_B=0;IN3_C=0;IN4_D=1;break;
		default: IN1_A=0;IN2_B=0;IN3_C=0;IN4_D=0;break;//停止相序	
	}			
}

							
	    u8 key=0;
			u8 dir=0;//默认逆时针方向
			u8 speed=0;
			u8 step=0;	

/*
	以下函数通过矩阵按键配合控制步进电机
*/
void motor_process()
{
	    if(motor_control)
			{
				 unsigned int motor_count;
				motor_count++;
				if(motor_count>300)
				{
					motor_count=0;
					dir=!dir;
				}
				key=0;
			
			step_motor_28BYJ48_send_pulse(step++,dir);
			if(step==8)step=0;		
			delay_ms(speed);
			}
	   
}

步进电机驱动头文件

#ifndef __MOTOR_H
#define __MOTOR_H

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;


void delay_ms(u16 ms);
void step_motor_28BYJ48_send_pulse(u8 step,u8 dir);

void motor_process();

#endif

pca9685舵机函数

#include <reg52.h>     
#include <intrins.h>  
#include <stdio.h>
#include <math.h>

#define PCA9685_adrr 0x80//  1+A5+A4+A3+A2+A1+A0+w/r 
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4


#define PCA9685_MODE1 0x00
#define PCA9685_PRESCALE 0xFE


#define LED0_ON_L 0x06
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09


#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD



//#define SERVO_ANGLE0  102 //0度对应4096的脉宽计数值
//#define SERVO_ANGLE180  476 //180度对应4096的脉宽计算值,根据不同舵机修改

typedef  unsigned char  uchar;        
typedef  unsigned int   uint;  

sbit scl=P2^0;                   //时钟输入线
sbit sda=P2^1;                   //数据输入/输出端


void delayms(uint z);
void delayus();
void init();
void start();
void stop();
void ACK();
void write_byte(uchar byte);
uchar PCA9685_read(uchar address);
uchar read_byte();
void PCA9685_write(uchar address,uchar date);
void reset(void) ;
void setPWMFreq(float freq); 
void servo_control(uchar num, uchar angle);

/**********************函数的声明*********************************/
/*---------------------------------------------------------------
                  毫秒延时函数
----------------------------------------------------------------*/
void delayms(uint z)
{
  uint x,y;
  for(x=z;x>0;x--)
      for(y=148;y>0;y--);
}
/*---------------------------------------------------------------
                                                                        IIC总线所需的通用函数
----------------------------------------------------------------*/
/*---------------------------------------------------------------
                 微妙级别延时函数 大于4.7us
----------------------------------------------------------------*/
void delayus()
{
		_nop_();          //在intrins.h文件里
		_nop_();
		_nop_();
		_nop_();
		_nop_();
}
/*---------------------------------------------------------------
                 IIC总线初始化函数 
----------------------------------------------------------------*/
void init()
{
    sda=1;                //sda scl使用前总是被拉高
    delayus();
    scl=1;
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线启动信号函数
----------------------------------------------------------------*/
void start()
{
    sda=1;
    delayus();
    scl=1;                        //scl拉高时 sda突然来个低电平 就启动了IIC总线
    delayus();
    sda=0;
    delayus();
    scl=0;
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线停止信号函数
----------------------------------------------------------------*/
void stop()
{
    sda=0;
    delayus();
    scl=1;                         //scl拉高时 sda突然来个高电平 就停止了IIC总线
    delayus();
    sda=1;                   
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线应答信号函数
----------------------------------------------------------------*/
void ACK()
{
    uchar i;
    scl=1;
    delayus();
    while((sda=1)&&(i<255))         
			i++;                                        
    scl=0;                                  
    delayus();
}
/*---------------------------------------------------------------
                 写一个字节,无返回值,需输入一个字节值
----------------------------------------------------------------*/
void write_byte(uchar byte)
{
    uchar i,temp;
    temp=byte;
    for(i=0;i<8;i++)
    {
        temp=temp<<1;  
        scl=0;                  
				delayus();
				sda=CY;                 
				delayus();
				scl=1;           
				delayus();
    }
    scl=0;                  
    delayus();
    sda=1;                 
    delayus();
}
/*---------------------------------------------------------------
                 读一个字节函数,有返回值
----------------------------------------------------------------*/
uchar read_byte()
{
		uchar i,j,k;
		scl=0;
		delayus();
		sda=1;
		delayus();
		for(i=0;i<8;i++)        
		{
			delayus();
			scl=1;
			delayus();
			if(sda==1)
			{
					j=1;
			}
			else j=0;
			k=(k<< 1)|j;  
			scl=0;            
		}
		delayus();
		return k;
}


/*---------------------------------------------------------------
                有关PCA9685模块的函数
----------------------------------------------------------------*/
/*---------------------------------------------------------------
                向PCA9685里写地址,数据
----------------------------------------------------------------*/
void PCA9685_write(uchar address,uchar date)
{
		start();
		write_byte(PCA9685_adrr);        //PCA9685的片选地址
		ACK();                          
		write_byte(address);  //写地址控制字节
		ACK();
		write_byte(date);          //写数据
		ACK();
		stop();
}
/*---------------------------------------------------------------
            从PCA9685里的地址值中读数据(有返回值)
----------------------------------------------------------------*/
uchar PCA9685_read(uchar address)
{
		uchar date;
		start();
		write_byte(PCA9685_adrr); //PCA9685的片选地址
		ACK();
		write_byte(address);
		ACK();
		start();
		write_byte(PCA9685_adrr|0x01);        //地址的第八位控制数据流方向,就是写或读
		ACK();
		date=read_byte();
		stop();
		return date;
}
/*---------------------------------------------------------------
                        PCA9685复位
----------------------------------------------------------------*/
void reset(void) 
{
	PCA9685_write(PCA9685_MODE1,0x0);
}


void begin(void) 
{
	reset();
}
/*---------------------------------------------------------------
                                        PCA9685修改频率函数
----------------------------------------------------------------*/
void setPWMFreq(float freq) 
{
		uint prescale,oldmode,newmode;
		float prescaleval;
		freq *= 0.92;  // Correct for overshoot in the frequency setting 
		prescaleval = 25000000;
		prescaleval /= 4096;
		prescaleval /= freq;
		prescaleval -= 1;
		prescale = floor(prescaleval + 0.5);
		
		oldmode = PCA9685_read(PCA9685_MODE1);
		newmode = (oldmode&0x7F) | 0x10; // sleep
		PCA9685_write(PCA9685_MODE1, newmode); // go to sleep
		PCA9685_write(PCA9685_PRESCALE, prescale); // set the prescaler
		PCA9685_write(PCA9685_MODE1, oldmode);
		delayms(2);
		PCA9685_write(PCA9685_MODE1, oldmode | 0xa1); 
}
/*---------------------------------------------------------------
                                PCA9685修改角度函数
num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096
一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时
跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,
off/4096的值就是PWM的占空比。
----------------------------------------------------------------*/
void servo_control(uchar num, uchar angle) 
{
		uint off = 102.4+2.275555556*angle;
		PCA9685_write(LED0_ON_L+4*num,0);
		PCA9685_write(LED0_ON_H+4*num,0);
		PCA9685_write(LED0_OFF_L+4*num,off);
		PCA9685_write(LED0_OFF_H+4*num,off>>8);
}



pca9685舵机函数头文件

#ifndef __SERVO_H
#define __SERVO_H

sbit scl=P2^0;                   //时钟输入线
sbit sda=P2^1;                   //数据输入/输出端


typedef  unsigned char  uchar;        
typedef  unsigned int   uint;  
void begin(void);
void delayms(uint z);
void delayus();
void init();
void start();
void stop();
void ACK();
void write_byte(uchar byte);
uchar PCA9685_read(uchar address);
uchar read_byte();
void PCA9685_write(uchar address,uchar date);
void reset(void) ;
void setPWMFreq(float freq); 
void servo_control(uchar num, uchar angle);

#endif

以下为红外遥控控制机械臂

主函数

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
#include "SERVO.h"
#include "CONTROL.h"
#include "INIT.h"
unsigned char count0=90;//初始化底部舵机角度
unsigned char count1=125;//初始化高度舵机角度
unsigned char count2=0;//初始化前后舵机角度
unsigned char count3=90;//初始化夹爪舵机角度

void main()
{
	Init_system();//系统初始化
	while(1)
	{
		control_process();//红外检测模式		
	}
}


Delay,c


void Delay(unsigned int xms)//自定义毫秒级延时
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

LCD1602.C

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	unsigned char i, j;

	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}

/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}

/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

LCD1602.H

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

INT0.C

#include <REGX52.H>

/**
  * @brief  外部中断0初始化
  * @param  无
  * @retval 无
  */
void Int0_Init(void)//定时器0初始化配置
{
	IT0=1;
	IE0=0;
	EX0=1;
	EA=1;
	PX0=1;
}

/*外部中断0中断函数模板
void Int0_Routine(void) interrupt 0
{
	
}
*/

INT0.H

#ifndef __INT0_H__
#define __INT0_H__

void Int0_Init(void);

#endif

TIMER0.C

#include <REGX52.H>

/**
  * @brief  定时器0初始化
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0;		//设置定时初值
	TH0 = 0;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 0;		//定时器0不计时
}

/**
  * @brief  定时器0设置计数器值
  * @param  Value,要设置的计数器值,范围:0~65535
  * @retval 无
  */
void Timer0_SetCounter(unsigned int Value)
{
	TH0=Value/256;
	TL0=Value%256;
}

/**
  * @brief  定时器0获取计数器值
  * @param  无
  * @retval 计数器值,范围:0~65535
  */
unsigned int Timer0_GetCounter(void)
{
	return (TH0<<8)|TL0;
}

/**
  * @brief  定时器0启动停止控制
  * @param  Flag 启动停止标志,1为启动,0为停止
  * @retval 无
  */
void Timer0_Run(unsigned char Flag)
{
	TR0=Flag;
}

TIMER0.H

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);

#endif

IR.C

#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"

unsigned int IR_Time;
unsigned char IR_State;

unsigned char IR_Data[4];
unsigned char IR_pData;

unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;

/**
  * @brief  红外遥控初始化
  * @param  无
  * @retval 无
  */
void IR_Init(void)
{
	Timer0_Init();
	Int0_Init();
}

/**
  * @brief  红外遥控获取收到数据帧标志位
  * @param  无
  * @retval 是否收到数据帧,1为收到,0为未收到
  */
unsigned char IR_GetDataFlag(void)
{
	if(IR_DataFlag)
	{
		IR_DataFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到连发帧标志位
  * @param  无
  * @retval 是否收到连发帧,1为收到,0为未收到
  */
unsigned char IR_GetRepeatFlag(void)
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag=0;
		return 1;
	}
	return 0;
}

/**
  * @brief  红外遥控获取收到的地址数据
  * @param  无
  * @retval 收到的地址数据
  */
unsigned char IR_GetAddress(void)
{
	return IR_Address;
}

/**
  * @brief  红外遥控获取收到的命令数据
  * @param  无
  * @retval 收到的命令数据
  */
unsigned char IR_GetCommand(void)
{
	return IR_Command;
}

//外部中断0中断函数,下降沿触发执行
void Int0_Routine(void) interrupt 0
{
	if(IR_State==0)				//状态0,空闲状态
	{
		Timer0_SetCounter(0);	//定时计数器清0
		Timer0_Run(1);			//定时器启动
		IR_State=1;				//置状态为1
	}
	else if(IR_State==1)		//状态1,等待Start信号或Repeat信号
	{
		IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间
		Timer0_SetCounter(0);	//定时计数器清0
		//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
		if(IR_Time>12442-500 && IR_Time<12442+500)
		{
			IR_State=2;			//置状态为2
		}
		//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
		else if(IR_Time>10368-500 && IR_Time<10368+500)
		{
			IR_RepeatFlag=1;	//置收到连发帧标志位为1
			Timer0_Run(0);		//定时器停止
			IR_State=0;			//置状态为0
		}
		else					//接收出错
		{
			IR_State=1;			//置状态为1
		}
	}
	else if(IR_State==2)		//状态2,接收数据
	{
		IR_Time=Timer0_GetCounter();	//获取上一次中断到此次中断的时间
		Timer0_SetCounter(0);	//定时计数器清0
		//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
		if(IR_Time>1032-500 && IR_Time<1032+500)
		{
			IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));	//数据对应位清0
			IR_pData++;			//数据位置指针自增
		}
		//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
		else if(IR_Time>2074-500 && IR_Time<2074+500)
		{
			IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));	//数据对应位置1
			IR_pData++;			//数据位置指针自增
		}
		else					//接收出错
		{
			IR_pData=0;			//数据位置指针清0
			IR_State=1;			//置状态为1
		}
		if(IR_pData>=32)		//如果接收到了32位数据
		{
			IR_pData=0;			//数据位置指针清0
			if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3]))	//数据验证
			{
				IR_Address=IR_Data[0];	//转存数据
				IR_Command=IR_Data[2];
				IR_DataFlag=1;	//置收到连发帧标志位为1
			}
			Timer0_Run(0);		//定时器停止
			IR_State=0;			//置状态为0
		}
	}
}

IR.H

#ifndef __IR_H__
#define __IR_H__

#define IR_POWER		0x45
#define IR_MODE			0x46
#define IR_MUTE			0x47
#define IR_START_STOP	0x44
#define IR_PREVIOUS		0x40
#define IR_NEXT			0x43
#define IR_EQ			0x07
#define IR_VOL_MINUS	0x15
#define IR_VOL_ADD		0x09
#define IR_0			0x16
#define IR_RPT			0x19
#define IR_USD			0x0D
#define IR_1			0x0C
#define IR_2			0x18
#define IR_3			0x5E
#define IR_4			0x08
#define IR_5			0x1C
#define IR_6			0x5A
#define IR_7			0x42
#define IR_8			0x52
#define IR_9			0x4A

void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);

#endif

SERVO.C

#include <reg52.h>     
#include <intrins.h>  
#include <stdio.h>
#include <math.h>

#define PCA9685_adrr 0x80//  1+A5+A4+A3+A2+A1+A0+w/r 
#define PCA9685_SUBADR1 0x2
#define PCA9685_SUBADR2 0x3
#define PCA9685_SUBADR3 0x4


#define PCA9685_MODE1 0x00
#define PCA9685_PRESCALE 0xFE


#define LED0_ON_L 0x06
#define LED0_ON_H 0x07
#define LED0_OFF_L 0x08
#define LED0_OFF_H 0x09


#define ALLLED_ON_L 0xFA
#define ALLLED_ON_H 0xFB
#define ALLLED_OFF_L 0xFC
#define ALLLED_OFF_H 0xFD



//#define SERVO_ANGLE0  102 //0度对应4096的脉宽计数值
//#define SERVO_ANGLE180  476 //180度对应4096的脉宽计算值,根据不同舵机修改

typedef  unsigned char  uchar;        
typedef  unsigned int   uint;  

sbit scl=P2^0;                   //时钟输入线
sbit sda=P2^1;                   //数据输入/输出端


void delayms(uint z);
void delayus();
void init();
void start();
void stop();
void ACK();
void write_byte(uchar byte);
uchar PCA9685_read(uchar address);
uchar read_byte();
void PCA9685_write(uchar address,uchar date);
void reset(void) ;
void setPWMFreq(float freq); 
void servo_control(uchar num, uchar angle);

/**********************函数的声明*********************************/
/*---------------------------------------------------------------
                  毫秒延时函数
----------------------------------------------------------------*/
void delayms(uint z)
{
  uint x,y;
  for(x=z;x>0;x--)
      for(y=148;y>0;y--);
}
/*---------------------------------------------------------------
                                                                        IIC总线所需的通用函数
----------------------------------------------------------------*/
/*---------------------------------------------------------------
                 微妙级别延时函数 大于4.7us
----------------------------------------------------------------*/
void delayus()
{
		_nop_();          //在intrins.h文件里
		_nop_();
		_nop_();
		_nop_();
		_nop_();
}
/*---------------------------------------------------------------
                 IIC总线初始化函数 
----------------------------------------------------------------*/
void init()
{
    sda=1;                //sda scl使用前总是被拉高
    delayus();
    scl=1;
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线启动信号函数
----------------------------------------------------------------*/
void start()
{
    sda=1;
    delayus();
    scl=1;                        //scl拉高时 sda突然来个低电平 就启动了IIC总线
    delayus();
    sda=0;
    delayus();
    scl=0;
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线停止信号函数
----------------------------------------------------------------*/
void stop()
{
    sda=0;
    delayus();
    scl=1;                         //scl拉高时 sda突然来个高电平 就停止了IIC总线
    delayus();
    sda=1;                   
    delayus();
}
/*---------------------------------------------------------------
                 IIC总线应答信号函数
----------------------------------------------------------------*/
void ACK()
{
    uchar i;
    scl=1;
    delayus();
    while((sda=1)&&(i<255))         
			i++;                                        
    scl=0;                                  
    delayus();
}
/*---------------------------------------------------------------
                 写一个字节,无返回值,需输入一个字节值
----------------------------------------------------------------*/
void write_byte(uchar byte)
{
    uchar i,temp;
    temp=byte;
    for(i=0;i<8;i++)
    {
        temp=temp<<1;  
        scl=0;                  
				delayus();
				sda=CY;                 
				delayus();
				scl=1;           
				delayus();
    }
    scl=0;                  
    delayus();
    sda=1;                 
    delayus();
}
/*---------------------------------------------------------------
                 读一个字节函数,有返回值
----------------------------------------------------------------*/
uchar read_byte()
{
		uchar i,j,k;
		scl=0;
		delayus();
		sda=1;
		delayus();
		for(i=0;i<8;i++)        
		{
			delayus();
			scl=1;
			delayus();
			if(sda==1)
			{
					j=1;
			}
			else j=0;
			k=(k<< 1)|j;  
			scl=0;            
		}
		delayus();
		return k;
}


/*---------------------------------------------------------------
                有关PCA9685模块的函数
----------------------------------------------------------------*/
/*---------------------------------------------------------------
                向PCA9685里写地址,数据
----------------------------------------------------------------*/
void PCA9685_write(uchar address,uchar date)
{
		start();
		write_byte(PCA9685_adrr);        //PCA9685的片选地址
		ACK();                          
		write_byte(address);  //写地址控制字节
		ACK();
		write_byte(date);          //写数据
		ACK();
		stop();
}
/*---------------------------------------------------------------
            从PCA9685里的地址值中读数据(有返回值)
----------------------------------------------------------------*/
uchar PCA9685_read(uchar address)
{
		uchar date;
		start();
		write_byte(PCA9685_adrr); //PCA9685的片选地址
		ACK();
		write_byte(address);
		ACK();
		start();
		write_byte(PCA9685_adrr|0x01);        //地址的第八位控制数据流方向,就是写或读
		ACK();
		date=read_byte();
		stop();
		return date;
}
/*---------------------------------------------------------------
                        PCA9685复位
----------------------------------------------------------------*/
void reset(void) 
{
	PCA9685_write(PCA9685_MODE1,0x0);
}


void begin(void) 
{
	reset();
}
/*---------------------------------------------------------------
                                        PCA9685修改频率函数
----------------------------------------------------------------*/
void setPWMFreq(float freq) 
{
		uint prescale,oldmode,newmode;
		float prescaleval;
		freq *= 0.92;  // Correct for overshoot in the frequency setting 
		prescaleval = 25000000;
		prescaleval /= 4096;
		prescaleval /= freq;
		prescaleval -= 1;
		prescale = floor(prescaleval + 0.5);
		
		oldmode = PCA9685_read(PCA9685_MODE1);
		newmode = (oldmode&0x7F) | 0x10; // sleep
		PCA9685_write(PCA9685_MODE1, newmode); // go to sleep
		PCA9685_write(PCA9685_PRESCALE, prescale); // set the prescaler
		PCA9685_write(PCA9685_MODE1, oldmode);
		delayms(2);
		PCA9685_write(PCA9685_MODE1, oldmode | 0xa1); 
}
/*---------------------------------------------------------------
                                PCA9685修改角度函数
num:舵机PWM输出引脚0~15,on:PWM上升计数值0~4096,off:PWM下降计数值0~4096
一个PWM周期分成4096份,由0开始+1计数,计到on时跳变为高电平,继续计数到off时
跳变为低电平,直到计满4096重新开始。所以当on不等于0时可作延时,当on等于0时,
off/4096的值就是PWM的占空比。
----------------------------------------------------------------*/
void servo_control(uchar num, uchar angle) 
{
		uint off = 102.4+2.275555556*angle;
		PCA9685_write(LED0_ON_L+4*num,0);
		PCA9685_write(LED0_ON_H+4*num,0);
		PCA9685_write(LED0_OFF_L+4*num,off);
		PCA9685_write(LED0_OFF_H+4*num,off>>8);
}



SERVO.H

#ifndef __SERVO_H
#define __SERVO_H

sbit scl=P2^0;                   //时钟输入线
sbit sda=P2^1;                   //数据输入/输出端


typedef  unsigned char  uchar;        
typedef  unsigned int   uint;  
void begin(void);
void delayms(uint z);
void delayus();
void init();
void start();
void stop();
void ACK();
void write_byte(uchar byte);
uchar PCA9685_read(uchar address);
uchar read_byte();
void PCA9685_write(uchar address,uchar date);
void reset(void) ;
void setPWMFreq(float freq); 
void servo_control(uchar num, uchar angle);

#endif

CONTROL.C

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
#include "SERVO.h"

sbit Buzzer=P2^5;
unsigned char Command;
unsigned char Address;
extern unsigned char count0;
extern unsigned char count1;
extern unsigned char count2;
extern unsigned char count3;

void control_process()
{
	if(IR_GetDataFlag() || IR_GetRepeatFlag())	//如果收到数据帧或者收到连发帧
		{
			Address=IR_GetAddress();		//获取遥控器地址码
			Command=IR_GetCommand();		//获取遥控器命令码
			
			/*
			底部舵机
			*/
			if(Command==IR_VOL_MINUS)		//如果遥控器VOL-按键按下
			{
				if(count0>0)
				{
					count0--;						//Num自减
					servo_control(4,count0);
					LCD_ShowNum(1,4,count0,3);		//显示舵机工作状态
				}
				if(count0==0)
				{
						Buzzer=~Buzzer;
				}
			}
			if(Command==IR_VOL_ADD)			//如果遥控器VOL+按键按下
			{
				if(count0<180)
				{
						count0++;							//Num自增
					servo_control(4,count0);
					LCD_ShowNum(1,4,count0,3);		//显示舵机工作状态
				}
				if(count0==180)
				{
						Buzzer=~Buzzer;
				}
			}
			 
       if(Command==IR_RPT	)		//如果遥控器VOL-按键按下
			{
				if(count1>0)
				{
					count1--;						//Num自减
					servo_control(1,count1);
					LCD_ShowNum(1,11,count1,3);		//显示舵机工作状态
				}
				if(count1==0)
				{
						Buzzer=~Buzzer;
				}
			}
			
			/*
				高度舵机
			*/
			if(Command==IR_USD)			//如果遥控器VOL+按键按下
			{
				if(count1<180)
				{
						count1++;							//Num自增
					servo_control(1,count1);
					LCD_ShowNum(1,11,count1,3);		//显示舵机工作状态
				}
				if(count1==180)
				{
						Buzzer=~Buzzer;
				}
			}	
      
      /*
				前后舵机
			*/			
			if(Command==IR_2	)		//如果遥控器VOL-按键按下
			{
				if(count2>0)
				{
					count2--;						//Num自减
					servo_control(2,count2);
					LCD_ShowNum(2,4,count2,3);		//显示舵机工作状态
				}
				if(count2==0)
				{
						Buzzer=~Buzzer;
				}
			}
			if(Command==IR_3)			//如果遥控器VOL+按键按下
			{
				if(count2<180)
				{
						count2++;							//Num自增
					servo_control(2,count2);
					LCD_ShowNum(2,4,count2,3);		//显示舵机工作状态
				}
				if(count2==180)
				{
						Buzzer=~Buzzer;
				}
			}	
			
			 /*
			   夹爪舵机
			*/			
			if(Command==IR_5	)		//如果遥控器VOL-按键按下
			{
				if(count3>90)
				{
					count3--;						//Num自减
					servo_control(3,count3);
					LCD_ShowNum(2,11,count3,3);		//显示舵机工作状态
				}
				if(count3==90)
				{
						Buzzer=~Buzzer;
				}
			}
			if(Command==IR_6)			//如果遥控器VOL+按键按下
			{
				if(count3<180)
				{
						count3++;							//Num自增
					servo_control(3,count3);
					LCD_ShowNum(2,11,count3,3);		//显示舵机工作状态
				}
				if(count3==180)
				{
						Buzzer=~Buzzer;
				}
			}
			
			
}
}
	
	

CONTROL.H

#ifndef __CONTROL_H__
#define __CONTROL__

void control_process();

#endif

INIT.C

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
#include "SERVO.h"
#include "CONTROL.h"

extern unsigned char count0;
extern unsigned char count1;
extern unsigned char count2;
extern unsigned char count3;

void Init_system()
{
		LCD_Init();
		LCD_ShowString(1,1,"L1:    L2:    M4");
		LCD_ShowString(2,1,"L3:    L4:    IR");
		LCD_ShowNum(1,4,count0,3);		//显示舵机工作状态
		LCD_ShowNum(1,11,count1,3);		//显示舵机工作状态
		LCD_ShowNum(2,4,count2,3);		//显示舵机工作状态
		LCD_ShowNum(2,11,count3,3);		//显示舵机工作状态
		IR_Init();
		init();
		begin();
		setPWMFreq(50);
		/*
		各舵机状态初始化
		*/
		servo_control(4,90);//底部舵机初始化0度
		delayms(300);
		servo_control(1,125);//高度调节舵机初始化
		delayms(300);
		servo_control(2,0);//前后舵机初始化
		delayms(300);
		servo_control(3,90);//抓取舵机初始化
		delayms(300);
}

INIT.H

#ifndef __INIT_H__
#define __INIT_H__

void Init_system();

#endif

猜你喜欢

转载自blog.csdn.net/qq_61134394/article/details/126199093