1. 程式人生 > >新手寫LinuxI2C驅動程式

新手寫LinuxI2C驅動程式

Linux下i2c驅動

接下來將是一個新手去寫i2c驅動,這個驅動也是自己第一次寫,用了2天時間去學習,最後終於完成,在寫驅動之前,我們需要搞明白以下幾點

  • Linux怎麼識別我們的i2c晶片
  • 怎麼去寫
  • 什麼是裝置地址

首先我們需要明白一個I2C驅動在微控制器上面怎麼寫,這個時候就需要去網上了解以下I2C的基礎知識,同時自己也要認真研究自己的晶片手冊,我這裡用的是TCA9555這個I2C晶片

查了網上的資料,大致總結一下,一個I2C驅動可以由以下幾個不同的方式來完成:
(1)使用gpio模擬i2c協議
(2)使用Linux系統中的I2C體系

1、使用gpio模擬i2c協議
我最開始就是想到這種方法,當然這種方法還是微控制器的思維,感覺自己目前還是缺少Linux系統的思維,這個需要多練,培養自己的Linux思維,在用gpio模擬i2c協議時,我們需要知道引腳的地址,然後去按照微控制器的方法去寫,很麻煩,我在這個過程中,是完全不瞭解I2C引腳的地址怎麼去獲取,然我在這裡卡了很久,感覺這條路行不通

2、使用Linux系統中的I2C體系
很明顯,我們的i2c一般都是接在處理器的I2C介面上面的(我用的是AM335X),此時我們需要做的就是讓處理器知道,我們需要使用這個I2C,說道這裡,也就是關鍵的部分—-如何註冊我們的裝置(讓整個系統知道)查了很多資料,大致有以下幾種方法:
(1)在、arch/arm/目錄下找到對應的mach檔案進行註冊
static struct i2c_board_info h4_i2c_board_info[] __initdata = {
{
I2C_BOARD_INFO(“isp1301_omap”, 0x2d),
.irq = OMAP_GPIO_IRQ(125),
},
這一種方法我也沒搞太懂,不過可以確定的是,在Linux核心加入裝置樹概念後,我們用裝置樹會更清楚明白
(2)裝置樹進行註冊(我用的方法)
&i2c1 {
pinctrl-names = “default”;
pinctrl-0 = <&i2c1_pins>;

    status = "okay";
    clock-frequency = <400000>;

    tca9555: [email protected] {
            compatible = "ti,tca9555";
            reg = <0x20>;
    };

此時就面臨一個問題,裝置樹怎麼註冊,首先我們搞清楚裝置地址這個概念:研究了一下,說白了就是看晶片手冊,我的是
這裡寫圖片描述
可以看到,只需知道A2A1A0就可,然後看一下自己的電路圖,檢視一下A2A1A0的連線情況,我的都是接地,然後在最高位補上0,就確定了我的裝置地址為0x20,從而就可以註冊裝置樹
註冊完了之後,我們就解決了第一個問題,讓Linux系統知道有這個裝置存在,接下來我們要做的就是,具體的驅動與Linux系統的握手,
(3)還有很多方法,我也沒有研究

裝置驅動怎麼寫

首先我們需要程式入口和出口:

“`
static int __init tca9555_init(void)
{
printk(DEVICE_NAME”\tinitialized\n”);
i2c_add_driver(&tca9555_driver);
return 0;
}

static void __exit tca9555_exit(void)
{
i2c_del_driver(&tca9555_driver);
}
呼叫系統中的i2c_add_driver(i2c_del_driver)來增加(解除安裝)自己的i2c裝置

然後寫tca9555_dristatic ,按照i2c_driver結構體的格式來寫
struct i2c_driver tca9555_driver = {
.driver.name = “tca9555”,
.id_table = tca9555_id,
.probe = tca9555_probe,
.remove = tca9555_remove,
};

填寫tca9555_static
const struct i2c_device_id tca9555_id[]={
{“tca9555”,0}, //這裡的名字一定要與裝置樹中的一致
{ /* end of list */ },
};

構建tca9555_probe函式,此函式主要用來匹配
static int tca9555_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ //匹配不成功,裝置節點不能註冊
printk(“misc ok\n”);
misc_register(&tca9555_dev); //混雜裝置
g_client = client;
return 0;
}

構建tca9555_remove函式用來登出裝置
static int tca9555_remove(struct i2c_client *client)
{
misc_deregister(&tca9555_dev);
g_client = NULL;
return 0;
}

因為我們在上面註冊了misc裝置,此時接下來就是混雜裝置驅動的編寫過程,首先構建一個結構體,由結構體引出我們需要處理的函式

static struct miscdevice tca9555_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = “tca9555_i2c”,
.fops = &tca9555_fops
};

static struct file_operations tca9555_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tca9555_ioctl
};

然後,我們應用程式與驅動之間的通訊就靠tca9555_ioctl這個函式來完成了,著這個過程中,我們如何達成I2C之間的通訊,其實是呼叫i2c_smbus_read_byte_data和i2c_smbus_write_byte_data這個函式來完成的,這個函式是Linux內建函式,我們需要用一個
tca9555_i2c_read來將其進行封裝:

static unsigned char tca9555_i2c_read(unsigned char addr)
{
return i2c_smbus_read_byte_data(g_client,addr);
}

從而,在ioctl的時候方便呼叫

差不多內容就是這麼多,驅動原始碼和測試程式我會在文章最後給大家,總結的來說,此I2C驅動對於很多新手來說很難是因為,不知道Linux裡面有I2C體系,然後知道了I2C體系後,網上也是各種方法都有,搞得人很混亂,從而無從下手,我也是根據網上的部落格模仿寫出來的

最後總結一下:
(1)首先註冊,讓系統知道硬體的存在
(2)如何握手,呼叫I2C_ADD_DRIVER,然後呼叫id probe來匹配我們的驅動(匹配不成功,程式不會往下走,也就是說,裝置節點註冊不成功)
(3)匹配成功後,呼叫misc裝置的註冊方法,註冊我們的裝置,寫ioctl函式,進行應用程與驅動層的通訊
(4)利用系統I2C通訊函式,我們用自己的函式對其進行封裝即可

這是第一篇部落格文章,以前好多次想寫來著,但最後忙著其他事也就忘記了,以後會把自己這段時間做的東西跟大家分享一下,希望大家多多交流