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;elsereturn -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 addri2c_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 elseKERNELDIR=/opt/kernel all:PWD=$(shell pwd) $(MAKE) -C $(KERNELDIR) M=$(PWD) clean:rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions module* Module* endif5)開發板測試
執行 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 ARMv7OK,可以看到符合開發板內核版本要求了,此時可加載驅動。
?
我現在的開發板上沒有 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", ®_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", ®_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,7SSDA=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
總結
以上是生活随笔為你收集整理的S5PV210开发 -- I2C 你知道多少?(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日常生活小技巧 -- 玩转 PDF
- 下一篇: 机器学习笔记(十三)——隐马尔科夫模型