/*********************************************************************************
|
* 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 ;
|
}
|