五、SPI客户端驱动——bmp280

spi客户端驱动

spi子系统类似i2c子系统,驱动注册方式和i2c类似。没有什么可说的,主要区别在于SPI总线会多两个信号线。

驱动注册

使用 module_spi_driver() 进行注册即可。在注册之前,需要定义号设备树匹配的结构体。

static struct of_device_id bmp280_of_match[] = {
    {.compatible = "bosch,bmp280-spi"},
    { },
};
MODULE_DEVICE_TABLE(of, bmp280_of_match);

static struct spi_driver bmp280_driver = {
    .probe = bmp280_probe,
    .remove = bmp280_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "bmp280",
        .of_match_table = of_match_ptr(bmp280_of_match),
    },
};

module_spi_driver(bmp280_driver);

注意这里的compatible属性,格式应该是 vendor,model 。node 将成为设备别名,如果设备树中的 node 名称和 model 字符串相等,那么也能匹配成功。或者 node 和 driver->name 相等,也将匹配成功。优先级顺序按照如下:

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
	const struct spi_device	*spi = to_spi_device(dev);
	const struct spi_driver	*sdrv = to_spi_driver(drv);

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	if (sdrv->id_table)
		return !!spi_match_id(sdrv->id_table, spi);

	return strcmp(spi->modalias, drv->name) == 0;
}

spi 也支持 id_table 方式进行匹配。但是在 probe 中不会传入 id_table 参数。
在 probe 中,我们需要去设置 SPI 主机的属性,以便适配我们得SPI设备。这里,bmp280 支持的模式是:0x000x11 ,最大 5MHz 速率。

static int bmp280_probe(struct spi_device *spi)
{
    struct bmp280_dev *bmp280;
    int ret = 0;

    bmp280 = devm_kzalloc(&spi->dev, sizeof(*bmp280), GFP_KERNEL);
    if (IS_ERR(bmp280)) {
        dev_err(&spi->dev, "kzalloc fail: 0x%lx\n", PTR_ERR(bmp280));
        return PTR_ERR(bmp280);
    }

    spi_set_drvdata(spi, bmp280);
    bmp280->spi = spi;

    spi->bits_per_word = 8;
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 5000000;
    ret = spi_setup(spi);
    if (ret) {
        return -EIO;
    }

    if (bmp280_init(bmp280)) {
        dev_err(&spi->dev, "bmp280 init error\n");
        return -EIO;
    }

    device_create_file(&spi->dev, &dev_attr_temperature);
    device_create_file(&spi->dev, &dev_attr_pressure);

    dev_info(&spi->dev, "bmp280 probe success\n");
    return 0;
}

设备树

除了驱动的设备树匹配外,SPI客户端驱动也是有地址信息的。如果有多个CS引脚,那么不同的CS引脚外接的设备对应不同的地址。CS-0 为 reg = <0x0> ,CS-1 为 reg = <0x01>,依次类推。
这里使用的开发板是rockchip rk3399:

rockchip_spi_probe
	spi_register_master
		of_register_spi_devices
			of_register_spi_device

of_register_spi_device 中,会去识别spi需要的设备树信息。如:

	rc = of_property_read_u32(nc, "reg", &value);
	if (rc) {
		dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
			nc->full_name, rc);
		goto err_out;
	}
	spi->chip_select = value;

	/* Mode (clock phase/polarity/etc.) */
	if (of_find_property(nc, "spi-cpha", NULL))
		spi->mode |= SPI_CPHA;
	if (of_find_property(nc, "spi-cpol", NULL))
		spi->mode |= SPI_CPOL;
	if (of_find_property(nc, "spi-cs-high", NULL))
		spi->mode |= SPI_CS_HIGH;
	if (of_find_property(nc, "spi-3wire", NULL))
		spi->mode |= SPI_3WIRE;
	if (of_find_property(nc, "spi-lsb-first", NULL))
		spi->mode |= SPI_LSB_FIRST;
		/* Device speed */
	......
	rc = of_property_read_u32(nc, "spi-max-frequency", &value);
	if (rc) {
		dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
			nc->full_name, rc);
		goto err_out;
	}
	spi->max_speed_hz = value;

对于spi主机的配置,我们都可以在设备树中完成。另外对于CS pin的定义,在:

rockchip_spi_probe
	spi_register_master
		of_spi_register_master
static int of_spi_register_master(struct spi_master *master)
{
	int nb, i, *cs;
	struct device_node *np = master->dev.of_node;

	if (!np)
		return 0;

	nb = of_gpio_named_count(np, "cs-gpios");
	master->num_chipselect = max_t(int, nb, master->num_chipselect);

	/* Return error only for an incorrectly formed cs-gpios property */
	if (nb == 0 || nb == -ENOENT)
		return 0;
	else if (nb < 0)
		return nb;

	cs = devm_kzalloc(&master->dev,
			  sizeof(int) * master->num_chipselect,
			  GFP_KERNEL);
	master->cs_gpios = cs;

	if (!master->cs_gpios)
		return -ENOMEM;

	for (i = 0; i < master->num_chipselect; i++)
		cs[i] = -ENOENT;

	for (i = 0; i < nb; i++)
		cs[i] = of_get_named_gpio(np, "cs-gpios", i);

	return 0;
}

这里会从 cs-gpios 这个属性中获取对于CS引脚的定义信息。这里有个奇怪的现象,rockchip平台,并没有在设备树中定义 cs-gpios 属性,但是CS引脚在SPI的使用过程中仍然可用。当然这里的CS引脚是 SPI对应的默认CS引脚:

		spi1 {
			spi1_clk: spi1-clk {
				rockchip,pins =
					<1 9 RK_FUNC_2 &pcfg_pull_up>;
			};
			spi1_cs0: spi1-cs0 {
				rockchip,pins =
					<1 10 RK_FUNC_2 &pcfg_pull_up>;
			};
			spi1_rx: spi1-rx {
				rockchip,pins =
					<1 7 RK_FUNC_2 &pcfg_pull_up>;
			};
			spi1_tx: spi1-tx {
				rockchip,pins =
					<1 8 RK_FUNC_2 &pcfg_pull_up>;
			};
		};

网上的说法是,有的平台SPI硬件会自动使用CS引脚。这里看代码:

static void spi_set_cs(struct spi_device *spi, bool enable)
{
	if (spi->mode & SPI_CS_HIGH)
		enable = !enable;

	if (gpio_is_valid(spi->cs_gpio)) {
		gpio_set_value(spi->cs_gpio, !enable);
		spi->master->set_cs(spi, !enable);
	} else {
		spi->master->set_cs(spi, !enable);
	}
}

应该是在自己设置了 cs-gpios 属性后,还是会调用rockchip平台的cs_gpio的设置函数。我猜测这里会对默认的CS_GPIO进行使用。

static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
{
	struct spi_master *master = spi->master;
	struct rockchip_spi *rs = spi_master_get_devdata(master);
	bool cs_asserted = spi->mode & SPI_CS_HIGH ? enable : !enable;

	/* Return immediately for no-op */
	if (cs_asserted == rs->cs_asserted[spi->chip_select])
		return;

	if (cs_asserted) {
		/* Keep things powered as long as CS is asserted */
		pm_runtime_get_sync(rs->dev);

		if (gpio_is_valid(spi->cs_gpio))
			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
		else
			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
	} else {
		if (gpio_is_valid(spi->cs_gpio))
			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
		else
			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));

		/* Drop reference from when we first asserted CS */
		pm_runtime_put(rs->dev);
	}

	rs->cs_asserted[spi->chip_select] = cs_asserted;
}

经过测试验证,当我们不定义 cs-gpios 时,默认的CS引脚确实是能正常工作的。当我们定义CS引脚后,默认的CS引脚还是正常工作的。

&spi1 {
	status = "okay";

    cs-gpios = <&gpio4 29 GPIO_ACTIVE_LOW>;

    bmp280-spi@0{
		status = "okay";
		compatible = "bosch,bmp280-spi";
		reg = <0x00>;
		spi-max-frequency = <10000000>;
		//spi-cpha;		/* SPI mode: CPHA=1 */
		//spi-cpol;   	/* SPI mode: CPOL=1 */
		//spi-cs-high;
	};
};

也就是说,上述dts中,定义了设备地址为0的设备的CS引脚,当SPI工作时,默认的CS引脚和定义的另外一个CS引脚是同时工作的。因此当,只有一个CS引脚时,在rockchip平台,就不需要去定义CS引脚了,硬件上最好就使用默认的CS引脚即可。
当我们一个SPI总线挂了两个设备时,就需要定义 cs-gpios 引脚了。将第一个CS引脚定义为默认的CS引脚,第二个引脚定义为我们第二设备的CS引脚。这样,控制对应地址的设备就能设置对应CS引脚了。

&spi1 {
	status = "okay";

    cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>,
                <&gpio4 29 GPIO_ACTIVE_LOW>;

    bmp280-spi@1{
		status = "okay";
		compatible = "bosch,bmp280-spi";
		reg = <0x01>;
		spi-max-frequency = <10000000>;
		//spi-cpha;		/* SPI mode: CPHA=1 */
		//spi-cpol;   	/* SPI mode: CPOL=1 */
		//spi-cs-high;
	};
};

这里,当我们将设备CS引脚接入到第一个CS引脚时,设置设备地址和reg为 0x0,那么控制设备时,就只有第一个CS引脚会有电平变化。当将CS引脚接入到第二个CS引脚时,设置设备地址和reg 为 0x01,那么控制设备时,就只有第二个CS引脚有电平变化。此时能发现,CS是根据设备地址来自行选择设置的。
of_spi_register_master() 函数会解析所有设置的 CS 引脚。在 spi_add_device 添加设备时,会根据 reg 地址设置相应的 CS 引脚给相应的设备地址。

int spi_add_device(struct spi_device *spi)
{
	......
	if (master->cs_gpios)
		spi->cs_gpio = master->cs_gpios[spi->chip_select];
	......
}

根据上述提到的 spi_set_cs 的定义,当reg 为0时,会同时设置SPI的默认CS引脚和 cs-gpios 中定义第0个CS引脚。
在调试时遇到一个问题,在使用 GPIO1_B4 作为CS引脚时,SPI通信时死活无法控制这个引脚,最后发现是这个引脚一直是高电平。暂不清楚是GPIO有问题,还是被其他外设占用了。当导出时,设置 out 为高或者低,测量出来的电平都是3.0V。更换其它引脚后,SPI通信正常。

bmp280 spi驱动

除了要在 probe 中设置设备支持的SPI主机模式,还要注意,bmp280使用SPI时,写寄存器地址时,不支持地址自增,因此,写一个寄存器,需要重新设置寄存器地址。读寄存器数据时,支持寄存器地址自增。
另外,更重要的是,SPI模式下,bmp280的寄存器地址只支持7bit,最高位为读写标志位。当SPI读写调不通时,多半是没有注意到这个特殊要求。
在这里插入图片描述

static int bmp280_spi_write(struct spi_device *spi, spi_w_ctrl *wc, size_t len)
{
    struct spi_transfer xfer = {0};
    u8 *data = kzalloc(sizeof(u8), 2 * len);
    int i = 0;
    struct spi_message msg = {0};
    int ret = 0;

    for (i = 0; i < len; i++) {
        data[2 * i] = wc[i].reg & (~(1 << 7));
        data[2 * i + 1] = wc[i].val;
    }

    xfer.tx_buf = data;
    xfer.len = 2 * len;
    // xfer.cs_change = 1;

    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);

    ret = spi_sync(spi, &msg);

    kfree(data);
    return ret;
}

static int bmp280_spi_read(struct spi_device *spi, u8 reg, u8 *data, size_t len)
{
#if 0
    return spi_write_then_read(spi, &reg, 1, data, len);
#else
    struct spi_transfer xfer = {0};
    u8 *tx_buf = kzalloc(sizeof(u8), len);
    u8 *rx_buf = kzalloc(sizeof(u8), len);
    struct spi_message msg = {0};
    int ret = 0;

    tx_buf[0] = reg;
    xfer.tx_buf = tx_buf;
    xfer.rx_buf = rx_buf;
    xfer.len = len + 1;
    // xfer.cs_change = 1;

    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);

    ret = spi_sync(spi, &msg);

    memcpy(data, rx_buf + 1, len);

    kfree(tx_buf);
    kfree(rx_buf);
    return ret;
#endif
}

代码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/errno.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>

struct bmp280_compensation {
    unsigned short dig_T1;
    signed short dig_T2;
    signed short dig_T3;
    unsigned short dig_P1;
    signed short dig_P2;
    signed short dig_P3;
    signed short dig_P4;
    signed short dig_P5;
    signed short dig_P6;
    signed short dig_P7;
    signed short dig_P8;
    signed short dig_P9;

    s32 t_fine;
};

struct bmp280_dev {
    struct spi_device *spi;
    struct bmp280_compensation compensation;

    s32 adc_T;
    s32 adc_P;

    s32 temperature;
    u32 pressure;
};

typedef struct {
    u8 reg;
    u8 val;
} spi_w_ctrl;

typedef u32 BMP280_U32_t;
typedef s32 BMP280_S32_t;
typedef s64 BMP280_S64_t;

// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123”
// equals 51.23 DegC. t_fine carries fine temperature as global value
static BMP280_S32_t bmp280_compensate_T_int32(BMP280_S32_t adc_T,
                                       struct bmp280_compensation *com)
{
    BMP280_S32_t var1, var2, T;
    var1 = ((((adc_T >> 3) - ((BMP280_S32_t)com->dig_T1 << 1))) *
            ((BMP280_S32_t)com->dig_T2)) >>
           11;
    var2 = (((((adc_T >> 4) - ((BMP280_S32_t)com->dig_T1)) *
              ((adc_T >> 4) - ((BMP280_S32_t)com->dig_T1))) >>
             12) *
            ((BMP280_S32_t)com->dig_T3)) >>
           14;
    com->t_fine = var1 + var2;
    T = (com->t_fine * 5 + 128) >> 8;
    return T;
}

// Returns pressure in Pa as unsigned 32 bit integer. Output value of “96386”
// equals 96386 Pa = 963.86 hPa
static BMP280_U32_t bmp280_compensate_P_int32(BMP280_S32_t adc_P,
                                        struct bmp280_compensation *com) 
{
    BMP280_S32_t var1, var2;
    BMP280_U32_t p;
    var1 = (((BMP280_S32_t)com->t_fine) >> 1) - (BMP280_S32_t)64000;
    var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((BMP280_S32_t)com->dig_P6);
    var2 = var2 + ((var1 * ((BMP280_S32_t)com->dig_P5)) << 1);
    var2 = (var2 >> 2) + (((BMP280_S32_t)com->dig_P4) << 16);
    var1 = (((com->dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) +
            ((((BMP280_S32_t)com->dig_P2) * var1) >> 1)) >>
           18;
    var1 = ((((32768 + var1)) * ((BMP280_S32_t)com->dig_P1)) >> 15);
    if (var1 == 0) {
        return 0; // avoid exception caused by division by zero
    }
    p = (((BMP280_U32_t)(((BMP280_S32_t)1048576) - adc_P) - (var2 >> 12))) *
        3125;
    if (p < 0x80000000) {
        p = (p << 1) / ((BMP280_U32_t)var1);
    } else {
        p = (p / (BMP280_U32_t)var1) * 2;
    }
    var1 = (((BMP280_S32_t)com->dig_P9) *
            ((BMP280_S32_t)(((p >> 3) * (p >> 3)) >> 13))) >>
           12;
    var2 = (((BMP280_S32_t)(p >> 2)) * ((BMP280_S32_t)com->dig_P8)) >> 13;
    p = (BMP280_U32_t)((BMP280_S32_t)p + ((var1 + var2 + com->dig_P7) >> 4));
    return p;
}

static int bmp280_spi_read(struct spi_device *spi, u8 reg, u8 *data, size_t len)
{
#if 0
    return spi_write_then_read(spi, &reg, 1, data, len);
#else
    struct spi_transfer xfer = {0};
    u8 *tx_buf = kzalloc(sizeof(u8), len);
    u8 *rx_buf = kzalloc(sizeof(u8), len);
    struct spi_message msg = {0};
    int ret = 0;

    tx_buf[0] = reg;
    xfer.tx_buf = tx_buf;
    xfer.rx_buf = rx_buf;
    xfer.len = len + 1;
    // xfer.cs_change = 1;

    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);

    ret = spi_sync(spi, &msg);

    memcpy(data, rx_buf + 1, len);

    kfree(tx_buf);
    kfree(rx_buf);
    return ret;
#endif
}

static int bmp280_spi_write(struct spi_device *spi, spi_w_ctrl *wc, size_t len)
{
    struct spi_transfer xfer = {0};
    u8 *data = kzalloc(sizeof(u8), 2 * len);
    int i = 0;
    struct spi_message msg = {0};
    int ret = 0;

    for (i = 0; i < len; i++) {
        data[2 * i] = wc[i].reg & (~(1 << 7));
        data[2 * i + 1] = wc[i].val;
    }

    xfer.tx_buf = data;
    xfer.len = 2 * len;
    // xfer.cs_change = 1;

    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);

    ret = spi_sync(spi, &msg);

    kfree(data);
    return ret;
}

static int bmp280_compensation_load(struct bmp280_dev *bmp280)
{
    u8 data[24] = {0};
    struct spi_device *spi = bmp280->spi;
    u8 reg = 0x88;
    int ret = 0;
    struct bmp280_compensation *com = &bmp280->compensation;

    ret = bmp280_spi_read(spi, reg, data, sizeof(data));
    if (ret) {
        return ret;
    }

    com->dig_T1 = data[0] | (data[1] << 8);
    com->dig_T2 = data[2] | (data[3] << 8);
    com->dig_T3 = data[4] | (data[5] << 8);
    com->dig_P1 = data[6] | (data[7] << 8);
    com->dig_P2 = data[8] | (data[9] << 8);
    com->dig_P3 = data[10] | (data[11] << 8);
    com->dig_P4 = data[12] | (data[13] << 8);
    com->dig_P5 = data[14] | (data[15] << 8);
    com->dig_P6 = data[16] | (data[17] << 8);
    com->dig_P7 = data[18] | (data[19] << 8);
    com->dig_P8 = data[20] | (data[21] << 8);
    com->dig_P9 = data[22] | (data[23] << 8);

    dev_info(&bmp280->spi->dev,
            "dig_T1=0x%x\n"
            "dig_T2=0x%x\n"
            "dig_T3=0x%x\n"
            "dig_P1=0x%x\n"
            "dig_P2=0x%x\n"
            "dig_P3=0x%x\n"
            "dig_P4=0x%x\n"
            "dig_P5=0x%x\n"
            "dig_P6=0x%x\n"
            "dig_P7=0x%x\n"
            "dig_P8=0x%x\n"
            "dig_P9=0x%x\n",
            com->dig_T1, com->dig_T2, com->dig_T3,
            com->dig_P1, com->dig_P2, com->dig_P3,
            com->dig_P4, com->dig_P5, com->dig_P6,
            com->dig_P7, com->dig_P8, com->dig_P9);
    return 0;
}

static int bmp280_read_adc(struct bmp280_dev *bmp280)
{
    u8 data[6] = {0};
    int ret = 0;
    struct spi_device *spi = bmp280->spi;
    spi_w_ctrl w_ctrl = {
        .reg = 0xF4,
        .val = (0x07 << 5) | (0x07 << 2) | 0x01,
    };
    u8 reg = 0xF7;

    /* write config for sample, oversampling x1, force mode */
    bmp280_spi_write(spi, &w_ctrl, 1);

    /* measure time Max 6.4ms: T and P oversampling x1 */
    msleep(7);

    ret = bmp280_spi_read(spi, reg, data, sizeof(data));
    if (ret) {
        return ret;
    }

    bmp280->adc_P = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
    bmp280->adc_T = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);

    dev_info(&bmp280->spi->dev, "adc_P: 0x%x, adc_T: 0x%x\n", bmp280->adc_P,
            bmp280->adc_T);
    return 0;
}

static int bmp280_read_temp_hum_press(struct bmp280_dev *bmp280)
{
    int ret = 0;

    ret = bmp280_read_adc(bmp280);
    if (ret) {
        return ret;
    }

    bmp280->temperature = bmp280_compensate_T_int32(bmp280->adc_T,
                                                    &bmp280->compensation);
    bmp280->pressure = bmp280_compensate_P_int32(bmp280->adc_P,
                                                &bmp280->compensation);

    dev_info(&bmp280->spi->dev, "temperature: %d, pressure: %u\n",
            bmp280->temperature, bmp280->pressure);
    return 0;
}

static ssize_t bmp280_temperature_show(struct device *dev,
                                        struct device_attribute *attr,
                                        char *buf)
{
    struct spi_device *spi = container_of(dev, struct spi_device, dev);
    struct bmp280_dev *bmp280 = spi_get_drvdata(spi);

    if (IS_ERR(bmp280)) {
        dev_err(dev, "i2c get cleintdata error: 0x%lx\n", PTR_ERR(bmp280));
    }

    bmp280_read_temp_hum_press(bmp280);
    return sprintf(buf, "%d\n", bmp280->temperature);
}

static ssize_t bmp280_temperature_store(struct device *dev,
                                        struct device_attribute *attr,
                                        const char *buf, size_t count)
{
    return 0;
}
static DEVICE_ATTR(temperature, 0644, bmp280_temperature_show,
                    bmp280_temperature_store);

static ssize_t bmp280_pressure_show(struct device *dev,
                                    struct device_attribute *attr,
                                    char *buf)
{
    struct spi_device *spi = container_of(dev, struct spi_device, dev);
    struct bmp280_dev *bmp280 = spi_get_drvdata(spi);

    if (IS_ERR(bmp280)) {
        dev_err(dev, "i2c get cleintdata error: 0x%lx\n", PTR_ERR(bmp280));
    }

    bmp280_read_temp_hum_press(bmp280);
    return sprintf(buf, "%u\n", bmp280->pressure);
}

static ssize_t bmp280_pressure_store(struct device *dev,
                                        struct device_attribute *attr,
                                        const char *buf, size_t count)
{
    return 0;
}
static DEVICE_ATTR(pressure, 0644, bmp280_pressure_show, bmp280_pressure_store);

static int bmp280_init(struct bmp280_dev *bmp280)
{
    struct spi_device *spi = bmp280->spi;
    u8 id = 0;
    int ret = 0;
    u8 reg = 0xD0;
    spi_w_ctrl w_ctrl = {
        .reg = 0xE0,
        .val = 0xB6,
    };

    ret = bmp280_spi_read(spi, reg, &id, 1);
    if (ret < 0) {
        return -EREMOTEIO;
    }

    dev_info(&spi->dev, "Read BMP280 Device ID: 0x%02x\n", id);

    /* Reset chip */
    ret = bmp280_spi_write(spi, &w_ctrl, 1);
    if (ret) {
        return ret;
    }

    /* startup time: 2ms */
    msleep(2);

    ret = bmp280_compensation_load(bmp280);
    if (ret) {
        return ret;
    }

    return 0;
}

static int bmp280_probe(struct spi_device *spi)
{
    struct bmp280_dev *bmp280;
    int ret = 0;

    bmp280 = devm_kzalloc(&spi->dev, sizeof(*bmp280), GFP_KERNEL);
    if (IS_ERR(bmp280)) {
        dev_err(&spi->dev, "kzalloc fail: 0x%lx\n", PTR_ERR(bmp280));
        return PTR_ERR(bmp280);
    }

    spi_set_drvdata(spi, bmp280);
    bmp280->spi = spi;

    spi->bits_per_word = 8;
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 5000000;
    ret = spi_setup(spi);
    if (ret) {
        return -EIO;
    }

    if (bmp280_init(bmp280)) {
        dev_err(&spi->dev, "bmp280 init error\n");
        return -EIO;
    }

    device_create_file(&spi->dev, &dev_attr_temperature);
    device_create_file(&spi->dev, &dev_attr_pressure);

    dev_info(&spi->dev, "bmp280 probe success\n");
    return 0;
}

static int bmp280_remove(struct spi_device *spi)
{
    spi_set_drvdata(spi, NULL);
    device_remove_file(&spi->dev, &dev_attr_temperature);
    device_remove_file(&spi->dev, &dev_attr_pressure);

    dev_info(&spi->dev, "bmp280 remove");
    return 0;
}

static struct of_device_id bmp280_of_match[] = {
    {.compatible = "bosch,bmp280-spi"},
    { },
};
MODULE_DEVICE_TABLE(of, bmp280_of_match);

static struct spi_driver bmp280_driver = {
    .probe = bmp280_probe,
    .remove = bmp280_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "bmp280",
        .of_match_table = of_match_ptr(bmp280_of_match),
    },
};

module_spi_driver(bmp280_driver);

MODULE_AUTHOR("duapple <duapple2@gmail.com>");
MODULE_LICENSE("GPL");
### Java SPI机制的优势 Java SPI(Service Provider Interface)作为一种服务发现机制,具有显著的技术优势。它允许开发人员通过定义接口并动态加载其实现类来增强系统的灵活性和扩展能力[^1]。 #### 解耦合 SPI的核心理念之一是解耦。通过将服务的实现与调用分离,使得应用程序可以在运行时动态选择具体的服务提供者,而无需硬编码依赖于特定实现。这种设计模式特别适合模块化架构中的组件交互需求[^3]。 #### 动态扩展性 借助SPI机制,新的功能模块可以被轻松加入到现有系统中而不必修改原有代码。只要遵循既定接口规范注册相应的ServiceProvider即可完成新特性的集成工作[^2]。 ### 应用场景分析 以下是几个典型的应用领域: #### 数据库驱动管理 JDBC便是基于此原理构建而成——不同厂商提供的数据库连接器作为各自独立插件存在;当应用启动期间会自动扫描classpath下META-INF/services目录寻找匹配项进而初始化对应类型的Driver对象实例供客户端使用. #### 日志框架适配层 像SLF4J这样的通用日志门面库就运用到了类似的思路:本身并不负责实际记录操作而是交由背后真正干活的那个第三方工具(比如logback或者java.util.logging)去执行具体的任务; 用户只需简单配置一下环境变量就能无缝切换不同的底层引擎支持. ```java // 假设有一个LoggerFactory接口用于生产各种风格的日志处理器 public interface LoggerFactory { Logger getLogger(String name); } // 实际项目里可能有多个针对该API的不同版本实现方案可供挑选... @Provider class LogBackBasedLoggerFactory implements LoggerFactory { ... } ``` #### 图片处理SDK集合体 设想一款多功能图像编辑软件需要兼容多种文件格式解析转换等功能,则完全可以考虑采用类似的设计策略让每种新增加的支持都能以最小侵入度的形式融入进来: ```xml <!-- META-INF/services/com.example.ImageProcessor --> com.vendor.jpeg.JpegHandler com.anothercompany.bmp.BmpAdapter org.opensource.png.PngEngineImpl ``` 以上仅列举了一些常见的例子而已实际上凡是涉及到多形态共存又希望保持高度灵活可控局面的地方都可以尝试引入这套解决方案加以优化改进.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值