標籤:i2c驅動 linux驅動
基於Linux的I2C驅動,採用probe方式。按照如下這個架構可以寫出任何支援I2C匯流排的器件的Linux驅動。
I2C器件串連至cpu的特定的i2c的介面,也就是掛載在cpu的i2c適配器上,i2c器件要和cpu進行資訊交換必須要通過cpu操作適配器來互動。cpu上有1個或多個適配器,每個適配器上可以掛載256個裝置地址不一樣的i2c器件,通過i2c驅動就可以讓cpu和適配器上的多個不一樣的i2c器件通訊而不會產生衝突。
驅動包括兩個檔案,dev.c和drive.c,其中dev.c是構建I2C裝置,即建立I2C_Client結構體,而driver.c是在probe中擷取dev.c中構建的i2c_client,然後構建fileoperation;具體步驟結合代碼如下。
1、在dev的入口函數中構建I2C-CLient
static unsigned short addr_list[] = {0x60, 0x50, I2C_CLIENT_END //這個數組包含了裝置地址,以I2C_CLIENT_END為數組結尾</span>};static struct i2c_client *at24cxx_client; //建立I2C_Client結構體static int at24cxx_dev_init(void){/*構建I2C_Client*/struct i2c_adapter *adapter;struct i2c_board_info info;adapter=i2c_get_adapter(0); //擷取適配器,因為有些cpu有多個I2C適配器,參數0為適配器編號memset(&info, 0, sizeof(struct i2c_board_info));strlcpy(info.type, "at24cxx", I2C_NAME_SIZE); //這個是I2C_NAME,是和i2c_driver匹配的關鍵字at24cxx_client=i2c_new_probed_device(adapter, &info, addr_list);//如果數組中地址的裝置存在則成功返回i2c_client結構體
//這個函數最終會調用device_create建立裝置i2c_put_adapter(adapter);if(at24cxx_client)return 0;else return -ENODEV;}
2、在driver.c的入口函數中註冊I2C_driver結構體
static const struct i2c_device_id = {
{ "at24cxx", 0 },
{ }
};
static struct i2c_driver at24cxx_driver = {.driver = {.name = "100ask",.owner = THIS_MODULE,},.probe = at24cxx_probe,.remove = __devexit_p(at24cxx_remove),.id_table = at24cxx_ids,//id_table中name是和dev建立i2c_client的name匹配,若匹配則會調用probe裝置方法};static int at24cxx_drv_init(void){/*×¢²ái2c_driver*/return i2c_add_driver(&at24cxx_driver);}3、在probe裝置方法中構建file_operation註冊字元裝置
static struct file_operations at24cxx_fops = {.owner = THIS_MODULE,.read = at24cxx_read,.write = at24cxx_write,};static int at24cxx_probe(struct i2c_client *client, const struct i2c_device_id *id){printk("%s %s %d.\n",__FILE__,__FUNCTION__,__LINE__);at24cxx_client = client;//將dev中構建的i2c_client結構體傳遞過來,因為這個結構體在read,write等裝置方法中傳遞資訊時需要用到major = register_chrdev(0,"at24cxx",&at24cxx_fops);//註冊字元裝置class = class_create(THIS_MODULE,"at24cxx");device_create(class,NULL,MKDEV(major,0),NULL,"at24cxx");return 0;}
4、構建write和read等裝置方法中傳遞I2C訊息
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);//這裡用i2c_smbus_read_byte_data函數來和實際的I2C裝置進行資訊互動</span>//這裡具體用哪個函數來傳遞訊息需要結合具體的器件的時序,不同的時序有不同的傳遞函數,查看Linux源碼中的說明文檔有關於傳遞函數的說明。copy_to_user(buf,&data,1);return 0;}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];if(!i2c_smbus_write_byte_data(at24cxx_client,addr,data))return 2;elsereturn -EIO;}
如果i2c匯流排中掛載了實際的i2c裝置,而且裝置地址在以上的addr_list中,則無論是先載入dev.ko還是先載入driver.ko都會成功的執行probe函數,然後建立字元裝置,在應用程式中open裝置,並調用read和write則會相應的調用driver中的read和write裝置方法。
測試應用程式
#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("can't open /dev/at24cxx\n");return -1;}if (strcmp(argv[1], "r") == 0){buf[0] = strtoul(argv[2], NULL, 0);printf("before data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[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 = 0x%02x, data = 0x%02x\n", buf[0], buf[1]);}else{print_usage(argv[0]);return -1;}return 0;}
2014--12--17
征途開始
後續補充
I2C驅動架構之probe方式