【STM32技巧】:HX711称重芯片的使用指南

概述

HX711 模块 A 通道带有 128 倍信号增益,可以将 5mV 的电压放大 128 倍,然后采样输
出 24bit AD 转换的值,单片机通过指定时序将 24bit 数据读出

如何计算传感器供电电压

HX711 可以在产生 VAVDD 和 AGND 电压,即 711 模块上的 E+和 E-电压。
该电压通过 :

VAVDD=VBG(R1 +R2 )/R2

例如:

VBG 为模块儿基准电压 1.25V,R1 = 20K,R2 = 8.2K,因此得出 VAVDD = 4.3V

(为了降低功耗,该电压只在采样时刻才有输出,因此用万用表读取的值可能低于 4.3v,因为万用表测量的是有效值。)

测重原理讲解

满量程输出电压 = 激励电压 * 灵敏度 1.0mv/v
例如:

供电电压是 5V 乘以灵敏度 1.0mV/V = 满量程 5mV。
相当于有 5Kg 重力产生时候产生 5mV 的电压。

如何将 AD值反向转换为重力值

假设重力为 A Kg,(x<5Kg),测量出来的 AD 值为 y
传感器输出,发送给 AD 模块儿的电压为 A Kg * 4.3mV / 5Kg = 0.86A mV
经过 128 倍增益后为 128 * 0.86A = 110.08AmV
转换为 24bit 数字信号为 110.08A mV * 224 / 4.3V = 429496.7296A
所以 y = 429496.7296A
因此得出 A = y / 429496.7296
所以得出程序中计算公式

Weight_Shiwu = (unsigned long)((float)Weight_Shiwu/429.5);

特别注意:
因为不同的传感器斜率特性曲线不是完全一样,因此,每一个传感器需要矫正这里的 429.5
这个除数,才能达到精度很高。

压力传感器安装方法

知识点:官方例程为什么要将读出的AD数据 异或 0x800000

count :是读出的AD数据
count :读出的数据是24位有符号,二进制补码是: 0x800000 - 0x7FFFFF (-8388607 ~ 8388607)				
count = count ^ 0x800000;   这里的意思是将二进制补码换算为 0 ~ 16777214,如果采集到的	INP-INN ≈ 0,那么 count = count ^ 0x800000 = 8388607 (16777214 / 2, 误差的大小实际值也会在这数增大减少)		
本次例程中,实际测试没有用到:count = count ^ 0x800000,直接使用AD原始数据 count >> 8 后的数据,做成16位精度


实际测试例程,STM32测试例程

注意:本人使用100kg的拉力传感器测试,灵敏度是2mV/V

#include "includes.h"


/*
*********************************************************************************************************
*                                             寄存器
*********************************************************************************************************
*/
#define  DWT_CYCCNT  *(volatile unsigned int *)0xE0001004
#define  DWT_CR      *(volatile unsigned int *)0xE0001000
#define  DEM_CR      *(volatile unsigned int *)0xE000EDFC
#define  DBGMCU_CR   *(volatile unsigned int *)0xE0042004

#define  DEM_CR_TRCENA               (1 << 24)
#define  DWT_CR_CYCCNTENA            (1 <<  0)

#define		WEIGHT_CAP_NUM							1		//重量采集数量 滤波使用

uint8_t		ucWeightCapCompleteFlag= 0;     //重量采集完成标志
uint8_t		ucWeightCapCount 			 = 0;			//重量采集计数
int32_t   ilWeightRawDataAddToal = 0;			//重量AD数据累计
int16_t		iWeightRawData = 0;							//重量AD数据


/*
*********************************************************************************************************
*	函 数 名: bsp_InitDWT
*	功能说明: 初始化DWT. 
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitDWT(void)
{
	DEM_CR         |= (unsigned int)DEM_CR_TRCENA;   /* Enable Cortex-M4's DWT CYCCNT reg.  */
	DWT_CYCCNT      = (unsigned int)0u;
	DWT_CR         |= (unsigned int)DWT_CR_CYCCNTENA;
}

/*
*********************************************************************************************************
*	函 数 名: DWT_DelayUS
*	功能说明: 这里的延时采用CPU的内部计数实现,32位计数器
*             	OSSchedLock(&err);
*				bsp_DelayUS(5);
*				OSSchedUnlock(&err); 根据实际情况看看是否需要加调度锁或选择关中断
*	形    参: _ulDelayTime  延迟长度,单位1 us
*	返 回 值: 无
*   说    明: 1. 主频168MHz的情况下,32位计数器计满是2^32/168000000 = 25.565秒
*                建议使用本函数做延迟的话,延迟在1秒以下。  
*             2. 实际通过示波器测试,微妙延迟函数比实际设置实际多运行0.25us左右的时间。
*             下面数据测试条件:
*             (1). MDK5.15,优化等级0, 不同的MDK优化等级对其没有影响。
*             (2). STM32F407IGT6
*             (3). 测试方法:
*				 GPIOI->BSRRL = GPIO_Pin_8;
*				 bsp_DelayUS(10);
*				 GPIOI->BSRRH = GPIO_Pin_8;
*             -------------------------------------------
*                测试                 实际执行
*             bsp_DelayUS(1)          1.2360us
*             bsp_DelayUS(2)          2.256us
*             bsp_DelayUS(3)          3.256us
*             bsp_DelayUS(4)          4.256us
*             bsp_DelayUS(5)          5.276us
*             bsp_DelayUS(6)          6.276us
*             bsp_DelayUS(7)          7.276us
*             bsp_DelayUS(8)          8.276us
*             bsp_DelayUS(9)          9.276us
*             bsp_DelayUS(10)         10.28us
*            3. 两个32位无符号数相减,获取的结果再赋值给32位无符号数依然可以正确的获取差值。
*              假如A,B,C都是32位无符号数。
*              如果A > B  那么A - B = C,这个很好理解,完全没有问题
*              如果A < B  那么A - B = C, C的数值就是0xFFFFFFFF - B + A + 1。这一点要特别注意,正好用于本函数。
*********************************************************************************************************
*/
void DWT_DelayUS(uint32_t _ulDelayTime)
{
  uint32_t tCnt, tDelayCnt;
	uint32_t tStart;
		
	tStart = DWT_CYCCNT;                                     /* 刚进入时的计数器值 */
	tCnt = 0;
	tDelayCnt = _ulDelayTime * (SystemCoreClock / 1000000);	 /* 需要的节拍数 */ 		      

	while(tCnt < tDelayCnt)
	{
		tCnt = DWT_CYCCNT - tStart; /* 求减过程中,如果发生第一次32位计数器重新计数,依然可以正确计算 */	
	}
}


/*
*********************************************************************************************************
*	函 数 名: HX711_GPIOInit
*	功能说明: 重量芯片HX711 GPIO初始化
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void  HX711_GPIOInit(void)
{

	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//使能PORTA

 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;				      //PA7 推挽输出 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  //推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;				      //PA6 推挽输出 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 		  	//上拉输入
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
}


/*
*********************************************************************************************************
*	函 数 名: HX711_Init
*	功能说明: 重量芯片HX711初始化
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/

void HX711_Init(void)
{
	CH376_SPI_SCS = 1;					//CH376 片选失能	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE);	
	SPI_Cmd(SPI1, DISABLE);			//禁止SPI
	HX711_GPIOInit();						//初始化IO
}


/*
*********************************************************************************************************
*	函 数 名: Task_WeightCap
*	功能说明: 重量采集任务
*	形    参: 无
*	返 回 值: 无
*********************************************************************************************************
*/
void Task_WeightCap(void)
{
	uint32_t	count = 0;
	int16_t		raw_data = 0;
	uint8_t		i;	
	HX711_Init();						//HX711重新初始化
	
	
	ADSK = 0;
	count= 0;
	while(ADDO);
	
	for(i=0; i<24; i++)
	{	
		ADSK = 1;
		DWT_DelayUS(1);
		count = count << 1;
		ADSK = 0;
		DWT_DelayUS(1);

		if(ADDO) 
		{
			count++;		
		}
	}
	
	//第25个时钟信号
	ADSK=1;
	DWT_DelayUS(1);
	ADSK=0;
	DWT_DelayUS(1);
	
	
	/*
		count :读出的数据是24位有符号,二进制补码是: 0x800000 - 0x7FFFFF (-8388607 ~ 8388607)				
		count = count ^ 0x800000;   这里的意思是将二进制补码换算为 0 ~ 16777214,如果采集到的	INP-INN ≈ 0,那么 count = count ^ 0x800000 = 8388607 (16777214 / 2, 误差的大小实际值也会在这数增大减少)		
		这里不用 count = count ^ 0x800000; 这么换算,直接使用 count >> 8 后的数据,做成16位精度
		
		1. 拉力传感器灵敏度S 2mV/V,  实际供电电源Uin 5.08V,满负载输出电压:Uout = S * Uin ≈ 10.16mV
		2. 这里是128倍放大 10.16mV * 128 = 1300.48mV
		3. 16位精度最小分辨率 Uin / 2^16 ≈ 0.00007751V,满量程输出AD值:1300.48	mV / 0.00007751V ≈ 16778
		4. 1g 对应的电压值是:10.16mV / 100kg = 0.0000001016V  换算实际的AD值:0.0000001016V * 128(放大倍数)/ 0.00007752V ≈ 0.16778
		5. 满量程AD值 16778 / 0.16778(1gAD值) = 实际的重量
		6. 注意:使用前要去皮
	*/
	
	
	//count = count ^ 0x800000;
	
	raw_data = count >> 8;
	
	ilWeightRawDataAddToal = raw_data + ilWeightRawDataAddToal;
	ucWeightCapCount++;
	
	if(ucWeightCapCount == WEIGHT_CAP_NUM)
	{
		ucWeightCapCount 				= 0;
		iWeightRawData = ilWeightRawDataAddToal / WEIGHT_CAP_NUM;
		ilWeightRawDataAddToal  = 0;
		ucWeightCapCompleteFlag = 1;
	}
	//raw_data = raw_data ^ 0x8000;
//	if(raw_data < 0)
//	{
//		raw_data = -raw_data;
//	}
	
//  printf("count %d raw_data %d  %f  %f kg\r\n", count, raw_data, (((double)raw_data) * 0.00007751) / 128, (float)(raw_data / 0.16778) / 1000);													//数据打印出来	
}

/*
*********************************************************************************************************
*	函 数 名: GetWeightRawData
*	功能说明: 得到重量的原始值 
*	形    参: iRawData 原始值
*	返 回 值: 1 已经得到 0 没有得到
*********************************************************************************************************
*/
uint8_t  GetWeightRawData(int16_t *iRawData)
{
		if(ucWeightCapCompleteFlag == 1)
		{
			*iRawData = iWeightRawData;
			ucWeightCapCompleteFlag = 0;
			return 1;
		}
		
		return 0;
}




两点直线方程校准算法,用于校准实际数据

	注意: 使用直线方程的首要条件是 重量传感器的线性度要好,可以使用标准砝码测试

/*
*********************************************************************************************************
*	函 数 名: CaculTwoPoint
*	功能说明: 根据2点直线方程,计算Y值
*	形    参: 2个点的坐标和x输入量,x1: 最小千克的AD值,y1:最小千克值,x2: 最大千克的AD值, 
*	y2: 最大千克值, x:输入的当前AD值
*	返 回 值: x对应的y值  返回实际的重量
*********************************************************************************************************
*/
static float  CaculTwoPoint(int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x)
{
  float value;
  value = y1 + (float)((int64_t)(y2 - y1) * (x - x1)) / (x2 - x1);
	return value;
}
物联沃分享整理
物联沃-IOTWORD物联网 » 【STM32技巧】:HX711称重芯片的使用指南

发表评论