基于HX711+压力传感器的密度计

目录

1. 测密度原理

1.1 测质量

1.2 测体积

1.2.1 测体积步骤

1.2.2 测体积示意图 

1.2.3 整体受力分析 

1.2.4 对物块受力分析

1.2.5 联立

2. 硬件部分

2.1 测密度流程 

3.软件实现

3.1 按键检测

3.2 LCD1602显示

3.3 HX711称重

3.4 数据处理呈现

3.4.1 小数呈现

3.4.2 稳定质量 

4.精度提高

记录一下用压力传感器做密度计的过程

单片机:51单片机

模块:HX711

公式: gif.latex?%5Crho%20%3D%5Cfrac%7Bm%7D%7Bv%7D

1. 测密度原理

        话说盘古开天地,我拿HX711做密度计。公式:gif.latex?%5Crho%20%3D%5Cfrac%7Bm%7D%7Bv%7D

1.1 测质量

        质量很简单,直接用HX711和压力传感器称出来就行了,至于怎么提高精度后面会说。

1.2 测体积

1.2.1 测体积步骤

1.把带水的合适大小的烧杯放在称上,去皮;

2.用细线拴住被测物体,使其浸没在水中,注意不要碰到容器壁和容器底;

3.测此时的质量即为物块体积;      

1.2.2 测体积示意图 

看一下大概示意图: 

物块通过细线悬挂在支架(上面那条横线)上,细线对物块的拉力为F1;

1.2.3 整体受力分析 

现在对物块、烧杯和水进行整体受力分析:

设容器和水的质量为M1,物块的质量为M2;秤对这个整体向上的支持力为F,此时可得:

F=(M1+M2)*g-F1=M1*g+(M2*g-F1)

也就是:F=M1*g+(M2*g-F1)

好,记住它,一会用。

1.2.4 对物块受力分析

 对物块受力分析可得:

F浮=M2*g-F1

1.2.5 联立

这两个式子联立:

F=M1*g+M2*g-F1

F浮=M2*g-F1

是不是可以得到:

F=M1*g+F浮

        再回到1.2.1中的第一步,可以看到,容器和水放在秤上之后进行了去皮操作,于是乎M1是不是就等于0了!!!

        既然如此,F=F浮 了吧?

        此时的压力F是在秤上以质量的形式表现出来的,我们不妨设这个“质量”为M0;

        那么上式就可以写作 M0*g = p*g*v;

        水的密度为1,上式简化为M0=v;

        这样,物块的体积在数值上是不是就等于秤称出来的值了??

        此时物块的体积就通过测质量的方式得到了。

        由上述步骤可知被测物体的密度要大于水。

2. 硬件部分

1.51单片机最小系统

2.HX711模块+压力传感器

3.4个按键

4.LCD1602液晶显示屏

4个按键的功能分别是:去皮、称重、记录、计算密度;

2.1 测密度流程 

1.测出被测物体质量M1,记录;

2.将带水的烧杯放在压力传感器上,去皮;

3.用细线拴住物体,使物体浸没在水中,测其质量M2,记录;

4.M1/M2即为物体密度;

3.软件实现

3.1 按键检测

        按照之前所学的按键检测方式,需要等待按键释放,感觉这样有点慢,这里借鉴了正点原子对按键的处理方式。

/********************
    C文件

**********************/
uchar KEY_Read_Nwiat()
{
    static uchar key_flag = 1;
    if(((KEY_COUNT == 0)||(KEY_MARK == 0)||(KEY_MEASURE == 0)||(KEY_PEEL == 0))&&key_flag == 1)
    {
        delay(5);
        key_flag = 0;
        if(KEY_COUNT == 0)        return KEY_COUNT_value;
        else if(KEY_MARK == 0)    return KEY_MARK_value;
        else if(KEY_MEASURE == 0) return KEY_MEASURE_value;
        else if(KEY_PEEL == 0)    return KEY_PEEL_value;
    }       
    else if(KEY_COUNT == 1&&KEY_MARK == 1&&KEY_MEASURE == 1&&KEY_PEEL == 1) key_flag = 1;
    return 0;

void KEY_UserConfig()
{
    KEY_MARK = 1;
    KEY_COUNT = 1;
    KEY_PEEL = 1;
    KEY_MEASURE = 1;
}



/*************************************
    头文件
*************************************/

//-----按键值-----
#define KEY_PEEL_value      1   //去皮返回值
#define KEY_MEASURE_value   2   //称量返回值
#define KEY_MARK_value      3   //记录返回值
#define KEY_COUNT_value     4   //计算返回值

//-----按键定义-----
sbit KEY_PEEL       =   P2^2;//去皮
sbit KEY_MEASURE    =   P2^3;//称量
sbit KEY_MARK       =   P2^4;//记录
sbit KEY_COUNT      =   P2^5;//计算

uchar KEY_Read_Nwiat();//无需等待
void KEY_UserConfig(); //初始化

3.2 LCD1602显示

        LCD1602写法多种多样,此处仅供参考,不完善的地方请大家多多担待。

/*********************************
    C文件
*********************************/
void Write_0com_1dat(bit choose,uchar com_dat)
{
	LCD1602_EN = 0;
	if(choose == 0)//低命令
	{
		LCD1602_RS = 0;
	}
	else if(choose == 1)
	{
		LCD1602_RS = 1;
	}
	P0 = com_dat;
	delay(5);
	LCD1602_EN = 1;
	delay(5);
	LCD1602_EN = 0;
}

//-----
void LCD1602_write_word(uchar location,uchar *s)
{
	Write_0com_1dat(0,location);//显示位置
	while(*s)
	{
		Write_0com_1dat(1,*s);
		s++;
		delay(5);
	}
}
//-----初始化-----
void LCD1602_UserConfig()
{
    LCD1602_EN = 0;
	Write_0com_1dat(0,0x38);
	Write_0com_1dat(0,0x0c);
	Write_0com_1dat(0,0x06);
	Write_0com_1dat(0,0x01);
}


/**********************************
    头文件

***********************************/

#define LCD1602_PORT 	P0
sbit LCD1602_RS = P2^0;
//sbit LCD1602_RW = P2^6;//不进行忙检测,直接接地了
sbit LCD1602_EN = P2^1;


void Write_0com_1dat(bit choose,uchar com_dat);
void LCD1602_write_word(uchar location,uchar *s);
void LCD1602_UserConfig();

3.3 HX711称重

/****************************
    C文件

****************************/
ulong Weight_Maopi = 0;
long Weight_Shiwu = 0;
float Weight_float = 0;
uchar Weight_buf[16] = "               ";

//-----读HX711-----
ulong HX711_Read(void)
{
    ulong count; 
	uchar i; 
  	HX711_DOUT=1; 
	_nop_();_nop_();
  	HX711_SCK=0; 
  	count=0;
	EA = 1; 
  	while(HX711_DOUT); 
	EA = 0;
  	for(i=0;i<24;i++)
	{ 
	  	HX711_SCK=1; 
	  	count=count<<1; 
		HX711_SCK=0; 
	  	if(HX711_DOUT)
			count++; 
	} 
 	HX711_SCK=1; 
    count=count^0x800000;//第25个脉冲下降沿来时,转换数据
	_nop_();_nop_();
	HX711_SCK=0;  
	return count;
}

//-----获取毛皮-----
void HX711_get_Maopi()
{
    Weight_Maopi = HX711_Read();
}

//-----获取质量-----
void HX711_get_Weight()
{
    Weight_Shiwu = HX711_Read();
    Weight_Shiwu -= Weight_Maopi;
    Weight_float = ((float)Weight_Shiwu/GapValue);
}


/******************************************
    头文件

******************************************/
//校准参数
//因为不同的传感器特性曲线不是很一致,
//因此,每一个传感器需要矫正这里这个参数才能使测量值很准确。
//当发现测试出来的重量偏大时,增加该数值。
//如果测试出来的重量偏小时,减小改数值。
//该值可以为小数
#define GapValue 500

sbit HX711_DOUT = P1^1; 
sbit HX711_SCK = P1^0; 

ulong HX711_Read(void);
void HX711_get_Maopi();
void HX711_get_Weight();

3.4 数据处理呈现

3.4.1 小数呈现

        由 3.3 可知,我用的是float类型的变量Weight_float去接收的质量,因此,怎么把这个带小数点的数呈现在LCD1602上呢?当然你可以把数值扩大十倍百倍甚至千倍,再用上四舍五入法,就把xxx.xx的小数转化为一位一位的字符了,但是我懒得这样弄,想到C语言中不是还有sprintf这个函数吗?虽然占用了51单片机很多空间!!!

3.4.2 稳定质量 

        HX711模块卖家提供的代码是一直测量物体质量,这样难免数值会一直跳动,因此,我们可以加一个测量按键,按键不按下就不测量,只有按键按下才进行测量,这样又会有一个问题:我两次测同一个物体的值不一样(有误差嘛),为了减小误差,我们采取按键按下后连续测量十次质量取其平均的方式得到物块较为真实的质量(下方代码42行)。

        废话不多说,上代码

/**************************

    main.c
**************************/


uchar i;
uchar key_val;
uchar mark_flag = 0;
extern uchar Weight_buf[16];
extern float Weight_float;
extern long Weight_Shiwu;
float Weight_average = 0;
float m,v;//质量体积

void mesure_density();//测密度

void main()
{
	LCD1602_UserConfig();
	KEY_UserConfig();
	HX711_get_Maopi();
	while(1)
	{
		mesure_density();
	}
}

void mesure_density()
{
	LCD1602_write_word(0x80,"Welcome to use..");
	key_val = KEY_Read_Nwiat();
	//-----去皮-----
	if(key_val == KEY_PEEL_value)
	{
		HX711_get_Maopi();
		LCD1602_write_word(0xc0,"    PEEL OK!    ");
		delay(500);
		LCD1602_write_word(0xc0,"                ");
	}
	//-----测量-----
	if(key_val == KEY_MEASURE_value)
	{
		LCD1602_write_word(0xc0,"                ");
		for(i=0;i<10;i++)
		{
			HX711_get_Weight();
			Weight_average+=Weight_float;
			LCD1602_write_word(0xc0,"      wait..    ");
		}
		Weight_average/=10;
		LCD1602_write_word(0xc0,"                ");
		sprintf(Weight_buf,"%.2f g",Weight_average);
		LCD1602_write_word(0xc0,Weight_buf);
	}
	//-----记录-----
	if(key_val == KEY_MARK_value)
	{
		mark_flag++;
		if(mark_flag == 1)
		{
			m = Weight_average;
			LCD1602_write_word(0xc0,"                ");
			sprintf(Weight_buf,"%.2f M OK!",m);
			LCD1602_write_word(0xc0,Weight_buf);
		}
		if(mark_flag == 2)
		{
			v = Weight_average;
			LCD1602_write_word(0xc0,"                ");
			sprintf(Weight_buf,"%.2f V OK!",v);
			LCD1602_write_word(0xc0,Weight_buf);
			mark_flag = 0;
			mark_flag|=0x80;
		}
	}
	//-----计算-----
	if(key_val == KEY_COUNT_value&&(mark_flag&0x80))
	{
		mark_flag = 0;
		LCD1602_write_word(0xc0,"                ");
		sprintf(Weight_buf,"%.2f g/cm3",m/v);
		LCD1602_write_word(0xc0,Weight_buf);
	}
}

4.精度提高

        做仪器总要讲究精度的吧。我们用称和砝码称出5g-150g的数据,填入Excel中拟合出一个函数,我们根据这个函数去算出更接近真实值的质量,下面是我测的一些数据。可以看出,测量的质量越大误差也就越大。

砝码测量值误差
54.960.04
109.850.15
1514.840.16
2019.690.31
2524.50.5
3029.430.57
3534.390.61
4039.250.75
4544.20.8
5049.10.9
5554.080.92
6058.921.08
6563.841.16
7068.71.3
7573.631.37
8078.551.45
8583.411.59
9088.361.64
9593.51.5
10098.171.83
105103.111.89
1101082
115112.872.13
120117.842.16
125122.722.28
130127.662.34
135132.552.45
140137.412.59
145142.322.68
150147.272.73

        把测量值和误差拟合出函数

14216e20bf154ef7a781d3c07619f643.png

         x是测量的质量,y是误差,测量值加上误差不久接近真实值了嘛!!!

        下面是我根据上面函数修改程序后的测量数据:

砝码测量值误差
54.560.44
109.470.53
1514.460.54
2019.470.53
2524.510.49
3029.520.48
3534.510.49
4039.510.49
4544.560.44
5049.530.47
5554.560.44
6059.630.37
6564.580.42
7069.590.41
7574.550.45
8079.50.5
8584.490.51
9089.550.45
9594.50.5
10099.610.39
105104.590.41
110109.650.35
115114.650.35
120119.690.31
125124.670.33
130129.710.29
135134.740.26
140139.740.26
145144.80.2
150149.760.24

        相对于上一次测量,这次误差就平稳多了

        再次拟合:

ed1cc0e8df224353a4ce4735bff53599.png

        这个函数是多项式了,加到程序里就差不多可以了,当然为了提高精度你也可以拟合更多次。 

        注意:误差处理这里在上面的程序里没写,因为每个称的误差不一样,加了我的误差分析或许会使你的更不准确!!!

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
基于HX711 51单片机电子秤的仿真制作源码可以如下所示: ```c #include <reg51.h> // 定义HX711的引脚 sbit DT = P1^0; // 数据引脚 sbit SCK = P1^1; // 时钟引脚 // 初始化HX711 void HX711_Init() { DT = 1; // 报告状态 SCK = 1; // 时钟置为高电平 TMOD &= 0xF0; // 配置定时器模式 TMOD |= 0x01; // 定时器0为16位工作模式 TH0 = 0xFC; // 定时器初值 TL0 = 0x18; TR0 = 1; // 启动定时器0 } // 读取HX711的数据 unsigned long HX711_Read() { unsigned long count; unsigned char i; count = 0; while (DT); for (i = 0; i < 24; i++) { SCK = 0; // 时钟置为低电平 count = count << 1; // 左移一位 TH0 = 0xFC; // 定时器复位 TL0 = 0x18; TR0 = 1; // 启动定时器0 while (!DT); // 等待DS变高 TR0 = 0; // 停止定时器0 if (TH0 > 0x80) // 判断定时器是否溢出 count++; SCK = 1; // 时钟置为高电平 } SCK = 0; count = count ^ 0x800000; return count; } void main() { unsigned long weight; // 定义重量变量 HX711_Init(); // 初始化HX711 while (1) { weight = HX711_Read(); // 读取HX711的数据 // 将重量数据进行处理和显示,例如将重量转换为千克或磅,并在数码管或LCD屏幕上显示 // ... } } ``` 以上是基于HX711 51单片机电子秤的仿真制作源码,其中通过初始化HX711模块,并定义对应的引脚,然后通过读取模块数据的函数来获取重量数据,并对重量数据进行处理和显示。其中定时器的使用可以提高读取精度。具体的数据处理和显示步骤需要根据实际需求来实现。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值