【附报告 51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果)

您所在的位置:网站首页 单片机主机 【附报告 51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果)

【附报告 51单片机多机通信】主机控制从机1步进电机、从机2超声波测距(主机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果)

2024-02-02 02:14| 来源: 网络整理| 查看: 265

一、设计目标

    使用三个51单片机,实现多机通信。

二、实现功能

    1号单片机可遥控2、3号单片机、超声波测距等;

    1号单片机可集中显示步进电机的转速、转向、启停状态以及超声波测距结果。

三、硬件原理

    51单片机,超声波测距模块,四相双极步进电机,导线,静态数码管,动态数码管,独立按键,74HC245芯片,74HC138芯片。

        5460d38d3c864746851f84ac52e3c071.png

 图1   

 

3eb589ddcfcd44d68535ccd3f95ba7eb.png

图2

 

455801d074444c8097fc8d250fd8239a.png

图3

 

7f89457d0aa7486faab68dd705b11ebb.png

图4

 

3c5529e346d14dc1821ac61db95af7ec.png

图5

6f528dbb348446cb8c12bbe522063d6d.png

图6

 

09133c9243cb4976860770b4600377d5.png

图7

 

90f029f2c9144f47bfa20e0591e991fc.png

图8

 

542c6b4c53844d65802626d82e35ff54.png

图9

 

81b125d4c9ad42c9adeaea8b14d458c3.png

图10

 

b318b09ec4594c6a898d957509019028.png

图11 

 

5010f1a6c98545e381d0acd73daaf882.png

图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时,发送数据帧。

四、程序流程图

cd35b7baaad34204800defa0a0e2f4bb.png

 

五、代码说明

    主机的代码由主程序、握手子程序、判断按键抬起子程序、延时程序、发送数字程序、同步电机程序、校验速度程序、清零程序、测距程序、显示中断程序组成。

    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