linux rk3506 mmc5633 mmc5603驱动

该文章已生成可运行项目,

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, &reg_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, &reg);
		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:

  1. Rotate the board 360 degrees
  2. Observe which heading option changes most consistently
  3. Modify calculate_magnetic_orientation to use the best option
    ---------------------------./mmc5603_test -c -f 10 ./mmc5603_test -f 10
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值