指南针,作为中国古代四大发明之一,在人类历史上起到了不可磨灭的作用。

现在如今手机上也都集成了指南针以供我们随时查看,那么在单片机中如何或者我们的方向呢?
这时候就就可以使用数字磁力计来测量磁场强度了。
HMC5883L是一款常用的数字磁力计(三轴磁力计),能够测量地球磁场或其他磁场的强度。它通常用于电子罗盘、导航系统、方向传感器等应用。

它能够测量三个垂直轴(X、Y、Z)方向上的磁场强度,提供完整的三维磁场数据。通过I2C接口来通讯。
当我们知道XY方向的磁场强度时利用反正切函数即可计算当前角度:arctan(Y/X)

不过在实际测量过程中可能会由于周围的磁场环境收到干扰。
本期我们利用STM32F103C8T6以及HMC5883L实现磁场方向的获取。



除了基础设置之外,我们需要开启STM32的硬件I2C。

PB6和PB7分别是I2C_SCL和I2C_SDA。

具体的寄存器配置大家可以从芯片手册中查看,这里不对每个具体的寄存器做过多赘述。
// 数据结构体
typedef struct
{
int16_t x;
int16_t y;
int16_t z;
} HMC5883L_Data_t;
typedef struct
{
float heading;
// 航向角(0-360度)
float magnitude;
// 磁场强度
float inclination;
// 倾角
struct
{
float x; // X轴磁场分量(单位:高斯)
float y; // Y轴磁场分量
float z; // Z轴磁场分量
} gauss;
} HMC5883L_Measure_t;
// HMC5883L寄存器地址定义
#define HMC5883L_ADDR (0x1E << 1)
// I2C地址 (0x3C)
#define HMC5883L_CONFIG_A 0x00
// 配置寄存器A
#define HMC5883L_CONFIG_B 0x01
// 配置寄存器B
#define HMC5883L_MODE 0x02
// 模式寄存器
#define HMC5883L_DATA_X_MSB 0x03
// X轴数据高字节
#define HMC5883L_DATA_X_LSB 0x04
// X轴数据低字节
#define HMC5883L_DATA_Z_MSB 0x05
// Z轴数据高字节
#define HMC5883L_DATA_Z_LSB 0x06
// Z轴数据低字节
#define HMC5883L_DATA_Y_MSB 0x07
// Y轴数据高字节
#define HMC5883L_DATA_Y_LSB 0x08
// Y轴数据低字节
#define HMC5883L_STATUS 0x09
// 状态寄存器
#define HMC5883L_ID_A 0x0A
// 识别寄存器A#define HMC5883L_ID_B 0x0B
// 识别寄存器B #define HMC5883L_ID_C 0x0C // 识别寄存器C
我们定义数据结构体和HMC5883L的句柄结构体,数据结构体包括了XYZ三个方向的具体值。句柄结构体包含了计算得到的航向角,磁场强度(XYZ向量和的模值)以及倾角(根据Z计算得到)
HAL_StatusTypeDef HMC5883L_Init(void){
uint8_t data;
HAL_StatusTypeDef status;
// 配置寄存器A: 采样平均数=8, 输出速率=15Hz, 正常测量配置
data = 0x70; // 0111 0000
status = HAL_I2C_Mem_Write(&hi2c1, HMC5883L_ADDR, HMC5883L_CONFIG_A, 1, &data, 1, HAL_MAX_DELAY);
if(status != HAL_OK) return status;
// 配置寄存器B: 增益设置为1090 LSB/Gauss data = 0x20;
// 0010 0000
status = HAL_I2C_Mem_Write(&hi2c1, HMC5883L_ADDR, HMC5883L_CONFIG_B, 1, &data, 1, HAL_MAX_DELAY);
if(status != HAL_OK) return status;
// 模式寄存器: 连续测量模式
data = 0x00;
status = HAL_I2C_Mem_Write(&hi2c1, HMC5883L_ADDR, HMC5883L_MODE, 1, &data, 1, HAL_MAX_DELAY);
return status;
}
根据手册配置输出速率、增益大小以及将模式配置为连续输出模式。
HAL_StatusTypeDef HMC5883L_ReadData(HMC5883L_Data_t *magData)
{
uint8_t buf[6];
HAL_StatusTypeDef status;
status = HAL_I2C_Mem_Read(&hi2c1, HMC5883L_ADDR, HMC5883L_DATA_X_MSB, 1, buf, 6, HAL_MAX_DELAY);
if(status != HAL_OK) return status;
//组合高八位和低八位
magData->x = (int16_t)((buf[0] << 8) | buf[1]);
magData->z = (int16_t)((buf[2] << 8) | buf[3]);
magData->y = (int16_t)((buf[4] << 8) | buf[5]);
return HAL_OK;
}

从参考手册中可以看到,总共从0x00~0x05六个八位寄存器中存放着XYZ的各自高低八位的数据,所以我们可以用I2C从0x00~0x05连续读取六个数据并将高八位低八位组合起来获得XYZ数据。
/**
* @brief 计算航向角(单位:度)
* @param magData: 磁力计数据结构体指针
* @retval 航向角(0-360度)
*/
float HMC5883L_GetHeadingDegrees(HMC5883L_Data_t *magData)
{
float heading = atan2f((float)magData->y, (float)magData->x);
// 转换为角度
heading *= 180.0f / 3.14159f;
// 确保角度在0-360范围内
if(heading < 0)
heading += 360.0f;
return heading;
}
/**
* @brief 获取所有测量数据
* @param rawData: 原始磁力计数据结构体指针
* @param measures: 存储测量数据的结构体指针
* @retval HAL状态
*/
HAL_StatusTypeDef HMC5883L_GetAllMeasures(HMC5883L_Data_t *rawData, HMC5883L_Measure_t *measures)
{
const float scale = 0.92;
// mG/LSB for ±1.3Ga量程
// 转换为高斯单位
measures->gauss.x = rawData->x * scale / 1000.0f;
measures->gauss.y = rawData->y * scale / 1000.0f;
measures->gauss.z = rawData->z * scale / 1000.0f;
// 计算航向角
measures->heading = atan2f(measures->gauss.y, measures->gauss.x);
measures->heading *= 180.0f / 3.14159f;
if(measures->heading < 0) {
measures->heading += 360.0f;
}
// 计算磁场强度(模值)
measures->magnitude = sqrtf(
measures->gauss.x * measures->gauss.x +
measures->gauss.y * measures->gauss.y +
measures->gauss.z * measures->gauss.z
);
// 计算倾角(与水平面的夹角)
measures->inclination = atan2f(
measures->gauss.z,
sqrtf(measures->gauss.x * measures->gauss.x + measures->gauss.y * measures->gauss.y)
);
measures->inclination *= 180.0f / 3.14159f;
return HAL_OK;
}
最后利用XYZ计算航向角,倾角以及磁场强度即可。
实测这个航向角测得还挺准


登录 或 注册 后才可以进行评论哦!
还没有评论,抢个沙发!