Linux驱动开发-①regmap②IIO子系统
Linux驱动开发-IIO驱动
- 一,regmap
- 二,IIO子系统
- 2.1初始化相关工作
- 2.2 通道
- 2.3 读实现
- over
一,regmap
对于spi和i2c,读写寄存器的框架不同,但设备本质一样,因此就有了regmap模型来对其进行简化,提供统一的接口函数来访问寄存器,而且使用很方便,使用时候,调用regmap的读写寄存器函数即可。
整体框架:
struct regmap *regmap;struct regmap_config regmap_config;/*对于spi的设备,之前的读写还需要对t->tx_buf,t->rx_buf等进行操作,再t放到message中,最后再利用spi_sync发送,比较麻烦*/static unsigned char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg){u8 ret;unsigned int data;ret = regmap_read(dev->regmap,reg,&data);//调用regmap的读函数return (u8)data;}void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data){regmap_write(dev->regmap,reg,data);//调用regmap的写函数}static int icm20608c_probe(struct spi_device *spi){int ret = 0;icm20608c.regmap_config.reg_bits = 8;//寄存器大小为8biticm20608c.regmap_config.val_bits = 8;//寄存器数据值8bit//icm20608c.regmap_config.read_flag_mask = 0x80;//读掩码icm20608c.regmap = regmap_init_spi(spi,&icm20608c.regmap_config);//关键就是这个 函 数,将regmap_config和spi设备关联起来,创建出regmapif(IS_ERR(icm20608c.regmap)){return PTR_ERR(icm20608c.regmap);}........}static int icm20608c_remove(struct spi_device *spi){.........regmap_exit(icm20608c.regmap);return 0;}
二,IIO子系统
Linux驱动喜欢把程序分层,并且一类的放到一起,IIO子系统就是针对ADC传感器,将比如加速度计,光传感器,气压计,磁力计等等设备,用一样的驱动模型。
结构体iio_dev来表示具体的IIO设备,整体的驱动框架,还是要看设备,比如spi设备,创建的就是spi_driver。主要包含三个内容:①在probe函数中,申请iio_dev内存,进行初始化,注册iio_dev。②iio_chan_spec对通道的建立,比如icm20608设备,有加速度xyz轴,陀螺仪xyz轴和温度,一共七个数据,因此要建立七个通道,在文件中显示,包含:加速度+x或y或z轴+什么数据,比如原始数据,量程等。③iio_info操作函数中,read_raw和write_raw函数具体的实现,具体怎么样实现,还是依靠regmap_read和regmap_write函数,对寄存器进行操作。
2.1初始化相关工作
注意,现在不建立全局变量了,之前会这样写:struct icm20608c_dev icm20608,从而在整个驱动中,直接调用icm20608。比如在remove函数中,想进行注销工作,先indio_dev = spi_get_drvdata(spi);获取indio_dev,再得到dev = iio_priv(indio_dev);,即原来没改过的icm20608地址。
初始化工作中,devm_iio_device_alloc(&spi->dev, sizeof(*dev)) 做了两件事①分配一个struct iio_dev结构的内存,②额外分配sizeof(*dev)大小的内存空间(这里是struct icm20608c_dev的大小。
iio_priv(indio_dev)是一个宏,它返回的是紧跟在iio_dev结构体后面的私有数据区域的指针,dev = iio_priv(indio_dev)获取的是紧跟在iio_dev后面的为驱动私有数据预留的内存区域的指针。
struct icm20608c_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex lock;
};
/*完成设备号注册 节点注册 spi设备初始化*/
static int icm20608c_probe(struct spi_device *spi)
{int ret = 0;struct icm20608c_dev *dev;struct iio_dev *indio_dev;/*1.iio_dev申请内存*/indio_dev = devm_iio_device_alloc(&spi->dev,sizeof(*dev));if(!indio_dev){return ret;}/*2.获取icm20608c_dev结构体地址*/dev = iio_priv(indio_dev);dev->spi = spi;spi_set_drvdata(spi,indio_dev);mutex_init(&dev->lock);/*3.初始化iio_dev成员变量*/indio_dev->dev.parent = &spi->dev;indio_dev->info = &icm20608c_info;indio_dev->name = ICMC20608C_NAME;indio_dev->modes = INDIO_DIRECT_MODE;indio_dev->channels = icm20608_channels;indio_dev->num_channels = ARRAY_SIZE(icm20608_channels);/*4.注册iio_dev*/ret = iio_device_register(indio_dev);if(ret<0){return ret;}/*5.初始化regmap_config并且注册*/dev->regmap_config.reg_bits = 8;//寄存器大小为8bitdev->regmap_config.val_bits = 8;//寄存器数据值8bit//dev.regmap_config.read_flag_mask = 0x80;//读掩码dev->regmap = regmap_init_spi(spi,&dev->regmap_config);if(IS_ERR(dev->regmap)){goto regmap_error;return PTR_ERR(dev->regmap);}/*6.初始化SPI设备*/spi->mode = SPI_MODE_0;//时钟模式spi_setup(spi);/*7.寄存器初始化内容*/icm20608c_reg_init(dev);printk("probe success \r\n");return 0;
regmap_error:iio_device_unregister(indio_dev); return ret;
}
static int icm20608c_remove(struct spi_device *spi)
{/*这段代码,为了获取icm20608c_dev的地址和iio_dev地址,以便进行解除*/struct icm20608c_dev *dev;struct iio_dev *indio_dev;indio_dev = spi_get_drvdata(spi);dev = iio_priv(indio_dev);/*删除regmap,注销iio_dev*/regmap_exit(dev->regmap);iio_device_unregister(indio_dev); printk("remove success \r\n");return 0;
}
2.2 通道
这个 ICM20608_CHAN(_type, _channel2, _index)宏,就相当于给带入里面的数进行赋值,创建出一个通道结构体,_type是表示各种数据,比如加速度IIO_ACCEL,当modified=1,channel2 为通道修饰符,表示对这个_type还有更具体的描述。 info_mask_shared_by_type 为相同type类型通道共有的(但是名字要一样,比如都是加速度类型,in_accel_sacle),info_mask_separate 为这个通道私有的(比如in_accel_x_raw),具体在文件中的顺序,依靠scan_index,为0时候,在最前面。
在文件中显示顺序:
#define ICM20608_CHAN(_type, _channel2, _index) \{ \.type = _type, \.modified = 1, \.channel2 = _channel2, \.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index = _index, \.scan_type = { \.sign = 's', \.realbits = 16, \.storagebits = 16, \.shift = 0, \.endianness = IIO_BE, \}, \}
enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP,
};
static const struct iio_chan_spec icm20608_channels[] = {{.type = IIO_TEMP,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)|BIT(IIO_CHAN_INFO_OFFSET)|BIT(IIO_CHAN_INFO_SCALE),.scan_index = INV_ICM20608_SCAN_TEMP,.scan_type = {.sign = 's',.realbits = 16,.storagebits = 16,.shift = 0,.endianness = IIO_BE,},},ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_X,INV_ICM20608_SCAN_GYRO_X),ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Y,INV_ICM20608_SCAN_GYRO_Y),ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Z,INV_ICM20608_SCAN_GYRO_Z),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ICM20608_SCAN_ACCL_X),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ICM20608_SCAN_ACCL_Y),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ICM20608_SCAN_ACCL_Z),
};
2.3 读实现
icm20608c_read_raw的返回值,决定读到的数据值,返回值IIO_VAL_INT为1,读到的数据按照整形返回,只有val,没有val2,返回值IIO_VAL_INT_PLUS_MICRO为2,按照val+val2/1000000反馈, 返回值IIO_VAL_INT_PLUS_NANO为2,按照val+val2/1000000000,返回值为负,读取错误。
对于IIO_CHAN_INFO_SCALE量程数据的读取,读到寄存器值为00 01 10 11,要进行转换成实际值,转换关系有数组gyro_scale_icm20608和accel_scale_icm20608构建,最后进行输出。
最终得到的真实数据,比如重力加速度=in_accel_z_raw(读取的值)×in_accel_scale(加速度量程)。
static const int gyro_scale_icm20608[] = {7629, 15258, 30517, 61035};
static const int accel_scale_icm20608[] = {61035, 122070, 244140, 488281};
static unsigned char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg)
{u8 ret;unsigned int data;ret = regmap_read(dev->regmap,reg,&data);return (u8)data;
}
void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data)
{regmap_write(dev->regmap,reg,data);
}
/*计算出要读寄存器的位置,根据带入的axis计算偏移地址+reg*/
static int icm20608_sensor_show(struct icm20608c_dev *dev, int reg,int axis,int *val)
{int ind, result;__be16 d;ind = (axis - IIO_MOD_X) * 2;result = regmap_bulk_read(dev->regmap, reg + ind, (u8 *)&d, 2);if (result)return -EINVAL;*val = (short)be16_to_cpup(&d);return IIO_VAL_INT;
}
static int icm20608c_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask)
{ int ret = 0;unsigned char regdata = 0;struct icm20608c_dev *dev = iio_priv(indio_dev);switch(mask){case IIO_CHAN_INFO_RAW://原始值mutex_lock(&dev->lock);switch(chan->type){case IIO_TEMP:ret = icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);//IIO_MOD_X意思是就从这个ICM20_TEMP_OUT_H开始break;case IIO_ACCEL:ret = icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan->channel2, val);break;case IIO_ANGL_VEL:ret = icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan->channel2, val);break;default:ret = -EINVAL;break;}mutex_unlock(&dev->lock);return ret;case IIO_CHAN_INFO_CALIBBIAS://校准值 switch(chan->type){ //对于加速度和陀螺仪的校准值,首先是校准值寄存器位置不同,其次是分为x,y,zcase IIO_ACCEL:mutex_lock(&dev->lock);ret = icm20608_sensor_show(dev, ICM20_XA_OFFSET_H, chan->channel2, val);mutex_unlock(&dev->lock); break;case IIO_ANGL_VEL:mutex_lock(&dev->lock);ret = icm20608_sensor_show(dev, ICM20_XG_OFFS_USRH, chan->channel2, val);mutex_unlock(&dev->lock); break;default:ret = -EINVAL;break; } break;case IIO_CHAN_INFO_SCALE://度量switch (chan->type) {case IIO_ANGL_VEL:mutex_lock(&dev->lock);regdata = (icm20608c_read_one_reg(dev, ICM20_GYRO_CONFIG) & 0X18) >> 3;*val = 0;*val2 = gyro_scale_icm20608[regdata];mutex_unlock(&dev->lock);return IIO_VAL_INT_PLUS_MICRO; /* 值为val+val2/1000000 */case IIO_ACCEL:mutex_lock(&dev->lock);regdata = (icm20608c_read_one_reg(dev, ICM20_ACCEL_CONFIG) & 0X18) >> 3;*val = 0;*val2 = accel_scale_icm20608[regdata];;mutex_unlock(&dev->lock);return IIO_VAL_INT_PLUS_NANO;/* 值为val+val2/1000000000 */case IIO_TEMP: *val = ICM20608_TEMP_SCALE/ 1000000;*val2 = ICM20608_TEMP_SCALE % 1000000;return IIO_VAL_INT_PLUS_MICRO; /* 值为val+val2/1000000 */default:return -EINVAL;}return ret;case IIO_CHAN_INFO_OFFSET://温度传感器offset值switch(chan->type){case IIO_TEMP:*val = ICM20608_TEMP_OFFSET; break;default:ret = -EINVAL; }return ret; default:break;}printk("icm20608c_read_raw\r\n");return ret;}
实现:
#include <linux/spi/spi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/regmap.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/unaligned/be_byteshift.h>
#include "666.h"
#define ICMC20608C_NAME "icm20608c"
#define ICM20608_TEMP_OFFSET 0
#define ICM20608_TEMP_SCALE 326800000struct icm20608c_dev {struct spi_device *spi;struct regmap *regmap;struct regmap_config regmap_config;struct mutex lock;
};
static const int gyro_scale_icm20608[] = {7629, 15258, 30517, 61035};
static const int accel_scale_icm20608[] = {61035, 122070, 244140, 488281};
#define ICM20608_CHAN(_type, _channel2, _index) \{ \.type = _type, \.modified = 1, \.channel2 = _channel2, \.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \BIT(IIO_CHAN_INFO_CALIBBIAS), \.scan_index = _index, \.scan_type = { \.sign = 's', \.realbits = 16, \.storagebits = 16, \.shift = 0, \.endianness = IIO_BE, \}, \}
enum inv_icm20608_scan {INV_ICM20608_SCAN_ACCL_X,INV_ICM20608_SCAN_ACCL_Y,INV_ICM20608_SCAN_ACCL_Z,INV_ICM20608_SCAN_TEMP,INV_ICM20608_SCAN_GYRO_X,INV_ICM20608_SCAN_GYRO_Y,INV_ICM20608_SCAN_GYRO_Z,INV_ICM20608_SCAN_TIMESTAMP,
};
static const struct iio_chan_spec icm20608_channels[] = {{.type = IIO_TEMP,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)|BIT(IIO_CHAN_INFO_OFFSET)|BIT(IIO_CHAN_INFO_SCALE),.scan_index = INV_ICM20608_SCAN_TEMP,.scan_type = {.sign = 's',.realbits = 16,.storagebits = 16,.shift = 0,.endianness = IIO_BE,},},ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_X,INV_ICM20608_SCAN_GYRO_X),ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Y,INV_ICM20608_SCAN_GYRO_Y),ICM20608_CHAN(IIO_ANGL_VEL,IIO_MOD_Z,INV_ICM20608_SCAN_GYRO_Z),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ICM20608_SCAN_ACCL_X),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ICM20608_SCAN_ACCL_Y),ICM20608_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ICM20608_SCAN_ACCL_Z),
};static unsigned char icm20608c_read_one_reg(struct icm20608c_dev *dev,u8 reg)
{u8 ret;unsigned int data;ret = regmap_read(dev->regmap,reg,&data);return (u8)data;
}
void icm20608c_write_one_reg66(struct icm20608c_dev *dev,u8 reg,unsigned char data)
{regmap_write(dev->regmap,reg,data);
}static int icm20608_sensor_show(struct icm20608c_dev *dev, int reg,int axis,int *val)
{int ind, result;__be16 d;ind = (axis - IIO_MOD_X) * 2;result = regmap_bulk_read(dev->regmap, reg + ind, (u8 *)&d, 2);if (result)return -EINVAL;*val = (short)be16_to_cpup(&d);return IIO_VAL_INT;
}
static int icm20608c_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask)
{ int ret = 0;unsigned char regdata = 0;struct icm20608c_dev *dev = iio_priv(indio_dev);switch(mask){case IIO_CHAN_INFO_RAW://原始值mutex_lock(&dev->lock);switch(chan->type){case IIO_TEMP:ret = icm20608_sensor_show(dev, ICM20_TEMP_OUT_H, IIO_MOD_X, val);//IIO_MOD_X意思是就从这个ICM20_TEMP_OUT_H开始break;case IIO_ACCEL:ret = icm20608_sensor_show(dev, ICM20_ACCEL_XOUT_H, chan->channel2, val);break;case IIO_ANGL_VEL:ret = icm20608_sensor_show(dev, ICM20_GYRO_XOUT_H, chan->channel2, val);break;default:ret = -EINVAL;break;}mutex_unlock(&dev->lock);return ret;case IIO_CHAN_INFO_CALIBBIAS://校准值 switch(chan->type){ //对于加速度和陀螺仪的校准值,首先是校准值寄存器位置不同,其次是分为x,y,zcase IIO_ACCEL:mutex_lock(&dev->lock);ret = icm20608_sensor_show(dev, ICM20_XA_OFFSET_H, chan->channel2, val);mutex_unlock(&dev->lock); break;case IIO_ANGL_VEL:mutex_lock(&dev->lock);ret = icm20608_sensor_show(dev, ICM20_XG_OFFS_USRH, chan->channel2, val);mutex_unlock(&dev->lock); break;default:ret = -EINVAL;break; } break;case IIO_CHAN_INFO_SCALE://度量switch (chan->type) {case IIO_ANGL_VEL:mutex_lock(&dev->lock);regdata = (icm20608c_read_one_reg(dev, ICM20_GYRO_CONFIG) & 0X18) >> 3;*val = 0;*val2 = gyro_scale_icm20608[regdata];mutex_unlock(&dev->lock);return IIO_VAL_INT_PLUS_MICRO; /* 值为val+val2/1000000 */case IIO_ACCEL:mutex_lock(&dev->lock);regdata = (icm20608c_read_one_reg(dev, ICM20_ACCEL_CONFIG) & 0X18) >> 3;*val = 0;*val2 = accel_scale_icm20608[regdata];;mutex_unlock(&dev->lock);return IIO_VAL_INT_PLUS_NANO;/* 值为val+val2/1000000000 */case IIO_TEMP: *val = ICM20608_TEMP_SCALE/ 1000000;*val2 = ICM20608_TEMP_SCALE % 1000000;return IIO_VAL_INT_PLUS_MICRO; /* 值为val+val2/1000000 */default:return -EINVAL;}return ret;case IIO_CHAN_INFO_OFFSET://温度传感器offset值switch(chan->type){case IIO_TEMP:*val = ICM20608_TEMP_OFFSET; break;default:ret = -EINVAL; }return ret; default:break;}printk("icm20608c_read_raw\r\n");return ret;}
static int icm20608c_write_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int val, int val2, long mask)
{int ret = 0;printk("icm20608c_write_raw\r\n");return ret;
}
static int icm20608c_write_raw_get_fmt(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,long mask)
{int ret = 0;return ret;
}/*文件操作函数*/
static struct iio_info icm20608c_info = {.read_raw = icm20608c_read_raw,.write_raw = icm20608c_write_raw,.write_raw_get_fmt = &icm20608c_write_raw_get_fmt,
};/*寄存器初始化*/
static void icm20608c_reg_init( struct icm20608c_dev *dev)
{u8 value = 0,ret = 0;icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_1,0x80);mdelay(50);icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_1,0x01);mdelay(50);ret = icm20608c_read_one_reg(dev, ICM20_PWR_MGMT_1);printk("ICM20_PWR_MGMT_1 = %X\r\n", ret);value = icm20608c_read_one_reg(dev, ICM20_WHO_AM_I);printk("ICM20608 ID = %X\r\n", value);icm20608c_write_one_reg66(dev, ICM20_SMPLRT_DIV, 0x00); icm20608c_write_one_reg66(dev, ICM20_GYRO_CONFIG, 0x18); icm20608c_write_one_reg66(dev, ICM20_ACCEL_CONFIG, 0x18); icm20608c_write_one_reg66(dev, ICM20_CONFIG, 0x04); icm20608c_write_one_reg66(dev, ICM20_ACCEL_CONFIG2, 0x04);icm20608c_write_one_reg66(dev, ICM20_PWR_MGMT_2, 0x00); icm20608c_write_one_reg66(dev, ICM20_LP_MODE_CFG, 0x00); icm20608c_write_one_reg66(dev, ICM20_FIFO_EN, 0x00);
};
const struct of_device_id of_icm20608c_table[]={{.compatible = "wyt,icm20608"},{}
};
const struct spi_device_id icm20608c_table[]={{"icm20608c",0},{ }
};
/*完成设备号注册 节点注册 spi设备初始化*/
static int icm20608c_probe(struct spi_device *spi)
{int ret = 0;struct icm20608c_dev *dev;struct iio_dev *indio_dev;/*1.iio_dev申请内存*/indio_dev = devm_iio_device_alloc(&spi->dev,sizeof(*dev));if(!indio_dev){return ret;}/*2.获取icm20608c_dev结构体地址*/dev = iio_priv(indio_dev);dev->spi = spi;spi_set_drvdata(spi,indio_dev);mutex_init(&dev->lock);/*3.初始化iio_dev成员变量*/indio_dev->dev.parent = &spi->dev;indio_dev->info = &icm20608c_info;indio_dev->name = ICMC20608C_NAME;indio_dev->modes = INDIO_DIRECT_MODE;indio_dev->channels = icm20608_channels;indio_dev->num_channels = ARRAY_SIZE(icm20608_channels);/*4.注册iio_dev*/ret = iio_device_register(indio_dev);if(ret<0){return ret;}/*5.初始化regmap_config并且注册*/dev->regmap_config.reg_bits = 8;//寄存器大小为8bitdev->regmap_config.val_bits = 8;//寄存器数据值8bit//dev.regmap_config.read_flag_mask = 0x80;//读掩码dev->regmap = regmap_init_spi(spi,&dev->regmap_config);if(IS_ERR(dev->regmap)){goto regmap_error;return PTR_ERR(dev->regmap);}/*6.初始化SPI设备*/spi->mode = SPI_MODE_0;//时钟模式spi_setup(spi);/*7.寄存器初始化内容*/icm20608c_reg_init(dev);printk("probe success \r\n");return 0;
regmap_error:iio_device_unregister(indio_dev); return ret;
}
static int icm20608c_remove(struct spi_device *spi)
{/*这段代码,为了获取icm20608c_dev的地址和iio_dev地址,以便进行解除*/struct icm20608c_dev *dev;struct iio_dev *indio_dev;indio_dev = spi_get_drvdata(spi);dev = iio_priv(indio_dev);/*删除regmap,注销iio_dev*/regmap_exit(dev->regmap);iio_device_unregister(indio_dev); printk("remove success \r\n");return 0;
}
static struct spi_driver icm20608c_driver={.probe = icm20608c_probe,.remove = icm20608c_remove,.driver = {.name = "icm20608c_spi_driver",.of_match_table = of_icm20608c_table,},.id_table = icm20608c_table,};
static int __init icm20608c_init(void)
{return spi_register_driver(&icm20608c_driver);
}
static void __exit icm20608c_exit(void)
{spi_unregister_driver(&icm20608c_driver);
}
/*驱动入口*/
module_init(icm20608c_init);
module_exit(icm20608c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WYT");
over
驱动部分总体上都结束了,基本上是字符设备的驱动编写,块设备写一个RAM的,网络设备简单看了看框架,还是要干点项目,后面从100节视频往后,就开始不想学了,后面的章节都是搭建一些环境和软件,也不用动手敲了,这两天把这个会议整完,把操作系统学完。五月开始,找点项目干干,感觉还是没学到什么东西啊!!!!
相关文章:
Linux驱动开发-①regmap②IIO子系统
Linux驱动开发-IIO驱动 一,regmap二,IIO子系统2.1初始化相关工作2.2 通道2.3 读实现 over 一,regmap 对于spi和i2c,读写寄存器的框架不同,但设备本质一样,因此就有了regmap模型来对其进行简化,提供统一的接…...
HTML5好看的水果蔬菜在线商城网站源码系列模板5
文章目录 1.设计来源1.1 主界面1.2 关于我们1.3 商品服务1.4 果蔬展示1.5 联系我们1.6 商品具体信息1.7 登录注册 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板,程序开发,在线开发,在线沟通 作者:xcLeigh 文章地址&#…...
L2-033 简单计算器满分笔记
本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示,计算器由两个堆栈组成,一个堆栈 S1 存放数字,另一个堆栈 S2 存放运算符。计算器的最下方有一个等号键,每次按下这个键,计算器就…...
其他网页正常进入,但是CSDN进入之后排版混乱
显示不正常,排版混乱 解决方法: ①打开网络设置 ②更改适配器 ③所连接的网络 --右键 属性 然后就可以正常访问了。...
BFC详解
1.定义: FC的全称为Formatting Conttext,元素在标准流里面 块级元素的布局属于Block Formatting Context(BFC)——即block level box都是BFC中布局 行内级元素的布局属于Inline Formatting Context (IFC) 2.那么在哪些情况下会创建BFC? 根元素…...
(H3C)vlan配置实验
1.实验拓扑 2.实验配置 [S1]dis cu #version 7.1.070, Alpha 7170 #sysname S1 # vlan 10 # vlan 20 # interface GigabitEthernet1/0/1port link-mode bridgeport link-type trunkport trunk permit vlan 1 10 20combo enable fiber # interface GigabitEthernet1/0/2port li…...
idea mvn执行打包命令后控制台乱码
首先在idea中查看maven的编码方式 执行mvn -v命令 查看编码语言是GBK C:\Users\13488>mvn -v Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: D:\maven\apache-maven-3.6.3\bin\.. Java version: 1.8.0_202, vendor: Oracle Corporation, runt…...
JSON.parse(JSON.stringify()) 与 lodash 的 cloneDeep:深度拷贝的比较与基础知识
JSON.parse(JSON.stringify()) 与 lodash 的 cloneDeep:深度拷贝的比较与基础知识 在 JavaScript 开发中,**深拷贝(Deep Copy)**是一个常见需求,尤其是在处理复杂对象和嵌套数据结构时。JSON.parse(JSON.stringify(o…...
搭建用友U9Cloud ERP及UAP IDE环境
应用环境 Microsoft Windows 10.0.19045.5487 x64 专业工作站版 22H2Internet Information Services - 10.0.19041.4522Microsoft SQL Server 2019 - 15.0.2130.3 (X64)Microsoft SQL Server Reporing Services 2019 - 15.0.9218.715SQL Server Management Studio -18.6 laster…...
Linux 系统新磁盘分区XFS挂载
以下是Linux系统中对新硬盘进行XFS文件系统格式化和挂载的完整操作指南: 一、确认硬盘识别 查看已识别硬盘 执行 lsblk 或 fdisk -l 命令,确认新硬盘设备标识(如 /dev/sdb)。 二、硬盘分区(可选) …...
Oracle测试题目及笔记(单选)
所有题目来自于互联网搜索 当 Oracle 服务器启动时,下列哪种文件不是必须的(D)。 A.数据文件 B.控制文件 C.日志文件 D.归档日志文件 数据文件、日志文件-在数据库的打开阶段使用 控制文件-在数…...
C语言链接数据库
目录 使用 yum 配置 mysqld 环境 查看 mysqld 服务的版本 创建 mysql 句柄 链接数据库 使用数据库 增加数据 修改数据 查询数据 获取查询结果的行数 获取查询结果的列数 获取查询结果的列名 获取查询结果所有数据 断开链接 C语言访问mysql数据库整体源码 通过…...
深入浅出 Redis:核心数据结构解析与应用场景Redis 数据结构
引言:Redis 为何如此之快?数据结构是关键 Redis (Remote Dictionary Server) 作为一款高性能的内存键值数据库,凭借其闪电般的速度和丰富的功能,在缓存、消息队列、排行榜等众多场景中得到了广泛应用。除了基于内存存储这一核心优…...
告别昂贵语音合成服务!用GPT-SoVITS生成你的个性化AI语音
文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 今天给大家介绍一款AI语音克隆工具——GPT-SoVITS。这款由花儿不哭大佬开发的工具是一款强大的训练声音模型与音频生成工具…...
前沿要塞:Vue组件安全工程的防御体系重构与技术突围
总章数字世界的钢铁长城 在某个凌晨3点的红蓝对抗演练中,某电商平台因组件级XSS漏洞导致千万级用户数据泄露。这不是虚构的灾难场景,而是2023年某A轮企业的真实遭遇。当传统安全方案在新型攻击面前节节败退时,我们需要为Vue组件铸造全新的数字…...
吴恩达深度学习复盘(19)XGBoost简介|神经网络与决策树
XGBoost 多年来,机器学习研究人员提出了许多构建决策树的方法,目前最常用的方法是对样本或决策树的实现收费。其中,XGBoost 是一种非常快速且易于使用的开源实现,已成功用于赢得许多机器学习竞赛和商业应用。 算法原理 基本思想…...
Docker部署禅道21.6开源版本
将数据库相关环境变量分开,增加注释或空格使得命令更易读。 如果你的 MySQL 主机、端口等配置没有变化,应该确保这些信息是安全的,并考虑使用 Docker secrets 或环境变量配置来避免直接暴露敏感信息。 docker run -d -it --privilegedtrue …...
《MySQL:MySQL表结构的基本操作》
创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名 datatype 表示列的类型 character set 字符集,如果没有指定字符集,则以所在数据…...
C++解析操作mat文件方法-基于vs2019
前言 工作中需要将C#脚本转为C++,所转脚本主要功能是进行mat数据文件的解析和矩阵运算。 1.C#版本 原C#脚本主要是基于 MathNet.Numerics.data.Matlab、MathNet.Numerics.LineAlgebra.Double、 MathNet.Numerics.LineAlgebra 中的MatlabReader、DenseMatrix、Matrix进行mat文…...
OpenCV 模板匹配方法详解
文章目录 1. 什么是模板匹配?2. 模板匹配的原理2.1数学表达 3. OpenCV 实现模板匹配3.1基本步骤 4. 模板匹配的局限性5. 总结 1. 什么是模板匹配? 模板匹配(Template Matching)是计算机视觉中的一种基础技术,用于在目…...
自已实现一个远程打印方案 解决小程序或APP在外面控制本地电脑打印实现
常规通过小程序或APP在外出时控制本地电脑实现打印功能,可以结合远程桌面技术、云打印服务或开发定制化的远程打印解决方案。 但这里我们采用自已的实现方案来解决 服务器端实现 搭建一个后端socket服务,监听来自手机的打印请求。监听到打印任务后向本…...
Oracle_00000
contents 基本使用 基本使用 Oracle安装后会自动创建sys和system这两个用户。 sys用户:具有最高权限。具有sysdba角色,有create database的权限。该用户默认密码是:manager system用户:管理员用户,具有sysoper角色。没…...
深入剖析 ORM:原理、优缺点、场景及多语言框架示例
ORM 即对象关系映射(Object Relational Mapping),它是一种编程技术,其作用是在面向对象编程语言里,把对象模型和关系型数据库的数据结构之间创建起映射,这样开发者就能够使用面向对象的方式来操作数据库&am…...
ARINC818协议-持续
一、帧头帧尾 SOF 和 EOF 分别代表视频帧传输的开始与结束,它们在封装过程有多种状态,SOF 分为 SOFi 和 SOFn,EOF 分为 EOFt 和 EOFn。传输系统中的视频信息包括像素数据信 息和辅助数据信息,分别存储在有效数据中的对象 0 和对象…...
【uniapp】uni.setClipboardData 方法失效 bug 解决方案
写了一个 copy 方法,但是怎么也没有弹窗复制成功 <text click"toCopy(myInfo.id)">复制 </text> 逐步打印发现 1 正常打印,2 没有打印,说明问题出现在 setClipboardData 方法执行中 toCopy(n) {// console.log(1,ty…...
智能sc一面
智能sc一面-2025/4/17 更多完善:真实面经 Java 的异常分类 异常分为两类,一类Error,一类Execption。这两个类都是Throwable的子类,只有继承Throwable 的类才可以被throw或者catch Error: 表示严重的系统问题,通常与代码无关&am…...
SAP HANA使用命令行快速导出导入
楔子 今天折腾了接近一下午,就为了使用SAP HANA自带的命令行工具来导出数据备份。 SAP HANA(后续简称Hana)是内存数据库,性能这一方面上还真没怕过谁。 由于SAP HANA提供了Hana Studio这个桌面工具来方便运维和DBA使用…...
Oracle DBMS_SCHEDULER 与 DBMS_JOB 的对比
Oracle DBMS_SCHEDULER 与 DBMS_JOB 的对比 一 基本概述对比 特性DBMS_JOB (旧版)DBMS_SCHEDULER (新版)引入版本Oracle 7 (1992年)Oracle 10g R1 (2003年)当前状态已过时但仍支持推荐使用的标准设计目的基础作业调度企业级作业调度系统 二 功能特性对比 2.1 作业定义能力 …...
【音视频】音视频FLV合成实战
FFmpeg合成流程 示例本程序会⽣成⼀个合成的⾳频和视频流,并将它们编码和封装输出到输出⽂件,输出格式是根据⽂件扩展名⾃动猜测的。 示例的流程图如下所示。 ffmpeg 的 Mux 主要分为 三步操作: avformat_write_header : 写⽂件…...
10.(vue3.x+vite)div实现tooltip功能(css实现)
1:效果截图 2:代码实现 <template><div><div class="tooltip" style="margin-top: 20%; margin-left: 20%; background-color: blueviolet; color: white;...
代码随想录算法训练营第三十七天| 52. 携带研究材料 518.零钱兑换II 377. 组合总和 Ⅳ 70. 爬楼梯(进阶版)
[TOC](代码随想录算法训练营第三十七天| 52. 携带研究材料 518.零钱兑换II 377. 组合总和 Ⅳ 70. 爬楼梯(进阶版) ) 入营第三十七天 难度:难 计划任务 完成任务 52. 携带研究材料 动态规划五部曲: 1.确定dp数组以及下标含义 dp[i][j]表示从下标[0-i]的…...
数智化招标采购系统分类及功能亮点
数智化招标采购系统是郑州信源公司运用“互联网”、大数据、人工智能、区块链、物联网等新兴技术,结合供应链管理理念,以招标采购为核心,提供交易、管理、数据、服务、监管为一体的高标准采购管理平台,赋能政企用户实现采购业务全…...
CSS appearance 属性:掌握UI元素的原生外观
在现代网页设计中,为了达到一致的用户体验,我们有时需要让HTML元素模仿操作系统的默认控件样式。CSS中的appearance属性提供了一种简便的方式来控制这些元素是否以及如何显示其默认外观。本文将详细介绍appearance属性,并通过实际代码示例来展…...
【JavaScript】二十四、JS的执行机制事件循环 + location + navigator + history
文章目录 1、JS执行机制(事件循环eventloop)2、BOM的window3、location对象3.1 href属性3.2 search属性3.3 hash属性3.4 reload方法 4、navigator对象5、history对象 1、JS执行机制(事件循环eventloop) 以下,两段代码…...
CSS核心笔记002
margin塌陷问题 第一个子元素的上margin会作用在父元素上, 最后一个子元素的下margin会作用在父元素上解决 1. 给父元素设置 不为0的pandding 2. 给父元素设置宽度不为0 的border 3. 给父元素设置样式 overflow:hiddenmargin合并问题 兄弟元素的下外margin和会下面兄弟的上…...
前端路由缓存实现
场景:以一体化为例:目前页面涉及页签和大量菜单路由,用户想要实现页面缓存,即列表页、详情页甚至是编辑弹框页都要实现数据缓存。 方案:使用router-view的keep-alive实现 。 一、实现思路 1.需求梳理 需要缓存模块&…...
加密软件的发展:从古典密码到量子安全
在数字化时代,信息安全已成为个人隐私、商业机密乃至国家安全的重要基石。加密软件作为保护信息安全的核心工具,经历了从简单替换到复杂算法的漫长演变过程。本文将系统梳理加密软件的发展历程,分析当前主流技术,并展望未来趋势。…...
用Zotero + Word 宏,一键插入带超链接的参考文献!
第一步:准备好Zotero Word 确认你已经完成以下准备: ✅ 已安装好 Zotero ✅ 已安装好 Zotero Word 插件(一般自动装好了) ✅ Word 可以正常插入参考文献 ✅ 已插入好一组参考文献(可以先插几个测试) …...
Java SpringBoot的自定义配置
一,一个类多个属性的情况 application.properties配置文件写法 my.config.ip127.0.0.1 my.config.port8080自定义配置类:MyTestConfig import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties…...
Spring Boot 动态热更新 HTTPS 证书的实现与原理
在实际生产环境中,HTTPS 证书定期更新是非常常见的需求。传统方式通常要求重启服务来加载新证书,但在一些高可用系统中,重启服务会造成连接中断和短暂不可用。本篇文章将介绍如何在 Spring Boot 项目中,实现 不重启服务的情况下热…...
天工股份业绩大起大落:2024营收双位数下滑,稳定性或存疑
《港湾商业观察》施子夫 近期,江苏天工科技股份有限公司(以下简称,“天工股份”)拟公开发行股票并在北京证券交易所上市获得中国证监会同意注册,完成上市审核阶段中最后也是最关键的一步。 值得一提的是,…...
深入浅出 NVIDIA CUDA 架构与并行计算技术
🐇明明跟你说过:个人主页 🏅个人专栏:《深度探秘:AI界的007》 🏅 🔖行路有良友,便是天堂🔖 目录 一、引言 1、CUDA为何重要:并行计算的时代 2、NVIDIA在…...
网安融合:打造网络+安全一体化的超预期体验
近日,2025锐捷网络EBG(中国)核心伙伴大会在苏州圆满落幕。来自全国2000合作伙伴齐聚苏州,共同见证这场盛会的举办。会上,锐捷网络发布了七大战略产品解决方案。其中网络安全产品事业部产品市场总监沈世海发布了“打造网络安全一体化的超预期体验”的主题报告。报告围绕让“让渠…...
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能…… 那么有哪些现成的 Rules 可以抄作业呢,今天我们官方输出了 Java、TypeScript、Python、Go、JavaScript 等语…...
3D人脸扫描技术如何让真人“进入“虚拟,虚拟数字人反向“激活“现实?
随着虚拟人技术的飞速发展,超写实数字人已经成为数字娱乐、广告营销和虚拟互动领域的核心趋势。无论是企业家、知名主持人还是明星,数字分身正在以高度还原的形象替代真人参与各类活动,甚至成为品牌代言、直播互动的新宠。 3D人脸扫描&#…...
12孔AG调陶笛音域全解析:从E4到C6的演奏艺术
一、音域范围的精准界定 12孔AG调陶笛的音域范围为E4(低音Mi)至C6(高音Do),横跨13个自然音级(即E4-F4-G4-A4-B4-C5-D5-E5-F5-G5-A5-B5-C6)。若以半音计算,实际覆盖15个半音…...
IDEA编译错误Refer to the generated Javadoc files in xxx apidocs dir
文章目录 一、IDEA编译报错 Refer to the generated Javadoc1.1、报错内容1.2、解决办法 一、IDEA编译报错 Refer to the generated Javadoc 1.1、报错内容 Command line was: /opt/jdk1.8.0_181/jre/../bin/javadoc options packagesRefer to the generated Javadoc files i…...
高效培训,借助课程编辑器塑造卓越团队
(一)打造沉浸式培训体验 在企业人才培养体系里,培训是提升员工能力素质的重要手段,课程编辑器中的 VR 技术为企业培训带来新体验。以机械制造企业为例,以往员工培训靠书面资料、平面图片或简单视频讲解复杂机械设备结…...
Pikachu靶场-CSRF
CSRF (跨站请求伪造) 详细介绍与技术分析 一、什么是 CSRF? CSRF(Cross-Site Request Forgery,跨站请求伪造),是一种利用已认证用户的身份,诱使该用户执行恶意操作的攻击手段。攻击者通过伪造一个用户请求,…...
Flask(3): 在Linux系统上部署项目
1 前言 说实话,我并不想接触linux系统,要记住太多的命令。我更习惯windows系统,鼠标点点,只要记住少量的命令就可以了。 但是我选择了python,就注定无法逃避linux系统。虽然python也能在windows上很好的运行࿰…...