From d5290a21ae5205ab038d20b2aa2dee7f6d06cc39 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Tue, 24 Mar 2020 12:27:05 +0100 Subject: [PATCH] tests: add PCF957X driver test application --- tests/driver_pcf857x/Makefile | 13 + tests/driver_pcf857x/Makefile.ci | 8 + tests/driver_pcf857x/README.md | 70 +++++ tests/driver_pcf857x/app.config.test | 7 + tests/driver_pcf857x/main.c | 434 +++++++++++++++++++++++++++ 5 files changed, 532 insertions(+) create mode 100644 tests/driver_pcf857x/Makefile create mode 100644 tests/driver_pcf857x/Makefile.ci create mode 100644 tests/driver_pcf857x/README.md create mode 100644 tests/driver_pcf857x/app.config.test create mode 100644 tests/driver_pcf857x/main.c diff --git a/tests/driver_pcf857x/Makefile b/tests/driver_pcf857x/Makefile new file mode 100644 index 000000000000..1e785fac83df --- /dev/null +++ b/tests/driver_pcf857x/Makefile @@ -0,0 +1,13 @@ +INCLUDES += -I$(APPDIR) + +include ../Makefile.tests_common + +ifeq (,$(filter pcf857%,$(USEMODULE))) + # default expander module + USEMODULE += pcf8575 +endif + +USEMODULE += shell +USEMODULE += benchmark + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_pcf857x/Makefile.ci b/tests/driver_pcf857x/Makefile.ci new file mode 100644 index 000000000000..1152ca53bcbb --- /dev/null +++ b/tests/driver_pcf857x/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + atmega328p-xplained-mini \ + # diff --git a/tests/driver_pcf857x/README.md b/tests/driver_pcf857x/README.md new file mode 100644 index 000000000000..39f7f7e6c8a6 --- /dev/null +++ b/tests/driver_pcf857x/README.md @@ -0,0 +1,70 @@ +# Texas Instruments PCF857X I2C I/O expanders test application + +## Overview + +This test appliation demonstrates the usage of the PCF857X driver interface +and can be used to test each PCF857X expander I/O pin with shell commands. + +## Compilation + +To use the test application, compile it with one or more of the pseudomodules +`pcf8574`, `pcf8574a` or `pcf8575` to enable the driver for your +expander modules. Please check the default configuration parameters in +`$(RIOTBASE)/drivers/pcf857x/include/pcf857x_params.h` and modify them +if necessary. Alternatively, a modified version of this file could be +placed in the directory of this test application to override it. +``` +USEMODULE=pcf8575 make -C tests/driver_pcf857x BOARD=... +``` +**Please note:** When no pseudomodule is given, `pcf8575` is used by default. + +To use external interrupts with the expander I/O pins, the PCF857X +low-active open-drain interrupt signal has to be enabled. Add module +`pcf857x_irq` for this purpose and define the MCU interrupt pin by +parameter `PCF857X_PARAM_INT_PIN`, e.g. +``` +CFLAGS="-DPCF857X_PARAM_INT_PIN=\(GPIO_PIN\(0,6\)\)" \ +USEMODULE="pcf8575 pcf857x_irq" make -C tests/driver_pcf857x BOARD=... +``` +**Please note:** Since interrupts are handled in the context of a separate +event thread, enabling interrupts requires more RAM. + +## Usage + +The test allows to use commands as known from GPIO test application for +PCF857X expanders: +``` +> help +Command Description +--------------------------------------- +init_out init as output (push-pull mode) +init_in init as input w/o pull resistor +init_in_pu init as input with pull-up +init_od init as output (open-drain without pull resistor) +init_od_pu init as output (open-drain with pull-up) +init_int init as external INT w/o pull resistor +enable_int enable or disable gpio interrupt +read read pin status +set set pin to HIGH +clear set pin to LOW +toggle toggle pin +bench run a set of predefined benchmarks + +``` +The number of the first PCF857X expander port used by the test application +is defined by the macro `PCF857X_PORT_0`, which is 16 by default. This value +can be overridden during compilation, e.g.: +``` +CFLAGS="-DPCF857X_PORT_0=8" \ +USEMODULE=pcf8575 make -C tests/driver_pcf857x BOARD=... +``` +Using the port number defined by `PCF857X_PORT_0` and the following port +numbers, you can apply the command to the PCF857X expander ports. For +example, the following command initializes I/O pin 7 of the first PCF857X +expander: +``` +init_out 16 7 +``` +Commands with port numbers less than `PCF857X_PORT_0` refer to GPIO +peripheral ports. Thus, both the I/O pins of the PCF857X expanders as well +as the GPIO peripheral pins of the MCU can be addressed by all commands. diff --git a/tests/driver_pcf857x/app.config.test b/tests/driver_pcf857x/app.config.test new file mode 100644 index 000000000000..6366f1bfcf28 --- /dev/null +++ b/tests/driver_pcf857x/app.config.test @@ -0,0 +1,7 @@ +# this file enables modules defined in Kconfig. Do not use this file for +# application configuration. This is only needed during migration. +CONFIG_MODULE_PCF857X=y + +CONFIG_MODULE_SHELL=y +CONFIG_MODULE_BENCHMARK=y +CONFIG_ZTIMER_USEC=y diff --git a/tests/driver_pcf857x/main.c b/tests/driver_pcf857x/main.c new file mode 100644 index 000000000000..89a6c12610bc --- /dev/null +++ b/tests/driver_pcf857x/main.c @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @brief Test application for Texas Instruments PCF857X I2C I/O expanders + * @author Gunar Schorcht + * @file + * + * ## Overview + * + * This test appliation demonstrates the usage of the PCF857X driver interface + * and can be used to test each PCF857X expander I/O pin with shell commands. + * + * The application bases on the test application for GPIO peripheral drivers + * which is under following copyright: + * + * Copyright (C) 2014,2017 Freie Universität Berlin + * @author Hauke Petersen + * + * ## Compilation + * + * To use the test application, compile it with one or more of the pseudomodules + * `pcf8574`, `pcf8574a` or `pcf8575` to enable the driver for your + * expander modules. Please check the default configuration parameters in + * `$(RIOTBASE)/drivers/pcf857x/include/pcf857x_params.h` and modify them + * if necessary. Alternatively, a modified version of this file could be + * placed in the directory of this test application to override it. + * ``` + * USEMODULE=pcf8575 make -C tests/driver_pcf857x BOARD=... + * ``` + * @note When no pseudomodule is given, `pcf8575` is used by default. + * + * To use external interrupts with the expander I/O pins, the PCF857X + * low-active open-drain interrupt signal has to be enabled. Add module + * `pcf857x_irq` for this purpose and define the MCU interrupt pin by + * parameter `PCF857X_PARAM_INT_PIN`, e.g. + * ``` + * CFLAGS="-DPCF857X_PARAM_INT_PIN=\(GPIO_PIN\(0,6\)\)" \ + * USEMODULE="pcf8575 pcf857x_irq" make -C tests/driver_pcf857x BOARD=... + * ``` + * @note Since interrupts are handled in the context of a separate event thread, + * enabling interrupts requires more RAM. + * + * ## Usage + * + * The test allows to use commands as known from GPIO test application for + * PCF857X expanders: + * ``` + * > help + * Command Description + * --------------------------------------- + * init_out init as output (push-pull mode) + * init_in init as input w/o pull resistor + * init_in_pu init as input with pull-up + * init_od init as output (open-drain without pull resistor) + * init_od_pu init as output (open-drain with pull-up) + * init_int init as external INT w/o pull resistor + * enable_int enable or disable gpio interrupt + * read read pin status + * set set pin to HIGH + * clear set pin to LOW + * toggle toggle pin + * bench run a set of predefined benchmarks + * ``` + * The number of the first PCF857X expander port used by the test application + * is defined by the macro `PCF857X_PORT_0`, which is 16 by default. This value + * can be overridden during compilation, e.g.: + * ``` + * CFLAGS="-DPCF857X_PORT_0=8" \ + * USEMODULE=pcf8575 make -C tests/driver_pcf857x BOARD=... + * ``` + * Using the port number defined by `PCF857X_PORT_0` and the following port + * numbers, you can apply the command to the PCF857X expander ports. For + * example, the following command initializes I/O pin 7 of the first PCF857X + * expander: + * ``` + * init_out 16 7 + * ``` + * Commands with port numbers less than `PCF857X_PORT_0` refer to GPIO + * peripheral ports. Thus, both the I/O pins of the PCF857X expanders as well + * as the GPIO peripheral pins of the MCU can be addressed by all commands. + */ + +#include +#include + +#include "pcf857x.h" +#include "pcf857x_params.h" + +#include "irq.h" +#include "shell.h" +#include "benchmark.h" + +#define BENCH_RUNS_DEFAULT (100UL * 100) + +/* Number of configured PCF857X I/O expander devices */ +#define PCF857X_NUM ARRAY_SIZE(pcf857x_params) + +/* Port number of the first PCF857X I/O expander device */ +#ifndef PCF857X_PORT_0 +#define PCF857X_PORT_0 (16) +#endif + +/* PCF857X devices allocation */ +pcf857x_t pcf857x_dev[PCF857X_NUM]; + +#ifdef MODULE_PCF857X_IRQ +static void cb(void *arg) +{ + printf("INT: external interrupt from pin %i\n", (int)arg); +} +#endif + +static int init_pin(int argc, char **argv, gpio_mode_t mode) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + if (po < PCF857X_PORT_0) { + gpio_init(GPIO_PIN(po, pi), mode); + } + else if (pcf857x_gpio_init(&pcf857x_dev[po - PCF857X_PORT_0], + pi, mode) < 0) { + printf("error: init PCF857X pin (dev %i, pin %02i) failed\n", po, pi); + return 1; + } + + return 0; +} + +static int init_out(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OUT); +} + +static int init_in(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN); +} + +static int init_in_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN_PU); +} + +static int init_od(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD); +} + +static int init_od_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD_PU); +} + +#ifdef MODULE_PCF857X_IRQ +static int init_int(int argc, char **argv) +{ + gpio_mode_t mode = GPIO_IN; + gpio_flank_t flank; + int fl; + + if (argc < 4) { + printf("usage: %s \n", argv[0]); + puts("\tflank:\n" + "\t0: falling\n" + "\t1: rising\n" + "\t2: both\n"); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + fl = atoi(argv[3]); + switch (fl) { + case 0: + flank = GPIO_FALLING; + break; + case 1: + flank = GPIO_RISING; + break; + case 2: + flank = GPIO_BOTH; + break; + default: + puts("error: invalid value for active flank"); + return 1; + } + + if (po < PCF857X_PORT_0) { + gpio_init_int(GPIO_PIN(po, pi), mode, flank, cb, (void *)pi); + } + else if (pcf857x_gpio_init_int(&pcf857x_dev[po - PCF857X_PORT_0], pi, + mode, flank, cb, (void *)pi) < 0) { + printf("error: init_int PCF857X pin (dev %i, pin %02i) failed\n", + po, pi); + return 1; + } + + return 0; +} + +static int enable_int(int argc, char **argv) +{ + int status; + + if (argc < 4) { + printf("usage: %s \n", argv[0]); + puts("\tstatus:\n" + "\t0: disable\n" + "\t1: enable\n"); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + status = atoi(argv[3]); + + switch (status) { + case 0: + puts("disabling GPIO interrupt"); + if (po < PCF857X_PORT_0) { + gpio_irq_disable(GPIO_PIN(po, pi)); + } + else { + pcf857x_gpio_irq_disable(&pcf857x_dev[po - PCF857X_PORT_0], pi); + } + break; + case 1: + puts("enabling GPIO interrupt"); + if (po < PCF857X_PORT_0) { + gpio_irq_enable(GPIO_PIN(po, pi)); + } + else { + pcf857x_gpio_irq_enable(&pcf857x_dev[po - PCF857X_PORT_0], pi); + } + break; + default: + puts("error: invalid status"); + return 1; + } + + return 0; +} +#endif /* MODULE_PCF857X_IRQ */ + +static int read(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + if (po < PCF857X_PORT_0) { + if (gpio_read(GPIO_PIN(po, pi))) { + printf("GPIO pin (port %i, pin %02i) is HIGH\n", po, pi); + } + else { + printf("GPIO pin (port %i, pin %02i) is LOW\n", po, pi); + } } + else { + if (pcf857x_gpio_read(&pcf857x_dev[po - PCF857X_PORT_0], pi)) { + printf("PCF857X pin (dev %i, pin %02i) is HIGH\n", po, pi); + } + else { + printf("PCF857X pin (dev %i, pin %02i) is LOW\n", po, pi); + } + } + return 0; +} + +static int set(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + if (po < PCF857X_PORT_0) { + gpio_set(GPIO_PIN(po, pi)); + } + else { + pcf857x_gpio_set(&pcf857x_dev[po - PCF857X_PORT_0], pi); + } + return 0; +} + +static int clear(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + if (po < PCF857X_PORT_0) { + gpio_clear(GPIO_PIN(po, pi)); + } + else { + pcf857x_gpio_clear(&pcf857x_dev[po - PCF857X_PORT_0], pi); + } + return 0; +} + +static int toggle(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + if (po < PCF857X_PORT_0) { + gpio_toggle(GPIO_PIN(po, pi)); + } + else { + pcf857x_gpio_toggle(&pcf857x_dev[po - PCF857X_PORT_0], pi); + } + return 0; +} + +static int bench(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s [# of runs]\n", argv[0]); + return 1; + } + + int po = atoi(argv[1]); + int pi = atoi(argv[2]); + + unsigned long runs = BENCH_RUNS_DEFAULT; + if (argc > 3) { + runs = (unsigned long)atol(argv[3]); + } + + puts("\nGPIO driver run-time performance benchmark\n"); + + if (po < PCF857X_PORT_0) { + BENCHMARK_FUNC("nop loop", runs, __asm__ volatile("nop")); + gpio_init(GPIO_PIN(po, pi), GPIO_OUT); + BENCHMARK_FUNC("gpio_set", runs, gpio_set(GPIO_PIN(po, pi))); + BENCHMARK_FUNC("gpio_clear", runs, gpio_clear(GPIO_PIN(po, pi))); + BENCHMARK_FUNC("gpio_toggle", runs, gpio_toggle(GPIO_PIN(po, pi))); + gpio_init(GPIO_PIN(po, pi), GPIO_IN); + BENCHMARK_FUNC("gpio_read", runs, (void)gpio_read(GPIO_PIN(po, pi))); + gpio_init(GPIO_PIN(po, pi), GPIO_OUT); + BENCHMARK_FUNC("gpio_write", runs, gpio_write(GPIO_PIN(po, pi), 1)); + } + else { + pcf857x_t* dev = &pcf857x_dev[po - PCF857X_PORT_0]; + BENCHMARK_FUNC("nop loop", runs, __asm__ volatile("nop")); + pcf857x_gpio_init(dev, pi, GPIO_OUT); + BENCHMARK_FUNC("gpio_set", runs, pcf857x_gpio_set(dev, pi)); + BENCHMARK_FUNC("gpio_clear", runs, pcf857x_gpio_clear(dev, pi)); + BENCHMARK_FUNC("gpio_toggle", runs, pcf857x_gpio_toggle(dev, pi)); + pcf857x_gpio_init(dev, pi, GPIO_IN); + BENCHMARK_FUNC("gpio_read", runs, (void)pcf857x_gpio_read(dev, pi)); + pcf857x_gpio_init(dev, pi, GPIO_OUT); + BENCHMARK_FUNC("gpio_write", runs, pcf857x_gpio_write(dev, pi, 1)); + puts("\n --- DONE ---"); + } + return 0; +} + +static const shell_command_t shell_commands[] = { + { "init_out", "init as output (push-pull mode)", init_out }, + { "init_in", "init as input w/o pull resistor", init_in }, + { "init_in_pu", "init as input with pull-up", init_in_pu }, + { "init_od", "init as output (open-drain without pull resistor)", init_od }, + { "init_od_pu", "init as output (open-drain with pull-up)", init_od_pu }, +#ifdef MODULE_PCF857X_IRQ + { "init_int", "init as external INT w/o pull resistor", init_int }, + { "enable_int", "enable or disable gpio interrupt", enable_int }, +#endif + { "read", "read pin status", read }, + { "set", "set pin to HIGH", set }, + { "clear", "set pin to LOW", clear }, + { "toggle", "toggle pin", toggle }, + { "bench", "run a set of predefined benchmarks", bench }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("PCF857X I/O expander GPIO peripheral driver test\n"); + puts("Initializing PCF857X"); + + /* initialize configured PCF857X devices */ + for (unsigned i = 0; i < PCF857X_NUM; i++) { + if (pcf857x_init(&pcf857x_dev[i], &pcf857x_params[i]) != PCF857X_OK) { + puts("[Failed]"); + return 1; + } + } + puts("[OK]\n"); + + printf("In this test, pins are specified by integer port and pin numbers.\n" + "PCF8574 has 8 I/O pins labeled P00...P07.\n" + "PCF8575 has 16 I/O pins labeled P00...P07 and P10...P17\n" + "Use port %d and pin 0...15 in all commands to access them.\n\n" + "NOTE: make sure the values you use exist! The\n" + " behavior for not existing ports/pins is not defined!\n", + PCF857X_PORT_0); + + /* start the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}