User Tools

Site Tools


tutorials:cb1:development:access_at24c_eeprom_via_i2c

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

tutorials:cb1:development:access_at24c_eeprom_via_i2c [2013/12/03 09:44]
soloforce [Abstract]
tutorials:cb1:development:access_at24c_eeprom_via_i2c [2013/12/23 14:50] (current)
Line 24: Line 24:
 This will configure Cubieboard'​s IO port PB18 & PB19 to TWI1-SCK & TWI1-SDA (I2C-1). This will configure Cubieboard'​s IO port PB18 & PB19 to TWI1-SCK & TWI1-SDA (I2C-1).
 ===== Connection ===== ===== Connection =====
 +Cubieboard side will be considered as the master side of I2C communication,​ while the AT24C EEPROM devices will be considered as the slave ones. In most cases, 4 wires of connection between Cubieboard & devices are enough, they are:
 +  * Vcc: Cubieboard Vcc(5V) <--> AT24C Vcc
 +  * Gnd: Cubieboard Gnd  <​--> ​ AT24C Gnd
 +  * SDA: Cubieboard PB19 <--> AT24C SDA
 +  * SCL: Cubieboard PB18 <--> AT24C SCL
 +The connection of AT24C slave devices is in parallel(like the circuits below, which is a sort of write-protection AT24C configuration circuits), but they should have various device address in order to be addressed by I2C master. The addressing problem will be discussed later in this tutorial.
  
 +{{:​tutorials:​cb1:​development:​at24c256_circuits.png?​500|}}
  
-===== AT24C Access =====+Once connection is done, you can use the //​**i2c-tools**//​(apt-get install it) to detect them. If things go normal, you should get the similar result below.
  
-===== Example Codes =====+{{:​tutorials:​cb1:​development:​i2c_detect.png?​500|}}
  
 +===== AT24C Access =====
 +AT24C series EEPROM'​s address could be splited into two parts, higher bits and lower bits. The higher bits are fixed at //​**0x5**//,​ while lower bits could be hard coded by setting //**A2, A1, A0**//'​s power logic. As for AT24C256, only A1, A0 are valid for address.
 +{{:​tutorials:​cb1:​development:​at24c_pinout.png?​500 |}}
 +===== Example Codes =====
 +This code snippet is an interactive demo for storing text strings and fetching them back to and from AT24C256 chips. Of course you can modified it to make it able to handle binary data. You maybe need install //​**libi2c-dev**//​ package to compile & run. 
 +<code c>
 +/*
 + * 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_BYTES ​ 64  ​
 + 
 +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[0].len = 2 + data_len;​ //​ the offset address word is 2 bytes long, plus the buf_len ​
 +    at24c256.msgs[0].addr = device_addr;​ //​ i2c slave device address ​
 +    at24c256.msgs[0].flags = 0; // 0 for write; 1 for read 
 +    at24c256.msgs[0].buf = (uint8 *)malloc( at24c256.msgs[0].len );
 +    if(!at24c256.msgs[0].buf){
 +        printf("​at24c256_write():​ Failed to allocate memory for i2c message buffer!\n"​);​
 +        return -1;
 +    }
 + 
 +    at24c256.msgs[0].buf[0] = (uint8)(offset >> 8); // MSB first 
 +    at24c256.msgs[0].buf[1] = (uint8)(offset);​ //​ LSB 
 +    strncpy( at24c256.msgs[0].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[0].buf);​
 +        return -1;
 +    }
 + 
 +    free(at24c256.msgs[0].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[2]; // message count in the structure ​
 +    char dummy_write_buf[2];​
 + 
 +    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[0].len = 2; // the offset address word is 2 bytes long 
 +    at24c256.msgs[0].addr = device_addr;​
 +    at24c256.msgs[0].flags = 0; // 0 for write; ​
 +    at24c256.msgs[0].buf = dummy_write_buf;​
 +    at24c256.msgs[0].buf[0] = (unsigned char)(offset >> 8); // write address ​
 +    at24c256.msgs[0].buf[1] = (unsigned char)(offset);​ //​ write address ​
 + 
 +    // read operation
 +    at24c256.msgs[1].len = data_len;
 +    at24c256.msgs[1].addr = device_addr;​
 +    at24c256.msgs[1].flags = 1; // 1 for read; 0 for write 
 +    at24c256.msgs[1].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[0]);​
 +    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);
 +}
 +</​code>​
 +{{:​tutorials:​cb1:​development:​i2c_rw.png?​500 |}}
 <WRAP noprint> <WRAP noprint>
-{{tag>Cubieboard ​Cubietruck}}+{{tag>Cubieboard1 Cubieboard2 ​Cubietruck ​I2C AT24C EEPROM}}
 </​WRAP>​ </​WRAP>​
tutorials/cb1/development/access_at24c_eeprom_via_i2c.1386035057.txt.gz · Last modified: 2013/12/23 14:50 (external edit)