Add AT24C EEPROM code
Signed-off-by: guowenxue <guowenxue@gmail.com>
1 files added
3 files modified
New file |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: at24c.c |
| | | * Description: This file is AT24Cxx EEPROM code |
| | | * |
| | | * Version: 1.0.0(10/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Pin connection: |
| | | * AT24Cxx Raspberry Pi 40Pin |
| | | * VCC <-----> #Pin1(3.3V) |
| | | * SDA <-----> #Pin3(SDA, BCM GPIO2) |
| | | * SCL <-----> #Pin5(SCL, BCM GPIO3) |
| | | * GND <-----> GND |
| | | * |
| | | * /boot/config.txt: |
| | | * dtoverlay=i2c1,pins_2_3 |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | | #include <stdint.h> |
| | | #include <string.h> |
| | | #include <unistd.h> |
| | | #include <libgen.h> |
| | | #include <getopt.h> |
| | | #include <time.h> |
| | | #include <errno.h> |
| | | #include <fcntl.h> |
| | | #include <sys/ioctl.h> |
| | | #include <sys/types.h> |
| | | #include <sys/stat.h> |
| | | #include <linux/types.h> |
| | | #include <linux/i2c.h> |
| | | #include <linux/i2c-dev.h> |
| | | |
| | | /*+----------------------+ |
| | | *| EEPROM Information | |
| | | *+----------------------+*/ |
| | | |
| | | /* AT24Cxx 7-bit I2C slave address */ |
| | | #define AT24C_CHIPADDR 0x50 |
| | | |
| | | enum |
| | | { |
| | | AT24C01 = 1, |
| | | AT24C02 = 2, |
| | | AT24C04 = 4, |
| | | AT24C08 = 8, |
| | | AT24C16 = 16, |
| | | AT24C32 = 32, |
| | | AT24C64 = 64, |
| | | AT24C128 = 128, |
| | | AT24C256 = 256, |
| | | AT24C512 = 512, |
| | | } chipid_t; |
| | | |
| | | typedef struct i2c_s |
| | | { |
| | | char *dev; /* I2C master device, /dev/i2c-N */ |
| | | int addr; /* 7-bits slave address */ |
| | | int fd; /* File description */ |
| | | } i2c_t; |
| | | |
| | | typedef struct eeprom_s |
| | | { |
| | | int chip; /* Chip ID */ |
| | | uint32_t capacity; /* Chip size in bytes */ |
| | | int pagesize; /* Page size in bytes */ |
| | | } eeprom_t; |
| | | |
| | | #define EEP_INFO(_chip, _capacity, _pagesize) \ |
| | | .chip = _chip, \ |
| | | .capacity = _capacity, \ |
| | | .pagesize = _pagesize, \ |
| | | |
| | | static eeprom_t at24c_ids[] = { |
| | | { EEP_INFO(AT24C01, 128, 8) }, |
| | | { EEP_INFO(AT24C02, 256, 8) }, |
| | | { EEP_INFO(AT24C04, 512, 16) }, |
| | | { EEP_INFO(AT24C08, 1024, 16) }, |
| | | { EEP_INFO(AT24C16, 2048, 16) }, |
| | | { EEP_INFO(AT24C32, 4096, 32) }, |
| | | { EEP_INFO(AT24C64, 8192, 32) }, |
| | | { EEP_INFO(AT24C128, 16384, 64) }, |
| | | { EEP_INFO(AT24C256, 32768, 64) }, |
| | | { EEP_INFO(AT24C512, 65536, 128) }, |
| | | }; |
| | | |
| | | typedef struct at24c_s |
| | | { |
| | | i2c_t i2c; |
| | | eeprom_t *eeprom; |
| | | } at24c_t; |
| | | |
| | | int at24c_init(at24c_t *at24c, int chip); |
| | | int at24c_read(at24c_t *at24c, int offset, uint8_t *buf, int size); |
| | | int at24c_write(at24c_t *at24c, int offset, uint8_t *data, int len); |
| | | int at24c_test(at24c_t *at24c); |
| | | |
| | | int i2c_init(i2c_t *i2c, char *i2cdev, int addr); |
| | | int i2c_write(i2c_t *i2c, uint8_t *data, int len); |
| | | int i2c_read(i2c_t *i2c, uint8_t *buf, int size); |
| | | void i2c_term(i2c_t *i2c); |
| | | |
| | | static inline void msleep(unsigned long ms); |
| | | void dump_buf(const char *prompt, char *buf, size_t len); |
| | | |
| | | static inline void banner(const char *progname) |
| | | { |
| | | printf("%s program Version v1.0.0\n", progname); |
| | | printf("Copyright (C) 2023 LingYun IoT System Studio.\n"); |
| | | } |
| | | |
| | | static void program_usage(const char *progname) |
| | | { |
| | | printf("Usage: %s [OPTION]...\n", progname); |
| | | printf(" %s is AT24Cxx EEPROM test program. \n", progname); |
| | | |
| | | printf(" -c[chipid ] Specify EEPROM chipID: 1,2,4,8...512 \n"); |
| | | printf(" -d[device ] Specify I2C device, such as /dev/i2c-1\n"); |
| | | printf(" -h[help ] Display this help information\n"); |
| | | printf(" -v[version ] Display the program version\n"); |
| | | |
| | | printf("\n"); |
| | | banner(progname); |
| | | return; |
| | | } |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | char *progname=NULL; |
| | | char *dev="/dev/i2c-1"; |
| | | int chipid = AT24C256; /* default */ |
| | | at24c_t at24c; |
| | | int rv; |
| | | |
| | | struct option long_options[] = { |
| | | {"chip", required_argument, NULL, 'c'}, |
| | | {"device", required_argument, NULL, 'd'}, |
| | | {"version", no_argument, NULL, 'v'}, |
| | | {"help", no_argument, NULL, 'h'}, |
| | | {NULL, 0, NULL, 0} |
| | | }; |
| | | |
| | | progname = basename(argv[0]); |
| | | |
| | | /* Parser the command line parameters */ |
| | | while ((rv = getopt_long(argc, argv, "c:d:vh", long_options, NULL)) != -1) |
| | | { |
| | | switch (rv) |
| | | { |
| | | case 'c': /* Set chip ID: 1,2,4,8...512 */ |
| | | chipid = atoi(optarg); |
| | | break; |
| | | |
| | | case 'd': /* Set I2C device path: /dev/i2c-1 */ |
| | | dev = optarg; |
| | | break; |
| | | |
| | | case 'v': /* Get software version */ |
| | | banner(progname); |
| | | return EXIT_SUCCESS; |
| | | |
| | | case 'h': /* Get help information */ |
| | | program_usage(progname); |
| | | return 0; |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if( at24c_init(&at24c, chipid) < 0 ) |
| | | { |
| | | printf("at24c initial failed!\n"); |
| | | return 1; |
| | | } |
| | | |
| | | if( i2c_init(&at24c.i2c, dev, AT24C_CHIPADDR) < 0 ) |
| | | { |
| | | printf("i2c initial failed!\n"); |
| | | return 2; |
| | | } |
| | | |
| | | if( at24c_test(&at24c) < 0 ) |
| | | { |
| | | return 3; |
| | | } |
| | | |
| | | i2c_term(&at24c.i2c); |
| | | return 0; |
| | | } |
| | | |
| | | /*+----------------------+ |
| | | *| EEPROM API functions | |
| | | *+----------------------+*/ |
| | | |
| | | int at24c_init(at24c_t *at24c, int chip) |
| | | { |
| | | int i; |
| | | |
| | | if( !at24c ) |
| | | return -1; |
| | | |
| | | for(i=0; i<sizeof(at24c_ids)/sizeof(at24c_ids[0]); i++) |
| | | { |
| | | if( at24c_ids[i].chip == chip ) |
| | | { |
| | | at24c->eeprom = &at24c_ids[i]; |
| | | printf("Detect EEPROM AT24C%02d capacity %d bytes, pagesize %d bytes.\r\n", |
| | | chip, at24c->eeprom->capacity, at24c->eeprom->pagesize); |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | printf("EEPROM: Can not found EEPROM by chip ID[%d]\r\n", chip); |
| | | return -2; |
| | | } |
| | | |
| | | int at24c_test(at24c_t *at24c) |
| | | { |
| | | eeprom_t *eeprom; |
| | | uint8_t buf[128]; |
| | | int i; |
| | | int addr = 0; |
| | | |
| | | if( !at24c ) |
| | | return -1; |
| | | |
| | | eeprom = at24c->eeprom; |
| | | |
| | | /* Read data before write */ |
| | | memset(buf, 0, sizeof(buf)); |
| | | if( at24c_read(at24c, addr, buf, sizeof(buf)) < 0 ) |
| | | { |
| | | return -2; |
| | | } |
| | | dump_buf("<<<EEPROM read data:\n", (char *)buf, sizeof(buf)); |
| | | |
| | | /* fill a page data */ |
| | | for(i=0; i<eeprom->pagesize; i++) |
| | | { |
| | | buf[i] = i; |
| | | } |
| | | |
| | | /* write a page data from the address not page alignment */ |
| | | if( at24c_write(at24c, addr+8, buf, eeprom->pagesize) < 0 ) |
| | | { |
| | | return -3; |
| | | } |
| | | |
| | | /* Read data after write */ |
| | | memset(buf, 0, sizeof(buf)); |
| | | if( at24c_read(at24c, addr, buf, sizeof(buf)) < 0 ) |
| | | { |
| | | return -2; |
| | | } |
| | | dump_buf("<<<EEPROM read data:\n", (char *)buf, sizeof(buf)); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* Figure 9. Page Write */ |
| | | int at24c_write_page(at24c_t *at24c, int offset, uint8_t *data, int len) |
| | | { |
| | | uint8_t buf[256]; |
| | | int bytes, rv; |
| | | int addrlen; |
| | | eeprom_t *eeprom; |
| | | |
| | | if(!at24c || offset<0 || !data || !len) |
| | | return -1; |
| | | |
| | | eeprom = at24c->eeprom; |
| | | |
| | | /* exceeding EEPROM size */ |
| | | if( offset+len > eeprom->capacity ) |
| | | return -1; |
| | | |
| | | if( len > eeprom->pagesize ) |
| | | len = eeprom->pagesize; |
| | | |
| | | /* |
| | | * A temporary write buffer must be used which contains both the address |
| | | * and the data to be written, put the address in first based upon the |
| | | * size of the address for the EEPROM. |
| | | */ |
| | | if( eeprom->chip >= AT24C16) |
| | | { |
| | | buf[0]=(uint8_t)(offset>>8); |
| | | buf[1]=(uint8_t)(offset); |
| | | addrlen = 2; |
| | | } |
| | | else |
| | | { |
| | | buf[0]=(uint8_t)(offset); |
| | | addrlen = 1; |
| | | } |
| | | |
| | | /* Put the data in the write buffer following the address */ |
| | | memcpy(&buf[addrlen], data, len); |
| | | |
| | | /* Write a page of data at the specified address to the EEPROM. */ |
| | | rv = i2c_write(&at24c->i2c, buf, len+addrlen); |
| | | if( rv < 0 ) |
| | | printf("%s() write data failed\n", __func__); |
| | | |
| | | /* Must give a delay here to wait page write finish */ |
| | | msleep(5); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | int at24c_write(at24c_t *at24c, int offset, uint8_t *data, int len) |
| | | { |
| | | int bytes; |
| | | eeprom_t *eeprom; |
| | | |
| | | if(!at24c || offset<0 || !data || len<0) |
| | | return -1; |
| | | |
| | | eeprom = at24c->eeprom; |
| | | |
| | | /* exceeding EEPROM size */ |
| | | if( offset+len > eeprom->capacity ) |
| | | return -1; |
| | | |
| | | /* The offset + write bytes shouldn't overflow that page, |
| | | * or it will over write the start bytes of this page */ |
| | | if( offset%eeprom->pagesize ) |
| | | bytes = eeprom->pagesize - offset%eeprom->pagesize; |
| | | |
| | | /* Write max one page at a time */ |
| | | while(len > 0) |
| | | { |
| | | if( at24c_write_page(at24c, offset, data, bytes) ) |
| | | { |
| | | return -2; |
| | | } |
| | | |
| | | len -= bytes; |
| | | data += bytes; |
| | | offset += bytes; |
| | | |
| | | bytes = len>eeprom->pagesize? eeprom->pagesize : len; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | /* Figure 12. Sequential Read */ |
| | | int at24c_read(at24c_t *at24c, int offset, uint8_t *buf, int size) |
| | | { |
| | | struct i2c_rdwr_ioctl_data tr; |
| | | eeprom_t *eeprom; |
| | | uint8_t addr[2]; |
| | | int addrlen; |
| | | int bytes; |
| | | int rv = 0; |
| | | |
| | | if(!at24c || offset<0 || !buf || size<=0) |
| | | return -1; |
| | | |
| | | eeprom = at24c->eeprom; |
| | | |
| | | /* exceeding EEPROM size */ |
| | | if( offset+size > eeprom->capacity ) |
| | | return -1; |
| | | |
| | | memset(buf, 0, size); |
| | | |
| | | if( eeprom->chip >= AT24C16) |
| | | { |
| | | addr[0]=(uint8_t)(offset>>8); |
| | | addr[1]=(uint8_t)(offset); |
| | | addrlen = 2; |
| | | } |
| | | else |
| | | { |
| | | addr[0]=(uint8_t)(offset); |
| | | addrlen = 1; |
| | | } |
| | | |
| | | /* We can't call i2c_write() and i2c_read() because it will generate a |
| | | * stop condition after send the offset address, it doesn't compatible |
| | | * with EEPROM sequential read sequence; |
| | | */ |
| | | |
| | | |
| | | /* use I2C userspace API to send two message without stop condition */ |
| | | |
| | | tr.nmsgs = 2; |
| | | tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs ); |
| | | if ( !tr.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | /* Create the I2C message for writing the EEPROM offset address */ |
| | | tr.msgs[0].addr = at24c->i2c.addr; |
| | | tr.msgs[0].flags = 0; //write |
| | | tr.msgs[0].len = addrlen; |
| | | tr.msgs[0].buf = addr; |
| | | |
| | | /* Create the I2C message for reading data from EEPROM */ |
| | | memset(buf, 0, size); |
| | | tr.msgs[1].addr = at24c->i2c.addr; |
| | | tr.msgs[1].flags = I2C_M_RD; //read |
| | | tr.msgs[1].len = size; |
| | | tr.msgs[1].buf = buf; |
| | | |
| | | if( ioctl(at24c->i2c.fd, I2C_RDWR, &tr)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | } |
| | | |
| | | free( tr.msgs ); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | /*+----------------------+ |
| | | *| I2C API functions | |
| | | *+----------------------+*/ |
| | | |
| | | int i2c_init(i2c_t *i2c, char *i2cdev, int addr) |
| | | { |
| | | if( !i2c || !i2cdev || !addr ) |
| | | return -1; |
| | | |
| | | memset(i2c, 0, sizeof(*i2c)); |
| | | i2c->addr = addr; |
| | | i2c->dev = i2cdev; |
| | | |
| | | if( (i2c->fd=open(i2cdev, O_RDWR)) < 0) |
| | | { |
| | | printf("open i2c device %s failed: %s\n", i2cdev, strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void i2c_term(i2c_t *i2c) |
| | | { |
| | | if( !i2c ) |
| | | return; |
| | | |
| | | if( i2c->fd > 0) |
| | | close(i2c->fd); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | int i2c_write(i2c_t *i2c, uint8_t *data, int len) |
| | | { |
| | | struct i2c_rdwr_ioctl_data tr; |
| | | int rv = 0; |
| | | |
| | | if( !data || len<= 0) |
| | | { |
| | | printf("%s() invalid input arguments!\n", __func__); |
| | | return -1; |
| | | } |
| | | |
| | | /* I2C device program API: ioctl() */ |
| | | tr.nmsgs = 1; |
| | | tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs ); |
| | | if ( !tr.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | tr.msgs[0].addr = i2c->addr; |
| | | tr.msgs[0].flags = 0; //write |
| | | tr.msgs[0].len = len; |
| | | tr.msgs[0].buf = malloc(len); |
| | | if( !tr.msgs[0].buf ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | memcpy(tr.msgs[0].buf, data, len); |
| | | |
| | | |
| | | if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | goto cleanup; |
| | | } |
| | | |
| | | cleanup: |
| | | if( tr.msgs[0].buf ) |
| | | free(tr.msgs[0].buf); |
| | | |
| | | if( tr.msgs ) |
| | | free(tr.msgs); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | int i2c_read(i2c_t *i2c, uint8_t *buf, int size) |
| | | { |
| | | struct i2c_rdwr_ioctl_data tr; |
| | | int rv = 0; |
| | | |
| | | if( !buf || size<= 0) |
| | | { |
| | | printf("%s() invalid input arguments!\n", __func__); |
| | | return -1; |
| | | } |
| | | |
| | | tr.nmsgs = 1; |
| | | tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs ); |
| | | if ( !tr.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | tr.msgs[0].addr = i2c->addr; |
| | | tr.msgs[0].flags = I2C_M_RD; //read |
| | | tr.msgs[0].len = size; |
| | | tr.msgs[0].buf = buf; |
| | | memset(buf, 0, size); |
| | | |
| | | if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | } |
| | | |
| | | free( tr.msgs ); |
| | | return rv; |
| | | } |
| | | |
| | | /*+----------------------+ |
| | | *| Misc functions | |
| | | *+----------------------+*/ |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | | struct timespec cSleep; |
| | | unsigned long ulTmp; |
| | | |
| | | cSleep.tv_sec = ms / 1000; |
| | | if (cSleep.tv_sec == 0) |
| | | { |
| | | ulTmp = ms * 10000; |
| | | cSleep.tv_nsec = ulTmp * 100; |
| | | } |
| | | else |
| | | { |
| | | cSleep.tv_nsec = 0; |
| | | } |
| | | |
| | | nanosleep(&cSleep, 0); |
| | | } |
| | | |
| | | #define LINELEN 81 |
| | | #define CHARS_PER_LINE 16 |
| | | static char *print_char = |
| | | " " |
| | | " " |
| | | " !\"#$%&'()*+,-./" |
| | | "0123456789:;<=>?" |
| | | "@ABCDEFGHIJKLMNO" |
| | | "PQRSTUVWXYZ[\\]^_" |
| | | "`abcdefghijklmno" |
| | | "pqrstuvwxyz{|}~ " |
| | | " " |
| | | " " |
| | | " ???????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????"; |
| | | |
| | | void dump_buf(const char *prompt, char *buf, size_t len) |
| | | { |
| | | int rc; |
| | | int idx; |
| | | char prn[LINELEN]; |
| | | char lit[CHARS_PER_LINE + 2]; |
| | | char hc[4]; |
| | | short line_done = 1; |
| | | |
| | | if( prompt ) |
| | | printf("%s", prompt); |
| | | |
| | | rc = len; |
| | | idx = 0; |
| | | lit[CHARS_PER_LINE] = '\0'; |
| | | |
| | | while (rc > 0) |
| | | { |
| | | if (line_done) |
| | | snprintf(prn, LINELEN, "%08X: ", idx); |
| | | |
| | | do |
| | | { |
| | | unsigned char c = buf[idx]; |
| | | snprintf(hc, 4, "%02X ", c); |
| | | strncat(prn, hc, LINELEN); |
| | | |
| | | lit[idx % CHARS_PER_LINE] = print_char[c]; |
| | | } |
| | | while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); |
| | | |
| | | line_done = (idx % CHARS_PER_LINE) == 0; |
| | | if (line_done) |
| | | { |
| | | printf("%s %s\r\n", prn, lit); |
| | | } |
| | | } |
| | | |
| | | if (!line_done) |
| | | { |
| | | int ldx = idx % CHARS_PER_LINE; |
| | | lit[ldx++] = print_char[(int)buf[idx]]; |
| | | lit[ldx] = '\0'; |
| | | |
| | | while ((++idx % CHARS_PER_LINE) != 0) |
| | | strncat(prn, " ", sizeof(prn)-strlen(prn)); |
| | | |
| | | printf("%s %s\r\n", prn, lit); |
| | | |
| | | } |
| | | } |
| | |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Pin connection: |
| | | * STH20 Module Raspberry Pi Board |
| | | * SHT20 Raspberry Pi 40Pin |
| | | * VCC <-----> #Pin1(3.3V) |
| | | * SDA <-----> #Pin3(SDA, BCM GPIO2) |
| | | * SCL <-----> #Pin5(SCL, BCM GPIO3) |
| | |
| | | * dtoverlay=i2c1,pins_2_3 |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <stdlib.h> |
| | | #include <string.h> |
| | |
| | | MODE_RDWR, /* I2C device API use read()/write() */ |
| | | }; |
| | | |
| | | typedef struct sht2x_s |
| | | typedef struct i2c_s |
| | | { |
| | | char *dev; /* SHT20 connect I2C device, /dev/i2c-N */ |
| | | int addr; /* SHT20 7-bits slave address */ |
| | | char *dev; /* I2C master device, /dev/i2c-N */ |
| | | int addr; /* 7-bits slave address */ |
| | | int fd; /* File description */ |
| | | int mode; /* I2C device API use read/write or ioctl mode */ |
| | | } sht2x_t; |
| | | } i2c_t; |
| | | |
| | | int sht2x_init(sht2x_t *sht2x); |
| | | int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size); |
| | | int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh); |
| | | int sht2x_softreset(i2c_t *i2c); |
| | | int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size); |
| | | int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh); |
| | | |
| | | int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr); |
| | | int i2c_write(i2c_t *i2c, uint8_t *data, int len); |
| | | int i2c_read(i2c_t *i2c, uint8_t *buf, int size); |
| | | void i2c_term(i2c_t *i2c); |
| | | |
| | | static inline void msleep(unsigned long ms); |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size); |
| | | static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len); |
| | | static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size); |
| | | void print_buf(const char *prompt, uint8_t *buf, int size); |
| | | void dump_buf(const char *prompt, char *buf, size_t len); |
| | | |
| | | static inline void banner(const char *progname) |
| | | { |
| | |
| | | |
| | | int main(int argc, char **argv) |
| | | { |
| | | char *dev="/dev/i2c-1"; |
| | | char *progname=NULL; |
| | | int mode = MODE_IOCTL; |
| | | int rv; |
| | | float temp, rh; |
| | | uint8_t serialnumber[8]; |
| | | char *progname=NULL; |
| | | sht2x_t sht2x; |
| | | i2c_t i2c; |
| | | |
| | | struct option long_options[] = { |
| | | {"device", required_argument, NULL, 'd'}, |
| | |
| | | |
| | | progname = basename(argv[0]); |
| | | |
| | | memset(&sht2x, 0, sizeof(sht2x)); |
| | | sht2x.addr = SHT2X_CHIPADDR; |
| | | sht2x.dev = "/dev/i2c-1"; |
| | | |
| | | /* Parser the command line parameters */ |
| | | while ((rv = getopt_long(argc, argv, "d:m:vh", long_options, NULL)) != -1) |
| | | { |
| | | switch (rv) |
| | | { |
| | | case 'd': /* Set I2C device path: /dev/i2c-1 */ |
| | | sht2x.dev = optarg; |
| | | dev = optarg; |
| | | break; |
| | | |
| | | case 'm': /* Set I2C API mode: 0,1 */ |
| | | sht2x.mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL; |
| | | mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL; |
| | | break; |
| | | |
| | | case 'v': /* Get software version */ |
| | |
| | | } |
| | | } |
| | | |
| | | if( sht2x_init(&sht2x) < 0 ) |
| | | if( i2c_init(&i2c, mode, dev, SHT2X_CHIPADDR) < 0 ) |
| | | { |
| | | printf("I2C master device initial failed.\n"); |
| | | return 1; |
| | | } |
| | | |
| | | if( sht2x_softreset(&i2c) < 0 ) |
| | | { |
| | | printf("SHT2x initialize failure, maybe device not present!\n"); |
| | | return 1; |
| | | } |
| | | |
| | | if( sht2x_get_serialnumber(&sht2x, serialnumber, 8) < 0) |
| | | if( sht2x_get_serialnumber(&i2c, serialnumber, 8) < 0) |
| | | { |
| | | printf("SHT2x get serial number failure\n"); |
| | | return 3; |
| | | } |
| | | |
| | | if( sht2x_get_temp_humidity(&sht2x, &temp, &rh) < 0 ) |
| | | if( sht2x_get_temp_humidity(&i2c, &temp, &rh) < 0 ) |
| | | { |
| | | printf("SHT2x get get temperature and relative humidity failure\n"); |
| | | return 3; |
| | |
| | | |
| | | printf("Temperature=%lf ℃ relative humidity=%lf.\n", temp, rh); |
| | | |
| | | close(sht2x.fd); |
| | | i2c_term(&i2c); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int sht2x_softreset(sht2x_t *sht2x) |
| | | int sht2x_softreset(i2c_t *i2c) |
| | | { |
| | | uint8_t buf[1]; |
| | | |
| | | if( !sht2x || sht2x->fd<0 ) |
| | | if( !i2c || i2c->fd<0 ) |
| | | { |
| | | printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); |
| | | return -1; |
| | |
| | | |
| | | /* software reset SHT2x */ |
| | | buf[0] = SOFTRESET; |
| | | if( i2c_write(sht2x, buf, 1) < 0 ) |
| | | if( i2c_write(i2c, buf, 1) < 0 ) |
| | | { |
| | | return -2; |
| | | } |
| | |
| | | return 0; |
| | | } |
| | | |
| | | int sht2x_init(sht2x_t *sht2x) |
| | | { |
| | | if( (sht2x->fd=open(sht2x->dev, O_RDWR)) < 0) |
| | | { |
| | | printf("i2c device open failed: %s\n", strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | { |
| | | /* set I2C mode and SHT2x slave address */ |
| | | ioctl(sht2x->fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */ |
| | | ioctl(sht2x->fd, I2C_SLAVE, SHT2X_CHIPADDR); /* set SHT2x slave address 0x40*/ |
| | | } |
| | | |
| | | if( sht2x_softreset(sht2x) < 0 ) |
| | | { |
| | | printf("SHT2x softreset failure\n"); |
| | | return -2; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int sht2x_get_temp_humidity(sht2x_t *sht2x, float *temp, float *rh) |
| | | int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh) |
| | | { |
| | | uint8_t buf[4]; |
| | | |
| | | if( !sht2x || !temp || !rh || sht2x->fd<0 ) |
| | | if( !i2c || !temp || !rh || i2c->fd<0 ) |
| | | { |
| | | printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); |
| | | return -1; |
| | |
| | | /* send trigger temperature measure command and read the data */ |
| | | memset(buf, 0, sizeof(buf)); |
| | | buf[0]=TRIGGER_TEMPERATURE_NO_HOLD; |
| | | i2c_write(sht2x, buf, 1); |
| | | i2c_write(i2c, buf, 1); |
| | | |
| | | msleep(85); /* datasheet: typ=66, max=85 */ |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(sht2x, buf, 3); |
| | | dump_buf("Temperature sample data: ", buf, 3); |
| | | i2c_read(i2c, buf, 3); |
| | | print_buf("Temperature sample data: ", buf, 3); |
| | | *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85; |
| | | |
| | | /* send trigger humidity measure command and read the data */ |
| | | memset(buf, 0, sizeof(buf)); |
| | | buf[0] = TRIGGER_HUMIDITY_NO_HOLD; |
| | | i2c_write(sht2x, buf, 1); |
| | | i2c_write(i2c, buf, 1); |
| | | |
| | | msleep(29); /* datasheet: typ=22, max=29 */ |
| | | memset(buf, 0, sizeof(buf)); |
| | | |
| | | i2c_read(sht2x, buf, 3); |
| | | dump_buf("Relative humidity sample data: ", buf, 3); |
| | | i2c_read(i2c, buf, 3); |
| | | print_buf("Relative humidity sample data: ", buf, 3); |
| | | *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6; |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int sht2x_get_serialnumber(sht2x_t *sht2x, uint8_t *serialnumber, int size) |
| | | int sht2x_get_serialnumber(i2c_t *i2c, uint8_t *serialnumber, int size) |
| | | { |
| | | uint8_t buf[4]={0x0}; |
| | | |
| | | if( !sht2x || sht2x->fd<0 || !serialnumber || size!=8 ) |
| | | if( !i2c || i2c->fd<0 || !serialnumber || size!=8 ) |
| | | { |
| | | printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); |
| | | return -1; |
| | |
| | | memset(buf, 0, sizeof(buf)); |
| | | buf[0] = 0xfa; /* command for readout on-chip memory */ |
| | | buf[1] = 0x0f; /* on-chip memory address */ |
| | | i2c_write(sht2x, buf, 2); |
| | | i2c_write(i2c, buf, 2); |
| | | |
| | | memset(buf, 0, sizeof(buf)); |
| | | i2c_read(sht2x, buf, 4); |
| | | i2c_read(i2c, buf, 4); |
| | | |
| | | if( !buf[0] && !buf[1] && !buf[2] && !buf[3] ) |
| | | { |
| | |
| | | memset(buf, 0, sizeof(buf) ); |
| | | buf[0]=0xfc; /* command for readout on-chip memory */ |
| | | buf[1]=0xc9; /* on-chip memory address */ |
| | | i2c_write(sht2x, buf, 2); |
| | | i2c_write(i2c, buf, 2); |
| | | |
| | | memset(buf, 0, sizeof(buf) ); |
| | | i2c_read(sht2x, buf, 4); |
| | | i2c_read(i2c, buf, 4); |
| | | |
| | | serialnumber[1]=buf[0]; /* Read SNC_1 */ |
| | | serialnumber[0]=buf[1]; /* Read SNC_0 */ |
| | | serialnumber[7]=buf[2]; /* Read SNA_1 */ |
| | | serialnumber[6]=buf[3]; /* Read SNA_0 */ |
| | | |
| | | dump_buf("SHT2x Serial number: ", serialnumber, 8); |
| | | print_buf("SHT2x Serial number: ", serialnumber, 8); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static int i2c_write(sht2x_t *sht2x, uint8_t *data, int len) |
| | | /*+----------------------+ |
| | | *| I2C API functions | |
| | | *+----------------------+*/ |
| | | |
| | | int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | if( !i2c || !i2cdev || !addr ) |
| | | return -1; |
| | | |
| | | memset(i2c, 0, sizeof(*i2c)); |
| | | i2c->addr = addr; |
| | | i2c->dev = i2cdev; |
| | | i2c->mode = mode; |
| | | |
| | | if( (i2c->fd=open(i2cdev, O_RDWR)) < 0) |
| | | { |
| | | printf("open i2c device %s failed: %s\n", i2cdev, strerror(errno)); |
| | | return -1; |
| | | } |
| | | |
| | | if( MODE_RDWR == i2c->mode ) |
| | | { |
| | | /* set I2C mode and SHT2x slave address */ |
| | | ioctl(i2c->fd, I2C_TENBIT, 0); /* Not 10-bit but 7-bit mode */ |
| | | ioctl(i2c->fd, I2C_SLAVE, i2c->addr); /* set SHT2x slave address */ |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void i2c_term(i2c_t *i2c) |
| | | { |
| | | if( !i2c ) |
| | | return; |
| | | |
| | | if( i2c->fd > 0) |
| | | close(i2c->fd); |
| | | |
| | | return ; |
| | | } |
| | | |
| | | int i2c_write(i2c_t *i2c, uint8_t *data, int len) |
| | | { |
| | | struct i2c_rdwr_ioctl_data tr; |
| | | int rv = 0; |
| | | |
| | | if( !data || len<= 0) |
| | |
| | | } |
| | | |
| | | /* I2C device program API: read()/write() */ |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | if( MODE_RDWR == i2c->mode ) |
| | | { |
| | | write(sht2x->fd, data, len); |
| | | write(i2c->fd, data, len); |
| | | return 0; |
| | | } |
| | | |
| | | /* I2C device program API: ioctl() */ |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | | tr.nmsgs = 1; |
| | | tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs ); |
| | | if ( !tr.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = sht2x->addr; |
| | | i2cdata.msgs[0].flags = 0; //write |
| | | i2cdata.msgs[0].len = len; |
| | | i2cdata.msgs[0].buf = malloc(len); |
| | | if( !i2cdata.msgs[0].buf ) |
| | | tr.msgs[0].addr = i2c->addr; |
| | | tr.msgs[0].flags = 0; //write |
| | | tr.msgs[0].len = len; |
| | | tr.msgs[0].buf = malloc(len); |
| | | if( !tr.msgs[0].buf ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | rv = -3; |
| | | goto cleanup; |
| | | } |
| | | memcpy(i2cdata.msgs[0].buf, data, len); |
| | | memcpy(tr.msgs[0].buf, data, len); |
| | | |
| | | |
| | | if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) |
| | | if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | |
| | | } |
| | | |
| | | cleanup: |
| | | if( i2cdata.msgs[0].buf ) |
| | | free(i2cdata.msgs[0].buf); |
| | | if( tr.msgs[0].buf ) |
| | | free(tr.msgs[0].buf); |
| | | |
| | | if( i2cdata.msgs ) |
| | | free(i2cdata.msgs); |
| | | if( tr.msgs ) |
| | | free(tr.msgs); |
| | | |
| | | return rv; |
| | | } |
| | | |
| | | static int i2c_read(sht2x_t *sht2x, uint8_t *buf, int size) |
| | | int i2c_read(i2c_t *i2c, uint8_t *buf, int size) |
| | | { |
| | | struct i2c_rdwr_ioctl_data i2cdata; |
| | | struct i2c_rdwr_ioctl_data tr; |
| | | int rv = 0; |
| | | |
| | | if( !buf || size<= 0) |
| | |
| | | } |
| | | |
| | | /* I2C device program API: read()/write() */ |
| | | if( MODE_RDWR == sht2x->mode ) |
| | | if( MODE_RDWR == i2c->mode ) |
| | | { |
| | | read(sht2x->fd, buf, size); |
| | | read(i2c->fd, buf, size); |
| | | return 0; |
| | | } |
| | | |
| | | /* I2C device program API: ioctl() */ |
| | | i2cdata.nmsgs = 1; |
| | | i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs ); |
| | | if ( !i2cdata.msgs ) |
| | | tr.nmsgs = 1; |
| | | tr.msgs = malloc( sizeof(struct i2c_msg)*tr.nmsgs ); |
| | | if ( !tr.msgs ) |
| | | { |
| | | printf("%s() msgs malloc failed!\n", __func__); |
| | | return -2; |
| | | } |
| | | |
| | | i2cdata.msgs[0].addr = sht2x->addr; |
| | | i2cdata.msgs[0].flags = I2C_M_RD; //read |
| | | i2cdata.msgs[0].len = size; |
| | | i2cdata.msgs[0].buf = buf; |
| | | tr.msgs[0].addr = i2c->addr; |
| | | tr.msgs[0].flags = I2C_M_RD; //read |
| | | tr.msgs[0].len = size; |
| | | tr.msgs[0].buf = buf; |
| | | memset(buf, 0, size); |
| | | |
| | | if( ioctl(sht2x->fd, I2C_RDWR, &i2cdata)<0 ) |
| | | if( ioctl(i2c->fd, I2C_RDWR, &tr)<0 ) |
| | | { |
| | | printf("%s() ioctl failure: %s\n", __func__, strerror(errno)); |
| | | rv = -4; |
| | | } |
| | | |
| | | free( i2cdata.msgs ); |
| | | free( tr.msgs ); |
| | | return rv; |
| | | } |
| | | |
| | | /*+----------------+ |
| | | *| Misc functions | |
| | | *+----------------+*/ |
| | | /*+----------------------+ |
| | | *| Misc functions | |
| | | *+----------------------+*/ |
| | | |
| | | static inline void msleep(unsigned long ms) |
| | | { |
| | |
| | | nanosleep(&cSleep, 0); |
| | | } |
| | | |
| | | static inline void dump_buf(const char *prompt, uint8_t *buf, int size) |
| | | void print_buf(const char *prompt, uint8_t *buf, int size) |
| | | { |
| | | int i; |
| | | |
| | |
| | | |
| | | return ; |
| | | } |
| | | |
| | | #define LINELEN 81 |
| | | #define CHARS_PER_LINE 16 |
| | | static char *print_char = |
| | | " " |
| | | " " |
| | | " !\"#$%&'()*+,-./" |
| | | "0123456789:;<=>?" |
| | | "@ABCDEFGHIJKLMNO" |
| | | "PQRSTUVWXYZ[\\]^_" |
| | | "`abcdefghijklmno" |
| | | "pqrstuvwxyz{|}~ " |
| | | " " |
| | | " " |
| | | " ???????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????" |
| | | "????????????????"; |
| | | |
| | | void dump_buf(const char *prompt, char *buf, size_t len) |
| | | { |
| | | int rc; |
| | | int idx; |
| | | char prn[LINELEN]; |
| | | char lit[CHARS_PER_LINE + 2]; |
| | | char hc[4]; |
| | | short line_done = 1; |
| | | |
| | | if( prompt ) |
| | | printf("%s", prompt); |
| | | |
| | | rc = len; |
| | | idx = 0; |
| | | lit[CHARS_PER_LINE] = '\0'; |
| | | |
| | | while (rc > 0) |
| | | { |
| | | if (line_done) |
| | | snprintf(prn, LINELEN, "%08X: ", idx); |
| | | |
| | | do |
| | | { |
| | | unsigned char c = buf[idx]; |
| | | snprintf(hc, 4, "%02X ", c); |
| | | strncat(prn, hc, LINELEN); |
| | | |
| | | lit[idx % CHARS_PER_LINE] = print_char[c]; |
| | | } |
| | | while (--rc > 0 && (++idx % CHARS_PER_LINE != 0)); |
| | | |
| | | line_done = (idx % CHARS_PER_LINE) == 0; |
| | | if (line_done) |
| | | { |
| | | printf("%s %s\r\n", prn, lit); |
| | | } |
| | | } |
| | | |
| | | if (!line_done) |
| | | { |
| | | int ldx = idx % CHARS_PER_LINE; |
| | | lit[ldx++] = print_char[(int)buf[idx]]; |
| | | lit[ldx] = '\0'; |
| | | |
| | | while ((++idx % CHARS_PER_LINE) != 0) |
| | | strncat(prn, " ", sizeof(prn)-strlen(prn)); |
| | | |
| | | printf("%s %s\r\n", prn, lit); |
| | | |
| | | } |
| | | } |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2023 LingYun IoT System Studio. All Rights Reserved. |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: at24c.c |
| | | * Description: This file is AT24Cxx EEPROM code |
| | | * |
| | | * Version: 1.0.0(10/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Description: This file is W25Qxx SPI Norflash driver on RaspberryPi 40Pin. |
| | | * Pin connection: |
| | | * W25QXX Raspberry Pi 40Pin |
| | | * VCC <---> Pin#1 (3.3V) |
| | | * CS <---> Pin#24(CS) |
| | | * DO <---> Pin#21(MISO) |
| | | * GND <---> Pin#9 (GND) |
| | | * CLK <---> Pin#23(SCLK) |
| | | * DI <---> Pin#19(MOSI) |
| | | * |
| | | * W25QXX RaspberryPi 40Pin |
| | | * VCC <---> 3.3V(Pin#1) |
| | | * CS <---> CS(Pin#24) |
| | | * DO <---> MISO(Pin#21) |
| | | * GND <---> GND(Pin#9) |
| | | * CLK <---> SCLK(Pin#23) |
| | | * DI <---> MOSI(Pin#19) |
| | | * /boot/config.txt: |
| | | * dtparam=spi=on |
| | | * |
| | | ********************************************************************************/ |
| | | |
| | |
| | | } while ((buf[1] & 0x01) == 0x01); |
| | | } |
| | | |
| | | /*+----------------+ |
| | | *| dump_buf | |
| | | *+----------------+*/ |
| | | /*+----------------------+ |
| | | *| Misc functions | |
| | | *+----------------------+*/ |
| | | |
| | | void print_buf(const char *prompt, uint8_t *buf, int size) |
| | | { |
| | |
| | | /********************************************************************************* |
| | | * Copyright: (C) 2023 LingYun IoT System Studio. All Rights Reserved. |
| | | * Copyright: (C) 2023 LingYun IoT System Studio |
| | | * All rights reserved. |
| | | * |
| | | * Filename: at24c.c |
| | | * Description: This file is AT24Cxx EEPROM code |
| | | * |
| | | * Version: 1.0.0(10/08/23) |
| | | * Author: Guo Wenxue <guowenxue@gmail.com> |
| | | * ChangeLog: 1, Release initial version on "10/08/23 17:52:00" |
| | | * |
| | | * Description: This file is W25Qxx SPI Norflash driver on RaspberryPi 40Pin. |
| | | * Pin connection: |
| | | * W25QXX Raspberry Pi 40Pin |
| | | * VCC <---> Pin#1 (3.3V) |
| | | * CS <---> Pin#24(CS) |
| | | * DO <---> Pin#21(MISO) |
| | | * GND <---> Pin#9 (GND) |
| | | * CLK <---> Pin#23(SCLK) |
| | | * DI <---> Pin#19(MOSI) |
| | | * |
| | | * W25QXX RaspberryPi 40Pin |
| | | * VCC <---> 3.3V(Pin#1) |
| | | * CS <---> CS(Pin#24) |
| | | * DO <---> MISO(Pin#21) |
| | | * GND <---> GND(Pin#9) |
| | | * CLK <---> SCLK(Pin#23) |
| | | * DI <---> MOSI(Pin#19) |
| | | * /boot/config.txt: |
| | | * dtparam=spi=on |
| | | * |
| | | ********************************************************************************/ |
| | | |