**说明:
**本博客主要是将光立方作为展示品来制作,通过超声波来感应距离,当有人靠近来观赏时候超声波检测的距离小于设定距离时候进行其他团的变化,同时触发重量感应器,当放上不同重量的东西的时候,可以变化不同类型的图案,达到人机交互。
一、准备材料:
蓝色LED雾灯: 512个(雾灯具有很好的视觉效果)
74HC5965N锁存器:8个
LED灯底座 :1个
重量检测器(HX711AD模块和四个应变片) :1个
AT89C51单片机: 1个
STC12C5A单片机:1个
二、各部分模块的制作
1、LED灯的焊接:焊接LED灯的教程网上已经一大堆了,这部分就略过了。
2、重量感应器的制作:
重量传感器主要有应变片和HX711模块组成,其原理图如下:
桥式电路组成重量感应,当有物体或者重量时候,应变片弯曲,同时引起电阻的改变,输出的电压产生变化,由HX711接收到变化转化成为数字信号发送给单片机(由51单片机读取重量变化的程序见结尾)。
3、超声波:
超声波主要由51的单片机来控制(代码见结尾)
三、各模块的功能以及共同的联系与工作
发光LED部分:由512个LED灯组成,每层有64个LED灯,每层的所有LED灯的阳极都
是连在一块的,模板上共有64个列,每个列的阴极都是连在一块的。
所以想让某个灯泡发光,只用给灯泡所在的层通电,同时所在的列
接地即可。
STC12C5A内部保存了所需的动画,根据51单片机进行处理相关
数据发送到显示专用的STC12C5A单片机的进行相应的动画选择。
重量检测器:该检测器由四片应变片组成桥式电路,粘在铝棒上,当有物体时候,
铝棒弯曲时候应变片同时收缩或者扩展引起电阻值改变,使得输出电压
变化。同时HX711数模转化模块接收到变化进行处理,经处理的数据传
送到51单片机上,得到数据进行处理。放上不同的
物体就会有不同的变化。
超声波及处理模块:超声波进行距离测试,用于检测有人靠近,或者距离小于设定的
距离。当符合时候,51单片机会进行相应的数据处理。
三个模块之间的合作:
整体上电后,发光LED部分进行预设动画显示,同时超声波进行人距离的检测,当有人靠近预设的距离时候,51单片机处理模块进行数据处理,同时触发重量检测器,此时可以进行重量的检测。重量检测器检测重量变化得到重量数据回传给51单片机处理模块,处理模块将所得的数据再次传输给STC12C5A,进行发光LED部分相应的动画模式选择。
四、制作过程遇到的问题及解决方法
问题一:
在整体焊接好之后512个LED灯不可能一次性全部按照所设想的那样不出差错的该亮的亮,该灭的灭,层共阳,列共阴,六十四个阴极和八个阳极,如上图一样,难免会出现短接现象,导致出现不必要的亮。这就是这部分的问题,焊接过程中难免会出错,所以焊接好之后需要仔细查看每个LED灯正负极。
问题二:
同样也是焊接问题,但是该部分不是LED灯的焊接部分,而是底座与LED灯部分的连接。该光立方是由8个锁存器与LED灯和单片机相连,所以芯片的引脚也要注意不要出现虚焊现象。
问题三:
在一切硬件的没有问题之后,可以进行LED点阵的编写。但是首先我们必须要熟悉所选的锁存器的使用方法,这样才能编写出最基础的图案显示函数,为后面的各种显示图案奠定基础。
问题四:
在焊接好硬件和写好基础的程序之后,可以实验一下效果显示效果。但是会发现显示效果不理想(可能LED灯很暗或者直接没反应),在检查一切之后发现硬件没问题,此时是单片机的问题,普通的单片机IO口的电流不够大,无法正常驱动如此多的LED灯,而且每个画面的执行速度太慢,所以最后改用STC12C5A单片机来作为主控芯片,一是驱动电流大,而是运算速度快。
问题五:
编写好相应的显示图案之后,用KEIL生成HEX文件,运用烧录软件烧录时候,弹出对话框说文件太大无法进行下载,超出了单片机的内存范围。在检查了一遍程序之后发现是储存动画的数组太大,所以只能改数组,但是数组一变,相应的显示函数也就需要改变。最后在权衡之后决定删减作用不大的程序,尽量在规定的大小之内。
问题六:
整个系统运用了两片单片机,其中STC12C5A主要是用来储存显示的帧数和用来显示动画的函数,AT89C51单片机是用来进行超声波的检测距离和处理重量传感器的回传数据。所以只能在两片单片机之间使用UART通信,但是AT89C51处理的数据发送给另外一片单片机上时候会出先乱码,此时应该检查两片单片机的波特率是不是一样的以及串口的协议是不是一样的,在调整好数据之后,可以进行自己的相关功能了。
总结
该套系统虽然不在学习的计划之中,但是也并非为一无用处。在制作东西的时候,对两种所用到的单片机的基础知识进行了一次比较大的复习,特别是串口通信部分,也完成了以前没做过的51单片机的通信部分。在各种问题下,学会了或者更加细心的,耐心的一步一步查找问题,解决问题,重新整理思路,创造必要的条件执行自己的想法。又是一种经验,对后面的学习又是一次有用的积累作用。
最后附上代码:
该部分为图案显示单片机的代码:
void hansao(unsigned char hs)
{
if(hs<8) //判断hs的值是否在行扫描的值范围内,不在范围内不执行任何操作
{
if(M_EN == 0)
{
if(hs==7) //选通第1行
{
P2=0Xfe;
delayus(liangdu);
P2 = 0xff; //消影
return;
}
if(hs==6) //选通第2行
{
P2=0Xfd;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==5) //选通第3行
{
P2=0Xfb;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==4) //选通第4行
{
P2=0Xf7;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==3) //选通第5行
{
P2=0Xef;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==2) //选通第6行
{
P2=0Xdf;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==1) //选通第7行
{
P2=0Xbf;
delayus(liangdu);
P2 = 0xff;
return;
}
if(hs==0) //选通第8行
{
P2=0X7f;
delayus(liangdu);
P2 = 0xff;
return;
}
}
}
}
//************************************595芯片驱动程序1 频谱模式和后面动画模式************************************
void sendbyte(unsigned int num1,unsigned int num2,unsigned int num3,unsigned int num4)
{
unsigned char c;
for(c=0;c<8;c++)
{
CLK=0;
M_LR_DS0=num1&0x80;
M_LG_DS0=num2&0x80;
M_HR_DS0=num3&0x80;
M_HG_DS0=num4&0x80;
CLK=1;
num1<<=1;
num2<<=1;
num3<<=1;
num4<<=1;
}
}
//************************************595芯片驱动程序2 //前面动画************************************
void sendbyte1(unsigned int num1,unsigned int num2,unsigned int num3,unsigned int num4)
{
unsigned char c;
for(c=0;c<8;c++)
{
CLK=0; //模拟时钟控制端口
M_LR_DS0=num1&0x01; //模拟数据发送端口
M_LG_DS0=num2&0x01;
M_HR_DS0=num3&0x01;
M_HG_DS0=num4&0x01;
CLK=1;
num1>>=1;
num2>>=1;
num3>>=1;
num4>>=1;
}
}
//************************************595芯片驱动程序3 上位机控制部分***********************************
void sendbyteck(unsigned int num1,unsigned int num2,unsigned int num3,unsigned int num4)
{
unsigned char c;
for(c=0;c<8;c++)
{
CLK=0;
M_LR_DS0=num1&0x01;
M_LG_DS0=num2&0x01;
M_HR_DS0=num3&0x01;
M_HG_DS0=num4&0x01;
CLK=1;
num1>>=1;
num2>>=1;
num3>>=1;
num4>>=1;
}
}
void zhen(uchar *a,uchar v) //前面动画数据处理
{
char layer;
while(v--)
{
for(layer=0;layer<8;layer++)
{
if(xsdsq==1){;}
else
{
key_baihei_scan();
if(msd==2||msd==6)
{
if(key2==1||key2==3||key2==5||key2==4)
{
key3=1;
M_EN=1;
break;
}
}
if(msd==4)
{
if(key2==1||key2==3||key2==5||key2==2)
{
key3=1;
M_EN=1;
break;
}
}
}
sendbyte1(~a[layer*8+7],~a[layer*8+5],~a[layer*8+3],~a[layer*8+2]);
sendbyte1(~a[layer*8+4],~a[layer*8+6],~a[layer*8+1],~a[layer*8+0]);
SUO=0;
_nop_();
SUO=1;
M_EN=0;
hansao(7-layer);
delayus(donghua_shudu);
M_EN=1;
if(dong == 1){ break;}
if(abcd == 1){break;}
if(gai == 1){break;}
}
delays(2);
}
}
void W_side(uchar *tab,char num,char v)
{
uchar a[64]={0};
int i,j;
for(j=0;j
下面这部分是处理重量传感器和超声波的代码:
#include
#include
typedef unsigned int u16;
typedef unsigned char u8;
#define chushizhi 8331 //8327
#define akg 8356 //8349
#define bkg 8368 //8359
#define ckg 8386 //8373
#define abkg akg+bkg-chushizhi
#define ackg akg+ckg-chushizhi
#define bckg bkg+ckg-chushizhi
#define abckg akg+bkg+ckg-2*chushizhi
sbit ADDO = P1^1;
sbit ADSK = P1^0;
sbit Trig = P1^2;
sbit Echo = P1^3;
u8 str[20]={0};
u8 i;
u16 get=0,dec,time;
int flag=0,flag_start=0;
void delay(u16 ms)
{
u16 t;
while(ms--)
for(t=0;t<120;t++);
}
/********************************************************************/
/************* get weight value *****************************/
/********************************************************************/
unsigned long ReadCount(void)
{
unsigned long Count;
unsigned char i;
ADSK=0;
Count=0;
while(ADDO);
for (i=0;i<24;i++)
{
ADSK=1;
Count=Count<<1;
ADSK=0;
if(ADDO) Count++;
}
ADSK=1;
Count=Count^0x800000;
ADSK=0;
return(Count);
}
/**************************************************************/
/*************** TXD and RXD code ***************************/
/**************************************************************/
void USARTinit()
{
SCON=0X50;
TMOD=0X20|TMOD;
PCON=0X80;
TH1=0XF3;
TL1=0XF3;
ES=1;
EA=1;
TR1=1;
}
void send(u8 Data)
{
SBUF=Data;
while(!TI);
TI=0;
}
void uartsend(u16 S1)
{
ES=1;
TI=0;
for(i=1;S1>=1;i++)
{
str[i]=(unsigned char)(S1%10)+'0';
(u16)S1=S1/10;
}
for(i=i-1;i>0;i--)
{
send(str[i]);
}
send('\r');
send('\n');
}
/********************************************************************/
/***************** get distence code *****************************/
/********************************************************************/
void T0inti()
{
TMOD=0x01|TMOD;
TH0=0;
TL0=0;
ET0=1;
EA=1;
}
void start()
{
Trig=1;
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_();_nop_(); _nop_(); _nop_();
Trig=0;
}
void Conut()
{
if(flag==0)
{
time=TH0*256+TL0;
TH0=0;
TL0=0;
(u16)dec=(time*1.7)/100; //算出来是CM
}
if(flag == 1)
{
flag = 0;
}
}
void speed()
{
start();
while(!Echo);
TR0=1;
while(Echo);
TR0=0;
Conut();
}
/******************************************************************/
/**************** choose right weight ***************************/
/*******************************************************************/
void judge()
{
get = ReadCount()/1000;
// uartsend(get);
if((getakg-3))
{
uartsend(1);flag_start=1;
}
if((getbkg-3))
{
uartsend(2);flag_start=1;
}
if((getckg-3))
{
uartsend(3);flag_start=1;
}
if((getabkg-3))
{
uartsend(4);flag_start=1;
}
if((getackg-3))
{
uartsend(5);flag_start=1;
}
if((getbckg-3))
{
uartsend(6);flag_start=1;
}
if((getabckg-3))
{
uartsend(7);flag_start=1;
}
}
/****************************************************************************
main code
******************************************************************************/
void main()
{
USARTinit();
T0inti();
while(1)
{
// judge ();
speed();
if(dec<81)
{
judge ();
if(flag_start ==1)
{
flag_start = 0;
}
else uartsend(8);
}
else uartsend(9);
delay(1000);
}
}
void zd() interrupt 1
{
flag=1;
}