LingYun Studio embeded system framwork software, such as thirdparty build shell and lingyun library
4bbc5f0e0843fe9acdc15eaf24afaf423e27d08f..80686e6bc4cb17e9d856647084725203fa63ebe6
2024-08-19 guowenxue
Add LCD API examples
80686e diff | tree
2024-08-19 guowenxue
Add modules in hal
fe3211 diff | tree
20 files added
3953 ■■■■■ changed files
hal/lcd/drm/drm_test.c 154 ●●●●● patch | view | raw | blame | history
hal/lcd/drm/libs/build.sh 44 ●●●●● patch | view | raw | blame | history
hal/lcd/drm/libs/libdrm/build.sh 98 ●●●●● patch | view | raw | blame | history
hal/lcd/drm/makefile 50 ●●●●● patch | view | raw | blame | history
hal/lcd/fb/fbtest.c 396 ●●●●● patch | view | raw | blame | history
hal/lcd/fb/makefile 43 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/libs_lvgl/build.sh 72 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/libs_lvgl/patches/gen_patch.sh 55 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/libs_lvgl/patches/lv_demos-8.0.1-imx6ull.patch 49 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/libs_lvgl/patches/lv_drivers-8.0.1-imx6ull.patch 103 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/libs_lvgl/patches/lvgl-8.0.2-imx6ull.patch 92 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/lvgl_demo.c 91 ●●●●● patch | view | raw | blame | history
hal/lcd/lvgl/makefile 46 ●●●●● patch | view | raw | blame | history
hal/modules/at24c.c 616 ●●●●● patch | view | raw | blame | history
hal/modules/ds18b20.c 134 ●●●●● patch | view | raw | blame | history
hal/modules/makefile 45 ●●●●● patch | view | raw | blame | history
hal/modules/sht20.c 516 ●●●●● patch | view | raw | blame | history
hal/modules/tsl2561.c 337 ●●●●● patch | view | raw | blame | history
hal/modules/w25qflash.c 831 ●●●●● patch | view | raw | blame | history
hal/modules/w25qflash.h 181 ●●●●● patch | view | raw | blame | history
hal/lcd/drm/drm_test.c
New file
@@ -0,0 +1,154 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
struct buffer_object {
    uint32_t width;
    uint32_t height;
    uint32_t pitch;
    uint32_t handle;
    uint32_t size;
    uint32_t *vaddr;
    uint32_t fb_id;
};
struct buffer_object buf[3];
static int modeset_create_fb(int fd, struct buffer_object *bo, uint32_t color)
{
    struct drm_mode_create_dumb create = {
    };
     struct drm_mode_map_dumb map = {
    };
    uint32_t i;
    create.width = bo->width;
    create.height = bo->height;
    create.bpp = 32;
    drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
    bo->pitch = create.pitch;
    bo->size = create.size;
    bo->handle = create.handle;
    drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,
               bo->handle, &bo->fb_id);
    map.handle = create.handle;
    drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
    bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, map.offset);
    for (i = 0; i < (bo->size / 4); i++)
        bo->vaddr[i] = color;
    return 0;
}
static void modeset_destroy_fb(int fd, struct buffer_object *bo)
{
    struct drm_mode_destroy_dumb destroy = {
    };
    drmModeRmFB(fd, bo->fb_id);
    munmap(bo->vaddr, bo->size);
    destroy.handle = bo->handle;
    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}
int main(int argc, char **argv)
{
    char             *dri_card="/dev/dri/card0";
    int               fd;
    uint32_t          conn_id;
    uint32_t          crtc_id;
    drmModeConnector *conn;
    drmModeRes       *res;
    if( argc != 2 )
    {
        printf("Usage: %s /dev/dri/card0\n", argv[0]);
        return 1;
    }
    else
    {
        dri_card = argv[1];
    }
    fd = open(dri_card, O_RDWR | O_CLOEXEC);
    if( fd < 0)
    {
        printf("Open '%s' failure: %s\n", dri_card, strerror(errno));
        return 0;
    }
    res = drmModeGetResources(fd);
    if( !res )
    {
        printf("drmModeGetResources() failure\n");
        return 0;
    }
    crtc_id = res->crtcs[0];
    conn_id = res->connectors[0];
    conn = drmModeGetConnector(fd, conn_id);
    if( !conn )
    {
        printf("drmModeGetConnector() failure\n");
        return 0;
    }
    buf[0].width = conn->modes[0].hdisplay;
    buf[0].height = conn->modes[0].vdisplay;
    buf[1].width = conn->modes[0].hdisplay;
    buf[1].height = conn->modes[0].vdisplay;
    buf[2].width = conn->modes[0].hdisplay;
    buf[2].height = conn->modes[0].vdisplay;
    printf("DRM create framebuffer now.\n");
    modeset_create_fb(fd, &buf[0], 0xff0000);
    modeset_create_fb(fd, &buf[1], 0x00ff00);
    modeset_create_fb(fd, &buf[2], 0x0000ff);
    printf("Show red screen on LCD.\n");
    drmModeSetCrtc(fd, crtc_id, buf[0].fb_id, 0, 0, &conn_id, 1, &conn->modes[0]);
    getchar();
    printf("Show green screen on LCD.\n");
    drmModeSetCrtc(fd, crtc_id, buf[1].fb_id, 0, 0, &conn_id, 1, &conn->modes[0]);
    getchar();
    printf("Show blue screen on LCD.\n");
    drmModeSetCrtc(fd, crtc_id, buf[2].fb_id, 0, 0, &conn_id, 1, &conn->modes[0]);
    getchar();
    printf("DRM destroy framebuffer now.\n");
    modeset_destroy_fb(fd, &buf[0]);
    modeset_destroy_fb(fd, &buf[1]);
    modeset_destroy_fb(fd, &buf[2]);
    drmModeFreeConnector(conn);
    return 0;
    drmModeFreeResources(res);
    close(fd);
    return 0;
}
hal/lcd/drm/libs/build.sh
New file
@@ -0,0 +1,44 @@
#!/bin/bash
function do_compile
{
    for dir in `ls`
    do
        if [ -f $dir/build*.sh ] ; then
            cd $dir
               ./build*.sh
            cd -
        fi
    done
}
function do_distclean
{
    for dir in `ls`
    do
        if [ -f $dir/build*.sh ] ; then
            rm -rf ${dir}/${dir}*
        fi
    done
}
if [ $# == 1 ] ; then
    if [ $1 == "clean" ] ; then
        rm -rf install
        exit;
    elif [ $1 == "distclean" ] ; then
        rm -rf install
        do_distclean
        exit;
    fi
    # export cross compiler
    if echo $1 | grep "\-linux\-" > /dev/null 2>&1 ; then
        export CROSS_COMPILE=$1
        echo -e "\033[40;33m --W-- export cross compiler $CROSS_COMPILE \033[0m\n"
    fi
fi
do_compile
hal/lcd/drm/libs/libdrm/build.sh
New file
@@ -0,0 +1,98 @@
#!/bin/bash
#+--------------------------------------------------------------------------------------------
#|Description:  This shell script used download and cross compile open source libs
#|     Author:  GuoWenxue <guowenxue@gmail.com>
#|  ChangeLog:
#|           1, Initialize 1.0.0 on 2011.04.12
#+--------------------------------------------------------------------------------------------
SCRIPT_PATH=`dirname ${BASH_SOURCE[0]}`
PREFIX_PATH=`realpath ${SCRIPT_PATH}/`/../install
CONF_FILE=cross_file.txt
# display in yellow
function pr_warn() {
    echo -e "\033[40;33m --W-- $1 \033[0m\n"
}
function generate_config()
{
cat > $CONF_FILE <<EOF
[binaries]
c = '${CROSS_COMPILE}gcc'
cpp = '${CROSS_COMPILE}g++'
ar = '${CROSS_COMPILE}ar'
strip = '${CROSS_COMPILE}strip'
[host_machine]
system = 'linux'
cpu_family = 'arm'
cpu = 'armv7'
endian = 'little'
[build_machine]
system = 'linux'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
EOF
}
function compile_libdrm()
{
    SRC_NAME=libdrm-2.4.110
    echo $PREFIX_PATH
    if [ -f ${PREFIX_PATH}/bin/modetest ] ; then
        pr_warn "$SRC_NAME already compile and installed"
        return 0;
    fi
    pr_warn "Start compile $SRC_NAME "
    if [ ! -d $SRC_NAME ] ; then
       if [ ! -f ${SRC_NAME}.tar.xz ] ; then
           wget https://dri.freedesktop.org/libdrm/${SRC_NAME}.tar.xz
       fi
       tar -xJf ${SRC_NAME}.tar.xz
    fi
    cd ${SRC_NAME}
    generate_config
    BUILD_PATH=tmp_build
    rm -rf $BUILD_PATH && mkdir -p $BUILD_PATH && cd $BUILD_PATH
    meson --prefix=$PREFIX_PATH \
          --cross-file=../$CONF_FILE \
          -D amdgpu=false \
          -D cairo-tests=false \
          -D etnaviv=false \
          -D exynos=false \
          -D freedreno=false \
          -D freedreno-kgsl=false \
          -D install-test-programs=true \
          -D intel=false \
          -D libkms=false \
          -D man-pages=false \
          -D nouveau=false \
          -D omap=false \
          -D radeon=false \
          -D tegra=false \
          -D udev=false \
          -D valgrind=false \
          -D vc4=false \
          -D vmwgfx=false
    ninja && ninja install
}
compile_libdrm
hal/lcd/drm/makefile
New file
@@ -0,0 +1,50 @@
#*********************************************************************************
#      Copyright:  (C) 2022 Avnet. All rights reserved.
#         Author:  Guo Wenxue<wenxue.guo@avnet.com>
#
#       Filename:  Makefile
#    Description:  This Makefile used to compile all the C source code file in
#                  current folder to a excutable binary file.
#
#********************************************************************************/
PRJ_PATH=$(shell pwd)
APP_NAME = drm_test
CROSS_COMPILE=arm-linux-gnueabihf-
# C source files in top-level directory
SRCFILES = $(wildcard *.c)
# common CFLAGS for our source code
CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized
# CFLAGS and LDFLAGS for opensource library in libs
CFLAGS+=-I ${PRJ_PATH}
CFLAGS+=-I ${PRJ_PATH}/libs/install/include/
CFLAGS+=-I ${PRJ_PATH}/libs/install/include/libdrm
LDFLAGS+=-L ${PRJ_PATH}/libs/install/lib
LDFLAGS+=-L ${PRJ_PATH}/libs/install/lib
LIBS+=-ldrm
.PHONY:libs
all: libs binary
libs:
    cd libs && ./build.sh ${CROSS_COMPILE}
modules:
    @set -e; for d in ${DIRS}; do $(MAKE) CROSS_COMPILE=${CROSS_COMPILE} CFLAGS="${CFLAGS}" -C $${d}; done
binary:  ${SRCFILES}
    $(CROSS_COMPILE)gcc $(CFLAGS) -o ${APP_NAME} $^ ${LDFLAGS} ${LIBS}
    @echo " Compile over"
#set -e; for d in ${DIRS}; do $(MAKE) clean -C $${d}; done
clean:
    @rm -f *.o $(APP_NAME)
distclean: clean
    @rm -rf cscope* tags
    @cd libs && ./build.sh distclean
hal/lcd/fb/fbtest.c
New file
@@ -0,0 +1,396 @@
/********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  fbtest.c
 *    Description:  This program used to:
 *                  1, Show framebuffer device information
 *                  2, Show RGB color on LCD full screen
 *                  3, Show BMP file on LCD;
 *
 *        Version:  1.0.0(08/19/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/19/23 12:56:31"
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/fb.h>
#include <sys/mman.h>
#define PROG_VERSION      "1.0.0"
enum
{
    CMD_SHOW_INFO, /* show LCD info  */
    CMD_SHOW_RGB,  /* show RGB color */
    CMD_SHOW_BMP,  /* show BMP file  */
};
typedef struct fb_ctx_s
{
    int                       fd;       /* framebuffer file descriptor */
    char                      dev[64];  /* framebuffer device name */
    long                      fb_size;  /* framebuffer size */
    int                       pix_size; /* lcd screen pix size  */
    struct fb_var_screeninfo  vinfo;    /* framebuffer var information */
    char                     *fbp;      /* framebuffer mmap() address */
} fb_ctx_t;
int fb_init(fb_ctx_t *fb_ctx);
int fb_term(fb_ctx_t *fb_ctx);
int show_rgb(fb_ctx_t *fb_ctx, int times);
int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file);
static void program_usage(char *progname)
{
    printf("Usage: %s [OPTION]...\n", progname);
    printf(" %s is a program to show RGB color or BMP file on LCD screen\n", progname);
    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
    printf(" -d[device  ]  Specify framebuffer device, such as: /dev/fb0\n");
    printf(" -c[color   ]  Display RGB corlor on LCD screen for some times, such as: -c 3\n");
    printf(" -b[bmp     ]  Display BMP file, such as: -b bg.bmp\n");
    printf(" -h[help    ]  Display this help information\n");
    printf(" -v[version ]  Display the program version\n");
    printf("\n%s version %s\n", progname, PROG_VERSION);
    return;
}
int main (int argc, char **argv)
{
    fb_ctx_t                  fb_ctx;
    char                     *progname=NULL;
    char                     *bmp_file=NULL;
    char                     *fb_dev="/dev/fb0";
    int                       cmd = CMD_SHOW_INFO;
    int                       opt, times;
    struct option long_options[] = {
        {"device", required_argument, NULL, 'd'},
        {"color", required_argument, NULL, 'c'},
        {"bmp", required_argument, NULL, 'b'},
        {"version", no_argument, NULL, 'v'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };
    progname = (char *)basename(argv[0]);
    /* Parser the command line parameters */
    while ((opt = getopt_long(argc, argv, "d:c:b:vh", long_options, NULL)) != -1)
    {
        switch (opt)
        {
            case 'd': /* Set framebuffer device */
                 fb_dev = optarg;
                break;
            case 'b': /* Set BMP file */
                bmp_file = optarg;
                cmd = CMD_SHOW_BMP;
                break;
            case 'c': /* Show RGB color */
                times = atoi(optarg);
                cmd = CMD_SHOW_RGB;
                break;
            case 'v':  /* Get software version */
                printf("%s version %s\n", progname, PROG_VERSION);
                return 0;
            case 'h':  /* Get help information */
                program_usage(progname);
                return 0;
            default:
                break;
        }
    }
    memset(&fb_ctx, 0, sizeof(fb_ctx));
    strncpy(fb_ctx.dev, fb_dev, sizeof(fb_ctx.dev));
    if( fb_init(&fb_ctx) < 0 )
    {
        printf("ERROR: Initial framebuffer device '%s' failure.\n", fb_ctx.dev);
        return 1;
    }
    switch( cmd )
    {
        case CMD_SHOW_RGB:
            show_rgb(&fb_ctx, times);
            break;
        case CMD_SHOW_BMP:
            show_bmp(&fb_ctx, bmp_file);
            break;
        default:
            break;
    }
    fb_term(&fb_ctx);
    return 0;
}
int fb_get_var_screeninfo(int fd, struct fb_var_screeninfo *vinfo)
{
    if( fd<0 || !vinfo )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    if(ioctl(fd, FBIOGET_VSCREENINFO, vinfo))
    {
        printf("ERROR: ioctl() get variable info failure: %s\n", strerror(errno));
        return -2;
    }
    printf("LCD information : %dx%d, bpp:%d rgba:%d/%d,%d/%d,%d/%d,%d/%d\n", vinfo->xres, vinfo->yres, vinfo->bits_per_pixel,
            vinfo->red.length, vinfo->red.offset, vinfo->green.length, vinfo->green.offset,
            vinfo->blue.length,vinfo->blue.offset, vinfo->transp.length, vinfo->transp.offset);
    return 0;
}
int fb_init(fb_ctx_t *fb_ctx)
{
    struct fb_var_screeninfo    *vinfo;
    if( !fb_ctx || !strlen(fb_ctx->dev) )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    if( (fb_ctx->fd=open(fb_ctx->dev, O_RDWR)) < 0 )
    {
        printf("ERROR: Open framebuffer device '%s' failure: %s\n", fb_ctx->dev, strerror(errno));
        return -2;
    }
    fb_get_var_screeninfo(fb_ctx->fd, &fb_ctx->vinfo);
    vinfo = &fb_ctx->vinfo;
    fb_ctx->fb_size = vinfo->xres * vinfo->yres * vinfo->bits_per_pixel / 8;
    fb_ctx->pix_size = vinfo->xres * vinfo->yres;
    fb_ctx->fbp =(char *)mmap(0, fb_ctx->fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_ctx->fd ,0);
    if ( !fb_ctx->fbp )
    {
        printf ("ERROR: Framebuffer mmap() failure: %s\n", strerror(errno));
        return -2;
    }
    vinfo->xoffset = 0;
    vinfo->yoffset = 0;
    return 0;
}
int fb_term(fb_ctx_t *fb_ctx)
{
    if( !fb_ctx )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    munmap(fb_ctx->fbp, fb_ctx->fb_size);
    close (fb_ctx->fd);
    return 0;
}
int show_rgb_screen(fb_ctx_t *fb_ctx)
{
    struct fb_var_screeninfo    *vinfo;
    char                        *fb_addr;
    int                          interval = 1;
    int                          i, color;
    int                          bpp_bytes;
    if( !fb_ctx )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    vinfo = &fb_ctx->vinfo;
    bpp_bytes = vinfo->bits_per_pixel / 8;
    printf("show full screen on red\n");
    color = (1<<vinfo->red.length-1) << vinfo->red.offset;
    fb_addr = fb_ctx->fbp;
    for(i=0; i<fb_ctx->pix_size; i++)
    {
        memcpy(fb_addr, &color, bpp_bytes);
        fb_addr += bpp_bytes;
    }
    sleep(interval);
    printf("show full screen on green\n");
    color = (1<<vinfo->green.length-1) << vinfo->green.offset;
    fb_addr = fb_ctx->fbp;
    for(i=0; i<fb_ctx->pix_size; i++)
    {
        memcpy(fb_addr, &color, bpp_bytes);
        fb_addr += bpp_bytes;
    }
    sleep(interval);
    printf("show full screen on blue\n");
    color = (1<<vinfo->blue.length-1) << vinfo->blue.offset;
    fb_addr = fb_ctx->fbp;
    for(i=0; i<fb_ctx->pix_size; i++)
    {
        memcpy(fb_addr, &color, bpp_bytes);
        fb_addr += bpp_bytes;
    }
    sleep(interval);
    return 0;
}
int show_rgb(fb_ctx_t *fb_ctx, int times)
{
    int       i;
    if( !fb_ctx )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    for(i=0; i<times; i++)
    {
        show_rgb_screen(fb_ctx);
    }
    return 0;
}
/* BMP format: https://blog.csdn.net/weixin_39756273/article/details/110585675  */
typedef struct bmp_file_head_s
{
    uint8_t  bfType[2];        /* BMP file type: "BM" */
    int32_t bfSize;           /* BMP file size */
    int32_t bfReserved;       /* reserved */
    int32_t bfoffBits;        /* image data offset */
}__attribute__((packed)) bmp_file_head_t;
typedef struct bmp_bitmap_info_s
{
    int32_t biSize;           /* this struture size */
    int32_t biWidth;          /* image width in pix */
    int32_t biHeight;         /* image height in pix */
    uint16_t biPlanes;         /* display planes, always be 1 */
    uint16_t biBitCount;       /* bpp: 1,4,8,16,24,32 */
    int32_t biCompress;       /* compress type */
    int32_t biSizeImage;      /* image size in byte */
    int32_t biXPelsPerMeter;  /* x-res in pix/m */
    int32_t biYPelsPerMeter;  /* y-res in pix/m */
    int32_t biClrUsed;
    int32_t biClrImportant;
}__attribute__((packed)) bmp_bitmap_info_t;
/*
 * BMP image file format:
 * +----------------+------------------+--------------------+---------------+
 * | File Head(14B) | Bitmap Info(40B) | color palette(OPT) |  bitmap data  |
 * +----------------+------------------+--------------------+---------------+
 */
int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file)
{
    struct fb_var_screeninfo    *vinfo;
    char                        *fb_addr;
    bmp_file_head_t            *file_head;
    bmp_bitmap_info_t          *bitmap_info;
    struct stat                 statbuf;
    char                       *f_addr;
    char                       *d_addr;
    char                       *p_addr;
    int                         fd;
    int                         bpp_bytes;
    int                         i, j;
    int                         width, height;
    if( !fb_ctx || !bmp_file )
    {
        printf("ERROR: Invalid input arguments\n");
        return -1;
    }
    if( (fd=open(bmp_file, O_RDONLY)) < 0 )
    {
        printf("ERROR: Open file '%s' failure: %s\n", bmp_file, strerror(errno));
        return -2;
    }
    fstat(fd, &statbuf);
    f_addr =(char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd ,0);
    if ( !f_addr )
    {
        printf ("ERROR: BMP file mmap() failure: %s\n", strerror(errno));
        return -2;
    }
    /* BMP file header */
    file_head = (bmp_file_head_t *)f_addr;
    if( memcmp(file_head->bfType, "BM", 2) != 0)
    {
        printf("ERROR: It's not a BMP file\n");
        return -3;
    }
    /* BMP bitmap information header */
    bitmap_info = (bmp_bitmap_info_t *)(f_addr+sizeof(bmp_file_head_t));
    /* BMP bitmap data */
    d_addr = f_addr + file_head->bfoffBits;
    vinfo = &fb_ctx->vinfo;
    fb_addr = fb_ctx->fbp;
    bpp_bytes = vinfo->bits_per_pixel / 8;
    width = bitmap_info->biWidth>vinfo->xres ? vinfo->xres : bitmap_info->biWidth;
    height = bitmap_info->biHeight>vinfo->yres ? vinfo->yres : bitmap_info->biHeight;
    printf("BMP file '%s': %dx%d and display %dx%d\n", bmp_file, bitmap_info->biWidth, bitmap_info->biHeight, width, height);
    /* if biHeight is positive, the bitmap is a bottom-up DIB */
    for(i=0; i<height; i++)
    {
        p_addr=d_addr+(height-1-i)*bitmap_info->biWidth*bpp_bytes;
        memcpy(fb_addr, p_addr, bpp_bytes*width);
        fb_addr += bpp_bytes * vinfo->xres;
    }
    munmap(f_addr, statbuf.st_size);
    close (fd);
    return 0;
}
hal/lcd/fb/makefile
New file
@@ -0,0 +1,43 @@
#********************************************************************************
#      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
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
hal/lcd/lvgl/libs_lvgl/build.sh
New file
@@ -0,0 +1,72 @@
#!/bin/bash
PRJ_PATH=`pwd`
BOARD=imx6ull
LYFTP_SRC=http://master.weike-iot.com:2211/src/
LIB_LVGL=lvgl-8.0.2
LIB_LVGL_DRV=lv_drivers-8.0.1
LIB_LVGL_DEMO=lv_demos-8.0.1
LIBS_DIR="lvgl lv_drivers lv_demos"
CROSS_COMPILE=arm-linux-gnueabihf-
if [ $# == 1 ] ; then
   CROSS_COMPILE=$1
fi
set -e
function prepare_lib()
{
    PACK_SUFIX=tar.gz
    PACK_NAME=$1
    DIR_NAME=$2
    if [ ! -d ${DIR_NAME} ] ; then
       if [ ! -s ${PACK_NAME}.${PACK_SUFIX} ] ; then
          wget ${LYFTP_SRC}/${PACK_NAME}.${PACK_SUFIX}
       fi
       tar -xzf ${PACK_NAME}.${PACK_SUFIX}
       patch -p0 < patches/${PACK_NAME}-${BOARD}.patch
       mv ${PACK_NAME} ${DIR_NAME}
       cd ${DIR_NAME}
       sed -i -e "s|.*CMAKE_C_COMPILER.*|set(CMAKE_C_COMPILER \"${CROSS_COMPILE}gcc\")|g" CMakeLists.txt
         temp_file=`ls *_template.h`
         conf_file=`echo ${temp_file/_template/}`
         cp ${temp_file} ../${conf_file}
       cd ${PRJ_PATH}
    fi
}
if [[ $# == 1 && $1 == clean ]] ; then
    rm -rf lv*
    rm -rf libs
    exit;
fi
prepare_lib ${LIB_LVGL} lvgl
prepare_lib ${LIB_LVGL_DRV} lv_drivers
prepare_lib ${LIB_LVGL_DEMO} lv_demos
mkdir -p ${PRJ_PATH}/libs
for lib in ${LIBS_DIR}
do
    if [ -f ${PRJ_PATH}/libs/lib${lib}.a ] ; then
        continue;
    fi
    mkdir -p ${lib}/build
    cd ${lib}/build/
    rm -rf * && cmake .. && make
    cp lib*.a ${PRJ_PATH}/libs/lib${lib}.a
    cd ${PRJ_PATH}
done
hal/lcd/lvgl/libs_lvgl/patches/gen_patch.sh
New file
@@ -0,0 +1,55 @@
#!/bin/bash
# Description: This shell script used to generate patch file
#      Author: guowenxue <guowenxue@gmail.com>
#     Version: 1.0.0
PROJ_PATH=`pwd`
PATCH_PATH=${PROJ_PATH}/patches
BOARD=imx6ull
PATCH_SUFFIX=${BOARD}.patch
set -e
if [ $# != 1 ] ; then
    echo "Usage: $0 [src_path]"
    printf "\nExample: \n"
    echo "$0 lvgl-8.0.2"
    exit ;
fi
SRC=`basename $1`
if [ ! -d ${SRC} ] ; then
    printf "\nERROR: ${SRC} source code not exist, exit now\n\n"
    exit
fi
if [ ! -f ${SRC}.tar.gz ] ; then
    printf "\nERROR: ${SRC}.tar.gz packet not exist, exit now\n\n"
    exit
fi
rm -rf ${SRC}/build
# rename new source code
mv ${SRC} ${SRC}-${BOARD}
# decompress orignal soruce code packet
tar -xzf ${SRC}.tar.gz
set +e
# generate patch file
diff -Nuar ${SRC} ${SRC}-${BOARD} > ${SRC}-${PATCH_SUFFIX}
# remove orignal soruce code
rm -rf ${SRC}
# recover new source code
mv ${SRC}-${BOARD} ${SRC}
hal/lcd/lvgl/libs_lvgl/patches/lv_demos-8.0.1-imx6ull.patch
New file
@@ -0,0 +1,49 @@
diff -Nuar lv_demos-8.0.1/CMakeLists.txt lv_demos-8.0.1-imx6ull/CMakeLists.txt
--- lv_demos-8.0.1/CMakeLists.txt    2021-06-14 20:00:00.000000000 +0800
+++ lv_demos-8.0.1-imx6ull/CMakeLists.txt    2021-09-27 21:26:45.823935469 +0800
@@ -1,2 +1,5 @@
+set(CMAKE_C_COMPILER "/opt/buildroot/cortex-a7/bin/arm-linux-gcc")
+include_directories(..)
+
 file(GLOB_RECURSE SOURCES src/*.c)
 add_library(lv_examples STATIC ${SOURCES})
diff -Nuar lv_demos-8.0.1/lv_demo_conf_template.h lv_demos-8.0.1-imx6ull/lv_demo_conf_template.h
--- lv_demos-8.0.1/lv_demo_conf_template.h    2021-06-14 20:00:00.000000000 +0800
+++ lv_demos-8.0.1-imx6ull/lv_demo_conf_template.h    2021-09-27 20:57:35.667901897 +0800
@@ -7,7 +7,7 @@
  * COPY THIS FILE AS lv_demo_conf.h
  */
-#if 0 /*Set it to "1" to enable the content*/
+#if 1 /*Set it to "1" to enable the content*/
 #ifndef LV_EX_CONF_H
 #define LV_EX_CONF_H
@@ -25,22 +25,22 @@
  *********************/
 /*Show some widget*/
-#define LV_USE_DEMO_WIDGETS        0
+#define LV_USE_DEMO_WIDGETS        1
 #if LV_USE_DEMO_WIDGETS
 #define LV_DEMO_WIDGETS_SLIDESHOW  0
 #endif
 /*Printer demo, optimized for 800x480*/
-#define LV_USE_DEMO_PRINTER     0
+#define LV_USE_DEMO_PRINTER     1
 /*Demonstrate the usage of encoder and keyboard*/
-#define LV_USE_DEMO_KEYPAD_AND_ENCODER     0
+#define LV_USE_DEMO_KEYPAD_AND_ENCODER     1
 /*Benchmark your system*/
-#define LV_USE_DEMO_BENCHMARK   0
+#define LV_USE_DEMO_BENCHMARK   1
 /*Stress test for LVGL*/
-#define LV_USE_DEMO_STRESS      0
+#define LV_USE_DEMO_STRESS      1
 /*Music player demo*/
 #define LV_USE_DEMO_MUSIC      1
hal/lcd/lvgl/libs_lvgl/patches/lv_drivers-8.0.1-imx6ull.patch
New file
@@ -0,0 +1,103 @@
diff -Nuar lv_drivers-8.0.1/CMakeLists.txt lv_drivers-8.0.1-imx6ull/CMakeLists.txt
--- lv_drivers-8.0.1/CMakeLists.txt    2021-06-14 19:54:20.000000000 +0800
+++ lv_drivers-8.0.1-imx6ull/CMakeLists.txt    2021-09-27 22:20:38.179997474 +0800
@@ -1,2 +1,5 @@
+set(CMAKE_C_COMPILER "/opt/buildroot/cortex-a7/bin/arm-linux-gcc")
+include_directories(..)
+
 file(GLOB_RECURSE SOURCES ./*.c)
 add_library(lv_drivers STATIC ${SOURCES})
diff -Nuar lv_drivers-8.0.1/indev/evdev.c lv_drivers-8.0.1-imx6ull/indev/evdev.c
--- lv_drivers-8.0.1/indev/evdev.c    2021-06-14 19:54:20.000000000 +0800
+++ lv_drivers-8.0.1-imx6ull/indev/evdev.c    2021-09-27 22:25:04.172002576 +0800
@@ -115,7 +115,7 @@
  * @param data store the evdev data here
  * @return false: because the points are not buffered, so no more data to be read
  */
-bool evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data)
+void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data)
 {
     struct input_event in;
@@ -196,7 +196,7 @@
         }
         evdev_key_val = data->key;
         evdev_button = data->state;
-        return false;
+        return;
         }
         }
     }
@@ -205,10 +205,10 @@
         /* No data retrieved */
         data->key = evdev_key_val;
     data->state = evdev_button;
-    return false;
+    return;
     }
     if(drv->type != LV_INDEV_TYPE_POINTER)
-        return false;
+        return;
     /*Store the collected data*/
 #if EVDEV_CALIBRATE
@@ -225,12 +225,12 @@
       data->point.x = 0;
     if(data->point.y < 0)
       data->point.y = 0;
-    if(data->point.x >= drv->disp->driver.hor_res)
-      data->point.x = drv->disp->driver.hor_res - 1;
-    if(data->point.y >= drv->disp->driver.ver_res)
-      data->point.y = drv->disp->driver.ver_res - 1;
+    if(data->point.x >= drv->disp->driver->hor_res)
+      data->point.x = drv->disp->driver->hor_res - 1;
+    if(data->point.y >= drv->disp->driver->ver_res)
+      data->point.y = drv->disp->driver->ver_res - 1;
-    return false;
+    return;
 }
 /**********************
diff -Nuar lv_drivers-8.0.1/indev/evdev.h lv_drivers-8.0.1-imx6ull/indev/evdev.h
--- lv_drivers-8.0.1/indev/evdev.h    2021-06-14 19:54:20.000000000 +0800
+++ lv_drivers-8.0.1-imx6ull/indev/evdev.h    2021-09-27 22:25:33.424003137 +0800
@@ -57,7 +57,7 @@
  * @param data store the evdev data here
  * @return false: because the points are not buffered, so no more data to be read
  */
-bool evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data);
+void evdev_read(lv_indev_drv_t * drv, lv_indev_data_t * data);
 /**********************
diff -Nuar lv_drivers-8.0.1/lv_drv_conf_template.h lv_drivers-8.0.1-imx6ull/lv_drv_conf_template.h
--- lv_drivers-8.0.1/lv_drv_conf_template.h    2021-06-14 19:54:20.000000000 +0800
+++ lv_drivers-8.0.1-imx6ull/lv_drv_conf_template.h    2021-09-27 22:20:38.183997474 +0800
@@ -7,7 +7,7 @@
  * COPY THIS FILE AS lv_drv_conf.h
  */
-#if 0 /*Set it to "1" to enable the content*/
+#if 1 /*Set it to "1" to enable the content*/
 #ifndef LV_DRV_CONF_H
 #define LV_DRV_CONF_H
@@ -272,7 +272,7 @@
  *  Linux frame buffer device (/dev/fbx)
  *-----------------------------------------*/
 #ifndef USE_FBDEV
-#  define USE_FBDEV           0
+#  define USE_FBDEV           1
 #endif
 #if USE_FBDEV
@@ -386,7 +386,7 @@
  * Mouse or touchpad as evdev interface (for Linux based systems)
  *------------------------------------------------*/
 #ifndef USE_EVDEV
-#  define USE_EVDEV           0
+#  define USE_EVDEV           1
 #endif
 #ifndef USE_BSD_EVDEV
hal/lcd/lvgl/libs_lvgl/patches/lvgl-8.0.2-imx6ull.patch
New file
@@ -0,0 +1,92 @@
diff -Nuar lvgl-8.0.2/CMakeLists.txt lvgl-8.0.2-imx6ull/CMakeLists.txt
--- lvgl-8.0.2/CMakeLists.txt    2021-07-16 23:40:51.000000000 +0800
+++ lvgl-8.0.2-imx6ull/CMakeLists.txt    2021-09-27 22:30:20.360008641 +0800
@@ -1,3 +1,7 @@
+
+set(CMAKE_C_COMPILER "/opt/buildroot/cortex-a7/bin/arm-linux-gcc")
+include_directories(..)
+
 if(ESP_PLATFORM)
 file(GLOB_RECURSE SOURCES src/*.c)
diff -Nuar lvgl-8.0.2/lv_conf_template.h lvgl-8.0.2-imx6ull/lv_conf_template.h
--- lvgl-8.0.2/lv_conf_template.h    2021-07-16 23:40:51.000000000 +0800
+++ lvgl-8.0.2-imx6ull/lv_conf_template.h    2021-09-27 22:33:47.448012614 +0800
@@ -7,7 +7,7 @@
  * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER
  */
-#if 0 /*Set it to "1" to enable content*/
+#if 1 /*Set it to "1" to enable content*/
 #ifndef LV_CONF_H
 #define LV_CONF_H
@@ -21,7 +21,7 @@
  *====================*/
 /*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
-#define LV_COLOR_DEPTH     32
+#define LV_COLOR_DEPTH     16
 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/
 #define LV_COLOR_16_SWAP   0
@@ -42,7 +42,7 @@
 #define LV_MEM_CUSTOM      0
 #if LV_MEM_CUSTOM == 0
 /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
-#  define LV_MEM_SIZE    (32U * 1024U)          /*[bytes]*/
+#  define LV_MEM_SIZE    (128U * 1024U)          /*[bytes]*/
 /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
 #  define LV_MEM_ADR          0     /*0: unused*/
@@ -68,9 +68,10 @@
 /*Use a custom tick source that tells the elapsed time in milliseconds.
  *It removes the need to manually update the tick with `lv_tick_inc()`)*/
-#define LV_TICK_CUSTOM     0
+#define LV_TICK_CUSTOM     1
 #if LV_TICK_CUSTOM
-#define LV_TICK_CUSTOM_INCLUDE  "Arduino.h"         /*Header for the system time function*/
+extern uint32_t millis(void);
+#define LV_TICK_CUSTOM_INCLUDE      <stdint.h>      /*Header for the system time function*/
 #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis())     /*Expression evaluating to current system time in ms*/
 #endif   /*LV_TICK_CUSTOM*/
@@ -137,7 +138,7 @@
  *-----------*/
 /*Enable the log module*/
-#define LV_USE_LOG      0
+#define LV_USE_LOG      1
 #if LV_USE_LOG
 /*How important log should be added:
@@ -151,7 +152,7 @@
 /*1: Print the log with 'printf';
  *0: User need to register a callback with `lv_log_register_print_cb()`*/
-#  define LV_LOG_PRINTF   0
+#  define LV_LOG_PRINTF   1
 /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
 #  define LV_LOG_TRACE_MEM            1
@@ -262,13 +263,13 @@
 /*Montserrat fonts with ASCII range and some symbols using bpp = 4
  *https://fonts.google.com/specimen/Montserrat*/
-#define LV_FONT_MONTSERRAT_8     0
-#define LV_FONT_MONTSERRAT_10    0
-#define LV_FONT_MONTSERRAT_12    0
+#define LV_FONT_MONTSERRAT_8     1
+#define LV_FONT_MONTSERRAT_10    1
+#define LV_FONT_MONTSERRAT_12    1
 #define LV_FONT_MONTSERRAT_14    1
-#define LV_FONT_MONTSERRAT_16    0
-#define LV_FONT_MONTSERRAT_18    0
-#define LV_FONT_MONTSERRAT_20    0
+#define LV_FONT_MONTSERRAT_16    1
+#define LV_FONT_MONTSERRAT_18    1
+#define LV_FONT_MONTSERRAT_20    1
 #define LV_FONT_MONTSERRAT_22    0
 #define LV_FONT_MONTSERRAT_24    0
 #define LV_FONT_MONTSERRAT_26    0
hal/lcd/lvgl/lvgl_demo.c
New file
@@ -0,0 +1,91 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  lvgl_demo.c
 *    Description:  This file is LVGL demo program.
 *
 *        Version:  1.0.0(2021å¹´09月27æ—¥)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2021å¹´09月27æ—¥ 22æ—¶16分52ç§’"
 *
 ********************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include "lv_demos/lv_demo.h"
#define DISP_BUF_SIZE (128 * 1024)
int main(void)
{
    lv_disp_drv_t               disp_drv;
    lv_indev_drv_t              indev_drv;
    static lv_color_t           buf[DISP_BUF_SIZE]; /* buffer for LVGL to draw the screen's content */
    static lv_disp_draw_buf_t   disp_buf; /* Initialize a descriptor for the buffer */
    /* LVGL context init */
    lv_init();
    /* Linux frame buffer device init */
    fbdev_init();
    /* linux touchscreen device init */
    lv_indev_drv_init(&indev_drv);
    indev_drv.type =LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb =evdev_read;
    lv_indev_drv_register(&indev_drv);
    /* Initialize and register a display driver */
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb = fbdev_flush;
    disp_drv.hor_res    = 800;
    disp_drv.ver_res    = 480;
    lv_disp_drv_register(&disp_drv);
    /* Create a Demo */
    //lv_demo_widgets();
    lv_demo_music();
    //lv_demo_keypad_encoder();
    //lv_demo_stress();
    /* Handle LitlevGL tasks (tickless mode) */
    while(1) {
        lv_task_handler();
        usleep(5000);
    }
    return 0;
}
/* Set in lv_conf.h as LV_TICK_CUSTOM_SYS_TIME_EXPR */
uint32_t millis(void)
{
    static uint64_t start_ms = 0;
    struct timeval tv_start;
    struct timeval tv_now;
    uint64_t now_ms;
    uint32_t time_ms;
    if(start_ms == 0)
    {
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }
    gettimeofday(&tv_now, NULL);
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;
    time_ms = now_ms - start_ms;
    return time_ms;
}
hal/lcd/lvgl/makefile
New file
@@ -0,0 +1,46 @@
CROSS_COMPILE=arm-linux-gnueabihf-
CC=${CROSS_COMPILE}gcc
CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized
CFLAGS += -O3 -g3 -I./ -I ./libs_lvgl/
LDFLAGS+=-L ./libs_lvgl/libs/ -llv_demos -llv_drivers -llvgl
BIN = lvgl_demo
MAINSRC = lvgl_demo.c
VPATH =
OBJEXT ?= .o
AOBJS = $(ASRCS:.S=$(OBJEXT))
COBJS = $(CSRCS:.c=$(OBJEXT))
MAINOBJ = $(MAINSRC:.c=$(OBJEXT))
SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
OBJS = $(AOBJS) $(COBJS)
## MAINOBJ -> OBJFILES
all: libs clean default
    cp ${BIN} /tftp
libs:
    cd libs_lvgl && ./build.sh ${CROSS_COMPILE}
%.o: %.c
    @$(CC)  $(CFLAGS) -c $< -o $@
    @echo "CC $<"
default: $(AOBJS) $(COBJS) $(MAINOBJ)
    $(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
clean:
    rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)
distclean: clean
    rm -rf cscope* tags
    cd libs_lvgl && ./build.sh clean
hal/modules/at24c.c
New file
@@ -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);
    }
}
hal/modules/ds18b20.c
New file
@@ -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;
}
hal/modules/makefile
New file
@@ -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
hal/modules/sht20.c
New file
@@ -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);
    }
}
hal/modules/tsl2561.c
New file
@@ -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], &reg_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 ;
}
hal/modules/w25qflash.c
New file
@@ -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);
    }
}
hal/modules/w25qflash.h
New file
@@ -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