标签:
最近做东西遇到需要读写EEPROM的需求,利用SMBUS的方式write bye,发现效率很低,因为是利用ioctl的方式write的,所以每进行一次都需要delay 2000多us。Write 4096 bytes居然花了10秒,这不能忍啊~
SMBUS protocol 里面还有提供 i2c_smbus_write_block_data 与 i2c_smbus_write_i2c_block_data两种方法,但是貌似前面一种方法很多embeded设备中是不支持的,晕~
那就直接i2c_smbus_write_i2c_block_data吧,然后看了一下SMBUS对他的封装:
1 static inline __s32 i2c_smbus_write_i2c_block_data(int file, __u8 command, 2 __u8 length, __u8 *values) 3 { 4 union i2c_smbus_data data; 5 int i; 6 if (length > 32) 7 length = 32; 8 for (i = 1; i <= length; i++) 9 data.block[i] = values[i-1]; 10 data.block[0] = length; 11 return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, 12 I2C_SMBUS_I2C_BLOCK_DATA, &data); 13 }
居然直接把length写死了最大32 bytes,这应该是为了配合EEPROM 的 Page Size 的,那万一我的 Page Size 是64,128甚至256呢....
所以还是直接 override i2c_smbus_write_i2c_block_data 吧~
后来试了一下总是莫名fail...
算了,果然是不能偷懒的,索性直接自己写个 fuction 吧,也方便自己以后使用~
话不多说上代码:
1 #include <stdio.h> 2 #include <fcntl.h> 3 #include <unistd.h> 4 #include <stdlib.h> 5 #include <linux/fs.h> 6 #include <sys/types.h> 7 #include <sys/ioctl.h> 8 #include <errno.h> 9 #include <assert.h> 10 #include <string.h> 11 #include <linux/i2c-dev.h> 12 13 14 #define EEPROM_TYPE_UNKNOWN 0 15 #define EEPROM_TYPE_8BIT_ADDR 1 16 #define EEPROM_TYPE_16BIT_ADDR 2 17 18 struct eeprom 19 { 20 char *dev; // device file i.e. /dev/i2c-N 21 int addr; // i2c address 22 int fd; // file descriptor 23 int type; // eeprom type 24 }; 25 26 int eeprom_open(char *dev_fqn, int addr, int type, struct eeprom* e) 27 { 28 int funcs, fd, r; 29 e->fd = e->addr = 0; 30 e->dev = 0; 31 32 fd = open(dev_fqn, O_RDWR); 33 if (fd <= 0) 34 return -1; 35 36 // get funcs list 37 if ((r = ioctl(fd, I2C_FUNCS, &funcs) < 0)) 38 return -1; 39 40 // check for req funcs 41 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE ); 42 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE ); 43 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_BYTE_DATA ); 44 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_BYTE_DATA ); 45 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_READ_WORD_DATA ); 46 CHECK_I2C_FUNC( funcs, I2C_FUNC_SMBUS_WRITE_WORD_DATA ); 47 48 // set working device 49 if ((r = ioctl(fd, I2C_SLAVE, addr)) < 0) 50 return -1; 51 e->fd = fd; 52 e->addr = addr; 53 e->dev = dev_fqn; 54 e->type = type; 55 return 0; 56 } 57 58 int eeprom_close(struct eeprom *e) 59 { 60 close(e->fd); 61 e->fd = -1; 62 e->dev = 0; 63 e->type = EEPROM_TYPE_UNKNOWN; 64 return 0; 65 } 66 67 int write_eeprom(char *dev_fqn, __u16 slaveAddr, __u8 eepromType, __u32 eepromPageSize, __u8 *buff, __u16 addr, int size) 68 { 69 struct eeprom e; 70 __u8 buf[eepromPageSize + 2]; 71 __u32 i,j,n,m; 72 __u16 regAddr; 73 __u8 *readyToWrite; 74 int lastSize = size; 75 76 if (!buff || size <= 0) 77 return -1; 78 79 if (eeprom_open(dev_fqn, slaveAddr, eepromType, &e) < 0) 80 { 81 eeprom_close(&e); 82 return -1; 83 } 84 85 if ((addr % eepromPageSize) != 0) 86 { 87 readyToWrite = buff; 88 regAddr = addr; 89 90 if ((lastSize = write_eeprom_less_pageSize_data( 91 e.fd, eepromPageSize, readyToWrite, regAddr, size)) < 0) 92 { 93 eeprom_close(&e); 94 return -1; 95 } 96 } 97 98 usleep(2175); 99 100 if (lastSize == 0) 101 { 102 eeprom_close(&e); 103 return 0; 104 } 105 106 if (lastSize < eepromPageSize) 107 { 108 readyToWrite = buff + (size - lastSize); 109 regAddr = addr + (size - lastSize); 110 111 if ((lastSize = write_eeprom_less_pageSize_data( 112 e.fd, eepromPageSize, readyToWrite, regAddr, lastSize)) < 0) 113 { 114 eeprom_close(&e); 115 return -1; 116 } 117 118 eeprom_close(&e); 119 return 0; 120 } 121 122 usleep(2175); 123 124 n = lastSize / eepromPageSize; 125 m = lastSize % eepromPageSize; 126 127 for (i = 0; i < n; i ++) 128 { 129 if (i != 0) 130 lastSize -= eepromPageSize; 131 readyToWrite = buff + (size - lastSize); 132 regAddr = addr + (size - lastSize); 133 134 buf[0] = (regAddr >> 8) & 0x00ff; 135 buf[1] = regAddr & 0x00ff; 136 137 for (j = 0; j < eepromPageSize; j ++) 138 { 139 buf[2 + j] = *(readyToWrite + j); 140 } 141 142 if (write(e.fd, buf, (eepromPageSize + 2)) != (eepromPageSize + 2)) 143 { 144 eeprom_close(&e); 145 return -1; 146 } 147 148 usleep(2175); 149 } 150 151 if (m == 0) 152 { 153 eeprom_close(&e); 154 return 0; 155 } 156 157 lastSize -= eepromPageSize; 158 159 readyToWrite = buff + (size - lastSize); 160 regAddr = addr + (size - lastSize); 161 162 if ((lastSize = write_eeprom_less_pageSize_data( 163 e.fd, eepromPageSize, readyToWrite, regAddr, lastSize)) < 0) 164 { 165 eeprom_close(&e); 166 return -1; 167 } 168 169 eeprom_close(&e); 170 return 0; 171 } 172 173 int write_eeprom_less_pageSize_data(int fd, __u32 eepromPageSize, __u8 *buff, __u16 addr, int size) 175 { 176 int writeSize,pageLastSize,i; 177 __u8 buf[eepromPageSize + 1]; 178 179 if (size <= 0) 180 return -1; 181 182 pageLastSize = eepromPageSize - (addr % eepromPageSize); 183 184 if (size <= pageLastSize) 185 writeSize = size; 186 else 187 writeSize = pageLastSize; 188 189 buf[0] = (addr >> 8) & 0x00ff; 190 buf[1] = addr & 0x00ff; 191 192 for (i = 0; i < writeSize; i ++) 193 { 194 buf[2 + i] = *(buff + i); 195 } 196 197 if (write(fd, buf, (writeSize + 2)) != (writeSize + 2)) 198 return -1; 199 200 return (size - writeSize); 201 }
也比较简单就不注释啦~(需要注意的是usleep的时间需要根据不同设备更改)
简单的用法如下:
1 int clean_eeprom(__u16 addr, int size) 2 { 3 __u8 buf[size]; 4 5 memset(buf, 0, sizeof(buf)); 6 7 if (write_eeprom("/dev/i2c-0", 0x0050, EEPROM_TYPE_16BIT_ADDR, 64, buf, addr, size) < 0) 8 return -1; 9 10 return 0; 11 }
使用之后发现写4096 bytes 从10秒缩短到0.1秒哦,真是棒棒哒~
By Shirlix
Linux quick write EEPROM by I2C bus
标签:
原文地址:http://www.cnblogs.com/Shirlix/p/5803688.html