1.设备树修改
完整文件
/**********TP**********/
&i2c2{
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&rm_io1_i2c2_scl &rm_io0_i2c2_sda>;
clock-frequency = <400000>;//i2c 400k oled
mmc5633@30 { // 传感器 I2C 地址(通常为 0x30,需核对硬件)
compatible = "memsic,mmc5603"; // 匹配驱动的 of_match_table
reg = <0x30>; // I2C 从地址
status = "okay";
};
// 添加OLED设备节点
oled@3c {
compatible = "ssd1306"; // 0.96寸OLED主控芯片(SSD1306)
reg = <0x3c>; // OLED的I2C地址(0x3c/0x3d二选一)
status = "okay";
width = <128>; // 屏幕宽度
height = <64>; // 屏幕高度
//reset-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>; // 可选:复位引脚(无则注释)
};
其它:
&pinctrl {
rm_io0 {
rm_io0_i2c2_sda: rm-io0-i2c2-sda {
rockchip,pins =
<0 RK_PA0 35 &pcfg_pull_none>;
};
};
rm_io1 {
rm_io1_i2c2_scl: rm-io1-i2c2-scl {
rockchip,pins =
<0 RK_PA1 34 &pcfg_pull_none>;
};
};
};
2.Kconfig
config MMC5633
tristate "MEMSIC MMC5633 3-axis magnetic sensor"
select REGMAP_I2C
select REGMAP_I3C if I3C
depends on I2C
depends on I3C || !I3C
help
Say yes here to build support for the MEMSIC MMC5633 3-axis
magnetic sensor.
To compile this driver as a module, choose M here: the module
will be called mmc5633
3.Makefile

4.驱动
// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC5633 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
* Copyright (c) 2025, NXP
*
* IIO driver for MMC5633, base on mmc35240.c
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i3c/device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/types.h>
#define MMC5633_REG_XOUT0 0x00
#define MMC5633_REG_XOUT1 0x01
#define MMC5633_REG_YOUT0 0x02
#define MMC5633_REG_YOUT1 0x03
#define MMC5633_REG_ZOUT0 0x04
#define MMC5633_REG_ZOUT1 0x05
#define MMC5633_REG_XOUT2 0x06
#define MMC5633_REG_YOUT2 0x07
#define MMC5633_REG_ZOUT2 0x08
#define MMC5633_REG_TOUT 0x09
#define MMC5633_REG_STATUS1 0x18
#define MMC5633_REG_STATUS0 0x19
#define MMC5633_REG_CTRL0 0x1b
#define MMC5633_REG_CTRL1 0x1c
#define MMC5633_REG_CTRL2 0x1d
#define MMC5633_REG_ID 0x39
#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
#define MMC5633_CTRL0_RESET BIT(4)
#define MMC5633_CTRL0_SET BIT(3)
#define MMC5633_CTRL0_MEAS_T BIT(1)
#define MMC5633_CTRL0_MEAS_M BIT(0)
#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
#define MMC5633_HDR_CTRL0_MEAS_M 0x01
#define MMC5633_HDR_CTRL0_MEAS_T 0x03
#define MMC5633_HDR_CTRL0_SET 0x05
#define MMC5633_HDR_CTRL0_RESET 0x07
enum mmc5633_axis {
MMC5633_AXIS_X,
MMC5633_AXIS_Y,
MMC5633_AXIS_Z,
MMC5633_TEMPERATURE,
};
struct mmc5633_data {
struct regmap *regmap;
struct i3c_device *i3cdev;
struct mutex mutex; /* protect to finish one whole measurement */
struct iio_trigger *trig; /* 触发源 */
struct timer_list timer; /* 软件定时器,自动触发采集 */
unsigned int samp_freq_hz; /* 当前采样频率(Hz) */
};
// 采样频率映射:[Hz, 微秒补偿]
static int mmc5633_samp_freq[][2] = {
{ 1, 200000 },
{ 2, 0 },
{ 3, 500000 },
{ 6, 600000 },
};
// 定义扫描通道(带Buffer配置)
#define MMC5633_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = MMC5633_AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = MMC5633_AXIS_ ## _axis, \
.scan_type = { \
.sign = 's', \
.realbits = 20, /* MMC5633磁强计20位有效数据 */ \
.storagebits = 32, /* 存储为32位整型 */ \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec mmc5633_channels[] = {
MMC5633_CHANNEL(X),
MMC5633_CHANNEL(Y),
MMC5633_CHANNEL(Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = MMC5633_TEMPERATURE,
.scan_index = MMC5633_TEMPERATURE,
.scan_type = {
.sign = 's',
.realbits = 8, /* 温度8位有效数据 */
.storagebits = 32,
.endianness = IIO_CPU,
},
},
// 时间戳通道(Buffer必须)
IIO_CHAN_SOFT_TIMESTAMP(4),
};
// 获取采样频率索引
static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
int val, int val2)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
if (mmc5633_samp_freq[i][0] == val &&
mmc5633_samp_freq[i][1] == val2)
return i;
return -EINVAL;
}
// 传感器初始化
static int mmc5633_init(struct mmc5633_data *data)
{
unsigned int reg_id;
int ret;
ret = regmap_read(data->regmap, MMC5633_REG_ID, ®_id);
if (ret)
return dev_err_probe(regmap_get_device(data->regmap), ret,
"Error reading product id\n");
dev_err(regmap_get_device(data->regmap), "MMC5633 chip id %x\n", reg_id);
// SET/RESET序列恢复传感器默认配置
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
if (ret)
return ret;
fsleep(MMC5633_WAIT_SET_RESET_US);
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
if (ret)
return ret;
dev_err(regmap_get_device(data->regmap), "MMC5633 fre 0-1hz\n");
// 设置默认采样频率(1Hz)
return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
}
// 触发单次测量
static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
{
unsigned int reg_status, val;
int ret;
val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
if (ret < 0)
return ret;
val = (address == MMC5633_TEMPERATURE) ?
MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
reg_status & val,
10 * USEC_PER_MSEC,
100 * 10 * USEC_PER_MSEC);
if (ret) {
dev_err(regmap_get_device(data->regmap), "data not ready\n");
return ret;
}
return 0;
}
// 读取测量数据
static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
{
int ret;
// 降级到I2C/SDR模式读取
ret = mmc5633_take_measurement(data, address);
if (ret < 0)
return ret;
if (address == MMC5633_TEMPERATURE)
return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT0, buf, sz);
}
#define MMC5633_ALL_SIZE (3 * 3 + 1) // X/Y/Z各3字节 + Temp1字节
// 解析原始数据
static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
{
if (index == MMC5633_TEMPERATURE) {
*val = buf[MMC5633_ALL_SIZE - 1];
return 0;
}
// 解析20位磁强计数据:X[19:12] X[11:4] Y[19:12] Y[11:4] Z[19:12] Z[11:4] X[3:0] Y[3:0] Z[3:0]
*val = get_unaligned_be16(buf + 2 * index) << 4;
*val |= buf[index + 6] >> 4;
return 0;
}
// 读取原始数据/scale/采样频率
static int mmc5633_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
char buf[MMC5633_ALL_SIZE];
unsigned int reg, i;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->mutex);
ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
ret = mmc5633_get_raw(data, chan->address, buf, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_MAGN) {
*val = 0;
*val2 = 62500; // 0.0000625 G/LSB = 62500 nT/LSB
} else {
*val = 0;
*val2 = 800000000; // 0.8°C/LSB
}
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
*val = -75; // 温度偏移:-75°C
return IIO_VAL_INT;
}
return -EINVAL;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, ®);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
if (i >= ARRAY_SIZE(mmc5633_samp_freq))
return -EINVAL;
*val = mmc5633_samp_freq[i][0];
*val2 = mmc5633_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
// 写入采样频率
static int mmc5633_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {
ret = mmc5633_get_samp_freq_index(data, val, val2);
if (ret < 0)
return ret;
mutex_lock(&data->mutex);
// 更新硬件采样频率
ret = regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
if(val==1)
{
val=20;
}
dev_err(regmap_get_device(data->regmap), "MMC5633 fre %d-%dhz\n",ret,val);
// 更新定时器采样频率
data->samp_freq_hz = val;
mod_timer(&data->timer, jiffies + msecs_to_jiffies(1000 / data->samp_freq_hz));
mutex_unlock(&data->mutex);
return ret;
}
default:
return -EINVAL;
}
}
// 读取可用采样频率
static int mmc5633_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (const int *)mmc5633_samp_freq;
*length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
// Buffer触发回调函数:采集数据并写入Buffer
static irqreturn_t mmc5633_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct mmc5633_data *data = iio_priv(indio_dev);
char buf[MMC5633_ALL_SIZE] = {0};
int32_t buffer_data[4] = {0}; // X/Y/Z/Temp
int ret;
mutex_lock(&data->mutex);
// 读取所有通道数据
ret = mmc5633_read_measurement(data, MMC5633_AXIS_X, buf, MMC5633_ALL_SIZE);
if (ret < 0) {
mutex_unlock(&data->mutex);
goto done;
}
// 解析数据
mmc5633_get_raw(data, MMC5633_AXIS_X, buf, (int *)&buffer_data[0]);
mmc5633_get_raw(data, MMC5633_AXIS_Y, buf, (int *)&buffer_data[1]);
mmc5633_get_raw(data, MMC5633_AXIS_Z, buf, (int *)&buffer_data[2]);
mmc5633_get_raw(data, MMC5633_TEMPERATURE, buf, (int *)&buffer_data[3]);
// 写入Buffer(带时间戳)
iio_push_to_buffers_with_timestamp(indio_dev, buffer_data,
iio_get_time_ns(indio_dev));
mutex_unlock(&data->mutex);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
// 软件定时器回调:自动触发数据采集
static void mmc5633_timer_trigger(struct timer_list *t)
{
struct mmc5633_data *data = from_timer(data, t, timer);
iio_trigger_get_drvdata(data->trig);
// 触发采集
iio_trigger_poll(data->trig);
// 重启定时器
mod_timer(&data->timer, jiffies + msecs_to_jiffies(1000 / data->samp_freq_hz));
}
// 创建软件触发源
static int mmc5633_setup_trigger(struct iio_dev *indio_dev)
{
struct mmc5633_data *data = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
int ret;
// 分配触发源
data->trig = devm_iio_trigger_alloc(dev, "%s-trigger", indio_dev->name);
if (!data->trig)
return -ENOMEM;
// 设置触发源私有数据
iio_trigger_set_drvdata(data->trig, indio_dev);
// 注册触发源
ret = devm_iio_trigger_register(dev, data->trig);
if (ret)
return ret;
// 初始化定时器(默认1Hz采样)
timer_setup(&data->timer, mmc5633_timer_trigger, 0);
data->samp_freq_hz = 20;
mod_timer(&data->timer, jiffies + msecs_to_jiffies(1000));
// 关联触发源到IIO设备
indio_dev->trig = data->trig;
return 0;
}
// IIO设备信息
static const struct iio_info mmc5633_info = {
.read_raw = mmc5633_read_raw,
.write_raw = mmc5633_write_raw,
.read_avail = mmc5633_read_avail,
};
// Regmap可写寄存器判断
static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return true;
default:
return false;
}
}
// Regmap可读寄存器判断
static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_XOUT0 ... MMC5633_REG_ZOUT2:
case MMC5633_REG_TOUT:
case MMC5633_REG_STATUS1:
case MMC5633_REG_ID:
return true;
default:
return false;
}
}
// Regmap易失性寄存器判断
static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return false;
default:
return true;
}
}
// Regmap默认值
static const struct reg_default mmc5633_reg_defaults[] = {
{ MMC5633_REG_CTRL0, 0x00 },
{ MMC5633_REG_CTRL1, 0x00 },
};
// Regmap配置
static const struct regmap_config mmc5633_regmap_config = {
.name = "mmc5633_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = MMC5633_REG_ID,
.cache_type = REGCACHE_FLAT,
.writeable_reg = mmc5633_is_writeable_reg,
.readable_reg = mmc5633_is_readable_reg,
.volatile_reg = mmc5633_is_volatile_reg,
.reg_defaults = mmc5633_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
};
// 通用probe函数
static int mmc5633_common_probe(struct regmap *regmap, char *name,
struct i3c_device *i3cdev)
{
struct device *dev = regmap_get_device(regmap);
struct mmc5633_data *data;
struct iio_dev *indio_dev;
int ret;
// 分配IIO设备
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
// 初始化私有数据
data = iio_priv(indio_dev);
data->regmap = regmap;
data->i3cdev = i3cdev;
dev_set_drvdata(dev, indio_dev); // 设置设备私有数据
mutex_init(&data->mutex);
// 配置IIO设备
indio_dev->info = &mmc5633_info;
indio_dev->name = name;
indio_dev->channels = mmc5633_channels;
indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
// 初始化触发源和Buffer
ret = mmc5633_setup_trigger(indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to setup trigger\n");
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
NULL,
mmc5633_trigger_handler,
NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to setup buffer\n");
// 初始化传感器
ret = mmc5633_init(data);
if (ret < 0)
return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
// 注册IIO设备
return devm_iio_device_register(dev, indio_dev);
}
// 休眠回调
static int mmc5633_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mmc5633_data *data = iio_priv(indio_dev);
struct regmap *regmap = data->regmap;
// 停止定时器
del_timer_sync(&data->timer);
// 缓存寄存器
regcache_cache_only(regmap, true);
return 0;
}
// 恢复回调
static int mmc5633_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mmc5633_data *data = iio_priv(indio_dev);
struct regmap *regmap = data->regmap;
int ret;
// 恢复寄存器配置
regcache_mark_dirty(regmap);
ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
if (ret)
dev_err(dev, "Failed to restore control registers\n");
// 重启定时器
mod_timer(&data->timer, jiffies + msecs_to_jiffies(1000 / data->samp_freq_hz));
regcache_cache_only(regmap, false);
return 0;
}
// I2C Probe
static int mmc5633_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct regmap *regmap;
// 初始化Regmap
regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
return mmc5633_common_probe(regmap, client->name, NULL);
}
// PM操作
static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
// 设备树匹配表
static const struct of_device_id mmc5633_of_match[] = {
{ .compatible = "memsic,mmc5603" },
{ .compatible = "memsic,mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(of, mmc5633_of_match);
// I2C设备ID表
static const struct i2c_device_id mmc5633_i2c_id[] = {
{ "mmc5603" },
{ "mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
// I2C驱动结构体
static struct i2c_driver mmc5633_i2c_driver = {
.driver = {
.name = "mmc5633_i2c",
.of_match_table = mmc5633_of_match,
.pm = pm_sleep_ptr(&mmc5633_pm_ops),
},
.probe = mmc5633_i2c_probe,
.id_table = mmc5633_i2c_id,
};
module_i2c_driver(mmc5633_i2c_driver);
MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
MODULE_LICENSE("GPL");
应用程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <dirent.h>
#include <math.h>
#include <getopt.h>
// 传感器 IIO 设备路径
char *IIO_DEV_PATH = NULL;
// 校准文件路径
#define CALIBRATION_FILE "./mmc5603_calibration.txt"
// 错误处理宏
#define CHECK_ERROR(ret, msg) do { if (ret < 0) { printf("Error: %s\n", msg); return ret; } } while (0)
// 读取文件内容(通用函数)
static int read_iio_file(const char *path, char *buf, size_t len) {
FILE *fp = fopen(path, "r");
if (!fp) {
// 不打印错误,避免过多输出
return -1;
}
memset(buf, 0, len);
if (fgets(buf, len - 1, fp) == NULL) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
// 写入文件内容(配置采样频率用)
static int write_iio_file(const char *path, const char *value) {
FILE *fp = fopen(path, "w");
if (!fp) {
// 不打印错误,避免过多输出
return -1;
}
if (fputs(value, fp) < 0) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
// 读取浮点数
static int read_float(const char *path, float *value) {
char buf[64];
if (read_iio_file(path, buf, sizeof(buf)) < 0)
return -1;
*value = strtof(buf, NULL);
return 0;
}
// 读取整数
static int read_int(const char *path, int *value) {
char buf[64];
if (read_iio_file(path, buf, sizeof(buf)) < 0)
return -1;
*value = atoi(buf);
return 0;
}
// 自动检测磁强计设备路径
static char* find_magnetometer_device(void) {
static char path[128];
DIR *dir;
struct dirent *entry;
char dev_path[128];
char name[64];
dir = opendir("/sys/bus/iio/devices/");
if (!dir) {
perror("opendir failed");
return NULL;
}
while ((entry = readdir(dir)) != NULL) {
if (strstr(entry->d_name, "iio:device") != NULL) {
snprintf(dev_path, sizeof(dev_path), "/sys/bus/iio/devices/%s/name", entry->d_name);
FILE *fp = fopen(dev_path, "r");
if (fp) {
if (fgets(name, sizeof(name), fp) != NULL) {
if (strstr(name, "mmc56") != NULL) {
snprintf(path, sizeof(path), "/sys/bus/iio/devices/%s", entry->d_name);
fclose(fp);
closedir(dir);
return path;
}
}
fclose(fp);
}
}
}
closedir(dir);
return NULL;
}
// 计算磁场方向和强度
static void calculate_magnetic_orientation(float x, float y, float z, float *heading, float *intensity) {
// 计算磁场强度
*intensity = sqrt(x*x + y*y + z*z);
// 计算方位角(与北方的夹角)
// 尝试不同的轴组合,找出正确的方向计算方式
// 选项1: 标准方式 (x=东, y=北)
float heading1 = atan2(y, x) * 180.0 / M_PI;
if (heading1 < 0) heading1 += 360.0;
// 选项2: 交换x和y (x=北, y=东)
float heading2 = atan2(x, y) * 180.0 / M_PI;
if (heading2 < 0) heading2 += 360.0;
// 选项3: 可能的传感器安装方向 (x=北, y=西)
float heading3 = atan2(-y, x) * 180.0 / M_PI;
if (heading3 < 0) heading3 += 360.0;
// 选项4: 可能的传感器安装方向 (x=东, y=南)
float heading4 = atan2(-x, y) * 180.0 / M_PI;
if (heading4 < 0) heading4 += 360.0;
// 打印所有选项的结果
printf("Debug: Headings - Option1: %.1f, Option2: %.1f, Option3: %.1f, Option4: %.1f\n",
heading1, heading2, heading3, heading4);
// 选择一个选项进行测试
*heading = heading3;
}
// 触发单次测量
static int trigger_measurement(void) {
char path[128];
// 尝试多种触发方式
// 方式1: 使用oneshot触发
snprintf(path, sizeof(path), "%s/trigger/current_trigger", IIO_DEV_PATH);
if (write_iio_file(path, "oneshot") == 0) {
printf("Trigger: oneshot mode\n");
usleep(5000); // 5ms
return 0;
}
// 方式2: 启用磁场测量
snprintf(path, sizeof(path), "%s/in_magn_en", IIO_DEV_PATH);
if (write_iio_file(path, "1") == 0) {
printf("Trigger: enabled mode\n");
usleep(5000);
return 0;
}
// 方式3: 尝试写入采样频率来触发
snprintf(path, sizeof(path), "%s/in_magn_sampling_frequency", IIO_DEV_PATH);
if (write_iio_file(path, "100") == 0) {
printf("Trigger: sampling frequency set to 100Hz\n");
usleep(5000);
return 0;
}
// 触发失败,继续执行
printf("Warning: all trigger methods failed\n");
usleep(10000); // 10ms
return 0;
}
// 读取传感器数据
static int read_mmc5633_data(float *x, float *y, float *z, float *temp) {
char path[128];
int raw_x, raw_y, raw_z, raw_temp;
float magn_scale, temp_scale;
int temp_offset;
// 触发测量
trigger_measurement();
// 1. 读取磁场缩放系数
snprintf(path, sizeof(path), "%s/in_magn_scale", IIO_DEV_PATH);
if (read_float(path, &magn_scale) < 0) {
printf("Error reading magn scale\n");
return -1;
}
// 2. 读取 X 轴磁场原始值
snprintf(path, sizeof(path), "%s/in_magn_x_raw", IIO_DEV_PATH);
if (read_int(path, &raw_x) < 0) {
printf("Error reading x raw\n");
return -1;
}
// 3. 读取 Y 轴磁场
snprintf(path, sizeof(path), "%s/in_magn_y_raw", IIO_DEV_PATH);
if (read_int(path, &raw_y) < 0) {
printf("Error reading y raw\n");
return -1;
}
// 4. 读取 Z 轴磁场
snprintf(path, sizeof(path), "%s/in_magn_z_raw", IIO_DEV_PATH);
if (read_int(path, &raw_z) < 0) {
printf("Error reading z raw\n");
return -1;
}
// 计算磁场值
*x = raw_x * magn_scale;
*y = raw_y * magn_scale;
*z = raw_z * magn_scale;
// 5. 读取温度
snprintf(path, sizeof(path), "%s/in_temp_raw", IIO_DEV_PATH);
if (read_int(path, &raw_temp) < 0) {
printf("Error reading temp raw\n");
return -1;
}
// 读取温度缩放系数
snprintf(path, sizeof(path), "%s/in_temp_scale", IIO_DEV_PATH);
if (read_float(path, &temp_scale) < 0) {
printf("Error reading temp scale\n");
return -1;
}
// 读取温度偏移
snprintf(path, sizeof(path), "%s/in_temp_offset", IIO_DEV_PATH);
if (read_int(path, &temp_offset) < 0) {
printf("Error reading temp offset\n");
return -1;
}
*temp = raw_temp * temp_scale + temp_offset;
// 打印调试信息
printf("Debug: magn_scale=%.9f\n", magn_scale);
printf("Debug: raw_x=%d, x=%.4f uT\n", raw_x, *x);
printf("Debug: raw_y=%d, y=%.4f uT\n", raw_y, *y);
printf("Debug: raw_z=%d, z=%.4f uT\n", raw_z, *z);
return 0;
}
// 配置采样频率(单位:Hz,需匹配驱动支持的频率)
static int set_sampling_freq(int freq) {
char path[128];
char value[32];
// 拼接采样频率配置文件路径
snprintf(path, sizeof(path), "%s/in_magn_sampling_frequency", IIO_DEV_PATH);
// 标准 IIO 格式:直接写入频率值
snprintf(value, sizeof(value), "%d", freq);
return write_iio_file(path, value);
}
// 检查传感器是否存在
static int check_sensor_exist(void) {
if (!IIO_DEV_PATH) {
IIO_DEV_PATH = find_magnetometer_device();
if (!IIO_DEV_PATH) {
printf("Magnetometer sensor not found\n");
return -1;
}
}
return 0;
}
// 打印传感器信息
static void print_sensor_info(void) {
char buf[64];
char path[128];
// 打印传感器名称
snprintf(path, sizeof(path), "%s/name", IIO_DEV_PATH);
if (read_iio_file(path, buf, sizeof(buf)) == 0) {
printf("Sensor Name: %s", buf);
}
// 打印可用的采样频率
snprintf(path, sizeof(path), "%s/in_magn_sampling_frequency_available", IIO_DEV_PATH);
if (read_iio_file(path, buf, sizeof(buf)) == 0) {
printf("Available Sampling Frequencies: %s", buf);
}
}
// 保存校准值到文件
static int save_calibration(float min_x, float max_x, float min_y, float max_y, float min_z, float max_z) {
FILE *fp = fopen(CALIBRATION_FILE, "w");
if (!fp) {
perror("Failed to open calibration file");
return -1;
}
fprintf(fp, "%.4f %.4f\n", min_x, max_x);
fprintf(fp, "%.4f %.4f\n", min_y, max_y);
fprintf(fp, "%.4f %.4f\n", min_z, max_z);
fclose(fp);
printf("Calibration saved to %s\n", CALIBRATION_FILE);
return 0;
}
// 从文件加载校准值
static int load_calibration(float *min_x, float *max_x, float *min_y, float *max_y, float *min_z, float *max_z) {
FILE *fp = fopen(CALIBRATION_FILE, "r");
if (!fp) {
printf("No calibration file found, using default values\n");
// 使用默认值
*min_x = -100.0;
*max_x = 100.0;
*min_y = -100.0;
*max_y = 100.0;
*min_z = -100.0;
*max_z = 100.0;
return -1;
}
if (fscanf(fp, "%f %f", min_x, max_x) != 2 ||
fscanf(fp, "%f %f", min_y, max_y) != 2 ||
fscanf(fp, "%f %f", min_z, max_z) != 2) {
fclose(fp);
printf("Invalid calibration file, using default values\n");
// 使用默认值
*min_x = -100.0;
*max_x = 100.0;
*min_y = -100.0;
*max_y = 100.0;
*min_z = -100.0;
*max_z = 100.0;
return -1;
}
fclose(fp);
printf("Calibration loaded from %s\n", CALIBRATION_FILE);
printf("X: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_x, *max_x, (*min_x + *max_x) / 2);
printf("Y: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_y, *max_y, (*min_y + *max_y) / 2);
printf("Z: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_z, *max_z, (*min_z + *max_z) / 2);
return 0;
}
// 简单的磁场校准
static void calibrate_magnetometer(float *min_x, float *max_x, float *min_y, float *max_y, float *min_z, float *max_z) {
float x, y, z, temp;
int i;
printf("\n=== Magnetometer Calibration ===\n");
printf("Rotate the board in all directions for 10 seconds...\n");
// 初始化最小值和最大值
*min_x = 999999.0;
*max_x = -999999.0;
*min_y = 999999.0;
*max_y = -999999.0;
*min_z = 999999.0;
*max_z = -999999.0;
// 采集10秒数据
for (i = 0; i < 100; i++) {
if (read_mmc5633_data(&x, &y, &z, &temp) < 0) {
printf("Calibration failed\n");
return;
}
// 更新最小值和最大值
if (x < *min_x) *min_x = x;
if (x > *max_x) *max_x = x;
if (y < *min_y) *min_y = y;
if (y > *max_y) *max_y = y;
if (z < *min_z) *min_z = z;
if (z > *max_z) *max_z = z;
usleep(100000); // 100ms
}
// 打印校准结果
printf("Calibration complete!\n");
printf("X: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_x, *max_x, (*min_x + *max_x) / 2);
printf("Y: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_y, *max_y, (*min_y + *max_y) / 2);
printf("Z: min=%.4f uT, max=%.4f uT, offset=%.4f uT\n", *min_z, *max_z, (*min_z + *max_z) / 2);
printf("=============================\n\n");
// 保存校准值到文件
save_calibration(*min_x, *max_x, *min_y, *max_y, *min_z, *max_z);
}
int main(int argc, char *argv[]) {
float x, y, z, temp;
int i, freq = 1;
struct timespec start, end;
double elapsed;
// 校准参数
float min_x, max_x, min_y, max_y, min_z, max_z;
// 命令行选项
int calibrate = 0;
int opt;
// 解析命令行参数
while ((opt = getopt(argc, argv, "cf:")) != -1) {
switch (opt) {
case 'c':
calibrate = 1;
break;
case 'f':
freq = atoi(optarg);
break;
default:
printf("Usage: %s [-c] [-f freq]\n", argv[0]);
printf(" -c: Calibrate the magnetometer\n");
printf(" -f: Set sampling frequency (Hz)\n");
return 1;
}
}
// 检查传感器是否存在
CHECK_ERROR(check_sensor_exist(), "Sensor not found");
printf("Found magnetometer at: %s\n", IIO_DEV_PATH);
// 打印传感器信息
print_sensor_info();
// 配置采样频率
printf("Setting sampling frequency to %d Hz\n", freq);
if (set_sampling_freq(freq) < 0) {
printf("Warning: set sampling freq failed (may need root权限)\n");
}
// 加载或执行校准
if (calibrate) {
// 执行磁场校准
calibrate_magnetometer(&min_x, &max_x, &min_y, &max_y, &min_z, &max_z);
} else {
// 从文件加载校准值
load_calibration(&min_x, &max_x, &min_y, &max_y, &min_z, &max_z);
}
// 循环读取数据
for (i = 0; i < 10; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
if (read_mmc5633_data(&x, &y, &z, &temp) < 0) {
printf("read data failed\n");
return -1;
}
// 应用校准偏移
float x_cal = x - (min_x + max_x) / 2;
float y_cal = y - (min_y + max_y) / 2;
float z_cal = z - (min_z + max_z) / 2;
clock_gettime(CLOCK_MONOTONIC, &end);
elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
// 计算磁场方向和强度
float heading, intensity;
calculate_magnetic_orientation(x_cal, y_cal, z_cal, &heading, &intensity);
// 打印结果
printf("=== MMC5603 Sensor Data ===\n");
printf("Raw: X=%.4f uT, Y=%.4f uT, Z=%.4f uT\n", x, y, z);
printf("Calibrated: X=%.4f uT, Y=%.4f uT, Z=%.4f uT\n", x_cal, y_cal, z_cal);
printf("Magnetic Intensity: %.4f uT\n", intensity);
printf("Heading: %.1f degrees\n", heading);
printf("Temperature: %.2f °C\n", temp);
printf("Reading Time: %.3f ms\n", elapsed * 1000);
printf("\nInstructions: \n");
printf("1. Rotate the board 360 degrees\n");
printf("2. Observe which heading option changes most consistently\n");
printf("3. Modify calculate_magnetic_orientation to use the best option\n");
printf("---------------------------./mmc5603_test -c -f 10 ./mmc5603_test -f 10 \n");
// 间隔指定时间读取一次
sleep(1);
}
return 0;
}
应用程序makefile
SDK=/home/book/work/rk3506/sdk
KERNELDIR ?= $(SDK)/kernel-6.1
CROSS_COMPILE = $(SDK)/prebuilts/gcc/linux-x86/arm/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-
PWD := $(shell pwd)
app_name=mmc5603_test
all:
$(CROSS_COMPILE)gcc mmc5603app.c -lm -o $(app_name)
chmod +x $(app_name)
clean:
rm -rf $(app_name)
运行
chmod +x mmc5603_test ;./mmc5603_test -c -f 1000
Trigger: sampling frequency set to 100Hz
Debug: magn_scale=0.000062500
Debug: raw_x=528147, x=33.0092 uT
Debug: raw_y=515869, y=32.2418 uT
Debug: raw_z=542233, z=33.8896 uT
Debug: Headings - Option1: 100.1, Option2: 349.9, Option3: 259.9, Option4: 10.1
=== MMC5603 Sensor Data ===
Raw: X=33.0092 uT, Y=32.2418 uT, Z=33.8896 uT
Calibrated: X=-0.0215 uT, Y=0.1205 uT, Z=0.0001 uT
Magnetic Intensity: 0.1224 uT
Heading: 259.9 degrees
Temperature: 22.60 °C
Reading Time: 46.453 ms
Instructions:
- Rotate the board 360 degrees
- Observe which heading option changes most consistently
- Modify calculate_magnetic_orientation to use the best option
---------------------------./mmc5603_test -c -f 10 ./mmc5603_test -f 10

1166

被折叠的 条评论
为什么被折叠?



