概述
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测试例程
注:我用1000kg拉力传感器测试,灵敏度为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;
}