驱动文件
\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 1
if(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 1
if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
&val))
dev_vdbg(dev->dev, "Byte %X acked!", val);
#endif
#if 0
if (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之间。