基於zynq平臺調試 24FC256驅動。需要注意的是此款eeprom是32K x 8 (256 Kbit)。爲了支持此款eeprom的尋址範圍,地址需要16位來表示。
1.先看此款eeprom的設備地址。
a0,a1,a2,三根線可以選擇設備的地址爲多少。我們靠硬件把地址設置爲0x56(0b1010110)。
2. 寫一byte操作。代碼如下:
val[0] = 0; //high addr
val[1] = 0; //low addr
val[2] = 0xf; //往0地址寫f
/* 數據傳輸三要素: 源,目的,長度 */
msg_w.addr = client->addr; /* 目的 */
msg_w.buf = val; /* 源 */
msg_w.len = 3; /* 高地址+低地址+數據=3 bytes */
msg_w.flags = 0; /* 表示寫 */
ret = i2c_transfer(client->adapter, &msg_w, 1);
printk(KERN_ERR "write ret = %d \n\r",ret);
用示波器抓SCL與sda。即往0地址寫入0xf。
和datasheet給的寫byte操作一致:
2.讀一byte,代碼如下
/* 數據傳輸三要素: 源,目的,長度 */
addr_read[0] = i; //high addr
addr_read[1] = 0; //low addr
/* 讀AT24CXX時,要先把要讀的存儲空間的地址發給它 */
msg[0].addr = client->addr; /* 目的 */
msg[0].buf = addr_read; /* 源 */
msg[0].len = 2; /* 地址=2 byte */
msg[0].flags = 0; /* 表示寫 */
/* 然後啓動讀操作 */
msg[1].addr = client->addr; /* 源 */
msg[1].buf = &data; /* 目的 */
msg[1].len = 1; /* 數據=1 byte */
msg[1].flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, msg, 2);
printk(KERN_ERR"ret = %d,data = 0x%x",ret,data);
讀操作原理如下:需要先寫地址,然後進行讀操作。也就是需要2步。
示波器抓的信號如下:
2.1.寫要讀取數據的地址:
2.2,開始讀一byte
讀寫驗證代碼:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/mod_devicetable.h>
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/platform_data/at24.h>
static const struct i2c_device_id myat24_ids[] = {
{ "24c256", 1 },
{ /* END OF LIST */ }
};
static int myat24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
unsigned char addr_read[2] = {0};
unsigned char data,i;
struct i2c_msg msg[2];
struct i2c_msg msg_w;
char val[3] = {0};
int ret = 0;
memset(msg,0,sizeof(msg));
printk(KERN_ERR "addr: 0x%x,name:%s \n\r",client->addr,client->name);
#if 0
for(i = 0; i < 10; i++) {
val[0] = 0; //high addr
val[1] = i; //low addr
val[2] = i; //val
/* 數據傳輸三要素: 源,目的,長度 */
msg_w.addr = client->addr; /* 目的 */
msg_w.buf = val; /* 源 */
msg_w.len = 3; /* 高地址+低地址+數據=3 byte */
msg_w.flags = 0; /* 表示寫 */
ret = i2c_transfer(client->adapter, &msg_w, 1);
printk(KERN_ERR "write ret = %d \n\r",ret);
//i2c_smbus_write_byte_data(client,addr,addr);
}
#endif
for(i = 0; i < 10; i++) {
/* 數據傳輸三要素: 源,目的,長度 */
addr_read[0] = 0; //high addr
addr_read[1] = i; //low addr
/* 讀AT24CXX時,要先把要讀的存儲空間的地址發給它 */
msg[0].addr = client->addr; /* 目的 */
msg[0].buf = addr_read; /* 源 */
msg[0].len = 2; /* 地址=2 byte */
msg[0].flags = 0; /* 表示寫 */
/* 然後啓動讀操作 */
msg[1].addr = client->addr; /* 源 */
msg[1].buf = &data; /* 目的 */
msg[1].len = 1; /* 數據=1 byte */
msg[1].flags = I2C_M_RD;
ret = i2c_transfer(client->adapter, msg, 2);
//data = i2c_smbus_read_byte_data(client, addr);
printk(KERN_ERR"ret = %d,data = 0x%x",ret,data);
}
return 0;
}
static int myat24_remove(struct i2c_client *client)
{
printk(KERN_ERR"remove myat24 \n\r");
return 0;
}
static struct i2c_driver myat24_driver = {
.driver = {
.name = "at24",
},
.probe = myat24_probe,
.remove = myat24_remove,
.id_table = myat24_ids,
};
static int __init my_at24_init(void)
{
return i2c_add_driver(&myat24_driver);
}
static void __exit my_at24_exit(void)
{
i2c_del_driver(&myat24_driver);
}
module_init(my_at24_init);
module_exit(my_at24_exit);
MODULE_AUTHOR("liu");
MODULE_LICENSE("GPL");
dts:
&i2c0 {
status = "okay";
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c0_default>;
at24@56 {
compatible = "at24,24c256";
pagesize = <64>;
reg = <0x56>;
};
};
後話:
爲了debug I2C方便,可以把如下信息打卡:
這樣代碼中的:debug信息就可以打印出來了,方便調試。
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif