【附报告 51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果) |
您所在的位置:网站首页 › 单片机主机 › 【附报告 51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果) |
一、设计目标 使用三个51单片机,实现多机通信。 二、实现功能 1号单片机可遥控2、3号单片机、超声波测距等; 1号单片机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果。 三、硬件原理 51单片机,超声波测距模块,四相双极步进电机,导线,静态数码管,动态数码管,独立按键,74HC245芯片,74HC138芯片。 图1
图2
图3
图4
图5 图6
图7
图8
图9
图10
图11
图12 图1为超声波测距模块的内部电路图。 图2为超声波测距的输出和接收声波的时序逻辑图。 图3、图5为动态数码管的控制。使用51单片机的P0_0、P0_1、P0_2端连接74HC138芯片的A、B、C引脚,从而控制哪一个数码管被点亮。 图6为静态数码管的内部电路图; 图7为控制步进电机的部分的内部电路图; 图4为独立按键设置; 图8为四线双极性步进电机内部原理图; 图9为步进电机转动的原理图: ①A加正极,A-加负极,B加负极,B-加负极(电机状态,N极朝上)。 ②A加负极,A-加负极,B加正极,B-加负极(电机状态,N极朝右,旋转90度)。 ③A加负极,A-加正极,B加负极,B-加负极(电机状态,N极朝下)。 ④A加负极,A-加负极,B加负极,B-加正极(电机状态,N极朝左)。 只要依次给相应引脚相应的电平就可以使得电机转动,转动的最小角度为90度。为了实现更加小角度的供电,本次设计中的供电顺序:A、AB、B、BA-、A-、A-B-、B-、B-A,转动的最小角度为45°。 图10、图11、图12为多机通信工作原理。 在多机通信中,主机必须要能对各个从机进行识别,在51系列单片机中可以通过SCON寄存器的SM2位来实现。当串口以方式2或方式3发送数据时,每一帧信息都是11位,第9位是数据可编程位,通过给TB8置1或置0来区别地址帧和数据帧,当该位为1时,发送地址帧;该位为0时,发送数据帧。 四、程序流程图
五、代码说明 主机的代码由主程序、握手子程序、判断按键抬起子程序、延时程序、发送数字程序、同步电机程序、校验速度程序、清零程序、测距程序、显示中断程序组成。 1号从机代码由主程序、延时程序、通信中断程序、调速程序、速度校验程序、显示程序组成。 2号从机代码由主程序、延时程序、通信中断程序、测距程序组成。 1.主函数 ①主机 /**********************主函数*************************/ void main() { zjcsh(); key(); lian(); return 0; }
②从机1 /*************************主函数**********************************************************/ void main() { csh(); Int0Init();//声明加速的中断 Int1Init();//声明减速的中断 while(1) { mode(key());//先按键扫描,后选择模式 } }
③从机2 /*************************主函数*******************************/ void main() { unsigned int time = 0; csh(); while(1) { time = RunOnce();//传感器接收到高电平的时间 distance = GetDistance(time); xianshi(distance); } }
2.子函数1 主机的延时函数。 /**********************延时函数*************************/ void delay(unsigned int t)//调节给电机供电的时间,从而调速 { while(t--);
3.子函数2 主机的动态数码管显示函数。用于动态数码管显示超声波测距的结果。 }/*********************动态数码管显示*************************/ void shumaguan(unsigned char wei,num)//第几位(wei)显示 { switch(wei) { case 1:P3_5=0;P3_6=0;P3_7=0;break; case 2:P3_5=0;P3_6=0;P3_7=1;break; case 3:P3_5=0;P3_6=1;P3_7=0;break; case 4:P3_5=0;P3_6=1;P3_7=1;break; case 5:P3_5=1;P3_6=0;P3_7=0;break; case 6:P3_5=1;P3_6=0;P3_7=1;break; case 7:P3_5=1;P3_6=1;P3_7=0;break; case 8:P3_5=1;P3_6=1;P3_7=1;break; } P1=shuzi[num]; delay(1); }
4.子函数3 主机的测距显示函数。用于控制动态数码管显示超声波测距的结果。 /**********************测距显示*************************/ void xianshi(int d) { int k=8,m; while(d!=0) { m=(d%10);//在数码管上显示个位,从后往前显示 shumaguan(k,m); k--;//数码管向前移位 d=d/10; } }5.子函数4 主机的初始化函数。定义工作方式3,定义SM2的初态。定义波特率9600。 /**********************主机初始化*************************/ void zjcsh() { TMOD=0x20; SCON=0xd0; TH1=TL1=0xfd; PCON=0X00;; TR1=1; ES=1; EA=1; }
6.子函数5 主机与从机建立联系的函数,并且发地址用于呼叫。 /**********************建立联系——发地址*************************/ void lian(date) { TB8=1;//发送的地址 if(date==1&&2&&3&&4&&5&&6) { SBUF=0xA1;//从机1 步进电机 } else { SBUF=0xA2;//从机2 超声波测距 } while(!TI); TI=0; }
7.子函数6 主机的中断接受及发数据函数。判断收到的从机反馈信息,判断是哪个从机发来的,并给两个从机分别发送不同的指令信息用于控制。若收到的是从机反馈的数据,则分别通过不同方式进行显示。动态数码管显示超声波测距结果,静态数码管显示步进电机的转速。 /**********************中断接收——发数据*************************/ void zd()interrupt 4 { RI=0; addr=SBUF; if(RB8==1)//判断是否为地址 { if(addr==0xA1) { TB8=0;//发送的是数据 SBUF=date;//给从机1发数据 while(!TI); TI=0; } if(addr==0xA2) { TB8=0;//发送的是数据 SBUF=date;//给从机2发数据 while(!TI); TI=0; } } else { if(RB8==0)//判断是否为数据 { dat=SBUF; if(addr==0xA1) { P0=smg[dat];//显示电机速度 } else { xianshi(dat);//显示测距结果 } } } }
8.子函数7 主机的按键判断函数。用于生成对步进电机的控制指令。 /**********************按键判断*************************/ void key() { if(P2_0==0) date=1;//顺时针 if(P2_1==0) date=2;//逆时针 if(P2_2==0) date=3;//启动 if(P2_3==0) date=4;//停机 if(P2_4==0) date=5;//加速 if(P2_5==0) date=6;//减速 if(P2_6==0) date=7;//超声波测距 if(P2_7==0) date=8; }
9.子函数8 从机1的延时函数。 /***************************延时函数*******************************/ void delay(unsigned int t)//调节给电机供电的时间,从而调速 { while(t--); }
10.子函数9 从机1的按键扫描函数。用于在从机端控制步进电机。 /***************************按键扫描***************************************/ int key() { if(P0_0==0) i=1;//顺时针 if(P0_1==0) i=2;//逆时针 if(P0_2==0) i=3;//启动/回到初速度 if(P0_3==0) i=4;//停机 return i; }
11.子函数10 从机1的模式判断函数。用于控制步进电机。 /****************************模式判断**************************************/ void mode(int i)//由按键扫描返回值判断 { switch(i) { /****************模式1 正转****************/ case 1: while(1) { for(n=0;n0;j--); return; }
17.子函数16 从机2的数码管显示函数。 /******************************************数码管控制函数***************************************/ unsigned char shuzi[]={0x3f/*0*/,0x06/*1*/,0x5b/*2*/,0x4f/*3*/,0x66/*4*/,0x6d/*5*/,0x7d/*6*/,0x07/*7*/,0x7f/*8*/,0x6f/*9*/};//显示的数字(num)的数组 void shumaguan(unsigned char wei,num)//第几位(wei)显示 { switch(wei) { case 1:P0_2=0;P0_1=0;P0_0=0;break; case 2:P0_2=0;P0_1=0;P0_0=1;break; case 3:P0_2=0;P0_1=1;P0_0=0;break; case 4:P0_2=0;P0_1=1;P0_0=1;break; case 5:P0_2=1;P0_1=0;P0_0=0;break; case 6:P0_2=1;P0_1=0;P0_0=1;break; case 7:P0_2=1;P0_1=1;P0_0=0;break; case 8:P0_2=1;P0_1=1;P0_0=1;break; } P3=shuzi[num]; delay(1); }
18.子函数17 从机2的定时器函数。用于产生方波。 /**********************************定时器函数 延时10us*************************************/ void Delay10us() { TMOD |= 0x01;//16位定时器/计数器 TH0 = 0xFF;//赋初值 TL0 = 0xF6;//赋初值 TR0 = 1;//启动 while(!TF0);//溢出 TF0 = 0;//清溢出 }
19.子函数18 从机2的算距离函数。 /******************************************算距离函数***************************************/ float GetDistance(unsigned int time) { float distance; distance = (float)time * 0.017;//cm 距离=高电平时间×声速/2 0.017cm/us return distance;//将距离返回主函数 }
20.子函数19 从机2的测时间函数。 /******************************************测时间函数***************************************/ unsigned int RunOnce() { unsigned int time; /******************发送10us高电平信号*************/ Trig = 0; Trig = 1; Delay10us(); Trig = 0; /**************等待高电平信号接收*****************/ while(!Echo); /*********T0清0重新计数(高电平持续时间)*********/ TH0 = 0; TL0 = 0; TR0 = 1; /*********等待高电平信号接收结束******************/ while(Echo); /*******************关闭T0计数********************/ TR0 = 0; /**********高电平时间赋值,单位us*****************/ time = TH0*256 + TL0; TH0 = 0; TL0 = 0; return time; }
21.子函数20 从机2的动态数码管显示结果函数。用于处理数据后交给数码管函数显示结果。 /**************************************动态数码管显示结果***********************************/ void xianshi(int d) { int k=8,m; while(d!=0) { m=(d%10);//在数码管上显示个位,从后往前显示 shumaguan(k,m); k--;//数码管向前移位 d=d/10; } }
22.子函数21 从机2的通信初始化函数。 /*************************初始化*******************************/ void csh() { TMOD=0x20; //定时器工作方式2 REN=1; SM0=1; SM1=1; SM2=1;//主机置0 从机置1 TH1=TL1=0xfd; //9600波特率 SCON=0X00; TR1=1;//开定时 TI=0; RI=0; ES=1;//开串口中断 EA=1;//串口总开关 }
23.子函数22 从机2的中断接收函数。 /*************************中断收*******************************/ void zd() interrupt 4 { RI=0; if(RB8==1)//再判断接受数据=地址 { if(addr==SBUF) { SM2=0; P2_0=0; TB8=1; SBUF=0XA2; while(!TI); TI=0; } } else { date=SBUF; P2_1=0; TB8=0; SBUF=distance; while(!TI); TI=0; SM2=1; } }六、源代码 1.主机 #include int addr; int date,dat; int num; int D=1;//V在延时函数中,用于调速;D用来控制数码管显示的数字 smg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//用于显示档速 0-f unsigned char shuzi[]={0x3f/*0*/,0x06/*1*/,0x5b/*2*/,0x4f/*3*/,0x66/*4*/,0x6d/*5*/,0x7d/*6*/,0x07/*7*/,0x7f/*8*/,0x6f/*9*/};//显示的数字(num)的数组 /**********************延时函数*************************/ void delay(unsigned int t)//调节给电机供电的时间,从而调速 { while(t--); }/*********************动态数码管显示*************************/ void shumaguan(unsigned char wei,num)//第几位(wei)显示 { switch(wei) { case 1:P3_5=0;P3_6=0;P3_7=0;break; case 2:P3_5=0;P3_6=0;P3_7=1;break; case 3:P3_5=0;P3_6=1;P3_7=0;break; case 4:P3_5=0;P3_6=1;P3_7=1;break; case 5:P3_5=1;P3_6=0;P3_7=0;break; case 6:P3_5=1;P3_6=0;P3_7=1;break; case 7:P3_5=1;P3_6=1;P3_7=0;break; case 8:P3_5=1;P3_6=1;P3_7=1;break; } P1=shuzi[num]; delay(1); } /**********************测距显示*************************/ void xianshi(int d) { int k=8,m; while(d!=0) { m=(d%10);//在数码管上显示个位,从后往前显示 shumaguan(k,m); k--;//数码管向前移位 d=d/10; } } /**********************主机初始化*************************/ void zjcsh() { TMOD=0x20; SCON=0xd0; TH1=TL1=0xfd; PCON=0X00;; TR1=1; ES=1; EA=1; } /**********************建立联系——发地址*************************/ void lian(date) { TB8=1;//发送的地址 if(date==1&&2&&3&&4&&5&&6) { SBUF=0xA1;//从机1 步进电机 } else { SBUF=0xA2;//从机2 超声波测距 } while(!TI); TI=0; } /**********************中断接收——发数据*************************/ void zd()interrupt 4 { RI=0; addr=SBUF; if(RB8==1)//判断是否为地址 { if(addr==0xA1) { TB8=0;//发送的是数据 SBUF=date;//给从机1发数据 while(!TI); TI=0; } if(addr==0xA2) { TB8=0;//发送的是数据 SBUF=date;//给从机2发数据 while(!TI); TI=0; } } else { if(RB8==0)//判断是否为数据 { dat=SBUF; if(addr==0xA1) { P0=smg[dat];//显示电机速度 } else { xianshi(dat);//显示测距结果 } } } } /**********************按键判断*************************/ void key() { if(P2_0==0) date=1;//顺时针 if(P2_1==0) date=2;//逆时针 if(P2_2==0) date=3;//启动 if(P2_3==0) date=4;//停机 if(P2_4==0) date=5;//加速 if(P2_5==0) date=6;//减速 if(P2_6==0) date=7;//超声波测距 if(P2_7==0) date=8; } /**********************主函数*************************/ void main() { zjcsh(); key(); lian(); return 0; }2.从机一 (步进电机) #include int addr=0xA1; int date; unsigned char SSZ[] = {0x08,0x0a,0x02,0x06,0x04,0x05,0x01,0x09};//顺时针数组 unsigned char NSZ[] = {0x09,0x01,0x05,0x04,0x06,0x02,0x0a,0x08};//逆时针数组 smg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//用于显示档速 0-f int V=500,D=1;//V在延时函数中,用于调速;D用来控制数码管显示的数字 int i=4;//先使电机处于停机状态 int n=0;//用于循环给电机通电 /*************************步进电机******************************************************************************/ /***************************延时函数*******************************/ void delay(unsigned int t)//调节给电机供电的时间,从而调速 { while(t--); } /***************************按键扫描***************************************/ int key() { if(P0_0==0) i=1;//顺时针 if(P0_1==0) i=2;//逆时针 if(P0_2==0) i=3;//启动/回到初速度 if(P0_3==0) i=4;//停机 return i; } /****************************模式判断**************************************/ void mode(int i)//由按键扫描返回值判断 { switch(i) { /****************模式1 正转****************/ case 1: while(1) { for(n=0;n0;j--); return; } /******************************************数码管控制函数***************************************/ unsigned char shuzi[]={0x3f/*0*/,0x06/*1*/,0x5b/*2*/,0x4f/*3*/,0x66/*4*/,0x6d/*5*/,0x7d/*6*/,0x07/*7*/,0x7f/*8*/,0x6f/*9*/};//显示的数字(num)的数组 void shumaguan(unsigned char wei,num)//第几位(wei)显示 { switch(wei) { case 1:P0_2=0;P0_1=0;P0_0=0;break; case 2:P0_2=0;P0_1=0;P0_0=1;break; case 3:P0_2=0;P0_1=1;P0_0=0;break; case 4:P0_2=0;P0_1=1;P0_0=1;break; case 5:P0_2=1;P0_1=0;P0_0=0;break; case 6:P0_2=1;P0_1=0;P0_0=1;break; case 7:P0_2=1;P0_1=1;P0_0=0;break; case 8:P0_2=1;P0_1=1;P0_0=1;break; } P3=shuzi[num]; delay(1); } /**********************************定时器函数 延时10us*************************************/ void Delay10us() { TMOD |= 0x01;//16位定时器/计数器 TH0 = 0xFF;//赋初值 TL0 = 0xF6;//赋初值 TR0 = 1;//启动 while(!TF0);//溢出 TF0 = 0;//清溢出 } /******************************************算距离函数***************************************/ float GetDistance(unsigned int time) { float distance; distance = (float)time * 0.017;//cm 距离=高电平时间×声速/2 0.017cm/us return distance;//将距离返回主函数 } /******************************************测时间函数***************************************/ unsigned int RunOnce() { unsigned int time; /******************发送10us高电平信号*************/ Trig = 0; Trig = 1; Delay10us(); Trig = 0; /**************等待高电平信号接收*****************/ while(!Echo); /*********T0清0重新计数(高电平持续时间)*********/ TH0 = 0; TL0 = 0; TR0 = 1; /*********等待高电平信号接收结束******************/ while(Echo); /*******************关闭T0计数********************/ TR0 = 0; /**********高电平时间赋值,单位us*****************/ time = TH0*256 + TL0; TH0 = 0; TL0 = 0; return time; } /**************************************动态数码管显示结果***********************************/ void xianshi(int d) { int k=8,m; while(d!=0) { m=(d%10);//在数码管上显示个位,从后往前显示 shumaguan(k,m); k--;//数码管向前移位 d=d/10; } } /*************************初始化*******************************/ void csh() { TMOD=0x20; //定时器工作方式2 REN=1; SM0=1; SM1=1; SM2=1;//主机置0 从机置1 TH1=TL1=0xfd; //9600波特率 SCON=0X00; TR1=1;//开定时 TI=0; RI=0; ES=1;//开串口中断 EA=1;//串口总开关 } /*************************中断收*******************************/ void zd() interrupt 4 { RI=0; if(RB8==1)//再判断接受数据=地址 { if(addr==SBUF) { SM2=0; P2_0=0; TB8=1; SBUF=0XA2; while(!TI); TI=0; } } else { date=SBUF; P2_1=0; TB8=0; SBUF=distance; while(!TI); TI=0; SM2=1; } } /*************************主函数*******************************/ void main() { unsigned int time = 0; csh(); while(1) { time = RunOnce();//传感器接收到高电平的时间 distance = GetDistance(time); xianshi(distance); } }
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |