Linux驱动:I2C驱动学习看这一篇就够了

原创 嵌入式悦翔园 2023-06-16 11:40

关注星标公众号,第一时间获取信息

一、前言

I2C协议是在开发中使用非常频繁的一种协议,相信大家在学习单片机的时候经常会用到支持I2C协议的模块,I2C 总线仅仅使用 SCL、SDA 这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和 PCB 板布线空间的占用。因此,I2C 总线被非常广泛地应用在 EEPROM、实时钟、小型 LCD 等设备与 CPU 的接口中。

但是与裸机开发不同的是在 Linux 系统中,I2C 驱动由 3 部分组成,即 I2C 核心I2C 总线驱动I2C 设备驱动。今天就从这三个部分来给大家讲解一下Linux中的I2C驱动,以及我们应该如何为我们的开发板添加一个I2C设备。

二、Linux 的 I2C 体系结构

由上面分析可知,Linux驱动分为三部分:I2C 核心I2C 总线驱动I2C 设备驱动

2.1 Linux I2C 核心

I2C 核心提供了 I2C 总线驱动和设备驱动的注册、注销方法,这部分主要是一些与硬件无关的的接口函数,这部分的代码一般不用我们普通开发者进行开发和修改,但是理解这部分的代码逻辑和接口还是非常必要的。

I2C 核心中的主要函数如下:

注册/注销 适配器(adapter)
int i2c_add_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);

注册/注销 I2C设备驱动程序
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);

创建并注册一个新的I2C设备
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);

I2C 传输、发送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);

上边三个函数用于实现与I2C设备之间的数据交换。i2c_transfer函数可以进行复杂的多消息传输,而i2c_master_sendi2c_master_recv函数用于单个数据消息的发送和接收。

这些函数提供了对于I2C总线读写操作的基本支持,简化了I2C设备驱动的开发,有了这些接口我们就不用关注I2C协议方面的代码了,只需要调用该接口即可完成数据的传输。

注意: i2c_transfer函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到 i2c_adapter 对应的 i2c_algorithm,并使用 i2c_algorithmmaster_xfer函数真正驱动硬件流程。

2.2 Linux I2C 适配器驱动

通过上面的介绍我们知道了I2C驱动主要分为三个部分,上面我们已经介绍了I2C核心这一部分,现在我们来介绍一下I2C 适配器驱动,我们知道I2C驱动和其他的那些字符设备驱动有所不同,I2C驱动中维持着一套自己的总线。

I2C 适配器驱动是Linux内核中的一个核心模块,总线层负责管理所有注册到系统的I2C总线适配器和设备,并提供与设备通信的API函数。它提供了一些基本的操作函数,如启动总线、停止总线、发送起始信号、发送停止信号等。但是这部分是由Linux内核完成的,并不需要我们开发者进行修改或添加,所以了解即可。

下面我们用一张图来看一下上面描述的这个过程:

2.3 Linux I2C 设备驱动

I2C 设备驱动要使用 i2c_driveri2c_client 数据结构并填充其中的成员函数。 i2c_client 一般被包含在设备的私有信息结构体 yyy_data 中,而 i2c_driver 则适合被定义为全局变量并初始化。

看到I2C设备驱动的这两个结构体大家是不是很熟悉了,I2C设备驱动是针对特定类型的I2C设备编写的驱动程序。它包含了对具体设备的操作和控制逻辑,通过调用I2C总线核心驱动提供的API函数与设备进行通信。设备驱动的主要任务包括初始化设备、读写数据、配置设备参数等。

因为这部分是针对特定类型的I2C设备编写的驱动程序,所以这部分才是要我们开发人员来完成编写的,我们如果需要在自己的开发板上添加一个新的I2C模块,我们就要首先编写I2C设备驱动这部分,这部分的编写需要调用上面我们介绍的I2C核心和I2C总线中接口函数来完成模块的初始化。

关于I2C设备驱动我们这里先做一个了解即可,后面会详细介绍这部分的内容,也是我们学习I2C驱动的重点内容。

2.4 Linux I2C驱动总结

I2C总线核心驱动(I2C Core Driver):【系统厂编写】I2C总线核心驱动是Linux内核中的一个核心模块,负责管理所有注册到系统的I2C总线适配器和设备,并提供与设备通信的API函数。它提供了一些基本的操作函数,如启动总线、停止总线、发送起始信号、发送停止信号等。

I2C适配器驱动(I2C Adapter Driver):【芯片厂提供】I2C适配器驱动负责与硬件的I2C控制器进行交互,完成硬件层面的初始化、配置和操作。它将底层硬件的特定接口与I2C总线核心驱动进行连接,使得核心驱动能够通过适配器驱动来访问硬件。

I2C设备驱动(I2C Device Driver):【开发者编写】I2C设备驱动是针对特定类型的I2C设备编写的驱动程序。它包含了对具体设备的操作和控制逻辑,通过调用I2C总线核心驱动提供的API函数与设备进行通信。设备驱动的主要任务包括初始化设备、读写数据、配置设备参数等。

三部分之间的关系如下:

  • I2C核心层驱动作为顶层驱动,管理整个I2C子系统,并提供了基本的I2C操作接口。

  • I2C适配器驱动负责与底层硬件的I2C控制器进行交互,通过适配器驱动,I2C总线核心驱动能够与硬件进行通信。

  • I2C设备驱动则针对具体的I2C设备编写,实现了对设备的初始化、读写数据等操作。

三、具体设备驱动分析

由于作为开发者我们需要关注并且需要我们亲自编写的部分就只有设备驱动了,所以我们今天就详细介绍一下设备驱动这部分。

当我们需要编写具体的I2C设备驱动程序时,我们需要编写以下内容:**probe函数remove函数操作函数以及数据传输与处理**,下面将对每部分进行详细介绍。

3.1 Probe函数

具体设备中的probe函数是I2C设备驱动中最重要的函数之一,用于在I2C设备与驱动匹配成功后进行初始化和注册设备。在probe函数中,可以执行以下任务:

  • 进行设备的特定初始化操作,例如配置设备寄存器、申请内存资源等。

  • 注册字符设备、输入设备或其他设备类别,使系统能够识别和使用该设备。

  • 存储设备私有数据,通常使用i2c_set_clientdata函数将私有数据与i2c_client相关联,方便后续的操作函数访问。

我们在学习其他设备驱动的时候就知道了probe函数是设备驱动匹配成功后被调用执行的。它的原型通常如下所示:

static int i2c_device_probe(struct i2c_client *client, const struct i2c_device_id *id);

下面我们就找一个设备驱动来分析一下我们应该如何编写:

这里以rk3x_i2c_probe为例给大家进行分析:
static int rk3x_i2c_probe(struct platform_device *pdev)
{
 struct device_node *np = pdev->dev.of_node;
 const struct of_device_id *match;
 struct rk3x_i2c *i2c;
 struct resource *mem;
 int ret = 0;
 int bus_nr;
 u32 value;
 int irq;
 unsigned long clk_rate;
 
 i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
 if (!i2c)
  return -ENOMEM;
 
 match = of_match_node(rk3x_i2c_match, np);
 i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
 
 /* use common interface to get I2C timing properties */
 i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);
 
 strlcpy(i2c->adap.name, "rk3x-i2c"sizeof(i2c->adap.name));
 i2c->adap.owner = THIS_MODULE;
 i2c->adap.algo = &rk3x_i2c_algorithm;
 i2c->adap.retries = 3;
 i2c->adap.dev.of_node = np;
 i2c->adap.algo_data = i2c;
 i2c->adap.dev.parent = &pdev->dev;
 
 i2c->dev = &pdev->dev;
 
 spin_lock_init(&i2c->lock);
 init_waitqueue_head(&i2c->wait);
 
 i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
 i2c->i2c_restart_nb.priority = 128;
 ret = register_i2c_restart_handler(&i2c->i2c_restart_nb);
 if (ret) {
  dev_err(&pdev->dev, "failed to setup i2c restart handler.\n");
  return ret;
 }
 
 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
 if (IS_ERR(i2c->regs))
  return PTR_ERR(i2c->regs);
 
 /* Try to set the I2C adapter number from dt */
 bus_nr = of_alias_get_id(np, "i2c");
 
 /*
  * Switch to new interface if the SoC also offers the old one.
  * The control bit is located in the GRF register space.
  */

 if (i2c->soc_data->grf_offset >= 0) {
  struct regmap *grf;
 
  grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
  if (IS_ERR(grf)) {
   dev_err(&pdev->dev,
    "rk3x-i2c needs 'rockchip,grf' property\n");
   return PTR_ERR(grf);
  }
 
  if (bus_nr < 0) {
   dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias");
   return -EINVAL;
  }
 
  /* 27+i: write mask, 11+i: value */
  value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
 
  ret = regmap_write(grf, i2c->soc_data->grf_offset, value);
  if (ret != 0) {
   dev_err(i2c->dev, "Could not write to GRF: %d\n", ret);
   return ret;
  }
 }
 
 /* IRQ setup */
 irq = platform_get_irq(pdev, 0);
 if (irq < 0) {
  dev_err(&pdev->dev, "cannot find rk3x IRQ\n");
  return irq;
 }
 
 ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
          0, dev_name(&pdev->dev), i2c);
 if (ret < 0) {
  dev_err(&pdev->dev, "cannot request IRQ\n");
  return ret;
 }
 
 platform_set_drvdata(pdev, i2c);
 
 if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
  /* Only one clock to use for bus clock and peripheral clock */
  i2c->clk = devm_clk_get(&pdev->dev, NULL);
  i2c->pclk = i2c->clk;
 } else {
  i2c->clk = devm_clk_get(&pdev->dev, "i2c");
  i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
 }
 
 if (IS_ERR(i2c->clk)) {
  ret = PTR_ERR(i2c->clk);
  if (ret != -EPROBE_DEFER)
   dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
  return ret;
 }
 if (IS_ERR(i2c->pclk)) {
  ret = PTR_ERR(i2c->pclk);
  if (ret != -EPROBE_DEFER)
   dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
  return ret;
 }
 
 ret = clk_prepare(i2c->clk);
 if (ret < 0) {
  dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret);
  return ret;
 }
 ret = clk_prepare(i2c->pclk);
 if (ret < 0) {
  dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret);
  goto err_clk;
 }
 
 i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
 ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
 if (ret != 0) {
  dev_err(&pdev->dev, "Unable to register clock notifier\n");
  goto err_pclk;
 }
 
 clk_rate = clk_get_rate(i2c->clk);
 rk3x_i2c_adapt_div(i2c, clk_rate);
 
 ret = i2c_add_adapter(&i2c->adap);
 if (ret < 0) {
  dev_err(&pdev->dev, "Could not register adapter\n");
  goto err_clk_notifier;
 }
 
 dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs);
 
 return 0;
 
err_clk_notifier:
 clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
err_pclk:
 clk_unprepare(i2c->pclk);
err_clk:
 clk_unprepare(i2c->clk);
 return ret;
}

从上面的代码我们可以发现rk3x_i2c_probe主要做了以下几件事情:

1、通过devm_kzalloc函数为rk3x_i2c结构体分配内存空间;
2、从设备树中获取I2C设备信息并填充rk3x_i2c结构体;
3、使用devm_platform_ioremap_resource函数来映射设备的寄存器资源到内存中;
4、获取并配置中断;
5、使用i2c_add_adapter注册设备

基本上这个驱动就是一个比较完整的I2C设备初始化流程了,我们如果想要编写其他设备的驱动可以参考该驱动初始化来进行编写。

3.2 读写函数

由于rk3x_i2c中的读写函数和该设备关联性较大,不具备通用性,这里以sx1_i2c_write_bytesx1_i2c_read_byte来给大家进行分析,该函数更具有通用性。

/* Write to I2C device */
int sx1_i2c_write_byte(u8 devaddr, u8 regoffset, u8 value)
{
 struct i2c_adapter *adap;
 int err;
 struct i2c_msg msg[1];
 unsigned char data[2];

 adap = i2c_get_adapter(0);
 if (!adap)
  return -ENODEV;
 msg->addr = devaddr; /* I2C address of chip */
 msg->flags = 0;
 msg->len = 2;
 msg->buf = data;
 data[0] = regoffset; /* register num */
 data[1] = value;  /* register data */
 err = i2c_transfer(adap, msg, 1);
 i2c_put_adapter(adap);
 if (err >= 0)
  return 0;
 return err;
}

/* Read from I2C device */
int sx1_i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value)
{
 struct i2c_adapter *adap;
 int err;
 struct i2c_msg msg[1];
 unsigned char data[2];

 adap = i2c_get_adapter(0);
 if (!adap)
  return -ENODEV;

 msg->addr = devaddr; /* I2C address of chip */
 msg->flags = 0;
 msg->len = 1;
 msg->buf = data;
 data[0] = regoffset; /* register num */
 err = i2c_transfer(adap, msg, 1);

 msg->addr = devaddr; /* I2C address */
 msg->flags = I2C_M_RD;
 msg->len = 1;
 msg->buf = data;
 err = i2c_transfer(adap, msg, 1);
 *value = data[0];
 i2c_put_adapter(adap);

 if (err >= 0)
  return 0;
 return err;
}

从上面的代码可以看出,sx1_i2c_write_byte主要完成了以下功能:

1、通过调用i2c_get_adapter(0)函数获取指定索引的I2C适配器对象并赋值给adap变量。
2、初始化一个struct i2c_msg类型的数组msg,该数组包含一个元素用于I2C消息的传输。
3、设置msg结构体中的字段:
 addr:设备的I2C地址。
 flags:传输标志位,此处为0表示写操作。
 len:要传输的字节数,此处设置为2,即寄存器地址和寄存器数据两个字节。
 buf:数据缓冲区的指针,用于存储要发送的数据。
4、将要写入的设备寄存器地址和数据分别存储在data数组的第一个和第二个元素中,即data[0] = regoffset;
和data[1] = value;。
5、调用i2c_transfer()函数进行I2C消息传输,将数据写入设备寄存器。
6、使用i2c_put_adapter()函数释放先前获取的I2C适配器对象。

sx1_i2c_read_byte主要完成了以下功能:

1、通过调用i2c_get_adapter(0)函数获取指定索引的I2C适配器对象并赋值给adap变量。
2、初始化一个struct i2c_msg类型的数组msg,该数组包含一个元素用于I2C消息的传输。
3、设置msg结构体中的字段:
 addr:设备的I2C地址。
 flags:传输标志位,此处为0表示写操作。
 len:要传输或接收的字节数。
 buf:数据缓冲区的指针,用于存储要发送或接收的数据。
4、将要读取的设备寄存器地址存储在data数组的第一个元素中,即data[0] = regoffset;

5、调用i2c_transfer()函数进行I2C消息传输,将数据写入设备寄存器。
6、更改flags字段为I2C_M_RD,表示接收模式(读操作)。
7、再次调用i2c_transfer()函数进行I2C消息传输,从设备中读取数据。
8、将读取到的数据存储在data数组的第一个元素中,即*value = data[0];。
9、使用i2c_put_adapter()函数释放先前获取的I2C适配器对象。

对比I2C读和写的过程大家可能会发现I2C读的过程为什么调用了两次i2c_transfer函数呢?多调用了一次i2c_transfer函数是因为我们在调用i2c_transfer读取数据时,需要先发送要读取的寄存器地址给设备,然后再从设备读取实际的数据。所以第一次使用i2c_transfer发送的信息为需要读取的地址信息,第二次将标志位改为读,然后使用i2c_transfer将从设备返回的信息存储到i2c_adapter中。

四、I2C驱动中几个重要的结构体

在I2C驱动中,有三个比较重要的结构体用于描述和管理I2C设备和传输操作。下面就这三个结构体的成员以及作用来给大家讲解一下:

4.1 i2c_adapter 结构体

定义位置:i2c.h结构体原型:

struct i2c_adapter {
 struct module *owner;
 unsigned int class;    /* classes to allow probing for */
 const struct i2c_algorithm *algo; /* the algorithm to access the bus */
 void *algo_data;

 /* data fields that are valid for all devices */
 const struct i2c_lock_operations *lock_ops;
 struct rt_mutex bus_lock;
 struct rt_mutex mux_lock;

 int timeout;   /* in jiffies */
 int retries;
 struct device dev;  /* the adapter device */
 unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED  0
#define I2C_ALF_SUSPEND_REPORTED 1

 int nr;
 char name[48];
 struct completion dev_released;

 struct mutex userspace_clients_lock;
 struct list_head userspace_clients;

 struct i2c_bus_recovery_info *bus_recovery_info;
 const struct i2c_adapter_quirks *quirks;

 struct irq_domain *host_notify_domain;
 struct regulator *bus_regulator;
};

几个重要的成员:

name:适配器的名称。
nr:适配器的编号。
bus_lock 和 bus_unlock:用于保护对适配器的并发访问的锁机制。
algo:指向 I2C 算法结构体的指针,包含了适配器的通信算法,如标准模式、快速模式、高速模式等。

4.2 i2c_client 结构体

定义位置:i2c.h结构体原型:

struct i2c_client {
 unsigned short flags;  /* div., see below  */
#define I2C_CLIENT_PEC  0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN  0x10 /* we have a ten bit chip address */
     /* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */
#define I2C_CLIENT_WAKE  0x80 /* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB  0x9000 /* Use Omnivision SCCB protocol */
     /* Must match I2C_M_STOP|IGNORE_NAK */

 unsigned short addr;  /* chip address - NOTE: 7bit */
     /* addresses are stored in the */
     /* _LOWER_ 7 bits  */
 char name[I2C_NAME_SIZE];
 struct i2c_adapter *adapter; /* the adapter we sit on */
 struct device dev;  /* the device structure  */
 int init_irq;   /* irq set at initialization */
 int irq;   /* irq issued by device  */
 struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
 i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
 void *devres_group_id;  /* ID of probe devres group */
};

几个重要的成员:

flags:标志位,用于指定设备的特性和行为。
addr:设备的I2C地址。
adapter:指向 i2c_adapter 的指针,表示所属的I2C适配器。
driver:指向设备驱动程序的指针,表示设备所使用的驱动。

4.3 i2c_driver 结构体

定义位置:i2c.h结构体原型:

struct i2c_driver {
 unsigned int class;

 union {
 /* Standard driver model interfaces */
  int (*probe)(struct i2c_client *client);
  /*
   * Legacy callback that was part of a conversion of .probe().
   * Today it has the same semantic as .probe(). Don't use for new
   * code.
   */

  int (*probe_new)(struct i2c_client *client);
 };
 void (*remove)(struct i2c_client *client);


 /* driver model interfaces that don't relate to enumeration  */
 void (*shutdown)(struct i2c_client *client);

 /* Alert callback, for example for the SMBus alert protocol.
  * The format and meaning of the data value depends on the protocol.
  * For the SMBus alert protocol, there is a single bit of data passed
  * as the alert response's low bit ("event flag").
  * For the SMBus Host Notify protocol, the data corresponds to the
  * 16-bit payload data reported by the slave device acting as master.
  */

 void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol,
        unsigned int data);

 /* a ioctl like command that can be used to perform specific functions
  * with the device.
  */

 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

 struct device_driver driver;
 const struct i2c_device_id *id_table;

 /* Device detection callback for automatic device creation */
 int (*detect)(struct i2c_client *client, struct i2c_board_info *info);
 const unsigned short *address_list;
 struct list_head clients;

 u32 flags;
};

几个重要的成员:

driver:是一个 struct device_driver 结构体,用于向Linux设备模型注册驱动程序。
probe 和 remove:指向探测和移除设备的函数指针,通过这两个函数,驱动程序可以在发现匹配的设备时执行初始化操作,并在设备被移除时执行清理操作。
id_table:用于指定驱动程序支持的I2C设备ID列表,以便匹配对应的设备。

这些结构体共同构成了Linux内核中的I2C驱动框架,提供了对I2C总线、适配器和设备的抽象和管理功能。开发者可以基于这些结构体来编写自己的I2C驱动程序,并实现与I2C设备的通信和控制。所以我们的工作就是填充这些结构体然后调用对应的接口把我们填充好的结构体传递给I2C设备器驱动和核心驱动从而完成设备的初始化和读写操作。

五、总结

I2C驱动的学习有一个特点:弄懂比较难,会用比较简单,这是因为有很多的有难度的内容以及和协议相关的内容都已经被Linux或者芯片厂封装好了,我们需要做的就是使用他们提供的这些接口完成指定设备的读写操作,但是我们的学习不能止步于此,所以我们不但要会用,还要知其然知其所以然。

关于I2C驱动的知识我也是才疏学浅,也有很多地方不是很了解,关于上面的知识点也只是我的一些理解,如果有不对的地方欢迎大家指出,我们一起交流学习。

推荐阅读



01

加入嵌入式交流群


02

嵌入式资源获取


03

STM32中断优先级详解


04

STM32下载程序新思路--使用串口下载程序


嵌入式悦翔园 专注于嵌入式技术,包括但不限于STM32、Arduino、51单片机、物联网、Linux等编程学习笔记,同时包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论 (0)
  • WT588F02B是广州唯创电子推出的一款高性能语音芯片,广泛应用于智能家电、安防设备、玩具等领域。然而,在实际开发中,用户可能会遇到烧录失败的问题,导致项目进度受阻。本文将从下载连线、文件容量、线路长度三大核心因素出发,深入分析烧录失败的原因并提供系统化的解决方案。一、检查下载器与芯片的物理连接问题表现烧录时提示"连接超时"或"设备未响应",或烧录进度条卡顿后报错。原因解析接口错位:WT588F02B采用SPI/UART双模通信,若下载器引脚定义与芯片引脚未严格对应(如TXD/RXD交叉错误)
    广州唯创电子 2025-03-26 09:05 150浏览
  • ​2025年3月27日​,贞光科技授权代理品牌紫光同芯正式发布新一代汽车安全芯片T97-415E。作为T97-315E的迭代升级产品,该芯片以大容量存储、全球化合规认证、双SPI接口协同为核心突破,直击智能网联汽车"多场景安全并行"与"出口合规"两大行业痛点,助力车企抢占智能驾驶与全球化市场双赛道。行业趋势锚定:三大升级回应智能化浪潮1. 大容量存储:破解车联网多任务瓶颈随着​车机功能泛在化​(数字钥匙、OTA、T-BOX等安全服务集成),传统安全芯片面临存储资源挤占难题。T97-415E创新性
    贞光科技 2025-03-27 13:50 168浏览
  • 在当今竞争激烈的工业环境中,效率和响应速度已成为企业制胜的关键。为了满足这一需求,我们隆重推出宏集Panorama COOX,这是Panorama Suite中首款集成的制造执行系统(MES)产品。这一创新产品将Panorama平台升级为全面的工业4.0解决方案,融合了工业SCADA和MES技术的双重优势,帮助企业实现生产效率和运营能力的全面提升。深度融合SCADA与MES,开启工业新纪元宏集Panorama COOX的诞生,源于我们对创新和卓越运营的不懈追求。通过战略性收购法国知名MES领域专
    宏集科技 2025-03-27 13:22 218浏览
  •       知识产权保护对工程师的双向影响      正向的激励,保护了工程师的创新成果与权益,给企业带来了知识产权方面的收益,企业的创新和发明大都是工程师的劳动成果,他们的职务发明应当受到奖励和保护,是企业发展的重要源泉。专利同时也成了工程师职称评定的指标之一,专利体现了工程师的创新能力,在求职、竞聘技术岗位或参与重大项目时,专利证书能显著增强个人竞争力。专利将工程师的创意转化为受法律保护的“无形资产”,避免技术成果被他人抄袭或无偿使
    广州铁金刚 2025-03-25 11:48 184浏览
  • 家电,在人们的日常生活中扮演着不可或缺的角色,也是提升人们幸福感的重要组成部分,那你了解家电的发展史吗?#70年代结婚流行“四大件”:手表、自行车、缝纫机,收音机,合成“三转一响”。#80年代随着改革开放的深化,中国经济开始飞速发展,黑白电视机、冰箱、洗衣机这“新三件”,成为了人们对生活的新诉求。#90年代彩电、冰箱、全自动洗衣机开始大量进入普通家庭,快速全面普及,90年代末,家电产品实现了从奢侈品到必需品的转变。#00年代至今00年代,随着人们追求高品质生活的愿望,常用的电视机、洗衣机等已经远
    启英AI平台 2025-03-25 14:12 92浏览
  • 在嵌入式语音系统的开发过程中,广州唯创电子推出的WT588系列语音芯片凭借其优异的音质表现和灵活的编程特性,广泛应用于智能终端、工业控制、消费电子等领域。作为该系列芯片的关键状态指示信号,BUSY引脚的设计处理直接影响着系统交互的可靠性和功能拓展性。本文将从电路原理、应用场景、设计策略三个维度,深入解析BUSY引脚的技术特性及其工程实践要点。一、BUSY引脚工作原理与信号特性1.1 电气参数电平标准:输出3.3V TTL电平(与VDD同源)驱动能力:典型值±8mA(可直接驱动LED)响应延迟:语
    广州唯创电子 2025-03-26 09:26 216浏览
  • 在智能语音产品的开发过程中,麦克风阵列的选型直接决定了用户体验的优劣。广州唯创电子提供的单麦克风与双麦克风解决方案,为不同场景下的语音交互需求提供了灵活选择。本文将深入解析两种方案的性能差异、适用场景及工程实现要点,为开发者提供系统化的设计决策依据。一、基础参数对比分析维度单麦克风方案双麦克风方案BOM成本¥1.2-2.5元¥4.8-6.5元信噪比(1m)58-62dB65-68dB拾音角度全向360°波束成形±30°功耗8mW@3.3V15mW@3.3V典型响应延迟120ms80ms二、技术原
    广州唯创电子 2025-03-27 09:23 180浏览
  • 案例概况在丹麦哥本哈根,西门子工程师们成功完成了一项高安全设施的数据集成项目。他们利用宏集Cogent DataHub软件,将高安全设施内的设备和仪器与远程监控位置连接起来,让技术人员能够在不违反安全规定、不引入未经授权人员的情况下,远程操作所需设备。突破OPC 服务器的远程连接难题该项目最初看似是一个常规的 OPC 应用:目标是将高安全性设施中的冷水机(chiller)设备及其 OPC DA 服务器,与远程监控站的两套 SCADA 系统(作为 OPC DA 客户端)连接起来。然而,在实际实施过
    宏集科技 2025-03-27 13:20 120浏览
  • 汽车导航系统市场及应用环境参照调研机构GII的研究报告中的市场预测,全球汽车导航系统市场预计将于 2030年达到472亿美元的市场规模,而2024年至2030年的年复合成长率则为可观的6.7%。汽车导航系统无疑已成为智能汽车不可或缺的重要功能之一。随着人们在日常生活中对汽车导航功能的日渐依赖,一旦出现定位不准确或地图错误等问题,就可能导致车主开错路线,平白浪费更多行车时间,不仅造成行车不便,甚或可能引发交通事故的发生。有鉴于此,如果想要提供消费者完善的使用者体验,在车辆开发阶段便针对汽车导航功能
    百佳泰测试实验室 2025-03-27 14:51 221浏览
  • 六西格玛首先是作为一个量度质量水平的指标,它代表了近乎完美的质量的水平。如果你每天都吃一个苹果,有一间水果店的老板跟你说,他们所卖的苹果,质量达到六西格玛水平,换言之,他们每卖一百万个苹果,只会有3.4个是坏的。你算了一下,发现你如果要从这个店里买到一个坏苹果,需要805年。你会还会选择其他店吗?首先发明六西格玛这个词的人——比尔·史密斯(Bill Smith)他是摩托罗拉(Motorloa)的工程师,在追求这个近乎完美的质量水平的时候,发明了一套方法模型,开始时是MAIC,后来慢慢演变成DMA
    优思学院 2025-03-27 11:47 169浏览
  • 在电子设计中,电磁兼容性(EMC)是确保设备既能抵御外部电磁干扰(EMI),又不会对自身或周围环境产生过量电磁辐射的关键。电容器、电感和磁珠作为三大核心元件,通过不同的机制协同作用,有效抑制电磁干扰。以下是其原理和应用场景的详细解析:1. 电容器:高频噪声的“吸尘器”作用原理:电容器通过“通高频、阻低频”的特性,为高频噪声提供低阻抗路径到地,形成滤波效果。例如,在电源和地之间并联电容,可吸收电源中的高频纹波和瞬态干扰。关键应用场景:电源去耦:在IC电源引脚附近放置0.1μF陶瓷电容,滤除数字电路
    时源芯微 2025-03-27 11:19 186浏览
  • 文/陈昊编辑/cc孙聪颖‍2025 年,作为中国实施制造强国战略第一个十年计划的关键里程碑,被赋予了极为重大的意义。两会政府工作报告清晰且坚定地指出,要全力加速新质生产力的发展进程,推动传统产业全方位向高端化、智能化与绿色化转型。基于此,有代表敏锐提议,中国制造应从前沿技术的应用切入,逐步拓展至产业生态的构建,最终延伸到提升用户体验的维度,打出独树一帜、具有鲜明特色的发展牌。正是在这样至关重要的时代背景之下,于 AWE 2025(中国家电及消费电子博览会)这一备受瞩目的舞台上,高端厨房的中国方案
    华尔街科技眼 2025-03-25 16:10 90浏览
  • 长期以来,智能家居对于大众家庭而言就像空中楼阁一般,华而不实,更有甚者,还将智能家居认定为资本家的营销游戏。商家们举着“智慧家居、智慧办公”的口号,将原本价格亲民、能用几十年的家电器具包装成为了高档商品,而消费者们最终得到的却是家居设备之间缺乏互操作性、不同品牌生态之间互不兼容的碎片化体验。这种早期的生态割裂现象致使消费者们对智能家居兴趣缺失,也造就了“智能家居无用论”的刻板印象。然而,自Matter协议发布之后,“命运的齿轮”开始转动,智能家居中的生态割裂现象与品牌生态之间的隔阂正被基于IP架
    华普微HOPERF 2025-03-27 09:46 133浏览
我要评论
0
6
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦