soloforce 发表于 2013-11-18 19:23:55

i2c访问外接AT24C256 EEPROM

本帖最后由 soloforce 于 2013-11-18 21:17 编辑

发个i2c访问at24c256 eeprom()的DIY,按例先上图。





用了四个at24c256, 并联在Cubieboard的TWI-1接口上,设备文件是/dev/i2c-1。下面我画的示意图有点小错,把WP脚接VCC了,会导致写保护(无法写入),必须悬空掉。


为了更清楚地了解at24c,给个设备编址说明,网上下载的,不过有错误,我已经修正了。

研究了一下午i2c on linux,终于写出了代码:
/*
* at24c256_i2c.c
*
* This script is a demo for AT24C256 EEPROM access via I2C on Cubieboard
*
* Author: soloforce, forum.cubietech.com
* Date: 2013.11.18
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL
* PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

// Cubieboard IO port PB18 & PB19 are configured to TWI1-SCK & TWI1-SDA (I2C-1)
#define DEFAULT_AT24C256_DEV"/dev/i2c-1"

// ATmega AT24C series EEPROM slave devices are addressed from 0x50~0x57
// As for AT24C256, should be 0x50~0x53
#define DEFAULT_AT24C256_ADDRESS 0x50

// AT24C256 has 512 pages, 64 bytes each
#define PAGE_BYTES64

typedef unsigned char uint8;
typedef unsigned short uint16;

/*
* Open the i2c bus device
*/
int init_at24c256_i2c(const char* device)
{
    int fd=open(device, O_RDWR);
    if(fd<0){
      perror("Failed to open I2C device!");
      return -1;
    }

    ioctl(fd, I2C_TIMEOUT, 3);      // i2c timeout, 1 for 10ms ; if too small, i2c may lose response
    ioctl(fd, I2C_RETRIES, 3);      // i2c retry limit
   
    return fd;
}

/*
* Page write (or partially page write), data_len should less than PAGE_BYTES
*
*/
int at24c256_write(int fd, int device_addr, uint16 offset, const char* data, int data_len)
{
    struct i2c_rdwr_ioctl_data at24c256;      // structure pass to i2c driver
    struct i2c_msg i2cmsg;

    if(data_len>PAGE_BYTES){
      printf("*** Warning: Page write mode should not exceed %d bytes, otherwise the current page will be rolled over!\n",PAGE_BYTES);
    }

    at24c256.nmsgs = 1;      // message count in the structure
    at24c256.msgs = &i2cmsg;
    at24c256.msgs.len = 2 + data_len;      // the offset address word is 2 bytes long, plus the buf_len
    at24c256.msgs.addr = device_addr;      // i2c slave device address
    at24c256.msgs.flags = 0;      // 0 for write; 1 for read
    at24c256.msgs.buf = (uint8 *)malloc( at24c256.msgs.len );
    if(!at24c256.msgs.buf){
      printf("at24c256_write(): Failed to allocate memory for i2c message buffer!\n");
      return -1;
    }
   
    at24c256.msgs.buf = (uint8)(offset >> 8);      // MSB first
    at24c256.msgs.buf = (uint8)(offset);      // LSB
    strncpy( at24c256.msgs.buf+2, data, data_len); // copy user's data to the structure buffer

    if(ioctl(fd, I2C_RDWR, (unsigned long)&at24c256)<0 ){      // write ioctl data to the i2c bus
      printf("at24c256_write(): Failed to write data to i2c device!\n");
      free(at24c256.msgs.buf);
      return -1;
    }
   
    free(at24c256.msgs.buf);
    return 0;
}

/*
* Sequential read from AT24C256, data_len should less than PAGE_BYTES
*/
int at24c256_read(int fd, int device_addr, uint16 offset, char* data, int data_len)
{
    struct i2c_rdwr_ioctl_data at24c256;      // structure pass to i2c driver
    struct i2c_msg i2cmsg; // message count in the structure
    char dummy_write_buf;
   
    if(data_len>PAGE_BYTES){
      printf("*** Warning: Sequential read should not exceed %d bytes, otherwise the read data will be rolled over!\n",PAGE_BYTES);
    }

    // A dummy write operation should be done according to the AT24C256 i2c protocol
    at24c256.nmsgs = 2;      // message count in the structure
    at24c256.msgs = i2cmsg;
    at24c256.msgs.len = 2; // the offset address word is 2 bytes long
    at24c256.msgs.addr = device_addr;
    at24c256.msgs.flags = 0;      // 0 for write;
    at24c256.msgs.buf = dummy_write_buf;
    at24c256.msgs.buf = (unsigned char)(offset >> 8);      // write address
    at24c256.msgs.buf = (unsigned char)(offset);      // write address

    // read operation
    at24c256.msgs.len = data_len;
    at24c256.msgs.addr = device_addr;
    at24c256.msgs.flags = 1;      // 1 for read; 0 for write
    at24c256.msgs.buf = data;

    if (ioctl(fd, I2C_RDWR, (unsigned long)&at24c256) < 0) {
      printf("at24c256_read(): Failed to read data from EEPROM via i2c!\n");
      return -1;
    }

    return 0;
}

/*
* Show help message
*/
void show_usage(char* argv[])
{
    printf("%s <-d device> <-a address> [-o offset] [-r length] [-w data] [-h]\n",argv);
    printf("\t-d device: i2c device file, like /dev/i2c-1 by default\n");
    printf("\t-a address: i2c slave device base address, like 0x50 by default\n");
    printf("\t-o offset: offset from base address, like 0x00, 0x10 ...\n");
    printf("\t-r length: read length bytes from at24c256 eeprom\n");
    printf("\t-w data: write to at24c256 eeprom\n");
    printf("\t-h: show help message\n\n");
}

int main(int argc, char* argv[])
{
    char* device=DEFAULT_AT24C256_DEV;
    int slave_address=DEFAULT_AT24C256_ADDRESS;
    int offset=0x00; // default offset is 0
    int length=32; // read 32 bytes by default
    char* data=NULL;
    int rw_mode=1; // 0 for write, 1 for read;

    if(argc==1){
      show_usage(argv);
      return 0;
    }

    int i;
    while ((i = getopt(argc, argv, "d:a:o:r:w:n:h")) >= 0){
      switch(i){
      case 'd':
            device=optarg;
            break;
      case 'a':
            if (sscanf(optarg, "0x%x", &slave_address) != 1) {
            fprintf(stderr, "Cannot parse '%s' as i2c slave device address, example: '0x50'\n", optarg);
                return -1;
            }
            break;
      case 'o':
            if (sscanf(optarg, "%d", &offset) != 1) {
            fprintf(stderr, "Cannot parse '%s' as address offset, example: '32'\n", optarg);
                return -1;
            }
            break;
      case 'r':
            rw_mode=1;
            if (sscanf(optarg, "%d", &length) != 1) {
                fprintf(stderr, "Cannot parse '%s' as length, example: '17'\n", optarg);
                return -1;
            }
            break;
      case 'w':
            rw_mode=0;
            data=optarg;
            length=strlen(data);
            break;
      case 'h':
            show_usage(argv);
            return 0;
            break;
      }
    }
   
    int fd=init_at24c256_i2c(device);
    if(fd<0) return -1;

    printf("i2c device file:%s\nslave_address:0x%X\nR/W offset:0x%X\n",device, slave_address, offset);

    if(rw_mode==0){
      // test at24c256 write
      at24c256_write(fd, slave_address, offset, data, length);
      printf("try to write: %s (%d bytes)!\n",data,length);
      usleep(10000); // some delay is necessary for the write operation done
    }else if(rw_mode==1){
      // test at24c256 read
      char *buf=(char*)malloc(length+1);
      memset(buf,0,length+1);
      at24c256_read(fd, slave_address, offset, buf, length);
      usleep(10000); // necessary for the read operation done
      
      printf("read from at24c256 eeprom: %s (%d bytes)\n", buf, strlen(buf));
      free(buf);
    }
   
    close(fd);
}

rgwan 发表于 2013-11-19 13:20:52

当年用8051驱动 24C02那个心酸啊,代码一长串的。
注意一下24系列的ROM的死区时间是8ms左右。如果两次写入间隔时间太短会没有响应。

sunbeyond 发表于 2014-3-14 14:15:10

怎么报错了。at24.c:48:12: error: invalid use of undefined type ‘struct i2c_msg’
这个结构体没声明啊。差那个头文件了吗?

lknlfy 发表于 2014-3-14 21:41:00

不错,基于应用层的驱动

lknlfy 发表于 2014-3-14 21:41:38

内核有专门针对at eeprom的驱动
页: [1]
查看完整版本: i2c访问外接AT24C256 EEPROM