目录

i2c模块框图 ug-1085(ch22)

2、控制器和从设备的设备树

3、查看i2c控制器驱动注册流程:  标准的平台驱动框架

从设备读取温度接口:

读写实质: 最终就是通过client中的adapter->algo读写


基于zynqmp iic控制器

i2c模块框图 ug-1085(ch22)

(a)有两条i2c总线 i2c0 i2c1中断id 49 50; CPU通过APB总线访问i2c控制器 ,i2c控制器通过scl和sda链接外部设备;

(b)中断id: ug-1085(13)中断控制器是 GIC

(c)寄存器和数据流向框图

Cpu通过APB总线访问i2c总线上挂载的外设,通过四个寄存器(控制寄存器、 发送数据寄存器、接收数据寄存器、状态寄存器)控制i2c控制器和外设(从设备)通信;

这些寄存器对应的地址(ug1087)

上述这些寄存器一般都在控制器发生传输初始化时候使用,后面控制器驱动程序中会用到;

2、控制器和从设备的设备树

pd_i2c1: pd-i2c1 {#power-domain-cells = <0x0>;pd-id = <0x26>;
};
i2c1: i2c@ff030000 {compatible = "cdns,i2c-r1p14", "cdns,i2c-r1p10";  /* 匹配控制器驱动 */status = "okay";                        /* 表示本节点是否生效 */clock-frequency = <100000>;               /* scl时钟*/  interrupt-parent = <&gic>;               /*父中断是gic中断控制器*/interrupts = <0 18 4>;                   /* 中断号 18  高电平*/reg = <0x0 0xff030000 0x0 0x1000>;       /* i2c基地址 */#address-cells = <1>;        #size-cells = <0>;power-domains = <&pd_i2c1>;tmp401: tmp401@4c { /* u23 */compatible = "ti,tmp401";reg = <0x4c>;};
};

根据设备树搜索源码: grep “cdns,i2c-r1p” -r | grep -v “dts”

3、查看i2c控制器驱动注册流程:  标准的平台驱动框架

匹配后执行cdns_i2c_probe函数;

probe中需要做的几件事:

  1. 获取设备树资源并使用这些资源初始化i2c寄存器(控制、发送、接收、状态、中断);
  2. 构建一个i2c_adapter,并初始化必须实现成员algo的方法
    1. id->adap.algo = &cdns_i2c_algo;
    2. master_xferfunctionality
  3. 初始化时钟注册中断(因为中断用来收发数据);
    1. devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,DRIVER_NAME, id);
  4. 初始化并向总线注册一个适配器;    i2c_add_adapter(&id->adap);

分析probe函数:

static int cdns_i2c_probe(struct platform_device *pdev)
{struct resource *r_mem;struct cdns_i2c *id;int ret;const struct of_device_id *match;
/* 给struct cdns_i2c 申请空间 内部包含适配器等其他资源 */id = devm_kzalloc(&pdev->dev, sizeof(*id), GFP_KERNEL); id->dev = &pdev->dev;                            /* 保存平台设备 */platform_set_drvdata(pdev, id);                 /* 保存struct cdns_i2c到驱动数据 */        match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);if (match && match->data) {const struct cdns_platform_data *data = match->data;id->quirks = data->quirks;                  //  0}id->pinctrl = devm_pinctrl_get(&pdev->dev);  if (!IS_ERR(id->pinctrl)) {ret = cdns_i2c_init_recovery_info(id, pdev);if (ret)return ret;}
/* 从设备树获取基地址 */r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);id->membase = devm_ioremap_resource(&pdev->dev, r_mem);if (IS_ERR(id->membase))return PTR_ERR(id->membase);
/* 获取中断号 */id->irq = platform_get_irq(pdev, 0);
/* 初始化适配器 */id->adap.owner = THIS_MODULE;id->adap.dev.of_node = pdev->dev.of_node;id->adap.algo = &cdns_i2c_algo;id->adap.timeout = CDNS_I2C_TIMEOUT;id->adap.retries = 3;       /* Default retry value. */id->adap.algo_data = id;id->adap.dev.parent = &pdev->dev;init_completion(&id->xfer_done);snprintf(id->adap.name, sizeof(id->adap.name),
"Cadence I2C at %08lx", (unsigned long)r_mem->start);id->clk = devm_clk_get(&pdev->dev, NULL);ret = clk_prepare_enable(id->clk);pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);pm_runtime_use_autosuspend(id->dev);pm_runtime_set_active(id->dev);pm_runtime_enable(id->dev);id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",&id->i2c_clk);if (ret || (id->i2c_clk > CDNS_I2C_SPEED_MAX))id->i2c_clk = CDNS_I2C_SPEED_DEFAULT;
/* 是指控制寄存器状态 使能ack  7位传输 master模式 */id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
/* 设置时钟 */ret = cdns_i2c_setclk(id->input_clk, id);
/* 注册中断 */ret = devm_request_irq(&pdev->dev, id->irq, cdns_i2c_isr, 0,DRIVER_NAME, id);
/* 设置超时时间 把id->ctrl_reg 写入寄存器    */cdns_i2c_init(id);
/* 注册适配器 */ ret = i2c_add_adapter(&id->adap);if (ret < 0)goto err_clk_dis;dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);return 0;
}

主要看下cdns_i2c_algo 和中断处理函数 cdns_i2c_isr:

static const struct i2c_algorithm cdns_i2c_algo = {

.master_xfer = cdns_i2c_master_xfer,          //i2c-core-base.c中__i2c_transfer会调用

.functionality = cdns_i2c_func,                      //是能够master_xfer接口能够支持的iic驱动种类

};

master_xfer都是在读写接口中调用,被核心层封装在了__i2c_transfer中如下:

zynqmp中cdns_i2c_master_xfer的实现:

这个函数中就可以看到对寄存器的一些操作,控制寄存器 状态寄存器等

static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num){int ret, count;u32 reg;struct cdns_i2c *id = adap->algo_data;bool hold_quirk;ret = pm_runtime_get_sync(id->dev);/* CDNS_I2C_SR_OFFSET = 0x04; 读取0x4状态寄存器 CDNS_I2C_SR_BA: bit 8 总线正在传输中 */if (msgs->len)if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) { //总线正在传输中的话直接返回ret = -EAGAIN;goto out;}hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);     //重复标志/* Set the flag to one when multiple messages are to be    * processed with a repeated start.  */if (num > 1) {   //发送多条消息分支for (count = 0; (count < num - 1 && hold_quirk); count++) {if (msgs[count].flags & I2C_M_RD) {dev_warn(adap->dev.parent,"Can't do repeated start after a receive message\n");ret = -EOPNOTSUPP;goto out;}}id->bus_hold_flag = 1;reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);reg |= CDNS_I2C_CR_HOLD;cdns_i2c_writereg(reg, CDNS_I2C_CR_OFFSET);  // 控制寄存器置位bit 4 } else {     //发送单个id->bus_hold_flag = 0;}/* Process the msg one by one */for (count = 0; count < num; count++, msgs++) {if (count == (num - 1))                  //当最后一次发送的时候释放标志位;释放sclkid->bus_hold_flag = 0;ret = cdns_i2c_process_msg(id, msgs, adap);       //发送消息/* Report the other error interrupts to application */if (id->err_status) {cdns_i2c_master_reset(adap);        //如果发送消息过程中出现了错误则复位,后面看如何复位if (id->err_status & CDNS_I2C_IXR_NACK) {ret = -ENXIO;goto out;}ret = -EIO;goto out;}}ret = num;
out:pm_runtime_mark_last_busy(id->dev);pm_runtime_put_autosuspend(id->dev);return ret;
}

循环当中调用的就是cdns_i2c_process_msg来发送数据:   在这个函数中可以看到主要是读取状态区分读写 和 地址位宽

static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,struct i2c_adapter *adap)
{unsigned long time_left;u32 reg;id->p_msg = msg;                           //保存了要发送的消息到struct cdns_i2c 结构体id->err_status = 0;reinit_completion(&id->xfer_done);                  //把id->xfer_done设置为0/* Check for the TEN Bit mode on each msg */reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);        //读取控制寄存器 0x00if (msg->flags & I2C_M_TEN) {                      //10位地址if (reg & CDNS_I2C_CR_NEA)               cdns_i2c_writereg(reg & ~CDNS_I2C_CR_NEA,CDNS_I2C_CR_OFFSET);} else {                                           //7位地址  if (!(reg & CDNS_I2C_CR_NEA))cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,CDNS_I2C_CR_OFFSET);}/* Check for zero length - Slave monitor mode */if (msg->len == 0)cdns_i2c_slvmon(id);/* Check for the R/W flag on each msg */else if (msg->flags & I2C_M_RD)                    //主机读取从设备的话 I2C_M_RD= 1;cdns_i2c_mrecv(id);                            //接收数据   elsecdns_i2c_msend(id);                            //发送数据/* Wait for the signal of completion */time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout); //等待id->xfer_done 置位if (time_left == 0) {i2c_recover_bus(adap);cdns_i2c_master_reset(adap);dev_err(id->adap.dev.parent, "timeout waiting on completion\n");return -ETIMEDOUT;}cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK,  CDNS_I2C_IDR_OFFSET);/* If it is bus arbitration error, try again */if (id->err_status & CDNS_I2C_IXR_ARB_LOST)return -EAGAIN;return 0;
}

cdns_i2c_mrecv: 一路下来终于到了接收数据的地方 (做了一下删减)

 使能中断cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);

static void cdns_i2c_mrecv(struct cdns_i2c *id)
{unsigned int ctrl_reg;unsigned int isr_status;id->p_recv_buf = id->p_msg->buf;       //保存id->recv_count = id->p_msg->len;/* Put the controller in master receive mode and clear the FIFO */ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);ctrl_reg |= CDNS_I2C_CR_RW | CDNS_I2C_CR_CLR_FIFO;if (id->p_msg->flags & I2C_M_RECV_LEN)id->recv_count = I2C_SMBUS_BLOCK_MAX + 1;id->curr_recv_count = id->recv_count;
检查iic中接收数据的长度比fifo的深度大,需要多次传输;if (id->recv_count > CDNS_I2C_FIFO_DEPTH)    ctrl_reg |= CDNS_I2C_CR_HOLD;cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);//清除中断状态寄存器中的中断: 0x10isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {   //传输大小判断 0x14寄存器cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,  CDNS_I2C_XFER_SIZE_OFFSET);id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;} else {        cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);    }
//中断使能 bit[ 0 1 2 5 6 7 9]置位  0x24中断使能寄存器cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
//从设备地址 写入到 0x08  IIC地址寄存器cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK, CDNS_I2C_ADDR_OFFSET);/* Clear the bus hold flag if bytes to receive is less than FIFO size */if (!id->bus_hold_flag && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&         (id->recv_count <= CDNS_I2C_FIFO_DEPTH))cdns_i2c_clear_bus_hold(id);}

看完这个函数 发现cdns_i2c_mrecv中没有对数据寄存器(0x0C)操作怎么读取数据?? cdns_i2c_process_msg里它使能了中断又等待id->xfer_done置位;搜索下就知道是在中断中传输的数据;中断的处理函数在probe中已经注册好了,还没有分析;

分析中断处理函数:中断函数最后对id->xfer_done做了置位; cdns_i2c_process_msg中等待判断这个标识置位

cdns_i2c_isr中断处理函数:
static irqreturn_t cdns_i2c_isr(int irq, void *ptr){cdns_i2c_master_isr(ptr);return IRQ_HANDLED;
}
static irqreturn_t cdns_i2c_master_isr(void *ptr)  //最终对i2c控制寄存器操作的函数
{unsigned int isr_status, avail_bytes, updatetx;unsigned int bytes_to_send;bool hold_quirk;struct cdns_i2c *id = ptr;/* Signal completion only after everything is updated */int done_flag = 0;irqreturn_t status = IRQ_NONE;
/* 读取中断状态,清空中断状态 */isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/* 检测中断状态 nack表示从机响应,或者主机正在仲裁,直接返回*/if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_ARB_LOST)) {done_flag = 1;        status = IRQ_HANDLED;}updatetx = 0;if (id->recv_count > id->curr_recv_count)updatetx = 1;hold_quirk = (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;/* When receiving, handle data interrupt and completion interrupt */if (id->p_recv_buf &&((isr_status & CDNS_I2C_IXR_COMP) ||(isr_status & CDNS_I2C_IXR_DATA))) {/* 读取数据  先读取状态寄存器 为读取数据有效 fifo深度*/while(cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_RXDV) {if((id->recv_count<CDNS_I2C_FIFO_DEPTH)&&!id->bus_hold_flag)cdns_i2c_clear_bus_hold(id);
/* 读取数据寄存器填充到 p_recv_buf中 */*(id->p_recv_buf)++ = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);id->recv_count--;id->curr_recv_count--;if (cdns_is_holdquirk(id, hold_quirk))  break;}
/*大型数据传输*/if (cdns_is_holdquirk(id, hold_quirk)) {/* wait while fifo is full */while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=(id->curr_recv_count - CDNS_I2C_FIFO_DEPTH));if(((int)(id->recv_count)-CDNS_I2C_FIFO_DEPTH) >
CDNS_I2C_TRANSFER_SIZE){cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,CDNS_I2C_XFER_SIZE_OFFSET);id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE +CDNS_I2C_FIFO_DEPTH;} else {cdns_i2c_writereg(id->recv_count -CDNS_I2C_FIFO_DEPTH,CDNS_I2C_XFER_SIZE_OFFSET);id->curr_recv_count = id->recv_count;}} else if (id->recv_count && !hold_quirk &&!id->curr_recv_count) {/* Set the slave address in address register*/cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,CDNS_I2C_ADDR_OFFSET);if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,CDNS_I2C_XFER_SIZE_OFFSET);id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;} else {cdns_i2c_writereg(id->recv_count,CDNS_I2C_XFER_SIZE_OFFSET);id->curr_recv_count = id->recv_count;}}/* Clear hold (if not repeated start) and signal completion */if ((isr_status & CDNS_I2C_IXR_COMP) && !id->recv_count) {if (!id->bus_hold_flag)cdns_i2c_clear_bus_hold(id);done_flag = 1;}status = IRQ_HANDLED;}/* When sending, handle transfer complete interrupt */if ((isr_status & CDNS_I2C_IXR_COMP) && !id->p_recv_buf) {if (id->send_count) {avail_bytes = CDNS_I2C_FIFO_DEPTH -cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);if (id->send_count > avail_bytes)bytes_to_send = avail_bytes;elsebytes_to_send = id->send_count;while (bytes_to_send--) {cdns_i2c_writereg((*(id->p_send_buf)++),CDNS_I2C_DATA_OFFSET);id->send_count--;}} else {done_flag = 1;}if (!id->send_count && !id->bus_hold_flag)cdns_i2c_clear_bus_hold(id);status = IRQ_HANDLED;}/* Handling Slave monitor mode interrupt */if (isr_status & CDNS_I2C_IXR_SLV_RDY) {unsigned int ctrl_reg;/* Read control register */ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);/* Disable slave monitor mode */ctrl_reg &= ~CDNS_I2C_CR_SLVMON;cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);/* Clear interrupt flag for slvmon mode */cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IDR_OFFSET);done_flag = 1;status = IRQ_HANDLED;}
/* 保存错误标识,在cdns_i2c_process_msg中判断处理 */id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;if (id->err_status)status = IRQ_HANDLED;
/*对数据处理完成 cdns_i2c_process_msg中等待判断这个标识置位 */if (done_flag)complete(&id->xfer_done);return status;
}

流程理一下;

cdns_i2c_probe         通过设备树匹配到probe

获取设备树资源(终端号,i2c基地址  时钟等等);

初始化adapter 结构

初始化alogrithm  id->adap.algo = &cdns_i2c_algo;

注册中断

注册adapter

设备驱动呢怎么使用adapter 和 alogrithm?

简单看个例子:

static const struct i2c_device_id tmp401_id[] = {{ "tmp401", tmp401 },{ "tmp411", tmp411 },
};static struct i2c_driver tmp401_driver = {.class     = I2C_CLASS_HWMON,.driver = {.name    = "tmp401",.of_match_table = of_match_ptr(tmp401_of_match),},.probe     = tmp401_probe,.id_table   = tmp401_id,.detect        = tmp401_detect,.address_list  = normal_i2c,
};

匹配后执行: static int tmp401_probe(struct i2c_client *client,  const struct i2c_device_id *id)

struct i2c_client {
    unsigned short flags;        /* div., see below        */
    unsigned short addr;        /* chip address - NOTE: 7bit    */
                    /* addresses are stored in the    */
                    /* _LOWER_ 7 bits        */
    char name[I2C_NAME_SIZE];
    struct i2c_adapter *adapter;    // 这个不就是我们控制器中初始化的过的嘛用它来产生中断收发数据
    struct device dev;        /* the device structure        */
    int irq;            /* irq issued by device        */
    struct list_head detected;
};

看下probe函数: 做了删减

static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);static const struct attribute_group tmp401_group = {.attrs = tmp401_attributes,
};
static struct attribute *tmp401_attributes[] = {&sensor_dev_attr_temp1_input.dev_attr.attr,NULL
}static int tmp401_probe(struct i2c_client *client,const struct i2c_device_id *id)
{static const char * const names[] = {"TMP401", "TMP411", "TMP431", "TMP432", "TMP435", "TMP461"};struct device *dev = &client->dev;struct device *hwmon_dev;struct tmp401_data *data;int groups = 0, status;data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);if (!data)return -ENOMEM;data->client = client;mutex_init(&data->update_lock);data->kind = id->driver_data;/* Initialize the TMP401 chip */status = tmp401_init_client(data, client);if (status < 0)return status;/* Register sysfs hooks */data->groups[groups++] = &tmp401_group;hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,data, data->groups);if (IS_ERR(hwmon_dev))return PTR_ERR(hwmon_dev);dev_info(dev, "Detected TI %s chip\n", names[data->kind]);return 0;
}

从设备读取温度接口:

static ssize_t show_temp_crit_hyst(struct device *dev,struct device_attribute *devattr, char *buf)
{int temp, index = to_sensor_dev_attr(devattr)->index;struct tmp401_data *data = tmp401_update_device(dev);if (IS_ERR(data))return PTR_ERR(data);mutex_lock(&data->update_lock);temp = tmp401_register_to_temp(data->temp[3][index], data->config);temp -= data->temp_crit_hyst * 1000;mutex_unlock(&data->update_lock);return sprintf(buf, "%d\n", temp);
}static struct tmp401_data *tmp401_update_device(struct device *dev)
{struct tmp401_data *data = dev_get_drvdata(dev);struct i2c_client *client = data->client;struct tmp401_data *ret = data;int i, val;unsigned long next_update;mutex_lock(&data->update_lock);next_update = data->last_updated +msecs_to_jiffies(data->update_interval);if (time_after(jiffies, next_update) || !data->valid) {if (data->kind != tmp432) {/** The driver uses the TMP432 status format internally.* Convert status to TMP432 format for other chips.*/val = i2c_smbus_read_byte_data(client, TMP401_STATUS);if (val < 0) {ret = ERR_PTR(val);goto abort;}data->status[0] =(val & TMP401_STATUS_REMOTE_OPEN) >> 1;data->status[1] =((val & TMP401_STATUS_REMOTE_LOW) >> 2) |((val & TMP401_STATUS_LOCAL_LOW) >> 5);data->status[2] =((val & TMP401_STATUS_REMOTE_HIGH) >> 3) |((val & TMP401_STATUS_LOCAL_HIGH) >> 6);data->status[3] = val & (TMP401_STATUS_LOCAL_CRIT| TMP401_STATUS_REMOTE_CRIT);} else {for (i = 0; i < ARRAY_SIZE(data->status); i++) {val = i2c_smbus_read_byte_data(client,TMP432_STATUS_REG[i]);if (val < 0) {ret = ERR_PTR(val);goto abort;}data->status[i] = val;}}val = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);if (val < 0) {ret = ERR_PTR(val);goto abort;}data->config = val;val = tmp401_update_device_reg16(client, data);if (val < 0) {ret = ERR_PTR(val);goto abort;}val = i2c_smbus_read_byte_data(client, TMP401_TEMP_CRIT_HYST);if (val < 0) {ret = ERR_PTR(val);goto abort;}data->temp_crit_hyst = val;data->last_updated = jiffies;data->valid = 1;}abort:mutex_unlock(&data->update_lock);return ret;
}

读写实质: 最终就是通过client中的adapter->algo读写

linux i2c 驱动二 IIC控制器相关推荐

  1. Linux i2c驱动框架分析 (二)

    Linux i2c驱动框架分析 (一) Linux i2c驱动框架分析 (二) Linux i2c驱动框架分析 (三) 通用i2c设备驱动分析 i2c core i2c核心(drivers/i2c/i ...

  2. linux I2C 驱动

    原文地址: http://hello2mao.github.io/2015/12/02/Linux_I2C_driver.html 目录 一.LinuxI2C驱动--概述 1.1 写在前面 1.2 I ...

  3. 【正点原子MP157连载】第四十章 Linux I2C驱动实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  4. Linux I2C驱动框架(超详细)

    Linux I2C驱动框架 文章目录 Linux I2C驱动框架 一.几个重要的对象 1.I2C总线 2.I2C驱动 3.I2C设备 4.I2C设配器 小结 二.内核源码分析 1.注册I2C驱动 2. ...

  5. 《linux设备驱动开发详解》笔记——15 linux i2c驱动

    <linux设备驱动开发详解>笔记--15 linux i2c驱动 15.1 总体结构 如下图,i2c驱动分为如下几个重要模块 核心层core,完成i2c总线.设备.驱动模型,对用户提供s ...

  6. 【正点原子Linux连载】第六十一章 Linux I2C驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  7. STM32MP157驱动开发——Linux I2C驱动

    相关文章:正点原子教程第四十章--Linux I2C驱动实验 0.前言   为了简化笔记的编写以及降低工作量,本节开始相关的基础知识部分通过引入原子哥的教材链接来完成,有兴趣的可以进入学习.   上一 ...

  8. linux i2c 内核初始io,linux I2C驱动移植

    linux I2C驱动移植 I²C总线仅使用SCL,SDA两根信号线实现设备间的数据交互,被广泛应用于微控制领域芯片与芯片之间的通信,如EEPROM,实时时钟,小型LCD等与CPU之间的通信. I2C ...

  9. linux I2C驱动架构解析

    I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...

最新文章

  1. 地址池命令 思科理由_思科互联网络操作系统 ——路由器接口
  2. icmp 报文中的进程号
  3. 初等数论四大基本定理
  4. 南阳oj92--图像有用区域(Bfs)
  5. java面试要点---Spring体系知识点复习,IOC,AOP---随时更新
  6. python海龟作图好看图案_python海龟绘图,其实python也很强大,这些技能你知道吗?...
  7. Android P2P语音通话实现 【转】http://macleo.iteye.com/blog/1707455
  8. 改造MFC程序,使原来不支持winsocket的工程支持winsocket
  9. java上下文的作用_Spring中的应用程序上下文有什么作用? - java
  10. 本地Git仓库关联Github项目
  11. matlab查表svpwm,SVPWM的查表生成方式代码
  12. Unity3D 保姆级安装教程与收费方案和版本、下载地址,看不会算我输
  13. 2020抖音无人直播技术:最新不封号无人直播的操作方法详细介绍
  14. 手撕《现代信号处理》——通俗易懂的现代信号处理章节详解集合
  15. 基于matlab的电池管理系统开发,基于MATLAB的锂电池组均衡仿真系统设计
  16. cpu超线程优缺点_今天看了下百度百科!看到了超线程的优缺点啊!转!
  17. sqlserver2012 查询远程数据库
  18. 毕业三年 /****************************转载**************HUST机械学长****************************/
  19. (翻译)常见问题解答(Frequently Asked Questions(FAQ))
  20. 华为推出“换皮肤”服务

热门文章

  1. win10使用easybcd误删引导进不去系统解决办法
  2. 基本正则表达式与扩展正则表达式
  3. NXP LS1046A及飞腾新四核 FT2004 PCIE EP端LINUX设备驱动开发
  4. 【HTML CSS】个人整理笔记
  5. 计算机虚拟仿真专业,虚拟仿真技术在计算机专业网络基础课程教学中的应用
  6. 泛微数字化项目风险管理平台:预警、评估、上报一体化
  7. OC中常用的数学函数以及浮点处理函数
  8. 吃鸡进游戏显示错误服务器繁忙,绝地求生游戏崩溃进不去怎么办 服务器炸了繁忙解决方法...
  9. 如何提高串口通信速度
  10. 还在辛苦一个个投简历?试试用web自动化海投