S5PV210开发 -- I2C 你知道多少?(二)

如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78782558

上一篇主要是介绍了下芯片手册 I2C 部分,都应该看些什么,以及上拉电阻取值和传输速率模式选择。

这一篇该来点程序了,首先以 AT24C02 (EEPROM)为基础介绍一下I2C设备驱动编程,然后以 MT9P031 为基础介绍 LINUX 下内核配置。 最后是 MPU6050 为基础的单片机下 I2C 通信程序。

一、I2C设备驱动编程

该部分我会以嵌入式Linux软硬件开发详解第 12 章,和Linux设备驱动开发详解第 15 章为参考来展开。

(1)I2C 设备驱动程序结构

1. I2C设备驱动层次结构

从系统的角度来看,Linux中 I2C 设备程序所处的位置如下图所示。

I2C设备驱动程序包含总线驱动层和设备驱动层两部分。设备驱动层为应用的 open、read、write 等提供相对应的接口函数,但是涉及具体的硬件操作,例如寄存器的操作等,则由总线驱动层来完成。

一般来说,针对具体的硬件平台,生产厂家通常已经写好总线驱动层相关内容,用户只要在内核配置选项中选择就可以了。

 

 

进行上述操作即为 S5PV210 选择了总线驱动层的代码,而程序设计者只需要编写设备驱动层的代码。

2. I2C 设备驱动程序

在设备驱动层,Linux内核对 I2C 设备驱动代码的组织符合 Linux 的设备驱动模型。如下图所示:

Linux 内核提供了 i2c_bus_type 总线来管理设备和驱动,左侧为多个 I2C 设备组成的设备链表,以 i2c_client 结构体来表示各个设备;右侧为适用于多个具体 I2C 设备驱动程序组成的驱动链表,以 i2c_driver 结构体来表示不同的驱动程序,下面我们对其进行简要的介绍。

这些结构体都是在 linux-2.6.32.17/include/linux/i2c.h 头文件下定义的,可自行查看相关源码。

1)I2C 设备

描述 I2C 设备的结构体为 i2c_client,其代码如下:

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;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};

I2C 设备结构体 i2c_client 是描述 I2C 设备的基本模板,驱动程序的设备结构应该包含该结构。

2)I2C 驱动

描述 I2C 驱动的结构体为 i2c_driver,其代码如下。

struct i2c_driver {
	unsigned int class;

	/* Notifies the driver that a new bus has appeared or is about to be
	 * removed. You should avoid using this if you can, it will probably
	 * be removed in a near future.
	 */
	int (*attach_adapter)(struct i2c_adapter *);
	int (*detach_adapter)(struct i2c_adapter *);

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	/* driver model interfaces that don't relate to enumeration  */
	void (*shutdown)(struct i2c_client *);
	int (*suspend)(struct i2c_client *, pm_message_t mesg);
	int (*resume)(struct i2c_client *);

	/* 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 *, int kind, struct i2c_board_info *);
	const struct i2c_client_address_data *address_data;
	struct list_head clients;
};

i2c_driver 结构体中,probe 成员为加载 I2C 驱动程序时探测 I2C 设备所调用的函数,而 remove 函数实现相反的功能。i2c_device_id 结构体代码如下。

struct i2c_device_id {
	char name[I2C_NAME_SIZE];
	kernel_ulong_t driver_data	/* Data private to the driver */
			__attribute__((aligned(sizeof(kernel_ulong_t))));
};

代码中 name 成员保存设备的名称,如“at24c02”等。

i2c_driver 结构体成员中我们只需要初始化 probe 和 remove 就够了,其他的函数都是可选的。

3)I2C 总线

描述I2C总线的结构体为i2c_bus_type,其代码如下。

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
	.suspend	= i2c_device_suspend,
	.resume		= i2c_device_resume,
};

i2c_bus_type 总线进行设备和驱动程序的匹配,依靠的是其 match 成员函数,其代码如下。

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

该函数调用了 i2c_match_id 函数,i2c_match_id 函数的内容如下。

static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
						const struct i2c_client *client)
{
	while (id->name[0]) {
		if (strcmp(client->name, id->name) == 0)
			return id;
		id++;
	}
	return NULL;
}

我们可以看出,match 函数实质是监测 client 描述的设备名称和驱动程序对应的设备名是否一致,如果一致,即找到了和设备对匹配的驱动程序。

上述为 2.6.35 版本 Linux 内核下的 I2C 设备驱动的框架,这里还涉及一些其他的结构体和函数,我们在示例中进行讲解。

(2)AT24C02 设备驱动程序

1. AT24C02 设备驱动程序

S5PV210 开发板具有一片 AMTEL 公司的 I2C 接口 EEPROM 芯片,型号是 AT24C02,其驱动程序代码如下。

1)AT24Cxx 设备代码

//设备驱动 at24cxx_dev.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>

//声明i2c_board_info 结构体
static struct i2c_board_info at24cxx_info = {
  //设备名 和 设备地址
	I2C_BOARD_INFO ("at24c02", 0x50);
}

//初始化 i2c_client
static struct i2c_client *at24cxx_client;

static int at24cxx_dev_init (void)
{
  //获取 I2C 总线适配器
	struct i2c_adapter *i2c_adap;
	//获取0号适配器
	i2c_adap = i2c_get_adapter (0);
	//将AT24CXX 加入0号适配器对应的总线管理设备链表中
	at24cxx_client = i2c_new_device (i2c_adap, &at24cxx_info);
	i2c_put_adapter (i2c_adap);

	return 0;
}

static void at24cxx_dev_exit (void)
{
	i2c_unregister_device (at24cxx_client);
}

module_init (at24cxx_dev_init);
module_exit (at24cxx_dev_exit);
MODULE_LICENSE ("GPL");

2)AT24Cxx驱动代码

//驱动代码 at24cxx_drv.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static int major;
static struct class *class;
static struct i2c_client *at24cxx_client;
static ssize_t at24cxx_read(struct file * file, char __user *buf, size_t count, loff_t *off)
{
	unsigned char addr, data;
	copy_from_user(&addr, buf, 1);
	data = i2c_smbus_read_byte_data(at24cxx_client, addr);
	copy_to_user(buf, &data, 1);
	return 1;
}
static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
{
	unsigned char ker_buf[2];
	unsigned char addr, data;
	copy_from_user(ker_buf, buf, 2);
	addr = ker_buf[0];
	data = ker_buf[1];
	printk("addr = 0x%02x, data = 0x%02x\n", addr, data);
	if (!i2c_smbus_write_byte_data(at24cxx_client, addr, data))
		return 2;
	else
		return -EIO;
}
static struct file_operations at24cxx_fops = {
	.owner = THIS_MODULE,
	.read = at24cxx_read,
	.write = at24cxx_write,
};
static int __devinit at24cxx_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
	at24cxx_client = client;
	//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "at24cxx", &at24cxx_fops);
	class = class_create(THIS_MODULE, "at24cxx");
	device_create(class, NULL, MKDEV(major, 0), NULL, "at24cxx");
	return 0;
}
static int __devexit at24cxx_remove(struct i2c_client *client)
{
	//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(class, MKDEV(major, 0));
	class_destroy(class);
	unregister_chrdev(major, "at24cxx");
	return 0;
}
static const struct i2c_device_id at24cxx_id_table[] = {
	{ "at24c08", 0 },
	{}
};
/* 分配/设置i2c_driver */
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name = "100ask",
		.owner = THIS_MODULE,
	},
	.probe = at24cxx_probe,
	.remove = __devexit_p(at24cxx_remove),
	.id_table = at24cxx_id_table,
};
static int at24cxx_drv_init(void)
{
	/*  注册i2c_driver */
	i2c_add_driver(&at24cxx_driver);
	return 0;
}
static void at24cxx_drv_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}
module_init(at24cxx_drv_init);
module_exit(at24cxx_drv_exit);
MODULE_LICENSE("GPL");

3)AT24Cxx测试程序

//测试程序 i2c_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* i2c_test r addr
 i2c_test w addr val */
void print_usage(char *file)
{
	printf("%s r addr\n", file);
	printf("%s w addr val\n", file);
}
int main(int argc, char **argv)
{
	int fd;
	unsigned char buf[2];
	if ((argc != 3) && (argc != 4))
	{
		print_usage(argv[0]);
		return -1;
	}
	fd = open("/dev/at24cxx", O_RDWR);
	if (fd < 0)
	{
		printf("cant open /dev/at24cxx\n");
		return -1;
	}
	if (strcmp(argv[1], "r") == 0)
	{
		buf[0] = strtoul(argv[2], NULL, 0);
		read(fd, buf, 1);
		printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]);
	}
	else if ((strcmp(argv[1], "w") == 0) && (argc == 4))
	{
		buf[0] = strtoul(argv[2], NULL, 0);
		buf[1] = strtoul(argv[3], NULL, 0);
		if (write(fd, buf, 2) != 2)
			printf("write err, addr = 0xx, data = 0xx\n", buf[0], buf[1]);
	}
	else
	{
		print_usage(argv[0]);
		return -1;
	}
	return 0;
}

4)Makefile

ifneq ($(KERNELRELEASE),)
    obj-m += at24cxx_drv.o
else
    KERNELDIR=/opt/kernel
all:
	PWD=$(shell pwd)  
	$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions module* Module*
endif

5)开发板测试

执行 make 生成驱动 at24cxx_drv.ko

# ls
at24cxx_dev.c  at24cxx_drv.ko     at24cxx_drv.mod.o  built-in.o  Makefile   modules.order
at24cxx_drv.c  at24cxx_drv.mod.c  at24cxx_drv.o      i2c_test.c  Makefile~  Module.symvers

在开发板新建一个 driver 文件夹,使用 tftp 工具将其拷贝到开发板 /driver 目录下

tftp -g -r at24cxx_drv.ko 192.168.x.xx

加载驱动:

insmod /driver/at24c02_drv.ko

 

然后编译测试文件 i2c_test.c 生成可执行文件 i2c_test

# arm-none-linux-gnueabi-gcc i2c_test.c -o i2c_test 

其中这里如果对于交叉编译不熟悉的人会出现问题,line 1: syntax error: unexpected word (expecting ")") 
解决,参看:S5PV210开发 -- 交叉编译器

最后,拷贝到开发板执行 ./i2c_test

 

因为编译kernel 和 运行的内核版本不一致,加载驱动时会出现问题:

# insmod at24cxx_drv.ko 
[  188.062254] at24cxx_drv: version magic '2.6.35.7-Concenwit preempt mod_unload ARMv7 ' 
should be '2.6.35.7 preempt mod_unload ARMv7 '
insmod: can't insert 'at24cxx_drv.ko': invalid module format

解决方法:

参看:内核模块编译怎样绕过insmod时的版本检查

(1)执行 modinfo 命令,先看一下 at24cxx_drv.ko 的信息。

# modinfo at24cxx_drv.ko 
filename:       at24cxx_drv.ko
license:        GPL
depends:        
vermagic:       2.6.35.7-Concenwit preempt mod_unload ARMv7 

(2)修改 kernel 的 vermagic,再重新编译设备驱动

vermagic 的第一个值 2.6.35.7-Concenwit 是由这 kernel/include/generated/utsrelease.h 里的 UTS_RELEASE 所定义。(可能utsrelease.h头文件位置不一样,使用find指令自己搜一下)。


之后再由 kernel/include/linux/vermagic.h 去组合出 VERMAGIC_STRING,也就是 kernel 的vermagic。

所以,我们只要把 UTS_RELEASE 改成我们的数字即可,当然若是懒得去尝试组合后的字串,也可以直接将VERMAGIC_STRING改成你要的字串。
建议修改完 vermagic.h,  utsrelease.h 后,还是重新编译一遍kernel,比较保险。

(3)操作

我是将utsrelease.h 里的 UTS_RELEASE 改为 #define UTS_RELEASE "2.6.35.7"(未重新编译内核...)

重新执行 make 生成驱动 at24cxx_drv.ko,使用 modinfo 指令查看 at24cxx_drv.ko 信息。

# modinfo at24cxx_drv.ko 
filename:       at24cxx_drv.ko
license:        GPL
depends:        
vermagic:       2.6.35.7 preempt mod_unload ARMv7 

OK,可以看到符合开发板内核版本要求了,此时可加载驱动。

 

我现在的开发板上没有 AT24C02 芯片没法测试,但是这个方法是没有问题的。

到此,将I2C设备驱动,讲完了。

如果对于I2C核心,I2C总线驱动,I2C设备驱动用到的结构体感兴趣。参看:Linux 设备驱动篇之I2c设备驱动

(3)编写I2C应用程序

很多 I2C 设备驱动是不需要自己写的,内核配置里都是有的。更多的工作是怎么调用设备驱动了。

参看:i2c驱动之i2c-dev驱动

验证i2c应用程序:

/* 
//作者:王磊 
//日期:2013.11.17 
//文件功能:实现ioctl函数调用,并操作i2c设备/dev/i2c/0进行读写数据 
//可以用i2c -r来检验数据是否已写入 
*/  
#include<stdio.h>  
#include<linux/types.h>  
#include<fcntl.h>  
#include<unistd.h>  
#include<stdlib.h>  
#include<sys/types.h>  
#include<sys/ioctl.h>  
#include<errno.h>  
#include<assert.h>  
#include<string.h>  
#include<linux/i2c.h>  
#include<linux/i2c-dev.h>  
  
int main(int argc, char** argv)  
{  
    struct i2c_rdwr_ioctl_data work_queue;  
  
    unsigned int slave_address,reg_address,dat;  
    unsigned int fd;  
    int ret;  
    char select;  
  
    fd=open("/dev/i2c/0",O_RDWR);  
    if(!fd)  
    {  
        printf("error on opening the device file\n");  
        exit(1);  
    }  
    ioctl(fd,I2C_TIMEOUT,2);//超时时间  
    ioctl(fd,I2C_RETRIES,1);//重复次数  
      
    //nmsgs决定了有多少start信号,一个msgs对应一个start信号,但这个start信号又不能适用于repeat start  
    //在nmsg个信号结束后总线会产生一个stop  
    work_queue.nmsgs = 1;  
    work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(work_queue.msgs));  
    if(!work_queue.msgs)  
    {  
        printf("memory alloc failed");  
        close(fd);  
        exit(1);  
    }  
  
    slave_address = 0x50;//24c08的访问地址是101000b  
    printf("please select:w or r?\n");  
    scanf("%c", &select);  
    if('w' == select)  
    {  
        printf("please input:address,dat?(example:0x00,0x00)\n");  
        scanf("%x,%x", &reg_address, &dat);  
        //往i2c里面写数据  
        printf("began to write\n");  
        work_queue.nmsgs  = 1;    
        (work_queue.msgs[0]).len = 2;//buf的长度  
        (work_queue.msgs[0]).flags = 0;//write  
        (work_queue.msgs[0]).addr = slave_address;//设备地址  
        (work_queue.msgs[0]).buf = (unsigned char *)malloc(2);  
        (work_queue.msgs[0]).buf[0] = reg_address;//写的地址  
        (work_queue.msgs[0]).buf[1] = dat;//你要写的数据  
  
        ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
        if(ret < 0)  
            printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
    }  
    else if('r' == select)  
    {  
        printf("please input:address?(example:0x00)\n");  
        scanf("%x", &reg_address);  
        //从i2c里面读出数据  
        printf("began to read:");  
        work_queue.nmsgs  = 1;  
        //先设定一下地址     
        (work_queue.msgs[0]).flags = 0;//write  
        (work_queue.msgs[0]).addr = slave_address;  
        (work_queue.msgs[0]).len = 1;  
        (work_queue.msgs[0]).buf = (unsigned char *)malloc(1);  
        (work_queue.msgs[0]).buf[0] = reg_address;//因为上面buf已经分配过了  
        ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
        if(ret < 0)  
            printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
  
    //因为i2c-dev不支持repeat start,所以只能将读数据操作中的写地址和读数据分为两次消息。     
        //然后从刚才设定的地址处读  
        work_queue.nmsgs  = 1;  
        (work_queue.msgs[0]).flags = I2C_M_RD;  
        (work_queue.msgs[0]).addr = slave_address;  
        (work_queue.msgs[0]).len = 1;  
        (work_queue.msgs[0]).buf[0] = 0;//初始化读缓冲  
        ret = ioctl(fd, I2C_RDWR, (unsigned long)&work_queue);  
        if(ret < 0)  
            printf("error during I2C_RDWR ioctl with error code %d\n", ret);  
        printf("reg_address=0x%2x,dat=0x%2x\n", reg_address, work_queue.msgs[0].buf[0]);  
    }  
    close(fd);  
    free((work_queue.msgs[0]).buf);  
    free(work_queue.msgs);  
    return 0;   
      
}  

 

再有培训时所做项目,有一个利用 eeprom 显示当前的软件和硬件版本号信息功能。

这里就不详讲了,直接给出项目工程,仅供参考。

下载:eeprom 显示当前软硬件版本号项目

二、LINUX 下内核配置

(1)内核配置

DM368开发时,有用到MT9P031,它是I2C通信。So,现在就以 MT9P031 为基础介绍 LINUX 下内核配置。

执行:dvsdk_dm368_4_02_00_06/psp/linux-2.6.32.17# make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- menuconfig

支持 mt9p031

Device Drivers  --->    
<*> Multimedia support  --->   
[*]   Video capture adapters  --->  
<*>   mt9p031 support    

 

支持 DaVinci I2C 设备

 Device Drivers  --->      
<*> I2C support  --->     
I2C Hardware Bus support  --->        
<*> DaVinci I2C driver  

当然,如果是DS1682、TSL2550 芯片还需要如下配置

 Device Drivers  --->      
<*> I2C support  --->    

Miscellaneous I2C Chip support  --->  

< > Dallas DS1682 Total Elapsed Time Recorder with Alarm        

          

其中,需要了解:

进入Device Drivers目录,选择 I2C Support,表示编译 I2C 驱动模块,会将 i2c-core.c 编译成模块文件 i2c-core.o

选择模块化编译 I2C device interface "*",则会将 i2c-dev.c 编译成 i2c-dev.o

选择 DaVinci I2C driver 则会将 mt9p031.c 编译成 mt9p031.o 

 


 

i2c-core.c、i2c-dev.c、mt9p031.c 这三个程序,有必要看一下。

(2)查看 board-dm365-evm.c 其支持 mt9p031 

dvsdk_dm368_4_02_00_06/psp/linux-2.6.32.17/arch/arm/mach-davinci# vi board-dm365-evm.c 

#define V4L2_STD_MT9P031_STD_ALL  (V4L2_STD_525_60\
	|V4L2_STD_625_50|V4L2_STD_525P_60\
	|V4L2_STD_625P_50|V4L2_STD_720P_30\
   |V4L2_STD_720P_50|V4L2_STD_720P_60\
   |V4L2_STD_1080I_30|V4L2_STD_1080I_50\
	|V4L2_STD_1080I_60|V4L2_STD_1080P_30\
   |V4L2_STD_1080P_50|V4L2_STD_1080P_60)

/* Input available at the mt9p031 */
static struct v4l2_input mt9p031_inputs[] = {
	{
		.index = 0,
		.name = "Camera",
		.type = V4L2_INPUT_TYPE_CAMERA,
      .std = V4L2_STD_MT9P031_STD_ALL,
	}
};
static struct vpfe_subdev_info vpfe_sub_devs[] = {
#ifdef CONFIG_VIDEO_TVP5150
	{
		.module_name = "tvp5150",
		.grp_id = VPFE_SUBDEV_TVP5150,
		.num_inputs = ARRAY_SIZE(tvp5150_inputs),
		.inputs = tvp5150_inputs,
		.routes = tvp5150_routes,
		.can_route = 1,
		.ccdc_if_params = {
			.if_type = VPFE_BT656,
			.hdpol = VPFE_PINPOL_POSITIVE,
			.vdpol = VPFE_PINPOL_POSITIVE,
		},
		.board_info = {
			//I2C_BOARD_INFO("tvp5150", 0xba),
			I2C_BOARD_INFO("tvp5150", 0x5d),
			//.platform_data = &tvp5150_pdata,
		},
	},
#else
	#ifdef CONFIG_VIDEO_TVP514X	
	{
		.module_name = "tvp5146",
		.grp_id = VPFE_SUBDEV_TVP5146,
		.num_inputs = ARRAY_SIZE(tvp5146_inputs),
		.inputs = tvp5146_inputs,
		.routes = tvp5146_routes,
		.can_route = 1,
		.ccdc_if_params = {
			.if_type = VPFE_BT656,
			.hdpol = VPFE_PINPOL_POSITIVE,
			.vdpol = VPFE_PINPOL_POSITIVE,
		},
		.board_info = {
			I2C_BOARD_INFO("tvp5146", 0x5d),
			.platform_data = &tvp5146_pdata,
		},
	},
	#endif
#endif

	/*
	{
		.module_name = "tvp7002",
		.grp_id = VPFE_SUBDEV_TVP7002,
		.num_inputs = ARRAY_SIZE(tvp7002_inputs),
		.inputs = tvp7002_inputs,
		.ccdc_if_params = {
			.if_type = VPFE_BT1120,
			.hdpol = VPFE_PINPOL_POSITIVE,
			.vdpol = VPFE_PINPOL_POSITIVE,
		},
		.board_info = {
			I2C_BOARD_INFO("tvp7002", 0x5c),
			.platform_data = &tvp7002_pdata,
		},
	},*/
	{
		.module_name = "ths7353",
		.grp_id = VPFE_SUBDEV_TVP7002,
		.board_info = {
			I2C_BOARD_INFO("ths7353", 0x2e),
		},
	},
	{
		.module_name = "mt9p031",
		.is_camera = 1,
		.grp_id = VPFE_SUBDEV_MT9P031,
		.num_inputs = ARRAY_SIZE(mt9p031_inputs),
		.inputs = mt9p031_inputs,
		.ccdc_if_params = {
			.if_type = VPFE_RAW_BAYER,
			.hdpol = VPFE_PINPOL_POSITIVE,
			.vdpol = VPFE_PINPOL_POSITIVE,
		},
		.board_info = {
			I2C_BOARD_INFO("mt9p031", 0x5d),
			/* this is for PCLK rising edge */
			.platform_data = (void *)1,
		},
	},
#ifdef CONFIG_VIDEO_TVP5158

       {										
                .module_name       = "I2C_JG",
                .num_inputs = ARRAY_SIZE(tvp5158_inputs_2),
                .inputs = tvp5158_inputs_2,
                .board_info = {
                        I2C_BOARD_INFO("tvp5158", 0x5F),
                        .platform_data = &tvp5158_pdata,
                },
                .can_route = 0,
                .ccdc_if_params = {
                        .if_type = VPFE_BT656,
                        .hdpol = VPFE_PINPOL_POSITIVE,
                        .vdpol = VPFE_PINPOL_POSITIVE,
                },
        },

        {
                .module_name       = "I2C_IG",
		.num_inputs = ARRAY_SIZE(tvp5158_inputs_1),
                .inputs = tvp5158_inputs_1,
                .board_info = {
                         I2C_BOARD_INFO("tvp5158", 0x5B), 
                        .platform_data = &tvp5158_pdata,
                },
                .can_route = 0,
                .ccdc_if_params = {
                        .if_type = VPFE_BT656,
                        .hdpol = VPFE_PINPOL_POSITIVE,
                        .vdpol = VPFE_PINPOL_POSITIVE,
                },
        },
 
#endif
};

(3)启动信息

如上配置OK,编译内核。然后启动信息中会出现如下信息。说明相机准备就绪,可以进行编解码开发了。

mt9p031 1-005d: Detected a MT9P031 chip ID 1801
mt9p031 1-005d: mt9p031 1-005d decoder driver registered !!
vpfe-capture vpfe-capture: v4l2 sub device mt9p031 registered
vpfe_register_ccdc_device: DM365 ISIF
DM365 ISIF is registered with vpfe.

三、单片机下I2C通信

这部分已经讲过了,在此不再重复。

参看:MPU6050开发 -- 进阶之I2C/SPI通信协议

参看:MPU6050开发 -- 在 C52 单片机上测试

参看:MPU6050开发 -- 测试程序分析

上面这三篇文章,除了介绍MPU6050芯片手册里的I2C协议,其中的程序也可当做在单片机上I2C测试程序的示例。

或者参看:i2c例程 简单易懂

/*******************************************************************
一、程序说明:
1, 24LC02器件地址是1010000R/W.
2, 数组写入24LC02采取页写方式.
3, 数组code从24LC02读出时采取自由读方式.
4, 采用4.00M晶体。
5,采用软件I2C。
二、硬件连接:
1, SDA------->23 pin.(当然你可以任意选择脚位)
2,  SCL------->18 Pin.(当然你可以任意选择脚位)
3, PORTD----->外接8个LED,显示读出的数据,在这里,读出的刚好是一个闪动的流水灯状态。
*******************************************************************/
#i nclude "pic.h"
#define uchar   unsigned char
#define nop()  asm("nop")
#define SCL  TRISC3
#define SDA  TRISC4
void start_i2c();
void stop_i2c();
void send_byte(uchar c);
uchar receive_byte();
void I_send_str(uchar sla,uchar suba,uchar *s,uchar no);
void delay_250ms();
void i2c_error ();
uchar code[]={0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff};
uchar no,ack,c,data;

void main(void)
{
  uchar i;
  TRISC=0Xff;     //C口设为输入 RC3为SCL线,RC4为SDA线。
  PORTC=0X00;     
  TRISD=0X00;     //D口为输出,显示IC24LC02中读出的内容
  PORTD=0X00;     //初始显示全亮

  I_send_str(0xa0,0x00,code,9); //页写入code数组到24LC02,器件地址为0Xa0,子地址为0X00,共9个数。
  
  delay_250ms();
  

  ///开始读出到D口进行显示,根据Random read时序图。
  while (1)
   {
     for (i=0x00;i<0x09;i++)
      {
       start_i2c();
       send_byte(0xa0);      //发送器件地址,即DEVICE ADDRESS。
         if (ack==0) i2c_error(); //如果24LC02无应答。则进入I2C ERROR错误指示。
       send_byte(i);         //发送字地址,即WORD ADDRESS。D口显示数组。
         if (ack==0) i2c_error();
       start_i2c();          //重新启动总线。
       send_byte(0xa1);      //发送读命令和器件地址DEVICE ADDRESS。
         if (ack==0) i2c_error();
       data=receive_byte();
       stop_i2c();
       PORTD=data;
       delay_250ms();
      }
   }
}

/*******************************************************************
起动总线函数
函数原型: void start_i2c();
Function: start on the I2C bus 
*******************************************************************/
void start_i2c()
{
  SDA=1;       //发送启始条件的数据信号
  nop();
  SCL=1;
  nop();nop();nop();nop();nop(); //24LC02要求建立时间大于4,7S
  SDA=0;                      //发送起始信号
  nop();nop();nop();nop();nop();
  SCL=0;                      //钳住I2C总线,准备发送数据或接收数据
  nop();nop();
}

/*******************************************************************
停止总线函数
函数原型: void stop_i2c();
Function: stop the I2C bus
*******************************************************************/
void stop_i2c()
{
  
  SDA=0; //发送结束条件的数据信号
  nop();
  SCL=1;
  nop();nop();nop();nop();nop();
  SDA=1;
  nop();nop();nop();nop();
}
/*=================================================================
字节数据传送函数
函数原型: void send_byte(uchar c);
Function: 将数据C发送出去,可以是地址,也可以是数据,发完后等待回应,并对此状态
          位进行操作(不应答或非应答都使ack=0 ),发送数据正常,ack=1;ack=0
          表示被控器无应答或损坏。
==================================================================*/
void send_byte(uchar c)
{
  uchar bit_count;
  for (bit_count=0;bit_count<8;bit_count++)
   {
    if ((c<<bit_count)&0x80) {SDA=1;}
    else {SDA=0;}
    nop();
    SCL=1;
    nop();nop();nop();nop();nop();
    SCL=0;
   }
   nop();nop();
   SDA=1;
   nop();nop();
   SCL=1;
   nop();nop();nop();
   if (RC4==1) ack=0;
   else ack=1; //用ASK=1为有应答信号
   SCL=0;
   nop();nop();
}
/*==================================================================
字节数据接收函数
函数原型:uchar receive_byte();
FUNCTION: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
          发完后请用应答函数。
===================================================================*/
uchar receive_byte()
{
   uchar retc,bit_count;
   retc=0;
   SDA=1;
   for (bit_count=0;bit_count<8;bit_count++)
   {
      nop();
      SCL=0;
      nop();nop();nop();nop();nop();
      SCL=1;
      nop();nop();
      retc=retc<<1;
      if (RC4==1)  retc=retc+1;
      nop();nop();
    }
    SCL=0;
    nop();nop();
    return (retc);
}

/*================================================================
向有子地址器件发送多字节数据函数
函数原型: bit I_send_str(uchar sla,uchar suba,uchar *s,uchar no);
Function: 从启动总线到发送地址,数据,结束总线的全过程,从器件地址sla。如果
          返回1表示操作成功,否则操作有误。
=================================================================*/
void I_send_str(uchar sla,uchar suba,uchar *s,uchar no)
{
   uchar i;
   start_i2c();
   send_byte(sla);
   if (ack==0)  i2c_error();
   send_byte(suba);
   if (ack==0)  i2c_error();
   for (i=0;i<no;i++)
    {
      send_byte(*s);
      if (ack==0)  i2c_error();
      s++;
    }
   stop_i2c();
  // return(1);
}
/*****************************************************************
延时函数
函数原型: void delay_250ms();
FUNCTION: 延明250ms
*****************************************************************/
void delay_250ms()
{
  unsigned int d=24999;
  while (--d);
}
/*****************************************************************
总线错误函数
函数原型: void i2c_error();
Function: 通过RD7闪动8次表示总线操作失败一次报警。
*****************************************************************/
void i2c_error ()
{
  uchar i;
  for (i=0;i<8;i++)
  {
    RD7=0;
    delay_250ms();
    RD7=1;
    delay_250ms();
  }
  
}
/**********END**************/

四、总结

用时一周,将I2C设备驱动、I2C内核配置、I2C单片机编程等讲完了。其中涉及到的程序,都是可以在以后的项目中移植的。So,这篇文章的含金量还是很高的。

如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78782558

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聚优致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值