地磁传感器移植指南

本文说明如何将地磁传感器接入 Solution Sensor 框架。接入后,应用可获取磁场和方位数据,用于指南针等场景。

本文以 QMC6310 为例,说明驱动适配、RT-Thread Sensor 注册、地磁算法调度和应用订阅流程。

1. 参考代码

文件

作用

solution\components\sensor\sensor_service\sensor_service.c

接收传感器数据并分发给订阅者

solution\components\sensor\sensor_service\sensor_service_ui.c

向 UI 提供传感器数据订阅和取消订阅接口

solution\components\sensor\sensor_algo\timer\sensor_timer.c

为传感器算法提供周期调度定时器

solution\components\sensor\sensor_algo\reg\sensor_reg.c

绑定传感器设备和算法实例

solution\components\sensor\sensor_algo\mag\mag_algo.c

地磁算法层:地磁初始化、校准与调度

solution\components\sensor\sensor_algo\mag\qmc6310\qmc6310_mag_service.c

QMC6310 适配:QMC6310 器件注册与数据读取

solution\components\sensor\sensor_algo\mag\mmc56x3\mmc56x3_mag_service.c

MMC56X3 适配:MMC56X3 器件注册与数据读取

2. 应用例程

场景

文件

调用接口

指南针 APP 打开 / 关闭地磁传感器

compass_gui.c

app_open_sensors()app_close_sensors()app_rt_data_info_get()

3. 原生驱动移植

本节实现传感器芯片最基础的硬件通信功能,包括I2C总线操作、中断处理和电源管理。

3.1 配置与宏定义

路径:SiFli SDK configuration Board Config Board Peripherals Sensor config Magnetic Sensor QMC6310

Board Peripherals 在 PC Simulator 工程中不显示,因此该路径仅适用于真实硬件工程。

配置项

说明

建议

SENSOR_USING_MAG

启用地磁传感器框架

必选

MAG_USING_QMC6310

启用 QMC6310 驱动

使用 QMC6310 时必选

QMC6310_I2C_BUS

I2C 总线名

按板级实际连接填写,如 i2c6

QMC6310_MODEL_NAME

设备名

默认 qmc6310

QMC6310_ALGO_NAME

算法设备名

默认 qmclib

QMC6310_USE_DMA

I2C DMA 方式

需要 DMA 时打开

QMC6310_POW_PIN

电源控制 GPIO

无控制脚时填 -1

如果启用 QMC6310_USE_DMA,还需要打开 QMC6310_I2C_BUS 对应的 BSP_USING_I2CxBSP_I2Cx_USING_DMA。默认总线名为 i2c6,该实例不在 52x 平台显示。

新增器件时,应在 sdk/customer/peripherals/sensor/mag/Kconfig 中增加配置项,并在 sdk/customer/peripherals/sensor/mag 下加入驱动实现。

宏名称

类型

说明

MAG_USING_QMC6310

必需

启用QMC6310地磁传感器

QMC6310_I2C_BUS

必需

I2C总线设备名称(如"i2c2")

QMC6310_POW_PIN

可选

电源控制GPIO引脚号

3.2 I2C总线初始化

初始化需要通过宏定义找到对应的总线设备,打开设备并对总线进行参数设置。

bool qmc6310_i2c_init(void)
{
    qmc6310_i2cbus = (struct rt_i2c_bus_device *)rt_device_find(QMC6310_I2C_BUS);
    if (RT_Device_Class_I2CBUS != qmc6310_i2cbus->parent.type)
    {
        qmc6310_i2cbus = NULL;
    }
    if (qmc6310_i2cbus)
    {
        rt_device_open((rt_device_t)qmc6310_i2cbus, RT_DEVICE_FLAG_RDWR);
    }
    else
    {
        rt_kprintf("bus not find\r\n");
        return RT_FALSE;
    }
    {
        struct rt_i2c_configuration configuration =
        {
            .mode = 0,
            .addr = 0,
            .timeout = 200,
            .max_hz = 400000,
        };

        rt_i2c_configure(qmc6310_i2cbus, &configuration);
    }

    rt_kprintf("mag_i2c_init ok!\r\n");

    return RT_TRUE;
}

3.3 I2C读写接口

总线读写函数供原生驱动调用,实现寄存器的数据交互。

rt_err_t mag_i2c_read(uint8_t i2c_addr, uint8_t reg, uint8_t *buf, uint8_t len)
{
    struct rt_i2c_msg msgs[2];
    uint32_t res = -RT_ERROR;
    uint8_t retries = 0;

    if (qmc6310_i2cbus)
    {

        msgs[0].addr = i2c_addr;
        msgs[0].flags = RT_I2C_WR;
        msgs[0].buf = ®
        msgs[0].len = 1;

        msgs[1].addr = i2c_addr;
        msgs[1].flags = RT_I2C_RD;
        msgs[1].buf = buf;
        msgs[1].len = len;

        if (rt_i2c_transfer(qmc6310_i2cbus, msgs, 2) == 2)
        {
            res = RT_EOK;
        }
        else
        {
            res = -RT_ERROR;
        }
        return res;
    }

    return res;
}
rt_err_t mag_i2c_write(uint8_t i2c_addr, uint8_t reg, uint8_t data)
{
    struct rt_i2c_msg msgs[1];
    uint8_t value[2];
    uint32_t res = -RT_ERROR;;
    uint8_t retries = 0;

    if (qmc6310_i2cbus)
    {
        value[0] = reg;
        value[1] = data;

        msgs[0].addr = i2c_addr;
        msgs[0].flags = RT_I2C_WR;
        msgs[0].buf = value;
        msgs[0].len = 2;

        if (rt_i2c_transfer(qmc6310_i2cbus, msgs, 1) == 1)
        {
            res = RT_EOK;
        }
        else
        {
            res = -RT_ERROR;
        }
        return res;
    }

    return res;
}

3.4 电源管理

void qmc6310_power_onoff(bool onoff)
{
    if (onoff)
    {
#if(QMC6310_POW_PIN >= 0)
        rt_pin_mode(QMC6310_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(QMC6310_POW_PIN, PIN_HIGH);
#endif
        rt_kprintf("qmc6310 power on!\n");
    }
    else
    {
#if(QMC6310_POW_PIN >= 0)
        rt_pin_mode(QMC6310_POW_PIN, PIN_MODE_OUTPUT);
        rt_pin_write(QMC6310_POW_PIN, PIN_LOW);
#endif
        rt_kprintf("qmc6310 power off!\n");
    }

}

4. RT-Thread 传感器设备移植

本节将原生驱动封装为RT-Thread标准传感器设备,实现与系统框架的无缝对接。

4.1 配置与宏定义

在目录solution\components\sensor\sensor_algo\mag添加对应的器件目录“qmc6310”,在“qmc6310"目录下创建qmc6310_mag_service.c文件,用于实现qmc6310注册为Sensor设备;

宏名称

类型

功能说明

取值/注意事项

QMC6310_BUFF_SIZE

宏定义

FIFO数据缓冲区大小,限制单次读取的最大帧数

默认1,单位:帧

QMC6310_PERIOD_TIMER

宏定义

数据读取周期,决定应用层数据刷新频率

默认200ms,单位:毫秒

MAG_USING_QMC6310

Kconfig 配置符号

控制整个驱动模块的编译开关

在 menuconfig 选择该项以启用驱动;构建系统生成对应条件编译宏,不应手工定义

4.2 关键数据结构

定义设备私有数据结构体和全局变量。

struct qmc6310_device
{
    rt_device_t bus;
    rt_uint8_t id;
    rt_uint8_t i2c_addr;
    void *gs_input;
};

static struct qmc6310_device *qmc6310_dev;

4.3 设备注册接口

将QMC6310注册为RT-Thread标准传感器设备。

static int rt_hw_qmc6310_register(const char *name)
{
    int result;
    {
        sensor_mag = rt_calloc(1, sizeof(struct rt_sensor_device));
        if (sensor_mag == RT_NULL)
            goto __exit;

        sensor_mag->info.type       = RT_SENSOR_CLASS_MAG;
        sensor_mag->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN;
        sensor_mag->info.model      = NULL;
        sensor_mag->info.unit       = RT_SENSOR_UNIT_MGAUSS;
        sensor_mag->info.intf_type  = RT_SENSOR_INTF_I2C;
        sensor_mag->info.range_max  = 360;
        sensor_mag->info.range_min  = 0;
        sensor_mag->info.period_min = 1;
        sensor_mag->data_len = 0;
        sensor_mag->data_buf = NULL;
        sensor_mag->config.mode = RT_SENSOR_MODE_POLLING;
        sensor_mag->config.irq_pin.pin = RT_PIN_NONE;
        sensor_mag->ops = &sensor_ops;

        result = rt_hw_sensor_register(sensor_mag, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
        if (result != RT_EOK)
        {
            rt_kprintf("MAG [%s] register err code: %d", name, result);;
            goto __exit;
        }
    }

    rt_kprintf("MAG [%s] init success!\n", name);
    return RT_EOK;

__exit:

    if (sensor_mag != RT_NULL)
    {
        rt_free(sensor_mag);
        sensor_mag = RT_NULL;
    }

    return -RT_ERROR;

}

static int qmc6310_register(void)
{
    /*if menuconfig config name, use menuconfig name*/
#ifdef QMC6310_MODEL_NAME
    static char  name[] = QMC6310_MODEL_NAME;
#else
    static char  name[] = "qmc6310";
#endif
    int ret = rt_hw_qmc6310_register(name);
    return ret;
}
INIT_COMPONENT_EXPORT(qmc6310_register);

INIT_COMPONENT_EXPORT(qmc6310_register);是RT-Thread系统组件初始化命令,用于在系统启动时调用qmc6310_register()完成设备的注册。

4.4 传感器接口实现

注册为Sensor设备必须完善接口,实现rt_sensor_ops结构体。

static struct rt_sensor_ops sensor_ops =
{
    qmc6310_fetch_data,
    qmc6310_control
};

4.4.1 数据获取接口

qmc6310_fetch_data()用于服务层向驱动层获取数据。

static rt_size_t qmc6310_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    RT_ASSERT(buf);
    return qmc6310_polling_get_data(sensor, buf);
}

qmc6310_polling_get_data()实现具体的数据读取逻辑:

static rt_size_t qmc6310_polling_get_data(rt_sensor_t sensor, void *data)
{
    mag_fifo_t *mag_fifo = (mag_fifo_t *)data;
    qmc6310_read_mag_xyz(&(mag_fifo->buf[0].mag_data[0]), &(mag_fifo->buf[0].mag_data[1]), &(mag_fifo->buf[0].mag_data[2]));
    mag_fifo->num = 1;
    return 1;
}

4.4.2 设备控制接口

qmc6310_control()用于服务层对驱动层的控制。

static rt_err_t qmc6310_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t result = RT_EOK;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
    {
        sensor_reg_info_t *info = (sensor_reg_info_t *)args;
        if (!qmc6310_dev)
        {
            info->sensor_id = 0;
            return RT_ERROR;
        }
        info->sensor_id = qmc6310_dev->id;

        if (info->gs_input)
            qmc6310_dev->gs_input = info->gs_input;     /*get gsensor data address */

        info->dev_period = QMC6310_PERIOD_TIMER;
        info->fifo_len = QMC6310_BUFF_SIZE;
        *(uint8_t *)args = qmc6310_dev->id;
        info->mag_algo_ops = qmc_algo_ops;
        info->mag_para_ops = qmc_para_ops;
    }
    break;
    case RT_SENSOR_CTRL_SET_RANGE:
        result = qmc6310_set_range(sensor, (rt_int32_t)args);
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        result = -RT_EINVAL;
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        result = qmc6310_set_mode(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        result = qmc6310_set_power(sensor, (rt_uint32_t)args & 0xff);
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        result = qmc6310_self_test(sensor, *((rt_uint8_t *)args));
        break;
    default:
        return -RT_ERROR;
    }

    return result;
}

4.4.3 电源控制实现

qmc6310_set_power()实现对传感器电源状态的控制。

static rt_err_t qmc6310_set_power(rt_sensor_t sensor, rt_uint8_t power)
{
    switch (power)
    {
    case RT_SENSOR_POWER_DOWN:
        qmc6310_close();
        qmc6310_power_onoff(RT_FALSE);
        break;
    case RT_SENSOR_POWER_NORMAL:
        qmc6310_power_onoff(RT_TRUE);
        qmc6310_open();
        break;
    case RT_SENSOR_POWER_LOW:
        qmc6310_lowpowermode();
        break;
    case RT_SENSOR_POWER_HIGH:
        break;
    default:
        break;
    }
    return RT_EOK;
}

4.5 设备初始化函数

qmc6310_open()作为qmc6310地磁传感器设备的初始化入口,完成硬件唤醒、资源分配和参数配置,使传感器进入可工作状态。

static rt_err_t qmc6310_open(void)
{
    rt_err_t ret = RT_EOK;
#ifdef QMC6310_DEBUG
    rt_kprintf("qmc6310 open start ! \n");
#endif
    if (!qmc6310_dev)
    {
        if (qmc6310_init() == 0)
        {
            qmc6310_dev = rt_calloc(1, sizeof(struct qmc6310_device));
            if (qmc6310_dev == RT_NULL)
            {
                ret = RT_ENOMEM;
                goto err;
            }
            qmc6310_dev->bus = (rt_device_t)qmc6310_get_i2c_handle();
            qmc6310_dev->i2c_addr = qmc6310_get_dev_addr();
            qmc6310_dev->id = qmc6310_get_dev_id();
            rt_kprintf("(%s) open ok!\n", sensor_mag->parent.parent.name);
            return RT_EOK;
        }
        ret = RT_ERROR;
err:
        rt_kprintf("qmc6310 init err! code: %d\n", ret);
    }
    return ret;
}

5. RT-Thread 算法设备移植

算法负责原始数据处理(如滤波、特征提取)。 本节实现将算法嫁到到RT-Thread设备驱动框架上,封装为"算法设备",负责原始数据处理(如滤波、特征提取)。以移植qmclib算法为例。

5.1 配置

在目录solution\components\sensor\sensor_algo\mag\qmc6310目录下创建添加算法的lib文件,添加算法设备驱动的qmc6310_mag_service.c

5.2 设备注册接口

static int rt_hw_qmc_algo_register(const char *name)
{
    /* magnetometer/compass sensor register */
    qmc_algo = rt_calloc(1, sizeof(struct rt_sensor_device));
    RT_ASSERT(qmc_algo)

    qmc_algo->info.type       = RT_SENSOR_CLASS_MAG;
    qmc_algo->info.vendor     = RT_SENSOR_VENDOR_UNKNOWN;
    qmc_algo->info.model      = RT_NULL;
    qmc_algo->info.unit       = RT_SENSOR_UNIT_NONE;
    qmc_algo->info.intf_type  = 0;
    qmc_algo->info.range_max  = 220;
    qmc_algo->info.range_min  = 1;
    qmc_algo->info.period_min = 1;
    qmc_algo->info.fifo_max     = 1;
    qmc_algo->data_buf        = RT_NULL;
    qmc_algo->data_len            = 0;        //must be 0, because don't use data buf
    qmc_algo->config.mode = RT_SENSOR_MODE_POLLING;
    qmc_algo->config.irq_pin.pin = RT_PIN_NONE;
    qmc_algo->ops = &sensor_algo_ops;
    rt_err_t result = rt_hw_sensor_register(qmc_algo, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
    if (result != RT_EOK)
    {
        rt_kprintf("MAG algo [%s] register err code: %d", name, result);
        goto __exit;
    }
    rt_kprintf("MAG algo[%s] init success!\n", name);
    return RT_EOK;
__exit:
    if (qmc_algo)
    {
        rt_free(qmc_algo);
        qmc_algo = RT_NULL;
    }
    return -RT_ERROR;
}
/**
 * @brief qmc algo  register, register name need unique, and longth is less than RT_NAME_MAX
*/
static int qmc_algo_register(void)
{
    /*register mag algo*/
    /*if  config QMC6310_ALGO_NAME, use QMC6310_ALGO_NAME, ohterwise use default name*/
#ifdef QMC6310_ALGO_NAME
    static char  name[] = QMC6310_ALGO_NAME;
#else
    static char  name[] = "qmclib";
#endif
    int ret = rt_hw_qmc_algo_register(name);
    return ret;
}
INIT_COMPONENT_EXPORT(qmc_algo_register);

5.2.1 数据获取接口

static rt_size_t qmc_algo_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
    return 0;
}

:算法设备不通过数据获取接口获取数据,而是通过回调函数直接处理。

5.2.2 设备控制接口

static rt_err_t qmc_algo_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
    rt_err_t result = RT_EOK;

    switch (cmd)
    {
    case RT_SENSOR_CTRL_GET_ID:
    {
        sensor_reg_info_t *info = args;
        info->sensor_id = 0xff;
        info->mag_algo_ops = qmc_algo_ops;
        info->mag_para_ops = qmc_para_ops;
        rt_kprintf("qmc algo_id	: %d ! \n", info->sensor_id);
        flag_calibrate = 0;
        break;
    }
    case RT_SENSOR_CTRL_SET_RANGE:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_ODR:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_MODE:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SET_POWER:
        result = RT_ERROR;
        break;
    case RT_SENSOR_CTRL_SELF_TEST:
        result = RT_ERROR;
        break;
    default:
        return -RT_ERROR;
    }

    return result;
}

:在这个函数中将qmc_algo_ops()qmc_para_ops()传递给上层的sensor_reg_info_t中的函数指针上,由上层的mag_algo进行统一调用。

5.3 算法处理函数

5.3.1 配置入口

用于处理上层下发的配置信息(如采样周期等参数)

static void qmc_para_ops(uint16_t msg_id, void *data)
{
    switch (msg_id)
    {
    case APP_SENSOR_SETTING_MAG_TIME:
    {
        qmc_result_peroid = *((uint32_t *)data);
        rt_kprintf("set qmc_result_peroid is %d ! \n", qmc_result_peroid);
        break;
    }
    default:
        ;
    }
}

5.3.2 数据入口

将地磁传感器原始数据传入函数,主动获取运动传感器数据,经过坐标对齐、磁场校正、姿态融合等算法操作后,产出方位角、俯仰角、横滚角和校准精度等数据,

static void  qmc_algo_ops(uint32_t peroid, mag_fifo_t *data, void *g_in)
{
    float mag_data[3];
    float result_data[3];
    float offset_data[3];
    float rr_data;
    float ori_data[3];
    int8_t accuracy;
    uint8_t mag_coordinate;
    float raw_mag[3];
    if (g_in)
        mag_get_gsensor_accxyz(g_in);
    else
        rt_kprintf("mag can't get acc data, try open macro USING_GSENSOR_DATA_STORE_LIST ,and open gs!\n");
    mag_data[0] = data->buf[0].mag_data[0];
    mag_data[1] = data->buf[0].mag_data[1];
    mag_data[2] = data->buf[0].mag_data[2];

    /* Convert the coordinate system */
    mag_coordinate = 2; //default
#ifdef COORDINATE_GS_TO_MAG
    mag_coordinate = 6;
#endif
    mag_coordinate_raw_to_real(mag_coordinate, mag_data, mag_real_data);

    raw_mag[0] = mag_real_data[0] * evers[0][0] + mag_real_data[1] * evers[0][1] + mag_real_data[2] * evers[0][2];
    raw_mag[1] = mag_real_data[0] * evers[1][0] + mag_real_data[1] * evers[1][1] + mag_real_data[2] * evers[1][2];
    raw_mag[2] = mag_real_data[0] * evers[2][0] + mag_real_data[1] * evers[2][1] + mag_real_data[2] * evers[2][2];


    convert_magnetic(raw_mag, result_data, offset_data, &rr_data, &accuracy);
    if ((!flag_calibrate) && accuracy == 3)
    {
        flag_calibrate = 1;
        rt_kprintf("flag_calibrate set 1\n");
    }
#ifdef QMC6310_DEBUG

    rt_kprintf("x = %f, y = %f, z = %f, x_bias= %f, y_bias= %f, z_bias = %f, accuracy = %d;\n",
               result_data[0], result_data[1], result_data[2], raw_mag[0] - result_data[0], raw_mag[1] - result_data[1],
               raw_mag[2] - result_data[2], accuracy);

    rt_kprintf("offset_data_x = %f, offset_data_y = %f, offset_data_z = %f, rr_data= %f;\n",
               offset_data[0], offset_data[1], offset_data[2], rr_data);
#endif
    QST_ORI_progress(acc, result_data, ori_data, &accuracy);

#ifdef QMC6310_DEBUG
    rt_kprintf("qmc data: mag_real_X=%f, mag_real_Y=%f, mag_real_Z=%f, accX=%f, accY=%f, accZ=%f !\n", \
               mag_real_data[0], mag_real_data[1], mag_real_data[2], acc[0], acc[1], acc[2]);
    rt_kprintf("mag data:ori_data=%f,%f,%f,uracy=%d\n", ori_data[0], ori_data[1], ori_data[2], accuracy);
#endif

    if ((!flag_calibrate))
        qmc_msg.uracy = accuracy;
    else
        qmc_msg.uracy = 3;



    qmc_msg.azimuth = ori_data[0];
    qmc_msg.pitch = ori_data[1];
    qmc_msg.roll = ori_data[2];
    if (qmc_msg.uracy == 0)
    {
        rt_kprintf("mag data invalid, Please calibrate first.\n");
    }
    mag_meas_result_postprocess(peroid, &qmc_msg);
}

5.4 运动传感器数据融合

地磁传感器必须借助运动传感器(加速度计、陀螺仪)进行姿态补偿与融合,才能输出稳定、精准的方位信息。

  1. 启用 USING_GSENSOR_DATA_STORE_LIST,用于缓存加速度计数据。

    • LCPU 工程路径:Gsensor data store in list

    • 52x/57x HCPU 工程路径:Sensor Config Gsensor data store in list

  2. 在真实硬件工程中打开加速度计:Board Peripherals Sensor config 6D Sensor for Accelerator and Gyro,并选择实际加速度计型号。

  3. PC Simulator 不显示真实硬件驱动路径,真实传感器算法也不在模拟器中运行。

完成配置后,可通过上层传入的数据链表获取可用的运动传感器数据。

static void qmc6310_get_gsensor_data(uint8_t index, float *x, float *y, float *z, void *g_in)
{
#if defined (USING_GSENSOR_DATA_STORE_LIST) && (defined (BF0_LCPU) || defined (SENSOR_IN_HCPU))
    /*mag and gs all in hcpu or  all in lcpu*/
    rt_list_t *pos, *next;
    gsensors_fifo_t *node;
    if (qmc6310_dev->gs_input)
    {
        rt_list_t *gsensor_data_header = (rt_list_t *)g_in;
        if (!rt_list_len(gsensor_data_header))  //if no gsensor data , return
            return;
        rt_list_for_each_safe(pos, next, gsensor_data_header)
        {
            node = rt_list_entry(pos, gsensors_fifo_t, list);
            if (index > node ->num)
                index = node ->num - 1;

            *x = node -> buf[index].acce_data[0];
            *y = node -> buf[index].acce_data[1];
            *z = node -> buf[index].acce_data[2];
#ifdef QMC6310_DEBUG
            rt_kprintf("qmc6310_get_gs_data: x=%f, y=%f, z=%f;\n", node -> buf[index].acce_data[0], node -> buf[index].acce_data[1], node -> buf[index].acce_data[2]);
#endif
            break;
        }

    }
#else
    /*mag inhcpu and gs in lcpu*/
    //to be add: get gs data in ui;
#endif
}
  1. 算法片段:在通过convert_magnetic获得已校准的地磁矢量后,将其与加速度计数据一同送入QST_ORI_progress,完成融合解算,得到最终的姿态输出。

    convert_magnetic(raw_mag, result_data, offset_data, &rr_data, &accuracy);
    if ((!flag_calibrate) && accuracy == 3)
    {
        flag_calibrate = 1;
        rt_kprintf("flag_calibrate set 1\n");
    }
    QST_ORI_progress(acc, result_data, ori_data, &accuracy);

6. 地磁传感器调度模块

调度模块负责周期性读取地磁数据,结合运动传感器数据进行姿态补偿,并输出指南针方位等业务数据。

6.1 器件与算法关联

6.1.1 核心接口说明

sensor_reg_open 是 Solution 基于 RT-Thread 扩展的核心接口,负责在系统初始化阶段“连接”已注册的传感器设备与算法设备,构建“硬件采集→算法处理”的数据链路,是传感器从“注册态”进入“可用态”的关键步骤。 主注册接口

rt_err_t sensor_reg_open(
    const char *sensor_name,     // 传感器设备名(如"mag_qmc6310")
    const char *algo_name,       // 算法设备名(如"mag_qmclib")
    sensor_reg_info_t *sensor_info, // 输出:传感器+算法信息
    rt_device_t *sensor_device   // 输出:传感器设备句柄
);

功能:完成传感器设备与算法模块的查找、验证与绑定。

数据结构定义

// 算法信息载体(在sensor_reg.h中定义)
typedef struct {
    sensor_type_t type;          // 传感器类型
    uint32_t sensor_id;          // 传感器芯片ID
    uint32_t dev_period;         // 采样周期(ms)
    uint8_t fifo_len;            // FIFO深度
    rt_uint32_t algo_period;    //算法周期(ms)
    // 算法函数指针(由算法设备注入)

    void (* mag_algo_ops_t)(uint32_t peroid, mag_fifo_t *data, void *g_in);
    void (* mag_para_ops_t)(uint16_t msg_id, void *data);
} sensor_reg_info_t;

6.1.2 应用示例

  1. mag_algo.c 中的宏定义,修改MAG_NAME为指定的"qmc6310",修改MAG_ALGO_NAME为指定的"qmclib"

fishy
  1. 初始化绑定设备,通过sensor_reg_open()查找并验证传感器设备(mag_qmc6310)与算法设备(mag_qmclib),通过算法设备向sensor_reg_info_t结构体注入其处理函数指针 (mag_algo_ops_tmag_para_ops_t);此后数据流处理时,调度器直接通过该函数指针调用算法。

static int mag_init(void)
{
    char *mag_name = "mag_"MAG_NAME;
#ifdef MAG_ALGO_NAME
    char *mag_algo_name = "mag_"MAG_ALGO_NAME;
#else
    char *mag_algo_name =  NULL;
#endif
    sensor_info.type = SENSOR_MAG;

    /*mag need gs data,so tell mag drv gs data list header*/
#if defined (SENSOR_USING_6D) && !defined (BSP_USING_PC_SIMULATOR) && defined (USING_GSENSOR_DATA_STORE_LIST)
    if (gsensor_is_opened())
        sensor_info.gs_input = gsensor_get_stored_data();
#endif
    if (RT_EOK != sensor_reg_open(mag_name, mag_algo_name, &sensor_info, &mag_device))
    {
        return RT_ERROR;
    }
    mag_id          = sensor_info.sensor_id;
    mag_period      = sensor_info.dev_period;;
    mag_fifo_len    = sensor_info.fifo_len;

#ifdef MAG_ALGO_DEBUG
    LOG_I("%s: type = %d, period = %d, fifo_len = %d, mag_algo_ops = 0x%p, mag_para_ops = 0x%p!", __func__, SENSOR_MAG,
          mag_period, mag_fifo_len, sensor_info.mag_algo_ops, sensor_info.mag_para_ops);
#endif
    return RT_EOK;
}
  1. 传感器设备与算法设备成功绑定,此时可以通过rt_device_read()直接读取处理经过算法处理后的传感器数据。

static int32_t mag_data_fetch(uint32_t data_size, void *data)
{
    if (!mag_device || !data) return RT_ERROR;

    rt_size_t size = rt_device_read(mag_device, 0, data, 1);

    if (0 == size) LOG_W("mag_data_fetch: err!!!\n");
    return size;
}

6.2 模块调度

6.2.1 基础概念

Solution 基于 RT-Thread 软件定时器 实现传感器的自动化调度,替代原生轮询机制,可灵活配置采样周期,同时适配低功耗场景(支持动态调整周期)。该调度器是 “器件采集→算法处理→数据上报” 全流程的触发核心。

6.2.2 示例代码

void mag_algo_scheduler(uint32_t period)
{
    static uint16_t mag_peroid_time = 0;
    int32_t ret = 0;
    if (!mag_is_opened())
        return;
    mag_peroid_time += period;
    if (mag_peroid_time < mag_period)
        return;
    mag_peroid_time = 0;

    /*LCPU  need fetch data in mag_algo_scheduler*/
#ifndef SENSOR_IN_HCPU
    ret = mag_data_fetch(0, &mag_data);
#endif
#ifdef MAG_ALGO_DEBUG
    rt_kprintf("mag_data x= %f, num = %d;\n", mag_data.buf[0].mag_data[0], mag_data.num);
#endif
    if (mag_data.num > 0 && sensor_info.mag_algo_ops)
    {
        sensor_info.mag_algo_ops(period, &mag_data, sensor_info.gs_input);
    }
}

:该函数由sensor_timer定时器定时触发,实现了数据的采集、处理、上报全流程