I2C设备注册的4种方法

2023-05-16

文章目录

  • 前言
  • 一、静态注册
  • 二、动态注册
  • 三、用户空间注册
  • 四、i2c驱动扫描注册


前言

I2C设备的4种添加方法:
1)静态注册
2)动态注册
3)用户空间注册
4)i2c驱动扫描注册


一、静态注册

静态注册就是在架构板级文件或初始化文件中添加i2c设备信息,并注册到特定位置(__i2c_board_list链表)上就可以了,如arm架构下board-xxx-yyy.c文件,x86架构下xxx-yyy-init-zzz.c文件。当系统静态注册i2c控制器(adapter)时,将会去查找这个链表,并实例化i2c设备添加到i2c总线上。注意:一定要赶在i2c控制器注册前将i2c设备信息添加到链表上。

1)定义一个 i2c_board_info 结构体

必须要有名字和设备地址,其他如中断号、私有数据非必须。

static struct i2c_board_info my_tmp75_info = {
    I2C_BOARD_INFO("my_tmp75", 0x48),
};
@my_tmp75是设备名字,用于匹配i2c驱动。
@0x48是i2c设备的基地址。

如果有多个设备,可以定义成结构数组,一次添加多个设备信息。

2)注册设备

使用 i2c_register_board_info 函数将i2c设备信息添加到特定链表,函数原型如下

i2c_register_board_info(int busnum, struct i2c_board_info const * info, unsigned n)
{
    devinfo->busnum = busnum; /* 组装i2c总线 */
    devinfo->board_info = *info; /* 绑定设备信息 */
    list_add_tail(&devinfo->list, &__i2c_board_list); /* 将设备信息添加进链表中 */
}
@busnum:哪一条总线,也就是选择哪一个i2c控制器(adapter)
@info:i2c设备信息,就是上面的结构体
@n:info中有几个设备

将在 i2c_register_adapter 函数中使用到

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    …
    if (adap->nr < __i2c_first_dynamic_bus_num)
        i2c_scan_static_board_info(adap);
    …
}
 
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
    struct i2c_devinfo        *devinfo;
 
    down_read(&__i2c_board_lock);
    list_for_each_entry(devinfo, &__i2c_board_list, list) {
        if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
            dev_err(&adapter->dev,"Can't create device at 0x%02x\n",devinfo->board_info.addr);
    }
    up_read(&__i2c_board_lock);
}

而调用 i2c_register_adapter 函数的有两个地方,分别是 i2c_add_adapter 函数和i2c_add_numbered_adapter 函数,但 i2c_add_adapter 函数中是动态分配的总线号,adap->nr一定比__i2c_first_dynamic_bus_num变量大,因此不会进入到i2c_scan_static_board_info函数,所以只有i2c_add_numbered_adapter 最终使用到,而这个函数是i2c控制器静态注册时调用的,因此静态注册i2c设备必须赶在i2c控制器注册前添加。


二、动态注册

动态注册i2c设备可以使用两个函数,分别为 i2c_new_device 函数与 i2c_new_probed_device 函数,它们两区别是:

  • i2c_new_device:不管i2c设备是否真的存在,都实例化 i2c_client。
  • i2c_new_probed_device:调用probe函数去探测i2c地址是否有回应,存在则实例化i2c_client。如果自己不提供probe函数的话,使用默认的i2c_default_probe函数。

1)使用 i2c_new_device 注册设备

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
 
static struct i2c_board_info my_tmp75_info = {
    I2C_BOARD_INFO("my_tmp75", 0x48),//这个名字很重要,用于匹配 I2C 驱动
};
 
static struct i2c_client *my_tmp75_client;
 
static int my_tmp75_init(void)
{
    struct i2c_adapter *i2c_adapt;
    int ret = 0;
 
    i2c_adapt = i2c_get_adapter(6);
    if (i2c_adapt == NULL)
    {
        printk("get adapter fail!\n");
        ret = -ENODEV;
    }
 
    my_tmp75_client = i2c_new_device(i2c_adapt, &my_tmp75_info);
    if (my_tmp75_client == NULL)
    {
        printk("i2c new fail!\n");
        ret = -ENODEV;
    }
 
    i2c_put_adapter(i2c_adapt);
 
    return ret;
}
 
static void my_tmp75_exit(void)
{
    i2c_unregister_device(my_tmp75_client);
}
 
module_init(my_tmp75_init);
module_exit(my_tmp75_exit);
 
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This my i2c device for tmp75");

2)使用 i2c_new_probed_device 注册设备

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
 
static struct i2c_client *my_tmp75_client;
 
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须以I2C_CLIENT_END宏结尾
 
static int my_i2c_dev_init(void)
{
    struct i2c_adapter *i2c_adap;
    struct i2c_board_info my_i2c_dev_info;
 
    memset(&my_i2c_dev_info, 0, sizeof(struct i2c_board_info));   
    strlcpy(my_i2c_dev_info.type, "my_tmp75", I2C_NAME_SIZE);
 
    i2c_adap = i2c_get_adapter(0);
    my_tmp75_client = i2c_new_probed_device(i2c_adap, &my_i2c_dev_info, addr_list, NULL);//只会匹配到 0x48 地址
    i2c_put_adapter(i2c_adap);
 
    if (my_tmp75_client)
        return 0;
    else
        return -ENODEV;
}
 
 
 
static void my_i2c_dev_exit(void)
{
    i2c_unregister_device(my_tmp75_client);
}
 
module_init(my_i2c_dev_init);
module_exit(my_i2c_dev_exit);
 
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This my i2c device for tmp75");


三、用户空间注册

1)创建 i2c 设备

echo i2c_test 0x48 > /sys/bus/i2c/devices/i2c-6/new_device

使用这种方法创建的 i2c 设备会挂在 i2c_adapter 的链表上,为了方便用户空间删除 i2c 设备!

2)删除设备

echo 0x48 > /sys/bus/i2c/devices/i2c-6/delete_device

删除设备只能删除在用户空间创建的 i2c 设备!

在 i2c 控制器注册时,会在/sys/bus/i2c/devices/目录下创建 i2c-x 设备文件,并且设置它的属性,而 new_device 和 delete_device 均是它的属性。

写new_device时会调用i2c_sysfs_new_device 函数,内部再调用 i2c_new_device 函数。

写delete_device时会调用i2c_sysfs_delete_device函数,内部再调用i2c_unregister_device函数。

在这里插入图片描述

/*
 * Let users instantiate I2C devices through sysfs. This can be used when
 * platform initialization code doesn't contain the proper data for
 * whatever reason. Also useful for drivers that do device detection and
 * detection fails, either because the device uses an unexpected address,
 * or this is a compatible device with different ID register values.
 *
 * Parameter checking may look overzealous, but we really don't want
 * the user to provide incorrect parameters.
 */
/*
 让用户通过 sysfs 实例化 I2C 设备。 当平台初始化代码由于某种原因不包含正确
 的数据时,可以使用它。 对于进行设备检测和检测失败的驱动程序也很有用,因为
 设备使用了意外的地址,或者这是具有不同 ID 寄存器值的兼容设备。
  *
 参数检查可能看起来过于热心,但我们真的不希望用户提供不正确的参数。
  */
static ssize_t
i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t count)
{
	struct i2c_adapter *adap = to_i2c_adapter(dev);
	struct i2c_board_info info;
	struct i2c_client *client;
	char *blank, end;
	int res;

	memset(&info, 0, sizeof(struct i2c_board_info));

	blank = strchr(buf, ' ');
	if (!blank) {
		dev_err(dev, "%s: Missing parameters\n", "new_device");
		return -EINVAL;
	}
	if (blank - buf > I2C_NAME_SIZE - 1) {
		dev_err(dev, "%s: Invalid device name\n", "new_device");
		return -EINVAL;
	}
	memcpy(info.type, buf, blank - buf);

	/* Parse remaining parameters, reject extra parameters */
	res = sscanf(++blank, "%hi%c", &info.addr, &end);
	if (res < 1) {
		dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
		return -EINVAL;
	}
	if (res > 1  && end != '\n') {
		dev_err(dev, "%s: Extra parameters\n", "new_device");
		return -EINVAL;
	}

	client = i2c_new_device(adap, &info);
	if (!client)
		return -EINVAL;

	/* Keep track of the added device */
	mutex_lock(&adap->userspace_clients_lock);
	list_add_tail(&client->detected, &adap->userspace_clients);
	mutex_unlock(&adap->userspace_clients_lock);
	dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
		 info.type, info.addr);

	return count;
}

/*
 * And of course let the users delete the devices they instantiated, if
 * they got it wrong. This interface can only be used to delete devices
 * instantiated by i2c_sysfs_new_device above. This guarantees that we
 * don't delete devices to which some kernel code still has references.
 *
 * Parameter checking may look overzealous, but we really don't want
 * the user to delete the wrong device.
 */
/*
* 当然,如果用户弄错了,让他们删除他们实例化的设备。 该接口只能用于删除
上面 i2c_sysfs_new_device 实例化的设备。 这保证了我们不会删除某些内核
代码仍然引用的设备。
*
参数检查可能看起来过于热心,但我们真的不希望用户删除错误的设备。
*/
static ssize_t
i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t count)
{
	struct i2c_adapter *adap = to_i2c_adapter(dev);
	struct i2c_client *client, *next;
	unsigned short addr;
	char end;
	int res;

	/* Parse parameters, reject extra parameters */
	res = sscanf(buf, "%hi%c", &addr, &end);
	if (res < 1) {
		dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
		return -EINVAL;
	}
	if (res > 1  && end != '\n') {
		dev_err(dev, "%s: Extra parameters\n", "delete_device");
		return -EINVAL;
	}

	/* Make sure the device was added through sysfs */
	res = -ENOENT;
	mutex_lock(&adap->userspace_clients_lock);
	list_for_each_entry_safe(client, next, &adap->userspace_clients,
				 detected) {
		if (client->addr == addr) {
			dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
				 "delete_device", client->name, client->addr);

			list_del(&client->detected);
			i2c_unregister_device(client);
			res = count;
			break;
		}
	}
	mutex_unlock(&adap->userspace_clients_lock);

	if (res < 0)
		dev_err(dev, "%s: Can't find device in list\n",
			"delete_device");
	return res;
}


四、i2c驱动扫描注册

在《i2c设备与驱动匹配过程》中说到,i2c 驱动注册时会使用两种匹配方法去寻找i2c设备,代码如下:

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.pm		= &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
    driver->driver.bus = &i2c_bus_type;//添加总线
 
    res = driver_register(&driver->driver);//驱动注册核心函数,注意只传入了driver成员
 
    /* 遍历所有挂在总线上的iic适配器,用它们去探测driver中指定的iic设备地址列表 */
    i2c_for_each_dev(driver, __process_new_driver);
}

driver_register 函数已将讲解过,现在来分析 i2c_for_each_dev 函数,

int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
    int res;
    mutex_lock(&core_lock);
    res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
    mutex_unlock(&core_lock);
    return res;
}
 
int bus_for_each_dev(struct bus_type *bus, struct device *start,
     void *data, int (*fn)(struct device *, void *))
{
    struct klist_iter i;
    struct device *dev;
    int error = 0;
    if (!bus || !bus->p)
        return -EINVAL;
 
    klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL));
 
    while (!error && (dev = next_device(&i)))
        error = fn(dev, data);
    klist_iter_exit(&i);
    return error;
}

最终调用 __process_new_driver 函数,使用 i2c 总线上所有 i2c 适配器去探测i2c驱动中的设备地址数组!

static struct device_type i2c_client_type = {
	.groups		= i2c_dev_attr_groups,
	.uevent		= i2c_device_uevent,
	.release	= i2c_client_dev_release,
};
struct device_type i2c_adapter_type = {
	.groups		= i2c_adapter_attr_groups,
	.release	= i2c_adapter_dev_release,
};
static int __process_new_driver(struct device *dev, void *data)
{
    if (dev->type != &i2c_adapter_type)
        return 0;
    return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

入口先判断传入的设备是不是i2c适配器(i2c控制器),因为在《i2c设备与驱动匹配过程》中说到,i2c 适配器和 i2c 设备一样,都会挂在 i2c 总线上,它们是通过 dev->type 项区分的。

static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
{
    /* Detect supported devices on that bus, and instantiate them */
    i2c_detect(adap, driver);
    …
}

最终调用i2c_detect函数,函数简化后如下:

static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
{
    int adap_id = i2c_adapter_id(adapter);
 
    address_list = driver->address_list;
    if (!driver->detect || !address_list)
        return 0;
    
    temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
    itemp_client->adapter = adapter;
 
    for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1)
    {
        temp_client->addr = address_list[i];
        err = i2c_detect_address(temp_client, driver);
        if (unlikely(err))
            break;
    }
}

如果 i2c 驱动的设备地址数组为空或 detect 函数不存在,则结束返回,否则临时实例化一个 temp_client 设备,赋值 adapter 为当前 i2c 控制器,然后在使用该 i2c 控制器去探测 i2c 驱动设备地址数组中的所有地址,关键函数是 i2c_detect_address 如下(简化后):

static int i2c_detect_address(struct i2c_client *temp_client, struct i2c_driver *driver)
{
    struct i2c_board_info info;
    struct i2c_adapter *adapter = temp_client->adapter;
    int addr = temp_client->addr;
    int err;
 
    err = i2c_check_7bit_addr_validity_strict(addr);//检查地址是否有效,即7位有效地址
    if (err) {
        return err;
    }
 
    if (i2c_check_addr_busy(adapter, addr))//跳过已经使用的i2c设备
        return 0;
 
    if (!i2c_default_probe(adapter, addr))//检查这个地址是否有回应
        return 0;
 
    memset(&info, 0, sizeof(struct i2c_board_info));
    info.addr = addr;
    err = driver->detect(temp_client, &info);
    if (err) {
        return err == -ENODEV ? 0 : err;
    }
 
    if (info.type[0] == '\0')
    {
    }
    else
    {
        struct i2c_client *client;
        client = i2c_new_device(adapter, &info);
        if (client)
            list_add_tail(&client->detected, &driver->clients);
    }
}

首先检查有效性、是否有设备回应、是否被使用,之后初始化了i2c_board_info 结构,注意只初始化了地址(实例化设备必须还要名字),然后调用了 i2c 驱动中的 detect 函数,如果成功则调用 i2c_new_device函数真正实例化i2c设备,并且将i2c设备挂在i2c驱动的链表上!注意:只有这种方式添加的i2c设备才会挂在驱动的链表上!

仔细思考上面就能发现,i2c驱动中的detect函数必须要填写i2c_board_info结构体中name,i2c_new_device才能实例化i2c设备。

所以,使用i2c驱动扫描注册设备时,需要按如下格式编写驱动!

#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
 
static int __devinit my_i2c_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    return 0;
}
 
static int __devexit my_i2c_drv_remove(struct i2c_client *client)
{
    return 0;
}
 
static const struct i2c_device_id my_dev_id_table[] = {
    { "my_i2c_dev", 0 },
    {}
};//这里的名字很重要,驱动第一种匹配设备的方式要用到
 
static int my_i2c_drv_detect(struct i2c_client *client, struct i2c_board_info *info)
{
    /* 能运行到这里, 表示该addr的设备是存在的
     * 但是有些设备单凭地址无法分辨(A芯片的地址是0x50, B芯片的地址也是0x50)
     * 还需要进一步读写I2C设备来分辨是哪款芯片,自己写方法
     * detect就是用来进一步分辨这个芯片是哪一款,并且设置info->type,也就是设备名字
     */
    printk("my_i2c_drv_detect: addr = 0x%x\n", client->addr);
 
    /* 进一步判断是哪一款 */
    strlcpy(info->type, "my_i2c_dev", I2C_NAME_SIZE);
    return 0;
}
 
static const unsigned short addr_list[] = { 0x46, 0x48, I2C_CLIENT_END };//必须使用I2C_CLIENT_END宏结尾
 
/* 1. 分配/设置i2c_driver */
static struct i2c_driver my_i2c_driver = {
    .class  = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备,不是对应类将不会调用匹配 */
    .driver        = {
        .name        = "my_i2c_dev",
        .owner        = THIS_MODULE,
    },
    .probe                = my_i2c_drv_probe,
    .remove        = __devexit_p(my_i2c_drv_remove),
    .id_table        = my_dev_id_table,
    .detect     = my_i2c_drv_detect,  /* 用这个函数来检测设备确实存在 ,并填充设备名字*/
    .address_list        = addr_list,   /* 这些设备的地址 */
};
 
static int my_i2c_drv_init(void)
{
    /* 2. 注册i2c_driver */
    i2c_add_driver(&my_i2c_driver);
 
    return 0;
}
 
static void my_i2c_drv_exit(void)
{
    i2c_del_driver(&my_i2cc_driver);
}
 
module_init(my_i2c_drv_init);
module_exit(my_i2c_drv_exit);
MODULE_LICENSE("GPL");

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

I2C设备注册的4种方法 的相关文章

  • 经典书籍--好书很多,重要的是有个目标,有个规划

    学了才是自己的 xff0c 分享出来 xff0c 和大家一起学习 好多书我也没有看过 好书很多 xff0c 重要的是有个目标 xff0c 有个规划 操作系统方面 操作系统概念 第七版 Operating System Concepts 鸟哥
  • 使用入口函数创建多个线程

    include lt iostream gt include lt thread gt using namespace std void func int a int b cout lt lt a 43 b lt lt this threa
  • 多线程编程lock_guard 和unique_lock (第六讲)

    平时看代码时 xff0c 也会使用到std lock guard xff0c 但是std unique lock 用的比较少 在看并发编程 xff0c 这里总结一下 方便后续使用 std unique lock也可以提供自动加锁 解锁功能
  • 程序员一般都浏览这些网站,不仅仅提升编程水平!

    来源 https www zhihu com question 283272958 程序员作为一个经常和互联网打交道的人群 xff0c 他们喜欢浏览那些网站呢 xff1f 不爱敲代码的程序猿整理了以下网站供大家参考 xff0c 排名不分先后
  • 电脑编程的基础知识——编译器和连接器

    核心提示 xff1a 我从没见过 xff08 不过应该有 xff09 任何一本C 43 43 教材有讲过何谓编译器 xff08 Compiler xff09 及连接器 xff08 Linker xff09 xff08 倒是在很老的C教材中见
  • 难得的c语言经典面试题

    变量的生存期 栈和堆的区别 变量的初始化问题 传指针和传值的实质 传指针的实质是传值 传值的时候 我们是做了一个复制品 在函数中只是对复制品在操作 进入函数和返回函数都是这个道理 经历了一个复制的过程 举个例子 csdn上的一个例子 这个程
  • V2X入门知识

    根据国家统计局的数据统计 xff0c 2018年全国一共发生了24万起交通事故 xff0c 其中死亡人数有6万多人 xff0c 每天将近200人 xff0c 很多公司都在尝试通过自动驾驶 xff0c 来遏制交通事故 xff0c 避免伤亡 x
  • 车联网V2X技术要点

    初探车联网V2X技术 xff08 这个链接的内容非常不错 xff09 https www jianshu com p ecc198efdd38 随着信息技术的发展 xff0c 车联网正在从概念变为现实 xff0c 深入研究车与车 xff08
  • 智能网联V2X的秘密?解读DSRC和C-V2X两种技术

    近年来智能交通系统的开发将主要集中在智能公路交通系统领域 xff0c 也就是俗称的车联网 其中 V2X 技术借助车 车 xff0c 车与路测基础设施 车与路人之间的无线通信 xff0c 实时感知车辆周边状况进行及时预警成为当前世界各国解决道
  • NR V2X新总结

    https mp weixin qq com s a7t9fwiJOc2LCCQsqeIvUw 图片不显示 xff0c 可以看链接 自从2018年6月RAN 80全会上启动了NR V2X技术研究后 xff0c 3GPP工作组针对NR V2X
  • ADAS是什么?

    先进驾驶辅助系统 xff08 Advanced Driver Assistant System xff09 xff0c 简称ADAS xff0c 是利用安装于车上的各式各样的传感器 xff0c 在第一时间收集车内外的环境数据 xff0c 进
  • 为什么要序列化,如何序列化和反序列化?

    当两个进程在进行远程通信时 xff0c 彼此可以发送各种类型的数据 无论是何种类型的数据 xff0c 都会以二进制序列的形式在网络上传送 发送方需要把这个对象转换为字节序列 xff0c 才能在网络上传送 xff1b 接收方则需要把字节序列再
  • AD/ADAS 自动驾驶领域相关书籍整理和推荐

    本文整理了最近各方面收集的有关ADAS 智能 无人驾驶 xff08 Intelligent Driverless Driving xff09 领域的书籍资料 xff0c 这些书中不乏比较具有前瞻性的五星级书籍 xff0c 也包括技术性相关的
  • 什么是JSON?

    我有点懒 xff0c 大家耐心看图 xff0c 哈哈
  • 关于C-V2X 你需要知道的十件事

    蜂窝车联网 xff0c 通信正持续获得生态系统的支持 xff0c 将成为对汽车安全和未来自动驾驶至关重要的一项技术 在整个汽车和科技行业也都能看到C V2X技术的发展势头 举例来说 xff0c 5G汽车联盟 xff08 5GAA xff09
  • C++中的四种强制转换 dynamic_case,const_cast,static_case,reinterprer_case的不同

    使用标准C 43 43 的类型转换符 xff1a static cast dynamic cast reinterpret cast 和const cast 1 static cast 用法 xff1a static cast lt typ
  • V2X高通的布局

  • 5W2H工作法,使工作更有条理,生活更好梳理

    发明者用五个以W开头的英语单词和两个以H开头的英语单词进行设问 xff0c 发现解决问题的线索 xff0c 寻找发明思路 xff0c 进行设计构思 xff0c 从而搞出新的发明项目 xff0c 这就叫做5W2H法 xff08 1 xff09
  • C 可变参数

    有时 xff0c 您可能会碰到这样的情况 xff0c 您希望函数带有可变数量的参数 xff0c 而不是预定义数量的参数 C 语言为这种情况提供了一个解决方案 xff0c 它允许您定义一个函数 xff0c 能根据具体的需求接受可变数量的参数
  • 给初学者:3个月学会机器学习 ||附完整路径+资源

    感觉本科学的三门数学课 xff0c 不是无用的鸡肋了 xff0c 可是我已经都还给老师了 http www sohu com a 225511837 99905135 https www jianshu com p 27124019c69b

随机推荐

  • 车路协调场景与演进与V2X SDK技术解析

    车路协调场景与演进与V2X SDK技术解析 xff1a 回看链接 https apposcmf8kb5033 h5 xiaoeknow com content page eyJ0eXBlIjoiMiIsInJlc291cmNlX3R5cGU
  • 新的开始之Win7、CentOS 6.4 双系统 硬盘安装

    目的 xff1a 在已经有Win7的操作系统上安装CentOS6 4的32位操作系统 本博客结合了以下的博客 http blog csdn net markho365 article details 8969591 http www cnb
  • 详解protobuf-从原理到使用

    这里写的少 xff0c 后面再补充 https www jianshu com p 419efe983cb2
  • signal(SIGCHLD, SIG_IGN)和signal(SIGPIPE, SIG_IGN);

    这个链接写的比较好 xff1a https yq aliyun com articles 42215 signal SIGCHLD SIG IGN 因为并发服务器常常fork很多子进程 xff0c 子进程终结之后需要服务器进程去wait清理
  • Linux c 网络socket编程

    网络编程 xff0c 一定离不开套接字 xff1b 那什么是套接字呢 xff1f 在Linux下 xff0c 所有的I O操作都是通过读写文件描述符而产生的 xff0c 文件描述符是一个和打开的文件相关联的整数 xff0c 这个文件并不只包
  • Linux c 下socket编程全面

    网络的Socket数据传输是一种特殊的I O xff0c Socket也是一种文件描述符 Socket也具有一个类似于打开文件的函数调用Socket xff0c 该函数返回一个整型的Socket描述符 xff0c 随后的连接建立 数据传输等
  • CANoe与金溢的obu can连接的环境问题 Cifconfig can0 up 失败 设置波特率失败

    今天搭建了CANoe与金溢的obu can连接的环境问题 遇到了一个让人不解的问题 can0起不来 xff0c 于是怀疑波特率不匹配 xff0c 使用调不了 Linux 设置波特率 ifconfig can0 down 关闭CAN0 ip
  • V2X-Locate方案,解决隧道内自动车辆定位问题

    2019年3月连网自驾车辆 Connected and Autonomous Vehicles xff0c CAV 通讯技术厂商CohdaWireless于挪威B rum市新建隧道 长达1 3英里 2 2公里 内采用V2X Locate方案
  • 一个高级软件工程师面试被问的问题

    使用new和malloc如何解决内存碎片问题 xff1f 多进程间通信几种方式 xff0c 你用过几种方式 xff1f 线程间通信 xff0c 用过几种方式 分不同的场景 xff0c 适合用哪种通信方式 内存管理 xff0c 如果让你来实现
  • 面试时你需要问HR什么问题?

    与职位相关的问题要多问 xff0c 如 xff1a 1 我知道该职位的首要职责 xff0c 但公司有没有其他的要求 xff1f 2 我的专长是XX xff0c 请问XX部门在公司占有什么样的位置 xff1f 6 面试之后的安排都是什么 xf
  • Linux中断实现浅析

    本文描述内容针对2 6 31 43 x86平台 xff0c 不包含硬件相关的细节 作者 xff1a 独孤九贱 xff1b 版权所有 xff0c 转载请注明出处 有问题欢迎与我交流讨论 一 概述 中断 xff0c 本质上是一个电信号 xff0
  • Linux常用命令

    应用patch patch p1 lt test1 patch 卸载patch patch Rp1 lt test1 patch
  • 【jetson nano、NX、TX2 开机自启动程序,开机自动解除硬件限制,开机默认最大性能工作。】

    jetson nano NX TX2 开机自启动程序 xff0c 开机自动解除硬件限制 xff0c 开机默认最大性能工作 jetson nano NX TX2是英伟达开发的边缘平台 xff0c 其良好的性能 亲民的价格非常适合部署深度学习模
  • 蒙特卡洛法(一)

    蒙特卡洛法也成为统计模拟方法 xff0c 通过从概率模型的随机抽样进行近似数值计算的方法 马尔科夫链蒙特卡洛法则是以马尔科夫链为概率模型的蒙特卡洛法 xff0c 构建一个马尔科夫链 xff0c 使其平稳分布就是要进行抽样的分布 xff0c
  • 芯片测试术语 ,片内测试(BIST),ATE测试

    芯片测试分为如下几类 xff1a 1 WAT xff1a Wafer AcceptanceTest xff0c wafer level 的管芯或结构测试 xff1b 2 CP xff1a chip probing xff0c wafer l
  • SBUS协议及编解码

    1 简介 SBUS本质是一种串口通信协议 xff0c 采用100K的波特率 xff0c 8位数据位 xff0c 两位停止位 xff0c 偶效验 xff0c 即8E2的串口通信 值得注意的有三点 xff1a 1 SBUS采用负逻辑 xff0c
  • Linux内核跨模块函数调用:EXPORT_SYMBOL()宏定义

    一 查看内核驱动代码你会发现很多的函数带有EXPORT SYMBOL 宏定义 二 那么EXPORT SYMBOL的作用是什么 xff1f EXPORT SYMBOL标签内定义的函数或者符号对全部内核代码公开 xff0c 不用修改内核代码就可
  • linux内核I2C子系统详解

    1 I2C通信协议 参考博客 xff1a I2C通信协议详解和通信流程分析 xff1b https csdnimg cn release blogv2 dist pc themesSkin skin3 template images bg
  • 内核驱动中断申请类型及函数分析

    ret 61 request irq chip gt irq xxx intr handler IRQF TRIGGER FALLING IRQF NO THREAD IRQF NO SUSPEND name chip 上面是中断初始化中调
  • I2C设备注册的4种方法

    文章目录 前言一 静态注册二 动态注册三 用户空间注册四 i2c驱动扫描注册 前言 I2C设备的4种添加方法 xff1a 1 xff09 静态注册 2 xff09 动态注册 3 xff09 用户空间注册 4 xff09 i2c驱动扫描注册