linuxi2c設備
A. 在linux上怎樣增加一個i2c設備
假設手上有一塊從淘寶上買來的開發板,我要在開發板的I2C匯流排上增加一個從設備(如at24c08),那麼我要怎樣寫這個「I2C設備驅動」,讓
應用程序可以訪問at24c08呢?
先來看一個最簡單的i2c設備驅動:
static struct i2c_board_info at24cxx_info = { //所支持的i2c設備的列表
I2C_BOARD_INFO("at24c08", 0x50), //一項代表一個支持的設備,它的名字叫做「at24c08」,器件地址是0x50
};
static struct i2c_client *at24cxx_client;
static int at24cxx_dev_init(void)
{
struct i2c_adapter *i2c_adap; //分配一個適配器的指針
i2c_adap = i2c_get_adapter(0); //調用core層的函數,獲得一個i2c匯流排。這里我們已經知道新增的器件掛接在編號為0的i2c匯流排上
at24cxx_client = i2c_new_device(i2c_adap, &at24cxx_info); // 把i2c適配器和新增的I2C器件關聯起來,這個用了i2c匯流排0,地址是0x50。這就組成了一個客戶端
at24cxx_client i2c_put_adapter(i2c_adap);
return 0;
}
static void at24cxx_dev_exit(void)
{
i2c_unregister_device(at24cxx_client);
}
mole_init(at24cxx_dev_init);
mole_exit(at24cxx_dev_exit);
從上面的程序可以看到,寫一個i2c設備驅動程序,與寫普通的字元驅動基本一樣。特別之處是它調用了i2c的core層的函數,以獲得對i2c匯流排的控制。因為用的是開發板,板上的與soc晶元(一般來說就是arm的晶元)i2c匯流排驅動一般都做好了,直接調用core層的函數就可以控制soc的i2c模塊了。也就是說,寫i2c設備驅動不需要關注arm內部的i2c模塊的寄存器,我們需要關注的是設備(at24c08)的寄存器以及它的datasheet對時序的要求。
其實,添加i2c設備的方法很靈活。根據Linux的官方文檔《linux-3.4.2\Documentation\i2c\instantiating-devices》,添加i2c設備的方法總結有4種:
1. i2c_register_board_info:根據匯流排編號、設備名字(「at24c08」)、設備地址(0x50)注冊一個字元驅動。這種方法最簡單、最粗暴,最貼近平時在開片機上開發i2c器件的。
2. i2c_new_device:根據i2c匯流排的編號,聲明一個i2c設備:這種方法就是上面例子用的方法。這種方法也簡單,但是需要事先知道器件掛接在哪條匯流排上。對於設備,還實現知道了設備地址0x50,匯流排適配器也支持名字為「at24c08」的設備
3. i2c_new_probed_device:
4.從用戶空間實例化一個器件:這個方法相當智能快速,如下輸入指令,即可增加一個i2c設備,同時增加了對應的設備文件。
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
根據英文文檔的標題,添加i2c設備有稱之為「i2c設備的實例化」。
從上述可以知道,在實例化一個i2c設備之前,除了有對應的驅動支持匯流排外(這里是匯流排0),還需要有一個驅動使用了匯流排0發送時序,支持名字為"at24c08"的器件。這個驅動用匯流排驅動的函數,配置了at24c08的寄存器。
B. 如何在linux下實現一個I2C與SPI的從機驅動
最簡情況下:
I2C:SDA數據線、SCL時鍾線。
SPI:DI輸入線、DO輸出線、CS片選先、CLK時鍾線。
可能不能寫到一個驅動中。
但是好在一般很少用到這么簡單的情況,廠家會對其擴展和改進。
比如 W25Q128FB/W25R128FV 系列快閃記憶體,支持 SPI、Dual SPI、Quad SPI 和 QPI。就拿 Quad SPI 來說,有 6 個引腳:
Quad SPI:D0-D3 輸入輸出線、CS片選先、CLK時鍾線。
其中 輸入為一位串列輸入 D0,輸出為四位串列輸出 D0-D3。(四位仍少於一個位元組,可姑且稱為串列)
Winbond華邦 這么做是為了加快快閃記憶體讀取速度(四位串列相比一位串列提高了四倍)。
因此關鍵在於 要進行怎樣的 IO。至於是否將二者寫到一個驅動看來並不重要。
C. linux i2c鐨勮懼囧拰椹卞姩鏄鎬庢牱鍖歸厤鐨
linux涓嬮┍鍔ㄤ唬鐮佸垎涓轟袱涓灞傛★紝涓涓鏄璁懼囨娊璞★紝涓涓鏄鐪熷疄璁懼 鍍廼2c-dev.c灞炰簬璁懼囨娊璞★紝浣犳病鏈夊畠錛屾墍鏈塩har璁懼囩殑i2c鏈哄埗閮藉け鏁 鍍廰d7417.c灞炰簬鐪熷疄璁懼囷紝娌℃湁瀹冨彧鏄褰卞搷ad7417瀵瑰簲鐨勫叿浣撹懼囥 鎵浠ヤ綘鎯充嬌鐢╝d7417鐨勫瑰簲璁懼囷紝榪欎袱涓鏂囦歡蹇...
D. linux下怎麼直接使用iic介面
利用Linux中IIC設備子系統移植IIC設備驅動
背景描述
IIC匯流排在嵌入式系統中應用十分廣泛,常見的有eeprom,rtc。一般的處理器會包含IIC的控制器,用來完成IIC時序的控制;另外一方面,由於IIC的時序簡單,使用GPIO口來模擬時序也是常見的做法。面對不同的IIC控制器,各種各樣的晶元以及linux源碼,如何更快做好IIC設備驅動。
問題描述
在我們的方案中,我們會用到eeprom,rtc以及tw2865。由於Hi3520的IIC控制器設計有問題,無法正常使用。而IIC控制器的SDA和SCL管腳正好是和兩個GPIO管腳復用的。Hisi將控制gpio來實現IIC的時序,從而對IIC設備進行操作。這種設計方式簡單明了,但使用IIC子系統,可以更方便的移植和維護其他的設備驅動。
問題分析
Hisi對於gpio口,rtc晶元以及tw2865的處理方式如下:將gpio口做成一個模塊化的驅動,該驅動模擬IIC時序,並向外提供一些函數介面,比如:EXPORT_SYMBOL(gpio_i2c_read_tw2815);等。對於具體的rtc晶元,將其注冊為一個misc設備,並利用gpio模塊導出的函數進行rtc晶元的配置操作。
其實對於linux-2.6.24\drivers\i2c目錄下代碼,我們可以加以利用。
Linux的IIC字結構分為三個組成部分:
IIC核心
IIC核心提供了IIC匯流排驅動和設備驅動的注冊、注銷方法,IICalgorithm上層的、與具體適配器無關的代碼以及探測設備、檢測設備地址的上層代碼。
IIC匯流排驅動
IIC匯流排驅動是對IIC硬體體系結構中適配器端的實現。
IIC設備驅動
IIC設備驅動是對IIC硬體體系總設備端的實現。
我們查看下該目錄下的makefile和kconfig:
obj-$(CONFIG_I2C_BOARDINFO) +=i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) +=i2c-dev.o
obj-y +=busses/ chips/ algos/
i2c-core.c就是IIC核心,buses中的文件是主流處理器中IIC匯流排的匯流排驅動,而chips中的文件就是常用晶元的驅動,algos中的文件實現了一些匯流排適配器的algorithm,其中就包括我們要用到的i2c-algo-bit.c文件。
我們首先利用i2c-gpio.c和i2c-algo-bit.c做好匯流排驅動。
在i2c-gpio.c中,mole_initi2c_gpio_initplatform_driver_probe(&i2c_gpio_driver,i2c_gpio_probe);
將其注冊為platform虛擬匯流排的驅動。
在staticint __init i2c_gpio_probe(struct platform_device *pdev)中,
定義了如下三個結構體:
structi2c_gpio_platform_data *pdata;//平台相關的gpio的設置
structi2c_algo_bit_data *bit_data;//包含algorithm的具體函數,setor
get SDA和SCL
structi2c_adapter *adap;//適配器
i2c_gpio_probe主要做了下面幾件事:
填充bit_data結構的各個函數指針,關聯到具體的操作SDA和SCl函數。
填充adap結構,adap->algo_data= bit_data;
pdata= pdev->dev.platform_data;
bit_data->data= pdata;
pdev->dev->driver_data= adap;
在i2c-core中注冊適配器類型。
inti2c_bit_add_numbered_bus(struct i2c_adapter *adap)
在staticint i2c_bit_prepare_bus(struct i2c_adapter *adap)中
adap->algo= &i2c_bit_algo;
將i2c_bit_algo與adap關聯上。
static const structi2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
其中,master_xfer函數指針就是IIC傳輸函數指針。
I2c-algo-bit.c還實現了IIC開始條件,結束條件的模擬,發送位元組,接收位元組以及應答位的處理。
i2c-gpio.c中的i2c_gpio_setsda_val等函數是與具體平台gpio相關的。
修改對應arch-hi3520v100目錄下的gpio.h中的各個函數,這些函數是通過操作寄存器來控制gpio的方向和值。
在對應mach-hi3520v100中的platform-devices.c中添加如下:
static structi2c_gpio_platform_data pdata = {
.sda_pin = 1<<0,
.sda_is_open_drain = 1,
.scl_pin = 1<<1,
.scl_is_open_drain = 1,
.udelay = 4, /* ~100 kHz */
};
static struct platform_devicehisilicon_i2c_gpio_device = {
.name = "i2c-gpio",
.id = -1,
.dev.platform_data = &pdata,
};
static struct platform_device*hisilicon_plat_devs[] __initdata = {
&hisilicon_i2c_gpio_device,
};
int __inithisilicon_register_platform_devices(void)
{
platform_add_devices(hisilicon_plat_devs,ARRAY_SIZE (hisilicon_plat_devs));
return 0;
}
通過platform添加devices和driver,使得pdev->dev.platform_data=pdata
綜合上面的過程,我們完成了adapter的注冊,並將用gpio口模擬的algorithm與adapter完成了關聯。
這樣,在rtc-x1205.c中,x1205_attach函數利用i2c核心完成client和adap的關聯。
在x1205_probe函數中填充i2c_client結構體,並調用i2c_attach_client通知iic核心。
接著注冊rtc驅動。
最後我們要讀取時間,就需要構造i2c_msg結構體,如下所示:
struct i2c_msg msgs[] = {
{ client->addr, 0, 2,dt_addr }, /* setup read ptr */
{ client->addr, I2C_M_RD,8, buf }, /* read date */
};
/* read date registers */
if((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev,"%s: read error\n", __FUNCTION__);
return -EIO;
}
dt_addr是寄存器的地址,I2C_M_RD表示iicread。
E. linux內核中i2c匯流排驅動對所有的i2c設備是否是通用的
i2C匯流排的驅動程序一般針對不同的CPU是不一樣的,所以都位於arch目錄下對應的cpu架構的common文件夾下。
對同一種架構的來看,I2C驅動僅實現底層的通信。故其是通用的。
F. Linux 應用可以直接使用/dev/i2c-0 讀寫i2c 設備嗎
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#define CHIP "/dev/i2c-1"
#define CHIP_ADDR 0x50
int main()
{
printf("hello, this is i2c tester\n");
int fd = open(CHIP, O_RDWR);
if (fd < 0)
{
printf("open "CHIP"failed\n");
goto exit;
}
if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0)
{ /* 設置晶元地址 */
printf("oictl:set slave address failed\n");
goto close;
}
unsigned char rddata;
unsigned char rdaddr[2] = {0, 0}; /* 將要讀取的數據在晶元中的偏移量 */
unsigned char wrbuf[3] = {0, 0, 0x3c}; /* 要寫的數據,頭兩位元組為偏移量 */
printf("input a char you want to write to E2PROM\n");
wrbuf[2] = getchar();
printf("write return:%d, write data:%x\n", write(fd, wrbuf, 3), wrbuf[2]);
sleep(1);
printf("write address return: %d\n",write(fd, rdaddr, 2)); /* 讀取之前首先設置讀取的偏移量 */
printf("read data return:%d\n", read(fd, &rddata, 1));
printf("rddata: %c\n", rddata);
close:
close(fd);
exit:
return 0;
}
G. i2clinux椹卞姩i2clinux
linux涓鐨勭綉鍗¢┍鍔ㄥ嚱鏁皃robe鐨勫叿浣撲綔鐢錛
probe鍦ㄨ懼囬┍鍔ㄨ娉ㄥ唽鍒板唴鏍鎬腑鐨勬椂鍊欙紝琚鎬葷嚎鍨嬮┍鍔ㄨ皟鐢ㄣ傛葷嚎椹卞姩綾諱技浜庣敤杞璁鏂規硶鎺㈡祴鎬葷嚎涓婄殑鎵鏈夎懼囷紝灝嗚懼囩殑璇嗗埆鍨嬩俊鎮鍜屽叧閿鏁版嵁緇撴瀯(pciids,usbids,i2cidsandetc.)浼犻掔粰probe鍑芥暟錛宲robe灝變細璇嗗埆鏄鍚︽槸鑷宸辮礋璐i┍鍔ㄧ殑璁懼囷紝騫惰礋璐e畬鎴愯ヨ懼囩殑鍒濆嬪寲鎿嶄綔銆
linux涓璱2c鎬葷嚎涓浠庢満鍦板潃鎬庝箞璁劇疆錛
鏈夊崟綰,鍙岀嚎鍜屼笁綰跨瓑.
I2C鑲瀹氭槸2綰跨殑(涓嶇畻鍦扮嚎).
I2C鍗忚紜瀹炲緢縐戝,姣3/4綰跨殑SPI瑕佸ソ,褰撶劧綰垮氶氳閫熺巼鐩稿瑰氨蹇浜.
I2C鐨勫師鍒欐槸:
鍦⊿CL=1(楂樼數騫)鏃,SDA鍗冧竾鍒蹇芥偁!!!
鍚﹀垯,SDA涓嬭煩鍒"鍒ょ綒"涓"璧峰嬩俊鍙稴",SDA涓婅煩鍒"鍒ょ綒"涓"鍋滄淇″彿P".
鍦⊿CL=0(浣庣數騫)鏃,SDA闅忎究蹇芥偁!!!(鍙鍒蹇芥偁榪囩伀鍒癝CL璺抽珮)
姣忎釜瀛楄妭鍚庡簲璇ョ敱瀵規柟鍥為佷竴涓搴旂瓟淇″彿ACK鍋氫負瀵規柟鍦ㄧ嚎鐨勬爣蹇.
闈炲簲絳斾俊鍙蜂竴鑸鍦ㄦ墍鏈夊瓧鑺傜殑鏈鍚庝竴涓瀛楄妭鍚.涓鑸瑕佺敱鍙屾柟鍗忚絳懼畾.