From fe321182084c5da0a91fd253e601af41613b94c2 Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Mon, 19 Aug 2024 16:46:58 +0800
Subject: [PATCH] Add modules in hal
---
hal/modules/sht20.c | 516 +++++++++++
hal/modules/ds18b20.c | 134 ++
hal/modules/at24c.c | 616 +++++++++++++
hal/modules/makefile | 45 +
hal/modules/tsl2561.c | 337 +++++++
hal/modules/w25qflash.h | 181 ++++
hal/modules/w25qflash.c | 831 ++++++++++++++++++
7 files changed, 2,660 insertions(+), 0 deletions(-)
diff --git a/hal/modules/at24c.c b/hal/modules/at24c.c
new file mode 100644
index 0000000..575bf55
--- /dev/null
+++ b/hal/modules/at24c.c
@@ -0,0 +1,616 @@
+/*********************************************************************************
+ * 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 Avnet.\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;
+ else
+ bytes = len>eeprom->pagesize? eeprom->pagesize : len;
+
+ /* 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);
+}
+
+void dump_buf(const char *prompt, char *buf, size_t len)
+{
+ char line[256];
+ size_t i, j;
+ int offset;
+
+ if( prompt )
+ {
+ printf("%s", prompt);
+ }
+
+ for(i = 0; i < len; i += 16)
+ {
+ offset = snprintf(line, sizeof(line), "%08zx: ", i);
+
+ /* Print hex representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ offset += snprintf(line + offset, sizeof(line) - offset, "%02x ", buf[i + j]);
+ else
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+
+ /* Print ASCII representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ {
+ unsigned char c = buf[i + j];
+ offset += snprintf(line + offset, sizeof(line) - offset, "%c", (c >= 32 && c <= 126) ? c : '.');
+ }
+ else
+ {
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+ }
+
+ /* Print the line */
+ printf("%s\n", line);
+ }
+}
diff --git a/hal/modules/ds18b20.c b/hal/modules/ds18b20.c
new file mode 100644
index 0000000..cb895a4
--- /dev/null
+++ b/hal/modules/ds18b20.c
@@ -0,0 +1,134 @@
+/*********************************************************************************
+ * Copyright: (C) 2023 LingYun IoT System Studio
+ * All rights reserved.
+ *
+ * Filename: ds18b20.c
+ * Description: This file is temperature sensor DS18B20 code
+ *
+ * Version: 1.0.0(2023/8/10)
+ * Author: Guo Wenxue <guowenxue@gmail.com>
+ * ChangeLog: 1, Release initial version on "2023/8/10 12:13:26"
+ *
+ * Pin connection:
+ *
+ * DS18B20 Module Raspberry Pi Board
+ * VCC <-----> #Pin1(3.3V)
+ * DQ <-----> #Pin7(BCM GPIO4)
+ * GND <-----> GND
+ *
+ * /boot/config.txt:
+ *
+ * dtoverlay=w1-gpio-pullup,gpiopin=4
+ *
+ ********************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+int ds18b20_get_temperature(float *temp);
+
+int main(int argc, char *argv[])
+{
+ float temp;
+
+ if( ds18b20_get_temperature(&temp) < 0 )
+ {
+ printf("ERROR: ds18b20 get temprature failure\n");
+ return 1;
+ }
+
+ printf("DS18B20 get temperature: %f ℃\n", temp);
+ return 0;
+}
+
+
+/* File Content:
+ pi@raspberrypi:~/guowenxue $ cat /sys/bus/w1/devices/28-041731f7c0ff/w1_slave
+ 3a 01 4b 46 7f ff 0c 10 a5 : crc=a5 YES
+ 3a 01 4b 46 7f ff 0c 10 a5 t=19625
+ */
+
+int ds18b20_get_temperature(float *temp)
+{
+ char w1_path[50] = "/sys/bus/w1/devices/";
+ char chip[20];
+ char buf[128];
+ DIR *dirp;
+ struct dirent *direntp;
+ int fd =-1;
+ char *ptr;
+ float value;
+ int found = 0;
+
+ if( !temp )
+ {
+ return -1;
+ }
+
+ /*+-------------------------------------------------------------------+
+ *| open dierectory /sys/bus/w1/devices to get chipset Serial Number |
+ *+-------------------------------------------------------------------+*/
+ if((dirp = opendir(w1_path)) == NULL)
+ {
+ printf("opendir error: %s\n", strerror(errno));
+ return -2;
+ }
+
+ while((direntp = readdir(dirp)) != NULL)
+ {
+ if(strstr(direntp->d_name,"28-"))
+ {
+ /* find and get the chipset SN filename */
+ strcpy(chip,direntp->d_name);
+ found = 1;
+ break;
+ }
+ }
+ closedir(dirp);
+
+ if( !found )
+ {
+ printf("Can not find ds18b20 in %s\n", w1_path);
+ return -3;
+ }
+
+ /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */
+ strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path));
+ strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path));
+
+ /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */
+ if( (fd=open(w1_path, O_RDONLY)) < 0 )
+ {
+ printf("open %s error: %s\n", w1_path, strerror(errno));
+ return -4;
+ }
+
+ if(read(fd, buf, sizeof(buf)) < 0)
+ {
+ printf("read %s error: %s\n", w1_path, strerror(errno));
+ return -5;
+ }
+
+ ptr = strstr(buf, "t=");
+ if( !ptr )
+ {
+ printf("ERROR: Can not get temperature\n");
+ return -6;
+ }
+
+ ptr+=2;
+
+ /* convert string value to float value */
+ *temp = atof(ptr)/1000;
+
+ close(fd);
+
+ return 0;
+}
diff --git a/hal/modules/makefile b/hal/modules/makefile
new file mode 100644
index 0000000..433e220
--- /dev/null
+++ b/hal/modules/makefile
@@ -0,0 +1,45 @@
+#********************************************************************************
+# Copyright: (C) 2023 LingYun IoT System Studio
+# All rights reserved.
+#
+# Filename: Makefile
+# Description: This file used to compile all the C file to respective binary,
+# and it will auto detect cross compile or local compile.
+#
+# Version: 1.0.0(11/08/23)
+# Author: Guo Wenxue <guowenxue@gmail.com>
+# ChangeLog: 1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd)
+INSTPATH=/tftp
+
+CROSS_COMPILE=arm-linux-gnueabihf-
+CC=${CROSS_COMPILE}gcc
+
+LDFLAGS += -lm
+
+SRCS = $(wildcard ${VPATH}/*.c)
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+
+SRCFILES = $(wildcard *.c)
+BINARIES=$(SRCFILES:%.c=%)
+
+all: binaries install
+
+binaries: ${BINARIES}
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+install:
+ cp $(BINARIES) ${INSTPATH}
+
+clean:
+ @rm -f *.o *.lo $(BINARIES)
+
+distclean: clean
+ @rm -f tags cscope*
+
+.PHONY: clean entry
diff --git a/hal/modules/sht20.c b/hal/modules/sht20.c
new file mode 100644
index 0000000..5aaa82d
--- /dev/null
+++ b/hal/modules/sht20.c
@@ -0,0 +1,516 @@
+/*********************************************************************************
+ * Copyright: (C) 2023 LingYun IoT System Studio
+ * All rights reserved.
+ *
+ * Filename: sht20.c
+ * Description: This file is temperature and relative humidity sensor SHT20 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:
+ * SHT20 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>
+
+/* SHT2X 7-bit I2C slave address */
+#define SHT2X_CHIPADDR 0x40
+
+/* SHT2X trigger command */
+#define SOFTRESET 0xFE
+#define TRIGGER_TEMPERATURE_NO_HOLD 0xF3
+#define TRIGGER_HUMIDITY_NO_HOLD 0xF5
+
+/* Linux /dev/i2c-x device program API mode */
+enum
+{
+ MODE_IOCTL, /* I2C device API use ioctl() */
+ MODE_RDWR, /* I2C device API use read()/write() */
+};
+
+typedef struct i2c_s
+{
+ 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 */
+} i2c_t;
+
+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);
+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)
+{
+ printf("%s program Version v1.0.0\n", progname);
+ printf("Copyright (C) 2023 Avnet.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+ printf("Usage: %s [OPTION]...\n", progname);
+ printf(" %s is SHT20 temperature and humidity sensor program. \n", progname);
+
+ printf(" -d[device ] Specify I2C device, such as /dev/i2c-1\n");
+ printf(" -m[mode ] Specify API mode, 0 for ioctl and 1 for read()/write()\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 *dev="/dev/i2c-1";
+ char *progname=NULL;
+ int mode = MODE_IOCTL;
+ int rv;
+ float temp, rh;
+ uint8_t serialnumber[8];
+ i2c_t i2c;
+
+ struct option long_options[] = {
+ {"device", required_argument, NULL, 'd'},
+ {"mode", required_argument, NULL, 'm'},
+ {"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, "d:m:vh", long_options, NULL)) != -1)
+ {
+ switch (rv)
+ {
+ case 'd': /* Set I2C device path: /dev/i2c-1 */
+ dev = optarg;
+ break;
+
+ case 'm': /* Set I2C API mode: 0,1 */
+ mode = atoi(optarg) ? MODE_RDWR : MODE_IOCTL;
+ break;
+
+ case 'v': /* Get software version */
+ banner(progname);
+ return EXIT_SUCCESS;
+
+ case 'h': /* Get help information */
+ program_usage(progname);
+ return 0;
+
+ default:
+ break;
+ }
+ }
+
+ 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(&i2c, serialnumber, 8) < 0)
+ {
+ printf("SHT2x get serial number failure\n");
+ return 3;
+ }
+
+ 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);
+
+ i2c_term(&i2c);
+
+ return 0;
+}
+
+int sht2x_softreset(i2c_t *i2c)
+{
+ uint8_t buf[1];
+
+ 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(i2c, buf, 1) < 0 )
+ {
+ return -2;
+ }
+
+ msleep(50);
+
+ return 0;
+}
+
+int sht2x_get_temp_humidity(i2c_t *i2c, float *temp, float *rh)
+{
+ uint8_t buf[4];
+
+ 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(i2c, buf, 1);
+
+ msleep(85); /* datasheet: typ=66, max=85 */
+
+ memset(buf, 0, sizeof(buf));
+ 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(i2c, buf, 1);
+
+ msleep(29); /* datasheet: typ=22, max=29 */
+ memset(buf, 0, sizeof(buf));
+
+ 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(i2c_t *i2c, uint8_t *serialnumber, int size)
+{
+ uint8_t buf[4]={0x0};
+
+ if( !i2c || i2c->fd<0 || !serialnumber || size!=8 )
+ {
+ printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
+ return -1;
+ }
+
+ /* Read SerialNumber from Location 1 */
+ memset(buf, 0, sizeof(buf));
+ buf[0] = 0xfa; /* command for readout on-chip memory */
+ buf[1] = 0x0f; /* on-chip memory address */
+ i2c_write(i2c, buf, 2);
+
+ memset(buf, 0, sizeof(buf));
+ i2c_read(i2c, buf, 4);
+
+ if( !buf[0] && !buf[1] && !buf[2] && !buf[3] )
+ {
+ printf("ERROR: SHT2X device not detected!\n");
+ return -2;
+ }
+
+ serialnumber[5]=buf[0]; /* Read SNB_3 */
+ serialnumber[4]=buf[1]; /* Read SNB_2 */
+ serialnumber[3]=buf[2]; /* Read SNB_1 */
+ serialnumber[2]=buf[3]; /* Read SNB_0 */
+
+ /* Read SerialNumber from Location 2 */
+ memset(buf, 0, sizeof(buf) );
+ buf[0]=0xfc; /* command for readout on-chip memory */
+ buf[1]=0xc9; /* on-chip memory address */
+ i2c_write(i2c, buf, 2);
+
+ memset(buf, 0, sizeof(buf) );
+ 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 */
+
+ print_buf("SHT2x Serial number: ", serialnumber, 8);
+
+ return 0;
+}
+
+/*+----------------------+
+ *| I2C API functions |
+ *+----------------------+*/
+
+int i2c_init(i2c_t *i2c, int mode, char *i2cdev, int addr)
+{
+ 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)
+ {
+ printf("%s() invalid input arguments!\n", __func__);
+ return -1;
+ }
+
+ /* I2C device program API: read()/write() */
+ if( MODE_RDWR == i2c->mode )
+ {
+ write(i2c->fd, data, len);
+ return 0;
+ }
+
+ /* 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;
+ }
+
+ /* I2C device program API: read()/write() */
+ if( MODE_RDWR == i2c->mode )
+ {
+ read(i2c->fd, buf, size);
+ return 0;
+ }
+
+ /* 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 = 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);
+}
+
+void print_buf(const char *prompt, uint8_t *buf, int size)
+{
+ int i;
+
+ if( !buf )
+ {
+ return ;
+ }
+
+ if( prompt )
+ {
+ printf("%s ", prompt);
+ }
+
+ for(i=0; i<size; i++)
+ {
+ printf("%02x ", buf[i]);
+ }
+ printf("\n");
+
+ return ;
+}
+
+void dump_buf(const char *prompt, char *buf, size_t len)
+{
+ char line[256];
+ size_t i, j;
+ int offset;
+
+ if( prompt )
+ {
+ printf("%s\n", prompt);
+ }
+
+ for(i = 0; i < len; i += 16)
+ {
+ offset = snprintf(line, sizeof(line), "%08zx: ", i);
+
+ /* Print hex representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ offset += snprintf(line + offset, sizeof(line) - offset, "%02x ", buf[i + j]);
+ else
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+
+ /* Print ASCII representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ {
+ unsigned char c = buf[i + j];
+ offset += snprintf(line + offset, sizeof(line) - offset, "%c", (c >= 32 && c <= 126) ? c : '.');
+ }
+ else
+ {
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+ }
+
+ /* Print the line */
+ printf("%s\n", line);
+ }
+}
+
diff --git a/hal/modules/tsl2561.c b/hal/modules/tsl2561.c
new file mode 100644
index 0000000..20bd0c2
--- /dev/null
+++ b/hal/modules/tsl2561.c
@@ -0,0 +1,337 @@
+/*********************************************************************************
+ * Copyright: (C) 2023 LingYun IoT System Studio
+ * All rights reserved.
+ *
+ * Filename: sht20.c
+ * Description: This file is the Lux sensor TSL2561 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:
+ * STH20 Module Raspberry Pi Board
+ * VCC <-----> #Pin1(3.3V)
+ * SDA <-----> #Pin27(SDA, BCM GPIO0)
+ * SCL <-----> #Pin28(SCL, BCM GPIO1)
+ * GND <-----> GND
+ *
+ * /boot/config.txt:
+ * dtoverlay=i2c0,pins_0_1
+ *
+ ********************************************************************************/
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <errno.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define TSL2561_I2C_ADDR 0x39
+
+#define CONTROL_REG 0x80
+#define REG_COUNT 4
+
+#define POWER_UP 0x03
+#define POWER_DOWN 0x00
+
+#define OFF 0
+#define ON 1
+
+/* Register Address */
+enum
+{
+ /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
+ DATA0LOW = 0x8C,
+ DATA0HIGH,
+
+ /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
+ DATA1LOW,
+ DATA1HIGH,
+};
+
+static const int regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
+
+float tsl2561_get_lux(int fd);
+static inline void print_datime(void);
+
+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 TSL2561 Lux sensor program.\n", progname);
+
+ printf(" -d[device ] Specify I2C device, such as /dev/i2c-0\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)
+{
+ int fd, rv;
+ float lux;
+ char *dev = "/dev/i2c-0";
+ char *progname=NULL;
+
+ struct option long_options[] = {
+ {"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, "d:vh", long_options, NULL)) != -1)
+ {
+ switch (rv)
+ {
+ 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;
+ }
+ }
+
+ /*+--------------------------------+
+ *| open /dev/i2c-x device |
+ *+--------------------------------+*/
+ if( (fd=open(dev, O_RDWR)) < 0)
+ {
+ printf("i2c device '%s' open failed: %s\n", dev, strerror(errno));
+ return -1;
+ }
+
+ while(1)
+ {
+ lux = tsl2561_get_lux(fd);
+
+ print_datime();
+ printf("TSLl2561 get lux: %.3f\n", lux);
+
+ sleep(1);
+ }
+
+ close(fd);
+ return 0;
+}
+
+static inline void print_datime(void)
+{
+ time_t tmp;
+ struct tm *p;
+
+ time(&tmp);
+
+ p=localtime(&tmp);
+
+
+ printf("%d-%02d-%02d %02d:%02d:%02d\t", (p->tm_year+1900),(p->tm_mon+1),
+ p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
+
+}
+
+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);
+ return ;
+}
+
+void tsl2561_power(int fd, int cmd)
+{
+ struct i2c_msg msg;
+ struct i2c_rdwr_ioctl_data data;
+ unsigned char buf[2];
+
+ msg.addr= TSL2561_I2C_ADDR;
+ msg.flags=0; /* write */
+ msg.len= 1;
+ msg.buf= buf;
+
+ data.nmsgs= 1;
+ data.msgs= &msg;
+
+ msg.buf[0]=CONTROL_REG;
+ if( ioctl(fd, I2C_RDWR, &data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return ;
+ }
+
+
+ if( cmd )
+ msg.buf[0]=POWER_UP;
+ else
+ msg.buf[0]=POWER_DOWN;
+
+ if( ioctl(fd, I2C_RDWR, &data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return ;
+ }
+
+ return ;
+}
+
+int tsl2561_readreg(int fd, unsigned char regaddr, unsigned char *regval)
+{
+ struct i2c_msg msg;
+ struct i2c_rdwr_ioctl_data data;
+ unsigned char buf[2];
+
+ msg.addr= TSL2561_I2C_ADDR;
+ msg.flags=0; /* write */
+ msg.len= 1;
+ msg.buf= buf;
+ msg.buf[0] = regaddr;
+
+ data.nmsgs= 1;
+ data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ msg.addr= TSL2561_I2C_ADDR;
+ msg.flags=I2C_M_RD; /* read */
+ msg.len= 1;
+ msg.buf= buf;
+
+ data.nmsgs= 1;
+ data.msgs= &msg;
+
+ if( ioctl(fd, I2C_RDWR, &data) < 0 )
+ {
+ printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ *regval = msg.buf[0];
+ return 0;
+}
+
+float tsl2561_get_lux(int fd)
+{
+ int i;
+ unsigned char reg_data[REG_COUNT];
+ unsigned char buf;
+
+ int chn0_data = 0;
+ int chn1_data = 0;
+
+ float div = 0.0;
+ float lux = 0.0;
+
+ tsl2561_power(fd, ON);
+
+ msleep(410); /* t(CONV) MAX 400ms */
+
+ /* Read register Channel0 and channel1 data from register */
+ for(i=0; i<REG_COUNT; i++)
+ {
+ tsl2561_readreg(fd, regs_addr[i], ®_data[i]);
+ }
+
+ chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW */
+ chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 + DATA1LOW */
+
+ if( chn0_data<=0 || chn1_data<0 )
+ {
+ lux = 0.0;
+ goto OUT;
+ }
+
+ div = (float)chn1_data / (float)chn0_data;
+
+ if( div>0 && div<=0.5 )
+ lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);
+
+ else if( div>0.5 && div<=0.61 )
+ lux = 0.0224*chn0_data-0.031*chn1_data;
+
+ else if( div>0.61 && div<=0.8 )
+ lux = 0.0128*chn0_data-0.0153*chn1_data;
+
+ else if( div>0.8 && div<=1.3 )
+ lux = 0.00146*chn0_data-0.00112*chn1_data;
+
+ else if( div>1.3 )
+ lux = 0.0;
+
+OUT:
+ tsl2561_power(fd, OFF);
+ return lux;
+}
+
+static inline void dump_buf(const char *prompt, char *buf, int size)
+{
+ int i;
+
+ if( !buf )
+ {
+ return ;
+ }
+
+ if( prompt )
+ {
+ printf("%-32s ", prompt);
+ }
+
+ for(i=0; i<size; i++)
+ {
+ printf("%02x ", buf[i]);
+ }
+ printf("\n");
+
+ return ;
+}
diff --git a/hal/modules/w25qflash.c b/hal/modules/w25qflash.c
new file mode 100644
index 0000000..5623718
--- /dev/null
+++ b/hal/modules/w25qflash.c
@@ -0,0 +1,831 @@
+/*********************************************************************************
+ * 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:
+ * 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)
+ *
+ * /boot/config.txt:
+ * dtparam=spi=on
+ *
+ ********************************************************************************/
+
+
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <linux/ioctl.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+#include "w25qflash.h"
+
+#ifdef _W25QXX_DEBUG
+#define spinor_print(format,args...) printf(format, ##args)
+#else
+#define spinor_print(format,args...) do{} while(0)
+#endif
+
+#define spinor_Delay(delay) usleep(delay*1000)
+
+/*+-----------------------+
+ *| Entry Functions |
+ *+-----------------------+*/
+
+void dump_buf(const char *prompt, char *buf, size_t len);
+
+int main (int argc, char **argv)
+{
+ spinor_test();
+
+ return 0;
+}
+
+
+/*+-----------------------+
+ *| SPI API Functions |
+ *+-----------------------+*/
+
+#define SPI_DEV "/dev/spidev0" /* SPI device 0 */
+#define SPI_CS0 0 /* SPI CS0 */
+#define SPI_CS1 1 /* SPI CS1 */
+#define SPI_BITS 8
+#define SPI_MODE 0 /* (SPI_CPHA|SPI_CPOL) */
+#define SPI_SPEED 500000
+
+#define SPI_DUMMY_BYTE 0xA5
+
+int spi_lowlevel_init(spi_bus_t *spi)
+{
+ uint8_t bits = SPI_BITS;
+ uint32_t speed = SPI_SPEED;
+ uint32_t mode = SPI_MODE;
+ uint32_t request;
+ char dev[32];
+ int rv = 0;
+
+ snprintf(dev, sizeof(dev), "%s.%d", SPI_DEV, spi->cs);
+
+ spi->hspi = open(dev, O_RDWR);
+ if (spi->hspi < 0)
+ {
+ printf("ERROR: open device %s failure: %s\r\n", dev, strerror(errno));
+ return -2;
+ }
+ printf("Open '%s' for W25Q SPI norflash\n", dev);
+
+ /*
+ * spi mode
+ */
+ request = mode;
+ if( ioctl(spi->hspi, SPI_IOC_WR_MODE32, &mode) < 0 )
+ {
+ printf("ERROR: can't set spi mode\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ if( ioctl(spi->hspi, SPI_IOC_RD_MODE32, &mode) < 0 )
+ {
+ printf("ERROR: can't get spi mode\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ if (request != mode)
+ {
+ printf("WARNING: device does not support requested mode 0x%x\n", request);
+ }
+
+ /*
+ * bits per word
+ */
+ if( ioctl(spi->hspi, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0 )
+ {
+ printf("ERROR: can't set bits per word\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ if( ioctl(spi->hspi, SPI_IOC_RD_BITS_PER_WORD, &bits) < 0 )
+ {
+ printf("ERROR: can't get bits per word\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ /*
+ * max speed hz
+ */
+ if( ioctl(spi->hspi, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0 )
+ {
+ printf("ERROR: can't set max speed hz\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ if( ioctl(spi->hspi, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0 )
+ {
+ printf("ERROR: can't get max speed hz\n");
+ rv = -3;
+ goto cleanup;
+ }
+
+ printf("spi mode: 0x%x\n", mode);
+ printf("bits per word: %u\n", bits);
+ printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
+
+cleanup:
+ if( rv < 0 )
+ {
+ close(spi->hspi);
+ spi->hspi = -1;
+ }
+
+ return rv;
+}
+
+void spi_cs_enable(spi_bus_t *spi)
+{
+ /*
+ * No need set CS in Linux because the device name /dev/spi0.0
+ * will choose the first slave device, second slave is spi0.1
+ */
+ (void)0;
+}
+
+void spi_cs_disable(spi_bus_t *spi)
+{
+ (void)0;
+}
+
+void spi_xcmd(spi_bus_t *spi, uint8_t command)
+{
+ uint8_t rxbyte;
+ struct spi_ioc_transfer tr = {
+ .tx_buf = (unsigned long)&command,
+ .rx_buf = (unsigned long)&rxbyte,
+ .len = 1,
+ .delay_usecs = 0,
+ .speed_hz = SPI_SPEED,
+ .bits_per_word = SPI_BITS,
+ };
+
+ if( ioctl(spi->hspi, SPI_IOC_MESSAGE(1), &tr) < 0 )
+ {
+ printf("ERROR: can't send spi message:%s\n", strerror(errno));
+ }
+
+ return;
+}
+
+void spi_xfer(spi_bus_t *spi, uint8_t *send_buf, uint8_t *recv_buf, int bytes)
+{
+ struct spi_ioc_transfer tr = {
+ .tx_buf = (unsigned long)send_buf,
+ .rx_buf = (unsigned long)recv_buf,
+ .len = bytes,
+ .delay_usecs = 0,
+ .speed_hz = SPI_SPEED,
+ .bits_per_word = SPI_BITS,
+ };
+
+ if( ioctl(spi->hspi, SPI_IOC_MESSAGE(1), &tr) < 0 )
+ {
+ printf("ERROR: can't send spi message:%s\n", strerror(errno));
+ }
+
+ return;
+}
+
+#define SPI_INFO(_hspi, _cs) {\
+ .hspi = _hspi, \
+ .hspi = _cs, \
+ .select = spi_cs_enable, \
+ .deselect = spi_cs_disable, \
+ .xcmd = spi_xcmd, \
+ .xfer = spi_xfer, \
+}
+
+static spi_bus_t spinor_spi = SPI_INFO(-1, SPI_CS0);
+
+
+/*+-----------------------+
+ *| W25Q SPI Norflash ID |
+ *+-----------------------+*/
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/* JEDEC ID the 3rd bytes is the storage capacity */
+#pragma GCC diagnostic ignored "-Wshift-count-overflow"
+#define CAPCITY_ID(id) (1UL<<(id&0xFF))
+#define NOR_INFO(_name, _jedec_id) \
+ .name = _name, \
+.jedec_id = _jedec_id, \
+.block_size = W25Q_BLKSIZE, \
+.sector_size= W25Q_SECSIZE, \
+.page_size = W25Q_PAGSIZE, \
+.capacity = CAPCITY_ID(_jedec_id), \
+.n_blocks = CAPCITY_ID(_jedec_id)/W25Q_BLKSIZE, \
+.n_sectors = CAPCITY_ID(_jedec_id)/W25Q_SECSIZE, \
+.n_pages = CAPCITY_ID(_jedec_id)/W25Q_PAGSIZE, \
+
+static flash_t flash_ids[] = {
+ { NOR_INFO("W25Q512", 0xef4020) },
+ { NOR_INFO("W25Q256", 0xef4019) },
+ { NOR_INFO("W25Q128", 0xef4018) },
+ { NOR_INFO("W25Q64", 0xef4017) },
+ { NOR_INFO("W25Q32", 0xef4016) },
+ { NOR_INFO("W25Q16", 0xef4015) },
+ { NOR_INFO("W25Q80", 0xef4014) },
+ { NOR_INFO("W25Q40", 0xef4013) },
+ { NOR_INFO("W25Q20", 0xef4012) },
+ { NOR_INFO("W25Q10", 0xef4011) },
+};
+
+/*+-------------------------------+
+ *| SPI Norflash HighLevel API |
+ *+-------------------------------+*/
+
+/* SPI Norflash API test function */
+void spinor_test(void)
+{
+ spinor_t spinor;
+ int i;
+ uint8_t buf[W25Q_PAGSIZE*2];
+
+ if( spinor_init(&spinor) < 0 )
+ return ;
+
+ //spinor_erase_chip(&spinor);
+ //spinor_erase_block(&spinor, 1, W25Q_BLKSIZE);
+ spinor_erase_sector(&spinor, 1, W25Q_SECSIZE);
+
+ memset(buf, 0, sizeof(buf));
+ spinor_read(&spinor, 0, buf, sizeof(buf));
+ dump_buf("<<<Read data after erase:\n", (char *)buf, sizeof(buf));
+
+ /* Read/Write data test on address not page align */
+ for(i=0; i<sizeof(buf); i++)
+ buf[i] = i;
+ spinor_write(&spinor, 16, buf, W25Q_PAGSIZE);
+
+ memset(buf, 0, sizeof(buf));
+ spinor_read(&spinor, 0, buf, W25Q_PAGSIZE*2);
+ dump_buf("<<<Read data after write:\n", (char *)buf, sizeof(buf));
+
+ return ;
+}
+
+/* Initial SPI and detect the flash chip */
+int spinor_init(spinor_t *spinor)
+{
+ int i, found = 0;
+ uint32_t jedec_id;
+
+ spinor->lock = 0;
+ spinor->spi = &spinor_spi;
+
+ /* Initial SPI bus */
+ if( spi_lowlevel_init(spinor->spi) < 0 )
+ {
+ printf("ERROR: SPI lowlevel init failed\r\n");
+ return -2;
+ }
+
+ /* Read JEDEC ID to find the flash */
+ jedec_id = spinor_read_jedecid(spinor->spi);
+ for(i=0; i<ARRAY_SIZE(flash_ids); i++)
+ {
+ if(flash_ids[i].jedec_id == jedec_id)
+ {
+ found = 1;
+ spinor->flash = &flash_ids[i];
+ break;
+ }
+ }
+
+ if( !found )
+ {
+ printf("ERROR: W25Q Norflash JEDEC ID detect failed!\r\n");
+ return -3;
+ }
+
+ printf("Norflash %s ID[0x%x] detected, capacity %llu KB, %u blocks, %u pages.\r\n",
+ spinor->flash->name, jedec_id, spinor->flash->capacity>>10,
+ spinor->flash->n_blocks, spinor->flash->n_pages);
+
+ return 0;
+}
+
+/* Description: Erase whole flash chip.
+ * Reference : P60, 8.2.32 Chip Erase (C7h / 60h)
+ */
+int spinor_erase_chip(spinor_t *spinor)
+{
+ spi_bus_t *spi = spinor->spi;
+ uint32_t timeout;
+ uint8_t regval;
+
+ while (spinor->lock == 1)
+ spinor_Delay(1);
+
+ spinor->lock = 1;
+ spi->select(spi);
+
+ spinor_print("Norflash EraseChip Begin...\r\n");
+
+ /* Wait for flash ready */
+ spinor_WaitForReady(spi);
+
+ /* Write Enable */
+ spinor_write_enable(spi);
+
+ /* Entire flash erase */
+ spi->xcmd(spi, SPINOR_OP_CHIP_ERASE);
+
+ /* Wait for erase finish */
+ timeout = 0;
+ while (1)
+ {
+ if (timeout == 300)
+ {
+ spinor_print("\r\nErase entire flash timeout\r\n");
+ break;
+ }
+
+ regval = spinor_read_status_reg(spi, SPINOR_OP_RDSR1);
+ if( !(regval&0x1) )
+ {
+ spinor_print("\r\nNorflash EraseChip done.\r\n");
+ break;
+ }
+
+#ifdef _W25QXX_DEBUG
+ printf(".");
+ spinor_Delay(1000);
+#endif
+ }
+
+ printf("\n");
+
+ spi->deselect(spi);
+ spinor->lock = 0;
+
+ return 0;
+}
+
+/* Description: Erase blocks by 64KiB,
+ * Reference : P59, 8.2.31 64KB Block Erase with 4-Byte Address (DCh)
+ * @address is the erase start physical address, which can be not block alignment such as 0x10001.
+ * @size is the erase size, which can be larger than a block such as 4097, and it will erase 2 blocks;
+ */
+int spinor_erase_block(spinor_t *spinor, uint32_t address, uint32_t size)
+{
+ spi_bus_t *spi = spinor->spi;
+ flash_t *flash = spinor->flash;
+ uint32_t block, first, last;
+ uint32_t addr;
+ uint8_t buf[5];
+ int bytes = 0;
+
+ while (spinor->lock == 1)
+ spinor_Delay(1);
+
+ spinor->lock = 1;
+ spi->select(spi);
+
+ spinor_print("Norflash Erase %d Bytes Block@0x%x Begin...\r\n", size, address);
+
+ /* find first and last erase block */
+ first = address / flash->block_size;
+ last = (address+size-1) / flash->block_size;
+
+ /* start erase all the blocks */
+ for( block=first; block<=last; block++)
+ {
+ addr = block * flash->sector_size;
+ spinor_print("Norflash Erase Block@%x ...\r\n", addr);
+
+ spinor_WaitForReady(spi);
+ spinor_write_enable(spi);
+
+ if (spinor->flash->n_blocks >= 512 ) /* larger than W25Q256 */
+ {
+ buf[bytes++] = SPINOR_OP_BE_4K_4B;
+ buf[bytes++] = (addr & 0xFF000000) >> 24;
+ }
+ else
+ {
+ buf[bytes++] = SPINOR_OP_BE_4K;
+ }
+ buf[bytes++] = (addr & 0xFF0000) >> 16 ;
+ buf[bytes++] = (addr & 0xFF00) >> 8 ;
+ buf[bytes++] = (addr & 0xFF);
+
+ spi->xfer(spi, buf, NULL, bytes);
+
+ spinor_WaitForReady(spi);
+ }
+
+ spinor_print("Norflash EraseBlock@0x%x done.\r\n", address);
+
+ spi->deselect(spi);
+ spinor->lock = 0;
+
+ return 0;
+}
+
+/* Description: Erase sectors by 4KiB
+ * Reference : P56, 8.2.28 Sector Erase with 4-Byte Address (21h)
+ * @address is the erase start physical address, which can be not sector alignment such as 0x1001.
+ * @size is the erase size, which can be larger than a sector such as 4097, and it will erase 2 sectors;
+ */
+int spinor_erase_sector(spinor_t *spinor, uint32_t address, uint32_t size)
+{
+ spi_bus_t *spi = spinor->spi;
+ flash_t *flash = spinor->flash;
+ uint32_t sector, first, last;
+ uint32_t addr;
+ uint8_t buf[5];
+ int bytes = 0;
+
+ while (spinor->lock == 1)
+ spinor_Delay(1);
+
+ spinor->lock = 1;
+ spi->select(spi);
+
+ spinor_print("Norflash Erase %d Bytes Sector@0x%x Begin...\r\n", size, address);
+
+ /* find first and last erase sector */
+ first = address / flash->sector_size;
+ last = (address+size-1) / flash->sector_size;
+
+ /* start erase all the sectors */
+ for( sector=first; sector<=last; sector++)
+ {
+ addr = sector * flash->sector_size;
+ spinor_print("Norflash Erase Sector@%x ...\r\n", addr);
+
+ spinor_WaitForReady(spi);
+ spinor_write_enable(spi);
+
+ if (spinor->flash->n_blocks >= 512 ) /* larger than W25Q256 */
+ {
+ buf[bytes++] = SPINOR_OP_SE_4B;
+ buf[bytes++] = (addr & 0xFF000000) >> 24;
+ }
+ else
+ {
+ buf[bytes++] = SPINOR_OP_SE;
+ }
+ buf[bytes++] = (addr & 0xFF0000) >> 16 ;
+ buf[bytes++] = (addr & 0xFF00) >> 8 ;
+ buf[bytes++] = (addr & 0xFF);
+
+ spi->xfer(spi, buf, NULL, bytes);
+
+ spinor_WaitForReady(spi);
+ }
+
+ spinor_print("Norflash EraseSector@0x%x done.\r\n", address);
+
+ spi->deselect(spi);
+ spinor->lock = 0;
+
+ return 0;
+}
+
+/* Description: Page random write by 256B
+ * @addr is the write start physical address, which can be not page alignment such as 0x101.
+ * @size is the write size, which can be larger than a page such as 257, and it will write 2 pages;
+ */
+int spinor_write(spinor_t *spinor, uint32_t address, uint8_t *data, uint32_t size)
+{
+ spi_bus_t *spi = spinor->spi;
+ flash_t *flash = spinor->flash;
+ uint32_t page, first, last;
+ uint32_t addr, ofset, len;
+ uint8_t buf[W25Q_PAGSIZE+OVERHEAD_SIZE];
+ int bytes = 0;
+
+ if( address+size > spinor->flash->capacity )
+ return -1;
+
+ while (spinor->lock == 1)
+ spinor_Delay(1);
+
+ spinor->lock = 1;
+ spi->select(spi);
+
+ spinor_print("Norflash Write %d Bytes to addr@0x%x Begin...\r\n", size, address);
+
+ /* find first and last write page */
+ first = address / flash->page_size;
+ last = (address+size-1) / flash->page_size;
+
+ /* address in page and offset in buffer */
+ addr = address;
+ ofset = 0;
+
+ /* start write all the pages */
+ for( page=first; page<=last; page++)
+ {
+ len = flash->page_size - (addr%flash->page_size);
+ len = len > size ? size : len;
+
+ spinor_print("Norflash write addr@0x%x, %u bytes\r\n", addr, len);
+
+ spinor_WaitForReady(spi);
+ spinor_write_enable(spi);
+
+ bytes = 0;
+ if (spinor->flash->n_blocks >= 512 )
+ {
+ buf[bytes++] = SPINOR_OP_PP_4B;
+ buf[bytes++] = (addr & 0xFF000000) >> 24;
+ }
+ else
+ {
+ buf[bytes++] = SPINOR_OP_PP;
+ }
+ buf[bytes++] = (addr & 0xFF0000) >> 16 ;
+ buf[bytes++] = (addr & 0xFF00) >> 8 ;
+ buf[bytes++] = (addr & 0xFF);
+
+ /* send command and data */
+ memcpy(&buf[bytes], data+ofset, len);
+ bytes += len;
+
+ spi->xfer(spi, buf, NULL, bytes);
+
+ spinor_WaitForReady(spi);
+
+ addr += len;
+ ofset += len;
+ size -= len;
+ }
+
+ spinor_print("Norflash WriteByte@0x%x done.\r\n", address);
+
+ spi->deselect(spi);
+ spinor->lock = 0;
+
+ return 0;
+}
+
+/* Description: The Fast Read instruction can read the entire memory chip.
+ * Reference : P41, 8.2.13 Fast Read with 4-Byte Address (0Ch)
+ * @address is the read start physical address, which can be not page alignment such as 0x101.
+ * @size is the read size, which can be larger than a page such as 257, and it will read 2 pages;
+ */
+int spinor_read(spinor_t *spinor, uint32_t address, uint8_t *data, uint32_t size)
+{
+ spi_bus_t *spi = spinor->spi;
+ uint8_t buf[W25Q_PAGSIZE+OVERHEAD_SIZE];
+ int bytes = 0;
+ int ofset;
+ uint32_t addr = address;
+
+ if( address+size > spinor->flash->capacity )
+ return -1;
+
+ while (spinor->lock == 1)
+ spinor_Delay(1);
+
+ spinor->lock = 1;
+ spi->select(spi);
+
+ spinor_print("Norflash Read %d Bytes from addr@0x%x Begin...\r\n", size, address);
+
+ while( size > 0 )
+ {
+ bytes = size>W25Q_PAGSIZE ? W25Q_PAGSIZE : size;
+ memset(buf, SPI_DUMMY_BYTE, sizeof(buf));
+
+ spinor_print("Norflash read addr@0x%x, %d bytes\r\n", addr, bytes);
+
+ /* send instruction and address */
+ ofset = 0;
+ if (spinor->flash->n_blocks >= 512 )
+ {
+ buf[ofset++] = SPINOR_OP_READ_FAST_4B;
+ buf[ofset++] = (addr & 0xFF000000) >> 24;
+ }
+ else
+ {
+ buf[ofset++] = SPINOR_OP_READ_FAST;
+ }
+ buf[ofset++] = (addr & 0xFF0000) >> 16 ;
+ buf[ofset++] = (addr & 0xFF00) >> 8 ;
+ buf[ofset++] = (addr & 0xFF);
+
+ ofset += 1; /* Skip first dummy byte */
+
+ /* Send command and read data out */
+ spi->xfer(spi, buf, buf, ofset+bytes);
+
+ memcpy(data, &buf[ofset], bytes);
+ size -= bytes;
+ addr += bytes;
+ data += bytes;
+ }
+
+ spinor_print("Norflash ReadBytes@0x%x done.\r\n", address);
+
+ spi->deselect(spi);
+ spinor->lock = 0;
+
+ return 0;
+}
+
+/*+-------------------------------+
+ *| SPI Norflash LowLevel API |
+ *+-------------------------------+*/
+
+/* Description: Read the chipset UNIQUE ID.
+ * Reference : P68, 8.2.40 Read Unique ID Number (4Bh)
+ */
+int spinor_read_uniqid(spi_bus_t *spi, uint8_t *uniq_id)
+{
+ uint8_t i;
+ uint8_t buf[13]; /* Instruction(1B) + Dummy(4B) + UID(8B)*/
+
+ if( !uniq_id )
+ return -1;
+
+ /* Enable SPI chip select */
+ spi->select(spi);
+
+ /* Wait for flash ready */
+ spinor_WaitForReady(spi);
+
+ buf[0] = SPINOR_OP_RDUID;
+ spi->xfer(spi, buf, buf, sizeof(buf));
+
+ /* Skip 4 bytes dummy bytes */
+ for (i=0; i<8; i++)
+ {
+ uniq_id[i] = buf[5+i];
+ }
+
+ /* Disable SPI chip select */
+ spi->deselect(spi);
+
+ return 0;
+}
+
+/* Description: Read the chipset JEDEC ID.
+ * Reference : P69, 8.2.41 Read JEDEC ID (9Fh)
+ */
+uint32_t spinor_read_jedecid(spi_bus_t *spi)
+{
+ uint32_t jedec_id = 0x0;
+ uint8_t buf[4];
+
+ /* Enable SPI chip select */
+ spi->select(spi);
+
+ /* Wait for flash ready */
+ spinor_WaitForReady(spi);
+
+ /* Send Read JEDEC ID command 0x9F */
+ buf[0] = SPINOR_OP_RDID;
+ spi->xfer(spi, buf, buf, sizeof(buf));
+ jedec_id = (buf[1] << 16) | (buf[2] << 8) | buf[3];
+
+ /* Disable SPI chip select */
+ spi->deselect(spi);
+
+ return jedec_id;
+}
+
+/* Description: Write Enable
+ * Reference : P31, 8.2.1 Write Enable (06h)
+ */
+void spinor_write_enable(spi_bus_t *spi)
+{
+ spi->xcmd(spi, SPINOR_OP_WREN);
+
+ spinor_WaitForReady(spi);
+}
+
+/* Description: Write Disable
+ * Reference : P32, 8.2.3 Write Disable (04h)
+ */
+void spinor_write_disable(spi_bus_t *spi)
+{
+ spi->xcmd(spi, SPINOR_OP_WRDI);
+
+ spinor_WaitForReady(spi);
+}
+
+/* Description: Read Status Register
+ * Reference : P32, 8.2.4 Read Status Register-1 (05h), Status Register-2 (35h) & Status Register-3 (15h)
+ */
+uint8_t spinor_read_status_reg(spi_bus_t *spi, uint8_t reg)
+{
+ uint8_t buf[2];
+
+ buf[0] = reg;
+ spi->xfer(spi, buf, buf, sizeof(buf));
+
+ return buf[1];
+}
+
+/* Description: Write Status Register
+ * Reference : P33, 8.2.5 Write Status Register-1 (01h), Status Register-2 (31h) & Status Register-3 (11h)
+ */
+void spinor_write_status_reg(spi_bus_t *spi, uint8_t reg, uint8_t value)
+{
+ uint8_t buf[2];
+
+ buf[0] = reg;
+ buf[1] = value;
+ spi->xfer(spi, buf, buf, sizeof(buf));
+}
+
+/* Description: Wait flash program/erase finished by read Status Register for BUSY bit
+ * Reference : P15, 7.1 Status Registers
+ */
+void spinor_WaitForReady(spi_bus_t *spi)
+{
+ uint8_t regval;
+
+ do
+ {
+ regval = spinor_read_status_reg(spi, SPINOR_OP_RDSR1);
+ } while ((regval & 0x01) == 0x01);
+}
+
+/*+----------------------+
+ *| Misc functions |
+ *+----------------------+*/
+
+void dump_buf(const char *prompt, char *buf, size_t len)
+{
+ char line[256];
+ size_t i, j;
+ int offset;
+
+ if( prompt )
+ {
+ printf("%s", prompt);
+ }
+
+ for(i = 0; i < len; i += 16)
+ {
+ offset = snprintf(line, sizeof(line), "%08zx: ", i);
+
+ /* Print hex representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ offset += snprintf(line + offset, sizeof(line) - offset, "%02x ", buf[i + j]);
+ else
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+
+ /* Print ASCII representation */
+ for (j = 0; j < 16; j++)
+ {
+ if (i + j < len)
+ {
+ unsigned char c = buf[i + j];
+ offset += snprintf(line + offset, sizeof(line) - offset, "%c", (c >= 32 && c <= 126) ? c : '.');
+ }
+ else
+ {
+ offset += snprintf(line + offset, sizeof(line) - offset, " ");
+ }
+ }
+
+ /* Print the line */
+ printf("%s\n", line);
+ }
+}
diff --git a/hal/modules/w25qflash.h b/hal/modules/w25qflash.h
new file mode 100644
index 0000000..6e946a0
--- /dev/null
+++ b/hal/modules/w25qflash.h
@@ -0,0 +1,181 @@
+/*********************************************************************************
+ * 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:
+ * 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)
+ *
+ * /boot/config.txt:
+ * dtparam=spi=on
+ *
+ ********************************************************************************/
+
+#ifndef _W25QFLASH_H
+#define _W25QFLASH_H
+
+#include <stdbool.h>
+
+#define _W25QXX_DEBUG 1
+
+/* W25Q SPI Norflash page/sector/block size */
+#define W25Q_PAGSIZE 256 /* 1Page=256B */
+#define W25Q_SECSIZE 4096 /* 1Sector=16Pages=4KB */
+#define W25Q_BLKSIZE 65536 /* 1Block=16Sector=64KB */
+
+/* Flash opcodes. Refer to <<W25Q256JV.pdf>> P26 Table 8.1.2 Instruction Set Table */
+#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
+#define SPINOR_OP_RDUID 0x4b /* Read unique ID */
+#define SPINOR_OP_WRSR1 0x01 /* Write status register-1 */
+#define SPINOR_OP_WRSR2 0x31 /* Write status register-2 */
+#define SPINOR_OP_WRSR3 0x11 /* Write status register-3 */
+#define SPINOR_OP_BP 0x02 /* Byte program */
+#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
+#define SPINOR_OP_WRDI 0x04 /* Write disable */
+#define SPINOR_OP_RDSR1 0x05 /* Read status register-1 */
+#define SPINOR_OP_RDSR2 0x35 /* Read status register-2 */
+#define SPINOR_OP_RDSR3 0x15 /* Read status register-3 */
+#define SPINOR_OP_WREN 0x06 /* Write enable */
+#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
+
+#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
+#define SPINOR_OP_BE_4K_4B 0xdc /* Block erase (64KiB) with 4-Byte Address */
+#define SPINOR_OP_BE_4K 0xd8 /* Block erase (64KiB) */
+#define SPINOR_OP_SE_4B 0x21 /* Sector erase (4KiB) with 4-Byte Address */
+#define SPINOR_OP_SE 0x20 /* Sector erase (4KiB) */
+#define SPINOR_OP_PP_4B 0x12 /* Page Program (up to 256 bytes) with 4-Byte Address */
+#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */
+#define SPINOR_OP_SRST 0x99 /* Software Reset */
+
+/* All the above command default work as 4 bytes mode */
+#define OVERHEAD_SIZE 6
+
+typedef struct spi_bus
+{
+ int hspi; /* SPI bus handler */
+ int cs; /* SPI cs pin number */
+
+ void (*select)(struct spi_bus *spi); /* CS enable function */
+ void (*deselect)(struct spi_bus *spi); /* CS disable function */
+ void (*xcmd)(struct spi_bus *spi, uint8_t command); /* Send a byte command */
+ void (*xfer)(struct spi_bus *spi, uint8_t *send_buf, uint8_t *recv_buf, int bytes); /* Transmit and Receive N byte */
+} spi_bus_t;
+
+typedef struct flash_s
+{
+ char *name; /* Chip name */
+ uint32_t jedec_id; /* JEDEC ID, 3 bytes */
+ uint64_t capacity; /* Chip size in bytes */
+ uint32_t block_size; /* Block size in bytes */
+ uint32_t sector_size; /* Sector size in bytes */
+ uint32_t page_size; /* Page size in bytes */
+ uint32_t n_blocks; /* Number of blocks */
+ uint32_t n_sectors; /* Number of sectors */
+ uint32_t n_pages; /* Number of pages */
+} flash_t;
+
+
+typedef struct spinor_s
+{
+ spi_bus_t *spi;
+ flash_t *flash;
+ uint8_t lock;
+} spinor_t;
+
+
+/*+-------------------------------+
+ *| SPI Norflash HighLevel API |
+ *+-------------------------------+*/
+
+/* SPI Norflash API test function */
+extern void spinor_test(void);
+
+/* Initial SPI and detect the flash chip */
+extern int spinor_init(spinor_t *spinor);
+
+/* Description: Erase whole flash chip.
+ * Reference : P60, 8.2.32 Chip Erase (C7h / 60h)
+ */
+extern int spinor_erase_chip(spinor_t *spinor);
+
+/* Description: Erase blocks by 64KiB,
+ * Reference : P59, 8.2.31 64KB Block Erase with 4-Byte Address (DCh)
+ * @address is the erase start physical address, which can be not block alignment such as 0x10001.
+ * @size is the erase size, which can be larger than a block such as 4097, and it will erase 2 blocks;
+ */
+extern int spinor_erase_block(spinor_t *spinor, uint32_t address, uint32_t size);
+
+/* Description: Erase sectors by 4KiB
+ * Reference : P56, 8.2.28 Sector Erase with 4-Byte Address (21h)
+ * @address is the erase start physical address, which can be not sector alignment such as 0x1001.
+ * @size is the erase size, which can be larger than a sector such as 4097, and it will erase 2 sectors;
+ */
+extern int spinor_erase_sector(spinor_t *spinor, uint32_t address, uint32_t size);
+
+/* Description: Page random write by 256B
+ * @addr is the write start physical address, which can be not page alignment such as 0x101.
+ * @size is the write size, which can be larger than a page such as 257, and it will write 2 pages;
+ */
+extern int spinor_write(spinor_t *spinor, uint32_t address, uint8_t *data, uint32_t bytes);
+
+/* Description: The Fast Read instruction can read the entire memory chip.
+ * Reference : P41, 8.2.13 Fast Read with 4-Byte Address (0Ch)
+ * @address is the read start physical address, which can be not page alignment such as 0x101.
+ * @size is the read size, which can be larger than a page such as 257, and it will read 2 pages;
+ */
+extern int spinor_read(spinor_t *spinor, uint32_t address, uint8_t *buf, uint32_t bytes);
+
+/*+-------------------------------+
+ *| SPI Norflash LowLevel API |
+ *+-------------------------------+*/
+
+/* Description: Read the chipset UNIQUE ID.
+ * Reference : P68, 8.2.40 Read Unique ID Number (4Bh)
+ */
+int spinor_read_uniqid(spi_bus_t *spi, uint8_t *uniq_id);
+
+/* Description: Read the chipset JEDEC ID.
+ * Reference : P69, 8.2.41 Read JEDEC ID (9Fh)
+ */
+uint32_t spinor_read_jedecid(spi_bus_t *spi);
+
+/* Description: Write Enable
+ * Reference : P31, 8.2.1 Write Enable (06h)
+ */
+void spinor_write_enable(spi_bus_t *spi);
+
+/* Description: Write Disable
+ * Reference : P32, 8.2.3 Write Disable (04h)
+ */
+void spinor_write_disable(spi_bus_t *spi);
+
+/* Description: Read Status Register
+ * Reference : P32, 8.2.4 Read Status Register-1 (05h), Status Register-2 (35h) & Status Register-3 (15h)
+ */
+uint8_t spinor_read_status_reg(spi_bus_t *spi, uint8_t reg);
+
+/* Description: Write Status Register
+ * Reference : P33, 8.2.5 Write Status Register-1 (01h), Status Register-2 (31h) & Status Register-3 (11h)
+ */
+void spinor_write_status_reg(spi_bus_t *spi, uint8_t reg, uint8_t value);
+
+/* Description: Wait flash program/erase finished by read Status Register for BUSY bit
+ * Reference : P15, 7.1 Status Registers
+ */
+void spinor_WaitForReady(spi_bus_t *spi);
+
+#endif
--
Gitblit v1.9.1