【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;
}