歡迎您光臨本站 註冊首頁

linux內核GPIO模擬I2C實例

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

使用GPIO口模擬I2C匯流排並掛載設備

前言:

在許多情況下,我們並沒有足夠的I2C匯流排,本文主在介紹如何利用Linux內核中的i2c-gpio模塊,利用2GPIO線模擬i2c匯流排,並掛載設備.

思路:

先通過對i2c-gpio所定義的結構體初始化(包括初始化i2c2條線,頻率,timeout)並將i2c-gpio模塊編譯進內核,實現用GPIO_X,GPIO_Y 2GPIO線註冊新的i2c匯流排.此時這個模塊對i2c設備是透明的,及掛在這2GPIO線的i2c設備可以直接使用Linux內核通用的i2c設備註冊,傳輸和註銷等方法.

步驟:

首先確認在註冊i2c-gpio模塊前,所要用到的2GPIO口是沒有被系統其它地方所調用的.

在每個系統平台啟動時,都會打開一系列的設備,他們通常實現在arch/目錄下相應的平檯子目錄中的例如setup.c,devices.c文件中,在這裡我們進行i2c匯流排的註冊以及設備的掛載.i2c-gpio定義的結構在include/linux/i2c-gpio.h中:

/**

* struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio

* @sda_pin: GPIO pin ID to use for SDA

* @scl_pin: GPIO pin ID to use for SCL

* @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz

* @timeout: clock stretching timeout in jiffies. If the slave keeps

* SCL low for longer than this, the transfer will time out.

* @sda_is_open_drain: SDA is configured as open drain, i.e. the pin

* isn't actively driven high when setting the output value high.

* gpio_get_value() must return the actual pin state even if the

* pin is configured as an output.

* @scl_is_open_drain: SCL is set up as open drain. Same requirements

* as for sda_is_open_drain apply.

* @scl_is_output_only: SCL output drivers cannot be turned off.

*/

struct i2c_gpio_platform_data {

unsigned int sda_pin;

unsigned int scl_pin;

int udelay;

int timeout;

unsigned int sda_is_open_drain:1;

unsigned int scl_is_open_drain:1;

unsigned int scl_is_output_only:1;

};

其中sda_pinscl_pin分別是i2c匯流排的數據線和時鐘線,在i2c-gpio中會通過gpio_request函數對這2個口進行申請,udelay和timeout如果不設初值,i2c-gpio中會自動將其設為默認值.

if (pdata->udelay)

bit_data->udelay = pdata->udelay;

else if (pdata->scl_is_output_only)

bit_data->udelay = 50; /* 10 kHz */

else

bit_data->udelay = 5; /* 100 kHz */

if (pdata->timeout)

bit_data->timeout = pdata->timeout;

else

bit_data->timeout = HZ / 10; /* 100 ms */

初始化這個結構體后再將其裝入platform_device結構體,方便註冊:

static struct platform_device i2c_device = {

.name = "device-name",

.id = your-id,

.dev = {

.platform_data = &i2c_data, // i2c_gpio_platform_data

},

};

註冊i2c-gpio設備

i2c設備掛入我們註冊的匯流排:

platform_device_register(&i2c_device);

static struct i2c_board_info i2c_device[] = {

{

I2C_BOARD_INFO("name", i2c_device_addr),

}

};

i2c_register_board_info(your-id, i2c_device, ARRAY_SIZE(i2c_device));

此時我們就可以在i2c設備的驅動程序中通過遍歷所在i2c匯流排,得到其所在的地址i2c_device_addr.

i2c驅動中,需要註冊一個i2c_driver的結構體,例如:

static const struct i2c_device_id lis35de_id[] = {

{ "lis35de", 0 },

{ }

};

static struct i2c_driver st_lis35de_driver = {

.probe = st_lis35de_probe,

.remove = st_lis35de_remove,

.suspend = st_lis35de_suspend,

.resume = st_lis35de_resume,

.id_table = lis35de_id,

.driver = {

.name = "lis35de",

},

};

static int __init st_lis35de_init(void)

{

printk(KERN_INFO "st_lis35de_init\n");

return i2c_add_driver(&st_lis35de_driver);

}

init時用i2c_add_driver(&st_lis35de_driver),此時將會對所在i2c匯流排進行遍歷並得到該設備的適配器等信息,主要目的即是使驅動得到自己的

i2c_client,在這個i2c_client中,已經有了該i2c設備的地址等信息,我們在驅動中定義一個新的i2c_client全局變數,把得到的這個i2c_client傳給這個全局變數,從而可以繼續後面的i2c操作.

此時我們就可以使用通用的i2c讀寫操作了.

總結:

直接用GPIO口模擬I2C時序和利用內核模塊

i2c-gpio虛擬i2c匯流排的區別:

1. GPIO口模擬I2C時序不需要在系統啟動時註冊I2C匯流排,只需要在I2C設備驅動中單獨實現.i2c-gpio模塊虛擬i2c匯流排需要在系統啟動時註冊新的I2C匯流排,並將i2c設備掛載到新的i2c匯流排,涉及的範圍較廣.

2. GPIO口模擬I2C時序,代碼操作較繁瑣,且不方便掛載多個

i2c設備.i2c-gpio模塊可以完全模擬i2c匯流排,可以掛載多個設備.

3. i2c讀寫操作時,用GPIO口模擬I2C時序需要每次根據讀/寫操作發送器件地址<<1 1/0,然後再發送寄存器地址.i2c-gpio模塊相當於直接在i2c匯流排上操作,在系統啟動掛載i2c設備時已經告訴了i2c匯流排它的地址,在該設備自己的驅動中,只需要通過i2c_add_driver操作即可以得到其地址等諸多信息,讀寫操作只需要發送寄存器地址即可.

附:i2c一般的讀寫操作

#include <linux/i2c.h>

/*

讀操作:

*/

static int i2c_RxData(char *rxData, int length)

{

struct i2c_msg msgs[] = {

/* 1個位元組的i2c設備寄存器地址告訴匯流排 */

{

.addr = client->addr,

.flags = 0, //寫操作

.len = 1,

.buf = rxData,

},

/* 從匯流排讀取length個位元組的數據,存入rxData */

{

.addr =client ->addr,

.flags = I2C_M_RD, //I2C_M_RDi2c.h中被定義為1,讀操作

.len = length,

.buf = rxData,

},

};

if (i2c_transfer(client->adapter, msgs, 2) < 0) { /* 傳輸並判斷是否傳輸錯誤 */

printk(KERN_ERR "I2C_RxData: transfer error\n");

return -EIO;

} else

return 0;

}

/*

寫操作

*/

static int i2c_TxData(char *txData, int length)

{

struct i2c_msg msg[] = {

/* 1個位元組是器件寄存器地址,後面的位元組是寫入的數據 */

{

.addr = client->addr,

.flags = 0,

.len = length,

.buf = txData,

},

};

if (i2c_transfer(client->adapter, msg, 1) < 0) {

printk(KERN_ERR "I2C_TxData: transfer error\n");

return -EIO;

} else

return 0;

}



[火星人 ] linux內核GPIO模擬I2C實例已經有1604次圍觀

http://coctec.com/docs/linux/show-post-50999.html