LingYun Studio embeded system framwork software, such as thirdparty build shell and lingyun library
guowenxue
2024-08-19 1e6300ae4614b2d8d74687240b2985f3128bc41b
Add led/pwm API source code
5 files added
824 ■■■■■ changed files
hal/api/leds.c 332 ●●●●● patch | view | raw | blame | history
hal/api/libgpiod/build.sh 118 ●●●●● patch | view | raw | blame | history
hal/api/libgpiod/makefile 11 ●●●●● patch | view | raw | blame | history
hal/api/makefile 54 ●●●●● patch | view | raw | blame | history
hal/api/pwm.c 309 ●●●●● patch | view | raw | blame | history
hal/api/leds.c
New file
@@ -0,0 +1,332 @@
/*********************************************************************************
 *      Copyright:  (C) 2024 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  led.c
 *    Description:  This file is used to control RGB 3-colors LED
 *
 *
 * Pin connection:
 *               RGB Led Module           IGKBoard
 *                   R        <----->      #Pin33
 *                   G        <----->      #Pin35
 *                   B        <----->      #Pin37
 *                  GND       <----->      GND
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <gpiod.h>
#define DELAY     300
#define ON        1
#define OFF       0
/* Three LEDs number */
enum
{
    LED_R = 0,
    LED_G,
    LED_B,
    LEDCNT,
};
enum
{
    ACTIVE_HIGH, /* High level will turn led on */
    ACTIVE_LOW,  /* Low level will turn led on */
};
/* Three LEDs hardware information */
typedef struct led_s
{
    const char               *name;      /* RGB 3-color LED name  */
    int                       chip_num;  /* RGB 3-color LED connect chip */
    int                       gpio_num;  /* RGB 3-color LED connect line */
    int                       active;    /* RGB 3-color LED active level */
    struct gpiod_line_request *request;  /* libgpiod gpio request handler */
} led_t;
static led_t leds_info[LEDCNT] =
{
    {"red",   0, 23, ACTIVE_HIGH, NULL }, /* GPIO1_IO23 on chip0 line 23, active high */
    {"green", 4, 1,  ACTIVE_HIGH, NULL }, /* GPIO5_IO01 on chip4 line 1, active high */
    {"blue",  4, 8,  ACTIVE_HIGH, NULL }, /* GPIO5_IO08 on chip4 line 8, active high */
};
/* Three LEDs API context */
typedef struct leds_s
{
    led_t               *leds;  /* led pointer to leds_info */
    int                  count; /* led count */
} leds_t;
/* function declaration  */
int init_led(leds_t *leds);
int term_led(leds_t *leds);
int turn_led(leds_t *leds, int which, int cmd);
static inline void msleep(unsigned long ms);
int g_stop = 0;
void sig_handler(int signum)
{
    switch( signum )
    {
        case SIGINT:
        case SIGTERM:
            g_stop = 1;
        default:
            break;
    }
    return ;
}
int main(int argc, char *argv[])
{
    int                 rv;
    leds_t              leds =
    {
        .leds  = leds_info,
        .count = LEDCNT,
    };
    if( (rv=init_led(&leds)) < 0 )
    {
        printf("initial leds gpio failure, rv=%d\n", rv);
        return 1;
    }
    printf("initial RGB Led gpios okay\n");
    signal(SIGINT,  sig_handler);
    signal(SIGTERM, sig_handler);
    while( !g_stop )
    {
        turn_led(&leds, LED_R, ON);
        msleep(DELAY);
        turn_led(&leds, LED_R, OFF);
        msleep(DELAY);
        turn_led(&leds, LED_G, ON);
        msleep(DELAY);
        turn_led(&leds, LED_G, OFF);
        msleep(DELAY);
        turn_led(&leds, LED_B, ON);
        msleep(DELAY);
        turn_led(&leds, LED_B, OFF);
        msleep(DELAY);
    }
    term_led(&leds);
    return 0;
}
int term_led(leds_t *leds)
{
    int            i;
    led_t         *led;
    printf("terminate RGB Led gpios\n");
    if( !leds )
    {
        printf("Invalid input arguments\n");
        return -1;
    }
    for(i=0; i<leds->count; i++)
    {
        led = &leds->leds[i];
        if( led->request )
        {
            turn_led(leds, i, OFF);
            gpiod_line_request_release(led->request);
        }
    }
    return 0;
}
int init_led(leds_t *leds)
{
    led_t                       *led;
    int                          i, rv = 0;
    char                         chip_dev[32];
    struct gpiod_chip           *chip;      /* gpio chip */
    struct gpiod_line_settings  *settings;  /* gpio direction, bias, active_low, value */
    struct gpiod_line_config    *line_cfg;  /* gpio line */
    struct gpiod_request_config *req_cfg;   /* gpio consumer, it can be NULL */
    if( !leds )
    {
        printf("Invalid input arguments\n");
        return -1;
    }
    /* defined in libgpiod-2.0/lib/line-settings.c:
        struct gpiod_line_settings {
            enum gpiod_line_direction direction;
            enum gpiod_line_edge edge_detection;
            enum gpiod_line_drive drive;
            enum gpiod_line_bias bias;
            bool active_low;
            enum gpiod_line_clock event_clock;
            long debounce_period_us;
            enum gpiod_line_value output_value;
        };
     */
    settings = gpiod_line_settings_new();
    if (!settings)
    {
        printf("unable to allocate line settings\n");
        rv = -2;
        goto cleanup;
    }
    /* defined in libgpiod-2.0/lib/line-config.c
        struct gpiod_line_config {
            struct per_line_config line_configs[LINES_MAX];
            size_t num_configs;
            enum gpiod_line_value output_values[LINES_MAX];
            size_t num_output_values;
            struct settings_node *sref_list;
        };
    */
    line_cfg = gpiod_line_config_new();
    if (!line_cfg)
    {
        printf("unable to allocate the line config structure");
        rv = -2;
        goto cleanup;
    }
    /* defined in libgpiod-2.0/lib/request-config.c:
        struct gpiod_request_config {
            char consumer[GPIO_MAX_NAME_SIZE];
            size_t event_buffer_size;
        };
     */
    req_cfg = gpiod_request_config_new();
    if (!req_cfg)
    {
        printf("unable to allocate the request config structure");
        rv = -2;
        goto cleanup;
    }
    for(i=0; i<leds->count; i++)
    {
        led = &leds->leds[i];
        snprintf(chip_dev, sizeof(chip_dev), "/dev/gpiochip%d", led->chip_num);
        chip = gpiod_chip_open(chip_dev);
        if( !chip )
        {
            printf("open gpiochip failure, maybe you need running as root\n");
            rv = -3;
            goto cleanup;
        }
        /* Set as output direction, active low and default level as inactive */
        gpiod_line_settings_reset(settings);
        gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT);
        gpiod_line_settings_set_active_low(settings, led->active);
        gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_INACTIVE);
        /* set gpio line */
        gpiod_line_config_reset(line_cfg);
        gpiod_line_config_add_line_settings(line_cfg, &led->gpio_num, 1, settings);
        /* Can be NULL for default settings. */
        gpiod_request_config_set_consumer(req_cfg, led->name);
        /* Request a set of lines for exclusive usage. */
        led->request = gpiod_chip_request_lines(chip, req_cfg, line_cfg);
        gpiod_chip_close(chip);
        //printf("request %5s led[%d] for gpio output okay\n", led->name, led->gpio);
    }
cleanup:
    if( rv< 0 )
        term_led(leds);
    if( line_cfg )
        gpiod_line_config_free(line_cfg);
    if( req_cfg )
        gpiod_request_config_free(req_cfg);
    if( settings )
        gpiod_line_settings_free(settings);
    return rv;
}
int turn_led(leds_t *leds, int which, int cmd)
{
    led_t         *led;
    int            rv = 0;
    int            value = 0;
    if( !leds || which<0 || which>=leds->count )
    {
        printf("Invalid input arguments\n");
        return -1;
    }
    led = &leds->leds[which];
    value = OFF==cmd ? GPIOD_LINE_VALUE_INACTIVE : GPIOD_LINE_VALUE_ACTIVE;
    gpiod_line_request_set_value(led->request, led->gpio_num, value);
    return 0;
}
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 ;
}
hal/api/libgpiod/build.sh
New file
@@ -0,0 +1,118 @@
#!/bin/bash
# library name and version
# Official: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
LIB_NAME=libgpiod-2.0
PACK_SUFIX=tar.gz
# Cross compiler for cross compile on Linux server
CROSS_COMPILE=arm-linux-gnueabihf-
# this project absolute path
PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
# binaries install path
PREFIX_PATH=$PRJ_PATH/install
# check installed or not file
INST_FILE=$PREFIX_PATH/lib/libgpiod.so
#+-------------------------+
#| Shell script functions  |
#+-------------------------+
function pr_error() {
    echo -e "\033[40;31m $1 \033[0m"
}
function pr_warn() {
    echo -e "\033[40;33m $1 \033[0m"
}
function pr_info() {
    echo -e "\033[40;32m $1 \033[0m"
}
function check_result()
{
    if [ $? != 0 ] ; then
        pr_error $1
    fi
}
function do_export()
{
    pr_warn "set cross(${CROSS_COMPILE})"
    # export cross toolchain
    export CC=${CROSS_COMPILE}gcc
    export CXX=${CROSS_COMPILE}g++
    export AS=${CROSS_COMPILE}as
    export AR=${CROSS_COMPILE}ar
    export LD=${CROSS_COMPILE}ld
    export NM=${CROSS_COMPILE}nm
    export RANLIB=${CROSS_COMPILE}ranlib
    export OBJDUMP=${CROSS_COMPILE}objdump
    export STRIP=${CROSS_COMPILE}strip
    # export cross configure
    export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
    # Clear LDFLAGS and CFLAGS
    export LDFLAGS=
    export CFLAGS=
}
function do_fetch()
{
    if [ -e ${INST_FILE} ] ; then
        pr_warn "$LIB_NAME compile and installed alredy"
        exit ;
    fi
    if [ -d $LIB_NAME ] ; then
        pr_warn "$LIB_NAME fetch already"
        return ;
    fi
    if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
        wget https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/snapshot/${LIB_NAME}.${PACK_SUFIX}
        check_result "ERROR: download ${LIB_NAME} failure"
    fi
    pr_warn "$LIB_NAME download already, decompress it now"
    tar -xzf ${LIB_NAME}.${PACK_SUFIX}
}
function do_build()
{
    cd $LIB_NAME
    ./autogen.sh
    do_export
    echo "ac_cv_func_malloc_0_nonnull=yes" > arm-linux.cache
    ./configure --prefix=${PREFIX_PATH} ${CONFIG_CROSS} --enable-static \
        --cache-file=arm-linux.cache --enable-tools
    check_result "ERROR: configure ${LIB_NAME} failure"
    make -j ${JOBS} && make install
    check_result "ERROR: compile ${LIB_NAME} failure"
}
function do_clean()
{
    rm -rf ${LIB_NAME}*
    rm -rf install
}
if [[ $# == 1 && $1 == -c ]] ;then
    pr_warn "start clean $LIB_NAME"
    do_clean
    exit;
fi
do_fetch
do_build
hal/api/libgpiod/makefile
New file
@@ -0,0 +1,11 @@
all: update_cross
    @./build.sh
clean:
    @./build.sh -c
update_cross:
ifdef CROSS_COMPILE
    sed -i 's|^CROSS_COMPILE=.*|CROSS_COMPILE=${CROSS_COMPILE}|g' build.sh
endif
hal/api/makefile
New file
@@ -0,0 +1,54 @@
#********************************************************************************
#      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=%)
# libgpiod compile install path
LIBGPIOD_PATH=libgpiod/install
CFLAGS+=-I ${LIBGPIOD_PATH}/include
LDFLAGS+=-L ${LIBGPIOD_PATH}/lib -lgpiod
all: libs binaries install
libs:
    make -C libgpiod CROSS_COMPILE=${CROSS_COMPILE}
binaries:  ${BINARIES}
%:  %.c
    $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
install:
    cp $(BINARIES) ${INSTPATH}
clean:
    @rm -f *.o *.lo $(BINARIES)
distclean: clean
    @rm -f  tags cscope*
    @make clean -C libgpiod
.PHONY: clean entry
hal/api/pwm.c
New file
@@ -0,0 +1,309 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  pwm.c
 *    Description:  This file is used to control PWM buzzer/Led
 *
 * Pin connection:
 *               PWM Module              Raspberry Pi Board
 *                  VCC       <----->      5V
 *                 buzzer     <----->      #Pin32(BCM GPIO12)
 *                  Led       <----->      #Pin33(BCM GPIO13)
 *                  GND       <----->      GND
 *
 * /boot/config.txt:
 *
 *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
 *          dtoverlay=pwm,pin=13,func=4 (Led)
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <libgen.h>
#define PWMCHIP_PATH      "/sys/class/pwm/pwmchip0"
#define ENABLE            1
#define DISABLE           0
int init_pwm(int channel, int freq, int duty);
int turn_pwm(int channel, int status);
int term_pwm(int channel);
static inline void msleep(unsigned long ms);
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(" This is pwm control program. \n");
    printf(" -c[channel ]  Specify PWM channel, such as 0\n");
    printf(" -f[freq    ]  Specify PWM frequency, default 2500(Hz)\n");
    printf(" -d[duty    ]  Specify PWM duty, default 50(50%%)\n");
    printf(" -s[status  ]  Specify PWM status: 1->on(default), 0->off\n");
    printf(" -h[help    ]  Display this help information\n");
    printf(" -v[version ]  Display the program version\n");
    printf("\n");
    printf("Example buzzer : %s -c 0 -f 2750 -d 50 -s 1\n", progname);
    printf("Example Led    : %s -c 1 -f 100 -d 50 -s 1\n", progname);
    printf("Example disable: %s -c 0 -s 0\n", progname);
    printf("\n");
    banner(progname);
    return;
}
int main(int argc, char **argv)
{
    int             rv;
    char           *progname=NULL;
    int             channel = -1;
    int             freq = 2500;
    int             duty = 50;
    int             status = ENABLE;
    struct option long_options[] = {
        {"channel", required_argument, NULL, 'c'},
        {"freq", required_argument, NULL, 'f'},
        {"duty", required_argument, NULL, 'd'},
        {"status", required_argument, NULL, 's'},
        {"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:f:d:s:vh", long_options, NULL)) != -1)
    {
        switch (rv)
        {
            case 'c': /*  Set pwm channel, such as 0,1 */
                channel = atoi(optarg);
                break;
            case 'f': /*  Set pwm frequency */
                freq = atoi(optarg);
                break;
            case 'd': /*  Set pwm duty cycle */
                duty = atoi(optarg);
                break;
            case 's': /*  Set pwm status, 0 for disable and 1 for enable */
                status = atoi(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(channel < 0 )
    {
        program_usage(progname);
        return 0;
    }
    if( status )
    {
        if( (rv=init_pwm(channel, freq, duty)) < 0 )
        {
            printf("initial PWM failure, rv=%d\n", rv);
            return 1;
        }
        turn_pwm(channel, ENABLE);
    }
    else
    {
        term_pwm(channel);
    }
    return 0;
}
/* check PWM $channel export or not */
int check_pwm(int channel)
{
    char          path[256];
    /* check /sys/class/pwm/pwmchip0/pwmN exist or not */
    snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel);
    return access(path, F_OK) ? 0 : 1;
}
/* export($export=1)/unexport($export=0) PWM $channel */
int export_pwm(int channel, int export)
{
    int           fd;
    char          buf[32];
    char          path[256];
    if( export && check_pwm(channel) )
        return 0; /* export already when export  */
    else if( !export && !check_pwm(channel) )
        return 0; /* unexport already when unexport  */
    /* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */
    snprintf(path, sizeof(path), "%s/%s", PWMCHIP_PATH, export?"export":"unexport");
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        printf("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    snprintf(buf, sizeof(buf), "%d", channel);
    write(fd, buf, strlen(buf));
    msleep(100);
    if( export && check_pwm(channel) )
        return 0; /* export already when export  */
    else if( !export && !check_pwm(channel) )
        return 0; /* unexport already when unexport  */
    return -4;
}
/* configure PWM $channel */
int config_pwm(int channel, int freq, int duty)
{
    int           fd;
    char          buf[32];
    char          path[256];
    int           period;
    int           duty_cycle;
    if( !check_pwm(channel) )
        return -2;
    /* open PWM period file and write period in ns */
    snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        printf("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    period = 1000000000/freq;
    snprintf(buf, sizeof(buf), "%d", period);
    write(fd, buf, strlen(buf));
    /* open PWM duty_cycle file and write duty */
    snprintf(path, sizeof(path), "%s/pwm%d/duty_cycle", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        printf("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    duty_cycle = (period*duty) / 100;
    snprintf(buf, sizeof(buf), "%d", duty_cycle);
    write(fd, buf, strlen(buf));
    return 0;
}
int init_pwm(int channel, int freq, int duty)
{
    int           rv;
    char          buf[32];
    char          path[256];
    if( (rv=export_pwm(channel, 1)) )
    {
        printf("export PWM channel[%d] failed, rv=%d\n", channel, rv);
        return rv;
    }
    if( (rv=config_pwm(channel, freq, duty)) )
    {
        printf("config PWM channel[%d] failed, rv=%d\n", channel, rv);
        return rv;
    }
    printf("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty);
    return 0;
}
int turn_pwm(int channel, int status)
{
    int           fd;
    char          buf[32];
    char          path[256];
    if( !check_pwm(channel) )
        return -1;
    /* open PWM enable file and enable(1)/disable(0) it */
    snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        printf("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    snprintf(buf, sizeof(buf), "%d", status?1:0);
    write(fd, buf, strlen(buf));
    printf("pwm[%d] %s\n", channel, status?"enable":"disable");
    return 0;
}
int term_pwm(int channel)
{
    if( !check_pwm(channel) )
        return 0;
    turn_pwm(channel, DISABLE);
    export_pwm(channel, 0);
    return 0;
}
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 ;
}