Add led/pwm API source code
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 ; |
| | | } |
New file |
| | |
| | | #!/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 |
New file |
| | |
| | | |
| | | 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 |
New file |
| | |
| | | #******************************************************************************** |
| | | # 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 |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 ; |
| | | } |