I2C 从设备

驱动文件

\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之间。

你可能感兴趣的:(驱动之源,linux)