diff --git a/.gitignore b/.gitignore index e2c57e5e5..9921eec3a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ Make.dep .context # nuttx/$(TOPDIR)/Makefile.[unix|win]::$(DIRLINKS_EXTERNAL_DIRS) .dirlinks +.vscode +.DS_Store diff --git a/examples/ws2812/Kconfig b/examples/ws2812/Kconfig new file mode 100644 index 000000000..c01fb20f2 --- /dev/null +++ b/examples/ws2812/Kconfig @@ -0,0 +1,33 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_WS2812 + tristate "ws2812 Demo Program" + default n + ---help--- + Enable the ws2812 demo + +if EXAMPLES_WS2812 + +config EXAMPLES_WS2812_PROGNAME + string "Program Name" + default "ws2812" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_WS2812_PRIORITY + int "Task Priority" + default 100 + +config EXAMPLES_WS2812_STACKSIZE + int "Stack Size" + default DEFAULT_TASK_STACKSIZE + +config EXAMPLES_WS2812_DEFAULT_DEV + string "Default Device" + default "/dev/leds0" + +endif diff --git a/examples/ws2812/Make.defs b/examples/ws2812/Make.defs new file mode 100644 index 000000000..8dbaa2b13 --- /dev/null +++ b/examples/ws2812/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/ws2812/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_WS2812),) +CONFIGURED_APPS += $(APPDIR)/examples/ws2812 +endif diff --git a/examples/ws2812/Makefile b/examples/ws2812/Makefile new file mode 100644 index 000000000..470674761 --- /dev/null +++ b/examples/ws2812/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/examples/ws2812/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# MTD R/W buffer test Example + +PROGNAME = $(CONFIG_EXAMPLES_WS2812_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_WS2812_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_WS2812_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_WS2812) + +MAINSRC = ws2812_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/ws2812/ws2812_main.c b/examples/ws2812/ws2812_main.c new file mode 100644 index 000000000..c7fae92d1 --- /dev/null +++ b/examples/ws2812/ws2812_main.c @@ -0,0 +1,315 @@ +/**************************************************************************** + * apps/examples/ws2812/ws2812_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct neo_config_s +{ + FAR char *path; + int loops; + int leds; + int delay; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct neo_config_s config = +{ + NULL, /* path - default set in main */ + 4, /* loop - all colors 4 times */ + CONFIG_WS2812_LED_COUNT, /* leds - based on config */ + 20000 /* delay - (in µs) ~50Hz */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: help + ****************************************************************************/ + +static void help(FAR struct neo_config_s *conf) +{ + printf("Usage: ws2812 [OPTIONS]\n"); + + printf("\nArguments are \"sticky\". " + "For example, once the device path is\n"); + printf("specified, that path will be re-used until it is changed.\n"); + + printf(" [-p path] selects the ws2812 device. " + "Default: %s Current: %s\n", + CONFIG_EXAMPLES_WS2812_DEFAULT_DEV, conf->path ? conf->path + : "NONE"); + + printf(" [-l leds] selects number of ws2812s in the chain. " + "Default: %d Current: %d\n", + CONFIG_WS2812_LED_COUNT, conf->leds); + + printf(" [-r repeat] selects the number change cycles. " + "Default: %d Current: %d\n", + 4, conf->loops); + + printf(" [-d delay] selects delay between updates. " + "Default: %d us Current: %d us\n", + 20000, conf->delay); +} + +/**************************************************************************** + * Name: set_devpath + ****************************************************************************/ + +static void set_devpath(FAR struct neo_config_s *conf, + FAR const char *devpath) +{ + /* Get rid of any old device path */ + + if (conf->path != NULL) + { + free(conf->path); + } + + /* Then set-up the new device path by copying the string */ + + conf->path = strdup(devpath); +} + +/**************************************************************************** + * Name: arg_string + ****************************************************************************/ + +static int arg_string(FAR char **arg, FAR char **value) +{ + FAR char *ptr = *arg; + + if (ptr[2] == '\0') + { + *value = arg[1]; + return 2; + } + else + { + *value = &ptr[2]; + return 1; + } +} + +/**************************************************************************** + * Name: arg_decimal + ****************************************************************************/ + +static int arg_decimal(FAR char **arg, FAR long *value) +{ + FAR char *string; + int ret; + + ret = arg_string(arg, &string); + *value = strtol(string, NULL, 10); + return ret; +} + +/**************************************************************************** + * Name: parse_args + ****************************************************************************/ + +static void parse_args(FAR struct neo_config_s *conf, + int argc, + FAR char **argv) +{ + FAR char *ptr; + FAR char *str; + long value; + int index; + int nargs; + + for (index = 1; index < argc; ) + { + ptr = argv[index]; + if (ptr[0] != '-') + { + fprintf(stderr, "Invalid options format: %s\n", ptr); + exit(1); + } + + switch (ptr[1]) + { + case 'l': + nargs = arg_decimal(&argv[index], &value); + if (value < 1 || value > INT_MAX) + { + fprintf(stderr, "Led count out of range: %ld\n", value); + exit(1); + } + + conf->leds = (uint32_t)value; + index += nargs; + break; + + case 'r': + nargs = arg_decimal(&argv[index], &value); + if (value < 1 || value > INT_MAX) + { + fprintf(stderr, "Repeat out of range: %ld\n", value); + exit(1); + } + + conf->loops = (uint32_t)value; + index += nargs; + break; + + case 'p': + nargs = arg_string(&argv[index], &str); + set_devpath(conf, str); + index += nargs; + break; + + case 'd': + nargs = arg_decimal(&argv[index], &value); + if (value < 1 || value > INT_MAX) + { + fprintf(stderr, "Delay out of range: %ld\n", value); + exit(1); + } + + conf->delay = (int)value; + index += nargs; + break; + + case 'h': + help(conf); + exit(0); + + default: + fprintf(stderr, "Unsupported option: %s\n", ptr); + help(conf); + exit(1); + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * ws2812_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + FAR uint32_t *buffer; + FAR uint32_t *bp; + ssize_t result; + + if (config.path == NULL) + { + config.path = strdup(CONFIG_EXAMPLES_WS2812_DEFAULT_DEV); + } + + /* Parse the command line */ + + parse_args(&config, argc, argv); + + /* Run the display loop */ + + int fd = open(config.path, O_WRONLY); + if (fd < 0) + { + fprintf(stderr, + "ws2812_main: open %s failed: %d\n", + config.path, + errno); + goto errout; + } + + buffer = calloc(4, config.leds); + + if (buffer == NULL) + { + fprintf(stderr, "ws2812_main: buffer allocation failed: %d\n", errno); + goto errout; + } + + for (int i = 0; i < config.loops; ++i) + { + for (int j = 0; j < 256; ++j) + { + bp = buffer; + + for (int k = 0; k < config.leds; ++k) + { + *bp++ = ws2812_gamma_correct( + ws2812_hsv_to_rgb((j + k) & 0xff, + 0xff, + 0xff)); + } + + lseek(fd, 0, SEEK_SET); + + result = write(fd, buffer, 4 * config.leds); + if (result != 4 * config.leds) + { + fprintf(stderr, + "ws2812_main: write failed: %d %d\n", + result, + errno); + + goto errout_with_dev; + } + + usleep(config.delay); + } + } + + free(buffer); + close(fd); + fflush(stdout); + return OK; + +errout_with_dev: + close(fd); + +errout: + fflush(stdout); + return ERROR; +}