1.硬件介绍
硬件实物图:
直接用51开发板。
外设电路设备包括称重传感器和 HX711 模块以及杜邦线。具体图片在文件夹里。
2.软件运行逻辑介绍
-
模块化部分的介绍
因为我把所有基础功能和拓展功能写在同一个文件里所以引入的模块会有点多,包含了HX711,Timer0,Delay,LCD1602和矩阵键盘等模块。然后我又写了一些函数在main中,并且建立main.h文件将这些函数进行声明。我会主要介绍HX711模块。
HX711模块的原理有一些复杂,简单来说就是通过配合称重传感器,通过一个简单函数读取这时的AD值并通过一个线性方程转换之后获取此时物体的真实重量。它除了电源接口还有数据引脚DOUT和时钟引脚SCK,引脚DOUT用来输出数据,引脚SCK用来输入脉冲。这两个引脚配合完成数据的处理。具体怎么操作我已经注释在HX711.c里了。
2. 主函数的大概介绍
在引入头文件和定义完变量和常量后,我设置了警报标志。我用两个选择分支判断:如果超重,警报标志为1,LCD显示屏会显示ERROR蜂鸣器会响,反之为0。然后就是在警报标志为0的情况下进行按键的选择分支:
其中1~8的显示只有在按键按下后才会更新,9和11是会实时更新的。我还在警报标志为0的情况下加入了LED灯亮和蜂鸣器响,并且只在放下物体之后响一下就停。然后测速的代码我是通过牛顿第二定律,通过分别测量物体静止时的重量和运动时的重量进行公式转换(测量之前要去皮),公式转换成代码如下:
if(Weight_Shiwu>temp)//向上的加速度
{a=(unsigned int)( ( (float)Weight_Shiwu/temp-1)*1000*g);}
if(Weight_Shiwu<temp)//向下的加速度
{a=(unsigned int)((1-(float)Weight_Shiwu/temp)*1000*g);}
//Weight_Shiwu是物体运动时的重量 temp是物体静止时的重量
然后再通过定时器以0.1秒为单位进行积分获得速度。我用占用主循环的方式完成PWM(但是我设置开关可以关闭它对主循环的占用)。
v=\int_0^t a(t) dt
加速度a关于t的函数为a(t)
代码如下:
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //一毫秒
if(T0Count>=100)//以0.1秒为单位积分
{T0Count=0;
dv=0.1*a;
if(Weight_Shiwu>temp)
{v=v+dv;}
if(Weight_Shiwu<temp)
{v=v-dv;}
}
}
-
重量(串口通信我设置在这个按键)
-
单位
-
记录数据
-
显示数据
-
总价
-
去皮
-
定义单价+
-
定义单价-
-
测速
-
关闭测速
-
PWM
-
关闭PWM
3.功能点的介绍
-
当检测到有重物时,亮起led灯并响起蜂鸣器。
这个我是直接用称重传感器获得的数据进行操作。增加判断条件weight>0,用for循环调节灯亮和蜂鸣器响的时间,
Delay1ms将蜂鸣器的频率调到500HZ。
-
利用称重器配合HX711模块得到ad值,通过pwm使得led灯
随着ad值的变化而产生变化,粗略外显出物体重量。
利用空载和放上重物时返回的AD值之差再除于校准参数GapValue
得到物体的真实重量,再将其作为参数条件LED的占空比,使LED的亮度随实物重量变化。
-
通过运算将ad值转化为重物的真实重量,并利用串口将重物真实重量发送到上位机,并尽可能的提高精度。
利用串口UARC模块将得到的实物重量发送到上位机,波特率9600,
文本模式。
-
实现不同单位的转换,公斤,克,磅等,同时能根据不同的单价(可通过按键控制)和重量,计算出总价,并显示。
运用不同的计算方式,将克单位转换成其他单位,并显示在LCD显示屏上。我设置了克,千克,斤,两这四个单位。
-
实现去皮功能,即将某容器置于电子秤上后,可显示记录该容器重量,后同时称量容器和重物时,能够控制减去容器重量。
按下去皮按钮后获得毛皮重量,与一开始获得的毛皮重量做减法获得容器重量,显示出来。重新计算实物重量,再次按下测重按钮会显示去完皮的重量。
-
重物的重量和其他信息能够通过oled(数码管,lcd)显示。
利用LCD1602显示。
-
实现数据存储功能,能够查看近几次的重量称量结果,并显示。
通过给数组数据赋值的方法,可调节数组大小改变可记录数据的多少。
然后用类似的方法将数组的数据显示出来。
-
实现速度测量功能,即将重物置于托盘,向上(下)移动托盘,据重物的视重(在运动时电子秤测量的重量)和真实重量(静止时电子秤测量的重量),得到加速度,利用定时器进行积分,获取实时速度并显示。
-
能够用oled/lcd设计菜单界面,能通过各种方式(如矩阵键盘等)切换功能模式。
主函数源码
#include "MatrixKey.h"
#include "main.h"
#include "HX711.h"
#include "LCD1602.h"
#include "Delay.h"
#include "uart.h"
#include "Timer0.h"
bit Flag_ERROR = 0;//警报标志
sbit speak= P2^5;//蜂鸣器
sbit LED=P2^3; //指示灯
unsigned long HX711_Buffer = 0;
unsigned long Weight_Maopi,Weight_Maopi1, Weight_Maopi2 ,Weight_Ronqi= 0;
long Weight_Shiwu = 0;//实物重量
unsigned int say=1;
unsigned int keynumber,sum,temp,s,i,j,k,n,x,y=0;
unsigned int z=1;
unsigned long v=0;
unsigned long a,dv;
unsigned int price;//设置总价,按元/斤算
unsigned int arr[4];//可以设置储存数据个数
#define g 9.8 //重力加速度
#define GapValue 1095//校准参数
//重量偏大时,增加该数值
//重量偏小时,减小改数值。
void adelay(unsigned int t)
{
while(t--);
}
//****************************************************
//主函数
//****************************************************
void main()
{ Uart_Init();
LCD_Init();
LCD_ShowString(1,1,"Menu");
Delay(500); //延时,等待传感器稳定
Get_Maopi(); //称毛皮重量
while(1)
{
Get_Weight(); //称重
get_a();
Scan_Key();
if(Flag_ERROR==0)
{ if(Weight_Shiwu > 0&&say==1)//每次放重物时蜂鸣器只响一次
{LED=0;
for(i=0;i<50;i++)//频率:500HZ
{speak=!speak;
Delay(1);
}LED=1;
say=0;
}
if(keynumber==9)//测速度
{
x=1;Timer0Init();LCD_Init();
Weight_Maopi2= (float)HX711_Read();
Weight_Ronqi=Weight_Maopi2-Weight_Maopi1; //已经自动去皮,获得物体的重量
temp=(unsigned int)((float)Weight_Ronqi/GapValue); //temp就是物体的重量
}
if(keynumber==10)//关闭测速
{ x=0;ET0=0;v=0;}//若再次进入时速度为0
if(keynumber==11)//打开PWM
{ y=1;
}
if(keynumber==12)//关闭PWM
{ y=0;
}
if(x==1)
{LCD_ShowChar(1,1,'v');
LCD_ShowNum(2,1,v/10000,1);
LCD_ShowNum(2,2,v%10000/1000,1);
LCD_ShowChar(2,3,'.');
LCD_ShowNum(2,4,v%1000/100,1);
LCD_ShowNum(2,5,v%100,1);
LCD_ShowString(2,6,"m/s");
}
if(y==1&&x==0)//用PWM时最好不要空载否则会出问题
{for(i=0;i<100;i++) //缺点:占用主程序,但是可以通过开关控制
{LED=0;
adelay(Weight_Shiwu+50 );
LED=1;
adelay(1000-Weight_Shiwu );//1000克以内亮度明显变化
}
}
if(keynumber==1)//防止代码太长,我只在这设置了串口通信
{
LCD_Init();
LCD_ShowString(1,1,"Weight");
LCD_ShowNum(2,1,Weight_Shiwu/1000 ,1);
LCD_ShowNum(2,2,Weight_Shiwu%1000/100 ,1);
LCD_ShowNum(2,3,Weight_Shiwu%100/10 ,1);
LCD_ShowNum(2,4,Weight_Shiwu%10 ,1);
LCD_ShowChar(2,6,'g');
Send_ASCII(Weight_Shiwu/1000 + 0X30);//文本模式 波特率 9600
Send_ASCII(Weight_Shiwu%1000/100 + 0X30);
Send_ASCII(Weight_Shiwu%100/10 + 0X30);
Send_ASCII(Weight_Shiwu%10 + 0X30);
Send_ASCII('g');
Send_ASCII(' ');
}
if(keynumber==2)//换单位
{ if( k==0)//单位kg
{ LCD_Init();
LCD_ShowString(1,1,"Weight 1");
LCD_ShowNum(2,1,Weight_Shiwu/1000 ,1);
LCD_ShowChar(2,2,'.');
LCD_ShowNum(2,3,Weight_Shiwu%1000/100 ,1);
LCD_ShowNum(2,4,Weight_Shiwu%100/10 ,1);
LCD_ShowNum(2,5,Weight_Shiwu%10 ,1);
LCD_ShowString(2,7,"kg");
}
if( k==1)//单位斤
{ LCD_Init();
LCD_ShowString(1,1,"Weight 2");
Weight_Shiwu=Weight_Shiwu*2;
LCD_ShowNum(2,1,Weight_Shiwu/1000 ,1);
LCD_ShowChar(2,2,'.');
LCD_ShowNum(2,3,Weight_Shiwu%1000/100 ,1);
LCD_ShowNum(2,4,Weight_Shiwu%100/10 ,1);
LCD_ShowNum(2,5,Weight_Shiwu%10 ,1);
LCD_ShowString(2,7,"jin");
}
if( k==2)//单位两
{ LCD_Init();
LCD_ShowString(1,1,"Weight 3");
Weight_Shiwu=Weight_Shiwu*20;
LCD_ShowNum(2,1,Weight_Shiwu/10000 ,1);
LCD_ShowNum(2,2,Weight_Shiwu%10000/1000 ,1);
LCD_ShowChar(2,3,'.');
LCD_ShowNum(2,4,Weight_Shiwu%1000/100 ,1);
LCD_ShowNum(2,5,Weight_Shiwu%100/10 ,1);
LCD_ShowString(2,7,"liang");
}
k++;
k=k%3;
}
if(keynumber==3)//记录数据:可记录4次数据,可以通过改变数组arr[]的 大小调节容量
{arr[s]=Weight_Shiwu;
s++;
s=s%4;
}
if( keynumber==4)//显示数据
{ LCD_Init();
LCD_ShowString(1,1,"Record");
LCD_ShowNum(1,8,j+1,2);
LCD_ShowNum(2,1,arr[j]/1000 ,1);
LCD_ShowNum(2,2,arr[j]%1000/100 ,1);
LCD_ShowNum(2,3,arr[j]%100/10 ,1);
LCD_ShowNum(2,4,arr[j]%10 ,1);
LCD_ShowChar(2,6,'g');
j++;
j=j%4;
}
if(keynumber==5)
{ LCD_Init();
LCD_ShowString(1,1,"Price");
sum=Weight_Shiwu*price*2;
LCD_ShowNum(2,1,sum/1000 ,1);
LCD_ShowChar(2,2,'.');
LCD_ShowNum(2,3,sum%1000/100 ,1);
LCD_ShowNum(2,4,sum%100/10 ,1);
LCD_ShowNum(2,5,sum%10 ,1);
LCD_ShowString(2,6,"yuan");
}
if( keynumber==6)//去皮并显示
{LCD_Init();
Get_Maopi();
Weight_Maopi2= (float)HX711_Read();
LCD_ShowString(1,1,"Ronqi");
Weight_Ronqi=Weight_Maopi2-Weight_Maopi1;
temp=(unsigned int)((float)Weight_Ronqi/GapValue);
LCD_ShowNum(2,1,temp/1000 ,1);
LCD_ShowNum(2,2,temp%1000/100 ,1);
LCD_ShowNum(2,3,temp%100/10 ,1);
LCD_ShowNum(2,4,temp%10 ,1);
LCD_ShowChar(2,5,'g');
}
if( keynumber==7)//自定义价格+
{ LCD_Init();
n++;
LCD_ShowString(1,1,"Define");
LCD_ShowString(2,5,"yuan/jin");
LCD_ShowNum(2,1,n/100 ,1);
LCD_ShowNum(2,2,n%100/10,1);
LCD_ShowNum(2,3,n%10 ,1);
price=n;
}
if( keynumber==8)//自定义价格-
{ LCD_Init();
if(n==0){n++;}//防止数值小于0
n--;
LCD_ShowString(1,1,"Define");
LCD_ShowString(2,5,"yuan/jin");
LCD_ShowNum(2,1,n/100 ,1);
LCD_ShowNum(2,2,n%100/10,1);
LCD_ShowNum(2,3,n%10 ,1);
price=n;
}
}
if(Flag_ERROR == 1)//超重报警
{ LCD_Init();
LCD_ShowString(2,1,"ERROR");
speak=1;
Send_Word("ERROR\n");
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //一毫秒
if(T0Count>=100)//以0.1秒为单位积分
{T0Count=0;
dv=0.1*a;
if(Weight_Shiwu>temp)
{v=v+dv;}
if(Weight_Shiwu<temp)
{v=v-dv;}
}
}
//****************************************************
//称重
//****************************************************
void Get_Weight()
{
Weight_Shiwu = (float)HX711_Read();
Weight_Shiwu = Weight_Shiwu-Weight_Maopi; //获取净重
if(Weight_Shiwu >=0)
{
Weight_Shiwu = (unsigned int)((float)Weight_Shiwu/GapValue); //计算实物的实际重量
if(Weight_Shiwu > 1000) //超重报警
{
Flag_ERROR = 1;
}
else
{
Flag_ERROR = 0;
}
}
else
{
say=1;
}
}
void Scan_Key()
{ keynumber=MatrixKey();}
//****************************************************
//获取毛皮重量
//****************************************************
void Get_Maopi()
{
Weight_Maopi= (float)HX711_Read();
if(z>0)//秤初始化时需要空载,Weight_Maopi1为初始AD值,只记录一次
{Weight_Maopi1=Weight_Maopi;
z--;
}
}
//****************************************************
//测量加速度
//****************************************************
void Get_a()
{
if(Weight_Shiwu>temp)//向上的加速度
{a=(unsigned int)(((float)Weight_Shiwu/temp-1)*1000*g);}//temp是物体静止时的重量
if(Weight_Shiwu<temp)//向下的加速度 //Weight_Shiwu是物体运动时的重量
{a=(unsigned int)((1-(float)Weight_Shiwu/temp)*1000*g);}
}
HX711模块代码
.c文件
#include "HX711.h"
//****************************************************
//延时函数
//****************************************************
void Delay__hx711_us(void)
{
_nop_();
_nop_();
}
//****************************************************
//读取HX711
//****************************************************
unsigned long HX711_Read(void) //增益128
{
unsigned long count;
unsigned char i;
HX711_DOUT=1; //数据端口拉高
Delay__hx711_us();//延时
HX711_SCK=0; //时钟端口拉低,使能AD
count=0;//ad值初始化
EA = 1; //打开总开关
while(HX711_DOUT);//AD转换未结束等待,否则开始读取
EA = 0;//关闭总开关
for(i=0;i<24;i++)//循环24次传数据,按位接收数据
{
HX711_SCK=1; //准备转换数据
count=count<<1; //向左移位,空出最低位进行传值操作
HX711_SCK=0; //时钟脉冲下降延,数据开始转换
if(HX711_DOUT)
count++; //数据端为高则最低位+1,为1,数据端为低则最低位不变
}
HX711_SCK=1; //第25个脉冲
count=count^0x800000;//第25个脉冲下降沿来时,转换数据,异或输出原码
Delay__hx711_us();//延时
HX711_SCK=0; //结束AD转换
return(count);//返回AD值
}
.h文件
#ifndef __HX711_H__j
#define __HX711_H__
#include <reg52.h>
#include <intrins.h>
//IO设置
sbit HX711_DOUT=P2^1;
sbit HX711_SCK=P2^0;
//函数或者变量声明
extern void Delay__hx711_us(void);
extern unsigned long HX711_Read(void);
#endif