** 1 **
** 前言 **
指南针,作为中国古代四大发明之一,在人类历史上起到了不可磨灭的作用。
现在如今手机上也都集成了指南针以供我们随时查看,那么在单片机中如何或者我们的方向呢?
这时候就就可以使用 ** 数字磁力计 ** 来测量磁场强度了。
HMC5883L是一款常用的数字磁力计(三轴磁力计),能够测量地球磁场或其他磁场的强度。它通常用于电子罗盘、导航系统、方向传感器等应用。
它能够测量三个垂直轴(X、Y、Z)方向上的磁场强度,提供完整的三维磁场数据。通过I2C接口来通讯。
当我们知道XY方向的磁场强度时利用反正切函数即可计算当前角度:arctan(Y/X)
不过在实际测量过程中可能会由于周围的磁场环境收到干扰。
本期我们利用 ** STM32F103C8T6以及HMC5883L ** 实现磁场方向的获取。
** 2 **
** CubeMX设置 **
除了基础设置之外,我们需要开启STM32的硬件I2C。
PB6和PB7分别是I2C_SCL和I2C_SDA。
** 3 **
** 代码实现 **
具体的寄存器配置大家可以从芯片手册中查看,这里不对每个具体的寄存器做过多赘述。
// 数据结构体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计算航向角,倾角以及磁场强度即可。
实测这个航向角测得还挺准
所有代码均上传到交流群的群文件中,可以在主页加群获取哦~