驱动文件
\drivers\i2c\busses\i2c-designware-slave.c I2C控制器
\drivers\i2c\i2c-slave-eeprom.c I2C设备示例
由于作为从,接收主写过来的数据时总是少一个,因为分析相关。
控制器驱动中断
加打印,很多时候不能打印出来。如下中断中根据中断类型,会设置不同的event类型,调用到设备的接口。例如类型:
事件类型 | 功能 |
I2C_SLAVE_WRITE_REQUESTED | 收到写请求 |
I2C_SLAVE_WRITE_RECEIVED | 收到地址或者数据 |
这里特别说明: 收到写请求,中断增加1,收到地址中断增加1,收到数据,中断增加1.
例如,主设备发送了1个字符,则在此从设备上收到了3个中断。
static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
{u32 raw_stat, stat, enabled;u8 val, slave_activity;stat = dw_readl(dev, DW_IC_INTR_STAT);enabled = dw_readl(dev, DW_IC_ENABLE);raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);slave_activity = ((dw_readl(dev, DW_IC_STATUS) &DW_IC_STATUS_SLAVE_ACTIVITY) >> 6);g_test_count= g_test_count+1;#if 1if(g_test_count>6){// printk("all intr num :%d\n",g_test_count);}#endif if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY) || !dev->slave){printk("abnor return intr num :%d,0x%x\n",enabled,raw_stat);return 0;}dev_dbg(dev->dev,"%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",enabled, slave_activity, raw_stat, stat);// printk( "%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",// enabled, slave_activity, raw_stat, stat);if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET)){printk( "write begin!");i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);}if (stat & DW_IC_INTR_RD_REQ) {if (slave_activity) {if (stat & DW_IC_INTR_RX_FULL) {val = dw_readl(dev, DW_IC_DATA_CMD);if (!i2c_slave_event(dev->slave,I2C_SLAVE_WRITE_RECEIVED,&val)) {dev_vdbg(dev->dev, "Byte %X acked!",val);if (g_test_count>5){printk( "Byte %X acked! int num : %d\n",val,g_test_count);}}dw_readl(dev, DW_IC_CLR_RD_REQ);stat = i2c_dw_read_clear_intrbits_slave(dev);} else {dw_readl(dev, DW_IC_CLR_RD_REQ);dw_readl(dev, DW_IC_CLR_RX_UNDER);stat = i2c_dw_read_clear_intrbits_slave(dev);}if (!i2c_slave_event(dev->slave,I2C_SLAVE_READ_REQUESTED,&val))dw_writel(dev, val, DW_IC_DATA_CMD);}}if (stat & DW_IC_INTR_RX_DONE) {if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,&val))dw_readl(dev, DW_IC_CLR_RX_DONE);i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);stat = i2c_dw_read_clear_intrbits_slave(dev);printk("abnormal done RX DONE\n");return 1;}if (stat & DW_IC_INTR_RX_FULL) {val = dw_readl(dev, DW_IC_DATA_CMD);#if 1if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,&val))dev_vdbg(dev->dev, "Byte %X acked!", val);#endif #if 0if (g_test_count>6){printk( "xhj Byte %X acked! int num : %d\n", val,g_test_count);}#endif } else {i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);stat = i2c_dw_read_clear_intrbits_slave(dev);}return 1;
}
i2c_slave_event
直接调用slave 注册时的回调接口,没什么特别的。
static inline int i2c_slave_event(struct i2c_client *client,enum i2c_slave_event event, u8 *val)
{return client->slave_cb(client, event, val);
}
设备驱动
i2c_slave_eeprom_slave_cb
设备接收数据
即上述的salve_cb接口
对于写来说:
1) 先进入 ,表示再一次的I2C START
case I2C_SLAVE_WRITE_REQUESTED:req_test++;eeprom->first_write = true;
2) 然后写buffer
switch (event) {case I2C_SLAVE_WRITE_RECEIVED:if (eeprom->first_write) {eeprom->buffer_idx = *val;eeprom->first_write = false;addr_test++;} else {spin_lock(&eeprom->buffer_lock);testbuf[eeprom->buffer_idx]=*val;eeprom->buffer[eeprom->buffer_idx++] = *val;data_test++;spin_unlock(&eeprom->buffer_lock);}break;
用户接口
i2c_slave_eeprom_bin_read
i2c_slave_eeprom_bin_write 即通过sys下的设备节点访问此设备。
static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj,struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{struct eeprom_data *eeprom;unsigned long flags;int i =0;eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj));spin_lock_irqsave(&eeprom->buffer_lock, flags);memcpy(buf, &eeprom->buffer[off], count);spin_unlock_irqrestore(&eeprom->buffer_lock, flags);printk("recv data num :%d,addr num %d,req_num %d\n",data_test,addr_test,req_test);for(i=0;i<10;i++){printk("0x%x\n", testbuf[i]);}return count;
}
这里
spin_lock_irqsave(&eeprom->buffer_lock, flags);而在中断里面的锁,则采用 spin_lock(&eeprom->buffer_lock);
为何不统一采用一个锁接口?
信号测量
1)I2C START为下降沿触发。因而示波器要进行同样的配置。
2) 频率为100K时,每格设置的大概时间 1/100k =20us。其他通信接口类似的算法。
3) 设置普通触发,一次抓取多个数据。
4) 触发电平trigger level 要设置在0-3.3V之间。