RaspberrPi project source code
Guo Wenxue
2024-04-11 1dab8d0c424af912c01eea9337e82250c5ef648f
commit | author | age
d973d6 1 /*********************************************************************************
G 2  *      Copyright:  (C) 2023 LingYun IoT System Studio
3  *                  All rights reserved.
4  *
5  *       Filename:  at24c.c
6  *    Description:  This file is AT24Cxx EEPROM code
7  *
8  *        Version:  1.0.0(10/08/23)
9  *         Author:  Guo Wenxue <guowenxue@gmail.com>
10  *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
11  *
12  * Pin connection:
13  *                 AT24Cxx                Raspberry Pi 40Pin
14  *                   VCC      <----->      #Pin1(3.3V)
15  *                   SDA      <----->      #Pin3(SDA, BCM GPIO2)
16  *                   SCL      <----->      #Pin5(SCL, BCM GPIO3)
17  *                   GND      <----->      GND
18  *
19  * /boot/config.txt:
20  *                  dtoverlay=i2c1,pins_2_3
21  *
22  ********************************************************************************/
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <libgen.h>
31 #include <getopt.h>
32 #include <time.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <linux/types.h>
39 #include <linux/i2c.h>
40 #include <linux/i2c-dev.h>
41
42 /*+----------------------+
43  *|  EEPROM Information  |
44  *+----------------------+*/
45
46 /* AT24Cxx 7-bit I2C slave address */
47 #define AT24C_CHIPADDR                  0x50
48
49 enum
50 {
51     AT24C01  = 1,
52     AT24C02  = 2,
53     AT24C04  = 4,
54     AT24C08  = 8,
55     AT24C16  = 16,
56     AT24C32  = 32,
57     AT24C64  = 64,
58     AT24C128 = 128,
59     AT24C256 = 256,
60     AT24C512 = 512,
61 } chipid_t;
62
63 typedef struct i2c_s
64 {
65     char           *dev;    /* I2C master device, /dev/i2c-N */
66     int             addr;   /* 7-bits slave address */
67     int             fd;     /* File description */
68 } i2c_t;
69
70 typedef struct eeprom_s
71 {
72     int             chip;       /* Chip ID */
73     uint32_t        capacity;   /* Chip size in bytes */
74     int             pagesize;   /* Page size in bytes */
75 } eeprom_t;
76
77 #define EEP_INFO(_chip, _capacity, _pagesize)       \
78     .chip       = _chip,                            \
79     .capacity   = _capacity,                        \
80     .pagesize   = _pagesize,                        \
81
82 static eeprom_t at24c_ids[] = {
83     { EEP_INFO(AT24C01,  128,   8)   },
84     { EEP_INFO(AT24C02,  256,   8)   },
85     { EEP_INFO(AT24C04,  512,   16)  },
86     { EEP_INFO(AT24C08,  1024,  16)  },
87     { EEP_INFO(AT24C16,  2048,  16)  },
88     { EEP_INFO(AT24C32,  4096,  32)  },
89     { EEP_INFO(AT24C64,  8192,  32)  },
90     { EEP_INFO(AT24C128, 16384, 64)  },
91     { EEP_INFO(AT24C256, 32768, 64)  },
92     { EEP_INFO(AT24C512, 65536, 128) },
93 };
94
95 typedef struct at24c_s
96 {
97     i2c_t            i2c;
98     eeprom_t        *eeprom;
99 } at24c_t;
100
101 int at24c_init(at24c_t *at24c, int chip);
102 int at24c_read(at24c_t *at24c, int offset, uint8_t *buf, int size);
103 int at24c_write(at24c_t *at24c, int offset, uint8_t *data, int len);
104 int at24c_test(at24c_t *at24c);
105
106 int i2c_init(i2c_t *i2c, char *i2cdev, int addr);
107 int i2c_write(i2c_t *i2c, uint8_t *data, int len);
108 int i2c_read(i2c_t *i2c, uint8_t *buf, int size);
109 void i2c_term(i2c_t *i2c);
110
111 static inline void msleep(unsigned long ms);
112 void dump_buf(const char *prompt, char *buf, size_t len);
113
114 static inline void banner(const char *progname)
115 {
116     printf("%s program Version v1.0.0\n", progname);
117     printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
118 }
119
120 static void program_usage(const char *progname)
121 {
122     printf("Usage: %s [OPTION]...\n", progname);
123     printf(" %s is AT24Cxx EEPROM test program. \n", progname);
124
125     printf(" -c[chipid  ]  Specify EEPROM chipID: 1,2,4,8...512 \n");
126     printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-1\n");
127     printf(" -h[help    ]  Display this help information\n");
128     printf(" -v[version ]  Display the program version\n");
129
130     printf("\n");
131     banner(progname);
132     return;
133 }
134
135 int main(int argc, char **argv)
136 {
137     char           *progname=NULL;
138     char           *dev="/dev/i2c-1";
139     int             chipid = AT24C256; /* default */
140     at24c_t         at24c;
141     int             rv;
142
143     struct option long_options[] = {
144         {"chip", required_argument, NULL, 'c'},
145         {"device", required_argument, NULL, 'd'},
146         {"version", no_argument, NULL, 'v'},
147         {"help", no_argument, NULL, 'h'},
148         {NULL, 0, NULL, 0}
149     };
150
151     progname = basename(argv[0]);
152
153     /* Parser the command line parameters */
154     while ((rv = getopt_long(argc, argv, "c:d:vh", long_options, NULL)) != -1)
155     {
156         switch (rv)
157         {
158             case 'c': /*  Set chip ID: 1,2,4,8...512 */
159                 chipid = atoi(optarg);
160                 break;
161
162             case 'd': /*  Set I2C device path: /dev/i2c-1 */
163                 dev = optarg;
164                 break;
165
166             case 'v':  /*  Get software version */
167                 banner(progname);
168                 return EXIT_SUCCESS;
169
170             case 'h':  /*  Get help information */
171                 program_usage(progname);
172                 return 0;
173
174             default:
175                 break;
176         }
177     }
178
179     if( at24c_init(&at24c, chipid) < 0 )
180     {
181         printf("at24c initial failed!\n");
182         return 1;
183     }
184
185     if( i2c_init(&at24c.i2c, dev, AT24C_CHIPADDR) < 0 )
186     {
187         printf("i2c initial failed!\n");
188         return 2;
189     }
190
191     if( at24c_test(&at24c) < 0 )
192     {
193         return 3;
194     }
195
196     i2c_term(&at24c.i2c);
197     return 0;
198 }
199
200 /*+----------------------+
201  *| EEPROM API functions |
202  *+----------------------+*/
203
204 int at24c_init(at24c_t *at24c, int chip)
205 {
206     int             i;
207
208     if( !at24c )
209         return -1;
210
211     for(i=0; i<sizeof(at24c_ids)/sizeof(at24c_ids[0]); i++)
212     {
213         if( at24c_ids[i].chip == chip )
214         {
215             at24c->eeprom = &at24c_ids[i];
216             printf("Detect EEPROM AT24C%02d capacity %d bytes, pagesize %d bytes.\r\n",
217                     chip, at24c->eeprom->capacity, at24c->eeprom->pagesize);
218             return 0;
219         }
220     }
221
222     printf("EEPROM: Can not found EEPROM by chip ID[%d]\r\n", chip);
223     return -2;
224 }
225
226 int at24c_test(at24c_t *at24c)
227 {
228     eeprom_t       *eeprom;
229     uint8_t         buf[128];
230     int             i;
231     int             addr = 0;
232
233     if( !at24c )
234         return -1;
235
236     eeprom = at24c->eeprom;
237
238     /* Read data before write */
239     memset(buf, 0, sizeof(buf));
240     if( at24c_read(at24c, addr, buf, sizeof(buf)) < 0 )
241     {
242         return -2;
243     }
244     dump_buf("<<<EEPROM read data:\n", (char *)buf, sizeof(buf));
245
246     /* fill a page data */
247     for(i=0; i<eeprom->pagesize; i++)
248     {
249         buf[i] = i;
250     }
251
252     /* write a page data from the address not page alignment */
253     if( at24c_write(at24c, addr+8, buf, eeprom->pagesize) < 0 )
254     {
255         return -3;
256     }
257
258     /* Read data after write */
259     memset(buf, 0, sizeof(buf));
260     if( at24c_read(at24c, addr, buf, sizeof(buf)) < 0 )
261     {
262         return -2;
263     }
264     dump_buf("<<<EEPROM read data:\n", (char *)buf, sizeof(buf));
265
266     return 0;
267 }
268
269 /* Figure 9. Page Write */
270 int at24c_write_page(at24c_t *at24c, int offset, uint8_t *data, int len)
271 {
272     uint8_t         buf[256];
273     int             bytes, rv;
274     int             addrlen;
275     eeprom_t       *eeprom;
276
277     if(!at24c || offset<0 || !data || !len)
278         return -1;
279
280     eeprom = at24c->eeprom;
281
282     /* exceeding EEPROM size  */
283     if( offset+len > eeprom->capacity )
284         return -1;
285
286     if( len > eeprom->pagesize )
287         len = eeprom->pagesize;
288
289     /*
290      * A temporary write buffer must be used which contains both the address
291      * and the data to be written, put the address in first based upon the
292      * size of the address for the EEPROM.
293      */
294     if( eeprom->chip >= AT24C16)
295     {
296         buf[0]=(uint8_t)(offset>>8);
297         buf[1]=(uint8_t)(offset);
298         addrlen = 2;
299     }
300     else
301     {
302         buf[0]=(uint8_t)(offset);
303         addrlen = 1;
304     }
305
306     /* Put the data in the write buffer following the address */
307     memcpy(&buf[addrlen], data, len);
308
309     /* Write a page of data at the specified address to the EEPROM. */
310     rv = i2c_write(&at24c->i2c, buf, len+addrlen);
311     if( rv < 0 )
312         printf("%s() write data failed\n", __func__);
313
314     /* Must give a delay here to wait page write finish */
315     msleep(5);
316
317     return rv;
318 }
319
320 int at24c_write(at24c_t *at24c, int offset, uint8_t *data, int len)
321 {
322     int             bytes;
323     eeprom_t       *eeprom;
324
325     if(!at24c || offset<0 || !data || len<0)
326         return -1;
327
328     eeprom = at24c->eeprom;
329
330     /* exceeding EEPROM size  */
331     if( offset+len > eeprom->capacity )
332         return -1;
333
334     /* The offset + write bytes shouldn't overflow that page,
335      * or it will over write the start bytes of this page  */
336     if( offset%eeprom->pagesize )
337         bytes = eeprom->pagesize - offset%eeprom->pagesize;
7fa125 338     else
G 339         bytes = len>eeprom->pagesize? eeprom->pagesize : len;
d973d6 340
G 341     /* Write max one page at a time */
342     while(len > 0)
343     {
344         if( at24c_write_page(at24c, offset, data, bytes) )
345         {
346             return -2;
347         }
348
349         len    -= bytes;
350         data   += bytes;
351         offset += bytes;
352
353         bytes = len>eeprom->pagesize? eeprom->pagesize : len;
354     }
355
356     return 0;
357 }
358
359 /* Figure 12. Sequential Read */
360 int at24c_read(at24c_t *at24c, int offset, uint8_t *buf, int size)
361 {
362     struct i2c_rdwr_ioctl_data      tr;
363     eeprom_t                       *eeprom;
364     uint8_t                         addr[2];
365     int                             addrlen;
366     int                             bytes;
367     int                             rv = 0;
368
369     if(!at24c || offset<0 || !buf || size<=0)
370         return -1;
371
372     eeprom = at24c->eeprom;
373
374     /* exceeding EEPROM size  */
375     if( offset+size > eeprom->capacity )
376         return -1;
377
378     memset(buf, 0, size);
379
380     if( eeprom->chip >= AT24C16)
381     {
382         addr[0]=(uint8_t)(offset>>8);
383         addr[1]=(uint8_t)(offset);
384         addrlen = 2;
385     }
386     else
387     {
388         addr[0]=(uint8_t)(offset);
389         addrlen = 1;
390     }
391
392     /* We can't call i2c_write() and i2c_read() because it will generate a
393      * stop condition after send the offset address, it doesn't compatible
394      * with EEPROM sequential read sequence;
395      */
396
397
398     /* use I2C userspace API to send two message without stop condition */
399
400     tr.nmsgs = 2;
401     tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
402     if ( !tr.msgs )
403     {
404         printf("%s() msgs malloc failed!\n", __func__);
405         return -2;
406     }
407
408     /* Create the I2C message for writing the EEPROM offset address */
409     tr.msgs[0].addr = at24c->i2c.addr;
410     tr.msgs[0].flags = 0; //write
411     tr.msgs[0].len = addrlen;
412     tr.msgs[0].buf = addr;
413
414     /* Create the I2C message for reading data from EEPROM */
415     memset(buf, 0, size);
416     tr.msgs[1].addr = at24c->i2c.addr;
417     tr.msgs[1].flags = I2C_M_RD; //read
418     tr.msgs[1].len = size;
419     tr.msgs[1].buf = buf;
420
421     if( ioctl(at24c->i2c.fd, I2C_RDWR, &tr)<0 )
422     {
423         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
424         rv = -4;
425     }
426
427     free( tr.msgs );
428     return rv;
429 }
430
431
432 /*+----------------------+
433  *|   I2C API functions  |
434  *+----------------------+*/
435
436 int i2c_init(i2c_t *i2c, char *i2cdev, int addr)
437 {
438     if( !i2c || !i2cdev || !addr )
439         return -1;
440
441     memset(i2c, 0, sizeof(*i2c));
442     i2c->addr = addr;
443     i2c->dev = i2cdev;
444
445     if( (i2c->fd=open(i2cdev, O_RDWR)) < 0)
446     {
447         printf("open i2c device %s failed: %s\n", i2cdev, strerror(errno));
448         return -1;
449     }
450
451     return 0;
452 }
453
454 void i2c_term(i2c_t *i2c)
455 {
456     if( !i2c )
457         return;
458
459     if( i2c->fd > 0)
460         close(i2c->fd);
461
462     return ;
463 }
464
465 int i2c_write(i2c_t *i2c, uint8_t *data, int len)
466 {
467     struct i2c_rdwr_ioctl_data      tr;
468     int                             rv = 0;
469
470     if( !data || len<= 0)
471     {
472         printf("%s() invalid input arguments!\n", __func__);
473         return -1;
474     }
475
476     /* I2C device program API: ioctl() */
477     tr.nmsgs = 1;
478     tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
479     if ( !tr.msgs )
480     {
481         printf("%s() msgs malloc failed!\n", __func__);
482         return -2;
483     }
484
485     tr.msgs[0].addr = i2c->addr;
486     tr.msgs[0].flags = 0; //write
487     tr.msgs[0].len = len;
488     tr.msgs[0].buf = malloc(len);
489     if( !tr.msgs[0].buf )
490     {
491         printf("%s() msgs malloc failed!\n", __func__);
492         rv = -3;
493         goto cleanup;
494     }
495     memcpy(tr.msgs[0].buf, data, len);
496
497
498     if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
499     {
500         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
501         rv = -4;
502         goto cleanup;
503     }
504
505 cleanup:
506     if( tr.msgs[0].buf )
507         free(tr.msgs[0].buf);
508
509     if( tr.msgs )
510         free(tr.msgs);
511
512     return rv;
513 }
514
515 int i2c_read(i2c_t *i2c, uint8_t *buf, int size)
516 {
517     struct i2c_rdwr_ioctl_data      tr;
518     int                             rv = 0;
519
520     if( !buf || size<= 0)
521     {
522         printf("%s() invalid input arguments!\n", __func__);
523         return -1;
524     }
525
526     tr.nmsgs = 1;
527     tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs );
528     if ( !tr.msgs )
529     {
530         printf("%s() msgs malloc failed!\n", __func__);
531         return -2;
532     }
533
534     tr.msgs[0].addr = i2c->addr;
535     tr.msgs[0].flags = I2C_M_RD; //read
536     tr.msgs[0].len = size;
537     tr.msgs[0].buf = buf;
538     memset(buf, 0, size);
539
540     if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 )
541     {
542         printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
543         rv = -4;
544     }
545
546     free( tr.msgs );
547     return rv;
548 }
549
550 /*+----------------------+
551  *|    Misc functions    |
552  *+----------------------+*/
553
554 static inline void msleep(unsigned long ms)
555 {
556     struct timespec cSleep;
557     unsigned long ulTmp;
558
559     cSleep.tv_sec = ms / 1000;
560     if (cSleep.tv_sec == 0)
561     {
562         ulTmp = ms * 10000;
563         cSleep.tv_nsec = ulTmp * 100;
564     }
565     else
566     {
567         cSleep.tv_nsec = 0;
568     }
569
570     nanosleep(&cSleep, 0);
571 }
572
573 #define LINELEN 81
574 #define CHARS_PER_LINE 16
575 static char *print_char =
576 "                "
577 "                "
578 " !\"#$%&'()*+,-./"
579 "0123456789:;<=>?"
580 "@ABCDEFGHIJKLMNO"
581 "PQRSTUVWXYZ[\\]^_"
582 "`abcdefghijklmno"
583 "pqrstuvwxyz{|}~ "
584 "                "
585 "                "
586 " ???????????????"
587 "????????????????"
588 "????????????????"
589 "????????????????"
590 "????????????????"
591 "????????????????";
592
593 void dump_buf(const char *prompt, char *buf, size_t len)
594 {
595     int rc;
596     int idx;
597     char prn[LINELEN];
598     char lit[CHARS_PER_LINE + 2];
599     char hc[4];
600     short line_done = 1;
601
602     if( prompt )
603         printf("%s", prompt);
604
605     rc = len;
606     idx = 0;
607     lit[CHARS_PER_LINE] = '\0';
608
609     while (rc > 0)
610     {
611         if (line_done)
612             snprintf(prn, LINELEN, "%08X: ", idx);
613
614         do
615         {
616             unsigned char c = buf[idx];
617             snprintf(hc, 4, "%02X ", c);
618             strncat(prn, hc, LINELEN);
619
620             lit[idx % CHARS_PER_LINE] = print_char[c];
621         }
622         while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
623
624         line_done = (idx % CHARS_PER_LINE) == 0;
625         if (line_done)
626         {
627             printf("%s  %s\r\n", prn, lit);
628         }
629     }
630
631     if (!line_done)
632     {
633         int ldx = idx % CHARS_PER_LINE;
634         lit[ldx++] = print_char[(int)buf[idx]];
635         lit[ldx] = '\0';
636
637         while ((++idx % CHARS_PER_LINE) != 0)
638             strncat(prn, "   ", sizeof(prn)-strlen(prn));
639
640         printf("%s  %s\r\n", prn, lit);
641
642     }
643 }